All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Fixes all 542 ESLint problems in the web package to achieve 0 errors and 0 warnings. Changes: - Fixed 144 issues: nullish coalescing, return types, unused variables - Fixed 118 issues: unnecessary conditions, type safety, template literals - Fixed 79 issues: non-null assertions, unsafe assignments, empty functions - Fixed 67 issues: explicit return types, promise handling, enum comparisons - Fixed 45 final warnings: missing return types, optional chains - Fixed 25 typecheck-related issues: async/await, type assertions, formatting - Fixed JSX.Element namespace errors across 90+ files All Quality Rails violations resolved. Lint and typecheck both pass with 0 problems. Files modified: 118 components, tests, hooks, and utilities Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
184 lines
4.6 KiB
TypeScript
184 lines
4.6 KiB
TypeScript
/**
|
|
* React Query hooks for layout management
|
|
*/
|
|
|
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
import type { UseQueryResult, UseMutationResult } from "@tanstack/react-query";
|
|
import type { UserLayout, WidgetPlacement } from "@mosaic/shared";
|
|
|
|
const LAYOUTS_KEY = ["layouts"];
|
|
|
|
interface CreateLayoutData {
|
|
name: string;
|
|
layout: WidgetPlacement[];
|
|
isDefault?: boolean;
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
interface UpdateLayoutData {
|
|
id: string;
|
|
name?: string;
|
|
layout?: WidgetPlacement[];
|
|
isDefault?: boolean;
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
/**
|
|
* Fetch all layouts for the current user
|
|
*/
|
|
export function useLayouts(): UseQueryResult<UserLayout[]> {
|
|
return useQuery<UserLayout[]>({
|
|
queryKey: LAYOUTS_KEY,
|
|
queryFn: async (): Promise<UserLayout[]> => {
|
|
const response = await fetch("/api/layouts");
|
|
if (!response.ok) {
|
|
throw new Error("Failed to fetch layouts");
|
|
}
|
|
return response.json() as Promise<UserLayout[]>;
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Fetch a single layout by ID
|
|
*/
|
|
export function useLayout(id: string): UseQueryResult<UserLayout> {
|
|
return useQuery<UserLayout>({
|
|
queryKey: [...LAYOUTS_KEY, id],
|
|
queryFn: async (): Promise<UserLayout> => {
|
|
const response = await fetch(`/api/layouts/${id}`);
|
|
if (!response.ok) {
|
|
throw new Error("Failed to fetch layout");
|
|
}
|
|
return response.json() as Promise<UserLayout>;
|
|
},
|
|
enabled: !!id,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Fetch the default layout
|
|
*/
|
|
export function useDefaultLayout(): UseQueryResult<UserLayout> {
|
|
return useQuery<UserLayout>({
|
|
queryKey: [...LAYOUTS_KEY, "default"],
|
|
queryFn: async (): Promise<UserLayout> => {
|
|
const response = await fetch("/api/layouts/default");
|
|
if (!response.ok) {
|
|
throw new Error("Failed to fetch default layout");
|
|
}
|
|
return response.json() as Promise<UserLayout>;
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create a new layout
|
|
*/
|
|
export function useCreateLayout(): UseMutationResult<UserLayout, Error, CreateLayoutData> {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (data: CreateLayoutData): Promise<UserLayout> => {
|
|
const response = await fetch("/api/layouts", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(data),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error("Failed to create layout");
|
|
}
|
|
|
|
return response.json() as Promise<UserLayout>;
|
|
},
|
|
onSuccess: (): void => {
|
|
// Invalidate layouts cache to refetch
|
|
void queryClient.invalidateQueries({ queryKey: LAYOUTS_KEY });
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update an existing layout
|
|
*/
|
|
export function useUpdateLayout(): UseMutationResult<UserLayout, Error, UpdateLayoutData> {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({ id, ...data }: UpdateLayoutData): Promise<UserLayout> => {
|
|
const response = await fetch(`/api/layouts/${id}`, {
|
|
method: "PATCH",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(data),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error("Failed to update layout");
|
|
}
|
|
|
|
return response.json() as Promise<UserLayout>;
|
|
},
|
|
onSuccess: (_data, variables): void => {
|
|
// Invalidate affected queries
|
|
void queryClient.invalidateQueries({ queryKey: LAYOUTS_KEY });
|
|
void queryClient.invalidateQueries({ queryKey: [...LAYOUTS_KEY, variables.id] });
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Delete a layout
|
|
*/
|
|
export function useDeleteLayout(): UseMutationResult<void, Error, string> {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (id: string): Promise<void> => {
|
|
const response = await fetch(`/api/layouts/${id}`, {
|
|
method: "DELETE",
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error("Failed to delete layout");
|
|
}
|
|
|
|
await response.json();
|
|
},
|
|
onSuccess: (): void => {
|
|
// Invalidate layouts cache to refetch
|
|
void queryClient.invalidateQueries({ queryKey: LAYOUTS_KEY });
|
|
},
|
|
});
|
|
}
|
|
|
|
interface UseSaveLayoutReturn {
|
|
saveLayout: (layout: WidgetPlacement[]) => void;
|
|
isSaving: boolean;
|
|
error: Error | null;
|
|
}
|
|
|
|
/**
|
|
* Helper hook to save layout changes with debouncing
|
|
*/
|
|
export function useSaveLayout(layoutId: string): UseSaveLayoutReturn {
|
|
const updateLayout = useUpdateLayout();
|
|
|
|
const saveLayout = (layout: WidgetPlacement[]): void => {
|
|
updateLayout.mutate({
|
|
id: layoutId,
|
|
layout,
|
|
});
|
|
};
|
|
|
|
return {
|
|
saveLayout,
|
|
isSaving: updateLayout.isPending,
|
|
error: updateLayout.error,
|
|
};
|
|
}
|