Consolidate toolbox loaders and reset helpers
Some checks failed
Deploy Sokko G / deploy (push) Failing after 2s

This commit is contained in:
Shinuwa 2026-09-10 13:45:48 +02:00
parent 6c61e11f9a
commit 865a4ffd97
7 changed files with 61 additions and 75 deletions

View file

@ -17,6 +17,7 @@ test("vite entrypoint and app shell are wired", async () => {
const homeOnboarding = await readFile("website/src/pages/HomeOnboardingSection.jsx", "utf8"); const homeOnboarding = await readFile("website/src/pages/HomeOnboardingSection.jsx", "utf8");
const aboutPage = await readFile("website/src/pages/AboutPage.jsx", "utf8"); const aboutPage = await readFile("website/src/pages/AboutPage.jsx", "utf8");
const libraryPage = await readFile("website/src/pages/LibraryPage.jsx", "utf8"); const libraryPage = await readFile("website/src/pages/LibraryPage.jsx", "utf8");
const moduleRegistry = await readFile("website/src/features/toolboxes/toolboxModuleRegistry.js", "utf8");
const hashRouter = await readFile("website/src/router/hashRouter.js", "utf8"); const hashRouter = await readFile("website/src/router/hashRouter.js", "utf8");
const routeContent = await readFile("website/src/router/RouteContent.jsx", "utf8"); const routeContent = await readFile("website/src/router/RouteContent.jsx", "utf8");
const bodyScrollLock = await readFile("website/src/utils/bodyScrollLock.js", "utf8"); const bodyScrollLock = await readFile("website/src/utils/bodyScrollLock.js", "utf8");
@ -83,8 +84,11 @@ test("vite entrypoint and app shell are wired", async () => {
assert.match(aboutPage, /about-tool-examples/); assert.match(aboutPage, /about-tool-examples/);
assert.match(libraryPage, /export function LibraryPage/); assert.match(libraryPage, /export function LibraryPage/);
assert.match(libraryPage, /LIBRARY_TOOLBOX_ID/); assert.match(libraryPage, /LIBRARY_TOOLBOX_ID/);
assert.match(libraryPage, /TaskPlannerModule/); assert.match(libraryPage, /TOOLBOX_MODULE_LOADERS/);
assert.match(libraryPage, /EquipmentPlannerModule/); assert.match(libraryPage, /Object\.entries\(TOOLBOX_MODULE_LOADERS\)/);
assert.match(moduleRegistry, /export const TOOLBOX_MODULE_LOADERS/);
assert.match(moduleRegistry, /taskPlanner: \(\) => import\("\.\/modules\/TaskPlannerModule\.jsx"\)/);
assert.match(moduleRegistry, /equipmentPlanner: \(\) => import\("\.\/modules\/EquipmentPlannerModule\.jsx"\)/);
assert.match(libraryPage, /normalizeEquipmentPlannerData/); assert.match(libraryPage, /normalizeEquipmentPlannerData/);
assert.match(libraryPage, /summarizeEquipmentPlannerData/); assert.match(libraryPage, /summarizeEquipmentPlannerData/);
assert.match(bodyScrollLock, /modalLocks/); assert.match(bodyScrollLock, /modalLocks/);

View file

@ -26,6 +26,7 @@ test("toolbox storage, cards and pages are wired", async () => {
const checklistStorage = await readFile("website/src/features/toolboxes/storage/modules/checklist.js", "utf8"); const checklistStorage = await readFile("website/src/features/toolboxes/storage/modules/checklist.js", "utf8");
const timerStorage = await readFile("website/src/features/toolboxes/storage/modules/timer.js", "utf8"); const timerStorage = await readFile("website/src/features/toolboxes/storage/modules/timer.js", "utf8");
const calendarStorage = await readFile("website/src/features/toolboxes/storage/modules/calendar.js", "utf8"); const calendarStorage = await readFile("website/src/features/toolboxes/storage/modules/calendar.js", "utf8");
const resetUtils = await readFile("website/src/features/toolboxes/modules/resetUtils.js", "utf8");
const equipmentStorage = await readFile("website/src/features/toolboxes/storage/modules/equipmentPlanner.js", "utf8"); const equipmentStorage = await readFile("website/src/features/toolboxes/storage/modules/equipmentPlanner.js", "utf8");
const indexedToolboxesHook = await readFile("website/src/features/toolboxes/storage/useIndexedToolboxes.js", "utf8"); const indexedToolboxesHook = await readFile("website/src/features/toolboxes/storage/useIndexedToolboxes.js", "utf8");
const optimisticModuleData = await readFile("website/src/features/toolboxes/storage/optimisticModuleData.js", "utf8"); const optimisticModuleData = await readFile("website/src/features/toolboxes/storage/optimisticModuleData.js", "utf8");
@ -86,6 +87,8 @@ test("toolbox storage, cards and pages are wired", async () => {
assert.match(storageCompactorRegistry, /goalTree: compactGoalTreeDataForStorage/); assert.match(storageCompactorRegistry, /goalTree: compactGoalTreeDataForStorage/);
assert.match(timerStorage, /scrollResults/); assert.match(timerStorage, /scrollResults/);
assert.match(calendarStorage, /normalizeCalendarData/); assert.match(calendarStorage, /normalizeCalendarData/);
assert.match(resetUtils, /export function getLatestDailyReset/);
assert.match(resetUtils, /export function getLatestWeeklyReset/);
assert.match(toolboxStorage, /nextId\("calendar"\)/); assert.match(toolboxStorage, /nextId\("calendar"\)/);
assert.match(toolboxCard, /DEFAULT_TOOLBOX_ICON/); assert.match(toolboxCard, /DEFAULT_TOOLBOX_ICON/);
assert.match(toolboxCard, /toolbox\.customCover/); assert.match(toolboxCard, /toolbox\.customCover/);

View file

@ -1,4 +1,5 @@
// Rôle : regroupe les calculs purs de l'arbre d'objectifs. // Rôle : regroupe les calculs purs de l'arbre d'objectifs.
import { getLatestDailyReset, getLatestWeeklyReset } from "./resetUtils.js";
export const GOAL_TREE_KINDS = ["step", "text", "checklist"]; export const GOAL_TREE_KINDS = ["step", "text", "checklist"];
export const GOAL_TREE_RESET_TYPES = ["none", "daily", "weekly"]; export const GOAL_TREE_RESET_TYPES = ["none", "daily", "weekly"];
export const GOAL_TREE_ICON_NAMES = ["cube", "fire", "flask", "grass", "leaf", "meat", "powder", "tear", "ball"]; export const GOAL_TREE_ICON_NAMES = ["cube", "fire", "flask", "grass", "leaf", "meat", "powder", "tear", "ball"];
@ -220,32 +221,6 @@ export function evaluateGoalTreeNodes(nodes) {
})); }));
} }
function parseResetTime(value) {
const [hours, minutes] = String(value || "00:00").split(":").map((part) => Number.parseInt(part, 10));
return {
hours: Number.isInteger(hours) ? Math.min(23, Math.max(0, hours)) : 0,
minutes: Number.isInteger(minutes) ? Math.min(59, Math.max(0, minutes)) : 0
};
}
function getLatestDailyReset(now, resetTime) {
const { hours, minutes } = parseResetTime(resetTime);
const reset = new Date(now);
reset.setHours(hours, minutes, 0, 0);
if (reset.getTime() > now) reset.setDate(reset.getDate() - 1);
return reset.getTime();
}
function getLatestWeeklyReset(now, resetTime, weekDay) {
const { hours, minutes } = parseResetTime(resetTime);
const reset = new Date(now);
reset.setHours(hours, minutes, 0, 0);
const dayDiff = (reset.getDay() - weekDay + 7) % 7;
reset.setDate(reset.getDate() - dayDiff);
if (reset.getTime() > now) reset.setDate(reset.getDate() - 7);
return reset.getTime();
}
export function applyGoalTreeDueResets(data, now = Date.now()) { export function applyGoalTreeDueResets(data, now = Date.now()) {
let latestResetAt = data.lastResetAt || 0; let latestResetAt = data.lastResetAt || 0;
let changed = false; let changed = false;

View file

@ -0,0 +1,26 @@
// Rôle : partage les calculs de réinitialisation temporelle des outils.
export function parseResetTime(value) {
const [hours, minutes] = String(value || "00:00").split(":").map((part) => Number.parseInt(part, 10));
return {
hours: Number.isInteger(hours) ? Math.min(23, Math.max(0, hours)) : 0,
minutes: Number.isInteger(minutes) ? Math.min(59, Math.max(0, minutes)) : 0
};
}
export function getLatestDailyReset(now, resetTime) {
const { hours, minutes } = parseResetTime(resetTime);
const reset = new Date(now);
reset.setHours(hours, minutes, 0, 0);
if (reset.getTime() > now) reset.setDate(reset.getDate() - 1);
return reset.getTime();
}
export function getLatestWeeklyReset(now, resetTime, weekDay) {
const { hours, minutes } = parseResetTime(resetTime);
const reset = new Date(now);
reset.setHours(hours, minutes, 0, 0);
const dayDiff = (reset.getDay() - weekDay + 7) % 7;
reset.setDate(reset.getDate() - dayDiff);
if (reset.getTime() > now) reset.setDate(reset.getDate() - 7);
return reset.getTime();
}

View file

@ -1,4 +1,6 @@
// Rôle : regroupe les constantes et helpers purs de l'outil task planner. // Rôle : regroupe les constantes et helpers purs de l'outil task planner.
import { getLatestDailyReset, getLatestWeeklyReset, parseResetTime } from "./resetUtils.js";
export const TASK_TYPES = ["daily", "weekly", "unique"]; export const TASK_TYPES = ["daily", "weekly", "unique"];
const DAILY_RESET_SOON_MS = 6 * 60 * 60 * 1000; const DAILY_RESET_SOON_MS = 6 * 60 * 60 * 1000;
const WEEKLY_RESET_SOON_MS = 48 * 60 * 60 * 1000; const WEEKLY_RESET_SOON_MS = 48 * 60 * 60 * 1000;
@ -19,32 +21,6 @@ export function getTaskTypeLabel(type, textContent) {
return textContent.uniqueType || "Ponctuelle"; return textContent.uniqueType || "Ponctuelle";
} }
export function parseResetTime(value) {
const [hours, minutes] = String(value || "00:00").split(":").map((part) => Number.parseInt(part, 10));
return {
hours: Number.isInteger(hours) ? Math.min(23, Math.max(0, hours)) : 0,
minutes: Number.isInteger(minutes) ? Math.min(59, Math.max(0, minutes)) : 0
};
}
export function getLatestDailyReset(now, resetTime) {
const { hours, minutes } = parseResetTime(resetTime);
const reset = new Date(now);
reset.setHours(hours, minutes, 0, 0);
if (reset.getTime() > now) reset.setDate(reset.getDate() - 1);
return reset.getTime();
}
export function getLatestWeeklyReset(now, resetTime, weekDay) {
const { hours, minutes } = parseResetTime(resetTime);
const reset = new Date(now);
reset.setHours(hours, minutes, 0, 0);
const dayDiff = (reset.getDay() - weekDay + 7) % 7;
reset.setDate(reset.getDate() - dayDiff);
if (reset.getTime() > now) reset.setDate(reset.getDate() - 7);
return reset.getTime();
}
export function getNextDailyReset(now, resetTime) { export function getNextDailyReset(now, resetTime) {
const { hours, minutes } = parseResetTime(resetTime); const { hours, minutes } = parseResetTime(resetTime);
const reset = new Date(now); const reset = new Date(now);

View file

@ -1,4 +1,4 @@
// Rôle : centralise les métadonnées légères des outils toolbox sans importer leurs composants. // Rôle : centralise les métadonnées et les chargeurs lazy des outils toolbox.
export const TOOLBOX_MODULE_DEFINITIONS = { export const TOOLBOX_MODULE_DEFINITIONS = {
notepad: { notepad: {
label: "Bloc Note", label: "Bloc Note",
@ -162,6 +162,26 @@ export const TOOLBOX_MODULES = Object.fromEntries(Object.entries(TOOLBOX_MODULE_
{ label: definition.label, icon: definition.icon } { label: definition.label, icon: definition.icon }
])); ]));
export const TOOLBOX_MODULE_LOADERS = {
notepad: () => import("./modules/NotepadModule.jsx").then((module) => ({ default: module.NotepadModule })),
memos: () => import("./modules/MemosModule.jsx").then((module) => ({ default: module.MemosModule })),
checklist: () => import("./modules/ChecklistModule.jsx").then((module) => ({ default: module.ChecklistModule })),
images: () => import("./modules/ImagesModule.jsx").then((module) => ({ default: module.ImagesModule })),
links: () => import("./modules/LinksModule.jsx").then((module) => ({ default: module.LinksModule })),
counters: () => import("./modules/CountersModule.jsx").then((module) => ({ default: module.CountersModule })),
randomizer: () => import("./modules/RandomizerModule.jsx").then((module) => ({ default: module.RandomizerModule })),
rngSimulator: () => import("./modules/RngSimulatorModule.jsx").then((module) => ({ default: module.RngSimulatorModule })),
combos: () => import("./modules/CombosModule.jsx").then((module) => ({ default: module.CombosModule })),
calculator: () => import("./modules/CalculatorModule.jsx").then((module) => ({ default: module.CalculatorModule })),
calendar: () => import("./modules/CalendarModule.jsx").then((module) => ({ default: module.CalendarModule })),
table: () => import("./modules/TableModule.jsx").then((module) => ({ default: module.TableModule })),
goalTree: () => import("./modules/GoalTreeModule.jsx").then((module) => ({ default: module.GoalTreeModule })),
timer: () => import("./modules/TimerModule.jsx").then((module) => ({ default: module.TimerModule })),
taskPlanner: () => import("./modules/TaskPlannerModule.jsx").then((module) => ({ default: module.TaskPlannerModule })),
equipmentPlanner: () => import("./modules/EquipmentPlannerModule.jsx").then((module) => ({ default: module.EquipmentPlannerModule })),
imageAnnotation: () => import("./modules/ImageAnnotationModule.jsx").then((module) => ({ default: module.ImageAnnotationModule }))
};
export const LIBRARY_CATEGORY_DEFINITIONS = [ export const LIBRARY_CATEGORY_DEFINITIONS = [
{ key: "notesTracking", types: ["memos", "notepad", "checklist", "counters"] }, { key: "notesTracking", types: ["memos", "notepad", "checklist", "counters"] },
{ key: "references", types: ["images", "imageAnnotation", "links"] }, { key: "references", types: ["images", "imageAnnotation", "links"] },

View file

@ -1,7 +1,7 @@
// Rôle : affiche la librairie des outils avec des exemples locaux non persistés. // Rôle : affiche la librairie des outils avec des exemples locaux non persistés.
import { lazy, Suspense, useEffect, useMemo, useRef, useState } from "react"; import { lazy, Suspense, useEffect, useMemo, useRef, useState } from "react";
import { Icon } from "../components/Icon.jsx"; import { Icon } from "../components/Icon.jsx";
import { LIBRARY_CATEGORY_DEFINITIONS, getToolboxModuleDefinition } from "../features/toolboxes/toolboxModuleRegistry.js"; import { LIBRARY_CATEGORY_DEFINITIONS, getToolboxModuleDefinition, TOOLBOX_MODULE_LOADERS } from "../features/toolboxes/toolboxModuleRegistry.js";
import { import {
clampQty, clampQty,
hostnameFromUrl, hostnameFromUrl,
@ -29,25 +29,7 @@ import {
const LIBRARY_TOOLBOX_ID = "library"; const LIBRARY_TOOLBOX_ID = "library";
const LIBRARY_ANCHOR_RETRY_DELAYS = [0, 80, 240, 600]; const LIBRARY_ANCHOR_RETRY_DELAYS = [0, 80, 240, 600];
const MODULE_COMPONENTS = { const MODULE_COMPONENTS = Object.fromEntries(Object.entries(TOOLBOX_MODULE_LOADERS).map(([type, loader]) => [type, lazy(loader)]));
memos: lazy(() => import("../features/toolboxes/modules/MemosModule.jsx").then((module) => ({ default: module.MemosModule }))),
notepad: lazy(() => import("../features/toolboxes/modules/NotepadModule.jsx").then((module) => ({ default: module.NotepadModule }))),
checklist: lazy(() => import("../features/toolboxes/modules/ChecklistModule.jsx").then((module) => ({ default: module.ChecklistModule }))),
images: lazy(() => import("../features/toolboxes/modules/ImagesModule.jsx").then((module) => ({ default: module.ImagesModule }))),
links: lazy(() => import("../features/toolboxes/modules/LinksModule.jsx").then((module) => ({ default: module.LinksModule }))),
counters: lazy(() => import("../features/toolboxes/modules/CountersModule.jsx").then((module) => ({ default: module.CountersModule }))),
combos: lazy(() => import("../features/toolboxes/modules/CombosModule.jsx").then((module) => ({ default: module.CombosModule }))),
calculator: lazy(() => import("../features/toolboxes/modules/CalculatorModule.jsx").then((module) => ({ default: module.CalculatorModule }))),
calendar: lazy(() => import("../features/toolboxes/modules/CalendarModule.jsx").then((module) => ({ default: module.CalendarModule }))),
table: lazy(() => import("../features/toolboxes/modules/TableModule.jsx").then((module) => ({ default: module.TableModule }))),
goalTree: lazy(() => import("../features/toolboxes/modules/GoalTreeModule.jsx").then((module) => ({ default: module.GoalTreeModule }))),
timer: lazy(() => import("../features/toolboxes/modules/TimerModule.jsx").then((module) => ({ default: module.TimerModule }))),
taskPlanner: lazy(() => import("../features/toolboxes/modules/TaskPlannerModule.jsx").then((module) => ({ default: module.TaskPlannerModule }))),
equipmentPlanner: lazy(() => import("../features/toolboxes/modules/EquipmentPlannerModule.jsx").then((module) => ({ default: module.EquipmentPlannerModule }))),
imageAnnotation: lazy(() => import("../features/toolboxes/modules/ImageAnnotationModule.jsx").then((module) => ({ default: module.ImageAnnotationModule }))),
randomizer: lazy(() => import("../features/toolboxes/modules/RandomizerModule.jsx").then((module) => ({ default: module.RandomizerModule }))),
rngSimulator: lazy(() => import("../features/toolboxes/modules/RngSimulatorModule.jsx").then((module) => ({ default: module.RngSimulatorModule })))
};
function cloneData(value) { function cloneData(value) {
if (value === undefined) return {}; if (value === undefined) return {};