@flamingo-stack/openframe-frontend-core 0.0.383 → 0.0.384-snapshot.20260708165758
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/{chunk-FL6YJLPC.cjs → chunk-2KDXNL7M.cjs} +7 -7
- package/dist/{chunk-FL6YJLPC.cjs.map → chunk-2KDXNL7M.cjs.map} +1 -1
- package/dist/{chunk-EGFLSQEJ.js → chunk-53R2NLYW.js} +4 -4
- package/dist/{chunk-W456FUZG.cjs → chunk-5I4ERVP4.cjs} +57 -57
- package/dist/{chunk-W456FUZG.cjs.map → chunk-5I4ERVP4.cjs.map} +1 -1
- package/dist/{chunk-BALSUWWU.cjs → chunk-5O2QED4E.cjs} +37 -37
- package/dist/{chunk-BALSUWWU.cjs.map → chunk-5O2QED4E.cjs.map} +1 -1
- package/dist/{chunk-EBOK7SG7.js → chunk-7DVZECFW.js} +2 -2
- package/dist/{chunk-SGMZQTP6.js → chunk-ACKCFE7D.js} +2 -2
- package/dist/{chunk-MY7TV47R.js → chunk-ACYF5YVM.js} +2 -2
- package/dist/{chunk-7TU6MLIQ.cjs → chunk-CIBBHSCP.cjs} +12 -12
- package/dist/{chunk-7TU6MLIQ.cjs.map → chunk-CIBBHSCP.cjs.map} +1 -1
- package/dist/{chunk-ZFIDR55N.cjs → chunk-DN5AIYP4.cjs} +9 -9
- package/dist/{chunk-ZFIDR55N.cjs.map → chunk-DN5AIYP4.cjs.map} +1 -1
- package/dist/{chunk-AJJF43ZL.js → chunk-IDF4BZHL.js} +2 -2
- package/dist/{chunk-6SR7HXGW.js → chunk-K557GA5G.js} +4 -4
- package/dist/{chunk-LI7OAUSA.js → chunk-L5FDBXYI.js} +4 -4
- package/dist/{chunk-AHCBZ6TC.cjs → chunk-LDOEIJIA.cjs} +62 -62
- package/dist/{chunk-AHCBZ6TC.cjs.map → chunk-LDOEIJIA.cjs.map} +1 -1
- package/dist/{chunk-WVJFSCXR.cjs → chunk-LFAEFMYA.cjs} +26 -26
- package/dist/{chunk-WVJFSCXR.cjs.map → chunk-LFAEFMYA.cjs.map} +1 -1
- package/dist/{chunk-S2V4EPKL.cjs → chunk-LRLY7VFQ.cjs} +15 -15
- package/dist/{chunk-S2V4EPKL.cjs.map → chunk-LRLY7VFQ.cjs.map} +1 -1
- package/dist/{chunk-KBG3PLKS.js → chunk-NTHZI6VG.js} +2 -2
- package/dist/{chunk-KQQ6U4UW.js → chunk-PNJXI4NZ.js} +4 -4
- package/dist/{chunk-KGSOMSHT.cjs → chunk-QXKQ2WZF.cjs} +25 -25
- package/dist/{chunk-KGSOMSHT.cjs.map → chunk-QXKQ2WZF.cjs.map} +1 -1
- package/dist/{chunk-B45U4FY5.js → chunk-UPHZGPXZ.js} +2 -2
- package/dist/{chunk-VQCV3GGI.cjs → chunk-VZHEFPPH.cjs} +227 -163
- package/dist/chunk-VZHEFPPH.cjs.map +1 -0
- package/dist/{chunk-YW6K7IYF.cjs → chunk-X5DBARHE.cjs} +31 -31
- package/dist/{chunk-YW6K7IYF.cjs.map → chunk-X5DBARHE.cjs.map} +1 -1
- package/dist/{chunk-CNDLMBFN.js → chunk-XQ77CK5C.js} +390 -326
- package/dist/chunk-XQ77CK5C.js.map +1 -0
- package/dist/{chunk-ZD6GZX7E.js → chunk-YDYO7DRL.js} +2 -2
- package/dist/{chunk-KFTCNBSJ.cjs → chunk-ZRVWRNVJ.cjs} +5 -5
- package/dist/{chunk-KFTCNBSJ.cjs.map → chunk-ZRVWRNVJ.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/approval-batch-message.d.ts +20 -0
- package/dist/components/chat/approval-batch-message.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +4 -2
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.js +3 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/features/notifications/approval-request-notification-tile.d.ts.map +1 -1
- package/dist/components/features/notifications/notification-tile.d.ts +8 -2
- package/dist/components/features/notifications/notification-tile.d.ts.map +1 -1
- package/dist/components/features/notifications/types.d.ts +11 -1
- package/dist/components/features/notifications/types.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +122 -120
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +11 -9
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/app-layout.d.ts +9 -1
- package/dist/components/navigation/app-layout.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/navigation/navigation-sidebar-item.d.ts.map +1 -1
- package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +5 -5
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/data-table/data-table-body.d.ts +6 -1
- package/dist/components/ui/data-table/data-table-body.d.ts.map +1 -1
- package/dist/components/ui/data-table/data-table-row.d.ts +7 -1
- package/dist/components/ui/data-table/data-table-row.d.ts.map +1 -1
- package/dist/components/ui/data-table/index.d.ts +1 -1
- package/dist/components/ui/index.cjs +4 -2
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +3 -1
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -1
- package/dist/tailwind.config.cjs +4 -1
- package/dist/tailwind.config.cjs.map +1 -1
- package/dist/tailwind.config.js +4 -1
- package/dist/tailwind.config.js.map +1 -1
- package/dist/types/navigation.d.ts +0 -1
- package/dist/types/navigation.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/approval-batch-message.tsx +28 -15
- package/src/components/features/notifications/approval-request-notification-tile.tsx +70 -46
- package/src/components/features/notifications/notification-tile.tsx +125 -41
- package/src/components/features/notifications/types.ts +11 -1
- package/src/components/navigation/app-layout.tsx +17 -1
- package/src/components/navigation/navigation-sidebar-item.tsx +1 -12
- package/src/components/navigation/navigation-sidebar.tsx +6 -1
- package/src/components/ui/data-table/data-table-body.tsx +7 -0
- package/src/components/ui/data-table/data-table-row.tsx +12 -1
- package/src/stories/NotificationDrawer.stories.tsx +220 -31
- package/src/types/navigation.ts +0 -1
- package/tailwind.config.ts +4 -1
- package/dist/chunk-CNDLMBFN.js.map +0 -1
- package/dist/chunk-VQCV3GGI.cjs.map +0 -1
- /package/dist/{chunk-EGFLSQEJ.js.map → chunk-53R2NLYW.js.map} +0 -0
- /package/dist/{chunk-EBOK7SG7.js.map → chunk-7DVZECFW.js.map} +0 -0
- /package/dist/{chunk-SGMZQTP6.js.map → chunk-ACKCFE7D.js.map} +0 -0
- /package/dist/{chunk-MY7TV47R.js.map → chunk-ACYF5YVM.js.map} +0 -0
- /package/dist/{chunk-AJJF43ZL.js.map → chunk-IDF4BZHL.js.map} +0 -0
- /package/dist/{chunk-6SR7HXGW.js.map → chunk-K557GA5G.js.map} +0 -0
- /package/dist/{chunk-LI7OAUSA.js.map → chunk-L5FDBXYI.js.map} +0 -0
- /package/dist/{chunk-KBG3PLKS.js.map → chunk-NTHZI6VG.js.map} +0 -0
- /package/dist/{chunk-KQQ6U4UW.js.map → chunk-PNJXI4NZ.js.map} +0 -0
- /package/dist/{chunk-B45U4FY5.js.map → chunk-UPHZGPXZ.js.map} +0 -0
- /package/dist/{chunk-ZD6GZX7E.js.map → chunk-YDYO7DRL.js.map} +0 -0
|
@@ -3,11 +3,21 @@ import { APPROVAL_STATUS, type ApprovalBatchData, type ChatApprovalStatus } from
|
|
|
3
3
|
|
|
4
4
|
export type NotificationVariant = 'default' | 'success' | 'warning' | 'error' | 'info'
|
|
5
5
|
|
|
6
|
-
export type NotificationSeverity = 'INFO' | 'WARNING' | 'DANGER'
|
|
6
|
+
export type NotificationSeverity = 'INFO' | 'SUCCESS' | 'WARNING' | 'DANGER'
|
|
7
7
|
|
|
8
8
|
export interface Notification {
|
|
9
9
|
id: string
|
|
10
10
|
variant?: NotificationVariant
|
|
11
|
+
/** Kind label rendered uppercase in the tile header (e.g. "New Customer Created"). */
|
|
12
|
+
type?: string
|
|
13
|
+
/** Custom icon for the tile header slot; inherits the severity color via `currentColor`. */
|
|
14
|
+
icon?: ReactNode
|
|
15
|
+
/**
|
|
16
|
+
* Image URL for the tile header slot; takes precedence over `icon`. Proxy external
|
|
17
|
+
* URLs on the consumer side (ad-blockers routinely block direct third-party avatars);
|
|
18
|
+
* on load failure the tile falls back to `icon`/dot.
|
|
19
|
+
*/
|
|
20
|
+
imageUrl?: string
|
|
11
21
|
title: ReactNode
|
|
12
22
|
description?: ReactNode
|
|
13
23
|
createdAt: number
|
|
@@ -67,6 +67,14 @@ export interface AppLayoutProps {
|
|
|
67
67
|
* the drawer is part of the layout chrome, not page content.
|
|
68
68
|
*/
|
|
69
69
|
drawer?: React.ReactNode
|
|
70
|
+
/**
|
|
71
|
+
* Full-width banner rendered ABOVE both the sidebar and the header, spanning
|
|
72
|
+
* the entire viewport width and pinned to the top of the layout. Optional —
|
|
73
|
+
* when omitted the layout is unchanged. Used for global, cross-page callouts
|
|
74
|
+
* (e.g. an onboarding "complete your setup" bar). The sidebar + header + main
|
|
75
|
+
* area occupy the remaining height below it.
|
|
76
|
+
*/
|
|
77
|
+
topBar?: React.ReactNode
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
export function AppLayout({
|
|
@@ -79,6 +87,7 @@ export function AppLayout({
|
|
|
79
87
|
mobileBurgerMenuProps,
|
|
80
88
|
disabled = false,
|
|
81
89
|
drawer,
|
|
90
|
+
topBar,
|
|
82
91
|
}: AppLayoutProps) {
|
|
83
92
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
|
|
84
93
|
const [drawerContainer, setDrawerContainer] = useState<HTMLDivElement | null>(null)
|
|
@@ -114,7 +123,13 @@ export function AppLayout({
|
|
|
114
123
|
return (
|
|
115
124
|
<AppLayoutDrawerContainerContext.Provider value={drawerContainer}>
|
|
116
125
|
<AppLayoutDrawerCoordinationContext.Provider value={drawerCoordination}>
|
|
117
|
-
<div className={cn("flex h-screen bg-ods-bg", className)}>
|
|
126
|
+
<div className={cn("flex flex-col h-screen bg-ods-bg", className)}>
|
|
127
|
+
{/* Full-width top banner above sidebar + header (optional) */}
|
|
128
|
+
{topBar}
|
|
129
|
+
{/* Sidebar + header + main occupy the remaining height below the banner.
|
|
130
|
+
`relative` so the tablet sidebar (position:absolute) anchors to this
|
|
131
|
+
row — below the topBar — instead of the viewport. */}
|
|
132
|
+
<div className="flex flex-1 min-h-0 relative">
|
|
118
133
|
<NavigationSidebar config={sidebarConfig} disabled={disabled} />
|
|
119
134
|
{/* Mobile Burger Menu - opens below header */}
|
|
120
135
|
<MobileBurgerMenu
|
|
@@ -158,6 +173,7 @@ export function AppLayout({
|
|
|
158
173
|
{drawer}
|
|
159
174
|
</div>
|
|
160
175
|
</div>
|
|
176
|
+
</div>
|
|
161
177
|
</div>
|
|
162
178
|
</AppLayoutDrawerCoordinationContext.Provider>
|
|
163
179
|
</AppLayoutDrawerContainerContext.Provider>
|
|
@@ -72,22 +72,11 @@ export function NavigationSidebarItemButton({
|
|
|
72
72
|
|
|
73
73
|
{hasUnread && showLabel && (
|
|
74
74
|
<span className="bg-ods-accent flex items-center justify-center flex-shrink-0 p-2 rounded-md size-6">
|
|
75
|
-
<span className="text-
|
|
75
|
+
<span className="text-h5 text-ods-text-on-accent">
|
|
76
76
|
{unreadCount > 99 ? '99+' : unreadCount}
|
|
77
77
|
</span>
|
|
78
78
|
</span>
|
|
79
79
|
)}
|
|
80
|
-
|
|
81
|
-
{item.badge && showLabel && (
|
|
82
|
-
<span
|
|
83
|
-
className={cn(
|
|
84
|
-
"text-sm flex-shrink-0 transition-colors duration-300",
|
|
85
|
-
isActive && !disabled ? "text-ods-accent" : "text-ods-text-secondary",
|
|
86
|
-
)}
|
|
87
|
-
>
|
|
88
|
-
{item.badge}
|
|
89
|
-
</span>
|
|
90
|
-
)}
|
|
91
80
|
</button>
|
|
92
81
|
)
|
|
93
82
|
}
|
|
@@ -136,7 +136,12 @@ export function NavigationSidebar({ config, disabled = false }: NavigationSideba
|
|
|
136
136
|
className={cn(
|
|
137
137
|
"flex-col hidden md:flex flex-shrink-0",
|
|
138
138
|
"bg-ods-card border-r border-ods-border",
|
|
139
|
-
|
|
139
|
+
// Tablet: float the sidebar over content. Anchored to the layout row
|
|
140
|
+
// (`absolute` within AppLayout's `relative` row) — NOT the viewport —
|
|
141
|
+
// so an optional `topBar` above the row is not overlapped. With no
|
|
142
|
+
// topBar the row spans the full viewport, so this is visually
|
|
143
|
+
// identical to a viewport-fixed sidebar.
|
|
144
|
+
isTablet ? "absolute inset-y-0 left-0 z-[45]" : "relative h-full",
|
|
140
145
|
transitionsEnabled && "transition-[width] duration-300",
|
|
141
146
|
config.className,
|
|
142
147
|
)}
|
|
@@ -29,6 +29,11 @@ export interface DataTableBodyProps<T = any> {
|
|
|
29
29
|
rowClassName?: string | ((item: T, index: number) => string)
|
|
30
30
|
/** Dense row height. */
|
|
31
31
|
compact?: boolean
|
|
32
|
+
/**
|
|
33
|
+
* Treat the design row height as a minimum so multi-line cell content grows
|
|
34
|
+
* the row instead of clipping. Default keeps the fixed height.
|
|
35
|
+
*/
|
|
36
|
+
autoHeight?: boolean
|
|
32
37
|
/**
|
|
33
38
|
* Click anywhere on a row (except elements with `data-no-row-click`). Prefer
|
|
34
39
|
* `useCallback` to avoid breaking `React.memo` on rows.
|
|
@@ -66,6 +71,7 @@ export function DataTableBody<T = any>({
|
|
|
66
71
|
className,
|
|
67
72
|
rowClassName,
|
|
68
73
|
compact,
|
|
74
|
+
autoHeight,
|
|
69
75
|
onRowClick,
|
|
70
76
|
rowHref,
|
|
71
77
|
minRows,
|
|
@@ -112,6 +118,7 @@ export function DataTableBody<T = any>({
|
|
|
112
118
|
onClick={onRowClick}
|
|
113
119
|
href={href}
|
|
114
120
|
compact={compact}
|
|
121
|
+
autoHeight={autoHeight}
|
|
115
122
|
className={cls}
|
|
116
123
|
subRow={renderSubRow?.(item)}
|
|
117
124
|
/>
|
|
@@ -13,6 +13,12 @@ export interface DataTableRowProps<T> {
|
|
|
13
13
|
href?: string | null
|
|
14
14
|
/** Dense row height. */
|
|
15
15
|
compact?: boolean
|
|
16
|
+
/**
|
|
17
|
+
* Treat the design row height as a minimum: a cell rendering multi-line
|
|
18
|
+
* content (e.g. a line-clamped description) grows the row instead of being
|
|
19
|
+
* clipped. Default keeps the fixed height.
|
|
20
|
+
*/
|
|
21
|
+
autoHeight?: boolean
|
|
16
22
|
className?: string
|
|
17
23
|
/** Expandable content rendered below the cells, inside the same card. */
|
|
18
24
|
subRow?: ReactNode
|
|
@@ -60,6 +66,7 @@ function DataTableRowImpl<T>({
|
|
|
60
66
|
onClick,
|
|
61
67
|
href,
|
|
62
68
|
compact,
|
|
69
|
+
autoHeight,
|
|
63
70
|
className,
|
|
64
71
|
subRow,
|
|
65
72
|
}: DataTableRowProps<T>) {
|
|
@@ -100,7 +107,11 @@ function DataTableRowImpl<T>({
|
|
|
100
107
|
<div
|
|
101
108
|
className={cn(
|
|
102
109
|
'flex items-center gap-[var(--spacing-system-mf)] px-[var(--spacing-system-mf)]',
|
|
103
|
-
compact
|
|
110
|
+
compact
|
|
111
|
+
? 'py-[var(--spacing-system-xsf)]'
|
|
112
|
+
: autoHeight
|
|
113
|
+
? 'py-[var(--spacing-system-sf)] min-h-[68px] md:min-h-[80px]'
|
|
114
|
+
: `py-0 ${ROW_HEIGHT_DESKTOP}`,
|
|
104
115
|
hasSubRow && 'border-b border-ods-border',
|
|
105
116
|
)}
|
|
106
117
|
>
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { Button } from '../components/ui/button';
|
|
4
|
+
import { BracketCurlyIcon } from '../components/icons-v2-generated/coding/bracket-curly-icon';
|
|
5
|
+
import { MonitorIcon } from '../components/icons-v2-generated/devices/monitor-icon';
|
|
6
|
+
import { AlertTriangleIcon } from '../components/icons-v2-generated/interface/alert-triangle-icon';
|
|
7
|
+
import { BannedIcon } from '../components/icons-v2-generated/security/banned-icon';
|
|
8
|
+
import { UserPlusIcon } from '../components/icons-v2-generated/users/user-plus-icon';
|
|
4
9
|
import {
|
|
5
10
|
ADMIN_APPROVAL_REQUEST_CONTEXT_TYPE,
|
|
6
11
|
ApprovalRequestNotificationTile,
|
|
@@ -44,47 +49,100 @@ const meta = {
|
|
|
44
49
|
export default meta;
|
|
45
50
|
type Story = StoryObj<typeof meta>;
|
|
46
51
|
|
|
52
|
+
/** Inline avatar stand-in so the image slot renders without network access. */
|
|
53
|
+
const CUSTOMER_AVATAR =
|
|
54
|
+
'data:image/svg+xml;utf8,' +
|
|
55
|
+
encodeURIComponent(
|
|
56
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><rect width="32" height="32" fill="#5EFC8D"/><circle cx="16" cy="12" r="6" fill="#212121"/><path d="M4 32c0-6.6 5.4-12 12-12s12 5.4 12 12" fill="#212121"/></svg>',
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
/** Seed an approval-request notification in a given resolution state. */
|
|
60
|
+
const approvalSeed = (
|
|
61
|
+
n: number,
|
|
62
|
+
resolution: string | null,
|
|
63
|
+
resolvedByName: string | null,
|
|
64
|
+
minutesAgo: number,
|
|
65
|
+
): Notification => ({
|
|
66
|
+
id: `seed-approval-${n}`,
|
|
67
|
+
variant: 'warning',
|
|
68
|
+
title: 'Clear stuck print queue',
|
|
69
|
+
description: 'Mingo wants to restart the Spooler service on SRV-PRINT01.',
|
|
70
|
+
createdAt: Date.now() - 1_000 * 60 * minutesAgo,
|
|
71
|
+
read: false,
|
|
72
|
+
meta: {
|
|
73
|
+
contextType: ADMIN_APPROVAL_REQUEST_CONTEXT_TYPE,
|
|
74
|
+
approvalRequestId: `seed-req-${n}`,
|
|
75
|
+
approvalType: 'ADMIN',
|
|
76
|
+
resolution,
|
|
77
|
+
resolvedByName,
|
|
78
|
+
toolCalls: [
|
|
79
|
+
{
|
|
80
|
+
toolExecutionRequestId: `seed-ter-${n}`,
|
|
81
|
+
toolName: 'run_command',
|
|
82
|
+
toolTitle: 'Restart print spooler',
|
|
83
|
+
toolType: 'OPENFRAME_RMM',
|
|
84
|
+
toolExplanation: 'Restarts the Spooler service to clear a stuck print queue.',
|
|
85
|
+
requiresApproval: true,
|
|
86
|
+
toolCallArguments: { command: 'Restart-Service -Name Spooler -Force' },
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
47
92
|
const SEED: Notification[] = [
|
|
48
93
|
{
|
|
49
94
|
id: 'seed-1',
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
95
|
+
severity: 'INFO',
|
|
96
|
+
type: 'New Customer Created',
|
|
97
|
+
imageUrl: CUSTOMER_AVATAR,
|
|
98
|
+
title: 'Acme Corp',
|
|
99
|
+
description: 'Added by John Smith',
|
|
53
100
|
createdAt: Date.now() - 1_000 * 30,
|
|
54
101
|
read: false,
|
|
55
102
|
},
|
|
103
|
+
approvalSeed(1, null, null, 2),
|
|
56
104
|
{
|
|
57
105
|
id: 'seed-2',
|
|
58
106
|
variant: 'success',
|
|
59
|
-
|
|
60
|
-
|
|
107
|
+
type: 'Device Deployment Complete',
|
|
108
|
+
icon: <MonitorIcon size={16} />,
|
|
109
|
+
title: 'HP EliteBook',
|
|
110
|
+
description: 'Deployed to Marketing Dept',
|
|
61
111
|
createdAt: Date.now() - 1_000 * 60 * 5,
|
|
62
112
|
read: false,
|
|
63
113
|
},
|
|
64
114
|
{
|
|
65
115
|
id: 'seed-3',
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
116
|
+
severity: 'DANGER',
|
|
117
|
+
type: 'Policy Violation Detected',
|
|
118
|
+
icon: <BannedIcon size={16} />,
|
|
119
|
+
title: 'ACME-WS01',
|
|
120
|
+
description: 'USB storage device connected',
|
|
69
121
|
createdAt: Date.now() - 1_000 * 60 * 12,
|
|
70
122
|
read: false,
|
|
71
123
|
},
|
|
72
124
|
{
|
|
73
125
|
id: 'seed-4',
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
126
|
+
severity: 'WARNING',
|
|
127
|
+
type: 'Disk Space Check Failed',
|
|
128
|
+
icon: <AlertTriangleIcon size={16} />,
|
|
129
|
+
title: 'SRV-DB01',
|
|
130
|
+
description: 'C: drive at 95% capacity',
|
|
77
131
|
createdAt: Date.now() - 1_000 * 60 * 35,
|
|
78
132
|
read: false,
|
|
79
133
|
},
|
|
134
|
+
approvalSeed(2, 'APPROVED', 'John Smith', 45),
|
|
80
135
|
{
|
|
81
136
|
id: 'seed-5',
|
|
82
137
|
variant: 'success',
|
|
83
|
-
|
|
84
|
-
|
|
138
|
+
type: 'Script Execution Complete',
|
|
139
|
+
icon: <BracketCurlyIcon size={16} />,
|
|
140
|
+
title: 'Windows Update',
|
|
141
|
+
description: 'Script finished on 45 devices',
|
|
85
142
|
createdAt: Date.now() - 1_000 * 60 * 60,
|
|
86
143
|
read: false,
|
|
87
144
|
},
|
|
145
|
+
approvalSeed(3, 'REJECTED', 'Jane Doe', 90),
|
|
88
146
|
{
|
|
89
147
|
id: 'seed-6',
|
|
90
148
|
variant: 'warning',
|
|
@@ -93,6 +151,7 @@ const SEED: Notification[] = [
|
|
|
93
151
|
createdAt: Date.now() - 1_000 * 60 * 60 * 2,
|
|
94
152
|
read: false,
|
|
95
153
|
},
|
|
154
|
+
approvalSeed(4, 'CANCELLED', null, 180),
|
|
96
155
|
];
|
|
97
156
|
|
|
98
157
|
function AutoOpen() {
|
|
@@ -104,7 +163,9 @@ function AutoOpen() {
|
|
|
104
163
|
}
|
|
105
164
|
|
|
106
165
|
/**
|
|
107
|
-
* Static preview of every tile variant — settled (non-live) state.
|
|
166
|
+
* Static preview of every tile variant — settled (non-live) state. The `type`
|
|
167
|
+
* header label picks up the variant color; tiles without an icon/image fall
|
|
168
|
+
* back to the severity dot in the header slot.
|
|
108
169
|
*/
|
|
109
170
|
export const AllTileVariants: Story = {
|
|
110
171
|
render: () => {
|
|
@@ -115,6 +176,7 @@ export const AllTileVariants: Story = {
|
|
|
115
176
|
notification={{
|
|
116
177
|
id: 't-default',
|
|
117
178
|
variant: 'default',
|
|
179
|
+
type: 'System Notice',
|
|
118
180
|
title: 'Heads up',
|
|
119
181
|
description: 'Neutral notification, no specific status.',
|
|
120
182
|
...base,
|
|
@@ -125,8 +187,9 @@ export const AllTileVariants: Story = {
|
|
|
125
187
|
notification={{
|
|
126
188
|
id: 't-success',
|
|
127
189
|
variant: 'success',
|
|
128
|
-
|
|
129
|
-
|
|
190
|
+
type: 'Device Deployment Complete',
|
|
191
|
+
title: 'HP EliteBook',
|
|
192
|
+
description: 'Deployed to Marketing Dept',
|
|
130
193
|
...base,
|
|
131
194
|
}}
|
|
132
195
|
onComplete={() => {}}
|
|
@@ -135,8 +198,9 @@ export const AllTileVariants: Story = {
|
|
|
135
198
|
notification={{
|
|
136
199
|
id: 't-error',
|
|
137
200
|
variant: 'error',
|
|
138
|
-
|
|
139
|
-
|
|
201
|
+
type: 'Policy Violation Detected',
|
|
202
|
+
title: 'ACME-WS01',
|
|
203
|
+
description: 'USB storage device connected',
|
|
140
204
|
...base,
|
|
141
205
|
}}
|
|
142
206
|
onComplete={() => {}}
|
|
@@ -145,6 +209,7 @@ export const AllTileVariants: Story = {
|
|
|
145
209
|
notification={{
|
|
146
210
|
id: 't-warning',
|
|
147
211
|
variant: 'warning',
|
|
212
|
+
type: 'Approval Required',
|
|
148
213
|
title: 'Tech Required',
|
|
149
214
|
description: 'Approval is required to execute the command.',
|
|
150
215
|
...base,
|
|
@@ -155,12 +220,97 @@ export const AllTileVariants: Story = {
|
|
|
155
220
|
notification={{
|
|
156
221
|
id: 't-info',
|
|
157
222
|
variant: 'info',
|
|
223
|
+
type: 'Agent Update',
|
|
158
224
|
title: 'Update available',
|
|
159
225
|
description: 'A new agent version is ready to install.',
|
|
160
226
|
...base,
|
|
161
227
|
}}
|
|
162
228
|
onComplete={() => {}}
|
|
163
229
|
/>
|
|
230
|
+
<NotificationTile
|
|
231
|
+
notification={{
|
|
232
|
+
id: 't-legacy',
|
|
233
|
+
variant: 'default',
|
|
234
|
+
title: 'Legacy notification',
|
|
235
|
+
description: 'No type, icon or image — header shows the dot fallback.',
|
|
236
|
+
...base,
|
|
237
|
+
}}
|
|
238
|
+
onComplete={() => {}}
|
|
239
|
+
/>
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* The new header fields: `severity` colors the type label and icon slot
|
|
247
|
+
* (INFO grey, WARNING amber, DANGER red) and overrides `variant`; `imageUrl`
|
|
248
|
+
* wins over `icon`, which wins over the dot fallback.
|
|
249
|
+
*/
|
|
250
|
+
export const SeverityIconAndImage: Story = {
|
|
251
|
+
render: () => {
|
|
252
|
+
const base = { createdAt: Date.now() - 60_000, read: true } as const;
|
|
253
|
+
return (
|
|
254
|
+
<div className="flex max-w-md flex-col gap-2">
|
|
255
|
+
<NotificationTile
|
|
256
|
+
notification={{
|
|
257
|
+
id: 's-image',
|
|
258
|
+
severity: 'INFO',
|
|
259
|
+
type: 'New Customer Created',
|
|
260
|
+
imageUrl: CUSTOMER_AVATAR,
|
|
261
|
+
title: 'Acme Corp',
|
|
262
|
+
description: 'Added by John Smith',
|
|
263
|
+
...base,
|
|
264
|
+
}}
|
|
265
|
+
onComplete={() => {}}
|
|
266
|
+
/>
|
|
267
|
+
<NotificationTile
|
|
268
|
+
notification={{
|
|
269
|
+
id: 's-icon-info',
|
|
270
|
+
severity: 'INFO',
|
|
271
|
+
type: 'New User Invited',
|
|
272
|
+
icon: <UserPlusIcon size={16} />,
|
|
273
|
+
title: 'jane.doe@acme.com',
|
|
274
|
+
description: 'Invited by John Smith',
|
|
275
|
+
...base,
|
|
276
|
+
}}
|
|
277
|
+
onComplete={() => {}}
|
|
278
|
+
/>
|
|
279
|
+
<NotificationTile
|
|
280
|
+
notification={{
|
|
281
|
+
id: 's-icon-warning',
|
|
282
|
+
severity: 'WARNING',
|
|
283
|
+
type: 'Disk Space Warning',
|
|
284
|
+
icon: <AlertTriangleIcon size={16} />,
|
|
285
|
+
title: 'SRV-DB01',
|
|
286
|
+
description: 'C: drive at 85% capacity',
|
|
287
|
+
...base,
|
|
288
|
+
}}
|
|
289
|
+
onComplete={() => {}}
|
|
290
|
+
/>
|
|
291
|
+
<NotificationTile
|
|
292
|
+
notification={{
|
|
293
|
+
id: 's-icon-danger',
|
|
294
|
+
severity: 'DANGER',
|
|
295
|
+
type: 'Policy Violation Detected',
|
|
296
|
+
icon: <BannedIcon size={16} />,
|
|
297
|
+
title: 'ACME-WS01',
|
|
298
|
+
description: 'USB storage device connected',
|
|
299
|
+
...base,
|
|
300
|
+
}}
|
|
301
|
+
onComplete={() => {}}
|
|
302
|
+
/>
|
|
303
|
+
<NotificationTile
|
|
304
|
+
notification={{
|
|
305
|
+
id: 's-dot-danger',
|
|
306
|
+
severity: 'DANGER',
|
|
307
|
+
type: 'Agent Offline',
|
|
308
|
+
title: 'ACME-WS07',
|
|
309
|
+
description: 'No icon configured — severity dot fallback.',
|
|
310
|
+
...base,
|
|
311
|
+
}}
|
|
312
|
+
onComplete={() => {}}
|
|
313
|
+
/>
|
|
164
314
|
</div>
|
|
165
315
|
);
|
|
166
316
|
},
|
|
@@ -179,9 +329,11 @@ export const LiveTile: Story = {
|
|
|
179
329
|
key={tick}
|
|
180
330
|
notification={{
|
|
181
331
|
id: 'live-tile',
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
332
|
+
severity: 'INFO',
|
|
333
|
+
type: 'New Customer Created',
|
|
334
|
+
imageUrl: CUSTOMER_AVATAR,
|
|
335
|
+
title: 'Acme Corp',
|
|
336
|
+
description: 'Added by John Smith',
|
|
185
337
|
createdAt: Date.now(),
|
|
186
338
|
read: false,
|
|
187
339
|
}}
|
|
@@ -212,7 +364,9 @@ export const DrawerEmpty: Story = {
|
|
|
212
364
|
|
|
213
365
|
/**
|
|
214
366
|
* Drawer pre-seeded to mirror the Figma reference, with the toggle row and the
|
|
215
|
-
* history button wired.
|
|
367
|
+
* history button wired. Includes approval-request notifications in every
|
|
368
|
+
* resolution state: pending (buttons), approved, rejected and cancelled
|
|
369
|
+
* (status tag + resolver name in place of the buttons).
|
|
216
370
|
*/
|
|
217
371
|
export const DrawerWithSeedData: Story = {
|
|
218
372
|
render: function DrawerWithSeedDataRender() {
|
|
@@ -222,11 +376,13 @@ export const DrawerWithSeedData: Story = {
|
|
|
222
376
|
onHistoryClick={() => alert('Navigate to /notifications')}
|
|
223
377
|
historyHref="/notifications"
|
|
224
378
|
onShowPopupsChange={(v) => console.log('showPopups →', v)}
|
|
379
|
+
renderTile={renderApprovalTile}
|
|
225
380
|
>
|
|
226
381
|
<AutoOpen />
|
|
227
382
|
<NotificationDrawer />
|
|
228
383
|
<p className="text-h6 text-ods-text-secondary">
|
|
229
|
-
|
|
384
|
+
Seeded notifications matching the Figma reference, including approvals
|
|
385
|
+
in all four resolution states.
|
|
230
386
|
</p>
|
|
231
387
|
</NotificationsProvider>
|
|
232
388
|
);
|
|
@@ -312,8 +468,13 @@ function PlaygroundControls() {
|
|
|
312
468
|
useNotifications();
|
|
313
469
|
const approvalSeq = React.useRef(0);
|
|
314
470
|
|
|
315
|
-
const fire = (
|
|
316
|
-
|
|
471
|
+
const fire = (
|
|
472
|
+
variant: NotificationVariant,
|
|
473
|
+
title: string,
|
|
474
|
+
description: string,
|
|
475
|
+
extra?: Partial<Pick<Notification, 'type' | 'icon' | 'imageUrl' | 'severity'>>,
|
|
476
|
+
) => {
|
|
477
|
+
addNotification({ variant, title, description, ...extra });
|
|
317
478
|
};
|
|
318
479
|
|
|
319
480
|
const fireWithDeepLink = () => {
|
|
@@ -417,7 +578,9 @@ function PlaygroundControls() {
|
|
|
417
578
|
<Button
|
|
418
579
|
variant="accent"
|
|
419
580
|
onClick={() =>
|
|
420
|
-
fire('success', '
|
|
581
|
+
fire('success', 'HP EliteBook', 'Deployed to Marketing Dept', {
|
|
582
|
+
type: 'Device Deployment Complete',
|
|
583
|
+
})
|
|
421
584
|
}
|
|
422
585
|
>
|
|
423
586
|
Fire success
|
|
@@ -425,23 +588,49 @@ function PlaygroundControls() {
|
|
|
425
588
|
<Button
|
|
426
589
|
variant="destructive"
|
|
427
590
|
onClick={() =>
|
|
428
|
-
fire('error', '
|
|
591
|
+
fire('error', 'ACME-WS01', 'USB storage device connected', {
|
|
592
|
+
type: 'Policy Violation Detected',
|
|
593
|
+
severity: 'DANGER',
|
|
594
|
+
icon: <BannedIcon size={16} />,
|
|
595
|
+
})
|
|
429
596
|
}
|
|
430
597
|
>
|
|
431
|
-
Fire
|
|
598
|
+
Fire danger + icon
|
|
432
599
|
</Button>
|
|
433
600
|
<Button
|
|
434
601
|
variant="outline"
|
|
435
|
-
onClick={() =>
|
|
602
|
+
onClick={() =>
|
|
603
|
+
fire('warning', 'Tech Required', 'Approval is required to execute the command.', {
|
|
604
|
+
type: 'Approval Required',
|
|
605
|
+
severity: 'WARNING',
|
|
606
|
+
icon: <AlertTriangleIcon size={16} />,
|
|
607
|
+
})
|
|
608
|
+
}
|
|
436
609
|
>
|
|
437
|
-
Fire warning
|
|
610
|
+
Fire warning + icon
|
|
438
611
|
</Button>
|
|
439
612
|
<Button
|
|
440
613
|
variant="transparent"
|
|
441
|
-
onClick={() =>
|
|
614
|
+
onClick={() =>
|
|
615
|
+
fire('info', 'Update available', 'A new agent version is ready to install.', {
|
|
616
|
+
type: 'Agent Update',
|
|
617
|
+
})
|
|
618
|
+
}
|
|
442
619
|
>
|
|
443
620
|
Fire info
|
|
444
621
|
</Button>
|
|
622
|
+
<Button
|
|
623
|
+
variant="outline"
|
|
624
|
+
onClick={() =>
|
|
625
|
+
fire('info', 'Acme Corp', 'Added by John Smith', {
|
|
626
|
+
type: 'New Customer Created',
|
|
627
|
+
severity: 'INFO',
|
|
628
|
+
imageUrl: CUSTOMER_AVATAR,
|
|
629
|
+
})
|
|
630
|
+
}
|
|
631
|
+
>
|
|
632
|
+
Fire with image
|
|
633
|
+
</Button>
|
|
445
634
|
<Button
|
|
446
635
|
variant="outline"
|
|
447
636
|
onClick={() => fire('default', 'Heads up', 'Neutral notification without a specific status.')}
|
package/src/types/navigation.ts
CHANGED
package/tailwind.config.ts
CHANGED
|
@@ -199,7 +199,10 @@ const config: Config = {
|
|
|
199
199
|
pink: "var(--ods-flamingo-pink-base)",
|
|
200
200
|
cyan: "var(--ods-flamingo-cyan-base)",
|
|
201
201
|
},
|
|
202
|
-
"open-yellow":
|
|
202
|
+
"open-yellow": {
|
|
203
|
+
DEFAULT: "var(--ods-open-yellow-base)",
|
|
204
|
+
secondary: "var(--ods-open-yellow-secondary)",
|
|
205
|
+
},
|
|
203
206
|
|
|
204
207
|
// Attention tokens (ods-colors.css). Components reference these BY
|
|
205
208
|
// NAME (error-state.tsx: `border-ods-attention-red-error` etc.) —
|