Skip to content

Historical snapshot archived 2026-09-25. This records an earlier review or plan, not current implementation or live ticket state. For current work, follow root AGENTS.md, the relevant BloxClips skill, and owning repository source/tests. Preserve approved decisions as evidence; verify their present authority before acting.

Frontend Flow ​

All submission-related UI surfaces in the Next.js frontend.


Clipper-Facing Surfaces ​

1. Submit Video Modal ​

Route: Embedded in multiple pages
Component: features/submissions/components/SubmitVideoModal.tsx
Screen: surfaces/content-rewards/screens/submissions/SubmitVideoScreen.tsx

Users: Authenticated clipper
Purpose: Submit 1–4 videos to a campaign

Data Source:

  • Campaigns: GET /api/campaigns (fetch on mount if not passed as prop)

API Calls:

  • POST /api/submissions — per video in batch (runBatch)

Actions:

  • Select campaign (dropdown, filtered to active && acceptingSubmissions)
  • Paste video URLs (up to 4)
  • Submit batch → sequential POST per URL
  • Handle 412 ACCOUNT_VERIFICATION_REQUIRED → show AccountVerificationModal → re-submit
  • Show success toast, call onSubmitted callback

Important Components:

  • PlatformChips — shows allowed platforms per campaign
  • AccountVerificationModal — bio-code verification flow
  • SubmissionStatusBadge / SubmissionStatusIcon — status display

Status Rendering: N/A (creation only)

Permissions: Requires auth cookie (credentials: include)

Related Files:

  • features/submissions/api/submissions.ts — fetchSubmissionHistory (not used here)
  • features/auth/components/AccountVerificationModal.tsx
  • features/campaigns/components/PlatformChips.tsx

2. Submission History (Clipper Dashboard) ​

Route: /dashboard/history
Screen: surfaces/content-rewards/screens/submissions/SubmissionHistoryScreen.tsx
Hook: features/submissions/hooks/useSubmissionHistory.ts

Users: Authenticated clipper
Purpose: View own submissions with filters, pagination, summary

Data Source: GET /api/submissions?status&limit&offset

API Calls:

  • fetchSubmissionHistory({filter, page, limit}) → features/submissions/api/submissions.ts

Actions:

  • Filter by status: all/accepted/pending/denied/flagged
  • Search by video title, campaign, game, platform, status
  • Paginate (10 per page)
  • View earnings summary (views, earned, accepted count, pending count)

Components:

  • SubmissionHistoryToolbar — filter + search
  • SubmissionHistoryList → SubmissionHistoryRow
  • SubmissionHistoryPagination
  • SubmissionHistorySummary — aggregate stats
  • SubmissionStatusBadge — status pill

Status Rendering:

  • getSubmissionStatusTone(status, variant="history") → success/danger/flagged/pending/warning
  • SubmissionStatusBadge variant="history"

Permissions: Own submissions only (backend enforces via userOwnedWhere)

Related Files:

  • features/submissions/types.ts — SubmissionHistoryItem, SubmissionStatusFilter
  • features/submissions/lib/formatters.ts — getSubmissionLiveViews, formatSubmissionDate, getPlatformLabel, getVideoThumbnail

3. Overview Dashboard (Recent Submissions) ​

Route: /dashboard
Screen: surfaces/content-rewards/screens/overview/OverviewScreen.tsx
Component: surfaces/content-rewards/screens/overview/components/RecentSubmissionsPanel.tsx

Users: Authenticated clipper
Purpose: Show 4 most recent submissions

Data Source: useOverviewData() → GET /api/overview (includes submissions)

Actions: Click → opens video link in new tab

Status Rendering: SubmissionStatusBadge + SubmissionStatusIcon


4. Campaigns Screen (Submit from Campaign Card) ​

Route: /dashboard/campaigns
Screen: surfaces/content-rewards/screens/campaigns/CampaignsScreen.tsx

Users: Authenticated clipper
Purpose: Browse campaigns, open submit modal per campaign

Data Source: useCampaigns() → GET /api/campaigns

Actions: Click "Submit" on campaign → opens SubmitVideoModal with campaignId pre-selected


Admin-Facing Surfaces ​

1. Admin Submissions Queue (Global) ​

Route: /dashboard/admin/submissions
Screen: surfaces/content-rewards/screens/admin/submissions/AdminSubmissionsScreen.tsx
Hook: features/admin/submissions/hooks/useAdminSubmissions.ts

Users: Admin (requireAdmin middleware)

Purpose: Review all submissions across campaigns

Data Source: GET /api/admin/submissions/review?campaignFilter&status&sortBy&sortDirection&page

API Calls:

  • fetchAdminSubmissionsReview → features/admin/submissions/api/adminSubmissions.ts
  • submitAdminSubmissionsStatus — accept/deny/flag
  • submitAdminSubmissionsBan — ban user
  • fetchAdminSubmissionsSnapshots — view history for chart

Actions:

  • Filter by campaign, status (pending/flagged/all)
  • Sort by queue/views/earnings/date
  • Paginate (20 per page)
  • Play video preview (embed)
  • Toggle chart (view history)
  • Accept / Deny (with reason popover) / Flag (with reason popover)
  • Ban user (modal with reason + duration)

Components:

  • AdminSubmissionsQueue — table rows
  • AdminSubmissionsFilters — status, sort, campaign filter strip
  • AdminSubmissionsBanModal
  • SubmissionReviewRow (shared) — embed, chart, status badge, actions

Status Rendering: getStatusBadge() in SubmissionReviewRow.tsx:139-168

Permissions: Admin only (enforced by requireAdmin middleware)

Related Files:

  • features/admin/submissions/types.ts — filter/sort types
  • features/submissions/components/SubmissionReviewRow.tsx — shared row component

2. Campaign Detail Admin (Campaign-Scoped Review) ​

Route: /dashboard/admin/campaigns/[id]
Screen: surfaces/content-rewards/screens/admin/campaign-detail/AdminCampaignDetailScreen.tsx
Hook: features/admin/campaign-detail/hooks/useAdminCampaignDetail.ts

Users: Admin

Purpose: Review submissions for a specific campaign with budget context

Data Source: GET /api/admin/campaigns/[id]?platformFilter&statusFilter&page

API Calls:

  • fetchAdminCampaignDetail → features/admin/campaign-detail/api/adminCampaignDetail.ts
  • submitAdminCampaignSubmissionStatus — same status endpoint
  • fetchAdminCampaignSubmissionSnapshots — same snapshots endpoint

Actions: Same as global queue, plus:

  • Platform filter (YouTube/TikTok/Instagram)
  • Budget/progress header (AdminCampaignDetailHeader)
  • Campaign stats panel

Components:

  • AdminCampaignDetailHeader — budget bar, progress
  • AdminCampaignDetailFilters — status + platform + sort
  • AdminCampaignDetailSubmissions — table (uses SubmissionRow from SubmissionReviewRow)

Status Rendering: Same SubmissionReviewRow shared component


3. Admin User Detail (Submissions Tab) ​

Route: /dashboard/admin/users/[userId]
Screen: surfaces/content-rewards/screens/admin/user-detail/AdminUserDetailScreen.tsx
Component: surfaces/content-rewards/screens/admin/user-detail/components/AdminUserDetailSubmissions.tsx

Users: Admin

Purpose: View all submissions for a specific user with earnings

Data Source: GET /api/admin/users/:userId (includes submissions with earnings)

Actions:

  • Update manual view count (PUT /api/admin/submissions/:id/views)
  • Update custom rate (PUT /api/admin/submissions/:id/rate)
  • Update custom cap (PUT /api/admin/submissions/:id/cap)
  • Delete submission (DELETE /api/admin/submissions/:id)

Components:

  • AdminUserDetailSubmissions → AdminUserDetailSubmissionRow
  • Inline edit modals for views/rate/cap

4. Admin Payout Detail (Submission Context) ​

Route: /dashboard/admin/payouts/[id]
Screen: surfaces/content-rewards/screens/admin/payout-detail/AdminPayoutDetailScreen.tsx
Component: AdminPayoutDetailItemPreview.tsx, AdminPayoutDetailVideoEmbed.tsx

Users: Admin

Purpose: Review payout items (each tied to a submission) before approve/send

Data Source: GET /api/admin/payouts/review/:id (includes items with submission + snapshots)

Actions:

  • Per-item decision: APPROVE/REJECT/FLAG
  • Bulk "Approve Unflagged"
  • View submission video embed
  • View view history chart (snapshots)

Status Rendering: PayoutItem.decision badge, submission status from SubmissionReviewRow


Shared Components ​

SubmissionReviewRow.tsx ​

Path: features/submissions/components/SubmissionReviewRow.tsx
Exports: Submission, Snapshot, SubmissionRow, getSubmissionEmbedUrl, SubmissionEmbedFrame, formatReviewNumber, getPlatformIcon, getStatusBadge

Used By:

  • Admin submissions queue (AdminSubmissionsQueue)
  • Admin campaign detail (AdminCampaignDetailSubmissions)
  • Admin payout detail (AdminPayoutDetailItemPreview)

Features:

  • Video embed (YouTube/TikTok/Instagram player)
  • Expandable view history chart (Recharts line chart)
  • Status badge
  • Duplicate link warning (same videoLink across submissions)
  • Flag/Deny reason popover
  • Platform icon

SubmissionStatusBadge.tsx ​

Path: features/submissions/components/SubmissionStatusBadge.tsx
Variants: summary (default), history
Uses: getSubmissionStatusTone from lib/status.ts

SubmissionStatusIcon.tsx ​

Path: features/submissions/components/SubmissionStatusIcon.tsx
Purpose: Small icon for overview panels


Frontend → Backend Trace ​

UI ActionHook/APIBackend Endpoint
Load campaignsfetch(${API_URL}/api/campaigns)GET /api/campaigns
Submit videofetch(${API_URL}/api/submissions, {method: POST})POST /api/submissions
Load my submissionsfetchSubmissionHistory()GET /api/submissions
Load admin queuefetchAdminSubmissionsReview()GET /api/admin/submissions/review
Accept submissionsubmitAdminSubmissionsStatus(id, 'ACCEPTED')PUT /api/admin/submissions/:id/status
Deny submissionsubmitAdminSubmissionsStatus(id, 'DENIED', reason)PUT /api/admin/submissions/:id/status
Flag submissionsubmitAdminSubmissionsStatus(id, 'FLAGGED', reason)PUT /api/admin/submissions/:id/status
Load snapshotsfetchAdminSubmissionsSnapshots(id)GET /api/admin/submissions/:id/snapshots
Update manual viewsupdateAdminUserDetailSubmissionViews(id, views)PUT /api/admin/submissions/:id/views
Update custom rateupdateAdminUserDetailSubmissionRate(id, rate)PUT /api/admin/submissions/:id/rate
Update custom cap(not exposed in user-detail API yet)PUT /api/admin/submissions/:id/cap
Delete submissiondeleteAdminUserDetailSubmission(id)DELETE /api/admin/submissions/:id

Duplicated / Legacy / Dead Screens ​

ScreenStatusNotes
Discord approval buttonsRemovedCommented in submissions.ts:37-124; buttonHandler returns "use dashboard"
EmailGate in SubmitVideoModalDisabledCommented in SubmitVideoModal.tsx:285-290 and backend
Legacy paidOut flowCoexistsSubmission.paidOut + Payout (legacy statuses) still in schema; delta flow is new
SubmitVideoScreen standaloneActiveRoute /dashboard/submit exists but modal is also embedded in overview/campaigns

Permission Gaps (Frontend Hides, Backend May Not Enforce) ​

ActionFrontend GateBackend Enforcement
Submit videoAuth + campaign selectrequireAuth + campaign active check
View own submissionsAuthuserOwnedWhere (webUserId OR discordId)
Admin queueAdmin nav linkrequireAdmin middleware
Accept/Deny/FlagAdmin UI buttonsrequireAdmin + auditLog
Manual view overrideAdmin user detailrequireAdmin
Custom rate/capAdmin user detailrequireAdmin
Delete submissionAdmin user detailDELETE /api/admin/submissions/:id

Note: DELETE /api/admin/submissions/:id is called from frontend (adminUserDetail.ts:36-37) but no matching route exists in admin.ts. This is a gap.