diff options
Diffstat (limited to 'makima')
| -rw-r--r-- | makima/frontend/src/hooks/useDogs.ts | 112 | ||||
| -rw-r--r-- | makima/frontend/src/lib/api.ts | 84 |
2 files changed, 196 insertions, 0 deletions
diff --git a/makima/frontend/src/hooks/useDogs.ts b/makima/frontend/src/hooks/useDogs.ts new file mode 100644 index 0000000..819219e --- /dev/null +++ b/makima/frontend/src/hooks/useDogs.ts @@ -0,0 +1,112 @@ +import { useState, useEffect, useCallback } from "react"; +import { + type DirectiveOrderGroup, + type CreateDOGRequest, + type UpdateDOGRequest, + listDogs, + createDog, + getDog, + updateDog as updateDogApi, + deleteDog as deleteDogApi, + pickUpDogOrders as pickUpDogOrdersApi, +} from "../lib/api"; + +export function useDogs(directiveId: string | undefined) { + const [dogs, setDogs] = useState<DirectiveOrderGroup[]>([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState<string | null>(null); + + const refresh = useCallback(async () => { + if (!directiveId) return; + try { + setLoading(true); + setError(null); + const res = await listDogs(directiveId); + setDogs(res.dogs); + } catch (e) { + setError(e instanceof Error ? e.message : "Failed to load DOGs"); + } finally { + setLoading(false); + } + }, [directiveId]); + + useEffect(() => { + refresh(); + }, [refresh]); + + const create = useCallback(async (req: CreateDOGRequest) => { + if (!directiveId) return null; + const dog = await createDog(directiveId, req); + await refresh(); + return dog; + }, [directiveId, refresh]); + + const update = useCallback(async (dogId: string, req: UpdateDOGRequest) => { + if (!directiveId) return; + await updateDogApi(directiveId, dogId, req); + await refresh(); + }, [directiveId, refresh]); + + const remove = useCallback(async (dogId: string) => { + if (!directiveId) return; + await deleteDogApi(directiveId, dogId); + await refresh(); + }, [directiveId, refresh]); + + const pickUpOrders = useCallback(async (dogId: string) => { + if (!directiveId) return null; + const result = await pickUpDogOrdersApi(directiveId, dogId); + await refresh(); + return result; + }, [directiveId, refresh]); + + return { dogs, loading, error, refresh, create, update, remove, pickUpOrders }; +} + +export function useDog(directiveId: string | undefined, dogId: string | undefined) { + const [dog, setDog] = useState<DirectiveOrderGroup | null>(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState<string | null>(null); + + const refresh = useCallback(async () => { + if (!directiveId || !dogId) return; + try { + setLoading(true); + setError(null); + const d = await getDog(directiveId, dogId); + setDog(d); + } catch (e) { + setError(e instanceof Error ? e.message : "Failed to load DOG"); + } finally { + setLoading(false); + } + }, [directiveId, dogId]); + + useEffect(() => { + setDog(null); + setError(null); + setLoading(true); + refresh(); + }, [directiveId, dogId]); // eslint-disable-line react-hooks/exhaustive-deps + + const update = useCallback(async (req: UpdateDOGRequest) => { + if (!directiveId || !dogId) return; + const d = await updateDogApi(directiveId, dogId, req); + setDog(d); + return d; + }, [directiveId, dogId]); + + const remove = useCallback(async () => { + if (!directiveId || !dogId) return; + await deleteDogApi(directiveId, dogId); + }, [directiveId, dogId]); + + const pickUpOrders = useCallback(async () => { + if (!directiveId || !dogId) return null; + const result = await pickUpDogOrdersApi(directiveId, dogId); + await refresh(); + return result; + }, [directiveId, dogId, refresh]); + + return { dog, loading, error, refresh, update, remove, pickUpOrders }; +} diff --git a/makima/frontend/src/lib/api.ts b/makima/frontend/src/lib/api.ts index 4923c1d..7968583 100644 --- a/makima/frontend/src/lib/api.ts +++ b/makima/frontend/src/lib/api.ts @@ -3387,6 +3387,85 @@ export async function pickUpOrders(directiveId: string): Promise<PickUpOrdersRes } // ============================================================================= +// Directive Order Groups (DOGs) API +// ============================================================================= + +export type DOGStatus = "open" | "in_progress" | "done" | "archived"; + +export interface DirectiveOrderGroup { + id: string; + directiveId: string; + ownerId: string; + name: string; + description: string | null; + status: DOGStatus; + createdAt: string; + updatedAt: string; +} + +export interface DOGListResponse { + dogs: DirectiveOrderGroup[]; +} + +export interface CreateDOGRequest { + name: string; + description?: string | null; +} + +export interface UpdateDOGRequest { + name?: string; + description?: string | null; + status?: DOGStatus; +} + +export async function listDogs(directiveId: string): Promise<DOGListResponse> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/dogs`); + if (!res.ok) throw new Error(`Failed to list DOGs: ${res.statusText}`); + return res.json(); +} + +export async function createDog(directiveId: string, req: CreateDOGRequest): Promise<DirectiveOrderGroup> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/dogs`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(req), + }); + if (!res.ok) throw new Error(`Failed to create DOG: ${res.statusText}`); + return res.json(); +} + +export async function getDog(directiveId: string, dogId: string): Promise<DirectiveOrderGroup> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/dogs/${dogId}`); + if (!res.ok) throw new Error(`Failed to get DOG: ${res.statusText}`); + return res.json(); +} + +export async function updateDog(directiveId: string, dogId: string, req: UpdateDOGRequest): Promise<DirectiveOrderGroup> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/dogs/${dogId}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(req), + }); + if (!res.ok) throw new Error(`Failed to update DOG: ${res.statusText}`); + return res.json(); +} + +export async function deleteDog(directiveId: string, dogId: string): Promise<void> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/dogs/${dogId}`, { + method: "DELETE", + }); + if (!res.ok) throw new Error(`Failed to delete DOG: ${res.statusText}`); +} + +export async function pickUpDogOrders(directiveId: string, dogId: string): Promise<PickUpOrdersResponse> { + const res = await authFetch(`${API_BASE}/api/v1/directives/${directiveId}/dogs/${dogId}/pick-up-orders`, { + method: "POST", + }); + if (!res.ok) throw new Error(`Failed to pick up DOG orders: ${res.statusText}`); + return res.json(); +} + +// ============================================================================= // Orders API // ============================================================================= @@ -3406,6 +3485,7 @@ export interface Order { directiveId: string | null; directiveStepId: string | null; directiveName: string | null; + dogId: string | null; repositoryUrl: string | null; createdAt: string; updatedAt: string; @@ -3424,6 +3504,7 @@ export interface CreateOrderRequest { orderType?: OrderType; labels?: string[]; directiveId: string; + dogId?: string | null; repositoryUrl?: string | null; } @@ -3436,6 +3517,7 @@ export interface UpdateOrderRequest { labels?: string[]; directiveId?: string | null; directiveStepId?: string | null; + dogId?: string | null; repositoryUrl?: string | null; } @@ -3445,6 +3527,7 @@ export async function listOrders( priority?: OrderPriority, directiveId?: string, search?: string, + dogId?: string, ): Promise<OrderListResponse> { const params = new URLSearchParams(); if (status) params.set("status", status); @@ -3452,6 +3535,7 @@ export async function listOrders( if (priority) params.set("priority", priority); if (directiveId) params.set("directiveId", directiveId); if (search) params.set("search", search); + if (dogId) params.set("dogId", dogId); const qs = params.toString(); const res = await authFetch(`${API_BASE}/api/v1/orders${qs ? `?${qs}` : ""}`); if (!res.ok) throw new Error(`Failed to list orders: ${res.statusText}`); |
