Skip to content

Commit 144702c

Browse files
fix(app-shell): label the approval decision panel's record summary "As submitted" (#27502) (#27635)
<!-- claude-projects-attribution: {"github_login":"os-bill"} --> _Requested by **Bill** · [project thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgDFH4S5pR1C8YvYCWWsZfXx)_ ## 维护者速读 - 改了什么:审批请求页决策面板里的记录摘要加上「提交时的内容」标题(十个语言包),摘要本身的数据与读取不变。 - 为什么改:摘要是目标记录提交时的快照,状态条读的是审批请求本身;审批后两者状态不同,没有标题时快照读起来像记录的当前状态,页面自相矛盾。 - 风险与回滚:只加一个标题和一个语言包键,无新读取、无契约变化;回滚即 revert 本 PR。 - 先看:`RecordSnapshotSummary.tsx`(section 与标题)、`ApprovalDecisionPanel.decidedSnapshot-27502.test.tsx`、`i18n/src/locales/zh.ts` 新键。 - 两分钟验证:`pnpm --dir ui exec vitest run packages/app-shell/src/views/approval-decision/`,预期 6 个文件 57 个测试全绿。 - 需要你做的:无,席位落地。 Fixes #27502 Clause-②: no Metadata-protocol: none ## What changes - **The summary is headed "As submitted".** `RecordSnapshotSummary`, the decision panel's summary of the request's record snapshot, is now a `section` whose heading reads the new pack row `approvalsInbox.snapshotAsSubmitted` ("As submitted", zh "提交时的内容") in the muted label style of the panel's "Waiting on"; the heading is the section's accessible name (`aria-labelledby`). It is drawn only when the summary draws content, and at any request status: it names the source, not a status. - **Nothing else moves.** The rows, the lead amount, the reads (the request row's `payload` / `payload_display` and the target object's definition) and the redaction are unchanged. No record is read; the record's current values stay on the Details tab's `record_id` pointer card. - **Copy.** One key in all ten `@object-ui/i18n` packs (en, ar, de, es, fr, ja, ko, pt, ru, zh). - **Docs.** The headers of `RecordSnapshotSummary.tsx` and `ApprovalDecisionPanel.tsx` (part 1) say the panel draws the submitted snapshot (objectstack#10749) and why it is labelled; the app-shell README's panel section says the same. One patch changeset for `@object-ui/app-shell` and `@object-ui/i18n`. ## Why The served `sys_approval_request_detail` page draws two faces that read two different rows: - the `record:highlights` strip reads the REQUEST row (`process_label`, `status`, `current_step_label`, `submitter_id`, `updated_at`), so after a decision it reads "Status Approved"; - the decision panel's summary reads `request.payload`, the target record as SUBMITTED, kept at rest as the request's evidence of what was submitted and served redacted per reader (objectstack#10749, ruling record 5377367175, Option B). It keeps "Status Submitted" and "Approved 0.00". Unlabelled, the snapshot read as the record's current state beside the request's status. The fork between a live read of the target and the labelled snapshot was ruled B (ruling record 6499914328 on this card): keep the snapshot, label it as the submitted values. ## Tests - `ApprovalDecisionPanel.decidedSnapshot-27502.test.tsx`, four pins over the served row shape: - a decided request (`status: 'approved'`, the snapshot's own `status: 'submitted'` under a declared `select` field): the summary is the region the pack row names, its status row reads `Submitted`, and nobody is waited on; - a pending request carries the heading too; - CONTROL: a snapshot with only undeclared keys draws no summary and no heading; - the key is defined and non-empty in all ten packs. <details><summary>Ablation (one-shot, not kept)</summary> Fix committed first. `node scripts/ablation-replace.mjs` deleted the heading's copy expression in `RecordSnapshotSummary.tsx` (anchor x1 -> x0, blob `c05edc2b` -> `8ff34b0a`): the decided and pending pins turned red, the control and the ten-pack pin stayed green (2 failed, 2 passed). Restored: blob after restore equals HEAD `c05edc2b`, `git diff HEAD` empty. The subject resolves from source (relative import; `@object-ui/i18n` is aliased to `src` in `ui/vitest.config.mts`), so no `dist` leg applies. </details> ## Verification <details><summary>Local runs at eda86be (merge base 492703e)</summary> - `pnpm --dir ui exec vitest run packages/app-shell/src/views/approval-decision/`: 6 files, 57 tests passed. - Controls: `packages/i18n/` plus the console's `approvalRequestsDataSource.servedPageSummary-12483` and `servedPageSummaryAmount-12534`: 92 files, 1403 passed, 13 skipped. - `pnpm --filter @object-ui/app-shell run type-check` exit 0; `pnpm --filter @object-ui/i18n run type-check` exit 0. - `node scripts/pm/dispatch-gates.mjs --commands` derived 34 commands; all 34 ran with exit 0, including `node scripts/check-sdui-manifest.mjs` and `pnpm --dir ui exec vitest run scripts/`; `--ran` reconciliation: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. Also `pnpm check:changeset-fixed` exit 0. - eslint on the three touched source and test files exit 0; `pnpm check:new-line-citations` (ui) 0 new citations. - Not run locally, CI's: `pnpm lint` (repo-wide), the UI test shards, `Type Check UI` / workspace type-check lanes, `Build & E2E UI`, `Live E2E UI`. </details> ## Acceptance notes - **The card's acceptance ("the page does not contradict itself") is met by labelling the source**, per ruling 6499914328: the summary now says it holds the submitted values, so it no longer reads against the request's status in the strip. - **Correction to the triage line.** Triage said the summary should read "the same read the strip uses". It cannot: the strip reads the request row, never the target record. The target's current values are on the Details tab's pointer card, which an approver without read access to the target may be refused; the snapshot is how such an approver sees what they decide on (objectstack#10749). - **The x3 beside "1 eligible approver" is not this card.** `PendingApprovers` draws each chip's slot count from `approverChips` (one person holding three quorum slots is one chip "x3"), while the progress line counts distinct people (`eligibleApproverCount`, objectui#12781). Different read, untouched here; class: a (user-visible copy with no explanation of the count) · reach: this card's own walkthrough (QA-source #27377 track 1) on the served request page · dedupe words: approver chip count, quorum slots, eligible approver, x3. The seat files it separately. - **Serial constraint.** Open PR #27533 edits `snapshotSummaryPlan.ts`; this PR does not touch that file. - The after-PR assignee and review request are skipped on purpose: the maintainer's rule is that a PR reaches a human only after the seat accepts it. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (not touched) - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; `pnpm lint` passes. (touched files linted locally; repo-wide run is CI's) - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. - [x] 6. `packages/spec` touched: its generated artifacts are regenerated and committed. (not touched) - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. - [x] The *Before Submitting* list in CONTRIBUTING.md is done. Implemented in session `session_01Utp6CaRUZ2GQ5mSm8Z7Aqd` (`domain:ui#4` card thread, claim 6499632812). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Utp6CaRUZ2GQ5mSm8Z7Aqd --- _Generated by [Claude Code](https://claude.ai/code/session_01Utp6CaRUZ2GQ5mSm8Z7Aqd)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent efa2915 commit 144702c

15 files changed

Lines changed: 217 additions & 5 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@object-ui/app-shell': patch
3+
'@object-ui/i18n': patch
4+
---
5+
6+
fix(app-shell): the approval decision panel's record summary is headed "As submitted" (#27502)
7+
8+
Clause-②: yes (widening)
9+
Metadata-protocol: none
10+
11+
The summary the approval request page's decision panel (`record:approval_decision`) draws comes from the request's record snapshot: the target record as it was submitted, kept as the request's evidence of what was submitted. After a decision the request's status, which the page's highlights show, moves on while the snapshot keeps the submitted values, so the summary read as the record's current state and the page contradicted itself. The summary is now a section headed "As submitted", in the muted label style of the panel's "Waiting on", and that heading is its accessible name. A pending request carries the heading too, and a summary that draws nothing draws no heading. The values, the rows and the reads are unchanged: the panel still reads no record, and the record's current values stay on the Details tab's record card.
12+
13+
New language-pack key in all ten packs: `approvalsInbox.snapshotAsSubmitted`.

‎ui/packages/app-shell/README.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1248,7 +1248,11 @@ key that only looks like an amount by its name never leads. A key the object
12481248
does not declare, a framework column, an
12491249
unresolved reference id, a field declared `hidden: true` and a field the
12501250
reader may not read take no row. Without the target object's definition, the
1251-
summary is not drawn.
1251+
summary is not drawn. The summary is headed "As submitted"
1252+
(`approvalsInbox.snapshotAsSubmitted`): the snapshot is the target record as it
1253+
was submitted, not its current state, so after a decision its values can
1254+
differ from the request's status in the page's highlights. The record's
1255+
current values are the Details tab's record card.
12521256

12531257
Two more parts come from the same request row. A request no person submitted
12541258
(its `submitter_id` absent or null, as the approvals get serves a request a
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/**
2+
* ObjectUI
3+
* Copyright (c) 2024-present ObjectStack Inc.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
/**
10+
* After a decision, the approval request page must not tell two stories
11+
* (objectstack#27502, ruling record 6499914328). The page's highlights read the
12+
* REQUEST row, whose status moves to approved; the decision panel's summary
13+
* reads the request's `payload`, the target record as it was SUBMITTED, which
14+
* keeps its submitted status (objectstack#10749, ruling record 5377367175: the
15+
* snapshot is kept as the evidence of what was submitted). So the summary is
16+
* labelled as the submitted values: a section whose accessible name is the
17+
* pack's `approvalsInbox.snapshotAsSubmitted` row, holding the snapshot's own
18+
* values.
19+
*
20+
* The rows are in the shape the approvals get serves the request page after a
21+
* reload: a decided request, its snapshot still at submission. The declared
22+
* actions bar is doubled: what is under test is the summary's label.
23+
*/
24+
25+
import * as React from 'react';
26+
import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
27+
import { cleanup, render, screen, waitFor, within } from '@testing-library/react';
28+
29+
vi.mock('../DeclaredActionsBar.js', () => ({
30+
DeclaredActionsBar: () => <div data-testid="declared-actions-probe" />,
31+
}));
32+
33+
import { MetadataCtx, RecordContextProvider } from '@object-ui/react';
34+
import { createI18n, I18nProvider } from '@object-ui/i18n';
35+
import { builtInLocales } from '@object-ui/i18n/locales';
36+
import { ApprovalDecisionRenderer } from './ApprovalDecisionPanel';
37+
import { resetApproverDirectoryCache } from '../../hooks/useApproverDirectory';
38+
39+
const i18n = createI18n({ defaultLanguage: 'en', detectBrowserLanguage: false });
40+
41+
/** The heading's pack key, and what one pack says for it. */
42+
const HEADING_KEY = 'approvalsInbox.snapshotAsSubmitted';
43+
const packHeading = (language: string): unknown =>
44+
HEADING_KEY.split('.').reduce<unknown>(
45+
(node, part) => (node as Record<string, unknown> | undefined)?.[part],
46+
(builtInLocales as Record<string, unknown>)[language],
47+
);
48+
49+
/** The pack row the heading must come from, rendered by the instance under test. */
50+
const HEADING = String(i18n.t(HEADING_KEY));
51+
52+
const EXPENSE_REPORT = {
53+
name: 'showcase_expense_report',
54+
fields: {
55+
title: { type: 'text', label: 'Title' },
56+
status: {
57+
type: 'select',
58+
label: 'Status',
59+
options: [
60+
{ value: 'submitted', label: 'Submitted' },
61+
{ value: 'approved', label: 'Approved' },
62+
],
63+
},
64+
approved_amount: { type: 'currency', label: 'Approved' },
65+
},
66+
};
67+
68+
const seen: string[] = [];
69+
const metadata = {
70+
getItem: async (type: string, name: string) => {
71+
seen.push(`${type}:${name}`);
72+
return type === 'object' && name === EXPENSE_REPORT.name ? EXPENSE_REPORT : null;
73+
},
74+
};
75+
76+
/** The target record as it was submitted: it does not move on a decision. */
77+
const SUBMITTED = { title: 'EXP-DEMO', status: 'submitted', approved_amount: 0 };
78+
79+
function requestRow(status: string, payload: Record<string, unknown> = SUBMITTED): Record<string, unknown> {
80+
return {
81+
id: 'areq_27502',
82+
process_name: 'flow:showcase_expense_committee',
83+
object_name: EXPENSE_REPORT.name,
84+
record_id: 'rec_1',
85+
status,
86+
submitter_id: 'u_dev_admin',
87+
pending_approvers: status === 'pending' ? ['u_dev_admin'] : [],
88+
pending_approver_names: { u_dev_admin: 'Dev Admin' },
89+
viewer: { can_act: status === 'pending', is_submitter: false, can_override: false },
90+
payload,
91+
};
92+
}
93+
94+
beforeEach(() => {
95+
resetApproverDirectoryCache();
96+
seen.length = 0;
97+
});
98+
99+
afterEach(() => {
100+
cleanup();
101+
});
102+
103+
function mount(data: Record<string, unknown>): HTMLElement {
104+
render(
105+
<I18nProvider instance={i18n}>
106+
<MetadataCtx.Provider value={metadata as never}>
107+
<RecordContextProvider objectName="sys_approval_request" recordId={data.id as string} data={data}>
108+
<ApprovalDecisionRenderer />
109+
</RecordContextProvider>
110+
</MetadataCtx.Provider>
111+
</I18nProvider>,
112+
);
113+
return screen.getByTestId('approval-decision-panel');
114+
}
115+
116+
describe('ApprovalDecisionPanel — the summary is labelled as the submitted values (objectstack#27502)', () => {
117+
it('a decided request: the snapshot keeps its submitted status, under the heading, and nobody is waited on', async () => {
118+
const panel = mount(requestRow('approved'));
119+
const summary = await within(panel).findByTestId('record-snapshot-summary');
120+
121+
// The decided state the reload serves: nobody is waited on any more.
122+
expect(within(panel).queryByTestId('approval-decision-pending-approvers')).toBeNull();
123+
// The summary is the region the heading names, and the heading is the pack row.
124+
expect(HEADING).not.toBe(HEADING_KEY);
125+
expect(within(panel).getByRole('region', { name: HEADING })).toBe(summary);
126+
expect(summary.querySelector('[data-summary-heading]')?.textContent).toBe(HEADING);
127+
// What the section holds is the snapshot as submitted, under the declared select field.
128+
const status = summary.querySelector('[data-summary-field="status"]');
129+
expect(status?.textContent).toBe('StatusSubmitted');
130+
});
131+
132+
it('a pending request carries the heading too: it names the source, not a status', async () => {
133+
const panel = mount(requestRow('pending'));
134+
const summary = await within(panel).findByTestId('record-snapshot-summary');
135+
136+
expect(within(panel).getByTestId('approval-decision-pending-approvers')).toBeTruthy();
137+
expect(within(panel).getByRole('region', { name: HEADING })).toBe(summary);
138+
});
139+
140+
it('CONTROL: a summary that draws nothing draws no heading', async () => {
141+
// Only keys the object does not declare: the definition answers, and no row is drawn.
142+
const panel = mount(requestRow('approved', { legacy_note: 'x' }));
143+
144+
await waitFor(() => expect(seen).toContain(`object:${EXPENSE_REPORT.name}`));
145+
await new Promise((r) => setTimeout(r, 0));
146+
expect(within(panel).queryByTestId('record-snapshot-summary')).toBeNull();
147+
expect(within(panel).queryByText(HEADING)).toBeNull();
148+
});
149+
150+
it('the heading copy is a key in all ten language packs', () => {
151+
const languages = Object.keys(builtInLocales);
152+
expect(languages).toHaveLength(10);
153+
for (const language of languages) {
154+
const value = packHeading(language);
155+
expect(typeof value, `${language} defines ${HEADING_KEY}`).toBe('string');
156+
expect((value as string).trim(), `${language} defines ${HEADING_KEY}`).not.toBe('');
157+
}
158+
});
159+
});

‎ui/packages/app-shell/src/views/approval-decision/ApprovalDecisionPanel.tsx‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@
2323
* declared labels and through the record page's own cells, with the
2424
* object's `hidden: true` fields and the reader's unreadable fields left
2525
* out. `RecordSnapshotSummary` draws it from the bound row's `payload`
26-
* and `payload_display`; see its header for what it reads.
26+
* and `payload_display`: the target record as SUBMITTED, kept as the
27+
* request's evidence of what was submitted (objectstack#10749), never a
28+
* live read. It is headed "As submitted" so a decided request's snapshot
29+
* never reads as the record's current state (objectstack#27502); the
30+
* current record is the Details tab's pointer card. See its header for
31+
* what it reads.
2732
* 2. **Where the flow stands** (objectui#12491, objectstack#26668) — the
2833
* vertical step line of this request's own route, the bound row's
2934
* `flow_route` as the approvals get serves it: the steps its run passed,

‎ui/packages/app-shell/src/views/approval-decision/RecordSnapshotSummary.tsx‎

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,22 @@
4848
* refused read), nothing is drawn: without the declarations there is no
4949
* declared label, no face and no `hidden` to honour.
5050
*
51+
* ## Labelled as the submitted values (objectstack#27502)
52+
*
53+
* The snapshot is the record as it was SUBMITTED, kept at rest as the
54+
* request's evidence of what was submitted and served redacted per reader
55+
* (objectstack#10749, ruling record 5377367175, Option B). It is not the
56+
* record's current state: after a decision the record's own status moves on
57+
* while the snapshot's stays. So the summary is a section headed "As
58+
* submitted" (`approvalsInbox.snapshotAsSubmitted`, in the muted label style
59+
* of the panel's "Waiting on"), its accessible name, and a snapshot value
60+
* never reads as the record's current one beside the page's highlights, which
61+
* read the request row (ruling record 6499914328 on objectstack#27502). The
62+
* heading names the source, not a status, so a pending request carries it
63+
* too; a summary that draws nothing draws no heading. The record's current
64+
* values are the Details tab's pointer card's to show, a live read of the
65+
* target that the reader may be refused; this face reads no record.
66+
*
5167
* ## Cost
5268
*
5369
* A row with no snapshot asks for nothing. Otherwise the definition is
@@ -72,7 +88,7 @@ import { isDeadlineSettled, withoutDeniedFields } from '@object-ui/core';
7288
import { getCellRenderer, resolveCellRendererType } from '@object-ui/fields';
7389
import { useSafeFieldLabel } from '@object-ui/i18n';
7490
import { usePermissions } from '@object-ui/permissions';
75-
import { useMetadataItem, usePredicateScope } from '@object-ui/react';
91+
import { useMetadataItem, useObjectTranslation, usePredicateScope } from '@object-ui/react';
7692
import type { FieldMetadata } from '@object-ui/types';
7793
import { distinctLabels, planSnapshotSummary, type FieldDef } from './snapshotSummaryPlan.js';
7894

@@ -151,6 +167,8 @@ function DeclaredSummary({ target, fields, shown, display }: {
151167
const summed = planSnapshotSummary(shown, fields, display).summed;
152168
const child = useMetadataItem('object', summed);
153169
const { fieldLabel } = useSafeFieldLabel();
170+
const { t } = useObjectTranslation();
171+
const headingId = React.useId();
154172
if (summed !== null && child.loading) return null;
155173

156174
// An unreadable definition leaves every `sum` an ordinary row.
@@ -172,7 +190,10 @@ function DeclaredSummary({ target, fields, shown, display }: {
172190
);
173191

174192
return (
175-
<div className="space-y-3" data-testid="record-snapshot-summary">
193+
<section className="space-y-3" aria-labelledby={headingId} data-testid="record-snapshot-summary">
194+
<div id={headingId} className="text-[11px] text-muted-foreground mb-1" data-summary-heading>
195+
{String(t('approvalsInbox.snapshotAsSubmitted', { defaultValue: 'As submitted' }))}
196+
</div>
176197
{plan.lead && (
177198
<div data-summary-lead={plan.lead}>
178199
<div className="text-[11px] text-muted-foreground">{labelOf.get(plan.lead)}</div>
@@ -191,6 +212,6 @@ function DeclaredSummary({ target, fields, shown, display }: {
191212
))}
192213
</div>
193214
)}
194-
</div>
215+
</section>
195216
);
196217
}

‎ui/packages/i18n/src/locales/ar.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4500,6 +4500,7 @@ const ar = {
45004500
progressBar: 'تقدم القرار',
45014501
declaredActions: 'الإجراءات',
45024502
decisionPanelOffRequestPage: 'لوحة قرار الموافقة تعمل فقط في صفحات طلبات الموافقة.',
4503+
snapshotAsSubmitted: 'كما قُدِّم',
45034504
attachmentChip: 'مرفق',
45044505
attachmentOpenFailed: 'تعذّر فتح المرفق — يرجى المحاولة مرة أخرى',
45054506
approveOneTitle: 'الموافقة على "{{title}}"؟',

‎ui/packages/i18n/src/locales/de.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4163,6 +4163,7 @@ const de = {
41634163
progressBar: 'Entscheidungsfortschritt',
41644164
declaredActions: 'Aktionen',
41654165
decisionPanelOffRequestPage: 'Der Entscheidungsbereich für Genehmigungen funktioniert nur auf Seiten von Genehmigungsanfragen.',
4166+
snapshotAsSubmitted: 'Wie eingereicht',
41664167
attachmentChip: 'Anhang',
41674168
attachmentOpenFailed: 'Anhang konnte nicht geöffnet werden – bitte erneut versuchen',
41684169
approveOneTitle: '„{{title}}“ genehmigen?',

‎ui/packages/i18n/src/locales/en.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4944,6 +4944,7 @@ const en = {
49444944
progressBar: 'Decision progress',
49454945
declaredActions: 'Actions',
49464946
decisionPanelOffRequestPage: 'The approval decision panel only works on approval request pages.',
4947+
snapshotAsSubmitted: 'As submitted',
49474948
attachmentChip: 'Attachment',
49484949
attachmentOpenFailed: 'Could not open the attachment — please try again',
49494950
approveOneTitle: 'Approve "{{title}}"?',

‎ui/packages/i18n/src/locales/es.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4248,6 +4248,7 @@ const es = {
42484248
progressBar: 'Progreso de la decisión',
42494249
declaredActions: 'Acciones',
42504250
decisionPanelOffRequestPage: 'El panel de decisión de aprobación solo funciona en las páginas de solicitudes de aprobación.',
4251+
snapshotAsSubmitted: 'Tal como se envió',
42514252
attachmentChip: 'Adjunto',
42524253
attachmentOpenFailed: 'No se pudo abrir el archivo adjunto: inténtalo de nuevo',
42534254
approveOneTitle: '¿Aprobar «{{title}}»?',

‎ui/packages/i18n/src/locales/fr.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4244,6 +4244,7 @@ const fr = {
42444244
progressBar: 'Avancement de la décision',
42454245
declaredActions: 'Actions',
42464246
decisionPanelOffRequestPage: 'Le panneau de décision d\'approbation ne fonctionne que sur les pages de demande d\'approbation.',
4247+
snapshotAsSubmitted: 'Tel que soumis',
42474248
attachmentChip: 'Pièce jointe',
42484249
attachmentOpenFailed: "Impossible d'ouvrir la pièce jointe — veuillez réessayer",
42494250
approveOneTitle: 'Approuver « {{title}} » ?',

0 commit comments

Comments
 (0)