v1.5.6: boneyard-js skeleton migration, TanStack Query refactor, rate-limit config
- Replace hand-coded skeleton CSS/JSX with boneyard-js auto-generated bones - Remove skeleton.css and @keyframes shimmer from base.css - Add <Skeleton> wrappers with fixtures to all 25+ page components - Generate 20 bone captures via boneyard CLI (CDP auth-gated capture) - Refactor data fetching from useEffect+useState to TanStack Query - Extract query hooks into src/admin/lib/queries/ and apiAdapter - Add usePaginatedQuery hook replacing useApiCall/useListData - Fix parseFloat || 0 anti-pattern in OfferDetail and OffersTemplates inputs - Fix customer_id mandatory validation on offer creation - Fix leave-requests comma-separated status filter (Prisma enum in: []) - Add cross-entity cache invalidation for orders/offers/invoices/projects - Make rate limits configurable via env vars (RATE_LIMIT_MAX, RATE_LIMIT_REFRESH, etc.) - Add boneyard.config.json with routes and breakpoints Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
99
src/admin/lib/queries/attendance.ts
Normal file
99
src/admin/lib/queries/attendance.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import apiFetch from "../../utils/api";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
interface LocationRaw {
|
||||
users?: { first_name: string; last_name: string };
|
||||
user_name?: string;
|
||||
shift_date: string;
|
||||
arrival_time?: string | null;
|
||||
departure_time?: string | null;
|
||||
arrival_lat?: string | number | null;
|
||||
arrival_lng?: string | number | null;
|
||||
arrival_accuracy?: number | null;
|
||||
arrival_address?: string | null;
|
||||
departure_lat?: string | number | null;
|
||||
departure_lng?: string | number | null;
|
||||
departure_accuracy?: number | null;
|
||||
departure_address?: string | null;
|
||||
}
|
||||
|
||||
export interface LocationRecord {
|
||||
user_name: string;
|
||||
shift_date: string;
|
||||
arrival_time?: string | null;
|
||||
departure_time?: string | null;
|
||||
arrival_lat?: string | number | null;
|
||||
arrival_lng?: string | number | null;
|
||||
arrival_accuracy?: number | null;
|
||||
arrival_address?: string | null;
|
||||
departure_lat?: string | number | null;
|
||||
departure_lng?: string | number | null;
|
||||
departure_accuracy?: number | null;
|
||||
departure_address?: string | null;
|
||||
}
|
||||
|
||||
export const attendanceHistoryOptions = (filters: {
|
||||
month?: string;
|
||||
userId?: number;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "history", filters],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
params.set("limit", "1000");
|
||||
if (filters.month) params.set("month", filters.month);
|
||||
if (filters.userId) params.set("user_id", String(filters.userId));
|
||||
return jsonQuery<Record<string, unknown>[]>(
|
||||
`/api/admin/attendance?${params.toString()}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const attendanceBalancesOptions = (year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "balances", year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
`/api/admin/attendance?action=balances&year=${year}`,
|
||||
),
|
||||
});
|
||||
|
||||
export const attendanceWorkFundOptions = (year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "workfund", year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
`/api/admin/attendance?action=workfund&year=${year}`,
|
||||
),
|
||||
});
|
||||
|
||||
export const attendanceProjectReportOptions = (year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "project-report", year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
`/api/admin/attendance?action=project_report&year=${year}`,
|
||||
),
|
||||
});
|
||||
|
||||
export const attendanceLocationOptions = (id: string | undefined) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "location", id],
|
||||
queryFn: async (): Promise<LocationRecord> => {
|
||||
const response = await apiFetch(
|
||||
`/api/admin/attendance?action=location&id=${id}`,
|
||||
);
|
||||
const result = await response.json();
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || "Záznam nebyl nalezen");
|
||||
}
|
||||
const raw = (result.data.record || result.data) as LocationRaw;
|
||||
const userName = raw.users
|
||||
? `${raw.users.first_name} ${raw.users.last_name}`.trim()
|
||||
: raw.user_name || "";
|
||||
const { users: _users, ...rest } = raw;
|
||||
return { ...rest, user_name: userName };
|
||||
},
|
||||
enabled: !!id,
|
||||
});
|
||||
Reference in New Issue
Block a user