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, "-");