@burdenoff/microfe-coolandlovely 2026.805.1 → 2026.820.2
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.d.ts.map +1 -1
- package/dist/coolandlovely/CoolAndLovelyRoot.js +60 -58
- package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
- package/dist/coolandlovely/components/AddToLookbookButton.d.ts.map +1 -1
- package/dist/coolandlovely/components/AddToLookbookButton.js +1 -0
- package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -1
- package/dist/coolandlovely/components/BrandCard.d.ts +11 -0
- package/dist/coolandlovely/components/BrandCard.d.ts.map +1 -0
- package/dist/coolandlovely/components/BrandCard.js +55 -0
- package/dist/coolandlovely/components/BrandCard.js.map +1 -0
- package/dist/coolandlovely/components/CollectionCard.d.ts +11 -0
- package/dist/coolandlovely/components/CollectionCard.d.ts.map +1 -0
- package/dist/coolandlovely/components/CollectionCard.js +60 -0
- package/dist/coolandlovely/components/CollectionCard.js.map +1 -0
- package/dist/coolandlovely/components/FeedbackWidget.d.ts +4 -0
- package/dist/coolandlovely/components/FeedbackWidget.d.ts.map +1 -0
- package/dist/coolandlovely/components/FeedbackWidget.js +134 -0
- package/dist/coolandlovely/components/FeedbackWidget.js.map +1 -0
- package/dist/coolandlovely/components/LookbookCard.d.ts +11 -0
- package/dist/coolandlovely/components/LookbookCard.d.ts.map +1 -0
- package/dist/coolandlovely/components/LookbookCard.js +60 -0
- package/dist/coolandlovely/components/LookbookCard.js.map +1 -0
- package/dist/coolandlovely/components/WardrobeItemCard.d.ts +14 -0
- package/dist/coolandlovely/components/WardrobeItemCard.d.ts.map +1 -0
- package/dist/coolandlovely/components/WardrobeItemCard.js +92 -0
- package/dist/coolandlovely/components/WardrobeItemCard.js.map +1 -0
- package/dist/coolandlovely/components/index.d.ts +5 -0
- package/dist/coolandlovely/components/index.d.ts.map +1 -1
- package/dist/coolandlovely/components/index.js +26 -0
- package/dist/coolandlovely/pages/HomePage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/HomePage.js +10 -4
- package/dist/coolandlovely/pages/HomePage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js +7 -7
- package/dist/coolandlovely/pages/brands/BrandsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandsListPage.js +48 -89
- package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +4 -4
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +13 -13
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +4 -4
- package/dist/coolandlovely/pages/collections/CollectionsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js +28 -74
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +5 -5
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +66 -112
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js +124 -124
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +46 -120
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
- package/dist/stories/BrandCard.stories.d.ts +10 -0
- package/dist/stories/BrandCard.stories.d.ts.map +1 -0
- package/dist/stories/CollectionCard.stories.d.ts +10 -0
- package/dist/stories/CollectionCard.stories.d.ts.map +1 -0
- package/dist/stories/FeedbackWidget.stories.d.ts +9 -0
- package/dist/stories/FeedbackWidget.stories.d.ts.map +1 -0
- package/dist/stories/LookCard.stories.d.ts +10 -0
- package/dist/stories/LookCard.stories.d.ts.map +1 -0
- package/dist/stories/LookbookCard.stories.d.ts +10 -0
- package/dist/stories/LookbookCard.stories.d.ts.map +1 -0
- package/dist/stories/WardrobeItemCard.stories.d.ts +11 -0
- package/dist/stories/WardrobeItemCard.stories.d.ts.map +1 -0
- package/dist/stories/__fixtures__/coolandlovely.d.ts +13 -0
- package/dist/stories/__fixtures__/coolandlovely.d.ts.map +1 -0
- package/package.json +4 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FeedbackWidget.js","names":[],"sources":["../../../src/coolandlovely/components/FeedbackWidget.tsx"],"sourcesContent":["/**\n * Lightweight NPS / feedback widget\n * @module coolandlovely/components\n *\n * Floating feedback capture mounted across all CoolAndLovely surfaces.\n * Submissions are stored locally and emitted as analytics events so the\n * product can gather signals without blocking on a backend integration.\n */\n\nimport type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport { MessageSquarePlus, X, Send } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { TextAreaField } from './Field';\nimport { cn } from '../utils/cn';\n\nconst STORAGE_KEY = 'coolandlovely-feedback-submitted-at';\nconst COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000; // one week\n\ninterface FeedbackSubmission {\n score: number;\n comment: string;\n path: string;\n submittedAt: string;\n}\n\nfunction hasRecentSubmission(): boolean {\n if (typeof window === 'undefined') return false;\n const raw = window.localStorage.getItem(STORAGE_KEY);\n if (!raw) return false;\n try {\n const ts = parseInt(raw, 10);\n return Date.now() - ts < COOLDOWN_MS;\n } catch {\n return false;\n }\n}\n\nfunction recordSubmission() {\n if (typeof window === 'undefined') return;\n window.localStorage.setItem(STORAGE_KEY, String(Date.now()));\n}\n\nfunction emitFeedbackEvent(submission: FeedbackSubmission) {\n // Emit a structured event that the host shell's analytics pipeline can pick up.\n if (typeof window === 'undefined') return;\n window.dispatchEvent(\n new CustomEvent('coolandlovely:feedback_submitted', {\n detail: submission,\n bubbles: true,\n })\n );\n}\n\nexport const FeedbackWidget: FC = () => {\n const [isOpen, setIsOpen] = useState(false);\n const [score, setScore] = useState<number | null>(null);\n const [comment, setComment] = useState('');\n const [submitted, setSubmitted] = useState(false);\n const [recentlySubmitted, setRecentlySubmitted] = useState(false);\n\n useEffect(() => {\n setRecentlySubmitted(hasRecentSubmission());\n }, []);\n\n const handleToggle = useCallback(() => {\n setIsOpen((open) => !open);\n }, []);\n\n const handleClose = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n const handleSubmit = useCallback(() => {\n if (score === null) return;\n\n const submission: FeedbackSubmission = {\n score,\n comment: comment.trim(),\n path: typeof window !== 'undefined' ? window.location.pathname : '',\n submittedAt: new Date().toISOString(),\n };\n\n emitFeedbackEvent(submission);\n recordSubmission();\n setSubmitted(true);\n setRecentlySubmitted(true);\n\n // Auto-close after a short delay so the user sees the thank-you state.\n window.setTimeout(() => {\n setIsOpen(false);\n setScore(null);\n setComment('');\n setSubmitted(false);\n }, 2500);\n }, [score, comment]);\n\n if (recentlySubmitted && !isOpen) {\n // Do not show the launcher at all for a week after submission.\n return null;\n }\n\n return (\n <div className=\"fixed bottom-4 right-4 z-50 flex flex-col items-end gap-3\">\n {isOpen && (\n <GlassCard className=\"w-[min(92vw,360px)] overflow-hidden p-0 shadow-xl\">\n <div className=\"flex items-center justify-between border-b border-border-seam px-4 py-3\">\n <h2 className=\"text-sm font-semibold text-text-primary\">\n How is CoolAndLovely working for you?\n </h2>\n <button\n type=\"button\"\n onClick={handleClose}\n aria-label=\"Close feedback\"\n className=\"rounded-md p-1 text-text-secondary transition-colors hover:bg-bg-surface hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <div className=\"p-4\">\n {submitted ? (\n <div className=\"py-6 text-center\">\n <p className=\"text-sm font-medium text-status-success-text\">\n Thank you for your feedback!\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n Your input helps us improve CoolAndLovely.\n </p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n <div>\n <p className=\"mb-2 text-xs font-medium text-text-secondary\">\n Rate your experience (0 = not likely, 10 = very likely)\n </p>\n <div className=\"flex justify-between gap-1\" role=\"group\" aria-label=\"NPS score\">\n {Array.from({ length: 11 }, (_, i) => i).map((value) => (\n <button\n key={value}\n type=\"button\"\n onClick={() => setScore(value)}\n aria-pressed={score === value}\n className={cn(\n 'flex h-8 w-7 items-center justify-center rounded-md text-xs font-medium transition-colors',\n score === value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'border border-border-seam bg-bg-surface text-text-secondary hover:border-action-primary-bg hover:text-text-primary'\n )}\n >\n {value}\n </button>\n ))}\n </div>\n </div>\n\n <TextAreaField\n id=\"feedback-comment\"\n label=\"What could be better?\"\n value={comment}\n onChange={setComment}\n placeholder=\"Tell us what’s working or what we should improve…\"\n rows={3}\n hint=\"Optional — your comment is stored with your score and current page.\"\n />\n\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={score === null}\n className={cn(\n 'inline-flex w-full items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors',\n score === null\n ? 'cursor-not-allowed bg-bg-surface text-text-secondary'\n : 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90'\n )}\n >\n <Send className=\"h-4 w-4\" aria-hidden=\"true\" />\n Submit feedback\n </button>\n </div>\n )}\n </div>\n </GlassCard>\n )}\n\n <button\n type=\"button\"\n onClick={handleToggle}\n aria-label={isOpen ? 'Close feedback' : 'Give feedback'}\n aria-expanded={isOpen}\n className={cn(\n 'flex h-12 w-12 items-center justify-center rounded-full shadow-lg transition-transform hover:scale-105 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]',\n isOpen\n ? 'bg-bg-surface text-text-primary'\n : 'bg-action-primary-bg text-action-primary-text'\n )}\n >\n {isOpen ? (\n <X className=\"h-5 w-5\" aria-hidden=\"true\" />\n ) : (\n <MessageSquarePlus className=\"h-5 w-5\" aria-hidden=\"true\" />\n )}\n </button>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { CooLookbook } from '../types';
|
|
3
|
+
|
|
4
|
+
interface LookbookCardProps {
|
|
5
|
+
lookbook: CooLookbook;
|
|
6
|
+
onClick?: () => void;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const LookbookCard: FC<LookbookCardProps>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=LookbookCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LookbookCard.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/LookbookCard.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAMhC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,UAAU,iBAAiB;IACzB,QAAQ,EAAE,WAAW,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAuC9C,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { Thumbnail as t } from "./Thumbnail.js";
|
|
3
|
+
import { TagList as n } from "./TagList.js";
|
|
4
|
+
import { VisibilityChip as r } from "./VisibilityChip.js";
|
|
5
|
+
import { BookHeart as i } from "lucide-react";
|
|
6
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
7
|
+
//#region src/coolandlovely/components/LookbookCard.tsx
|
|
8
|
+
var s = ({ lookbook: s, onClick: c, className: l }) => {
|
|
9
|
+
let u = Array.isArray(s.entries) ? s.entries.length : null;
|
|
10
|
+
return /* @__PURE__ */ o("button", {
|
|
11
|
+
type: "button",
|
|
12
|
+
onClick: c,
|
|
13
|
+
"aria-label": `Open lookbook ${s.name}`,
|
|
14
|
+
className: e("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)]", l),
|
|
15
|
+
children: [/* @__PURE__ */ a(t, {
|
|
16
|
+
icon: /* @__PURE__ */ a(i, { className: "h-10 w-10" }),
|
|
17
|
+
seed: s.id,
|
|
18
|
+
fileId: s.cover?.id,
|
|
19
|
+
alt: s.name,
|
|
20
|
+
className: "aspect-[4/3] w-full rounded-none"
|
|
21
|
+
}), /* @__PURE__ */ o("div", {
|
|
22
|
+
className: "flex flex-1 flex-col p-4",
|
|
23
|
+
children: [
|
|
24
|
+
/* @__PURE__ */ o("div", {
|
|
25
|
+
className: "flex items-start justify-between gap-2",
|
|
26
|
+
children: [/* @__PURE__ */ o("div", {
|
|
27
|
+
className: "min-w-0",
|
|
28
|
+
children: [/* @__PURE__ */ a("h3", {
|
|
29
|
+
className: "truncate text-base font-semibold text-text-primary",
|
|
30
|
+
children: s.name
|
|
31
|
+
}), u !== null && /* @__PURE__ */ o("p", {
|
|
32
|
+
className: "mt-0.5 text-xs text-text-secondary tabular-nums",
|
|
33
|
+
children: [
|
|
34
|
+
u,
|
|
35
|
+
" ",
|
|
36
|
+
u === 1 ? "item" : "items"
|
|
37
|
+
]
|
|
38
|
+
})]
|
|
39
|
+
}), /* @__PURE__ */ a(r, {
|
|
40
|
+
visibility: s.visibility,
|
|
41
|
+
className: "shrink-0"
|
|
42
|
+
})]
|
|
43
|
+
}),
|
|
44
|
+
s.description && /* @__PURE__ */ a("p", {
|
|
45
|
+
className: "mt-1.5 line-clamp-2 text-sm text-text-secondary",
|
|
46
|
+
children: s.description
|
|
47
|
+
}),
|
|
48
|
+
s.tags.length > 0 && /* @__PURE__ */ a(n, {
|
|
49
|
+
tags: s.tags,
|
|
50
|
+
max: 4,
|
|
51
|
+
className: "mt-3"
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
})]
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
export { s as LookbookCard };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=LookbookCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LookbookCard.js","names":[],"sources":["../../../src/coolandlovely/components/LookbookCard.tsx"],"sourcesContent":["/**\n * LookbookCard — a themed board card for list/grids.\n * @module coolandlovely/components\n *\n * Cover image, name, item count, visibility chip, description and tags.\n * Extracted from LookbooksListPage for isolated rendering/tests.\n */\n\nimport type { FC } from 'react';\nimport { BookHeart } from 'lucide-react';\nimport { Thumbnail } from './Thumbnail';\nimport { TagList } from './TagList';\nimport { VisibilityChip } from './VisibilityChip';\nimport { cn } from '../utils/cn';\nimport type { CooLookbook } from '../types';\n\ninterface LookbookCardProps {\n lookbook: CooLookbook;\n onClick?: () => void;\n className?: string;\n}\n\nexport const LookbookCard: FC<LookbookCardProps> = ({ lookbook, onClick, className }) => {\n const entryCount = Array.isArray(lookbook.entries) ? lookbook.entries.length : null;\n\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={`Open lookbook ${lookbook.name}`}\n className={cn(\n '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 className\n )}\n >\n <Thumbnail\n icon={<BookHeart className=\"h-10 w-10\" />}\n seed={lookbook.id}\n fileId={lookbook.cover?.id}\n alt={lookbook.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\">{lookbook.name}</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={lookbook.visibility} className=\"shrink-0\" />\n </div>\n {lookbook.description && (\n <p className=\"mt-1.5 line-clamp-2 text-sm text-text-secondary\">{lookbook.description}</p>\n )}\n {lookbook.tags.length > 0 && <TagList tags={lookbook.tags} max={4} className=\"mt-3\" />}\n </div>\n </button>\n );\n};\n"],"mappings":";;;;;;;AAsBA,IAAa,KAAuC,EAAE,aAAU,YAAS,mBAAgB;CACvF,IAAM,IAAa,MAAM,QAAQ,EAAS,QAAQ,GAAG,EAAS,QAAQ,SAAS;AAE/E,QACE,kBAAC,UAAD;EACE,MAAK;EACI;EACT,cAAY,iBAAiB,EAAS;EACtC,WAAW,EACT,qSACA,EACD;YAPH,CASE,kBAAC,GAAD;GACE,MAAM,kBAAC,GAAD,EAAW,WAAU,aAAc,CAAA;GACzC,MAAM,EAAS;GACf,QAAQ,EAAS,OAAO;GACxB,KAAK,EAAS;GACd,WAAU;GACV,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAsD,EAAS;OAAU,CAAA,EACtF,MAAe,QACd,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG;QAAW;QAAE,MAAe,IAAI,SAAS;QACxC;SAEF;SACN,kBAAC,GAAD;MAAgB,YAAY,EAAS;MAAY,WAAU;MAAa,CAAA,CACpE;;IACL,EAAS,eACR,kBAAC,KAAD;KAAG,WAAU;eAAmD,EAAS;KAAgB,CAAA;IAE1F,EAAS,KAAK,SAAS,KAAK,kBAAC,GAAD;KAAS,MAAM,EAAS;KAAM,KAAK;KAAG,WAAU;KAAS,CAAA;IAClF;KACC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { CooWardrobeItem } from '../types';
|
|
3
|
+
|
|
4
|
+
interface WardrobeItemCardProps {
|
|
5
|
+
item: CooWardrobeItem;
|
|
6
|
+
onLogWear?: () => void;
|
|
7
|
+
onRemove?: () => void;
|
|
8
|
+
isLoggingWear?: boolean;
|
|
9
|
+
isRemoving?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const WardrobeItemCard: FC<WardrobeItemCardProps>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=WardrobeItemCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WardrobeItemCard.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/WardrobeItemCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAOhC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,UAAU,qBAAqB;IAC7B,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,gBAAgB,EAAE,EAAE,CAAC,qBAAqB,CAwGtD,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { StatusBadge as t } from "./StatusBadge.js";
|
|
3
|
+
import { Thumbnail as n } from "./Thumbnail.js";
|
|
4
|
+
import { formatPrice as r, formatRelativeTime as i, titleCase as a } from "../utils/formatters.js";
|
|
5
|
+
import { categoryIcon as o } from "../utils/icons.js";
|
|
6
|
+
import { CheckCircle2 as s, Loader2 as c, Trash2 as l } from "lucide-react";
|
|
7
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
8
|
+
//#region src/coolandlovely/components/WardrobeItemCard.tsx
|
|
9
|
+
var f = ({ item: f, onLogWear: p, onRemove: m, isLoggingWear: h, isRemoving: g, className: _ }) => {
|
|
10
|
+
let v = f.brandName || f.colour || a(f.category);
|
|
11
|
+
return /* @__PURE__ */ d("div", {
|
|
12
|
+
className: e("group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface shadow-sm transition-shadow hover:shadow-lg", _),
|
|
13
|
+
children: [/* @__PURE__ */ d("div", {
|
|
14
|
+
className: "relative",
|
|
15
|
+
children: [
|
|
16
|
+
/* @__PURE__ */ u(n, {
|
|
17
|
+
icon: o(f.category, 40),
|
|
18
|
+
seed: f.id,
|
|
19
|
+
fileId: f.image?.id,
|
|
20
|
+
alt: f.name,
|
|
21
|
+
className: "aspect-[4/5] w-full rounded-none"
|
|
22
|
+
}),
|
|
23
|
+
/* @__PURE__ */ d("div", {
|
|
24
|
+
className: "absolute right-2.5 top-2.5 flex h-12 w-12 flex-col items-center justify-center rounded-full bg-bg-surface/90 text-center leading-none shadow-sm ring-1 ring-[var(--color-accent-soft)] backdrop-blur",
|
|
25
|
+
children: [/* @__PURE__ */ u("span", {
|
|
26
|
+
className: "text-[9px] font-medium uppercase tracking-wide text-text-secondary",
|
|
27
|
+
children: "worn"
|
|
28
|
+
}), /* @__PURE__ */ d("span", {
|
|
29
|
+
className: "text-sm font-bold tabular-nums text-text-primary",
|
|
30
|
+
children: [f.timesWorn, "×"]
|
|
31
|
+
})]
|
|
32
|
+
}),
|
|
33
|
+
/* @__PURE__ */ d("div", {
|
|
34
|
+
className: "absolute inset-x-0 bottom-0 flex items-center gap-1.5 bg-gradient-to-t from-bg-canvas/90 to-transparent p-2.5 pt-8 opacity-100 transition-opacity duration-200 sm:opacity-0 sm:focus-within:opacity-100 sm:group-hover:opacity-100",
|
|
35
|
+
children: [/* @__PURE__ */ d("button", {
|
|
36
|
+
type: "button",
|
|
37
|
+
disabled: h,
|
|
38
|
+
onClick: p,
|
|
39
|
+
"aria-label": `Log a wear for ${f.name}`,
|
|
40
|
+
className: "inline-flex items-center gap-1 rounded-lg bg-bg-surface/90 px-2.5 py-1 text-xs font-medium text-status-success-text shadow-sm backdrop-blur transition-colors hover:bg-bg-surface disabled:opacity-50",
|
|
41
|
+
children: [h ? /* @__PURE__ */ u(c, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ u(s, { className: "h-3.5 w-3.5" }), "Wore it"]
|
|
42
|
+
}), /* @__PURE__ */ u("button", {
|
|
43
|
+
type: "button",
|
|
44
|
+
disabled: g,
|
|
45
|
+
onClick: m,
|
|
46
|
+
"aria-label": `Remove ${f.name}`,
|
|
47
|
+
className: "ml-auto inline-flex h-7 w-7 items-center justify-center rounded-lg bg-bg-surface/90 text-text-secondary shadow-sm backdrop-blur transition-colors hover:text-status-error-text disabled:opacity-50",
|
|
48
|
+
children: g ? /* @__PURE__ */ u(c, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ u(l, { className: "h-3.5 w-3.5" })
|
|
49
|
+
})]
|
|
50
|
+
})
|
|
51
|
+
]
|
|
52
|
+
}), /* @__PURE__ */ d("div", {
|
|
53
|
+
className: "flex flex-1 flex-col p-4",
|
|
54
|
+
children: [
|
|
55
|
+
/* @__PURE__ */ u("p", {
|
|
56
|
+
className: "truncate text-sm font-semibold text-text-primary",
|
|
57
|
+
children: f.name
|
|
58
|
+
}),
|
|
59
|
+
/* @__PURE__ */ u("p", {
|
|
60
|
+
className: "truncate text-xs text-text-secondary",
|
|
61
|
+
children: v
|
|
62
|
+
}),
|
|
63
|
+
/* @__PURE__ */ d("div", {
|
|
64
|
+
className: "mt-2 flex flex-wrap items-center gap-1.5",
|
|
65
|
+
children: [/* @__PURE__ */ u(t, { status: f.source }), f.size && /* @__PURE__ */ u("span", {
|
|
66
|
+
className: "rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
|
|
67
|
+
children: f.size
|
|
68
|
+
})]
|
|
69
|
+
}),
|
|
70
|
+
f.notes && /* @__PURE__ */ u("p", {
|
|
71
|
+
className: "mt-2 line-clamp-2 text-xs text-text-secondary",
|
|
72
|
+
children: f.notes
|
|
73
|
+
}),
|
|
74
|
+
/* @__PURE__ */ d("div", {
|
|
75
|
+
className: "mt-3 flex items-center justify-between gap-2 border-t border-border-seam pt-2 text-xs text-text-secondary",
|
|
76
|
+
children: [/* @__PURE__ */ u("span", { children: f.lastWornAt ? `Last worn ${i(f.lastWornAt)}` : "Not worn yet" }), f.costPerWear == null ? f.purchasePrice == null ? null : /* @__PURE__ */ d("span", {
|
|
77
|
+
className: "shrink-0 text-text-secondary",
|
|
78
|
+
children: [r(f.purchasePrice, f.currency ?? "USD"), " paid"]
|
|
79
|
+
}) : /* @__PURE__ */ d("span", {
|
|
80
|
+
title: `${r(f.purchasePrice ?? 0, f.currency ?? "USD")} ÷ ${f.timesWorn} wears`,
|
|
81
|
+
className: "shrink-0 font-medium text-text-primary",
|
|
82
|
+
children: [r(f.costPerWear, f.currency ?? "USD"), "/wear"]
|
|
83
|
+
})]
|
|
84
|
+
})
|
|
85
|
+
]
|
|
86
|
+
})]
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
//#endregion
|
|
90
|
+
export { f as WardrobeItemCard };
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=WardrobeItemCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WardrobeItemCard.js","names":[],"sources":["../../../src/coolandlovely/components/WardrobeItemCard.tsx"],"sourcesContent":["/**\n * WardrobeItemCard — a digital wardrobe piece card for list/grids.\n * @module coolandlovely/components\n *\n * Image-forward card with a worn-count badge, hover action bar (log wear / remove),\n * details, size, source and cost-per-wear. Extracted from WardrobeListPage for\n * isolated rendering/tests.\n */\n\nimport type { FC } from 'react';\nimport { CheckCircle2, Loader2, Trash2 } from 'lucide-react';\nimport { Thumbnail } from './Thumbnail';\nimport { StatusBadge } from './StatusBadge';\nimport { categoryIcon } from '../utils/icons';\nimport { titleCase, formatRelativeTime, formatPrice } from '../utils/formatters';\nimport { cn } from '../utils/cn';\nimport type { CooWardrobeItem } from '../types';\n\ninterface WardrobeItemCardProps {\n item: CooWardrobeItem;\n onLogWear?: () => void;\n onRemove?: () => void;\n isLoggingWear?: boolean;\n isRemoving?: boolean;\n className?: string;\n}\n\nexport const WardrobeItemCard: FC<WardrobeItemCardProps> = ({\n item,\n onLogWear,\n onRemove,\n isLoggingWear,\n isRemoving,\n className,\n}) => {\n const subtitle = item.brandName || item.colour || titleCase(item.category);\n\n return (\n <div\n className={cn(\n 'group flex flex-col overflow-hidden rounded-xl border border-border-seam bg-bg-surface shadow-sm transition-shadow hover:shadow-lg',\n className\n )}\n >\n {/* Image-forward hero */}\n <div className=\"relative\">\n <Thumbnail\n icon={categoryIcon(item.category, 40)}\n seed={item.id}\n fileId={item.image?.id}\n alt={item.name}\n className=\"aspect-[4/5] w-full rounded-none\"\n />\n\n {/* Worn-count badge */}\n <div className=\"absolute right-2.5 top-2.5 flex h-12 w-12 flex-col items-center justify-center rounded-full bg-bg-surface/90 text-center leading-none shadow-sm ring-1 ring-[var(--color-accent-soft)] backdrop-blur\">\n <span className=\"text-[9px] font-medium uppercase tracking-wide text-text-secondary\">\n worn\n </span>\n <span className=\"text-sm font-bold tabular-nums text-text-primary\">\n {item.timesWorn}×\n </span>\n </div>\n\n {/* Hover action bar */}\n <div className=\"absolute inset-x-0 bottom-0 flex items-center gap-1.5 bg-gradient-to-t from-bg-canvas/90 to-transparent p-2.5 pt-8 opacity-100 transition-opacity duration-200 sm:opacity-0 sm:focus-within:opacity-100 sm:group-hover:opacity-100\">\n <button\n type=\"button\"\n disabled={isLoggingWear}\n onClick={onLogWear}\n aria-label={`Log a wear for ${item.name}`}\n className=\"inline-flex items-center gap-1 rounded-lg bg-bg-surface/90 px-2.5 py-1 text-xs font-medium text-status-success-text shadow-sm backdrop-blur transition-colors hover:bg-bg-surface disabled:opacity-50\"\n >\n {isLoggingWear ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"h-3.5 w-3.5\" />\n )}\n Wore it\n </button>\n <button\n type=\"button\"\n disabled={isRemoving}\n onClick={onRemove}\n aria-label={`Remove ${item.name}`}\n className=\"ml-auto inline-flex h-7 w-7 items-center justify-center rounded-lg bg-bg-surface/90 text-text-secondary shadow-sm backdrop-blur transition-colors hover:text-status-error-text disabled:opacity-50\"\n >\n {isRemoving ? (\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 </div>\n\n {/* Details */}\n <div className=\"flex flex-1 flex-col p-4\">\n <p className=\"truncate text-sm font-semibold text-text-primary\">{item.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">{subtitle}</p>\n <div className=\"mt-2 flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={item.source} />\n {item.size && (\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {item.size}\n </span>\n )}\n </div>\n {item.notes && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{item.notes}</p>\n )}\n <div className=\"mt-3 flex items-center justify-between gap-2 border-t border-border-seam pt-2 text-xs text-text-secondary\">\n <span>\n {item.lastWornAt ? `Last worn ${formatRelativeTime(item.lastWornAt)}` : 'Not worn yet'}\n </span>\n {item.costPerWear != null ? (\n <span\n title={`${formatPrice(item.purchasePrice ?? 0, item.currency ?? 'USD')} ÷ ${item.timesWorn} wears`}\n className=\"shrink-0 font-medium text-text-primary\"\n >\n {formatPrice(item.costPerWear, item.currency ?? 'USD')}/wear\n </span>\n ) : item.purchasePrice != null ? (\n <span className=\"shrink-0 text-text-secondary\">\n {formatPrice(item.purchasePrice, item.currency ?? 'USD')} paid\n </span>\n ) : null}\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;AA2BA,IAAa,KAA+C,EAC1D,SACA,cACA,aACA,kBACA,eACA,mBACI;CACJ,IAAM,IAAW,EAAK,aAAa,EAAK,UAAU,EAAU,EAAK,SAAS;AAE1E,QACE,kBAAC,OAAD;EACE,WAAW,EACT,sIACA,EACD;YAJH,CAOE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,MAAM,EAAa,EAAK,UAAU,GAAG;KACrC,MAAM,EAAK;KACX,QAAQ,EAAK,OAAO;KACpB,KAAK,EAAK;KACV,WAAU;KACV,CAAA;IAGF,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAqE;MAE9E,CAAA,EACP,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACG,EAAK,WAAU,IACX;QACH;;IAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,UAAU;MACV,SAAS;MACT,cAAY,kBAAkB,EAAK;MACnC,WAAU;gBALZ,CAOG,IACC,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA,GAEhD,kBAAC,GAAD,EAAc,WAAU,eAAgB,CAAA,EACxC,UAEK;SACT,kBAAC,UAAD;MACE,MAAK;MACL,UAAU;MACV,SAAS;MACT,cAAY,UAAU,EAAK;MAC3B,WAAU;gBAET,IACC,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA,GAEhD,kBAAC,GAAD,EAAQ,WAAU,eAAgB,CAAA;MAE7B,CAAA,CACL;;IACF;MAGN,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,KAAD;KAAG,WAAU;eAAoD,EAAK;KAAS,CAAA;IAC/E,kBAAC,KAAD;KAAG,WAAU;eAAwC;KAAa,CAAA;IAClE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAa,QAAQ,EAAK,QAAU,CAAA,EACnC,EAAK,QACJ,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAK;MACD,CAAA,CAEL;;IACL,EAAK,SACJ,kBAAC,KAAD;KAAG,WAAU;eAAiD,EAAK;KAAU,CAAA;IAE/E,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD,EAAA,UACG,EAAK,aAAa,aAAa,EAAmB,EAAK,WAAW,KAAK,gBACnE,CAAA,EACN,EAAK,eAAe,OAOjB,EAAK,iBAAiB,OAItB,OAHF,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACG,EAAY,EAAK,eAAe,EAAK,YAAY,MAAM,EAAC,QACpD;UATP,kBAAC,QAAD;MACE,OAAO,GAAG,EAAY,EAAK,iBAAiB,GAAG,EAAK,YAAY,MAAM,CAAC,KAAK,EAAK,UAAU;MAC3F,WAAU;gBAFZ,CAIG,EAAY,EAAK,aAAa,EAAK,YAAY,MAAM,EAAC,QAClD;QAML;;IACF;KACF"}
|
|
@@ -17,10 +17,15 @@ export { Comments } from './Comments';
|
|
|
17
17
|
export { FollowButton } from './FollowButton';
|
|
18
18
|
export { AddToLookbookButton } from './AddToLookbookButton';
|
|
19
19
|
export { LookCard } from './LookCard';
|
|
20
|
+
export { BrandCard } from './BrandCard';
|
|
20
21
|
export { BrandCover } from './BrandCover';
|
|
22
|
+
export { CollectionCard } from './CollectionCard';
|
|
23
|
+
export { LookbookCard } from './LookbookCard';
|
|
24
|
+
export { WardrobeItemCard } from './WardrobeItemCard';
|
|
21
25
|
export { StepTracker } from './StepTracker';
|
|
22
26
|
export { BrandCreatorAvatars } from './BrandCreatorAvatars';
|
|
23
27
|
export { ShopItemCard } from './ShopItemCard';
|
|
24
28
|
export { ColourSwatchRow } from './ColourSwatchRow';
|
|
25
29
|
export { VisibilityChip } from './VisibilityChip';
|
|
30
|
+
export { FeedbackWidget } from './FeedbackWidget';
|
|
26
31
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "./PageLayout.js";
|
|
2
|
+
import "./MetricsCard.js";
|
|
3
|
+
import "./StatusBadge.js";
|
|
4
|
+
import "./StateViews.js";
|
|
5
|
+
import "./FileImage.js";
|
|
6
|
+
import "./Thumbnail.js";
|
|
7
|
+
import "./AddToLookbookButton.js";
|
|
8
|
+
import "./LookCard.js";
|
|
9
|
+
import "./EmptyState.js";
|
|
10
|
+
import "./Field.js";
|
|
11
|
+
import "./ImageUploadField.js";
|
|
12
|
+
import "./TagList.js";
|
|
13
|
+
import "./ActorName.js";
|
|
14
|
+
import "./Comments.js";
|
|
15
|
+
import "./FollowButton.js";
|
|
16
|
+
import "./BrandCover.js";
|
|
17
|
+
import "./BrandCard.js";
|
|
18
|
+
import "./CollectionCard.js";
|
|
19
|
+
import "./VisibilityChip.js";
|
|
20
|
+
import "./LookbookCard.js";
|
|
21
|
+
import "./WardrobeItemCard.js";
|
|
22
|
+
import "./StepTracker.js";
|
|
23
|
+
import "./BrandCreatorAvatars.js";
|
|
24
|
+
import "./ShopItemCard.js";
|
|
25
|
+
import "./ColourSwatchRow.js";
|
|
26
|
+
import "./FeedbackWidget.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HomePage.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/pages/HomePage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"HomePage.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/pages/HomePage.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA4JH,wBAAgB,QAAQ,gCAmSvB"}
|
|
@@ -46,7 +46,8 @@ var I = [
|
|
|
46
46
|
description: "What you own",
|
|
47
47
|
icon: /* @__PURE__ */ j(S, { className: "h-5 w-5" }),
|
|
48
48
|
path: "/wardrobe",
|
|
49
|
-
color: "bg-status-warning-bg-subtle text-status-warning-text"
|
|
49
|
+
color: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
50
|
+
tourAttr: "coolandlovely-wardrobe-link"
|
|
50
51
|
}
|
|
51
52
|
], L = [
|
|
52
53
|
{
|
|
@@ -54,7 +55,8 @@ var I = [
|
|
|
54
55
|
description: "Storefronts with a publish lifecycle",
|
|
55
56
|
icon: /* @__PURE__ */ j(O, { className: "h-6 w-6" }),
|
|
56
57
|
path: "/brands",
|
|
57
|
-
color: "bg-accent-purple-subtle text-text-primary"
|
|
58
|
+
color: "bg-accent-purple-subtle text-text-primary",
|
|
59
|
+
tourAttr: "coolandlovely-brands-link"
|
|
58
60
|
},
|
|
59
61
|
{
|
|
60
62
|
label: "Collections",
|
|
@@ -103,14 +105,16 @@ var I = [
|
|
|
103
105
|
description: "Your taste, palette & sizing",
|
|
104
106
|
icon: /* @__PURE__ */ j(A, { className: "h-6 w-6" }),
|
|
105
107
|
path: "/profile",
|
|
106
|
-
color: "bg-accent-blue-subtle text-accent-blue"
|
|
108
|
+
color: "bg-accent-blue-subtle text-accent-blue",
|
|
109
|
+
tourAttr: "coolandlovely-style-profile-link"
|
|
107
110
|
},
|
|
108
111
|
{
|
|
109
112
|
label: "Discover",
|
|
110
113
|
description: "A feed tuned to your taste",
|
|
111
114
|
icon: /* @__PURE__ */ j(b, { className: "h-6 w-6" }),
|
|
112
115
|
path: "/discover",
|
|
113
|
-
color: "bg-[var(--color-accent-soft)] text-text-primary"
|
|
116
|
+
color: "bg-[var(--color-accent-soft)] text-text-primary",
|
|
117
|
+
tourAttr: "coolandlovely-discover-feed"
|
|
114
118
|
},
|
|
115
119
|
{
|
|
116
120
|
label: "Trends",
|
|
@@ -225,6 +229,7 @@ function R() {
|
|
|
225
229
|
children: I.map((e) => /* @__PURE__ */ M("button", {
|
|
226
230
|
type: "button",
|
|
227
231
|
onClick: () => y(e.path),
|
|
232
|
+
"data-tour": e.tourAttr,
|
|
228
233
|
className: t("flex items-start gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left shadow-sm transition-all", "hover:border-accent-foreground/20 hover:shadow-md"),
|
|
229
234
|
children: [
|
|
230
235
|
/* @__PURE__ */ j("div", {
|
|
@@ -256,6 +261,7 @@ function R() {
|
|
|
256
261
|
children: L.map((e) => /* @__PURE__ */ M("button", {
|
|
257
262
|
type: "button",
|
|
258
263
|
onClick: () => y(e.path),
|
|
264
|
+
"data-tour": e.tourAttr,
|
|
259
265
|
className: "flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-5 text-left transition-all hover:border-accent-foreground/20 hover:shadow-md",
|
|
260
266
|
children: [/* @__PURE__ */ j("div", {
|
|
261
267
|
className: t("flex h-12 w-12 items-center justify-center rounded-full", e.color),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/coolandlovely/pages/HomePage.tsx"],"sourcesContent":["/**\n * Overview / Home Page\n * @module coolandlovely/pages\n *\n * The CoolAndLovely command center landing page: a style-operating-system\n * dashboard rolling up live catalog (brands, items), social (looks,\n * collaborations) and the shopper's own wardrobe — all from real `coo*` ops.\n */\n\nimport { useMemo } from 'react';\nimport {\n Store,\n Shirt,\n Sparkles,\n Handshake,\n Shapes,\n Heart,\n ArrowRight,\n Star,\n Plus,\n BookHeart,\n UserCircle,\n Compass,\n TrendingUp,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { Thumbnail } from '../components/Thumbnail';\nimport { LookCard } from '../components/LookCard';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useBrands, useStyleItems, useLooks, useCollaborations, useWardrobeItems } from '../hooks';\nimport { cn } from '../utils/cn';\nimport { buildCreatedAtSparkline, sparklineDeltaLabel, sparklineTrendDirection } from '../utils';\nimport type { DashboardStats } from '../types';\n\n/** Time-of-day greeting for the dashboard title (local clock, honest — no data). */\nfunction greeting(): string {\n const hour = new Date().getHours();\n if (hour < 12) return 'Good morning';\n if (hour < 18) return 'Good afternoon';\n return 'Good evening';\n}\n\ninterface QuickAction {\n label: string;\n description: string;\n icon: React.ReactNode;\n path: string;\n color: string;\n}\n\nconst quickActions: QuickAction[] = [\n {\n label: 'New Brand',\n description: 'Launch a storefront',\n icon: <Store className=\"h-5 w-5\" />,\n path: '/brands/new',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Add Style Item',\n description: 'List a piece',\n icon: <Shirt className=\"h-5 w-5\" />,\n path: '/items/new',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Publish a Look',\n description: 'Shop-the-look outfit',\n icon: <Sparkles className=\"h-5 w-5\" />,\n path: '/looks/new',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'My Wardrobe',\n description: 'What you own',\n icon: <Heart className=\"h-5 w-5\" />,\n path: '/wardrobe',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n];\n\nconst modules = [\n {\n label: 'Brands',\n description: 'Storefronts with a publish lifecycle',\n icon: <Store className=\"h-6 w-6\" />,\n path: '/brands',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Collections',\n description: 'Seasonal drops & curated edits',\n icon: <Shapes className=\"h-6 w-6\" />,\n path: '/collections',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n label: 'Style Items',\n description: 'The shoppable catalogue',\n icon: <Shirt className=\"h-6 w-6\" />,\n path: '/items',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Looks',\n description: 'Published shop-the-look outfits',\n icon: <Sparkles className=\"h-6 w-6\" />,\n path: '/looks',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'Collaborations',\n description: 'Brand × creator partnerships',\n icon: <Handshake className=\"h-6 w-6\" />,\n path: '/collaborations',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n {\n label: 'Wardrobe',\n description: 'Your digital closet & wear log',\n icon: <Heart className=\"h-6 w-6\" />,\n path: '/wardrobe',\n color: 'bg-status-error-bg-subtle text-status-error-text',\n },\n {\n label: 'Lookbooks',\n description: 'Themed boards of saved pieces',\n icon: <BookHeart className=\"h-6 w-6\" />,\n path: '/lookbooks',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Style Profile',\n description: 'Your taste, palette & sizing',\n icon: <UserCircle className=\"h-6 w-6\" />,\n path: '/profile',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n label: 'Discover',\n description: 'A feed tuned to your taste',\n icon: <Compass className=\"h-6 w-6\" />,\n path: '/discover',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Trends',\n description: 'Live signals from platform activity',\n icon: <TrendingUp className=\"h-6 w-6\" />,\n path: '/trends',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n];\n\nexport function HomePage() {\n const { navigateTo, currentUser } = useCoolAndLovely();\n\n const brands = useBrands();\n const items = useStyleItems();\n const looks = useLooks();\n const collaborations = useCollaborations();\n const wardrobe = useWardrobeItems();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n const isLoading =\n brands.isLoading ||\n items.isLoading ||\n looks.isLoading ||\n collaborations.isLoading ||\n wardrobe.isLoading;\n\n const isError = brands.isError && items.isError && looks.isError && collaborations.isError;\n\n const stats = useMemo<DashboardStats>(() => {\n const brandItems = brands.data?.items ?? [];\n const styleItems = items.data?.items ?? [];\n const lookItems = looks.data?.items ?? [];\n const collabs = collaborations.data ?? [];\n const wardrobeItems = wardrobe.data ?? [];\n return {\n totalBrands: brandItems.length,\n publishedBrands: brandItems.filter((b) => b.status === 'PUBLISHED').length,\n totalStyleItems: styleItems.length,\n activeStyleItems: styleItems.filter((i) => i.status === 'ACTIVE').length,\n totalLooks: lookItems.length,\n featuredLooks: lookItems.filter((l) => l.isFeatured).length,\n activeCollaborations: collabs.filter(\n (c) => c.status === 'ACCEPTED' || c.status === 'IN_PROGRESS'\n ).length,\n wardrobeItems: wardrobeItems.length,\n };\n }, [brands.data, items.data, looks.data, collaborations.data, wardrobe.data]);\n\n const latestLooks = useMemo(() => [...(looks.data?.items ?? [])].slice(0, 5), [looks.data]);\n\n const recentBrands = useMemo(\n () =>\n [...(brands.data?.items ?? [])]\n .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n .slice(0, 4),\n [brands.data]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <LoadingView label=\"Loading your style command center…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <ErrorView\n title=\"Could not load CoolAndLovely\"\n message=\"The fashion service did not respond. Please try again.\"\n onRetry={() => {\n void brands.refetch();\n void items.refetch();\n void looks.refetch();\n void collaborations.refetch();\n void wardrobe.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n // Real (non-fabricated) 7-day sparkline series derived from each entity's\n // own createdAt — falls back to the plain text sub-label when there is no\n // meaningful history yet (e.g. everything was seeded at once).\n const brandSparkline = buildCreatedAtSparkline(brands.data?.items ?? []);\n const itemSparkline = buildCreatedAtSparkline(items.data?.items ?? []);\n const lookSparkline = buildCreatedAtSparkline(looks.data?.items ?? []);\n const collabSparkline = buildCreatedAtSparkline(collaborations.data ?? []);\n\n const metrics = [\n {\n icon: <Store className=\"h-5 w-5\" />,\n value: stats.totalBrands.toString(),\n label: 'Brands',\n sub: sparklineDeltaLabel(brandSparkline) ?? `${stats.publishedBrands} published`,\n sparklineData: brandSparkline,\n trend: sparklineTrendDirection(brandSparkline),\n },\n {\n icon: <Shirt className=\"h-5 w-5\" />,\n value: stats.totalStyleItems.toString(),\n label: 'Style Items',\n sub: sparklineDeltaLabel(itemSparkline) ?? `${stats.activeStyleItems} active`,\n sparklineData: itemSparkline,\n trend: sparklineTrendDirection(itemSparkline),\n },\n {\n icon: <Sparkles className=\"h-5 w-5\" />,\n value: stats.totalLooks.toString(),\n label: 'Looks',\n sub: sparklineDeltaLabel(lookSparkline) ?? `${stats.featuredLooks} featured`,\n sparklineData: lookSparkline,\n trend: sparklineTrendDirection(lookSparkline),\n },\n {\n icon: <Handshake className=\"h-5 w-5\" />,\n value: stats.activeCollaborations.toString(),\n label: 'Active Collabs',\n sub: sparklineDeltaLabel(collabSparkline) ?? `${stats.wardrobeItems} in wardrobe`,\n sparklineData: collabSparkline,\n trend: sparklineTrendDirection(collabSparkline),\n },\n ];\n\n return (\n <PageLayout\n title={`${greeting()}, ${firstName}`}\n description=\"Your fashion & lifestyle command center\"\n hero\n >\n <PagePurpose className=\"mb-6\">\n CoolAndLovely connects the three sides of style on one workspace-native model: brands launch\n storefronts and seasonal collections, you build a wardrobe and lookbooks, and creators\n publish shop-the-look outfits and run brand collaborations. Jump into any module below — all\n data is live from the CoolAndLovely service.\n </PagePurpose>\n\n {/* Headline metrics — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">At a glance</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {metrics.map((m, index) => (\n <MetricsCard\n key={m.label}\n icon={m.icon}\n value={m.value}\n label={m.label}\n trendValue={m.sub}\n trend={m.trend}\n sparklineData={m.sparklineData}\n featured={index === 0}\n />\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Quick Actions */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Quick actions</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {quickActions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={() => navigateTo(action.path)}\n className={cn(\n 'flex items-start gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left shadow-sm transition-all',\n 'hover:border-accent-foreground/20 hover:shadow-md'\n )}\n >\n <div\n className={cn(\n 'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',\n action.color\n )}\n >\n {action.icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">{action.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{action.description}</p>\n </div>\n <Plus className=\"mt-1 h-4 w-4 shrink-0 text-text-secondary\" />\n </button>\n ))}\n </div>\n </section>\n\n {/* Modules */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Explore</h2>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {modules.map((mod) => (\n <button\n key={mod.label}\n type=\"button\"\n onClick={() => navigateTo(mod.path)}\n className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-5 text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div\n className={cn('flex h-12 w-12 items-center justify-center rounded-full', mod.color)}\n >\n {mod.icon}\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{mod.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{mod.description}</p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {/* Latest looks */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Latest looks</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {latestLooks.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-6 text-sm text-text-secondary\">\n No looks published yet. Be the first to publish a shop-the-look outfit.\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5\">\n {latestLooks.map((look) => (\n <LookCard\n key={look.id}\n id={look.id}\n title={look.title}\n occasion={look.occasion}\n imageFileId={look.image?.id}\n likeCount={look.likeCount}\n isLikedByMe={look.isLikedByMe}\n isSavedByMe={look.isSavedByMe}\n onOpen={() => navigateTo(`/looks/${look.id}`)}\n showActions\n />\n ))}\n </div>\n )}\n </section>\n\n {/* Recent brands */}\n {recentBrands.length > 0 && (\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recently updated brands</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {recentBrands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"group flex items-center gap-3 rounded-xl border border-border-seam bg-bg-surface p-3 text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Store className=\"h-5 w-5\" />}\n seed={brand.id}\n fileId={brand.logo?.id ?? brand.cover?.id}\n alt={brand.name}\n className=\"h-11 w-11 shrink-0 rounded-full ring-2 ring-bg-surface\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{brand.name}</p>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n </button>\n ))}\n </div>\n </section>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/coolandlovely/pages/HomePage.tsx"],"sourcesContent":["/**\n * Overview / Home Page\n * @module coolandlovely/pages\n *\n * The CoolAndLovely command center landing page: a style-operating-system\n * dashboard rolling up live catalog (brands, items), social (looks,\n * collaborations) and the shopper's own wardrobe — all from real `coo*` ops.\n */\n\nimport { useMemo } from 'react';\nimport {\n Store,\n Shirt,\n Sparkles,\n Handshake,\n Shapes,\n Heart,\n ArrowRight,\n Star,\n Plus,\n BookHeart,\n UserCircle,\n Compass,\n TrendingUp,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { Thumbnail } from '../components/Thumbnail';\nimport { LookCard } from '../components/LookCard';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useBrands, useStyleItems, useLooks, useCollaborations, useWardrobeItems } from '../hooks';\nimport { cn } from '../utils/cn';\nimport { buildCreatedAtSparkline, sparklineDeltaLabel, sparklineTrendDirection } from '../utils';\nimport type { DashboardStats } from '../types';\n\n/** Time-of-day greeting for the dashboard title (local clock, honest — no data). */\nfunction greeting(): string {\n const hour = new Date().getHours();\n if (hour < 12) return 'Good morning';\n if (hour < 18) return 'Good afternoon';\n return 'Good evening';\n}\n\ninterface QuickAction {\n label: string;\n description: string;\n icon: React.ReactNode;\n path: string;\n color: string;\n tourAttr?: string;\n}\n\nconst quickActions: QuickAction[] = [\n {\n label: 'New Brand',\n description: 'Launch a storefront',\n icon: <Store className=\"h-5 w-5\" />,\n path: '/brands/new',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Add Style Item',\n description: 'List a piece',\n icon: <Shirt className=\"h-5 w-5\" />,\n path: '/items/new',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Publish a Look',\n description: 'Shop-the-look outfit',\n icon: <Sparkles className=\"h-5 w-5\" />,\n path: '/looks/new',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'My Wardrobe',\n description: 'What you own',\n icon: <Heart className=\"h-5 w-5\" />,\n path: '/wardrobe',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n tourAttr: 'coolandlovely-wardrobe-link',\n },\n];\n\nconst modules = [\n {\n label: 'Brands',\n description: 'Storefronts with a publish lifecycle',\n icon: <Store className=\"h-6 w-6\" />,\n path: '/brands',\n color: 'bg-accent-purple-subtle text-text-primary',\n tourAttr: 'coolandlovely-brands-link',\n },\n {\n label: 'Collections',\n description: 'Seasonal drops & curated edits',\n icon: <Shapes className=\"h-6 w-6\" />,\n path: '/collections',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n label: 'Style Items',\n description: 'The shoppable catalogue',\n icon: <Shirt className=\"h-6 w-6\" />,\n path: '/items',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Looks',\n description: 'Published shop-the-look outfits',\n icon: <Sparkles className=\"h-6 w-6\" />,\n path: '/looks',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'Collaborations',\n description: 'Brand × creator partnerships',\n icon: <Handshake className=\"h-6 w-6\" />,\n path: '/collaborations',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n {\n label: 'Wardrobe',\n description: 'Your digital closet & wear log',\n icon: <Heart className=\"h-6 w-6\" />,\n path: '/wardrobe',\n color: 'bg-status-error-bg-subtle text-status-error-text',\n },\n {\n label: 'Lookbooks',\n description: 'Themed boards of saved pieces',\n icon: <BookHeart className=\"h-6 w-6\" />,\n path: '/lookbooks',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Style Profile',\n description: 'Your taste, palette & sizing',\n icon: <UserCircle className=\"h-6 w-6\" />,\n path: '/profile',\n color: 'bg-accent-blue-subtle text-accent-blue',\n tourAttr: 'coolandlovely-style-profile-link',\n },\n {\n label: 'Discover',\n description: 'A feed tuned to your taste',\n icon: <Compass className=\"h-6 w-6\" />,\n path: '/discover',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n tourAttr: 'coolandlovely-discover-feed',\n },\n {\n label: 'Trends',\n description: 'Live signals from platform activity',\n icon: <TrendingUp className=\"h-6 w-6\" />,\n path: '/trends',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n];\n\nexport function HomePage() {\n const { navigateTo, currentUser } = useCoolAndLovely();\n\n const brands = useBrands();\n const items = useStyleItems();\n const looks = useLooks();\n const collaborations = useCollaborations();\n const wardrobe = useWardrobeItems();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n const isLoading =\n brands.isLoading ||\n items.isLoading ||\n looks.isLoading ||\n collaborations.isLoading ||\n wardrobe.isLoading;\n\n const isError = brands.isError && items.isError && looks.isError && collaborations.isError;\n\n const stats = useMemo<DashboardStats>(() => {\n const brandItems = brands.data?.items ?? [];\n const styleItems = items.data?.items ?? [];\n const lookItems = looks.data?.items ?? [];\n const collabs = collaborations.data ?? [];\n const wardrobeItems = wardrobe.data ?? [];\n return {\n totalBrands: brandItems.length,\n publishedBrands: brandItems.filter((b) => b.status === 'PUBLISHED').length,\n totalStyleItems: styleItems.length,\n activeStyleItems: styleItems.filter((i) => i.status === 'ACTIVE').length,\n totalLooks: lookItems.length,\n featuredLooks: lookItems.filter((l) => l.isFeatured).length,\n activeCollaborations: collabs.filter(\n (c) => c.status === 'ACCEPTED' || c.status === 'IN_PROGRESS'\n ).length,\n wardrobeItems: wardrobeItems.length,\n };\n }, [brands.data, items.data, looks.data, collaborations.data, wardrobe.data]);\n\n const latestLooks = useMemo(() => [...(looks.data?.items ?? [])].slice(0, 5), [looks.data]);\n\n const recentBrands = useMemo(\n () =>\n [...(brands.data?.items ?? [])]\n .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n .slice(0, 4),\n [brands.data]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <LoadingView label=\"Loading your style command center…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <ErrorView\n title=\"Could not load CoolAndLovely\"\n message=\"The fashion service did not respond. Please try again.\"\n onRetry={() => {\n void brands.refetch();\n void items.refetch();\n void looks.refetch();\n void collaborations.refetch();\n void wardrobe.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n // Real (non-fabricated) 7-day sparkline series derived from each entity's\n // own createdAt — falls back to the plain text sub-label when there is no\n // meaningful history yet (e.g. everything was seeded at once).\n const brandSparkline = buildCreatedAtSparkline(brands.data?.items ?? []);\n const itemSparkline = buildCreatedAtSparkline(items.data?.items ?? []);\n const lookSparkline = buildCreatedAtSparkline(looks.data?.items ?? []);\n const collabSparkline = buildCreatedAtSparkline(collaborations.data ?? []);\n\n const metrics = [\n {\n icon: <Store className=\"h-5 w-5\" />,\n value: stats.totalBrands.toString(),\n label: 'Brands',\n sub: sparklineDeltaLabel(brandSparkline) ?? `${stats.publishedBrands} published`,\n sparklineData: brandSparkline,\n trend: sparklineTrendDirection(brandSparkline),\n },\n {\n icon: <Shirt className=\"h-5 w-5\" />,\n value: stats.totalStyleItems.toString(),\n label: 'Style Items',\n sub: sparklineDeltaLabel(itemSparkline) ?? `${stats.activeStyleItems} active`,\n sparklineData: itemSparkline,\n trend: sparklineTrendDirection(itemSparkline),\n },\n {\n icon: <Sparkles className=\"h-5 w-5\" />,\n value: stats.totalLooks.toString(),\n label: 'Looks',\n sub: sparklineDeltaLabel(lookSparkline) ?? `${stats.featuredLooks} featured`,\n sparklineData: lookSparkline,\n trend: sparklineTrendDirection(lookSparkline),\n },\n {\n icon: <Handshake className=\"h-5 w-5\" />,\n value: stats.activeCollaborations.toString(),\n label: 'Active Collabs',\n sub: sparklineDeltaLabel(collabSparkline) ?? `${stats.wardrobeItems} in wardrobe`,\n sparklineData: collabSparkline,\n trend: sparklineTrendDirection(collabSparkline),\n },\n ];\n\n return (\n <PageLayout\n title={`${greeting()}, ${firstName}`}\n description=\"Your fashion & lifestyle command center\"\n hero\n >\n <PagePurpose className=\"mb-6\">\n CoolAndLovely connects the three sides of style on one workspace-native model: brands launch\n storefronts and seasonal collections, you build a wardrobe and lookbooks, and creators\n publish shop-the-look outfits and run brand collaborations. Jump into any module below — all\n data is live from the CoolAndLovely service.\n </PagePurpose>\n\n {/* Headline metrics — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">At a glance</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {metrics.map((m, index) => (\n <MetricsCard\n key={m.label}\n icon={m.icon}\n value={m.value}\n label={m.label}\n trendValue={m.sub}\n trend={m.trend}\n sparklineData={m.sparklineData}\n featured={index === 0}\n />\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Quick Actions */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Quick actions</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {quickActions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={() => navigateTo(action.path)}\n data-tour={action.tourAttr}\n className={cn(\n 'flex items-start gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left shadow-sm transition-all',\n 'hover:border-accent-foreground/20 hover:shadow-md'\n )}\n >\n <div\n className={cn(\n 'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',\n action.color\n )}\n >\n {action.icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">{action.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{action.description}</p>\n </div>\n <Plus className=\"mt-1 h-4 w-4 shrink-0 text-text-secondary\" />\n </button>\n ))}\n </div>\n </section>\n\n {/* Modules */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Explore</h2>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {modules.map((mod) => (\n <button\n key={mod.label}\n type=\"button\"\n onClick={() => navigateTo(mod.path)}\n data-tour={mod.tourAttr}\n className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-5 text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div\n className={cn('flex h-12 w-12 items-center justify-center rounded-full', mod.color)}\n >\n {mod.icon}\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{mod.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{mod.description}</p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {/* Latest looks */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Latest looks</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {latestLooks.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-6 text-sm text-text-secondary\">\n No looks published yet. Be the first to publish a shop-the-look outfit.\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5\">\n {latestLooks.map((look) => (\n <LookCard\n key={look.id}\n id={look.id}\n title={look.title}\n occasion={look.occasion}\n imageFileId={look.image?.id}\n likeCount={look.likeCount}\n isLikedByMe={look.isLikedByMe}\n isSavedByMe={look.isSavedByMe}\n onOpen={() => navigateTo(`/looks/${look.id}`)}\n showActions\n />\n ))}\n </div>\n )}\n </section>\n\n {/* Recent brands */}\n {recentBrands.length > 0 && (\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recently updated brands</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {recentBrands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"group flex items-center gap-3 rounded-xl border border-border-seam bg-bg-surface p-3 text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Store className=\"h-5 w-5\" />}\n seed={brand.id}\n fileId={brand.logo?.id ?? brand.cover?.id}\n alt={brand.name}\n className=\"h-11 w-11 shrink-0 rounded-full ring-2 ring-bg-surface\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-1.5\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{brand.name}</p>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n </button>\n ))}\n </div>\n </section>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -8,9 +8,9 @@ import { useBrand as s, useTransitionBrand as c, useUpdateBrand as l } from "../
|
|
|
8
8
|
import { formatDate as u, formatPrice as d, titleCase as f } from "../../utils/formatters.js";
|
|
9
9
|
import "../../hooks/index.js";
|
|
10
10
|
import { categoryIcon as p } from "../../utils/icons.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { TagList as m } from "../../components/TagList.js";
|
|
12
|
+
import { Comments as h } from "../../components/Comments.js";
|
|
13
|
+
import { BrandCover as g } from "../../components/BrandCover.js";
|
|
14
14
|
import { ArrowLeft as _, ExternalLink as v, Loader2 as y, Pencil as b, Plus as x, Shapes as S, Shirt as C, Store as w } from "lucide-react";
|
|
15
15
|
import { useParams as T } from "react-router";
|
|
16
16
|
import { Fragment as E, jsx as D, jsxs as O } from "react/jsx-runtime";
|
|
@@ -50,7 +50,7 @@ function j() {
|
|
|
50
50
|
children: [/* @__PURE__ */ O("div", {
|
|
51
51
|
className: "space-y-6 lg:col-span-2",
|
|
52
52
|
children: [
|
|
53
|
-
/* @__PURE__ */ D(
|
|
53
|
+
/* @__PURE__ */ D(g, {
|
|
54
54
|
coverFileId: I.cover?.id,
|
|
55
55
|
logoFileId: I.logo?.id,
|
|
56
56
|
name: I.name,
|
|
@@ -100,7 +100,7 @@ function j() {
|
|
|
100
100
|
children: [
|
|
101
101
|
/* @__PURE__ */ O("div", {
|
|
102
102
|
className: "flex flex-wrap items-center justify-between gap-3",
|
|
103
|
-
children: [I.aesthetics.length > 0 ? /* @__PURE__ */ D(
|
|
103
|
+
children: [I.aesthetics.length > 0 ? /* @__PURE__ */ D(m, {
|
|
104
104
|
tags: I.aesthetics.map(f),
|
|
105
105
|
tone: "accent",
|
|
106
106
|
max: 12
|
|
@@ -110,7 +110,7 @@ function j() {
|
|
|
110
110
|
className: "text-sm text-text-secondary",
|
|
111
111
|
children: I.bio
|
|
112
112
|
}),
|
|
113
|
-
I.tags.length > 0 && /* @__PURE__ */ D(
|
|
113
|
+
I.tags.length > 0 && /* @__PURE__ */ D(m, {
|
|
114
114
|
tags: I.tags,
|
|
115
115
|
max: 12
|
|
116
116
|
}),
|
|
@@ -336,7 +336,7 @@ function j() {
|
|
|
336
336
|
]
|
|
337
337
|
})]
|
|
338
338
|
}),
|
|
339
|
-
/* @__PURE__ */ D(
|
|
339
|
+
/* @__PURE__ */ D(h, {
|
|
340
340
|
entityType: "CooBrand",
|
|
341
341
|
entityId: I.id,
|
|
342
342
|
className: "mt-0"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrandsListPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/brands/BrandsListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"BrandsListPage.d.ts","sourceRoot":"","sources":["../../../../src/coolandlovely/pages/brands/BrandsListPage.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAwBH,wBAAgB,cAAc,gCA6H7B"}
|