From 1296b6af4296b08f8b81c99af33c6b20298f7ff3 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Fri, 1 Dec 2023 15:50:01 +0530 Subject: [PATCH] style: updated the UI of the instance admin setup and the sign in workflow (#2962) * style: updated the UI of the signin and instance setups * fix: form validations and mutations * fix: updated Link tags in accordance to next v14 * chore: latest features image, reset password redirection --- .../account/sign-in-forms/create-password.tsx | 12 +- .../account/sign-in-forms/email-form.tsx | 4 +- .../sign-in-forms/optional-set-password.tsx | 15 +- .../account/sign-in-forms/password.tsx | 23 +- web/components/account/sign-in-forms/root.tsx | 4 +- .../sign-in-forms/set-password-link.tsx | 46 +-- .../account/sign-in-forms/unique-code.tsx | 49 ++-- .../common/latest-feature-block.tsx | 35 +-- web/components/cycles/cycles-board-card.tsx | 9 +- web/components/cycles/cycles-list-item.tsx | 4 +- web/components/inbox/issue-card.tsx | 126 +++++---- web/components/instance/help-section.tsx | 4 +- .../instance/instance-admin-restriction.tsx | 10 +- web/components/instance/not-ready-view.tsx | 18 +- web/components/instance/setup-done-view.tsx | 81 ++++-- .../instance/setup-form/email-code-form.tsx | 135 +++++---- .../instance/setup-form/email-form.tsx | 94 +++---- .../instance/setup-form/password-form.tsx | 24 +- web/components/instance/setup-form/root.tsx | 10 +- web/components/instance/setup-view.tsx | 2 +- web/components/instance/sidebar-menu.tsx | 4 +- .../issues/attachment/attachments.tsx | 46 ++- web/components/issues/main-content.tsx | 4 +- web/components/modules/module-card-item.tsx | 4 +- web/components/modules/module-list-item.tsx | 4 +- web/components/page-views/signin.tsx | 29 +- web/components/pages/pages-list/list-item.tsx | 262 +++++++++--------- web/components/views/view-list-item.tsx | 4 +- .../views/default-view-list-item.tsx | 4 +- .../workspace/views/view-list-item.tsx | 4 +- web/layouts/admin-layout/layout.tsx | 13 +- web/layouts/auth-layout/user-wrapper.tsx | 1 - web/layouts/instance-layout/index.tsx | 4 +- .../settings-layout/profile/sidebar.tsx | 25 +- .../settings-layout/project/sidebar.tsx | 20 +- web/pages/accounts/password.tsx | 93 ++++--- web/public/onboarding/onboarding-pages.svg | 62 +++++ web/services/auth.service.ts | 2 +- web/store/instance/instance.store.ts | 2 +- 39 files changed, 717 insertions(+), 575 deletions(-) create mode 100644 web/public/onboarding/onboarding-pages.svg diff --git a/web/components/account/sign-in-forms/create-password.tsx b/web/components/account/sign-in-forms/create-password.tsx index 9494d1518..bc8fb300c 100644 --- a/web/components/account/sign-in-forms/create-password.tsx +++ b/web/components/account/sign-in-forms/create-password.tsx @@ -1,4 +1,5 @@ import React from "react"; +import Link from "next/link"; import { Controller, useForm } from "react-hook-form"; // services import { AuthService } from "services/auth.service"; @@ -74,7 +75,7 @@ export const CreatePasswordForm: React.FC = (props) => { return ( <> -

+

Let{"'"}s get a new password

@@ -117,16 +118,23 @@ export const CreatePasswordForm: React.FC = (props) => { hasError={Boolean(errors.password)} placeholder="Create password" className="w-full h-[46px] placeholder:text-onboarding-text-400 border border-onboarding-border-100 pr-12" + minLength={8} /> )} /> -

+

Whatever you choose now will be your account{"'"}s password until you change it.

+

+ When you click the button above, you agree with our{" "} + + terms and conditions of service. + +

); diff --git a/web/components/account/sign-in-forms/email-form.tsx b/web/components/account/sign-in-forms/email-form.tsx index c8e138a49..f704d4134 100644 --- a/web/components/account/sign-in-forms/email-form.tsx +++ b/web/components/account/sign-in-forms/email-form.tsx @@ -83,10 +83,10 @@ export const EmailForm: React.FC = (props) => { return ( <> -

+

Get on your flight deck!

-

+

Sign in with the email you used to sign up for Plane

diff --git a/web/components/account/sign-in-forms/optional-set-password.tsx b/web/components/account/sign-in-forms/optional-set-password.tsx index 372dc3947..57161054b 100644 --- a/web/components/account/sign-in-forms/optional-set-password.tsx +++ b/web/components/account/sign-in-forms/optional-set-password.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import Link from "next/link"; import { Controller, useForm } from "react-hook-form"; // ui import { Button, Input } from "@plane/ui"; @@ -37,12 +38,12 @@ export const OptionalSetPasswordForm: React.FC = (props) => { return ( <> -

Set a password

+

Set a password

- If you{"'"}d to do away with codes, set a password here. + If you{"'"}d to do away with codes, set a password here

-
+ = (props) => { /> )} /> -
+
+

+ When you click Go to workspace above, you agree with our{" "} + + terms and conditions of service. + +

); diff --git a/web/components/account/sign-in-forms/password.tsx b/web/components/account/sign-in-forms/password.tsx index bdaa34b22..547f59e9d 100644 --- a/web/components/account/sign-in-forms/password.tsx +++ b/web/components/account/sign-in-forms/password.tsx @@ -1,4 +1,5 @@ import React from "react"; +import Link from "next/link"; import { Controller, useForm } from "react-hook-form"; import { XCircle } from "lucide-react"; // services @@ -184,17 +185,25 @@ export const PasswordForm: React.FC = (props) => { /> )} /> - +
+ +
+

+ When you click the button above, you agree with our{" "} + + terms and conditions of service. + +

); diff --git a/web/components/account/sign-in-forms/root.tsx b/web/components/account/sign-in-forms/root.tsx index d2d6ef753..9797c8573 100644 --- a/web/components/account/sign-in-forms/root.tsx +++ b/web/components/account/sign-in-forms/root.tsx @@ -23,6 +23,8 @@ type Props = { handleSignInRedirection: () => Promise; }; +const OAUTH_HIDDEN_STEPS = [ESignInSteps.OPTIONAL_SET_PASSWORD, ESignInSteps.CREATE_PASSWORD]; + export const SignInRoot: React.FC = (props) => { const { handleSignInRedirection } = props; // states @@ -72,7 +74,7 @@ export const SignInRoot: React.FC = (props) => { /> )} - {signInStep !== ESignInSteps.OPTIONAL_SET_PASSWORD && ( + {!OAUTH_HIDDEN_STEPS.includes(signInStep) && ( setEmail(newEmail)} handleStepChange={(step: ESignInSteps) => setSignInStep(step)} diff --git a/web/components/account/sign-in-forms/set-password-link.tsx b/web/components/account/sign-in-forms/set-password-link.tsx index 34e643b92..f683a26fc 100644 --- a/web/components/account/sign-in-forms/set-password-link.tsx +++ b/web/components/account/sign-in-forms/set-password-link.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import Link from "next/link"; import { Controller, useForm } from "react-hook-form"; import { XCircle } from "lucide-react"; // services @@ -48,6 +49,13 @@ export const SetPasswordLink: React.FC = (props) => { await authService .emailCheck(payload) + .then(() => + setToastAlert({ + type: "success", + title: "Success!", + message: "We have sent a new link to your email.", + }) + ) .catch((err) => setToastAlert({ type: "error", @@ -60,15 +68,15 @@ export const SetPasswordLink: React.FC = (props) => { return ( <> -

- Get on your flight deck! +

+ Get on your flight deck

- We have sent a link to {email}, so you can set a + We have sent a link to {email}, so you can set a password

-
+
= (props) => { )} />
-
- -
+ +

+ When you click the button above, you agree with our{" "} + + terms and conditions of service. + +

); diff --git a/web/components/account/sign-in-forms/unique-code.tsx b/web/components/account/sign-in-forms/unique-code.tsx index 0091819f8..43e183fd5 100644 --- a/web/components/account/sign-in-forms/unique-code.tsx +++ b/web/components/account/sign-in-forms/unique-code.tsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import Link from "next/link"; import { Controller, useForm } from "react-hook-form"; import { CornerDownLeft, XCircle } from "lucide-react"; // services @@ -44,7 +45,7 @@ export const UniqueCodeForm: React.FC = (props) => { // toast alert const { setToastAlert } = useToast(); // timer - const { timer: resendTimerCode, setTimer: setResendCodeTimer } = useTimer(); + const { timer: resendTimerCode, setTimer: setResendCodeTimer } = useTimer(30); // form info const { control, @@ -131,11 +132,9 @@ export const UniqueCodeForm: React.FC = (props) => { return ( <> -

- Moving to the runway -

-

- Paste the code you got at {email} below. +

Moving to the runway

+

+ Paste the code you got at {email} below

@@ -201,20 +200,24 @@ export const UniqueCodeForm: React.FC = (props) => { /> )} /> - +
+ +
+

+ When you click Confirm above, you agree with our{" "} + + terms and conditions of service. + +

); diff --git a/web/components/common/latest-feature-block.tsx b/web/components/common/latest-feature-block.tsx index 46309f6da..bade52d62 100644 --- a/web/components/common/latest-feature-block.tsx +++ b/web/components/common/latest-feature-block.tsx @@ -1,34 +1,35 @@ import Image from "next/image"; +import Link from "next/link"; import { useTheme } from "next-themes"; // icons import { Lightbulb } from "lucide-react"; // images -import signInIssues from "public/onboarding/onboarding-issues.svg"; +import latestFeatures from "public/onboarding/onboarding-pages.svg"; export const LatestFeatureBlock = () => { const { resolvedTheme } = useTheme(); return ( <> -
+
-

- Try the latest features, like Tiptap editor, to write compelling responses.{" "} - {}}> - See new features - +

+ Pages gets a facelift! Write anything and use Galileo to help you start.{" "} + + Learn more +

-
- Plane Issues +
+
+ Plane Issues +
); diff --git a/web/components/cycles/cycles-board-card.tsx b/web/components/cycles/cycles-board-card.tsx index 8a74565d5..22b59fc5f 100644 --- a/web/components/cycles/cycles-board-card.tsx +++ b/web/components/cycles/cycles-board-card.tsx @@ -33,7 +33,10 @@ export interface ICyclesBoardCard { export const CyclesBoardCard: FC = (props) => { const { cycle, workspaceSlug, projectId } = props; // store - const { cycle: cycleStore, trackEvent: { setTrackElement } } = useMobxStore(); + const { + cycle: cycleStore, + trackEvent: { setTrackElement }, + } = useMobxStore(); // toast const { setToastAlert } = useToast(); // states @@ -152,7 +155,7 @@ export const CyclesBoardCard: FC = (props) => { /> - +
@@ -268,7 +271,7 @@ export const CyclesBoardCard: FC = (props) => {
-
+
); diff --git a/web/components/cycles/cycles-list-item.tsx b/web/components/cycles/cycles-list-item.tsx index 531a637aa..30fcda221 100644 --- a/web/components/cycles/cycles-list-item.tsx +++ b/web/components/cycles/cycles-list-item.tsx @@ -153,7 +153,7 @@ export const CyclesListItem: FC = (props) => { projectId={projectId} /> - +
@@ -262,7 +262,7 @@ export const CyclesListItem: FC = (props) => {
- +
); diff --git a/web/components/inbox/issue-card.tsx b/web/components/inbox/issue-card.tsx index abf82208e..24f267506 100644 --- a/web/components/inbox/issue-card.tsx +++ b/web/components/inbox/issue-card.tsx @@ -27,71 +27,69 @@ export const InboxIssueCard: React.FC = (props) => { return ( - -
-
-

- {issue.project_detail?.identifier}-{issue.sequence_id} -

-
{issue.name}
-
-
- - - - -
- - {renderShortDateWithYearFormat(issue.created_at ?? "")} -
-
-
-
s.value === issueStatus)?.textColor - }`} - > - {issueStatus === -2 ? ( - <> - - Pending - - ) : issueStatus === -1 ? ( - <> - - Declined - - ) : issueStatus === 0 ? ( - <> - - - {new Date(issue.issue_inbox[0].snoozed_till ?? "") < new Date() ? "Snoozed date passed" : "Snoozed"} - - - ) : issueStatus === 1 ? ( - <> - - Accepted - - ) : ( - <> - - Duplicate - - )} -
+
+
+

+ {issue.project_detail?.identifier}-{issue.sequence_id} +

+
{issue.name}
- +
+ + + + +
+ + {renderShortDateWithYearFormat(issue.created_at ?? "")} +
+
+
+
s.value === issueStatus)?.textColor + }`} + > + {issueStatus === -2 ? ( + <> + + Pending + + ) : issueStatus === -1 ? ( + <> + + Declined + + ) : issueStatus === 0 ? ( + <> + + + {new Date(issue.issue_inbox[0].snoozed_till ?? "") < new Date() ? "Snoozed date passed" : "Snoozed"} + + + ) : issueStatus === 1 ? ( + <> + + Accepted + + ) : ( + <> + + Duplicate + + )} +
+
); }; diff --git a/web/components/instance/help-section.tsx b/web/components/instance/help-section.tsx index f880688f4..5d1e48c9c 100644 --- a/web/components/instance/help-section.tsx +++ b/web/components/instance/help-section.tsx @@ -98,12 +98,12 @@ export const InstanceHelpSection: FC = () => { if (href) return ( - +
{name} - +
); else diff --git a/web/components/instance/instance-admin-restriction.tsx b/web/components/instance/instance-admin-restriction.tsx index c1e8a47db..5551c0786 100644 --- a/web/components/instance/instance-admin-restriction.tsx +++ b/web/components/instance/instance-admin-restriction.tsx @@ -67,12 +67,10 @@ export const InstanceAdminRestriction: FC = ({ re
- - - +
diff --git a/web/components/instance/not-ready-view.tsx b/web/components/instance/not-ready-view.tsx index 501ab4d41..1333d65a6 100644 --- a/web/components/instance/not-ready-view.tsx +++ b/web/components/instance/not-ready-view.tsx @@ -1,29 +1,29 @@ import React from "react"; import Image from "next/image"; import { useTheme } from "next-themes"; -// image +// images import instanceNotReady from "public/instance/plane-instance-not-ready.webp"; import PlaneWhiteLogo from "public/plane-logos/white-horizontal-with-blue-logo.svg"; -import PlaneDarkLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg"; +import PlaneBlackLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg"; export const InstanceNotReady = () => { const { resolvedTheme } = useTheme(); - const planeLogo = resolvedTheme === "dark" ? PlaneWhiteLogo : PlaneDarkLogo; + const planeLogo = resolvedTheme === "dark" ? PlaneWhiteLogo : PlaneBlackLogo; return (
-
-
-
+
+
+
- image + Plane logo
- image + Instance not ready
-

Your Plane instance isn’t ready yet

+

Your Plane instance isn{"'"}t ready yet

Ask your Instance Admin to complete set-up first.

diff --git a/web/components/instance/setup-done-view.tsx b/web/components/instance/setup-done-view.tsx index 4885e006a..4eab4013f 100644 --- a/web/components/instance/setup-done-view.tsx +++ b/web/components/instance/setup-done-view.tsx @@ -1,43 +1,66 @@ -import React from "react"; +import React, { useState } from "react"; import Image from "next/image"; - +import { useTheme } from "next-themes"; // ui import { Button } from "@plane/ui"; import { UserCog2 } from "lucide-react"; -// image +// images import instanceSetupDone from "public/instance-setup-done.svg"; -import PlaneLogo from "public/plane-logos/blue-without-text.png"; +import PlaneBlackLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg"; +import PlaneWhiteLogo from "public/plane-logos/white-horizontal-with-blue-logo.svg"; +import { useMobxStore } from "lib/mobx/store-provider"; -export const InstanceSetupDone = () => ( -
-
-
-
-
- image - To the stratosphere now! -
+export const InstanceSetupDone = () => { + // states + const [isRedirecting, setIsRedirecting] = useState(false); + // next-themes + const { resolvedTheme } = useTheme(); + // mobx store + const { + instance: { fetchInstanceInfo }, + } = useMobxStore(); -
- image -
+ const planeLogo = resolvedTheme === "dark" ? PlaneWhiteLogo : PlaneBlackLogo; -
- - Your instance is now ready for more security, more controls, and more intelligence. - - + const redirectToGodMode = async () => { + setIsRedirecting(true); -
- Use this wisely. Remember, with great power comes great responsibility.🕷️ + await fetchInstanceInfo().finally(() => setIsRedirecting(false)); + }; + + return ( +
+
+
+
+
+
+ Plane logo +
+
+ +
+
+ image +
+
+ +
+
+
+ Your instance is now ready for more security, more controls, and more intelligence. +
+

+ Use this wisely. Remember, with great power comes great responsibility. +

+
+
-
-); + ); +}; diff --git a/web/components/instance/setup-form/email-code-form.tsx b/web/components/instance/setup-form/email-code-form.tsx index 6264ba20f..24643d833 100644 --- a/web/components/instance/setup-form/email-code-form.tsx +++ b/web/components/instance/setup-form/email-code-form.tsx @@ -1,4 +1,4 @@ -import { FC } from "react"; +import { FC, useState } from "react"; import { useForm, Controller } from "react-hook-form"; // ui import { Input, Button } from "@plane/ui"; @@ -24,6 +24,8 @@ export interface IInstanceSetupEmailCodeForm { export const InstanceSetupEmailCodeForm: FC = (props) => { const { handleNextStep, email, moveBack } = props; + // states + const [isResendingCode, setIsResendingCode] = useState(false); // form info const { control, @@ -40,10 +42,10 @@ export const InstanceSetupEmailCodeForm: FC = (prop const { setToastAlert } = useToast(); const { timer, setTimer } = useTimer(30); // computed - const isResendDisabled = timer > 0 || isSubmitting; + const isResendDisabled = timer > 0 || isResendingCode; - const handleEmailCodeFormSubmit = (formValues: InstanceSetupEmailCodeFormValues) => - authService + const handleEmailCodeFormSubmit = async (formValues: InstanceSetupEmailCodeFormValues) => + await authService .instanceMagicSignIn({ key: `magic_${formValues.email}`, token: formValues.token }) .then(() => { reset(); @@ -51,42 +53,40 @@ export const InstanceSetupEmailCodeForm: FC = (prop }) .catch((err) => { setToastAlert({ - title: "Oops!", type: "error", - message: err?.error, + title: "Error!", + message: err?.error ?? "Something went wrong. Please try again.", }); }); - const resendMagicCode = () => { - setTimer(30); - authService + const resendMagicCode = async () => { + setIsResendingCode(true); + + await authService .instanceAdminEmailCode({ email }) - .then(() => { - // setCodeResending(false); - setTimer(30); - }) + .then(() => setTimer(30)) .catch((err) => { setToastAlert({ - title: "Oops!", type: "error", - message: err?.error, + title: "Error!", + message: err?.error ?? "Something went wrong. Please try again.", }); - }); + }) + .finally(() => setIsResendingCode(false)); }; return (
-
-

- Let’s secure your instance -

-
-

Paste the code you got at

- {email} - below. -
- -
+

+ Let{"'"}s secure your instance +

+

+ Paste the code you got at +
+ {email} below. +

+
+
= (prop ) || "Email address is not valid", }} render={({ field: { value, onChange } }) => ( -
+
= (prop
)} /> -
- {timer > 0 ? ( - Request new code in {timer}s - ) : isSubmitting ? ( - "Sending new code..." - ) : ( -
- -
- )} +
+
- ( -
- -
- )} - /> - -
+ ( +
+ +
+ )} + /> +
); diff --git a/web/components/instance/setup-form/email-form.tsx b/web/components/instance/setup-form/email-form.tsx index b2c336695..7305095a9 100644 --- a/web/components/instance/setup-form/email-form.tsx +++ b/web/components/instance/setup-form/email-form.tsx @@ -44,61 +44,59 @@ export const InstanceSetupEmailForm: FC = (props) => { }) .catch((err) => { setToastAlert({ - title: "Oops!", type: "error", - message: err?.error, + title: "Error!", + message: err?.error ?? "Something went wrong. Please try again.", }); }); return (
-
-

- Let’s secure your instance -

-

- Explore privacy options. Get AI features. Secure access.
Takes 2 minutes. -

- -
- - /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( - value - ) || "Email address is not valid", - }} - render={({ field: { value, onChange } }) => ( -
- + Let{"'"}s secure your instance + +

+ Explore privacy options. Get AI features. Secure access. +
+ Takes 2 minutes. +

+
+ + /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( + value + ) || "Email address is not valid", + }} + render={({ field: { value, onChange } }) => ( +
+ + {value.length > 0 && ( + setValue("email", "")} /> - {value.length > 0 && ( - setValue("email", "")} - /> - )} -
- )} - /> -

- Use your email address if you are the instance admin.
Use your admin’s e-mail if you are not. -

- - -
+ )} +
+ )} + /> +

+ Use your email address if you are the instance admin.
Use your admin’s e-mail if you are not. +

+
); diff --git a/web/components/instance/setup-form/password-form.tsx b/web/components/instance/setup-form/password-form.tsx index f963b960e..86053cfd7 100644 --- a/web/components/instance/setup-form/password-form.tsx +++ b/web/components/instance/setup-form/password-form.tsx @@ -1,4 +1,5 @@ import React from "react"; +import Link from "next/link"; import { useForm, Controller } from "react-hook-form"; // ui import { Input, Button } from "@plane/ui"; @@ -43,14 +44,14 @@ export const InstanceSetupPasswordForm: React.FC = ( return (
-

+

Moving to the runway

- {"Let's set a password so you can do away with codes."} + Let{"'"}s set a password so you can do away with codes.

-
+
= (
)} /> - -
+
= (
)} /> +

+ Whatever you choose now will be your account{"'"}s password +

-

- {"Whatever you choose now will be your account's password"} -

+

+ When you click the button above, you agree with our{" "} + + terms and conditions of service. + +

diff --git a/web/components/instance/setup-form/root.tsx b/web/components/instance/setup-form/root.tsx index ebc6c5649..46f5493ba 100644 --- a/web/components/instance/setup-form/root.tsx +++ b/web/components/instance/setup-form/root.tsx @@ -21,13 +21,11 @@ export const InstanceSetupFormRoot = () => { return ( <> {setupStep === EInstanceSetupSteps.DONE ? ( -
- -
+ ) : ( -
-
-
+
+
+
{setupStep === EInstanceSetupSteps.EMAIL && ( { diff --git a/web/components/instance/setup-view.tsx b/web/components/instance/setup-view.tsx index 07d4455dd..e79f6321e 100644 --- a/web/components/instance/setup-view.tsx +++ b/web/components/instance/setup-view.tsx @@ -24,7 +24,7 @@ export const InstanceSetupView = observer(() => { return ( <> -
+
Plane Logo diff --git a/web/components/instance/sidebar-menu.tsx b/web/components/instance/sidebar-menu.tsx index 8feaa44c2..54a04529f 100644 --- a/web/components/instance/sidebar-menu.tsx +++ b/web/components/instance/sidebar-menu.tsx @@ -54,7 +54,7 @@ export const InstanceAdminSidebarMenu = () => { return ( - +
{ )}
- +
); })} diff --git a/web/components/issues/attachment/attachments.tsx b/web/components/issues/attachment/attachments.tsx index d2f592f2e..86cafd7a9 100644 --- a/web/components/issues/attachment/attachments.tsx +++ b/web/components/issues/attachment/attachments.tsx @@ -59,33 +59,31 @@ export const IssueAttachments = () => { key={file.id} className="flex h-[60px] items-center justify-between gap-1 rounded-md border-[2px] border-custom-border-200 bg-custom-background-100 px-4 py-2 text-sm" > - - -
-
{getFileIcon(getFileExtension(file.asset))}
-
-
- - {truncateText(`${getFileName(file.attributes.name)}`, 10)} - - person.member.id === file.updated_by)?.member.display_name ?? "" - } uploaded on ${renderLongDateFormat(file.updated_at)}`} - > - - - - -
+ +
+
{getFileIcon(getFileExtension(file.asset))}
+
+
+ + {truncateText(`${getFileName(file.attributes.name)}`, 10)} + + person.member.id === file.updated_by)?.member.display_name ?? "" + } uploaded on ${renderLongDateFormat(file.updated_at)}`} + > + + + + +
-
- {getFileExtension(file.asset).toUpperCase()} - {convertBytesToSize(file.attributes.size)} -
+
+ {getFileExtension(file.asset).toUpperCase()} + {convertBytesToSize(file.attributes.size)}
- +
- ) : ( - - )} - - )} - {!page.archived_at && userCanEdit && ( - - {page.access ? ( - - ) : ( - - )} - - )} + /> + {label.name} +
+ ))} +
+
+ {page.archived_at ? ( projectMember.member.id === page.created_by)?.member - .display_name ?? "" - } on ${renderLongDateFormat(`${page.created_at}`)}`} + tooltipContent={`Archived at ${render24HourFormatTime(page.archived_at)} on ${renderShortDate( + page.archived_at + )}`} > - +

{render24HourFormatTime(page.archived_at)}

- {page.archived_at ? ( - - {userCanEdit && ( - <> - -
- - Restore page -
-
- -
- - Delete page -
-
- - )} - -
- - Copy page link -
-
-
- ) : ( - - {userCanEdit && ( - <> - -
- - Edit page -
-
- -
- - Archive page -
-
- - )} - -
- - Copy page link -
-
-
- )} -
+ ) : ( + +

{render24HourFormatTime(page.updated_at)}

+
+ )} + {!page.archived_at && userCanEdit && ( + + {page.is_favorite ? ( + + ) : ( + + )} + + )} + {!page.archived_at && userCanEdit && ( + + {page.access ? ( + + ) : ( + + )} + + )} + projectMember.member.id === page.created_by)?.member + .display_name ?? "" + } on ${renderLongDateFormat(`${page.created_at}`)}`} + > + + + {page.archived_at ? ( + + {userCanEdit && ( + <> + +
+ + Restore page +
+
+ +
+ + Delete page +
+
+ + )} + +
+ + Copy page link +
+
+
+ ) : ( + + {userCanEdit && ( + <> + +
+ + Edit page +
+
+ +
+ + Archive page +
+
+ + )} + +
+ + Copy page link +
+
+
+ )}
- +
diff --git a/web/components/views/view-list-item.tsx b/web/components/views/view-list-item.tsx index 7ed182c84..65d2e4e14 100644 --- a/web/components/views/view-list-item.tsx +++ b/web/components/views/view-list-item.tsx @@ -57,7 +57,7 @@ export const ProjectViewListItem: React.FC = observer((props) => { setDeleteViewModal(false)} />
- +
@@ -128,7 +128,7 @@ export const ProjectViewListItem: React.FC = observer((props) => {
- +
diff --git a/web/components/workspace/views/default-view-list-item.tsx b/web/components/workspace/views/default-view-list-item.tsx index a168f20ea..d75d8271c 100644 --- a/web/components/workspace/views/default-view-list-item.tsx +++ b/web/components/workspace/views/default-view-list-item.tsx @@ -18,7 +18,7 @@ export const GlobalDefaultViewListItem: React.FC = observer((props) => { return (
- +
@@ -29,7 +29,7 @@ export const GlobalDefaultViewListItem: React.FC = observer((props) => {
- +
); diff --git a/web/components/workspace/views/view-list-item.tsx b/web/components/workspace/views/view-list-item.tsx index 29a4627a0..a84c2d558 100644 --- a/web/components/workspace/views/view-list-item.tsx +++ b/web/components/workspace/views/view-list-item.tsx @@ -32,7 +32,7 @@ export const GlobalViewListItem: React.FC = observer((props) => { setDeleteViewModal(false)} />
- +
@@ -77,7 +77,7 @@ export const GlobalViewListItem: React.FC = observer((props) => {
- +
diff --git a/web/layouts/admin-layout/layout.tsx b/web/layouts/admin-layout/layout.tsx index 1a5c188eb..044a2b77b 100644 --- a/web/layouts/admin-layout/layout.tsx +++ b/web/layouts/admin-layout/layout.tsx @@ -1,4 +1,5 @@ import { FC, ReactNode } from "react"; +import { observer } from "mobx-react-lite"; // layouts import { AdminAuthWrapper, UserAuthWrapper } from "layouts/auth-layout"; // components @@ -12,20 +13,14 @@ export interface IInstanceAdminLayout { children: ReactNode; } -export const InstanceAdminLayout: FC = (props) => { +export const InstanceAdminLayout: FC = observer((props) => { const { children } = props; // store const { instance: { instance }, - user: { currentUser }, } = useMobxStore(); - // fetch - console.log("instance", instance); - - if (instance?.is_setup_done === false) { - return ; - } + if (instance?.is_setup_done === false) return ; return ( <> @@ -46,4 +41,4 @@ export const InstanceAdminLayout: FC = (props) => { ); -}; +}); diff --git a/web/layouts/auth-layout/user-wrapper.tsx b/web/layouts/auth-layout/user-wrapper.tsx index 2bd6ac8ac..0f2ccf5e1 100644 --- a/web/layouts/auth-layout/user-wrapper.tsx +++ b/web/layouts/auth-layout/user-wrapper.tsx @@ -21,7 +21,6 @@ export const UserAuthWrapper: FC = observer((props) => { fetchCurrentUser, fetchCurrentUserInstanceAdminStatus, fetchCurrentUserSettings, - }, workspace: { fetchWorkspaces }, } = useMobxStore(); diff --git a/web/layouts/instance-layout/index.tsx b/web/layouts/instance-layout/index.tsx index 75aa374c1..858f30c8d 100644 --- a/web/layouts/instance-layout/index.tsx +++ b/web/layouts/instance-layout/index.tsx @@ -24,7 +24,9 @@ const InstanceLayout: FC = observer(({ children }) => { const router = useRouter(); const isGodMode = router.pathname.includes("god-mode"); - useSWR("INSTANCE_INFO", () => fetchInstanceInfo()); + useSWR("INSTANCE_INFO", () => fetchInstanceInfo(), { + revalidateOnFocus: false, + }); useEffect(() => { if (instance?.is_activated === false) { diff --git a/web/layouts/settings-layout/profile/sidebar.tsx b/web/layouts/settings-layout/profile/sidebar.tsx index 759c29429..6ee161fbd 100644 --- a/web/layouts/settings-layout/profile/sidebar.tsx +++ b/web/layouts/settings-layout/profile/sidebar.tsx @@ -102,18 +102,19 @@ export const ProfileLayoutSidebar = observer(() => { } ${sidebarCollapsed ? "left-0" : "-left-full md:left-0"}`} >
- - - - - {!sidebarCollapsed && ( -

Profile settings

- )} + +
+ + + + {!sidebarCollapsed && ( +

Profile settings

+ )} +
{!sidebarCollapsed && ( diff --git a/web/layouts/settings-layout/project/sidebar.tsx b/web/layouts/settings-layout/project/sidebar.tsx index dfd975cd3..b025a53d2 100644 --- a/web/layouts/settings-layout/project/sidebar.tsx +++ b/web/layouts/settings-layout/project/sidebar.tsx @@ -50,17 +50,15 @@ export const ProjectSettingsSidebar = () => {
{projectLinks.map((link) => ( - -
- {link.label} -
-
+
+ {link.label} +
))}
diff --git a/web/pages/accounts/password.tsx b/web/pages/accounts/password.tsx index 8c031a408..f85840cda 100644 --- a/web/pages/accounts/password.tsx +++ b/web/pages/accounts/password.tsx @@ -1,4 +1,4 @@ -import { ReactElement } from "react"; +import { ReactElement, useCallback } from "react"; import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/router"; @@ -15,11 +15,14 @@ import DefaultLayout from "layouts/default-layout"; import { Button, Input } from "@plane/ui"; // images import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png"; -import signInIssues from "public/onboarding/onboarding-issues.svg"; +import latestFeatures from "public/onboarding/onboarding-pages.svg"; // helpers import { checkEmailValidity } from "helpers/string.helper"; // type import { NextPageWithLayout } from "types/app"; +import { useMobxStore } from "lib/mobx/store-provider"; +// types +import { IUser, IUserSettings } from "types"; type TResetPasswordFormValues = { email: string; @@ -42,6 +45,10 @@ const HomePage: NextPageWithLayout = () => { const { resolvedTheme } = useTheme(); // toast const { setToastAlert } = useToast(); + // mobx store + const { + user: { fetchCurrentUser, fetchCurrentUserSettings }, + } = useMobxStore(); // form info const { control, @@ -54,6 +61,31 @@ const HomePage: NextPageWithLayout = () => { }, }); + const handleSignInRedirection = useCallback( + async (user: IUser) => { + // if the user is not onboarded, redirect them to the onboarding page + if (!user.is_onboarded) { + router.push("/onboarding"); + return; + } + + // if the user is onboarded, fetch their last workspace details + await fetchCurrentUserSettings().then((userSettings: IUserSettings) => { + const workspaceSlug = + userSettings?.workspace?.last_workspace_slug || userSettings?.workspace?.fallback_workspace_slug; + if (workspaceSlug) router.push(`/${workspaceSlug}`); + else router.push("/profile"); + }); + }, + [fetchCurrentUserSettings, router] + ); + + const mutateUserInfo = useCallback(async () => { + await fetchCurrentUser().then(async (user) => { + await handleSignInRedirection(user); + }); + }, [fetchCurrentUser, handleSignInRedirection]); + const handleResetPassword = async (formData: TResetPasswordFormValues) => { if (!uidb64 || !token || !email) return; @@ -61,13 +93,16 @@ const HomePage: NextPageWithLayout = () => { new_password: formData.password, }; - await authService.resetPassword(uidb64.toString(), token.toString(), payload).catch((err) => - setToastAlert({ - type: "error", - title: "Error!", - message: err?.error ?? "Something went wrong. Please try again.", - }) - ); + await authService + .resetPassword(uidb64.toString(), token.toString(), payload) + .then(() => mutateUserInfo()) + .catch((err) => + setToastAlert({ + type: "error", + title: "Error!", + message: err?.error ?? "Something went wrong. Please try again.", + }) + ); }; return ( @@ -82,7 +117,7 @@ const HomePage: NextPageWithLayout = () => {
-

+

Let{"'"}s get a new password

@@ -123,6 +158,7 @@ const HomePage: NextPageWithLayout = () => { hasError={Boolean(errors.password)} placeholder="Choose password" className="w-full h-[46px] placeholder:text-onboarding-text-400 border border-onboarding-border-100 pr-12" + minLength={8} /> )} /> @@ -142,13 +178,8 @@ const HomePage: NextPageWithLayout = () => {

When you click the button above, you agree with our{" "} - - terms and conditions of service. + + terms and conditions of service.

@@ -157,25 +188,21 @@ const HomePage: NextPageWithLayout = () => {

Try the latest features, like Tiptap editor, to write compelling responses.{" "} - - - See new features - + + See new features

-
- Plane Issues +
+
+ Plane Issues +
diff --git a/web/public/onboarding/onboarding-pages.svg b/web/public/onboarding/onboarding-pages.svg new file mode 100644 index 000000000..93118ebe0 --- /dev/null +++ b/web/public/onboarding/onboarding-pages.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/services/auth.service.ts b/web/services/auth.service.ts index 43511d33d..47325cf30 100644 --- a/web/services/auth.service.ts +++ b/web/services/auth.service.ts @@ -55,7 +55,7 @@ export class AuthService extends APIService { new_password: string; } ): Promise { - return this.post(`/api/reset-password/${uidb64}/${token}/`, data) + return this.post(`/api/reset-password/${uidb64}/${token}/`, data, { headers: {} }) .then((response) => { if (response?.status === 200) { this.setAccessToken(response?.data?.access_token); diff --git a/web/store/instance/instance.store.ts b/web/store/instance/instance.store.ts index 81db85ae8..a846b8e2e 100644 --- a/web/store/instance/instance.store.ts +++ b/web/store/instance/instance.store.ts @@ -71,7 +71,7 @@ export class InstanceStore implements IInstanceStore { } /** - * fetch instace info from API + * fetch instance info from API */ fetchInstanceInfo = async () => { try {