167 lines
4.5 KiB
TypeScript
167 lines
4.5 KiB
TypeScript
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,
|
|
};
|
|
} |