Refactor task data management with composables and remove direct API calls from components
This commit is contained in:
@@ -10,11 +10,7 @@ const currentPath = computed(() => router.currentRoute.value.path);
|
|||||||
<div class="overflow-hidden">
|
<div class="overflow-hidden">
|
||||||
|
|
||||||
<main class="pb-40 overflow-y-scroll h-screen">
|
<main class="pb-40 overflow-y-scroll h-screen">
|
||||||
<RouterView v-slot="{ Component }">
|
<RouterView />
|
||||||
<Transition name="fade">
|
|
||||||
<component :is="Component" />
|
|
||||||
</Transition>
|
|
||||||
</RouterView>
|
|
||||||
</main>
|
</main>
|
||||||
<div class="dock dock-xl bg-neutral-400">
|
<div class="dock dock-xl bg-neutral-400">
|
||||||
<RouterLink to="/create" :class="currentPath === '/create' ? 'dock-active' : ''">
|
<RouterLink to="/create" :class="currentPath === '/create' ? 'dock-active' : ''">
|
||||||
|
|||||||
43
src/composables/useApi.ts
Normal file
43
src/composables/useApi.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { fetch } from '@tauri-apps/plugin-http';
|
||||||
|
|
||||||
|
const BASE_URL = 'https://automation.deep-node.de/webhook';
|
||||||
|
const AUTH_HEADER = '';
|
||||||
|
|
||||||
|
export function useApi() {
|
||||||
|
const apiFetch = async (endpoint: string, options: RequestInit = {}) => {
|
||||||
|
const url = endpoint.startsWith('http') ? endpoint : `${BASE_URL}/${endpoint}`;
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': AUTH_HEADER,
|
||||||
|
...options.headers,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
...options,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`API call failed: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
|
const get = (endpoint: string, options: RequestInit = {}) =>
|
||||||
|
apiFetch(endpoint, { ...options, method: 'GET' });
|
||||||
|
|
||||||
|
const post = (endpoint: string, body: any, options: RequestInit = {}) =>
|
||||||
|
apiFetch(endpoint, { ...options, method: 'POST', body: JSON.stringify(body) });
|
||||||
|
|
||||||
|
const put = (endpoint: string, body: any, options: RequestInit = {}) =>
|
||||||
|
apiFetch(endpoint, { ...options, method: 'PUT', body: JSON.stringify(body) });
|
||||||
|
|
||||||
|
return {
|
||||||
|
get,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
fetch: apiFetch,
|
||||||
|
};
|
||||||
|
}
|
||||||
73
src/composables/useTasks.ts
Normal file
73
src/composables/useTasks.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
import { useApi } from './useApi.ts';
|
||||||
|
import { Task } from '../types.ts';
|
||||||
|
|
||||||
|
const tasks = ref<Task[]>([]);
|
||||||
|
const isLoading = ref(false);
|
||||||
|
const error = ref<string | null>(null);
|
||||||
|
|
||||||
|
export function useTasks() {
|
||||||
|
const api = useApi();
|
||||||
|
|
||||||
|
const fetchTasks = async (force = false) => {
|
||||||
|
if (tasks.value.length > 0 && !force) return;
|
||||||
|
|
||||||
|
isLoading.value = true;
|
||||||
|
error.value = null;
|
||||||
|
try {
|
||||||
|
const response = await api.get('e5880167-9322-4d7b-8a38-e06bae8a7734/list');
|
||||||
|
const data = await response.json();
|
||||||
|
tasks.value = data.tasks ?? [];
|
||||||
|
} catch (e: any) {
|
||||||
|
error.value = e.message || 'Failed to fetch tasks';
|
||||||
|
console.error('Error fetching tasks:', e);
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createTask = async (taskData: Partial<Task>) => {
|
||||||
|
isLoading.value = true;
|
||||||
|
error.value = null;
|
||||||
|
try {
|
||||||
|
// Get next ID as per current logic in CreateScreen.vue
|
||||||
|
const nextId = await api.get('d49dde4c-530d-46ee-8205-d1357563ac16')
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((json) => json.nextId as number)
|
||||||
|
.catch(() => null);
|
||||||
|
|
||||||
|
if (!nextId) throw new Error('Could not get next task ID');
|
||||||
|
|
||||||
|
const newTask: Partial<Task> = {
|
||||||
|
...taskData,
|
||||||
|
id_: nextId,
|
||||||
|
logs: [],
|
||||||
|
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);
|
||||||
|
} catch (e: any) {
|
||||||
|
error.value = e.message || 'Failed to create task';
|
||||||
|
console.error('Error creating task:', e);
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
tasks,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
fetchTasks,
|
||||||
|
createTask,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,25 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { fetch } from '@tauri-apps/plugin-http';
|
|
||||||
import { router } from '../router.ts';
|
import { router } from '../router.ts';
|
||||||
|
import { useTasks } from '../composables/useTasks.ts';
|
||||||
import { Task } from '../types.ts';
|
import { Task } from '../types.ts';
|
||||||
|
|
||||||
|
const { createTask } = useTasks();
|
||||||
|
|
||||||
const handleSubmit = async(e: Event) => {
|
const handleSubmit = async(e: Event) => {
|
||||||
const data = new FormData(e.target as HTMLFormElement);
|
const data = new FormData(e.target as HTMLFormElement);
|
||||||
const task: Partial<Task> = Object.fromEntries(data)
|
const task: Partial<Task> = Object.fromEntries(data)
|
||||||
|
|
||||||
const nextId = await fetch(
|
await createTask(task);
|
||||||
'https://automation.deep-node.de/webhook/d49dde4c-530d-46ee-8205-d1357563ac16',
|
|
||||||
{ method: 'GET', headers: { 'Content-Type': 'application/json', 'Authorization': 'Basic cGF1bDoxMG1hYmF1MTU=', }}
|
|
||||||
).then((response) => response.json()).then((json) => json.nextId as number).catch(() => null);
|
|
||||||
|
|
||||||
if (!nextId) return;
|
|
||||||
task.id_ = nextId;
|
|
||||||
task.logs = [];
|
|
||||||
|
|
||||||
await fetch(
|
|
||||||
'https://automation.deep-node.de/webhook/e5880167-9322-4d7b-8a38-e06bae8a7734/list',
|
|
||||||
{ method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ tasks: [task] } )}
|
|
||||||
);
|
|
||||||
await router.push('/');
|
await router.push('/');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { fetch } from '@tauri-apps/plugin-http';
|
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { Task, TaskStatus } from '../types.ts';
|
import { useTasks } from '../composables/useTasks.ts';
|
||||||
|
import { TaskStatus } from '../types.ts';
|
||||||
import { PhCaretDown, PhCaretUp, PhCheckSquare, PhDotsThree, PhPlay, PhSquare } from '@phosphor-icons/vue';
|
import { PhCaretDown, PhCaretUp, PhCheckSquare, PhDotsThree, PhPlay, PhSquare } from '@phosphor-icons/vue';
|
||||||
|
|
||||||
const rawTasks = ref<Task[]>([]);
|
const { tasks, fetchTasks } = useTasks();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
rawTasks.value = await fetch('https://automation.deep-node.de/webhook/e5880167-9322-4d7b-8a38-e06bae8a7734/list', { method: 'GET', headers: { 'Content-Type': 'application/json'} })
|
await fetchTasks();
|
||||||
.then(response => response.json())
|
|
||||||
.then((data: { tasks: Task[] }) => rawTasks.value = data.tasks ?? [])
|
|
||||||
.catch(() => []);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const visibleTasks = computed(() => rawTasks.value.filter(task => !task.archived))
|
const visibleTasks = computed(() => tasks.value.filter(task => !task.archived))
|
||||||
|
|
||||||
const categorizedTasks = computed(() => visibleTasks.value.reduce((acc, task) => {
|
const categorizedTasks = computed(() => visibleTasks.value.reduce((acc, task) => {
|
||||||
const tag = task.tag ?? 'Uncategorized';
|
const tag = task.tag ?? 'Uncategorized';
|
||||||
|
|||||||
Reference in New Issue
Block a user