From 7f47b85c368cd94286a6311a8986ec5ab108b6cb Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Wed, 29 Apr 2026 00:38:04 +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=B4?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D0=B9=D0=BD=20=D0=B8=20=D1=80=D1=83=D1=81?= =?UTF-8?q?=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=86=D0=B8=D1=8F=20God=20Mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/app/(all)/(dashboard)/ai/form.tsx | 28 +- .../admin/app/(all)/(dashboard)/ai/page.tsx | 6 +- .../(dashboard)/authentication/gitea/form.tsx | 32 +- .../(dashboard)/authentication/gitea/page.tsx | 14 +- .../authentication/github/form.tsx | 45 +- .../authentication/github/page.tsx | 14 +- .../authentication/gitlab/form.tsx | 30 +- .../authentication/gitlab/page.tsx | 14 +- .../authentication/google/form.tsx | 42 +- .../authentication/google/page.tsx | 15 +- .../(all)/(dashboard)/authentication/page.tsx | 27 +- .../(dashboard)/email/email-config-form.tsx | 32 +- .../app/(all)/(dashboard)/email/page.tsx | 18 +- .../(dashboard)/email/test-email-modal.tsx | 25 +- .../app/(all)/(dashboard)/general/form.tsx | 32 +- .../(all)/(dashboard)/general/intercom.tsx | 6 +- .../app/(all)/(dashboard)/general/page.tsx | 7 +- .../app/(all)/(dashboard)/image/form.tsx | 12 +- .../app/(all)/(dashboard)/image/page.tsx | 6 +- .../admin/app/(all)/(dashboard)/layout.tsx | 4 +- .../(all)/(dashboard)/sidebar-dropdown.tsx | 79 +-- .../(dashboard)/sidebar-help-section.tsx | 26 +- .../app/(all)/(dashboard)/sidebar-menu.tsx | 59 +-- .../admin/app/(all)/(dashboard)/sidebar.tsx | 2 +- .../(dashboard)/workspace/create/form.tsx | 38 +- .../(dashboard)/workspace/create/page.tsx | 6 +- .../admin/app/(all)/(home)/auth-header.tsx | 6 +- .../admin/app/(all)/(home)/auth-helpers.tsx | 48 +- .../apps/admin/app/(all)/(home)/layout.tsx | 2 +- .../apps/admin/app/(all)/(home)/page.tsx | 4 +- .../admin/app/(all)/(home)/sign-in-form.tsx | 18 +- plane-src/apps/admin/app/components/404.tsx | 11 +- plane-src/apps/admin/app/root.tsx | 16 +- .../authentication-method-card.tsx | 2 +- .../authentication/gitea-config.tsx | 4 +- .../authentication/github-config.tsx | 4 +- .../authentication/gitlab-config.tsx | 4 +- .../authentication/google-config.tsx | 4 +- .../components/common/breadcrumb-link.tsx | 39 +- .../admin/components/common/code-block.tsx | 2 +- .../common/confirm-discard-modal.tsx | 12 +- .../components/common/controller-input.tsx | 4 +- .../components/common/controller-switch.tsx | 2 +- .../admin/components/common/copy-field.tsx | 10 +- .../admin/components/common/header/core.ts | 14 +- .../admin/components/common/header/index.tsx | 48 +- .../components/common/new-user-popup.tsx | 13 +- .../admin/components/common/page-header.tsx | 3 +- .../admin/components/common/page-wrapper.tsx | 8 +- .../admin/components/instance/failure.tsx | 8 +- .../instance/instance-not-ready.tsx | 8 +- .../admin/components/instance/setup-form.tsx | 50 +- plane-src/apps/admin/hooks/oauth/core.tsx | 17 +- .../apps/admin/hooks/use-sidebar-menu/core.ts | 24 +- plane-src/apps/admin/providers/core.tsx | 2 +- plane-src/apps/admin/styles/globals.css | 482 ++++++++++++++++++ .../filters/header/helpers/dropdown.tsx | 204 +++++--- 57 files changed, 1104 insertions(+), 588 deletions(-) diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/ai/form.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/ai/form.tsx index affbda4..76b72aa 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/ai/form.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/ai/form.tsx @@ -41,17 +41,17 @@ export function InstanceAIForm(props: IInstanceAIForm) { { key: "LLM_MODEL", type: "text", - label: "LLM Model", + label: "LLM-модель", description: ( <> - Choose an OpenAI engine.{" "} + Выберите модель OpenAI.{" "} - Learn more + Подробнее ), @@ -62,17 +62,17 @@ export function InstanceAIForm(props: IInstanceAIForm) { { key: "LLM_API_KEY", type: "password", - label: "API key", + label: "API-ключ", description: ( <> - You will find your API key{" "} + API-ключ находится{" "} - here. + здесь. ), @@ -89,8 +89,8 @@ export function InstanceAIForm(props: IInstanceAIForm) { .then(() => setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success", - message: "AI Settings updated successfully", + title: "Сохранено", + message: "ИИ-настройки обновлены", }) ) .catch((err) => console.error(err)); @@ -101,7 +101,7 @@ export function InstanceAIForm(props: IInstanceAIForm) {
OpenAI
-
If you use ChatGPT, this is for you.
+
Используется для встроенных функций на базе OpenAI.
{aiFormFields.map((field) => ( @@ -122,15 +122,15 @@ export function InstanceAIForm(props: IInstanceAIForm) {
-
- +
+
- If you have a preferred AI models vendor, please get in{" "} + Если нужен другой провайдер ИИ-моделей, свяжитесь{" "} - touch with us. + с командой поддержки.
diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/ai/page.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/ai/page.tsx index dec3200..2b0fa9e 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/ai/page.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/ai/page.tsx @@ -25,8 +25,8 @@ const InstanceAIPage = observer(function InstanceAIPage(_props: Route.ComponentP return ( {formattedConfig ? ( @@ -45,6 +45,6 @@ const InstanceAIPage = observer(function InstanceAIPage(_props: Route.ComponentP ); }); -export const meta: Route.MetaFunction = () => [{ title: "Artificial Intelligence Settings - God Mode" }]; +export const meta: Route.MetaFunction = () => [{ title: "ИИ-настройки - NODE.DC" }]; export default InstanceAIPage; diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitea/form.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitea/form.tsx index f05464a..88e8e21 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitea/form.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitea/form.tsx @@ -58,9 +58,9 @@ export function InstanceGiteaConfigForm(props: Props) { { key: "GITEA_HOST", type: "text", - label: "Gitea Host", + label: "Хост Gitea", description: ( - <>Use the URL of your Gitea instance. For the official Gitea instance, use "https://gitea.com". + <>Укажите URL вашего Gitea-инстанса. Для официального сервиса используйте "https://gitea.com". ), placeholder: "https://gitea.com", error: Boolean(errors.GITEA_HOST), @@ -72,7 +72,7 @@ export function InstanceGiteaConfigForm(props: Props) { label: "Client ID", description: ( <> - You will get this from your{" "} + Возьмите значение в настройках{" "} - Gitea OAuth application settings. + Gitea OAuth-приложения. ), @@ -94,7 +94,7 @@ export function InstanceGiteaConfigForm(props: Props) { label: "Client secret", description: ( <> - Your client secret is also found in your{" "} + Секрет клиента находится в настройках{" "} - Gitea OAuth application settings. + Gitea OAuth-приложения. ), @@ -124,8 +124,8 @@ export function InstanceGiteaConfigForm(props: Props) { url: `${originURL}/auth/gitea/callback/`, description: ( <> - We will auto-generate this. Paste this into your Authorized Callback URI{" "} - field{" "} + Значение сформировано автоматически. Вставьте его в поле{" "} + Authorized Callback URI{" "} - here. + здесь. ), @@ -147,8 +147,8 @@ export function InstanceGiteaConfigForm(props: Props) { const response = await updateInstanceConfigurations(payload); setToast({ type: TOAST_TYPE.SUCCESS, - title: "Done!", - message: "Your Gitea authentication is configured. You should test it now.", + title: "Готово", + message: "Аутентификация через Gitea настроена. Проверьте вход перед включением в проде.", }); reset({ GITEA_HOST: response.find((item) => item.key === "GITEA_HOST")?.value, @@ -178,7 +178,7 @@ export function InstanceGiteaConfigForm(props: Props) {
-
Gitea-provided details for Plane
+
Данные Gitea для NODE.DC
{GITEA_FORM_FIELDS.map((field) => ( - {isSubmitting ? "Saving" : "Save changes"} + {isSubmitting ? "Сохранение" : "Сохранить изменения"} - Go back + Назад
-
-
Plane-provided details for Gitea
+
+
Данные NODE.DC для Gitea
{GITEA_SERVICE_FIELD.map((field) => ( ))} diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx index fe8eae4..a742649 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitea/page.tsx @@ -41,14 +41,14 @@ const InstanceGiteaAuthenticationPage = observer(function InstanceGiteaAuthentic const updateConfigPromise = updateInstanceConfigurations(payload); setPromiseToast(updateConfigPromise, { - loading: "Saving Configuration", + loading: "Сохранение конфигурации", success: { - title: "Configuration saved", - message: () => `Gitea authentication is now ${value === "1" ? "active" : "disabled"}.`, + title: "Конфигурация сохранена", + message: () => `Вход через Gitea ${value === "1" ? "включен" : "отключен"}.`, }, error: { - title: "Error", - message: () => "Failed to save configuration", + title: "Ошибка", + message: () => "Не удалось сохранить конфигурацию", }, }); @@ -69,7 +69,7 @@ const InstanceGiteaAuthenticationPage = observer(function InstanceGiteaAuthentic customHeader={ } config={ ); }); -export const meta: Route.MetaFunction = () => [{ title: "Gitea Authentication - God Mode" }]; +export const meta: Route.MetaFunction = () => [{ title: "Gitea OAuth - NODE.DC" }]; export default InstanceGiteaAuthenticationPage; diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx index 2425216..ae64ecc 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx @@ -62,7 +62,7 @@ export function InstanceGithubConfigForm(props: Props) { label: "Client ID", description: ( <> - You will get this from your{" "} + Возьмите значение в настройках{" "} - GitHub OAuth application settings. + GitHub OAuth-приложения. ), @@ -84,7 +84,7 @@ export function InstanceGithubConfigForm(props: Props) { label: "Client secret", description: ( <> - Your client secret is also found in your{" "} + Секрет клиента находится в настройках{" "} - GitHub OAuth application settings. + GitHub OAuth-приложения. ), @@ -103,8 +103,8 @@ export function InstanceGithubConfigForm(props: Props) { { key: "GITHUB_ORGANIZATION_ID", type: "text", - label: "Organization ID", - description: <>The organization github ID., + label: "ID организации", + description: <>ID организации GitHub., placeholder: "123456789", error: Boolean(errors.GITHUB_ORGANIZATION_ID), required: false, @@ -123,7 +123,8 @@ export function InstanceGithubConfigForm(props: Props) { url: originURL, description: ( <> - We will auto-generate this. Paste this into the Authorized origin URL field{" "} + Значение сформировано автоматически. Вставьте его в поле{" "} + Authorized origin URL{" "} - here. + здесь. ), @@ -145,8 +146,8 @@ export function InstanceGithubConfigForm(props: Props) { url: `${originURL}/auth/github/callback/`, description: ( <> - We will auto-generate this. Paste this into your Authorized Callback URI{" "} - field{" "} + Значение сформировано автоматически. Вставьте его в поле{" "} + Authorized Callback URI{" "} - here. + здесь. ), @@ -168,8 +169,8 @@ export function InstanceGithubConfigForm(props: Props) { const response = await updateInstanceConfigurations(payload); setToast({ type: TOAST_TYPE.SUCCESS, - title: "Done!", - message: "Your GitHub authentication is configured. You should test it now.", + title: "Готово", + message: "Аутентификация через GitHub настроена. Проверьте вход перед включением в проде.", }); reset({ GITHUB_CLIENT_ID: response.find((item) => item.key === "GITHUB_CLIENT_ID")?.value, @@ -199,7 +200,7 @@ export function InstanceGithubConfigForm(props: Props) {
-
GitHub-provided details for Plane
+
Данные GitHub для NODE.DC
{GITHUB_FORM_FIELDS.map((field) => ( - {isSubmitting ? "Saving" : "Save changes"} + {isSubmitting ? "Сохранение" : "Сохранить изменения"} - Go back + Назад
-
Plane-provided details for GitHub
+
Данные NODE.DC для GitHub
{/* common service details */} -
+
{GITHUB_COMMON_SERVICE_DETAILS.map((field) => ( ))}
{/* web service details */} -
-
+
+
- Web + Веб
-
+
{GITHUB_SERVICE_DETAILS.map((field) => ( ))} diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx index a7a29cf..ac90351 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx @@ -49,14 +49,14 @@ const InstanceGithubAuthenticationPage = observer(function InstanceGithubAuthent const updateConfigPromise = updateInstanceConfigurations(payload); setPromiseToast(updateConfigPromise, { - loading: "Saving Configuration", + loading: "Сохранение конфигурации", success: { - title: "Configuration saved", - message: () => `GitHub authentication is now ${value === "1" ? "active" : "disabled"}.`, + title: "Конфигурация сохранена", + message: () => `Вход через GitHub ${value === "1" ? "включен" : "отключен"}.`, }, error: { - title: "Error", - message: () => "Failed to save configuration", + title: "Ошибка", + message: () => "Не удалось сохранить конфигурацию", }, }); @@ -77,7 +77,7 @@ const InstanceGithubAuthenticationPage = observer(function InstanceGithubAuthent customHeader={ [{ title: "GitHub Authentication - God Mode" }]; +export const meta: Route.MetaFunction = () => [{ title: "GitHub OAuth - NODE.DC" }]; export default InstanceGithubAuthenticationPage; diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx index 7df6faf..b365e35 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx @@ -58,10 +58,10 @@ export function InstanceGitlabConfigForm(props: Props) { { key: "GITLAB_HOST", type: "text", - label: "Host", + label: "Хост", description: ( <> - This is either https://gitlab.com or the domain.tld where you host GitLab. + Укажите https://gitlab.com или domain.tld, если GitLab развернут у вас. ), placeholder: "https://gitlab.com", @@ -74,7 +74,7 @@ export function InstanceGitlabConfigForm(props: Props) { label: "Application ID", description: ( <> - Get this from your{" "} + Возьмите значение в настройках{" "} - GitLab OAuth application settings + GitLab OAuth-приложения . @@ -97,7 +97,7 @@ export function InstanceGitlabConfigForm(props: Props) { label: "Secret", description: ( <> - The client secret is also found in your{" "} + Секрет клиента находится в настройках{" "} - GitLab OAuth application settings + GitLab OAuth-приложения . @@ -128,7 +128,7 @@ export function InstanceGitlabConfigForm(props: Props) { url: `${originURL}/auth/gitlab/callback/`, description: ( <> - We will auto-generate this. Paste this into the Redirect URI field of your{" "} + Значение сформировано автоматически. Вставьте его в поле Redirect URI{" "} - GitLab OAuth application + GitLab OAuth-приложения . @@ -151,8 +151,8 @@ export function InstanceGitlabConfigForm(props: Props) { const response = await updateInstanceConfigurations(payload); setToast({ type: TOAST_TYPE.SUCCESS, - title: "Done!", - message: "Your GitLab authentication is configured. You should test it now.", + title: "Готово", + message: "Аутентификация через GitLab настроена. Проверьте вход перед включением в проде.", }); reset({ GITLAB_HOST: response.find((item) => item.key === "GITLAB_HOST")?.value, @@ -182,7 +182,7 @@ export function InstanceGitlabConfigForm(props: Props) {
-
GitLab-provided details for Plane
+
Данные GitLab для NODE.DC
{GITLAB_FORM_FIELDS.map((field) => ( - {isSubmitting ? "Saving" : "Save changes"} + {isSubmitting ? "Сохранение" : "Сохранить изменения"} - Go back + Назад
-
-
Plane-provided details for GitLab
+
+
Данные NODE.DC для GitLab
{GITLAB_SERVICE_FIELD.map((field) => ( ))} diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx index 5bcaef7..3a6d98c 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx @@ -43,14 +43,14 @@ const InstanceGitlabAuthenticationPage = observer(function InstanceGitlabAuthent const updateConfigPromise = updateInstanceConfigurations(payload); setPromiseToast(updateConfigPromise, { - loading: "Saving Configuration", + loading: "Сохранение конфигурации", success: { - title: "Configuration saved", - message: () => `GitLab authentication is now ${value === "1" ? "active" : "disabled"}.`, + title: "Конфигурация сохранена", + message: () => `Вход через GitLab ${value === "1" ? "включен" : "отключен"}.`, }, error: { - title: "Error", - message: () => "Failed to save configuration", + title: "Ошибка", + message: () => "Не удалось сохранить конфигурацию", }, }); @@ -68,7 +68,7 @@ const InstanceGitlabAuthenticationPage = observer(function InstanceGitlabAuthent customHeader={ } config={ [{ title: "GitLab Authentication - God Mode" }]; +export const meta: Route.MetaFunction = () => [{ title: "GitLab OAuth - NODE.DC" }]; export default InstanceGitlabAuthenticationPage; diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx index 698ff34..b1e8d08 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx @@ -61,7 +61,7 @@ export function InstanceGoogleConfigForm(props: Props) { label: "Client ID", description: ( <> - Your client ID lives in your Google API Console.{" "} + Client ID находится в Google API Console.{" "} - Learn more + Подробнее ), @@ -83,7 +83,7 @@ export function InstanceGoogleConfigForm(props: Props) { label: "Client secret", description: ( <> - Your client secret should also be in your Google API Console.{" "} + Client secret также находится в Google API Console.{" "} - Learn more + Подробнее ), @@ -113,15 +113,15 @@ export function InstanceGoogleConfigForm(props: Props) { url: originURL, description: (

- We will auto-generate this. Paste this into your{" "} - Authorized JavaScript origins field. For this OAuth client{" "} + Значение сформировано автоматически. Вставьте его в поле{" "} + Authorized JavaScript origins для OAuth-клиента{" "} - here. + здесь.

), @@ -135,15 +135,15 @@ export function InstanceGoogleConfigForm(props: Props) { url: `${originURL}/auth/google/callback/`, description: (

- We will auto-generate this. Paste this into your Authorized Redirect URI{" "} - field. For this OAuth client{" "} + Значение сформировано автоматически. Вставьте его в поле{" "} + Authorized Redirect URI для OAuth-клиента{" "} - here. + здесь.

), @@ -157,8 +157,8 @@ export function InstanceGoogleConfigForm(props: Props) { const response = await updateInstanceConfigurations(payload); setToast({ type: TOAST_TYPE.SUCCESS, - title: "Done!", - message: "Your Google authentication is configured. You should test it now.", + title: "Готово", + message: "Аутентификация через Google настроена. Проверьте вход перед включением в проде.", }); reset({ GOOGLE_CLIENT_ID: response.find((item) => item.key === "GOOGLE_CLIENT_ID")?.value, @@ -187,7 +187,7 @@ export function InstanceGoogleConfigForm(props: Props) {
-
Google-provided details for Plane
+
Данные Google для NODE.DC
{GOOGLE_FORM_FIELDS.map((field) => ( - {isSubmitting ? "Saving" : "Save changes"} + {isSubmitting ? "Сохранение" : "Сохранить изменения"} - Go back + Назад
-
Plane-provided details for Google
+
Данные NODE.DC для Google
{/* common service details */} -
+
{GOOGLE_COMMON_SERVICE_DETAILS.map((field) => ( ))}
{/* web service details */} -
-
+
+
- Web + Веб
-
+
{GOOGLE_SERVICE_DETAILS.map((field) => ( ))} diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx index 93a6149..d780107 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx @@ -43,14 +43,14 @@ const InstanceGoogleAuthenticationPage = observer(function InstanceGoogleAuthent const updateConfigPromise = updateInstanceConfigurations(payload); setPromiseToast(updateConfigPromise, { - loading: "Saving Configuration", + loading: "Сохранение конфигурации", success: { - title: "Configuration saved", - message: () => `Google authentication is now ${value === "1" ? "active" : "disabled"}.`, + title: "Конфигурация сохранена", + message: () => `Вход через Google ${value === "1" ? "включен" : "отключен"}.`, }, error: { - title: "Error", - message: () => "Failed to save configuration", + title: "Ошибка", + message: () => "Не удалось сохранить конфигурацию", }, }); @@ -68,8 +68,7 @@ const InstanceGoogleAuthenticationPage = observer(function InstanceGoogleAuthent customHeader={ } config={ [{ title: "Google Authentication - God Mode" }]; +export const meta: Route.MetaFunction = () => [{ title: "Google OAuth - NODE.DC" }]; export default InstanceGoogleAuthenticationPage; diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/page.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/page.tsx index 26e5fc5..46b0930 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/authentication/page.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/authentication/page.tsx @@ -55,9 +55,8 @@ const InstanceAuthenticationPage = observer(function InstanceAuthenticationPage( if (!canDisable) { setToast({ type: TOAST_TYPE.ERROR, - title: "Cannot disable authentication", - message: - "At least one authentication method must remain enabled. Please enable another method before disabling this one.", + title: "Нельзя отключить вход", + message: "Должен остаться хотя бы один способ входа. Сначала включите другой способ аутентификации.", }); return; } @@ -74,14 +73,14 @@ const InstanceAuthenticationPage = observer(function InstanceAuthenticationPage( const updateConfigPromise = updateInstanceConfigurations(payload); setPromiseToast(updateConfigPromise, { - loading: "Saving configuration", + loading: "Сохранение конфигурации", success: { - title: "Success", - message: () => "Configuration saved successfully", + title: "Сохранено", + message: () => "Конфигурация обновлена", }, error: { - title: "Error", - message: () => "Failed to save configuration", + title: "Ошибка", + message: () => "Не удалось сохранить конфигурацию", }, }); @@ -111,8 +110,8 @@ const InstanceAuthenticationPage = observer(function InstanceAuthenticationPage( return ( {formattedConfig ? ( @@ -120,9 +119,9 @@ const InstanceAuthenticationPage = observer(function InstanceAuthenticationPage(
-
Allow anyone to sign up even without an invite
+
Разрешить регистрацию без приглашения
- Toggling this off will only let users sign up when they are invited. + Если выключить, новые пользователи смогут зарегистрироваться только по приглашению.
@@ -143,7 +142,7 @@ const InstanceAuthenticationPage = observer(function InstanceAuthenticationPage(
-
Available authentication modes
+
Доступные способы входа
{authenticationModes.map((method) => ( [{ title: "Authentication Settings - Plane Web" }]; +export const meta: Route.MetaFunction = () => [{ title: "Аутентификация - NODE.DC" }]; export default InstanceAuthenticationPage; diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx index 794d39a..909c989 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx @@ -31,7 +31,7 @@ type TEmailSecurityKeys = "EMAIL_USE_TLS" | "EMAIL_USE_SSL" | "NONE"; const EMAIL_SECURITY_OPTIONS: { [key in TEmailSecurityKeys]: string } = { EMAIL_USE_TLS: "TLS", EMAIL_USE_SSL: "SSL", - NONE: "No email security", + NONE: "Без шифрования", }; export function InstanceEmailForm(props: IInstanceEmailForm) { @@ -63,7 +63,7 @@ export function InstanceEmailForm(props: IInstanceEmailForm) { { key: "EMAIL_HOST", type: "text", - label: "Host", + label: "Хост", placeholder: "email.google.com", error: Boolean(errors.EMAIL_HOST), required: true, @@ -71,7 +71,7 @@ export function InstanceEmailForm(props: IInstanceEmailForm) { { key: "EMAIL_PORT", type: "text", - label: "Port", + label: "Порт", placeholder: "8080", error: Boolean(errors.EMAIL_PORT), required: true, @@ -79,9 +79,9 @@ export function InstanceEmailForm(props: IInstanceEmailForm) { { key: "EMAIL_FROM", type: "text", - label: "Sender's email address", + label: "Email отправителя", description: - "This is the email address your users will see when getting emails from this instance. You will need to verify this address.", + "Этот адрес будут видеть пользователи в письмах от инстанса. Адрес нужно подтвердить на стороне SMTP.", placeholder: "no-reply@projectplane.so", error: Boolean(errors.EMAIL_FROM), required: true, @@ -92,7 +92,7 @@ export function InstanceEmailForm(props: IInstanceEmailForm) { { key: "EMAIL_HOST_USER", type: "text", - label: "Username", + label: "Имя пользователя", placeholder: "getitdone@projectplane.so", error: Boolean(errors.EMAIL_HOST_USER), required: false, @@ -100,8 +100,8 @@ export function InstanceEmailForm(props: IInstanceEmailForm) { { key: "EMAIL_HOST_PASSWORD", type: "password", - label: "Password", - placeholder: "Password", + label: "Пароль", + placeholder: "Пароль", error: Boolean(errors.EMAIL_HOST_PASSWORD), required: false, }, @@ -114,8 +114,8 @@ export function InstanceEmailForm(props: IInstanceEmailForm) { .then(() => setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success", - message: "Email Settings updated successfully", + title: "Сохранено", + message: "Настройки почты обновлены", }) ) .catch((err) => console.error(err)); @@ -163,12 +163,12 @@ export function InstanceEmailForm(props: IInstanceEmailForm) { /> ))}
-

Email security

+

Защита соединения

{Object.entries(EMAIL_SECURITY_OPTIONS).map(([key, value]) => ( @@ -183,9 +183,9 @@ export function InstanceEmailForm(props: IInstanceEmailForm) {
-
Authentication
+
Аутентификация
- This is optional, but we recommend setting up a username and a password for your SMTP server. + Необязательно, но для SMTP-сервера обычно нужны имя пользователя и пароль.
@@ -215,7 +215,7 @@ export function InstanceEmailForm(props: IInstanceEmailForm) { loading={isSubmitting} disabled={!isValid || !isDirty} > - {isSubmitting ? "Saving" : "Save changes"} + {isSubmitting ? "Сохранение" : "Сохранить изменения"}
diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/email/page.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/email/page.tsx index e74b34b..605b36a 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/email/page.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/email/page.tsx @@ -34,14 +34,14 @@ const InstanceEmailPage = observer(function InstanceEmailPage(_props: Route.Comp await disableEmail(); setIsSMTPEnabled(false); setToast({ - title: "Email feature disabled", - message: "Email feature has been disabled", + title: "Почта отключена", + message: "Отправка писем через SMTP отключена", type: TOAST_TYPE.SUCCESS, }); } catch (_error) { setToast({ - title: "Error disabling email", - message: "Failed to disable email feature. Please try again.", + title: "Не удалось отключить почту", + message: "Повторите попытку.", type: TOAST_TYPE.ERROR, }); } finally { @@ -60,13 +60,13 @@ const InstanceEmailPage = observer(function InstanceEmailPage(_props: Route.Comp return ( - Plane can send useful emails to you and your users from your own instance without talking to the Internet. + NODE.DC может отправлять системные письма пользователям через ваш SMTP-сервер.
- Set it up below and please test your settings before you save them.  - Misconfigs can lead to email bounces and errors. + Заполните параметры ниже и проверьте отправку перед сохранением.  + Ошибки в конфигурации приводят к отказам доставки.
), @@ -98,6 +98,6 @@ const InstanceEmailPage = observer(function InstanceEmailPage(_props: Route.Comp ); }); -export const meta: Route.MetaFunction = () => [{ title: "Email Settings - God Mode" }]; +export const meta: Route.MetaFunction = () => [{ title: "Настройки почты - NODE.DC" }]; export default InstanceEmailPage; diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx index 630b886..a24746a 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx @@ -58,7 +58,7 @@ export function SendTestEmailModal(props: Props) { setSendEmailStep(ESendEmailSteps.SUCCESS); }) .catch((error) => { - setError(error?.error || "Failed to send email"); + setError(error?.error || "Не удалось отправить письмо"); setSendEmailStep(ESendEmailSteps.FAILED); }) .finally(() => { @@ -91,13 +91,13 @@ export function SendTestEmailModal(props: Props) { leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +

{sendEmailStep === ESendEmailSteps.SEND_EMAIL - ? "Send test email" + ? "Отправить тестовое письмо" : sendEmailStep === ESendEmailSteps.SUCCESS - ? "Email send" - : "Failed"}{" "} + ? "Письмо отправлено" + : "Ошибка отправки"}{" "}

{sendEmailStep === ESendEmailSteps.SEND_EMAIL && ( @@ -106,28 +106,25 @@ export function SendTestEmailModal(props: Props) { type="email" value={receiverEmail} onChange={(e) => setReceiverEmail(e.target.value)} - placeholder="Receiver email" - className="w-full resize-none text-16" + placeholder="Email получателя" + className="nodedc-settings-input w-full resize-none text-16" tabIndex={1} /> )} {sendEmailStep === ESendEmailSteps.SUCCESS && (
-

- We have sent the test email to {receiverEmail}. Please check your spam folder if you cannot find - it. -

-

If you still cannot find it, recheck your SMTP configuration and trigger a new test email.

+

Тестовое письмо отправлено на {receiverEmail}. Если письма нет во входящих, проверьте спам.

+

Если письмо не пришло, проверьте SMTP-настройки и отправьте тест заново.

)} {sendEmailStep === ESendEmailSteps.FAILED &&
{error}
}
{sendEmailStep === ESendEmailSteps.SEND_EMAIL && ( )}
diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/general/form.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/general/form.tsx index 0b402b7..d55d5c9 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/general/form.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/general/form.tsx @@ -60,8 +60,8 @@ export const GeneralConfigurationForm = observer(function GeneralConfigurationFo .then(() => setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success", - message: "Settings updated successfully", + title: "Сохранено", + message: "Основные настройки обновлены", }) ) .catch((err) => console.error(err)); @@ -70,41 +70,41 @@ export const GeneralConfigurationForm = observer(function GeneralConfigurationFo return (
-
Instance details
+
Данные инстанса
-

Email

+

Email администратора

-

Instance ID

+

ID инстанса

@@ -112,7 +112,7 @@ export const GeneralConfigurationForm = observer(function GeneralConfigurationFo
-
Chat + telemetry
+
Чат и телеметрия
@@ -122,17 +122,17 @@ export const GeneralConfigurationForm = observer(function GeneralConfigurationFo
-
Let Plane collect anonymous usage data
+
Разрешить анонимную телеметрию
- No PII is collected.This anonymized data is used to understand how you use Plane and build new features - in line with{" "} + Персональные данные не собираются. Анонимные события помогают понимать, как используется система, с + учетом{" "} - our Telemetry Policy. + политики телеметрии.
@@ -158,7 +158,7 @@ export const GeneralConfigurationForm = observer(function GeneralConfigurationFo }} loading={isSubmitting} > - {isSubmitting ? "Saving" : "Save changes"} + {isSubmitting ? "Сохранение" : "Сохранить изменения"}
diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/general/intercom.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/general/intercom.tsx index 656e2d6..112a906 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/general/intercom.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/general/intercom.tsx @@ -64,10 +64,10 @@ export const IntercomConfig = observer(function IntercomConfig(props: TIntercomC
-
Chat with us
+
Встроенный чат поддержки
- Let your users chat with us via Intercom or another service. Toggling Telemetry off turns this off - automatically. + Разрешает пользователям писать в поддержку через Intercom или аналогичный сервис. При отключении + телеметрии чат отключается автоматически.
diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/general/page.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/general/page.tsx index cb0a8c6..7508cee 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/general/page.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/general/page.tsx @@ -20,9 +20,8 @@ function GeneralPage() { return ( {instance && instanceAdmins && } @@ -30,6 +29,6 @@ function GeneralPage() { ); } -export const meta: Route.MetaFunction = () => [{ title: "General Settings - God Mode" }]; +export const meta: Route.MetaFunction = () => [{ title: "Основные настройки - NODE.DC" }]; export default observer(GeneralPage); diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/image/form.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/image/form.tsx index 72ab513..db721d8 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/image/form.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/image/form.tsx @@ -41,8 +41,8 @@ export function InstanceImageConfigForm(props: IInstanceImageConfigForm) { .then(() => setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success", - message: "Image Configuration Settings updated successfully", + title: "Сохранено", + message: "Настройки изображений обновлены", }) ) .catch((err) => console.error(err)); @@ -55,17 +55,17 @@ export function InstanceImageConfigForm(props: IInstanceImageConfigForm) { control={control} type="password" name="UNSPLASH_ACCESS_KEY" - label="Access key from your Unsplash account" + label="Access key аккаунта Unsplash" description={ <> - You will find your access key in your Unsplash developer console.  + Ключ доступа находится в консоли разработчика Unsplash.  - Learn more. + Подробнее. } @@ -77,7 +77,7 @@ export function InstanceImageConfigForm(props: IInstanceImageConfigForm) {
diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/image/page.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/image/page.tsx index e410e87..04ffa26 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/image/page.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/image/page.tsx @@ -25,8 +25,8 @@ const InstanceImagePage = observer(function InstanceImagePage(_props: Route.Comp return ( {formattedConfig ? ( @@ -41,6 +41,6 @@ const InstanceImagePage = observer(function InstanceImagePage(_props: Route.Comp ); }); -export const meta: Route.MetaFunction = () => [{ title: "Images Settings - God Mode" }]; +export const meta: Route.MetaFunction = () => [{ title: "Изображения - NODE.DC" }]; export default InstanceImagePage; diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/layout.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/layout.tsx index e56acf7..b0ec4b8 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/layout.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/layout.tsx @@ -38,9 +38,9 @@ function AdminLayout(_props: Route.ComponentProps) { if (isUserLoggedIn) { return ( -
+
-
+
diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx index d1ddb1f..32818be 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx @@ -36,11 +36,13 @@ export const AdminSidebarDropdown = observer(function AdminSidebarDropdown() { const handleSignOut = () => signOut(); - const getSidebarMenuItems = () => ( + const getSidebarMenuItems = (align: "left" | "right" = "left") => ( - Switch to {resolvedTheme === "dark" ? "light" : "dark"} mode + {resolvedTheme === "dark" ? "Светлая тема" : "Темная тема"}
@@ -65,10 +67,10 @@ export const AdminSidebarDropdown = observer(function AdminSidebarDropdown() { - Sign out + Выйти
@@ -81,10 +83,10 @@ export const AdminSidebarDropdown = observer(function AdminSidebarDropdown() { }, [csrfToken]); return ( -
+
@@ -94,8 +96,8 @@ export const AdminSidebarDropdown = observer(function AdminSidebarDropdown() { "cursor-default": !isSidebarCollapsed, })} > -
- +
+
{isSidebarCollapsed && ( @@ -114,38 +116,39 @@ export const AdminSidebarDropdown = observer(function AdminSidebarDropdown() { {!isSidebarCollapsed && ( -
-

Instance admin

+
+

Глобальный админ

+
Супер-администратор
)} + + {!isSidebarCollapsed && currentUser && ( + + + + + + + {getSidebarMenuItems("right")} + + + )}
- - {!isSidebarCollapsed && currentUser && ( - - - - - - - {getSidebarMenuItems()} - - - )}
); }); diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx index 51401f3..dc2cf0d 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx @@ -20,17 +20,17 @@ import { useInstance, useTheme } from "@/hooks/store"; const helpOptions = [ { - name: "Documentation", + name: "Документация", href: "https://docs.plane.so/", Icon: PageIcon, }, { - name: "Join our Forum", + name: "Форум Plane", href: "https://forum.plane.so", Icon: MessageSquare, }, { - name: "Report a bug", + name: "Сообщить об ошибке", href: "https://github.com/makeplane/plane/issues/new/choose", Icon: GithubIcon, }, @@ -50,26 +50,26 @@ export const AdminSidebarHelpSection = observer(function AdminSidebarHelpSection return (
- + - {!isSidebarCollapsed && "Redirect to Plane"} + {!isSidebarCollapsed && "В приложение"} - + - +
diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-menu.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-menu.tsx index 319cf01..b247295 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-menu.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/sidebar-menu.tsx @@ -21,6 +21,10 @@ export const AdminSidebarMenu = observer(function AdminSidebarMenu() { const { isSidebarCollapsed, toggleSidebar } = useTheme(); // derived values const sidebarMenu = useSidebarMenu(); + const sidebarMenuGroups = [ + { label: "ИНСТАНС", items: sidebarMenu.slice(0, 4) }, + { label: "ВОЗМОЖНОСТИ", items: sidebarMenu.slice(4) }, + ]; const handleItemClick = () => { if (window.innerWidth < 768) { @@ -29,36 +33,33 @@ export const AdminSidebarMenu = observer(function AdminSidebarMenu() { }; return ( -
- {sidebarMenu.map((item, index) => { - const isActive = item.href === pathName || pathName?.includes(item.href); - return ( - -
- -
- {} - {!isSidebarCollapsed && ( -
-
{item.name}
-
{item.description}
+
+ {sidebarMenuGroups.map((group) => ( +
+ {!isSidebarCollapsed &&
{group.label}
} +
+ {group.items.map((item) => { + const isActive = item.href === pathName || Boolean(pathName?.startsWith(item.href)); + return ( + + +
+ {} + {!isSidebarCollapsed &&
{item.name}
}
- )} -
- -
- - ); - })} + + + ); + })} +
+
+ ))}
); }); diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/sidebar.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/sidebar.tsx index 6d9c970..c3667da 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/sidebar.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/sidebar.tsx @@ -44,7 +44,7 @@ export const AdminSidebar = observer(function AdminSidebar() { return (
diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx index d250b76..cc3ef7e 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx @@ -56,16 +56,16 @@ export function WorkspaceCreateForm() { .then(async () => { setToast({ type: TOAST_TYPE.SUCCESS, - title: "Success!", - message: "Workspace created successfully.", + title: "Готово", + message: "Воркспейс создан.", }); router.push(`/workspace`); }) .catch(() => { setToast({ type: TOAST_TYPE.ERROR, - title: "Error!", - message: "Workspace could not be created. Please try again.", + title: "Ошибка", + message: "Не удалось создать воркспейс. Попробуйте еще раз.", }); }); } else setSlugError(true); @@ -73,8 +73,8 @@ export function WorkspaceCreateForm() { .catch(() => { setToast({ type: TOAST_TYPE.ERROR, - title: "Error!", - message: "Some error occurred while creating workspace. Please try again.", + title: "Ошибка", + message: "При создании воркспейса произошла ошибка. Попробуйте еще раз.", }); }); }; @@ -91,7 +91,7 @@ export function WorkspaceCreateForm() {
-

Name your workspace

+

Название воркспейса

)} /> @@ -122,8 +122,8 @@ export function WorkspaceCreateForm() {
-

Set your workspace's URL

-
+

URL воркспейса

+
{workspaceBaseURL}
- {slugError &&

This URL is taken. Try something else.

} + {slugError &&

Этот URL уже занят. Выберите другой.

} {invalidSlug && ( -

{`URLs can contain only ( - ), ( _ ) and alphanumeric characters.`}

+

{`URL может содержать только латинские буквы, цифры, "-" и "_".`}

)} {errors.slug && {errors.slug.message}}
-

How many people will use this workspace?

+

Сколько людей будет работать в воркспейсе?

( c === value) ?? ( - Select a range + Выберите диапазон ) } - buttonClassName="!border-[0.5px] !border-subtle !shadow-none" + buttonClassName="nodedc-settings-select !border-[0.5px] !border-subtle !shadow-none" input > {ORGANIZATION_SIZE.map((item) => ( @@ -196,10 +196,10 @@ export function WorkspaceCreateForm() { disabled={!isValid} loading={isSubmitting} > - {isSubmitting ? "Creating workspace" : "Create workspace"} + {isSubmitting ? "Создание" : "Создать воркспейс"} - Go back + Назад
diff --git a/plane-src/apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx b/plane-src/apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx index a31d03d..0ff40c0 100644 --- a/plane-src/apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx +++ b/plane-src/apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx @@ -16,8 +16,8 @@ const WorkspaceCreatePage = observer(function WorkspaceCreatePage(_props: Route. return ( @@ -25,6 +25,6 @@ const WorkspaceCreatePage = observer(function WorkspaceCreatePage(_props: Route. ); }); -export const meta: Route.MetaFunction = () => [{ title: "Create Workspace - God Mode" }]; +export const meta: Route.MetaFunction = () => [{ title: "Создание воркспейса - NODE.DC" }]; export default WorkspaceCreatePage; diff --git a/plane-src/apps/admin/app/(all)/(home)/auth-header.tsx b/plane-src/apps/admin/app/(all)/(home)/auth-header.tsx index ae94821..d27ac93 100644 --- a/plane-src/apps/admin/app/(all)/(home)/auth-header.tsx +++ b/plane-src/apps/admin/app/(all)/(home)/auth-header.tsx @@ -5,14 +5,16 @@ */ import Link from "next/link"; -import { PlaneLockup } from "@plane/propel/icons"; export function AuthHeader() { return (
- + NODE.DC + + Глобальное администрирование +
); } diff --git a/plane-src/apps/admin/app/(all)/(home)/auth-helpers.tsx b/plane-src/apps/admin/app/(all)/(home)/auth-helpers.tsx index ea18dc9..85713a6 100644 --- a/plane-src/apps/admin/app/(all)/(home)/auth-helpers.tsx +++ b/plane-src/apps/admin/app/(all)/(home)/auth-helpers.tsx @@ -22,56 +22,56 @@ const errorCodeMessages: { } = { // admin [EAdminAuthErrorCodes.ADMIN_ALREADY_EXIST]: { - title: `Admin already exists`, - message: () => `Admin already exists. Please try again.`, + title: `Администратор уже существует`, + message: () => `Администратор уже существует. Попробуйте еще раз.`, }, [EAdminAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME]: { - title: `Email, password and first name required`, - message: () => `Email, password and first name required. Please try again.`, + title: `Нужны email, пароль и имя`, + message: () => `Укажите email, пароль и имя. Попробуйте еще раз.`, }, [EAdminAuthErrorCodes.INVALID_ADMIN_EMAIL]: { - title: `Invalid admin email`, - message: () => `Invalid admin email. Please try again.`, + title: `Некорректный email администратора`, + message: () => `Некорректный email администратора. Попробуйте еще раз.`, }, [EAdminAuthErrorCodes.INVALID_ADMIN_PASSWORD]: { - title: `Invalid admin password`, - message: () => `Invalid admin password. Please try again.`, + title: `Некорректный пароль администратора`, + message: () => `Некорректный пароль администратора. Попробуйте еще раз.`, }, [EAdminAuthErrorCodes.REQUIRED_ADMIN_EMAIL_PASSWORD]: { - title: `Email and password required`, - message: () => `Email and password required. Please try again.`, + title: `Нужны email и пароль`, + message: () => `Укажите email и пароль. Попробуйте еще раз.`, }, [EAdminAuthErrorCodes.ADMIN_AUTHENTICATION_FAILED]: { - title: `Authentication failed`, - message: () => `Authentication failed. Please try again.`, + title: `Ошибка входа`, + message: () => `Не удалось войти. Проверьте данные и попробуйте еще раз.`, }, [EAdminAuthErrorCodes.ADMIN_USER_ALREADY_EXIST]: { - title: `Admin user already exists`, + title: `Администратор уже существует`, message: () => (
- Admin user already exists.  + Администратор уже существует.  - Sign In + Войти -  now. +  сейчас.
), }, [EAdminAuthErrorCodes.ADMIN_USER_DOES_NOT_EXIST]: { - title: `Admin user does not exist`, + title: `Администратор не найден`, message: () => (
- Admin user does not exist.  + Администратор не найден.  - Sign In + Войти -  now. +  сейчас.
), }, [EAdminAuthErrorCodes.ADMIN_USER_DEACTIVATED]: { - title: `User account deactivated`, - message: () => `User account deactivated. Please contact ${SUPPORT_EMAIL ? SUPPORT_EMAIL : "administrator"}.`, + title: `Аккаунт деактивирован`, + message: () => `Аккаунт деактивирован. Свяжитесь с ${SUPPORT_EMAIL ? SUPPORT_EMAIL : "администратором"}.`, }, }; @@ -92,8 +92,8 @@ export const authErrorHandler = (errorCode: EAdminAuthErrorCodes, email?: string return { type: EErrorAlertType.BANNER_ALERT, code: errorCode, - title: errorCodeMessages[errorCode]?.title || "Error", - message: errorCodeMessages[errorCode]?.message(email) || "Something went wrong. Please try again.", + title: errorCodeMessages[errorCode]?.title || "Ошибка", + message: errorCodeMessages[errorCode]?.message(email) || "Что-то пошло не так. Попробуйте еще раз.", }; return undefined; diff --git a/plane-src/apps/admin/app/(all)/(home)/layout.tsx b/plane-src/apps/admin/app/(all)/(home)/layout.tsx index a334536..4f10692 100644 --- a/plane-src/apps/admin/app/(all)/(home)/layout.tsx +++ b/plane-src/apps/admin/app/(all)/(home)/layout.tsx @@ -22,7 +22,7 @@ function RootLayout() { }, [replace, isUserLoggedIn]); return ( -
+
); diff --git a/plane-src/apps/admin/app/(all)/(home)/page.tsx b/plane-src/apps/admin/app/(all)/(home)/page.tsx index 7947adc..0e94102 100644 --- a/plane-src/apps/admin/app/(all)/(home)/page.tsx +++ b/plane-src/apps/admin/app/(all)/(home)/page.tsx @@ -45,6 +45,6 @@ function HomePage() { export default observer(HomePage); export const meta: Route.MetaFunction = () => [ - { title: "Admin – Instance Setup & Sign-In" }, - { name: "description", content: "Configure your Plane instance or sign in to the admin portal." }, + { title: "NODE.DC - вход в админ-панель" }, + { name: "description", content: "Настройка инстанса NODE.DC и вход в глобальную админ-панель." }, ]; diff --git a/plane-src/apps/admin/app/(all)/(home)/sign-in-form.tsx b/plane-src/apps/admin/app/(all)/(home)/sign-in-form.tsx index 4e0afb8..88e697c 100644 --- a/plane-src/apps/admin/app/(all)/(home)/sign-in-form.tsx +++ b/plane-src/apps/admin/app/(all)/(home)/sign-in-form.tsx @@ -112,10 +112,10 @@ export function InstanceSignInForm() { <>
-
+
handleFormChange("password", e.target.value)} autoComplete="off" @@ -188,7 +188,7 @@ export function InstanceSignInForm() {
diff --git a/plane-src/apps/admin/app/components/404.tsx b/plane-src/apps/admin/app/components/404.tsx index 473dbb6..d052957 100644 --- a/plane-src/apps/admin/app/components/404.tsx +++ b/plane-src/apps/admin/app/components/404.tsx @@ -17,19 +17,16 @@ function PageNotFound() {
- 404 - Page not found + 404 - страница не найдена
-

Oops! Something went wrong.

-

- Sorry, the page you are looking for cannot be found. It may have been removed, had its name changed, or is - temporarily unavailable. -

+

Страница не найдена

+

Похоже, раздел был удален, переименован или временно недоступен.

diff --git a/plane-src/apps/admin/app/root.tsx b/plane-src/apps/admin/app/root.tsx index 5d4eafb..fb4eb70 100644 --- a/plane-src/apps/admin/app/root.tsx +++ b/plane-src/apps/admin/app/root.tsx @@ -21,9 +21,8 @@ import interVariableWoff2 from "@fontsource-variable/inter/files/inter-latin-wgh import "@fontsource/material-symbols-rounded"; import "@fontsource/ibm-plex-mono"; -const APP_TITLE = "Plane | Simple, extensible, open-source project management tool."; -const APP_DESCRIPTION = - "Open-source project management tool to manage work items, sprints, and product roadmaps with peace of mind."; +const APP_TITLE = "NODE.DC | Глобальное администрирование"; +const APP_DESCRIPTION = "Панель глобального администрирования инстанса NODE.DC."; export const links: LinksFunction = () => [ { rel: "apple-touch-icon", sizes: "180x180", href: appleTouchIcon }, @@ -43,7 +42,7 @@ export const links: LinksFunction = () => [ export function Layout({ children }: { children: ReactNode }) { return ( - + @@ -66,15 +65,14 @@ export const meta: Route.MetaFunction = () => [ { property: "og:url", content: "https://plane.so/" }, { name: "keywords", - content: - "software development, customer feedback, software, accelerate, code management, release management, project management, work items tracking, agile, scrum, kanban, collaboration", + content: "NODE.DC, администрирование, рабочие пространства, проекты, пользователи, настройки инстанса", }, - { name: "twitter:site", content: "@planepowers" }, + { name: "twitter:site", content: "@nodedc" }, ]; export default function Root() { return ( -
+
); @@ -91,7 +89,7 @@ export function HydrateFallback() { export function ErrorBoundary({ error: _error }: Route.ErrorBoundaryProps) { return (
-

Something went wrong.

+

Что-то пошло не так.

); } diff --git a/plane-src/apps/admin/components/authentication/authentication-method-card.tsx b/plane-src/apps/admin/components/authentication/authentication-method-card.tsx index 86934bb..cfd5e78 100644 --- a/plane-src/apps/admin/components/authentication/authentication-method-card.tsx +++ b/plane-src/apps/admin/components/authentication/authentication-method-card.tsx @@ -22,7 +22,7 @@ export function AuthenticationMethodCard(props: Props) { return (
diff --git a/plane-src/apps/admin/components/authentication/gitea-config.tsx b/plane-src/apps/admin/components/authentication/gitea-config.tsx index ef9d6db..8040fcf 100644 --- a/plane-src/apps/admin/components/authentication/gitea-config.tsx +++ b/plane-src/apps/admin/components/authentication/gitea-config.tsx @@ -35,7 +35,7 @@ export const GiteaConfiguration = observer(function GiteaConfiguration(props: Pr {GiteaConfigured ? (
- Edit + Изменить - Configure + Настроить )} diff --git a/plane-src/apps/admin/components/authentication/github-config.tsx b/plane-src/apps/admin/components/authentication/github-config.tsx index 06443bf..ce9d157 100644 --- a/plane-src/apps/admin/components/authentication/github-config.tsx +++ b/plane-src/apps/admin/components/authentication/github-config.tsx @@ -34,7 +34,7 @@ export const GithubConfiguration = observer(function GithubConfiguration(props: {isGithubConfigured ? (
- Edit + Изменить - Configure + Настроить )} diff --git a/plane-src/apps/admin/components/authentication/gitlab-config.tsx b/plane-src/apps/admin/components/authentication/gitlab-config.tsx index dcd3bed..cea2570 100644 --- a/plane-src/apps/admin/components/authentication/gitlab-config.tsx +++ b/plane-src/apps/admin/components/authentication/gitlab-config.tsx @@ -34,7 +34,7 @@ export const GitlabConfiguration = observer(function GitlabConfiguration(props: {isGitlabConfigured ? (
- Edit + Изменить - Configure + Настроить )} diff --git a/plane-src/apps/admin/components/authentication/google-config.tsx b/plane-src/apps/admin/components/authentication/google-config.tsx index 556dd5a..b710488 100644 --- a/plane-src/apps/admin/components/authentication/google-config.tsx +++ b/plane-src/apps/admin/components/authentication/google-config.tsx @@ -34,7 +34,7 @@ export const GoogleConfiguration = observer(function GoogleConfiguration(props: {isGoogleConfigured ? (
- Edit + Изменить - Configure + Настроить )} diff --git a/plane-src/apps/admin/components/common/breadcrumb-link.tsx b/plane-src/apps/admin/components/common/breadcrumb-link.tsx index 46d4fd1..e370600 100644 --- a/plane-src/apps/admin/components/common/breadcrumb-link.tsx +++ b/plane-src/apps/admin/components/common/breadcrumb-link.tsx @@ -11,26 +11,35 @@ type Props = { label?: string; href?: string; icon?: React.ReactNode | undefined; + isCurrent?: boolean; }; export function BreadcrumbLink(props: Props) { - const { href, label, icon } = props; + const { href, label, icon, isCurrent = false } = props; + + const content = ( + <> + {icon &&
{icon}
} +
{label}
+ + ); + return ( -
  • -
    - {href ? ( - - {icon &&
    {icon}
    } -
    {label}
    - - ) : ( -
    - {icon &&
    {icon}
    } -
    {label}
    -
    - )} -
    +
  • + {href && !isCurrent ? ( + + {content} + + ) : ( +
    + {content} +
    + )}
  • ); diff --git a/plane-src/apps/admin/components/common/code-block.tsx b/plane-src/apps/admin/components/common/code-block.tsx index 02c44be..3e72f8d 100644 --- a/plane-src/apps/admin/components/common/code-block.tsx +++ b/plane-src/apps/admin/components/common/code-block.tsx @@ -16,7 +16,7 @@ export function CodeBlock({ children, className, darkerShade }: TProps) { return ( - +
    - You have unsaved changes + Есть несохраненные изменения
    -

    - Changes you made will be lost if you go back. Do you wish to go back? -

    +

    Если уйти назад, текущие правки будут потеряны.

    - Go back + Уйти назад
    diff --git a/plane-src/apps/admin/components/common/controller-input.tsx b/plane-src/apps/admin/components/common/controller-input.tsx index 3d95b44..0e9b39a 100644 --- a/plane-src/apps/admin/components/common/controller-input.tsx +++ b/plane-src/apps/admin/components/common/controller-input.tsx @@ -46,7 +46,7 @@ export function ControllerInput(props: Props) { ( diff --git a/plane-src/apps/admin/components/common/controller-switch.tsx b/plane-src/apps/admin/components/common/controller-switch.tsx index 5868194..8bb1a78 100644 --- a/plane-src/apps/admin/components/common/controller-switch.tsx +++ b/plane-src/apps/admin/components/common/controller-switch.tsx @@ -27,7 +27,7 @@ export function ControllerSwitch(props: Props) { return (
    -

    Refresh user attributes from {label} during sign in

    +

    Обновлять атрибуты пользователя из {label} при входе

    { navigator.clipboard.writeText(url); setToast({ type: TOAST_TYPE.INFO, - title: "Copied to clipboard", - message: `The ${label} has been successfully copied to your clipboard`, + title: "Скопировано", + message: `${label} скопировано в буфер обмена`, }); }} > -

    {url}

    - +

    {url}

    +
    {description}
    diff --git a/plane-src/apps/admin/components/common/header/core.ts b/plane-src/apps/admin/components/common/header/core.ts index 4cfc8ee..c452d20 100644 --- a/plane-src/apps/admin/components/common/header/core.ts +++ b/plane-src/apps/admin/components/common/header/core.ts @@ -5,15 +5,15 @@ */ export const CORE_HEADER_SEGMENT_LABELS: Record = { - general: "General", - ai: "Artificial Intelligence", - email: "Email", - authentication: "Authentication", - image: "Image", + general: "Основное", + ai: "Искусственный интеллект", + email: "Почта", + authentication: "Аутентификация", + image: "Изображения", google: "Google", github: "GitHub", gitlab: "GitLab", gitea: "Gitea", - workspace: "Workspace", - create: "Create", + workspace: "Воркспейсы", + create: "Создание", }; diff --git a/plane-src/apps/admin/components/common/header/index.tsx b/plane-src/apps/admin/components/common/header/index.tsx index 48b9ca7..3bb84ce 100644 --- a/plane-src/apps/admin/components/common/header/index.tsx +++ b/plane-src/apps/admin/components/common/header/index.tsx @@ -4,11 +4,10 @@ * See the LICENSE file for details. */ +import { Fragment } from "react"; import { observer } from "mobx-react"; import { usePathname } from "next/navigation"; -import { Menu, Settings } from "lucide-react"; -// icons -import { Breadcrumbs } from "@plane/ui"; +import { ChevronRight, Menu, Settings } from "lucide-react"; // components import { BreadcrumbLink } from "../breadcrumb-link"; // hooks @@ -22,6 +21,7 @@ export const HamburgerToggle = observer(function HamburgerToggle() { return (
    @@ -46,7 +45,7 @@ export const NewUserPopup = observer(function NewUserPopup() { src={resolveGeneralTheme(resolvedTheme) === "dark" ? TakeoffIconDark : TakeoffIconLight} height={80} width={80} - alt="Plane icon" + alt="NODE.DC" />
    diff --git a/plane-src/apps/admin/components/common/page-header.tsx b/plane-src/apps/admin/components/common/page-header.tsx index eafc4e9..8543c34 100644 --- a/plane-src/apps/admin/components/common/page-header.tsx +++ b/plane-src/apps/admin/components/common/page-header.tsx @@ -10,7 +10,8 @@ type TPageHeader = { }; export function PageHeader(props: TPageHeader) { - const { title = "God Mode - Plane", description = "Plane god mode" } = props; + const { title = "NODE.DC - глобальное администрирование", description = "Глобальное администрирование NODE.DC" } = + props; return ( <> diff --git a/plane-src/apps/admin/components/common/page-wrapper.tsx b/plane-src/apps/admin/components/common/page-wrapper.tsx index b5a19e6..b777413 100644 --- a/plane-src/apps/admin/components/common/page-wrapper.tsx +++ b/plane-src/apps/admin/components/common/page-wrapper.tsx @@ -24,16 +24,16 @@ export const PageWrapper = (props: TPageWrapperProps) => { return (
    {customHeader ? ( -
    {customHeader}
    +
    {customHeader}
    ) : ( header && ( -
    +
    {header.title}
    {header.description}
    @@ -42,7 +42,7 @@ export const PageWrapper = (props: TPageWrapperProps) => {
    ) )} -
    +
    {children}
    diff --git a/plane-src/apps/admin/components/instance/failure.tsx b/plane-src/apps/admin/components/instance/failure.tsx index 1f1610f..08836d9 100644 --- a/plane-src/apps/admin/components/instance/failure.tsx +++ b/plane-src/apps/admin/components/instance/failure.tsx @@ -27,15 +27,15 @@ export const InstanceFailureView = observer(function InstanceFailureView() {
    - Instance failure illustration -

    Unable to fetch instance details.

    + Ошибка загрузки инстанса +

    Не удалось загрузить данные инстанса.

    - We were unable to fetch the details of the instance. Fret not, it might just be a connectivity issue. + Проверьте соединение с API и попробуйте обновить страницу.

    diff --git a/plane-src/apps/admin/components/instance/instance-not-ready.tsx b/plane-src/apps/admin/components/instance/instance-not-ready.tsx index 4fa5e06..c5b85f8 100644 --- a/plane-src/apps/admin/components/instance/instance-not-ready.tsx +++ b/plane-src/apps/admin/components/instance/instance-not-ready.tsx @@ -14,15 +14,15 @@ export function InstanceNotReady() {
    -

    Welcome aboard Plane!

    - Plane Logo -

    Get started by setting up your instance and workspace

    +

    Добро пожаловать в NODE.DC

    + NODE.DC +

    Начните с настройки инстанса и первого воркспейса

    diff --git a/plane-src/apps/admin/components/instance/setup-form.tsx b/plane-src/apps/admin/components/instance/setup-form.tsx index 74e80db..f26f75d 100644 --- a/plane-src/apps/admin/components/instance/setup-form.tsx +++ b/plane-src/apps/admin/components/instance/setup-form.tsx @@ -140,10 +140,10 @@ export function InstanceSetupForm() { <>
    -
    +
    {errorData.type && errorData?.message && @@ -163,15 +163,15 @@ export function InstanceSetupForm() {
    { const validation = validatePersonName(e.target.value); @@ -186,15 +186,15 @@ export function InstanceSetupForm() {
    { const validation = validatePersonName(e.target.value); @@ -210,10 +210,10 @@ export function InstanceSetupForm() {
    { const validation = validateCompanyName(e.target.value, false); @@ -253,16 +253,16 @@ export function InstanceSetupForm() {
    handleFormChange("password", e.target.value)} hasError={errorData.type && errorData.type === EErrorCodes.INVALID_PASSWORD ? true : false} @@ -298,7 +298,7 @@ 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" + placeholder="Повторите пароль" + className="nodedc-settings-input w-full border border-subtle !bg-surface-1 pr-12 placeholder:text-placeholder" onFocus={() => setIsRetryPasswordInputFocused(true)} onBlur={() => setIsRetryPasswordInputFocused(false)} autoComplete="new-password" @@ -336,9 +336,7 @@ export function InstanceSetupForm() {
    {!!formData.confirm_password && formData.password !== formData.confirm_password && - renderPasswordMatchError && ( - Passwords don{"'"}t match - )} + renderPasswordMatchError && Пароли не совпадают}
    @@ -352,7 +350,7 @@ export function InstanceSetupForm() { />
    diff --git a/plane-src/apps/admin/hooks/oauth/core.tsx b/plane-src/apps/admin/hooks/oauth/core.tsx index 9e6914e..05a15ae 100644 --- a/plane-src/apps/admin/hooks/oauth/core.tsx +++ b/plane-src/apps/admin/hooks/oauth/core.tsx @@ -35,17 +35,16 @@ export const getCoreAuthenticationModesMap: ( }) => ({ "unique-codes": { key: "unique-codes", - name: "Unique codes", - description: - "Log in or sign up for Plane using codes sent via email. You need to have set up SMTP to use this method.", + name: "Одноразовые коды", + description: "Вход и регистрация по кодам из email. Для этого способа нужен настроенный SMTP.", icon: , config: , enabledConfigKey: "ENABLE_MAGIC_LINK_LOGIN", }, "passwords-login": { key: "passwords-login", - name: "Passwords", - description: "Allow members to create accounts with passwords and use it with their email addresses to sign in.", + name: "Пароли", + description: "Пользователи создают аккаунты с паролем и входят по email.", icon: , config: , enabledConfigKey: "ENABLE_EMAIL_PASSWORD", @@ -53,7 +52,7 @@ export const getCoreAuthenticationModesMap: ( google: { key: "google", name: "Google", - description: "Allow members to log in or sign up for Plane with their Google accounts.", + description: "Вход и регистрация через аккаунты Google.", icon: Google Logo, config: , enabledConfigKey: "IS_GOOGLE_ENABLED", @@ -61,7 +60,7 @@ export const getCoreAuthenticationModesMap: ( github: { key: "github", name: "GitHub", - description: "Allow members to log in or sign up for Plane with their GitHub accounts.", + description: "Вход и регистрация через аккаунты GitHub.", icon: ( GitLab Logo, config: , enabledConfigKey: "IS_GITLAB_ENABLED", @@ -84,7 +83,7 @@ export const getCoreAuthenticationModesMap: ( gitea: { key: "gitea", name: "Gitea", - description: "Allow members to log in or sign up to plane with their Gitea accounts.", + description: "Вход и регистрация через аккаунты Gitea.", icon: Gitea Logo, config: , enabledConfigKey: "IS_GITEA_ENABLED", diff --git a/plane-src/apps/admin/hooks/use-sidebar-menu/core.ts b/plane-src/apps/admin/hooks/use-sidebar-menu/core.ts index 9d4e449..61752c1 100644 --- a/plane-src/apps/admin/hooks/use-sidebar-menu/core.ts +++ b/plane-src/apps/admin/hooks/use-sidebar-menu/core.ts @@ -15,38 +15,38 @@ export type TCoreSidebarMenuKey = "general" | "email" | "workspace" | "authentic export const coreSidebarMenuLinks: Record = { general: { Icon: Cog, - name: "General", - description: "Identify your instances and get key details.", + name: "Основное", + description: "Имя инстанса, ID и телеметрия.", href: `/general/`, }, email: { Icon: Mail, - name: "Email", - description: "Configure your SMTP controls.", + name: "Почта", + description: "SMTP и тестовая отправка.", href: `/email/`, }, workspace: { Icon: WorkspaceIcon, - name: "Workspaces", - description: "Manage all workspaces on this instance.", + name: "Воркспейсы", + description: "Все рабочие пространства инстанса.", href: `/workspace/`, }, authentication: { Icon: LockIcon, - name: "Authentication", - description: "Configure authentication modes.", + name: "Аутентификация", + description: "Вход, регистрация и OAuth.", href: `/authentication/`, }, ai: { Icon: BrainCog, - name: "Artificial intelligence", - description: "Configure your OpenAI creds.", + name: "ИИ", + description: "OpenAI модель и ключ API.", href: `/ai/`, }, image: { Icon: Image, - name: "Images in Plane", - description: "Allow third-party image libraries.", + name: "Изображения", + description: "Внешние библиотеки изображений.", href: `/image/`, }, }; diff --git a/plane-src/apps/admin/providers/core.tsx b/plane-src/apps/admin/providers/core.tsx index 3b22c70..fda4846 100644 --- a/plane-src/apps/admin/providers/core.tsx +++ b/plane-src/apps/admin/providers/core.tsx @@ -24,7 +24,7 @@ const DEFAULT_SWR_CONFIG = { export function CoreProviders({ children }: { children: React.ReactNode }) { return ( - + diff --git a/plane-src/apps/admin/styles/globals.css b/plane-src/apps/admin/styles/globals.css index 7f7f248..daeab34 100644 --- a/plane-src/apps/admin/styles/globals.css +++ b/plane-src/apps/admin/styles/globals.css @@ -1,5 +1,27 @@ @import "@plane/tailwind-config/index.css"; +:root { + --nodedc-accent-rgb: 195 255 102; + --nodedc-on-accent-rgb: 11 17 23; + --nodedc-card-passive-rgb: 42 43 46; + --nodedc-on-card-passive-rgb: 245 247 251; + --nodedc-card-active-rgb: 195 255 102; + --nodedc-on-card-active-rgb: 11 17 23; + --brand-default: rgb(var(--nodedc-accent-rgb)); + --brand-300: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 65%, white); + --brand-700: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 75%, black); + --bg-accent-primary: rgb(var(--nodedc-accent-rgb)); + --bg-accent-primary-hover: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 82%, white); + --bg-accent-primary-active: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 90%, black); + --txt-on-color: rgb(var(--nodedc-on-accent-rgb)); + --txt-icon-on-color: rgb(var(--nodedc-on-accent-rgb)); +} + +html, +body { + background: #050506; +} + .shadow-custom { box-shadow: 2px 2px 8px 2px rgba(234, 231, 250, 0.3); /* Convert #EAE7FA4D to rgba */ } @@ -40,3 +62,463 @@ 0 0 4px --alpha(var(--background-color-accent-primary) / 40%) !important; will-change: transform, opacity; } + +@layer components { + .nodedc-admin-root { + min-height: 100vh; + background: + radial-gradient(circle at 12% -8%, rgba(var(--nodedc-accent-rgb), 0.18), transparent 32rem), + radial-gradient(circle at 88% 0%, rgba(var(--nodedc-card-active-rgb), 0.1), transparent 30rem), #050506; + color: var(--text-color-primary); + } + + .nodedc-admin-shell { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.018) 0%, rgba(255, 255, 255, 0.006) 100%), rgba(5, 5, 7, 0.94); + } + + .nodedc-admin-main { + background: transparent !important; + } + + .nodedc-auth-shell { + background: + radial-gradient(circle at 20% 0%, rgba(var(--nodedc-accent-rgb), 0.2), transparent 30rem), + radial-gradient(circle at 78% 10%, rgba(var(--nodedc-card-active-rgb), 0.11), transparent 26rem), #050506 !important; + } + + .nodedc-auth-card { + border-radius: 1.75rem; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.036) 0%, rgba(255, 255, 255, 0.012) 100%), rgba(8, 8, 11, 0.78); + padding: 1.5rem; + -webkit-backdrop-filter: blur(34px); + backdrop-filter: blur(34px); + box-shadow: + 0 24px 64px rgba(0, 0, 0, 0.42), + inset 0 1px 0 rgba(255, 255, 255, 0.035); + } + + .nodedc-glass-sidebar { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.024) 0%, rgba(255, 255, 255, 0.008) 100%), rgba(8, 8, 11, 0.9) !important; + border-right: 1px solid rgba(255, 255, 255, 0.07) !important; + -webkit-backdrop-filter: blur(28px); + backdrop-filter: blur(28px); + box-shadow: + inset -1px 0 0 rgba(255, 255, 255, 0.06), + inset 0 1px 0 rgba(255, 255, 255, 0.015), + 0 18px 48px rgba(0, 0, 0, 0.26); + } + + .nodedc-glass-modal, + .nodedc-glass-surface { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.036) 0%, rgba(255, 255, 255, 0.012) 100%), rgba(8, 8, 11, 0.88) !important; + border: 0 !important; + outline: none !important; + -webkit-backdrop-filter: blur(38px); + backdrop-filter: blur(38px); + box-shadow: + 0 22px 58px rgba(0, 0, 0, 0.34), + inset 0 1px 0 rgba(255, 255, 255, 0.025); + } + + .nodedc-glass-popup-surface { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.012) 100%), rgba(8, 8, 11, 0.93) !important; + border: 0 !important; + outline: none !important; + border-radius: 1.25rem !important; + -webkit-backdrop-filter: blur(42px); + backdrop-filter: blur(42px); + box-shadow: 0 22px 60px rgba(0, 0, 0, 0.36); + } + + .nodedc-admin-header { + min-height: 4.25rem; + border: 0 !important; + border-radius: 0 0 1.35rem 1.35rem; + margin: 0.65rem 0.75rem 0; + width: calc(100% - 1.5rem) !important; + } + + .nodedc-admin-breadcrumbs { + display: flex; + min-width: 0; + align-items: center; + gap: 0.35rem; + } + + .nodedc-admin-breadcrumb-pill { + min-height: 2.5rem; + border: 0 !important; + border-radius: 1.25rem !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.036) 0%, rgba(255, 255, 255, 0.014) 100%), + rgba(255, 255, 255, 0.04) !important; + color: rgba(255, 255, 255, 0.72) !important; + padding: 0.55rem 0.9rem !important; + box-shadow: none !important; + transition: + background-color 160ms ease, + color 160ms ease; + } + + .nodedc-admin-breadcrumb-pill:hover { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.018) 100%), + rgba(255, 255, 255, 0.065) !important; + color: var(--text-color-primary) !important; + } + + .nodedc-admin-breadcrumb-pill[data-current="true"] { + color: rgb(var(--nodedc-accent-rgb)) !important; + } + + .nodedc-admin-breadcrumb-pill[data-current="true"] * { + color: rgb(var(--nodedc-accent-rgb)) !important; + } + + .nodedc-admin-breadcrumb-separator { + color: rgba(255, 255, 255, 0.32); + } + + .nodedc-page { + padding: 1rem 0 1.5rem; + } + + .nodedc-page-header { + border: 0 !important; + border-radius: 1.35rem; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.025) 0%, rgba(255, 255, 255, 0.01) 100%), rgba(255, 255, 255, 0.025); + padding: 1.1rem 1.25rem !important; + } + + .nodedc-page-body { + padding-top: 0.15rem; + } + + .nodedc-settings-card { + border: 0 !important; + outline: none !important; + border-radius: 1.35rem !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0%, rgba(255, 255, 255, 0.012) 100%), + rgba(255, 255, 255, 0.032) !important; + box-shadow: none !important; + -webkit-backdrop-filter: blur(18px); + backdrop-filter: blur(18px); + } + + .nodedc-settings-card:hover { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.018) 100%), rgba(255, 255, 255, 0.046) !important; + } + + .nodedc-admin-sidebar-profile { + border: 0 !important; + border-radius: 1.35rem !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.044) 0%, rgba(255, 255, 255, 0.016) 100%), + rgba(255, 255, 255, 0.04) !important; + box-shadow: none !important; + -webkit-backdrop-filter: blur(18px); + backdrop-filter: blur(18px); + } + + .nodedc-admin-sidebar-avatar-button { + border: 0 !important; + border-radius: 0.65rem !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.014) 100%), + rgba(0, 0, 0, 0.32) !important; + color: rgb(var(--nodedc-accent-rgb)) !important; + box-shadow: none !important; + } + + .nodedc-admin-sidebar-section-label { + padding: 0.55rem 0.9rem 0.45rem; + color: rgba(255, 255, 255, 0.54); + font-size: 0.68rem; + font-weight: 700; + letter-spacing: 0.18em; + text-transform: uppercase; + } + + .nodedc-settings-sidebar-item { + min-height: 2.75rem; + border: 0 !important; + outline: none !important; + border-radius: 1.1rem !important; + background: transparent !important; + color: rgba(255, 255, 255, 0.76) !important; + padding-inline: 0.95rem !important; + box-shadow: none !important; + } + + .nodedc-settings-sidebar-item:hover { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.014) 100%), rgba(255, 255, 255, 0.03) !important; + color: var(--text-color-primary) !important; + } + + .nodedc-settings-sidebar-item[data-active="true"] { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.016) 100%), rgba(255, 255, 255, 0.045) !important; + color: rgb(var(--nodedc-accent-rgb)) !important; + box-shadow: none !important; + } + + .nodedc-settings-sidebar-item[data-active="true"] * { + color: rgb(var(--nodedc-accent-rgb)) !important; + } + + .nodedc-admin-sidebar-action { + min-height: 2.25rem; + border: 0 !important; + border-radius: 999px !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.036) 0%, rgba(255, 255, 255, 0.014) 100%), + rgba(255, 255, 255, 0.045) !important; + color: rgba(255, 255, 255, 0.7) !important; + box-shadow: none !important; + } + + .nodedc-admin-sidebar-action:hover { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.052) 0%, rgba(255, 255, 255, 0.018) 100%), + rgba(255, 255, 255, 0.07) !important; + color: var(--text-color-primary) !important; + } + + .nodedc-settings-input, + .nodedc-admin-root input:not([type="checkbox"]) { + min-height: 3rem; + border: 0 !important; + outline: none !important; + border-radius: 1.25rem !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0%, rgba(255, 255, 255, 0.012) 100%), + rgba(255, 255, 255, 0.032) !important; + color: var(--text-color-primary) !important; + box-shadow: none !important; + -webkit-backdrop-filter: blur(18px); + backdrop-filter: blur(18px); + } + + .nodedc-settings-input:focus, + .nodedc-settings-input:focus-visible, + .nodedc-admin-root input:not([type="checkbox"]):focus, + .nodedc-admin-root input:not([type="checkbox"]):focus-visible { + outline: none !important; + box-shadow: inset 0 0 0 1px rgba(var(--nodedc-accent-rgb), 0.28) !important; + } + + .nodedc-settings-input input { + min-height: auto !important; + border-radius: 0 !important; + background: transparent !important; + box-shadow: none !important; + } + + .nodedc-settings-select { + min-height: 3rem !important; + border: 0 !important; + outline: none !important; + border-radius: 1.25rem !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0%, rgba(255, 255, 255, 0.012) 100%), + rgba(255, 255, 255, 0.032) !important; + color: var(--text-color-primary) !important; + box-shadow: none !important; + } + + .nodedc-admin-root [data-slot="button"], + .nodedc-admin-root button, + .nodedc-admin-root a[class*="rounded"] { + outline: none !important; + } + + .nodedc-settings-save-button, + .nodedc-admin-root [data-slot="button"].bg-accent-primary, + .nodedc-admin-root button.bg-accent-primary:not([role="switch"]), + .nodedc-admin-root a.bg-accent-primary { + min-height: 2.75rem; + border: 0 !important; + border-radius: 1.25rem !important; + background: rgb(var(--nodedc-card-active-rgb)) !important; + color: rgb(var(--nodedc-on-card-active-rgb)) !important; + box-shadow: none !important; + padding-inline: 1.35rem !important; + } + + .nodedc-settings-save-button:hover, + .nodedc-admin-root [data-slot="button"].bg-accent-primary:hover, + .nodedc-admin-root button.bg-accent-primary:not([role="switch"]):hover, + .nodedc-admin-root a.bg-accent-primary:hover { + background: color-mix(in srgb, rgb(var(--nodedc-card-active-rgb)) 82%, white) !important; + color: rgb(var(--nodedc-on-card-active-rgb)) !important; + } + + .nodedc-settings-save-button *, + .nodedc-admin-root [data-slot="button"].bg-accent-primary *, + .nodedc-admin-root button.bg-accent-primary:not([role="switch"]) *, + .nodedc-admin-root a.bg-accent-primary * { + color: rgb(var(--nodedc-on-card-active-rgb)) !important; + } + + .nodedc-admin-root button[role="switch"] { + min-height: auto !important; + padding: 0 !important; + border-radius: 9999px !important; + border-color: rgba(255, 255, 255, 0.1) !important; + background: rgba(255, 255, 255, 0.13) !important; + box-shadow: none !important; + } + + .nodedc-admin-root button[role="switch"][aria-checked="true"] { + border-color: transparent !important; + background: rgb(var(--nodedc-card-active-rgb)) !important; + } + + .nodedc-admin-root button[role="switch"]:hover { + background: rgba(255, 255, 255, 0.17) !important; + } + + .nodedc-admin-root button[role="switch"][aria-checked="true"]:hover { + background: color-mix(in srgb, rgb(var(--nodedc-card-active-rgb)) 88%, white) !important; + } + + .nodedc-admin-root button[role="switch"] > span[aria-hidden="true"] { + background: rgba(255, 255, 255, 0.72) !important; + } + + .nodedc-admin-root button[role="switch"][aria-checked="true"] > span[aria-hidden="true"] { + background: rgba(9, 10, 9, 0.94) !important; + } + + .nodedc-admin-root input[type="checkbox"] { + min-height: 1rem !important; + width: 1rem !important; + height: 1rem !important; + padding: 0 !important; + border-radius: 0.35rem !important; + border-color: rgba(255, 255, 255, 0.22) !important; + background: rgba(255, 255, 255, 0.06) !important; + box-shadow: none !important; + } + + .nodedc-admin-root input[type="checkbox"]:hover { + border-color: rgba(255, 255, 255, 0.36) !important; + background: rgba(255, 255, 255, 0.09) !important; + } + + .nodedc-admin-root input[type="checkbox"]:checked, + .nodedc-admin-root input[type="checkbox"]:indeterminate { + border-color: transparent !important; + background: rgb(var(--nodedc-card-active-rgb)) !important; + } + + .nodedc-admin-root input[type="checkbox"]:focus-visible { + outline: none !important; + box-shadow: 0 0 0 4px rgba(var(--nodedc-accent-rgb), 0.18) !important; + } + + .nodedc-settings-secondary-button { + min-height: 2.75rem; + border: 0 !important; + border-radius: 1.25rem !important; + background: rgba(255, 255, 255, 0.06) !important; + color: var(--text-color-primary) !important; + box-shadow: none !important; + padding-inline: 1.25rem !important; + } + + .nodedc-admin-root [data-slot="button"].border-strong, + .nodedc-admin-root a.border-strong, + .nodedc-admin-root button.border-strong:not([role="switch"]) { + min-height: 2.75rem; + border: 0 !important; + border-radius: 1.25rem !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.022) 100%), + rgba(255, 255, 255, 0.055) !important; + color: rgba(255, 255, 255, 0.76) !important; + box-shadow: none !important; + padding-inline: 1.25rem !important; + } + + .nodedc-settings-secondary-button:hover, + .nodedc-admin-root [data-slot="button"].border-strong:hover, + .nodedc-admin-root a.border-strong:hover, + .nodedc-admin-root button.border-strong:not([role="switch"]):hover { + background: rgba(255, 255, 255, 0.1) !important; + color: var(--text-color-primary) !important; + } + + .nodedc-admin-root [data-slot="button"]:disabled, + .nodedc-admin-root button:disabled:not([role="switch"]), + .nodedc-admin-root [aria-disabled="true"] { + border: 0 !important; + background: rgba(255, 255, 255, 0.035) !important; + color: rgba(255, 255, 255, 0.34) !important; + opacity: 1 !important; + box-shadow: none !important; + } + + .nodedc-settings-note { + border: 0 !important; + border-radius: 1.25rem !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.045) 0%, rgba(255, 255, 255, 0.018) 100%), + rgba(255, 255, 255, 0.045) !important; + color: rgba(255, 255, 255, 0.72) !important; + box-shadow: none !important; + } + + .nodedc-settings-note a { + color: rgb(var(--nodedc-card-active-rgb)) !important; + text-decoration-color: rgba(var(--nodedc-card-active-rgb), 0.48) !important; + } + + .nodedc-settings-helper-card { + border: 0 !important; + border-radius: 1.35rem !important; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.048) 0%, rgba(255, 255, 255, 0.018) 100%), + rgba(255, 255, 255, 0.045) !important; + box-shadow: none !important; + -webkit-backdrop-filter: blur(18px); + backdrop-filter: blur(18px); + } + + .nodedc-settings-helper-card-header { + border: 0 !important; + background: rgba(255, 255, 255, 0.035) !important; + color: rgba(255, 255, 255, 0.72) !important; + } + + .nodedc-admin-root .bg-layer-1, + .nodedc-admin-root .bg-layer-2, + .nodedc-admin-root .bg-layer-3 { + background-color: rgba(255, 255, 255, 0.04) !important; + } + + .nodedc-admin-root .border-subtle, + .nodedc-admin-root .border-subtle-1 { + border-color: rgba(255, 255, 255, 0.07) !important; + } + + .nodedc-admin-root :focus-visible { + outline: none !important; + } + + .nodedc-code-chip { + border: 0 !important; + border-radius: 0.65rem !important; + background: rgba(255, 255, 255, 0.08) !important; + color: rgb(var(--nodedc-card-active-rgb)) !important; + } +} diff --git a/plane-src/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx b/plane-src/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx index ea1dd51..b1d611c 100644 --- a/plane-src/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx +++ b/plane-src/apps/web/core/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx @@ -4,11 +4,10 @@ * See the LICENSE file for details. */ -import React, { Fragment, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; +import { createPortal } from "react-dom"; import type { Placement } from "@popperjs/core"; import { usePopper } from "react-popper"; -// headless ui -import { Popover, Portal, Transition } from "@headlessui/react"; // ui import { Button } from "@plane/propel/button"; @@ -45,93 +44,132 @@ export function FiltersDropdown(props: Props) { const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); + const [isOpen, setIsOpen] = useState(false); const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: placement ?? "auto", }); - return ( - - {({ open, close }) => ( - <> - - {menuButton ? ( - - ) : ( -
    -
    - -
    -
    - -
    -
    - )} -
    - { + if (!disabled) setIsOpen((current) => !current); + }, [disabled]); + + const closeDropdown = useCallback(() => setIsOpen(false), []); + + const handleTriggerKeyDown = useCallback( + (event: React.KeyboardEvent) => { + if (disabled) return; + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + toggleDropdown(); + } + if (event.key === "Escape") closeDropdown(); + }, + [closeDropdown, disabled, toggleDropdown] + ); + + useEffect(() => { + if (!isOpen) return; + + const handleDocumentMouseDown = (event: MouseEvent) => { + const target = event.target; + if (!(target instanceof Node)) return; + + if (referenceElement?.contains(target) || popperElement?.contains(target)) return; + closeDropdown(); + }; + + const handleDocumentKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") closeDropdown(); + }; + + document.addEventListener("mousedown", handleDocumentMouseDown); + document.addEventListener("keydown", handleDocumentKeyDown); + + return () => { + document.removeEventListener("mousedown", handleDocumentMouseDown); + document.removeEventListener("keydown", handleDocumentKeyDown); + }; + }, [closeDropdown, isOpen, popperElement, referenceElement]); + + const dropdownPanel = + isOpen && + createPortal( +
    +
    +
    - - {/** translate-y-0 is a hack to create new stacking context. Required for safari */} - -
    -
    - {typeof children === "function" ? children({ closeDropdown: close }) : children} -
    + {typeof children === "function" ? children({ closeDropdown }) : children} +
    +
    +
    , + document.body + ); + + return ( + <> + {menuButton ? ( + + ) : ( +
    +
    + +
    +
    + +
    +
    )} - + {dropdownPanel} + ); }