create-ej-app 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.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/cli.js +68 -0
  4. package/package.json +36 -0
  5. package/template/.env.example +12 -0
  6. package/template/.eslintrc.cjs +24 -0
  7. package/template/components.json +16 -0
  8. package/template/next.config.mjs +18 -0
  9. package/template/package.json +68 -0
  10. package/template/postcss.config.cjs +6 -0
  11. package/template/prettier.config.ts +11 -0
  12. package/template/prisma/schema.prisma +136 -0
  13. package/template/public/next.svg +1 -0
  14. package/template/src/app/(app)/dashboard/page.tsx +19 -0
  15. package/template/src/app/(app)/layout.tsx +117 -0
  16. package/template/src/app/(app)/roles/[id]/actions.ts +100 -0
  17. package/template/src/app/(app)/roles/[id]/page.tsx +36 -0
  18. package/template/src/app/(app)/roles/[id]/render.tsx +266 -0
  19. package/template/src/app/(app)/roles/actions.ts +67 -0
  20. package/template/src/app/(app)/roles/page.tsx +51 -0
  21. package/template/src/app/(app)/roles/render.tsx +70 -0
  22. package/template/src/app/(app)/settings/actions.ts +18 -0
  23. package/template/src/app/(app)/settings/loading.tsx +28 -0
  24. package/template/src/app/(app)/settings/page.tsx +28 -0
  25. package/template/src/app/(app)/settings/render.tsx +113 -0
  26. package/template/src/app/(app)/tasks/[id]/actions.ts +107 -0
  27. package/template/src/app/(app)/tasks/[id]/page.tsx +55 -0
  28. package/template/src/app/(app)/tasks/[id]/render.tsx +307 -0
  29. package/template/src/app/(app)/tasks/actions.ts +75 -0
  30. package/template/src/app/(app)/tasks/page.tsx +66 -0
  31. package/template/src/app/(app)/tasks/render.tsx +124 -0
  32. package/template/src/app/(app)/users/[id]/actions.ts +104 -0
  33. package/template/src/app/(app)/users/[id]/page.tsx +39 -0
  34. package/template/src/app/(app)/users/[id]/render.tsx +320 -0
  35. package/template/src/app/(app)/users/actions.ts +67 -0
  36. package/template/src/app/(app)/users/page.tsx +61 -0
  37. package/template/src/app/(app)/users/render.tsx +108 -0
  38. package/template/src/app/(auth)/layout.tsx +20 -0
  39. package/template/src/app/(auth)/login/page.tsx +49 -0
  40. package/template/src/app/(auth)/login/user-auth-form.tsx +152 -0
  41. package/template/src/app/(marketing)/layout.tsx +42 -0
  42. package/template/src/app/(marketing)/page.tsx +96 -0
  43. package/template/src/app/api/auth/[...nextauth]/route.ts +7 -0
  44. package/template/src/app/favicon.ico +0 -0
  45. package/template/src/app/globals.css +76 -0
  46. package/template/src/app/layout.tsx +32 -0
  47. package/template/src/components/app-nav-command.tsx +160 -0
  48. package/template/src/components/dark-mode-toggle.tsx +43 -0
  49. package/template/src/components/data-table/data-table-column-header.tsx +91 -0
  50. package/template/src/components/data-table/data-table-faceted-filter.tsx +149 -0
  51. package/template/src/components/data-table/data-table-loading.tsx +77 -0
  52. package/template/src/components/data-table/data-table-pagination.tsx +94 -0
  53. package/template/src/components/data-table/data-table-toolbar.tsx +78 -0
  54. package/template/src/components/data-table/data-table-types.ts +17 -0
  55. package/template/src/components/data-table/data-table-view-options.tsx +59 -0
  56. package/template/src/components/data-table/data-table.tsx +301 -0
  57. package/template/src/components/data-table/simple-data-table.tsx +109 -0
  58. package/template/src/components/empty-placeholder.tsx +78 -0
  59. package/template/src/components/heading.tsx +17 -0
  60. package/template/src/components/icons.tsx +83 -0
  61. package/template/src/components/main-nav.tsx +64 -0
  62. package/template/src/components/mobile-nav.tsx +50 -0
  63. package/template/src/components/shell.tsx +13 -0
  64. package/template/src/components/side-nav.tsx +51 -0
  65. package/template/src/components/site-footer.tsx +31 -0
  66. package/template/src/components/system-info.tsx +36 -0
  67. package/template/src/components/theme-provider.tsx +13 -0
  68. package/template/src/components/ui/alert-dialog.tsx +145 -0
  69. package/template/src/components/ui/avatar.tsx +50 -0
  70. package/template/src/components/ui/badge.tsx +36 -0
  71. package/template/src/components/ui/button.tsx +56 -0
  72. package/template/src/components/ui/card.tsx +79 -0
  73. package/template/src/components/ui/checkbox.tsx +30 -0
  74. package/template/src/components/ui/command.tsx +155 -0
  75. package/template/src/components/ui/dialog.tsx +123 -0
  76. package/template/src/components/ui/dropdown-menu.tsx +200 -0
  77. package/template/src/components/ui/form.tsx +176 -0
  78. package/template/src/components/ui/input.tsx +25 -0
  79. package/template/src/components/ui/label.tsx +26 -0
  80. package/template/src/components/ui/popover.tsx +31 -0
  81. package/template/src/components/ui/select.tsx +121 -0
  82. package/template/src/components/ui/separator.tsx +31 -0
  83. package/template/src/components/ui/skeleton.tsx +15 -0
  84. package/template/src/components/ui/table.tsx +114 -0
  85. package/template/src/components/ui/textarea.tsx +24 -0
  86. package/template/src/components/ui/toast.tsx +127 -0
  87. package/template/src/components/ui/toaster.tsx +35 -0
  88. package/template/src/components/ui/use-toast.ts +189 -0
  89. package/template/src/components/user-account-nav.tsx +60 -0
  90. package/template/src/components/user-avatar.tsx +25 -0
  91. package/template/src/env.mjs +67 -0
  92. package/template/src/hooks/use-debounce.ts +17 -0
  93. package/template/src/hooks/use-lock-body.tsx +12 -0
  94. package/template/src/lib/auth.ts +183 -0
  95. package/template/src/lib/cn.ts +4 -0
  96. package/template/src/lib/db.ts +20 -0
  97. package/template/src/lib/delay.ts +2 -0
  98. package/template/src/lib/formatDate.ts +11 -0
  99. package/template/src/lib/generateLabel.ts +5 -0
  100. package/template/src/lib/siteConfig.ts +4 -0
  101. package/template/src/types/UnwrapPromise.ts +1 -0
  102. package/template/tailwind.config.ts +79 -0
  103. package/template/tsconfig.json +46 -0
@@ -0,0 +1,60 @@
1
+ 'use client'
2
+
3
+ import { User } from 'next-auth'
4
+ import { signOut } from 'next-auth/react'
5
+ import Link from 'next/link'
6
+ import { FC, HTMLAttributes } from 'react'
7
+
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuSeparator,
13
+ DropdownMenuTrigger
14
+ } from '@/components/ui/dropdown-menu'
15
+ import { UserAvatar } from '@/components/user-avatar'
16
+
17
+ interface UserAccountNavProps extends HTMLAttributes<HTMLDivElement> {
18
+ user: Pick<User, 'name' | 'image' | 'email'>
19
+ }
20
+
21
+ export const UserAccountNav: FC<UserAccountNavProps> = ({ user }) => {
22
+ return (
23
+ <DropdownMenu>
24
+ <DropdownMenuTrigger>
25
+ <UserAvatar
26
+ user={{ name: user.name || null, image: user.image || null }}
27
+ className="h-8 w-8"
28
+ />
29
+ </DropdownMenuTrigger>
30
+ <DropdownMenuContent align="end">
31
+ <div className="flex items-center justify-start gap-2 p-2">
32
+ <div className="flex flex-col space-y-1 leading-none">
33
+ {user.name && <p className="font-medium">{user.name}</p>}
34
+ {user.email && (
35
+ <p className="w-[200px] truncate text-sm text-muted-foreground">
36
+ {user.email}
37
+ </p>
38
+ )}
39
+ </div>
40
+ </div>
41
+ <DropdownMenuSeparator />
42
+ <DropdownMenuItem asChild>
43
+ <Link href="/settings">Settings</Link>
44
+ </DropdownMenuItem>
45
+ <DropdownMenuSeparator />
46
+ <DropdownMenuItem
47
+ className="cursor-pointer"
48
+ onSelect={event => {
49
+ event.preventDefault()
50
+ signOut({
51
+ callbackUrl: `${window.location.origin}/login`
52
+ })
53
+ }}
54
+ >
55
+ Sign out
56
+ </DropdownMenuItem>
57
+ </DropdownMenuContent>
58
+ </DropdownMenu>
59
+ )
60
+ }
@@ -0,0 +1,25 @@
1
+ import { User } from '@prisma/client'
2
+ import { AvatarProps } from '@radix-ui/react-avatar'
3
+ import { FC } from 'react'
4
+
5
+ import { Icons } from '@/components/icons'
6
+ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
7
+
8
+ interface UserAvatarProps extends AvatarProps {
9
+ user: Pick<User, 'image' | 'name'>
10
+ }
11
+
12
+ export const UserAvatar: FC<UserAvatarProps> = ({ user, ...props }) => {
13
+ return (
14
+ <Avatar {...props}>
15
+ {user.image ? (
16
+ <AvatarImage alt="Picture" src={user.image} />
17
+ ) : (
18
+ <AvatarFallback>
19
+ <span className="sr-only">{user.name}</span>
20
+ <Icons.user className="h-4 w-4" />
21
+ </AvatarFallback>
22
+ )}
23
+ </Avatar>
24
+ )
25
+ }
@@ -0,0 +1,67 @@
1
+ import { createEnv } from '@t3-oss/env-nextjs'
2
+ import { z } from 'zod'
3
+
4
+ export const env = createEnv({
5
+ /**
6
+ * Specify your server-side environment variables schema here. This way you can ensure the app
7
+ * isn't built with invalid env vars.
8
+ */
9
+ server: {
10
+ NODE_ENV: z.enum(['development', 'test', 'production']),
11
+ DATABASE_URL: z.string().url(),
12
+ NEXTAUTH_SECRET:
13
+ process.env.NODE_ENV === 'production'
14
+ ? z.string().min(1)
15
+ : z.string().min(1).optional(),
16
+ NEXTAUTH_URL: z.preprocess(
17
+ // This makes Vercel deployments not fail if you don't set NEXTAUTH_URL
18
+ // Since NextAuth.js automatically uses the VERCEL_URL if present.
19
+ str => process.env.VERCEL_URL ?? str,
20
+ // VERCEL_URL doesn't include `https` so it cant be validated as a URL
21
+ process.env.VERCEL ? z.string().min(1) : z.string().url()
22
+ ),
23
+ EMAIL_SERVER_HOST: z.string().min(1),
24
+ EMAIL_SERVER_PORT: z.number(),
25
+ EMAIL_SERVER_USER: z.string().min(1),
26
+ EMAIL_SERVER_PASSWORD: z.string().min(1),
27
+ EMAIL_FROM: z.string().email(),
28
+ GOOGLE_ID: z.string().min(1),
29
+ GOOGLE_SECRET: z.string().min(1),
30
+ GITHUB_ID: z.string().min(1),
31
+ GITHUB_SECRET: z.string().min(1)
32
+ },
33
+
34
+ /**
35
+ * Specify your client-side environment variables schema here. This way you can ensure the app
36
+ * isn't built with invalid env vars. To expose them to the client, prefix them with
37
+ * `NEXT_PUBLIC_`.
38
+ */
39
+ client: {
40
+ // NEXT_PUBLIC_CLIENTVAR: z.string().min(1),
41
+ },
42
+
43
+ /**
44
+ * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
45
+ * middlewares) or client-side so we need to destruct manually.
46
+ */
47
+ runtimeEnv: {
48
+ NODE_ENV: process.env.NODE_ENV,
49
+ DATABASE_URL: process.env.DATABASE_URL,
50
+ NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
51
+ NEXTAUTH_URL: process.env.NEXTAUTH_URL,
52
+ EMAIL_SERVER_HOST: process.env.EMAIL_SERVER_HOST,
53
+ EMAIL_SERVER_PORT: parseInt(process.env.EMAIL_SERVER_PORT || ''),
54
+ EMAIL_SERVER_USER: process.env.EMAIL_SERVER_USER,
55
+ EMAIL_SERVER_PASSWORD: process.env.EMAIL_SERVER_PASSWORD,
56
+ EMAIL_FROM: process.env.EMAIL_FROM,
57
+ GOOGLE_ID: process.env.GOOGLE_ID,
58
+ GOOGLE_SECRET: process.env.GOOGLE_SECRET,
59
+ GITHUB_ID: process.env.GITHUB_ID,
60
+ GITHUB_SECRET: process.env.GITHUB_SECRET
61
+ },
62
+ /**
63
+ * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation.
64
+ * This is especially useful for Docker builds.
65
+ */
66
+ skipValidation: !!process.env.SKIP_ENV_VALIDATION
67
+ })
@@ -0,0 +1,17 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useState } from 'react'
4
+
5
+ export function useDebounce<T>(value: T, delay?: number): T {
6
+ const [debouncedValue, setDebouncedValue] = useState<T>(value)
7
+
8
+ useEffect(() => {
9
+ const timer = setTimeout(() => setDebouncedValue(value), delay || 500)
10
+
11
+ return () => {
12
+ clearTimeout(timer)
13
+ }
14
+ }, [value, delay])
15
+
16
+ return debouncedValue
17
+ }
@@ -0,0 +1,12 @@
1
+ import { useLayoutEffect } from 'react'
2
+
3
+ // @see https://usehooks.com/useLockBodyScroll.
4
+ export function useLockBody() {
5
+ useLayoutEffect((): (() => void) => {
6
+ const originalStyle: string = window.getComputedStyle(
7
+ document.body
8
+ ).overflow
9
+ document.body.style.overflow = 'hidden'
10
+ return () => (document.body.style.overflow = originalStyle)
11
+ }, [])
12
+ }
@@ -0,0 +1,183 @@
1
+ import { PrismaAdapter } from '@next-auth/prisma-adapter'
2
+ import { Permissions, UserType } from '@prisma/client'
3
+ import {
4
+ Session,
5
+ getServerSession,
6
+ type DefaultSession,
7
+ type NextAuthOptions
8
+ } from 'next-auth'
9
+ import EmailProvider from 'next-auth/providers/email'
10
+ import GitHubProvider from 'next-auth/providers/github'
11
+ import GoogleProvider from 'next-auth/providers/google'
12
+
13
+ import { env } from '@/env.mjs'
14
+ import { prisma } from '@/lib/db'
15
+
16
+ /**
17
+ * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session`
18
+ * object and keep type safety.
19
+ *
20
+ * @see https://next-auth.js.org/getting-started/typescript#module-augmentation
21
+ */
22
+ declare module 'next-auth' {
23
+ interface Session extends DefaultSession {
24
+ user: {
25
+ id: string
26
+ type: UserType
27
+ roleId?: string | null
28
+ } & DefaultSession['user']
29
+ }
30
+
31
+ interface User {
32
+ type: UserType
33
+ roleId?: string | null
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Options for NextAuth.js used to configure adapters, providers, callbacks, etc.
39
+ *
40
+ * @see https://next-auth.js.org/configuration/options
41
+ */
42
+ export const authOptions: NextAuthOptions = {
43
+ pages: {
44
+ signIn: '/login'
45
+ },
46
+ callbacks: {
47
+ session({ session, user }) {
48
+ return {
49
+ ...session,
50
+ user: {
51
+ ...session.user,
52
+ id: user.id,
53
+ type: user.type,
54
+ roleId: user.roleId
55
+ }
56
+ }
57
+ }
58
+ },
59
+ adapter: PrismaAdapter(prisma),
60
+ providers: [
61
+ EmailProvider({
62
+ server: {
63
+ host: env.EMAIL_SERVER_HOST,
64
+ port: env.EMAIL_SERVER_PORT,
65
+ auth: {
66
+ user: env.EMAIL_SERVER_USER,
67
+ pass: env.EMAIL_SERVER_PASSWORD
68
+ }
69
+ },
70
+ from: env.EMAIL_FROM
71
+ }),
72
+ GoogleProvider({
73
+ clientId: env.GOOGLE_ID,
74
+ clientSecret: env.GOOGLE_SECRET
75
+ }),
76
+ GitHubProvider({
77
+ clientId: env.GITHUB_ID,
78
+ clientSecret: env.GITHUB_SECRET
79
+ })
80
+ ]
81
+ }
82
+
83
+ /**
84
+ * Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file.
85
+ *
86
+ * @see https://next-auth.js.org/configuration/nextjs
87
+ */
88
+ export const getAuthSession = () => getServerSession(authOptions)
89
+
90
+ export const authGuard = async (
91
+ types?: UserType | UserType[],
92
+ permission?: Permissions,
93
+ session?: Session
94
+ ) => {
95
+ if (!session) {
96
+ const tempSession = await getAuthSession()
97
+ if (!tempSession) return false
98
+ session = tempSession
99
+ }
100
+
101
+ if (session.user.type === 'Root') return session
102
+
103
+ if (types) {
104
+ if (Array.isArray(types)) {
105
+ if (!types.includes(session.user.type)) return false
106
+ } else {
107
+ if (types !== session.user.type) return false
108
+ }
109
+ }
110
+
111
+ if (!session.user.roleId || !permission) return false
112
+
113
+ const c = await prisma.rolePermission.count({
114
+ where: {
115
+ roleId: session.user.roleId,
116
+ permission
117
+ }
118
+ })
119
+ if (!c) return false
120
+
121
+ return session
122
+ }
123
+
124
+ export const checkForPermission = async (
125
+ permission: Permissions,
126
+ session?: Session
127
+ ) => {
128
+ if (!session) {
129
+ const tempSession = await getAuthSession()
130
+ if (!tempSession) return false
131
+ session = tempSession
132
+ }
133
+
134
+ if (session.user.type === 'Root') return true
135
+ if (!session.user.roleId) return false
136
+
137
+ const c = await prisma.rolePermission.count({
138
+ where: {
139
+ roleId: session.user.roleId,
140
+ permission
141
+ }
142
+ })
143
+
144
+ return !!c
145
+ }
146
+
147
+ export const getAuthSessionWithPermissions = async () => {
148
+ const session = await getAuthSession()
149
+
150
+ if (session && session.user) {
151
+ if (session.user.type === 'Root') {
152
+ return {
153
+ session,
154
+ permissions: ['*']
155
+ }
156
+ } else if (session.user.roleId) {
157
+ const permissions = await prisma.rolePermission.findMany({
158
+ where: {
159
+ roleId: session.user.roleId
160
+ },
161
+ select: {
162
+ permission: true
163
+ },
164
+ orderBy: {
165
+ permission: 'asc'
166
+ }
167
+ })
168
+ return {
169
+ session,
170
+ permissions: permissions.map(p => p.permission)
171
+ }
172
+ } else {
173
+ return {
174
+ session,
175
+ permissions: []
176
+ }
177
+ }
178
+ }
179
+
180
+ return {
181
+ session
182
+ }
183
+ }
@@ -0,0 +1,4 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs))
@@ -0,0 +1,20 @@
1
+ import { PrismaClient } from '@prisma/client'
2
+
3
+ import { env } from '@/env.mjs'
4
+
5
+ const globalForPrisma = globalThis as unknown as {
6
+ prisma: PrismaClient | undefined
7
+ }
8
+
9
+ export const prisma =
10
+ globalForPrisma.prisma ||
11
+ new PrismaClient({
12
+ log:
13
+ env.NODE_ENV === 'development'
14
+ ? [
15
+ // 'info', 'query', 'error', 'warn'
16
+ ]
17
+ : ['error']
18
+ })
19
+
20
+ if (env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
@@ -0,0 +1,2 @@
1
+ export const delay = (ms: number) =>
2
+ new Promise(resolve => setTimeout(resolve, ms))
@@ -0,0 +1,11 @@
1
+ import dayjs from 'dayjs'
2
+ import relativeTime from 'dayjs/plugin/relativeTime'
3
+
4
+ dayjs.extend(relativeTime)
5
+
6
+ export const formatDate = (date: Date) => dayjs(date).format('DD/MM/YYYY')
7
+
8
+ export const formatDateTime = (date: Date) =>
9
+ dayjs(date).format('DD/MM/YYYY hh:mm:ss A')
10
+
11
+ export const timesAgo = (date: Date) => dayjs(date).fromNow()
@@ -0,0 +1,5 @@
1
+ export const generateLabel = (str: string) =>
2
+ str
3
+ .replaceAll('_', ' ')
4
+ .replaceAll(/([A-Z])/g, ' $1')
5
+ .trim()
@@ -0,0 +1,4 @@
1
+ export const siteConfig = {
2
+ name: 'Project Name',
3
+ description: 'Project Description'
4
+ }
@@ -0,0 +1 @@
1
+ export type UnwrapPromise<T> = T extends Promise<infer U> ? U : T
@@ -0,0 +1,79 @@
1
+ import type { Config } from 'tailwindcss'
2
+
3
+ const tailwindConfig: Config = {
4
+ darkMode: ['class'],
5
+ content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
6
+ theme: {
7
+ container: {
8
+ center: true,
9
+ padding: '2rem',
10
+ screens: {
11
+ '2xl': '1400px'
12
+ }
13
+ },
14
+ extend: {
15
+ backgroundImage: {
16
+ 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
17
+ 'gradient-conic':
18
+ 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))'
19
+ },
20
+ colors: {
21
+ border: 'hsl(var(--border))',
22
+ input: 'hsl(var(--input))',
23
+ ring: 'hsl(var(--ring))',
24
+ background: 'hsl(var(--background))',
25
+ foreground: 'hsl(var(--foreground))',
26
+ primary: {
27
+ DEFAULT: 'hsl(var(--primary))',
28
+ foreground: 'hsl(var(--primary-foreground))'
29
+ },
30
+ secondary: {
31
+ DEFAULT: 'hsl(var(--secondary))',
32
+ foreground: 'hsl(var(--secondary-foreground))'
33
+ },
34
+ destructive: {
35
+ DEFAULT: 'hsl(var(--destructive))',
36
+ foreground: 'hsl(var(--destructive-foreground))'
37
+ },
38
+ muted: {
39
+ DEFAULT: 'hsl(var(--muted))',
40
+ foreground: 'hsl(var(--muted-foreground))'
41
+ },
42
+ accent: {
43
+ DEFAULT: 'hsl(var(--accent))',
44
+ foreground: 'hsl(var(--accent-foreground))'
45
+ },
46
+ popover: {
47
+ DEFAULT: 'hsl(var(--popover))',
48
+ foreground: 'hsl(var(--popover-foreground))'
49
+ },
50
+ card: {
51
+ DEFAULT: 'hsl(var(--card))',
52
+ foreground: 'hsl(var(--card-foreground))'
53
+ }
54
+ },
55
+ borderRadius: {
56
+ lg: 'var(--radius)',
57
+ md: 'calc(var(--radius) - 2px)',
58
+ sm: 'calc(var(--radius) - 4px)'
59
+ },
60
+ keyframes: {
61
+ 'accordion-down': {
62
+ from: { height: '0' },
63
+ to: { height: 'var(--radix-accordion-content-height)' }
64
+ },
65
+ 'accordion-up': {
66
+ from: { height: 'var(--radix-accordion-content-height)' },
67
+ to: { height: '0' }
68
+ }
69
+ },
70
+ animation: {
71
+ 'accordion-down': 'accordion-down 0.2s ease-out',
72
+ 'accordion-up': 'accordion-up 0.2s ease-out'
73
+ }
74
+ }
75
+ },
76
+ plugins: [require('tailwindcss-animate')]
77
+ }
78
+
79
+ module.exports = tailwindConfig
@@ -0,0 +1,46 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
5
+ "allowJs": true,
6
+ "checkJs": true,
7
+ "skipLibCheck": true,
8
+ "strict": true,
9
+ "noImplicitAny": true,
10
+ "strictNullChecks": true,
11
+ "strictFunctionTypes": true,
12
+ "noImplicitThis": true,
13
+ "noUncheckedIndexedAccess": true,
14
+ "noUnusedLocals": true,
15
+ "noUnusedParameters": true,
16
+ "noImplicitReturns": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "forceConsistentCasingInFileNames": true,
19
+ "noEmit": true,
20
+ "esModuleInterop": true,
21
+ "module": "ESNext",
22
+ "moduleResolution": "Bundler",
23
+ "resolveJsonModule": true,
24
+ "isolatedModules": true,
25
+ "jsx": "preserve",
26
+ "incremental": true,
27
+ "plugins": [
28
+ {
29
+ "name": "next"
30
+ }
31
+ ],
32
+ "paths": {
33
+ "@/*": ["./src/*"]
34
+ }
35
+ },
36
+ "include": [
37
+ ".eslintrc.cjs",
38
+ "next-env.d.ts",
39
+ "**/*.ts",
40
+ "**/*.tsx",
41
+ "**/*.cjs",
42
+ "**/*.mjs",
43
+ ".next/types/**/*.ts"
44
+ ],
45
+ "exclude": ["node_modules"]
46
+ }