dvijaykrishnan commited on
Commit
a3a9068
·
1 Parent(s): 6b7bc46

feat: Add dark mode toggle and update UI components with theme-aware styling.

Browse files
dev.log CHANGED
@@ -516,3 +516,314 @@ Re-triggering analysis for 18BsJxwYETw (status: pending)
516
  POST /dashboard 200 in 2440ms
517
  GET /api/auth/get-session 200 in 677ms
518
  POST /dashboard 200 in 765ms
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
516
  POST /dashboard 200 in 2440ms
517
  GET /api/auth/get-session 200 in 677ms
518
  POST /dashboard 200 in 765ms
519
+ ✓ Compiled in 220ms
520
+ ✓ Compiled in 395ms
521
+ ✓ Compiled in 100ms
522
+ ✓ Compiled in 346ms
523
+ GET /api/auth/get-session 200 in 1467ms
524
+ ✓ Compiled /dashboard in 29ms
525
+ GET /api/auth/get-session 200 in 662ms
526
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
527
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
528
+ GET /dashboard 200 in 3416ms
529
+ GET /dashboard 200 in 2649ms
530
+ ✓ Compiled / in 59ms
531
+ Cache miss - fetching trending content from database
532
+ Cache miss - fetching featured creators from database
533
+ GET / 200 in 2106ms
534
+ ✓ Compiled /_not-found/page in 298ms
535
+ GET /placeholder-product.png 404 in 357ms
536
+ ⨯ The requested resource isn't a valid image for /placeholder-product.png received null
537
+ ✓ Compiled in 111ms
538
+ Cache miss - fetching trending content from database
539
+ Cache miss - fetching featured creators from database
540
+ ✓ Compiled in 239ms
541
+ GET / 200 in 388ms
542
+ GET /api/auth/get-session 200 in 1568ms
543
+ ✓ Compiled /dashboard in 41ms
544
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
545
+ ✓ Compiled in 91ms
546
+ GET /dashboard 200 in 3161ms
547
+ ✓ Compiled in 492ms
548
+ ✓ Compiled in 108ms
549
+ ✓ Compiled in 123ms
550
+ ✓ Compiled in 135ms
551
+ ✓ Compiled in 93ms
552
+ ✓ Compiled in 161ms
553
+ ✓ Compiled in 87ms
554
+ Cache miss - fetching trending content from database
555
+ Cache miss - fetching featured creators from database
556
+ GET / 200 in 1940ms
557
+ GET /favicon.ico?favicon.0b3bf435.ico 200 in 380ms
558
+ GET /api/auth/get-session 200 in 1456ms
559
+ GET /api/auth/get-session 200 in 1503ms
560
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
561
+ GET /dashboard 200 in 4082ms
562
+ GET /api/auth/get-session 200 in 562ms
563
+ GET /api/auth/get-session 200 in 557ms
564
+ POST /dashboard 200 in 1638ms
565
+ GET /api/auth/get-session 200 in 1418ms
566
+ ○ Compiling /dashboard/moderation ...
567
+ ✓ Compiled /dashboard/moderation in 593ms
568
+ GET /dashboard/moderation 200 in 2911ms
569
+ GET /api/auth/get-session 200 in 620ms
570
+ POST /dashboard 200 in 1723ms
571
+ GET /api/auth/get-session 200 in 627ms
572
+ POST /dashboard 200 in 765ms
573
+ GET /api/auth/get-session 200 in 1451ms
574
+ ✓ Compiled in 465ms
575
+ ✓ Compiled / in 117ms
576
+ Cache miss - fetching trending content from database
577
+ Cache miss - fetching featured creators from database
578
+ Cache miss - fetching trending content from database
579
+ Cache miss - fetching featured creators from database
580
+ GET / 200 in 1111ms
581
+ GET / 200 in 1685ms
582
+ Cache miss - fetching trending content from database
583
+ Cache miss - fetching featured creators from database
584
+ ✓ Compiled in 130ms
585
+ GET /api/auth/get-session 200 in 1354ms
586
+ GET / 200 in 223ms
587
+ ✓ Compiled /dashboard in 39ms
588
+ GET /api/auth/get-session 200 in 741ms
589
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
590
+ GET /api/auth/get-session 200 in 557ms
591
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
592
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
593
+ GET /dashboard 200 in 2891ms
594
+ GET /dashboard 200 in 2113ms
595
+ GET /dashboard 200 in 2737ms
596
+ ✓ Compiled /showcase/[videoId] in 48ms
597
+ Self-healing: Re-triggering stuck analysis for video iumWFCvrLLY
598
+ [ingestAndAnalyze] START - videoId: iumWFCvrLLY, userId: undefined, skipAnalysis: undefined
599
+ [ingestAndAnalyze] Fetching oembed for iumWFCvrLLY...
600
+ GET /showcase/iumWFCvrLLY 200 in 1568ms
601
+ [ingestAndAnalyze] oembed fetch failed with status 403. Trying API fallback...
602
+ [ingestAndAnalyze] Trying full API fallback for iumWFCvrLLY...
603
+ YouTube API fallback failed: Error: Video not found in YouTube API
604
+ at ShowcaseService.ingestAndAnalyze (src/features/vault/services/showcase.service.ts:411:35)
605
+ 409 |
606
+ 410 | if (!videoApiData.items?.[0]) {
607
+ > 411 | throw new Error('Video not found in YouTube API');
608
+ | ^
609
+ 412 | }
610
+ 413 |
611
+ 414 | const snippet = videoApiData.items[0].snippet;
612
+ Showcase ingest error (detailed): {
613
+ error: Error: Failed to fetch video metadata via API
614
+ at ShowcaseService.ingestAndAnalyze (src/features/vault/services/showcase.service.ts:434:31)
615
+ 432 | } catch (apiError) {
616
+ 433 | console.error('YouTube API fallback failed:', apiError);
617
+ > 434 | throw new Error('Failed to fetch video metadata via API');
618
+ | ^
619
+ 435 | }
620
+ 436 | } else {
621
+ 437 | throw new Error('Video unavailable (OEmbed failed and no API key configured)');,
622
+ videoId: 'iumWFCvrLLY',
623
+ stack: 'Error: Failed to fetch video metadata via API\n' +
624
+ ' at ShowcaseService.ingestAndAnalyze (/Users/vijaykrishnan/Dev/Vault/.next/server/chunks/ssr/[root-of-the-server]__85a520ff._.js:2202:31)\n' +
625
+ ' at process.processTicksAndRejections (node:internal/process/task_queues:105:5)'
626
+ }
627
+ GET /favicon.ico?favicon.0b3bf435.ico 200 in 270ms
628
+ GET /api/auth/get-session 200 in 565ms
629
+ POST /dashboard 200 in 805ms
630
+ GET /api/auth/get-session 200 in 664ms
631
+ POST /dashboard 200 in 809ms
632
+ ✓ Compiled in 145ms
633
+ ✓ Compiled in 127ms
634
+ Self-healing: Re-triggering stuck analysis for video UzVH2TzvwiU
635
+ [ingestAndAnalyze] START - videoId: UzVH2TzvwiU, userId: undefined, skipAnalysis: undefined
636
+ [ingestAndAnalyze] Fetching oembed for UzVH2TzvwiU...
637
+ GET /showcase/UzVH2TzvwiU 200 in 1180ms
638
+ [ingestAndAnalyze] oembed fetch failed with status 403. Trying API fallback...
639
+ [ingestAndAnalyze] Trying full API fallback for UzVH2TzvwiU...
640
+ YouTube API fallback failed: Error: Video not found in YouTube API
641
+ at ShowcaseService.ingestAndAnalyze (src/features/vault/services/showcase.service.ts:411:35)
642
+ 409 |
643
+ 410 | if (!videoApiData.items?.[0]) {
644
+ > 411 | throw new Error('Video not found in YouTube API');
645
+ | ^
646
+ 412 | }
647
+ 413 |
648
+ 414 | const snippet = videoApiData.items[0].snippet;
649
+ Showcase ingest error (detailed): {
650
+ error: Error: Failed to fetch video metadata via API
651
+ at ShowcaseService.ingestAndAnalyze (src/features/vault/services/showcase.service.ts:434:31)
652
+ 432 | } catch (apiError) {
653
+ 433 | console.error('YouTube API fallback failed:', apiError);
654
+ > 434 | throw new Error('Failed to fetch video metadata via API');
655
+ | ^
656
+ 435 | }
657
+ 436 | } else {
658
+ 437 | throw new Error('Video unavailable (OEmbed failed and no API key configured)');,
659
+ videoId: 'UzVH2TzvwiU',
660
+ stack: 'Error: Failed to fetch video metadata via API\n' +
661
+ ' at ShowcaseService.ingestAndAnalyze (/Users/vijaykrishnan/Dev/Vault/.next/server/chunks/ssr/[root-of-the-server]__85a520ff._.js:2202:31)\n' +
662
+ ' at process.processTicksAndRejections (node:internal/process/task_queues:105:5)'
663
+ }
664
+ ✓ Compiled in 118ms
665
+ GET /api/auth/get-session 200 in 1476ms
666
+ POST /dashboard 200 in 2987ms
667
+ GET /api/auth/get-session 200 in 648ms
668
+ POST /dashboard 200 in 1012ms
669
+ Self-healing: Re-triggering stuck analysis for video fqXkCp2zj30
670
+ [ingestAndAnalyze] START - videoId: fqXkCp2zj30, userId: undefined, skipAnalysis: undefined
671
+ [ingestAndAnalyze] Fetching oembed for fqXkCp2zj30...
672
+ GET /showcase/fqXkCp2zj30 200 in 1424ms
673
+ [ingestAndAnalyze] oembed fetch failed with status 403. Trying API fallback...
674
+ [ingestAndAnalyze] Trying full API fallback for fqXkCp2zj30...
675
+ YouTube API fallback failed: Error: Video not found in YouTube API
676
+ at ShowcaseService.ingestAndAnalyze (src/features/vault/services/showcase.service.ts:411:35)
677
+ 409 |
678
+ 410 | if (!videoApiData.items?.[0]) {
679
+ > 411 | throw new Error('Video not found in YouTube API');
680
+ | ^
681
+ 412 | }
682
+ 413 |
683
+ 414 | const snippet = videoApiData.items[0].snippet;
684
+ Showcase ingest error (detailed): {
685
+ error: Error: Failed to fetch video metadata via API
686
+ at ShowcaseService.ingestAndAnalyze (src/features/vault/services/showcase.service.ts:434:31)
687
+ 432 | } catch (apiError) {
688
+ 433 | console.error('YouTube API fallback failed:', apiError);
689
+ > 434 | throw new Error('Failed to fetch video metadata via API');
690
+ | ^
691
+ 435 | }
692
+ 436 | } else {
693
+ 437 | throw new Error('Video unavailable (OEmbed failed and no API key configured)');,
694
+ videoId: 'fqXkCp2zj30',
695
+ stack: 'Error: Failed to fetch video metadata via API\n' +
696
+ ' at ShowcaseService.ingestAndAnalyze (/Users/vijaykrishnan/Dev/Vault/.next/server/chunks/ssr/[root-of-the-server]__85a520ff._.js:2202:31)\n' +
697
+ ' at process.processTicksAndRejections (node:internal/process/task_queues:105:5)'
698
+ }
699
+ GET /api/auth/get-session 200 in 824ms
700
+ POST /dashboard 200 in 811ms
701
+ GET /api/auth/get-session 200 in 692ms
702
+ POST /dashboard 200 in 762ms
703
+ ✓ Compiled in 232ms
704
+ Cache miss - fetching trending content from database
705
+ Cache miss - fetching featured creators from database
706
+ GET / 200 in 1636ms
707
+ GET /api/auth/get-session 200 in 1868ms
708
+ ✓ Compiled /dashboard in 100ms
709
+ [DashboardPage] userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
710
+ GET /dashboard 200 in 3179ms
711
+ GET /api/auth/get-session 200 in 1457ms
712
+ GET /api/auth/get-session 200 in 563ms
713
+ [quickAddVideoAction] Video ID parsed: mWkXURsMAMk. Calling ingestAndAnalyze with skipAnalysis: true...
714
+ [ingestAndAnalyze] START - videoId: mWkXURsMAMk, userId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw, skipAnalysis: true
715
+ [ingestAndAnalyze] Fetching oembed for mWkXURsMAMk...
716
+ [ingestAndAnalyze] OEmbed Metadata fetched: ✅ Best eReader 2026 [Watch This Before You Make a Choice!]
717
+ [ingestAndAnalyze] Generated slug: theforemostpicks2026 for author: Foremost Picks
718
+ [ingestAndAnalyze] Slug 'theforemostpicks2026' already taken. Generating unique variation.
719
+ [ingestAndAnalyze] Creating NEW channel for creatorId: pvcRv8Ruf0FqiKwPLSUYjpMhBkTnyhJw
720
+ [ingestAndAnalyze] Attempting to prepopulate products from description for mWkXURsMAMk...
721
+ [prepopulate] Found 6 potential links to process.
722
+ [prepopulate] Processing link: https://geni.us/Y2An4
723
+ METADATA_FETCH: Unknown domain geni.us, attempting redirect resolution...
724
+ REDIRECT_RESOLVE: Checking https://geni.us/Y2An4
725
+ REDIRECT_RESOLVE: Resolved https://geni.us/Y2An4 -> https://www.amazon.com/dp/B0DSZWF122?tag=top5pickksof-20&geniuslink=true
726
+ METADATA_FETCH: Initializing for www.amazon.com
727
+ METADATA_FETCH: Service credentials missing, trying FallbackScraper for amazon
728
+ Could not parse CSS stylesheet
729
+ Could not parse CSS stylesheet
730
+ Could not parse CSS stylesheet
731
+ Could not parse CSS stylesheet
732
+ METADATA_FETCH: FallbackScraper completed for amazon {
733
+ hasImage: true,
734
+ imageUrl: 'https://m.media-amazon.com/images/I/51lam7v+AwL._AC_SL1000_.jpg',
735
+ hasName: true
736
+ }
737
+ Failed to set fetch cache https://www.amazon.com/dp/B0DSZWF122?tag=top5pickksof-20&geniuslink=true [Error: Failed to set Next.js data cache for https://www.amazon.com/dp/B0DSZWF122?tag=top5pickksof-20&geniuslink=true, items over 2MB can not be cached (2766645 bytes)]
738
+ [prepopulate] Successfully added product: BOOX Palma2 Mobile ePaper eBook Reader Fingerprint Recognition Smart Button Dual Microphones Off White
739
+ [prepopulate] Processing link: https://geni.us/vBxa228
740
+ METADATA_FETCH: Unknown domain geni.us, attempting redirect resolution...
741
+ REDIRECT_RESOLVE: Checking https://geni.us/vBxa228
742
+ REDIRECT_RESOLVE: Resolved https://geni.us/vBxa228 -> https://www.amazon.com/dp/B0CFPJYX7P?tag=top5pickksof-20&geniuslink=true
743
+ METADATA_FETCH: Initializing for www.amazon.com
744
+ METADATA_FETCH: Service credentials missing, trying FallbackScraper for amazon
745
+ Could not parse CSS stylesheet
746
+ Could not parse CSS stylesheet
747
+ Could not parse CSS stylesheet
748
+ Could not parse CSS stylesheet
749
+ METADATA_FETCH: FallbackScraper completed for amazon {
750
+ hasImage: true,
751
+ imageUrl: 'https://m.media-amazon.com/images/I/71YwNBmu+aL._AC_SL1500_.jpg',
752
+ hasName: true
753
+ }
754
+ Failed to set fetch cache https://www.amazon.com/dp/B0CFPJYX7P?tag=top5pickksof-20&geniuslink=true [Error: Failed to set Next.js data cache for https://www.amazon.com/dp/B0CFPJYX7P?tag=top5pickksof-20&geniuslink=true, items over 2MB can not be cached (2648509 bytes)]
755
+ [prepopulate] Successfully added product: Amazon Kindle Paperwhite 16GB (newest model) – 20% faster, with new 7" glare-free display and weeks of battery life – Black
756
+ [prepopulate] Processing link: https://geni.us/ZNv6u
757
+ METADATA_FETCH: Unknown domain geni.us, attempting redirect resolution...
758
+ REDIRECT_RESOLVE: Checking https://geni.us/ZNv6u
759
+ REDIRECT_RESOLVE: Resolved https://geni.us/ZNv6u -> https://www.amazon.com/dp/B0CZXWGK79?tag=top5pickksof-20&geniuslink=true
760
+ METADATA_FETCH: Initializing for www.amazon.com
761
+ METADATA_FETCH: Service credentials missing, trying FallbackScraper for amazon
762
+ Could not parse CSS stylesheet
763
+ Could not parse CSS stylesheet
764
+ Could not parse CSS stylesheet
765
+ Could not parse CSS stylesheet
766
+ METADATA_FETCH: FallbackScraper completed for amazon {
767
+ hasImage: true,
768
+ imageUrl: 'https://m.media-amazon.com/images/I/61Y4ELO5kOL._AC_SL1080_.jpg',
769
+ hasName: true
770
+ }
771
+ Failed to set fetch cache https://www.amazon.com/dp/B0CZXWGK79?tag=top5pickksof-20&geniuslink=true [Error: Failed to set Next.js data cache for https://www.amazon.com/dp/B0CZXWGK79?tag=top5pickksof-20&geniuslink=true, items over 2MB can not be cached (3351549 bytes)]
772
+ [prepopulate] Successfully added product: Kobo Libra Colour
773
+ [prepopulate] Processing link: https://geni.us/2xbHMQ
774
+ METADATA_FETCH: Unknown domain geni.us, attempting redirect resolution...
775
+ REDIRECT_RESOLVE: Checking https://geni.us/2xbHMQ
776
+ REDIRECT_RESOLVE: Resolved https://geni.us/2xbHMQ -> https://www.amazon.com/dp/B0CGVSKR1G?tag=top5pickksof-20&geniuslink=true
777
+ METADATA_FETCH: Initializing for www.amazon.com
778
+ METADATA_FETCH: Service credentials missing, trying FallbackScraper for amazon
779
+ Could not parse CSS stylesheet
780
+ Could not parse CSS stylesheet
781
+ Could not parse CSS stylesheet
782
+ Could not parse CSS stylesheet
783
+ METADATA_FETCH: FallbackScraper completed for amazon {
784
+ hasImage: true,
785
+ imageUrl: 'https://m.media-amazon.com/images/I/71KFrD7KfCL._AC_SL1500_.jpg',
786
+ hasName: true
787
+ }
788
+ Failed to set fetch cache https://www.amazon.com/dp/B0CGVSKR1G?tag=top5pickksof-20&geniuslink=true [Error: Failed to set Next.js data cache for https://www.amazon.com/dp/B0CGVSKR1G?tag=top5pickksof-20&geniuslink=true, items over 2MB can not be cached (2513853 bytes)]
789
+ [prepopulate] Successfully added product: Amazon Kindle Colorsoft 16 GB (newest model) – With color display and adjustable warm light – No Ads – Black
790
+ [prepopulate] Processing link: https://geni.us/TGrtk
791
+ METADATA_FETCH: Unknown domain geni.us, attempting redirect resolution...
792
+ REDIRECT_RESOLVE: Checking https://geni.us/TGrtk
793
+ REDIRECT_RESOLVE: Resolved https://geni.us/TGrtk -> https://www.amazon.com/dp/B0CZ9T2C6B?tag=top5pickksof-20&geniuslink=true
794
+ METADATA_FETCH: Initializing for www.amazon.com
795
+ METADATA_FETCH: Service credentials missing, trying FallbackScraper for amazon
796
+ Could not parse CSS stylesheet
797
+ Could not parse CSS stylesheet
798
+ Could not parse CSS stylesheet
799
+ Could not parse CSS stylesheet
800
+ METADATA_FETCH: FallbackScraper completed for amazon {
801
+ hasImage: true,
802
+ imageUrl: 'https://m.media-amazon.com/images/I/81xUpeu-LzL._AC_SL1500_.jpg',
803
+ hasName: true
804
+ }
805
+ Failed to set fetch cache https://www.amazon.com/dp/B0CZ9T2C6B?tag=top5pickksof-20&geniuslink=true [Error: Failed to set Next.js data cache for https://www.amazon.com/dp/B0CZ9T2C6B?tag=top5pickksof-20&geniuslink=true, items over 2MB can not be cached (2630133 bytes)]
806
+ [prepopulate] Successfully added product: Amazon Kindle Scribe (64GB) - Your notes, documents and books, all in one place. With built-in AI notebook summarization. Includes Premium Pen - Tungsten
807
+ [prepopulate] Processing link: https://geni.us/seABI
808
+ METADATA_FETCH: Unknown domain geni.us, attempting redirect resolution...
809
+ REDIRECT_RESOLVE: Checking https://geni.us/seABI
810
+ REDIRECT_RESOLVE: Resolved https://geni.us/seABI -> https://www.amazon.com/dp/B0DG9ZXWMK?tag=top5pickksof-20&geniuslink=true
811
+ METADATA_FETCH: Initializing for www.amazon.com
812
+ METADATA_FETCH: Service credentials missing, trying FallbackScraper for amazon
813
+ Could not parse CSS stylesheet
814
+ Could not parse CSS stylesheet
815
+ Could not parse CSS stylesheet
816
+ Could not parse CSS stylesheet
817
+ METADATA_FETCH: FallbackScraper completed for amazon {
818
+ hasImage: true,
819
+ imageUrl: 'https://m.media-amazon.com/images/I/71dzWnEOQvL._AC_SL1500_.jpg',
820
+ hasName: true
821
+ }
822
+ Failed to set fetch cache https://www.amazon.com/dp/B0DG9ZXWMK?tag=top5pickksof-20&geniuslink=true [Error: Failed to set Next.js data cache for https://www.amazon.com/dp/B0DG9ZXWMK?tag=top5pickksof-20&geniuslink=true, items over 2MB can not be cached (3577397 bytes)]
823
+ [prepopulate] Successfully added product: reMarkable Paper Pro Bundle – Includes 11.8” reMarkable Paper Tablet, and Marker Plus Pen with Eraser
824
+ [quickAddVideoAction] ingestAndAnalyze result for mWkXURsMAMk: { success: true }
825
+ POST /dashboard 200 in 21445ms
826
+ GET /showcase/mWkXURsMAMk 200 in 1619ms
827
+ GET /showcase/mWkXURsMAMk 200 in 1565ms
828
+ ✓ Compiled /register in 299ms
829
+ GET /register?videoId=mWkXURsMAMk 200 in 335ms
src/app/dashboard/page.tsx CHANGED
@@ -174,17 +174,20 @@ export default async function DashboardPage({
174
  {/* Key Metrics Row */}
175
  <KeyMetricsRow />
176
 
177
- {/* Quick nav */}
 
 
 
178
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
179
  <Link href="/dashboard/moderation" className="group">
180
- <Card className="glass border-white/10 hover:bg-white/[0.07] transition-all duration-300 cursor-pointer">
181
  <CardContent className="flex items-center gap-4 p-4">
182
- <div className="p-2.5 bg-white/5 rounded-lg border border-white/10 group-hover:scale-110 transition-transform">
183
  <ShieldCheck className="h-5 w-5 text-amber-400" />
184
  </div>
185
  <div className="flex-1 min-w-0">
186
  <p className="text-sm font-medium">Product Review</p>
187
- <p className="text-xs text-muted-foreground">Review AI-detected products</p>
188
  </div>
189
  {pendingCount > 0 && (
190
  <Badge className="bg-amber-500 text-white text-xs">{pendingCount}</Badge>
@@ -192,11 +195,12 @@ export default async function DashboardPage({
192
  </CardContent>
193
  </Card>
194
  </Link>
 
195
  {channels.length > 0 && channels[0].creatorSlug && (
196
  <Link href={`/vault/${channels[0].creatorSlug}`} className="group">
197
- <Card className="glass border-white/10 hover:bg-white/[0.07] transition-all duration-300 cursor-pointer">
198
  <CardContent className="flex items-center gap-4 p-4">
199
- <div className="p-2.5 bg-white/5 rounded-lg border border-white/10 group-hover:scale-110 transition-transform">
200
  <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
201
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
202
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
@@ -204,7 +208,7 @@ export default async function DashboardPage({
204
  </div>
205
  <div className="flex-1 min-w-0">
206
  <p className="text-sm font-medium">View Public Vault</p>
207
- <p className="text-xs text-muted-foreground">See your public storefront</p>
208
  </div>
209
  </CardContent>
210
  </Card>
@@ -217,12 +221,6 @@ export default async function DashboardPage({
217
  channels={channels}
218
  isGoogleLinked={!!googleAccount?.accessToken}
219
  />
220
-
221
- <div className="mt-8 border-t border-white/5 pt-8">
222
- <div className="max-w-xl">
223
- <QuickAnalyzeTool />
224
- </div>
225
- </div>
226
  </div>
227
 
228
  </div>
 
174
  {/* Key Metrics Row */}
175
  <KeyMetricsRow />
176
 
177
+ {/* Add to Vault - Full Width */}
178
+ <QuickAnalyzeTool />
179
+
180
+ {/* Quick Actions Row */}
181
  <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
182
  <Link href="/dashboard/moderation" className="group">
183
+ <Card className="glass hover:bg-white/[0.07] dark:hover:bg-white/[0.05] transition-all duration-300 cursor-pointer">
184
  <CardContent className="flex items-center gap-4 p-4">
185
+ <div className="p-2.5 bg-background border border-border group-hover:scale-110 transition-transform rounded-lg">
186
  <ShieldCheck className="h-5 w-5 text-amber-400" />
187
  </div>
188
  <div className="flex-1 min-w-0">
189
  <p className="text-sm font-medium">Product Review</p>
190
+ <p className="text-xs text-muted-foreground">Review AI detections</p>
191
  </div>
192
  {pendingCount > 0 && (
193
  <Badge className="bg-amber-500 text-white text-xs">{pendingCount}</Badge>
 
195
  </CardContent>
196
  </Card>
197
  </Link>
198
+
199
  {channels.length > 0 && channels[0].creatorSlug && (
200
  <Link href={`/vault/${channels[0].creatorSlug}`} className="group">
201
+ <Card className="glass hover:bg-white/[0.07] dark:hover:bg-white/[0.05] transition-all duration-300 cursor-pointer">
202
  <CardContent className="flex items-center gap-4 p-4">
203
+ <div className="p-2.5 bg-background border border-border group-hover:scale-110 transition-transform rounded-lg">
204
  <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
205
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
206
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
 
208
  </div>
209
  <div className="flex-1 min-w-0">
210
  <p className="text-sm font-medium">View Public Vault</p>
211
+ <p className="text-xs text-muted-foreground">See your storefront</p>
212
  </div>
213
  </CardContent>
214
  </Card>
 
221
  channels={channels}
222
  isGoogleLinked={!!googleAccount?.accessToken}
223
  />
 
 
 
 
 
 
224
  </div>
225
 
226
  </div>
src/app/globals.css CHANGED
@@ -103,12 +103,25 @@
103
  }
104
 
105
  /* Glassmorphism utility */
 
 
 
 
 
 
 
 
 
 
106
  .glass {
107
- @apply bg-white/5 backdrop-blur-md border border-white/10;
 
 
108
  }
109
 
110
  .glass-panel {
111
- @apply bg-background/60 backdrop-blur-xl border border-border;
 
112
  }
113
 
114
  /* Animations */
 
103
  }
104
 
105
  /* Glassmorphism utility */
106
+ :root {
107
+ --glass-bg: 0 0% 0% / 0.03;
108
+ --glass-border: 0 0% 0% / 0.08;
109
+ }
110
+
111
+ .dark {
112
+ --glass-bg: 0 0% 100% / 0.05;
113
+ --glass-border: 0 0% 100% / 0.1;
114
+ }
115
+
116
  .glass {
117
+ @apply backdrop-blur-md transition-all duration-300;
118
+ background-color: hsl(var(--glass-bg));
119
+ border: 1px solid hsl(var(--glass-border));
120
  }
121
 
122
  .glass-panel {
123
+ @apply backdrop-blur-xl border border-border transition-all duration-300;
124
+ @apply bg-background/60;
125
  }
126
 
127
  /* Animations */
src/components/auth/sign-in-form.tsx CHANGED
@@ -42,7 +42,7 @@ export function SignInForm() {
42
  type="button"
43
  disabled={loading}
44
  onClick={handleGoogleSignIn}
45
- className="w-full h-11 text-base glass border-white/10 hover:bg-white/5 transition-all duration-300"
46
  >
47
  {loading ? (
48
  <Loader2 className="mr-2 h-5 w-5 animate-spin" />
 
42
  type="button"
43
  disabled={loading}
44
  onClick={handleGoogleSignIn}
45
+ className="w-full h-11 text-base border-border hover:bg-secondary transition-all duration-300"
46
  >
47
  {loading ? (
48
  <Loader2 className="mr-2 h-5 w-5 animate-spin" />
src/components/auth/sign-up-form.tsx CHANGED
@@ -42,7 +42,7 @@ export function SignUpForm() {
42
  type="button"
43
  disabled={loading}
44
  onClick={handleGoogleSignIn}
45
- className="w-full h-11 text-base glass border-white/10 hover:bg-white/5 transition-all duration-300"
46
  >
47
  {loading ? (
48
  <Loader2 className="mr-2 h-5 w-5 animate-spin" />
 
42
  type="button"
43
  disabled={loading}
44
  onClick={handleGoogleSignIn}
45
+ className="w-full h-11 text-base border-border hover:bg-secondary transition-all duration-300"
46
  >
47
  {loading ? (
48
  <Loader2 className="mr-2 h-5 w-5 animate-spin" />
src/components/auth/user-nav.tsx CHANGED
@@ -15,6 +15,8 @@ import { Button } from "@/components/ui/button";
15
  import { LogOut, User, LayoutDashboard, Settings } from "lucide-react";
16
  import Link from "next/link";
17
 
 
 
18
  export function UserNav() {
19
  const { data: session } = useSession();
20
  const router = useRouter();
@@ -39,48 +41,51 @@ export function UserNav() {
39
  .toUpperCase() || "U";
40
 
41
  return (
42
- <DropdownMenu>
43
- <DropdownMenuTrigger asChild>
44
- <Button variant="ghost" className="relative h-8 w-8 rounded-full hover:bg-transparent p-0">
45
- <Avatar className="h-8 w-8 border-2 border-primary/50 transition-all hover:border-primary ring-2 ring-primary/10">
46
- <AvatarImage src={user.image || ""} alt={user.name || "User"} />
47
- <AvatarFallback className="bg-primary text-primary-foreground font-bold">
48
- {initials}
49
- </AvatarFallback>
50
- </Avatar>
51
- </Button>
52
- </DropdownMenuTrigger>
53
- <DropdownMenuContent className="w-56 glass border-white/10" align="end" forceMount>
54
- <DropdownMenuLabel className="font-normal">
55
- <div className="flex flex-col space-y-1">
56
- <p className="text-sm font-medium leading-none">{user.name}</p>
57
- <p className="text-xs leading-none text-muted-foreground">
58
- {user.email}
59
- </p>
60
- </div>
61
- </DropdownMenuLabel>
62
- <DropdownMenuSeparator className="bg-white/10" />
63
- <DropdownMenuItem asChild className="focus:bg-white/10 cursor-pointer">
64
- <Link href="/dashboard" className="flex w-full items-center">
65
- <LayoutDashboard className="mr-2 h-4 w-4" />
66
- <span>Dashboard</span>
67
- </Link>
68
- </DropdownMenuItem>
69
- <DropdownMenuItem asChild className="focus:bg-white/10 cursor-pointer">
70
- <Link href="/dashboard/settings" className="flex w-full items-center">
71
- <Settings className="mr-2 h-4 w-4" />
72
- <span>Settings</span>
73
- </Link>
74
- </DropdownMenuItem>
75
- <DropdownMenuSeparator className="bg-white/10" />
76
- <DropdownMenuItem
77
- onClick={handleSignOut}
78
- className="focus:bg-red-500/10 text-red-500 focus:text-red-500 cursor-pointer"
79
- >
80
- <LogOut className="mr-2 h-4 w-4" />
81
- <span>Log out</span>
82
- </DropdownMenuItem>
83
- </DropdownMenuContent>
84
- </DropdownMenu>
 
 
 
85
  );
86
  }
 
15
  import { LogOut, User, LayoutDashboard, Settings } from "lucide-react";
16
  import Link from "next/link";
17
 
18
+ import { ModeToggle } from "@/components/mode-toggle";
19
+
20
  export function UserNav() {
21
  const { data: session } = useSession();
22
  const router = useRouter();
 
41
  .toUpperCase() || "U";
42
 
43
  return (
44
+ <div className="flex items-center gap-3">
45
+ <ModeToggle />
46
+ <DropdownMenu>
47
+ <DropdownMenuTrigger asChild>
48
+ <Button variant="ghost" className="relative h-8 w-8 rounded-full hover:bg-transparent p-0">
49
+ <Avatar className="h-8 w-8 border-2 border-primary/50 transition-all hover:border-primary ring-2 ring-primary/10">
50
+ <AvatarImage src={user.image || ""} alt={user.name || "User"} />
51
+ <AvatarFallback className="bg-primary text-primary-foreground font-bold">
52
+ {initials}
53
+ </AvatarFallback>
54
+ </Avatar>
55
+ </Button>
56
+ </DropdownMenuTrigger>
57
+ <DropdownMenuContent className="w-56 glass border-white/10" align="end" forceMount>
58
+ <DropdownMenuLabel className="font-normal">
59
+ <div className="flex flex-col space-y-1">
60
+ <p className="text-sm font-medium leading-none">{user.name}</p>
61
+ <p className="text-xs leading-none text-muted-foreground">
62
+ {user.email}
63
+ </p>
64
+ </div>
65
+ </DropdownMenuLabel>
66
+ <DropdownMenuSeparator className="bg-white/10" />
67
+ <DropdownMenuItem asChild className="focus:bg-white/10 cursor-pointer">
68
+ <Link href="/dashboard" className="flex w-full items-center">
69
+ <LayoutDashboard className="mr-2 h-4 w-4" />
70
+ <span>Dashboard</span>
71
+ </Link>
72
+ </DropdownMenuItem>
73
+ <DropdownMenuItem asChild className="focus:bg-white/10 cursor-pointer">
74
+ <Link href="/dashboard/settings" className="flex w-full items-center">
75
+ <Settings className="mr-2 h-4 w-4" />
76
+ <span>Settings</span>
77
+ </Link>
78
+ </DropdownMenuItem>
79
+ <DropdownMenuSeparator className="bg-white/10" />
80
+ <DropdownMenuItem
81
+ onClick={handleSignOut}
82
+ className="focus:bg-red-500/10 text-red-500 focus:text-red-500 cursor-pointer"
83
+ >
84
+ <LogOut className="mr-2 h-4 w-4" />
85
+ <span>Log out</span>
86
+ </DropdownMenuItem>
87
+ </DropdownMenuContent>
88
+ </DropdownMenu>
89
+ </div>
90
  );
91
  }
src/components/mode-toggle.tsx ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Moon, Sun } from "lucide-react"
5
+ import { useTheme } from "next-themes"
6
+
7
+ import { Button } from "@/components/ui/button"
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuTrigger,
13
+ } from "@/components/ui/dropdown-menu"
14
+
15
+ export function ModeToggle() {
16
+ const { setTheme } = useTheme()
17
+
18
+ return (
19
+ <DropdownMenu>
20
+ <DropdownMenuTrigger asChild>
21
+ <Button variant="ghost" size="icon" className="h-8 w-8 rounded-full">
22
+ <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
23
+ <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
24
+ <span className="sr-only">Toggle theme</span>
25
+ </Button>
26
+ </DropdownMenuTrigger>
27
+ <DropdownMenuContent align="end" className="glass border-white/10">
28
+ <DropdownMenuItem onClick={() => setTheme("light")} className="focus:bg-white/10 cursor-pointer">
29
+ Light
30
+ </DropdownMenuItem>
31
+ <DropdownMenuItem onClick={() => setTheme("dark")} className="focus:bg-white/10 cursor-pointer">
32
+ Dark
33
+ </DropdownMenuItem>
34
+ <DropdownMenuItem onClick={() => setTheme("system")} className="focus:bg-white/10 cursor-pointer">
35
+ System
36
+ </DropdownMenuItem>
37
+ </DropdownMenuContent>
38
+ </DropdownMenu>
39
+ )
40
+ }
src/features/analytics/components/key-metrics-row.tsx CHANGED
@@ -60,19 +60,19 @@ export function KeyMetricsRow() {
60
  <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
61
  {/* Active Items */}
62
  <Link href="/dashboard/moderation?status=approved" className="block">
63
- <Card className="glass border-white/10 hover:bg-white/[0.07] transition-all duration-300 cursor-pointer hover:shadow-lg">
64
  <CardHeader className="pb-3">
65
  <div className="flex items-center justify-between">
66
- <CardTitle className="text-sm font-medium text-gray-400">Active Items</CardTitle>
67
- <Package className="h-4 w-4 text-gray-400" />
68
  </div>
69
  </CardHeader>
70
  <CardContent>
71
- <div className="text-3xl font-bold text-gray-200">
72
  {metricsData.activeItems}
73
  </div>
74
  <div className="flex items-center gap-1.5 mt-1">
75
- <p className="text-xs text-gray-500">
76
  {metricsData.activeItemsTrend > 0 ? '+' : ''}{metricsData.activeItemsTrend} this week
77
  </p>
78
  </div>
@@ -82,10 +82,10 @@ export function KeyMetricsRow() {
82
 
83
  {/* Untapped Opportunities */}
84
  <Link href="/dashboard/requests" className="block">
85
- <Card className="glass border-white/10 hover:bg-white/[0.07] transition-all duration-300 cursor-pointer hover:shadow-lg">
86
  <CardHeader className="pb-3">
87
  <div className="flex items-center justify-between">
88
- <CardTitle className="text-sm font-medium text-gray-400">Untapped Opportunities</CardTitle>
89
  <svg
90
  xmlns="http://www.w3.org/2000/svg"
91
  viewBox="0 0 24 24"
@@ -97,7 +97,7 @@ export function KeyMetricsRow() {
97
  </div>
98
  </CardHeader>
99
  <CardContent>
100
- <div className="text-3xl font-bold text-gray-200">
101
  {metricsData.pendingDemand}
102
  </div>
103
  <div className="flex items-center gap-1.5 mt-1">
@@ -106,7 +106,7 @@ export function KeyMetricsRow() {
106
  +{metricsData.pendingDemandTrend} this week
107
  </span>
108
  )}
109
- <p className="text-xs text-gray-500">
110
  review to monetize demand
111
  </p>
112
  </div>
@@ -115,13 +115,13 @@ export function KeyMetricsRow() {
115
  </Link>
116
 
117
  {/* Total Clicks */}
118
- <Card className="glass border-white/10 hover:bg-white/[0.07] transition-all duration-300">
119
  <CardHeader className="pb-3">
120
  <div className="flex items-center justify-between">
121
- <CardTitle className="text-sm font-medium text-gray-400">Total Clicks</CardTitle>
122
  <svg
123
  xmlns="http://www.w3.org/2000/svg"
124
- className="h-4 w-4 text-gray-400"
125
  fill="none"
126
  viewBox="0 0 24 24"
127
  stroke="currentColor"
@@ -136,7 +136,7 @@ export function KeyMetricsRow() {
136
  </div>
137
  </CardHeader>
138
  <CardContent>
139
- <div className="text-3xl font-bold text-gray-200">
140
  {metricsData.totalClicks.toLocaleString()}
141
  </div>
142
  <div className="flex items-center gap-1.5 mt-1">
@@ -145,7 +145,7 @@ export function KeyMetricsRow() {
145
  {metricsData.totalClicksTrend > 0 ? '↑' : '↓'} {Math.abs(metricsData.totalClicksTrend)}%
146
  </span>
147
  )}
148
- <p className="text-xs text-gray-500">vs last week</p>
149
  </div>
150
  </CardContent>
151
  </Card>
 
60
  <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
61
  {/* Active Items */}
62
  <Link href="/dashboard/moderation?status=approved" className="block">
63
+ <Card className="glass hover:bg-white/[0.07] dark:hover:bg-white/[0.07] transition-all duration-300 cursor-pointer hover:shadow-lg">
64
  <CardHeader className="pb-3">
65
  <div className="flex items-center justify-between">
66
+ <CardTitle className="text-sm font-medium text-muted-foreground">Active Items</CardTitle>
67
+ <Package className="h-4 w-4 text-muted-foreground" />
68
  </div>
69
  </CardHeader>
70
  <CardContent>
71
+ <div className="text-3xl font-bold">
72
  {metricsData.activeItems}
73
  </div>
74
  <div className="flex items-center gap-1.5 mt-1">
75
+ <p className="text-xs text-muted-foreground">
76
  {metricsData.activeItemsTrend > 0 ? '+' : ''}{metricsData.activeItemsTrend} this week
77
  </p>
78
  </div>
 
82
 
83
  {/* Untapped Opportunities */}
84
  <Link href="/dashboard/requests" className="block">
85
+ <Card className="glass hover:bg-white/[0.07] dark:hover:bg-white/[0.07] transition-all duration-300 cursor-pointer hover:shadow-lg">
86
  <CardHeader className="pb-3">
87
  <div className="flex items-center justify-between">
88
+ <CardTitle className="text-sm font-medium text-muted-foreground">Untapped Opportunities</CardTitle>
89
  <svg
90
  xmlns="http://www.w3.org/2000/svg"
91
  viewBox="0 0 24 24"
 
97
  </div>
98
  </CardHeader>
99
  <CardContent>
100
+ <div className="text-3xl font-bold">
101
  {metricsData.pendingDemand}
102
  </div>
103
  <div className="flex items-center gap-1.5 mt-1">
 
106
  +{metricsData.pendingDemandTrend} this week
107
  </span>
108
  )}
109
+ <p className="text-xs text-muted-foreground">
110
  review to monetize demand
111
  </p>
112
  </div>
 
115
  </Link>
116
 
117
  {/* Total Clicks */}
118
+ <Card className="glass hover:bg-white/[0.07] dark:hover:bg-white/[0.07] transition-all duration-300">
119
  <CardHeader className="pb-3">
120
  <div className="flex items-center justify-between">
121
+ <CardTitle className="text-sm font-medium text-muted-foreground">Total Clicks</CardTitle>
122
  <svg
123
  xmlns="http://www.w3.org/2000/svg"
124
+ className="h-4 w-4 text-muted-foreground"
125
  fill="none"
126
  viewBox="0 0 24 24"
127
  stroke="currentColor"
 
136
  </div>
137
  </CardHeader>
138
  <CardContent>
139
+ <div className="text-3xl font-bold">
140
  {metricsData.totalClicks.toLocaleString()}
141
  </div>
142
  <div className="flex items-center gap-1.5 mt-1">
 
145
  {metricsData.totalClicksTrend > 0 ? '↑' : '↓'} {Math.abs(metricsData.totalClicksTrend)}%
146
  </span>
147
  )}
148
+ <p className="text-xs text-muted-foreground">vs last week</p>
149
  </div>
150
  </CardContent>
151
  </Card>
src/features/dashboard/components/video-list-row.tsx CHANGED
@@ -47,9 +47,9 @@ export function VideoListRow({ video }: VideoListRowProps) {
47
  const hasUnmetOpportunities = video.detectionCount > 0 && video.monetizedCount === 0;
48
 
49
  return (
50
- <div className="flex flex-col sm:flex-row items-start sm:items-center gap-4 p-4 hover:bg-white/[0.03] transition-colors group">
51
  {/* Thumbnail */}
52
- <div className="relative shrink-0 w-full sm:w-40 aspect-video rounded-lg overflow-hidden border border-white/10 group-hover:border-primary/50 transition-colors">
53
  {video.thumbnailUrl ? (
54
  <img
55
  src={video.thumbnailUrl}
@@ -57,7 +57,7 @@ export function VideoListRow({ video }: VideoListRowProps) {
57
  className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
58
  />
59
  ) : (
60
- <div className="w-full h-full bg-white/5 flex items-center justify-center">
61
  <Play className="h-8 w-8 text-muted-foreground" />
62
  </div>
63
  )}
@@ -103,7 +103,7 @@ export function VideoListRow({ video }: VideoListRowProps) {
103
  {video.detectionCount} Detections
104
  </Badge>
105
  ) : (
106
- <Badge variant="secondary" className="bg-white/5 text-muted-foreground border-white/10 text-[10px] gap-1 px-1.5 py-0 h-5">
107
  <Clock className="h-3 w-3" />
108
  {video.scanStatus === 'completed' ? 'Refine Mode' : 'Ready to Scan'}
109
  </Badge>
@@ -112,7 +112,7 @@ export function VideoListRow({ video }: VideoListRowProps) {
112
  </div>
113
 
114
  {/* Engagement Sparkstats */}
115
- <div className="flex items-center gap-4 sm:gap-6 shrink-0 w-full sm:w-auto pt-2 sm:pt-0 border-t sm:border-0 border-white/5 mt-2 sm:mt-0">
116
  <div className="flex flex-col items-center gap-0.5 group/stat">
117
  <MousePointerClick className={cn("h-4 w-4 transition-colors", video.clickCount > 0 ? "text-primary" : "text-muted-foreground/30")} />
118
  <span className="text-xs font-bold leading-none">{video.clickCount || 0}</span>
@@ -132,7 +132,7 @@ export function VideoListRow({ video }: VideoListRowProps) {
132
  </div>
133
 
134
  <div className="ml-auto sm:ml-4">
135
- <Button asChild variant="outline" size="sm" className="h-8 rounded-lg glass-border hover:bg-white/10 text-xs gap-1.5">
136
  <Link href={`/showcase/${video.youtubeVideoId}`}>
137
  Manage
138
  <ExternalLink className="h-3 w-3" />
 
47
  const hasUnmetOpportunities = video.detectionCount > 0 && video.monetizedCount === 0;
48
 
49
  return (
50
+ <div className="flex flex-col sm:flex-row items-start sm:items-center gap-4 p-4 hover:bg-secondary/30 transition-colors group">
51
  {/* Thumbnail */}
52
+ <div className="relative shrink-0 w-full sm:w-40 aspect-video rounded-lg overflow-hidden border border-border group-hover:border-primary/50 transition-colors">
53
  {video.thumbnailUrl ? (
54
  <img
55
  src={video.thumbnailUrl}
 
57
  className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
58
  />
59
  ) : (
60
+ <div className="w-full h-full bg-secondary flex items-center justify-center">
61
  <Play className="h-8 w-8 text-muted-foreground" />
62
  </div>
63
  )}
 
103
  {video.detectionCount} Detections
104
  </Badge>
105
  ) : (
106
+ <Badge variant="secondary" className="bg-secondary text-muted-foreground border-border text-[10px] gap-1 px-1.5 py-0 h-5">
107
  <Clock className="h-3 w-3" />
108
  {video.scanStatus === 'completed' ? 'Refine Mode' : 'Ready to Scan'}
109
  </Badge>
 
112
  </div>
113
 
114
  {/* Engagement Sparkstats */}
115
+ <div className="flex items-center gap-4 sm:gap-6 shrink-0 w-full sm:w-auto pt-2 sm:pt-0 border-t sm:border-0 border-border/50 mt-2 sm:mt-0">
116
  <div className="flex flex-col items-center gap-0.5 group/stat">
117
  <MousePointerClick className={cn("h-4 w-4 transition-colors", video.clickCount > 0 ? "text-primary" : "text-muted-foreground/30")} />
118
  <span className="text-xs font-bold leading-none">{video.clickCount || 0}</span>
 
132
  </div>
133
 
134
  <div className="ml-auto sm:ml-4">
135
+ <Button asChild variant="outline" size="sm" className="h-8 rounded-lg border-border hover:bg-secondary text-xs gap-1.5 transition-colors">
136
  <Link href={`/showcase/${video.youtubeVideoId}`}>
137
  Manage
138
  <ExternalLink className="h-3 w-3" />
src/features/dashboard/components/video-workbench.tsx CHANGED
@@ -120,9 +120,9 @@ export function VideoWorkbench({ initialVideos, channels, isGoogleLinked }: Vide
120
 
121
  if (initialVideos.length === 0) {
122
  return (
123
- <Card className="border-dashed glass bg-white/5 border-white/10">
124
  <CardContent className="flex flex-col items-center justify-center p-12 text-center">
125
- <div className="w-16 h-16 rounded-full bg-white/5 flex items-center justify-center mb-6 border border-white/10">
126
  <Youtube className="h-8 w-8 text-muted-foreground" />
127
  </div>
128
  <CardTitle className="text-2xl">No Videos Found</CardTitle>
@@ -154,28 +154,28 @@ export function VideoWorkbench({ initialVideos, channels, isGoogleLinked }: Vide
154
  <p className="text-sm text-muted-foreground">Master list of your videos and their monetization performance.</p>
155
  </div>
156
 
157
- <div className="flex flex-wrap items-center gap-4 bg-white/5 p-2 rounded-xl border border-white/10 backdrop-blur-sm">
158
- <div className="px-3 border-r border-white/10 last:border-0">
159
  <p className="text-[10px] uppercase font-bold text-muted-foreground tracking-widest mb-0.5">Total Clicks</p>
160
  <p className="text-lg font-bold text-primary">{stats.totalClicks.toLocaleString()}</p>
161
  </div>
162
- <div className="px-3 border-r border-white/10 last:border-0">
163
  <p className="text-[10px] uppercase font-bold text-muted-foreground tracking-widest mb-0.5">Fan Engagements</p>
164
  <p className="text-lg font-bold text-amber-400">{stats.totalPledges.toLocaleString()}</p>
165
  </div>
166
  <div className="px-3">
167
  <p className="text-[10px] uppercase font-bold text-muted-foreground tracking-widest mb-0.5">Monetized</p>
168
- <p className="text-lg font-bold text-green-400">{stats.monetizedVideos} <span className="text-xs font-normal text-muted-foreground">/ {initialVideos.length}</span></p>
169
  </div>
170
 
171
- <div className="px-2 border-l border-white/10 ml-2">
172
  <AlertDialog>
173
  <AlertDialogTrigger asChild>
174
- <Button variant="ghost" size="icon" className="h-9 w-9 text-muted-foreground hover:bg-white/10 rounded-full">
175
  <Settings2 className="h-4 w-4" />
176
  </Button>
177
  </AlertDialogTrigger>
178
- <AlertDialogContent className="glass-panel border-white/10 backdrop-blur-xl">
179
  <AlertDialogHeader>
180
  <AlertDialogTitle>Account Connections</AlertDialogTitle>
181
  <AlertDialogDescription>
@@ -185,9 +185,9 @@ export function VideoWorkbench({ initialVideos, channels, isGoogleLinked }: Vide
185
 
186
  <div className="space-y-4 my-4">
187
  {channels.map(channel => (
188
- <div key={channel.id} className="flex items-center gap-3 p-3 rounded-xl bg-white/5 border border-white/10">
189
  {channel.thumbnailUrl && (
190
- <img src={channel.thumbnailUrl} alt="" className="w-10 h-10 rounded-full border border-white/20" />
191
  )}
192
  <div className="flex-1 min-w-0">
193
  <p className="text-sm font-bold truncate">{channel.channelName}</p>
@@ -213,7 +213,7 @@ export function VideoWorkbench({ initialVideos, channels, isGoogleLinked }: Vide
213
  Disconnect YouTube Account
214
  </Button>
215
  </div>
216
- <AlertDialogCancel className="bg-white/5 border-white/10 hover:bg-white/10">Close</AlertDialogCancel>
217
  </AlertDialogFooter>
218
  </AlertDialogContent>
219
  </AlertDialog>
@@ -224,7 +224,7 @@ export function VideoWorkbench({ initialVideos, channels, isGoogleLinked }: Vide
224
  {/* Controls Bar */}
225
  <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4 py-2">
226
  {/* Tabs */}
227
- <div className="flex items-center gap-1 bg-white/5 p-1 rounded-xl border border-white/10">
228
  {(["all", "active", "opportunities", "requested"] as Tab[]).map(t => (
229
  <button
230
  key={t}
@@ -232,7 +232,7 @@ export function VideoWorkbench({ initialVideos, channels, isGoogleLinked }: Vide
232
  className={cn(
233
  "px-4 py-1.5 rounded-lg text-sm font-medium capitalize transition-all",
234
  tab === t
235
- ? "bg-white/10 text-foreground shadow-sm"
236
  : "text-muted-foreground hover:text-foreground"
237
  )}
238
  >
@@ -244,7 +244,7 @@ export function VideoWorkbench({ initialVideos, channels, isGoogleLinked }: Vide
244
  <div className="flex flex-wrap items-center gap-3">
245
  {/* Channel Filter (if multiple) */}
246
  {channels.length > 1 && (
247
- <div className="hidden sm:flex items-center gap-2 px-3 py-1.5 rounded-xl bg-white/5 border border-white/10">
248
  <Filter className="h-3.5 w-3.5 text-muted-foreground" />
249
  <select
250
  value={filterChannel}
@@ -277,8 +277,8 @@ export function VideoWorkbench({ initialVideos, channels, isGoogleLinked }: Vide
277
  className={cn(
278
  "flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs font-semibold transition-all border",
279
  sortBy === f.id
280
- ? "bg-primary/20 border-primary/30 text-primary"
281
- : "bg-white/5 border-white/10 text-muted-foreground hover:border-white/20"
282
  )}
283
  >
284
  {f.icon}
@@ -293,8 +293,8 @@ export function VideoWorkbench({ initialVideos, channels, isGoogleLinked }: Vide
293
  </div>
294
 
295
  {/* Video List */}
296
- <Card className="glass border-white/10 overflow-hidden">
297
- <div className="divide-y divide-white/5">
298
  {filteredAndSortedVideos.length > 0 ? (
299
  filteredAndSortedVideos.map(video => (
300
  <VideoListRow key={video.id} video={video} />
 
120
 
121
  if (initialVideos.length === 0) {
122
  return (
123
+ <Card className="border-dashed glass">
124
  <CardContent className="flex flex-col items-center justify-center p-12 text-center">
125
+ <div className="w-16 h-16 rounded-full bg-secondary flex items-center justify-center mb-6 border border-border">
126
  <Youtube className="h-8 w-8 text-muted-foreground" />
127
  </div>
128
  <CardTitle className="text-2xl">No Videos Found</CardTitle>
 
154
  <p className="text-sm text-muted-foreground">Master list of your videos and their monetization performance.</p>
155
  </div>
156
 
157
+ <div className="flex flex-wrap items-center gap-4 glass p-2 rounded-xl backdrop-blur-sm">
158
+ <div className="px-3 border-r border-border last:border-0">
159
  <p className="text-[10px] uppercase font-bold text-muted-foreground tracking-widest mb-0.5">Total Clicks</p>
160
  <p className="text-lg font-bold text-primary">{stats.totalClicks.toLocaleString()}</p>
161
  </div>
162
+ <div className="px-3 border-r border-border last:border-0">
163
  <p className="text-[10px] uppercase font-bold text-muted-foreground tracking-widest mb-0.5">Fan Engagements</p>
164
  <p className="text-lg font-bold text-amber-400">{stats.totalPledges.toLocaleString()}</p>
165
  </div>
166
  <div className="px-3">
167
  <p className="text-[10px] uppercase font-bold text-muted-foreground tracking-widest mb-0.5">Monetized</p>
168
+ <p className="text-lg font-bold text-green-400 dark:text-green-400">{stats.monetizedVideos} <span className="text-xs font-normal text-muted-foreground">/ {initialVideos.length}</span></p>
169
  </div>
170
 
171
+ <div className="px-2 border-l border-border ml-2">
172
  <AlertDialog>
173
  <AlertDialogTrigger asChild>
174
+ <Button variant="ghost" size="icon" className="h-9 w-9 text-muted-foreground hover:bg-secondary rounded-full">
175
  <Settings2 className="h-4 w-4" />
176
  </Button>
177
  </AlertDialogTrigger>
178
+ <AlertDialogContent className="glass-panel backdrop-blur-xl">
179
  <AlertDialogHeader>
180
  <AlertDialogTitle>Account Connections</AlertDialogTitle>
181
  <AlertDialogDescription>
 
185
 
186
  <div className="space-y-4 my-4">
187
  {channels.map(channel => (
188
+ <div key={channel.id} className="flex items-center gap-3 p-3 rounded-xl bg-secondary border border-border">
189
  {channel.thumbnailUrl && (
190
+ <img src={channel.thumbnailUrl} alt="" className="w-10 h-10 rounded-full border border-border" />
191
  )}
192
  <div className="flex-1 min-w-0">
193
  <p className="text-sm font-bold truncate">{channel.channelName}</p>
 
213
  Disconnect YouTube Account
214
  </Button>
215
  </div>
216
+ <AlertDialogCancel className="bg-secondary border-border hover:bg-muted transition-colors">Close</AlertDialogCancel>
217
  </AlertDialogFooter>
218
  </AlertDialogContent>
219
  </AlertDialog>
 
224
  {/* Controls Bar */}
225
  <div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4 py-2">
226
  {/* Tabs */}
227
+ <div className="flex items-center gap-1 bg-secondary p-1 rounded-xl border border-border">
228
  {(["all", "active", "opportunities", "requested"] as Tab[]).map(t => (
229
  <button
230
  key={t}
 
232
  className={cn(
233
  "px-4 py-1.5 rounded-lg text-sm font-medium capitalize transition-all",
234
  tab === t
235
+ ? "bg-background text-foreground shadow-sm border border-border/50"
236
  : "text-muted-foreground hover:text-foreground"
237
  )}
238
  >
 
244
  <div className="flex flex-wrap items-center gap-3">
245
  {/* Channel Filter (if multiple) */}
246
  {channels.length > 1 && (
247
+ <div className="hidden sm:flex items-center gap-2 px-3 py-1.5 rounded-xl bg-secondary border border-border">
248
  <Filter className="h-3.5 w-3.5 text-muted-foreground" />
249
  <select
250
  value={filterChannel}
 
277
  className={cn(
278
  "flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-xs font-semibold transition-all border",
279
  sortBy === f.id
280
+ ? "bg-primary/10 border-primary/20 text-primary"
281
+ : "bg-secondary border-border text-muted-foreground hover:border-muted-foreground/30"
282
  )}
283
  >
284
  {f.icon}
 
293
  </div>
294
 
295
  {/* Video List */}
296
+ <Card className="glass overflow-hidden shadow-sm">
297
+ <div className="divide-y divide-border/30">
298
  {filteredAndSortedVideos.length > 0 ? (
299
  filteredAndSortedVideos.map(video => (
300
  <VideoListRow key={video.id} video={video} />
src/features/discovery/components/youtube-connect-section.tsx CHANGED
@@ -130,7 +130,7 @@ export function YouTubeConnectSection({
130
  Disconnect Account
131
  </Button>
132
  </AlertDialogTrigger>
133
- <AlertDialogContent className="glass-panel border-white/10 backdrop-blur-xl">
134
  <AlertDialogHeader>
135
  <AlertDialogTitle>Disconnect YouTube Account?</AlertDialogTitle>
136
  <AlertDialogDescription>
@@ -139,7 +139,7 @@ export function YouTubeConnectSection({
139
  </AlertDialogDescription>
140
  </AlertDialogHeader>
141
  <AlertDialogFooter>
142
- <AlertDialogCancel className="bg-white/5 border-white/10 hover:bg-white/10">Cancel</AlertDialogCancel>
143
  <AlertDialogAction
144
  onClick={handleDisconnect}
145
  className="bg-destructive text-white hover:bg-destructive/90 transition-colors"
@@ -151,7 +151,7 @@ export function YouTubeConnectSection({
151
  </AlertDialog>
152
  )}
153
  {channels.length === 0 && (
154
- <Button onClick={handleConnect} disabled={isConnecting} variant="outline" className="gap-2 glass hover:bg-white/10 transition-all">
155
  <Youtube className="h-4 w-4 text-red-600" />
156
  Connect YouTube
157
  </Button>
@@ -159,9 +159,9 @@ export function YouTubeConnectSection({
159
  </div>
160
 
161
  {channels.length === 0 ? (
162
- <Card className="border-dashed glass bg-white/5 border-white/10 transition-all hover:bg-white/[0.07]">
163
  <CardHeader className="text-center py-10">
164
- <div className="mx-auto w-12 h-12 rounded-full bg-white/5 flex items-center justify-center mb-4 border border-white/10">
165
  <Youtube className="h-6 w-6 text-red-500" />
166
  </div>
167
  <CardTitle>No YouTube Channels Connected</CardTitle>
@@ -194,14 +194,14 @@ export function YouTubeConnectSection({
194
  alt={channel.channelName}
195
  width={48}
196
  height={48}
197
- className="w-12 h-12 rounded-full border border-white/20 shadow-lg object-cover group-hover:scale-105 transition-transform"
198
  />
199
- <div className="absolute -bottom-1 -right-1 bg-background rounded-full p-0.5 border border-white/20">
200
  <Youtube className="h-3 w-3 text-red-600" />
201
  </div>
202
  </div>
203
  ) : (
204
- <div className="w-12 h-12 rounded-full bg-white/5 flex items-center justify-center border border-white/10">
205
  <Youtube className="h-6 w-6 text-red-600" />
206
  </div>
207
  )}
@@ -222,7 +222,7 @@ export function YouTubeConnectSection({
222
  {status.label}
223
  </Badge>
224
  </CardHeader>
225
- <CardContent className="pt-0 flex justify-end items-center bg-white/5 px-6 py-3 border-t border-white/10">
226
  <Button variant="ghost" size="sm" className="h-7 px-2 text-xs gap-1 opacity-70 hover:opacity-100 transition-opacity" asChild>
227
  <a href={`https://youtube.com/channel/${channel.channelId}`} target="_blank" rel="noopener noreferrer">
228
  View <ExternalLink className="h-3 w-3" />
 
130
  Disconnect Account
131
  </Button>
132
  </AlertDialogTrigger>
133
+ <AlertDialogContent className="glass-panel backdrop-blur-xl">
134
  <AlertDialogHeader>
135
  <AlertDialogTitle>Disconnect YouTube Account?</AlertDialogTitle>
136
  <AlertDialogDescription>
 
139
  </AlertDialogDescription>
140
  </AlertDialogHeader>
141
  <AlertDialogFooter>
142
+ <AlertDialogCancel className="bg-secondary border-border hover:bg-muted transition-colors">Cancel</AlertDialogCancel>
143
  <AlertDialogAction
144
  onClick={handleDisconnect}
145
  className="bg-destructive text-white hover:bg-destructive/90 transition-colors"
 
151
  </AlertDialog>
152
  )}
153
  {channels.length === 0 && (
154
+ <Button onClick={handleConnect} disabled={isConnecting} variant="outline" className="gap-2 glass hover:bg-secondary transition-all">
155
  <Youtube className="h-4 w-4 text-red-600" />
156
  Connect YouTube
157
  </Button>
 
159
  </div>
160
 
161
  {channels.length === 0 ? (
162
+ <Card className="border-dashed glass bg-secondary/30 border-border transition-all hover:bg-secondary/50">
163
  <CardHeader className="text-center py-10">
164
+ <div className="mx-auto w-12 h-12 rounded-full bg-secondary flex items-center justify-center mb-4 border border-border">
165
  <Youtube className="h-6 w-6 text-red-500" />
166
  </div>
167
  <CardTitle>No YouTube Channels Connected</CardTitle>
 
194
  alt={channel.channelName}
195
  width={48}
196
  height={48}
197
+ className="w-12 h-12 rounded-full border border-border shadow-lg object-cover group-hover:scale-105 transition-transform"
198
  />
199
+ <div className="absolute -bottom-1 -right-1 bg-background rounded-full p-0.5 border border-border">
200
  <Youtube className="h-3 w-3 text-red-600" />
201
  </div>
202
  </div>
203
  ) : (
204
+ <div className="w-12 h-12 rounded-full bg-secondary flex items-center justify-center border border-border">
205
  <Youtube className="h-6 w-6 text-red-600" />
206
  </div>
207
  )}
 
222
  {status.label}
223
  </Badge>
224
  </CardHeader>
225
+ <CardContent className="pt-0 flex justify-end items-center bg-secondary/30 px-6 py-3 border-t border-border">
226
  <Button variant="ghost" size="sm" className="h-7 px-2 text-xs gap-1 opacity-70 hover:opacity-100 transition-opacity" asChild>
227
  <a href={`https://youtube.com/channel/${channel.channelId}`} target="_blank" rel="noopener noreferrer">
228
  View <ExternalLink className="h-3 w-3" />
src/features/vault/components/affiliate-submission-dialog.tsx CHANGED
@@ -92,7 +92,7 @@ export function AffiliateSubmissionDialog({
92
  </Button>
93
  )}
94
  </DialogTrigger>
95
- <DialogContent className="sm:max-w-[500px] glass border-white/10 text-white">
96
  <DialogHeader>
97
  <DialogTitle className="text-xl font-bold tracking-tight">Submit Product Link</DialogTitle>
98
  <DialogDescription className="text-muted-foreground">
@@ -107,7 +107,7 @@ export function AffiliateSubmissionDialog({
107
  placeholder="e.g. Sony A7 III Camera"
108
  value={formData.productName}
109
  onChange={(e) => setFormData({ ...formData, productName: e.target.value })}
110
- className="bg-white/5 border-white/10 text-white"
111
  required
112
  />
113
  </div>
@@ -119,7 +119,7 @@ export function AffiliateSubmissionDialog({
119
  placeholder="https://amazon.com/..."
120
  value={formData.productUrl}
121
  onChange={(e) => setFormData({ ...formData, productUrl: e.target.value })}
122
- className="bg-white/5 border-white/10 text-white"
123
  required
124
  />
125
  </div>
@@ -130,7 +130,7 @@ export function AffiliateSubmissionDialog({
130
  placeholder="https://amzn.to/..."
131
  value={formData.affiliateUrl}
132
  onChange={(e) => setFormData({ ...formData, affiliateUrl: e.target.value })}
133
- className="bg-white/5 border-white/10 text-white"
134
  required
135
  />
136
  </div>
@@ -142,7 +142,7 @@ export function AffiliateSubmissionDialog({
142
  placeholder="e.g. This is the exact model used in the video..."
143
  value={formData.note}
144
  onChange={(e) => setFormData({ ...formData, note: e.target.value })}
145
- className="min-h-[80px] bg-white/5 border-white/10 text-white"
146
  />
147
  </div>
148
  <div className="grid grid-cols-2 gap-4">
@@ -153,7 +153,7 @@ export function AffiliateSubmissionDialog({
153
  placeholder="Alex"
154
  value={formData.submitterName}
155
  onChange={(e) => setFormData({ ...formData, submitterName: e.target.value })}
156
- className="bg-white/5 border-white/10 text-white"
157
  />
158
  </div>
159
  <div className="space-y-2">
@@ -164,7 +164,7 @@ export function AffiliateSubmissionDialog({
164
  placeholder="alex@example.com"
165
  value={formData.submitterEmail}
166
  onChange={(e) => setFormData({ ...formData, submitterEmail: e.target.value })}
167
- className="bg-white/5 border-white/10 text-white"
168
  />
169
  </div>
170
  </div>
 
92
  </Button>
93
  )}
94
  </DialogTrigger>
95
+ <DialogContent className="sm:max-w-[500px] glass-panel">
96
  <DialogHeader>
97
  <DialogTitle className="text-xl font-bold tracking-tight">Submit Product Link</DialogTitle>
98
  <DialogDescription className="text-muted-foreground">
 
107
  placeholder="e.g. Sony A7 III Camera"
108
  value={formData.productName}
109
  onChange={(e) => setFormData({ ...formData, productName: e.target.value })}
110
+ className="bg-secondary border-border"
111
  required
112
  />
113
  </div>
 
119
  placeholder="https://amazon.com/..."
120
  value={formData.productUrl}
121
  onChange={(e) => setFormData({ ...formData, productUrl: e.target.value })}
122
+ className="bg-secondary border-border"
123
  required
124
  />
125
  </div>
 
130
  placeholder="https://amzn.to/..."
131
  value={formData.affiliateUrl}
132
  onChange={(e) => setFormData({ ...formData, affiliateUrl: e.target.value })}
133
+ className="bg-secondary border-border"
134
  required
135
  />
136
  </div>
 
142
  placeholder="e.g. This is the exact model used in the video..."
143
  value={formData.note}
144
  onChange={(e) => setFormData({ ...formData, note: e.target.value })}
145
+ className="min-h-[80px] bg-secondary border-border"
146
  />
147
  </div>
148
  <div className="grid grid-cols-2 gap-4">
 
153
  placeholder="Alex"
154
  value={formData.submitterName}
155
  onChange={(e) => setFormData({ ...formData, submitterName: e.target.value })}
156
+ className="bg-secondary border-border"
157
  />
158
  </div>
159
  <div className="space-y-2">
 
164
  placeholder="alex@example.com"
165
  value={formData.submitterEmail}
166
  onChange={(e) => setFormData({ ...formData, submitterEmail: e.target.value })}
167
+ className="bg-secondary border-border"
168
  />
169
  </div>
170
  </div>
src/features/vault/components/product-request-dialog.tsx CHANGED
@@ -151,12 +151,12 @@ export function ProductRequestDialog({ videoId: initialVideoId, creatorId, video
151
  return (
152
  <Dialog open={open} onOpenChange={setOpen}>
153
  <DialogTrigger asChild>
154
- <Button variant="outline" className="rounded-full gap-2 border-primary/20 hover:border-primary/40 hover:bg-primary/5 transition-all text-white">
155
  <HelpCircle className="w-4 h-4" />
156
  Can't find a product?
157
  </Button>
158
  </DialogTrigger>
159
- <DialogContent className="sm:max-w-[540px] glass border-white/10 text-white">
160
  <DialogHeader>
161
  <DialogTitle className="text-xl font-bold tracking-tight">Request a Product</DialogTitle>
162
  <DialogDescription className="text-muted-foreground">
@@ -168,10 +168,10 @@ export function ProductRequestDialog({ videoId: initialVideoId, creatorId, video
168
  <div className="space-y-2">
169
  <Label htmlFor="video-select" className="text-sm font-medium">Which video did you see it in?*</Label>
170
  <Select value={selectedVideoId} onValueChange={setSelectedVideoId}>
171
- <SelectTrigger className="bg-white/5 border-white/10 focus:border-primary/50 text-white">
172
  <SelectValue placeholder="Select a video" />
173
  </SelectTrigger>
174
- <SelectContent className="bg-zinc-900 border-white/10 text-white">
175
  {videos.map(v => (
176
  <SelectItem key={v.id} value={v.id}>{v.title}</SelectItem>
177
  ))}
@@ -186,7 +186,7 @@ export function ProductRequestDialog({ videoId: initialVideoId, creatorId, video
186
  placeholder="e.g. The blue jacket at 04:20..."
187
  value={formData.note}
188
  onChange={(e) => setFormData({ ...formData, note: e.target.value })}
189
- className="min-h-[80px] bg-white/5 border-white/10 focus:border-primary/50 text-white placeholder:text-muted-foreground/50"
190
  required
191
  />
192
  </div>
@@ -198,7 +198,7 @@ export function ProductRequestDialog({ videoId: initialVideoId, creatorId, video
198
  placeholder="Alex"
199
  value={formData.name}
200
  onChange={(e) => setFormData({ ...formData, name: e.target.value })}
201
- className="bg-white/5 border-white/10 text-white placeholder:text-muted-foreground/50"
202
  />
203
  </div>
204
  <div className="space-y-2">
@@ -209,7 +209,7 @@ export function ProductRequestDialog({ videoId: initialVideoId, creatorId, video
209
  placeholder="alex@gmail.com"
210
  value={formData.email}
211
  onChange={(e) => setFormData({ ...formData, email: e.target.value })}
212
- className="bg-white/5 border-white/10 text-white placeholder:text-muted-foreground/50"
213
  />
214
  </div>
215
  </div>
@@ -224,7 +224,7 @@ export function ProductRequestDialog({ videoId: initialVideoId, creatorId, video
224
  aspectRatio="video"
225
  placeholderText="Paste image link or upload a screenshot"
226
  extraLabel={
227
- <span className="ml-auto text-[10px] font-normal text-muted-foreground bg-white/5 px-1.5 py-0.5 rounded border border-white/10 animate-pulse">
228
  Cmd+V to Paste
229
  </span>
230
  }
 
151
  return (
152
  <Dialog open={open} onOpenChange={setOpen}>
153
  <DialogTrigger asChild>
154
+ <Button variant="outline" className="rounded-full gap-2 border-primary/20 hover:border-primary/40 hover:bg-primary/5 transition-all">
155
  <HelpCircle className="w-4 h-4" />
156
  Can't find a product?
157
  </Button>
158
  </DialogTrigger>
159
+ <DialogContent className="sm:max-w-[540px] glass-panel">
160
  <DialogHeader>
161
  <DialogTitle className="text-xl font-bold tracking-tight">Request a Product</DialogTitle>
162
  <DialogDescription className="text-muted-foreground">
 
168
  <div className="space-y-2">
169
  <Label htmlFor="video-select" className="text-sm font-medium">Which video did you see it in?*</Label>
170
  <Select value={selectedVideoId} onValueChange={setSelectedVideoId}>
171
+ <SelectTrigger className="bg-secondary border-border focus:border-primary/50">
172
  <SelectValue placeholder="Select a video" />
173
  </SelectTrigger>
174
+ <SelectContent className="bg-popover border-border">
175
  {videos.map(v => (
176
  <SelectItem key={v.id} value={v.id}>{v.title}</SelectItem>
177
  ))}
 
186
  placeholder="e.g. The blue jacket at 04:20..."
187
  value={formData.note}
188
  onChange={(e) => setFormData({ ...formData, note: e.target.value })}
189
+ className="min-h-[80px] bg-secondary border-border focus:border-primary/50 placeholder:text-muted-foreground/50"
190
  required
191
  />
192
  </div>
 
198
  placeholder="Alex"
199
  value={formData.name}
200
  onChange={(e) => setFormData({ ...formData, name: e.target.value })}
201
+ className="bg-secondary border-border placeholder:text-muted-foreground/50"
202
  />
203
  </div>
204
  <div className="space-y-2">
 
209
  placeholder="alex@gmail.com"
210
  value={formData.email}
211
  onChange={(e) => setFormData({ ...formData, email: e.target.value })}
212
+ className="bg-secondary border-border placeholder:text-muted-foreground/50"
213
  />
214
  </div>
215
  </div>
 
224
  aspectRatio="video"
225
  placeholderText="Paste image link or upload a screenshot"
226
  extraLabel={
227
+ <span className="ml-auto text-[10px] font-normal text-muted-foreground bg-secondary px-1.5 py-0.5 rounded border border-border animate-pulse">
228
  Cmd+V to Paste
229
  </span>
230
  }
src/features/vault/components/quick-analyze-tool.tsx CHANGED
@@ -38,40 +38,39 @@ export function QuickAnalyzeTool() {
38
  };
39
 
40
  return (
41
- <Card className="glass border-white/10 overflow-hidden relative">
42
- <div className="absolute top-0 right-0 p-3 opacity-10">
43
- <Sparkles className="h-12 w-12" />
44
- </div>
45
- <CardHeader>
46
- <CardTitle className="flex items-center gap-2">
47
- <Sparkles className="h-5 w-5 text-amber-400" />
48
- Quick Analyze
49
- </CardTitle>
50
- <CardDescription>
51
- Paste a YouTube link to instantly analyze and showcase products from the video.
52
- </CardDescription>
53
- </CardHeader>
54
- <CardContent>
55
- <form onSubmit={handleSubmit} className="space-y-4">
56
- <div className="flex gap-2">
57
  <Input
58
- placeholder="https://www.youtube.com/watch?v=..."
59
  value={url}
60
  onChange={(e) => setUrl(e.target.value)}
61
- className="bg-white/5 border-white/10"
62
  disabled={loading}
63
  />
64
- <Button type="submit" disabled={loading || !url}>
65
- {loading ? <Loader2 className="h-4 w-4 animate-spin" /> : "Analyze"}
66
- </Button>
67
  </div>
68
- {error && (
69
- <Alert variant="destructive" className="bg-red-500/10 border-red-500/20 text-red-400">
70
- <AlertCircle className="h-4 w-4" />
71
- <AlertDescription>{error}</AlertDescription>
72
- </Alert>
73
- )}
 
 
 
 
 
 
 
74
  </form>
 
 
 
 
 
 
75
  </CardContent>
76
  </Card>
77
  );
 
38
  };
39
 
40
  return (
41
+ <Card className="glass overflow-hidden shadow-sm hover:shadow-md transition-all duration-300">
42
+ <CardContent className="p-4">
43
+ <form onSubmit={handleSubmit} className="flex flex-col sm:flex-row items-center gap-3">
44
+ <div className="relative flex-1 w-full">
45
+ <Sparkles className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-amber-400/80" />
 
 
 
 
 
 
 
 
 
 
 
46
  <Input
47
+ placeholder="Add any YouTube video to your Vault..."
48
  value={url}
49
  onChange={(e) => setUrl(e.target.value)}
50
+ className="pl-10 h-11 bg-secondary/50 border-border focus:border-primary/50 transition-all font-medium"
51
  disabled={loading}
52
  />
 
 
 
53
  </div>
54
+ <Button
55
+ type="submit"
56
+ disabled={loading || !url}
57
+ className="h-11 px-6 shadow-sm hover:translate-y-[-1px] active:translate-y-[0px] transition-all"
58
+ >
59
+ {loading ? (
60
+ <Loader2 className="h-4 w-4 animate-spin" />
61
+ ) : (
62
+ <span className="flex items-center gap-2">
63
+ Add to Vault
64
+ </span>
65
+ )}
66
+ </Button>
67
  </form>
68
+ {error && (
69
+ <div className="mt-3 flex items-center gap-2 text-xs text-destructive bg-destructive/5 p-2 rounded-lg border border-destructive/10 animate-in slide-in-from-top-1">
70
+ <AlertCircle className="h-3.5 w-3.5" />
71
+ <p>{error}</p>
72
+ </div>
73
+ )}
74
  </CardContent>
75
  </Card>
76
  );