UI fix:
- Close-only modals showing a redundant second close button now use
hideCancel: ReceivedInvoices paid-detail modal (was two identical "Zavřít"
buttons) and PlanCellModal "Den je součástí rozsahu".
- Add modal-duplicate-close test enforcing close-only modals set hideCancel.
Lint: cleared all 68 warnings → 0.
- preserve-caught-error: attach { cause } in ai.service / exchange-rates.
- no-require-imports: package.json version read via fs (APP_VERSION) instead
of require(), avoiding a rootDir-expanding static JSON import.
- react-hooks/exhaustive-deps (11): ref-in-cleanup copies, derived-value
useMemo wrapping, PlanGrid field extraction, stable nextKey useCallback,
AuthContext documented cycle-break.
- no-explicit-any (53): precise route param/Prisma types, generic enrich*()
preserving payload shape, minimal vite module type, frontend body/query-key
types, SystemInfo for Settings.
Refactor (test enablement): shift-form types moved to dependency-free
shiftFormTypes.ts so the print-HTML builders are unit-testable without the
component graph; characterization test pins their output.
Gates: 649 tests pass, tsc -b clean, lint 0. Verified touched flows live
via Playwright (PlanWork CRUD + optimistic cache, warehouse form keys,
Settings system info, invoice detail).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
import { useEffect, useMemo } from "react";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import MenuItem from "@mui/material/MenuItem";
|
|
import { Select } from "../../ui";
|
|
import { warehouseReservationListOptions } from "../../lib/queries/warehouse";
|
|
|
|
interface ReservationPickerProps {
|
|
itemId: number | null;
|
|
projectId: number | null;
|
|
value: number | null;
|
|
onChange: (reservationId: number | null, remainingQty: number) => void;
|
|
}
|
|
|
|
export default function ReservationPicker({
|
|
itemId,
|
|
projectId,
|
|
value,
|
|
onChange,
|
|
}: ReservationPickerProps) {
|
|
const { data: result, isFetching } = useQuery(
|
|
warehouseReservationListOptions({
|
|
item_id: itemId ?? undefined,
|
|
project_id: projectId ?? undefined,
|
|
status: "ACTIVE",
|
|
perPage: 100,
|
|
}),
|
|
);
|
|
|
|
const reservations = useMemo(() => result?.data ?? [], [result?.data]);
|
|
|
|
// When item/project change, the loaded reservation list changes. If the
|
|
// currently-selected reservation is no longer available, clear it and notify
|
|
// the parent so it doesn't keep holding a stale reservationId. Only act once
|
|
// the query has settled (result present, not fetching) to avoid resetting
|
|
// while the new list is still loading.
|
|
useEffect(() => {
|
|
if (
|
|
value != null &&
|
|
result !== undefined &&
|
|
!isFetching &&
|
|
!reservations.some((r) => r.id === value)
|
|
) {
|
|
onChange(null, 0);
|
|
}
|
|
}, [value, result, isFetching, reservations, onChange]);
|
|
|
|
return (
|
|
<Select
|
|
value={value == null ? "" : String(value)}
|
|
onChange={(val) => {
|
|
if (!val) {
|
|
onChange(null, 0);
|
|
return;
|
|
}
|
|
const reservationId = Number(val);
|
|
const reservation = reservations.find((r) => r.id === reservationId);
|
|
if (reservation) {
|
|
onChange(reservationId, reservation.remaining_qty);
|
|
}
|
|
}}
|
|
>
|
|
<MenuItem value="">— žádná rezervace —</MenuItem>
|
|
{reservations.map((r) => (
|
|
<MenuItem key={r.id} value={String(r.id)}>
|
|
R{r.id} — {r.project?.name ?? `Projekt #${r.project_id}`} — zbývá:{" "}
|
|
{r.remaining_qty} {r.item?.unit ?? "ks"}
|
|
</MenuItem>
|
|
))}
|
|
</Select>
|
|
);
|
|
}
|