Finalized Filter List Design and Table Column Spacing

This commit is contained in:
2025-11-28 01:44:54 -07:00
parent aacb15f6ab
commit 736e659e40

View File

@@ -55,7 +55,7 @@ const gradientButtonSx = {
}, },
}; };
const AUTO_SIZE_COLUMNS = ["name", "type", "deviceCount", "site", "lastEditedBy", "lastEdited"]; const AUTO_SIZE_COLUMNS = ["type", "deviceCount", "site", "lastEditedBy", "lastEdited"];
const FILTER_TYPE_META = { const FILTER_TYPE_META = {
global: { global: {
label: "Global", label: "Global",
@@ -93,9 +93,9 @@ const SAMPLE_ROWS = [
]; ];
function formatTimestamp(ts) { function formatTimestamp(ts) {
if (!ts) return ""; if (!ts) return "";
const date = new Date(ts); const date = new Date(ts);
if (Number.isNaN(date.getTime())) return ""; if (Number.isNaN(date.getTime())) return "";
return date.toLocaleString(); return date.toLocaleString();
} }
@@ -165,6 +165,24 @@ export default function DeviceFilterList({ onCreateFilter, onEditFilter, refresh
const [rows, setRows] = useState([]); const [rows, setRows] = useState([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState(null); const [error, setError] = useState(null);
const autoSizeColumns = useCallback(
(api) => {
if (!api || loading || !rows.length) return;
const doSize = () => {
try {
api.autoSizeColumns(AUTO_SIZE_COLUMNS, true);
} catch {
/* ignore autosize failures */
}
};
if (typeof requestAnimationFrame === "function") {
requestAnimationFrame(doSize);
} else {
setTimeout(doSize, 0);
}
},
[loading, rows.length]
);
const loadFilters = useCallback(async () => { const loadFilters = useCallback(async () => {
setLoading(true); setLoading(true);
@@ -174,7 +192,7 @@ export default function DeviceFilterList({ onCreateFilter, onEditFilter, refresh
if (resp.status === 404) { if (resp.status === 404) {
// Endpoint not available yet; surface sample data without hard failure // Endpoint not available yet; surface sample data without hard failure
setRows(normalizeFilters(SAMPLE_ROWS)); setRows(normalizeFilters(SAMPLE_ROWS));
setError("Device filter API not found (404) showing sample filters."); setError("Device filter API not found (404) showing sample filters.");
} else { } else {
if (!resp.ok) { if (!resp.ok) {
throw new Error(`Failed to load filters (${resp.status})`); throw new Error(`Failed to load filters (${resp.status})`);
@@ -206,37 +224,20 @@ export default function DeviceFilterList({ onCreateFilter, onEditFilter, refresh
const handleGridReady = useCallback((params) => { const handleGridReady = useCallback((params) => {
gridRef.current = params.api; gridRef.current = params.api;
requestAnimationFrame(() => { autoSizeColumns(params.api);
try { }, [autoSizeColumns]);
params.api.autoSizeColumns(AUTO_SIZE_COLUMNS, true);
} catch {
/* no-op */
}
});
}, []);
const autoSize = useCallback(() => {
if (!gridRef.current || loading || !rows.length) return;
const api = gridRef.current;
requestAnimationFrame(() => {
try {
api.autoSizeColumns(AUTO_SIZE_COLUMNS, true);
} catch {
/* ignore autosize failures */
}
});
}, [loading, rows.length]);
useEffect(() => { useEffect(() => {
autoSize(); autoSizeColumns(gridRef.current);
}, [rows, loading, autoSize]); }, [rows, loading, autoSizeColumns]);
const columnDefs = useMemo(() => { const columnDefs = useMemo(() => {
return [ return [
{ {
headerName: "Filter Name", headerName: "Filter Name",
field: "name", field: "name",
minWidth: 200, minWidth: 700,
flex: 1,
cellRenderer: (params) => { cellRenderer: (params) => {
const value = params.value || "Unnamed Filter"; const value = params.value || "Unnamed Filter";
return ( return (
@@ -244,13 +245,21 @@ export default function DeviceFilterList({ onCreateFilter, onEditFilter, refresh
onClick={() => onEditFilter?.(params.data)} onClick={() => onEditFilter?.(params.data)}
variant="text" variant="text"
size="small" size="small"
disableRipple
disableFocusRipple
disableTouchRipple
sx={{ sx={{
textTransform: "none", textTransform: "none",
color: "#7dd3fc", color: "#7dd3fc",
fontWeight: 600, fontWeight: 600,
px: 0, px: 0,
minWidth: "unset", minWidth: "unset",
"&:hover": { color: "#a5e7ff", textDecoration: "underline" }, backgroundColor: "transparent",
"&:hover": {
color: "#a5e7ff",
textDecoration: "underline",
backgroundColor: "transparent",
},
}} }}
> >
{value} {value}
@@ -262,7 +271,7 @@ export default function DeviceFilterList({ onCreateFilter, onEditFilter, refresh
{ {
headerName: "Type", headerName: "Type",
field: "type", field: "type",
minWidth: 150, minWidth: 140,
cellRenderer: (params) => { cellRenderer: (params) => {
const type = String(params.value || "").toLowerCase() === "site" ? "Site" : "Global"; const type = String(params.value || "").toLowerCase() === "site" ? "Site" : "Global";
return <FilterTypePill type={type} />; return <FilterTypePill type={type} />;
@@ -272,36 +281,35 @@ export default function DeviceFilterList({ onCreateFilter, onEditFilter, refresh
{ {
headerName: "Devices Targeted", headerName: "Devices Targeted",
field: "deviceCount", field: "deviceCount",
width: 160, minWidth: 165,
valueFormatter: (params) => { valueFormatter: (params) => {
if (typeof params.value === "number" && Number.isFinite(params.value)) { if (typeof params.value === "number" && Number.isFinite(params.value)) {
return params.value.toLocaleString(); return params.value.toLocaleString();
} }
return ""; return "";
}, },
cellClass: "auto-col-tight", cellClass: "auto-col-tight",
}, },
{ {
headerName: "Site", headerName: "Sites Targeted",
field: "site", field: "site",
minWidth: 140, minWidth: 150,
cellRenderer: (params) => { cellRenderer: (params) => {
const value = params.value; const value = params.value;
return value ? value : ""; return value ? value : "";
}, },
cellClass: "auto-col-tight", cellClass: "auto-col-tight",
}, },
{ {
headerName: "Last Edited By", headerName: "Last Edited By",
field: "lastEditedBy", field: "lastEditedBy",
minWidth: 160, minWidth: 150,
cellClass: "auto-col-tight", cellClass: "auto-col-tight",
}, },
{ {
headerName: "Last Edited", headerName: "Last Edited",
field: "lastEdited", field: "lastEdited",
minWidth: 180, minWidth: 180,
flex: 1,
valueFormatter: (params) => formatTimestamp(params.value), valueFormatter: (params) => formatTimestamp(params.value),
cellClass: "auto-col-tight", cellClass: "auto-col-tight",
}, },
@@ -340,7 +348,7 @@ export default function DeviceFilterList({ onCreateFilter, onEditFilter, refresh
gap: 2, gap: 2,
}} }}
> >
<Box sx={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column", gap: 2 }}> <Box sx={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column", gap: 1.5 }}>
<Box <Box
className={gridTheme.themeName} className={gridTheme.themeName}
sx={{ sx={{