@burdenoff/microfe-coolandlovely 2026.820.5 → 2026.829.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/coolandlovely/components/ActivityTrail.d.ts +24 -0
- package/dist/coolandlovely/components/ActivityTrail.d.ts.map +1 -0
- package/dist/coolandlovely/components/ActivityTrail.js +70 -0
- package/dist/coolandlovely/components/ActivityTrail.js.map +1 -0
- package/dist/coolandlovely/components/Comments.js +4 -4
- package/dist/coolandlovely/components/FeedbackWidget.d.ts.map +1 -1
- package/dist/coolandlovely/components/FeedbackWidget.js +72 -70
- package/dist/coolandlovely/components/FeedbackWidget.js.map +1 -1
- package/dist/coolandlovely/components/LookCard.js +24 -24
- package/dist/coolandlovely/components/StateViews.d.ts.map +1 -1
- package/dist/coolandlovely/components/StateViews.js +35 -26
- package/dist/coolandlovely/components/StateViews.js.map +1 -1
- package/dist/coolandlovely/components/StatusBadge.d.ts.map +1 -1
- package/dist/coolandlovely/components/StatusBadge.js +45 -15
- package/dist/coolandlovely/components/StatusBadge.js.map +1 -1
- package/dist/coolandlovely/components/VisibilityChip.d.ts.map +1 -1
- package/dist/coolandlovely/components/VisibilityChip.js +22 -17
- package/dist/coolandlovely/components/VisibilityChip.js.map +1 -1
- package/dist/coolandlovely/components/index.d.ts +1 -0
- package/dist/coolandlovely/components/index.d.ts.map +1 -1
- package/dist/coolandlovely/components/index.js +1 -0
- package/dist/coolandlovely/hooks/index.d.ts +4 -0
- package/dist/coolandlovely/hooks/index.d.ts.map +1 -1
- package/dist/coolandlovely/hooks/index.js +4 -1
- package/dist/coolandlovely/hooks/useActivity.d.ts +39 -0
- package/dist/coolandlovely/hooks/useActivity.d.ts.map +1 -0
- package/dist/coolandlovely/hooks/useActivity.js +38 -0
- package/dist/coolandlovely/hooks/useActivity.js.map +1 -0
- package/dist/coolandlovely/hooks/useAssistantEntity.d.ts +24 -0
- package/dist/coolandlovely/hooks/useAssistantEntity.d.ts.map +1 -0
- package/dist/coolandlovely/hooks/useAssistantEntity.js +12 -0
- package/dist/coolandlovely/hooks/useAssistantEntity.js.map +1 -0
- package/dist/coolandlovely/hooks/useTr.d.ts +2 -0
- package/dist/coolandlovely/hooks/useTr.d.ts.map +1 -0
- package/dist/coolandlovely/hooks/useTr.js +14 -0
- package/dist/coolandlovely/hooks/useTr.js.map +1 -0
- package/dist/coolandlovely/pages/HomePage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/HomePage.js +172 -132
- package/dist/coolandlovely/pages/HomePage.js.map +1 -1
- package/dist/coolandlovely/pages/admin/AdminBrandsPage.js +10 -10
- package/dist/coolandlovely/pages/admin/AdminCollaborationsPage.js +16 -16
- package/dist/coolandlovely/pages/admin/AdminFeaturedItemsPage.js +17 -17
- package/dist/coolandlovely/pages/brands/BrandDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js +171 -151
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandFormPage.js +9 -9
- package/dist/coolandlovely/pages/brands/BrandsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandsListPage.js +80 -76
- package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +121 -120
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +7 -7
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +82 -81
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +111 -110
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionFormPage.js +9 -9
- package/dist/coolandlovely/pages/collections/CollectionsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js +54 -55
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/discover/DiscoverPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/discover/DiscoverPage.js +452 -307
- package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +117 -116
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js +10 -10
- package/dist/coolandlovely/pages/items/StyleItemsListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js +105 -95
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +32 -29
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +88 -79
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookDetailPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/looks/LookDetailPage.js +135 -134
- package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookFormPage.js +6 -6
- package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LooksListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/looks/LooksListPage.js +94 -94
- package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js +226 -199
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
- package/dist/coolandlovely/pages/trends/TrendsPage.js +8 -8
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +5 -5
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.d.ts.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +71 -69
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
- package/dist/utils/pageContext.d.ts +23 -0
- package/dist/utils/pageContext.d.ts.map +1 -1
- package/dist/utils/pageContext.js +37 -25
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
interface ActivityTrailProps {
|
|
4
|
+
/** ActivityLog.resourceType written by the svc, e.g. "coolandlovely-brand". */
|
|
5
|
+
resourceType: string;
|
|
6
|
+
/** The record id (the panel stays idle until it is present). */
|
|
7
|
+
resourceId?: string;
|
|
8
|
+
/** Section heading (default "Audit trail"). */
|
|
9
|
+
title?: string;
|
|
10
|
+
/** Max entries to request (default 20). */
|
|
11
|
+
limit?: number;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `coolandlovely.brand_transitioned` → "Brand transitioned".
|
|
16
|
+
*
|
|
17
|
+
* Only reshapes the snake_case event names the audit plugin emits; anything
|
|
18
|
+
* else (the generic `graphql_mutation` fallback, which carries the raw field
|
|
19
|
+
* name) is shown verbatim rather than mangled by a lowercase pass.
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatActivityAction(action: string): string;
|
|
22
|
+
export declare const ActivityTrail: FC<ActivityTrailProps>;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=ActivityTrail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActivityTrail.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/ActivityTrail.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAQhC,UAAU,kBAAkB;IAC1B,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;IACrB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAkEhD,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { useEntityActivity as t } from "../hooks/useActivity.js";
|
|
3
|
+
import { formatDate as n, formatRelativeTime as r, titleCase as i } from "../utils/formatters.js";
|
|
4
|
+
import { ActorName as a } from "./ActorName.js";
|
|
5
|
+
import { History as o, Loader2 as s, RotateCw as c } from "lucide-react";
|
|
6
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
7
|
+
import { GlassCard as d } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
//#region src/coolandlovely/components/ActivityTrail.tsx
|
|
9
|
+
function f(e) {
|
|
10
|
+
let t = e.includes(".") ? e.slice(e.lastIndexOf(".") + 1) : e;
|
|
11
|
+
if (!/^[a-z0-9_]+$/.test(t)) return e;
|
|
12
|
+
let n = i(t);
|
|
13
|
+
return n.charAt(0) + n.slice(1).toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
var p = ({ resourceType: i, resourceId: p, title: m = "Audit trail", limit: h = 20, className: g }) => {
|
|
16
|
+
let _ = t(i, p, h), v = _.data ?? [];
|
|
17
|
+
return /* @__PURE__ */ u(d, {
|
|
18
|
+
treatment: "plain",
|
|
19
|
+
className: e("p-5", g),
|
|
20
|
+
children: [/* @__PURE__ */ u("h3", {
|
|
21
|
+
className: "mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary",
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ l(o, { className: "h-4 w-4" }),
|
|
24
|
+
" ",
|
|
25
|
+
m
|
|
26
|
+
]
|
|
27
|
+
}), _.isError ? /* @__PURE__ */ u("div", {
|
|
28
|
+
className: "space-y-2",
|
|
29
|
+
children: [/* @__PURE__ */ l("p", {
|
|
30
|
+
className: "text-sm text-text-secondary",
|
|
31
|
+
children: "Activity could not be loaded for this record."
|
|
32
|
+
}), /* @__PURE__ */ u("button", {
|
|
33
|
+
type: "button",
|
|
34
|
+
onClick: () => void _.refetch(),
|
|
35
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken",
|
|
36
|
+
children: [/* @__PURE__ */ l(c, { className: "h-3.5 w-3.5" }), " Retry"]
|
|
37
|
+
})]
|
|
38
|
+
}) : _.isFetched ? v.length === 0 ? /* @__PURE__ */ l("p", {
|
|
39
|
+
className: "text-sm text-text-secondary",
|
|
40
|
+
children: "No changes recorded yet. Edits, lifecycle transitions and archival are logged here as they happen."
|
|
41
|
+
}) : /* @__PURE__ */ l("ul", {
|
|
42
|
+
className: "divide-y divide-border-seam",
|
|
43
|
+
children: v.map((e) => /* @__PURE__ */ u("li", {
|
|
44
|
+
className: "flex items-start justify-between gap-3 py-2.5 first:pt-0",
|
|
45
|
+
children: [/* @__PURE__ */ u("div", {
|
|
46
|
+
className: "min-w-0",
|
|
47
|
+
children: [/* @__PURE__ */ l("p", {
|
|
48
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
49
|
+
children: f(e.action)
|
|
50
|
+
}), /* @__PURE__ */ l("p", {
|
|
51
|
+
className: "mt-0.5 truncate text-xs text-text-secondary",
|
|
52
|
+
children: /* @__PURE__ */ l(a, { id: e.userId })
|
|
53
|
+
})]
|
|
54
|
+
}), /* @__PURE__ */ l("time", {
|
|
55
|
+
dateTime: e.timestamp,
|
|
56
|
+
title: n(e.timestamp),
|
|
57
|
+
className: "shrink-0 whitespace-nowrap text-xs text-text-secondary",
|
|
58
|
+
children: r(e.timestamp)
|
|
59
|
+
})]
|
|
60
|
+
}, e.id))
|
|
61
|
+
}) : /* @__PURE__ */ u("p", {
|
|
62
|
+
className: "flex items-center gap-2 text-sm text-text-secondary",
|
|
63
|
+
children: [/* @__PURE__ */ l(s, { className: "h-4 w-4 animate-spin" }), " Loading activity…"]
|
|
64
|
+
})]
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
//#endregion
|
|
68
|
+
export { p as ActivityTrail };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=ActivityTrail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActivityTrail.js","names":[],"sources":["../../../src/coolandlovely/components/ActivityTrail.tsx"],"sourcesContent":["/**\n * ActivityTrail — resource-scoped audit trail for a CoolAndLovely record.\n * @module coolandlovely/components\n *\n * Reads the platform `ActivityLog` rows that the svc's mutation-audit plugin\n * already writes for every `coo*` mutation (see `hooks/useActivity.ts`). Who\n * changed what, when — resolved to a real display name via `ActorName`.\n *\n * No fabricated history: a record with no rows shows an empty state, and a\n * caller without the workspace `activity-log:read` permission sees the error\n * instead of a convincing-looking blank timeline.\n */\n\nimport type { FC } from 'react';\nimport { History, Loader2, RotateCw } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { ActorName } from './ActorName';\nimport { useEntityActivity } from '../hooks/useActivity';\nimport { formatDate, formatRelativeTime, titleCase } from '../utils/formatters';\nimport { cn } from '../utils/cn';\n\ninterface ActivityTrailProps {\n /** ActivityLog.resourceType written by the svc, e.g. \"coolandlovely-brand\". */\n resourceType: string;\n /** The record id (the panel stays idle until it is present). */\n resourceId?: string;\n /** Section heading (default \"Audit trail\"). */\n title?: string;\n /** Max entries to request (default 20). */\n limit?: number;\n className?: string;\n}\n\n/**\n * `coolandlovely.brand_transitioned` → \"Brand transitioned\".\n *\n * Only reshapes the snake_case event names the audit plugin emits; anything\n * else (the generic `graphql_mutation` fallback, which carries the raw field\n * name) is shown verbatim rather than mangled by a lowercase pass.\n */\nexport function formatActivityAction(action: string): string {\n const tail = action.includes('.') ? action.slice(action.lastIndexOf('.') + 1) : action;\n if (!/^[a-z0-9_]+$/.test(tail)) return action;\n const label = titleCase(tail);\n return label.charAt(0) + label.slice(1).toLowerCase();\n}\n\nexport const ActivityTrail: FC<ActivityTrailProps> = ({\n resourceType,\n resourceId,\n title = 'Audit trail',\n limit = 20,\n className,\n}) => {\n const query = useEntityActivity(resourceType, resourceId, limit);\n const entries = query.data ?? [];\n\n return (\n <GlassCard treatment=\"plain\" className={cn('p-5', className)}>\n <h3 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <History className=\"h-4 w-4\" /> {title}\n </h3>\n\n {query.isError ? (\n <div className=\"space-y-2\">\n <p className=\"text-sm text-text-secondary\">\n Activity could not be loaded for this record.\n </p>\n <button\n type=\"button\"\n onClick={() => void query.refetch()}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-1.5 text-xs font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <RotateCw className=\"h-3.5 w-3.5\" /> Retry\n </button>\n </div>\n ) : !query.isFetched ? (\n // Deliberately NOT `isLoading`: a query held back (no record id, no\n // workspace) has never run, and \"no changes recorded\" would be a claim\n // about data we never asked for.\n <p className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> Loading activity…\n </p>\n ) : entries.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n No changes recorded yet. Edits, lifecycle transitions and archival are logged here as they\n happen.\n </p>\n ) : (\n <ul className=\"divide-y divide-border-seam\">\n {entries.map((entry) => (\n <li key={entry.id} className=\"flex items-start justify-between gap-3 py-2.5 first:pt-0\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {formatActivityAction(entry.action)}\n </p>\n <p className=\"mt-0.5 truncate text-xs text-text-secondary\">\n <ActorName id={entry.userId} />\n </p>\n </div>\n <time\n dateTime={entry.timestamp}\n title={formatDate(entry.timestamp)}\n className=\"shrink-0 whitespace-nowrap text-xs text-text-secondary\"\n >\n {formatRelativeTime(entry.timestamp)}\n </time>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n );\n};\n"],"mappings":";;;;;;;;AAwCA,SAAgB,EAAqB,GAAwB;CAC3D,IAAM,IAAO,EAAO,SAAS,IAAI,GAAG,EAAO,MAAM,EAAO,YAAY,IAAI,GAAG,EAAE,GAAG;AAChF,KAAI,CAAC,eAAe,KAAK,EAAK,CAAE,QAAO;CACvC,IAAM,IAAQ,EAAU,EAAK;AAC7B,QAAO,EAAM,OAAO,EAAE,GAAG,EAAM,MAAM,EAAE,CAAC,aAAa;;AAGvD,IAAa,KAAyC,EACpD,iBACA,eACA,WAAQ,eACR,WAAQ,IACR,mBACI;CACJ,IAAM,IAAQ,EAAkB,GAAc,GAAY,EAAM,EAC1D,IAAU,EAAM,QAAQ,EAAE;AAEhC,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAW,EAAG,OAAO,EAAU;YAA5D,CACE,kBAAC,MAAD;GAAI,WAAU;aAAd;IACE,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA;;IAAE;IAC9B;MAEJ,EAAM,UACL,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAEvC,CAAA,EACJ,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,KAAK,EAAM,SAAS;IACnC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EAAA,SAC7B;MACL;OACH,EAAM,YAOP,EAAQ,WAAW,IACrB,kBAAC,KAAD;GAAG,WAAU;aAA8B;GAGvC,CAAA,GAEJ,kBAAC,MAAD;GAAI,WAAU;aACX,EAAQ,KAAK,MACZ,kBAAC,MAAD;IAAmB,WAAU;cAA7B,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAqB,EAAM,OAAO;MACjC,CAAA,EACJ,kBAAC,KAAD;MAAG,WAAU;gBACX,kBAAC,GAAD,EAAW,IAAI,EAAM,QAAU,CAAA;MAC7B,CAAA,CACA;QACN,kBAAC,QAAD;KACE,UAAU,EAAM;KAChB,OAAO,EAAW,EAAM,UAAU;KAClC,WAAU;eAET,EAAmB,EAAM,UAAU;KAC/B,CAAA,CACJ;MAhBI,EAAM,GAgBV,CACL;GACC,CAAA,GA7BL,kBAAC,KAAD;GAAG,WAAU;aAAb,CACE,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,EAAA,qBAC1C;KA6BI"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn as e } from "../utils/cn.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useAddComment as t, useEntityComments as n } from "../hooks/useComments.js";
|
|
3
|
+
import { formatRelativeTime as r } from "../utils/formatters.js";
|
|
4
4
|
import { ActorName as i } from "./ActorName.js";
|
|
5
5
|
import { useState as a } from "react";
|
|
6
6
|
import { Loader2 as o, MessageSquare as s, Send as c } from "lucide-react";
|
|
@@ -8,7 +8,7 @@ import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
|
8
8
|
import { GlassCard as d } from "@burdenoff/fe-libs/ui";
|
|
9
9
|
//#region src/coolandlovely/components/Comments.tsx
|
|
10
10
|
var f = ({ entityType: f, entityId: p, title: m = "Reviews & feedback", className: h }) => {
|
|
11
|
-
let g =
|
|
11
|
+
let g = n(f, p), _ = t(), [v, y] = a(""), b = g.data?.comments ?? [], x = g.data?.total ?? b.length;
|
|
12
12
|
return /* @__PURE__ */ u("section", {
|
|
13
13
|
className: e("mt-6", h),
|
|
14
14
|
children: [
|
|
@@ -91,7 +91,7 @@ var f = ({ entityType: f, entityId: p, title: m = "Reviews & feedback", classNam
|
|
|
91
91
|
className: "truncate text-sm font-medium text-text-primary"
|
|
92
92
|
}), /* @__PURE__ */ u("span", {
|
|
93
93
|
className: "shrink-0 text-xs text-text-secondary",
|
|
94
|
-
children: [
|
|
94
|
+
children: [r(e.createdAt), e.edited ? " · edited" : ""]
|
|
95
95
|
})]
|
|
96
96
|
}), /* @__PURE__ */ l("p", {
|
|
97
97
|
className: "mt-1 whitespace-pre-wrap break-words text-sm text-text-secondary",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeedbackWidget.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/FeedbackWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"FeedbackWidget.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/FeedbackWidget.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA8ChC,eAAO,MAAM,cAAc,EAAE,EA6K5B,CAAC"}
|
|
@@ -1,127 +1,129 @@
|
|
|
1
1
|
import { cn as e } from "../utils/cn.js";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { useTr as t } from "../hooks/useTr.js";
|
|
3
|
+
import "../hooks/index.js";
|
|
4
|
+
import { TextAreaField as n } from "./Field.js";
|
|
5
|
+
import { useCallback as r, useEffect as i, useState as a } from "react";
|
|
6
|
+
import { MessageSquarePlus as o, Send as s, X as c } from "lucide-react";
|
|
7
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
8
|
+
import { GlassCard as d } from "@burdenoff/fe-libs/ui";
|
|
7
9
|
//#region src/coolandlovely/components/FeedbackWidget.tsx
|
|
8
|
-
var
|
|
9
|
-
function
|
|
10
|
+
var f = "coolandlovely-feedback-submitted-at", p = 10080 * 60 * 1e3;
|
|
11
|
+
function m() {
|
|
10
12
|
if (typeof window > "u") return !1;
|
|
11
|
-
let e = window.localStorage.getItem(
|
|
13
|
+
let e = window.localStorage.getItem(f);
|
|
12
14
|
if (!e) return !1;
|
|
13
15
|
try {
|
|
14
16
|
let t = parseInt(e, 10);
|
|
15
|
-
return Date.now() - t <
|
|
17
|
+
return Date.now() - t < p;
|
|
16
18
|
} catch {
|
|
17
19
|
return !1;
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
|
-
function
|
|
21
|
-
typeof window > "u" || window.localStorage.setItem(
|
|
22
|
+
function h() {
|
|
23
|
+
typeof window > "u" || window.localStorage.setItem(f, String(Date.now()));
|
|
22
24
|
}
|
|
23
|
-
function
|
|
25
|
+
function g(e) {
|
|
24
26
|
typeof window > "u" || window.dispatchEvent(new CustomEvent("coolandlovely:feedback_submitted", {
|
|
25
27
|
detail: e,
|
|
26
28
|
bubbles: !0
|
|
27
29
|
}));
|
|
28
30
|
}
|
|
29
|
-
var
|
|
30
|
-
let [
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
var _ = () => {
|
|
32
|
+
let f = t(), [p, _] = a(!1), [v, y] = a(null), [b, x] = a(""), [S, C] = a(!1), [w, T] = a(!1);
|
|
33
|
+
i(() => {
|
|
34
|
+
T(m());
|
|
33
35
|
}, []);
|
|
34
|
-
let
|
|
35
|
-
|
|
36
|
-
}, []),
|
|
37
|
-
|
|
38
|
-
}, []),
|
|
39
|
-
|
|
40
|
-
score:
|
|
41
|
-
comment:
|
|
36
|
+
let E = r(() => {
|
|
37
|
+
_((e) => !e);
|
|
38
|
+
}, []), D = r(() => {
|
|
39
|
+
_(!1);
|
|
40
|
+
}, []), O = r(() => {
|
|
41
|
+
v !== null && (g({
|
|
42
|
+
score: v,
|
|
43
|
+
comment: b.trim(),
|
|
42
44
|
path: typeof window < "u" ? window.location.pathname : "",
|
|
43
45
|
submittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
44
|
-
}),
|
|
45
|
-
|
|
46
|
+
}), h(), C(!0), T(!0), window.setTimeout(() => {
|
|
47
|
+
_(!1), y(null), x(""), C(!1);
|
|
46
48
|
}, 2500));
|
|
47
|
-
}, [
|
|
48
|
-
return
|
|
49
|
+
}, [v, b]);
|
|
50
|
+
return w && !p ? null : /* @__PURE__ */ u("div", {
|
|
49
51
|
className: "fixed bottom-4 right-4 z-50 flex flex-col items-end gap-3",
|
|
50
|
-
children: [
|
|
52
|
+
children: [p && /* @__PURE__ */ u(d, {
|
|
51
53
|
className: "w-[min(92vw,360px)] overflow-hidden p-0 shadow-xl",
|
|
52
|
-
children: [/* @__PURE__ */
|
|
54
|
+
children: [/* @__PURE__ */ u("div", {
|
|
53
55
|
className: "flex items-center justify-between border-b border-border-seam px-4 py-3",
|
|
54
|
-
children: [/* @__PURE__ */
|
|
56
|
+
children: [/* @__PURE__ */ l("h2", {
|
|
55
57
|
className: "text-sm font-semibold text-text-primary",
|
|
56
|
-
children: "How is CoolAndLovely working for you?"
|
|
57
|
-
}), /* @__PURE__ */
|
|
58
|
+
children: f("coolandlovely.feedback.title", "How is CoolAndLovely working for you?")
|
|
59
|
+
}), /* @__PURE__ */ l("button", {
|
|
58
60
|
type: "button",
|
|
59
|
-
onClick:
|
|
60
|
-
"aria-label": "Close feedback",
|
|
61
|
+
onClick: D,
|
|
62
|
+
"aria-label": f("coolandlovely.feedback.close", "Close feedback"),
|
|
61
63
|
className: "rounded-md p-1 text-text-secondary transition-colors hover:bg-bg-surface hover:text-text-primary",
|
|
62
|
-
children: /* @__PURE__ */ c
|
|
64
|
+
children: /* @__PURE__ */ l(c, {
|
|
63
65
|
className: "h-4 w-4",
|
|
64
66
|
"aria-hidden": "true"
|
|
65
67
|
})
|
|
66
68
|
})]
|
|
67
|
-
}), /* @__PURE__ */
|
|
69
|
+
}), /* @__PURE__ */ l("div", {
|
|
68
70
|
className: "p-4",
|
|
69
|
-
children:
|
|
71
|
+
children: S ? /* @__PURE__ */ u("div", {
|
|
70
72
|
className: "py-6 text-center",
|
|
71
|
-
children: [/* @__PURE__ */
|
|
73
|
+
children: [/* @__PURE__ */ l("p", {
|
|
72
74
|
className: "text-sm font-medium text-status-success-text",
|
|
73
|
-
children: "Thank you for your feedback!"
|
|
74
|
-
}), /* @__PURE__ */
|
|
75
|
+
children: f("coolandlovely.feedback.thankYou", "Thank you for your feedback!")
|
|
76
|
+
}), /* @__PURE__ */ l("p", {
|
|
75
77
|
className: "mt-1 text-xs text-text-secondary",
|
|
76
|
-
children: "Your input helps us improve CoolAndLovely."
|
|
78
|
+
children: f("coolandlovely.feedback.helpText", "Your input helps us improve CoolAndLovely.")
|
|
77
79
|
})]
|
|
78
|
-
}) : /* @__PURE__ */
|
|
80
|
+
}) : /* @__PURE__ */ u("div", {
|
|
79
81
|
className: "space-y-4",
|
|
80
82
|
children: [
|
|
81
|
-
/* @__PURE__ */
|
|
83
|
+
/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
|
|
82
84
|
className: "mb-2 text-xs font-medium text-text-secondary",
|
|
83
|
-
children: "Rate your experience (0 = not likely, 10 = very likely)"
|
|
84
|
-
}), /* @__PURE__ */
|
|
85
|
+
children: f("coolandlovely.feedback.ratePrompt", "Rate your experience (0 = not likely, 10 = very likely)")
|
|
86
|
+
}), /* @__PURE__ */ l("div", {
|
|
85
87
|
className: "flex justify-between gap-1",
|
|
86
88
|
role: "group",
|
|
87
|
-
"aria-label": "NPS score",
|
|
88
|
-
children: Array.from({ length: 11 }, (e, t) => t).map((t) => /* @__PURE__ */
|
|
89
|
+
"aria-label": f("coolandlovely.feedback.npsLabel", "NPS score"),
|
|
90
|
+
children: Array.from({ length: 11 }, (e, t) => t).map((t) => /* @__PURE__ */ l("button", {
|
|
89
91
|
type: "button",
|
|
90
|
-
onClick: () =>
|
|
91
|
-
"aria-pressed":
|
|
92
|
-
className: e("flex h-8 w-7 items-center justify-center rounded-md text-xs font-medium transition-colors",
|
|
92
|
+
onClick: () => y(t),
|
|
93
|
+
"aria-pressed": v === t,
|
|
94
|
+
className: e("flex h-8 w-7 items-center justify-center rounded-md text-xs font-medium transition-colors", v === t ? "bg-action-primary-bg text-action-primary-text" : "border border-border-seam bg-bg-surface text-text-secondary hover:border-action-primary-bg hover:text-text-primary"),
|
|
93
95
|
children: t
|
|
94
96
|
}, t))
|
|
95
97
|
})] }),
|
|
96
|
-
/* @__PURE__ */
|
|
98
|
+
/* @__PURE__ */ l(n, {
|
|
97
99
|
id: "feedback-comment",
|
|
98
|
-
label: "What could be better?",
|
|
99
|
-
value:
|
|
100
|
-
onChange:
|
|
101
|
-
placeholder: "Tell us what’s working or what we should improve…",
|
|
100
|
+
label: f("coolandlovely.feedback.commentLabel", "What could be better?"),
|
|
101
|
+
value: b,
|
|
102
|
+
onChange: x,
|
|
103
|
+
placeholder: f("coolandlovely.feedback.commentPlaceholder", "Tell us what’s working or what we should improve…"),
|
|
102
104
|
rows: 3,
|
|
103
|
-
hint: "Optional — your comment is stored with your score and current page."
|
|
105
|
+
hint: f("coolandlovely.feedback.commentHint", "Optional — your comment is stored with your score and current page.")
|
|
104
106
|
}),
|
|
105
|
-
/* @__PURE__ */
|
|
107
|
+
/* @__PURE__ */ u("button", {
|
|
106
108
|
type: "button",
|
|
107
|
-
onClick:
|
|
108
|
-
disabled:
|
|
109
|
-
className: e("inline-flex w-full items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors",
|
|
110
|
-
children: [/* @__PURE__ */
|
|
109
|
+
onClick: O,
|
|
110
|
+
disabled: v === null,
|
|
111
|
+
className: e("inline-flex w-full items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors", v === null ? "cursor-not-allowed bg-bg-surface text-text-secondary" : "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90"),
|
|
112
|
+
children: [/* @__PURE__ */ l(s, {
|
|
111
113
|
className: "h-4 w-4",
|
|
112
114
|
"aria-hidden": "true"
|
|
113
|
-
}), "Submit feedback"]
|
|
115
|
+
}), f("coolandlovely.feedback.submit", "Submit feedback")]
|
|
114
116
|
})
|
|
115
117
|
]
|
|
116
118
|
})
|
|
117
119
|
})]
|
|
118
|
-
}), /* @__PURE__ */
|
|
120
|
+
}), /* @__PURE__ */ l("button", {
|
|
119
121
|
type: "button",
|
|
120
|
-
onClick:
|
|
121
|
-
"aria-label":
|
|
122
|
-
"aria-expanded":
|
|
123
|
-
className: e("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)]",
|
|
124
|
-
children:
|
|
122
|
+
onClick: E,
|
|
123
|
+
"aria-label": p ? f("coolandlovely.feedback.close", "Close feedback") : f("coolandlovely.feedback.open", "Give feedback"),
|
|
124
|
+
"aria-expanded": p,
|
|
125
|
+
className: e("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)]", p ? "bg-bg-surface text-text-primary" : "bg-action-primary-bg text-action-primary-text"),
|
|
126
|
+
children: l(p ? c : o, {
|
|
125
127
|
className: "h-5 w-5",
|
|
126
128
|
"aria-hidden": "true"
|
|
127
129
|
})
|
|
@@ -129,6 +131,6 @@ var g = () => {
|
|
|
129
131
|
});
|
|
130
132
|
};
|
|
131
133
|
//#endregion
|
|
132
|
-
export {
|
|
134
|
+
export { _ as FeedbackWidget };
|
|
133
135
|
|
|
134
136
|
//# sourceMappingURL=FeedbackWidget.js.map
|
|
@@ -1 +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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"}
|
|
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';\nimport { useTr } from '../hooks';\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 tr = useTr();\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 {tr('coolandlovely.feedback.title', 'How is CoolAndLovely working for you?')}\n </h2>\n <button\n type=\"button\"\n onClick={handleClose}\n aria-label={tr('coolandlovely.feedback.close', '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 {tr('coolandlovely.feedback.thankYou', 'Thank you for your feedback!')}\n </p>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {tr(\n 'coolandlovely.feedback.helpText',\n 'Your input helps us improve CoolAndLovely.'\n )}\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 {tr(\n 'coolandlovely.feedback.ratePrompt',\n 'Rate your experience (0 = not likely, 10 = very likely)'\n )}\n </p>\n <div\n className=\"flex justify-between gap-1\"\n role=\"group\"\n aria-label={tr('coolandlovely.feedback.npsLabel', 'NPS score')}\n >\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={tr('coolandlovely.feedback.commentLabel', 'What could be better?')}\n value={comment}\n onChange={setComment}\n placeholder={tr(\n 'coolandlovely.feedback.commentPlaceholder',\n 'Tell us what’s working or what we should improve…'\n )}\n rows={3}\n hint={tr(\n 'coolandlovely.feedback.commentHint',\n 'Optional — your comment is stored with your score and current page.'\n )}\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 {tr('coolandlovely.feedback.submit', 'Submit feedback')}\n </button>\n </div>\n )}\n </div>\n </GlassCard>\n )}\n\n <button\n type=\"button\"\n onClick={handleToggle}\n aria-label={\n isOpen\n ? tr('coolandlovely.feedback.close', 'Close feedback')\n : tr('coolandlovely.feedback.open', 'Give feedback')\n }\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":";;;;;;;;;AAiBA,IAAM,IAAc,uCACd,IAAc,QAAc,KAAK;AASvC,SAAS,IAA+B;AACtC,KAAI,OAAO,SAAW,IAAa,QAAO;CAC1C,IAAM,IAAM,OAAO,aAAa,QAAQ,EAAY;AACpD,KAAI,CAAC,EAAK,QAAO;AACjB,KAAI;EACF,IAAM,IAAK,SAAS,GAAK,GAAG;AAC5B,SAAO,KAAK,KAAK,GAAG,IAAK;SACnB;AACN,SAAO;;;AAIX,SAAS,IAAmB;AACtB,QAAO,SAAW,OACtB,OAAO,aAAa,QAAQ,GAAa,OAAO,KAAK,KAAK,CAAC,CAAC;;AAG9D,SAAS,EAAkB,GAAgC;AAErD,QAAO,SAAW,OACtB,OAAO,cACL,IAAI,YAAY,oCAAoC;EAClD,QAAQ;EACR,SAAS;EACV,CAAC,CACH;;AAGH,IAAa,UAA2B;CACtC,IAAM,IAAK,GAAO,EACZ,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,CAAC,GAAS,KAAc,EAAS,GAAG,EACpC,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,CAAC,GAAmB,KAAwB,EAAS,GAAM;AAEjE,SAAgB;AACd,IAAqB,GAAqB,CAAC;IAC1C,EAAE,CAAC;CAEN,IAAM,IAAe,QAAkB;AACrC,KAAW,MAAS,CAAC,EAAK;IACzB,EAAE,CAAC,EAEA,IAAc,QAAkB;AACpC,IAAU,GAAM;IACf,EAAE,CAAC,EAEA,IAAe,QAAkB;AACjC,QAAU,SASd,EAPuC;GACrC;GACA,SAAS,EAAQ,MAAM;GACvB,MAAM,OAAO,SAAW,MAAc,OAAO,SAAS,WAAW;GACjE,8BAAa,IAAI,MAAM,EAAC,aAAa;GACtC,CAE4B,EAC7B,GAAkB,EAClB,EAAa,GAAK,EAClB,EAAqB,GAAK,EAG1B,OAAO,iBAAiB;AAItB,GAHA,EAAU,GAAM,EAChB,EAAS,KAAK,EACd,EAAW,GAAG,EACd,EAAa,GAAM;KAClB,KAAK;IACP,CAAC,GAAO,EAAQ,CAAC;AAOpB,QALI,KAAqB,CAAC,IAEjB,OAIP,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,KACC,kBAAC,GAAD;GAAW,WAAU;aAArB,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,gCAAgC,wCAAwC;KACzE,CAAA,EACL,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,cAAY,EAAG,gCAAgC,iBAAiB;KAChE,WAAU;eAEV,kBAAC,GAAD;MAAG,WAAU;MAAU,eAAY;MAAS,CAAA;KACrC,CAAA,CACL;OAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,IACC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAG,mCAAmC,+BAA+B;MACpE,CAAA,EACJ,kBAAC,KAAD;MAAG,WAAU;gBACV,EACC,mCACA,6CACD;MACC,CAAA,CACA;SAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,qCACA,0DACD;OACC,CAAA,EACJ,kBAAC,OAAD;OACE,WAAU;OACV,MAAK;OACL,cAAY,EAAG,mCAAmC,YAAY;iBAE7D,MAAM,KAAK,EAAE,QAAQ,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC,KAAK,MAC5C,kBAAC,UAAD;QAEE,MAAK;QACL,eAAe,EAAS,EAAM;QAC9B,gBAAc,MAAU;QACxB,WAAW,EACT,6FACA,MAAU,IACN,kDACA,qHACL;kBAEA;QACM,EAZF,EAYE,CACT;OACE,CAAA,CACF,EAAA,CAAA;MAEN,kBAAC,GAAD;OACE,IAAG;OACH,OAAO,EAAG,uCAAuC,wBAAwB;OACzE,OAAO;OACP,UAAU;OACV,aAAa,EACX,6CACA,oDACD;OACD,MAAM;OACN,MAAM,EACJ,sCACA,sEACD;OACD,CAAA;MAEF,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UAAU,MAAU;OACpB,WAAW,EACT,mHACA,MAAU,OACN,yDACA,8EACL;iBATH,CAWE,kBAAC,GAAD;QAAM,WAAU;QAAU,eAAY;QAAS,CAAA,EAC9C,EAAG,iCAAiC,kBAAkB,CAChD;;MACL;;IAEJ,CAAA,CACI;MAGd,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,cACE,IACI,EAAG,gCAAgC,iBAAiB,GACpD,EAAG,+BAA+B,gBAAgB;GAExD,iBAAe;GACf,WAAW,EACT,uMACA,IACI,oCACA,gDACL;aAGC,EADD,IACE,IAEA,GAFD;IAAG,WAAU;IAAU,eAAY;IAAS,CAEgB;GAEvD,CAAA,CACL"}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { cn as e } from "../utils/cn.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useLikeLook as t, useSaveLook as n, useUnlikeLook as r, useUnsaveLook as i } from "../hooks/useCoolAndLovelyApi.js";
|
|
3
|
+
import { Thumbnail as a } from "./Thumbnail.js";
|
|
4
4
|
import { AddToLookbookButton as o } from "./AddToLookbookButton.js";
|
|
5
5
|
import { titleCase as s } from "../utils/formatters.js";
|
|
6
6
|
import { useEffect as c, useState as l } from "react";
|
|
7
7
|
import { Bookmark as u, Heart as d, Sparkles as f } from "lucide-react";
|
|
8
8
|
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
9
9
|
//#region src/coolandlovely/components/LookCard.tsx
|
|
10
|
-
var h = ({ lookId:
|
|
11
|
-
let o =
|
|
10
|
+
var h = ({ lookId: n, likeCount: i, isLikedByMe: a }) => {
|
|
11
|
+
let o = t(), s = r(), [u, f] = l(!!a), [h, g] = l(i), _ = o.isPending || s.isPending;
|
|
12
12
|
return c(() => {
|
|
13
|
-
_ || (f(!!a), g(
|
|
13
|
+
_ || (f(!!a), g(i));
|
|
14
14
|
}, [
|
|
15
|
-
|
|
15
|
+
i,
|
|
16
16
|
a,
|
|
17
17
|
_
|
|
18
18
|
]), /* @__PURE__ */ m("button", {
|
|
@@ -20,12 +20,12 @@ var h = ({ lookId: t, likeCount: r, isLikedByMe: a }) => {
|
|
|
20
20
|
onClick: () => {
|
|
21
21
|
if (_) return;
|
|
22
22
|
let e = !u;
|
|
23
|
-
f(e), g((t) => t + (e ? 1 : -1)), (e ? o : s).mutate(
|
|
23
|
+
f(e), g((t) => t + (e ? 1 : -1)), (e ? o : s).mutate(n, {
|
|
24
24
|
onSuccess: (e) => {
|
|
25
25
|
f(!!e.isLikedByMe), g(e.likeCount);
|
|
26
26
|
},
|
|
27
27
|
onError: () => {
|
|
28
|
-
f(!e), g(
|
|
28
|
+
f(!e), g(i);
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
},
|
|
@@ -38,16 +38,16 @@ var h = ({ lookId: t, likeCount: r, isLikedByMe: a }) => {
|
|
|
38
38
|
children: h
|
|
39
39
|
})]
|
|
40
40
|
});
|
|
41
|
-
}, g = ({ lookId: t, isSavedByMe:
|
|
42
|
-
let
|
|
41
|
+
}, g = ({ lookId: t, isSavedByMe: r }) => {
|
|
42
|
+
let a = n(), o = i(), [s, d] = l(!!r), f = a.isPending || o.isPending;
|
|
43
43
|
return c(() => {
|
|
44
|
-
f || d(!!
|
|
45
|
-
}, [
|
|
44
|
+
f || d(!!r);
|
|
45
|
+
}, [r, f]), /* @__PURE__ */ p("button", {
|
|
46
46
|
type: "button",
|
|
47
47
|
onClick: () => {
|
|
48
48
|
if (f) return;
|
|
49
49
|
let e = !s;
|
|
50
|
-
d(e), (e ?
|
|
50
|
+
d(e), (e ? a : o).mutate(t, {
|
|
51
51
|
onSuccess: (e) => d(!!e.isSavedByMe),
|
|
52
52
|
onError: () => d(!e)
|
|
53
53
|
});
|
|
@@ -58,29 +58,29 @@ var h = ({ lookId: t, likeCount: r, isLikedByMe: a }) => {
|
|
|
58
58
|
className: "inline-flex items-center justify-center rounded-full bg-bg-surface/90 p-1.5 text-text-primary shadow-sm backdrop-blur transition-colors hover:bg-bg-surface disabled:opacity-60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
|
|
59
59
|
children: /* @__PURE__ */ p(u, { className: e("h-3.5 w-3.5", s ? "fill-current text-accent-primary" : "text-text-secondary") })
|
|
60
60
|
});
|
|
61
|
-
}, _ = ({ id:
|
|
61
|
+
}, _ = ({ id: t, title: n, occasion: r, imageFileId: i, likeCount: c, isLikedByMe: l, isSavedByMe: u, onOpen: _, showActions: v = !1, className: y }) => /* @__PURE__ */ m("div", {
|
|
62
62
|
className: e("group relative overflow-hidden rounded-xl border border-border-seam bg-bg-surface transition-shadow hover:shadow-lg", y),
|
|
63
63
|
children: [/* @__PURE__ */ p("button", {
|
|
64
64
|
type: "button",
|
|
65
65
|
onClick: _,
|
|
66
|
-
"aria-label": `Open look ${
|
|
66
|
+
"aria-label": `Open look ${n}`,
|
|
67
67
|
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)]",
|
|
68
|
-
children: /* @__PURE__ */ p(
|
|
68
|
+
children: /* @__PURE__ */ p(a, {
|
|
69
69
|
icon: /* @__PURE__ */ p(f, { className: "h-8 w-8" }),
|
|
70
|
-
seed:
|
|
71
|
-
fileId:
|
|
72
|
-
alt:
|
|
70
|
+
seed: t,
|
|
71
|
+
fileId: i,
|
|
72
|
+
alt: n,
|
|
73
73
|
className: "aspect-[3/4] w-full rounded-none",
|
|
74
74
|
overlay: /* @__PURE__ */ m("div", {
|
|
75
75
|
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/45 to-transparent p-3 pt-10",
|
|
76
76
|
children: [/* @__PURE__ */ p("p", {
|
|
77
77
|
className: "truncate text-sm font-semibold text-text-primary",
|
|
78
|
-
children:
|
|
78
|
+
children: n
|
|
79
79
|
}), /* @__PURE__ */ m("div", {
|
|
80
80
|
className: "flex items-center justify-between gap-2",
|
|
81
81
|
children: [/* @__PURE__ */ p("span", {
|
|
82
82
|
className: "inline-flex items-center rounded-full bg-bg-surface/85 px-2 py-0.5 text-[11px] font-medium text-text-primary backdrop-blur",
|
|
83
|
-
children: s(
|
|
83
|
+
children: s(r)
|
|
84
84
|
}), !v && typeof c == "number" && c > 0 ? /* @__PURE__ */ m("span", {
|
|
85
85
|
className: "inline-flex items-center gap-1 text-xs text-text-secondary",
|
|
86
86
|
children: [/* @__PURE__ */ p(d, { className: "h-3.5 w-3.5 fill-current text-accent-primary" }), /* @__PURE__ */ p("span", {
|
|
@@ -95,15 +95,15 @@ var h = ({ lookId: t, likeCount: r, isLikedByMe: a }) => {
|
|
|
95
95
|
className: "absolute right-2 top-2 flex items-center gap-1.5 opacity-100 transition-opacity duration-200 sm:opacity-0 sm:focus-within:opacity-100 sm:group-hover:opacity-100",
|
|
96
96
|
children: [
|
|
97
97
|
/* @__PURE__ */ p(h, {
|
|
98
|
-
lookId:
|
|
98
|
+
lookId: t,
|
|
99
99
|
likeCount: c ?? 0,
|
|
100
100
|
isLikedByMe: l
|
|
101
101
|
}),
|
|
102
102
|
/* @__PURE__ */ p(g, {
|
|
103
|
-
lookId:
|
|
103
|
+
lookId: t,
|
|
104
104
|
isSavedByMe: u
|
|
105
105
|
}),
|
|
106
|
-
/* @__PURE__ */ p(o, { lookId:
|
|
106
|
+
/* @__PURE__ */ p(o, { lookId: t })
|
|
107
107
|
]
|
|
108
108
|
}) : null]
|
|
109
109
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StateViews.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/StateViews.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"StateViews.d.ts","sourceRoot":"","sources":["../../../src/coolandlovely/components/StateViews.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAKhC,UAAU,gBAAgB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAK5C,CAAC;AAEF,UAAU,cAAc;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,SAAS,EAAE,EAAE,CAAC,cAAc,CA4BxC,CAAC"}
|
|
@@ -1,34 +1,43 @@
|
|
|
1
1
|
import { cn as e } from "../utils/cn.js";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { useTr as t } from "../hooks/useTr.js";
|
|
3
|
+
import "../hooks/index.js";
|
|
4
|
+
import { AlertCircle as n, Loader2 as r, RotateCw as i } from "lucide-react";
|
|
5
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
4
6
|
//#region src/coolandlovely/components/StateViews.tsx
|
|
5
|
-
var
|
|
6
|
-
className: e("flex min-h-[40vh] flex-col items-center justify-center gap-3",
|
|
7
|
-
children: [/* @__PURE__ */
|
|
7
|
+
var s = ({ label: t = "Loading…", className: n }) => /* @__PURE__ */ o("div", {
|
|
8
|
+
className: e("flex min-h-[40vh] flex-col items-center justify-center gap-3", n),
|
|
9
|
+
children: [/* @__PURE__ */ a(r, { className: "h-8 w-8 animate-spin text-primary" }), /* @__PURE__ */ a("p", {
|
|
8
10
|
className: "text-sm text-text-secondary",
|
|
9
11
|
children: t
|
|
10
12
|
})]
|
|
11
|
-
}),
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
className: "
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
})
|
|
13
|
+
}), c = ({ title: r, message: s, onRetry: c, className: l }) => {
|
|
14
|
+
let u = t();
|
|
15
|
+
return /* @__PURE__ */ o("div", {
|
|
16
|
+
className: e("flex min-h-[40vh] flex-col items-center justify-center gap-3 text-center", l),
|
|
17
|
+
children: [
|
|
18
|
+
/* @__PURE__ */ a(n, { className: "h-10 w-10 text-status-error-text" }),
|
|
19
|
+
/* @__PURE__ */ a("h2", {
|
|
20
|
+
className: "text-lg font-semibold text-text-primary",
|
|
21
|
+
children: r ?? u("coolandlovely.error.title", "Something went wrong")
|
|
22
|
+
}),
|
|
23
|
+
/* @__PURE__ */ a("p", {
|
|
24
|
+
className: "max-w-sm text-sm text-text-secondary",
|
|
25
|
+
children: s ?? u("coolandlovely.error.message", "We could not load this data. Please try again.")
|
|
26
|
+
}),
|
|
27
|
+
c && /* @__PURE__ */ o("button", {
|
|
28
|
+
type: "button",
|
|
29
|
+
onClick: c,
|
|
30
|
+
className: "mt-1 inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken",
|
|
31
|
+
children: [
|
|
32
|
+
/* @__PURE__ */ a(i, { className: "h-4 w-4" }),
|
|
33
|
+
" ",
|
|
34
|
+
u("coolandlovely.error.retry", "Retry")
|
|
35
|
+
]
|
|
36
|
+
})
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
};
|
|
31
40
|
//#endregion
|
|
32
|
-
export {
|
|
41
|
+
export { c as ErrorView, s as LoadingView };
|
|
33
42
|
|
|
34
43
|
//# sourceMappingURL=StateViews.js.map
|