@burdenoff/microfe-conversations 2026.624.1 → 2026.625.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.
@@ -12,82 +12,83 @@ import { useEffect as f, useMemo as p, useRef as m, useState as h } from "react"
12
12
  import { ArrowLeft as g, Bell as _, Download as v, Eye as y, Save as b, Settings as x } from "lucide-react";
13
13
  import { useNavigate as S } from "react-router-dom";
14
14
  import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
15
- import { Breadcrumbs as E } from "@burdenoff/fe-libs/chrome";
16
- import { Button as D, PageContainer as O } from "@burdenoff/fe-libs/ui";
15
+ import { Button as E, GlassCard as D, PageContainer as O } from "@burdenoff/fe-libs/ui";
16
+ import { Breadcrumbs as k } from "@burdenoff/fe-libs/chrome";
17
17
  //#region src/conversations/pages/ConversationsSettingsPage.tsx
18
- function k() {
19
- let k = S(), { basePath: j } = e(), { activeConversationId: M } = i(), N = t(), { conversation: P, loading: F } = a(M ?? ""), { updateConversation: I, loading: L, error: R } = o(), [z, B] = h(!0), [V, H] = h(!0), [U, W] = h("ALL"), [G, K] = h(!1), q = m(null), { exportComments: J, loading: Y, downloadUrl: X } = d();
18
+ function A() {
19
+ let A = S(), { basePath: M } = e(), { activeConversationId: N } = i(), P = t(), { conversation: F, loading: I } = a(N ?? ""), { updateConversation: L, loading: R, error: z } = o(), [B, V] = h(!0), [H, U] = h(!0), [W, G] = h("ALL"), [K, q] = h(!1), J = m(null), { exportComments: Y, loading: X, downloadUrl: Z } = d();
20
20
  f(() => {
21
- let e = P?.settings && typeof P.settings == "object" ? P.settings : null;
22
- e && (typeof e.showTypingIndicators == "boolean" && B(e.showTypingIndicators), typeof e.showReadReceipts == "boolean" && H(e.showReadReceipts), typeof e.defaultNotificationLevel == "string" && W(e.defaultNotificationLevel));
23
- }, [P]);
24
- let Z = async () => {
25
- if (P?.id) {
21
+ let e = F?.settings && typeof F.settings == "object" ? F.settings : null;
22
+ e && (typeof e.showTypingIndicators == "boolean" && V(e.showTypingIndicators), typeof e.showReadReceipts == "boolean" && U(e.showReadReceipts), typeof e.defaultNotificationLevel == "string" && G(e.defaultNotificationLevel));
23
+ }, [F]);
24
+ let Q = async () => {
25
+ if (F?.id) {
26
26
  l("light");
27
27
  try {
28
- let e = !!await I({
29
- id: P.id,
28
+ let e = !!await L({
29
+ id: F.id,
30
30
  settings: {
31
- showTypingIndicators: z,
32
- showReadReceipts: V,
33
- defaultNotificationLevel: U
31
+ showTypingIndicators: B,
32
+ showReadReceipts: H,
33
+ defaultNotificationLevel: W
34
34
  }
35
35
  });
36
- u(e ? "success" : "error"), K(e);
36
+ u(e ? "success" : "error"), q(e);
37
37
  } catch (e) {
38
38
  throw u("error"), e;
39
39
  }
40
40
  }
41
- }, Q = j || "/conversations";
41
+ }, $ = M || "/conversations";
42
42
  return /* @__PURE__ */ T(O, {
43
43
  padded: !1,
44
44
  className: "h-full min-h-0",
45
45
  children: [/* @__PURE__ */ w("div", {
46
46
  className: "border-b border-border-subtle bg-bg-surface px-4 py-3 sm:px-6",
47
- children: /* @__PURE__ */ w(E, { items: p(() => [{
48
- label: N("conversations.title", "Conversations"),
49
- href: Q,
50
- onClick: () => k(Q)
47
+ children: /* @__PURE__ */ w(k, { items: p(() => [{
48
+ label: P("conversations.title", "Conversations"),
49
+ href: $,
50
+ onClick: () => A($)
51
51
  }, {
52
- label: N("conversations.settings", "Settings"),
52
+ label: P("conversations.settings", "Settings"),
53
53
  isCurrentPage: !0
54
54
  }], [
55
- Q,
56
- k,
57
- N
55
+ $,
56
+ A,
57
+ P
58
58
  ]) })
59
59
  }), /* @__PURE__ */ w(r, {
60
60
  dataTour: "conversation-settings-page",
61
- title: N("conversations.settings", "Settings"),
62
- subtitle: P ? `${N("conversations.settingsFor", "Conversation settings for")} ${P.title}` : N("conversations.selectConversationForSettings", "Select a conversation to edit settings"),
61
+ title: P("conversations.settings", "Settings"),
62
+ subtitle: F ? `${P("conversations.settingsFor", "Conversation settings for")} ${F.title}` : P("conversations.selectConversationForSettings", "Select a conversation to edit settings"),
63
63
  icon: /* @__PURE__ */ w(x, { size: 24 }),
64
64
  maxWidth: "lg",
65
65
  headerActions: /* @__PURE__ */ T("div", {
66
66
  className: "flex items-center gap-2",
67
- children: [/* @__PURE__ */ T(D, {
67
+ children: [/* @__PURE__ */ T(E, {
68
68
  type: "button",
69
69
  variant: "ghost",
70
70
  size: "sm",
71
- onClick: () => k(Q),
72
- "aria-label": N("conversations.backToInbox", "Back to conversations"),
73
- children: [/* @__PURE__ */ w(g, { size: 14 }), N("conversations.back", "Back")]
74
- }), /* @__PURE__ */ T(D, {
71
+ onClick: () => A($),
72
+ "aria-label": P("conversations.backToInbox", "Back to conversations"),
73
+ children: [/* @__PURE__ */ w(g, { size: 14 }), P("conversations.back", "Back")]
74
+ }), /* @__PURE__ */ T(E, {
75
75
  type: "button",
76
- onClick: () => void Z(),
77
- disabled: L || !P,
76
+ onClick: () => void Q(),
77
+ disabled: R || !F,
78
78
  size: "sm",
79
- children: [/* @__PURE__ */ w(b, { size: 14 }), L ? N("conversations.saving", "Saving...") : N("conversations.saveChanges", "Save Changes")]
79
+ children: [/* @__PURE__ */ w(b, { size: 14 }), R ? P("conversations.saving", "Saving...") : P("conversations.saveChanges", "Save Changes")]
80
80
  })]
81
81
  }),
82
- children: F ? /* @__PURE__ */ w("div", {
82
+ children: I ? /* @__PURE__ */ w("div", {
83
83
  className: "text-sm text-text-secondary",
84
- children: N("conversations.loadingSettings", "Loading settings...")
85
- }) : P ? /* @__PURE__ */ T(C, { children: [
84
+ children: P("conversations.loadingSettings", "Loading settings...")
85
+ }) : F ? /* @__PURE__ */ T(C, { children: [
86
86
  /* @__PURE__ */ w(n, {
87
- title: N("conversations.notifications", "Notifications"),
88
- description: N("conversations.notificationsDescription", "Configure delivery behavior for this conversation"),
89
- children: /* @__PURE__ */ w("div", {
90
- className: "space-y-4 rounded-xl border border-border-subtle p-4",
87
+ title: P("conversations.notifications", "Notifications"),
88
+ description: P("conversations.notificationsDescription", "Configure delivery behavior for this conversation"),
89
+ children: /* @__PURE__ */ w(D, {
90
+ treatment: "glass",
91
+ className: "space-y-4 border-border-seam p-4",
91
92
  children: /* @__PURE__ */ T("div", {
92
93
  className: "flex items-start justify-between gap-4",
93
94
  children: [/* @__PURE__ */ T("div", {
@@ -97,31 +98,32 @@ function k() {
97
98
  children: /* @__PURE__ */ w(_, { size: 16 })
98
99
  }), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("p", {
99
100
  className: "text-sm font-medium text-text-primary",
100
- children: N("conversations.defaultNotificationLevel", "Default Notification Level")
101
+ children: P("conversations.defaultNotificationLevel", "Default Notification Level")
101
102
  }), /* @__PURE__ */ w("p", {
102
103
  className: "text-xs text-text-secondary mt-0.5",
103
- children: N("conversations.defaultNotificationLevelHint", "This updates conversation settings JSON via GraphQL.")
104
+ children: P("conversations.defaultNotificationLevelHint", "This updates conversation settings JSON via GraphQL.")
104
105
  })] })]
105
106
  }), /* @__PURE__ */ T("select", {
106
- value: U,
107
- onChange: (e) => W(e.target.value),
108
- className: "px-3 py-1.5 text-sm rounded-lg border border-border-subtle bg-bg-surface focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
107
+ value: W,
108
+ onChange: (e) => G(e.target.value),
109
+ "aria-label": P("conversations.defaultNotificationLevel", "Default Notification Level"),
110
+ className: "min-h-11 px-3 py-1.5 text-sm rounded-lg border border-border-seam bg-bg-surface focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
109
111
  children: [
110
112
  /* @__PURE__ */ w("option", {
111
113
  value: "ALL",
112
- children: N("conversations.allMessages", "All Messages")
114
+ children: P("conversations.allMessages", "All Messages")
113
115
  }),
114
116
  /* @__PURE__ */ w("option", {
115
117
  value: "MENTIONS",
116
- children: N("conversations.mentionsOnly", "Mentions Only")
118
+ children: P("conversations.mentionsOnly", "Mentions Only")
117
119
  }),
118
120
  /* @__PURE__ */ w("option", {
119
121
  value: "DIRECT",
120
- children: N("conversations.directOnly", "Direct Only")
122
+ children: P("conversations.directOnly", "Direct Only")
121
123
  }),
122
124
  /* @__PURE__ */ w("option", {
123
125
  value: "NONE",
124
- children: N("conversations.none", "None")
126
+ children: P("conversations.none", "None")
125
127
  })
126
128
  ]
127
129
  })]
@@ -129,30 +131,32 @@ function k() {
129
131
  })
130
132
  }),
131
133
  /* @__PURE__ */ w(n, {
132
- title: N("conversations.messageBehavior", "Message Behavior"),
133
- description: N("conversations.messageBehaviorDescription", "Tune read and typing experience"),
134
- children: /* @__PURE__ */ T("div", {
135
- className: "space-y-4 rounded-xl border border-border-subtle p-4",
136
- children: [/* @__PURE__ */ w(A, {
134
+ title: P("conversations.messageBehavior", "Message Behavior"),
135
+ description: P("conversations.messageBehaviorDescription", "Tune read and typing experience"),
136
+ children: /* @__PURE__ */ T(D, {
137
+ treatment: "glass",
138
+ className: "space-y-4 border-border-seam p-4",
139
+ children: [/* @__PURE__ */ w(j, {
137
140
  icon: /* @__PURE__ */ w(y, { size: 16 }),
138
- label: N("conversations.typingIndicators", "Typing Indicators"),
139
- description: N("conversations.typingIndicatorsDescription", "Show others when someone is typing"),
140
- checked: z,
141
- onChange: B
142
- }), /* @__PURE__ */ w(A, {
141
+ label: P("conversations.typingIndicators", "Typing Indicators"),
142
+ description: P("conversations.typingIndicatorsDescription", "Show others when someone is typing"),
143
+ checked: B,
144
+ onChange: V
145
+ }), /* @__PURE__ */ w(j, {
143
146
  icon: /* @__PURE__ */ w(y, { size: 16 }),
144
- label: N("conversations.readReceipts", "Read Receipts"),
145
- description: N("conversations.readReceiptsDescription", "Display read status for conversation participants"),
146
- checked: V,
147
- onChange: H
147
+ label: P("conversations.readReceipts", "Read Receipts"),
148
+ description: P("conversations.readReceiptsDescription", "Display read status for conversation participants"),
149
+ checked: H,
150
+ onChange: U
148
151
  })]
149
152
  })
150
153
  }),
151
154
  /* @__PURE__ */ w(n, {
152
- title: N("conversations.exportData", "Export Data"),
153
- description: N("conversations.exportDataDescription", "Download conversation comments in your preferred format"),
154
- children: /* @__PURE__ */ T("div", {
155
- className: "space-y-3 rounded-xl border border-border-subtle p-4",
155
+ title: P("conversations.exportData", "Export Data"),
156
+ description: P("conversations.exportDataDescription", "Download conversation comments in your preferred format"),
157
+ children: /* @__PURE__ */ T(D, {
158
+ treatment: "glass",
159
+ className: "space-y-3 border-border-seam p-4",
156
160
  children: [
157
161
  /* @__PURE__ */ w("div", {
158
162
  className: "flex flex-wrap gap-2",
@@ -164,22 +168,22 @@ function k() {
164
168
  permission: c.CONVERSATION_READ,
165
169
  children: /* @__PURE__ */ T("button", {
166
170
  type: "button",
167
- disabled: Y || !P,
171
+ disabled: X || !F,
168
172
  onClick: async () => {
169
- let t = await J("conversation", P.id, e);
170
- t && q.current && (q.current.href = t, q.current.download = `conversation-${P.id}.${e.toLowerCase()}`, q.current.click());
173
+ let t = await Y("conversation", F.id, e);
174
+ t && J.current && (J.current.href = t, J.current.download = `conversation-${F.id}.${e.toLowerCase()}`, J.current.click());
171
175
  },
172
- className: "inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg border border-border-subtle text-text-secondary hover:text-text-primary hover:bg-bg-sunken/50 transition-colors disabled:opacity-50",
173
- children: [/* @__PURE__ */ w(v, { size: 12 }), Y ? N("conversations.exporting", "Exporting…") : e]
176
+ className: "inline-flex min-h-11 items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg border border-border-seam text-text-secondary hover:text-text-primary hover:bg-bg-sunken/50 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:opacity-50",
177
+ children: [/* @__PURE__ */ w(v, { size: 12 }), X ? P("conversations.exporting", "Exporting…") : e]
174
178
  })
175
179
  }, e))
176
180
  }),
177
- X && /* @__PURE__ */ w("p", {
181
+ Z && /* @__PURE__ */ w("p", {
178
182
  className: "text-xs text-status-success-text",
179
- children: N("conversations.exportReady", "Export ready — downloading…")
183
+ children: P("conversations.exportReady", "Export ready — downloading…")
180
184
  }),
181
185
  /* @__PURE__ */ w("a", {
182
- ref: q,
186
+ ref: J,
183
187
  href: "#download",
184
188
  tabIndex: -1,
185
189
  className: "hidden",
@@ -188,22 +192,22 @@ function k() {
188
192
  ]
189
193
  })
190
194
  }),
191
- R && /* @__PURE__ */ w("div", {
195
+ z && /* @__PURE__ */ w("div", {
192
196
  className: "text-xs text-status-error-text rounded-lg border border-status-error-border/30 bg-status-error-bg/10 px-3 py-2",
193
- children: R.message
197
+ children: z.message
194
198
  }),
195
- G && !R && /* @__PURE__ */ w("div", {
199
+ K && !z && /* @__PURE__ */ w("div", {
196
200
  className: "text-xs text-status-success-text rounded-lg border border-border-default bg-status-success-bg-subtle px-3 py-2",
197
- children: N("conversations.settingsSaved", "Settings saved.")
201
+ children: P("conversations.settingsSaved", "Settings saved.")
198
202
  })
199
203
  ] }) : /* @__PURE__ */ w("div", {
200
204
  className: "text-sm text-text-secondary",
201
- children: N("conversations.openConversationFirst", "Open a conversation first, then come back to settings.")
205
+ children: P("conversations.openConversationFirst", "Open a conversation first, then come back to settings.")
202
206
  })
203
207
  })]
204
208
  });
205
209
  }
206
- function A({ icon: e, label: t, description: n, checked: r, onChange: i }) {
210
+ function j({ icon: e, label: t, description: n, checked: r, onChange: i }) {
207
211
  return /* @__PURE__ */ w(s, {
208
212
  permission: c.CONVERSATION_UPDATE,
209
213
  children: /* @__PURE__ */ T("div", {
@@ -233,6 +237,6 @@ function A({ icon: e, label: t, description: n, checked: r, onChange: i }) {
233
237
  });
234
238
  }
235
239
  //#endregion
236
- export { k as ConversationsSettingsPage };
240
+ export { A as ConversationsSettingsPage };
237
241
 
238
242
  //# sourceMappingURL=ConversationsSettingsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationsSettingsPage.js","names":[],"sources":["../../../src/conversations/pages/ConversationsSettingsPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { ArrowLeft, Bell, Download, Eye, Save, Settings } from \"lucide-react\";\nimport { Breadcrumbs, type BreadcrumbItem } from \"@burdenoff/fe-libs/chrome\";\nimport { Button, PageContainer } from \"@burdenoff/fe-libs/ui\";\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\";\nimport { useTr } from \"../utils/i18n\";\n\nexport function ConversationsSettingsPage() {\n const navigate = useNavigate();\n const { basePath } = useConversations();\n const { activeConversationId } = useConversationsStore();\n const tr = useTr();\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 const inboxHref = basePath || \"/conversations\";\n const breadcrumbItems = useMemo<BreadcrumbItem[]>(\n () => [\n {\n label: tr(\"conversations.title\", \"Conversations\"),\n href: inboxHref,\n onClick: () => navigate(inboxHref),\n },\n {\n label: tr(\"conversations.settings\", \"Settings\"),\n isCurrentPage: true,\n },\n ],\n [inboxHref, navigate, tr],\n );\n\n return (\n <PageContainer padded={false} className=\"h-full min-h-0\">\n <div className=\"border-b border-border-subtle bg-bg-surface px-4 py-3 sm:px-6\">\n <Breadcrumbs items={breadcrumbItems} />\n </div>\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 variant=\"ghost\"\n size=\"sm\"\n onClick={() => navigate(inboxHref)}\n aria-label={tr(\n \"conversations.backToInbox\",\n \"Back to conversations\",\n )}\n >\n <ArrowLeft size={14} />\n {tr(\"conversations.back\", \"Back\")}\n </Button>\n <Button\n type=\"button\"\n onClick={() => void handleSave()}\n disabled={saving || !conversation}\n size=\"sm\"\n >\n <Save size={14} />\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-text-secondary\">\n {tr(\"conversations.loadingSettings\", \"Loading settings...\")}\n </div>\n ) : !conversation ? (\n <div className=\"text-sm text-text-secondary\">\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-subtle 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-text-primary\">\n {tr(\n \"conversations.defaultNotificationLevel\",\n \"Default Notification Level\",\n )}\n </p>\n <p className=\"text-xs text-text-secondary 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-subtle bg-bg-surface focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-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-subtle 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-subtle 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-subtle text-text-secondary hover:text-text-primary hover:bg-bg-sunken/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-status-error-text rounded-lg border border-status-error-border/30 bg-status-error-bg/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 </PageContainer>\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-bg-sunken text-text-secondary\">\n {icon}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{label}</p>\n <p className=\"text-xs text-text-secondary 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-action-primary-bg\" : \"bg-bg-sunken\"}`}\n >\n <span\n className={`pointer-events-none inline-block h-5 w-5 rounded-full bg-bg-surface 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, useMemo, useRef, useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { ArrowLeft, Bell, Download, Eye, Save, Settings } from \"lucide-react\";\nimport { Breadcrumbs, type BreadcrumbItem } from \"@burdenoff/fe-libs/chrome\";\nimport { Button, GlassCard, PageContainer } from \"@burdenoff/fe-libs/ui\";\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\";\nimport { useTr } from \"../utils/i18n\";\n\nexport function ConversationsSettingsPage() {\n const navigate = useNavigate();\n const { basePath } = useConversations();\n const { activeConversationId } = useConversationsStore();\n const tr = useTr();\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 const inboxHref = basePath || \"/conversations\";\n const breadcrumbItems = useMemo<BreadcrumbItem[]>(\n () => [\n {\n label: tr(\"conversations.title\", \"Conversations\"),\n href: inboxHref,\n onClick: () => navigate(inboxHref),\n },\n {\n label: tr(\"conversations.settings\", \"Settings\"),\n isCurrentPage: true,\n },\n ],\n [inboxHref, navigate, tr],\n );\n\n return (\n <PageContainer padded={false} className=\"h-full min-h-0\">\n <div className=\"border-b border-border-subtle bg-bg-surface px-4 py-3 sm:px-6\">\n <Breadcrumbs items={breadcrumbItems} />\n </div>\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 variant=\"ghost\"\n size=\"sm\"\n onClick={() => navigate(inboxHref)}\n aria-label={tr(\n \"conversations.backToInbox\",\n \"Back to conversations\",\n )}\n >\n <ArrowLeft size={14} />\n {tr(\"conversations.back\", \"Back\")}\n </Button>\n <Button\n type=\"button\"\n onClick={() => void handleSave()}\n disabled={saving || !conversation}\n size=\"sm\"\n >\n <Save size={14} />\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-text-secondary\">\n {tr(\"conversations.loadingSettings\", \"Loading settings...\")}\n </div>\n ) : !conversation ? (\n <div className=\"text-sm text-text-secondary\">\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 <GlassCard\n treatment=\"glass\"\n className=\"space-y-4 border-border-seam p-4\"\n >\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-text-primary\">\n {tr(\n \"conversations.defaultNotificationLevel\",\n \"Default Notification Level\",\n )}\n </p>\n <p className=\"text-xs text-text-secondary 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 aria-label={tr(\n \"conversations.defaultNotificationLevel\",\n \"Default Notification Level\",\n )}\n className=\"min-h-11 px-3 py-1.5 text-sm rounded-lg border border-border-seam bg-bg-surface focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-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 </GlassCard>\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 <GlassCard\n treatment=\"glass\"\n className=\"space-y-4 border-border-seam p-4\"\n >\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 </GlassCard>\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 <GlassCard\n treatment=\"glass\"\n className=\"space-y-3 border-border-seam p-4\"\n >\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 min-h-11 items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-lg border border-border-seam text-text-secondary hover:text-text-primary hover:bg-bg-sunken/50 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] 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 </GlassCard>\n </PageSection>\n\n {saveError && (\n <div className=\"text-xs text-status-error-text rounded-lg border border-status-error-border/30 bg-status-error-bg/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 </PageContainer>\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-bg-sunken text-text-secondary\">\n {icon}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{label}</p>\n <p className=\"text-xs text-text-secondary 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-action-primary-bg\" : \"bg-bg-sunken\"}`}\n >\n <span\n className={`pointer-events-none inline-block h-5 w-5 rounded-full bg-bg-surface shadow transition ${checked ? \"translate-x-5\" : \"translate-x-0\"}`}\n />\n </button>\n </div>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-conversations",
3
- "version": "2026.624.1",
3
+ "version": "2026.625.1",
4
4
  "description": "Burdenoff Conversations Microfrontend - Complete messaging and conversation interface",
5
5
  "type": "module",
6
6
  "files": [
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@apollo/client": "^4.0.11",
46
- "@burdenoff/fe-libs": "2026.529.1",
46
+ "@burdenoff/fe-libs": "2026.625.2",
47
47
  "@tanstack/react-query": "^5.90.16",
48
48
  "clsx": "^2.1.1",
49
49
  "graphql": "^16.10.0",