Appearance
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→ showAccountVerificationModal→ re-submit - Show success toast, call
onSubmittedcallback
Important Components:
PlatformChips— shows allowed platforms per campaignAccountVerificationModal— bio-code verification flowSubmissionStatusBadge/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.tsxfeatures/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 + searchSubmissionHistoryList→SubmissionHistoryRowSubmissionHistoryPaginationSubmissionHistorySummary— aggregate statsSubmissionStatusBadge— status pill
Status Rendering:
getSubmissionStatusTone(status, variant="history")→ success/danger/flagged/pending/warningSubmissionStatusBadgevariant="history"
Permissions: Own submissions only (backend enforces via userOwnedWhere)
Related Files:
features/submissions/types.ts—SubmissionHistoryItem,SubmissionStatusFilterfeatures/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.tssubmitAdminSubmissionsStatus— accept/deny/flagsubmitAdminSubmissionsBan— ban userfetchAdminSubmissionsSnapshots— 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 rowsAdminSubmissionsFilters— status, sort, campaign filter stripAdminSubmissionsBanModalSubmissionReviewRow(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 typesfeatures/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.tssubmitAdminCampaignSubmissionStatus— same status endpointfetchAdminCampaignSubmissionSnapshots— 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, progressAdminCampaignDetailFilters— status + platform + sortAdminCampaignDetailSubmissions— table (usesSubmissionRowfromSubmissionReviewRow)
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 Action | Hook/API | Backend Endpoint |
|---|---|---|
| Load campaigns | fetch(${API_URL}/api/campaigns) | GET /api/campaigns |
| Submit video | fetch(${API_URL}/api/submissions, {method: POST}) | POST /api/submissions |
| Load my submissions | fetchSubmissionHistory() | GET /api/submissions |
| Load admin queue | fetchAdminSubmissionsReview() | GET /api/admin/submissions/review |
| Accept submission | submitAdminSubmissionsStatus(id, 'ACCEPTED') | PUT /api/admin/submissions/:id/status |
| Deny submission | submitAdminSubmissionsStatus(id, 'DENIED', reason) | PUT /api/admin/submissions/:id/status |
| Flag submission | submitAdminSubmissionsStatus(id, 'FLAGGED', reason) | PUT /api/admin/submissions/:id/status |
| Load snapshots | fetchAdminSubmissionsSnapshots(id) | GET /api/admin/submissions/:id/snapshots |
| Update manual views | updateAdminUserDetailSubmissionViews(id, views) | PUT /api/admin/submissions/:id/views |
| Update custom rate | updateAdminUserDetailSubmissionRate(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 submission | deleteAdminUserDetailSubmission(id) | DELETE /api/admin/submissions/:id |
Duplicated / Legacy / Dead Screens
| Screen | Status | Notes |
|---|---|---|
| Discord approval buttons | Removed | Commented in submissions.ts:37-124; buttonHandler returns "use dashboard" |
| EmailGate in SubmitVideoModal | Disabled | Commented in SubmitVideoModal.tsx:285-290 and backend |
Legacy paidOut flow | Coexists | Submission.paidOut + Payout (legacy statuses) still in schema; delta flow is new |
SubmitVideoScreen standalone | Active | Route /dashboard/submit exists but modal is also embedded in overview/campaigns |
Permission Gaps (Frontend Hides, Backend May Not Enforce)
| Action | Frontend Gate | Backend Enforcement |
|---|---|---|
| Submit video | Auth + campaign select | requireAuth + campaign active check |
| View own submissions | Auth | userOwnedWhere (webUserId OR discordId) |
| Admin queue | Admin nav link | requireAdmin middleware |
| Accept/Deny/Flag | Admin UI buttons | requireAdmin + auditLog |
| Manual view override | Admin user detail | requireAdmin |
| Custom rate/cap | Admin user detail | requireAdmin |
| Delete submission | Admin user detail | DELETE /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.