feat(salas): acesso operacional do funcionário à sala + bloqueio p/ manutenção

Funcionário/membro com vínculo na clínica dona da sala passa a ter a sala como
workspace de acesso OPERACIONAL (papel_sala='operador'): vê a agenda da sala e
bloqueia horário para manutenção, sem locação nem financeiro. O Painel da Sala
detecta o modo operador e esconde recebíveis/relatório/baixa, mostrando só a
agenda + ação de bloqueio.

Bloqueio de manutenção: reserva tipo='manutencao' (coluna nova, default
'locacao'), valor 0, nasce 'confirmada' (sem aprovação) e NÃO gera recebível.
Reaproveita o anti-conflito de horário da sala (409 em sobreposição) e o
cancelamento por solicitante/unidade. A agenda renderiza o bloqueio em cinza.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-06-23 18:23:36 +02:00
parent 718a7886e1
commit 983582f066
3 changed files with 181 additions and 17 deletions
+17
View File
@@ -1011,6 +1011,23 @@ export const HybridBackend = {
return await res.json().catch(() => ({ feitas: [], recebidas: [] }));
},
// Agenda de uma sala (reservas + bloqueios de manutenção) — usado pelo operador da unidade.
getSalaReservas: async (salaId: string): Promise<any[]> => {
const res = await apiFetch(`${API_URL}/salas/${salaId}/reservas`);
return await res.json().catch(() => []);
},
// Bloqueio de sala para manutenção (sem cobrança; tira a sala da agenda no intervalo).
criarManutencaoSala: async (salaId: string, body: { inicio: string; fim: string; motivo?: string }) => {
const res = await apiFetch(`${API_URL}/salas/${salaId}/manutencao`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)});
const data = await res.json().catch(() => ({}));
if (!res.ok) throw new Error(data?.error || 'Falha ao bloquear a sala.');
return data;
},
saveFinanceiro: async (item: Partial<FinanceiroItem>) => {
const clinicaId = HybridBackend.getActiveWorkspace()?.id || '';
const data = enforceUppercase(item);