Melissa polish + Prontuario Visao Geral + agenda historico
Sprints B (05-03) e C (05-04) acumulados: - NotificationDrawer/Item redesign (visual mais limpo, ações inline) - Dock pins compose (useMelissaDockPins) + cache store global (melissaCacheStore) - MelissaAgenda: timeline FullCalendar parity + cards resumo, histórico card com useMelissaAgendaHistorico, MelissaEventoPanel ajustado - useFeriados: cache opt-in pra evitar fetch redundante de feriados - PatientProntuario: aba Visão Geral nova; PatientConversationsTab polish - AgendaClinicMosaic / AgendaTerapeutaPage / useAgendaSettings: ajustes de paridade com Melissa - DocumentsListPage: pequenos ajustes - DB migration 20260504000001: fix do trigger pra status 'excluido' nas cancel_notifications Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,8 @@ const emit = defineEmits([
|
||||
'faltou',
|
||||
'cancelar',
|
||||
'remarcar',
|
||||
'edit',
|
||||
'edit-sessao', // botão dedicado ao lado das horas → AgendaEventDialog
|
||||
'edit-paciente', // botão "Editar" do grupo Outras opções → PatientCadastroDialog
|
||||
'abrir-prontuario',
|
||||
'whatsapp',
|
||||
'historico'
|
||||
@@ -68,9 +69,6 @@ const isSessaoComPaciente = computed(
|
||||
() => ev.value.tipo === 'sessao' && (ev.value.patient_id || ev.value.pacienteNome)
|
||||
);
|
||||
|
||||
// Status finais não permitem mudar pra outro status (UI mais clara)
|
||||
const statusEhFinal = computed(() => /realizad|faltou|cancelad/i.test(ev.value.status || ''));
|
||||
|
||||
function fmtHora(decimal) {
|
||||
if (decimal === null || decimal === undefined || Number.isNaN(decimal)) return '—';
|
||||
const h = Math.floor(decimal);
|
||||
@@ -123,6 +121,16 @@ function modalidadeIcon(mod) {
|
||||
{{ fmtHora(ev.startH) }} – {{ fmtHora(ev.endH) }}
|
||||
<span v-if="duracaoMin() !== null" class="evento-row__sub">· {{ duracaoMin() }}min</span>
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="evento-row__edit"
|
||||
:disabled="busy"
|
||||
v-tooltip.top="'Editar sessão (data, hora, recorrência…)'"
|
||||
@click="emit('edit-sessao')"
|
||||
>
|
||||
<i class="pi pi-pencil" />
|
||||
<span>Editar sessão</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="ev.modalidade" class="evento-row">
|
||||
@@ -140,83 +148,111 @@ function modalidadeIcon(mod) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action bar — agrupada por contexto -->
|
||||
<!-- Action bar — agrupada por contexto.
|
||||
Cada botão tem ícone + label textual empilhados pra reduzir
|
||||
ambiguidade (tooltip sozinho não é descobrível em touch). -->
|
||||
<footer class="evento-actions">
|
||||
<!-- Grupo Status — só pra sessão e quando ainda não é status final -->
|
||||
<div v-if="isSessaoComPaciente && !statusEhFinal" class="evento-actions__group">
|
||||
<button
|
||||
class="evento-act evento-act--ok"
|
||||
:disabled="busy"
|
||||
v-tooltip.top="'Marcar como realizada'"
|
||||
@click="emit('concluir')"
|
||||
>
|
||||
<i class="pi pi-check-circle" />
|
||||
</button>
|
||||
<button
|
||||
class="evento-act evento-act--warn"
|
||||
:disabled="busy"
|
||||
v-tooltip.top="'Marcar como falta'"
|
||||
@click="emit('faltou')"
|
||||
>
|
||||
<i class="pi pi-user-minus" />
|
||||
</button>
|
||||
<button
|
||||
class="evento-act"
|
||||
:disabled="busy"
|
||||
v-tooltip.top="'Remarcar'"
|
||||
@click="emit('remarcar')"
|
||||
>
|
||||
<i class="pi pi-calendar-clock" />
|
||||
</button>
|
||||
<button
|
||||
class="evento-act evento-act--danger"
|
||||
:disabled="busy"
|
||||
v-tooltip.top="'Cancelar'"
|
||||
@click="emit('cancelar')"
|
||||
>
|
||||
<i class="pi pi-ban" />
|
||||
</button>
|
||||
</div>
|
||||
<!-- Grupo Status — sempre visível pra sessão (permite trocar
|
||||
de status mesmo após marcar realizado/faltou/cancelado).
|
||||
Status atual fica destacado via .is-current. -->
|
||||
<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--ok"
|
||||
:class="{ 'is-current': statusSlug === 'realizado' }"
|
||||
:disabled="busy"
|
||||
@click="emit('concluir')"
|
||||
>
|
||||
<i class="pi pi-check-circle" />
|
||||
<span class="evento-act__label">Realizada</span>
|
||||
</button>
|
||||
<button
|
||||
class="evento-act evento-act--warn"
|
||||
:class="{ 'is-current': statusSlug === 'faltou' }"
|
||||
:disabled="busy"
|
||||
@click="emit('faltou')"
|
||||
>
|
||||
<i class="pi pi-user-minus" />
|
||||
<span class="evento-act__label">Falta</span>
|
||||
</button>
|
||||
<button
|
||||
class="evento-act"
|
||||
:class="{ 'is-current': statusSlug === 'remarcar' || statusSlug === 'remarcado' }"
|
||||
:disabled="busy"
|
||||
@click="emit('remarcar')"
|
||||
>
|
||||
<i class="pi pi-calendar-clock" />
|
||||
<span class="evento-act__label">Reagendar</span>
|
||||
</button>
|
||||
<button
|
||||
class="evento-act evento-act--danger"
|
||||
:class="{ 'is-current': statusSlug === 'cancelado' }"
|
||||
:disabled="busy"
|
||||
@click="emit('cancelar')"
|
||||
>
|
||||
<i class="pi pi-ban" />
|
||||
<span class="evento-act__label">Cancelar</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Grupo Paciente — só pra sessão com paciente vinculado -->
|
||||
<div v-if="isSessaoComPaciente" class="evento-actions__group">
|
||||
<button
|
||||
class="evento-act"
|
||||
:disabled="busy"
|
||||
v-tooltip.top="'Abrir prontuário'"
|
||||
@click="emit('abrir-prontuario')"
|
||||
>
|
||||
<i class="pi pi-file" />
|
||||
</button>
|
||||
<button
|
||||
class="evento-act"
|
||||
:disabled="busy"
|
||||
v-tooltip.top="'Conversar (WhatsApp)'"
|
||||
@click="emit('whatsapp')"
|
||||
>
|
||||
<i class="pi pi-whatsapp" />
|
||||
</button>
|
||||
<button
|
||||
class="evento-act"
|
||||
:disabled="busy"
|
||||
v-tooltip.top="'Histórico de sessões'"
|
||||
@click="emit('historico')"
|
||||
>
|
||||
<i class="pi pi-history" />
|
||||
</button>
|
||||
</div>
|
||||
<!-- Grupo Outras opções — só pra sessão com paciente.
|
||||
"Editar" abre o cadastro do paciente (não a sessão);
|
||||
pra editar a sessão, usar o botão ao lado das horas. -->
|
||||
<section v-if="isSessaoComPaciente" class="evento-actions__section">
|
||||
<div class="evento-actions__label">Outras opções:</div>
|
||||
<div class="evento-actions__group">
|
||||
<button
|
||||
class="evento-act"
|
||||
:disabled="busy"
|
||||
@click="emit('abrir-prontuario')"
|
||||
>
|
||||
<i class="pi pi-file" />
|
||||
<span class="evento-act__label">Prontuário</span>
|
||||
</button>
|
||||
<button
|
||||
class="evento-act"
|
||||
:disabled="busy"
|
||||
@click="emit('historico')"
|
||||
>
|
||||
<i class="pi pi-history" />
|
||||
<span class="evento-act__label">Sessões</span>
|
||||
</button>
|
||||
<button
|
||||
class="evento-act"
|
||||
:disabled="busy"
|
||||
@click="emit('whatsapp')"
|
||||
>
|
||||
<i class="pi pi-whatsapp" />
|
||||
<span class="evento-act__label">Conversar</span>
|
||||
</button>
|
||||
<button
|
||||
class="evento-act"
|
||||
:disabled="busy"
|
||||
v-tooltip.top="'Editar cadastro do paciente'"
|
||||
@click="emit('edit-paciente')"
|
||||
>
|
||||
<i class="pi pi-user-edit" />
|
||||
<span class="evento-act__label">Editar</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Grupo Geral — Editar sempre disponível -->
|
||||
<div class="evento-actions__group">
|
||||
<button
|
||||
class="evento-act"
|
||||
:disabled="busy"
|
||||
v-tooltip.top="'Editar evento'"
|
||||
@click="emit('edit')"
|
||||
>
|
||||
<i class="pi pi-pencil" />
|
||||
</button>
|
||||
</div>
|
||||
<!-- Grupo Geral (não-sessão: bloqueio/compromisso/etc).
|
||||
Aqui "Editar" abre o evento em si (não tem paciente). -->
|
||||
<section v-else class="evento-actions__section">
|
||||
<div class="evento-actions__group">
|
||||
<button
|
||||
class="evento-act"
|
||||
:disabled="busy"
|
||||
@click="emit('edit-sessao')"
|
||||
>
|
||||
<i class="pi pi-pencil" />
|
||||
<span class="evento-act__label">Editar</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
@@ -333,6 +369,34 @@ function modalidadeIcon(mod) {
|
||||
margin-left: 4px;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
/* Botão "Editar sessão" inline na linha das horas. Discreto na largura
|
||||
padrão, ganha destaque no hover. Margin-left auto pra alinhar à direita. */
|
||||
.evento-row__edit {
|
||||
margin-left: auto;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 4px 10px;
|
||||
background: var(--m-bg-soft);
|
||||
border: 1px solid var(--m-border);
|
||||
border-radius: 100px;
|
||||
color: var(--m-text-muted);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
transition: background-color 140ms ease, color 140ms ease, border-color 140ms ease;
|
||||
}
|
||||
.evento-row__edit:hover:not(:disabled) {
|
||||
background: var(--m-bg-soft-hover);
|
||||
color: var(--m-text);
|
||||
border-color: var(--m-accent, var(--primary-color, #7c6af7));
|
||||
}
|
||||
.evento-row__edit:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.evento-row__edit i { font-size: 0.65rem; }
|
||||
.evento-status {
|
||||
padding: 2px 10px;
|
||||
border-radius: 999px;
|
||||
@@ -378,25 +442,41 @@ function modalidadeIcon(mod) {
|
||||
/* ─── Action bar ────────────────────────────────── */
|
||||
.evento-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid var(--m-border);
|
||||
justify-content: space-between;
|
||||
}
|
||||
.evento-actions__section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
.evento-actions__label {
|
||||
font-size: 0.7rem;
|
||||
color: var(--m-text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
font-weight: 600;
|
||||
padding-left: 2px;
|
||||
}
|
||||
.evento-actions__group {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
background: var(--m-bg-soft);
|
||||
border: 1px solid var(--m-border);
|
||||
border-radius: 12px;
|
||||
padding: 4px;
|
||||
}
|
||||
.evento-act {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 8px 4px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--m-text);
|
||||
@@ -406,6 +486,13 @@ function modalidadeIcon(mod) {
|
||||
font-family: inherit;
|
||||
transition: background-color 140ms ease, color 140ms ease, transform 140ms ease;
|
||||
}
|
||||
.evento-act__label {
|
||||
font-size: 0.7rem;
|
||||
line-height: 1.1;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.01em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.evento-act:hover:not(:disabled) {
|
||||
background: var(--m-bg-soft-hover);
|
||||
transform: translateY(-1px);
|
||||
@@ -431,6 +518,29 @@ function modalidadeIcon(mod) {
|
||||
background: rgba(239, 68, 68, 0.15);
|
||||
}
|
||||
|
||||
/* Estado .is-current — sinaliza o status atual da sessão dentro do
|
||||
grupo de actions. Permite que o usuário troque o status mesmo após
|
||||
marcar realizado/faltou/cancelado, vendo qual está ativo. */
|
||||
.evento-act.is-current {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.evento-act--ok.is-current {
|
||||
color: rgb(16, 185, 129);
|
||||
background: rgba(16, 185, 129, 0.18);
|
||||
box-shadow: inset 0 0 0 1px rgba(16, 185, 129, 0.55);
|
||||
}
|
||||
.evento-act--warn.is-current {
|
||||
color: rgb(245, 158, 11);
|
||||
background: rgba(245, 158, 11, 0.18);
|
||||
box-shadow: inset 0 0 0 1px rgba(245, 158, 11, 0.55);
|
||||
}
|
||||
.evento-act--danger.is-current {
|
||||
color: rgb(239, 68, 68);
|
||||
background: rgba(239, 68, 68, 0.18);
|
||||
box-shadow: inset 0 0 0 1px rgba(239, 68, 68, 0.55);
|
||||
}
|
||||
|
||||
/* Light mode — overlay ainda mais discreto */
|
||||
html:not(.app-dark) .evento-layer {
|
||||
background: rgba(15, 23, 42, 0.18);
|
||||
|
||||
Reference in New Issue
Block a user