From a86ed9f5f3d6ad3eb5a5af9c28258ea2a9400432 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Tue, 12 May 2026 23:12:07 +0300 Subject: [PATCH] =?UTF-8?q?UI=20-=20=D0=9C=D0=95=D0=96=D0=9F=D0=A0=D0=9E?= =?UTF-8?q?=D0=95=D0=9A=D0=A2=D0=9D=D0=90=D0=AF=20=D0=9A=D0=9E=D0=9C=D0=9C?= =?UTF-8?q?=D0=A3=D0=9D=D0=98=D0=9A=D0=90=D0=A6=D0=98=D0=AF:=20=D0=BB?= =?UTF-8?q?=D0=B0=D1=82=D0=B8=D0=BD=D1=81=D0=BA=D0=B8=D0=B9=20URL=20worksp?= =?UTF-8?q?ace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../onboarding/steps/workspace/create.tsx | 10 ++-- .../workspace/create-workspace-form.tsx | 16 ++++--- .../apps/web/core/helpers/workspace-slug.ts | 48 +++++++++++++++++++ 3 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 plane-src/apps/web/core/helpers/workspace-slug.ts diff --git a/plane-src/apps/web/core/components/onboarding/steps/workspace/create.tsx b/plane-src/apps/web/core/components/onboarding/steps/workspace/create.tsx index 08e68b4..bccb638 100644 --- a/plane-src/apps/web/core/components/onboarding/steps/workspace/create.tsx +++ b/plane-src/apps/web/core/components/onboarding/steps/workspace/create.tsx @@ -16,6 +16,7 @@ import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { IUser, IWorkspace } from "@plane/types"; import { Spinner } from "@plane/ui"; import { cn, validateWorkspaceName, validateSlug } from "@plane/utils"; +import { createWorkspaceSlug } from "@/helpers/workspace-slug"; // hooks import { useInstance } from "@/hooks/store/use-instance"; import { useWorkspace } from "@/hooks/store/use-workspace"; @@ -160,7 +161,7 @@ export const WorkspaceCreateStep = observer(function WorkspaceCreateStep({ onChange={(event) => { onChange(event.target.value); setValue("name", event.target.value); - setValue("slug", event.target.value.toLocaleLowerCase().trim().replace(/ /g, "-"), { + setValue("slug", createWorkspaceSlug(event.target.value), { shouldValidate: true, }); }} @@ -215,12 +216,13 @@ export const WorkspaceCreateStep = observer(function WorkspaceCreateStep({ id="slug" name="slug" type="text" - value={value.toLocaleLowerCase().trim().replace(/ /g, "-")} + value={createWorkspaceSlug(value)} onChange={(e) => { - const validation = validateSlug(e.target.value); + const nextSlug = createWorkspaceSlug(e.target.value); + const validation = validateSlug(nextSlug); if (validation === true) setInvalidSlug(false); else setInvalidSlug(true); - onChange(e.target.value.toLowerCase()); + onChange(nextSlug); }} ref={ref} placeholder={t("workspace_creation.form.url.placeholder")} diff --git a/plane-src/apps/web/core/components/workspace/create-workspace-form.tsx b/plane-src/apps/web/core/components/workspace/create-workspace-form.tsx index e887cfa..5369a21 100644 --- a/plane-src/apps/web/core/components/workspace/create-workspace-form.tsx +++ b/plane-src/apps/web/core/components/workspace/create-workspace-form.tsx @@ -17,6 +17,7 @@ import type { IWorkspace } from "@plane/types"; import { Input } from "@plane/ui"; import { validateWorkspaceName, validateSlug } from "@plane/utils"; import { SelectionDropdown } from "@/components/common/selection-dropdown"; +import { createWorkspaceSlug } from "@/helpers/workspace-slug"; // hooks import { useWorkspace } from "@/hooks/store/use-workspace"; import { useAppRouter } from "@/hooks/use-app-router"; @@ -61,7 +62,7 @@ export const CreateWorkspaceForm = observer(function CreateWorkspaceForm(props: const inputShellClassName = isNodeDCAuth ? "nodedc-auth-input-shell flex w-full items-center px-4" : "flex flex-col gap-1"; const inputClassName = isNodeDCAuth ? "nodedc-auth-input h-12 w-full px-0 py-0 text-14" : "w-full"; const urlShellClassName = isNodeDCAuth - ? "nodedc-auth-input-shell flex w-full items-center px-4" + ? "nodedc-auth-input-shell flex w-full items-center px-4 text-14" : "flex w-full items-center rounded-md border border-subtle bg-layer-2 px-3"; const urlInputClassName = isNodeDCAuth ? "nodedc-auth-input block h-12 w-full border-none bg-transparent !px-0 py-0 text-14" @@ -161,7 +162,7 @@ export const CreateWorkspaceForm = observer(function CreateWorkspaceForm(props: onChange={(e) => { onChange(e.target.value); setValue("name", e.target.value); - setValue("slug", e.target.value.toLocaleLowerCase().trim().replace(/ /g, "-"), { + setValue("slug", createWorkspaceSlug(e.target.value), { shouldValidate: true, }); }} @@ -181,7 +182,9 @@ export const CreateWorkspaceForm = observer(function CreateWorkspaceForm(props: {requiredMark}
- {window && window.location.host}/ + + {window && window.location.host}/ + { - const validation = validateSlug(e.target.value); + const nextSlug = createWorkspaceSlug(e.target.value); + const validation = validateSlug(nextSlug); if (validation === true) setInvalidSlug(false); else setInvalidSlug(true); - onChange(e.target.value.toLowerCase()); + onChange(nextSlug); }} ref={ref} hasError={Boolean(errors.slug)} diff --git a/plane-src/apps/web/core/helpers/workspace-slug.ts b/plane-src/apps/web/core/helpers/workspace-slug.ts new file mode 100644 index 0000000..df4613a --- /dev/null +++ b/plane-src/apps/web/core/helpers/workspace-slug.ts @@ -0,0 +1,48 @@ +const CYRILLIC_TO_LATIN: Record = { + а: "a", + б: "b", + в: "v", + г: "g", + д: "d", + е: "e", + ё: "e", + ж: "zh", + з: "z", + и: "i", + й: "y", + к: "k", + л: "l", + м: "m", + н: "n", + о: "o", + п: "p", + р: "r", + с: "s", + т: "t", + у: "u", + ф: "f", + х: "kh", + ц: "ts", + ч: "ch", + ш: "sh", + щ: "shch", + ъ: "", + ы: "y", + ь: "", + э: "e", + ю: "yu", + я: "ya", +}; + +export const createWorkspaceSlug = (value: string) => + value + .trim() + .toLocaleLowerCase() + .split("") + .map((character) => CYRILLIC_TO_LATIN[character] ?? character) + .join("") + .normalize("NFKD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, "") + .replace(/-{2,}/g, "-");