chore: refactored and resolved build issues on the issues and issue detail page (#3340)

* fix: handled undefined issue_id in list layout

* dev: issue detail store and optimization

* dev: issue filter and list operations

* fix: typo on labels update

* dev: Handled all issues in the list layout in project issues

* dev: handled kanban and auick add issue in swimlanes

* chore: fixed peekoverview in kanban

* chore: fixed peekoverview in calendar

* chore: fixed peekoverview in gantt

* chore: updated quick add in the gantt chart

* chore: handled issue detail properties and resolved build issues

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
This commit is contained in:
guru_sainath 2024-01-10 20:09:45 +05:30 committed by sriram veeraghanta
parent e6b31e2550
commit 4611ec0b83
112 changed files with 3303 additions and 2560 deletions

View file

@ -0,0 +1,4 @@
export * from "./reaction-selector";
export * from "./issue";
// export * from "./issue-comment";

View file

@ -0,0 +1,103 @@
import { FC, useMemo } from "react";
import { observer } from "mobx-react-lite";
// components
import { ReactionSelector } from "./reaction-selector";
// hooks
import { useIssueDetail } from "hooks/store";
import useToast from "hooks/use-toast";
// types
import { IUser } from "@plane/types";
import { renderEmoji } from "helpers/emoji.helper";
export type TIssueReaction = {
workspaceSlug: string;
projectId: string;
issueId: string;
currentUser: IUser;
};
export const IssueReaction: FC<TIssueReaction> = observer((props) => {
const { workspaceSlug, projectId, issueId, currentUser } = props;
// hooks
const {
reaction: { getReactionsByIssueId, reactionsByUser },
createReaction,
removeReaction,
} = useIssueDetail();
const { setToastAlert } = useToast();
const reactionIds = getReactionsByIssueId(issueId);
const userReactions = reactionsByUser(issueId, currentUser.id).map((r) => r.reaction);
const issueReactionOperations = useMemo(
() => ({
create: async (reaction: string) => {
try {
if (!workspaceSlug || !projectId || !issueId) throw new Error("Missing fields");
await createReaction(workspaceSlug, projectId, issueId, reaction);
setToastAlert({
title: "Reaction created successfully",
type: "success",
message: "Reaction created successfully",
});
} catch (error) {
setToastAlert({
title: "Reaction creation failed",
type: "error",
message: "Reaction creation failed",
});
}
},
remove: async (reaction: string) => {
try {
if (!workspaceSlug || !projectId || !issueId) throw new Error("Missing fields");
await removeReaction(workspaceSlug, projectId, issueId, reaction, currentUser.id);
setToastAlert({
title: "Reaction removed successfully",
type: "success",
message: "Reaction removed successfully",
});
} catch (error) {
setToastAlert({
title: "Reaction remove failed",
type: "error",
message: "Reaction remove failed",
});
}
},
react: async (reaction: string) => {
if (userReactions.includes(reaction)) await issueReactionOperations.remove(reaction);
else await issueReactionOperations.create(reaction);
},
}),
[workspaceSlug, projectId, issueId, currentUser, createReaction, removeReaction, setToastAlert, userReactions]
);
return (
<div className="mt-4 relative flex items-center gap-1.5">
<ReactionSelector size="md" position="top" value={userReactions} onSelect={issueReactionOperations.react} />
{reactionIds &&
Object.keys(reactionIds || {}).map(
(reaction) =>
reactionIds[reaction]?.length > 0 && (
<>
<button
type="button"
onClick={() => issueReactionOperations.react(reaction)}
key={reaction}
className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${
userReactions.includes(reaction) ? "bg-custom-primary-100/10" : "bg-custom-background-80"
}`}
>
<span>{renderEmoji(reaction)}</span>
<span className={userReactions.includes(reaction) ? "text-custom-primary-100" : ""}>
{(reactionIds || {})[reaction].length}{" "}
</span>
</button>
</>
)
)}
</div>
);
});

View file

@ -0,0 +1,74 @@
import { Fragment } from "react";
import { Popover, Transition } from "@headlessui/react";
// helper
import { renderEmoji } from "helpers/emoji.helper";
// icons
import { SmilePlus } from "lucide-react";
const reactionEmojis = ["128077", "128078", "128516", "128165", "128533", "129505", "9992", "128064"];
interface Props {
size?: "sm" | "md" | "lg";
position?: "top" | "bottom";
value?: string | string[] | null;
onSelect: (emoji: string) => void;
}
export const ReactionSelector: React.FC<Props> = (props) => {
const { onSelect, position, size } = props;
return (
<Popover className="relative">
{({ open, close: closePopover }) => (
<>
<Popover.Button
className={`${
open ? "" : "text-opacity-90"
} group inline-flex items-center rounded-md bg-custom-background-80 focus:outline-none`}
>
<span
className={`flex items-center justify-center rounded-md px-2 ${
size === "sm" ? "h-6 w-6" : size === "md" ? "h-7 w-7" : "h-8 w-8"
}`}
>
<SmilePlus className="h-3.5 w-3.5 text-custom-text-100" />
</span>
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel
className={`absolute -left-2 z-10 bg-custom-sidebar-background-100 ${
position === "top" ? "-top-12" : "-bottom-12"
}`}
>
<div className="rounded-md border border-custom-border-200 bg-custom-sidebar-background-100 p-1 shadow-custom-shadow-sm">
<div className="flex gap-x-1">
{reactionEmojis.map((emoji) => (
<button
key={emoji}
type="button"
onClick={() => {
onSelect(emoji);
closePopover();
}}
className="flex select-none items-center justify-between rounded-md p-1 text-sm hover:bg-custom-sidebar-background-90"
>
{renderEmoji(emoji)}
</button>
))}
</div>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
);
};