import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query' import { computed, toValue, type MaybeRefOrGetter } from 'vue' import type { components } from './generated/schema' import { apiClient } from './client' import { throwApiProblem } from './contracts' import { queryKeys } from './queryClient' import { reportOperationResult } from '../services/operationResults' export type ProjectDto = components['schemas']['ProjectDto'] export type ProjectTaskDto = components['schemas']['ProjectTaskDto'] export interface CreateProjectInput { name: string description?: string | null } export interface UpdateProjectInput { name?: string | null description?: string | null status?: string | null } export async function fetchProjects(signal?: AbortSignal): Promise { const { data, error, response } = await apiClient.GET('/api/v1/projects', { signal }) if (!response.ok || error || !data) { await throwApiProblem(response, 'Projekte konnten nicht geladen werden') } return data as ProjectDto[] } export async function fetchProject(id: string, signal?: AbortSignal): Promise { const { data, error, response } = await apiClient.GET('/api/v1/projects/{id}', { params: { path: { id } }, signal, }) if (!response.ok || error || !data) { await throwApiProblem(response, 'Projekt konnte nicht geladen werden') } return data as ProjectDto } async function createProject(input: CreateProjectInput): Promise { const { data, error, response } = await apiClient.POST('/api/v1/projects', { body: { name: input.name, description: input.description ?? null, }, }) if (!response.ok || error || !data) { await throwApiProblem(response, 'Projekt konnte nicht erstellt werden') } const project = data as ProjectDto reportOperationResult(project.operation, 'Projekt erstellt') return withoutProjectOperation(project) } export async function updateProject(id: string, input: UpdateProjectInput): Promise { const { data, error, response } = await apiClient.PATCH('/api/v1/projects/{id}', { params: { path: { id } }, body: input as components['schemas']['UpdateProjectRequest'], }) if (!response.ok || error || !data) { await throwApiProblem(response, 'Projekt konnte nicht gespeichert werden') } const project = data as ProjectDto reportOperationResult(project.operation, 'Projekt aktualisiert') return withoutProjectOperation(project) } function withoutProjectOperation(project: ProjectDto): ProjectDto { const { operation: _operation, ...persistedProject } = project return persistedProject } export async function fetchProjectTasks( id: string, signal?: AbortSignal, ): Promise { const { data, error, response } = await apiClient.GET('/api/v1/projects/{id}/tasks', { params: { path: { id } }, signal, }) if (!response.ok || error || !data) { await throwApiProblem(response, 'Projektaufgaben konnten nicht geladen werden') } return data as ProjectTaskDto[] } export function useProjects() { const client = useQueryClient() const query = useQuery({ queryKey: queryKeys.projects(), queryFn: ({ signal }) => fetchProjects(signal), }) const createMutation = useMutation({ mutationFn: createProject, onSuccess: project => { client.setQueryData(queryKeys.projects(), current => current ? [project, ...current.filter(item => item.id !== project.id)] : [project]) client.setQueryData(queryKeys.project(project.id), project) }, }) return { query, createProject: (input: CreateProjectInput) => createMutation.mutateAsync(input), creating: createMutation.isPending, createError: createMutation.error, } } export function useProject(id: MaybeRefOrGetter) { const client = useQueryClient() const query = useQuery({ queryKey: computed(() => queryKeys.project(toValue(id))), queryFn: ({ signal }) => fetchProject(toValue(id), signal), enabled: () => Boolean(toValue(id)), }) const updateMutation = useMutation({ mutationFn: (input: UpdateProjectInput) => updateProject(toValue(id), input), onSuccess: project => { client.setQueryData(queryKeys.project(project.id), project) client.setQueryData(queryKeys.projects(), current => current?.map(item => item.id === project.id ? project : item)) }, }) return { query, updateProject: (input: UpdateProjectInput) => updateMutation.mutateAsync(input), updating: updateMutation.isPending, updateError: updateMutation.error, } } export function useProjectTasks(id: MaybeRefOrGetter) { return useQuery({ queryKey: computed(() => queryKeys.projectTasks(toValue(id))), queryFn: ({ signal }) => fetchProjectTasks(toValue(id), signal), enabled: () => Boolean(toValue(id)), }) }