agenda popover: botao Agendada + fixes pos-C10/B

Adicoes (durante teste C10/A2):
- Botao "Agendada" no popover (pi-calendar, variante --info azul
  cyan) pra permitir reset de status realizado/faltou/cancelado
  voltando pra agendado sem precisar abrir o AgendaEventDialog.
  Wire-up: emit 'agendar' -> onAgendar -> updateEventoStatus.
- CSS .evento-act--info: hover + is-current com tom cyan
  (#38bdf8 do domainColors da agenda). Highlight generico
  rgba(255,255,255,0.12) era invisivel em light mode.

Bug fixes durante teste C10/B com Otto Rank:
- MelissaEventoPanel paymentLabel: usar paymentAmount tambem pra
  state='pending' (antes so 'paid' usava; pending caia em ev.price
  e mostrava R$ 150 original quando o pendente real era R$ 30 da
  multa).
- useMelissaAgenda onUpdateSeriesEvent: chamar _reloadRange() apos
  _applyStatusDecisions. Sem isso o paymentStateMap+amountMap nao
  re-populavam apos status change com multa -> FullCalendar e
  popover ficavam stale ate F5/troca de view.

Pendencia salva em memoria: travas em reverse transitions
(faltou->agendado deixa multa orfa). User hit pra valer com Otto
durante teste, R$ 30 limpo manualmente no DB. Implementar pos-C13.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Leonardo
2026-05-20 09:16:15 -03:00
parent d6423da9b4
commit 3caf5792f8
3 changed files with 33 additions and 5 deletions
+25 -5
View File
@@ -25,6 +25,7 @@ const props = defineProps({
const emit = defineEmits([
'close',
'agendar',
'concluir',
'faltou',
'cancelar',
@@ -159,11 +160,12 @@ const paymentIcon = computed(() => {
});
const paymentLabel = computed(() => {
const state = ev.value.paymentState;
// Pra estado 'paid', usar o VALOR REAL pago (paymentAmount, vem do
// financial_record). Em pacote upfront, é o package_price total —
// o evento.price pode ter sido editado depois e divergir. Em outros
// estados, fallback pro price/insurance_value do evento.
const valor = state === 'paid' && ev.value.paymentAmount != null
// Pra estados 'paid' e 'pending' usar o VALOR REAL do record (paymentAmount).
// Necessário pra cobrir caso de multa: original cancelled R$ 200 + multa
// pending R$ 30 → state='pending' mas paymentAmount=30 (não R$ 200 do ev.price).
// Em pacote upfront paid, é o package_price total. Só cai no fallback de
// ev.price/insurance_value quando state='none' (sem record ativo).
const valor = (state === 'paid' || state === 'pending') && ev.value.paymentAmount != null
? ev.value.paymentAmount
: (ev.value.price ?? ev.value.insurance_value);
const valorFmt = (valor != null && !Number.isNaN(Number(valor)))
@@ -358,6 +360,15 @@ function modalidadeIcon(mod) {
<section v-if="isSessaoComPaciente" class="evento-actions__section">
<div class="evento-actions__label">Marcar sessão como:</div>
<div class="evento-actions__group">
<button
class="evento-act evento-act--info"
:class="{ 'is-current': statusSlug === 'agendado' || statusSlug === '' }"
:disabled="busy"
@click="emit('agendar')"
>
<i class="pi pi-calendar" />
<span class="evento-act__label">Agendada</span>
</button>
<button
class="evento-act evento-act--ok"
:class="{ 'is-current': statusSlug === 'realizado' }"
@@ -905,6 +916,10 @@ html.app-dark .evento-row__pay-action--revogar {
opacity: 0.45;
cursor: not-allowed;
}
.evento-act--info:hover:not(:disabled) {
color: rgb(56, 189, 248);
background: rgba(56, 189, 248, 0.10);
}
.evento-act--ok:hover:not(:disabled) {
color: rgb(16, 185, 129);
background: rgba(16, 185, 129, 0.15);
@@ -925,6 +940,11 @@ html.app-dark .evento-row__pay-action--revogar {
background: rgba(255, 255, 255, 0.12);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}
.evento-act--info.is-current {
color: rgb(56, 189, 248);
background: rgba(56, 189, 248, 0.18);
box-shadow: inset 0 0 0 1px rgba(56, 189, 248, 0.55);
}
.evento-act--ok.is-current {
color: rgb(16, 185, 129);
background: rgba(16, 185, 129, 0.18);
+2
View File
@@ -708,6 +708,7 @@ async function updateEventoStatus(novoStatus, msgSucesso) {
}
}
function onAgendar() { updateEventoStatus('agendado', 'Sessão marcada como agendada'); }
function onConcluir() { updateEventoStatus('realizado', 'Sessão marcada como realizada'); }
function onFaltou() { updateEventoStatus('faltou', 'Marcada como falta'); }
function onCancelar() { updateEventoStatus('cancelado', 'Evento cancelado'); }
@@ -2490,6 +2491,7 @@ function onKeydown(e) {
:evento="eventoSelecionado"
:busy="eventoBusy"
@close="fecharEvento"
@agendar="onAgendar"
@concluir="onConcluir"
@faltou="onFaltou"
@cancelar="onCancelar"
@@ -1258,6 +1258,12 @@ function _buildHandlers(deps) {
ctx,
decision
});
// 3) Reload do range pra propagar paymentState/Amount atualizados
// pro FullCalendar + popover. Sem isso, badge $ e label "A receber"
// ficam stale até trocar de view ou F5. Caso típico: faltou+multa
// mostra R$ original (cancelled) em vez do R$ multa novo.
await _reloadRange();
} catch (e) {
toast.add({ severity: 'warn', summary: 'Erro', detail: e?.message || 'Falha ao atualizar status.', life: 3000 });
}