Sync: Community Changes #3885

This commit is contained in:
sriram veeraghanta
2025-08-11 18:51:50 +05:30
committed by GitHub
79 changed files with 438 additions and 493 deletions

View File

@@ -0,0 +1,12 @@
"use client";
import Link from "next/link";
import { PlaneLockup } from "@plane/ui";
export const AuthHeader = () => (
<div className="flex items-center justify-between gap-6 w-full flex-shrink-0 sticky top-0">
<Link href="/">
<PlaneLockup height={20} width={95} className="text-custom-text-100" />
</Link>
</div>
);

View File

@@ -1,33 +1,9 @@
"use client";
import Image from "next/image";
import Link from "next/link";
import { useTheme } from "next-themes";
import { PlaneLockup } from "@plane/ui";
// logo assets
import PlaneBackgroundPatternDark from "public/auth/background-pattern-dark.svg";
import PlaneBackgroundPattern from "public/auth/background-pattern.svg";
export default function RootLayout({ children }: { children: React.ReactNode }) {
const { resolvedTheme } = useTheme();
const patternBackground = resolvedTheme === "light" ? PlaneBackgroundPattern : PlaneBackgroundPatternDark;
return (
<div className="relative">
<div className="h-screen w-full overflow-hidden overflow-y-auto flex flex-col">
<div className="container h-[110px] flex-shrink-0 mx-auto px-5 lg:px-0 flex items-center justify-between gap-5 z-50">
<div className="flex items-center gap-x-2 py-10">
<Link href={`/`}>
<PlaneLockup className="h-7 w-auto text-custom-text-100" />
</Link>
</div>
</div>
<div className="absolute inset-0 z-0">
<Image src={patternBackground} className="w-screen h-full object-cover" alt="Plane background pattern" />
</div>
<div className="relative z-10 flex-grow">{children}</div>
</div>
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
{children}
</div>
);
}

View File

@@ -2,8 +2,8 @@
import { observer } from "mobx-react";
// components
import { LogoSpinner } from "@/components/common/logo-spinner";
import { InstanceFailureView } from "@/components/instance/failure";
import { InstanceLoading } from "@/components/instance/loading";
import { InstanceSetupForm } from "@/components/instance/setup-form";
// hooks
import { useInstance } from "@/hooks/store";
@@ -17,46 +17,24 @@ const HomePage = () => {
// if instance is not fetched, show loading
if (!instance && !error) {
return (
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
<InstanceLoading />
<div className="flex items-center justify-center h-screen w-full">
<LogoSpinner />
</div>
);
}
// if instance fetch fails, show failure view
if (error) {
return (
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
<InstanceFailureView />
</div>
);
return <InstanceFailureView />;
}
// if instance is fetched and setup is not done, show setup form
if (instance && !instance?.is_setup_done) {
return (
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
<InstanceSetupForm />
</div>
);
return <InstanceSetupForm />;
}
// if instance is fetched and setup is done, show sign in form
return (
<div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10 lg:pt-28 transition-all">
<div className="relative flex flex-col space-y-6">
<div className="text-center space-y-1">
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">
Manage your Plane instance
</h3>
<p className="font-medium text-onboarding-text-400">
Configure instance-wide settings to secure your instance
</p>
</div>
<InstanceSignInForm />
</div>
</div>
);
return <InstanceSignInForm />;
};
export default observer(HomePage);

View File

@@ -10,7 +10,9 @@ import { Button, Input, Spinner } from "@plane/ui";
// components
import { Banner } from "@/components/common/banner";
// local components
import { FormHeader } from "../../../core/components/instance/form-header";
import { AuthBanner } from "./auth-banner";
import { AuthHeader } from "./auth-header";
import { authErrorHandler } from "./auth-helpers";
// service initialization
@@ -101,78 +103,91 @@ export const InstanceSignInForm: FC = () => {
}, [errorCode]);
return (
<form
className="space-y-4"
method="POST"
action={`${API_BASE_URL}/api/instances/admins/sign-in/`}
onSubmit={() => setIsSubmitting(true)}
onError={() => setIsSubmitting(false)}
>
{errorData.type && errorData?.message ? (
<Banner type="error" message={errorData?.message} />
) : (
<>{errorInfo && <AuthBanner bannerData={errorInfo} handleBannerData={(value) => setErrorInfo(value)} />}</>
)}
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="email"
name="email"
type="email"
inputSize="md"
placeholder="name@company.com"
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
autoComplete="on"
autoFocus
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password">
Password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="password"
name="password"
type={showPassword ? "text" : "password"}
inputSize="md"
placeholder="Enter your password"
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
autoComplete="on"
<>
<AuthHeader />
<div className="flex flex-col justify-center items-center flex-grow w-full py-6 mt-10">
<div className="relative flex flex-col gap-6 max-w-[22.5rem] w-full">
<FormHeader
heading="Manage your Plane instance"
subHeading="Configure instance-wide settings to secure your instance"
/>
{showPassword ? (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(false)}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(true)}
>
<Eye className="h-4 w-4" />
</button>
)}
<form
className="space-y-4"
method="POST"
action={`${API_BASE_URL}/api/instances/admins/sign-in/`}
onSubmit={() => setIsSubmitting(true)}
onError={() => setIsSubmitting(false)}
>
{errorData.type && errorData?.message ? (
<Banner type="error" message={errorData?.message} />
) : (
<>
{errorInfo && <AuthBanner bannerData={errorInfo} handleBannerData={(value) => setErrorInfo(value)} />}
</>
)}
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="email"
name="email"
type="email"
inputSize="md"
placeholder="name@company.com"
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
autoComplete="on"
autoFocus
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="password">
Password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="password"
name="password"
type={showPassword ? "text" : "password"}
inputSize="md"
placeholder="Enter your password"
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
autoComplete="on"
/>
{showPassword ? (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(false)}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(true)}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Sign in"}
</Button>
</div>
</form>
</div>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Sign in"}
</Button>
</div>
</form>
</>
);
};

View File

@@ -7,11 +7,11 @@ import LogoSpinnerLight from "@/public/images/logo-spinner-light.gif";
export const LogoSpinner = () => {
const { resolvedTheme } = useTheme();
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerDark : LogoSpinnerLight;
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerLight : LogoSpinnerDark;
return (
<div className="flex items-center justify-center">
<Image src={logoSrc} alt="logo" className="w-[82px] h-[82px] mr-2" priority={false} />
<Image src={logoSrc} alt="logo" className="h-6 w-auto sm:h-11" />
</div>
);
};

View File

@@ -1,13 +1,15 @@
"use client";
import { FC } from "react";
import { observer } from "mobx-react";
import Image from "next/image";
import { useTheme } from "next-themes";
import { Button } from "@plane/ui";
// assets
import { AuthHeader } from "@/app/(all)/(home)/auth-header";
import InstanceFailureDarkImage from "@/public/instance/instance-failure-dark.svg";
import InstanceFailureImage from "@/public/instance/instance-failure.svg";
export const InstanceFailureView: FC = () => {
export const InstanceFailureView: FC = observer(() => {
const { resolvedTheme } = useTheme();
const instanceImage = resolvedTheme === "dark" ? InstanceFailureDarkImage : InstanceFailureImage;
@@ -17,22 +19,24 @@ export const InstanceFailureView: FC = () => {
};
return (
<div className="h-full w-full relative container px-5 mx-auto flex justify-center items-center">
<div className="w-auto max-w-2xl relative space-y-8 py-10">
<div className="relative flex flex-col justify-center items-center space-y-4">
<Image src={instanceImage} alt="Plane Logo" />
<h3 className="font-medium text-2xl text-white ">Unable to fetch instance details.</h3>
<p className="font-medium text-base text-center">
We were unable to fetch the details of the instance. <br />
Fret not, it might just be a connectivity issue.
</p>
</div>
<div className="flex justify-center">
<Button size="md" onClick={handleRetry}>
Retry
</Button>
<>
<AuthHeader />
<div className="flex flex-col justify-center items-center flex-grow w-full py-6 mt-10">
<div className="relative flex flex-col gap-6 max-w-[22.5rem] w-full">
<div className="relative flex flex-col justify-center items-center space-y-4">
<Image src={instanceImage} alt="Plane Logo" />
<h3 className="font-medium text-2xl text-white text-center">Unable to fetch instance details.</h3>
<p className="font-medium text-base text-center">
We were unable to fetch the details of the instance. Fret not, it might just be a connectivity issue.
</p>
</div>
<div className="flex justify-center">
<Button size="md" onClick={handleRetry}>
Retry
</Button>
</div>
</div>
</div>
</div>
</>
);
};
});

View File

@@ -0,0 +1,8 @@
"use client";
export const FormHeader = ({ heading, subHeading }: { heading: string; subHeading: string }) => (
<div className="flex flex-col gap-1">
<span className="text-2xl font-semibold text-custom-text-100 leading-7">{heading}</span>
<span className="text-lg font-semibold text-custom-text-400 leading-7">{subHeading}</span>
</div>
);

View File

@@ -13,7 +13,7 @@ export const InstanceNotReady: FC = () => (
<div className="relative flex flex-col justify-center items-center space-y-4">
<h1 className="text-3xl font-bold pb-3">Welcome aboard Plane!</h1>
<Image src={PlaneTakeOffImage} alt="Plane Logo" />
<p className="font-medium text-base text-onboarding-text-400">
<p className="font-medium text-base text-custom-text-400">
Get started by setting up your instance and workspace
</p>
</div>

View File

@@ -6,16 +6,12 @@ import LogoSpinnerLight from "@/public/images/logo-spinner-light.gif";
export const InstanceLoading = () => {
const { resolvedTheme } = useTheme();
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerDark : LogoSpinnerLight;
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerLight : LogoSpinnerDark;
return (
<div className="h-full w-full relative container px-5 mx-auto flex justify-center items-center">
<div className="w-auto max-w-2xl relative space-y-8 py-10">
<div className="relative flex flex-col justify-center items-center space-y-4">
<Image src={logoSrc} alt="logo" className="w-[82px] h-[82px] mr-2" priority={false} />
<h3 className="font-medium text-2xl text-white ">Fetching instance details...</h3>
</div>
</div>
<div className="flex items-center justify-center">
<Image src={logoSrc} alt="logo" className="h-6 w-auto sm:h-11" />
</div>
);
};

View File

@@ -10,7 +10,9 @@ import { AuthService } from "@plane/services";
import { Button, Checkbox, Input, PasswordStrengthIndicator, Spinner } from "@plane/ui";
import { getPasswordStrength } from "@plane/utils";
// components
import { AuthHeader } from "@/app/(all)/(home)/auth-header";
import { Banner } from "@/components/common/banner";
import { FormHeader } from "@/components/instance/form-header";
// service initialization
const authService = new AuthService();
@@ -131,227 +133,221 @@ export const InstanceSetupForm: FC = (props) => {
const renderPasswordMatchError = !isRetryPasswordInputFocused || confirmPassword.length >= password.length;
return (
<div className="max-w-lg lg:max-w-md w-full">
<div className="relative flex flex-col space-y-6">
<div className="text-center space-y-1">
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">
Setup your Plane Instance
</h3>
<p className="font-medium text-onboarding-text-400">
Post setup you will be able to manage this Plane instance.
</p>
<>
<AuthHeader />
<div className="flex flex-col justify-center items-center flex-grow w-full py-6 mt-10">
<div className="relative flex flex-col gap-6 max-w-[22.5rem] w-full">
<FormHeader
heading="Setup your Plane Instance"
subHeading="Post setup you will be able to manage this Plane instance."
/>
{errorData.type &&
errorData?.message &&
![EErrorCodes.INVALID_EMAIL, EErrorCodes.INVALID_PASSWORD].includes(errorData.type) && (
<Banner type="error" message={errorData?.message} />
)}
<form
className="space-y-4"
method="POST"
action={`${API_BASE_URL}/api/instances/admins/sign-up/`}
onSubmit={() => setIsSubmitting(true)}
onError={() => setIsSubmitting(false)}
>
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<input type="hidden" name="is_telemetry_enabled" value={formData.is_telemetry_enabled ? "True" : "False"} />
<div className="flex flex-col sm:flex-row items-center gap-4">
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="first_name">
First name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="first_name"
name="first_name"
type="text"
inputSize="md"
placeholder="Wilber"
value={formData.first_name}
onChange={(e) => handleFormChange("first_name", e.target.value)}
autoComplete="on"
autoFocus
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="last_name">
Last name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="last_name"
name="last_name"
type="text"
inputSize="md"
placeholder="Wright"
value={formData.last_name}
onChange={(e) => handleFormChange("last_name", e.target.value)}
autoComplete="on"
/>
</div>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="email"
name="email"
type="email"
inputSize="md"
placeholder="name@company.com"
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
hasError={errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL ? true : false}
autoComplete="on"
/>
{errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL && errorData.message && (
<p className="px-1 text-xs text-red-500">{errorData.message}</p>
)}
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="company_name">
Company name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="company_name"
name="company_name"
type="text"
inputSize="md"
placeholder="Company name"
value={formData.company_name}
onChange={(e) => handleFormChange("company_name", e.target.value)}
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="password">
Set a password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
id="password"
name="password"
type={showPassword.password ? "text" : "password"}
inputSize="md"
placeholder="New password..."
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
hasError={errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD ? true : false}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="on"
/>
{showPassword.password ? (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("password")}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("password")}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
{errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD && errorData.message && (
<p className="px-1 text-xs text-red-500">{errorData.message}</p>
)}
<PasswordStrengthIndicator password={formData.password} isFocused={isPasswordInputFocused} />
</div>
<div className="w-full space-y-1">
<label className="text-sm text-custom-text-300 font-medium" htmlFor="confirm_password">
Confirm password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
type={showPassword.retypePassword ? "text" : "password"}
id="confirm_password"
name="confirm_password"
inputSize="md"
value={formData.confirm_password}
onChange={(e) => handleFormChange("confirm_password", e.target.value)}
placeholder="Confirm password"
className="w-full border border-custom-border-100 !bg-custom-background-100 pr-12 placeholder:text-custom-text-400"
onFocus={() => setIsRetryPasswordInputFocused(true)}
onBlur={() => setIsRetryPasswordInputFocused(false)}
/>
{showPassword.retypePassword ? (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("retypePassword")}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("retypePassword")}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
{!!formData.confirm_password &&
formData.password !== formData.confirm_password &&
renderPasswordMatchError && <span className="text-sm text-red-500">Passwords don{"'"}t match</span>}
</div>
<div className="relative flex gap-2">
<div>
<Checkbox
className="w-4 h-4"
iconClassName="w-3 h-3"
id="is_telemetry_enabled"
onChange={() => handleFormChange("is_telemetry_enabled", !formData.is_telemetry_enabled)}
checked={formData.is_telemetry_enabled}
/>
</div>
<label className="text-sm text-custom-text-300 font-medium cursor-pointer" htmlFor="is_telemetry_enabled">
Allow Plane to anonymously collect usage events.{" "}
<a
tabIndex={-1}
href="https://developers.plane.so/self-hosting/telemetry"
target="_blank"
rel="noopener noreferrer"
className="text-sm font-medium text-blue-500 hover:text-blue-600 flex-shrink-0"
>
See More
</a>
</label>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
</Button>
</div>
</form>
</div>
{errorData.type &&
errorData?.message &&
![EErrorCodes.INVALID_EMAIL, EErrorCodes.INVALID_PASSWORD].includes(errorData.type) && (
<Banner type="error" message={errorData?.message} />
)}
<form
className="space-y-4"
method="POST"
action={`${API_BASE_URL}/api/instances/admins/sign-up/`}
onSubmit={() => setIsSubmitting(true)}
onError={() => setIsSubmitting(false)}
>
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<input type="hidden" name="is_telemetry_enabled" value={formData.is_telemetry_enabled ? "True" : "False"} />
<div className="flex flex-col sm:flex-row items-center gap-4">
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="first_name">
First name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="first_name"
name="first_name"
type="text"
inputSize="md"
placeholder="Wilber"
value={formData.first_name}
onChange={(e) => handleFormChange("first_name", e.target.value)}
autoComplete="on"
autoFocus
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="last_name">
Last name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="last_name"
name="last_name"
type="text"
inputSize="md"
placeholder="Wright"
value={formData.last_name}
onChange={(e) => handleFormChange("last_name", e.target.value)}
autoComplete="on"
/>
</div>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
Email <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="email"
name="email"
type="email"
inputSize="md"
placeholder="name@company.com"
value={formData.email}
onChange={(e) => handleFormChange("email", e.target.value)}
hasError={errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL ? true : false}
autoComplete="on"
/>
{errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL && errorData.message && (
<p className="px-1 text-xs text-red-500">{errorData.message}</p>
)}
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="company_name">
Company name <span className="text-red-500">*</span>
</label>
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="company_name"
name="company_name"
type="text"
inputSize="md"
placeholder="Company name"
value={formData.company_name}
onChange={(e) => handleFormChange("company_name", e.target.value)}
/>
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password">
Set a password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
id="password"
name="password"
type={showPassword.password ? "text" : "password"}
inputSize="md"
placeholder="New password..."
value={formData.password}
onChange={(e) => handleFormChange("password", e.target.value)}
hasError={errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD ? true : false}
onFocus={() => setIsPasswordInputFocused(true)}
onBlur={() => setIsPasswordInputFocused(false)}
autoComplete="on"
/>
{showPassword.password ? (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("password")}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("password")}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
{errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD && errorData.message && (
<p className="px-1 text-xs text-red-500">{errorData.message}</p>
)}
<PasswordStrengthIndicator password={formData.password} isFocused={isPasswordInputFocused} />
</div>
<div className="w-full space-y-1">
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="confirm_password">
Confirm password <span className="text-red-500">*</span>
</label>
<div className="relative">
<Input
type={showPassword.retypePassword ? "text" : "password"}
id="confirm_password"
name="confirm_password"
inputSize="md"
value={formData.confirm_password}
onChange={(e) => 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 ? (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("retypePassword")}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
type="button"
tabIndex={-1}
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
onClick={() => handleShowPassword("retypePassword")}
>
<Eye className="h-4 w-4" />
</button>
)}
</div>
{!!formData.confirm_password &&
formData.password !== formData.confirm_password &&
renderPasswordMatchError && <span className="text-sm text-red-500">Passwords don{"'"}t match</span>}
</div>
<div className="relative flex items-center pt-2 gap-2">
<div>
<Checkbox
className="w-4 h-4"
iconClassName="w-3 h-3"
id="is_telemetry_enabled"
onChange={() => handleFormChange("is_telemetry_enabled", !formData.is_telemetry_enabled)}
checked={formData.is_telemetry_enabled}
/>
</div>
<label
className="text-sm text-onboarding-text-300 font-medium cursor-pointer"
htmlFor="is_telemetry_enabled"
>
Allow Plane to anonymously collect usage events.
</label>
<a
tabIndex={-1}
href="https://developers.plane.so/self-hosting/telemetry"
target="_blank"
rel="noopener noreferrer"
className="text-sm font-medium text-blue-500 hover:text-blue-600"
>
See More
</a>
</div>
<div className="py-2">
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Continue"}
</Button>
</div>
</form>
</div>
</div>
</>
);
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 954 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -135,28 +135,6 @@
--color-border-300: 212, 212, 212; /* strong border- 1 */
--color-border-400: 185, 185, 185; /* strong border- 2 */
/* onboarding colors */
--gradient-onboarding-100: linear-gradient(106deg, #f2f6ff 29.8%, #e1eaff 99.34%);
--gradient-onboarding-200: linear-gradient(129deg, rgba(255, 255, 255, 0) -22.23%, rgba(255, 255, 255, 0.8) 62.98%);
--gradient-onboarding-300: linear-gradient(164deg, #fff 4.25%, rgba(255, 255, 255, 0.06) 93.5%);
--gradient-onboarding-400: linear-gradient(129deg, rgba(255, 255, 255, 0) -22.23%, rgba(255, 255, 255, 0.8) 62.98%);
--color-onboarding-text-100: 23, 23, 23;
--color-onboarding-text-200: 58, 58, 58;
--color-onboarding-text-300: 82, 82, 82;
--color-onboarding-text-400: 163, 163, 163;
--color-onboarding-background-100: 236, 241, 255;
--color-onboarding-background-200: 255, 255, 255;
--color-onboarding-background-300: 236, 241, 255;
--color-onboarding-background-400: 177, 206, 250;
--color-onboarding-border-100: 229, 229, 229;
--color-onboarding-border-200: 217, 228, 255;
--color-onboarding-border-300: 229, 229, 229, 0.5;
--color-onboarding-shadow-sm: 0px 4px 20px 0px rgba(126, 139, 171, 0.1);
/* toast theme */
--color-toast-success-text: 62, 155, 79;
--color-toast-error-text: 220, 62, 66;
@@ -244,27 +222,6 @@
--color-border-300: 46, 46, 46; /* strong border- 1 */
--color-border-400: 58, 58, 58; /* strong border- 2 */
/* onboarding colors */
--gradient-onboarding-100: linear-gradient(106deg, #18191b 25.17%, #18191b 99.34%);
--gradient-onboarding-200: linear-gradient(129deg, rgba(47, 49, 53, 0.8) -22.23%, rgba(33, 34, 37, 0.8) 62.98%);
--gradient-onboarding-300: linear-gradient(167deg, rgba(47, 49, 53, 0.45) 19.22%, #212225 98.48%);
--color-onboarding-text-100: 237, 238, 240;
--color-onboarding-text-200: 176, 180, 187;
--color-onboarding-text-300: 118, 123, 132;
--color-onboarding-text-400: 105, 110, 119;
--color-onboarding-background-100: 54, 58, 64;
--color-onboarding-background-200: 40, 42, 45;
--color-onboarding-background-300: 40, 42, 45;
--color-onboarding-background-400: 67, 72, 79;
--color-onboarding-border-100: 54, 58, 64;
--color-onboarding-border-200: 54, 58, 64;
--color-onboarding-border-300: 34, 35, 38, 0.5;
--color-onboarding-shadow-sm: 0px 4px 20px 0px rgba(39, 44, 56, 0.1);
/* toast theme */
--color-toast-success-text: 178, 221, 181;
--color-toast-error-text: 206, 44, 49;

View File

@@ -8,7 +8,7 @@ import LogoSpinnerLight from "@/public/images/logo-spinner-light.gif";
export const LogoSpinner = () => {
const { resolvedTheme } = useTheme();
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerDark : LogoSpinnerLight;
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerLight : LogoSpinnerDark;
return (
<div className="flex items-center justify-center">

View File

@@ -68,7 +68,8 @@ export const BarChart = React.memo(<K extends string, T extends string>(props: T
opacity={!!activeLegend && activeLegend !== bar.key ? 0.1 : 1}
shape={(shapeProps: any) => {
const shapeVariant = barShapeVariants[bar.shapeVariant ?? "bar"];
return shapeVariant(shapeProps, bar, stackKeys) as any;
const node = shapeVariant(shapeProps, bar, stackKeys);
return React.isValidElement(node) ? node : <>{node}</>;
}}
className="[&_path]:transition-opacity [&_path]:duration-200"
onMouseEnter={() => setActiveBar(bar.key)}

View File

@@ -26,9 +26,15 @@
"fix:format": "prettier --write \"**/*.{ts,tsx,md,json,css,scss}\"",
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@plane/eslint-config": "*",
"@plane/typescript-config": "*",
"@types/react": "18.3.11",
"@types/react-dom": "18.3.1",
"tsup": "8.4.0",
"typescript": "5.8.3"
}

View File

@@ -1,5 +1,5 @@
import React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
export interface AuthForgotPasswordProps {
onForgotPassword?: () => void;

View File

@@ -1,6 +1,6 @@
import React, { useState, useMemo } from "react";
import { E_PASSWORD_STRENGTH } from "@plane/constants";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { Button } from "../button/button";
import { Spinner } from "../spinners/circular-spinner";
import { AuthConfirmPasswordInput } from "./auth-confirm-password-input";

View File

@@ -1,6 +1,6 @@
import { Eye, EyeOff } from "lucide-react";
import React, { useState } from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { Input } from "../form-fields/input";
export interface AuthInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "autoComplete"> {

View File

@@ -2,7 +2,7 @@ import React from "react";
// ui
import { Tooltip } from "../tooltip";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
// types
import { TAvatarSize, getSizeInfo, isAValidNumber } from "./avatar";

View File

@@ -2,7 +2,7 @@ import React from "react";
// ui
import { Tooltip } from "../tooltip";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
export type TAvatarSize = "sm" | "md" | "base" | "lg" | number;

View File

@@ -1,7 +1,7 @@
import * as React from "react";
// helpers
import { getIconStyling, getBadgeStyling, TBadgeVariant, TBadgeSizes } from "./helper";
import { cn } from "../../helpers";
import { cn } from "../utils";
export interface BadgeProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: TBadgeVariant;

View File

@@ -1,6 +1,6 @@
import { ChevronRight } from "lucide-react";
import * as React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { Tooltip } from "../tooltip";
type BreadcrumbsProps = {

View File

@@ -2,7 +2,7 @@
import { CheckIcon } from "lucide-react";
import * as React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
// ui
import { CustomMenu, TContextMenuItem } from "../dropdowns";
import { Tooltip } from "../tooltip";

View File

@@ -1,7 +1,7 @@
import * as React from "react";
import { useState } from "react";
import { ICustomSearchSelectOption } from "@plane/types";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { CustomSearchSelect } from "../dropdowns";
import { Tooltip } from "../tooltip";
import { Breadcrumbs } from "./breadcrumbs";

View File

@@ -1,7 +1,7 @@
import * as React from "react";
import { getIconStyling, getButtonStyling, TButtonVariant, TButtonSizes } from "./helper";
import { cn } from "../../helpers";
import { cn } from "../utils";
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: TButtonVariant;

View File

@@ -1,7 +1,7 @@
import * as React from "react";
import { Switch } from "@headlessui/react";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
interface IToggleSwitchProps {
value: boolean;

View File

@@ -4,7 +4,7 @@ import { ChevronLeft } from "lucide-react";
import * as React from "react";
import { DayPicker } from "react-day-picker";
import { cn } from "../helpers";
import { cn } from "./utils";
export type CalendarProps = React.ComponentProps<typeof DayPicker>;

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
import {
ECardDirection,
ECardSpacing,

View File

@@ -1,5 +1,5 @@
import React, { FC } from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { DropdownIcon, ISvgIcons } from "../icons";
type Props = {

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { Row } from "../row";
import { ERowVariant, TRowVariant } from "../row/helper";

View File

@@ -1,7 +1,7 @@
import React, { forwardRef } from "react";
import { MoreVertical } from "lucide-react";
import React, { forwardRef } from "react";
// helpers
import { cn } from "../helpers";
import { cn } from "./utils";
interface IDragHandle {
className?: string;

View File

@@ -1,5 +1,5 @@
import React from "react";
import { cn } from "../helpers";
import { cn } from "./utils";
type Props = {
isVisible: boolean;

View File

@@ -1,8 +1,7 @@
import React, { Fragment } from "react";
// headless ui
import { Combobox } from "@headlessui/react";
import React, { Fragment } from "react";
// helper
import { cn } from "../../../helpers";
import { cn } from "../../utils";
import { IMultiSelectDropdownButton, ISingleSelectDropdownButton } from "../dropdown";
export const DropdownButton: React.FC<IMultiSelectDropdownButton | ISingleSelectDropdownButton> = (props) => {

View File

@@ -1,10 +1,8 @@
import React, { FC, useEffect, useRef } from "react";
// headless ui
import { Combobox } from "@headlessui/react";
// icons
import { Search } from "lucide-react";
import React, { FC, useEffect, useRef } from "react";
// helpers
import { cn } from "../../../helpers";
import { cn } from "../../utils";
interface IInputSearch {
isOpen: boolean;
@@ -32,9 +30,11 @@ export const InputSearch: FC<IInputSearch> = (props) => {
useEffect(() => {
if (isOpen && !isMobile) {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
inputRef.current && inputRef.current.focus();
}
}, [isOpen, isMobile]);
return (
<div
className={cn(

View File

@@ -1,14 +1,12 @@
import React from "react";
// headless ui
import { Combobox } from "@headlessui/react";
// icons
import { Check } from "lucide-react";
// components
import { DropdownOptionsLoader, InputSearch } from ".";
import React from "react";
// helpers
import { cn } from "../../../helpers";
import { cn } from "../../utils";
// types
import { IMultiSelectDropdownOptions, ISingleSelectDropdownOptions } from "../dropdown";
// components
import { DropdownOptionsLoader, InputSearch } from ".";
export const DropdownOptions: React.FC<IMultiSelectDropdownOptions | ISingleSelectDropdownOptions> = (props) => {
const {

View File

@@ -5,7 +5,7 @@ import { usePopper } from "react-popper";
// plane imports
import { useOutsideClickDetector } from "@plane/hooks";
// local imports
import { cn } from "../../helpers";
import { cn } from "../utils";
import { useDropdownKeyPressed } from "../hooks/use-dropdown-key-pressed";
import { DropdownButton } from "./common";
import { DropdownOptions } from "./common/options";

View File

@@ -5,7 +5,7 @@ import { usePopper } from "react-popper";
// plane imports
import { useOutsideClickDetector } from "@plane/hooks";
// local imports
import { cn } from "../../helpers";
import { cn } from "../utils";
import { useDropdownKeyPressed } from "../hooks/use-dropdown-key-pressed";
import { DropdownButton } from "./common";
import { DropdownOptions } from "./common/options";

View File

@@ -2,7 +2,7 @@ import { ChevronRight } from "lucide-react";
import React, { useState, useRef, useContext } from "react";
import { usePopper } from "react-popper";
// helpers
import { cn } from "../../../helpers";
import { cn } from "../../utils";
// types
import { TContextMenuItem, ContextMenuContext, Portal } from "./root";

View File

@@ -1,11 +1,9 @@
import React, { useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom";
// plane helpers
import { useOutsideClickDetector } from "@plane/hooks";
// helpers
import { cn } from "../../../helpers";
// hooks
import { usePlatformOS } from "../../hooks/use-platform-os";
// helpers
import { cn } from "../../utils";
// components
import { ContextMenuItem } from "./item";

View File

@@ -6,7 +6,7 @@ import { usePopper } from "react-popper";
// plane helpers
import { useOutsideClickDetector } from "@plane/hooks";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
// hooks
import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down";
// types

View File

@@ -6,7 +6,7 @@ import { usePopper } from "react-popper";
// plane imports
import { useOutsideClickDetector } from "@plane/hooks";
// local imports
import { cn } from "../../helpers";
import { cn } from "../utils";
import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down";
import { Tooltip } from "../tooltip";
import { ICustomSearchSelectProps } from "./helper";

View File

@@ -7,7 +7,7 @@ import { useOutsideClickDetector } from "@plane/hooks";
// hooks
import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
// types
import { ICustomSelectItemProps, ICustomSelectProps } from "./helper";

View File

@@ -5,7 +5,7 @@ import EmojiPicker from "emoji-picker-react";
// plane helpers
import { useOutsideClickDetector } from "@plane/hooks";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
// hooks
import { LucideIconsList } from "./lucide-icons-list";
// helpers

View File

@@ -7,7 +7,7 @@ import { useOutsideClickDetector } from "@plane/hooks";
// components
import { IconsList } from "./icons-list";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
// hooks
import { EmojiIconPickerTypes, TABS_LIST, TCustomEmojiPicker } from "./emoji-icon-helper";

View File

@@ -3,7 +3,7 @@ import React, { useEffect, useState } from "react";
// icons
import useFontFaceObserver from "use-font-face-observer";
import { MATERIAL_ICONS_LIST } from "..";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { Input } from "../form-fields";
import { InfoIcon } from "../icons";
// components

View File

@@ -2,7 +2,7 @@ import { Search } from "lucide-react";
import React, { useEffect, useState } from "react";
// local imports
import { LUCIDE_ICONS_LIST } from "..";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { Input } from "../form-fields";
import { InfoIcon } from "../icons";
import { DEFAULT_COLORS, TIconsListProps, adjustColorForContrast } from "./emoji-icon-helper";

View File

@@ -1,7 +1,7 @@
import React from "react";
import { Star } from "lucide-react";
import React from "react";
// helpers
import { cn } from "../helpers";
import { cn } from "./utils";
type Props = {
buttonClassName?: string;

View File

@@ -1,6 +1,6 @@
import * as React from "react";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
export interface CheckboxProps extends React.InputHTMLAttributes<HTMLInputElement> {
containerClassName?: string;

View File

@@ -3,7 +3,7 @@ import * as React from "react";
import { ColorResult, SketchPicker } from "react-color";
import { usePopper } from "react-popper";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
// components
import { Button } from "../button";
import { Input } from "./input";

View File

@@ -1,6 +1,6 @@
import * as React from "react";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
mode?: "primary" | "transparent" | "true-transparent";

View File

@@ -1,6 +1,6 @@
import React, { useRef } from "react";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
// hooks
import { useAutoResizeTextArea } from "../hooks/use-auto-resize-textarea";

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { EHeaderVariant, getHeaderStyle, THeaderVariant } from "./helper";
import { ERowVariant, Row } from "../row";

View File

@@ -1,6 +1,6 @@
import * as React from "react";
import { AlertCircle, Ban, SignalHigh, SignalLow, SignalMedium } from "lucide-react";
import { cn } from "../../helpers";
import { cn } from "../utils";
export type TIssuePriorities = "urgent" | "high" | "medium" | "low" | "none";

View File

@@ -1,6 +1,6 @@
import React from "react";
// helpers
import { cn } from "../helpers";
import { cn } from "./utils";
type Props = {
children: React.ReactNode;

View File

@@ -6,7 +6,7 @@ import { ModalCore } from "./modal-core";
// constants
import { EModalPosition, EModalWidth } from "./constants";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
export type TModalVariant = "danger" | "primary";

View File

@@ -3,7 +3,7 @@ import { Dialog, Transition } from "@headlessui/react";
// constants
import { EModalPosition, EModalWidth } from "./constants";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
type Props = {
children: React.ReactNode;

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
export interface OAuthButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
text: string;

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { OAuthButton } from "./oauth-button";
export type TOAuthOption = {

View File

@@ -2,7 +2,7 @@ import React, { Fragment } from "react";
// components
import { Popover } from "./popover";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
// types
import { TPopoverMenu } from "./types";

View File

@@ -2,7 +2,7 @@ import React, { Fragment, Ref, useState } from "react";
import { usePopper } from "react-popper";
import { Popover as HeadlessReactPopover, Transition } from "@headlessui/react";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
// types
import { TPopover } from "./types";
import { EllipsisVertical } from "lucide-react";

View File

@@ -1,6 +1,6 @@
import React from "react";
import { Tooltip } from "../tooltip";
import { cn } from "../../helpers";
import { cn } from "../utils";
type Props = {
data: any;

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { ERowVariant, rowStyle, TRowVariant } from "./helper";
export interface RowProps extends React.HTMLAttributes<HTMLDivElement> {

View File

@@ -1,7 +1,7 @@
"use client";
import * as RadixScrollArea from "@radix-ui/react-scroll-area";
import React, { FC } from "react";
import { cn } from "../helpers";
import { cn } from "./utils";
type TScrollAreaProps = {
type?: "auto" | "always" | "scroll" | "hover";

View File

@@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from "react";
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
import { isEqual } from "lodash";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { attachClosestEdge, extractClosestEdge } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
import { DropIndicator } from "../drop-indicator";

View File

@@ -1,6 +1,6 @@
import * as React from "react";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
export interface ISpinner extends React.SVGAttributes<SVGElement> {
height?: string;

View File

@@ -1,6 +1,6 @@
import React from "react";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
// types
import { TTableData } from "./types";

View File

@@ -2,7 +2,7 @@ import { Tab } from "@headlessui/react";
import { LucideProps } from "lucide-react";
import React, { FC } from "react";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
export type TabListItem = {
key: string;

View File

@@ -2,7 +2,7 @@ import { Tab } from "@headlessui/react";
import React, { FC, Fragment, useEffect, useState } from "react";
// helpers
import { useLocalStorage } from "@plane/hooks";
import { cn } from "../../helpers";
import { cn } from "../utils";
// types
import { TabList, TabListItem } from "./tab-list";

View File

@@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
import { ETagSize, ETagVariant, getTagStyle, TTagSize, TTagVariant } from "./helper";
export interface TagProps extends React.ComponentProps<"div"> {

View File

@@ -5,7 +5,7 @@ import { AlertTriangle, CheckCircle2, X, XCircle } from "lucide-react";
// spinner
import { CircularBarSpinner } from "../spinners";
// helper
import { cn } from "../../helpers";
import { cn } from "../utils";
export enum TOAST_TYPE {
SUCCESS = "success",

View File

@@ -1,7 +1,7 @@
import { Tooltip2 } from "@blueprintjs/popover2";
import React, { useEffect, useRef, useState } from "react";
// helpers
import { cn } from "../../helpers";
import { cn } from "../utils";
export type TPosition =
| "top"
@@ -22,9 +22,9 @@ export type TPosition =
interface ITooltipProps {
tooltipHeading?: string;
tooltipContent: string | any;
tooltipContent: string | React.ReactNode;
position?: TPosition;
children: any;
children: React.ReactElement;
disabled?: boolean;
className?: string;
openDelay?: number;

View File

@@ -1,5 +1,5 @@
import React from "react";
import { cn } from "../../helpers";
import { cn } from "../utils";
type Props = {
children: React.ReactNode;

View File

@@ -1 +1,2 @@
export * from "./classname";
export * from "./icons";

View File

@@ -4768,7 +4768,7 @@
dependencies:
"@types/reactcss" "*"
"@types/react-dom@18.3.0", "@types/react-dom@18.3.5", "@types/react-dom@^18.2.18":
"@types/react-dom@18.3.0", "@types/react-dom@18.3.1", "@types/react-dom@18.3.5", "@types/react-dom@^18.2.18":
version "18.3.5"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.5.tgz#45f9f87398c5dcea085b715c58ddcf1faf65f716"
integrity sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==
@@ -4785,7 +4785,7 @@
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044"
integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==
"@types/react@*", "@types/react@18.3.1", "@types/react@18.3.18", "@types/react@^18.3.11":
"@types/react@*", "@types/react@18.3.1", "@types/react@18.3.11", "@types/react@18.3.18", "@types/react@^18.3.11":
version "18.3.18"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.18.tgz#9b382c4cd32e13e463f97df07c2ee3bbcd26904b"
integrity sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==