forked from makeplane/plane
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dev: instance setup workflow (makeplane#2935)
* chore: instance type updated * chore: instance not ready screen added * chore: instance layout added * chore: instance magic sign in endpoint and type added * chore: instance admin password endpoint added * chore: instance setup page added * chore: instance setup form added * chore: instance layout updated * fix: instance admin workflow setup * fix: admin workflow setup --------- Co-authored-by: sriram veeraghanta <[email protected]>
- Loading branch information
1 parent
ee30eb0
commit fd5b7d2
Showing
25 changed files
with
905 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from "./product-updates-modal"; | ||
export * from "./empty-state"; | ||
export * from "./latest-feature-block"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import Image from "next/image"; | ||
import { useTheme } from "next-themes"; | ||
// icons | ||
import { Lightbulb } from "lucide-react"; | ||
// images | ||
import signInIssues from "public/onboarding/onboarding-issues.svg"; | ||
|
||
export const LatestFeatureBlock = () => { | ||
const { resolvedTheme } = useTheme(); | ||
|
||
return ( | ||
<> | ||
<div | ||
className={`flex py-2 bg-onboarding-background-100 border border-onboarding-border-200 mx-auto rounded-[3.5px] sm:w-96 mt-16`} | ||
> | ||
<Lightbulb className="h-7 w-7 mr-2 mx-3" /> | ||
<p className={`text-sm text-left text-onboarding-text-100`}> | ||
Try the latest features, like Tiptap editor, to write compelling responses.{" "} | ||
<span className="font-medium text-sm underline hover:cursor-pointer" onClick={() => {}}> | ||
See new features | ||
</span> | ||
</p> | ||
</div> | ||
<div className="flex justify-center border border-onboarding-border-200 sm:w-96 sm:h-52 object-cover mt-8 mx-auto rounded-md bg-onboarding-background-100 "> | ||
<Image | ||
src={signInIssues} | ||
alt="Plane Issues" | ||
className={`flex object-cover rounded-md ${ | ||
resolvedTheme === "dark" ? "bg-onboarding-background-100" : "bg-custom-primary-70" | ||
} `} | ||
/> | ||
</div> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import React from "react"; | ||
import Image from "next/image"; | ||
import { useTheme } from "next-themes"; | ||
// image | ||
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"; | ||
|
||
export const InstanceNotReady = () => { | ||
const { resolvedTheme } = useTheme(); | ||
|
||
const planeLogo = resolvedTheme === "dark" ? PlaneWhiteLogo : PlaneDarkLogo; | ||
|
||
return ( | ||
<div className="h-screen w-full overflow-y-auto bg-onboarding-gradient-100"> | ||
<div className={`h-screen w-full pt-24`}> | ||
<div className="h-auto bg-onboarding-gradient-100 md:w-2/3 sm:w-4/5 p-4 rounded-md mx-auto shadow-sm border border-custom-border-100 "> | ||
<div className={`relative px-7 sm:px-0 bg-onboarding-gradient-200 h-full rounded-md`}> | ||
<div className="flex items-center py-10 justify-center"> | ||
<Image src={planeLogo} className="h-44 w-full" alt="image" /> | ||
</div> | ||
<div className="mt-20"> | ||
<Image src={instanceNotReady} className="h-46 w-full" alt="image" /> | ||
</div> | ||
<div className="flex flex-col gap-5 items-center py-12 pb-20 w-full"> | ||
<h3 className="text-2xl font-medium">Your Plane instance isn’t ready yet</h3> | ||
<p className="text-sm">Ask your Instance Admin to complete set-up first.</p> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import React from "react"; | ||
import Image from "next/image"; | ||
|
||
// ui | ||
import { Button } from "@plane/ui"; | ||
import { UserCog2 } from "lucide-react"; | ||
// image | ||
import instanceSetupDone from "public/instance-setup-done.svg"; | ||
import PlaneLogo from "public/plane-logos/blue-without-text.png"; | ||
|
||
export const InstanceSetupDone = () => ( | ||
<div className="h-screen w-full overflow-hidden"> | ||
<div className={`bg-onboarding-gradient-100 h-screen w-full pt-12`}> | ||
<div className="h-full bg-onboarding-gradient-100 md:w-2/3 sm:w-4/5 px-4 pt-4 rounded-t-md mx-auto shadow-sm border-x border-t border-custom-border-200 "> | ||
<div | ||
className={`flex flex-col items-center relative px-7 sm:px-0 bg-onboarding-gradient-200 h-full rounded-t-md overflow-auto`} | ||
> | ||
<div className="flex items-center gap-5 py-10 justify-center"> | ||
<Image src={PlaneLogo} height={44} width={44} alt="image" /> | ||
<span className="text-4xl font-semibold">To the stratosphere now!</span> | ||
</div> | ||
|
||
<div className="flex items-center justify-center"> | ||
<Image src={instanceSetupDone} height={360} width={444} alt="image" /> | ||
</div> | ||
|
||
<div className="flex flex-col gap-8 items-center py-12 w-full"> | ||
<span className="text-xl font-medium"> | ||
Your instance is now ready for more security, more controls, and more intelligence. | ||
</span> | ||
<Button size="lg" prependIcon={<UserCog2 />}> | ||
Go to God Mode | ||
</Button> | ||
|
||
<div className="flex p-2.5 text-custom-primary-100 bg-custom-primary-10 border border-custom-primary-100 text-sm text-left mx-auto rounded"> | ||
Use this wisely. Remember, with great power comes great responsibility.🕷️ | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import { FC } from "react"; | ||
import { useForm, Controller } from "react-hook-form"; | ||
// ui | ||
import { Input, Button } from "@plane/ui"; | ||
// icons | ||
import { XCircle } from "lucide-react"; | ||
// services | ||
import { AuthService } from "services/auth.service"; | ||
const authService = new AuthService(); | ||
// hooks | ||
import useToast from "hooks/use-toast"; | ||
import useTimer from "hooks/use-timer"; | ||
|
||
export interface InstanceSetupEmailCodeFormValues { | ||
email: string; | ||
token: string; | ||
} | ||
|
||
export interface IInstanceSetupEmailCodeForm { | ||
email: string; | ||
handleNextStep: () => void; | ||
moveBack: () => void; | ||
} | ||
|
||
export const InstanceSetupEmailCodeForm: FC<IInstanceSetupEmailCodeForm> = (props) => { | ||
const { handleNextStep, email, moveBack } = props; | ||
// form info | ||
const { | ||
control, | ||
handleSubmit, | ||
reset, | ||
formState: { isSubmitting }, | ||
} = useForm<InstanceSetupEmailCodeFormValues>({ | ||
defaultValues: { | ||
email, | ||
token: "", | ||
}, | ||
}); | ||
// hooks | ||
const { setToastAlert } = useToast(); | ||
const { timer, setTimer } = useTimer(30); | ||
// computed | ||
const isResendDisabled = timer > 0 || isSubmitting; | ||
|
||
const handleEmailCodeFormSubmit = (formValues: InstanceSetupEmailCodeFormValues) => | ||
authService | ||
.instanceMagicSignIn({ key: `magic_${formValues.email}`, token: formValues.token }) | ||
.then(() => { | ||
reset(); | ||
handleNextStep(); | ||
}) | ||
.catch((err) => { | ||
setToastAlert({ | ||
title: "Oops!", | ||
type: "error", | ||
message: err?.error, | ||
}); | ||
}); | ||
|
||
const resendMagicCode = () => { | ||
setTimer(30); | ||
authService | ||
.instanceAdminEmailCode({ email }) | ||
.then(() => { | ||
// setCodeResending(false); | ||
setTimer(30); | ||
}) | ||
.catch((err) => { | ||
setToastAlert({ | ||
title: "Oops!", | ||
type: "error", | ||
message: err?.error, | ||
}); | ||
}); | ||
}; | ||
|
||
return ( | ||
<form onSubmit={handleSubmit(handleEmailCodeFormSubmit)}> | ||
<div className="pb-2"> | ||
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-onboarding-text-100"> | ||
Let’s secure your instance | ||
</h1> | ||
<div className="text-center text-sm text-onboarding-text-200 mt-3"> | ||
<p>Paste the code you got at </p> | ||
<span className="text-center text-sm text-custom-primary-80 mt-1 font-semibold ">{email}</span> | ||
<span className="text-onboarding-text-200">below.</span> | ||
</div> | ||
|
||
<div className="relative mt-10 w-full sm:w-[360px] mx-auto"> | ||
<Controller | ||
name="email" | ||
control={control} | ||
rules={{ | ||
required: "Email address is required", | ||
validate: (value) => | ||
/^(([^<>()[\]\\.,;:\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 } }) => ( | ||
<div className={`flex items-center relative rounded-md bg-onboarding-background-200 mb-4`}> | ||
<Input | ||
id="email" | ||
name="email" | ||
type="email" | ||
value={value} | ||
onChange={onChange} | ||
disabled | ||
placeholder="[email protected]" | ||
className={`w-full h-[46px] placeholder:text-onboarding-text-400 border border-onboarding-border-100 pr-12`} | ||
/> | ||
<XCircle | ||
className="h-5 w-5 absolute stroke-custom-text-400 hover:cursor-pointer right-3" | ||
onClick={() => moveBack()} | ||
/> | ||
</div> | ||
)} | ||
/> | ||
<div | ||
className={`flex w-full justify-end text-xs outline-none ${ | ||
isResendDisabled ? "cursor-default text-custom-text-200" : "cursor-pointer text-custom-primary-100" | ||
} `} | ||
> | ||
{timer > 0 ? ( | ||
<span className="text-right">Request new code in {timer}s</span> | ||
) : isSubmitting ? ( | ||
"Sending new code..." | ||
) : ( | ||
<div className="flex justify-end w-full"> | ||
<button | ||
type="button" | ||
className="w-fit pb-2 text-xs outline-none cursor-pointer text-custom-primary-100" | ||
onClick={resendMagicCode} | ||
disabled={isResendDisabled} | ||
> | ||
<span className="font-medium">Resend</span> | ||
</button> | ||
</div> | ||
)} | ||
</div> | ||
<Controller | ||
name="token" | ||
control={control} | ||
rules={{ required: true }} | ||
render={({ field: { value, onChange } }) => ( | ||
<div className={`flex items-center relative rounded-md bg-onboarding-background-200 mb-4`}> | ||
<Input | ||
id="token" | ||
name="token" | ||
type="text" | ||
value={value} | ||
onChange={onChange} | ||
placeholder="gets-sets-flys" | ||
className="border-onboarding-border-100 h-[46px] w-full " | ||
/> | ||
</div> | ||
)} | ||
/> | ||
|
||
<Button variant="primary" className="w-full mt-4" size="xl" type="submit" loading={isSubmitting}> | ||
{isSubmitting ? "Verifying..." : "Next step"} | ||
</Button> | ||
</div> | ||
</div> | ||
</form> | ||
); | ||
}; |
Oops, something went wrong.