From 68930c256f7b4215df52530ffeca3fce2b42f6bd Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Sat, 22 Apr 2023 00:15:45 +0530 Subject: [PATCH 01/64] style: sidebar theming (#925) * style: sidebar workspace dropdown theming * style: progress chart and progress bar theming * style: module and cycle sidebar theming --- .../components/core/sidebar/progress-chart.tsx | 18 +++--------------- apps/app/components/cycles/sidebar.tsx | 16 ++++++++-------- .../modules/sidebar-select/select-lead.tsx | 4 ++-- .../modules/sidebar-select/select-members.tsx | 4 ++-- apps/app/components/modules/sidebar.tsx | 12 ++++++------ apps/app/components/ui/progress-bar.tsx | 2 +- .../components/workspace/sidebar-dropdown.tsx | 8 ++++---- apps/app/styles/globals.css | 5 +++++ 8 files changed, 31 insertions(+), 38 deletions(-) diff --git a/apps/app/components/core/sidebar/progress-chart.tsx b/apps/app/components/core/sidebar/progress-chart.tsx index bb3f7e059..f8454650e 100644 --- a/apps/app/components/core/sidebar/progress-chart.tsx +++ b/apps/app/components/core/sidebar/progress-chart.tsx @@ -40,7 +40,7 @@ const ProgressChart: React.FC = ({ issues, start, end }) => { const CustomTooltip = ({ active, payload }: TooltipProps) => { if (active && payload && payload.length) { return ( -
+

{payload[0].payload.currentDate}

); @@ -68,20 +68,8 @@ const ProgressChart: React.FC = ({ issues, start, end }) => { - - + + } /> = ({
{cycle ? ( <> @@ -297,15 +297,15 @@ export const CycleDetailsSidebar: React.FC = ({
- + {cycle.description}
-
- +
+ Lead
@@ -323,17 +323,17 @@ export const CycleDetailsSidebar: React.FC = ({ {cycle.owned_by.first_name.charAt(0)} )} - {cycle.owned_by.first_name} + {cycle.owned_by.first_name}
-
- +
+ Progress
-
+
diff --git a/apps/app/components/modules/sidebar-select/select-lead.tsx b/apps/app/components/modules/sidebar-select/select-lead.tsx index 795d0f75c..b551294fb 100644 --- a/apps/app/components/modules/sidebar-select/select-lead.tsx +++ b/apps/app/components/modules/sidebar-select/select-lead.tsx @@ -54,8 +54,8 @@ export const SidebarLeadSelect: React.FC = ({ value, onChange }) => { return (
-
- +
+ Lead
diff --git a/apps/app/components/modules/sidebar-select/select-members.tsx b/apps/app/components/modules/sidebar-select/select-members.tsx index c71a2c528..2f024913f 100644 --- a/apps/app/components/modules/sidebar-select/select-members.tsx +++ b/apps/app/components/modules/sidebar-select/select-members.tsx @@ -50,8 +50,8 @@ export const SidebarMembersSelect: React.FC = ({ value, onChange }) => { return (
-
- +
+ Members
diff --git a/apps/app/components/modules/sidebar.tsx b/apps/app/components/modules/sidebar.tsx index b33bf6613..e3d75be45 100644 --- a/apps/app/components/modules/sidebar.tsx +++ b/apps/app/components/modules/sidebar.tsx @@ -185,7 +185,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen,
{module ? ( <> @@ -336,7 +336,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen,
- + {module.description}
@@ -368,12 +368,12 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen, />
-
- +
+ Progress
-
+
= ({ issues, module, isOpen,
-
+
{memberRole && module.link_module && module.link_module.length > 0 ? ( = ({ return ( {renderOuterCircle()} - + ); }; diff --git a/apps/app/components/workspace/sidebar-dropdown.tsx b/apps/app/components/workspace/sidebar-dropdown.tsx index fa9163d65..b1e23a12f 100644 --- a/apps/app/components/workspace/sidebar-dropdown.tsx +++ b/apps/app/components/workspace/sidebar-dropdown.tsx @@ -86,7 +86,7 @@ export const WorkspaceSidebarDropdown = () => {
@@ -189,9 +189,9 @@ export const WorkspaceSidebarDropdown = () => { onClick={() => { router.push("/create-workspace"); }} - className="flex w-full items-center gap-1 text-sm text-gray-600" + className="flex w-full items-center gap-1 text-sm text-brand-secondary" > - + Create Workspace
@@ -209,7 +209,7 @@ export const WorkspaceSidebarDropdown = () => { {link.name} diff --git a/apps/app/styles/globals.css b/apps/app/styles/globals.css index 3393842ee..c16b39807 100644 --- a/apps/app/styles/globals.css +++ b/apps/app/styles/globals.css @@ -206,4 +206,9 @@ ); } +.progress-bar{ + fill: currentColor; + color: rgba(var(--color-bg-sidebar)); +} + /* end react datepicker styling */ From e17c82411985233389370b600e99739e41d1afc6 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Sat, 22 Apr 2023 00:56:17 +0530 Subject: [PATCH 02/64] style: dashboard styling (#924) --- apps/app/components/workspace/activity-graph.tsx | 4 ++-- .../components/workspace/completed-issues-graph.tsx | 4 ++-- apps/app/components/workspace/issues-list.tsx | 10 +++++----- apps/app/components/workspace/issues-pie-chart.tsx | 4 ++-- apps/app/components/workspace/issues-stats.tsx | 2 +- apps/app/pages/[workspaceSlug]/index.tsx | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/app/components/workspace/activity-graph.tsx b/apps/app/components/workspace/activity-graph.tsx index 741a44e9c..1f9db203d 100644 --- a/apps/app/components/workspace/activity-graph.tsx +++ b/apps/app/components/workspace/activity-graph.tsx @@ -118,7 +118,7 @@ export const ActivityGraph: React.FC = ({ activities }) => { } h-4 w-4 rounded ${ isActive ? `bg-brand-accent ${activitiesIntensity(isActive.activity_count)}` - : "bg-brand-base" + : "bg-brand-surface-2" }`} /> @@ -127,7 +127,7 @@ export const ActivityGraph: React.FC = ({ activities }) => {
Less - + diff --git a/apps/app/components/workspace/completed-issues-graph.tsx b/apps/app/components/workspace/completed-issues-graph.tsx index 7439bf9cb..e34f1a939 100644 --- a/apps/app/components/workspace/completed-issues-graph.tsx +++ b/apps/app/components/workspace/completed-issues-graph.tsx @@ -55,10 +55,10 @@ export const CompletedIssuesGraph: React.FC = ({ month, issues, setMonth ))}
-
+
- + } /> diff --git a/apps/app/components/workspace/issues-list.tsx b/apps/app/components/workspace/issues-list.tsx index 3325040de..9cf7b4295 100644 --- a/apps/app/components/workspace/issues-list.tsx +++ b/apps/app/components/workspace/issues-list.tsx @@ -36,10 +36,10 @@ export const IssuesList: React.FC = ({ issues, type }) => {

{type} Issues

{issues ? ( -
+

{type}

@@ -83,11 +83,11 @@ export const IssuesList: React.FC = ({ issues, type }) => { }) ) : (
-
+
- + No issues found. Use{" "} -
C
shortcut to +
C
shortcut to create a new issue
diff --git a/apps/app/components/workspace/issues-pie-chart.tsx b/apps/app/components/workspace/issues-pie-chart.tsx index eedcdd6f3..d65e80c4a 100644 --- a/apps/app/components/workspace/issues-pie-chart.tsx +++ b/apps/app/components/workspace/issues-pie-chart.tsx @@ -69,7 +69,7 @@ export const IssuesPieChart: React.FC = ({ groupedIssues }) => { /> - = 0 ? 1 : -1) * 12} y={ey} textAnchor={textAnchor} fill="#333"> + = 0 ? 1 : -1) * 12} y={ey} textAnchor={textAnchor} fill="#858e96"> {value} issues @@ -79,7 +79,7 @@ export const IssuesPieChart: React.FC = ({ groupedIssues }) => { return (

Issues by States

-
+
= ({ data }) => ( -
+
diff --git a/apps/app/pages/[workspaceSlug]/index.tsx b/apps/app/pages/[workspaceSlug]/index.tsx index 5e46cac57..adf893f88 100644 --- a/apps/app/pages/[workspaceSlug]/index.tsx +++ b/apps/app/pages/[workspaceSlug]/index.tsx @@ -21,7 +21,7 @@ import type { NextPage } from "next"; import { USER_WORKSPACE_DASHBOARD } from "constants/fetch-keys"; const WorkspacePage: NextPage = () => { - const [month, setMonth] = useState(new Date().getMonth() + 1); + const [month, setMonth] = useState(new Date().getMonth() + 1); const router = useRouter(); const { workspaceSlug } = router.query; @@ -42,7 +42,7 @@ const WorkspacePage: NextPage = () => {

@@ -55,7 +55,7 @@ const WorkspacePage: NextPage = () => { Star us on GitHub From cb814dd68b752c1154d57bf06a6b985e1b78a90d Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Sat, 22 Apr 2023 00:57:16 +0530 Subject: [PATCH 03/64] chore: added new restricted workspace slugs (#928) --- apps/app/components/workspace/create-workspace-form.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/app/components/workspace/create-workspace-form.tsx b/apps/app/components/workspace/create-workspace-form.tsx index 1c4e7114f..eef1e74bf 100644 --- a/apps/app/components/workspace/create-workspace-form.tsx +++ b/apps/app/components/workspace/create-workspace-form.tsx @@ -28,12 +28,15 @@ type Props = { }; const restrictedUrls = [ + "api", "create-workspace", "error", + "installations", "invitations", "magic-sign-in", "onboarding", "signin", + "workspace-member-invitation", ]; export const CreateWorkspaceForm: React.FC = ({ From c638b6aba6fe0c3b7ec468685207be8d9c794559 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Sat, 22 Apr 2023 00:59:57 +0530 Subject: [PATCH 04/64] chore: new estimates workflow (#927) * chore: new services for estimates * chore: new estimates workflow --- .../create-update-estimate-modal.tsx | 315 +++++++++++++---- .../estimates/estimate-points-modal.tsx | 329 ------------------ apps/app/components/estimates/index.tsx | 3 +- .../components/estimates/single-estimate.tsx | 114 ++---- .../app/components/issues/select/estimate.tsx | 2 +- .../issues/view-select/estimate.tsx | 2 +- .../states/create-update-state-inline.tsx | 3 +- apps/app/components/ui/input/index.tsx | 8 +- apps/app/constants/fetch-keys.ts | 2 - apps/app/helpers/array.helper.ts | 2 + apps/app/hooks/use-estimate-option.tsx | 23 +- .../[projectId]/settings/estimates.tsx | 13 +- apps/app/services/estimates.service.ts | 108 ++---- apps/app/types/estimate.d.ts | 12 + 14 files changed, 349 insertions(+), 587 deletions(-) delete mode 100644 apps/app/components/estimates/estimate-points-modal.tsx diff --git a/apps/app/components/estimates/create-update-estimate-modal.tsx b/apps/app/components/estimates/create-update-estimate-modal.tsx index 92c32a1c2..3d89e5fcf 100644 --- a/apps/app/components/estimates/create-update-estimate-modal.tsx +++ b/apps/app/components/estimates/create-update-estimate-modal.tsx @@ -6,19 +6,20 @@ import { mutate } from "swr"; // react-hook-form import { useForm } from "react-hook-form"; +// headless ui +import { Dialog, Transition } from "@headlessui/react"; // services import estimatesService from "services/estimates.service"; -// ui -import { Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui"; -import { Dialog, Transition } from "@headlessui/react"; - // hooks import useToast from "hooks/use-toast"; - +// ui +import { Input, PrimaryButton, SecondaryButton, TextArea } from "components/ui"; +// helpers +import { checkDuplicates } from "helpers/array.helper"; // types -import { IEstimate } from "types"; +import { IEstimate, IEstimateFormData } from "types"; // fetch-keys -import { ESTIMATES_LIST } from "constants/fetch-keys"; +import { ESTIMATES_LIST, ESTIMATE_DETAILS } from "constants/fetch-keys"; type Props = { isOpen: boolean; @@ -26,18 +27,35 @@ type Props = { data?: IEstimate; }; -const defaultValues: Partial = { +type FormValues = { + name: string; + description: string; + value1: string; + value2: string; + value3: string; + value4: string; + value5: string; + value6: string; +}; + +const defaultValues: Partial = { name: "", description: "", + value1: "", + value2: "", + value3: "", + value4: "", + value5: "", + value6: "", }; export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, isOpen }) => { const { register, - formState: { errors, isSubmitting }, + formState: { isSubmitting }, handleSubmit, reset, - } = useForm({ + } = useForm({ defaultValues, }); @@ -51,47 +69,48 @@ export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, const { setToastAlert } = useToast(); - const createEstimate = async (formData: IEstimate) => { + const createEstimate = async (payload: IEstimateFormData) => { if (!workspaceSlug || !projectId) return; - const payload = { - name: formData.name, - description: formData.description, - }; - await estimatesService .createEstimate(workspaceSlug as string, projectId as string, payload) - .then((res) => { - mutate( - ESTIMATES_LIST(projectId as string), - (prevData) => [res, ...(prevData ?? [])], - false - ); + .then(() => { + mutate(ESTIMATES_LIST(projectId as string)); + onClose(); }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Error: Estimate could not be created", - }); + .catch((err) => { + if (err.status === 400) + setToastAlert({ + type: "error", + title: "Error!", + message: "Estimate with that name already exists. Please try again with another name.", + }); + else + setToastAlert({ + type: "error", + title: "Error!", + message: "Estimate could not be created. Please try again.", + }); }); - - onClose(); }; - const updateEstimate = async (formData: IEstimate) => { + const updateEstimate = async (payload: IEstimateFormData) => { if (!workspaceSlug || !projectId || !data) return; - const payload = { - name: formData.name, - description: formData.description, - }; - mutate( - ESTIMATES_LIST(projectId as string), + ESTIMATES_LIST(projectId.toString()), (prevData) => prevData?.map((p) => { - if (p.id === data.id) return { ...p, ...payload }; + if (p.id === data.id) + return { + ...p, + name: payload.estimate.name, + description: payload.estimate.description, + points: p.points.map((point, index) => ({ + ...point, + value: payload.estimate_points[index].value, + })), + }; return p; }), @@ -100,23 +119,116 @@ export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, await estimatesService .patchEstimate(workspaceSlug as string, projectId as string, data?.id as string, payload) - .then(() => handleClose()) + .then(() => { + mutate(ESTIMATES_LIST(projectId.toString())); + mutate(ESTIMATE_DETAILS(data.id)); + handleClose(); + }) .catch(() => { setToastAlert({ type: "error", title: "Error!", - message: "Error: Estimate could not be updated", + message: "Estimate could not be updated. Please try again.", }); }); onClose(); }; + const onSubmit = async (formData: FormValues) => { + if (!formData.name || formData.name === "") { + setToastAlert({ + type: "error", + title: "Error!", + message: "Estimate title cannot be empty.", + }); + return; + } + + if ( + formData.value1 === "" || + formData.value2 === "" || + formData.value3 === "" || + formData.value4 === "" || + formData.value5 === "" || + formData.value6 === "" + ) { + setToastAlert({ + type: "error", + title: "Error!", + message: "Estimate point cannot be empty.", + }); + return; + } + + if ( + checkDuplicates([ + formData.value1, + formData.value2, + formData.value3, + formData.value4, + formData.value5, + formData.value6, + ]) + ) { + setToastAlert({ + type: "error", + title: "Error!", + message: "Estimate points cannot have duplicate values.", + }); + return; + } + + const payload: IEstimateFormData = { + estimate: { + name: formData.name, + description: formData.description, + }, + estimate_points: [ + { + key: 0, + value: formData.value1, + }, + { + key: 1, + value: formData.value2, + }, + { + key: 2, + value: formData.value3, + }, + { + key: 3, + value: formData.value4, + }, + { + key: 4, + value: formData.value5, + }, + { + key: 5, + value: formData.value6, + }, + ], + }; + + if (data) await updateEstimate(payload); + else await createEstimate(payload); + }; + useEffect(() => { - reset({ - ...defaultValues, - ...data, - }); + if (data) + reset({ + ...defaultValues, + ...data, + value1: data.points[0]?.value, + value2: data.points[1]?.value, + value3: data.points[2]?.value, + value4: data.points[3]?.value, + value5: data.points[4]?.value, + value6: data.points[5]?.value, + }); + else reset({ ...defaultValues }); }, [data, reset]); return ( @@ -146,10 +258,8 @@ export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - -

+ +
{data ? "Update" : "Create"} Estimate @@ -161,17 +271,8 @@ export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, type="name" placeholder="Title" autoComplete="off" - mode="transparent" className="resize-none text-xl" - error={errors.name} register={register} - validations={{ - required: "Title is required", - maxLength: { - value: 255, - message: "Title should be less than 255 characters", - }, - }} />
@@ -180,11 +281,107 @@ export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, name="description" placeholder="Description" className="h-32 resize-none text-sm" - mode="transparent" - error={errors.description} register={register} />
+
+
+ + 1 + + + + +
+
+ + 2 + + + + +
+
+ + 3 + + + + +
+
+ + 4 + + + + +
+
+ + 5 + + + + +
+
+ + 6 + + + + +
+
Cancel diff --git a/apps/app/components/estimates/estimate-points-modal.tsx b/apps/app/components/estimates/estimate-points-modal.tsx deleted file mode 100644 index e3ce73aee..000000000 --- a/apps/app/components/estimates/estimate-points-modal.tsx +++ /dev/null @@ -1,329 +0,0 @@ -import React, { useEffect } from "react"; - -import { useRouter } from "next/router"; - -import { useForm } from "react-hook-form"; - -import { mutate } from "swr"; - -// services -import estimatesService from "services/estimates.service"; -// headless ui -import { Dialog, Transition } from "@headlessui/react"; -// hooks -import useToast from "hooks/use-toast"; -// ui -import { Input, PrimaryButton, SecondaryButton } from "components/ui"; -// types -import type { IEstimate, IEstimatePoint } from "types"; -// fetch-keys -import { ESTIMATE_POINTS_LIST } from "constants/fetch-keys"; - -type Props = { - isOpen: boolean; - data?: IEstimatePoint[]; - estimate: IEstimate | null; - onClose: () => void; -}; - -interface FormValues { - value1: string; - value2: string; - value3: string; - value4: string; - value5: string; - value6: string; -} - -const defaultValues: FormValues = { - value1: "", - value2: "", - value3: "", - value4: "", - value5: "", - value6: "", -}; - -export const EstimatePointsModal: React.FC = ({ isOpen, data, estimate, onClose }) => { - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { setToastAlert } = useToast(); - - const { - register, - formState: { isSubmitting }, - handleSubmit, - reset, - } = useForm({ defaultValues }); - - const handleClose = () => { - onClose(); - reset(); - }; - - const createEstimatePoints = async (formData: FormValues) => { - if (!workspaceSlug || !projectId) return; - - const payload = { - estimate_points: [ - { - key: 0, - value: formData.value1, - }, - { - key: 1, - value: formData.value2, - }, - { - key: 2, - value: formData.value3, - }, - { - key: 3, - value: formData.value4, - }, - { - key: 4, - value: formData.value5, - }, - { - key: 5, - value: formData.value6, - }, - ], - }; - - await estimatesService - .createEstimatePoints( - workspaceSlug as string, - projectId as string, - estimate?.id as string, - payload - ) - .then(() => { - handleClose(); - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Estimate points could not be created. Please try again.", - }); - }); - }; - - const updateEstimatePoints = async (formData: FormValues) => { - if (!workspaceSlug || !projectId || !data || data.length === 0) return; - - const payload = { - estimate_points: data.map((d, index) => ({ - id: d.id, - value: (formData as any)[`value${index + 1}`], - })), - }; - - await estimatesService - .patchEstimatePoints( - workspaceSlug as string, - projectId as string, - estimate?.id as string, - payload - ) - .then(() => { - handleClose(); - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Estimate points could not be created. Please try again.", - }); - }); - }; - - const onSubmit = async (formData: FormValues) => { - let c = 0; - - Object.keys(formData).map((key) => { - if (formData[key as keyof FormValues] === "") c++; - }); - - if (c !== 0) { - setToastAlert({ - type: "error", - title: "Error!", - message: "Please fill all the fields.", - }); - return; - } - - if (data && data.length !== 0) await updateEstimatePoints(formData); - else await createEstimatePoints(formData); - - if (estimate) mutate(ESTIMATE_POINTS_LIST(estimate.id)); - }; - - useEffect(() => { - if (!data || data.length < 6) return; - - reset({ - ...defaultValues, - value1: data[0].value, - value2: data[1].value, - value3: data[2].value, - value4: data[3].value, - value5: data[4].value, - value6: data[5].value, - }); - }, [data, reset]); - - return ( - - handleClose()}> - -
- - -
-
- - - -
-
-

- {data && data.length > 0 ? "Update" : "Create"} Estimate Points -

-
-
- - 1 - - - - -
-
- - 2 - - - - -
-
- - 3 - - - - -
-
- - 4 - - - - -
-
- - 5 - - - - -
-
- - 6 - - - - -
-
-
-
- -
- handleClose()}>Cancel - - {data && data.length > 0 - ? isSubmitting - ? "Updating Points..." - : "Update Points" - : isSubmitting - ? "Creating Points..." - : "Create Points"} - -
- -
-
-
-
-
-
- ); -}; diff --git a/apps/app/components/estimates/index.tsx b/apps/app/components/estimates/index.tsx index 40248b525..f20c74780 100644 --- a/apps/app/components/estimates/index.tsx +++ b/apps/app/components/estimates/index.tsx @@ -1,4 +1,3 @@ export * from "./create-update-estimate-modal"; export * from "./single-estimate"; -export * from "./estimate-points-modal" -export * from "./delete-estimate-modal" +export * from "./delete-estimate-modal"; diff --git a/apps/app/components/estimates/single-estimate.tsx b/apps/app/components/estimates/single-estimate.tsx index e0155675b..43dcc45b7 100644 --- a/apps/app/components/estimates/single-estimate.tsx +++ b/apps/app/components/estimates/single-estimate.tsx @@ -2,31 +2,21 @@ import React, { useState } from "react"; import { useRouter } from "next/router"; -import useSWR from "swr"; - // services -import estimatesService from "services/estimates.service"; import projectService from "services/project.service"; // hooks import useToast from "hooks/use-toast"; import useProjectDetails from "hooks/use-project-details"; // components -import { EstimatePointsModal, DeleteEstimateModal } from "components/estimates"; +import { DeleteEstimateModal } from "components/estimates"; // ui -import { CustomMenu } from "components/ui"; +import { CustomMenu, SecondaryButton } from "components/ui"; //icons -import { - PencilIcon, - TrashIcon, - SquaresPlusIcon, - ListBulletIcon, -} from "@heroicons/react/24/outline"; +import { PencilIcon, TrashIcon } from "@heroicons/react/24/outline"; // helpers import { orderArrayBy } from "helpers/array.helper"; // types import { IEstimate } from "types"; -// fetch-keys -import { ESTIMATE_POINTS_LIST } from "constants/fetch-keys"; type Props = { estimate: IEstimate; @@ -39,7 +29,6 @@ export const SingleEstimate: React.FC = ({ editEstimate, handleEstimateDelete, }) => { - const [isEstimatePointsModalOpen, setIsEstimatePointsModalOpen] = useState(false); const [isDeleteEstimateModalOpen, setIsDeleteEstimateModalOpen] = useState(false); const router = useRouter(); @@ -49,18 +38,6 @@ export const SingleEstimate: React.FC = ({ const { projectDetails, mutateProjectDetails } = useProjectDetails(); - const { data: estimatePoints } = useSWR( - workspaceSlug && projectId ? ESTIMATE_POINTS_LIST(estimate.id) : null, - workspaceSlug && projectId - ? () => - estimatesService.getEstimatesPointsList( - workspaceSlug as string, - projectId as string, - estimate.id - ) - : null - ); - const handleUseEstimate = async () => { if (!workspaceSlug || !projectId) return; @@ -87,78 +64,61 @@ export const SingleEstimate: React.FC = ({ return ( <> - setIsEstimatePointsModalOpen(false)} - data={estimatePoints ? orderArrayBy(estimatePoints, "key") : undefined} - />
-
+
-
+
{estimate.name} {projectDetails?.estimate && projectDetails?.estimate === estimate.id && ( - + In use )}
-

+

{estimate.description}

- - {projectDetails?.estimate !== estimate.id && - estimatePoints && - estimatePoints.length > 0 && ( - -
- - Use estimate -
-
- )} - setIsEstimatePointsModalOpen(true)}> -
- - - {estimatePoints && estimatePoints?.length > 0 ? "Edit points" : "Create points"} - -
-
- { - editEstimate(estimate); - }} - > -
- - Edit estimate -
-
- {projectDetails?.estimate !== estimate.id && ( +
+ {projectDetails?.estimate !== estimate.id && estimate.points.length > 0 && ( + + Use + + )} + { - setIsDeleteEstimateModalOpen(true); + editEstimate(estimate); }} >
- - Delete estimate + + Edit estimate
- )} -
+ {projectDetails?.estimate !== estimate.id && ( + { + setIsDeleteEstimateModalOpen(true); + }} + > +
+ + Delete estimate +
+
+ )} + +
- {estimatePoints && estimatePoints.length > 0 ? ( -
+ {estimate.points.length > 0 ? ( +
Estimate points ( - {estimatePoints.map((point, index) => ( -
+ {orderArrayBy(estimate.points, "key").map((point, index) => ( +
{point.value} - {index !== estimatePoints.length - 1 && ","}{" "} + {index !== estimate.points.length - 1 && ","}{" "}
))}
@@ -166,7 +126,7 @@ export const SingleEstimate: React.FC = ({
) : (
-

No estimate points

+

No estimate points

)}
diff --git a/apps/app/components/issues/select/estimate.tsx b/apps/app/components/issues/select/estimate.tsx index 813bd14f0..142a0369b 100644 --- a/apps/app/components/issues/select/estimate.tsx +++ b/apps/app/components/issues/select/estimate.tsx @@ -22,7 +22,7 @@ export const IssueEstimateSelect: React.FC = ({ value, onChange }) => { value={value} label={
- + {estimatePoints?.find((e) => e.key === value)?.value ?? "Estimate"} diff --git a/apps/app/components/issues/view-select/estimate.tsx b/apps/app/components/issues/view-select/estimate.tsx index a44c01ce0..737896c4f 100644 --- a/apps/app/components/issues/view-select/estimate.tsx +++ b/apps/app/components/issues/view-select/estimate.tsx @@ -56,7 +56,7 @@ export const ViewEstimateSelect: React.FC = ({ }} label={ -
+
{estimateValue ?? "Estimate"}
diff --git a/apps/app/components/states/create-update-state-inline.tsx b/apps/app/components/states/create-update-state-inline.tsx index 54bd85eee..3a13faf25 100644 --- a/apps/app/components/states/create-update-state-inline.tsx +++ b/apps/app/components/states/create-update-state-inline.tsx @@ -98,8 +98,7 @@ export const CreateUpdateStateInline: React.FC = ({ data, onClose, select setToastAlert({ type: "error", title: "Error!", - message: - "Another state exists with the same name. Please try again with another name.", + message: "State with that name already exists. Please try again with another name.", }); else setToastAlert({ diff --git a/apps/app/components/ui/input/index.tsx b/apps/app/components/ui/input/index.tsx index 0139dae1a..b2c93193a 100644 --- a/apps/app/components/ui/input/index.tsx +++ b/apps/app/components/ui/input/index.tsx @@ -1,7 +1,7 @@ import * as React from "react"; -// common -import { Props } from "./types"; + // types +import { Props } from "./types"; export const Input: React.FC = ({ label, @@ -21,7 +21,7 @@ export const Input: React.FC = ({ }) => ( <> {label && ( -
-
+
Cancel - + {isDeleteLoading ? "Deleting..." : "Delete"}
diff --git a/apps/app/components/states/single-state.tsx b/apps/app/components/states/single-state.tsx index 2a65a1674..a295d36ca 100644 --- a/apps/app/components/states/single-state.tsx +++ b/apps/app/components/states/single-state.tsx @@ -15,6 +15,7 @@ import { PencilSquareIcon, TrashIcon, } from "@heroicons/react/24/outline"; +import { getStateGroupIcon } from "components/icons"; // helpers import { addSpaceIfCamelCase } from "helpers/string.helper"; import { groupBy, orderArrayBy } from "helpers/array.helper"; @@ -22,8 +23,7 @@ import { orderStateGroups } from "helpers/state.helper"; // types import { IState } from "types"; // fetch-keys -import { STATE_LIST } from "constants/fetch-keys"; -import { getStateGroupIcon } from "components/icons"; +import { STATES_LIST } from "constants/fetch-keys"; type Props = { index: number; @@ -60,7 +60,7 @@ export const SingleState: React.FC = ({ newStatesList = orderArrayBy(newStatesList, "sequence", "ascending"); mutate( - STATE_LIST(projectId as string), + STATES_LIST(projectId as string), orderStateGroups(groupBy(newStatesList, "group")), false ); @@ -76,7 +76,7 @@ export const SingleState: React.FC = ({ default: true, }) .then(() => { - mutate(STATE_LIST(projectId as string)); + mutate(STATES_LIST(projectId as string)); setIsSubmitting(false); }) .catch(() => { @@ -89,7 +89,7 @@ export const SingleState: React.FC = ({ default: true, }) .then(() => { - mutate(STATE_LIST(projectId as string)); + mutate(STATES_LIST(projectId as string)); setIsSubmitting(false); }) .catch(() => { @@ -115,7 +115,7 @@ export const SingleState: React.FC = ({ newStatesList = orderArrayBy(newStatesList, "sequence", "ascending"); mutate( - STATE_LIST(projectId as string), + STATES_LIST(projectId as string), orderStateGroups(groupBy(newStatesList, "group")), false ); @@ -126,7 +126,7 @@ export const SingleState: React.FC = ({ }) .then((res) => { console.log(res); - mutate(STATE_LIST(projectId as string)); + mutate(STATES_LIST(projectId as string)); }) .catch((err) => { console.error(err); @@ -140,7 +140,7 @@ export const SingleState: React.FC = ({
{getStateGroupIcon(state.group, "20", "20", state.color)}
-
{addSpaceIfCamelCase(state.name)}
+
{addSpaceIfCamelCase(state.name)}

{state.description}

diff --git a/apps/app/components/views/select-filters.tsx b/apps/app/components/views/select-filters.tsx index 2d25983c7..1b6986346 100644 --- a/apps/app/components/views/select-filters.tsx +++ b/apps/app/components/views/select-filters.tsx @@ -15,7 +15,7 @@ import { getStatesList } from "helpers/state.helper"; // types import { IIssueFilterOptions, IQuery } from "types"; // fetch-keys -import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATE_LIST } from "constants/fetch-keys"; +import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATES_LIST } from "constants/fetch-keys"; // constants import { PRIORITIES } from "constants/project"; @@ -36,7 +36,7 @@ export const SelectFilters: React.FC = ({ const { workspaceSlug, projectId } = router.query; const { data: states } = useSWR( - workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, + workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null diff --git a/apps/app/constants/fetch-keys.ts b/apps/app/constants/fetch-keys.ts index 51491ab1e..409f9a4d5 100644 --- a/apps/app/constants/fetch-keys.ts +++ b/apps/app/constants/fetch-keys.ts @@ -89,8 +89,8 @@ export const CYCLE_DRAFT_LIST = (projectId: string) => export const CYCLE_COMPLETE_LIST = (projectId: string) => `CYCLE_COMPLETE_LIST_${projectId.toUpperCase()}`; -export const STATE_LIST = (projectId: string) => `STATE_LIST_${projectId.toUpperCase()}`; -export const STATE_DETAIL = "STATE_DETAILS"; +export const STATES_LIST = (projectId: string) => `STATES_LIST_${projectId.toUpperCase()}`; +export const STATE_DETAILS = "STATE_DETAILS"; export const USER_ISSUE = (workspaceSlug: string) => `USER_ISSUE_${workspaceSlug.toUpperCase()}`; export const USER_ACTIVITY = "USER_ACTIVITY"; diff --git a/apps/app/helpers/state.helper.ts b/apps/app/helpers/state.helper.ts index 7229957d0..512da4f19 100644 --- a/apps/app/helpers/state.helper.ts +++ b/apps/app/helpers/state.helper.ts @@ -1,7 +1,7 @@ // types -import { IState, StateResponse } from "types"; +import { IState, IStateResponse } from "types"; -export const orderStateGroups = (unorderedStateGroups: StateResponse) => +export const orderStateGroups = (unorderedStateGroups: IStateResponse) => Object.assign( { backlog: [], unstarted: [], started: [], completed: [], cancelled: [] }, unorderedStateGroups diff --git a/apps/app/hooks/use-issues-view.tsx b/apps/app/hooks/use-issues-view.tsx index cb3ddb43e..57b584db4 100644 --- a/apps/app/hooks/use-issues-view.tsx +++ b/apps/app/hooks/use-issues-view.tsx @@ -20,7 +20,7 @@ import { CYCLE_ISSUES_WITH_PARAMS, MODULE_ISSUES_WITH_PARAMS, PROJECT_ISSUES_LIST_WITH_PARAMS, - STATE_LIST, + STATES_LIST, } from "constants/fetch-keys"; const useIssuesView = () => { @@ -100,7 +100,7 @@ const useIssuesView = () => { ); const { data: states } = useSWR( - workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, + workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null diff --git a/apps/app/hooks/use-my-issues-filter.tsx b/apps/app/hooks/use-my-issues-filter.tsx index 9d26a7a60..bd97427f5 100644 --- a/apps/app/hooks/use-my-issues-filter.tsx +++ b/apps/app/hooks/use-my-issues-filter.tsx @@ -12,7 +12,7 @@ import { getStatesList } from "helpers/state.helper"; // types import { Properties, NestedKeyOf, IIssue } from "types"; // fetch-keys -import { STATE_LIST } from "constants/fetch-keys"; +import { STATES_LIST } from "constants/fetch-keys"; // constants import { PRIORITIES } from "constants/project"; @@ -41,7 +41,7 @@ const useMyIssuesProperties = (issues?: IIssue[]) => { const { user } = useUser(); const { data: stateGroups } = useSWR( - workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, + workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/states.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/states.tsx index a91c1c6cb..8b513d640 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/states.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/states.tsx @@ -6,7 +6,8 @@ import useSWR from "swr"; // services import stateService from "services/state.service"; -import projectService from "services/project.service"; +// hooks +import useProjectDetails from "hooks/use-project-details"; // layouts import { ProjectAuthorizationWrapper } from "layouts/auth-layout"; // components @@ -26,7 +27,7 @@ import { getStatesList, orderStateGroups } from "helpers/state.helper"; // types import type { NextPage } from "next"; // fetch-keys -import { PROJECT_DETAILS, STATE_LIST } from "constants/fetch-keys"; +import { STATES_LIST } from "constants/fetch-keys"; const StatesSettings: NextPage = () => { const [activeGroup, setActiveGroup] = useState(null); @@ -36,15 +37,10 @@ const StatesSettings: NextPage = () => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; - const { data: projectDetails } = useSWR( - workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null, - workspaceSlug && projectId - ? () => projectService.getProject(workspaceSlug as string, projectId as string) - : null - ); + const { projectDetails } = useProjectDetails(); const { data: states } = useSWR( - workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, + workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, workspaceSlug && projectId ? () => stateService.getStates(workspaceSlug as string, projectId as string) : null diff --git a/apps/app/services/state.service.ts b/apps/app/services/state.service.ts index 69f30a3dc..a9d0e4cdb 100644 --- a/apps/app/services/state.service.ts +++ b/apps/app/services/state.service.ts @@ -8,7 +8,7 @@ const trackEvent = process.env.NEXT_PUBLIC_TRACK_EVENTS === "true" || process.env.NEXT_PUBLIC_TRACK_EVENTS === "1"; // types -import type { IState, StateResponse } from "types"; +import type { IState, IStateResponse } from "types"; class ProjectStateServices extends APIService { constructor() { @@ -26,7 +26,7 @@ class ProjectStateServices extends APIService { }); } - async getStates(workspaceSlug: string, projectId: string): Promise { + async getStates(workspaceSlug: string, projectId: string): Promise { return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/states/`) .then((response) => response?.data) .catch((error) => { @@ -96,7 +96,7 @@ class ProjectStateServices extends APIService { return response?.data; }) .catch((error) => { - throw error?.response?.data; + throw error?.response; }); } } diff --git a/apps/app/types/state.d.ts b/apps/app/types/state.d.ts index c0d9514c2..c3d69e90b 100644 --- a/apps/app/types/state.d.ts +++ b/apps/app/types/state.d.ts @@ -19,6 +19,6 @@ export interface IState { workspace_detail: IWorkspaceLite; } -export interface StateResponse { +export interface IStateResponse { [key: string]: IState[]; } From d99f669b89937e5757d399f41420cb2239d6cc31 Mon Sep 17 00:00:00 2001 From: pablohashescobar <118773738+pablohashescobar@users.noreply.github.com> Date: Sat, 22 Apr 2023 18:21:22 +0530 Subject: [PATCH 14/64] dev: add s3 url for staging (#934) --- apiserver/plane/settings/staging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/plane/settings/staging.py b/apiserver/plane/settings/staging.py index b43327c09..d4d0e5e12 100644 --- a/apiserver/plane/settings/staging.py +++ b/apiserver/plane/settings/staging.py @@ -80,7 +80,7 @@ AWS_S3_BUCKET_NAME = os.environ.get("AWS_S3_BUCKET_NAME") AWS_S3_ADDRESSING_STYLE = "auto" # The full URL to the S3 endpoint. Leave blank to use the default region URL. -AWS_S3_ENDPOINT_URL = "" +AWS_S3_ENDPOINT_URL = os.environ.get("AWS_S3_ENDPOINT_URL", "") # A prefix to be applied to every stored file. This will be joined to every filename using the "/" separator. AWS_S3_KEY_PREFIX = "" From c80094581e85708ab36e604326e9e440759cb982 Mon Sep 17 00:00:00 2001 From: Kunal Vishwakarma <116634168+kunalv17@users.noreply.github.com> Date: Sat, 22 Apr 2023 21:54:50 +0530 Subject: [PATCH 15/64] feat: frontend slack integration (#932) * feat: slack integration frontend * feat: slack integration frontend complete * Co-authored-by: Aaryan Khandelwal --- apps/app/.env.example | 1 + apps/app/components/integration/index.ts | 2 + .../integration/single-integration-card.tsx | 8 +- .../app/components/integration/slack/index.ts | 1 + .../integration/slack/select-channel.tsx | 105 ++++++++++++++++++ .../project/single-integration-card.tsx | 64 ++++++++--- apps/app/constants/fetch-keys.ts | 4 + apps/app/hooks/use-integration-popup.tsx | 15 ++- apps/app/pages/api/slack-redirect.ts | 23 ++++ .../pages/installations/[provider]/index.tsx | 66 ++++++++++- .../app/services/app-installations.service.ts | 46 +++++++- turbo.json | 4 +- 12 files changed, 308 insertions(+), 31 deletions(-) create mode 100644 apps/app/components/integration/slack/index.ts create mode 100644 apps/app/components/integration/slack/select-channel.tsx create mode 100644 apps/app/pages/api/slack-redirect.ts diff --git a/apps/app/.env.example b/apps/app/.env.example index cdf30fc72..9e41ba88d 100644 --- a/apps/app/.env.example +++ b/apps/app/.env.example @@ -9,3 +9,4 @@ NEXT_PUBLIC_ENABLE_OAUTH=0 NEXT_PUBLIC_ENABLE_SENTRY=0 NEXT_PUBLIC_ENABLE_SESSION_RECORDER=0 NEXT_PUBLIC_TRACK_EVENTS=0 +NEXT_PUBLIC_SLACK_CLIENT_ID="" diff --git a/apps/app/components/integration/index.ts b/apps/app/components/integration/index.ts index 39ae51e33..a734113b6 100644 --- a/apps/app/components/integration/index.ts +++ b/apps/app/components/integration/index.ts @@ -8,3 +8,5 @@ export * from "./single-integration-card"; export * from "./github"; // jira export * from "./jira"; +// slack +export * from "./slack"; diff --git a/apps/app/components/integration/single-integration-card.tsx b/apps/app/components/integration/single-integration-card.tsx index 0f1819bb9..d6ac13bc8 100644 --- a/apps/app/components/integration/single-integration-card.tsx +++ b/apps/app/components/integration/single-integration-card.tsx @@ -11,7 +11,7 @@ import IntegrationService from "services/integration"; import useToast from "hooks/use-toast"; import useIntegrationPopup from "hooks/use-integration-popup"; // ui -import { DangerButton, Loader, SecondaryButton } from "components/ui"; +import { DangerButton, Loader, PrimaryButton } from "components/ui"; // icons import GithubLogo from "public/services/github.png"; import SlackLogo from "public/services/slack.png"; @@ -33,7 +33,7 @@ const integrationDetails: { [key: string]: any } = { }, slack: { logo: SlackLogo, - installed: "Activate Slack integrations on individual projects to sync with specific cahnnels.", + installed: "Activate Slack integrations on individual projects to sync with specific channels.", notInstalled: "Connect with Slack with your Plane workspace to sync project issues.", }, }; @@ -139,9 +139,9 @@ export const SingleIntegrationCard: React.FC = ({ integration }) => { {deletingIntegration ? "Removing..." : "Remove installation"} ) : ( - + {isInstalling ? "Installing..." : "Add installation"} - + ) ) : ( diff --git a/apps/app/components/integration/slack/index.ts b/apps/app/components/integration/slack/index.ts new file mode 100644 index 000000000..3bd1c965c --- /dev/null +++ b/apps/app/components/integration/slack/index.ts @@ -0,0 +1 @@ +export * from "./select-channel"; \ No newline at end of file diff --git a/apps/app/components/integration/slack/select-channel.tsx b/apps/app/components/integration/slack/select-channel.tsx new file mode 100644 index 000000000..621245dc8 --- /dev/null +++ b/apps/app/components/integration/slack/select-channel.tsx @@ -0,0 +1,105 @@ +import React, { useState, useEffect } from "react"; + +import { useRouter } from "next/router"; + +import useSWR, { mutate } from "swr"; +// services +import appinstallationsService from "services/app-installations.service"; + +// ui +import { Loader } from "components/ui"; +// hooks +import useToast from "hooks/use-toast"; +import useIntegrationPopup from "hooks/use-integration-popup"; +// types +import { IWorkspaceIntegration } from "types"; +// fetch-keys +import { SLACK_CHANNEL_INFO } from "constants/fetch-keys"; + +type Props = { + integration: IWorkspaceIntegration; +}; + +export const SelectChannel: React.FC = ({ integration }) => { + const [deletingProjectSync, setDeletingProjectSync] = useState(false); + + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { startAuth } = useIntegrationPopup("slackChannel", integration.id); + + const { data: projectIntegration } = useSWR( + workspaceSlug && projectId && integration.id + ? SLACK_CHANNEL_INFO(workspaceSlug as string, projectId as string) + : null, + () => + workspaceSlug && projectId && integration.id + ? appinstallationsService.getSlackChannelDetail( + workspaceSlug as string, + projectId as string, + integration.id as string + ) + : null + ); + + useEffect(() => { + if (projectIntegration?.length > 0) { + setDeletingProjectSync(true); + } + if (projectIntegration?.length === 0) { + setDeletingProjectSync(false); + } + }, [projectIntegration]); + + const handleDelete = async () => { + if (projectIntegration.length === 0) return; + mutate(SLACK_CHANNEL_INFO, (prevData: any) => { + if (!prevData) return; + return prevData.id !== integration.id; + }).then(() => setDeletingProjectSync(false)); + appinstallationsService + .removeSlackChannel( + workspaceSlug as string, + projectId as string, + integration.id as string, + projectIntegration?.[0]?.id + ) + .catch((err) => console.log(err)); + }; + + const handleAuth = async () => { + await startAuth(); + setDeletingProjectSync(true); + }; + + return ( + <> + {projectIntegration ? ( + + ) : ( + + + + )} + + ); +}; diff --git a/apps/app/components/project/single-integration-card.tsx b/apps/app/components/project/single-integration-card.tsx index 20a84b0b4..4a329fa93 100644 --- a/apps/app/components/project/single-integration-card.tsx +++ b/apps/app/components/project/single-integration-card.tsx @@ -10,18 +10,34 @@ import projectService from "services/project.service"; import { useRouter } from "next/router"; import useToast from "hooks/use-toast"; // components -import { SelectRepository } from "components/integration"; +import { SelectRepository, SelectChannel } from "components/integration"; // icons import GithubLogo from "public/logos/github-square.png"; +import SlackLogo from "public/services/slack.png"; // types import { IWorkspaceIntegration } from "types"; // fetch-keys import { PROJECT_GITHUB_REPOSITORY } from "constants/fetch-keys"; +import { comboMatches } from "@blueprintjs/core"; type Props = { integration: IWorkspaceIntegration; }; +const integrationDetails: { [key: string]: any } = { + github: { + logo: GithubLogo, + installed: + "Activate GitHub integrations on individual projects to sync with specific repositories.", + notInstalled: "Connect with GitHub with your Plane workspace to sync project issues.", + }, + slack: { + logo: SlackLogo, + installed: "Activate Slack integrations on individual projects to sync with specific cahnnels.", + notInstalled: "Connect with Slack with your Plane workspace to sync project issues.", + }, +}; + export const SingleIntegration: React.FC = ({ integration }) => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -83,29 +99,43 @@ export const SingleIntegration: React.FC = ({ integration }) => {
- GithubLogo + GithubLogo

{integration.integration_detail.title}

-

Select GitHub repository to enable sync.

+

+ {integration.integration_detail.provider === "github" + ? "Select GitHub repository to enable sync." + : integration.integration_detail.provider === "slack" + ? "Connect your slack channel to this project to get regular updates. Control which notification you want to receive" + : null} +

- 0 - ? `${syncedGithubRepository[0].repo_detail.owner}/${syncedGithubRepository[0].repo_detail.name}` - : null - } - label={ - syncedGithubRepository && syncedGithubRepository.length > 0 - ? `${syncedGithubRepository[0].repo_detail.owner}/${syncedGithubRepository[0].repo_detail.name}` - : "Select Repository" - } - onChange={handleChange} - /> + {integration.integration_detail.provider === "github" && ( + 0 + ? `${syncedGithubRepository[0].repo_detail.owner}/${syncedGithubRepository[0].repo_detail.name}` + : null + } + label={ + syncedGithubRepository && syncedGithubRepository.length > 0 + ? `${syncedGithubRepository[0].repo_detail.owner}/${syncedGithubRepository[0].repo_detail.name}` + : "Select Repository" + } + onChange={handleChange} + /> + )} + {integration.integration_detail.provider === "slack" && ( + + )}
)} diff --git a/apps/app/constants/fetch-keys.ts b/apps/app/constants/fetch-keys.ts index 409f9a4d5..a4773e233 100644 --- a/apps/app/constants/fetch-keys.ts +++ b/apps/app/constants/fetch-keys.ts @@ -138,6 +138,10 @@ export const IMPORTER_SERVICES_LIST = (workspaceSlug: string) => export const GITHUB_REPOSITORY_INFO = (workspaceSlug: string, repoName: string) => `GITHUB_REPO_INFO_${workspaceSlug.toString().toUpperCase()}_${repoName.toUpperCase()}`; +// slack-project-integration +export const SLACK_CHANNEL_INFO = (workspaceSlug: string, projectId: string) => + `SLACK_CHANNEL_INFO_${workspaceSlug.toString().toUpperCase()}_${projectId.toUpperCase()}`; + // Calendar export const PROJECT_CALENDAR_ISSUES = (projectId: string) => `CALENDAR_ISSUES_${projectId.toUpperCase()}`; diff --git a/apps/app/hooks/use-integration-popup.tsx b/apps/app/hooks/use-integration-popup.tsx index 03137a195..cd1ae6c0a 100644 --- a/apps/app/hooks/use-integration-popup.tsx +++ b/apps/app/hooks/use-integration-popup.tsx @@ -2,17 +2,24 @@ import { useRef, useState } from "react"; import { useRouter } from "next/router"; -const useIntegrationPopup = (provider: string | undefined) => { +const useIntegrationPopup = (provider: string | undefined, stateParams?: string) => { const [authLoader, setAuthLoader] = useState(false); const router = useRouter(); - const { workspaceSlug } = router.query; + const { workspaceSlug, projectId } = router.query; const providerUrls: { [key: string]: string } = { github: `https://github.com/apps/${ process.env.NEXT_PUBLIC_GITHUB_APP_NAME - }/installations/new?state=${workspaceSlug as string}`, - slack: "", + }/installations/new?state=${workspaceSlug?.toString()}`, + slack: `https://slack.com/oauth/v2/authorize?scope=chat%3Awrite%2Cim%3Ahistory%2Cim%3Awrite%2Clinks%3Aread%2Clinks%3Awrite%2Cusers%3Aread%2Cusers%3Aread.email&user_scope=&&client_id=${ + process.env.NEXT_PUBLIC_SLACK_CLIENT_ID + }&state=${workspaceSlug?.toString()}`, + slackChannel: `https://slack.com/oauth/v2/authorize?scope=incoming-webhook&client_id=${ + process.env.NEXT_PUBLIC_SLACK_CLIENT_ID + }&state=${workspaceSlug?.toString()},${projectId?.toString()}${ + stateParams ? "," + stateParams : "" + }`, }; const popup = useRef(); diff --git a/apps/app/pages/api/slack-redirect.ts b/apps/app/pages/api/slack-redirect.ts new file mode 100644 index 000000000..e4a220bd5 --- /dev/null +++ b/apps/app/pages/api/slack-redirect.ts @@ -0,0 +1,23 @@ +// pages/api/slack/authorize.js +import axios from "axios"; +import { NextApiRequest, NextApiResponse } from "next"; + +export default async function handleSlackAuthorize(req: NextApiRequest, res: NextApiResponse) { + const { code } = req.body; + + if (!code || code === "") return res.status(400).json({ message: "Code is required" }); + + const response = await axios({ + method: "post", + url: "https://slack.com/api/oauth.v2.access", + params: { + client_id: process.env.NEXT_PUBLIC_SLACK_CLIENT_ID, + client_secret: process.env.NEXT_PUBLIC_SLACK_CLIENT_SECRET, + code, + }, + }); + + // if (response?.data?.ok) + res.status(200).json(response.data); + // else res.status(404).json(response.data); +} diff --git a/apps/app/pages/installations/[provider]/index.tsx b/apps/app/pages/installations/[provider]/index.tsx index 31eb53c9e..70b1a02c2 100644 --- a/apps/app/pages/installations/[provider]/index.tsx +++ b/apps/app/pages/installations/[provider]/index.tsx @@ -2,14 +2,20 @@ import React, { useEffect } from "react"; // services import appinstallationsService from "services/app-installations.service"; + +import useToast from "hooks/use-toast"; + // components import { Spinner } from "components/ui"; +import { useRouter } from "next/router"; + interface IGithuPostInstallationProps { installation_id: string; setup_action: string; state: string; provider: string; + code: string; } // TODO:Change getServerSideProps to router.query @@ -18,12 +24,16 @@ const AppPostInstallation = ({ setup_action, state, provider, + code, }: IGithuPostInstallationProps) => { + + const { setToastAlert } = useToast(); + useEffect(() => { - if (state && installation_id) { + if (provider === "github" && state && installation_id) { appinstallationsService - .addGithubApp(state, provider, { installation_id }) - .then((res) => { + .addInstallationApp(state, provider, { installation_id }) + .then(() => { window.opener = null; window.open("", "_self"); window.close(); @@ -31,8 +41,56 @@ const AppPostInstallation = ({ .catch((err) => { console.log(err); }); + } else if (provider === "slack" && state && code) { + appinstallationsService + .getSlackAuthDetails(code) + .then((res) => { + const [workspaceSlug, projectId, integrationId] = state.split(","); + + if(!projectId) { + const payload = { + metadata: { + ...res, + }, + }; + + appinstallationsService + .addInstallationApp(state, provider, payload) + .then((r) => { + window.opener = null; + window.open("", "_self"); + window.close(); + }) + .catch((err) => { + throw err?.response; + }); + } else { + const payload = { + access_token: res.access_token, + bot_user_id: res.bot_user_id, + webhook_url: res.incoming_webhook.url, + data: res, + team_id: res.team.id, + team_name: res.team.name, + scopes: res.scope, + }; + appinstallationsService + .addSlackChannel(workspaceSlug, projectId, integrationId, payload) + .then((r) => { + window.opener = null; + window.open("", "_self"); + window.close(); + }) + .catch((err) => { + throw err.response + }) + } + }) + .catch((err) => { + console.log(err); + }); } - }, [state, installation_id, provider]); + }, [state, installation_id, provider, code]); return (
diff --git a/apps/app/services/app-installations.service.ts b/apps/app/services/app-installations.service.ts index 3ceae3b1a..8ee04dcfe 100644 --- a/apps/app/services/app-installations.service.ts +++ b/apps/app/services/app-installations.service.ts @@ -1,4 +1,5 @@ // services +import axios from "axios"; import APIService from "services/api.service"; const { NEXT_PUBLIC_API_BASE_URL } = process.env; @@ -8,13 +9,56 @@ class AppInstallationsService extends APIService { super(NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000"); } - async addGithubApp(workspaceSlug: string, provider: string, data: any): Promise { + async addInstallationApp(workspaceSlug: string, provider: string, data: any): Promise { return this.post(`/api/workspaces/${workspaceSlug}/workspace-integrations/${provider}/`, data) .then((response) => response?.data) .catch((error) => { throw error?.response; }); } + + async addSlackChannel(workspaceSlug: string, projectId: string, integrationId: string | null | undefined, data: any): Promise { + return this.post( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/workspace-integrations/${integrationId}/project-slack-sync/`, + data + ) + .then((response) => response?.data) + .catch((error) => { + throw error?.response; + }); + } + + async getSlackChannelDetail(workspaceSlug: string, projectId: string, integrationId: string | null | undefined): Promise { + return this.get( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/workspace-integrations/${integrationId}/project-slack-sync/` + ) + .then((response) => response?.data) + .catch((error) => { + throw error?.response; + }); + } + + async removeSlackChannel(workspaceSlug: string, projectId: string, integrationId: string | null | undefined, slackSyncId: string | undefined): Promise { + return this.delete( + `/api/workspaces/${workspaceSlug}/projects/${projectId}/workspace-integrations/${integrationId}/project-slack-sync/${slackSyncId}` + ) + .then((response) => response?.data) + .catch((error) => { + throw error?.response; + }); + } + + async getSlackAuthDetails(code: string): Promise { + const response = await axios({ + method: "post", + url: "/api/slack-redirect", + data: { + code, + }, + }); + + return response.data; + } } export default new AppInstallationsService(); diff --git a/turbo.json b/turbo.json index fbf86f566..12476ceb9 100644 --- a/turbo.json +++ b/turbo.json @@ -16,7 +16,9 @@ "TRACKER_ACCESS_KEY", "NEXT_PUBLIC_CRISP_ID", "NEXT_PUBLIC_ENABLE_SESSION_RECORDER", - "NEXT_PUBLIC_SESSION_RECORDER_KEY" + "NEXT_PUBLIC_SESSION_RECORDER_KEY", + "NEXT_PUBLIC_SLACK_CLIENT_ID", + "NEXT_PUBLIC_SLACK_CLIENT_SECRET" ], "pipeline": { "build": { From 169a60723bee0f8c4d02e33bf0c7548995b8c3b9 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Sat, 22 Apr 2023 23:46:19 +0530 Subject: [PATCH 16/64] style: project settings theming (#936) * style: project and workspace members theming * style: project features theming * style: project settings states theming * style: project settings labels theming * style: project settings integrations theming --- .../labels/create-update-label-inline.tsx | 20 +- .../components/labels/single-label-group.tsx | 10 +- apps/app/components/labels/single-label.tsx | 6 +- .../project/single-integration-card.tsx | 29 +- .../states/create-update-state-inline.tsx | 4 +- apps/app/components/states/single-state.tsx | 24 +- .../[projectId]/settings/features.tsx | 255 +++++++----------- .../projects/[projectId]/settings/members.tsx | 38 +-- .../projects/[projectId]/settings/states.tsx | 5 +- .../[workspaceSlug]/settings/members.tsx | 103 ++++--- apps/app/services/project.service.ts | 2 +- apps/app/services/track-event.service.ts | 2 +- apps/app/styles/globals.css | 7 +- 13 files changed, 211 insertions(+), 294 deletions(-) diff --git a/apps/app/components/labels/create-update-label-inline.tsx b/apps/app/components/labels/create-update-label-inline.tsx index 70c87bc1c..6fe2d2495 100644 --- a/apps/app/components/labels/create-update-label-inline.tsx +++ b/apps/app/components/labels/create-update-label-inline.tsx @@ -108,28 +108,26 @@ export const CreateUpdateLabelInline = forwardRef(function CreateUpd return (
- + {({ open }) => ( <> - {watch("color") && watch("color") !== "" && ( - - )} + = ({ }; return ( - + {({ open }) => ( <>
@@ -74,7 +78,7 @@ export const SingleLabelGroup: React.FC = ({ -
{label.name}
+
{label.name}
@@ -122,7 +126,7 @@ export const SingleLabelGroup: React.FC = ({ key={child.id} className="group flex items-center justify-between rounded-md border border-brand-base p-2 text-sm" > -
+
= ({ editLabel, handleLabelDelete, }) => ( -
+
= ({ backgroundColor: label.color && label.color !== "" ? label.color : "#000", }} /> -
{label.name}
+
{label.name}
addLabelToGroup(label)}> diff --git a/apps/app/components/project/single-integration-card.tsx b/apps/app/components/project/single-integration-card.tsx index 4a329fa93..584c65fe8 100644 --- a/apps/app/components/project/single-integration-card.tsx +++ b/apps/app/components/project/single-integration-card.tsx @@ -18,7 +18,6 @@ import SlackLogo from "public/services/slack.png"; import { IWorkspaceIntegration } from "types"; // fetch-keys import { PROJECT_GITHUB_REPOSITORY } from "constants/fetch-keys"; -import { comboMatches } from "@blueprintjs/core"; type Props = { integration: IWorkspaceIntegration; @@ -27,14 +26,12 @@ type Props = { const integrationDetails: { [key: string]: any } = { github: { logo: GithubLogo, - installed: - "Activate GitHub integrations on individual projects to sync with specific repositories.", - notInstalled: "Connect with GitHub with your Plane workspace to sync project issues.", + description: "Select GitHub repository to enable sync.", }, slack: { logo: SlackLogo, - installed: "Activate Slack integrations on individual projects to sync with specific cahnnels.", - notInstalled: "Connect with Slack with your Plane workspace to sync project issues.", + description: + "Connect your slack channel to this project to get regular updates. Control which notification you want to receive.", }, }; @@ -67,19 +64,19 @@ export const SingleIntegration: React.FC = ({ integration }) => { } = repo; projectService - .syncGiuthubRepository(workspaceSlug as string, projectId as string, integration.id, { + .syncGithubRepository(workspaceSlug as string, projectId as string, integration.id, { name, owner: login, repository_id: id, url: html_url, }) - .then((res) => { + .then(() => { mutate(PROJECT_GITHUB_REPOSITORY(projectId as string)); setToastAlert({ type: "success", title: "Success!", - message: `${login}/${name} respository synced with the project successfully.`, + message: `${login}/${name} repository synced with the project successfully.`, }); }) .catch((err) => { @@ -88,7 +85,7 @@ export const SingleIntegration: React.FC = ({ integration }) => { setToastAlert({ type: "error", title: "Error!", - message: "Respository could not be synced with the project. Please try again.", + message: "Repository could not be synced with the project. Please try again.", }); }); }; @@ -96,24 +93,20 @@ export const SingleIntegration: React.FC = ({ integration }) => { return ( <> {integration && ( -
+
GithubLogo

{integration.integration_detail.title}

-

- {integration.integration_detail.provider === "github" - ? "Select GitHub repository to enable sync." - : integration.integration_detail.provider === "slack" - ? "Connect your slack channel to this project to get regular updates. Control which notification you want to receive" - : null} +

+ {integrationDetails[integration.integration_detail.provider].description}

diff --git a/apps/app/components/states/create-update-state-inline.tsx b/apps/app/components/states/create-update-state-inline.tsx index 36f780bd1..42ab52945 100644 --- a/apps/app/components/states/create-update-state-inline.tsx +++ b/apps/app/components/states/create-update-state-inline.tsx @@ -155,7 +155,7 @@ export const CreateUpdateStateInline: React.FC = ({ data, onClose, select return (
@@ -163,7 +163,7 @@ export const CreateUpdateStateInline: React.FC = ({ data, onClose, select <> {watch("color") && watch("color") !== "" && ( diff --git a/apps/app/components/states/single-state.tsx b/apps/app/components/states/single-state.tsx index a295d36ca..d9425c524 100644 --- a/apps/app/components/states/single-state.tsx +++ b/apps/app/components/states/single-state.tsx @@ -134,21 +134,19 @@ export const SingleState: React.FC = ({ }; return ( -
+
{getStateGroupIcon(state.group, "20", "20", state.color)}
-
{addSpaceIfCamelCase(state.name)}
-

{state.description}

+
{addSpaceIfCamelCase(state.name)}
+

{state.description}

{index !== 0 && ( )} {state.default ? ( - Default + Default ) : (
diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx index 4e48e2916..150d28b4a 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx @@ -6,7 +6,7 @@ import useSWR, { mutate } from "swr"; // services import projectService from "services/project.service"; -import trackEventServices from "services/track-event.service"; +import trackEventServices, { MiscellaneousEventType } from "services/track-event.service"; // layouts import { ProjectAuthorizationWrapper } from "layouts/auth-layout"; // hooks @@ -23,6 +23,52 @@ import type { NextPage } from "next"; // fetch-keys import { PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys"; +const featuresList = [ + { + title: "Cycles", + description: + "Cycles are enabled for all the projects in this workspace. Access them from the sidebar.", + icon: , + property: "cycle_view", + }, + { + title: "Modules", + description: + "Modules are enabled for all the projects in this workspace. Access it from the sidebar.", + icon: , + property: "module_view", + }, + { + title: "Views", + description: + "Views are enabled for all the projects in this workspace. Access it from the sidebar.", + icon: , + property: "issue_views_view", + }, + { + title: "Pages", + description: + "Pages are enabled for all the projects in this workspace. Access it from the sidebar.", + icon: , + property: "page_view", + }, +]; + +const getEventType = (feature: string, toggle: boolean): MiscellaneousEventType => { + switch (feature) { + case "Cycles": + return toggle ? "TOGGLE_CYCLE_ON" : "TOGGLE_CYCLE_OFF"; + case "Modules": + return toggle ? "TOGGLE_MODULE_ON" : "TOGGLE_MODULE_OFF"; + case "Views": + return toggle ? "TOGGLE_VIEW_ON" : "TOGGLE_VIEW_OFF"; + case "Pages": + return toggle ? "TOGGLE_PAGES_ON" : "TOGGLE_PAGES_OFF"; + default: + return toggle ? "TOGGLE_PAGES_ON" : "TOGGLE_PAGES_OFF"; + } +}; + const FeaturesSettings: NextPage = () => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -91,170 +137,57 @@ const FeaturesSettings: NextPage = () => {

Features

-
-
- -
-

Cycles

-

- Cycles are enabled for all the projects in this workspace. Access them from the - navigation bar. -

-
-
- -
-
-
- -
-

Modules

-

- Modules are enabled for all the projects in this workspace. Access it from the - navigation bar. -

+
+ {feature.icon} +
+

{feature.title}

+

{feature.description}

+
-
- -
-
-
- -
-

Views

-

- Views are enabled for all the projects in this workspace. Access it from the - navigation bar. -

-
+ role="switch" + aria-checked={projectDetails?.[feature.property as keyof IProject]} + onClick={() => { + trackEventServices.trackMiscellaneousEvent( + { + workspaceId: (projectDetails?.workspace as any)?.id, + workspaceSlug, + projectId, + projectIdentifier: projectDetails?.identifier, + projectName: projectDetails?.name, + }, + !projectDetails?.[feature.property as keyof IProject] + ? getEventType(feature.title, true) + : getEventType(feature.title, false) + ); + handleSubmit({ + [feature.property]: !projectDetails?.[feature.property as keyof IProject], + }); + }} + > + Use {feature.title} +
- -
-
-
- -
-

Pages

-

- Pages are enabled for all the projects in this workspace. Access them from the - navigation bar. -

-
-
- -
+ ))}
diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx index 06efddf27..d7fb1d824 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/members.tsx @@ -10,6 +10,7 @@ import projectService from "services/project.service"; import workspaceService from "services/workspace.service"; // hooks import useToast from "hooks/use-toast"; +import useProjectDetails from "hooks/use-project-details"; // layouts import { ProjectAuthorizationWrapper } from "layouts/auth-layout"; // components @@ -23,12 +24,7 @@ import { PlusIcon, XMarkIcon } from "@heroicons/react/24/outline"; // types import type { NextPage } from "next"; // fetch-keys -import { - PROJECT_DETAILS, - PROJECT_INVITATIONS, - PROJECT_MEMBERS, - WORKSPACE_DETAILS, -} from "constants/fetch-keys"; +import { PROJECT_INVITATIONS, PROJECT_MEMBERS, WORKSPACE_DETAILS } from "constants/fetch-keys"; // constants import { ROLE } from "constants/workspace"; @@ -48,24 +44,13 @@ const MembersSettings: NextPage = () => { () => (workspaceSlug ? workspaceService.getWorkspace(workspaceSlug as string) : 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 { projectDetails } = useProjectDetails(); const { data: projectMembers, mutate: mutateMembers } = useSWR( workspaceSlug && projectId ? PROJECT_MEMBERS(projectId as string) : null, workspaceSlug && projectId ? () => projectService.projectMembers(workspaceSlug as string, projectId as string) - : null, - { - onErrorRetry(err, _, __, revalidate, revalidateOpts) { - if (err?.status === 403) return; - setTimeout(() => revalidate(revalidateOpts), 5000); - }, - } + : null ); const { data: projectInvitations, mutate: mutateInvitations } = useSWR( @@ -176,11 +161,11 @@ const MembersSettings: NextPage = () => { ) : ( -
+
{members.length > 0 ? members.map((member) => (
-
+
{member.avatar && member.avatar !== "" ? ( {

{member.email}

- {!member.member && ( - - Request Pending - - )}
+ {!member.member && ( +
+ Pending +
+ )} { console.log(err); }); }} + position="right" > {Object.keys(ROLE).map((key) => ( diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/states.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/states.tsx index 8b513d640..b8040c473 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/states.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/states.tsx @@ -110,7 +110,10 @@ const StatesSettings: NextPage = () => { handleDeleteState={() => setSelectDeleteState(state.id)} /> ) : ( -
+
{ setActiveGroup(null); diff --git a/apps/app/pages/[workspaceSlug]/settings/members.tsx b/apps/app/pages/[workspaceSlug]/settings/members.tsx index f993c2ff9..98d4eaaf2 100644 --- a/apps/app/pages/[workspaceSlug]/settings/members.tsx +++ b/apps/app/pages/[workspaceSlug]/settings/members.tsx @@ -157,7 +157,7 @@ const MembersSettings: NextPage = () => { ) : ( -
+
{members.length > 0 ? members.map((member) => (
@@ -184,64 +184,63 @@ const MembersSettings: NextPage = () => {

{member.email}

-
+
{!member?.status && ( -
+

Pending

)} -
- { - workspaceService - .updateWorkspaceMember(activeWorkspace?.slug as string, member.id, { - role: value, - }) - .then(() => { - mutateMembers( - (prevData) => - prevData?.map((m) => - m.id === member.id ? { ...m, role: value } : m - ), - false - ); - setToastAlert({ - title: "Success", - type: "success", - message: "Member role updated successfully.", - }); - }) - .catch(() => { - setToastAlert({ - title: "Error", - type: "error", - message: "An error occurred while updating member role.", - }); + { + workspaceService + .updateWorkspaceMember(activeWorkspace?.slug as string, member.id, { + role: value, + }) + .then(() => { + mutateMembers( + (prevData) => + prevData?.map((m) => + m.id === member.id ? { ...m, role: value } : m + ), + false + ); + setToastAlert({ + title: "Success", + type: "success", + message: "Member role updated successfully.", }); + }) + .catch(() => { + setToastAlert({ + title: "Error", + type: "error", + message: "An error occurred while updating member role.", + }); + }); + }} + position="right" + > + {Object.keys(ROLE).map((key) => ( + + <>{ROLE[parseInt(key) as keyof typeof ROLE]} + + ))} + + + { + if (member.member) { + setSelectedRemoveMember(member.id); + } else { + setSelectedInviteRemoveMember(member.id); + } }} > - {Object.keys(ROLE).map((key) => ( - - <>{ROLE[parseInt(key) as keyof typeof ROLE]} - - ))} - - - { - if (member.member) { - setSelectedRemoveMember(member.id); - } else { - setSelectedInviteRemoveMember(member.id); - } - }} - > - Remove member - - -
+ Remove member + +
)) diff --git a/apps/app/services/project.service.ts b/apps/app/services/project.service.ts index f1740152b..f7daf5674 100644 --- a/apps/app/services/project.service.ts +++ b/apps/app/services/project.service.ts @@ -244,7 +244,7 @@ class ProjectServices extends APIService { }); } - async syncGiuthubRepository( + async syncGithubRepository( workspaceSlug: string, projectId: string, workspaceIntegrationId: string, diff --git a/apps/app/services/track-event.service.ts b/apps/app/services/track-event.service.ts index a8b77150c..5d7ed32f1 100644 --- a/apps/app/services/track-event.service.ts +++ b/apps/app/services/track-event.service.ts @@ -47,7 +47,7 @@ type ViewEventType = "VIEW_CREATE" | "VIEW_UPDATE" | "VIEW_DELETE"; type IssueCommentType = "ISSUE_COMMENT_CREATE" | "ISSUE_COMMENT_UPDATE" | "ISSUE_COMMENT_DELETE"; -type MiscellaneousEventType = +export type MiscellaneousEventType = | "TOGGLE_CYCLE_ON" | "TOGGLE_CYCLE_OFF" | "TOGGLE_MODULE_ON" diff --git a/apps/app/styles/globals.css b/apps/app/styles/globals.css index c16b39807..5f846e038 100644 --- a/apps/app/styles/globals.css +++ b/apps/app/styles/globals.css @@ -96,8 +96,11 @@ -webkit-font-smoothing: antialiased; } -/* scrollbar style */ +body { + color: rgba(var(--color-text-base)); +} +/* scrollbar style */ ::-webkit-scrollbar { display: none; } @@ -206,7 +209,7 @@ ); } -.progress-bar{ +.progress-bar { fill: currentColor; color: rgba(var(--color-bg-sidebar)); } From 8eddc4b3047af0670180f29f3cdb616fbbf9315c Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Sun, 23 Apr 2023 00:00:46 +0530 Subject: [PATCH 17/64] fix: points not updating while editing estimate (#935) --- .../create-update-estimate-modal.tsx | 85 +++++++++++++------ apps/app/types/estimate.d.ts | 1 + 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/apps/app/components/estimates/create-update-estimate-modal.tsx b/apps/app/components/estimates/create-update-estimate-modal.tsx index 3d89e5fcf..98a5a430b 100644 --- a/apps/app/components/estimates/create-update-estimate-modal.tsx +++ b/apps/app/components/estimates/create-update-estimate-modal.tsx @@ -184,32 +184,65 @@ export const CreateUpdateEstimateModal: React.FC = ({ handleClose, data, name: formData.name, description: formData.description, }, - estimate_points: [ - { - key: 0, - value: formData.value1, - }, - { - key: 1, - value: formData.value2, - }, - { - key: 2, - value: formData.value3, - }, - { - key: 3, - value: formData.value4, - }, - { - key: 4, - value: formData.value5, - }, - { - key: 5, - value: formData.value6, - }, - ], + estimate_points: data + ? [ + { + id: data.points[0].id, + key: 0, + value: formData.value1, + }, + { + id: data.points[1].id, + key: 1, + value: formData.value2, + }, + { + id: data.points[2].id, + key: 2, + value: formData.value3, + }, + { + id: data.points[3].id, + key: 3, + value: formData.value4, + }, + { + id: data.points[4].id, + key: 4, + value: formData.value5, + }, + { + id: data.points[5].id, + key: 5, + value: formData.value6, + }, + ] + : [ + { + key: 0, + value: formData.value1, + }, + { + key: 1, + value: formData.value2, + }, + { + key: 2, + value: formData.value3, + }, + { + key: 3, + value: formData.value4, + }, + { + key: 4, + value: formData.value5, + }, + { + key: 5, + value: formData.value6, + }, + ], }; if (data) await updateEstimate(payload); diff --git a/apps/app/types/estimate.d.ts b/apps/app/types/estimate.d.ts index a222e1cf6..6d14a20ca 100644 --- a/apps/app/types/estimate.d.ts +++ b/apps/app/types/estimate.d.ts @@ -31,6 +31,7 @@ export interface IEstimateFormData { description: string; }; estimate_points: { + id?: string; key: number; value: string; }[]; From 4b02886c40175e57a879afebd90dc5770b72450c Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Sun, 23 Apr 2023 01:43:58 +0530 Subject: [PATCH 18/64] chore: remove outline button component (#937) --- apps/app/components/ui/index.ts | 9 ++- apps/app/components/ui/outline-button.tsx | 60 ------------------- .../projects/[projectId]/settings/index.tsx | 8 +-- .../pages/[workspaceSlug]/settings/index.tsx | 13 +--- 4 files changed, 11 insertions(+), 79 deletions(-) delete mode 100644 apps/app/components/ui/outline-button.tsx diff --git a/apps/app/components/ui/index.ts b/apps/app/components/ui/index.ts index 1982f63e3..855655062 100644 --- a/apps/app/components/ui/index.ts +++ b/apps/app/components/ui/index.ts @@ -9,15 +9,14 @@ export * from "./custom-select"; export * from "./date"; export * from "./datepicker"; export * from "./empty-space"; +export * from "./empty-state"; +export * from "./labels-list"; +export * from "./linear-progress-indicator"; export * from "./loader"; export * from "./multi-input"; +export * from "./multi-level-dropdown"; export * from "./multi-level-select"; -export * from "./outline-button"; export * from "./progress-bar"; export * from "./spinner"; export * from "./tooltip"; -export * from "./labels-list"; -export * from "./linear-progress-indicator"; -export * from "./empty-state"; -export * from "./multi-level-dropdown"; export * from "./toggle-switch"; diff --git a/apps/app/components/ui/outline-button.tsx b/apps/app/components/ui/outline-button.tsx deleted file mode 100644 index b4938b730..000000000 --- a/apps/app/components/ui/outline-button.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from "react"; - -type Props = { - onClick?: () => void; - children: React.ReactNode; - type?: "button" | "submit" | "reset"; - className?: string; - theme?: "primary" | "secondary" | "success" | "danger"; - size?: "sm" | "rg" | "md" | "lg"; - disabled?: boolean; -}; - -export const OutlineButton = React.forwardRef( - ( - { - children, - onClick, - type = "button", - size = "sm", - className = "", - theme = "primary", - disabled = false, - }, - ref - ) => ( - - ) -); - -OutlineButton.displayName = "Button"; diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx index 8c75c247c..6194eb73d 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/index.tsx @@ -23,8 +23,8 @@ import { TextArea, Loader, CustomSelect, - OutlineButton, SecondaryButton, + DangerButton, } from "components/ui"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; // types @@ -346,12 +346,12 @@ const GeneralSettings: NextPage = () => {
{projectDetails ? (
- setSelectedProject(projectDetails.id ?? null)} + outline > Delete Project - +
) : ( diff --git a/apps/app/pages/[workspaceSlug]/settings/index.tsx b/apps/app/pages/[workspaceSlug]/settings/index.tsx index 043ff4989..bed30c48b 100644 --- a/apps/app/pages/[workspaceSlug]/settings/index.tsx +++ b/apps/app/pages/[workspaceSlug]/settings/index.tsx @@ -18,14 +18,7 @@ import { WorkspaceAuthorizationLayout } from "layouts/auth-layout"; import { ImageUploadModal } from "components/core"; import { DeleteWorkspaceModal } from "components/workspace"; // ui -import { - Spinner, - Input, - CustomSelect, - OutlineButton, - SecondaryButton, - DangerButton, -} from "components/ui"; +import { Spinner, Input, CustomSelect, SecondaryButton, DangerButton } from "components/ui"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; // icons import { LinkIcon } from "@heroicons/react/24/outline"; @@ -325,9 +318,9 @@ const WorkspaceSettings: NextPage = () => {

- setIsOpen(true)}> + setIsOpen(true)} outline> Delete the workspace - +
From 213dc3f8e88e9eaecc8e85eee598d2aad0ec6605 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:19:43 +0530 Subject: [PATCH 19/64] style: signin page theming (#938) --- apps/app/components/account/email-code-form.tsx | 13 ++++++++----- apps/app/components/account/email-password-form.tsx | 7 +++++-- apps/app/components/account/github-login-button.tsx | 4 ++-- apps/app/components/ui/input/index.tsx | 2 +- apps/app/layouts/default-layout/index.tsx | 2 +- apps/app/pages/signin.tsx | 8 ++++---- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/apps/app/components/account/email-code-form.tsx b/apps/app/components/account/email-code-form.tsx index 389153d60..48288f77e 100644 --- a/apps/app/components/account/email-code-form.tsx +++ b/apps/app/components/account/email-code-form.tsx @@ -92,13 +92,13 @@ export const EmailCodeForm = ({ onSuccess }: any) => { <> {(codeSent || codeResent) && ( -
+
-
-

+

{codeResent ? "Please check your mail for new code." : "Please check your mail for code."} @@ -141,7 +141,9 @@ export const EmailCodeForm = ({ onSuccess }: any) => { diff --git a/apps/app/components/ui/input/index.tsx b/apps/app/components/ui/input/index.tsx index b2c93193a..b6e09f876 100644 --- a/apps/app/components/ui/input/index.tsx +++ b/apps/app/components/ui/input/index.tsx @@ -42,7 +42,7 @@ export const Input: React.FC = ({ : mode === "trueTransparent" ? "rounded border-none bg-transparent ring-0" : "" - } ${error ? "border-red-500/20" : ""} ${error && mode === "primary" ? "bg-red-500/20" : ""} ${ + } ${error ? "border-red-500" : ""} ${error && mode === "primary" ? "bg-red-500/20" : ""} ${ fullWidth ? "w-full" : "" } ${size === "rg" ? "px-3 py-2" : size === "lg" ? "p-3" : ""} ${className}`} {...rest} diff --git a/apps/app/layouts/default-layout/index.tsx b/apps/app/layouts/default-layout/index.tsx index 4134f6352..b615ab548 100644 --- a/apps/app/layouts/default-layout/index.tsx +++ b/apps/app/layouts/default-layout/index.tsx @@ -21,7 +21,7 @@ type Props = { const DefaultLayout: React.FC = ({ meta, children }) => ( -

+
<>{children}
diff --git a/apps/app/pages/signin.tsx b/apps/app/pages/signin.tsx index 3b423b370..a42188fce 100644 --- a/apps/app/pages/signin.tsx +++ b/apps/app/pages/signin.tsx @@ -98,12 +98,12 @@ const SignInPage: NextPage = () => { }} > {isLoading && ( -
+

Signing in. Please wait...

)} -
+
@@ -113,12 +113,12 @@ const SignInPage: NextPage = () => {
-
+
{parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0") ? ( <> -
+
From 2ec8fbab34216272f3cc1dba82f7ac2df29c000f Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:19:53 +0530 Subject: [PATCH 20/64] style: modals theming (#940) * style: existing issues list modal * style: parent issues list modal * style: issue modal * style: cycle modal * style: module modal * style: view modal * style: page modal * style: delete modals --- .../core/existing-issues-list-modal.tsx | 25 +++++----- .../components/cycles/delete-cycle-modal.tsx | 22 +++++--- apps/app/components/cycles/form.tsx | 11 ++-- apps/app/components/cycles/modal.tsx | 4 +- .../issues/comment/comment-card.tsx | 11 ++-- .../components/issues/delete-issue-modal.tsx | 10 ++-- apps/app/components/issues/form.tsx | 7 ++- apps/app/components/issues/modal.tsx | 6 +-- .../issues/parent-issues-list-modal.tsx | 50 +++++++++---------- apps/app/components/issues/select/date.tsx | 21 +++----- .../app/components/issues/select/estimate.tsx | 4 +- apps/app/components/issues/select/label.tsx | 44 +++++++--------- .../app/components/issues/select/priority.tsx | 2 +- apps/app/components/issues/select/state.tsx | 8 +-- apps/app/components/issues/sidebar.tsx | 2 +- .../modules/delete-module-modal.tsx | 22 +++++--- apps/app/components/modules/form.tsx | 16 ++---- apps/app/components/modules/modal.tsx | 4 +- apps/app/components/modules/select/index.ts | 6 +-- .../select/{select-lead.tsx => lead.tsx} | 16 +++--- .../{select-members.tsx => members.tsx} | 0 .../select/{select-status.tsx => status.tsx} | 13 +++-- .../pages/create-update-page-modal.tsx | 4 +- .../components/pages/delete-page-modal.tsx | 25 ++++++---- apps/app/components/pages/page-form.tsx | 26 ++-------- .../project/delete-project-modal.tsx | 16 +++--- .../project/send-project-invitation-modal.tsx | 8 +-- .../app/components/rich-text-editor/index.tsx | 4 +- .../components/ui/custom-search-select.tsx | 4 +- apps/app/components/ui/date.tsx | 21 +++----- .../components/views/delete-view-modal.tsx | 25 ++++++---- apps/app/components/views/form.tsx | 2 - apps/app/components/views/modal.tsx | 13 +++-- .../workspace/delete-workspace-modal.tsx | 16 +++--- apps/app/contexts/project-member.context.tsx | 8 +-- apps/app/tailwind.config.js | 1 + 36 files changed, 228 insertions(+), 249 deletions(-) rename apps/app/components/modules/select/{select-lead.tsx => lead.tsx} (84%) rename apps/app/components/modules/select/{select-members.tsx => members.tsx} (100%) rename apps/app/components/modules/select/{select-status.tsx => status.tsx} (83%) diff --git a/apps/app/components/core/existing-issues-list-modal.tsx b/apps/app/components/core/existing-issues-list-modal.tsx index 2d5ac37dc..3a906b356 100644 --- a/apps/app/components/core/existing-issues-list-modal.tsx +++ b/apps/app/components/core/existing-issues-list-modal.tsx @@ -117,7 +117,7 @@ export const ExistingIssuesListModal: React.FC = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
@@ -130,7 +130,7 @@ export const ExistingIssuesListModal: React.FC = ({ leaveFrom="opacity-100 scale-100" leaveTo="opacity-0 scale-95" > - + = ({ {filteredIssues.length > 0 ? (
  • {query === "" && ( -

    +

    Select issues to add

    )} @@ -167,10 +167,10 @@ export const ExistingIssuesListModal: React.FC = ({ as="label" htmlFor={`issue-${issue.id}`} value={issue.id} - className={({ active }) => - `flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 ${ - active ? "bg-gray-900 bg-opacity-5 text-brand-base" : "" - }` + className={({ active, selected }) => + `flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 text-brand-secondary ${ + active ? "bg-brand-surface-2 text-brand-base" : "" + } ${selected ? "text-brand-base" : ""}` } > {({ selected }) => ( @@ -182,7 +182,7 @@ export const ExistingIssuesListModal: React.FC = ({ backgroundColor: issue.state_detail.color, }} /> - + {issue.project_detail.identifier}-{issue.sequence_id} {issue.name} @@ -194,10 +194,11 @@ export const ExistingIssuesListModal: React.FC = ({
  • ) : (
    - -

    + +

    No issues found. Create a new issue with{" "} -
    C
    . +
    C
    + .

    )} diff --git a/apps/app/components/cycles/delete-cycle-modal.tsx b/apps/app/components/cycles/delete-cycle-modal.tsx index b69537cdc..58b670f3a 100644 --- a/apps/app/components/cycles/delete-cycle-modal.tsx +++ b/apps/app/components/cycles/delete-cycle-modal.tsx @@ -139,7 +139,7 @@ export const DeleteCycleModal: React.FC = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -153,30 +153,36 @@ export const DeleteCycleModal: React.FC = ({ leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - -
    + +
    -
    +
    - + Delete Cycle

    Are you sure you want to delete cycle-{" "} - {data?.name}? All of the data related - to the cycle will be permanently removed. This action cannot be undone. + + {data?.name} + + ? All of the data related to the cycle will be permanently removed. This + action cannot be undone.

    -
    +
    Cancel {isDeleteLoading ? "Deleting..." : "Delete"} diff --git a/apps/app/components/cycles/form.tsx b/apps/app/components/cycles/form.tsx index a15547ddc..f977bc74b 100644 --- a/apps/app/components/cycles/form.tsx +++ b/apps/app/components/cycles/form.tsx @@ -113,7 +113,6 @@ export const CycleForm: React.FC = ({ handleFormSubmit, handleClose, stat
    = ({ handleFormSubmit, handleClose, stat name="description" placeholder="Description" className="h-32 resize-none text-sm" - mode="transparent" error={errors.description} register={register} /> @@ -166,7 +164,8 @@ export const CycleForm: React.FC = ({ handleFormSubmit, handleClose, stat setToastAlert({ type: "error", title: "Error!", - message: "The date you have entered is invalid. Please check and enter a valid date.", + message: + "The date you have entered is invalid. Please check and enter a valid date.", }); } } @@ -197,7 +196,8 @@ export const CycleForm: React.FC = ({ handleFormSubmit, handleClose, stat setToastAlert({ type: "error", title: "Error!", - message: "The date you have entered is invalid. Please check and enter a valid date.", + message: + "The date you have entered is invalid. Please check and enter a valid date.", }); } } @@ -220,7 +220,8 @@ export const CycleForm: React.FC = ({ handleFormSubmit, handleClose, stat ? "cursor-pointer" : "cursor-not-allowed" } - loading={isSubmitting || checkEmptyDate ? false : isDateValid ? false : true} + disabled={checkEmptyDate ? false : isDateValid ? false : true} + loading={isSubmitting} > {status ? isSubmitting diff --git a/apps/app/components/cycles/modal.tsx b/apps/app/components/cycles/modal.tsx index aba22b0ec..cd439c372 100644 --- a/apps/app/components/cycles/modal.tsx +++ b/apps/app/components/cycles/modal.tsx @@ -151,7 +151,7 @@ export const CreateUpdateCycleModal: React.FC = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -164,7 +164,7 @@ export const CreateUpdateCycleModal: React.FC = ({ leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - + = ({ comment, onSubmit, handleCommentD {comment.actor_detail.first_name} {comment.actor_detail.is_bot ? "Bot" : " " + comment.actor_detail.last_name}
    -

    Commented {timeAgo(comment.created_at)}

    +

    + Commented {timeAgo(comment.created_at)} +

    {isEditing ? ( @@ -94,13 +96,13 @@ export const CommentCard: React.FC = ({ comment, onSubmit, handleCommentD
    diff --git a/apps/app/components/issues/delete-issue-modal.tsx b/apps/app/components/issues/delete-issue-modal.tsx index 4fb5f92ea..903022584 100644 --- a/apps/app/components/issues/delete-issue-modal.tsx +++ b/apps/app/components/issues/delete-issue-modal.tsx @@ -88,7 +88,7 @@ export const DeleteIssueModal: React.FC = ({ isOpen, handleClose, data }) leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -102,10 +102,10 @@ export const DeleteIssueModal: React.FC = ({ isOpen, handleClose, data }) leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
    - +
    -

    +

    Are you sure you want to delete issue{" "} - + {data?.project_detail.identifier}-{data?.sequence_id} {""}? All of the data related to the issue will be permanently removed. This diff --git a/apps/app/components/issues/form.tsx b/apps/app/components/issues/form.tsx index 8556c2551..ca085f103 100644 --- a/apps/app/components/issues/form.tsx +++ b/apps/app/components/issues/form.tsx @@ -221,7 +221,7 @@ export const IssueForm: FC = ({

    {watch("parent") && watch("parent") !== "" ? ( -
    +
    = ({ .color, }} /> - + {/* {projects?.find((p) => p.id === projectId)?.identifier}- */} {issues.find((i) => i.id === watch("parent"))?.sequence_id} @@ -253,7 +253,6 @@ export const IssueForm: FC = ({ onChange={handleTitleChange} className="resize-none text-xl" placeholder="Title" - mode="transparent" autoComplete="off" error={errors.name} register={register} @@ -294,7 +293,7 @@ export const IssueForm: FC = ({ )}
    -
    +
    {issueName && issueName !== "" && ( ) : ( <> - - Due Date + + Due Date )} diff --git a/apps/app/components/issues/select/estimate.tsx b/apps/app/components/issues/select/estimate.tsx index 142a0369b..18204295b 100644 --- a/apps/app/components/issues/select/estimate.tsx +++ b/apps/app/components/issues/select/estimate.tsx @@ -22,8 +22,8 @@ export const IssueEstimateSelect: React.FC = ({ value, onChange }) => { value={value} label={
    - - + + {estimatePoints?.find((e) => e.key === value)?.value ?? "Estimate"}
    diff --git a/apps/app/components/issues/select/label.tsx b/apps/app/components/issues/select/label.tsx index f5b023280..a7ada1133 100644 --- a/apps/app/components/issues/select/label.tsx +++ b/apps/app/components/issues/select/label.tsx @@ -58,16 +58,7 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, > {({ open }: any) => ( <> - - `flex cursor-pointer items-center rounded-md border border-brand-base text-xs shadow-sm duration-200 - ${ - open - ? "border-brand-accent bg-brand-accent/5 outline-none ring-1 ring-brand-accent " - : "hover:bg-brand-accent/5 " - }` - } - > + {value && value.length > 0 ? ( = ({ setIsOpen, value, onChange, /> ) : ( - - + + Label )} @@ -96,12 +87,12 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, > -
    +
    setQuery(event.target.value)} placeholder="Search for label..." displayValue={(assigned: any) => assigned?.name} @@ -121,7 +112,7 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, className={({ active }) => `${ active ? "bg-brand-surface-2" : "" - } group flex min-w-[14rem] cursor-pointer select-none items-center gap-2 truncate rounded px-1 py-1.5 text-gray-600` + } group flex min-w-[14rem] cursor-pointer select-none items-center gap-2 truncate rounded px-1 py-1.5 text-brand-secondary` } value={label.id} > @@ -129,10 +120,9 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange,
    {label.name} @@ -150,8 +140,8 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, ); } else return ( -
    -
    +
    +
    {label.name}
    @@ -161,7 +151,7 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, className={({ active }) => `${ active ? "bg-brand-surface-2" : "" - } group flex min-w-[14rem] cursor-pointer select-none items-center gap-2 truncate rounded px-1 py-1.5 text-gray-600` + } group flex min-w-[14rem] cursor-pointer select-none items-center gap-2 truncate rounded px-1 py-1.5 text-brand-secondary` } value={child.id} > @@ -169,9 +159,9 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange,
    {child.name} @@ -202,9 +192,9 @@ export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, className="flex w-full select-none items-center rounded py-2 px-1 hover:bg-brand-surface-2" onClick={() => setIsOpen(true)} > - -
    diff --git a/apps/app/components/issues/select/priority.tsx b/apps/app/components/issues/select/priority.tsx index 89e07b4e6..7404ac5a0 100644 --- a/apps/app/components/issues/select/priority.tsx +++ b/apps/app/components/issues/select/priority.tsx @@ -20,7 +20,7 @@ export const IssuePrioritySelect: React.FC = ({ value, onChange }) => ( {getPriorityIcon(value, `text-xs ${value ? "" : "text-brand-secondary"}`)} - + {value ?? "Priority"}
    diff --git a/apps/app/components/issues/select/state.tsx b/apps/app/components/issues/select/state.tsx index 5a9204d39..fa4807815 100644 --- a/apps/app/components/issues/select/state.tsx +++ b/apps/app/components/issues/select/state.tsx @@ -56,15 +56,17 @@ export const IssueStateSelect: React.FC = ({ setIsOpen, value, onChange, onChange={onChange} options={options} label={ -
    +
    {selectedOption ? ( getStateGroupIcon(selectedOption.group, "16", "16", selectedOption.color) ) : currentDefaultState ? ( getStateGroupIcon(currentDefaultState.group, "16", "16", currentDefaultState.color) ) : ( - + )} - {selectedOption?.name ? selectedOption.name : currentDefaultState?.name ?? "State"} + {selectedOption?.name + ? selectedOption.name + : currentDefaultState?.name ?? State}
    } footerOption={ diff --git a/apps/app/components/issues/sidebar.tsx b/apps/app/components/issues/sidebar.tsx index 963581b48..fbdcd7714 100644 --- a/apps/app/components/issues/sidebar.tsx +++ b/apps/app/components/issues/sidebar.tsx @@ -316,7 +316,7 @@ export const IssueDetailsSidebar: React.FC = ({ issueDetail?.parent_detail ? ( ) : ( <> - - {label} + + {label} )} diff --git a/apps/app/components/views/delete-view-modal.tsx b/apps/app/components/views/delete-view-modal.tsx index 1ae6865c5..31cfc33e9 100644 --- a/apps/app/components/views/delete-view-modal.tsx +++ b/apps/app/components/views/delete-view-modal.tsx @@ -81,7 +81,7 @@ export const DeleteViewModal: React.FC = ({ isOpen, data, setIsOpen }) => leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -95,31 +95,36 @@ export const DeleteViewModal: React.FC = ({ isOpen, data, setIsOpen }) => leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - -
    + +
    -
    +
    - + Delete View

    - Are you sure you want to delete view- {" "} - {data?.name} - ? All of the data related to the view will be permanently removed. - This action cannot be undone. + Are you sure you want to delete view-{" "} + + {data?.name} + + ? All of the data related to the view will be permanently removed. This + action cannot be undone.

    -
    +
    Cancel {isDeleteLoading ? "Deleting..." : "Delete"} diff --git a/apps/app/components/views/form.tsx b/apps/app/components/views/form.tsx index d75a66430..1b8875138 100644 --- a/apps/app/components/views/form.tsx +++ b/apps/app/components/views/form.tsx @@ -78,7 +78,6 @@ export const ViewForm: React.FC = ({ type="name" placeholder="Title" autoComplete="off" - mode="transparent" className="resize-none text-xl" error={errors.name} register={register} @@ -97,7 +96,6 @@ export const ViewForm: React.FC = ({ name="description" placeholder="Description" className="h-32 resize-none text-sm" - mode="transparent" error={errors.description} register={register} /> diff --git a/apps/app/components/views/modal.tsx b/apps/app/components/views/modal.tsx index aafda3499..a04c19b64 100644 --- a/apps/app/components/views/modal.tsx +++ b/apps/app/components/views/modal.tsx @@ -21,10 +21,15 @@ type Props = { isOpen: boolean; handleClose: () => void; data?: IView | null; - preLoadedData?: Partial | null ; + preLoadedData?: Partial | null; }; -export const CreateUpdateViewModal: React.FC = ({ isOpen, handleClose, data, preLoadedData }) => { +export const CreateUpdateViewModal: React.FC = ({ + isOpen, + handleClose, + data, + preLoadedData, +}) => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -114,7 +119,7 @@ export const CreateUpdateViewModal: React.FC = ({ isOpen, handleClose, da leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -128,7 +133,7 @@ export const CreateUpdateViewModal: React.FC = ({ isOpen, handleClose, da leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - + = ({ isOpen, data, onClose }) leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -102,10 +102,10 @@ export const DeleteWorkspaceModal: React.FC = ({ isOpen, data, onClose }) leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
    - + -
    +

    Enter the workspace name{" "} - {selectedWorkspace?.name} to continue: + {selectedWorkspace?.name}{" "} + to continue:

    = ({ isOpen, data, onClose }) />
    -
    +

    - To confirm, type delete my workspace{" "} + To confirm, type{" "} + delete my workspace{" "} below:

    = (props) => { workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId.toString()) : null, workspaceSlug && projectId ? () => projectService.projectMemberMe(workspaceSlug.toString(), projectId.toString()) - : null, - { - onErrorRetry(err, _, __, ___, revalidateOpts) { - if (err.status === 401 || err.status === 403) return; - revalidateOpts.retryCount = 5; - }, - } + : null ); const loading = !memberDetails && !error; diff --git a/apps/app/tailwind.config.js b/apps/app/tailwind.config.js index 732e4be00..9dde3bb74 100644 --- a/apps/app/tailwind.config.js +++ b/apps/app/tailwind.config.js @@ -34,6 +34,7 @@ module.exports = { "surface-1": withOpacity("--color-bg-surface-1"), "surface-2": withOpacity("--color-bg-surface-2"), sidebar: withOpacity("--color-bg-sidebar"), + backdrop: "#131313", }, }, textColor: { From ae26b17cab677defe783e8ffba08782530490cdf Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:20:02 +0530 Subject: [PATCH 21/64] style: filters list theming (#941) --- apps/app/components/core/filter-list.tsx | 41 +++++++++++------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/apps/app/components/core/filter-list.tsx b/apps/app/components/core/filter-list.tsx index 2ede6a367..70f980aae 100644 --- a/apps/app/components/core/filter-list.tsx +++ b/apps/app/components/core/filter-list.tsx @@ -59,7 +59,7 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { key={key} className="flex items-center gap-x-2 rounded-full border border-brand-base bg-brand-surface-2 px-2 py-1" > - + {replaceUnderscoreIfSnakeCase(key)}: {filters[key as keyof IIssueFilterOptions] === null || @@ -75,7 +75,7 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { return (

    = ({ filters, setFilters }) => { {filters.priority?.map((priority: any) => (

    {getPriorityIcon(priority)} @@ -170,7 +170,7 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { return (

    {member?.first_name} @@ -203,7 +203,7 @@ export const FilterList: React.FC = ({ filters, setFilters }) => {
    - ) : (key as keyof IIssueFilterOptions) === "created_by" ? ( + ) : key === "created_by" ? (
    {filters.created_by?.map((memberId: string) => { const member = members?.find((m) => m.member.id === memberId)?.member; @@ -211,7 +211,7 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { return (
    {member?.first_name} @@ -253,25 +253,20 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { const color = label.color !== "" ? label.color : "#0f172a"; return (
    - - {label.name} - + {label.name} @@ -341,8 +336,8 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { } className="flex items-center gap-x-1 rounded-full border border-brand-base bg-brand-surface-2 px-3 py-1.5 text-xs" > - Clear all filters - + Clear all filters + )}
    From 7116acc33176daa0ff4ac8b0839d582d72f1a232 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:20:14 +0530 Subject: [PATCH 22/64] style: auth pages theming (#942) --- .../components/auth-screens/not-authorized-view.tsx | 10 ++++------ .../components/auth-screens/project/join-project.tsx | 4 ++-- .../components/auth-screens/workspace/not-a-member.tsx | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/apps/app/components/auth-screens/not-authorized-view.tsx b/apps/app/components/auth-screens/not-authorized-view.tsx index 054e5bfa3..0ae730b5b 100644 --- a/apps/app/components/auth-screens/not-authorized-view.tsx +++ b/apps/app/components/auth-screens/not-authorized-view.tsx @@ -36,16 +36,14 @@ export const NotAuthorizedView: React.FC = ({ actionButton, type }) => { alt="ProjectSettingImg" />
    -

    - Oops! You are not authorized to view this page -

    +

    Oops! You are not authorized to view this page

    -
    +
    {user ? (

    You have signed in as {user.email}.
    - Sign in + Sign in {" "} with different account that has access to this page.

    @@ -53,7 +51,7 @@ export const NotAuthorizedView: React.FC = ({ actionButton, type }) => {

    You need to{" "} - Sign in + Sign in {" "} with an account that has access to this page.

    diff --git a/apps/app/components/auth-screens/project/join-project.tsx b/apps/app/components/auth-screens/project/join-project.tsx index cf8fabd41..098469114 100644 --- a/apps/app/components/auth-screens/project/join-project.tsx +++ b/apps/app/components/auth-screens/project/join-project.tsx @@ -45,9 +45,9 @@ export const JoinProject: React.FC = () => {
    JoinProject
    -

    You are not a member of this project

    +

    You are not a member of this project

    -
    +

    You are not a member of this project, but you can join this project by clicking the button below. diff --git a/apps/app/components/auth-screens/workspace/not-a-member.tsx b/apps/app/components/auth-screens/workspace/not-a-member.tsx index 542727593..9627143ba 100644 --- a/apps/app/components/auth-screens/workspace/not-a-member.tsx +++ b/apps/app/components/auth-screens/workspace/not-a-member.tsx @@ -20,12 +20,12 @@ export const NotAWorkspaceMember = () => {

    Not Authorized!

    -

    +

    You{"'"}re not a member of this workspace. Please contact the workspace admin to get an invitation or check your pending invitations.

    -
    +
    Check pending invites From 5412e097016bc734053f83433bfd2dba86604237 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 24 Apr 2023 11:28:05 +0530 Subject: [PATCH 23/64] chore: empty input fields text (#943) --- .../core/board-view/single-issue.tsx | 2 +- .../core/list-view/single-issue.tsx | 2 +- apps/app/components/cycles/sidebar.tsx | 38 +++++++++--------- .../issues/sidebar-select/parent.tsx | 12 +++--- apps/app/components/issues/sidebar.tsx | 1 + .../issues/view-select/due-date.tsx | 4 +- .../modules/sidebar-select/select-lead.tsx | 24 ++++------- apps/app/components/modules/sidebar.tsx | 40 ++++++++++--------- apps/app/helpers/date-time.helper.ts | 9 +++-- apps/app/styles/globals.css | 3 +- 10 files changed, 67 insertions(+), 68 deletions(-) diff --git a/apps/app/components/core/board-view/single-issue.tsx b/apps/app/components/core/board-view/single-issue.tsx index b36008c77..e0ad96d1f 100644 --- a/apps/app/components/core/board-view/single-issue.tsx +++ b/apps/app/components/core/board-view/single-issue.tsx @@ -349,7 +349,7 @@ export const SingleBoardIssue: React.FC = ({ /> )} {properties.sub_issue_count && ( -
    +
    {issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
    )} diff --git a/apps/app/components/core/list-view/single-issue.tsx b/apps/app/components/core/list-view/single-issue.tsx index 0d34adf9e..71ccc9db4 100644 --- a/apps/app/components/core/list-view/single-issue.tsx +++ b/apps/app/components/core/list-view/single-issue.tsx @@ -268,7 +268,7 @@ export const SingleListIssue: React.FC = ({ /> )} {properties.sub_issue_count && ( -
    +
    {issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
    )} diff --git a/apps/app/components/cycles/sidebar.tsx b/apps/app/components/cycles/sidebar.tsx index f896d232e..863da7934 100644 --- a/apps/app/components/cycles/sidebar.tsx +++ b/apps/app/components/cycles/sidebar.tsx @@ -141,25 +141,25 @@ export const CycleDetailsSidebar: React.FC = ({ <>
    -
    - +
    + {capitalizeFirstLetter(cycleStatus)}
    -
    - +
    + {({ open }) => ( <> - {renderShortDate(new Date(`${cycle?.start_date}`))} + + {renderShortDate(new Date(`${cycle?.start_date}`), "Start date")} + = ({ )} - + - + {({ open }) => ( <> - + - {renderShortDate(new Date(`${cycle?.end_date}`))} + {renderShortDate(new Date(`${cycle?.end_date}`), "End date")} = ({ leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - + = ({
    -
    -
    -
    +
    +
    +

    {cycle.name}

    {!isCompleted && ( diff --git a/apps/app/components/issues/sidebar-select/parent.tsx b/apps/app/components/issues/sidebar-select/parent.tsx index 36c4a57cc..92a51269f 100644 --- a/apps/app/components/issues/sidebar-select/parent.tsx +++ b/apps/app/components/issues/sidebar-select/parent.tsx @@ -83,11 +83,13 @@ export const SidebarParentSelect: React.FC = ({ onClick={() => setIsParentModalOpen(true)} disabled={isNotAllowed} > - {watch("parent") && watch("parent") !== "" - ? `${issues?.find((i) => i.id === watch("parent"))?.project_detail?.identifier}-${ - issues?.find((i) => i.id === watch("parent"))?.sequence_id - }` - : "Select issue"} + {watch("parent") && watch("parent") !== "" ? ( + `${issues?.find((i) => i.id === watch("parent"))?.project_detail?.identifier}-${ + issues?.find((i) => i.id === watch("parent"))?.sequence_id + }` + ) : ( + Select issue + )}
    diff --git a/apps/app/components/issues/sidebar.tsx b/apps/app/components/issues/sidebar.tsx index fbdcd7714..54cf69e1d 100644 --- a/apps/app/components/issues/sidebar.tsx +++ b/apps/app/components/issues/sidebar.tsx @@ -354,6 +354,7 @@ export const IssueDetailsSidebar: React.FC = ({ name="target_date" render={({ field: { value } }) => ( submitChanges({ diff --git a/apps/app/components/issues/view-select/due-date.tsx b/apps/app/components/issues/view-select/due-date.tsx index 61aad5766..d3f16c5b4 100644 --- a/apps/app/components/issues/view-select/due-date.tsx +++ b/apps/app/components/issues/view-select/due-date.tsx @@ -31,7 +31,7 @@ export const ViewDueDateSelect: React.FC = ({ issue, partialUpdateIssue, }`} > { partialUpdateIssue({ @@ -51,7 +51,7 @@ export const ViewDueDateSelect: React.FC = ({ issue, partialUpdateIssue, "ISSUE_PROPERTY_UPDATE_DUE_DATE" ); }} - className={issue?.target_date ? "w-[6.5rem]" : "w-[3rem] text-center"} + className={issue?.target_date ? "w-[6.5rem]" : "w-[5rem] text-center"} disabled={isNotAllowed} />
    diff --git a/apps/app/components/modules/sidebar-select/select-lead.tsx b/apps/app/components/modules/sidebar-select/select-lead.tsx index b551294fb..cea3627f1 100644 --- a/apps/app/components/modules/sidebar-select/select-lead.tsx +++ b/apps/app/components/modules/sidebar-select/select-lead.tsx @@ -62,25 +62,17 @@ export const SidebarLeadSelect: React.FC = ({ value, onChange }) => { +
    + {selectedOption && } {selectedOption ? ( - + selectedOption?.first_name && selectedOption.first_name !== "" ? ( + selectedOption?.first_name + ) : ( + selectedOption?.email + ) ) : ( -
    - No user -
    + No lead )} - {selectedOption - ? selectedOption?.first_name && selectedOption.first_name !== "" - ? selectedOption?.first_name - : selectedOption?.email - : "N/A"}
    } options={options} diff --git a/apps/app/components/modules/sidebar.tsx b/apps/app/components/modules/sidebar.tsx index e3d75be45..3b3076e05 100644 --- a/apps/app/components/modules/sidebar.tsx +++ b/apps/app/components/modules/sidebar.tsx @@ -198,9 +198,7 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen, render={({ field: { value } }) => ( + {capitalizeFirstLetter(`${watch("status")}`)} } @@ -218,17 +216,19 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen, )} />
    -
    - +
    + {({ open }) => ( <> - {renderShortDate(new Date(`${module.start_date}`))} + + {renderShortDate(new Date(`${module.start_date}`), "Start date")} + = ({ issues, module, isOpen, )} - + - + {({ open }) => ( <> - {renderShortDate(new Date(`${module?.target_date}`))} + + {renderShortDate(new Date(`${module?.target_date}`), "End date")} + = ({ issues, module, isOpen,
    -
    -
    -
    +
    +
    +

    {module.name}

    setModuleDeleteModal(true)}> @@ -522,9 +524,9 @@ export const ModuleDetailsSidebar: React.FC = ({ issues, module, isOpen,
    -
    -
    -

    Links

    +
    +
    +

    Links

    ) : ( -
    No previous imports available.
    +

    + No previous imports available. +

    ) ) : ( - + diff --git a/apps/app/components/integration/jira/confirm-import.tsx b/apps/app/components/integration/jira/confirm-import.tsx index 79c56bf4b..1945bb501 100644 --- a/apps/app/components/integration/jira/confirm-import.tsx +++ b/apps/app/components/integration/jira/confirm-import.tsx @@ -17,30 +17,30 @@ export const JiraConfirmImport: React.FC = () => {
    -

    Migrating

    +

    Migrating

    -

    {watch("data.total_issues")}

    -

    Issues

    +

    {watch("data.total_issues")}

    +

    Issues

    -

    {watch("data.total_states")}

    -

    States

    +

    {watch("data.total_states")}

    +

    States

    -

    {watch("data.total_modules")}

    -

    Modules

    +

    {watch("data.total_modules")}

    +

    Modules

    -

    {watch("data.total_labels")}

    -

    Labels

    +

    {watch("data.total_labels")}

    +

    Labels

    -

    +

    {watch("data.users").filter((user) => user.import).length}

    -

    User

    +

    User

    diff --git a/apps/app/components/integration/jira/give-details.tsx b/apps/app/components/integration/jira/give-details.tsx index fb4032b68..a47fc67fc 100644 --- a/apps/app/components/integration/jira/give-details.tsx +++ b/apps/app/components/integration/jira/give-details.tsx @@ -30,15 +30,11 @@ export const JiraGetImportDetail: React.FC = () => {
    -

    Jira Personal Access Token

    -

    +

    Jira Personal Access Token

    +

    Get to know your access token by navigating to{" "} - + Atlassian Settings @@ -61,8 +57,8 @@ export const JiraGetImportDetail: React.FC = () => {

    -

    Jira Project Key

    -

    If XXX-123 is your issue, then enter XXX

    +

    Jira Project Key

    +

    If XXX-123 is your issue, then enter XXX

    {
    -

    Jira Email Address

    -

    +

    Jira Email Address

    +

    Enter the Gmail account that you use in Jira account

    @@ -102,8 +98,8 @@ export const JiraGetImportDetail: React.FC = () => {
    -

    Jira Installation or Cloud Host Name

    -

    Enter your companies cloud host name

    +

    Jira Installation or Cloud Host Name

    +

    Enter your companies cloud host name

    {
    -

    Import to project

    -

    Select which project you want to import to.

    +

    Import to project

    +

    + Select which project you want to import to. +

    { onChange={onChange} label={ - {value && value !== "" - ? projects.find((p) => p.id === value)?.name - : "Select Project"} + {value && value !== "" ? ( + projects.find((p) => p.id === value)?.name + ) : ( + Select a project + )} } > @@ -151,7 +151,7 @@ export const JiraGetImportDetail: React.FC = () => { )) ) : ( -
    +

    You don{"'"}t have any project. Please create a project first.

    )} @@ -162,7 +162,7 @@ export const JiraGetImportDetail: React.FC = () => { const event = new KeyboardEvent("keydown", { key: "p" }); document.dispatchEvent(event); }} - className="flex cursor-pointer select-none items-center space-x-2 truncate rounded px-1 py-1.5 text-gray-500" + className="flex cursor-pointer select-none items-center space-x-2 truncate rounded px-1 py-1.5 text-brand-secondary" > Create new project diff --git a/apps/app/components/integration/jira/import-users.tsx b/apps/app/components/integration/jira/import-users.tsx index fa48bc772..da14f7d15 100644 --- a/apps/app/components/integration/jira/import-users.tsx +++ b/apps/app/components/integration/jira/import-users.tsx @@ -52,11 +52,13 @@ export const JiraImportUsers: FC = () => { })) ?? []; return ( -
    +
    -

    Users

    -

    Update, invite or choose not to invite assignee

    +

    Users

    +

    + Update, invite or choose not to invite assignee +

    { {watch("data.invite_users") && (
    -
    Name
    -
    Import as
    +
    Name
    +
    Import as
    diff --git a/apps/app/components/integration/jira/jira-project-detail.tsx b/apps/app/components/integration/jira/jira-project-detail.tsx index 48220a8c1..2e4061638 100644 --- a/apps/app/components/integration/jira/jira-project-detail.tsx +++ b/apps/app/components/integration/jira/jira-project-detail.tsx @@ -102,12 +102,12 @@ export const JiraProjectDetail: React.FC = (props) => { if (error) { return (
    -

    +

    Something went wrong. Please{" "} {" "} @@ -121,37 +121,37 @@ export const JiraProjectDetail: React.FC = (props) => {

    -

    Import Data

    -

    Import Completed. We have found:

    +

    Import Data

    +

    Import Completed. We have found:

    -

    {projectInfo?.issues}

    -

    Issues

    +

    {projectInfo?.issues}

    +

    Issues

    -

    {projectInfo?.states}

    -

    States

    +

    {projectInfo?.states}

    +

    States

    -

    {projectInfo?.modules}

    -

    Modules

    +

    {projectInfo?.modules}

    +

    Modules

    -

    {projectInfo?.labels}

    -

    Labels

    +

    {projectInfo?.labels}

    +

    Labels

    -

    {projectInfo?.users?.length}

    -

    Users

    +

    {projectInfo?.users?.length}

    +

    Users

    -

    Import Epics

    -

    Import epics as modules

    +

    Import Epics

    +

    Import epics as modules

    { return (
    -
    +
    @@ -114,7 +114,7 @@ export const JiraImporterRoot = () => {
    -
    +
    jira logo @@ -131,14 +131,14 @@ export const JiraImporterRoot = () => { index > activeIntegrationState() + 1 || Boolean(index === activeIntegrationState() + 1 && disableTopBarAfter) } - className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full border ${ + className={`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full border border-brand-base ${ index <= activeIntegrationState() - ? `border-[#3F76FF] bg-[#3F76FF] text-white ${ + ? `border-brand-accent bg-brand-accent ${ index === activeIntegrationState() ? "border-opacity-100 bg-opacity-100" : "border-opacity-80 bg-opacity-80" }` - : "border-gray-300" + : "border-brand-base" }`} > {
    {" "} @@ -177,7 +179,7 @@ export const JiraImporterRoot = () => { {currentStep?.state === "import-confirmation" && }
    -
    +
    {currentStep?.state !== "import-configure" && ( { diff --git a/apps/app/components/integration/single-import.tsx b/apps/app/components/integration/single-import.tsx index bef5b7301..f513c391f 100644 --- a/apps/app/components/integration/single-import.tsx +++ b/apps/app/components/integration/single-import.tsx @@ -18,28 +18,28 @@ const importersList: { [key: string]: string } = { }; export const SingleImport: React.FC = ({ service, refreshing, handleDelete }) => ( -
    +
    -

    +

    Import from {importersList[service.service]} to{" "} {service.project_detail.name} {refreshing ? "Refreshing..." : service.status}

    -
    +
    {renderShortDateWithYearFormat(service.created_at)}| Imported by{" "} From 7234d6f68b780259d6794414236aec70cf7755ab Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 24 Apr 2023 13:21:09 +0530 Subject: [PATCH 25/64] style: workspace and project settings (#946) --- .../components/emoji-icon-picker/index.tsx | 31 +++++++++---------- .../integration/single-integration-card.tsx | 2 +- .../project-authorization-wrapper.tsx | 4 +-- .../workspace-authorization-wrapper.tsx | 2 +- .../projects/[projectId]/settings/control.tsx | 17 +++++----- .../projects/[projectId]/settings/index.tsx | 30 ++++++++++-------- .../[workspaceSlug]/settings/billing.tsx | 18 ++++++----- .../pages/[workspaceSlug]/settings/index.tsx | 20 ++++++------ 8 files changed, 66 insertions(+), 58 deletions(-) diff --git a/apps/app/components/emoji-icon-picker/index.tsx b/apps/app/components/emoji-icon-picker/index.tsx index 6cb3b84f9..592d7bb01 100644 --- a/apps/app/components/emoji-icon-picker/index.tsx +++ b/apps/app/components/emoji-icon-picker/index.tsx @@ -36,7 +36,7 @@ const EmojiIconPicker: React.FC = ({ const [isOpen, setIsOpen] = useState(false); const [openColorPicker, setOpenColorPicker] = useState(false); - const [activeColor, setActiveColor] = useState("#020617"); + const [activeColor, setActiveColor] = useState("#858e96"); const [recentEmojis, setRecentEmojis] = useState([]); @@ -69,8 +69,8 @@ const EmojiIconPicker: React.FC = ({ leaveFrom="transform opacity-100 scale-100" leaveTo="transform opacity-0 scale-95" > - -
    + +
    {tabOptions.map((tab) => ( @@ -82,7 +82,7 @@ const EmojiIconPicker: React.FC = ({ setOpenColorPicker(false); }} className={`-my-1 w-1/2 border-b pb-2 text-center text-sm font-medium outline-none transition-colors ${ - selected ? "border-theme text-theme" : "border-transparent text-gray-500" + selected ? "" : "border-transparent text-brand-secondary" }`} > {tab.title} @@ -95,12 +95,12 @@ const EmojiIconPicker: React.FC = ({ {recentEmojis.length > 0 && (
    -

    Recent

    +

    Recent

    {recentEmojis.map((emoji) => (
    )} -
    +
    {emojis.map((emoji) => (
    = ({ />
    -
    - -
    +
    +
    {icons.material_rounded.map((icon) => ( +
    {isMonthlyView ? "Monthly" : "Weekly"}
    @@ -445,61 +445,87 @@ export const CalendarView: React.FC = ({ addIssueToDate }) => { showWeekEnds ? "grid-cols-7" : "grid-cols-5" } `} > - {currentViewDaysData.map((date, index) => ( - - {(provided, snapshot) => ( -
    { + const totalIssues = date.issues.length; + + return ( + + {(provided) => ( +
    - {isMonthlyView && {formatDate(new Date(date.date), "d")}} - {date.issues.length > 0 && - date.issues.map((issue: IIssue, index) => ( - - {(provided, snapshot) => ( -
    - - {issue.name} - -
    - )} -
    - ))} -
    - + )} +
    - - Add new issue - + +
    + {provided.placeholder}
    - {provided.placeholder} -
    - )} -
    - ))} + )} + + ); + })}
    diff --git a/apps/app/components/core/issues-view.tsx b/apps/app/components/core/issues-view.tsx index 240368d7f..f063de373 100644 --- a/apps/app/components/core/issues-view.tsx +++ b/apps/app/components/core/issues-view.tsx @@ -426,7 +426,7 @@ export const IssuesView: React.FC = ({ )}
    {areFiltersApplied && ( -
    +
    )} From c5b034385ff5258d53da141b4765708239091e41 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:53:07 +0530 Subject: [PATCH 27/64] chore: added custom toggle switch everywhere (#949) --- .../core/calendar-view/calendar.tsx | 23 ++++----------- .../components/core/issues-view-filter.tsx | 23 ++++----------- .../integration/github/import-data.tsx | 22 ++------------- apps/app/components/issues/form.tsx | 26 ++++++----------- apps/app/components/ui/toggle-switch.tsx | 23 +++++++++++---- .../[projectId]/settings/features.tsx | 28 ++++--------------- 6 files changed, 45 insertions(+), 100 deletions(-) diff --git a/apps/app/components/core/calendar-view/calendar.tsx b/apps/app/components/core/calendar-view/calendar.tsx index 19a09710a..dd814ed09 100644 --- a/apps/app/components/core/calendar-view/calendar.tsx +++ b/apps/app/components/core/calendar-view/calendar.tsx @@ -26,7 +26,7 @@ import { import { Popover, Transition } from "@headlessui/react"; import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; -import { CustomMenu, Spinner } from "components/ui"; +import { CustomMenu, Spinner, ToggleSwitch } from "components/ui"; // icon import { CheckIcon, @@ -390,23 +390,10 @@ export const CalendarView: React.FC = ({ addIssueToDate }) => {

    Show weekends

    - + setShowWeekEnds(!showWeekEnds)} + />
    diff --git a/apps/app/components/core/issues-view-filter.tsx b/apps/app/components/core/issues-view-filter.tsx index 49bf5cf6b..9a9f9494f 100644 --- a/apps/app/components/core/issues-view-filter.tsx +++ b/apps/app/components/core/issues-view-filter.tsx @@ -10,7 +10,7 @@ import { Popover, Transition } from "@headlessui/react"; // components import { SelectFilters } from "components/views"; // ui -import { CustomMenu } from "components/ui"; +import { CustomMenu, ToggleSwitch } from "components/ui"; // icons import { ChevronDownIcon, @@ -213,23 +213,10 @@ export const IssuesFilterView: React.FC = () => { <>

    Show empty states

    - + setShowEmptyGroups(!showEmptyGroups)} + />
    + onChange(!value)} /> )} />
    diff --git a/apps/app/components/issues/form.tsx b/apps/app/components/issues/form.tsx index ca085f103..8d1db9e54 100644 --- a/apps/app/components/issues/form.tsx +++ b/apps/app/components/issues/form.tsx @@ -26,7 +26,14 @@ import { CreateStateModal } from "components/states"; import { CreateUpdateCycleModal } from "components/cycles"; import { CreateLabelModal } from "components/labels"; // ui -import { CustomMenu, Input, Loader, PrimaryButton, SecondaryButton } from "components/ui"; +import { + CustomMenu, + Input, + Loader, + PrimaryButton, + SecondaryButton, + ToggleSwitch, +} from "components/ui"; // icons import { SparklesIcon, XMarkIcon } from "@heroicons/react/24/outline"; // helpers @@ -449,22 +456,7 @@ export const IssueForm: FC = ({ onClick={() => setCreateMore((prevData) => !prevData)} > Create more - + {}} size="md" />
    Discard diff --git a/apps/app/components/ui/toggle-switch.tsx b/apps/app/components/ui/toggle-switch.tsx index 8c221dad2..fe4e2c96d 100644 --- a/apps/app/components/ui/toggle-switch.tsx +++ b/apps/app/components/ui/toggle-switch.tsx @@ -4,27 +4,38 @@ type Props = { value: boolean; onChange: (value: boolean) => void; label?: string; + size?: "sm" | "md" | "lg"; disabled?: boolean; className?: string; }; export const ToggleSwitch: React.FC = (props) => { - const { value, onChange, label, disabled, className } = props; + const { value, onChange, label, size = "sm", disabled, className } = props; return ( {label} diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx index 150d28b4a..3b5a09e6c 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/features.tsx @@ -12,7 +12,7 @@ import { ProjectAuthorizationWrapper } from "layouts/auth-layout"; // hooks import useToast from "hooks/use-toast"; // ui -import { SecondaryButton } from "components/ui"; +import { SecondaryButton, ToggleSwitch } from "components/ui"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; // icons import { ContrastIcon, PeopleGroupIcon, ViewListIcon } from "components/icons"; @@ -149,16 +149,9 @@ const FeaturesSettings: NextPage = () => {

    {feature.description}

    - + size="lg" + />
    ))}
    From 7d96adcb7010f4d3c245b1a085944af9bd8935c3 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:53:18 +0530 Subject: [PATCH 28/64] chore: empty state for estimates (#950) --- apps/app/components/ui/empty-state.tsx | 8 +-- .../[projectId]/settings/estimates.tsx | 59 +++++++++++-------- .../app/public/empty-state/empty-estimate.svg | 49 +++++++++++++++ 3 files changed, 89 insertions(+), 27 deletions(-) create mode 100644 apps/app/public/empty-state/empty-estimate.svg diff --git a/apps/app/components/ui/empty-state.tsx b/apps/app/components/ui/empty-state.tsx index c0fec4ee5..4e469d967 100644 --- a/apps/app/components/ui/empty-state.tsx +++ b/apps/app/components/ui/empty-state.tsx @@ -9,7 +9,7 @@ import { PlusIcon } from "@heroicons/react/24/outline"; import { capitalizeFirstLetter } from "helpers/string.helper"; type Props = { - type: "cycle" | "module" | "project" | "issue" | "view" | "page"; + type: "cycle" | "module" | "project" | "issue" | "view" | "page" | "estimate"; title: string; description: React.ReactNode | string; imgURL: string; @@ -30,7 +30,7 @@ export const EmptyState: React.FC = ({ type, title, description, imgURL, case "view": return "V"; case "page": - return "D" + return "D"; default: return null; } @@ -41,11 +41,11 @@ export const EmptyState: React.FC = ({ type, title, description, imgURL, {type}
    -

    {title}

    +

    {title}

    {shortcutKey(type) && ( Use shortcut{" "} - + {shortcutKey(type)} {" "} to create {type} from anywhere. diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx index 4ea47423f..679392f41 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx @@ -16,10 +16,12 @@ import { CreateUpdateEstimateModal, SingleEstimate } from "components/estimates" //hooks import useToast from "hooks/use-toast"; // ui -import { Loader, SecondaryButton } from "components/ui"; +import { EmptyState, Loader, SecondaryButton } from "components/ui"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; // icons import { PlusIcon } from "@heroicons/react/24/outline"; +// images +import emptyEstimate from "public/empty-state/empty-estimate.svg"; // types import { IEstimate, IProject } from "types"; import type { NextPage } from "next"; @@ -135,28 +137,39 @@ const EstimatesSettings: NextPage = () => {
    - {estimatesList && estimatesList.length > 0 && ( -
    - <> - {estimatesList ? ( - estimatesList.map((estimate) => ( - editEstimate(estimate)} - handleEstimateDelete={(estimateId) => removeEstimate(estimateId)} - /> - )) - ) : ( - - - - - - - )} - -
    + {estimatesList ? ( + estimatesList.length > 0 ? ( +
    + {estimatesList.map((estimate) => ( + editEstimate(estimate)} + handleEstimateDelete={(estimateId) => removeEstimate(estimateId)} + /> + ))} +
    + ) : ( +
    + { + setEstimateToUpdate(undefined); + setEstimateFormOpen(true); + }} + /> +
    + ) + ) : ( + + + + + + )} diff --git a/apps/app/public/empty-state/empty-estimate.svg b/apps/app/public/empty-state/empty-estimate.svg new file mode 100644 index 000000000..eb2926ad9 --- /dev/null +++ b/apps/app/public/empty-state/empty-estimate.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c9f866e5384ea28c713a4e00fdac86375f113028 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 24 Apr 2023 18:53:30 +0530 Subject: [PATCH 29/64] style: profile settings, activity theming (#951) --- apps/app/components/core/feeds.tsx | 25 ++++++++++-------- apps/app/components/issues/activity.tsx | 4 +-- .../issues/comment/comment-card.tsx | 9 +++---- .../[workspaceSlug]/me/profile/index.tsx | 26 ++++++++++--------- 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/apps/app/components/core/feeds.tsx b/apps/app/components/core/feeds.tsx index 0d68fd7a6..b5cb0bb78 100644 --- a/apps/app/components/core/feeds.tsx +++ b/apps/app/components/core/feeds.tsx @@ -67,15 +67,19 @@ const activityDetails: { }, name: { message: "set the name to", - icon:
    +
    {activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? ( @@ -219,7 +223,7 @@ export const Feeds: React.FC = ({ activities }) => ( @@ -242,9 +246,8 @@ export const Feeds: React.FC = ({ activities }) => ( : activity.old_value } editable={false} - onBlur={() => ({})} noBorder - customClassName="text-xs bg-brand-surface-1" + customClassName="text-xs border border-brand-base bg-brand-base" />
    @@ -268,7 +271,7 @@ export const Feeds: React.FC = ({ activities }) => (
    -
    +
    {activity.field ? ( activityDetails[activity.field as keyof typeof activityDetails]?.icon ) : activity.actor_detail.avatar && diff --git a/apps/app/components/issues/activity.tsx b/apps/app/components/issues/activity.tsx index f14ee2761..b3e419e17 100644 --- a/apps/app/components/issues/activity.tsx +++ b/apps/app/components/issues/activity.tsx @@ -110,9 +110,7 @@ const activityDetails: { }, }; -type Props = {}; - -export const IssueActivitySection: React.FC = () => { +export const IssueActivitySection: React.FC = () => { const router = useRouter(); const { workspaceSlug, projectId, issueId } = router.query; diff --git a/apps/app/components/issues/comment/comment-card.tsx b/apps/app/components/issues/comment/comment-card.tsx index b368812a3..0d9aae830 100644 --- a/apps/app/components/issues/comment/comment-card.tsx +++ b/apps/app/components/issues/comment/comment-card.tsx @@ -68,7 +68,10 @@ export const CommentCard: React.FC = ({ comment, onSubmit, handleCommentD )} -
    @@ -110,10 +113,6 @@ export const CommentCard: React.FC = ({ comment, onSubmit, handleCommentD
    ) : ( - //
    {
    -

    Profile Picture

    -

    +

    Profile Picture

    +

    Max file size is 5MB. Supported file types are .jpg and .png.

    @@ -156,7 +156,7 @@ const Profile: NextPage = () => {
    + } options={options} multiple noChevron @@ -531,6 +547,13 @@ const SinglePage: NextPage = () => { />
    )} + {labelModal && typeof projectId === "string" && ( + setLabelModal(false)} + projectId={projectId} + /> + )} ) : ( From 8c1ad69f0cb2f6fd0d6e2c818113df388f3254e5 Mon Sep 17 00:00:00 2001 From: Kunal Vishwakarma <116634168+kunalv17@users.noreply.github.com> Date: Tue, 25 Apr 2023 12:09:29 +0530 Subject: [PATCH 33/64] chore: changed pages empty state image (#954) --- apps/app/public/empty-state/empty-page.svg | 42 +++++++++++----------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/app/public/empty-state/empty-page.svg b/apps/app/public/empty-state/empty-page.svg index 8246b68c1..a2fd4cc46 100644 --- a/apps/app/public/empty-state/empty-page.svg +++ b/apps/app/public/empty-state/empty-page.svg @@ -1,40 +1,40 @@ - - - - - - - - + + + + + + + + - - - - - - - + + + + + + + - + - - + + - + - - + + From 529ed4432ca931c838c3e66488d376bb9414e052 Mon Sep 17 00:00:00 2001 From: Kunal Vishwakarma <116634168+kunalv17@users.noreply.github.com> Date: Tue, 25 Apr 2023 12:10:05 +0530 Subject: [PATCH 34/64] chore: fixed sidebar highlight not working (#952) --- .../components/project/single-sidebar-project.tsx | 4 ++-- apps/app/components/workspace/sidebar-menu.tsx | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/apps/app/components/project/single-sidebar-project.tsx b/apps/app/components/project/single-sidebar-project.tsx index 7c16b62bf..b2db09157 100644 --- a/apps/app/components/project/single-sidebar-project.tsx +++ b/apps/app/components/project/single-sidebar-project.tsx @@ -171,7 +171,7 @@ export const SingleSidebarProject: React.FC = ({ = ({
    { { + {children} + +); + +export const MarkdownRenderer: React.FC = ({ markdown, options = {} }) => { + const customComponents = { + h1: HeadingPrimary, + h3: HeadingSecondary, + p: Paragraph, + ol: OrderedList, + ul: UnorderedList, + a: Link, + }; + + return ( + + {markdown} + + ); +}; diff --git a/apps/app/package.json b/apps/app/package.json index 6911625b2..d3c414210 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -36,6 +36,7 @@ "react-dom": "18.2.0", "react-dropzone": "^14.2.3", "react-hook-form": "^7.38.0", + "react-markdown": "^8.0.7", "recharts": "^2.3.2", "remirror": "^2.0.23", "swr": "^1.3.0", From 1364c842e0b6b3553f9ec354830e29454777f638 Mon Sep 17 00:00:00 2001 From: anmolsinghbhatia Date: Tue, 25 Apr 2023 17:33:53 +0530 Subject: [PATCH 37/64] feat: product updates button added --- .../command-palette/command-pallette.tsx | 8 -------- apps/app/components/command-palette/index.ts | 1 - apps/app/components/ui/index.ts | 1 + .../product-updates-modal.tsx | 12 ++++++------ apps/app/pages/[workspaceSlug]/index.tsx | 15 ++++++++++++--- 5 files changed, 19 insertions(+), 18 deletions(-) rename apps/app/components/{command-palette => ui}/product-updates-modal.tsx (89%) diff --git a/apps/app/components/command-palette/command-pallette.tsx b/apps/app/components/command-palette/command-pallette.tsx index 101ae25fd..26555ddf1 100644 --- a/apps/app/components/command-palette/command-pallette.tsx +++ b/apps/app/components/command-palette/command-pallette.tsx @@ -45,7 +45,6 @@ import { ChangeIssuePriority, ChangeIssueAssignee, ChangeInterfaceTheme, - ProductUpdatesModal, } from "components/command-palette"; import { BulkDeleteIssuesModal } from "components/core"; import { CreateUpdateCycleModal } from "components/cycles"; @@ -75,7 +74,6 @@ export const CommandPalette: React.FC = () => { const [isIssueModalOpen, setIsIssueModalOpen] = useState(false); const [isProjectModalOpen, setIsProjectModalOpen] = useState(false); const [isShortcutsModalOpen, setIsShortcutsModalOpen] = useState(false); - const [isProductUpdatesModalOpen, setIsProductUpdatesModalOpen] = useState(false); const [isCreateCycleModalOpen, setIsCreateCycleModalOpen] = useState(false); const [isCreateViewModalOpen, setIsCreateViewModalOpen] = useState(false); const [isCreateModuleModalOpen, setIsCreateModuleModalOpen] = useState(false); @@ -223,8 +221,6 @@ export const CommandPalette: React.FC = () => { setIsCreateCycleModalOpen(true); } else if (keyPressed === "m") { setIsCreateModuleModalOpen(true); - } else if (keyPressed === "u") { - setIsProductUpdatesModalOpen(true); } } }, @@ -328,10 +324,6 @@ export const CommandPalette: React.FC = () => { return ( <> - {workspaceSlug && ( )} diff --git a/apps/app/components/command-palette/index.ts b/apps/app/components/command-palette/index.ts index 27bad1357..858aba401 100644 --- a/apps/app/components/command-palette/index.ts +++ b/apps/app/components/command-palette/index.ts @@ -4,4 +4,3 @@ export * from "./change-issue-state"; export * from "./change-issue-priority"; export * from "./change-issue-assignee"; export * from "./change-interface-theme"; -export * from "./product-updates-modal"; diff --git a/apps/app/components/ui/index.ts b/apps/app/components/ui/index.ts index 5c23ecdf5..3f50d1a21 100644 --- a/apps/app/components/ui/index.ts +++ b/apps/app/components/ui/index.ts @@ -21,3 +21,4 @@ export * from "./spinner"; export * from "./tooltip"; export * from "./toggle-switch"; export * from "./markdown-to-component"; +export * from "./product-updates-modal"; diff --git a/apps/app/components/command-palette/product-updates-modal.tsx b/apps/app/components/ui/product-updates-modal.tsx similarity index 89% rename from apps/app/components/command-palette/product-updates-modal.tsx rename to apps/app/components/ui/product-updates-modal.tsx index c099cf505..b37517343 100644 --- a/apps/app/components/command-palette/product-updates-modal.tsx +++ b/apps/app/components/ui/product-updates-modal.tsx @@ -18,7 +18,7 @@ export const ProductUpdatesModal: React.FC = ({ isOpen, setIsOpen }) => { console.log("updates:", updates); return ( - + = ({ isOpen, setIsOpen }) => { leaveFrom="opacity-100" leaveTo="opacity-0" > -
    +
    @@ -42,19 +42,19 @@ export const ProductUpdatesModal: React.FC = ({ isOpen, setIsOpen }) => { leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - -
    + +
    Product Updates diff --git a/apps/app/pages/[workspaceSlug]/index.tsx b/apps/app/pages/[workspaceSlug]/index.tsx index adf893f88..a39bc163a 100644 --- a/apps/app/pages/[workspaceSlug]/index.tsx +++ b/apps/app/pages/[workspaceSlug]/index.tsx @@ -15,6 +15,7 @@ import { IssuesPieChart, IssuesStats, } from "components/workspace"; +import { ProductUpdatesModal } from "components/ui"; // types import type { NextPage } from "next"; // fetch-keys @@ -22,6 +23,7 @@ import { USER_WORKSPACE_DASHBOARD } from "constants/fetch-keys"; const WorkspacePage: NextPage = () => { const [month, setMonth] = useState(new Date().getMonth() + 1); + const [isProductUpdatesModalOpen, setIsProductUpdatesModalOpen] = useState(false); const router = useRouter(); const { workspaceSlug } = router.query; @@ -39,6 +41,10 @@ const WorkspacePage: NextPage = () => { return ( +
    { Plane is open source, support us by starring us on GitHub.

    - {/* - View roadmap - */} + Date: Tue, 25 Apr 2023 18:02:13 +0530 Subject: [PATCH 38/64] feat: product updates tag and date added --- .../components/ui/product-updates-modal.tsx | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/apps/app/components/ui/product-updates-modal.tsx b/apps/app/components/ui/product-updates-modal.tsx index b37517343..b3388d31e 100644 --- a/apps/app/components/ui/product-updates-modal.tsx +++ b/apps/app/components/ui/product-updates-modal.tsx @@ -3,10 +3,14 @@ import useSWR from "swr"; // headless ui import { Dialog, Transition } from "@headlessui/react"; +// component +import { MarkdownRenderer, Spinner } from "components/ui"; // icons import { XMarkIcon } from "@heroicons/react/20/solid"; +// services import workspaceService from "services/workspace.service"; -import { MarkdownRenderer } from "components/ui"; +// helper +import { renderLongDateFormat } from "helpers/date-time.helper"; type Props = { isOpen: boolean; @@ -15,7 +19,6 @@ type Props = { export const ProductUpdatesModal: React.FC = ({ isOpen, setIsOpen }) => { const { data: updates } = useSWR("PRODUCT_UPDATES", () => workspaceService.getProductUpdates()); - console.log("updates:", updates); return ( @@ -60,9 +63,29 @@ export const ProductUpdatesModal: React.FC = ({ isOpen, setIsOpen }) => { - {updates && - updates.length > 0 && - updates.map((item: any) => )} + {updates && updates.length > 0 ? ( + updates.map((item: any, index: number) => ( + <> +
    + + {item.tag_name} + + {renderLongDateFormat(item.published_at)} + {index === 0 && ( + + New + + )} +
    + + + )) + ) : ( +
    + + Loading... +
    + )}
    From 3480b450f24e1642b79d082c1b040ee20090e879 Mon Sep 17 00:00:00 2001 From: anmolsinghbhatia Date: Tue, 25 Apr 2023 18:10:23 +0530 Subject: [PATCH 39/64] fix: build fix --- apps/app/pages/[workspaceSlug]/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/app/pages/[workspaceSlug]/index.tsx b/apps/app/pages/[workspaceSlug]/index.tsx index a39bc163a..5edef60eb 100644 --- a/apps/app/pages/[workspaceSlug]/index.tsx +++ b/apps/app/pages/[workspaceSlug]/index.tsx @@ -59,7 +59,7 @@ const WorkspacePage: NextPage = () => { onClick={() => setIsProductUpdatesModalOpen(true)} className="rounded-md border-2 border-brand-base px-3 py-1.5 text-sm font-medium duration-300" > - What's New? + {`What's New?`} Date: Tue, 25 Apr 2023 18:35:58 +0530 Subject: [PATCH 40/64] style: made issue list view responsive --- .../core/list-view/single-issue.tsx | 36 ++++++++++--------- apps/app/layouts/app-layout/app-header.tsx | 4 +-- .../projects/[projectId]/issues/index.tsx | 9 +++-- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/apps/app/components/core/list-view/single-issue.tsx b/apps/app/components/core/list-view/single-issue.tsx index 71ccc9db4..67978fe92 100644 --- a/apps/app/components/core/list-view/single-issue.tsx +++ b/apps/app/components/core/list-view/single-issue.tsx @@ -216,7 +216,7 @@ export const SingleListIssue: React.FC = ({
    { e.preventDefault(); setContextMenu(true); @@ -224,26 +224,28 @@ export const SingleListIssue: React.FC = ({ }} > - - {properties.key && ( - - - {issue.project_detail?.identifier}-{issue.sequence_id} + -
    +
    {properties.priority && ( = ({ breadcrumbs, left, right, setToggleSidebar }) => ( -
    -
    +
    +
    From d021a5696a13fa25d0f52cff9cd4c24057e71c82 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Tue, 25 Apr 2023 19:00:20 +0530 Subject: [PATCH 41/64] fix: sub issues list fix (#960) --- .../app/components/issues/sub-issues-list.tsx | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/apps/app/components/issues/sub-issues-list.tsx b/apps/app/components/issues/sub-issues-list.tsx index 456140158..d4a5dd4b3 100644 --- a/apps/app/components/issues/sub-issues-list.tsx +++ b/apps/app/components/issues/sub-issues-list.tsx @@ -57,22 +57,6 @@ export const SubIssuesList: FC = ({ parentIssue }) => { : null ); - const updateSubIssuesState = (data: IIssue[]) => { - const backlogCount = data.filter((i) => i.state_detail.group === "backlog").length; - const unstartedCount = data.filter((i) => i.state_detail.group === "unstarted").length; - const startedCount = data.filter((i) => i.state_detail.group === "started").length; - const completedCount = data.filter((i) => i.state_detail.group === "completed").length; - const cancelledCount = data.filter((i) => i.state_detail.group === "cancelled").length; - - return { - backlog: backlogCount ?? 0, - unstarted: unstartedCount ?? 0, - started: startedCount ?? 0, - completed: completedCount ?? 0, - cancelled: cancelledCount ?? 0, - }; - }; - const addAsSubIssue = async (data: { issues: string[] }) => { if (!workspaceSlug || !projectId) return; @@ -86,13 +70,21 @@ export const SubIssuesList: FC = ({ parentIssue }) => { (prevData) => { if (!prevData) return prevData; let newSubIssues = prevData.sub_issues as IIssue[]; + + const stateDistribution = { ...prevData.state_distribution }; + data.issues.forEach((issueId: string) => { const issue = issues?.find((i) => i.id === issueId); - if (issue) newSubIssues.push(issue); + if (issue) { + newSubIssues.push(issue); + const issueGroup = issue.state_detail.group; + stateDistribution[issueGroup] = stateDistribution[issueGroup] + 1; + } }); + newSubIssues = orderArrayBy(newSubIssues, "created_at", "descending"); return { - state_distribution: updateSubIssuesState(newSubIssues), + state_distribution: stateDistribution, sub_issues: newSubIssues, }; }, @@ -129,8 +121,12 @@ export const SubIssuesList: FC = ({ parentIssue }) => { (prevData) => { if (!prevData) return prevData; const updatedArray = (prevData.sub_issues ?? []).filter((i) => i.id !== issueId); + + const stateDistribution = { ...prevData.state_distribution }; + const issueGroup = issues?.find((i) => i.id === issueId)?.state_detail.group ?? "backlog"; + stateDistribution[issueGroup] = stateDistribution[issueGroup] - 1; return { - state_distribution: updateSubIssuesState(updatedArray), + state_distribution: stateDistribution, sub_issues: updatedArray, }; }, @@ -222,7 +218,7 @@ export const SubIssuesList: FC = ({ parentIssue }) => { {subIssuesResponse.state_distribution && ( -
    +
    = ({ parentIssue }) => { }} />
    - + {isNaN(completionPercentage) ? 0 : completionPercentage > 100 From 028a350cd1e9424aa7df8367320ba13e9260c048 Mon Sep 17 00:00:00 2001 From: Dakshesh Jain Date: Tue, 25 Apr 2023 19:43:52 +0530 Subject: [PATCH 42/64] style: issue list dropdown on next line --- apps/app/components/core/list-view/single-issue.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/app/components/core/list-view/single-issue.tsx b/apps/app/components/core/list-view/single-issue.tsx index 67978fe92..a45e3d29c 100644 --- a/apps/app/components/core/list-view/single-issue.tsx +++ b/apps/app/components/core/list-view/single-issue.tsx @@ -216,7 +216,7 @@ export const SingleListIssue: React.FC = ({
    { e.preventDefault(); setContextMenu(true); @@ -224,7 +224,7 @@ export const SingleListIssue: React.FC = ({ }} > -
    + -
    +
    {properties.priority && ( Date: Fri, 28 Apr 2023 17:48:40 +0530 Subject: [PATCH 43/64] fix: cycle & module sidebar default tab fix (#964) --- apps/app/components/core/sidebar/sidebar-progress-stats.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/app/components/core/sidebar/sidebar-progress-stats.tsx b/apps/app/components/core/sidebar/sidebar-progress-stats.tsx index 84b79e4df..3ddf24fe4 100644 --- a/apps/app/components/core/sidebar/sidebar-progress-stats.tsx +++ b/apps/app/components/core/sidebar/sidebar-progress-stats.tsx @@ -78,7 +78,7 @@ export const SidebarProgressStats: React.FC = ({ return 2; default: - return 3; + return 0; } }; return ( @@ -94,7 +94,7 @@ export const SidebarProgressStats: React.FC = ({ return setTab("States"); default: - return setTab("States"); + return setTab("Assignees"); } }} > From f0f24b6fc4f3ce1fb0e1a3f3913befcbcd1260c1 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Fri, 28 Apr 2023 17:49:04 +0530 Subject: [PATCH 44/64] style: module , cycle & icon styling (#963) * style: target icon updated * style: cycle card theming * style: module card theming * style: no current cycle message --- apps/app/components/cycles/cycles-list.tsx | 12 ++----- .../components/cycles/single-cycle-card.tsx | 20 +++++------ apps/app/components/icons/target-icon.tsx | 26 ++++++++------ .../components/modules/single-module-card.tsx | 36 ++++++++++--------- 4 files changed, 49 insertions(+), 45 deletions(-) diff --git a/apps/app/components/cycles/cycles-list.tsx b/apps/app/components/cycles/cycles-list.tsx index 68592b051..f8d607dbe 100644 --- a/apps/app/components/cycles/cycles-list.tsx +++ b/apps/app/components/cycles/cycles-list.tsx @@ -25,7 +25,6 @@ export const CyclesList: React.FC = ({ }) => { const [cycleDeleteModal, setCycleDeleteModal] = useState(false); const [selectedCycleForDelete, setSelectedCycleForDelete] = useState(); - const [showNoCurrentCycleMessage, setShowNoCurrentCycleMessage] = useState(true); const handleDeleteCycle = (cycle: ICycle) => { setSelectedCycleForDelete({ ...cycle, actionType: "delete" }); @@ -61,14 +60,9 @@ export const CyclesList: React.FC = ({ ))}
    ) : type === "current" ? ( - showNoCurrentCycleMessage && ( -
    -

    No current cycle is present.

    - -
    - ) +
    +

    No current cycle is present.

    +
    ) : ( = ({ return (
    -
    +
    @@ -269,20 +269,20 @@ export const SingleCycleCard: React.FC = ({ )}
    -
    +
    - - Start : + + Start : {renderShortDateWithYearFormat(startDate)}
    - - End : + + End : {renderShortDateWithYearFormat(endDate)}
    -
    +
    {cycle.owned_by.avatar && cycle.owned_by.avatar !== "" ? ( = ({ alt={cycle.owned_by.first_name} /> ) : ( - + {cycle.owned_by.first_name.charAt(0)} )} - {cycle.owned_by.first_name} + {cycle.owned_by.first_name}
    {!isCompleted && ( @@ -350,7 +350,7 @@ export const SingleCycleCard: React.FC = ({ {({ open }) => (
    diff --git a/apps/app/components/icons/target-icon.tsx b/apps/app/components/icons/target-icon.tsx index c9fbde869..e27c21d7a 100644 --- a/apps/app/components/icons/target-icon.tsx +++ b/apps/app/components/icons/target-icon.tsx @@ -6,15 +6,21 @@ export const TargetIcon: React.FC = ({ width = "24", height = "24", className, - color = "black", + color = "#858E96", }) => ( - - - - - - - - + + + -); \ No newline at end of file +); diff --git a/apps/app/components/modules/single-module-card.tsx b/apps/app/components/modules/single-module-card.tsx index 030f51c0d..f7192de6f 100644 --- a/apps/app/components/modules/single-module-card.tsx +++ b/apps/app/components/modules/single-module-card.tsx @@ -14,7 +14,13 @@ import { DeleteModuleModal } from "components/modules"; // ui import { AssigneesList, Avatar, CustomMenu, Tooltip } from "components/ui"; // icons -import { LinkIcon, PencilIcon, StarIcon, TrashIcon } from "@heroicons/react/24/outline"; +import { + CalendarDaysIcon, + LinkIcon, + PencilIcon, + StarIcon, + TrashIcon, +} from "@heroicons/react/24/outline"; import { CalendarMonthIcon, TargetIcon } from "components/icons"; // helpers @@ -122,7 +128,7 @@ export const SingleModuleCard: React.FC = ({ module, handleEditModule }) setIsOpen={setModuleDeleteModal} data={module} /> -
    +
    @@ -137,7 +143,7 @@ export const SingleModuleCard: React.FC = ({ module, handleEditModule })
    -
    +
    {module?.status?.replace("-", " ")}
    {module.is_favorite ? ( @@ -172,24 +178,24 @@ export const SingleModuleCard: React.FC = ({ module, handleEditModule })
    -
    +
    - - Start: + + Start: {renderShortDateWithYearFormat(startDate)}
    - - End: + + End: {renderShortDateWithYearFormat(endDate)}
    -
    -
    +
    +
    Progress -
    +
    = ({ module, handleEditModule })
    {isNaN(completionPercentage) ? 0 : completionPercentage.toFixed(0)}%
    -
    -

    +

    +

    Last updated: - - {renderShortDateWithYearFormat(lastUpdated)} - + {renderShortDateWithYearFormat(lastUpdated)}

    {module.members_detail.length > 0 && (
    From 0b9b4bb289d69d3e222dd6aa15bb2427489b591b Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Fri, 28 Apr 2023 17:49:16 +0530 Subject: [PATCH 45/64] fix: cycle & module mutation , feat: cycle & module toast alerts (#962) * fix: cycle remove issue mutation * fix: module remove issue mutation * feat: issue removed toast alert added --- .../components/core/board-view/all-boards.tsx | 2 +- .../core/board-view/single-board.tsx | 5 +- apps/app/components/core/issues-view.tsx | 54 +++++++++++++++++-- .../components/core/list-view/all-lists.tsx | 2 +- .../components/core/list-view/single-list.tsx | 4 +- 5 files changed, 57 insertions(+), 10 deletions(-) diff --git a/apps/app/components/core/board-view/all-boards.tsx b/apps/app/components/core/board-view/all-boards.tsx index 19d0e7636..69346cd18 100644 --- a/apps/app/components/core/board-view/all-boards.tsx +++ b/apps/app/components/core/board-view/all-boards.tsx @@ -17,7 +17,7 @@ type Props = { openIssuesListModal?: (() => void) | null; handleDeleteIssue: (issue: IIssue) => void; handleTrashBox: (isDragging: boolean) => void; - removeIssue: ((bridgeId: string) => void) | null; + removeIssue: ((bridgeId: string, issueId: string) => void) | null; isCompleted?: boolean; userAuth: UserAuth; }; diff --git a/apps/app/components/core/board-view/single-board.tsx b/apps/app/components/core/board-view/single-board.tsx index 0c13d8bcf..76226f0cf 100644 --- a/apps/app/components/core/board-view/single-board.tsx +++ b/apps/app/components/core/board-view/single-board.tsx @@ -29,7 +29,7 @@ type Props = { handleDeleteIssue: (issue: IIssue) => void; openIssuesListModal?: (() => void) | null; handleTrashBox: (isDragging: boolean) => void; - removeIssue: ((bridgeId: string) => void) | null; + removeIssue: ((bridgeId: string, issueId: string) => void) | null; isCompleted?: boolean; userAuth: UserAuth; }; @@ -130,7 +130,8 @@ export const SingleBoard: React.FC = ({ handleDeleteIssue={handleDeleteIssue} handleTrashBox={handleTrashBox} removeIssue={() => { - if (removeIssue && issue.bridge_id) removeIssue(issue.bridge_id); + if (removeIssue && issue.bridge_id) + removeIssue(issue.bridge_id, issue.id); }} isCompleted={isCompleted} userAuth={userAuth} diff --git a/apps/app/components/core/issues-view.tsx b/apps/app/components/core/issues-view.tsx index f063de373..497d7fba4 100644 --- a/apps/app/components/core/issues-view.tsx +++ b/apps/app/components/core/issues-view.tsx @@ -314,10 +314,26 @@ export const IssuesView: React.FC = ({ ); const removeIssueFromCycle = useCallback( - (bridgeId: string) => { + (bridgeId: string, issueId: string) => { if (!workspaceSlug || !projectId || !cycleId) return; - mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params)); + mutate( + CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params), + (prevData: any) => { + if (!prevData) return prevData; + if (selectedGroup) { + const filteredData: any = {}; + for (const key in prevData) { + filteredData[key] = prevData[key].filter((item: any) => item.id !== issueId); + } + return filteredData; + } else { + const filteredData = prevData.filter((i: any) => i.id !== issueId); + return filteredData; + } + }, + false + ); issuesService .removeIssueFromCycle( @@ -326,6 +342,13 @@ export const IssuesView: React.FC = ({ cycleId as string, bridgeId ) + .then(() => { + setToastAlert({ + title: "Success", + message: "Issue removed successfully.", + type: "success", + }); + }) .catch((e) => { console.log(e); }); @@ -334,10 +357,26 @@ export const IssuesView: React.FC = ({ ); const removeIssueFromModule = useCallback( - (bridgeId: string) => { + (bridgeId: string, issueId: string) => { if (!workspaceSlug || !projectId || !moduleId) return; - mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params)); + mutate( + MODULE_ISSUES_WITH_PARAMS(moduleId as string, params), + (prevData: any) => { + if (!prevData) return prevData; + if (selectedGroup) { + const filteredData: any = {}; + for (const key in prevData) { + filteredData[key] = prevData[key].filter((item: any) => item.id !== issueId); + } + return filteredData; + } else { + const filteredData = prevData.filter((item: any) => item.id !== issueId); + return filteredData; + } + }, + false + ); modulesService .removeIssueFromModule( @@ -346,6 +385,13 @@ export const IssuesView: React.FC = ({ moduleId as string, bridgeId ) + .then(() => { + setToastAlert({ + title: "Success", + message: "Issue removed successfully.", + type: "success", + }); + }) .catch((e) => { console.log(e); }); diff --git a/apps/app/components/core/list-view/all-lists.tsx b/apps/app/components/core/list-view/all-lists.tsx index 2ea40ffe4..d8fa8b9ee 100644 --- a/apps/app/components/core/list-view/all-lists.tsx +++ b/apps/app/components/core/list-view/all-lists.tsx @@ -14,7 +14,7 @@ type Props = { handleEditIssue: (issue: IIssue) => void; handleDeleteIssue: (issue: IIssue) => void; openIssuesListModal?: (() => void) | null; - removeIssue: ((bridgeId: string) => void) | null; + removeIssue: ((bridgeId: string, issueId: string) => void) | null; isCompleted?: boolean; userAuth: UserAuth; }; diff --git a/apps/app/components/core/list-view/single-list.tsx b/apps/app/components/core/list-view/single-list.tsx index c39e6a56b..abe82ed73 100644 --- a/apps/app/components/core/list-view/single-list.tsx +++ b/apps/app/components/core/list-view/single-list.tsx @@ -37,7 +37,7 @@ type Props = { handleEditIssue: (issue: IIssue) => void; handleDeleteIssue: (issue: IIssue) => void; openIssuesListModal?: (() => void) | null; - removeIssue: ((bridgeId: string) => void) | null; + removeIssue: ((bridgeId: string, issueId: string) => void) | null; isCompleted?: boolean; userAuth: UserAuth; }; @@ -204,7 +204,7 @@ export const SingleList: React.FC = ({ makeIssueCopy={() => makeIssueCopy(issue)} handleDeleteIssue={handleDeleteIssue} removeIssue={() => { - if (removeIssue !== null && issue.bridge_id) removeIssue(issue.bridge_id); + if (removeIssue !== null && issue.bridge_id) removeIssue(issue.bridge_id, issue.id); }} isCompleted={isCompleted} userAuth={userAuth} From 3175ce9136146b91a4f92320bf97e181c9bbb4db Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Fri, 28 Apr 2023 17:49:29 +0530 Subject: [PATCH 46/64] style: create project modal (#957) --- .../components/core/image-picker-popover.tsx | 4 +- .../project/create-project-modal.tsx | 80 +++++++++---------- 2 files changed, 40 insertions(+), 44 deletions(-) diff --git a/apps/app/components/core/image-picker-popover.tsx b/apps/app/components/core/image-picker-popover.tsx index 9a3d5ff2f..8b2edf3fb 100644 --- a/apps/app/components/core/image-picker-popover.tsx +++ b/apps/app/components/core/image-picker-popover.tsx @@ -65,7 +65,7 @@ export const ImagePickerPopover: React.FC = ({ label, value, onChange }) return ( setIsOpen((prev) => !prev)} > {label} @@ -79,7 +79,7 @@ export const ImagePickerPopover: React.FC = ({ label, value, onChange }) leaveFrom="transform opacity-100 scale-100" leaveTo="transform opacity-0 scale-95" > - +
    diff --git a/apps/app/components/project/create-project-modal.tsx b/apps/app/components/project/create-project-modal.tsx index 1b82210bd..94b088133 100644 --- a/apps/app/components/project/create-project-modal.tsx +++ b/apps/app/components/project/create-project-modal.tsx @@ -231,7 +231,6 @@ export const CreateProjectModal: React.FC = (props) => { error={errors.name} register={register} className="text-xl" - mode="transparent" validations={{ required: "Name is required", maxLength: { @@ -243,46 +242,32 @@ export const CreateProjectModal: React.FC = (props) => {
    -
    - setIsChangeIdentifierRequired(false)} - validations={{ - required: "Identifier is required", - validate: (value) => - /^[A-Z]+$/.test(value) || "Identifier must be uppercase text.", - minLength: { - value: 1, - message: "Identifier must at least be of 1 character", - }, - maxLength: { - value: 5, - message: "Identifier must at most be of 5 characters", - }, - }} - /> -
    - -
    -