UI - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: auth screens, error pages и новый канон внешних контуров
This commit is contained in:
@@ -8,20 +8,18 @@ export function MaintenanceMessage() {
|
||||
const linkMap = [
|
||||
{
|
||||
key: "mail_to",
|
||||
label: "Contact Support",
|
||||
value: "mailto:support@plane.so",
|
||||
label: "Служба поддержки",
|
||||
value: "https://nodedc.dctouch.ru/",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-2.5">
|
||||
<h1 className="text-left text-18 font-semibold text-primary">
|
||||
🚧 Looks like NODE.DC didn't start up correctly!
|
||||
</h1>
|
||||
<h1 className="text-left text-18 font-semibold text-primary">🚧 NODE.DC запустился с ошибкой.</h1>
|
||||
<span className="text-left text-14 font-medium text-secondary">
|
||||
Some services might have failed to start. Please check your container logs to identify and resolve the issue.
|
||||
If you're stuck, reach out to our support team for more help.
|
||||
Часть сервисов могла не подняться. Проверьте логи контейнеров и устраните причину. Если нужна помощь,
|
||||
переходите в службу поддержки.
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 flex items-center justify-start gap-6">
|
||||
@@ -31,7 +29,7 @@ export function MaintenanceMessage() {
|
||||
href={link.value}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-13 text-accent-primary hover:underline"
|
||||
className="nodedc-error-link text-13"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
|
||||
@@ -29,13 +29,13 @@ export const ExternalContoursIssueContentProperties = observer(function External
|
||||
if (!issue || !issue?.id) return <></>;
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col divide-y-2 divide-subtle-1">
|
||||
<div className="nodedc-external-section flex w-full flex-col px-4 py-4">
|
||||
<div className="w-full overflow-y-auto">
|
||||
<h5 className="mb-2 text-body-sm-medium">{t("external_contours_page.properties.section_title")}</h5>
|
||||
<div className={`divide-y-2 divide-subtle-1 ${!isEditable ? "opacity-60" : ""}`}>
|
||||
<div className={`${!isEditable ? "opacity-60" : ""}`}>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex h-8 items-center gap-2">
|
||||
<div className="flex w-2/5 flex-shrink-0 items-center gap-1 text-13 text-tertiary">
|
||||
<div className="nodedc-external-panel flex min-h-12 items-center gap-3 px-4 py-3">
|
||||
<div className="flex w-2/5 flex-shrink-0 items-center gap-1.5 text-13 text-tertiary">
|
||||
<PriorityPropertyIcon className="h-4 w-4 flex-shrink-0" />
|
||||
<span>{t("priority")}</span>
|
||||
</div>
|
||||
@@ -44,14 +44,14 @@ export const ExternalContoursIssueContentProperties = observer(function External
|
||||
onChange={(val) => issue?.id && issueOperations.update(workspaceSlug, targetProjectId, issue.id, { priority: val })}
|
||||
disabled={!isEditable}
|
||||
buttonVariant="border-with-text"
|
||||
className="w-3/5 flex-grow rounded-sm px-2 hover:bg-layer-1"
|
||||
className="w-3/5 flex-grow rounded-full px-3 hover:bg-white/6"
|
||||
buttonContainerClassName="w-full text-left"
|
||||
buttonClassName="h-auto w-min whitespace-nowrap"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex min-h-8 items-center gap-2">
|
||||
<div className="flex w-2/5 flex-shrink-0 items-center gap-1 text-13 text-tertiary">
|
||||
<div className="nodedc-external-panel flex min-h-12 items-center gap-3 px-4 py-3">
|
||||
<div className="flex w-2/5 flex-shrink-0 items-center gap-1.5 text-13 text-tertiary">
|
||||
<LabelPropertyIcon className="h-4 w-4 flex-shrink-0" />
|
||||
<span>{t("labels")}</span>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,6 @@ import { useEffect, useMemo, useRef } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import type { EditorRefApi } from "@plane/editor";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Badge } from "@plane/propel/badge";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import type { TExternalContourRequest, TIssue, TNameDescriptionLoader } from "@plane/types";
|
||||
import { EFileAssetType } from "@plane/types";
|
||||
@@ -139,8 +138,8 @@ export const ExternalContoursIssueMainContent = observer(function ExternalContou
|
||||
|
||||
if (!hasDirectTargetAccess) {
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-4 pb-4">
|
||||
<div className="space-y-4">
|
||||
<div className="nodedc-external-section space-y-4 p-5">
|
||||
{isSourceEditable ? (
|
||||
<>
|
||||
<IssueTitleInput
|
||||
@@ -190,23 +189,23 @@ export const ExternalContoursIssueMainContent = observer(function ExternalContou
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="py-4">
|
||||
<ExternalContoursRequestTraceability contourRequest={contourRequest} />
|
||||
</div>
|
||||
<ExternalContoursRequestTraceability contourRequest={contourRequest} />
|
||||
|
||||
<div className="py-4">
|
||||
<div className="mb-2 text-body-sm-medium">{t("external_contours_page.properties.section_title")}</div>
|
||||
<div className="nodedc-external-section flex flex-col gap-3 px-4 py-4">
|
||||
<div className="text-body-sm-medium">{t("external_contours_page.properties.section_title")}</div>
|
||||
<div className="flex flex-col gap-3 text-13 text-secondary">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="nodedc-external-panel flex flex-wrap items-center gap-2 px-4 py-3">
|
||||
<span className="text-tertiary">{t("priority")}</span>
|
||||
<Badge variant="neutral">{issue.priority || t("none")}</Badge>
|
||||
<span className="rounded-full bg-white/6 px-3 py-1.5 text-12 font-medium text-primary">
|
||||
{issue.priority || t("none")}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<div className="nodedc-external-panel flex flex-wrap items-center gap-2 px-4 py-3">
|
||||
<span className="text-tertiary">{t("labels")}</span>
|
||||
{issue.label_details?.length ? (
|
||||
issue.label_details.map((label) => (
|
||||
<div key={label.id} className="flex items-center gap-1 rounded-sm border border-strong px-2 py-1 text-11">
|
||||
<div key={label.id} className="flex items-center gap-1 rounded-full bg-white/6 px-3 py-1.5 text-11">
|
||||
<span className="h-2 w-2 rounded-full" style={{ backgroundColor: label.color }} />
|
||||
<span>{label.name}</span>
|
||||
</div>
|
||||
@@ -230,8 +229,12 @@ export const ExternalContoursIssueMainContent = observer(function ExternalContou
|
||||
|
||||
<ExternalContoursMirroredActivity activity={mirroredActivity} />
|
||||
|
||||
{!isSourceEditable && <div className="py-4 text-13 text-secondary">{t("external_contours_page.readonly_source_view")}</div>}
|
||||
</>
|
||||
{!isSourceEditable && (
|
||||
<div className="nodedc-external-empty px-4 py-4 text-13 text-secondary">
|
||||
{t("external_contours_page.readonly_source_view")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,14 +53,15 @@ export const ExternalContoursListItem = observer(function ExternalContoursListIt
|
||||
onClick={(e) => handleIssueRedirection(e, request.id)}
|
||||
>
|
||||
<Row
|
||||
data-active={selectedInboxIssueId === request.id}
|
||||
className={cn(
|
||||
"relative flex cursor-pointer flex-col gap-2 border border-t-transparent border-r-transparent border-b-subtle-1 border-l-transparent py-4 transition-all hover:bg-accent-primary/5",
|
||||
{ "border border-accent-strong": selectedInboxIssueId === request.id }
|
||||
"nodedc-external-card relative flex cursor-pointer flex-col gap-4 px-4 py-4 transition-all hover:bg-white/5",
|
||||
{ "ring-1 ring-accent-primary/35": selectedInboxIssueId === request.id }
|
||||
)}
|
||||
>
|
||||
<div className="space-y-1">
|
||||
<div className="space-y-2">
|
||||
<div className="relative flex items-center justify-between gap-2">
|
||||
<div className="flex items-center gap-2 text-11 font-medium text-tertiary">
|
||||
<div className="flex min-w-0 items-center gap-2 text-11 font-medium text-tertiary">
|
||||
<span>
|
||||
{issue.project_detail?.identifier || "REQ"}-{issue.sequence_id}
|
||||
</span>
|
||||
@@ -71,9 +72,11 @@ export const ExternalContoursListItem = observer(function ExternalContoursListIt
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<ExternalContourStatePill request={request} />
|
||||
<div className="shrink-0">
|
||||
<ExternalContourStatePill request={request} />
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="w-full truncate text-13">{issue.name}</h3>
|
||||
<h3 className="w-full text-15 leading-6 font-semibold text-primary">{issue.name}</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
@@ -93,7 +96,7 @@ export const ExternalContoursListItem = observer(function ExternalContoursListIt
|
||||
)}
|
||||
|
||||
{visibleLabels.map((label) => (
|
||||
<div key={label.id} className="relative flex !h-[17.5px] items-center gap-1 rounded-sm border border-strong px-1 text-11">
|
||||
<div key={label.id} className="relative flex h-6 items-center gap-1 rounded-full bg-white/6 px-2 text-11">
|
||||
<span className="h-2 w-2 rounded-full" style={{ backgroundColor: label.color }} />
|
||||
<span className="max-w-28 truncate normal-case">{label.name}</span>
|
||||
</div>
|
||||
@@ -107,7 +110,7 @@ export const ExternalContoursListItem = observer(function ExternalContoursListIt
|
||||
key={assignee.id}
|
||||
src={assignee.avatar_url || ""}
|
||||
name={assignee.display_name || "NODE.DC"}
|
||||
size="md"
|
||||
size="lg"
|
||||
showTooltip
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -65,20 +65,20 @@ export const ExternalContoursMirroredActivity = observer(function ExternalContou
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-3 py-4">
|
||||
<div className="nodedc-external-section space-y-3 px-4 py-4">
|
||||
<div className="text-body-sm-medium">{t("external_contours_page.mirror.activity_title")}</div>
|
||||
|
||||
{activity.length > 0 ? (
|
||||
<div className="space-y-3">
|
||||
{activity.map((item) => (
|
||||
<div key={item.id} className="rounded-md border border-subtle bg-surface-1 px-4 py-3">
|
||||
<div key={item.id} className="nodedc-external-panel px-4 py-3">
|
||||
<div className="text-13 text-primary">{renderMessage(item)}</div>
|
||||
<div className="mt-1 text-11 text-secondary">{renderFormattedDate(item.created_at)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-md border border-dashed border-subtle px-4 py-6 text-13 text-secondary">
|
||||
<div className="nodedc-external-empty px-4 py-6 text-13 text-secondary">
|
||||
{t("external_contours_page.mirror.activity_empty")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
+3
-3
@@ -19,7 +19,7 @@ export const ExternalContoursMirroredAttachments = observer(function ExternalCon
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="space-y-3 py-4">
|
||||
<div className="nodedc-external-section space-y-3 px-4 py-4">
|
||||
<div className="text-body-sm-medium">{t("external_contours_page.mirror.attachments_title")}</div>
|
||||
|
||||
{attachments.length > 0 ? (
|
||||
@@ -36,7 +36,7 @@ export const ExternalContoursMirroredAttachments = observer(function ExternalCon
|
||||
href={attachment.download_url || "#"}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="flex min-h-[60px] items-center justify-between gap-3 rounded-md border-[2px] border-subtle bg-surface-1 px-4 py-2 text-13 transition-colors hover:bg-surface-2"
|
||||
className="nodedc-external-panel flex min-h-[72px] items-center justify-between gap-3 px-4 py-3 text-13 transition-colors hover:bg-white/6"
|
||||
>
|
||||
<div className="flex items-center gap-3 overflow-hidden">
|
||||
<div className="h-7 w-7 flex-shrink-0">{fileIcon}</div>
|
||||
@@ -55,7 +55,7 @@ export const ExternalContoursMirroredAttachments = observer(function ExternalCon
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-md border border-dashed border-subtle px-4 py-6 text-13 text-secondary">
|
||||
<div className="nodedc-external-empty px-4 py-6 text-13 text-secondary">
|
||||
{t("external_contours_page.mirror.attachments_empty")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -19,7 +19,7 @@ export const ExternalContoursMirroredComments = observer(function ExternalContou
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="space-y-3 py-4">
|
||||
<div className="nodedc-external-section space-y-3 px-4 py-4">
|
||||
<div className="text-body-sm-medium">{t("external_contours_page.mirror.comments_title")}</div>
|
||||
|
||||
{comments.length > 0 ? (
|
||||
@@ -27,7 +27,7 @@ export const ExternalContoursMirroredComments = observer(function ExternalContou
|
||||
{comments.map((comment) => {
|
||||
const actorName = comment.actor_detail?.display_name || t("external_contours_page.mirror.system_actor");
|
||||
return (
|
||||
<div key={comment.id} className="rounded-md border border-subtle bg-surface-1 p-4">
|
||||
<div key={comment.id} className="nodedc-external-panel p-4">
|
||||
<div className="mb-3 flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar src={comment.actor_detail?.avatar_url || ""} name={actorName} size="md" />
|
||||
@@ -49,7 +49,7 @@ export const ExternalContoursMirroredComments = observer(function ExternalContou
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-md border border-dashed border-subtle px-4 py-6 text-13 text-secondary">
|
||||
<div className="nodedc-external-empty px-4 py-6 text-13 text-secondary">
|
||||
{t("external_contours_page.mirror.comments_empty")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
+8
-8
@@ -17,9 +17,9 @@ type Props = {
|
||||
};
|
||||
|
||||
const TraceabilityCell = ({ label, children }: { label: string; children: ReactNode }) => (
|
||||
<div className="rounded-md border border-subtle bg-surface-1 p-3">
|
||||
<div className="nodedc-external-panel p-4">
|
||||
<div className="text-11 font-medium text-tertiary">{label}</div>
|
||||
<div className="mt-1 min-h-6 text-13 font-medium text-secondary">{children}</div>
|
||||
<div className="mt-2 min-h-6 text-13 font-medium text-secondary">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -41,7 +41,7 @@ export const ExternalContoursRequestTraceability = observer(function ExternalCon
|
||||
const assigneeDetails = issue.assignee_details ?? [];
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-subtle bg-surface-2 p-4">
|
||||
<div className="nodedc-external-section p-5">
|
||||
<div className="mb-3">
|
||||
<h5 className="text-body-sm-medium">{t("external_contours_page.traceability.title")}</h5>
|
||||
<p className="mt-1 text-12 text-tertiary">{t("external_contours_page.traceability.description")}</p>
|
||||
@@ -73,7 +73,7 @@ export const ExternalContoursRequestTraceability = observer(function ExternalCon
|
||||
{assigneeDetails.map((assignee) => (
|
||||
<div key={assignee.id} className="flex items-center gap-2">
|
||||
<Avatar src={assignee.avatar_url || ""} name={assignee.display_name || t("common.none")} size="sm" showTooltip />
|
||||
<span className="text-12 font-medium text-secondary">{assignee.display_name}</span>
|
||||
<span className="text-13 font-medium text-secondary">{assignee.display_name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -90,13 +90,13 @@ export const ExternalContoursRequestTraceability = observer(function ExternalCon
|
||||
{requestedAt ? renderFormattedDate(requestedAt) : t("common.none")}
|
||||
</TraceabilityCell>
|
||||
|
||||
<TraceabilityCell label={t("external_contours_page.traceability.last_updated")}>
|
||||
{lastUpdatedAt ? renderFormattedDate(lastUpdatedAt) : t("common.none")}
|
||||
</TraceabilityCell>
|
||||
|
||||
<TraceabilityCell label={t("external_contours_page.traceability.due_date")}>
|
||||
{dueDate}
|
||||
</TraceabilityCell>
|
||||
|
||||
<TraceabilityCell label={t("external_contours_page.traceability.last_updated")}>
|
||||
{lastUpdatedAt ? renderFormattedDate(lastUpdatedAt) : t("common.none")}
|
||||
</TraceabilityCell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -97,11 +97,15 @@ export const ExternalContoursRoot = observer(function ExternalContoursRoot(props
|
||||
inboxIssueId={inboxIssueId.toString()}
|
||||
/>
|
||||
) : (
|
||||
<EmptyStateCompact
|
||||
assetKey="intake"
|
||||
title={t("external_contours_page.empty_state.detail_title")}
|
||||
assetClassName="size-20"
|
||||
/>
|
||||
<div className="flex h-full w-full items-center justify-center px-8">
|
||||
<div className="nodedc-external-section max-w-xl px-8 py-10">
|
||||
<EmptyStateCompact
|
||||
assetKey="intake"
|
||||
title={t("external_contours_page.empty_state.detail_title")}
|
||||
assetClassName="size-20"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -10,7 +10,6 @@ import { useTranslation } from "@plane/i18n";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import type { TInboxIssueCurrentTab } from "@plane/types";
|
||||
import { EInboxIssueCurrentTab } from "@plane/types";
|
||||
import { EHeaderVariant, Header } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
import { useProjectExternalContours } from "@/hooks/store/use-project-external-contours";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
@@ -42,18 +41,20 @@ export const ExternalContoursSidebar = observer(function ExternalContoursSidebar
|
||||
}
|
||||
}, [currentTab, filteredRequestIds, inboxIssueId, projectId, router, workspaceSlug]);
|
||||
|
||||
const currentCount = currentTab === EInboxIssueCurrentTab.CLOSED ? closedRequestIds.length : openRequestIds.length;
|
||||
|
||||
return (
|
||||
<div className="h-full w-full flex-shrink-0 border-r border-strong bg-surface-1">
|
||||
<div className="nodedc-external-sidebar-shell h-full w-full flex-shrink-0 border-r border-strong/40">
|
||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
||||
<Header variant={EHeaderVariant.SECONDARY}>
|
||||
{tabNavigationOptions.map((option) => (
|
||||
<div
|
||||
<div className="px-4 py-4">
|
||||
<div className="nodedc-filter-row-shell flex items-center gap-2 p-1">
|
||||
{tabNavigationOptions.map((option) => {
|
||||
const count = option.key === EInboxIssueCurrentTab.CLOSED ? closedRequestIds.length : openRequestIds.length;
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
key={option.key}
|
||||
data-active={currentTab === option.key}
|
||||
className={cn(
|
||||
"relative flex h-full cursor-pointer items-center gap-1 px-3 text-13 font-medium transition-all",
|
||||
currentTab === option.key ? "text-accent-primary" : "hover:text-secondary"
|
||||
"nodedc-external-tab flex flex-1 items-center justify-center gap-2 text-13 font-medium transition-all"
|
||||
)}
|
||||
onClick={() => {
|
||||
if (currentTab !== option.key) {
|
||||
@@ -62,33 +63,34 @@ export const ExternalContoursSidebar = observer(function ExternalContoursSidebar
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div>{t(option.i18n_label)}</div>
|
||||
{currentTab === option.key && (
|
||||
<div className="rounded-full bg-accent-primary/20 px-1.5 py-0.5 text-11 font-semibold text-accent-primary">
|
||||
{currentCount}
|
||||
<div>{t(option.i18n_label)}</div>
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-full px-1.5 py-0.5 text-11 font-semibold",
|
||||
currentTab === option.key ? "bg-accent-primary/15 text-accent-primary" : "bg-white/5 text-secondary"
|
||||
)}
|
||||
>
|
||||
{count}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
"absolute right-0 bottom-0 left-0 rounded-t-md border",
|
||||
currentTab === option.key ? "border-accent-strong" : "border-transparent"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Header>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="vertical-scrollbar scrollbar-md h-full w-full overflow-hidden overflow-y-auto">
|
||||
<div className="vertical-scrollbar scrollbar-md h-full w-full overflow-hidden overflow-y-auto px-4 pb-4">
|
||||
{filteredRequestIds.length > 0 ? (
|
||||
filteredRequestIds.map((requestId) => (
|
||||
<ExternalContoursListItem
|
||||
key={requestId}
|
||||
setIsMobileSidebar={setIsMobileSidebar}
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
requestId={requestId}
|
||||
/>
|
||||
))
|
||||
<div className="space-y-3">
|
||||
{filteredRequestIds.map((requestId) => (
|
||||
<ExternalContoursListItem
|
||||
key={requestId}
|
||||
setIsMobileSidebar={setIsMobileSidebar}
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
requestId={requestId}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
{currentTab === EInboxIssueCurrentTab.OPEN ? (
|
||||
|
||||
@@ -47,7 +47,7 @@ export const ExternalContoursSourceReplyBox = observer(function ExternalContours
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-3 py-4">
|
||||
<div className="nodedc-external-section space-y-3 px-4 py-4">
|
||||
<div className="text-body-sm-medium">{t("external_contours_page.reply.title")}</div>
|
||||
<TextArea
|
||||
value={comment}
|
||||
@@ -55,9 +55,15 @@ export const ExternalContoursSourceReplyBox = observer(function ExternalContours
|
||||
rows={4}
|
||||
placeholder={t("external_contours_page.reply.placeholder")}
|
||||
disabled={isSubmitting}
|
||||
className="nodedc-modal-input min-h-[7rem] resize-none !rounded-[1.2rem] !border-0 !bg-white/4"
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
<Button variant="primary" onClick={handleSubmit} disabled={isSubmitting || !comment.trim()}>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleSubmit}
|
||||
disabled={isSubmitting || !comment.trim()}
|
||||
className="nodedc-modal-primary-button"
|
||||
>
|
||||
{t("external_contours_page.reply.submit")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user