UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: настройки проекта, модалки и drag-and-drop канон
This commit is contained in:
parent
bd7a7d96fe
commit
df68c96795
|
|
@ -0,0 +1,111 @@
|
||||||
|
# HDESIGN CODE
|
||||||
|
|
||||||
|
Документ фиксирует канон интерфейса NODE.DC, чтобы не обсуждать одни и те же правила повторно.
|
||||||
|
|
||||||
|
## Источник цветов
|
||||||
|
- Основной runtime-конфиг цветов: [design.config.json](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/design.config.json)
|
||||||
|
- Рабочая web-копия: [plane-src/apps/web/design.config.json](/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src/apps/web/design.config.json)
|
||||||
|
- В рантайме используются CSS variables:
|
||||||
|
- `--nodedc-accent-rgb`
|
||||||
|
- `--nodedc-card-passive-rgb`
|
||||||
|
- `--nodedc-card-active-rgb`
|
||||||
|
|
||||||
|
## Цветовые правила
|
||||||
|
- `accent_rgb`: акцентный цвет интерфейса.
|
||||||
|
- `passive_card_rgb`: пассивные карточки.
|
||||||
|
- `active_card_rgb`: активные карточки и primary CTA в зелёной теме.
|
||||||
|
- Primary/active элементы используют акцентный или `active_card_rgb`.
|
||||||
|
- Secondary элементы не должны иметь ярких outline и цветных рамок без явной причины.
|
||||||
|
|
||||||
|
## Радиусы
|
||||||
|
- Главные модалки и большие surface-контейнеры: `1.75rem`
|
||||||
|
- Стандартные glass-карточки и settings-карточки: `1.35rem`
|
||||||
|
- Поля ввода, селекты, secondary/primary кнопки, chip-кнопки: `1.25rem`
|
||||||
|
- Малые круглые action-кнопки: `999px` или полный круг при квадратной коробке
|
||||||
|
|
||||||
|
## Outline и рамки
|
||||||
|
- Внешние outline у контролов запрещены.
|
||||||
|
- Синий browser outline должен быть снят и заменён на нормальный hover/focus surface.
|
||||||
|
- Если нужен контур, он должен быть частью дизайна:
|
||||||
|
- мягкий glass border
|
||||||
|
- акцентный border для drag/drop или active-state
|
||||||
|
- Красные технические outline, debug-рамки и случайные browser-box shadow запрещены.
|
||||||
|
|
||||||
|
## Glass и фон
|
||||||
|
- Popup, dropdown, modal, sidebar overlays и settings-карточки используют matte black glass.
|
||||||
|
- База:
|
||||||
|
- тёмный полупрозрачный фон
|
||||||
|
- `backdrop-filter: blur(...)`
|
||||||
|
- мягкая стеклянная граница
|
||||||
|
- Popup не должны выглядеть просто прозрачными. Если blur не читается, проблема в слое рендера, а не в одном `rgba`.
|
||||||
|
|
||||||
|
## Кнопки
|
||||||
|
- Все кнопки без жёсткого outline.
|
||||||
|
- Primary button:
|
||||||
|
- фон: акцентный или `active_card_rgb`
|
||||||
|
- текст: чёрный или очень тёмный, если фон светлый
|
||||||
|
- hover: более светлая версия того же цвета
|
||||||
|
- Save/update button:
|
||||||
|
- если это зафиксированный green CTA, текст должен быть контрастным и читаемым
|
||||||
|
- hover осветляет текущий тон, а не уходит в синий
|
||||||
|
- Secondary button:
|
||||||
|
- тёмный glass фон
|
||||||
|
- без border-outline
|
||||||
|
- hover немного светлее базового surface
|
||||||
|
- Danger button:
|
||||||
|
- без кислотно-красных рамок
|
||||||
|
- мягкий danger surface
|
||||||
|
|
||||||
|
## Поля и селекты
|
||||||
|
- Все поля ввода, textarea, select, chip-select:
|
||||||
|
- скруглённые
|
||||||
|
- без внешних outline
|
||||||
|
- glass background
|
||||||
|
- единая вертикальная высота для одного класса контролов
|
||||||
|
- Placeholder и label должны быть читаемы и не прилипать к краям.
|
||||||
|
|
||||||
|
## Toolbar и верхние панели
|
||||||
|
- Элементы верхней панели центрируются по одной горизонтальной оси.
|
||||||
|
- Активный layout/tool mode выделяется кругом акцентного цвета, не квадратной плашкой.
|
||||||
|
- Кнопки `Отображение`, `Аналитика`, `Добавить рабочий элемент`:
|
||||||
|
- одинаковая высота
|
||||||
|
- каноничные радиусы
|
||||||
|
- нормальные горизонтальные paddings, чтобы текст не лип к краям
|
||||||
|
|
||||||
|
## Карточки
|
||||||
|
- Внутренние карточки строятся по симметричным верхним и нижним padding.
|
||||||
|
- Верхняя ось:
|
||||||
|
- аватар
|
||||||
|
- имя
|
||||||
|
- вторичная строка
|
||||||
|
- action-circle справа
|
||||||
|
должны сидеть на согласованной геометрии
|
||||||
|
- Нижняя ось:
|
||||||
|
- assignee bubbles
|
||||||
|
- дата
|
||||||
|
должны быть симметричны верхней
|
||||||
|
|
||||||
|
## Dropdown и popup
|
||||||
|
- Все dropdown/popup приводятся к единому matte glass канону.
|
||||||
|
- Запрещены:
|
||||||
|
- квадратные active-box вокруг круглых кнопок
|
||||||
|
- жёсткие border-outline
|
||||||
|
- светлый фон, если основной экран тёмный
|
||||||
|
- Search shell внутри popup должен использовать тот же стиль, что и сам popup.
|
||||||
|
|
||||||
|
## Drag and drop
|
||||||
|
- Drag overlay использует акцентный контур.
|
||||||
|
- Delete dropzone:
|
||||||
|
- без красного технического свечения
|
||||||
|
- текст локализован
|
||||||
|
- акцентный outline допустим
|
||||||
|
|
||||||
|
## Тексты
|
||||||
|
- Пользовательский UI на русском, если экран русифицирован.
|
||||||
|
- Не оставлять смешанные подписи вида `Created at / Updated at / Label / State group`, если экран уже на русском.
|
||||||
|
|
||||||
|
## Правило внедрения
|
||||||
|
- Новый экран или popup не стилизуется локально “на глаз”.
|
||||||
|
- Сначала используется существующий shared-класс или shared-component.
|
||||||
|
- Если shared-слоя нет, создаётся reusable-класс/компонент и уже через него приводятся все похожие места.
|
||||||
|
- Цель: не точечная покраска одного окна, а единый системный канон.
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
{
|
|
||||||
"nodedc": {
|
|
||||||
"accent_rgb": [195, 255, 102],
|
|
||||||
"passive_card_rgb": [42, 43, 46],
|
|
||||||
"active_card_rgb": [195, 255, 102]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
plane-src/apps/web/design.config.json
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
* See the LICENSE file for details.
|
* See the LICENSE file for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { CSSProperties, ReactNode } from "react";
|
||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
import { Links, Meta, Outlet, Scripts } from "react-router";
|
import { Links, Meta, Outlet, Scripts } from "react-router";
|
||||||
import type { LinksFunction } from "react-router";
|
import type { LinksFunction } from "react-router";
|
||||||
|
|
@ -22,6 +22,7 @@ import icon512 from "@/app/assets/icons/icon-512x512.png?url";
|
||||||
import ogImage from "@/app/assets/og-image.png?url";
|
import ogImage from "@/app/assets/og-image.png?url";
|
||||||
import globalStyles from "@/styles/globals.css?url";
|
import globalStyles from "@/styles/globals.css?url";
|
||||||
import type { Route } from "./+types/root";
|
import type { Route } from "./+types/root";
|
||||||
|
import designConfig from "../design.config.json";
|
||||||
// components
|
// components
|
||||||
import { LogoSpinner } from "@/components/common/logo-spinner";
|
import { LogoSpinner } from "@/components/common/logo-spinner";
|
||||||
// local
|
// local
|
||||||
|
|
@ -35,6 +36,12 @@ import "@fontsource/ibm-plex-mono";
|
||||||
|
|
||||||
const APP_TITLE = "NODE.DC | Self-hosted task management workspace.";
|
const APP_TITLE = "NODE.DC | Self-hosted task management workspace.";
|
||||||
|
|
||||||
|
const designConfigStyle = {
|
||||||
|
"--nodedc-accent-rgb": designConfig.nodedc.accent_rgb.join(" "),
|
||||||
|
"--nodedc-card-passive-rgb": designConfig.nodedc.passive_card_rgb.join(" "),
|
||||||
|
"--nodedc-card-active-rgb": designConfig.nodedc.active_card_rgb.join(" "),
|
||||||
|
} as CSSProperties;
|
||||||
|
|
||||||
export const links: LinksFunction = () => [
|
export const links: LinksFunction = () => [
|
||||||
{ rel: "icon", type: "image/png", sizes: "32x32", href: favicon32 },
|
{ rel: "icon", type: "image/png", sizes: "32x32", href: favicon32 },
|
||||||
{ rel: "icon", type: "image/png", sizes: "16x16", href: favicon16 },
|
{ rel: "icon", type: "image/png", sizes: "16x16", href: favicon16 },
|
||||||
|
|
@ -58,7 +65,7 @@ export function Layout({ children }: { children: ReactNode }) {
|
||||||
const isSessionRecorderEnabled = parseInt(process.env.VITE_ENABLE_SESSION_RECORDER || "0");
|
const isSessionRecorderEnabled = parseInt(process.env.VITE_ENABLE_SESSION_RECORDER || "0");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning style={designConfigStyle}>
|
||||||
<head>
|
<head>
|
||||||
<meta charSet="utf-8" />
|
<meta charSet="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export const EstimateListItemButtons = observer(function EstimateListItemButtons
|
||||||
return (
|
return (
|
||||||
<div className="relative flex items-center gap-1">
|
<div className="relative flex items-center gap-1">
|
||||||
<button
|
<button
|
||||||
className="relative flex h-6 w-6 flex-shrink-0 cursor-pointer items-center justify-center overflow-hidden rounded-sm transition-colors hover:bg-layer-1"
|
className="nodedc-settings-secondary-button relative flex h-10 w-10 min-h-0 min-w-0 flex-shrink-0 cursor-pointer items-center justify-center overflow-hidden rounded-[1rem] px-0 transition-colors"
|
||||||
onClick={() => onDeleteClick && onDeleteClick(estimateId)}
|
onClick={() => onDeleteClick && onDeleteClick(estimateId)}
|
||||||
data-ph-element={PROJECT_SETTINGS_TRACKER_ELEMENTS.ESTIMATES_LIST_ITEM}
|
data-ph-element={PROJECT_SETTINGS_TRACKER_ELEMENTS.ESTIMATES_LIST_ITEM}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ export const IssuesHeader = observer(function IssuesHeader() {
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
size="lg"
|
size="lg"
|
||||||
className="nodedc-toolbar-primary"
|
className="nodedc-toolbar-primary nodedc-toolbar-primary-wide"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
|
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ function ProjectAttributes(props: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { control } = useFormContext<IProject>();
|
const { control } = useFormContext<IProject>();
|
||||||
const { getIndex } = getTabIndex(ETabIndices.PROJECT_CREATE, isMobile);
|
const { getIndex } = getTabIndex(ETabIndices.PROJECT_CREATE, isMobile);
|
||||||
|
const projectAttributeChipClassName =
|
||||||
|
"nodedc-modal-chip !h-10 !rounded-[1.25rem] !px-4 !py-2 !text-13";
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<Controller
|
<Controller
|
||||||
|
|
@ -50,8 +52,8 @@ function ProjectAttributes(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
className="h-full"
|
className="h-10"
|
||||||
buttonClassName="h-full"
|
buttonClassName={projectAttributeChipClassName}
|
||||||
noChevron
|
noChevron
|
||||||
tabIndex={getIndex("network")}
|
tabIndex={getIndex("network")}
|
||||||
>
|
>
|
||||||
|
|
@ -84,6 +86,9 @@ function ProjectAttributes(props: Props) {
|
||||||
placeholder={t("lead")}
|
placeholder={t("lead")}
|
||||||
multiple={false}
|
multiple={false}
|
||||||
buttonVariant="border-with-text"
|
buttonVariant="border-with-text"
|
||||||
|
buttonClassName={projectAttributeChipClassName}
|
||||||
|
buttonContainerClassName="!h-10"
|
||||||
|
showUserDetails
|
||||||
tabIndex={getIndex("lead")}
|
tabIndex={getIndex("lead")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -167,8 +167,8 @@ export const CreateProjectForm = observer(function CreateProjectForm(props: TCre
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<ProjectCreateHeader handleClose={handleClose} isMobile={isMobile} />
|
<ProjectCreateHeader handleClose={handleClose} isMobile={isMobile} />
|
||||||
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="px-3">
|
<form onSubmit={handleSubmit(onSubmit)} className="px-6">
|
||||||
<div className="mt-9 space-y-6 pb-5">
|
<div className="mt-10 space-y-6 pb-6">
|
||||||
<ProjectCommonAttributes
|
<ProjectCommonAttributes
|
||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import { Button, getButtonStyling } from "@plane/propel/button";
|
||||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import { EFileAssetType } from "@plane/types";
|
import { EFileAssetType } from "@plane/types";
|
||||||
import { Input, Loader } from "@plane/ui";
|
import { Input, Loader } from "@plane/ui";
|
||||||
|
import { cn } from "@plane/utils";
|
||||||
// helpers
|
// helpers
|
||||||
import { STATIC_COVER_IMAGES, getCoverImageDisplayURL } from "@/helpers/cover-image.helper";
|
import { STATIC_COVER_IMAGES, getCoverImageDisplayURL } from "@/helpers/cover-image.helper";
|
||||||
// hooks
|
// hooks
|
||||||
|
|
@ -40,6 +41,7 @@ type Props = {
|
||||||
value: string | null;
|
value: string | null;
|
||||||
control: Control<any>;
|
control: Control<any>;
|
||||||
onChange: (data: string) => void;
|
onChange: (data: string) => void;
|
||||||
|
buttonClassName?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
tabIndex?: number;
|
tabIndex?: number;
|
||||||
isProfileCover?: boolean;
|
isProfileCover?: boolean;
|
||||||
|
|
@ -50,7 +52,17 @@ type Props = {
|
||||||
const fileService = new FileService();
|
const fileService = new FileService();
|
||||||
|
|
||||||
export const ImagePickerPopover = observer(function ImagePickerPopover(props: Props) {
|
export const ImagePickerPopover = observer(function ImagePickerPopover(props: Props) {
|
||||||
const { label, value, control, onChange, disabled = false, tabIndex, isProfileCover = false, projectId } = props;
|
const {
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
control,
|
||||||
|
onChange,
|
||||||
|
buttonClassName = "",
|
||||||
|
disabled = false,
|
||||||
|
tabIndex,
|
||||||
|
isProfileCover = false,
|
||||||
|
projectId,
|
||||||
|
} = props;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
// states
|
// states
|
||||||
const [image, setImage] = useState<File | null>(null);
|
const [image, setImage] = useState<File | null>(null);
|
||||||
|
|
@ -191,7 +203,11 @@ export const ImagePickerPopover = observer(function ImagePickerPopover(props: Pr
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover className="relative z-19" ref={ref} tabIndex={tabIndex} onKeyDown={handleKeyDown}>
|
<Popover className="relative z-19" ref={ref} tabIndex={tabIndex} onKeyDown={handleKeyDown}>
|
||||||
<Popover.Button className={getButtonStyling("secondary", "sm")} onClick={handleOnClick} disabled={disabled}>
|
<Popover.Button
|
||||||
|
className={cn(getButtonStyling("secondary", "sm"), buttonClassName)}
|
||||||
|
onClick={handleOnClick}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
{label}
|
{label}
|
||||||
</Popover.Button>
|
</Popover.Button>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { observer } from "mobx-react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
// plane imports
|
// plane imports
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
|
import { Button } from "@plane/propel/button";
|
||||||
// components
|
// components
|
||||||
import { SettingsBoxedControlItem } from "@/components/settings/boxed-control-item";
|
import { SettingsBoxedControlItem } from "@/components/settings/boxed-control-item";
|
||||||
import { SettingsHeading } from "@/components/settings/heading";
|
import { SettingsHeading } from "@/components/settings/heading";
|
||||||
|
|
@ -74,7 +75,7 @@ export const EstimateRoot = observer(function EstimateRoot(props: TEstimateRoot)
|
||||||
/>
|
/>
|
||||||
{/* active estimates section */}
|
{/* active estimates section */}
|
||||||
<div className="mt-12 flex flex-col gap-y-4">
|
<div className="mt-12 flex flex-col gap-y-4">
|
||||||
<SettingsHeading title="Estimates list" variant="h6" />
|
<SettingsHeading title={t("project_settings.estimates.list_heading")} variant="h6" />
|
||||||
<EstimateList
|
<EstimateList
|
||||||
estimateIds={[currentActiveEstimateId]}
|
estimateIds={[currentActiveEstimateId]}
|
||||||
isAdmin={isAdmin}
|
isAdmin={isAdmin}
|
||||||
|
|
@ -91,12 +92,15 @@ export const EstimateRoot = observer(function EstimateRoot(props: TEstimateRoot)
|
||||||
assetClassName="size-20"
|
assetClassName="size-20"
|
||||||
title={t("settings_empty_state.estimates.title")}
|
title={t("settings_empty_state.estimates.title")}
|
||||||
description={t("settings_empty_state.estimates.description")}
|
description={t("settings_empty_state.estimates.description")}
|
||||||
actions={[
|
customButton={
|
||||||
{
|
<Button
|
||||||
label: t("settings_empty_state.estimates.cta_primary"),
|
variant="ghost"
|
||||||
onClick: () => setIsEstimateCreateModalOpen(true),
|
className="nodedc-settings-primary-button"
|
||||||
},
|
onClick={() => setIsEstimateCreateModalOpen(true)}
|
||||||
]}
|
>
|
||||||
|
{t("settings_empty_state.estimates.cta_primary")}
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
align="start"
|
align="start"
|
||||||
rootClassName="py-20"
|
rootClassName="py-20"
|
||||||
/>
|
/>
|
||||||
|
|
@ -105,11 +109,10 @@ export const EstimateRoot = observer(function EstimateRoot(props: TEstimateRoot)
|
||||||
{archivedEstimateIds && archivedEstimateIds.length > 0 && (
|
{archivedEstimateIds && archivedEstimateIds.length > 0 && (
|
||||||
<div className="mt-12 flex flex-col gap-y-4">
|
<div className="mt-12 flex flex-col gap-y-4">
|
||||||
<SettingsHeading
|
<SettingsHeading
|
||||||
title="Archived estimates"
|
title={t("project_settings.estimates.archived_heading")}
|
||||||
description={
|
description={
|
||||||
<>
|
<>
|
||||||
Estimates have gone through a change, these are the estimates you had in your older versions which
|
{t("project_settings.estimates.archived_description")}
|
||||||
were not in use. Read more about them
|
|
||||||
<a
|
<a
|
||||||
href={"https://docs.plane.so/core-concepts/projects/run-project#estimate"}
|
href={"https://docs.plane.so/core-concepts/projects/run-project#estimate"}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|
|
||||||
|
|
@ -74,16 +74,16 @@ export const DeleteInboxIssueModal = observer(function DeleteInboxIssueModal({
|
||||||
isSubmitting={isDeleting}
|
isSubmitting={isDeleting}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
title={t("inbox_issue.modals.delete.title")}
|
title={t("inbox_issue.modals.delete.title")}
|
||||||
// TODO: Need to translate the confirmation message
|
|
||||||
content={
|
content={
|
||||||
<>
|
t("inbox_issue.modals.delete.content", {
|
||||||
Are you sure you want to delete work item{" "}
|
value: `${projectDetails?.identifier}-${data?.sequence_id}`,
|
||||||
<span className="font-medium break-words text-primary">
|
})
|
||||||
{projectDetails?.identifier}-{data?.sequence_id}
|
|
||||||
</span>
|
|
||||||
{""}? The work item will only be deleted from the intake and this action cannot be undone.
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
|
primaryButtonText={{
|
||||||
|
loading: t("deleting"),
|
||||||
|
default: t("delete"),
|
||||||
|
}}
|
||||||
|
secondaryButtonText={t("cancel")}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -119,14 +119,17 @@ export const DeleteIssueModal = observer(function DeleteIssueModal(props: Props)
|
||||||
title={t("entity.delete.label", { entity: isEpic ? t("common.epic") : t("common.work_item") })}
|
title={t("entity.delete.label", { entity: isEpic ? t("common.epic") : t("common.work_item") })}
|
||||||
content={
|
content={
|
||||||
<>
|
<>
|
||||||
{/* TODO: Translate here */}
|
{t("entity.delete.confirmation", {
|
||||||
{`Are you sure you want to delete ${isEpic ? "epic" : "work item"} `}
|
entity: isEpic ? t("common.epic") : t("common.work_item"),
|
||||||
<span className="font-medium break-words text-primary">
|
identifier: `${projectDetails?.identifier}-${issue?.sequence_id}`,
|
||||||
{projectDetails?.identifier}-{issue?.sequence_id}
|
})}
|
||||||
</span>
|
|
||||||
{` ? All of the data related to the ${isEpic ? "epic" : "work item"} will be permanently removed. This action cannot be undone.`}
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
primaryButtonText={{
|
||||||
|
loading: t("deleting"),
|
||||||
|
default: t("delete"),
|
||||||
|
}}
|
||||||
|
secondaryButtonText={t("cancel")}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ export const HeaderFilters = observer(function HeaderFilters(props: Props) {
|
||||||
</FiltersDropdown>
|
</FiltersDropdown>
|
||||||
{canUserCreateIssue ? (
|
{canUserCreateIssue ? (
|
||||||
<Button
|
<Button
|
||||||
className="nodedc-toolbar-pill hidden md:inline-flex"
|
className="nodedc-toolbar-pill nodedc-toolbar-pill-wide hidden md:inline-flex"
|
||||||
onClick={() => setAnalyticsModal(true)}
|
onClick={() => setAnalyticsModal(true)}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="lg"
|
size="lg"
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ export function FiltersDropdown(props: Props) {
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
prependIcon={icon}
|
prependIcon={icon}
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
className="nodedc-toolbar-pill relative"
|
className="nodedc-toolbar-pill nodedc-toolbar-pill-wide relative"
|
||||||
data-active={open}
|
data-active={open}
|
||||||
size="lg"
|
size="lg"
|
||||||
>
|
>
|
||||||
|
|
@ -81,7 +81,7 @@ export function FiltersDropdown(props: Props) {
|
||||||
ref={setReferenceElement}
|
ref={setReferenceElement}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
tabIndex={tabIndex}
|
tabIndex={tabIndex}
|
||||||
className="nodedc-toolbar-pill"
|
className="nodedc-toolbar-pill nodedc-toolbar-pill-wide"
|
||||||
size="lg"
|
size="lg"
|
||||||
>
|
>
|
||||||
{miniIcon || title}
|
{miniIcon || title}
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,10 @@ export function GroupDragOverlay(props: Props) {
|
||||||
<div
|
<div
|
||||||
ref={messageContainerRef}
|
ref={messageContainerRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
`absolute top-0 left-0 h-full w-full items-center rounded-sm bg-layer-1/85 text-13 font-medium text-tertiary ${dragColumnOrientation}`,
|
`absolute top-0 left-0 h-full w-full items-center rounded-[1.5rem] bg-layer-1/85 text-13 font-medium text-tertiary ${dragColumnOrientation}`,
|
||||||
{
|
{
|
||||||
"z-2 flex flex-col border-[1px] border-strong": shouldOverlayBeVisible,
|
"z-2 flex flex-col border border-[rgb(var(--nodedc-accent-rgb))]/55 bg-black/35": shouldOverlayBeVisible,
|
||||||
"bg-danger-subtle": workflowDisabledSource && isDropDisabled,
|
"bg-black/40": workflowDisabledSource && isDropDisabled,
|
||||||
},
|
},
|
||||||
{ hidden: !shouldOverlayBeVisible }
|
{ hidden: !shouldOverlayBeVisible }
|
||||||
)}
|
)}
|
||||||
|
|
@ -67,14 +67,13 @@ export function GroupDragOverlay(props: Props) {
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={cn("my-8 flex flex-col items-center rounded-sm p-3", {
|
className={cn(
|
||||||
"text-secondary": shouldOverlayBeVisible,
|
"my-8 flex max-w-[20rem] flex-col items-center rounded-[1.25rem] px-4 py-3 text-center text-secondary"
|
||||||
"text-danger-secondary": isDropDisabled,
|
)}
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
{dropErrorMessage ? (
|
{dropErrorMessage ? (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<AlertCircle width={13} height={13} />
|
<AlertCircle width={13} height={13} className="text-[rgb(var(--nodedc-accent-rgb))]" />
|
||||||
<span>{dropErrorMessage}</span>
|
<span>{dropErrorMessage}</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -84,7 +83,7 @@ export function GroupDragOverlay(props: Props) {
|
||||||
{t("issue.layouts.ordered_by_label")} <span className="font-semibold">{t(readableOrderBy)}</span>.
|
{t("issue.layouts.ordered_by_label")} <span className="font-semibold">{t(readableOrderBy)}</span>.
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<span>{t("entity.drop_here_to_move", { entity: isEpic ? "epic" : "work item" })}</span>
|
<span>{t("entity.drop_here_to_move", { entity: t(isEpic ? "common.epic" : "common.work_item") })}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { autoScrollForElements } from "@atlaskit/pragmatic-drag-and-drop-auto-sc
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { EIssueFilterType, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
import { EIssueFilterType, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
import type { EIssuesStoreType } from "@plane/types";
|
import type { EIssuesStoreType } from "@plane/types";
|
||||||
import { EIssueServiceType, EIssueLayoutTypes } from "@plane/types";
|
import { EIssueServiceType, EIssueLayoutTypes } from "@plane/types";
|
||||||
//hooks
|
//hooks
|
||||||
|
|
@ -89,6 +90,7 @@ export const BaseKanBanRoot = observer(function BaseKanBanRoot(props: IBaseKanBa
|
||||||
const [isDragOverDelete, setIsDragOverDelete] = useState(false);
|
const [isDragOverDelete, setIsDragOverDelete] = useState(false);
|
||||||
|
|
||||||
const { isDragging } = useKanbanView();
|
const { isDragging } = useKanbanView();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const displayFilters = issuesFilter?.issueFilters?.displayFilters;
|
const displayFilters = issuesFilter?.issueFilters?.displayFilters;
|
||||||
const displayProperties = issuesFilter?.issueFilters?.displayProperties;
|
const displayProperties = issuesFilter?.issueFilters?.displayProperties;
|
||||||
|
|
@ -249,17 +251,17 @@ export const BaseKanBanRoot = observer(function BaseKanBanRoot(props: IBaseKanBa
|
||||||
<div
|
<div
|
||||||
className={`fixed left-1/2 -translate-x-1/2 ${
|
className={`fixed left-1/2 -translate-x-1/2 ${
|
||||||
isDragging ? "z-40" : ""
|
isDragging ? "z-40" : ""
|
||||||
} top-3 mx-3 flex w-72 items-center justify-center`}
|
} top-3 mx-3 flex w-[min(40rem,calc(100vw-2rem))] items-center justify-center`}
|
||||||
ref={deleteAreaRef}
|
ref={deleteAreaRef}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
isDragging ? `opacity-100` : `opacity-0`
|
isDragging ? `opacity-100` : `opacity-0`
|
||||||
} flex w-full items-center justify-center rounded-sm border-2 border-danger-strong/20 bg-surface-1 px-3 py-5 text-11 font-medium text-danger-primary italic ${
|
} nodedc-glass-popup-surface flex w-full items-center justify-center rounded-[1.5rem] border-2 border-[rgb(var(--nodedc-accent-rgb))]/80 px-6 py-4 text-center text-[13px] font-medium text-secondary ring-1 ring-[rgb(var(--nodedc-accent-rgb))]/20 ${
|
||||||
isDragOverDelete ? "bg-danger-primary blur-2xl" : ""
|
isDragOverDelete ? "border-[rgb(var(--nodedc-accent-rgb))] bg-white/6 ring-[rgb(var(--nodedc-accent-rgb))]/35" : ""
|
||||||
} transition duration-300`}
|
} transition duration-300`}
|
||||||
>
|
>
|
||||||
Drop here to delete the work item.
|
{t("entity.drop_here_to_delete", { entity: t("common.work_item") })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<IssueLayoutHOC layout={EIssueLayoutTypes.KANBAN}>
|
<IssueLayoutHOC layout={EIssueLayoutTypes.KANBAN}>
|
||||||
|
|
|
||||||
|
|
@ -415,9 +415,9 @@ export const CreateUpdateIssueModalBase = observer(function CreateUpdateIssueMod
|
||||||
return (
|
return (
|
||||||
<ModalCore
|
<ModalCore
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
position={EModalPosition.TOP}
|
position={EModalPosition.CENTER}
|
||||||
width={isDuplicateModalOpen ? EModalWidth.VIXL : EModalWidth.XXXXL}
|
width={isDuplicateModalOpen ? EModalWidth.VIXL : EModalWidth.XXXXL}
|
||||||
className="rounded-lg !bg-transparent shadow-none transition-[width] ease-linear"
|
className="transition-[width] ease-linear"
|
||||||
>
|
>
|
||||||
{withDraftIssueWrapper ? (
|
{withDraftIssueWrapper ? (
|
||||||
<DraftIssueLayout {...commonIssueModalProps} changesMade={changesMade} onChange={handleFormChange} />
|
<DraftIssueLayout {...commonIssueModalProps} changesMade={changesMade} onChange={handleFormChange} />
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ export const IssueDescriptionEditor = observer(function IssueDescriptionEditor(p
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative rounded-lg border-[0.5px] border-subtle-1 bg-layer-2">
|
<div className="nodedc-modal-editor relative min-h-[180px]">
|
||||||
{descriptionHtmlData === undefined || !projectId ? (
|
{descriptionHtmlData === undefined || !projectId ? (
|
||||||
<Loader className="max-h-64 min-h-[120px] space-y-2 overflow-hidden rounded-md border border-subtle p-3 py-2 pt-3">
|
<Loader className="max-h-64 min-h-[120px] space-y-2 overflow-hidden rounded-md border border-subtle p-3 py-2 pt-3">
|
||||||
<Loader.Item width="100%" height="26px" />
|
<Loader.Item width="100%" height="26px" />
|
||||||
|
|
@ -203,7 +203,7 @@ export const IssueDescriptionEditor = observer(function IssueDescriptionEditor(p
|
||||||
project_id: projectId?.toString() ?? "",
|
project_id: projectId?.toString() ?? "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
containerClassName="pt-3 min-h-[120px]"
|
containerClassName="min-h-[180px] !border-none !bg-transparent !p-0"
|
||||||
uploadFile={async (blockId, file) => {
|
uploadFile={async (blockId, file) => {
|
||||||
try {
|
try {
|
||||||
const { asset_id } = await uploadEditorAsset({
|
const { asset_id } = await uploadEditorAsset({
|
||||||
|
|
@ -243,11 +243,11 @@ export const IssueDescriptionEditor = observer(function IssueDescriptionEditor(p
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="border-0.5 z-10 flex items-center justify-end gap-2 p-3">
|
<div className="z-10 flex items-center justify-end gap-2 px-4 pb-4">
|
||||||
{issueName && issueName.trim() !== "" && config?.has_llm_configured && (
|
{issueName && issueName.trim() !== "" && config?.has_llm_configured && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`flex items-center gap-1 rounded-sm bg-surface-2 px-1.5 py-1 text-caption-sm-regular hover:bg-layer-1 ${
|
className={`flex items-center gap-1 rounded-[1rem] bg-white/6 px-2.5 py-1.5 text-caption-sm-regular text-secondary transition-colors hover:bg-white/10 ${
|
||||||
iAmFeelingLucky ? "cursor-wait" : ""
|
iAmFeelingLucky ? "cursor-wait" : ""
|
||||||
}`}
|
}`}
|
||||||
onClick={handleAutoGenerateDescription}
|
onClick={handleAutoGenerateDescription}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ export const IssueTitleInput = observer(function IssueTitleInput(props: TIssueTi
|
||||||
ref={issueTitleRef || ref}
|
ref={issueTitleRef || ref}
|
||||||
hasError={Boolean(errors.name)}
|
hasError={Boolean(errors.name)}
|
||||||
placeholder={t("title")}
|
placeholder={t("title")}
|
||||||
className="w-full text-body-sm-regular"
|
className="nodedc-modal-input w-full !px-4 !py-3 !text-[15px]"
|
||||||
autoFocus
|
autoFocus
|
||||||
tabIndex={getIndex("name")}
|
tabIndex={getIndex("name")}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -376,15 +376,15 @@ export const IssueFormRoot = observer(function IssueFormRoot(props: IssueFormPro
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<div className="flex gap-2 bg-transparent">
|
<div className="nodedc-work-item-modal flex gap-3 bg-transparent">
|
||||||
<div className="w-full rounded-lg">
|
<div className="w-full">
|
||||||
<form
|
<form
|
||||||
ref={formRef}
|
ref={formRef}
|
||||||
onSubmit={handleSubmit((data) => handleFormSubmit(data))}
|
onSubmit={handleSubmit((data) => handleFormSubmit(data))}
|
||||||
className="flex w-full flex-col"
|
className="flex w-full flex-col"
|
||||||
>
|
>
|
||||||
<div className="rounded-t-lg bg-surface-1 p-5">
|
<div className="px-6 pt-6 pb-2">
|
||||||
<h3 className="pb-2 text-h4-medium text-secondary">{modalTitle}</h3>
|
<h3 className="pb-2 text-18 font-medium text-secondary">{modalTitle}</h3>
|
||||||
<div className="flex items-center justify-between pt-2 pb-4">
|
<div className="flex items-center justify-between pt-2 pb-4">
|
||||||
<div className="flex items-center gap-x-1">
|
<div className="flex items-center gap-x-1">
|
||||||
<IssueProjectSelect
|
<IssueProjectSelect
|
||||||
|
|
@ -452,7 +452,7 @@ export const IssueFormRoot = observer(function IssueFormRoot(props: IssueFormPro
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"space-y-3 bg-surface-1 pb-4",
|
"space-y-4 bg-transparent pb-4",
|
||||||
activeAdditionalPropertiesLength > 4 &&
|
activeAdditionalPropertiesLength > 4 &&
|
||||||
"vertical-scrollbar scrollbar-sm max-h-[45vh] overflow-hidden overflow-y-auto"
|
"vertical-scrollbar scrollbar-sm max-h-[45vh] overflow-hidden overflow-y-auto"
|
||||||
)}
|
)}
|
||||||
|
|
@ -486,12 +486,7 @@ export const IssueFormRoot = observer(function IssueFormRoot(props: IssueFormPro
|
||||||
workspaceSlug={workspaceSlug?.toString()}
|
workspaceSlug={workspaceSlug?.toString()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div className={cn("border-t border-subtle/70 bg-transparent px-6 py-4")}>
|
||||||
className={cn(
|
|
||||||
"rounded-b-lg border-t-[0.5px] border-subtle bg-surface-1 px-4 py-3",
|
|
||||||
activeAdditionalPropertiesLength > 0 && "shadow-raised-100"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="pb-3">
|
<div className="pb-3">
|
||||||
<IssueDefaultProperties
|
<IssueDefaultProperties
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -509,7 +504,7 @@ export const IssueFormRoot = observer(function IssueFormRoot(props: IssueFormPro
|
||||||
</div>
|
</div>
|
||||||
{showActionButtons && (
|
{showActionButtons && (
|
||||||
<div
|
<div
|
||||||
className="flex items-center justify-end gap-4 border-t-[0.5px] border-subtle pt-6 pb-3"
|
className="flex items-center justify-end gap-4 border-t border-subtle/70 pt-6 pb-2"
|
||||||
tabIndex={getIndex("create_more")}
|
tabIndex={getIndex("create_more")}
|
||||||
>
|
>
|
||||||
{!data?.id && (
|
{!data?.id && (
|
||||||
|
|
@ -530,6 +525,7 @@ export const IssueFormRoot = observer(function IssueFormRoot(props: IssueFormPro
|
||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="lg"
|
size="lg"
|
||||||
|
className="nodedc-modal-secondary-button min-w-[8.25rem]"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (editorRef.current?.isEditorReadyToDiscard()) {
|
if (editorRef.current?.isEditorReadyToDiscard()) {
|
||||||
onClose();
|
onClose();
|
||||||
|
|
@ -553,6 +549,10 @@ export const IssueFormRoot = observer(function IssueFormRoot(props: IssueFormPro
|
||||||
ref={submitBtnRef}
|
ref={submitBtnRef}
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
|
className={cn(
|
||||||
|
"min-w-[8.25rem]",
|
||||||
|
moveToIssue ? "nodedc-modal-secondary-button" : "nodedc-modal-primary-button"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{isSubmitting ? primaryButtonText.loading : primaryButtonText.default}
|
{isSubmitting ? primaryButtonText.loading : primaryButtonText.default}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -566,6 +566,7 @@ export const IssueFormRoot = observer(function IssueFormRoot(props: IssueFormPro
|
||||||
onClick={handleMoveToProjects}
|
onClick={handleMoveToProjects}
|
||||||
disabled={isMoving}
|
disabled={isMoving}
|
||||||
size="lg"
|
size="lg"
|
||||||
|
className="nodedc-modal-primary-button min-w-[8.25rem]"
|
||||||
>
|
>
|
||||||
{t("add_to_project")}
|
{t("add_to_project")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -579,7 +580,7 @@ export const IssueFormRoot = observer(function IssueFormRoot(props: IssueFormPro
|
||||||
{shouldRenderDuplicateModal && (
|
{shouldRenderDuplicateModal && (
|
||||||
<div
|
<div
|
||||||
ref={modalContainerRef}
|
ref={modalContainerRef}
|
||||||
className="shadow-xl bg-pi-50 relative flex flex-col gap-2.5 rounded-lg px-3 py-4"
|
className="nodedc-glass-surface relative flex flex-col gap-2.5 rounded-[1.75rem] px-4 py-4"
|
||||||
style={{ maxHeight: formRef?.current?.offsetHeight ? `${formRef.current.offsetHeight}px` : "436px" }}
|
style={{ maxHeight: formRef?.current?.offsetHeight ? `${formRef.current.offsetHeight}px` : "436px" }}
|
||||||
>
|
>
|
||||||
<DuplicateModalRoot
|
<DuplicateModalRoot
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ export const CreateUpdateLabelInline = observer(
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={`flex w-full scroll-m-8 items-center gap-2 bg-surface-1 ${labelForm ? "" : "hidden"}`}
|
className={`flex w-full scroll-m-8 items-center gap-2 ${labelForm ? "" : "hidden"}`}
|
||||||
>
|
>
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<Popover className="relative z-10 flex h-full w-full items-center justify-center">
|
<Popover className="relative z-10 flex h-full w-full items-center justify-center">
|
||||||
|
|
@ -229,21 +229,22 @@ export const CreateUpdateLabelInline = observer(
|
||||||
ref={ref}
|
ref={ref}
|
||||||
hasError={Boolean(errors.name)}
|
hasError={Boolean(errors.name)}
|
||||||
placeholder={t("project_settings.labels.label_title")}
|
placeholder={t("project_settings.labels.label_title")}
|
||||||
className="w-full"
|
className="nodedc-settings-input w-full"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="secondary" onClick={() => handleClose()}>
|
<Button variant="ghost" onClick={() => handleClose()} className="nodedc-settings-secondary-button">
|
||||||
{t("cancel")}
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="ghost"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleSubmit(handleFormSubmit)();
|
handleSubmit(handleFormSubmit)();
|
||||||
}}
|
}}
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
|
className="nodedc-settings-primary-button"
|
||||||
>
|
>
|
||||||
{isUpdating ? (isSubmitting ? t("updating") : t("update")) : isSubmitting ? t("adding") : t("add")}
|
{isUpdating ? (isSubmitting ? t("updating") : t("update")) : isSubmitting ? t("adding") : t("add")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export function LabelDragPreview(props: LabelDragPreviewProps) {
|
||||||
const { label, isGroup } = props;
|
const { label, isGroup } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-[1px] border-subtle bg-surface-1 py-3 pr-4 pl-2">
|
<div className="nodedc-settings-card rounded-[1.25rem] py-3 pr-4 pl-2">
|
||||||
<LabelName name={label.name} color={label.color} isGroup={isGroup} />
|
<LabelName name={label.name} color={label.color} isGroup={isGroup} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -78,13 +78,11 @@ export const ProjectSettingLabelGroup = observer(function ProjectSettingLabelGro
|
||||||
<LabelDndHOC label={label} isGroup isChild={false} isLastChild={isLastChild} onDrop={onDrop}>
|
<LabelDndHOC label={label} isGroup isChild={false} isLastChild={isLastChild} onDrop={onDrop}>
|
||||||
{(isDragging, isDroppingInLabel, dragHandleRef) => (
|
{(isDragging, isDroppingInLabel, dragHandleRef) => (
|
||||||
<div
|
<div
|
||||||
className={`rounded-sm ${isDroppingInLabel ? "border-[2px] border-accent-strong" : "border-[1.5px] border-transparent"}`}
|
className={`rounded-[1.25rem] ${isDroppingInLabel ? "border-[2px] border-accent-strong" : "border-[1.5px] border-transparent"}`}
|
||||||
>
|
>
|
||||||
<Disclosure
|
<Disclosure
|
||||||
as="div"
|
as="div"
|
||||||
className={`rounded-sm text-primary ${
|
className={`nodedc-settings-card rounded-[1.25rem] text-primary ${isDragging ? "bg-layer-1" : "bg-surface-1"}`}
|
||||||
!isDroppingInLabel ? "border-[0.5px] border-subtle" : ""
|
|
||||||
} ${isDragging ? "bg-layer-1" : "bg-surface-1"}`}
|
|
||||||
defaultOpen
|
defaultOpen
|
||||||
>
|
>
|
||||||
{({ open }) => (
|
{({ open }) => (
|
||||||
|
|
|
||||||
|
|
@ -87,12 +87,12 @@ export function ProjectSettingLabelItem(props: Props) {
|
||||||
<LabelDndHOC label={label} isGroup={false} isChild={isChild} isLastChild={isLastChild} onDrop={onDrop}>
|
<LabelDndHOC label={label} isGroup={false} isChild={isChild} isLastChild={isLastChild} onDrop={onDrop}>
|
||||||
{(isDragging, isDroppingInLabel, dragHandleRef) => (
|
{(isDragging, isDroppingInLabel, dragHandleRef) => (
|
||||||
<div
|
<div
|
||||||
className={`rounded-sm ${isDroppingInLabel ? "border-[2px] border-accent-strong" : "border-[1.5px] border-transparent"}`}
|
className={`rounded-[1.25rem] ${isDroppingInLabel ? "border-[2px] border-accent-strong" : "border-[1.5px] border-transparent"}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`group relative flex items-center justify-between gap-2 space-y-3 rounded-sm px-1 py-3 ${
|
className={`nodedc-settings-card group relative flex items-center justify-between gap-2 space-y-3 rounded-[1.25rem] px-3 py-3 ${
|
||||||
isDroppingInLabel ? "" : "border-[0.5px] border-subtle"
|
isDragging || isParentDragging ? "bg-layer-1" : "bg-surface-1"
|
||||||
} ${isDragging || isParentDragging ? "bg-layer-1" : "bg-surface-1"}`}
|
}`}
|
||||||
>
|
>
|
||||||
{isEditLabelForm ? (
|
{isEditLabelForm ? (
|
||||||
<CreateUpdateLabelInline
|
<CreateUpdateLabelInline
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ export const ProjectSettingsLabelList = observer(function ProjectSettingsLabelLi
|
||||||
description={t("project_settings.labels.description")}
|
description={t("project_settings.labels.description")}
|
||||||
control={
|
control={
|
||||||
isEditable && (
|
isEditable && (
|
||||||
<Button variant="primary" size="lg" onClick={newLabel}>
|
<Button variant="ghost" size="lg" onClick={newLabel} className="nodedc-settings-primary-button min-w-[11rem]">
|
||||||
{t("common.add_label")}
|
{t("common.add_label")}
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
|
|
@ -93,7 +93,7 @@ export const ProjectSettingsLabelList = observer(function ProjectSettingsLabelLi
|
||||||
/>
|
/>
|
||||||
<div className="mt-6 w-full">
|
<div className="mt-6 w-full">
|
||||||
{showLabelForm && (
|
{showLabelForm && (
|
||||||
<div className="my-2 w-full rounded-sm border border-subtle px-3.5 py-2">
|
<div className="nodedc-settings-card my-2 w-full px-4 py-3">
|
||||||
<CreateUpdateLabelInline
|
<CreateUpdateLabelInline
|
||||||
labelForm={showLabelForm}
|
labelForm={showLabelForm}
|
||||||
setLabelForm={setLabelForm}
|
setLabelForm={setLabelForm}
|
||||||
|
|
@ -114,14 +114,11 @@ export const ProjectSettingsLabelList = observer(function ProjectSettingsLabelLi
|
||||||
assetClassName="size-20"
|
assetClassName="size-20"
|
||||||
title={t("settings_empty_state.labels.title")}
|
title={t("settings_empty_state.labels.title")}
|
||||||
description={t("settings_empty_state.labels.description")}
|
description={t("settings_empty_state.labels.description")}
|
||||||
actions={[
|
customButton={
|
||||||
{
|
<Button variant="ghost" className="nodedc-settings-primary-button" onClick={newLabel}>
|
||||||
label: t("settings_empty_state.labels.cta_primary"),
|
{t("settings_empty_state.labels.cta_primary")}
|
||||||
onClick: () => {
|
</Button>
|
||||||
newLabel();
|
}
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
align="start"
|
align="start"
|
||||||
rootClassName="py-20"
|
rootClassName="py-20"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ type TStateForm = {
|
||||||
function PopoverButton({ color }: { color?: string }) {
|
function PopoverButton({ color }: { color?: string }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="group inline-flex h-5 w-5 items-center rounded-sm text-14 font-medium transition-all focus:outline-none"
|
className="group inline-flex h-5 w-5 items-center rounded-full text-14 font-medium transition-all focus:outline-none"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: color ?? "black",
|
backgroundColor: color ?? "black",
|
||||||
}}
|
}}
|
||||||
|
|
@ -65,7 +65,7 @@ export function StateForm(props: TStateForm) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex space-x-2 rounded-sm bg-surface-1 p-3">
|
<div className="nodedc-settings-card relative flex space-x-2 rounded-[1.25rem] p-3">
|
||||||
{/* color */}
|
{/* color */}
|
||||||
<div className="mt-2 h-full flex-shrink-0">
|
<div className="mt-2 h-full flex-shrink-0">
|
||||||
<Popover button={<PopoverButton color={formData?.color} />} panelClassName="mt-4 -ml-3">
|
<Popover button={<PopoverButton color={formData?.color} />} panelClassName="mt-4 -ml-3">
|
||||||
|
|
@ -83,7 +83,7 @@ export function StateForm(props: TStateForm) {
|
||||||
value={formData?.name}
|
value={formData?.name}
|
||||||
onChange={(e) => handleFormData("name", e.target.value)}
|
onChange={(e) => handleFormData("name", e.target.value)}
|
||||||
hasError={(errors && Boolean(errors.name)) || false}
|
hasError={(errors && Boolean(errors.name)) || false}
|
||||||
className="w-full"
|
className="nodedc-settings-input w-full"
|
||||||
maxLength={100}
|
maxLength={100}
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
|
|
@ -96,14 +96,27 @@ export function StateForm(props: TStateForm) {
|
||||||
value={formData?.description}
|
value={formData?.description}
|
||||||
onChange={(e) => handleFormData("description", e.target.value)}
|
onChange={(e) => handleFormData("description", e.target.value)}
|
||||||
hasError={(errors && Boolean(errors.description)) || false}
|
hasError={(errors && Boolean(errors.description)) || false}
|
||||||
className="min-h-14 w-full resize-none text-13"
|
className="nodedc-settings-input min-h-14 w-full resize-none text-13"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Button onClick={formSubmit} variant="primary" size="lg" disabled={buttonDisabled}>
|
<Button
|
||||||
|
onClick={formSubmit}
|
||||||
|
variant="ghost"
|
||||||
|
size="lg"
|
||||||
|
disabled={buttonDisabled}
|
||||||
|
className="nodedc-settings-primary-button"
|
||||||
|
>
|
||||||
{buttonTitle}
|
{buttonTitle}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="button" variant="secondary" size="lg" disabled={buttonDisabled} onClick={onCancel}>
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="lg"
|
||||||
|
disabled={buttonDisabled}
|
||||||
|
onClick={onCancel}
|
||||||
|
className="nodedc-settings-secondary-button"
|
||||||
|
>
|
||||||
{t("cancel")}
|
{t("cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ export const GroupItem = observer(function GroupItem(props: TGroupItem) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn("space-y-1 rounded-sm border border-subtle bg-surface-2 p-2 transition-all", groupItemClassName)}
|
className={cn("nodedc-settings-card space-y-1 rounded-[1.35rem] p-3 transition-all", groupItemClassName)}
|
||||||
ref={dropElementRef}
|
ref={dropElementRef}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
|
|
@ -68,6 +68,7 @@ export const GroupItem = observer(function GroupItem(props: TGroupItem) {
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-5 w-5 flex-shrink-0 items-center justify-center overflow-hidden rounded-sm transition-all",
|
"flex h-5 w-5 flex-shrink-0 items-center justify-center overflow-hidden rounded-sm transition-all",
|
||||||
|
"rounded-full",
|
||||||
{
|
{
|
||||||
"rotate-0": currentStateExpanded,
|
"rotate-0": currentStateExpanded,
|
||||||
"-rotate-90": !currentStateExpanded,
|
"-rotate-90": !currentStateExpanded,
|
||||||
|
|
@ -76,7 +77,7 @@ export const GroupItem = observer(function GroupItem(props: TGroupItem) {
|
||||||
>
|
>
|
||||||
<ChevronDownIcon className="h-4 w-4" />
|
<ChevronDownIcon className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-6 w-6 flex-shrink-0 items-center justify-center overflow-hidden rounded-sm">
|
<div className="flex h-6 w-6 flex-shrink-0 items-center justify-center overflow-hidden rounded-full">
|
||||||
<StateGroupIcon stateGroup={groupKey} size={EIconSize.XL} />
|
<StateGroupIcon stateGroup={groupKey} size={EIconSize.XL} />
|
||||||
</div>
|
</div>
|
||||||
<div className="px-1 text-14 font-medium text-secondary">{translatedGroupKey}</div>
|
<div className="px-1 text-14 font-medium text-secondary">{translatedGroupKey}</div>
|
||||||
|
|
@ -86,6 +87,7 @@ export const GroupItem = observer(function GroupItem(props: TGroupItem) {
|
||||||
data-ph-element={STATE_TRACKER_ELEMENTS.STATE_GROUP_ADD_BUTTON}
|
data-ph-element={STATE_TRACKER_ELEMENTS.STATE_GROUP_ADD_BUTTON}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-6 w-6 flex-shrink-0 cursor-pointer items-center justify-center overflow-hidden rounded-sm text-accent-primary/80 transition-colors hover:bg-layer-1 hover:text-accent-primary",
|
"flex h-6 w-6 flex-shrink-0 cursor-pointer items-center justify-center overflow-hidden rounded-sm text-accent-primary/80 transition-colors hover:bg-layer-1 hover:text-accent-primary",
|
||||||
|
"rounded-full hover:bg-white/6",
|
||||||
(!isEditable || createState) && "cursor-not-allowed text-placeholder hover:text-placeholder"
|
(!isEditable || createState) && "cursor-not-allowed text-placeholder hover:text-placeholder"
|
||||||
)}
|
)}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ export const StateItem = observer(function StateItem(props: TStateItem) {
|
||||||
<div
|
<div
|
||||||
ref={draggableElementRef}
|
ref={draggableElementRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
"group relative rounded-sm border border-subtle bg-surface-1 px-3.5 py-3",
|
"nodedc-settings-field group relative rounded-[1.2rem] px-3.5 py-3",
|
||||||
isDragging ? `opacity-50` : `opacity-100`,
|
isDragging ? `opacity-50` : `opacity-100`,
|
||||||
totalStates === 1 ? `cursor-auto` : `cursor-grab`,
|
totalStates === 1 ? `cursor-auto` : `cursor-grab`,
|
||||||
stateItemClassName
|
stateItemClassName
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,12 @@ export function CreateProjectModal(props: Props) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalCore isOpen={isOpen} position={EModalPosition.TOP} width={EModalWidth.XXXXL}>
|
<ModalCore
|
||||||
|
isOpen={isOpen}
|
||||||
|
position={EModalPosition.CENTER}
|
||||||
|
width={EModalWidth.XXXXL}
|
||||||
|
className="overflow-hidden"
|
||||||
|
>
|
||||||
{currentStep === EProjectCreationSteps.CREATE_PROJECT && (
|
{currentStep === EProjectCreationSteps.CREATE_PROJECT && (
|
||||||
<CreateProjectForm
|
<CreateProjectForm
|
||||||
setToFavorite={setToFavorite}
|
setToFavorite={setToFavorite}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ function ProjectCommonAttributes(props: Props) {
|
||||||
onChange={handleNameChange(onChange)}
|
onChange={handleNameChange(onChange)}
|
||||||
hasError={Boolean(errors.name)}
|
hasError={Boolean(errors.name)}
|
||||||
placeholder={t("project_name")}
|
placeholder={t("project_name")}
|
||||||
className="focus:border-blue-400 w-full"
|
className="nodedc-modal-input w-full text-13"
|
||||||
tabIndex={getIndex("name")}
|
tabIndex={getIndex("name")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -113,7 +113,7 @@ function ProjectCommonAttributes(props: Props) {
|
||||||
onChange={handleIdentifierChange(onChange)}
|
onChange={handleIdentifierChange(onChange)}
|
||||||
hasError={Boolean(errors.identifier)}
|
hasError={Boolean(errors.identifier)}
|
||||||
placeholder={t("project_id")}
|
placeholder={t("project_id")}
|
||||||
className={cn("focus:border-blue-400 w-full pr-7 text-11", {
|
className={cn("nodedc-modal-input w-full pr-7 text-12", {
|
||||||
uppercase: value,
|
uppercase: value,
|
||||||
})}
|
})}
|
||||||
tabIndex={getIndex("identifier")}
|
tabIndex={getIndex("identifier")}
|
||||||
|
|
@ -144,7 +144,7 @@ function ProjectCommonAttributes(props: Props) {
|
||||||
onChange(e);
|
onChange(e);
|
||||||
handleFormOnChange?.();
|
handleFormOnChange?.();
|
||||||
}}
|
}}
|
||||||
className="focus:border-blue-400 !h-24 text-13"
|
className="nodedc-modal-input !h-24 text-13"
|
||||||
hasError={Boolean(errors?.description)}
|
hasError={Boolean(errors?.description)}
|
||||||
tabIndex={getIndex("description")}
|
tabIndex={getIndex("description")}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,11 @@ function ProjectCreateHeader(props: Props) {
|
||||||
const { getIndex } = getTabIndex(ETabIndices.PROJECT_CREATE, isMobile);
|
const { getIndex } = getTabIndex(ETabIndices.PROJECT_CREATE, isMobile);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group relative h-44 w-full rounded-lg">
|
<div className="group relative h-44 w-full rounded-[1.75rem]">
|
||||||
<CoverImage
|
<CoverImage
|
||||||
src={coverImage}
|
src={coverImage}
|
||||||
alt={t("project_cover_image_alt")}
|
alt={t("project_cover_image_alt")}
|
||||||
className="absolute top-0 left-0 h-full w-full rounded-lg"
|
className="absolute top-0 left-0 h-full w-full rounded-[1.75rem]"
|
||||||
/>
|
/>
|
||||||
{showActionButtons && (
|
{showActionButtons && (
|
||||||
<div className="absolute top-2.5 left-2.5">
|
<div className="absolute top-2.5 left-2.5">
|
||||||
|
|
@ -60,8 +60,13 @@ function ProjectCreateHeader(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isClosable && (
|
{isClosable && (
|
||||||
<div className="absolute top-2 right-2 p-2">
|
<div className="absolute top-3 right-3">
|
||||||
<button type="button" onClick={handleClose} tabIndex={getIndex("close")}>
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleClose}
|
||||||
|
tabIndex={getIndex("close")}
|
||||||
|
className="grid h-10 w-10 place-items-center rounded-[1.25rem] bg-black/32 text-on-color outline-none backdrop-blur-md transition-colors hover:bg-black/48"
|
||||||
|
>
|
||||||
<CloseIcon className="h-5 w-5 text-on-color" />
|
<CloseIcon className="h-5 w-5 text-on-color" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -79,6 +84,7 @@ function ProjectCreateHeader(props: Props) {
|
||||||
}}
|
}}
|
||||||
control={control}
|
control={control}
|
||||||
value={value ?? null}
|
value={value ?? null}
|
||||||
|
buttonClassName="nodedc-overlay-button"
|
||||||
tabIndex={getIndex("cover_image")}
|
tabIndex={getIndex("cover_image")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -96,7 +102,7 @@ function ProjectCreateHeader(props: Props) {
|
||||||
className="flex items-center justify-center"
|
className="flex items-center justify-center"
|
||||||
buttonClassName="flex items-center justify-center"
|
buttonClassName="flex items-center justify-center"
|
||||||
label={
|
label={
|
||||||
<span className="grid h-11 w-11 place-items-center rounded-md border border-subtle bg-layer-2">
|
<span className="grid h-11 w-11 place-items-center rounded-[1.2rem] bg-black/36 shadow-[0_10px_24px_rgba(0,0,0,0.22)] backdrop-blur-md">
|
||||||
<Logo logo={value} size={20} />
|
<Logo logo={value} size={20} />
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,23 @@ function ProjectCreateButtons(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-end gap-2 border-t border-subtle py-4">
|
<div className="flex justify-end gap-2 border-t border-subtle py-4">
|
||||||
<Button variant="secondary" size="lg" onClick={handleClose} tabIndex={getIndex("cancel")}>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="lg"
|
||||||
|
onClick={handleClose}
|
||||||
|
tabIndex={getIndex("cancel")}
|
||||||
|
className="nodedc-modal-secondary-button min-w-[8.75rem]"
|
||||||
|
>
|
||||||
{t("common.cancel")}
|
{t("common.cancel")}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="primary" size="lg" type="submit" loading={isSubmitting} tabIndex={getIndex("submit")}>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="lg"
|
||||||
|
type="submit"
|
||||||
|
loading={isSubmitting}
|
||||||
|
tabIndex={getIndex("submit")}
|
||||||
|
className="nodedc-modal-primary-button min-w-[10.5rem]"
|
||||||
|
>
|
||||||
{isSubmitting ? t("creating") : t("create_project")}
|
{isSubmitting ? t("creating") : t("create_project")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
// plane imports
|
// plane imports
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { Button } from "@plane/propel/button";
|
import { Button } from "@plane/propel/button";
|
||||||
import { ChevronDownIcon } from "@plane/propel/icons";
|
import { ChevronDownIcon } from "@plane/propel/icons";
|
||||||
import { EUserProjectRoles, EUserWorkspaceRoles } from "@plane/types";
|
import { EUserProjectRoles, EUserWorkspaceRoles } from "@plane/types";
|
||||||
|
|
@ -52,11 +53,12 @@ const RoleFilterGroup = observer(function RoleFilterGroup({
|
||||||
const [isExpanded, setIsExpanded] = useState(true);
|
const [isExpanded, setIsExpanded] = useState(true);
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
const roleOptions = memberType === "project" ? PROJECT_ROLE_OPTIONS : WORKSPACE_ROLE_OPTIONS;
|
const roleOptions = memberType === "project" ? PROJECT_ROLE_OPTIONS : WORKSPACE_ROLE_OPTIONS;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<FilterHeader
|
<FilterHeader
|
||||||
title={`Roles${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
|
title={`${t("roles")}${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
|
||||||
isPreviewEnabled={isExpanded}
|
isPreviewEnabled={isExpanded}
|
||||||
handleIsPreviewEnabled={() => setIsExpanded(!isExpanded)}
|
handleIsPreviewEnabled={() => setIsExpanded(!isExpanded)}
|
||||||
/>
|
/>
|
||||||
|
|
@ -96,13 +98,14 @@ export const MemberListFiltersDropdown = observer(function MemberListFiltersDrop
|
||||||
const { appliedFilters, handleUpdate, memberType } = props;
|
const { appliedFilters, handleUpdate, memberType } = props;
|
||||||
|
|
||||||
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
const appliedFiltersCount = appliedFilters?.length ?? 0;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
customButton={
|
customButton={
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Button variant="secondary" size="lg" className="flex items-center gap-2">
|
<Button variant="ghost" size="lg" className="nodedc-settings-secondary-button flex items-center gap-2">
|
||||||
<span>Filters</span>
|
<span>{t("filters")}</span>
|
||||||
<ChevronDownIcon className="h-3 w-3" />
|
<ChevronDownIcon className="h-3 w-3" />
|
||||||
</Button>
|
</Button>
|
||||||
{appliedFiltersCount > 0 && (
|
{appliedFiltersCount > 0 && (
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,7 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
|
||||||
control={control}
|
control={control}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value ?? null}
|
value={value ?? null}
|
||||||
|
buttonClassName="nodedc-overlay-button"
|
||||||
disabled={!isAdmin}
|
disabled={!isAdmin}
|
||||||
projectId={project.id}
|
projectId={project.id}
|
||||||
/>
|
/>
|
||||||
|
|
@ -286,7 +287,7 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
hasError={Boolean(errors.name)}
|
hasError={Boolean(errors.name)}
|
||||||
className="rounded-md !p-3 font-medium"
|
className="nodedc-settings-input !px-4 !py-3 font-medium"
|
||||||
placeholder={t("common.project_name")}
|
placeholder={t("common.project_name")}
|
||||||
disabled={!isAdmin}
|
disabled={!isAdmin}
|
||||||
/>
|
/>
|
||||||
|
|
@ -306,7 +307,7 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
|
||||||
value={value}
|
value={value}
|
||||||
placeholder={t("project_description_placeholder")}
|
placeholder={t("project_description_placeholder")}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
className="min-h-[102px] text-13 font-medium"
|
className="nodedc-settings-input min-h-[102px] !rounded-[1.35rem] text-13 font-medium"
|
||||||
hasError={Boolean(errors?.description)}
|
hasError={Boolean(errors?.description)}
|
||||||
disabled={!isAdmin}
|
disabled={!isAdmin}
|
||||||
/>
|
/>
|
||||||
|
|
@ -342,7 +343,7 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
|
||||||
ref={ref}
|
ref={ref}
|
||||||
hasError={Boolean(errors.identifier)}
|
hasError={Boolean(errors.identifier)}
|
||||||
placeholder={t("project_settings.general.enter_project_id")}
|
placeholder={t("project_settings.general.enter_project_id")}
|
||||||
className="w-full font-medium"
|
className="nodedc-settings-input w-full pr-10 font-medium"
|
||||||
disabled={!isAdmin}
|
disabled={!isAdmin}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -383,7 +384,7 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
buttonClassName="!border-subtle !shadow-none font-medium rounded-md"
|
buttonClassName="nodedc-settings-select !h-12 font-medium"
|
||||||
input
|
input
|
||||||
disabled={!isAdmin}
|
disabled={!isAdmin}
|
||||||
// optionsClassName="w-full"
|
// optionsClassName="w-full"
|
||||||
|
|
@ -418,7 +419,7 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
|
||||||
onChange(value);
|
onChange(value);
|
||||||
}}
|
}}
|
||||||
error={Boolean(errors.timezone)}
|
error={Boolean(errors.timezone)}
|
||||||
buttonClassName="border-none"
|
buttonClassName="nodedc-settings-select !h-12 !border-0"
|
||||||
disabled={!isAdmin}
|
disabled={!isAdmin}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
@ -429,7 +430,14 @@ export function ProjectDetailsForm(props: IProjectDetailsForm) {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between py-2">
|
<div className="flex items-center justify-between py-2">
|
||||||
<>
|
<>
|
||||||
<Button variant="primary" size="lg" type="submit" loading={isLoading} disabled={!isAdmin}>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="lg"
|
||||||
|
type="submit"
|
||||||
|
loading={isLoading}
|
||||||
|
disabled={!isAdmin}
|
||||||
|
className="nodedc-settings-save-button min-w-[11.5rem]"
|
||||||
|
>
|
||||||
{isLoading ? t("updating") : t("common.update_project")}
|
{isLoading ? t("updating") : t("common.update_project")}
|
||||||
</Button>
|
</Button>
|
||||||
<span className="text-13 text-placeholder italic">
|
<span className="text-13 text-placeholder italic">
|
||||||
|
|
|
||||||
|
|
@ -84,11 +84,11 @@ export const ProjectMemberList = observer(function ProjectMemberList(props: TPro
|
||||||
<div className="flex items-center justify-between gap-4 overflow-x-hidden border-b border-subtle py-2">
|
<div className="flex items-center justify-between gap-4 overflow-x-hidden border-b border-subtle py-2">
|
||||||
<div className="text-14 font-semibold">{t("common.members")}</div>
|
<div className="text-14 font-semibold">{t("common.members")}</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="flex items-center justify-start gap-1.5 rounded-md border border-subtle bg-surface-1 px-2 py-1">
|
<div className="nodedc-settings-field flex min-h-[2.75rem] items-center justify-start gap-1.5 px-3">
|
||||||
<SearchIcon className="h-3.5 w-3.5" />
|
<SearchIcon className="h-3.5 w-3.5" />
|
||||||
<input
|
<input
|
||||||
className="w-full max-w-[234px] border-none bg-transparent text-13 placeholder:text-placeholder focus:outline-none"
|
className="w-full max-w-[234px] border-none bg-transparent text-13 placeholder:text-placeholder focus:outline-none"
|
||||||
placeholder="Search"
|
placeholder={t("search")}
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
autoFocus
|
autoFocus
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
|
@ -101,11 +101,12 @@ export const ProjectMemberList = observer(function ProjectMemberList(props: TPro
|
||||||
/>
|
/>
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="ghost"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setInviteModal(true);
|
setInviteModal(true);
|
||||||
}}
|
}}
|
||||||
data-ph-element={MEMBER_TRACKER_ELEMENTS.HEADER_ADD_BUTTON}
|
data-ph-element={MEMBER_TRACKER_ELEMENTS.HEADER_ADD_BUTTON}
|
||||||
|
className="nodedc-settings-primary-button min-w-[11rem]"
|
||||||
>
|
>
|
||||||
{t("add_member")}
|
{t("add_member")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import React from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { Ban } from "lucide-react";
|
import { Ban } from "lucide-react";
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { EUserProjectRoles } from "@plane/types";
|
import { EUserProjectRoles } from "@plane/types";
|
||||||
// plane ui
|
// plane ui
|
||||||
import { Avatar, CustomSearchSelect } from "@plane/ui";
|
import { Avatar, CustomSearchSelect } from "@plane/ui";
|
||||||
|
|
@ -24,6 +25,7 @@ type Props = {
|
||||||
|
|
||||||
export const MemberSelect = observer(function MemberSelect(props: Props) {
|
export const MemberSelect = observer(function MemberSelect(props: Props) {
|
||||||
const { value, onChange, isDisabled = false } = props;
|
const { value, onChange, isDisabled = false } = props;
|
||||||
|
const { t } = useTranslation();
|
||||||
// router
|
// router
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
// store hooks
|
// store hooks
|
||||||
|
|
@ -72,12 +74,12 @@ export const MemberSelect = observer(function MemberSelect(props: Props) {
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Ban className="h-3.5 w-3.5 rotate-90 text-placeholder" />
|
<Ban className="h-3.5 w-3.5 rotate-90 text-placeholder" />
|
||||||
<span className="text-13 text-placeholder">None</span>
|
<span className="text-13 text-placeholder">{t("none")}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
buttonClassName="!px-3 !py-2 bg-surface-1"
|
buttonClassName="nodedc-settings-select !w-full !justify-between !px-4 !py-3"
|
||||||
options={
|
options={
|
||||||
options &&
|
options &&
|
||||||
options && [
|
options && [
|
||||||
|
|
@ -88,7 +90,7 @@ export const MemberSelect = observer(function MemberSelect(props: Props) {
|
||||||
content: (
|
content: (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Ban className="h-3.5 w-3.5 rotate-90 text-placeholder" />
|
<Ban className="h-3.5 w-3.5 rotate-90 text-placeholder" />
|
||||||
<span className="py-0.5 text-13 text-placeholder">None</span>
|
<span className="py-0.5 text-13 text-placeholder">{t("none")}</span>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { observer } from "mobx-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
// ui
|
// ui
|
||||||
import { Button, getButtonStyling } from "@plane/propel/button";
|
import { Button } from "@plane/propel/button";
|
||||||
import { Logo } from "@plane/propel/emoji-icon-picker";
|
import { Logo } from "@plane/propel/emoji-icon-picker";
|
||||||
import { Row } from "@plane/ui";
|
import { Row } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
|
|
@ -35,7 +35,7 @@ export const ProjectFeatureUpdate = observer(function ProjectFeatureUpdate(props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Row className="py-6">
|
<Row className="px-6 py-6">
|
||||||
<ProjectFeaturesList workspaceSlug={workspaceSlug} projectId={projectId} isAdmin />
|
<ProjectFeaturesList workspaceSlug={workspaceSlug} projectId={projectId} isAdmin />
|
||||||
</Row>
|
</Row>
|
||||||
<div className="mt-4 flex items-center justify-between gap-2 border-t border-subtle px-6 py-4">
|
<div className="mt-4 flex items-center justify-between gap-2 border-t border-subtle px-6 py-4">
|
||||||
|
|
@ -45,13 +45,19 @@ export const ProjectFeatureUpdate = observer(function ProjectFeatureUpdate(props
|
||||||
{t("created").toLowerCase()}.
|
{t("created").toLowerCase()}.
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button variant="secondary" size="lg" onClick={onClose} tabIndex={1}>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="lg"
|
||||||
|
onClick={onClose}
|
||||||
|
tabIndex={1}
|
||||||
|
className="nodedc-modal-secondary-button min-w-[8.75rem]"
|
||||||
|
>
|
||||||
{t("close")}
|
{t("close")}
|
||||||
</Button>
|
</Button>
|
||||||
<Link
|
<Link
|
||||||
href={`/${workspaceSlug}/projects/${projectId}/issues`}
|
href={`/${workspaceSlug}/projects/${projectId}/issues`}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className={getButtonStyling("primary", "lg")}
|
className="nodedc-modal-primary-button inline-flex min-w-[10.5rem] items-center justify-center"
|
||||||
tabIndex={2}
|
tabIndex={2}
|
||||||
>
|
>
|
||||||
{t("open_project")}
|
{t("open_project")}
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,9 @@ type TDefaultSettingItemProps = {
|
||||||
|
|
||||||
function DefaultSettingItem({ title, description, children }: TDefaultSettingItemProps) {
|
function DefaultSettingItem({ title, description, children }: TDefaultSettingItemProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-between gap-x-2">
|
<div className="nodedc-settings-card flex items-center justify-between gap-x-6 px-5 py-4">
|
||||||
<div className="flex flex-col gap-0.5">
|
<div className="flex flex-col gap-1">
|
||||||
<h4 className="text-13 font-medium">{title}</h4>
|
<h4 className="text-13 font-medium text-primary">{title}</h4>
|
||||||
<p className="text-11 text-tertiary">{description}</p>
|
<p className="text-11 text-tertiary">{description}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full max-w-48 sm:max-w-64">{children}</div>
|
<div className="w-full max-w-48 sm:max-w-64">{children}</div>
|
||||||
|
|
@ -139,7 +139,10 @@ export const ProjectSettingsMemberDefaults = observer(function ProjectSettingsMe
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-6 flex flex-col gap-y-6">
|
<div className="my-6 flex flex-col gap-y-6">
|
||||||
<DefaultSettingItem title="Project Lead" description="Select the project lead for the project.">
|
<DefaultSettingItem
|
||||||
|
title={t("project_settings.members.project_lead")}
|
||||||
|
description={t("project_settings.members.project_lead_description")}
|
||||||
|
>
|
||||||
{currentProjectDetails ? (
|
{currentProjectDetails ? (
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -160,7 +163,10 @@ export const ProjectSettingsMemberDefaults = observer(function ProjectSettingsMe
|
||||||
</Loader>
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</DefaultSettingItem>
|
</DefaultSettingItem>
|
||||||
<DefaultSettingItem title="Default Assignee" description="Select the default assignee for the project.">
|
<DefaultSettingItem
|
||||||
|
title={t("project_settings.members.default_assignee")}
|
||||||
|
description={t("project_settings.members.default_assignee_description")}
|
||||||
|
>
|
||||||
{currentProjectDetails ? (
|
{currentProjectDetails ? (
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
|
|
@ -183,8 +189,8 @@ export const ProjectSettingsMemberDefaults = observer(function ProjectSettingsMe
|
||||||
</DefaultSettingItem>
|
</DefaultSettingItem>
|
||||||
{currentProjectDetails && (
|
{currentProjectDetails && (
|
||||||
<DefaultSettingItem
|
<DefaultSettingItem
|
||||||
title="Guest access"
|
title={t("project_settings.members.guest_super_permissions.title")}
|
||||||
description="This will allow guests to have view access to all the project work items."
|
description={t("project_settings.members.guest_super_permissions.sub_heading")}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-end">
|
<div className="flex items-center justify-end">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
|
|
|
||||||
|
|
@ -55,28 +55,29 @@ export const GeneralProjectSettingsControlSection = observer(function GeneralPro
|
||||||
isOpen={Boolean(selectProject)}
|
isOpen={Boolean(selectProject)}
|
||||||
onClose={() => setSelectedProject(null)}
|
onClose={() => setSelectedProject(null)}
|
||||||
/>
|
/>
|
||||||
<div className="rounded-lg border border-subtle bg-layer-2">
|
<div className="flex flex-col gap-1.5">
|
||||||
{/* Project Selector */}
|
|
||||||
<SettingsBoxedControlItem
|
<SettingsBoxedControlItem
|
||||||
className="rounded-b-none border-0 border-b"
|
|
||||||
title={t("archive")}
|
title={t("archive")}
|
||||||
description={t("project_settings_control.archive_description")}
|
description={t("project_settings_control.archive_description")}
|
||||||
control={
|
control={
|
||||||
<Button variant="secondary" onClick={() => setArchiveProject(true)}>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => setArchiveProject(true)}
|
||||||
|
className="nodedc-settings-secondary-button"
|
||||||
|
>
|
||||||
{t("archive")}
|
{t("archive")}
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{/* Format Selector */}
|
|
||||||
<SettingsBoxedControlItem
|
<SettingsBoxedControlItem
|
||||||
className="rounded-t-none border-0"
|
|
||||||
title={t("delete")}
|
title={t("delete")}
|
||||||
description={t("project_settings_control.delete_description")}
|
description={t("project_settings_control.delete_description")}
|
||||||
control={
|
control={
|
||||||
<Button
|
<Button
|
||||||
variant="error-outline"
|
variant="ghost"
|
||||||
onClick={() => setSelectedProject(currentProjectDetails.id ?? null)}
|
onClick={() => setSelectedProject(currentProjectDetails.id ?? null)}
|
||||||
data-ph-element={PROJECT_TRACKER_ELEMENTS.DELETE_PROJECT_BUTTON}
|
data-ph-element={PROJECT_TRACKER_ELEMENTS.DELETE_PROJECT_BUTTON}
|
||||||
|
className="nodedc-settings-danger-button"
|
||||||
>
|
>
|
||||||
{t("delete")}
|
{t("delete")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export function SettingsBoxedControlItem(props: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex w-full flex-col items-start gap-4 rounded-lg border border-subtle bg-layer-2 px-4 py-3 md:flex-row md:items-center md:justify-between md:gap-8",
|
"nodedc-settings-card flex w-full flex-col items-start gap-4 px-5 py-4 md:flex-row md:items-center md:justify-between md:gap-8",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"nodedc": {
|
||||||
|
"accent_rgb": [195, 255, 102],
|
||||||
|
"passive_card_rgb": [42, 43, 46],
|
||||||
|
"active_card_rgb": [195, 255, 102]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,102 +1,33 @@
|
||||||
/**
|
/*
|
||||||
* Copyright 2018 Google Inc. All Rights Reserved.
|
* Temporary cleanup service worker.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
*
|
||||||
* you may not use this file except in compliance with the License.
|
* We intentionally stop intercepting requests, clear legacy caches, and
|
||||||
* You may obtain a copy of the License at
|
* unregister ourselves. This prevents stale HTML/chunk bundles from surviving
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* fresh web deployments and causing client-side hydration/runtime crashes.
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// If the loader is already loaded, just stop.
|
self.addEventListener("install", (event) => {
|
||||||
if (!self.define) {
|
event.waitUntil(self.skipWaiting());
|
||||||
let registry = {};
|
|
||||||
|
|
||||||
// Used for `eval` and `importScripts` where we can't get script URL by other means.
|
|
||||||
// In both cases, it's safe to use a global var because those functions are synchronous.
|
|
||||||
let nextDefineUri;
|
|
||||||
|
|
||||||
const singleRequire = (uri, parentUri) => {
|
|
||||||
uri = new URL(uri + ".js", parentUri).href;
|
|
||||||
return (
|
|
||||||
registry[uri] ||
|
|
||||||
new Promise((resolve) => {
|
|
||||||
if ("document" in self) {
|
|
||||||
const script = document.createElement("script");
|
|
||||||
script.src = uri;
|
|
||||||
script.onload = resolve;
|
|
||||||
document.head.appendChild(script);
|
|
||||||
} else {
|
|
||||||
nextDefineUri = uri;
|
|
||||||
importScripts(uri);
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}).then(() => {
|
|
||||||
let promise = registry[uri];
|
|
||||||
if (!promise) {
|
|
||||||
throw new Error(`Module ${uri} didn’t register its module`);
|
|
||||||
}
|
|
||||||
return promise;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.define = (depsNames, factory) => {
|
|
||||||
const uri = nextDefineUri || ("document" in self ? document.currentScript.src : "") || location.href;
|
|
||||||
if (registry[uri]) {
|
|
||||||
// Module is already loading or loaded.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let exports = {};
|
|
||||||
const require = (depUri) => singleRequire(depUri, uri);
|
|
||||||
const specialDeps = {
|
|
||||||
module: { uri },
|
|
||||||
exports,
|
|
||||||
require,
|
|
||||||
};
|
|
||||||
registry[uri] = Promise.all(depsNames.map((depName) => specialDeps[depName] || require(depName))).then((deps) => {
|
|
||||||
factory(...deps);
|
|
||||||
return exports;
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
define(["./workbox-9f2f79cf"], function (workbox) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
importScripts();
|
self.addEventListener("activate", (event) => {
|
||||||
self.skipWaiting();
|
event.waitUntil(
|
||||||
workbox.clientsClaim();
|
(async () => {
|
||||||
workbox.registerRoute(
|
const cacheKeys = await caches.keys();
|
||||||
"/",
|
await Promise.all(cacheKeys.map((cacheKey) => caches.delete(cacheKey)));
|
||||||
new workbox.NetworkFirst({
|
|
||||||
cacheName: "start-url",
|
const clients = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
|
||||||
plugins: [
|
await Promise.all(
|
||||||
{
|
clients.map((client) =>
|
||||||
cacheWillUpdate: async ({ request, response, event, state }) => {
|
"navigate" in client && typeof client.navigate === "function" ? client.navigate(client.url) : Promise.resolve()
|
||||||
if (response && response.type === "opaqueredirect") {
|
)
|
||||||
return new Response(response.body, {
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
headers: response.headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
"GET"
|
|
||||||
);
|
);
|
||||||
workbox.registerRoute(
|
|
||||||
/.*/i,
|
await self.registration.unregister();
|
||||||
new workbox.NetworkOnly({
|
})()
|
||||||
cacheName: "dev",
|
|
||||||
plugins: [],
|
|
||||||
}),
|
|
||||||
"GET"
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
//# sourceMappingURL=sw.js.map
|
|
||||||
|
self.addEventListener("fetch", () => {
|
||||||
|
// Intentionally empty: do not cache or intercept runtime requests.
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@
|
||||||
--nodedc-accent-rgb: 51 163 255;
|
--nodedc-accent-rgb: 51 163 255;
|
||||||
--nodedc-card-passive-rgb: 42 43 46;
|
--nodedc-card-passive-rgb: 42 43 46;
|
||||||
--nodedc-card-active-rgb: 195 255 102;
|
--nodedc-card-active-rgb: 195 255 102;
|
||||||
|
--brand-default: rgb(var(--nodedc-accent-rgb));
|
||||||
|
--brand-300: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 35%, white);
|
||||||
|
--brand-700: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 75%, black);
|
||||||
/* end background colors */
|
/* end background colors */
|
||||||
}
|
}
|
||||||
/* background colors */
|
/* background colors */
|
||||||
|
|
@ -419,7 +422,7 @@
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
border-radius: 999px !important;
|
border-radius: 999px !important;
|
||||||
min-height: 2.5rem;
|
min-height: 2.5rem;
|
||||||
padding-inline: 1rem;
|
padding-inline: 1.35rem;
|
||||||
background: rgba(18, 18, 22, 0.94) !important;
|
background: rgba(18, 18, 22, 0.94) !important;
|
||||||
color: var(--text-color-primary) !important;
|
color: var(--text-color-primary) !important;
|
||||||
transition:
|
transition:
|
||||||
|
|
@ -428,6 +431,11 @@
|
||||||
transform 160ms ease;
|
transform 160ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nodedc-toolbar-pill-wide {
|
||||||
|
min-width: 9.75rem;
|
||||||
|
padding-inline: 1.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
.nodedc-toolbar-pill:hover {
|
.nodedc-toolbar-pill:hover {
|
||||||
background: rgba(24, 24, 29, 0.96) !important;
|
background: rgba(24, 24, 29, 0.96) !important;
|
||||||
}
|
}
|
||||||
|
|
@ -442,13 +450,261 @@
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
border-radius: 999px !important;
|
border-radius: 999px !important;
|
||||||
min-height: 2.5rem;
|
min-height: 2.5rem;
|
||||||
padding-inline: 1rem;
|
padding-inline: 1.55rem;
|
||||||
background: rgb(var(--nodedc-accent-rgb)) !important;
|
background: rgb(var(--nodedc-accent-rgb)) !important;
|
||||||
color: #0b1117 !important;
|
color: #0b1117 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nodedc-toolbar-primary-wide {
|
||||||
|
min-width: 13rem;
|
||||||
|
padding-inline: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.nodedc-toolbar-primary:hover {
|
.nodedc-toolbar-primary:hover {
|
||||||
background: rgba(var(--nodedc-accent-rgb), 0.92) !important;
|
background: color-mix(in srgb, rgb(var(--nodedc-accent-rgb)) 82%, white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-modal-secondary-button {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.25rem !important;
|
||||||
|
background: rgba(255, 255, 255, 0.06) !important;
|
||||||
|
color: var(--text-color-primary) !important;
|
||||||
|
padding-inline: 1.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-modal-secondary-button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-modal-primary-button {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.25rem !important;
|
||||||
|
background: rgb(var(--nodedc-card-active-rgb)) !important;
|
||||||
|
color: #0b1117 !important;
|
||||||
|
padding-inline: 1.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-modal-primary-button:hover {
|
||||||
|
background: color-mix(in srgb, rgb(var(--nodedc-card-active-rgb)) 82%, white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-modal-danger-button {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.25rem !important;
|
||||||
|
padding-inline: 1.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-modal-chip {
|
||||||
|
min-height: 2.5rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.25rem !important;
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.012) 100%),
|
||||||
|
rgba(255, 255, 255, 0.028) !important;
|
||||||
|
color: var(--text-color-secondary) !important;
|
||||||
|
padding-inline: 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-modal-chip:hover {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.038) 0%, rgba(255, 255, 255, 0.016) 100%),
|
||||||
|
rgba(255, 255, 255, 0.04) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-card {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.026) 0%, rgba(255, 255, 255, 0.01) 100%),
|
||||||
|
rgba(255, 255, 255, 0.032);
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.35rem !important;
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
-webkit-backdrop-filter: blur(18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-field {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0%, rgba(255, 255, 255, 0.012) 100%),
|
||||||
|
rgba(255, 255, 255, 0.03) !important;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.25rem !important;
|
||||||
|
-webkit-backdrop-filter: blur(18px);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
transition: background 160ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-field:hover,
|
||||||
|
.nodedc-settings-field:focus-within {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.038) 0%, rgba(255, 255, 255, 0.016) 100%),
|
||||||
|
rgba(255, 255, 255, 0.042) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-input {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.028) 0%, rgba(255, 255, 255, 0.012) 100%),
|
||||||
|
rgba(255, 255, 255, 0.03) !important;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.25rem !important;
|
||||||
|
min-height: 3rem;
|
||||||
|
color: var(--text-color-primary) !important;
|
||||||
|
font-size: 0.875rem !important;
|
||||||
|
-webkit-backdrop-filter: blur(18px);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-input:focus,
|
||||||
|
.nodedc-settings-input:focus-visible {
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-select {
|
||||||
|
min-height: 3rem !important;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: 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.03) !important;
|
||||||
|
color: var(--text-color-primary) !important;
|
||||||
|
-webkit-backdrop-filter: blur(18px);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-select:hover,
|
||||||
|
.nodedc-settings-select:focus-visible {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.038) 0%, rgba(255, 255, 255, 0.016) 100%),
|
||||||
|
rgba(255, 255, 255, 0.042) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-chip {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: 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.03) !important;
|
||||||
|
color: var(--text-color-primary) !important;
|
||||||
|
padding-inline: 1rem !important;
|
||||||
|
-webkit-backdrop-filter: blur(18px);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-chip:hover {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.038) 0%, rgba(255, 255, 255, 0.016) 100%),
|
||||||
|
rgba(255, 255, 255, 0.042) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-primary-button {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.25rem !important;
|
||||||
|
background: rgb(var(--nodedc-card-active-rgb)) !important;
|
||||||
|
color: #0b1117 !important;
|
||||||
|
padding-inline: 1.35rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-primary-button:hover {
|
||||||
|
background: color-mix(in srgb, rgb(var(--nodedc-card-active-rgb)) 82%, white) !important;
|
||||||
|
color: #0b1117 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-save-button {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.25rem !important;
|
||||||
|
background: rgb(var(--nodedc-card-active-rgb)) !important;
|
||||||
|
color: #0b1117 !important;
|
||||||
|
padding-inline: 1.45rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-save-button:hover {
|
||||||
|
background: color-mix(in srgb, rgb(var(--nodedc-card-active-rgb)) 82%, white) !important;
|
||||||
|
color: #0b1117 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-overlay-button {
|
||||||
|
min-height: 2.5rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.05rem !important;
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.018) 100%),
|
||||||
|
rgba(8, 8, 10, 0.58) !important;
|
||||||
|
color: #f5f7fb !important;
|
||||||
|
padding-inline: 1rem !important;
|
||||||
|
-webkit-backdrop-filter: blur(18px);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
transition:
|
||||||
|
background 120ms ease,
|
||||||
|
color 120ms ease,
|
||||||
|
transform 120ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-overlay-button:hover {
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(255, 255, 255, 0.075) 0%, rgba(255, 255, 255, 0.03) 100%),
|
||||||
|
rgba(8, 8, 10, 0.72) !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-secondary-button {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.25rem !important;
|
||||||
|
background: rgba(255, 255, 255, 0.06) !important;
|
||||||
|
color: var(--text-color-primary) !important;
|
||||||
|
padding-inline: 1.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-secondary-button:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1) !important;
|
||||||
|
color: var(--text-color-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-danger-button {
|
||||||
|
min-height: 2.75rem;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-radius: 1.25rem !important;
|
||||||
|
background: rgba(255, 82, 82, 0.14) !important;
|
||||||
|
color: rgb(255, 141, 141) !important;
|
||||||
|
padding-inline: 1.25rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nodedc-settings-danger-button:hover {
|
||||||
|
background: rgba(255, 82, 82, 0.2) !important;
|
||||||
|
color: rgb(255, 162, 162) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nodedc-toolbar-filter-toggle {
|
.nodedc-toolbar-filter-toggle {
|
||||||
|
|
|
||||||
|
|
@ -916,8 +916,11 @@ export default {
|
||||||
priority: "{entity} Priority",
|
priority: "{entity} Priority",
|
||||||
all: "All {entity}",
|
all: "All {entity}",
|
||||||
drop_here_to_move: "Drop here to move the {entity}",
|
drop_here_to_move: "Drop here to move the {entity}",
|
||||||
|
drop_here_to_delete: "Drop here to delete the {entity}",
|
||||||
delete: {
|
delete: {
|
||||||
label: "Delete {entity}",
|
label: "Delete {entity}",
|
||||||
|
confirmation:
|
||||||
|
"Are you sure you want to delete {entity} {identifier}? All related data will be permanently removed and this action cannot be undone.",
|
||||||
success: "{entity} deleted successfully",
|
success: "{entity} deleted successfully",
|
||||||
failed: "{entity} delete failed",
|
failed: "{entity} delete failed",
|
||||||
},
|
},
|
||||||
|
|
@ -1865,7 +1868,9 @@ export default {
|
||||||
members: {
|
members: {
|
||||||
label: "Members",
|
label: "Members",
|
||||||
project_lead: "Project lead",
|
project_lead: "Project lead",
|
||||||
|
project_lead_description: "Choose the project lead.",
|
||||||
default_assignee: "Default assignee",
|
default_assignee: "Default assignee",
|
||||||
|
default_assignee_description: "Choose the default assignee for the project.",
|
||||||
guest_super_permissions: {
|
guest_super_permissions: {
|
||||||
title: "Grant view access to all work items for guest users:",
|
title: "Grant view access to all work items for guest users:",
|
||||||
sub_heading: "This will allow guests to have view access to all the project work items.",
|
sub_heading: "This will allow guests to have view access to all the project work items.",
|
||||||
|
|
@ -1908,6 +1913,9 @@ export default {
|
||||||
label: "Estimates",
|
label: "Estimates",
|
||||||
title: "Enable estimates for my project",
|
title: "Enable estimates for my project",
|
||||||
enable_description: "They help you in communicating complexity and workload of the team.",
|
enable_description: "They help you in communicating complexity and workload of the team.",
|
||||||
|
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",
|
||||||
no_estimate: "No estimate",
|
no_estimate: "No estimate",
|
||||||
new: "New estimate system",
|
new: "New estimate system",
|
||||||
create: {
|
create: {
|
||||||
|
|
|
||||||
|
|
@ -1072,8 +1072,11 @@ export default {
|
||||||
priority: "Приоритет {entity}",
|
priority: "Приоритет {entity}",
|
||||||
all: "Все {entity}",
|
all: "Все {entity}",
|
||||||
drop_here_to_move: "Переместите {entity} сюда",
|
drop_here_to_move: "Переместите {entity} сюда",
|
||||||
|
drop_here_to_delete: "Перетащите сюда, чтобы удалить {entity}",
|
||||||
delete: {
|
delete: {
|
||||||
label: "Удалить {entity}",
|
label: "Удалить {entity}",
|
||||||
|
confirmation:
|
||||||
|
"Вы уверены, что хотите удалить {entity} {identifier}? Все связанные данные будут удалены без возможности восстановления.",
|
||||||
success: "{entity} успешно удалён",
|
success: "{entity} успешно удалён",
|
||||||
failed: "Ошибка удаления {entity}",
|
failed: "Ошибка удаления {entity}",
|
||||||
},
|
},
|
||||||
|
|
@ -2024,7 +2027,9 @@ export default {
|
||||||
members: {
|
members: {
|
||||||
label: "Участники",
|
label: "Участники",
|
||||||
project_lead: "Руководитель проекта",
|
project_lead: "Руководитель проекта",
|
||||||
|
project_lead_description: "Выберите руководителя проекта.",
|
||||||
default_assignee: "Ответственный по умолчанию",
|
default_assignee: "Ответственный по умолчанию",
|
||||||
|
default_assignee_description: "Выберите ответственного по умолчанию для проекта.",
|
||||||
guest_super_permissions: {
|
guest_super_permissions: {
|
||||||
title: "Дать гостям доступ на просмотр всех рабочих элементов:",
|
title: "Дать гостям доступ на просмотр всех рабочих элементов:",
|
||||||
sub_heading: "Гости смогут просматривать все рабочие элементы проекта",
|
sub_heading: "Гости смогут просматривать все рабочие элементы проекта",
|
||||||
|
|
@ -2067,6 +2072,9 @@ export default {
|
||||||
label: "Оценки",
|
label: "Оценки",
|
||||||
title: "Включить оценки для моего проекта",
|
title: "Включить оценки для моего проекта",
|
||||||
enable_description: "Они помогают вам в общении о сложности и рабочей нагрузке команды.",
|
enable_description: "Они помогают вам в общении о сложности и рабочей нагрузке команды.",
|
||||||
|
list_heading: "Список оценок",
|
||||||
|
archived_heading: "Архивные оценки",
|
||||||
|
archived_description: "Это оценки из предыдущих версий проекта, которые сейчас не используются. Подробнее о них",
|
||||||
no_estimate: "Без оценки",
|
no_estimate: "Без оценки",
|
||||||
new: "Новая система оценок",
|
new: "Новая система оценок",
|
||||||
create: {
|
create: {
|
||||||
|
|
|
||||||
|
|
@ -202,10 +202,10 @@ function CustomMenu(props: ICustomMenuDropdownProps) {
|
||||||
className={cn(
|
className={cn(
|
||||||
"nodedc-glass-modal nodedc-glass-popup-surface my-1 min-w-[13rem] overflow-y-auto rounded-[1.25rem] border-0 px-2 py-2.5 text-12 whitespace-nowrap shadow-none outline-none",
|
"nodedc-glass-modal nodedc-glass-popup-surface my-1 min-w-[13rem] overflow-y-auto rounded-[1.25rem] border-0 px-2 py-2.5 text-12 whitespace-nowrap shadow-none outline-none",
|
||||||
{
|
{
|
||||||
"max-h-60": maxHeight === "lg",
|
"max-h-[min(85vh,40rem)]": maxHeight === "lg",
|
||||||
"max-h-48": maxHeight === "md",
|
"max-h-[min(70vh,24rem)]": maxHeight === "md",
|
||||||
"max-h-36": maxHeight === "rg",
|
"max-h-[min(55vh,18rem)]": maxHeight === "rg",
|
||||||
"max-h-28": maxHeight === "sm",
|
"max-h-[min(40vh,12rem)]": maxHeight === "sm",
|
||||||
},
|
},
|
||||||
optionsClassName
|
optionsClassName
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ function CustomSelect(props: ICustomSelectProps) {
|
||||||
<Combobox.Options data-prevent-outside-click>
|
<Combobox.Options data-prevent-outside-click>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-30 my-1 min-w-48 overflow-y-scroll rounded-md border-[0.5px] border-subtle-1 bg-surface-1 px-2 py-2.5 text-11 whitespace-nowrap focus:outline-none",
|
"nodedc-dropdown-surface z-30 my-1 min-w-48 overflow-y-scroll whitespace-nowrap focus:outline-none",
|
||||||
optionsClassName
|
optionsClassName
|
||||||
)}
|
)}
|
||||||
ref={setPopperElement}
|
ref={setPopperElement}
|
||||||
|
|
@ -166,9 +166,9 @@ function Option(props: ICustomSelectItemProps) {
|
||||||
value={value}
|
value={value}
|
||||||
className={({ active }) =>
|
className={({ active }) =>
|
||||||
cn(
|
cn(
|
||||||
"flex cursor-pointer items-center justify-between gap-2 truncate rounded-sm px-1 py-1.5 text-secondary select-none",
|
"nodedc-dropdown-option cursor-pointer text-secondary",
|
||||||
{
|
{
|
||||||
"bg-layer-transparent-hover": active,
|
"bg-white/6": active,
|
||||||
},
|
},
|
||||||
className
|
className
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -75,8 +75,14 @@ export function AlertModalCore(props: Props) {
|
||||||
const Icon = VARIANT_ICONS[variant];
|
const Icon = VARIANT_ICONS[variant];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalCore isOpen={isOpen} handleClose={handleClose} position={position} width={width}>
|
<ModalCore
|
||||||
<div className="flex flex-col items-center gap-4 p-5 sm:flex-row sm:items-start">
|
isOpen={isOpen}
|
||||||
|
handleClose={handleClose}
|
||||||
|
position={position}
|
||||||
|
width={width}
|
||||||
|
className="transition-[width] ease-linear"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center gap-4 px-6 pt-6 pb-5 sm:flex-row sm:items-start">
|
||||||
{!hideIcon && (
|
{!hideIcon && (
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
@ -88,15 +94,24 @@ export function AlertModalCore(props: Props) {
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<div className="text-center sm:text-left">
|
<div className="text-center sm:text-left">
|
||||||
<h3 className="text-16 font-medium">{title}</h3>
|
<h3 className="text-18 font-medium text-secondary">{title}</h3>
|
||||||
<p className="mt-1 text-13 text-secondary">{content}</p>
|
<p className="mt-1 text-13 text-secondary">{content}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col-reverse gap-2 border-t-[0.5px] border-subtle px-5 py-4 sm:flex-row sm:justify-end">
|
<div className="flex flex-col-reverse gap-3 border-t border-subtle/70 px-6 py-4 sm:flex-row sm:justify-end">
|
||||||
<Button variant="secondary" onClick={handleClose}>
|
<Button variant="secondary" onClick={handleClose} className="nodedc-modal-secondary-button min-w-[8.25rem]">
|
||||||
{secondaryButtonText}
|
{secondaryButtonText}
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant={BUTTON_VARIANTS[variant]} tabIndex={1} onClick={handleSubmit} loading={isSubmitting}>
|
<Button
|
||||||
|
variant={BUTTON_VARIANTS[variant]}
|
||||||
|
tabIndex={1}
|
||||||
|
onClick={handleSubmit}
|
||||||
|
loading={isSubmitting}
|
||||||
|
className={cn("min-w-[8.25rem]", {
|
||||||
|
"nodedc-modal-danger-button": variant === "danger",
|
||||||
|
"nodedc-modal-primary-button": variant === "primary",
|
||||||
|
})}
|
||||||
|
>
|
||||||
{isSubmitting ? primaryButtonText.loading : primaryButtonText.default}
|
{isSubmitting ? primaryButtonText.loading : primaryButtonText.default}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue