Add planner-aware memos and unified tool configuration panels
All checks were successful
Deploy Sokko G / deploy (push) Successful in 18s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 18s
This commit is contained in:
parent
acd9b2fe11
commit
808610dcf2
53 changed files with 842 additions and 329 deletions
|
|
@ -248,6 +248,14 @@ export function validateSiteContent(site) {
|
|||
"toolboxes.spaces.moveSuccess",
|
||||
"toolboxes.spaces.emptyText",
|
||||
"toolboxes.spaces.limitReached",
|
||||
"toolboxes.moduleConfiguration.buttonLabel",
|
||||
"toolboxes.moduleConfiguration.panelLabel",
|
||||
"toolboxes.moduleConfiguration.organizationTitle",
|
||||
"toolboxes.moduleConfiguration.spaceLabel",
|
||||
"toolboxes.moduleConfiguration.guideTitle",
|
||||
"toolboxes.moduleConfiguration.dataTitle",
|
||||
"toolboxes.moduleConfiguration.importButtonLabel",
|
||||
"toolboxes.moduleConfiguration.exportButtonLabel",
|
||||
"toolboxes.drawer.emptyText",
|
||||
"toolboxes.drawer.createLabel",
|
||||
"toolboxes.drawer.linkExistingTitle",
|
||||
|
|
@ -345,6 +353,12 @@ export function validateSiteContent(site) {
|
|||
"toolboxes.modules.memos.tickerLabel",
|
||||
"toolboxes.modules.memos.hideTickerTitle",
|
||||
"toolboxes.modules.memos.showTickerTitle",
|
||||
"toolboxes.modules.memos.settingsTitle",
|
||||
"toolboxes.modules.memos.plannerLinkLabel",
|
||||
"toolboxes.modules.memos.plannerNoneLabel",
|
||||
"toolboxes.modules.memos.plannerAllLabel",
|
||||
"toolboxes.modules.memos.plannerDefaultLabel",
|
||||
"toolboxes.modules.memos.taskAlertTemplate",
|
||||
"toolboxes.modules.memos.importPlaceholder",
|
||||
"toolboxes.modules.memos.importOpenButton",
|
||||
"toolboxes.modules.memos.exportOpenButton",
|
||||
|
|
@ -830,7 +844,6 @@ export function validateSiteContent(site) {
|
|||
"toolboxes.modules.calendar.requiredError",
|
||||
"toolboxes.modules.calendar.invalidRangeError",
|
||||
"toolboxes.modules.taskPlanner.settingsTitle",
|
||||
"toolboxes.modules.taskPlanner.settingsButtonLabel",
|
||||
"toolboxes.modules.taskPlanner.hideCompletedTitle",
|
||||
"toolboxes.modules.taskPlanner.globalWeeklyResetDayLabel",
|
||||
"toolboxes.modules.taskPlanner.resetTimeLabel",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ 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, isTaskResetSoon, moveTask } from "../website/src/features/toolboxes/modules/taskPlannerUtils.js";
|
||||
import { getMemosDataAfterPlannerRemoval, getPlannerTaskTickerItems, resolveMemosPlannerLink } from "../website/src/features/toolboxes/modules/memosPlannerUtils.js";
|
||||
import { createInput, getInputLabel, normalizeCustomKey, parseHoldSeconds } from "../website/src/features/toolboxes/modules/combosUtils.js";
|
||||
import { getBoundsFromStartAndSize, getSelectionBounds, moveSelectionCells, parseTsvBlock, pasteCellBlock } from "../website/src/features/toolboxes/modules/tableUtils.js";
|
||||
import { canToggleAutoRefresh, getCountdownValidationError, isAlertGuarded } from "../website/src/features/toolboxes/modules/timerOptions.js";
|
||||
|
|
@ -97,7 +98,8 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
const toolboxPages = await readFile("website/src/features/toolboxes/ToolboxPages.jsx", "utf8");
|
||||
const toolboxModules = await readFile("website/src/features/toolboxes/modules/ToolboxModules.jsx", "utf8");
|
||||
const moduleShell = await readFile("website/src/features/toolboxes/modules/ModuleShell.jsx", "utf8");
|
||||
const moduleRegistryBundle = `${moduleRegistry}\n${moduleComponents}\n${addToolControls}\n${toolboxModules}\n${moduleShell}`;
|
||||
const moduleConfiguration = await readFile("website/src/features/toolboxes/modules/ModuleConfiguration.jsx", "utf8");
|
||||
const moduleRegistryBundle = `${moduleRegistry}\n${moduleComponents}\n${addToolControls}\n${toolboxModules}\n${moduleShell}\n${moduleConfiguration}`;
|
||||
const moduleMetadataRegistry = await readFile("website/src/features/toolboxes/toolboxModuleRegistry.js", "utf8");
|
||||
const notepadModule = await readFile("website/src/features/toolboxes/modules/NotepadModule.jsx", "utf8");
|
||||
const notepadToolbar = await readFile("website/src/features/toolboxes/modules/NotepadToolbar.jsx", "utf8");
|
||||
|
|
@ -139,7 +141,7 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
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 taskPlannerSettings = await readFile("website/src/features/toolboxes/modules/TaskPlannerSettings.jsx", "utf8");
|
||||
const taskPlannerBundle = `${taskPlannerBranch}\n${taskPlannerItem}\n${taskPlannerItemPanels}\n${taskPlannerReorder}\n${taskPlannerTopbar}\n${taskPlannerSettings}`;
|
||||
const taskPlannerBundle = `${taskPlannerModule}\n${taskPlannerBranch}\n${taskPlannerItem}\n${taskPlannerItemPanels}\n${taskPlannerReorder}\n${taskPlannerTopbar}\n${taskPlannerSettings}`;
|
||||
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 goalTreeParts = await readFile("website/src/features/toolboxes/modules/GoalTreeParts.jsx", "utf8");
|
||||
|
|
@ -240,12 +242,18 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(moduleShell, /module-scroll-button/);
|
||||
assert.match(moduleShell, /module-collapse-button/);
|
||||
assert.match(moduleShell, /aria-expanded=\{!collapsed\}/);
|
||||
assert.match(moduleShell, /!collapsed &&/);
|
||||
assert.match(moduleShell, /!collapsed \|\| configurationOpen/);
|
||||
assert.match(moduleShell, /module-configuration-button/);
|
||||
assert.match(moduleShell, /aria-controls=\{configurationId\}/);
|
||||
assert.match(moduleShell, /module-configuration-panel/);
|
||||
assert.match(moduleShell, /hidden=\{collapsed\}/);
|
||||
assert.match(moduleConfiguration, /createPortal/);
|
||||
assert.doesNotMatch(moduleShell, /module-exchange-button/);
|
||||
assert.match(addToolControls, /Icon name="dropdown"/);
|
||||
assert.match(addToolControls, /CompactDropdown/);
|
||||
assert.doesNotMatch(source, /<select value="" onChange=\{\(event\) => addModule/);
|
||||
assert.match(notepadModule, /export function NotepadModule/);
|
||||
assert.match(notepadModule, /TextExchangeActions/);
|
||||
assert.match(notepadModule, /ModuleTextExchangeConfiguration/);
|
||||
assert.match(notepadModule, /type="notepad"/);
|
||||
assert.match(notepadToolbar, /export function NotepadToolbar/);
|
||||
assert.match(notepadToolbar, /FormattingColorDropdown/);
|
||||
|
|
@ -300,7 +308,7 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(timerBundle, /useInlineEdit/);
|
||||
assert.match(timerModule, /useDraftForm/);
|
||||
assert.match(timerModule, /normalizeTimerData/);
|
||||
assert.match(timerModule, /TextExchangeActions/);
|
||||
assert.match(timerModule, /ModuleTextExchangeConfiguration/);
|
||||
assert.match(calendarModule, /export function CalendarModule/);
|
||||
assert.match(calendarModule, /normalizeCalendarData/);
|
||||
assert.match(calendarBundle, /calendar-mode-choice/);
|
||||
|
|
@ -322,7 +330,7 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(calendarBundle, /data-size=\{eventSurfaceSize\}/);
|
||||
assert.match(calendarBundle, /is-compact/);
|
||||
assert.match(calendarController, /handleKeyDown/);
|
||||
assert.match(calendarContent, /TextExchangeActions/);
|
||||
assert.match(calendarContent, /ModuleTextExchangeConfiguration/);
|
||||
assert.match(calendarContent, /type="calendar"/);
|
||||
assert.match(calendarBundle, /repeatLabel/);
|
||||
assert.match(calendarBundle, /CalendarAlertModeSwitch/);
|
||||
|
|
@ -342,7 +350,7 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(goalTreeModule, /export function GoalTreeModule/);
|
||||
assert.match(goalTreeBundle, /normalizeGoalTreeData/);
|
||||
assert.match(goalTreeBundle, /applyGoalTreeDueResets/);
|
||||
assert.match(goalTreeModule, /TextExchangeActions/);
|
||||
assert.match(goalTreeModule, /ModuleTextExchangeConfiguration/);
|
||||
assert.match(goalTreeBundle, /DiagramConnectors/);
|
||||
assert.match(goalTreeEditor, /export function GoalTreeEditor/);
|
||||
assert.match(goalTreeEditor, /Tabs/);
|
||||
|
|
@ -388,14 +396,14 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(equipmentPlannerEditors, /export \{ CharacteristicsEditor \}/);
|
||||
assert.match(equipmentPlannerEditors, /export \{ MaterialsEditor \}/);
|
||||
assert.match(equipmentPlannerEditors, /export \{ SocketItemsEditor \}/);
|
||||
assert.match(equipmentPlannerModule, /TextExchangeActions/);
|
||||
assert.match(equipmentPlannerModule, /ModuleTextExchangeConfiguration/);
|
||||
assert.match(equipmentPlannerItem, /export function EquipmentItem/);
|
||||
assert.match(equipmentPlannerPickers, /export function IconChoiceDropdown/);
|
||||
assert.match(equipmentPlannerSummary, /export function EquipmentPlannerSummary/);
|
||||
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, /ModuleTextExchangeConfiguration/);
|
||||
assert.match(checklistModule, /editing &&/);
|
||||
assert.match(checklistParts, /ChecklistItem/);
|
||||
assert.match(checklistParts, /export function ChecklistSection/);
|
||||
|
|
@ -420,7 +428,7 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(imagesModule, /editing &&/);
|
||||
assert.match(imagesModule, /clipboardData/);
|
||||
assert.match(imagesModule, /createImageAnnotationModule/);
|
||||
assert.match(imagesModule, /TextExchangeActions/);
|
||||
assert.match(imagesModule, /ModuleTextExchangeConfiguration/);
|
||||
assert.match(imagesModule, /image-annotate-button/);
|
||||
assert.match(imagesModule, /kind: "collection"/);
|
||||
assert.match(imagesModule, /galleryButton/);
|
||||
|
|
@ -432,15 +440,15 @@ test("toolbox module registry and modules expose expected behavior", async () =>
|
|||
assert.match(imageAnnotationModule, /context\.setImage/);
|
||||
assert.match(imageAnnotationModule, /createMarkerId/);
|
||||
assert.match(imageAnnotationModule, /<Icon name="zoom"/);
|
||||
assert.match(imageAnnotationModule, /TextExchangeActions/);
|
||||
assert.match(linksModule, /TextExchangeActions/);
|
||||
assert.match(imageAnnotationModule, /ModuleTextExchangeConfiguration/);
|
||||
assert.match(linksModule, /ModuleTextExchangeConfiguration/);
|
||||
assert.match(linksModule, /normalizeUrl/);
|
||||
assert.match(linksModule, /useGroupedReorder/);
|
||||
assert.match(linksModule, /link-drag-handle/);
|
||||
assert.match(countersModule, /useGroupedReorder/);
|
||||
assert.match(countersModule, /orientation: "horizontal"/);
|
||||
assert.match(countersModule, /counter-drag-handle/);
|
||||
assert.match(countersModule, /TextExchangeActions/);
|
||||
assert.match(countersModule, /ModuleTextExchangeConfiguration/);
|
||||
assert.match(timerModule, /useGroupedReorder/);
|
||||
assert.match(timerModule, /reorderEnabled={!data\.sortResults}/);
|
||||
assert.match(timerModule, /parentId=\{moduleId\}/);
|
||||
|
|
@ -510,6 +518,41 @@ test("task planner helpers keep hierarchy, reorder and reset behavior stable", (
|
|||
assert.equal(isTaskResetSoon({ id: "h", title: "H", type: "unique", checked: false }, resetData, new Date(2026, 0, 6, 7, 0).getTime()), false);
|
||||
});
|
||||
|
||||
test("memos planner links select reset alerts and clear removed targets", () => {
|
||||
const modules = [
|
||||
{ id: "planner-a", type: "taskPlanner", title: "Raids" },
|
||||
{ id: "planner-b", type: "taskPlanner", title: "Farm" },
|
||||
{ id: "notes", type: "memos" }
|
||||
];
|
||||
const plannerData = {
|
||||
"planner-a": {
|
||||
resetTime: "06:00",
|
||||
weeklyResetDay: 1,
|
||||
tasks: [
|
||||
{ id: "parent", title: "Parent", type: "unique", category: "Raid" },
|
||||
{ id: "soon", title: "Donjon", type: "daily" },
|
||||
{ id: "done", title: "Déjà faite", type: "daily", checked: true, checkedAt: Date.UTC(2026, 0, 2, 0) }
|
||||
],
|
||||
relations: [{ id: "relation", fromTaskId: "soon", toTaskId: "parent" }]
|
||||
},
|
||||
"planner-b": { resetTime: "18:00", tasks: [{ id: "later", title: "Récolte", type: "daily" }] }
|
||||
};
|
||||
const alerts = getPlannerTaskTickerItems({
|
||||
plannerLink: "all",
|
||||
modules,
|
||||
getPlannerData: (plannerId) => plannerData[plannerId],
|
||||
nowMs: Date.UTC(2026, 0, 2, 1),
|
||||
alertTemplate: "Tâche : {category}{title} expire bientôt"
|
||||
});
|
||||
|
||||
assert.deepEqual(alerts.map((item) => item.text), ["Tâche : Raid Donjon expire bientôt"]);
|
||||
assert.equal(alerts[0].kind, "task-warning");
|
||||
assert.equal(resolveMemosPlannerLink("missing", modules), "");
|
||||
assert.equal(getMemosDataAfterPlannerRemoval({ plannerLink: "planner-a" }, "planner-a", modules.slice(1)).plannerLink, "");
|
||||
assert.equal(getMemosDataAfterPlannerRemoval({ plannerLink: "all" }, "planner-a", modules.slice(1)).plannerLink, "all");
|
||||
assert.equal(getMemosDataAfterPlannerRemoval({ plannerLink: "all" }, "planner-b", [{ id: "notes", type: "memos" }]).plannerLink, "");
|
||||
});
|
||||
|
||||
test("equipment planner options expose stable formatting and socket helpers", () => {
|
||||
assert.deepEqual(SOCKET_SHAPES, ["ball", "jewel"]);
|
||||
assert.equal(formatValue(1.23456), "1.235");
|
||||
|
|
|
|||
|
|
@ -14,10 +14,34 @@ import { formatUpdatedAt, isHtmlEmpty, queryBlockFormat, queryCommandState } fro
|
|||
import { createBalancedTeams, drawWeightedEntries, filterExclusionsForPlayers, getDiceShape, rollDice } from "../website/src/features/toolboxes/modules/randomizerUtils.js";
|
||||
import { calculateAtLeastOneProbability, calculateDrawsForChance, calculateRngCostSummary, calculateRngRealDrawsSummary, calculateRngSummary, getAveragePositionStatus, getDurationPresetsForAverageHours, getEffectiveRate, normalizeCostString, normalizeRateString, simulateRngDraws } from "../website/src/features/toolboxes/modules/rngSimulatorUtils.js";
|
||||
import { getTimePatternRecurrenceMs, getTimePatternTargetMs } from "../website/src/features/toolboxes/modules/timerUtils.js";
|
||||
import { compactModuleDataForStorage, createToolboxExportPayload, moduleStorageKey, normalizeCalculatorData, normalizeChecklistData, normalizeCombosData, normalizeCountersData, normalizeEquipmentPlannerData, normalizeGoalTreeData, normalizeImageAnnotationData, normalizeLinksData, normalizeMemosData, normalizeModuleOrder, normalizeNotepadData, normalizeRandomizerData, normalizeRngSimulatorData, normalizeTableData, normalizeTaskPlannerData, normalizeTimerData, normalizeToolbox, normalizeUrl, summarizeEquipmentPlannerData } from "../website/src/features/toolboxes/storage/toolboxStorage.js";
|
||||
import { compactModuleDataForStorage, createToolboxExportPayload, moduleStorageKey, normalizeCalculatorData, normalizeChecklistData, normalizeCombosData, normalizeCountersData, normalizeEquipmentPlannerData, normalizeGoalTreeData, normalizeImageAnnotationData, normalizeLinksData, normalizeMemosData, normalizeModuleOrder, normalizeNotepadData, normalizeRandomizerData, normalizeRngSimulatorData, normalizeTableData, normalizeTaskPlannerData, normalizeTimerData, normalizeToolbox, normalizeUrl, remapMemosPlannerLink, summarizeEquipmentPlannerData } from "../website/src/features/toolboxes/storage/toolboxStorage.js";
|
||||
import { createToolboxSpace, deleteToolboxSpace, moveToolboxModule, reorderToolboxSpaces } from "../website/src/features/toolboxes/toolboxSpaceUtils.js";
|
||||
import { applyGroupedReorderOperation, completeGroupOrder, getBoundaryItemId, getGroupedEntries, moveGroupOrder, moveGroupOrderToEnd, moveGroupOrderToStart, moveItem, moveItemGroup } from "../website/src/hooks/useGroupedReorder.js";
|
||||
|
||||
test("memos persist and export a remapped task planner link", () => {
|
||||
const normalized = normalizeMemosData({ plannerLink: "planner", items: [] });
|
||||
assert.equal(normalized.plannerLink, "planner");
|
||||
assert.equal(compactModuleDataForStorage("memos", normalized).plannerLink, "planner");
|
||||
assert.equal(remapMemosPlannerLink({ plannerLink: "planner" }, (id) => `new-${id}`).plannerLink, "new-planner");
|
||||
assert.equal(remapMemosPlannerLink({ plannerLink: "all" }, () => "unused").plannerLink, "all");
|
||||
|
||||
const toolbox = normalizeToolbox({
|
||||
id: "toolbox",
|
||||
name: "Linked planners",
|
||||
modules: [
|
||||
{ id: "memos", type: "memos" },
|
||||
{ id: "planner", type: "taskPlanner" }
|
||||
]
|
||||
});
|
||||
const payload = createToolboxExportPayload(toolbox, {
|
||||
[moduleStorageKey(toolbox.id, "memos")]: { plannerLink: "planner" },
|
||||
[moduleStorageKey(toolbox.id, "planner")]: { tasks: [] }
|
||||
});
|
||||
const exportedMemos = payload.toolbox.modules.find((module) => module.type === "memos");
|
||||
const exportedPlanner = payload.toolbox.modules.find((module) => module.type === "taskPlanner");
|
||||
assert.equal(payload.modules[exportedMemos.id].plannerLink, exportedPlanner.id);
|
||||
});
|
||||
|
||||
function createTextImportContext() {
|
||||
let index = 0;
|
||||
return {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue