Correcao Sidebar Classico e Rail, Correcao Layout, Ajuste de Breakpoint para Tailwind, Ajuste AppTopbar, Ajuste Menu PopOver, Recriado Paleta de Cores, Inserido algumas animações leves, Reajuste Cor items NOVOS da tabela, Drawer Ajuda Corrigido no Logout, Whatsapp, sms, email, recursos extras

This commit is contained in:
Leonardo
2026-03-24 21:26:58 -03:00
parent a89d1f5560
commit 53a4980396
453 changed files with 121427 additions and 174407 deletions
+190 -191
View File
@@ -24,278 +24,277 @@
// mapAgendaEventosToCalendarEvents
// ─────────────────────────────────────────────────────────────────────────────
export function mapAgendaEventosToCalendarEvents (rows) {
return (rows || []).map(_mapRow).filter(Boolean)
export function mapAgendaEventosToCalendarEvents(rows) {
return (rows || []).map(_mapRow).filter(Boolean);
}
// ─────────────────────────────────────────────────────────────────────────────
// mapAgendaEventosToClinicResourceEvents
// ─────────────────────────────────────────────────────────────────────────────
export function mapAgendaEventosToClinicResourceEvents (rows) {
return (rows || []).map((r) => {
const ev = _mapRow(r)
if (!ev) return null
ev.resourceId = normalizeId(r?.owner_id ?? r?.terapeuta_id ?? null)
return ev
}).filter(Boolean)
export function mapAgendaEventosToClinicResourceEvents(rows) {
return (rows || [])
.map((r) => {
const ev = _mapRow(r);
if (!ev) return null;
ev.resourceId = normalizeId(r?.owner_id ?? r?.terapeuta_id ?? null);
return ev;
})
.filter(Boolean);
}
// ─────────────────────────────────────────────────────────────────────────────
// mapper interno
// ─────────────────────────────────────────────────────────────────────────────
function _mapRow (r) {
if (!r) return null
function _mapRow(r) {
if (!r) return null;
const isOccurrence = !!r.is_occurrence
const isRealSession = !isOccurrence
const isOccurrence = !!r.is_occurrence;
const isRealSession = !isOccurrence;
const ownerId = normalizeId(r?.owner_id ?? r?.terapeuta_id ?? null)
const ownerId = normalizeId(r?.owner_id ?? r?.terapeuta_id ?? null);
// commitment / cores
const commitment = r.determined_commitments ?? r.commitment ?? null
const baseBg = commitment?.bg_color ? `#${commitment.bg_color}` : null
const baseTxt = commitment?.text_color ? `#${commitment.text_color}` : null
const statusBg = _statusBgColor(r.status)
const bgColor = statusBg ?? baseBg ?? undefined
const txtColor = baseTxt ?? (statusBg ? '#ffffff' : undefined)
// commitment / cores
const commitment = r.determined_commitments ?? r.commitment ?? null;
const baseBg = commitment?.bg_color ? `#${commitment.bg_color}` : null;
const baseTxt = commitment?.text_color ? `#${commitment.text_color}` : null;
const statusBg = _statusBgColor(r.status);
const bgColor = statusBg ?? baseBg ?? undefined;
const txtColor = baseTxt ?? (statusBg ? '#ffffff' : undefined);
// título
const nomeP = r.patients?.nome_completo ?? r.paciente_nome ?? r.patient_name ?? ''
const titleBase = r.titulo_custom || r.titulo || (nomeP ? nomeP : tituloFallback(r.tipo))
const icon = _statusIcon(r.status, isOccurrence, !!r.recurrence_id)
const title = `${icon}${titleBase}`
// título
const nomeP = r.patients?.nome_completo ?? r.paciente_nome ?? r.patient_name ?? '';
const titleBase = r.titulo_custom || r.titulo || (nomeP ? nomeP : tituloFallback(r.tipo));
const icon = _statusIcon(r.status, isOccurrence, !!r.recurrence_id);
const title = `${icon}${titleBase}`;
// recorrência — nova + fallback legada
const recurrenceId = r.recurrence_id ?? null
const originalDate = r.original_date ?? r.recurrence_date ?? null
const exceptionType = r.exception_type ?? null
// recorrência — nova + fallback legada
const recurrenceId = r.recurrence_id ?? null;
const originalDate = r.original_date ?? r.recurrence_date ?? null;
const exceptionType = r.exception_type ?? null;
return {
id: r.id ?? `occ::${recurrenceId}::${originalDate}`,
title,
start: r.inicio_em,
end: r.fim_em,
return {
id: r.id ?? `occ::${recurrenceId}::${originalDate}`,
title,
start: r.inicio_em,
end: r.fim_em,
...(bgColor && { backgroundColor: bgColor, borderColor: bgColor }),
...(txtColor && { textColor: txtColor }),
...(bgColor && { backgroundColor: bgColor, borderColor: bgColor }),
...(txtColor && { textColor: txtColor }),
extendedProps: {
// identidade
dbId: r.id ?? null,
isOccurrence,
isRealSession,
extendedProps: {
// identidade
dbId: r.id ?? null,
isOccurrence,
isRealSession,
// owner
owner_id: ownerId,
terapeuta_id: normalizeId(r?.terapeuta_id ?? null),
// owner
owner_id: ownerId,
terapeuta_id: normalizeId(r?.terapeuta_id ?? null),
// compromisso
tipo: r.tipo ?? null,
status: r.status ?? null,
determined_commitment_id: r.determined_commitment_id ?? null,
commitment_bg_color: bgColor ?? null,
commitment_text_color: txtColor ?? null,
// compromisso
tipo: r.tipo ?? null,
status: r.status ?? null,
determined_commitment_id: r.determined_commitment_id ?? null,
commitment_bg_color: bgColor ?? null,
commitment_text_color: txtColor ?? null,
// paciente
patient_id: r.patient_id ?? null,
paciente_id: r.patient_id ?? null, // alias para compatibilidade com dialog/form
paciente_nome: nomeP,
paciente_avatar: r.patients?.avatar_url ?? r.paciente_avatar ?? null,
paciente_status: r.patients?.status ?? r.paciente_status ?? null,
// paciente
patient_id: r.patient_id ?? null,
paciente_id: r.patient_id ?? null, // alias para compatibilidade com dialog/form
paciente_nome: nomeP,
paciente_avatar: r.patients?.avatar_url ?? r.paciente_avatar ?? null,
paciente_status: r.patients?.status ?? r.paciente_status ?? null,
// campos
observacoes: r.observacoes ?? null,
titulo_custom: r.titulo_custom ?? null,
extra_fields: r.extra_fields ?? null,
modalidade: r.modalidade ?? null,
// campos
observacoes: r.observacoes ?? null,
titulo_custom: r.titulo_custom ?? null,
extra_fields: r.extra_fields ?? null,
modalidade: r.modalidade ?? null,
// privacidade (clínica)
visibility_scope: r.visibility_scope ?? null,
masked: !!r.masked,
// privacidade (clínica)
visibility_scope: r.visibility_scope ?? null,
masked: !!r.masked,
// recorrência — NOVA arquitetura
recurrence_id: recurrenceId,
original_date: originalDate,
exception_type: exceptionType,
exception_id: r.exception_id ?? null,
exception_reason: r.exception_reason ?? null,
// recorrência — NOVA arquitetura
recurrence_id: recurrenceId,
original_date: originalDate,
exception_type: exceptionType,
exception_id: r.exception_id ?? null,
exception_reason: r.exception_reason ?? null,
// recorrência — fallback LEGADA (não quebra enquanto migra)
serie_id: r.serie_id ?? recurrenceId ?? null,
serie_dia_semana: r.agenda_series?.dia_semana ?? r.serie_dia_semana ?? null,
serie_hora: r.agenda_series?.hora_inicio ?? r.serie_hora ?? null,
serie_duracao: r.agenda_series?.duracao_min ?? r.serie_duracao ?? null,
serie_status: r.agenda_series?.status ?? r.serie_status ?? null,
is_exception: r.is_exception ?? (exceptionType != null),
// recorrência — fallback LEGADA (não quebra enquanto migra)
serie_id: r.serie_id ?? recurrenceId ?? null,
serie_dia_semana: r.agenda_series?.dia_semana ?? r.serie_dia_semana ?? null,
serie_hora: r.agenda_series?.hora_inicio ?? r.serie_hora ?? null,
serie_duracao: r.agenda_series?.duracao_min ?? r.serie_duracao ?? null,
serie_status: r.agenda_series?.status ?? r.serie_status ?? null,
is_exception: r.is_exception ?? exceptionType != null,
// financeiro
price: r.price ?? null,
billed: r.billed ?? false,
billing_contract_id: r.billing_contract_id ?? null,
insurance_plan_id: r.insurance_plan_id ?? null,
insurance_guide_number: r.insurance_guide_number ?? null,
insurance_value: r.insurance_value != null ? Number(r.insurance_value) : null,
insurance_plan_service_id: r.insurance_plan_service_id ?? null,
// financeiro
price: r.price ?? null,
billed: r.billed ?? false,
billing_contract_id: r.billing_contract_id ?? null,
insurance_plan_id: r.insurance_plan_id ?? null,
insurance_guide_number: r.insurance_guide_number ?? null,
insurance_value: r.insurance_value != null ? Number(r.insurance_value) : null,
insurance_plan_service_id: r.insurance_plan_service_id ?? null,
// timestamps
inicio_em: r.inicio_em,
fim_em: r.fim_em,
tenant_id: r.tenant_id ?? null,
}
}
// timestamps
inicio_em: r.inicio_em,
fim_em: r.fim_em,
tenant_id: r.tenant_id ?? null
}
};
}
// ─────────────────────────────────────────────────────────────────────────────
// buildNextSessions
// ─────────────────────────────────────────────────────────────────────────────
export function buildNextSessions (rows, now = new Date()) {
const nowMs = now.getTime()
return (rows || [])
.filter(r => new Date(r.fim_em).getTime() >= nowMs)
.slice(0, 6)
.map(r => ({
id: r.id,
title: r.titulo || tituloFallback(r.tipo),
startISO: r.inicio_em,
endISO: r.fim_em,
tipo: r.tipo,
status: r.status,
pacienteId: r.patient_id || null
}))
export function buildNextSessions(rows, now = new Date()) {
const nowMs = now.getTime();
return (rows || [])
.filter((r) => new Date(r.fim_em).getTime() >= nowMs)
.slice(0, 6)
.map((r) => ({
id: r.id,
title: r.titulo || tituloFallback(r.tipo),
startISO: r.inicio_em,
endISO: r.fim_em,
tipo: r.tipo,
status: r.status,
pacienteId: r.patient_id || null
}));
}
// ─────────────────────────────────────────────────────────────────────────────
// calcDefaultSlotDuration
// ─────────────────────────────────────────────────────────────────────────────
export function calcDefaultSlotDuration (settings) {
const min =
((settings?.usar_granularidade_custom && settings?.granularidade_min) || 0) ||
settings?.admin_slot_visual_minutos ||
30
return minutesToDuration(min)
export function calcDefaultSlotDuration(settings) {
const min = (settings?.usar_granularidade_custom && settings?.granularidade_min) || 0 || settings?.admin_slot_visual_minutos || 30;
return minutesToDuration(min);
}
// ─────────────────────────────────────────────────────────────────────────────
// buildWeeklyBreakBackgroundEvents — código original preservado integralmente
// ─────────────────────────────────────────────────────────────────────────────
export function buildWeeklyBreakBackgroundEvents (pausas, rangeStart, rangeEnd) {
if (!Array.isArray(pausas) || pausas.length === 0) return []
export function buildWeeklyBreakBackgroundEvents(pausas, rangeStart, rangeEnd) {
if (!Array.isArray(pausas) || pausas.length === 0) return [];
const out = []
const dayMs = 24 * 60 * 60 * 1000
const out = [];
const dayMs = 24 * 60 * 60 * 1000;
for (let ts = startOfDay(rangeStart).getTime(); ts < rangeEnd.getTime(); ts += dayMs) {
const d = new Date(ts)
const dow = d.getDay()
for (let ts = startOfDay(rangeStart).getTime(); ts < rangeEnd.getTime(); ts += dayMs) {
const d = new Date(ts);
const dow = d.getDay();
for (const p of pausas) {
const wd = normalizeWeekday(p?.weekday ?? p?.dia_semana)
if (wd === null || wd !== dow) continue
for (const p of pausas) {
const wd = normalizeWeekday(p?.weekday ?? p?.dia_semana);
if (wd === null || wd !== dow) continue;
const start = asTime(p?.start ?? p?.inicio ?? p?.from)
const end = asTime(p?.end ?? p?.fim ?? p?.to)
if (!start || !end) continue
const start = asTime(p?.start ?? p?.inicio ?? p?.from);
const end = asTime(p?.end ?? p?.fim ?? p?.to);
if (!start || !end) continue;
out.push({
id: `break-${ts}-${start}-${end}`,
start: combineDateTimeISO(d, start),
end: combineDateTimeISO(d, end),
display: 'background',
overlap: false,
extendedProps: { kind: 'break', label: p?.label ?? 'Pausa' }
})
out.push({
id: `break-${ts}-${start}-${end}`,
start: combineDateTimeISO(d, start),
end: combineDateTimeISO(d, end),
display: 'background',
overlap: false,
extendedProps: { kind: 'break', label: p?.label ?? 'Pausa' }
});
}
}
}
return out
return out;
}
// ─────────────────────────────────────────────────────────────────────────────
// minutesToDuration / tituloFallback
// ─────────────────────────────────────────────────────────────────────────────
export function minutesToDuration (min) {
const h = Math.floor(min / 60)
const m = min % 60
return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:00`
export function minutesToDuration(min) {
const h = Math.floor(min / 60);
const m = min % 60;
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:00`;
}
export function tituloFallback (tipo) {
const t = String(tipo || '').toLowerCase()
if (t.includes('sess')) return 'Sessão'
if (t.includes('block') || t.includes('bloq')) return 'Bloqueio'
if (t.includes('pessoal')) return 'Pessoal'
if (t.includes('clin')) return 'Clínica'
return 'Compromisso'
export function tituloFallback(tipo) {
const t = String(tipo || '').toLowerCase();
if (t.includes('sess')) return 'Sessão';
if (t.includes('block') || t.includes('bloq')) return 'Bloqueio';
if (t.includes('pessoal')) return 'Pessoal';
if (t.includes('clin')) return 'Clínica';
return 'Compromisso';
}
// ─────────────────────────────────────────────────────────────────────────────
// helpers de status
// ─────────────────────────────────────────────────────────────────────────────
function _statusBgColor (status) {
const map = {
agendado: '#3b82f6', // azul
realizado: '#22c55e', // verde
faltou: '#f97316', // laranja
cancelado: '#ef4444', // vermelho
remarcar: '#a855f7', // roxo
bloqueado: '#6b7280', // cinza
}
return map[status] ?? null
function _statusBgColor(status) {
const map = {
agendado: '#3b82f6', // azul
realizado: '#22c55e', // verde
faltou: '#f97316', // laranja
cancelado: '#ef4444', // vermelho
remarcar: '#a855f7', // roxo
bloqueado: '#6b7280' // cinza
};
return map[status] ?? null;
}
function _statusIcon (status, isOccurrence, hasSerie) {
if (status === 'realizado') return '✓ '
if (status === 'faltou') return '✗ '
if (status === 'cancelado') return '∅ '
if (status === 'remarcar') return '↺ '
if (status === 'bloqueado') return '⊘ '
if (hasSerie || isOccurrence) return '↻ '
return ''
function _statusIcon(status, isOccurrence, hasSerie) {
if (status === 'realizado') return '✓ ';
if (status === 'faltou') return '✗ ';
if (status === 'cancelado') return '∅ ';
if (status === 'remarcar') return '↺ ';
if (status === 'bloqueado') return '⊘ ';
if (hasSerie || isOccurrence) return '↻ ';
return '';
}
// ─────────────────────────────────────────────────────────────────────────────
// helpers internos — originais preservados
// ─────────────────────────────────────────────────────────────────────────────
function normalizeId (v) {
if (v === null || v === undefined) return null
const s = String(v).trim()
return s ? s : null
function normalizeId(v) {
if (v === null || v === undefined) return null;
const s = String(v).trim();
return s ? s : null;
}
function normalizeWeekday (value) {
if (value === null || value === undefined) return null
const n = Number(value)
if (Number.isNaN(n)) return null
if (n >= 0 && n <= 6) return n
if (n >= 1 && n <= 7) return n === 7 ? 0 : n
return null
function normalizeWeekday(value) {
if (value === null || value === undefined) return null;
const n = Number(value);
if (Number.isNaN(n)) return null;
if (n >= 0 && n <= 6) return n;
if (n >= 1 && n <= 7) return n === 7 ? 0 : n;
return null;
}
function asTime (v) {
if (!v || typeof v !== 'string') return null
const s = v.trim()
if (/^\d{2}:\d{2}$/.test(s)) return `${s}:00`
if (/^\d{2}:\d{2}:\d{2}$/.test(s)) return s
return null
function asTime(v) {
if (!v || typeof v !== 'string') return null;
const s = v.trim();
if (/^\d{2}:\d{2}$/.test(s)) return `${s}:00`;
if (/^\d{2}:\d{2}:\d{2}$/.test(s)) return s;
return null;
}
function startOfDay (d) {
const x = new Date(d)
x.setHours(0, 0, 0, 0)
return x
function startOfDay(d) {
const x = new Date(d);
x.setHours(0, 0, 0, 0);
return x;
}
function combineDateTimeISO (date, timeHHMMSS) {
const yyyy = date.getFullYear()
const mm = String(date.getMonth() + 1).padStart(2, '0')
const dd = String(date.getDate()).padStart(2, '0')
return `${yyyy}-${mm}-${dd}T${timeHHMMSS}`
}
function combineDateTimeISO(date, timeHHMMSS) {
const yyyy = date.getFullYear();
const mm = String(date.getMonth() + 1).padStart(2, '0');
const dd = String(date.getDate()).padStart(2, '0');
return `${yyyy}-${mm}-${dd}T${timeHHMMSS}`;
}