@burdenoff/microfe-conversations 2026.602.1 → 2026.624.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.
- package/dist/conversations/components/LazyPageBoundary.js +7 -7
- package/dist/conversations/components/LazyPageBoundary.js.map +1 -1
- package/dist/conversations/components/MemberPicker.js +8 -8
- package/dist/conversations/components/MemberPicker.js.map +1 -1
- package/dist/conversations/components/MessageBubble.js +18 -18
- package/dist/conversations/components/MessageBubble.js.map +1 -1
- package/dist/conversations/components/NewConversationDialog.js +19 -19
- package/dist/conversations/components/NewConversationDialog.js.map +1 -1
- package/dist/conversations/pages/AdminConversationsLandingPage.js +9 -9
- package/dist/conversations/pages/AdminConversationsLandingPage.js.map +1 -1
- package/dist/conversations/pages/ConversationAdminPages.js +38 -38
- package/dist/conversations/pages/ConversationAdminPages.js.map +1 -1
- package/dist/conversations/pages/ConversationDetailPage.js +35 -35
- package/dist/conversations/pages/ConversationDetailPage.js.map +1 -1
- package/dist/conversations/pages/ConversationsSettingsPage.js +15 -15
- package/dist/conversations/pages/ConversationsSettingsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NewConversationDialog.js","names":[],"sources":["../../../src/conversations/components/NewConversationDialog.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from \"react\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@burdenoff/fe-libs/ui\";\nimport {\n Globe,\n Hash,\n Lock,\n Megaphone,\n MessageCircle,\n Shield,\n Users,\n} from \"lucide-react\";\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\nimport {\n useCreateConversation,\n type WorkspaceDirectoryMember,\n} from \"../hooks/useConversationQueries\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { MemberPicker } from \"./MemberPicker\";\nimport type { ConversationType, ConversationVisibility } from \"../types\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\n/** Extract the first meaningful GraphQL error message from an ApolloError. */\nfunction getErrorMessage(error: Error): string {\n if (\"graphQLErrors\" in error) {\n const gqlErrors = (error as { graphQLErrors?: Array<{ message?: string }> })\n .graphQLErrors;\n if (gqlErrors && gqlErrors.length > 0 && gqlErrors[0]?.message) {\n return gqlErrors[0].message;\n }\n }\n return error.message;\n}\n\ninterface ConversationTypeOption {\n type: ConversationType;\n label: string;\n description: string;\n icon: React.ReactNode;\n}\n\nexport interface NewConversationDialogProps {\n /** Whether the dialog is open. */\n open: boolean;\n /** Open-state change handler. Fires with `false` on close (overlay click,\n * Escape, X button, Cancel). */\n onOpenChange: (open: boolean) => void;\n /**\n * Called after a conversation is successfully created. Receives the new\n * conversation id so the parent can navigate to the detail route.\n * The dialog also closes itself (via `onOpenChange(false)`) before this\n * fires, so the parent does not need to close the dialog manually.\n */\n onCreated?: (conversationId: string) => void;\n}\n\n/**\n * NewConversationDialog — modal form for creating a new conversation.\n *\n * Renders inside the canonical `<Dialog>` primitive from\n * `@burdenoff/fe-libs/ui` so it inherits theme tokens, focus management,\n * and overlay behaviour from the shell.\n *\n * Form state is owned locally; on successful submit the dialog clears\n * state, closes itself, and invokes `onCreated` with the new\n * conversation id.\n */\nexport function NewConversationDialog({\n open,\n onOpenChange,\n onCreated,\n}: NewConversationDialogProps) {\n const { createConversation, loading, error } = useCreateConversation();\n const { currentUser } = useConversations();\n const bus = useEventBus();\n const tr = useTr();\n\n const CONVERSATION_TYPES: ConversationTypeOption[] = useMemo(\n () => [\n {\n type: \"DIRECT\" as ConversationType,\n label: tr(\"conversations.directMessage\", \"Direct Message\"),\n description: tr(\n \"conversations.directMessageDescription\",\n \"One-on-one private conversation\",\n ),\n icon: <MessageCircle size={20} />,\n },\n {\n type: \"GROUP\" as ConversationType,\n label: tr(\"conversations.groupChat\", \"Group Chat\"),\n description: tr(\n \"conversations.groupChatDescription\",\n \"Invite multiple people to a group conversation\",\n ),\n icon: <Users size={20} />,\n },\n {\n type: \"CHANNEL\" as ConversationType,\n label: tr(\"conversations.channel\", \"Channel\"),\n description: tr(\n \"conversations.channelDescription\",\n \"A topic-based channel that anyone can join\",\n ),\n icon: <Hash size={20} />,\n },\n {\n type: \"ANNOUNCEMENT\" as ConversationType,\n label: tr(\"conversations.announcement\", \"Announcement\"),\n description: tr(\n \"conversations.announcementDescription\",\n \"Broadcast messages to a large audience\",\n ),\n icon: <Megaphone size={20} />,\n },\n ],\n [tr],\n );\n\n const VISIBILITY_OPTIONS: Array<{\n value: ConversationVisibility;\n label: string;\n description: string;\n icon: React.ReactNode;\n }> = useMemo(\n () => [\n {\n value: \"PUBLIC\" as ConversationVisibility,\n label: tr(\"conversations.public\", \"Public\"),\n description: tr(\n \"conversations.publicDescription\",\n \"Anyone in the workspace can discover and join\",\n ),\n icon: <Globe size={16} />,\n },\n {\n value: \"PRIVATE\" as ConversationVisibility,\n label: tr(\"conversations.private\", \"Private\"),\n description: tr(\n \"conversations.privateDescription\",\n \"Only invited members can access\",\n ),\n icon: <Lock size={16} />,\n },\n {\n value: \"PROTECTED\" as ConversationVisibility,\n label: tr(\"conversations.protected\", \"Protected\"),\n description: tr(\n \"conversations.protectedDescription\",\n \"Discoverable but requires approval to join\",\n ),\n icon: <Shield size={16} />,\n },\n ],\n [tr],\n );\n\n const [selectedType, setSelectedType] = useState<ConversationType>(\"GROUP\");\n const [title, setTitle] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [visibility, setVisibility] =\n useState<ConversationVisibility>(\"PRIVATE\");\n const [selectedMembers, setSelectedMembers] = useState<\n WorkspaceDirectoryMember[]\n >([]);\n\n const isDirectMessage = selectedType === \"DIRECT\";\n const needsTitle = !isDirectMessage;\n\n const participantIds = useMemo(\n () => selectedMembers.map((member) => member.id),\n [selectedMembers],\n );\n\n const resetForm = useCallback(() => {\n setSelectedType(\"GROUP\");\n setTitle(\"\");\n setDescription(\"\");\n setVisibility(\"PRIVATE\");\n setSelectedMembers([]);\n }, []);\n\n const addMember = useCallback(\n (member: WorkspaceDirectoryMember) => {\n setSelectedMembers((prev) => {\n if (prev.some((existing) => existing.id === member.id)) return prev;\n // A direct message has exactly one recipient.\n return isDirectMessage ? [member] : [...prev, member];\n });\n },\n [isDirectMessage],\n );\n\n const removeMember = useCallback((userId: string) => {\n setSelectedMembers((prev) => prev.filter((member) => member.id !== userId));\n }, []);\n\n const requiresParticipants =\n selectedType === \"DIRECT\" || selectedType === \"GROUP\";\n\n // Never offer the current user as a participant — they are added implicitly\n // as the conversation creator.\n const excludeIds = useMemo(\n () => (currentUser?.id ? [currentUser.id] : []),\n [currentUser?.id],\n );\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean) => {\n if (!nextOpen) {\n resetForm();\n }\n onOpenChange(nextOpen);\n },\n [onOpenChange, resetForm],\n );\n\n const handleCreate = useCallback(async () => {\n if (requiresParticipants && participantIds.length === 0) return;\n if (needsTitle && !title.trim()) return;\n\n nativeImpact(\"light\");\n try {\n const conversation = await createConversation({\n type: selectedType,\n title: needsTitle ? title.trim() : undefined,\n description: description.trim() || undefined,\n participantIds,\n visibility,\n });\n\n if (!conversation) {\n nativeNotify(\"error\");\n return;\n }\n nativeNotify(\"success\");\n bus.emit(\"conversations.conversation.created\", {\n conversationId: conversation.id,\n conversationType: selectedType,\n source: \"new-conversation-dialog\",\n });\n resetForm();\n onOpenChange(false);\n onCreated?.(conversation.id);\n } catch (error) {\n nativeNotify(\"error\");\n throw error;\n }\n }, [\n bus,\n requiresParticipants,\n participantIds,\n needsTitle,\n title,\n createConversation,\n selectedType,\n description,\n visibility,\n resetForm,\n onOpenChange,\n onCreated,\n ]);\n\n const canCreate =\n (!requiresParticipants || participantIds.length > 0) &&\n (!needsTitle || title.trim().length > 0);\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogContent\n className=\"sm:max-w-[640px] max-h-[90vh] overflow-y-auto\"\n data-tour=\"new-conversation-dialog\"\n >\n <DialogHeader>\n <DialogTitle>\n {tr(\"conversations.newConversation\", \"New Conversation\")}\n </DialogTitle>\n <DialogDescription>\n {tr(\n \"conversations.newConversationSubtitle\",\n \"Start a new conversation with your team\",\n )}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-6\">\n <section data-tour=\"conversation-type-selector\" className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n {tr(\"conversations.conversationType\", \"Conversation Type\")}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n {CONVERSATION_TYPES.map((option) => (\n <button\n key={option.type}\n type=\"button\"\n onClick={() => {\n setSelectedType(option.type);\n if (option.type === \"DIRECT\") {\n setSelectedMembers((prev) => prev.slice(0, 1));\n }\n }}\n className={cn(\n \"flex items-start gap-3 p-4 rounded-xl border text-left transition-all duration-150\",\n selectedType === option.type\n ? \"border-primary bg-primary/5 ring-1 ring-primary/20\"\n : \"border-border hover:border-muted-foreground/30 hover:bg-muted/30\",\n )}\n >\n <div\n className={cn(\n \"p-2 rounded-lg\",\n selectedType === option.type\n ? \"bg-primary/10 text-primary\"\n : \"bg-muted text-muted-foreground\",\n )}\n >\n {option.icon}\n </div>\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {option.label}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {option.description}\n </p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {!isDirectMessage && (\n <section\n data-tour=\"conversation-visibility-selector\"\n className=\"space-y-3\"\n >\n <h3 className=\"text-sm font-semibold text-foreground\">\n {tr(\"conversations.visibility\", \"Visibility\")}\n </h3>\n <div className=\"flex gap-2 flex-wrap\">\n {VISIBILITY_OPTIONS.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => setVisibility(option.value)}\n className={cn(\n \"flex items-center gap-2 px-4 py-2.5 rounded-lg border text-sm transition-all\",\n visibility === option.value\n ? \"border-primary bg-primary/5 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:text-foreground hover:bg-muted/30\",\n )}\n >\n {option.icon}\n {option.label}\n </button>\n ))}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {\n VISIBILITY_OPTIONS.find(\n (option) => option.value === visibility,\n )?.description\n }\n </p>\n </section>\n )}\n\n {needsTitle && (\n <section\n data-tour=\"conversation-create-details\"\n className=\"space-y-3\"\n >\n <div>\n <label\n htmlFor=\"conv-title\"\n className=\"text-sm font-semibold text-foreground\"\n >\n {tr(\"conversations.name\", \"Name\")}{\" \"}\n <span className=\"text-destructive\">*</span>\n </label>\n <input\n id=\"conv-title\"\n type=\"text\"\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n placeholder={\n selectedType === \"CHANNEL\"\n ? tr(\n \"conversations.channelNamePlaceholder\",\n \"e.g., engineering-general\",\n )\n : tr(\n \"conversations.conversationNamePlaceholder\",\n \"e.g., Product Launch Q1\",\n )\n }\n maxLength={200}\n className=\"mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring\"\n />\n </div>\n <div>\n <label\n htmlFor=\"conv-desc\"\n className=\"text-sm font-semibold text-foreground\"\n >\n {tr(\"conversations.description\", \"Description\")}\n </label>\n <textarea\n id=\"conv-desc\"\n value={description}\n onChange={(event) => setDescription(event.target.value)}\n placeholder={tr(\n \"conversations.descriptionPlaceholder\",\n \"What is this conversation about?\",\n )}\n rows={2}\n maxLength={1000}\n className=\"mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring resize-none\"\n />\n </div>\n </section>\n )}\n\n <section data-tour=\"conversation-participants\" className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n {isDirectMessage\n ? tr(\"conversations.recipient\", \"Recipient\")\n : tr(\"conversations.participants\", \"Participants\")}\n {requiresParticipants ? (\n <span className=\"ml-1 text-destructive\">*</span>\n ) : (\n <span className=\"ml-1.5 text-xs font-normal text-muted-foreground\">\n ({tr(\"conversations.optional\", \"optional\")})\n </span>\n )}\n </h3>\n\n <MemberPicker\n selected={selectedMembers}\n onAdd={addMember}\n onRemove={removeMember}\n single={isDirectMessage}\n excludeIds={excludeIds}\n />\n </section>\n\n {error && (\n <div className=\"text-xs text-destructive rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2\">\n {getErrorMessage(error)}\n </div>\n )}\n </div>\n\n <DialogFooter>\n <button\n type=\"button\"\n onClick={() => handleOpenChange(false)}\n className=\"px-4 py-2 text-sm font-medium rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n {tr(\"conversations.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleCreate()}\n disabled={!canCreate || loading}\n className={cn(\n \"inline-flex items-center gap-2 px-5 py-2 text-sm font-medium rounded-lg transition-all\",\n canCreate && !loading\n ? \"bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm\"\n : \"bg-muted text-muted-foreground cursor-not-allowed\",\n )}\n >\n {loading\n ? tr(\"conversations.creating\", \"Creating...\")\n : tr(\"conversations.createConversation\", \"Create Conversation\")}\n </button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NewConversationDialog.js","names":[],"sources":["../../../src/conversations/components/NewConversationDialog.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from \"react\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@burdenoff/fe-libs/ui\";\nimport {\n Globe,\n Hash,\n Lock,\n Megaphone,\n MessageCircle,\n Shield,\n Users,\n} from \"lucide-react\";\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\nimport {\n useCreateConversation,\n type WorkspaceDirectoryMember,\n} from \"../hooks/useConversationQueries\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { MemberPicker } from \"./MemberPicker\";\nimport type { ConversationType, ConversationVisibility } from \"../types\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\n/** Extract the first meaningful GraphQL error message from an ApolloError. */\nfunction getErrorMessage(error: Error): string {\n if (\"graphQLErrors\" in error) {\n const gqlErrors = (error as { graphQLErrors?: Array<{ message?: string }> })\n .graphQLErrors;\n if (gqlErrors && gqlErrors.length > 0 && gqlErrors[0]?.message) {\n return gqlErrors[0].message;\n }\n }\n return error.message;\n}\n\ninterface ConversationTypeOption {\n type: ConversationType;\n label: string;\n description: string;\n icon: React.ReactNode;\n}\n\nexport interface NewConversationDialogProps {\n /** Whether the dialog is open. */\n open: boolean;\n /** Open-state change handler. Fires with `false` on close (overlay click,\n * Escape, X button, Cancel). */\n onOpenChange: (open: boolean) => void;\n /**\n * Called after a conversation is successfully created. Receives the new\n * conversation id so the parent can navigate to the detail route.\n * The dialog also closes itself (via `onOpenChange(false)`) before this\n * fires, so the parent does not need to close the dialog manually.\n */\n onCreated?: (conversationId: string) => void;\n}\n\n/**\n * NewConversationDialog — modal form for creating a new conversation.\n *\n * Renders inside the canonical `<Dialog>` primitive from\n * `@burdenoff/fe-libs/ui` so it inherits theme tokens, focus management,\n * and overlay behaviour from the shell.\n *\n * Form state is owned locally; on successful submit the dialog clears\n * state, closes itself, and invokes `onCreated` with the new\n * conversation id.\n */\nexport function NewConversationDialog({\n open,\n onOpenChange,\n onCreated,\n}: NewConversationDialogProps) {\n const { createConversation, loading, error } = useCreateConversation();\n const { currentUser } = useConversations();\n const bus = useEventBus();\n const tr = useTr();\n\n const CONVERSATION_TYPES: ConversationTypeOption[] = useMemo(\n () => [\n {\n type: \"DIRECT\" as ConversationType,\n label: tr(\"conversations.directMessage\", \"Direct Message\"),\n description: tr(\n \"conversations.directMessageDescription\",\n \"One-on-one private conversation\",\n ),\n icon: <MessageCircle size={20} />,\n },\n {\n type: \"GROUP\" as ConversationType,\n label: tr(\"conversations.groupChat\", \"Group Chat\"),\n description: tr(\n \"conversations.groupChatDescription\",\n \"Invite multiple people to a group conversation\",\n ),\n icon: <Users size={20} />,\n },\n {\n type: \"CHANNEL\" as ConversationType,\n label: tr(\"conversations.channel\", \"Channel\"),\n description: tr(\n \"conversations.channelDescription\",\n \"A topic-based channel that anyone can join\",\n ),\n icon: <Hash size={20} />,\n },\n {\n type: \"ANNOUNCEMENT\" as ConversationType,\n label: tr(\"conversations.announcement\", \"Announcement\"),\n description: tr(\n \"conversations.announcementDescription\",\n \"Broadcast messages to a large audience\",\n ),\n icon: <Megaphone size={20} />,\n },\n ],\n [tr],\n );\n\n const VISIBILITY_OPTIONS: Array<{\n value: ConversationVisibility;\n label: string;\n description: string;\n icon: React.ReactNode;\n }> = useMemo(\n () => [\n {\n value: \"PUBLIC\" as ConversationVisibility,\n label: tr(\"conversations.public\", \"Public\"),\n description: tr(\n \"conversations.publicDescription\",\n \"Anyone in the workspace can discover and join\",\n ),\n icon: <Globe size={16} />,\n },\n {\n value: \"PRIVATE\" as ConversationVisibility,\n label: tr(\"conversations.private\", \"Private\"),\n description: tr(\n \"conversations.privateDescription\",\n \"Only invited members can access\",\n ),\n icon: <Lock size={16} />,\n },\n {\n value: \"PROTECTED\" as ConversationVisibility,\n label: tr(\"conversations.protected\", \"Protected\"),\n description: tr(\n \"conversations.protectedDescription\",\n \"Discoverable but requires approval to join\",\n ),\n icon: <Shield size={16} />,\n },\n ],\n [tr],\n );\n\n const [selectedType, setSelectedType] = useState<ConversationType>(\"GROUP\");\n const [title, setTitle] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [visibility, setVisibility] =\n useState<ConversationVisibility>(\"PRIVATE\");\n const [selectedMembers, setSelectedMembers] = useState<\n WorkspaceDirectoryMember[]\n >([]);\n\n const isDirectMessage = selectedType === \"DIRECT\";\n const needsTitle = !isDirectMessage;\n\n const participantIds = useMemo(\n () => selectedMembers.map((member) => member.id),\n [selectedMembers],\n );\n\n const resetForm = useCallback(() => {\n setSelectedType(\"GROUP\");\n setTitle(\"\");\n setDescription(\"\");\n setVisibility(\"PRIVATE\");\n setSelectedMembers([]);\n }, []);\n\n const addMember = useCallback(\n (member: WorkspaceDirectoryMember) => {\n setSelectedMembers((prev) => {\n if (prev.some((existing) => existing.id === member.id)) return prev;\n // A direct message has exactly one recipient.\n return isDirectMessage ? [member] : [...prev, member];\n });\n },\n [isDirectMessage],\n );\n\n const removeMember = useCallback((userId: string) => {\n setSelectedMembers((prev) => prev.filter((member) => member.id !== userId));\n }, []);\n\n const requiresParticipants =\n selectedType === \"DIRECT\" || selectedType === \"GROUP\";\n\n // Never offer the current user as a participant — they are added implicitly\n // as the conversation creator.\n const excludeIds = useMemo(\n () => (currentUser?.id ? [currentUser.id] : []),\n [currentUser?.id],\n );\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean) => {\n if (!nextOpen) {\n resetForm();\n }\n onOpenChange(nextOpen);\n },\n [onOpenChange, resetForm],\n );\n\n const handleCreate = useCallback(async () => {\n if (requiresParticipants && participantIds.length === 0) return;\n if (needsTitle && !title.trim()) return;\n\n nativeImpact(\"light\");\n try {\n const conversation = await createConversation({\n type: selectedType,\n title: needsTitle ? title.trim() : undefined,\n description: description.trim() || undefined,\n participantIds,\n visibility,\n });\n\n if (!conversation) {\n nativeNotify(\"error\");\n return;\n }\n nativeNotify(\"success\");\n bus.emit(\"conversations.conversation.created\", {\n conversationId: conversation.id,\n conversationType: selectedType,\n source: \"new-conversation-dialog\",\n });\n resetForm();\n onOpenChange(false);\n onCreated?.(conversation.id);\n } catch (error) {\n nativeNotify(\"error\");\n throw error;\n }\n }, [\n bus,\n requiresParticipants,\n participantIds,\n needsTitle,\n title,\n createConversation,\n selectedType,\n description,\n visibility,\n resetForm,\n onOpenChange,\n onCreated,\n ]);\n\n const canCreate =\n (!requiresParticipants || participantIds.length > 0) &&\n (!needsTitle || title.trim().length > 0);\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogContent\n className=\"sm:max-w-[640px] max-h-[90vh] overflow-y-auto\"\n data-tour=\"new-conversation-dialog\"\n >\n <DialogHeader>\n <DialogTitle>\n {tr(\"conversations.newConversation\", \"New Conversation\")}\n </DialogTitle>\n <DialogDescription>\n {tr(\n \"conversations.newConversationSubtitle\",\n \"Start a new conversation with your team\",\n )}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-6\">\n <section data-tour=\"conversation-type-selector\" className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr(\"conversations.conversationType\", \"Conversation Type\")}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n {CONVERSATION_TYPES.map((option) => (\n <button\n key={option.type}\n type=\"button\"\n onClick={() => {\n setSelectedType(option.type);\n if (option.type === \"DIRECT\") {\n setSelectedMembers((prev) => prev.slice(0, 1));\n }\n }}\n className={cn(\n \"flex items-start gap-3 p-4 rounded-xl border text-left transition-all duration-150\",\n selectedType === option.type\n ? \"border-primary bg-action-primary-bg/5 ring-1 ring-primary/20\"\n : \"border-border-subtle hover:border-muted-foreground/30 hover:bg-bg-sunken/30\",\n )}\n >\n <div\n className={cn(\n \"p-2 rounded-lg\",\n selectedType === option.type\n ? \"bg-action-primary-bg/10 text-primary\"\n : \"bg-bg-sunken text-text-secondary\",\n )}\n >\n {option.icon}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {option.label}\n </p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {option.description}\n </p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {!isDirectMessage && (\n <section\n data-tour=\"conversation-visibility-selector\"\n className=\"space-y-3\"\n >\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {tr(\"conversations.visibility\", \"Visibility\")}\n </h3>\n <div className=\"flex gap-2 flex-wrap\">\n {VISIBILITY_OPTIONS.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => setVisibility(option.value)}\n className={cn(\n \"flex items-center gap-2 px-4 py-2.5 rounded-lg border text-sm transition-all\",\n visibility === option.value\n ? \"border-primary bg-action-primary-bg/5 text-primary font-medium\"\n : \"border-border-subtle text-text-secondary hover:text-text-primary hover:bg-bg-sunken/30\",\n )}\n >\n {option.icon}\n {option.label}\n </button>\n ))}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {\n VISIBILITY_OPTIONS.find(\n (option) => option.value === visibility,\n )?.description\n }\n </p>\n </section>\n )}\n\n {needsTitle && (\n <section\n data-tour=\"conversation-create-details\"\n className=\"space-y-3\"\n >\n <div>\n <label\n htmlFor=\"conv-title\"\n className=\"text-sm font-semibold text-text-primary\"\n >\n {tr(\"conversations.name\", \"Name\")}{\" \"}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"conv-title\"\n type=\"text\"\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n placeholder={\n selectedType === \"CHANNEL\"\n ? tr(\n \"conversations.channelNamePlaceholder\",\n \"e.g., engineering-general\",\n )\n : tr(\n \"conversations.conversationNamePlaceholder\",\n \"e.g., Product Launch Q1\",\n )\n }\n maxLength={200}\n className=\"mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border-subtle bg-bg-surface placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <div>\n <label\n htmlFor=\"conv-desc\"\n className=\"text-sm font-semibold text-text-primary\"\n >\n {tr(\"conversations.description\", \"Description\")}\n </label>\n <textarea\n id=\"conv-desc\"\n value={description}\n onChange={(event) => setDescription(event.target.value)}\n placeholder={tr(\n \"conversations.descriptionPlaceholder\",\n \"What is this conversation about?\",\n )}\n rows={2}\n maxLength={1000}\n className=\"mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border-subtle bg-bg-surface placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n </section>\n )}\n\n <section data-tour=\"conversation-participants\" className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {isDirectMessage\n ? tr(\"conversations.recipient\", \"Recipient\")\n : tr(\"conversations.participants\", \"Participants\")}\n {requiresParticipants ? (\n <span className=\"ml-1 text-status-error-text\">*</span>\n ) : (\n <span className=\"ml-1.5 text-xs font-normal text-text-secondary\">\n ({tr(\"conversations.optional\", \"optional\")})\n </span>\n )}\n </h3>\n\n <MemberPicker\n selected={selectedMembers}\n onAdd={addMember}\n onRemove={removeMember}\n single={isDirectMessage}\n excludeIds={excludeIds}\n />\n </section>\n\n {error && (\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 {getErrorMessage(error)}\n </div>\n )}\n </div>\n\n <DialogFooter>\n <button\n type=\"button\"\n onClick={() => handleOpenChange(false)}\n className=\"px-4 py-2 text-sm font-medium rounded-lg text-text-secondary hover:text-text-primary hover:bg-bg-sunken/50 transition-colors\"\n >\n {tr(\"conversations.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleCreate()}\n disabled={!canCreate || loading}\n className={cn(\n \"inline-flex items-center gap-2 px-5 py-2 text-sm font-medium rounded-lg transition-all\",\n canCreate && !loading\n ? \"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 shadow-sm\"\n : \"bg-bg-sunken text-text-secondary cursor-not-allowed\",\n )}\n >\n {loading\n ? tr(\"conversations.creating\", \"Creating...\")\n : tr(\"conversations.createConversation\", \"Create Conversation\")}\n </button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -67,16 +67,16 @@ function f() {
|
|
|
67
67
|
children: /* @__PURE__ */ u("div", {
|
|
68
68
|
className: "flex items-center gap-3",
|
|
69
69
|
children: [/* @__PURE__ */ l("div", {
|
|
70
|
-
className: "p-2 rounded-md bg-
|
|
70
|
+
className: "p-2 rounded-md bg-bg-sunken",
|
|
71
71
|
children: /* @__PURE__ */ l(a, {
|
|
72
|
-
className: "size-5 text-
|
|
72
|
+
className: "size-5 text-text-secondary",
|
|
73
73
|
"aria-hidden": "true"
|
|
74
74
|
})
|
|
75
75
|
}), /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h1", {
|
|
76
|
-
className: "text-lg font-semibold text-
|
|
76
|
+
className: "text-lg font-semibold text-text-primary",
|
|
77
77
|
children: n("conversations.admin.landingTitle", "Conversations admin")
|
|
78
78
|
}), /* @__PURE__ */ l("p", {
|
|
79
|
-
className: "text-sm text-
|
|
79
|
+
className: "text-sm text-text-secondary",
|
|
80
80
|
children: n("conversations.admin.landingDescription", "Governance tools for workspace conversations. Select a section below to continue.")
|
|
81
81
|
})] })]
|
|
82
82
|
})
|
|
@@ -86,23 +86,23 @@ function f() {
|
|
|
86
86
|
className: "grid gap-3 sm:grid-cols-2 max-w-3xl",
|
|
87
87
|
children: i.map((e) => /* @__PURE__ */ u(c, {
|
|
88
88
|
to: e.path,
|
|
89
|
-
className: "group flex items-start gap-3 rounded-lg border border-border-subtle bg-
|
|
89
|
+
className: "group flex items-start gap-3 rounded-lg border border-border-subtle bg-bg-surface p-4 transition-colors hover:border-border-subtle hover:bg-accent",
|
|
90
90
|
children: [/* @__PURE__ */ l("div", {
|
|
91
|
-
className: "p-2 rounded-md bg-
|
|
91
|
+
className: "p-2 rounded-md bg-bg-sunken text-text-secondary group-hover:text-text-primary",
|
|
92
92
|
children: e.icon
|
|
93
93
|
}), /* @__PURE__ */ u("div", {
|
|
94
94
|
className: "flex-1 min-w-0",
|
|
95
95
|
children: [/* @__PURE__ */ u("div", {
|
|
96
96
|
className: "flex items-center justify-between gap-2",
|
|
97
97
|
children: [/* @__PURE__ */ l("span", {
|
|
98
|
-
className: "text-sm font-medium text-
|
|
98
|
+
className: "text-sm font-medium text-text-primary",
|
|
99
99
|
children: e.label
|
|
100
100
|
}), /* @__PURE__ */ l(r, {
|
|
101
|
-
className: "size-4 text-
|
|
101
|
+
className: "size-4 text-text-secondary",
|
|
102
102
|
"aria-hidden": "true"
|
|
103
103
|
})]
|
|
104
104
|
}), /* @__PURE__ */ l("p", {
|
|
105
|
-
className: "mt-1 text-xs text-
|
|
105
|
+
className: "mt-1 text-xs text-text-secondary",
|
|
106
106
|
children: e.description
|
|
107
107
|
})]
|
|
108
108
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminConversationsLandingPage.js","names":[],"sources":["../../../src/conversations/pages/AdminConversationsLandingPage.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { useMemo } from \"react\";\nimport { Link } from \"react-router-dom\";\nimport {\n MessageCircle,\n ShieldCheck,\n ListChecks,\n Activity,\n Package,\n ChevronRight,\n} from \"lucide-react\";\nimport { useTr } from \"../utils/i18n\";\n\ninterface AdminSectionDefinition {\n id: string;\n /** i18n key used for the card label */\n labelKey: string;\n /** English fallback rendered when the i18n key is not resolved */\n labelFallback: string;\n /** i18n key used for the card description */\n descriptionKey: string;\n /** English fallback for the description */\n descriptionFallback: string;\n /** Route path relative to the admin basePath */\n path: string;\n icon: ReactNode;\n}\n\n// Module-scoped section catalogue. The icon JSX is stable (purely presentational,\n// no context or dynamic props) so constructing once at parse time avoids rebuilding\n// four lucide nodes on every render. If icons ever need to respond to context\n// (theme, RBAC), move the icon field into the useMemo mapper below.\nconst SECTION_DEFINITIONS: AdminSectionDefinition[] = [\n {\n id: \"moderation\",\n labelKey: \"conversations.admin.moderation\",\n labelFallback: \"Moderation\",\n descriptionKey: \"conversations.admin.moderationDescription\",\n descriptionFallback:\n \"Reported and flagged messages, moderation actions, and reviewer assignments.\",\n path: \"moderation\",\n icon: <ShieldCheck className=\"size-5\" aria-hidden=\"true\" />,\n },\n {\n id: \"bulk\",\n labelKey: \"conversations.admin.bulkOperations\",\n labelFallback: \"Bulk Operations\",\n descriptionKey: \"conversations.admin.bulkOperationsDescription\",\n descriptionFallback:\n \"Bulk archive / unarchive, participant edits, and cross-conversation actions.\",\n path: \"bulk\",\n icon: <ListChecks className=\"size-5\" aria-hidden=\"true\" />,\n },\n {\n id: \"audit\",\n labelKey: \"conversations.admin.auditLog\",\n labelFallback: \"Audit Log\",\n descriptionKey: \"conversations.admin.auditLogDescription\",\n descriptionFallback:\n \"Activity stream for admin / moderator actions on workspace conversations.\",\n path: \"audit\",\n icon: <Activity className=\"size-5\" aria-hidden=\"true\" />,\n },\n {\n id: \"apps\",\n labelKey: \"conversations.admin.appInstallations\",\n labelFallback: \"App Installations\",\n descriptionKey: \"conversations.admin.appInstallationsDescription\",\n descriptionFallback:\n \"Install, configure, and remove conversation apps and integrations.\",\n path: \"apps\",\n icon: <Package className=\"size-5\" aria-hidden=\"true\" />,\n },\n];\n\n/**\n * Admin-facing landing page for the Conversations microfrontend.\n *\n * Lightweight overview + section directory. Each card links to its canonical\n * sub-route (`/moderation`, `/bulk`, `/audit`, `/apps`) for the API-backed\n * admin surfaces.\n */\nexport function AdminConversationsLandingPage() {\n const tr = useTr();\n\n const sections = useMemo(\n () =>\n SECTION_DEFINITIONS.map((section) => ({\n ...section,\n label: tr(section.labelKey, section.labelFallback),\n description: tr(section.descriptionKey, section.descriptionFallback),\n })),\n [tr],\n );\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"border-b border-border-subtle px-6 py-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-md bg-
|
|
1
|
+
{"version":3,"file":"AdminConversationsLandingPage.js","names":[],"sources":["../../../src/conversations/pages/AdminConversationsLandingPage.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { useMemo } from \"react\";\nimport { Link } from \"react-router-dom\";\nimport {\n MessageCircle,\n ShieldCheck,\n ListChecks,\n Activity,\n Package,\n ChevronRight,\n} from \"lucide-react\";\nimport { useTr } from \"../utils/i18n\";\n\ninterface AdminSectionDefinition {\n id: string;\n /** i18n key used for the card label */\n labelKey: string;\n /** English fallback rendered when the i18n key is not resolved */\n labelFallback: string;\n /** i18n key used for the card description */\n descriptionKey: string;\n /** English fallback for the description */\n descriptionFallback: string;\n /** Route path relative to the admin basePath */\n path: string;\n icon: ReactNode;\n}\n\n// Module-scoped section catalogue. The icon JSX is stable (purely presentational,\n// no context or dynamic props) so constructing once at parse time avoids rebuilding\n// four lucide nodes on every render. If icons ever need to respond to context\n// (theme, RBAC), move the icon field into the useMemo mapper below.\nconst SECTION_DEFINITIONS: AdminSectionDefinition[] = [\n {\n id: \"moderation\",\n labelKey: \"conversations.admin.moderation\",\n labelFallback: \"Moderation\",\n descriptionKey: \"conversations.admin.moderationDescription\",\n descriptionFallback:\n \"Reported and flagged messages, moderation actions, and reviewer assignments.\",\n path: \"moderation\",\n icon: <ShieldCheck className=\"size-5\" aria-hidden=\"true\" />,\n },\n {\n id: \"bulk\",\n labelKey: \"conversations.admin.bulkOperations\",\n labelFallback: \"Bulk Operations\",\n descriptionKey: \"conversations.admin.bulkOperationsDescription\",\n descriptionFallback:\n \"Bulk archive / unarchive, participant edits, and cross-conversation actions.\",\n path: \"bulk\",\n icon: <ListChecks className=\"size-5\" aria-hidden=\"true\" />,\n },\n {\n id: \"audit\",\n labelKey: \"conversations.admin.auditLog\",\n labelFallback: \"Audit Log\",\n descriptionKey: \"conversations.admin.auditLogDescription\",\n descriptionFallback:\n \"Activity stream for admin / moderator actions on workspace conversations.\",\n path: \"audit\",\n icon: <Activity className=\"size-5\" aria-hidden=\"true\" />,\n },\n {\n id: \"apps\",\n labelKey: \"conversations.admin.appInstallations\",\n labelFallback: \"App Installations\",\n descriptionKey: \"conversations.admin.appInstallationsDescription\",\n descriptionFallback:\n \"Install, configure, and remove conversation apps and integrations.\",\n path: \"apps\",\n icon: <Package className=\"size-5\" aria-hidden=\"true\" />,\n },\n];\n\n/**\n * Admin-facing landing page for the Conversations microfrontend.\n *\n * Lightweight overview + section directory. Each card links to its canonical\n * sub-route (`/moderation`, `/bulk`, `/audit`, `/apps`) for the API-backed\n * admin surfaces.\n */\nexport function AdminConversationsLandingPage() {\n const tr = useTr();\n\n const sections = useMemo(\n () =>\n SECTION_DEFINITIONS.map((section) => ({\n ...section,\n label: tr(section.labelKey, section.labelFallback),\n description: tr(section.descriptionKey, section.descriptionFallback),\n })),\n [tr],\n );\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"border-b border-border-subtle px-6 py-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 rounded-md bg-bg-sunken\">\n <MessageCircle\n className=\"size-5 text-text-secondary\"\n aria-hidden=\"true\"\n />\n </div>\n <div>\n <h1 className=\"text-lg font-semibold text-text-primary\">\n {tr(\"conversations.admin.landingTitle\", \"Conversations admin\")}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"conversations.admin.landingDescription\",\n \"Governance tools for workspace conversations. Select a section below to continue.\",\n )}\n </p>\n </div>\n </div>\n </div>\n <div className=\"flex-1 min-h-0 overflow-auto p-6\">\n <div className=\"grid gap-3 sm:grid-cols-2 max-w-3xl\">\n {sections.map((section) => (\n <Link\n key={section.id}\n to={section.path}\n className=\"group flex items-start gap-3 rounded-lg border border-border-subtle bg-bg-surface p-4 transition-colors hover:border-border-subtle hover:bg-accent\"\n >\n <div className=\"p-2 rounded-md bg-bg-sunken text-text-secondary group-hover:text-text-primary\">\n {section.icon}\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">\n {section.label}\n </span>\n <ChevronRight\n className=\"size-4 text-text-secondary\"\n aria-hidden=\"true\"\n />\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {section.description}\n </p>\n </div>\n </Link>\n ))}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;AAgCA,IAAM,IAAgD;CACpD;EACE,IAAI;EACJ,UAAU;EACV,eAAe;EACf,gBAAgB;EAChB,qBACE;EACF,MAAM;EACN,MAAM,kBAAC,GAAD;GAAa,WAAU;GAAS,eAAY;GAAS,CAAA;EAC5D;CACD;EACE,IAAI;EACJ,UAAU;EACV,eAAe;EACf,gBAAgB;EAChB,qBACE;EACF,MAAM;EACN,MAAM,kBAAC,GAAD;GAAY,WAAU;GAAS,eAAY;GAAS,CAAA;EAC3D;CACD;EACE,IAAI;EACJ,UAAU;EACV,eAAe;EACf,gBAAgB;EAChB,qBACE;EACF,MAAM;EACN,MAAM,kBAAC,GAAD;GAAU,WAAU;GAAS,eAAY;GAAS,CAAA;EACzD;CACD;EACE,IAAI;EACJ,UAAU;EACV,eAAe;EACf,gBAAgB;EAChB,qBACE;EACF,MAAM;EACN,MAAM,kBAAC,GAAD;GAAS,WAAU;GAAS,eAAY;GAAS,CAAA;EACxD;CACF;AASD,SAAgB,IAAgC;CAC9C,IAAM,IAAK,GAAO,EAEZ,IAAW,QAEb,EAAoB,KAAK,OAAa;EACpC,GAAG;EACH,OAAO,EAAG,EAAQ,UAAU,EAAQ,cAAc;EAClD,aAAa,EAAG,EAAQ,gBAAgB,EAAQ,oBAAoB;EACrE,EAAE,EACL,CAAC,EAAG,CACL;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MACE,WAAU;MACV,eAAY;MACZ,CAAA;KACE,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAG,oCAAoC,sBAAsB;KAC3D,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,0CACA,oFACD;KACC,CAAA,CACA,EAAA,CAAA,CACF;;GACF,CAAA,EACN,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAS,KAAK,MACb,kBAAC,GAAD;KAEE,IAAI,EAAQ;KACZ,WAAU;eAHZ,CAKE,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAQ;MACL,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAQ;QACJ,CAAA,EACP,kBAAC,GAAD;QACE,WAAU;QACV,eAAY;QACZ,CAAA,CACE;UACN,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAQ;OACP,CAAA,CACA;QACD;OArBA,EAAQ,GAqBR,CACP;IACE,CAAA;GACF,CAAA,CACF"}
|
|
@@ -158,7 +158,7 @@ function A(e) {
|
|
|
158
158
|
}
|
|
159
159
|
function j({ children: e }) {
|
|
160
160
|
return /* @__PURE__ */ _("span", {
|
|
161
|
-
className: "rounded-full border border-border bg-
|
|
161
|
+
className: "rounded-full border border-border-subtle bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary",
|
|
162
162
|
children: e
|
|
163
163
|
});
|
|
164
164
|
}
|
|
@@ -188,11 +188,11 @@ function M() {
|
|
|
188
188
|
})
|
|
189
189
|
}),
|
|
190
190
|
a && s && /* @__PURE__ */ _("p", {
|
|
191
|
-
className: "text-sm text-
|
|
191
|
+
className: "text-sm text-text-secondary",
|
|
192
192
|
children: "Loading reports…"
|
|
193
193
|
}),
|
|
194
194
|
a && c && /* @__PURE__ */ _("p", {
|
|
195
|
-
className: "text-sm text-
|
|
195
|
+
className: "text-sm text-status-error-text",
|
|
196
196
|
children: c.message
|
|
197
197
|
}),
|
|
198
198
|
a && !s && !c && m.length === 0 && /* @__PURE__ */ _(r, {
|
|
@@ -206,7 +206,7 @@ function M() {
|
|
|
206
206
|
/* @__PURE__ */ _("div", {
|
|
207
207
|
className: "grid gap-3",
|
|
208
208
|
children: m.map((e) => /* @__PURE__ */ _("article", {
|
|
209
|
-
className: "rounded-lg border border-border bg-
|
|
209
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface p-4 shadow-sm",
|
|
210
210
|
children: /* @__PURE__ */ v("div", {
|
|
211
211
|
className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",
|
|
212
212
|
children: [/* @__PURE__ */ v("div", {
|
|
@@ -218,21 +218,21 @@ function M() {
|
|
|
218
218
|
/* @__PURE__ */ _(j, { children: e.status }),
|
|
219
219
|
e.message.isHidden && /* @__PURE__ */ _(j, { children: "Hidden" }),
|
|
220
220
|
/* @__PURE__ */ v("span", {
|
|
221
|
-
className: "text-xs text-
|
|
221
|
+
className: "text-xs text-text-secondary",
|
|
222
222
|
children: ["Reported ", A(e.createdAt)]
|
|
223
223
|
})
|
|
224
224
|
]
|
|
225
225
|
}),
|
|
226
226
|
/* @__PURE__ */ _("p", {
|
|
227
|
-
className: "text-sm font-medium text-
|
|
227
|
+
className: "text-sm font-medium text-text-primary",
|
|
228
228
|
children: e.reason
|
|
229
229
|
}),
|
|
230
230
|
/* @__PURE__ */ _("p", {
|
|
231
|
-
className: "rounded-md bg-
|
|
231
|
+
className: "rounded-md bg-bg-sunken p-3 text-sm text-text-secondary",
|
|
232
232
|
children: O(e.message.content)
|
|
233
233
|
}),
|
|
234
234
|
/* @__PURE__ */ v("p", {
|
|
235
|
-
className: "text-xs text-
|
|
235
|
+
className: "text-xs text-text-secondary",
|
|
236
236
|
children: [
|
|
237
237
|
"Message ",
|
|
238
238
|
e.messageId,
|
|
@@ -251,7 +251,7 @@ function M() {
|
|
|
251
251
|
children: [/* @__PURE__ */ _("button", {
|
|
252
252
|
type: "button",
|
|
253
253
|
disabled: p,
|
|
254
|
-
className: "rounded-md border border-border bg-
|
|
254
|
+
className: "rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary hover:bg-accent disabled:opacity-50",
|
|
255
255
|
onClick: () => void d({ variables: { input: {
|
|
256
256
|
reportId: e.id,
|
|
257
257
|
action: "DISMISS"
|
|
@@ -260,7 +260,7 @@ function M() {
|
|
|
260
260
|
}), /* @__PURE__ */ _("button", {
|
|
261
261
|
type: "button",
|
|
262
262
|
disabled: p,
|
|
263
|
-
className: "rounded-md bg-primary px-3 py-2 text-sm text-primary-
|
|
263
|
+
className: "rounded-md bg-action-primary-bg px-3 py-2 text-sm text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
|
|
264
264
|
onClick: () => void d({ variables: { input: {
|
|
265
265
|
reportId: e.id,
|
|
266
266
|
action: "HIDE_MESSAGE"
|
|
@@ -299,7 +299,7 @@ function N() {
|
|
|
299
299
|
children: [/* @__PURE__ */ v("select", {
|
|
300
300
|
value: u,
|
|
301
301
|
onChange: (e) => d(e.target.value),
|
|
302
|
-
className: "rounded-md border border-border bg-
|
|
302
|
+
className: "rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary",
|
|
303
303
|
children: [
|
|
304
304
|
/* @__PURE__ */ _("option", {
|
|
305
305
|
value: "ARCHIVE",
|
|
@@ -317,7 +317,7 @@ function N() {
|
|
|
317
317
|
}), /* @__PURE__ */ v("button", {
|
|
318
318
|
type: "button",
|
|
319
319
|
disabled: o.size === 0 || w,
|
|
320
|
-
className: "rounded-md bg-primary px-3 py-2 text-sm text-primary-
|
|
320
|
+
className: "rounded-md bg-action-primary-bg px-3 py-2 text-sm text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
|
|
321
321
|
onClick: () => void b({ variables: { input: {
|
|
322
322
|
conversationIds: Array.from(o),
|
|
323
323
|
action: u
|
|
@@ -335,15 +335,15 @@ function N() {
|
|
|
335
335
|
})
|
|
336
336
|
}),
|
|
337
337
|
a && p && /* @__PURE__ */ _("p", {
|
|
338
|
-
className: "text-sm text-
|
|
338
|
+
className: "text-sm text-text-secondary",
|
|
339
339
|
children: "Loading conversations…"
|
|
340
340
|
}),
|
|
341
341
|
a && m && /* @__PURE__ */ _("p", {
|
|
342
|
-
className: "text-sm text-
|
|
342
|
+
className: "text-sm text-status-error-text",
|
|
343
343
|
children: m.message
|
|
344
344
|
}),
|
|
345
345
|
C?.bulkUpdateConversations && /* @__PURE__ */ v("div", {
|
|
346
|
-
className: "rounded-lg border border-border bg-
|
|
346
|
+
className: "rounded-lg border border-border-subtle bg-bg-sunken p-3 text-sm text-text-secondary",
|
|
347
347
|
children: [
|
|
348
348
|
C.bulkUpdateConversations.successCount,
|
|
349
349
|
" succeeded,",
|
|
@@ -353,9 +353,9 @@ function N() {
|
|
|
353
353
|
]
|
|
354
354
|
}),
|
|
355
355
|
a && /* @__PURE__ */ _("div", {
|
|
356
|
-
className: "overflow-hidden rounded-lg border border-border bg-
|
|
356
|
+
className: "overflow-hidden rounded-lg border border-border-subtle bg-bg-surface",
|
|
357
357
|
children: T.map((e) => /* @__PURE__ */ v("label", {
|
|
358
|
-
className: "flex cursor-pointer items-center gap-3 border-b border-border px-4 py-3 last:border-b-0 hover:bg-accent",
|
|
358
|
+
className: "flex cursor-pointer items-center gap-3 border-b border-border-subtle px-4 py-3 last:border-b-0 hover:bg-accent",
|
|
359
359
|
children: [/* @__PURE__ */ _("input", {
|
|
360
360
|
type: "checkbox",
|
|
361
361
|
"aria-label": `Select ${e.title || "untitled conversation"}`,
|
|
@@ -364,10 +364,10 @@ function N() {
|
|
|
364
364
|
}), /* @__PURE__ */ v("div", {
|
|
365
365
|
className: "min-w-0 flex-1",
|
|
366
366
|
children: [/* @__PURE__ */ _("p", {
|
|
367
|
-
className: "truncate text-sm font-medium text-
|
|
367
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
368
368
|
children: e.title || "Untitled conversation"
|
|
369
369
|
}), /* @__PURE__ */ v("p", {
|
|
370
|
-
className: "text-xs text-
|
|
370
|
+
className: "text-xs text-text-secondary",
|
|
371
371
|
children: [
|
|
372
372
|
e.type,
|
|
373
373
|
" · ",
|
|
@@ -402,11 +402,11 @@ function P() {
|
|
|
402
402
|
maxWidth: "2xl",
|
|
403
403
|
children: [
|
|
404
404
|
s && /* @__PURE__ */ _("p", {
|
|
405
|
-
className: "text-sm text-
|
|
405
|
+
className: "text-sm text-text-secondary",
|
|
406
406
|
children: "Loading activity…"
|
|
407
407
|
}),
|
|
408
408
|
l && /* @__PURE__ */ _("p", {
|
|
409
|
-
className: "text-sm text-
|
|
409
|
+
className: "text-sm text-status-error-text",
|
|
410
410
|
children: l.message
|
|
411
411
|
}),
|
|
412
412
|
!s && !l && u.length === 0 && /* @__PURE__ */ _(r, {
|
|
@@ -416,24 +416,24 @@ function P() {
|
|
|
416
416
|
/* @__PURE__ */ _("div", {
|
|
417
417
|
className: "grid gap-3",
|
|
418
418
|
children: u.map((e) => /* @__PURE__ */ v("article", {
|
|
419
|
-
className: "rounded-lg border border-border bg-
|
|
419
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
420
420
|
children: [
|
|
421
421
|
/* @__PURE__ */ v("div", {
|
|
422
422
|
className: "flex flex-wrap items-center justify-between gap-2",
|
|
423
423
|
children: [/* @__PURE__ */ _("p", {
|
|
424
|
-
className: "text-sm font-medium text-
|
|
424
|
+
className: "text-sm font-medium text-text-primary",
|
|
425
425
|
children: e.action
|
|
426
426
|
}), /* @__PURE__ */ _("span", {
|
|
427
|
-
className: "text-xs text-
|
|
427
|
+
className: "text-xs text-text-secondary",
|
|
428
428
|
children: A(e.timestamp)
|
|
429
429
|
})]
|
|
430
430
|
}),
|
|
431
431
|
/* @__PURE__ */ _("p", {
|
|
432
|
-
className: "mt-1 text-sm text-
|
|
432
|
+
className: "mt-1 text-sm text-text-secondary",
|
|
433
433
|
children: e.description || "No description provided"
|
|
434
434
|
}),
|
|
435
435
|
/* @__PURE__ */ v("p", {
|
|
436
|
-
className: "mt-2 text-xs text-
|
|
436
|
+
className: "mt-2 text-xs text-text-secondary",
|
|
437
437
|
children: [
|
|
438
438
|
"User ",
|
|
439
439
|
e.userId,
|
|
@@ -481,26 +481,26 @@ function F() {
|
|
|
481
481
|
title: "Install app",
|
|
482
482
|
description: "Install a workspace-scoped conversation app by app ID.",
|
|
483
483
|
children: [/* @__PURE__ */ v("div", {
|
|
484
|
-
className: "grid gap-3 rounded-lg border border-border bg-
|
|
484
|
+
className: "grid gap-3 rounded-lg border border-border-subtle bg-bg-surface p-4 sm:grid-cols-[minmax(0,1fr)_minmax(0,2fr)_auto]",
|
|
485
485
|
children: [
|
|
486
486
|
/* @__PURE__ */ _("input", {
|
|
487
487
|
value: l,
|
|
488
488
|
onChange: (e) => u(e.target.value),
|
|
489
489
|
placeholder: "app-id",
|
|
490
490
|
"aria-label": "App ID",
|
|
491
|
-
className: "rounded-md border border-border bg-
|
|
491
|
+
className: "rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
492
492
|
}),
|
|
493
493
|
/* @__PURE__ */ _("textarea", {
|
|
494
494
|
value: f,
|
|
495
495
|
onChange: (e) => m(e.target.value),
|
|
496
496
|
rows: 3,
|
|
497
497
|
"aria-label": "App settings JSON",
|
|
498
|
-
className: "rounded-md border border-border bg-
|
|
498
|
+
className: "rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary"
|
|
499
499
|
}),
|
|
500
500
|
/* @__PURE__ */ _("button", {
|
|
501
501
|
type: "button",
|
|
502
502
|
disabled: P,
|
|
503
|
-
className: "rounded-md bg-primary px-3 py-2 text-sm text-primary-
|
|
503
|
+
className: "rounded-md bg-action-primary-bg px-3 py-2 text-sm text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50",
|
|
504
504
|
onClick: () => {
|
|
505
505
|
let e = B(f);
|
|
506
506
|
if (!l.trim() || !e) {
|
|
@@ -517,32 +517,32 @@ function F() {
|
|
|
517
517
|
})
|
|
518
518
|
]
|
|
519
519
|
}), x && /* @__PURE__ */ _("p", {
|
|
520
|
-
className: "text-sm text-
|
|
520
|
+
className: "text-sm text-status-error-text",
|
|
521
521
|
children: x
|
|
522
522
|
})]
|
|
523
523
|
}),
|
|
524
524
|
O && /* @__PURE__ */ _("p", {
|
|
525
|
-
className: "text-sm text-
|
|
525
|
+
className: "text-sm text-text-secondary",
|
|
526
526
|
children: "Loading app installations…"
|
|
527
527
|
}),
|
|
528
528
|
k && /* @__PURE__ */ _("p", {
|
|
529
|
-
className: "text-sm text-
|
|
529
|
+
className: "text-sm text-status-error-text",
|
|
530
530
|
children: k.message
|
|
531
531
|
}),
|
|
532
532
|
/* @__PURE__ */ _("div", {
|
|
533
533
|
className: "grid gap-3",
|
|
534
534
|
children: z.map((e) => /* @__PURE__ */ v("article", {
|
|
535
|
-
className: "rounded-lg border border-border bg-
|
|
535
|
+
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
536
536
|
children: [/* @__PURE__ */ v("div", {
|
|
537
537
|
className: "flex flex-wrap items-start justify-between gap-3",
|
|
538
538
|
children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ v("div", {
|
|
539
539
|
className: "flex items-center gap-2",
|
|
540
540
|
children: [/* @__PURE__ */ _("h3", {
|
|
541
|
-
className: "text-sm font-semibold text-
|
|
541
|
+
className: "text-sm font-semibold text-text-primary",
|
|
542
542
|
children: e.appId
|
|
543
543
|
}), /* @__PURE__ */ _(j, { children: e.state })]
|
|
544
544
|
}), /* @__PURE__ */ v("p", {
|
|
545
|
-
className: "text-xs text-
|
|
545
|
+
className: "text-xs text-text-secondary",
|
|
546
546
|
children: [
|
|
547
547
|
"Version ",
|
|
548
548
|
e.appVersion,
|
|
@@ -556,7 +556,7 @@ function F() {
|
|
|
556
556
|
})] }), /* @__PURE__ */ v("button", {
|
|
557
557
|
type: "button",
|
|
558
558
|
disabled: R,
|
|
559
|
-
className: "inline-flex items-center gap-2 rounded-md border border-border bg-
|
|
559
|
+
className: "inline-flex items-center gap-2 rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary hover:bg-accent disabled:opacity-50",
|
|
560
560
|
onClick: () => void L({ variables: { id: e.id } }),
|
|
561
561
|
children: [/* @__PURE__ */ _(p, {
|
|
562
562
|
className: "size-4",
|
|
@@ -573,7 +573,7 @@ function F() {
|
|
|
573
573
|
...n,
|
|
574
574
|
[e.id]: t.target.value
|
|
575
575
|
})),
|
|
576
|
-
className: "rounded-md border border-border bg-
|
|
576
|
+
className: "rounded-md border border-border-subtle bg-bg-surface px-3 py-2 font-mono text-xs text-text-primary"
|
|
577
577
|
}), /* @__PURE__ */ _("button", {
|
|
578
578
|
type: "button",
|
|
579
579
|
disabled: I,
|