refactor: display components

This commit is contained in:
Aaryan Khandelwal 2023-09-19 13:07:33 +05:30
parent 14be78564a
commit 7cf263ecd4
16 changed files with 288 additions and 132 deletions

View File

@ -1,17 +1,25 @@
// ui
import { ToggleSwitch } from "components/ui";
// types
import { Props } from "./types";
import { ICustomAttribute } from "types";
type Props = {
attributeDetails: ICustomAttribute;
issueId: string;
projectId: string;
value: boolean;
onChange: (val: boolean) => void;
};
export const CustomCheckboxAttribute: React.FC<Props & { value: boolean }> = ({
attributeDetails,
onChange,
value,
}) => {
const handleUpdateCheckbox = (val: boolean | string) => onChange(val.toString());
const handleUpdateCheckbox = (val: boolean) => onChange(val);
return (
<div className="bg-custom-background-80 flex items-center gap-2 rounded px-2.5 py-0.5 text-xs">
<div className="text-xs truncate">
{attributeDetails.extra_settings.representation === "toggle_switch" ? (
<ToggleSwitch value={value ?? false} onChange={handleUpdateCheckbox} />
) : (
@ -23,7 +31,6 @@ export const CustomCheckboxAttribute: React.FC<Props & { value: boolean }> = ({
/>
</div>
)}
<span>{attributeDetails.display_name}</span>
</div>
);
};

View File

@ -1,7 +1,15 @@
// react-datepicker
import ReactDatePicker from "react-datepicker";
// types
import { Props } from "./types";
import { ICustomAttribute } from "types";
type Props = {
attributeDetails: ICustomAttribute;
issueId: string;
projectId: string;
value: Date | undefined;
onChange: (val: Date | null) => void;
};
const DATE_FORMATS: { [key: string]: string } = {
"MM-DD-YYYY": "MM-dd-yyyy",
@ -14,16 +22,12 @@ const TIME_FORMATS: { [key: string]: string } = {
"24": "HH:mm",
};
export const CustomDateTimeAttribute: React.FC<Props & { value: Date | undefined }> = ({
attributeDetails,
onChange,
value,
}) => (
export const CustomDateTimeAttribute: React.FC<Props> = ({ attributeDetails, onChange, value }) => (
<div className="flex-shrink-0">
<ReactDatePicker
selected={value}
onChange={onChange}
className="bg-custom-background-80 rounded text-xs px-2.5 py-0.5 outline-none"
className="bg-custom-background-80 rounded text-xs px-2.5 py-0.5 outline-none truncate"
calendarClassName="!bg-custom-background-80"
dateFormat={`${
attributeDetails.extra_settings.hide_date
@ -36,6 +40,7 @@ export const CustomDateTimeAttribute: React.FC<Props & { value: Date | undefined
}`}
showTimeInput={!attributeDetails.extra_settings.hide_time}
isClearable={!attributeDetails.is_required}
placeholderText={`Enter ${attributeDetails.display_name}`}
/>
</div>
);

View File

@ -3,13 +3,17 @@ import { useEffect, useState } from "react";
// react-hook-form
import { Controller, useForm } from "react-hook-form";
// types
import { Props } from "./types";
import { ICustomAttribute } from "types";
export const CustomEmailAttribute: React.FC<Props & { value: string | undefined }> = ({
attributeDetails,
onChange,
value,
}) => {
type Props = {
attributeDetails: ICustomAttribute;
issueId: string;
projectId: string;
value: string | undefined;
onChange: (val: string) => void;
};
export const CustomEmailAttribute: React.FC<Props> = ({ attributeDetails, onChange, value }) => {
const [isEditing, setIsEditing] = useState(false);
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { email: "" } });
@ -45,7 +49,10 @@ export const CustomEmailAttribute: React.FC<Props & { value: string | undefined
return (
<div className="flex-shrink-0">
{!isEditing && (
<div className="cursor-pointer text-xs truncate" onClick={() => setIsEditing(true)}>
<div
className="cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 w-min max-w-full whitespace-nowrap"
onClick={() => setIsEditing(true)}
>
{value && value !== "" ? value : "Empty"}
</div>
)}

View File

@ -8,17 +8,26 @@ import { useDropzone } from "react-dropzone";
import fileService from "services/file.service";
// hooks
import useToast from "hooks/use-toast";
import useWorkspaceDetails from "hooks/use-workspace-details";
// icons
import { getFileIcon } from "components/icons";
import { X } from "lucide-react";
// helpers
import { getFileExtension, getFileName } from "helpers/attachment.helper";
// types
import { Props } from "./types";
import useWorkspaceDetails from "hooks/use-workspace-details";
import { ICustomAttribute } from "types";
type Props = {
attributeDetails: ICustomAttribute;
issueId: string;
projectId: string;
value: string | undefined;
onChange: (val: string | undefined) => void;
};
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5 MB
export const CustomFileAttribute: React.FC<Props & { value: string | undefined }> = (props) => {
export const CustomFileAttribute: React.FC<Props> = (props) => {
const { attributeDetails, onChange, value } = props;
const [isUploading, setIsUploading] = useState(false);
@ -80,6 +89,13 @@ export const CustomFileAttribute: React.FC<Props & { value: string | undefined }
]
);
const handleRemoveFile = () => {
if (!workspaceDetails || !value || value === "") return;
onChange(undefined);
fileService.deleteFile(workspaceDetails.id, value);
};
const { getRootProps, getInputProps, isDragActive, isDragReject, fileRejections } = useDropzone({
onDrop,
maxSize: MAX_FILE_SIZE,
@ -95,15 +111,24 @@ export const CustomFileAttribute: React.FC<Props & { value: string | undefined }
return (
<div className="flex-shrink-0 truncate space-y-1">
{value && value !== "" && (
<a
href={value}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1 p-1 rounded border border-custom-border-200 text-xs truncate"
>
<span className="flex-shrink-0 h-6 w-6">{getFileIcon(getFileExtension(value))}</span>
<span className="truncate">{getFileName(value)}</span>
</a>
<div className="group flex items-center justify-between gap-2 p-1 rounded border border-custom-border-200 text-xs truncate">
<a
href={value}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1 flex-grow truncate"
>
<span className="flex-shrink-0 h-6 w-6">{getFileIcon(getFileExtension(value))}</span>
<span className="truncate">{getFileName(value)}</span>
</a>
<button
type="button"
className="hidden group-hover:grid place-items-center flex-shrink-0"
onClick={handleRemoveFile}
>
<X size={12} strokeWidth={1.5} />
</button>
</div>
)}
<div
{...getRootProps()}

View File

@ -5,13 +5,17 @@ import { Controller, useForm } from "react-hook-form";
// ui
import { ProgressBar } from "components/ui";
// types
import { Props } from "./types";
import { ICustomAttribute } from "types";
export const CustomNumberAttribute: React.FC<Props & { value: number | undefined }> = ({
attributeDetails,
onChange,
value,
}) => {
type Props = {
attributeDetails: ICustomAttribute;
issueId: string;
projectId: string;
value: number | undefined;
onChange: (val: number | undefined) => void;
};
export const CustomNumberAttribute: React.FC<Props> = ({ attributeDetails, onChange, value }) => {
const [isEditing, setIsEditing] = useState(false);
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { number: "" } });
@ -19,7 +23,10 @@ export const CustomNumberAttribute: React.FC<Props & { value: number | undefined
const handleFormSubmit = (data: { number: string }) => {
setIsEditing(false);
onChange(data.number);
const number = parseInt(data.number, 10);
if (isNaN(number)) onChange(undefined);
else onChange(number);
};
useEffect(() => {
@ -84,10 +91,10 @@ export const CustomNumberAttribute: React.FC<Props & { value: number | undefined
</>
) : (
<div
className="cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 rounded w-min whitespace-nowrap flex-grow"
className="text-xs truncate bg-custom-background-80 px-2.5 py-0.5 w-min max-w-full whitespace-nowrap"
onClick={() => setIsEditing(true)}
>
{value ?? `Enter ${attributeDetails.display_name}`}
{value ?? "Empty"}
</div>
)}
</div>

View File

@ -12,11 +12,19 @@ import modulesService from "services/modules.service";
// icons
import { Search } from "lucide-react";
// types
import { Props } from "./types";
import { ICustomAttribute } from "types";
// fetch-keys
import { CYCLES_LIST, MODULE_LIST } from "constants/fetch-keys";
export const CustomRelationAttribute: React.FC<Props & { value: string | undefined }> = ({
type Props = {
attributeDetails: ICustomAttribute;
issueId: string;
projectId: string;
value: string | undefined;
onChange: (val: string | undefined) => void;
};
export const CustomRelationAttribute: React.FC<Props> = ({
attributeDetails,
onChange,
projectId,

View File

@ -7,15 +7,22 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector";
// icons
import { Check, Search } from "lucide-react";
// types
import { Props } from "./types";
import { ICustomAttribute } from "types";
export const CustomSelectAttribute: React.FC<
Props &
(
| { multiple?: false; value: string | undefined }
| { multiple?: true; value: string[] | undefined }
)
> = (props) => {
type Props = {
attributeDetails: ICustomAttribute;
issueId: string;
projectId: string;
} & (
| {
multiple?: false;
onChange: (val: string | undefined) => void;
value: string | undefined;
}
| { multiple?: true; onChange: (val: string[] | undefined) => void; value: string[] | undefined }
);
export const CustomSelectAttribute: React.FC<Props> = (props) => {
const { attributeDetails, multiple = false, onChange, value } = props;
const [isOpen, setIsOpen] = useState(false);
@ -62,8 +69,8 @@ export const CustomSelectAttribute: React.FC<
});
const comboboxProps: any = {
value,
onChange,
value,
};
if (multiple) comboboxProps.multiple = true;
@ -101,14 +108,16 @@ export const CustomSelectAttribute: React.FC<
</div>
)
) : (
<span
className="px-2.5 py-0.5 rounded text-xs"
style={{
backgroundColor: `${options.find((o) => o.id === value)?.color}40`,
}}
>
{options.find((o) => o.id === value)?.display_name}
</span>
<div className="flex items-center gap-2 flex-wrap">
<span
className="px-2.5 py-0.5 rounded text-xs"
style={{
backgroundColor: `${options.find((o) => o.id === value)?.color}40`,
}}
>
{options.find((o) => o.id === value)?.display_name}
</span>
</div>
)
) : (
<div className="cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 rounded">

View File

@ -3,7 +3,15 @@ import { useEffect, useState } from "react";
// react-hook-form
import { Controller, useForm } from "react-hook-form";
// types
import { Props } from "./types";
import { ICustomAttribute } from "types";
type Props = {
attributeDetails: ICustomAttribute;
issueId: string;
projectId: string;
value: string | undefined;
onChange: (val: string) => void;
};
export const CustomTextAttribute: React.FC<Props & { value: string | undefined }> = ({
attributeDetails,
@ -45,7 +53,10 @@ export const CustomTextAttribute: React.FC<Props & { value: string | undefined }
return (
<div className="flex-shrink-0">
{!isEditing && (
<div className="cursor-pointer text-xs truncate" onClick={() => setIsEditing(true)}>
<div
className="cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 w-min max-w-full whitespace-nowrap"
onClick={() => setIsEditing(true)}
>
{value && value !== "" ? value : `Enter ${attributeDetails.display_name}`}
</div>
)}

View File

@ -1,9 +0,0 @@
// types
import { ICustomAttribute } from "types";
export type Props = {
attributeDetails: ICustomAttribute;
issueId: string;
onChange: (value: any) => void;
projectId: string;
};

View File

@ -3,7 +3,15 @@ import { useEffect, useState } from "react";
// react-hook-form
import { Controller, useForm } from "react-hook-form";
// types
import { Props } from "./types";
import { ICustomAttribute } from "types";
type Props = {
attributeDetails: ICustomAttribute;
issueId: string;
projectId: string;
value: string | undefined;
onChange: (val: string) => void;
};
export const CustomUrlAttribute: React.FC<Props & { value: string | undefined }> = ({
attributeDetails,
@ -43,8 +51,11 @@ export const CustomUrlAttribute: React.FC<Props & { value: string | undefined }>
return (
<div className="flex-shrink-0">
{!isEditing && (
<div className="cursor-pointer text-xs truncate" onClick={() => setIsEditing(true)}>
{value && value !== "" ? value : `Enter ${attributeDetails.display_name}`}
<div
className="cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 w-min max-w-full whitespace-nowrap"
onClick={() => setIsEditing(true)}
>
{value && value !== "" ? value : "Empty"}
</div>
)}
{isEditing && (

View File

@ -3,11 +3,11 @@ import { Controller } from "react-hook-form";
// components
import { FormComponentProps, Input } from "components/custom-attributes";
// ui
import { CustomSelect, ToggleSwitch } from "components/ui";
import { CustomSelect, ToggleSwitch, Tooltip } from "components/ui";
// constants
import { DATE_FORMATS, TIME_FORMATS } from "constants/custom-attributes";
export const DateTimeAttributeForm: React.FC<FormComponentProps> = ({ control }) => (
export const DateTimeAttributeForm: React.FC<FormComponentProps> = ({ control, watch }) => (
<div className="space-y-3">
<Controller
control={control}
@ -44,8 +44,20 @@ export const DateTimeAttributeForm: React.FC<FormComponentProps> = ({ control })
name="extra_settings.hide_date"
render={({ field: { onChange, value } }) => (
<div className="flex items-center justify-end gap-1 mt-2">
<ToggleSwitch value={value ?? false} onChange={onChange} size="sm" />
<span className="text-xs">Don{"'"}t show date</span>
<Tooltip
tooltipContent="Cannot disable both, date and time"
disabled={!watch("extra_settings.hide_time")}
>
<div className="flex items-center gap-1">
<ToggleSwitch
value={value ?? false}
onChange={onChange}
size="sm"
disabled={watch("extra_settings.hide_time")}
/>
<span className="text-xs">Don{"'"}t show date</span>
</div>
</Tooltip>
</div>
)}
/>
@ -78,8 +90,20 @@ export const DateTimeAttributeForm: React.FC<FormComponentProps> = ({ control })
name="extra_settings.hide_time"
render={({ field: { onChange, value } }) => (
<div className="flex items-center justify-end gap-1 mt-2">
<ToggleSwitch value={value ?? false} onChange={onChange} size="sm" />
<span className="text-xs">Don{"'"}t show time</span>
<Tooltip
tooltipContent="Cannot disable both, date and time"
disabled={!watch("extra_settings.hide_date")}
>
<div className="flex items-center gap-1">
<ToggleSwitch
value={value ?? false}
onChange={onChange}
size="sm"
disabled={watch("extra_settings.hide_date")}
/>
<span className="text-xs">Don{"'"}t show time</span>
</div>
</Tooltip>
</div>
)}
/>

View File

@ -63,7 +63,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
<CustomCheckboxAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
onChange={(val) => onChange(attribute.id, [`${val}`])}
projectId={projectId}
value={values[attribute.id]?.[0] === "true" ? true : false}
/>
@ -72,7 +72,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
<CustomDateTimeAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
onChange={(val) => onChange(attribute.id, [val?.toISOString() ?? ""])}
projectId={projectId}
value={
values[attribute.id]?.[0] ? new Date(values[attribute.id]?.[0]) : undefined
@ -101,7 +101,9 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
<CustomSelectAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string[]) => onChange(attribute.id, val)}
onChange={(val) => {
if (val) onChange(attribute.id, val);
}}
projectId={projectId}
value={values[attribute.id] ?? []}
multiple
@ -111,7 +113,9 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
<CustomNumberAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
onChange={(val) => {
if (val) onChange(attribute.id, [val.toString()]);
}}
projectId={projectId}
value={
values[attribute.id]?.[0] ? parseInt(values[attribute.id]?.[0]) : undefined
@ -122,7 +126,9 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
<CustomRelationAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
onChange={(val) => {
if (val) onChange(attribute.id, [val]);
}}
projectId={projectId}
value={values[attribute.id]?.[0]}
/>
@ -131,16 +137,19 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
<CustomSelectAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
onChange={(val) => {
if (val) onChange(attribute.id, [val]);
}}
projectId={projectId}
value={attribute.default_value !== "" ? attribute.default_value : undefined}
multiple={false}
/>
)}
{attribute.type === "text" && (
<CustomTextAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
onChange={(val) => onChange(attribute.id, [val])}
projectId={projectId}
value={attribute.default_value}
/>
@ -149,10 +158,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
<CustomUrlAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => {
console.log(val);
onChange(attribute.id, [val]);
}}
onChange={(val: string) => onChange(attribute.id, [val])}
projectId={projectId}
value={values[attribute.id]?.[0]}
/>

View File

@ -26,9 +26,10 @@ import { CUSTOM_ATTRIBUTES_LIST } from "constants/custom-attributes";
type Props = {
issue: IIssue | undefined;
projectId: string;
};
export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue }) => {
export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue, projectId }) => {
const router = useRouter();
const { workspaceSlug } = router.query;
@ -37,14 +38,20 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
customAttributeValues: customAttributeValuesStore,
} = useMobxStore();
const { entityAttributes, fetchEntityDetails } = customAttributesStore;
const { issueAttributeValues, fetchIssueAttributeValues } = customAttributeValuesStore;
const { issueAttributeValues, fetchIssueAttributeValues, deleteAttributeValue } =
customAttributeValuesStore;
const handleAttributeUpdate = (attributeId: string, value: string[]) => {
const handleAttributeUpdate = (attributeId: string, value: string | string[] | undefined) => {
if (!issue || !workspaceSlug) return;
if (!value) {
deleteAttributeValue(workspaceSlug.toString(), projectId, issue.id, attributeId);
return;
}
const payload: ICustomAttributeValueFormData = {
issue_properties: {
[attributeId]: value,
[attributeId]: Array.isArray(value) ? value : [value],
},
};
@ -109,7 +116,7 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
<CustomCheckboxAttribute
attributeDetails={attribute}
issueId={issue.id}
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
onChange={(val) => handleAttributeUpdate(attribute.id, [`${val}`])}
projectId={issue.project}
value={
attributeValue ? (attributeValue?.[0]?.value === "true" ? true : false) : false
@ -120,7 +127,9 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
<CustomDateTimeAttribute
attributeDetails={attribute}
issueId={issue.id}
onChange={(val: Date) => handleAttributeUpdate(attribute.id, [val.toISOString()])}
onChange={(val: Date | null) => {
handleAttributeUpdate(attribute.id, val ? [val.toISOString()] : undefined);
}}
projectId={issue.project}
value={attributeValue ? new Date(attributeValue?.[0]?.value ?? "") : undefined}
/>
@ -129,7 +138,9 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
<CustomEmailAttribute
attributeDetails={attribute}
issueId={issue.id}
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
onChange={(val) => {
handleAttributeUpdate(attribute.id, val && val !== "" ? [val] : undefined);
}}
projectId={issue.project}
value={attributeValue ? attributeValue?.[0]?.value : undefined}
/>
@ -138,7 +149,7 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
<CustomFileAttribute
attributeDetails={attribute}
issueId={issue.id}
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
onChange={(val) => handleAttributeUpdate(attribute.id, val)}
projectId={issue.project}
value={attributeValue ? attributeValue?.[0]?.value : undefined}
/>
@ -147,7 +158,7 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
<CustomSelectAttribute
attributeDetails={attribute}
issueId={issue.id}
onChange={(val: string[]) => handleAttributeUpdate(attribute.id, val)}
onChange={(val) => handleAttributeUpdate(attribute.id, val)}
projectId={issue.project}
value={Array.isArray(attributeValue) ? attributeValue.map((v) => v.value) : []}
multiple
@ -157,7 +168,9 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
<CustomNumberAttribute
attributeDetails={attribute}
issueId={issue.id}
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
onChange={(val) => {
handleAttributeUpdate(attribute.id, val ? val.toString() : undefined);
}}
projectId={issue.project}
value={
attributeValue ? parseInt(attributeValue?.[0]?.value ?? "0", 10) : undefined
@ -168,7 +181,7 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
<CustomRelationAttribute
attributeDetails={attribute}
issueId={issue.id}
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
onChange={(val) => handleAttributeUpdate(attribute.id, val)}
projectId={issue.project}
value={attributeValue ? attributeValue?.[0]?.value : undefined}
/>
@ -177,16 +190,19 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
<CustomSelectAttribute
attributeDetails={attribute}
issueId={issue.id}
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
onChange={(val) => handleAttributeUpdate(attribute.id, val)}
projectId={issue.project}
value={attributeValue ? attributeValue?.[0]?.value : undefined}
multiple={false}
/>
)}
{attribute.type === "text" && (
<CustomTextAttribute
attributeDetails={attribute}
issueId={issue.id}
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
onChange={(val) =>
handleAttributeUpdate(attribute.id, val && val !== "" ? [val] : undefined)
}
projectId={issue.project}
value={attributeValue ? attributeValue?.[0].value : undefined}
/>
@ -195,7 +211,9 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
<CustomUrlAttribute
attributeDetails={attribute}
issueId={issue.id}
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
onChange={(val) =>
handleAttributeUpdate(attribute.id, val && val !== "" ? [val] : undefined)
}
projectId={issue.project}
value={attributeValue ? attributeValue?.[0]?.value : undefined}
/>

View File

@ -305,7 +305,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
data={issueDetail ?? null}
user={user}
/>
<div className="h-full w-full flex flex-col divide-y-2 divide-custom-border-200 overflow-hidden">
<div className="h-full w-full flex flex-col divide-y divide-custom-border-300 overflow-hidden">
<div className="flex items-center justify-between px-5 pb-3">
<h4 className="text-sm font-medium">
{issueDetail?.project_detail?.identifier}-{issueDetail?.sequence_id}
@ -349,30 +349,9 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
</div>
<div className="h-full w-full px-5 overflow-y-auto">
<div className={`divide-y-2 divide-custom-border-200 ${uneditable ? "opacity-60" : ""}`}>
<div className={`divide-y divide-custom-border-300 ${uneditable ? "opacity-60" : ""}`}>
{showFirstSection && (
<div className="py-1">
{/* {(fieldsToShow.includes("all") || fieldsToShow.includes("entity")) && (
<div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm text-custom-text-200 sm:basis-1/2">
<Squares2X2Icon className="h-4 w-4 flex-shrink-0" />
<p>Object</p>
</div>
<div className="sm:basis-1/2">
<Controller
control={control}
name="entity"
render={({ field: { value } }) => (
<ObjectsSelect
onChange={(val: string | null) => submitChanges({ entity: val })}
projectId={projectId?.toString() ?? ""}
value={value}
/>
)}
/>
</div>
</div>
)} */}
{(fieldsToShow.includes("all") || fieldsToShow.includes("state")) && (
<div className="flex flex-wrap items-center py-2">
<div className="flex items-center gap-x-2 text-sm text-custom-text-200 sm:basis-1/2">
@ -663,6 +642,14 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
)}
</div>
)}
{watchIssue("entity") && (
<div className="py-1">
<SidebarCustomAttributesList
issue={issueDetail}
projectId={projectId?.toString() ?? ""}
/>
</div>
)}
</div>
{(fieldsToShow.includes("all") || fieldsToShow.includes("label")) &&
watchIssue("entity") === null && (
@ -705,11 +692,6 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
</div>
</div>
)}
{watchIssue("entity") && (
<div className="py-1">
<SidebarCustomAttributesList issue={issueDetail} />
</div>
)}
</div>
</div>
</>

View File

@ -91,6 +91,21 @@ class CustomAttributesService extends APIService {
throw error?.response?.data;
});
}
async deletePropertyValue(
workspaceSlug: string,
projectId: string,
issueId: string,
propertyId: string
): Promise<any> {
return this.delete(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/property-values/${propertyId}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}
const customAttributesService = new CustomAttributesService();

View File

@ -20,6 +20,7 @@ class CustomAttributeValuesStore {
issueAttributeValues: observable.ref,
fetchIssueAttributeValues: action,
createAttributeValue: action,
deleteAttributeValue: action,
});
this.rootStore = _rootStore;
@ -94,7 +95,6 @@ class CustomAttributeValuesStore {
...this.issueAttributeValues,
[issueId]: response.children,
};
this.fetchIssueAttributeValuesLoader = false;
});
} catch (error) {
runInAction(() => {
@ -102,6 +102,36 @@ class CustomAttributeValuesStore {
});
}
};
deleteAttributeValue = async (
workspaceSlug: string,
projectId: string,
issueId: string,
propertyId: string
) => {
const newChildren = [...(this.issueAttributeValues?.[issueId] ?? [])];
newChildren.filter((c) => c.id !== propertyId);
try {
runInAction(() => {
this.issueAttributeValues = {
...this.issueAttributeValues,
[issueId]: newChildren,
};
});
await customAttributesService.deletePropertyValue(
workspaceSlug,
projectId,
issueId,
propertyId
);
} catch (error) {
runInAction(() => {
this.error = error;
});
}
};
}
export default CustomAttributeValuesStore;