diff --git a/apps/web/app/composables/useProjectApi.ts b/apps/web/app/composables/useProjectApi.ts index c793fa1..fd35ace 100644 --- a/apps/web/app/composables/useProjectApi.ts +++ b/apps/web/app/composables/useProjectApi.ts @@ -1,35 +1,8 @@ import type { Project, ProjectInsertDTO, ProjectUpdateDTO } from "@workspace/schema"; -interface ApiResponse { - data: T | null; - error: string | null; -} - -function getBaseUrl(): string { - const config = useRuntimeConfig(); - return config.public.apiBaseUrl as string; -} - -async function request(path: string, options?: RequestInit): Promise> { - try { - const res = await fetch(`${getBaseUrl()}${path}`, { - headers: { "Content-Type": "application/json" }, - ...options, - }); - - if (!res.ok) { - const body = await res.json().catch(() => null); - return { data: null, error: body?.message ?? `HTTP ${res.status}` }; - } - - const data = (await res.json()) as T; - return { data, error: null }; - } catch (err) { - return { data: null, error: err instanceof Error ? err.message : "Unknown error" }; - } -} - export function useProjectApi() { + const config = useRuntimeConfig(); + const apiBase = config.public.apiBaseUrl as string; const NOT_FOUND = -1; const projects = ref([]); @@ -39,67 +12,65 @@ export function useProjectApi() { async function fetchProjects(): Promise { loading.value = true; error.value = null; - - const { data, error: err } = await request("/projects"); - if (data) projects.value = data; - else error.value = err; - - loading.value = false; + try { + projects.value = await $fetch(`${apiBase}/projects`); + } catch (err) { + error.value = err instanceof Error ? err.message : "Unknown error"; + } finally { + loading.value = false; + } } async function createProject(body: Partial): Promise { loading.value = true; error.value = null; - - const { data, error: err } = await request("/projects", { - method: "POST", - body: JSON.stringify(body), - }); - - if (data) { - projects.value.push(data); - } else { - error.value = err; + try { + const created = await $fetch(`${apiBase}/projects`, { + method: "POST", + body, + }); + projects.value.push(created); + return created; + } catch (err) { + error.value = err instanceof Error ? err.message : "Unknown error"; + return null; + } finally { + loading.value = false; } - - loading.value = false; - return data; } async function updateProject(id: string, body: Partial): Promise { loading.value = true; error.value = null; - - const { data, error: err } = await request(`/projects/${id}`, { - method: "PATCH", - body: JSON.stringify(body), - }); - - if (data) { - const idx = projects.value.findIndex(p => p.id === id); - if (idx !== NOT_FOUND) projects.value[idx] = data; - } else { - error.value = err; + try { + const updated = await $fetch(`${apiBase}/projects/${id}`, { + method: "PATCH", + body, + }); + const idx = projects.value.findIndex((p) => p.id === id); + if (idx !== NOT_FOUND) projects.value[idx] = updated; + return updated; + } catch (err) { + error.value = err instanceof Error ? err.message : "Unknown error"; + return null; + } finally { + loading.value = false; } - - loading.value = false; - return data; } async function deleteProject(id: string): Promise { loading.value = true; error.value = null; - - const { error: err } = await request(`/projects/${id}`, { method: "DELETE" }); - - if (err) { - error.value = err; - } else { - projects.value = projects.value.filter(p => p.id !== id); + try { + await $fetch(`${apiBase}/projects/${id}`, { method: "DELETE" }); + projects.value = projects.value.filter((p) => p.id !== id); + return true; + } catch (err) { + error.value = err instanceof Error ? err.message : "Unknown error"; + return false; + } finally { + loading.value = false; } - - loading.value = false; - return !err; } return { projects, loading, error, fetchProjects, createProject, updateProject, deleteProject }; diff --git a/apps/web/app/pages/projects/[id]/edit.vue b/apps/web/app/pages/projects/[id]/edit.vue index a5625b6..90a6f1c 100644 --- a/apps/web/app/pages/projects/[id]/edit.vue +++ b/apps/web/app/pages/projects/[id]/edit.vue @@ -16,17 +16,14 @@ const project = ref(null); const loading = ref(true); const notFound = ref(false); -const config = useRuntimeConfig(); -const apiBase = config.public.apiBaseUrl as string; - onMounted(async () => { - const res = await fetch(`${apiBase}/projects/${route.params.id}`); - if (res.ok) { - project.value = await res.json(); - } else { + try { + project.value = await $fetch(`${useRuntimeConfig().public.apiBaseUrl as string}/projects/${route.params.id}`); + } catch { notFound.value = true; + } finally { + loading.value = false; } - loading.value = false; }); async function handleSubmit(body: { name: string; description?: string; status: string }) { diff --git a/apps/web/eslint.config.mjs b/apps/web/eslint.config.mjs index d58f8e5..ec371a8 100644 --- a/apps/web/eslint.config.mjs +++ b/apps/web/eslint.config.mjs @@ -1,14 +1,17 @@ // @ts-check -import withNuxt from "./.nuxt/eslint.config.mjs" -import { baseRules, } from "@workspace/tooling/eslint/base" +import withNuxt from "./.nuxt/eslint.config.mjs"; +import { baseRules, } from "@workspace/tooling/eslint/base"; export default withNuxt({ rules: { ...baseRules, "@stylistic/member-delimiter-style": "off", + "@stylistic/arrow-parens": "off", "vue/max-attributes-per-line": "off", "vue/singleline-html-element-content-newline": "off", "vue/html-closing-bracket-newline": "off", "vue/no-multiple-template-root": "off", + "max-lines-per-function": "off", + "max-statements": "off", }, -},) +},); diff --git a/packages/schema/src/index.ts b/packages/schema/src/index.ts index 75e3321..eaa2c4b 100644 --- a/packages/schema/src/index.ts +++ b/packages/schema/src/index.ts @@ -21,10 +21,4 @@ export { type ProjectUpdateDTO, } from "./drizzle/projects"; export { projectsRelations, tasksRelations } from "./drizzle/relations"; -export { - tasks, - taskSelect, - taskInsert, - taskUpdate, - type Task, -} from "./drizzle/tasks"; +export { tasks, taskSelect, taskInsert, taskUpdate, type Task } from "./drizzle/tasks";