@crystaltech/hsms-shared-ui 0.5.30 → 0.5.31-alpha-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,946 +1,282 @@
1
- # hsms-shared-ui
1
+ # HSMS Shared UI
2
2
 
3
- ## HSMS-UI Documentation
4
-
5
- ### Overview
6
-
7
- `@crystaltech/hsms-shared-ui` is a customizable UI component library built with React and Material-UI. It provides a structured layout system with a `MainLayout` component, theme customization, and configurable navigation options for web applications.
8
-
9
- ---
3
+ A reusable React + MUI component library for HSMS applications, including a configurable layout, theme tools, and UI primitives.
10
4
 
11
5
  ## Installation
12
6
 
13
- To install the package, run:
14
-
15
- ```sh
16
- pnpm install @crystaltech/hsms-shared-ui@latest
17
- ```
18
-
19
- or using Yarn:
20
-
21
- ```sh
22
- yarn add @crystaltech/hsms-shared-ui@latest
23
- ```
24
-
25
- ---
26
-
27
- ## Usage
28
-
29
- ### Must Follow
30
-
31
- Create a `types` folder inside the `src` directory and add the following file:
32
-
33
- ```
34
- /src/types/hsms-shared-ui.d.ts
35
- ```
36
-
37
- **Note:** Make sure to add the following content to the `hsms-shared-ui.d.ts` file:
7
+ - pnpm: `pnpm add @crystaltech/hsms-shared-ui`
8
+ - yarn: `yarn add @crystaltech/hsms-shared-ui`
9
+ - npm: `npm i @crystaltech/hsms-shared-ui`
38
10
 
39
- ```ts
40
- declare module "@crystaltech/hsms-shared-ui";
41
- ```
11
+ Peer deps (match your app): `@mui/material`, `@mui/icons-material`, `@mui/x-date-pickers`, `@emotion/*`, `react`, `react-dom`, `dayjs`, `react-perfect-scrollbar`, `simplebar-react`.
42
12
 
43
- ### Theme Component Setup
13
+ ### Using in other modules
44
14
 
45
- Wrap your root component with the Theme Customization component and pass theme object as a prop to the component. The prop name is defaultTheme, like this,
46
-
47
- ```ts
48
- import { ThemeCustomization } from "@anwarhossain1/hsms-ui";
49
- import ReactDOM from "react-dom/client";
50
- import { BrowserRouter } from "react-router-dom";
51
- import App from "./App";
52
- import "./index.css";
53
- import reportWebVitals from "./reportWebVitals";
54
- const defaultTheme = {
55
- palette: {
56
- mode: "light",
57
- navbar: {
58
- background: "#000000",
59
- foreground: "#000000",
60
- },
61
- sidebar: {
62
- background: "#000000",
63
- foreground: "#000000",
64
- },
65
- primary: {
66
- main: "#ef5350", // You can change this color
67
- },
68
- secondary: {
69
- main: "#f8fafc",
70
- },
71
- background: {
72
- default: "#f8fafc",
73
- paper: "#ffffff",
74
- },
75
- text: {
76
- primary: "#1e293b",
77
- secondary: "#64748b",
78
- },
79
- neutral: {
80
- 100: "#f6f9fc",
81
- 200: "#e9ecef",
82
- 300: "#dee2e6",
83
- 400: "#ced4da",
84
- 500: "#adb5bd",
85
- 600: "#868e96",
86
- 700: "#495057",
87
- 800: "#343a40",
88
- 900: "#212529",
89
- },
90
- },
91
- typography: {
92
- fontFamily: "Roboto",
93
- h1: {
94
- fontSize: "2.5rem",
95
- fontWeight: 700,
96
- lineHeight: "3rem",
97
- },
98
- h2: {
99
- fontSize: "2rem",
100
- fontWeight: 700,
101
- lineHeight: "2.5rem",
102
- },
103
- h3: {
104
- fontSize: "1.75rem",
105
- fontWeight: 600,
106
- lineHeight: "2.25rem",
107
- },
108
- h4: {
109
- fontSize: "1.5rem",
110
- fontWeight: 600,
111
- lineHeight: "2rem",
112
- },
113
- h5: {
114
- fontSize: "1.25rem",
115
- fontWeight: 500,
116
- lineHeight: "1.75rem",
117
- },
118
- h6: {
119
- fontSize: "1rem",
120
- fontWeight: 500,
121
- lineHeight: "1.5rem",
122
- },
123
- subtitle1: {
124
- fontSize: "1.125rem",
125
- fontWeight: 400,
126
- lineHeight: "1.5rem",
127
- },
128
- subtitle2: {
129
- fontSize: "1rem",
130
- fontWeight: 400,
131
- lineHeight: "1.25rem",
132
- },
133
- body1: {
134
- fontSize: "1rem",
135
- fontWeight: 400,
136
- lineHeight: "1.5rem",
137
- },
138
- body2: {
139
- fontSize: "0.875rem",
140
- fontWeight: 400,
141
- lineHeight: "1.25rem",
142
- },
143
- button: {
144
- fontSize: "0.875rem",
145
- fontWeight: 500,
146
- lineHeight: "1.25rem",
147
- textTransform: "uppercase",
148
- },
149
- caption: {
150
- fontSize: "0.75rem",
151
- fontWeight: 400,
152
- lineHeight: "1rem",
153
- },
154
- overline: {
155
- fontSize: "0.75rem",
156
- fontWeight: 400,
157
- lineHeight: "1rem",
158
- textTransform: "uppercase",
159
- },
160
- },
161
- };
162
- const root = ReactDOM.createRoot(document.getElementById("root"));
163
- root.render(
164
- <ThemeCustomization defaultTheme={defaultTheme}>
165
- <BrowserRouter>
166
- <App />
167
- </BrowserRouter>
168
- </ThemeCustomization>
169
- );
170
-
171
- // If you want to start measuring performance in your app, pass a function
172
- // to log results (for example: reportWebVitals(console.log))
173
- // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
174
- reportWebVitals();
175
- ```
15
+ - Install typings (TypeScript projects):
16
+ - `pnpm add -D typescript @types/react @types/react-dom`
17
+ - Configure TypeScript:
18
+ - `tsconfig.json`:
19
+ - `compilerOptions.jsx: "react-jsx"`
20
+ - `compilerOptions.moduleResolution: "bundler"` (or `"node"`)
21
+ - `compilerOptions.skipLibCheck: true` (optional)
22
+ - Import components:
23
+ - `import { MainLayout, ThemeSetting, CustomButton } from '@crystaltech/hsms-shared-ui';`
24
+ - Import types (optional):
25
+ - `import type { ITheme } from '@crystaltech/hsms-shared-ui/dist/theme/defaultTheme';`
26
+ - `import type { ILayoutConfig } from '@crystaltech/hsms-shared-ui/dist/components/layout/MainLayout';`
27
+ - JavaScript projects:
28
+ - Enable type suggestions: add `// @ts-check` to files or use JSDoc typedefs.
29
+ - In VS Code ensure: `javascript.suggest.autoImports` and `typescript.suggest.autoImports` are enabled.
30
+ - Monorepo linking:
31
+ - Use workspace linking or `pnpm link` and restart the TS server if auto-import suggestions don’t show.
176
32
 
177
- The whole project will contain properties of the theme object. This object will laterly come from API.
33
+ ## Quick Start (Theme)
178
34
 
179
- ### Importing Components
35
+ Wrap your app with `ThemeCustomization` and use `ThemeSetting` to edit and persist the theme.
180
36
 
181
37
  ```tsx
182
- import { MainLayout } from "@crystaltech/hsms-shared-ui";
183
- import { Routes, Route } from "react-router-dom";
184
-
185
- import { menuConfig, settingsConfig } from "./dummyData";
38
+ import { ThemeCustomization, ThemeSetting } from "@crystaltech/hsms-shared-ui";
39
+ import { defaultTheme, ITheme } from "@crystaltech/hsms-shared-ui/dist/theme/defaultTheme";
186
40
 
187
- const layoutConfig = {
188
- navbar: {
189
- showHamburgerInMobile: true,
190
- themeToggler: false,
191
- },
192
- sideDrawer: {
193
- menuConfig,
194
- settingsConfig: settingsConfig,
195
- isMinimized: false,
196
- drawerWidth: 240,
197
- },
198
- footerText: settingsConfig.footerText,
199
- };
200
-
201
- function App() {
41
+ export default function App() {
42
+ const [theme, setTheme] = useState<ITheme>(defaultTheme);
43
+ const handleSave = async (next: ITheme) => { setTheme(next); return next; };
202
44
  return (
203
- <MainLayout layoutConfig={layoutConfig}>
204
- <Routes>{/* Your routes with components */}</Routes>
205
- </MainLayout>
45
+ <ThemeCustomization defaultTheme={theme}>
46
+ <ThemeSetting theme={theme} onSave={handleSave} />
47
+ </ThemeCustomization>
206
48
  );
207
49
  }
208
-
209
- export default App;
210
50
  ```
211
51
 
212
- ---
213
-
214
- ## Components
215
-
216
- ### 1. `MainLayout`
217
-
218
- A flexible layout component that includes a Navbar, Sidebar, and Footer.
219
-
220
- **Props:**
52
+ ## Theme Model
221
53
 
222
- | Prop | Type | Description |
223
- | -------------- | ------ | --------------------------------------- |
224
- | `layoutConfig` | Object | Configures navbar, sidebar, and footer. |
54
+ - `ITheme` lives in `src/theme/defaultTheme.ts` and includes `palette` (navbar, sidebar, primary/secondary, background, text, neutral) and `typography`.
55
+ - Use `defaultTheme` as a starting point; pass a new object to re-theme.
225
56
 
226
- ---
57
+ ## Layout
227
58
 
228
- ### 2. `ThemeCustomization`
229
-
230
- A wrapper component that provides theme customization for Material-UI.
231
-
232
- **Usage:**
233
- Wrap your application inside `<ThemeCustomization>` to apply global styles.
59
+ ### MainLayout
60
+ - Props: `title: string`, `layoutConfig: ILayoutConfig`, `logoutHandler?: () => Promise<void>`
61
+ - `ILayoutConfig`:
62
+ - `userManage?: { user: UserInfo; redirectUrl: string }`
63
+ - `navbar: { showHamburgerInMobile: boolean; themeToggler: boolean }`
64
+ - `sideDrawer: { menuConfig: IMenuConfig[]; settingsConfig: ISettingsConfig; isMinimized: boolean; drawerWidth: number }`
65
+ - `footerText: string`
234
66
 
67
+ Example:
235
68
  ```tsx
236
- <ThemeCustomization defaultTheme={}>
237
- {" "}
238
- // pass the DefaultTheme object either manually or from api
239
- <MainLayout layoutConfig={layoutConfig}>{/* Your content here */}</MainLayout>
240
- </ThemeCustomization>
241
- ```
242
-
243
- as we don't have api right now for ThemeCustomization component, we can use the given variable and pass it as a props
244
-
245
- ```ts
246
- const defaultTheme = {
247
- palette: {
248
- mode: "light",
249
- navbar: {
250
- background: "#000000",
251
- foreground: "#000000",
252
- },
253
- sidebar: {
254
- background: "#000000",
255
- foreground: "#000000",
256
- },
257
- primary: {
258
- main: "#ef5350", // You can change this color
259
- },
260
- secondary: {
261
- main: "#f8fafc",
262
- },
263
- background: {
264
- default: "#f8fafc",
265
- paper: "#ffffff",
266
- },
267
- text: {
268
- primary: "#1e293b",
269
- secondary: "#64748b",
270
- },
271
- neutral: {
272
- 100: "#f6f9fc",
273
- 200: "#e9ecef",
274
- 300: "#dee2e6",
275
- 400: "#ced4da",
276
- 500: "#adb5bd",
277
- 600: "#868e96",
278
- 700: "#495057",
279
- 800: "#343a40",
280
- 900: "#212529",
281
- },
282
- },
283
- typography: {
284
- fontFamily: "Roboto",
285
- h1: {
286
- fontSize: "2.5rem",
287
- fontWeight: 700,
288
- lineHeight: "3rem",
289
- },
290
- h2: {
291
- fontSize: "2rem",
292
- fontWeight: 700,
293
- lineHeight: "2.5rem",
294
- },
295
- h3: {
296
- fontSize: "1.75rem",
297
- fontWeight: 600,
298
- lineHeight: "2.25rem",
299
- },
300
- h4: {
301
- fontSize: "1.5rem",
302
- fontWeight: 600,
303
- lineHeight: "2rem",
304
- },
305
- h5: {
306
- fontSize: "1.25rem",
307
- fontWeight: 500,
308
- lineHeight: "1.75rem",
309
- },
310
- h6: {
311
- fontSize: "1rem",
312
- fontWeight: 500,
313
- lineHeight: "1.5rem",
314
- },
315
- subtitle1: {
316
- fontSize: "1.125rem",
317
- fontWeight: 400,
318
- lineHeight: "1.5rem",
319
- },
320
- subtitle2: {
321
- fontSize: "1rem",
322
- fontWeight: 400,
323
- lineHeight: "1.25rem",
324
- },
325
- body1: {
326
- fontSize: "1rem",
327
- fontWeight: 400,
328
- lineHeight: "1.5rem",
329
- },
330
- body2: {
331
- fontSize: "0.875rem",
332
- fontWeight: 400,
333
- lineHeight: "1.25rem",
334
- },
335
- button: {
336
- fontSize: "0.875rem",
337
- fontWeight: 500,
338
- lineHeight: "1.25rem",
339
- textTransform: "uppercase",
340
- },
341
- caption: {
342
- fontSize: "0.75rem",
343
- fontWeight: 400,
344
- lineHeight: "1rem",
345
- },
346
- overline: {
347
- fontSize: "0.75rem",
348
- fontWeight: 400,
349
- lineHeight: "1rem",
350
- textTransform: "uppercase",
351
- },
352
- },
353
- };
354
- ```
355
-
356
- ---
357
-
358
- ## Configuration Options
359
-
360
- The `layoutConfig` object provides the following options:
69
+ import { MainLayout } from "@crystaltech/hsms-shared-ui";
70
+ import { menuConfig, settingsConfig } from "@crystaltech/hsms-shared-ui/dist/components/layout/layoutConfig";
361
71
 
362
- ```js
363
72
  const layoutConfig = {
364
- navbar: {
365
- showHamburgerInMobile: true,
366
- themeToggler: false,
367
- },
368
- sideDrawer: {
369
- menuConfig,
370
- settingsConfig,
371
- isMinimized: false,
372
- drawerWidth: 240,
373
- },
374
- footerText: "Your Footer Text Here",
73
+ navbar: { showHamburgerInMobile: true, themeToggler: false },
74
+ sideDrawer: { menuConfig, settingsConfig, isMinimized: false, drawerWidth: 260 },
75
+ userManage: { user, redirectUrl: "/login" },
76
+ footerText: "© 2025 All rights reserved",
375
77
  };
376
- ```
377
-
378
- ---
379
-
380
- ### 3. `CustomButton`
381
-
382
- CustomButton import examples
383
-
384
- ```ts
385
- <CustomButton variant="outlined">Hello</CustomButton>
386
- <CustomButton variant="text">Hello</CustomButton>
387
- <CustomButton variant="outlined" startIcon={<DeleteIcon />}>
388
- Hello
389
- </CustomButton>
390
- <CustomButton endIcon={<SendIcon />}>Hello</CustomButton>
391
- <CustomButton endIcon={<SendIcon />} loading>
392
- Hello
393
- </CustomButton>
394
78
 
79
+ <MainLayout title="HSMS" layoutConfig={layoutConfig}>
80
+ {/* children */}
81
+ </MainLayout>
395
82
  ```
396
83
 
397
- Button Props Inside CustomButton
84
+ ### MainlayoutWithWrapper
85
+ - Wraps `MainLayout` with a `BrowserRouter`. Use when you don’t already have a router at the app root.
398
86
 
399
- ```ts
400
- interface CustomButtonProps extends ButtonProps {
401
- startIcon?: React.ReactNode;
402
- endIcon?: React.ReactNode;
403
- loading?: boolean;
404
- }
87
+ ### RoutesConfigLayout
88
+ - Props: `pageTitle`, `user`, `authenticated`, `userRoles?`, `clients?`, `loginMethod`, `logoutHandler`, `redirectPath?`, `pages`, `publicRoutes?`, `Layout?`, `mainLayoutConfig`, `NotFoundPage?`.
89
+ - Builds routes from `pages` and protects them via `ProtectedRoute`.
405
90
 
406
- const CustomButton: React.FC<CustomButtonProps> = ({
407
- variant = "contained",
408
- startIcon,
409
- endIcon,
410
- loading = false,
411
- disabled = false,
412
- children,
413
- onClick,
414
- ...props
415
- }) => {
416
- return (
417
- <Button
418
- variant={variant}
419
- startIcon={!loading ? startIcon : null}
420
- endIcon={!loading ? endIcon : null}
421
- disabled={disabled || loading}
422
- onClick={onClick}
423
- {...props}
424
- >
425
- {loading ? <CircularProgress size={24} color="inherit" /> : children}
426
- </Button>
427
- );
428
- };
91
+ Example (static pages map):
92
+ ```tsx
93
+ import { RoutesConfigLayout } from "@crystaltech/hsms-shared-ui";
94
+ import Home from "./pages/index";
95
+ import TablePage from "./pages/table";
96
+
97
+ const pages = {
98
+ "/src/pages/index.tsx": { default: Home },
99
+ "/src/pages/table/index.tsx": { default: TablePage, roles: ["admin"] },
100
+ } satisfies import("@crystaltech/hsms-shared-ui/dist/routes/RoutesConfigLayout").IPages;
101
+
102
+ <RoutesConfigLayout
103
+ pageTitle="HSMS"
104
+ user={user}
105
+ authenticated={isAuthenticated}
106
+ loginMethod={login}
107
+ logoutHandler={logout}
108
+ redirectPath="/login"
109
+ pages={pages}
110
+ publicRoutes={[{ path: "/about", element: <About /> }]}
111
+ mainLayoutConfig={layoutConfig}
112
+ />
429
113
  ```
430
114
 
431
- ### 4. `CustomIconButton`
432
-
433
- `Example`
115
+ `UserInfo` supports `realm_access.roles` and per-client roles (`resource_access[client].roles`). Route-level `roles` override global roles if provided.
434
116
 
435
- ```ts
436
- <CustomIconButton aria-label="delete">
437
- <DeleteIcon />
438
- </CustomIconButton>
439
- <CustomIconButton aria-label="delete" color="primary">
440
- <AlarmIcon />
441
- </CustomIconButton>
442
- <CustomIconButton aria-label="delete" color="secondary">
443
- <AlarmIcon />
444
- </CustomIconButton>
445
- ```
117
+ ## Data Table
446
118
 
447
- `Props`
119
+ ### MultiDynamicTable
120
+ - Props: `tableData: DataStructure`, `rowsPerPage: number`, `page: number`, `handleChangePage`, `handleChangeRowsPerPage`.
121
+ - Features: sticky headers, column visibility toggling, keyword filter (`CustomInput`), pagination.
448
122
 
123
+ Types (inferred):
449
124
  ```ts
450
- import { IconButton, IconButtonProps } from "@mui/material";
451
-
452
- interface CustomIconButtonProps extends IconButtonProps {
453
- children: React.ReactNode;
454
- }
455
-
456
- const CustomIconButton: React.FC<CustomIconButtonProps> = ({
457
- children,
458
- ...props
459
- }) => {
460
- return <IconButton {...props}>{children}</IconButton>;
125
+ export type FieldContentActionsProps = {
126
+ label: string;
127
+ icon?: React.ReactNode;
128
+ onClick: (item: FieldItem) => void;
129
+ variant?: "outlined" | "filled";
461
130
  };
462
-
463
- export default CustomIconButton;
464
- ```
465
-
466
- ### 5. `CustomTabs`
467
-
468
- `USAGE`
469
-
470
- ```ts
471
- const tabsData = [
472
- { label: "Tab One", value: "one" },
473
- { label: "Tab Two", value: "two" },
474
- { label: "Tab Three", value: "three" },
475
- ];
476
-
477
- const [selectedTab, setSelectedTab] = useState(tabsData[0].value);
478
-
479
- const handleTabsChange = (event: React.SyntheticEvent, value: string) => {
480
- setSelectedTab(value);
131
+ export type DatePickerProps = { date: string; onChange?: (item: FieldItem, date: Date) => void };
132
+ export type FieldItem = {
133
+ id: number; // same across rows for a column
134
+ fieldName: string;
135
+ isVisible: boolean;
136
+ fieldType: "text" | "date" | "link" | "label" | "actions" | "date-picker";
137
+ fieldContent: string | DatePickerProps | FieldContentActionsProps[];
138
+ to?: string; // for link
481
139
  };
482
-
483
- <CustomTabs
484
- tabs={tabsData}
485
- value={selectedTab}
486
- onChange={handleTabsChange}
487
- variant="scrollable" //tabs others props can be passed
488
- />;
489
- ```
490
-
491
- ### 6. `CustomTextField`
492
-
493
- you can pass existing Mui Textfield's pass into it too
494
-
495
- ```ts
496
- <CustomTextField
497
- value={name}
498
- onChange={(value) => setName(value)}
499
- label="Name"
500
- />
140
+ export type FieldItemArray = FieldItem[];
141
+ export type DataStructure = FieldItem[][]; // rows => columns
501
142
  ```
502
143
 
503
- ### 7. `CustomAutoComplete`
144
+ Example data:
145
+ ```tsx
146
+ import { MultiDynamicTable } from "@crystaltech/hsms-shared-ui";
147
+ import Visibility from "@mui/icons-material/Visibility";
504
148
 
505
- ```ts
506
- const moviesData = [
507
- { title: "The Shawshank Redemption", year: 1994 },
508
- { title: "The Godfather", year: 1972 },
149
+ const dynamicTableData: DataStructure = [
150
+ [
151
+ { id: 1, fieldName: "Name", isVisible: true, fieldType: "link", fieldContent: "Alice", to: "/users/1" },
152
+ { id: 2, fieldName: "Status", isVisible: true, fieldType: "label", fieldContent: "Active" },
153
+ { id: 3, fieldName: "Joined", isVisible: true, fieldType: "date", fieldContent: "2025-01-01" },
154
+ { id: 4, fieldName: "Actions", isVisible: true, fieldType: "actions", fieldContent: [
155
+ { label: "View", icon: <Visibility />, variant: "outlined", onClick: (item) => console.log(item) },
156
+ ] },
157
+ { id: 5, fieldName: "Reminder", isVisible: true, fieldType: "date-picker", fieldContent: {
158
+ date: "2025-01-05", onChange: (item, date) => console.log(item, date)
159
+ } },
160
+ ],
161
+ [
162
+ { id: 1, fieldName: "Name", isVisible: true, fieldType: "link", fieldContent: "Bob", to: "/users/2" },
163
+ { id: 2, fieldName: "Status", isVisible: true, fieldType: "label", fieldContent: "Inactive" },
164
+ { id: 3, fieldName: "Joined", isVisible: true, fieldType: "date", fieldContent: "2024-12-01" },
165
+ { id: 4, fieldName: "Actions", isVisible: true, fieldType: "actions", fieldContent: [] },
166
+ { id: 5, fieldName: "Reminder", isVisible: true, fieldType: "date-picker", fieldContent: { date: "2025-02-01" } },
167
+ ],
509
168
  ];
510
169
 
511
- <CustomAutoComplete
512
- label="Select multiple movies"
513
- options={moviesData}
514
- getOptionLabel={(option) => option.title}
515
- onChange={(value) => console.log(value)}
516
- multiple
517
- />;
518
-
519
- if you want to see multiple section pass multiple as a prop otherwise no need to pass multiple
520
- ```
521
-
522
- ### 8. `AsyncAutoComplete`
523
-
524
- for passing data at the time of user interaction (api fetch) use this component
525
-
526
- ```ts
527
- <AsyncAutocomplete
528
- label="Select a Movie"
529
- fetchOptions={fetchMovies}
530
- getOptionLabel={(option) => option.title}
531
- isOptionEqualToValue={(option, value) => option.title === value.title}
532
- onChange={(selectedMovie) => console.log("Selected movie:", selectedMovie)}
533
- multiple
170
+ <MultiDynamicTable
171
+ tableData={dynamicTableData}
172
+ page={0}
173
+ rowsPerPage={10}
174
+ handleChangePage={(_, p) => setPage(p)}
175
+ handleChangeRowsPerPage={(e) => setRowsPerPage(parseInt(e.target.value, 10))}
534
176
  />
535
177
  ```
536
178
 
537
- ### 9. `CustomSelect (DropDown)`
179
+ ## UI Components
538
180
 
539
- ```ts
540
- const ageOptions = [
541
- { value: 10, label: "Ten" },
542
- { value: 20, label: "Twenty" },
543
- { value: 30, label: "Thirty" },
544
- ];
181
+ All components are re-exported from `src/index.ts`.
545
182
 
546
- const [age, setAge] = useState("");
183
+ - CustomButton
184
+ - Extends MUI `ButtonProps`; adds `startIcon`, `endIcon`, `loading`.
185
+ - `loading` disables the button and shows a spinner.
547
186
 
548
- const handleAgeChange = (event: SelectChangeEvent) => {
549
- setAge(event.target.value as string);
550
- };
187
+ - CustomIconButton
188
+ - Thin wrapper over MUI `IconButton` that always renders `children` icon.
551
189
 
552
- <CustomSelect
553
- label="Age"
554
- value={age}
555
- options={ageOptions}
556
- onChange={handleAgeChange}
557
- />;
558
- ```
190
+ - CustomInput
191
+ - Props: `startIcon?`, `placeholder?`, `onChange?(value)`, `count?`, `onClear?`.
192
+ - Shows count badge and clear button when not empty.
559
193
 
560
- ### 10. `CustomCheckbox & CustomRadioGroup`
194
+ - CustomCheckbox
195
+ - Props: `checked?`, `onChange?(checked)`, `label?`. Manages internal state.
561
196
 
562
- ```ts
563
- const radioOptions = [
564
- { label: "Female", value: "female" },
565
- { label: "Male", value: "male" },
566
- { label: "Other", value: "other" },
567
- ];
197
+ - CustomRadioGroup
198
+ - Props: `label`, `options: {label,value}[]`, `value`, `onChange(value)`.
568
199
 
569
- const [checked, setChecked] = useState<boolean>(false);
570
- const [selectedValue, setSelectedValue] = useState(radioOptions[0].value);
200
+ - CustomSelect
201
+ - Props: `label`, `value`, `options: { value: string|number; label: string }[]`, `onChange(SelectChangeEvent)`.
571
202
 
572
- <CustomCheckbox checked={checked} label="CheckBox" onChange={setChecked} />
203
+ - CustomSwitch
204
+ - Props: `checked`, `onChange(checked)`.
573
205
 
574
- <CustomRadioGroup
575
- label="Gender"
576
- options={radioOptions}
577
- value={selectedValue}
578
- onChange={(value) => setSelectedValue(value)}
579
- />
206
+ - CustomTabs
207
+ - Props: `tabs: {label,value}[]`, `value`, `onChange(event,value)`, plus `TabsProps`.
580
208
 
581
- ```
209
+ - CustomDatePicker
210
+ - Props: `label`, `value: Dayjs|null`, `onChange(Dayjs|null|undefined)`; clearable.
582
211
 
583
- ### 11. `Selectable List`
212
+ - ControlledDatePicker
213
+ - Props: `label?`, `initialValue?: string`, `onChange?(Dayjs|null)`; manages its own state.
584
214
 
585
- ```ts
586
- const items = [
587
- { label: "Inbox", icon: <InboxIcon /> },
588
- { label: "Drafts", icon: <DraftsIcon /> },
589
- { label: "Trash" }, // No icon
590
- { label: "Spam" }, // No icon
591
- ];
215
+ - CustomColorPicker
216
+ - Props: `initialColor?`, `onChange(color)`; uses `react-color` `SketchPicker`.
592
217
 
593
- <CustomSelectableList items={items} onSelect={(index) => console.log(index)} />;
594
- ```
218
+ - CustomScrollbar
219
+ - Props: `children`, `maxWidth?`, `maxHeight?`; wraps `simplebar-react`.
595
220
 
596
- ### 12. `MultiDynamicTable`
221
+ - AsyncAutocomplete<T>
222
+ - Props: `label`, `multiple?`, `fetchOptions()`, `getOptionLabel`, `isOptionEqualToValue`, `onChange?`.
223
+ - Fetches options on open; shows loading indicator.
224
+ - Example:
225
+ ```tsx
226
+ <AsyncAutocomplete
227
+ label="Users"
228
+ fetchOptions={async () => [{ id:1, name:"Alice" }]}
229
+ getOptionLabel={(o) => o.name}
230
+ isOptionEqualToValue={(o,v) => o.id === v.id}
231
+ onChange={(sel) => console.log(sel)}
232
+ />
233
+ ```
597
234
 
598
- #### Data props data structure
235
+ - CheckboxListWithAvatar
236
+ - Props: `items: { id:number; label:string; avatarSrc? }[]`, `checkedItems?`, `onChange(checkedIds)`.
599
237
 
600
- ```ts
601
- const data: DataStructure = [
602
- //Each Array represents as a row
603
- [
604
- //each object represents a coumn data
605
- {
606
- id: 0,
607
- fieldName: "Title",
608
- fieldType: "text",
609
- fieldContent: "ajgobi",
610
- isDefault: true,
611
- isVisible: true,
612
- },
613
- {
614
- id: 1,
615
- fieldName: "B",
616
- fieldType: "link",
617
- fieldContent: "Click me",
618
- isDefault: true,
619
- isVisible: true,
620
- },
621
- {
622
- id: 2,
623
- fieldName: "Date",
624
- fieldType: "date",
625
- fieldContent: "Feb 20, 2025",
626
- isDefault: false,
627
- isVisible: false,
628
- },
629
- {
630
- id: 3,
631
- fieldName: "D",
632
- fieldType: "link",
633
- fieldContent: "Click me",
634
- isDefault: false,
635
- isVisible: false,
636
- },
637
- {
638
- id: 4,
639
- fieldName: "Actions",
640
- fieldType: "actions",
641
- fieldContent: [
642
- {
643
- label: "Edit",
644
- onClick: (data) => {
645
- console.log(data);
646
- },
647
- icon: <EditLocationAlt />,
648
- variant: "contained",
649
- },
650
- {
651
- label: "Delete",
652
- onClick: (data) => {
653
- console.log(data);
654
- },
655
- icon: <DeleteOutlineSharp />,
656
- variant: "outlined",
657
- },
658
- ],
659
- isDefault: false,
660
- isVisible: false,
661
- },
662
- {
663
- id: 5,
664
- fieldName: "Actions",
665
- fieldType: "actions",
666
- fieldContent: [
667
- {
668
- label: "Update",
669
- onClick: (data) => {
670
- console.log(data);
671
- },
672
- icon: <EditLocationAlt />,
673
- variant: "contained",
674
- },
675
- ],
676
-
677
- isDefault: false,
678
- isVisible: false,
679
- },
680
- {
681
- id: 6,
682
- fieldName: "G",
683
- fieldType: "link",
684
- fieldContent: "Click me",
685
- isDefault: false,
686
- isVisible: false,
687
- },
688
- {
689
- id: 7,
690
- fieldName: "H",
691
- fieldType: "link",
692
- fieldContent:
693
- "Click me fjaf afjasfjasfj aksfjdajfajsfkjakfjaksfjaksfjkasfjaskfjk sdgs sgfsdg asfjaskfjaskjfaksjfaksfjaskfjaksjfkasfjaskfjaksdfjaskfjaskfjaskfjasfjaskfjaskfjaksjfaskjfkasfjj",
694
- isDefault: false,
695
- isVisible: false,
696
- },
697
- {
698
- id: 8,
699
- fieldName: "Date Picker",
700
- fieldType: "date-picker",
701
- fieldContent: {
702
- date: "12-12-2022",
703
- onChange: (parentItem: FieldItem, timeValue: Date) => {
704
- console.log(parentItem, timeValue);
705
- },
706
- },
707
- isDefault: false,
708
- isVisible: false,
709
- },
710
- {
711
- id: 9,
712
- fieldName: "J",
713
- fieldType: "link",
714
- fieldContent: "Click me",
715
- isDefault: false,
716
- isVisible: false,
717
- },
718
- ],
719
- [
720
- {
721
- id: 0,
722
- fieldName: "A",
723
- fieldType: "link",
724
- fieldContent: "hello",
725
- isDefault: true,
726
- isVisible: true,
727
- },
728
- {
729
- id: 1,
730
- fieldName: "B",
731
- fieldType: "link",
732
- fieldContent: "Click me",
733
- isDefault: true,
734
- isVisible: true,
735
- },
736
- {
737
- id: 2,
738
- fieldName: "C",
739
- fieldType: "link",
740
- fieldContent: "Click me",
741
- isDefault: false,
742
- isVisible: false,
743
- },
744
- {
745
- id: 3,
746
- fieldName: "D",
747
- fieldType: "link",
748
- fieldContent: "Click me",
749
- isDefault: false,
750
- isVisible: false,
751
- },
752
- {
753
- id: 4,
754
- fieldName: "E",
755
- fieldType: "link",
756
- fieldContent: "Click me",
757
- isDefault: false,
758
- isVisible: false,
759
- },
760
- {
761
- id: 5,
762
- fieldName: "F",
763
- fieldType: "link",
764
- fieldContent: "Click me",
765
- isDefault: false,
766
- isVisible: false,
767
- },
768
- {
769
- id: 6,
770
- fieldName: "G",
771
- fieldType: "link",
772
- fieldContent: "Click me",
773
- isDefault: false,
774
- isVisible: false,
775
- },
776
- {
777
- id: 7,
778
- fieldName: "H",
779
- fieldType: "link",
780
- fieldContent:
781
- "Click me fjaf afjasfjasfj aksfjdajfajsfkjakfjaksfjaksfjkasfjaskfjk sdgs sgfsdg asfjaskfjaskjfaksjfaksfjaskfjaksjfkasfjaskfjaksdfjaskfjaskfjaskfjasfjaskfjaskfjaksjfaskjfkasfjj",
782
- isDefault: false,
783
- isVisible: false,
784
- },
785
- {
786
- id: 8,
787
- fieldName: "I",
788
- fieldType: "link",
789
- fieldContent: "Click me",
790
- isDefault: false,
791
- isVisible: false,
792
- },
793
- {
794
- id: 9,
795
- fieldName: "J",
796
- fieldType: "link",
797
- fieldContent: "Click me",
798
- isDefault: false,
799
- isVisible: false,
800
- },
801
- ],
802
- [
803
- {
804
- id: 0,
805
- fieldName: "A",
806
- fieldType: "link",
807
- fieldContent: "gkkk",
808
- isDefault: true,
809
- isVisible: true,
810
- to: "/habi-jabi",
811
- },
812
- {
813
- id: 1,
814
- fieldName: "B",
815
- fieldType: "label",
816
- fieldContent: "jam me",
817
- isDefault: true,
818
- isVisible: true,
819
- },
820
- {
821
- id: 2,
822
- fieldName: "C",
823
- fieldType: "link",
824
- fieldContent: "Click me",
825
- isDefault: false,
826
- isVisible: false,
827
- },
828
- {
829
- id: 3,
830
- fieldName: "D",
831
- fieldType: "link",
832
- fieldContent: "Click me",
833
- isDefault: false,
834
- isVisible: false,
835
- },
836
- {
837
- id: 4,
838
- fieldName: "E",
839
- fieldType: "link",
840
- fieldContent: "Click me",
841
- isDefault: false,
842
- isVisible: false,
843
- },
844
- {
845
- id: 5,
846
- fieldName: "F",
847
- fieldType: "link",
848
- fieldContent: "Click me",
849
- isDefault: false,
850
- isVisible: false,
851
- },
852
- {
853
- id: 6,
854
- fieldName: "G",
855
- fieldType: "link",
856
- fieldContent: "Click me",
857
- isDefault: false,
858
- isVisible: false,
859
- },
860
- {
861
- id: 7,
862
- fieldName: "H",
863
- fieldType: "link",
864
- fieldContent:
865
- "Click me fjaf afjasfjasfj aksfjdajfajsfkjakfjaksfjaksfjkasfjaskfjk sdgs sgfsdg asfjaskfjaskjfaksjfaksfjaskfjaksjfkasfjaskfjaksdfjaskfjaskfjaskfjasfjaskfjaskfjaksjfaskjfkasfjj",
866
- isDefault: false,
867
- isVisible: false,
868
- },
869
- {
870
- id: 8,
871
- fieldName: "I",
872
- fieldType: "link",
873
- fieldContent: "Click me",
874
- isDefault: false,
875
- isVisible: false,
876
- },
877
- {
878
- id: 9,
879
- fieldName: "J",
880
- fieldType: "link",
881
- fieldContent: "Click me",
882
- isDefault: false,
883
- isVisible: false,
884
- },
885
- ],
886
- ];
887
- ```
888
- usage of the MultiDynamicTable
238
+ - CustomSelectableList
239
+ - Props: `items: { label:string; icon? }[]`, `onSelect?(index)`; supports selection and a divider after the second item.
889
240
 
890
- ```ts
891
- import { useState } from "react";
892
- import MultiDynamicTable from "./MultiDynamicTable/MultiDynamicTable";
893
- import { dynamicTableData } from "./MultiDynamicTable/dynamicTableData";
241
+ - Loader
242
+ - Props: `size?`, `text?`, `fullScreen?`.
243
+ - Shows animated spinner with module logo; optionally full-screen overlay.
894
244
 
895
- const Tables = () => {
896
- const [page, setPage] = useState(0);
897
- const [rowsPerPage, setRowsPerPage] = useState(10);
245
+ ## Exports
898
246
 
899
- const handleChangePage = (_event: unknown, newPage: number) => {
900
- setPage(newPage);
901
- };
902
-
903
- const handleChangeRowsPerPage = (
904
- event: React.ChangeEvent<HTMLInputElement>
905
- ) => {
906
- setRowsPerPage(parseInt(event.target.value, 10));
907
- setPage(0);
908
- };
909
-
910
- return (
911
- <>
912
- <MultiDynamicTable
913
- tableData={dynamicTableData}
914
- page={page}
915
- rowsPerPage={rowsPerPage}
916
- handleChangePage={handleChangePage}
917
- handleChangeRowsPerPage={handleChangeRowsPerPage}
918
- />
919
- </>
920
- );
921
- };
922
-
923
- export default Tables;
247
+ ```ts
248
+ import {
249
+ ThemeSetting,
250
+ ThemeCustomization,
251
+ MainLayout,
252
+ MainlayoutWithWrapper,
253
+ RoutesConfigLayout,
254
+ MultiDynamicTable,
255
+ ProfilePages,
256
+ // UI primitives
257
+ AsyncAutocomplete,
258
+ CheckboxListWithAvatar,
259
+ ControlledDatePicker,
260
+ CustomButton,
261
+ CustomCheckbox,
262
+ CustomColorPicker,
263
+ CustomDatePicker,
264
+ CustomIconButton,
265
+ CustomInput,
266
+ CustomRadioGroup,
267
+ CustomScrollbar,
268
+ CustomSelect,
269
+ CustomSelectableList,
270
+ CustomSwitch,
271
+ CustomTabs,
272
+ CustomTextField,
273
+ Loader,
274
+ } from "@crystaltech/hsms-shared-ui";
924
275
  ```
925
- ### 13. `CustomTabs`
926
-
927
- ### 14. `CustomTabs`
928
-
929
- ### 15. `CustomTabs`
930
-
931
- ### 16. `CustomTabs`
932
-
933
- ### 17. `CustomTabs`
934
-
935
- ### 18. `CustomTabs`
936
-
937
- ## License
938
-
939
- This package is licensed under the **MIT License**.
940
-
941
- ---
942
276
 
943
- ## Author
277
+ ## Notes
944
278
 
945
- Developed by **Anwar Hossain**.
946
- For questions or contributions, reach out via **GitHub**.
279
+ - `RoutesConfigLayout` uses `ProtectedRoute` to enforce `roles` and `clients`; route-level `roles` override global ones.
280
+ - Column visibility toggling in `MultiDynamicTable` relies on shared `id` values across rows for a given column.
281
+ - Ensure peer versions of MUI and React match your app.
282
+ - Refer to `src/theme/defaultTheme.ts` for the complete `ITheme` shape.