@almadar/ui 4.22.3 → 4.23.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.
@@ -21617,6 +21617,7 @@ var init_DashboardLayout = __esm({
21617
21617
  init_Typography();
21618
21618
  init_Icon();
21619
21619
  init_useAuthContext();
21620
+ init_useEventBus();
21620
21621
  init_useTranslate();
21621
21622
  DashboardLayout = ({
21622
21623
  appName = "{{APP_TITLE}}",
@@ -21624,11 +21625,29 @@ var init_DashboardLayout = __esm({
21624
21625
  navItems = [],
21625
21626
  user: userProp,
21626
21627
  headerActions,
21627
- showSearch = true,
21628
+ showSearch = false,
21629
+ searchEvent,
21630
+ onSearchSubmit,
21631
+ notifications,
21632
+ notificationClickEvent,
21633
+ onNotificationClick,
21634
+ showThemeToggle = true,
21628
21635
  sidebarFooter,
21629
21636
  onSignOut: onSignOutProp,
21630
21637
  children
21631
21638
  }) => {
21639
+ const eventBus = useEventBus();
21640
+ const searchEnabled = showSearch || Boolean(searchEvent) || Boolean(onSearchSubmit);
21641
+ const notificationsEnabled = Array.isArray(notifications);
21642
+ const unreadCount = notificationsEnabled ? notifications.filter((n) => n.read !== true).length : 0;
21643
+ const handleSearchSubmit = (value) => {
21644
+ if (searchEvent) eventBus.emit(`UI:${searchEvent}`, { value });
21645
+ if (onSearchSubmit) onSearchSubmit(value);
21646
+ };
21647
+ const handleNotificationClick = () => {
21648
+ if (notificationClickEvent) eventBus.emit(`UI:${notificationClickEvent}`, {});
21649
+ if (onNotificationClick) onNotificationClick();
21650
+ };
21632
21651
  const [sidebarOpen, setSidebarOpen] = React127.useState(false);
21633
21652
  const [userMenuOpen, setUserMenuOpen] = React127.useState(false);
21634
21653
  const location = reactRouterDom.useLocation();
@@ -21709,17 +21728,7 @@ var init_DashboardLayout = __esm({
21709
21728
  ))
21710
21729
  }
21711
21730
  ),
21712
- sidebarFooter || /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(
21713
- reactRouterDom.Link,
21714
- {
21715
- to: "/settings",
21716
- className: "flex items-center gap-3 px-3 py-2 text-sm text-muted-foreground dark:text-muted-foreground rounded-lg hover:bg-muted dark:hover:bg-muted",
21717
- children: [
21718
- /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Settings, { className: "h-5 w-5" }),
21719
- t("common.settings")
21720
- ]
21721
- }
21722
- ) })
21731
+ sidebarFooter && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: sidebarFooter })
21723
21732
  ]
21724
21733
  }
21725
21734
  ),
@@ -21746,32 +21755,40 @@ var init_DashboardLayout = __esm({
21746
21755
  children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Menu, { className: "h-5 w-5" })
21747
21756
  }
21748
21757
  ),
21749
- showSearch && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
21758
+ searchEnabled && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
21750
21759
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground dark:text-muted-foreground" }),
21751
21760
  /* @__PURE__ */ jsxRuntime.jsx(
21752
21761
  Input,
21753
21762
  {
21754
21763
  type: "search",
21755
21764
  placeholder: t("common.search"),
21756
- className: "pl-10 w-full"
21765
+ className: "pl-10 w-full",
21766
+ onKeyDown: (e) => {
21767
+ if (e.key === "Enter") {
21768
+ handleSearchSubmit(e.target.value);
21769
+ }
21770
+ }
21757
21771
  }
21758
21772
  )
21759
21773
  ] }) }),
21760
21774
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "center", gap: "xs", children: [
21761
21775
  headerActions,
21762
- /* @__PURE__ */ jsxRuntime.jsx(ThemeToggle, {}),
21763
- /* @__PURE__ */ jsxRuntime.jsxs(
21776
+ showThemeToggle && /* @__PURE__ */ jsxRuntime.jsx(ThemeToggle, {}),
21777
+ notificationsEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
21764
21778
  Button,
21765
21779
  {
21766
21780
  variant: "ghost",
21767
21781
  className: "relative p-2 rounded-full hover:bg-muted dark:hover:bg-muted",
21782
+ onClick: handleNotificationClick,
21783
+ "aria-label": t("common.notifications"),
21768
21784
  children: [
21769
21785
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Bell, { className: "h-5 w-5 text-muted-foreground dark:text-muted-foreground" }),
21770
- /* @__PURE__ */ jsxRuntime.jsx(
21786
+ unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(
21771
21787
  Box,
21772
21788
  {
21773
21789
  as: "span",
21774
- className: "absolute top-1 right-1 w-2 h-2 bg-error rounded-full"
21790
+ className: "absolute -top-0.5 -right-0.5 min-w-[18px] h-[18px] px-1 bg-error rounded-full text-[10px] font-semibold text-white flex items-center justify-center",
21791
+ children: unreadCount > 99 ? "99+" : unreadCount
21775
21792
  }
21776
21793
  )
21777
21794
  ]
@@ -21836,17 +21853,6 @@ var init_DashboardLayout = __esm({
21836
21853
  }
21837
21854
  )
21838
21855
  ] }),
21839
- /* @__PURE__ */ jsxRuntime.jsxs(
21840
- reactRouterDom.Link,
21841
- {
21842
- to: "/settings",
21843
- className: "flex items-center gap-2 px-4 py-2 text-sm text-foreground dark:text-foreground hover:bg-muted dark:hover:bg-muted",
21844
- children: [
21845
- /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Settings, { className: "h-4 w-4" }),
21846
- t("common.settings")
21847
- ]
21848
- }
21849
- ),
21850
21856
  /* @__PURE__ */ jsxRuntime.jsxs(
21851
21857
  Button,
21852
21858
  {
@@ -21871,7 +21877,7 @@ var init_DashboardLayout = __esm({
21871
21877
  )
21872
21878
  }
21873
21879
  ),
21874
- /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "main", className: "p-4 sm:p-6 pb-20 sm:pb-6", children })
21880
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "main", className: "p-4 sm:p-6", children })
21875
21881
  ] })
21876
21882
  ] });
21877
21883
  };
package/dist/avl/index.js CHANGED
@@ -3,7 +3,7 @@ import { Html, RoundedBox, OrbitControls as OrbitControls$1, Grid as Grid$1, Sta
3
3
  import * as React127 from 'react';
4
4
  import React127__default, { createContext, useContext, useRef, useState, useCallback, useMemo, useEffect, Suspense, useLayoutEffect, useReducer, lazy, useId, forwardRef, useImperativeHandle, Component } from 'react';
5
5
  import * as LucideIcons from 'lucide-react';
6
- import { Loader2, ChevronDown, X, Check, Copy, AlertTriangle, Info, AlertCircle, CheckCircle, List, Printer, ChevronRight, ChevronLeft, Code, FileText, WrapText, Trash2, Settings, Menu as Menu$1, Search, Bell, LogOut, ZoomOut, ZoomIn, Download, FileQuestion, Inbox, XCircle, Filter, Plus, Pause, Play, RotateCcw, Package, Calendar, Pencil, Eye, MoreHorizontal, Image as Image$1, Upload, Minus, ArrowLeft, HelpCircle, ChevronUp, Eraser, Star, TrendingUp, TrendingDown, ArrowUp, ArrowDown, MoreVertical, Sun, Moon, Circle, Clock, CheckCircle2, ArrowRight, FileWarning, SkipForward, Bug, Send, Wrench, User, Tag, DollarSign, Zap, Sword, Move, Heart, Shield } from 'lucide-react';
6
+ import { Loader2, ChevronDown, X, Check, Copy, AlertTriangle, Info, AlertCircle, CheckCircle, List, Printer, ChevronRight, ChevronLeft, Code, FileText, WrapText, Trash2, Menu as Menu$1, Search, Bell, LogOut, ZoomOut, ZoomIn, Download, FileQuestion, Inbox, XCircle, Filter, Plus, Pause, Play, RotateCcw, Package, Calendar, Pencil, Eye, MoreHorizontal, Image as Image$1, Upload, Minus, ArrowLeft, HelpCircle, ChevronUp, Eraser, Star, TrendingUp, TrendingDown, ArrowUp, ArrowDown, MoreVertical, Sun, Moon, Circle, Clock, CheckCircle2, ArrowRight, FileWarning, SkipForward, Bug, Send, Wrench, User, Tag, DollarSign, Zap, Sword, Move, Heart, Shield } from 'lucide-react';
7
7
  import { evaluate, createMinimalContext } from '@almadar/evaluator';
8
8
  import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1, isEntityAwarePattern } from '@almadar/patterns';
9
9
  import { createPortal } from 'react-dom';
@@ -21571,6 +21571,7 @@ var init_DashboardLayout = __esm({
21571
21571
  init_Typography();
21572
21572
  init_Icon();
21573
21573
  init_useAuthContext();
21574
+ init_useEventBus();
21574
21575
  init_useTranslate();
21575
21576
  DashboardLayout = ({
21576
21577
  appName = "{{APP_TITLE}}",
@@ -21578,11 +21579,29 @@ var init_DashboardLayout = __esm({
21578
21579
  navItems = [],
21579
21580
  user: userProp,
21580
21581
  headerActions,
21581
- showSearch = true,
21582
+ showSearch = false,
21583
+ searchEvent,
21584
+ onSearchSubmit,
21585
+ notifications,
21586
+ notificationClickEvent,
21587
+ onNotificationClick,
21588
+ showThemeToggle = true,
21582
21589
  sidebarFooter,
21583
21590
  onSignOut: onSignOutProp,
21584
21591
  children
21585
21592
  }) => {
21593
+ const eventBus = useEventBus();
21594
+ const searchEnabled = showSearch || Boolean(searchEvent) || Boolean(onSearchSubmit);
21595
+ const notificationsEnabled = Array.isArray(notifications);
21596
+ const unreadCount = notificationsEnabled ? notifications.filter((n) => n.read !== true).length : 0;
21597
+ const handleSearchSubmit = (value) => {
21598
+ if (searchEvent) eventBus.emit(`UI:${searchEvent}`, { value });
21599
+ if (onSearchSubmit) onSearchSubmit(value);
21600
+ };
21601
+ const handleNotificationClick = () => {
21602
+ if (notificationClickEvent) eventBus.emit(`UI:${notificationClickEvent}`, {});
21603
+ if (onNotificationClick) onNotificationClick();
21604
+ };
21586
21605
  const [sidebarOpen, setSidebarOpen] = useState(false);
21587
21606
  const [userMenuOpen, setUserMenuOpen] = useState(false);
21588
21607
  const location = useLocation();
@@ -21663,17 +21682,7 @@ var init_DashboardLayout = __esm({
21663
21682
  ))
21664
21683
  }
21665
21684
  ),
21666
- sidebarFooter || /* @__PURE__ */ jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: /* @__PURE__ */ jsxs(
21667
- Link,
21668
- {
21669
- to: "/settings",
21670
- className: "flex items-center gap-3 px-3 py-2 text-sm text-muted-foreground dark:text-muted-foreground rounded-lg hover:bg-muted dark:hover:bg-muted",
21671
- children: [
21672
- /* @__PURE__ */ jsx(Settings, { className: "h-5 w-5" }),
21673
- t("common.settings")
21674
- ]
21675
- }
21676
- ) })
21685
+ sidebarFooter && /* @__PURE__ */ jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: sidebarFooter })
21677
21686
  ]
21678
21687
  }
21679
21688
  ),
@@ -21700,32 +21709,40 @@ var init_DashboardLayout = __esm({
21700
21709
  children: /* @__PURE__ */ jsx(Menu$1, { className: "h-5 w-5" })
21701
21710
  }
21702
21711
  ),
21703
- showSearch && /* @__PURE__ */ jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
21712
+ searchEnabled && /* @__PURE__ */ jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
21704
21713
  /* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground dark:text-muted-foreground" }),
21705
21714
  /* @__PURE__ */ jsx(
21706
21715
  Input,
21707
21716
  {
21708
21717
  type: "search",
21709
21718
  placeholder: t("common.search"),
21710
- className: "pl-10 w-full"
21719
+ className: "pl-10 w-full",
21720
+ onKeyDown: (e) => {
21721
+ if (e.key === "Enter") {
21722
+ handleSearchSubmit(e.target.value);
21723
+ }
21724
+ }
21711
21725
  }
21712
21726
  )
21713
21727
  ] }) }),
21714
21728
  /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "xs", children: [
21715
21729
  headerActions,
21716
- /* @__PURE__ */ jsx(ThemeToggle, {}),
21717
- /* @__PURE__ */ jsxs(
21730
+ showThemeToggle && /* @__PURE__ */ jsx(ThemeToggle, {}),
21731
+ notificationsEnabled && /* @__PURE__ */ jsxs(
21718
21732
  Button,
21719
21733
  {
21720
21734
  variant: "ghost",
21721
21735
  className: "relative p-2 rounded-full hover:bg-muted dark:hover:bg-muted",
21736
+ onClick: handleNotificationClick,
21737
+ "aria-label": t("common.notifications"),
21722
21738
  children: [
21723
21739
  /* @__PURE__ */ jsx(Bell, { className: "h-5 w-5 text-muted-foreground dark:text-muted-foreground" }),
21724
- /* @__PURE__ */ jsx(
21740
+ unreadCount > 0 && /* @__PURE__ */ jsx(
21725
21741
  Box,
21726
21742
  {
21727
21743
  as: "span",
21728
- className: "absolute top-1 right-1 w-2 h-2 bg-error rounded-full"
21744
+ className: "absolute -top-0.5 -right-0.5 min-w-[18px] h-[18px] px-1 bg-error rounded-full text-[10px] font-semibold text-white flex items-center justify-center",
21745
+ children: unreadCount > 99 ? "99+" : unreadCount
21729
21746
  }
21730
21747
  )
21731
21748
  ]
@@ -21790,17 +21807,6 @@ var init_DashboardLayout = __esm({
21790
21807
  }
21791
21808
  )
21792
21809
  ] }),
21793
- /* @__PURE__ */ jsxs(
21794
- Link,
21795
- {
21796
- to: "/settings",
21797
- className: "flex items-center gap-2 px-4 py-2 text-sm text-foreground dark:text-foreground hover:bg-muted dark:hover:bg-muted",
21798
- children: [
21799
- /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" }),
21800
- t("common.settings")
21801
- ]
21802
- }
21803
- ),
21804
21810
  /* @__PURE__ */ jsxs(
21805
21811
  Button,
21806
21812
  {
@@ -21825,7 +21831,7 @@ var init_DashboardLayout = __esm({
21825
21831
  )
21826
21832
  }
21827
21833
  ),
21828
- /* @__PURE__ */ jsx(Box, { as: "main", className: "p-4 sm:p-6 pb-20 sm:pb-6", children })
21834
+ /* @__PURE__ */ jsx(Box, { as: "main", className: "p-4 sm:p-6", children })
21829
21835
  ] })
21830
21836
  ] });
21831
21837
  };
@@ -17041,6 +17041,7 @@ var init_DashboardLayout = __esm({
17041
17041
  init_Typography();
17042
17042
  init_Icon();
17043
17043
  init_useAuthContext();
17044
+ init_useEventBus();
17044
17045
  init_useTranslate();
17045
17046
  exports.DashboardLayout = ({
17046
17047
  appName = "{{APP_TITLE}}",
@@ -17048,11 +17049,29 @@ var init_DashboardLayout = __esm({
17048
17049
  navItems = [],
17049
17050
  user: userProp,
17050
17051
  headerActions,
17051
- showSearch = true,
17052
+ showSearch = false,
17053
+ searchEvent,
17054
+ onSearchSubmit,
17055
+ notifications,
17056
+ notificationClickEvent,
17057
+ onNotificationClick,
17058
+ showThemeToggle = true,
17052
17059
  sidebarFooter,
17053
17060
  onSignOut: onSignOutProp,
17054
17061
  children
17055
17062
  }) => {
17063
+ const eventBus = useEventBus();
17064
+ const searchEnabled = showSearch || Boolean(searchEvent) || Boolean(onSearchSubmit);
17065
+ const notificationsEnabled = Array.isArray(notifications);
17066
+ const unreadCount = notificationsEnabled ? notifications.filter((n) => n.read !== true).length : 0;
17067
+ const handleSearchSubmit = (value) => {
17068
+ if (searchEvent) eventBus.emit(`UI:${searchEvent}`, { value });
17069
+ if (onSearchSubmit) onSearchSubmit(value);
17070
+ };
17071
+ const handleNotificationClick = () => {
17072
+ if (notificationClickEvent) eventBus.emit(`UI:${notificationClickEvent}`, {});
17073
+ if (onNotificationClick) onNotificationClick();
17074
+ };
17056
17075
  const [sidebarOpen, setSidebarOpen] = React110.useState(false);
17057
17076
  const [userMenuOpen, setUserMenuOpen] = React110.useState(false);
17058
17077
  const location = reactRouterDom.useLocation();
@@ -17137,17 +17156,7 @@ var init_DashboardLayout = __esm({
17137
17156
  ))
17138
17157
  }
17139
17158
  ),
17140
- sidebarFooter || /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 border-t border-border dark:border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(
17141
- reactRouterDom.Link,
17142
- {
17143
- to: "/settings",
17144
- className: "flex items-center gap-3 px-3 py-2 text-sm text-muted-foreground dark:text-muted-foreground rounded-lg hover:bg-muted dark:hover:bg-muted",
17145
- children: [
17146
- /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Settings, { className: "h-5 w-5" }),
17147
- t("common.settings")
17148
- ]
17149
- }
17150
- ) })
17159
+ sidebarFooter && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 border-t border-border dark:border-border", children: sidebarFooter })
17151
17160
  ]
17152
17161
  }
17153
17162
  ),
@@ -17174,32 +17183,40 @@ var init_DashboardLayout = __esm({
17174
17183
  children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Menu, { className: "h-5 w-5" })
17175
17184
  }
17176
17185
  ),
17177
- showSearch && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "relative", children: [
17186
+ searchEnabled && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "relative", children: [
17178
17187
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground dark:text-muted-foreground" }),
17179
17188
  /* @__PURE__ */ jsxRuntime.jsx(
17180
17189
  exports.Input,
17181
17190
  {
17182
17191
  type: "search",
17183
17192
  placeholder: t("common.search"),
17184
- className: "pl-10 w-full"
17193
+ className: "pl-10 w-full",
17194
+ onKeyDown: (e) => {
17195
+ if (e.key === "Enter") {
17196
+ handleSearchSubmit(e.target.value);
17197
+ }
17198
+ }
17185
17199
  }
17186
17200
  )
17187
17201
  ] }) }),
17188
17202
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "center", gap: "xs", children: [
17189
17203
  headerActions,
17190
- /* @__PURE__ */ jsxRuntime.jsx(exports.ThemeToggle, {}),
17191
- /* @__PURE__ */ jsxRuntime.jsxs(
17204
+ showThemeToggle && /* @__PURE__ */ jsxRuntime.jsx(exports.ThemeToggle, {}),
17205
+ notificationsEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
17192
17206
  exports.Button,
17193
17207
  {
17194
17208
  variant: "ghost",
17195
17209
  className: "relative p-2 rounded-full hover:bg-muted dark:hover:bg-muted",
17210
+ onClick: handleNotificationClick,
17211
+ "aria-label": t("common.notifications"),
17196
17212
  children: [
17197
17213
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Bell, { className: "h-5 w-5 text-muted-foreground dark:text-muted-foreground" }),
17198
- /* @__PURE__ */ jsxRuntime.jsx(
17214
+ unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(
17199
17215
  exports.Box,
17200
17216
  {
17201
17217
  as: "span",
17202
- className: "absolute top-1 right-1 w-2 h-2 bg-error rounded-full"
17218
+ className: "absolute -top-0.5 -right-0.5 min-w-[18px] h-[18px] px-1 bg-error rounded-full text-[10px] font-semibold text-white flex items-center justify-center",
17219
+ children: unreadCount > 99 ? "99+" : unreadCount
17203
17220
  }
17204
17221
  )
17205
17222
  ]
@@ -17264,17 +17281,6 @@ var init_DashboardLayout = __esm({
17264
17281
  }
17265
17282
  )
17266
17283
  ] }),
17267
- /* @__PURE__ */ jsxRuntime.jsxs(
17268
- reactRouterDom.Link,
17269
- {
17270
- to: "/settings",
17271
- className: "flex items-center gap-2 px-4 py-2 text-sm text-foreground dark:text-foreground hover:bg-muted dark:hover:bg-muted",
17272
- children: [
17273
- /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Settings, { className: "h-4 w-4" }),
17274
- t("common.settings")
17275
- ]
17276
- }
17277
- ),
17278
17284
  /* @__PURE__ */ jsxRuntime.jsxs(
17279
17285
  exports.Button,
17280
17286
  {
@@ -17299,7 +17305,7 @@ var init_DashboardLayout = __esm({
17299
17305
  )
17300
17306
  }
17301
17307
  ),
17302
- /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "main", className: "p-4 sm:p-6 pb-20 sm:pb-6", children })
17308
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "main", className: "p-4 sm:p-6", children })
17303
17309
  ] })
17304
17310
  ] });
17305
17311
  };
@@ -4,7 +4,7 @@ import * as React110 from 'react';
4
4
  import React110__default, { useContext, useMemo, useRef, useEffect, useCallback, createContext, useState, Suspense, lazy, useSyncExternalStore, useLayoutEffect, useId } from 'react';
5
5
  import { EventBusContext, useTraitScope, TraitScopeProvider } from '@almadar/ui/providers';
6
6
  import * as LucideIcons from 'lucide-react';
7
- import { Loader2, X, AlertTriangle, Info, AlertCircle, CheckCircle, ChevronDown, List, Printer, ChevronRight, ChevronLeft, XCircle, Wrench, RotateCcw, Send, Code, FileText, WrapText, Check, Copy, Zap, Sword, Move, Heart, Shield, Trash2, Settings, Menu as Menu$1, Search, Bell, LogOut, ChevronUp, MoreHorizontal, Bug, ZoomOut, ZoomIn, Download, Pause, Play, Package, Calendar, Pencil, Eye, Image as Image$1, Upload, ArrowRight, ArrowLeft, Eraser, SkipForward, TrendingUp, TrendingDown, Minus, ArrowUp, ArrowDown, MoreVertical, Circle, Clock, CheckCircle2, HelpCircle, FileQuestion, Inbox, Plus, User, Filter, Star, FileWarning, Tag, DollarSign, Sun, Moon } from 'lucide-react';
7
+ import { Loader2, X, AlertTriangle, Info, AlertCircle, CheckCircle, ChevronDown, List, Printer, ChevronRight, ChevronLeft, XCircle, Wrench, RotateCcw, Send, Code, FileText, WrapText, Check, Copy, Zap, Sword, Move, Heart, Shield, Trash2, Menu as Menu$1, Search, Bell, LogOut, ChevronUp, MoreHorizontal, Bug, ZoomOut, ZoomIn, Download, Pause, Play, Package, Calendar, Pencil, Eye, Image as Image$1, Upload, ArrowRight, ArrowLeft, Eraser, SkipForward, TrendingUp, TrendingDown, Minus, ArrowUp, ArrowDown, MoreVertical, Circle, Clock, CheckCircle2, HelpCircle, FileQuestion, Inbox, Plus, User, Filter, Star, FileWarning, Tag, DollarSign, Sun, Moon } from 'lucide-react';
8
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
9
  import { evaluate, createMinimalContext } from '@almadar/evaluator';
10
10
  import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
@@ -16996,6 +16996,7 @@ var init_DashboardLayout = __esm({
16996
16996
  init_Typography();
16997
16997
  init_Icon();
16998
16998
  init_useAuthContext();
16999
+ init_useEventBus();
16999
17000
  init_useTranslate();
17000
17001
  DashboardLayout = ({
17001
17002
  appName = "{{APP_TITLE}}",
@@ -17003,11 +17004,29 @@ var init_DashboardLayout = __esm({
17003
17004
  navItems = [],
17004
17005
  user: userProp,
17005
17006
  headerActions,
17006
- showSearch = true,
17007
+ showSearch = false,
17008
+ searchEvent,
17009
+ onSearchSubmit,
17010
+ notifications,
17011
+ notificationClickEvent,
17012
+ onNotificationClick,
17013
+ showThemeToggle = true,
17007
17014
  sidebarFooter,
17008
17015
  onSignOut: onSignOutProp,
17009
17016
  children
17010
17017
  }) => {
17018
+ const eventBus = useEventBus();
17019
+ const searchEnabled = showSearch || Boolean(searchEvent) || Boolean(onSearchSubmit);
17020
+ const notificationsEnabled = Array.isArray(notifications);
17021
+ const unreadCount = notificationsEnabled ? notifications.filter((n) => n.read !== true).length : 0;
17022
+ const handleSearchSubmit = (value) => {
17023
+ if (searchEvent) eventBus.emit(`UI:${searchEvent}`, { value });
17024
+ if (onSearchSubmit) onSearchSubmit(value);
17025
+ };
17026
+ const handleNotificationClick = () => {
17027
+ if (notificationClickEvent) eventBus.emit(`UI:${notificationClickEvent}`, {});
17028
+ if (onNotificationClick) onNotificationClick();
17029
+ };
17011
17030
  const [sidebarOpen, setSidebarOpen] = useState(false);
17012
17031
  const [userMenuOpen, setUserMenuOpen] = useState(false);
17013
17032
  const location = useLocation();
@@ -17092,17 +17111,7 @@ var init_DashboardLayout = __esm({
17092
17111
  ))
17093
17112
  }
17094
17113
  ),
17095
- sidebarFooter || /* @__PURE__ */ jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: /* @__PURE__ */ jsxs(
17096
- Link,
17097
- {
17098
- to: "/settings",
17099
- className: "flex items-center gap-3 px-3 py-2 text-sm text-muted-foreground dark:text-muted-foreground rounded-lg hover:bg-muted dark:hover:bg-muted",
17100
- children: [
17101
- /* @__PURE__ */ jsx(Settings, { className: "h-5 w-5" }),
17102
- t("common.settings")
17103
- ]
17104
- }
17105
- ) })
17114
+ sidebarFooter && /* @__PURE__ */ jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: sidebarFooter })
17106
17115
  ]
17107
17116
  }
17108
17117
  ),
@@ -17129,32 +17138,40 @@ var init_DashboardLayout = __esm({
17129
17138
  children: /* @__PURE__ */ jsx(Menu$1, { className: "h-5 w-5" })
17130
17139
  }
17131
17140
  ),
17132
- showSearch && /* @__PURE__ */ jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
17141
+ searchEnabled && /* @__PURE__ */ jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
17133
17142
  /* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground dark:text-muted-foreground" }),
17134
17143
  /* @__PURE__ */ jsx(
17135
17144
  Input,
17136
17145
  {
17137
17146
  type: "search",
17138
17147
  placeholder: t("common.search"),
17139
- className: "pl-10 w-full"
17148
+ className: "pl-10 w-full",
17149
+ onKeyDown: (e) => {
17150
+ if (e.key === "Enter") {
17151
+ handleSearchSubmit(e.target.value);
17152
+ }
17153
+ }
17140
17154
  }
17141
17155
  )
17142
17156
  ] }) }),
17143
17157
  /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "xs", children: [
17144
17158
  headerActions,
17145
- /* @__PURE__ */ jsx(ThemeToggle, {}),
17146
- /* @__PURE__ */ jsxs(
17159
+ showThemeToggle && /* @__PURE__ */ jsx(ThemeToggle, {}),
17160
+ notificationsEnabled && /* @__PURE__ */ jsxs(
17147
17161
  Button,
17148
17162
  {
17149
17163
  variant: "ghost",
17150
17164
  className: "relative p-2 rounded-full hover:bg-muted dark:hover:bg-muted",
17165
+ onClick: handleNotificationClick,
17166
+ "aria-label": t("common.notifications"),
17151
17167
  children: [
17152
17168
  /* @__PURE__ */ jsx(Bell, { className: "h-5 w-5 text-muted-foreground dark:text-muted-foreground" }),
17153
- /* @__PURE__ */ jsx(
17169
+ unreadCount > 0 && /* @__PURE__ */ jsx(
17154
17170
  Box,
17155
17171
  {
17156
17172
  as: "span",
17157
- className: "absolute top-1 right-1 w-2 h-2 bg-error rounded-full"
17173
+ className: "absolute -top-0.5 -right-0.5 min-w-[18px] h-[18px] px-1 bg-error rounded-full text-[10px] font-semibold text-white flex items-center justify-center",
17174
+ children: unreadCount > 99 ? "99+" : unreadCount
17158
17175
  }
17159
17176
  )
17160
17177
  ]
@@ -17219,17 +17236,6 @@ var init_DashboardLayout = __esm({
17219
17236
  }
17220
17237
  )
17221
17238
  ] }),
17222
- /* @__PURE__ */ jsxs(
17223
- Link,
17224
- {
17225
- to: "/settings",
17226
- className: "flex items-center gap-2 px-4 py-2 text-sm text-foreground dark:text-foreground hover:bg-muted dark:hover:bg-muted",
17227
- children: [
17228
- /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" }),
17229
- t("common.settings")
17230
- ]
17231
- }
17232
- ),
17233
17239
  /* @__PURE__ */ jsxs(
17234
17240
  Button,
17235
17241
  {
@@ -17254,7 +17260,7 @@ var init_DashboardLayout = __esm({
17254
17260
  )
17255
17261
  }
17256
17262
  ),
17257
- /* @__PURE__ */ jsx(Box, { as: "main", className: "p-4 sm:p-6 pb-20 sm:pb-6", children })
17263
+ /* @__PURE__ */ jsx(Box, { as: "main", className: "p-4 sm:p-6", children })
17258
17264
  ] })
17259
17265
  ] });
17260
17266
  };
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { LucideIcon } from "lucide-react";
3
+ import type { EventEmit } from "@almadar/core";
3
4
  export interface NavItem {
4
5
  label: string;
5
6
  href: string;
@@ -8,12 +9,24 @@ export interface NavItem {
8
9
  badge?: string | number;
9
10
  children?: NavItem[];
10
11
  }
12
+ export interface NotificationItem {
13
+ id: string;
14
+ /** Short label shown in the dropdown row. */
15
+ message: string;
16
+ /** Optional secondary text. */
17
+ description?: string;
18
+ /** Optional ISO timestamp string. */
19
+ createdAt?: string;
20
+ /** Optional flag — bell badge counts items where read !== true. */
21
+ read?: boolean;
22
+ }
11
23
  export interface DashboardLayoutProps {
12
24
  /** App name shown in sidebar */
13
25
  appName?: string;
14
26
  /** Logo component or URL */
15
27
  logo?: React.ReactNode;
16
- /** Navigation items */
28
+ /** Navigation items. Apps that need a Settings page should add it
29
+ * as a navItems entry, not depend on baked-in chrome. */
17
30
  navItems?: NavItem[];
18
31
  /** Current user info (optional - auto-populated from auth context if not provided) */
19
32
  user?: {
@@ -21,11 +34,34 @@ export interface DashboardLayoutProps {
21
34
  email: string;
22
35
  avatar?: string;
23
36
  };
24
- /** Header actions (notifications, etc.) */
37
+ /** Header actions (extra slots beyond bell/search/theme). */
25
38
  headerActions?: React.ReactNode;
26
- /** Show search in header */
39
+ /** Show the top-bar search box. Default `false` — opt in by setting
40
+ * `searchEvent` (any truthy value implies showSearch) or this flag. */
27
41
  showSearch?: boolean;
28
- /** Custom sidebar footer */
42
+ /** Declarative search event — fires `UI:{searchEvent}` on the bus
43
+ * when the user submits the search box (Enter key). Setting this
44
+ * implies `showSearch=true`. Use `onSearchSubmit` for direct React
45
+ * usage instead. */
46
+ searchEvent?: EventEmit<{
47
+ value: string;
48
+ }>;
49
+ /** React-side search submit callback. Used when the host wires the
50
+ * layout directly (not via render-ui pattern resolution). */
51
+ onSearchSubmit?: (value: string) => void;
52
+ /** Notification list. Pass an empty array to show the bell with no
53
+ * badge; omit / pass null to hide the bell entirely. */
54
+ notifications?: NotificationItem[] | null;
55
+ /** Declarative bell click event — fires `UI:{notificationClickEvent}`
56
+ * on the bus with an empty payload when the user clicks the bell. */
57
+ notificationClickEvent?: EventEmit<Record<string, never>>;
58
+ /** React-side bell click callback. */
59
+ onNotificationClick?: () => void;
60
+ /** Show the theme toggle button in the header. Default `true` —
61
+ * universally useful for a11y / dark mode. */
62
+ showThemeToggle?: boolean;
63
+ /** Custom sidebar footer (optional). When omitted, the sidebar has
64
+ * no footer — apps that need Settings/etc. add them via navItems. */
29
65
  sidebarFooter?: React.ReactNode;
30
66
  /** Callback when user clicks sign out (optional - uses auth context signOut if not provided) */
31
67
  onSignOut?: () => void;
@@ -18368,6 +18368,7 @@ var init_DashboardLayout = __esm({
18368
18368
  init_Typography();
18369
18369
  init_Icon();
18370
18370
  init_useAuthContext();
18371
+ init_useEventBus();
18371
18372
  init_useTranslate();
18372
18373
  DashboardLayout = ({
18373
18374
  appName = "{{APP_TITLE}}",
@@ -18375,11 +18376,29 @@ var init_DashboardLayout = __esm({
18375
18376
  navItems = [],
18376
18377
  user: userProp,
18377
18378
  headerActions,
18378
- showSearch = true,
18379
+ showSearch = false,
18380
+ searchEvent,
18381
+ onSearchSubmit,
18382
+ notifications,
18383
+ notificationClickEvent,
18384
+ onNotificationClick,
18385
+ showThemeToggle = true,
18379
18386
  sidebarFooter,
18380
18387
  onSignOut: onSignOutProp,
18381
18388
  children
18382
18389
  }) => {
18390
+ const eventBus = useEventBus();
18391
+ const searchEnabled = showSearch || Boolean(searchEvent) || Boolean(onSearchSubmit);
18392
+ const notificationsEnabled = Array.isArray(notifications);
18393
+ const unreadCount = notificationsEnabled ? notifications.filter((n) => n.read !== true).length : 0;
18394
+ const handleSearchSubmit = (value) => {
18395
+ if (searchEvent) eventBus.emit(`UI:${searchEvent}`, { value });
18396
+ if (onSearchSubmit) onSearchSubmit(value);
18397
+ };
18398
+ const handleNotificationClick = () => {
18399
+ if (notificationClickEvent) eventBus.emit(`UI:${notificationClickEvent}`, {});
18400
+ if (onNotificationClick) onNotificationClick();
18401
+ };
18383
18402
  const [sidebarOpen, setSidebarOpen] = React115.useState(false);
18384
18403
  const [userMenuOpen, setUserMenuOpen] = React115.useState(false);
18385
18404
  const location = reactRouterDom.useLocation();
@@ -18460,17 +18479,7 @@ var init_DashboardLayout = __esm({
18460
18479
  ))
18461
18480
  }
18462
18481
  ),
18463
- sidebarFooter || /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(
18464
- reactRouterDom.Link,
18465
- {
18466
- to: "/settings",
18467
- className: "flex items-center gap-3 px-3 py-2 text-sm text-muted-foreground dark:text-muted-foreground rounded-lg hover:bg-muted dark:hover:bg-muted",
18468
- children: [
18469
- /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Settings, { className: "h-5 w-5" }),
18470
- t("common.settings")
18471
- ]
18472
- }
18473
- ) })
18482
+ sidebarFooter && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: sidebarFooter })
18474
18483
  ]
18475
18484
  }
18476
18485
  ),
@@ -18497,32 +18506,40 @@ var init_DashboardLayout = __esm({
18497
18506
  children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Menu, { className: "h-5 w-5" })
18498
18507
  }
18499
18508
  ),
18500
- showSearch && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
18509
+ searchEnabled && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
18501
18510
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground dark:text-muted-foreground" }),
18502
18511
  /* @__PURE__ */ jsxRuntime.jsx(
18503
18512
  Input,
18504
18513
  {
18505
18514
  type: "search",
18506
18515
  placeholder: t("common.search"),
18507
- className: "pl-10 w-full"
18516
+ className: "pl-10 w-full",
18517
+ onKeyDown: (e) => {
18518
+ if (e.key === "Enter") {
18519
+ handleSearchSubmit(e.target.value);
18520
+ }
18521
+ }
18508
18522
  }
18509
18523
  )
18510
18524
  ] }) }),
18511
18525
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "center", gap: "xs", children: [
18512
18526
  headerActions,
18513
- /* @__PURE__ */ jsxRuntime.jsx(ThemeToggle, {}),
18514
- /* @__PURE__ */ jsxRuntime.jsxs(
18527
+ showThemeToggle && /* @__PURE__ */ jsxRuntime.jsx(ThemeToggle, {}),
18528
+ notificationsEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
18515
18529
  Button,
18516
18530
  {
18517
18531
  variant: "ghost",
18518
18532
  className: "relative p-2 rounded-full hover:bg-muted dark:hover:bg-muted",
18533
+ onClick: handleNotificationClick,
18534
+ "aria-label": t("common.notifications"),
18519
18535
  children: [
18520
18536
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Bell, { className: "h-5 w-5 text-muted-foreground dark:text-muted-foreground" }),
18521
- /* @__PURE__ */ jsxRuntime.jsx(
18537
+ unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(
18522
18538
  Box,
18523
18539
  {
18524
18540
  as: "span",
18525
- className: "absolute top-1 right-1 w-2 h-2 bg-error rounded-full"
18541
+ className: "absolute -top-0.5 -right-0.5 min-w-[18px] h-[18px] px-1 bg-error rounded-full text-[10px] font-semibold text-white flex items-center justify-center",
18542
+ children: unreadCount > 99 ? "99+" : unreadCount
18526
18543
  }
18527
18544
  )
18528
18545
  ]
@@ -18587,17 +18604,6 @@ var init_DashboardLayout = __esm({
18587
18604
  }
18588
18605
  )
18589
18606
  ] }),
18590
- /* @__PURE__ */ jsxRuntime.jsxs(
18591
- reactRouterDom.Link,
18592
- {
18593
- to: "/settings",
18594
- className: "flex items-center gap-2 px-4 py-2 text-sm text-foreground dark:text-foreground hover:bg-muted dark:hover:bg-muted",
18595
- children: [
18596
- /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Settings, { className: "h-4 w-4" }),
18597
- t("common.settings")
18598
- ]
18599
- }
18600
- ),
18601
18607
  /* @__PURE__ */ jsxRuntime.jsxs(
18602
18608
  Button,
18603
18609
  {
@@ -18622,7 +18628,7 @@ var init_DashboardLayout = __esm({
18622
18628
  )
18623
18629
  }
18624
18630
  ),
18625
- /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "main", className: "p-4 sm:p-6 pb-20 sm:pb-6", children })
18631
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "main", className: "p-4 sm:p-6", children })
18626
18632
  ] })
18627
18633
  ] });
18628
18634
  };
@@ -5,7 +5,7 @@ import { EventBusContext, useTraitScope, TraitScopeProvider } from '@almadar/ui/
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import * as LucideIcons from 'lucide-react';
8
- import { X, AlertTriangle, Info, AlertCircle, CheckCircle, Loader2, List, Printer, ChevronRight, ChevronLeft, Check, Copy, Code, FileText, WrapText, Trash2, Settings, Menu as Menu$1, Search, Bell, ChevronDown, LogOut, ZoomOut, ZoomIn, Download, FileQuestion, Inbox, XCircle, Filter, Plus, Pause, Play, RotateCcw, Package, Calendar, Pencil, Eye, MoreHorizontal, Image as Image$1, Upload, Minus, ArrowLeft, HelpCircle, ChevronUp, Eraser, Star, TrendingUp, TrendingDown, ArrowUp, ArrowDown, MoreVertical, Sun, Moon, Circle, Clock, CheckCircle2, ArrowRight, FileWarning, SkipForward, Bug, Send, Wrench, User, Tag, DollarSign, Zap, Sword, Move, Heart, Shield } from 'lucide-react';
8
+ import { X, AlertTriangle, Info, AlertCircle, CheckCircle, Loader2, List, Printer, ChevronRight, ChevronLeft, Check, Copy, Code, FileText, WrapText, Trash2, Menu as Menu$1, Search, Bell, ChevronDown, LogOut, ZoomOut, ZoomIn, Download, FileQuestion, Inbox, XCircle, Filter, Plus, Pause, Play, RotateCcw, Package, Calendar, Pencil, Eye, MoreHorizontal, Image as Image$1, Upload, Minus, ArrowLeft, HelpCircle, ChevronUp, Eraser, Star, TrendingUp, TrendingDown, ArrowUp, ArrowDown, MoreVertical, Sun, Moon, Circle, Clock, CheckCircle2, ArrowRight, FileWarning, SkipForward, Bug, Send, Wrench, User, Tag, DollarSign, Zap, Sword, Move, Heart, Shield } from 'lucide-react';
9
9
  import { evaluate, createMinimalContext } from '@almadar/evaluator';
10
10
  import { useUISlots } from '@almadar/ui/context';
11
11
  import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
@@ -18323,6 +18323,7 @@ var init_DashboardLayout = __esm({
18323
18323
  init_Typography();
18324
18324
  init_Icon();
18325
18325
  init_useAuthContext();
18326
+ init_useEventBus();
18326
18327
  init_useTranslate();
18327
18328
  DashboardLayout = ({
18328
18329
  appName = "{{APP_TITLE}}",
@@ -18330,11 +18331,29 @@ var init_DashboardLayout = __esm({
18330
18331
  navItems = [],
18331
18332
  user: userProp,
18332
18333
  headerActions,
18333
- showSearch = true,
18334
+ showSearch = false,
18335
+ searchEvent,
18336
+ onSearchSubmit,
18337
+ notifications,
18338
+ notificationClickEvent,
18339
+ onNotificationClick,
18340
+ showThemeToggle = true,
18334
18341
  sidebarFooter,
18335
18342
  onSignOut: onSignOutProp,
18336
18343
  children
18337
18344
  }) => {
18345
+ const eventBus = useEventBus();
18346
+ const searchEnabled = showSearch || Boolean(searchEvent) || Boolean(onSearchSubmit);
18347
+ const notificationsEnabled = Array.isArray(notifications);
18348
+ const unreadCount = notificationsEnabled ? notifications.filter((n) => n.read !== true).length : 0;
18349
+ const handleSearchSubmit = (value) => {
18350
+ if (searchEvent) eventBus.emit(`UI:${searchEvent}`, { value });
18351
+ if (onSearchSubmit) onSearchSubmit(value);
18352
+ };
18353
+ const handleNotificationClick = () => {
18354
+ if (notificationClickEvent) eventBus.emit(`UI:${notificationClickEvent}`, {});
18355
+ if (onNotificationClick) onNotificationClick();
18356
+ };
18338
18357
  const [sidebarOpen, setSidebarOpen] = useState(false);
18339
18358
  const [userMenuOpen, setUserMenuOpen] = useState(false);
18340
18359
  const location = useLocation();
@@ -18415,17 +18434,7 @@ var init_DashboardLayout = __esm({
18415
18434
  ))
18416
18435
  }
18417
18436
  ),
18418
- sidebarFooter || /* @__PURE__ */ jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: /* @__PURE__ */ jsxs(
18419
- Link,
18420
- {
18421
- to: "/settings",
18422
- className: "flex items-center gap-3 px-3 py-2 text-sm text-muted-foreground dark:text-muted-foreground rounded-lg hover:bg-muted dark:hover:bg-muted",
18423
- children: [
18424
- /* @__PURE__ */ jsx(Settings, { className: "h-5 w-5" }),
18425
- t("common.settings")
18426
- ]
18427
- }
18428
- ) })
18437
+ sidebarFooter && /* @__PURE__ */ jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: sidebarFooter })
18429
18438
  ]
18430
18439
  }
18431
18440
  ),
@@ -18452,32 +18461,40 @@ var init_DashboardLayout = __esm({
18452
18461
  children: /* @__PURE__ */ jsx(Menu$1, { className: "h-5 w-5" })
18453
18462
  }
18454
18463
  ),
18455
- showSearch && /* @__PURE__ */ jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
18464
+ searchEnabled && /* @__PURE__ */ jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
18456
18465
  /* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground dark:text-muted-foreground" }),
18457
18466
  /* @__PURE__ */ jsx(
18458
18467
  Input,
18459
18468
  {
18460
18469
  type: "search",
18461
18470
  placeholder: t("common.search"),
18462
- className: "pl-10 w-full"
18471
+ className: "pl-10 w-full",
18472
+ onKeyDown: (e) => {
18473
+ if (e.key === "Enter") {
18474
+ handleSearchSubmit(e.target.value);
18475
+ }
18476
+ }
18463
18477
  }
18464
18478
  )
18465
18479
  ] }) }),
18466
18480
  /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "xs", children: [
18467
18481
  headerActions,
18468
- /* @__PURE__ */ jsx(ThemeToggle, {}),
18469
- /* @__PURE__ */ jsxs(
18482
+ showThemeToggle && /* @__PURE__ */ jsx(ThemeToggle, {}),
18483
+ notificationsEnabled && /* @__PURE__ */ jsxs(
18470
18484
  Button,
18471
18485
  {
18472
18486
  variant: "ghost",
18473
18487
  className: "relative p-2 rounded-full hover:bg-muted dark:hover:bg-muted",
18488
+ onClick: handleNotificationClick,
18489
+ "aria-label": t("common.notifications"),
18474
18490
  children: [
18475
18491
  /* @__PURE__ */ jsx(Bell, { className: "h-5 w-5 text-muted-foreground dark:text-muted-foreground" }),
18476
- /* @__PURE__ */ jsx(
18492
+ unreadCount > 0 && /* @__PURE__ */ jsx(
18477
18493
  Box,
18478
18494
  {
18479
18495
  as: "span",
18480
- className: "absolute top-1 right-1 w-2 h-2 bg-error rounded-full"
18496
+ className: "absolute -top-0.5 -right-0.5 min-w-[18px] h-[18px] px-1 bg-error rounded-full text-[10px] font-semibold text-white flex items-center justify-center",
18497
+ children: unreadCount > 99 ? "99+" : unreadCount
18481
18498
  }
18482
18499
  )
18483
18500
  ]
@@ -18542,17 +18559,6 @@ var init_DashboardLayout = __esm({
18542
18559
  }
18543
18560
  )
18544
18561
  ] }),
18545
- /* @__PURE__ */ jsxs(
18546
- Link,
18547
- {
18548
- to: "/settings",
18549
- className: "flex items-center gap-2 px-4 py-2 text-sm text-foreground dark:text-foreground hover:bg-muted dark:hover:bg-muted",
18550
- children: [
18551
- /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" }),
18552
- t("common.settings")
18553
- ]
18554
- }
18555
- ),
18556
18562
  /* @__PURE__ */ jsxs(
18557
18563
  Button,
18558
18564
  {
@@ -18577,7 +18583,7 @@ var init_DashboardLayout = __esm({
18577
18583
  )
18578
18584
  }
18579
18585
  ),
18580
- /* @__PURE__ */ jsx(Box, { as: "main", className: "p-4 sm:p-6 pb-20 sm:pb-6", children })
18586
+ /* @__PURE__ */ jsx(Box, { as: "main", className: "p-4 sm:p-6", children })
18581
18587
  ] })
18582
18588
  ] });
18583
18589
  };
@@ -18144,6 +18144,7 @@ var init_DashboardLayout = __esm({
18144
18144
  init_Typography();
18145
18145
  init_Icon();
18146
18146
  init_useAuthContext();
18147
+ init_useEventBus();
18147
18148
  init_useTranslate();
18148
18149
  DashboardLayout = ({
18149
18150
  appName = "{{APP_TITLE}}",
@@ -18151,11 +18152,29 @@ var init_DashboardLayout = __esm({
18151
18152
  navItems = [],
18152
18153
  user: userProp,
18153
18154
  headerActions,
18154
- showSearch = true,
18155
+ showSearch = false,
18156
+ searchEvent,
18157
+ onSearchSubmit,
18158
+ notifications,
18159
+ notificationClickEvent,
18160
+ onNotificationClick,
18161
+ showThemeToggle = true,
18155
18162
  sidebarFooter,
18156
18163
  onSignOut: onSignOutProp,
18157
18164
  children
18158
18165
  }) => {
18166
+ const eventBus = useEventBus();
18167
+ const searchEnabled = showSearch || Boolean(searchEvent) || Boolean(onSearchSubmit);
18168
+ const notificationsEnabled = Array.isArray(notifications);
18169
+ const unreadCount = notificationsEnabled ? notifications.filter((n) => n.read !== true).length : 0;
18170
+ const handleSearchSubmit = (value) => {
18171
+ if (searchEvent) eventBus.emit(`UI:${searchEvent}`, { value });
18172
+ if (onSearchSubmit) onSearchSubmit(value);
18173
+ };
18174
+ const handleNotificationClick = () => {
18175
+ if (notificationClickEvent) eventBus.emit(`UI:${notificationClickEvent}`, {});
18176
+ if (onNotificationClick) onNotificationClick();
18177
+ };
18159
18178
  const [sidebarOpen, setSidebarOpen] = React113.useState(false);
18160
18179
  const [userMenuOpen, setUserMenuOpen] = React113.useState(false);
18161
18180
  const location = reactRouterDom.useLocation();
@@ -18236,17 +18255,7 @@ var init_DashboardLayout = __esm({
18236
18255
  ))
18237
18256
  }
18238
18257
  ),
18239
- sidebarFooter || /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(
18240
- reactRouterDom.Link,
18241
- {
18242
- to: "/settings",
18243
- className: "flex items-center gap-3 px-3 py-2 text-sm text-muted-foreground dark:text-muted-foreground rounded-lg hover:bg-muted dark:hover:bg-muted",
18244
- children: [
18245
- /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Settings, { className: "h-5 w-5" }),
18246
- t("common.settings")
18247
- ]
18248
- }
18249
- ) })
18258
+ sidebarFooter && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: sidebarFooter })
18250
18259
  ]
18251
18260
  }
18252
18261
  ),
@@ -18273,32 +18282,40 @@ var init_DashboardLayout = __esm({
18273
18282
  children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Menu, { className: "h-5 w-5" })
18274
18283
  }
18275
18284
  ),
18276
- showSearch && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
18285
+ searchEnabled && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
18277
18286
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground dark:text-muted-foreground" }),
18278
18287
  /* @__PURE__ */ jsxRuntime.jsx(
18279
18288
  Input,
18280
18289
  {
18281
18290
  type: "search",
18282
18291
  placeholder: t("common.search"),
18283
- className: "pl-10 w-full"
18292
+ className: "pl-10 w-full",
18293
+ onKeyDown: (e) => {
18294
+ if (e.key === "Enter") {
18295
+ handleSearchSubmit(e.target.value);
18296
+ }
18297
+ }
18284
18298
  }
18285
18299
  )
18286
18300
  ] }) }),
18287
18301
  /* @__PURE__ */ jsxRuntime.jsxs(HStack, { align: "center", gap: "xs", children: [
18288
18302
  headerActions,
18289
- /* @__PURE__ */ jsxRuntime.jsx(ThemeToggle, {}),
18290
- /* @__PURE__ */ jsxRuntime.jsxs(
18303
+ showThemeToggle && /* @__PURE__ */ jsxRuntime.jsx(ThemeToggle, {}),
18304
+ notificationsEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
18291
18305
  Button,
18292
18306
  {
18293
18307
  variant: "ghost",
18294
18308
  className: "relative p-2 rounded-full hover:bg-muted dark:hover:bg-muted",
18309
+ onClick: handleNotificationClick,
18310
+ "aria-label": t("common.notifications"),
18295
18311
  children: [
18296
18312
  /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Bell, { className: "h-5 w-5 text-muted-foreground dark:text-muted-foreground" }),
18297
- /* @__PURE__ */ jsxRuntime.jsx(
18313
+ unreadCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(
18298
18314
  Box,
18299
18315
  {
18300
18316
  as: "span",
18301
- className: "absolute top-1 right-1 w-2 h-2 bg-error rounded-full"
18317
+ className: "absolute -top-0.5 -right-0.5 min-w-[18px] h-[18px] px-1 bg-error rounded-full text-[10px] font-semibold text-white flex items-center justify-center",
18318
+ children: unreadCount > 99 ? "99+" : unreadCount
18302
18319
  }
18303
18320
  )
18304
18321
  ]
@@ -18363,17 +18380,6 @@ var init_DashboardLayout = __esm({
18363
18380
  }
18364
18381
  )
18365
18382
  ] }),
18366
- /* @__PURE__ */ jsxRuntime.jsxs(
18367
- reactRouterDom.Link,
18368
- {
18369
- to: "/settings",
18370
- className: "flex items-center gap-2 px-4 py-2 text-sm text-foreground dark:text-foreground hover:bg-muted dark:hover:bg-muted",
18371
- children: [
18372
- /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Settings, { className: "h-4 w-4" }),
18373
- t("common.settings")
18374
- ]
18375
- }
18376
- ),
18377
18383
  /* @__PURE__ */ jsxRuntime.jsxs(
18378
18384
  Button,
18379
18385
  {
@@ -18398,7 +18404,7 @@ var init_DashboardLayout = __esm({
18398
18404
  )
18399
18405
  }
18400
18406
  ),
18401
- /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "main", className: "p-4 sm:p-6 pb-20 sm:pb-6", children })
18407
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { as: "main", className: "p-4 sm:p-6", children })
18402
18408
  ] })
18403
18409
  ] });
18404
18410
  };
@@ -5,7 +5,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import * as LucideIcons from 'lucide-react';
8
- import { X, AlertTriangle, Info, AlertCircle, CheckCircle, Loader2, List, Printer, ChevronRight, ChevronLeft, Check, Copy, Code, FileText, WrapText, Trash2, Settings, Menu as Menu$1, Search, Bell, ChevronDown, LogOut, ZoomOut, ZoomIn, Download, FileQuestion, Inbox, XCircle, Filter, Plus, Pause, Play, RotateCcw, Package, Calendar, Pencil, Eye, MoreHorizontal, Image as Image$1, Upload, Minus, ArrowLeft, HelpCircle, ChevronUp, Eraser, Star, TrendingUp, TrendingDown, ArrowUp, ArrowDown, MoreVertical, Sun, Moon, Circle, Clock, CheckCircle2, ArrowRight, FileWarning, SkipForward, Bug, Send, Wrench, User, Tag, DollarSign, Zap, Sword, Move, Heart, Shield } from 'lucide-react';
8
+ import { X, AlertTriangle, Info, AlertCircle, CheckCircle, Loader2, List, Printer, ChevronRight, ChevronLeft, Check, Copy, Code, FileText, WrapText, Trash2, Menu as Menu$1, Search, Bell, ChevronDown, LogOut, ZoomOut, ZoomIn, Download, FileQuestion, Inbox, XCircle, Filter, Plus, Pause, Play, RotateCcw, Package, Calendar, Pencil, Eye, MoreHorizontal, Image as Image$1, Upload, Minus, ArrowLeft, HelpCircle, ChevronUp, Eraser, Star, TrendingUp, TrendingDown, ArrowUp, ArrowDown, MoreVertical, Sun, Moon, Circle, Clock, CheckCircle2, ArrowRight, FileWarning, SkipForward, Bug, Send, Wrench, User, Tag, DollarSign, Zap, Sword, Move, Heart, Shield } from 'lucide-react';
9
9
  import { evaluate, createMinimalContext } from '@almadar/evaluator';
10
10
  import { useUISlots, UISlotProvider } from '@almadar/ui/context';
11
11
  import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
@@ -18099,6 +18099,7 @@ var init_DashboardLayout = __esm({
18099
18099
  init_Typography();
18100
18100
  init_Icon();
18101
18101
  init_useAuthContext();
18102
+ init_useEventBus();
18102
18103
  init_useTranslate();
18103
18104
  DashboardLayout = ({
18104
18105
  appName = "{{APP_TITLE}}",
@@ -18106,11 +18107,29 @@ var init_DashboardLayout = __esm({
18106
18107
  navItems = [],
18107
18108
  user: userProp,
18108
18109
  headerActions,
18109
- showSearch = true,
18110
+ showSearch = false,
18111
+ searchEvent,
18112
+ onSearchSubmit,
18113
+ notifications,
18114
+ notificationClickEvent,
18115
+ onNotificationClick,
18116
+ showThemeToggle = true,
18110
18117
  sidebarFooter,
18111
18118
  onSignOut: onSignOutProp,
18112
18119
  children
18113
18120
  }) => {
18121
+ const eventBus = useEventBus();
18122
+ const searchEnabled = showSearch || Boolean(searchEvent) || Boolean(onSearchSubmit);
18123
+ const notificationsEnabled = Array.isArray(notifications);
18124
+ const unreadCount = notificationsEnabled ? notifications.filter((n) => n.read !== true).length : 0;
18125
+ const handleSearchSubmit = (value) => {
18126
+ if (searchEvent) eventBus.emit(`UI:${searchEvent}`, { value });
18127
+ if (onSearchSubmit) onSearchSubmit(value);
18128
+ };
18129
+ const handleNotificationClick = () => {
18130
+ if (notificationClickEvent) eventBus.emit(`UI:${notificationClickEvent}`, {});
18131
+ if (onNotificationClick) onNotificationClick();
18132
+ };
18114
18133
  const [sidebarOpen, setSidebarOpen] = useState(false);
18115
18134
  const [userMenuOpen, setUserMenuOpen] = useState(false);
18116
18135
  const location = useLocation();
@@ -18191,17 +18210,7 @@ var init_DashboardLayout = __esm({
18191
18210
  ))
18192
18211
  }
18193
18212
  ),
18194
- sidebarFooter || /* @__PURE__ */ jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: /* @__PURE__ */ jsxs(
18195
- Link,
18196
- {
18197
- to: "/settings",
18198
- className: "flex items-center gap-3 px-3 py-2 text-sm text-muted-foreground dark:text-muted-foreground rounded-lg hover:bg-muted dark:hover:bg-muted",
18199
- children: [
18200
- /* @__PURE__ */ jsx(Settings, { className: "h-5 w-5" }),
18201
- t("common.settings")
18202
- ]
18203
- }
18204
- ) })
18213
+ sidebarFooter && /* @__PURE__ */ jsx(Box, { className: "p-4 border-t border-border dark:border-border", children: sidebarFooter })
18205
18214
  ]
18206
18215
  }
18207
18216
  ),
@@ -18228,32 +18237,40 @@ var init_DashboardLayout = __esm({
18228
18237
  children: /* @__PURE__ */ jsx(Menu$1, { className: "h-5 w-5" })
18229
18238
  }
18230
18239
  ),
18231
- showSearch && /* @__PURE__ */ jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
18240
+ searchEnabled && /* @__PURE__ */ jsx(Box, { className: "hidden sm:block flex-1 max-w-md", children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
18232
18241
  /* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground dark:text-muted-foreground" }),
18233
18242
  /* @__PURE__ */ jsx(
18234
18243
  Input,
18235
18244
  {
18236
18245
  type: "search",
18237
18246
  placeholder: t("common.search"),
18238
- className: "pl-10 w-full"
18247
+ className: "pl-10 w-full",
18248
+ onKeyDown: (e) => {
18249
+ if (e.key === "Enter") {
18250
+ handleSearchSubmit(e.target.value);
18251
+ }
18252
+ }
18239
18253
  }
18240
18254
  )
18241
18255
  ] }) }),
18242
18256
  /* @__PURE__ */ jsxs(HStack, { align: "center", gap: "xs", children: [
18243
18257
  headerActions,
18244
- /* @__PURE__ */ jsx(ThemeToggle, {}),
18245
- /* @__PURE__ */ jsxs(
18258
+ showThemeToggle && /* @__PURE__ */ jsx(ThemeToggle, {}),
18259
+ notificationsEnabled && /* @__PURE__ */ jsxs(
18246
18260
  Button,
18247
18261
  {
18248
18262
  variant: "ghost",
18249
18263
  className: "relative p-2 rounded-full hover:bg-muted dark:hover:bg-muted",
18264
+ onClick: handleNotificationClick,
18265
+ "aria-label": t("common.notifications"),
18250
18266
  children: [
18251
18267
  /* @__PURE__ */ jsx(Bell, { className: "h-5 w-5 text-muted-foreground dark:text-muted-foreground" }),
18252
- /* @__PURE__ */ jsx(
18268
+ unreadCount > 0 && /* @__PURE__ */ jsx(
18253
18269
  Box,
18254
18270
  {
18255
18271
  as: "span",
18256
- className: "absolute top-1 right-1 w-2 h-2 bg-error rounded-full"
18272
+ className: "absolute -top-0.5 -right-0.5 min-w-[18px] h-[18px] px-1 bg-error rounded-full text-[10px] font-semibold text-white flex items-center justify-center",
18273
+ children: unreadCount > 99 ? "99+" : unreadCount
18257
18274
  }
18258
18275
  )
18259
18276
  ]
@@ -18318,17 +18335,6 @@ var init_DashboardLayout = __esm({
18318
18335
  }
18319
18336
  )
18320
18337
  ] }),
18321
- /* @__PURE__ */ jsxs(
18322
- Link,
18323
- {
18324
- to: "/settings",
18325
- className: "flex items-center gap-2 px-4 py-2 text-sm text-foreground dark:text-foreground hover:bg-muted dark:hover:bg-muted",
18326
- children: [
18327
- /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" }),
18328
- t("common.settings")
18329
- ]
18330
- }
18331
- ),
18332
18338
  /* @__PURE__ */ jsxs(
18333
18339
  Button,
18334
18340
  {
@@ -18353,7 +18359,7 @@ var init_DashboardLayout = __esm({
18353
18359
  )
18354
18360
  }
18355
18361
  ),
18356
- /* @__PURE__ */ jsx(Box, { as: "main", className: "p-4 sm:p-6 pb-20 sm:pb-6", children })
18362
+ /* @__PURE__ */ jsx(Box, { as: "main", className: "p-4 sm:p-6", children })
18357
18363
  ] })
18358
18364
  ] });
18359
18365
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "4.22.3",
3
+ "version": "4.23.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "main": "./dist/components/index.js",