2023-09-19 11:39:50 +00:00
|
|
|
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";
|
2023-09-19 11:39:50 +00:00
|
|
|
// headless ui
|
|
|
|
import { Disclosure } from "@headlessui/react";
|
2023-09-15 05:54:51 +00:00
|
|
|
// components
|
|
|
|
import {
|
|
|
|
CustomCheckboxAttribute,
|
2023-09-15 12:17:51 +00:00
|
|
|
CustomDateTimeAttribute,
|
2023-09-15 05:54:51 +00:00
|
|
|
CustomFileAttribute,
|
2023-09-15 12:17:51 +00:00
|
|
|
CustomRelationAttribute,
|
2023-09-15 05:54:51 +00:00
|
|
|
CustomSelectAttribute,
|
|
|
|
} from "components/custom-attributes";
|
|
|
|
// ui
|
2023-09-19 11:39:50 +00:00
|
|
|
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;
|
2023-09-19 11:39:50 +00:00
|
|
|
onChange: (attributeId: string, val: string | string[] | undefined) => void;
|
2023-09-14 07:39:21 +00:00
|
|
|
projectId: string;
|
2023-09-15 12:17:51 +00:00
|
|
|
values: { [key: string]: string[] };
|
2023-09-14 07:39:21 +00:00
|
|
|
};
|
|
|
|
|
2023-09-19 11:39:50 +00:00
|
|
|
const DESCRIPTION_FIELDS: TCustomAttributeTypes[] = ["email", "number", "text", "url"];
|
2023-09-14 07:39:21 +00:00
|
|
|
|
2023-09-19 11:39:50 +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
|
|
|
|
2023-09-19 11:39:50 +00:00
|
|
|
const [hideOptionalFields, setHideOptionalFields] = useState(false);
|
2023-09-14 07:39:21 +00:00
|
|
|
|
2023-09-19 11:39:50 +00:00
|
|
|
const router = useRouter();
|
|
|
|
const { workspaceSlug } = router.query;
|
2023-09-14 07:39:21 +00:00
|
|
|
|
2023-09-19 11:39:50 +00:00
|
|
|
const { customAttributes: customAttributesStore } = useMobxStore();
|
|
|
|
const { entityAttributes, fetchEntityDetails, fetchEntityDetailsLoader } = customAttributesStore;
|
2023-09-14 07:39:21 +00:00
|
|
|
|
2023-09-19 11:39:50 +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"}`}
|
2023-09-19 11:39:50 +00:00
|
|
|
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">
|
2023-09-19 11:39:50 +00:00
|
|
|
{Object.entries(nonDescriptionFields).map(([attributeId, attribute]) => (
|
2023-09-14 07:39:21 +00:00
|
|
|
<div key={attributeId}>
|
2023-09-15 12:17:51 +00:00
|
|
|
{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
|
|
|
/>
|
|
|
|
)}
|
2023-09-15 12:17:51 +00:00
|
|
|
{attribute.type === "datetime" && (
|
|
|
|
<CustomDateTimeAttribute
|
2023-09-15 05:54:51 +00:00
|
|
|
attributeDetails={attribute}
|
2023-09-19 13:34:54 +00:00
|
|
|
className="bg-transparent border border-custom-border-200 py-1"
|
2023-09-15 05:54:51 +00:00
|
|
|
issueId={issueId}
|
2023-09-19 11:39:50 +00:00
|
|
|
onChange={(val) =>
|
|
|
|
onChange(attribute.id, val ? [val.toISOString()] : undefined)
|
|
|
|
}
|
2023-09-15 05:54:51 +00:00
|
|
|
projectId={projectId}
|
2023-09-15 12:17:51 +00:00
|
|
|
value={
|
2023-09-15 20:08:02 +00:00
|
|
|
values[attribute.id]?.[0] ? new Date(values[attribute.id]?.[0]) : undefined
|
2023-09-15 12:17:51 +00:00
|
|
|
}
|
2023-09-15 05:54:51 +00:00
|
|
|
/>
|
|
|
|
)}
|
2023-09-15 07:06:51 +00:00
|
|
|
{attribute.type === "file" && (
|
2023-09-15 05:54:51 +00:00
|
|
|
<CustomFileAttribute
|
|
|
|
attributeDetails={attribute}
|
2023-09-19 13:34:54 +00:00
|
|
|
className="bg-transparent border border-custom-border-200 py-1"
|
2023-09-15 05:54:51 +00:00
|
|
|
issueId={issueId}
|
2023-09-19 11:39:50 +00:00
|
|
|
onChange={(val) => onChange(attribute.id, val)}
|
2023-09-15 12:17:51 +00:00
|
|
|
projectId={projectId}
|
2023-09-19 13:34:54 +00:00
|
|
|
value={values[attribute.id]?.[0]}
|
2023-09-15 12:17:51 +00:00
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{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"
|
2023-09-15 12:17:51 +00:00
|
|
|
issueId={issueId}
|
2023-09-19 11:39:50 +00:00
|
|
|
onChange={(val) => onChange(attribute.id, val)}
|
2023-09-15 12:17:51 +00:00
|
|
|
projectId={projectId}
|
2023-09-15 20:08:02 +00:00
|
|
|
value={values[attribute.id] ?? []}
|
2023-09-15 12:17:51 +00:00
|
|
|
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"
|
2023-09-15 12:17:51 +00:00
|
|
|
issueId={issueId}
|
2023-09-19 11:39:50 +00:00
|
|
|
onChange={(val) => onChange(attribute.id, val)}
|
2023-09-15 12:17:51 +00:00
|
|
|
projectId={projectId}
|
2023-09-15 20:08:02 +00:00
|
|
|
value={values[attribute.id]?.[0]}
|
2023-09-15 12:17:51 +00:00
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{attribute.type === "select" && (
|
|
|
|
<CustomSelectAttribute
|
|
|
|
attributeDetails={attribute}
|
2023-09-19 13:34:54 +00:00
|
|
|
className="bg-transparent border border-custom-border-200 py-1"
|
2023-09-15 12:17:51 +00:00
|
|
|
issueId={issueId}
|
2023-09-19 11:39:50 +00:00
|
|
|
onChange={(val) => onChange(attribute.id, val)}
|
2023-09-15 12:17:51 +00:00
|
|
|
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-15 12:17:51 +00:00
|
|
|
/>
|
|
|
|
)}
|
2023-09-14 07:39:21 +00:00
|
|
|
</div>
|
|
|
|
))}
|
2023-09-19 11:39:50 +00:00
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
});
|