Improve task planner reset settings
All checks were successful
Deploy Sokko G / deploy (push) Successful in 6s

This commit is contained in:
Shinuwa 2026-08-03 16:46:44 +02:00
parent d54a93e1f4
commit ee1c8afa9a
7 changed files with 180 additions and 63 deletions

View file

@ -485,6 +485,10 @@
"uncategorizedDropTitle": "Sans catégorie",
"showCategoryTitle": "Afficher cette catégorie",
"hideCategoryTitle": "Réduire cette catégorie",
"taskDailyResetTimeLabel": "Heure quotidienne",
"inheritResetTime": "Utiliser l'heure globale",
"taskResetLabel": "Réinitialisation",
"noTaskResetLabel": "Aucun reset",
"taskWeeklyResetDayLabel": "Jour hebdo",
"inheritWeeklyResetDay": "Global",
"linksLabel": "Parent",

View file

@ -55,7 +55,7 @@ function applyDueResets(data, now = Date.now()) {
const tasks = data.tasks.map((task) => {
if (task.type === "unique") return task;
const resetAt = task.type === "daily"
? getLatestDailyReset(now, data.resetTime)
? getLatestDailyReset(now, task.dailyResetTime || data.resetTime)
: getLatestWeeklyReset(now, data.resetTime, task.weeklyResetDay ?? data.weeklyResetDay);
latestResetAt = Math.max(latestResetAt, resetAt);
if (!task.checked || resetAt <= (task.checkedAt || data.lastResetAt)) return task;
@ -845,6 +845,7 @@ function TaskPlannerItem({
const nextType = event.target.value;
const nextTask = { ...current, type: nextType };
if (nextType !== "weekly") delete nextTask.weeklyResetDay;
if (nextType !== "daily") delete nextTask.dailyResetTime;
return nextTask;
})}
aria-label={textContent.typeLabel || "Type"}
@ -909,41 +910,22 @@ function TaskPlannerItem({
)}
{settingsOpen && (
<div className="task-planner-task-settings">
{task.type === "weekly" && (
<label className="task-planner-weekly-override">
<span>{textContent.taskWeeklyResetDayLabel || "Jour hebdo"}</span>
<select
value={task.weeklyResetDay ?? ""}
onChange={(event) => onUpdateTask(task.id, (current) => {
const nextTask = { ...current };
if (event.target.value === "") {
delete nextTask.weeklyResetDay;
} else {
nextTask.weeklyResetDay = Number(event.target.value);
}
return nextTask;
})}
>
<option value="">{textContent.inheritWeeklyResetDay || "Global"}</option>
{WEEK_DAYS.map((day) => <option key={day.value} value={day.value}>{day.label}</option>)}
</select>
</label>
)}
<LinksEditor
label={textContent.linksLabel || "Parent"}
relations={relations}
task={task}
tasks={tasks}
parentMap={parentMap}
relationTargets={relationTargets}
emptyLabel={textContent.emptyLinks || "Aucun parent"}
addLabel={textContent.addLinkLabel || "Définir un parent"}
removeLabel={textContent.removeRelationTitle || "Retirer"}
prerequisiteLabel={textContent.prerequisiteCheckboxLabel || "Pré requis"}
onAddLinkTarget={onAddLinkTarget}
onRemoveLinkTarget={onRemoveLinkTarget}
onSetPrerequisite={onSetPrerequisite}
/>
<TaskResetSettings task={task} data={data} textContent={textContent} onUpdateTask={onUpdateTask} />
<LinksEditor
label={textContent.linksLabel || "Parent"}
relations={relations}
task={task}
tasks={tasks}
parentMap={parentMap}
relationTargets={relationTargets}
emptyLabel={textContent.emptyLinks || "Aucun parent"}
addLabel={textContent.addLinkLabel || "Définir un parent"}
removeLabel={textContent.removeRelationTitle || "Retirer"}
prerequisiteLabel={textContent.prerequisiteCheckboxLabel || "Pré requis"}
onAddLinkTarget={onAddLinkTarget}
onRemoveLinkTarget={onRemoveLinkTarget}
onSetPrerequisite={onSetPrerequisite}
/>
</div>
)}
{children && <ul className="task-planner-list task-planner-child-list">{children}</ul>}
@ -951,6 +933,73 @@ function TaskPlannerItem({
);
}
function TaskResetSettings({ task, data, textContent, onUpdateTask }) {
if (task.type === "daily") {
const hasOverride = Boolean(task.dailyResetTime);
return (
<div className="task-planner-reset-override">
<span>{textContent.taskDailyResetTimeLabel || "Heure quotidienne"}</span>
<div className="task-planner-reset-control">
<input
type="time"
value={task.dailyResetTime || data.resetTime}
onChange={(event) => onUpdateTask(task.id, (current) => {
const nextTask = { ...current };
if (event.target.value && event.target.value !== data.resetTime) nextTask.dailyResetTime = event.target.value;
else delete nextTask.dailyResetTime;
return nextTask;
})}
aria-label={`${textContent.taskDailyResetTimeLabel || "Heure quotidienne"} ${task.title}`}
/>
<button
type="button"
onClick={() => onUpdateTask(task.id, (current) => {
const nextTask = { ...current };
delete nextTask.dailyResetTime;
return nextTask;
})}
disabled={!hasOverride}
title={textContent.inheritResetTime || "Utiliser l'heure globale"}
aria-label={`${textContent.inheritResetTime || "Utiliser l'heure globale"} ${task.title}`}
>
<Icon name="rubber" />
</button>
</div>
</div>
);
}
if (task.type === "weekly") {
return (
<label className="task-planner-reset-override">
<span>{textContent.taskWeeklyResetDayLabel || "Jour hebdo"}</span>
<select
value={task.weeklyResetDay ?? ""}
onChange={(event) => onUpdateTask(task.id, (current) => {
const nextTask = { ...current };
if (event.target.value === "") {
delete nextTask.weeklyResetDay;
} else {
nextTask.weeklyResetDay = Number(event.target.value);
}
return nextTask;
})}
>
<option value="">{textContent.inheritWeeklyResetDay || "Global"}</option>
{WEEK_DAYS.map((day) => <option key={day.value} value={day.value}>{day.label}</option>)}
</select>
</label>
);
}
return (
<div className="task-planner-reset-override is-empty">
<span>{textContent.taskResetLabel || "Réinitialisation"}</span>
<p>{textContent.noTaskResetLabel || "Aucun reset"}</p>
</div>
);
}
function LinksEditor({ label, relations, task, tasks, parentMap, relationTargets, emptyLabel, addLabel, removeLabel, prerequisiteLabel, onAddLinkTarget, onRemoveLinkTarget, onSetPrerequisite }) {
const descendantIds = new Set();
function collectDescendants(taskId) {
@ -965,6 +1014,17 @@ function LinksEditor({ label, relations, task, tasks, parentMap, relationTargets
return (
<div className="task-planner-relations">
<span>{label}</span>
<select
value=""
onChange={(event) => {
onAddLinkTarget(task.id, event.target.value);
event.target.value = "";
}}
aria-label={addLabel}
>
<option value="">{addLabel}</option>
{availableTargets.map((target) => <option key={target.id} value={target.id}>{target.title}</option>)}
</select>
<div>
{relations.length ? relations.map((relation) => (
<div className="task-planner-relation-chip" key={relation.id}>
@ -984,17 +1044,6 @@ function LinksEditor({ label, relations, task, tasks, parentMap, relationTargets
</div>
)) : <em>{emptyLabel}</em>}
</div>
<select
value=""
onChange={(event) => {
onAddLinkTarget(task.id, event.target.value);
event.target.value = "";
}}
aria-label={addLabel}
>
<option value="">{addLabel}</option>
{availableTargets.map((target) => <option key={target.id} value={target.id}>{target.title}</option>)}
</select>
</div>
);
}

View file

@ -610,6 +610,7 @@ function normalizeTaskPlannerTask(task) {
checkedAt: normalizeTimestamp(task?.checkedAt)
};
if (category) normalized.category = category;
if (type === "daily" && isClockTimeString(task?.dailyResetTime)) normalized.dailyResetTime = task.dailyResetTime;
if (type === "weekly" && task?.weeklyResetDay !== undefined) normalized.weeklyResetDay = normalizeWeekDay(task.weeklyResetDay);
if (!normalized.checked) normalized.checkedAt = 0;
return normalized;
@ -869,6 +870,7 @@ export function compactModuleDataForStorage(type, value) {
if (task.category) compactTask.category = task.category;
if (task.checked) compactTask.checked = true;
if (task.checked && task.checkedAt) compactTask.checkedAt = task.checkedAt;
if (task.type === "daily" && task.dailyResetTime && task.dailyResetTime !== normalized.resetTime) compactTask.dailyResetTime = task.dailyResetTime;
if (task.type === "weekly" && task.weeklyResetDay !== undefined) compactTask.weeklyResetDay = task.weeklyResetDay;
return compactTask;
});

View file

@ -1948,14 +1948,14 @@ textarea:focus {
}
.task-planner-settings label,
.task-planner-weekly-override {
.task-planner-reset-override {
display: grid;
gap: 6px;
min-width: 0;
}
.task-planner-settings span,
.task-planner-weekly-override span,
.task-planner-reset-override > span,
.task-planner-relations > span {
color: var(--color-text-secondary);
font-size: var(--font-size-xs);
@ -1967,9 +1967,11 @@ textarea:focus {
.task-planner-settings input,
.task-planner-add-form select,
.task-planner-type-select,
.task-planner-weekly-override select,
.task-planner-reset-override select,
.task-planner-reset-override input,
.task-planner-relations select {
width: 100%;
min-height: 36px;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background-color: rgba(15, 23, 42, 0.64);
@ -1979,7 +1981,7 @@ textarea:focus {
.task-planner-settings select option,
.task-planner-add-form select option,
.task-planner-type-select option,
.task-planner-weekly-override select option,
.task-planner-reset-override select option,
.task-planner-relations select option {
background: #11182f;
color: var(--color-text-primary);
@ -2349,8 +2351,8 @@ textarea:focus {
.task-planner-task-settings {
display: grid;
grid-template-columns: minmax(130px, 0.32fr) minmax(0, 1fr);
gap: 10px;
grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
gap: 12px;
align-items: start;
padding: 10px;
border: 1px solid rgba(165, 180, 252, 0.1);
@ -2358,6 +2360,50 @@ textarea:focus {
background: rgba(7, 10, 24, 0.24);
}
.task-planner-reset-control {
display: grid;
grid-template-columns: minmax(0, 1fr) 34px;
gap: 8px;
align-items: center;
}
.task-planner-reset-control button {
display: inline-grid;
width: 34px;
min-width: 34px;
min-height: 36px;
place-items: center;
padding: 0;
border: 1px solid rgba(165, 180, 252, 0.12);
border-radius: var(--radius-md);
background: rgba(21, 26, 48, 0.58);
color: var(--color-text-secondary);
}
.task-planner-reset-control button:disabled {
opacity: 0.42;
cursor: not-allowed;
}
.task-planner-reset-control button .ui-icon {
width: 14px;
height: 14px;
}
.task-planner-reset-override.is-empty p {
display: grid;
min-height: 36px;
align-items: center;
margin: 0;
padding: 0 12px;
border: 1px solid rgba(165, 180, 252, 0.08);
border-radius: var(--radius-md);
background: rgba(15, 23, 42, 0.34);
color: var(--color-text-muted);
font-size: var(--font-size-sm);
font-weight: 800;
}
.task-planner-relations {
display: grid;
grid-column: span 1;
@ -2367,8 +2413,8 @@ textarea:focus {
.task-planner-relations > div {
display: grid;
gap: 5px;
min-height: 28px;
gap: 6px;
min-height: 36px;
}
.task-planner-relations em {
@ -2434,10 +2480,16 @@ textarea:focus {
}
.task-planner-relations select {
min-height: 34px;
min-height: 36px;
padding-inline: 10px;
}
@media (max-width: 760px) {
.task-planner-task-settings {
grid-template-columns: 1fr;
}
}
.combos-module {
display: grid;
gap: 12px;
@ -3497,7 +3549,7 @@ button.combo-input-token.combo-value-grab .ui-icon {
.task-planner-settings select,
.task-planner-add-form select,
.task-planner-type-select,
.task-planner-weekly-override select,
.task-planner-reset-override select,
.task-planner-relations select {
appearance: none;
-webkit-appearance: none;
@ -3527,8 +3579,8 @@ button.combo-input-token.combo-value-grab .ui-icon {
.task-planner-add-form select:focus,
.task-planner-type-select:hover,
.task-planner-type-select:focus,
.task-planner-weekly-override select:hover,
.task-planner-weekly-override select:focus,
.task-planner-reset-override select:hover,
.task-planner-reset-override select:focus,
.task-planner-relations select:hover,
.task-planner-relations select:focus {
border-color: rgba(196, 181, 253, 0.22);
@ -4296,6 +4348,7 @@ button.combo-input-token.combo-value-grab .ui-icon {
.task-planner-settings-toggle:hover,
.task-planner-dropdown-button:hover,
.task-planner-settings-button:hover,
.task-planner-reset-control button:hover:not(:disabled),
.task-planner-relation-chip button:hover,
.module-scroll-button:hover,
.tool-split-scroll-toggle:hover,