@burdenoff/microfe-coolandlovely 2026.820.5 → 2026.829.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/coolandlovely/components/ActivityTrail.d.ts +24 -0
- package/dist/coolandlovely/components/ActivityTrail.d.ts.map +1 -0
- package/dist/coolandlovely/components/ActivityTrail.js +70 -0
- package/dist/coolandlovely/components/ActivityTrail.js.map +1 -0
- package/dist/coolandlovely/components/Comments.js +4 -4
- package/dist/coolandlovely/components/FeedbackWidget.d.ts.map +1 -1
- package/dist/coolandlovely/components/FeedbackWidget.js +72 -70
- package/dist/coolandlovely/components/FeedbackWidget.js.map +1 -1
- package/dist/coolandlovely/components/LookCard.js +24 -24
- package/dist/coolandlovely/components/StateViews.d.ts.map +1 -1
- package/dist/coolandlovely/components/StateViews.js +35 -26
- package/dist/coolandlovely/components/StateViews.js.map +1 -1
- package/dist/coolandlovely/components/StatusBadge.d.ts.map +1 -1
- package/dist/coolandlovely/components/StatusBadge.js +45 -15
- package/dist/coolandlovely/components/StatusBadge.js.map +1 -1
- package/dist/coolandlovely/components/VisibilityChip.d.ts.map +1 -1
- package/dist/coolandlovely/components/VisibilityChip.js +22 -17
- package/dist/coolandlovely/components/VisibilityChip.js.map +1 -1
- package/dist/coolandlovely/components/index.d.ts +1 -0
- package/dist/coolandlovely/components/index.d.ts.map +1 -1
- package/dist/coolandlovely/components/index.js +1 -0
- package/dist/coolandlovely/hooks/index.d.ts +4 -0
- package/dist/coolandlovely/hooks/index.d.ts.map +1 -1
- package/dist/coolandlovely/hooks/index.js +4 -1
- package/dist/coolandlovely/hooks/useActivity.d.ts +39 -0
- package/dist/coolandlovely/hooks/useActivity.d.ts.map +1 -0
- package/dist/coolandlovely/hooks/useActivity.js +38 -0
- package/dist/coolandlovely/hooks/useActivity.js.map +1 -0
- package/dist/coolandlovely/hooks/useAssistantEntity.d.ts +24 -0
- package/dist/coolandlovely/hooks/useAssistantEntity.d.ts.map +1 -0
- package/dist/coolandlovely/hooks/useAssistantEntity.js +12 -0
- package/dist/coolandlovely/hooks/useAssistantEntity.js.map +1 -0
- package/dist/coolandlovely/hooks/useTr.d.ts +2 -0
- package/dist/coolandlovely/hooks/useTr.d.ts.map +1 -0
- package/dist/coolandlovely/hooks/useTr.js +14 -0
- package/dist/coolandlovely/hooks/useTr.js.map +1 -0
- package/dist/coolandlovely/pages/HomePage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/HomePage.js +172 -132
- package/dist/coolandlovely/pages/HomePage.js.map +1 -1
- package/dist/coolandlovely/pages/admin/AdminBrandsPage.js +10 -10
- package/dist/coolandlovely/pages/admin/AdminCollaborationsPage.js +16 -16
- package/dist/coolandlovely/pages/admin/AdminFeaturedItemsPage.js +17 -17
- package/dist/coolandlovely/pages/brands/BrandDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js +171 -151
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandFormPage.js +9 -9
- package/dist/coolandlovely/pages/brands/BrandsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandsListPage.js +80 -76
- package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +121 -120
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +7 -7
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +82 -81
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +111 -110
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionFormPage.js +9 -9
- package/dist/coolandlovely/pages/collections/CollectionsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js +54 -55
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/discover/DiscoverPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/discover/DiscoverPage.js +452 -307
- package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +117 -116
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js +10 -10
- package/dist/coolandlovely/pages/items/StyleItemsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js +105 -95
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +32 -29
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +88 -79
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/looks/LookDetailPage.js +135 -134
- package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookFormPage.js +6 -6
- package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LooksListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/looks/LooksListPage.js +94 -94
- package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js +226 -199
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
- package/dist/coolandlovely/pages/trends/TrendsPage.js +8 -8
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +5 -5
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +71 -69
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
- package/dist/utils/pageContext.d.ts +23 -0
- package/dist/utils/pageContext.d.ts.map +1 -1
- package/dist/utils/pageContext.js +37 -25
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/coolandlovely/pages/HomePage.tsx"],"sourcesContent":["/**\n * Overview / Home Page\n * @module coolandlovely/pages\n *\n * The CoolAndLovely command center landing page: a style-operating-system\n * dashboard rolling up live catalog (brands, items), social (looks,\n * collaborations) and the shopper's own wardrobe — all from real `coo*` ops.\n */\n\nimport { useMemo } from 'react';\nimport {\n Store,\n Shirt,\n Sparkles,\n Handshake,\n Shapes,\n Heart,\n ArrowRight,\n Star,\n Plus,\n BookHeart,\n UserCircle,\n Compass,\n TrendingUp,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { Thumbnail } from '../components/Thumbnail';\nimport { LookCard } from '../components/LookCard';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useBrands, useStyleItems, useLooks, useCollaborations, useWardrobeItems } from '../hooks';\nimport { cn } from '../utils/cn';\nimport { buildCreatedAtSparkline, sparklineDeltaLabel, sparklineTrendDirection } from '../utils';\nimport type { DashboardStats } from '../types';\n\n/** Time-of-day greeting for the dashboard title (local clock, honest — no data). */\nfunction greeting(): string {\n const hour = new Date().getHours();\n if (hour < 12) return 'Good morning';\n if (hour < 18) return 'Good afternoon';\n return 'Good evening';\n}\n\ninterface QuickAction {\n label: string;\n description: string;\n icon: React.ReactNode;\n path: string;\n color: string;\n tourAttr?: string;\n}\n\nconst quickActions: QuickAction[] = [\n {\n label: 'New Brand',\n description: 'Launch a storefront',\n icon: <Store className=\"h-5 w-5\" />,\n path: '/brands/new',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Add Style Item',\n description: 'List a piece',\n icon: <Shirt className=\"h-5 w-5\" />,\n path: '/items/new',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Publish a Look',\n description: 'Shop-the-look outfit',\n icon: <Sparkles className=\"h-5 w-5\" />,\n path: '/looks/new',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'My Wardrobe',\n description: 'What you own',\n icon: <Heart className=\"h-5 w-5\" />,\n path: '/wardrobe',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n tourAttr: 'coolandlovely-wardrobe-link',\n },\n];\n\nconst modules = [\n {\n label: 'Brands',\n description: 'Storefronts with a publish lifecycle',\n icon: <Store className=\"h-6 w-6\" />,\n path: '/brands',\n color: 'bg-accent-purple-subtle text-text-primary',\n tourAttr: 'coolandlovely-brands-link',\n },\n {\n label: 'Collections',\n description: 'Seasonal drops & curated edits',\n icon: <Shapes className=\"h-6 w-6\" />,\n path: '/collections',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n label: 'Style Items',\n description: 'The shoppable catalogue',\n icon: <Shirt className=\"h-6 w-6\" />,\n path: '/items',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Looks',\n description: 'Published shop-the-look outfits',\n icon: <Sparkles className=\"h-6 w-6\" />,\n path: '/looks',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'Collaborations',\n description: 'Brand × creator partnerships',\n icon: <Handshake className=\"h-6 w-6\" />,\n path: '/collaborations',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n {\n label: 'Wardrobe',\n description: 'Your digital closet & wear log',\n icon: <Heart className=\"h-6 w-6\" />,\n path: '/wardrobe',\n color: 'bg-status-error-bg-subtle text-status-error-text',\n },\n {\n label: 'Lookbooks',\n description: 'Themed boards of saved pieces',\n icon: <BookHeart className=\"h-6 w-6\" />,\n path: '/lookbooks',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Style Profile',\n description: 'Your taste, palette & sizing',\n icon: <UserCircle className=\"h-6 w-6\" />,\n path: '/profile',\n color: 'bg-accent-blue-subtle text-accent-blue',\n tourAttr: 'coolandlovely-style-profile-link',\n },\n {\n label: 'Discover',\n description: 'A feed tuned to your taste',\n icon: <Compass className=\"h-6 w-6\" />,\n path: '/discover',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n tourAttr: 'coolandlovely-discover-feed',\n },\n {\n label: 'Trends',\n description: 'Live signals from platform activity',\n icon: <TrendingUp className=\"h-6 w-6\" />,\n path: '/trends',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n];\n\nexport function HomePage() {\n const { navigateTo, currentUser } = useCoolAndLovely();\n\n const brands = useBrands();\n const items = useStyleItems();\n const looks = useLooks();\n const collaborations = useCollaborations();\n const wardrobe = useWardrobeItems();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n const isLoading =\n brands.isLoading ||\n items.isLoading ||\n looks.isLoading ||\n collaborations.isLoading ||\n wardrobe.isLoading;\n\n const isError = brands.isError && items.isError && looks.isError && collaborations.isError;\n\n const stats = useMemo<DashboardStats>(() => {\n const brandItems = brands.data?.items ?? [];\n const styleItems = items.data?.items ?? [];\n const lookItems = looks.data?.items ?? [];\n const collabs = collaborations.data ?? [];\n const wardrobeItems = wardrobe.data ?? [];\n return {\n totalBrands: brandItems.length,\n publishedBrands: brandItems.filter((b) => b.status === 'PUBLISHED').length,\n totalStyleItems: styleItems.length,\n activeStyleItems: styleItems.filter((i) => i.status === 'ACTIVE').length,\n totalLooks: lookItems.length,\n featuredLooks: lookItems.filter((l) => l.isFeatured).length,\n activeCollaborations: collabs.filter(\n (c) => c.status === 'ACCEPTED' || c.status === 'IN_PROGRESS'\n ).length,\n wardrobeItems: wardrobeItems.length,\n };\n }, [brands.data, items.data, looks.data, collaborations.data, wardrobe.data]);\n\n const latestLooks = useMemo(() => [...(looks.data?.items ?? [])].slice(0, 5), [looks.data]);\n\n const recentBrands = useMemo(\n () =>\n [...(brands.data?.items ?? [])]\n .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n .slice(0, 4),\n [brands.data]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <LoadingView label=\"Loading your style command center…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <ErrorView\n title=\"Could not load CoolAndLovely\"\n message=\"The fashion service did not respond. Please try again.\"\n onRetry={() => {\n void brands.refetch();\n void items.refetch();\n void looks.refetch();\n void collaborations.refetch();\n void wardrobe.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n // Real (non-fabricated) 7-day sparkline series derived from each entity's\n // own createdAt — falls back to the plain text sub-label when there is no\n // meaningful history yet (e.g. everything was seeded at once).\n const brandSparkline = buildCreatedAtSparkline(brands.data?.items ?? []);\n const itemSparkline = buildCreatedAtSparkline(items.data?.items ?? []);\n const lookSparkline = buildCreatedAtSparkline(looks.data?.items ?? []);\n const collabSparkline = buildCreatedAtSparkline(collaborations.data ?? []);\n\n const metrics = [\n {\n icon: <Store className=\"h-5 w-5\" />,\n value: stats.totalBrands.toString(),\n label: 'Brands',\n sub: sparklineDeltaLabel(brandSparkline) ?? `${stats.publishedBrands} published`,\n sparklineData: brandSparkline,\n trend: sparklineTrendDirection(brandSparkline),\n },\n {\n icon: <Shirt className=\"h-5 w-5\" />,\n value: stats.totalStyleItems.toString(),\n label: 'Style Items',\n sub: sparklineDeltaLabel(itemSparkline) ?? `${stats.activeStyleItems} active`,\n sparklineData: itemSparkline,\n trend: sparklineTrendDirection(itemSparkline),\n },\n {\n icon: <Sparkles className=\"h-5 w-5\" />,\n value: stats.totalLooks.toString(),\n label: 'Looks',\n sub: sparklineDeltaLabel(lookSparkline) ?? `${stats.featuredLooks} featured`,\n sparklineData: lookSparkline,\n trend: sparklineTrendDirection(lookSparkline),\n },\n {\n icon: <Handshake className=\"h-5 w-5\" />,\n value: stats.activeCollaborations.toString(),\n label: 'Active Collabs',\n sub: sparklineDeltaLabel(collabSparkline) ?? `${stats.wardrobeItems} in wardrobe`,\n sparklineData: collabSparkline,\n trend: sparklineTrendDirection(collabSparkline),\n },\n ];\n\n return (\n <PageLayout\n title={`${greeting()}, ${firstName}`}\n description=\"Your fashion & lifestyle command center\"\n hero\n >\n <PagePurpose className=\"mb-6\">\n CoolAndLovely connects the three sides of style on one workspace-native model: brands launch\n storefronts and seasonal collections, you build a wardrobe and lookbooks, and creators\n publish shop-the-look outfits and run brand collaborations. Jump into any module below — all\n data is live from the CoolAndLovely service.\n </PagePurpose>\n\n {/* Headline metrics — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">At a glance</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {metrics.map((m, index) => (\n <MetricsCard\n key={m.label}\n icon={m.icon}\n value={m.value}\n label={m.label}\n trendValue={m.sub}\n trend={m.trend}\n sparklineData={m.sparklineData}\n featured={index === 0}\n />\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Quick Actions */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Quick actions</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {quickActions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={() => navigateTo(action.path)}\n data-tour={action.tourAttr}\n className={cn(\n 'flex items-start gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left shadow-sm transition-all',\n 'hover:border-accent-foreground/20 hover:shadow-md'\n )}\n >\n <div\n className={cn(\n 'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',\n action.color\n )}\n >\n {action.icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">{action.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{action.description}</p>\n </div>\n <Plus className=\"mt-1 h-4 w-4 shrink-0 text-text-secondary\" />\n </button>\n ))}\n </div>\n </section>\n\n {/* Modules */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Explore</h2>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {modules.map((mod) => (\n <button\n key={mod.label}\n type=\"button\"\n onClick={() => navigateTo(mod.path)}\n data-tour={mod.tourAttr}\n className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-5 text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div\n className={cn('flex h-12 w-12 items-center justify-center rounded-full', mod.color)}\n >\n {mod.icon}\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{mod.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{mod.description}</p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {/* Latest looks */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Latest looks</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {latestLooks.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-6 text-sm text-text-secondary\">\n No looks published yet. Be the first to publish a shop-the-look outfit.\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5\">\n {latestLooks.map((look) => (\n <LookCard\n key={look.id}\n id={look.id}\n title={look.title}\n occasion={look.occasion}\n imageFileId={look.image?.id}\n likeCount={look.likeCount}\n isLikedByMe={look.isLikedByMe}\n isSavedByMe={look.isSavedByMe}\n onOpen={() => navigateTo(`/looks/${look.id}`)}\n showActions\n />\n ))}\n </div>\n )}\n </section>\n\n {/* Recent brands */}\n {recentBrands.length > 0 && (\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recently updated brands</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {recentBrands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"group flex items-center gap-3 rounded-xl border border-border-seam bg-bg-surface p-3 text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Store className=\"h-5 w-5\" />}\n seed={brand.id}\n fileId={brand.logo?.id ?? brand.cover?.id}\n alt={brand.name}\n className=\"h-11 w-11 shrink-0 rounded-full ring-2 ring-bg-surface\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{brand.name}</p>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n </button>\n ))}\n </div>\n </section>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/coolandlovely/pages/HomePage.tsx"],"sourcesContent":["/**\n * Overview / Home Page\n * @module coolandlovely/pages\n *\n * The CoolAndLovely command center landing page: a style-operating-system\n * dashboard rolling up live catalog (brands, items), social (looks,\n * collaborations) and the shopper's own wardrobe — all from real `coo*` ops.\n */\n\nimport { useMemo } from 'react';\nimport {\n Store,\n Shirt,\n Sparkles,\n Handshake,\n Shapes,\n Heart,\n ArrowRight,\n Star,\n Plus,\n BookHeart,\n UserCircle,\n Compass,\n TrendingUp,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { Thumbnail } from '../components/Thumbnail';\nimport { LookCard } from '../components/LookCard';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport {\n useBrands,\n useStyleItems,\n useLooks,\n useCollaborations,\n useWardrobeItems,\n useTr,\n} from '../hooks';\nimport { cn } from '../utils/cn';\nimport { buildCreatedAtSparkline, sparklineDeltaLabel, sparklineTrendDirection } from '../utils';\nimport type { DashboardStats } from '../types';\n\ninterface QuickAction {\n labelKey: string;\n descriptionKey: string;\n label: string;\n description: string;\n icon: React.ReactNode;\n path: string;\n color: string;\n tourAttr?: string;\n}\n\ninterface ModuleItem {\n labelKey: string;\n descriptionKey: string;\n label: string;\n description: string;\n icon: React.ReactNode;\n path: string;\n color: string;\n tourAttr?: string;\n}\n\nconst quickActions: QuickAction[] = [\n {\n labelKey: 'newBrand',\n descriptionKey: 'newBrandDescription',\n label: 'New Brand',\n description: 'Launch a storefront',\n icon: <Store className=\"h-5 w-5\" />,\n path: '/brands/new',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n labelKey: 'addStyleItem',\n descriptionKey: 'addStyleItemDescription',\n label: 'Add Style Item',\n description: 'List a piece',\n icon: <Shirt className=\"h-5 w-5\" />,\n path: '/items/new',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n labelKey: 'publishLook',\n descriptionKey: 'publishLookDescription',\n label: 'Publish a Look',\n description: 'Shop-the-look outfit',\n icon: <Sparkles className=\"h-5 w-5\" />,\n path: '/looks/new',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n labelKey: 'myWardrobe',\n descriptionKey: 'myWardrobeDescription',\n label: 'My Wardrobe',\n description: 'What you own',\n icon: <Heart className=\"h-5 w-5\" />,\n path: '/wardrobe',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n tourAttr: 'coolandlovely-wardrobe-link',\n },\n];\n\nconst modules: ModuleItem[] = [\n {\n labelKey: 'brands',\n descriptionKey: 'brandsDescription',\n label: 'Brands',\n description: 'Storefronts with a publish lifecycle',\n icon: <Store className=\"h-6 w-6\" />,\n path: '/brands',\n color: 'bg-accent-purple-subtle text-text-primary',\n tourAttr: 'coolandlovely-brands-link',\n },\n {\n labelKey: 'collections',\n descriptionKey: 'collectionsDescription',\n label: 'Collections',\n description: 'Seasonal drops & curated edits',\n icon: <Shapes className=\"h-6 w-6\" />,\n path: '/collections',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n labelKey: 'styleItems',\n descriptionKey: 'styleItemsDescription',\n label: 'Style Items',\n description: 'The shoppable catalogue',\n icon: <Shirt className=\"h-6 w-6\" />,\n path: '/items',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n labelKey: 'looks',\n descriptionKey: 'looksDescription',\n label: 'Looks',\n description: 'Published shop-the-look outfits',\n icon: <Sparkles className=\"h-6 w-6\" />,\n path: '/looks',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n labelKey: 'collaborations',\n descriptionKey: 'collaborationsDescription',\n label: 'Collaborations',\n description: 'Brand × creator partnerships',\n icon: <Handshake className=\"h-6 w-6\" />,\n path: '/collaborations',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n {\n labelKey: 'wardrobe',\n descriptionKey: 'wardrobeDescription',\n label: 'Wardrobe',\n description: 'Your digital closet & wear log',\n icon: <Heart className=\"h-6 w-6\" />,\n path: '/wardrobe',\n color: 'bg-status-error-bg-subtle text-status-error-text',\n },\n {\n labelKey: 'lookbooks',\n descriptionKey: 'lookbooksDescription',\n label: 'Lookbooks',\n description: 'Themed boards of saved pieces',\n icon: <BookHeart className=\"h-6 w-6\" />,\n path: '/lookbooks',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n labelKey: 'styleProfile',\n descriptionKey: 'styleProfileDescription',\n label: 'Style Profile',\n description: 'Your taste, palette & sizing',\n icon: <UserCircle className=\"h-6 w-6\" />,\n path: '/profile',\n color: 'bg-accent-blue-subtle text-accent-blue',\n tourAttr: 'coolandlovely-style-profile-link',\n },\n {\n labelKey: 'discover',\n descriptionKey: 'discoverDescription',\n label: 'Discover',\n description: 'A feed tuned to your taste',\n icon: <Compass className=\"h-6 w-6\" />,\n path: '/discover',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n tourAttr: 'coolandlovely-discover-feed',\n },\n {\n labelKey: 'trends',\n descriptionKey: 'trendsDescription',\n label: 'Trends',\n description: 'Live signals from platform activity',\n icon: <TrendingUp className=\"h-6 w-6\" />,\n path: '/trends',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n];\n\nexport function HomePage() {\n const { navigateTo, currentUser } = useCoolAndLovely();\n const tr = useTr();\n\n const greeting = useMemo(() => {\n const hour = new Date().getHours();\n if (hour < 12) return tr('coolandlovely.greeting.morning', 'Good morning');\n if (hour < 18) return tr('coolandlovely.greeting.afternoon', 'Good afternoon');\n return tr('coolandlovely.greeting.evening', 'Good evening');\n }, [tr]);\n\n const brands = useBrands();\n const items = useStyleItems();\n const looks = useLooks();\n const collaborations = useCollaborations();\n const wardrobe = useWardrobeItems();\n\n const firstName =\n currentUser?.firstName ?? currentUser?.name ?? tr('coolandlovely.greeting.there', 'there');\n\n const isLoading =\n brands.isLoading ||\n items.isLoading ||\n looks.isLoading ||\n collaborations.isLoading ||\n wardrobe.isLoading;\n\n const isError = brands.isError && items.isError && looks.isError && collaborations.isError;\n\n const stats = useMemo<DashboardStats>(() => {\n const brandItems = brands.data?.items ?? [];\n const styleItems = items.data?.items ?? [];\n const lookItems = looks.data?.items ?? [];\n const collabs = collaborations.data ?? [];\n const wardrobeItems = wardrobe.data ?? [];\n return {\n totalBrands: brandItems.length,\n publishedBrands: brandItems.filter((b) => b.status === 'PUBLISHED').length,\n totalStyleItems: styleItems.length,\n activeStyleItems: styleItems.filter((i) => i.status === 'ACTIVE').length,\n totalLooks: lookItems.length,\n featuredLooks: lookItems.filter((l) => l.isFeatured).length,\n activeCollaborations: collabs.filter(\n (c) => c.status === 'ACCEPTED' || c.status === 'IN_PROGRESS'\n ).length,\n wardrobeItems: wardrobeItems.length,\n };\n }, [brands.data, items.data, looks.data, collaborations.data, wardrobe.data]);\n\n const latestLooks = useMemo(() => [...(looks.data?.items ?? [])].slice(0, 5), [looks.data]);\n\n const recentBrands = useMemo(\n () =>\n [...(brands.data?.items ?? [])]\n .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n .slice(0, 4),\n [brands.data]\n );\n\n if (isLoading) {\n return (\n <PageLayout title={tr('coolandlovely.appName', 'CoolAndLovely')}>\n <LoadingView\n label={tr('coolandlovely.loading.dashboard', 'Loading your style command center…')}\n />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title={tr('coolandlovely.appName', 'CoolAndLovely')}>\n <ErrorView\n title={tr('coolandlovely.error.loadTitle', 'Could not load CoolAndLovely')}\n message={tr(\n 'coolandlovely.error.loadMessage',\n 'The fashion service did not respond. Please try again.'\n )}\n onRetry={() => {\n void brands.refetch();\n void items.refetch();\n void looks.refetch();\n void collaborations.refetch();\n void wardrobe.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n // Real (non-fabricated) 7-day sparkline series derived from each entity's\n // own createdAt — falls back to the plain text sub-label when there is no\n // meaningful history yet (e.g. everything was seeded at once).\n const brandSparkline = buildCreatedAtSparkline(brands.data?.items ?? []);\n const itemSparkline = buildCreatedAtSparkline(items.data?.items ?? []);\n const lookSparkline = buildCreatedAtSparkline(looks.data?.items ?? []);\n const collabSparkline = buildCreatedAtSparkline(collaborations.data ?? []);\n\n const metrics = [\n {\n icon: <Store className=\"h-5 w-5\" />,\n value: stats.totalBrands.toString(),\n labelKey: 'brands',\n label: 'Brands',\n sub:\n sparklineDeltaLabel(brandSparkline) ??\n tr('coolandlovely.metrics.publishedBrands', '{{count}} published', {\n count: stats.publishedBrands,\n }),\n sparklineData: brandSparkline,\n trend: sparklineTrendDirection(brandSparkline),\n },\n {\n icon: <Shirt className=\"h-5 w-5\" />,\n value: stats.totalStyleItems.toString(),\n labelKey: 'styleItems',\n label: 'Style Items',\n sub:\n sparklineDeltaLabel(itemSparkline) ??\n tr('coolandlovely.metrics.activeItems', '{{count}} active', {\n count: stats.activeStyleItems,\n }),\n sparklineData: itemSparkline,\n trend: sparklineTrendDirection(itemSparkline),\n },\n {\n icon: <Sparkles className=\"h-5 w-5\" />,\n value: stats.totalLooks.toString(),\n labelKey: 'looks',\n label: 'Looks',\n sub:\n sparklineDeltaLabel(lookSparkline) ??\n tr('coolandlovely.metrics.featuredLooks', '{{count}} featured', {\n count: stats.featuredLooks,\n }),\n sparklineData: lookSparkline,\n trend: sparklineTrendDirection(lookSparkline),\n },\n {\n icon: <Handshake className=\"h-5 w-5\" />,\n value: stats.activeCollaborations.toString(),\n labelKey: 'activeCollabs',\n label: 'Active Collabs',\n sub:\n sparklineDeltaLabel(collabSparkline) ??\n tr('coolandlovely.metrics.inWardrobe', '{{count}} in wardrobe', {\n count: stats.wardrobeItems,\n }),\n sparklineData: collabSparkline,\n trend: sparklineTrendDirection(collabSparkline),\n },\n ];\n\n return (\n <PageLayout\n title={`${greeting}, ${firstName}`}\n description={tr(\n 'coolandlovely.dashboard.subtitle',\n 'Your fashion & lifestyle command center'\n )}\n hero\n >\n <PagePurpose className=\"mb-6\">\n {tr(\n 'coolandlovely.dashboard.purpose',\n 'CoolAndLovely connects the three sides of style on one workspace-native model: brands launch storefronts and seasonal collections, you build a wardrobe and lookbooks, and creators publish shop-the-look outfits and run brand collaborations. Jump into any module below — all data is live from the CoolAndLovely service.'\n )}\n </PagePurpose>\n\n {/* Headline metrics — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n {tr('coolandlovely.dashboard.atAGlance', 'At a glance')}\n </h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {metrics.map((m, index) => (\n <MetricsCard\n key={m.labelKey}\n icon={m.icon}\n value={m.value}\n label={tr(`coolandlovely.module.${m.labelKey}`, m.label)}\n trendValue={m.sub}\n trend={m.trend}\n sparklineData={m.sparklineData}\n featured={index === 0}\n />\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Quick Actions */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n {tr('coolandlovely.dashboard.quickActions', 'Quick actions')}\n </h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {quickActions.map((action) => (\n <button\n key={action.labelKey}\n type=\"button\"\n onClick={() => navigateTo(action.path)}\n data-tour={action.tourAttr}\n className={cn(\n 'flex items-start gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left shadow-sm transition-all',\n 'hover:border-accent-foreground/20 hover:shadow-md'\n )}\n >\n <div\n className={cn(\n 'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',\n action.color\n )}\n >\n {action.icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">\n {tr(`coolandlovely.quickAction.${action.labelKey}`, action.label)}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(`coolandlovely.quickAction.${action.descriptionKey}`, action.description)}\n </p>\n </div>\n <Plus className=\"mt-1 h-4 w-4 shrink-0 text-text-secondary\" />\n </button>\n ))}\n </div>\n </section>\n\n {/* Modules */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">\n {tr('coolandlovely.dashboard.explore', 'Explore')}\n </h2>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {modules.map((mod) => (\n <button\n key={mod.labelKey}\n type=\"button\"\n onClick={() => navigateTo(mod.path)}\n data-tour={mod.tourAttr}\n className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-5 text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div\n className={cn('flex h-12 w-12 items-center justify-center rounded-full', mod.color)}\n >\n {mod.icon}\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {tr(`coolandlovely.module.${mod.labelKey}`, mod.label)}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {tr(`coolandlovely.module.${mod.descriptionKey}`, mod.description)}\n </p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {/* Latest looks */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('coolandlovely.dashboard.latestLooks', 'Latest looks')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr('coolandlovely.actions.viewAll', 'View all')} <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {latestLooks.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-6 text-sm text-text-secondary\">\n {tr(\n 'coolandlovely.empty.latestLooks',\n 'No looks published yet. Be the first to publish a shop-the-look outfit.'\n )}\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5\">\n {latestLooks.map((look) => (\n <LookCard\n key={look.id}\n id={look.id}\n title={look.title}\n occasion={look.occasion}\n imageFileId={look.image?.id}\n likeCount={look.likeCount}\n isLikedByMe={look.isLikedByMe}\n isSavedByMe={look.isSavedByMe}\n onOpen={() => navigateTo(`/looks/${look.id}`)}\n showActions\n />\n ))}\n </div>\n )}\n </section>\n\n {/* Recent brands */}\n {recentBrands.length > 0 && (\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('coolandlovely.dashboard.recentBrands', 'Recently updated brands')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr('coolandlovely.actions.viewAll', 'View all')}{' '}\n <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {recentBrands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"group flex items-center gap-3 rounded-xl border border-border-seam bg-bg-surface p-3 text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Store className=\"h-5 w-5\" />}\n seed={brand.id}\n fileId={brand.logo?.id ?? brand.cover?.id}\n alt={brand.name}\n className=\"h-11 w-11 shrink-0 rounded-full ring-2 ring-bg-surface\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{brand.name}</p>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n </button>\n ))}\n </div>\n </section>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
2
|
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
-
import {
|
|
4
|
-
import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
|
|
5
|
-
import { Thumbnail as a } from "../../components/Thumbnail.js";
|
|
6
|
-
import { useApproveBrand as o, useBrands as s, useRejectBrand as c } from "../../hooks/useCoolAndLovelyApi.js";
|
|
3
|
+
import { useApproveBrand as n, useBrands as r, useRejectBrand as i } from "../../hooks/useCoolAndLovelyApi.js";
|
|
7
4
|
import "../../hooks/index.js";
|
|
5
|
+
import { StatusBadge as a } from "../../components/StatusBadge.js";
|
|
6
|
+
import { ErrorView as o, LoadingView as s } from "../../components/StateViews.js";
|
|
7
|
+
import { Thumbnail as c } from "../../components/Thumbnail.js";
|
|
8
8
|
import { EmptyState as l } from "../../components/EmptyState.js";
|
|
9
9
|
import { Check as u, Store as d, X as f } from "lucide-react";
|
|
10
10
|
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
11
11
|
//#region src/coolandlovely/pages/admin/AdminBrandsPage.tsx
|
|
12
12
|
function h({ brand: t }) {
|
|
13
|
-
let r =
|
|
13
|
+
let r = n(), o = i(), s = r.isPending || o.isPending;
|
|
14
14
|
return /* @__PURE__ */ m("div", {
|
|
15
15
|
className: "flex items-center gap-4 rounded-lg border border-border-seam bg-bg-surface p-4",
|
|
16
16
|
children: [
|
|
17
|
-
/* @__PURE__ */ p(
|
|
17
|
+
/* @__PURE__ */ p(c, {
|
|
18
18
|
icon: /* @__PURE__ */ p(d, { className: "h-5 w-5" }),
|
|
19
19
|
seed: t.id,
|
|
20
20
|
fileId: t.logo?.id ?? t.cover?.id,
|
|
@@ -29,7 +29,7 @@ function h({ brand: t }) {
|
|
|
29
29
|
children: [/* @__PURE__ */ p("h3", {
|
|
30
30
|
className: "truncate text-sm font-semibold text-text-primary",
|
|
31
31
|
children: t.name
|
|
32
|
-
}), /* @__PURE__ */ p(
|
|
32
|
+
}), /* @__PURE__ */ p(a, { status: t.status })]
|
|
33
33
|
}),
|
|
34
34
|
/* @__PURE__ */ m("p", {
|
|
35
35
|
className: "truncate text-xs text-text-secondary",
|
|
@@ -52,7 +52,7 @@ function h({ brand: t }) {
|
|
|
52
52
|
}), /* @__PURE__ */ m("button", {
|
|
53
53
|
type: "button",
|
|
54
54
|
disabled: s,
|
|
55
|
-
onClick: () =>
|
|
55
|
+
onClick: () => o.mutate(t.id),
|
|
56
56
|
className: e("inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium", "bg-status-error-bg-subtle text-status-error-text hover:bg-status-error-bg-subtle/80", "disabled:cursor-not-allowed disabled:opacity-50"),
|
|
57
57
|
children: [/* @__PURE__ */ p(f, { className: "h-4 w-4" }), " Reject"]
|
|
58
58
|
})]
|
|
@@ -61,11 +61,11 @@ function h({ brand: t }) {
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
function g() {
|
|
64
|
-
let e =
|
|
64
|
+
let e = r({ status: "PENDING_REVIEW" }), n = e.data?.items ?? [];
|
|
65
65
|
return /* @__PURE__ */ p(t, {
|
|
66
66
|
title: "Admin — Brands",
|
|
67
67
|
description: "Approve or reject storefronts pending review",
|
|
68
|
-
children: e.isLoading ? /* @__PURE__ */ p(
|
|
68
|
+
children: e.isLoading ? /* @__PURE__ */ p(s, { label: "Loading pending brands…" }) : e.isError ? /* @__PURE__ */ p(o, { onRetry: () => void e.refetch() }) : n.length === 0 ? /* @__PURE__ */ p(l, {
|
|
69
69
|
illustration: "empty-data",
|
|
70
70
|
icon: /* @__PURE__ */ p(d, { className: "h-10 w-10" }),
|
|
71
71
|
title: "No pending brands",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
2
|
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
-
import {
|
|
4
|
-
import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
|
|
5
|
-
import { useCollaborations as a, useReviewCollaboration as o } from "../../hooks/useCoolAndLovelyApi.js";
|
|
6
|
-
import { formatPrice as s, titleCase as c } from "../../utils/formatters.js";
|
|
3
|
+
import { useCollaborations as n, useReviewCollaboration as r } from "../../hooks/useCoolAndLovelyApi.js";
|
|
7
4
|
import "../../hooks/index.js";
|
|
5
|
+
import { StatusBadge as i } from "../../components/StatusBadge.js";
|
|
6
|
+
import { ErrorView as a, LoadingView as o } from "../../components/StateViews.js";
|
|
7
|
+
import { formatPrice as s, titleCase as c } from "../../utils/formatters.js";
|
|
8
8
|
import { EmptyState as l } from "../../components/EmptyState.js";
|
|
9
9
|
import { ActorName as u } from "../../components/ActorName.js";
|
|
10
10
|
import { useState as d } from "react";
|
|
@@ -34,7 +34,7 @@ var v = [
|
|
|
34
34
|
}
|
|
35
35
|
];
|
|
36
36
|
function b({ collaboration: t }) {
|
|
37
|
-
let
|
|
37
|
+
let n = r(), [a, o] = d(""), l = v.includes(t.status), m = n.isPending;
|
|
38
38
|
return /* @__PURE__ */ _("div", {
|
|
39
39
|
className: "rounded-lg border border-border-seam bg-bg-surface p-4",
|
|
40
40
|
children: [
|
|
@@ -47,7 +47,7 @@ function b({ collaboration: t }) {
|
|
|
47
47
|
children: [/* @__PURE__ */ g("h3", {
|
|
48
48
|
className: "truncate text-sm font-semibold text-text-primary",
|
|
49
49
|
children: t.title
|
|
50
|
-
}), /* @__PURE__ */ g(
|
|
50
|
+
}), /* @__PURE__ */ g(i, { status: t.status })]
|
|
51
51
|
}), /* @__PURE__ */ _("p", {
|
|
52
52
|
className: "mt-0.5 text-xs text-text-secondary",
|
|
53
53
|
children: [
|
|
@@ -75,8 +75,8 @@ function b({ collaboration: t }) {
|
|
|
75
75
|
className: "flex items-center gap-2",
|
|
76
76
|
children: [/* @__PURE__ */ g(p, { className: "h-4 w-4 text-text-secondary" }), /* @__PURE__ */ g("input", {
|
|
77
77
|
type: "text",
|
|
78
|
-
value:
|
|
79
|
-
onChange: (e) =>
|
|
78
|
+
value: a,
|
|
79
|
+
onChange: (e) => o(e.target.value),
|
|
80
80
|
placeholder: "Optional review notes",
|
|
81
81
|
className: "flex-1 rounded-md border border-border-seam bg-bg-surface px-3 py-1.5 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:ring-1 focus:ring-primary"
|
|
82
82
|
})]
|
|
@@ -85,11 +85,11 @@ function b({ collaboration: t }) {
|
|
|
85
85
|
children: [/* @__PURE__ */ _("button", {
|
|
86
86
|
type: "button",
|
|
87
87
|
disabled: m,
|
|
88
|
-
onClick: () =>
|
|
88
|
+
onClick: () => n.mutate({
|
|
89
89
|
id: t.id,
|
|
90
90
|
input: {
|
|
91
91
|
decision: "REJECT",
|
|
92
|
-
notes:
|
|
92
|
+
notes: a
|
|
93
93
|
}
|
|
94
94
|
}),
|
|
95
95
|
className: e("inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium", "bg-status-error-bg-subtle text-status-error-text hover:bg-status-error-bg-subtle/80", "disabled:cursor-not-allowed disabled:opacity-50"),
|
|
@@ -97,11 +97,11 @@ function b({ collaboration: t }) {
|
|
|
97
97
|
}), /* @__PURE__ */ _("button", {
|
|
98
98
|
type: "button",
|
|
99
99
|
disabled: m,
|
|
100
|
-
onClick: () =>
|
|
100
|
+
onClick: () => n.mutate({
|
|
101
101
|
id: t.id,
|
|
102
102
|
input: {
|
|
103
103
|
decision: "APPROVE",
|
|
104
|
-
notes:
|
|
104
|
+
notes: a
|
|
105
105
|
}
|
|
106
106
|
}),
|
|
107
107
|
className: e("inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium", "bg-status-success-bg-subtle text-status-success-text hover:bg-status-success-bg-subtle/80", "disabled:cursor-not-allowed disabled:opacity-50"),
|
|
@@ -113,7 +113,7 @@ function b({ collaboration: t }) {
|
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
function x() {
|
|
116
|
-
let [
|
|
116
|
+
let [r, i] = d("ALL"), s = n({ status: r === "ALL" ? void 0 : r }), c = s.data ?? [];
|
|
117
117
|
return /* @__PURE__ */ _(t, {
|
|
118
118
|
title: "Admin — Collaborations",
|
|
119
119
|
description: "Review and advance brand × creator partnerships",
|
|
@@ -121,11 +121,11 @@ function x() {
|
|
|
121
121
|
className: "mb-6 flex flex-wrap items-center gap-2",
|
|
122
122
|
children: y.map((t) => /* @__PURE__ */ g("button", {
|
|
123
123
|
type: "button",
|
|
124
|
-
onClick: () =>
|
|
125
|
-
className: e("rounded-full px-3 py-1.5 text-sm font-medium transition-colors",
|
|
124
|
+
onClick: () => i(t.value),
|
|
125
|
+
className: e("rounded-full px-3 py-1.5 text-sm font-medium transition-colors", r === t.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
126
126
|
children: t.label
|
|
127
127
|
}, t.value))
|
|
128
|
-
}), s.isLoading ? /* @__PURE__ */ g(
|
|
128
|
+
}), s.isLoading ? /* @__PURE__ */ g(o, { label: "Loading collaborations…" }) : s.isError ? /* @__PURE__ */ g(a, { onRetry: () => void s.refetch() }) : c.length === 0 ? /* @__PURE__ */ g(l, {
|
|
129
129
|
illustration: "empty-data",
|
|
130
130
|
icon: /* @__PURE__ */ g(m, { className: "h-10 w-10" }),
|
|
131
131
|
title: "No collaborations to review",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
2
|
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
-
import {
|
|
4
|
-
import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
|
|
5
|
-
import { Thumbnail as a } from "../../components/Thumbnail.js";
|
|
6
|
-
import { useFeatureStyleItem as o, useStyleItems as s, useUnfeatureStyleItem as c } from "../../hooks/useCoolAndLovelyApi.js";
|
|
7
|
-
import { formatPrice as l, titleCase as u } from "../../utils/formatters.js";
|
|
3
|
+
import { useFeatureStyleItem as n, useStyleItems as r, useUnfeatureStyleItem as i } from "../../hooks/useCoolAndLovelyApi.js";
|
|
8
4
|
import "../../hooks/index.js";
|
|
5
|
+
import { StatusBadge as a } from "../../components/StatusBadge.js";
|
|
6
|
+
import { ErrorView as o, LoadingView as s } from "../../components/StateViews.js";
|
|
7
|
+
import { Thumbnail as c } from "../../components/Thumbnail.js";
|
|
8
|
+
import { formatPrice as l, titleCase as u } from "../../utils/formatters.js";
|
|
9
9
|
import { STYLE_CATEGORIES as d, categoryIcon as f } from "../../utils/icons.js";
|
|
10
10
|
import { EmptyState as p } from "../../components/EmptyState.js";
|
|
11
11
|
import { useState as m } from "react";
|
|
@@ -14,11 +14,11 @@ import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
|
14
14
|
//#region src/coolandlovely/pages/admin/AdminFeaturedItemsPage.tsx
|
|
15
15
|
var b = 24;
|
|
16
16
|
function x({ item: t }) {
|
|
17
|
-
let r =
|
|
17
|
+
let r = n(), o = i(), s = r.isPending || o.isPending;
|
|
18
18
|
return /* @__PURE__ */ y("div", {
|
|
19
19
|
className: "flex items-center gap-4 rounded-lg border border-border-seam bg-bg-surface p-3",
|
|
20
20
|
children: [
|
|
21
|
-
/* @__PURE__ */ v(
|
|
21
|
+
/* @__PURE__ */ v(c, {
|
|
22
22
|
icon: f(t.category, 20),
|
|
23
23
|
seed: t.id,
|
|
24
24
|
fileId: t.image?.id,
|
|
@@ -51,10 +51,10 @@ function x({ item: t }) {
|
|
|
51
51
|
}),
|
|
52
52
|
/* @__PURE__ */ y("div", {
|
|
53
53
|
className: "flex shrink-0 items-center gap-2",
|
|
54
|
-
children: [/* @__PURE__ */ v(
|
|
54
|
+
children: [/* @__PURE__ */ v(a, { status: t.status }), t.isFeatured ? /* @__PURE__ */ v("button", {
|
|
55
55
|
type: "button",
|
|
56
56
|
disabled: s,
|
|
57
|
-
onClick: () =>
|
|
57
|
+
onClick: () => o.mutate(t.id),
|
|
58
58
|
className: e("inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium", "bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80", "disabled:cursor-not-allowed disabled:opacity-50"),
|
|
59
59
|
children: "Unfeature"
|
|
60
60
|
}) : /* @__PURE__ */ y("button", {
|
|
@@ -69,11 +69,11 @@ function x({ item: t }) {
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
function S() {
|
|
72
|
-
let [n,
|
|
72
|
+
let [n, i] = m("ALL"), [a, c] = m(0), l = r({
|
|
73
73
|
category: n === "ALL" ? void 0 : n,
|
|
74
74
|
pagination: {
|
|
75
75
|
limit: b,
|
|
76
|
-
offset:
|
|
76
|
+
offset: a
|
|
77
77
|
}
|
|
78
78
|
}), g = l.data?.items ?? [], S = l.data?.pageInfo.hasMore ?? !1;
|
|
79
79
|
return /* @__PURE__ */ y(t, {
|
|
@@ -84,14 +84,14 @@ function S() {
|
|
|
84
84
|
children: [/* @__PURE__ */ v("button", {
|
|
85
85
|
type: "button",
|
|
86
86
|
onClick: () => {
|
|
87
|
-
|
|
87
|
+
i("ALL"), c(0);
|
|
88
88
|
},
|
|
89
89
|
className: e("rounded-full px-3 py-1.5 text-xs font-medium transition-colors", n === "ALL" ? "bg-accent-purple-subtle text-text-primary" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
90
90
|
children: "All categories"
|
|
91
91
|
}), d.map((t) => /* @__PURE__ */ y("button", {
|
|
92
92
|
type: "button",
|
|
93
93
|
onClick: () => {
|
|
94
|
-
|
|
94
|
+
i(t), c(0);
|
|
95
95
|
},
|
|
96
96
|
className: e("inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium transition-colors", n === t ? "bg-accent-purple-subtle text-text-primary" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
97
97
|
children: [
|
|
@@ -100,7 +100,7 @@ function S() {
|
|
|
100
100
|
u(t)
|
|
101
101
|
]
|
|
102
102
|
}, t))]
|
|
103
|
-
}), l.isLoading ? /* @__PURE__ */ v(
|
|
103
|
+
}), l.isLoading ? /* @__PURE__ */ v(s, { label: "Loading style items…" }) : l.isError ? /* @__PURE__ */ v(o, { onRetry: () => void l.refetch() }) : g.length === 0 ? /* @__PURE__ */ v(p, {
|
|
104
104
|
illustration: "empty-data",
|
|
105
105
|
icon: /* @__PURE__ */ v(h, { className: "h-10 w-10" }),
|
|
106
106
|
title: "No style items found",
|
|
@@ -108,19 +108,19 @@ function S() {
|
|
|
108
108
|
}) : /* @__PURE__ */ y(_, { children: [/* @__PURE__ */ v("div", {
|
|
109
109
|
className: "space-y-3",
|
|
110
110
|
children: g.map((e) => /* @__PURE__ */ v(x, { item: e }, e.id))
|
|
111
|
-
}), (
|
|
111
|
+
}), (a > 0 || S) && /* @__PURE__ */ y("div", {
|
|
112
112
|
className: "mt-6 flex items-center justify-center gap-2",
|
|
113
113
|
children: [
|
|
114
114
|
/* @__PURE__ */ v("button", {
|
|
115
115
|
type: "button",
|
|
116
|
-
disabled:
|
|
116
|
+
disabled: a === 0,
|
|
117
117
|
onClick: () => c((e) => Math.max(0, e - b)),
|
|
118
118
|
className: "inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40",
|
|
119
119
|
children: "Previous"
|
|
120
120
|
}),
|
|
121
121
|
/* @__PURE__ */ y("span", {
|
|
122
122
|
className: "text-sm text-text-secondary",
|
|
123
|
-
children: ["Page ",
|
|
123
|
+
children: ["Page ", a / b + 1]
|
|
124
124
|
}),
|
|
125
125
|
/* @__PURE__ */ v("button", {
|
|
126
126
|
type: "button",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrandDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/brands/BrandDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"BrandDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/brands/BrandDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+CH,wBAAgB,eAAe,gCAkT9B"}
|