Compare commits

...
9 Commits
Author SHA1 Message Date
klemek ae030ea7f5 chore: 1.7.8
Python Lint CI / ruff (push) Successful in 4m55s
Python Lint CI / ty (push) Successful in 6m0s
Python Lint CI / ruff-format-check (push) Successful in 6m50s
TS Lint / ESLint (push) Successful in 7m58s
Docker Build / build (push) Successful in 13m35s
TS Lint / Oxlint (push) Successful in 7m9s
TS Lint / TypeScript (push) Successful in 7m33s
2026-09-16 11:35:19 +02:00
klemek 8007468abd fix(dates): invalid UTC in RRULE 2026-09-16 11:35:01 +02:00
klemek f3cd3b8086 style: show one-time tasks in italic
TS Lint / Oxlint (push) Successful in 1m42s
TS Lint / ESLint (push) Successful in 1m58s
TS Lint / TypeScript (push) Successful in 1m58s
Docker Build / build (push) Successful in 5m33s
2026-09-06 11:59:01 +02:00
klemek c91fc10f9f chore: 1.7.7
Python Lint CI / ruff-format-check (push) Successful in 2m46s
Python Lint CI / ty (push) Successful in 3m32s
TS Lint / Oxlint (push) Successful in 3m7s
TS Lint / ESLint (push) Successful in 3m16s
Docker Build / build (push) Successful in 7m29s
TS Lint / TypeScript (push) Successful in 1m50s
Python Lint CI / ruff (push) Successful in 3m4s
2026-08-10 12:48:31 +02:00
klemek ffab02a3f9 fix: rrule next reset
TS Lint / Oxlint (push) Canceled after 44s
TS Lint / ESLint (push) Canceled after 45s
Docker Build / build (push) Canceled after 45s
TS Lint / TypeScript (push) Canceled after 44s
2026-08-10 12:48:25 +02:00
klemek aeeabe81c7 chore: 1.7.6
Python Lint CI / ruff (push) Successful in 2m38s
Python Lint CI / ruff-format-check (push) Successful in 3m51s
Python Lint CI / ty (push) Successful in 3m22s
Docker Build / build (push) Successful in 7m56s
TS Lint / ESLint (push) Successful in 4m50s
TS Lint / Oxlint (push) Successful in 3m40s
TS Lint / TypeScript (push) Successful in 3m44s
2026-08-02 09:48:32 +02:00
klemek 87eff715f6 fix: rrule detection 2026-08-02 09:48:28 +02:00
klemek 63c80f0bae chore: 1.7.5
Python Lint CI / ruff (push) Successful in 5m57s
Python Lint CI / ruff-format-check (push) Successful in 6m31s
Python Lint CI / ty (push) Successful in 6m31s
TS Lint / ESLint (push) Successful in 6m1s
TS Lint / Oxlint (push) Successful in 5m17s
Docker Build / build (push) Successful in 14m5s
TS Lint / TypeScript (push) Successful in 5m27s
2026-08-02 09:40:44 +02:00
klemek f36c8ba315 fix: sorting resets 2026-08-02 09:40:41 +02:00
8 changed files with 43 additions and 12 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "tout-doux",
"version": "1.7.4",
"version": "1.7.8",
"private": true,
"type": "module",
"engines": {
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "tout-doux"
version = "1.7.4"
version = "1.7.8"
description = ""
requires-python = ">=3.14"
dependencies = [
+7 -3
View File
@@ -1,24 +1,28 @@
<script setup lang="ts">
import { parseDateInput, formatDateInput } from "@/lib/dates";
const emit = defineEmits<(e: "input"|"change", value: Date) => void>();
const date = defineModel<Date>({ required: true });
function onInput(event: Event) {
const inputValue = (event.target as HTMLInputElement).value;
date.value = new Date(inputValue);
date.value = parseDateInput(inputValue);
emit('input', date.value);
}
function onChange(event: Event) {
const inputValue = (event.target as HTMLInputElement).value;
date.value = new Date(inputValue);
date.value = parseDateInput(inputValue);
emit('change', date.value);
}
</script>
<template>
<input
:value="date.toISOString().substring(0, 10)"
:value="formatDateInput(date)"
type="date"
@input="onInput"
@change="onChange"
+1 -1
View File
@@ -173,7 +173,7 @@ watch(task, () => {
:class="showMissed && missed ? 'text-error' : ''"
@click.prevent="onOpen"
>
<div>{{ task.name }}</div>
<div :class="isOneTime(task.reset_cron) ? 'italic' : ''">{{ task.name }}</div>
<div
v-if="showLastChecked && !checked && task.check_date"
class="font-light text-xs italic"
+16
View File
@@ -1,6 +1,22 @@
import { formatDistanceToNow } from "date-fns";
import { enUS, fr } from "date-fns/locale";
export function parseDateInput(rawDate: string): Date {
const parts = rawDate.split('-').map(part => parseInt(part, 10));
if (!parts[0] || !parts[1] || !parts[2]) {
return new Date(new Date().setHours(0, 0, 0, 0))
}
return new Date(parts[0], parts[1] - 1, parts[2]);
}
export function formatDateInput(date: Date): string {
return [
date.getFullYear().toFixed(0),
(date.getMonth() + 1).toFixed(0).padStart(2, '0'),
(date.getDate()).toFixed(0).padStart(2, '0'),
].join('-');
}
export function relativeTime(date: Date, locale: string): string {
return formatDistanceToNow(date, {
addSuffix: true,
+6 -3
View File
@@ -22,7 +22,7 @@ export function parseCron(cron: string | null): {
value: 0,
date: null,
};
} else if (cron.startsWith("RRULE:") || cron.startsWith("DTSTART:")) {
} else if (isRrule(cron)) {
return parseRrule(cron);
} else if ((value = /^0 (\d+) \* \* \*$/i.exec(cron)) !== null) {
return {
@@ -75,6 +75,10 @@ export function parseCron(cron: string | null): {
}
}
function isRrule(cron: string): boolean {
return cron.startsWith("RRULE:") || cron.startsWith("DTSTART:");
}
export function parseRrule(rule: string): {
type: CronType;
value: number;
@@ -193,11 +197,10 @@ export function nextCronReset(
return null;
}
try {
if (cron.startsWith("RRULE:")) {
if (isRrule(cron)) {
try {
const rule = new RRule({
...RRule.parseString(cron),
count: 2,
});
return rule.after(date) ?? null;
+10 -2
View File
@@ -50,6 +50,14 @@ export function taskFutureReset(task: Task): Date | null {
);
}
export function taskSortReset(task: Task): Date | null {
let resetDate = taskNextReset(task);
while (resetDate !== null && resetDate.getTime() < new Date().getTime()) {
resetDate = nextCronReset(resetDate, task.reset_cron, task.id);
}
return resetDate;
}
export function taskCompleted(task: Task): boolean {
if (task.check_date === null) {
return false;
@@ -94,8 +102,8 @@ export function taskSortFunction(
case SortType.RESET_DATE:
return (a: Task, b: Task) =>
(reverse ? -1 : 1) *
((taskNextReset(a) ?? fallbackDate(a)).getTime() -
(taskNextReset(b) ?? fallbackDate(b)).getTime());
((taskSortReset(a) ?? fallbackDate(a)).getTime() -
(taskSortReset(b) ?? fallbackDate(b)).getTime());
case SortType.NAME:
return (a: Task, b: Task) =>
(reverse ? -1 : 1) * a.name.localeCompare(b.name);
Generated
+1 -1
View File
@@ -323,7 +323,7 @@ asyncpg = [
[[package]]
name = "tout-doux"
version = "1.7.4"
version = "1.7.8"
source = { editable = "." }
dependencies = [
{ name = "flask", extra = ["async"] },