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
{loadError && {loadError}}

{t('clinic_staff')}

s.user_id} rows={staff} onRowClick={(s) => void open('staff', s)} columns={[ { key: 'name', label: t('clinic_user'), render: (s) => {s.is_doctor ? swatch(s.color) : null}{s.name} }, { key: 'scope', label: t('clinic_scope'), render: (s) => t(`clinic_scope_${s.scope}`) }, { key: 'specialty', label: t('clinic_specialty'), render: (s) => s.specialty || '—' }, { key: 'is_active', label: t('common_status'), render: (s) => {t(s.is_active ? 'common_active' : 'common_inactive')} } ]} rowActions={(s) => [{ label: t('common_edit'), icon: Pencil, onClick: () => { void open('staff', s).then(() => setEditing(true)); } }]} />

{t('clinic_types')}

x.id} rows={types} onRowClick={(x) => void open('type', x)} columns={[ { key: 'name', label: t('clinic_name'), render: (x) => {swatch(x.color)}{x.name} }, { key: 'duration_minutes', label: t('clinic_duration') }, { key: 'is_active', label: t('common_status'), render: (x) => {t(x.is_active ? 'common_active' : 'common_inactive')} } ]} rowActions={(x) => [{ label: t('common_edit'), icon: Pencil, onClick: () => { void open('type', x).then(() => setEditing(true)); } }]} />

{t('clinic_schedules')}

d.id} rows={scheduleDoctors} onRowClick={(d) => void open('schedule', null, { doctor: d })} columns={[ { key: 'name', label: t('clinic_doctor'), render: (d) => {swatch(d.color)}{d.name} }, { key: 'schedules', label: t('clinic_schedules'), sortable: false, render: scheduleText } ]} /> {popup &&
{error && {error}} {editing && } {!editing ? <> {popup.kind === 'schedule' ?
{(popup.rows || []).length ? popup.rows.map((s) =>

{t(`calendar_${WEEKDAYS[s.weekday]}`)}: {String(s.starts_time).slice(0, 5)}–{String(s.ends_time).slice(0, 5)}

) :

—

}
:
{detail.map(([label, val]) =>

{label}: {val}

)}
}
: <> {popup.kind === 'staff' && <> {popup.item ? : draft.setField('userId', x)} options={[{ value: '', label: t('common_select') }, ...users.filter((u) => u.is_active && !staffIds.has(u.id)).map((u) => ({ value: u.id, label: u.name }))]} />} draft.setValues((old) => ({ ...old, scope: x, isDoctor: x === 'own' ? true : old.isDoctor }))} options={['all', 'own'].map((x) => ({ value: x, label: t(`clinic_scope_${x}`) }))} /> draft.setField('specialty', e.target.value)} /> draft.setField('color', e.target.value)} />
} {popup.kind === 'type' && <> draft.setField('name', e.target.value)} /> draft.setField('durationMinutes', Number(e.target.value))} /> draft.setField('color', e.target.value)} /> } {popup.kind === 'schedule' && <> {rows.map((s, i) =>
setRows(rows.map((r, j) => j === i ? { ...r, weekday: Number(x) } : r))} options={WEEKDAYS.map((x, n) => ({ value: n, label: t(`calendar_${x}`) }))} /> setRows(rows.map((r, j) => j === i ? { ...r, startsTime: e.target.value } : r))} /> setRows(rows.map((r, j) => j === i ? { ...r, endsTime: e.target.value } : r))} />
)}
}
}
}
; }