import { useEffect, useState } from 'react'; import { Plus, Pencil, Trash2 } from 'lucide-react'; import { useUi } from '../../context/ui-context.jsx'; import { useDraftForm } from '../../hooks/useDraftForm.js'; import { useGuardedClose } from '../../components/NavigationGuard.jsx'; import { DraftNotice } from '../../components/DraftNotice.jsx'; import { Button } from '../../components/Button.jsx'; import { Field, FieldRow } from '../../components/Field.jsx'; import { ComboBox } from '../../components/ComboBox.jsx'; import { DetailPopup } from '../../components/DetailPopup.jsx'; import { InfoBar } from '../../components/InfoBar.jsx'; import { ListView } from '../../components/ListView.jsx'; import { Badge } from '../../components/Badge.jsx'; import * as api from '../../services/clinic.js'; import { listUsers } from '../../services/users.js'; import { WEEKDAYS } from './clinicUtils.js'; // Configuración de la agenda (admin/superadmin): Personal, Tipos de cita y Horario de atención. // Cada sección es un listado; "Agregar"/clic en fila abre un popup en lectura y "Editar" habilita. const staffForm = (s) => s ? { userId: s.user_id, scope: s.scope, isDoctor: !!s.is_doctor, specialty: s.specialty || '', color: s.color || '#0078d4', isActive: !!s.is_active, editVersion: s.edit_version } : { userId: '', scope: 'own', isDoctor: true, specialty: '', color: '#0078d4', isActive: true }; const typeForm = (x) => x ? { name: x.name, durationMinutes: x.duration_minutes, color: x.color, isActive: !!x.is_active, editVersion: x.edit_version } : { name: '', durationMinutes: 30, color: '#0078d4', isActive: true }; const scheduleForm = (rows) => ({ rows: rows.map((s) => ({ weekday: s.weekday, startsTime: String(s.starts_time).slice(0, 5), endsTime: String(s.ends_time).slice(0, 5) })) }); const swatch = (color) => ; export function ClinicConfigTab({ onChanged }) { const { t, pushToast } = useUi(); const guardedClose = useGuardedClose(); const [staff, setStaff] = useState([]), [scheduleDoctors, setScheduleDoctors] = useState([]), [users, setUsers] = useState([]), [types, setTypes] = useState([]); const [popup, setPopup] = useState(null), [editing, setEditing] = useState(false), [error, setError] = useState(''), [loadError, setLoadError] = useState(''); async function reload() { try { const [s, u, ty] = await Promise.all([api.clinicStaff(), listUsers(), api.clinicTypes()]); // Horario por médico (todos los médicos del personal, aunque quien configura no sea personal). const docs = s.filter((x) => x.is_doctor); const segs = await Promise.all(docs.map((d) => api.clinicSchedules(d.user_id))); setStaff(s); setUsers(u); setTypes(ty); setScheduleDoctors(docs.map((d, i) => ({ id: d.user_id, name: d.name, color: d.color, schedules: segs[i] }))); setLoadError(''); } catch (e) { setLoadError(e.message); } } useEffect(() => { void reload(); }, []); const initial = !popup ? {} : popup.kind === 'staff' ? staffForm(popup.item) : popup.kind === 'type' ? typeForm(popup.item) : scheduleForm(popup.rows || []); const draft = useDraftForm({ formId: popup?.kind === 'staff' ? 'clinic-staff' : popup?.kind === 'type' ? 'clinic-type' : 'clinic-schedule', entityId: popup?.kind === 'staff' ? popup.item?.user_id : popup?.kind === 'type' ? popup.item?.id : popup?.doctor?.id, initial, label: t(popup?.kind === 'staff' ? 'clinic_staff' : popup?.kind === 'type' ? 'clinic_types' : 'clinic_schedules'), enabled: !!popup && editing, onDiscard: () => { if (popup?.item || popup?.kind === 'schedule') setEditing(false); else setPopup(null); } }); const v = draft.values; async function open(kind, item = null, extra = {}) { setError(''); if (kind === 'schedule') extra.rows = extra.doctor.schedules; setPopup({ kind, item, ...extra }); setEditing(!item && kind !== 'schedule'); } function close() { void guardedClose(editing ? draft : null, () => { setPopup(null); setEditing(false); }); } const cancelEdit = () => draft.discard(); async function save() { try { if (popup.kind === 'staff') await api.clinicSaveStaff(v.userId, v); if (popup.kind === 'type') await api.clinicSaveType(v, popup.item?.id); if (popup.kind === 'schedule') await api.clinicSaveSchedules(popup.doctor.id, { segments: rows }); } catch (e) { setError(e.message); return; } draft.clear(); setPopup(null); setEditing(false); pushToast({ tone: 'success', message: t('common_saved') }); // Guardado ya hecho: si falla el refresco, se avisa fuera del popup (que ya está cerrado). try { await reload(); await onChanged(); } catch (e) { setLoadError(e.message); } } const staffIds = new Set(staff.map((s) => s.user_id)); const scheduleText = (d) => (d.schedules || []).map((s) => `${t(`calendar_${WEEKDAYS[s.weekday]}`)} ${String(s.starts_time).slice(0, 5)}–${String(s.ends_time).slice(0, 5)}`).join(' · ') || '—'; const rows = (v?.rows || []).map((r) => ({ weekday: Number(r.weekday), startsTime: r.startsTime ?? String(r.starts_time || '').slice(0, 5), endsTime: r.endsTime ?? String(r.ends_time || '').slice(0, 5) })); const setRows = (next) => draft.setField('rows', next); const canSave = popup?.kind === 'staff' ? !!v.userId : popup?.kind === 'type' ? !!v.name?.trim() && Number(v.durationMinutes) > 0 : true; const detail = !popup?.item ? [] : popup.kind === 'staff' ? [ [t('clinic_user'), popup.item.name], [t('clinic_scope'), t(`clinic_scope_${popup.item.scope}`)], [t('clinic_is_doctor'), t(popup.item.is_doctor ? 'common_yes' : 'common_no')], [t('clinic_specialty'), popup.item.specialty || '—'], [t('clinic_color'), popup.item.color ? <>{swatch(popup.item.color)} {popup.item.color}> : '—'], [t('common_status'), t(popup.item.is_active ? 'common_active' : 'common_inactive')] ] : popup.kind === 'type' ? [ [t('clinic_name'), popup.item.name], [t('clinic_duration'), popup.item.duration_minutes], [t('clinic_color'), <>{swatch(popup.item.color)} {popup.item.color}>], [t('common_status'), t(popup.item.is_active ? 'common_active' : 'common_inactive')] ] : []; return
{t(`calendar_${WEEKDAYS[s.weekday]}`)}: {String(s.starts_time).slice(0, 5)}–{String(s.ends_time).slice(0, 5)}
) :—
}{label}: {val}
)}