metis-desk/modules/assets/src/web/assets/select/ui/asset-picker-dialog.tsx
David ccb52fb534 feat: add catalog field picker dialog and list components
- 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.
2026-10-15 15:58:01 +02:00

88 lines
2.8 KiB
TypeScript

import { FormActions, FormBody, FormDialog } from "@repo/rdx-ui/components/form-dialog";
import { Button } from "@repo/shadcn-ui/components/button";
import { useTranslation } from "react-i18next";
import { InventoryFeedback } from "../../../shared";
import { useAssetPickerController } from "../controllers/use-asset-picker-controller";
import type { AssetSelectProps } from "../types/asset-select-props";
import { AssetPickerList } from "./asset-picker-list";
export function AssetPickerDialog({
selection,
onClose,
}: {
selection: AssetSelectProps;
onClose: () => void;
}) {
const { t } = useTranslation("assets");
const c = useAssetPickerController(selection, onClose);
return (
<FormDialog
cancelLabel={t("cancel")}
formActions
layout="picker"
onOpenChange={(open) => {
if (!open) {
onClose();
}
}}
open
title={t(selection.mode === "multiple" ? "assetPicker.multiple" : "assetPicker.single")}
wide
>
<FormBody>
<p className="text-sm text-muted-foreground">{selection.label}</p>
{(c.inventory.isPending || c.catalog.isPending) && <p role="status">{t("loading")}</p>}
<InventoryFeedback error={c.inventory.error ?? c.catalog.error} />
{(c.inventory.isError || c.catalog.isError) && (
<Button
onClick={() => {
c.retry().catch(() => undefined);
}}
type="button"
variant="outline"
>
{t("retry")}
</Button>
)}
{c.inventory.data && (
<AssetPickerList
assets={c.assets}
catalog={c.catalog.data?.items ?? []}
disabled={c.disabled}
mode={selection.mode ?? "single"}
onClearFilters={() => {
c.setSearch("");
c.setTemplateId(null);
}}
onSearchChange={c.setSearch}
onTemplateChange={(id) => c.setTemplateId(id === "all" ? null : id)}
onToggle={c.toggle}
search={c.search}
selectedIds={c.selectedIds}
sites={selection.sites}
templateId={c.templateId}
unavailableLabels={
new Map(
selection.options
.filter((option) => option.disabled)
.map((option) => [option.value, option.label])
)
}
/>
)}
<p aria-live="polite" className="text-sm text-muted-foreground">
{t("assetPicker.selected", { count: c.selectedIds.length })}
</p>
</FormBody>
<FormActions>
<Button disabled={!c.canConfirm} onClick={c.confirm} type="button">
{t("assetPicker.confirm")}
</Button>
</FormActions>
</FormDialog>
);
}