UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: приведение delete-модалок к black-glass канону

This commit is contained in:
DCCONSTRUCTIONS
2026-04-20 18:48:47 +03:00
parent cbd40791e4
commit 6cb0545957
20 changed files with 239 additions and 158 deletions
@@ -1138,9 +1138,14 @@ export default {
file_size_limit: "File must be of {size}MB or less in size.",
drag_and_drop: "Drag and drop anywhere to upload",
delete: "Delete attachment",
delete_confirmation:
"Are you sure you want to delete attachment {value}? This attachment will be permanently removed and this action cannot be undone.",
},
label: {
select: "Add labels",
delete_confirmation:
'Are you sure you want to delete label "{value}"? It will be removed from all work items and from any views where this label is used.',
delete_error: "Label could not be deleted. Please try again.",
create: {
success: "Label created successfully",
failed: "Label creation failed",
@@ -1205,7 +1210,7 @@ export default {
modals: {
decline: {
title: "Decline work item",
content: "Are you sure you want to decline work item {value}?",
content: "Are you sure you want to decline work item {value}? This action cannot be undone.",
},
delete: {
title: "Delete work item",
@@ -1691,6 +1696,11 @@ export default {
description: "Automate notifications to external services when project events occur.",
title: "Webhooks",
add_webhook: "Add webhook",
delete: {
title: "Delete webhook",
description:
"Are you sure you want to delete this webhook? Future events will no longer be delivered to it. This action cannot be undone.",
},
modal: {
title: "Create webhook",
details: "Webhook details",
@@ -1886,6 +1896,12 @@ export default {
heading: "States",
description: "Define and customize workflow states to track the progress of your work items.",
describe_this_state_for_your_members: "Describe this state for your members.",
delete: {
blocked: "This state still contains work items. Move them to another state before deleting it.",
error: "State could not be deleted. Please try again.",
disabled_default: "Cannot delete the default state.",
disabled_last: "Cannot leave the group without states.",
},
empty_state: {
title: "No states available for the {groupKey} group",
description: "Please create a new state",
@@ -1914,6 +1930,17 @@ export default {
label: "Estimates",
title: "Enable estimates for my project",
enable_description: "They help you in communicating complexity and workload of the team.",
delete_modal: {
title: "Delete estimate system",
description:
'Deleting the estimate system "{value}" will permanently remove it from every work item in this project. If you enable estimates again later, you will need to configure work items again.',
submit: "Delete estimate system",
loading: "Deleting",
success_title: "Estimate system deleted",
success_message: "The estimate system has been removed from the project.",
error_title: "Estimate system delete failed",
error_message: "We were unable to delete the estimate system. Please try again.",
},
list_heading: "Estimate list",
archived_heading: "Archived estimates",
archived_description: "These are estimates from earlier project versions that are not currently in use. Read more",
@@ -2304,6 +2331,14 @@ export default {
},
},
project_page: {
delete_modal: {
title: "Delete page",
content: 'Are you sure you want to delete page "{value}"? The page will be permanently removed and this action cannot be undone.',
success_title: "Page deleted",
success_message: "Page deleted successfully.",
error_title: "Page delete failed",
error_message: "Page could not be deleted. Please try again.",
},
empty_state: {
general: {
title:
@@ -1294,9 +1294,14 @@ export default {
file_size_limit: "Максимальный размер файла - {size} МБ",
drag_and_drop: "Перетащите файл для загрузки",
delete: "Удалить вложение",
delete_confirmation:
"Вы уверены, что хотите удалить вложение {value}? Вложение будет удалено без возможности восстановления.",
},
label: {
select: "Выбрать метку",
delete_confirmation:
'Вы уверены, что хотите удалить метку "{value}"? Она будет удалена из всех рабочих элементов и представлений, где используется.',
delete_error: "Не удалось удалить метку. Попробуйте снова.",
create: {
success: "Метка создана",
failed: "Ошибка создания метки",
@@ -1361,7 +1366,7 @@ export default {
modals: {
decline: {
title: "Отклонить рабочий элемент",
content: "Вы уверены, что хотите отклонить рабочий элемент {value}?",
content: "Вы уверены, что хотите отклонить рабочий элемент {value}? Это действие нельзя отменить.",
},
delete: {
title: "Удалить рабочий элемент",
@@ -1853,6 +1858,11 @@ export default {
description: "Автоматизируйте уведомления во внешние сервисы при событиях проекта.",
title: "Вебхуки",
add_webhook: "Добавить вебхук",
delete: {
title: "Удалить вебхук",
description:
"Вы уверены, что хотите удалить этот вебхук? События больше не будут отправляться в этот вебхук. Это действие нельзя отменить.",
},
modal: {
title: "Создать вебхук",
details: "Детали вебхука",
@@ -2045,6 +2055,12 @@ export default {
heading: "Статусы",
description: "Определяйте и настраивайте статусы рабочего процесса для отслеживания прогресса рабочих элементов.",
describe_this_state_for_your_members: "Опишите этот статус для участников",
delete: {
blocked: "В этом статусе есть рабочие элементы. Переместите их в другой статус, чтобы удалить текущий.",
error: "Не удалось удалить статус. Попробуйте снова.",
disabled_default: "Нельзя удалить статус по умолчанию.",
disabled_last: "Нельзя оставить группу без статусов.",
},
empty_state: {
title: "Нет статусов для группы {groupKey}",
description: "Создайте новый статус",
@@ -2073,6 +2089,17 @@ export default {
label: "Оценки",
title: "Включить оценки для моего проекта",
enable_description: "Они помогают вам в общении о сложности и рабочей нагрузке команды.",
delete_modal: {
title: "Удалить систему оценок",
description:
'Удаление системы оценок "{value}" безвозвратно уберет её из всех рабочих элементов проекта. Если вы включите оценки снова, рабочие элементы придется настраивать заново.',
submit: "Удалить систему оценок",
loading: "Удаление",
success_title: "Система оценок удалена",
success_message: "Система оценок удалена из проекта.",
error_title: "Не удалось удалить систему оценок",
error_message: "Мы не смогли удалить систему оценок. Попробуйте снова.",
},
list_heading: "Список оценок",
archived_heading: "Архивные оценки",
archived_description: "Это оценки из предыдущих версий проекта, которые сейчас не используются. Подробнее о них",
@@ -2461,6 +2488,14 @@ export default {
},
},
project_page: {
delete_modal: {
title: "Удалить страницу",
content: 'Вы уверены, что хотите удалить страницу "{value}"? Страница будет удалена без возможности восстановления.',
success_title: "Страница удалена",
success_message: "Страница успешно удалена.",
error_title: "Не удалось удалить страницу",
error_message: "Не удалось удалить страницу. Попробуйте снова.",
},
empty_state: {
general: {
title: "Создавайте заметки, документы или базу знаний. Используйте Galileo, ИИ-помощник NODE.DC.",
@@ -41,9 +41,8 @@ export interface DialogTitleProps extends React.ComponentProps<typeof BaseDialog
}
// Constants
const OVERLAY_CLASSNAME = cn("fixed inset-0 z-90 bg-backdrop/70 backdrop-blur-sm");
const BASE_CLASSNAME =
"nodedc-glass-modal relative w-full rounded-[28px] border border-subtle/70 bg-surface-1/78 text-left shadow-[0_16px_48px_rgba(0,0,0,0.34)] backdrop-blur-2xl z-100";
const OVERLAY_CLASSNAME = cn("fixed inset-0 z-90 bg-black/60 backdrop-blur-md");
const BASE_CLASSNAME = "nodedc-glass-modal relative z-100 w-full rounded-[28px] text-left";
// Utility functions
const getPositionClassNames = (position: DialogPosition) =>
@@ -84,7 +84,7 @@ export function ModalPortal({
const positionClass = fullScreen ? "" : PORTAL_POSITION_CLASSES[position];
return cn(
"nodedc-glass-modal absolute top-0 h-full rounded-[28px] border border-subtle/70 bg-surface-1/78 shadow-[0_16px_48px_rgba(0,0,0,0.34)] backdrop-blur-2xl transition-transform duration-300 ease-out",
"nodedc-glass-modal absolute top-0 h-full rounded-[28px] transition-transform duration-300 ease-out",
widthClass,
positionClass,
contentClassName
@@ -101,7 +101,7 @@ export function ModalPortal({
>
{showOverlay && (
<div
className={cn("absolute inset-0 bg-black/50 backdrop-blur-sm transition-colors duration-300", overlayClassName)}
className={cn("absolute inset-0 bg-black/60 backdrop-blur-md transition-colors duration-300", overlayClassName)}
onClick={handleOverlayClick}
aria-hidden="true"
/>
+1
View File
@@ -34,6 +34,7 @@
"@headlessui/react": "^1.7.3",
"@plane/constants": "workspace:*",
"@plane/hooks": "workspace:*",
"@plane/i18n": "workspace:*",
"@plane/propel": "workspace:*",
"@plane/types": "workspace:*",
"@plane/utils": "workspace:*",
@@ -8,6 +8,7 @@ import type { LucideIcon } from "lucide-react";
import { AlertTriangle, Info } from "lucide-react";
import React from "react";
// components
import { useTranslation } from "@plane/i18n";
import type { TButtonVariant } from "@plane/propel/button";
import { Button } from "@plane/propel/button";
import { cn } from "../utils";
@@ -43,16 +44,17 @@ const VARIANT_ICONS: Record<TModalVariant, LucideIcon> = {
};
const BUTTON_VARIANTS: Record<TModalVariant, TButtonVariant> = {
danger: "error-fill",
danger: "primary",
primary: "primary",
};
const VARIANT_CLASSES: Record<TModalVariant, string> = {
danger: "bg-danger-subtle text-danger-primary",
primary: "bg-accent-primary/20 text-accent-primary",
danger: "nodedc-modal-alert-icon nodedc-modal-alert-icon-danger",
primary: "nodedc-modal-alert-icon nodedc-modal-alert-icon-primary",
};
export function AlertModalCore(props: Props) {
const { t } = useTranslation();
const {
content,
handleClose,
@@ -62,10 +64,10 @@ export function AlertModalCore(props: Props) {
isOpen,
position = EModalPosition.CENTER,
primaryButtonText = {
loading: "Deleting",
default: "Delete",
loading: t("deleting"),
default: t("delete"),
},
secondaryButtonText = "Cancel",
secondaryButtonText = t("cancel"),
title,
variant = "danger",
width = EModalWidth.XL,
@@ -94,11 +96,11 @@ export function AlertModalCore(props: Props) {
</span>
)}
<div className="text-center sm:text-left">
<h3 className="text-18 font-medium text-secondary">{title}</h3>
<h3 className="text-18 font-medium text-primary">{title}</h3>
<p className="mt-1 text-13 text-secondary">{content}</p>
</div>
</div>
<div className="flex flex-col-reverse gap-3 border-t border-subtle/70 px-6 py-4 sm:flex-row sm:justify-end">
<div className="flex flex-col-reverse gap-3 px-6 py-4 sm:flex-row sm:justify-end">
<Button variant="secondary" onClick={handleClose} className="nodedc-modal-secondary-button min-w-[8.25rem]">
{secondaryButtonText}
</Button>
@@ -108,8 +110,8 @@ export function AlertModalCore(props: Props) {
onClick={handleSubmit}
loading={isSubmitting}
className={cn("min-w-[8.25rem]", {
"nodedc-modal-danger-button": variant === "danger",
"nodedc-modal-primary-button": variant === "primary",
"nodedc-modal-danger-button": variant === "danger",
})}
>
{isSubmitting ? primaryButtonText.loading : primaryButtonText.default}
@@ -41,7 +41,7 @@ export function ModalCore(props: Props) {
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-backdrop/70 backdrop-blur-sm transition-opacity" />
<div className="fixed inset-0 bg-black/60 backdrop-blur-md transition-opacity" />
</Transition.Child>
<div className="fixed inset-0 z-30 overflow-y-auto">
@@ -57,7 +57,7 @@ export function ModalCore(props: Props) {
>
<Dialog.Panel
className={cn(
"nodedc-glass-modal relative w-full transform rounded-[28px] border border-subtle/70 bg-surface-1/78 text-left shadow-[0_16px_48px_rgba(0,0,0,0.34)] backdrop-blur-2xl transition-all",
"nodedc-glass-modal relative w-full transform rounded-[28px] text-left transition-all",
width,
className
)}