91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
import Autocomplete, { createFilterOptions } from "@mui/material/Autocomplete";
|
|
import MuiTextField from "@mui/material/TextField";
|
|
import Box from "@mui/material/Box";
|
|
import Typography from "@mui/material/Typography";
|
|
import type { Customer } from "../lib/queries/offers";
|
|
|
|
interface CustomerPickerProps {
|
|
customers: Customer[];
|
|
/** Selected customer id (FK), or null when none chosen. */
|
|
value: number | null;
|
|
onChange: (id: number | null) => void;
|
|
disabled?: boolean;
|
|
/**
|
|
* When set, draws the input in its error state (red border). The error TEXT
|
|
* is owned by the surrounding <Field> wrapper, so we deliberately do NOT
|
|
* render helperText here to avoid duplicating the message.
|
|
*/
|
|
error?: string;
|
|
placeholder?: string;
|
|
/** Optional id forwarded by <Field> for label association. */
|
|
id?: string;
|
|
}
|
|
|
|
// Search matches the customer name AND the IČO (company_id), so typing either
|
|
// finds the record. Match on a stringified "name + company_id" so MUI's default
|
|
// case-insensitive substring filtering covers both.
|
|
const filterCustomers = createFilterOptions<Customer>({
|
|
stringify: (c) => `${c.name} ${c.company_id ?? ""}`,
|
|
});
|
|
|
|
/**
|
|
* Shared searchable customer picker — a controlled MUI Autocomplete over the
|
|
* customers list, bound to the customer id. Used by Offers, Invoices and Issued
|
|
* Orders so all three FK customer selectors behave identically. Renders bare
|
|
* (no label/error text); wrap it in the page's <Field label error> for the
|
|
* label + error line.
|
|
*/
|
|
export default function CustomerPicker({
|
|
customers,
|
|
value,
|
|
onChange,
|
|
disabled,
|
|
error,
|
|
placeholder,
|
|
id,
|
|
}: CustomerPickerProps) {
|
|
const selected = customers.find((c) => c.id === value) ?? null;
|
|
return (
|
|
<Autocomplete<Customer>
|
|
options={customers}
|
|
value={selected}
|
|
onChange={(_, opt) => onChange(opt ? opt.id : null)}
|
|
getOptionLabel={(c) => c?.name ?? ""}
|
|
isOptionEqualToValue={(o, v) => o.id === v.id}
|
|
filterOptions={filterCustomers}
|
|
disabled={disabled}
|
|
size="small"
|
|
fullWidth
|
|
autoHighlight
|
|
renderOption={(props, c) => {
|
|
const { key, ...rest } = props as typeof props & { key: string };
|
|
return (
|
|
<Box component="li" key={key} {...rest}>
|
|
<Box>
|
|
{c.name}
|
|
{(c.company_id || c.city) && (
|
|
<Typography
|
|
variant="caption"
|
|
sx={{ display: "block", color: "text.secondary" }}
|
|
>
|
|
{c.company_id && `IČ: ${c.company_id}`}
|
|
{c.company_id && c.city && " · "}
|
|
{c.city}
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
</Box>
|
|
);
|
|
}}
|
|
renderInput={(params) => (
|
|
<MuiTextField
|
|
{...params}
|
|
id={id}
|
|
placeholder={placeholder ?? "Vyberte zákazníka…"}
|
|
error={!!error}
|
|
/>
|
|
)}
|
|
/>
|
|
);
|
|
}
|