@burdenoff/microfe-conversations 2026.608.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.
@@ -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-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":";;;;;;;;;;;;;;;;;AAkBA,SAAgB,IAA4B;CAC1C,IAAM,IAAW,GAAa,EACxB,EAAE,gBAAa,GAAkB,EACjC,EAAE,4BAAyB,GAAuB,EAClD,IAAK,GAAO,EACZ,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;;;IAIJ,IAAY,KAAY;AAgB9B,QACE,kBAAC,GAAD;EAAe,QAAQ;EAAO,WAAU;YAAxC,CACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD,EAAa,OAlBK,QAChB,CACJ;IACE,OAAO,EAAG,uBAAuB,gBAAgB;IACjD,MAAM;IACN,eAAe,EAAS,EAAU;IACnC,EACD;IACE,OAAO,EAAG,0BAA0B,WAAW;IAC/C,eAAe;IAChB,CACF,EACD;IAAC;IAAW;IAAU;IAAG,CAC1B,EAK4C,CAAA;GACnC,CAAA,EACN,kBAAC,GAAD;GACE,UAAS;GACT,OAAO,EAAG,0BAA0B,WAAW;GAC/C,UACE,IACI,GAAG,EAAG,6BAA6B,4BAA4B,CAAC,GAAG,EAAa,UAChF,EACE,+CACA,yCACD;GAEP,MAAM,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;GAC5B,UAAS;GACT,eACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,MAAK;KACL,eAAe,EAAS,EAAU;KAClC,cAAY,EACV,6BACA,wBACD;eARH,CAUE,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA,EACtB,EAAG,sBAAsB,OAAO,CAC1B;QACT,kBAAC,GAAD;KACE,MAAK;KACL,eAAe,KAAK,GAAY;KAChC,UAAU,KAAU,CAAC;KACrB,MAAK;eAJP,CAME,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,IACG,EAAG,wBAAwB,YAAY,GACvC,EAAG,6BAA6B,eAAe,CAC5C;OACL;;aAGP,IACC,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAG,iCAAiC,sBAAsB;IACvD,CAAA,GACH,IAQH,kBAAA,GAAA,EAAA,UAAA;IACE,kBAAC,GAAD;KACE,OAAO,EAAG,+BAA+B,gBAAgB;KACzD,aAAa,EACX,0CACA,oDACD;eAED,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;SACd,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,0CACA,6BACD;SACC,CAAA,EACJ,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,8CACA,uDACD;SACC,CAAA,CACA,EAAA,CAAA,CACF;WACN,kBAAC,UAAD;QACE,OAAO;QACP,WAAW,MACT,EAA4B,EAAM,OAAO,MAAM;QAEjD,WAAU;kBALZ;SAOE,kBAAC,UAAD;UAAQ,OAAM;oBACX,EAAG,6BAA6B,eAAe;UACzC,CAAA;SACT,kBAAC,UAAD;UAAQ,OAAM;oBACX,EAAG,8BAA8B,gBAAgB;UAC3C,CAAA;SACT,kBAAC,UAAD;UAAQ,OAAM;oBACX,EAAG,4BAA4B,cAAc;UACvC,CAAA;SACT,kBAAC,UAAD;UAAQ,OAAM;oBACX,EAAG,sBAAsB,OAAO;UAC1B,CAAA;SACF;UACL;;MACF,CAAA;KACM,CAAA;IAEd,kBAAC,GAAD;KACE,OAAO,EAAG,iCAAiC,mBAAmB;KAC9D,aAAa,EACX,4CACA,kCACD;eAED,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA;OACvB,OAAO,EACL,kCACA,oBACD;OACD,aAAa,EACX,6CACA,qCACD;OACD,SAAS;OACT,UAAU;OACV,CAAA,EACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA;OACvB,OAAO,EAAG,8BAA8B,gBAAgB;OACxD,aAAa,EACX,yCACA,oDACD;OACD,SAAS;OACT,UAAU;OACV,CAAA,CACE;;KACM,CAAA;IAEd,kBAAC,GAAD;KACE,OAAO,EAAG,4BAA4B,cAAc;KACpD,aAAa,EACX,uCACA,0DACD;eAED,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBACX;SAAC;SAAQ;SAAO;SAAW,CAAW,KAAK,MAC3C,kBAAC,GAAD;SAEE,YAAY,EAA0B;mBAEtC,kBAAC,UAAD;UACE,MAAK;UACL,UAAU,KAAa,CAAC;UACxB,SAAS,YAAY;WACnB,IAAM,IAAM,MAAM,EAChB,gBACA,EAAc,IACd,EACD;AACD,WAAI,KAAO,EAAgB,YACzB,EAAgB,QAAQ,OAAO,GAC/B,EAAgB,QAAQ,WAAW,gBAAgB,EAAc,GAAG,GAAG,EAAO,aAAa,IAC3F,EAAgB,QAAQ,OAAO;;UAGnC,WAAU;oBAfZ,CAiBE,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA,EACrB,IACG,EAAG,2BAA2B,aAAa,GAC3C,EACG;;SACO,EAzBX,EAyBW,CAClB;QACE,CAAA;OACL,KACC,kBAAC,KAAD;QAAG,WAAU;kBACV,EACC,6BACA,8BACD;QACC,CAAA;OAKN,kBAAC,KAAD;QACE,KAAK;QACL,MAAK;QACL,UAAU;QACV,WAAU;QACV,eAAY;QACZ,CAAA;OACE;;KACM,CAAA;IAEb,KACC,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAU;KACP,CAAA;IAEP,KAAS,CAAC,KACT,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAG,+BAA+B,kBAAkB;KACjD,CAAA;IAEP,EAAA,CAAA,GApKH,kBAAC,OAAD;IAAK,WAAU;cACZ,EACC,uCACA,yDACD;IACG,CAAA;GAiKG,CAAA,CACC;;;AAIpB,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, 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.608.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",