chore: custom attributes on the create issue modal

This commit is contained in:
Aaryan Khandelwal 2023-09-15 17:47:51 +05:30
parent 57f4941ee2
commit ef77ca6524
5 changed files with 147 additions and 57 deletions

View File

@ -84,7 +84,11 @@ export const CustomSelectAttribute: React.FC<
backgroundColor: `${selectedOption?.color}40`,
}}
>
{selectedOption?.display_name ?? "Select"}
{Array.isArray(value)
? value.length > 0
? value.map((v) => options.find((o) => o.id === v)?.display_name).join(", ")
: `Select ${attributeDetails.display_name}`
: value}
</Combobox.Button>
<Transition
as={React.Fragment}
@ -98,7 +102,7 @@ export const CustomSelectAttribute: React.FC<
<div className="fixed z-10 top-0 left-0 h-full w-full cursor-auto">
<Combobox.Options
ref={dropdownOptionsRef}
className="absolute z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none w-48 whitespace-nowrap"
className="fixed z-10 border border-custom-border-300 px-2 py-2.5 rounded bg-custom-background-100 text-xs shadow-lg focus:outline-none w-48 whitespace-nowrap"
>
<div className="flex w-full items-center justify-start rounded-sm border-[0.6px] border-custom-border-200 bg-custom-background-90 px-2 mb-1">
<Search className="text-custom-text-400" size={12} strokeWidth={1.5} />

View File

@ -21,9 +21,7 @@ export const CustomUrlAttribute: React.FC<Props & { value: string | undefined }>
};
useEffect(() => {
if (isEditing) {
setFocus("url");
}
if (isEditing) setFocus("url");
}, [isEditing, setFocus]);
useEffect(() => {

View File

@ -8,9 +8,14 @@ import { observer } from "mobx-react-lite";
// components
import {
CustomCheckboxAttribute,
CustomDateTimeAttribute,
CustomEmailAttribute,
CustomFileAttribute,
CustomNumberAttribute,
CustomRelationAttribute,
CustomSelectAttribute,
CustomTextAttribute,
CustomUrlAttribute,
} from "components/custom-attributes";
// ui
import { Loader } from "components/ui";
@ -18,12 +23,13 @@ import { Loader } from "components/ui";
type Props = {
entityId: string;
issueId: string;
onSubmit: () => Promise<void>;
onChange: (attributeId: string, val: string[]) => Promise<void>;
projectId: string;
values: { [key: string]: string[] };
};
export const IssueModalCustomAttributesList: React.FC<Props> = observer(
({ entityId, issueId, onSubmit, projectId }) => {
({ entityId, issueId, onChange, projectId, values }) => {
const router = useRouter();
const { workspaceSlug } = router.query;
@ -42,7 +48,7 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
}, [entityAttributes, entityId, fetchEntityDetails, workspaceSlug]);
return (
<div>
<>
{fetchEntityDetailsLoader ? (
<Loader className="flex items-center gap-2">
<Loader.Item height="27px" width="90px" />
@ -50,50 +56,114 @@ export const IssueModalCustomAttributesList: React.FC<Props> = observer(
<Loader.Item height="27px" width="90px" />
</Loader>
) : (
<div className="flex items-center gap-2">
<>
{Object.entries(attributes).map(([attributeId, attribute]) => (
<div key={attributeId}>
{attribute.type === "text" && (
<CustomTextAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={() => {}}
projectId={projectId}
value={attribute.default_value ?? ""}
/>
)}
{attribute.type === "select" && (
<CustomSelectAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={() => {}}
projectId={projectId}
value={attribute.default_value ?? ""}
/>
)}
{attribute.type === "checkbox" && (
<CustomCheckboxAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={() => {}}
onChange={(val: string) => onChange(attribute.id, [val])}
projectId={projectId}
value={attribute.default_value === "checked" ? true : false}
/>
)}
{attribute.type === "datetime" && (
<CustomDateTimeAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
projectId={projectId}
value={
attribute.default_value !== "" ? new Date(attribute.default_value) : undefined
}
/>
)}
{attribute.type === "email" && (
<CustomEmailAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
projectId={projectId}
value={attribute.default_value}
/>
)}
{attribute.type === "file" && (
<CustomFileAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={() => {}}
onChange={(val: string) => onChange(attribute.id, [val])}
projectId={projectId}
value={null}
value={undefined}
/>
)}
{attribute.type === "multi_select" && (
<CustomSelectAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string[]) => onChange(attribute.id, val)}
projectId={projectId}
value={[]}
multiple
/>
)}
{attribute.type === "number" && (
<CustomNumberAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
projectId={projectId}
value={
attribute.default_value !== ""
? parseInt(attribute.default_value, 10)
: undefined
}
/>
)}
{attribute.type === "relation" && (
<CustomRelationAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
projectId={projectId}
value={attribute.default_value !== "" ? attribute.default_value : undefined}
/>
)}
{attribute.type === "select" && (
<CustomSelectAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
projectId={projectId}
value={attribute.default_value !== "" ? attribute.default_value : undefined}
/>
)}
{attribute.type === "text" && (
<CustomTextAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => onChange(attribute.id, [val])}
projectId={projectId}
value={attribute.default_value}
/>
)}
{attribute.type === "url" && (
<CustomUrlAttribute
attributeDetails={attribute}
issueId={issueId}
onChange={(val: string) => {
console.log(val);
onChange(attribute.id, [val]);
}}
projectId={projectId}
value={values[attribute.id]?.[0]}
/>
)}
</div>
))}
</div>
</>
)}
</div>
</>
);
}
);

View File

@ -2,6 +2,9 @@ import React, { FC, useState, useEffect, useRef } from "react";
import { useRouter } from "next/router";
// mobx
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
// react-hook-form
import { Controller, useForm } from "react-hook-form";
// services
@ -75,18 +78,20 @@ export interface IssueFormProps {
)[];
}
export const IssueForm: FC<IssueFormProps> = ({
handleFormSubmit,
initialData,
projectId,
setActiveProject,
createMore,
setCreateMore,
handleClose,
status,
user,
fieldsToShow,
}) => {
export const IssueForm: FC<IssueFormProps> = observer((props) => {
const {
handleFormSubmit,
initialData,
projectId,
setActiveProject,
createMore,
setCreateMore,
handleClose,
status,
user,
fieldsToShow,
} = props;
const [stateModal, setStateModal] = useState(false);
const [labelModal, setLabelModal] = useState(false);
const [parentIssueListModalOpen, setParentIssueListModalOpen] = useState(false);
@ -95,6 +100,8 @@ export const IssueForm: FC<IssueFormProps> = ({
const [gptAssistantModal, setGptAssistantModal] = useState(false);
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
const [attributesList, setAttributesList] = useState<{ [key: string]: string[] }>({});
const editorRef = useRef<any>(null);
const router = useRouter();
@ -102,6 +109,9 @@ export const IssueForm: FC<IssueFormProps> = ({
const { setToastAlert } = useToast();
const { customAttributeValues: customAttributeValuesStore } = useMobxStore();
const { createAttributeValue } = customAttributeValuesStore;
const {
register,
formState: { errors, isSubmitting },
@ -122,19 +132,17 @@ export const IssueForm: FC<IssueFormProps> = ({
const handleCreateUpdateIssue = async (formData: Partial<IIssue>) => {
await handleFormSubmit(formData);
if (!workspaceSlug) return;
await createAttributeValue(workspaceSlug.toString(), projectId, watch("id"), {
issue_properties: attributesList,
});
setGptAssistantModal(false);
reset({
...defaultValues,
project: projectId,
description: {
type: "doc",
content: [
{
type: "paragraph",
},
],
},
description_html: "<p></p>",
});
editorRef?.current?.clearEditor();
@ -220,6 +228,8 @@ export const IssueForm: FC<IssueFormProps> = ({
const maxDate = targetDate ? new Date(targetDate) : null;
maxDate?.setDate(maxDate.getDate());
console.log("attributesList", attributesList);
return (
<>
{projectId && (
@ -545,9 +555,17 @@ export const IssueForm: FC<IssueFormProps> = ({
) : (
<IssueModalCustomAttributesList
entityId={watch("entity") ?? ""}
issueId=""
onSubmit={async () => {}}
issueId={watch("id") ?? ""}
onChange={async (attributeId: string, val: string[]) => {
console.log(val);
setAttributesList((prev) => ({
...prev,
[attributeId]: val,
}));
}}
projectId={projectId}
values={attributesList}
/>
)}
</div>
@ -578,4 +596,4 @@ export const IssueForm: FC<IssueFormProps> = ({
</form>
</>
);
};
});

View File

@ -25,7 +25,7 @@ class ProjectIssuesServices extends APIService {
projectId: string,
data: any,
user: ICurrentUserResponse | undefined
): Promise<any> {
): Promise<IIssue> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/`, data)
.then((response) => {
if (trackEvent) trackEventServices.trackIssueEvent(response.data, "ISSUE_CREATE", user);
@ -404,7 +404,7 @@ class ProjectIssuesServices extends APIService {
issueId: string,
data: Partial<IIssue>,
user: ICurrentUserResponse | undefined
): Promise<any> {
): Promise<IIssue> {
return this.patch(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/`,
data