Copyright, Financeiro, Lançamentos, aprimoramentos de ui
This commit is contained in:
@@ -1,4 +1,19 @@
|
||||
<!-- src/features/agenda/components/AgendaEventDialog.vue -->
|
||||
<!--
|
||||
|--------------------------------------------------------------------------
|
||||
| Agência PSI
|
||||
|--------------------------------------------------------------------------
|
||||
| Criado e desenvolvido por Leonardo Nohama
|
||||
|
|
||||
| Tecnologia aplicada à escuta.
|
||||
| Estrutura para o cuidado.
|
||||
|
|
||||
| Arquivo: src/features/agenda/components/AgendaEventDialog.vue
|
||||
| Data: 2026
|
||||
| Local: São Carlos/SP — Brasil
|
||||
|--------------------------------------------------------------------------
|
||||
| © 2026 — Todos os direitos reservados
|
||||
|--------------------------------------------------------------------------
|
||||
-->
|
||||
<template>
|
||||
<Dialog
|
||||
v-model:visible="visible"
|
||||
@@ -457,7 +472,7 @@
|
||||
:style="{ background: `#${selectedCommitment.bg_color}20`, color: `#${selectedCommitment.bg_color}`, borderColor: `#${selectedCommitment.bg_color}40` }"
|
||||
>{{ selectedCommitmentName }}</span>
|
||||
<Tag v-else :value="selectedCommitmentName" severity="info" />
|
||||
<Tag v-if="isSessionEvent" :value="labelStatusSessao(form.status)" :severity="statusSeverity(form.status)" />
|
||||
<Tag v-if="isSessionEvent" :value="labelStatusSessao(form.status)" :severity="statusSeverity(form.status)" :class="statusExtraClass(form.status)" />
|
||||
</div>
|
||||
|
||||
<div class="summary-row">
|
||||
@@ -713,6 +728,13 @@
|
||||
</FloatLabel>
|
||||
</div>
|
||||
|
||||
<!-- ── COBRANÇA DA SESSÃO ──────────────────────────── -->
|
||||
<AgendaEventoFinanceiroPanel
|
||||
v-if="isSessionEvent && isEdit && eventRow?.id"
|
||||
:evento="eventRow"
|
||||
class="mb-3"
|
||||
/>
|
||||
|
||||
<!-- Opção de recorrência para sessão SEM série (criação ou avulsa) -->
|
||||
<template v-if="!hasSerie">
|
||||
<div class="side-card__title mb-2">Frequência</div>
|
||||
@@ -1147,8 +1169,10 @@ import InputNumber from 'primevue/inputnumber'
|
||||
import RadioButton from 'primevue/radiobutton'
|
||||
import Message from 'primevue/message'
|
||||
import { useConfirm } from 'primevue/useconfirm'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import { supabase } from '@/lib/supabase/client'
|
||||
import ComponentCadastroRapido from '@/components/ComponentCadastroRapido.vue'
|
||||
import AgendaEventoFinanceiroPanel from '@/components/agenda/AgendaEventoFinanceiroPanel.vue'
|
||||
import { useServices } from '@/features/agenda/composables/useServices'
|
||||
import { useCommitmentServices } from '@/features/agenda/composables/useCommitmentServices'
|
||||
import { usePatientDiscounts } from '@/features/agenda/composables/usePatientDiscounts'
|
||||
@@ -1193,8 +1217,9 @@ const props = defineProps({
|
||||
newPatientRoute: { type: String, default: '' },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'save', 'delete', 'updateSeriesEvent', 'editSeriesOccurrence'])
|
||||
const emit = defineEmits(['update:modelValue', 'save', 'delete', 'updateSeriesEvent', 'editSeriesOccurrence', 'updated'])
|
||||
const confirm = useConfirm()
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
@@ -1205,6 +1230,7 @@ const visible = computed({
|
||||
|
||||
const step = ref(1)
|
||||
const isEdit = computed(() => !!props.eventRow?.id || !!props.eventRow?.is_occurrence)
|
||||
|
||||
const allowBack = computed(() => !props.lockCommitment && !props.presetCommitmentId)
|
||||
|
||||
// ── série ─────────────────────────────────────────────────
|
||||
@@ -1405,6 +1431,48 @@ function isNativeSession (c) {
|
||||
|
||||
const form = ref(resetForm())
|
||||
|
||||
// ── ConfirmDialog para status sensíveis (cancelado / remarcar) ────────────
|
||||
const _prevStatus = ref(null)
|
||||
const _skipStatusWatch = ref(false)
|
||||
watch(() => form.value?.status, async (newVal, oldVal) => {
|
||||
if (_skipStatusWatch.value) return
|
||||
if (!isEdit.value || !form.value?.id) return
|
||||
if (newVal !== 'cancelado' && newVal !== 'remarcar') return
|
||||
|
||||
_prevStatus.value = oldVal
|
||||
|
||||
const isCancelar = newVal === 'cancelado'
|
||||
confirm.require({
|
||||
header: isCancelar ? 'Cancelar sessão' : 'Remarcar sessão',
|
||||
message: isCancelar
|
||||
? 'Tem certeza que deseja cancelar esta sessão? O status será salvo imediatamente.'
|
||||
: 'Tem certeza que deseja marcar esta sessão para remarcação? O status será salvo imediatamente.',
|
||||
icon: isCancelar ? 'pi pi-times-circle' : 'pi pi-refresh',
|
||||
acceptLabel: 'Sim, confirmar',
|
||||
rejectLabel: 'Não',
|
||||
acceptSeverity: isCancelar ? 'danger' : 'warn',
|
||||
accept: async () => {
|
||||
try {
|
||||
const { data, error } = await supabase
|
||||
.from('agenda_eventos')
|
||||
.update({ status: newVal })
|
||||
.eq('id', form.value.id)
|
||||
.select()
|
||||
.single()
|
||||
if (error) throw error
|
||||
toast.add({ severity: 'success', summary: 'Status atualizado', detail: `Sessão marcada como ${labelStatusSessao(newVal)}.`, life: 3000 })
|
||||
emit('updated', data)
|
||||
} catch (e) {
|
||||
toast.add({ severity: 'error', summary: 'Erro', detail: e?.message || 'Não foi possível atualizar o status.', life: 4000 })
|
||||
form.value.status = _prevStatus.value
|
||||
}
|
||||
},
|
||||
reject: () => {
|
||||
form.value.status = _prevStatus.value
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
// ── Precificação / Serviços ─────────────────────────────────────────
|
||||
const { services, getDefaultPrice, load: loadServices } = useServices()
|
||||
const { loadItems: _csLoadItems, saveItems: saveCommitmentItems, loadItemsOrTemplate: _csLoadItemsOrTemplate } = useCommitmentServices()
|
||||
@@ -1818,7 +1886,10 @@ watch(
|
||||
console.log('[AgendaEventDialog] abriu — eventRow:', JSON.parse(JSON.stringify(props.eventRow || {})))
|
||||
console.log('[AgendaEventDialog] isEdit:', isEdit.value, 'hasSerie:', hasSerie.value)
|
||||
|
||||
_skipStatusWatch.value = true
|
||||
form.value = resetForm()
|
||||
await nextTick()
|
||||
_skipStatusWatch.value = false
|
||||
samePatientConflict.value = null
|
||||
recorrenciaType.value = 'avulsa'
|
||||
diasSelecionados.value = []
|
||||
@@ -2760,21 +2831,28 @@ const googleCalendarUrl = computed(() => {
|
||||
})
|
||||
|
||||
function labelStatusSessao (v) {
|
||||
const map = { agendado: 'Agendado', realizado: 'Realizado', faltou: 'Faltou', cancelado: 'Cancelado' }
|
||||
const map = { agendado: 'Agendado', realizado: 'Realizado', faltou: 'Faltou', cancelado: 'Cancelado', remarcar: 'Remarcar' }
|
||||
return map[v] || '—'
|
||||
}
|
||||
function statusSeverity (v) {
|
||||
if (v === 'agendado') return 'success'
|
||||
if (v === 'realizado') return 'secondary'
|
||||
if (v === 'faltou') return 'danger'
|
||||
if (v === 'agendado') return 'info'
|
||||
if (v === 'realizado') return 'success'
|
||||
if (v === 'faltou') return 'warn'
|
||||
if (v === 'cancelado') return 'danger'
|
||||
if (v === 'remarcar') return 'secondary' // cor real via classe CSS
|
||||
return 'secondary'
|
||||
}
|
||||
function statusExtraClass (v) {
|
||||
return v === 'remarcar' ? 'tag-remarcar' : ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.agenda-event-composer :deep(.p-dialog-content) { padding: .75rem; }
|
||||
|
||||
/* ── tag: remarcar (roxo — sem severity nativo no PrimeVue) ─ */
|
||||
:deep(.tag-remarcar) { background: #a855f7 !important; color: #fff !important; }
|
||||
|
||||
/* ── header dot ─────────────────────────────────── */
|
||||
.header-dot {
|
||||
width: 10px; height: 10px; border-radius: 50%;
|
||||
|
||||
Reference in New Issue
Block a user