chore: set default values on the issue modal

This commit is contained in:
Aaryan Khandelwal 2023-09-20 17:16:01 +05:30
parent 4a13b30874
commit 8ff1e8dd87
7 changed files with 218 additions and 214 deletions

View File

@ -26,7 +26,6 @@ const checkboxAttributeRepresentations = [
export const CheckboxAttributeForm: React.FC<FormComponentProps> = ({ control }) => (
<>
<div className="space-y-3">
{" "}
<Controller
control={control}
name="display_name"
@ -36,30 +35,39 @@ export const CheckboxAttributeForm: React.FC<FormComponentProps> = ({ control })
/>
<div>
<p className="text-xs">Default value</p>
<div className="mt-2 flex items-center gap-6 accent-custom-primary-100">
<div className="flex items-center gap-1 text-xs">
<input
type="radio"
name="default_value"
value="true"
id="checked"
className="scale-75"
defaultChecked
/>
<label htmlFor="checked">Checked</label>
</div>
<Controller
control={control}
name="default_value"
render={({ field: { onChange, value } }) => (
<div className="mt-2 flex items-center gap-6 accent-custom-primary-100">
<div className="flex items-center gap-1 text-xs">
<input
type="radio"
name="default_value"
value="true"
id="checked"
className="scale-75"
defaultChecked={value === "true"}
onChange={(e) => onChange(e.target.value)}
/>
<label htmlFor="checked">Checked</label>
</div>
<div className="flex items-center gap-1 text-xs">
<input
type="radio"
name="default_value"
value="false"
id="unchecked"
className="scale-75"
/>
<label htmlFor="unchecked">Unchecked</label>
</div>
</div>
<div className="flex items-center gap-1 text-xs">
<input
type="radio"
name="default_value"
value="false"
id="unchecked"
className="scale-75"
defaultChecked={value === "false"}
onChange={(e) => onChange(e.target.value)}
/>
<label htmlFor="unchecked">Unchecked</label>
</div>
</div>
)}
/>
</div>
</div>
<div className="my-3 border-t-[0.5px] border-custom-border-200" />

View File

@ -4,7 +4,7 @@ import { observer } from "mobx-react-lite";
// components
import { CustomCheckboxAttribute } from "components/custom-attributes";
// ui
import { Loader, Tooltip } from "components/ui";
import { Tooltip } from "components/ui";
type Props = {
entityId: string;
@ -24,35 +24,23 @@ export const CustomAttributesCheckboxes: React.FC<Props> = observer((props) => {
const checkboxFields = Object.values(attributes).filter((a) => a.type === "checkbox");
return (
<>
{customAttributes.fetchEntityDetailsLoader ? (
<Loader className="space-y-3.5">
<Loader.Item height="35px" />
<Loader.Item height="35px" />
<Loader.Item height="35px" />
</Loader>
) : (
<div className="space-y-4">
{Object.entries(checkboxFields).map(([attributeId, attribute]) => (
<div key={attributeId} className="flex items-center gap-2">
<Tooltip tooltipContent={attribute.display_name} position="top-left">
<p className="text-xs text-custom-text-300 w-2/5 truncate">
{attribute.display_name}
</p>
</Tooltip>
<div className="w-3/5 flex-shrink-0">
<CustomCheckboxAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val) => onChange(attribute.id, [`${val}`])}
projectId={projectId}
value={values[attribute.id]?.[0] === "true" ? true : false}
/>
</div>
</div>
))}
<div className="space-y-4">
{Object.entries(checkboxFields).map(([attributeId, attribute]) => (
<div key={attributeId} className="flex items-center gap-2">
<Tooltip tooltipContent={attribute.display_name} position="top-left">
<p className="text-xs text-custom-text-300 w-2/5 truncate">{attribute.display_name}</p>
</Tooltip>
<div className="w-3/5 flex-shrink-0">
<CustomCheckboxAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val) => onChange(attribute.id, [`${val}`])}
projectId={projectId}
value={values[attribute.id]?.[0] === "true" ? true : false}
/>
</div>
</div>
)}
</>
))}
</div>
);
});

View File

@ -6,7 +6,7 @@ import { observer } from "mobx-react-lite";
// headless ui
import { Disclosure } from "@headlessui/react";
// ui
import { Loader, ToggleSwitch } from "components/ui";
import { ToggleSwitch } from "components/ui";
// icons
import { ChevronDown } from "lucide-react";
// types
@ -36,63 +36,53 @@ export const CustomAttributesDescriptionFields: React.FC<Props> = observer((prop
);
return (
<>
{customAttributes.fetchEntityDetailsLoader ? (
<Loader className="space-y-3.5">
<Loader.Item height="35px" />
<Loader.Item height="35px" />
<Loader.Item height="35px" />
</Loader>
) : (
<Disclosure defaultOpen>
{({ open }) => (
<>
<div className="flex items-center justify-between gap-2">
<Disclosure.Button className="font-medium flex items-center gap-2">
<ChevronDown
className={`transition-all ${open ? "" : "-rotate-90"}`}
size={14}
strokeWidth={1.5}
/>
Form Attributes
</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>
<Disclosure defaultOpen>
{({ open }) => (
<>
<div className="flex items-center justify-between gap-2">
<Disclosure.Button className="font-medium flex items-center gap-2">
<ChevronDown
className={`transition-all ${open ? "" : "-rotate-90"}`}
size={14}
strokeWidth={1.5}
/>
Custom Attributes
</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]}
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 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>
))}
</Disclosure.Panel>
</>
)}
</>
</Disclosure>
);
});

View File

@ -16,7 +16,7 @@ import useWorkspaceDetails from "hooks/use-workspace-details";
import useToast from "hooks/use-toast";
// components
// ui
import { Loader, ToggleSwitch, Tooltip } from "components/ui";
import { Loader, Tooltip } from "components/ui";
// icons
import { ChevronDown, Plus, Trash2 } from "lucide-react";
import { getFileIcon } from "components/icons";
@ -203,23 +203,14 @@ export const CustomAttributesFileUploads: React.FC<Props> = observer((props) =>
<Disclosure defaultOpen>
{({ open }) => (
<>
<div className="flex items-center justify-between gap-2">
<Disclosure.Button className="font-medium flex items-center gap-2">
<ChevronDown
className={`transition-all ${open ? "" : "-rotate-90"}`}
size={14}
strokeWidth={1.5}
/>
Attachment attributes
</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.Button className="font-medium flex items-center gap-2">
<ChevronDown
className={`transition-all ${open ? "" : "-rotate-90"}`}
size={14}
strokeWidth={1.5}
/>
Attachment attributes
</Disclosure.Button>
<Disclosure.Panel className="mt-2 grid grid-cols-3 gap-4">
{Object.entries(fileUploadFields).map(([attributeId, attribute]) => (
<div key={attributeId}>

View File

@ -41,66 +41,62 @@ export const CustomAttributesSelectFields: React.FC<Props> = observer((props) =>
<Loader.Item height="27px" width="90px" />
</Loader>
) : (
<div className="flex items-center gap-2 flex-wrap">
{Object.entries(selectFields).map(([attributeId, attribute]) => (
<div key={attributeId}>
{attribute.type === "datetime" && (
<CustomDateTimeAttribute
attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1 shadow-custom-shadow-2xs"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val ? [val.toISOString()] : undefined)}
projectId={projectId}
value={
values[attribute.id]?.[0] ? new Date(values[attribute.id]?.[0]) : undefined
}
/>
)}
{attribute.type === "file" && (
<CustomFileAttribute
attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1 shadow-custom-shadow-2xs"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
value={values[attribute.id]?.[0]}
/>
)}
{attribute.type === "multi_select" && (
<CustomSelectAttribute
attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1 shadow-custom-shadow-2xs"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
value={values[attribute.id] ?? []}
multiple
/>
)}
{attribute.type === "relation" && (
<CustomRelationAttribute
attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1 shadow-custom-shadow-2xs"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
value={values[attribute.id]?.[0]}
/>
)}
{attribute.type === "select" && (
<CustomSelectAttribute
attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1 shadow-custom-shadow-2xs"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
value={values[attribute.id]?.[0]}
multiple={false}
/>
)}
</div>
))}
</div>
Object.entries(selectFields).map(([attributeId, attribute]) => (
<div key={attributeId}>
{attribute.type === "datetime" && (
<CustomDateTimeAttribute
attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1 shadow-custom-shadow-2xs"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val ? [val.toISOString()] : undefined)}
projectId={projectId}
value={values[attribute.id]?.[0] ? new Date(values[attribute.id]?.[0]) : undefined}
/>
)}
{attribute.type === "file" && (
<CustomFileAttribute
attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1 shadow-custom-shadow-2xs"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
value={values[attribute.id]?.[0]}
/>
)}
{attribute.type === "multi_select" && (
<CustomSelectAttribute
attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1 shadow-custom-shadow-2xs"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
value={values[attribute.id] ?? []}
multiple
/>
)}
{attribute.type === "relation" && (
<CustomRelationAttribute
attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1 shadow-custom-shadow-2xs"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
value={values[attribute.id]?.[0]}
/>
)}
{attribute.type === "select" && (
<CustomSelectAttribute
attributeDetails={attribute}
className="bg-transparent border border-custom-border-200 py-1 shadow-custom-shadow-2xs"
issueId={issueId}
onChange={(val) => onChange(attribute.id, val)}
projectId={projectId}
value={values[attribute.id]?.[0]}
multiple={false}
/>
)}
</div>
))
)}
</>
);

View File

@ -33,7 +33,14 @@ import {
ObjectsSelect,
} from "components/custom-attributes";
// ui
import { CustomMenu, Input, PrimaryButton, SecondaryButton, ToggleSwitch } from "components/ui";
import {
CustomMenu,
Input,
Loader,
PrimaryButton,
SecondaryButton,
ToggleSwitch,
} from "components/ui";
import { TipTapEditor } from "components/tiptap";
// icons
import { SparklesIcon, XMarkIcon } from "@heroicons/react/24/outline";
@ -253,6 +260,20 @@ export const IssueForm: FC<IssueFormProps> = observer((props) => {
}
}, [customAttributes, entityId, workspaceSlug]);
// assign default values to attributes
useEffect(() => {
if (
!entityId ||
!customAttributes.entityAttributes[entityId] ||
Object.keys(customAttributesList).length > 0
)
return;
Object.values(customAttributes.entityAttributes[entityId]).forEach((attribute) => {
handleCustomAttributesChange(attribute.id, attribute.default_value);
});
}, [customAttributes, customAttributesList, entityId, handleCustomAttributesChange]);
// fetch issue attribute values
useEffect(() => {
if (!initialData || !initialData.id) return;
@ -476,29 +497,39 @@ export const IssueForm: FC<IssueFormProps> = observer((props) => {
</div>
)}
{entityId !== null && (
<div className="space-y-5">
<CustomAttributesDescriptionFields
entityId={entityId ?? ""}
issueId={watch("id") ?? ""}
onChange={handleCustomAttributesChange}
projectId={projectId}
values={customAttributesList}
/>
<CustomAttributesCheckboxes
entityId={entityId ?? ""}
issueId={watch("id") ?? ""}
onChange={handleCustomAttributesChange}
projectId={projectId}
values={customAttributesList}
/>
<CustomAttributesFileUploads
entityId={entityId ?? ""}
issueId={watch("id") ?? ""}
onChange={handleCustomAttributesChange}
projectId={projectId}
values={customAttributesList}
/>
</div>
<>
{customAttributes.fetchEntityDetailsLoader ? (
<Loader className="space-y-3.5">
<Loader.Item height="35px" />
<Loader.Item height="35px" />
<Loader.Item height="35px" />
</Loader>
) : (
<div className="space-y-5">
<CustomAttributesDescriptionFields
entityId={entityId ?? ""}
issueId={watch("id") ?? ""}
onChange={handleCustomAttributesChange}
projectId={projectId}
values={customAttributesList}
/>
<CustomAttributesCheckboxes
entityId={entityId ?? ""}
issueId={watch("id") ?? ""}
onChange={handleCustomAttributesChange}
projectId={projectId}
values={customAttributesList}
/>
<CustomAttributesFileUploads
entityId={entityId ?? ""}
issueId={watch("id") ?? ""}
onChange={handleCustomAttributesChange}
projectId={projectId}
values={customAttributesList}
/>
</div>
)}
</>
)}
</div>
</div>

View File

@ -33,7 +33,7 @@ export const CUSTOM_ATTRIBUTES_LIST: {
},
icon: CheckCircle,
initialPayload: {
default_value: "checked",
default_value: "true",
extra_settings: {
representation: "check",
},