Skip to content

Commit ca96fcb

Browse files
committed
feat(web): add ChildEntityToolbar to BranchManager for consistent list controls
Replace SectionHeader-only layout with ChildEntityToolbar for consistent view/filter/sort controls across the tenant branch management panel. - Add filter dropdown (all/active/inactive branches). - Add sorting by name/code with ascending/descending toggle. - Add list/thumbnail view mode toggle. - Apply filtered and sorted branch list to card rendering.
1 parent a2cc100 commit ca96fcb

1 file changed

Lines changed: 53 additions & 9 deletions

File tree

src/apps/ums.web-app/src/presentation/identity/tenant/components/BranchManager.tsx

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { SectionHeader } from '@shared/components/SectionHeader';
2020
import { CodeBadge } from '@shared/components/CodeBadge';
2121
import { IconButton } from '@shared/components/Tooltip';
2222
import { RefreshCw, MapPin, EyeOff, ShieldCheck, Trash2, Pencil, Save, X } from 'lucide-react';
23+
import { ChildEntityToolbar } from '@shared/components/ChildEntityToolbar';
2324

2425
interface BranchManagerProps {
2526
tenantId: string;
@@ -47,6 +48,12 @@ export const BranchManager: React.FC<BranchManagerProps> = ({ tenantId }) => {
4748
const [geofencing, setGeofencing] = useState('');
4849
const [errorMsg, setErrorMsg] = useState('');
4950

51+
// ── View controls ─────────────────────────────────────────────────────────
52+
const [viewMode, setViewMode] = useState<'list' | 'thumbnail'>('list');
53+
const [activeFilter, setActiveFilter] = useState('all');
54+
const [sortBy, setSortBy] = useState('name');
55+
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc');
56+
5057
// ── Inline edit ───────────────────────────────────────────────────────────
5158
const edit = useInlineEdit<BranchDraft>(['name', 'code', 'geofencingMetadata']);
5259

@@ -88,16 +95,51 @@ export const BranchManager: React.FC<BranchManagerProps> = ({ tenantId }) => {
8895
}
8996
};
9097

98+
let filteredBranches = branches;
99+
if (activeFilter !== 'all') {
100+
filteredBranches = filteredBranches.filter((b) => (activeFilter === 'active' ? b.isActive : !b.isActive));
101+
}
102+
filteredBranches = [...filteredBranches].sort((a, b) => {
103+
let cmp = 0;
104+
if (sortBy === 'name') cmp = a.name.localeCompare(b.name);
105+
else if (sortBy === 'code') cmp = a.code.localeCompare(b.code);
106+
return sortOrder === 'asc' ? cmp : -cmp;
107+
});
108+
91109
return (
92110
<div className="space-y-4 select-none">
93-
<SectionHeader
94-
title={t.subLocations}
95-
subtitle={t.subLocationsSubtitle}
96-
actions={
97-
<IconButton tooltip={t.refreshBranches} onClick={() => refetch()} className={isFetching ? 'animate-spin' : ''}>
98-
<RefreshCw className="w-3.5 h-3.5" />
99-
</IconButton>
100-
}
111+
<div className="flex items-center justify-between">
112+
<SectionHeader
113+
title={t.subLocations}
114+
subtitle={t.subLocationsSubtitle}
115+
actions={
116+
<IconButton tooltip={t.refreshBranches} onClick={() => refetch()} className={isFetching ? 'animate-spin' : ''}>
117+
<RefreshCw className="w-3.5 h-3.5" />
118+
</IconButton>
119+
}
120+
/>
121+
</div>
122+
123+
<ChildEntityToolbar
124+
viewMode={viewMode}
125+
onViewModeChange={setViewMode}
126+
filterOptions={[
127+
{ label: 'Todas', value: 'all' },
128+
{ label: 'Activas', value: 'active' },
129+
{ label: 'Inactivas', value: 'inactive' },
130+
]}
131+
activeFilter={activeFilter}
132+
onFilterChange={setActiveFilter}
133+
sortOptions={[
134+
{ label: 'Nombre', value: 'name' },
135+
{ label: 'Código', value: 'code' },
136+
]}
137+
sortBy={sortBy}
138+
onSortByChange={setSortBy}
139+
sortOrder={sortOrder}
140+
onSortOrderToggle={() => setSortOrder((o) => (o === 'asc' ? 'desc' : 'asc'))}
141+
itemCount={branches.length}
142+
itemLabel="Sucursal"
101143
/>
102144

103145
<div className="grid grid-cols-1 gap-4">
@@ -143,8 +185,10 @@ export const BranchManager: React.FC<BranchManagerProps> = ({ tenantId }) => {
143185
</div>
144186
) : branches.length === 0 ? (
145187
<EmptyState icon={<MapPin className="w-6 h-6 text-m3-outline" />} message={t.noBranches} />
188+
) : filteredBranches.length === 0 ? (
189+
<EmptyState icon={<MapPin className="w-6 h-6 text-m3-outline" />} message="No hay sucursales que coincidan con el filtro" />
146190
) : (
147-
branches.map((b) =>
191+
filteredBranches.map((b) =>
148192
edit.isEditing(b.branchId) ? (
149193
/* ── Inline edit form ── */
150194
<div key={b.branchId} className="p-3.5 rounded-xl border border-m3-primary/40 bg-m3-surface-container/60 animate-fadeIn space-y-2.5">

0 commit comments

Comments
 (0)