🗺️
flow-overview.md
flowchart TD
    V(["🌐 Visitor"])
    LP["Landing Page"]
    REG["Register Account"]
    LOG["Login"]
    EV["Email Verification"]
    ROLE{"Role?"}

    V --> LP
    LP --> REG
    LP --> LOG
    REG --> EV
    EV --> ROLE
    LOG --> ROLE

    ROLE -->|ADMIN| ADM_DASH
    ROLE -->|ADVERTISER| ADV_DASH
    ROLE -->|PUBLISHER| PUB_DASH

    subgraph ADMIN ["🛡️ Admin Portal"]
        ADM_DASH["Admin Dashboard"]
        ADM_DASH --> UM["User Management"]
        ADM_DASH --> SM["Screen Management"]
        ADM_DASH --> CM["Campaign Oversight"]
        ADM_DASH --> MA["Media Approval"]
        ADM_DASH --> PS["Payment Settings"]
        ADM_DASH --> PA["Platform Analytics"]
        ADM_DASH --> DSP["DSP / Programmatic Ads"]
        ADM_DASH --> LC["Language & Location Config"]
        ADM_DASH --> EB["Early Bird & Branding"]
    end

    subgraph ADVERTISER ["📢 Advertiser Portal"]
        ADV_DASH["Advertiser Dashboard"]
        ADV_DASH --> BAL["Balance & Top-up"]
        BAL --> CHKOUT["Payment Checkout"]
        ADV_DASH --> MC["My Campaigns"]
        MC --> CC["Create Campaign"]
        CC --> MU["Upload Media / Creative Studio"]
        CC --> SS["Select Screens & Schedule"]
        ADV_DASH --> ADVANA["Campaign Analytics"]
        ADV_DASH --> TH["Transaction History"]
    end

    subgraph PUBLISHER ["📺 Publisher Portal"]
        PUB_DASH["Publisher Dashboard"]
        PUB_DASH --> RS["Register Screens"]
        PUB_DASH --> SR["Set Screen Rates"]
        PUB_DASH --> EARN["Earnings & Payouts"]
        PUB_DASH --> PUBANA["Screen Analytics"]
        PUB_DASH --> USB["USB Export for Offline Screens"]
    end

    subgraph SCREEN ["🖥️ TV Screen App"]
        HB["Heartbeat Check-in (60s)"]
        HB --> FETCH["Fetch Active Campaigns"]
        FETCH --> PLAY["Play Ad Creatives"]
        PLAY --> SYNC["Sync View Analytics"]
        SYNC --> HB
    end

    RS -.->|"Screen goes live (activation code)"| HB
    CC -.->|"Campaign approved & scheduled"| FETCH

    style ADMIN fill:#fff3e0,stroke:#ef6c00
    style ADVERTISER fill:#e3f2fd,stroke:#1565c0
    style PUBLISHER fill:#e8f5e9,stroke:#2e7d32
    style SCREEN fill:#fce4ec,stroke:#880e4f
        

⚙️
flow-architecture.md
flowchart TB
    subgraph CLIENTS ["⬛ Client Layer"]
        direction LR
        BROWSER["React SPA\nVite + Ant Design + Redux Toolkit"]
        TVAPP["TV / Screen App\nAndroid / Web player"]
    end

    subgraph NGINX_LAYER ["🔀 Reverse Proxy"]
        NGINX["Nginx  :80 / :443  SSL termination"]
    end

    subgraph API ["⚙️ NestJS API — Express :3001"]
        direction TB

        subgraph AUTH_USERS ["Auth & Identity"]
            AUTH["AuthModule\nJWT · Passport · Bcrypt · Email verify"]
            USERS["UsersModule\nAdmin · Advertiser · Publisher"]
        end

        subgraph CONTENT ["Content & Media"]
            MEDIA["MediaModule\nMulter upload · Admin approval"]
        end

        subgraph AD_CORE ["Advertising Core"]
            CAMPAIGNS["CampaignsModule\nCreate · Schedule · Status · M:N screens"]
            SCREENS["ScreensModule\nRegistration · Search · Rate lookup"]
            CHARGING["ChargingModule\nRate engine · Slot deductions · Publisher credit"]
            PROGRAMMATIC["ProgrammaticModule\nDSP · RTB bidding · Impressions"]
        end

        subgraph DELIVERY ["Screen Delivery"]
            TVMOD["TvAppModule\nHeartbeat · Campaign feed · Override"]
            USBMOD["UsbExportModule\nOffline package builder"]
            ANALYTICS["AnalyticsModule\nViews · CTR · System / Campaign reports"]
        end

        subgraph PLATFORM ["Platform Services"]
            PAYMENTS["PaymentModule\nCheckout · Webhooks · Multi-gateway"]
            NOTIF["NotificationsModule\nIn-app · Email · Bull queue"]
            LOGGING["LoggingModule\nActivity audit trail"]
            AICHAT["AiChatModule\nClaude AI assistant"]
            I18N["I18nModule\nLanguages · Translations"]
            LOC["LocationsModule\nCities · Regions · Venues"]
            BRAND["BrandingModule\nLogo · Theme"]
            LANDING["LandingModule\nPublic site · Contact forms"]
        end
    end

    subgraph INFRA ["🗄️ Infrastructure"]
        direction LR
        MYSQL[("MySQL 8\nPrisma ORM — 40 models")]
        REDIS[("Redis 7\nBull queues · Cache")]
        FILES[/"uploads/media/\nDocker named volume"/]
    end

    subgraph EXTERNAL ["🌐 External Services"]
        direction LR
        PAYEXT["Payment Gateways\nStripe · Razorpay"]
        SMTP["SMTP\n(Ethereal in dev)"]
        CLAUDE["Claude AI API\nAnthropic"]
    end

    BROWSER -->|"HTTPS REST · Bearer JWT"| NGINX
    TVAPP -->|"Screen Token"| NGINX
    NGINX --> API

    API --> MYSQL
    API --> REDIS
    MEDIA --> FILES
    PAYMENTS <-->|"Checkout + Webhook"| PAYEXT
    AUTH -->|"Verification email"| SMTP
    NOTIF -->|"Email alerts"| SMTP
    AICHAT <-->|"Messages API"| CLAUDE

    style CLIENTS fill:#e8eaf6,stroke:#3949ab
    style NGINX_LAYER fill:#f3e5f5,stroke:#6a1b9a
    style API fill:#e0f2f1,stroke:#00695c
    style INFRA fill:#fff8e1,stroke:#f57f17
    style EXTERNAL fill:#fce4ec,stroke:#880e4f
        

🔐
sequence-auth.md
sequenceDiagram
    actor U as User (Browser)
    participant FE as React Frontend
    participant API as NestJS AuthModule
    participant DB as MySQL
    participant MAIL as SMTP Service

    rect rgb(232, 245, 233)
    Note over U,MAIL: REGISTRATION
    U->>FE: Fill register form (email, password, role)
    FE->>API: POST /api/auth/register
    API->>DB: Check email uniqueness
    DB-->>API: OK
    API->>API: bcrypt.hash(password, 10)
    API->>DB: INSERT User { email, hash, role, emailVerified: false }
    API->>MAIL: Send verification email (token link)
    API-->>FE: 201 { message: "Check your inbox" }
    FE-->>U: Show verify notice
    end

    rect rgb(227, 242, 253)
    Note over U,MAIL: EMAIL VERIFICATION
    U->>FE: Click verification link
    FE->>API: GET /api/auth/verify-email?token=xxx
    API->>DB: Validate token → SET emailVerified=true
    API-->>FE: 200 { message: "Email verified" }
    FE-->>U: Redirect to login
    end

    rect rgb(255, 248, 225)
    Note over U,MAIL: LOGIN
    U->>FE: Enter email + password
    FE->>API: POST /api/auth/login
    API->>DB: Find user by email
    DB-->>API: User record
    API->>API: bcrypt.compare(password, hash)
    alt Invalid credentials
        API-->>FE: 401 Unauthorized
        FE-->>U: Show error
    else Valid credentials
        API->>API: Sign accessToken (JWT 24h) + refreshToken (7d)
        API-->>FE: 200 { accessToken, refreshToken, user }
        FE->>FE: Store tokens, dispatch Redux auth.setUser
        Note right of FE: ADMIN → /admin/dashboard\nADVERTISER → /advertiser/dashboard\nPUBLISHER → /publisher/dashboard
        FE-->>U: Redirect by role
    end
    end

    rect rgb(252, 228, 236)
    Note over U,MAIL: AUTHENTICATED REQUESTS
    U->>FE: Navigate to protected page
    FE->>API: GET /api/* { Authorization: Bearer accessToken }
    API->>API: JwtAuthGuard validates token
    API->>API: RolesGuard checks user.role vs @Roles()
    alt Token expired
        API-->>FE: 401
        FE->>API: POST /api/auth/refresh { refreshToken }
        API-->>FE: New accessToken
        FE->>API: Retry original request
    else Role forbidden
        API-->>FE: 403 Forbidden
        FE-->>U: Redirect to /unauthorized
    else Valid
        API-->>FE: 200 ResponseHelper.success(data)
        FE-->>U: Render page
    end
    end

    rect rgb(237, 231, 246)
    Note over U,MAIL: SCREEN LOGIN (TV App)
    participant SCREEN as TV Screen App
    SCREEN->>API: POST /api/auth/screen-login { activationCode }
    API->>DB: Validate screen_codes WHERE code=? AND used=false
    API->>DB: UPDATE Screen SET status=ACTIVE
    API->>API: Sign screenToken (JWT, type: screen)
    API-->>SCREEN: 200 { screenToken, screenConfig }
    end
        

📋
sequence-campaign.md
sequenceDiagram
    actor ADV as Advertiser
    actor ADM as Admin
    participant FE as React Frontend
    participant API as NestJS API
    participant DB as MySQL
    participant REDIS as Redis (Bull Queue)
    participant STORE as File Storage
    participant TV as TV Screen App

    rect rgb(227, 242, 253)
    Note over ADV,TV: STEP 1 — MEDIA UPLOAD
    ADV->>FE: Upload creative (image/video)
    FE->>API: POST /api/media/upload (multipart/form-data)
    API->>STORE: Multer saves to uploads/media/uuid.ext
    API->>DB: INSERT Media { status: PENDING, advertiserId }
    API-->>FE: 201 { mediaId, status: PENDING }
    FE-->>ADV: "Awaiting admin approval"
    end

    rect rgb(255, 248, 225)
    Note over ADV,TV: STEP 2 — ADMIN APPROVAL
    ADM->>FE: Open Media Approval panel
    FE->>API: GET /api/media/pending
    API->>DB: SELECT Media WHERE status=PENDING
    API-->>FE: Pending media list
    ADM->>FE: Click Approve
    FE->>API: PATCH /api/media/:id { status: APPROVED }
    API->>DB: UPDATE Media SET status=APPROVED
    API->>DB: INSERT Notification for advertiser
    API-->>FE: 200 OK
    end

    rect rgb(232, 245, 233)
    Note over ADV,TV: STEP 3 — CAMPAIGN CREATION
    ADV->>FE: Create new campaign
    FE->>API: GET /api/screens (search/filter by city, venue)
    API->>DB: SELECT Screens WHERE active=true + filters
    API-->>FE: Screen list with rates
    ADV->>FE: Select screens, set budget + schedule
    FE->>API: POST /api/campaigns { mediaId, screenIds[], startDate, endDate, budget }
    API->>DB: Check walletBalance >= estimatedCost
    alt Insufficient balance
        API-->>FE: 402 Insufficient balance
        FE-->>ADV: Prompt to top up
    else Balance OK
        API->>DB: INSERT Campaign { status: SCHEDULED }
        API->>DB: INSERT CampaignScreens[] (one row per screen)
        API->>REDIS: scheduleJob("activateCampaign", { campaignId }, delay)
        API-->>FE: 201 { campaignId, status: SCHEDULED }
    end
    end

    rect rgb(252, 228, 236)
    Note over ADV,TV: STEP 4 — ACTIVATION & CHARGING
    REDIS->>API: Bull job fires at startDate
    API->>DB: UPDATE Campaign SET status=ACTIVE
    loop Every ad play (reported by TV App)
        API->>DB: SELECT ScreenRate + walletBalance
        alt Balance depleted
            API->>DB: UPDATE Campaign SET status=PAUSED
            API->>DB: INSERT Notification (low balance alert)
        else OK
            API->>DB: INSERT ChargingTransaction (DEBIT advertiser)
            API->>DB: UPDATE walletBalance -= amount
            API->>DB: INSERT ChargingTransaction (CREDIT publisher)
        end
    end
    REDIS->>API: Bull job fires at endDate
    API->>DB: UPDATE Campaign SET status=COMPLETED
    end

    rect rgb(237, 231, 246)
    Note over ADV,TV: STEP 5 — TV DELIVERY
    TV->>API: GET /api/tv/screens/:id/campaigns
    API->>DB: SELECT active campaigns WHERE screenId AND schedule matches NOW()
    API-->>TV: [{ mediaUrl, duration, order }]
    loop Campaign rotation
        TV->>STORE: GET /uploads/media/filename
        TV->>TV: Display ad for durationSeconds
    end
    end

    rect rgb(255, 243, 224)
    Note over ADV,TV: STEP 6 — ANALYTICS
    TV->>API: POST /api/tv/screens/:id/analytics/sync { plays[] }
    API->>DB: INSERT Analytics records (bulk)
    ADV->>FE: View Campaign Analytics
    FE->>API: GET /api/analytics/campaigns/:id
    API->>DB: Aggregate views, spend, reach by screen + date
    API-->>FE: { totalViews, totalSpend, dailyTrend[], screenBreakdown[] }
    FE-->>ADV: Charts and stats
    end
        

🖥️
sequence-screen.md
sequenceDiagram
    actor PUB as Publisher
    participant FE as React Frontend
    participant API as NestJS API
    participant DB as MySQL
    participant STORE as File Storage
    participant SCR as Screen Device (TV App)

    rect rgb(232, 245, 233)
    Note over PUB,SCR: STEP 1 — SCREEN REGISTRATION
    PUB->>FE: Add Screen (name, city, venue, size)
    FE->>API: POST /api/publisher/screens
    API->>DB: INSERT Screen { status: PENDING_ACTIVATION }
    API->>DB: INSERT screen_codes { code: random 6-char }
    API-->>FE: 201 { screenId, activationCode: "ABC123" }
    FE-->>PUB: Show activation code + QR
    end

    rect rgb(227, 242, 253)
    Note over PUB,SCR: STEP 2 — TV APP ACTIVATION
    SCR->>API: POST /api/auth/screen-login { activationCode }
    API->>DB: Validate screen_codes WHERE code=? AND used=false
    API->>DB: UPDATE screen_codes SET used=true
    API->>DB: UPDATE Screen SET status=ACTIVE
    API->>API: Sign screenToken = jwt.sign({ sub: screenId, type: screen })
    API-->>SCR: 200 { screenToken, config }
    SCR->>SCR: Persist screenToken
    end

    rect rgb(255, 248, 225)
    Note over PUB,SCR: STEP 3 — RATE SETUP
    PUB->>FE: Set pricing for screen
    FE->>API: POST /api/charging/rates { screenId, pricePerSlot, slotDurationSeconds }
    API->>DB: INSERT ScreenRate { isActive: true }
    API-->>FE: 201 rate saved
    FE-->>PUB: "Screen now visible to advertisers with pricing"
    end

    rect rgb(252, 228, 236)
    Note over PUB,SCR: STEP 4 — HEARTBEAT LOOP
    loop Every 60 seconds
        SCR->>API: POST /api/tv/screens/:id/heartbeat { status, deviceInfo }
        API->>DB: UPSERT HeartbeatLog { timestamp: NOW() }
        API->>DB: UPDATE Screen SET lastSeenAt=NOW()
        API->>DB: SELECT pending commands for screen
        API-->>SCR: 200 { commands: [], configUpdate: null }
        SCR->>SCR: Process returned commands
    end
    end

    rect rgb(237, 231, 246)
    Note over PUB,SCR: STEP 5 — CAMPAIGN FETCH & PLAYBACK
    SCR->>API: GET /api/tv/screens/:id/campaigns
    API->>DB: SELECT active campaigns WHERE screenId AND NOW() in schedule
    API-->>SCR: [{ campaignId, mediaUrl, type, durationSeconds }]
    loop Campaign rotation
        SCR->>SCR: Check local media cache
        alt Not cached
            SCR->>API: GET /uploads/media/filename
            API->>STORE: Read file
            STORE-->>API: Binary data
            API-->>SCR: Media file
            SCR->>SCR: Cache locally
        end
        SCR->>SCR: Display ad for durationSeconds
    end
    end

    rect rgb(255, 243, 224)
    Note over PUB,SCR: STEP 6 — ANALYTICS SYNC
    loop Every 5 minutes
        SCR->>API: POST /api/tv/screens/:id/analytics/sync { plays[] }
        API->>DB: INSERT Analytics[] bulk
        API->>DB: UPDATE Campaign totalViews
        API-->>SCR: 200 { synced: N }
        SCR->>SCR: Clear local play log
    end
    end
        

💳
sequence-payments.md
sequenceDiagram
    actor ADV as Advertiser
    actor ADM as Admin
    participant FE as React Frontend
    participant API as NestJS PaymentModule
    participant GW as Payment Gateway (Stripe/Razorpay)
    participant DB as MySQL
    participant NOTIF as NotificationsModule
    actor PUB as Publisher

    rect rgb(227, 242, 253)
    Note over ADV,PUB: STEP 1 — CHOOSE GATEWAY
    ADV->>FE: Open "Add Funds" → select amount
    FE->>API: GET /api/payments/gateways
    API->>DB: SELECT PaymentGateway WHERE isActive=true
    API-->>FE: Active gateways list
    ADV->>FE: Select gateway + confirm amount
    end

    rect rgb(232, 245, 233)
    Note over ADV,PUB: STEP 2 — CHECKOUT SESSION
    FE->>API: POST /api/payments/create-checkout-session { amount, gatewayId }
    API->>DB: INSERT PaymentTransaction { status: PENDING, reference: uuid }
    API->>GW: Create payment session
    GW-->>API: { sessionId, checkoutUrl }
    API-->>FE: { checkoutUrl }
    FE-->>ADV: Redirect to gateway checkout
    end

    rect rgb(255, 248, 225)
    Note over ADV,PUB: STEP 3 — USER PAYS ON GATEWAY
    ADV->>GW: Complete payment (card + 3DS)
    GW-->>ADV: Success page
    ADV->>FE: Redirect to /payment/success
    end

    rect rgb(252, 228, 236)
    Note over ADV,PUB: STEP 4 — WEBHOOK (server-to-server)
    GW->>API: POST /api/payments/webhook/:gateway (signed)
    API->>API: Verify HMAC-SHA256 signature
    alt Signature invalid
        API-->>GW: 400 Bad Request
    else Already processed (idempotency)
        API-->>GW: 200 OK (no-op)
    else New payment
        API->>DB: UPDATE PaymentTransaction SET status=COMPLETED
        API->>DB: UPDATE User SET walletBalance += amount
        API->>DB: INSERT Transaction { type: CREDIT }
        API->>NOTIF: Notify advertiser
        NOTIF-->>ADV: "₹X added to your balance"
        API-->>GW: 200 OK
    end
    end

    rect rgb(237, 231, 246)
    Note over ADV,PUB: STEP 5 — CAMPAIGN CHARGING (per play)
    loop Per ad play event
        API->>DB: SELECT ScreenRate + walletBalance
        alt Balance < pricePerSlot
            API->>DB: UPDATE Campaign SET status=PAUSED
            NOTIF-->>ADV: "Campaign paused: low balance"
        else OK
            API->>DB: INSERT ChargingTransaction (DEBIT advertiser)
            API->>DB: UPDATE walletBalance -= pricePerSlot
            API->>DB: INSERT ChargingTransaction (CREDIT publisher)
        end
    end
    end

    rect rgb(255, 243, 224)
    Note over ADV,PUB: STEP 6 — PUBLISHER PAYOUT
    PUB->>FE: Request payout (amount)
    FE->>API: POST /api/publisher/payout-request
    API->>DB: INSERT PayoutRequest { status: PENDING }
    ADM->>FE: Review payout requests
    ADM->>FE: Approve payout
    FE->>API: PATCH /api/publisher/payout-requests/:id { status: APPROVED }
    API->>DB: UPDATE PayoutRequest SET status=APPROVED
    API->>DB: UPDATE Store earnings -= amount
    NOTIF-->>PUB: "Payout of ₹X approved"
    end