Compare commits
2 commits
184314d913
...
c096cc07fe
| Author | SHA1 | Date | |
|---|---|---|---|
| c096cc07fe | |||
| cce3f3c7f8 |
12 changed files with 35 additions and 102 deletions
10
server.mjs
10
server.mjs
|
|
@ -67,7 +67,7 @@ function resolvePath(url) {
|
||||||
const publicCandidate = fileIfReadable(resolve(publicRoot, `.${requested}`));
|
const publicCandidate = fileIfReadable(resolve(publicRoot, `.${requested}`));
|
||||||
if (publicCandidate && isInsideRoot(publicRoot, publicCandidate)) return publicCandidate;
|
if (publicCandidate && isInsideRoot(publicRoot, publicCandidate)) return publicCandidate;
|
||||||
|
|
||||||
return join(root, "index.html");
|
return extname(requested) ? null : join(root, "index.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = createServer((req, res) => {
|
const server = createServer((req, res) => {
|
||||||
|
|
@ -78,9 +78,15 @@ const server = createServer((req, res) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const extension = extname(file);
|
||||||
|
const cacheControl = extension === ".html"
|
||||||
|
? "no-store"
|
||||||
|
: extension === ".json"
|
||||||
|
? "no-cache"
|
||||||
|
: "public, max-age=31536000, immutable";
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
"Content-Type": types[extname(file)] || "application/octet-stream",
|
"Content-Type": types[extname(file)] || "application/octet-stream",
|
||||||
"Cache-Control": "no-store"
|
"Cache-Control": cacheControl
|
||||||
});
|
});
|
||||||
createReadStream(file).pipe(res);
|
createReadStream(file).pipe(res);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -200,11 +200,10 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
||||||
const tableUtils = await readFile("website/src/features/toolboxes/modules/tableUtils.js", "utf8");
|
const tableUtils = await readFile("website/src/features/toolboxes/modules/tableUtils.js", "utf8");
|
||||||
const tableFormulaEngine = await readFile("website/src/features/toolboxes/modules/tableFormulaEngine.js", "utf8");
|
const tableFormulaEngine = await readFile("website/src/features/toolboxes/modules/tableFormulaEngine.js", "utf8");
|
||||||
const timerModule = await readFile("website/src/features/toolboxes/modules/TimerModule.jsx", "utf8");
|
const timerModule = await readFile("website/src/features/toolboxes/modules/TimerModule.jsx", "utf8");
|
||||||
const timerParts = await readFile("website/src/features/toolboxes/modules/TimerParts.jsx", "utf8");
|
|
||||||
const timerCountdown = await readFile("website/src/features/toolboxes/modules/TimerCountdown.jsx", "utf8");
|
const timerCountdown = await readFile("website/src/features/toolboxes/modules/TimerCountdown.jsx", "utf8");
|
||||||
const timerSharedControls = await readFile("website/src/features/toolboxes/modules/TimerSharedControls.jsx", "utf8");
|
const timerSharedControls = await readFile("website/src/features/toolboxes/modules/TimerSharedControls.jsx", "utf8");
|
||||||
const timerStopwatch = await readFile("website/src/features/toolboxes/modules/TimerStopwatch.jsx", "utf8");
|
const timerStopwatch = await readFile("website/src/features/toolboxes/modules/TimerStopwatch.jsx", "utf8");
|
||||||
const timerBundle = `${timerModule}\n${timerParts}\n${timerCountdown}\n${timerSharedControls}\n${timerStopwatch}`;
|
const timerBundle = `${timerModule}\n${timerCountdown}\n${timerSharedControls}\n${timerStopwatch}`;
|
||||||
const timerOptions = await readFile("website/src/features/toolboxes/modules/timerOptions.js", "utf8");
|
const timerOptions = await readFile("website/src/features/toolboxes/modules/timerOptions.js", "utf8");
|
||||||
const calendarModule = await readFile("website/src/features/toolboxes/modules/CalendarModule.jsx", "utf8");
|
const calendarModule = await readFile("website/src/features/toolboxes/modules/CalendarModule.jsx", "utf8");
|
||||||
const calendarController = await readFile("website/src/features/toolboxes/modules/useCalendarController.js", "utf8");
|
const calendarController = await readFile("website/src/features/toolboxes/modules/useCalendarController.js", "utf8");
|
||||||
|
|
@ -221,14 +220,13 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
||||||
const calendarBundle = `${calendarModule}\n${calendarController}\n${calendarDraftControls}\n${calendarEventActions}\n${calendarLifecycle}\n${calendarModeChoice}\n${calendarContent}\n${calendarDraft}\n${calendarEventForm}\n${calendarEventControls}\n${calendarMonthDatePicker}\n${calendarViews}`;
|
const calendarBundle = `${calendarModule}\n${calendarController}\n${calendarDraftControls}\n${calendarEventActions}\n${calendarLifecycle}\n${calendarModeChoice}\n${calendarContent}\n${calendarDraft}\n${calendarEventForm}\n${calendarEventControls}\n${calendarMonthDatePicker}\n${calendarViews}`;
|
||||||
const calendarUtils = await readFile("website/src/features/toolboxes/modules/calendarUtils.js", "utf8");
|
const calendarUtils = await readFile("website/src/features/toolboxes/modules/calendarUtils.js", "utf8");
|
||||||
const taskPlannerModule = await readFile("website/src/features/toolboxes/modules/TaskPlannerModule.jsx", "utf8");
|
const taskPlannerModule = await readFile("website/src/features/toolboxes/modules/TaskPlannerModule.jsx", "utf8");
|
||||||
const taskPlannerItems = await readFile("website/src/features/toolboxes/modules/TaskPlannerItems.jsx", "utf8");
|
|
||||||
const taskPlannerBranch = await readFile("website/src/features/toolboxes/modules/TaskPlannerBranch.jsx", "utf8");
|
const taskPlannerBranch = await readFile("website/src/features/toolboxes/modules/TaskPlannerBranch.jsx", "utf8");
|
||||||
const taskPlannerItem = await readFile("website/src/features/toolboxes/modules/TaskPlannerItem.jsx", "utf8");
|
const taskPlannerItem = await readFile("website/src/features/toolboxes/modules/TaskPlannerItem.jsx", "utf8");
|
||||||
const taskPlannerItemPanels = await readFile("website/src/features/toolboxes/modules/TaskPlannerItemPanels.jsx", "utf8");
|
const taskPlannerItemPanels = await readFile("website/src/features/toolboxes/modules/TaskPlannerItemPanels.jsx", "utf8");
|
||||||
const taskPlannerReorder = await readFile("website/src/features/toolboxes/modules/useTaskPlannerReorder.js", "utf8");
|
const taskPlannerReorder = await readFile("website/src/features/toolboxes/modules/useTaskPlannerReorder.js", "utf8");
|
||||||
const taskPlannerTopbar = await readFile("website/src/features/toolboxes/modules/TaskPlannerTopbar.jsx", "utf8");
|
const taskPlannerTopbar = await readFile("website/src/features/toolboxes/modules/TaskPlannerTopbar.jsx", "utf8");
|
||||||
const taskPlannerSettings = await readFile("website/src/features/toolboxes/modules/TaskPlannerSettings.jsx", "utf8");
|
const taskPlannerSettings = await readFile("website/src/features/toolboxes/modules/TaskPlannerSettings.jsx", "utf8");
|
||||||
const taskPlannerBundle = `${taskPlannerItems}\n${taskPlannerBranch}\n${taskPlannerItem}\n${taskPlannerItemPanels}\n${taskPlannerReorder}\n${taskPlannerTopbar}\n${taskPlannerSettings}`;
|
const taskPlannerBundle = `${taskPlannerBranch}\n${taskPlannerItem}\n${taskPlannerItemPanels}\n${taskPlannerReorder}\n${taskPlannerTopbar}\n${taskPlannerSettings}`;
|
||||||
const taskPlannerUtils = await readFile("website/src/features/toolboxes/modules/taskPlannerUtils.js", "utf8");
|
const taskPlannerUtils = await readFile("website/src/features/toolboxes/modules/taskPlannerUtils.js", "utf8");
|
||||||
const goalTreeModule = await readFile("website/src/features/toolboxes/modules/GoalTreeModule.jsx", "utf8");
|
const goalTreeModule = await readFile("website/src/features/toolboxes/modules/GoalTreeModule.jsx", "utf8");
|
||||||
const goalTreeEditor = await readFile("website/src/features/toolboxes/modules/GoalTreeEditor.jsx", "utf8");
|
const goalTreeEditor = await readFile("website/src/features/toolboxes/modules/GoalTreeEditor.jsx", "utf8");
|
||||||
|
|
@ -279,8 +277,6 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
||||||
["CalendarModule.jsx", calendarModule],
|
["CalendarModule.jsx", calendarModule],
|
||||||
["modules/index.jsx", moduleRegistry],
|
["modules/index.jsx", moduleRegistry],
|
||||||
["GoalTreeParts.jsx", goalTreeParts],
|
["GoalTreeParts.jsx", goalTreeParts],
|
||||||
["TimerParts.jsx", timerParts],
|
|
||||||
["TaskPlannerItems.jsx", taskPlannerItems],
|
|
||||||
["RandomizerModule.jsx", randomizerModule]
|
["RandomizerModule.jsx", randomizerModule]
|
||||||
].forEach(([fileName, fileSource]) => {
|
].forEach(([fileName, fileSource]) => {
|
||||||
assert.ok(countLines(fileSource) <= 220, `${fileName} must remain a short orchestrator/barrel`);
|
assert.ok(countLines(fileSource) <= 220, `${fileName} must remain a short orchestrator/barrel`);
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,8 @@ function getToneFromDelta(value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RngSimulatorModule({ toolboxId, moduleId, context, editing = false, setEditing = () => {}, textExchangeOpen }) {
|
export function RngSimulatorModule({ toolboxId, moduleId, context, editing = false, setEditing = () => {}, textExchangeOpen }) {
|
||||||
const data = context.normalizeRngSimulatorData(context.getModuleData(toolboxId, moduleId, {}));
|
const rawData = context.getModuleData(toolboxId, moduleId, {});
|
||||||
|
const data = useMemo(() => context.normalizeRngSimulatorData(rawData), [context, rawData]);
|
||||||
const textContent = context.moduleText?.rngSimulator || {};
|
const textContent = context.moduleText?.rngSimulator || {};
|
||||||
const tabs = useMemo(() => [
|
const tabs = useMemo(() => [
|
||||||
{ ...RNG_TABS[0], label: textContent.trackingTabLabel || RNG_TABS[0].label },
|
{ ...RNG_TABS[0], label: textContent.trackingTabLabel || RNG_TABS[0].label },
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
// Rôle : conserve les exports publics historiques des sous-composants task planner.
|
|
||||||
export { TaskPlannerBranch } from "./TaskPlannerBranch.jsx";
|
|
||||||
export { TaskPlannerItem } from "./TaskPlannerItem.jsx";
|
|
||||||
|
|
@ -31,16 +31,20 @@ import {
|
||||||
} from "./timerOptions.js";
|
} from "./timerOptions.js";
|
||||||
|
|
||||||
export function TimerModule({ toolboxId, moduleId, context, textExchangeOpen }) {
|
export function TimerModule({ toolboxId, moduleId, context, textExchangeOpen }) {
|
||||||
const data = context.normalizeTimerData(context.getModuleData(toolboxId, moduleId, { activeTab: "stopwatch" }));
|
const rawData = context.getModuleData(toolboxId, moduleId, { activeTab: "stopwatch" });
|
||||||
|
const data = useMemo(() => context.normalizeTimerData(rawData), [context, rawData]);
|
||||||
const textContent = context.moduleText?.timer || {};
|
const textContent = context.moduleText?.timer || {};
|
||||||
const activeTab = data.activeTab;
|
const activeTab = data.activeTab;
|
||||||
const [nowMs, setNowMs] = useState(getNowMs);
|
const [nowMs, setNowMs] = useState(getNowMs);
|
||||||
const countdownDraft = useDraftForm(DEFAULT_COUNTDOWN_FORM);
|
const countdownDraft = useDraftForm(DEFAULT_COUNTDOWN_FORM);
|
||||||
|
|
||||||
|
const clockActive = Boolean(data.stopwatch.startedAt || data.countdowns.some((countdown) => countdown.autoRefresh));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!clockActive) return undefined;
|
||||||
const intervalId = window.setInterval(() => setNowMs(getNowMs()), 50);
|
const intervalId = window.setInterval(() => setNowMs(getNowMs()), 50);
|
||||||
return () => window.clearInterval(intervalId);
|
return () => window.clearInterval(intervalId);
|
||||||
}, []);
|
}, [clockActive]);
|
||||||
|
|
||||||
const stopwatchElapsedMs = data.stopwatch.startedAt
|
const stopwatchElapsedMs = data.stopwatch.startedAt
|
||||||
? data.stopwatch.elapsedMs + Math.max(0, nowMs - data.stopwatch.startedAt)
|
? data.stopwatch.elapsedMs + Math.max(0, nowMs - data.stopwatch.startedAt)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
// Rôle : conserve les exports publics historiques des sous-composants timer.
|
|
||||||
export { CountdownControls, CountdownList } from "./TimerCountdown.jsx";
|
|
||||||
export { EditableTimerLabel, TimerAlertModeSwitch, TimerValue, TimePartsInput } from "./TimerSharedControls.jsx";
|
|
||||||
export { StopwatchControls, StopwatchLapList } from "./TimerStopwatch.jsx";
|
|
||||||
|
|
@ -191,11 +191,6 @@ export function getEventDurationMs(event) {
|
||||||
return Number.isFinite(startMinutes) && Number.isFinite(endMinutes) && endMinutes > startMinutes ? (endMinutes - startMinutes) * MINUTE_MS : 0;
|
return Number.isFinite(startMinutes) && Number.isFinite(endMinutes) && endMinutes > startMinutes ? (endMinutes - startMinutes) * MINUTE_MS : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getEventEndMs(event, startMs) {
|
|
||||||
const durationMs = getEventDurationMs(event);
|
|
||||||
return durationMs > 0 ? startMs + durationMs : startMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCalendarAlertOffsets(event) {
|
export function getCalendarAlertOffsets(event) {
|
||||||
const rawOffsets = Array.isArray(event?.alertOffsetMinutes) ? event.alertOffsetMinutes : [event?.alertOffsetMinutes];
|
const rawOffsets = Array.isArray(event?.alertOffsetMinutes) ? event.alertOffsetMinutes : [event?.alertOffsetMinutes];
|
||||||
const offsets = [...new Set(rawOffsets.map(Number).filter((offset) => CALENDAR_ALERT_OFFSETS.includes(offset)))];
|
const offsets = [...new Set(rawOffsets.map(Number).filter((offset) => CALENDAR_ALERT_OFFSETS.includes(offset)))];
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ import {
|
||||||
} from "./goalTreeUtils.js";
|
} from "./goalTreeUtils.js";
|
||||||
|
|
||||||
export function useGoalTreeController({ toolboxId, moduleId, context, editing, setEditing, openEditor }) {
|
export function useGoalTreeController({ toolboxId, moduleId, context, editing, setEditing, openEditor }) {
|
||||||
const data = context.normalizeGoalTreeData(context.getModuleData(toolboxId, moduleId, { nodes: [] }));
|
const rawData = context.getModuleData(toolboxId, moduleId, { nodes: [] });
|
||||||
|
const data = useMemo(() => context.normalizeGoalTreeData(rawData), [context, rawData]);
|
||||||
const textContent = context.moduleText?.goalTree || {};
|
const textContent = context.moduleText?.goalTree || {};
|
||||||
const treeOrientation = data.orientation || "horizontal";
|
const treeOrientation = data.orientation || "horizontal";
|
||||||
const verticalView = treeOrientation === "vertical";
|
const verticalView = treeOrientation === "vertical";
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,11 @@
|
||||||
// Rôle : normalise et compacte les cellules et libellés de l'outil table.
|
// Rôle : normalise et compacte les cellules et libellés de l'outil table.
|
||||||
|
import { columnIndexToName } from "../../modules/tableFormulaEngine.js";
|
||||||
|
|
||||||
function clampTableSize(value, fallback, max) {
|
function clampTableSize(value, fallback, max) {
|
||||||
const parsed = Number.parseInt(value, 10);
|
const parsed = Number.parseInt(value, 10);
|
||||||
return Number.isInteger(parsed) ? Math.min(max, Math.max(1, parsed)) : fallback;
|
return Number.isInteger(parsed) ? Math.min(max, Math.max(1, parsed)) : fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
function columnIndexToName(index) {
|
|
||||||
let value = Math.max(0, Number(index) || 0) + 1;
|
|
||||||
let name = "";
|
|
||||||
while (value > 0) {
|
|
||||||
const remainder = (value - 1) % 26;
|
|
||||||
name = String.fromCharCode(65 + remainder) + name;
|
|
||||||
value = Math.floor((value - 1) / 26);
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tableCellAddress(rowIndex, columnIndex) {
|
function tableCellAddress(rowIndex, columnIndex) {
|
||||||
return `${columnIndexToName(columnIndex)}${rowIndex + 1}`;
|
return `${columnIndexToName(columnIndex)}${rowIndex + 1}`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,10 @@ export function useAppData(route) {
|
||||||
setSiteContentError(error.message);
|
setSiteContentError(error.message);
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
fetch("/data/games.json").then((response) => response.ok ? response.json() : { games: [] }).catch((error) => {
|
fetch("/data/games.json").then((response) => {
|
||||||
|
if (!response.ok) throw new Error("Impossible de charger la liste des jeux.");
|
||||||
|
return response.json();
|
||||||
|
}).catch((error) => {
|
||||||
setGamesError(error.message);
|
setGamesError(error.message);
|
||||||
return { games: [] };
|
return { games: [] };
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
// Rôle : surveille globalement les comptes à rebours actifs et déclenche les alertes internes autorisées.
|
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
import { getCountdownTargetMs } from "../features/toolboxes/modules/timerUtils.js";
|
|
||||||
import { moduleStorageKey } from "../features/toolboxes/storage/toolboxStorage.js";
|
|
||||||
|
|
||||||
function getVisibleToolboxIds(route, drawerGameId, links) {
|
|
||||||
const visibleIds = new Set();
|
|
||||||
const toolboxMatch = route.match(/^\/toolbox\/([^/]+)/);
|
|
||||||
if (toolboxMatch?.[1]) visibleIds.add(toolboxMatch[1]);
|
|
||||||
const drawerToolboxId = drawerGameId ? links[drawerGameId] : "";
|
|
||||||
if (drawerToolboxId) visibleIds.add(drawerToolboxId);
|
|
||||||
return visibleIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAlertMessage(template, toolbox, countdown) {
|
|
||||||
return template
|
|
||||||
.replaceAll("{label}", countdown.label)
|
|
||||||
.replaceAll("{toolbox}", toolbox.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useTimerAlerts({ toolboxes, moduleData, links, route, drawerGameId, normalizeTimerData, textContent, notify }) {
|
|
||||||
const previousTargetsRef = useRef(new Map());
|
|
||||||
const lastTickRef = useRef(Date.now());
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const visibleToolboxIds = getVisibleToolboxIds(route, drawerGameId, links);
|
|
||||||
const alertMessageTemplate = textContent?.alertMessage || "Compte à rebours terminé : {label}";
|
|
||||||
|
|
||||||
function tick() {
|
|
||||||
const nowMs = Date.now();
|
|
||||||
const lastTickMs = lastTickRef.current;
|
|
||||||
const nextTargets = new Map();
|
|
||||||
|
|
||||||
toolboxes.forEach((toolbox) => {
|
|
||||||
toolbox.modules.forEach((module) => {
|
|
||||||
if (module.type !== "timer") return;
|
|
||||||
|
|
||||||
const data = normalizeTimerData(moduleData[moduleStorageKey(toolbox.id, module.id)]);
|
|
||||||
|
|
||||||
data.countdowns.forEach((countdown) => {
|
|
||||||
if (countdown.alertMode === "off") return;
|
|
||||||
if (countdown.alertMode === "visible" && !visibleToolboxIds.has(toolbox.id)) return;
|
|
||||||
|
|
||||||
const key = `${toolbox.id}:${module.id}:${countdown.id}`;
|
|
||||||
const targetMs = getCountdownTargetMs(countdown, nowMs);
|
|
||||||
const previousTargetMs = previousTargetsRef.current.get(key) || targetMs;
|
|
||||||
nextTargets.set(key, targetMs);
|
|
||||||
|
|
||||||
if (!targetMs || previousTargetMs <= lastTickMs || previousTargetMs > nowMs) return;
|
|
||||||
notify(getAlertMessage(alertMessageTemplate, toolbox, countdown));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
previousTargetsRef.current = nextTargets;
|
|
||||||
lastTickRef.current = nowMs;
|
|
||||||
}
|
|
||||||
|
|
||||||
tick();
|
|
||||||
const intervalId = window.setInterval(tick, 500);
|
|
||||||
return () => window.clearInterval(intervalId);
|
|
||||||
}, [drawerGameId, links, moduleData, normalizeTimerData, notify, route, textContent, toolboxes]);
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Rôle : choisit la page à rendre selon la route active.
|
// Rôle : choisit la page à rendre selon la route active.
|
||||||
import { lazy, Suspense } from "react";
|
import { lazy, Suspense, useEffect } from "react";
|
||||||
import { AboutPage } from "../pages/AboutPage.jsx";
|
import { AboutPage } from "../pages/AboutPage.jsx";
|
||||||
import { HomePage } from "../pages/HomePage.jsx";
|
import { HomePage } from "../pages/HomePage.jsx";
|
||||||
import { navigate } from "./hashRouter.js";
|
import { navigate } from "./hashRouter.js";
|
||||||
|
|
@ -17,6 +17,13 @@ function RouteLoader() {
|
||||||
return <section className="empty nds-empty-state"><p>Chargement...</p></section>;
|
return <section className="empty nds-empty-state"><p>Chargement...</p></section>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function UnknownRouteRedirect() {
|
||||||
|
useEffect(() => {
|
||||||
|
navigate("/");
|
||||||
|
}, []);
|
||||||
|
return <RouteLoader />;
|
||||||
|
}
|
||||||
|
|
||||||
export function RouteContent(props) {
|
export function RouteContent(props) {
|
||||||
const { route } = props;
|
const { route } = props;
|
||||||
if (route === "/") return <HomePage {...props} />;
|
if (route === "/") return <HomePage {...props} />;
|
||||||
|
|
@ -27,6 +34,5 @@ export function RouteContent(props) {
|
||||||
if (route.startsWith("/toolbox/")) return <Suspense fallback={<RouteLoader />}><ToolboxPage {...props} toolboxId={route.split("/")[2]} /></Suspense>;
|
if (route.startsWith("/toolbox/")) return <Suspense fallback={<RouteLoader />}><ToolboxPage {...props} toolboxId={route.split("/")[2]} /></Suspense>;
|
||||||
if (route === "/games") return <Suspense fallback={<RouteLoader />}><GamesPage {...props} /></Suspense>;
|
if (route === "/games") return <Suspense fallback={<RouteLoader />}><GamesPage {...props} /></Suspense>;
|
||||||
if (route.startsWith("/games/")) return <Suspense fallback={<RouteLoader />}><GameRoute {...props} gameId={route.split("/")[2]} category={route.split("/")[3] || ""} /></Suspense>;
|
if (route.startsWith("/games/")) return <Suspense fallback={<RouteLoader />}><GameRoute {...props} gameId={route.split("/")[2]} category={route.split("/")[3] || ""} /></Suspense>;
|
||||||
navigate("/");
|
return <UnknownRouteRedirect />;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue