@burdenoff/microfe-conversations 2026.525.3 → 2026.527.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/conversations/ConversationsUserRoutes.js +4 -3
  2. package/dist/conversations/ConversationsUserRoutes.js.map +1 -1
  3. package/dist/conversations/components/EmptyState.js +27 -0
  4. package/dist/conversations/components/EmptyState.js.map +1 -0
  5. package/dist/conversations/components/LazyPageBoundary.js +26 -42
  6. package/dist/conversations/components/LazyPageBoundary.js.map +1 -1
  7. package/dist/conversations/components/MessageBubble.js +3 -1
  8. package/dist/conversations/components/MessageBubble.js.map +1 -1
  9. package/dist/conversations/components/PageLayout.js +3 -39
  10. package/dist/conversations/components/PageLayout.js.map +1 -1
  11. package/dist/conversations/components/PageSection.js +25 -0
  12. package/dist/conversations/components/PageSection.js.map +1 -0
  13. package/dist/conversations/components/PermissionGuard.js +1 -1
  14. package/dist/conversations/components/PermissionGuard.js.map +1 -1
  15. package/dist/conversations/components/lazyRetry.js +23 -0
  16. package/dist/conversations/components/lazyRetry.js.map +1 -0
  17. package/dist/conversations/constants/permissions.js +0 -1
  18. package/dist/conversations/constants/permissions.js.map +1 -1
  19. package/dist/conversations/hooks/useMessageQueries.js.map +1 -1
  20. package/dist/conversations/hooks/useMessageSubscriptions.js.map +1 -1
  21. package/dist/conversations/pages/AdminConversationsLandingPage.js +6 -6
  22. package/dist/conversations/pages/AdminConversationsLandingPage.js.map +1 -1
  23. package/dist/conversations/pages/ConversationAdminPages.js +56 -49
  24. package/dist/conversations/pages/ConversationAdminPages.js.map +1 -1
  25. package/dist/conversations/pages/ConversationDetailPage.js +3 -2
  26. package/dist/conversations/pages/ConversationDetailPage.js.map +1 -1
  27. package/dist/conversations/pages/ConversationListPage.js +133 -136
  28. package/dist/conversations/pages/ConversationListPage.js.map +1 -1
  29. package/dist/conversations/pages/ConversationsLandingPage.js +50 -57
  30. package/dist/conversations/pages/ConversationsLandingPage.js.map +1 -1
  31. package/dist/conversations/pages/ConversationsSettingsPage.js +8 -5
  32. package/dist/conversations/pages/ConversationsSettingsPage.js.map +1 -1
  33. package/dist/conversations/pages/NewConversationPage.js +87 -88
  34. package/dist/conversations/pages/NewConversationPage.js.map +1 -1
  35. package/dist/conversations/providers/ConversationsProvider.js +1 -1
  36. package/dist/conversations/providers/ConversationsProvider.js.map +1 -1
  37. package/dist/conversations/utils/mappers.js.map +1 -1
  38. package/package.json +1 -1
@@ -1,97 +1,91 @@
1
- import { PermissionGuard as e } from "../components/PermissionGuard.js";
2
- import { CONVERSATIONS_PERMISSIONS as t } from "../constants/permissions.js";
3
- import { ConversationListPage as n } from "./ConversationListPage.js";
4
- import { NewConversationPage as r } from "./NewConversationPage.js";
5
- import { ConversationsSettingsPage as i } from "./ConversationsSettingsPage.js";
6
- import { useUserFeatureFlags as a } from "../hooks/useUserFeatureFlags.js";
7
- import { useEffect as o, useMemo as s } from "react";
8
- import { useSearchParams as c } from "react-router-dom";
9
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
10
- import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
1
+ import { useTr as e } from "../utils/i18n.js";
2
+ import { PermissionGuard as t } from "../components/PermissionGuard.js";
3
+ import { CONVERSATIONS_PERMISSIONS as n } from "../constants/permissions.js";
4
+ import { ConversationListPage as r } from "./ConversationListPage.js";
5
+ import { NewConversationPage as i } from "./NewConversationPage.js";
6
+ import { ConversationsSettingsPage as a } from "./ConversationsSettingsPage.js";
7
+ import { useUserFeatureFlags as o } from "../hooks/useUserFeatureFlags.js";
8
+ import { useEffect as s, useMemo as c } from "react";
9
+ import { useSearchParams as l } from "react-router-dom";
10
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
11
11
  import { Tabs as f } from "@burdenoff/fe-libs/chrome";
12
12
  //#region src/conversations/pages/ConversationsLandingPage.tsx
13
13
  var p = "all";
14
- function m({ title: e }) {
15
- let { t } = d();
16
- return /* @__PURE__ */ l("div", {
14
+ function m({ title: t }) {
15
+ let n = e();
16
+ return /* @__PURE__ */ u("div", {
17
17
  className: "flex h-full items-center justify-center p-8 text-center",
18
- children: /* @__PURE__ */ u("div", {
18
+ children: /* @__PURE__ */ d("div", {
19
19
  className: "space-y-2",
20
- children: [/* @__PURE__ */ l("h3", {
20
+ children: [/* @__PURE__ */ u("h3", {
21
21
  className: "text-lg font-semibold text-foreground",
22
- children: e
23
- }), /* @__PURE__ */ l("p", {
22
+ children: t
23
+ }), /* @__PURE__ */ u("p", {
24
24
  className: "text-sm text-muted-foreground",
25
- children: ((e, n) => {
26
- let r = t(e);
27
- return r === e ? n : r;
28
- })("conversations.comingSoon", "Coming soon.")
25
+ children: n("conversations.comingSoon", "Coming soon.")
29
26
  })]
30
27
  })
31
28
  });
32
29
  }
33
30
  function h() {
34
- let [h, g] = c(), { t: _ } = d(), v = (e, t) => {
35
- let n = _(e);
36
- return n === e ? t : n;
37
- }, { archivedEnabled: y } = a(), b = s(() => [
31
+ let [h, g] = l(), _ = e(), { archivedEnabled: v } = o(), y = c(() => [
38
32
  {
39
33
  id: p,
40
- label: v("conversations.allConversations", "All Conversations")
34
+ label: _("conversations.allConversations", "All Conversations")
41
35
  },
42
36
  {
43
37
  id: "channels",
44
- label: v("conversations.channels", "Channels")
38
+ label: _("conversations.channels", "Channels")
45
39
  },
46
40
  {
47
41
  id: "direct",
48
- label: v("conversations.directMessages", "Direct Messages")
42
+ label: _("conversations.directMessages", "Direct Messages")
49
43
  },
50
44
  {
51
45
  id: "groups",
52
- label: v("conversations.groups", "Groups")
46
+ label: _("conversations.groups", "Groups")
53
47
  },
54
- ...y ? [{
48
+ ...v ? [{
55
49
  id: "archived",
56
- label: v("conversations.archived", "Archived")
50
+ label: _("conversations.archived", "Archived")
57
51
  }] : [],
58
52
  {
59
53
  id: "new",
60
- label: v("conversations.new", "New")
54
+ label: _("conversations.new", "New")
61
55
  },
62
56
  {
63
57
  id: "settings",
64
- label: v("conversations.settings", "Settings")
58
+ label: _("conversations.settings", "Settings")
65
59
  }
66
- ], [_, y]), x = h.get("tab"), S = x === "archived" && !y ? p : x ?? p;
67
- return o(() => {
68
- !y && x === "archived" && g((e) => {
60
+ ], [_, v]), b = h.get("tab"), x = b === "archived" && !v ? p : b ?? p;
61
+ return s(() => {
62
+ !v && b === "archived" && g((e) => {
69
63
  let t = new URLSearchParams(e);
70
64
  return t.delete("tab"), t;
71
65
  }, { replace: !0 });
72
66
  }, [
73
- y,
74
- x,
67
+ v,
68
+ b,
75
69
  g
76
- ]), /* @__PURE__ */ l(e, {
77
- permission: t.CONVERSATION_READ,
70
+ ]), /* @__PURE__ */ u(t, {
71
+ permission: n.CONVERSATION_READ,
78
72
  allowUnavailablePermissions: !0,
79
- children: /* @__PURE__ */ u("main", {
73
+ children: /* @__PURE__ */ d("main", {
80
74
  "data-tour": "conversations-landing",
81
75
  className: "flex flex-col h-full",
82
76
  "aria-labelledby": "conversations-page-heading",
83
77
  children: [
84
- /* @__PURE__ */ l("h1", {
78
+ /* @__PURE__ */ u("h1", {
85
79
  id: "conversations-page-heading",
86
80
  className: "sr-only",
87
- children: v("conversations.title", "Conversations")
81
+ children: _("conversations.title", "Conversations")
88
82
  }),
89
- /* @__PURE__ */ l("div", {
83
+ /* @__PURE__ */ u("div", {
90
84
  "data-tour": "conversations-tabs",
91
85
  className: "border-b border-border-subtle px-4 pt-3",
92
- children: /* @__PURE__ */ l(f, {
93
- items: b,
94
- activeTabId: S,
86
+ children: /* @__PURE__ */ u(f, {
87
+ items: y,
88
+ activeTabId: x,
95
89
  onTabChange: (e) => {
96
90
  g((t) => {
97
91
  let n = new URLSearchParams(t);
@@ -101,19 +95,18 @@ function h() {
101
95
  variant: "underline"
102
96
  })
103
97
  }),
104
- /* @__PURE__ */ u("div", {
98
+ /* @__PURE__ */ d("section", {
105
99
  className: "flex-1 min-h-0 overflow-auto",
106
- role: "region",
107
100
  "aria-live": "polite",
108
- "aria-label": v("conversations.activePanel", "Active conversations panel"),
101
+ "aria-label": _("conversations.activePanel", "Active conversations panel"),
109
102
  children: [
110
- S === "all" && /* @__PURE__ */ l(n, {}),
111
- S === "channels" && /* @__PURE__ */ l(n, { forcedFilterParam: "channel" }),
112
- S === "direct" && /* @__PURE__ */ l(n, { forcedFilterParam: "direct" }),
113
- S === "groups" && /* @__PURE__ */ l(n, { forcedFilterParam: "group" }),
114
- S === "archived" && /* @__PURE__ */ l(m, { title: v("conversations.archivedTitle", "Archived Conversations") }),
115
- S === "new" && /* @__PURE__ */ l(r, {}),
116
- S === "settings" && /* @__PURE__ */ l(i, {})
103
+ x === "all" && /* @__PURE__ */ u(r, {}),
104
+ x === "channels" && /* @__PURE__ */ u(r, { forcedFilterParam: "channel" }),
105
+ x === "direct" && /* @__PURE__ */ u(r, { forcedFilterParam: "direct" }),
106
+ x === "groups" && /* @__PURE__ */ u(r, { forcedFilterParam: "group" }),
107
+ x === "archived" && /* @__PURE__ */ u(m, { title: _("conversations.archivedTitle", "Archived Conversations") }),
108
+ x === "new" && /* @__PURE__ */ u(i, {}),
109
+ x === "settings" && /* @__PURE__ */ u(a, {})
117
110
  ]
118
111
  })
119
112
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationsLandingPage.js","names":[],"sources":["../../../src/conversations/pages/ConversationsLandingPage.tsx"],"sourcesContent":["import { useEffect, useMemo } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport { Tabs, type TabItem } from \"@burdenoff/fe-libs/chrome\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\nimport { ConversationListPage } from \"./ConversationListPage\";\nimport { NewConversationPage } from \"./NewConversationPage\";\nimport { ConversationsSettingsPage } from \"./ConversationsSettingsPage\";\nimport { PermissionGuard } from \"../components/PermissionGuard\";\nimport { CONVERSATIONS_PERMISSIONS } from \"../constants/permissions\";\nimport { useUserFeatureFlags } from \"../hooks/useUserFeatureFlags\";\n\nconst DEFAULT_TAB_ID = \"all\";\n\n/**\n * Coming-soon placeholder shown for tabs whose dedicated pages\n * are not yet implemented in this microfrontend.\n */\nfunction ComingSoon({ title }: { title: string }) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n return (\n <div className=\"flex h-full items-center justify-center p-8 text-center\">\n <div className=\"space-y-2\">\n <h3 className=\"text-lg font-semibold text-foreground\">{title}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\"conversations.comingSoon\", \"Coming soon.\")}\n </p>\n </div>\n </div>\n );\n}\n\n/**\n * Tabbed landing page for the Conversations microfrontend.\n *\n * Renders an underline tab bar at the top and swaps the active panel\n * based on the `?tab=...` URL search param. The first tab (\"all\") uses\n * a clean URL with no `tab` param.\n */\nexport function ConversationsLandingPage() {\n const [params, setParams] = useSearchParams();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const { archivedEnabled } = useUserFeatureFlags();\n\n const tabs: TabItem[] = useMemo(\n () => [\n {\n id: DEFAULT_TAB_ID,\n label: tr(\"conversations.allConversations\", \"All Conversations\"),\n },\n { id: \"channels\", label: tr(\"conversations.channels\", \"Channels\") },\n {\n id: \"direct\",\n label: tr(\"conversations.directMessages\", \"Direct Messages\"),\n },\n { id: \"groups\", label: tr(\"conversations.groups\", \"Groups\") },\n ...(archivedEnabled\n ? [{ id: \"archived\", label: tr(\"conversations.archived\", \"Archived\") }]\n : []),\n { id: \"new\", label: tr(\"conversations.new\", \"New\") },\n { id: \"settings\", label: tr(\"conversations.settings\", \"Settings\") },\n ],\n // Suppression is for `tr`, which closes over `t`; `archivedEnabled` IS listed.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [t, archivedEnabled],\n );\n\n const currentTab = params.get(\"tab\");\n // Render `activeId` correctly for the archived-off case; the effect below cleans the URL lazily.\n const activeId =\n currentTab === \"archived\" && !archivedEnabled\n ? DEFAULT_TAB_ID\n : (currentTab ?? DEFAULT_TAB_ID);\n\n useEffect(() => {\n if (!archivedEnabled && currentTab === \"archived\") {\n // Updater form reads the freshest params at flush time to preserve concurrent query-param changes.\n setParams(\n (prev) => {\n const next = new URLSearchParams(prev);\n next.delete(\"tab\");\n return next;\n },\n { replace: true },\n );\n }\n }, [archivedEnabled, currentTab, setParams]);\n\n const handleTabChange = (id: string) => {\n setParams(\n (prev) => {\n const next = new URLSearchParams(prev);\n if (id === DEFAULT_TAB_ID) {\n next.delete(\"tab\");\n } else {\n next.set(\"tab\", id);\n }\n return next;\n },\n { replace: true },\n );\n };\n\n return (\n <PermissionGuard\n permission={CONVERSATIONS_PERMISSIONS.CONVERSATION_READ}\n allowUnavailablePermissions\n >\n <main\n data-tour=\"conversations-landing\"\n className=\"flex flex-col h-full\"\n aria-labelledby=\"conversations-page-heading\"\n >\n <h1 id=\"conversations-page-heading\" className=\"sr-only\">\n {tr(\"conversations.title\", \"Conversations\")}\n </h1>\n <div\n data-tour=\"conversations-tabs\"\n className=\"border-b border-border-subtle px-4 pt-3\"\n >\n <Tabs\n items={tabs}\n activeTabId={activeId}\n onTabChange={handleTabChange}\n variant=\"underline\"\n />\n </div>\n <div\n className=\"flex-1 min-h-0 overflow-auto\"\n role=\"region\"\n aria-live=\"polite\"\n aria-label={tr(\n \"conversations.activePanel\",\n \"Active conversations panel\",\n )}\n >\n {activeId === \"all\" && <ConversationListPage />}\n {activeId === \"channels\" && (\n <ConversationListPage forcedFilterParam=\"channel\" />\n )}\n {activeId === \"direct\" && (\n <ConversationListPage forcedFilterParam=\"direct\" />\n )}\n {activeId === \"groups\" && (\n <ConversationListPage forcedFilterParam=\"group\" />\n )}\n {activeId === \"archived\" && (\n <ComingSoon\n title={tr(\n \"conversations.archivedTitle\",\n \"Archived Conversations\",\n )}\n />\n )}\n {activeId === \"new\" && <NewConversationPage />}\n {activeId === \"settings\" && <ConversationsSettingsPage />}\n </div>\n </main>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ConversationsLandingPage.js","names":[],"sources":["../../../src/conversations/pages/ConversationsLandingPage.tsx"],"sourcesContent":["import { useEffect, useMemo } from \"react\";\nimport { useSearchParams } from \"react-router-dom\";\nimport { Tabs, type TabItem } from \"@burdenoff/fe-libs/chrome\";\nimport { ConversationListPage } from \"./ConversationListPage\";\nimport { NewConversationPage } from \"./NewConversationPage\";\nimport { ConversationsSettingsPage } from \"./ConversationsSettingsPage\";\nimport { PermissionGuard } from \"../components/PermissionGuard\";\nimport { CONVERSATIONS_PERMISSIONS } from \"../constants/permissions\";\nimport { useUserFeatureFlags } from \"../hooks/useUserFeatureFlags\";\nimport { useTr } from \"../utils/i18n\";\n\nconst DEFAULT_TAB_ID = \"all\";\n\n/**\n * Coming-soon placeholder shown for tabs whose dedicated pages\n * are not yet implemented in this microfrontend.\n */\nfunction ComingSoon({ title }: { title: string }) {\n const tr = useTr();\n\n return (\n <div className=\"flex h-full items-center justify-center p-8 text-center\">\n <div className=\"space-y-2\">\n <h3 className=\"text-lg font-semibold text-foreground\">{title}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\"conversations.comingSoon\", \"Coming soon.\")}\n </p>\n </div>\n </div>\n );\n}\n\n/**\n * Tabbed landing page for the Conversations microfrontend.\n *\n * Renders an underline tab bar at the top and swaps the active panel\n * based on the `?tab=...` URL search param. The first tab (\"all\") uses\n * a clean URL with no `tab` param.\n */\nexport function ConversationsLandingPage() {\n const [params, setParams] = useSearchParams();\n const tr = useTr();\n\n const { archivedEnabled } = useUserFeatureFlags();\n\n const tabs: TabItem[] = useMemo(\n () => [\n {\n id: DEFAULT_TAB_ID,\n label: tr(\"conversations.allConversations\", \"All Conversations\"),\n },\n { id: \"channels\", label: tr(\"conversations.channels\", \"Channels\") },\n {\n id: \"direct\",\n label: tr(\"conversations.directMessages\", \"Direct Messages\"),\n },\n { id: \"groups\", label: tr(\"conversations.groups\", \"Groups\") },\n ...(archivedEnabled\n ? [{ id: \"archived\", label: tr(\"conversations.archived\", \"Archived\") }]\n : []),\n { id: \"new\", label: tr(\"conversations.new\", \"New\") },\n { id: \"settings\", label: tr(\"conversations.settings\", \"Settings\") },\n ],\n [tr, archivedEnabled],\n );\n\n const currentTab = params.get(\"tab\");\n // Render `activeId` correctly for the archived-off case; the effect below cleans the URL lazily.\n const activeId =\n currentTab === \"archived\" && !archivedEnabled\n ? DEFAULT_TAB_ID\n : (currentTab ?? DEFAULT_TAB_ID);\n\n useEffect(() => {\n if (!archivedEnabled && currentTab === \"archived\") {\n // Updater form reads the freshest params at flush time to preserve concurrent query-param changes.\n setParams(\n (prev) => {\n const next = new URLSearchParams(prev);\n next.delete(\"tab\");\n return next;\n },\n { replace: true },\n );\n }\n }, [archivedEnabled, currentTab, setParams]);\n\n const handleTabChange = (id: string) => {\n setParams(\n (prev) => {\n const next = new URLSearchParams(prev);\n if (id === DEFAULT_TAB_ID) {\n next.delete(\"tab\");\n } else {\n next.set(\"tab\", id);\n }\n return next;\n },\n { replace: true },\n );\n };\n\n return (\n <PermissionGuard\n permission={CONVERSATIONS_PERMISSIONS.CONVERSATION_READ}\n allowUnavailablePermissions\n >\n <main\n data-tour=\"conversations-landing\"\n className=\"flex flex-col h-full\"\n aria-labelledby=\"conversations-page-heading\"\n >\n <h1 id=\"conversations-page-heading\" className=\"sr-only\">\n {tr(\"conversations.title\", \"Conversations\")}\n </h1>\n <div\n data-tour=\"conversations-tabs\"\n className=\"border-b border-border-subtle px-4 pt-3\"\n >\n <Tabs\n items={tabs}\n activeTabId={activeId}\n onTabChange={handleTabChange}\n variant=\"underline\"\n />\n </div>\n <section\n className=\"flex-1 min-h-0 overflow-auto\"\n aria-live=\"polite\"\n aria-label={tr(\n \"conversations.activePanel\",\n \"Active conversations panel\",\n )}\n >\n {activeId === \"all\" && <ConversationListPage />}\n {activeId === \"channels\" && (\n <ConversationListPage forcedFilterParam=\"channel\" />\n )}\n {activeId === \"direct\" && (\n <ConversationListPage forcedFilterParam=\"direct\" />\n )}\n {activeId === \"groups\" && (\n <ConversationListPage forcedFilterParam=\"group\" />\n )}\n {activeId === \"archived\" && (\n <ComingSoon\n title={tr(\n \"conversations.archivedTitle\",\n \"Archived Conversations\",\n )}\n />\n )}\n {activeId === \"new\" && <NewConversationPage />}\n {activeId === \"settings\" && <ConversationsSettingsPage />}\n </section>\n </main>\n </PermissionGuard>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAWA,IAAM,IAAiB;AAMvB,SAAS,EAAW,EAAE,YAA4B;CAChD,IAAM,IAAK,GAAO;AAElB,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cAAyC;IAAW,CAAA,EAClE,kBAAC,KAAD;IAAG,WAAU;cACV,EAAG,4BAA4B,eAAe;IAC7C,CAAA,CACA;;EACF,CAAA;;AAWV,SAAgB,IAA2B;CACzC,IAAM,CAAC,GAAQ,KAAa,GAAiB,EACvC,IAAK,GAAO,EAEZ,EAAE,uBAAoB,GAAqB,EAE3C,IAAkB,QAChB;EACJ;GACE,IAAI;GACJ,OAAO,EAAG,kCAAkC,oBAAoB;GACjE;EACD;GAAE,IAAI;GAAY,OAAO,EAAG,0BAA0B,WAAW;GAAE;EACnE;GACE,IAAI;GACJ,OAAO,EAAG,gCAAgC,kBAAkB;GAC7D;EACD;GAAE,IAAI;GAAU,OAAO,EAAG,wBAAwB,SAAS;GAAE;EAC7D,GAAI,IACA,CAAC;GAAE,IAAI;GAAY,OAAO,EAAG,0BAA0B,WAAW;GAAE,CAAC,GACrE,EAAE;EACN;GAAE,IAAI;GAAO,OAAO,EAAG,qBAAqB,MAAM;GAAE;EACpD;GAAE,IAAI;GAAY,OAAO,EAAG,0BAA0B,WAAW;GAAE;EACpE,EACD,CAAC,GAAI,EAAgB,CACtB,EAEK,IAAa,EAAO,IAAI,MAAM,EAE9B,IACJ,MAAe,cAAc,CAAC,IAC1B,IACC,KAAc;AA+BrB,QA7BA,QAAgB;AACd,EAAI,CAAC,KAAmB,MAAe,cAErC,GACG,MAAS;GACR,IAAM,IAAO,IAAI,gBAAgB,EAAK;AAEtC,UADA,EAAK,OAAO,MAAM,EACX;KAET,EAAE,SAAS,IAAM,CAClB;IAEF;EAAC;EAAiB;EAAY;EAAU,CAAC,EAkB1C,kBAAC,GAAD;EACE,YAAY,EAA0B;EACtC,6BAAA;YAEA,kBAAC,QAAD;GACE,aAAU;GACV,WAAU;GACV,mBAAgB;aAHlB;IAKE,kBAAC,MAAD;KAAI,IAAG;KAA6B,WAAU;eAC3C,EAAG,uBAAuB,gBAAgB;KACxC,CAAA;IACL,kBAAC,OAAD;KACE,aAAU;KACV,WAAU;eAEV,kBAAC,GAAD;MACE,OAAO;MACP,aAAa;MACb,cAnCe,MAAe;AACtC,UACG,MAAS;QACR,IAAM,IAAO,IAAI,gBAAgB,EAAK;AAMtC,eALI,MAAO,IACT,EAAK,OAAO,MAAM,GAElB,EAAK,IAAI,OAAO,EAAG,EAEd;UAET,EAAE,SAAS,IAAM,CAClB;;MAwBO,SAAQ;MACR,CAAA;KACE,CAAA;IACN,kBAAC,WAAD;KACE,WAAU;KACV,aAAU;KACV,cAAY,EACV,6BACA,6BACD;eANH;MAQG,MAAa,SAAS,kBAAC,GAAD,EAAwB,CAAA;MAC9C,MAAa,cACZ,kBAAC,GAAD,EAAsB,mBAAkB,WAAY,CAAA;MAErD,MAAa,YACZ,kBAAC,GAAD,EAAsB,mBAAkB,UAAW,CAAA;MAEpD,MAAa,YACZ,kBAAC,GAAD,EAAsB,mBAAkB,SAAU,CAAA;MAEnD,MAAa,cACZ,kBAAC,GAAD,EACE,OAAO,EACL,+BACA,yBACD,EACD,CAAA;MAEH,MAAa,SAAS,kBAAC,GAAD,EAAuB,CAAA;MAC7C,MAAa,cAAc,kBAAC,GAAD,EAA6B,CAAA;MACjD;;IACL;;EACS,CAAA"}
@@ -1,5 +1,6 @@
1
1
  import { useConversations as e } from "../providers/ConversationsProvider.js";
2
- import { PageLayout as t, PageSection as n } from "../components/PageLayout.js";
2
+ import { PageSection as t } from "../components/PageSection.js";
3
+ import { PageLayout as n } from "../components/PageLayout.js";
3
4
  import { useConversationsStore as r } from "../store/conversationsStore.js";
4
5
  import { useGetConversation as i, useUpdateConversation as a } from "../hooks/useConversationQueries.js";
5
6
  import { PermissionGuard as o } from "../components/PermissionGuard.js";
@@ -39,7 +40,7 @@ function T() {
39
40
  }
40
41
  }
41
42
  };
42
- return /* @__PURE__ */ S(t, {
43
+ return /* @__PURE__ */ S(n, {
43
44
  dataTour: "conversation-settings-page",
44
45
  title: A("conversations.settings", "Settings"),
45
46
  subtitle: j ? `${A("conversations.settingsFor", "Conversation settings for")} ${j.title}` : A("conversations.selectConversationForSettings", "Select a conversation to edit settings"),
@@ -64,7 +65,7 @@ function T() {
64
65
  className: "text-sm text-muted-foreground",
65
66
  children: A("conversations.loadingSettings", "Loading settings...")
66
67
  }) : j ? /* @__PURE__ */ C(x, { children: [
67
- /* @__PURE__ */ S(n, {
68
+ /* @__PURE__ */ S(t, {
68
69
  title: A("conversations.notifications", "Notifications"),
69
70
  description: A("conversations.notificationsDescription", "Configure delivery behavior for this conversation"),
70
71
  children: /* @__PURE__ */ S("div", {
@@ -109,7 +110,7 @@ function T() {
109
110
  })
110
111
  })
111
112
  }),
112
- /* @__PURE__ */ S(n, {
113
+ /* @__PURE__ */ S(t, {
113
114
  title: A("conversations.messageBehavior", "Message Behavior"),
114
115
  description: A("conversations.messageBehaviorDescription", "Tune read and typing experience"),
115
116
  children: /* @__PURE__ */ C("div", {
@@ -129,7 +130,7 @@ function T() {
129
130
  })]
130
131
  })
131
132
  }),
132
- /* @__PURE__ */ S(n, {
133
+ /* @__PURE__ */ S(t, {
133
134
  title: A("conversations.exportData", "Export Data"),
134
135
  description: A("conversations.exportDataDescription", "Download conversation comments in your preferred format"),
135
136
  children: /* @__PURE__ */ C("div", {
@@ -161,6 +162,8 @@ function T() {
161
162
  }),
162
163
  /* @__PURE__ */ S("a", {
163
164
  ref: W,
165
+ href: "#download",
166
+ tabIndex: -1,
164
167
  className: "hidden",
165
168
  "aria-hidden": "true"
166
169
  })
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationsSettingsPage.js","names":[],"sources":["../../../src/conversations/pages/ConversationsSettingsPage.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useNavigate } from \"react-router-dom\";\nimport { ArrowLeft, Bell, Download, Eye, Save, Settings } from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n useGetConversation,\n useUpdateConversation,\n} from \"../hooks/useConversationQueries\";\nimport { useExportConversation } from \"../hooks/useExportConversation\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { useConversationsStore } from \"../store/conversationsStore\";\nimport { PermissionGuard } from \"../components/PermissionGuard\";\nimport { CONVERSATIONS_PERMISSIONS } from \"../constants/permissions\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\nexport function ConversationsSettingsPage() {\n const navigate = useNavigate();\n const { basePath } = useConversations();\n const { activeConversationId } = useConversationsStore();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { conversation, loading: conversationLoading } = useGetConversation(\n activeConversationId ?? \"\",\n );\n const {\n updateConversation,\n loading: saving,\n error: saveError,\n } = useUpdateConversation();\n\n const [showTypingIndicators, setShowTypingIndicators] = useState(true);\n const [showReadReceipts, setShowReadReceipts] = useState(true);\n const [defaultNotificationLevel, setDefaultNotificationLevel] =\n useState(\"ALL\");\n const [saved, setSaved] = useState(false);\n const downloadLinkRef = useRef<HTMLAnchorElement>(null);\n const {\n exportComments,\n loading: exporting,\n downloadUrl,\n } = useExportConversation();\n\n useEffect(() => {\n const settings =\n conversation?.settings && typeof conversation.settings === \"object\"\n ? (conversation.settings as Record<string, unknown>)\n : null;\n if (!settings) return;\n if (typeof settings.showTypingIndicators === \"boolean\") {\n setShowTypingIndicators(settings.showTypingIndicators);\n }\n if (typeof settings.showReadReceipts === \"boolean\") {\n setShowReadReceipts(settings.showReadReceipts);\n }\n if (typeof settings.defaultNotificationLevel === \"string\") {\n setDefaultNotificationLevel(settings.defaultNotificationLevel);\n }\n }, [conversation]);\n\n const handleSave = async () => {\n if (!conversation?.id) return;\n nativeImpact(\"light\");\n try {\n const updated = await updateConversation({\n id: conversation.id,\n settings: {\n showTypingIndicators,\n showReadReceipts,\n defaultNotificationLevel,\n },\n });\n const ok = Boolean(updated);\n nativeNotify(ok ? \"success\" : \"error\");\n setSaved(ok);\n } catch (error) {\n nativeNotify(\"error\");\n throw error;\n }\n };\n\n return (\n <PageLayout\n dataTour=\"conversation-settings-page\"\n title={tr(\"conversations.settings\", \"Settings\")}\n subtitle={\n conversation\n ? `${tr(\"conversations.settingsFor\", \"Conversation settings for\")} ${conversation.title}`\n : tr(\n \"conversations.selectConversationForSettings\",\n \"Select a conversation to edit settings\",\n )\n }\n icon={<Settings size={24} />}\n maxWidth=\"lg\"\n headerActions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigate(basePath || \"/conversations\")}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n <ArrowLeft size={16} />\n {tr(\"conversations.back\", \"Back\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleSave()}\n disabled={saving || !conversation}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors shadow-sm disabled:opacity-50\"\n >\n <Save size={16} />\n {saving\n ? tr(\"conversations.saving\", \"Saving...\")\n : tr(\"conversations.saveChanges\", \"Save Changes\")}\n </button>\n </div>\n }\n >\n {conversationLoading ? (\n <div className=\"text-sm text-muted-foreground\">\n {tr(\"conversations.loadingSettings\", \"Loading settings...\")}\n </div>\n ) : !conversation ? (\n <div className=\"text-sm text-muted-foreground\">\n {tr(\n \"conversations.openConversationFirst\",\n \"Open a conversation first, then come back to settings.\",\n )}\n </div>\n ) : (\n <>\n <PageSection\n title={tr(\"conversations.notifications\", \"Notifications\")}\n description={tr(\n \"conversations.notificationsDescription\",\n \"Configure delivery behavior for this conversation\",\n )}\n >\n <div className=\"space-y-4 rounded-xl border border-border p-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"p-2 rounded-lg bg-status-info-bg-subtle text-status-info-text\">\n <Bell size={16} />\n </div>\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {tr(\n \"conversations.defaultNotificationLevel\",\n \"Default Notification Level\",\n )}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {tr(\n \"conversations.defaultNotificationLevelHint\",\n \"This updates conversation settings JSON via GraphQL.\",\n )}\n </p>\n </div>\n </div>\n <select\n value={defaultNotificationLevel}\n onChange={(event) =>\n setDefaultNotificationLevel(event.target.value)\n }\n className=\"px-3 py-1.5 text-sm rounded-lg border border-border bg-background focus:outline-none focus:ring-2 focus:ring-ring\"\n >\n <option value=\"ALL\">\n {tr(\"conversations.allMessages\", \"All Messages\")}\n </option>\n <option value=\"MENTIONS\">\n {tr(\"conversations.mentionsOnly\", \"Mentions Only\")}\n </option>\n <option value=\"DIRECT\">\n {tr(\"conversations.directOnly\", \"Direct Only\")}\n </option>\n <option value=\"NONE\">\n {tr(\"conversations.none\", \"None\")}\n </option>\n </select>\n </div>\n </div>\n </PageSection>\n\n <PageSection\n title={tr(\"conversations.messageBehavior\", \"Message Behavior\")}\n description={tr(\n \"conversations.messageBehaviorDescription\",\n \"Tune read and typing experience\",\n )}\n >\n <div className=\"space-y-4 rounded-xl border border-border p-4\">\n <ToggleSetting\n icon={<Eye size={16} />}\n label={tr(\n \"conversations.typingIndicators\",\n \"Typing Indicators\",\n )}\n description={tr(\n \"conversations.typingIndicatorsDescription\",\n \"Show others when someone is typing\",\n )}\n checked={showTypingIndicators}\n onChange={setShowTypingIndicators}\n />\n <ToggleSetting\n icon={<Eye size={16} />}\n label={tr(\"conversations.readReceipts\", \"Read Receipts\")}\n description={tr(\n \"conversations.readReceiptsDescription\",\n \"Display read status for conversation participants\",\n )}\n checked={showReadReceipts}\n onChange={setShowReadReceipts}\n />\n </div>\n </PageSection>\n\n <PageSection\n title={tr(\"conversations.exportData\", \"Export Data\")}\n description={tr(\n \"conversations.exportDataDescription\",\n \"Download conversation comments in your preferred format\",\n )}\n >\n <div className=\"space-y-3 rounded-xl border border-border p-4\">\n <div className=\"flex flex-wrap gap-2\">\n {([\"JSON\", \"CSV\", \"MARKDOWN\"] as const).map((format) => (\n <PermissionGuard\n key={format}\n permission={CONVERSATIONS_PERMISSIONS.CONVERSATION_READ}\n >\n <button\n type=\"button\"\n disabled={exporting || !conversation}\n onClick={async () => {\n const url = await exportComments(\n \"conversation\",\n conversation!.id,\n format,\n );\n if (url && downloadLinkRef.current) {\n downloadLinkRef.current.href = url;\n downloadLinkRef.current.download = `conversation-${conversation!.id}.${format.toLowerCase()}`;\n downloadLinkRef.current.click();\n }\n }}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors disabled:opacity-50\"\n >\n <Download size={12} />\n {exporting\n ? tr(\"conversations.exporting\", \"Exporting…\")\n : format}\n </button>\n </PermissionGuard>\n ))}\n </div>\n {downloadUrl && (\n <p className=\"text-xs text-status-success-text\">\n {tr(\n \"conversations.exportReady\",\n \"Export ready — downloading…\",\n )}\n </p>\n )}\n {/* Hidden anchor driven programmatically for download trigger */}\n <a ref={downloadLinkRef} className=\"hidden\" aria-hidden=\"true\" />\n </div>\n </PageSection>\n\n {saveError && (\n <div className=\"text-xs text-destructive rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2\">\n {saveError.message}\n </div>\n )}\n {saved && !saveError && (\n <div className=\"text-xs text-status-success-text rounded-lg border border-border-default bg-status-success-bg-subtle px-3 py-2\">\n {tr(\"conversations.settingsSaved\", \"Settings saved.\")}\n </div>\n )}\n </>\n )}\n </PageLayout>\n );\n}\n\nfunction ToggleSetting({\n icon,\n label,\n description,\n checked,\n onChange,\n}: {\n icon: React.ReactNode;\n label: string;\n description: string;\n checked: boolean;\n onChange: (value: boolean) => void;\n}) {\n return (\n <PermissionGuard permission={CONVERSATIONS_PERMISSIONS.CONVERSATION_UPDATE}>\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"p-2 rounded-lg bg-muted text-muted-foreground\">\n {icon}\n </div>\n <div>\n <p className=\"text-sm font-medium text-foreground\">{label}</p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {description}\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={label}\n onClick={() => onChange(!checked)}\n className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${checked ? \"bg-primary\" : \"bg-muted\"}`}\n >\n <span\n className={`pointer-events-none inline-block h-5 w-5 rounded-full bg-background shadow transition ${checked ? \"translate-x-5\" : \"translate-x-0\"}`}\n />\n </button>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ConversationsSettingsPage.js","names":[],"sources":["../../../src/conversations/pages/ConversationsSettingsPage.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useNavigate } from \"react-router-dom\";\nimport { ArrowLeft, Bell, Download, Eye, Save, Settings } from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n useGetConversation,\n useUpdateConversation,\n} from \"../hooks/useConversationQueries\";\nimport { useExportConversation } from \"../hooks/useExportConversation\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { useConversationsStore } from \"../store/conversationsStore\";\nimport { PermissionGuard } from \"../components/PermissionGuard\";\nimport { CONVERSATIONS_PERMISSIONS } from \"../constants/permissions\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\nexport function ConversationsSettingsPage() {\n const navigate = useNavigate();\n const { basePath } = useConversations();\n const { activeConversationId } = useConversationsStore();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { conversation, loading: conversationLoading } = useGetConversation(\n activeConversationId ?? \"\",\n );\n const {\n updateConversation,\n loading: saving,\n error: saveError,\n } = useUpdateConversation();\n\n const [showTypingIndicators, setShowTypingIndicators] = useState(true);\n const [showReadReceipts, setShowReadReceipts] = useState(true);\n const [defaultNotificationLevel, setDefaultNotificationLevel] =\n useState(\"ALL\");\n const [saved, setSaved] = useState(false);\n const downloadLinkRef = useRef<HTMLAnchorElement>(null);\n const {\n exportComments,\n loading: exporting,\n downloadUrl,\n } = useExportConversation();\n\n useEffect(() => {\n const settings =\n conversation?.settings && typeof conversation.settings === \"object\"\n ? (conversation.settings as Record<string, unknown>)\n : null;\n if (!settings) return;\n if (typeof settings.showTypingIndicators === \"boolean\") {\n setShowTypingIndicators(settings.showTypingIndicators);\n }\n if (typeof settings.showReadReceipts === \"boolean\") {\n setShowReadReceipts(settings.showReadReceipts);\n }\n if (typeof settings.defaultNotificationLevel === \"string\") {\n setDefaultNotificationLevel(settings.defaultNotificationLevel);\n }\n }, [conversation]);\n\n const handleSave = async () => {\n if (!conversation?.id) return;\n nativeImpact(\"light\");\n try {\n const updated = await updateConversation({\n id: conversation.id,\n settings: {\n showTypingIndicators,\n showReadReceipts,\n defaultNotificationLevel,\n },\n });\n const ok = Boolean(updated);\n nativeNotify(ok ? \"success\" : \"error\");\n setSaved(ok);\n } catch (error) {\n nativeNotify(\"error\");\n throw error;\n }\n };\n\n return (\n <PageLayout\n dataTour=\"conversation-settings-page\"\n title={tr(\"conversations.settings\", \"Settings\")}\n subtitle={\n conversation\n ? `${tr(\"conversations.settingsFor\", \"Conversation settings for\")} ${conversation.title}`\n : tr(\n \"conversations.selectConversationForSettings\",\n \"Select a conversation to edit settings\",\n )\n }\n icon={<Settings size={24} />}\n maxWidth=\"lg\"\n headerActions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigate(basePath || \"/conversations\")}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n <ArrowLeft size={16} />\n {tr(\"conversations.back\", \"Back\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleSave()}\n disabled={saving || !conversation}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors shadow-sm disabled:opacity-50\"\n >\n <Save size={16} />\n {saving\n ? tr(\"conversations.saving\", \"Saving...\")\n : tr(\"conversations.saveChanges\", \"Save Changes\")}\n </button>\n </div>\n }\n >\n {conversationLoading ? (\n <div className=\"text-sm text-muted-foreground\">\n {tr(\"conversations.loadingSettings\", \"Loading settings...\")}\n </div>\n ) : !conversation ? (\n <div className=\"text-sm text-muted-foreground\">\n {tr(\n \"conversations.openConversationFirst\",\n \"Open a conversation first, then come back to settings.\",\n )}\n </div>\n ) : (\n <>\n <PageSection\n title={tr(\"conversations.notifications\", \"Notifications\")}\n description={tr(\n \"conversations.notificationsDescription\",\n \"Configure delivery behavior for this conversation\",\n )}\n >\n <div className=\"space-y-4 rounded-xl border border-border p-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"p-2 rounded-lg bg-status-info-bg-subtle text-status-info-text\">\n <Bell size={16} />\n </div>\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {tr(\n \"conversations.defaultNotificationLevel\",\n \"Default Notification Level\",\n )}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {tr(\n \"conversations.defaultNotificationLevelHint\",\n \"This updates conversation settings JSON via GraphQL.\",\n )}\n </p>\n </div>\n </div>\n <select\n value={defaultNotificationLevel}\n onChange={(event) =>\n setDefaultNotificationLevel(event.target.value)\n }\n className=\"px-3 py-1.5 text-sm rounded-lg border border-border bg-background focus:outline-none focus:ring-2 focus:ring-ring\"\n >\n <option value=\"ALL\">\n {tr(\"conversations.allMessages\", \"All Messages\")}\n </option>\n <option value=\"MENTIONS\">\n {tr(\"conversations.mentionsOnly\", \"Mentions Only\")}\n </option>\n <option value=\"DIRECT\">\n {tr(\"conversations.directOnly\", \"Direct Only\")}\n </option>\n <option value=\"NONE\">\n {tr(\"conversations.none\", \"None\")}\n </option>\n </select>\n </div>\n </div>\n </PageSection>\n\n <PageSection\n title={tr(\"conversations.messageBehavior\", \"Message Behavior\")}\n description={tr(\n \"conversations.messageBehaviorDescription\",\n \"Tune read and typing experience\",\n )}\n >\n <div className=\"space-y-4 rounded-xl border border-border p-4\">\n <ToggleSetting\n icon={<Eye size={16} />}\n label={tr(\n \"conversations.typingIndicators\",\n \"Typing Indicators\",\n )}\n description={tr(\n \"conversations.typingIndicatorsDescription\",\n \"Show others when someone is typing\",\n )}\n checked={showTypingIndicators}\n onChange={setShowTypingIndicators}\n />\n <ToggleSetting\n icon={<Eye size={16} />}\n label={tr(\"conversations.readReceipts\", \"Read Receipts\")}\n description={tr(\n \"conversations.readReceiptsDescription\",\n \"Display read status for conversation participants\",\n )}\n checked={showReadReceipts}\n onChange={setShowReadReceipts}\n />\n </div>\n </PageSection>\n\n <PageSection\n title={tr(\"conversations.exportData\", \"Export Data\")}\n description={tr(\n \"conversations.exportDataDescription\",\n \"Download conversation comments in your preferred format\",\n )}\n >\n <div className=\"space-y-3 rounded-xl border border-border p-4\">\n <div className=\"flex flex-wrap gap-2\">\n {([\"JSON\", \"CSV\", \"MARKDOWN\"] as const).map((format) => (\n <PermissionGuard\n key={format}\n permission={CONVERSATIONS_PERMISSIONS.CONVERSATION_READ}\n >\n <button\n type=\"button\"\n disabled={exporting || !conversation}\n onClick={async () => {\n const url = await exportComments(\n \"conversation\",\n conversation!.id,\n format,\n );\n if (url && downloadLinkRef.current) {\n downloadLinkRef.current.href = url;\n downloadLinkRef.current.download = `conversation-${conversation!.id}.${format.toLowerCase()}`;\n downloadLinkRef.current.click();\n }\n }}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors disabled:opacity-50\"\n >\n <Download size={12} />\n {exporting\n ? tr(\"conversations.exporting\", \"Exporting…\")\n : format}\n </button>\n </PermissionGuard>\n ))}\n </div>\n {downloadUrl && (\n <p className=\"text-xs text-status-success-text\">\n {tr(\n \"conversations.exportReady\",\n \"Export ready — downloading…\",\n )}\n </p>\n )}\n {/* Hidden anchor driven programmatically for download trigger.\n href is set imperatively before .click() — placeholder satisfies\n anchor-is-valid; tabIndex=-1 keeps it out of focus order. */}\n <a\n ref={downloadLinkRef}\n href=\"#download\"\n tabIndex={-1}\n className=\"hidden\"\n aria-hidden=\"true\"\n />\n </div>\n </PageSection>\n\n {saveError && (\n <div className=\"text-xs text-destructive rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2\">\n {saveError.message}\n </div>\n )}\n {saved && !saveError && (\n <div className=\"text-xs text-status-success-text rounded-lg border border-border-default bg-status-success-bg-subtle px-3 py-2\">\n {tr(\"conversations.settingsSaved\", \"Settings saved.\")}\n </div>\n )}\n </>\n )}\n </PageLayout>\n );\n}\n\nfunction ToggleSetting({\n icon,\n label,\n description,\n checked,\n onChange,\n}: {\n icon: React.ReactNode;\n label: string;\n description: string;\n checked: boolean;\n onChange: (value: boolean) => void;\n}) {\n return (\n <PermissionGuard permission={CONVERSATIONS_PERMISSIONS.CONVERSATION_UPDATE}>\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"p-2 rounded-lg bg-muted text-muted-foreground\">\n {icon}\n </div>\n <div>\n <p className=\"text-sm font-medium text-foreground\">{label}</p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {description}\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={label}\n onClick={() => onChange(!checked)}\n className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${checked ? \"bg-primary\" : \"bg-muted\"}`}\n >\n <span\n className={`pointer-events-none inline-block h-5 w-5 rounded-full bg-background shadow transition ${checked ? \"translate-x-5\" : \"translate-x-0\"}`}\n />\n </button>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}