"use client"; import { FC, useEffect, useMemo, useState } from "react"; import { useSearchParams } from "next/navigation"; // icons import { Eye, EyeOff } from "lucide-react"; // ui import { Button, Checkbox, Input, Spinner } from "@plane/ui"; // components import { Banner, PasswordStrengthMeter } from "@/components/common"; // helpers import { API_BASE_URL } from "@/helpers/common.helper"; import { getPasswordStrength } from "@/helpers/password.helper"; // services import { AuthService } from "@/services/auth.service"; // service initialization const authService = new AuthService(); // error codes enum EErrorCodes { INSTANCE_NOT_CONFIGURED = "INSTANCE_NOT_CONFIGURED", ADMIN_ALREADY_EXIST = "ADMIN_ALREADY_EXIST", REQUIRED_EMAIL_PASSWORD_FIRST_NAME = "REQUIRED_EMAIL_PASSWORD_FIRST_NAME", INVALID_EMAIL = "INVALID_EMAIL", INVALID_PASSWORD = "INVALID_PASSWORD", USER_ALREADY_EXISTS = "USER_ALREADY_EXISTS", } type TError = { type: EErrorCodes | undefined; message: string | undefined; }; // form data type TFormData = { first_name: string; last_name: string; email: string; company_name: string; password: string; confirm_password?: string; is_telemetry_enabled: boolean; }; const defaultFromData: TFormData = { first_name: "", last_name: "", email: "", company_name: "", password: "", is_telemetry_enabled: true, }; export const InstanceSetupForm: FC = (props) => { const {} = props; // search params const searchParams = useSearchParams(); const firstNameParam = searchParams.get("first_name") || undefined; const lastNameParam = searchParams.get("last_name") || undefined; const companyParam = searchParams.get("company") || undefined; const emailParam = searchParams.get("email") || undefined; const isTelemetryEnabledParam = (searchParams.get("is_telemetry_enabled") === "True" ? true : false) || true; const errorCode = searchParams.get("error_code") || undefined; const errorMessage = searchParams.get("error_message") || undefined; // state const [showPassword, setShowPassword] = useState({ password: false, retypePassword: false, }); const [csrfToken, setCsrfToken] = useState(undefined); const [formData, setFormData] = useState(defaultFromData); const [isPasswordInputFocused, setIsPasswordInputFocused] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [isRetryPasswordInputFocused, setIsRetryPasswordInputFocused] = useState(false); const handleShowPassword = (key: keyof typeof showPassword) => setShowPassword((prev) => ({ ...prev, [key]: !prev[key] })); const handleFormChange = (key: keyof TFormData, value: string | boolean) => setFormData((prev) => ({ ...prev, [key]: value })); useEffect(() => { if (csrfToken === undefined) authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token)); }, [csrfToken]); useEffect(() => { if (firstNameParam) setFormData((prev) => ({ ...prev, first_name: firstNameParam })); if (lastNameParam) setFormData((prev) => ({ ...prev, last_name: lastNameParam })); if (companyParam) setFormData((prev) => ({ ...prev, company_name: companyParam })); if (emailParam) setFormData((prev) => ({ ...prev, email: emailParam })); if (isTelemetryEnabledParam) setFormData((prev) => ({ ...prev, is_telemetry_enabled: isTelemetryEnabledParam })); }, [firstNameParam, lastNameParam, companyParam, emailParam, isTelemetryEnabledParam]); // derived values const errorData: TError = useMemo(() => { if (errorCode && errorMessage) { switch (errorCode) { case EErrorCodes.INSTANCE_NOT_CONFIGURED: return { type: EErrorCodes.INSTANCE_NOT_CONFIGURED, message: errorMessage }; case EErrorCodes.ADMIN_ALREADY_EXIST: return { type: EErrorCodes.ADMIN_ALREADY_EXIST, message: errorMessage }; case EErrorCodes.REQUIRED_EMAIL_PASSWORD_FIRST_NAME: return { type: EErrorCodes.REQUIRED_EMAIL_PASSWORD_FIRST_NAME, message: errorMessage }; case EErrorCodes.INVALID_EMAIL: return { type: EErrorCodes.INVALID_EMAIL, message: errorMessage }; case EErrorCodes.INVALID_PASSWORD: return { type: EErrorCodes.INVALID_PASSWORD, message: errorMessage }; case EErrorCodes.USER_ALREADY_EXISTS: return { type: EErrorCodes.USER_ALREADY_EXISTS, message: errorMessage }; default: return { type: undefined, message: undefined }; } } else return { type: undefined, message: undefined }; }, [errorCode, errorMessage]); const isButtonDisabled = useMemo( () => !isSubmitting && formData.first_name && formData.email && formData.password && getPasswordStrength(formData.password) >= 3 && formData.password === formData.confirm_password ? false : true, [formData.confirm_password, formData.email, formData.first_name, formData.password, isSubmitting] ); const password = formData?.password ?? ""; const confirmPassword = formData?.confirm_password ?? ""; const renderPasswordMatchError = !isRetryPasswordInputFocused || confirmPassword.length >= password.length; return (

Setup your Plane Instance

Post setup you will be able to manage this Plane instance.

{errorData.type && errorData?.message && ![EErrorCodes.INVALID_EMAIL, EErrorCodes.INVALID_PASSWORD].includes(errorData.type) && ( )}
setIsSubmitting(true)} onError={() => setIsSubmitting(false)} >
handleFormChange("first_name", e.target.value)} autoFocus />
handleFormChange("last_name", e.target.value)} />
handleFormChange("email", e.target.value)} hasError={errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL ? true : false} /> {errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL && errorData.message && (

{errorData.message}

)}
handleFormChange("company_name", e.target.value)} />
handleFormChange("password", e.target.value)} hasError={errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD ? true : false} onFocus={() => setIsPasswordInputFocused(true)} onBlur={() => setIsPasswordInputFocused(false)} /> {showPassword.password ? ( ) : ( )}
{errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD && errorData.message && (

{errorData.message}

)} {isPasswordInputFocused && }
handleFormChange("confirm_password", e.target.value)} placeholder="Confirm password" className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400" onFocus={() => setIsRetryPasswordInputFocused(true)} onBlur={() => setIsRetryPasswordInputFocused(false)} /> {showPassword.retypePassword ? ( ) : ( )}
{!!formData.confirm_password && formData.password !== formData.confirm_password && renderPasswordMatchError && Passwords don{"'"}t match}
handleFormChange("is_telemetry_enabled", !formData.is_telemetry_enabled)} checked={formData.is_telemetry_enabled} />
See More
); };