diff --git a/src/composables/useNotifications.js b/src/composables/useNotifications.js index da6233d..b043b16 100644 --- a/src/composables/useNotifications.js +++ b/src/composables/useNotifications.js @@ -15,9 +15,12 @@ |-------------------------------------------------------------------------- */ import { onMounted, onUnmounted } from 'vue'; +import { useRouter } from 'vue-router'; import { useToast } from 'primevue/usetoast'; import { supabase } from '@/lib/supabase/client'; -import { useNotificationStore } from '@/stores/notificationStore'; +import { useNotificationStore, fireBrowserNotification } from '@/stores/notificationStore'; +import { useConversationDrawerStore } from '@/stores/conversationDrawerStore'; +import { useTenantStore } from '@/stores/tenantStore'; // Alertas de sistema ficam fixos em vermelho até o usuário fechar manualmente. // 24h em ms — na prática "sticky" (PrimeVue Toast não aceita Infinity). @@ -26,9 +29,25 @@ const STICKY_TOAST_LIFE_MS = 24 * 60 * 60 * 1000; // Fallback polling — garante catch-up mesmo se Realtime perder eventos. const POLLING_INTERVAL_MS = 60_000; +// 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, role) { + if (!link || typeof link !== 'string') return link; + const alias = DEEPLINK_ALIASES[link]; + if (!alias) return link; + return alias[role] || alias.therapist; +} + export function useNotifications() { const store = useNotificationStore(); const toast = useToast(); + const router = useRouter(); + const conversationDrawer = useConversationDrawerStore(); + const tenantStore = useTenantStore(); const alertedIds = new Set(); // ids que já dispararam toast nesta sessão let ownerId = null; let pollTimer = null; @@ -62,8 +81,44 @@ export function useNotifications() { }); } + // Ação principal ao clicar numa notificação (seja do toast, sino ou OS): + // 1. Se tem thread_key, abre o drawer global na thread + // 2. Senão, resolve alias e navega via router + async function handleNotificationAction(item) { + if (!item) return; + const payload = item.payload || {}; + + if (payload.thread_key) { + try { + const tenantId = tenantStore.activeTenantId; + const { data } = await supabase + .from('conversation_threads') + .select('*') + .eq('tenant_id', tenantId) + .eq('thread_key', payload.thread_key) + .maybeSingle(); + if (data) { + await conversationDrawer.openForThread(data); + return; + } + } catch { + // cai no fallback abaixo + } + } + + const role = tenantStore?.activeRole || 'therapist'; + const link = resolveDeeplink(payload.deeplink, role); + if (link) { + if (typeof link === 'string' && link.startsWith('/')) router.push(link); + else window.location.href = link; + } + } + function onRealtimeNotification(item) { showToastFor(item); + // Dispara a notificação nativa do browser com handler de click que + // aplica a mesma lógica (drawer / alias) do toast e do sininho. + fireBrowserNotification(item, handleNotificationAction); } // Re-carrega notifs do DB e dispara toast AGREGADO pras system_alert diff --git a/src/stores/notificationStore.js b/src/stores/notificationStore.js index 8639e87..ec19e83 100644 --- a/src/stores/notificationStore.js +++ b/src/stores/notificationStore.js @@ -38,7 +38,7 @@ function browserNotifEnabled() { } } -function fireBrowserNotification(item) { +export function fireBrowserNotification(item, onClick) { if (!browserNotifEnabled()) return; if (document.hasFocus() && document.visibilityState === 'visible') return; // não notifica se tab ativa try { @@ -52,9 +52,11 @@ function fireBrowserNotification(item) { }); n.onclick = () => { window.focus(); - if (item?.payload?.deeplink) { - window.location.hash = ''; - window.location.pathname = item.payload.deeplink; + if (typeof onClick === 'function') { + try { onClick(item); } catch { /* ignore */ } + } else if (item?.payload?.deeplink) { + // fallback: navegação direta se ninguém registrou handler + window.location.href = item.payload.deeplink; } n.close(); }; @@ -129,7 +131,6 @@ export const useNotificationStore = defineStore('notifications', { }, (payload) => { this.items.unshift(payload.new); - fireBrowserNotification(payload.new); if (typeof onInsert === 'function') { try { onInsert(payload.new); } catch { /* ignore */ } }