Skip to content

Commit dfd0fc8

Browse files
committed
refactor: Simplify ProjectInfo UI and improve share page UX
- Simplify ProjectInfo header to single row: Name [Approve] [Info] [Download] - Move project details (title, client, description) into Info modal - Add approval confirmation modal matching app design - ThumbnailReel: auto-collapse on desktop with blinking hint tooltip - ThumbnailReel: mobile starts expanded, can still collapse manually - Timeline markers: limit comments to 2 on mobile, 4 on desktop - Reduce vertical video (9:16) size constraints - Increase thumbnail sizes on desktop for grid and reel - Remove double border on selected thumbnail in reel
1 parent e5a874e commit dfd0fc8

8 files changed

Lines changed: 389 additions & 331 deletions

File tree

src/app/admin/projects/[id]/share/page.tsx

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { useEffect, useRef, useState, useCallback } from 'react'
4-
import { useParams, useSearchParams, useRouter } from 'next/navigation'
4+
import { useParams, useSearchParams, useRouter, usePathname } from 'next/navigation'
55
import Link from 'next/link'
66
import VideoPlayer from '@/components/VideoPlayer'
77
import CommentSection from '@/components/CommentSection'
@@ -17,6 +17,7 @@ export default function AdminSharePage() {
1717
const params = useParams()
1818
const searchParams = useSearchParams()
1919
const router = useRouter()
20+
const pathname = usePathname()
2021
const id = params?.id as string
2122

2223
// Parse URL parameters for video seeking (same as public share page)
@@ -397,17 +398,28 @@ export default function AdminSharePage() {
397398
setViewState('grid')
398399
}, [project?.videosByName, urlVideoName])
399400

400-
// Handle video selection
401+
// Handle video selection - update URL so refresh preserves state
401402
const handleVideoSelect = useCallback((videoName: string) => {
402403
setActiveVideoName(videoName)
403404
setActiveVideosRaw(project.videosByName[videoName])
404405
setViewState('player')
405-
}, [project?.videosByName])
406406

407-
// Handle back to grid
407+
// Update URL with video parameter (preserves state on refresh)
408+
const params = new URLSearchParams(searchParams?.toString() || '')
409+
params.set('video', videoName)
410+
router.replace(`${pathname}?${params.toString()}`, { scroll: false })
411+
}, [project?.videosByName, searchParams, pathname, router])
412+
413+
// Handle back to grid - remove video param from URL
408414
const handleBackToGrid = useCallback(() => {
409415
setViewState('grid')
410-
}, [])
416+
417+
// Remove video parameter from URL
418+
const params = new URLSearchParams(searchParams?.toString() || '')
419+
params.delete('video')
420+
const newUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname
421+
router.replace(newUrl || '', { scroll: false })
422+
}, [searchParams, pathname, router])
411423

412424
// Show loading state while project loads
413425
if (loading) {
@@ -495,7 +507,6 @@ export default function AdminSharePage() {
495507
thumbnailsByName={thumbnailsByName}
496508
thumbnailsLoading={thumbnailsLoading}
497509
onVideoSelect={handleVideoSelect}
498-
projectTitle={project.title}
499510
/>
500511
</div>
501512
</div>
@@ -517,31 +528,29 @@ export default function AdminSharePage() {
517528
/>
518529
)}
519530

531+
{/* Single video: show simple header */}
532+
{!hasMultipleVideos && (
533+
<div className="bg-card border-b border-border px-3 py-2 sm:px-4 sm:py-2.5 flex items-center gap-2">
534+
<Button
535+
variant="ghost"
536+
size="sm"
537+
onClick={() => router.push(projectUrl)}
538+
className="shrink-0 gap-1.5 px-2 sm:px-3 h-8 sm:h-9"
539+
>
540+
<ArrowLeft className="w-4 h-4" />
541+
<span className="hidden sm:inline text-sm">Back</span>
542+
</Button>
543+
<div className="flex-1 min-w-0">
544+
<h1 className="text-sm sm:text-base font-semibold text-foreground truncate">
545+
{project.title}
546+
</h1>
547+
</div>
548+
</div>
549+
)}
550+
520551
{/* Main Content Area */}
521552
<div className="flex-1 flex flex-col min-w-0 overflow-y-auto">
522553
<div className="max-w-screen-2xl mx-auto w-full px-3 sm:px-4 lg:px-6 py-3 sm:py-6 flex-1 min-h-0 flex flex-col">
523-
{/* Header */}
524-
<div className="mb-6 flex flex-wrap items-center gap-4">
525-
<Button
526-
variant="ghost"
527-
size="default"
528-
className="px-3"
529-
onClick={() => router.push(projectUrl)}
530-
>
531-
<ArrowLeft className="w-4 h-4 mr-2" />
532-
<span className="hidden sm:inline">Back to Project</span>
533-
<span className="sm:hidden">Back</span>
534-
</Button>
535-
<div className="flex-1 min-w-0">
536-
<h1 className="text-2xl sm:text-3xl font-bold text-foreground truncate">
537-
{project.title}
538-
</h1>
539-
<p className="text-muted-foreground text-sm mt-1">
540-
Share View
541-
</p>
542-
</div>
543-
</div>
544-
545554
{/* Main Content */}
546555
{readyVideos.length === 0 ? (
547556
<Card className="bg-card border-border rounded-lg">

src/app/globals.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,16 @@
228228
background-position: 28px 0;
229229
}
230230
}
231+
232+
/* Slow blink animation for attention */
233+
@keyframes blink {
234+
0%, 100% {
235+
opacity: 1;
236+
transform: scale(1);
237+
}
238+
50% {
239+
opacity: 0.5;
240+
transform: scale(1.1);
241+
}
242+
}
231243
}

src/app/share/[token]/SharePageClient.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { useEffect, useRef, useState, useCallback } from 'react'
4-
import { useSearchParams } from 'next/navigation'
4+
import { useSearchParams, usePathname, useRouter } from 'next/navigation'
55
import VideoPlayer from '@/components/VideoPlayer'
66
import CommentSection from '@/components/CommentSection'
77
import ThumbnailGrid from '@/components/ThumbnailGrid'
@@ -21,6 +21,8 @@ interface SharePageClientProps {
2121

2222
export default function SharePageClient({ token }: SharePageClientProps) {
2323
const searchParams = useSearchParams()
24+
const pathname = usePathname()
25+
const router = useRouter()
2426

2527
// Parse URL parameters for video seeking
2628
const urlTimestamp = searchParams?.get('t') ? parseInt(searchParams.get('t')!, 10) : null
@@ -487,17 +489,28 @@ export default function SharePageClient({ token }: SharePageClientProps) {
487489
setViewState('grid')
488490
}, [project?.videosByName, urlVideoName])
489491

490-
// Handle video selection
492+
// Handle video selection - update URL so refresh preserves state
491493
const handleVideoSelect = useCallback((videoName: string) => {
492494
setActiveVideoName(videoName)
493495
setActiveVideosRaw(project.videosByName[videoName])
494496
setViewState('player')
495-
}, [project?.videosByName])
496497

497-
// Handle back to grid
498+
// Update URL with video parameter (preserves state on refresh)
499+
const params = new URLSearchParams(searchParams?.toString() || '')
500+
params.set('video', videoName)
501+
router.replace(`${pathname}?${params.toString()}`, { scroll: false })
502+
}, [project?.videosByName, searchParams, pathname, router])
503+
504+
// Handle back to grid - remove video param from URL
498505
const handleBackToGrid = useCallback(() => {
499506
setViewState('grid')
500-
}, [])
507+
508+
// Remove video parameter from URL
509+
const params = new URLSearchParams(searchParams?.toString() || '')
510+
params.delete('video')
511+
const newUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname
512+
router.replace(newUrl || '', { scroll: false })
513+
}, [searchParams, pathname, router])
501514

502515
async function handleSendOtp(e: React.FormEvent) {
503516
e.preventDefault()
@@ -848,6 +861,8 @@ export default function SharePageClient({ token }: SharePageClientProps) {
848861
thumbnailsLoading={thumbnailsLoading}
849862
onVideoSelect={handleVideoSelect}
850863
projectTitle={project.title}
864+
projectDescription={isGuest ? undefined : project.description}
865+
clientName={isGuest ? undefined : project.clientName}
851866
/>
852867
</div>
853868
</div>

0 commit comments

Comments
 (0)