@umituz/web-dashboard 4.6.4 → 4.7.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/web-dashboard",
3
- "version": "4.6.4",
3
+ "version": "4.7.0",
4
4
  "description": "Installable app skeleton for the fleet — layouts, auth, onboarding, analytics (dependency-free SVG charts), calendar, i18n injection, permissions/session/module registry; ships its own udb-* stylesheet and --u-* tokens",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -1,12 +1,12 @@
1
1
  import { useState } from "react";
2
2
  import {
3
3
  Bell, X, Sun, Moon, Menu, User, Settings, LogOut,
4
- ChevronDown, CreditCard
4
+ ChevronDown
5
5
  } from "lucide-react";
6
6
  import { Button } from "../../../primitives";
7
7
  import { useNavigate } from "react-router-dom";
8
8
  import { useT } from "../../../i18n";
9
- import type { DashboardHeaderProps } from "../types/layout";
9
+ import type { DashboardHeaderProps, HeaderProfileItem } from "../types/layout";
10
10
  import type { DashboardNotification } from "../types/notification";
11
11
  import type { DashboardUser } from "../types/user";
12
12
  import { formatNotificationTime } from "../utils/dashboard";
@@ -26,8 +26,9 @@ interface DashboardHeaderPropsExtended extends DashboardHeaderProps {
26
26
  settingsRoute?: string;
27
27
  /** Profile route */
28
28
  profileRoute?: string;
29
- /** Billing route */
30
- billingRoute?: string;
29
+ /** Host-injected profile-menu entries — billing and friends live here,
30
+ * not in the skeleton. */
31
+ extraProfileItems?: HeaderProfileItem[];
31
32
  }
32
33
 
33
34
  /**
@@ -53,7 +54,7 @@ export const DashboardHeader = ({
53
54
  onDismissNotification,
54
55
  settingsRoute = "/dashboard/settings",
55
56
  profileRoute = "/dashboard/profile",
56
- billingRoute = "/dashboard/billing",
57
+ extraProfileItems = [],
57
58
  }: DashboardHeaderPropsExtended) => {
58
59
  const navigate = useNavigate();
59
60
  const t = useT();
@@ -200,14 +201,21 @@ export const DashboardHeader = ({
200
201
  <User className="udb-menu__item-icon" />
201
202
  {t('common.profile')}
202
203
  </button>
203
- <button
204
- type="button"
205
- onClick={() => { navigate(billingRoute); setProfileOpen(false); }}
206
- className="udb-menu__item"
207
- >
208
- <CreditCard className="udb-menu__item-icon" />
209
- {t('common.billing')}
210
- </button>
204
+ {extraProfileItems.map((item) => (
205
+ <button
206
+ key={item.key}
207
+ type="button"
208
+ onClick={() => {
209
+ if (item.to) navigate(item.to);
210
+ item.onSelect?.();
211
+ setProfileOpen(false);
212
+ }}
213
+ className="udb-menu__item"
214
+ >
215
+ {item.icon}
216
+ {item.label}
217
+ </button>
218
+ ))}
211
219
  <button
212
220
  type="button"
213
221
  onClick={() => { navigate(settingsRoute); setProfileOpen(false); }}
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { renderToString } from 'react-dom/server';
3
+ import { PageHeader } from './PageHeader';
4
+
5
+ describe('PageHeader markup smoke', () => {
6
+ it('renders kicker before title and omits it when undefined', () => {
7
+ const html = renderToString(
8
+ <PageHeader kicker="Customer area" title="Policies" subtitle="Your coverage at a glance" />,
9
+ );
10
+ const kickerAt = html.indexOf('udb-pageheader__kicker');
11
+ const titleAt = html.indexOf('udb-pageheader__title');
12
+ expect(kickerAt).toBeGreaterThan(-1);
13
+ expect(kickerAt).toBeLessThan(titleAt);
14
+ expect(html).toContain('Customer area');
15
+
16
+ const bare = renderToString(<PageHeader title="Policies" />);
17
+ expect(bare).not.toContain('udb-pageheader__kicker');
18
+ });
19
+
20
+ it('renders actions and breadcrumb nodes in their slots', () => {
21
+ const html = renderToString(
22
+ <PageHeader
23
+ title="Claims"
24
+ breadcrumbs={<a href="/policies">Policies</a>}
25
+ actions={<button type="button">New claim</button>}
26
+ />,
27
+ );
28
+ expect(html).toContain('udb-pageheader__breadcrumbs');
29
+ expect(html).toContain('udb-pageheader__actions');
30
+ expect(html).toContain('New claim');
31
+ });
32
+ });
@@ -6,6 +6,7 @@ export interface PageHeaderCrumb {
6
6
  }
7
7
 
8
8
  export interface PageHeaderProps {
9
+ kicker?: ReactNode;
9
10
  title?: ReactNode;
10
11
  subtitle?: ReactNode;
11
12
  description?: ReactNode;
@@ -17,7 +18,7 @@ export interface PageHeaderProps {
17
18
  const isCrumbList = (value: ReactNode | readonly PageHeaderCrumb[]): value is readonly PageHeaderCrumb[] =>
18
19
  Array.isArray(value) && value.every((item) => typeof item === "object" && item !== null && "label" in item);
19
20
 
20
- export const PageHeader = ({ title, subtitle, description, breadcrumbs, actions, className }: PageHeaderProps) => {
21
+ export const PageHeader = ({ kicker, title, subtitle, description, breadcrumbs, actions, className }: PageHeaderProps) => {
21
22
  const crumbNode =
22
23
  breadcrumbs === undefined
23
24
  ? undefined
@@ -32,6 +33,7 @@ export const PageHeader = ({ title, subtitle, description, breadcrumbs, actions,
32
33
  <div className={`udb-pageheader${className ? ` ${className}` : ""}`}>
33
34
  {crumbNode !== undefined && <div className="udb-pageheader__breadcrumbs">{crumbNode}</div>}
34
35
  <div className="udb-pageheader__main">
36
+ {kicker !== undefined && <p className="udb-pageheader__kicker">{kicker}</p>}
35
37
  {title !== undefined && <h1 className="udb-pageheader__title">{title}</h1>}
36
38
  {(subtitle ?? description) !== undefined && (
37
39
  <p className="udb-pageheader__subtitle">{subtitle ?? description}</p>
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  export type { SidebarItem, SidebarGroup } from './sidebar';
6
- export type { DashboardHeaderProps, DashboardSidebarProps, DashboardLayoutConfig } from './layout';
6
+ export type { DashboardHeaderProps, DashboardSidebarProps, DashboardLayoutConfig, HeaderProfileItem } from './layout';
7
7
  export type { DashboardTheme, DashboardThemePreset } from './theme';
8
8
  export type { DashboardNotification } from './notification';
9
9
  export type { DashboardUser, UserNavMenuItem } from './user';
@@ -22,6 +22,24 @@ export interface DashboardHeaderProps {
22
22
  onToggleTheme?: () => void;
23
23
  }
24
24
 
25
+ /**
26
+ * Host-injected profile-menu entry — the extension seam for pages the
27
+ * skeleton does not own (billing, referrals, anything). Rendered above
28
+ * Settings; `to` navigates, `onSelect` runs, both close the menu.
29
+ */
30
+ export interface HeaderProfileItem {
31
+ /** Stable key for list rendering */
32
+ key: string;
33
+ /** Display label (already translated by the host) */
34
+ label: string;
35
+ /** Optional icon node — bring your own sizing (`udb-menu__item-icon`) */
36
+ icon?: React.ReactNode;
37
+ /** Route to navigate to */
38
+ to?: string;
39
+ /** Click handler (runs after navigation when both are set) */
40
+ onSelect?: () => void;
41
+ }
42
+
25
43
  /**
26
44
  * Dashboard sidebar props
27
45
  */
package/src/index.ts CHANGED
@@ -11,18 +11,6 @@
11
11
  export * from './domains/layouts';
12
12
  export * from './domains/settings';
13
13
  export * from './domains/calendar';
14
- export {
15
- OnboardingWizard,
16
- useOnboarding,
17
- useOnboardingStep,
18
- } from './domains/onboarding';
19
-
20
- export type {
21
- OnboardingStep,
22
- OnboardingState,
23
- OnboardingConfig,
24
- } from './domains/onboarding';
25
-
26
14
  export {
27
15
  AuthLayout,
28
16
  LoginForm,