forked from github/plane
refactor: display components
This commit is contained in:
parent
14be78564a
commit
7cf263ecd4
@ -1,17 +1,25 @@
|
|||||||
// ui
|
// ui
|
||||||
import { ToggleSwitch } from "components/ui";
|
import { ToggleSwitch } from "components/ui";
|
||||||
// types
|
// 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 }> = ({
|
export const CustomCheckboxAttribute: React.FC<Props & { value: boolean }> = ({
|
||||||
attributeDetails,
|
attributeDetails,
|
||||||
onChange,
|
onChange,
|
||||||
value,
|
value,
|
||||||
}) => {
|
}) => {
|
||||||
const handleUpdateCheckbox = (val: boolean | string) => onChange(val.toString());
|
const handleUpdateCheckbox = (val: boolean) => onChange(val);
|
||||||
|
|
||||||
return (
|
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" ? (
|
{attributeDetails.extra_settings.representation === "toggle_switch" ? (
|
||||||
<ToggleSwitch value={value ?? false} onChange={handleUpdateCheckbox} />
|
<ToggleSwitch value={value ?? false} onChange={handleUpdateCheckbox} />
|
||||||
) : (
|
) : (
|
||||||
@ -23,7 +31,6 @@ export const CustomCheckboxAttribute: React.FC<Props & { value: boolean }> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<span>{attributeDetails.display_name}</span>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
// react-datepicker
|
// react-datepicker
|
||||||
import ReactDatePicker from "react-datepicker";
|
import ReactDatePicker from "react-datepicker";
|
||||||
// types
|
// 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 } = {
|
const DATE_FORMATS: { [key: string]: string } = {
|
||||||
"MM-DD-YYYY": "MM-dd-yyyy",
|
"MM-DD-YYYY": "MM-dd-yyyy",
|
||||||
@ -14,16 +22,12 @@ const TIME_FORMATS: { [key: string]: string } = {
|
|||||||
"24": "HH:mm",
|
"24": "HH:mm",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CustomDateTimeAttribute: React.FC<Props & { value: Date | undefined }> = ({
|
export const CustomDateTimeAttribute: React.FC<Props> = ({ attributeDetails, onChange, value }) => (
|
||||||
attributeDetails,
|
|
||||||
onChange,
|
|
||||||
value,
|
|
||||||
}) => (
|
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<ReactDatePicker
|
<ReactDatePicker
|
||||||
selected={value}
|
selected={value}
|
||||||
onChange={onChange}
|
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"
|
calendarClassName="!bg-custom-background-80"
|
||||||
dateFormat={`${
|
dateFormat={`${
|
||||||
attributeDetails.extra_settings.hide_date
|
attributeDetails.extra_settings.hide_date
|
||||||
@ -36,6 +40,7 @@ export const CustomDateTimeAttribute: React.FC<Props & { value: Date | undefined
|
|||||||
}`}
|
}`}
|
||||||
showTimeInput={!attributeDetails.extra_settings.hide_time}
|
showTimeInput={!attributeDetails.extra_settings.hide_time}
|
||||||
isClearable={!attributeDetails.is_required}
|
isClearable={!attributeDetails.is_required}
|
||||||
|
placeholderText={`Enter ${attributeDetails.display_name}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -3,13 +3,17 @@ import { useEffect, useState } from "react";
|
|||||||
// react-hook-form
|
// react-hook-form
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// types
|
// types
|
||||||
import { Props } from "./types";
|
import { ICustomAttribute } from "types";
|
||||||
|
|
||||||
export const CustomEmailAttribute: React.FC<Props & { value: string | undefined }> = ({
|
type Props = {
|
||||||
attributeDetails,
|
attributeDetails: ICustomAttribute;
|
||||||
onChange,
|
issueId: string;
|
||||||
value,
|
projectId: string;
|
||||||
}) => {
|
value: string | undefined;
|
||||||
|
onChange: (val: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CustomEmailAttribute: React.FC<Props> = ({ attributeDetails, onChange, value }) => {
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { email: "" } });
|
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { email: "" } });
|
||||||
@ -45,7 +49,10 @@ export const CustomEmailAttribute: React.FC<Props & { value: string | undefined
|
|||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
{!isEditing && (
|
{!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"}
|
{value && value !== "" ? value : "Empty"}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -8,17 +8,26 @@ import { useDropzone } from "react-dropzone";
|
|||||||
import fileService from "services/file.service";
|
import fileService from "services/file.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
|
import useWorkspaceDetails from "hooks/use-workspace-details";
|
||||||
// icons
|
// icons
|
||||||
import { getFileIcon } from "components/icons";
|
import { getFileIcon } from "components/icons";
|
||||||
|
import { X } from "lucide-react";
|
||||||
// helpers
|
// helpers
|
||||||
import { getFileExtension, getFileName } from "helpers/attachment.helper";
|
import { getFileExtension, getFileName } from "helpers/attachment.helper";
|
||||||
// types
|
// types
|
||||||
import { Props } from "./types";
|
import { ICustomAttribute } from "types";
|
||||||
import useWorkspaceDetails from "hooks/use-workspace-details";
|
|
||||||
|
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
|
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 { attributeDetails, onChange, value } = props;
|
||||||
|
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
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({
|
const { getRootProps, getInputProps, isDragActive, isDragReject, fileRejections } = useDropzone({
|
||||||
onDrop,
|
onDrop,
|
||||||
maxSize: MAX_FILE_SIZE,
|
maxSize: MAX_FILE_SIZE,
|
||||||
@ -95,15 +111,24 @@ export const CustomFileAttribute: React.FC<Props & { value: string | undefined }
|
|||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0 truncate space-y-1">
|
<div className="flex-shrink-0 truncate space-y-1">
|
||||||
{value && value !== "" && (
|
{value && value !== "" && (
|
||||||
<a
|
<div className="group flex items-center justify-between gap-2 p-1 rounded border border-custom-border-200 text-xs truncate">
|
||||||
href={value}
|
<a
|
||||||
target="_blank"
|
href={value}
|
||||||
rel="noopener noreferrer"
|
target="_blank"
|
||||||
className="flex items-center gap-1 p-1 rounded border border-custom-border-200 text-xs truncate"
|
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>
|
<span className="flex-shrink-0 h-6 w-6">{getFileIcon(getFileExtension(value))}</span>
|
||||||
</a>
|
<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
|
<div
|
||||||
{...getRootProps()}
|
{...getRootProps()}
|
||||||
|
@ -5,13 +5,17 @@ import { Controller, useForm } from "react-hook-form";
|
|||||||
// ui
|
// ui
|
||||||
import { ProgressBar } from "components/ui";
|
import { ProgressBar } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import { Props } from "./types";
|
import { ICustomAttribute } from "types";
|
||||||
|
|
||||||
export const CustomNumberAttribute: React.FC<Props & { value: number | undefined }> = ({
|
type Props = {
|
||||||
attributeDetails,
|
attributeDetails: ICustomAttribute;
|
||||||
onChange,
|
issueId: string;
|
||||||
value,
|
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 [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
const { control, handleSubmit, reset, setFocus } = useForm({ defaultValues: { number: "" } });
|
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 }) => {
|
const handleFormSubmit = (data: { number: string }) => {
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
|
|
||||||
onChange(data.number);
|
const number = parseInt(data.number, 10);
|
||||||
|
|
||||||
|
if (isNaN(number)) onChange(undefined);
|
||||||
|
else onChange(number);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -84,10 +91,10 @@ export const CustomNumberAttribute: React.FC<Props & { value: number | undefined
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<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)}
|
onClick={() => setIsEditing(true)}
|
||||||
>
|
>
|
||||||
{value ?? `Enter ${attributeDetails.display_name}`}
|
{value ?? "Empty"}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,11 +12,19 @@ import modulesService from "services/modules.service";
|
|||||||
// icons
|
// icons
|
||||||
import { Search } from "lucide-react";
|
import { Search } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { Props } from "./types";
|
import { ICustomAttribute } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { CYCLES_LIST, MODULE_LIST } from "constants/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,
|
attributeDetails,
|
||||||
onChange,
|
onChange,
|
||||||
projectId,
|
projectId,
|
||||||
|
@ -7,15 +7,22 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
|||||||
// icons
|
// icons
|
||||||
import { Check, Search } from "lucide-react";
|
import { Check, Search } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { Props } from "./types";
|
import { ICustomAttribute } from "types";
|
||||||
|
|
||||||
export const CustomSelectAttribute: React.FC<
|
type Props = {
|
||||||
Props &
|
attributeDetails: ICustomAttribute;
|
||||||
(
|
issueId: string;
|
||||||
| { multiple?: false; value: string | undefined }
|
projectId: string;
|
||||||
| { multiple?: true; value: string[] | undefined }
|
} & (
|
||||||
)
|
| {
|
||||||
> = (props) => {
|
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 { attributeDetails, multiple = false, onChange, value } = props;
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
@ -62,8 +69,8 @@ export const CustomSelectAttribute: React.FC<
|
|||||||
});
|
});
|
||||||
|
|
||||||
const comboboxProps: any = {
|
const comboboxProps: any = {
|
||||||
value,
|
|
||||||
onChange,
|
onChange,
|
||||||
|
value,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (multiple) comboboxProps.multiple = true;
|
if (multiple) comboboxProps.multiple = true;
|
||||||
@ -101,14 +108,16 @@ export const CustomSelectAttribute: React.FC<
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<span
|
<div className="flex items-center gap-2 flex-wrap">
|
||||||
className="px-2.5 py-0.5 rounded text-xs"
|
<span
|
||||||
style={{
|
className="px-2.5 py-0.5 rounded text-xs"
|
||||||
backgroundColor: `${options.find((o) => o.id === value)?.color}40`,
|
style={{
|
||||||
}}
|
backgroundColor: `${options.find((o) => o.id === value)?.color}40`,
|
||||||
>
|
}}
|
||||||
{options.find((o) => o.id === value)?.display_name}
|
>
|
||||||
</span>
|
{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">
|
<div className="cursor-pointer text-xs truncate bg-custom-background-80 px-2.5 py-0.5 rounded">
|
||||||
|
@ -3,7 +3,15 @@ import { useEffect, useState } from "react";
|
|||||||
// react-hook-form
|
// react-hook-form
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// types
|
// 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 }> = ({
|
export const CustomTextAttribute: React.FC<Props & { value: string | undefined }> = ({
|
||||||
attributeDetails,
|
attributeDetails,
|
||||||
@ -45,7 +53,10 @@ export const CustomTextAttribute: React.FC<Props & { value: string | undefined }
|
|||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
{!isEditing && (
|
{!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}`}
|
{value && value !== "" ? value : `Enter ${attributeDetails.display_name}`}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
// types
|
|
||||||
import { ICustomAttribute } from "types";
|
|
||||||
|
|
||||||
export type Props = {
|
|
||||||
attributeDetails: ICustomAttribute;
|
|
||||||
issueId: string;
|
|
||||||
onChange: (value: any) => void;
|
|
||||||
projectId: string;
|
|
||||||
};
|
|
@ -3,7 +3,15 @@ import { useEffect, useState } from "react";
|
|||||||
// react-hook-form
|
// react-hook-form
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
// types
|
// 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 }> = ({
|
export const CustomUrlAttribute: React.FC<Props & { value: string | undefined }> = ({
|
||||||
attributeDetails,
|
attributeDetails,
|
||||||
@ -43,8 +51,11 @@ export const CustomUrlAttribute: React.FC<Props & { value: string | undefined }>
|
|||||||
return (
|
return (
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
{!isEditing && (
|
{!isEditing && (
|
||||||
<div className="cursor-pointer text-xs truncate" onClick={() => setIsEditing(true)}>
|
<div
|
||||||
{value && value !== "" ? value : `Enter ${attributeDetails.display_name}`}
|
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>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isEditing && (
|
{isEditing && (
|
||||||
|
@ -3,11 +3,11 @@ import { Controller } from "react-hook-form";
|
|||||||
// components
|
// components
|
||||||
import { FormComponentProps, Input } from "components/custom-attributes";
|
import { FormComponentProps, Input } from "components/custom-attributes";
|
||||||
// ui
|
// ui
|
||||||
import { CustomSelect, ToggleSwitch } from "components/ui";
|
import { CustomSelect, ToggleSwitch, Tooltip } from "components/ui";
|
||||||
// constants
|
// constants
|
||||||
import { DATE_FORMATS, TIME_FORMATS } from "constants/custom-attributes";
|
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">
|
<div className="space-y-3">
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@ -44,8 +44,20 @@ export const DateTimeAttributeForm: React.FC<FormComponentProps> = ({ control })
|
|||||||
name="extra_settings.hide_date"
|
name="extra_settings.hide_date"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<div className="flex items-center justify-end gap-1 mt-2">
|
<div className="flex items-center justify-end gap-1 mt-2">
|
||||||
<ToggleSwitch value={value ?? false} onChange={onChange} size="sm" />
|
<Tooltip
|
||||||
<span className="text-xs">Don{"'"}t show date</span>
|
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>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@ -78,8 +90,20 @@ export const DateTimeAttributeForm: React.FC<FormComponentProps> = ({ control })
|
|||||||
name="extra_settings.hide_time"
|
name="extra_settings.hide_time"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<div className="flex items-center justify-end gap-1 mt-2">
|
<div className="flex items-center justify-end gap-1 mt-2">
|
||||||
<ToggleSwitch value={value ?? false} onChange={onChange} size="sm" />
|
<Tooltip
|
||||||
<span className="text-xs">Don{"'"}t show time</span>
|
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>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
@ -63,7 +63,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
|
|||||||
<CustomCheckboxAttribute
|
<CustomCheckboxAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
onChange={(val: string) => onChange(attribute.id, [val])}
|
onChange={(val) => onChange(attribute.id, [`${val}`])}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
value={values[attribute.id]?.[0] === "true" ? true : false}
|
value={values[attribute.id]?.[0] === "true" ? true : false}
|
||||||
/>
|
/>
|
||||||
@ -72,7 +72,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
|
|||||||
<CustomDateTimeAttribute
|
<CustomDateTimeAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
onChange={(val: string) => onChange(attribute.id, [val])}
|
onChange={(val) => onChange(attribute.id, [val?.toISOString() ?? ""])}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
value={
|
value={
|
||||||
values[attribute.id]?.[0] ? new Date(values[attribute.id]?.[0]) : undefined
|
values[attribute.id]?.[0] ? new Date(values[attribute.id]?.[0]) : undefined
|
||||||
@ -101,7 +101,9 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
|
|||||||
<CustomSelectAttribute
|
<CustomSelectAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
onChange={(val: string[]) => onChange(attribute.id, val)}
|
onChange={(val) => {
|
||||||
|
if (val) onChange(attribute.id, val);
|
||||||
|
}}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
value={values[attribute.id] ?? []}
|
value={values[attribute.id] ?? []}
|
||||||
multiple
|
multiple
|
||||||
@ -111,7 +113,9 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
|
|||||||
<CustomNumberAttribute
|
<CustomNumberAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
onChange={(val: string) => onChange(attribute.id, [val])}
|
onChange={(val) => {
|
||||||
|
if (val) onChange(attribute.id, [val.toString()]);
|
||||||
|
}}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
value={
|
value={
|
||||||
values[attribute.id]?.[0] ? parseInt(values[attribute.id]?.[0]) : undefined
|
values[attribute.id]?.[0] ? parseInt(values[attribute.id]?.[0]) : undefined
|
||||||
@ -122,7 +126,9 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
|
|||||||
<CustomRelationAttribute
|
<CustomRelationAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
onChange={(val: string) => onChange(attribute.id, [val])}
|
onChange={(val) => {
|
||||||
|
if (val) onChange(attribute.id, [val]);
|
||||||
|
}}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
value={values[attribute.id]?.[0]}
|
value={values[attribute.id]?.[0]}
|
||||||
/>
|
/>
|
||||||
@ -131,16 +137,19 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
|
|||||||
<CustomSelectAttribute
|
<CustomSelectAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
onChange={(val: string) => onChange(attribute.id, [val])}
|
onChange={(val) => {
|
||||||
|
if (val) onChange(attribute.id, [val]);
|
||||||
|
}}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
value={attribute.default_value !== "" ? attribute.default_value : undefined}
|
value={attribute.default_value !== "" ? attribute.default_value : undefined}
|
||||||
|
multiple={false}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{attribute.type === "text" && (
|
{attribute.type === "text" && (
|
||||||
<CustomTextAttribute
|
<CustomTextAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
onChange={(val: string) => onChange(attribute.id, [val])}
|
onChange={(val) => onChange(attribute.id, [val])}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
value={attribute.default_value}
|
value={attribute.default_value}
|
||||||
/>
|
/>
|
||||||
@ -149,10 +158,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
|
|||||||
<CustomUrlAttribute
|
<CustomUrlAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
onChange={(val: string) => {
|
onChange={(val: string) => onChange(attribute.id, [val])}
|
||||||
console.log(val);
|
|
||||||
onChange(attribute.id, [val]);
|
|
||||||
}}
|
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
value={values[attribute.id]?.[0]}
|
value={values[attribute.id]?.[0]}
|
||||||
/>
|
/>
|
||||||
|
@ -26,9 +26,10 @@ import { CUSTOM_ATTRIBUTES_LIST } from "constants/custom-attributes";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: IIssue | undefined;
|
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 router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
@ -37,14 +38,20 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
|
|||||||
customAttributeValues: customAttributeValuesStore,
|
customAttributeValues: customAttributeValuesStore,
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
const { entityAttributes, fetchEntityDetails } = customAttributesStore;
|
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 (!issue || !workspaceSlug) return;
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
deleteAttributeValue(workspaceSlug.toString(), projectId, issue.id, attributeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const payload: ICustomAttributeValueFormData = {
|
const payload: ICustomAttributeValueFormData = {
|
||||||
issue_properties: {
|
issue_properties: {
|
||||||
[attributeId]: value,
|
[attributeId]: Array.isArray(value) ? value : [value],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -109,7 +116,7 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
|
|||||||
<CustomCheckboxAttribute
|
<CustomCheckboxAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issue.id}
|
issueId={issue.id}
|
||||||
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
|
onChange={(val) => handleAttributeUpdate(attribute.id, [`${val}`])}
|
||||||
projectId={issue.project}
|
projectId={issue.project}
|
||||||
value={
|
value={
|
||||||
attributeValue ? (attributeValue?.[0]?.value === "true" ? true : false) : false
|
attributeValue ? (attributeValue?.[0]?.value === "true" ? true : false) : false
|
||||||
@ -120,7 +127,9 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
|
|||||||
<CustomDateTimeAttribute
|
<CustomDateTimeAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issue.id}
|
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}
|
projectId={issue.project}
|
||||||
value={attributeValue ? new Date(attributeValue?.[0]?.value ?? "") : undefined}
|
value={attributeValue ? new Date(attributeValue?.[0]?.value ?? "") : undefined}
|
||||||
/>
|
/>
|
||||||
@ -129,7 +138,9 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
|
|||||||
<CustomEmailAttribute
|
<CustomEmailAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issue.id}
|
issueId={issue.id}
|
||||||
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
|
onChange={(val) => {
|
||||||
|
handleAttributeUpdate(attribute.id, val && val !== "" ? [val] : undefined);
|
||||||
|
}}
|
||||||
projectId={issue.project}
|
projectId={issue.project}
|
||||||
value={attributeValue ? attributeValue?.[0]?.value : undefined}
|
value={attributeValue ? attributeValue?.[0]?.value : undefined}
|
||||||
/>
|
/>
|
||||||
@ -138,7 +149,7 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
|
|||||||
<CustomFileAttribute
|
<CustomFileAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issue.id}
|
issueId={issue.id}
|
||||||
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
|
onChange={(val) => handleAttributeUpdate(attribute.id, val)}
|
||||||
projectId={issue.project}
|
projectId={issue.project}
|
||||||
value={attributeValue ? attributeValue?.[0]?.value : undefined}
|
value={attributeValue ? attributeValue?.[0]?.value : undefined}
|
||||||
/>
|
/>
|
||||||
@ -147,7 +158,7 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
|
|||||||
<CustomSelectAttribute
|
<CustomSelectAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issue.id}
|
issueId={issue.id}
|
||||||
onChange={(val: string[]) => handleAttributeUpdate(attribute.id, val)}
|
onChange={(val) => handleAttributeUpdate(attribute.id, val)}
|
||||||
projectId={issue.project}
|
projectId={issue.project}
|
||||||
value={Array.isArray(attributeValue) ? attributeValue.map((v) => v.value) : []}
|
value={Array.isArray(attributeValue) ? attributeValue.map((v) => v.value) : []}
|
||||||
multiple
|
multiple
|
||||||
@ -157,7 +168,9 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
|
|||||||
<CustomNumberAttribute
|
<CustomNumberAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issue.id}
|
issueId={issue.id}
|
||||||
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
|
onChange={(val) => {
|
||||||
|
handleAttributeUpdate(attribute.id, val ? val.toString() : undefined);
|
||||||
|
}}
|
||||||
projectId={issue.project}
|
projectId={issue.project}
|
||||||
value={
|
value={
|
||||||
attributeValue ? parseInt(attributeValue?.[0]?.value ?? "0", 10) : undefined
|
attributeValue ? parseInt(attributeValue?.[0]?.value ?? "0", 10) : undefined
|
||||||
@ -168,7 +181,7 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
|
|||||||
<CustomRelationAttribute
|
<CustomRelationAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issue.id}
|
issueId={issue.id}
|
||||||
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
|
onChange={(val) => handleAttributeUpdate(attribute.id, val)}
|
||||||
projectId={issue.project}
|
projectId={issue.project}
|
||||||
value={attributeValue ? attributeValue?.[0]?.value : undefined}
|
value={attributeValue ? attributeValue?.[0]?.value : undefined}
|
||||||
/>
|
/>
|
||||||
@ -177,16 +190,19 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
|
|||||||
<CustomSelectAttribute
|
<CustomSelectAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issue.id}
|
issueId={issue.id}
|
||||||
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
|
onChange={(val) => handleAttributeUpdate(attribute.id, val)}
|
||||||
projectId={issue.project}
|
projectId={issue.project}
|
||||||
value={attributeValue ? attributeValue?.[0]?.value : undefined}
|
value={attributeValue ? attributeValue?.[0]?.value : undefined}
|
||||||
|
multiple={false}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{attribute.type === "text" && (
|
{attribute.type === "text" && (
|
||||||
<CustomTextAttribute
|
<CustomTextAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issue.id}
|
issueId={issue.id}
|
||||||
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
|
onChange={(val) =>
|
||||||
|
handleAttributeUpdate(attribute.id, val && val !== "" ? [val] : undefined)
|
||||||
|
}
|
||||||
projectId={issue.project}
|
projectId={issue.project}
|
||||||
value={attributeValue ? attributeValue?.[0].value : undefined}
|
value={attributeValue ? attributeValue?.[0].value : undefined}
|
||||||
/>
|
/>
|
||||||
@ -195,7 +211,9 @@ export const SidebarCustomAttributesList: React.FC<Props> = observer(({ issue })
|
|||||||
<CustomUrlAttribute
|
<CustomUrlAttribute
|
||||||
attributeDetails={attribute}
|
attributeDetails={attribute}
|
||||||
issueId={issue.id}
|
issueId={issue.id}
|
||||||
onChange={(val: string) => handleAttributeUpdate(attribute.id, [val])}
|
onChange={(val) =>
|
||||||
|
handleAttributeUpdate(attribute.id, val && val !== "" ? [val] : undefined)
|
||||||
|
}
|
||||||
projectId={issue.project}
|
projectId={issue.project}
|
||||||
value={attributeValue ? attributeValue?.[0]?.value : undefined}
|
value={attributeValue ? attributeValue?.[0]?.value : undefined}
|
||||||
/>
|
/>
|
||||||
|
@ -305,7 +305,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
data={issueDetail ?? null}
|
data={issueDetail ?? null}
|
||||||
user={user}
|
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">
|
<div className="flex items-center justify-between px-5 pb-3">
|
||||||
<h4 className="text-sm font-medium">
|
<h4 className="text-sm font-medium">
|
||||||
{issueDetail?.project_detail?.identifier}-{issueDetail?.sequence_id}
|
{issueDetail?.project_detail?.identifier}-{issueDetail?.sequence_id}
|
||||||
@ -349,30 +349,9 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="h-full w-full px-5 overflow-y-auto">
|
<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 && (
|
{showFirstSection && (
|
||||||
<div className="py-1">
|
<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")) && (
|
{(fieldsToShow.includes("all") || fieldsToShow.includes("state")) && (
|
||||||
<div className="flex flex-wrap items-center py-2">
|
<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">
|
<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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{watchIssue("entity") && (
|
||||||
|
<div className="py-1">
|
||||||
|
<SidebarCustomAttributesList
|
||||||
|
issue={issueDetail}
|
||||||
|
projectId={projectId?.toString() ?? ""}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{(fieldsToShow.includes("all") || fieldsToShow.includes("label")) &&
|
{(fieldsToShow.includes("all") || fieldsToShow.includes("label")) &&
|
||||||
watchIssue("entity") === null && (
|
watchIssue("entity") === null && (
|
||||||
@ -705,11 +692,6 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{watchIssue("entity") && (
|
|
||||||
<div className="py-1">
|
|
||||||
<SidebarCustomAttributesList issue={issueDetail} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -91,6 +91,21 @@ class CustomAttributesService extends APIService {
|
|||||||
throw error?.response?.data;
|
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();
|
const customAttributesService = new CustomAttributesService();
|
||||||
|
@ -20,6 +20,7 @@ class CustomAttributeValuesStore {
|
|||||||
issueAttributeValues: observable.ref,
|
issueAttributeValues: observable.ref,
|
||||||
fetchIssueAttributeValues: action,
|
fetchIssueAttributeValues: action,
|
||||||
createAttributeValue: action,
|
createAttributeValue: action,
|
||||||
|
deleteAttributeValue: action,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.rootStore = _rootStore;
|
this.rootStore = _rootStore;
|
||||||
@ -94,7 +95,6 @@ class CustomAttributeValuesStore {
|
|||||||
...this.issueAttributeValues,
|
...this.issueAttributeValues,
|
||||||
[issueId]: response.children,
|
[issueId]: response.children,
|
||||||
};
|
};
|
||||||
this.fetchIssueAttributeValuesLoader = false;
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
runInAction(() => {
|
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;
|
export default CustomAttributeValuesStore;
|
||||||
|
Loading…
Reference in New Issue
Block a user