feat(charts): add persistent workspace chart management
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type {
|
||||
WorkspaceChartInterval,
|
||||
WorkspaceChartMode,
|
||||
WorkspaceChartTimeRange,
|
||||
} from "../../../components/charts/WorkspaceChart";
|
||||
|
||||
const API_BASE_URL = "http://localhost:18450";
|
||||
const SAVE_DEBOUNCE_MS = 800;
|
||||
|
||||
export type ChartLayoutMode =
|
||||
| "single"
|
||||
| "twoColumns"
|
||||
| "twoRows"
|
||||
| "fourGrid";
|
||||
|
||||
export type PersistedChartWorkspaceItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
mode: WorkspaceChartMode;
|
||||
selectedSensorKeys: string[];
|
||||
timeRange: WorkspaceChartTimeRange;
|
||||
interval: WorkspaceChartInterval;
|
||||
};
|
||||
|
||||
type ChartWorkspaceScope =
|
||||
| "GLOBAL"
|
||||
| "CLIMATE"
|
||||
| "IRRIGATION"
|
||||
| "METEO"
|
||||
| "LIGHTING"
|
||||
| "HYDRO"
|
||||
| "AEROPONICS";
|
||||
|
||||
type ChartWorkspaceResponse = {
|
||||
id: number;
|
||||
scope: ChartWorkspaceScope;
|
||||
layoutMode: ChartLayoutMode;
|
||||
chartsJson: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
type UseChartWorkspacePersistenceParams = {
|
||||
scope: ChartWorkspaceScope;
|
||||
layoutMode: ChartLayoutMode;
|
||||
charts: PersistedChartWorkspaceItem[];
|
||||
onLoaded: (workspace: {
|
||||
layoutMode: ChartLayoutMode;
|
||||
charts: PersistedChartWorkspaceItem[];
|
||||
}) => void;
|
||||
};
|
||||
|
||||
export function useChartWorkspacePersistence({
|
||||
scope,
|
||||
layoutMode,
|
||||
charts,
|
||||
onLoaded,
|
||||
}: UseChartWorkspacePersistenceParams) {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const saveTimeoutRef = useRef<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
async function loadWorkspace() {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/api/chart-workspaces/${scope}`,
|
||||
);
|
||||
|
||||
if (response.status === 404 || response.status === 500) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load workspace: ${response.status}`);
|
||||
}
|
||||
|
||||
const payload = (await response.json()) as ChartWorkspaceResponse;
|
||||
|
||||
if (cancelled) return;
|
||||
|
||||
onLoaded({
|
||||
layoutMode: payload.layoutMode,
|
||||
charts: JSON.parse(payload.chartsJson) as PersistedChartWorkspaceItem[],
|
||||
});
|
||||
|
||||
setError(null);
|
||||
} catch (error) {
|
||||
if (!cancelled) {
|
||||
console.error("Failed to load chart workspace", error);
|
||||
setError("Não foi possível carregar o workspace.");
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) {
|
||||
setLoaded(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadWorkspace();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [scope]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loaded) return;
|
||||
|
||||
if (saveTimeoutRef.current !== null) {
|
||||
window.clearTimeout(saveTimeoutRef.current);
|
||||
}
|
||||
|
||||
saveTimeoutRef.current = window.setTimeout(() => {
|
||||
async function saveWorkspace() {
|
||||
try {
|
||||
setSaving(true);
|
||||
|
||||
const response = await fetch(
|
||||
`${API_BASE_URL}/api/chart-workspaces/${scope}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
layoutMode,
|
||||
chartsJson: JSON.stringify(charts),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to save workspace: ${response.status}`);
|
||||
}
|
||||
|
||||
setError(null);
|
||||
} catch (error) {
|
||||
console.error("Failed to save chart workspace", error);
|
||||
setError("Não foi possível guardar o workspace.");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
saveWorkspace();
|
||||
}, SAVE_DEBOUNCE_MS);
|
||||
|
||||
return () => {
|
||||
if (saveTimeoutRef.current !== null) {
|
||||
window.clearTimeout(saveTimeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, [charts, layoutMode, loaded, scope]);
|
||||
|
||||
return {
|
||||
loaded,
|
||||
saving,
|
||||
error,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user