create-ej-app 3.2.0 → 4.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/cli.js +5 -1
- package/cli.ts +40 -33
- package/package.json +4 -4
- package/template/base/.env.example +10 -9
- package/template/base/.eslintrc.cjs +17 -15
- package/template/base/components.json +1 -1
- package/template/base/drizzle.config.ts +11 -0
- package/template/base/next.config.mjs +1 -12
- package/template/base/package.json +63 -56
- package/template/base/postcss.config.mjs +8 -0
- package/template/base/prettier.config.mjs +11 -0
- package/template/base/public/vercel.svg +1 -0
- package/template/base/src/app/(app)/dashboard/layout.tsx +14 -0
- package/template/base/src/app/(app)/dashboard/page.tsx +0 -7
- package/template/base/src/app/(app)/layout.tsx +55 -20
- package/template/base/src/app/(app)/roles/[id]/actions.ts +59 -0
- package/template/base/src/app/(app)/roles/[id]/page.tsx +46 -0
- package/template/base/src/app/(app)/roles/[id]/render.tsx +264 -0
- package/template/base/src/app/(app)/roles/[id]/validation.ts +22 -0
- package/template/base/src/app/(app)/roles/actions.ts +33 -0
- package/template/base/src/app/(app)/roles/page.tsx +52 -0
- package/template/base/src/app/(app)/roles/render.tsx +80 -0
- package/template/base/src/app/(app)/settings/actions.ts +11 -12
- package/template/base/src/app/(app)/settings/layout.tsx +14 -0
- package/template/base/src/app/(app)/settings/page.tsx +1 -10
- package/template/base/src/app/(app)/settings/render.tsx +11 -17
- package/template/base/src/app/(app)/settings/validation.ts +5 -0
- package/template/base/src/app/(app)/tasks/[id]/actions.ts +69 -124
- package/template/base/src/app/(app)/tasks/[id]/page.tsx +37 -3
- package/template/base/src/app/(app)/tasks/[id]/render.tsx +162 -93
- package/template/base/src/app/(app)/tasks/[id]/validation.ts +18 -0
- package/template/base/src/app/(app)/tasks/actions.ts +34 -64
- package/template/base/src/app/(app)/tasks/page.tsx +15 -9
- package/template/base/src/app/(app)/tasks/render.tsx +28 -6
- package/template/base/src/app/(app)/tasks/statusEnum.ts +3 -0
- package/template/base/src/app/(app)/users/[id]/actions.ts +47 -62
- package/template/base/src/app/(app)/users/[id]/page.tsx +15 -8
- package/template/base/src/app/(app)/users/[id]/render.tsx +119 -34
- package/template/base/src/app/(app)/users/[id]/validation.ts +18 -0
- package/template/base/src/app/(app)/users/actions.ts +28 -49
- package/template/base/src/app/(app)/users/page.tsx +22 -9
- package/template/base/src/app/(app)/users/render.tsx +47 -3
- package/template/base/src/app/(auth)/layout.tsx +7 -0
- package/template/base/src/app/(auth)/login/page.tsx +2 -2
- package/template/base/src/app/(auth)/login/render.tsx +146 -0
- package/template/base/src/app/(marketing)/layout.tsx +1 -1
- package/template/base/src/app/(marketing)/page.tsx +8 -7
- package/template/base/src/app/favicon.ico +0 -0
- package/template/base/src/app/layout.tsx +14 -17
- package/template/base/src/components/app-nav-command.tsx +18 -0
- package/template/base/src/components/data-table/data-table-column-header.tsx +2 -2
- package/template/base/src/components/icons.tsx +21 -5
- package/template/base/src/components/ui/alert-dialog.tsx +1 -1
- package/template/base/src/components/ui/command.tsx +1 -1
- package/template/base/src/components/ui/dialog.tsx +1 -1
- package/template/base/src/components/ui/sonner.tsx +31 -0
- package/template/base/src/components/user-avatar.tsx +4 -2
- package/template/base/src/data-access/resource.ts +85 -0
- package/template/base/src/data-access/role-permission.ts +45 -0
- package/template/base/src/data-access/role.ts +105 -0
- package/template/base/src/data-access/task.ts +141 -0
- package/template/base/src/data-access/user.ts +141 -0
- package/template/base/src/db/index.ts +14 -0
- package/template/base/src/db/schema.ts +287 -0
- package/template/base/src/env.mjs +10 -30
- package/template/base/src/hooks/use-lock-body.tsx +2 -1
- package/template/base/src/lib/auth.ts +32 -23
- package/template/base/src/lib/currentUser.ts +8 -0
- package/template/base/src/lib/s3Client.ts +156 -0
- package/template/base/src/lib/safe-action.ts +19 -0
- package/template/base/src/lib/sortOrderEnum.ts +2 -0
- package/template/base/src/use-case/role-permission.ts +19 -0
- package/template/base/src/use-case/role.ts +110 -0
- package/template/base/src/use-case/task.ts +166 -0
- package/template/base/src/use-case/user.ts +121 -0
- package/template/base/tailwind.config.ts +5 -4
- package/template/base/tsconfig.json +3 -2
- package/template/prisma/.env.example +16 -0
- package/template/prisma/.eslintrc.cjs +24 -0
- package/template/prisma/components.json +17 -0
- package/template/prisma/next.config.mjs +15 -0
- package/template/prisma/package.json +80 -0
- package/template/prisma/public/next.svg +1 -0
- package/template/prisma/src/app/(app)/dashboard/page.tsx +19 -0
- package/template/prisma/src/app/(app)/layout.tsx +90 -0
- package/template/prisma/src/app/(app)/settings/actions.ts +18 -0
- package/template/prisma/src/app/(app)/settings/loading.tsx +28 -0
- package/template/prisma/src/app/(app)/settings/page.tsx +28 -0
- package/template/prisma/src/app/(app)/settings/render.tsx +113 -0
- package/template/prisma/src/app/(app)/tasks/[id]/actions.ts +147 -0
- package/template/prisma/src/app/(app)/tasks/[id]/page.tsx +25 -0
- package/template/prisma/src/app/(app)/tasks/[id]/render.tsx +413 -0
- package/template/prisma/src/app/(app)/tasks/actions.ts +69 -0
- package/template/prisma/src/app/(app)/tasks/page.tsx +53 -0
- package/template/prisma/src/app/(app)/tasks/render.tsx +107 -0
- package/template/prisma/src/app/(app)/users/[id]/actions.ts +83 -0
- package/template/prisma/src/app/(app)/users/[id]/page.tsx +32 -0
- package/template/prisma/src/app/(app)/users/[id]/render.tsx +240 -0
- package/template/prisma/src/app/(app)/users/actions.ts +55 -0
- package/template/prisma/src/app/(app)/users/page.tsx +49 -0
- package/template/prisma/src/app/(app)/users/render.tsx +80 -0
- package/template/prisma/src/app/(auth)/layout.tsx +20 -0
- package/template/prisma/src/app/(auth)/login/page.tsx +49 -0
- package/template/prisma/src/app/(marketing)/layout.tsx +42 -0
- package/template/prisma/src/app/(marketing)/page.tsx +96 -0
- package/template/prisma/src/app/api/auth/[...nextauth]/route.ts +7 -0
- package/template/prisma/src/app/favicon.ico +0 -0
- package/template/prisma/src/app/layout.tsx +34 -0
- package/template/prisma/src/components/app-nav-command.tsx +160 -0
- package/template/prisma/src/components/dark-mode-toggle.tsx +43 -0
- package/template/prisma/src/components/data-table/data-table-column-header.tsx +91 -0
- package/template/prisma/src/components/data-table/data-table-faceted-filter.tsx +149 -0
- package/template/prisma/src/components/data-table/data-table-loading.tsx +77 -0
- package/template/prisma/src/components/data-table/data-table-pagination.tsx +94 -0
- package/template/prisma/src/components/data-table/data-table-toolbar.tsx +78 -0
- package/template/prisma/src/components/data-table/data-table-types.ts +17 -0
- package/template/prisma/src/components/data-table/data-table-view-options.tsx +59 -0
- package/template/prisma/src/components/data-table/data-table.tsx +301 -0
- package/template/prisma/src/components/data-table/simple-data-table.tsx +109 -0
- package/template/prisma/src/components/empty-placeholder.tsx +78 -0
- package/template/prisma/src/components/heading.tsx +17 -0
- package/template/prisma/src/components/icons.tsx +85 -0
- package/template/prisma/src/components/main-nav.tsx +64 -0
- package/template/prisma/src/components/shell.tsx +13 -0
- package/template/prisma/src/components/side-nav.tsx +51 -0
- package/template/prisma/src/components/site-footer.tsx +31 -0
- package/template/prisma/src/components/system-info.tsx +36 -0
- package/template/prisma/src/components/ui/alert-dialog.tsx +141 -0
- package/template/prisma/src/components/ui/avatar.tsx +50 -0
- package/template/prisma/src/components/ui/badge.tsx +36 -0
- package/template/prisma/src/components/ui/button.tsx +56 -0
- package/template/prisma/src/components/ui/card.tsx +79 -0
- package/template/prisma/src/components/ui/checkbox.tsx +30 -0
- package/template/prisma/src/components/ui/command.tsx +155 -0
- package/template/prisma/src/components/ui/dialog.tsx +122 -0
- package/template/prisma/src/components/ui/dropdown-menu.tsx +200 -0
- package/template/prisma/src/components/ui/form.tsx +176 -0
- package/template/prisma/src/components/ui/input.tsx +25 -0
- package/template/prisma/src/components/ui/label.tsx +26 -0
- package/template/prisma/src/components/ui/popover.tsx +31 -0
- package/template/prisma/src/components/ui/select.tsx +160 -0
- package/template/prisma/src/components/ui/separator.tsx +31 -0
- package/template/prisma/src/components/ui/skeleton.tsx +15 -0
- package/template/prisma/src/components/ui/table.tsx +117 -0
- package/template/prisma/src/components/user-account-nav.tsx +60 -0
- package/template/prisma/src/components/user-avatar.tsx +25 -0
- package/template/prisma/src/env.mjs +78 -0
- package/template/prisma/src/hooks/use-debounce.ts +17 -0
- package/template/prisma/src/hooks/use-lock-body.tsx +12 -0
- package/template/prisma/src/lib/auth.ts +80 -0
- package/template/prisma/src/lib/cn.ts +4 -0
- package/template/prisma/src/lib/delay.ts +2 -0
- package/template/prisma/src/lib/formatDate.ts +11 -0
- package/template/prisma/src/lib/generateLabel.ts +5 -0
- package/template/prisma/src/lib/siteConfig.ts +4 -0
- package/template/prisma/src/styles/globals.css +76 -0
- package/template/prisma/tailwind.config.ts +80 -0
- package/template/prisma/tsconfig.json +46 -0
- /package/template/{advanced/src/styles → base/src/app}/globals.css +0 -0
- /package/template/{advanced/src/hooks/use-debounce.ts → base/src/hooks/use-debounce.tsx} +0 -0
- /package/template/{base → prisma}/postcss.config.cjs +0 -0
- /package/template/{base → prisma}/prettier.config.ts +0 -0
- /package/template/{base → prisma}/prisma/schema.prisma +0 -0
- /package/template/{base → prisma}/src/app/(auth)/login/user-auth-form.tsx +0 -0
- /package/template/{base → prisma}/src/app/api/upload/route.ts +0 -0
- /package/template/{advanced → prisma}/src/components/mobile-nav.tsx +0 -0
- /package/template/{base → prisma}/src/components/theme-provider.tsx +0 -0
- /package/template/{base → prisma}/src/components/ui/textarea.tsx +0 -0
- /package/template/{base → prisma}/src/components/ui/toast.tsx +0 -0
- /package/template/{base → prisma}/src/components/ui/toaster.tsx +0 -0
- /package/template/{base → prisma}/src/components/ui/use-toast.ts +0 -0
- /package/template/{base → prisma}/src/lib/db.ts +0 -0
- /package/template/{base → prisma}/src/lib/storageClient.ts +0 -0
- /package/template/{base → prisma}/src/styles/editor.css +0 -0
- /package/template/{base → prisma}/src/types/UnwrapPromise.ts +0 -0
- /package/template/{advanced → prisma-advanced}/.env.example +0 -0
- /package/template/{advanced → prisma-advanced}/.eslintrc.cjs +0 -0
- /package/template/{advanced → prisma-advanced}/components.json +0 -0
- /package/template/{advanced → prisma-advanced}/next.config.mjs +0 -0
- /package/template/{advanced → prisma-advanced}/package.json +0 -0
- /package/template/{advanced → prisma-advanced}/postcss.config.cjs +0 -0
- /package/template/{advanced → prisma-advanced}/prettier.config.ts +0 -0
- /package/template/{advanced → prisma-advanced}/prisma/schema.prisma +0 -0
- /package/template/{advanced → prisma-advanced}/public/next.svg +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/dashboard/page.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/layout.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/actions.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/page.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/[id]/render.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/actions.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/page.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/roles/render.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/actions.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/loading.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/page.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/settings/render.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/actions.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/page.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/[id]/render.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/actions.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/page.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/tasks/render.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/actions.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/page.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/users/[id]/render.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/users/actions.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/users/page.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(app)/users/render.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(auth)/layout.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(auth)/login/page.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(auth)/login/user-auth-form.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(marketing)/layout.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/(marketing)/page.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/api/auth/[...nextauth]/route.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/api/upload/route.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/favicon.ico +0 -0
- /package/template/{advanced → prisma-advanced}/src/app/layout.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/app-nav-command.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/dark-mode-toggle.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-column-header.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-faceted-filter.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-loading.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-pagination.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-toolbar.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-types.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table-view-options.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/data-table/data-table.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/data-table/simple-data-table.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/empty-placeholder.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/heading.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/icons.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/main-nav.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/shell.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/side-nav.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/site-footer.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/system-info.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/theme-provider.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/alert-dialog.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/avatar.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/badge.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/button.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/card.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/checkbox.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/command.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/dialog.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/dropdown-menu.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/form.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/input.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/label.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/popover.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/select.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/separator.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/skeleton.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/table.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/textarea.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/toast.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/toaster.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/ui/use-toast.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/user-account-nav.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/components/user-avatar.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/env.mjs +0 -0
- /package/template/{base → prisma-advanced}/src/hooks/use-debounce.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/hooks/use-lock-body.tsx +0 -0
- /package/template/{advanced → prisma-advanced}/src/lib/auth.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/lib/cn.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/lib/db.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/lib/delay.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/lib/formatDate.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/lib/generateLabel.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/lib/siteConfig.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/lib/storageClient.ts +0 -0
- /package/template/{advanced → prisma-advanced}/src/styles/editor.css +0 -0
- /package/template/{base → prisma-advanced}/src/styles/globals.css +0 -0
- /package/template/{advanced → prisma-advanced}/src/types/UnwrapPromise.ts +0 -0
- /package/template/{advanced → prisma-advanced}/tailwind.config.ts +0 -0
- /package/template/{advanced → prisma-advanced}/tsconfig.json +0 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import { Icons } from '@/components/icons'
|
|
4
|
+
import { cn } from '@/lib/cn'
|
|
5
|
+
|
|
6
|
+
interface EmptyPlaceholderProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
7
|
+
|
|
8
|
+
export function EmptyPlaceholder({
|
|
9
|
+
className,
|
|
10
|
+
children,
|
|
11
|
+
...props
|
|
12
|
+
}: EmptyPlaceholderProps) {
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className={cn(
|
|
16
|
+
'flex min-h-[400px] flex-col items-center justify-center rounded-md border border-dashed p-8 text-center animate-in fade-in-50',
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
>
|
|
21
|
+
<div className="mx-auto flex max-w-[420px] flex-col items-center justify-center text-center">
|
|
22
|
+
{children}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface EmptyPlaceholderIconProps
|
|
29
|
+
extends Partial<React.SVGProps<SVGSVGElement>> {
|
|
30
|
+
name: keyof typeof Icons
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
EmptyPlaceholder.Icon = function EmptyPlaceHolderIcon({
|
|
34
|
+
name,
|
|
35
|
+
className
|
|
36
|
+
}: EmptyPlaceholderIconProps) {
|
|
37
|
+
const Icon = Icons[name]
|
|
38
|
+
|
|
39
|
+
if (!Icon) {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className="flex h-20 w-20 items-center justify-center rounded-full bg-muted">
|
|
45
|
+
<Icon className={cn('h-10 w-10', className)} />
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface EmptyPlacholderTitleProps
|
|
51
|
+
extends React.HTMLAttributes<HTMLHeadingElement> {}
|
|
52
|
+
|
|
53
|
+
EmptyPlaceholder.Title = function EmptyPlaceholderTitle({
|
|
54
|
+
className,
|
|
55
|
+
...props
|
|
56
|
+
}: EmptyPlacholderTitleProps) {
|
|
57
|
+
return (
|
|
58
|
+
<h2 className={cn('mt-6 text-xl font-semibold', className)} {...props} />
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface EmptyPlacholderDescriptionProps
|
|
63
|
+
extends React.HTMLAttributes<HTMLParagraphElement> {}
|
|
64
|
+
|
|
65
|
+
EmptyPlaceholder.Description = function EmptyPlaceholderDescription({
|
|
66
|
+
className,
|
|
67
|
+
...props
|
|
68
|
+
}: EmptyPlacholderDescriptionProps) {
|
|
69
|
+
return (
|
|
70
|
+
<p
|
|
71
|
+
className={cn(
|
|
72
|
+
'mb-8 mt-2 text-center text-sm font-normal leading-6 text-muted-foreground',
|
|
73
|
+
className
|
|
74
|
+
)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { FC, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
export const Heading: FC<{
|
|
4
|
+
heading: string
|
|
5
|
+
text?: string
|
|
6
|
+
children?: ReactNode
|
|
7
|
+
}> = ({ heading, text, children }) => {
|
|
8
|
+
return (
|
|
9
|
+
<div className="flex items-center justify-between px-2">
|
|
10
|
+
<div className="grid gap-1">
|
|
11
|
+
<h1 className="font-heading text-3xl md:text-4xl">{heading}</h1>
|
|
12
|
+
{text && <p className="text-lg text-muted-foreground">{text}</p>}
|
|
13
|
+
</div>
|
|
14
|
+
{children}
|
|
15
|
+
</div>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { cn } from '@/lib/cn'
|
|
2
|
+
import {
|
|
3
|
+
ArrowDown,
|
|
4
|
+
ArrowRight,
|
|
5
|
+
ArrowUp,
|
|
6
|
+
ArrowUpDown,
|
|
7
|
+
Biohazard,
|
|
8
|
+
Check,
|
|
9
|
+
ChevronLeft,
|
|
10
|
+
ChevronRight,
|
|
11
|
+
ChevronsLeft,
|
|
12
|
+
ChevronsRight,
|
|
13
|
+
ClipboardList,
|
|
14
|
+
Download,
|
|
15
|
+
Edit,
|
|
16
|
+
EyeOff,
|
|
17
|
+
Github,
|
|
18
|
+
Laptop,
|
|
19
|
+
LayoutDashboard,
|
|
20
|
+
Loader2,
|
|
21
|
+
LogIn,
|
|
22
|
+
LogOut,
|
|
23
|
+
LucideIcon,
|
|
24
|
+
LucideProps,
|
|
25
|
+
Moon,
|
|
26
|
+
Plus,
|
|
27
|
+
PlusCircle,
|
|
28
|
+
Save,
|
|
29
|
+
Settings,
|
|
30
|
+
Settings2,
|
|
31
|
+
Sun,
|
|
32
|
+
Trash,
|
|
33
|
+
User,
|
|
34
|
+
X
|
|
35
|
+
} from 'lucide-react'
|
|
36
|
+
|
|
37
|
+
export type Icon = LucideIcon
|
|
38
|
+
|
|
39
|
+
export const Icons = {
|
|
40
|
+
logo: Biohazard,
|
|
41
|
+
sun: Sun,
|
|
42
|
+
moon: Moon,
|
|
43
|
+
laptop: Laptop,
|
|
44
|
+
chevronLeft: ChevronLeft,
|
|
45
|
+
arrowRight: ArrowRight,
|
|
46
|
+
github: Github,
|
|
47
|
+
google: ({ ...props }: LucideProps) => (
|
|
48
|
+
<svg
|
|
49
|
+
aria-hidden="true"
|
|
50
|
+
focusable="false"
|
|
51
|
+
data-prefix="fab"
|
|
52
|
+
data-icon="google"
|
|
53
|
+
role="img"
|
|
54
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
55
|
+
viewBox="0 0 30 30"
|
|
56
|
+
{...props}
|
|
57
|
+
className={cn('fill-current', props.className)}
|
|
58
|
+
>
|
|
59
|
+
<path d="M 15.003906 3 C 8.3749062 3 3 8.373 3 15 C 3 21.627 8.3749062 27 15.003906 27 C 25.013906 27 27.269078 17.707 26.330078 13 L 25 13 L 22.732422 13 L 15 13 L 15 17 L 22.738281 17 C 21.848702 20.448251 18.725955 23 15 23 C 10.582 23 7 19.418 7 15 C 7 10.582 10.582 7 15 7 C 17.009 7 18.839141 7.74575 20.244141 8.96875 L 23.085938 6.1289062 C 20.951937 4.1849063 18.116906 3 15.003906 3 z"></path>
|
|
60
|
+
</svg>
|
|
61
|
+
),
|
|
62
|
+
login: LogIn,
|
|
63
|
+
logout: LogOut,
|
|
64
|
+
user: User,
|
|
65
|
+
spinner: Loader2,
|
|
66
|
+
settings: Settings,
|
|
67
|
+
close: X,
|
|
68
|
+
add: Plus,
|
|
69
|
+
edit: Edit,
|
|
70
|
+
save: Save,
|
|
71
|
+
delete: Trash,
|
|
72
|
+
download: Download,
|
|
73
|
+
dashboard: LayoutDashboard,
|
|
74
|
+
task: ClipboardList,
|
|
75
|
+
arrowUp: ArrowUp,
|
|
76
|
+
arrowDown: ArrowDown,
|
|
77
|
+
arrowUpDown: ArrowUpDown,
|
|
78
|
+
eyeOff: EyeOff,
|
|
79
|
+
plusCircle: PlusCircle,
|
|
80
|
+
check: Check,
|
|
81
|
+
chevronsLeft: ChevronsLeft,
|
|
82
|
+
chevronsRight: ChevronsRight,
|
|
83
|
+
chevronRight: ChevronRight,
|
|
84
|
+
mixer: Settings2
|
|
85
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link'
|
|
4
|
+
import { useSelectedLayoutSegment } from 'next/navigation'
|
|
5
|
+
import { FC, useState } from 'react'
|
|
6
|
+
|
|
7
|
+
import { Icons } from '@/components/icons'
|
|
8
|
+
import { MobileNav } from '@/components/mobile-nav'
|
|
9
|
+
import { cn } from '@/lib/cn'
|
|
10
|
+
import { siteConfig } from '@/lib/siteConfig'
|
|
11
|
+
|
|
12
|
+
type MainNavItem = {
|
|
13
|
+
title: string
|
|
14
|
+
href: string
|
|
15
|
+
disabled?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type MainNavProps = {
|
|
19
|
+
logoLink?: string
|
|
20
|
+
items?: MainNavItem[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const MainNav: FC<MainNavProps> = ({ logoLink = '/', items }) => {
|
|
24
|
+
const [showMobileMenu, setShowMobileMenu] = useState(false)
|
|
25
|
+
const segment = useSelectedLayoutSegment()
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="flex md:gap-10">
|
|
29
|
+
<Link href={logoLink} className="hidden items-center space-x-2 md:flex">
|
|
30
|
+
<Icons.logo />
|
|
31
|
+
<span className="hidden font-bold sm:inline-block">
|
|
32
|
+
{siteConfig.name}
|
|
33
|
+
</span>
|
|
34
|
+
</Link>
|
|
35
|
+
{items?.length ? (
|
|
36
|
+
<nav className="hidden gap-6 md:flex">
|
|
37
|
+
{items?.map((item, i) => (
|
|
38
|
+
<Link
|
|
39
|
+
key={i}
|
|
40
|
+
href={item.disabled ? '#' : item.href}
|
|
41
|
+
className={cn(
|
|
42
|
+
'flex items-center text-lg font-medium transition-colors hover:text-foreground/80 sm:text-sm',
|
|
43
|
+
item.href.startsWith(`/${segment}`)
|
|
44
|
+
? 'text-foreground'
|
|
45
|
+
: 'text-foreground/60',
|
|
46
|
+
item.disabled && 'cursor-not-allowed opacity-80'
|
|
47
|
+
)}
|
|
48
|
+
>
|
|
49
|
+
{item.title}
|
|
50
|
+
</Link>
|
|
51
|
+
))}
|
|
52
|
+
</nav>
|
|
53
|
+
) : undefined}
|
|
54
|
+
<button
|
|
55
|
+
className="flex items-center space-x-2 md:hidden"
|
|
56
|
+
onClick={() => setShowMobileMenu(prev => !prev)}
|
|
57
|
+
>
|
|
58
|
+
{showMobileMenu ? <Icons.close /> : <Icons.logo />}
|
|
59
|
+
<span className="font-bold">Menu</span>
|
|
60
|
+
</button>
|
|
61
|
+
{showMobileMenu && items?.length && <MobileNav items={items} />}
|
|
62
|
+
</div>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FC } from 'react'
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/lib/cn'
|
|
4
|
+
|
|
5
|
+
interface ShellProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
6
|
+
|
|
7
|
+
export const Shell: FC<ShellProps> = ({ children, className, ...props }) => {
|
|
8
|
+
return (
|
|
9
|
+
<div className={cn('grid items-start gap-8 p-1', className)} {...props}>
|
|
10
|
+
{children}
|
|
11
|
+
</div>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link'
|
|
4
|
+
import { usePathname } from 'next/navigation'
|
|
5
|
+
|
|
6
|
+
import { Icons } from '@/components/icons'
|
|
7
|
+
import { cn } from '@/lib/cn'
|
|
8
|
+
import { FC } from 'react'
|
|
9
|
+
|
|
10
|
+
type IconKeys = keyof typeof Icons
|
|
11
|
+
|
|
12
|
+
interface SideNavProps {
|
|
13
|
+
items: {
|
|
14
|
+
title: string
|
|
15
|
+
href?: string | null
|
|
16
|
+
disabled?: boolean | null
|
|
17
|
+
icon?: IconKeys | null
|
|
18
|
+
}[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const SideNav: FC<SideNavProps> = ({ items }) => {
|
|
22
|
+
const path = usePathname()
|
|
23
|
+
|
|
24
|
+
if (!items?.length) return null
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<nav className="grid items-start gap-2">
|
|
28
|
+
{items.map((item, index) => {
|
|
29
|
+
const Icon = Icons[item.icon || 'arrowRight']
|
|
30
|
+
return (
|
|
31
|
+
item.href && (
|
|
32
|
+
<Link key={index} href={item.disabled ? '/' : item.href}>
|
|
33
|
+
<span
|
|
34
|
+
className={cn(
|
|
35
|
+
'group flex items-center rounded-md px-3 py-2 text-sm font-medium',
|
|
36
|
+
path.includes(item.href)
|
|
37
|
+
? 'bg-accent'
|
|
38
|
+
: 'transparent underline-offset-4 hover:underline',
|
|
39
|
+
item.disabled && 'cursor-not-allowed opacity-80'
|
|
40
|
+
)}
|
|
41
|
+
>
|
|
42
|
+
<Icon className="mr-2 h-4 w-4" />
|
|
43
|
+
<span>{item.title}</span>
|
|
44
|
+
</span>
|
|
45
|
+
</Link>
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
})}
|
|
49
|
+
</nav>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FC, HTMLAttributes } from 'react'
|
|
2
|
+
|
|
3
|
+
import { DarkModeToggle } from '@/components/dark-mode-toggle'
|
|
4
|
+
import { Icons } from '@/components/icons'
|
|
5
|
+
import { cn } from '@/lib/cn'
|
|
6
|
+
import { siteConfig } from '@/lib/siteConfig'
|
|
7
|
+
|
|
8
|
+
export const SiteFooter: FC<HTMLAttributes<HTMLElement>> = ({ className }) => {
|
|
9
|
+
return (
|
|
10
|
+
<footer className={cn(className)}>
|
|
11
|
+
<div className="container flex flex-col items-center justify-between gap-2 py-4 sm:flex-row">
|
|
12
|
+
<div className="flex flex-col items-center gap-2 sm:flex-row">
|
|
13
|
+
<Icons.logo />
|
|
14
|
+
<p className="text-center text-sm leading-loose sm:text-left">
|
|
15
|
+
{siteConfig.name}. Built by{' '}
|
|
16
|
+
<a
|
|
17
|
+
href="https://github.com/ejekanshjain"
|
|
18
|
+
target="_blank"
|
|
19
|
+
rel="noreferrer"
|
|
20
|
+
className="font-medium underline underline-offset-4"
|
|
21
|
+
>
|
|
22
|
+
ejekanshjain
|
|
23
|
+
</a>
|
|
24
|
+
.
|
|
25
|
+
</p>
|
|
26
|
+
</div>
|
|
27
|
+
<DarkModeToggle />
|
|
28
|
+
</div>
|
|
29
|
+
</footer>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { FC, ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
6
|
+
import { Separator } from '@/components/ui/separator'
|
|
7
|
+
|
|
8
|
+
type SystemInfoProps = {
|
|
9
|
+
items: {
|
|
10
|
+
label: string
|
|
11
|
+
value: string | ReactNode
|
|
12
|
+
}[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const SystemInfo: FC<SystemInfoProps> = ({ items }) => {
|
|
16
|
+
return (
|
|
17
|
+
<Card className="max-w-[600px]">
|
|
18
|
+
<CardHeader>
|
|
19
|
+
<CardTitle>System Information</CardTitle>
|
|
20
|
+
</CardHeader>
|
|
21
|
+
<CardContent>
|
|
22
|
+
<dl className="grid gap-4">
|
|
23
|
+
{items.map((item, i) => (
|
|
24
|
+
<div key={i}>
|
|
25
|
+
<dt className="font-semibold">{item.label}</dt>
|
|
26
|
+
<dd>{item.value}</dd>
|
|
27
|
+
{i < items.length - 1 ? (
|
|
28
|
+
<Separator className="mt-2" />
|
|
29
|
+
) : undefined}
|
|
30
|
+
</div>
|
|
31
|
+
))}
|
|
32
|
+
</dl>
|
|
33
|
+
</CardContent>
|
|
34
|
+
</Card>
|
|
35
|
+
)
|
|
36
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
import { buttonVariants } from '@/components/ui/button'
|
|
7
|
+
import { cn } from '@/lib/cn'
|
|
8
|
+
|
|
9
|
+
const AlertDialog = AlertDialogPrimitive.Root
|
|
10
|
+
|
|
11
|
+
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
|
12
|
+
|
|
13
|
+
const AlertDialogPortal = AlertDialogPrimitive.Portal
|
|
14
|
+
|
|
15
|
+
const AlertDialogOverlay = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
|
18
|
+
>(({ className, ...props }, ref) => (
|
|
19
|
+
<AlertDialogPrimitive.Overlay
|
|
20
|
+
className={cn(
|
|
21
|
+
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
ref={ref}
|
|
26
|
+
/>
|
|
27
|
+
))
|
|
28
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
|
|
29
|
+
|
|
30
|
+
const AlertDialogContent = React.forwardRef<
|
|
31
|
+
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
|
32
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
|
33
|
+
>(({ className, ...props }, ref) => (
|
|
34
|
+
<AlertDialogPortal>
|
|
35
|
+
<AlertDialogOverlay />
|
|
36
|
+
<AlertDialogPrimitive.Content
|
|
37
|
+
ref={ref}
|
|
38
|
+
className={cn(
|
|
39
|
+
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
|
40
|
+
className
|
|
41
|
+
)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
</AlertDialogPortal>
|
|
45
|
+
))
|
|
46
|
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
|
47
|
+
|
|
48
|
+
const AlertDialogHeader = ({
|
|
49
|
+
className,
|
|
50
|
+
...props
|
|
51
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
52
|
+
<div
|
|
53
|
+
className={cn(
|
|
54
|
+
'flex flex-col space-y-2 text-center sm:text-left',
|
|
55
|
+
className
|
|
56
|
+
)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
AlertDialogHeader.displayName = 'AlertDialogHeader'
|
|
61
|
+
|
|
62
|
+
const AlertDialogFooter = ({
|
|
63
|
+
className,
|
|
64
|
+
...props
|
|
65
|
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
66
|
+
<div
|
|
67
|
+
className={cn(
|
|
68
|
+
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
|
|
69
|
+
className
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
AlertDialogFooter.displayName = 'AlertDialogFooter'
|
|
75
|
+
|
|
76
|
+
const AlertDialogTitle = React.forwardRef<
|
|
77
|
+
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
|
78
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
|
79
|
+
>(({ className, ...props }, ref) => (
|
|
80
|
+
<AlertDialogPrimitive.Title
|
|
81
|
+
ref={ref}
|
|
82
|
+
className={cn('text-lg font-semibold', className)}
|
|
83
|
+
{...props}
|
|
84
|
+
/>
|
|
85
|
+
))
|
|
86
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
|
|
87
|
+
|
|
88
|
+
const AlertDialogDescription = React.forwardRef<
|
|
89
|
+
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
|
90
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
|
91
|
+
>(({ className, ...props }, ref) => (
|
|
92
|
+
<AlertDialogPrimitive.Description
|
|
93
|
+
ref={ref}
|
|
94
|
+
className={cn('text-sm text-muted-foreground', className)}
|
|
95
|
+
{...props}
|
|
96
|
+
/>
|
|
97
|
+
))
|
|
98
|
+
AlertDialogDescription.displayName =
|
|
99
|
+
AlertDialogPrimitive.Description.displayName
|
|
100
|
+
|
|
101
|
+
const AlertDialogAction = React.forwardRef<
|
|
102
|
+
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
|
103
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
|
104
|
+
>(({ className, ...props }, ref) => (
|
|
105
|
+
<AlertDialogPrimitive.Action
|
|
106
|
+
ref={ref}
|
|
107
|
+
className={cn(buttonVariants(), className)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
))
|
|
111
|
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
|
112
|
+
|
|
113
|
+
const AlertDialogCancel = React.forwardRef<
|
|
114
|
+
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
|
115
|
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
|
116
|
+
>(({ className, ...props }, ref) => (
|
|
117
|
+
<AlertDialogPrimitive.Cancel
|
|
118
|
+
ref={ref}
|
|
119
|
+
className={cn(
|
|
120
|
+
buttonVariants({ variant: 'outline' }),
|
|
121
|
+
'mt-2 sm:mt-0',
|
|
122
|
+
className
|
|
123
|
+
)}
|
|
124
|
+
{...props}
|
|
125
|
+
/>
|
|
126
|
+
))
|
|
127
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
|
|
128
|
+
|
|
129
|
+
export {
|
|
130
|
+
AlertDialog,
|
|
131
|
+
AlertDialogAction,
|
|
132
|
+
AlertDialogCancel,
|
|
133
|
+
AlertDialogContent,
|
|
134
|
+
AlertDialogDescription,
|
|
135
|
+
AlertDialogFooter,
|
|
136
|
+
AlertDialogHeader,
|
|
137
|
+
AlertDialogOverlay,
|
|
138
|
+
AlertDialogPortal,
|
|
139
|
+
AlertDialogTitle,
|
|
140
|
+
AlertDialogTrigger
|
|
141
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as AvatarPrimitive from '@radix-ui/react-avatar'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/cn'
|
|
7
|
+
|
|
8
|
+
const Avatar = React.forwardRef<
|
|
9
|
+
React.ElementRef<typeof AvatarPrimitive.Root>,
|
|
10
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
|
11
|
+
>(({ className, ...props }, ref) => (
|
|
12
|
+
<AvatarPrimitive.Root
|
|
13
|
+
ref={ref}
|
|
14
|
+
className={cn(
|
|
15
|
+
'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
|
|
16
|
+
className
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
))
|
|
21
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName
|
|
22
|
+
|
|
23
|
+
const AvatarImage = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof AvatarPrimitive.Image>,
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
26
|
+
>(({ className, ...props }, ref) => (
|
|
27
|
+
<AvatarPrimitive.Image
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={cn('aspect-square h-full w-full', className)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
))
|
|
33
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName
|
|
34
|
+
|
|
35
|
+
const AvatarFallback = React.forwardRef<
|
|
36
|
+
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
|
37
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
38
|
+
>(({ className, ...props }, ref) => (
|
|
39
|
+
<AvatarPrimitive.Fallback
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={cn(
|
|
42
|
+
'flex h-full w-full items-center justify-center rounded-full bg-muted',
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
))
|
|
48
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
|
|
49
|
+
|
|
50
|
+
export { Avatar, AvatarFallback, AvatarImage }
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
|
|
4
|
+
import { cn } from '@/lib/cn'
|
|
5
|
+
|
|
6
|
+
const badgeVariants = cva(
|
|
7
|
+
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default:
|
|
12
|
+
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
|
13
|
+
secondary:
|
|
14
|
+
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
15
|
+
destructive:
|
|
16
|
+
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
|
17
|
+
outline: 'text-foreground'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: {
|
|
21
|
+
variant: 'default'
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
export interface BadgeProps
|
|
27
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
28
|
+
VariantProps<typeof badgeVariants> {}
|
|
29
|
+
|
|
30
|
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
|
31
|
+
return (
|
|
32
|
+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { Badge, badgeVariants }
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Slot } from '@radix-ui/react-slot'
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
import { cn } from '@/lib/cn'
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
13
|
+
destructive:
|
|
14
|
+
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
15
|
+
outline:
|
|
16
|
+
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
17
|
+
secondary:
|
|
18
|
+
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
19
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
20
|
+
link: 'text-primary underline-offset-4 hover:underline'
|
|
21
|
+
},
|
|
22
|
+
size: {
|
|
23
|
+
default: 'h-10 px-4 py-2',
|
|
24
|
+
sm: 'h-9 rounded-md px-3',
|
|
25
|
+
lg: 'h-11 rounded-md px-8',
|
|
26
|
+
icon: 'h-10 w-10'
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
variant: 'default',
|
|
31
|
+
size: 'default'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
export interface ButtonProps
|
|
37
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
38
|
+
VariantProps<typeof buttonVariants> {
|
|
39
|
+
asChild?: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
43
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
44
|
+
const Comp = asChild ? Slot : 'button'
|
|
45
|
+
return (
|
|
46
|
+
<Comp
|
|
47
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
48
|
+
ref={ref}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
Button.displayName = 'Button'
|
|
55
|
+
|
|
56
|
+
export { Button, buttonVariants }
|