@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.
- package/dist/conversations/ConversationsRoot.js +1 -1
- package/dist/conversations/ConversationsRoot.js.map +1 -1
- package/dist/conversations/ConversationsUserRoot.js +1 -1
- package/dist/conversations/ConversationsUserRoot.js.map +1 -1
- package/dist/conversations/ConversationsUserRoutes.js +6 -6
- package/dist/conversations/ConversationsUserRoutes.js.map +1 -1
- package/dist/conversations/components/ConversationRow.js +176 -0
- package/dist/conversations/components/ConversationRow.js.map +1 -0
- package/dist/conversations/components/EmptyState.js +10 -21
- package/dist/conversations/components/EmptyState.js.map +1 -1
- package/dist/conversations/components/InboxLayout.js +189 -0
- package/dist/conversations/components/InboxLayout.js.map +1 -0
- package/dist/conversations/components/NewConversationDialog.js +285 -0
- package/dist/conversations/components/NewConversationDialog.js.map +1 -0
- package/dist/conversations/components/PageLayout.js +16 -34
- package/dist/conversations/components/PageLayout.js.map +1 -1
- package/dist/conversations/components/PageSection.js +10 -19
- package/dist/conversations/components/PageSection.js.map +1 -1
- package/dist/conversations/hooks/useConversationQueries.js +48 -34
- package/dist/conversations/hooks/useConversationQueries.js.map +1 -1
- package/dist/conversations/pages/ConversationListPage.js +187 -226
- package/dist/conversations/pages/ConversationListPage.js.map +1 -1
- package/dist/conversations/pages/ConversationsLandingPage.js +109 -99
- package/dist/conversations/pages/ConversationsLandingPage.js.map +1 -1
- package/dist/conversations/pages/ConversationsSettingsPage.js +201 -182
- package/dist/conversations/pages/ConversationsSettingsPage.js.map +1 -1
- package/dist/conversations/pages/NewConversationPage.js +19 -280
- package/dist/conversations/pages/NewConversationPage.js.map +1 -1
- package/dist/generated/wspace-operations.js +72 -56
- package/dist/generated/wspace-operations.js.map +1 -1
- package/package.json +2 -2
- package/dist/conversations/components/ConversationCard.js +0 -119
- 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 {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
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
|
|
17
|
-
let
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
30
|
-
id:
|
|
28
|
+
let e = !!await I({
|
|
29
|
+
id: P.id,
|
|
31
30
|
settings: {
|
|
32
|
-
showTypingIndicators:
|
|
33
|
-
showReadReceipts:
|
|
34
|
-
defaultNotificationLevel:
|
|
31
|
+
showTypingIndicators: z,
|
|
32
|
+
showReadReceipts: V,
|
|
33
|
+
defaultNotificationLevel: U
|
|
35
34
|
}
|
|
36
35
|
});
|
|
37
|
-
|
|
36
|
+
u(e ? "success" : "error"), K(e);
|
|
38
37
|
} catch (e) {
|
|
39
|
-
throw
|
|
38
|
+
throw u("error"), e;
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
|
-
};
|
|
43
|
-
return /* @__PURE__ */
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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__ */
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
174
|
-
|
|
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
|
-
|
|
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
|
|
188
|
-
return /* @__PURE__ */
|
|
189
|
-
permission:
|
|
190
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
211
|
+
children: [/* @__PURE__ */ T("div", {
|
|
193
212
|
className: "flex items-start gap-3",
|
|
194
|
-
children: [/* @__PURE__ */
|
|
213
|
+
children: [/* @__PURE__ */ w("div", {
|
|
195
214
|
className: "p-2 rounded-lg bg-muted text-muted-foreground",
|
|
196
215
|
children: e
|
|
197
|
-
}), /* @__PURE__ */
|
|
216
|
+
}), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("p", {
|
|
198
217
|
className: "text-sm font-medium text-foreground",
|
|
199
218
|
children: t
|
|
200
|
-
}), /* @__PURE__ */
|
|
219
|
+
}), /* @__PURE__ */ w("p", {
|
|
201
220
|
className: "text-xs text-muted-foreground mt-0.5",
|
|
202
221
|
children: n
|
|
203
222
|
})] })]
|
|
204
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
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 {
|
|
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":"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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, 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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"}
|