MelissaPaciente: usa AgendaEventDialog GLOBAL via inject (em vez de dialog local)
User pediu pra trazer o AgendaEventDialog completo da Agenda pra dentro do prontuario. Estrategia: NAO duplicar o dialog (que ja vive no MelissaLayout). Em vez disso, reusar via provide/inject — pattern que ja existe (MELISSA_AGENDA_KEY). NOVO em src/layout/melissa/composables/useMelissaAgenda.js - onCreateEventoForPatient(patientId) — espelha onCreateEvento (defaults hoje proximo slot 15min, duracao default), mas injeta paciente_id no dialogEventRow. Adicionada ao return do composable. MELISSAPACIENTE.VUE - inject(MELISSA_AGENDA_KEY) pra acessar a instancia do useMelissaAgenda do MelissaLayout. - goAgendar(): chama melissaAgenda.onCreateEventoForPatient(patientId) (defensive: warn toast se nao tem inject ou funcao). - Watch em melissaAgenda.dialogOpen pra refetchar sessions+recorrencias quando o dialog fecha (true -> false), independente se foi save ou cancel. REMOVIDO (sem mais necessario — AgendaEventDialog faz tudo) - Refs novaSessaoOpen, novaSessaoForm - Catalogos FREQ_OPCOES, DIAS_SEMANA_OPCOES, QTD_SESSOES_OPCOES, SESSAO_TIPOS, SESSAO_DURACOES, SESSAO_MODALIDADES - Helpers toggleDiaSelecionado, qtdSessoesEfetiva, novaSessaoCtaLabel - Function salvarSessao (~110L de logica avulsa+recorrencia) - Import supabase (nao usado direto mais) - Import useRecurrence (era pro createRule no salvarSessao) - Import WEEKDAY_LABEL_BLOCK (era pro preview de freq) - Template <Dialog> Nova Sessao com header custom + form + freq chips + qtd sessoes + footer (~180L) Resultado: MelissaPaciente fica mais enxuto e usa exatamente o mesmo dialog completo que MelissaAgenda — todos os recursos do AgendaEventDialog (tipos de evento, paciente picker, comprometimento de servicos/billing, freq com preview de ocorrencias + conflitos, validacao por work rules, edicao de serie etc) ficam disponiveis no prontuario sem duplicacao. ESLint: 0 errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,11 +18,11 @@
|
||||
*
|
||||
* Prefixo CSS: .mpa-* (Melissa PAciente).
|
||||
*/
|
||||
import { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { ref, computed, watch, nextTick, inject, onMounted, onBeforeUnmount } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { useConversationDrawerStore } from '@/stores/conversationDrawerStore';
|
||||
import { supabase } from '@/lib/supabase/client';
|
||||
import { MELISSA_AGENDA_KEY } from './composables/useMelissaAgenda';
|
||||
import DocumentsListPage from '@/features/documents/DocumentsListPage.vue';
|
||||
import PatientConversationsTab from '@/features/patients/prontuario/PatientConversationsTab.vue';
|
||||
import PatientCadastroDialog from '@/components/ui/PatientCadastroDialog.vue';
|
||||
@@ -32,7 +32,6 @@ import { usePatientFinancial } from '@/features/patients/composables/usePatientF
|
||||
import { usePatientMessages } from '@/features/patients/composables/usePatientMessages';
|
||||
import { usePatientDocuments } from '@/features/patients/composables/usePatientDocuments';
|
||||
import { usePatientRecurrences } from '@/features/patients/composables/usePatientRecurrences';
|
||||
import { useRecurrence } from '@/features/agenda/composables/useRecurrence';
|
||||
import {
|
||||
pickField,
|
||||
calcAge,
|
||||
@@ -44,7 +43,6 @@ import {
|
||||
fmtDayShort,
|
||||
fmtRecurrenceLabel,
|
||||
fmtRecurrenceFim,
|
||||
WEEKDAY_LABEL as WEEKDAY_LABEL_BLOCK,
|
||||
fmtCPF,
|
||||
fmtRG,
|
||||
fmtGender,
|
||||
@@ -70,6 +68,11 @@ const router = useRouter();
|
||||
const toast = useToast();
|
||||
const conversationDrawerStore = useConversationDrawerStore();
|
||||
|
||||
// Inject do MelissaLayout — da acesso ao AgendaEventDialog global,
|
||||
// suas mutations (onCreateEventoForPatient) e o flag dialogOpen pra
|
||||
// detectar fechamento e refetchar sessions/recorrencias.
|
||||
const melissaAgenda = inject(MELISSA_AGENDA_KEY, null);
|
||||
|
||||
// ── Composables de dados ───────────────────────────────────
|
||||
const detail = usePatientDetail();
|
||||
const sessionsHook = usePatientSessions();
|
||||
@@ -77,7 +80,6 @@ const financialHook = usePatientFinancial();
|
||||
const messagesHook = usePatientMessages();
|
||||
const documentsHook = usePatientDocuments();
|
||||
const recorrenciasHook = usePatientRecurrences();
|
||||
const recurrenceHook = useRecurrence();
|
||||
|
||||
// ── Breakpoints + drawer ───────────────────────────────────
|
||||
const drawerOpen = ref(false);
|
||||
@@ -456,85 +458,8 @@ function addFinancial() {
|
||||
novoLancOpen.value = true;
|
||||
}
|
||||
|
||||
// Atalho: navega pra aba Agenda + abre dialog de nova sessao.
|
||||
// Frequencia espelha AgendaEventDialog: avulsa | semanal | quinzenal |
|
||||
// diasEspecificos. Avulsa cria 1 row em agenda_eventos; demais criam
|
||||
// regra em recurrence_rules via useRecurrence.
|
||||
const novaSessaoOpen = ref(false);
|
||||
const novaSessaoForm = ref({
|
||||
tipo: 'sessao',
|
||||
data: '',
|
||||
hora: '',
|
||||
duracao_min: 50,
|
||||
modalidade: 'presencial',
|
||||
titulo_custom: '',
|
||||
observacoes: '',
|
||||
freq: 'avulsa',
|
||||
diasSelecionados: [],
|
||||
qtdMode: '12',
|
||||
qtdCustom: 12
|
||||
});
|
||||
const FREQ_OPCOES = [
|
||||
{ value: 'avulsa', label: 'Avulsa' },
|
||||
{ value: 'semanal', label: 'Semanal' },
|
||||
{ value: 'quinzenal', label: 'Quinzenal' },
|
||||
{ value: 'diasEspecificos', label: 'Dias específicos' }
|
||||
];
|
||||
const DIAS_SEMANA_OPCOES = [
|
||||
{ value: 1, short: 'Seg' },
|
||||
{ value: 2, short: 'Ter' },
|
||||
{ value: 3, short: 'Qua' },
|
||||
{ value: 4, short: 'Qui' },
|
||||
{ value: 5, short: 'Sex' },
|
||||
{ value: 6, short: 'Sáb' },
|
||||
{ value: 0, short: 'Dom' }
|
||||
];
|
||||
const QTD_SESSOES_OPCOES = [
|
||||
{ value: '4', label: '4 sessões' },
|
||||
{ value: '8', label: '8 sessões' },
|
||||
{ value: '12', label: '12 sessões' },
|
||||
{ value: 'personalizar', label: 'Personalizar' }
|
||||
];
|
||||
|
||||
function toggleDiaSelecionado(d) {
|
||||
const arr = [...(novaSessaoForm.value.diasSelecionados || [])];
|
||||
const idx = arr.indexOf(d);
|
||||
if (idx === -1) arr.push(d);
|
||||
else arr.splice(idx, 1);
|
||||
novaSessaoForm.value.diasSelecionados = arr;
|
||||
}
|
||||
|
||||
const qtdSessoesEfetiva = computed(() => {
|
||||
const f = novaSessaoForm.value;
|
||||
if (f.qtdMode === 'personalizar') return Number(f.qtdCustom) || null;
|
||||
return Number(f.qtdMode) || null;
|
||||
});
|
||||
|
||||
// Label dinamico do botao "Salvar"
|
||||
const novaSessaoCtaLabel = computed(() => {
|
||||
return novaSessaoForm.value.freq === 'avulsa' ? 'Agendar sessão' : 'Criar recorrência';
|
||||
});
|
||||
const SESSAO_TIPOS = [
|
||||
{ label: 'Sessão', value: 'sessao' },
|
||||
{ label: 'Primeira consulta', value: 'primeira' },
|
||||
{ label: 'Retorno', value: 'retorno' },
|
||||
{ label: 'Avaliação', value: 'avaliacao' },
|
||||
{ label: 'Devolutiva', value: 'devolutiva' }
|
||||
];
|
||||
const SESSAO_DURACOES = [
|
||||
{ label: '30 min', value: 30 },
|
||||
{ label: '40 min', value: 40 },
|
||||
{ label: '45 min', value: 45 },
|
||||
{ label: '50 min', value: 50 },
|
||||
{ label: '55 min', value: 55 },
|
||||
{ label: '1 hora', value: 60 },
|
||||
{ label: '1h 30', value: 90 },
|
||||
{ label: '2 horas', value: 120 }
|
||||
];
|
||||
const SESSAO_MODALIDADES = [
|
||||
{ label: 'Presencial', value: 'presencial' },
|
||||
{ label: 'Online (vídeo)', value: 'online' }
|
||||
];
|
||||
// Abre o AgendaEventDialog GLOBAL (mesmo que MelissaAgenda usa) com
|
||||
// paciente pre-selecionado. O dialog vive no MelissaLayout via provide.
|
||||
function goAgendar() {
|
||||
activeTab.value = 'agenda';
|
||||
if (isMobile.value) drawerOpen.value = false;
|
||||
@@ -542,132 +467,30 @@ function goAgendar() {
|
||||
toast.add({ severity: 'warn', summary: 'Paciente sem ID', life: 2500 });
|
||||
return;
|
||||
}
|
||||
// Default: amanha as 09:00, sessao 50min presencial.
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
novaSessaoForm.value = {
|
||||
tipo: 'sessao',
|
||||
data: tomorrow.toISOString().slice(0, 10),
|
||||
hora: '09:00',
|
||||
duracao_min: 50,
|
||||
modalidade: 'presencial',
|
||||
titulo_custom: '',
|
||||
observacoes: '',
|
||||
freq: 'avulsa',
|
||||
diasSelecionados: [],
|
||||
qtdMode: '12',
|
||||
qtdCustom: 12
|
||||
};
|
||||
novaSessaoOpen.value = true;
|
||||
}
|
||||
async function salvarSessao() {
|
||||
const f = novaSessaoForm.value;
|
||||
if (!f.data) {
|
||||
toast.add({ severity: 'warn', summary: 'Data obrigatória', life: 2500 });
|
||||
return;
|
||||
}
|
||||
if (!f.hora) {
|
||||
toast.add({ severity: 'warn', summary: 'Hora obrigatória', life: 2500 });
|
||||
return;
|
||||
}
|
||||
const [y, m, d] = f.data.split('-').map(Number);
|
||||
const [hh, mm] = f.hora.split(':').map(Number);
|
||||
const inicio = new Date(y, (m || 1) - 1, d || 1, hh || 0, mm || 0, 0);
|
||||
if (Number.isNaN(inicio.getTime())) {
|
||||
toast.add({ severity: 'warn', summary: 'Data/hora inválida', life: 2500 });
|
||||
return;
|
||||
}
|
||||
|
||||
// Caminho RECORRENTE: cria regra em recurrence_rules via useRecurrence.
|
||||
// Ocorrencias sao geradas dinamicamente.
|
||||
if (f.freq !== 'avulsa') {
|
||||
// Mapeamento freq -> { type, interval, weekdays }
|
||||
let type, interval, weekdays;
|
||||
if (f.freq === 'semanal') {
|
||||
type = 'weekly';
|
||||
interval = 1;
|
||||
weekdays = [inicio.getDay()];
|
||||
} else if (f.freq === 'quinzenal') {
|
||||
type = 'biweekly';
|
||||
interval = 2;
|
||||
weekdays = [inicio.getDay()];
|
||||
} else if (f.freq === 'diasEspecificos') {
|
||||
if (!f.diasSelecionados.length) {
|
||||
toast.add({ severity: 'warn', summary: 'Selecione ao menos um dia da semana', life: 3000 });
|
||||
return;
|
||||
}
|
||||
type = 'custom_weekdays';
|
||||
interval = 1;
|
||||
weekdays = [...f.diasSelecionados].sort();
|
||||
}
|
||||
const max = qtdSessoesEfetiva.value;
|
||||
if (!max || max < 1) {
|
||||
toast.add({ severity: 'warn', summary: 'Quantidade de sessões inválida', life: 2500 });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { data: userData } = await supabase.auth.getUser();
|
||||
const ownerId = userData?.user?.id;
|
||||
const rule = {
|
||||
patient_id: props.patientId,
|
||||
owner_id: ownerId,
|
||||
type,
|
||||
interval,
|
||||
weekdays,
|
||||
start_date: f.data,
|
||||
end_date: null,
|
||||
max_occurrences: max,
|
||||
start_time: f.hora,
|
||||
duration_min: Number(f.duracao_min) || 50,
|
||||
modalidade: f.modalidade,
|
||||
titulo_custom: String(f.titulo_custom || '').trim() || null,
|
||||
observacoes: String(f.observacoes || '').trim() || null,
|
||||
status: 'ativo'
|
||||
};
|
||||
await recurrenceHook.createRule(rule);
|
||||
if (!melissaAgenda || typeof melissaAgenda.onCreateEventoForPatient !== 'function') {
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Recorrência criada',
|
||||
detail: `${max} sessões previstas. Veja em "Recorrências".`,
|
||||
severity: 'warn',
|
||||
summary: 'Agenda indisponível',
|
||||
detail: 'Aguarde a agenda carregar e tente novamente.',
|
||||
life: 3000
|
||||
});
|
||||
novaSessaoOpen.value = false;
|
||||
return;
|
||||
}
|
||||
melissaAgenda.onCreateEventoForPatient(props.patientId);
|
||||
}
|
||||
|
||||
// Watch o dialogOpen do AgendaEventDialog: quando ele fecha (true -> false)
|
||||
// refetcha sessoes + recorrencias do paciente. Cobre tanto save quanto
|
||||
// close-sem-salvar (idempotente; load() ja eh barato).
|
||||
if (melissaAgenda?.dialogOpen) {
|
||||
watch(melissaAgenda.dialogOpen, async (now, prev) => {
|
||||
if (prev && !now && props.patientId) {
|
||||
await Promise.all([
|
||||
sessionsHook.load(props.patientId),
|
||||
recorrenciasHook.load(props.patientId)
|
||||
]);
|
||||
} catch (e) {
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Falha ao criar recorrência',
|
||||
detail: e?.message || 'Erro inesperado',
|
||||
life: 4000
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Caminho SESSAO UNICA: insert direto em agenda_eventos.
|
||||
const fim = new Date(inicio.getTime() + (Number(f.duracao_min) || 50) * 60 * 1000);
|
||||
const result = await sessionsHook.createSession(props.patientId, {
|
||||
inicio_em: inicio.toISOString(),
|
||||
fim_em: fim.toISOString(),
|
||||
tipo: f.tipo,
|
||||
modalidade: f.modalidade,
|
||||
titulo_custom: f.titulo_custom,
|
||||
observacoes: f.observacoes
|
||||
});
|
||||
if (result.ok) {
|
||||
toast.add({ severity: 'success', summary: 'Sessão agendada', life: 2200 });
|
||||
novaSessaoOpen.value = false;
|
||||
} else {
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Falha ao agendar',
|
||||
detail: result.error || 'Erro inesperado',
|
||||
life: 4000
|
||||
});
|
||||
}
|
||||
}
|
||||
async function salvarLancamento() {
|
||||
const f = novoLancForm.value;
|
||||
@@ -2381,185 +2204,11 @@ onBeforeUnmount(() => {
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<!-- Dialog: nova sessao na agenda. Mesma logica do Lancamento — form
|
||||
simples inline com tipo/data/hora/duracao/modalidade/titulo/obs.
|
||||
Header custom: icon + titulo + subtitulo com nome do paciente. -->
|
||||
<Dialog
|
||||
v-model:visible="novaSessaoOpen"
|
||||
modal
|
||||
dismissable-mask
|
||||
:style="{ width: '460px', maxWidth: '92vw' }"
|
||||
>
|
||||
<template #header>
|
||||
<div class="mpa-dlg-head">
|
||||
<div class="mpa-dlg-head__icon">
|
||||
<i class="pi pi-calendar-plus" />
|
||||
</div>
|
||||
<div class="mpa-dlg-head__text">
|
||||
<div class="mpa-dlg-head__title">Nova sessão</div>
|
||||
<div class="mpa-dlg-head__sub">{{ dash(nomeCompleto) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="mpa-novo-lanc">
|
||||
<div class="mpa-novo-lanc__field">
|
||||
<label class="mpa-novo-lanc__label">Tipo</label>
|
||||
<Select
|
||||
v-model="novaSessaoForm.tipo"
|
||||
:options="SESSAO_TIPOS"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div class="mpa-novo-lanc__row">
|
||||
<div class="mpa-novo-lanc__field">
|
||||
<label class="mpa-novo-lanc__label">Data *</label>
|
||||
<InputText
|
||||
v-model="novaSessaoForm.data"
|
||||
type="date"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div class="mpa-novo-lanc__field">
|
||||
<label class="mpa-novo-lanc__label">Hora *</label>
|
||||
<InputText
|
||||
v-model="novaSessaoForm.hora"
|
||||
type="time"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mpa-novo-lanc__row">
|
||||
<div class="mpa-novo-lanc__field">
|
||||
<label class="mpa-novo-lanc__label">Duração</label>
|
||||
<Select
|
||||
v-model="novaSessaoForm.duracao_min"
|
||||
:options="SESSAO_DURACOES"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div class="mpa-novo-lanc__field">
|
||||
<label class="mpa-novo-lanc__label">Modalidade</label>
|
||||
<Select
|
||||
v-model="novaSessaoForm.modalidade"
|
||||
:options="SESSAO_MODALIDADES"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mpa-novo-lanc__field">
|
||||
<label class="mpa-novo-lanc__label">Título <span class="mpa-novo-lanc__opt">(opcional)</span></label>
|
||||
<InputText
|
||||
v-model="novaSessaoForm.titulo_custom"
|
||||
placeholder="Ex: Sessão de avaliação inicial"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div class="mpa-novo-lanc__field">
|
||||
<label class="mpa-novo-lanc__label">Observações <span class="mpa-novo-lanc__opt">(opcional)</span></label>
|
||||
<Textarea
|
||||
v-model="novaSessaoForm.observacoes"
|
||||
rows="2"
|
||||
auto-resize
|
||||
placeholder="Anotações que aparecem no card da agenda"
|
||||
class="w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Bloco de frequencia (espelha AgendaEventDialog: chips + qtd) -->
|
||||
<div class="mpa-recur">
|
||||
<label class="mpa-novo-lanc__label">Frequência</label>
|
||||
<div class="mpa-freq-chips">
|
||||
<button
|
||||
v-for="opt in FREQ_OPCOES"
|
||||
:key="opt.value"
|
||||
type="button"
|
||||
class="mpa-freq-chip"
|
||||
:class="{ 'is-active': novaSessaoForm.freq === opt.value }"
|
||||
@click="novaSessaoForm.freq = opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Preview semanal/quinzenal -->
|
||||
<div v-if="novaSessaoForm.freq === 'semanal' || novaSessaoForm.freq === 'quinzenal'" class="mpa-freq-preview">
|
||||
<i class="pi pi-refresh" />
|
||||
<span>
|
||||
{{ novaSessaoForm.freq === 'quinzenal' ? 'A cada 2 semanas, toda' : 'Toda' }}
|
||||
{{ novaSessaoForm.data ? (WEEKDAY_LABEL_BLOCK[new Date(novaSessaoForm.data + 'T00:00:00').getDay()] || '...').toLowerCase() : '...' }},
|
||||
às {{ novaSessaoForm.hora || '—' }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Dias da semana (diasEspecificos) -->
|
||||
<div v-if="novaSessaoForm.freq === 'diasEspecificos'" class="mpa-freq-dias">
|
||||
<label class="mpa-novo-lanc__label">Dias da semana</label>
|
||||
<div class="mpa-dias-grid">
|
||||
<button
|
||||
v-for="d in DIAS_SEMANA_OPCOES"
|
||||
:key="d.value"
|
||||
type="button"
|
||||
class="mpa-dia-chip"
|
||||
:class="{ 'is-active': novaSessaoForm.diasSelecionados.includes(d.value) }"
|
||||
@click="toggleDiaSelecionado(d.value)"
|
||||
>
|
||||
{{ d.short }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quantidade de sessoes (so quando nao avulsa) -->
|
||||
<div v-if="novaSessaoForm.freq !== 'avulsa'" class="mpa-freq-qtd">
|
||||
<label class="mpa-novo-lanc__label">Quantidade de sessões</label>
|
||||
<div class="mpa-freq-chips">
|
||||
<button
|
||||
v-for="opt in QTD_SESSOES_OPCOES"
|
||||
:key="opt.value"
|
||||
type="button"
|
||||
class="mpa-freq-chip mpa-freq-chip--sm"
|
||||
:class="{ 'is-active': novaSessaoForm.qtdMode === opt.value }"
|
||||
@click="novaSessaoForm.qtdMode = opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="novaSessaoForm.qtdMode === 'personalizar'" class="mpa-freq-qtd-custom">
|
||||
<InputNumber
|
||||
v-model="novaSessaoForm.qtdCustom"
|
||||
:min="1"
|
||||
:max="200"
|
||||
show-buttons
|
||||
button-layout="horizontal"
|
||||
fluid
|
||||
>
|
||||
<template #decrementbuttonicon><i class="pi pi-minus" /></template>
|
||||
<template #incrementbuttonicon><i class="pi pi-plus" /></template>
|
||||
</InputNumber>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<button class="mpa-quick-btn" @click="novaSessaoOpen = false">
|
||||
<i class="pi pi-times" />
|
||||
<span>Cancelar</span>
|
||||
</button>
|
||||
<button
|
||||
class="mpa-quick-btn mpa-quick-btn--cta"
|
||||
:disabled="sessionsHook.busy.value"
|
||||
@click="salvarSessao"
|
||||
>
|
||||
<i :class="sessionsHook.busy.value ? 'pi pi-spin pi-spinner' : 'pi pi-check'" :style="{ color: '#10b981' }" />
|
||||
<span>{{ novaSessaoCtaLabel }}</span>
|
||||
</button>
|
||||
</template>
|
||||
</Dialog>
|
||||
<!-- Dialog Nova Sessao removido — agora usa o AgendaEventDialog
|
||||
GLOBAL do MelissaLayout (mesmo que a Agenda usa). MelissaPaciente
|
||||
dispara via melissaAgenda.onCreateEventoForPatient(patientId).
|
||||
Watch em melissaAgenda.dialogOpen refetcha sessions+recorrencias
|
||||
quando o dialog fecha (cobre save e cancel). -->
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -590,6 +590,48 @@ function _buildHandlers(deps) {
|
||||
dialogOpen.value = true;
|
||||
}
|
||||
|
||||
// ── onCreateEventoForPatient — abre o AgendaEventDialog com paciente
|
||||
// pre-selecionado. Usado pelo MelissaPaciente quando o user clica
|
||||
// "Agendar" na sidebar Acoes Rapidas. Mesma logica de onCreateEvento
|
||||
// (defaults razoaveis: hoje proximo slot 15min, duracao default), so
|
||||
// que injeta paciente_id no dialogEventRow.
|
||||
function onCreateEventoForPatient(patientId) {
|
||||
if (!ownerId.value) {
|
||||
toast.add({
|
||||
severity: 'warn',
|
||||
summary: 'Agenda',
|
||||
detail: 'Aguarde carregar as configurações da agenda.',
|
||||
life: 3000
|
||||
});
|
||||
return;
|
||||
}
|
||||
const durMin =
|
||||
settings.value?.session_duration_min ??
|
||||
settings.value?.duracao_padrao_minutos ??
|
||||
50;
|
||||
const base = new Date();
|
||||
base.setSeconds(0, 0);
|
||||
const remainder = base.getMinutes() % 15;
|
||||
if (remainder !== 0) {
|
||||
base.setMinutes(base.getMinutes() + (15 - remainder));
|
||||
}
|
||||
|
||||
dialogEventRow.value = {
|
||||
owner_id: ownerId.value,
|
||||
terapeuta_id: null,
|
||||
paciente_id: patientId ? String(patientId) : null,
|
||||
tipo: EVENTO_TIPO.SESSAO,
|
||||
status: 'agendado',
|
||||
titulo: deriveTituloDefaultByTipo(EVENTO_TIPO.SESSAO),
|
||||
observacoes: null,
|
||||
visibility_scope: 'public',
|
||||
determined_commitment_id: null
|
||||
};
|
||||
dialogStartISO.value = base.toISOString();
|
||||
dialogEndISO.value = new Date(base.getTime() + durMin * 60000).toISOString();
|
||||
dialogOpen.value = true;
|
||||
}
|
||||
|
||||
// ── onSelectTime — click-drag no FC pra criar evento novo ──
|
||||
// Dinâmica de duração:
|
||||
// click sem drag → settings.session_duration_min (default 50)
|
||||
@@ -814,6 +856,7 @@ function _buildHandlers(deps) {
|
||||
return {
|
||||
onEditEvento,
|
||||
onCreateEvento,
|
||||
onCreateEventoForPatient,
|
||||
onSelectTime,
|
||||
persistMoveOrResize,
|
||||
onEditSeriesOccurrence,
|
||||
|
||||
Reference in New Issue
Block a user