izen-react-starter 1.1.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1233 -25
- package/dist/{MIMHJGAX-CX0R0gR-.js → MIMHJGAX-DM3PPc6i.js} +1 -1
- package/dist/{Q7LWSL4U-DisRfAmB.js → Q7LWSL4U-Dp8mxbtM.js} +2 -2
- package/dist/{VLTTJS3N-hByn9hgE.js → VLTTJS3N-Dfn6o7Pv.js} +2 -2
- package/dist/components/charts/ChartAreaInteractive.d.ts +2 -0
- package/dist/components/charts/ChartAreaInteractive.d.ts.map +1 -0
- package/dist/components/charts/index.d.ts +2 -0
- package/dist/components/charts/index.d.ts.map +1 -0
- package/dist/components/common/Header.d.ts +13 -0
- package/dist/components/common/Header.d.ts.map +1 -0
- package/dist/components/common/Heading.d.ts +7 -0
- package/dist/components/common/Heading.d.ts.map +1 -0
- package/dist/components/common/PageHead.d.ts +5 -0
- package/dist/components/common/PageHead.d.ts.map +1 -0
- package/dist/components/common/ThemeToggle.d.ts +5 -0
- package/dist/components/common/ThemeToggle.d.ts.map +1 -0
- package/dist/components/common/index.d.ts +5 -0
- package/dist/components/common/index.d.ts.map +1 -0
- package/dist/components/date-picker/DatePickerWithRange.d.ts +8 -0
- package/dist/components/date-picker/DatePickerWithRange.d.ts.map +1 -0
- package/dist/components/date-picker/DateRangeFilter.d.ts +7 -0
- package/dist/components/date-picker/DateRangeFilter.d.ts.map +1 -0
- package/dist/components/date-picker/index.d.ts +3 -0
- package/dist/components/date-picker/index.d.ts.map +1 -0
- package/dist/components/form/CheckboxGroup.d.ts +15 -0
- package/dist/components/form/CheckboxGroup.d.ts.map +1 -0
- package/dist/components/form/ComboboxSelect.d.ts +24 -0
- package/dist/components/form/ComboboxSelect.d.ts.map +1 -0
- package/dist/components/form/DatePicker.d.ts +14 -0
- package/dist/components/form/DatePicker.d.ts.map +1 -0
- package/dist/components/form/FileUploadButton.d.ts +19 -0
- package/dist/components/form/FileUploadButton.d.ts.map +1 -0
- package/dist/components/form/FormButtons.d.ts +19 -0
- package/dist/components/form/FormButtons.d.ts.map +1 -0
- package/dist/components/form/FormInput.d.ts +15 -0
- package/dist/components/form/FormInput.d.ts.map +1 -0
- package/dist/components/form/FormLayout.d.ts +21 -0
- package/dist/components/form/FormLayout.d.ts.map +1 -0
- package/dist/components/form/RadioGroup.d.ts +14 -0
- package/dist/components/form/RadioGroup.d.ts.map +1 -0
- package/dist/components/form/Select.d.ts +21 -0
- package/dist/components/form/Select.d.ts.map +1 -0
- package/dist/components/form/TextInput.d.ts +13 -0
- package/dist/components/form/TextInput.d.ts.map +1 -0
- package/dist/components/form/TimeInput.d.ts +13 -0
- package/dist/components/form/TimeInput.d.ts.map +1 -0
- package/dist/components/form/index.d.ts +23 -0
- package/dist/components/form/index.d.ts.map +1 -0
- package/dist/components/layout/AppSidebar.d.ts +24 -0
- package/dist/components/layout/AppSidebar.d.ts.map +1 -0
- package/dist/components/layout/DashboardLayout.d.ts +12 -0
- package/dist/components/layout/DashboardLayout.d.ts.map +1 -0
- package/dist/components/layout/NavDocuments.d.ts +11 -0
- package/dist/components/layout/NavDocuments.d.ts.map +1 -0
- package/dist/components/layout/NavMain.d.ts +24 -0
- package/dist/components/layout/NavMain.d.ts.map +1 -0
- package/dist/components/layout/NavSecondary.d.ts +14 -0
- package/dist/components/layout/NavSecondary.d.ts.map +1 -0
- package/dist/components/layout/NavUser.d.ts +18 -0
- package/dist/components/layout/NavUser.d.ts.map +1 -0
- package/dist/components/layout/SiteHeader.d.ts +7 -0
- package/dist/components/layout/SiteHeader.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +8 -0
- package/dist/components/layout/index.d.ts.map +1 -0
- package/dist/components/modals/AlertModal.d.ts +10 -0
- package/dist/components/modals/AlertModal.d.ts.map +1 -0
- package/dist/components/modals/DeleteDialog.d.ts +7 -0
- package/dist/components/modals/DeleteDialog.d.ts.map +1 -0
- package/dist/components/modals/PopupModal.d.ts +16 -0
- package/dist/components/modals/PopupModal.d.ts.map +1 -0
- package/dist/components/modals/index.d.ts +4 -0
- package/dist/components/modals/index.d.ts.map +1 -0
- package/dist/components/navigation/DashboardNav.d.ts +18 -0
- package/dist/components/navigation/DashboardNav.d.ts.map +1 -0
- package/dist/components/navigation/MobileSidebar.d.ts +12 -0
- package/dist/components/navigation/MobileSidebar.d.ts.map +1 -0
- package/dist/components/navigation/Sidebar.d.ts +8 -0
- package/dist/components/navigation/Sidebar.d.ts.map +1 -0
- package/dist/components/navigation/UserNav.d.ts +10 -0
- package/dist/components/navigation/UserNav.d.ts.map +1 -0
- package/dist/components/navigation/index.d.ts +5 -0
- package/dist/components/navigation/index.d.ts.map +1 -0
- package/dist/components/overlay/Overlay.d.ts +5 -0
- package/dist/components/overlay/Overlay.d.ts.map +1 -0
- package/dist/components/overlay/index.d.ts +2 -0
- package/dist/components/overlay/index.d.ts.map +1 -0
- package/dist/components/search/TableSearchInput.d.ts +5 -0
- package/dist/components/search/TableSearchInput.d.ts.map +1 -0
- package/dist/components/search/index.d.ts +2 -0
- package/dist/components/search/index.d.ts.map +1 -0
- package/dist/components/table/DataTable.d.ts +14 -0
- package/dist/components/table/DataTable.d.ts.map +1 -0
- package/dist/components/table/DataTableSkeleton.d.ts +9 -0
- package/dist/components/table/DataTableSkeleton.d.ts.map +1 -0
- package/dist/components/table/Pagination.d.ts +7 -0
- package/dist/components/table/Pagination.d.ts.map +1 -0
- package/dist/components/table/PaginationSection.d.ts +8 -0
- package/dist/components/table/PaginationSection.d.ts.map +1 -0
- package/dist/components/table/ServerDataTable.d.ts +10 -0
- package/dist/components/table/ServerDataTable.d.ts.map +1 -0
- package/dist/components/table/SpecialDaysTable.d.ts +26 -0
- package/dist/components/table/SpecialDaysTable.d.ts.map +1 -0
- package/dist/components/table/Table.d.ts +28 -0
- package/dist/components/table/Table.d.ts.map +1 -0
- package/dist/components/table/TableActions.d.ts +19 -0
- package/dist/components/table/TableActions.d.ts.map +1 -0
- package/dist/components/table/TableHeader.d.ts +6 -0
- package/dist/components/table/TableHeader.d.ts.map +1 -0
- package/dist/components/table/index.d.ts +15 -0
- package/dist/components/table/index.d.ts.map +1 -0
- package/dist/components/tabs/GenericTab.d.ts +7 -0
- package/dist/components/tabs/GenericTab.d.ts.map +1 -0
- package/dist/components/tabs/index.d.ts +2 -0
- package/dist/components/tabs/index.d.ts.map +1 -0
- package/dist/components/ui/index.d.ts +0 -2
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/constants/urls.d.ts +2 -0
- package/dist/constants/urls.d.ts.map +1 -0
- package/dist/index-Dnn3beiE.js +62337 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/react-starter.js +326 -298
- package/dist/react-starter.umd.cjs +373 -199
- package/dist/services/apiService.d.ts +3 -4
- package/dist/services/apiService.d.ts.map +1 -1
- package/package.json +10 -2
- package/dist/index-BlTeKmzn.js +0 -45406
package/README.md
CHANGED
|
@@ -211,6 +211,269 @@ function MyApp() {
|
|
|
211
211
|
}
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
+
### Generic Table
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
import { GenericTable, ActionType } from 'izen-react-starter';
|
|
218
|
+
|
|
219
|
+
type SpecialDay = {
|
|
220
|
+
id: number;
|
|
221
|
+
name: string;
|
|
222
|
+
start: string;
|
|
223
|
+
end: string;
|
|
224
|
+
floatingHoliday?: boolean;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
function SpecialDaysView({ rows, pagination, onAction }: { rows: SpecialDay[]; pagination: any; onAction: (row: SpecialDay, action: ActionType) => void }) {
|
|
228
|
+
return (
|
|
229
|
+
<GenericTable
|
|
230
|
+
rows={rows}
|
|
231
|
+
columns={[
|
|
232
|
+
{ header: 'Name', render: (row) => `${row.name}${row.floatingHoliday ? ' (Floating Holiday)' : ''}` },
|
|
233
|
+
{ header: 'Start', render: (row) => new Date(row.start).toLocaleDateString() },
|
|
234
|
+
{ header: 'End', render: (row) => new Date(row.end).toLocaleDateString() },
|
|
235
|
+
]}
|
|
236
|
+
getRowId={(row) => row.id}
|
|
237
|
+
onAction={(row, action) => onAction(row, action)}
|
|
238
|
+
getActionLink={(row) => `/preferences/special-days/${row.id}`}
|
|
239
|
+
pagination={pagination}
|
|
240
|
+
emptyText="No special days found."
|
|
241
|
+
/>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Table Utilities
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
import { TableActions, Pagination, TableHeader, GenericTab } from 'izen-react-starter';
|
|
250
|
+
|
|
251
|
+
// Actions cell example
|
|
252
|
+
<TableActions
|
|
253
|
+
link="/resource/123"
|
|
254
|
+
Item={{ id: 123 }}
|
|
255
|
+
handleAction={(row, action) => console.log(row, action)}
|
|
256
|
+
/>;
|
|
257
|
+
|
|
258
|
+
// Pagination example (expects meta/links/url shape)
|
|
259
|
+
<Pagination
|
|
260
|
+
meta={{ currentPage: 1, itemsPerPage: 10, totalItems: 42 }}
|
|
261
|
+
links={{ prev: null, next: '?page=2', first: '?page=1', last: '?page=5' }}
|
|
262
|
+
url="/api/resources"
|
|
263
|
+
/>;
|
|
264
|
+
|
|
265
|
+
// Table header helper (basic)
|
|
266
|
+
<table>
|
|
267
|
+
<TableHeader headers={["Name", "Email"]} />
|
|
268
|
+
{/* ...rows */}
|
|
269
|
+
</table>;
|
|
270
|
+
|
|
271
|
+
// GenericTab helper to wrap tab content and reuse TableActions
|
|
272
|
+
<GenericTab
|
|
273
|
+
key="notes"
|
|
274
|
+
data={[{ id: 1, text: 'Note text' }]}
|
|
275
|
+
handleAction={(item, action) => console.log(item, action)}
|
|
276
|
+
/>;
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Layout System
|
|
280
|
+
|
|
281
|
+
The library provides a complete layout system with sidebar, header, and navigation components.
|
|
282
|
+
|
|
283
|
+
#### DashboardLayout (Complete Layout)
|
|
284
|
+
|
|
285
|
+
A full dashboard layout that combines sidebar and header:
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
import { DashboardLayout, Overlay } from 'izen-react-starter';
|
|
289
|
+
import { Home, Settings, FileText, HelpCircle } from 'lucide-react';
|
|
290
|
+
|
|
291
|
+
function App() {
|
|
292
|
+
return (
|
|
293
|
+
<DashboardLayout
|
|
294
|
+
sidebarProps={{
|
|
295
|
+
brandName: "My App",
|
|
296
|
+
brandIcon: Home,
|
|
297
|
+
navMain: [
|
|
298
|
+
{ title: "Dashboard", url: "/dashboard", icon: Home },
|
|
299
|
+
{
|
|
300
|
+
title: "Settings",
|
|
301
|
+
url: "#",
|
|
302
|
+
icon: Settings,
|
|
303
|
+
items: [
|
|
304
|
+
{ title: "Profile", url: "/settings/profile" },
|
|
305
|
+
{ title: "Security", url: "/settings/security" },
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
navSecondary: [
|
|
310
|
+
{ title: "Help", url: "/help", icon: HelpCircle },
|
|
311
|
+
{ title: "Docs", url: "/docs", icon: FileText, target: "_blank" },
|
|
312
|
+
],
|
|
313
|
+
user: {
|
|
314
|
+
name: "John Doe",
|
|
315
|
+
email: "john@example.com",
|
|
316
|
+
avatar: "/avatar.jpg",
|
|
317
|
+
},
|
|
318
|
+
onLogout: () => console.log("Logout"),
|
|
319
|
+
userMenuItems: [
|
|
320
|
+
{ label: "Profile", onClick: () => {} },
|
|
321
|
+
{ label: "Settings", onClick: () => {} },
|
|
322
|
+
],
|
|
323
|
+
}}
|
|
324
|
+
headerProps={{
|
|
325
|
+
pageTitles: {
|
|
326
|
+
"/dashboard": "Dashboard",
|
|
327
|
+
"/settings": "Settings",
|
|
328
|
+
},
|
|
329
|
+
defaultTitle: "My App",
|
|
330
|
+
}}
|
|
331
|
+
defaultOpen={true}
|
|
332
|
+
showOverlay={false}
|
|
333
|
+
overlayComponent={<Overlay show={false} />}
|
|
334
|
+
>
|
|
335
|
+
{/* Your page content */}
|
|
336
|
+
</DashboardLayout>
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
#### AppSidebar (Sidebar Component)
|
|
342
|
+
|
|
343
|
+
Collapsible sidebar with navigation menu:
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
import { AppSidebar } from 'izen-react-starter';
|
|
347
|
+
import { Home, Settings, Users } from 'lucide-react';
|
|
348
|
+
|
|
349
|
+
<AppSidebar
|
|
350
|
+
brandName="Acme Inc"
|
|
351
|
+
brandIcon={Home}
|
|
352
|
+
navMain={[
|
|
353
|
+
{ title: "Home", url: "/", icon: Home, badge: "3" },
|
|
354
|
+
{
|
|
355
|
+
title: "Settings",
|
|
356
|
+
url: "#",
|
|
357
|
+
icon: Settings,
|
|
358
|
+
items: [
|
|
359
|
+
{ title: "Profile", url: "/settings/profile" },
|
|
360
|
+
{ title: "Security", url: "/settings/security" },
|
|
361
|
+
]
|
|
362
|
+
},
|
|
363
|
+
]}
|
|
364
|
+
navSecondary={[
|
|
365
|
+
{ title: "Help", url: "/help", icon: HelpCircle },
|
|
366
|
+
]}
|
|
367
|
+
user={{
|
|
368
|
+
name: "Jane Doe",
|
|
369
|
+
email: "jane@example.com",
|
|
370
|
+
avatar: "/avatar.jpg",
|
|
371
|
+
}}
|
|
372
|
+
onLogout={() => console.log("Logging out")}
|
|
373
|
+
userMenuItems={[
|
|
374
|
+
{ label: "Account", onClick: () => {} },
|
|
375
|
+
]}
|
|
376
|
+
/>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
#### Navigation Components
|
|
380
|
+
|
|
381
|
+
**NavMain** - Main navigation menu with collapsible groups:
|
|
382
|
+
|
|
383
|
+
```tsx
|
|
384
|
+
import { NavMain } from 'izen-react-starter';
|
|
385
|
+
import { Home, Settings } from 'lucide-react';
|
|
386
|
+
|
|
387
|
+
<NavMain
|
|
388
|
+
items={[
|
|
389
|
+
{ title: "Dashboard", url: "/dashboard", icon: Home },
|
|
390
|
+
{
|
|
391
|
+
title: "Settings",
|
|
392
|
+
icon: Settings,
|
|
393
|
+
items: [
|
|
394
|
+
{ title: "Profile", url: "/settings/profile" },
|
|
395
|
+
{ title: "Security", url: "/settings/security", badge: "2" },
|
|
396
|
+
]
|
|
397
|
+
},
|
|
398
|
+
]}
|
|
399
|
+
/>
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
**NavSecondary** - Secondary navigation links:
|
|
403
|
+
|
|
404
|
+
```tsx
|
|
405
|
+
import { NavSecondary } from 'izen-react-starter';
|
|
406
|
+
import { HelpCircle, FileText } from 'lucide-react';
|
|
407
|
+
|
|
408
|
+
<NavSecondary
|
|
409
|
+
items={[
|
|
410
|
+
{ title: "Help", url: "/help", icon: HelpCircle },
|
|
411
|
+
{ title: "Documentation", url: "https://docs.example.com", icon: FileText, target: "_blank" },
|
|
412
|
+
]}
|
|
413
|
+
/>
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
**NavUser** - User menu dropdown:
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
import { NavUser } from 'izen-react-starter';
|
|
420
|
+
|
|
421
|
+
<NavUser
|
|
422
|
+
user={{
|
|
423
|
+
name: "John Doe",
|
|
424
|
+
email: "john@example.com",
|
|
425
|
+
avatar: "/avatar.jpg",
|
|
426
|
+
}}
|
|
427
|
+
onLogout={() => console.log("Logout")}
|
|
428
|
+
menuItems={[
|
|
429
|
+
{ label: "Profile", onClick: () => {} },
|
|
430
|
+
{ label: "Settings", onClick: () => {} },
|
|
431
|
+
]}
|
|
432
|
+
/>
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
**NavDocuments** - Document list with actions:
|
|
436
|
+
|
|
437
|
+
```tsx
|
|
438
|
+
import { NavDocuments } from 'izen-react-starter';
|
|
439
|
+
import { FileText } from 'lucide-react';
|
|
440
|
+
|
|
441
|
+
<NavDocuments
|
|
442
|
+
items={[
|
|
443
|
+
{ name: "Report Q1", url: "/docs/q1", icon: FileText },
|
|
444
|
+
{ name: "Budget 2024", url: "/docs/budget", icon: FileText },
|
|
445
|
+
]}
|
|
446
|
+
/>
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
#### SiteHeader
|
|
450
|
+
|
|
451
|
+
Header with page title and sidebar trigger:
|
|
452
|
+
|
|
453
|
+
```tsx
|
|
454
|
+
import { SiteHeader } from 'izen-react-starter';
|
|
455
|
+
|
|
456
|
+
<SiteHeader
|
|
457
|
+
pageTitles={{
|
|
458
|
+
"/dashboard": "Dashboard",
|
|
459
|
+
"/settings": "Settings",
|
|
460
|
+
}}
|
|
461
|
+
defaultTitle="My App"
|
|
462
|
+
/>
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Charts and Overlay
|
|
466
|
+
|
|
467
|
+
```tsx
|
|
468
|
+
import { ChartAreaInteractive, Overlay } from 'izen-react-starter';
|
|
469
|
+
|
|
470
|
+
// Chart with timeframe controls
|
|
471
|
+
<ChartAreaInteractive />;
|
|
472
|
+
|
|
473
|
+
// Simple overlay toggled by boolean
|
|
474
|
+
<Overlay show={isLoading} />;
|
|
475
|
+
```
|
|
476
|
+
|
|
214
477
|
### Layout Context
|
|
215
478
|
|
|
216
479
|
```tsx
|
|
@@ -243,36 +506,56 @@ function MyComponent() {
|
|
|
243
506
|
### API Service
|
|
244
507
|
|
|
245
508
|
```tsx
|
|
246
|
-
import {
|
|
509
|
+
import React, { useEffect } from 'react';
|
|
510
|
+
import { useApiService } from 'izen-react-starter';
|
|
247
511
|
|
|
248
|
-
//
|
|
249
|
-
|
|
512
|
+
// Set the base URL once, typically in your app root
|
|
513
|
+
export function App() {
|
|
514
|
+
const apiService = useApiService();
|
|
250
515
|
|
|
251
|
-
|
|
252
|
-
apiService.
|
|
516
|
+
useEffect(() => {
|
|
517
|
+
apiService.setBaseURL('https://api.example.com');
|
|
518
|
+
}, [apiService]);
|
|
253
519
|
|
|
254
|
-
|
|
255
|
-
async function fetchData() {
|
|
256
|
-
try {
|
|
257
|
-
const data = await apiService.get('/users');
|
|
258
|
-
console.log(data);
|
|
259
|
-
} catch (error) {
|
|
260
|
-
console.error('Error fetching data:', error);
|
|
261
|
-
}
|
|
520
|
+
return <UsersPage />;
|
|
262
521
|
}
|
|
263
522
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
523
|
+
// Usage in a page/component
|
|
524
|
+
export function UsersPage() {
|
|
525
|
+
const apiService = useApiService();
|
|
526
|
+
|
|
527
|
+
// Fetch users example
|
|
528
|
+
const fetchUsers = async () => {
|
|
529
|
+
try {
|
|
530
|
+
const users = await apiService.get('/users');
|
|
531
|
+
console.log(users);
|
|
532
|
+
} catch (error) {
|
|
533
|
+
console.error('Error fetching users:', error);
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
// Create user example
|
|
538
|
+
const createUser = async () => {
|
|
539
|
+
try {
|
|
540
|
+
const response = await apiService.post('/users', {
|
|
541
|
+
name: 'John Doe',
|
|
542
|
+
email: 'john@example.com',
|
|
543
|
+
});
|
|
544
|
+
console.log(response);
|
|
545
|
+
} catch (error) {
|
|
546
|
+
console.error('Error creating user:', error);
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
return (
|
|
551
|
+
<div>
|
|
552
|
+
<button onClick={fetchUsers}>Fetch Users</button>
|
|
553
|
+
<button onClick={createUser}>Create User</button>
|
|
554
|
+
</div>
|
|
555
|
+
);
|
|
274
556
|
}
|
|
275
557
|
```
|
|
558
|
+
> **Tip:** You only need to set the baseURL once per app session. After that, all requests will use this base URL automatically. The token will always be injected from AuthProvider for every request.
|
|
276
559
|
|
|
277
560
|
### Role-Based Access Control (RBAC)
|
|
278
561
|
|
|
@@ -565,8 +848,933 @@ Methods:
|
|
|
565
848
|
- `patch<T>(url, data?, config?)`: Promise<T>
|
|
566
849
|
- `delete<T>(url, config?)`: Promise<T>
|
|
567
850
|
- `setBaseURL(baseURL)`: void
|
|
568
|
-
|
|
569
|
-
|
|
851
|
+
|
|
852
|
+
> **Note:** The API service now always uses the latest token from the AuthProvider. Use the `useApiService` hook in your components. You no longer need to call `setAuthToken` or `removeAuthToken` manually.
|
|
853
|
+
### Organized Component Library
|
|
854
|
+
|
|
855
|
+
The library includes a comprehensive set of components organized by functionality:
|
|
856
|
+
|
|
857
|
+
#### Modals
|
|
858
|
+
|
|
859
|
+
Reusable modal dialogs for common user interactions.
|
|
860
|
+
|
|
861
|
+
```tsx
|
|
862
|
+
import { AlertModal, DeleteDialog, PopupModal } from 'izen-react-starter';
|
|
863
|
+
|
|
864
|
+
// Alert Modal - Generic confirmation dialog
|
|
865
|
+
function MyComponent() {
|
|
866
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
867
|
+
|
|
868
|
+
return (
|
|
869
|
+
<AlertModal
|
|
870
|
+
isOpen={isOpen}
|
|
871
|
+
onClose={() => setIsOpen(false)}
|
|
872
|
+
onConfirm={handleConfirm}
|
|
873
|
+
loading={loading}
|
|
874
|
+
title="Are you sure?"
|
|
875
|
+
description="This action cannot be undone."
|
|
876
|
+
/>
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Delete Dialog - Specific delete confirmation
|
|
881
|
+
<DeleteDialog
|
|
882
|
+
openDeleteDialog={isDeleteOpen}
|
|
883
|
+
setIsOpenDeleteDialog={setIsDeleteOpen}
|
|
884
|
+
onDelete={handleDelete}
|
|
885
|
+
/>
|
|
886
|
+
|
|
887
|
+
// Popup Modal - Complex modal with scroll area and optional "Add New" button
|
|
888
|
+
<PopupModal
|
|
889
|
+
isOpen={modalName === 'create'}
|
|
890
|
+
setIsOpen={setModalName}
|
|
891
|
+
modalName="create"
|
|
892
|
+
title="Add New Item"
|
|
893
|
+
showAddBtn={true}
|
|
894
|
+
isAllowedCreate={true}
|
|
895
|
+
renderModal={(onClose) => (
|
|
896
|
+
<YourForm
|
|
897
|
+
onSubmit={(data) => {
|
|
898
|
+
handleSubmit(data);
|
|
899
|
+
onClose();
|
|
900
|
+
}}
|
|
901
|
+
/>
|
|
902
|
+
)}
|
|
903
|
+
extraBtns={() => (
|
|
904
|
+
<Button onClick={handleExtraAction}>Custom Action</Button>
|
|
905
|
+
)}
|
|
906
|
+
/>
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
#### Navigation Components
|
|
910
|
+
|
|
911
|
+
Complete navigation system with responsive design.
|
|
912
|
+
|
|
913
|
+
```tsx
|
|
914
|
+
import { UserNav, DashboardNav, Sidebar, MobileSidebar, Header } from 'izen-react-starter';
|
|
915
|
+
|
|
916
|
+
// User navigation dropdown with avatar
|
|
917
|
+
<UserNav
|
|
918
|
+
user={{
|
|
919
|
+
name: 'John Doe',
|
|
920
|
+
email: 'john@example.com',
|
|
921
|
+
avatar: 'https://example.com/avatar.jpg'
|
|
922
|
+
}}
|
|
923
|
+
onLogout={handleLogout}
|
|
924
|
+
/>
|
|
925
|
+
|
|
926
|
+
// Dashboard navigation list with icons
|
|
927
|
+
const navItems = [
|
|
928
|
+
{ href: '/dashboard', label: 'Dashboard', icon: 'home', isShow: true },
|
|
929
|
+
{ href: '/settings', label: 'Settings', icon: 'settings', isShow: true },
|
|
930
|
+
{ href: '/users', label: 'Users', icon: 'users', isShow: false } // Hidden item
|
|
931
|
+
];
|
|
932
|
+
|
|
933
|
+
<DashboardNav
|
|
934
|
+
items={navItems}
|
|
935
|
+
setOpen={setIsSidebarOpen} // Optional - for mobile
|
|
936
|
+
/>
|
|
937
|
+
|
|
938
|
+
// Desktop sidebar with branding
|
|
939
|
+
<Sidebar
|
|
940
|
+
navItems={navItems}
|
|
941
|
+
logoText="My App"
|
|
942
|
+
logoHref="/"
|
|
943
|
+
/>
|
|
944
|
+
|
|
945
|
+
// Mobile sidebar with sheet overlay
|
|
946
|
+
<MobileSidebar
|
|
947
|
+
sidebarOpen={isMobileOpen}
|
|
948
|
+
setSidebarOpen={setIsMobileOpen}
|
|
949
|
+
navItems={navItems}
|
|
950
|
+
logoText="My App"
|
|
951
|
+
logoHref="/"
|
|
952
|
+
/>
|
|
953
|
+
|
|
954
|
+
// Complete header with user nav and theme toggle
|
|
955
|
+
<Header
|
|
956
|
+
title="My Application"
|
|
957
|
+
user={{ name: 'John Doe', email: 'john@example.com' }}
|
|
958
|
+
onLogout={handleLogout}
|
|
959
|
+
setTheme={setTheme}
|
|
960
|
+
extraContent={
|
|
961
|
+
<Button>Custom Button</Button>
|
|
962
|
+
}
|
|
963
|
+
/>
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
#### Date Picker Components
|
|
967
|
+
|
|
968
|
+
Date selection and filtering with URL parameter integration.
|
|
969
|
+
|
|
970
|
+
```tsx
|
|
971
|
+
import { DatePickerWithRange, DateRangeFilter } from 'izen-react-starter';
|
|
972
|
+
|
|
973
|
+
// Date range picker with calendar (3 months view)
|
|
974
|
+
<DatePickerWithRange
|
|
975
|
+
startDate={new Date('2024-01-01')}
|
|
976
|
+
endDate={new Date('2024-01-31')}
|
|
977
|
+
onChange={(from, to) => {
|
|
978
|
+
console.log('Selected range:', from, to);
|
|
979
|
+
// Update your state or make API calls
|
|
980
|
+
}}
|
|
981
|
+
className="my-custom-class"
|
|
982
|
+
/>
|
|
983
|
+
|
|
984
|
+
// Date range filter with URL params (great for tables/reports)
|
|
985
|
+
// Automatically syncs with URL query parameters
|
|
986
|
+
<DateRangeFilter
|
|
987
|
+
startDateParamName="startDate" // URL param name, defaults to 'filterStartDate'
|
|
988
|
+
endDateParamName="endDate" // URL param name, defaults to 'filterEndDate'
|
|
989
|
+
className="mb-4"
|
|
990
|
+
/>
|
|
991
|
+
// URL will be updated to: ?startDate=2024-01-01&endDate=2024-01-31
|
|
992
|
+
// Default range: 2 days ago to 3 days ahead
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
#### Search Components
|
|
996
|
+
|
|
997
|
+
Search input with debouncing and URL parameter integration.
|
|
998
|
+
|
|
999
|
+
```tsx
|
|
1000
|
+
import { TableSearchInput } from 'izen-react-starter';
|
|
1001
|
+
|
|
1002
|
+
// Debounced search input (1 second delay)
|
|
1003
|
+
// Automatically updates URL search params
|
|
1004
|
+
<TableSearchInput
|
|
1005
|
+
placeholder="Search users..."
|
|
1006
|
+
/>
|
|
1007
|
+
// Updates URL to: ?search=query&page=1
|
|
1008
|
+
// Integrates with react-router-dom's useSearchParams
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
#### Common UI Components
|
|
1012
|
+
|
|
1013
|
+
Basic UI building blocks used across the application.
|
|
1014
|
+
|
|
1015
|
+
```tsx
|
|
1016
|
+
import { Heading, PageHead, ThemeToggle, Header } from 'izen-react-starter';
|
|
1017
|
+
|
|
1018
|
+
// Page heading with optional description
|
|
1019
|
+
<Heading
|
|
1020
|
+
title="Dashboard"
|
|
1021
|
+
description="Welcome to your dashboard"
|
|
1022
|
+
className="mb-4"
|
|
1023
|
+
/>
|
|
1024
|
+
|
|
1025
|
+
// Document head/title (uses react-helmet-async)
|
|
1026
|
+
<PageHead title="Dashboard | My App" />
|
|
1027
|
+
|
|
1028
|
+
// Theme toggle dropdown (light/dark/pink/system)
|
|
1029
|
+
<ThemeToggle setTheme={(theme) => {
|
|
1030
|
+
// theme: 'light' | 'dark' | 'pink' | 'system'
|
|
1031
|
+
console.log('Theme changed to:', theme);
|
|
1032
|
+
}} />
|
|
1033
|
+
```
|
|
1034
|
+
|
|
1035
|
+
#### Enhanced Table Components
|
|
1036
|
+
|
|
1037
|
+
Advanced table utilities with server-side features.
|
|
1038
|
+
|
|
1039
|
+
```tsx
|
|
1040
|
+
import {
|
|
1041
|
+
DataTableSkeleton,
|
|
1042
|
+
PaginationSection,
|
|
1043
|
+
ServerDataTable,
|
|
1044
|
+
TableSearchInput
|
|
1045
|
+
} from 'izen-react-starter';
|
|
1046
|
+
|
|
1047
|
+
// Loading skeleton while data is fetching
|
|
1048
|
+
<DataTableSkeleton
|
|
1049
|
+
columnCount={5}
|
|
1050
|
+
rowCount={10}
|
|
1051
|
+
searchableColumnCount={1}
|
|
1052
|
+
filterableColumnCount={2}
|
|
1053
|
+
showViewOptions={true}
|
|
1054
|
+
/>
|
|
1055
|
+
|
|
1056
|
+
// Client-side pagination component
|
|
1057
|
+
<PaginationSection
|
|
1058
|
+
totalPosts={100}
|
|
1059
|
+
postsPerPage={10}
|
|
1060
|
+
currentPage={currentPage}
|
|
1061
|
+
setCurrentPage={setCurrentPage}
|
|
1062
|
+
/>
|
|
1063
|
+
|
|
1064
|
+
// Server-side data table with TanStack Table
|
|
1065
|
+
// Automatically syncs with URL params (?page=1&limit=10)
|
|
1066
|
+
const columns: ColumnDef<User>[] = [
|
|
1067
|
+
{
|
|
1068
|
+
accessorKey: 'name',
|
|
1069
|
+
header: 'Name',
|
|
1070
|
+
},
|
|
1071
|
+
{
|
|
1072
|
+
accessorKey: 'email',
|
|
1073
|
+
header: 'Email',
|
|
1074
|
+
},
|
|
1075
|
+
];
|
|
1076
|
+
|
|
1077
|
+
<ServerDataTable
|
|
1078
|
+
columns={columns}
|
|
1079
|
+
data={users}
|
|
1080
|
+
pageCount={totalPages}
|
|
1081
|
+
pageSizeOptions={[10, 20, 50, 100]}
|
|
1082
|
+
/>
|
|
1083
|
+
// Features:
|
|
1084
|
+
// - Automatic URL sync (?page=2&limit=20)
|
|
1085
|
+
// - Server-side pagination
|
|
1086
|
+
// - Row selection
|
|
1087
|
+
// - Sorting support
|
|
1088
|
+
// - Custom page size options
|
|
1089
|
+
```
|
|
1090
|
+
|
|
1091
|
+
**Complete Table Example with Search and Filters:**
|
|
1092
|
+
|
|
1093
|
+
```tsx
|
|
1094
|
+
import {
|
|
1095
|
+
ServerDataTable,
|
|
1096
|
+
TableSearchInput,
|
|
1097
|
+
DateRangeFilter,
|
|
1098
|
+
DataTableSkeleton
|
|
1099
|
+
} from 'izen-react-starter';
|
|
1100
|
+
|
|
1101
|
+
function UsersTable() {
|
|
1102
|
+
const { data, isLoading } = useQuery({
|
|
1103
|
+
queryKey: ['users', searchParams.toString()],
|
|
1104
|
+
queryFn: () => fetchUsers(searchParams)
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
if (isLoading) {
|
|
1108
|
+
return <DataTableSkeleton columnCount={4} />;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
return (
|
|
1112
|
+
<div>
|
|
1113
|
+
{/* Search and filters */}
|
|
1114
|
+
<div className="flex gap-4 mb-4">
|
|
1115
|
+
<TableSearchInput placeholder="Search users..." />
|
|
1116
|
+
<DateRangeFilter />
|
|
1117
|
+
</div>
|
|
1118
|
+
|
|
1119
|
+
{/* Data table */}
|
|
1120
|
+
<ServerDataTable
|
|
1121
|
+
columns={columns}
|
|
1122
|
+
data={data.users}
|
|
1123
|
+
pageCount={data.totalPages}
|
|
1124
|
+
/>
|
|
1125
|
+
</div>
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
```
|
|
1129
|
+
|
|
1130
|
+
### Form Components
|
|
1131
|
+
|
|
1132
|
+
The library provides a complete set of form components that are library-ready without tight coupling to app-specific contexts.
|
|
1133
|
+
|
|
1134
|
+
#### FileUploadButton
|
|
1135
|
+
|
|
1136
|
+
Flexible file upload button with validation:
|
|
1137
|
+
|
|
1138
|
+
```tsx
|
|
1139
|
+
import { FileUploadButton } from 'izen-react-starter';
|
|
1140
|
+
|
|
1141
|
+
<FileUploadButton
|
|
1142
|
+
title="Upload File"
|
|
1143
|
+
name="document"
|
|
1144
|
+
accept={{
|
|
1145
|
+
'application/pdf': ['.pdf'],
|
|
1146
|
+
'image/*': ['.png', '.jpg', '.jpeg']
|
|
1147
|
+
}}
|
|
1148
|
+
maxSize={5 * 1024 * 1024} // 5MB
|
|
1149
|
+
disabled={false}
|
|
1150
|
+
onValidationError={(error) => {
|
|
1151
|
+
console.error('Validation error:', error);
|
|
1152
|
+
// Show toast or alert
|
|
1153
|
+
}}
|
|
1154
|
+
onSuccess={(file) => {
|
|
1155
|
+
console.log('File uploaded:', file);
|
|
1156
|
+
// Handle file upload
|
|
1157
|
+
}}
|
|
1158
|
+
className="my-4"
|
|
1159
|
+
/>
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
#### DatePicker
|
|
1163
|
+
|
|
1164
|
+
Calendar-based date picker with popover:
|
|
1165
|
+
|
|
1166
|
+
```tsx
|
|
1167
|
+
import { DatePicker } from 'izen-react-starter';
|
|
1168
|
+
|
|
1169
|
+
<DatePicker
|
|
1170
|
+
title="Select Date"
|
|
1171
|
+
name="eventDate"
|
|
1172
|
+
value={selectedDate}
|
|
1173
|
+
onChange={(date) => setSelectedDate(date)}
|
|
1174
|
+
placeholder="Pick a date"
|
|
1175
|
+
error={errors.eventDate}
|
|
1176
|
+
disabled={false}
|
|
1177
|
+
/>
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
#### TimeInput
|
|
1181
|
+
|
|
1182
|
+
Time input with hours, minutes, and optional seconds:
|
|
1183
|
+
|
|
1184
|
+
```tsx
|
|
1185
|
+
import { TimeInput } from 'izen-react-starter';
|
|
1186
|
+
|
|
1187
|
+
<TimeInput
|
|
1188
|
+
title="Event Time"
|
|
1189
|
+
name="startTime"
|
|
1190
|
+
value="14:30"
|
|
1191
|
+
onChange={(time) => console.log('Time:', time)}
|
|
1192
|
+
showSeconds={false}
|
|
1193
|
+
error={errors.startTime}
|
|
1194
|
+
disabled={false}
|
|
1195
|
+
/>
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
#### TextInput
|
|
1199
|
+
|
|
1200
|
+
Versatile text input supporting multiple types and textarea:
|
|
1201
|
+
|
|
1202
|
+
```tsx
|
|
1203
|
+
import { TextInput } from 'izen-react-starter';
|
|
1204
|
+
import { Mail, Lock } from 'lucide-react';
|
|
1205
|
+
|
|
1206
|
+
// Text input with icon
|
|
1207
|
+
<TextInput
|
|
1208
|
+
title="Email"
|
|
1209
|
+
name="email"
|
|
1210
|
+
type="email"
|
|
1211
|
+
icon={<Mail />}
|
|
1212
|
+
placeholder="Enter your email"
|
|
1213
|
+
error={errors.email}
|
|
1214
|
+
/>
|
|
1215
|
+
|
|
1216
|
+
// Password input
|
|
1217
|
+
<TextInput
|
|
1218
|
+
title="Password"
|
|
1219
|
+
name="password"
|
|
1220
|
+
type="password"
|
|
1221
|
+
icon={<Lock />}
|
|
1222
|
+
placeholder="Enter password"
|
|
1223
|
+
error={errors.password}
|
|
1224
|
+
/>
|
|
1225
|
+
|
|
1226
|
+
// Textarea
|
|
1227
|
+
<TextInput
|
|
1228
|
+
title="Description"
|
|
1229
|
+
name="description"
|
|
1230
|
+
type="textarea"
|
|
1231
|
+
rows={5}
|
|
1232
|
+
placeholder="Enter description"
|
|
1233
|
+
error={errors.description}
|
|
1234
|
+
/>
|
|
1235
|
+
|
|
1236
|
+
// Number input
|
|
1237
|
+
<TextInput
|
|
1238
|
+
title="Age"
|
|
1239
|
+
name="age"
|
|
1240
|
+
type="number"
|
|
1241
|
+
min={0}
|
|
1242
|
+
max={120}
|
|
1243
|
+
placeholder="Enter age"
|
|
1244
|
+
/>
|
|
1245
|
+
```
|
|
1246
|
+
|
|
1247
|
+
#### CheckboxGroup
|
|
1248
|
+
|
|
1249
|
+
Checkbox input supporting single or multiple items:
|
|
1250
|
+
|
|
1251
|
+
```tsx
|
|
1252
|
+
import { CheckboxGroup } from 'izen-react-starter';
|
|
1253
|
+
|
|
1254
|
+
// Single checkbox
|
|
1255
|
+
<CheckboxGroup
|
|
1256
|
+
title="Accept Terms"
|
|
1257
|
+
name="terms"
|
|
1258
|
+
items={[
|
|
1259
|
+
{ id: 'terms', name: 'terms', displayName: 'I accept the terms and conditions' }
|
|
1260
|
+
]}
|
|
1261
|
+
error={errors.terms}
|
|
1262
|
+
/>
|
|
1263
|
+
|
|
1264
|
+
// Multiple checkboxes
|
|
1265
|
+
<CheckboxGroup
|
|
1266
|
+
title="Select Features"
|
|
1267
|
+
name="features"
|
|
1268
|
+
items={[
|
|
1269
|
+
{ id: 'feature1', name: 'features', displayName: 'Email Notifications', checked: true },
|
|
1270
|
+
{ id: 'feature2', name: 'features', displayName: 'SMS Alerts', checked: false },
|
|
1271
|
+
{ id: 'feature3', name: 'features', displayName: 'Push Notifications', checked: true },
|
|
1272
|
+
]}
|
|
1273
|
+
onChange={(e) => {
|
|
1274
|
+
const checked = e.target.checked;
|
|
1275
|
+
const id = e.target.id;
|
|
1276
|
+
console.log(`Checkbox ${id} is ${checked ? 'checked' : 'unchecked'}`);
|
|
1277
|
+
}}
|
|
1278
|
+
/>
|
|
1279
|
+
```
|
|
1280
|
+
|
|
1281
|
+
#### RadioGroup
|
|
1282
|
+
|
|
1283
|
+
Radio button group with horizontal or vertical layout:
|
|
1284
|
+
|
|
1285
|
+
```tsx
|
|
1286
|
+
import { RadioGroup } from 'izen-react-starter';
|
|
1287
|
+
|
|
1288
|
+
// Horizontal layout
|
|
1289
|
+
<RadioGroup
|
|
1290
|
+
title="Select Plan"
|
|
1291
|
+
name="plan"
|
|
1292
|
+
items={[
|
|
1293
|
+
{ value: 'free', title: 'Free Plan' },
|
|
1294
|
+
{ value: 'pro', title: 'Pro Plan' },
|
|
1295
|
+
{ value: 'enterprise', title: 'Enterprise Plan' },
|
|
1296
|
+
]}
|
|
1297
|
+
onChange={(e) => console.log('Selected:', e.target.value)}
|
|
1298
|
+
error={errors.plan}
|
|
1299
|
+
/>
|
|
1300
|
+
|
|
1301
|
+
// Vertical layout
|
|
1302
|
+
<RadioGroup
|
|
1303
|
+
title="Notification Preference"
|
|
1304
|
+
name="notifications"
|
|
1305
|
+
vertical={true}
|
|
1306
|
+
items={[
|
|
1307
|
+
{ value: 'all', title: 'All Notifications' },
|
|
1308
|
+
{ value: 'important', title: 'Important Only' },
|
|
1309
|
+
{ value: 'none', title: 'None' },
|
|
1310
|
+
]}
|
|
1311
|
+
onChange={(e) => setNotificationPref(e.target.value)}
|
|
1312
|
+
/>
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
**Complete Form Example:**
|
|
1316
|
+
|
|
1317
|
+
```tsx
|
|
1318
|
+
import {
|
|
1319
|
+
TextInput,
|
|
1320
|
+
DatePicker,
|
|
1321
|
+
TimeInput,
|
|
1322
|
+
CheckboxGroup,
|
|
1323
|
+
RadioGroup,
|
|
1324
|
+
FileUploadButton
|
|
1325
|
+
} from 'izen-react-starter';
|
|
1326
|
+
import { useState } from 'react';
|
|
1327
|
+
|
|
1328
|
+
function EventForm() {
|
|
1329
|
+
const [formData, setFormData] = useState({
|
|
1330
|
+
name: '',
|
|
1331
|
+
date: undefined,
|
|
1332
|
+
time: '09:00',
|
|
1333
|
+
type: 'public',
|
|
1334
|
+
features: [],
|
|
1335
|
+
document: null
|
|
1336
|
+
});
|
|
1337
|
+
|
|
1338
|
+
const [errors, setErrors] = useState({});
|
|
1339
|
+
|
|
1340
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
1341
|
+
e.preventDefault();
|
|
1342
|
+
console.log('Form data:', formData);
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
return (
|
|
1346
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
1347
|
+
<TextInput
|
|
1348
|
+
title="Event Name"
|
|
1349
|
+
name="name"
|
|
1350
|
+
value={formData.name}
|
|
1351
|
+
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
|
1352
|
+
placeholder="Enter event name"
|
|
1353
|
+
error={errors.name}
|
|
1354
|
+
/>
|
|
1355
|
+
|
|
1356
|
+
<DatePicker
|
|
1357
|
+
title="Event Date"
|
|
1358
|
+
name="date"
|
|
1359
|
+
value={formData.date}
|
|
1360
|
+
onChange={(date) => setFormData({ ...formData, date })}
|
|
1361
|
+
error={errors.date}
|
|
1362
|
+
/>
|
|
1363
|
+
|
|
1364
|
+
<TimeInput
|
|
1365
|
+
title="Start Time"
|
|
1366
|
+
name="time"
|
|
1367
|
+
value={formData.time}
|
|
1368
|
+
onChange={(time) => setFormData({ ...formData, time })}
|
|
1369
|
+
error={errors.time}
|
|
1370
|
+
/>
|
|
1371
|
+
|
|
1372
|
+
<RadioGroup
|
|
1373
|
+
title="Event Type"
|
|
1374
|
+
name="type"
|
|
1375
|
+
items={[
|
|
1376
|
+
{ value: 'public', title: 'Public Event' },
|
|
1377
|
+
{ value: 'private', title: 'Private Event' },
|
|
1378
|
+
]}
|
|
1379
|
+
onChange={(e) => setFormData({ ...formData, type: e.target.value })}
|
|
1380
|
+
/>
|
|
1381
|
+
|
|
1382
|
+
<CheckboxGroup
|
|
1383
|
+
title="Event Features"
|
|
1384
|
+
name="features"
|
|
1385
|
+
items={[
|
|
1386
|
+
{ id: 'catering', name: 'features', displayName: 'Catering' },
|
|
1387
|
+
{ id: 'parking', name: 'features', displayName: 'Parking' },
|
|
1388
|
+
{ id: 'wifi', name: 'features', displayName: 'WiFi' },
|
|
1389
|
+
]}
|
|
1390
|
+
/>
|
|
1391
|
+
|
|
1392
|
+
<FileUploadButton
|
|
1393
|
+
title="Event Document"
|
|
1394
|
+
name="document"
|
|
1395
|
+
accept={{ 'application/pdf': ['.pdf'] }}
|
|
1396
|
+
onSuccess={(file) => setFormData({ ...formData, document: file })}
|
|
1397
|
+
onValidationError={(error) => setErrors({ ...errors, document: error })}
|
|
1398
|
+
/>
|
|
1399
|
+
|
|
1400
|
+
<button type="submit" className="btn btn-primary">
|
|
1401
|
+
Create Event
|
|
1402
|
+
</button>
|
|
1403
|
+
</form>
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
```
|
|
1407
|
+
|
|
1408
|
+
#### FormInput
|
|
1409
|
+
|
|
1410
|
+
Enhanced input component with card number formatting and date display:
|
|
1411
|
+
|
|
1412
|
+
```tsx
|
|
1413
|
+
import { FormInput } from 'izen-react-starter';
|
|
1414
|
+
import { User, Mail, CreditCard } from 'lucide-react';
|
|
1415
|
+
|
|
1416
|
+
// Text input with icon
|
|
1417
|
+
<FormInput
|
|
1418
|
+
title="Full Name"
|
|
1419
|
+
name="fullName"
|
|
1420
|
+
type="text"
|
|
1421
|
+
icon={<User />}
|
|
1422
|
+
placeholder="Enter your name"
|
|
1423
|
+
value={name}
|
|
1424
|
+
onValueChange={(value) => setName(value)}
|
|
1425
|
+
error={errors.name}
|
|
1426
|
+
/>
|
|
1427
|
+
|
|
1428
|
+
// Card number with auto-formatting
|
|
1429
|
+
<FormInput
|
|
1430
|
+
title="Card Number"
|
|
1431
|
+
name="cardNumber"
|
|
1432
|
+
type="cardNumber"
|
|
1433
|
+
icon={<CreditCard />}
|
|
1434
|
+
placeholder="0000 0000 0000 0000"
|
|
1435
|
+
value={cardNumber}
|
|
1436
|
+
onValueChange={(value) => setCardNumber(value)}
|
|
1437
|
+
/>
|
|
1438
|
+
|
|
1439
|
+
// Date input with formatted label
|
|
1440
|
+
<FormInput
|
|
1441
|
+
title="Birth Date"
|
|
1442
|
+
name="birthDate"
|
|
1443
|
+
type="date"
|
|
1444
|
+
value={birthDate}
|
|
1445
|
+
onChange={(e) => setBirthDate(e.target.value)}
|
|
1446
|
+
showDateLabel={true} // Shows formatted date like "(Jan 15)"
|
|
1447
|
+
/>
|
|
1448
|
+
|
|
1449
|
+
// Textarea
|
|
1450
|
+
<FormInput
|
|
1451
|
+
title="Comments"
|
|
1452
|
+
name="comments"
|
|
1453
|
+
type="textarea"
|
|
1454
|
+
rows={4}
|
|
1455
|
+
placeholder="Enter your comments"
|
|
1456
|
+
value={comments}
|
|
1457
|
+
onValueChange={(value) => setComments(value)}
|
|
1458
|
+
/>
|
|
1459
|
+
```
|
|
1460
|
+
|
|
1461
|
+
#### FormSelect
|
|
1462
|
+
|
|
1463
|
+
Standard dropdown select component:
|
|
1464
|
+
|
|
1465
|
+
> **Note:** This component is exported as `FormSelect` to avoid naming conflict with shadcn/ui's `Select` component.
|
|
1466
|
+
|
|
1467
|
+
```tsx
|
|
1468
|
+
import { FormSelect } from 'izen-react-starter';
|
|
1469
|
+
|
|
1470
|
+
<FormSelect
|
|
1471
|
+
title="Country"
|
|
1472
|
+
name="country"
|
|
1473
|
+
placeholder="Select country"
|
|
1474
|
+
value={country}
|
|
1475
|
+
options={[
|
|
1476
|
+
{ value: 'us', label: 'United States' },
|
|
1477
|
+
{ value: 'uk', label: 'United Kingdom' },
|
|
1478
|
+
{ value: 'ca', label: 'Canada' },
|
|
1479
|
+
]}
|
|
1480
|
+
onChange={(value) => setCountry(value)}
|
|
1481
|
+
error={errors.country}
|
|
1482
|
+
/>
|
|
1483
|
+
|
|
1484
|
+
// With "Please Select" option
|
|
1485
|
+
<FormSelect
|
|
1486
|
+
title="Category"
|
|
1487
|
+
name="category"
|
|
1488
|
+
showOtherOption={true}
|
|
1489
|
+
otherOptionLabel="Please Select"
|
|
1490
|
+
options={categories}
|
|
1491
|
+
onChange={(value) => setCategory(value)}
|
|
1492
|
+
/>
|
|
1493
|
+
```
|
|
1494
|
+
|
|
1495
|
+
#### ComboboxSelect
|
|
1496
|
+
|
|
1497
|
+
Advanced searchable select with Command palette:
|
|
1498
|
+
|
|
1499
|
+
```tsx
|
|
1500
|
+
import { ComboboxSelect } from 'izen-react-starter';
|
|
1501
|
+
import { Building } from 'lucide-react';
|
|
1502
|
+
|
|
1503
|
+
<ComboboxSelect
|
|
1504
|
+
title="Company"
|
|
1505
|
+
name="company"
|
|
1506
|
+
icon={<Building />}
|
|
1507
|
+
placeholder="Search companies..."
|
|
1508
|
+
value={selectedCompany}
|
|
1509
|
+
options={companies}
|
|
1510
|
+
onChange={(value) => setSelectedCompany(value)}
|
|
1511
|
+
onSearch={(searchTerm) => {
|
|
1512
|
+
// Trigger API call to search companies
|
|
1513
|
+
fetchCompanies(searchTerm);
|
|
1514
|
+
}}
|
|
1515
|
+
showOtherOption={true}
|
|
1516
|
+
otherOptionLabel="No Company"
|
|
1517
|
+
emptyMessage="No companies found."
|
|
1518
|
+
error={errors.company}
|
|
1519
|
+
/>
|
|
1520
|
+
```
|
|
1521
|
+
|
|
1522
|
+
**Features:**
|
|
1523
|
+
- Searchable with Command palette UI
|
|
1524
|
+
- Toggle selection (click again to deselect)
|
|
1525
|
+
- Optional "other" option
|
|
1526
|
+
- Custom empty state message
|
|
1527
|
+
- Search callback for dynamic loading
|
|
1528
|
+
|
|
1529
|
+
#### FormButtons
|
|
1530
|
+
|
|
1531
|
+
Reusable form action buttons:
|
|
1532
|
+
|
|
1533
|
+
```tsx
|
|
1534
|
+
import { FormButtons } from 'izen-react-starter';
|
|
1535
|
+
|
|
1536
|
+
<FormButtons
|
|
1537
|
+
loading={isSubmitting}
|
|
1538
|
+
showSubmit={true}
|
|
1539
|
+
showCancel={true}
|
|
1540
|
+
showReset={true}
|
|
1541
|
+
submitText="Save"
|
|
1542
|
+
cancelText="Cancel"
|
|
1543
|
+
resetText="Clear"
|
|
1544
|
+
onCancel={() => router.back()}
|
|
1545
|
+
onReset={() => form.reset()}
|
|
1546
|
+
onSubmit={() => form.handleSubmit(onSubmit)()}
|
|
1547
|
+
/>
|
|
1548
|
+
```
|
|
1549
|
+
|
|
1550
|
+
**Props:**
|
|
1551
|
+
- `loading`: Shows "Please wait..." text
|
|
1552
|
+
- `showSubmit/showCancel/showReset`: Toggle button visibility
|
|
1553
|
+
- `submitText/cancelText/resetText`: Custom button labels
|
|
1554
|
+
- `onCancel/onReset/onSubmit`: Click handlers
|
|
1555
|
+
|
|
1556
|
+
#### FormLayout
|
|
1557
|
+
|
|
1558
|
+
Complete form wrapper with error display and action buttons:
|
|
1559
|
+
|
|
1560
|
+
```tsx
|
|
1561
|
+
import { FormLayout, FormInput, FormSelect } from 'izen-react-starter';
|
|
1562
|
+
import { useState } from 'react';
|
|
1563
|
+
|
|
1564
|
+
function CreateUser() {
|
|
1565
|
+
const [loading, setLoading] = useState(false);
|
|
1566
|
+
const [error, setError] = useState('');
|
|
1567
|
+
|
|
1568
|
+
const handleSubmit = async (data: any) => {
|
|
1569
|
+
setLoading(true);
|
|
1570
|
+
setError('');
|
|
1571
|
+
|
|
1572
|
+
try {
|
|
1573
|
+
await createUser(data);
|
|
1574
|
+
router.push('/users');
|
|
1575
|
+
} catch (err) {
|
|
1576
|
+
setError(err.message);
|
|
1577
|
+
} finally {
|
|
1578
|
+
setLoading(false);
|
|
1579
|
+
}
|
|
1580
|
+
};
|
|
1581
|
+
|
|
1582
|
+
return (
|
|
1583
|
+
<FormLayout
|
|
1584
|
+
onSubmit={handleSubmit}
|
|
1585
|
+
error={error}
|
|
1586
|
+
loading={loading}
|
|
1587
|
+
showSubmit={true}
|
|
1588
|
+
showCancel={true}
|
|
1589
|
+
showReset={true}
|
|
1590
|
+
submitText="Create User"
|
|
1591
|
+
onCancel={() => router.back()}
|
|
1592
|
+
onReset={() => {
|
|
1593
|
+
// Reset form logic
|
|
1594
|
+
}}
|
|
1595
|
+
fullHeight={false}
|
|
1596
|
+
>
|
|
1597
|
+
<FormInput
|
|
1598
|
+
title="Name"
|
|
1599
|
+
name="name"
|
|
1600
|
+
placeholder="Enter name"
|
|
1601
|
+
/>
|
|
1602
|
+
|
|
1603
|
+
<FormInput
|
|
1604
|
+
title="Email"
|
|
1605
|
+
name="email"
|
|
1606
|
+
type="email"
|
|
1607
|
+
placeholder="Enter email"
|
|
1608
|
+
/>
|
|
1609
|
+
|
|
1610
|
+
<FormSelect
|
|
1611
|
+
title="Role"
|
|
1612
|
+
name="role"
|
|
1613
|
+
options={[
|
|
1614
|
+
{ value: 'admin', label: 'Admin' },
|
|
1615
|
+
{ value: 'user', label: 'User' },
|
|
1616
|
+
]}
|
|
1617
|
+
/>
|
|
1618
|
+
</FormLayout>
|
|
1619
|
+
);
|
|
1620
|
+
}
|
|
1621
|
+
```
|
|
1622
|
+
|
|
1623
|
+
**Features:**
|
|
1624
|
+
- Automatic form submission handling
|
|
1625
|
+
- Error display with Alert component
|
|
1626
|
+
- Integrated FormButtons
|
|
1627
|
+
- Card wrapper with styling
|
|
1628
|
+
- Loading state management
|
|
1629
|
+
- Sticky button footer
|
|
1630
|
+
|
|
1631
|
+
**Complete Advanced Form Example:**
|
|
1632
|
+
|
|
1633
|
+
```tsx
|
|
1634
|
+
import {
|
|
1635
|
+
FormLayout,
|
|
1636
|
+
FormInput,
|
|
1637
|
+
Select,
|
|
1638
|
+
ComboboxSelect,
|
|
1639
|
+
DatePicker,
|
|
1640
|
+
TimeInput,
|
|
1641
|
+
CheckboxGroup,
|
|
1642
|
+
RadioGroup,
|
|
1643
|
+
FileUploadButton
|
|
1644
|
+
} from 'izen-react-starter';
|
|
1645
|
+
import { useState } from 'react';
|
|
1646
|
+
|
|
1647
|
+
function AdvancedForm() {
|
|
1648
|
+
const [loading, setLoading] = useState(false);
|
|
1649
|
+
const [error, setError] = useState('');
|
|
1650
|
+
const [formData, setFormData] = useState({
|
|
1651
|
+
name: '',
|
|
1652
|
+
email: '',
|
|
1653
|
+
cardNumber: '',
|
|
1654
|
+
country: '',
|
|
1655
|
+
company: '',
|
|
1656
|
+
eventDate: undefined,
|
|
1657
|
+
startTime: '09:00',
|
|
1658
|
+
plan: 'free',
|
|
1659
|
+
features: [],
|
|
1660
|
+
document: null
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
const handleSubmit = async (data: any) => {
|
|
1664
|
+
setLoading(true);
|
|
1665
|
+
try {
|
|
1666
|
+
// Process form data
|
|
1667
|
+
await submitForm({ ...formData, ...data });
|
|
1668
|
+
} catch (err) {
|
|
1669
|
+
setError(err.message);
|
|
1670
|
+
} finally {
|
|
1671
|
+
setLoading(false);
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1675
|
+
return (
|
|
1676
|
+
<FormLayout
|
|
1677
|
+
onSubmit={handleSubmit}
|
|
1678
|
+
error={error}
|
|
1679
|
+
loading={loading}
|
|
1680
|
+
submitText="Submit Registration"
|
|
1681
|
+
fullHeight={false}
|
|
1682
|
+
>
|
|
1683
|
+
<div className="grid grid-cols-2 gap-4">
|
|
1684
|
+
<FormInput
|
|
1685
|
+
title="Full Name"
|
|
1686
|
+
name="name"
|
|
1687
|
+
placeholder="John Doe"
|
|
1688
|
+
value={formData.name}
|
|
1689
|
+
onValueChange={(value) => setFormData({ ...formData, name: value })}
|
|
1690
|
+
/>
|
|
1691
|
+
|
|
1692
|
+
<FormInput
|
|
1693
|
+
title="Email"
|
|
1694
|
+
name="email"
|
|
1695
|
+
type="email"
|
|
1696
|
+
placeholder="john@example.com"
|
|
1697
|
+
value={formData.email}
|
|
1698
|
+
onValueChange={(value) => setFormData({ ...formData, email: value })}
|
|
1699
|
+
/>
|
|
1700
|
+
</div>
|
|
1701
|
+
|
|
1702
|
+
<FormInput
|
|
1703
|
+
title="Card Number"
|
|
1704
|
+
name="cardNumber"
|
|
1705
|
+
type="cardNumber"
|
|
1706
|
+
placeholder="0000 0000 0000 0000"
|
|
1707
|
+
value={formData.cardNumber}
|
|
1708
|
+
onValueChange={(value) => setFormData({ ...formData, cardNumber: value })}
|
|
1709
|
+
/>
|
|
1710
|
+
|
|
1711
|
+
<FormSelect
|
|
1712
|
+
title="Country"
|
|
1713
|
+
name="country"
|
|
1714
|
+
placeholder="Select country"
|
|
1715
|
+
value={formData.country}
|
|
1716
|
+
options={countries}
|
|
1717
|
+
onChange={(value) => setFormData({ ...formData, country: value })}
|
|
1718
|
+
/>
|
|
1719
|
+
|
|
1720
|
+
<ComboboxSelect
|
|
1721
|
+
title="Company"
|
|
1722
|
+
name="company"
|
|
1723
|
+
placeholder="Search companies..."
|
|
1724
|
+
value={formData.company}
|
|
1725
|
+
options={companies}
|
|
1726
|
+
onChange={(value) => setFormData({ ...formData, company: value })}
|
|
1727
|
+
onSearch={(term) => searchCompanies(term)}
|
|
1728
|
+
/>
|
|
1729
|
+
|
|
1730
|
+
<div className="grid grid-cols-2 gap-4">
|
|
1731
|
+
<DatePicker
|
|
1732
|
+
title="Event Date"
|
|
1733
|
+
name="eventDate"
|
|
1734
|
+
value={formData.eventDate}
|
|
1735
|
+
onChange={(date) => setFormData({ ...formData, eventDate: date })}
|
|
1736
|
+
/>
|
|
1737
|
+
|
|
1738
|
+
<TimeInput
|
|
1739
|
+
title="Start Time"
|
|
1740
|
+
name="startTime"
|
|
1741
|
+
value={formData.startTime}
|
|
1742
|
+
onChange={(time) => setFormData({ ...formData, startTime: time })}
|
|
1743
|
+
/>
|
|
1744
|
+
</div>
|
|
1745
|
+
|
|
1746
|
+
<RadioGroup
|
|
1747
|
+
title="Subscription Plan"
|
|
1748
|
+
name="plan"
|
|
1749
|
+
items={[
|
|
1750
|
+
{ value: 'free', title: 'Free' },
|
|
1751
|
+
{ value: 'pro', title: 'Pro' },
|
|
1752
|
+
{ value: 'enterprise', title: 'Enterprise' },
|
|
1753
|
+
]}
|
|
1754
|
+
onChange={(e) => setFormData({ ...formData, plan: e.target.value })}
|
|
1755
|
+
/>
|
|
1756
|
+
|
|
1757
|
+
<CheckboxGroup
|
|
1758
|
+
title="Features"
|
|
1759
|
+
name="features"
|
|
1760
|
+
items={[
|
|
1761
|
+
{ id: 'email', name: 'features', displayName: 'Email Notifications' },
|
|
1762
|
+
{ id: 'sms', name: 'features', displayName: 'SMS Alerts' },
|
|
1763
|
+
{ id: 'push', name: 'features', displayName: 'Push Notifications' },
|
|
1764
|
+
]}
|
|
1765
|
+
/>
|
|
1766
|
+
|
|
1767
|
+
<FileUploadButton
|
|
1768
|
+
title="Upload Document"
|
|
1769
|
+
name="document"
|
|
1770
|
+
accept={{ 'application/pdf': ['.pdf'] }}
|
|
1771
|
+
maxSize={5 * 1024 * 1024}
|
|
1772
|
+
onSuccess={(file) => setFormData({ ...formData, document: file })}
|
|
1773
|
+
/>
|
|
1774
|
+
</FormLayout>
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
```
|
|
570
1778
|
|
|
571
1779
|
### RBAC System
|
|
572
1780
|
|