feat(project-crud-sync): add planning artifacts for CRUD project lifecycle #5
|
|
@ -1,35 +1,8 @@
|
|||
import type { Project, ProjectInsertDTO, ProjectUpdateDTO } from "@workspace/schema";
|
||||
|
||||
interface ApiResponse<T> {
|
||||
data: T | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
function getBaseUrl(): string {
|
||||
const config = useRuntimeConfig();
|
||||
return config.public.apiBaseUrl as string;
|
||||
}
|
||||
|
||||
async function request<T>(path: string, options?: RequestInit): Promise<ApiResponse<T>> {
|
||||
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<Project[]>([]);
|
||||
|
|
@ -39,67 +12,65 @@ export function useProjectApi() {
|
|||
async function fetchProjects(): Promise<void> {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
const { data, error: err } = await request<Project[]>("/projects");
|
||||
if (data) projects.value = data;
|
||||
else error.value = err;
|
||||
|
||||
try {
|
||||
projects.value = await $fetch<Project[]>(`${apiBase}/projects`);
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : "Unknown error";
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function createProject(body: Partial<ProjectInsertDTO>): Promise<Project | null> {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
const { data, error: err } = await request<Project>("/projects", {
|
||||
try {
|
||||
const created = await $fetch<Project>(`${apiBase}/projects`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
body,
|
||||
});
|
||||
|
||||
if (data) {
|
||||
projects.value.push(data);
|
||||
} else {
|
||||
error.value = err;
|
||||
}
|
||||
|
||||
projects.value.push(created);
|
||||
return created;
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : "Unknown error";
|
||||
return null;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateProject(id: string, body: Partial<ProjectUpdateDTO>): Promise<Project | null> {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
const { data, error: err } = await request<Project>(`/projects/${id}`, {
|
||||
try {
|
||||
const updated = await $fetch<Project>(`${apiBase}/projects/${id}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(body),
|
||||
body,
|
||||
});
|
||||
|
||||
if (data) {
|
||||
const idx = projects.value.findIndex(p => p.id === id);
|
||||
if (idx !== NOT_FOUND) projects.value[idx] = data;
|
||||
} else {
|
||||
error.value = err;
|
||||
}
|
||||
|
||||
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;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteProject(id: string): Promise<boolean> {
|
||||
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;
|
||||
return !err;
|
||||
}
|
||||
}
|
||||
|
||||
return { projects, loading, error, fetchProjects, createProject, updateProject, deleteProject };
|
||||
|
|
|
|||
|
|
@ -16,17 +16,14 @@ const project = ref<Project | null>(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<Project>(`${useRuntimeConfig().public.apiBaseUrl as string}/projects/${route.params.id}`);
|
||||
} catch {
|
||||
notFound.value = true;
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
async function handleSubmit(body: { name: string; description?: string; status: string }) {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
},
|
||||
},)
|
||||
},);
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
Loading…
Reference in a new issue