11"use client"
22
33import { useState , useEffect } from "react"
4+ import type { ReactNode } from "react"
45import { useParams } from "next/navigation"
56import Link from "next/link"
67import { FoxMascot } from "@/components/fox-mascot"
@@ -16,7 +17,11 @@ import {
1617 Lightbulb ,
1718 Play ,
1819 Loader2 ,
19- AlertCircle
20+ AlertCircle ,
21+ CheckCircle2 ,
22+ Wrench ,
23+ ClipboardCheck ,
24+ Sparkles
2025} from "lucide-react"
2126
2227function ScoreGauge ( { score } : { score : number } ) {
@@ -66,6 +71,25 @@ const dimensionMeta: Record<string, { icon: any; color: string; hex: string }> =
6671 "Pacing" : { icon : Clock , color : "duo-purple" , hex : "#CE82FF" } ,
6772}
6873
74+ function EvidenceLink ( { videoId, timestamp, children, className = "" } : {
75+ videoId : string
76+ timestamp : number
77+ children : ReactNode
78+ className ?: string
79+ } ) {
80+ return (
81+ < a
82+ href = { `https://www.youtube.com/watch?v=${ videoId } &t=${ Math . floor ( timestamp || 0 ) } ` }
83+ target = "_blank"
84+ rel = "noopener noreferrer"
85+ className = { `inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-black transition-colors ${ className } ` }
86+ >
87+ < Play className = "w-3 h-3 fill-current" />
88+ { children }
89+ </ a >
90+ )
91+ }
92+
6993export default function AuditPage ( ) {
7094 const params = useParams ( )
7195 const jobId = params . jobId as string
@@ -125,7 +149,15 @@ export default function AuditPage() {
125149 }
126150
127151 const { result, videoMeta } = data
128- const { overallScore, topPriority, dimensions, timestampedSuggestions } = result
152+ const {
153+ overallScore,
154+ executiveSummary,
155+ strongestMoment,
156+ topPriority,
157+ dimensions,
158+ timestampedSuggestions,
159+ publishingChecklist
160+ } = result
129161
130162 return (
131163 < div className = "min-h-screen bg-white" >
@@ -154,6 +186,16 @@ export default function AuditPage() {
154186 < p className = "text-duo-text-muted font-semibold" >
155187 { videoMeta . author }
156188 </ p >
189+ { executiveSummary && (
190+ < div className = "mt-4 max-w-3xl rounded-2xl border-2 border-duo-border bg-duo-surface/40 p-4" >
191+ < p className = "mb-2 text-xs font-black uppercase tracking-wider text-duo-text-muted" >
192+ Private Coaching Summary
193+ </ p >
194+ < MarkdownContent className = "text-sm font-semibold leading-relaxed text-duo-text" >
195+ { executiveSummary }
196+ </ MarkdownContent >
197+ </ div >
198+ ) }
157199 </ div >
158200
159201 { /* Overall Score & Top Priority */ }
@@ -178,6 +220,50 @@ export default function AuditPage() {
178220 < MarkdownContent className = "text-duo-text-muted text-sm font-semibold" >
179221 { topPriority . description }
180222 </ MarkdownContent >
223+ < div className = "mt-3 flex flex-wrap items-center gap-2" >
224+ { topPriority . timestamp !== undefined && (
225+ < EvidenceLink
226+ videoId = { videoMeta . videoId }
227+ timestamp = { topPriority . timestamp }
228+ className = "bg-duo-orange/10 text-duo-orange hover:bg-duo-orange hover:text-white"
229+ >
230+ { formatTime ( topPriority . timestamp ) }
231+ </ EvidenceLink >
232+ ) }
233+ { topPriority . impact && (
234+ < span className = "rounded-full bg-duo-red/10 px-3 py-1 text-xs font-black text-duo-red" >
235+ { topPriority . impact } impact
236+ </ span >
237+ ) }
238+ { topPriority . effort && (
239+ < span className = "rounded-full bg-duo-blue/10 px-3 py-1 text-xs font-black text-duo-blue" >
240+ { topPriority . effort }
241+ </ span >
242+ ) }
243+ </ div >
244+ </ div >
245+ </ div >
246+ </ div >
247+ ) }
248+
249+ { strongestMoment && (
250+ < div className = "card-duo p-4 border-l-4 border-duo-green" >
251+ < div className = "flex items-start gap-3" >
252+ < Sparkles className = "w-5 h-5 text-duo-green flex-shrink-0 mt-0.5" />
253+ < div className = "flex-1" >
254+ < h3 className = "font-bold text-duo-text mb-1" > { strongestMoment . title } </ h3 >
255+ < MarkdownContent className = "text-duo-text-muted text-sm font-semibold" >
256+ { strongestMoment . description }
257+ </ MarkdownContent >
258+ { strongestMoment . timestamp !== undefined && (
259+ < EvidenceLink
260+ videoId = { videoMeta . videoId }
261+ timestamp = { strongestMoment . timestamp }
262+ className = "mt-3 bg-duo-green/10 text-duo-green hover:bg-duo-green hover:text-white"
263+ >
264+ Keep this move at { formatTime ( strongestMoment . timestamp ) }
265+ </ EvidenceLink >
266+ ) }
181267 </ div >
182268 </ div >
183269 </ div >
@@ -208,16 +294,77 @@ export default function AuditPage() {
208294 { dim . feedback }
209295 </ MarkdownContent >
210296
211- { dim . suggestions && dim . suggestions . length > 0 && (
212- < div className = "space-y-2" >
213- { dim . suggestions . map ( ( suggestion : string , idx : number ) => (
214- < div key = { idx } className = "flex items-start gap-2 text-sm " >
215- < Lightbulb className = "w-4 h-4 text-duo-yellow flex-shrink-0 mt-0.5 " />
216- < MarkdownContent className = "text-duo-text font-semibold" > { suggestion } </ MarkdownContent >
297+ < div className = "grid grid-cols-1 gap-3" >
298+ { dim . keepDoing && dim . keepDoing . length > 0 && (
299+ < div className = "rounded-2xl bg-duo-green/5 p-3" >
300+ < div className = "mb-2 flex items-center gap-2" >
301+ < CheckCircle2 className = "w-4 h-4 text-duo-green " />
302+ < span className = "text-xs font-black uppercase tracking-wider text-duo-green" > Keep Doing </ span >
217303 </ div >
218- ) ) }
219- </ div >
220- ) }
304+ < div className = "space-y-2" >
305+ { dim . keepDoing . map ( ( item : string , idx : number ) => (
306+ < MarkdownContent key = { idx } className = "text-sm font-semibold text-duo-text" >
307+ { item }
308+ </ MarkdownContent >
309+ ) ) }
310+ </ div >
311+ </ div >
312+ ) }
313+
314+ { dim . improveBeforePublishing && dim . improveBeforePublishing . length > 0 && (
315+ < div className = "rounded-2xl bg-duo-orange/5 p-3" >
316+ < div className = "mb-2 flex items-center gap-2" >
317+ < Wrench className = "w-4 h-4 text-duo-orange" />
318+ < span className = "text-xs font-black uppercase tracking-wider text-duo-orange" > Improve Before Publishing</ span >
319+ </ div >
320+ < div className = "space-y-2" >
321+ { dim . improveBeforePublishing . map ( ( item : string , idx : number ) => (
322+ < MarkdownContent key = { idx } className = "text-sm font-semibold text-duo-text" >
323+ { item }
324+ </ MarkdownContent >
325+ ) ) }
326+ </ div >
327+ </ div >
328+ ) }
329+
330+ { ( ! dim . keepDoing && ! dim . improveBeforePublishing ) && dim . suggestions && dim . suggestions . length > 0 && (
331+ < div className = "space-y-2" >
332+ { dim . suggestions . map ( ( suggestion : string , idx : number ) => (
333+ < div key = { idx } className = "flex items-start gap-2 text-sm" >
334+ < Lightbulb className = "w-4 h-4 text-duo-yellow flex-shrink-0 mt-0.5" />
335+ < MarkdownContent className = "text-duo-text font-semibold" > { suggestion } </ MarkdownContent >
336+ </ div >
337+ ) ) }
338+ </ div >
339+ ) }
340+
341+ { dim . evidence && dim . evidence . length > 0 && (
342+ < div className = "mt-1 rounded-2xl border border-duo-border bg-white p-3" >
343+ < p className = "mb-3 text-xs font-black uppercase tracking-wider text-duo-text-muted" >
344+ Evidence
345+ </ p >
346+ < div className = "space-y-3" >
347+ { dim . evidence . map ( ( item : any , idx : number ) => (
348+ < div key = { idx } className = "border-t border-duo-border pt-3 first:border-t-0 first:pt-0" >
349+ < EvidenceLink
350+ videoId = { videoMeta . videoId }
351+ timestamp = { item . timestamp }
352+ className = "mb-2 bg-duo-blue/10 text-duo-blue hover:bg-duo-blue hover:text-white"
353+ >
354+ { formatTime ( item . timestamp ) }
355+ </ EvidenceLink >
356+ < MarkdownContent className = "text-sm font-black text-duo-text" >
357+ { item . quoteOrMoment }
358+ </ MarkdownContent >
359+ < MarkdownContent className = "mt-1 text-xs font-semibold text-duo-text-muted" >
360+ { item . whyItMatters }
361+ </ MarkdownContent >
362+ </ div >
363+ ) ) }
364+ </ div >
365+ </ div >
366+ ) }
367+ </ div >
221368 </ div >
222369 )
223370 } ) }
@@ -239,22 +386,91 @@ export default function AuditPage() {
239386 : "bg-white border-duo-yellow/20"
240387 } `}
241388 >
242- < a
243- href = { item . youtubeLink || `https://www.youtube.com/watch?v=${ videoMeta . videoId } &t=${ Math . floor ( item . timestamp ) } ` }
244- target = "_blank"
245- rel = "noopener noreferrer"
246- className = { `flex-shrink-0 px-4 py-2 rounded-2xl text-sm font-black flex items-center gap-2 border-b-4 ${
389+ < EvidenceLink
390+ videoId = { videoMeta . videoId }
391+ timestamp = { item . timestamp }
392+ className = { `flex-shrink-0 border-b-4 px-4 py-2 text-sm ${
247393 item . type === "positive"
248394 ? "bg-duo-green text-white border-duo-green-dark"
249395 : "bg-duo-yellow text-duo-text border-duo-yellow-dark"
250396 } `}
251397 >
252- < Play className = "w-4 h-4 fill-current" />
253398 { formatTime ( item . timestamp ) }
254- </ a >
255- < MarkdownContent className = "text-duo-text font-bold text-lg flex-1" >
256- { item . note }
399+ </ EvidenceLink >
400+ < div className = "flex-1" >
401+ < div className = "mb-2 flex flex-wrap gap-2" >
402+ { item . impact && (
403+ < span className = "rounded-full bg-duo-red/10 px-3 py-1 text-xs font-black text-duo-red" >
404+ { item . impact } impact
405+ </ span >
406+ ) }
407+ { item . effort && (
408+ < span className = "rounded-full bg-duo-blue/10 px-3 py-1 text-xs font-black text-duo-blue" >
409+ { item . effort }
410+ </ span >
411+ ) }
412+ </ div >
413+ < MarkdownContent className = "text-duo-text font-bold text-lg" >
414+ { item . note }
415+ </ MarkdownContent >
416+ { item . suggestedRewrite && (
417+ < div className = "mt-4 rounded-2xl border-2 border-duo-border bg-duo-surface/60 p-4" >
418+ < p className = "mb-2 text-xs font-black uppercase tracking-wider text-duo-text-muted" >
419+ { item . type === "positive" ? "Reuse This Move" : "Suggested Rewrite" }
420+ </ p >
421+ < MarkdownContent className = "text-sm font-semibold leading-relaxed text-duo-text" >
422+ { item . suggestedRewrite }
423+ </ MarkdownContent >
424+ </ div >
425+ ) }
426+ </ div >
427+ </ div >
428+ ) ) }
429+ </ div >
430+ </ div >
431+ ) }
432+
433+ { /* Publishing Checklist */ }
434+ { publishingChecklist && publishingChecklist . length > 0 && (
435+ < div className = "card-duo p-8 mb-12" >
436+ < div className = "mb-6 flex items-center gap-3" >
437+ < div className = "flex h-10 w-10 items-center justify-center rounded-full bg-duo-green/10" >
438+ < ClipboardCheck className = "h-5 w-5 text-duo-green" />
439+ </ div >
440+ < div >
441+ < h2 className = "text-base font-black uppercase tracking-wider text-duo-text" >
442+ Publishing Checklist
443+ </ h2 >
444+ < p className = "text-sm font-semibold text-duo-text-muted" >
445+ The fastest edits to make before students see this lecture.
446+ </ p >
447+ </ div >
448+ </ div >
449+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-3" >
450+ { publishingChecklist . map ( ( item : any , index : number ) => (
451+ < div key = { index } className = "rounded-2xl border-2 border-duo-border bg-duo-surface/30 p-4" >
452+ < div className = "mb-3 flex items-center justify-between gap-3" >
453+ < span className = "flex h-7 w-7 items-center justify-center rounded-full bg-duo-green text-xs font-black text-white" >
454+ { index + 1 }
455+ </ span >
456+ { item . timestamp !== undefined && (
457+ < EvidenceLink
458+ videoId = { videoMeta . videoId }
459+ timestamp = { item . timestamp }
460+ className = "bg-white text-duo-blue hover:bg-duo-blue hover:text-white"
461+ >
462+ { formatTime ( item . timestamp ) }
463+ </ EvidenceLink >
464+ ) }
465+ </ div >
466+ < MarkdownContent className = "text-sm font-bold text-duo-text" >
467+ { item . item }
257468 </ MarkdownContent >
469+ { item . impact && (
470+ < span className = "mt-3 inline-flex rounded-full bg-duo-red/10 px-3 py-1 text-xs font-black text-duo-red" >
471+ { item . impact } impact
472+ </ span >
473+ ) }
258474 </ div >
259475 ) ) }
260476 </ div >
0 commit comments