- Introduced `CatalogFieldPickerDialog` and `CatalogFieldPickerList` for selecting catalog fields. - Implemented `appendTemplateFields` helper function for managing template fields. - Added tests for catalog field selection and template field management. - Created reusable components for field picking and reordering in the UI library. - Enhanced drag-and-drop functionality for reordering items in lists.
101 lines
3.1 KiB
TypeScript
101 lines
3.1 KiB
TypeScript
import { Button } from "@repo/shadcn-ui/components/button";
|
|
import type { ReactNode } from "react";
|
|
|
|
import { useFieldPickerController } from "../hooks/use-field-picker-controller";
|
|
|
|
import type { FieldPickerSelection } from "./field-picker-list";
|
|
import { FormActions, FormBody, FormDialog } from "./form-dialog";
|
|
|
|
/** Selección provisional y alta opcional compartidas, independientes de catálogos y endpoints. */
|
|
export function FieldPickerDialog(props: {
|
|
items: readonly { id: string }[];
|
|
canAdd: (ids: readonly string[]) => boolean;
|
|
onConfirm: (ids: readonly string[]) => boolean;
|
|
onClose: () => void;
|
|
labels: {
|
|
title: string;
|
|
cancel: string;
|
|
help: string;
|
|
destination: string;
|
|
selected: (count: number) => string;
|
|
limit: string;
|
|
invalid: string;
|
|
create: string;
|
|
confirm: string;
|
|
};
|
|
renderList: (selection: FieldPickerSelection) => ReactNode;
|
|
quickCreate?:
|
|
| {
|
|
disabled?: boolean | undefined;
|
|
render: (callbacks: {
|
|
onOpenChange: (open: boolean) => void;
|
|
onSaved: (id: string) => void;
|
|
}) => ReactNode;
|
|
}
|
|
| undefined;
|
|
}) {
|
|
const c = useFieldPickerController({
|
|
fields: props.items,
|
|
canAdd: props.canAdd,
|
|
onConfirm: props.onConfirm,
|
|
onClose: props.onClose,
|
|
});
|
|
|
|
return (
|
|
<FormDialog
|
|
cancelLabel={props.labels.cancel}
|
|
formActions
|
|
layout="picker"
|
|
onOpenChange={(open) => {
|
|
if (!open) {
|
|
props.onClose();
|
|
}
|
|
}}
|
|
open
|
|
title={props.labels.title}
|
|
wide
|
|
>
|
|
<FormBody>
|
|
<p className="text-sm text-muted-foreground">{props.labels.help}</p>
|
|
<p className="text-sm font-medium">{props.labels.destination}</p>
|
|
{props.renderList({ disabled: c.disabled, onToggle: c.toggle, selectedIds: c.selectedIds })}
|
|
<p aria-live="polite" className="text-sm text-muted-foreground">
|
|
{props.labels.selected(c.selectedIds.length)}
|
|
</p>
|
|
{props.items.some((field) => !c.selectedIds.includes(field.id) && c.disabled(field.id)) && (
|
|
<p className="text-sm text-muted-foreground" role="status">
|
|
{props.labels.limit}
|
|
</p>
|
|
)}
|
|
{!c.canConfirm && c.selectedIds.length > 0 && (
|
|
<p className="text-sm text-destructive" role="alert">
|
|
{props.labels.invalid}
|
|
</p>
|
|
)}
|
|
</FormBody>
|
|
<FormActions
|
|
leadingActions={
|
|
props.quickCreate && (
|
|
<Button
|
|
aria-haspopup="dialog"
|
|
disabled={props.quickCreate.disabled}
|
|
onClick={() => c.setCreatingField(true)}
|
|
type="button"
|
|
variant="outline"
|
|
>
|
|
{props.labels.create}
|
|
</Button>
|
|
)
|
|
}
|
|
>
|
|
<Button disabled={!c.canConfirm} onClick={c.confirm} type="button">
|
|
{props.labels.confirm}
|
|
</Button>
|
|
</FormActions>
|
|
{props.quickCreate &&
|
|
c.creatingField &&
|
|
props.quickCreate.render({ onOpenChange: c.setCreatingField, onSaved: c.selectCreated })}
|
|
</FormDialog>
|
|
);
|
|
}
|