@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.
Files changed (100) hide show
  1. package/dist/coolandlovely/components/ActivityTrail.d.ts +24 -0
  2. package/dist/coolandlovely/components/ActivityTrail.d.ts.map +1 -0
  3. package/dist/coolandlovely/components/ActivityTrail.js +70 -0
  4. package/dist/coolandlovely/components/ActivityTrail.js.map +1 -0
  5. package/dist/coolandlovely/components/Comments.js +4 -4
  6. package/dist/coolandlovely/components/FeedbackWidget.d.ts.map +1 -1
  7. package/dist/coolandlovely/components/FeedbackWidget.js +72 -70
  8. package/dist/coolandlovely/components/FeedbackWidget.js.map +1 -1
  9. package/dist/coolandlovely/components/LookCard.js +24 -24
  10. package/dist/coolandlovely/components/StateViews.d.ts.map +1 -1
  11. package/dist/coolandlovely/components/StateViews.js +35 -26
  12. package/dist/coolandlovely/components/StateViews.js.map +1 -1
  13. package/dist/coolandlovely/components/StatusBadge.d.ts.map +1 -1
  14. package/dist/coolandlovely/components/StatusBadge.js +45 -15
  15. package/dist/coolandlovely/components/StatusBadge.js.map +1 -1
  16. package/dist/coolandlovely/components/VisibilityChip.d.ts.map +1 -1
  17. package/dist/coolandlovely/components/VisibilityChip.js +22 -17
  18. package/dist/coolandlovely/components/VisibilityChip.js.map +1 -1
  19. package/dist/coolandlovely/components/index.d.ts +1 -0
  20. package/dist/coolandlovely/components/index.d.ts.map +1 -1
  21. package/dist/coolandlovely/components/index.js +1 -0
  22. package/dist/coolandlovely/hooks/index.d.ts +4 -0
  23. package/dist/coolandlovely/hooks/index.d.ts.map +1 -1
  24. package/dist/coolandlovely/hooks/index.js +4 -1
  25. package/dist/coolandlovely/hooks/useActivity.d.ts +39 -0
  26. package/dist/coolandlovely/hooks/useActivity.d.ts.map +1 -0
  27. package/dist/coolandlovely/hooks/useActivity.js +38 -0
  28. package/dist/coolandlovely/hooks/useActivity.js.map +1 -0
  29. package/dist/coolandlovely/hooks/useAssistantEntity.d.ts +24 -0
  30. package/dist/coolandlovely/hooks/useAssistantEntity.d.ts.map +1 -0
  31. package/dist/coolandlovely/hooks/useAssistantEntity.js +12 -0
  32. package/dist/coolandlovely/hooks/useAssistantEntity.js.map +1 -0
  33. package/dist/coolandlovely/hooks/useTr.d.ts +2 -0
  34. package/dist/coolandlovely/hooks/useTr.d.ts.map +1 -0
  35. package/dist/coolandlovely/hooks/useTr.js +14 -0
  36. package/dist/coolandlovely/hooks/useTr.js.map +1 -0
  37. package/dist/coolandlovely/pages/HomePage.d.ts.map +1 -1
  38. package/dist/coolandlovely/pages/HomePage.js +172 -132
  39. package/dist/coolandlovely/pages/HomePage.js.map +1 -1
  40. package/dist/coolandlovely/pages/admin/AdminBrandsPage.js +10 -10
  41. package/dist/coolandlovely/pages/admin/AdminCollaborationsPage.js +16 -16
  42. package/dist/coolandlovely/pages/admin/AdminFeaturedItemsPage.js +17 -17
  43. package/dist/coolandlovely/pages/brands/BrandDetailPage.d.ts.map +1 -1
  44. package/dist/coolandlovely/pages/brands/BrandDetailPage.js +171 -151
  45. package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
  46. package/dist/coolandlovely/pages/brands/BrandFormPage.js +9 -9
  47. package/dist/coolandlovely/pages/brands/BrandsListPage.d.ts.map +1 -1
  48. package/dist/coolandlovely/pages/brands/BrandsListPage.js +80 -76
  49. package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
  50. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.d.ts.map +1 -1
  51. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +121 -120
  52. package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
  53. package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +7 -7
  54. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.d.ts.map +1 -1
  55. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +82 -81
  56. package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
  57. package/dist/coolandlovely/pages/collections/CollectionDetailPage.d.ts.map +1 -1
  58. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +111 -110
  59. package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
  60. package/dist/coolandlovely/pages/collections/CollectionFormPage.js +9 -9
  61. package/dist/coolandlovely/pages/collections/CollectionsListPage.d.ts.map +1 -1
  62. package/dist/coolandlovely/pages/collections/CollectionsListPage.js +54 -55
  63. package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
  64. package/dist/coolandlovely/pages/discover/DiscoverPage.d.ts.map +1 -1
  65. package/dist/coolandlovely/pages/discover/DiscoverPage.js +452 -307
  66. package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
  67. package/dist/coolandlovely/pages/items/StyleItemDetailPage.d.ts.map +1 -1
  68. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +117 -116
  69. package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
  70. package/dist/coolandlovely/pages/items/StyleItemFormPage.js +10 -10
  71. package/dist/coolandlovely/pages/items/StyleItemsListPage.d.ts.map +1 -1
  72. package/dist/coolandlovely/pages/items/StyleItemsListPage.js +105 -95
  73. package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
  74. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.d.ts.map +1 -1
  75. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +32 -29
  76. package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
  77. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.d.ts.map +1 -1
  78. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +88 -79
  79. package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
  80. package/dist/coolandlovely/pages/looks/LookDetailPage.d.ts.map +1 -1
  81. package/dist/coolandlovely/pages/looks/LookDetailPage.js +135 -134
  82. package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
  83. package/dist/coolandlovely/pages/looks/LookFormPage.js +6 -6
  84. package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
  85. package/dist/coolandlovely/pages/looks/LooksListPage.d.ts.map +1 -1
  86. package/dist/coolandlovely/pages/looks/LooksListPage.js +94 -94
  87. package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
  88. package/dist/coolandlovely/pages/profile/StyleProfilePage.d.ts.map +1 -1
  89. package/dist/coolandlovely/pages/profile/StyleProfilePage.js +226 -199
  90. package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
  91. package/dist/coolandlovely/pages/trends/TrendsPage.js +8 -8
  92. package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +5 -5
  93. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.d.ts.map +1 -1
  94. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +71 -69
  95. package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
  96. package/dist/utils/pageContext.d.ts +23 -0
  97. package/dist/utils/pageContext.d.ts.map +1 -1
  98. package/dist/utils/pageContext.js +37 -25
  99. package/dist/utils/pageContext.js.map +1 -1
  100. 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 { formatRelativeTime as t } from "../utils/formatters.js";
3
- import { useAddComment as n, useEntityComments as r } from "../hooks/useComments.js";
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 = r(f, p), _ = n(), [v, y] = a(""), b = g.data?.comments ?? [], x = g.data?.total ?? b.length;
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: [t(e.createdAt), e.edited ? " · edited" : ""]
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;AA6ChC,eAAO,MAAM,cAAc,EAAE,EAwJ5B,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 { TextAreaField as t } from "./Field.js";
3
- import { useCallback as n, useEffect as r, useState as i } from "react";
4
- import { MessageSquarePlus as a, Send as o, X as s } from "lucide-react";
5
- import { jsx as c, jsxs as l } from "react/jsx-runtime";
6
- import { GlassCard as u } from "@burdenoff/fe-libs/ui";
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 d = "coolandlovely-feedback-submitted-at", f = 10080 * 60 * 1e3;
9
- function p() {
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(d);
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 < f;
17
+ return Date.now() - t < p;
16
18
  } catch {
17
19
  return !1;
18
20
  }
19
21
  }
20
- function m() {
21
- typeof window > "u" || window.localStorage.setItem(d, String(Date.now()));
22
+ function h() {
23
+ typeof window > "u" || window.localStorage.setItem(f, String(Date.now()));
22
24
  }
23
- function h(e) {
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 g = () => {
30
- let [d, f] = i(!1), [g, _] = i(null), [v, y] = i(""), [b, x] = i(!1), [S, C] = i(!1);
31
- r(() => {
32
- C(p());
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 w = n(() => {
35
- f((e) => !e);
36
- }, []), T = n(() => {
37
- f(!1);
38
- }, []), E = n(() => {
39
- g !== null && (h({
40
- score: g,
41
- comment: v.trim(),
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
- }), m(), x(!0), C(!0), window.setTimeout(() => {
45
- f(!1), _(null), y(""), x(!1);
46
+ }), h(), C(!0), T(!0), window.setTimeout(() => {
47
+ _(!1), y(null), x(""), C(!1);
46
48
  }, 2500));
47
- }, [g, v]);
48
- return S && !d ? null : /* @__PURE__ */ l("div", {
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: [d && /* @__PURE__ */ l(u, {
52
+ children: [p && /* @__PURE__ */ u(d, {
51
53
  className: "w-[min(92vw,360px)] overflow-hidden p-0 shadow-xl",
52
- children: [/* @__PURE__ */ l("div", {
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__ */ c("h2", {
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__ */ c("button", {
58
+ children: f("coolandlovely.feedback.title", "How is CoolAndLovely working for you?")
59
+ }), /* @__PURE__ */ l("button", {
58
60
  type: "button",
59
- onClick: T,
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(s, {
64
+ children: /* @__PURE__ */ l(c, {
63
65
  className: "h-4 w-4",
64
66
  "aria-hidden": "true"
65
67
  })
66
68
  })]
67
- }), /* @__PURE__ */ c("div", {
69
+ }), /* @__PURE__ */ l("div", {
68
70
  className: "p-4",
69
- children: b ? /* @__PURE__ */ l("div", {
71
+ children: S ? /* @__PURE__ */ u("div", {
70
72
  className: "py-6 text-center",
71
- children: [/* @__PURE__ */ c("p", {
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__ */ c("p", {
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__ */ l("div", {
80
+ }) : /* @__PURE__ */ u("div", {
79
81
  className: "space-y-4",
80
82
  children: [
81
- /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("p", {
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__ */ c("div", {
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__ */ c("button", {
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: () => _(t),
91
- "aria-pressed": g === t,
92
- className: e("flex h-8 w-7 items-center justify-center rounded-md text-xs font-medium transition-colors", g === 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"),
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__ */ c(t, {
98
+ /* @__PURE__ */ l(n, {
97
99
  id: "feedback-comment",
98
- label: "What could be better?",
99
- value: v,
100
- onChange: y,
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__ */ l("button", {
107
+ /* @__PURE__ */ u("button", {
106
108
  type: "button",
107
- onClick: E,
108
- disabled: g === null,
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", g === null ? "cursor-not-allowed bg-bg-surface text-text-secondary" : "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90"),
110
- children: [/* @__PURE__ */ c(o, {
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__ */ c("button", {
120
+ }), /* @__PURE__ */ l("button", {
119
121
  type: "button",
120
- onClick: w,
121
- "aria-label": d ? "Close feedback" : "Give feedback",
122
- "aria-expanded": d,
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)]", d ? "bg-bg-surface text-text-primary" : "bg-action-primary-bg text-action-primary-text"),
124
- children: c(d ? s : a, {
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 { g as FeedbackWidget };
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":"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"}
@@ -1,18 +1,18 @@
1
1
  import { cn as e } from "../utils/cn.js";
2
- import { Thumbnail as t } from "./Thumbnail.js";
3
- import { useLikeLook as n, useSaveLook as r, useUnlikeLook as i, useUnsaveLook as a } from "../hooks/useCoolAndLovelyApi.js";
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: t, likeCount: r, isLikedByMe: a }) => {
11
- let o = n(), s = i(), [u, f] = l(!!a), [h, g] = l(r), _ = o.isPending || s.isPending;
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(r));
13
+ _ || (f(!!a), g(i));
14
14
  }, [
15
- r,
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(t, {
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(r);
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: n }) => {
42
- let i = r(), o = a(), [s, d] = l(!!n), f = i.isPending || o.isPending;
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(!!n);
45
- }, [n, f]), /* @__PURE__ */ p("button", {
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 ? i : o).mutate(t, {
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: n, title: r, occasion: i, imageFileId: a, likeCount: c, isLikedByMe: l, isSavedByMe: u, onOpen: _, showActions: v = !1, className: y }) => /* @__PURE__ */ m("div", {
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 ${r}`,
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(t, {
68
+ children: /* @__PURE__ */ p(a, {
69
69
  icon: /* @__PURE__ */ p(f, { className: "h-8 w-8" }),
70
- seed: n,
71
- fileId: a,
72
- alt: r,
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: r
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(i)
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: n,
98
+ lookId: t,
99
99
  likeCount: c ?? 0,
100
100
  isLikedByMe: l
101
101
  }),
102
102
  /* @__PURE__ */ p(g, {
103
- lookId: n,
103
+ lookId: t,
104
104
  isSavedByMe: u
105
105
  }),
106
- /* @__PURE__ */ p(o, { lookId: n })
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;AAIhC,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,CAyBxC,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 { AlertCircle as t, Loader2 as n, RotateCw as r } from "lucide-react";
3
- import { jsx as i, jsxs as a } from "react/jsx-runtime";
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 o = ({ label: t = "Loading…", className: r }) => /* @__PURE__ */ a("div", {
6
- className: e("flex min-h-[40vh] flex-col items-center justify-center gap-3", r),
7
- children: [/* @__PURE__ */ i(n, { className: "h-8 w-8 animate-spin text-primary" }), /* @__PURE__ */ i("p", {
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
- }), s = ({ title: n = "Something went wrong", message: o = "We could not load this data. Please try again.", onRetry: s, className: c }) => /* @__PURE__ */ a("div", {
12
- className: e("flex min-h-[40vh] flex-col items-center justify-center gap-3 text-center", c),
13
- children: [
14
- /* @__PURE__ */ i(t, { className: "h-10 w-10 text-status-error-text" }),
15
- /* @__PURE__ */ i("h2", {
16
- className: "text-lg font-semibold text-text-primary",
17
- children: n
18
- }),
19
- /* @__PURE__ */ i("p", {
20
- className: "max-w-sm text-sm text-text-secondary",
21
- children: o
22
- }),
23
- s && /* @__PURE__ */ a("button", {
24
- type: "button",
25
- onClick: s,
26
- 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",
27
- children: [/* @__PURE__ */ i(r, { className: "h-4 w-4" }), " Retry"]
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 { s as ErrorView, o as LoadingView };
41
+ export { c as ErrorView, s as LoadingView };
33
42
 
34
43
  //# sourceMappingURL=StateViews.js.map