Files
GearBox/src/client/hooks/useSetups.ts
Jean-Luc Makiola edc9793c2d feat: migrate setup visibility from boolean to three-tier system
Replace isPublic boolean with visibility enum (private/link/public) across
the full stack. Add shares table to schema for future share link support.
Update all services, routes, schemas, hooks, components, and tests.

Plan: 32-01 (Setup Sharing System - Schema Migration)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 17:55:46 +02:00

153 lines
3.7 KiB
TypeScript

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
ApiError,
apiDelete,
apiGet,
apiPatch,
apiPost,
apiPut,
} from "../lib/api";
interface SetupListItem {
id: number;
name: string;
visibility: "private" | "link" | "public";
createdAt: string;
updatedAt: string;
itemCount: number;
totalWeight: number;
totalCost: number;
}
interface SetupItemWithCategory {
id: number;
name: string;
weightGrams: number | null;
priceCents: number | null;
quantity: number;
categoryId: number;
notes: string | null;
productUrl: string | null;
imageFilename: string | null;
createdAt: string;
updatedAt: string;
categoryName: string;
categoryIcon: string;
classification: string;
}
interface SetupWithItems {
id: number;
name: string;
visibility: "private" | "link" | "public";
createdAt: string;
updatedAt: string;
items: SetupItemWithCategory[];
}
export type { SetupItemWithCategory, SetupListItem, SetupWithItems };
export function useSetups() {
return useQuery({
queryKey: ["setups"],
queryFn: () => apiGet<SetupListItem[]>("/api/setups"),
});
}
export function useSetup(setupId: number | null) {
return useQuery({
queryKey: ["setups", setupId],
queryFn: () => apiGet<SetupWithItems>(`/api/setups/${setupId}`),
enabled: setupId != null,
retry: (count, error) =>
error instanceof ApiError && error.status === 404 ? false : count < 3,
});
}
export function usePublicSetup(setupId: number | null) {
return useQuery({
queryKey: ["setups", setupId, "public"],
queryFn: () => apiGet<SetupWithItems>(`/api/setups/${setupId}/public`),
enabled: setupId != null,
retry: (count, error) =>
error instanceof ApiError && error.status === 404 ? false : count < 3,
});
}
export function useCreateSetup() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: { name: string }) =>
apiPost<SetupListItem>("/api/setups", data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["setups"] });
},
});
}
export function useUpdateSetup(setupId: number) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: {
name?: string;
visibility?: "private" | "link" | "public";
}) => apiPut<SetupListItem>(`/api/setups/${setupId}`, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["setups"] });
},
});
}
export function useDeleteSetup() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: number) =>
apiDelete<{ success: boolean }>(`/api/setups/${id}`),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["setups"] });
},
});
}
export function useSyncSetupItems(setupId: number) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (itemIds: number[]) =>
apiPut<{ success: boolean }>(`/api/setups/${setupId}/items`, { itemIds }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["setups"] });
},
});
}
export function useRemoveSetupItem(setupId: number) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (itemId: number) =>
apiDelete<{ success: boolean }>(`/api/setups/${setupId}/items/${itemId}`),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["setups"] });
},
});
}
export function useUpdateItemClassification(setupId: number) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
itemId,
classification,
}: {
itemId: number;
classification: string;
}) =>
apiPatch<{ success: boolean }>(
`/api/setups/${setupId}/items/${itemId}/classification`,
{ classification },
),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["setups", setupId] });
},
});
}