@flamingo-stack/openframe-frontend-core 0.0.850 → 0.0.852
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/export-map.json +1 -1
- package/dist/{case-study-card-X2HKPKT7.cjs → case-study-card-WP5Z234Q.cjs} +5 -5
- package/dist/chat-protocol/events.d.ts +4 -1
- package/dist/chat-protocol/events.d.ts.map +1 -1
- package/dist/chat-protocol/index.cjs +90 -40
- package/dist/chat-protocol/nats-decoder.d.ts.map +1 -1
- package/dist/chat-protocol/nats-decoder.js +5 -1
- package/dist/chat-protocol/policy-review.d.ts +18 -0
- package/dist/chat-protocol/policy-review.d.ts.map +1 -0
- package/dist/chat-protocol/policy-review.js +55 -0
- package/dist/{chunk-BU2PSRAS.cjs → chunk-2W22LY3W.cjs} +11 -11
- package/dist/{chunk-ZDSC3GFX.cjs → chunk-3SJVF3HW.cjs} +6 -6
- package/dist/{chunk-GAYLAA3L.cjs → chunk-3ZQESOPJ.cjs} +79 -79
- package/dist/{chunk-IPGGSFR7.cjs → chunk-477P6AQH.cjs} +6 -6
- package/dist/{chunk-5CBI2ZYV.cjs → chunk-5RMBLOJ5.cjs} +12 -12
- package/dist/{chunk-36DUCHI7.cjs → chunk-6GWRPW6B.cjs} +4 -4
- package/dist/{chunk-SOSIXRQ3.cjs → chunk-AZEO7HBB.cjs} +2 -2
- package/dist/{chunk-FRG724DU.cjs → chunk-BNHTBLAG.cjs} +17 -17
- package/dist/{chunk-VRWBJL7B.cjs → chunk-CHGIACIP.cjs} +91 -91
- package/dist/{chunk-CAGNWDVJ.cjs → chunk-COJG2ATY.cjs} +45 -45
- package/dist/{chunk-XKOL5CNU.cjs → chunk-D3H6AI7O.cjs} +956 -1077
- package/dist/{chunk-ALRYVZ3E.cjs → chunk-DLTPSP5F.cjs} +2 -2
- package/dist/{chunk-3W4ASAXN.cjs → chunk-EIACEY2L.cjs} +4 -4
- package/dist/{chunk-TIVPOVIM.cjs → chunk-ETZFBB7O.cjs} +2 -2
- package/dist/{chunk-GRCOEICK.cjs → chunk-G7GF4PWA.cjs} +6 -6
- package/dist/{chunk-7ICHJVAT.cjs → chunk-GEZ3UXCA.cjs} +25 -25
- package/dist/chunk-GGUFVIJ6.cjs +28 -0
- package/dist/{chunk-ZGEWT3SF.cjs → chunk-HXURNKXI.cjs} +3 -3
- package/dist/{chunk-TX4NTH22.cjs → chunk-IC4IJJB2.cjs} +102 -102
- package/dist/{chunk-BUEEQD3G.cjs → chunk-IGVAPIEW.cjs} +2 -2
- package/dist/{chunk-IG6ROHPS.cjs → chunk-IO6NN3SL.cjs} +54 -54
- package/dist/{chunk-5PIR77V5.cjs → chunk-KHOB3TES.cjs} +4 -4
- package/dist/{chunk-JQCQCE6U.cjs → chunk-KKFDRCQF.cjs} +2 -2
- package/dist/{chunk-6AJYGXXS.cjs → chunk-KNN2GCMW.cjs} +21 -21
- package/dist/{chunk-YJZ7R3W5.cjs → chunk-KXFIYO4O.cjs} +8 -8
- package/dist/{chunk-FXLROQUT.cjs → chunk-LFEURIZN.cjs} +590 -607
- package/dist/{chunk-JWIITCVB.cjs → chunk-MFDRCTM3.cjs} +47 -47
- package/dist/{chunk-OHHDX3Z4.cjs → chunk-MXY2OGEV.cjs} +42 -42
- package/dist/{chunk-533QK4R6.cjs → chunk-NMWLXHFH.cjs} +130 -130
- package/dist/{chunk-MDLT6D46.cjs → chunk-O3EKOXFZ.cjs} +20 -20
- package/dist/{chunk-HLJXCOMA.cjs → chunk-OH5TXFRR.cjs} +4 -4
- package/dist/{chunk-SA2NNWIJ.cjs → chunk-P63CRPWI.cjs} +19 -64
- package/dist/{chunk-R5JPZ6TY.cjs → chunk-QOW2GP3V.cjs} +21 -21
- package/dist/{chunk-JBTH3O2C.cjs → chunk-SSKMGH5O.cjs} +4 -4
- package/dist/{chunk-4YBS4HIO.cjs → chunk-U767P4JU.cjs} +455 -38
- package/dist/{chunk-FNQWL4W4.cjs → chunk-UO2EJGRJ.cjs} +45 -45
- package/dist/{chunk-IHY7E4EX.cjs → chunk-USPUZO53.cjs} +4 -4
- package/dist/{chunk-IN3LA2FU.cjs → chunk-UV45EPXN.cjs} +2 -2
- package/dist/{chunk-S3QXLD4X.cjs → chunk-VCFQHPOO.cjs} +37 -3
- package/dist/{chunk-2QZUTNL3.cjs → chunk-VUHHLRAY.cjs} +92 -92
- package/dist/{chunk-QJER2ZNA.cjs → chunk-WFAZMASF.cjs} +3 -3
- package/dist/{chunk-LGYPCUHO.cjs → chunk-X3WIOMEE.cjs} +36 -36
- package/dist/{cloud-tenants-TVB3DT3J.cjs → cloud-tenants-O33HCUYB.cjs} +50 -50
- package/dist/components/case-studies/index.cjs +69 -69
- package/dist/components/chat/approval-batch-message.d.ts +5 -0
- package/dist/components/chat/approval-batch-message.d.ts.map +1 -1
- package/dist/components/chat/approval-batch-message.js +36 -10
- package/dist/components/chat/index.cjs +45 -45
- package/dist/components/chat/policy-review.d.ts +27 -0
- package/dist/components/chat/policy-review.d.ts.map +1 -0
- package/dist/components/chat/policy-review.js +67 -0
- package/dist/components/chat/stream/message-mutations.d.ts.map +1 -1
- package/dist/components/chat/stream/message-mutations.js +4 -1
- package/dist/components/chat/tool-execution-display.d.ts.map +1 -1
- package/dist/components/chat/tool-execution-display.js +16 -5
- package/dist/components/chat/types/message.types.d.ts +40 -2
- package/dist/components/chat/types/message.types.d.ts.map +1 -1
- package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
- package/dist/components/chat/utils/extract-incomplete-message-state.js +2 -0
- package/dist/components/chat/utils/message-segment-accumulator.d.ts.map +1 -1
- package/dist/components/chat/utils/message-segment-accumulator.js +3 -0
- package/dist/components/chat/utils/process-historical-messages.d.ts +2 -2
- package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.js +7 -1
- package/dist/components/contact/index.cjs +51 -51
- package/dist/components/docs/index.cjs +54 -54
- package/dist/components/embeds/index.cjs +51 -51
- package/dist/components/faq/index.cjs +52 -52
- package/dist/components/features/approval-level.d.ts +6 -12
- package/dist/components/features/approval-level.d.ts.map +1 -1
- package/dist/components/features/approval-level.js +45 -55
- package/dist/components/features/index.cjs +55 -54
- package/dist/components/features/notifications/approval-request-notification-tile.d.ts.map +1 -1
- package/dist/components/features/notifications/approval-request-notification-tile.js +71 -48
- package/dist/components/features/notifications/types.d.ts +2 -0
- package/dist/components/features/notifications/types.d.ts.map +1 -1
- package/dist/components/features/notifications/types.js +3 -1
- package/dist/components/features/policy-configuration-panel.d.ts.map +1 -1
- package/dist/components/features/policy-configuration-panel.js +75 -64
- package/dist/components/help-center-pages/index.cjs +156 -156
- package/dist/components/icons-v2-generated/brand-logos/index.d.ts +1 -0
- package/dist/components/icons-v2-generated/brand-logos/index.d.ts.map +1 -1
- package/dist/components/icons-v2-generated/brand-logos/index.js +2 -0
- package/dist/components/icons-v2-generated/brand-logos/stripe-icon.d.ts +8 -0
- package/dist/components/icons-v2-generated/brand-logos/stripe-icon.d.ts.map +1 -0
- package/dist/components/icons-v2-generated/brand-logos/stripe-icon.js +24 -0
- package/dist/components/icons-v2-generated/icon-loaders.d.ts.map +1 -1
- package/dist/components/icons-v2-generated/icon-loaders.js +1 -0
- package/dist/components/icons-v2-generated/icon-names.js +1 -1
- package/dist/components/icons-v2-generated/index.cjs +33 -29
- package/dist/components/index.cjs +122 -123
- package/dist/components/meeting-scheduler/index.cjs +113 -112
- package/dist/components/navigation/index.cjs +54 -54
- package/dist/components/onboarding-guides/index.cjs +53 -53
- package/dist/components/product-demo/index.cjs +61 -61
- package/dist/components/related-content/index.cjs +9 -9
- package/dist/components/tickets/index.cjs +57 -57
- package/dist/components/ui/file-manager/index.cjs +8 -9
- package/dist/components/ui/index.cjs +55 -56
- package/dist/components/ui/radio-group.d.ts.map +1 -1
- package/dist/components/ui/radio-group.js +8 -2
- package/dist/components/ui/tab-selector.d.ts +7 -1
- package/dist/components/ui/tab-selector.d.ts.map +1 -1
- package/dist/components/ui/tab-selector.js +22 -3
- package/dist/{devices-NGCTSXIU.cjs → devices-2RGGMC2B.cjs} +51 -51
- package/dist/{embeddable-chat-322NDFP5.cjs → embeddable-chat-SJEEJI4N.cjs} +122 -122
- package/dist/hooks/index.cjs +9 -9
- package/dist/{icon-loaders-SUPEKMBJ.cjs → icon-loaders-CTH6E7OP.cjs} +1 -0
- package/dist/index.cjs +61 -62
- package/dist/{knowledge-75PO5DYS.cjs → knowledge-5TJIXVMD.cjs} +52 -52
- package/dist/{logs-PZESOAIG.cjs → logs-HGQOTRKZ.cjs} +57 -57
- package/dist/{policies-6XNMBFWO.cjs → policies-GWFSLVSM.cjs} +54 -54
- package/dist/{remote-session-Q2FIBCGT.cjs → remote-session-SG44CNOK.cjs} +36 -36
- package/dist/{scripts-B3JW6TPF.cjs → scripts-PGJNAZF4.cjs} +57 -57
- package/dist/{software-update-NWP542J7.cjs → software-update-KKLSYHDI.cjs} +54 -54
- package/dist/stripe-icon-YKVABTNI.cjs +7 -0
- package/dist/{tickets-board-7FLGQEHF.cjs → tickets-board-SQY2Y6QV.cjs} +39 -38
- package/dist/utils/index.cjs +32 -1
- package/package.json +1 -1
- package/src/chat-protocol/events.ts +4 -1
- package/src/chat-protocol/nats-decoder.ts +4 -0
- package/src/chat-protocol/policy-review.ts +71 -0
- package/src/components/chat/approval-batch-message.tsx +47 -10
- package/src/components/chat/policy-review.tsx +90 -0
- package/src/components/chat/stream/message-mutations.ts +4 -0
- package/src/components/chat/tool-execution-display.tsx +23 -4
- package/src/components/chat/types/message.types.ts +42 -2
- package/src/components/chat/utils/extract-incomplete-message-state.ts +2 -0
- package/src/components/chat/utils/message-segment-accumulator.ts +3 -0
- package/src/components/chat/utils/process-historical-messages.ts +14 -3
- package/src/components/features/approval-level.tsx +74 -78
- package/src/components/features/notifications/approval-request-notification-tile.tsx +75 -50
- package/src/components/features/notifications/types.ts +4 -0
- package/src/components/features/policy-configuration-panel.tsx +100 -83
- package/src/components/icons-v2/icon-colors.json +3 -0
- package/src/components/icons-v2-generated/brand-logos/index.ts +1 -0
- package/src/components/icons-v2-generated/brand-logos/stripe-icon.tsx +27 -0
- package/src/components/icons-v2-generated/icon-loaders.ts +1 -0
- package/src/components/icons-v2-generated/icon-names.ts +1 -1
- package/src/components/ui/radio-group.tsx +20 -2
- package/src/components/ui/tab-selector.tsx +26 -3
- package/src/styles/ods-colors.css +1 -0
- package/dist/chunk-N3MBQRCQ.cjs +0 -41
- /package/dist/{chunk-46547WQW.cjs → chunk-7HH4Y27U.cjs} +0 -0
|
@@ -5,7 +5,9 @@ import { cn } from '../../../utils/cn';
|
|
|
5
5
|
import { ApprovalBatchMessage, ApprovalStatusTag } from '../../chat/approval-batch-message';
|
|
6
6
|
import { ExpandChevron } from '../../chat/expand-chevron';
|
|
7
7
|
import { useCollapsible } from '../../chat/hooks/use-collapsible';
|
|
8
|
+
import { PolicyEscalationAlert, isPolicyEscalated } from '../../chat/policy-review';
|
|
8
9
|
import type { ChatApprovalStatus } from '../../chat/types';
|
|
10
|
+
import { AlertCircleIcon } from '../../icons-v2-generated';
|
|
9
11
|
import { Button } from '../../ui/button';
|
|
10
12
|
import {
|
|
11
13
|
APPROVAL_BUTTON_CLASS,
|
|
@@ -85,6 +87,10 @@ export function ApprovalRequestNotificationTile({
|
|
|
85
87
|
type: notification.type ?? 'Approval Required',
|
|
86
88
|
};
|
|
87
89
|
|
|
90
|
+
// What Mingo escalated is read with the commands folded away, in the chat block's order (Figma 1375-114530): the
|
|
91
|
+
// "!" beside the chevron, the "Mingo Escalated" band before the buttons, Approve in the warning colour.
|
|
92
|
+
const escalatedCalls = batchData.toolCalls.filter(call => isPolicyEscalated(call.policyReview));
|
|
93
|
+
const escalated = escalatedCalls.length > 0;
|
|
88
94
|
const commandWord = batchData.toolCalls.length > 1 ? 'commands' : 'command';
|
|
89
95
|
const toggleLabel = `${expanded ? 'Hide' : 'Show'} ${commandWord} section`;
|
|
90
96
|
|
|
@@ -119,57 +125,75 @@ export function ApprovalRequestNotificationTile({
|
|
|
119
125
|
timeLabel={timeLabel}
|
|
120
126
|
paused={pinned || processing}
|
|
121
127
|
actions={
|
|
122
|
-
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
<>
|
|
129
|
+
{!lockscreen &&
|
|
130
|
+
escalatedCalls.map(call => (
|
|
131
|
+
// Edge to edge, as under the chat's command row: the tile's padding cancelled (`w-auto`, so the stretched
|
|
132
|
+
// flex item grows into it instead of keeping the Alert's `w-full`), a rule above and below.
|
|
133
|
+
<PolicyEscalationAlert
|
|
134
|
+
key={call.toolExecutionRequestId}
|
|
135
|
+
review={call.policyReview}
|
|
136
|
+
className="-mx-[var(--spacing-system-s)] w-auto border-y border-ods-border"
|
|
137
|
+
/>
|
|
138
|
+
))}
|
|
139
|
+
<div className="flex w-full items-center gap-[var(--spacing-system-xs)]">
|
|
140
|
+
{status === 'pending' ? (
|
|
141
|
+
<>
|
|
142
|
+
<Button
|
|
143
|
+
variant={escalated ? 'warning' : 'accent'}
|
|
144
|
+
size="small"
|
|
145
|
+
onClick={() => resolve('approved', onApprove)}
|
|
146
|
+
disabled={processing}
|
|
147
|
+
data-pressed={approvePressed || undefined}
|
|
148
|
+
className={cn(
|
|
149
|
+
'transition-[filter,transform] duration-200 motion-reduce:transition-none',
|
|
150
|
+
lockscreen ? LOCKSCREEN_APPROVAL_BUTTON_CLASS : APPROVAL_BUTTON_CLASS,
|
|
151
|
+
approvePressed && 'scale-[0.98] brightness-75',
|
|
152
|
+
)}
|
|
153
|
+
>
|
|
154
|
+
Approve
|
|
155
|
+
</Button>
|
|
156
|
+
<Button
|
|
157
|
+
variant="outline"
|
|
158
|
+
size="small"
|
|
159
|
+
onClick={() => resolve('rejected', onReject)}
|
|
160
|
+
disabled={processing}
|
|
161
|
+
className={lockscreen ? LOCKSCREEN_APPROVAL_BUTTON_CLASS : APPROVAL_BUTTON_CLASS}
|
|
162
|
+
>
|
|
163
|
+
Reject
|
|
164
|
+
</Button>
|
|
165
|
+
</>
|
|
166
|
+
) : (
|
|
167
|
+
<span className="flex min-w-0 items-center gap-[var(--spacing-system-xsf)]">
|
|
168
|
+
<ApprovalStatusTag status={status} />
|
|
169
|
+
{approval.resolvedByName ? (
|
|
170
|
+
<span className="truncate text-ods-text-secondary text-h6">by {approval.resolvedByName}</span>
|
|
171
|
+
) : null}
|
|
172
|
+
</span>
|
|
173
|
+
)}
|
|
174
|
+
{!lockscreen && (
|
|
175
|
+
<span className="ml-auto flex shrink-0 items-center gap-[var(--spacing-system-xs)]">
|
|
176
|
+
{escalated && !expanded && (
|
|
177
|
+
// Decorative: the band above says it in words. Open, each command row draws its own.
|
|
178
|
+
<AlertCircleIcon className="size-4 text-ods-warning" aria-hidden data-escalation-mark />
|
|
135
179
|
)}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
<span className="truncate text-ods-text-secondary text-h6">by {approval.resolvedByName}</span>
|
|
154
|
-
) : null}
|
|
155
|
-
</span>
|
|
156
|
-
)}
|
|
157
|
-
{!lockscreen && (
|
|
158
|
-
<Button
|
|
159
|
-
variant="outline"
|
|
160
|
-
size="small"
|
|
161
|
-
onClick={() => {
|
|
162
|
-
setExpanded(prev => !prev);
|
|
163
|
-
setPinned(true);
|
|
164
|
-
}}
|
|
165
|
-
aria-expanded={expanded}
|
|
166
|
-
aria-label={toggleLabel}
|
|
167
|
-
className={APPROVAL_TOGGLE_BUTTON_CLASS}
|
|
168
|
-
>
|
|
169
|
-
<ExpandChevron expanded={expanded} />
|
|
170
|
-
</Button>
|
|
171
|
-
)}
|
|
172
|
-
</div>
|
|
180
|
+
<Button
|
|
181
|
+
variant="outline"
|
|
182
|
+
size="small"
|
|
183
|
+
onClick={() => {
|
|
184
|
+
setExpanded(prev => !prev);
|
|
185
|
+
setPinned(true);
|
|
186
|
+
}}
|
|
187
|
+
aria-expanded={expanded}
|
|
188
|
+
aria-label={toggleLabel}
|
|
189
|
+
className={APPROVAL_TOGGLE_BUTTON_CLASS}
|
|
190
|
+
>
|
|
191
|
+
<ExpandChevron expanded={expanded} />
|
|
192
|
+
</Button>
|
|
193
|
+
</span>
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
</>
|
|
173
197
|
}
|
|
174
198
|
>
|
|
175
199
|
{!lockscreen && (
|
|
@@ -182,6 +206,7 @@ export function ApprovalRequestNotificationTile({
|
|
|
182
206
|
data={batchData}
|
|
183
207
|
showExecutionStatus={false}
|
|
184
208
|
showFooterActions={false}
|
|
209
|
+
showEscalationAlerts={false}
|
|
185
210
|
maxBodyHeight="50vh"
|
|
186
211
|
className="mb-0 rounded-none border-0"
|
|
187
212
|
/>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
+
import { policyReviewField } from '../../../chat-protocol/policy-review';
|
|
2
3
|
import type { NotificationSeverity } from '../../../utils/notifications';
|
|
3
4
|
import { APPROVAL_STATUS, type ApprovalBatchData, type ChatApprovalStatus } from '../../chat/types';
|
|
4
5
|
|
|
@@ -73,6 +74,8 @@ export interface ApprovalToolCallMeta {
|
|
|
73
74
|
requiresApproval?: boolean;
|
|
74
75
|
approvalType?: string | null;
|
|
75
76
|
toolCallArguments?: Record<string, unknown> | null;
|
|
77
|
+
/** The guardrail review, as the wire sends it: normalized into the batch by `approvalMetaToBatchData`. */
|
|
78
|
+
policyReview?: unknown;
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
/** Approval payload stashed in `notification.meta` for an approval-request notification. */
|
|
@@ -138,6 +141,7 @@ export function approvalMetaToBatchData(approval: ApprovalNotificationMeta): App
|
|
|
138
141
|
requiresApproval: tc.requiresApproval ?? true,
|
|
139
142
|
approvalType: tc.approvalType ?? null,
|
|
140
143
|
toolCallArguments: tc.toolCallArguments ?? null,
|
|
144
|
+
...policyReviewField(tc.policyReview),
|
|
141
145
|
})),
|
|
142
146
|
};
|
|
143
147
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ChevronDown, Shield } from 'lucide-react';
|
|
4
3
|
import type { FC } from 'react';
|
|
5
4
|
import { useState } from 'react';
|
|
6
5
|
import type {
|
|
@@ -11,17 +10,21 @@ import type {
|
|
|
11
10
|
} from '../../types/permissions';
|
|
12
11
|
import { cn } from '../../utils/cn';
|
|
13
12
|
import { useCollapsible } from '../chat/hooks/use-collapsible';
|
|
13
|
+
import { Chevron02DownIcon, ShieldIcon } from '../icons-v2-generated';
|
|
14
14
|
import { TextLineSkeleton } from '../loading/unified-skeleton';
|
|
15
15
|
import { ToolIcon } from '../tool-icon';
|
|
16
16
|
import { Button } from '../ui/button';
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
DropdownMenu,
|
|
19
|
+
DropdownMenuContent,
|
|
20
|
+
DropdownMenuItem,
|
|
21
|
+
DropdownMenuSeparator,
|
|
22
|
+
DropdownMenuTrigger,
|
|
23
|
+
} from '../ui/dropdown-menu';
|
|
18
24
|
import { loadingItemProps, useLoadingScope } from '../ui/loading-scope';
|
|
19
|
-
import { APPROVAL_LEVEL_META, APPROVAL_LEVELS } from './approval-level';
|
|
25
|
+
import { APPROVAL_LEVEL_META, APPROVAL_LEVELS, ApprovalLevelView } from './approval-level';
|
|
20
26
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const POLICY_LEVEL_WIDTH = 'w-[180px]';
|
|
24
|
-
const CATEGORY_LEVEL_WIDTH = 'w-[256px]';
|
|
27
|
+
const CATEGORY_LEVEL_WIDTH = 'w-[296px]';
|
|
25
28
|
|
|
26
29
|
/** How many collapsed category rows the panel draws while loading when the host passes no categories. */
|
|
27
30
|
export const POLICY_CONFIGURATION_PANEL_SKELETON_CATEGORIES = 4;
|
|
@@ -40,9 +43,22 @@ const CATEGORY_LEVEL_COLUMN_CLASS = cn(
|
|
|
40
43
|
CATEGORY_LEVEL_WIDTH,
|
|
41
44
|
);
|
|
42
45
|
/** The expand / collapse control's chevron. */
|
|
43
|
-
const CHEVRON_CLASS = 'h-6 w-6 text-ods-text-primary transition-transform duration-300';
|
|
46
|
+
const CHEVRON_CLASS = '!h-6 !w-6 text-ods-text-primary transition-transform duration-300';
|
|
47
|
+
|
|
48
|
+
/** A level as the dropdown writes it: its icon in its colour (the trigger's chevron size, 24px), then its name. */
|
|
49
|
+
const LevelOption: FC<{ level: ApprovalLevel }> = ({ level }) => {
|
|
50
|
+
const { label, Icon, toneClassName } = APPROVAL_LEVEL_META[level];
|
|
51
|
+
return (
|
|
52
|
+
<span className="inline-flex min-w-0 items-center gap-[var(--spacing-system-xsf)]">
|
|
53
|
+
<Icon size={24} className={cn('shrink-0', toneClassName)} aria-hidden />
|
|
54
|
+
<span className="truncate">{label}</span>
|
|
55
|
+
</span>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
44
58
|
|
|
45
|
-
|
|
59
|
+
/** A dropdown item at its trigger's scale: the trigger's type and icon size, a field's height (48px). The menu is
|
|
60
|
+
* portalled to the window, so it steps with the window (`md:`), not the content. */
|
|
61
|
+
const LEVEL_MENU_ITEM_CLASS = 'p-[var(--spacing-system-sf)] text-h4 [&_svg]:size-4 md:[&_svg]:size-6';
|
|
46
62
|
|
|
47
63
|
const ApprovalLevelDropdown: FC<{
|
|
48
64
|
value: ApprovalLevel | undefined;
|
|
@@ -58,27 +74,31 @@ const ApprovalLevelDropdown: FC<{
|
|
|
58
74
|
<Button
|
|
59
75
|
variant="outline"
|
|
60
76
|
disabled={disabled}
|
|
61
|
-
rightIcon={<
|
|
77
|
+
rightIcon={<Chevron02DownIcon className="!h-6 !w-6 text-ods-text-secondary" />}
|
|
62
78
|
className={cn(
|
|
63
|
-
'flex h-auto items-center justify-between gap-[var(--spacing-system-xsf)] rounded-md border border-ods-border bg-ods-card !
|
|
79
|
+
'flex h-auto items-center justify-between gap-[var(--spacing-system-xsf)] rounded-md border border-ods-border bg-ods-card !p-[var(--spacing-system-sf)] transition-colors !text-h4 hover:bg-ods-bg-hover',
|
|
64
80
|
value ? 'text-ods-text-primary' : 'text-ods-text-secondary',
|
|
65
81
|
triggerClassName,
|
|
66
82
|
)}
|
|
67
83
|
>
|
|
68
|
-
{value ?
|
|
84
|
+
{value ? <LevelOption level={value} /> : 'Set Global Permission'}
|
|
69
85
|
</Button>
|
|
70
86
|
</DropdownMenuTrigger>
|
|
71
87
|
<DropdownMenuContent align="end" className={contentClassName}>
|
|
72
|
-
{
|
|
73
|
-
<DropdownMenuItem onClick={() => onChange(
|
|
74
|
-
|
|
75
|
-
</DropdownMenuItem>
|
|
76
|
-
)}
|
|
77
|
-
{approvalLevelOptions.map(option => (
|
|
78
|
-
<DropdownMenuItem key={option.value} onClick={() => onChange(option.value)} className="text-h6">
|
|
79
|
-
{option.label}
|
|
88
|
+
{APPROVAL_LEVELS.map(level => (
|
|
89
|
+
<DropdownMenuItem key={level} onClick={() => onChange(level)} className={LEVEL_MENU_ITEM_CLASS}>
|
|
90
|
+
<LevelOption level={level} />
|
|
80
91
|
</DropdownMenuItem>
|
|
81
92
|
))}
|
|
93
|
+
{/* The way back out comes last, apart from the levels. */}
|
|
94
|
+
{allowClear && (
|
|
95
|
+
<>
|
|
96
|
+
<DropdownMenuSeparator />
|
|
97
|
+
<DropdownMenuItem onClick={() => onChange(undefined)} className={LEVEL_MENU_ITEM_CLASS}>
|
|
98
|
+
Clear Global Permission
|
|
99
|
+
</DropdownMenuItem>
|
|
100
|
+
</>
|
|
101
|
+
)}
|
|
82
102
|
</DropdownMenuContent>
|
|
83
103
|
</DropdownMenu>
|
|
84
104
|
);
|
|
@@ -89,56 +109,49 @@ const PolicyRow: FC<{
|
|
|
89
109
|
editMode: boolean;
|
|
90
110
|
onPermissionChange: (categoryId: string, policyId: string, level: ApprovalLevel) => void;
|
|
91
111
|
}> = ({ policy, categoryId, editMode, onPermissionChange }) => {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
112
|
+
const nameId = `policy-${categoryId}-${policy.id}`;
|
|
113
|
+
// The level as every guardrail surface draws it: its icon in its colour, then its name (Figma 1608-49525), or, while
|
|
114
|
+
// editing, its name in its colour beside the four levels' icons to pick from (Figma 4365-22663).
|
|
115
|
+
const level = policy.approvalLevel ? (
|
|
116
|
+
<ApprovalLevelView
|
|
117
|
+
value={policy.approvalLevel}
|
|
118
|
+
editable={editMode}
|
|
119
|
+
onChange={next => onPermissionChange(categoryId, policy.id, next)}
|
|
120
|
+
aria-labelledby={nameId}
|
|
121
|
+
/>
|
|
122
|
+
) : null;
|
|
95
123
|
|
|
96
124
|
return (
|
|
97
125
|
<div className="flex items-start gap-[var(--spacing-system-m)] border-b border-ods-border bg-ods-bg px-[var(--spacing-system-m)] py-[var(--spacing-system-sf)] last:border-b-0 content-md:items-center">
|
|
98
|
-
{/* Tool Icon
|
|
99
|
-
|
|
126
|
+
{/* Tool Icon: grey, as the category's (Figma 1608-49526). Editing on a phone it moves beside the name, and the
|
|
127
|
+
strip gets the row's width (Figma 4365-22455). */}
|
|
128
|
+
<div className={cn(ICON_TILE_CLASS, 'text-ods-text-secondary', editMode && 'hidden content-md:flex')}>
|
|
100
129
|
<ToolIcon toolType={policy.toolName} size={16} />
|
|
101
130
|
</div>
|
|
102
131
|
|
|
103
|
-
{/* Policy Info — on mobile the approval level stacks below the
|
|
132
|
+
{/* Policy Info — on mobile the approval level stacks below the name */}
|
|
104
133
|
<div className="flex min-w-0 flex-1 flex-col">
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<ApprovalLevelDropdown
|
|
112
|
-
value={policy.approvalLevel}
|
|
113
|
-
onChange={handleChange}
|
|
114
|
-
triggerClassName="w-full"
|
|
115
|
-
contentClassName={POLICY_LEVEL_WIDTH}
|
|
116
|
-
/>
|
|
117
|
-
</div>
|
|
118
|
-
) : policy.approvalLevel ? (
|
|
119
|
-
<span className="text-ods-text-primary text-h4 content-md:hidden">
|
|
120
|
-
{getApprovalLevelLabel(policy.approvalLevel)}
|
|
121
|
-
</span>
|
|
122
|
-
) : null}
|
|
123
|
-
</div>
|
|
124
|
-
|
|
125
|
-
{/* Approval Level column (desktop only) */}
|
|
126
|
-
<div className="hidden shrink-0 content-md:block">
|
|
127
|
-
{editMode ? (
|
|
128
|
-
<ApprovalLevelDropdown
|
|
129
|
-
value={policy.approvalLevel}
|
|
130
|
-
onChange={handleChange}
|
|
131
|
-
triggerClassName={POLICY_LEVEL_WIDTH}
|
|
132
|
-
contentClassName={POLICY_LEVEL_WIDTH}
|
|
133
|
-
/>
|
|
134
|
-
) : (
|
|
135
|
-
<div className={cn('px-[var(--spacing-system-sf)] py-[var(--spacing-system-xsf)]', POLICY_LEVEL_WIDTH)}>
|
|
136
|
-
{policy.approvalLevel && (
|
|
137
|
-
<span className="text-ods-text-primary text-h4">{getApprovalLevelLabel(policy.approvalLevel)}</span>
|
|
138
|
-
)}
|
|
134
|
+
<div className="flex min-w-0 items-center gap-[var(--spacing-system-sf)]">
|
|
135
|
+
<div className="flex min-w-0 flex-1 flex-col">
|
|
136
|
+
{/* The rule's name only: its command pattern is a regex, not something to read. */}
|
|
137
|
+
<p id={nameId} className="truncate text-ods-text-primary text-h4" title={policy.name}>
|
|
138
|
+
{policy.name}
|
|
139
|
+
</p>
|
|
139
140
|
</div>
|
|
141
|
+
{editMode && (
|
|
142
|
+
<span className="flex shrink-0 text-ods-text-secondary content-md:hidden">
|
|
143
|
+
<ToolIcon toolType={policy.toolName} size={16} />
|
|
144
|
+
</span>
|
|
145
|
+
)}
|
|
146
|
+
</div>
|
|
147
|
+
{level && (
|
|
148
|
+
<div className={cn('flex content-md:hidden', editMode && 'mt-[var(--spacing-system-sf)]')}>{level}</div>
|
|
140
149
|
)}
|
|
141
150
|
</div>
|
|
151
|
+
|
|
152
|
+
{/* Approval Level column (desktop only). A flex box, not a block: a block's line box around the inline level
|
|
153
|
+
stood taller than the level and set it above the row's centre. */}
|
|
154
|
+
{level && <div className="hidden shrink-0 items-center content-md:flex">{level}</div>}
|
|
142
155
|
</div>
|
|
143
156
|
);
|
|
144
157
|
};
|
|
@@ -186,15 +199,21 @@ const CategorySection: FC<{
|
|
|
186
199
|
{/* Category Header */}
|
|
187
200
|
<div
|
|
188
201
|
{...loadingItemProps(props)}
|
|
189
|
-
|
|
202
|
+
// The whole row toggles, so the whole row lights on hover; not while the pointer is on the bulk dropdown, which
|
|
203
|
+
// is its own target. Open, a rule under the row (on the frame below) ends the lit row at an edge.
|
|
204
|
+
className={cn(
|
|
205
|
+
CATEGORY_HEADER_CLASS,
|
|
206
|
+
!loading &&
|
|
207
|
+
'cursor-pointer transition-colors hover:bg-ods-bg-hover [&:has([data-bulk-level]:hover)]:bg-ods-card',
|
|
208
|
+
)}
|
|
190
209
|
onClick={loading ? undefined : () => onToggle(category.id)}
|
|
191
210
|
>
|
|
192
211
|
{/* Category Icon (static: the host's, else the default shield) */}
|
|
193
212
|
<div className={ICON_TILE_CLASS}>
|
|
194
|
-
<div className="text-ods-text-secondary">{category.icon ?? <
|
|
213
|
+
<div className="text-ods-text-secondary">{category.icon ?? <ShieldIcon size={16} />}</div>
|
|
195
214
|
</div>
|
|
196
215
|
|
|
197
|
-
{/* Category Info
|
|
216
|
+
{/* Category Info. A phone has no bulk dropdown (Figma 4365-22446): its rules are set one by one. */}
|
|
198
217
|
<div className={CATEGORY_TEXT_CLASS}>
|
|
199
218
|
{category.name ? (
|
|
200
219
|
<p className="truncate text-ods-text-primary text-h4" title={category.name}>
|
|
@@ -206,25 +225,15 @@ const CategorySection: FC<{
|
|
|
206
225
|
{loading ? (
|
|
207
226
|
<TextLineSkeleton type="h6" className="w-24" />
|
|
208
227
|
) : (
|
|
209
|
-
<p className="text-ods-text-secondary text-h6">
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<div className="mt-[var(--spacing-system-xsf)] content-md:hidden" onClick={e => e.stopPropagation()}>
|
|
213
|
-
<ApprovalLevelDropdown
|
|
214
|
-
value={bulkLevel}
|
|
215
|
-
onChange={handleBulkChange}
|
|
216
|
-
allowClear
|
|
217
|
-
triggerClassName="w-full"
|
|
218
|
-
contentClassName={CATEGORY_LEVEL_WIDTH}
|
|
219
|
-
disabled={loading}
|
|
220
|
-
/>
|
|
221
|
-
</div>
|
|
228
|
+
<p className="text-ods-text-secondary text-h6">
|
|
229
|
+
{category.policies.length} {category.policies.length === 1 ? 'Configuration' : 'Configurations'}
|
|
230
|
+
</p>
|
|
222
231
|
)}
|
|
223
232
|
</div>
|
|
224
233
|
|
|
225
234
|
{/* Global Permission column (desktop only) */}
|
|
226
235
|
{editMode ? (
|
|
227
|
-
<div className="hidden shrink-0 content-md:block" onClick={e => e.stopPropagation()}>
|
|
236
|
+
<div data-bulk-level className="hidden shrink-0 content-md:block" onClick={e => e.stopPropagation()}>
|
|
228
237
|
<ApprovalLevelDropdown
|
|
229
238
|
value={bulkLevel}
|
|
230
239
|
onChange={handleBulkChange}
|
|
@@ -247,8 +256,13 @@ const CategorySection: FC<{
|
|
|
247
256
|
e.stopPropagation();
|
|
248
257
|
onToggle(category.id);
|
|
249
258
|
}}
|
|
250
|
-
leftIcon={<
|
|
251
|
-
|
|
259
|
+
leftIcon={<Chevron02DownIcon className={cn(CHEVRON_CLASS, isExpanded && 'rotate-180')} />}
|
|
260
|
+
// 40px on a phone (Figma 4365-22452), 48 from content-md.
|
|
261
|
+
className={cn(
|
|
262
|
+
'h-auto w-auto shrink-0 rounded-md !p-[var(--spacing-system-xsf)] content-md:!p-[var(--spacing-system-sf)]',
|
|
263
|
+
// The row is the hover; the chevron adds none of its own.
|
|
264
|
+
'hover:bg-transparent active:bg-transparent',
|
|
265
|
+
)}
|
|
252
266
|
aria-label={isExpanded ? 'Collapse' : 'Expand'}
|
|
253
267
|
/>
|
|
254
268
|
</div>
|
|
@@ -264,7 +278,10 @@ const CategorySection: FC<{
|
|
|
264
278
|
}}
|
|
265
279
|
className={isExpanded ? 'opacity-100' : 'opacity-0'}
|
|
266
280
|
>
|
|
267
|
-
<div
|
|
281
|
+
<div
|
|
282
|
+
ref={innerRef}
|
|
283
|
+
className="border-t border-ods-border px-[var(--spacing-system-mf)] pb-[var(--spacing-system-mf)] pt-[var(--spacing-system-mf)]"
|
|
284
|
+
>
|
|
268
285
|
<div className="overflow-hidden rounded-md border border-ods-border">
|
|
269
286
|
{/* No rows while the policies are read: nothing read is drawn, even collapsed. */}
|
|
270
287
|
{(loading ? [] : category.policies).map(policy => (
|
|
@@ -290,9 +307,9 @@ const CategorySection: FC<{
|
|
|
290
307
|
* background data refreshes never collapse the user's view. Bulk selections
|
|
291
308
|
* reset when `editMode` changes.
|
|
292
309
|
*
|
|
293
|
-
* Responsive: below `md`
|
|
294
|
-
*
|
|
295
|
-
* right-hand columns.
|
|
310
|
+
* Responsive (content width): below `content-md` a rule's level stacks under its
|
|
311
|
+
* pattern, and a category has no bulk dropdown (Figma 4365-22405); from
|
|
312
|
+
* `content-md` both are right-hand columns.
|
|
296
313
|
*/
|
|
297
314
|
/** One shared empty map, so a reset that changes nothing changes no identity. */
|
|
298
315
|
const NO_BULK_LEVELS: Record<string, ApprovalLevel | undefined> = {};
|
|
@@ -113,6 +113,7 @@ export { SlackLogoIcon } from './slack-logo-icon';
|
|
|
113
113
|
export { SlackMonoIcon } from './slack-mono-icon';
|
|
114
114
|
export { SoundcloudIcon } from './soundcloud-icon';
|
|
115
115
|
export { SpotifyIcon } from './spotify-icon';
|
|
116
|
+
export { StripeIcon } from './stripe-icon';
|
|
116
117
|
export { TelegramIcon } from './telegram-icon';
|
|
117
118
|
export { TiktokIcon } from './tiktok-icon';
|
|
118
119
|
export { TiktokSquareIcon } from './tiktok-square-icon';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SVGProps } from "react";
|
|
2
|
+
export interface StripeIconProps
|
|
3
|
+
extends Omit<SVGProps<SVGSVGElement>, "width" | "height"> {
|
|
4
|
+
className?: string;
|
|
5
|
+
size?: number;
|
|
6
|
+
color?: string;
|
|
7
|
+
}
|
|
8
|
+
export function StripeIcon({
|
|
9
|
+
className = "",
|
|
10
|
+
size = 24,
|
|
11
|
+
color = "var(--social-stripe)",
|
|
12
|
+
...props
|
|
13
|
+
}: StripeIconProps) {
|
|
14
|
+
return (
|
|
15
|
+
<svg
|
|
16
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
17
|
+
fill={color}
|
|
18
|
+
viewBox="0 0 24 24"
|
|
19
|
+
width={size}
|
|
20
|
+
height={size}
|
|
21
|
+
className={className}
|
|
22
|
+
{...props}
|
|
23
|
+
>
|
|
24
|
+
<path d="M13.976 9.15c-2.172-.806-3.356-1.426-3.356-2.409 0-.831.683-1.305 1.901-1.305 2.227 0 4.515.858 6.09 1.631l.89-5.494C18.252.975 15.697 0 12.165 0 9.667 0 7.589.654 6.104 1.872 4.56 3.147 3.757 4.992 3.757 7.218c0 4.039 2.467 5.76 6.476 7.219 2.585.92 3.445 1.574 3.445 2.583 0 .98-.84 1.545-2.354 1.545-1.875 0-4.965-.921-6.99-2.109l-.9 5.555C5.175 22.99 8.385 24 11.714 24c2.641 0 4.843-.624 6.328-1.813 1.664-1.305 2.525-3.236 2.525-5.732 0-4.128-2.524-5.851-6.594-7.305z" />
|
|
25
|
+
</svg>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -1605,6 +1605,7 @@ export const ICON_LOADERS: Readonly<Record<string, () => Promise<IconModule>>> =
|
|
|
1605
1605
|
StrawberryIcon: () => import('./food-and-drinks/strawberry-icon').then(module => ({ default: module.StrawberryIcon })),
|
|
1606
1606
|
StreetViewIcon: () => import('./map-and-travel/street-view-icon').then(module => ({ default: module.StreetViewIcon })),
|
|
1607
1607
|
StrikeThroughIcon: () => import('./text-editor/strike-through-icon').then(module => ({ default: module.StrikeThroughIcon })),
|
|
1608
|
+
StripeIcon: () => import('./brand-logos/stripe-icon').then(module => ({ default: module.StripeIcon })),
|
|
1608
1609
|
SubtitleIcon: () => import('./media-playback/subtitle-icon').then(module => ({ default: module.SubtitleIcon })),
|
|
1609
1610
|
SuitcaseIcon: () => import('./map-and-travel/suitcase-icon').then(module => ({ default: module.SuitcaseIcon })),
|
|
1610
1611
|
Sun01Icon: () => import('./weather-and-nature/sun-01-icon').then(module => ({ default: module.Sun01Icon })),
|