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 && (