@burdenoff/microfe-conversations 2026.527.2 → 2026.528.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 (33) hide show
  1. package/dist/conversations/ConversationsRoot.js +1 -1
  2. package/dist/conversations/ConversationsRoot.js.map +1 -1
  3. package/dist/conversations/ConversationsUserRoot.js +1 -1
  4. package/dist/conversations/ConversationsUserRoot.js.map +1 -1
  5. package/dist/conversations/ConversationsUserRoutes.js +6 -6
  6. package/dist/conversations/ConversationsUserRoutes.js.map +1 -1
  7. package/dist/conversations/components/ConversationRow.js +176 -0
  8. package/dist/conversations/components/ConversationRow.js.map +1 -0
  9. package/dist/conversations/components/EmptyState.js +10 -21
  10. package/dist/conversations/components/EmptyState.js.map +1 -1
  11. package/dist/conversations/components/InboxLayout.js +189 -0
  12. package/dist/conversations/components/InboxLayout.js.map +1 -0
  13. package/dist/conversations/components/NewConversationDialog.js +285 -0
  14. package/dist/conversations/components/NewConversationDialog.js.map +1 -0
  15. package/dist/conversations/components/PageLayout.js +16 -34
  16. package/dist/conversations/components/PageLayout.js.map +1 -1
  17. package/dist/conversations/components/PageSection.js +10 -19
  18. package/dist/conversations/components/PageSection.js.map +1 -1
  19. package/dist/conversations/hooks/useConversationQueries.js +48 -34
  20. package/dist/conversations/hooks/useConversationQueries.js.map +1 -1
  21. package/dist/conversations/pages/ConversationListPage.js +187 -226
  22. package/dist/conversations/pages/ConversationListPage.js.map +1 -1
  23. package/dist/conversations/pages/ConversationsLandingPage.js +109 -99
  24. package/dist/conversations/pages/ConversationsLandingPage.js.map +1 -1
  25. package/dist/conversations/pages/ConversationsSettingsPage.js +201 -182
  26. package/dist/conversations/pages/ConversationsSettingsPage.js.map +1 -1
  27. package/dist/conversations/pages/NewConversationPage.js +19 -280
  28. package/dist/conversations/pages/NewConversationPage.js.map +1 -1
  29. package/dist/generated/wspace-operations.js +72 -56
  30. package/dist/generated/wspace-operations.js.map +1 -1
  31. package/package.json +2 -2
  32. package/dist/conversations/components/ConversationCard.js +0 -119
  33. package/dist/conversations/components/ConversationCard.js.map +0 -1
@@ -1,219 +1,238 @@
1
1
  import { useConversations as e } from "../providers/ConversationsProvider.js";
2
- import { PageSection as t } from "../components/PageSection.js";
3
- import { PageLayout as n } from "../components/PageLayout.js";
4
- import { useConversationsStore as r } from "../store/conversationsStore.js";
5
- import { useGetConversation as i, useUpdateConversation as a } from "../hooks/useConversationQueries.js";
6
- import { PermissionGuard as o } from "../components/PermissionGuard.js";
7
- import { CONVERSATIONS_PERMISSIONS as s } from "../constants/permissions.js";
8
- import { nativeImpact as c, nativeNotify as l } from "../../utils/nativeBridge.js";
9
- import { useExportConversation as u } from "../hooks/useExportConversation.js";
10
- import { useEffect as d, useRef as f, useState as p } from "react";
11
- import { ArrowLeft as m, Bell as h, Download as g, Eye as _, Save as v, Settings as y } from "lucide-react";
12
- import { useNavigate as b } from "react-router-dom";
13
- import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";
14
- import { useI18n as w } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
2
+ import { useTr as t } from "../utils/i18n.js";
3
+ import { PageSection as n } from "../components/PageSection.js";
4
+ import { PageLayout as r } from "../components/PageLayout.js";
5
+ import { useConversationsStore as i } from "../store/conversationsStore.js";
6
+ import { useGetConversation as a, useUpdateConversation as o } from "../hooks/useConversationQueries.js";
7
+ import { PermissionGuard as s } from "../components/PermissionGuard.js";
8
+ import { CONVERSATIONS_PERMISSIONS as c } from "../constants/permissions.js";
9
+ import { nativeImpact as l, nativeNotify as u } from "../../utils/nativeBridge.js";
10
+ import { useExportConversation as d } from "../hooks/useExportConversation.js";
11
+ import { useEffect as f, useMemo as p, useRef as m, useState as h } from "react";
12
+ import { ArrowLeft as g, Bell as _, Download as v, Eye as y, Save as b, Settings as x } from "lucide-react";
13
+ import { useNavigate as S } from "react-router-dom";
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
17
  //#region src/conversations/pages/ConversationsSettingsPage.tsx
16
- function T() {
17
- let T = b(), { basePath: D } = e(), { activeConversationId: O } = r(), { t: k } = w(), A = (e, t) => {
18
- let n = k(e);
19
- return n === e ? t : n;
20
- }, { conversation: j, loading: M } = i(O ?? ""), { updateConversation: N, loading: P, error: F } = a(), [I, L] = p(!0), [R, z] = p(!0), [B, V] = p("ALL"), [H, U] = p(!1), W = f(null), { exportComments: G, loading: K, downloadUrl: q } = u();
21
- d(() => {
22
- let e = j?.settings && typeof j.settings == "object" ? j.settings : null;
23
- e && (typeof e.showTypingIndicators == "boolean" && L(e.showTypingIndicators), typeof e.showReadReceipts == "boolean" && z(e.showReadReceipts), typeof e.defaultNotificationLevel == "string" && V(e.defaultNotificationLevel));
24
- }, [j]);
25
- let J = async () => {
26
- if (j?.id) {
27
- c("light");
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();
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) {
26
+ l("light");
28
27
  try {
29
- let e = !!await N({
30
- id: j.id,
28
+ let e = !!await I({
29
+ id: P.id,
31
30
  settings: {
32
- showTypingIndicators: I,
33
- showReadReceipts: R,
34
- defaultNotificationLevel: B
31
+ showTypingIndicators: z,
32
+ showReadReceipts: V,
33
+ defaultNotificationLevel: U
35
34
  }
36
35
  });
37
- l(e ? "success" : "error"), U(e);
36
+ u(e ? "success" : "error"), K(e);
38
37
  } catch (e) {
39
- throw l("error"), e;
38
+ throw u("error"), e;
40
39
  }
41
40
  }
42
- };
43
- return /* @__PURE__ */ S(n, {
44
- dataTour: "conversation-settings-page",
45
- title: A("conversations.settings", "Settings"),
46
- subtitle: j ? `${A("conversations.settingsFor", "Conversation settings for")} ${j.title}` : A("conversations.selectConversationForSettings", "Select a conversation to edit settings"),
47
- icon: /* @__PURE__ */ S(y, { size: 24 }),
48
- maxWidth: "lg",
49
- headerActions: /* @__PURE__ */ C("div", {
50
- className: "flex items-center gap-2",
51
- children: [/* @__PURE__ */ C("button", {
52
- type: "button",
53
- onClick: () => T(D || "/conversations"),
54
- 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",
55
- children: [/* @__PURE__ */ S(m, { size: 16 }), A("conversations.back", "Back")]
56
- }), /* @__PURE__ */ C("button", {
57
- type: "button",
58
- onClick: () => void J(),
59
- disabled: P || !j,
60
- 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",
61
- children: [/* @__PURE__ */ S(v, { size: 16 }), P ? A("conversations.saving", "Saving...") : A("conversations.saveChanges", "Save Changes")]
62
- })]
63
- }),
64
- children: M ? /* @__PURE__ */ S("div", {
65
- className: "text-sm text-muted-foreground",
66
- children: A("conversations.loadingSettings", "Loading settings...")
67
- }) : j ? /* @__PURE__ */ C(x, { children: [
68
- /* @__PURE__ */ S(t, {
69
- title: A("conversations.notifications", "Notifications"),
70
- description: A("conversations.notificationsDescription", "Configure delivery behavior for this conversation"),
71
- children: /* @__PURE__ */ S("div", {
72
- className: "space-y-4 rounded-xl border border-border p-4",
73
- children: /* @__PURE__ */ C("div", {
74
- className: "flex items-start justify-between gap-4",
75
- children: [/* @__PURE__ */ C("div", {
76
- className: "flex items-start gap-3",
77
- children: [/* @__PURE__ */ S("div", {
78
- className: "p-2 rounded-lg bg-status-info-bg-subtle text-status-info-text",
79
- children: /* @__PURE__ */ S(h, { size: 16 })
80
- }), /* @__PURE__ */ C("div", { children: [/* @__PURE__ */ S("p", {
81
- className: "text-sm font-medium text-foreground",
82
- children: A("conversations.defaultNotificationLevel", "Default Notification Level")
83
- }), /* @__PURE__ */ S("p", {
84
- className: "text-xs text-muted-foreground mt-0.5",
85
- children: A("conversations.defaultNotificationLevelHint", "This updates conversation settings JSON via GraphQL.")
86
- })] })]
87
- }), /* @__PURE__ */ C("select", {
88
- value: B,
89
- onChange: (e) => V(e.target.value),
90
- className: "px-3 py-1.5 text-sm rounded-lg border border-border bg-background focus:outline-none focus:ring-2 focus:ring-ring",
91
- children: [
92
- /* @__PURE__ */ S("option", {
93
- value: "ALL",
94
- children: A("conversations.allMessages", "All Messages")
95
- }),
96
- /* @__PURE__ */ S("option", {
97
- value: "MENTIONS",
98
- children: A("conversations.mentionsOnly", "Mentions Only")
99
- }),
100
- /* @__PURE__ */ S("option", {
101
- value: "DIRECT",
102
- children: A("conversations.directOnly", "Direct Only")
103
- }),
104
- /* @__PURE__ */ S("option", {
105
- value: "NONE",
106
- children: A("conversations.none", "None")
107
- })
108
- ]
109
- })]
110
- })
111
- })
112
- }),
113
- /* @__PURE__ */ S(t, {
114
- title: A("conversations.messageBehavior", "Message Behavior"),
115
- description: A("conversations.messageBehaviorDescription", "Tune read and typing experience"),
116
- children: /* @__PURE__ */ C("div", {
117
- className: "space-y-4 rounded-xl border border-border p-4",
118
- children: [/* @__PURE__ */ S(E, {
119
- icon: /* @__PURE__ */ S(_, { size: 16 }),
120
- label: A("conversations.typingIndicators", "Typing Indicators"),
121
- description: A("conversations.typingIndicatorsDescription", "Show others when someone is typing"),
122
- checked: I,
123
- onChange: L
124
- }), /* @__PURE__ */ S(E, {
125
- icon: /* @__PURE__ */ S(_, { size: 16 }),
126
- label: A("conversations.readReceipts", "Read Receipts"),
127
- description: A("conversations.readReceiptsDescription", "Display read status for conversation participants"),
128
- checked: R,
129
- onChange: z
130
- })]
131
- })
41
+ }, Q = j || "/conversations";
42
+ return /* @__PURE__ */ T(O, {
43
+ padded: !1,
44
+ className: "h-full min-h-0",
45
+ children: [/* @__PURE__ */ w("div", {
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)
51
+ }, {
52
+ label: N("conversations.settings", "Settings"),
53
+ isCurrentPage: !0
54
+ }], [
55
+ Q,
56
+ k,
57
+ N
58
+ ]) })
59
+ }), /* @__PURE__ */ w(r, {
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"),
63
+ icon: /* @__PURE__ */ w(x, { size: 24 }),
64
+ maxWidth: "lg",
65
+ headerActions: /* @__PURE__ */ T("div", {
66
+ className: "flex items-center gap-2",
67
+ children: [/* @__PURE__ */ T(D, {
68
+ type: "button",
69
+ variant: "ghost",
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, {
75
+ type: "button",
76
+ onClick: () => void Z(),
77
+ disabled: L || !P,
78
+ size: "sm",
79
+ children: [/* @__PURE__ */ w(b, { size: 14 }), L ? N("conversations.saving", "Saving...") : N("conversations.saveChanges", "Save Changes")]
80
+ })]
132
81
  }),
133
- /* @__PURE__ */ S(t, {
134
- title: A("conversations.exportData", "Export Data"),
135
- description: A("conversations.exportDataDescription", "Download conversation comments in your preferred format"),
136
- children: /* @__PURE__ */ C("div", {
137
- className: "space-y-3 rounded-xl border border-border p-4",
138
- children: [
139
- /* @__PURE__ */ S("div", {
140
- className: "flex flex-wrap gap-2",
141
- children: [
142
- "JSON",
143
- "CSV",
144
- "MARKDOWN"
145
- ].map((e) => /* @__PURE__ */ S(o, {
146
- permission: s.CONVERSATION_READ,
147
- children: /* @__PURE__ */ C("button", {
148
- type: "button",
149
- disabled: K || !j,
150
- onClick: async () => {
151
- let t = await G("conversation", j.id, e);
152
- t && W.current && (W.current.href = t, W.current.download = `conversation-${j.id}.${e.toLowerCase()}`, W.current.click());
153
- },
154
- 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",
155
- children: [/* @__PURE__ */ S(g, { size: 12 }), K ? A("conversations.exporting", "Exporting…") : e]
156
- })
157
- }, e))
158
- }),
159
- q && /* @__PURE__ */ S("p", {
160
- className: "text-xs text-status-success-text",
161
- children: A("conversations.exportReady", "Export ready — downloading…")
162
- }),
163
- /* @__PURE__ */ S("a", {
164
- ref: W,
165
- href: "#download",
166
- tabIndex: -1,
167
- className: "hidden",
168
- "aria-hidden": "true"
82
+ children: F ? /* @__PURE__ */ w("div", {
83
+ className: "text-sm text-muted-foreground",
84
+ children: N("conversations.loadingSettings", "Loading settings...")
85
+ }) : P ? /* @__PURE__ */ T(C, { children: [
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 p-4",
91
+ children: /* @__PURE__ */ T("div", {
92
+ className: "flex items-start justify-between gap-4",
93
+ children: [/* @__PURE__ */ T("div", {
94
+ className: "flex items-start gap-3",
95
+ children: [/* @__PURE__ */ w("div", {
96
+ className: "p-2 rounded-lg bg-status-info-bg-subtle text-status-info-text",
97
+ children: /* @__PURE__ */ w(_, { size: 16 })
98
+ }), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("p", {
99
+ className: "text-sm font-medium text-foreground",
100
+ children: N("conversations.defaultNotificationLevel", "Default Notification Level")
101
+ }), /* @__PURE__ */ w("p", {
102
+ className: "text-xs text-muted-foreground mt-0.5",
103
+ children: N("conversations.defaultNotificationLevelHint", "This updates conversation settings JSON via GraphQL.")
104
+ })] })]
105
+ }), /* @__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 bg-background focus:outline-none focus:ring-2 focus:ring-ring",
109
+ children: [
110
+ /* @__PURE__ */ w("option", {
111
+ value: "ALL",
112
+ children: N("conversations.allMessages", "All Messages")
113
+ }),
114
+ /* @__PURE__ */ w("option", {
115
+ value: "MENTIONS",
116
+ children: N("conversations.mentionsOnly", "Mentions Only")
117
+ }),
118
+ /* @__PURE__ */ w("option", {
119
+ value: "DIRECT",
120
+ children: N("conversations.directOnly", "Direct Only")
121
+ }),
122
+ /* @__PURE__ */ w("option", {
123
+ value: "NONE",
124
+ children: N("conversations.none", "None")
125
+ })
126
+ ]
127
+ })]
169
128
  })
170
- ]
129
+ })
130
+ }),
131
+ /* @__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 p-4",
136
+ children: [/* @__PURE__ */ w(A, {
137
+ 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, {
143
+ 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
148
+ })]
149
+ })
150
+ }),
151
+ /* @__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 p-4",
156
+ children: [
157
+ /* @__PURE__ */ w("div", {
158
+ className: "flex flex-wrap gap-2",
159
+ children: [
160
+ "JSON",
161
+ "CSV",
162
+ "MARKDOWN"
163
+ ].map((e) => /* @__PURE__ */ w(s, {
164
+ permission: c.CONVERSATION_READ,
165
+ children: /* @__PURE__ */ T("button", {
166
+ type: "button",
167
+ disabled: Y || !P,
168
+ 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());
171
+ },
172
+ 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",
173
+ children: [/* @__PURE__ */ w(v, { size: 12 }), Y ? N("conversations.exporting", "Exporting…") : e]
174
+ })
175
+ }, e))
176
+ }),
177
+ X && /* @__PURE__ */ w("p", {
178
+ className: "text-xs text-status-success-text",
179
+ children: N("conversations.exportReady", "Export ready — downloading…")
180
+ }),
181
+ /* @__PURE__ */ w("a", {
182
+ ref: q,
183
+ href: "#download",
184
+ tabIndex: -1,
185
+ className: "hidden",
186
+ "aria-hidden": "true"
187
+ })
188
+ ]
189
+ })
190
+ }),
191
+ R && /* @__PURE__ */ w("div", {
192
+ className: "text-xs text-destructive rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2",
193
+ children: R.message
194
+ }),
195
+ G && !R && /* @__PURE__ */ w("div", {
196
+ 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.")
171
198
  })
172
- }),
173
- F && /* @__PURE__ */ S("div", {
174
- className: "text-xs text-destructive rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2",
175
- children: F.message
176
- }),
177
- H && !F && /* @__PURE__ */ S("div", {
178
- className: "text-xs text-status-success-text rounded-lg border border-border-default bg-status-success-bg-subtle px-3 py-2",
179
- children: A("conversations.settingsSaved", "Settings saved.")
199
+ ] }) : /* @__PURE__ */ w("div", {
200
+ className: "text-sm text-muted-foreground",
201
+ children: N("conversations.openConversationFirst", "Open a conversation first, then come back to settings.")
180
202
  })
181
- ] }) : /* @__PURE__ */ S("div", {
182
- className: "text-sm text-muted-foreground",
183
- children: A("conversations.openConversationFirst", "Open a conversation first, then come back to settings.")
184
- })
203
+ })]
185
204
  });
186
205
  }
187
- function E({ icon: e, label: t, description: n, checked: r, onChange: i }) {
188
- return /* @__PURE__ */ S(o, {
189
- permission: s.CONVERSATION_UPDATE,
190
- children: /* @__PURE__ */ C("div", {
206
+ function A({ icon: e, label: t, description: n, checked: r, onChange: i }) {
207
+ return /* @__PURE__ */ w(s, {
208
+ permission: c.CONVERSATION_UPDATE,
209
+ children: /* @__PURE__ */ T("div", {
191
210
  className: "flex items-start justify-between gap-4",
192
- children: [/* @__PURE__ */ C("div", {
211
+ children: [/* @__PURE__ */ T("div", {
193
212
  className: "flex items-start gap-3",
194
- children: [/* @__PURE__ */ S("div", {
213
+ children: [/* @__PURE__ */ w("div", {
195
214
  className: "p-2 rounded-lg bg-muted text-muted-foreground",
196
215
  children: e
197
- }), /* @__PURE__ */ C("div", { children: [/* @__PURE__ */ S("p", {
216
+ }), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("p", {
198
217
  className: "text-sm font-medium text-foreground",
199
218
  children: t
200
- }), /* @__PURE__ */ S("p", {
219
+ }), /* @__PURE__ */ w("p", {
201
220
  className: "text-xs text-muted-foreground mt-0.5",
202
221
  children: n
203
222
  })] })]
204
- }), /* @__PURE__ */ S("button", {
223
+ }), /* @__PURE__ */ w("button", {
205
224
  type: "button",
206
225
  role: "switch",
207
226
  "aria-checked": r,
208
227
  "aria-label": t,
209
228
  onClick: () => i(!r),
210
229
  className: `relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors ${r ? "bg-primary" : "bg-muted"}`,
211
- children: /* @__PURE__ */ S("span", { className: `pointer-events-none inline-block h-5 w-5 rounded-full bg-background shadow transition ${r ? "translate-x-5" : "translate-x-0"}` })
230
+ children: /* @__PURE__ */ w("span", { className: `pointer-events-none inline-block h-5 w-5 rounded-full bg-background shadow transition ${r ? "translate-x-5" : "translate-x-0"}` })
212
231
  })]
213
232
  })
214
233
  });
215
234
  }
216
235
  //#endregion
217
- export { T as ConversationsSettingsPage };
236
+ export { k as ConversationsSettingsPage };
218
237
 
219
238
  //# sourceMappingURL=ConversationsSettingsPage.js.map
@@ -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 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":";;;;;;;;;;;;;;;AAgBA,SAAgB,IAA4B;CAC1C,IAAM,IAAW,GAAa,EACxB,EAAE,gBAAa,GAAkB,EACjC,EAAE,4BAAyB,GAAuB,EAClD,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAE9B,EAAE,iBAAc,SAAS,MAAwB,EACrD,KAAwB,GACzB,EACK,EACJ,uBACA,SAAS,GACT,OAAO,MACL,GAAuB,EAErB,CAAC,GAAsB,KAA2B,EAAS,GAAK,EAChE,CAAC,GAAkB,KAAuB,EAAS,GAAK,EACxD,CAAC,GAA0B,KAC/B,EAAS,MAAM,EACX,CAAC,GAAO,KAAY,EAAS,GAAM,EACnC,IAAkB,EAA0B,KAAK,EACjD,EACJ,mBACA,SAAS,GACT,mBACE,GAAuB;AAE3B,SAAgB;EACd,IAAM,IACJ,GAAc,YAAY,OAAO,EAAa,YAAa,WACtD,EAAa,WACd;AACD,QACD,OAAO,EAAS,wBAAyB,aAC3C,EAAwB,EAAS,qBAAqB,EAEpD,OAAO,EAAS,oBAAqB,aACvC,EAAoB,EAAS,iBAAiB,EAE5C,OAAO,EAAS,4BAA6B,YAC/C,EAA4B,EAAS,yBAAyB;IAE/D,CAAC,EAAa,CAAC;CAElB,IAAM,IAAa,YAAY;AACxB,SAAc,IACnB;KAAa,QAAQ;AACrB,OAAI;IASF,IAAM,IAAK,EARK,MAAM,EAAmB;KACvC,IAAI,EAAa;KACjB,UAAU;MACR;MACA;MACA;MACD;KACF,CAAC;AAGF,IADA,EAAa,IAAK,YAAY,QAAQ,EACtC,EAAS,EAAG;YACL,GAAO;AAEd,UADA,EAAa,QAAQ,EACf;;;;AAIV,QACE,kBAAC,GAAD;EACE,UAAS;EACT,OAAO,EAAG,0BAA0B,WAAW;EAC/C,UACE,IACI,GAAG,EAAG,6BAA6B,4BAA4B,CAAC,GAAG,EAAa,UAChF,EACE,+CACA,yCACD;EAEP,MAAM,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;EAC5B,UAAS;EACT,eACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAS,KAAY,iBAAiB;IACrD,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA,EACtB,EAAG,sBAAsB,OAAO,CAC1B;OACT,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,KAAK,GAAY;IAChC,UAAU,KAAU,CAAC;IACrB,WAAU;cAJZ,CAME,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,IACG,EAAG,wBAAwB,YAAY,GACvC,EAAG,6BAA6B,eAAe,CAC5C;MACL;;YAGP,IACC,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAG,iCAAiC,sBAAsB;GACvD,CAAA,GACH,IAQH,kBAAA,GAAA,EAAA,UAAA;GACE,kBAAC,GAAD;IACE,OAAO,EAAG,+BAA+B,gBAAgB;IACzD,aAAa,EACX,0CACA,oDACD;cAED,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;QACd,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EACC,0CACA,6BACD;QACC,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBACV,EACC,8CACA,uDACD;QACC,CAAA,CACA,EAAA,CAAA,CACF;UACN,kBAAC,UAAD;OACE,OAAO;OACP,WAAW,MACT,EAA4B,EAAM,OAAO,MAAM;OAEjD,WAAU;iBALZ;QAOE,kBAAC,UAAD;SAAQ,OAAM;mBACX,EAAG,6BAA6B,eAAe;SACzC,CAAA;QACT,kBAAC,UAAD;SAAQ,OAAM;mBACX,EAAG,8BAA8B,gBAAgB;SAC3C,CAAA;QACT,kBAAC,UAAD;SAAQ,OAAM;mBACX,EAAG,4BAA4B,cAAc;SACvC,CAAA;QACT,kBAAC,UAAD;SAAQ,OAAM;mBACX,EAAG,sBAAsB,OAAO;SAC1B,CAAA;QACF;SACL;;KACF,CAAA;IACM,CAAA;GAEd,kBAAC,GAAD;IACE,OAAO,EAAG,iCAAiC,mBAAmB;IAC9D,aAAa,EACX,4CACA,kCACD;cAED,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA;MACvB,OAAO,EACL,kCACA,oBACD;MACD,aAAa,EACX,6CACA,qCACD;MACD,SAAS;MACT,UAAU;MACV,CAAA,EACF,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA;MACvB,OAAO,EAAG,8BAA8B,gBAAgB;MACxD,aAAa,EACX,yCACA,oDACD;MACD,SAAS;MACT,UAAU;MACV,CAAA,CACE;;IACM,CAAA;GAEd,kBAAC,GAAD;IACE,OAAO,EAAG,4BAA4B,cAAc;IACpD,aAAa,EACX,uCACA,0DACD;cAED,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBACX;QAAC;QAAQ;QAAO;QAAW,CAAW,KAAK,MAC3C,kBAAC,GAAD;QAEE,YAAY,EAA0B;kBAEtC,kBAAC,UAAD;SACE,MAAK;SACL,UAAU,KAAa,CAAC;SACxB,SAAS,YAAY;UACnB,IAAM,IAAM,MAAM,EAChB,gBACA,EAAc,IACd,EACD;AACD,UAAI,KAAO,EAAgB,YACzB,EAAgB,QAAQ,OAAO,GAC/B,EAAgB,QAAQ,WAAW,gBAAgB,EAAc,GAAG,GAAG,EAAO,aAAa,IAC3F,EAAgB,QAAQ,OAAO;;SAGnC,WAAU;mBAfZ,CAiBE,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EACrB,IACG,EAAG,2BAA2B,aAAa,GAC3C,EACG;;QACO,EAzBX,EAyBW,CAClB;OACE,CAAA;MACL,KACC,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,6BACA,8BACD;OACC,CAAA;MAKN,kBAAC,KAAD;OACE,KAAK;OACL,MAAK;OACL,UAAU;OACV,WAAU;OACV,eAAY;OACZ,CAAA;MACE;;IACM,CAAA;GAEb,KACC,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAU;IACP,CAAA;GAEP,KAAS,CAAC,KACT,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAG,+BAA+B,kBAAkB;IACjD,CAAA;GAEP,EAAA,CAAA,GApKH,kBAAC,OAAD;GAAK,WAAU;aACZ,EACC,uCACA,yDACD;GACG,CAAA;EAiKG,CAAA;;AAIjB,SAAS,EAAc,EACrB,SACA,UACA,gBACA,YACA,eAOC;AACD,QACE,kBAAC,GAAD;EAAiB,YAAY,EAA0B;YACrD,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACZ;KACG,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eAAuC;KAAU,CAAA,EAC9D,kBAAC,KAAD;KAAG,WAAU;eACV;KACC,CAAA,CACA,EAAA,CAAA,CACF;OACN,kBAAC,UAAD;IACE,MAAK;IACL,MAAK;IACL,gBAAc;IACd,cAAY;IACZ,eAAe,EAAS,CAAC,EAAQ;IACjC,WAAW,yHAAyH,IAAU,eAAe;cAE7J,kBAAC,QAAD,EACE,WAAW,yFAAyF,IAAU,kBAAkB,mBAChI,CAAA;IACK,CAAA,CACL;;EACU,CAAA"}
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-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 </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-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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"}