import { SearchableSelect } from '../components/SearchableSelect'; import { useEffect, useMemo, useState } from 'react'; import type { FormEvent } from 'react'; import { Alert, LoadingBlock, errorMessage } from '../components/Feedback'; import { Icon } from '../components/Icon'; import { FindingCatalogProposalsPanel } from '../features/inspections/FindingCatalogProposalsPanel'; import { FindingCatalogTypeApplicabilityPanel } from '../features/inspections/FindingCatalogTypeApplicabilityPanel'; import { createFindingCatalogItem, createFindingCategory, getFindingCatalogAdmin, updateFindingCatalogItem, updateFindingCategory, } from '../lib/api'; import type { FindingAdminCatalog, FindingAdminCatalogItem, FindingAdminCategory, } from '../lib/api'; type CategoryEditor = FindingAdminCategory | 'new' | null; type ItemEditor = FindingAdminCatalogItem | 'new' | null; const EMPTY_CATALOG: FindingAdminCatalog = { categories: [], items: [] }; export function FindingCatalogPage() { const [catalog, setCatalog] = useState(EMPTY_CATALOG); const [selectedCategoryId, setSelectedCategoryId] = useState(''); const [search, setSearch] = useState(''); const [showInactive, setShowInactive] = useState(false); const [categoryEditor, setCategoryEditor] = useState(null); const [categoryCode, setCategoryCode] = useState(''); const [categoryName, setCategoryName] = useState(''); const [categoryOrder, setCategoryOrder] = useState(0); const [categoryActive, setCategoryActive] = useState(true); const [itemEditor, setItemEditor] = useState(null); const [itemCategoryId, setItemCategoryId] = useState(''); const [itemCode, setItemCode] = useState(''); const [itemNumber, setItemNumber] = useState(1); const [itemTitle, setItemTitle] = useState(''); const [legalBasis, setLegalBasis] = useState(''); const [glossary, setGlossary] = useState(''); const [importNote, setImportNote] = useState(''); const [suggestedSeverity, setSuggestedSeverity] = useState(''); const [itemActive, setItemActive] = useState(true); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const load = async (preferCategoryId?: string) => { const loaded = await getFindingCatalogAdmin(); setCatalog(loaded); setSelectedCategoryId((current) => { const preferred = preferCategoryId ?? current; return loaded.categories.some((category) => category.id === preferred) ? preferred : loaded.categories[0]?.id ?? ''; }); }; useEffect(() => { load().catch((requestError) => setError(errorMessage(requestError))).finally(() => setLoading(false)); }, []); const categories = useMemo(() => catalog.categories.filter((category) => showInactive || category.isActive, ), [catalog.categories, showInactive]); const items = useMemo(() => { const needle = search.trim().toLocaleLowerCase(); return catalog.items.filter((item) => item.categoryId === selectedCategoryId && (showInactive || item.isActive) && (!needle || [item.code, item.title, item.legalBasis, item.glossary] .some((value) => value?.toLocaleLowerCase().includes(needle))), ); }, [catalog.items, search, selectedCategoryId, showInactive]); const selectedCategory = catalog.categories.find((category) => category.id === selectedCategoryId) ?? null; const openCategory = (value: FindingAdminCategory | 'new') => { setCategoryEditor(value); if (value === 'new') { setCategoryCode(''); setCategoryName(''); setCategoryOrder(catalog.categories.length ? Math.max(...catalog.categories.map((item) => item.sortOrder)) + 10 : 10); setCategoryActive(true); } else { setCategoryCode(value.code); setCategoryName(value.name); setCategoryOrder(value.sortOrder); setCategoryActive(value.isActive); } setError(''); setSuccess(''); }; const saveCategory = async (event: FormEvent) => { event.preventDefault(); setSaving(true); setError(''); setSuccess(''); try { const saved = categoryEditor === 'new' ? await createFindingCategory({ code: categoryCode, name: categoryName, sortOrder: categoryOrder }) : await updateFindingCategory(categoryEditor!.id, { name: categoryName, sortOrder: categoryOrder, isActive: categoryActive, }); await load(saved.id); setCategoryEditor(null); setSuccess(categoryEditor === 'new' ? 'Categoría creada correctamente' : 'Categoría actualizada'); } catch (requestError) { setError(errorMessage(requestError)); } finally { setSaving(false); } }; const openItem = (value: FindingAdminCatalogItem | 'new') => { setItemEditor(value); if (value === 'new') { const nextNumber = items.length ? Math.max(...items.map((item) => item.sourceNumber)) + 1 : 1; setItemCategoryId(selectedCategoryId); setItemCode(''); setItemNumber(nextNumber); setItemTitle(''); setLegalBasis(''); setGlossary(''); setImportNote(''); setSuggestedSeverity(''); setItemActive(true); } else { setItemCategoryId(value.categoryId); setItemCode(value.code); setItemNumber(value.sourceNumber); setItemTitle(value.title); setLegalBasis(value.legalBasis ?? ''); setGlossary(value.glossary ?? ''); setImportNote(value.importNote ?? ''); setSuggestedSeverity(value.suggestedSeverity ?? ''); setItemActive(value.isActive); } setError(''); setSuccess(''); }; const saveItem = async (event: FormEvent) => { event.preventDefault(); setSaving(true); setError(''); setSuccess(''); const input = { categoryId: itemCategoryId, sourceNumber: itemNumber, title: itemTitle, legalBasis: legalBasis || null, glossary: glossary || null, importNote: importNote || null, suggestedSeverity: suggestedSeverity === '' ? null : suggestedSeverity, }; try { const saved = itemEditor === 'new' ? await createFindingCatalogItem({ ...input, code: itemCode }) : await updateFindingCatalogItem(itemEditor!.id, { ...input, isActive: itemActive }); await load(saved.categoryId); setItemEditor(null); setSuccess(itemEditor === 'new' ? 'Tipo de hallazgo creado correctamente' : `Tipo actualizado: ahora está en revisión ${saved.revision}`); } catch (requestError) { setError(errorMessage(requestError)); } finally { setSaving(false); } }; if (loading) return ; return
CONFIGURACIÓN PARA LA APK

Catálogo de hallazgos

Administrá las opciones que el inspector podrá seleccionar durante una inspección.

Historial protegido. Los cambios crean una nueva revisión y no modifican los hallazgos ya registrados en actas anteriores.
{error && {error}}{success && {success}} {catalog.categories.length === 0 ?
No hay categorías configuradas

Creá la primera categoría para comenzar.

:
{selectedCategory &&
{selectedCategory.isActive ? 'CATEGORÍA ACTIVA' : 'CATEGORÍA INACTIVA'}

{selectedCategory.name}

{selectedCategory.code} · orden {selectedCategory.sortOrder}
}
{items.length} resultado{items.length === 1 ? '' : 's'}
{items.length === 0 ?
No hay tipos de hallazgo para los filtros seleccionados.
:
{items.map((item) => )}
}
} {categoryEditor &&
CATÁLOGO

{categoryEditor === 'new' ? 'Nueva categoría' : 'Editar categoría'}

{categoryEditor !== 'new' && }
} {itemEditor &&
TIPO DE HALLAZGO

{itemEditor === 'new' ? 'Nuevo tipo de hallazgo' : 'Editar tipo de hallazgo'}

{itemEditor !== 'new' &&

Revisión actual: {itemEditor.revision}

}