plane/web/components/custom-attributes/attributes-list/issue-modal-attributes-list.tsx

198 lines
7.8 KiB
TypeScript
Raw Normal View History

import { useEffect, useState } from "react";
2023-09-14 07:39:21 +00:00
import { useRouter } from "next/router";
// mobx
import { useMobxStore } from "lib/mobx/store-provider";
import { observer } from "mobx-react-lite";
// headless ui
import { Disclosure } from "@headlessui/react";
// components
import {
CustomCheckboxAttribute,
CustomDateTimeAttribute,
CustomFileAttribute,
CustomRelationAttribute,
CustomSelectAttribute,
} from "components/custom-attributes";
// ui
import { Loader, ToggleSwitch } from "components/ui";
// icons
import { ChevronDown } from "lucide-react";
// types
import { TCustomAttributeTypes } from "types";
2023-09-14 07:39:21 +00:00
type Props = {
entityId: string;
issueId: string;
onChange: (attributeId: string, val: string | string[] | undefined) => void;
2023-09-14 07:39:21 +00:00
projectId: string;
values: { [key: string]: string[] };
2023-09-14 07:39:21 +00:00
};
const DESCRIPTION_FIELDS: TCustomAttributeTypes[] = ["email", "number", "text", "url"];
2023-09-14 07:39:21 +00:00
export const IssueModalCustomAttributesList: React.FC<Props> = observer((props) => {
const { entityId, issueId, onChange, projectId, values } = props;
2023-09-14 07:39:21 +00:00
const [hideOptionalFields, setHideOptionalFields] = useState(false);
2023-09-14 07:39:21 +00:00
const router = useRouter();
const { workspaceSlug } = router.query;
2023-09-14 07:39:21 +00:00
const { customAttributes: customAttributesStore } = useMobxStore();
const { entityAttributes, fetchEntityDetails, fetchEntityDetailsLoader } = customAttributesStore;
2023-09-14 07:39:21 +00:00
const attributes = entityAttributes[entityId] ?? {};
// fetch entity details
useEffect(() => {
if (!entityAttributes[entityId]) {
if (!workspaceSlug) return;
fetchEntityDetails(workspaceSlug.toString(), entityId);
}
}, [entityAttributes, entityId, fetchEntityDetails, workspaceSlug]);
const descriptionFields = Object.values(attributes).filter((a) =>
DESCRIPTION_FIELDS.includes(a.type)
);
const nonDescriptionFields = Object.values(attributes).filter(
(a) => !DESCRIPTION_FIELDS.includes(a.type)
);
return (
<>
{fetchEntityDetailsLoader ? (
<Loader className="flex items-center gap-2">
<Loader.Item height="27px" width="90px" />
<Loader.Item height="27px" width="90px" />
<Loader.Item height="27px" width="90px" />
</Loader>
) : (
<>
<Disclosure defaultOpen>
{({ open }) => (
<>
<div className="flex items-center justify-between gap-2">
<Disclosure.Button className="font-medium flex items-center gap-2">
<ChevronDown
2023-09-19 13:34:54 +00:00
className={`transition-all ${open ? "" : "-rotate-90"}`}
size={14}
strokeWidth={1.5}
/>
Description Fields
</Disclosure.Button>
<div className={`flex items-center gap-1 ${open ? "" : "hidden"}`}>
<span className="text-xs">Hide optional fields</span>
<ToggleSwitch
value={hideOptionalFields}
onChange={() => setHideOptionalFields((prev) => !prev)}
/>
</div>
</div>
<Disclosure.Panel className="space-y-3.5 mt-2">
{Object.entries(descriptionFields).map(([attributeId, attribute]) => (
<div
key={attributeId}
className={hideOptionalFields && attribute.is_required ? "hidden" : ""}
>
<input
type={attribute.type}
className="border border-custom-border-200 rounded w-full px-2 py-1.5 text-xs placeholder:text-custom-text-400 focus:outline-none"
placeholder={attribute.display_name}
min={attribute.extra_settings.divided_by ? 0 : undefined}
max={attribute.extra_settings.divided_by ?? undefined}
value={values[attribute.id]?.[0] ?? attribute.default_value}
onChange={(e) => onChange(attribute.id, e.target.value)}
required={attribute.is_required}
/>
{attribute.type === "number" &&
attribute.extra_settings?.representation !== "numerical" && (
<span className="text-custom-text-400 text-[10px]">
Maximum value: {attribute.extra_settings?.divided_by}
</span>
)}
</div>
))}
</Disclosure.Panel>
</>
)}
</Disclosure>
2023-09-19 13:34:54 +00:00
<div className="flex items-center gap-2 flex-wrap mt-3.5">
{Object.entries(nonDescriptionFields).map(([attributeId, attribute]) => (
2023-09-14 07:39:21 +00:00
<div key={attributeId}>
{attribute.type === "checkbox" && (
<CustomCheckboxAttribute
2023-09-14 07:39:21 +00:00
attributeDetails={attribute}
issueId={issueId}
2023-09-19 07:37:33 +00:00
onChange={(val) => onChange(attribute.id, [`${val}`])}
2023-09-14 07:39:21 +00:00
projectId={projectId}
2023-09-15 20:08:02 +00:00
value={values[attribute.id]?.[0] === "true" ? true : false}
2023-09-14 07:39:21 +00:00
/>
)}
{attribute.type === "datetime" && (
<CustomDateTimeAttribute
attributeDetails={attribute}
2023-09-19 13:34:54 +00:00
className="bg-transparent border border-custom-border-200 py-1"
issueId={issueId}
onChange={(val) =>
onChange(attribute.id, val ? [val.toISOString()] : undefined)
}
projectId={projectId}
value={
2023-09-15 20:08:02 +00:00
values[attribute.id]?.[0] ? new Date(values[attribute.id]?.[0]) : undefined
}
/>
)}
2023-09-15 07:06:51 +00:00
{attribute.type === "file" && (
<CustomFileAttribute
attributeDetails={attribute}
2023-09-19 13:34:54 +00:00
className="bg-transparent border border-custom-border-200 py-1"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
2023-09-19 13:34:54 +00:00
value={values[attribute.id]?.[0]}
/>
)}
{attribute.type === "multi_select" && (
<CustomSelectAttribute
attributeDetails={attribute}
2023-09-19 13:34:54 +00:00
className="bg-transparent border border-custom-border-200 py-1"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
2023-09-15 20:08:02 +00:00
value={values[attribute.id] ?? []}
multiple
/>
)}
{attribute.type === "relation" && (
<CustomRelationAttribute
attributeDetails={attribute}
2023-09-19 13:34:54 +00:00
className="bg-transparent border border-custom-border-200 py-1"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
2023-09-15 20:08:02 +00:00
value={values[attribute.id]?.[0]}
/>
)}
{attribute.type === "select" && (
<CustomSelectAttribute
attributeDetails={attribute}
2023-09-19 13:34:54 +00:00
className="bg-transparent border border-custom-border-200 py-1"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
2023-09-19 12:11:48 +00:00
value={values[attribute.id]?.[0]}
2023-09-19 07:37:33 +00:00
multiple={false}
/>
)}
2023-09-14 07:39:21 +00:00
</div>
))}
</div>
</>
)}
</>
);
});