(null);
// `details`: the lines a refusal carries under its sentence, in the server's
- // own words (objectui#13160).
+ // own words (objectui#13160). `heading`: a console sentence, in the reader's
+ // language, drawn above a refusal that is then shown whole rather than cut
+ // to its first sentence (objectstack#31003).
const [msg, setMsg] = React.useState<{
kind: 'ok' | 'err';
text: string;
details?: readonly string[];
+ heading?: string;
} | null>(null);
// ADR-0033 — pending DRAFT items bound to this package. AI-authored metadata
// lands as drafts that the active-only browsers hide, so without this the
@@ -629,6 +640,14 @@ export function PackageDetailSheet({
kind: 'err',
text: e?.message ?? t('engine.packages.detail.actionFailed', locale),
...(e instanceof PackagePublishRefusal ? { details: e.reasons } : {}),
+ // objectstack#31003 — the disable door refuses while an enabled
+ // validation rule reads a field the package adds. The server's sentence
+ // names every such rule (package · object · rule) and then the remedy,
+ // so it is drawn whole under a heading in the reader's language: cut to
+ // its first sentence, the remedy sat behind "More".
+ ...(errorCodeIs(e, DISABLE_RESTRICTED_BY_VALIDATION_RULE)
+ ? { heading: t('engine.packages.detail.disableRestricted', locale) }
+ : {}),
});
} finally {
setBusy(null);
@@ -1174,8 +1193,22 @@ export function PackageDetailSheet({
}`}
>
{/* A refusal leads with its first sentence; the rest of the
- server's text is behind "More" (objectui#13332). */}
- {msg.kind === 'err' ? : msg.text}
+ server's text is behind "More" (objectui#13332). One with a
+ heading is drawn whole under it (objectstack#31003). */}
+ {msg.kind === 'err' && msg.heading ? (
+ <>
+
+ {msg.heading}
+
+
+ {msg.text}
+
+ >
+ ) : msg.kind === 'err' ? (
+
+ ) : (
+ msg.text
+ )}
{msg.details && msg.details.length > 0 && (
// Not capped: the sheet scrolls (`overflow-y-auto`), as it does
// for the pending-drafts list above. `break-words` keeps a long
@@ -1301,6 +1334,13 @@ export function PackageDetailSheet({
{t('engine.packages.detail.disableDescription', locale)}
+ {/* objectstack#31003 — the served `disableable` says what the
+ package allows, not whether a rule elsewhere still reads its
+ fields; the door answers that only when asked, so the dialog
+ says the disable can still be refused. */}
+
+ {t('engine.packages.detail.disableMayBeRefused', locale)}
+
diff --git a/ui/packages/i18n/src/namespaces/studio/en.ts b/ui/packages/i18n/src/namespaces/studio/en.ts
index bf19cf0ce61..b026048bdbe 100644
--- a/ui/packages/i18n/src/namespaces/studio/en.ts
+++ b/ui/packages/i18n/src/namespaces/studio/en.ts
@@ -1933,6 +1933,8 @@ const STUDIO_EN: Record = {
'engine.packages.detail.disable': 'Disable',
'engine.packages.detail.disableTitle': 'Disable "{name}"?',
'engine.packages.detail.disableDescription': 'The package is taken out of service for every user until it is enabled again. No records are deleted, and you can enable it again at any time.',
+ 'engine.packages.detail.disableMayBeRefused': 'The disable is refused while an active validation rule reads a field this package adds; the refusal names each such rule.',
+ 'engine.packages.detail.disableRestricted': 'This package cannot be disabled while an active validation rule reads a field it adds. Switch off or change each rule named below first, or keep the package enabled.',
'engine.packages.detail.export': 'Export',
'engine.packages.detail.exporting': 'Exporting…',
'engine.packages.detail.deletePackage': 'Delete package',
diff --git a/ui/packages/i18n/src/namespaces/studio/zh.ts b/ui/packages/i18n/src/namespaces/studio/zh.ts
index 3fcc9cbfd0a..210c932216b 100644
--- a/ui/packages/i18n/src/namespaces/studio/zh.ts
+++ b/ui/packages/i18n/src/namespaces/studio/zh.ts
@@ -1745,6 +1745,8 @@ const STUDIO_ZH: Record = {
'engine.packages.detail.disable': '禁用',
'engine.packages.detail.disableTitle': '禁用「{name}」?',
'engine.packages.detail.disableDescription': '在重新启用之前,此软件包将对所有用户停止服务。不会删除任何记录,您可以随时重新启用。',
+ 'engine.packages.detail.disableMayBeRefused': '如果有生效中的验证规则读取此软件包添加的字段,禁用会被拒绝,拒绝信息会逐条列出这些规则。',
+ 'engine.packages.detail.disableRestricted': '有生效中的验证规则读取此软件包添加的字段,因此暂时无法禁用。请先停用或修改下方列出的每条规则,或保持此软件包启用。',
'engine.packages.detail.export': '导出',
'engine.packages.detail.exporting': '导出中…',
'engine.packages.detail.deletePackage': '删除软件包',