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>
This commit is contained in:
@@ -33,6 +33,94 @@ export interface LocationRecord {
|
||||
departure_address?: string | null;
|
||||
}
|
||||
|
||||
export interface ProjectLogEntry {
|
||||
id?: number;
|
||||
project_id?: number;
|
||||
project_name?: string;
|
||||
started_at?: string;
|
||||
ended_at?: string | null;
|
||||
hours?: string | number | null;
|
||||
minutes?: string | number | null;
|
||||
}
|
||||
|
||||
export interface AttendanceRecord {
|
||||
id: number;
|
||||
shift_date: string;
|
||||
leave_type?: string;
|
||||
leave_hours?: number;
|
||||
arrival_time?: string | null;
|
||||
departure_time?: string | null;
|
||||
break_start?: string | null;
|
||||
break_end?: string | null;
|
||||
notes?: string;
|
||||
project_name?: string;
|
||||
project_logs?: ProjectLogEntry[];
|
||||
}
|
||||
|
||||
export interface BalanceEntry {
|
||||
name: string;
|
||||
vacation_total: number;
|
||||
vacation_used: number;
|
||||
vacation_remaining: number;
|
||||
sick_used: number;
|
||||
}
|
||||
|
||||
export interface UserShort {
|
||||
id: number | string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface BalancesData {
|
||||
users: UserShort[];
|
||||
balances: Record<string, BalanceEntry>;
|
||||
}
|
||||
|
||||
export interface FundUserData {
|
||||
name: string;
|
||||
worked: number;
|
||||
covered: number;
|
||||
overtime: number;
|
||||
missing: number;
|
||||
}
|
||||
|
||||
export interface MonthFundData {
|
||||
month_name: string;
|
||||
fund: number;
|
||||
fund_to_date: number;
|
||||
business_days: number;
|
||||
users?: Record<string, FundUserData>;
|
||||
}
|
||||
|
||||
export interface FundData {
|
||||
months: Record<string, MonthFundData>;
|
||||
holidays: unknown[];
|
||||
users: UserShort[];
|
||||
balances: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface ProjectUser {
|
||||
user_id: number;
|
||||
user_name: string;
|
||||
hours: number;
|
||||
}
|
||||
|
||||
export interface ProjectEntry {
|
||||
project_id: number | null;
|
||||
project_number?: string;
|
||||
project_name?: string;
|
||||
hours: number;
|
||||
users: ProjectUser[];
|
||||
}
|
||||
|
||||
export interface MonthProjectData {
|
||||
month_name: string;
|
||||
projects: ProjectEntry[];
|
||||
}
|
||||
|
||||
export interface ProjectData {
|
||||
months: Record<string, MonthProjectData>;
|
||||
}
|
||||
|
||||
export const attendanceHistoryOptions = (filters: {
|
||||
month?: string;
|
||||
userId?: number;
|
||||
@@ -44,7 +132,7 @@ export const attendanceHistoryOptions = (filters: {
|
||||
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>[]>(
|
||||
return jsonQuery<AttendanceRecord[]>(
|
||||
`/api/admin/attendance?${params.toString()}`,
|
||||
);
|
||||
},
|
||||
@@ -54,7 +142,7 @@ export const attendanceBalancesOptions = (year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "balances", year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
jsonQuery<BalancesData>(
|
||||
`/api/admin/attendance?action=balances&year=${year}`,
|
||||
),
|
||||
});
|
||||
@@ -63,16 +151,14 @@ export const attendanceWorkFundOptions = (year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "workfund", year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
`/api/admin/attendance?action=workfund&year=${year}`,
|
||||
),
|
||||
jsonQuery<FundData>(`/api/admin/attendance?action=workfund&year=${year}`),
|
||||
});
|
||||
|
||||
export const attendanceProjectReportOptions = (year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "project-report", year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
jsonQuery<ProjectData>(
|
||||
`/api/admin/attendance?action=project_report&year=${year}`,
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export interface BankAccount {
|
||||
id: number;
|
||||
account_name: string;
|
||||
bank_name: string;
|
||||
account_number: string;
|
||||
iban: string;
|
||||
bic: string;
|
||||
currency: string;
|
||||
is_default: boolean;
|
||||
}
|
||||
|
||||
export const bankAccountsOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["bank-accounts"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>[]>("/api/admin/bank-accounts"),
|
||||
queryFn: () => jsonQuery<BankAccount[]>("/api/admin/bank-accounts"),
|
||||
staleTime: 2 * 60_000,
|
||||
});
|
||||
|
||||
|
||||
@@ -31,6 +31,89 @@ export interface InvoiceStats {
|
||||
vat_month_czk: number;
|
||||
}
|
||||
|
||||
export interface InvoiceItem {
|
||||
id?: number;
|
||||
description: string;
|
||||
item_description?: string;
|
||||
quantity: number;
|
||||
unit: string;
|
||||
unit_price: number;
|
||||
is_included_in_total: boolean;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
export interface InvoiceDetail {
|
||||
id: number;
|
||||
invoice_number: string;
|
||||
customer_id: number | null;
|
||||
customer_name: string;
|
||||
customer?: {
|
||||
company_id?: string;
|
||||
vat_id?: string;
|
||||
};
|
||||
status: string;
|
||||
issue_date: string;
|
||||
due_date: string;
|
||||
taxable_date?: string;
|
||||
tax_date?: string;
|
||||
currency: string;
|
||||
language: string;
|
||||
vat_rate: number;
|
||||
apply_vat: boolean;
|
||||
exchange_rate: string;
|
||||
notes?: string;
|
||||
payment_method?: string;
|
||||
variable_symbol?: string;
|
||||
constant_symbol?: string;
|
||||
issued_by?: string | null;
|
||||
paid_date?: string;
|
||||
billing_text?: string;
|
||||
bank_name?: string;
|
||||
bank_swift?: string;
|
||||
bank_iban?: string;
|
||||
bank_account?: string;
|
||||
bank_account_id?: number | null;
|
||||
items?: InvoiceItem[];
|
||||
subtotal: number;
|
||||
vat_amount: number;
|
||||
total: number;
|
||||
order?: {
|
||||
id: number;
|
||||
order_number: string;
|
||||
status?: string;
|
||||
} | null;
|
||||
order_id?: number;
|
||||
order_number?: string;
|
||||
has_pdf?: boolean;
|
||||
valid_transitions?: string[];
|
||||
}
|
||||
|
||||
export interface ReceivedInvoice {
|
||||
id: number;
|
||||
supplier_name: string;
|
||||
invoice_number: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
vat_rate: number;
|
||||
issue_date: string;
|
||||
due_date: string;
|
||||
notes: string;
|
||||
status: string;
|
||||
file_name?: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ReceivedStats {
|
||||
total_month: CurrencyAmount[];
|
||||
total_month_czk: number | null;
|
||||
vat_month: CurrencyAmount[];
|
||||
vat_month_czk: number | null;
|
||||
unpaid: CurrencyAmount[];
|
||||
unpaid_czk: number | null;
|
||||
unpaid_count: number;
|
||||
month_count: number;
|
||||
}
|
||||
|
||||
export const invoiceListOptions = (filters: {
|
||||
search?: string;
|
||||
sort?: string;
|
||||
@@ -93,7 +176,7 @@ export const receivedInvoiceListOptions = (filters: {
|
||||
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(
|
||||
return paginatedJsonQuery<ReceivedInvoice>(
|
||||
`/api/admin/received-invoices${qs ? `?${qs}` : ""}`,
|
||||
);
|
||||
},
|
||||
@@ -112,7 +195,7 @@ export const receivedInvoiceStatsOptions = (month: number, year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["invoices", "received", "stats", month, year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
jsonQuery<ReceivedStats>(
|
||||
`/api/admin/received-invoices/stats?month=${month}&year=${year}`,
|
||||
),
|
||||
});
|
||||
@@ -120,7 +203,6 @@ export const receivedInvoiceStatsOptions = (month: number, year: number) =>
|
||||
export const invoiceDetailOptions = (id: string | undefined) =>
|
||||
queryOptions({
|
||||
queryKey: ["invoices", id],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(`/api/admin/invoices/${id}`),
|
||||
queryFn: () => jsonQuery<InvoiceDetail>(`/api/admin/invoices/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export interface LeaveRequest {
|
||||
id: number;
|
||||
leave_type: string;
|
||||
date_from: string;
|
||||
date_to: string;
|
||||
total_days: number;
|
||||
total_hours: number;
|
||||
status: string;
|
||||
notes?: string;
|
||||
reviewer_note?: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const leaveRequestsOptions = (mine = true) =>
|
||||
queryOptions({
|
||||
queryKey: ["leave-requests", mine ? "mine" : "all"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
jsonQuery<LeaveRequest[]>(
|
||||
`/api/admin/leave-requests${mine ? "?mine=1" : ""}`,
|
||||
),
|
||||
});
|
||||
|
||||
@@ -1,22 +1,67 @@
|
||||
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<Record<string, unknown>>("/api/admin/customers"),
|
||||
queryFn: () => jsonQuery<Customer[]>("/api/admin/customers"),
|
||||
staleTime: 2 * 60_000,
|
||||
});
|
||||
|
||||
export const offerTemplatesOptions = (action?: string) =>
|
||||
export const itemTemplatesOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["offer-templates", action ?? "all"],
|
||||
queryFn: () => {
|
||||
const url = action
|
||||
? `/api/admin/offers-templates?action=${action}`
|
||||
: "/api/admin/offers-templates";
|
||||
return jsonQuery<Record<string, unknown>[]>(url);
|
||||
},
|
||||
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: {
|
||||
@@ -40,11 +85,62 @@ export const offerListOptions = (filters: {
|
||||
},
|
||||
});
|
||||
|
||||
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<Record<string, unknown>>(`/api/admin/offers/${id}`),
|
||||
queryFn: () => jsonQuery<OfferDetailData>(`/api/admin/offers/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,60 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery, paginatedJsonQuery } from "../apiAdapter";
|
||||
|
||||
export interface OrderItem {
|
||||
id?: number;
|
||||
description: string;
|
||||
item_description?: string;
|
||||
quantity: number;
|
||||
unit: string;
|
||||
unit_price: number;
|
||||
is_included_in_total: number | boolean;
|
||||
}
|
||||
|
||||
export interface OrderSection {
|
||||
id?: number;
|
||||
title: string;
|
||||
title_cz?: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface OrderInvoice {
|
||||
id: number;
|
||||
invoice_number: string;
|
||||
}
|
||||
|
||||
export interface OrderProject {
|
||||
id: number;
|
||||
project_number: string;
|
||||
name: string;
|
||||
has_nas_folder?: boolean;
|
||||
}
|
||||
|
||||
export interface OrderData {
|
||||
id: number;
|
||||
order_number: string;
|
||||
quotation_id: number;
|
||||
quotation_number: string;
|
||||
project_code?: string;
|
||||
customer_name: string;
|
||||
customer_order_number: string;
|
||||
currency: string;
|
||||
created_at: string;
|
||||
status: string;
|
||||
notes: string;
|
||||
attachment_name?: string;
|
||||
apply_vat: number | boolean;
|
||||
vat_rate: number;
|
||||
language?: string;
|
||||
items: OrderItem[];
|
||||
sections: OrderSection[];
|
||||
scope_title?: string;
|
||||
scope_description?: string;
|
||||
valid_transitions?: string[];
|
||||
invoice?: OrderInvoice;
|
||||
project?: OrderProject;
|
||||
}
|
||||
|
||||
export const orderListOptions = (filters: {
|
||||
search?: string;
|
||||
sort?: string;
|
||||
@@ -25,7 +79,6 @@ export const orderListOptions = (filters: {
|
||||
export const orderDetailOptions = (id: string | undefined) =>
|
||||
queryOptions({
|
||||
queryKey: ["orders", id],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(`/api/admin/orders/${id}`),
|
||||
queryFn: () => jsonQuery<OrderData>(`/api/admin/orders/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
|
||||
@@ -2,6 +2,32 @@ import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery, paginatedJsonQuery } from "../apiAdapter";
|
||||
import apiFetch from "../../utils/api";
|
||||
|
||||
export interface ProjectNote {
|
||||
id: number;
|
||||
content: string;
|
||||
user_name: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface ProjectData {
|
||||
id: number;
|
||||
project_number: string;
|
||||
name: string;
|
||||
status: string;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
customer_name: string;
|
||||
responsible_user_id: string;
|
||||
notes?: string;
|
||||
order_id?: number;
|
||||
order_number?: string;
|
||||
order_status?: string;
|
||||
quotation_id?: number;
|
||||
quotation_number?: string;
|
||||
has_nas_folder?: boolean;
|
||||
project_notes?: ProjectNote[];
|
||||
}
|
||||
|
||||
export const projectListOptions = (filters: {
|
||||
search?: string;
|
||||
sort?: string;
|
||||
@@ -26,8 +52,7 @@ export const projectListOptions = (filters: {
|
||||
export const projectDetailOptions = (id: string | undefined) =>
|
||||
queryOptions({
|
||||
queryKey: ["projects", id],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(`/api/admin/projects/${id}`),
|
||||
queryFn: () => jsonQuery<ProjectData>(`/api/admin/projects/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,11 +1,63 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export interface CompanySettingsCustomField {
|
||||
name: string;
|
||||
value: string;
|
||||
showLabel: boolean;
|
||||
}
|
||||
|
||||
export interface CompanySettingsData {
|
||||
// Company info
|
||||
company_name?: string;
|
||||
street?: string;
|
||||
city?: string;
|
||||
postal_code?: string;
|
||||
country?: string;
|
||||
company_id?: string;
|
||||
vat_id?: string;
|
||||
ico?: string;
|
||||
dic?: string;
|
||||
has_logo?: boolean;
|
||||
has_logo_dark?: boolean;
|
||||
custom_fields?: CompanySettingsCustomField[];
|
||||
supplier_field_order?: string[];
|
||||
|
||||
// System settings
|
||||
require_2fa?: boolean;
|
||||
default_currency?: string;
|
||||
default_vat_rate?: number;
|
||||
available_currencies?: string[];
|
||||
available_vat_rates?: number[];
|
||||
break_threshold_hours?: number;
|
||||
break_duration_short?: number;
|
||||
break_duration_long?: number;
|
||||
clock_rounding_minutes?: number;
|
||||
invoice_alert_email?: string;
|
||||
leave_notify_email?: string;
|
||||
smtp_from?: string;
|
||||
smtp_from_name?: string;
|
||||
max_login_attempts?: number;
|
||||
lockout_minutes?: number;
|
||||
max_requests_per_minute?: number;
|
||||
|
||||
// Numbering
|
||||
quotation_prefix?: string;
|
||||
order_type_code?: string;
|
||||
invoice_type_code?: string;
|
||||
offer_number_pattern?: string;
|
||||
order_number_pattern?: string;
|
||||
invoice_number_pattern?: string;
|
||||
|
||||
// Misc
|
||||
app_version?: string;
|
||||
}
|
||||
|
||||
export const companySettingsOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["company-settings"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>("/api/admin/company-settings"),
|
||||
jsonQuery<CompanySettingsData>("/api/admin/company-settings"),
|
||||
staleTime: 5 * 60_000,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export interface TripVehicle {
|
||||
id: number;
|
||||
spz: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface TripUser {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface BackendTrip {
|
||||
id: number;
|
||||
vehicle_id: number;
|
||||
user_id: number;
|
||||
trip_date: string;
|
||||
start_km: number;
|
||||
end_km: number;
|
||||
distance: number | null;
|
||||
route_from: string;
|
||||
route_to: string;
|
||||
is_business: boolean;
|
||||
notes: string | null;
|
||||
users: { id: number; first_name: string; last_name: string };
|
||||
vehicles: { id: number; name: string; spz: string };
|
||||
}
|
||||
|
||||
export const tripListOptions = (filters: {
|
||||
month?: number;
|
||||
year?: number;
|
||||
@@ -32,24 +59,21 @@ export const tripListOptions = (filters: {
|
||||
if (filters.page) params.set("page", String(filters.page));
|
||||
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||
const qs = params.toString();
|
||||
return jsonQuery<Record<string, unknown>[]>(
|
||||
`/api/admin/trips${qs ? `?${qs}` : ""}`,
|
||||
);
|
||||
return jsonQuery<BackendTrip[]>(`/api/admin/trips${qs ? `?${qs}` : ""}`);
|
||||
},
|
||||
});
|
||||
|
||||
export const tripVehiclesOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["trips", "vehicles"],
|
||||
queryFn: () => jsonQuery<Record<string, unknown>[]>("/api/admin/vehicles"),
|
||||
queryFn: () => jsonQuery<TripVehicle[]>("/api/admin/vehicles"),
|
||||
staleTime: 2 * 60_000,
|
||||
});
|
||||
|
||||
export const tripUsersOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["trips", "users"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>[]>("/api/admin/trips/users"),
|
||||
queryFn: () => jsonQuery<TripUser[]>("/api/admin/trips/users"),
|
||||
staleTime: 2 * 60_000,
|
||||
});
|
||||
|
||||
@@ -75,8 +99,6 @@ export const tripHistoryOptions = (filters: {
|
||||
params.set("vehicle_id", String(filters.vehicleId));
|
||||
if (filters.userId) params.set("user_id", String(filters.userId));
|
||||
const qs = params.toString();
|
||||
return jsonQuery<Record<string, unknown>[]>(
|
||||
`/api/admin/trips${qs ? `?${qs}` : ""}`,
|
||||
);
|
||||
return jsonQuery<BackendTrip[]>(`/api/admin/trips${qs ? `?${qs}` : ""}`);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
role_id: number;
|
||||
roles?: { id: number; name: string; display_name: string } | null;
|
||||
is_active: boolean;
|
||||
}
|
||||
|
||||
export interface Role {
|
||||
id: number;
|
||||
name: string;
|
||||
display_name: string;
|
||||
}
|
||||
|
||||
export const userListOptions = (permission?: string) =>
|
||||
queryOptions({
|
||||
queryKey: ["users", { permission }],
|
||||
@@ -8,13 +25,13 @@ export const userListOptions = (permission?: string) =>
|
||||
const url = permission
|
||||
? `/api/admin/users?permission=${encodeURIComponent(permission)}&limit=100`
|
||||
: "/api/admin/users?limit=100";
|
||||
return jsonQuery<Record<string, unknown>>(url);
|
||||
return jsonQuery<User[]>(url);
|
||||
},
|
||||
});
|
||||
|
||||
export const roleListOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["roles"],
|
||||
queryFn: () => jsonQuery<Record<string, unknown>[]>("/api/admin/roles"),
|
||||
queryFn: () => jsonQuery<Role[]>("/api/admin/roles"),
|
||||
staleTime: 2 * 60_000,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user