From 2099a985e9c6f9539ffd464f1fd571caf36d8f7b Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Fri, 6 Jan 2023 23:38:44 +0530 Subject: [PATCH] fix: mutation bugs, remirror bugs, style: consistent droopdowns and buttons --- .../common/list-view/single-issue.tsx | 230 ++++++++------- .../components/onboarding/invite-members.tsx | 27 ++ .../components/onboarding/user-details.tsx | 106 +++++++ apps/app/components/onboarding/workspace.tsx | 258 +++++++++++++++++ .../project/cycles/board-view/index.tsx | 2 +- .../cycles/board-view/single-board.tsx | 2 +- .../project/cycles/list-view/index.tsx | 2 +- .../project/issues/BoardView/index.tsx | 4 +- .../create-update-issue-modal/index.tsx | 22 ++ .../select-priority.tsx | 3 +- .../issues/issue-detail/activity/index.tsx | 190 ++++++------ .../comment/issue-comment-card.tsx | 75 ++--- .../issue-detail-sidebar/index.tsx | 14 +- .../issue-detail-sidebar/select-blocked.tsx | 14 +- .../issue-detail-sidebar/select-blocker.tsx | 28 +- .../issue-detail-sidebar/select-cycle.tsx | 19 +- .../project/issues/list-view/index.tsx | 85 ++---- .../project/modules/board-view/index.tsx | 2 +- .../modules/board-view/single-board.tsx | 2 +- .../project/modules/list-view/index.tsx | 2 +- .../modules/module-detail-sidebar/index.tsx | 15 +- .../project/modules/module-link-modal.tsx | 11 +- .../app/components/rich-text-editor/index.tsx | 16 +- .../rich-text-editor/toolbar/index.tsx | 4 +- .../components/workspace/SingleInvitation.tsx | 8 +- apps/app/pages/[workspaceSlug]/index.tsx | 228 ++++++++------- .../pages/[workspaceSlug]/me/my-issues.tsx | 6 +- .../projects/[projectId]/cycles/[cycleId].tsx | 119 +++++--- .../projects/[projectId]/cycles/index.tsx | 4 +- .../projects/[projectId]/issues/[issueId].tsx | 273 ++++++------------ .../[projectId]/modules/[moduleId].tsx | 6 +- .../projects/[projectId]/settings/control.tsx | 170 +++-------- .../projects/[projectId]/settings/index.tsx | 34 ++- .../pages/[workspaceSlug]/settings/index.tsx | 89 +++--- apps/app/pages/invitations.tsx | 7 +- apps/app/pages/onboarding.tsx | 65 +++-- apps/app/styles/editor.css | 2 +- apps/app/styles/globals.css | 5 +- apps/app/types/issues.d.ts | 2 + apps/app/ui/button/index.tsx | 6 +- apps/app/ui/custom-listbox/index.tsx | 60 ++-- apps/app/ui/custom-menu/index.tsx | 6 +- apps/app/ui/custom-menu/types.d.ts | 2 +- apps/app/ui/custom-select/index.tsx | 28 +- apps/app/ui/icons/tag-icon.tsx | 9 +- apps/app/ui/input/index.tsx | 10 +- apps/app/ui/outline-button/index.tsx | 67 +++++ apps/app/ui/spinner/index.tsx | 2 +- 48 files changed, 1342 insertions(+), 999 deletions(-) create mode 100644 apps/app/components/onboarding/invite-members.tsx create mode 100644 apps/app/components/onboarding/user-details.tsx create mode 100644 apps/app/components/onboarding/workspace.tsx create mode 100644 apps/app/ui/outline-button/index.tsx diff --git a/apps/app/components/common/list-view/single-issue.tsx b/apps/app/components/common/list-view/single-issue.tsx index dc76573936a..67abb234579 100644 --- a/apps/app/components/common/list-view/single-issue.tsx +++ b/apps/app/components/common/list-view/single-issue.tsx @@ -1,3 +1,6 @@ +// react +import { useState } from "react"; +// next import Link from "next/link"; import { useRouter } from "next/router"; @@ -20,7 +23,7 @@ import { } from "constants/common"; type Props = { - type: string; + type?: string; issue: IIssue; properties: Properties; editIssue: () => void; @@ -37,7 +40,7 @@ const SingleListIssue: React.FC = ({ removeIssue, }) => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + let { workspaceSlug, projectId } = router.query; const { data: issues } = useSWR( workspaceSlug && projectId @@ -51,121 +54,126 @@ const SingleListIssue: React.FC = ({ const totalChildren = issues?.results.filter((i) => i.parent === issue.id).length; return ( -
- -
- {properties.priority && ( -
- {issue.priority ?? "None"} -
-
Priority
-
- {issue.priority ?? "None"} + <> +
+ +
+ {properties.priority && ( +
+ {/* {getPriorityIcon(issue.priority ?? "")} */} + {issue.priority ?? "None"} +
+
Priority
+
+ {issue.priority ?? "None"} +
-
- )} - {properties.state && ( -
- - {addSpaceIfCamelCase(issue?.state_detail.name)} -
-
State
-
{issue?.state_detail.name}
+ )} + {properties.state && ( +
+ + {addSpaceIfCamelCase(issue?.state_detail.name)} +
+
State
+
{issue?.state_detail.name}
+
-
- )} - {properties.due_date && ( -
- - {issue.target_date ? renderShortNumericDateFormat(issue.target_date) : "N/A"} -
-
Due date
-
{renderShortNumericDateFormat(issue.target_date ?? "")}
-
- {issue.target_date && - (issue.target_date < new Date().toISOString() - ? `Due date has passed by ${findHowManyDaysLeft(issue.target_date)} days` - : findHowManyDaysLeft(issue.target_date) <= 3 - ? `Due date is in ${findHowManyDaysLeft(issue.target_date)} days` - : "Due date")} + )} + {properties.due_date && ( +
+ + {issue.target_date ? renderShortNumericDateFormat(issue.target_date) : "N/A"} +
+
Due date
+
{renderShortNumericDateFormat(issue.target_date ?? "")}
+
+ {issue.target_date && + (issue.target_date < new Date().toISOString() + ? `Due date has passed by ${findHowManyDaysLeft(issue.target_date)} days` + : findHowManyDaysLeft(issue.target_date) <= 3 + ? `Due date is in ${findHowManyDaysLeft(issue.target_date)} days` + : "Due date")} +
-
- )} - {properties.sub_issue_count && projectId && ( -
- {totalChildren} {totalChildren === 1 ? "sub-issue" : "sub-issues"} -
- )} - - editIssue()}>Edit - removeIssue()}> - <>Remove from {type} - - handleDeleteIssue()}> - Delete permanently - - + )} + {properties.sub_issue_count && projectId && ( +
+ {totalChildren} {totalChildren === 1 ? "sub-issue" : "sub-issues"} +
+ )} + {type && ( + + editIssue()}>Edit + removeIssue()}> + <>Remove from {type} + + handleDeleteIssue()}> + Delete permanently + + + )} +
-
+ ); }; diff --git a/apps/app/components/onboarding/invite-members.tsx b/apps/app/components/onboarding/invite-members.tsx new file mode 100644 index 00000000000..f5fc794cab9 --- /dev/null +++ b/apps/app/components/onboarding/invite-members.tsx @@ -0,0 +1,27 @@ +type Props = { + setStep: React.Dispatch>; +}; + +const InviteMembers: React.FC = ({ setStep }) => { + return ( +
+
+

User Details

+
+
+ + +
+
+ ); +}; + +export default InviteMembers; diff --git a/apps/app/components/onboarding/user-details.tsx b/apps/app/components/onboarding/user-details.tsx new file mode 100644 index 00000000000..15a6b13a4dd --- /dev/null +++ b/apps/app/components/onboarding/user-details.tsx @@ -0,0 +1,106 @@ +// types +import useToast from "lib/hooks/useToast"; +import userService from "lib/services/user.service"; +import { useEffect } from "react"; +import { Controller, useForm } from "react-hook-form"; +import { IUser } from "types"; +import { CustomSelect, Input } from "ui"; + +type Props = { + user: IUser | undefined; + setStep: React.Dispatch>; +}; + +const UserDetails: React.FC = ({ user, setStep }) => { + const { setToastAlert } = useToast(); + + const { + register, + handleSubmit, + control, + reset, + formState: { errors, isSubmitting }, + } = useForm(); + + const onSubmit = (formData: IUser) => { + console.log(formData); + + userService + .updateUser(formData) + .then((res) => { + setToastAlert({ + title: "User details updated successfully!", + type: "success", + }); + setStep(2); + }) + .catch((err) => { + console.log(err); + }); + }; + + useEffect(() => { + reset({ + first_name: user && user.first_name, + last_name: user && user.last_name, + }); + }, [user, reset]); + + return ( +
+
+

User Details

+
+
+ +
+
+ +
+
+ ( + + SDE + + )} + /> +
+
+
+
+ +
+
+ ); +}; + +export default UserDetails; diff --git a/apps/app/components/onboarding/workspace.tsx b/apps/app/components/onboarding/workspace.tsx new file mode 100644 index 00000000000..eb1e8fcb1c1 --- /dev/null +++ b/apps/app/components/onboarding/workspace.tsx @@ -0,0 +1,258 @@ +// react +import { useEffect, useState } from "react"; +// next +import Image from "next/image"; +// swr +import useSWR from "swr"; +// react-hook-form +import { Controller, useForm } from "react-hook-form"; +// hooks +import useToast from "lib/hooks/useToast"; +// services +import workspaceService from "lib/services/workspace.service"; +// headless ui +import { Tab } from "@headlessui/react"; +// ui +import { CustomSelect, Input } from "ui"; +// fetch-keys +import { USER_WORKSPACE_INVITATIONS } from "constants/fetch-keys"; +// types +import { IWorkspace, IWorkspaceMemberInvitation } from "types"; + +type Props = { + setStep: React.Dispatch>; +}; + +const Workspace: React.FC = ({ setStep }) => { + const [invitationsRespond, setInvitationsRespond] = useState([]); + + const { setToastAlert } = useToast(); + + const { data: invitations, mutate } = useSWR(USER_WORKSPACE_INVITATIONS, () => + workspaceService.userWorkspaceInvitations() + ); + + const { + register, + handleSubmit, + control, + reset, + formState: { errors, isSubmitting }, + } = useForm(); + + const handleCreateWorkspace = (formData: IWorkspace) => { + console.log(formData); + + workspaceService + .createWorkspace(formData) + .then((res) => { + console.log(res); + setToastAlert({ + type: "success", + title: "Workspace created successfully!", + }); + }) + .catch((err) => { + console.log(err); + }); + }; + + const handleInvitation = ( + workspace_invitation: IWorkspaceMemberInvitation, + action: "accepted" | "withdraw" + ) => { + if (action === "accepted") { + setInvitationsRespond((prevData) => { + return [...prevData, workspace_invitation.id]; + }); + } else if (action === "withdraw") { + setInvitationsRespond((prevData) => { + return prevData.filter((item: string) => item !== workspace_invitation.id); + }); + } + }; + + const submitInvitations = () => { + workspaceService + .joinWorkspaces({ invitations: invitationsRespond }) + .then(async (res: any) => { + console.log(res); + await mutate(); + setStep(3); + }) + .catch((err) => { + console.log(err); + }); + }; + + useEffect(() => { + reset({ + name: "", + }); + }, [reset]); + + return ( + + + + `${selected ? "border-b-2 border-gray-900 text-gray-900" : "text-gray-500"} py-4` + } + > + New workspace + + + `${selected ? "border-b-2 border-gray-900 text-gray-900" : "text-gray-400"} py-4` + } + > + Invited workspaces + + + + +
+
+
+
+ +
+
+ +
+
+ ( + + {[ + { value: 5, label: "5" }, + { value: 10, label: "10" }, + { value: 25, label: "25" }, + { value: 50, label: "50" }, + ]?.map((item) => ( + + {item.label} + + ))} + + )} + /> +
+
+
+
+ +
+
+
+ +
+
+ {invitations && invitations.length > 0 + ? invitations.map((invitation) => ( +
+ +
+ )) + : "No invitations"} +
+ +
+ +
+
+
+
+
+ ); +}; + +export default Workspace; diff --git a/apps/app/components/project/cycles/board-view/index.tsx b/apps/app/components/project/cycles/board-view/index.tsx index 00e6118268c..86be2231501 100644 --- a/apps/app/components/project/cycles/board-view/index.tsx +++ b/apps/app/components/project/cycles/board-view/index.tsx @@ -129,7 +129,7 @@ const CyclesBoardView: React.FC = ({ bgColor={ selectedGroup === "state_detail.name" ? states?.find((s) => s.name === singleGroup)?.color - : undefined + : "#000000" } properties={properties} removeIssueFromCycle={removeIssueFromCycle} diff --git a/apps/app/components/project/cycles/board-view/single-board.tsx b/apps/app/components/project/cycles/board-view/single-board.tsx index c7663b03e9a..73a1281badc 100644 --- a/apps/app/components/project/cycles/board-view/single-board.tsx +++ b/apps/app/components/project/cycles/board-view/single-board.tsx @@ -197,7 +197,7 @@ const SingleModuleBoard: React.FC = ({ } className="mt-1" optionsPosition="left" - withoutBorder + noBorder > { diff --git a/apps/app/components/project/cycles/list-view/index.tsx b/apps/app/components/project/cycles/list-view/index.tsx index 5c6552c5436..08905f92fd4 100644 --- a/apps/app/components/project/cycles/list-view/index.tsx +++ b/apps/app/components/project/cycles/list-view/index.tsx @@ -164,7 +164,7 @@ const CyclesListView: React.FC = ({ } optionsPosition="left" - withoutBorder + noBorder > { diff --git a/apps/app/components/project/issues/BoardView/index.tsx b/apps/app/components/project/issues/BoardView/index.tsx index f10138fd2e0..31e40a65a1d 100644 --- a/apps/app/components/project/issues/BoardView/index.tsx +++ b/apps/app/components/project/issues/BoardView/index.tsx @@ -9,8 +9,6 @@ import { DragDropContext } from "react-beautiful-dnd"; // services import stateServices from "lib/services/state.service"; import issuesServices from "lib/services/issues.service"; -// hooks -import useUser from "lib/hooks/useUser"; // fetching keys import { STATE_LIST } from "constants/fetch-keys"; // components @@ -221,7 +219,7 @@ const BoardView: React.FC = ({ bgColor={ selectedGroup === "state_detail.name" ? states?.find((s) => s.name === singleGroup)?.color - : undefined + : "#000000" } handleDeleteIssue={handleDeleteIssue} partialUpdateIssue={partialUpdateIssue} diff --git a/apps/app/components/project/issues/create-update-issue-modal/index.tsx b/apps/app/components/project/issues/create-update-issue-modal/index.tsx index 46d8a82c687..88afc2b4334 100644 --- a/apps/app/components/project/issues/create-update-issue-modal/index.tsx +++ b/apps/app/components/project/issues/create-update-issue-modal/index.tsx @@ -37,10 +37,12 @@ import { CYCLE_ISSUES, USER_ISSUE, PROJECTS_LIST, + MODULE_ISSUES, } from "constants/fetch-keys"; // common import { renderDateFormat, cosineSimilarity } from "constants/common"; import projectService from "lib/services/project.service"; +import modulesService from "lib/services/modules.service"; const RemirrorRichTextEditor = dynamic(() => import("components/rich-text-editor"), { ssr: false, @@ -156,6 +158,7 @@ const CreateUpdateIssuesModal: React.FC = (props) => { const addIssueToCycle = async (issueId: string, cycleId: string) => { if (!workspaceSlug || !projectId) return; + await issuesServices .addIssueToCycle(workspaceSlug as string, projectId, cycleId, { issues: [issueId], @@ -193,6 +196,20 @@ const CreateUpdateIssuesModal: React.FC = (props) => { }); }; + const addIssueToModule = async (issueId: string, moduleId: string) => { + if (!workspaceSlug || !projectId) return; + + modulesService + .addIssuesToModule(workspaceSlug as string, projectId, moduleId as string, { + issues: [issueId], + }) + .then((res) => { + console.log(res); + mutate(MODULE_ISSUES(moduleId as string)); + }) + .catch((e) => console.log(e)); + }; + const onSubmit = async (formData: IIssue) => { if (!workspaceSlug || !projectId) return; @@ -208,6 +225,11 @@ const CreateUpdateIssuesModal: React.FC = (props) => { if (formData.cycle && formData.cycle !== null) { addIssueToCycle(res.id, formData.cycle); } + + if (formData.module && formData.module !== null) { + addIssueToModule(res.id, formData.module); + } + resetForm(); if (!createMore) handleClose(); setToastAlert({ diff --git a/apps/app/components/project/issues/create-update-issue-modal/select-priority.tsx b/apps/app/components/project/issues/create-update-issue-modal/select-priority.tsx index 0d4f5869b2d..4d45fb8010e 100644 --- a/apps/app/components/project/issues/create-update-issue-modal/select-priority.tsx +++ b/apps/app/components/project/issues/create-update-issue-modal/select-priority.tsx @@ -12,6 +12,7 @@ import { CustomListbox } from "ui"; // types import type { IIssue } from "types"; import type { Control } from "react-hook-form"; +import { getPriorityIcon } from "constants/global"; type Props = { control: Control; @@ -31,7 +32,7 @@ const SelectPriority: React.FC = ({ control }) => { value={value} optionsFontsize="sm" onChange={onChange} - icon={} + icon={getPriorityIcon(value)} /> )} /> diff --git a/apps/app/components/project/issues/issue-detail/activity/index.tsx b/apps/app/components/project/issues/issue-detail/activity/index.tsx index cd1bdc1c77f..933ce1c91a2 100644 --- a/apps/app/components/project/issues/issue-detail/activity/index.tsx +++ b/apps/app/components/project/issues/issue-detail/activity/index.tsx @@ -6,14 +6,11 @@ import { useRouter } from "next/router"; import useSWR from "swr"; // fetch-keys -import { PROJECT_ISSUES_ACTIVITY, STATE_LIST, PROJECT_ISSUES_LIST } from "constants/fetch-keys"; +import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys"; // common -import { addSpaceIfCamelCase, timeAgo } from "constants/common"; +import { addSpaceIfCamelCase, renderShortNumericDateFormat, timeAgo } from "constants/common"; // services -import stateService from "lib/services/state.service"; import issuesServices from "lib/services/issues.service"; -// hooks -import useUser from "lib/hooks/useUser"; // ui import { Loader } from "ui"; // icons @@ -24,16 +21,57 @@ import { Squares2X2Icon, UserIcon, } from "@heroicons/react/24/outline"; +import { BlockedIcon, BlockerIcon, TagIcon, UserGroupIcon } from "ui/icons"; -const activityIcons: { - [key: string]: JSX.Element; +const activityDetails: { + [key: string]: { + message?: string; + icon: JSX.Element; + }; } = { - state: , - priority: , - name: , - description: , - target_date: , - parent: , + assignee: { + message: "removed the assignee", + icon: , + }, + assignees: { + message: "added a new assignee", + icon: , + }, + blocks: { + message: "marked this issue being blocked by", + icon: , + }, + blocking: { + message: "marked this issue is blocking", + icon: , + }, + labels: { + icon: , + }, + state: { + message: "set the state to", + icon: , + }, + priority: { + message: "set the priority to", + icon: , + }, + name: { + message: "set the name to", + icon: , + }, + description: { + message: "set the description to", + icon: , + }, + target_date: { + message: "set the due date to", + icon: , + }, + parent: { + message: "set the parent to", + icon: , + }, }; const IssueActivitySection: React.FC = () => { @@ -41,15 +79,6 @@ const IssueActivitySection: React.FC = () => { const { workspaceSlug, projectId, issueId } = router.query; - const { data: issues } = useSWR( - workspaceSlug && projectId - ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) - : null, - workspaceSlug && projectId - ? () => issuesServices.getIssues(workspaceSlug as string, projectId as string) - : null - ); - const { data: issueActivities } = useSWR( workspaceSlug && projectId && issueId ? PROJECT_ISSUES_ACTIVITY : null, workspaceSlug && projectId && issueId @@ -62,21 +91,14 @@ const IssueActivitySection: React.FC = () => { : null ); - const { data: states } = useSWR( - workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => stateService.getStates(workspaceSlug as string, projectId as string) - : null - ); - return ( <> {issueActivities ? ( -
+
{issueActivities.map((activity, index) => { if (activity.field !== "updated_by") return ( -
+
{issueActivities.length > 1 && index !== issueActivities.length - 1 ? ( { /> ) : null} {activity.field ? ( -
-
- {activityIcons[activity.field as keyof typeof activityIcons]} +
+
+ {activityDetails[activity.field as keyof typeof activityDetails]?.icon}
) : ( -
- {activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? ( - {activity.actor_detail.name} - ) : ( -
- {activity.actor_detail.first_name.charAt(0)} -
- )} +
+
+ {activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? ( + {activity.actor_detail.name} + ) : ( +
+ {activity.actor_detail.first_name.charAt(0)} +
+ )} +
)} @@ -116,48 +138,32 @@ const IssueActivitySection: React.FC = () => { {activity.actor_detail.first_name} {activity.actor_detail.last_name} - {activity.verb} - {activity.verb !== "created" ? ( - {activity.field ?? "commented"} - ) : ( - " this issue" - )} + + {" "} + {activity.field === "labels" + ? activity.new_value !== "" + ? "add a new label" + : "removed the label" + : activityDetails[activity.field as keyof typeof activityDetails] + ?.message}{" "} + + + {activity.field === "state" + ? activity.new_value + ? addSpaceIfCamelCase(activity.new_value) + : "None" + : activity.field === "labels" + ? activity.new_value !== "" + ? activity.new_value + : activity.old_value + : activity.field === "assignee" + ? activity.old_value + : activity.field === "target_date" + ? renderShortNumericDateFormat(activity.new_value) + : activity.new_value ?? "None"} + {timeAgo(activity.created_at)}

-
- {activity.verb !== "created" && ( -
-
- From: - {activity.field === "state" - ? activity.old_value - ? addSpaceIfCamelCase( - states?.find((s) => s.id === activity.old_value)?.name ?? "" - ) - : "None" - : activity.field === "parent" - ? activity.old_value - ? issues?.results.find((i) => i.id === activity.old_value)?.name - : "None" - : activity.old_value ?? "None"} -
-
- To: - {activity.field === "state" - ? activity.new_value - ? addSpaceIfCamelCase( - states?.find((s) => s.id === activity.new_value)?.name ?? "" - ) - : "None" - : activity.field === "parent" - ? activity.new_value - ? issues?.results.find((i) => i.id === activity.new_value)?.name - : "None" - : activity.new_value ?? "None"} -
-
- )} -
); diff --git a/apps/app/components/project/issues/issue-detail/comment/issue-comment-card.tsx b/apps/app/components/project/issues/issue-detail/comment/issue-comment-card.tsx index fa2bec7850b..bb106085a37 100644 --- a/apps/app/components/project/issues/issue-detail/comment/issue-comment-card.tsx +++ b/apps/app/components/project/issues/issue-detail/comment/issue-comment-card.tsx @@ -9,7 +9,7 @@ import useUser from "lib/hooks/useUser"; // headless ui import { Menu } from "@headlessui/react"; // ui -import { TextArea } from "ui"; +import { CustomMenu, TextArea } from "ui"; // icons import { CheckIcon, EllipsisHorizontalIcon, XMarkIcon } from "@heroicons/react/24/outline"; // types @@ -49,8 +49,8 @@ const CommentCard: React.FC = ({ comment, onSubmit, handleCommentDeletion return (
-
-
+
+
{comment.actor_detail.avatar && comment.actor_detail.avatar !== "" ? ( = ({ comment, onSubmit, handleCommentDeletion /> ) : (
{comment.actor_detail.first_name.charAt(0)}
)}
-
+
+

+ + {comment.actor_detail.first_name} {comment.actor_detail.last_name} + + {timeAgo(comment.created_at)} +

{isEditing ? (
@@ -83,20 +89,20 @@ const CommentCard: React.FC = ({ comment, onSubmit, handleCommentDeletion mode="transparent" className="w-full" /> -
+
@@ -110,48 +116,19 @@ const CommentCard: React.FC = ({ comment, onSubmit, handleCommentDeletion )}
-

- - {comment.actor_detail.first_name} {comment.actor_detail.last_name} - - {timeAgo(comment.created_at)} -

{user?.id === comment.actor && ( -
- - - - - - -
- -
-
- -
- -
-
-
-
-
+ + setIsEditing(true)}>Edit + { + handleCommentDeletion(comment.id); + }} + > + Delete + + )}
diff --git a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/index.tsx b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/index.tsx index fd0b654a122..9320b2361a7 100644 --- a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/index.tsx +++ b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/index.tsx @@ -2,7 +2,7 @@ import React, { useState } from "react"; // next import { useRouter } from "next/router"; // swr -import useSWR from "swr"; +import useSWR, { mutate } from "swr"; // react hook form import { useForm, Controller, UseFormWatch } from "react-hook-form"; // react-color @@ -37,7 +37,7 @@ import { } from "@heroicons/react/24/outline"; // types import type { Control } from "react-hook-form"; -import type { ICycle, IIssue, IIssueLabels } from "types"; +import type { ICycle, IIssue, IIssueLabels, IssueResponse } from "types"; // fetch-keys import { PROJECT_ISSUE_LABELS, PROJECT_ISSUES_LIST, PROJECT_DETAILS } from "constants/fetch-keys"; // common @@ -65,7 +65,7 @@ const IssueDetailSidebar: React.FC = ({ const [deleteIssueModal, setDeleteIssueModal] = useState(false); const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug, projectId, issueId } = router.query; const { setToastAlert } = useToast(); @@ -112,13 +112,14 @@ const IssueDetailSidebar: React.FC = ({ const handleCycleChange = (cycleDetail: ICycle) => { if (!workspaceSlug || !projectId || !issueDetail) return; - submitChanges({ cycle: cycleDetail.id, cycle_detail: cycleDetail }); + mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)); + issuesServices .addIssueToCycle(workspaceSlug as string, projectId as string, cycleDetail.id, { issues: [issueDetail.id], }) - .then(() => { - submitChanges({}); + .then((res) => { + console.log(res); }); }; @@ -210,7 +211,6 @@ const IssueDetailSidebar: React.FC = ({ /> i.id !== issueDetail?.id) ?? []} watch={watchIssue} /> diff --git a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-blocked.tsx b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-blocked.tsx index ae2665709ca..f46d0982a03 100644 --- a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-blocked.tsx +++ b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-blocked.tsx @@ -8,9 +8,8 @@ import useSWR from "swr"; import { SubmitHandler, useForm, UseFormWatch } from "react-hook-form"; // services import issuesService from "lib/services/issues.service"; -import projectService from "lib/services/project.service"; // constants -import { PROJECT_DETAILS, PROJECT_ISSUES_LIST } from "constants/fetch-keys"; +import { PROJECT_ISSUES_LIST } from "constants/fetch-keys"; // hooks import useToast from "lib/hooks/useToast"; // headless ui @@ -31,7 +30,6 @@ type FormInput = { type Props = { submitChanges: (formData: Partial) => void; - issueDetail: IIssue | undefined; issuesList: IIssue[]; watch: UseFormWatch; }; @@ -45,7 +43,7 @@ const SelectBlocked: React.FC = ({ submitChanges, issuesList, watch }) => const { setToastAlert } = useToast(); - const { data: issues, mutate: mutateIssues } = useSWR( + const { data: issues } = useSWR( workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null, @@ -54,7 +52,7 @@ const SelectBlocked: React.FC = ({ submitChanges, issuesList, watch }) => : null ); - const { register, handleSubmit, reset } = useForm(); + const { register, handleSubmit, reset, watch: watchBlocked } = useForm(); const handleClose = () => { setIsBlockedModalOpen(false); @@ -78,6 +76,8 @@ const SelectBlocked: React.FC = ({ submitChanges, issuesList, watch }) => handleClose(); }; + console.log(watchBlocked("issue_ids")); + return (
@@ -185,7 +185,7 @@ const SelectBlocked: React.FC = ({ submitChanges, issuesList, watch }) => = ({ submitChanges, issuesList, watch }) => = ({ submitChanges, issuesList, watch }) => : null ); - const { data: projectDetails } = useSWR( - workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null, - workspaceSlug && projectId - ? () => projectService.getProject(workspaceSlug as string, projectId as string) - : null - ); - const { register, handleSubmit, reset } = useForm(); const handleClose = () => { @@ -99,15 +91,15 @@ const SelectBlocker: React.FC = ({ submitChanges, issuesList, watch }) => className="group flex cursor-pointer items-center gap-1 rounded-2xl border border-white px-1.5 py-0.5 text-xs text-yellow-500 duration-300 hover:border-yellow-500 hover:bg-yellow-50" > i.id === issue)?.id }`} > - {`${projectDetails?.identifier}-${ - issues?.results.find((i) => i.id === issue)?.sequence_id - }`} + {`${ + issues?.results.find((i) => i.id === issue)?.project_detail?.identifier + }-${issues?.results.find((i) => i.id === issue)?.sequence_id}`} = ({ submitChanges, issuesList, watch }) => = ({ submitChanges, issuesList, watch }) => = ({ submitChanges, issuesList, watch }) => }} /> - {projectDetails?.identifier}-{issue.sequence_id} + { + issues?.results.find((i) => i.id === issue.id) + ?.project_detail?.identifier + } + -{issue.sequence_id} {issue.name}
diff --git a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-cycle.tsx b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-cycle.tsx index 8d8b2d95c05..6f487b945ff 100644 --- a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-cycle.tsx +++ b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-cycle.tsx @@ -16,7 +16,7 @@ import { Spinner, CustomSelect } from "ui"; // icons import { ArrowPathIcon } from "@heroicons/react/24/outline"; // types -import { CycleIssueResponse, ICycle, IIssue } from "types"; +import { CycleIssueResponse, ICycle, IIssue, IssueResponse } from "types"; // common import { classNames } from "constants/common"; @@ -29,7 +29,7 @@ type Props = { const SelectCycle: React.FC = ({ issueDetail, control, handleCycleChange }) => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug, projectId, issueId } = router.query; const { data: cycles } = useSWR( workspaceSlug && projectId ? CYCLE_LIST(projectId as string) : null, @@ -41,21 +41,14 @@ const SelectCycle: React.FC = ({ issueDetail, control, handleCycleChange const removeIssueFromCycle = (bridgeId: string, cycleId: string) => { if (!workspaceSlug || !projectId) return; + mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)); + issuesService .removeIssueFromCycle(workspaceSlug as string, projectId as string, cycleId, bridgeId) .then((res) => { console.log(res); - mutate( - CYCLE_ISSUES(cycleId), - (prevData) => prevData?.filter((p) => p.id !== bridgeId), - false - ); - mutate( - workspaceSlug && projectId - ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) - : null - ); + mutate(CYCLE_ISSUES(cycleId)); }) .catch((e) => { console.log(e); @@ -82,7 +75,7 @@ const SelectCycle: React.FC = ({ issueDetail, control, handleCycleChange "hidden truncate text-left sm:block" )} > - {value ? cycles?.find((c) => c.id === value.cycle_detail.id)?.name : "None"} + {value ? value?.cycle_detail?.name : "None"} } value={value} diff --git a/apps/app/components/project/issues/list-view/index.tsx b/apps/app/components/project/issues/list-view/index.tsx index da9e36fdc68..7dd52dc5a9c 100644 --- a/apps/app/components/project/issues/list-view/index.tsx +++ b/apps/app/components/project/issues/list-view/index.tsx @@ -29,7 +29,7 @@ import { WORKSPACE_MEMBERS, } from "constants/fetch-keys"; // ui -import { CustomMenu, Spinner } from "ui"; +import { CustomMenu, CustomSelect, Spinner } from "ui"; // icons import User from "public/user.png"; import { ChevronDownIcon, PlusIcon, CalendarDaysIcon } from "@heroicons/react/24/outline"; @@ -228,9 +228,6 @@ const ListView: React.FC = ({ : "None", "text-sm" )} - {issue.priority && issue.priority !== "" - ? issue.priority - : null} = ({ value={priority} > {getPriorityIcon(priority, "text-sm")} - {priority} + {priority ?? "None"} ))} @@ -284,59 +281,39 @@ const ListView: React.FC = ({ )} {properties.state && ( - + + {addSpaceIfCamelCase(issue.state_detail.name)} + + } value={issue.state} onChange={(data: string) => { partialUpdateIssue({ state: data }, issue.id); }} - className="group relative flex-shrink-0" + maxHeight="md" + noChevron > - {({ open }) => ( - <> -
- - - {addSpaceIfCamelCase(issue.state_detail.name)} - - - - - {states?.map((state) => ( - - classNames( - active ? "bg-indigo-50" : "bg-white", - "cursor-pointer select-none px-3 py-2" - ) - } - value={state.id} - > - {addSpaceIfCamelCase(state.name)} - - ))} - - -
-
-
State
-
{issue.state_detail.name}
-
- - )} -
+ {states?.map((state) => ( + + <> + + {addSpaceIfCamelCase(state.name)} + + + ))} + )} {properties.due_date && (
= ({ leaveFrom="opacity-100" leaveTo="opacity-0" > - + {people?.map((person) => ( = ({ bgColor={ selectedGroup === "state_detail.name" ? states?.find((s) => s.name === singleGroup)?.color - : undefined + : "#000000" } properties={properties} removeIssueFromModule={removeIssueFromModule} diff --git a/apps/app/components/project/modules/board-view/single-board.tsx b/apps/app/components/project/modules/board-view/single-board.tsx index a154ebb165e..e9cc8c476c9 100644 --- a/apps/app/components/project/modules/board-view/single-board.tsx +++ b/apps/app/components/project/modules/board-view/single-board.tsx @@ -197,7 +197,7 @@ const SingleCycleBoard: React.FC = ({ } className="mt-1" optionsPosition="left" - withoutBorder + noBorder > { diff --git a/apps/app/components/project/modules/list-view/index.tsx b/apps/app/components/project/modules/list-view/index.tsx index 5f383416e81..5aef47ea342 100644 --- a/apps/app/components/project/modules/list-view/index.tsx +++ b/apps/app/components/project/modules/list-view/index.tsx @@ -163,7 +163,7 @@ const ModulesListView: React.FC = ({ } optionsPosition="left" - withoutBorder + noBorder > { diff --git a/apps/app/components/project/modules/module-detail-sidebar/index.tsx b/apps/app/components/project/modules/module-detail-sidebar/index.tsx index ac697bdc71c..59b4671c8c3 100644 --- a/apps/app/components/project/modules/module-detail-sidebar/index.tsx +++ b/apps/app/components/project/modules/module-detail-sidebar/index.tsx @@ -28,7 +28,7 @@ import { // types import { IModule, ModuleIssueResponse } from "types"; // fetch-keys -import { MODULE_DETAIL } from "constants/fetch-keys"; +import { MODULE_LIST } from "constants/fetch-keys"; // common import { copyTextToClipboard, groupBy } from "constants/common"; @@ -55,9 +55,7 @@ const ModuleDetailSidebar: React.FC = ({ const [moduleLinkModal, setModuleLinkModal] = useState(false); const router = useRouter(); - const { - query: { workspaceSlug, projectId }, - } = router; + const { workspaceSlug, projectId, moduleId } = router.query; const { setToastAlert } = useToast(); @@ -91,7 +89,12 @@ const ModuleDetailSidebar: React.FC = ({ .patchModule(workspaceSlug as string, projectId as string, module.id, data) .then((res) => { console.log(res); - mutate(MODULE_DETAIL); + mutate(projectId && MODULE_LIST(projectId as string), (prevData) => + (prevData ?? []).map((module) => { + if (module.id === moduleId) return { ...module, ...data }; + return module; + }) + ); }) .catch((e) => { console.log(e); @@ -215,7 +218,7 @@ const ModuleDetailSidebar: React.FC = ({ render={({ field: { value, onChange } }) => ( { submitChanges({ target_date: e.target.value }); diff --git a/apps/app/components/project/modules/module-link-modal.tsx b/apps/app/components/project/modules/module-link-modal.tsx index fc40e8e94b8..82fd04d6508 100644 --- a/apps/app/components/project/modules/module-link-modal.tsx +++ b/apps/app/components/project/modules/module-link-modal.tsx @@ -16,7 +16,7 @@ import { Button, Input } from "ui"; // types import type { IModule, ModuleLink } from "types"; // fetch-keys -import { MODULE_DETAIL } from "constants/fetch-keys"; +import { MODULE_DETAIL, MODULE_LIST } from "constants/fetch-keys"; type Props = { isOpen: boolean; @@ -31,7 +31,7 @@ const defaultValues: ModuleLink = { const ModuleLinkModal: React.FC = ({ isOpen, module, handleClose }) => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug, projectId, moduleId } = router.query; const { register, @@ -57,7 +57,12 @@ const ModuleLinkModal: React.FC = ({ isOpen, module, handleClose }) => { await modulesService .patchModule(workspaceSlug as string, projectId as string, module.id, payload) .then((res) => { - mutate(MODULE_DETAIL); + mutate(projectId && MODULE_LIST(projectId as string), (prevData) => + (prevData ?? []).map((module) => { + if (module.id === moduleId) return { ...module, ...payload }; + return module; + }) + ); onClose(); }) .catch((err) => { diff --git a/apps/app/components/rich-text-editor/index.tsx b/apps/app/components/rich-text-editor/index.tsx index 0b1fbe6bbe5..d9f34f0b475 100644 --- a/apps/app/components/rich-text-editor/index.tsx +++ b/apps/app/components/rich-text-editor/index.tsx @@ -1,4 +1,4 @@ -import { useCallback, FC, useEffect } from "react"; +import { useCallback, FC, useEffect, useState } from "react"; import { InvalidContentHandler } from "remirror"; import { BoldExtension, @@ -34,6 +34,7 @@ import { tableControllerPluginKey, TableExtension } from "@remirror/extension-re import { RichTextToolbar } from "./toolbar"; import { MentionAutoComplete } from "./mention-autocomplete"; import fileService from "lib/services/file.service"; +import { Spinner } from "ui"; type SetProgress = (progress: number) => void; interface FileWithProgress { @@ -58,6 +59,8 @@ const RemirrorRichTextEditor: FC = ({ value = "", showToolbar = true, }) => { + const [imageLoader, setImageLoader] = useState(false); + // remirror error handler const onError: InvalidContentHandler = useCallback( ({ json, invalidContent, transformers }: any) => { @@ -68,11 +71,15 @@ const RemirrorRichTextEditor: FC = ({ ); const uploadImageHandler = (value: any): any => { + setImageLoader(true); + try { const formData = new FormData(); formData.append("asset", value[0].file); formData.append("attributes", JSON.stringify({})); + setImageLoader(true); + return [ () => { return new Promise(async (resolve, reject) => { @@ -87,6 +94,8 @@ const RemirrorRichTextEditor: FC = ({ width: "100%", src: imageUrl, }); + + setImageLoader(false); }); }, ]; @@ -146,6 +155,11 @@ const RemirrorRichTextEditor: FC = ({
)} + {imageLoader && ( +
+ +
+ )} {/* */} diff --git a/apps/app/components/rich-text-editor/toolbar/index.tsx b/apps/app/components/rich-text-editor/toolbar/index.tsx index 5ed801b6eae..f20d554d764 100644 --- a/apps/app/components/rich-text-editor/toolbar/index.tsx +++ b/apps/app/components/rich-text-editor/toolbar/index.tsx @@ -35,9 +35,9 @@ export const RichTextToolbar: React.FC = () => {
-
+ {/*
-
+
*/}
diff --git a/apps/app/components/workspace/SingleInvitation.tsx b/apps/app/components/workspace/SingleInvitation.tsx index 2930776c0a2..c6c3249b538 100644 --- a/apps/app/components/workspace/SingleInvitation.tsx +++ b/apps/app/components/workspace/SingleInvitation.tsx @@ -22,13 +22,13 @@ const SingleInvitation: React.FC = ({ <>
  • diff --git a/apps/app/pages/[workspaceSlug]/me/my-issues.tsx b/apps/app/pages/[workspaceSlug]/me/my-issues.tsx index a8030292ae5..cc9236e5388 100644 --- a/apps/app/pages/[workspaceSlug]/me/my-issues.tsx +++ b/apps/app/pages/[workspaceSlug]/me/my-issues.tsx @@ -29,9 +29,10 @@ import { classNames, replaceUnderscoreIfSnakeCase } from "constants/common"; import SingleListIssue from "components/common/list-view/single-issue"; const MyIssues: NextPage = () => { + const router = useRouter(); const { query: { workspaceSlug }, - } = useRouter(); + } = router; const { user } = useUser(); @@ -40,6 +41,8 @@ const MyIssues: NextPage = () => { user && workspaceSlug ? () => userService.userIssues(workspaceSlug as string) : null ); + console.log(myIssues); + const { data: people } = useSWR( workspaceSlug ? WORKSPACE_MEMBERS : null, workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug as string) : null @@ -166,7 +169,6 @@ const MyIssues: NextPage = () => { issue={issue} properties={properties} removeIssue={() => {}} - type="cycle" /> ))}
    diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx index 834797ff741..33f5991696f 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx @@ -25,10 +25,15 @@ import useIssuesProperties from "lib/hooks/useIssuesProperties"; // headless ui import { Popover, Transition } from "@headlessui/react"; // ui -import { BreadcrumbItem, Breadcrumbs, CustomMenu } from "ui"; +import { BreadcrumbItem, Breadcrumbs, CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "ui"; // icons import { Squares2X2Icon } from "@heroicons/react/20/solid"; -import { ArrowPathIcon, ChevronDownIcon, ListBulletIcon } from "@heroicons/react/24/outline"; +import { + ArrowPathIcon, + ChevronDownIcon, + ListBulletIcon, + PlusIcon, +} from "@heroicons/react/24/outline"; // types import { CycleIssueResponse, IIssue, Properties, SelectIssue } from "types"; // fetch-keys @@ -97,6 +102,7 @@ const SingleCycle: React.FC = () => { cycleServices.getCycleIssues(activeWorkspace?.slug, activeProject?.id, cycleId as string) : null ); + console.log(cycleIssues); const cycleIssuesArray = cycleIssues?.map((issue) => { return { bridge: issue.id, ...issue.issue_detail }; }); @@ -371,41 +377,82 @@ const SingleCycle: React.FC = () => { } noPadding > -
    -
    - {issueView === "list" ? ( - - ) : ( - - )} -
    -
    - c.id === (cycleId as string))} - cycleIssues={cycleIssues ?? []} - /> + {Object.keys(groupedByIssues) ? ( + Object.keys(groupedByIssues).length > 0 ? ( +
    +
    + {issueView === "list" ? ( + + ) : ( + + )} +
    +
    + c.id === (cycleId as string))} + cycleIssues={cycleIssues ?? []} + /> +
    +
    + ) : ( +
    + + + Use{" "} +
    Ctrl/Command + I
    {" "} + shortcut to create a new issue + + } + Icon={PlusIcon} + action={() => { + const e = new KeyboardEvent("keydown", { + ctrlKey: true, + key: "i", + }); + document.dispatchEvent(e); + }} + /> + Open list} + Icon={ListBulletIcon} + action={() => openIssuesListModal()} + /> +
    +
    + ) + ) : ( +
    +
    -
    + )} ); diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index 396044f50aa..adb8e4a5c21 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -140,7 +140,7 @@ const ProjectSprints: NextPage = () => { - + { type="upcoming" /> - + import("components/rich-text-editor"), { ssr: false }); -const IssueActivitySection = dynamic( - () => import("components/project/issues/issue-detail/activity"), - { - loading: () => ( - -
    - - -
    -
    - ), - ssr: false, - } -); const defaultValues = { name: "", @@ -278,46 +260,26 @@ const IssueDetail: NextPage = () => { - - - - - - -
    - {siblingIssues && siblingIssues.length > 0 ? ( - siblingIssues.map((issue) => ( - - - - {activeProject.identifier}-{issue.sequence_id} - - - - )) - ) : ( - - No other sub-issues - - )} -
    -
    -
    -
    + + {siblingIssues && siblingIssues.length > 0 ? ( + siblingIssues.map((issue) => ( + + + + {activeProject.identifier}-{issue.sequence_id} + + + + )) + ) : ( + + No other sibling issues + + )} +
    ) : null}
    @@ -340,7 +302,12 @@ const IssueDetail: NextPage = () => { render={({ field: { value, onChange } }) => ( { + onChange(val); + debounce(() => { + handleSubmit(submitChanges)(); + }, 5000)(); + }} placeholder="Enter Your Text..." /> )} @@ -374,35 +341,11 @@ const IssueDetail: NextPage = () => { Create new - - - - - - - -
    - - - -
    -
    -
    -
    + + setIsAddAsSubIssueOpen(true)}> + Add an existing issue + +
    ) : null}
    @@ -439,34 +382,13 @@ const IssueDetail: NextPage = () => {
    - - - - - - + handleSubIssueRemove(subIssue.id)} > - -
    - - - -
    -
    -
    -
    + Remove as sub-issue + +
    ))} @@ -476,85 +398,52 @@ const IssueDetail: NextPage = () => { )} ) : ( - - - - Add sub-issue - - - + + Add sub-issue + + } + optionsPosition="left" + noBorder + > + { + setIsOpen(true); + setPreloadedData({ + parent: issueDetail.id, + actionType: "createIssue", + }); + }} > - -
    - - - - - - -
    -
    -
    -
    + Create new + + { + setIsAddAsSubIssueOpen(true); + setPreloadedData({ + parent: issueDetail.id, + actionType: "createIssue", + }); + }} + > + Add an existing issue + + + // + // + // + // Add sub-issue + // + // )}
    - - - {["Comments", "Activity"].map((item) => ( - - `rounded-md border-2 border-gray-700 px-3 py-1 text-sm ${ - selected ? "bg-gray-700 text-white" : "" - }` - } - > - {item} - - ))} - - - - - - - - - - +

    Comments/Activity

    + +
    diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx index 3501083eb86..2cdb3b26f7f 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx @@ -219,7 +219,7 @@ const SingleModule = () => { @@ -233,7 +233,7 @@ const SingleModule = () => { setDeleteIssue(undefined)} isOpen={!!deleteIssue} - data={issues?.results.find((issue) => issue.id === deleteIssue)} + data={moduleIssuesArray?.find((issue) => issue.id === deleteIssue)} /> { Use{" "}
    Ctrl/Command + I
    {" "} - shortcut to create a new cycle + shortcut to create a new issue
    } Icon={PlusIcon} diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/control.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/control.tsx index c84206fc449..773822b0422 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/control.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/control.tsx @@ -18,7 +18,7 @@ import workspaceService from "lib/services/workspace.service"; // hooks import useToast from "lib/hooks/useToast"; // ui -import { BreadcrumbItem, Breadcrumbs, Button } from "ui"; +import { BreadcrumbItem, Breadcrumbs, Button, CustomSelect } from "ui"; // icons import { CheckIcon, ChevronDownIcon } from "@heroicons/react/24/outline"; // types @@ -157,73 +157,25 @@ const ControlSettings: NextPage = (props) => {

    Project Lead

    Select the project leader.

    ( - - {({ open }) => ( - <> -
    - - - {people?.find((person) => person.member.id === value)?.member - .first_name ?? "Select Lead"} - - - - - - {people?.map((person) => ( - - `${ - active ? "bg-indigo-50" : "" - } relative cursor-default select-none px-3 py-2 text-gray-900` - } - value={person.member.id} - > - {({ selected, active }) => ( - <> - - {person.member.first_name !== "" - ? person.member.first_name - : person.member.email} - - - {selected ? ( - - - ) : null} - - )} - - ))} - - -
    - - )} -
    + control={control} + render={({ field }) => ( + person.member.id === field.value)?.member + .first_name ?? "Select Lead" + } + input + > + {people?.map((person) => ( + + {person.member.first_name !== "" + ? person.member.first_name + : person.member.email} + + ))} + )} />
    @@ -235,73 +187,25 @@ const ControlSettings: NextPage = (props) => { Select the default assignee for the project.

    ( - - {({ open }) => ( - <> -
    - - - {people?.find((p) => p.member.id === value)?.member.first_name ?? - "Select Default Assignee"} - - - - - - {people?.map((person) => ( - - `${ - active ? "bg-indigo-50" : "" - } relative cursor-default select-none px-3 py-2 text-gray-900` - } - value={person.member.id} - > - {({ selected, active }) => ( - <> - - {person.member.first_name !== "" - ? person.member.first_name - : person.member.email} - - - {selected ? ( - - - ) : null} - - )} - - ))} - - -
    - - )} -
    + control={control} + render={({ field }) => ( + p.member.id === field.value)?.member.first_name ?? + "Select Default Assignee" + } + input + > + {people?.map((person) => ( + + {person.member.first_name !== "" + ? person.member.first_name + : person.member.email} + + ))} + )} />
    diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx index 8aa2f6834d7..649d65c70e3 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx @@ -31,6 +31,7 @@ import { Select, TextArea, Loader, + CustomSelect, } from "ui"; // types import { IProject, IWorkspace } from "types"; @@ -282,19 +283,28 @@ const GeneralSettings: NextPage = (props) => {

    Network

    Select privacy type for the project.

    {projectDetails ? ( - ( + + {[ + { value: 5, label: "5" }, + { value: 10, label: "10" }, + { value: 25, label: "25" }, + { value: 50, label: "50" }, + ]?.map((item) => ( + + {item.label} + + ))} + + )} />
    - {isOwner && ( - <> -
    - -
    -
    -
    -

    Danger Zone

    -

    - The danger zone of the workspace delete page is a critical area that requires - careful consideration and attention. When deleting a workspace, all of the - data and resources within that workspace will be permanently removed and - cannot be recovered. -

    -
    -
    - -
    -
    - - )} +
    + +
    +
    +
    +

    Danger Zone

    +

    + The danger zone of the workspace delete page is a critical area that requires + careful consideration and attention. When deleting a workspace, all of the data + and resources within that workspace will be permanently removed and cannot be + recovered. +

    +
    +
    + setIsOpen(true)}> + Delete the workspace + +
    +
    ) : ( diff --git a/apps/app/pages/invitations.tsx b/apps/app/pages/invitations.tsx index bd748330fc1..c45dc9a3c34 100644 --- a/apps/app/pages/invitations.tsx +++ b/apps/app/pages/invitations.tsx @@ -55,8 +55,9 @@ const OnBoard: NextPage = () => { } }; - const submitInvitations = async () => { + const submitInvitations = () => { userService.updateUserOnBoard(); + workspaceService .joinWorkspaces({ invitations: invitationsRespond }) .then(() => { @@ -133,7 +134,9 @@ const OnBoard: NextPage = () => {
    ))} - + + +
    ) : ( diff --git a/apps/app/pages/onboarding.tsx b/apps/app/pages/onboarding.tsx index 550acd52ce5..0c19b6118fb 100644 --- a/apps/app/pages/onboarding.tsx +++ b/apps/app/pages/onboarding.tsx @@ -2,6 +2,8 @@ import { useState } from "react"; // next import { useRouter } from "next/router"; +// hooks +import useUser from "lib/hooks/useUser"; // layouts import DefaultLayout from "layouts/DefaultLayout"; // components @@ -9,40 +11,55 @@ import Welcome from "components/onboarding/welcome"; import PlanWithIssues from "components/onboarding/plan-with-issues"; import MoveWithCycles from "components/onboarding/move-with-cycles"; import BreakIntoModules from "components/onboarding/break-into-modules"; +import UserDetails from "components/onboarding/user-details"; +import Workspace from "components/onboarding/workspace"; +import InviteMembers from "components/onboarding/invite-members"; const Onboarding = () => { const [step, setStep] = useState(1); const router = useRouter(); + const { user } = useUser(); + return (
    -
    -
    - {step === 1 ? ( - - ) : step === 2 ? ( - - ) : step === 3 ? ( - - ) : step === 4 ? ( - - ) : null} -
    -
    - + {step <= 3 ? ( + step === 1 ? ( + + ) : step === 2 ? ( + + ) : ( + + ) + ) : ( +
    +
    + {step === 4 ? ( + + ) : step === 5 ? ( + + ) : step === 6 ? ( + + ) : step === 7 ? ( + + ) : null} +
    +
    + +
    -
    + )}
    ); diff --git a/apps/app/styles/editor.css b/apps/app/styles/editor.css index f3b9ee07fb5..f74eb8e5f1b 100644 --- a/apps/app/styles/editor.css +++ b/apps/app/styles/editor.css @@ -354,7 +354,7 @@ img.ProseMirror-separator { margin-bottom: 1em; } -.remirror-editor-wrapper { +.remirror-editor-wrapper .remirror-editor { min-height: 150px; } diff --git a/apps/app/styles/globals.css b/apps/app/styles/globals.css index 67f2056b98f..1fc1053bf56 100644 --- a/apps/app/styles/globals.css +++ b/apps/app/styles/globals.css @@ -25,6 +25,9 @@ /* Scrollbar style */ ::-webkit-scrollbar { + display: none; +} +/* ::-webkit-scrollbar { height: 10px; width: 5px; border-radius: 10px; @@ -42,7 +45,7 @@ ::-webkit-scrollbar-thumb:hover { background: #908f8f; -} +} */ .no-scrollbar::-webkit-scrollbar { display: none; diff --git a/apps/app/types/issues.d.ts b/apps/app/types/issues.d.ts index 4d8dbb660be..bbf5d276b45 100644 --- a/apps/app/types/issues.d.ts +++ b/apps/app/types/issues.d.ts @@ -86,6 +86,8 @@ export interface IIssue { labels_list: string[]; blockers: any[]; blocked_issue_details: any[]; + sprints: string | null; + module: string | null; cycle: string | null; cycle_detail: ICycle | null; } diff --git a/apps/app/ui/button/index.tsx b/apps/app/ui/button/index.tsx index ab6e483199c..0bd26cc9aea 100644 --- a/apps/app/ui/button/index.tsx +++ b/apps/app/ui/button/index.tsx @@ -34,13 +34,13 @@ const Button = React.forwardRef( disabled={disabled} className={classNames( className, - "inline-flex items-center justify-center rounded font-medium", + "inline-flex items-center justify-center rounded font-medium duration-300", theme === "primary" ? `${ disabled ? "opacity-70" : "" - } border border-transparent bg-theme text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500` + } border border-transparent bg-gray-200 shadow-sm hover:bg-gray-300 focus:outline-none` : theme === "secondary" - ? "border bg-transparent hover:bg-gray-100" + ? "border border-gray-300 bg-transparent hover:bg-gray-200" : theme === "success" ? `${ disabled ? "opacity-70" : "" diff --git a/apps/app/ui/custom-listbox/index.tsx b/apps/app/ui/custom-listbox/index.tsx index f48bb7ded16..04c55150a58 100644 --- a/apps/app/ui/custom-listbox/index.tsx +++ b/apps/app/ui/custom-listbox/index.tsx @@ -85,55 +85,31 @@ const CustomListbox: React.FC = ({ options.map((option) => ( + className={({ selected, active }) => `${ + selected || + (Array.isArray(value) + ? value.includes(option.value) + : value === option.value) + ? "bg-indigo-50 font-medium" + : "" + } ${ active ? "bg-indigo-50" : "" } relative cursor-pointer select-none p-2 text-gray-900` } value={option.value} > - {({ selected, active }) => ( - <> + + {option.color && ( - {option.color && ( - - )} - {option.display} - - - {selected || - (Array.isArray(value) - ? value.includes(option.value) - : value === option.value) ? ( - - - ) : null} - - )} + className="h-1.5 w-1.5 flex-shrink-0 rounded-full" + style={{ + backgroundColor: option.color, + }} + > + )} + {option.display} + )) ) : ( diff --git a/apps/app/ui/custom-menu/index.tsx b/apps/app/ui/custom-menu/index.tsx index 612e809684b..6656771c924 100644 --- a/apps/app/ui/custom-menu/index.tsx +++ b/apps/app/ui/custom-menu/index.tsx @@ -17,7 +17,7 @@ const CustomMenu = ({ ellipsis = false, width = "auto", textAlignment, - withoutBorder = false, + noBorder = false, optionsPosition = "right", }: Props) => { return ( @@ -36,7 +36,7 @@ const CustomMenu = ({ ? "text-center" : "text-left" } ${ - withoutBorder + noBorder ? "rounded" : "rounded-md border shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500" } ${ @@ -52,7 +52,7 @@ const CustomMenu = ({ }`} > {label} - {!withoutBorder &&
    diff --git a/apps/app/ui/custom-menu/types.d.ts b/apps/app/ui/custom-menu/types.d.ts index 6cdfcbf7608..588b30ab06e 100644 --- a/apps/app/ui/custom-menu/types.d.ts +++ b/apps/app/ui/custom-menu/types.d.ts @@ -5,7 +5,7 @@ export type Props = { ellipsis?: boolean; width?: "sm" | "md" | "lg" | "xl" | "auto"; textAlignment?: "left" | "center" | "right"; - withoutBorder?: boolean; + noBorder?: boolean; optionsPosition?: "left" | "right"; }; diff --git a/apps/app/ui/custom-select/index.tsx b/apps/app/ui/custom-select/index.tsx index 34ac1583048..ab4412b1e44 100644 --- a/apps/app/ui/custom-select/index.tsx +++ b/apps/app/ui/custom-select/index.tsx @@ -10,7 +10,10 @@ type CustomSelectProps = { children: React.ReactNode; label: string | JSX.Element; textAlignment?: "left" | "center" | "right"; + maxHeight?: "sm" | "rg" | "md" | "lg" | "none"; width?: "auto" | string; + input?: boolean; + noChevron?: boolean; }; const CustomSelect = ({ @@ -19,7 +22,10 @@ const CustomSelect = ({ textAlignment, value, onChange, + maxHeight = "none", width = "auto", + input = false, + noChevron = false, }: CustomSelectProps) => { return (
    {label} -
    @@ -53,8 +61,18 @@ const CustomSelect = ({ leaveTo="transform opacity-0 scale-95" >
    {children}
    @@ -75,8 +93,8 @@ const Option: React.FC = ({ children, value, className }) => { - `${ - active || selected ? "bg-indigo-50" : "" + `${selected ? "bg-indigo-50 font-medium" : ""} ${ + active ? "bg-indigo-50" : "" } relative flex cursor-pointer select-none items-center gap-2 truncate p-2 text-gray-900 ${className}` } > diff --git a/apps/app/ui/icons/tag-icon.tsx b/apps/app/ui/icons/tag-icon.tsx index ac55144cafe..5881ad3a8a2 100644 --- a/apps/app/ui/icons/tag-icon.tsx +++ b/apps/app/ui/icons/tag-icon.tsx @@ -2,7 +2,12 @@ import React from "react"; import type { Props } from "./types"; -export const TagIcon: React.FC = ({ width = "24", height = "24", className }) => { +export const TagIcon: React.FC = ({ + width = "24", + height = "24", + className, + color = "black", +}) => { return ( = ({ width = "24", height = "24", classNam > ); diff --git a/apps/app/ui/input/index.tsx b/apps/app/ui/input/index.tsx index 561e865a3f6..0c9ab453745 100644 --- a/apps/app/ui/input/index.tsx +++ b/apps/app/ui/input/index.tsx @@ -23,7 +23,7 @@ const Input: React.FC = ({ return ( <> {label && ( -