Files
litoral-central-frontend/src/features/maincharts/hooks/useChartWorkspacePersistence.ts
T

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,
};
}