Skip to content

Commit 7cb199b

Browse files
os-zhuangclaude
andauthored
feat(approvals): label pending-approver chips with their group (objectui#2807) (#2811)
The drawer's 'waiting on' chips now key their collapse by (name, group) using the framework's pending_approver_groups: the same person filling two groups stays two labeled chips (Dev Admin · finance / · legal), one group filled twice collapses to a single chip with a count. Group renders as a muted · <group> sub-tag. Degrades to plain dedupe + count when no group data present. Rebased onto latest main (resolves overlap with the #2810 admin-override change in the same file). Closes objectui#2807 (UI half) Claude-Session: https://claude.ai/code/session_01Cu48mLFUdRBmMh8Z8R3CVz Co-authored-by: Claude <noreply@anthropic.com>
1 parent dcb90e8 commit 7cb199b

3 files changed

Lines changed: 56 additions & 12 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
"@object-ui/console": patch
3+
---
4+
5+
feat(approvals): label pending-approver chips with their group (objectui#2807)
6+
7+
Follow-up to #2762 P1-2. The dedupe pass collapsed repeated "waiting on"
8+
chips to one with a `×N` count, but couldn't say *which* group (finance /
9+
legal / …) each pending approver represented in a 会签 (per_group) request —
10+
the data wasn't there. With the framework now emitting
11+
`pending_approver_groups` (`@objectstack/plugin-approvals`), the drawer:
12+
13+
- keys the chip collapse by **(name, group)** — the same person filling two
14+
different groups stays two labeled chips (`Dev Admin · finance`,
15+
`Dev Admin · legal`), while one group filled twice collapses to a single
16+
chip with a count;
17+
- renders the group as a muted `· <group>` sub-tag on the chip.
18+
19+
Degrades cleanly: with no group data (non-`per_group`, or an older backend)
20+
the key is the name alone, keeping the plain dedupe + `×N` behavior.

apps/console/src/pages/system/ApprovalsInboxPage.tsx

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -161,27 +161,39 @@ function submitterDisplay(r: ApprovalRequestRow): string {
161161
function approverDisplay(a: string, r: ApprovalRequestRow): string {
162162
return r.pending_approver_names?.[a] || formatIdentity(a);
163163
}
164+
/** The group(s) a pending approver represents (会签), joined for display. */
165+
function approverGroup(a: string, r: ApprovalRequestRow): string | undefined {
166+
const gs = r.pending_approver_groups?.[a];
167+
return gs && gs.length ? gs.join(' / ') : undefined;
168+
}
164169
/**
165-
* Dedupe the pending-approver chips by display label (#2762 P1-2): a person
166-
* who fills more than one approver slot showed up as N identical chips. Collapse
167-
* them to one chip carrying a count, preserving first-seen order; the tooltip
168-
* keeps every underlying id so the raw slots stay inspectable.
170+
* Collapse the pending-approver chips (#2762 P1-2), now keyed by (name, group)
171+
* so 会签 comprehension is preserved (objectui#2807): the same person filling
172+
* two *different* groups stays two labeled chips (finance / legal), while a
173+
* person filling one group twice collapses to a single chip with a count. When
174+
* no group data is present (non-`per_group`, or an older backend) the key is the
175+
* name alone, degrading to the plain dedupe + count. First-seen order is kept;
176+
* the tooltip keeps every underlying id so the raw slots stay inspectable.
169177
*/
170-
function approverChips(r: ApprovalRequestRow): Array<{ label: string; count: number; title: string }> {
178+
function approverChips(
179+
r: ApprovalRequestRow,
180+
): Array<{ label: string; group?: string; count: number; title: string }> {
171181
const order: string[] = [];
172-
const byLabel = new Map<string, { label: string; count: number; title: string }>();
182+
const byKey = new Map<string, { label: string; group?: string; count: number; title: string }>();
173183
for (const a of r.pending_approvers || []) {
174184
const label = approverDisplay(a, r);
175-
const seen = byLabel.get(label);
185+
const group = approverGroup(a, r);
186+
const key = group ? `${label} ${group}` : label;
187+
const seen = byKey.get(key);
176188
if (seen) {
177189
seen.count += 1;
178190
if (a && !seen.title.split(', ').includes(a)) seen.title += `, ${a}`;
179191
} else {
180-
byLabel.set(label, { label, count: 1, title: a || label });
181-
order.push(label);
192+
byKey.set(key, { label, group, count: 1, title: a || label });
193+
order.push(key);
182194
}
183195
}
184-
return order.map((l) => byLabel.get(l)!);
196+
return order.map((k) => byKey.get(k)!);
185197
}
186198
/**
187199
* A request with no human submitter — flow- or system-initiated (#2762 P1-4).
@@ -1707,9 +1719,13 @@ export function ApprovalsInboxPage() {
17071719
{tr('waitingOn', 'Waiting on')}
17081720
</div>
17091721
<div className="flex flex-wrap gap-1">
1710-
{approverChips(selected).map((chip) => (
1711-
<Badge key={chip.label} variant="outline" className="text-[11px]" title={chip.title}>
1722+
{approverChips(selected).map((chip, i) => (
1723+
<Badge key={`${chip.label}-${chip.group ?? ''}-${i}`} variant="outline" className="text-[11px]" title={chip.title}>
17121724
{chip.label}
1725+
{chip.group && (
1726+
// 会签 group this slot represents (objectui#2807).
1727+
<span className="ml-1 text-muted-foreground">· {chip.group}</span>
1728+
)}
17131729
{chip.count > 1 && (
17141730
<span className="ml-1 text-muted-foreground">×{chip.count}</span>
17151731
)}

apps/console/src/services/approvalsApi.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ export interface ApprovalRequestRow {
5454
object_label?: string;
5555
/** Display names for user-id entries in `pending_approvers` (id → name). */
5656
pending_approver_names?: Record<string, string>;
57+
/**
58+
* Group membership of each still-pending approver, `per_group` (会签) requests
59+
* only (objectui#2807): approver id → the group key(s) it fills, e.g.
60+
* `{ u_devadmin: ['finance', 'legal'] }`. Lets the drawer label each "waiting
61+
* on" chip with its group. Absent for non-`per_group` behaviors and for slots
62+
* whose group was unnamed.
63+
*/
64+
pending_approver_groups?: Record<string, string[]>;
5765
/** Display values for lookup fields in `payload` (field key → record title). */
5866
payload_display?: Record<string, string>;
5967
/** SLA deadline (`created_at + escalation.timeoutHours`), display-only. */

0 commit comments

Comments
 (0)