Initial commit: Plane
Some checks failed
Branch Build CE / Build Setup (push) Has been cancelled
Branch Build CE / Build-Push Admin Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Web Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Space Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Live Collaboration Docker Image (push) Has been cancelled
Branch Build CE / Build-Push API Server Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Proxy Docker Image (push) Has been cancelled
Branch Build CE / Build-Push AIO Docker Image (push) Has been cancelled
Branch Build CE / Upload Build Assets (push) Has been cancelled
Branch Build CE / Build Release (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Codespell / Check for spelling errors (push) Has been cancelled
Sync Repositories / sync_changes (push) Has been cancelled

Synced from upstream: 8853637e981ed7d8a6cff32bd98e7afe20f54362
This commit is contained in:
chuan
2025-11-07 00:00:52 +08:00
commit 8ebde8aa05
4886 changed files with 462270 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
import type { FC } from "react";
import { AlertCircle, CheckCircle2 } from "lucide-react";
type TBanner = {
type: "success" | "error";
message: string;
};
export const Banner: FC<TBanner> = (props) => {
const { type, message } = props;
return (
<div
className={`rounded-md p-2 w-full border ${type === "error" ? "bg-red-500/5 border-red-400" : "bg-green-500/5 border-green-400"}`}
>
<div className="flex items-center justify-center">
<div className="flex-shrink-0">
{type === "error" ? (
<span className="flex items-center justify-center h-6 w-6 rounded-full">
<AlertCircle className="h-5 w-5 text-red-600" aria-hidden="true" />
</span>
) : (
<CheckCircle2 className="h-5 w-5 text-green-600" aria-hidden="true" />
)}
</div>
<div className="ml-1">
<p className={`text-sm font-medium ${type === "error" ? "text-red-600" : "text-green-600"}`}>{message}</p>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,38 @@
"use client";
import Link from "next/link";
import { Tooltip } from "@plane/propel/tooltip";
type Props = {
label?: string;
href?: string;
icon?: React.ReactNode | undefined;
};
export const BreadcrumbLink: React.FC<Props> = (props) => {
const { href, label, icon } = props;
return (
<Tooltip tooltipContent={label} position="bottom">
<li className="flex items-center space-x-2" tabIndex={-1}>
<div className="flex flex-wrap items-center gap-2.5">
{href ? (
<Link
className="flex items-center gap-1 text-sm font-medium text-custom-text-300 hover:text-custom-text-100"
href={href}
>
{icon && (
<div className="flex h-5 w-5 items-center justify-center overflow-hidden !text-[1rem]">{icon}</div>
)}
<div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>
</Link>
) : (
<div className="flex cursor-default items-center gap-1 text-sm font-medium text-custom-text-100">
{icon && <div className="flex h-5 w-5 items-center justify-center overflow-hidden">{icon}</div>}
<div className="relative line-clamp-1 block max-w-[150px] overflow-hidden truncate">{label}</div>
</div>
)}
</div>
</li>
</Tooltip>
);
};

View File

@@ -0,0 +1,21 @@
import { cn } from "@plane/utils";
type TProps = {
children: React.ReactNode;
className?: string;
darkerShade?: boolean;
};
export const CodeBlock = ({ children, className, darkerShade }: TProps) => (
<span
className={cn(
"px-0.5 text-xs text-custom-text-300 bg-custom-background-90 font-semibold rounded-md border border-custom-border-100",
{
"text-custom-text-200 bg-custom-background-80 border-custom-border-200": darkerShade,
},
className
)}
>
{children}
</span>
);

View File

@@ -0,0 +1,74 @@
"use client";
import React from "react";
import Link from "next/link";
// headless ui
import { Dialog, Transition } from "@headlessui/react";
// ui
import { Button, getButtonStyling } from "@plane/propel/button";
type Props = {
isOpen: boolean;
handleClose: () => void;
onDiscardHref: string;
};
export const ConfirmDiscardModal: React.FC<Props> = (props) => {
const { isOpen, handleClose, onDiscardHref } = props;
return (
<Transition.Root show={isOpen} as={React.Fragment}>
<Dialog as="div" className="relative z-50" onClose={handleClose}>
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-custom-backdrop transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="my-10 flex items-center justify-center p-4 text-center sm:p-0 md:my-32">
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-custom-background-100 text-left shadow-custom-shadow-md transition-all sm:my-8 sm:w-[30rem]">
<div className="px-4 pb-4 pt-5 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start">
<div className="mt-3 text-center sm:mt-0 sm:text-left">
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-300">
You have unsaved changes
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-custom-text-400">
Changes you made will be lost if you go back. Do you wish to go back?
</p>
</div>
</div>
</div>
</div>
<div className="flex justify-end items-center p-4 sm:px-6 gap-2">
<Button variant="neutral-primary" size="sm" onClick={handleClose}>
Keep editing
</Button>
<Link href={onDiscardHref} className={getButtonStyling("primary", "sm")}>
Go back
</Link>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
};

View File

@@ -0,0 +1,84 @@
"use client";
import React, { useState } from "react";
import type { Control } from "react-hook-form";
import { Controller } from "react-hook-form";
// icons
import { Eye, EyeOff } from "lucide-react";
// plane internal packages
import { Input } from "@plane/ui";
import { cn } from "@plane/utils";
type Props = {
control: Control<any>;
type: "text" | "password";
name: string;
label: string;
description?: string | React.ReactNode;
placeholder: string;
error: boolean;
required: boolean;
};
export type TControllerInputFormField = {
key: string;
type: "text" | "password";
label: string;
description?: string | React.ReactNode;
placeholder: string;
error: boolean;
required: boolean;
};
export const ControllerInput: React.FC<Props> = (props) => {
const { name, control, type, label, description, placeholder, error, required } = props;
// states
const [showPassword, setShowPassword] = useState(false);
return (
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-300">{label}</h4>
<div className="relative">
<Controller
control={control}
name={name}
rules={{ required: required ? `${label} is required.` : false }}
render={({ field: { value, onChange, ref } }) => (
<Input
id={name}
name={name}
type={type === "password" && showPassword ? "text" : type}
value={value}
onChange={onChange}
ref={ref}
hasError={error}
placeholder={placeholder}
className={cn("w-full rounded-md font-medium", {
"pr-10": type === "password",
})}
/>
)}
/>
{type === "password" &&
(showPassword ? (
<button
tabIndex={-1}
className="absolute right-3 top-2.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(false)}
>
<EyeOff className="h-4 w-4" />
</button>
) : (
<button
tabIndex={-1}
className="absolute right-3 top-2.5 flex items-center justify-center text-custom-text-400"
onClick={() => setShowPassword(true)}
>
<Eye className="h-4 w-4" />
</button>
))}
</div>
{description && <p className="pt-0.5 text-xs text-custom-text-300">{description}</p>}
</div>
);
};

View File

@@ -0,0 +1,46 @@
"use client";
import React from "react";
// ui
import { Copy } from "lucide-react";
import { Button } from "@plane/propel/button";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
type Props = {
label: string;
url: string;
description: string | React.ReactNode;
};
export type TCopyField = {
key: string;
label: string;
url: string;
description: string | React.ReactNode;
};
export const CopyField: React.FC<Props> = (props) => {
const { label, url, description } = props;
return (
<div className="flex flex-col gap-1">
<h4 className="text-sm text-custom-text-200">{label}</h4>
<Button
variant="neutral-primary"
className="flex items-center justify-between py-2"
onClick={() => {
navigator.clipboard.writeText(url);
setToast({
type: TOAST_TYPE.INFO,
title: "Copied to clipboard",
message: `The ${label} has been successfully copied to your clipboard`,
});
}}
>
<p className="text-sm font-medium">{url}</p>
<Copy size={18} color="#B9B9B9" />
</Button>
<div className="text-xs text-custom-text-300">{description}</div>
</div>
);
};

View File

@@ -0,0 +1,48 @@
"use client";
import React from "react";
import Image from "next/image";
import { Button } from "@plane/propel/button";
type Props = {
title: string;
description?: React.ReactNode;
image?: string;
primaryButton?: {
icon?: any;
text: string;
onClick: () => void;
};
secondaryButton?: React.ReactNode;
disabled?: boolean;
};
export const EmptyState: React.FC<Props> = ({
title,
description,
image,
primaryButton,
secondaryButton,
disabled = false,
}) => (
<div className={`flex h-full w-full items-center justify-center`}>
<div className="flex w-full flex-col items-center text-center">
{image && <Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text || "button image"} />}
<h6 className="mb-3 mt-6 text-xl font-semibold sm:mt-8">{title}</h6>
{description && <p className="mb-7 px-5 text-custom-text-300 sm:mb-8">{description}</p>}
<div className="flex items-center gap-4">
{primaryButton && (
<Button
variant="primary"
prependIcon={primaryButton.icon}
onClick={primaryButton.onClick}
disabled={disabled}
>
{primaryButton.text}
</Button>
)}
{secondaryButton}
</div>
</div>
</div>
);

View File

@@ -0,0 +1,16 @@
import Image from "next/image";
import { useTheme } from "next-themes";
import LogoSpinnerDark from "@/app/assets/images/logo-spinner-dark.gif?url";
import LogoSpinnerLight from "@/app/assets/images/logo-spinner-light.gif?url";
export const LogoSpinner = () => {
const { resolvedTheme } = useTheme();
const logoSrc = resolvedTheme === "dark" ? LogoSpinnerLight : LogoSpinnerDark;
return (
<div className="flex items-center justify-center">
<Image src={logoSrc} alt="logo" className="h-6 w-auto sm:h-11" />
</div>
);
};

View File

@@ -0,0 +1,17 @@
"use client";
type TPageHeader = {
title?: string;
description?: string;
};
export const PageHeader: React.FC<TPageHeader> = (props) => {
const { title = "God Mode - Plane", description = "Plane god mode" } = props;
return (
<>
<title>{title}</title>
<meta name="description" content={description} />
</>
);
};