MelissaPaciente Fase 6: Tab Financeiro completa + mark paid (mutation que legacy nao tem)
EXTENSAO src/features/patients/utils/patientFormatters.js
- recordStatus(r): pago / vencido (paid_at NULL && due_date < hoje) / pendente
- RECORD_STATUS_LABEL map
- fmtPaymentMethod(v): PIX/Cartao/Dinheiro/Boleto/Transferencia/Convenio
cobrindo variantes pt-br + camelCase
EXTENSAO src/features/patients/composables/usePatientFinancial.js
- ref `busy` + `_lastPatientId` interno
- recordsOrdenados computed: DESC por due_date com fallback created_at
- markPaid(recordId): UPDATE financial_records SET paid_at=NOW() +
auto-reload via _lastPatientId. Retorna {ok, error?}
- markUnpaid(recordId): reverte (paid_at=NULL) + auto-reload
MELISSAPACIENTE.VUE — script
- Imports: recordStatus, RECORD_STATUS_LABEL, fmtPaymentMethod
- markRecordPaid(r): chama financialHook.markPaid + toast success/error
- revertRecordPaid(r): chama markUnpaid + toast
MELISSAPACIENTE.VUE — Tab Financeiro reescrita (substitui placeholder Fase 1)
- Loading state
- Empty state com CTA "Novo lancamento" (mpa-quick-btn--cta)
- 3 KPIs: Pago / Pendente com proxVenc / Em atraso (cor adaptativa
vermelho quando > 0, cinza quando 0)
- Header "Lancamentos" com badge count + botao "+ Novo" no canto
- Tabela 6-col responsiva:
- Vencimento (date mono + relative)
- Descricao
- Forma (PIX/Cartao/etc)
- Valor (mono right-aligned)
- Status pill colorida (verde pago / vermelho vencido / azul pendente)
- Action button (pi-check verde marca pago / pi-undo amarelo reverte)
- border-left adaptativa por status
- Mobile: tabela colapsa em cards 2-col 4-row
DIFERENCA DO LEGACY: o PatientProntuario.vue exibe a tabela mas NAO
permite marcar pago/reverter direto dela. MelissaPaciente adiciona essa
acao inline (mutation auto-reload).
CSS: ~190L novos. Padrao Melissa: status pills com color-mix, JetBrains
Mono pra valores, header cell uppercase letter-spacing.
ESLint: 0 errors da minha mudanca.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,50 @@ Touched: none
|
|||||||
## [2026-05-08 00:00] session | Melissa cfg-* nativas + temas + cronometro DB
|
## [2026-05-08 00:00] session | Melissa cfg-* nativas + temas + cronometro DB
|
||||||
Touched: none
|
Touched: none
|
||||||
|
|
||||||
|
## [2026-05-08 17:30] session | MelissaPaciente Fase 6 — Tab Financeiro completa + mark paid mutation
|
||||||
|
Touched: none
|
||||||
|
Detalhes: Tab Financeiro espelha o legacy + adiciona mutation que o
|
||||||
|
legacy NAO tem (mark/unmark pago direto da tabela).
|
||||||
|
|
||||||
|
EXTENSAO patientFormatters.js:
|
||||||
|
- recordStatus(r): pago | vencido | pendente
|
||||||
|
- RECORD_STATUS_LABEL map.
|
||||||
|
- fmtPaymentMethod(v): PIX/Cartao/Dinheiro/Boleto/Transferencia/Convenio
|
||||||
|
cobrindo variantes.
|
||||||
|
|
||||||
|
EXTENSAO usePatientFinancial.js:
|
||||||
|
- ref `busy` + `_lastPatientId` interno.
|
||||||
|
- recordsOrdenados computed (DESC por due_date com fallback created_at).
|
||||||
|
- markPaid(recordId): UPDATE financial_records SET paid_at=NOW() + auto-
|
||||||
|
reload. Retorna {ok, error?}.
|
||||||
|
- markUnpaid(recordId): UPDATE SET paid_at=NULL + auto-reload (reverte).
|
||||||
|
|
||||||
|
MELISSAPACIENTE.VUE — script
|
||||||
|
- Imports: recordStatus, RECORD_STATUS_LABEL, fmtPaymentMethod.
|
||||||
|
- markRecordPaid(record) handler: chama markPaid + toast success/error.
|
||||||
|
- revertRecordPaid(record): chama markUnpaid + toast.
|
||||||
|
|
||||||
|
MELISSAPACIENTE.VUE — Tab Financeiro reescrita
|
||||||
|
- Loading state.
|
||||||
|
- Empty state com CTA "Novo lancamento" (botao mpa-quick-btn--cta).
|
||||||
|
- 3 KPIs (Pago / Pendente com proxVenc / Em atraso com cor adaptativa).
|
||||||
|
- Header "Lancamentos" com badge count + botao "+ Novo" no canto.
|
||||||
|
- Tabela 6-col: Vencimento (date mono+rel) | Descricao | Forma | Valor
|
||||||
|
(mono right) | Status pill colorida (pago verde / vencido vermelho /
|
||||||
|
pendente azul) | Action button.
|
||||||
|
- Action: pi-check (verde) pra marcar pago, pi-undo (amarelo) pra reverter.
|
||||||
|
- border-left adaptativa por status (verde pago / vermelho vencido /
|
||||||
|
azul pendente).
|
||||||
|
- Mobile: tabela colapsa em cards 2-col 4-row (date|amount / desc /
|
||||||
|
method|status / action).
|
||||||
|
|
||||||
|
CSS: ~190L novos pros componentes (mpa-fin__table/row/date/desc/method/
|
||||||
|
amount/status/action + responsive). Padrao Melissa: status pills com
|
||||||
|
color-mix, JetBrains Mono pra valores, header cell uppercase letter-
|
||||||
|
spacing.
|
||||||
|
|
||||||
|
ESLint: 0 errors da minha mudanca.
|
||||||
|
|
||||||
## [2026-05-08 16:30] session | MelissaPaciente Fase 5 — Tab Agenda completa
|
## [2026-05-08 16:30] session | MelissaPaciente Fase 5 — Tab Agenda completa
|
||||||
Touched: none
|
Touched: none
|
||||||
Detalhes: Tab Agenda com KPIs, filtros, agrupamento por mes e acoes
|
Detalhes: Tab Agenda com KPIs, filtros, agrupamento por mes e acoes
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ export function usePatientFinancial() {
|
|||||||
const records = ref([]);
|
const records = ref([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const error = ref('');
|
const error = ref('');
|
||||||
|
const busy = ref(false);
|
||||||
|
let _lastPatientId = null;
|
||||||
|
|
||||||
async function load(patientId) {
|
async function load(patientId) {
|
||||||
|
_lastPatientId = patientId || null;
|
||||||
if (!patientId) {
|
if (!patientId) {
|
||||||
records.value = [];
|
records.value = [];
|
||||||
return;
|
return;
|
||||||
@@ -101,15 +104,74 @@ export function usePatientFinancial() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lancamentos ordenados DESC por due_date (fallback created_at).
|
||||||
|
* Mais recente primeiro pra alimentar a tabela da Tab Financeiro.
|
||||||
|
*/
|
||||||
|
const recordsOrdenados = computed(() =>
|
||||||
|
[...records.value].sort((a, b) => {
|
||||||
|
const da = a.due_date || a.created_at;
|
||||||
|
const db = b.due_date || b.created_at;
|
||||||
|
return new Date(db) - new Date(da);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marca um lancamento como pago (paid_at = now). Auto-reload.
|
||||||
|
* Retorna {ok, error?}.
|
||||||
|
*/
|
||||||
|
async function markPaid(recordId) {
|
||||||
|
if (!recordId || busy.value) return { ok: false, error: 'busy' };
|
||||||
|
busy.value = true;
|
||||||
|
try {
|
||||||
|
const { error: err } = await supabase
|
||||||
|
.from('financial_records')
|
||||||
|
.update({ paid_at: new Date().toISOString() })
|
||||||
|
.eq('id', recordId);
|
||||||
|
if (err) throw err;
|
||||||
|
if (_lastPatientId) await load(_lastPatientId);
|
||||||
|
return { ok: true };
|
||||||
|
} catch (e) {
|
||||||
|
return { ok: false, error: e?.message || 'Erro ao marcar como pago' };
|
||||||
|
} finally {
|
||||||
|
busy.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverte: remove paid_at (volta pra pendente). Auto-reload.
|
||||||
|
*/
|
||||||
|
async function markUnpaid(recordId) {
|
||||||
|
if (!recordId || busy.value) return { ok: false, error: 'busy' };
|
||||||
|
busy.value = true;
|
||||||
|
try {
|
||||||
|
const { error: err } = await supabase
|
||||||
|
.from('financial_records')
|
||||||
|
.update({ paid_at: null })
|
||||||
|
.eq('id', recordId);
|
||||||
|
if (err) throw err;
|
||||||
|
if (_lastPatientId) await load(_lastPatientId);
|
||||||
|
return { ok: true };
|
||||||
|
} catch (e) {
|
||||||
|
return { ok: false, error: e?.message || 'Erro ao reverter pagamento' };
|
||||||
|
} finally {
|
||||||
|
busy.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
records,
|
records,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
|
busy,
|
||||||
load,
|
load,
|
||||||
totalRecebido,
|
totalRecebido,
|
||||||
totalEmAberto,
|
totalEmAberto,
|
||||||
totalAtrasado,
|
totalAtrasado,
|
||||||
ultimoPago,
|
ultimoPago,
|
||||||
statusFinanceiro
|
statusFinanceiro,
|
||||||
|
recordsOrdenados,
|
||||||
|
markPaid,
|
||||||
|
markUnpaid
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,6 +222,42 @@ export const STATUS_LABEL = {
|
|||||||
bloqueado: 'Bloqueado'
|
bloqueado: 'Bloqueado'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determina o status financeiro de um lancamento:
|
||||||
|
* - "pago": paid_at preenchido
|
||||||
|
* - "vencido": due_date < hoje E paid_at vazio
|
||||||
|
* - "pendente": demais casos com paid_at vazio
|
||||||
|
*/
|
||||||
|
export function recordStatus(r) {
|
||||||
|
if (r?.paid_at) return 'pago';
|
||||||
|
if (r?.due_date) {
|
||||||
|
const ms = new Date(r.due_date + 'T23:59:59').getTime();
|
||||||
|
if (!Number.isNaN(ms) && ms < Date.now()) return 'vencido';
|
||||||
|
}
|
||||||
|
return 'pendente';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RECORD_STATUS_LABEL = {
|
||||||
|
pago: 'Pago',
|
||||||
|
pendente: 'Pendente',
|
||||||
|
vencido: 'Vencido'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapeia variantes de payment_method pra label legivel.
|
||||||
|
*/
|
||||||
|
export function fmtPaymentMethod(v) {
|
||||||
|
const s = String(v ?? '').toLowerCase();
|
||||||
|
if (!s) return '';
|
||||||
|
if (s === 'pix') return 'PIX';
|
||||||
|
if (s === 'cartao' || s === 'cartão' || s === 'credit_card') return 'Cartão';
|
||||||
|
if (s === 'dinheiro' || s === 'cash') return 'Dinheiro';
|
||||||
|
if (s === 'boleto') return 'Boleto';
|
||||||
|
if (s === 'transferencia' || s === 'transfer' || s === 'ted' || s === 'doc') return 'Transferência';
|
||||||
|
if (s === 'convenio' || s === 'convênio') return 'Convênio';
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
export const STATUS_SEVERITY = {
|
export const STATUS_SEVERITY = {
|
||||||
agendado: 'info',
|
agendado: 'info',
|
||||||
realizado: 'success',
|
realizado: 'success',
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ import {
|
|||||||
fmtMarital,
|
fmtMarital,
|
||||||
fmtPhoneMobile,
|
fmtPhoneMobile,
|
||||||
sessionDuration,
|
sessionDuration,
|
||||||
|
recordStatus,
|
||||||
|
RECORD_STATUS_LABEL,
|
||||||
|
fmtPaymentMethod,
|
||||||
STATUS_LABEL,
|
STATUS_LABEL,
|
||||||
STATUS_SEVERITY,
|
STATUS_SEVERITY,
|
||||||
tagStyle as tagStyleHelper
|
tagStyle as tagStyleHelper
|
||||||
@@ -243,6 +246,34 @@ const agendaAgrupadas = computed(() => {
|
|||||||
return groups;
|
return groups;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handler de mutacao financeira: marcar pago / reverter
|
||||||
|
async function markRecordPaid(record) {
|
||||||
|
const result = await financialHook.markPaid(record.id);
|
||||||
|
if (result.ok) {
|
||||||
|
toast.add({ severity: 'success', summary: 'Lançamento marcado como pago', life: 2200 });
|
||||||
|
} else {
|
||||||
|
toast.add({
|
||||||
|
severity: 'error',
|
||||||
|
summary: 'Falha ao marcar pago',
|
||||||
|
detail: result.error || 'Erro inesperado',
|
||||||
|
life: 4000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function revertRecordPaid(record) {
|
||||||
|
const result = await financialHook.markUnpaid(record.id);
|
||||||
|
if (result.ok) {
|
||||||
|
toast.add({ severity: 'success', summary: 'Pagamento revertido', life: 2200 });
|
||||||
|
} else {
|
||||||
|
toast.add({
|
||||||
|
severity: 'error',
|
||||||
|
summary: 'Falha ao reverter',
|
||||||
|
detail: result.error || 'Erro inesperado',
|
||||||
|
life: 4000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handler de mutacao de status (Realizada / Falta / Cancelar)
|
// Handler de mutacao de status (Realizada / Falta / Cancelar)
|
||||||
async function updateSessionStatus(ev, novoStatus, msg) {
|
async function updateSessionStatus(ev, novoStatus, msg) {
|
||||||
const result = await sessionsHook.updateStatus(ev.id, novoStatus);
|
const result = await sessionsHook.updateStatus(ev.id, novoStatus);
|
||||||
@@ -1454,24 +1485,157 @@ onBeforeUnmount(() => {
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ABA: Financeiro -->
|
<!-- ABA: Financeiro (Fase 6 — KPIs + tabela + mark paid) -->
|
||||||
<div v-else-if="activeTab === 'financ'" class="mpa-tab">
|
<div v-else-if="activeTab === 'financ'" class="mpa-tab">
|
||||||
<div class="mpa-w">
|
<!-- Loading -->
|
||||||
<div class="mpa-w__head">
|
<div v-if="financialHook.loading.value" class="mpa-empty">
|
||||||
<div class="mpa-w__icon mpa-w__icon--orange"><i class="pi pi-wallet" /></div>
|
<i class="pi pi-spin pi-spinner mr-2" /> Carregando…
|
||||||
<div class="mpa-w__title">
|
|
||||||
<div class="mpa-w__title-text">Financeiro — Fase 6</div>
|
|
||||||
<div class="mpa-w__sub">Recebíveis, atrasos, histórico</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="mpa-w__body">
|
|
||||||
<p class="mpa-placeholder">
|
|
||||||
Em desenvolvimento — <strong>Fase 6</strong>. Recebido R$ {{ financialHook.totalRecebido.value.toFixed(2) }};
|
|
||||||
em aberto R$ {{ financialHook.totalEmAberto.value.toFixed(2) }};
|
|
||||||
atrasado R$ {{ financialHook.totalAtrasado.value.toFixed(2) }}.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Empty state com CTA -->
|
||||||
|
<div v-else-if="!financialHook.records.value.length" class="mpa-empty mpa-empty--rich">
|
||||||
|
<div class="mpa-empty__icon"><i class="pi pi-wallet" /></div>
|
||||||
|
<div class="mpa-empty__title">Sem lançamentos financeiros</div>
|
||||||
|
<div class="mpa-empty__sub">
|
||||||
|
Adicione o primeiro lançamento de cobrança ou recebimento deste paciente.
|
||||||
|
</div>
|
||||||
|
<button type="button" class="mpa-quick-btn mpa-quick-btn--cta" @click="addFinancial">
|
||||||
|
<i class="pi pi-plus" :style="{ color: '#f59e0b' }" />
|
||||||
|
<span>Novo lançamento</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<!-- 3 KPIs financeiros -->
|
||||||
|
<div class="mpa-kpis">
|
||||||
|
<article class="mpa-kpi" style="--c:#4ade80">
|
||||||
|
<span class="mpa-kpi__num">01</span>
|
||||||
|
<header class="mpa-kpi__head">
|
||||||
|
<div class="mpa-kpi__icon"><i class="pi pi-check-circle" /></div>
|
||||||
|
<span class="mpa-kpi__tag">Pago</span>
|
||||||
|
</header>
|
||||||
|
<div class="mpa-kpi__big mpa-kpi__big--small">
|
||||||
|
{{ fmtCurrency(financialHook.statusFinanceiro.value.totalPago) }}
|
||||||
|
</div>
|
||||||
|
<div class="mpa-kpi__cap">
|
||||||
|
{{ financialHook.records.value.filter((r) => !!r.paid_at).length }}
|
||||||
|
{{ financialHook.records.value.filter((r) => !!r.paid_at).length === 1 ? 'lançamento' : 'lançamentos' }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="mpa-kpi" style="--c:var(--p-primary-color)">
|
||||||
|
<span class="mpa-kpi__num">02</span>
|
||||||
|
<header class="mpa-kpi__head">
|
||||||
|
<div class="mpa-kpi__icon"><i class="pi pi-clock" /></div>
|
||||||
|
<span class="mpa-kpi__tag">Pendente</span>
|
||||||
|
</header>
|
||||||
|
<div class="mpa-kpi__big mpa-kpi__big--small">
|
||||||
|
{{ fmtCurrency(financialHook.statusFinanceiro.value.totalPendente) }}
|
||||||
|
</div>
|
||||||
|
<div class="mpa-kpi__cap">
|
||||||
|
<template v-if="financialHook.statusFinanceiro.value.proxVenc">
|
||||||
|
Próx. venc. {{ fmtDateBR(financialHook.statusFinanceiro.value.proxVenc.due_date) }}
|
||||||
|
</template>
|
||||||
|
<template v-else>A receber</template>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article
|
||||||
|
class="mpa-kpi"
|
||||||
|
:style="financialHook.statusFinanceiro.value.vencidos > 0 ? '--c:#f87171' : '--c:#94a3b8'"
|
||||||
|
>
|
||||||
|
<span class="mpa-kpi__num">03</span>
|
||||||
|
<header class="mpa-kpi__head">
|
||||||
|
<div class="mpa-kpi__icon"><i class="pi pi-exclamation-triangle" /></div>
|
||||||
|
<span class="mpa-kpi__tag">Em atraso</span>
|
||||||
|
</header>
|
||||||
|
<div class="mpa-kpi__big">{{ financialHook.statusFinanceiro.value.vencidos }}</div>
|
||||||
|
<div class="mpa-kpi__cap">
|
||||||
|
{{ financialHook.statusFinanceiro.value.vencidos === 0
|
||||||
|
? 'Tudo em dia'
|
||||||
|
: (financialHook.statusFinanceiro.value.vencidos === 1 ? 'lançamento vencido' : 'lançamentos vencidos') }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabela de lançamentos -->
|
||||||
|
<section class="mpa-panel">
|
||||||
|
<header class="mpa-panel__head">
|
||||||
|
<div class="mpa-panel__title"><i class="pi pi-list" /> Lançamentos</div>
|
||||||
|
<div class="mpa-fin__head-actions">
|
||||||
|
<span class="mpa-panel__badge">{{ financialHook.recordsOrdenados.value.length }}</span>
|
||||||
|
<button type="button" class="mpa-icon-btn-sm" v-tooltip.left="'Novo lançamento'" @click="addFinancial">
|
||||||
|
<i class="pi pi-plus" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="mpa-fin__table" role="table">
|
||||||
|
<div class="mpa-fin__row mpa-fin__row--head" role="row">
|
||||||
|
<span role="columnheader">Vencimento</span>
|
||||||
|
<span role="columnheader">Descrição</span>
|
||||||
|
<span role="columnheader" class="mpa-fin__col-method">Forma</span>
|
||||||
|
<span role="columnheader" class="mpa-fin__col-amount">Valor</span>
|
||||||
|
<span role="columnheader" class="mpa-fin__col-status">Status</span>
|
||||||
|
<span role="columnheader" class="mpa-fin__col-action"></span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="r in financialHook.recordsOrdenados.value"
|
||||||
|
:key="r.id"
|
||||||
|
class="mpa-fin__row"
|
||||||
|
:data-status="recordStatus(r)"
|
||||||
|
role="row"
|
||||||
|
>
|
||||||
|
<span class="mpa-fin__date" role="cell">
|
||||||
|
<span class="mpa-fin__date-main">
|
||||||
|
{{ r.due_date ? fmtDateBR(r.due_date) : fmtDateBR(r.created_at) }}
|
||||||
|
</span>
|
||||||
|
<span v-if="r.due_date" class="mpa-fin__date-rel">
|
||||||
|
{{ fmtRelative(r.due_date) }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="mpa-fin__desc" role="cell">
|
||||||
|
{{ r.description || (r.category ? r.category : 'Lançamento') }}
|
||||||
|
</span>
|
||||||
|
<span class="mpa-fin__method" role="cell">
|
||||||
|
{{ fmtPaymentMethod(r.payment_method) || '—' }}
|
||||||
|
</span>
|
||||||
|
<span class="mpa-fin__amount" role="cell">
|
||||||
|
{{ fmtCurrency(Number(r.amount) || 0) }}
|
||||||
|
</span>
|
||||||
|
<span class="mpa-fin__status-cell" role="cell">
|
||||||
|
<span class="mpa-fin__status" :data-status="recordStatus(r)">
|
||||||
|
<span class="mpa-fin__status-dot" />
|
||||||
|
{{ RECORD_STATUS_LABEL[recordStatus(r)] }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="mpa-fin__action" role="cell">
|
||||||
|
<!-- Marca como pago se ainda nao foi -->
|
||||||
|
<button
|
||||||
|
v-if="!r.paid_at"
|
||||||
|
type="button"
|
||||||
|
v-tooltip.left="'Marcar como pago'"
|
||||||
|
class="mpa-ag__act mpa-ag__act--ok"
|
||||||
|
:disabled="financialHook.busy.value"
|
||||||
|
@click="markRecordPaid(r)"
|
||||||
|
>
|
||||||
|
<i class="pi pi-check" />
|
||||||
|
</button>
|
||||||
|
<!-- Reverte pagamento -->
|
||||||
|
<button
|
||||||
|
v-else
|
||||||
|
type="button"
|
||||||
|
v-tooltip.left="'Reverter pagamento'"
|
||||||
|
class="mpa-ag__act mpa-ag__act--warn"
|
||||||
|
:disabled="financialHook.busy.value"
|
||||||
|
@click="revertRecordPaid(r)"
|
||||||
|
>
|
||||||
|
<i class="pi pi-undo" />
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ABA: Documentos -->
|
<!-- ABA: Documentos -->
|
||||||
@@ -2498,6 +2662,189 @@ onBeforeUnmount(() => {
|
|||||||
font-size: 0.66rem !important;
|
font-size: 0.66rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ═══════ Tab Financeiro (Fase 6) ═══════ */
|
||||||
|
.mpa-fin__head-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.mpa-icon-btn-sm {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--m-bg-medium);
|
||||||
|
border: 1px solid var(--m-border);
|
||||||
|
color: var(--p-primary-color);
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
transition: background-color 120ms ease, border-color 120ms ease;
|
||||||
|
}
|
||||||
|
.mpa-icon-btn-sm:hover {
|
||||||
|
background: color-mix(in srgb, var(--p-primary-color) 14%, transparent);
|
||||||
|
border-color: color-mix(in srgb, var(--p-primary-color) 38%, transparent);
|
||||||
|
}
|
||||||
|
.mpa-icon-btn-sm > i { font-size: 0.78rem; }
|
||||||
|
|
||||||
|
/* Quick btn variant CTA pra empty state */
|
||||||
|
.mpa-quick-btn--cta {
|
||||||
|
margin: 12px auto 0;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: var(--m-bg-medium);
|
||||||
|
border-color: var(--m-border-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabela financeira */
|
||||||
|
.mpa-fin__table {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.mpa-fin__row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 110px 1fr 90px 100px 100px 36px;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 14px;
|
||||||
|
border-top: 1px solid var(--m-border);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
transition: background-color 120ms ease;
|
||||||
|
}
|
||||||
|
.mpa-fin__row:first-child { border-top: none; }
|
||||||
|
.mpa-fin__row:hover:not(.mpa-fin__row--head) { background: var(--m-bg-medium); }
|
||||||
|
.mpa-fin__row[data-status="pago"] { border-left-color: rgb(34, 197, 94); }
|
||||||
|
.mpa-fin__row[data-status="vencido"] { border-left-color: rgb(239, 68, 68); }
|
||||||
|
.mpa-fin__row[data-status="pendente"] { border-left-color: rgb(96, 165, 250); }
|
||||||
|
|
||||||
|
.mpa-fin__row--head {
|
||||||
|
background: var(--m-bg-medium);
|
||||||
|
font-size: 0.66rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
color: var(--m-text-muted);
|
||||||
|
border-left-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mpa-fin__date {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.mpa-fin__date-main {
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--m-text);
|
||||||
|
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
}
|
||||||
|
.mpa-fin__date-rel {
|
||||||
|
font-size: 0.66rem;
|
||||||
|
color: var(--m-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mpa-fin__desc {
|
||||||
|
color: var(--m-text);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.mpa-fin__method {
|
||||||
|
color: var(--m-text-muted);
|
||||||
|
font-size: 0.74rem;
|
||||||
|
}
|
||||||
|
.mpa-fin__amount {
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--m-text);
|
||||||
|
text-align: right;
|
||||||
|
font-family: 'JetBrains Mono', ui-monospace, monospace;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mpa-fin__col-amount { text-align: right; }
|
||||||
|
.mpa-fin__col-method,
|
||||||
|
.mpa-fin__col-status,
|
||||||
|
.mpa-fin__col-action { text-align: center; }
|
||||||
|
|
||||||
|
.mpa-fin__status {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
background: var(--m-bg-medium);
|
||||||
|
border: 1px solid var(--m-border);
|
||||||
|
}
|
||||||
|
.mpa-fin__status-dot {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: currentColor;
|
||||||
|
}
|
||||||
|
.mpa-fin__status[data-status="pago"] {
|
||||||
|
background: color-mix(in srgb, rgb(34, 197, 94) 12%, transparent);
|
||||||
|
color: rgb(34, 197, 94);
|
||||||
|
border-color: color-mix(in srgb, rgb(34, 197, 94) 35%, transparent);
|
||||||
|
}
|
||||||
|
.mpa-fin__status[data-status="vencido"] {
|
||||||
|
background: color-mix(in srgb, rgb(239, 68, 68) 12%, transparent);
|
||||||
|
color: rgb(239, 68, 68);
|
||||||
|
border-color: color-mix(in srgb, rgb(239, 68, 68) 35%, transparent);
|
||||||
|
}
|
||||||
|
.mpa-fin__status[data-status="pendente"] {
|
||||||
|
background: color-mix(in srgb, rgb(96, 165, 250) 12%, transparent);
|
||||||
|
color: rgb(96, 165, 250);
|
||||||
|
border-color: color-mix(in srgb, rgb(96, 165, 250) 35%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mpa-fin__action {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: stack tabela em cards 2-col */
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.mpa-fin__row--head { display: none; }
|
||||||
|
.mpa-fin__row {
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
grid-template-rows: auto auto auto;
|
||||||
|
gap: 4px 10px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
}
|
||||||
|
.mpa-fin__date {
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
.mpa-fin__amount {
|
||||||
|
grid-row: 1;
|
||||||
|
grid-column: 2;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.mpa-fin__desc {
|
||||||
|
grid-row: 2;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
}
|
||||||
|
.mpa-fin__method {
|
||||||
|
grid-row: 3;
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
.mpa-fin__status-cell {
|
||||||
|
grid-row: 3;
|
||||||
|
grid-column: 2;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.mpa-fin__action {
|
||||||
|
grid-row: 4;
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ═══════ Tab Agenda (Fase 5) ═══════ */
|
/* ═══════ Tab Agenda (Fase 5) ═══════ */
|
||||||
.mpa-ag__group + .mpa-ag__group { margin-top: 10px; }
|
.mpa-ag__group + .mpa-ag__group { margin-top: 10px; }
|
||||||
.mpa-ag__list {
|
.mpa-ag__list {
|
||||||
|
|||||||
Reference in New Issue
Block a user