From da29a2a7233b08ee3c040e8f94815c461e64a9ac Mon Sep 17 00:00:00 2001 From: jlightner Date: Sat, 4 Apr 2026 00:13:48 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Replaced=203=20placeholder=20cards=20wi?= =?UTF-8?q?th=20real=20creator=20dashboard:=204=20stat=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "frontend/src/api/creator-dashboard.ts" - "frontend/src/pages/CreatorDashboard.tsx" - "frontend/src/pages/CreatorDashboard.module.css" - "frontend/src/api/index.ts" GSD-Task: S02/T02 --- .gsd/milestones/M020/slices/S02/S02-PLAN.md | 2 +- .../M020/slices/S02/tasks/T01-VERIFY.json | 9 + .../M020/slices/S02/tasks/T02-SUMMARY.md | 83 ++++++ frontend/src/api/creator-dashboard.ts | 32 +++ frontend/src/api/index.ts | 1 + .../src/pages/CreatorDashboard.module.css | 265 +++++++++++++++++- frontend/src/pages/CreatorDashboard.tsx | 261 +++++++++++++++-- frontend/tsconfig.app.tsbuildinfo | 2 +- 8 files changed, 624 insertions(+), 31 deletions(-) create mode 100644 .gsd/milestones/M020/slices/S02/tasks/T01-VERIFY.json create mode 100644 .gsd/milestones/M020/slices/S02/tasks/T02-SUMMARY.md create mode 100644 frontend/src/api/creator-dashboard.ts diff --git a/.gsd/milestones/M020/slices/S02/S02-PLAN.md b/.gsd/milestones/M020/slices/S02/S02-PLAN.md index 117a92d..57f268e 100644 --- a/.gsd/milestones/M020/slices/S02/S02-PLAN.md +++ b/.gsd/milestones/M020/slices/S02/S02-PLAN.md @@ -31,7 +31,7 @@ Constraints: - Estimate: 45m - Files: backend/routers/creator_dashboard.py, backend/schemas.py, backend/main.py - Verify: curl -s -H 'Authorization: Bearer $TOKEN' http://localhost:8000/api/v1/creator/dashboard | python3 -m json.tool — returns JSON with video_count, technique_count, key_moment_count, search_impressions, techniques (array), videos (array). Unauthenticated request returns 401. -- [ ] **T02: Replace placeholder dashboard with real stats and content library** — Replace the three placeholder cards in CreatorDashboard.tsx with real data from the new endpoint. Add the API module, types, and all frontend rendering. +- [x] **T02: Replaced 3 placeholder cards with real creator dashboard: 4 stat cards, techniques table with category badges, videos table with status badges, loading/error/empty states, and responsive mobile layout** — Replace the three placeholder cards in CreatorDashboard.tsx with real data from the new endpoint. Add the API module, types, and all frontend rendering. Steps: 1. Create `frontend/src/api/creator-dashboard.ts` with TypeScript types matching the backend response schema and a `fetchCreatorDashboard()` function using the shared `request()` helper from `client.ts` diff --git a/.gsd/milestones/M020/slices/S02/tasks/T01-VERIFY.json b/.gsd/milestones/M020/slices/S02/tasks/T01-VERIFY.json new file mode 100644 index 0000000..d578ee6 --- /dev/null +++ b/.gsd/milestones/M020/slices/S02/tasks/T01-VERIFY.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": 1, + "taskId": "T01", + "unitId": "M020/S02/T01", + "timestamp": 1775261359844, + "passed": true, + "discoverySource": "none", + "checks": [] +} diff --git a/.gsd/milestones/M020/slices/S02/tasks/T02-SUMMARY.md b/.gsd/milestones/M020/slices/S02/tasks/T02-SUMMARY.md new file mode 100644 index 0000000..3efd5de --- /dev/null +++ b/.gsd/milestones/M020/slices/S02/tasks/T02-SUMMARY.md @@ -0,0 +1,83 @@ +--- +id: T02 +parent: S02 +milestone: M020 +provides: [] +requires: [] +affects: [] +key_files: ["frontend/src/api/creator-dashboard.ts", "frontend/src/pages/CreatorDashboard.tsx", "frontend/src/pages/CreatorDashboard.module.css", "frontend/src/api/index.ts"] +key_decisions: ["Used ?? '' fallback for CSS module class lookups to satisfy noUncheckedIndexedAccess", "Separate desktop table and mobile card views toggled via CSS media queries"] +patterns_established: [] +drill_down_paths: [] +observability_surfaces: [] +duration: "" +verification_result: "TypeScript check (tsc --noEmit) passed with zero errors. TypeScript build (tsc -b) passed. Vite production build (npm run build) succeeded — 88 modules transformed, bundle produced." +completed_at: 2026-04-04T00:13:32.094Z +blocker_discovered: false +--- + +# T02: Replaced 3 placeholder cards with real creator dashboard: 4 stat cards, techniques table with category badges, videos table with status badges, loading/error/empty states, and responsive mobile layout + +> Replaced 3 placeholder cards with real creator dashboard: 4 stat cards, techniques table with category badges, videos table with status badges, loading/error/empty states, and responsive mobile layout + +## What Happened +--- +id: T02 +parent: S02 +milestone: M020 +key_files: + - frontend/src/api/creator-dashboard.ts + - frontend/src/pages/CreatorDashboard.tsx + - frontend/src/pages/CreatorDashboard.module.css + - frontend/src/api/index.ts +key_decisions: + - Used ?? '' fallback for CSS module class lookups to satisfy noUncheckedIndexedAccess + - Separate desktop table and mobile card views toggled via CSS media queries +duration: "" +verification_result: passed +completed_at: 2026-04-04T00:13:32.095Z +blocker_discovered: false +--- + +# T02: Replaced 3 placeholder cards with real creator dashboard: 4 stat cards, techniques table with category badges, videos table with status badges, loading/error/empty states, and responsive mobile layout + +**Replaced 3 placeholder cards with real creator dashboard: 4 stat cards, techniques table with category badges, videos table with status badges, loading/error/empty states, and responsive mobile layout** + +## What Happened + +Created the API module with TypeScript types matching the backend CreatorDashboardResponse and a fetchCreatorDashboard() function. Rewrote CreatorDashboard.tsx to fetch real data on mount and render 4 stat cards, a techniques table with linked titles and category badges, a videos table with processing status badges, loading skeleton, error state, and not-linked empty state. Updated CSS module with stat card styles, data tables, badge variants for both processing status and topic category, mobile card layout, and responsive breakpoints. Fixed noUncheckedIndexedAccess strict mode issues with ?? fallback on CSS module class lookups. + +## Verification + +TypeScript check (tsc --noEmit) passed with zero errors. TypeScript build (tsc -b) passed. Vite production build (npm run build) succeeded — 88 modules transformed, bundle produced. + +## Verification Evidence + +| # | Command | Exit Code | Verdict | Duration | +|---|---------|-----------|---------|----------| +| 1 | `cd frontend && npx tsc --noEmit` | 0 | ✅ pass | 4000ms | +| 2 | `cd frontend && npx tsc -b` | 0 | ✅ pass | 3500ms | +| 3 | `cd frontend && npm run build` | 0 | ✅ pass | 4800ms | + + +## Deviations + +Added ?? '' fallback to CSS module class lookups to satisfy noUncheckedIndexedAccess: true in tsconfig — not anticipated in plan. + +## Known Issues + +None. + +## Files Created/Modified + +- `frontend/src/api/creator-dashboard.ts` +- `frontend/src/pages/CreatorDashboard.tsx` +- `frontend/src/pages/CreatorDashboard.module.css` +- `frontend/src/api/index.ts` + + +## Deviations +Added ?? '' fallback to CSS module class lookups to satisfy noUncheckedIndexedAccess: true in tsconfig — not anticipated in plan. + +## Known Issues +None. diff --git a/frontend/src/api/creator-dashboard.ts b/frontend/src/api/creator-dashboard.ts new file mode 100644 index 0000000..e8ea572 --- /dev/null +++ b/frontend/src/api/creator-dashboard.ts @@ -0,0 +1,32 @@ +import { request, BASE } from "./client"; + +// ── Types ──────────────────────────────────────────────────────────────────── + +export interface CreatorDashboardTechnique { + title: string; + slug: string; + topic_category: string; + created_at: string; + key_moment_count: number; +} + +export interface CreatorDashboardVideo { + filename: string; + processing_status: string; + created_at: string; +} + +export interface CreatorDashboardResponse { + video_count: number; + technique_count: number; + key_moment_count: number; + search_impressions: number; + techniques: CreatorDashboardTechnique[]; + videos: CreatorDashboardVideo[]; +} + +// ── Functions ──────────────────────────────────────────────────────────────── + +export async function fetchCreatorDashboard(): Promise { + return request(`${BASE}/creator/dashboard`); +} diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index eb78d5e..a4b2d58 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -13,3 +13,4 @@ export * from "./reports"; export * from "./admin-pipeline"; export * from "./admin-techniques"; export * from "./auth"; +export * from "./creator-dashboard"; diff --git a/frontend/src/pages/CreatorDashboard.module.css b/frontend/src/pages/CreatorDashboard.module.css index f0f4afe..50e1bfc 100644 --- a/frontend/src/pages/CreatorDashboard.module.css +++ b/frontend/src/pages/CreatorDashboard.module.css @@ -76,33 +76,263 @@ color: var(--color-text-primary); } -.cards { +/* ── Stats row ─────────────────────────────────────────────────────────────── */ + +.statsRow { display: grid; - grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + grid-template-columns: repeat(4, 1fr); gap: 1rem; + margin-bottom: 2rem; } -.card { +.statCard { background: var(--color-bg-surface); border: 1px solid var(--color-border); border-radius: 10px; padding: 1.25rem; + display: flex; + flex-direction: column; + gap: 0.25rem; + min-height: 80px; } -.cardTitle { - font-size: 0.875rem; +.statValue { + font-size: 1.75rem; + font-weight: 700; + color: var(--color-text-primary); + font-variant-numeric: tabular-nums; + line-height: 1.1; +} + +.statLabel { + font-size: 0.8125rem; + font-weight: 500; + color: var(--color-text-muted); + text-transform: uppercase; + letter-spacing: 0.04em; +} + +/* ── Sections ──────────────────────────────────────────────────────────────── */ + +.section { + margin-bottom: 2rem; +} + +.sectionTitle { + font-size: 1rem; font-weight: 600; color: var(--color-text-primary); + margin: 0 0 0.75rem; +} + +.emptyText { + color: var(--color-text-muted); + font-size: 0.875rem; +} + +/* ── Table ─────────────────────────────────────────────────────────────────── */ + +.tableWrap { + overflow-x: auto; +} + +.table { + width: 100%; + border-collapse: collapse; + font-size: 0.875rem; +} + +.table th { + text-align: left; + padding: 0.625rem 0.75rem; + font-weight: 600; + color: var(--color-text-muted); + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.04em; + border-bottom: 1px solid var(--color-border); +} + +.table td { + padding: 0.625rem 0.75rem; + color: var(--color-text-secondary); + border-bottom: 1px solid var(--color-border-subtle, var(--color-border)); +} + +.table tbody tr:hover { + background: var(--color-bg-surface-hover); +} + +.link { + color: var(--color-accent); + text-decoration: none; + font-weight: 500; +} + +.link:hover { + text-decoration: underline; +} + +.filename { + font-family: var(--font-mono, monospace); + font-size: 0.8125rem; + word-break: break-all; +} + +/* ── Badges ────────────────────────────────────────────────────────────────── */ + +.badge { + display: inline-block; + padding: 0.125rem 0.5rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + white-space: nowrap; +} + +/* Processing status badges */ +.badgeComplete { + background: var(--color-badge-approved-bg); + color: var(--color-badge-approved-text); +} + +.badgeProcessing { + background: var(--color-badge-edited-bg); + color: var(--color-badge-edited-text); +} + +.badgeError { + background: var(--color-badge-rejected-bg); + color: var(--color-badge-rejected-text); +} + +.badgePending { + background: var(--color-badge-pending-bg); + color: var(--color-badge-pending-text); +} + +/* Category badges */ +.badgeCatSoundDesign { + background: var(--color-badge-cat-sound-design-bg); + color: var(--color-badge-cat-sound-design-text); +} + +.badgeCatMixing { + background: var(--color-badge-cat-mixing-bg); + color: var(--color-badge-cat-mixing-text); +} + +.badgeCatSynthesis { + background: var(--color-badge-cat-synthesis-bg); + color: var(--color-badge-cat-synthesis-text); +} + +.badgeCatArrangement { + background: var(--color-badge-cat-arrangement-bg); + color: var(--color-badge-cat-arrangement-text); +} + +.badgeCatWorkflow { + background: var(--color-badge-cat-workflow-bg); + color: var(--color-badge-cat-workflow-text); +} + +.badgeCatMastering { + background: var(--color-badge-cat-mastering-bg); + color: var(--color-badge-cat-mastering-text); +} + +.badgeCatMusicTheory { + background: var(--color-badge-cat-music-theory-bg); + color: var(--color-badge-cat-music-theory-text); +} + +.badgeCatDefault { + background: var(--color-badge-category-bg); + color: var(--color-badge-category-text); +} + +/* ── Mobile cards (hidden on desktop, shown on mobile) ─────────────────── */ + +.mobileCards { + display: none; +} + +.mobileCard { + background: var(--color-bg-surface); + border: 1px solid var(--color-border); + border-radius: 8px; + padding: 0.875rem 1rem; +} + +.mobileCardMeta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem; + margin-top: 0.375rem; + font-size: 0.8125rem; + color: var(--color-text-muted); +} + +/* ── Empty / error states ──────────────────────────────────────────────────── */ + +.emptyState { + text-align: center; + padding: 3rem 1rem; + color: var(--color-text-muted); +} + +.emptyState h2 { + font-size: 1.125rem; + color: var(--color-text-secondary); margin: 0 0 0.5rem; } -.cardBody { - font-size: 0.8125rem; - color: var(--color-text-muted); - line-height: 1.5; +.emptyState p { + font-size: 0.875rem; + margin: 0; } -/* ── Mobile ────────────────────────────────────────────────────────────────── */ +.errorState { + background: var(--color-error-bg, rgba(220, 38, 38, 0.1)); + color: var(--color-error, #ef4444); + padding: 1rem; + border-radius: 8px; + font-size: 0.875rem; +} + +/* ── Skeleton loading ──────────────────────────────────────────────────────── */ + +.skeleton { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.skeletonPulse { + background: var(--color-bg-surface); + border: 1px solid var(--color-border); + border-radius: 10px; + animation: pulse 1.5s ease-in-out infinite; +} + +.skeletonBlock { + height: 120px; +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +/* ── Responsive ────────────────────────────────────────────────────────────── */ + +@media (max-width: 1024px) { + .statsRow { + grid-template-columns: repeat(2, 1fr); + } +} @media (max-width: 768px) { .layout { @@ -129,4 +359,19 @@ .content { padding: 1.25rem; } + + .statsRow { + grid-template-columns: 1fr; + } + + /* Hide table, show mobile cards */ + .tableWrap { + display: none; + } + + .mobileCards { + display: flex; + flex-direction: column; + gap: 0.5rem; + } } diff --git a/frontend/src/pages/CreatorDashboard.tsx b/frontend/src/pages/CreatorDashboard.tsx index c580d82..927210b 100644 --- a/frontend/src/pages/CreatorDashboard.tsx +++ b/frontend/src/pages/CreatorDashboard.tsx @@ -1,6 +1,12 @@ -import { NavLink } from "react-router-dom"; +import { useEffect, useState } from "react"; +import { Link, NavLink } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; import { useDocumentTitle } from "../hooks/useDocumentTitle"; +import { + fetchCreatorDashboard, + type CreatorDashboardResponse, +} from "../api/creator-dashboard"; +import { ApiError } from "../api/client"; import styles from "./CreatorDashboard.module.css"; function SidebarNav() { @@ -42,10 +48,95 @@ function SidebarNav() { export { SidebarNav }; +/* ── Stat card ─────────────────────────────────────────────────────────────── */ + +function StatCard({ value, label }: { value: number; label: string }) { + return ( +
+ {value.toLocaleString()} + {label} +
+ ); +} + +/* ── Helpers ────────────────────────────────────────────────────────────────── */ + +function formatDate(iso: string): string { + return new Date(iso).toLocaleDateString(undefined, { + year: "numeric", + month: "short", + day: "numeric", + }); +} + +/** Map processing_status to a badge CSS class name (module-scoped). */ +function statusBadgeClass(status: string): string { + switch (status.toLowerCase()) { + case "complete": + case "completed": + return styles.badgeComplete ?? ""; + case "processing": + return styles.badgeProcessing ?? ""; + case "error": + case "failed": + return styles.badgeError ?? ""; + default: + return styles.badgePending ?? ""; + } +} + +/** Map topic_category to a badge CSS class name. */ +function categoryBadgeClass(cat: string): string { + const slug = cat.toLowerCase().replace(/[\s_]+/g, "-"); + const map: Record = { + "sound-design": styles.badgeCatSoundDesign, + mixing: styles.badgeCatMixing, + synthesis: styles.badgeCatSynthesis, + arrangement: styles.badgeCatArrangement, + workflow: styles.badgeCatWorkflow, + mastering: styles.badgeCatMastering, + "music-theory": styles.badgeCatMusicTheory, + }; + return map[slug] ?? styles.badgeCatDefault ?? ""; +} + +/* ── Main component ────────────────────────────────────────────────────────── */ + export default function CreatorDashboard() { useDocumentTitle("Creator Dashboard"); const { user } = useAuth(); + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + let cancelled = false; + setLoading(true); + setError(null); + + fetchCreatorDashboard() + .then((res) => { + if (!cancelled) setData(res); + }) + .catch((err) => { + if (cancelled) return; + if (err instanceof ApiError && err.status === 404) { + // No creator profile linked — show friendly empty state + setError("not_linked"); + } else { + setError(err instanceof ApiError ? err.detail : "Failed to load dashboard"); + } + }) + .finally(() => { + if (!cancelled) setLoading(false); + }); + + return () => { + cancelled = true; + }; + }, []); + return (
@@ -53,27 +144,159 @@ export default function CreatorDashboard() {

Welcome back{user?.display_name ? `, ${user.display_name}` : ""}

-
-
-

Content Stats

-

- Content analytics coming in M020. You'll see views, engagement, and technique performance here. -

+ + {loading && } + + {!loading && error === "not_linked" && ( +
+

No Creator Profile

+

Your account isn't linked to a creator profile yet. Contact an admin to get set up.

-
-

Recent Activity

-

- Activity feed coming soon. Track updates to your technique pages. -

+ )} + + {!loading && error && error !== "not_linked" && ( +
+

Could not load dashboard: {error}

-
-

Quick Actions

-

- Content management tools will appear here once the creator content module is live. -

-
-
+ )} + + {!loading && !error && data && ( + <> + {/* ── Stats row ──────────────────────────────────────────── */} +
+ + + + +
+ + {/* ── Techniques ─────────────────────────────────────────── */} +
+

Technique Pages

+ {data.techniques.length === 0 ? ( +

No technique pages yet.

+ ) : ( + <> + {/* Desktop table */} +
+ + + + + + + + + + + {data.techniques.map((t) => ( + + + + + + + ))} + +
TitleCategoryMomentsCreated
+ + {t.title} + + + + {t.topic_category} + + {t.key_moment_count}{formatDate(t.created_at)}
+
+ + {/* Mobile cards */} +
+ {data.techniques.map((t) => ( +
+ + {t.title} + +
+ + {t.topic_category} + + {t.key_moment_count} moments + {formatDate(t.created_at)} +
+
+ ))} +
+ + )} +
+ + {/* ── Videos ──────────────────────────────────────────────── */} +
+

Source Videos

+ {data.videos.length === 0 ? ( +

No videos uploaded yet.

+ ) : ( + <> +
+ + + + + + + + + + {data.videos.map((v) => ( + + + + + + ))} + +
FilenameStatusUploaded
{v.filename} + + {v.processing_status} + + {formatDate(v.created_at)}
+
+ +
+ {data.videos.map((v) => ( +
+ {v.filename} +
+ + {v.processing_status} + + {formatDate(v.created_at)} +
+
+ ))} +
+ + )} +
+ + )}
); } + +/* ── Loading skeleton ──────────────────────────────────────────────────────── */ + +function DashboardSkeleton() { + return ( +
+
+ {[1, 2, 3, 4].map((i) => ( +
+ ))} +
+
+
+
+ ); +} diff --git a/frontend/tsconfig.app.tsbuildinfo b/frontend/tsconfig.app.tsbuildinfo index e2a350a..a24504a 100644 --- a/frontend/tsconfig.app.tsbuildinfo +++ b/frontend/tsconfig.app.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/admin-pipeline.ts","./src/api/admin-techniques.ts","./src/api/auth.ts","./src/api/client.ts","./src/api/creators.ts","./src/api/index.ts","./src/api/reports.ts","./src/api/search.ts","./src/api/stats.ts","./src/api/techniques.ts","./src/api/topics.ts","./src/api/videos.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/CategoryIcons.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/PlayerControls.tsx","./src/components/ProtectedRoute.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/SocialIcons.tsx","./src/components/SortDropdown.tsx","./src/components/TableOfContents.tsx","./src/components/TagList.tsx","./src/components/TranscriptSidebar.tsx","./src/components/VideoPlayer.tsx","./src/context/AuthContext.tsx","./src/hooks/useCountUp.ts","./src/hooks/useDocumentTitle.ts","./src/hooks/useMediaSync.ts","./src/hooks/useSortPreference.ts","./src/pages/About.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/AdminTechniquePages.tsx","./src/pages/CreatorDashboard.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorSettings.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/Home.tsx","./src/pages/Login.tsx","./src/pages/Register.tsx","./src/pages/SearchResults.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/pages/WatchPage.tsx","./src/utils/catSlug.ts","./src/utils/citations.tsx"],"version":"5.6.3"} \ No newline at end of file +{"root":["./src/App.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/admin-pipeline.ts","./src/api/admin-techniques.ts","./src/api/auth.ts","./src/api/client.ts","./src/api/creator-dashboard.ts","./src/api/creators.ts","./src/api/index.ts","./src/api/reports.ts","./src/api/search.ts","./src/api/stats.ts","./src/api/techniques.ts","./src/api/topics.ts","./src/api/videos.ts","./src/components/AdminDropdown.tsx","./src/components/AppFooter.tsx","./src/components/CategoryIcons.tsx","./src/components/CopyLinkButton.tsx","./src/components/CreatorAvatar.tsx","./src/components/PlayerControls.tsx","./src/components/ProtectedRoute.tsx","./src/components/ReportIssueModal.tsx","./src/components/SearchAutocomplete.tsx","./src/components/SocialIcons.tsx","./src/components/SortDropdown.tsx","./src/components/TableOfContents.tsx","./src/components/TagList.tsx","./src/components/TranscriptSidebar.tsx","./src/components/VideoPlayer.tsx","./src/context/AuthContext.tsx","./src/hooks/useCountUp.ts","./src/hooks/useDocumentTitle.ts","./src/hooks/useMediaSync.ts","./src/hooks/useSortPreference.ts","./src/pages/About.tsx","./src/pages/AdminPipeline.tsx","./src/pages/AdminReports.tsx","./src/pages/AdminTechniquePages.tsx","./src/pages/CreatorDashboard.tsx","./src/pages/CreatorDetail.tsx","./src/pages/CreatorSettings.tsx","./src/pages/CreatorsBrowse.tsx","./src/pages/Home.tsx","./src/pages/Login.tsx","./src/pages/Register.tsx","./src/pages/SearchResults.tsx","./src/pages/SubTopicPage.tsx","./src/pages/TechniquePage.tsx","./src/pages/TopicsBrowse.tsx","./src/pages/WatchPage.tsx","./src/utils/catSlug.ts","./src/utils/citations.tsx"],"version":"5.6.3"} \ No newline at end of file