Ajuste em Massa - Paciente, Terapeuta, Clinica e Admin - Inicio agenda
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import Dialog from 'primevue/dialog'
|
||||
import Button from 'primevue/button'
|
||||
import Dropdown from 'primevue/dropdown'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Textarea from 'primevue/textarea'
|
||||
import FloatLabel from 'primevue/floatlabel'
|
||||
import InputNumber from 'primevue/inputnumber'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
|
||||
// Para editar
|
||||
eventRow: { type: Object, default: null },
|
||||
|
||||
// Para criar via seleção no calendário
|
||||
initialStartISO: { type: String, default: '' },
|
||||
initialEndISO: { type: String, default: '' },
|
||||
|
||||
// Quem é o dono da agenda (owner_id)
|
||||
ownerId: { type: String, default: '' },
|
||||
|
||||
// Se estiver criando na visão clínica e quiser atribuir a um owner específico
|
||||
allowOwnerEdit: { type: Boolean, default: false },
|
||||
ownerOptions: { type: Array, default: () => [] } // [{ label, value }]
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'save', 'delete'])
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (v) => emit('update:modelValue', v)
|
||||
})
|
||||
|
||||
const tipoOptions = [
|
||||
{ label: 'Sessão', value: 'sessao' },
|
||||
{ label: 'Bloqueio', value: 'bloqueio' },
|
||||
{ label: 'Pessoal', value: 'pessoal' },
|
||||
{ label: 'Clínica', value: 'clinica' }
|
||||
]
|
||||
|
||||
const statusOptions = [
|
||||
{ label: 'Agendado', value: 'agendado' },
|
||||
{ label: 'Realizado', value: 'realizado' },
|
||||
{ label: 'Faltou', value: 'faltou' },
|
||||
{ label: 'Cancelado', value: 'cancelado' }
|
||||
]
|
||||
|
||||
const form = ref(resetForm())
|
||||
|
||||
watch(
|
||||
() => [props.eventRow, props.initialStartISO, props.initialEndISO, props.ownerId],
|
||||
() => {
|
||||
form.value = resetForm()
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function resetForm () {
|
||||
const r = props.eventRow
|
||||
|
||||
// ISO strings (timestamptz)
|
||||
const startISO = r?.inicio_em || props.initialStartISO || ''
|
||||
const endISO = r?.fim_em || props.initialEndISO || ''
|
||||
|
||||
return {
|
||||
id: r?.id || null,
|
||||
owner_id: r?.owner_id || props.ownerId || '',
|
||||
terapeuta_id: r?.terapeuta_id ?? null,
|
||||
paciente_id: r?.paciente_id ?? null,
|
||||
|
||||
tipo: r?.tipo || 'sessao',
|
||||
status: r?.status || 'agendado',
|
||||
titulo: r?.titulo || '',
|
||||
observacoes: r?.observacoes || '',
|
||||
|
||||
inicio_em: startISO,
|
||||
fim_em: endISO,
|
||||
|
||||
// ajuda de UX (minutos) caso você queira editar duração fácil
|
||||
duracaoMin: calcMinutes(startISO, endISO) || 50
|
||||
}
|
||||
}
|
||||
|
||||
function calcMinutes (a, b) {
|
||||
try {
|
||||
if (!a || !b) return null
|
||||
const ms = new Date(b).getTime() - new Date(a).getTime()
|
||||
return Math.max(0, Math.round(ms / 60000))
|
||||
} catch { return null }
|
||||
}
|
||||
|
||||
function addMinutesISO (iso, min) {
|
||||
const d = new Date(iso)
|
||||
d.setMinutes(d.getMinutes() + Number(min || 0))
|
||||
return d.toISOString()
|
||||
}
|
||||
|
||||
const isEdit = computed(() => !!form.value.id)
|
||||
|
||||
const canSave = computed(() => {
|
||||
if (!form.value.owner_id) return false
|
||||
if (!form.value.inicio_em) return false
|
||||
if (!form.value.fim_em) return false
|
||||
const a = new Date(form.value.inicio_em).getTime()
|
||||
const b = new Date(form.value.fim_em).getTime()
|
||||
return b > a
|
||||
})
|
||||
|
||||
function applyDuration () {
|
||||
if (!form.value.inicio_em) return
|
||||
form.value.fim_em = addMinutesISO(form.value.inicio_em, form.value.duracaoMin || 50)
|
||||
}
|
||||
|
||||
function onSave () {
|
||||
if (!canSave.value) return
|
||||
|
||||
const payload = {
|
||||
owner_id: form.value.owner_id,
|
||||
terapeuta_id: form.value.terapeuta_id,
|
||||
paciente_id: form.value.paciente_id,
|
||||
|
||||
tipo: form.value.tipo,
|
||||
status: form.value.status,
|
||||
titulo: form.value.titulo || null,
|
||||
observacoes: form.value.observacoes || null,
|
||||
|
||||
inicio_em: form.value.inicio_em,
|
||||
fim_em: form.value.fim_em
|
||||
}
|
||||
|
||||
emit('save', { id: form.value.id, payload })
|
||||
}
|
||||
|
||||
function onDelete () {
|
||||
if (!form.value.id) return
|
||||
emit('delete', form.value.id)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:visible="visible" modal :style="{ width: '720px', maxWidth: '95vw' }" :header="isEdit ? 'Editar evento' : 'Novo evento'">
|
||||
<div class="grid gap-4">
|
||||
<div class="grid md:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<FloatLabel>
|
||||
<Dropdown
|
||||
id="tipo"
|
||||
class="w-full"
|
||||
:options="tipoOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
v-model="form.tipo"
|
||||
/>
|
||||
<label for="tipo">Tipo</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FloatLabel>
|
||||
<Dropdown
|
||||
id="status"
|
||||
class="w-full"
|
||||
:options="statusOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
v-model="form.status"
|
||||
/>
|
||||
<label for="status">Status</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
|
||||
<div v-if="allowOwnerEdit">
|
||||
<FloatLabel>
|
||||
<Dropdown
|
||||
id="owner"
|
||||
class="w-full"
|
||||
:options="ownerOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
v-model="form.owner_id"
|
||||
/>
|
||||
<label for="owner">Profissional</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FloatLabel>
|
||||
<InputText id="titulo" class="w-full" v-model="form.titulo" placeholder=" " />
|
||||
<label for="titulo">Título</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<FloatLabel>
|
||||
<InputText id="inicio" class="w-full" v-model="form.inicio_em" placeholder=" " />
|
||||
<label for="inicio">Início (ISO)</label>
|
||||
</FloatLabel>
|
||||
<div class="text-xs opacity-70 mt-1">Por enquanto em ISO. Depois trocamos para DatePicker bonito.</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FloatLabel>
|
||||
<InputText id="fim" class="w-full" v-model="form.fim_em" placeholder=" " />
|
||||
<label for="fim">Fim (ISO)</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid md:grid-cols-2 gap-3 items-end">
|
||||
<div>
|
||||
<FloatLabel>
|
||||
<InputNumber id="dur" class="w-full" v-model="form.duracaoMin" :min="5" :max="480" />
|
||||
<label for="dur">Duração (min)</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<Button label="Aplicar duração" severity="secondary" outlined icon="pi pi-clock" @click="applyDuration" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<FloatLabel>
|
||||
<Textarea id="obs" class="w-full" autoResize rows="3" v-model="form.observacoes" placeholder=" " />
|
||||
<label for="obs">Observações</label>
|
||||
</FloatLabel>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center pt-2">
|
||||
<Button v-if="isEdit" label="Excluir" icon="pi pi-trash" severity="danger" outlined @click="onDelete" />
|
||||
|
||||
<div class="flex gap-2 ml-auto">
|
||||
<Button label="Cancelar" severity="secondary" outlined @click="visible = false" />
|
||||
<Button label="Salvar" icon="pi pi-check" :disabled="!canSave" @click="onSave" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
Reference in New Issue
Block a user