@campfire-interactive/shell-header 0.6.5 → 0.8.1
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/index.d.ts +48 -2
- package/dist/index.js +197 -51
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,45 @@ interface CurrencyOption {
|
|
|
44
44
|
/** Human-readable label shown in the dropdown, e.g. "US Dollar", "Euro". */
|
|
45
45
|
label: string;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Build/deploy metadata for one side of the stack (webapp or API).
|
|
49
|
+
*
|
|
50
|
+
* `rolloutDate` is optional: the webapp side always has one (written
|
|
51
|
+
* by each deploy step to `rollout.json`); the backend side currently
|
|
52
|
+
* does not (the Lambda env-var update needed to inject one is a known
|
|
53
|
+
* footgun and isn't worth the risk for "when was this last deployed").
|
|
54
|
+
* The modal conditionally renders the "Deployed" row when present.
|
|
55
|
+
*/
|
|
56
|
+
interface VersionInfo {
|
|
57
|
+
/** Short git SHA of the commit the build came from, e.g. "a4b1c8d". */
|
|
58
|
+
revisionId: string;
|
|
59
|
+
/** ISO 8601 commit date of that SHA. */
|
|
60
|
+
revisionDate: string;
|
|
61
|
+
/**
|
|
62
|
+
* ISO 8601 timestamp of when this artifact was deployed to the current
|
|
63
|
+
* environment. Written separately from the build artifact so promotions
|
|
64
|
+
* (e.g. dev → prod) reflect the destination deploy time, not the build time.
|
|
65
|
+
*/
|
|
66
|
+
rolloutDate?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build/deploy metadata surfaced by the hidden About modal (Ctrl+Alt+A).
|
|
70
|
+
* All fields are host-supplied; the package never fetches anything itself.
|
|
71
|
+
*
|
|
72
|
+
* `webapp` is always shown; `api` is optional and renders as a second
|
|
73
|
+
* section when present. This lets apps adopt the webapp side before
|
|
74
|
+
* their API has the matching `/v1/version` endpoint wired up.
|
|
75
|
+
*/
|
|
76
|
+
interface AboutInfo {
|
|
77
|
+
/** Display name of the app, e.g. "OMSF". */
|
|
78
|
+
productTitle: string;
|
|
79
|
+
/** Environment label, e.g. "dev", "prod", "pr-42". */
|
|
80
|
+
environment: string;
|
|
81
|
+
/** Webapp build + deploy metadata (always rendered). */
|
|
82
|
+
webapp: VersionInfo;
|
|
83
|
+
/** API build + deploy metadata (rendered as a second section when present). */
|
|
84
|
+
api?: VersionInfo;
|
|
85
|
+
}
|
|
47
86
|
/**
|
|
48
87
|
* Subset of the platform-notifications `Notification` shape that the bell UI
|
|
49
88
|
* actually renders. Hosts typically map their fetched items (e.g., from
|
|
@@ -147,13 +186,20 @@ interface ShellHeaderProps {
|
|
|
147
186
|
* endpoint) and for propagating it through the rest of the UI.
|
|
148
187
|
*/
|
|
149
188
|
onCurrencyChange?: (currency: string) => void | Promise<void>;
|
|
189
|
+
/**
|
|
190
|
+
* Optional build/deploy metadata. When provided, enables a hidden
|
|
191
|
+
* Ctrl+Alt+A (Cmd+Opt+A on Mac) shortcut that opens an About modal
|
|
192
|
+
* showing this metadata with a copy-to-clipboard button. Omitting this
|
|
193
|
+
* prop disables the shortcut and renders nothing extra.
|
|
194
|
+
*/
|
|
195
|
+
about?: AboutInfo;
|
|
150
196
|
/** Fixed width for the app brand area (e.g., to align with a sidebar below). */
|
|
151
197
|
brandWidth?: number;
|
|
152
198
|
/** Optional content in the left/center area (filters, search, breadcrumbs, etc.) */
|
|
153
199
|
children?: React.ReactNode;
|
|
154
200
|
}
|
|
155
201
|
|
|
156
|
-
declare function ShellHeader({ appId, user, authorizedApps, currentTenantRole, notificationCount, onNotificationClick, notifications, onMarkRead, onMarkAllRead, onNotificationItemClick, onLogout, locale, supportedLocales, onLocaleChange, currency, supportedCurrencies, onCurrencyChange, tenant, tenants, onTenantSwitch, brandWidth, children, }: ShellHeaderProps): react_jsx_runtime.JSX.Element;
|
|
202
|
+
declare function ShellHeader({ appId, user, authorizedApps, currentTenantRole, notificationCount, onNotificationClick, notifications, onMarkRead, onMarkAllRead, onNotificationItemClick, onLogout, locale, supportedLocales, onLocaleChange, currency, supportedCurrencies, onCurrencyChange, tenant, tenants, onTenantSwitch, about, brandWidth, children, }: ShellHeaderProps): react_jsx_runtime.JSX.Element;
|
|
157
203
|
|
|
158
204
|
interface LocaleSwitcherProps {
|
|
159
205
|
currentLocale: string;
|
|
@@ -171,4 +217,4 @@ declare const appCatalog: AppDefinition[];
|
|
|
171
217
|
*/
|
|
172
218
|
declare function getAppUrl(app: AppDefinition): string;
|
|
173
219
|
|
|
174
|
-
export { type AppDefinition, type LocaleOption, LocaleSwitcher, type NotificationItem, ShellHeader, type ShellHeaderProps, type ShellUser, appCatalog, getAppUrl };
|
|
220
|
+
export { type AboutInfo, type AppDefinition, type CurrencyOption, type LocaleOption, LocaleSwitcher, type NotificationItem, ShellHeader, type ShellHeaderProps, type ShellUser, type VersionInfo, appCatalog, getAppUrl };
|
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ function styleInject(css, { insertAt } = {}) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// src/styles.css
|
|
27
|
-
styleInject(".cfi-sh-header {\n display: flex;\n align-items: center;\n height: var(--cfi-shell-header-height, 48px);\n padding: 0 var(--cfi-spacing-md, 1rem);\n background: white;\n color: var(--cfi-color-gray-900, #111827);\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n position: relative;\n z-index: 1000;\n}\n.cfi-sh-left {\n display: flex;\n align-items: center;\n flex: 1;\n gap: var(--cfi-spacing-md, 1rem);\n overflow: hidden;\n}\n.cfi-sh-app-brand {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n flex-shrink: 0;\n}\n.cfi-sh-app-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n color: white;\n font-size: 14px;\n font-weight: 700;\n font-family: inherit;\n border-radius: var(--cfi-radius-md, 0.375rem);\n flex-shrink: 0;\n}\n.cfi-sh-app-title {\n font-weight: 600;\n font-size: var(--cfi-font-size-base, 1rem);\n color: var(--cfi-color-gray-900, #111827);\n white-space: nowrap;\n}\n.cfi-sh-right {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n flex-shrink: 0;\n}\n.cfi-sh-icon-btn {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: transparent;\n border: none;\n border-radius: 50%;\n color: var(--cfi-color-gray-600, #4b5563);\n cursor: pointer;\n transition: background 150ms;\n}\n.cfi-sh-icon-btn:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-badge {\n position: absolute;\n top: 2px;\n right: 2px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n background: var(--cfi-color-error, #ef4444);\n color: white;\n font-size: 10px;\n font-weight: 600;\n line-height: 16px;\n text-align: center;\n border-radius: 99px;\n}\n.cfi-sh-app-switcher {\n position: relative;\n}\n.cfi-sh-app-grid {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding: var(--cfi-spacing-sm, 0.5rem);\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n min-width: 280px;\n}\n.cfi-sh-app-grid-item {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-xs, 0.25rem);\n border-radius: var(--cfi-radius-md, 0.375rem);\n color: var(--cfi-color-gray-700, #374151);\n text-decoration: none;\n cursor: pointer;\n transition: background 100ms;\n font-family: inherit;\n}\n.cfi-sh-app-grid-item:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-app-grid-item-active {\n background: var(--cfi-color-primary-50, #fff7ed);\n color: var(--cfi-color-primary-700, #c2410c);\n}\n.cfi-sh-app-grid-item-active:hover {\n background: var(--cfi-color-primary-100, #ffedd5);\n}\n.cfi-sh-app-grid-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n}\n.cfi-sh-app-grid-label {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-align: center;\n line-height: 1.2;\n max-width: 80px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cfi-sh-user-menu {\n position: relative;\n}\n.cfi-sh-avatar-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n background: var(--cfi-color-primary-600, #ea580c);\n border: none;\n border-radius: 50%;\n cursor: pointer;\n transition: opacity 150ms;\n}\n.cfi-sh-avatar-btn:hover {\n opacity: 0.85;\n}\n.cfi-sh-avatar-img {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n object-fit: cover;\n}\n.cfi-sh-avatar-initials {\n color: white;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n font-family: inherit;\n}\n.cfi-sh-user-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: 200px;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n}\n.cfi-sh-user-header {\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n display: flex;\n flex-direction: column;\n}\n.cfi-sh-user-name {\n font-weight: 500;\n color: var(--cfi-color-gray-900, #111827);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-user-email {\n color: var(--cfi-color-gray-500, #6b7280);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n}\n.cfi-sh-divider {\n height: 1px;\n background: var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-menu-item {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n width: 100%;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n background: none;\n border: none;\n color: var(--cfi-color-gray-700, #374151);\n cursor: pointer;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-menu-item:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-menu-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.cfi-sh-locale-switcher {\n position: relative;\n}\n.cfi-sh-locale-btn {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n height: 32px;\n padding: 0 var(--cfi-spacing-sm, 0.5rem);\n background: transparent;\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-md, 0.375rem);\n color: var(--cfi-color-gray-700, #374151);\n cursor: pointer;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-locale-btn:hover:not(:disabled) {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-locale-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.cfi-sh-locale-label {\n letter-spacing: 0.02em;\n}\n.cfi-sh-locale-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: 180px;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n padding: var(--cfi-spacing-xs, 0.25rem) 0;\n}\n.cfi-sh-locale-dropdown .cfi-sh-menu-item {\n justify-content: space-between;\n}\n.cfi-sh-locale-item-active {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-locale-item-label {\n flex: 1;\n text-align: left;\n}\n.cfi-sh-locale-item-code {\n color: var(--cfi-color-gray-400, #9ca3af);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n letter-spacing: 0.02em;\n}\n.cfi-sh-locale-row {\n justify-content: flex-start;\n}\n.cfi-sh-locale-row-label {\n flex: 1;\n text-align: left;\n}\n.cfi-sh-locale-row-current {\n color: var(--cfi-color-gray-500, #6b7280);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n margin-right: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-locale-chevron {\n transition: transform 150ms ease;\n flex-shrink: 0;\n}\n.cfi-sh-locale-chevron-open {\n transform: rotate(180deg);\n}\n.cfi-sh-locale-sublist {\n border-top: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-sh-locale-sub-item {\n padding-left: calc(var(--cfi-spacing-md, 1rem) + var(--cfi-spacing-md, 1rem));\n justify-content: space-between;\n}\n.cfi-sh-tenant-static {\n cursor: default;\n pointer-events: none;\n}\n.cfi-sh-tenant-check {\n color: var(--cfi-color-violet-600, #7c3aed);\n}\n.cfi-sh-notif {\n position: relative;\n}\n.cfi-sh-notif-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n width: 360px;\n max-height: 480px;\n display: flex;\n flex-direction: column;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n overflow: hidden;\n}\n.cfi-sh-notif-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n flex-shrink: 0;\n}\n.cfi-sh-notif-title {\n font-weight: 600;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-notif-mark-all {\n background: none;\n border: none;\n color: var(--cfi-color-primary-600, #ea580c);\n cursor: pointer;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-family: inherit;\n padding: 0;\n}\n.cfi-sh-notif-mark-all:hover {\n text-decoration: underline;\n}\n.cfi-sh-notif-list {\n overflow-y: auto;\n flex: 1;\n}\n.cfi-sh-notif-empty {\n padding: var(--cfi-spacing-lg, 1.5rem) var(--cfi-spacing-md, 1rem);\n color: var(--cfi-color-gray-500, #6b7280);\n text-align: center;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-notif-item {\n display: flex;\n align-items: flex-start;\n gap: var(--cfi-spacing-sm, 0.5rem);\n width: 100%;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n background: none;\n border: none;\n border-bottom: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n text-align: left;\n cursor: pointer;\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-notif-item:hover {\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-sh-notif-item:last-child {\n border-bottom: none;\n}\n.cfi-sh-notif-dot {\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n margin-top: 6px;\n background: var(--cfi-color-primary-600, #ea580c);\n border-radius: 50%;\n}\n.cfi-sh-notif-dot-placeholder {\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n margin-top: 6px;\n}\n.cfi-sh-notif-content {\n flex: 1;\n min-width: 0;\n}\n.cfi-sh-notif-item-title {\n font-weight: 500;\n color: var(--cfi-color-gray-900, #111827);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n margin-bottom: 2px;\n}\n.cfi-sh-notif-item-unread .cfi-sh-notif-item-title {\n font-weight: 600;\n}\n.cfi-sh-notif-item-body {\n color: var(--cfi-color-gray-600, #4b5563);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.cfi-sh-notif-item-time {\n color: var(--cfi-color-gray-400, #9ca3af);\n font-size: 11px;\n margin-top: 4px;\n}\n");
|
|
27
|
+
styleInject(".cfi-sh-header {\n display: flex;\n align-items: center;\n height: var(--cfi-shell-header-height, 48px);\n padding: 0 var(--cfi-spacing-md, 1rem);\n background: white;\n color: var(--cfi-color-gray-900, #111827);\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n position: relative;\n z-index: 1000;\n}\n.cfi-sh-left {\n display: flex;\n align-items: center;\n flex: 1;\n gap: var(--cfi-spacing-md, 1rem);\n overflow: hidden;\n}\n.cfi-sh-app-brand {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n flex-shrink: 0;\n}\n.cfi-sh-app-badge {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n color: white;\n font-size: 14px;\n font-weight: 700;\n font-family: inherit;\n border-radius: var(--cfi-radius-md, 0.375rem);\n flex-shrink: 0;\n}\n.cfi-sh-app-title {\n font-weight: 600;\n font-size: var(--cfi-font-size-base, 1rem);\n color: var(--cfi-color-gray-900, #111827);\n white-space: nowrap;\n}\n.cfi-sh-right {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n flex-shrink: 0;\n}\n.cfi-sh-icon-btn {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: transparent;\n border: none;\n border-radius: 50%;\n color: var(--cfi-color-gray-600, #4b5563);\n cursor: pointer;\n transition: background 150ms;\n}\n.cfi-sh-icon-btn:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-badge {\n position: absolute;\n top: 2px;\n right: 2px;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n background: var(--cfi-color-error, #ef4444);\n color: white;\n font-size: 10px;\n font-weight: 600;\n line-height: 16px;\n text-align: center;\n border-radius: 99px;\n}\n.cfi-sh-app-switcher {\n position: relative;\n}\n.cfi-sh-app-grid {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding: var(--cfi-spacing-sm, 0.5rem);\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n min-width: 280px;\n}\n.cfi-sh-app-grid-item {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-xs, 0.25rem);\n border-radius: var(--cfi-radius-md, 0.375rem);\n color: var(--cfi-color-gray-700, #374151);\n text-decoration: none;\n cursor: pointer;\n transition: background 100ms;\n font-family: inherit;\n}\n.cfi-sh-app-grid-item:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-app-grid-item-active {\n background: var(--cfi-color-primary-50, #fff7ed);\n color: var(--cfi-color-primary-700, #c2410c);\n}\n.cfi-sh-app-grid-item-active:hover {\n background: var(--cfi-color-primary-100, #ffedd5);\n}\n.cfi-sh-app-grid-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n}\n.cfi-sh-app-grid-label {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-align: center;\n line-height: 1.2;\n max-width: 80px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.cfi-sh-user-menu {\n position: relative;\n}\n.cfi-sh-avatar-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n background: var(--cfi-color-primary-600, #ea580c);\n border: none;\n border-radius: 50%;\n cursor: pointer;\n transition: opacity 150ms;\n}\n.cfi-sh-avatar-btn:hover {\n opacity: 0.85;\n}\n.cfi-sh-avatar-img {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n object-fit: cover;\n}\n.cfi-sh-avatar-initials {\n color: white;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n font-family: inherit;\n}\n.cfi-sh-user-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: 200px;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n}\n.cfi-sh-user-header {\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n display: flex;\n flex-direction: column;\n}\n.cfi-sh-user-name {\n font-weight: 500;\n color: var(--cfi-color-gray-900, #111827);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-user-email {\n color: var(--cfi-color-gray-500, #6b7280);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n}\n.cfi-sh-divider {\n height: 1px;\n background: var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-menu-item {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n width: 100%;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n background: none;\n border: none;\n color: var(--cfi-color-gray-700, #374151);\n cursor: pointer;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-menu-item:hover {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-menu-item:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.cfi-sh-locale-switcher {\n position: relative;\n}\n.cfi-sh-locale-btn {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-xs, 0.25rem);\n height: 32px;\n padding: 0 var(--cfi-spacing-sm, 0.5rem);\n background: transparent;\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n border-radius: var(--cfi-radius-md, 0.375rem);\n color: var(--cfi-color-gray-700, #374151);\n cursor: pointer;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-locale-btn:hover:not(:disabled) {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-locale-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.cfi-sh-locale-label {\n letter-spacing: 0.02em;\n}\n.cfi-sh-locale-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n min-width: 180px;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n padding: var(--cfi-spacing-xs, 0.25rem) 0;\n}\n.cfi-sh-locale-dropdown .cfi-sh-menu-item {\n justify-content: space-between;\n}\n.cfi-sh-locale-item-active {\n background: var(--cfi-color-gray-100, #f3f4f6);\n}\n.cfi-sh-locale-item-label {\n flex: 1;\n text-align: left;\n}\n.cfi-sh-locale-item-code {\n color: var(--cfi-color-gray-400, #9ca3af);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-weight: 600;\n letter-spacing: 0.02em;\n}\n.cfi-sh-locale-row {\n justify-content: flex-start;\n}\n.cfi-sh-locale-row-label {\n flex: 1;\n text-align: left;\n}\n.cfi-sh-locale-row-current {\n color: var(--cfi-color-gray-500, #6b7280);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n margin-right: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-locale-chevron {\n transition: transform 150ms ease;\n flex-shrink: 0;\n}\n.cfi-sh-locale-chevron-open {\n transform: rotate(180deg);\n}\n.cfi-sh-locale-sublist {\n border-top: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-sh-locale-sub-item {\n padding-left: calc(var(--cfi-spacing-md, 1rem) + var(--cfi-spacing-md, 1rem));\n justify-content: space-between;\n}\n.cfi-sh-tenant-static {\n cursor: default;\n pointer-events: none;\n}\n.cfi-sh-tenant-check {\n color: var(--cfi-color-violet-600, #7c3aed);\n}\n.cfi-sh-notif {\n position: relative;\n}\n.cfi-sh-notif-dropdown {\n position: absolute;\n top: calc(100% + 8px);\n right: 0;\n width: 360px;\n max-height: 480px;\n display: flex;\n flex-direction: column;\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: var(--cfi-shadow-lg, 0 10px 15px rgba(0, 0, 0, 0.1));\n border: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n z-index: 1001;\n overflow: hidden;\n}\n.cfi-sh-notif-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n flex-shrink: 0;\n}\n.cfi-sh-notif-title {\n font-weight: 600;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-notif-mark-all {\n background: none;\n border: none;\n color: var(--cfi-color-primary-600, #ea580c);\n cursor: pointer;\n font-size: var(--cfi-font-size-xs, 0.75rem);\n font-family: inherit;\n padding: 0;\n}\n.cfi-sh-notif-mark-all:hover {\n text-decoration: underline;\n}\n.cfi-sh-notif-list {\n overflow-y: auto;\n flex: 1;\n}\n.cfi-sh-notif-empty {\n padding: var(--cfi-spacing-lg, 1.5rem) var(--cfi-spacing-md, 1rem);\n color: var(--cfi-color-gray-500, #6b7280);\n text-align: center;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-notif-item {\n display: flex;\n align-items: flex-start;\n gap: var(--cfi-spacing-sm, 0.5rem);\n width: 100%;\n padding: var(--cfi-spacing-sm, 0.5rem) var(--cfi-spacing-md, 1rem);\n background: none;\n border: none;\n border-bottom: 1px solid var(--cfi-color-gray-100, #f3f4f6);\n text-align: left;\n cursor: pointer;\n font-family: inherit;\n transition: background 100ms;\n}\n.cfi-sh-notif-item:hover {\n background: var(--cfi-color-gray-50, #f9fafb);\n}\n.cfi-sh-notif-item:last-child {\n border-bottom: none;\n}\n.cfi-sh-notif-dot {\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n margin-top: 6px;\n background: var(--cfi-color-primary-600, #ea580c);\n border-radius: 50%;\n}\n.cfi-sh-notif-dot-placeholder {\n flex-shrink: 0;\n width: 8px;\n height: 8px;\n margin-top: 6px;\n}\n.cfi-sh-notif-content {\n flex: 1;\n min-width: 0;\n}\n.cfi-sh-notif-item-title {\n font-weight: 500;\n color: var(--cfi-color-gray-900, #111827);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n margin-bottom: 2px;\n}\n.cfi-sh-notif-item-unread .cfi-sh-notif-item-title {\n font-weight: 600;\n}\n.cfi-sh-notif-item-body {\n color: var(--cfi-color-gray-600, #4b5563);\n font-size: var(--cfi-font-size-xs, 0.75rem);\n line-height: 1.4;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.cfi-sh-notif-item-time {\n color: var(--cfi-color-gray-400, #9ca3af);\n font-size: 11px;\n margin-top: 4px;\n}\n.cfi-sh-about-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.4);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1100;\n font-family: var(--cfi-font-family, system-ui, sans-serif);\n}\n.cfi-sh-about-card {\n background: white;\n border-radius: var(--cfi-radius-lg, 0.5rem);\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n min-width: 360px;\n max-width: 500px;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-about-header {\n display: flex;\n align-items: center;\n gap: var(--cfi-spacing-sm, 0.5rem);\n padding: var(--cfi-spacing-md, 1rem);\n border-bottom: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-about-title {\n font-weight: 600;\n font-size: var(--cfi-font-size-lg, 1.125rem);\n flex: 1;\n}\n.cfi-sh-about-env {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n background: var(--cfi-color-gray-100, #f3f4f6);\n color: var(--cfi-color-gray-700, #374151);\n padding: 2px 8px;\n border-radius: var(--cfi-radius-sm, 0.25rem);\n font-weight: 600;\n}\n.cfi-sh-about-close {\n background: none;\n border: none;\n font-size: 1.5rem;\n line-height: 1;\n cursor: pointer;\n color: var(--cfi-color-gray-500, #6b7280);\n padding: 0 4px;\n font-family: inherit;\n}\n.cfi-sh-about-close:hover {\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-about-body {\n padding: var(--cfi-spacing-md, 1rem);\n display: flex;\n flex-direction: column;\n gap: var(--cfi-spacing-md, 1rem);\n}\n.cfi-sh-about-section-title {\n font-size: var(--cfi-font-size-xs, 0.75rem);\n text-transform: uppercase;\n letter-spacing: 0.05em;\n font-weight: 600;\n color: var(--cfi-color-gray-500, #6b7280);\n margin-bottom: var(--cfi-spacing-xs, 0.25rem);\n}\n.cfi-sh-about-list {\n display: grid;\n grid-template-columns: auto 1fr;\n gap: var(--cfi-spacing-xs, 0.25rem) var(--cfi-spacing-md, 1rem);\n margin: 0;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n}\n.cfi-sh-about-list dt {\n color: var(--cfi-color-gray-600, #4b5563);\n font-weight: 500;\n}\n.cfi-sh-about-list dd {\n margin: 0;\n color: var(--cfi-color-gray-900, #111827);\n}\n.cfi-sh-about-list code {\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Consolas,\n monospace;\n font-size: var(--cfi-font-size-sm, 0.875rem);\n background: var(--cfi-color-gray-100, #f3f4f6);\n padding: 1px 6px;\n border-radius: var(--cfi-radius-sm, 0.25rem);\n}\n.cfi-sh-about-date {\n color: var(--cfi-color-gray-500, #6b7280);\n}\n.cfi-sh-about-footer {\n display: flex;\n justify-content: flex-end;\n padding: var(--cfi-spacing-md, 1rem);\n border-top: 1px solid var(--cfi-color-gray-200, #e5e7eb);\n}\n.cfi-sh-about-copy {\n background: var(--cfi-color-gray-100, #f3f4f6);\n border: 1px solid var(--cfi-color-gray-300, #d1d5db);\n color: var(--cfi-color-gray-900, #111827);\n padding: 6px 12px;\n border-radius: var(--cfi-radius-md, 0.375rem);\n font-size: var(--cfi-font-size-sm, 0.875rem);\n font-weight: 500;\n cursor: pointer;\n font-family: inherit;\n transition: background 150ms;\n}\n.cfi-sh-about-copy:hover {\n background: var(--cfi-color-gray-200, #e5e7eb);\n}\n");
|
|
28
28
|
|
|
29
29
|
// src/appCatalog.ts
|
|
30
30
|
var appCatalog = [
|
|
@@ -496,8 +496,149 @@ function UserMenu({
|
|
|
496
496
|
] });
|
|
497
497
|
}
|
|
498
498
|
|
|
499
|
+
// src/AboutModal.tsx
|
|
500
|
+
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
501
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
502
|
+
function AboutModal({ about, onClose }) {
|
|
503
|
+
const [copied, setCopied] = useState4(false);
|
|
504
|
+
useEffect4(() => {
|
|
505
|
+
function handleEsc(e) {
|
|
506
|
+
if (e.key === "Escape") onClose();
|
|
507
|
+
}
|
|
508
|
+
document.addEventListener("keydown", handleEsc);
|
|
509
|
+
return () => document.removeEventListener("keydown", handleEsc);
|
|
510
|
+
}, [onClose]);
|
|
511
|
+
async function handleCopy() {
|
|
512
|
+
const text = formatClipboardPayload(about);
|
|
513
|
+
try {
|
|
514
|
+
await navigator.clipboard.writeText(text);
|
|
515
|
+
setCopied(true);
|
|
516
|
+
window.setTimeout(() => setCopied(false), 2e3);
|
|
517
|
+
} catch {
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const hasApi = about.api !== void 0;
|
|
521
|
+
return /* @__PURE__ */ jsx5(
|
|
522
|
+
"div",
|
|
523
|
+
{
|
|
524
|
+
className: "cfi-sh-about-overlay",
|
|
525
|
+
role: "dialog",
|
|
526
|
+
"aria-modal": "true",
|
|
527
|
+
"aria-label": "About this app",
|
|
528
|
+
onClick: (e) => {
|
|
529
|
+
if (e.target === e.currentTarget) onClose();
|
|
530
|
+
},
|
|
531
|
+
children: /* @__PURE__ */ jsxs5("div", { className: "cfi-sh-about-card", children: [
|
|
532
|
+
/* @__PURE__ */ jsxs5("div", { className: "cfi-sh-about-header", children: [
|
|
533
|
+
/* @__PURE__ */ jsx5("span", { className: "cfi-sh-about-title", children: about.productTitle }),
|
|
534
|
+
/* @__PURE__ */ jsx5("span", { className: "cfi-sh-about-env", children: about.environment }),
|
|
535
|
+
/* @__PURE__ */ jsx5(
|
|
536
|
+
"button",
|
|
537
|
+
{
|
|
538
|
+
className: "cfi-sh-about-close",
|
|
539
|
+
type: "button",
|
|
540
|
+
onClick: onClose,
|
|
541
|
+
"aria-label": "Close",
|
|
542
|
+
children: "\xD7"
|
|
543
|
+
}
|
|
544
|
+
)
|
|
545
|
+
] }),
|
|
546
|
+
/* @__PURE__ */ jsxs5("div", { className: "cfi-sh-about-body", children: [
|
|
547
|
+
/* @__PURE__ */ jsx5(VersionSection, { title: hasApi ? "Webapp" : null, version: about.webapp }),
|
|
548
|
+
about.api && /* @__PURE__ */ jsx5(VersionSection, { title: "API", version: about.api })
|
|
549
|
+
] }),
|
|
550
|
+
/* @__PURE__ */ jsx5("div", { className: "cfi-sh-about-footer", children: /* @__PURE__ */ jsx5(
|
|
551
|
+
"button",
|
|
552
|
+
{
|
|
553
|
+
className: "cfi-sh-about-copy",
|
|
554
|
+
type: "button",
|
|
555
|
+
onClick: handleCopy,
|
|
556
|
+
children: copied ? "Copied!" : "Copy"
|
|
557
|
+
}
|
|
558
|
+
) })
|
|
559
|
+
] })
|
|
560
|
+
}
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
function VersionSection({ title, version }) {
|
|
564
|
+
return /* @__PURE__ */ jsxs5("div", { className: "cfi-sh-about-section", children: [
|
|
565
|
+
title && /* @__PURE__ */ jsx5("div", { className: "cfi-sh-about-section-title", children: title }),
|
|
566
|
+
/* @__PURE__ */ jsxs5("dl", { className: "cfi-sh-about-list", children: [
|
|
567
|
+
/* @__PURE__ */ jsx5("dt", { children: "Revision" }),
|
|
568
|
+
/* @__PURE__ */ jsxs5("dd", { children: [
|
|
569
|
+
/* @__PURE__ */ jsx5("code", { children: version.revisionId }),
|
|
570
|
+
/* @__PURE__ */ jsxs5("span", { className: "cfi-sh-about-date", children: [
|
|
571
|
+
" (",
|
|
572
|
+
formatDate(version.revisionDate),
|
|
573
|
+
")"
|
|
574
|
+
] })
|
|
575
|
+
] }),
|
|
576
|
+
version.rolloutDate && /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
577
|
+
/* @__PURE__ */ jsx5("dt", { children: "Deployed" }),
|
|
578
|
+
/* @__PURE__ */ jsx5("dd", { children: formatDateTime(version.rolloutDate) })
|
|
579
|
+
] })
|
|
580
|
+
] })
|
|
581
|
+
] });
|
|
582
|
+
}
|
|
583
|
+
function formatClipboardPayload(about) {
|
|
584
|
+
const header = `**${about.productTitle}** (${about.environment})`;
|
|
585
|
+
const webappLines = formatLines(about.webapp);
|
|
586
|
+
if (!about.api) {
|
|
587
|
+
return [header, ...webappLines].join("\n");
|
|
588
|
+
}
|
|
589
|
+
const apiLines = formatLines(about.api);
|
|
590
|
+
return [
|
|
591
|
+
header,
|
|
592
|
+
"",
|
|
593
|
+
"Webapp:",
|
|
594
|
+
...webappLines,
|
|
595
|
+
"",
|
|
596
|
+
"API:",
|
|
597
|
+
...apiLines
|
|
598
|
+
].join("\n");
|
|
599
|
+
}
|
|
600
|
+
function formatLines(v) {
|
|
601
|
+
const lines = [`Revision: ${v.revisionId} (${formatDate(v.revisionDate)})`];
|
|
602
|
+
if (v.rolloutDate) lines.push(`Deployed: ${formatDateTime(v.rolloutDate)}`);
|
|
603
|
+
return lines;
|
|
604
|
+
}
|
|
605
|
+
function formatDate(iso) {
|
|
606
|
+
const d = new Date(iso);
|
|
607
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
608
|
+
return d.toISOString().slice(0, 10);
|
|
609
|
+
}
|
|
610
|
+
function formatDateTime(iso) {
|
|
611
|
+
const d = new Date(iso);
|
|
612
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
613
|
+
return d.toISOString().replace("T", " ").slice(0, 19) + " UTC";
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// src/useAboutShortcut.ts
|
|
617
|
+
import { useEffect as useEffect5, useState as useState5 } from "react";
|
|
618
|
+
function useAboutShortcut(enabled) {
|
|
619
|
+
const [isOpen, setIsOpen] = useState5(false);
|
|
620
|
+
useEffect5(() => {
|
|
621
|
+
if (!enabled) return;
|
|
622
|
+
function handleKeyDown(e) {
|
|
623
|
+
const platformMod = e.ctrlKey || e.metaKey;
|
|
624
|
+
if (!platformMod || !e.altKey || e.shiftKey) return;
|
|
625
|
+
if (e.key.toLowerCase() !== "a") return;
|
|
626
|
+
const target = e.target;
|
|
627
|
+
if (target) {
|
|
628
|
+
const tag = target.tagName;
|
|
629
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || target.isContentEditable) return;
|
|
630
|
+
}
|
|
631
|
+
e.preventDefault();
|
|
632
|
+
setIsOpen((open) => !open);
|
|
633
|
+
}
|
|
634
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
635
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
636
|
+
}, [enabled]);
|
|
637
|
+
return [isOpen, setIsOpen];
|
|
638
|
+
}
|
|
639
|
+
|
|
499
640
|
// src/ShellHeader.tsx
|
|
500
|
-
import { jsx as
|
|
641
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
501
642
|
function ShellHeader({
|
|
502
643
|
appId,
|
|
503
644
|
user,
|
|
@@ -519,61 +660,66 @@ function ShellHeader({
|
|
|
519
660
|
tenant,
|
|
520
661
|
tenants,
|
|
521
662
|
onTenantSwitch,
|
|
663
|
+
about,
|
|
522
664
|
brandWidth,
|
|
523
665
|
children
|
|
524
666
|
}) {
|
|
525
667
|
const currentApp = appCatalog.find((a) => a.id === appId);
|
|
668
|
+
const [aboutOpen, setAboutOpen] = useAboutShortcut(about !== void 0);
|
|
526
669
|
const brandStyle = brandWidth ? { width: `${brandWidth}px`, flexShrink: 0, boxSizing: "border-box" } : void 0;
|
|
527
|
-
return /* @__PURE__ */
|
|
528
|
-
/* @__PURE__ */
|
|
529
|
-
/* @__PURE__ */
|
|
530
|
-
|
|
531
|
-
|
|
670
|
+
return /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
671
|
+
/* @__PURE__ */ jsxs6("header", { className: "cfi-sh-header", children: [
|
|
672
|
+
/* @__PURE__ */ jsxs6("div", { className: "cfi-sh-left", children: [
|
|
673
|
+
/* @__PURE__ */ jsxs6("div", { className: "cfi-sh-app-brand", style: brandStyle, children: [
|
|
674
|
+
currentApp && /* @__PURE__ */ jsx6("span", { className: "cfi-sh-app-badge", style: { background: currentApp.color }, children: currentApp.letter }),
|
|
675
|
+
/* @__PURE__ */ jsx6("span", { className: "cfi-sh-app-title", children: currentApp?.name || appId })
|
|
676
|
+
] }),
|
|
677
|
+
children
|
|
532
678
|
] }),
|
|
533
|
-
children
|
|
679
|
+
/* @__PURE__ */ jsxs6("div", { className: "cfi-sh-right", children: [
|
|
680
|
+
/* @__PURE__ */ jsx6(
|
|
681
|
+
NotificationBell,
|
|
682
|
+
{
|
|
683
|
+
count: notificationCount,
|
|
684
|
+
onClick: onNotificationClick,
|
|
685
|
+
items: notifications,
|
|
686
|
+
onMarkRead,
|
|
687
|
+
onMarkAllRead,
|
|
688
|
+
onItemClick: onNotificationItemClick
|
|
689
|
+
}
|
|
690
|
+
),
|
|
691
|
+
/* @__PURE__ */ jsx6(AppSwitcher, { currentAppId: appId, authorizedApps, currentTenantRole }),
|
|
692
|
+
/* @__PURE__ */ jsx6(
|
|
693
|
+
UserMenu,
|
|
694
|
+
{
|
|
695
|
+
user,
|
|
696
|
+
onLogout,
|
|
697
|
+
color: currentApp?.color,
|
|
698
|
+
locale,
|
|
699
|
+
supportedLocales,
|
|
700
|
+
onLocaleChange,
|
|
701
|
+
currency,
|
|
702
|
+
supportedCurrencies,
|
|
703
|
+
onCurrencyChange,
|
|
704
|
+
tenant,
|
|
705
|
+
tenants,
|
|
706
|
+
onTenantSwitch
|
|
707
|
+
}
|
|
708
|
+
)
|
|
709
|
+
] })
|
|
534
710
|
] }),
|
|
535
|
-
/* @__PURE__ */
|
|
536
|
-
/* @__PURE__ */ jsx5(
|
|
537
|
-
NotificationBell,
|
|
538
|
-
{
|
|
539
|
-
count: notificationCount,
|
|
540
|
-
onClick: onNotificationClick,
|
|
541
|
-
items: notifications,
|
|
542
|
-
onMarkRead,
|
|
543
|
-
onMarkAllRead,
|
|
544
|
-
onItemClick: onNotificationItemClick
|
|
545
|
-
}
|
|
546
|
-
),
|
|
547
|
-
/* @__PURE__ */ jsx5(AppSwitcher, { currentAppId: appId, authorizedApps, currentTenantRole }),
|
|
548
|
-
/* @__PURE__ */ jsx5(
|
|
549
|
-
UserMenu,
|
|
550
|
-
{
|
|
551
|
-
user,
|
|
552
|
-
onLogout,
|
|
553
|
-
color: currentApp?.color,
|
|
554
|
-
locale,
|
|
555
|
-
supportedLocales,
|
|
556
|
-
onLocaleChange,
|
|
557
|
-
currency,
|
|
558
|
-
supportedCurrencies,
|
|
559
|
-
onCurrencyChange,
|
|
560
|
-
tenant,
|
|
561
|
-
tenants,
|
|
562
|
-
onTenantSwitch
|
|
563
|
-
}
|
|
564
|
-
)
|
|
565
|
-
] })
|
|
711
|
+
about && aboutOpen && /* @__PURE__ */ jsx6(AboutModal, { about, onClose: () => setAboutOpen(false) })
|
|
566
712
|
] });
|
|
567
713
|
}
|
|
568
714
|
|
|
569
715
|
// src/LocaleSwitcher.tsx
|
|
570
|
-
import { useState as
|
|
571
|
-
import { jsx as
|
|
716
|
+
import { useState as useState6, useRef as useRef4, useEffect as useEffect6 } from "react";
|
|
717
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
572
718
|
function LocaleSwitcher({ currentLocale, supportedLocales, onLocaleChange }) {
|
|
573
|
-
const [open, setOpen] =
|
|
574
|
-
const [updating, setUpdating] =
|
|
719
|
+
const [open, setOpen] = useState6(false);
|
|
720
|
+
const [updating, setUpdating] = useState6(false);
|
|
575
721
|
const ref = useRef4(null);
|
|
576
|
-
|
|
722
|
+
useEffect6(() => {
|
|
577
723
|
function handleClickOutside(e) {
|
|
578
724
|
if (ref.current && !ref.current.contains(e.target)) {
|
|
579
725
|
setOpen(false);
|
|
@@ -597,8 +743,8 @@ function LocaleSwitcher({ currentLocale, supportedLocales, onLocaleChange }) {
|
|
|
597
743
|
setOpen(false);
|
|
598
744
|
}
|
|
599
745
|
}
|
|
600
|
-
return /* @__PURE__ */
|
|
601
|
-
/* @__PURE__ */
|
|
746
|
+
return /* @__PURE__ */ jsxs7("div", { className: "cfi-sh-locale-switcher", ref, children: [
|
|
747
|
+
/* @__PURE__ */ jsxs7(
|
|
602
748
|
"button",
|
|
603
749
|
{
|
|
604
750
|
className: "cfi-sh-locale-btn",
|
|
@@ -609,20 +755,20 @@ function LocaleSwitcher({ currentLocale, supportedLocales, onLocaleChange }) {
|
|
|
609
755
|
disabled: updating,
|
|
610
756
|
title: current?.label ?? currentLocale,
|
|
611
757
|
children: [
|
|
612
|
-
/* @__PURE__ */
|
|
613
|
-
/* @__PURE__ */
|
|
758
|
+
/* @__PURE__ */ jsx7(Globe, { size: 16 }),
|
|
759
|
+
/* @__PURE__ */ jsx7("span", { className: "cfi-sh-locale-label", children: buttonLabel })
|
|
614
760
|
]
|
|
615
761
|
}
|
|
616
762
|
),
|
|
617
|
-
open && /* @__PURE__ */
|
|
763
|
+
open && /* @__PURE__ */ jsx7("div", { className: "cfi-sh-locale-dropdown", children: supportedLocales.map((loc) => /* @__PURE__ */ jsxs7(
|
|
618
764
|
"button",
|
|
619
765
|
{
|
|
620
766
|
className: loc.code === currentLocale ? "cfi-sh-menu-item cfi-sh-locale-item-active" : "cfi-sh-menu-item",
|
|
621
767
|
onClick: () => handleSelect(loc.code),
|
|
622
768
|
disabled: updating,
|
|
623
769
|
children: [
|
|
624
|
-
/* @__PURE__ */
|
|
625
|
-
/* @__PURE__ */
|
|
770
|
+
/* @__PURE__ */ jsx7("span", { className: "cfi-sh-locale-item-label", children: loc.label }),
|
|
771
|
+
/* @__PURE__ */ jsx7("span", { className: "cfi-sh-locale-item-code", children: loc.code.toUpperCase() })
|
|
626
772
|
]
|
|
627
773
|
},
|
|
628
774
|
loc.code
|