diff --git a/eslint.config.js b/eslint.config.js index e628cdc..c9c9357 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -41,7 +41,7 @@ export default [ varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_" }], - "react-refresh/only-export-components": "off" + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }] } }, { diff --git a/tests/static-toolboxes.test.mjs b/tests/static-toolboxes.test.mjs index 313b650..500c63a 100644 --- a/tests/static-toolboxes.test.mjs +++ b/tests/static-toolboxes.test.mjs @@ -2,6 +2,8 @@ import { readFile } from "node:fs/promises"; import { test } from "node:test"; import assert from "node:assert/strict"; +import { SOCKET_SHAPES, formatValue, getSocketItemName, setItemCategory } from "../website/src/features/toolboxes/modules/equipmentPlannerOptions.js"; +import { applyDueResets, getTaskParentMap, getTaskTreeIds, moveTask } from "../website/src/features/toolboxes/modules/taskPlannerUtils.js"; test("toolbox storage, cards and pages are wired", async () => { const source = await readFile("website/src/main.jsx", "utf8"); @@ -111,7 +113,10 @@ test("toolbox module registry and modules expose expected behavior", async () => 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 taskPlannerModule = await readFile("website/src/features/toolboxes/modules/TaskPlannerModule.jsx", "utf8"); + const taskPlannerItems = await readFile("website/src/features/toolboxes/modules/TaskPlannerItems.jsx", "utf8"); + const taskPlannerUtils = await readFile("website/src/features/toolboxes/modules/taskPlannerUtils.js", "utf8"); const equipmentPlannerModule = await readFile("website/src/features/toolboxes/modules/EquipmentPlannerModule.jsx", "utf8"); + const equipmentPlannerOptions = await readFile("website/src/features/toolboxes/modules/equipmentPlannerOptions.js", "utf8"); const checklistModule = await readFile("website/src/features/toolboxes/modules/ChecklistModule.jsx", "utf8"); const imagesModule = await readFile("website/src/features/toolboxes/modules/ImagesModule.jsx", "utf8"); const imageAnnotationModule = await readFile("website/src/features/toolboxes/modules/ImageAnnotationModule.jsx", "utf8"); @@ -253,17 +258,20 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(taskPlannerModule, /export function TaskPlannerModule/); assert.match(taskPlannerModule, /normalizeTaskPlannerData/); assert.match(taskPlannerModule, /applyDueResets/); - assert.match(taskPlannerModule, /relations/); - assert.match(taskPlannerModule, /prerequisite/); - assert.match(taskPlannerModule, /categoryOrder/); - assert.match(taskPlannerModule, /categoryLabel/); - assert.match(taskPlannerModule, /task-planner-warning/); + assert.match(taskPlannerModule, /TaskPlannerBranch/); assert.match(taskPlannerModule, /useGroupedReorder/); - assert.match(taskPlannerModule, /useInlineEdit/); assert.match(taskPlannerModule, /useDraftForm/); - assert.match(taskPlannerModule, /commitOnEnter: false/); assert.match(taskPlannerModule, /TextExchangeActions/); assert.match(taskPlannerModule, /reorderFeatures/); + assert.match(taskPlannerItems, /export function TaskPlannerBranch/); + assert.match(taskPlannerItems, /function TaskPlannerItem/); + assert.match(taskPlannerItems, /task-planner-warning/); + assert.match(taskPlannerItems, /useInlineEdit/); + assert.match(taskPlannerItems, /commitOnEnter: false/); + assert.match(taskPlannerItems, /categoryLabel/); + assert.match(taskPlannerItems, /prerequisite/); + assert.match(taskPlannerUtils, /export function getTaskParentMap/); + assert.match(taskPlannerUtils, /export function applyDueResets/); assert.doesNotMatch(taskPlannerModule, /canMoveItem:/); assert.doesNotMatch(taskPlannerModule, /canMoveGroup:/); assert.match(equipmentPlannerModule, /export function EquipmentPlannerModule/); @@ -274,6 +282,8 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(equipmentPlannerModule, /useGroupedReorder/); assert.match(equipmentPlannerModule, /reorderFeatures/); assert.match(equipmentPlannerModule, /TextExchangeActions/); + assert.match(equipmentPlannerOptions, /export const EQUIPMENT_ICONS/); + assert.match(equipmentPlannerOptions, /export function formatValue/); assert.match(checklistModule, /export function ChecklistModule/); assert.match(checklistModule, /TextExchangeActions/); assert.match(checklistModule, /editing &&/); @@ -354,3 +364,35 @@ test("toolbox module registry and modules expose expected behavior", async () => assert.match(imageViewer, / { + const tasks = [ + { id: "a", title: "A", type: "daily", checked: true, checkedAt: Date.UTC(2026, 0, 1, 7) }, + { id: "b", title: "B", type: "unique", checked: false }, + { id: "c", title: "C", type: "weekly", checked: true, checkedAt: Date.UTC(2026, 0, 1, 7) } + ]; + const relations = [{ id: "r1", fromTaskId: "b", toTaskId: "a", prerequisite: false }]; + const parentMap = getTaskParentMap(tasks, relations); + + assert.equal(parentMap.get("b"), "a"); + assert.deepEqual(getTaskTreeIds(["a"], tasks, parentMap), ["a", "b"]); + assert.deepEqual(moveTask(tasks, "c", "a").map((task) => task.id), ["c", "a", "b"]); + + const resetData = applyDueResets({ + tasks, + relations, + resetTime: "06:00", + weeklyResetDay: 4, + categoryOrder: [], + lastResetAt: Date.UTC(2026, 0, 1, 5) + }, Date.UTC(2026, 0, 2, 8)); + assert.equal(resetData.tasks.find((task) => task.id === "a").checked, false); +}); + +test("equipment planner options expose stable formatting and socket helpers", () => { + assert.deepEqual(SOCKET_SHAPES, ["ball", "jewel"]); + assert.equal(formatValue(1.23456), "1.235"); + assert.deepEqual(setItemCategory({ id: "item" }, "DPS"), { id: "item", category: "DPS" }); + assert.deepEqual(setItemCategory({ id: "item", category: "DPS" }, ""), { id: "item" }); + assert.equal(getSocketItemName({ socketItems: [{ id: "gem", name: "Ruby" }] }, "gem"), "Ruby"); +}); diff --git a/website/src/features/games/CopyChecklistItemsButton.jsx b/website/src/features/games/CopyChecklistItemsButton.jsx index 70855e8..609b6e0 100644 --- a/website/src/features/games/CopyChecklistItemsButton.jsx +++ b/website/src/features/games/CopyChecklistItemsButton.jsx @@ -1,4 +1,5 @@ // Rôle : copie des données filtrées au format compatible avec l'import checklist. +/* eslint-disable react-refresh/only-export-components -- expose aussi les formatters publics testés pour l'import checklist. */ import { useState } from "react"; import { Icon } from "../../components/Icon.jsx"; diff --git a/website/src/features/toolboxes/ToolboxCard.jsx b/website/src/features/toolboxes/ToolboxCard.jsx index 09a7d55..35e841b 100644 --- a/website/src/features/toolboxes/ToolboxCard.jsx +++ b/website/src/features/toolboxes/ToolboxCard.jsx @@ -1,4 +1,5 @@ // Rôle : affiche une toolbox dans la liste, avec actions rapides et icône personnalisable. +/* eslint-disable react-refresh/only-export-components -- expose aussi les helpers publics de rendu des cartes toolbox. */ import { CompactDropdown } from "../../components/CompactDropdown.jsx"; import { Icon } from "../../components/Icon.jsx"; import { diff --git a/website/src/main.jsx b/website/src/main.jsx index cde9368..60852a6 100644 --- a/website/src/main.jsx +++ b/website/src/main.jsx @@ -1,4 +1,5 @@ // Rôle : point d'entrée React, assemble données, routes, shell et overlays. +/* eslint-disable react-refresh/only-export-components -- point d'entrée Vite sans export de composant. */ import { lazy, Suspense, useCallback, useEffect, useState } from "react"; import { createRoot } from "react-dom/client"; import "./styles/main.scss";