Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/30119-studio-customization-environment-line.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@object-ui/app-shell': patch
'@object-ui/i18n': patch
---

fix(app-shell): Studio's publish review sheet says a customization lives in this environment's database and moves only by `os package export` (#30119)

Clause-②: yes (widening) — new `@object-ui/i18n` key `preview.changes.customizationEnvironment` in every pack; `preview/savedToCustomization` gains `customizationEnvironmentText` and `PACKAGE_EXPORT_COMMAND`
Metadata-protocol: none

Reviewing a read-only package's customizations, the sheet adds one line under "Saved to BASE's customizations": the changes are kept in this environment's database as items of the `.custom` package it names (the id the server answered for the base), not in the project's source, and they reach another environment only through `os package export`.
14 changes: 12 additions & 2 deletions ui/packages/app-shell/src/preview/DraftChangesPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ import {
// spec-named local declaration is what `scripts/check-spec-symbol-derivation.mjs`
// refuses, and a faithful copy is exactly the fork that guard exists to prevent.
import { fetchPendingDrafts } from './usePendingDrafts.js';
import { savedToCustomizationText } from './savedToCustomization.js';
import { customizationEnvironmentText, savedToCustomizationText } from './savedToCustomization.js';
import { canonicalMetaUrlType } from '@objectstack/spec/shared';
import { resolveI18nLabel, type I18nLabel } from '@objectstack/spec/ui';
import { MetadataProtectionFields, OverrideDeclarationFields, stripReadDecorations } from '@objectstack/spec/kernel';
Expand Down Expand Up @@ -1406,7 +1406,9 @@ export interface DraftChangesPanelProps {
* objectstack ADR-0146 D3 — the label of the read-only base whose
* `<base>.custom` pair these drafts are saved to. When set, the sheet says
* so under its title ({@link savedToCustomizationText}); the host passes it
* only while it reviews a pair's drafts.
* only while it reviews a pair's drafts, so `packageId` is then the pair, and
* the sheet names it in the line that says where the changes live
* ({@link customizationEnvironmentText}, objectstack#30119).
*/
customizationOf?: string;
}
Expand Down Expand Up @@ -1702,6 +1704,14 @@ export function DraftChangesPanel({
{savedToCustomizationText(t, customizationOf)}
</p>
) : null}
{/* objectstack#30119 (ADR-0146 D2) — and where they live: this
* environment's database, as items of the pair (`packageId`, the
* pair the host reviews), moved only by `os package export`. */}
{customizationOf && packageId ? (
<p data-testid="draft-changes-environment" className="text-xs text-muted-foreground">
{customizationEnvironmentText(t, packageId)}
</p>
) : null}
</SheetHeader>
{posture === 'confirm' && entries && entries.length > 0 && !error ? (
// objectui#12517 — what the publish carries, counted off the same
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ vi.mock('@object-ui/i18n', async (importOriginal) => {

import { createI18n, I18nProvider } from '@object-ui/i18n';
import { DraftChangesPanel, computeChangeDetail } from '../DraftChangesPanel';
import { PACKAGE_EXPORT_COMMAND, customizationEnvironmentText } from '../savedToCustomization';

afterEach(() => {
vi.restoreAllMocks();
Expand Down Expand Up @@ -293,5 +294,40 @@ describe('DraftChangesPanel — names the base whose customizations it saves (ob
renderIn('en');
await waitFor(() => expect(screen.getByText('ticket')).toBeInTheDocument());
expect(screen.queryByTestId('draft-changes-saved-to')).not.toBeInTheDocument();
expect(screen.queryByTestId('draft-changes-environment')).not.toBeInTheDocument();
});

/*
* objectstack#30119 (ADR-0146 D2) — under it, where the changes live: this
* environment's database, as items of the pair the host reviews (its
* `packageId`, named as served), and the one command that moves them,
* verbatim in every pack.
*/
it('names the pair and `os package export` in the line under it, in en', async () => {
renderIn('en', { customizationOf: 'HotCRM', packageId: 'com.hotcrm.custom' });
const line = await screen.findByTestId('draft-changes-environment');
expect(line.textContent).toContain('this environment’s database');
expect(line.textContent).toContain('com.hotcrm.custom');
expect(line.textContent).toContain('os package export');
});

it('keeps the pair id and the command untranslated in zh', async () => {
renderIn('zh', { customizationOf: 'HotCRM', packageId: 'com.hotcrm.custom' });
const line = await screen.findByTestId('draft-changes-environment');
expect(line.textContent).toContain('当前环境的数据库');
expect(line.textContent).toContain('com.hotcrm.custom');
expect(line.textContent).toContain('os package export');
});
});

describe('the environment line keeps the command verbatim in every pack (objectstack#30119)', () => {
const LOCALES = ['en', 'zh', 'ja', 'ko', 'de', 'fr', 'es', 'pt', 'ru', 'ar'] as const;
it.each(LOCALES)('%s: names the pair and `os package export` once each', async (lang) => {
const i18n = createI18n({ defaultLanguage: lang, detectBrowserLanguage: false });
await i18n.changeLanguage(lang);
const text = customizationEnvironmentText(i18n.t.bind(i18n) as never, 'com.hotcrm.custom');
expect(text).not.toContain('{{');
expect(text.split('com.hotcrm.custom')).toHaveLength(2);
expect(text.split(PACKAGE_EXPORT_COMMAND)).toHaveLength(2);
});
});
23 changes: 23 additions & 0 deletions ui/packages/app-shell/src/preview/savedToCustomization.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,29 @@ export function savedToCustomizationText(t: Translate, baseLabel: string): strin
});
}

/**
* The command that moves a customization to another environment, spelled
* verbatim in every language: it is typed, not read (objectstack#30119).
*/
export const PACKAGE_EXPORT_COMMAND = 'os package export';

/**
* objectstack#30119 (ADR-0146 D2) — the sentence under "Saved to BASE's
* customizations" in the pending-changes sheet: the changes live in this
* environment's database as items of `pairId`, the `<base>.custom` package
* the server answered for the base, not in the project's source, and only
* {@link PACKAGE_EXPORT_COMMAND} takes them to another environment. True on
* every deployment, so it is not switched by the served `environment`.
*/
export function customizationEnvironmentText(t: Translate, pairId: string): string {
return t('preview.changes.customizationEnvironment', {
pair: pairId,
command: PACKAGE_EXPORT_COMMAND,
defaultValue:
'These changes are kept in this environment’s database as items of {{pair}}, not in your project’s source. They reach another environment only through {{command}}.',
});
}

/** {@link savedToCustomizationText} as an element, for the Studio item header's note line. */
export function SavedToCustomizationNote({ baseLabel, testId }: { baseLabel: string; testId?: string }) {
const { t } = useObjectTranslation();
Expand Down
1 change: 1 addition & 0 deletions ui/packages/i18n/src/locales/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4947,6 +4947,7 @@ const ar = {
title: 'التغييرات المعلّقة',
description: 'ما الذي سيغيّره النشر. تُضاف العناصر الجديدة؛ وتستبدل التحديثات النسخة المنشورة.',
savedToCustomization: 'محفوظ في تخصيصات {{package}}',
customizationEnvironment: 'تُحفظ هذه التغييرات في قاعدة بيانات هذه البيئة كعناصر في {{pair}}، وليست في الشيفرة المصدرية لمشروعك. لا تنتقل إلى بيئة أخرى إلا عبر {{command}}.',
loading: 'جارٍ تحميل التغييرات المعلّقة…',
loadFailed: 'تعذّر تحميل التغييرات المعلّقة:',
empty: 'لا شيء معلّق — نُشرت كل المسودات.',
Expand Down
1 change: 1 addition & 0 deletions ui/packages/i18n/src/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4542,6 +4542,7 @@ const de = {
title: 'Ausstehende Änderungen',
description: 'Was die Veröffentlichung ändert. Neue Elemente werden hinzugefügt; Aktualisierungen überschreiben die Live-Version.',
savedToCustomization: 'Gespeichert in den Anpassungen von {{package}}',
customizationEnvironment: 'Diese Änderungen liegen in der Datenbank dieser Umgebung, als Elemente von {{pair}}, nicht im Quellcode Ihres Projekts. In eine andere Umgebung gelangen sie nur mit {{command}}.',
loading: 'Ausstehende Änderungen werden geladen…',
loadFailed: 'Ausstehende Änderungen konnten nicht geladen werden:',
empty: 'Nichts ausstehend — alle Entwürfe wurden veröffentlicht.',
Expand Down
3 changes: 3 additions & 0 deletions ui/packages/i18n/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4003,6 +4003,9 @@ const en = {
description: 'What publishing will change. New items are added; updates overwrite the live version.',
// ADR-0146 D3 — where a customization of a read-only base is saved: its `.custom` pair.
savedToCustomization: 'Saved to {{package}}’s customizations',
// objectstack#30119 (ADR-0146 D2) — the pair lives in this environment's database and moves only by export;
// `command` is always the literal `os package export`, never translated.
customizationEnvironment: 'These changes are kept in this environment’s database as items of {{pair}}, not in your project’s source. They reach another environment only through {{command}}.',
loading: 'Loading pending changes…',
loadFailed: 'Could not load pending changes:',
empty: 'Nothing pending — every draft has been published.',
Expand Down
1 change: 1 addition & 0 deletions ui/packages/i18n/src/locales/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4644,6 +4644,7 @@ const es = {
title: 'Cambios pendientes',
description: 'Lo que cambiará al publicar. Los elementos nuevos se añaden; las actualizaciones sobrescriben la versión publicada.',
savedToCustomization: 'Guardado en las personalizaciones de {{package}}',
customizationEnvironment: 'Estos cambios se guardan en la base de datos de este entorno, como elementos de {{pair}}, no en el código fuente de su proyecto. Solo llegan a otro entorno mediante {{command}}.',
loading: 'Cargando cambios pendientes…',
loadFailed: 'No se pudieron cargar los cambios pendientes:',
empty: 'Nada pendiente — todos los borradores se han publicado.',
Expand Down
1 change: 1 addition & 0 deletions ui/packages/i18n/src/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4640,6 +4640,7 @@ const fr = {
title: 'Modifications en attente',
description: 'Ce que la publication changera. Les nouveaux éléments sont ajoutés ; les mises à jour remplacent la version en ligne.',
savedToCustomization: 'Enregistré dans les personnalisations de {{package}}',
customizationEnvironment: 'Ces modifications sont stockées dans la base de données de cet environnement, comme éléments de {{pair}}, et non dans les sources de votre projet. Elles ne passent dans un autre environnement que par {{command}}.',
loading: 'Chargement des modifications en attente…',
loadFailed: 'Impossible de charger les modifications en attente :',
empty: 'Rien en attente — tous les brouillons ont été publiés.',
Expand Down
1 change: 1 addition & 0 deletions ui/packages/i18n/src/locales/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4544,6 +4544,7 @@ const ja = {
title: '保留中の変更',
description: '公開で何が変わるか。新規項目は追加され、更新は公開中のバージョンを上書きします。',
savedToCustomization: '{{package}} のカスタマイズに保存',
customizationEnvironment: 'これらの変更はこの環境のデータベースに {{pair}} の項目として保存され、プロジェクトのソースには含まれません。別の環境へは {{command}} でのみ移せます。',
loading: '保留中の変更を読み込み中…',
loadFailed: '保留中の変更を読み込めませんでした:',
empty: '保留中の変更はありません — すべてのドラフトが公開済みです。',
Expand Down
1 change: 1 addition & 0 deletions ui/packages/i18n/src/locales/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4541,6 +4541,7 @@ const ko = {
title: '대기 중인 변경 사항',
description: '게시 시 변경되는 내용입니다. 새 항목은 추가되고 업데이트는 게시된 버전을 덮어씁니다.',
savedToCustomization: '{{package}}의 사용자 지정에 저장됨',
customizationEnvironment: '이 변경 사항은 이 환경의 데이터베이스에 {{pair}}의 항목으로 저장되며 프로젝트 소스에는 없습니다. 다른 환경으로는 {{command}}로만 옮길 수 있습니다.',
loading: '대기 중인 변경 사항 불러오는 중…',
loadFailed: '대기 중인 변경 사항을 불러오지 못했습니다:',
empty: '대기 중인 항목 없음 — 모든 드래프트가 게시되었습니다.',
Expand Down
1 change: 1 addition & 0 deletions ui/packages/i18n/src/locales/pt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4639,6 +4639,7 @@ const pt = {
title: 'Alterações pendentes',
description: 'O que a publicação vai mudar. Itens novos são adicionados; atualizações sobrescrevem a versão publicada.',
savedToCustomization: 'Salvo nas personalizações de {{package}}',
customizationEnvironment: 'Estas alterações ficam no banco de dados deste ambiente, como itens de {{pair}}, e não no código-fonte do seu projeto. Só chegam a outro ambiente por meio de {{command}}.',
loading: 'Carregando alterações pendentes…',
loadFailed: 'Não foi possível carregar as alterações pendentes:',
empty: 'Nada pendente — todos os rascunhos foram publicados.',
Expand Down
1 change: 1 addition & 0 deletions ui/packages/i18n/src/locales/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4755,6 +4755,7 @@ const ru = {
title: 'Ожидающие изменения',
description: 'Что изменит публикация. Новые элементы добавляются; обновления перезаписывают опубликованную версию.',
savedToCustomization: 'Сохранено в настройках {{package}}',
customizationEnvironment: 'Эти изменения хранятся в базе данных этой среды как элементы {{pair}}, а не в исходном коде проекта. В другую среду они попадают только через {{command}}.',
loading: 'Загрузка ожидающих изменений…',
loadFailed: 'Не удалось загрузить ожидающие изменения:',
empty: 'Ничего не ожидает — все черновики опубликованы.',
Expand Down
1 change: 1 addition & 0 deletions ui/packages/i18n/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3463,6 +3463,7 @@ const zh = {
title: '待发布的变更',
description: '发布将带来以下变更。“新增”为全新内容,“更新”将覆盖线上版本。',
savedToCustomization: '保存到 {{package}} 的定制',
customizationEnvironment: '这些变更保存在当前环境的数据库里,是 {{pair}} 的条目,不在项目源码中;只有通过 {{command}} 才能带到其他环境。',
loading: '正在加载待发布变更…',
loadFailed: '无法加载待发布变更:',
empty: '没有待发布的内容 —— 所有草稿均已发布。',
Expand Down
Loading