nexstruct 1.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/AGENTS.md +122 -0
- package/LICENSE +21 -0
- package/README.md +103 -0
- package/package.json +99 -0
- package/scaffold/generator.js +409 -0
- package/scaffold/index.js +20 -0
- package/scaffold/prompts.js +108 -0
- package/templates/api/axios/src/api/axios/client.api.ts +30 -0
- package/templates/api/axios/src/api/axios/users.api.ts +15 -0
- package/templates/api/fetch/src/api/fetch/client.api.ts +68 -0
- package/templates/api/fetch/src/api/fetch/users.api.ts +15 -0
- package/templates/api/trpc/src/api/trpc/client.api.ts +4 -0
- package/templates/api/trpc/src/api/trpc/router.api.ts +15 -0
- package/templates/api/trpc/src/api/trpc/server.client.api.ts +4 -0
- package/templates/api/trpc/src/providers/trpc.provider.tsx +24 -0
- package/templates/auth/clerk/src/auth/clerk/auth.service.ts +4 -0
- package/templates/auth/clerk/src/hooks/use-auth.hook.ts +13 -0
- package/templates/auth/clerk/src/middleware.ts +7 -0
- package/templates/auth/clerk/src/providers/auth.provider.tsx +6 -0
- package/templates/auth/next-auth/src/app/api/auth/[...nextauth]/route.ts +5 -0
- package/templates/auth/next-auth/src/auth/next-auth/auth.service.ts +45 -0
- package/templates/auth/next-auth/src/hooks/use-session.hook.ts +13 -0
- package/templates/auth/next-auth/src/providers/session.provider.tsx +6 -0
- package/templates/forms/formik/src/components/forms/login-form.component.tsx +30 -0
- package/templates/forms/formik/src/forms/formik/hooks/use-form-config.hook.ts +7 -0
- package/templates/forms/formik/src/forms/formik/schemas/example.schema.ts +8 -0
- package/templates/forms/react-hook-form/src/components/forms/login-form.component.tsx +27 -0
- package/templates/forms/react-hook-form/src/forms/react-hook-form/hooks/use-form.hook.ts +13 -0
- package/templates/forms/react-hook-form/src/forms/react-hook-form/schemas/example.schema.ts +15 -0
- package/templates/nextjs-base/next.config.ts +5 -0
- package/templates/nextjs-base/postcss.config.mjs +9 -0
- package/templates/nextjs-base/src/app/_components/navbar.tsx +88 -0
- package/templates/nextjs-base/src/app/_components/sidebar.tsx +223 -0
- package/templates/nextjs-base/src/app/error.tsx +39 -0
- package/templates/nextjs-base/src/app/globals.css +71 -0
- package/templates/nextjs-base/src/app/layout.tsx +21 -0
- package/templates/nextjs-base/src/app/loading.tsx +13 -0
- package/templates/nextjs-base/src/app/not-found.tsx +22 -0
- package/templates/nextjs-base/src/app/page.tsx +10 -0
- package/templates/nextjs-base/tailwind.config.ts +69 -0
- package/templates/shared/src/components/common/theme-toggle.component.tsx +31 -0
- package/templates/shared/src/components/common/toast/custom-message.component.tsx +18 -0
- package/templates/shared/src/components/common/toast/index.ts +8 -0
- package/templates/shared/src/components/common/toast/toast-message.component.tsx +112 -0
- package/templates/shared/src/hooks/use-debounce.hook.ts +12 -0
- package/templates/shared/src/hooks/use-fetch.hook.ts +42 -0
- package/templates/shared/src/hooks/use-intersection-observer.hook.ts +39 -0
- package/templates/shared/src/hooks/use-local-storage.hook.ts +30 -0
- package/templates/shared/src/hooks/use-media-query.hook.ts +26 -0
- package/templates/shared/src/hooks/use-toggle.hook.ts +12 -0
- package/templates/shared/src/lib/utils.util.ts +361 -0
- package/templates/shared/src/providers/theme.provider.tsx +17 -0
- package/templates/shared/src/providers/toast.provider.tsx +32 -0
- package/templates/shared/src/types/common.type.ts +34 -0
- package/templates/state/context/src/store/context/auth.context.tsx +47 -0
- package/templates/state/context/src/store/context/counter.context.tsx +41 -0
- package/templates/state/context/src/store/context/index.ts +2 -0
- package/templates/state/redux/src/providers/redux.provider.tsx +7 -0
- package/templates/state/redux/src/store/redux/hooks.store.ts +5 -0
- package/templates/state/redux/src/store/redux/index.ts +4 -0
- package/templates/state/redux/src/store/redux/slices/api.slice.ts +8 -0
- package/templates/state/redux/src/store/redux/slices/counter.slice.ts +24 -0
- package/templates/state/redux/src/store/redux/store.store.ts +13 -0
- package/templates/state/zustand/src/store/zustand/counter.store.ts +15 -0
- package/templates/state/zustand/src/store/zustand/index.ts +2 -0
- package/templates/state/zustand/src/store/zustand/user.store.ts +32 -0
- package/templates/ui/antd/COMPONENT_GUIDE.md +326 -0
- package/templates/ui/antd/src/app/examples/dialog/page.tsx +205 -0
- package/templates/ui/antd/src/app/examples/form/page.tsx +160 -0
- package/templates/ui/antd/src/app/examples/layout.tsx +125 -0
- package/templates/ui/antd/src/app/examples/page.tsx +64 -0
- package/templates/ui/antd/src/app/examples/table/page.tsx +118 -0
- package/templates/ui/antd/src/app/page.tsx +283 -0
- package/templates/ui/antd/src/components/common/DynamicTable/dynamic-table.component.tsx +79 -0
- package/templates/ui/antd/src/components/common/button/action-button.component.tsx +63 -0
- package/templates/ui/antd/src/components/common/dialog/dialog-wrapper.component.tsx +63 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/check-field.component.tsx +55 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/date-picker-field.component.tsx +80 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/limit-field.component.tsx +26 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/multi-check-field.component.tsx +56 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/number-field.component.tsx +100 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/otp-field.component.tsx +63 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/password-field.component.tsx +106 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/phone-number-field.component.tsx +78 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/radio-field.component.tsx +55 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/range-date-picker.component.tsx +66 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/search-field.component.tsx +24 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/select-field.component.tsx +82 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/single-check-field.component.tsx +50 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/single-select-field.component.tsx +86 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/string-number-field.component.tsx +80 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/switch-field.component.tsx +62 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/text-area-field.component.tsx +85 -0
- package/templates/ui/antd/src/components/common/fields/assets/components/text-field.component.tsx +88 -0
- package/templates/ui/antd/src/components/common/fields/assets/interface/input-props.type.ts +233 -0
- package/templates/ui/antd/src/components/common/fields/cusInputField.component.tsx +40 -0
- package/templates/ui/antd/src/components/common/pagination/pagination.component.tsx +27 -0
- package/templates/ui/antd/src/components/ui/avatar.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/badge.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/button.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/card.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/checkbox.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/dialog.component.tsx +9 -0
- package/templates/ui/antd/src/components/ui/dropdown-menu.component.tsx +10 -0
- package/templates/ui/antd/src/components/ui/form.component.tsx +12 -0
- package/templates/ui/antd/src/components/ui/input.component.tsx +13 -0
- package/templates/ui/antd/src/components/ui/label.component.tsx +18 -0
- package/templates/ui/antd/src/components/ui/popover.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/progress.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/radio-group.component.tsx +10 -0
- package/templates/ui/antd/src/components/ui/scroll-area.component.tsx +25 -0
- package/templates/ui/antd/src/components/ui/select.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/separator.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/sheet.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/switch.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/table.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/tabs.component.tsx +8 -0
- package/templates/ui/antd/src/components/ui/textarea.component.tsx +9 -0
- package/templates/ui/antd/src/components/ui/tooltip.component.tsx +8 -0
- package/templates/ui/antd/src/lib/theme.util.ts +40 -0
- package/templates/ui/antd/src/providers/antd.provider.tsx +13 -0
- package/templates/ui/mui/src/app/examples/layout.tsx +113 -0
- package/templates/ui/mui/src/app/examples/page.tsx +716 -0
- package/templates/ui/mui/src/app/page.tsx +298 -0
- package/templates/ui/mui/src/components/common/DynamicTable/dynamic-table.component.tsx +131 -0
- package/templates/ui/mui/src/components/common/button/action-button.component.tsx +57 -0
- package/templates/ui/mui/src/components/common/dialog/dialog-wrapper.component.tsx +55 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/check-field.component.tsx +51 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/date-picker-field.component.tsx +50 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/multi-check-field.component.tsx +14 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/number-field.component.tsx +59 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/password-field.component.tsx +87 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/phone-number-field.component.tsx +48 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/radio-field.component.tsx +37 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/search-field.component.tsx +41 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/select-field.component.tsx +77 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/single-check-field.component.tsx +39 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/single-select-field.component.tsx +56 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/string-number-field.component.tsx +52 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/switch-field.component.tsx +35 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/text-area-field.component.tsx +46 -0
- package/templates/ui/mui/src/components/common/fields/assets/components/text-field.component.tsx +51 -0
- package/templates/ui/mui/src/components/common/fields/assets/interface/input-props.type.ts +193 -0
- package/templates/ui/mui/src/components/common/fields/cusInputField.component.tsx +34 -0
- package/templates/ui/mui/src/components/common/pagination/pagination.component.tsx +59 -0
- package/templates/ui/mui/src/components/ui/avatar.component.tsx +19 -0
- package/templates/ui/mui/src/components/ui/badge.component.tsx +18 -0
- package/templates/ui/mui/src/components/ui/button.component.tsx +22 -0
- package/templates/ui/mui/src/components/ui/card.component.tsx +39 -0
- package/templates/ui/mui/src/components/ui/checkbox.component.tsx +21 -0
- package/templates/ui/mui/src/components/ui/dialog.component.tsx +38 -0
- package/templates/ui/mui/src/components/ui/dropdown-menu.component.tsx +43 -0
- package/templates/ui/mui/src/components/ui/form.component.tsx +98 -0
- package/templates/ui/mui/src/components/ui/input.component.tsx +15 -0
- package/templates/ui/mui/src/components/ui/label.component.tsx +15 -0
- package/templates/ui/mui/src/components/ui/popover.component.tsx +20 -0
- package/templates/ui/mui/src/components/ui/progress.component.tsx +19 -0
- package/templates/ui/mui/src/components/ui/radio-group.component.tsx +25 -0
- package/templates/ui/mui/src/components/ui/scroll-area.component.tsx +27 -0
- package/templates/ui/mui/src/components/ui/select.component.tsx +26 -0
- package/templates/ui/mui/src/components/ui/separator.component.tsx +11 -0
- package/templates/ui/mui/src/components/ui/sheet.component.tsx +44 -0
- package/templates/ui/mui/src/components/ui/switch.component.tsx +23 -0
- package/templates/ui/mui/src/components/ui/table.component.tsx +34 -0
- package/templates/ui/mui/src/components/ui/tabs.component.tsx +38 -0
- package/templates/ui/mui/src/components/ui/textarea.component.tsx +18 -0
- package/templates/ui/mui/src/components/ui/tooltip.component.tsx +24 -0
- package/templates/ui/mui/src/lib/theme.util.ts +73 -0
- package/templates/ui/mui/src/providers/mui.provider.tsx +13 -0
- package/templates/ui/shadcn/COMPONENT_GUIDE.md +306 -0
- package/templates/ui/shadcn/src/app/examples/dialog/page.tsx +122 -0
- package/templates/ui/shadcn/src/app/examples/form/page.tsx +107 -0
- package/templates/ui/shadcn/src/app/examples/layout.tsx +24 -0
- package/templates/ui/shadcn/src/app/examples/page.tsx +30 -0
- package/templates/ui/shadcn/src/app/examples/table/page.tsx +77 -0
- package/templates/ui/shadcn/src/app/page.tsx +20 -0
- package/templates/ui/shadcn/src/components/common/DynamicTable/dynamic-table.component.tsx +136 -0
- package/templates/ui/shadcn/src/components/common/button/action-button.component.tsx +68 -0
- package/templates/ui/shadcn/src/components/common/dialog/dialog-wrapper.component.tsx +58 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/check-field.component.tsx +52 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/date-picker-field.component.tsx +62 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/dynamic-file-upload-field.component.tsx +152 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/limit-field.component.tsx +73 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/multi-check-field.component.tsx +46 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/number-field.component.tsx +124 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/otp-field.component.tsx +61 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/password-field.component.tsx +110 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/phone-number-field.component.tsx +90 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/radio-field.component.tsx +41 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/range-date-picker.component.tsx +71 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/rich-text-editor.component.tsx +91 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/search-field.component.tsx +34 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/select-field.component.tsx +231 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/single-check-field.component.tsx +42 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/single-select-field.component.tsx +82 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/string-number-field.component.tsx +68 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/switch-field.component.tsx +61 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/text-area-field.component.tsx +62 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/text-area-with-file.component.tsx +142 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/text-field.component.tsx +80 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/tiny-editor.component.tsx +51 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/upload-profile-picture.component.tsx +103 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/components/upload-video-file.component.tsx +86 -0
- package/templates/ui/shadcn/src/components/common/fields/assets/interface/input-props.type.ts +198 -0
- package/templates/ui/shadcn/src/components/common/fields/cusInputField.component.tsx +52 -0
- package/templates/ui/shadcn/src/components/common/pagination/pagination.component.tsx +68 -0
- package/templates/ui/shadcn/src/components/ui/avatar.component.tsx +37 -0
- package/templates/ui/shadcn/src/components/ui/badge.component.tsx +28 -0
- package/templates/ui/shadcn/src/components/ui/button.component.tsx +52 -0
- package/templates/ui/shadcn/src/components/ui/card.component.tsx +46 -0
- package/templates/ui/shadcn/src/components/ui/checkbox.component.tsx +25 -0
- package/templates/ui/shadcn/src/components/ui/dialog.component.tsx +98 -0
- package/templates/ui/shadcn/src/components/ui/dropdown-menu.component.tsx +163 -0
- package/templates/ui/shadcn/src/components/ui/form.component.tsx +110 -0
- package/templates/ui/shadcn/src/components/ui/input-otp.component.tsx +64 -0
- package/templates/ui/shadcn/src/components/ui/input.component.tsx +23 -0
- package/templates/ui/shadcn/src/components/ui/label.component.tsx +23 -0
- package/templates/ui/shadcn/src/components/ui/popover.component.tsx +27 -0
- package/templates/ui/shadcn/src/components/ui/progress.component.tsx +22 -0
- package/templates/ui/shadcn/src/components/ui/radio-group.component.tsx +33 -0
- package/templates/ui/shadcn/src/components/ui/scroll-area.component.tsx +37 -0
- package/templates/ui/shadcn/src/components/ui/select.component.tsx +139 -0
- package/templates/ui/shadcn/src/components/ui/separator.component.tsx +23 -0
- package/templates/ui/shadcn/src/components/ui/sheet.component.tsx +89 -0
- package/templates/ui/shadcn/src/components/ui/switch.component.tsx +26 -0
- package/templates/ui/shadcn/src/components/ui/table.component.tsx +71 -0
- package/templates/ui/shadcn/src/components/ui/tabs.component.tsx +52 -0
- package/templates/ui/shadcn/src/components/ui/textarea.component.tsx +20 -0
- package/templates/ui/shadcn/src/components/ui/tooltip.component.tsx +25 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { DialogWrapper } from '@/components/common/dialog/dialog-wrapper.component';
|
|
5
|
+
import { ActionButton } from '@/components/common/button/action-button.component';
|
|
6
|
+
import { Pagination } from '@/components/common/pagination/pagination.component';
|
|
7
|
+
import { Edit, Trash2, Plus, Info, AlertTriangle, CheckCircle } from 'lucide-react';
|
|
8
|
+
import { Button } from '@/components/ui/button.component';
|
|
9
|
+
|
|
10
|
+
const sampleItems = Array.from({ length: 25 }, (_, i) => ({
|
|
11
|
+
id: i + 1,
|
|
12
|
+
title: `Item ${i + 1}`,
|
|
13
|
+
status: ['Active', 'Pending', 'Archived'][i % 3],
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
export default function DialogExamplePage() {
|
|
17
|
+
const [basicOpen, setBasicOpen] = useState(false);
|
|
18
|
+
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
19
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
20
|
+
const perPage = 5;
|
|
21
|
+
const totalPages = Math.ceil(sampleItems.length / perPage);
|
|
22
|
+
const paginatedItems = sampleItems.slice((currentPage - 1) * perPage, currentPage * perPage);
|
|
23
|
+
|
|
24
|
+
const variants = [
|
|
25
|
+
{ variant: 'default' as const, icon: Info, label: 'Info', desc: 'Default variant' },
|
|
26
|
+
{ variant: 'destructive' as const, icon: Trash2, label: 'Delete', desc: 'Destructive variant' },
|
|
27
|
+
{ variant: 'outline' as const, icon: Edit, label: 'Edit', desc: 'Outline variant' },
|
|
28
|
+
{ variant: 'secondary' as const, icon: CheckCircle, label: 'Confirm', desc: 'Secondary variant' },
|
|
29
|
+
{ variant: 'ghost' as const, icon: Plus, label: 'Add', desc: 'Ghost variant' },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className="space-y-8">
|
|
34
|
+
<div>
|
|
35
|
+
<h1 className="text-2xl font-bold">Dialog, ActionButton & Pagination Demo</h1>
|
|
36
|
+
<p className="text-muted-foreground mt-1">Reusable DialogWrapper with sticky header, ActionButton variants with tooltips, and pagination.</p>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<section className="space-y-4">
|
|
40
|
+
<h2 className="text-lg font-semibold">ActionButton Variants</h2>
|
|
41
|
+
<div className="flex flex-wrap gap-3">
|
|
42
|
+
{variants.map((v) => (
|
|
43
|
+
<ActionButton
|
|
44
|
+
key={v.variant}
|
|
45
|
+
variant={v.variant}
|
|
46
|
+
icon={<v.icon className="h-4 w-4" />}
|
|
47
|
+
buttonContent={v.label}
|
|
48
|
+
tooltipContent={v.desc}
|
|
49
|
+
/>
|
|
50
|
+
))}
|
|
51
|
+
</div>
|
|
52
|
+
</section>
|
|
53
|
+
|
|
54
|
+
<section className="space-y-4">
|
|
55
|
+
<h2 className="text-lg font-semibold">ActionButton with Loading</h2>
|
|
56
|
+
<ActionButton
|
|
57
|
+
buttonContent="Save Changes"
|
|
58
|
+
isPending
|
|
59
|
+
variant="default"
|
|
60
|
+
/>
|
|
61
|
+
<ActionButton
|
|
62
|
+
buttonContent="Deleting..."
|
|
63
|
+
isPending
|
|
64
|
+
variant="destructive"
|
|
65
|
+
loadingContent="Please wait..."
|
|
66
|
+
/>
|
|
67
|
+
</section>
|
|
68
|
+
|
|
69
|
+
<section className="space-y-4">
|
|
70
|
+
<h2 className="text-lg font-semibold">DialogWrapper</h2>
|
|
71
|
+
<div className="flex flex-wrap gap-3">
|
|
72
|
+
<ActionButton buttonContent="Open Basic Dialog" variant="default" icon={<Info className="h-4 w-4" />} handleOpen={() => setBasicOpen(true)} />
|
|
73
|
+
<ActionButton buttonContent="Confirm Action" variant="destructive" icon={<AlertTriangle className="h-4 w-4" />} handleOpen={() => setConfirmOpen(true)} />
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
<DialogWrapper open={basicOpen} setOpen={setBasicOpen} title="Basic Dialog" description="This is a reusable dialog with a sticky header and scrollable content.">
|
|
77
|
+
<p className="text-muted-foreground">This dialog demonstrates the DialogWrapper component. The header stays fixed while content scrolls.</p>
|
|
78
|
+
<div className="mt-4 space-y-2">
|
|
79
|
+
{Array.from({ length: 20 }, (_, i) => (
|
|
80
|
+
<p key={i} className="text-sm text-muted-foreground">Scrollable content line {i + 1}.</p>
|
|
81
|
+
))}
|
|
82
|
+
</div>
|
|
83
|
+
</DialogWrapper>
|
|
84
|
+
|
|
85
|
+
<DialogWrapper
|
|
86
|
+
open={confirmOpen} setOpen={setConfirmOpen}
|
|
87
|
+
title="Confirm Deletion"
|
|
88
|
+
description="This action cannot be undone."
|
|
89
|
+
footer={
|
|
90
|
+
<div className="flex gap-2 ml-auto">
|
|
91
|
+
<Button variant="outline" onClick={() => setConfirmOpen(false)}>Cancel</Button>
|
|
92
|
+
<Button variant="destructive" onClick={() => { alert('Confirmed!'); setConfirmOpen(false); }}>
|
|
93
|
+
Delete
|
|
94
|
+
</Button>
|
|
95
|
+
</div>
|
|
96
|
+
}
|
|
97
|
+
>
|
|
98
|
+
<p className="text-muted-foreground">Are you sure you want to proceed with this action?</p>
|
|
99
|
+
</DialogWrapper>
|
|
100
|
+
</section>
|
|
101
|
+
|
|
102
|
+
<section className="space-y-4">
|
|
103
|
+
<h2 className="text-lg font-semibold">Pagination</h2>
|
|
104
|
+
<div className="space-y-2">
|
|
105
|
+
{paginatedItems.map((item) => (
|
|
106
|
+
<div key={item.id} className="flex items-center justify-between p-3 border rounded-lg">
|
|
107
|
+
<span className="font-medium">{item.title}</span>
|
|
108
|
+
<span className={`text-xs px-2 py-1 rounded ${
|
|
109
|
+
item.status === 'Active' ? 'bg-green-100 text-green-700' :
|
|
110
|
+
item.status === 'Pending' ? 'bg-yellow-100 text-yellow-700' :
|
|
111
|
+
'bg-gray-100 text-gray-700'
|
|
112
|
+
}`}>
|
|
113
|
+
{item.status}
|
|
114
|
+
</span>
|
|
115
|
+
</div>
|
|
116
|
+
))}
|
|
117
|
+
</div>
|
|
118
|
+
<Pagination currentPage={currentPage} totalPages={totalPages} setCurrentPage={setCurrentPage} />
|
|
119
|
+
</section>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useForm } from 'react-hook-form';
|
|
4
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { CustomField } from '@/components/common/fields/cusInputField.component';
|
|
7
|
+
import { ActionButton } from '@/components/common/button/action-button.component';
|
|
8
|
+
|
|
9
|
+
const formSchema = z.object({
|
|
10
|
+
fullName: z.string().min(2, 'Name must be at least 2 characters'),
|
|
11
|
+
bio: z.string().optional(),
|
|
12
|
+
age: z.coerce.number().min(1, 'Required').max(150),
|
|
13
|
+
phone: z.string().optional(),
|
|
14
|
+
password: z.string().min(6, 'Min 6 characters'),
|
|
15
|
+
role: z.string().min(1, 'Select a role'),
|
|
16
|
+
notify: z.boolean().optional(),
|
|
17
|
+
acceptTerms: z.boolean().refine((v) => v === true, 'You must accept'),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
type FormValues = z.infer<typeof formSchema>;
|
|
21
|
+
|
|
22
|
+
export default function FormExamplePage() {
|
|
23
|
+
const form = useForm<FormValues>({
|
|
24
|
+
resolver: zodResolver(formSchema),
|
|
25
|
+
defaultValues: { fullName: '', bio: '', age: 0, phone: '', password: '', role: '', notify: false, acceptTerms: false },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const onSubmit = (data: FormValues) => alert(JSON.stringify(data, null, 2));
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="space-y-8">
|
|
32
|
+
<div>
|
|
33
|
+
<h1 className="text-2xl font-bold">CustomField Form Demo</h1>
|
|
34
|
+
<p className="text-muted-foreground mt-1">All field types with react-hook-form + zod validation.</p>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 max-w-xl">
|
|
38
|
+
<CustomField.Text
|
|
39
|
+
form={form}
|
|
40
|
+
name="fullName"
|
|
41
|
+
labelName="Full Name"
|
|
42
|
+
placeholder="Enter your name"
|
|
43
|
+
required
|
|
44
|
+
/>
|
|
45
|
+
|
|
46
|
+
<CustomField.TextArea
|
|
47
|
+
form={form}
|
|
48
|
+
name="bio"
|
|
49
|
+
labelName="Biography"
|
|
50
|
+
placeholder="Tell us about yourself"
|
|
51
|
+
rows={3}
|
|
52
|
+
/>
|
|
53
|
+
|
|
54
|
+
<CustomField.Number
|
|
55
|
+
form={form}
|
|
56
|
+
name="age"
|
|
57
|
+
labelName="Age"
|
|
58
|
+
numberType="integer"
|
|
59
|
+
placeholder="Enter age"
|
|
60
|
+
required
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
<CustomField.StringNumber
|
|
64
|
+
form={form}
|
|
65
|
+
name="phone"
|
|
66
|
+
labelName="Phone Number"
|
|
67
|
+
placeholder="Enter digits only"
|
|
68
|
+
/>
|
|
69
|
+
|
|
70
|
+
<CustomField.Password
|
|
71
|
+
form={form}
|
|
72
|
+
name="password"
|
|
73
|
+
labelName="Password"
|
|
74
|
+
placeholder="Min 6 characters"
|
|
75
|
+
required
|
|
76
|
+
mode="normal"
|
|
77
|
+
/>
|
|
78
|
+
|
|
79
|
+
<CustomField.SingleSelectField
|
|
80
|
+
form={form}
|
|
81
|
+
name="role"
|
|
82
|
+
labelName="Role"
|
|
83
|
+
placeholder="Select role"
|
|
84
|
+
options={['Admin', 'Editor', 'Viewer']}
|
|
85
|
+
required
|
|
86
|
+
/>
|
|
87
|
+
|
|
88
|
+
<CustomField.SwitchField
|
|
89
|
+
form={form}
|
|
90
|
+
name="notify"
|
|
91
|
+
labelName="Email Notifications"
|
|
92
|
+
description="Receive email updates"
|
|
93
|
+
border
|
|
94
|
+
/>
|
|
95
|
+
|
|
96
|
+
<CustomField.SingleCheckField
|
|
97
|
+
form={form}
|
|
98
|
+
name="acceptTerms"
|
|
99
|
+
labelName="I accept the terms and conditions"
|
|
100
|
+
required
|
|
101
|
+
/>
|
|
102
|
+
|
|
103
|
+
<ActionButton type="submit" buttonContent="Submit" btnSize="lg" />
|
|
104
|
+
</form>
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
import { ArrowLeft } from 'lucide-react';
|
|
5
|
+
import { TooltipProvider } from '@/components/ui/tooltip.component';
|
|
6
|
+
|
|
7
|
+
export default function ExamplesLayout({ children }: { children: React.ReactNode }) {
|
|
8
|
+
return (
|
|
9
|
+
<TooltipProvider>
|
|
10
|
+
<div className="min-h-screen bg-background">
|
|
11
|
+
<header className="sticky top-0 z-10 border-b bg-background/95 backdrop-blur">
|
|
12
|
+
<div className="max-w-6xl mx-auto px-4 h-14 flex items-center gap-4">
|
|
13
|
+
<Link href="/examples" className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors">
|
|
14
|
+
<ArrowLeft className="h-4 w-4" />
|
|
15
|
+
Examples
|
|
16
|
+
</Link>
|
|
17
|
+
<span className="text-sm font-medium">Nexstruct — Component Demo</span>
|
|
18
|
+
</div>
|
|
19
|
+
</header>
|
|
20
|
+
<main className="max-w-6xl mx-auto p-6">{children}</main>
|
|
21
|
+
</div>
|
|
22
|
+
</TooltipProvider>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
|
|
5
|
+
const examples = [
|
|
6
|
+
{ href: '/examples/form', title: 'Form Components', desc: 'All CustomField field types with react-hook-form validation' },
|
|
7
|
+
{ href: '/examples/table', title: 'DynamicTable', desc: 'Config-driven table with loading, empty, and checkbox selection states' },
|
|
8
|
+
{ href: '/examples/dialog', title: 'Dialog, ActionButton & Pagination', desc: 'Reusable dialog wrapper, action button variants, and page navigation' },
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export default function ExamplesPage() {
|
|
12
|
+
return (
|
|
13
|
+
<div className="min-h-screen p-8 max-w-4xl mx-auto">
|
|
14
|
+
<h1 className="text-3xl font-bold mb-2">Component Examples</h1>
|
|
15
|
+
<p className="text-muted-foreground mb-8">
|
|
16
|
+
Interactive demos of the shadcn/ui custom component system.
|
|
17
|
+
</p>
|
|
18
|
+
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
19
|
+
{examples.map((ex) => (
|
|
20
|
+
<Link key={ex.href} href={ex.href}
|
|
21
|
+
className="block p-6 rounded-lg border hover:border-primary hover:shadow-md transition-all"
|
|
22
|
+
>
|
|
23
|
+
<h2 className="text-lg font-semibold mb-2">{ex.title}</h2>
|
|
24
|
+
<p className="text-sm text-muted-foreground">{ex.desc}</p>
|
|
25
|
+
</Link>
|
|
26
|
+
))}
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { DynamicTable, type TableColumn } from '@/components/common/DynamicTable/dynamic-table.component';
|
|
5
|
+
import { Pagination } from '@/components/common/pagination/pagination.component';
|
|
6
|
+
import { LimitField } from '@/components/common/fields/assets/components/limit-field.component';
|
|
7
|
+
import { ActionButton } from '@/components/common/button/action-button.component';
|
|
8
|
+
|
|
9
|
+
interface User {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
email: string;
|
|
13
|
+
role: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const allUsers: User[] = Array.from({ length: 53 }, (_, i) => ({
|
|
17
|
+
id: String(i + 1),
|
|
18
|
+
name: `User ${i + 1}`,
|
|
19
|
+
email: `user${i + 1}@example.com`,
|
|
20
|
+
role: i % 3 === 0 ? 'Admin' : i % 3 === 1 ? 'Editor' : 'Viewer',
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const columns: TableColumn[] = [
|
|
24
|
+
{ key: 'name', header: 'Name' },
|
|
25
|
+
{ key: 'email', header: 'Email' },
|
|
26
|
+
{ key: 'role', header: 'Role', render: (item: User) => (
|
|
27
|
+
<span className="px-2 py-1 text-xs rounded bg-primary/10 text-primary capitalize">{item.role}</span>
|
|
28
|
+
)},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export default function TableExamplePage() {
|
|
32
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
33
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
34
|
+
const [limit, setLimit] = useState('10');
|
|
35
|
+
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
36
|
+
|
|
37
|
+
const perPage = Number(limit);
|
|
38
|
+
const totalPages = Math.ceil(allUsers.length / perPage);
|
|
39
|
+
const paginatedData = allUsers.slice((currentPage - 1) * perPage, currentPage * perPage);
|
|
40
|
+
|
|
41
|
+
const refresh = () => {
|
|
42
|
+
setIsLoading(true);
|
|
43
|
+
setTimeout(() => setIsLoading(false), 1000);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div className="space-y-8">
|
|
48
|
+
<div>
|
|
49
|
+
<h1 className="text-2xl font-bold">DynamicTable Demo</h1>
|
|
50
|
+
<p className="text-muted-foreground mt-1">Config-driven table with loading state, checkbox selection, and pagination.</p>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div className="flex items-center justify-between">
|
|
54
|
+
<div className="flex items-center gap-4">
|
|
55
|
+
<LimitField setLimit={(v) => { setLimit(v); setCurrentPage(1); }} totalItems={allUsers.length} />
|
|
56
|
+
<ActionButton buttonContent="Refresh" onClick={refresh} variant="outline" btnSize="sm" />
|
|
57
|
+
</div>
|
|
58
|
+
{selectedIds.length > 0 && (
|
|
59
|
+
<p className="text-sm text-muted-foreground">{selectedIds.length} selected</p>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<DynamicTable
|
|
64
|
+
data={paginatedData}
|
|
65
|
+
isLoading={isLoading}
|
|
66
|
+
config={{ columns }}
|
|
67
|
+
currentPage={currentPage}
|
|
68
|
+
setCurrentPage={setCurrentPage}
|
|
69
|
+
isCheckBox
|
|
70
|
+
selectedIds={selectedIds}
|
|
71
|
+
setSelectedIds={setSelectedIds}
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
<Pagination currentPage={currentPage} totalPages={totalPages} setCurrentPage={setCurrentPage} />
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import Link from 'next/link';
|
|
2
|
+
|
|
3
|
+
export default function Home() {
|
|
4
|
+
return (
|
|
5
|
+
<main className="flex min-h-screen flex-col items-center justify-center p-24">
|
|
6
|
+
<h1 className="text-4xl font-bold">Nexstruct</h1>
|
|
7
|
+
<p className="mt-4 text-lg text-muted-foreground">
|
|
8
|
+
Your project is ready. Start building.
|
|
9
|
+
</p>
|
|
10
|
+
<div className="mt-8 flex gap-4">
|
|
11
|
+
<Link
|
|
12
|
+
href="/examples"
|
|
13
|
+
className="inline-flex items-center justify-center rounded-md bg-primary px-6 py-3 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
|
|
14
|
+
>
|
|
15
|
+
View Component Examples
|
|
16
|
+
</Link>
|
|
17
|
+
</div>
|
|
18
|
+
</main>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Table, TableBody, TableCell, TableHead, TableHeader, TableRow, TableCaption,
|
|
5
|
+
} from '@/components/ui/table.component';
|
|
6
|
+
import { Loader2 } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
export type TableColumn = {
|
|
9
|
+
key: string;
|
|
10
|
+
header: string;
|
|
11
|
+
className?: string;
|
|
12
|
+
render?: (item: any, index: number) => React.ReactNode;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type TableConfig = {
|
|
16
|
+
columns: TableColumn[];
|
|
17
|
+
emptyMessage?: string;
|
|
18
|
+
showPagination?: boolean;
|
|
19
|
+
rowClassName?: (item: any) => string;
|
|
20
|
+
renderExpandableRow?: (item: any, index: number) => React.ReactNode;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
interface Pagination {
|
|
24
|
+
page: number;
|
|
25
|
+
total: number;
|
|
26
|
+
perPage?: number;
|
|
27
|
+
totalPages: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface DynamicTableProps {
|
|
31
|
+
isLoading: boolean;
|
|
32
|
+
pagination?: Pagination;
|
|
33
|
+
currentPage: number;
|
|
34
|
+
setCurrentPage: (page: number) => void;
|
|
35
|
+
config: TableConfig;
|
|
36
|
+
data: any;
|
|
37
|
+
isCheckBox?: boolean;
|
|
38
|
+
selectedIds?: string[];
|
|
39
|
+
setSelectedIds?: (ids: string[]) => void;
|
|
40
|
+
setSelectObject?: (data: any[]) => void;
|
|
41
|
+
renderExpandedRow?: (item: any, index: number) => React.ReactNode;
|
|
42
|
+
pageName?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function DynamicTable({
|
|
46
|
+
data, isLoading, pagination, currentPage, setCurrentPage, config,
|
|
47
|
+
selectedIds = [], setSelectedIds = () => {}, setSelectObject = () => {},
|
|
48
|
+
isCheckBox = false, renderExpandedRow,
|
|
49
|
+
}: DynamicTableProps) {
|
|
50
|
+
const isEmpty = !isLoading && (!data || data.length === 0);
|
|
51
|
+
|
|
52
|
+
const isRowSelected = (id: string) => selectedIds?.includes(id);
|
|
53
|
+
|
|
54
|
+
const toggleRowSelection = (id: string) => {
|
|
55
|
+
const newSelectedIds = isRowSelected(id)
|
|
56
|
+
? selectedIds.filter((item) => item !== id)
|
|
57
|
+
: [...selectedIds, id];
|
|
58
|
+
setSelectedIds(newSelectedIds);
|
|
59
|
+
setSelectObject(data.filter((item: any) => newSelectedIds.includes(item.id)));
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const toggleSelectAll = () => {
|
|
63
|
+
const allSelected = data?.every((item: any) => isRowSelected(item.id));
|
|
64
|
+
const newSelectedIds = allSelected ? [] : data.map((item: any) => item.id);
|
|
65
|
+
setSelectedIds(newSelectedIds);
|
|
66
|
+
setSelectObject(allSelected ? [] : data);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="rounded-md border">
|
|
71
|
+
<Table>
|
|
72
|
+
{pagination && pagination.totalPages > 0 && (
|
|
73
|
+
<TableCaption className="p-4">
|
|
74
|
+
{/* Pagination component rendered separately */}
|
|
75
|
+
</TableCaption>
|
|
76
|
+
)}
|
|
77
|
+
<TableHeader className="bg-muted/50">
|
|
78
|
+
<TableRow>
|
|
79
|
+
{isCheckBox && (
|
|
80
|
+
<TableHead className="w-10 pl-4">
|
|
81
|
+
<input
|
|
82
|
+
type="checkbox"
|
|
83
|
+
checked={data?.length > 0 && data?.every((item: any) => isRowSelected(item.id))}
|
|
84
|
+
onChange={toggleSelectAll}
|
|
85
|
+
className="h-4 w-4"
|
|
86
|
+
/>
|
|
87
|
+
</TableHead>
|
|
88
|
+
)}
|
|
89
|
+
{config.columns.map((column) => (
|
|
90
|
+
<TableHead key={column.key} className={column.className}>
|
|
91
|
+
{column.header}
|
|
92
|
+
</TableHead>
|
|
93
|
+
))}
|
|
94
|
+
</TableRow>
|
|
95
|
+
</TableHeader>
|
|
96
|
+
<TableBody>
|
|
97
|
+
{isLoading ? (
|
|
98
|
+
<TableRow>
|
|
99
|
+
<TableCell colSpan={config.columns.length + (isCheckBox ? 1 : 0)} className="h-64 text-center">
|
|
100
|
+
<div className="flex items-center justify-center">
|
|
101
|
+
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
102
|
+
</div>
|
|
103
|
+
</TableCell>
|
|
104
|
+
</TableRow>
|
|
105
|
+
) : isEmpty ? (
|
|
106
|
+
<TableRow>
|
|
107
|
+
<TableCell colSpan={config.columns.length + (isCheckBox ? 1 : 0)} className="h-64 text-center text-muted-foreground">
|
|
108
|
+
{config.emptyMessage || 'No data available'}
|
|
109
|
+
</TableCell>
|
|
110
|
+
</TableRow>
|
|
111
|
+
) : (
|
|
112
|
+
data?.map((row: any, rowIndex: number) => (
|
|
113
|
+
<TableRow key={rowIndex} className={config.rowClassName?.(row)}>
|
|
114
|
+
{isCheckBox && (
|
|
115
|
+
<TableCell className="pl-4">
|
|
116
|
+
<input
|
|
117
|
+
type="checkbox"
|
|
118
|
+
checked={isRowSelected(row.id)}
|
|
119
|
+
onChange={() => toggleRowSelection(row.id)}
|
|
120
|
+
className="h-4 w-4"
|
|
121
|
+
/>
|
|
122
|
+
</TableCell>
|
|
123
|
+
)}
|
|
124
|
+
{config.columns.map((col, colIndex) => (
|
|
125
|
+
<TableCell key={colIndex} className={col.className}>
|
|
126
|
+
{col.render ? col.render(row, rowIndex) : row[col.key]}
|
|
127
|
+
</TableCell>
|
|
128
|
+
))}
|
|
129
|
+
</TableRow>
|
|
130
|
+
))
|
|
131
|
+
)}
|
|
132
|
+
</TableBody>
|
|
133
|
+
</Table>
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Button } from '@/components/ui/button.component';
|
|
4
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip.component';
|
|
5
|
+
import { cn } from '@/lib/utils.util';
|
|
6
|
+
import { Loader2 } from 'lucide-react';
|
|
7
|
+
import { forwardRef } from 'react';
|
|
8
|
+
|
|
9
|
+
interface ActionButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
10
|
+
type?: 'submit' | 'button' | 'reset';
|
|
11
|
+
btnSize?: 'default' | 'sm' | 'lg' | 'icon';
|
|
12
|
+
btnStyle?: string;
|
|
13
|
+
tooltipStyle?: string;
|
|
14
|
+
variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link';
|
|
15
|
+
tooltipContent?: string;
|
|
16
|
+
buttonContent?: React.ReactNode;
|
|
17
|
+
icon?: React.ReactNode;
|
|
18
|
+
lastIcon?: React.ReactNode;
|
|
19
|
+
isPending?: boolean;
|
|
20
|
+
handleOpen?: (e?: React.MouseEvent<HTMLButtonElement>) => void;
|
|
21
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
22
|
+
loadingContent?: React.ReactNode;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ActionButton = forwardRef<HTMLButtonElement, ActionButtonProps>(
|
|
26
|
+
({ type = 'button', btnSize = 'default', btnStyle, tooltipStyle,
|
|
27
|
+
variant = 'default', tooltipContent, buttonContent, icon, lastIcon,
|
|
28
|
+
isPending = false, handleOpen, side = 'top', loadingContent,
|
|
29
|
+
disabled = false, ...props }, ref) => {
|
|
30
|
+
const buttonEl = (
|
|
31
|
+
<Button
|
|
32
|
+
ref={ref}
|
|
33
|
+
type={type}
|
|
34
|
+
size={btnSize}
|
|
35
|
+
onClick={handleOpen}
|
|
36
|
+
variant={variant}
|
|
37
|
+
disabled={isPending || disabled}
|
|
38
|
+
className={cn(
|
|
39
|
+
'capitalize cursor-pointer',
|
|
40
|
+
(isPending || disabled) && 'opacity-60 cursor-not-allowed',
|
|
41
|
+
btnStyle,
|
|
42
|
+
(icon) && (buttonContent || isPending) && 'gap-x-3',
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
>
|
|
46
|
+
{!isPending && icon}
|
|
47
|
+
{(buttonContent || isPending) && (
|
|
48
|
+
<span>{isPending ? (loadingContent ?? buttonContent) : buttonContent}</span>
|
|
49
|
+
)}
|
|
50
|
+
{!isPending && lastIcon}
|
|
51
|
+
{isPending && <Loader2 className="ml-2 h-4 w-4 animate-spin" />}
|
|
52
|
+
</Button>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
if (!tooltipContent) return buttonEl;
|
|
56
|
+
return (
|
|
57
|
+
<Tooltip>
|
|
58
|
+
<TooltipTrigger asChild>{buttonEl}</TooltipTrigger>
|
|
59
|
+
<TooltipContent side={side} className={cn('bg-muted-foreground text-primary-foreground', tooltipStyle)}>
|
|
60
|
+
<p className="text-[10px]">{tooltipContent}</p>
|
|
61
|
+
</TooltipContent>
|
|
62
|
+
</Tooltip>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
ActionButton.displayName = 'ActionButton';
|
|
67
|
+
|
|
68
|
+
export { ActionButton };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Dialog, DialogClose, DialogContent, DialogDescription,
|
|
5
|
+
DialogFooter, DialogHeader, DialogTitle, DialogTrigger,
|
|
6
|
+
} from '@/components/ui/dialog.component';
|
|
7
|
+
import { cn } from '@/lib/utils.util';
|
|
8
|
+
import { X } from 'lucide-react';
|
|
9
|
+
import type { ReactNode } from 'react';
|
|
10
|
+
|
|
11
|
+
interface DialogWrapperProps {
|
|
12
|
+
style?: string;
|
|
13
|
+
open: boolean;
|
|
14
|
+
setOpen: (open: boolean) => void;
|
|
15
|
+
triggerContent?: ReactNode;
|
|
16
|
+
title?: ReactNode;
|
|
17
|
+
description?: string;
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
closer?: boolean;
|
|
20
|
+
footer?: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function DialogWrapper({
|
|
24
|
+
open, setOpen, triggerContent, title, description,
|
|
25
|
+
children, closer = true, style, footer,
|
|
26
|
+
}: DialogWrapperProps) {
|
|
27
|
+
return (
|
|
28
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
29
|
+
{triggerContent && <DialogTrigger asChild>{triggerContent}</DialogTrigger>}
|
|
30
|
+
<DialogContent
|
|
31
|
+
className={cn(
|
|
32
|
+
'flex flex-col max-h-[90%] lg:max-h-[80%] overflow-hidden',
|
|
33
|
+
style,
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
{closer && (
|
|
37
|
+
<DialogClose asChild>
|
|
38
|
+
<button className="cursor-pointer absolute top-3 right-3 rounded-sm opacity-70 hover:opacity-100 focus:outline-none z-10">
|
|
39
|
+
<X className="h-5 w-5" />
|
|
40
|
+
<span className="sr-only">Close</span>
|
|
41
|
+
</button>
|
|
42
|
+
</DialogClose>
|
|
43
|
+
)}
|
|
44
|
+
<DialogHeader
|
|
45
|
+
className={cn(
|
|
46
|
+
'shrink-0 capitalize bg-background',
|
|
47
|
+
title || description ? 'py-3 px-2 border-b' : 'pt-2 bg-transparent',
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
50
|
+
<DialogTitle className={title ? 'block' : 'hidden'}>{title || ''}</DialogTitle>
|
|
51
|
+
<DialogDescription className={description ? 'block' : 'hidden'}>{description || ''}</DialogDescription>
|
|
52
|
+
</DialogHeader>
|
|
53
|
+
<div className="flex-1 overflow-y-auto px-4 py-4">{children}</div>
|
|
54
|
+
{footer && <DialogFooter className="border-t px-4 py-3">{footer}</DialogFooter>}
|
|
55
|
+
</DialogContent>
|
|
56
|
+
</Dialog>
|
|
57
|
+
);
|
|
58
|
+
}
|