@burdenoff/microfe-coolandlovely 2026.706.1 → 2026.706.3

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.
Files changed (89) hide show
  1. package/dist/coolandlovely/CoolAndLovelyRoot.js +62 -46
  2. package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
  3. package/dist/coolandlovely/CoolAndLovelyRoutes.js +14 -6
  4. package/dist/coolandlovely/CoolAndLovelyRoutes.js.map +1 -1
  5. package/dist/coolandlovely/components/ActorName.js +29 -0
  6. package/dist/coolandlovely/components/ActorName.js.map +1 -0
  7. package/dist/coolandlovely/components/AddToLookbookButton.js +81 -0
  8. package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -0
  9. package/dist/coolandlovely/components/BrandCover.js +39 -0
  10. package/dist/coolandlovely/components/BrandCover.js.map +1 -0
  11. package/dist/coolandlovely/components/BrandCreatorAvatars.js +57 -0
  12. package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -0
  13. package/dist/coolandlovely/components/ColourSwatchRow.js +86 -0
  14. package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -0
  15. package/dist/coolandlovely/components/Comments.js +109 -0
  16. package/dist/coolandlovely/components/Comments.js.map +1 -0
  17. package/dist/coolandlovely/components/Field.js +22 -2
  18. package/dist/coolandlovely/components/Field.js.map +1 -1
  19. package/dist/coolandlovely/components/FileImage.js +17 -0
  20. package/dist/coolandlovely/components/FileImage.js.map +1 -0
  21. package/dist/coolandlovely/components/FollowButton.js +24 -0
  22. package/dist/coolandlovely/components/FollowButton.js.map +1 -0
  23. package/dist/coolandlovely/components/ImageUploadField.js +153 -0
  24. package/dist/coolandlovely/components/ImageUploadField.js.map +1 -0
  25. package/dist/coolandlovely/components/LookCard.js +77 -0
  26. package/dist/coolandlovely/components/LookCard.js.map +1 -0
  27. package/dist/coolandlovely/components/MetricsCard.js +1 -1
  28. package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
  29. package/dist/coolandlovely/components/ShopItemCard.js +45 -0
  30. package/dist/coolandlovely/components/ShopItemCard.js.map +1 -0
  31. package/dist/coolandlovely/components/StepTracker.js +35 -0
  32. package/dist/coolandlovely/components/StepTracker.js.map +1 -0
  33. package/dist/coolandlovely/components/Thumbnail.js +25 -14
  34. package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
  35. package/dist/coolandlovely/components/VisibilityChip.js +32 -0
  36. package/dist/coolandlovely/components/VisibilityChip.js.map +1 -0
  37. package/dist/coolandlovely/hooks/index.js +2 -0
  38. package/dist/coolandlovely/hooks/useComments.js +68 -0
  39. package/dist/coolandlovely/hooks/useComments.js.map +1 -0
  40. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +155 -60
  41. package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
  42. package/dist/coolandlovely/hooks/useFiles.js +111 -0
  43. package/dist/coolandlovely/hooks/useFiles.js.map +1 -0
  44. package/dist/coolandlovely/pages/HomePage.js +130 -133
  45. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  46. package/dist/coolandlovely/pages/brands/BrandDetailPage.js +261 -228
  47. package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
  48. package/dist/coolandlovely/pages/brands/BrandFormPage.js +146 -104
  49. package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
  50. package/dist/coolandlovely/pages/brands/BrandsListPage.js +13 -11
  51. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  52. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +117 -95
  53. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
  54. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
  55. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +50 -43
  56. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
  57. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +134 -107
  58. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
  59. package/dist/coolandlovely/pages/collections/CollectionFormPage.js +117 -102
  60. package/dist/coolandlovely/pages/collections/CollectionFormPage.js.map +1 -1
  61. package/dist/coolandlovely/pages/collections/CollectionsListPage.js +59 -54
  62. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  63. package/dist/coolandlovely/pages/discover/DiscoverPage.js +223 -0
  64. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -0
  65. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +176 -144
  66. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
  67. package/dist/coolandlovely/pages/items/StyleItemFormPage.js +144 -130
  68. package/dist/coolandlovely/pages/items/StyleItemFormPage.js.map +1 -1
  69. package/dist/coolandlovely/pages/items/StyleItemsListPage.js +73 -62
  70. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
  71. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +215 -102
  72. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
  73. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +115 -86
  74. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  75. package/dist/coolandlovely/pages/looks/LookDetailPage.js +217 -155
  76. package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
  77. package/dist/coolandlovely/pages/looks/LookFormPage.js +147 -96
  78. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
  79. package/dist/coolandlovely/pages/looks/LooksListPage.js +60 -95
  80. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
  81. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +376 -146
  82. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  83. package/dist/coolandlovely/pages/trends/TrendsPage.js +249 -0
  84. package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -0
  85. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +136 -88
  86. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
  87. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +75 -74
  88. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  89. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"LookbookDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/lookbooks/LookbookDetailPage.tsx"],"sourcesContent":["/**\n * Lookbook Detail Page\n * @module coolandlovely/pages/lookbooks\n *\n * Single lookbook (`cooLookbook`) showing its clipped entries, with per-entry\n * removal (`cooRemoveLookbookEntry`).\n */\n\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, BookHeart, Sparkles, Tag, Trash2, Loader2 } 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 { TagList } from '../../components/TagList';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useLookbook, useRemoveLookbookEntry } from '../../hooks';\n\nexport function LookbookDetailPage() {\n const { lookbookId } = useParams<{ lookbookId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useLookbook(lookbookId);\n const removeEntry = useRemoveLookbookEntry();\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Lookbook\">\n <LoadingView label=\"Loading lookbook…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Lookbook\">\n <ErrorView title=\"Lookbook not found\" onRetry={() => void query.refetch()} />\n </PageLayout>\n );\n }\n\n const lookbook = query.data;\n const entries = [...(lookbook.entries ?? [])].sort((a, b) => a.position - b.position);\n\n return (\n <PageLayout\n title={lookbook.name}\n description={lookbook.description ?? undefined}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/lookbooks')}\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 }\n >\n <div className=\"mx-auto max-w-3xl\">\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-12 w-12 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary\">\n <BookHeart className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"truncate text-xl font-semibold text-text-primary\">{lookbook.name}</h2>\n <StatusBadge status={lookbook.visibility} className=\"mt-1\" />\n </div>\n </div>\n {lookbook.tags.length > 0 && <TagList tags={lookbook.tags} className=\"mt-4\" />}\n </GlassCard>\n\n <section className=\"mt-6\">\n <h3 className=\"mb-3 text-lg font-semibold text-text-primary\">\n Entries ({entries.length})\n </h3>\n {entries.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n This lookbook has no entries yet. Clip a catalogue piece or save a look to add one.\n </p>\n ) : (\n <div className=\"divide-y divide-border-seam rounded-lg border border-border-seam bg-bg-surface\">\n {entries.map((entry) => {\n const isLook = Boolean(entry.lookId);\n return (\n <div key={entry.id} className=\"flex items-center gap-4 px-4 py-3\">\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-bg-sunken text-text-secondary\">\n {isLook ? <Sparkles className=\"h-5 w-5\" /> : <Tag className=\"h-5 w-5\" />}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium text-text-primary\">\n {isLook ? 'Saved look' : 'Clipped piece'}\n </p>\n {entry.note && (\n <p className=\"truncate text-xs text-text-secondary\">{entry.note}</p>\n )}\n </div>\n {isLook && entry.lookId && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/looks/${entry.lookId}`)}\n className=\"shrink-0 text-xs font-medium text-primary hover:underline\"\n >\n View\n </button>\n )}\n {!isLook && entry.styleItemId && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/items/${entry.styleItemId}`)}\n className=\"shrink-0 text-xs font-medium text-primary hover:underline\"\n >\n View\n </button>\n )}\n <button\n type=\"button\"\n disabled={removeEntry.isPending}\n onClick={() => removeEntry.mutate(entry.id)}\n aria-label=\"Remove entry\"\n className=\"inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-lg text-text-secondary transition-colors hover:bg-status-error-bg-subtle hover:text-status-error-text disabled:opacity-50\"\n >\n {removeEntry.isPending && removeEntry.variables === entry.id ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Trash2 className=\"h-3.5 w-3.5\" />\n )}\n </button>\n </div>\n );\n })}\n </div>\n )}\n </section>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAkBA,SAAgB,IAAqB;CACnC,IAAM,EAAE,kBAAe,GAAmC,EACpD,EAAE,kBAAe,GAAkB,EACnC,IAAQ,EAAY,EAAW,EAC/B,IAAc,GAAwB;AAE5C,KAAI,EAAM,UACR,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD,EAAa,OAAM,qBAAsB,CAAA;EAC9B,CAAA;AAIjB,KAAI,EAAM,WAAW,CAAC,EAAM,KAC1B,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD;GAAW,OAAM;GAAqB,eAAe,KAAK,EAAM,SAAS;GAAI,CAAA;EAClE,CAAA;CAIjB,IAAM,IAAW,EAAM,MACjB,IAAU,CAAC,GAAI,EAAS,WAAW,EAAE,CAAE,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS;AAErF,QACE,kBAAC,GAAD;EACE,OAAO,EAAS;EAChB,aAAa,EAAS,eAAe,KAAA;EACrC,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,aAAa;GACvC,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,QAC1B;;YAGX,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cAAvC,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;MAC7B,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAoD,EAAS;OAAU,CAAA,EACrF,kBAAC,GAAD;OAAa,QAAQ,EAAS;OAAY,WAAU;OAAS,CAAA,CACzD;QACF;QACL,EAAS,KAAK,SAAS,KAAK,kBAAC,GAAD;KAAS,MAAM,EAAS;KAAM,WAAU;KAAS,CAAA,CACpE;OAEZ,kBAAC,WAAD;IAAS,WAAU;cAAnB,CACE,kBAAC,MAAD;KAAI,WAAU;eAAd;MAA6D;MACjD,EAAQ;MAAO;MACtB;QACJ,EAAQ,WAAW,IAClB,kBAAC,KAAD;KAAG,WAAU;eAAqF;KAE9F,CAAA,GAEJ,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAQ,KAAK,MAAU;MACtB,IAAM,IAAS,EAAQ,EAAM;AAC7B,aACE,kBAAC,OAAD;OAAoB,WAAU;iBAA9B;QACE,kBAAC,OAAD;SAAK,WAAU;mBACH,EAAT,IAAU,IAAmC,GAApC,EAAU,WAAU,WAAY,CAA8B;SACpE,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBACV,IAAS,eAAe;UACvB,CAAA,EACH,EAAM,QACL,kBAAC,KAAD;UAAG,WAAU;oBAAwC,EAAM;UAAS,CAAA,CAElE;;QACL,KAAU,EAAM,UACf,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAW,UAAU,EAAM,SAAS;SACnD,WAAU;mBACX;SAEQ,CAAA;QAEV,CAAC,KAAU,EAAM,eAChB,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAW,UAAU,EAAM,cAAc;SACxD,WAAU;mBACX;SAEQ,CAAA;QAEX,kBAAC,UAAD;SACE,MAAK;SACL,UAAU,EAAY;SACtB,eAAe,EAAY,OAAO,EAAM,GAAG;SAC3C,cAAW;SACX,WAAU;mBAET,EAAY,aAAa,EAAY,cAAc,EAAM,KACxD,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA,GAEhD,kBAAC,GAAD,EAAQ,WAAU,eAAgB,CAAA;SAE7B,CAAA;QACL;SA3CI,EAAM,GA2CV;OAER;KACE,CAAA,CAEA;MACN;;EACK,CAAA"}
1
+ {"version":3,"file":"LookbookDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/lookbooks/LookbookDetailPage.tsx"],"sourcesContent":["/**\n * Lookbook Detail Page\n * @module coolandlovely/pages/lookbooks\n *\n * Single lookbook (`cooLookbook`) rendered as a Pinterest-style masonry board.\n * Each entry's `lookId` / `styleItemId` is resolved client-side against the\n * already-fetched looks/items lists to a real title + image, with an optional\n * note badge and a hover X-remove (`cooRemoveLookbookEntry`). New entries are\n * added inline (`cooAddLookbookEntry`).\n */\n\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, BookHeart, Sparkles, Tag, Loader2, Plus, X, Clock } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { VisibilityChip } from '../../components/VisibilityChip';\nimport { SelectField, TextField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport {\n useLookbook,\n useRemoveLookbookEntry,\n useAddLookbookEntry,\n useLooks,\n useStyleItems,\n} from '../../hooks';\nimport { formatRelativeTime } from '../../utils/formatters';\n\ntype EntryKind = 'look' | 'item';\n\n/** Alternating aspect ratios give the board its masonry rhythm. */\nconst TILE_ASPECTS = ['aspect-[3/4]', 'aspect-square', 'aspect-[4/5]'];\n\nexport function LookbookDetailPage() {\n const { lookbookId } = useParams<{ lookbookId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useLookbook(lookbookId);\n const removeEntry = useRemoveLookbookEntry();\n const addEntry = useAddLookbookEntry();\n\n const [adding, setAdding] = useState(false);\n const [entryKind, setEntryKind] = useState<EntryKind>('look');\n const [selectedId, setSelectedId] = useState('');\n const [note, setNote] = useState('');\n\n const looks = useLooks({ pagination: { limit: 100, offset: 0 } });\n const items = useStyleItems({ pagination: { limit: 100, offset: 0 } });\n\n const lookById = useMemo(\n () => new Map((looks.data?.items ?? []).map((l) => [l.id, l])),\n [looks.data]\n );\n const itemById = useMemo(\n () => new Map((items.data?.items ?? []).map((i) => [i.id, i])),\n [items.data]\n );\n const usableItems = useMemo(\n () => (items.data?.items ?? []).filter((i) => i.status !== 'RETIRED'),\n [items.data]\n );\n\n const lookOptions = useMemo(\n () => (looks.data?.items ?? []).map((l) => ({ value: l.id, label: l.title })),\n [looks.data]\n );\n const itemOptions = useMemo(\n () =>\n usableItems.map((i) => ({\n value: i.id,\n label: `${i.name}${i.brand?.name ? ` · ${i.brand.name}` : ''}`,\n })),\n [usableItems]\n );\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Lookbook\">\n <LoadingView label=\"Loading lookbook…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Lookbook\">\n <ErrorView title=\"Lookbook not found\" onRetry={() => void query.refetch()} />\n </PageLayout>\n );\n }\n\n const lookbook = query.data;\n const entries = [...(lookbook.entries ?? [])].sort((a, b) => a.position - b.position);\n\n const handleAddEntry = (e: React.FormEvent) => {\n e.preventDefault();\n if (!lookbookId || !selectedId) return;\n addEntry.mutate(\n {\n lookbookId,\n lookId: entryKind === 'look' ? selectedId : undefined,\n styleItemId: entryKind === 'item' ? selectedId : undefined,\n note: note.trim() || undefined,\n },\n {\n onSuccess: () => {\n setSelectedId('');\n setNote('');\n setAdding(false);\n },\n }\n );\n };\n\n return (\n <PageLayout\n title={lookbook.name}\n description={lookbook.description ?? undefined}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/lookbooks')}\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={() => {\n setAdding((v) => !v);\n setSelectedId('');\n }}\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 {adding ? <X className=\"h-4 w-4\" /> : <Plus className=\"h-4 w-4\" />}\n {adding ? 'Close' : 'Add entry'}\n </button>\n </div>\n }\n >\n <div className=\"mx-auto max-w-5xl\">\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary\">\n <BookHeart className=\"h-6 w-6\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"truncate text-xl font-semibold text-text-primary\">{lookbook.name}</h2>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-2\">\n <VisibilityChip visibility={lookbook.visibility} />\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" /> Updated {formatRelativeTime(lookbook.updatedAt)}\n </span>\n <span className=\"text-xs text-text-secondary tabular-nums\">\n · {entries.length} {entries.length === 1 ? 'item' : 'items'}\n </span>\n </div>\n </div>\n </div>\n {lookbook.tags.length > 0 && <TagList tags={lookbook.tags} className=\"mt-4\" />}\n </GlassCard>\n\n {adding && (\n <form onSubmit={handleAddEntry} className=\"mt-6\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"mb-4 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <Plus className=\"h-5 w-5\" /> Add to this lookbook\n </h3>\n <div className=\"mb-4 flex flex-wrap gap-2\">\n {(['look', 'item'] as EntryKind[]).map((k) => (\n <button\n key={k}\n type=\"button\"\n onClick={() => {\n setEntryKind(k);\n setSelectedId('');\n }}\n className={\n entryKind === k\n ? 'rounded-full bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text'\n : 'rounded-full bg-bg-sunken px-3 py-1.5 text-sm font-medium text-text-secondary hover:text-text-primary'\n }\n >\n {k === 'look' ? 'Save a look' : 'Clip a piece'}\n </button>\n ))}\n </div>\n <SelectField\n label={entryKind === 'look' ? 'Look' : 'Style item'}\n value={selectedId}\n onChange={setSelectedId}\n options={entryKind === 'look' ? lookOptions : itemOptions}\n placeholder={\n entryKind === 'look'\n ? looks.isLoading\n ? 'Loading looks…'\n : 'Select a look'\n : items.isLoading\n ? 'Loading items…'\n : 'Select a style item'\n }\n />\n <TextField\n label=\"Note (optional)\"\n value={note}\n onChange={setNote}\n placeholder=\"Why you saved it…\"\n className=\"mt-4\"\n />\n {addEntry.isError && (\n <p className=\"mt-3 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {addEntry.error instanceof Error ? addEntry.error.message : 'Could not add entry'}\n </p>\n )}\n <div className=\"mt-4 flex justify-end\">\n <button\n type=\"submit\"\n disabled={!selectedId || addEntry.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 {addEntry.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4\" />\n )}\n Add entry\n </button>\n </div>\n </GlassCard>\n </form>\n )}\n\n <section className=\"mt-6\">\n <h3 className=\"mb-3 text-lg font-semibold text-text-primary\">Board ({entries.length})</h3>\n {entries.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n This lookbook has no entries yet. Clip a catalogue piece or save a look to add one.\n </p>\n ) : (\n <div className=\"columns-2 gap-4 md:columns-3\">\n {entries.map((entry, idx) => {\n const isLook = Boolean(entry.lookId);\n const look = entry.lookId ? lookById.get(entry.lookId) : undefined;\n const item = entry.styleItemId ? itemById.get(entry.styleItemId) : undefined;\n const title = isLook\n ? (look?.title ?? 'Saved look')\n : (item?.name ?? 'Clipped piece');\n const imageFileId = isLook ? look?.image?.id : item?.image?.id;\n const removing = removeEntry.isPending && removeEntry.variables === entry.id;\n const onOpen = () => {\n if (isLook && entry.lookId) navigateTo(`/looks/${entry.lookId}`);\n else if (entry.styleItemId) navigateTo(`/items/${entry.styleItemId}`);\n };\n return (\n <div\n key={entry.id}\n className=\"group relative mb-4 break-inside-avoid overflow-hidden rounded-xl border border-border-seam bg-bg-surface shadow-sm transition-shadow hover:shadow-lg\"\n >\n <button\n type=\"button\"\n onClick={onOpen}\n aria-label={`Open ${title}`}\n className=\"block w-full text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <Thumbnail\n icon={\n isLook ? <Sparkles className=\"h-8 w-8\" /> : <Tag className=\"h-8 w-8\" />\n }\n seed={entry.id}\n fileId={imageFileId}\n alt={title}\n className={`w-full rounded-none ${TILE_ASPECTS[idx % TILE_ASPECTS.length]}`}\n overlay={\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 flex flex-col gap-1.5 bg-gradient-to-t from-bg-canvas/90 via-bg-canvas/40 to-transparent p-3 pt-10\">\n {entry.note && (\n <span className=\"inline-flex w-fit max-w-full items-center rounded-md bg-bg-canvas/80 px-2 py-0.5 text-[11px] font-medium text-text-primary backdrop-blur\">\n <span className=\"truncate\">{entry.note}</span>\n </span>\n )}\n <p className=\"truncate text-sm font-semibold text-text-primary\">\n {title}\n </p>\n </div>\n }\n />\n </button>\n <button\n type=\"button\"\n disabled={removeEntry.isPending}\n onClick={() => removeEntry.mutate(entry.id)}\n aria-label={`Remove ${title}`}\n className=\"absolute right-2 top-2 z-10 inline-flex h-7 w-7 items-center justify-center rounded-full bg-bg-surface/90 text-text-secondary opacity-100 shadow-sm backdrop-blur transition-opacity hover:text-status-error-text sm:opacity-0 sm:focus-visible:opacity-100 sm:group-hover:opacity-100 disabled:opacity-60\"\n >\n {removing ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <X className=\"h-3.5 w-3.5\" />\n )}\n </button>\n </div>\n );\n })}\n </div>\n )}\n </section>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1,156 +1,185 @@
1
1
  import { useCoolAndLovely as e } from "../../context/CoolAndLovelyContext.js";
2
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";
3
+ import { ErrorView as n, LoadingView as r } from "../../components/StateViews.js";
4
+ import { COO_FILE_FOLDERS as i } from "../../hooks/useFiles.js";
5
5
  import { Thumbnail as a } from "../../components/Thumbnail.js";
6
6
  import { useCreateLookbook as o, useLookbooks as s } from "../../hooks/useCoolAndLovelyApi.js";
7
- import "../../hooks/index.js";
8
7
  import { parseCsv as c, titleCase as l } from "../../utils/formatters.js";
8
+ import "../../hooks/index.js";
9
9
  import { TagList as u } from "../../components/TagList.js";
10
10
  import { SelectField as d, TextAreaField as f, TextField as p } from "../../components/Field.js";
11
- import { EmptyState as m } from "../../components/EmptyState.js";
12
- import { useState as h } from "react";
13
- import { BookHeart as g, Loader2 as _, Plus as v, X as y } from "lucide-react";
14
- import { jsx as b, jsxs as x } from "react/jsx-runtime";
15
- import { GlassCard as S } from "@burdenoff/fe-libs/ui";
11
+ import { ImageUploadField as m } from "../../components/ImageUploadField.js";
12
+ import { EmptyState as h } from "../../components/EmptyState.js";
13
+ import { VisibilityChip as g } from "../../components/VisibilityChip.js";
14
+ import { useState as _ } from "react";
15
+ import { BookHeart as v, Loader2 as y, Plus as b, X as x } from "lucide-react";
16
+ import { jsx as S, jsxs as C } from "react/jsx-runtime";
17
+ import { GlassCard as w } from "@burdenoff/fe-libs/ui";
16
18
  //#region src/coolandlovely/pages/lookbooks/LookbooksListPage.tsx
17
- var C = [
19
+ var T = [
18
20
  "PRIVATE",
19
21
  "FOLLOWERS",
20
22
  "PUBLIC"
21
23
  ];
22
- function w() {
23
- let { navigateTo: w } = e(), T = s(), E = o(), [D, O] = h(!1), [k, A] = h(""), [j, M] = h(""), [N, P] = h("PRIVATE"), [F, I] = h(""), [L, R] = h(), z = T.data ?? [], B = C.map((e) => ({
24
+ function E() {
25
+ let { navigateTo: E } = e(), D = s(), O = o(), [k, A] = _(!1), [j, M] = _(""), [N, P] = _(""), [F, I] = _("PRIVATE"), [L, R] = _(""), [z, B] = _(""), [V, H] = _(), U = D.data ?? [], W = T.map((e) => ({
24
26
  value: e,
25
27
  label: l(e)
26
28
  }));
27
- return /* @__PURE__ */ x(t, {
29
+ return /* @__PURE__ */ C(t, {
28
30
  title: "Lookbooks",
29
31
  description: "Themed boards of saved catalogue pieces and looks",
30
- actions: /* @__PURE__ */ x("button", {
32
+ actions: /* @__PURE__ */ C("button", {
31
33
  type: "button",
32
- onClick: () => O((e) => !e),
34
+ onClick: () => A((e) => !e),
33
35
  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",
34
- children: [b(D ? y : v, { className: "h-4 w-4" }), D ? "Close" : "New lookbook"]
36
+ children: [S(k ? x : b, { className: "h-4 w-4" }), k ? "Close" : "New lookbook"]
35
37
  }),
36
- children: [D && /* @__PURE__ */ b("form", {
38
+ children: [k && /* @__PURE__ */ S("form", {
37
39
  onSubmit: (e) => {
38
- if (e.preventDefault(), !k.trim()) {
39
- R("Name is required");
40
+ if (e.preventDefault(), !j.trim()) {
41
+ H("Name is required");
40
42
  return;
41
43
  }
42
- R(void 0), E.mutate({
43
- name: k.trim(),
44
- description: j.trim() || void 0,
45
- visibility: N,
46
- tags: c(F)
44
+ H(void 0), O.mutate({
45
+ name: j.trim(),
46
+ description: N.trim() || void 0,
47
+ visibility: F,
48
+ tags: c(L),
49
+ coverFileId: z || void 0
47
50
  }, { onSuccess: (e) => {
48
- O(!1), A(""), M(""), I(""), P("PRIVATE"), w(`/lookbooks/${e.id}`);
51
+ A(!1), M(""), P(""), R(""), I("PRIVATE"), B(""), E(`/lookbooks/${e.id}`);
49
52
  } });
50
53
  },
51
54
  className: "mb-6",
52
- children: /* @__PURE__ */ x(S, {
55
+ children: /* @__PURE__ */ C(w, {
53
56
  treatment: "plain",
54
57
  className: "p-5",
55
58
  children: [
56
- /* @__PURE__ */ x("h2", {
59
+ /* @__PURE__ */ C("h2", {
57
60
  className: "mb-4 flex items-center gap-2 text-base font-semibold text-text-primary",
58
- children: [/* @__PURE__ */ b(g, { className: "h-5 w-5" }), " New lookbook"]
61
+ children: [/* @__PURE__ */ S(v, { className: "h-5 w-5" }), " New lookbook"]
59
62
  }),
60
- /* @__PURE__ */ x("div", {
63
+ /* @__PURE__ */ C("div", {
61
64
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
62
- children: [/* @__PURE__ */ b(p, {
65
+ children: [/* @__PURE__ */ S(p, {
63
66
  label: "Name",
64
- value: k,
65
- onChange: A,
67
+ value: j,
68
+ onChange: M,
66
69
  placeholder: "Summer wedding guest",
67
70
  required: !0,
68
- error: L
69
- }), /* @__PURE__ */ b(d, {
71
+ error: V
72
+ }), /* @__PURE__ */ S(d, {
70
73
  label: "Visibility",
71
- value: N,
72
- onChange: (e) => P(e),
73
- options: B
74
+ value: F,
75
+ onChange: (e) => I(e),
76
+ options: W
74
77
  })]
75
78
  }),
76
- /* @__PURE__ */ b(f, {
79
+ /* @__PURE__ */ S(f, {
77
80
  label: "Description",
78
- value: j,
79
- onChange: M,
81
+ value: N,
82
+ onChange: P,
80
83
  placeholder: "What this board is for…",
81
84
  rows: 2,
82
85
  className: "mt-4"
83
86
  }),
84
- /* @__PURE__ */ b(p, {
87
+ /* @__PURE__ */ S(p, {
85
88
  label: "Tags",
86
- value: F,
87
- onChange: I,
89
+ value: L,
90
+ onChange: R,
88
91
  placeholder: "summer, formal",
89
92
  hint: "Comma-separated",
90
93
  className: "mt-4"
91
94
  }),
92
- E.isError && /* @__PURE__ */ b("p", {
95
+ /* @__PURE__ */ S(m, {
96
+ label: "Cover image",
97
+ aspect: "wide",
98
+ value: z,
99
+ onChange: B,
100
+ folder: i.lookbooks,
101
+ className: "mt-4"
102
+ }),
103
+ O.isError && /* @__PURE__ */ S("p", {
93
104
  className: "mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
94
- children: E.error instanceof Error ? E.error.message : "Something went wrong"
105
+ children: O.error instanceof Error ? O.error.message : "Something went wrong"
95
106
  }),
96
- /* @__PURE__ */ b("div", {
107
+ /* @__PURE__ */ S("div", {
97
108
  className: "mt-4 flex justify-end",
98
- children: /* @__PURE__ */ x("button", {
109
+ children: /* @__PURE__ */ C("button", {
99
110
  type: "submit",
100
- disabled: E.isPending,
111
+ disabled: O.isPending,
101
112
  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",
102
- children: [E.isPending ? /* @__PURE__ */ b(_, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ b(v, { className: "h-4 w-4" }), "Create lookbook"]
113
+ children: [O.isPending ? /* @__PURE__ */ S(y, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ S(b, { className: "h-4 w-4" }), "Create lookbook"]
103
114
  })
104
115
  })
105
116
  ]
106
117
  })
107
- }), T.isLoading ? /* @__PURE__ */ b(i, { label: "Loading lookbooks…" }) : T.isError ? /* @__PURE__ */ b(r, { onRetry: () => void T.refetch() }) : z.length === 0 ? /* @__PURE__ */ b(m, {
118
+ }), D.isLoading ? /* @__PURE__ */ S(r, { label: "Loading lookbooks…" }) : D.isError ? /* @__PURE__ */ S(n, { onRetry: () => void D.refetch() }) : U.length === 0 ? /* @__PURE__ */ S(h, {
108
119
  illustration: "empty-data",
109
- icon: /* @__PURE__ */ b(g, { className: "h-10 w-10" }),
120
+ icon: /* @__PURE__ */ S(v, { className: "h-10 w-10" }),
110
121
  title: "No lookbooks yet",
111
122
  description: "Create a themed board to clip catalogue pieces and looks.",
112
- action: /* @__PURE__ */ x("button", {
123
+ action: /* @__PURE__ */ C("button", {
113
124
  type: "button",
114
- onClick: () => O(!0),
125
+ onClick: () => A(!0),
115
126
  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",
116
- children: [/* @__PURE__ */ b(v, { className: "h-4 w-4" }), " New lookbook"]
127
+ children: [/* @__PURE__ */ S(b, { className: "h-4 w-4" }), " New lookbook"]
128
+ })
129
+ }) : /* @__PURE__ */ S("div", {
130
+ className: "grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3",
131
+ children: U.map((e) => {
132
+ let t = Array.isArray(e.entries) ? e.entries.length : null;
133
+ return /* @__PURE__ */ C("button", {
134
+ type: "button",
135
+ onClick: () => E(`/lookbooks/${e.id}`),
136
+ className: "group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--color-focus-ring)]",
137
+ children: [/* @__PURE__ */ S(a, {
138
+ icon: /* @__PURE__ */ S(v, { className: "h-10 w-10" }),
139
+ seed: e.id,
140
+ fileId: e.cover?.id,
141
+ alt: e.name,
142
+ className: "aspect-[4/3] w-full rounded-none"
143
+ }), /* @__PURE__ */ C("div", {
144
+ className: "flex flex-1 flex-col p-4",
145
+ children: [
146
+ /* @__PURE__ */ C("div", {
147
+ className: "flex items-start justify-between gap-2",
148
+ children: [/* @__PURE__ */ C("div", {
149
+ className: "min-w-0",
150
+ children: [/* @__PURE__ */ S("h3", {
151
+ className: "truncate text-base font-semibold text-text-primary",
152
+ children: e.name
153
+ }), t !== null && /* @__PURE__ */ C("p", {
154
+ className: "mt-0.5 text-xs text-text-secondary tabular-nums",
155
+ children: [
156
+ t,
157
+ " ",
158
+ t === 1 ? "item" : "items"
159
+ ]
160
+ })]
161
+ }), /* @__PURE__ */ S(g, {
162
+ visibility: e.visibility,
163
+ className: "shrink-0"
164
+ })]
165
+ }),
166
+ e.description && /* @__PURE__ */ S("p", {
167
+ className: "mt-1.5 line-clamp-2 text-sm text-text-secondary",
168
+ children: e.description
169
+ }),
170
+ e.tags.length > 0 && /* @__PURE__ */ S(u, {
171
+ tags: e.tags,
172
+ max: 4,
173
+ className: "mt-3"
174
+ })
175
+ ]
176
+ })]
177
+ }, e.id);
117
178
  })
118
- }) : /* @__PURE__ */ b("div", {
119
- className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
120
- children: z.map((e) => /* @__PURE__ */ x("button", {
121
- type: "button",
122
- onClick: () => w(`/lookbooks/${e.id}`),
123
- 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",
124
- children: [/* @__PURE__ */ b(a, {
125
- icon: /* @__PURE__ */ b(g, { className: "h-9 w-9" }),
126
- seed: e.id,
127
- className: "h-28 w-full"
128
- }), /* @__PURE__ */ x("div", {
129
- className: "p-4",
130
- children: [
131
- /* @__PURE__ */ x("div", {
132
- className: "flex items-start justify-between gap-2",
133
- children: [/* @__PURE__ */ b("h3", {
134
- className: "truncate text-base font-semibold text-text-primary",
135
- children: e.name
136
- }), /* @__PURE__ */ b(n, { status: e.visibility })]
137
- }),
138
- e.description && /* @__PURE__ */ b("p", {
139
- className: "mt-1.5 line-clamp-2 text-sm text-text-secondary",
140
- children: e.description
141
- }),
142
- e.tags.length > 0 && /* @__PURE__ */ b(u, {
143
- tags: e.tags,
144
- max: 4,
145
- className: "mt-3"
146
- })
147
- ]
148
- })]
149
- }, e.id))
150
179
  })]
151
180
  });
152
181
  }
153
182
  //#endregion
154
- export { w as LookbooksListPage };
183
+ export { E as LookbooksListPage };
155
184
 
156
185
  //# sourceMappingURL=LookbooksListPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LookbooksListPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/lookbooks/LookbooksListPage.tsx"],"sourcesContent":["/**\n * Lookbooks List Page\n * @module coolandlovely/pages/lookbooks\n *\n * Lists themed lookbook boards (`cooListLookbooks`) and supports creating one\n * inline (`cooCreateLookbook`).\n */\n\nimport { useState } from 'react';\nimport { Plus, BookHeart, Loader2, X } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\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 { TextField, TextAreaField, SelectField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useLookbooks, useCreateLookbook } from '../../hooks';\nimport { parseCsv, titleCase } from '../../utils/formatters';\nimport type { CooVisibility } from '../../types';\n\nconst VISIBILITIES: CooVisibility[] = ['PRIVATE', 'FOLLOWERS', 'PUBLIC'];\n\nexport function LookbooksListPage() {\n const { navigateTo } = useCoolAndLovely();\n const query = useLookbooks();\n const createLookbook = useCreateLookbook();\n\n const [creating, setCreating] = useState(false);\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [visibility, setVisibility] = useState<CooVisibility>('PRIVATE');\n const [tags, setTags] = useState('');\n const [nameError, setNameError] = useState<string | undefined>();\n\n const lookbooks = query.data ?? [];\n const visibilityOptions = VISIBILITIES.map((v) => ({ value: v, label: titleCase(v) }));\n\n const handleCreate = (e: React.FormEvent) => {\n e.preventDefault();\n if (!name.trim()) {\n setNameError('Name is required');\n return;\n }\n setNameError(undefined);\n createLookbook.mutate(\n {\n name: name.trim(),\n description: description.trim() || undefined,\n visibility,\n tags: parseCsv(tags),\n },\n {\n onSuccess: (lb) => {\n setCreating(false);\n setName('');\n setDescription('');\n setTags('');\n setVisibility('PRIVATE');\n navigateTo(`/lookbooks/${lb.id}`);\n },\n }\n );\n };\n\n return (\n <PageLayout\n title=\"Lookbooks\"\n description=\"Themed boards of saved catalogue pieces and looks\"\n actions={\n <button\n type=\"button\"\n onClick={() => setCreating((v) => !v)}\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 {creating ? <X className=\"h-4 w-4\" /> : <Plus className=\"h-4 w-4\" />}\n {creating ? 'Close' : 'New lookbook'}\n </button>\n }\n >\n {creating && (\n <form onSubmit={handleCreate} className=\"mb-6\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h2 className=\"mb-4 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <BookHeart className=\"h-5 w-5\" /> New lookbook\n </h2>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <TextField\n label=\"Name\"\n value={name}\n onChange={setName}\n placeholder=\"Summer wedding guest\"\n required\n error={nameError}\n />\n <SelectField\n label=\"Visibility\"\n value={visibility}\n onChange={(v) => setVisibility(v as CooVisibility)}\n options={visibilityOptions}\n />\n </div>\n <TextAreaField\n label=\"Description\"\n value={description}\n onChange={setDescription}\n placeholder=\"What this board is for…\"\n rows={2}\n className=\"mt-4\"\n />\n <TextField\n label=\"Tags\"\n value={tags}\n onChange={setTags}\n placeholder=\"summer, formal\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n {createLookbook.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {createLookbook.error instanceof Error\n ? createLookbook.error.message\n : 'Something went wrong'}\n </p>\n )}\n <div className=\"mt-4 flex justify-end\">\n <button\n type=\"submit\"\n disabled={createLookbook.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 {createLookbook.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4\" />\n )}\n Create lookbook\n </button>\n </div>\n </GlassCard>\n </form>\n )}\n\n {query.isLoading ? (\n <LoadingView label=\"Loading lookbooks…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : lookbooks.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<BookHeart className=\"h-10 w-10\" />}\n title=\"No lookbooks yet\"\n description=\"Create a themed board to clip catalogue pieces and looks.\"\n action={\n <button\n type=\"button\"\n onClick={() => setCreating(true)}\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 lookbook\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {lookbooks.map((lb) => (\n <button\n key={lb.id}\n type=\"button\"\n onClick={() => navigateTo(`/lookbooks/${lb.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\n icon={<BookHeart className=\"h-9 w-9\" />}\n seed={lb.id}\n className=\"h-28 w-full\"\n />\n <div className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">{lb.name}</h3>\n <StatusBadge status={lb.visibility} />\n </div>\n {lb.description && (\n <p className=\"mt-1.5 line-clamp-2 text-sm text-text-secondary\">\n {lb.description}\n </p>\n )}\n {lb.tags.length > 0 && <TagList tags={lb.tags} max={4} className=\"mt-3\" />}\n </div>\n </button>\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"LookbooksListPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/lookbooks/LookbooksListPage.tsx"],"sourcesContent":["/**\n * Lookbooks List Page\n * @module coolandlovely/pages/lookbooks\n *\n * Lists themed lookbook boards (`cooListLookbooks`) as mood-board cards — a\n * cover, a visibility chip and (when the list selection carries entries) an\n * item count — and supports creating one inline (`cooCreateLookbook`).\n */\n\nimport { useState } from 'react';\nimport { Plus, BookHeart, Loader2, X } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { VisibilityChip } from '../../components/VisibilityChip';\nimport { TextField, TextAreaField, SelectField } from '../../components/Field';\nimport { ImageUploadField } from '../../components/ImageUploadField';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useLookbooks, useCreateLookbook } from '../../hooks';\nimport { COO_FILE_FOLDERS } from '../../hooks/useFiles';\nimport { parseCsv, titleCase } from '../../utils/formatters';\nimport type { CooVisibility } from '../../types';\n\nconst VISIBILITIES: CooVisibility[] = ['PRIVATE', 'FOLLOWERS', 'PUBLIC'];\n\nexport function LookbooksListPage() {\n const { navigateTo } = useCoolAndLovely();\n const query = useLookbooks();\n const createLookbook = useCreateLookbook();\n\n const [creating, setCreating] = useState(false);\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [visibility, setVisibility] = useState<CooVisibility>('PRIVATE');\n const [tags, setTags] = useState('');\n const [coverFileId, setCoverFileId] = useState('');\n const [nameError, setNameError] = useState<string | undefined>();\n\n const lookbooks = query.data ?? [];\n const visibilityOptions = VISIBILITIES.map((v) => ({ value: v, label: titleCase(v) }));\n\n const handleCreate = (e: React.FormEvent) => {\n e.preventDefault();\n if (!name.trim()) {\n setNameError('Name is required');\n return;\n }\n setNameError(undefined);\n createLookbook.mutate(\n {\n name: name.trim(),\n description: description.trim() || undefined,\n visibility,\n tags: parseCsv(tags),\n coverFileId: coverFileId || undefined,\n },\n {\n onSuccess: (lb) => {\n setCreating(false);\n setName('');\n setDescription('');\n setTags('');\n setVisibility('PRIVATE');\n setCoverFileId('');\n navigateTo(`/lookbooks/${lb.id}`);\n },\n }\n );\n };\n\n return (\n <PageLayout\n title=\"Lookbooks\"\n description=\"Themed boards of saved catalogue pieces and looks\"\n actions={\n <button\n type=\"button\"\n onClick={() => setCreating((v) => !v)}\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 {creating ? <X className=\"h-4 w-4\" /> : <Plus className=\"h-4 w-4\" />}\n {creating ? 'Close' : 'New lookbook'}\n </button>\n }\n >\n {creating && (\n <form onSubmit={handleCreate} className=\"mb-6\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h2 className=\"mb-4 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <BookHeart className=\"h-5 w-5\" /> New lookbook\n </h2>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <TextField\n label=\"Name\"\n value={name}\n onChange={setName}\n placeholder=\"Summer wedding guest\"\n required\n error={nameError}\n />\n <SelectField\n label=\"Visibility\"\n value={visibility}\n onChange={(v) => setVisibility(v as CooVisibility)}\n options={visibilityOptions}\n />\n </div>\n <TextAreaField\n label=\"Description\"\n value={description}\n onChange={setDescription}\n placeholder=\"What this board is for…\"\n rows={2}\n className=\"mt-4\"\n />\n <TextField\n label=\"Tags\"\n value={tags}\n onChange={setTags}\n placeholder=\"summer, formal\"\n hint=\"Comma-separated\"\n className=\"mt-4\"\n />\n <ImageUploadField\n label=\"Cover image\"\n aspect=\"wide\"\n value={coverFileId}\n onChange={setCoverFileId}\n folder={COO_FILE_FOLDERS.lookbooks}\n className=\"mt-4\"\n />\n {createLookbook.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {createLookbook.error instanceof Error\n ? createLookbook.error.message\n : 'Something went wrong'}\n </p>\n )}\n <div className=\"mt-4 flex justify-end\">\n <button\n type=\"submit\"\n disabled={createLookbook.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 {createLookbook.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Plus className=\"h-4 w-4\" />\n )}\n Create lookbook\n </button>\n </div>\n </GlassCard>\n </form>\n )}\n\n {query.isLoading ? (\n <LoadingView label=\"Loading lookbooks…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : lookbooks.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<BookHeart className=\"h-10 w-10\" />}\n title=\"No lookbooks yet\"\n description=\"Create a themed board to clip catalogue pieces and looks.\"\n action={\n <button\n type=\"button\"\n onClick={() => setCreating(true)}\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 lookbook\n </button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3\">\n {lookbooks.map((lb) => {\n const entryCount = Array.isArray(lb.entries) ? lb.entries.length : null;\n return (\n <button\n key={lb.id}\n type=\"button\"\n onClick={() => navigateTo(`/lookbooks/${lb.id}`)}\n className=\"group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <Thumbnail\n icon={<BookHeart className=\"h-10 w-10\" />}\n seed={lb.id}\n fileId={lb.cover?.id}\n alt={lb.name}\n className=\"aspect-[4/3] w-full rounded-none\"\n />\n <div className=\"flex flex-1 flex-col 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\">\n {lb.name}\n </h3>\n {entryCount !== null && (\n <p className=\"mt-0.5 text-xs text-text-secondary tabular-nums\">\n {entryCount} {entryCount === 1 ? 'item' : 'items'}\n </p>\n )}\n </div>\n <VisibilityChip visibility={lb.visibility} className=\"shrink-0\" />\n </div>\n {lb.description && (\n <p className=\"mt-1.5 line-clamp-2 text-sm text-text-secondary\">\n {lb.description}\n </p>\n )}\n {lb.tags.length > 0 && <TagList tags={lb.tags} max={4} className=\"mt-3\" />}\n </div>\n </button>\n );\n })}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}