230 lines
8.9 KiB
TypeScript
230 lines
8.9 KiB
TypeScript
import { useNavigation } from "react-router";
|
|
import { useEffect, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import FormGroup from "~/components/ui/forms/FormGroup";
|
|
import InputCheckboxWithDescription from "~/components/ui/input/InputCheckboxWithDescription";
|
|
import InputRadioGroup from "~/components/ui/input/InputRadioGroup";
|
|
import InputSelector from "~/components/ui/input/InputSelector";
|
|
import InputText from "~/components/ui/input/InputText";
|
|
import { EntityWithDetails } from "~/utils/db/entities/entities.db.server";
|
|
import { EntityRelationshipWithDetails } from "~/utils/db/entities/entityRelationships.db.server";
|
|
|
|
interface Props {
|
|
entity: EntityWithDetails;
|
|
entities: EntityWithDetails[];
|
|
item?: EntityRelationshipWithDetails;
|
|
}
|
|
|
|
export default function EntityRelationshipForm({ entity, entities, item }: Props) {
|
|
const { t } = useTranslation();
|
|
|
|
const navigation = useNavigation();
|
|
|
|
const [type, setType] = useState<string>(!item ? "child" : item.parentId === entity?.id ? "parent" : "child");
|
|
const [parentId, setParentId] = useState<string | undefined>(item?.parentId);
|
|
const [childId, setChildId] = useState<string | undefined>(item?.childId);
|
|
const [child, setChild] = useState<EntityWithDetails>();
|
|
const [parent, setParent] = useState<EntityWithDetails>();
|
|
const [title, setTitle] = useState(item?.title ?? "");
|
|
const [relationshipType, setRelationshipType] = useState(item?.type ?? "one-to-many");
|
|
const [required, setRequired] = useState(item ? item.required : false);
|
|
const [cascade, setCascade] = useState(item ? item.cascade : false);
|
|
const [readOnly, setReadOnly] = useState(item ? item.readOnly : false);
|
|
const [hiddenIfEmpty, setHiddenIfEmpty] = useState(item ? item.hiddenIfEmpty : false);
|
|
const [childEntityViewId, setChildEntityViewId] = useState(item?.childEntityViewId ?? undefined);
|
|
const [parentEntityViewId, setParentEntityViewId] = useState(item?.parentEntityViewId ?? undefined);
|
|
|
|
useEffect(() => {
|
|
if (type === "parent") {
|
|
setParentId(entity?.id);
|
|
setChildId(undefined);
|
|
} else {
|
|
setParentId(undefined);
|
|
setChildId(entity?.id);
|
|
}
|
|
}, [entity, type, entities]);
|
|
|
|
useEffect(() => {
|
|
setChild(entities.find((i) => i.id === childId));
|
|
}, [childId, entities]);
|
|
|
|
useEffect(() => {
|
|
setParent(entities.find((i) => i.id === parentId));
|
|
}, [parentId, entities]);
|
|
|
|
return (
|
|
<div className="space-y-2">
|
|
{/* <h3 className="border-b border-border pb-4 text-lg font-medium leading-3 text-foreground">{t(entity.title)} relationship</h3> */}
|
|
<FormGroup
|
|
id={item?.id}
|
|
editing={true}
|
|
state={{
|
|
loading: navigation.state === "loading",
|
|
submitting: navigation.state === "submitting",
|
|
}}
|
|
>
|
|
<div className="grid grid-cols-2 gap-2">
|
|
<InputRadioGroup
|
|
className="col-span-2"
|
|
name="type"
|
|
title={t("models.relationship.form.direction")}
|
|
value={type}
|
|
disabled={item !== undefined}
|
|
setValue={(e) => setType(e?.toString() ?? "")}
|
|
options={[
|
|
{
|
|
name: t("models.relationship.form.entityIsParent", { entity: t(entity.title) }),
|
|
value: "parent",
|
|
},
|
|
{
|
|
name: t("models.relationship.form.entityIsChild", { entity: t(entity.title) }),
|
|
value: "child",
|
|
},
|
|
]}
|
|
/>
|
|
{type === "child" && (
|
|
<InputSelector
|
|
className="col-span-2"
|
|
withSearch={false}
|
|
name="parentId"
|
|
title={t("models.relationship.form.parentEntity")}
|
|
value={parentId}
|
|
disabled={item !== undefined}
|
|
setValue={(e) => setParentId(e?.toString() ?? "")}
|
|
options={entities.map((i) => {
|
|
return {
|
|
value: i.id,
|
|
name: t(i.title),
|
|
};
|
|
})}
|
|
required
|
|
/>
|
|
)}
|
|
{type === "parent" && (
|
|
<InputSelector
|
|
className="col-span-2"
|
|
withSearch={false}
|
|
name="childId"
|
|
title={t("models.relationship.form.childEntity")}
|
|
value={childId}
|
|
disabled={item !== undefined}
|
|
setValue={(e) => setChildId(e?.toString() ?? "")}
|
|
options={entities.map((i) => {
|
|
return {
|
|
value: i.id,
|
|
name: t(i.title),
|
|
};
|
|
})}
|
|
required
|
|
/>
|
|
)}
|
|
<InputSelector
|
|
withSearch={false}
|
|
className="col-span-2"
|
|
name="relationshipType"
|
|
title={t("models.relationship.form.relationshipType")}
|
|
value={relationshipType}
|
|
setValue={(e) => setRelationshipType(e?.toString() ?? "one-to-many")}
|
|
options={[
|
|
{ name: t("models.relationship.types.oneToMany"), value: "one-to-many" },
|
|
{ name: t("models.relationship.types.oneToOne"), value: "one-to-one" },
|
|
{ name: t("models.relationship.types.manyToOne"), value: "many-to-one" },
|
|
{ name: t("models.relationship.types.manyToMany"), value: "many-to-many" },
|
|
]}
|
|
required
|
|
/>
|
|
<InputSelector
|
|
disabled={child?.views.filter((f) => f.isSystem).length === 0}
|
|
withSearch={false}
|
|
className="col-span-2"
|
|
name="childEntityViewId"
|
|
title={t("models.relationship.form.childrenView")}
|
|
value={childEntityViewId}
|
|
setValue={(e) => setChildEntityViewId(e?.toString() ?? undefined)}
|
|
options={
|
|
child?.views
|
|
.filter((f) => f.isSystem)
|
|
.map((f) => {
|
|
return {
|
|
value: f.id,
|
|
name: `${f.title} (${f.layout})`,
|
|
};
|
|
}) ?? []
|
|
}
|
|
hint={
|
|
<>
|
|
{childEntityViewId !== undefined && (
|
|
<button type="button" onClick={() => setChildEntityViewId(undefined)} className="text-muted-foreground hover:text-foreground/80 text-sm">
|
|
{t("shared.clear")}
|
|
</button>
|
|
)}
|
|
</>
|
|
}
|
|
/>
|
|
<InputSelector
|
|
disabled={parent?.views.filter((f) => f.isSystem).length === 0}
|
|
withSearch={false}
|
|
className="col-span-2"
|
|
name="parentEntityViewId"
|
|
title={t("models.relationship.form.parentView")}
|
|
value={parentEntityViewId}
|
|
setValue={(e) => setParentEntityViewId(e?.toString() ?? undefined)}
|
|
options={
|
|
parent?.views
|
|
.filter((f) => f.isSystem)
|
|
.map((f) => {
|
|
return {
|
|
value: f.id,
|
|
name: `${f.title} (${f.layout})`,
|
|
};
|
|
}) ?? []
|
|
}
|
|
hint={
|
|
<>
|
|
{childEntityViewId !== undefined && (
|
|
<button type="button" onClick={() => setChildEntityViewId(undefined)} className="text-muted-foreground hover:text-foreground/80 text-sm">
|
|
{t("shared.clear")}
|
|
</button>
|
|
)}
|
|
</>
|
|
}
|
|
/>
|
|
<InputCheckboxWithDescription
|
|
className="col-span-2"
|
|
name="required"
|
|
title={t("models.relationship.required")}
|
|
description={t("models.relationship.form.descriptions.required")}
|
|
value={required}
|
|
setValue={(e) => setRequired(Boolean(e))}
|
|
/>
|
|
<InputCheckboxWithDescription
|
|
className="col-span-2"
|
|
name="cascade"
|
|
title={t("models.relationship.form.cascadeDelete")}
|
|
description={t("models.relationship.form.descriptions.cascadeDelete")}
|
|
value={cascade}
|
|
setValue={(e) => setCascade(Boolean(e))}
|
|
/>
|
|
<InputCheckboxWithDescription
|
|
className="col-span-2"
|
|
name="readOnly"
|
|
title={t("models.relationship.form.readOnly")}
|
|
description={t("models.relationship.form.descriptions.readOnly")}
|
|
value={readOnly}
|
|
setValue={(e) => setReadOnly(Boolean(e))}
|
|
/>
|
|
<InputCheckboxWithDescription
|
|
className="col-span-2"
|
|
name="hiddenIfEmpty"
|
|
title={t("models.relationship.form.hiddenIfEmpty")}
|
|
description={t("models.relationship.form.descriptions.hiddenIfEmpty")}
|
|
value={hiddenIfEmpty}
|
|
setValue={(e) => setHiddenIfEmpty(Boolean(e))}
|
|
/>
|
|
<InputText className="col-span-2" name="title" title={t("models.relationship.form.titleOptional")} value={title} setValue={(e) => setTitle(e)} />
|
|
</div>
|
|
</FormGroup>
|
|
</div>
|
|
);
|
|
}
|