Melissa: deep-link via URL + Pacientes (WIP) + cronometro reset
Roteamento por URL (substitui o ref local secaoAberta):
- routes.misc.js: rota vira /preview/melissa/:secao? — param opcional
- MelissaLayout.vue: secaoAberta agora e computed do route.params.secao,
validado contra SECOES (chave invalida -> null). abrirSecao/fecharSecao
fazem router.push em vez de mutar ref. Habilita back/forward, refresh
e deep-link tipo /preview/melissa/agenda.
Pagina Pacientes (WIP, ainda nao wireada no slot do Layout):
- src/layout/melissa/MelissaPacientes.vue (novo, ~? linhas) — fullscreen
3-col espelhando MelissaAgenda: aside esquerda com filtros (status /
grupos / tags), lista central com cards + busca, quick view direita
com KPIs do paciente selecionado + acoes.
- Carrega pacientes (todos os status), grupos/tags do tenant, vinculos
patient_groups + patient_tags + session counts em paralelo.
- Integra PatientProntuario (overlay), PatientCadastroDialog,
PatientCreatePopover + ComponentCadastroRapido, e
conversationDrawerStore (acao WhatsApp da quick view).
useMelissaPacientes ganha opcao { onlyActive }:
- default true (compat com cards do resumo / cronometro / eventos hoje
— so faz sentido com ativos)
- false retorna Ativo + Inativo + Arquivado, pra uso na pagina nova
- select agora inclui data_nascimento (necessario pros KPIs da quick view)
Cronometro: zera ao parar — terminou a sessao, fica pronto pra proxima
sem precisar reabrir o popover.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -112,6 +112,8 @@ function toggle() {
|
|||||||
if (timer) clearInterval(timer);
|
if (timer) clearInterval(timer);
|
||||||
timer = null;
|
timer = null;
|
||||||
running.value = false;
|
running.value = false;
|
||||||
|
// Zera ao parar — sessão acabou, deixa pronto pra próxima
|
||||||
|
seconds.value = props.duracaoMinutos * 60;
|
||||||
} else {
|
} else {
|
||||||
if (timer) clearInterval(timer);
|
if (timer) clearInterval(timer);
|
||||||
timer = setInterval(() => {
|
timer = setInterval(() => {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
* Rota atual (sandbox): /preview/melissa
|
* Rota atual (sandbox): /preview/melissa
|
||||||
*/
|
*/
|
||||||
import { ref, computed, watch, onMounted, onBeforeUnmount, provide } from 'vue';
|
import { ref, computed, watch, onMounted, onBeforeUnmount, provide } from 'vue';
|
||||||
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import { useToast } from 'primevue/usetoast';
|
import { useToast } from 'primevue/usetoast';
|
||||||
import { useLayout } from '@/layout/composables/layout';
|
import { useLayout } from '@/layout/composables/layout';
|
||||||
import { applyThemeEngine } from '@/theme/theme.options';
|
import { applyThemeEngine } from '@/theme/theme.options';
|
||||||
@@ -91,16 +92,26 @@ const SECOES = {
|
|||||||
medicos: { label: 'Médicos e referências', icon: 'pi pi-user-edit', descricao: 'Profissionais que encaminham ou recebem pacientes.' }
|
medicos: { label: 'Médicos e referências', icon: 'pi pi-user-edit', descricao: 'Profissionais que encaminham ou recebem pacientes.' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const secaoAberta = ref(null); // chave em SECOES, ou null
|
// Seção ativa = param `:secao?` da rota. URL é a fonte da verdade pra
|
||||||
|
// permitir back/forward e deep-link (ex: /preview/melissa/agenda abre o
|
||||||
|
// overlay da agenda direto). Se a chave for inválida, vira null.
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
const secaoAberta = computed(() => {
|
||||||
|
const s = route.params?.secao;
|
||||||
|
return s && SECOES[s] ? s : null;
|
||||||
|
});
|
||||||
|
|
||||||
function abrirSecao(key) {
|
function abrirSecao(key) {
|
||||||
// Fecha overlays paralelos pra evitar empilhamento
|
// Fecha overlays paralelos pra evitar empilhamento
|
||||||
workspaceOpen.value = false;
|
workspaceOpen.value = false;
|
||||||
eventoSelecionado.value = null;
|
eventoSelecionado.value = null;
|
||||||
secaoAberta.value = key;
|
if (key === secaoAberta.value) return; // no-op, evita push duplicado
|
||||||
|
router.push({ name: 'PreviewMelissa', params: { secao: key } });
|
||||||
}
|
}
|
||||||
function fecharSecao() {
|
function fecharSecao() {
|
||||||
secaoAberta.value = null;
|
if (!secaoAberta.value) return;
|
||||||
|
router.push({ name: 'PreviewMelissa', params: {} });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefs de layout/UI (toque, fundo, opacidade, formato hora)
|
// Prefs de layout/UI (toque, fundo, opacidade, formato hora)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,16 @@ function normalizeStatus(s) {
|
|||||||
return v.charAt(0).toUpperCase() + v.slice(1);
|
return v.charAt(0).toUpperCase() + v.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useMelissaPacientes() {
|
/**
|
||||||
|
* @param {object} [opts]
|
||||||
|
* @param {boolean} [opts.onlyActive=true]
|
||||||
|
* true (default, legado) = retorna só status='Ativo' (uso original: cards do
|
||||||
|
* resumo, cronômetro, eventos hoje — só faz sentido com ativos).
|
||||||
|
* false = retorna todos (Ativo + Inativo + Arquivado), pra páginas que
|
||||||
|
* precisam mostrar/filtrar por status (ex.: MelissaPacientes).
|
||||||
|
*/
|
||||||
|
export function useMelissaPacientes(opts = {}) {
|
||||||
|
const onlyActive = opts.onlyActive !== false; // default true (compat)
|
||||||
const tenantStore = useTenantStore();
|
const tenantStore = useTenantStore();
|
||||||
|
|
||||||
const pacientes = ref([]);
|
const pacientes = ref([]);
|
||||||
@@ -66,7 +75,7 @@ export function useMelissaPacientes() {
|
|||||||
// ('ativo', 'Ativo', 'active', null...). Normaliza e filtra no client.
|
// ('ativo', 'Ativo', 'active', null...). Normaliza e filtra no client.
|
||||||
const { data, error: err } = await supabase
|
const { data, error: err } = await supabase
|
||||||
.from('patients')
|
.from('patients')
|
||||||
.select('id, nome_completo, email_principal, telefone, status, avatar_url, last_attended_at, created_at')
|
.select('id, nome_completo, email_principal, telefone, status, avatar_url, last_attended_at, created_at, data_nascimento')
|
||||||
.eq('owner_id', userId)
|
.eq('owner_id', userId)
|
||||||
.eq('tenant_id', tid)
|
.eq('tenant_id', tid)
|
||||||
.order('nome_completo', { ascending: true })
|
.order('nome_completo', { ascending: true })
|
||||||
@@ -82,10 +91,11 @@ export function useMelissaPacientes() {
|
|||||||
avatar_url: r.avatar_url || null,
|
avatar_url: r.avatar_url || null,
|
||||||
status: normalizeStatus(r.status),
|
status: normalizeStatus(r.status),
|
||||||
last_attended_at: r.last_attended_at || null,
|
last_attended_at: r.last_attended_at || null,
|
||||||
created_at: r.created_at || null
|
created_at: r.created_at || null,
|
||||||
|
data_nascimento: r.data_nascimento || null
|
||||||
}));
|
}));
|
||||||
|
|
||||||
pacientes.value = todos.filter((p) => p.status === 'Ativo');
|
pacientes.value = onlyActive ? todos.filter((p) => p.status === 'Ativo') : todos;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error.value = e?.message || 'Erro ao carregar pacientes';
|
error.value = e?.message || 'Erro ao carregar pacientes';
|
||||||
pacientes.value = [];
|
pacientes.value = [];
|
||||||
|
|||||||
@@ -25,8 +25,11 @@ export default {
|
|||||||
|
|
||||||
// Sandbox do layout Melissa (Direção B — lockscreen-style)
|
// Sandbox do layout Melissa (Direção B — lockscreen-style)
|
||||||
// Standalone, sem auth, sem AppLayout. Promovido de /preview/dashboard-win11.
|
// Standalone, sem auth, sem AppLayout. Promovido de /preview/dashboard-win11.
|
||||||
|
// Param `:secao?` opcional reflete a seção aberta na URL (agenda,
|
||||||
|
// pacientes, conversas, etc.) — permite deep-link, back/forward,
|
||||||
|
// refresh preservando estado.
|
||||||
{
|
{
|
||||||
path: 'preview/melissa',
|
path: 'preview/melissa/:secao?',
|
||||||
name: 'PreviewMelissa',
|
name: 'PreviewMelissa',
|
||||||
component: () => import('@/layout/melissa/MelissaLayout.vue')
|
component: () => import('@/layout/melissa/MelissaLayout.vue')
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user