Toast SLA: botão "Abrir conversa" abre drawer direto da thread
O alerta já vem com payload.thread_key vindo do edge conversation-sla- check. Agora o toast renderiza 2 botões lado a lado quando thread_key existe: - "Abrir conversa" (outlined) → abre ConversationDrawer global direto na thread, sem navegar de página. Usa o store global que já existe. - "Abrir CRM →" (solid) → fallback pra lista inteira via deeplink alias. openConversationDrawer busca o row da view conversation_threads pelo tenant+thread_key e delega pro conversationDrawerStore.openForThread. Se a thread sumiu (arquivada/paciente deletado), cai no fallback de navegar pra /conversas. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -56,7 +56,8 @@ export function useNotifications() {
|
|||||||
group: 'system-alerts',
|
group: 'system-alerts',
|
||||||
data: {
|
data: {
|
||||||
deeplink: payload.deeplink,
|
deeplink: payload.deeplink,
|
||||||
actionLabel: payload.actionLabel || defaultActionLabel(payload.deeplink)
|
actionLabel: payload.actionLabel || defaultActionLabel(payload.deeplink),
|
||||||
|
thread_key: payload.thread_key || null
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+51
-10
@@ -19,6 +19,9 @@ import { useLayout } from '@/layout/composables/layout';
|
|||||||
import { computed, onMounted, onBeforeUnmount, provide, watch } from 'vue';
|
import { computed, onMounted, onBeforeUnmount, provide, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { supabase } from '@/lib/supabase/client';
|
||||||
|
import { useConversationDrawerStore } from '@/stores/conversationDrawerStore';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// Aliases "semânticos" → resolvidos pra rota real com base no role atual.
|
// Aliases "semânticos" → resolvidos pra rota real com base no role atual.
|
||||||
@@ -47,6 +50,32 @@ function goToDeeplink(link) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Abre o drawer global de conversa a partir do thread_key (do payload do alerta).
|
||||||
|
// Busca o row da view conversation_threads e delega pro store.
|
||||||
|
async function openConversationDrawer(threadKey) {
|
||||||
|
if (!threadKey) return;
|
||||||
|
try {
|
||||||
|
const tenantId = tenantStore.activeTenantId;
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('conversation_threads')
|
||||||
|
.select('*')
|
||||||
|
.eq('tenant_id', tenantId)
|
||||||
|
.eq('thread_key', threadKey)
|
||||||
|
.maybeSingle();
|
||||||
|
if (error) throw error;
|
||||||
|
if (!data) {
|
||||||
|
// Thread sumiu (foi arquivada ou paciente deletado) — fallback pra lista
|
||||||
|
goToDeeplink('/conversas');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const drawerStore = useConversationDrawerStore();
|
||||||
|
await drawerStore.openForThread(data);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[AppLayout] openConversationDrawer falhou:', e?.message);
|
||||||
|
goToDeeplink('/conversas');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import AppFooter from './AppFooter.vue';
|
import AppFooter from './AppFooter.vue';
|
||||||
import AppSidebar from './AppSidebar.vue';
|
import AppSidebar from './AppSidebar.vue';
|
||||||
import AppTopbar from './AppTopbar.vue';
|
import AppTopbar from './AppTopbar.vue';
|
||||||
@@ -216,16 +245,28 @@ onBeforeUnmount(() => {
|
|||||||
<div class="text-xs mt-0.5 leading-relaxed opacity-85">{{ slotProps.message.detail }}</div>
|
<div class="text-xs mt-0.5 leading-relaxed opacity-85">{{ slotProps.message.detail }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<div class="flex gap-2 self-end flex-wrap">
|
||||||
v-if="slotProps.message.data?.deeplink"
|
<Button
|
||||||
:label="slotProps.message.data?.actionLabel || 'Abrir'"
|
v-if="slotProps.message.data?.thread_key"
|
||||||
icon="pi pi-arrow-right"
|
label="Abrir conversa"
|
||||||
iconPos="right"
|
icon="pi pi-comment"
|
||||||
size="small"
|
size="small"
|
||||||
severity="danger"
|
severity="danger"
|
||||||
class="rounded-full self-end"
|
outlined
|
||||||
@click="goToDeeplink(slotProps.message.data.deeplink)"
|
class="rounded-full"
|
||||||
/>
|
@click="openConversationDrawer(slotProps.message.data.thread_key)"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
v-if="slotProps.message.data?.deeplink"
|
||||||
|
:label="slotProps.message.data?.actionLabel || 'Abrir'"
|
||||||
|
icon="pi pi-arrow-right"
|
||||||
|
iconPos="right"
|
||||||
|
size="small"
|
||||||
|
severity="danger"
|
||||||
|
class="rounded-full"
|
||||||
|
@click="goToDeeplink(slotProps.message.data.deeplink)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Toast>
|
</Toast>
|
||||||
|
|||||||
Reference in New Issue
Block a user