metis-desk/modules/companies/src/web/access/access-field-control.tsx

153 lines
4.2 KiB
TypeScript
Raw Normal View History

import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
import { Input } from "@repo/shadcn-ui/components/input";
import { Label } from "@repo/shadcn-ui/components/label";
import type { SelectAction } from "@repo/shadcn-ui/components/select-with-action";
import { SelectWithAction } from "@repo/shadcn-ui/components/select-with-action";
import { Textarea } from "@repo/shadcn-ui/components/textarea";
import type { UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import type { AccessField } from "./access-fields";
import type { AccessFormValues } from "./access-form-values";
type Option = { value: string; label: string };
export function AccessFieldControl({
field,
id,
form,
value,
options,
disabled,
onSelected,
onCreate,
createDisabled = false,
createLabel,
}: {
field: AccessField;
id: string;
form: UseFormReturn<AccessFormValues>;
value: string | boolean | string[] | undefined;
options: readonly Option[];
disabled: boolean;
onSelected: (value: string) => void;
onCreate?: (() => void) | undefined;
createDisabled?: boolean;
createLabel?: string | undefined;
}) {
const { t } = useTranslation("access");
if (field.type === "boolean") {
return (
<div className="flex items-center gap-2">
<Checkbox
checked={Boolean(value)}
disabled={disabled}
id={id}
onCheckedChange={(checked) => form.setValue(field.key, checked === true)}
/>
<Label htmlFor={id}>{t(field.key)}</Label>
</div>
);
}
if (field.type === "permissions") {
return (
<PermissionsControl
fieldKey={field.key}
form={form}
id={id}
options={options}
value={value}
/>
);
}
return (
<div className="space-y-2">
<Label htmlFor={id}>
{t(field.key)}
{field.required ? "" : t("optional")}
</Label>
{field.type === "note" ? (
<Textarea id={id} maxLength={4000} rows={4} {...form.register(field.key)} />
) : field.type === "select" ? (
<SelectWithAction
action={selectAction(
onCreate,
createDisabled,
createLabel ?? t(quickAddLabel(field.key))
)}
disabled={disabled}
id={id}
label={t(field.key)}
onValueChange={onSelected}
options={options}
placeholder={t("choose")}
value={stringValue(value)}
/>
) : (
<Input
id={id}
maxLength={field.key === "name" ? 100 : 255}
required={field.required}
type={field.type === "date" ? "date" : "text"}
{...form.register(field.key)}
/>
)}
</div>
);
}
function stringValue(value: string | boolean | string[] | undefined) {
return typeof value === "string" ? value : "";
}
function PermissionsControl({
fieldKey,
id,
value,
options,
form,
}: {
fieldKey: string;
id: string;
value: string | boolean | string[] | undefined;
options: readonly Option[];
form: UseFormReturn<AccessFormValues>;
}) {
const { t } = useTranslation("access");
const values = Array.isArray(value) ? value : [];
return (
<fieldset className="space-y-2">
<legend className="text-sm font-medium">{t(fieldKey)}</legend>
{options.map((option) => (
<div className="flex items-center gap-2" key={option.value}>
<Checkbox
checked={values.includes(option.value)}
id={`${id}-${option.value}`}
onCheckedChange={(checked) =>
form.setValue(
fieldKey,
checked ? [...values, option.value] : values.filter((item) => item !== option.value)
)
}
/>
<Label htmlFor={`${id}-${option.value}`}>{option.label}</Label>
</div>
))}
{!options.length && <p className="text-sm text-muted-foreground">{t("noPermissions")}</p>}
</fieldset>
);
}
function quickAddLabel(key: string) {
return key === "systemId" ? "quickAddSystem" : "quickAddEnvironment";
}
function selectAction(
onClick: (() => void) | undefined,
disabled: boolean,
label: string
): SelectAction | undefined {
if (!onClick) {
return undefined;
}
return { onClick, disabled, label };
}