diff --git a/web-v2/src/features/inspections/FindingCatalogTypeApplicabilityPanel.tsx b/web-v2/src/features/inspections/FindingCatalogTypeApplicabilityPanel.tsx index 29f3871..e1b804e 100644 --- a/web-v2/src/features/inspections/FindingCatalogTypeApplicabilityPanel.tsx +++ b/web-v2/src/features/inspections/FindingCatalogTypeApplicabilityPanel.tsx @@ -1,5 +1,6 @@ import { SearchableSelect } from '../../components/SearchableSelect'; import { useEffect, useMemo, useState } from 'react'; +import { useSearchParams } from 'react-router'; import { Alert, LoadingBlock, errorMessage } from '../../components/Feedback'; import { Icon } from '../../components/Icon'; import { getFindingCatalogAdmin } from '../../lib/api'; @@ -12,19 +13,25 @@ import type { InventoryFamily } from '../../lib/inventoryStructureApi'; const EMPTY_CATALOG: FindingAdminCatalog = { categories: [], items: [] }; +type ViewMode = 'ASSOCIATED' | 'ALL'; + export function FindingCatalogTypeApplicabilityPanel() { + const [searchParams] = useSearchParams(); + const requestedFamilyId = searchParams.get('familyId') ?? ''; const [families, setFamilies] = useState([]); const [catalog, setCatalog] = useState(EMPTY_CATALOG); const [familyId, setFamilyId] = useState(''); const [enabled, setEnabled] = useState>(new Set()); - const [reason, setReason] = useState('Actualización de aplicabilidad por clasificación de Inventario'); + const [reason, setReason] = useState('Actualización de Hallazgos asociados a la clasificación de Inventario'); const [search, setSearch] = useState(''); + const [viewMode, setViewMode] = useState('ASSOCIATED'); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const selectedFamily = families.find((family) => family.id === familyId) ?? null; + const savedIds = useMemo(() => new Set(selectedFamily?.findingItemIds ?? []), [selectedFamily]); useEffect(() => { Promise.all([listInventoryFamiliesAdmin(), getFindingCatalogAdmin()]) @@ -32,15 +39,19 @@ export function FindingCatalogTypeApplicabilityPanel() { const activeFamilies = loadedFamilies.filter((family) => family.isActive !== false); setFamilies(activeFamilies); setCatalog(loadedCatalog); - setFamilyId(activeFamilies[0]?.id ?? ''); + setFamilyId(activeFamilies.some((family) => family.id === requestedFamilyId) + ? requestedFamilyId + : activeFamilies[0]?.id ?? ''); }) .catch((requestError) => setError(errorMessage(requestError))) .finally(() => setLoading(false)); - }, []); + }, [requestedFamilyId]); useEffect(() => { const selected = families.find((family) => family.id === familyId); setEnabled(new Set(selected?.findingItemIds ?? [])); + setViewMode('ASSOCIATED'); + setSearch(''); setSuccess(''); setError(''); }, [familyId, families]); @@ -53,7 +64,7 @@ export function FindingCatalogTypeApplicabilityPanel() { catalog.categories.map((category) => [category.id, category.name]), ), [catalog.categories]); - const visible = useMemo(() => { + const available = useMemo(() => { const needle = search.trim().toLocaleLowerCase('es-AR'); return catalog.items.filter((item) => item.isActive @@ -63,12 +74,23 @@ export function FindingCatalogTypeApplicabilityPanel() { ); }, [catalog.items, search, activeCategoryIds, categoryName]); + const visible = useMemo(() => viewMode === 'ASSOCIATED' + ? available.filter((item) => savedIds.has(item.id)) + : available, + [available, savedIds, viewMode]); + const toggle = (id: string) => setEnabled((current) => { const next = new Set(current); if (next.has(id)) next.delete(id); else next.add(id); return next; }); + const selectVisible = () => setEnabled((current) => { + const next = new Set(current); + visible.forEach((item) => next.add(item.id)); + return next; + }); + const save = async () => { if (!selectedFamily) return; setSaving(true); setError(''); setSuccess(''); @@ -82,6 +104,7 @@ export function FindingCatalogTypeApplicabilityPanel() { setFamilies((current) => current.map((family) => family.id === selectedFamily.id ? { ...family, findingItemIds: itemIds, findingCount: itemIds.length } : family)); + setViewMode('ASSOCIATED'); setSuccess(`Hallazgos guardados para ${selectedFamily.name}.`); } catch (requestError) { setError(errorMessage(requestError)); @@ -98,9 +121,9 @@ export function FindingCatalogTypeApplicabilityPanel() {
APLICABILIDAD POR INSTALACIÓN / SUBINSTALACIÓN

Qué Hallazgos verá el inspector

-

Los Hallazgos se vinculan a la clasificación concreta del elemento, no a una “función”. La opción OTROS permanece siempre disponible en la APK.

+

Al elegir una clasificación se muestran primero sólo sus Hallazgos asociados. Usá “Todos para vincular” únicamente cuando quieras agregar o quitar asociaciones.

- {enabled.size} vinculados + {savedIds.size} asociados {error && {error}}{success && {success}}
@@ -116,17 +139,30 @@ export function FindingCatalogTypeApplicabilityPanel() {
{selectedFamily &&
-

{selectedFamily.level === 'INSTALLATION' ? 'Instalación' : 'Subinstalación'}: {selectedFamily.parentFamilyName ? `${selectedFamily.parentFamilyName} → ` : ''}{selectedFamily.name}. Actualmente tiene {selectedFamily.findingCount ?? enabled.size} Hallazgo{(selectedFamily.findingCount ?? enabled.size) === 1 ? '' : 's'} asociado{(selectedFamily.findingCount ?? enabled.size) === 1 ? '' : 's'}.

+

{selectedFamily.level === 'INSTALLATION' ? 'Instalación' : 'Subinstalación'}: {selectedFamily.parentFamilyName ? `${selectedFamily.parentFamilyName} → ` : ''}{selectedFamily.name}. Tiene {savedIds.size} Hallazgo{savedIds.size === 1 ? '' : 's'} asociado{savedIds.size === 1 ? '' : 's'}.

} -
- - + +
+ +
-
{visible.map((item) => )}
-