Skip to content

channel stats caching, plan-based sync limits, and dashboard architecture refactor#199

Open
GauravOP-03 wants to merge 5 commits into
creatorai-app:mainfrom
GauravOP-03:feat/youtube-dashboard-stats-sync
Open

channel stats caching, plan-based sync limits, and dashboard architecture refactor#199
GauravOP-03 wants to merge 5 commits into
creatorai-app:mainfrom
GauravOP-03:feat/youtube-dashboard-stats-sync

Conversation

@GauravOP-03
Copy link
Copy Markdown
Collaborator

Context

This PR improves the YouTube Channel Stats flow across database, backend, and frontend.

Primary goals:

  • prevent unnecessary repeated YouTube API fetches
  • reduce token refresh pressure and reconnect friction
  • introduce plan-aware usage controls (daily limit + cooldown)
  • refactor dashboard UI into maintainable feature modules

Changed File Structure (only touched paths)

apps/
  api/
    src/
      youtube/
        youtube.controller.ts
        youtube.service.ts

  web/
    app/
      dashboard/
        channel-stats/
          page.tsx
    components/
      dashboard/
        main/
          ConnectedDashboardView.tsx
          DashboardHome.tsx
          OnboardingDashboardView.tsx
          SharedComponents.tsx
          types.ts
          utils.ts
          skeleton/
            DashboardSkeleton.tsx
        channel-stats/
          ChannelStatsError.tsx
          Skeleton.tsx
          util.ts
    hooks/
      useChannelStats.ts
    tailwind.config.ts
    next.config.mjs

packages/
  supabase/
    migrations/
      20260429125432_plans_v2.sql
  validations/
    src/
      index.ts
      types/
        youtubeStatsTypes.ts


Database / Migration Changes

File

  • packages/supabase/migrations/20260429125432_plans_v2.sql

What changed

  1. plans table enhancements

    • Added daily_limit int not null default 5
    • Added cooldown_minutes int not null default 60
  2. youtube_channels caching + usage fields

    • Added top_videos jsonb
    • Added recent_videos jsonb
    • Added last_synced_at timestamptz
    • Added last_used_at timestamptz
    • Added usage_count int not null default 0
    • Added usage_reset_date date not null default current_date
    • Added youtube_trained_videos jsonb
  3. Added function: use_feature(p_user_id uuid)

    • Resolves active subscription plan limits.
    • Falls back to starter plan defaults when needed.
    • Enforces cooldown + daily limit.
    • Atomically updates usage counters.
    • Returns structured JSON result (allowed, reason, remaining, daily_limit, etc.).
  4. Added function: get_feature_usage(p_user_id uuid)

    • Returns non-mutating usage state for UI.
    • Includes plan name, usage count, remaining quota, cooldown remaining, and can_use_now.

Detailed function behavior (use_feature and get_feature_usage)

use_feature(p_user_id uuid) - write path (consume one usage)

This function is called when user performs an action that should consume quota (for example, force sync).

Step-by-step working:

  1. Resolve applicable plan limits

    • Reads active subscription for the user by joining subscriptions -> plans.
    • Picks highest daily_limit plan when multiple active rows exist.
    • If no active subscription exists, falls back to starter plan.
    • If starter row is missing, hard-falls back to defaults:
      • daily_limit = 5
      • cooldown_minutes = 60
  2. Load usage state from youtube_channels

    • Fetches usage_count, last_used_at, and usage_reset_date.
    • If usage_reset_date is from a previous day (or null), it treats usage as reset:
      • usage_count = 0
      • last_used_at = null
  3. Enforce cooldown first

    • Computes minutes since last use.
    • If current request falls inside cooldown window:
      • returns early with:
        • allowed: false
        • reason: "cooldown"
        • minutes_remaining
        • message for UI
  4. Enforce daily limit second

    • If usage_count >= daily_limit:
      • returns early with:
        • allowed: false
        • reason: "daily_limit"
        • current usage + remaining quota metadata
  5. Atomically consume one usage

    • Updates youtube_channels in one SQL update:
      • increments usage_count (or sets to 1 if day rolled over)
      • sets usage_reset_date = current_date
      • sets last_used_at = now()
    • Returns success payload:
      • allowed: true
      • plan metadata
      • updated usage + remaining quota
      • cooldown config

Why this is useful:

  • Guarantees that expensive API-triggering actions are constrained by plan policy.
  • Keeps all quota decisions centralized in DB, reducing backend branching complexity.
  • Prevents over-consumption during concurrent requests via single update semantics.
  • Returns structured denial reasons so frontend can show exact UX state.

get_feature_usage(p_user_id uuid) - read path (status only)

This function is called when frontend only needs current usage status (without consuming quota).

Step-by-step working:

  1. Resolves plan limits using the exact same fallback chain:
    • active subscription -> starter plan -> hard-coded defaults.
  2. Reads usage_count, last_used_at, and usage_reset_date from youtube_channels.
  3. Applies same day-rollover logic:
    • if reset date is old/null, usage is treated as fresh (0).
  4. Computes cooldown remaining in minutes:
    • max(cooldown_minutes - minutes_since_last_used, 0).
  5. Returns UI-friendly status object:
    • plan, usage_count, remaining, daily_limit,
    • cooldown_minutes, cooldown_remaining,
    • can_use_now (true only when cooldown elapsed and daily limit not reached).

Why this is useful:

  • Zero-side-effect endpoint for dashboard polling/rendering.
  • Enables deterministic button states (Sync now vs disabled with reason).
  • Avoids accidental quota consumption on page refresh.
  • Keeps read and write concerns separated for reliability and observability.

Combined design benefit

Using both functions together creates a clean control loop:

  • Read: get_feature_usage tells frontend whether action is currently available.
  • Write: use_feature is the single authority that actually consumes quota.

This pattern improves correctness, UX clarity, and long-term maintainability.

Why these decisions and their benefits

Plan-level config in plans (daily_limit, cooldown_minutes)

  • Makes limits configurable per plan without redeploying backend code.
  • Supports pricing/entitlement evolution with minimal risk.
  • Keeps business policy data-driven and centralized in DB.

Channel stats caching in youtube_channels

  • Avoids calling YouTube API on every page visit.
  • Improves response time and dashboard reliability.
  • Reduces repeated token usage and potential token-expiry reconnect prompts.
  • Lowers external API dependency and request volume.

use_feature + get_feature_usage split

  • Clean separation of concerns:
    • use_feature: permission check + usage consume
    • get_feature_usage: read-only status
  • Safer frontend interactions and easier debugging.
  • Predictable behavior for button state and sync workflows.

Migration execution status

  • Migration has already been executed successfully in the production database.
  • All schema changes and DB functions in 20260429125432_plans_v2.sql are live in production.

Backend (NestJS) Changes

Files

  • apps/api/src/youtube/youtube.controller.ts
  • apps/api/src/youtube/youtube.service.ts

What changed

  • Added/expanded YouTube channel stats endpoint flow with force sync support.
  • Integrated usage gating via DB functions (use_feature, get_feature_usage).
  • Implemented channel analytics aggregation (top/recent videos with enriched stats).
  • Persisted synced analytics into cached DB columns.
  • Kept token lifecycle resilient via access token validation + refresh fallback.
  • Added trained videos read/write support on youtube_channels.

Important bug fix included

  • Force sync response now returns custom_url, country, default_language, and thumbnail from the updated selected row (not from token-only prefetch), preventing undefined metadata in sync response.

Frontend Changes

Dashboard refactor (main experience)

Files

  • apps/web/components/dashboard/main/DashboardHome.tsx
  • apps/web/components/dashboard/main/ConnectedDashboardView.tsx
  • apps/web/components/dashboard/main/OnboardingDashboardView.tsx
  • apps/web/components/dashboard/main/SharedComponents.tsx
  • apps/web/components/dashboard/main/types.ts
  • apps/web/components/dashboard/main/utils.ts
  • apps/web/components/dashboard/main/skeleton/DashboardSkeleton.tsx

What changed

  • Split large dashboard rendering into state-based views:
    • onboarding view
    • connected view
  • Extracted reusable UI sections into SharedComponents.
  • Centralized shared types/constants/utilities.
  • Improved loading skeleton parity with final layout.

Design pattern followed

  • Container + Presentational:
    • DashboardHome orchestrates state and routing decisions.
    • ConnectedDashboardView and OnboardingDashboardView focus on UI rendering.
  • Feature-local modularization:
    • related UI/types/helpers grouped under main/.
  • Composition over duplication:
    • shared cards/headers/actions reused through shared modules.

Benefits

  • Better maintainability and faster feature iteration.
  • Lower cognitive load for future contributors.
  • Cleaner separation of business state from visual structure.
  • Reduced UI duplication and easier consistency enforcement.

Channel Stats Frontend Feature

Files

  • apps/web/app/dashboard/channel-stats/page.tsx
  • apps/web/components/dashboard/channel-stats/ChannelStatsError.tsx
  • apps/web/components/dashboard/channel-stats/Skeleton.tsx
  • apps/web/components/dashboard/channel-stats/util.ts
  • apps/web/hooks/useChannelStats.ts
  • apps/web/next.config.mjs

What changed

  • Built/refined dedicated channel stats page.
  • Extracted feature-specific error and loading components.
  • Added channel-stats scoped utility functions.
  • Added useChannelStats hook for fetch/loading/error orchestration.
  • Updated API client usage paths and supporting constants/config.

Benefits

  • Better UX for loading/error/sync states.
  • Cleaner feature boundary and easier testability.
  • Reusable data fetching abstraction for channel stats workflows.
  • More robust and maintainable stats page architecture.

Validation Package Updates

Files

  • packages/validations/src/types/youtubeStatsTypes.ts
  • packages/validations/src/index.ts

What changed

  • Added shared TypeScript types for YouTube stats payloads.
  • Exported new types via package index for cross-app usage.

Benefits

  • Contract consistency across backend and frontend.
  • Better compile-time safety and fewer payload mismatch bugs.
  • Simplifies future endpoint/UI expansion.

User Impact

  • Faster, more reliable channel stats experience.
  • Fewer unnecessary YouTube API calls and token churn.
  • Transparent sync eligibility via quota/cooldown.
  • Cleaner, modular dashboard architecture for ongoing enhancements.

Suggested Test Plan

  • Connect YouTube and verify onboarding/connected dashboard transitions.
  • Open channel stats page:
    • skeleton -> populated state renders correctly
    • error component appears on API failure
  • Trigger force sync repeatedly:
    • cooldown enforcement works
    • daily limit enforcement works
  • Verify non-force requests serve cached data.
  • Confirm force sync response returns:
    • custom_url
    • country
    • default_language
    • thumbnail
  • Validate plan-level changes by updating plans.daily_limit / plans.cooldown_minutes.

@GauravOP-03 GauravOP-03 requested a review from afrinxnahar April 29, 2026 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant