dev: sync staging with master

This commit is contained in:
vamsi 2022-11-25 00:40:36 +05:30
commit 6d7abf6590
36 changed files with 1449 additions and 1366 deletions

View file

@ -36,6 +36,7 @@ type Props = {
>;
bgColor?: string;
stateId?: string;
createdBy?: string;
};
const SingleBoard: React.FC<Props> = ({
@ -48,6 +49,7 @@ const SingleBoard: React.FC<Props> = ({
setPreloadedData,
bgColor = "#0f2b16",
stateId,
createdBy,
}) => {
// Collapse/Expand
const [show, setState] = useState<any>(true);
@ -118,6 +120,8 @@ const SingleBoard: React.FC<Props> = ({
>
{groupTitle === null || groupTitle === "null"
? "None"
: createdBy
? createdBy
: addSpaceIfCamelCase(groupTitle)}
</h2>
<span className="text-gray-500 text-sm ml-0.5">
@ -280,7 +284,7 @@ const SingleBoard: React.FC<Props> = ({
</div>
) : (
<div
className={`h-5 w-5 bg-gray-500 text-white border-2 border-white grid place-items-center rounded-full`}
className={`h-5 w-5 bg-gray-700 text-white border-2 border-white grid place-items-center rounded-full`}
>
{assignee.first_name.charAt(0)}
</div>

View file

@ -18,9 +18,9 @@ import SingleBoard from "components/project/issues/BoardView/SingleBoard";
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
import CreateUpdateIssuesModal from "components/project/issues/CreateUpdateIssueModal";
// ui
import { Spinner, Button } from "ui";
import { Spinner } from "ui";
// types
import type { IState, IIssue, Properties, NestedKeyOf } from "types";
import type { IState, IIssue, Properties, NestedKeyOf, ProjectMember } from "types";
type Props = {
properties: Properties;
@ -28,9 +28,10 @@ type Props = {
groupedByIssues: {
[key: string]: IIssue[];
};
members: ProjectMember[] | undefined;
};
const BoardView: React.FC<Props> = ({ properties, selectedGroup, groupedByIssues }) => {
const BoardView: React.FC<Props> = ({ properties, selectedGroup, groupedByIssues, members }) => {
const [isOpen, setIsOpen] = useState(false);
const [isIssueOpen, setIsIssueOpen] = useState(false);
@ -164,7 +165,7 @@ const BoardView: React.FC<Props> = ({ properties, selectedGroup, groupedByIssues
/>
{groupedByIssues ? (
groupedByIssues ? (
<div className="h-full w-full">
<div className="w-full" style={{ height: "calc(82vh - 1.5rem)" }}>
<DragDropContext onDragEnd={handleOnDragEnd}>
<div className="h-full w-full overflow-hidden">
<StrictModeDroppable droppableId="state" type="state" direction="horizontal">
@ -180,6 +181,12 @@ const BoardView: React.FC<Props> = ({ properties, selectedGroup, groupedByIssues
key={singleGroup}
selectedGroup={selectedGroup}
groupTitle={singleGroup}
createdBy={
members
? members?.find((m) => m.member.id === singleGroup)?.member
.first_name
: undefined
}
groupedByIssues={groupedByIssues}
index={index}
setIsIssueOpen={setIsIssueOpen}

View file

@ -46,7 +46,7 @@ const SelectSprint: React.FC<Props> = ({ control, setIsOpen }) => {
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute mt-1 bg-white shadow-lg max-h-28 rounded-md py-1 text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<Listbox.Options className="absolute z-10 mt-1 bg-white shadow-lg max-h-28 rounded-md py-1 text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<div className="p-1">
{sprints?.map((sprint) => (
<Listbox.Option
@ -63,16 +63,6 @@ const SelectSprint: React.FC<Props> = ({ control, setIsOpen }) => {
<span className={`block ${selected && "font-semibold"}`}>
{sprint.name}
</span>
{selected && (
<span
className={`absolute inset-y-0 right-0 flex items-center pr-4 ${
active ? "text-white" : "text-indigo-600"
}`}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
)}
</>
)}
</Listbox.Option>

View file

@ -98,7 +98,7 @@ const SelectLabels: React.FC<Props> = ({ control }) => {
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute mt-1 bg-white shadow-lg max-h-28 rounded-md text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<Listbox.Options className="absolute z-10 mt-1 bg-white shadow-lg max-h-28 rounded-md text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<div className="p-1">
{issueLabels?.map((label) => (
<Listbox.Option
@ -121,18 +121,6 @@ const SelectLabels: React.FC<Props> = ({ control }) => {
>
{label.name}
</span>
{selected ? (
<span
className={`absolute inset-y-0 right-0 flex items-center pr-4 ${
active || (value ?? []).some((i) => i === label.id)
? "text-white"
: "text-indigo-600"
}`}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>

View file

@ -39,7 +39,7 @@ const SelectPriority: React.FC<Props> = ({ control }) => {
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute mt-1 w-full bg-white shadow-lg max-h-28 rounded-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none text-xs">
<Listbox.Options className="absolute z-10 mt-1 w-full w-[5rem] bg-white shadow-lg max-h-28 rounded-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none text-xs">
<div className="p-1">
{PRIORITIES.map((priority) => (
<Listbox.Option
@ -55,21 +55,11 @@ const SelectPriority: React.FC<Props> = ({ control }) => {
<>
<span
className={`block capitalize ${
selected ? "font-semibold" : "font-normal"
selected ? "font-medium" : "font-normal"
}`}
>
{priority}
</span>
{selected ? (
<span
className={`absolute inset-y-0 right-0 flex items-center pr-4 ${
active ? "text-white" : "text-indigo-600"
}`}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>

View file

@ -50,7 +50,7 @@ const SelectProject: React.FC<Props> = ({ control }) => {
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute mt-1 bg-white shadow-lg max-h-28 rounded-md py-1 text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<Listbox.Options className="absolute z-10 mt-1 bg-white shadow-lg max-h-28 rounded-md py-1 text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<div className="p-1">
{projects ? (
projects.length > 0 ? (

View file

@ -147,7 +147,7 @@ const CreateUpdateIssuesModal: React.FC<Props> = ({
setToastAlert({
title: "Success",
type: "success",
message: "Issue added to sprint successfully",
message: "Issue added to cycle successfully",
});
})
.catch((err) => {
@ -394,7 +394,7 @@ const CreateUpdateIssuesModal: React.FC<Props> = ({
register={register}
/>
</div>
<div className="flex items-center gap-2">
<div className="flex items-center flex-wrap gap-2">
<SelectState control={control} setIsOpen={setIsStateModalOpen} />
<SelectCycles control={control} setIsOpen={setIsCycleModalOpen} />
<SelectPriority control={control} />

View file

@ -19,11 +19,20 @@ import {
PROJECT_ISSUE_LABELS,
} from "constants/fetch-keys";
// commons
import { classNames } from "constants/common";
import { classNames, copyTextToClipboard } from "constants/common";
// ui
import { Input, Button } from "ui";
// icons
import { Bars3BottomRightIcon, PlusIcon, UserIcon, TagIcon } from "@heroicons/react/24/outline";
import {
UserIcon,
TagIcon,
UserGroupIcon,
ChevronDownIcon,
Squares2X2Icon,
ChartBarIcon,
ClipboardDocumentIcon,
LinkIcon,
} from "@heroicons/react/24/outline";
// types
import type { Control } from "react-hook-form";
import type { IIssue, IIssueLabels, IssueResponse, IState, WorkspaceMember } from "types";
@ -31,6 +40,7 @@ import type { IIssue, IIssueLabels, IssueResponse, IState, WorkspaceMember } fro
type Props = {
control: Control<IIssue, any>;
submitChanges: (formData: Partial<IIssue>) => void;
issueDetail: IIssue | undefined;
};
const PRIORITIES = ["high", "medium", "low"];
@ -39,7 +49,7 @@ const defaultValues: Partial<IIssueLabels> = {
name: "",
};
const IssueDetailSidebar: React.FC<Props> = ({ control, submitChanges }) => {
const IssueDetailSidebar: React.FC<Props> = ({ control, submitChanges, issueDetail }) => {
const { activeWorkspace, activeProject } = useUser();
const { data: states } = useSWR<IState[]>(
@ -90,65 +100,88 @@ const IssueDetailSidebar: React.FC<Props> = ({ control, submitChanges }) => {
});
};
const sidebarOptions = [
{
label: "Priority",
name: "priority",
canSelectMultipleOptions: false,
icon: ChartBarIcon,
options: PRIORITIES.map((property) => ({
label: property,
value: property,
})),
},
{
label: "Status",
name: "state",
canSelectMultipleOptions: false,
icon: Squares2X2Icon,
options: states?.map((state) => ({
label: state.name,
value: state.id,
})),
},
{
label: "Assignees",
name: "assignees_list",
canSelectMultipleOptions: true,
icon: UserGroupIcon,
options: people?.map((person) => ({
label: person.member.first_name,
value: person.member.id,
})),
},
{
label: "Blocker",
name: "blockers_list",
canSelectMultipleOptions: true,
icon: UserIcon,
options: projectIssues?.results?.map((issue) => ({
label: issue.name,
value: issue.id,
})),
},
{
label: "Blocked",
name: "blocked_list",
canSelectMultipleOptions: true,
icon: UserIcon,
options: projectIssues?.results?.map((issue) => ({
label: issue.name,
value: issue.id,
})),
},
];
return (
<div className="w-full h-full">
<div className="h-full w-full">
<div className="space-y-3">
<div className="flex flex-col gap-y-4">
{[
{
label: "Priority",
name: "priority",
canSelectMultipleOptions: false,
icon: Bars3BottomRightIcon,
options: PRIORITIES.map((property) => ({
label: property,
value: property,
})),
},
{
label: "Status",
name: "state",
canSelectMultipleOptions: false,
icon: Bars3BottomRightIcon,
options: states?.map((state) => ({
label: state.name,
value: state.id,
})),
},
{
label: "Assignees",
name: "assignees_list",
canSelectMultipleOptions: true,
icon: UserIcon,
options: people?.map((person) => ({
label: person.member.first_name,
value: person.member.id,
})),
},
{
label: "Blocker",
name: "blockers_list",
canSelectMultipleOptions: true,
icon: UserIcon,
options: projectIssues?.results?.map((issue) => ({
label: issue.name,
value: issue.id,
})),
},
{
label: "Blocked",
name: "blocked_list",
canSelectMultipleOptions: true,
icon: UserIcon,
options: projectIssues?.results?.map((issue) => ({
label: issue.name,
value: issue.id,
})),
},
].map((item) => (
<div className="flex items-center gap-x-2" key={item.label}>
<div className="flex items-center gap-x-2">
<item.icon className="w-5 h-5 text-gray-500" />
<h3 className="text-lg font-medium leading-6 text-gray-900">Quick Actions</h3>
<div className="flex items-center gap-2 flex-wrap">
<button
type="button"
className="p-2 hover:bg-gray-100 border rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 duration-300"
onClick={() =>
copyTextToClipboard(
`https://app.plane.so/projects/${activeProject?.id}/issues/${issueDetail?.id}`
)
}
>
<LinkIcon className="h-3.5 w-3.5" />
</button>
<button
type="button"
className="p-2 hover:bg-gray-100 border rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 duration-300"
onClick={() => copyTextToClipboard(`${issueDetail?.id}`)}
>
<ClipboardDocumentIcon className="h-3.5 w-3.5" />
</button>
</div>
{sidebarOptions.map((item) => (
<div className="flex items-center justify-between gap-x-2" key={item.label}>
<div className="flex items-center gap-x-2 text-sm">
<item.icon className="h-4 w-4" />
<p>{item.label}</p>
</div>
<div>
@ -160,68 +193,61 @@ const IssueDetailSidebar: React.FC<Props> = ({ control, submitChanges }) => {
as="div"
value={value}
multiple={item.canSelectMultipleOptions}
onChange={(value) => submitChanges({ [item.name]: value })}
onChange={(value: any) => submitChanges({ [item.name]: value })}
className="flex-shrink-0"
>
{({ open }) => (
<>
<Listbox.Label className="sr-only">{item.label}</Listbox.Label>
<div className="relative">
<Listbox.Button className="relative inline-flex items-center whitespace-nowrap rounded-full bg-gray-50 py-2 px-2 text-sm font-medium text-gray-500 hover:bg-gray-100 sm:px-3 border border-dashed">
<PlusIcon
className="h-5 w-5 flex-shrink-0 text-gray-300 sm:-ml-1"
aria-hidden="true"
/>
<span
className={classNames(
value ? "" : "text-gray-900",
"hidden truncate capitalize sm:ml-2 sm:block w-16"
)}
>
{value
? Array.isArray(value)
? value
.map(
(i: any) =>
item.options?.find((option) => option.value === i)
?.label
)
.join(", ") || `Select ${item.label}`
: item.options?.find((option) => option.value === value)?.label
: `Select ${item.label}`}
</span>
</Listbox.Button>
<Transition
show={open}
as={React.Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
<div className="relative">
<Listbox.Button className="relative flex justify-between items-center gap-1 hover:bg-gray-100 border rounded-md shadow-sm px-2 py-1 cursor-pointer focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 text-sm duration-300">
<span
className={classNames(
value ? "" : "text-gray-900",
"hidden truncate sm:block w-16 text-left",
item.label === "Priority" ? "capitalize" : ""
)}
>
<Listbox.Options className="absolute right-0 z-10 mt-1 max-h-56 w-52 overflow-auto rounded-lg bg-white py-3 text-base shadow ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{value
? Array.isArray(value)
? value
.map(
(i: any) =>
item.options?.find((option) => option.value === i)?.label
)
.join(", ") || item.label
: item.options?.find((option) => option.value === value)?.label
: "None"}
</span>
<ChevronDownIcon className="h-3 w-3" />
</Listbox.Button>
<Transition
show={open}
as={React.Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute z-10 right-0 mt-1 w-40 bg-white shadow-lg max-h-28 rounded-md py-1 text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<div className="p-1">
{item.options?.map((option) => (
<Listbox.Option
key={option.value}
className={({ active, selected }) =>
classNames(
active || selected ? "bg-indigo-50" : "bg-white",
"relative cursor-default select-none py-2 px-3"
)
`${
active || selected ? "text-white bg-theme" : "text-gray-900"
} ${
item.label === "Priority" && "capitalize"
} cursor-pointer select-none relative p-2 rounded-md truncate`
}
value={option.value}
>
<div className="flex items-center">
<span className="ml-3 block capitalize font-medium">
{option.label}
</span>
</div>
{option.label}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</>
</div>
</Listbox.Options>
</Transition>
</div>
)}
</Listbox>
)}
@ -230,11 +256,11 @@ const IssueDetailSidebar: React.FC<Props> = ({ control, submitChanges }) => {
</div>
))}
<div>
<form className="flex" onSubmit={handleSubmit(onSubmit)}>
<form className="flex items-center gap-x-2" onSubmit={handleSubmit(onSubmit)}>
<Input
id="name"
name="name"
placeholder="Add label"
placeholder="Add new label"
register={register}
validations={{
required: false,
@ -246,9 +272,9 @@ const IssueDetailSidebar: React.FC<Props> = ({ control, submitChanges }) => {
</Button>
</form>
</div>
<div className="flex items-center gap-x-2">
<div className="flex items-center gap-x-2">
<TagIcon className="w-5 h-5 text-gray-500" />
<div className="flex justify-between items-center gap-x-2">
<div className="flex items-center gap-x-2 text-sm">
<TagIcon className="w-4 h-4" />
<p>Label</p>
</div>
<div>
@ -267,15 +293,11 @@ const IssueDetailSidebar: React.FC<Props> = ({ control, submitChanges }) => {
<>
<Listbox.Label className="sr-only">Label</Listbox.Label>
<div className="relative">
<Listbox.Button className="relative inline-flex items-center whitespace-nowrap rounded-full bg-gray-50 py-2 px-2 text-sm font-medium text-gray-500 hover:bg-gray-100 sm:px-3 border border-dashed">
<PlusIcon
className="h-5 w-5 flex-shrink-0 text-gray-300 sm:-ml-1"
aria-hidden="true"
/>
<Listbox.Button className="relative flex justify-between items-center gap-1 hover:bg-gray-100 border rounded-md shadow-sm px-2 py-1 cursor-pointer focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 text-sm duration-300">
<span
className={classNames(
value ? "" : "text-gray-900",
"hidden truncate capitalize sm:ml-2 sm:block w-16"
"hidden truncate capitalize sm:block w-16 text-left"
)}
>
{value && value.length > 0
@ -285,8 +307,9 @@ const IssueDetailSidebar: React.FC<Props> = ({ control, submitChanges }) => {
issueLabels?.find((option) => option.id === i)?.name
)
.join(", ")
: `Select label`}
: "None"}
</span>
<ChevronDownIcon className="h-3 w-3" />
</Listbox.Button>
<Transition
@ -296,25 +319,22 @@ const IssueDetailSidebar: React.FC<Props> = ({ control, submitChanges }) => {
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute right-0 z-10 mt-1 max-h-56 w-52 overflow-auto rounded-lg bg-white py-3 text-base shadow ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{issueLabels?.map((label: any) => (
<Listbox.Option
key={label.id}
className={({ active, selected }) =>
classNames(
active || selected ? "bg-indigo-50" : "bg-white",
"relative cursor-default select-none py-2 px-3"
)
}
value={label.id}
>
<div className="flex items-center">
<span className="ml-3 block capitalize font-medium">
{label.name}
</span>
</div>
</Listbox.Option>
))}
<Listbox.Options className="absolute z-10 right-0 mt-1 w-40 bg-white shadow-lg max-h-28 rounded-md py-1 text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<div className="p-1">
{issueLabels?.map((label: any) => (
<Listbox.Option
key={label.id}
className={({ active, selected }) =>
`${
active || selected ? "text-white bg-theme" : "text-gray-900"
} cursor-pointer select-none relative p-2 rounded-md truncate`
}
value={label.id}
>
{label.name}
</Listbox.Option>
))}
</div>
</Listbox.Options>
</Transition>
</div>

View file

@ -0,0 +1,122 @@
// next
import Image from "next/image";
import {
ChartBarIcon,
ChatBubbleBottomCenterTextIcon,
Squares2X2Icon,
} from "@heroicons/react/24/outline";
import { addSpaceIfCamelCase, timeAgo } from "constants/common";
import { IState } from "types";
import { Spinner } from "ui";
type Props = {
issueActivities: any[] | undefined;
states: IState[] | undefined;
};
const activityIcons = {
state: <Squares2X2Icon className="h-4 w-4" />,
priority: <ChartBarIcon className="h-4 w-4" />,
name: <ChatBubbleBottomCenterTextIcon className="h-4 w-4" />,
description: <ChatBubbleBottomCenterTextIcon className="h-4 w-4" />,
};
const IssueActivitySection: React.FC<Props> = ({ issueActivities, states }) => {
return (
<>
{issueActivities ? (
<div className="space-y-3">
{issueActivities.map((activity) => {
if (activity.field !== "updated_by")
return (
<div key={activity.id} className="relative flex gap-x-2 w-full">
{issueActivities.length > 1 ? (
<span
className="absolute top-5 left-2.5 h-full w-0.5 bg-gray-200"
aria-hidden="true"
/>
) : null}
{activity.field ? (
<div className="relative z-10 flex-shrink-0 -ml-1">
<div
className={`h-7 w-7 bg-gray-700 text-white border-2 border-white grid place-items-center rounded-full`}
>
{activityIcons[activity.field as keyof typeof activityIcons]}
</div>
</div>
) : (
<div className="relative z-10 flex-shrink-0 border-2 border-white -ml-1.5">
{activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? (
<Image
src={activity.actor_detail.avatar}
alt={activity.actor_detail.name}
height={30}
width={30}
className="rounded-full"
/>
) : (
<div
className={`h-8 w-8 bg-gray-700 text-white border-2 border-white grid place-items-center rounded-full`}
>
{activity.actor_detail.first_name.charAt(0)}
</div>
)}
</div>
)}
<div className="w-full">
<p>
{activity.actor_detail.first_name} {activity.actor_detail.last_name}{" "}
<span>{activity.verb}</span>{" "}
{activity.verb !== "created" ? (
<span>{activity.field ?? "commented"}</span>
) : (
" this issue"
)}
</p>
<p className="text-xs text-gray-500">{timeAgo(activity.created_at)}</p>
<div className="w-full mt-2">
{activity.verb !== "created" && (
<div className="text-sm">
<div>
From:{" "}
<span className="text-gray-500">
{activity.field === "state"
? activity.old_value
? addSpaceIfCamelCase(
states?.find((s) => s.id === activity.old_value)?.name ?? ""
)
: "None"
: activity.old_value}
</span>
</div>
<div>
To:{" "}
<span className="text-gray-500">
{activity.field === "state"
? activity.new_value
? addSpaceIfCamelCase(
states?.find((s) => s.id === activity.new_value)?.name ?? ""
)
: "None"
: activity.new_value}
</span>
</div>
</div>
)}
</div>
</div>
</div>
);
})}
</div>
) : (
<div className="w-full h-full flex justify-center items-center">
<Spinner />
</div>
)}
</>
);
};
export default IssueActivitySection;

View file

@ -8,11 +8,14 @@ import issuesServices from "lib/services/issues.services";
// fetch keys
import { PROJECT_ISSUES_COMMENTS } from "constants/fetch-keys";
// components
import CommentCard from "components/project/issues/comment/IssueCommentCard";
import CommentCard from "components/project/issues/issue-detail/comment/IssueCommentCard";
// ui
import { TextArea, Button, Spinner } from "ui";
// types
import type { IIssueComment } from "types";
// icons
import UploadingIcon from "public/animated-icons/uploading.json";
type Props = {
comments?: IIssueComment[];
workspaceSlug: string;
@ -67,9 +70,9 @@ const IssueCommentSection: React.FC<Props> = ({ comments, issueId, projectId, wo
};
return (
<div className="space-y-3 px-2">
<div className="space-y-5">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="p-2 bg-indigo-50 rounded-md mb-6">
<div className="p-2 bg-indigo-50 rounded-md">
<div className="w-full">
<TextArea
id="comment"
@ -99,6 +102,7 @@ const IssueCommentSection: React.FC<Props> = ({ comments, issueId, projectId, wo
<div className="w-full flex justify-end">
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Adding comment..." : "Add comment"}
{/* <UploadingIcon /> */}
</Button>
</div>
</div>