feat: title as menu link and updated lists history
Docker Build / build (push) Has been cancelled
TS Lint / ESLint (push) Has been cancelled
TS Lint / Oxlint (push) Has been cancelled
TS Lint / TypeScript (push) Has been cancelled

This commit is contained in:
2026-07-16 11:47:35 +02:00
parent fb48f64ed8
commit 765f3e1f8a
5 changed files with 70 additions and 41 deletions
+3 -2
View File
@@ -27,7 +27,8 @@ onMounted(() => {
<div <div
class="max-w-md min-w-96 bg-base-100 rounded-box shadow-md p-4" class="max-w-md min-w-96 bg-base-100 rounded-box shadow-md p-4"
> >
<h1 <router-link
to="/"
class="text-4xl font-bold opacity-60 tracking-wide select-none" class="text-4xl font-bold opacity-60 tracking-wide select-none"
> >
<list-todo-icon <list-todo-icon
@@ -36,7 +37,7 @@ onMounted(() => {
class="inline" class="inline"
/> />
Tout Doux Tout Doux
</h1> </router-link>
<router-view /> <router-view />
</div> </div>
</div> </div>
-20
View File
@@ -1,20 +0,0 @@
import { getCookie, setCookie } from "@/lib/cookies";
const COOKIE_NAME = "recent-lists";
const MAX_RECENT_LISTS = 32;
export function getRecentLists(): string[] {
const rawData = getCookie(COOKIE_NAME, "");
if (!rawData) {
return [];
}
return rawData.split(",");
}
export function saveRecentList(list: string): void {
const recentLists = [list]
.concat(getRecentLists())
.filter((v, i, a) => a.indexOf(v) === i)
.slice(0, MAX_RECENT_LISTS);
setCookie(COOKIE_NAME, recentLists.join(","));
}
+25
View File
@@ -0,0 +1,25 @@
import { ref } from "vue";
import { defineStore } from "pinia";
import { getCookie, setCookie } from "@/lib/cookies";
const COOKIE_NAME = "recent-lists";
const MAX_RECENT_LISTS = 32;
export const useListStore = defineStore("lists", () => {
const lists = ref<string[]>([]);
const rawData = getCookie(COOKIE_NAME, "");
if (rawData) {
lists.value = rawData.split(",");
}
function addList(list: string) {
lists.value = [list]
.concat(lists.value)
.filter((v, i, a) => a.indexOf(v) === i)
.slice(0, MAX_RECENT_LISTS);
setCookie(COOKIE_NAME, lists.value.join(","));
}
return { lists, addList };
});
+3 -2
View File
@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { getRecentLists } from "@/lib/lists"; import { useListStore } from "@/stores/lists";
import { ArrowBigRightDashIcon, DicesIcon, HistoryIcon } from "@lucide/vue"; import { ArrowBigRightDashIcon, DicesIcon, HistoryIcon } from "@lucide/vue";
import { generateSlug } from "random-word-slugs"; import { generateSlug } from "random-word-slugs";
import { defineAsyncComponent, onBeforeMount, ref } from "vue"; import { defineAsyncComponent, onBeforeMount, ref } from "vue";
@@ -9,7 +9,8 @@ const RecentItem = defineAsyncComponent(
); );
const listNameInput = ref<string>(generateSlug(4)); const listNameInput = ref<string>(generateSlug(4));
const recentLists = ref<string[]>(getRecentLists());
const { lists: recentLists } = useListStore();
function onRandom() { function onRandom() {
listNameInput.value = generateSlug(4); listNameInput.value = generateSlug(4);
+36 -14
View File
@@ -19,9 +19,9 @@ import { SortType } from "@/enums";
import { booleanCookieRef, enumCookieRef } from "@/lib/cookies"; import { booleanCookieRef, enumCookieRef } from "@/lib/cookies";
import { useAlertStore } from "@/stores/alerts"; import { useAlertStore } from "@/stores/alerts";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { onBeforeRouteUpdate, useRoute } from "vue-router"; import { onBeforeRouteLeave, onBeforeRouteUpdate, useRoute } from "vue-router";
import { shareLink } from "@/lib/share"; import { shareLink } from "@/lib/share";
import { saveRecentList } from "@/lib/lists"; import { useListStore } from "@/stores/lists";
const route = useRoute(); const route = useRoute();
@@ -29,6 +29,7 @@ const list = ref<string | null>(null);
const loading = ref<boolean>(true); const loading = ref<boolean>(true);
const taskInput = ref<string>(""); const taskInput = ref<string>("");
const tasks = ref<Task[]>([]); const tasks = ref<Task[]>([]);
const refreshTimeout = ref<number>(0);
const sortType = enumCookieRef<SortType>("sSortType", SortType.CREATED_AT); const sortType = enumCookieRef<SortType>("sSortType", SortType.CREATED_AT);
const sortReverse = booleanCookieRef("sortReverse", false); const sortReverse = booleanCookieRef("sortReverse", false);
@@ -49,18 +50,21 @@ const allCompleted = computed<boolean>(
const i18n = useI18n(); const i18n = useI18n();
const { alertSuccess, alertError, alertWarning } = useAlertStore(); const { alertSuccess, alertError, alertWarning } = useAlertStore();
const { addList } = useListStore();
function fetchTasks() { function fetchTasks(requestedList: string) {
if (list.value) { const initialList = list.value;
if (list.value === requestedList) {
getTasks(list.value) getTasks(list.value)
.then((data: Task[]) => { .then((data: Task[]) => {
if (list.value === initialList) {
tasks.value = data; tasks.value = data;
reSortTasks(); reSortTasks(true);
loading.value = false; loading.value = false;
if (data.length && list.value) { refreshTimeout.value = setTimeout(() => {
saveRecentList(list.value); fetchTasks(list.value ?? "");
}, 10000);
} }
setTimeout(fetchTasks, 10000);
}) })
.catch(() => { .catch(() => {
if (tasks.value.length !== 0) { if (tasks.value.length !== 0) {
@@ -72,9 +76,14 @@ function fetchTasks() {
} }
} }
function reSortTasks() { function reSortTasks(update = false) {
tasks.value = sortTasks(tasks.value, sortType.value, sortReverse.value); tasks.value = sortTasks(tasks.value, sortType.value, sortReverse.value);
if (update) {
updateTitle(); updateTitle();
if (tasks.value.length && list.value) {
addList(list.value);
}
}
} }
function onNewTask() { function onNewTask() {
@@ -85,7 +94,7 @@ function onNewTask() {
}) })
.then((data: Task) => { .then((data: Task) => {
tasks.value.push(data); tasks.value.push(data);
reSortTasks(); reSortTasks(true);
alertSuccess(i18n.t("alerts.task_created")); alertSuccess(i18n.t("alerts.task_created"));
}) })
.catch(() => { .catch(() => {
@@ -97,6 +106,10 @@ function onNewTask() {
} }
} }
function onUpdateTask() {
reSortTasks(true);
}
function onCloneTask(task: Task) { function onCloneTask(task: Task) {
if (list.value) { if (list.value) {
createTask(list.value, { createTask(list.value, {
@@ -105,6 +118,7 @@ function onCloneTask(task: Task) {
}) })
.then((data: Task) => { .then((data: Task) => {
tasks.value.push(data); tasks.value.push(data);
reSortTasks(true);
alertSuccess(i18n.t("alerts.task_cloned")); alertSuccess(i18n.t("alerts.task_cloned"));
}) })
.catch(() => { .catch(() => {
@@ -116,6 +130,7 @@ function onCloneTask(task: Task) {
function onDeleteTask(task: Task) { function onDeleteTask(task: Task) {
deleteTask(task.id) deleteTask(task.id)
.then(() => { .then(() => {
reSortTasks(true);
alertSuccess(i18n.t("alerts.task_deleted")); alertSuccess(i18n.t("alerts.task_deleted"));
}) })
.catch(() => { .catch(() => {
@@ -159,12 +174,16 @@ function updateTitle() {
onBeforeMount(() => { onBeforeMount(() => {
list.value = (route.params.list as string).slice(0, 128); list.value = (route.params.list as string).slice(0, 128);
fetchTasks(); fetchTasks(list.value);
}); });
onBeforeRouteUpdate((to) => { onBeforeRouteUpdate((to) => {
list.value = (to.params.list as string).slice(0, 128); list.value = (to.params.list as string).slice(0, 128);
fetchTasks(); fetchTasks(list.value);
});
onBeforeRouteLeave(() => {
clearTimeout(refreshTimeout.value);
}); });
</script> </script>
@@ -183,7 +202,10 @@ onBeforeRouteUpdate((to) => {
:stroke-width="1" :stroke-width="1"
/></span> /></span>
</div> </div>
<div v-if="!loading && empty" class="mt-4 mb-4 italic text-xl font-extralight"> <div
v-if="!loading && empty"
class="mt-4 mb-4 italic text-xl font-extralight"
>
<search-icon class="inline" /> <search-icon class="inline" />
<span v-html="$t('empty_hint')"></span> <span v-html="$t('empty_hint')"></span>
</div> </div>
@@ -201,7 +223,7 @@ onBeforeRouteUpdate((to) => {
v-model="tasks[i]" v-model="tasks[i]"
:show-last-checked="showLastChecked" :show-last-checked="showLastChecked"
:show-next-reset="showNextReset" :show-next-reset="showNextReset"
@updated="updateTitle" @updated="onUpdateTask"
@clone="onCloneTask" @clone="onCloneTask"
@delete="onDeleteTask" @delete="onDeleteTask"
/> />