From 1026ae3eb18cab84f2d8ac9068c2b1f8ea92a654 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Sat, 8 Apr 2023 18:05:54 +0530 Subject: [PATCH] chore: user auth layer (#749) * chore: use estimate points hook created * chore: user auth layer * fix: build error --- .../core/board-view/single-issue.tsx | 14 +- .../components/estimates/single-estimate.tsx | 155 +++++++------- apps/app/components/issues/index.ts | 1 + .../issues/view-select/estimate.tsx | 69 ++++++ .../components/issues/view-select/index.ts | 1 + apps/app/contexts/user.context.tsx | 13 +- apps/app/hooks/use-estimate-option.tsx | 49 +++++ apps/app/hooks/use-issue-properties.tsx | 2 + apps/app/hooks/use-my-issues-filter.tsx | 1 + apps/app/hooks/use-project-details.tsx | 42 ++++ apps/app/hooks/use-user.tsx | 35 +--- .../project-authorization-wrapper.tsx | 149 ++++++------- .../user-authorization-wrapper.tsx | 28 +++ .../workspace-authorization-wrapper.tsx | 119 ++++++----- .../[projectId]/settings/estimates.tsx | 25 +-- apps/app/pages/create-workspace.tsx | 52 ++--- apps/app/pages/invitations.tsx | 198 ++++++++---------- apps/app/pages/onboarding.tsx | 130 +++++------- apps/app/services/user.service.ts | 4 +- apps/app/types/issues.d.ts | 3 +- 20 files changed, 603 insertions(+), 487 deletions(-) create mode 100644 apps/app/components/issues/view-select/estimate.tsx create mode 100644 apps/app/hooks/use-estimate-option.tsx create mode 100644 apps/app/hooks/use-project-details.tsx create mode 100644 apps/app/layouts/auth-layout/user-authorization-wrapper.tsx diff --git a/apps/app/components/core/board-view/single-issue.tsx b/apps/app/components/core/board-view/single-issue.tsx index 6c965a737..9158d6d1f 100644 --- a/apps/app/components/core/board-view/single-issue.tsx +++ b/apps/app/components/core/board-view/single-issue.tsx @@ -21,9 +21,10 @@ import useToast from "hooks/use-toast"; import { ViewAssigneeSelect, ViewDueDateSelect, + ViewEstimateSelect, ViewPrioritySelect, ViewStateSelect, -} from "components/issues/view-select"; +} from "components/issues"; // ui import { ContextMenu, CustomMenu } from "components/ui"; // icons @@ -48,6 +49,7 @@ import { MODULE_ISSUES_WITH_PARAMS, PROJECT_ISSUES_LIST_WITH_PARAMS, } from "constants/fetch-keys"; +import useEstimateOption from "hooks/use-estimate-option"; type Props = { type?: string; @@ -90,6 +92,8 @@ export const SingleBoardIssue: React.FC = ({ const { orderBy, params } = useIssuesView(); + const { estimateValue } = useEstimateOption(issue.estimate_point); + const router = useRouter(); const { workspaceSlug, projectId, cycleId, moduleId } = router.query; @@ -342,6 +346,14 @@ export const SingleBoardIssue: React.FC = ({ selfPositioned /> )} + {properties.estimate && ( + + )} diff --git a/apps/app/components/estimates/single-estimate.tsx b/apps/app/components/estimates/single-estimate.tsx index d20ef1db8..4741dc3f0 100644 --- a/apps/app/components/estimates/single-estimate.tsx +++ b/apps/app/components/estimates/single-estimate.tsx @@ -1,44 +1,41 @@ import React, { useState } from "react"; -// ui -import { CustomMenu, PrimaryButton } from "components/ui"; -// types -import { IEstimate, IProject } from "types"; -//icons -import { PencilIcon, TrashIcon, SquaresPlusIcon, ListBulletIcon } from "@heroicons/react/24/outline"; - -import useSWR, { mutate } from "swr"; - -import useToast from "hooks/use-toast"; - -import estimatesService from "services/estimates.service"; -import projectService from "services/project.service"; - -import { EstimatePointsModal } from "./estimate-points-modal"; import { useRouter } from "next/router"; -import { ESTIMATE_POINTS_LIST } from "constants/fetch-keys"; -import { PlusIcon } from "components/icons"; +import useSWR from "swr"; -interface IEstimatePoints { - key: string; - value: string; -} +// 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 } from "components/estimates"; +// ui +import { CustomMenu } from "components/ui"; +//icons +import { + PencilIcon, + TrashIcon, + SquaresPlusIcon, + ListBulletIcon, +} from "@heroicons/react/24/outline"; +// types +import { IEstimate, IProject } from "types"; +// fetch-keys +import { ESTIMATE_POINTS_LIST } from "constants/fetch-keys"; type Props = { estimate: IEstimate; editEstimate: (estimate: IEstimate) => void; handleEstimateDelete: (estimateId: string) => void; - activeEstimate: IEstimate | null; - setActiveEstimate: React.Dispatch>; }; export const SingleEstimate: React.FC = ({ estimate, editEstimate, handleEstimateDelete, - activeEstimate, - setActiveEstimate, }) => { const [isEstimatePointsModalOpen, setIsEstimatePointsModalOpen] = useState(false); @@ -47,6 +44,8 @@ export const SingleEstimate: React.FC = ({ const { setToastAlert } = useToast(); + const { projectDetails, mutateProjectDetails } = useProjectDetails(); + const { data: estimatePoints } = useSWR( workspaceSlug && projectId ? ESTIMATE_POINTS_LIST(estimate.id) : null, workspaceSlug && projectId @@ -59,12 +58,19 @@ export const SingleEstimate: React.FC = ({ : null ); - const handleActiveEstimate = async () => { - if (!workspaceSlug || !projectId || !estimate) return; - const payload: Partial = { + const handleUseEstimate = async () => { + if (!workspaceSlug || !projectId) return; + + const payload = { estimate: estimate.id, }; - setActiveEstimate(estimate); + + mutateProjectDetails((prevData) => { + if (!prevData) return prevData; + + return { ...prevData, estimate: estimate.id }; + }, false); + await projectService .updateProject(workspaceSlug as string, projectId as string, payload) .catch(() => { @@ -77,56 +83,63 @@ export const SingleEstimate: React.FC = ({ }; return ( -
+ <> setIsEstimatePointsModalOpen(false)} /> -
+
-
-
{estimate.name}
+
+
+ {estimate.name} + {projectDetails?.estimate && projectDetails?.estimate === estimate.id && ( + + In use + + )} +

{estimate.description}

-
- - - - + + {projectDetails?.estimate && projectDetails?.estimate !== estimate.id && ( + +
+ Use estimate - +
- setIsEstimatePointsModalOpen(true)}> - - - {estimatePoints?.length === 8 ? "Update points" : "Create points"} - - - { - editEstimate(estimate); - }} - > - - - Edit estimate - - - { - handleEstimateDelete(estimate.id); - }} - > - - - Delete estimate - - -
-
+ )} + setIsEstimatePointsModalOpen(true)}> +
+ + {estimatePoints?.length === 8 ? "Update points" : "Create points"} +
+
+ { + editEstimate(estimate); + }} + > +
+ + Edit estimate +
+
+ { + handleEstimateDelete(estimate.id); + }} + > +
+ + Delete estimate +
+
+
{estimatePoints && estimatePoints.length > 0 ? (
@@ -140,11 +153,11 @@ export const SingleEstimate: React.FC = ({ {estimatePoints.length > 0 && ")"}
) : ( -
-

No estimate points

-
+
+

No estimate points

+
)}
-
+ ); }; diff --git a/apps/app/components/issues/index.ts b/apps/app/components/issues/index.ts index 1b6c63b4a..c5322645f 100644 --- a/apps/app/components/issues/index.ts +++ b/apps/app/components/issues/index.ts @@ -1,5 +1,6 @@ export * from "./comment"; export * from "./sidebar-select"; +export * from "./view-select"; export * from "./activity"; export * from "./delete-issue-modal"; export * from "./description-form"; diff --git a/apps/app/components/issues/view-select/estimate.tsx b/apps/app/components/issues/view-select/estimate.tsx new file mode 100644 index 000000000..bdc916cff --- /dev/null +++ b/apps/app/components/issues/view-select/estimate.tsx @@ -0,0 +1,69 @@ +import React from "react"; + +// ui +import { CustomSelect, Tooltip } from "components/ui"; +// icons +import { getPriorityIcon } from "components/icons/priority-icon"; +// types +import { IIssue } from "types"; +// constants +import { PRIORITIES } from "constants/project"; +// services +import trackEventServices from "services/track-event.service"; +import useEstimateOption from "hooks/use-estimate-option"; + +type Props = { + issue: IIssue; + partialUpdateIssue: (formData: Partial) => void; + position?: "left" | "right"; + selfPositioned?: boolean; + isNotAllowed: boolean; +}; + +export const ViewEstimateSelect: React.FC = ({ + issue, + partialUpdateIssue, + position = "left", + selfPositioned = false, + isNotAllowed, +}) => { + const { isEstimateActive, estimatePoints, estimateValue } = useEstimateOption( + issue.estimate_point + ); + + return ( + { + partialUpdateIssue({ priority: data, state: issue.state, target_date: issue.target_date }); + trackEventServices.trackIssuePartialPropertyUpdateEvent( + { + workspaceSlug: issue.workspace_detail.slug, + workspaceId: issue.workspace_detail.id, + projectId: issue.project_detail.id, + projectIdentifier: issue.project_detail.identifier, + projectName: issue.project_detail.name, + issueId: issue.id, + }, + "ISSUE_PROPERTY_UPDATE_PRIORITY" + ); + }} + label={ + + <>{estimateValue} + + } + maxHeight="md" + noChevron + disabled={isNotAllowed} + position={position} + selfPositioned={selfPositioned} + > + {estimatePoints?.map((estimate) => ( + + <>{estimate.value} + + ))} + + ); +}; diff --git a/apps/app/components/issues/view-select/index.ts b/apps/app/components/issues/view-select/index.ts index c5f427971..55ecfcbdb 100644 --- a/apps/app/components/issues/view-select/index.ts +++ b/apps/app/components/issues/view-select/index.ts @@ -1,4 +1,5 @@ export * from "./assignee"; export * from "./due-date"; +export * from "./estimate"; export * from "./priority"; export * from "./state"; diff --git a/apps/app/contexts/user.context.tsx b/apps/app/contexts/user.context.tsx index 1b8b7bca6..6aa98d3cc 100644 --- a/apps/app/contexts/user.context.tsx +++ b/apps/app/contexts/user.context.tsx @@ -1,13 +1,11 @@ import React, { createContext, ReactElement } from "react"; -// next -import { useRouter } from "next/router"; -// swr + import useSWR, { KeyedMutator } from "swr"; + // services import userService from "services/user.service"; // constants import { CURRENT_USER } from "constants/fetch-keys"; - // types import type { IUser } from "types"; @@ -22,18 +20,11 @@ interface IUserContextProps { export const UserContext = createContext({} as IUserContextProps); export const UserProvider = ({ children }: { children: ReactElement }) => { - const router = useRouter(); - // API to fetch user information const { data, error, mutate } = useSWR(CURRENT_USER, () => userService.currentUser(), { shouldRetryOnError: false, }); - if (error) { - router.push("/signin"); - return null; - } - return ( { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { projectDetails } = useProjectDetails(); + + const { data: estimatePoints, error: estimatePointsError } = useSWR( + workspaceSlug && projectId && projectDetails && projectDetails?.estimate + ? ESTIMATE_POINTS_LIST(projectDetails.estimate as string) + : null, + workspaceSlug && projectId && projectDetails && projectDetails.estimate + ? () => + estimatesService.getEstimatesPointsList( + workspaceSlug as string, + projectId as string, + projectDetails.estimate + ) + : null + ); + + const estimateValue: any = + (estimateKey && estimatePoints?.find((e) => e.key === estimateKey)?.value) ?? "None"; + + useEffect(() => { + if (estimatePointsError?.status === 404) router.push("/404"); + else if (estimatePointsError) router.push("/error"); + }, [estimatePointsError, router]); + + return { + isEstimateActive: projectDetails?.estimate ? true : false, + estimatePoints, + estimateValue, + }; +}; + +export default useEstimateOption; diff --git a/apps/app/hooks/use-issue-properties.tsx b/apps/app/hooks/use-issue-properties.tsx index b3d389137..5c8a9402d 100644 --- a/apps/app/hooks/use-issue-properties.tsx +++ b/apps/app/hooks/use-issue-properties.tsx @@ -15,6 +15,7 @@ const initialValues: Properties = { priority: false, state: true, sub_issue_count: false, + estimate: false, }; const useIssuesProperties = (workspaceSlug?: string, projectId?: string) => { @@ -90,6 +91,7 @@ const useIssuesProperties = (workspaceSlug?: string, projectId?: string) => { priority: properties.priority, state: properties.state, sub_issue_count: properties.sub_issue_count, + estimate: properties.estimate, }; return [newProperties, updateIssueProperties] as const; diff --git a/apps/app/hooks/use-my-issues-filter.tsx b/apps/app/hooks/use-my-issues-filter.tsx index f382ada77..b1991f30b 100644 --- a/apps/app/hooks/use-my-issues-filter.tsx +++ b/apps/app/hooks/use-my-issues-filter.tsx @@ -24,6 +24,7 @@ const initialValues: Properties = { priority: false, state: true, sub_issue_count: false, + estimate: false, }; // TODO: Refactor this logic diff --git a/apps/app/hooks/use-project-details.tsx b/apps/app/hooks/use-project-details.tsx new file mode 100644 index 000000000..c18890311 --- /dev/null +++ b/apps/app/hooks/use-project-details.tsx @@ -0,0 +1,42 @@ +import { useEffect } from "react"; + +import { useRouter } from "next/router"; + +import useSWR from "swr"; + +// services +import projectService from "services/project.service"; +// fetch-keys +import { PROJECT_DETAILS } from "constants/fetch-keys"; + +const useProjectDetails = () => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { + data: projectDetails, + error: projectDetailsError, + mutate: mutateProjectDetails, + } = useSWR( + workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null, + workspaceSlug && projectId + ? () => projectService.getProject(workspaceSlug as string, projectId as string) + : null + ); + + useEffect(() => { + if (projectDetailsError?.status === 404) { + router.push("/404"); + } else if (projectDetailsError) { + router.push("/error"); + } + }, [projectDetailsError, router]); + + return { + projectDetails, + projectDetailsError, + mutateProjectDetails, + }; +}; + +export default useProjectDetails; diff --git a/apps/app/hooks/use-user.tsx b/apps/app/hooks/use-user.tsx index 9d2c6d162..e59cb32e5 100644 --- a/apps/app/hooks/use-user.tsx +++ b/apps/app/hooks/use-user.tsx @@ -1,40 +1,11 @@ -import { useContext, useEffect } from "react"; -import { useRouter } from "next/router"; +import { useContext } from "react"; + // context import { UserContext } from "contexts/user.context"; -interface useUserOptions { - redirectTo?: string; -} - -const useUser = (options: useUserOptions = {}) => { - // props - const { redirectTo = null } = options; +const useUser = () => { // context const contextData = useContext(UserContext); - // router - const router = useRouter(); - - /** - * Checks for redirect url and user details from the API. - * if the user is not authenticated, user will be redirected - * to the provided redirectTo route. - */ - useEffect(() => { - if (!contextData?.user || !redirectTo) return; - - if (!contextData?.user) { - if (redirectTo) { - router?.pathname !== redirectTo && router.push(redirectTo); - } - router?.pathname !== "/signin" && router.push("/signin"); - } - if (contextData?.user) { - if (redirectTo) { - router?.pathname !== redirectTo && router.push(redirectTo); - } - } - }, [contextData?.user, redirectTo, router]); return { ...contextData }; }; diff --git a/apps/app/layouts/auth-layout/project-authorization-wrapper.tsx b/apps/app/layouts/auth-layout/project-authorization-wrapper.tsx index 31ccb0408..bc0fcc8e8 100644 --- a/apps/app/layouts/auth-layout/project-authorization-wrapper.tsx +++ b/apps/app/layouts/auth-layout/project-authorization-wrapper.tsx @@ -5,13 +5,12 @@ import { useRouter } from "next/router"; // contexts import { useProjectMyMembership, ProjectMemberProvider } from "contexts/project-member.context"; -// hooks -import useUser from "hooks/use-user"; // layouts import Container from "layouts/container"; import AppHeader from "layouts/app-layout/app-header"; import AppSidebar from "layouts/app-layout/app-sidebar"; import SettingsNavbar from "layouts/settings-navbar"; +import { WorkspaceAuthorizationLayout } from "./workspace-authorization-wrapper"; // components import { NotAuthorizedView, JoinProject } from "components/auth-screens"; import { CommandPalette } from "components/command-palette"; @@ -59,83 +58,87 @@ const ProjectAuthorizationWrapped: React.FC = ({ const router = useRouter(); const { workspaceSlug, projectId } = router.query; - const user = useUser(); - const { loading, error, memberRole: memberType } = useProjectMyMembership(); const settingsLayout = router.pathname.includes("/settings"); return ( - - -
- - {loading ? ( -
-

Loading...

-
- ) : error?.status === 401 || error?.status === 403 ? ( - - ) : error?.status === 404 ? ( -
-
-

No such project exist. Create one?

- { - const e = new KeyboardEvent("keydown", { key: "p" }); - document.dispatchEvent(e); - }} - > - Create project - + + + +
+ + {loading ? ( +
+

Loading...

-
- ) : settingsLayout && (memberType?.isGuest || memberType?.isViewer) ? ( - - - - Go to issues - - - - } - type="project" - /> - ) : ( -
- {!noHeader && ( - - )} -
- {settingsLayout && ( -
-
-

Project Settings

-

- This information will be displayed to every member of the project. -

-
- -
+ ) : error?.status === 401 || error?.status === 403 ? ( + + ) : error?.status === 404 ? ( +
+
+

No such project exist. Create one?

+ { + const e = new KeyboardEvent("keydown", { key: "p" }); + document.dispatchEvent(e); + }} + > + Create project + +
+
+ ) : settingsLayout && (memberType?.isGuest || memberType?.isViewer) ? ( + + + + Go to issues + + + + } + type="project" + /> + ) : ( +
+ {!noHeader && ( + )} - {children} -
-
- )} -
- +
+ {settingsLayout && ( +
+
+

Project Settings

+

+ This information will be displayed to every member of the project. +

+
+ +
+ )} + {children} +
+ + )} +
+ + ); }; diff --git a/apps/app/layouts/auth-layout/user-authorization-wrapper.tsx b/apps/app/layouts/auth-layout/user-authorization-wrapper.tsx new file mode 100644 index 000000000..98375c1e0 --- /dev/null +++ b/apps/app/layouts/auth-layout/user-authorization-wrapper.tsx @@ -0,0 +1,28 @@ +import useSWR from "swr"; + +import { CURRENT_USER } from "constants/fetch-keys"; +import userService from "services/user.service"; +import { useRouter } from "next/router"; + +type Props = { + children: React.ReactNode; +}; + +export const UserAuthorizationLayout: React.FC = ({ children }) => { + const router = useRouter(); + + const { data: currentUser, error } = useSWR(CURRENT_USER, () => userService.currentUser()); + + if (!currentUser && !error) { + return
Loading...
; + } + + if (error?.status === 401) { + const redirectTo = router.asPath; + + router.push(`/signin?next=${redirectTo}`); + return null; + } + + return <>{children}; +}; diff --git a/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx b/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx index 3a88733a6..f9af96a2c 100644 --- a/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx +++ b/apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx @@ -7,8 +7,6 @@ import useSWR from "swr"; // services import workspaceServices from "services/workspace.service"; -// hooks -import useUser from "hooks/use-user"; // layouts import Container from "layouts/container"; import AppSidebar from "layouts/app-layout/app-sidebar"; @@ -22,6 +20,7 @@ import { PrimaryButton } from "components/ui"; import { LayerDiagonalIcon } from "components/icons"; // fetch-keys import { WORKSPACE_MEMBERS_ME } from "constants/fetch-keys"; +import { UserAuthorizationLayout } from "./user-authorization-wrapper"; type Meta = { title?: string | null; @@ -58,8 +57,6 @@ export const WorkspaceAuthorizationLayout: React.FC = ({ const router = useRouter(); const { workspaceSlug } = router.query; - const user = useUser(); - const { data: workspaceMemberMe, error } = useSWR( workspaceSlug ? WORKSPACE_MEMBERS_ME(workspaceSlug as string) : null, workspaceSlug ? () => workspaceServices.workspaceMemberMe(workspaceSlug.toString()) : null, @@ -99,60 +96,66 @@ export const WorkspaceAuthorizationLayout: React.FC = ({ }; return ( - - -
- - {settingsLayout && (memberType?.isGuest || memberType?.isViewer) ? ( - - - - Go to workspace - - - - } - type="workspace" - /> - ) : ( -
- {!noHeader && ( - - )} -
- {(settingsLayout || profilePage) && ( -
-
-

- {profilePage ? "Profile" : "Workspace"} Settings -

-

- {profilePage - ? "This information will be visible to only you." - : "This information will be displayed to every member of the workspace."} -

-
- -
+ + + +
+ + {settingsLayout && (memberType?.isGuest || memberType?.isViewer) ? ( + + + + Go to workspace + + + + } + type="workspace" + /> + ) : ( +
+ {!noHeader && ( + )} - {children} -
-
- )} -
-
+
+ {(settingsLayout || profilePage) && ( +
+
+

+ {profilePage ? "Profile" : "Workspace"} Settings +

+

+ {profilePage + ? "This information will be visible to only you." + : "This information will be displayed to every member of the workspace."} +

+
+ +
+ )} + {children} +
+ + )} +
+
+ ); }; diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx index 5ee1b5268..ade12455c 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/settings/estimates.tsx @@ -6,13 +6,12 @@ import useSWR, { mutate } from "swr"; // services import estimatesService from "services/estimates.service"; -import projectService from "services/project.service"; - +// hooks +import useProjectDetails from "hooks/use-project-details"; // layouts import { ProjectAuthorizationWrapper } from "layouts/auth-layout"; // components import { CreateUpdateEstimateModal, SingleEstimate } from "components/estimates"; - //hooks import useToast from "hooks/use-toast"; // ui @@ -21,10 +20,10 @@ import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; // icons import { PlusIcon } from "@heroicons/react/24/outline"; // types -import { IEstimate, IProject } from "types"; +import { IEstimate } from "types"; import type { NextPage } from "next"; // fetch-keys -import { ESTIMATES_LIST, PROJECT_DETAILS } from "constants/fetch-keys"; +import { ESTIMATES_LIST } from "constants/fetch-keys"; const EstimatesSettings: NextPage = () => { const [estimateFormOpen, setEstimateFormOpen] = useState(false); @@ -32,14 +31,14 @@ const EstimatesSettings: NextPage = () => { const [isUpdating, setIsUpdating] = useState(false); const [estimateToUpdate, setEstimateToUpdate] = useState(); - const [activeEstimate, setActiveEstimate] = useState(null); - const router = useRouter(); const { workspaceSlug, projectId } = router.query; const { setToastAlert } = useToast(); - const scollToRef = useRef(null); + const { projectDetails } = useProjectDetails(); + + const scrollToRef = useRef(null); const { data: estimatesList } = useSWR( workspaceSlug && projectId ? ESTIMATES_LIST(projectId as string) : null, @@ -74,13 +73,6 @@ const EstimatesSettings: NextPage = () => { }); }; - const { data: projectDetails } = useSWR( - workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null, - workspaceSlug && projectId - ? () => projectService.getProject(workspaceSlug as string, projectId as string) - : null - ); - return ( <> {
-
{estimatesList && estimatesList.length > 0 && (
<> @@ -131,8 +122,6 @@ const EstimatesSettings: NextPage = () => { editEstimate(estimate)} handleEstimateDelete={(estimateId) => removeEstimate(estimateId)} /> diff --git a/apps/app/pages/create-workspace.tsx b/apps/app/pages/create-workspace.tsx index 587cd51b4..d445e873e 100644 --- a/apps/app/pages/create-workspace.tsx +++ b/apps/app/pages/create-workspace.tsx @@ -3,14 +3,13 @@ import React from "react"; import { useRouter } from "next/router"; import Image from "next/image"; -// constants -import { requiredAuth } from "lib/auth"; // layouts import DefaultLayout from "layouts/default-layout"; +import { UserAuthorizationLayout } from "layouts/auth-layout/user-authorization-wrapper"; // images import Logo from "public/onboarding/logo.svg"; // types -import type { NextPage, NextPageContext } from "next"; +import type { NextPage } from "next"; // constants import { CreateWorkspaceForm } from "components/workspace"; @@ -23,42 +22,23 @@ const CreateWorkspace: NextPage = () => { }; return ( - -
-
-
- Plane Logo + + +
+
+
+ Plane Logo +
+ {}} + onSubmit={(res) => router.push(`/${res.slug}`)} + />
- {}} - onSubmit={(res) => router.push(`/${res.slug}`)} - />
-
- + + ); }; -export const getServerSideProps = async (ctx: NextPageContext) => { - const user = await requiredAuth(ctx.req?.headers.cookie); - - const redirectAfterSignIn = ctx.req?.url; - - if (!user) { - return { - redirect: { - destination: `/signin?next=${redirectAfterSignIn}`, - permanent: false, - }, - }; - } - - return { - props: { - user, - }, - }; -}; - export default CreateWorkspace; diff --git a/apps/app/pages/invitations.tsx b/apps/app/pages/invitations.tsx index d970e11bd..e75dc26fe 100644 --- a/apps/app/pages/invitations.tsx +++ b/apps/app/pages/invitations.tsx @@ -1,10 +1,10 @@ import React, { useState } from "react"; + import Link from "next/link"; import { useRouter } from "next/router"; + import useSWR from "swr"; -// lib -import { requiredAuth } from "lib/auth"; // services import workspaceService from "services/workspace.service"; // hooks @@ -12,6 +12,7 @@ import useUser from "hooks/use-user"; import useToast from "hooks/use-toast"; // layouts import DefaultLayout from "layouts/default-layout"; +import { UserAuthorizationLayout } from "layouts/auth-layout/user-authorization-wrapper"; // components import SingleInvitation from "components/workspace/single-invitation"; // ui @@ -19,7 +20,7 @@ import { Spinner, EmptySpace, EmptySpaceItem, SecondaryButton, PrimaryButton } f // icons import { CubeIcon, PlusIcon } from "@heroicons/react/24/outline"; // types -import type { NextPage, NextPageContext } from "next"; +import type { NextPage } from "next"; import type { IWorkspaceMemberInvitation } from "types"; const OnBoard: NextPage = () => { @@ -77,115 +78,96 @@ const OnBoard: NextPage = () => { }; return ( - -
- {user && ( -
-

logged in as {user.email}

-
- )} - -
- {invitations && workspaces ? ( - invitations.length > 0 ? ( -
-

Workspace Invitations

-

- Select invites that you want to accept. -

-
    - {invitations.map((invitation) => ( - - ))} -
-
- - - Go to Home - - - - Accept and Continue - -
-
- ) : workspaces && workspaces.length > 0 ? ( -
-

Your workspaces

- {workspaces.map((workspace) => ( - - -
-
- - {workspace.name} -
-
-

{workspace.owner.first_name}

-
-
-
- - ))} -
- ) : ( - invitations.length === 0 && - workspaces.length === 0 && ( - - { - router.push("/create-workspace"); - }} - /> - - ) - ) - ) : ( -
- + + +
+ {user && ( +
+

logged in as {user.email}

)} + +
+ {invitations && workspaces ? ( + invitations.length > 0 ? ( +
+

Workspace Invitations

+

+ Select invites that you want to accept. +

+
    + {invitations.map((invitation) => ( + + ))} +
+
+ + + Go to Home + + + + Accept and Continue + +
+
+ ) : workspaces && workspaces.length > 0 ? ( +
+

Your workspaces

+ {workspaces.map((workspace) => ( + + +
+
+ + {workspace.name} +
+
+

{workspace.owner.first_name}

+
+
+
+ + ))} +
+ ) : ( + invitations.length === 0 && + workspaces.length === 0 && ( + + { + router.push("/create-workspace"); + }} + /> + + ) + ) + ) : ( +
+ +
+ )} +
-
- + + ); }; -export const getServerSideProps = async (ctx: NextPageContext) => { - const user = await requiredAuth(ctx.req?.headers.cookie); - - const redirectAfterSignIn = ctx.req?.url; - - if (!user) { - return { - redirect: { - destination: `/signin?next=${redirectAfterSignIn}`, - permanent: false, - }, - }; - } - - return { - props: { - user, - }, - }; -}; - export default OnBoard; diff --git a/apps/app/pages/onboarding.tsx b/apps/app/pages/onboarding.tsx index 0c1c95326..a72a79775 100644 --- a/apps/app/pages/onboarding.tsx +++ b/apps/app/pages/onboarding.tsx @@ -3,14 +3,13 @@ import { useState } from "react"; import Image from "next/image"; import { useRouter } from "next/router"; -// lib -import { requiredAuth } from "lib/auth"; // services import userService from "services/user.service"; // hooks import useUser from "hooks/use-user"; // layouts import DefaultLayout from "layouts/default-layout"; +import { UserAuthorizationLayout } from "layouts/auth-layout/user-authorization-wrapper"; // components import { InviteMembers, OnboardingCard, UserDetails, Workspace } from "components/onboarding"; // ui @@ -20,7 +19,7 @@ import { ONBOARDING_CARDS } from "constants/workspace"; // images import Logo from "public/onboarding/logo.svg"; // types -import type { NextPage, GetServerSidePropsContext } from "next"; +import type { NextPage } from "next"; const Onboarding: NextPage = () => { const [step, setStep] = useState(1); @@ -33,83 +32,64 @@ const Onboarding: NextPage = () => { const { user } = useUser(); return ( - -
- {step <= 3 ? ( -
-
- Plane Logo -
- {step === 1 ? ( - - ) : step === 2 ? ( - - ) : ( - - )} -
- ) : ( -
-
- {step === 4 ? ( - - ) : step === 5 ? ( - - ) : step === 6 ? ( - - ) : step === 7 ? ( - + + +
+ {step <= 3 ? ( +
+
+ Plane Logo +
+ {step === 1 ? ( + + ) : step === 2 ? ( + ) : ( - + )} -
- { - if (step === 8) { - userService - .updateUserOnBoard({ userRole }) - .then(() => { - router.push("/"); - }) - .catch((err) => { - console.log(err); - }); - } else setStep((prevData) => prevData + 1); - }} - > - {step === 4 || step === 8 ? "Get Started" : "Next"} - +
+ ) : ( +
+
+ {step === 4 ? ( + + ) : step === 5 ? ( + + ) : step === 6 ? ( + + ) : step === 7 ? ( + + ) : ( + + )} +
+ { + if (step === 8) { + userService + .updateUserOnBoard({ userRole }) + .then(() => { + router.push("/"); + }) + .catch((err) => { + console.log(err); + }); + } else setStep((prevData) => prevData + 1); + }} + > + {step === 4 || step === 8 ? "Get Started" : "Next"} + +
-
- )} -
-
+ )} +
+ + ); }; -export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { - const user = await requiredAuth(ctx.req?.headers.cookie); - - const redirectAfterSignIn = ctx.resolvedUrl; - - if (!user) { - return { - redirect: { - destination: `/signin?next=${redirectAfterSignIn}`, - permanent: false, - }, - }; - } - - return { - props: { - user, - }, - }; -}; - export default Onboarding; diff --git a/apps/app/services/user.service.ts b/apps/app/services/user.service.ts index cab79ab3a..b035ecf8e 100644 --- a/apps/app/services/user.service.ts +++ b/apps/app/services/user.service.ts @@ -30,12 +30,10 @@ class UserService extends APIService { } async currentUser(): Promise { - if (!this.getAccessToken()) return null; return this.get("/api/users/me/") .then((response) => response?.data) .catch((error) => { - this.purgeAccessToken(); - throw error?.response?.data; + throw error?.response; }); } diff --git a/apps/app/types/issues.d.ts b/apps/app/types/issues.d.ts index ea151cb57..10f084325 100644 --- a/apps/app/types/issues.d.ts +++ b/apps/app/types/issues.d.ts @@ -186,6 +186,7 @@ export type Properties = { priority: boolean; state: boolean; sub_issue_count: boolean; + estimate: boolean; }; export interface IIssueLabels { @@ -272,4 +273,4 @@ export interface IIssueAttachment { updated_at: string; updated_by: string; workspace: string; -} \ No newline at end of file +}