diff --git a/src/components/notifications/NotificationItem.vue b/src/components/notifications/NotificationItem.vue index 8db1014..f8e6301 100644 --- a/src/components/notifications/NotificationItem.vue +++ b/src/components/notifications/NotificationItem.vue @@ -39,9 +39,24 @@ const typeMap = { new_patient: { icon: 'pi-user-plus', border: 'border-sky-500' }, recurrence_alert: { icon: 'pi-refresh', border: 'border-amber-500' }, session_status: { icon: 'pi-calendar-times', border: 'border-orange-500' }, - inbound_message: { icon: 'pi-whatsapp', border: 'border-emerald-500' } + inbound_message: { icon: 'pi-whatsapp', border: 'border-emerald-500' }, + system_alert: { icon: 'pi-exclamation-circle', border: 'border-red-600' } }; +// Aliases semânticos do deeplink → rota real por role. Mesmo map do AppLayout. +const DEEPLINK_ALIASES = { + '/crm/conversas': { therapist: '/therapist/conversas', clinic_admin: '/admin/conversas' }, + '/conversas': { therapist: '/therapist/conversas', clinic_admin: '/admin/conversas' } +}; + +function resolveDeeplink(link) { + if (!link || typeof link !== 'string') return link; + const alias = DEEPLINK_ALIASES[link]; + if (!alias) return link; + const role = tenantStore?.activeRole || 'therapist'; + return alias[role] || alias.therapist; +} + const meta = computed(() => typeMap[props.item.type] || { icon: 'pi-bell', border: 'border-gray-300' }); const isUnread = computed(() => !props.item.read_at); @@ -49,10 +64,30 @@ const timeAgo = computed(() => formatDistanceToNow(new Date(props.item.created_a const initials = computed(() => props.item.payload?.avatar_initials || '?'); -function handleRowClick() { +async function openConversationByThreadKey(threadKey) { + if (!threadKey) return false; + try { + const tenantId = tenantStore.activeTenantId; + const { supabase } = await import('@/lib/supabase/client'); + const { data } = await supabase + .from('conversation_threads') + .select('*') + .eq('tenant_id', tenantId) + .eq('thread_key', threadKey) + .maybeSingle(); + if (!data) return false; + await conversationDrawer.openForThread(data); + return true; + } catch { + return false; + } +} + +async function handleRowClick() { + const payload = props.item.payload || {}; + // Inbound message → abre drawer global (evita 403 em rota admin/therapist) if (props.item.type === 'inbound_message') { - const payload = props.item.payload || {}; if (payload.patient_id) { conversationDrawer.openForPatient(payload.patient_id); } else if (payload.from_number) { @@ -74,8 +109,39 @@ function handleRowClick() { return; } - // Outros tipos: segue o deeplink normal - const deeplink = props.item.payload?.deeplink; + // System alert com thread_key → abre drawer da conversa + if (payload.thread_key) { + const ok = await openConversationByThreadKey(payload.thread_key); + if (ok) { + store.drawerOpen = false; + emit('read', props.item.id); + return; + } + } + + // Fallback: segue deeplink resolvido por alias + const deeplink = resolveDeeplink(payload.deeplink); + if (deeplink) { + router.push(deeplink); + store.drawerOpen = false; + emit('read', props.item.id); + } +} + +async function handleOpenConversation(e) { + e.stopPropagation(); + const payload = props.item.payload || {}; + const ok = await openConversationByThreadKey(payload.thread_key); + if (ok) { + store.drawerOpen = false; + emit('read', props.item.id); + } +} + +function handleOpenDeeplink(e) { + e.stopPropagation(); + const payload = props.item.payload || {}; + const deeplink = resolveDeeplink(payload.deeplink); if (deeplink) { router.push(deeplink); store.drawerOpen = false; @@ -110,7 +176,27 @@ function handleArchive(e) {
{{ item.payload?.title }}
{{ item.payload?.detail }}
-{{ timeAgo }}
+