@burdenoff/microfe-proserviceworld 2026.624.1 → 2026.626.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.
Files changed (31) hide show
  1. package/dist/proserviceworld/pages/AdminReportsPage.js +396 -329
  2. package/dist/proserviceworld/pages/AdminReportsPage.js.map +1 -1
  3. package/dist/proserviceworld/pages/AdminUsersPage.js +261 -237
  4. package/dist/proserviceworld/pages/AdminUsersPage.js.map +1 -1
  5. package/dist/proserviceworld/pages/AnalyticsPage.js +373 -315
  6. package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
  7. package/dist/proserviceworld/pages/BookingsPage.js +221 -200
  8. package/dist/proserviceworld/pages/BookingsPage.js.map +1 -1
  9. package/dist/proserviceworld/pages/DashboardPage.js +474 -405
  10. package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
  11. package/dist/proserviceworld/pages/DocumentsPage.js +386 -317
  12. package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
  13. package/dist/proserviceworld/pages/MessagesPage.js +201 -192
  14. package/dist/proserviceworld/pages/MessagesPage.js.map +1 -1
  15. package/dist/proserviceworld/pages/PaymentsPage.js +398 -340
  16. package/dist/proserviceworld/pages/PaymentsPage.js.map +1 -1
  17. package/dist/proserviceworld/pages/ProjectsPage.js +543 -463
  18. package/dist/proserviceworld/pages/ProjectsPage.js.map +1 -1
  19. package/dist/proserviceworld/pages/ProviderDetailPage.js +420 -346
  20. package/dist/proserviceworld/pages/ProviderDetailPage.js.map +1 -1
  21. package/dist/proserviceworld/pages/ProvidersPage.js +249 -244
  22. package/dist/proserviceworld/pages/ProvidersPage.js.map +1 -1
  23. package/dist/proserviceworld/pages/ReviewsPage.js +93 -76
  24. package/dist/proserviceworld/pages/ReviewsPage.js.map +1 -1
  25. package/dist/proserviceworld/pages/ServiceDetailsPage.js +377 -309
  26. package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +1 -1
  27. package/dist/proserviceworld/pages/ServicesPage.js +116 -109
  28. package/dist/proserviceworld/pages/ServicesPage.js.map +1 -1
  29. package/dist/proserviceworld/pages/VerificationPage.js +264 -251
  30. package/dist/proserviceworld/pages/VerificationPage.js.map +1 -1
  31. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"MessagesPage.js","names":[],"sources":["../../../src/proserviceworld/pages/MessagesPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { Badge, Button, Card, CardContent, Input } from '@burdenoff/fe-libs/ui';\nimport {\n Search,\n Send,\n Circle,\n CheckCheck,\n Check,\n MessageSquare,\n Phone,\n Video,\n MoreHorizontal,\n Paperclip,\n Smile,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\nimport { tWithFallback } from '../utils/i18n';\n\nexport function MessagesPage() {\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n const { conversations } = useProServiceWorldStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedConversationId, setSelectedConversationId] = useState<string>(\n conversations[0]?.id ?? ''\n );\n const [messageInput, setMessageInput] = useState('');\n\n const filteredConversations = useMemo(() => {\n if (!searchQuery.trim()) return conversations;\n const q = searchQuery.toLowerCase();\n return conversations.filter(\n (c) => c.participantName.toLowerCase().includes(q) || c.lastMessage.toLowerCase().includes(q)\n );\n }, [conversations, searchQuery]);\n\n const selectedConversation = conversations.find((c) => c.id === selectedConversationId);\n const totalUnread = conversations.reduce((sum, c) => sum + c.unreadCount, 0);\n const unreadSummary =\n totalUnread > 0\n ? tr('proserviceworld.messages.subtitle.unread', '{{count}} unread messages', {\n count: totalUnread,\n })\n : tr('proserviceworld.messages.subtitle.caughtUp', 'All caught up');\n\n const formatTime = (timestamp: string) => {\n const date = new Date(timestamp);\n const now = new Date();\n const diffDays = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24));\n if (diffDays === 0) {\n return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n }\n if (diffDays === 1) return tr('proserviceworld.messages.time.yesterday', 'Yesterday');\n if (diffDays < 7) return date.toLocaleDateString([], { weekday: 'short' });\n return date.toLocaleDateString([], { month: 'short', day: 'numeric' });\n };\n\n return (\n <div className=\"p-6\">\n <div className=\"mb-6 flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n {tr('proserviceworld.messages.title', 'Messages')}\n </h1>\n <p className=\"text-sm text-text-secondary\">{unreadSummary}</p>\n </div>\n <Button size=\"sm\">\n <MessageSquare className=\"mr-2 h-4 w-4\" />\n {tr('proserviceworld.messages.actions.newMessage', 'New Message')}\n </Button>\n </div>\n\n <div\n className=\"grid gap-0 overflow-hidden rounded-lg border lg:grid-cols-3\"\n style={{ height: 'calc(100vh - 220px)', minHeight: '500px' }}\n >\n {/* Conversation sidebar */}\n <div className=\"flex flex-col border-r lg:col-span-1\">\n {/* Search */}\n <div className=\"border-b p-3\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder={tr(\n 'proserviceworld.messages.search.placeholder',\n 'Search conversations...'\n )}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-10\"\n />\n </div>\n </div>\n\n {/* Conversation list */}\n <div className=\"flex-1 overflow-y-auto\">\n {filteredConversations.length === 0 ? (\n <div className=\"p-6 text-center text-sm text-text-secondary\">\n {tr('proserviceworld.messages.empty.list', 'No conversations found.')}\n </div>\n ) : (\n filteredConversations.map((conversation) => (\n <button\n key={conversation.id}\n onClick={() => setSelectedConversationId(conversation.id)}\n className={`w-full border-b p-3 text-left transition-colors ${\n selectedConversationId === conversation.id\n ? 'bg-action-primary-bg/5'\n : 'hover:bg-bg-sunken/50'\n }`}\n >\n <div className=\"flex items-start gap-3\">\n {/* Avatar */}\n <div className=\"relative shrink-0\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-full bg-action-primary-bg/10 font-semibold text-primary\">\n {conversation.participantName.charAt(0)}\n </div>\n {conversation.online && (\n <Circle className=\"absolute -bottom-0.5 -right-0.5 h-3.5 w-3.5 fill-green-500 text-green-500 stroke-background\" />\n )}\n </div>\n\n {/* Content */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between\">\n <h3\n className={`truncate text-sm ${conversation.unreadCount > 0 ? 'font-bold' : 'font-medium'}`}\n >\n {conversation.participantName}\n </h3>\n <span className=\"shrink-0 text-xs text-text-secondary\">\n {formatTime(conversation.lastMessageTime)}\n </span>\n </div>\n <p className=\"text-xs text-text-secondary\">\n {conversation.participantRole}\n </p>\n <div className=\"mt-1 flex items-center justify-between\">\n <p\n className={`truncate text-xs ${conversation.unreadCount > 0 ? 'font-medium text-text-primary' : 'text-text-secondary'}`}\n >\n {conversation.lastMessage}\n </p>\n {conversation.unreadCount > 0 && (\n <Badge className=\"ml-2 h-5 w-5 shrink-0 rounded-full p-0 text-xs flex items-center justify-center\">\n {conversation.unreadCount}\n </Badge>\n )}\n </div>\n </div>\n </div>\n </button>\n ))\n )}\n </div>\n </div>\n\n {/* Chat area */}\n <div className=\"flex flex-col lg:col-span-2\">\n {selectedConversation ? (\n <>\n {/* Chat header */}\n <div className=\"flex items-center justify-between border-b p-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"relative\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-full bg-action-primary-bg/10 font-semibold text-primary\">\n {selectedConversation.participantName.charAt(0)}\n </div>\n {selectedConversation.online && (\n <Circle className=\"absolute -bottom-0.5 -right-0.5 h-3.5 w-3.5 fill-green-500 text-green-500 stroke-background\" />\n )}\n </div>\n <div>\n <h3 className=\"font-medium\">{selectedConversation.participantName}</h3>\n <p className=\"text-xs text-text-secondary\">\n {selectedConversation.online\n ? tr('proserviceworld.messages.presence.online', 'Online')\n : tr('proserviceworld.messages.presence.offline', 'Offline')}{' '}\n - {selectedConversation.participantRole}\n </p>\n </div>\n </div>\n <div className=\"flex gap-1\">\n <Button variant=\"outline\" size=\"sm\">\n <Phone className=\"h-4 w-4\" />\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n <Video className=\"h-4 w-4\" />\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n\n {/* Messages area */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {selectedConversation.messages.map((message) => (\n <div\n key={message.id}\n className={`flex ${message.isOwn ? 'justify-end' : 'justify-start'}`}\n >\n <div\n className={`flex max-w-[75%] items-end gap-2 ${message.isOwn ? 'flex-row-reverse' : ''}`}\n >\n {!message.isOwn && (\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-action-primary-bg/10 text-xs font-semibold text-primary\">\n {message.senderName.charAt(0)}\n </div>\n )}\n <div>\n <div\n className={`rounded-2xl px-4 py-2 ${\n message.isOwn\n ? 'bg-action-primary-bg text-action-primary-text rounded-br-md'\n : 'bg-bg-sunken rounded-bl-md'\n }`}\n >\n <p className=\"text-sm\">{message.content}</p>\n </div>\n <div\n className={`mt-1 flex items-center gap-1 text-xs text-text-secondary ${message.isOwn ? 'justify-end' : ''}`}\n >\n <span>{formatTime(message.timestamp)}</span>\n {message.isOwn &&\n (message.read ? (\n <CheckCheck className=\"h-3 w-3 text-blue-500\" />\n ) : (\n <Check className=\"h-3 w-3\" />\n ))}\n </div>\n </div>\n </div>\n </div>\n ))}\n </div>\n\n {/* Message input */}\n <div className=\"border-t p-3\">\n <div className=\"flex items-center gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <Paperclip className=\"h-4 w-4\" />\n </Button>\n <Input\n placeholder={tr(\n 'proserviceworld.messages.input.placeholder',\n 'Type a message...'\n )}\n value={messageInput}\n onChange={(e) => setMessageInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && messageInput.trim()) {\n setMessageInput('');\n }\n }}\n className=\"flex-1\"\n />\n <Button variant=\"outline\" size=\"sm\">\n <Smile className=\"h-4 w-4\" />\n </Button>\n <Button size=\"sm\" disabled={!messageInput.trim()}>\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </>\n ) : (\n <div className=\"flex flex-1 items-center justify-center text-text-secondary\">\n <div className=\"text-center\">\n <MessageSquare className=\"mx-auto h-12 w-12 text-text-secondary/50\" />\n <p className=\"mt-2 font-medium\">\n {tr('proserviceworld.messages.emptyConversation.title', 'Select a conversation')}\n </p>\n <p className=\"text-sm\">\n {tr(\n 'proserviceworld.messages.emptyConversation.description',\n 'Choose a conversation to start messaging.'\n )}\n </p>\n </div>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MessagesPage.js","names":[],"sources":["../../../src/proserviceworld/pages/MessagesPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n Badge,\n Button,\n Input,\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Search,\n Send,\n Circle,\n CheckCheck,\n Check,\n MessageSquare,\n Phone,\n Video,\n MoreHorizontal,\n Paperclip,\n Smile,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\nimport { tWithFallback } from '../utils/i18n';\n\nexport function MessagesPage() {\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n const { conversations } = useProServiceWorldStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedConversationId, setSelectedConversationId] = useState<string>(\n conversations[0]?.id ?? ''\n );\n const [messageInput, setMessageInput] = useState('');\n\n const filteredConversations = useMemo(() => {\n if (!searchQuery.trim()) return conversations;\n const q = searchQuery.toLowerCase();\n return conversations.filter(\n (c) => c.participantName.toLowerCase().includes(q) || c.lastMessage.toLowerCase().includes(q)\n );\n }, [conversations, searchQuery]);\n\n const selectedConversation = conversations.find((c) => c.id === selectedConversationId);\n const totalUnread = conversations.reduce((sum, c) => sum + c.unreadCount, 0);\n const unreadSummary =\n totalUnread > 0\n ? tr('proserviceworld.messages.subtitle.unread', '{{count}} unread messages', {\n count: totalUnread,\n })\n : tr('proserviceworld.messages.subtitle.caughtUp', 'All caught up');\n\n const formatTime = (timestamp: string) => {\n const date = new Date(timestamp);\n const now = new Date();\n const diffDays = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24));\n if (diffDays === 0) {\n return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n }\n if (diffDays === 1) return tr('proserviceworld.messages.time.yesterday', 'Yesterday');\n if (diffDays < 7) return date.toLocaleDateString([], { weekday: 'short' });\n return date.toLocaleDateString([], { month: 'short', day: 'numeric' });\n };\n\n return (\n <div className=\"p-6 bg-bg-canvas min-h-screen\">\n <div className=\"relative overflow-hidden rounded-card -mx-2 px-2 pt-1 mb-6 flex items-center justify-between gap-4\">\n <AuroraBackground intensity=\"subtle\" />\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n <GradientText>{tr('proserviceworld.messages.title', 'Messages')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{unreadSummary}</p>\n </div>\n <Button size=\"sm\">\n <MessageSquare className=\"mr-2 h-4 w-4\" />\n {tr('proserviceworld.messages.actions.newMessage', 'New Message')}\n </Button>\n </div>\n\n <div className=\"mb-6\">\n <PagePurpose icon={<MessageSquare className=\"h-4 w-4\" />}>\n {tr(\n 'proserviceworld.messages.purpose',\n 'Keep every client and provider conversation in one place. Discuss scope, share updates, and coordinate bookings in real time — pick a conversation on the left to read the full thread and reply.'\n )}\n </PagePurpose>\n </div>\n\n <div\n className=\"grid gap-0 overflow-hidden rounded-card border border-border-seam bg-bg-surface shadow-pop lg:grid-cols-3\"\n style={{ height: 'calc(100vh - 220px)', minHeight: '500px' }}\n >\n {/* Conversation sidebar */}\n <div className=\"flex flex-col border-r border-border-seam lg:col-span-1\">\n {/* Search */}\n <div className=\"border-b border-border-seam p-3\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder={tr(\n 'proserviceworld.messages.search.placeholder',\n 'Search conversations...'\n )}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-10\"\n />\n </div>\n </div>\n\n {/* Conversation list */}\n <div className=\"flex-1 overflow-y-auto\">\n {filteredConversations.length === 0 ? (\n <div className=\"p-4\">\n <IllustratedEmptyState\n illustration={searchQuery ? 'empty-search' : 'empty-people'}\n title={tr('proserviceworld.messages.empty.list', 'No conversations found')}\n description={\n searchQuery\n ? tr(\n 'proserviceworld.messages.empty.listSearch',\n 'No conversations match your search.'\n )\n : tr(\n 'proserviceworld.messages.empty.listDescription',\n 'Conversations with clients and providers will appear here.'\n )\n }\n />\n </div>\n ) : (\n filteredConversations.map((conversation) => (\n <button\n key={conversation.id}\n onClick={() => setSelectedConversationId(conversation.id)}\n className={`w-full border-b border-border-seam p-3 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-border-focus ${\n selectedConversationId === conversation.id\n ? 'bg-accent-soft'\n : 'hover:bg-bg-sunken/50'\n }`}\n >\n <div className=\"flex items-start gap-3\">\n {/* Avatar */}\n <div className=\"relative shrink-0\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-full bg-accent-soft font-semibold text-text-primary\">\n {conversation.participantName.charAt(0)}\n </div>\n {conversation.online && (\n <Circle className=\"absolute -bottom-0.5 -right-0.5 h-3.5 w-3.5 fill-status-success-text text-status-success-text stroke-bg-surface motion-safe:animate-vc-glow-pulse\" />\n )}\n </div>\n\n {/* Content */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between\">\n <h3\n className={`truncate text-sm ${conversation.unreadCount > 0 ? 'font-bold' : 'font-medium'}`}\n >\n {conversation.participantName}\n </h3>\n <span className=\"shrink-0 text-xs text-text-secondary\">\n {formatTime(conversation.lastMessageTime)}\n </span>\n </div>\n <p className=\"text-xs text-text-secondary\">\n {conversation.participantRole}\n </p>\n <div className=\"mt-1 flex items-center justify-between\">\n <p\n className={`truncate text-xs ${conversation.unreadCount > 0 ? 'font-medium text-text-primary' : 'text-text-secondary'}`}\n >\n {conversation.lastMessage}\n </p>\n {conversation.unreadCount > 0 && (\n <Badge className=\"ml-2 h-5 w-5 shrink-0 rounded-full p-0 text-xs flex items-center justify-center\">\n {conversation.unreadCount}\n </Badge>\n )}\n </div>\n </div>\n </div>\n </button>\n ))\n )}\n </div>\n </div>\n\n {/* Chat area */}\n <div className=\"flex flex-col lg:col-span-2\">\n {selectedConversation ? (\n <>\n {/* Chat header */}\n <div className=\"flex items-center justify-between border-b border-border-seam p-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"relative\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-full bg-accent-soft font-semibold text-text-primary\">\n {selectedConversation.participantName.charAt(0)}\n </div>\n {selectedConversation.online && (\n <Circle className=\"absolute -bottom-0.5 -right-0.5 h-3.5 w-3.5 fill-status-success-text text-status-success-text stroke-bg-surface motion-safe:animate-vc-glow-pulse\" />\n )}\n </div>\n <div>\n <h3 className=\"font-medium\">{selectedConversation.participantName}</h3>\n <p className=\"text-xs text-text-secondary\">\n {selectedConversation.online\n ? tr('proserviceworld.messages.presence.online', 'Online')\n : tr('proserviceworld.messages.presence.offline', 'Offline')}{' '}\n - {selectedConversation.participantRole}\n </p>\n </div>\n </div>\n <div className=\"flex gap-1\">\n <Button variant=\"outline\" size=\"sm\">\n <Phone className=\"h-4 w-4\" />\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n <Video className=\"h-4 w-4\" />\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n\n {/* Messages area */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {selectedConversation.messages.map((message) => (\n <div\n key={message.id}\n className={`flex ${message.isOwn ? 'justify-end' : 'justify-start'}`}\n >\n <div\n className={`flex max-w-[75%] items-end gap-2 ${message.isOwn ? 'flex-row-reverse' : ''}`}\n >\n {!message.isOwn && (\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-accent-soft text-xs font-semibold text-text-primary\">\n {message.senderName.charAt(0)}\n </div>\n )}\n <div>\n <div\n className={`rounded-2xl px-4 py-2 ${\n message.isOwn\n ? 'bg-action-primary-bg text-action-primary-text rounded-br-md'\n : 'bg-bg-sunken rounded-bl-md'\n }`}\n >\n <p className=\"text-sm\">{message.content}</p>\n </div>\n <div\n className={`mt-1 flex items-center gap-1 text-xs text-text-secondary ${message.isOwn ? 'justify-end' : ''}`}\n >\n <span>{formatTime(message.timestamp)}</span>\n {message.isOwn &&\n (message.read ? (\n <CheckCheck className=\"h-3 w-3 text-accent-blue\" />\n ) : (\n <Check className=\"h-3 w-3\" />\n ))}\n </div>\n </div>\n </div>\n </div>\n ))}\n </div>\n\n {/* Message input */}\n <div className=\"border-t border-border-seam p-3\">\n <div className=\"flex items-center gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <Paperclip className=\"h-4 w-4\" />\n </Button>\n <Input\n placeholder={tr(\n 'proserviceworld.messages.input.placeholder',\n 'Type a message...'\n )}\n value={messageInput}\n onChange={(e) => setMessageInput(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && messageInput.trim()) {\n setMessageInput('');\n }\n }}\n className=\"flex-1\"\n />\n <Button variant=\"outline\" size=\"sm\">\n <Smile className=\"h-4 w-4\" />\n </Button>\n <Button size=\"sm\" disabled={!messageInput.trim()}>\n <Send className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </>\n ) : (\n <div className=\"flex flex-1 items-center justify-center\">\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={tr('proserviceworld.messages.emptyConversation.title', 'Select a conversation')}\n description={tr(\n 'proserviceworld.messages.emptyConversation.description',\n 'Choose a conversation to start messaging.'\n )}\n />\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}