From f39328576e0924f0af848f0f68c596ad278f6b56 Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Thu, 17 Sep 2026 02:01:08 -0300 Subject: [PATCH] fix(services): replace status filter with active/inactive tabs (SH-304) --- .../_components/services-registry-view.tsx | 112 +++++++++++----- .../use-services-registry-controller.ts | 28 ++-- .../settings/services-registry-view.test.tsx | 126 ++++++++++++++++++ 3 files changed, 224 insertions(+), 42 deletions(-) create mode 100644 src/test/app/(protected)/settings/services-registry-view.test.tsx diff --git a/src/app/(protected)/settings/_components/services-registry-view.tsx b/src/app/(protected)/settings/_components/services-registry-view.tsx index 286f904e..954e449e 100644 --- a/src/app/(protected)/settings/_components/services-registry-view.tsx +++ b/src/app/(protected)/settings/_components/services-registry-view.tsx @@ -3,20 +3,18 @@ import { Box, Button, CircularProgress, - FormControl, - InputLabel, - MenuItem, - Select, Stack, + Tab, Table, TableBody, TableCell, TableHead, TableRow, + Tabs, TextField, Typography, } from "@mui/material"; -import { Plus } from "lucide-react"; +import { Plus, Search } from "lucide-react"; import { SettingsNav } from "@/components/common/settings-nav"; import { useServicesRegistryController, @@ -30,9 +28,23 @@ import { ServiceIcon, } from "@/app/(protected)/settings/_components/services-registry-components"; +function statusTabLabel(label: string, count: number) { + return ( + + {label} + + {count} + + + ); +} + export function ServicesRegistryView() { const controller = useServicesRegistryController(); - const activeCount = controller.services.filter((service) => service.isActive).length; return ( @@ -49,7 +61,7 @@ export function ServicesRegistryView() { {controller.services.length} service{controller.services.length === 1 ? "" : "s"} ·{" "} - {activeCount} active + {controller.activeCount} active {controller.canManage && ( @@ -68,25 +80,44 @@ export function ServicesRegistryView() { size="small" value={controller.search} onChange={(event) => controller.setSearch(event.target.value)} - placeholder="Search services..." + placeholder="Search service or trade..." className="flex-1" - aria-label="Search services" + slotProps={{ htmlInput: { "aria-label": "Search services" } }} /> - - Status - - + controller.setStatus(value)} + aria-label="Service status" + className="mb-4" + sx={{ + minHeight: 36, + borderBottom: (theme) => `1px solid ${theme.palette.divider}`, + "& .MuiTab-root": { + minHeight: 36, + px: 2, + py: 0, + fontSize: 13, + fontWeight: 500, + textTransform: "none", + }, + "& .Mui-selected": { fontWeight: 600 }, + "& .MuiTabs-indicator": { height: 2 }, + }} + > + + + + {Boolean(controller.error) && ( {controller.error?.message || "Failed to load services"} )} @@ -104,6 +135,27 @@ export function ServicesRegistryView() { )} + {!controller.isLoading && ( + `1px solid ${theme.palette.divider}`, + pt: 1, + }} + > + + {controller.filteredServices.length} {controller.status} service + {controller.filteredServices.length === 1 ? "" : "s"} + {controller.search.trim() ? " · filtered" : ""} + + + {controller.activeCount} active · {controller.inactiveCount} inactive + + + )} + void; }) { - const statusLabel = status === "active" ? "active" : "inactive"; return ( - + + + - {search - ? "No services found" - : status === "all" - ? "No services configured yet" - : `No ${statusLabel} services configured yet`} + {search ? "No services found" : `No ${status} services configured yet`} {search ? "Try a different search, or add a new service." - : status === "all" - ? "Add a service to make it available for work orders." - : `There are no ${statusLabel} services matching this view.`} + : `There are no ${status} services matching this view.`} {onAdd !== undefined && (