---
title: "Білий лейбл"
description: "Документація білого лейблу для платформи Ring"
locale: "uk"
---
# Приклади білого лейблу

Повні приклади білого лейблингу та кастомізації для агентств та підприємств, які розробляють з платформою Ring.

## 🎨 Кастомізація бренду

### Повне перевизначення бренду

// lib/brand-config.ts

{`export interface BrandConfig {
  name: string
  logo: {
    light: string
    dark: string
    favicon: string
  }
  colors: {
    primary: string
    secondary: string
    accent: string
    background: string
    foreground: string
  }
  typography: {
    fontFamily: string
    headingFont?: string
  }
  domain: string
  contact: {
    email: string
    phone?: string
    address?: string
  }
}

// Приклади конфігурацій брендів
export const brandConfigs: Record<string, BrandConfig> = {
  'tech-startup': {
    name: 'TechConnect',
    logo: {
      light: '/brands/techconnect/logo-light.svg',
      dark: '/brands/techconnect/logo-dark.svg',
      favicon: '/brands/techconnect/favicon.ico'
    },
    colors: {
      primary: '#3B82F6',
      secondary: '#8B5CF6',
      accent: '#10B981',
      background: '#FFFFFF',
      foreground: '#1F2937'
    },
    typography: {
      fontFamily: 'Inter, sans-serif',
      headingFont: 'Poppins, sans-serif'
    },
    domain: 'techconnect.com',
    contact: {
      email: 'hello@techconnect.com',
      phone: '+1 (555) 123-4567'
    }
  },
  'healthcare-network': {
    name: 'MedConnect',
    logo: {
      light: '/brands/medconnect/logo-light.svg',
      dark: '/brands/medconnect/logo-dark.svg',
      favicon: '/brands/medconnect/favicon.ico'
    },
    colors: {
      primary: '#059669',
      secondary: '#0891B2',
      accent: '#DC2626',
      background: '#F9FAFB',
      foreground: '#111827'
    },
    typography: {
      fontFamily: 'Source Sans Pro, sans-serif',
      headingFont: 'Merriweather, serif'
    },
    domain: 'medconnect.health',
    contact: {
      email: 'support@medconnect.health',
      phone: '+1 (555) 987-6543'
    }
  }
}

// Brand context provider
export function getBrandConfig(domain: string): BrandConfig {
  // In production, this would be determined by the domain
  const subdomain = domain.split('.')[0]
  return brandConfigs[subdomain] || brandConfigs['tech-startup']
}`}

### Динамічний провайдер бренду

// components/BrandProvider.tsx

{`'use client'

import { createContext, useContext, useEffect, useState } from 'react'
import { BrandConfig, getBrandConfig } from '@/lib/brand-config'

const BrandContext = createContext(null)

export function BrandProvider({ children }: { children: React.ReactNode }) {
  const [brand, setBrand] = useState(null)

  useEffect(() => {
    // Get brand config based on current domain
    const domain = window.location.hostname
    const config = getBrandConfig(domain)
    setBrand(config)

    // Apply brand styles dynamically
    applyBrandStyles(config)
  }, [])

  const applyBrandStyles = (config: BrandConfig) => {
    const root = document.documentElement

    // Apply CSS custom properties
    root.style.setProperty('--brand-primary', config.colors.primary)
    root.style.setProperty('--brand-secondary', config.colors.secondary)
    root.style.setProperty('--brand-accent', config.colors.accent)
    root.style.setProperty('--brand-background', config.colors.background)
    root.style.setProperty('--brand-foreground', config.colors.foreground)
    root.style.setProperty('--brand-font', config.typography.fontFamily)
    
    if (config.typography.headingFont) {
      root.style.setProperty('--brand-heading-font', config.typography.headingFont)
    }

    // Update document title and favicon
    document.title = `${config.name} - Professional Networking Platform`
    
    const favicon = document.querySelector('link[rel="icon"]') as HTMLLinkElement
    if (favicon) {
      favicon.href = config.logo.favicon
    }
  }

  if (!brand) {
    return Loading brand configuration...
  }

  return (
    
      {children}
    
  )
}

export function useBrand() {
  const context = useContext(BrandContext)
  if (!context) {
    throw new Error('useBrand must be used within a BrandProvider')
  }
  return context
}`}

### Компонент брендованого заголовка

// components/BrandedHeader.tsx

{`'use client'

import { useBrand } from './BrandProvider'
import { useSession, signIn, signOut } from 'next-auth/react'

export function BrandedHeader() {
  const brand = useBrand()
  const { data: session } = useSession()

  return (
    
      
        
          {/* Brand Logo */}
          
            
            
            
              {brand.name}
            
          

          {/* Navigation */}
          
            
              Organizations
            
            
              Opportunities
            
            
              Marketplace
            
          

          {/* User Menu */}
          
            {session ? (
              
                
                  Welcome, {session.user?.name}
                
                 signOut()}
                  className="text-sm text-gray-500 hover:text-gray-700"
                >
                  Sign Out
                
              
            ) : (
               signIn()}
                className="px-4 py-2 rounded-md text-white font-medium"
                style={{ backgroundColor: brand.colors.primary }}
              >
                Sign In
              
            )}
          
        
      
    
  )
}`}

## 🏢 Мультитенантна архітектура

### Конфігурація тенанта

// lib/tenant-config.ts

{`export interface TenantConfig {
  id: string
  name: string
  domain: string
  subdomain: string
  brand: BrandConfig
  features: {
    entities: boolean
    opportunities: boolean
    messaging: boolean
    wallet: boolean
    store: boolean
    nftMarketplace: boolean
    staking: boolean
  }
  limits: {
    maxEntities: number
    maxUsers: number
    storageGB: number
  }
  integrations: {
    customDomain: boolean
    sso: boolean
    api: boolean
    webhooks: boolean
  }
  subscription: {
    plan: 'starter' | 'professional' | 'enterprise'
    status: 'active' | 'suspended' | 'cancelled'
    expiresAt: string
  }
}

export const tenantConfigs: Record<string, TenantConfig> = {
  'tech-startup': {
    id: 'tech-startup-001',
    name: 'TechConnect',
    domain: 'techconnect.com',
    subdomain: 'techconnect',
    brand: brandConfigs['tech-startup'],
    features: {
      entities: true,
      opportunities: true,
      messaging: true,
      wallet: true,
      store: false,
      nftMarketplace: false,
      staking: false
    },
    limits: {
      maxEntities: 1000,
      maxUsers: 5000,
      storageGB: 100
    },
    integrations: {
      customDomain: true,
      sso: true,
      api: true,
      webhooks: true
    },
    subscription: {
      plan: 'professional',
      status: 'active',
      expiresAt: '2024-12-31T23:59:59Z'
    }
  }
}

export function getTenantConfig(domain: string): TenantConfig | null {
  const subdomain = domain.split('.')[0]
  return tenantConfigs[subdomain] || null
}`}

### Компонент контролю функцій

// components/FeatureGate.tsx

{`'use client'

import { useTenant } from '@/hooks/useTenant'

interface FeatureGateProps {
  feature: keyof TenantConfig['features']
  children: React.ReactNode
  fallback?: React.ReactNode
}

export function FeatureGate({ feature, children, fallback }: FeatureGateProps) {
  const tenant = useTenant()

  if (!tenant?.features[feature]) {
    return fallback || (
      
        
          This feature is not available in your current plan.
        
        
          Upgrade Plan
        
      
    )
  }

  return <>{children}</>
}

// Usage example
export function Dashboard() {
  return (
    
      
        Organizations
        Manage your organizations and teams.
      

      
        
          Wallet
          Manage your crypto wallet and tokens.
        
      

      
        
          Marketplace
          Buy and sell products and services.
        
      
    
  )
}`}

## 🎯 Налаштування власного домену

### Конфігурація домену

// lib/domain-config.ts

{`export interface DomainConfig {
  domain: string
  tenantId: string
  ssl: {
    enabled: boolean
    certificate?: string
    privateKey?: string
  }
  dns: {
    verified: boolean
    records: Array<{
      type: 'A' | 'CNAME' | 'TXT'
      name: string
      value: string
      ttl: number
    }>
  }
  status: 'pending' | 'active' | 'failed'
}

export async function setupCustomDomain(
  tenantId: string,
  domain: string
): Promise {
  // Verify domain ownership
  const verificationToken = generateVerificationToken()
  
  const config: DomainConfig = {
    domain,
    tenantId,
    ssl: {
      enabled: false
    },
    dns: {
      verified: false,
      records: [
        {
          type: 'CNAME',
          name: domain,
          value: 'ring-platform.vercel.app',
          ttl: 300
        },
        {
          type: 'TXT',
          name: `_ring-verification.${domain}`,
          value: verificationToken,
          ttl: 300
        }
      ]
    },
    status: 'pending'
  }

  // Save configuration
  await saveDomainConfig(config)
  
  // Start verification process
  await verifyDomainOwnership(config)
  
  return config
}

async function verifyDomainOwnership(config: DomainConfig): Promise {
  // Implementation would check DNS records
  // This is a simplified example
  return true
}

function generateVerificationToken(): string {
  return Math.random().toString(36).substring(2, 15) + 
         Math.random().toString(36).substring(2, 15)
}

async function saveDomainConfig(config: DomainConfig): Promise {
  // Save to database
}`}

### Компонент налаштування домену

// components/DomainSetup.tsx

{`'use client'

import { useState } from 'react'
import { setupCustomDomain } from '@/lib/domain-config'

export function DomainSetup() {
  const [domain, setDomain] = useState('')
  const [loading, setLoading] = useState(false)
  const [config, setConfig] = useState(null)

  const handleSetupDomain = async (e: React.FormEvent) => {
    e.preventDefault()
    setLoading(true)

    try {
      const domainConfig = await setupCustomDomain('current-tenant-id', domain)
      setConfig(domainConfig)
    } catch (error) {
      console.error('Failed to setup domain:', error)
      alert('Failed to setup custom domain')
    } finally {
      setLoading(false)
    }
  }

  return (
    
      Налаштування власного домену

      {!config ? (
        
          
            
              Власний домен
            
             setDomain(e.target.value)}
              placeholder="вашдомен.com"
              className="w-full p-3 border border-gray-300 rounded-lg"
              required
            />
            
              Введіть ваш власний домен без http:// або https://
            
          

          
            {loading ? 'Налаштування домену...' : 'Налаштувати власний домен'}
          
        
      ) : (
        
          
            
              Потрібна конфігурація DNS
            
            
              Будь ласка, додайте наступні DNS записи до вашого домену:
            

            
              {config.dns.records.map((record, index) => (
                
                  
                    
                      Тип: {record.type}
                    
                    
                      Ім'я: {record.name}
                    
                    
                      Значення: 
                      
                        {record.value}
                      
                    
                    
                      TTL: {record.ttl}
                    
                  
                
              ))}
            
          

          
            Наступні кроки
            
              Додайте DNS записи до вашого провайдера домену
              Дочекайтеся поширення DNS (до 24 годин)
              Ми автоматично перевіримо та активуємо ваш домен
              SSL сертифікат буде створено автоматично
            
          

          
            
              
              
                Статус: {config.status === 'active' ? 'Активний' : 
                         config.status === 'pending' ? 'Очікування' : 'Неактивний'}
              
            

             window.location.reload()}
              className="text-blue-600 hover:text-blue-800 text-sm"
            >
              Перевірити статус
            
          
        
      )}
    
  )
}`}

## 🔧 Конфігурація середовища

### Налаштування багатосередовищності

// lib/env-config.ts

{`export interface EnvironmentConfig {
  name: 'development' | 'staging' | 'production'
  api: {
    baseUrl: string
    timeout: number
    retries: number
  }
  database: {
    url: string
    ssl: boolean
  }
  auth: {
    providers: string[]
    sessionTimeout: number
  }
  features: {
    debug: boolean
    analytics: boolean
    errorTracking: boolean
  }
  integrations: {
    stripe: boolean
    sendgrid: boolean
    aws: boolean
  }
}

export const environments: Record<string, EnvironmentConfig> = {
  development: {
    name: 'development',
    api: {
      baseUrl: 'http://localhost:3000/api',
      timeout: 10000,
      retries: 3
    },
    database: {
      url: process.env.DATABASE_URL || 'postgresql://localhost:5432/ring_dev',
      ssl: false
    },
    auth: {
      providers: ['email', 'google'],
      sessionTimeout: 24 * 60 * 60 // 24 hours
    },
    features: {
      debug: true,
      analytics: false,
      errorTracking: false
    },
    integrations: {
      stripe: false,
      sendgrid: false,
      aws: false
    }
  },
  production: {
    name: 'production',
    api: {
      baseUrl: 'https://ring.ck.ua/api',
      timeout: 5000,
      retries: 5
    },
    database: {
      url: process.env.DATABASE_URL!,
      ssl: true
    },
    auth: {
      providers: ['email', 'google', 'apple', 'metamask'],
      sessionTimeout: 7 * 24 * 60 * 60 // 7 days
    },
    features: {
      debug: false,
      analytics: true,
      errorTracking: true
    },
    integrations: {
      stripe: true,
      sendgrid: true,
      aws: true
    }
  }
}

export function getEnvironmentConfig(): EnvironmentConfig {
  const env = process.env.NODE_ENV as keyof typeof environments
  return environments[env] || environments.development
}`}

### Компоненти для Конкретних Середовищ

// components/EnvironmentBanner.tsx

{`'use client'

import { getEnvironmentConfig } from '@/lib/env-config'

export function EnvironmentBanner() {
  const config = getEnvironmentConfig()

  if (config.name === 'production') {
    return null
  }

  const bannerColors = {
    development: 'bg-green-600',
    staging: 'bg-yellow-600'
  }

  return (
    
      
        {config.name.toUpperCase()} ENVIRONMENT
      
      {config.features.debug && ' - Debug Mode Enabled'}
    
  )
}

// components/ConditionalFeature.tsx
interface ConditionalFeatureProps {
  environment: 'development' | 'staging' | 'production'
  children: React.ReactNode
}

export function ConditionalFeature({ environment, children }: ConditionalFeatureProps) {
  const config = getEnvironmentConfig()

  if (config.name !== environment) {
    return null
  }

  return <>{children}</>
}

// Usage
export function AdminPanel() {
  return (
    
      Admin Panel
      
      
        
          Development Only: Debug tools and test data controls
        
      

      {/* Regular admin content */}
    
  )
}`}

## 📊 Аналітика та відстеження

### Налаштування користувацької аналітики

// lib/analytics.ts

{`export interface AnalyticsEvent {
  name: string
  properties?: Record<string, any>
  userId?: string
  tenantId?: string
  timestamp?: string
}

export class Analytics {
  private tenantId: string
  private userId?: string

  constructor(tenantId: string, userId?: string) {
    this.tenantId = tenantId
    this.userId = userId
  }

  track(event: AnalyticsEvent) {
    const enrichedEvent = {
      ...event,
      tenantId: this.tenantId,
      userId: this.userId || event.userId,
      timestamp: new Date().toISOString(),
      properties: {
        ...event.properties,
        url: window.location.href,
        userAgent: navigator.userAgent,
        referrer: document.referrer
      }
    }

    // Send to analytics service
    this.sendEvent(enrichedEvent)
  }

  private async sendEvent(event: AnalyticsEvent) {
    try {
      await fetch('/api/analytics/track', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(event)
      })
    } catch (error) {
      console.error('Analytics tracking failed:', error)
    }
  }

  // Predefined events
  trackPageView(page: string) {
    this.track({
      name: 'page_view',
      properties: { page }
    })
  }

  trackEntityCreated(entityType: string) {
    this.track({
      name: 'entity_created',
      properties: { entityType }
    })
  }

  trackOpportunityApplied(opportunityId: string, opportunityType: string) {
    this.track({
      name: 'opportunity_applied',
      properties: { opportunityId, opportunityType }
    })
  }

  trackWalletConnected(walletType: string) {
    this.track({
      name: 'wallet_connected',
      properties: { walletType }
    })
  }
}

// Analytics hook
export function useAnalytics() {
  const tenant = useTenant()
  const { data: session } = useSession()

  return new Analytics(tenant?.id || 'unknown', session?.user?.id)
}`}

---

*Готові побачити повні реалізації? Перевірте [Реальні додатки](/uk/docs/examples/real-world) або дослідіть наші приклади [Інтеграції API](/uk/docs/examples/api-integration).*
