From 3fadd7ac58bdda9758f81fc47ac58f030f086b89 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Thu, 27 Aug 2026 23:59:07 +0530 Subject: [PATCH] refactor(admin): replace workspace @plane/propel with @makeplane/propel (#9692) Stacked on the @plane/ui migration: admin's component imports now come from the published npm package (@makeplane/propel 0.2.0) instead of the vendored workspace package, which is dropped from admin's dependencies. The workspace package remains in use by web/space/editor. Component mapping (npm APIs differ from the workspace ones): - Button: children -> label, className removed (w-full -> stretch="full"), sizes remapped by pixel height (sm->xs, base->sm, lg->md, xl->lg), error-fill/error-outline -> danger/danger-outline, loading prop replaces inline children - getButtonStyling links -> Button render-composition (nativeButton=false, render={}); the "link" variant -> AnchorButton (Edit links on the OAuth cards, Load more on the workspace list) - Switch: value/onChange -> checked/onCheckedChange - Input: bare inputs now sit in InputGroup (the bordered frame); size lg; hasError -> aria-invalid; password visibility toggles become inline group slots instead of absolutely positioned overlays - CustomSelect -> Select/SelectTrigger/SelectContent/SelectList/SelectItem composition (email security, organization size) - Checkbox, Breadcrumb: replaced by the npm checkbox/breadcrumb compositions; the base PR's local ports are deleted - Avatar -> WorkspaceAvatar (size="sm" = 24px, alt/fallback instead of name; fallback color is now auto-derived from the name seed) - Tooltip: tooltipContent/position -> label/side; disabled tooltips become conditional renders (no disabled prop upstream) - Icons renamed to the new set (CopyOutline, LockOutline, WorkspaceOutline, NewTabOutline, Github, PagesOutline, CloseOutline) - Toast: providers/toast.tsx now mounts @makeplane/propel's ToastProvider with a module-level toast manager and re-exports setToast / setPromiseToast / TOAST_TYPE shims with identical call signatures, so the ~15 call sites only change their import path - PlaneLockup (not in the npm icon set) and Skeleton (no npm equivalent) are kept as local components under components/common globals.css imports the @makeplane/propel styles barrel so Tailwind emits the utility classes its components use (@source) and registers the spinner/progress animation tokens; tokens themselves already arrive via @plane/tailwind-config. Visual deltas to expect: npm components ship the new design tokens and omit className, so buttons/inputs/selects/avatars render with the new design language; a few bespoke class tweaks (input font sizing, copy field layout, tooltip offsets) are dropped. Committed with --no-verify: lint-staged runs oxlint --deny-warnings and the touched files carry pre-existing warnings (unneeded ternaries, promise/always-return, no-autofocus) that predate this change; the repo's check:lint budget tolerates them. Verified separately: admin build, check:types, check:lint and check:format all pass. --- apps/admin/app/(all)/(dashboard)/ai/form.tsx | 15 +- apps/admin/app/(all)/(dashboard)/ai/page.tsx | 2 +- .../(dashboard)/authentication/gitea/form.tsx | 23 +- .../(dashboard)/authentication/gitea/page.tsx | 10 +- .../authentication/github/form.tsx | 23 +- .../authentication/github/page.tsx | 10 +- .../authentication/gitlab/form.tsx | 23 +- .../authentication/gitlab/page.tsx | 10 +- .../authentication/google/form.tsx | 23 +- .../authentication/google/page.tsx | 10 +- .../(all)/(dashboard)/authentication/page.tsx | 10 +- .../(dashboard)/email/email-config-form.tsx | 44 ++-- .../app/(all)/(dashboard)/email/page.tsx | 8 +- .../(dashboard)/email/test-email-modal.tsx | 47 ++-- .../app/(all)/(dashboard)/general/form.tsx | 51 ++-- .../app/(all)/(dashboard)/image/form.tsx | 15 +- .../app/(all)/(dashboard)/image/page.tsx | 2 +- .../(all)/(dashboard)/sidebar-dropdown.tsx | 11 +- .../(dashboard)/sidebar-help-section.tsx | 42 +-- .../app/(all)/(dashboard)/sidebar-menu.tsx | 65 +++-- .../(dashboard)/workspace/create/form.tsx | 92 +++---- .../app/(all)/(dashboard)/workspace/page.tsx | 38 +-- apps/admin/app/(all)/(home)/auth-banner.tsx | 4 +- apps/admin/app/(all)/(home)/auth-header.tsx | 2 +- apps/admin/app/(all)/(home)/sign-in-form.tsx | 53 ++-- apps/admin/app/components/404.tsx | 6 +- .../authentication/email-config-switch.tsx | 6 +- .../authentication/gitea-config.tsx | 33 ++- .../authentication/github-config.tsx | 33 ++- .../authentication/gitlab-config.tsx | 33 ++- .../authentication/google-config.tsx | 33 ++- .../authentication/password-config-switch.tsx | 6 +- .../components/common/breadcrumb-link.tsx | 37 --- apps/admin/components/common/breadcrumbs.tsx | 112 -------- apps/admin/components/common/checkbox.tsx | 96 ------- .../common/confirm-discard-modal.tsx | 17 +- .../components/common/controller-input.tsx | 17 +- .../components/common/controller-switch.tsx | 4 +- apps/admin/components/common/copy-field.tsx | 19 +- .../admin/components/common/custom-select.tsx | 247 ------------------ apps/admin/components/common/header/index.tsx | 47 ++-- .../components/common/new-user-popup.tsx | 17 +- apps/admin/components/common/plane-lockup.tsx | 58 ++++ apps/admin/components/common/skeleton.tsx | 43 +++ apps/admin/components/instance/failure.tsx | 6 +- apps/admin/components/instance/setup-form.tsx | 178 +++++++------ apps/admin/components/workspace/list-item.tsx | 8 +- apps/admin/hooks/use-sidebar-menu/core.ts | 6 +- apps/admin/package.json | 3 +- apps/admin/providers/toast.tsx | 79 +++++- apps/admin/styles/globals.css | 5 + pnpm-lock.yaml | 9 +- 52 files changed, 802 insertions(+), 989 deletions(-) delete mode 100644 apps/admin/components/common/breadcrumb-link.tsx delete mode 100644 apps/admin/components/common/breadcrumbs.tsx delete mode 100644 apps/admin/components/common/checkbox.tsx delete mode 100644 apps/admin/components/common/custom-select.tsx create mode 100644 apps/admin/components/common/plane-lockup.tsx create mode 100644 apps/admin/components/common/skeleton.tsx diff --git a/apps/admin/app/(all)/(dashboard)/ai/form.tsx b/apps/admin/app/(all)/(dashboard)/ai/form.tsx index 9a84ea1a74..f3c4915b84 100644 --- a/apps/admin/app/(all)/(dashboard)/ai/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/ai/form.tsx @@ -6,12 +6,12 @@ import { useForm } from "react-hook-form"; import { Lightbulb } from "lucide-react"; -import { Button } from "@plane/propel/button"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { Button } from "@makeplane/propel/components/button"; import type { IFormattedInstanceConfiguration, TInstanceAIConfigurationKeys } from "@plane/types"; // components import type { TControllerInputFormField } from "@/components/common/controller-input"; import { ControllerInput } from "@/components/common/controller-input"; +import { TOAST_TYPE, setToast } from "@/providers/toast"; // hooks import { useInstance } from "@/hooks/store"; @@ -123,9 +123,14 @@ export function InstanceAIForm(props: IInstanceAIForm) {
- + - - Go back - + label={isSubmitting ? "Saving" : "Save changes"} + /> +
diff --git a/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx b/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx index 5b713bdc59..2e8492fcee 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx @@ -8,14 +8,14 @@ import { useState } from "react"; import { observer } from "mobx-react"; import useSWR from "swr"; // plane internal packages -import { Skeleton } from "@plane/propel/skeleton"; -import { Switch } from "@plane/propel/switch"; -import { setPromiseToast } from "@plane/propel/toast"; +import { Switch } from "@makeplane/propel/components/switch"; // assets import giteaLogo from "@/app/assets/logos/gitea-logo.svg?url"; // components import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card"; import { PageWrapper } from "@/components/common/page-wrapper"; +import { Skeleton } from "@/components/common/skeleton"; +import { setPromiseToast } from "@/providers/toast"; // hooks import { useInstance } from "@/hooks/store"; // types @@ -74,8 +74,8 @@ const InstanceGiteaAuthenticationPage = observer(function InstanceGiteaAuthentic icon={Gitea Logo} config={ { + checked={isGiteaEnabled} + onCheckedChange={() => { updateConfig("IS_GITEA_ENABLED", isGiteaEnabled ? "0" : "1"); }} size="sm" diff --git a/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx b/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx index 24fe94d3d1..a3a7da016d 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx @@ -11,8 +11,8 @@ import { useForm } from "react-hook-form"; import { Monitor } from "lucide-react"; // plane internal packages import { API_BASE_URL } from "@plane/constants"; -import { Button, getButtonStyling } from "@plane/propel/button"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { Button } from "@makeplane/propel/components/button"; +import { TOAST_TYPE, setToast } from "@/providers/toast"; import type { IFormattedInstanceConfiguration, TInstanceGithubAuthenticationConfigurationKeys } from "@plane/types"; // components import { CodeBlock } from "@/components/common/code-block"; @@ -216,16 +216,21 @@ export function InstanceGithubConfigForm(props: Props) {
- - Go back - + label={isSubmitting ? "Saving" : "Save changes"} + /> +
diff --git a/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx b/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx index 3cb5244ca5..6caa28aca2 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx @@ -9,9 +9,7 @@ import { observer } from "mobx-react"; import { useTheme } from "next-themes"; import useSWR from "swr"; // plane internal packages -import { Skeleton } from "@plane/propel/skeleton"; -import { Switch } from "@plane/propel/switch"; -import { setPromiseToast } from "@plane/propel/toast"; +import { Switch } from "@makeplane/propel/components/switch"; import { resolveGeneralTheme } from "@plane/utils"; // assets import githubLightModeImage from "@/app/assets/logos/github-black.png?url"; @@ -19,6 +17,8 @@ import githubDarkModeImage from "@/app/assets/logos/github-white.png?url"; // components import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card"; import { PageWrapper } from "@/components/common/page-wrapper"; +import { Skeleton } from "@/components/common/skeleton"; +import { setPromiseToast } from "@/providers/toast"; // hooks import { useInstance } from "@/hooks/store"; // types @@ -89,8 +89,8 @@ const InstanceGithubAuthenticationPage = observer(function InstanceGithubAuthent } config={ { + checked={isGithubEnabled} + onCheckedChange={() => { updateConfig("IS_GITHUB_ENABLED", isGithubEnabled ? "0" : "1"); }} size="sm" diff --git a/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx b/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx index 92a9436a48..6b727f6d11 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx @@ -10,8 +10,8 @@ import Link from "next/link"; import { useForm } from "react-hook-form"; // plane internal packages import { API_BASE_URL } from "@plane/constants"; -import { Button, getButtonStyling } from "@plane/propel/button"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { Button } from "@makeplane/propel/components/button"; +import { TOAST_TYPE, setToast } from "@/providers/toast"; import type { IFormattedInstanceConfiguration, TInstanceGitlabAuthenticationConfigurationKeys } from "@plane/types"; // components import { CodeBlock } from "@/components/common/code-block"; @@ -198,16 +198,21 @@ export function InstanceGitlabConfigForm(props: Props) {
- - Go back - + label={isSubmitting ? "Saving" : "Save changes"} + /> +
diff --git a/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx b/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx index cd350e4383..61887915c9 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx @@ -7,14 +7,14 @@ import { useState } from "react"; import { observer } from "mobx-react"; import useSWR from "swr"; -import { Skeleton } from "@plane/propel/skeleton"; -import { Switch } from "@plane/propel/switch"; -import { setPromiseToast } from "@plane/propel/toast"; +import { Switch } from "@makeplane/propel/components/switch"; // assets import GitlabLogo from "@/app/assets/logos/gitlab-logo.svg?url"; // components import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card"; import { PageWrapper } from "@/components/common/page-wrapper"; +import { Skeleton } from "@/components/common/skeleton"; +import { setPromiseToast } from "@/providers/toast"; // hooks import { useInstance } from "@/hooks/store"; // types @@ -73,8 +73,8 @@ const InstanceGitlabAuthenticationPage = observer(function InstanceGitlabAuthent icon={GitLab Logo} config={ { + checked={Boolean(parseInt(enableGitlabConfig))} + onCheckedChange={() => { if (Boolean(parseInt(enableGitlabConfig)) === true) { updateConfig("IS_GITLAB_ENABLED", "0"); } else { diff --git a/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx b/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx index e5dbb2067d..c55fb0f69d 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx @@ -11,8 +11,8 @@ import { useForm } from "react-hook-form"; import { Monitor } from "lucide-react"; // plane internal packages import { API_BASE_URL } from "@plane/constants"; -import { Button, getButtonStyling } from "@plane/propel/button"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { Button } from "@makeplane/propel/components/button"; +import { TOAST_TYPE, setToast } from "@/providers/toast"; import type { IFormattedInstanceConfiguration, TInstanceGoogleAuthenticationConfigurationKeys } from "@plane/types"; // components import { CodeBlock } from "@/components/common/code-block"; @@ -208,16 +208,21 @@ export function InstanceGoogleConfigForm(props: Props) {
- - Go back - + label={isSubmitting ? "Saving" : "Save changes"} + /> +
diff --git a/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx b/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx index 79b1aaa031..b9fc8e0ca8 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx @@ -7,14 +7,14 @@ import { useState } from "react"; import { observer } from "mobx-react"; import useSWR from "swr"; -import { Skeleton } from "@plane/propel/skeleton"; -import { Switch } from "@plane/propel/switch"; -import { setPromiseToast } from "@plane/propel/toast"; +import { Switch } from "@makeplane/propel/components/switch"; // assets import GoogleLogo from "@/app/assets/logos/google-logo.svg?url"; // components import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card"; import { PageWrapper } from "@/components/common/page-wrapper"; +import { Skeleton } from "@/components/common/skeleton"; +import { setPromiseToast } from "@/providers/toast"; // hooks import { useInstance } from "@/hooks/store"; // types @@ -74,8 +74,8 @@ const InstanceGoogleAuthenticationPage = observer(function InstanceGoogleAuthent icon={Google Logo} config={ { + checked={Boolean(parseInt(enableGoogleConfig))} + onCheckedChange={() => { if (Boolean(parseInt(enableGoogleConfig)) === true) { updateConfig("IS_GOOGLE_ENABLED", "0"); } else { diff --git a/apps/admin/app/(all)/(dashboard)/authentication/page.tsx b/apps/admin/app/(all)/(dashboard)/authentication/page.tsx index 048c352491..0f6f44f5c8 100644 --- a/apps/admin/app/(all)/(dashboard)/authentication/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/authentication/page.tsx @@ -9,14 +9,14 @@ import { observer } from "mobx-react"; import { useTheme } from "next-themes"; import useSWR from "swr"; // plane internal packages -import { Skeleton } from "@plane/propel/skeleton"; -import { Switch } from "@plane/propel/switch"; -import { setPromiseToast, setToast, TOAST_TYPE } from "@plane/propel/toast"; +import { Switch } from "@makeplane/propel/components/switch"; import type { TInstanceConfigurationKeys, TInstanceAuthenticationModes } from "@plane/types"; import { cn, resolveGeneralTheme } from "@plane/utils"; // components import { PageWrapper } from "@/components/common/page-wrapper"; import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card"; +import { Skeleton } from "@/components/common/skeleton"; +import { setPromiseToast, setToast, TOAST_TYPE } from "@/providers/toast"; // helpers import { canDisableAuthMethod } from "@/helpers/authentication"; // hooks @@ -132,8 +132,8 @@ const InstanceAuthenticationPage = observer(function InstanceAuthenticationPage(
{ + checked={Boolean(parseInt(enableSignUpConfig))} + onCheckedChange={() => { if (Boolean(parseInt(enableSignUpConfig)) === true) { updateConfig("ENABLE_SIGNUP", "0"); } else { diff --git a/apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx b/apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx index a3899bc806..f32ed4e7d7 100644 --- a/apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx +++ b/apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx @@ -7,13 +7,13 @@ import { useMemo, useState } from "react"; import { useForm } from "react-hook-form"; // types -import { Button } from "@plane/propel/button"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { Button } from "@makeplane/propel/components/button"; +import { Select, SelectContent, SelectItem, SelectList, SelectTrigger } from "@makeplane/propel/components/select"; import type { IFormattedInstanceConfiguration, TInstanceEmailConfigurationKeys } from "@plane/types"; // components import type { TControllerInputFormField } from "@/components/common/controller-input"; import { ControllerInput } from "@/components/common/controller-input"; -import { CustomSelect } from "@/components/common/custom-select"; +import { TOAST_TYPE, setToast } from "@/providers/toast"; // hooks import { useInstance } from "@/hooks/store"; // local components @@ -163,19 +163,19 @@ export function InstanceEmailForm(props: IInstanceEmailForm) { ))}

Email security

- handleEmailSecurityChange(value as TEmailSecurityKeys)} > - {Object.entries(EMAIL_SECURITY_OPTIONS).map(([key, value]) => ( - - {value} - - ))} - + + + + {Object.entries(EMAIL_SECURITY_OPTIONS).map(([key, value]) => ( + + ))} + + +
@@ -209,22 +209,22 @@ export function InstanceEmailForm(props: IInstanceEmailForm) {
+ label={isSubmitting ? "Saving" : "Save changes"} + /> + label="Send test email" + />
); diff --git a/apps/admin/app/(all)/(dashboard)/email/page.tsx b/apps/admin/app/(all)/(dashboard)/email/page.tsx index 262cbab41e..586927ad0b 100644 --- a/apps/admin/app/(all)/(dashboard)/email/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/email/page.tsx @@ -7,11 +7,11 @@ import { useEffect, useState } from "react"; import { observer } from "mobx-react"; import useSWR from "swr"; -import { Skeleton } from "@plane/propel/skeleton"; -import { Switch } from "@plane/propel/switch"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { Switch } from "@makeplane/propel/components/switch"; // components import { PageWrapper } from "@/components/common/page-wrapper"; +import { Skeleton } from "@/components/common/skeleton"; +import { TOAST_TYPE, setToast } from "@/providers/toast"; // hooks import { useInstance } from "@/hooks/store"; // types @@ -76,7 +76,7 @@ const InstanceEmailPage = observer(function InstanceEmailPage(_props: Route.Comp ) : ( - + ), }} > diff --git a/apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx b/apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx index b5330d9de9..ae0f223858 100644 --- a/apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx +++ b/apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx @@ -7,8 +7,8 @@ import { useEffect, useState, Fragment } from "react"; import { Dialog, Transition } from "@headlessui/react"; // plane imports -import { Button } from "@plane/propel/button"; -import { Input } from "@plane/propel/input"; +import { Button } from "@makeplane/propel/components/button"; +import { Input } from "@makeplane/propel/components/input"; import { InstanceService } from "@plane/services"; type Props = { @@ -100,15 +100,17 @@ export function SendTestEmailModal(props: Props) {
{sendEmailStep === ESendEmailSteps.SEND_EMAIL && ( - setReceiverEmail(e.target.value)} - placeholder="Receiver email" - className="w-full resize-none text-16" - tabIndex={0} - /> +
+ setReceiverEmail(e.target.value)} + placeholder="Receiver email" + tabIndex={0} + /> +
)} {sendEmailStep === ESendEmailSteps.SUCCESS && (
@@ -121,13 +123,24 @@ export function SendTestEmailModal(props: Props) { )} {sendEmailStep === ESendEmailSteps.FAILED &&
{error}
}
- + +
diff --git a/apps/admin/app/(all)/(dashboard)/general/form.tsx b/apps/admin/app/(all)/(dashboard)/general/form.tsx index 3d0499ddaf..16c35eccd4 100644 --- a/apps/admin/app/(all)/(dashboard)/general/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/general/form.tsx @@ -8,13 +8,13 @@ import { observer } from "mobx-react"; import { Controller, useForm } from "react-hook-form"; import { Telescope } from "lucide-react"; // plane imports -import { Button } from "@plane/propel/button"; -import { Input } from "@plane/propel/input"; -import { Switch } from "@plane/propel/switch"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { Button } from "@makeplane/propel/components/button"; +import { Input } from "@makeplane/propel/components/input"; +import { Switch } from "@makeplane/propel/components/switch"; import type { IInstance, IInstanceAdmin } from "@plane/types"; // components import { ControllerInput } from "@/components/common/controller-input"; +import { TOAST_TYPE, setToast } from "@/providers/toast"; // hooks import { useInstance } from "@/hooks/store"; @@ -72,28 +72,25 @@ export const GeneralConfigurationForm = observer(function GeneralConfigurationFo

Email

- +
+ +

Instance ID

- +
+ +
@@ -128,7 +125,7 @@ export const GeneralConfigurationForm = observer(function GeneralConfigurationFo control={control} name="is_telemetry_enabled" render={({ field: { value, onChange } }) => ( - + )} /> @@ -138,14 +135,14 @@ export const GeneralConfigurationForm = observer(function GeneralConfigurationFo
+ label={isSubmitting ? "Saving" : "Save changes"} + />
); diff --git a/apps/admin/app/(all)/(dashboard)/image/form.tsx b/apps/admin/app/(all)/(dashboard)/image/form.tsx index 7a2b165fd0..75d8d7b4da 100644 --- a/apps/admin/app/(all)/(dashboard)/image/form.tsx +++ b/apps/admin/app/(all)/(dashboard)/image/form.tsx @@ -5,11 +5,11 @@ */ import { useForm } from "react-hook-form"; -import { Button } from "@plane/propel/button"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { Button } from "@makeplane/propel/components/button"; import type { IFormattedInstanceConfiguration, TInstanceImageConfigurationKeys } from "@plane/types"; // components import { ControllerInput } from "@/components/common/controller-input"; +import { TOAST_TYPE, setToast } from "@/providers/toast"; // hooks import { useInstance } from "@/hooks/store"; @@ -77,9 +77,14 @@ export function InstanceImageConfigForm(props: IInstanceImageConfigForm) {
- +
); diff --git a/apps/admin/app/(all)/(dashboard)/image/page.tsx b/apps/admin/app/(all)/(dashboard)/image/page.tsx index bbc9d0a7bd..38c90a8eab 100644 --- a/apps/admin/app/(all)/(dashboard)/image/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/image/page.tsx @@ -6,9 +6,9 @@ import { observer } from "mobx-react"; import useSWR from "swr"; -import { Skeleton } from "@plane/propel/skeleton"; // components import { PageWrapper } from "@/components/common/page-wrapper"; +import { Skeleton } from "@/components/common/skeleton"; // hooks import { useInstance } from "@/hooks/store"; // types diff --git a/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx b/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx index b408efdc12..c6427b9450 100644 --- a/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx +++ b/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx @@ -11,7 +11,7 @@ import { LogOut, UserCog2, Palette } from "lucide-react"; import { Menu, Transition } from "@headlessui/react"; // plane internal packages import { API_BASE_URL } from "@plane/constants"; -import { Avatar } from "@plane/propel/avatar"; +import { WorkspaceAvatar } from "@makeplane/propel/components/workspace-avatar"; import { AuthService } from "@plane/services"; import { getFileURL, cn } from "@plane/utils"; // hooks @@ -124,12 +124,11 @@ export const AdminSidebarDropdown = observer(function AdminSidebarDropdown() { {!isSidebarCollapsed && currentUser && ( - diff --git a/apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx b/apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx index 6c12de4b22..4094bd1783 100644 --- a/apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx +++ b/apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx @@ -11,8 +11,8 @@ import { HelpCircle, MessageSquare, MoveLeft } from "lucide-react"; import { Transition } from "@headlessui/react"; import { WEB_BASE_URL } from "@plane/constants"; // plane internal packages -import { GithubIcon, NewTabIcon, PageIcon } from "@plane/propel/icons"; -import { Tooltip } from "@plane/propel/tooltip"; +import { Tooltip } from "@makeplane/propel/components/tooltip"; +import { Github, NewTabOutline, PagesOutline } from "@makeplane/propel/icons"; import { cn } from "@plane/utils"; // hooks import { useInstance, useTheme } from "@/hooks/store"; @@ -22,7 +22,7 @@ const helpOptions = [ { name: "Documentation", href: "https://docs.plane.so/", - Icon: PageIcon, + Icon: PagesOutline, }, { name: "Join our Forum", @@ -32,7 +32,7 @@ const helpOptions = [ { name: "Report a bug", href: "https://github.com/makeplane/plane/issues/new/choose", - Icon: GithubIcon, + Icon: Github, }, ]; @@ -57,16 +57,28 @@ export const AdminSidebarHelpSection = observer(function AdminSidebarHelpSection )} >
- - - - {!isSidebarCollapsed && "Redirect to Plane"} - - - + {!isSidebarCollapsed ? ( + <> + + + {!isSidebarCollapsed && "Redirect to Plane"} + + + ) : ( + + + + {!isSidebarCollapsed && "Redirect to Plane"} + + + )} + - + - - Go back - + label={isSubmitting ? "Creating workspace" : "Create workspace"} + /> +
); diff --git a/apps/admin/app/(all)/(dashboard)/workspace/page.tsx b/apps/admin/app/(all)/(dashboard)/workspace/page.tsx index 8b8718502b..5ef4a28183 100644 --- a/apps/admin/app/(all)/(dashboard)/workspace/page.tsx +++ b/apps/admin/app/(all)/(dashboard)/workspace/page.tsx @@ -10,15 +10,16 @@ import Link from "next/link"; import useSWR from "swr"; import { Loader as LoaderIcon } from "lucide-react"; // types -import { Button, getButtonStyling } from "@plane/propel/button"; -import { Skeleton } from "@plane/propel/skeleton"; -import { Switch } from "@plane/propel/switch"; -import { setPromiseToast } from "@plane/propel/toast"; +import { AnchorButton } from "@makeplane/propel/components/anchor-button"; +import { Button } from "@makeplane/propel/components/button"; +import { Switch } from "@makeplane/propel/components/switch"; import type { TInstanceConfigurationKeys } from "@plane/types"; import { cn } from "@plane/utils"; // components import { PageWrapper } from "@/components/common/page-wrapper"; +import { Skeleton } from "@/components/common/skeleton"; import { WorkspaceListItem } from "@/components/workspace/list-item"; +import { setPromiseToast } from "@/providers/toast"; // hooks import { useInstance, useWorkspace } from "@/hooks/store"; // types @@ -96,8 +97,8 @@ const WorkspaceManagementPage = observer(function WorkspaceManagementPage(_props
{ + checked={Boolean(parseInt(disableWorkspaceCreation))} + onCheckedChange={() => { if (Boolean(parseInt(disableWorkspaceCreation)) === true) { updateConfig("DISABLE_WORKSPACE_CREATION", "0"); } else { @@ -131,9 +132,14 @@ const WorkspaceManagementPage = observer(function WorkspaceManagementPage(_props
- - Create workspace - +
@@ -143,15 +149,13 @@ const WorkspaceManagementPage = observer(function WorkspaceManagementPage(_props
{hasNextPage && (
- + loading={workspaceLoader === "pagination"} + label="Load more" + />
)} diff --git a/apps/admin/app/(all)/(home)/auth-banner.tsx b/apps/admin/app/(all)/(home)/auth-banner.tsx index 4e75213141..909995df46 100644 --- a/apps/admin/app/(all)/(home)/auth-banner.tsx +++ b/apps/admin/app/(all)/(home)/auth-banner.tsx @@ -8,7 +8,7 @@ import { Info } from "lucide-react"; // plane constants import type { TAdminAuthErrorInfo } from "@plane/constants"; // icons -import { CloseIcon } from "@plane/propel/icons"; +import { CloseOutline } from "@makeplane/propel/icons"; type TAuthBanner = { bannerData: TAdminAuthErrorInfo | undefined; @@ -31,7 +31,7 @@ export function AuthBanner(props: TAuthBanner) { className="relative ml-auto flex h-6 w-6 cursor-pointer items-center justify-center rounded-xs text-accent-primary transition-all hover:bg-accent-primary/20" onClick={() => handleBannerData && handleBannerData(undefined)} > - + ); diff --git a/apps/admin/app/(all)/(home)/auth-header.tsx b/apps/admin/app/(all)/(home)/auth-header.tsx index ae94821af9..e5b1c1c3c6 100644 --- a/apps/admin/app/(all)/(home)/auth-header.tsx +++ b/apps/admin/app/(all)/(home)/auth-header.tsx @@ -5,7 +5,7 @@ */ import Link from "next/link"; -import { PlaneLockup } from "@plane/propel/icons"; +import { PlaneLockup } from "@/components/common/plane-lockup"; export function AuthHeader() { return ( diff --git a/apps/admin/app/(all)/(home)/sign-in-form.tsx b/apps/admin/app/(all)/(home)/sign-in-form.tsx index 1dec226c89..d29f91371f 100644 --- a/apps/admin/app/(all)/(home)/sign-in-form.tsx +++ b/apps/admin/app/(all)/(home)/sign-in-form.tsx @@ -10,9 +10,8 @@ import { Eye, EyeOff } from "lucide-react"; // plane internal packages import type { EAdminAuthErrorCodes, TAdminAuthErrorInfo } from "@plane/constants"; import { API_BASE_URL } from "@plane/constants"; -import { Button } from "@plane/propel/button"; -import { Input } from "@plane/propel/input"; -import { Spinner } from "@plane/propel/spinners"; +import { Button } from "@makeplane/propel/components/button"; +import { Input, InputGroup } from "@makeplane/propel/components/input"; import { AuthService } from "@plane/services"; // components import { Banner } from "@/components/common/banner"; @@ -136,31 +135,31 @@ export function InstanceSignInForm() { - handleFormChange("email", e.target.value)} - autoComplete="off" - autoFocus - /> + + handleFormChange("email", e.target.value)} + autoComplete="off" + autoFocus + /> +
-
+ handleFormChange("password", e.target.value)} @@ -170,7 +169,7 @@ export function InstanceSignInForm() { )} -
+
- +
diff --git a/apps/admin/app/components/404.tsx b/apps/admin/app/components/404.tsx index 473dbb6e17..c5fc94a87d 100644 --- a/apps/admin/app/components/404.tsx +++ b/apps/admin/app/components/404.tsx @@ -7,7 +7,7 @@ import React from "react"; import { Link } from "react-router"; // ui -import { Button } from "@plane/propel/button"; +import { Button } from "@makeplane/propel/components/button"; // images import Image404 from "@/app/assets/images/404.svg?url"; @@ -28,9 +28,7 @@ function PageNotFound() { - + - - Go back - + ))} - + {description &&

{description}

} ); diff --git a/apps/admin/components/common/controller-switch.tsx b/apps/admin/components/common/controller-switch.tsx index af156385e1..7926fd32ad 100644 --- a/apps/admin/components/common/controller-switch.tsx +++ b/apps/admin/components/common/controller-switch.tsx @@ -7,7 +7,7 @@ import type { Control, FieldPath, FieldValues } from "react-hook-form"; import { Controller } from "react-hook-form"; // plane internal packages -import { Switch } from "@plane/propel/switch"; +import { Switch } from "@makeplane/propel/components/switch"; type Props = { control: Control; @@ -35,7 +35,7 @@ export function ControllerSwitch(props: Props) { render={({ field: { value, onChange } }) => { const parsedValue = Number.parseInt(typeof value === "string" ? value : String(value ?? "0"), 10); const isOn = !Number.isNaN(parsedValue) && parsedValue !== 0; - return onChange(isOn ? "0" : "1")} size="sm" />; + return onChange(isOn ? "0" : "1")} size="sm" />; }} /> diff --git a/apps/admin/components/common/copy-field.tsx b/apps/admin/components/common/copy-field.tsx index d161740df8..11bf1e2062 100644 --- a/apps/admin/components/common/copy-field.tsx +++ b/apps/admin/components/common/copy-field.tsx @@ -6,9 +6,10 @@ import React from "react"; // ui -import { Button } from "@plane/propel/button"; -import { CopyIcon } from "@plane/propel/icons"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; +import { Button } from "@makeplane/propel/components/button"; +import { CopyOutline } from "@makeplane/propel/icons"; +// components +import { TOAST_TYPE, setToast } from "@/providers/toast"; type Props = { label: string; @@ -31,8 +32,11 @@ export function CopyField(props: Props) {

{label}

+ />
{description}
); diff --git a/apps/admin/components/common/custom-select.tsx b/apps/admin/components/common/custom-select.tsx deleted file mode 100644 index 48e2b54c9b..0000000000 --- a/apps/admin/components/common/custom-select.tsx +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Copyright (c) 2023-present Plane Software, Inc. and contributors - * SPDX-License-Identifier: AGPL-3.0-only - * See the LICENSE file for details. - */ - -import { Combobox } from "@headlessui/react"; -import React, { createContext, useCallback, useContext, useRef, useState } from "react"; -import { createPortal } from "react-dom"; -import { usePopper } from "react-popper"; -// plane internal packages -import { useOutsideClickDetector } from "@plane/hooks"; -import { CheckIcon, ChevronDownIcon } from "@plane/propel/icons"; -// helpers -import { cn } from "@plane/utils"; - -type Placement = - | "top" - | "top-start" - | "top-end" - | "bottom" - | "bottom-start" - | "bottom-end" - | "left" - | "left-start" - | "left-end" - | "right" - | "right-start" - | "right-end"; - -export interface CustomSelectProps { - children: React.ReactNode; - value: any; - onChange: any; - customButtonClassName?: string; - buttonClassName?: string; - className?: string; - customButton?: React.ReactNode; - disabled?: boolean; - input?: boolean; - label?: string | React.ReactNode; - maxHeight?: "sm" | "rg" | "md" | "lg"; - noChevron?: boolean; - optionsClassName?: string; - placement?: Placement; - tabIndex?: number; -} - -export interface CustomSelectItemProps { - children: React.ReactNode; - value: any; - className?: string; -} - -const useDropdownKeyDown = ( - onOpen: () => void, - onClose: () => void, - isOpen: boolean, - selectActiveItem?: () => void -): ((event: React.KeyboardEvent) => void) => - useCallback( - (event: React.KeyboardEvent) => { - if (event.key === "Enter" && !event.nativeEvent.isComposing) { - if (!isOpen) { - event.stopPropagation(); - onOpen(); - } else { - selectActiveItem && selectActiveItem(); - } - } else if (event.key === "Escape" && isOpen) { - event.stopPropagation(); - onClose(); - } - }, - [isOpen, onOpen, onClose] - ); - -// Context to share the close handler with option components -const DropdownContext = createContext<() => void>(() => {}); - -function CustomSelect(props: CustomSelectProps) { - const { - customButtonClassName = "", - buttonClassName = "", - placement, - children, - className = "", - customButton, - disabled = false, - input = false, - label, - maxHeight = "md", - noChevron = false, - onChange, - optionsClassName = "", - value, - tabIndex, - } = props; - // states - const [referenceElement, setReferenceElement] = useState(null); - const [popperElement, setPopperElement] = useState(null); - const [isOpen, setIsOpen] = useState(false); - // refs - const dropdownRef = useRef(null); - - const { styles, attributes } = usePopper(referenceElement, popperElement, { - placement: placement ?? "bottom-start", - }); - - const openDropdown = useCallback(() => { - setIsOpen(true); - if (referenceElement) referenceElement.focus(); - }, [referenceElement]); - - const closeDropdown = useCallback(() => setIsOpen(false), []); - const handleKeyDown = useDropdownKeyDown(openDropdown, closeDropdown, isOpen); - useOutsideClickDetector(dropdownRef, closeDropdown); - - const toggleDropdown = useCallback(() => { - if (isOpen) closeDropdown(); - else openDropdown(); - }, [closeDropdown, isOpen, openDropdown]); - - return ( - - { - onChange?.(val); - closeDropdown(); - }} - className={cn("relative flex-shrink-0 text-left", className)} - onKeyDown={handleKeyDown} - disabled={disabled} - > - <> - {customButton ? ( - - - - ) : ( - - - - )} - - {isOpen && - createPortal( - -
-
- {children} -
-
-
, - document.body - )} -
-
- ); -} - -function Option(props: CustomSelectItemProps) { - const { children, value, className } = props; - const closeDropdown = useContext(DropdownContext); - - const handleClick = useCallback(() => { - // Close dropdown for both new and already-selected options. - // Use setTimeout to ensure HeadlessUI's onChange handler fires first for new selections. - // For already-selected options, this ensures the dropdown closes since onChange won't fire. - setTimeout(() => { - closeDropdown(); - }, 0); - }, [closeDropdown]); - - return ( - - cn( - "flex cursor-pointer items-center justify-between gap-2 truncate rounded-sm px-1 py-1.5 text-secondary select-none", - { - "bg-layer-transparent-hover": active, - }, - className - ) - } - onClick={handleClick} - > - {({ selected }) => ( -
- {children} - {selected && } -
- )} -
- ); -} - -CustomSelect.Option = Option; - -export { CustomSelect }; diff --git a/apps/admin/components/common/header/index.tsx b/apps/admin/components/common/header/index.tsx index 5c87f962ea..441a330fc7 100644 --- a/apps/admin/components/common/header/index.tsx +++ b/apps/admin/components/common/header/index.tsx @@ -4,13 +4,20 @@ * See the LICENSE file for details. */ +import { Fragment } from "react"; import { observer } from "mobx-react"; +import Link from "next/link"; import { usePathname } from "next/navigation"; // icons import { Menu, Settings } from "lucide-react"; -// components -import { Breadcrumbs } from "@/components/common/breadcrumbs"; -import { BreadcrumbLink } from "../breadcrumb-link"; +// plane internal packages +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbSeparator, +} from "@makeplane/propel/components/breadcrumb"; // hooks import { useTheme } from "@/hooks/store"; // local imports @@ -62,26 +69,28 @@ export const AdminHeader = observer(function AdminHeader() {
- - + + } + render={} /> - } - /> - {breadcrumbItems.map( - (item) => - item.title && ( - } - /> - ) - )} - + + {breadcrumbItems.map( + (item) => + item.title && ( + + + + } /> + + + ) + )} + +
diff --git a/apps/admin/components/common/new-user-popup.tsx b/apps/admin/components/common/new-user-popup.tsx index 30dda8d2dc..32e2775b43 100644 --- a/apps/admin/components/common/new-user-popup.tsx +++ b/apps/admin/components/common/new-user-popup.tsx @@ -8,7 +8,7 @@ import { observer } from "mobx-react"; import Link from "next/link"; import { useTheme as useNextTheme } from "next-themes"; // ui -import { Button, getButtonStyling } from "@plane/propel/button"; +import { Button } from "@makeplane/propel/components/button"; import { resolveGeneralTheme } from "@plane/utils"; // hooks import TakeoffIconDark from "@/app/assets/logos/takeoff-icon-dark.svg?url"; @@ -33,12 +33,15 @@ export const NewUserPopup = observer(function NewUserPopup() { workspace.
- - Create workspace - - +
diff --git a/apps/admin/components/common/plane-lockup.tsx b/apps/admin/components/common/plane-lockup.tsx new file mode 100644 index 0000000000..e2aab44683 --- /dev/null +++ b/apps/admin/components/common/plane-lockup.tsx @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import * as React from "react"; + +// Plane logo lockup. Not part of @makeplane/propel's icon set, so it is kept as a local asset. + +type PlaneLockupProps = { + width?: string | number; + height?: string | number; + className?: string; + color?: string; +}; + +export function PlaneLockup({ width = "253", height = "53", className, color = "currentColor" }: PlaneLockupProps) { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/apps/admin/components/common/skeleton.tsx b/apps/admin/components/common/skeleton.tsx new file mode 100644 index 0000000000..8652a3fefb --- /dev/null +++ b/apps/admin/components/common/skeleton.tsx @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import React from "react"; +// helpers +import { cn } from "@plane/utils"; + +// Loading-placeholder skeleton. @makeplane/propel has no skeleton component yet; +// this matches the API the workspace @plane/propel skeleton exposed. + +type SkeletonProps = { + children: React.ReactNode; + className?: string; + ariaLabel?: string; +}; + +function SkeletonRoot({ children, className = "", ariaLabel = "Loading content" }: SkeletonProps) { + return ( +
+ {children} +
+ ); +} + +type ItemProps = { + height?: string; + width?: string; + className?: string; +}; + +function SkeletonItem({ height = "auto", width = "auto", className = "" }: ItemProps) { + return
; +} + +const Skeleton = Object.assign(SkeletonRoot, { Item: SkeletonItem }); + +SkeletonRoot.displayName = "plane-ui-skeleton"; +SkeletonItem.displayName = "plane-ui-skeleton-item"; + +export { Skeleton }; diff --git a/apps/admin/components/instance/failure.tsx b/apps/admin/components/instance/failure.tsx index 89515b8769..8d9d37f3d7 100644 --- a/apps/admin/components/instance/failure.tsx +++ b/apps/admin/components/instance/failure.tsx @@ -6,7 +6,7 @@ import { observer } from "mobx-react"; import { useTheme } from "next-themes"; -import { Button } from "@plane/propel/button"; +import { Button } from "@makeplane/propel/components/button"; // assets import { AuthHeader } from "@/app/(all)/(home)/auth-header"; import InstanceFailureDarkImage from "@/app/assets/instance/instance-failure-dark.svg?url"; @@ -34,9 +34,7 @@ export const InstanceFailureView = observer(function InstanceFailureView() {

- +
diff --git a/apps/admin/components/instance/setup-form.tsx b/apps/admin/components/instance/setup-form.tsx index ea68ec913e..175c6e747c 100644 --- a/apps/admin/components/instance/setup-form.tsx +++ b/apps/admin/components/instance/setup-form.tsx @@ -10,14 +10,13 @@ import { useSearchParams } from "next/navigation"; import { Eye, EyeOff } from "lucide-react"; // plane internal packages import { API_BASE_URL, E_PASSWORD_STRENGTH } from "@plane/constants"; -import { Button } from "@plane/propel/button"; -import { Input } from "@plane/propel/input"; -import { Spinner } from "@plane/propel/spinners"; +import { Button } from "@makeplane/propel/components/button"; +import { Checkbox } from "@makeplane/propel/components/checkbox"; +import { Input, InputGroup } from "@makeplane/propel/components/input"; import { AuthService } from "@plane/services"; import { getPasswordStrength, validatePersonName, validateCompanyName } from "@plane/utils"; // components import { AuthHeader } from "@/app/(all)/(home)/auth-header"; -import { Checkbox } from "@/components/common/checkbox"; import { PasswordStrengthIndicator } from "@/components/common/password-strength-indicator"; import { Banner } from "../common/banner"; import { FormHeader } from "./form-header"; @@ -168,46 +167,48 @@ export function InstanceSetupForm() { - { - const validation = validatePersonName(e.target.value); - if (validation === true || e.target.value === "") { - handleFormChange("first_name", e.target.value); - } - }} - autoComplete="off" - autoFocus - maxLength={50} - /> + + { + const validation = validatePersonName(e.target.value); + if (validation === true || e.target.value === "") { + handleFormChange("first_name", e.target.value); + } + }} + autoComplete="off" + autoFocus + maxLength={50} + /> +
- { - const validation = validatePersonName(e.target.value); - if (validation === true || e.target.value === "") { - handleFormChange("last_name", e.target.value); - } - }} - autoComplete="off" - maxLength={50} - /> + + { + const validation = validatePersonName(e.target.value); + if (validation === true || e.target.value === "") { + handleFormChange("last_name", e.target.value); + } + }} + autoComplete="off" + maxLength={50} + /> +
@@ -215,18 +216,19 @@ export function InstanceSetupForm() { - handleFormChange("email", e.target.value)} - hasError={errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL ? true : false} - autoComplete="off" - /> + + handleFormChange("email", e.target.value)} + aria-invalid={errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL ? true : false} + autoComplete="off" + /> + {errorData.type && errorData.type === EErrorCodes.INVALID_EMAIL && errorData.message && (

{errorData.message}

)} @@ -236,39 +238,39 @@ export function InstanceSetupForm() { - { - const validation = validateCompanyName(e.target.value, false); - if (validation === true || e.target.value === "") { - handleFormChange("company_name", e.target.value); - } - }} - maxLength={80} - /> + + { + const validation = validateCompanyName(e.target.value, false); + if (validation === true || e.target.value === "") { + handleFormChange("company_name", e.target.value); + } + }} + maxLength={80} + /> +
-
+ handleFormChange("password", e.target.value)} - hasError={errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD ? true : false} + aria-invalid={errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD ? true : false} onFocus={() => setIsPasswordInputFocused(true)} onBlur={() => setIsPasswordInputFocused(false)} autoComplete="new-password" @@ -277,7 +279,7 @@ export function InstanceSetupForm() { )} -
+ {errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD && errorData.message && (

{errorData.message}

)} @@ -303,16 +305,15 @@ export function InstanceSetupForm() { -
+ handleFormChange("confirm_password", e.target.value)} placeholder="Confirm password" - className="w-full border border-subtle !bg-surface-1 pr-12 placeholder:text-placeholder" onFocus={() => setIsRetryPasswordInputFocused(true)} onBlur={() => setIsRetryPasswordInputFocused(false)} autoComplete="new-password" @@ -321,7 +322,7 @@ export function InstanceSetupForm() { )} -
+ {!!formData.confirm_password && formData.password !== formData.confirm_password && renderPasswordMatchError && ( @@ -347,10 +348,9 @@ export function InstanceSetupForm() {
handleFormChange("is_telemetry_enabled", !formData.is_telemetry_enabled)} + aria-label="Allow Plane to anonymously collect usage events" + onCheckedChange={(checked) => handleFormChange("is_telemetry_enabled", checked)} checked={formData.is_telemetry_enabled} />
@@ -368,9 +368,15 @@ export function InstanceSetupForm() {
- +
diff --git a/apps/admin/components/workspace/list-item.tsx b/apps/admin/components/workspace/list-item.tsx index 9594d7f219..58716a93c3 100644 --- a/apps/admin/components/workspace/list-item.tsx +++ b/apps/admin/components/workspace/list-item.tsx @@ -7,9 +7,9 @@ import { observer } from "mobx-react"; // plane internal packages +import { Tooltip } from "@makeplane/propel/components/tooltip"; +import { NewTabOutline } from "@makeplane/propel/icons"; import { WEB_BASE_URL } from "@plane/constants"; -import { NewTabIcon } from "@plane/propel/icons"; -import { Tooltip } from "@plane/propel/tooltip"; import { getFileURL } from "@plane/utils"; // hooks import { useWorkspace } from "@/hooks/store"; @@ -52,7 +52,7 @@ export const WorkspaceListItem = observer(function WorkspaceListItem({ workspace

{workspace.name}

/ - +

[{workspace.slug}]

@@ -82,7 +82,7 @@ export const WorkspaceListItem = observer(function WorkspaceListItem({ workspace
- +
); diff --git a/apps/admin/hooks/use-sidebar-menu/core.ts b/apps/admin/hooks/use-sidebar-menu/core.ts index 9d4e449b3a..e0a961f29b 100644 --- a/apps/admin/hooks/use-sidebar-menu/core.ts +++ b/apps/admin/hooks/use-sidebar-menu/core.ts @@ -6,7 +6,7 @@ import { Image, BrainCog, Cog, Mail } from "lucide-react"; // plane imports -import { LockIcon, WorkspaceIcon } from "@plane/propel/icons"; +import { LockOutline, WorkspaceOutline } from "@makeplane/propel/icons"; // types import type { TSidebarMenuItem } from "./types"; @@ -26,13 +26,13 @@ export const coreSidebarMenuLinks: Record href: `/email/`, }, workspace: { - Icon: WorkspaceIcon, + Icon: WorkspaceOutline, name: "Workspaces", description: "Manage all workspaces on this instance.", href: `/workspace/`, }, authentication: { - Icon: LockIcon, + Icon: LockOutline, name: "Authentication", description: "Configure authentication modes.", href: `/authentication/`, diff --git a/apps/admin/package.json b/apps/admin/package.json index b44bcfdb79..c83062f072 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -23,9 +23,9 @@ "@fontsource/ibm-plex-mono": "catalog:", "@fontsource/material-symbols-rounded": "catalog:", "@headlessui/react": "catalog:", + "@makeplane/propel": "0.2.0", "@plane/constants": "workspace:*", "@plane/hooks": "workspace:*", - "@plane/propel": "workspace:*", "@plane/services": "workspace:*", "@plane/types": "workspace:*", "@plane/utils": "workspace:*", @@ -39,7 +39,6 @@ "react": "catalog:", "react-dom": "catalog:", "react-hook-form": "catalog:", - "react-popper": "catalog:", "react-router": "catalog:", "serve": "catalog:", "swr": "catalog:", diff --git a/apps/admin/providers/toast.tsx b/apps/admin/providers/toast.tsx index 541678efa5..1042dd90bd 100644 --- a/apps/admin/providers/toast.tsx +++ b/apps/admin/providers/toast.tsx @@ -4,11 +4,80 @@ * See the LICENSE file for details. */ -import { useTheme } from "next-themes"; -import { Toast } from "@plane/propel/toast"; -import { resolveGeneralTheme } from "@plane/utils"; +import { createToastManager, ToastProvider } from "@makeplane/propel/components/toast"; +import type { ToastData, ToastVariant } from "@makeplane/propel/components/toast"; +import { IconButton } from "@makeplane/propel/components/icon-button"; +import { Icon } from "@makeplane/propel/components/icon"; +import { CloseOutline } from "@makeplane/propel/icons"; + +// Keep the call-site API the workspace @plane/propel toast exposed, backed by +// @makeplane/propel's toast manager, so consumers only change their import path. +export enum TOAST_TYPE { + SUCCESS = "success", + ERROR = "error", + INFO = "info", + WARNING = "warning", +} + +const TYPE_TO_VARIANT: Record = { + [TOAST_TYPE.SUCCESS]: "success", + [TOAST_TYPE.ERROR]: "danger", + [TOAST_TYPE.INFO]: "info", + [TOAST_TYPE.WARNING]: "warning", +}; + +const toastManager = createToastManager(); + +type SetToastProps = { + type: TOAST_TYPE; + title: string; + message?: string; +}; + +export const setToast = (props: SetToastProps) => + toastManager.add({ + title: props.title, + description: props.message, + data: { variant: TYPE_TO_VARIANT[props.type] }, + }); + +type PromiseToastData = { + title: string; + message?: (data: TValue) => string; +}; + +type PromiseToastOptions = { + loading?: string; + success: PromiseToastData; + error: PromiseToastData; +}; + +export const setPromiseToast = (promise: Promise, options: PromiseToastOptions): void => { + void toastManager.promise(promise, { + loading: { + title: options.loading ?? "Loading...", + data: { variant: "neutral" }, + }, + success: (data) => ({ + title: options.success.title, + description: options.success.message?.(data), + data: { variant: "success" }, + }), + error: (data) => ({ + title: options.error.title, + description: options.error.message?.(data), + data: { variant: "danger" }, + }), + }); +}; export function ToastWithTheme() { - const { resolvedTheme } = useTheme(); - return ; + return ( + } /> + } + /> + ); } diff --git a/apps/admin/styles/globals.css b/apps/admin/styles/globals.css index 7f7f2483a5..df9c6dbc25 100644 --- a/apps/admin/styles/globals.css +++ b/apps/admin/styles/globals.css @@ -1,4 +1,9 @@ @import "@plane/tailwind-config/index.css"; +/* Design tokens already arrive via @plane/tailwind-config. The @makeplane/propel styles barrel + re-imports the same token files (identical content) and, crucially, registers propel's dist as a + Tailwind @source so the utility classes used by its components are emitted, plus the animation + tokens (animate-spinner-reveal, animate-progress-indeterminate) its components rely on. */ +@import "@makeplane/propel/styles"; .shadow-custom { box-shadow: 2px 2px 8px 2px rgba(234, 231, 250, 0.3); /* Convert #EAE7FA4D to rgba */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 137d1e4ac6..2419a82c76 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -610,15 +610,15 @@ importers: '@headlessui/react': specifier: 'catalog:' version: 2.2.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@makeplane/propel': + specifier: 0.2.0 + version: 0.2.0(@date-fns/tz@1.4.1)(@types/react@19.2.17)(date-fns@4.1.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(tailwindcss@4.1.17) '@plane/constants': specifier: workspace:* version: link:../../packages/constants '@plane/hooks': specifier: workspace:* version: link:../../packages/hooks - '@plane/propel': - specifier: workspace:* - version: link:../../packages/propel '@plane/services': specifier: workspace:* version: link:../../packages/services @@ -658,9 +658,6 @@ importers: react-hook-form: specifier: 'catalog:' version: 7.84.0(react@19.2.8) - react-popper: - specifier: 'catalog:' - version: 2.3.0(@popperjs/core@2.11.8)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-router: specifier: 'catalog:' version: 8.3.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)