mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-12-15 16:55:48 -07:00
Finalized Filter List Design and Table Column Spacing
This commit is contained in:
@@ -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={{
|
||||||
|
|||||||
Reference in New Issue
Block a user