Files
app/src/admin/lib/queries/offers.ts
BOHA cd6d3daf43 v1.6.5: fix attendance unique constraint, schema sync, seed script
- Change attendance idx_attendance_user_date from unique to index (allow multiple shifts per day)
- Reset migrations to single baseline init migration
- Add seed script with admin user (admin/admin)
- Update CLAUDE.md with migration workflow documentation
- Various frontend fixes (queries, pages, hooks)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 16:21:34 +02:00

155 lines
3.6 KiB
TypeScript

import { queryOptions } from "@tanstack/react-query";
import { jsonQuery, paginatedJsonQuery } from "../apiAdapter";
export interface ItemTemplate {
id: number;
name: string;
description: string;
default_price: number;
category: string;
}
export interface ScopeSection {
_key: string;
title: string;
title_cz: string;
content: string;
}
export interface ScopeTemplate {
id: number;
name: string;
description?: string;
scope_template_sections?: ScopeSection[];
}
export interface CustomField {
name: string;
value: string;
showLabel: boolean;
_key?: string;
}
export interface Customer {
id: number;
name: string;
street?: string;
city?: string;
postal_code?: string;
country?: string;
company_id?: string;
vat_id?: string;
quotation_count: number;
custom_fields?: CustomField[];
customer_field_order?: string[];
}
export const offerCustomersOptions = () =>
queryOptions({
queryKey: ["offer-customers"],
queryFn: () => jsonQuery<Customer[]>("/api/admin/customers"),
staleTime: 2 * 60_000,
});
export const itemTemplatesOptions = () =>
queryOptions({
queryKey: ["offer-templates", "items"],
queryFn: () =>
jsonQuery<ItemTemplate[]>("/api/admin/offers-templates?action=items"),
});
export const scopeTemplatesOptions = () =>
queryOptions({
queryKey: ["offer-templates", "scopes"],
queryFn: () => jsonQuery<ScopeTemplate[]>("/api/admin/offers-templates"),
});
export const offerListOptions = (filters: {
search?: string;
sort?: string;
order?: string;
page?: number;
perPage?: number;
}) =>
queryOptions({
queryKey: ["offers", "list", filters],
queryFn: () => {
const params = new URLSearchParams();
if (filters.search) params.set("search", filters.search);
if (filters.sort) params.set("sort", filters.sort);
if (filters.order) params.set("order", filters.order);
if (filters.page) params.set("page", String(filters.page));
if (filters.perPage) params.set("per_page", String(filters.perPage));
const qs = params.toString();
return paginatedJsonQuery(`/api/admin/offers${qs ? `?${qs}` : ""}`);
},
});
export interface OfferItemData {
id?: number;
description: string;
item_description: string;
quantity: number;
unit: string;
unit_price: number;
is_included_in_total: boolean;
position?: number;
}
export interface OfferSectionData {
title: string;
title_cz: string;
content: string;
position?: number;
}
export interface OfferLockInfo {
user_id: number;
username: string;
full_name: string;
}
export interface OfferOrderInfo {
id: number;
order_number: string;
status?: string;
}
export interface OfferDetailData {
id: number;
quotation_number: string;
project_code: string;
customer_id: number | null;
customer_name: string;
created_at: string;
valid_until: string;
currency: string;
language: string;
vat_rate: number;
apply_vat: boolean;
exchange_rate: string;
scope_title: string;
scope_description: string;
items?: OfferItemData[];
sections?: OfferSectionData[];
status: string;
order: OfferOrderInfo | null;
locked_by: OfferLockInfo | null;
}
export const offerDetailOptions = (id: string | undefined) =>
queryOptions({
queryKey: ["offers", id],
queryFn: () => jsonQuery<OfferDetailData>(`/api/admin/offers/${id}`),
enabled: !!id,
});
export const offerNextNumberOptions = () =>
queryOptions({
queryKey: ["offers", "next-number"],
queryFn: () =>
jsonQuery<{ next_number?: string; number?: string }>(
"/api/admin/offers/next-number",
),
});