@alepha/ui 0.19.0 → 0.19.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.
Files changed (151) hide show
  1. package/dist/admin/{AdminApiKeys-Bt1PjO6o.js → AdminApiKeys-C2ze85eD.js} +2 -2
  2. package/dist/admin/{AdminApiKeys-Bt1PjO6o.js.map → AdminApiKeys-C2ze85eD.js.map} +1 -1
  3. package/dist/admin/{AdminAudits-C7c1CN4c.js → AdminAudits-BIj81e4k.js} +2 -2
  4. package/dist/admin/{AdminAudits-C7c1CN4c.js.map → AdminAudits-BIj81e4k.js.map} +1 -1
  5. package/dist/admin/{AdminDashboard-C3RXpTp6.js → AdminDashboard-PMVzrwSu.js} +2 -2
  6. package/dist/admin/{AdminDashboard-C3RXpTp6.js.map → AdminDashboard-PMVzrwSu.js.map} +1 -1
  7. package/dist/admin/AdminFiles-Bq03BLt-.js +189 -0
  8. package/dist/admin/AdminFiles-Bq03BLt-.js.map +1 -0
  9. package/dist/admin/{AdminJobExecutions-D-G8RIlr.js → AdminJobs-D1_QGCDy.js} +400 -356
  10. package/dist/admin/AdminJobs-D1_QGCDy.js.map +1 -0
  11. package/dist/admin/{AdminLayout-BmZ9mtXh.js → AdminLayout-BNiwiw2D.js} +2 -2
  12. package/dist/admin/{AdminLayout-BmZ9mtXh.js.map → AdminLayout-BNiwiw2D.js.map} +1 -1
  13. package/dist/admin/{AdminNotifications-DHdzksww.js → AdminNotifications-DSKQtUfn.js} +84 -122
  14. package/dist/admin/AdminNotifications-DSKQtUfn.js.map +1 -0
  15. package/dist/admin/{AdminParameters-CyZQSXnN.js → AdminParameters-CoB7EhyM.js} +2 -2
  16. package/dist/admin/{AdminParameters-CyZQSXnN.js.map → AdminParameters-CoB7EhyM.js.map} +1 -1
  17. package/dist/admin/{AdminSessions--xwELDSO.js → AdminSessions-DFbFcrJQ.js} +2 -2
  18. package/dist/admin/{AdminSessions--xwELDSO.js.map → AdminSessions-DFbFcrJQ.js.map} +1 -1
  19. package/dist/admin/{AdminUserLayout-DvBTG5gd.js → AdminUserLayout-fSfi3KMm.js} +3 -3
  20. package/dist/admin/{AdminUserLayout-DvBTG5gd.js.map → AdminUserLayout-fSfi3KMm.js.map} +1 -1
  21. package/dist/admin/{AdminUserProfile-CzsPBl6Z.js → AdminUserProfile-_C-h8vUK.js} +3 -3
  22. package/dist/admin/{AdminUserProfile-CzsPBl6Z.js.map → AdminUserProfile-_C-h8vUK.js.map} +1 -1
  23. package/dist/admin/{AdminUserSessions-C-aUnhVN.js → AdminUserSessions-KpJHIeQo.js} +2 -2
  24. package/dist/admin/{AdminUserSessions-C-aUnhVN.js.map → AdminUserSessions-KpJHIeQo.js.map} +1 -1
  25. package/dist/admin/{AdminUsers-BYwei5sj.js → AdminUsers-DcVrzdQP.js} +2 -2
  26. package/dist/admin/{AdminUsers-BYwei5sj.js.map → AdminUsers-DcVrzdQP.js.map} +1 -1
  27. package/dist/admin/{AuthLayout-CkPGLJku.js → AuthLayout-CazfLzcf.js} +2 -2
  28. package/dist/admin/{AuthLayout-CkPGLJku.js.map → AuthLayout-CazfLzcf.js.map} +1 -1
  29. package/dist/admin/{Login-DSBqNsZc.js → Login-CaMjUrDP.js} +2 -2
  30. package/dist/admin/{Login-DSBqNsZc.js.map → Login-CaMjUrDP.js.map} +1 -1
  31. package/dist/admin/{Profile-CDRjJo0P.js → Profile-Ca4fZX15.js} +2 -2
  32. package/dist/{auth/Profile-Cy93pNTw.js.map → admin/Profile-Ca4fZX15.js.map} +1 -1
  33. package/dist/admin/{Register-4QGFOnfh.js → Register-C5DyKWPO.js} +2 -2
  34. package/dist/{demo/Register-KKZwr_lL.js.map → admin/Register-C5DyKWPO.js.map} +1 -1
  35. package/dist/admin/{ResetPassword-Gxc9L_mY.js → ResetPassword-BA5sAgXo.js} +2 -2
  36. package/dist/{auth/ResetPassword-B61QPlQi.js.map → admin/ResetPassword-BA5sAgXo.js.map} +1 -1
  37. package/dist/admin/{VerifyEmail-D7G5NnaN.js → VerifyEmail-DKNXROj_.js} +2 -2
  38. package/dist/{auth/VerifyEmail-CqBJ11id.js.map → admin/VerifyEmail-DKNXROj_.js.map} +1 -1
  39. package/dist/admin/{adminUserAtom-DCi4wf-v.js → adminUserAtom-BLNc7XbT.js} +1 -1
  40. package/dist/admin/{adminUserAtom-DCi4wf-v.js.map → adminUserAtom-BLNc7XbT.js.map} +1 -1
  41. package/dist/admin/{core-D1AbU50V.js → core-CJCEx18C.js} +111 -4
  42. package/dist/admin/core-CJCEx18C.js.map +1 -0
  43. package/dist/admin/index.d.ts +21 -3
  44. package/dist/admin/index.d.ts.map +1 -1
  45. package/dist/admin/index.js +29 -58
  46. package/dist/admin/index.js.map +1 -1
  47. package/dist/auth/{AuthLayout-CfRKcTqP.js → AuthLayout-vXPcCVzp.js} +2 -2
  48. package/dist/auth/{AuthLayout-CfRKcTqP.js.map → AuthLayout-vXPcCVzp.js.map} +1 -1
  49. package/dist/auth/{Login-DJyweoPS.js → Login-Dg08QR20.js} +2 -2
  50. package/dist/{demo/Login-CqG1iJbn.js.map → auth/Login-Dg08QR20.js.map} +1 -1
  51. package/dist/{demo/Profile-C0ojJCaG.js → auth/Profile-Bb5O1yeh.js} +2 -2
  52. package/dist/{admin/Profile-CDRjJo0P.js.map → auth/Profile-Bb5O1yeh.js.map} +1 -1
  53. package/dist/auth/{Register-CSqzzitW.js → Register-B2AN71NC.js} +2 -2
  54. package/dist/auth/{Register-CSqzzitW.js.map → Register-B2AN71NC.js.map} +1 -1
  55. package/dist/{demo/ResetPassword-DMrLFEtr.js → auth/ResetPassword-BLxwzbDj.js} +2 -2
  56. package/dist/{admin/ResetPassword-Gxc9L_mY.js.map → auth/ResetPassword-BLxwzbDj.js.map} +1 -1
  57. package/dist/auth/{VerifyEmail-CqBJ11id.js → VerifyEmail-CSDOk3Zm.js} +2 -2
  58. package/dist/{demo/VerifyEmail-BFCAFz6T.js.map → auth/VerifyEmail-CSDOk3Zm.js.map} +1 -1
  59. package/dist/auth/{core-C6D3pazL.js → core-DuGkjPiU.js} +2 -1
  60. package/dist/auth/core-DuGkjPiU.js.map +1 -0
  61. package/dist/auth/index.d.ts +20 -0
  62. package/dist/auth/index.d.ts.map +1 -1
  63. package/dist/auth/index.js +11 -11
  64. package/dist/core/index.d.ts +69 -17
  65. package/dist/core/index.d.ts.map +1 -1
  66. package/dist/core/index.js +110 -8
  67. package/dist/core/index.js.map +1 -1
  68. package/dist/demo/{AuthLayout-Dq5tSLSc.js → AuthLayout-DPsOOG4u.js} +2 -2
  69. package/dist/demo/{AuthLayout-Dq5tSLSc.js.map → AuthLayout-DPsOOG4u.js.map} +1 -1
  70. package/dist/demo/{DemoButton-_Ws2w-J0.js → DemoButton-wzcqGk4u.js} +3 -3
  71. package/dist/demo/{DemoButton-_Ws2w-J0.js.map → DemoButton-wzcqGk4u.js.map} +1 -1
  72. package/dist/demo/{DemoControlSelect-ChP4ZOpQ.js → DemoControlSelect-CMWvQ6Gm.js} +3 -3
  73. package/dist/demo/{DemoControlSelect-ChP4ZOpQ.js.map → DemoControlSelect-CMWvQ6Gm.js.map} +1 -1
  74. package/dist/demo/{DemoDataTable-Hwf_UUni.js → DemoDataTable-CHsAP3e2.js} +3 -3
  75. package/dist/demo/{DemoDataTable-Hwf_UUni.js.map → DemoDataTable-CHsAP3e2.js.map} +1 -1
  76. package/dist/demo/{DemoDialog-B01OMVRd.js → DemoDialog-Co2IePxX.js} +2 -2
  77. package/dist/demo/{DemoDialog-B01OMVRd.js.map → DemoDialog-Co2IePxX.js.map} +1 -1
  78. package/dist/demo/{DemoFlex-870PEl0V.js → DemoFlex-OEwQt5do.js} +3 -3
  79. package/dist/demo/{DemoFlex-870PEl0V.js.map → DemoFlex-OEwQt5do.js.map} +1 -1
  80. package/dist/demo/DemoHeading-Db-XkQIK.js +69 -0
  81. package/dist/demo/DemoHeading-Db-XkQIK.js.map +1 -0
  82. package/dist/demo/{DemoHome-DRbL2eGf.js → DemoHome-Cyp29ygy.js} +2 -2
  83. package/dist/demo/{DemoHome-DRbL2eGf.js.map → DemoHome-Cyp29ygy.js.map} +1 -1
  84. package/dist/demo/{DemoJsonViewer-DoABiqBW.js → DemoJsonViewer-DXtCeMzH.js} +3 -3
  85. package/dist/demo/{DemoJsonViewer-DoABiqBW.js.map → DemoJsonViewer-DXtCeMzH.js.map} +1 -1
  86. package/dist/demo/{DemoLayout-CN_PDCX2.js → DemoLayout-hh9VmZQP.js} +2 -2
  87. package/dist/demo/{DemoLayout-CN_PDCX2.js.map → DemoLayout-hh9VmZQP.js.map} +1 -1
  88. package/dist/demo/{DemoLogin-B5x-ug3Q.js → DemoLogin-DX7mnmkh.js} +13 -8
  89. package/dist/demo/{DemoLogin-B5x-ug3Q.js.map → DemoLogin-DX7mnmkh.js.map} +1 -1
  90. package/dist/demo/{DemoRegister-Q6sg2xuV.js → DemoRegister-DVcZl04m.js} +13 -8
  91. package/dist/demo/{DemoRegister-Q6sg2xuV.js.map → DemoRegister-DVcZl04m.js.map} +1 -1
  92. package/dist/demo/{DemoResetPassword-DrqZfmEw.js → DemoResetPassword-CPENlZH5.js} +13 -8
  93. package/dist/demo/{DemoResetPassword-DrqZfmEw.js.map → DemoResetPassword-CPENlZH5.js.map} +1 -1
  94. package/dist/demo/{DemoSidebar-CfKS6w1o.js → DemoSidebar-CGu7DZeM.js} +3 -3
  95. package/dist/demo/{DemoSidebar-CfKS6w1o.js.map → DemoSidebar-CGu7DZeM.js.map} +1 -1
  96. package/dist/demo/{DemoText-pT6Gi5b5.js → DemoText-DYUJ7bY_.js} +3 -3
  97. package/dist/demo/{DemoText-pT6Gi5b5.js.map → DemoText-DYUJ7bY_.js.map} +1 -1
  98. package/dist/demo/{DemoToast-I13NBzQQ.js → DemoToast-CgdnZNvx.js} +2 -2
  99. package/dist/demo/{DemoToast-I13NBzQQ.js.map → DemoToast-CgdnZNvx.js.map} +1 -1
  100. package/dist/demo/{DemoTypeForm-BqzcrtvN.js → DemoTypeForm-Pims-cGa.js} +3 -3
  101. package/dist/demo/{DemoTypeForm-BqzcrtvN.js.map → DemoTypeForm-Pims-cGa.js.map} +1 -1
  102. package/dist/demo/{DemoVerifyEmail-HwD8xfQw.js → DemoVerifyEmail-C7B3xxch.js} +8 -8
  103. package/dist/demo/{DemoVerifyEmail-HwD8xfQw.js.map → DemoVerifyEmail-C7B3xxch.js.map} +1 -1
  104. package/dist/demo/{Login-CqG1iJbn.js → Login-pwMF4TUj.js} +2 -2
  105. package/dist/{auth/Login-DJyweoPS.js.map → demo/Login-pwMF4TUj.js.map} +1 -1
  106. package/dist/{auth/Profile-Cy93pNTw.js → demo/Profile-BliZapZS.js} +2 -2
  107. package/dist/demo/{Profile-C0ojJCaG.js.map → Profile-BliZapZS.js.map} +1 -1
  108. package/dist/demo/{Register-KKZwr_lL.js → Register-CiwAT7Hy.js} +2 -2
  109. package/dist/{admin/Register-4QGFOnfh.js.map → demo/Register-CiwAT7Hy.js.map} +1 -1
  110. package/dist/{auth/ResetPassword-B61QPlQi.js → demo/ResetPassword-l9Vg4JE-.js} +2 -2
  111. package/dist/demo/{ResetPassword-DMrLFEtr.js.map → ResetPassword-l9Vg4JE-.js.map} +1 -1
  112. package/dist/demo/{Showcase-D49Wud2v.js → Showcase-CX6bDgwe.js} +2 -2
  113. package/dist/demo/{Showcase-D49Wud2v.js.map → Showcase-CX6bDgwe.js.map} +1 -1
  114. package/dist/demo/{VerifyEmail-BFCAFz6T.js → VerifyEmail-CAB-OS7i.js} +2 -2
  115. package/dist/{admin/VerifyEmail-D7G5NnaN.js.map → demo/VerifyEmail-CAB-OS7i.js.map} +1 -1
  116. package/dist/demo/{auth-D9qTZzCa.js → auth-uegJAdKu.js} +8 -8
  117. package/dist/demo/{auth-D9qTZzCa.js.map → auth-uegJAdKu.js.map} +1 -1
  118. package/dist/demo/{core-DRtQklr3.js → core-B4LVHzPn.js} +111 -9
  119. package/dist/demo/core-B4LVHzPn.js.map +1 -0
  120. package/dist/demo/index.js +19 -19
  121. package/dist/demo/index.js.map +1 -1
  122. package/package.json +6 -9
  123. package/src/admin/AdminRouter.tsx +5 -37
  124. package/src/admin/components/files/AdminFiles.tsx +123 -1
  125. package/src/admin/components/jobs/{AdminJobExecutions.tsx → AdminJobs.tsx} +450 -317
  126. package/src/admin/components/notifications/AdminNotifications.tsx +11 -25
  127. package/src/core/components/Section.tsx +109 -0
  128. package/src/core/components/SectionHeader.tsx +106 -0
  129. package/src/core/index.ts +4 -1
  130. package/src/core/table/components/DataTable.tsx +5 -1
  131. package/src/demo/DemoRouter.ts +1 -1
  132. package/src/demo/components/auth/DemoLogin.tsx +5 -0
  133. package/src/demo/components/auth/DemoRegister.tsx +5 -0
  134. package/src/demo/components/auth/DemoResetPassword.tsx +5 -0
  135. package/src/demo/components/core/DemoHeading.tsx +56 -3
  136. package/dist/admin/AdminFiles-31ivR6Wq.js +0 -110
  137. package/dist/admin/AdminFiles-31ivR6Wq.js.map +0 -1
  138. package/dist/admin/AdminJobDashboard-BABLe7hL.js +0 -402
  139. package/dist/admin/AdminJobDashboard-BABLe7hL.js.map +0 -1
  140. package/dist/admin/AdminJobExecutions-D-G8RIlr.js.map +0 -1
  141. package/dist/admin/AdminJobRegistry-oIS3K9NX.js +0 -269
  142. package/dist/admin/AdminJobRegistry-oIS3K9NX.js.map +0 -1
  143. package/dist/admin/AdminNotifications-DHdzksww.js.map +0 -1
  144. package/dist/admin/core-D1AbU50V.js.map +0 -1
  145. package/dist/auth/core-C6D3pazL.js.map +0 -1
  146. package/dist/demo/DemoHeading-C1YR27fz.js +0 -17
  147. package/dist/demo/DemoHeading-C1YR27fz.js.map +0 -1
  148. package/dist/demo/core-DRtQklr3.js.map +0 -1
  149. package/src/admin/components/jobs/AdminJobDashboard.tsx +0 -380
  150. package/src/admin/components/jobs/AdminJobRegistry.tsx +0 -301
  151. package/src/core/components/Heading.tsx +0 -19
@@ -4,6 +4,7 @@ import {
4
4
  DataTable,
5
5
  DetailList,
6
6
  Flex,
7
+ Section,
7
8
  Text,
8
9
  useToast,
9
10
  } from "@alepha/ui";
@@ -384,19 +385,13 @@ const NotificationDetailContent = ({
384
385
  </Flex>
385
386
 
386
387
  {/* Details */}
387
- <Paper p="sm" radius="md" withBorder>
388
- <Text size="sm" fw={600} mb="xs">
389
- Details
390
- </Text>
388
+ <Section title="Details" p="sm">
391
389
  <DetailList items={detailItems} columns={2} />
392
- </Paper>
390
+ </Section>
393
391
 
394
392
  {/* Rendered Content */}
395
393
  {rendered && (
396
- <Paper p="sm" radius="md" withBorder>
397
- <Text size="sm" fw={600} mb="xs">
398
- Content
399
- </Text>
394
+ <Section title="Content" p="sm">
400
395
  {rendered.type === "email" && (
401
396
  <Flex direction="column" gap="xs">
402
397
  <Flex direction="column" gap={2}>
@@ -455,25 +450,19 @@ const NotificationDetailContent = ({
455
450
  </Flex>
456
451
  </Flex>
457
452
  )}
458
- </Paper>
453
+ </Section>
459
454
  )}
460
455
 
461
456
  {/* Variables */}
462
457
  {detail.variables && Object.keys(detail.variables).length > 0 && (
463
- <Paper p="sm" radius="md" withBorder>
464
- <Text size="sm" fw={600} mb="xs">
465
- Variables
466
- </Text>
458
+ <Section title="Variables" p="sm">
467
459
  <Code block>{JSON.stringify(detail.variables, null, 2)}</Code>
468
- </Paper>
460
+ </Section>
469
461
  )}
470
462
 
471
463
  {/* Error */}
472
464
  {detail.error && (
473
- <Paper p="sm" radius="md" withBorder>
474
- <Text size="sm" fw={600} mb="xs">
475
- Error
476
- </Text>
465
+ <Section title="Error" p="sm">
477
466
  <Paper p="xs" radius="sm" withBorder>
478
467
  <Text
479
468
  size="sm"
@@ -485,15 +474,12 @@ const NotificationDetailContent = ({
485
474
  {detail.error}
486
475
  </Text>
487
476
  </Paper>
488
- </Paper>
477
+ </Section>
489
478
  )}
490
479
 
491
480
  {/* Logs */}
492
481
  {detail.logs && detail.logs.length > 0 && (
493
- <Paper p="sm" radius="md" withBorder>
494
- <Text size="sm" fw={600} mb="xs">
495
- Logs ({detail.logs.length})
496
- </Text>
482
+ <Section title={`Logs (${detail.logs.length})`} p="sm">
497
483
  <Flex
498
484
  direction="column"
499
485
  style={{ maxHeight: 300, overflowY: "auto" }}
@@ -510,7 +496,7 @@ const NotificationDetailContent = ({
510
496
  </Flex>
511
497
  ))}
512
498
  </Flex>
513
- </Paper>
499
+ </Section>
514
500
  )}
515
501
  </Flex>
516
502
  );
@@ -0,0 +1,109 @@
1
+ import { Collapse } from "@mantine/core";
2
+ import { IconChevronDown, IconChevronRight } from "@tabler/icons-react";
3
+ import { type ReactNode, useState } from "react";
4
+ import { ui } from "../constants/ui.ts";
5
+ import type { ActionProps } from "./buttons/ActionButton.tsx";
6
+ import Flex from "./Flex.tsx";
7
+ import SectionHeader, { type SectionHeaderProps } from "./SectionHeader.tsx";
8
+
9
+ export interface SectionProps
10
+ extends Omit<SectionHeaderProps, "extra" | "size" | "title"> {
11
+ /**
12
+ * Section header title. When omitted, no header is rendered.
13
+ */
14
+ title?: string | ReactNode;
15
+ /**
16
+ * Section content.
17
+ */
18
+ children?: ReactNode;
19
+
20
+ /**
21
+ * When `true`, the section body can be collapsed/expanded.
22
+ */
23
+ collapsible?: boolean;
24
+
25
+ /**
26
+ * Initial collapsed state. Only applies when `collapsible` is `true`.
27
+ */
28
+ defaultCollapsed?: boolean;
29
+
30
+ /**
31
+ * Padding for the section body.
32
+ */
33
+ p?: string | number;
34
+ }
35
+
36
+ const Section = (props: SectionProps) => {
37
+ const {
38
+ children,
39
+ collapsible,
40
+ defaultCollapsed = false,
41
+ title,
42
+ icon,
43
+ tag,
44
+ subtitle,
45
+ actions,
46
+ p = "md",
47
+ ...rest
48
+ } = props;
49
+
50
+ const [collapsed, setCollapsed] = useState(
51
+ collapsible ? defaultCollapsed : false,
52
+ );
53
+
54
+ const hasHeader = !!title;
55
+
56
+ const headerActions: ActionProps[] = [...(actions ?? [])];
57
+
58
+ const chevronExtra = collapsible ? (
59
+ <Flex
60
+ centerY
61
+ style={{ cursor: "pointer", flexShrink: 0 }}
62
+ onClick={() => setCollapsed((v) => !v)}
63
+ >
64
+ {collapsed ? (
65
+ <IconChevronRight size={ui.sizes.icon.sm} />
66
+ ) : (
67
+ <IconChevronDown size={ui.sizes.icon.sm} />
68
+ )}
69
+ </Flex>
70
+ ) : undefined;
71
+
72
+ return (
73
+ <Flex col surface rounded bordered {...rest}>
74
+ {hasHeader && (
75
+ <Flex
76
+ p="sm"
77
+ px="md"
78
+ style={{
79
+ cursor: collapsible ? "pointer" : undefined,
80
+ }}
81
+ onClick={collapsible ? () => setCollapsed((v) => !v) : undefined}
82
+ >
83
+ <SectionHeader
84
+ icon={icon}
85
+ title={title}
86
+ tag={tag}
87
+ subtitle={subtitle}
88
+ actions={headerActions}
89
+ size="sm"
90
+ extra={chevronExtra}
91
+ />
92
+ </Flex>
93
+ )}
94
+ {collapsible ? (
95
+ <Collapse in={!collapsed}>
96
+ <Flex col p={p} {...(hasHeader ? { borderedTop: true } : {})}>
97
+ {children}
98
+ </Flex>
99
+ </Collapse>
100
+ ) : (
101
+ <Flex col p={p} {...(hasHeader ? { borderedTop: true } : {})}>
102
+ {children}
103
+ </Flex>
104
+ )}
105
+ </Flex>
106
+ );
107
+ };
108
+
109
+ export default Section;
@@ -0,0 +1,106 @@
1
+ import { Title } from "@mantine/core";
2
+ import type { ComponentType, ReactNode } from "react";
3
+ import { ui } from "../constants/ui.ts";
4
+ import { renderIcon } from "../helpers/renderIcon.tsx";
5
+ import type { ActionProps } from "./buttons/ActionButton.tsx";
6
+ import ActionButton from "./buttons/ActionButton.tsx";
7
+ import Flex from "./Flex.tsx";
8
+ import Text from "./Text.tsx";
9
+
10
+ export interface SectionHeaderProps {
11
+ /**
12
+ * Icon displayed before the title.
13
+ * Accepts a React element or a component type (e.g. `IconUser` from tabler).
14
+ */
15
+ icon?: ReactNode | ComponentType<{ size?: number }>;
16
+
17
+ /**
18
+ * Main title text.
19
+ */
20
+ title: string | ReactNode;
21
+
22
+ /**
23
+ * Optional tag displayed after the title (e.g. a Badge or status text).
24
+ */
25
+ tag?: ReactNode;
26
+
27
+ /**
28
+ * Subtitle displayed below the title.
29
+ */
30
+ subtitle?: string | ReactNode;
31
+
32
+ /**
33
+ * Action buttons displayed on the right side.
34
+ */
35
+ actions?: ActionProps[];
36
+
37
+ /**
38
+ * Controls the title size and icon size.
39
+ *
40
+ * - `"sm"` → Title order 5, icon 16px
41
+ * - `"md"` → Title order 4, icon 20px (default)
42
+ * - `"lg"` → Title order 3, icon 24px
43
+ */
44
+ size?: "sm" | "md" | "lg";
45
+
46
+ /**
47
+ * Extra content appended to the right side (after actions).
48
+ */
49
+ extra?: ReactNode;
50
+ }
51
+
52
+ const TITLE_ORDER: Record<string, 1 | 2 | 3 | 4 | 5 | 6> = {
53
+ sm: 5,
54
+ md: 4,
55
+ lg: 3,
56
+ };
57
+
58
+ const ICON_SIZE: Record<string, number> = {
59
+ sm: ui.sizes.icon.xs,
60
+ md: ui.sizes.icon.sm,
61
+ lg: ui.sizes.icon.md,
62
+ };
63
+
64
+ const SectionHeader = (props: SectionHeaderProps) => {
65
+ const { icon, title, tag, subtitle, actions, size = "md", extra } = props;
66
+
67
+ const iconSize = ICON_SIZE[size];
68
+ const titleOrder = TITLE_ORDER[size];
69
+
70
+ return (
71
+ <Flex col gap={2}>
72
+ <Flex centerY gap="xs">
73
+ {icon && (
74
+ <Flex centerY style={{ flexShrink: 0 }}>
75
+ {renderIcon(icon, iconSize)}
76
+ </Flex>
77
+ )}
78
+ <Title order={titleOrder} lh={1.2}>
79
+ {title}
80
+ </Title>
81
+ {tag}
82
+ <Flex fill />
83
+ {actions && actions.length > 0 && (
84
+ <Flex gap="xs" centerY style={{ flexShrink: 0 }}>
85
+ {actions.map((actionProps, index) => (
86
+ <ActionButton
87
+ key={index}
88
+ size="xs"
89
+ variant="default"
90
+ {...actionProps}
91
+ />
92
+ ))}
93
+ </Flex>
94
+ )}
95
+ {extra}
96
+ </Flex>
97
+ {subtitle && (
98
+ <Text muted small>
99
+ {subtitle}
100
+ </Text>
101
+ )}
102
+ </Flex>
103
+ );
104
+ };
105
+
106
+ export default SectionHeader;
package/src/core/index.ts CHANGED
@@ -61,7 +61,6 @@ export { default as ConfirmDialog } from "./components/dialogs/ConfirmDialog.tsx
61
61
  export { default as PromptDialog } from "./components/dialogs/PromptDialog.tsx";
62
62
  export type { FlexProps } from "./components/Flex.tsx";
63
63
  export { default as Flex } from "./components/Flex.tsx";
64
- export { default as Heading } from "./components/Heading.tsx";
65
64
  export type {
66
65
  AppBarBack,
67
66
  AppBarBurger,
@@ -105,6 +104,10 @@ export { Sidebar } from "./components/layout/Sidebar.tsx";
105
104
  export { SidebarCollapsedItem } from "./components/layout/SidebarCollapsedItem.tsx";
106
105
  export type { SidebarItemProps } from "./components/layout/SidebarItem.tsx";
107
106
  export { SidebarItem } from "./components/layout/SidebarItem.tsx";
107
+ export type { SectionProps } from "./components/Section.tsx";
108
+ export { default as Section } from "./components/Section.tsx";
109
+ export type { SectionHeaderProps } from "./components/SectionHeader.tsx";
110
+ export { default as SectionHeader } from "./components/SectionHeader.tsx";
108
111
  export type { TextProps } from "./components/Text.tsx";
109
112
  export { default as Text } from "./components/Text.tsx";
110
113
  export * from "./constants/ui.ts";
@@ -90,7 +90,11 @@ const toAriaSort = (
90
90
  return "none";
91
91
  };
92
92
 
93
- const FIT_STYLE = { width: 1, whiteSpace: "nowrap" } as const;
93
+ const FIT_STYLE = {
94
+ width: 1,
95
+ whiteSpace: "nowrap",
96
+ minWidth: "fit-content",
97
+ } as const;
94
98
 
95
99
  const DataTable = <T extends object, Filters extends TObject>(
96
100
  props: DataTableProps<T, Filters>,
@@ -76,7 +76,7 @@ export class DemoRouter {
76
76
  demoHeading = $page({
77
77
  icon: IconHeading,
78
78
  path: "/heading",
79
- label: "Heading",
79
+ label: "Section",
80
80
  lazy: () => import("./components/core/DemoHeading.tsx"),
81
81
  });
82
82
 
@@ -104,6 +104,11 @@ const buildRealmConfig = (props: {
104
104
  requireNumbers: true,
105
105
  requireSpecialCharacters: false,
106
106
  },
107
+ loginRateLimit: {
108
+ ipMaxAttempts: 15,
109
+ accountMaxAttempts: 5,
110
+ windowMs: 15 * 60 * 1000,
111
+ },
107
112
  },
108
113
  };
109
114
  };
@@ -98,6 +98,11 @@ const buildRealmConfig = (props: {
98
98
  requireNumbers: true,
99
99
  requireSpecialCharacters: false,
100
100
  },
101
+ loginRateLimit: {
102
+ ipMaxAttempts: 15,
103
+ accountMaxAttempts: 5,
104
+ windowMs: 15 * 60 * 1000,
105
+ },
101
106
  },
102
107
  };
103
108
  };
@@ -45,6 +45,11 @@ const buildRealmConfig = (props: {
45
45
  requireNumbers: true,
46
46
  requireSpecialCharacters: false,
47
47
  },
48
+ loginRateLimit: {
49
+ ipMaxAttempts: 15,
50
+ accountMaxAttempts: 5,
51
+ windowMs: 15 * 60 * 1000,
52
+ },
48
53
  },
49
54
  };
50
55
  };
@@ -1,11 +1,64 @@
1
- import { Heading } from "@alepha/ui";
1
+ import { Flex, Section, SectionHeader } from "@alepha/ui";
2
+ import { IconUser } from "@tabler/icons-react";
2
3
  import { t } from "alepha";
3
4
  import Showcase from "../shared/Showcase.tsx";
4
5
 
5
6
  const DemoHeading = () => {
6
7
  return (
7
- <Showcase title="Flex" schema={t.object({})} initialValues={{}}>
8
- {(props) => <Heading title={"Hello!"} />}
8
+ <Showcase
9
+ title="SectionHeader & Section"
10
+ schema={t.object({})}
11
+ initialValues={{}}
12
+ >
13
+ {() => (
14
+ <Flex col gap="lg" w="100%">
15
+ <SectionHeader title="Simple" />
16
+
17
+ <SectionHeader
18
+ title="With Icon & Subtitle"
19
+ icon={IconUser}
20
+ subtitle="This is a subtitle"
21
+ />
22
+
23
+ <SectionHeader
24
+ title="With Actions"
25
+ subtitle="Section with action buttons"
26
+ actions={[
27
+ { children: "Edit", onClick: () => {} },
28
+ { children: "Delete", intent: "danger", onClick: () => {} },
29
+ ]}
30
+ />
31
+
32
+ <Section title="Basic Section">
33
+ Section body content goes here.
34
+ </Section>
35
+
36
+ <Section
37
+ title="With Icon & Actions"
38
+ icon={IconUser}
39
+ subtitle="A section with header features"
40
+ actions={[{ children: "Edit", onClick: () => {} }]}
41
+ >
42
+ Section body with icon and actions.
43
+ </Section>
44
+
45
+ <Section
46
+ title="Collapsible Section"
47
+ collapsible
48
+ subtitle="Click header to toggle"
49
+ >
50
+ This content can be collapsed.
51
+ </Section>
52
+
53
+ <Section title="Collapsed by Default" collapsible defaultCollapsed>
54
+ This was hidden initially.
55
+ </Section>
56
+
57
+ <Section>
58
+ Section without header — just a bordered surface container.
59
+ </Section>
60
+ </Flex>
61
+ )}
9
62
  </Showcase>
10
63
  );
11
64
  };
@@ -1,110 +0,0 @@
1
- import { l as Flex, r as DataTable, s as Text } from "./core-D1AbU50V.js";
2
- import { t } from "alepha";
3
- import { useI18n } from "alepha/react/i18n";
4
- import { jsx } from "react/jsx-runtime";
5
- import { useClient } from "alepha/react";
6
- import { files } from "alepha/api/files";
7
- //#region ../../src/admin/components/files/AdminFiles.tsx
8
- const AdminFiles = () => {
9
- const client = useClient();
10
- const { l } = useI18n();
11
- const filters = t.object({
12
- bucket: t.optional(t.string()),
13
- name: t.optional(t.string({ $control: { query: t.pick(files.schema, [
14
- "name",
15
- "bucket",
16
- "mimeType"
17
- ]) } }))
18
- });
19
- const formatFileSize = (bytes) => {
20
- if (bytes === 0) return "0 B";
21
- const k = 1024;
22
- const sizes = [
23
- "B",
24
- "KB",
25
- "MB",
26
- "GB"
27
- ];
28
- const i = Math.floor(Math.log(bytes) / Math.log(k));
29
- return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;
30
- };
31
- return /* @__PURE__ */ jsx(Flex, {
32
- p: "md",
33
- flex: 1,
34
- direction: "column",
35
- children: /* @__PURE__ */ jsx(DataTable, {
36
- submitOnInit: true,
37
- defaultSize: 10,
38
- typeFormProps: {
39
- skipSubmitButton: true,
40
- columns: 3
41
- },
42
- tableProps: {
43
- horizontalSpacing: "xs",
44
- verticalSpacing: "xs"
45
- },
46
- onFilterChange: (key, _value, form) => {
47
- if (key === "name" || key === "bucket") return form.submit();
48
- },
49
- filters,
50
- items: async (filters) => {
51
- return await client.findFiles({ query: filters });
52
- },
53
- columns: {
54
- name: {
55
- label: "Name",
56
- value: (item) => /* @__PURE__ */ jsx(Text, {
57
- size: "sm",
58
- fw: 500,
59
- lineClamp: 1,
60
- children: item.name
61
- })
62
- },
63
- bucket: {
64
- label: "Bucket",
65
- value: (item) => /* @__PURE__ */ jsx(Text, {
66
- size: "xs",
67
- ff: "monospace",
68
- children: item.bucket
69
- })
70
- },
71
- mimeType: {
72
- label: "Type",
73
- value: (item) => /* @__PURE__ */ jsx(Text, {
74
- size: "xs",
75
- c: "dimmed",
76
- children: item.mimeType
77
- })
78
- },
79
- size: {
80
- label: "Size",
81
- value: (item) => /* @__PURE__ */ jsx(Text, {
82
- size: "xs",
83
- c: "dimmed",
84
- children: formatFileSize(item.size)
85
- })
86
- },
87
- creatorName: {
88
- label: "Creator",
89
- value: (item) => /* @__PURE__ */ jsx(Text, {
90
- size: "xs",
91
- c: "dimmed",
92
- children: item.creatorName || "-"
93
- })
94
- },
95
- createdAt: {
96
- label: "Created",
97
- value: (item) => /* @__PURE__ */ jsx(Text, {
98
- size: "xs",
99
- c: "dimmed",
100
- children: l(item.createdAt, { date: "fromNow" })
101
- })
102
- }
103
- }
104
- })
105
- });
106
- };
107
- //#endregion
108
- export { AdminFiles as default };
109
-
110
- //# sourceMappingURL=AdminFiles-31ivR6Wq.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AdminFiles-31ivR6Wq.js","names":[],"sources":["../../src/admin/components/files/AdminFiles.tsx"],"sourcesContent":["import { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { type Page, t } from \"alepha\";\nimport { type FileController, type FileEntity, files } from \"alepha/api/files\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\n\nconst AdminFiles = () => {\n const client = useClient<FileController>();\n const { l } = useI18n();\n\n const filters = t.object({\n bucket: t.optional(t.string()),\n name: t.optional(\n t.string({\n $control: {\n query: t.pick(files.schema, [\"name\", \"bucket\", \"mimeType\"]),\n },\n }),\n ),\n });\n\n const formatFileSize = (bytes: number) => {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n };\n\n return (\n <Flex p=\"md\" flex={1} direction={\"column\"}>\n <DataTable<FileEntity, typeof filters>\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 3,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"name\" || key === \"bucket\") {\n return form.submit();\n }\n }}\n filters={filters}\n items={async (filters) => {\n const response = await client.findFiles({\n query: filters,\n });\n\n return response as Page<FileEntity>;\n }}\n columns={{\n name: {\n label: \"Name\",\n value: (item) => (\n <Text size=\"sm\" fw={500} lineClamp={1}>\n {item.name}\n </Text>\n ),\n },\n bucket: {\n label: \"Bucket\",\n value: (item) => (\n <Text size=\"xs\" ff=\"monospace\">\n {item.bucket}\n </Text>\n ),\n },\n mimeType: {\n label: \"Type\",\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {item.mimeType}\n </Text>\n ),\n },\n size: {\n label: \"Size\",\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {formatFileSize(item.size)}\n </Text>\n ),\n },\n creatorName: {\n label: \"Creator\",\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {item.creatorName || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Created\",\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminFiles;\n"],"mappings":";;;;;;;AAMA,MAAM,mBAAmB;CACvB,MAAM,SAAS,WAA2B;CAC1C,MAAM,EAAE,MAAM,SAAS;CAEvB,MAAM,UAAU,EAAE,OAAO;EACvB,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;EAC9B,MAAM,EAAE,SACN,EAAE,OAAO,EACP,UAAU,EACR,OAAO,EAAE,KAAK,MAAM,QAAQ;GAAC;GAAQ;GAAU;GAAW,CAAC,EAC5D,EACF,CAAC,CACH;EACF,CAAC;CAEF,MAAM,kBAAkB,UAAkB;AACxC,MAAI,UAAU,EAAG,QAAO;EACxB,MAAM,IAAI;EACV,MAAM,QAAQ;GAAC;GAAK;GAAM;GAAM;GAAK;EACrC,MAAM,IAAI,KAAK,MAAM,KAAK,IAAI,MAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AACnD,SAAO,GAAG,OAAO,YAAY,QAAQ,KAAK,GAAG,QAAQ,EAAE,CAAC,CAAC,GAAG,MAAM;;AAGpE,QACE,oBAAC,MAAD;EAAM,GAAE;EAAK,MAAM;EAAG,WAAW;YAC/B,oBAAC,WAAD;GACE,cAAA;GACA,aAAa;GACb,eAAe;IACb,kBAAkB;IAClB,SAAS;IACV;GACD,YAAY;IACV,mBAAmB;IACnB,iBAAiB;IAClB;GACD,iBAAiB,KAAK,QAAQ,SAAS;AACrC,QAAI,QAAQ,UAAU,QAAQ,SAC5B,QAAO,KAAK,QAAQ;;GAGf;GACT,OAAO,OAAO,YAAY;AAKxB,WAJiB,MAAM,OAAO,UAAU,EACtC,OAAO,SACR,CAAC;;GAIJ,SAAS;IACP,MAAM;KACJ,OAAO;KACP,QAAQ,SACN,oBAAC,MAAD;MAAM,MAAK;MAAK,IAAI;MAAK,WAAW;gBACjC,KAAK;MACD,CAAA;KAEV;IACD,QAAQ;KACN,OAAO;KACP,QAAQ,SACN,oBAAC,MAAD;MAAM,MAAK;MAAK,IAAG;gBAChB,KAAK;MACD,CAAA;KAEV;IACD,UAAU;KACR,OAAO;KACP,QAAQ,SACN,oBAAC,MAAD;MAAM,MAAK;MAAK,GAAE;gBACf,KAAK;MACD,CAAA;KAEV;IACD,MAAM;KACJ,OAAO;KACP,QAAQ,SACN,oBAAC,MAAD;MAAM,MAAK;MAAK,GAAE;gBACf,eAAe,KAAK,KAAK;MACrB,CAAA;KAEV;IACD,aAAa;KACX,OAAO;KACP,QAAQ,SACN,oBAAC,MAAD;MAAM,MAAK;MAAK,GAAE;gBACf,KAAK,eAAe;MAChB,CAAA;KAEV;IACD,WAAW;KACT,OAAO;KACP,QAAQ,SACN,oBAAC,MAAD;MAAM,MAAK;MAAK,GAAE;gBACf,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;MAClC,CAAA;KAEV;IACF;GACD,CAAA;EACG,CAAA"}