feat(agenda): importa cor do evento Google + UI do modal pequeno

- agendamentos.cor (TEXT); import grava cor do evento (colorId/agenda)
- frontend usa ag.cor (prioridade: status > cor própria > cor do dentista)
- StackModal: remove backdrop-blur (vidro); mobile vira bottom-sheet de altura auto, desktop painel pequeno com max-w; botão maximizar visível no mobile; padding responsivo

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-06-09 18:26:17 +02:00
parent 1cfd184886
commit c54532d5ea
3 changed files with 17 additions and 9 deletions
+8 -3
View File
@@ -2020,6 +2020,9 @@ app.post('/api/agenda/importar-google', authGuard, async (req, res) => {
const auth = await getClinicOAuth(clinicaId);
if (!auth) return res.status(400).json({ success: false, message: 'Nenhuma conta Google conectada nesta clínica.' });
const calendar = google.calendar({ version: 'v3', auth });
// Cor da agenda (fallback p/ eventos sem cor própria).
let calColor = null;
try { const cm = await calendar.calendarList.get({ calendarId: 'primary' }); calColor = cm.data.backgroundColor || null; } catch { /* escopo */ }
const now = Date.now();
const timeMin = new Date(now - 7 * 86400000).toISOString();
const timeMax = new Date(now + (Number(dias) || 60) * 86400000).toISOString();
@@ -2034,11 +2037,12 @@ app.post('/api/agenda/importar-google', authGuard, async (req, res) => {
const { rows: ex } = await pool.query('SELECT 1 FROM agendamentos WHERE google_event_id = $1 AND clinica_id = $2 LIMIT 1', [it.id, clinicaId]);
if (ex.length) { pulados++; continue; }
const end = it.end?.dateTime || it.end?.date || start;
const cor = (it.colorId && GOOGLE_EVENT_COLORS[it.colorId]) || calColor || null;
const id = `ag_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
await pool.query(
`INSERT INTO agendamentos (id, clinica_id, pacientenome, start_time, end_time, status, procedimento, google_event_id, created_by, created_by_nome, version)
VALUES ($1,$2,$3,$4,$5,'agendado',$6,$7,$8,$9,1)`,
[id, clinicaId, it.summary || 'Importado do Google', start, end, 'IMPORTADO (GOOGLE)', it.id, actor, actorNm]);
`INSERT INTO agendamentos (id, clinica_id, pacientenome, start_time, end_time, status, procedimento, google_event_id, cor, created_by, created_by_nome, version)
VALUES ($1,$2,$3,$4,$5,'agendado',$6,$7,$8,$9,$10,1)`,
[id, clinicaId, it.summary || 'Importado do Google', start, end, 'IMPORTADO (GOOGLE)', it.id, cor, actor, actorNm]);
await registrarAudit(pool, { clinicaId, entidade: 'agendamento', entidadeId: id, acao: 'criou', actorId: actor, actorNome: actorNm, detalhes: { origem: 'google', summary: it.summary } });
criados++;
}
@@ -3314,6 +3318,7 @@ async function runMigrations() {
`ALTER TABLE agendamentos ADD COLUMN IF NOT EXISTS canceled_at TIMESTAMPTZ`,
`ALTER TABLE agendamentos ADD COLUMN IF NOT EXISTS version INTEGER DEFAULT 1`,
`ALTER TABLE agendamentos ADD COLUMN IF NOT EXISTS google_event_id TEXT`,
`ALTER TABLE agendamentos ADD COLUMN IF NOT EXISTS cor TEXT`,
`CREATE TABLE IF NOT EXISTS audit_log (
id TEXT PRIMARY KEY,
clinica_id TEXT,