Files
agenciapsilmno/src/composables/useFeriados.js
T
Leonardo 957e912a7f 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>
2026-05-06 09:11:55 -03:00

167 lines
6.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/composables/useFeriados.js
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
import { ref, computed } from 'vue';
import { supabase } from '@/lib/supabase/client';
import { getFeriadosNacionais } from '@/utils/feriadosBR';
import { useMelissaCacheStore, MELISSA_CACHE_TTL } from '@/stores/melissaCacheStore';
// opts.cache (opt-in): habilita stale-while-revalidate via melissaCacheStore.
// Default false pra preservar comportamento em páginas SaaS/admin que
// editam feriados (esperam invalidação imediata após criar/remover).
export function useFeriados(opts = {}) {
const useCache = !!opts.cache;
const cache = useCache ? useMelissaCacheStore() : null;
const ano = ref(new Date().getFullYear());
const loading = ref(false);
const municipais = ref([]); // linhas da tabela `feriados`
// ── Nacionais (algoritmo, sem DB) ─────────────────────────
const nacionais = computed(() => getFeriadosNacionais(ano.value).map((f) => ({ ...f, tipo: 'nacional' })));
// ── Todos juntos, ordenados por data ─────────────────────
const todos = computed(() => [...nacionais.value, ...municipais.value.map((f) => ({ ...f, tipo: f.tipo || 'municipal' }))].sort((a, b) => a.data.localeCompare(b.data)));
// ── Feriados de um mês (112) ─────────────────────────────
function doMes(mes) {
const m = String(mes).padStart(2, '0');
const prefix = `${ano.value}-${m}`;
return todos.value.filter((f) => f.data.startsWith(prefix));
}
// ── Próximos N dias ───────────────────────────────────────
function proximos(dias = 30) {
const hoje = new Date();
const limite = new Date(hoje);
limite.setDate(limite.getDate() + dias);
const hojeISO = toISO(hoje);
const limiteISO = toISO(limite);
return todos.value.filter((f) => f.data >= hojeISO && f.data <= limiteISO);
}
function toISO(d) {
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}
async function _doFetch(tenantId, cacheKey) {
const { data, error } = await supabase
.from('feriados')
.select('*')
.or(`tenant_id.eq.${tenantId},tenant_id.is.null`)
.gte('data', `${ano.value}-01-01`)
.lte('data', `${ano.value}-12-31`)
.order('data');
if (error) throw error;
const rows = data || [];
if (cache && cacheKey) cache.set('feriados', rows, cacheKey);
municipais.value = rows;
return rows;
}
// ── Load municipais do Supabase ───────────────────────────
async function load(tenantId, year) {
if (year) ano.value = year;
if (!tenantId) return;
if (cache) {
const cacheKey = `${tenantId}:${ano.value}`;
const cached = cache.get('feriados', cacheKey, MELISSA_CACHE_TTL.feriados);
if (cached) {
municipais.value = cached;
_doFetch(tenantId, cacheKey).catch((e) => {
// eslint-disable-next-line no-console
console.warn('[useFeriados] revalidate', e);
});
return;
}
loading.value = true;
try { await _doFetch(tenantId, cacheKey); }
finally { loading.value = false; }
return;
}
// Comportamento legado (sem cache) — páginas de admin que editam.
loading.value = true;
try {
const { data, error } = await supabase
.from('feriados')
.select('*')
.or(`tenant_id.eq.${tenantId},tenant_id.is.null`)
.gte('data', `${ano.value}-01-01`)
.lte('data', `${ano.value}-12-31`)
.order('data');
if (error) throw error;
municipais.value = data || [];
} finally {
loading.value = false;
}
}
// ── Criar feriado municipal ───────────────────────────────
async function criar(payload) {
const { data, error } = await supabase.from('feriados').insert(payload).select().single();
if (error) throw error;
municipais.value = [...municipais.value, data].sort((a, b) => a.data.localeCompare(b.data));
if (cache) cache.invalidate('feriados');
return data;
}
// ── Remover feriado municipal ─────────────────────────────
async function remover(id) {
const { error } = await supabase.from('feriados').delete().eq('id', id);
if (error) throw error;
municipais.value = municipais.value.filter((f) => f.id !== id);
if (cache) cache.invalidate('feriados');
}
// ── Verificar duplicata ───────────────────────────────────
function isDuplicata(data, nome) {
return todos.value.some((f) => f.data === data && f.nome.trim().toLowerCase() === nome.trim().toLowerCase());
}
// ── Converter para eventos do FullCalendar (background) ──
function toFcEvents(lista) {
return lista.map((f) => ({
id: `feriado_${f.id || f.data}_${f.nome}`,
title: f.nome,
start: f.data,
allDay: true,
display: 'background',
color: 'rgba(251, 191, 36, 0.25)',
extendedProps: { _feriado: true, tipo: f.tipo }
}));
}
const fcEvents = computed(() => toFcEvents(todos.value));
return {
ano,
loading,
nacionais,
municipais,
todos,
fcEvents,
load,
criar,
remover,
doMes,
proximos,
isDuplicata
};
}