diff --git a/package.json b/package.json index e20e717..b293c7e 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,12 @@ "@tauri-apps/plugin-http": "~2.5.7", "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-store": "~2.4.2", + "@types/luxon": "^3.7.1", + "@vueuse/core": "^14.2.1", "daisyui": "^5.5.18", "jsencrypt": "^3.5.4", + "luxon": "^3.7.2", + "uuid": "^13.0.0", "vue": "^3.5.28", "vue-router": "^4.6.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17f09c5..e1f4260 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,12 +23,24 @@ importers: '@tauri-apps/plugin-store': specifier: ~2.4.2 version: 2.4.2 + '@types/luxon': + specifier: ^3.7.1 + version: 3.7.1 + '@vueuse/core': + specifier: ^14.2.1 + version: 14.2.1(vue@3.5.28(typescript@5.9.3)) daisyui: specifier: ^5.5.18 version: 5.5.18 jsencrypt: specifier: ^3.5.4 version: 3.5.4 + luxon: + specifier: ^3.7.2 + version: 3.7.2 + uuid: + specifier: ^13.0.0 + version: 13.0.0 vue: specifier: ^3.5.28 version: 3.5.28(typescript@5.9.3) @@ -615,6 +627,12 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/luxon@3.7.1': + resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==} + + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + '@vitejs/plugin-vue@6.0.4': resolution: {integrity: sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -674,6 +692,19 @@ packages: '@vue/shared@3.5.28': resolution: {integrity: sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ==} + '@vueuse/core@14.2.1': + resolution: {integrity: sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ==} + peerDependencies: + vue: ^3.5.0 + + '@vueuse/metadata@14.2.1': + resolution: {integrity: sha512-1ButlVtj5Sb/HDtIy1HFr1VqCP4G6Ypqt5MAo0lCgjokrk2mvQKsK2uuy0vqu/Ks+sHfuHo0B9Y9jn9xKdjZsw==} + + '@vueuse/shared@14.2.1': + resolution: {integrity: sha512-shTJncjV9JTI4oVNyF1FQonetYAiTBd+Qj7cY89SWbXSkx7gyhrgtEdF2ZAVWS1S3SHlaROO6F2IesJxQEkZBw==} + peerDependencies: + vue: ^3.5.0 + alien-signals@1.0.13: resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} @@ -810,6 +841,10 @@ packages: resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} engines: {node: '>= 12.0.0'} + luxon@3.7.2: + resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} + engines: {node: '>=12'} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -864,6 +899,10 @@ packages: engines: {node: '>=14.17'} hasBin: true + uuid@13.0.0: + resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} + hasBin: true + vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1285,6 +1324,10 @@ snapshots: '@types/estree@1.0.8': {} + '@types/luxon@3.7.1': {} + + '@types/web-bluetooth@0.0.21': {} + '@vitejs/plugin-vue@6.0.4(vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1))(vue@3.5.28(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.2 @@ -1377,6 +1420,19 @@ snapshots: '@vue/shared@3.5.28': {} + '@vueuse/core@14.2.1(vue@3.5.28(typescript@5.9.3))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 14.2.1 + '@vueuse/shared': 14.2.1(vue@3.5.28(typescript@5.9.3)) + vue: 3.5.28(typescript@5.9.3) + + '@vueuse/metadata@14.2.1': {} + + '@vueuse/shared@14.2.1(vue@3.5.28(typescript@5.9.3))': + dependencies: + vue: 3.5.28(typescript@5.9.3) + alien-signals@1.0.13: {} balanced-match@1.0.2: {} @@ -1495,6 +1551,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.31.1 lightningcss-win32-x64-msvc: 1.31.1 + luxon@3.7.2: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -1563,6 +1621,8 @@ snapshots: typescript@5.9.3: {} + uuid@13.0.0: {} + vite@7.3.1(jiti@2.6.1)(lightningcss@1.31.1): dependencies: esbuild: 0.27.3 diff --git a/src/App.vue b/src/App.vue index 0e48d17..dddfa54 100644 --- a/src/App.vue +++ b/src/App.vue @@ -12,7 +12,7 @@ const currentPath = computed(() => router.currentRoute.value.path);
-
+
diff --git a/src/components/TodoItem.vue b/src/components/TodoItem.vue new file mode 100644 index 0000000..cac24cd --- /dev/null +++ b/src/components/TodoItem.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/src/composables/useApi.ts b/src/composables/useApi.ts index 77ee8b2..71496a5 100644 --- a/src/composables/useApi.ts +++ b/src/composables/useApi.ts @@ -51,10 +51,10 @@ export function useApi() { const get = (endpoint: string, options: RequestInit = {}) => apiFetch(endpoint, { ...options, method: 'GET' }); - const post = (endpoint: string, body: BodyInit | null, options: RequestInit = {}) => + const post = (endpoint: string, body: unknown, options: RequestInit = {}) => apiFetch(endpoint, { ...options, method: 'POST', body: JSON.stringify(body) }); - const put = (endpoint: string, body: BodyInit | null, options: RequestInit = {}) => + const put = (endpoint: string, body: unknown, options: RequestInit = {}) => apiFetch(endpoint, { ...options, method: 'PUT', body: JSON.stringify(body) }); return { diff --git a/src/composables/useSettings.ts b/src/composables/useSettings.ts index 9b66ae3..b66700d 100644 --- a/src/composables/useSettings.ts +++ b/src/composables/useSettings.ts @@ -27,7 +27,7 @@ export function useSettings() { let password = readSettings.password ?? ''; if (password) { try { - password = decrypt(password); + password = decrypt(password) as string; } catch (error) { console.warn('Failed to decrypt stored password:', error); } @@ -41,7 +41,7 @@ export function useSettings() { const saveSettings = async () => { const encryptedPassword = settings.value.password - ? encrypt(settings.value.password) + ? encrypt(settings.value.password) as string : ''; await setValue('settings', { diff --git a/src/composables/useTasks.ts b/src/composables/useTasks.ts index 3d1d9f9..25659df 100644 --- a/src/composables/useTasks.ts +++ b/src/composables/useTasks.ts @@ -15,8 +15,7 @@ export function useTasks() { isLoading.value = true; error.value = null; try { - const response = await api.get('e5880167-9322-4d7b-8a38-e06bae8a7734/list'); - const data = await response.json(); + const data = await api.get('e5880167-9322-4d7b-8a38-e06bae8a7734/list').then((res) => res.json()); tasks.value = data.tasks ?? []; } catch (e: any) { error.value = e.message || 'Failed to fetch tasks'; @@ -45,15 +44,10 @@ export function useTasks() { archived: false, }; - await api.put('e5880167-9322-4d7b-8a38-e06bae8a7734/list', { tasks: [newTask] }); - - // Update local store (optimistic update or refetch) - // Since it's a PUT to '.../list' with {tasks: [task]}, it seems to add/update? - // Based on CreateScreen.vue, it just navigates back. - // To keep store in sync without full refetch, we could add it locally if we knew the full structure - // But maybe it's safer to refetch or at least push to local state if we have the full object. - // Let's refetch to be sure it's in sync with server. - await fetchTasks(true); + const data = await api.put('e5880167-9322-4d7b-8a38-e06bae8a7734/list', { tasks: [newTask] }).then((res) => res.json()); + if (data.tasks) { + tasks.value = data.tasks; + } } catch (e: any) { error.value = e.message || 'Failed to create task'; console.error('Error creating task:', e); @@ -63,11 +57,30 @@ export function useTasks() { } }; + const updateTask = async (task: Task) => { + console.log('updateTask',task); + isLoading.value = true; + error.value = null; + try { + const data = await api.put('e5880167-9322-4d7b-8a38-e06bae8a7734/list', { tasks: [task] }).then((res) => res.json()); + if (data.tasks) { + tasks.value = data.tasks; + } + } catch (e: any) { + error.value = e.message || 'Failed to update task'; + console.error('Error updating task:', e); + throw e; + } finally { + isLoading.value = false; + } + } + return { tasks, isLoading, error, fetchTasks, createTask, + updateTask, }; } diff --git a/src/screens/ListScreen.vue b/src/screens/ListScreen.vue index 789bd7f..d03e9a5 100644 --- a/src/screens/ListScreen.vue +++ b/src/screens/ListScreen.vue @@ -1,8 +1,10 @@