@burdenoff/microfe-coolandlovely 2026.625.2 → 2026.628.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/CoolAndLovelyRoot.js +66 -122
- package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
- package/dist/coolandlovely/CoolAndLovelyRoutes.js +87 -175
- package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
- package/dist/coolandlovely/components/Field.js +81 -0
- package/dist/coolandlovely/components/Field.js.map +1 -0
- package/dist/coolandlovely/components/StateViews.js +34 -0
- package/dist/coolandlovely/components/StateViews.js.map +1 -0
- package/dist/coolandlovely/components/StatusBadge.js +25 -22
- package/dist/coolandlovely/components/StatusBadge.js.map +1 -1
- package/dist/coolandlovely/components/TagList.js +21 -0
- package/dist/coolandlovely/components/TagList.js.map +1 -0
- package/dist/coolandlovely/components/Thumbnail.js +29 -0
- package/dist/coolandlovely/components/Thumbnail.js.map +1 -0
- package/dist/coolandlovely/context/CoolAndLovelyContext.js +10 -10
- package/dist/coolandlovely/context/CoolAndLovelyContext.js.map +1 -1
- package/dist/coolandlovely/hooks/index.js +1 -0
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +462 -169
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
- package/dist/coolandlovely/pages/HomePage.js +251 -845
- package/dist/coolandlovely/pages/HomePage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js +319 -0
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/brands/BrandFormPage.js +190 -0
- package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/brands/BrandsListPage.js +158 -0
- package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +253 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +206 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +129 -0
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -0
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +217 -0
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/collections/CollectionFormPage.js +217 -0
- package/dist/coolandlovely/pages/collections/CollectionFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js +122 -0
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -0
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +230 -0
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js +276 -0
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js +170 -0
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -0
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +124 -0
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +156 -0
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -0
- package/dist/coolandlovely/pages/looks/LookDetailPage.js +238 -0
- package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -0
- package/dist/coolandlovely/pages/looks/LookFormPage.js +209 -0
- package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/looks/LooksListPage.js +181 -0
- package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -0
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js +224 -0
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -0
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +173 -0
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -0
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +172 -0
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -0
- package/dist/coolandlovely/store/coolandlovelyStore.js +2 -6
- package/dist/coolandlovely/store/coolandlovelyStore.js.map +1 -1
- package/dist/coolandlovely/utils/formatters.js +31 -23
- package/dist/coolandlovely/utils/formatters.js.map +1 -1
- package/dist/coolandlovely/utils/icons.js +34 -0
- package/dist/coolandlovely/utils/icons.js.map +1 -0
- package/dist/index.js +4 -4
- package/package.json +1 -1
- package/dist/coolandlovely/components/InvestmentCard.js +0 -98
- package/dist/coolandlovely/components/InvestmentCard.js.map +0 -1
- package/dist/coolandlovely/components/RatingDisplay.js +0 -35
- package/dist/coolandlovely/components/RatingDisplay.js.map +0 -1
- package/dist/coolandlovely/mocks/data.js +0 -1517
- package/dist/coolandlovely/mocks/data.js.map +0 -1
- package/dist/coolandlovely/mocks/users.js +0 -34
- package/dist/coolandlovely/mocks/users.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminAnalyticsPage.js +0 -651
- package/dist/coolandlovely/pages/admin/AdminAnalyticsPage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminDashboard.js +0 -673
- package/dist/coolandlovely/pages/admin/AdminDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminFinancialPage.js +0 -427
- package/dist/coolandlovely/pages/admin/AdminFinancialPage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminOrdersPage.js +0 -665
- package/dist/coolandlovely/pages/admin/AdminOrdersPage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminProfilePage.js +0 -264
- package/dist/coolandlovely/pages/admin/AdminProfilePage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/AdminSettingsPage.js +0 -654
- package/dist/coolandlovely/pages/admin/AdminSettingsPage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/CartManagementPage.js +0 -874
- package/dist/coolandlovely/pages/admin/CartManagementPage.js.map +0 -1
- package/dist/coolandlovely/pages/admin/UserManagementPage.js +0 -898
- package/dist/coolandlovely/pages/admin/UserManagementPage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/AnalyticsDashboard.js +0 -842
- package/dist/coolandlovely/pages/cart-owner/AnalyticsDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/BusinessSettingsPage.js +0 -705
- package/dist/coolandlovely/pages/cart-owner/BusinessSettingsPage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/CartOwnerDashboard.js +0 -802
- package/dist/coolandlovely/pages/cart-owner/CartOwnerDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/CartOwnerProfilePage.js +0 -891
- package/dist/coolandlovely/pages/cart-owner/CartOwnerProfilePage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/CartServiceConfigPage.js +0 -639
- package/dist/coolandlovely/pages/cart-owner/CartServiceConfigPage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/InventoryManagementPage.js +0 -914
- package/dist/coolandlovely/pages/cart-owner/InventoryManagementPage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/OrderManagementPage.js +0 -882
- package/dist/coolandlovely/pages/cart-owner/OrderManagementPage.js.map +0 -1
- package/dist/coolandlovely/pages/cart-owner/StaffManagementPage.js +0 -831
- package/dist/coolandlovely/pages/cart-owner/StaffManagementPage.js.map +0 -1
- package/dist/coolandlovely/pages/common/HelpSupportPage.js +0 -1325
- package/dist/coolandlovely/pages/common/HelpSupportPage.js.map +0 -1
- package/dist/coolandlovely/pages/common/NotificationCenterPage.js +0 -671
- package/dist/coolandlovely/pages/common/NotificationCenterPage.js.map +0 -1
- package/dist/coolandlovely/pages/common/SettingsPage.js +0 -973
- package/dist/coolandlovely/pages/common/SettingsPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/BookingDetailsPage.js +0 -630
- package/dist/coolandlovely/pages/customer/BookingDetailsPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/CartDetailPage.js +0 -623
- package/dist/coolandlovely/pages/customer/CartDetailPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/CartDiscoveryPage.js +0 -558
- package/dist/coolandlovely/pages/customer/CartDiscoveryPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/CheckoutPage.js +0 -529
- package/dist/coolandlovely/pages/customer/CheckoutPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/CustomerDashboard.js +0 -538
- package/dist/coolandlovely/pages/customer/CustomerDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/customer/CustomerProfilePage.js +0 -658
- package/dist/coolandlovely/pages/customer/CustomerProfilePage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/DirectServiceBookingPage.js +0 -632
- package/dist/coolandlovely/pages/customer/DirectServiceBookingPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/FavoritesPage.js +0 -487
- package/dist/coolandlovely/pages/customer/FavoritesPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/OrderHistoryPage.js +0 -447
- package/dist/coolandlovely/pages/customer/OrderHistoryPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/OrderTrackingPage.js +0 -425
- package/dist/coolandlovely/pages/customer/OrderTrackingPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/PaymentMethodsPage.js +0 -494
- package/dist/coolandlovely/pages/customer/PaymentMethodsPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/ReviewsPage.js +0 -616
- package/dist/coolandlovely/pages/customer/ReviewsPage.js.map +0 -1
- package/dist/coolandlovely/pages/customer/SavedAddressesPage.js +0 -449
- package/dist/coolandlovely/pages/customer/SavedAddressesPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestmentDetailsPage.js +0 -887
- package/dist/coolandlovely/pages/investor/InvestmentDetailsPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestmentOpportunitiesPage.js +0 -945
- package/dist/coolandlovely/pages/investor/InvestmentOpportunitiesPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestorAnalyticsPage.js +0 -687
- package/dist/coolandlovely/pages/investor/InvestorAnalyticsPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestorDashboard.js +0 -455
- package/dist/coolandlovely/pages/investor/InvestorDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestorInvestmentsPage.js +0 -405
- package/dist/coolandlovely/pages/investor/InvestorInvestmentsPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestorPortfolioPage.js +0 -602
- package/dist/coolandlovely/pages/investor/InvestorPortfolioPage.js.map +0 -1
- package/dist/coolandlovely/pages/investor/InvestorProfilePage.js +0 -1070
- package/dist/coolandlovely/pages/investor/InvestorProfilePage.js.map +0 -1
- package/dist/coolandlovely/pages/worker/TaskDetailsPage.js +0 -533
- package/dist/coolandlovely/pages/worker/TaskDetailsPage.js.map +0 -1
- package/dist/coolandlovely/pages/worker/WorkerDashboard.js +0 -671
- package/dist/coolandlovely/pages/worker/WorkerDashboard.js.map +0 -1
- package/dist/coolandlovely/pages/worker/WorkerOrdersPage.js +0 -680
- package/dist/coolandlovely/pages/worker/WorkerOrdersPage.js.map +0 -1
- package/dist/coolandlovely/pages/worker/WorkerProfilePage.js +0 -692
- package/dist/coolandlovely/pages/worker/WorkerProfilePage.js.map +0 -1
- package/dist/coolandlovely/pages/worker/WorkerSchedulePage.js +0 -583
- package/dist/coolandlovely/pages/worker/WorkerSchedulePage.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollectionDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/collections/CollectionDetailPage.tsx"],"sourcesContent":["/**\n * Collection Detail Page\n * @module coolandlovely/pages/collections\n *\n * Single collection (`cooCollection`) with publish (`cooPublishCollection`),\n * edit, its brand link and member items.\n */\n\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Pencil, Rocket, Loader2, Shapes, Store, Plus } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useCollection, usePublishCollection } from '../../hooks';\nimport { categoryIcon } from '../../utils/icons';\nimport { formatPrice, formatDate, titleCase } from '../../utils/formatters';\n\nexport function CollectionDetailPage() {\n const { collectionId } = useParams<{ collectionId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useCollection(collectionId);\n const publish = usePublishCollection();\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Collection\">\n <LoadingView label=\"Loading collection…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Collection\">\n <ErrorView title=\"Collection not found\" onRetry={() => void query.refetch()} />\n </PageLayout>\n );\n }\n\n const collection = query.data;\n const items = collection.items ?? [];\n const canPublish = collection.status === 'DRAFT' || collection.status === 'SCHEDULED';\n\n return (\n <PageLayout\n title={collection.name}\n description={collection.brand?.name ?? undefined}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/collections')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/collections/${collection.id}/edit`)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Pencil className=\"h-4 w-4\" /> Edit\n </button>\n {canPublish && (\n <button\n type=\"button\"\n disabled={publish.isPending}\n onClick={() => publish.mutate(collection.id)}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {publish.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Rocket className=\"h-4 w-4\" />\n )}\n Publish\n </button>\n )}\n </div>\n }\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2\">\n <GlassCard treatment=\"glass\" className=\"overflow-hidden p-0\">\n <Thumbnail icon={<Shapes className=\"h-12 w-12\" />} seed={collection.id} className=\"h-40 w-full\" />\n <div className=\"p-6\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"text-xl font-semibold text-text-primary\">{collection.name}</h2>\n <StatusBadge status={collection.status} className=\"ml-auto\" />\n </div>\n {collection.description && (\n <p className=\"mt-3 text-sm text-text-secondary\">{collection.description}</p>\n )}\n {collection.tags.length > 0 && <TagList tags={collection.tags} className=\"mt-4\" />}\n </div>\n </GlassCard>\n\n <section className=\"mt-6\">\n <h3 className=\"mb-3 text-lg font-semibold text-text-primary\">\n Items ({items.length})\n </h3>\n {items.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n No items in this collection yet.\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3\">\n {items.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => navigateTo(`/items/${item.id}`)}\n className=\"overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail icon={categoryIcon(item.category, 24)} seed={item.id} className=\"h-24 w-full\" />\n <div className=\"p-3\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{item.name}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {formatPrice(item.price, item.currency)}\n </p>\n </div>\n </button>\n ))}\n </div>\n )}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/items/new?brandId=${collection.brandId}&collectionId=${collection.id}`)}\n className=\"mt-3 inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <Plus className=\"h-4 w-4\" /> Add item to this collection\n </button>\n </section>\n </div>\n\n <div className=\"space-y-4\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">Details</h3>\n <dl className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Season</dt>\n <dd className=\"text-text-primary\">{titleCase(collection.season)}</dd>\n </div>\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Publish at</dt>\n <dd className=\"text-text-primary\">\n {collection.publishAt\n ? formatDate(collection.publishAt, { hour: undefined, minute: undefined })\n : '—'}\n </dd>\n </div>\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Ends at</dt>\n <dd className=\"text-text-primary\">\n {collection.endAt\n ? formatDate(collection.endAt, { hour: undefined, minute: undefined })\n : '—'}\n </dd>\n </div>\n </dl>\n </GlassCard>\n\n {collection.brand && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/brands/${collection.brand?.id}`)}\n className=\"flex w-full items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n <Thumbnail icon={<Store className=\"h-5 w-5\" />} seed={collection.brand.id} className=\"h-10 w-10 shrink-0\" />\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{collection.brand.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">View brand</p>\n </div>\n </button>\n )}\n\n {publish.isError && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-xs text-status-error-text\">\n {publish.error instanceof Error ? publish.error.message : 'Publish failed'}\n </p>\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAqBA,SAAgB,IAAuB;CACrC,IAAM,EAAE,oBAAiB,GAAqC,EACxD,EAAE,kBAAe,GAAkB,EACnC,IAAQ,EAAc,EAAa,EACnC,IAAU,GAAsB;AAEtC,KAAI,EAAM,UACR,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD,EAAa,OAAM,uBAAwB,CAAA;EAChC,CAAA;AAIjB,KAAI,EAAM,WAAW,CAAC,EAAM,KAC1B,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD;GAAW,OAAM;GAAuB,eAAe,KAAK,EAAM,SAAS;GAAI,CAAA;EACpE,CAAA;CAIjB,IAAM,IAAa,EAAM,MACnB,IAAQ,EAAW,SAAS,EAAE,EAC9B,IAAa,EAAW,WAAW,WAAW,EAAW,WAAW;AAE1E,QACE,kBAAC,GAAD;EACE,OAAO,EAAW;EAClB,aAAa,EAAW,OAAO,QAAQ,KAAA;EACvC,SACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,eAAe;KACzC,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,QAC1B;;IACT,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,gBAAgB,EAAW,GAAG,OAAO;KAC/D,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,QACvB;;IACR,KACC,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,EAAQ;KAClB,eAAe,EAAQ,OAAO,EAAW,GAAG;KAC5C,WAAU;eAJZ,CAMG,EAAQ,YACP,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAC9B,UAEK;;IAEP;;YAGR,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAW,WAAU;KAAQ,WAAU;eAAvC,CACE,kBAAC,GAAD;MAAW,MAAM,kBAAC,GAAD,EAAQ,WAAU,aAAc,CAAA;MAAE,MAAM,EAAW;MAAI,WAAU;MAAgB,CAAA,EAClG,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAA2C,EAAW;SAAU,CAAA,EAC9E,kBAAC,GAAD;SAAa,QAAQ,EAAW;SAAQ,WAAU;SAAY,CAAA,CAC1D;;OACL,EAAW,eACV,kBAAC,KAAD;QAAG,WAAU;kBAAoC,EAAW;QAAgB,CAAA;OAE7E,EAAW,KAAK,SAAS,KAAK,kBAAC,GAAD;QAAS,MAAM,EAAW;QAAM,WAAU;QAAS,CAAA;OAC9E;QACI;QAEZ,kBAAC,WAAD;KAAS,WAAU;eAAnB;MACE,kBAAC,MAAD;OAAI,WAAU;iBAAd;QAA6D;QACnD,EAAM;QAAO;QAClB;;MACJ,EAAM,WAAW,IAChB,kBAAC,KAAD;OAAG,WAAU;iBAAqF;OAE9F,CAAA,GAEJ,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAM,KAAK,MACV,kBAAC,UAAD;QAEE,MAAK;QACL,eAAe,EAAW,UAAU,EAAK,KAAK;QAC9C,WAAU;kBAJZ,CAME,kBAAC,GAAD;SAAW,MAAM,EAAa,EAAK,UAAU,GAAG;SAAE,MAAM,EAAK;SAAI,WAAU;SAAgB,CAAA,EAC3F,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAkD,EAAK;UAAS,CAAA,EAC7E,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAY,EAAK,OAAO,EAAK,SAAS;UACrC,CAAA,CACA;WACC;UAZF,EAAK,GAYH,CACT;OACE,CAAA;MAER,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,sBAAsB,EAAW,QAAQ,gBAAgB,EAAW,KAAK;OACnG,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,+BACrB;;MACD;OACN;OAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;OAAY,CAAA,EACzE,kBAAC,MAAD;OAAI,WAAU;iBAAd;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBAAsB;UAAW,CAAA,EAC/C,kBAAC,MAAD;UAAI,WAAU;oBAAqB,EAAU,EAAW,OAAO;UAAM,CAAA,CACjE;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBAAsB;UAAe,CAAA,EACnD,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAW,YACR,EAAW,EAAW,WAAW;WAAE,MAAM,KAAA;WAAW,QAAQ,KAAA;WAAW,CAAC,GACxE;UACD,CAAA,CACD;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBAAsB;UAAY,CAAA,EAChD,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAW,QACR,EAAW,EAAW,OAAO;WAAE,MAAM,KAAA;WAAW,QAAQ,KAAA;WAAW,CAAC,GACpE;UACD,CAAA,CACD;;QACH;SACK;;KAEX,EAAW,SACV,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,WAAW,EAAW,OAAO,KAAK;MAC5D,WAAU;gBAHZ,CAKE,kBAAC,GAAD;OAAW,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OAAE,MAAM,EAAW,MAAM;OAAI,WAAU;OAAuB,CAAA,EAC5G,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAkD,EAAW,MAAM;QAAS,CAAA,EACzF,kBAAC,KAAD;QAAG,WAAU;kBAAuC;QAAc,CAAA,CAC9D;SACC;;KAGV,EAAQ,WACP,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAQ,iBAAiB,QAAQ,EAAQ,MAAM,UAAU;MACxD,CAAA;KAEF;MACF;;EACK,CAAA"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
+
import { ErrorView as n, LoadingView as r } from "../../components/StateViews.js";
|
|
4
|
+
import { useBrands as i, useCollection as a, useCreateCollection as o, useUpdateCollection as s } from "../../hooks/useCoolAndLovelyApi.js";
|
|
5
|
+
import "../../hooks/index.js";
|
|
6
|
+
import { parseCsv as c, titleCase as l } from "../../utils/formatters.js";
|
|
7
|
+
import { SelectField as u, TextAreaField as d, TextField as f } from "../../components/Field.js";
|
|
8
|
+
import { useEffect as p, useMemo as m, useState as h } from "react";
|
|
9
|
+
import { ArrowLeft as g, Loader2 as _, Save as v, Shapes as y } from "lucide-react";
|
|
10
|
+
import { useParams as b, useSearchParams as x } from "react-router-dom";
|
|
11
|
+
import { jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
12
|
+
import { GlassCard as w } from "@burdenoff/fe-libs/ui";
|
|
13
|
+
//#region src/coolandlovely/pages/collections/CollectionFormPage.tsx
|
|
14
|
+
var T = [
|
|
15
|
+
"SPRING",
|
|
16
|
+
"SUMMER",
|
|
17
|
+
"AUTUMN",
|
|
18
|
+
"WINTER",
|
|
19
|
+
"RESORT",
|
|
20
|
+
"ALL_SEASON"
|
|
21
|
+
], E = {
|
|
22
|
+
brandId: "",
|
|
23
|
+
name: "",
|
|
24
|
+
description: "",
|
|
25
|
+
season: "ALL_SEASON",
|
|
26
|
+
tags: "",
|
|
27
|
+
publishAt: "",
|
|
28
|
+
endAt: ""
|
|
29
|
+
};
|
|
30
|
+
function D(e) {
|
|
31
|
+
if (!e) return;
|
|
32
|
+
let t = new Date(e);
|
|
33
|
+
return Number.isNaN(t.getTime()) ? void 0 : t.toISOString();
|
|
34
|
+
}
|
|
35
|
+
function O(e) {
|
|
36
|
+
if (!e) return "";
|
|
37
|
+
let t = new Date(e);
|
|
38
|
+
return Number.isNaN(t.getTime()) ? "" : t.toISOString().slice(0, 10);
|
|
39
|
+
}
|
|
40
|
+
function k() {
|
|
41
|
+
let { collectionId: k } = b(), [A] = x(), j = !!k, { navigateTo: M } = e(), N = a(j ? k : void 0), P = i({ pagination: {
|
|
42
|
+
limit: 100,
|
|
43
|
+
offset: 0
|
|
44
|
+
} }), F = o(), I = s(), [L, R] = h(() => ({
|
|
45
|
+
...E,
|
|
46
|
+
brandId: A.get("brandId") ?? ""
|
|
47
|
+
})), [z, B] = h({});
|
|
48
|
+
p(() => {
|
|
49
|
+
if (j && N.data) {
|
|
50
|
+
let e = N.data;
|
|
51
|
+
R({
|
|
52
|
+
brandId: e.brandId,
|
|
53
|
+
name: e.name,
|
|
54
|
+
description: e.description ?? "",
|
|
55
|
+
season: e.season,
|
|
56
|
+
tags: e.tags.join(", "),
|
|
57
|
+
publishAt: O(e.publishAt),
|
|
58
|
+
endAt: O(e.endAt)
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}, [j, N.data]);
|
|
62
|
+
let V = (e, t) => R((n) => ({
|
|
63
|
+
...n,
|
|
64
|
+
[e]: t
|
|
65
|
+
})), H = j ? I : F, U = m(() => (P.data?.items ?? []).map((e) => ({
|
|
66
|
+
value: e.id,
|
|
67
|
+
label: e.name
|
|
68
|
+
})), [P.data]), W = T.map((e) => ({
|
|
69
|
+
value: e,
|
|
70
|
+
label: l(e)
|
|
71
|
+
})), G = () => {
|
|
72
|
+
let e = {};
|
|
73
|
+
return L.name.trim() || (e.name = "Name is required"), !j && !L.brandId && (e.brandId = "Brand is required"), B(e), Object.keys(e).length === 0;
|
|
74
|
+
};
|
|
75
|
+
return j && N.isLoading ? /* @__PURE__ */ S(t, {
|
|
76
|
+
title: "Edit collection",
|
|
77
|
+
children: /* @__PURE__ */ S(r, { label: "Loading collection…" })
|
|
78
|
+
}) : j && (N.isError || !N.data) ? /* @__PURE__ */ S(t, {
|
|
79
|
+
title: "Edit collection",
|
|
80
|
+
children: /* @__PURE__ */ S(n, {
|
|
81
|
+
title: "Collection not found",
|
|
82
|
+
onRetry: () => void N.refetch()
|
|
83
|
+
})
|
|
84
|
+
}) : /* @__PURE__ */ S(t, {
|
|
85
|
+
title: j ? "Edit collection" : "New collection",
|
|
86
|
+
description: j ? "Update this seasonal drop" : "Group pieces into a seasonal drop",
|
|
87
|
+
actions: /* @__PURE__ */ C("button", {
|
|
88
|
+
type: "button",
|
|
89
|
+
onClick: () => M(j && k ? `/collections/${k}` : "/collections"),
|
|
90
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
|
|
91
|
+
children: [/* @__PURE__ */ S(g, { className: "h-4 w-4" }), " Cancel"]
|
|
92
|
+
}),
|
|
93
|
+
children: /* @__PURE__ */ S("form", {
|
|
94
|
+
onSubmit: (e) => {
|
|
95
|
+
e.preventDefault(), G() && (j && k ? I.mutate({
|
|
96
|
+
id: k,
|
|
97
|
+
input: {
|
|
98
|
+
name: L.name.trim(),
|
|
99
|
+
description: L.description.trim() || void 0,
|
|
100
|
+
season: L.season,
|
|
101
|
+
tags: c(L.tags),
|
|
102
|
+
publishAt: D(L.publishAt),
|
|
103
|
+
endAt: D(L.endAt)
|
|
104
|
+
}
|
|
105
|
+
}, { onSuccess: () => M(`/collections/${k}`) }) : F.mutate({
|
|
106
|
+
brandId: L.brandId,
|
|
107
|
+
name: L.name.trim(),
|
|
108
|
+
description: L.description.trim() || void 0,
|
|
109
|
+
season: L.season,
|
|
110
|
+
tags: c(L.tags),
|
|
111
|
+
publishAt: D(L.publishAt),
|
|
112
|
+
endAt: D(L.endAt)
|
|
113
|
+
}, { onSuccess: (e) => M(`/collections/${e.id}`) }));
|
|
114
|
+
},
|
|
115
|
+
className: "mx-auto max-w-2xl",
|
|
116
|
+
children: /* @__PURE__ */ C(w, {
|
|
117
|
+
treatment: "plain",
|
|
118
|
+
className: "p-6",
|
|
119
|
+
children: [
|
|
120
|
+
/* @__PURE__ */ C("div", {
|
|
121
|
+
className: "mb-6 flex items-center gap-3",
|
|
122
|
+
children: [/* @__PURE__ */ S("div", {
|
|
123
|
+
className: "flex h-11 w-11 items-center justify-center rounded-lg bg-accent-blue-subtle text-accent-blue",
|
|
124
|
+
children: /* @__PURE__ */ S(y, { className: "h-5 w-5" })
|
|
125
|
+
}), /* @__PURE__ */ C("div", { children: [/* @__PURE__ */ S("h2", {
|
|
126
|
+
className: "text-base font-semibold text-text-primary",
|
|
127
|
+
children: "Collection details"
|
|
128
|
+
}), /* @__PURE__ */ S("p", {
|
|
129
|
+
className: "text-xs text-text-secondary",
|
|
130
|
+
children: "A collection belongs to one brand."
|
|
131
|
+
})] })]
|
|
132
|
+
}),
|
|
133
|
+
!j && /* @__PURE__ */ S(u, {
|
|
134
|
+
id: "brandId",
|
|
135
|
+
label: "Brand",
|
|
136
|
+
value: L.brandId,
|
|
137
|
+
onChange: (e) => V("brandId", e),
|
|
138
|
+
options: U,
|
|
139
|
+
required: !0,
|
|
140
|
+
placeholder: P.isLoading ? "Loading brands…" : "Select a brand",
|
|
141
|
+
error: z.brandId,
|
|
142
|
+
className: "mb-4"
|
|
143
|
+
}),
|
|
144
|
+
/* @__PURE__ */ C("div", {
|
|
145
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
146
|
+
children: [/* @__PURE__ */ S(f, {
|
|
147
|
+
id: "name",
|
|
148
|
+
label: "Name",
|
|
149
|
+
value: L.name,
|
|
150
|
+
onChange: (e) => V("name", e),
|
|
151
|
+
placeholder: "Spring Awakening",
|
|
152
|
+
required: !0,
|
|
153
|
+
error: z.name
|
|
154
|
+
}), /* @__PURE__ */ S(u, {
|
|
155
|
+
id: "season",
|
|
156
|
+
label: "Season",
|
|
157
|
+
value: L.season,
|
|
158
|
+
onChange: (e) => V("season", e),
|
|
159
|
+
options: W
|
|
160
|
+
})]
|
|
161
|
+
}),
|
|
162
|
+
/* @__PURE__ */ S(d, {
|
|
163
|
+
id: "description",
|
|
164
|
+
label: "Description",
|
|
165
|
+
value: L.description,
|
|
166
|
+
onChange: (e) => V("description", e),
|
|
167
|
+
placeholder: "Describe the drop…",
|
|
168
|
+
rows: 3,
|
|
169
|
+
className: "mt-4"
|
|
170
|
+
}),
|
|
171
|
+
/* @__PURE__ */ C("div", {
|
|
172
|
+
className: "mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
173
|
+
children: [/* @__PURE__ */ S(f, {
|
|
174
|
+
id: "publishAt",
|
|
175
|
+
label: "Publish date",
|
|
176
|
+
type: "date",
|
|
177
|
+
value: L.publishAt,
|
|
178
|
+
onChange: (e) => V("publishAt", e)
|
|
179
|
+
}), /* @__PURE__ */ S(f, {
|
|
180
|
+
id: "endAt",
|
|
181
|
+
label: "End date",
|
|
182
|
+
type: "date",
|
|
183
|
+
value: L.endAt,
|
|
184
|
+
onChange: (e) => V("endAt", e)
|
|
185
|
+
})]
|
|
186
|
+
}),
|
|
187
|
+
/* @__PURE__ */ S(f, {
|
|
188
|
+
id: "tags",
|
|
189
|
+
label: "Tags",
|
|
190
|
+
value: L.tags,
|
|
191
|
+
onChange: (e) => V("tags", e),
|
|
192
|
+
placeholder: "pastel, floral",
|
|
193
|
+
hint: "Comma-separated",
|
|
194
|
+
className: "mt-4"
|
|
195
|
+
}),
|
|
196
|
+
H.isError && /* @__PURE__ */ S("p", {
|
|
197
|
+
className: "mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
|
|
198
|
+
children: H.error instanceof Error ? H.error.message : "Something went wrong"
|
|
199
|
+
}),
|
|
200
|
+
/* @__PURE__ */ S("div", {
|
|
201
|
+
className: "mt-6 flex justify-end gap-2",
|
|
202
|
+
children: /* @__PURE__ */ C("button", {
|
|
203
|
+
type: "submit",
|
|
204
|
+
disabled: H.isPending,
|
|
205
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50",
|
|
206
|
+
children: [H.isPending ? /* @__PURE__ */ S(_, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ S(v, { className: "h-4 w-4" }), j ? "Save changes" : "Create collection"]
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
]
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
//#endregion
|
|
215
|
+
export { k as CollectionFormPage };
|
|
216
|
+
|
|
217
|
+
//# sourceMappingURL=CollectionFormPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollectionFormPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/collections/CollectionFormPage.tsx"],"sourcesContent":["/**\n * Collection Create / Edit Page\n * @module coolandlovely/pages/collections\n *\n * Drives `cooCreateCollection` (new) and `cooUpdateCollection` (edit).\n * Brand selection is required on create and locked on edit.\n */\n\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams, useSearchParams } from 'react-router-dom';\nimport { ArrowLeft, Loader2, Save, Shapes } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { TextField, TextAreaField, SelectField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useCollection, useCreateCollection, useUpdateCollection, useBrands } from '../../hooks';\nimport { parseCsv, titleCase } from '../../utils/formatters';\nimport type { CooSeason } from '../../types';\n\nconst SEASONS: CooSeason[] = ['SPRING', 'SUMMER', 'AUTUMN', 'WINTER', 'RESORT', 'ALL_SEASON'];\n\ninterface FormState {\n brandId: string;\n name: string;\n description: string;\n season: CooSeason;\n tags: string;\n publishAt: string;\n endAt: string;\n}\n\nconst EMPTY: FormState = {\n brandId: '',\n name: '',\n description: '',\n season: 'ALL_SEASON',\n tags: '',\n publishAt: '',\n endAt: '',\n};\n\n/** ISO timestamp <input type=date> values use yyyy-mm-dd; convert to ISO. */\nfunction toIso(date: string): string | undefined {\n if (!date) return undefined;\n const d = new Date(date);\n return Number.isNaN(d.getTime()) ? undefined : d.toISOString();\n}\n\nfunction toDateInput(iso: string | null): string {\n if (!iso) return '';\n const d = new Date(iso);\n return Number.isNaN(d.getTime()) ? '' : d.toISOString().slice(0, 10);\n}\n\nexport function CollectionFormPage() {\n const { collectionId } = useParams<{ collectionId: string }>();\n const [searchParams] = useSearchParams();\n const isEdit = Boolean(collectionId);\n const { navigateTo } = useCoolAndLovely();\n\n const existing = useCollection(isEdit ? collectionId : undefined);\n const brands = useBrands({ pagination: { limit: 100, offset: 0 } });\n const createCollection = useCreateCollection();\n const updateCollection = useUpdateCollection();\n\n const [form, setForm] = useState<FormState>(() => ({\n ...EMPTY,\n brandId: searchParams.get('brandId') ?? '',\n }));\n const [errors, setErrors] = useState<Partial<Record<keyof FormState, string>>>({});\n\n useEffect(() => {\n if (isEdit && existing.data) {\n const c = existing.data;\n setForm({\n brandId: c.brandId,\n name: c.name,\n description: c.description ?? '',\n season: c.season,\n tags: c.tags.join(', '),\n publishAt: toDateInput(c.publishAt),\n endAt: toDateInput(c.endAt),\n });\n }\n }, [isEdit, existing.data]);\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) =>\n setForm((f) => ({ ...f, [key]: value }));\n\n const mutation = isEdit ? updateCollection : createCollection;\n\n const brandOptions = useMemo(\n () => (brands.data?.items ?? []).map((b) => ({ value: b.id, label: b.name })),\n [brands.data]\n );\n const seasonOptions = SEASONS.map((s) => ({ value: s, label: titleCase(s) }));\n\n const validate = (): boolean => {\n const next: Partial<Record<keyof FormState, string>> = {};\n if (!form.name.trim()) next.name = 'Name is required';\n if (!isEdit && !form.brandId) next.brandId = 'Brand is required';\n setErrors(next);\n return Object.keys(next).length === 0;\n };\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!validate()) return;\n\n if (isEdit && collectionId) {\n updateCollection.mutate(\n {\n id: collectionId,\n input: {\n name: form.name.trim(),\n description: form.description.trim() || undefined,\n season: form.season,\n tags: parseCsv(form.tags),\n publishAt: toIso(form.publishAt),\n endAt: toIso(form.endAt),\n },\n },\n { onSuccess: () => navigateTo(`/collections/${collectionId}`) }\n );\n } else {\n createCollection.mutate(\n {\n brandId: form.brandId,\n name: form.name.trim(),\n description: form.description.trim() || undefined,\n season: form.season,\n tags: parseCsv(form.tags),\n publishAt: toIso(form.publishAt),\n endAt: toIso(form.endAt),\n },\n { onSuccess: (c) => navigateTo(`/collections/${c.id}`) }\n );\n }\n };\n\n if (isEdit && existing.isLoading) {\n return (\n <PageLayout title=\"Edit collection\">\n <LoadingView label=\"Loading collection…\" />\n </PageLayout>\n );\n }\n\n if (isEdit && (existing.isError || !existing.data)) {\n return (\n <PageLayout title=\"Edit collection\">\n <ErrorView title=\"Collection not found\" onRetry={() => void existing.refetch()} />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={isEdit ? 'Edit collection' : 'New collection'}\n description={isEdit ? 'Update this seasonal drop' : 'Group pieces into a seasonal drop'}\n actions={\n <button\n type=\"button\"\n onClick={() =>\n navigateTo(isEdit && collectionId ? `/collections/${collectionId}` : '/collections')\n }\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Cancel\n </button>\n }\n >\n <form onSubmit={handleSubmit} className=\"mx-auto max-w-2xl\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-11 w-11 items-center justify-center rounded-lg bg-accent-blue-subtle text-accent-blue\">\n <Shapes className=\"h-5 w-5\" />\n </div>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Collection details</h2>\n <p className=\"text-xs text-text-secondary\">A collection belongs to one brand.</p>\n </div>\n </div>\n\n {!isEdit && (\n <SelectField\n id=\"brandId\"\n label=\"Brand\"\n value={form.brandId}\n onChange={(v) => setField('brandId', v)}\n options={brandOptions}\n required\n placeholder={brands.isLoading ? 'Loading brands…' : 'Select a brand'}\n error={errors.brandId}\n className=\"mb-4\"\n />\n )}\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <TextField\n id=\"name\"\n label=\"Name\"\n value={form.name}\n onChange={(v) => setField('name', v)}\n placeholder=\"Spring Awakening\"\n required\n error={errors.name}\n />\n <SelectField\n id=\"season\"\n label=\"Season\"\n value={form.season}\n onChange={(v) => setField('season', v as CooSeason)}\n options={seasonOptions}\n />\n </div>\n\n <TextAreaField\n id=\"description\"\n label=\"Description\"\n value={form.description}\n onChange={(v) => setField('description', v)}\n placeholder=\"Describe the drop…\"\n rows={3}\n className=\"mt-4\"\n />\n\n <div className=\"mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <TextField\n id=\"publishAt\"\n label=\"Publish date\"\n type=\"date\"\n value={form.publishAt}\n onChange={(v) => setField('publishAt', v)}\n />\n <TextField\n id=\"endAt\"\n label=\"End date\"\n type=\"date\"\n value={form.endAt}\n onChange={(v) => setField('endAt', v)}\n />\n </div>\n\n <TextField\n id=\"tags\"\n label=\"Tags\"\n value={form.tags}\n onChange={(v) => setField('tags', v)}\n placeholder=\"pastel, floral\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n\n {mutation.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {mutation.error instanceof Error ? mutation.error.message : 'Something went wrong'}\n </p>\n )}\n\n <div className=\"mt-6 flex justify-end gap-2\">\n <button\n type=\"submit\"\n disabled={mutation.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {mutation.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n {isEdit ? 'Save changes' : 'Create collection'}\n </button>\n </div>\n </GlassCard>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAoBA,IAAM,IAAuB;CAAC;CAAU;CAAU;CAAU;CAAU;CAAU;CAAa,EAYvF,IAAmB;CACvB,SAAS;CACT,MAAM;CACN,aAAa;CACb,QAAQ;CACR,MAAM;CACN,WAAW;CACX,OAAO;CACR;AAGD,SAAS,EAAM,GAAkC;AAC/C,KAAI,CAAC,EAAM;CACX,IAAM,IAAI,IAAI,KAAK,EAAK;AACxB,QAAO,OAAO,MAAM,EAAE,SAAS,CAAC,GAAG,KAAA,IAAY,EAAE,aAAa;;AAGhE,SAAS,EAAY,GAA4B;AAC/C,KAAI,CAAC,EAAK,QAAO;CACjB,IAAM,IAAI,IAAI,KAAK,EAAI;AACvB,QAAO,OAAO,MAAM,EAAE,SAAS,CAAC,GAAG,KAAK,EAAE,aAAa,CAAC,MAAM,GAAG,GAAG;;AAGtE,SAAgB,IAAqB;CACnC,IAAM,EAAE,oBAAiB,GAAqC,EACxD,CAAC,KAAgB,GAAiB,EAClC,IAAS,EAAQ,GACjB,EAAE,kBAAe,GAAkB,EAEnC,IAAW,EAAc,IAAS,IAAe,KAAA,EAAU,EAC3D,IAAS,EAAU,EAAE,YAAY;EAAE,OAAO;EAAK,QAAQ;EAAG,EAAE,CAAC,EAC7D,IAAmB,GAAqB,EACxC,IAAmB,GAAqB,EAExC,CAAC,GAAM,KAAW,SAA2B;EACjD,GAAG;EACH,SAAS,EAAa,IAAI,UAAU,IAAI;EACzC,EAAE,EACG,CAAC,GAAQ,KAAa,EAAmD,EAAE,CAAC;AAElF,SAAgB;AACd,MAAI,KAAU,EAAS,MAAM;GAC3B,IAAM,IAAI,EAAS;AACnB,KAAQ;IACN,SAAS,EAAE;IACX,MAAM,EAAE;IACR,aAAa,EAAE,eAAe;IAC9B,QAAQ,EAAE;IACV,MAAM,EAAE,KAAK,KAAK,KAAK;IACvB,WAAW,EAAY,EAAE,UAAU;IACnC,OAAO,EAAY,EAAE,MAAM;IAC5B,CAAC;;IAEH,CAAC,GAAQ,EAAS,KAAK,CAAC;CAE3B,IAAM,KAAuC,GAAQ,MACnD,GAAS,OAAO;EAAE,GAAG;GAAI,IAAM;EAAO,EAAE,EAEpC,IAAW,IAAS,IAAmB,GAEvC,IAAe,SACZ,EAAO,MAAM,SAAS,EAAE,EAAE,KAAK,OAAO;EAAE,OAAO,EAAE;EAAI,OAAO,EAAE;EAAM,EAAE,EAC7E,CAAC,EAAO,KAAK,CACd,EACK,IAAgB,EAAQ,KAAK,OAAO;EAAE,OAAO;EAAG,OAAO,EAAU,EAAE;EAAE,EAAE,EAEvE,UAA0B;EAC9B,IAAM,IAAiD,EAAE;AAIzD,SAHK,EAAK,KAAK,MAAM,KAAE,EAAK,OAAO,qBAC/B,CAAC,KAAU,CAAC,EAAK,YAAS,EAAK,UAAU,sBAC7C,EAAU,EAAK,EACR,OAAO,KAAK,EAAK,CAAC,WAAW;;AAsDtC,QAhBI,KAAU,EAAS,YAEnB,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD,EAAa,OAAM,uBAAwB,CAAA;EAChC,CAAA,GAIb,MAAW,EAAS,WAAW,CAAC,EAAS,QAEzC,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD;GAAW,OAAM;GAAuB,eAAe,KAAK,EAAS,SAAS;GAAI,CAAA;EACvE,CAAA,GAKf,kBAAC,GAAD;EACE,OAAO,IAAS,oBAAoB;EACpC,aAAa,IAAS,8BAA8B;EACpD,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eACE,EAAW,KAAU,IAAe,gBAAgB,MAAiB,eAAe;GAEtF,WAAU;aALZ,CAOE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,UAC1B;;YAGX,kBAAC,QAAD;GAAM,WAnEY,MAAuB;AAC3C,MAAE,gBAAgB,EACb,GAAU,KAEX,KAAU,IACZ,EAAiB,OACf;KACE,IAAI;KACJ,OAAO;MACL,MAAM,EAAK,KAAK,MAAM;MACtB,aAAa,EAAK,YAAY,MAAM,IAAI,KAAA;MACxC,QAAQ,EAAK;MACb,MAAM,EAAS,EAAK,KAAK;MACzB,WAAW,EAAM,EAAK,UAAU;MAChC,OAAO,EAAM,EAAK,MAAM;MACzB;KACF,EACD,EAAE,iBAAiB,EAAW,gBAAgB,IAAe,EAAE,CAChE,GAED,EAAiB,OACf;KACE,SAAS,EAAK;KACd,MAAM,EAAK,KAAK,MAAM;KACtB,aAAa,EAAK,YAAY,MAAM,IAAI,KAAA;KACxC,QAAQ,EAAK;KACb,MAAM,EAAS,EAAK,KAAK;KACzB,WAAW,EAAM,EAAK,UAAU;KAChC,OAAO,EAAM,EAAK,MAAM;KACzB,EACD,EAAE,YAAY,MAAM,EAAW,gBAAgB,EAAE,KAAK,EAAE,CACzD;;GAoC6B,WAAU;aACtC,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cAAvC;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;OAC1B,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA4C;OAAuB,CAAA,EACjF,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAsC,CAAA,CAC7E,EAAA,CAAA,CACF;;KAEL,CAAC,KACA,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,WAAW,EAAE;MACvC,SAAS;MACT,UAAA;MACA,aAAa,EAAO,YAAY,oBAAoB;MACpD,OAAO,EAAO;MACd,WAAU;MACV,CAAA;KAGJ,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,OAAO,EAAK;OACZ,WAAW,MAAM,EAAS,QAAQ,EAAE;OACpC,aAAY;OACZ,UAAA;OACA,OAAO,EAAO;OACd,CAAA,EACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,OAAO,EAAK;OACZ,WAAW,MAAM,EAAS,UAAU,EAAe;OACnD,SAAS;OACT,CAAA,CACE;;KAEN,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,eAAe,EAAE;MAC3C,aAAY;MACZ,MAAM;MACN,WAAU;MACV,CAAA;KAEF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,MAAK;OACL,OAAO,EAAK;OACZ,WAAW,MAAM,EAAS,aAAa,EAAE;OACzC,CAAA,EACF,kBAAC,GAAD;OACE,IAAG;OACH,OAAM;OACN,MAAK;OACL,OAAO,EAAK;OACZ,WAAW,MAAM,EAAS,SAAS,EAAE;OACrC,CAAA,CACE;;KAEN,kBAAC,GAAD;MACE,IAAG;MACH,OAAM;MACN,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,QAAQ,EAAE;MACpC,aAAY;MACZ,MAAK;MACL,WAAU;MACV,CAAA;KAED,EAAS,WACR,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAS,iBAAiB,QAAQ,EAAS,MAAM,UAAU;MAC1D,CAAA;KAGN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,EAAS;OACnB,WAAU;iBAHZ,CAKG,EAAS,YACR,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAE7B,IAAS,iBAAiB,oBACpB;;MACL,CAAA;KACI;;GACP,CAAA;EACI,CAAA"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
|
|
2
|
+
import { cn as t } from "../../utils/cn.js";
|
|
3
|
+
import { PageLayout as n } from "../../components/PageLayout.js";
|
|
4
|
+
import { StatusBadge as r } from "../../components/StatusBadge.js";
|
|
5
|
+
import { ErrorView as i, LoadingView as a } from "../../components/StateViews.js";
|
|
6
|
+
import { Thumbnail as o } from "../../components/Thumbnail.js";
|
|
7
|
+
import { useCollections as s } from "../../hooks/useCoolAndLovelyApi.js";
|
|
8
|
+
import "../../hooks/index.js";
|
|
9
|
+
import { formatDate as c, titleCase as l } from "../../utils/formatters.js";
|
|
10
|
+
import { TagList as u } from "../../components/TagList.js";
|
|
11
|
+
import { EmptyState as d } from "../../components/EmptyState.js";
|
|
12
|
+
import { useState as f } from "react";
|
|
13
|
+
import { Plus as p, Shapes as m } from "lucide-react";
|
|
14
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
15
|
+
//#region src/coolandlovely/pages/collections/CollectionsListPage.tsx
|
|
16
|
+
var _ = [
|
|
17
|
+
{
|
|
18
|
+
value: "ALL",
|
|
19
|
+
label: "All"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
value: "LIVE",
|
|
23
|
+
label: "Live"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
value: "SCHEDULED",
|
|
27
|
+
label: "Scheduled"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
value: "DRAFT",
|
|
31
|
+
label: "Draft"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
value: "ENDED",
|
|
35
|
+
label: "Ended"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
value: "ARCHIVED",
|
|
39
|
+
label: "Archived"
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
function v() {
|
|
43
|
+
let { navigateTo: v } = e(), [y, b] = f("ALL"), x = s({ status: y === "ALL" ? void 0 : y }), S = x.data ?? [];
|
|
44
|
+
return /* @__PURE__ */ g(n, {
|
|
45
|
+
title: "Collections",
|
|
46
|
+
description: "Seasonal drops and curated edits grouped by brand",
|
|
47
|
+
actions: /* @__PURE__ */ g("button", {
|
|
48
|
+
type: "button",
|
|
49
|
+
onClick: () => v("/collections/new"),
|
|
50
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90",
|
|
51
|
+
children: [/* @__PURE__ */ h(p, { className: "h-4 w-4" }), " New collection"]
|
|
52
|
+
}),
|
|
53
|
+
children: [/* @__PURE__ */ h("div", {
|
|
54
|
+
className: "mb-6 flex flex-wrap items-center gap-2",
|
|
55
|
+
children: _.map((e) => /* @__PURE__ */ h("button", {
|
|
56
|
+
type: "button",
|
|
57
|
+
onClick: () => b(e.value),
|
|
58
|
+
className: t("rounded-full px-3 py-1.5 text-sm font-medium transition-colors", y === e.value ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
59
|
+
children: e.label
|
|
60
|
+
}, e.value))
|
|
61
|
+
}), x.isLoading ? /* @__PURE__ */ h(a, { label: "Loading collections…" }) : x.isError ? /* @__PURE__ */ h(i, { onRetry: () => void x.refetch() }) : S.length === 0 ? /* @__PURE__ */ h(d, {
|
|
62
|
+
illustration: "empty-data",
|
|
63
|
+
icon: /* @__PURE__ */ h(m, { className: "h-10 w-10" }),
|
|
64
|
+
title: "No collections yet",
|
|
65
|
+
description: "Group pieces into a seasonal drop to start a collection.",
|
|
66
|
+
action: /* @__PURE__ */ g("button", {
|
|
67
|
+
type: "button",
|
|
68
|
+
onClick: () => v("/collections/new"),
|
|
69
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text",
|
|
70
|
+
children: [/* @__PURE__ */ h(p, { className: "h-4 w-4" }), " New collection"]
|
|
71
|
+
})
|
|
72
|
+
}) : /* @__PURE__ */ h("div", {
|
|
73
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
74
|
+
children: S.map((e) => /* @__PURE__ */ g("button", {
|
|
75
|
+
type: "button",
|
|
76
|
+
onClick: () => v(`/collections/${e.id}`),
|
|
77
|
+
className: "overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md",
|
|
78
|
+
children: [/* @__PURE__ */ h(o, {
|
|
79
|
+
icon: /* @__PURE__ */ h(m, { className: "h-9 w-9" }),
|
|
80
|
+
seed: e.id,
|
|
81
|
+
className: "h-28 w-full"
|
|
82
|
+
}), /* @__PURE__ */ g("div", {
|
|
83
|
+
className: "p-4",
|
|
84
|
+
children: [
|
|
85
|
+
/* @__PURE__ */ g("div", {
|
|
86
|
+
className: "flex items-start justify-between gap-2",
|
|
87
|
+
children: [/* @__PURE__ */ g("div", {
|
|
88
|
+
className: "min-w-0",
|
|
89
|
+
children: [/* @__PURE__ */ h("h3", {
|
|
90
|
+
className: "truncate text-base font-semibold text-text-primary",
|
|
91
|
+
children: e.name
|
|
92
|
+
}), e.brand?.name && /* @__PURE__ */ h("p", {
|
|
93
|
+
className: "truncate text-xs text-text-secondary",
|
|
94
|
+
children: e.brand.name
|
|
95
|
+
})]
|
|
96
|
+
}), /* @__PURE__ */ h(r, { status: e.status })]
|
|
97
|
+
}),
|
|
98
|
+
/* @__PURE__ */ g("div", {
|
|
99
|
+
className: "mt-2 flex items-center gap-2 text-xs text-text-secondary",
|
|
100
|
+
children: [/* @__PURE__ */ h("span", {
|
|
101
|
+
className: "rounded-full bg-bg-sunken px-2 py-0.5",
|
|
102
|
+
children: l(e.season)
|
|
103
|
+
}), e.publishAt && /* @__PURE__ */ g("span", { children: ["Publishes ", c(e.publishAt, {
|
|
104
|
+
hour: void 0,
|
|
105
|
+
minute: void 0
|
|
106
|
+
})] })]
|
|
107
|
+
}),
|
|
108
|
+
e.tags.length > 0 && /* @__PURE__ */ h(u, {
|
|
109
|
+
tags: e.tags,
|
|
110
|
+
max: 4,
|
|
111
|
+
className: "mt-3"
|
|
112
|
+
})
|
|
113
|
+
]
|
|
114
|
+
})]
|
|
115
|
+
}, e.id))
|
|
116
|
+
})]
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { v as CollectionsListPage };
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=CollectionsListPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollectionsListPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/collections/CollectionsListPage.tsx"],"sourcesContent":["/**\n * Collections List Page\n * @module coolandlovely/pages/collections\n *\n * Lists seasonal collections (`cooListCollections`) with status filter.\n */\n\nimport { useState } from 'react';\nimport { Plus, Shapes } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useCollections } from '../../hooks';\nimport { titleCase, formatDate } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooCollectionStatus } from '../../types';\n\nconst STATUS_OPTIONS: Array<{ value: CooCollectionStatus | 'ALL'; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'LIVE', label: 'Live' },\n { value: 'SCHEDULED', label: 'Scheduled' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'ENDED', label: 'Ended' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nexport function CollectionsListPage() {\n const { navigateTo } = useCoolAndLovely();\n const [status, setStatus] = useState<CooCollectionStatus | 'ALL'>('ALL');\n\n const query = useCollections({ status: status === 'ALL' ? undefined : status });\n const collections = query.data ?? [];\n\n return (\n <PageLayout\n title=\"Collections\"\n description=\"Seasonal drops and curated edits grouped by brand\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/collections/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" /> New collection\n </button>\n }\n >\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">\n {STATUS_OPTIONS.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => setStatus(opt.value)}\n className={cn(\n 'rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n status === opt.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {opt.label}\n </button>\n ))}\n </div>\n\n {query.isLoading ? (\n <LoadingView label=\"Loading collections…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : collections.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Shapes className=\"h-10 w-10\" />}\n title=\"No collections yet\"\n description=\"Group pieces into a seasonal drop to start a collection.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/collections/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n <Plus className=\"h-4 w-4\" /> New collection\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {collections.map((c) => (\n <button\n key={c.id}\n type=\"button\"\n onClick={() => navigateTo(`/collections/${c.id}`)}\n className=\"overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail icon={<Shapes className=\"h-9 w-9\" />} seed={c.id} className=\"h-28 w-full\" />\n <div className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">{c.name}</h3>\n {c.brand?.name && (\n <p className=\"truncate text-xs text-text-secondary\">{c.brand.name}</p>\n )}\n </div>\n <StatusBadge status={c.status} />\n </div>\n <div className=\"mt-2 flex items-center gap-2 text-xs text-text-secondary\">\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">{titleCase(c.season)}</span>\n {c.publishAt && <span>Publishes {formatDate(c.publishAt, { hour: undefined, minute: undefined })}</span>}\n </div>\n {c.tags.length > 0 && <TagList tags={c.tags} max={4} className=\"mt-3\" />}\n </div>\n </button>\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAqBA,IAAM,IAA+E;CACnF;EAAE,OAAO;EAAO,OAAO;EAAO;CAC9B;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAS,OAAO;EAAS;CAClC;EAAE,OAAO;EAAS,OAAO;EAAS;CAClC;EAAE,OAAO;EAAY,OAAO;EAAY;CACzC;AAED,SAAgB,IAAsB;CACpC,IAAM,EAAE,kBAAe,GAAkB,EACnC,CAAC,GAAQ,KAAa,EAAsC,MAAM,EAElE,IAAQ,EAAe,EAAE,QAAQ,MAAW,QAAQ,KAAA,IAAY,GAAQ,CAAC,EACzE,IAAc,EAAM,QAAQ,EAAE;AAEpC,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,mBAAmB;GAC7C,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,kBACrB;;YAVb,CAaE,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAe,KAAK,MACnB,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe,EAAU,EAAI,MAAM;IACnC,WAAW,EACT,kEACA,MAAW,EAAI,QACX,kDACA,2DACL;cAEA,EAAI;IACE,EAXF,EAAI,MAWF,CACT;GACE,CAAA,EAEL,EAAM,YACL,kBAAC,GAAD,EAAa,OAAM,wBAAyB,CAAA,GAC1C,EAAM,UACR,kBAAC,GAAD,EAAW,eAAe,KAAK,EAAM,SAAS,EAAI,CAAA,GAChD,EAAY,WAAW,IACzB,kBAAC,GAAD;GACE,cAAa;GACb,MAAM,kBAAC,GAAD,EAAQ,WAAU,aAAc,CAAA;GACtC,OAAM;GACN,aAAY;GACZ,QACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,mBAAmB;IAC7C,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,kBACrB;;GAEX,CAAA,GAEF,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAY,KAAK,MAChB,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe,EAAW,gBAAgB,EAAE,KAAK;IACjD,WAAU;cAJZ,CAME,kBAAC,GAAD;KAAW,MAAM,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;KAAE,MAAM,EAAE;KAAI,WAAU;KAAgB,CAAA,EACvF,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAAsD,EAAE;SAAU,CAAA,EAC/E,EAAE,OAAO,QACR,kBAAC,KAAD;SAAG,WAAU;mBAAwC,EAAE,MAAM;SAAS,CAAA,CAEpE;WACN,kBAAC,GAAD,EAAa,QAAQ,EAAE,QAAU,CAAA,CAC7B;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAyC,EAAU,EAAE,OAAO;QAAQ,CAAA,EACnF,EAAE,aAAa,kBAAC,QAAD,EAAA,UAAA,CAAM,cAAW,EAAW,EAAE,WAAW;QAAE,MAAM,KAAA;QAAW,QAAQ,KAAA;QAAW,CAAC,CAAQ,EAAA,CAAA,CACpG;;MACL,EAAE,KAAK,SAAS,KAAK,kBAAC,GAAD;OAAS,MAAM,EAAE;OAAM,KAAK;OAAG,WAAU;OAAS,CAAA;MACpE;OACC;MAtBF,EAAE,GAsBA,CACT;GACE,CAAA,CAEG"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
3
|
+
import { StatusBadge as n } from "../../components/StatusBadge.js";
|
|
4
|
+
import { ErrorView as r, LoadingView as i } from "../../components/StateViews.js";
|
|
5
|
+
import { Thumbnail as a } from "../../components/Thumbnail.js";
|
|
6
|
+
import { useArchiveStyleItem as o, useStyleItem as s } from "../../hooks/useCoolAndLovelyApi.js";
|
|
7
|
+
import "../../hooks/index.js";
|
|
8
|
+
import { formatDate as c, formatPrice as l, titleCase as u } from "../../utils/formatters.js";
|
|
9
|
+
import { TagList as d } from "../../components/TagList.js";
|
|
10
|
+
import { categoryIcon as f } from "../../utils/icons.js";
|
|
11
|
+
import { Archive as p, ArrowLeft as m, Loader2 as h, Pencil as g, Shapes as _, Store as v } from "lucide-react";
|
|
12
|
+
import { useParams as y } from "react-router-dom";
|
|
13
|
+
import { jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
14
|
+
import { GlassCard as S } from "@burdenoff/fe-libs/ui";
|
|
15
|
+
//#region src/coolandlovely/pages/items/StyleItemDetailPage.tsx
|
|
16
|
+
function C() {
|
|
17
|
+
let { itemId: C } = y(), { navigateTo: w } = e(), T = s(C), E = o();
|
|
18
|
+
if (T.isLoading) return /* @__PURE__ */ b(t, {
|
|
19
|
+
title: "Style item",
|
|
20
|
+
children: /* @__PURE__ */ b(i, { label: "Loading item…" })
|
|
21
|
+
});
|
|
22
|
+
if (T.isError || !T.data) return /* @__PURE__ */ b(t, {
|
|
23
|
+
title: "Style item",
|
|
24
|
+
children: /* @__PURE__ */ b(r, {
|
|
25
|
+
title: "Item not found",
|
|
26
|
+
onRetry: () => void T.refetch()
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
let D = T.data, O = D.status === "RETIRED";
|
|
30
|
+
return /* @__PURE__ */ b(t, {
|
|
31
|
+
title: D.name,
|
|
32
|
+
description: D.brand?.name ?? void 0,
|
|
33
|
+
actions: /* @__PURE__ */ x("div", {
|
|
34
|
+
className: "flex items-center gap-2",
|
|
35
|
+
children: [
|
|
36
|
+
/* @__PURE__ */ x("button", {
|
|
37
|
+
type: "button",
|
|
38
|
+
onClick: () => w("/items"),
|
|
39
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
|
|
40
|
+
children: [/* @__PURE__ */ b(m, { className: "h-4 w-4" }), " Back"]
|
|
41
|
+
}),
|
|
42
|
+
/* @__PURE__ */ x("button", {
|
|
43
|
+
type: "button",
|
|
44
|
+
onClick: () => w(`/items/${D.id}/edit`),
|
|
45
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90",
|
|
46
|
+
children: [/* @__PURE__ */ b(g, { className: "h-4 w-4" }), " Edit"]
|
|
47
|
+
}),
|
|
48
|
+
!O && /* @__PURE__ */ x("button", {
|
|
49
|
+
type: "button",
|
|
50
|
+
disabled: E.isPending,
|
|
51
|
+
onClick: () => E.mutate(D.id, { onSuccess: () => w("/items") }),
|
|
52
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50",
|
|
53
|
+
children: [E.isPending ? /* @__PURE__ */ b(h, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ b(p, { className: "h-4 w-4" }), "Archive"]
|
|
54
|
+
})
|
|
55
|
+
]
|
|
56
|
+
}),
|
|
57
|
+
children: /* @__PURE__ */ x("div", {
|
|
58
|
+
className: "grid grid-cols-1 gap-6 lg:grid-cols-3",
|
|
59
|
+
children: [/* @__PURE__ */ b("div", {
|
|
60
|
+
className: "lg:col-span-2",
|
|
61
|
+
children: /* @__PURE__ */ x(S, {
|
|
62
|
+
treatment: "glass",
|
|
63
|
+
className: "overflow-hidden p-0",
|
|
64
|
+
children: [/* @__PURE__ */ b(a, {
|
|
65
|
+
icon: f(D.category, 40),
|
|
66
|
+
seed: D.id,
|
|
67
|
+
className: "h-52 w-full"
|
|
68
|
+
}), /* @__PURE__ */ x("div", {
|
|
69
|
+
className: "p-6",
|
|
70
|
+
children: [
|
|
71
|
+
/* @__PURE__ */ x("div", {
|
|
72
|
+
className: "flex items-center gap-2",
|
|
73
|
+
children: [/* @__PURE__ */ b("h2", {
|
|
74
|
+
className: "text-xl font-semibold text-text-primary",
|
|
75
|
+
children: D.name
|
|
76
|
+
}), /* @__PURE__ */ b(n, {
|
|
77
|
+
status: D.status,
|
|
78
|
+
className: "ml-auto"
|
|
79
|
+
})]
|
|
80
|
+
}),
|
|
81
|
+
/* @__PURE__ */ b("p", {
|
|
82
|
+
className: "mt-1 text-2xl font-bold text-text-primary tabular-nums",
|
|
83
|
+
children: l(D.price, D.currency)
|
|
84
|
+
}),
|
|
85
|
+
D.description && /* @__PURE__ */ b("p", {
|
|
86
|
+
className: "mt-3 text-sm text-text-secondary",
|
|
87
|
+
children: D.description
|
|
88
|
+
}),
|
|
89
|
+
/* @__PURE__ */ x("div", {
|
|
90
|
+
className: "mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
91
|
+
children: [
|
|
92
|
+
D.colours.length > 0 && /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
93
|
+
className: "mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary",
|
|
94
|
+
children: "Colours"
|
|
95
|
+
}), /* @__PURE__ */ b(d, {
|
|
96
|
+
tags: D.colours,
|
|
97
|
+
max: 10
|
|
98
|
+
})] }),
|
|
99
|
+
D.sizes.length > 0 && /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
100
|
+
className: "mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary",
|
|
101
|
+
children: "Sizes"
|
|
102
|
+
}), /* @__PURE__ */ b(d, {
|
|
103
|
+
tags: D.sizes,
|
|
104
|
+
max: 10
|
|
105
|
+
})] }),
|
|
106
|
+
D.materials.length > 0 && /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
107
|
+
className: "mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary",
|
|
108
|
+
children: "Materials"
|
|
109
|
+
}), /* @__PURE__ */ b(d, {
|
|
110
|
+
tags: D.materials,
|
|
111
|
+
max: 10
|
|
112
|
+
})] }),
|
|
113
|
+
D.tags.length > 0 && /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
|
|
114
|
+
className: "mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary",
|
|
115
|
+
children: "Tags"
|
|
116
|
+
}), /* @__PURE__ */ b(d, {
|
|
117
|
+
tags: D.tags,
|
|
118
|
+
max: 10,
|
|
119
|
+
tone: "accent"
|
|
120
|
+
})] })
|
|
121
|
+
]
|
|
122
|
+
})
|
|
123
|
+
]
|
|
124
|
+
})]
|
|
125
|
+
})
|
|
126
|
+
}), /* @__PURE__ */ x("div", {
|
|
127
|
+
className: "space-y-4",
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ x(S, {
|
|
130
|
+
treatment: "plain",
|
|
131
|
+
className: "p-5",
|
|
132
|
+
children: [/* @__PURE__ */ b("h3", {
|
|
133
|
+
className: "mb-3 text-sm font-semibold text-text-primary",
|
|
134
|
+
children: "Details"
|
|
135
|
+
}), /* @__PURE__ */ x("dl", {
|
|
136
|
+
className: "space-y-2 text-sm",
|
|
137
|
+
children: [
|
|
138
|
+
/* @__PURE__ */ x("div", {
|
|
139
|
+
className: "flex justify-between gap-2",
|
|
140
|
+
children: [/* @__PURE__ */ b("dt", {
|
|
141
|
+
className: "text-text-secondary",
|
|
142
|
+
children: "Category"
|
|
143
|
+
}), /* @__PURE__ */ b("dd", {
|
|
144
|
+
className: "text-text-primary",
|
|
145
|
+
children: u(D.category)
|
|
146
|
+
})]
|
|
147
|
+
}),
|
|
148
|
+
/* @__PURE__ */ x("div", {
|
|
149
|
+
className: "flex justify-between gap-2",
|
|
150
|
+
children: [/* @__PURE__ */ b("dt", {
|
|
151
|
+
className: "text-text-secondary",
|
|
152
|
+
children: "SKU"
|
|
153
|
+
}), /* @__PURE__ */ b("dd", {
|
|
154
|
+
className: "text-text-primary",
|
|
155
|
+
children: D.sku ?? "—"
|
|
156
|
+
})]
|
|
157
|
+
}),
|
|
158
|
+
/* @__PURE__ */ x("div", {
|
|
159
|
+
className: "flex justify-between gap-2",
|
|
160
|
+
children: [/* @__PURE__ */ b("dt", {
|
|
161
|
+
className: "text-text-secondary",
|
|
162
|
+
children: "Currency"
|
|
163
|
+
}), /* @__PURE__ */ b("dd", {
|
|
164
|
+
className: "text-text-primary",
|
|
165
|
+
children: D.currency
|
|
166
|
+
})]
|
|
167
|
+
}),
|
|
168
|
+
/* @__PURE__ */ x("div", {
|
|
169
|
+
className: "flex justify-between gap-2",
|
|
170
|
+
children: [/* @__PURE__ */ b("dt", {
|
|
171
|
+
className: "text-text-secondary",
|
|
172
|
+
children: "Created"
|
|
173
|
+
}), /* @__PURE__ */ b("dd", {
|
|
174
|
+
className: "text-text-primary",
|
|
175
|
+
children: c(D.createdAt, {
|
|
176
|
+
hour: void 0,
|
|
177
|
+
minute: void 0
|
|
178
|
+
})
|
|
179
|
+
})]
|
|
180
|
+
})
|
|
181
|
+
]
|
|
182
|
+
})]
|
|
183
|
+
}),
|
|
184
|
+
D.brand && /* @__PURE__ */ x("button", {
|
|
185
|
+
type: "button",
|
|
186
|
+
onClick: () => w(`/brands/${D.brand?.id}`),
|
|
187
|
+
className: "flex w-full items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left transition-colors hover:bg-bg-sunken/50",
|
|
188
|
+
children: [/* @__PURE__ */ b(a, {
|
|
189
|
+
icon: /* @__PURE__ */ b(v, { className: "h-5 w-5" }),
|
|
190
|
+
seed: D.brand.id,
|
|
191
|
+
className: "h-10 w-10 shrink-0"
|
|
192
|
+
}), /* @__PURE__ */ x("div", {
|
|
193
|
+
className: "min-w-0",
|
|
194
|
+
children: [/* @__PURE__ */ b("p", {
|
|
195
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
196
|
+
children: D.brand.name
|
|
197
|
+
}), /* @__PURE__ */ b("p", {
|
|
198
|
+
className: "truncate text-xs text-text-secondary",
|
|
199
|
+
children: "View brand"
|
|
200
|
+
})]
|
|
201
|
+
})]
|
|
202
|
+
}),
|
|
203
|
+
D.collection && /* @__PURE__ */ x("button", {
|
|
204
|
+
type: "button",
|
|
205
|
+
onClick: () => w(`/collections/${D.collection?.id}`),
|
|
206
|
+
className: "flex w-full items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left transition-colors hover:bg-bg-sunken/50",
|
|
207
|
+
children: [/* @__PURE__ */ b(a, {
|
|
208
|
+
icon: /* @__PURE__ */ b(_, { className: "h-5 w-5" }),
|
|
209
|
+
seed: D.collection.id,
|
|
210
|
+
className: "h-10 w-10 shrink-0"
|
|
211
|
+
}), /* @__PURE__ */ x("div", {
|
|
212
|
+
className: "min-w-0",
|
|
213
|
+
children: [/* @__PURE__ */ b("p", {
|
|
214
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
215
|
+
children: D.collection.name
|
|
216
|
+
}), /* @__PURE__ */ b("p", {
|
|
217
|
+
className: "truncate text-xs text-text-secondary",
|
|
218
|
+
children: "View collection"
|
|
219
|
+
})]
|
|
220
|
+
})]
|
|
221
|
+
})
|
|
222
|
+
]
|
|
223
|
+
})]
|
|
224
|
+
})
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
export { C as StyleItemDetailPage };
|
|
229
|
+
|
|
230
|
+
//# sourceMappingURL=StyleItemDetailPage.js.map
|