create-ej-app 12.0.0 → 15.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 -4
- package/cli.ts +9 -4
- package/package.json +3 -3
- package/template/base/package.json +23 -29
- package/template/base/src/lib/auth.ts +15 -6
- package/template/base/terraform/artifact-registry.tf +5 -0
- package/template/base/terraform/backend-service.tf +13 -0
- package/template/base/terraform/cleanup.sh +9 -0
- package/template/base/terraform/cloudbuild.tf +70 -0
- package/template/base/terraform/cloudrun.tf +68 -0
- package/template/base/terraform/database.tf +60 -0
- package/template/base/terraform/forwarding-rule.tf +43 -0
- package/template/base/terraform/ip-address.tf +11 -0
- package/template/base/terraform/network-endpoint-group.tf +11 -0
- package/template/base/terraform/network.tf +21 -0
- package/template/base/terraform/pre.sh +3 -0
- package/template/base/terraform/providers.tf +18 -0
- package/template/base/terraform/secret.txt +11 -0
- package/template/base/terraform/setup.sh +25 -0
- package/template/base/terraform/ssl-certs.tf +32 -0
- package/template/base/terraform/target-https-proxy copy.tf +14 -0
- package/template/base/terraform/target-https-proxy.tf +14 -0
- package/template/base/terraform/terraform.tfvars +4 -0
- package/template/base/terraform/url-map.tf +23 -0
- package/template/base/terraform/variables.tf +21 -0
- package/template/old/.env.example +0 -17
- package/template/old/.eslintrc.cjs +0 -26
- package/template/old/components.json +0 -17
- package/template/old/drizzle.config.ts +0 -11
- package/template/old/next.config.mjs +0 -4
- package/template/old/package.json +0 -87
- package/template/old/postcss.config.mjs +0 -8
- package/template/old/prettier.config.mjs +0 -11
- package/template/old/public/next.svg +0 -1
- package/template/old/public/vercel.svg +0 -1
- package/template/old/src/app/(app)/dashboard/layout.tsx +0 -14
- package/template/old/src/app/(app)/dashboard/page.tsx +0 -12
- package/template/old/src/app/(app)/layout.tsx +0 -125
- package/template/old/src/app/(app)/roles/[id]/actions.ts +0 -59
- package/template/old/src/app/(app)/roles/[id]/page.tsx +0 -46
- package/template/old/src/app/(app)/roles/[id]/render.tsx +0 -264
- package/template/old/src/app/(app)/roles/[id]/validation.ts +0 -22
- package/template/old/src/app/(app)/roles/actions.ts +0 -33
- package/template/old/src/app/(app)/roles/page.tsx +0 -52
- package/template/old/src/app/(app)/roles/render.tsx +0 -80
- package/template/old/src/app/(app)/settings/actions.ts +0 -17
- package/template/old/src/app/(app)/settings/layout.tsx +0 -14
- package/template/old/src/app/(app)/settings/loading.tsx +0 -28
- package/template/old/src/app/(app)/settings/page.tsx +0 -19
- package/template/old/src/app/(app)/settings/render.tsx +0 -107
- package/template/old/src/app/(app)/settings/validation.ts +0 -5
- package/template/old/src/app/(app)/tasks/[id]/actions.ts +0 -92
- package/template/old/src/app/(app)/tasks/[id]/page.tsx +0 -59
- package/template/old/src/app/(app)/tasks/[id]/render.tsx +0 -482
- package/template/old/src/app/(app)/tasks/[id]/validation.ts +0 -18
- package/template/old/src/app/(app)/tasks/actions.ts +0 -39
- package/template/old/src/app/(app)/tasks/page.tsx +0 -59
- package/template/old/src/app/(app)/tasks/render.tsx +0 -129
- package/template/old/src/app/(app)/tasks/statusEnum.ts +0 -3
- package/template/old/src/app/(app)/users/[id]/actions.ts +0 -68
- package/template/old/src/app/(app)/users/[id]/page.tsx +0 -39
- package/template/old/src/app/(app)/users/[id]/render.tsx +0 -325
- package/template/old/src/app/(app)/users/[id]/validation.ts +0 -18
- package/template/old/src/app/(app)/users/actions.ts +0 -34
- package/template/old/src/app/(app)/users/page.tsx +0 -62
- package/template/old/src/app/(app)/users/render.tsx +0 -124
- package/template/old/src/app/(auth)/layout.tsx +0 -27
- package/template/old/src/app/(auth)/login/page.tsx +0 -49
- package/template/old/src/app/(auth)/login/render.tsx +0 -146
- package/template/old/src/app/(marketing)/layout.tsx +0 -42
- package/template/old/src/app/(marketing)/page.tsx +0 -97
- package/template/old/src/app/api/auth/[...nextauth]/route.ts +0 -7
- package/template/old/src/app/favicon.ico +0 -0
- package/template/old/src/app/globals.css +0 -76
- package/template/old/src/app/layout.tsx +0 -31
- package/template/old/src/components/app-nav-command.tsx +0 -178
- package/template/old/src/components/dark-mode-toggle.tsx +0 -43
- package/template/old/src/components/data-table/data-table-column-header.tsx +0 -91
- package/template/old/src/components/data-table/data-table-faceted-filter.tsx +0 -149
- package/template/old/src/components/data-table/data-table-loading.tsx +0 -77
- package/template/old/src/components/data-table/data-table-pagination.tsx +0 -94
- package/template/old/src/components/data-table/data-table-toolbar.tsx +0 -78
- package/template/old/src/components/data-table/data-table-types.ts +0 -17
- package/template/old/src/components/data-table/data-table-view-options.tsx +0 -59
- package/template/old/src/components/data-table/data-table.tsx +0 -301
- package/template/old/src/components/data-table/simple-data-table.tsx +0 -109
- package/template/old/src/components/empty-placeholder.tsx +0 -78
- package/template/old/src/components/heading.tsx +0 -17
- package/template/old/src/components/icons.tsx +0 -101
- package/template/old/src/components/main-nav.tsx +0 -64
- package/template/old/src/components/mobile-nav.tsx +0 -50
- package/template/old/src/components/shell.tsx +0 -13
- package/template/old/src/components/side-nav.tsx +0 -51
- package/template/old/src/components/site-footer.tsx +0 -31
- package/template/old/src/components/system-info.tsx +0 -36
- package/template/old/src/components/ui/alert-dialog.tsx +0 -141
- package/template/old/src/components/ui/avatar.tsx +0 -50
- package/template/old/src/components/ui/badge.tsx +0 -36
- package/template/old/src/components/ui/button.tsx +0 -56
- package/template/old/src/components/ui/card.tsx +0 -79
- package/template/old/src/components/ui/checkbox.tsx +0 -30
- package/template/old/src/components/ui/command.tsx +0 -155
- package/template/old/src/components/ui/dialog.tsx +0 -122
- package/template/old/src/components/ui/dropdown-menu.tsx +0 -200
- package/template/old/src/components/ui/form.tsx +0 -176
- package/template/old/src/components/ui/input.tsx +0 -25
- package/template/old/src/components/ui/label.tsx +0 -26
- package/template/old/src/components/ui/popover.tsx +0 -31
- package/template/old/src/components/ui/select.tsx +0 -160
- package/template/old/src/components/ui/separator.tsx +0 -31
- package/template/old/src/components/ui/skeleton.tsx +0 -15
- package/template/old/src/components/ui/sonner.tsx +0 -31
- package/template/old/src/components/ui/table.tsx +0 -117
- package/template/old/src/components/user-account-nav.tsx +0 -60
- package/template/old/src/components/user-avatar.tsx +0 -27
- package/template/old/src/data-access/resource.ts +0 -85
- package/template/old/src/data-access/role-permission.ts +0 -45
- package/template/old/src/data-access/role.ts +0 -105
- package/template/old/src/data-access/task.ts +0 -141
- package/template/old/src/data-access/user.ts +0 -141
- package/template/old/src/db/index.ts +0 -14
- package/template/old/src/db/schema.ts +0 -287
- package/template/old/src/env.mjs +0 -58
- package/template/old/src/hooks/use-debounce.tsx +0 -17
- package/template/old/src/hooks/use-lock-body.tsx +0 -13
- package/template/old/src/lib/auth.ts +0 -89
- package/template/old/src/lib/cn.ts +0 -4
- package/template/old/src/lib/currentUser.ts +0 -8
- package/template/old/src/lib/delay.ts +0 -2
- package/template/old/src/lib/formatDate.ts +0 -11
- package/template/old/src/lib/generateLabel.ts +0 -5
- package/template/old/src/lib/s3Client.ts +0 -156
- package/template/old/src/lib/safe-action.ts +0 -19
- package/template/old/src/lib/siteConfig.ts +0 -4
- package/template/old/src/lib/sortOrderEnum.ts +0 -2
- package/template/old/src/use-case/role-permission.ts +0 -19
- package/template/old/src/use-case/role.ts +0 -110
- package/template/old/src/use-case/task.ts +0 -166
- package/template/old/src/use-case/user.ts +0 -121
- package/template/old/tailwind.config.ts +0 -81
- package/template/old/tsconfig.json +0 -47
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { Metadata } from 'next'
|
|
2
|
-
import { notFound } from 'next/navigation'
|
|
3
|
-
|
|
4
|
-
import { authGuard, getAuthSession } from '@/lib/auth'
|
|
5
|
-
import { siteConfig } from '@/lib/siteConfig'
|
|
6
|
-
import { getUsersAction } from './actions'
|
|
7
|
-
import { Render } from './render'
|
|
8
|
-
|
|
9
|
-
export const metadata: Metadata = {
|
|
10
|
-
title: 'Users' + ' | ' + siteConfig.name
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const Users = async ({
|
|
14
|
-
searchParams
|
|
15
|
-
}: {
|
|
16
|
-
searchParams: {
|
|
17
|
-
page?: string
|
|
18
|
-
limit?: string
|
|
19
|
-
sort?: string
|
|
20
|
-
name?: string
|
|
21
|
-
email?: string
|
|
22
|
-
type?: string
|
|
23
|
-
}
|
|
24
|
-
}) => {
|
|
25
|
-
const session = await getAuthSession()
|
|
26
|
-
if (!session) return notFound()
|
|
27
|
-
|
|
28
|
-
const g = await authGuard(session)
|
|
29
|
-
if (!g) return notFound()
|
|
30
|
-
|
|
31
|
-
const page = searchParams.page ? parseInt(searchParams.page) : 1
|
|
32
|
-
const limit = searchParams.limit ? parseInt(searchParams.limit) : 10
|
|
33
|
-
|
|
34
|
-
const [sortBy, sortOrder] =
|
|
35
|
-
typeof searchParams.sort === 'string'
|
|
36
|
-
? (searchParams.sort.split('.') as [any, 'asc' | 'desc' | undefined])
|
|
37
|
-
: []
|
|
38
|
-
|
|
39
|
-
const name = searchParams.name
|
|
40
|
-
const email = searchParams.email
|
|
41
|
-
|
|
42
|
-
const type =
|
|
43
|
-
typeof searchParams.type === 'string'
|
|
44
|
-
? (searchParams.type.split('.') as any[])
|
|
45
|
-
: undefined
|
|
46
|
-
|
|
47
|
-
const data = await getUsersAction({
|
|
48
|
-
page,
|
|
49
|
-
limit,
|
|
50
|
-
sortBy,
|
|
51
|
-
sortOrder,
|
|
52
|
-
name,
|
|
53
|
-
email,
|
|
54
|
-
type
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
if (!data?.data) throw new Error('Users data not found')
|
|
58
|
-
|
|
59
|
-
return <Render data={data.data} />
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export default Users
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import Link from 'next/link'
|
|
4
|
-
import { FC } from 'react'
|
|
5
|
-
|
|
6
|
-
import { DataTable } from '@/components/data-table/data-table'
|
|
7
|
-
import { DataTableColumnHeader } from '@/components/data-table/data-table-column-header'
|
|
8
|
-
import { Heading } from '@/components/heading'
|
|
9
|
-
import { Icons } from '@/components/icons'
|
|
10
|
-
import { Shell } from '@/components/shell'
|
|
11
|
-
import { Button } from '@/components/ui/button'
|
|
12
|
-
import { User } from '@/db/schema'
|
|
13
|
-
import { formatDateTime, timesAgo } from '@/lib/formatDate'
|
|
14
|
-
import { generateLabel } from '@/lib/generateLabel'
|
|
15
|
-
import { InferSelectModel } from 'drizzle-orm'
|
|
16
|
-
|
|
17
|
-
const types = User.type.enumValues.map(x => ({
|
|
18
|
-
label: generateLabel(x),
|
|
19
|
-
value: x
|
|
20
|
-
}))
|
|
21
|
-
|
|
22
|
-
export const Render: FC<{
|
|
23
|
-
data: {
|
|
24
|
-
users: {
|
|
25
|
-
id: string
|
|
26
|
-
name?: string | null
|
|
27
|
-
email: string
|
|
28
|
-
type?: InferSelectModel<typeof User>['type']
|
|
29
|
-
role?: {
|
|
30
|
-
name: string
|
|
31
|
-
} | null
|
|
32
|
-
createdAt: Date
|
|
33
|
-
updatedAt?: Date | null
|
|
34
|
-
}[]
|
|
35
|
-
total: number
|
|
36
|
-
}
|
|
37
|
-
}> = ({ data }) => {
|
|
38
|
-
return (
|
|
39
|
-
<Shell>
|
|
40
|
-
<Heading heading="Users" text="List of all the users">
|
|
41
|
-
<Link href="/users/new">
|
|
42
|
-
<Button>
|
|
43
|
-
<Icons.add className="mr-2 h-4 w-4" />
|
|
44
|
-
New
|
|
45
|
-
</Button>
|
|
46
|
-
</Link>
|
|
47
|
-
</Heading>
|
|
48
|
-
<DataTable
|
|
49
|
-
columns={[
|
|
50
|
-
{
|
|
51
|
-
accessorKey: 'name',
|
|
52
|
-
header: ({ column }) => (
|
|
53
|
-
<DataTableColumnHeader column={column} title="Name" />
|
|
54
|
-
),
|
|
55
|
-
cell: ({ row }) => (
|
|
56
|
-
<Link
|
|
57
|
-
className="underline underline-offset-4"
|
|
58
|
-
href={`/users/${row.original.id}`}
|
|
59
|
-
>
|
|
60
|
-
{row.original.name}
|
|
61
|
-
</Link>
|
|
62
|
-
)
|
|
63
|
-
},
|
|
64
|
-
{
|
|
65
|
-
accessorKey: 'email',
|
|
66
|
-
header: ({ column }) => (
|
|
67
|
-
<DataTableColumnHeader column={column} title="Email" />
|
|
68
|
-
)
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
accessorKey: 'type',
|
|
72
|
-
header: ({ column }) => (
|
|
73
|
-
<DataTableColumnHeader column={column} title="Type" />
|
|
74
|
-
)
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
accessorKey: 'role',
|
|
78
|
-
header: ({ column }) => (
|
|
79
|
-
<DataTableColumnHeader column={column} title="Role" />
|
|
80
|
-
),
|
|
81
|
-
cell: ({ row }) => row.original.role?.name,
|
|
82
|
-
enableSorting: false
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
accessorKey: 'createdAt',
|
|
86
|
-
header: ({ column }) => (
|
|
87
|
-
<DataTableColumnHeader column={column} title="Created At" />
|
|
88
|
-
),
|
|
89
|
-
cell: ({ row }) => formatDateTime(row.original.createdAt)
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
accessorKey: 'updatedAt',
|
|
93
|
-
header: ({ column }) => (
|
|
94
|
-
<DataTableColumnHeader column={column} title="Updated At" />
|
|
95
|
-
),
|
|
96
|
-
cell: ({ row }) =>
|
|
97
|
-
row.original.updatedAt
|
|
98
|
-
? timesAgo(row.original.updatedAt)
|
|
99
|
-
: undefined
|
|
100
|
-
}
|
|
101
|
-
]}
|
|
102
|
-
data={data.users}
|
|
103
|
-
total={data.total}
|
|
104
|
-
searchableColumns={[
|
|
105
|
-
{
|
|
106
|
-
id: 'name',
|
|
107
|
-
title: 'Name'
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
id: 'email',
|
|
111
|
-
title: 'Email'
|
|
112
|
-
}
|
|
113
|
-
]}
|
|
114
|
-
filterableColumns={[
|
|
115
|
-
{
|
|
116
|
-
id: 'type',
|
|
117
|
-
title: 'Type',
|
|
118
|
-
options: types
|
|
119
|
-
}
|
|
120
|
-
]}
|
|
121
|
-
/>
|
|
122
|
-
</Shell>
|
|
123
|
-
)
|
|
124
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Metadata } from 'next'
|
|
2
|
-
import { redirect } from 'next/navigation'
|
|
3
|
-
import { ReactNode } from 'react'
|
|
4
|
-
|
|
5
|
-
import { SiteFooter } from '@/components/site-footer'
|
|
6
|
-
import { getAuthSession } from '@/lib/auth'
|
|
7
|
-
import { siteConfig } from '@/lib/siteConfig'
|
|
8
|
-
|
|
9
|
-
export const metadata: Metadata = {
|
|
10
|
-
title: 'Login | ' + siteConfig.name,
|
|
11
|
-
description: siteConfig.description
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const AuthLayout = async ({ children }: { children: ReactNode }) => {
|
|
15
|
-
const session = await getAuthSession()
|
|
16
|
-
|
|
17
|
-
if (session?.user) return redirect('/dashboard')
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<div className="flex min-h-screen flex-col justify-between">
|
|
21
|
-
<main>{children}</main>
|
|
22
|
-
<SiteFooter />
|
|
23
|
-
</div>
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default AuthLayout
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import Link from 'next/link'
|
|
2
|
-
|
|
3
|
-
import { Icons } from '@/components/icons'
|
|
4
|
-
import { Button } from '@/components/ui/button'
|
|
5
|
-
import { Render } from './render'
|
|
6
|
-
|
|
7
|
-
const LoginPage = () => {
|
|
8
|
-
return (
|
|
9
|
-
<div className="container flex h-[90vh] w-screen flex-col items-center justify-center">
|
|
10
|
-
<Link href="/" className="absolute left-4 top-4">
|
|
11
|
-
<Button variant="ghost">
|
|
12
|
-
<Icons.chevronLeft className="mr-2 h-4 w-4" />
|
|
13
|
-
Back
|
|
14
|
-
</Button>
|
|
15
|
-
</Link>
|
|
16
|
-
<div className="mx-auto flex w-full flex-col justify-center space-y-6 sm:w-80">
|
|
17
|
-
<div className="flex flex-col space-y-2 text-center">
|
|
18
|
-
<Icons.logo className="mx-auto h-6 w-6" />
|
|
19
|
-
<h1 className="text-2xl font-semibold tracking-tight">
|
|
20
|
-
Welcome back
|
|
21
|
-
</h1>
|
|
22
|
-
<p className="text-sm text-muted-foreground">
|
|
23
|
-
Enter your email to sign in to your account
|
|
24
|
-
</p>
|
|
25
|
-
</div>
|
|
26
|
-
<Render />
|
|
27
|
-
<p className="px-8 text-center text-sm text-muted-foreground">
|
|
28
|
-
By clicking continue, you agree to our{' '}
|
|
29
|
-
<Link
|
|
30
|
-
href="/terms"
|
|
31
|
-
className="underline underline-offset-4 hover:text-primary"
|
|
32
|
-
>
|
|
33
|
-
Terms of Service
|
|
34
|
-
</Link>{' '}
|
|
35
|
-
and{' '}
|
|
36
|
-
<Link
|
|
37
|
-
href="/privacy"
|
|
38
|
-
className="underline underline-offset-4 hover:text-primary"
|
|
39
|
-
>
|
|
40
|
-
Privacy Policy
|
|
41
|
-
</Link>
|
|
42
|
-
.
|
|
43
|
-
</p>
|
|
44
|
-
</div>
|
|
45
|
-
</div>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export default LoginPage
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { zodResolver } from '@hookform/resolvers/zod'
|
|
4
|
-
import { signIn } from 'next-auth/react'
|
|
5
|
-
import { useSearchParams } from 'next/navigation'
|
|
6
|
-
import { FC, HTMLAttributes, useState } from 'react'
|
|
7
|
-
import { useForm } from 'react-hook-form'
|
|
8
|
-
import { toast } from 'sonner'
|
|
9
|
-
import { z } from 'zod'
|
|
10
|
-
|
|
11
|
-
import { Icons } from '@/components/icons'
|
|
12
|
-
import { Button } from '@/components/ui/button'
|
|
13
|
-
import { Input } from '@/components/ui/input'
|
|
14
|
-
import { Label } from '@/components/ui/label'
|
|
15
|
-
import { cn } from '@/lib/cn'
|
|
16
|
-
|
|
17
|
-
const userAuthSchema = z.object({
|
|
18
|
-
email: z.string().email()
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
type FormData = z.infer<typeof userAuthSchema>
|
|
22
|
-
|
|
23
|
-
interface RenderProps extends HTMLAttributes<HTMLDivElement> {}
|
|
24
|
-
|
|
25
|
-
export const Render: FC = ({ className, ...props }: RenderProps) => {
|
|
26
|
-
const {
|
|
27
|
-
register,
|
|
28
|
-
handleSubmit,
|
|
29
|
-
formState: { errors }
|
|
30
|
-
} = useForm<FormData>({
|
|
31
|
-
resolver: zodResolver(userAuthSchema)
|
|
32
|
-
})
|
|
33
|
-
const [isLoading, setIsLoading] = useState(false)
|
|
34
|
-
const [isGoogleLoading, setIsGoogleLoading] = useState(false)
|
|
35
|
-
const [isGithubLoading, setIsGithubLoading] = useState(false)
|
|
36
|
-
|
|
37
|
-
const searchParams = useSearchParams()
|
|
38
|
-
|
|
39
|
-
async function onSubmit(data: FormData) {
|
|
40
|
-
setIsLoading(true)
|
|
41
|
-
|
|
42
|
-
const signInResult = await signIn('email', {
|
|
43
|
-
email: data.email.toLowerCase(),
|
|
44
|
-
redirect: false,
|
|
45
|
-
callbackUrl: searchParams?.get('from') || '/dashboard'
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
setIsLoading(false)
|
|
49
|
-
|
|
50
|
-
if (!signInResult?.ok) {
|
|
51
|
-
return toast('Something went wrong.', {
|
|
52
|
-
description: 'Your sign in request failed. Please try again.'
|
|
53
|
-
})
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return toast('Check your email', {
|
|
57
|
-
description: 'We sent you a login link. Be sure to check your spam too.'
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<div className={cn('grid gap-6', className)} {...props}>
|
|
63
|
-
<form onSubmit={handleSubmit(onSubmit)}>
|
|
64
|
-
<div className="grid gap-2">
|
|
65
|
-
<div className="grid gap-1">
|
|
66
|
-
<Label className="sr-only" htmlFor="email">
|
|
67
|
-
Email
|
|
68
|
-
</Label>
|
|
69
|
-
<Input
|
|
70
|
-
id="email"
|
|
71
|
-
placeholder="name@example.com"
|
|
72
|
-
type="email"
|
|
73
|
-
autoCapitalize="none"
|
|
74
|
-
autoComplete="email"
|
|
75
|
-
autoCorrect="off"
|
|
76
|
-
disabled={isLoading}
|
|
77
|
-
{...register('email')}
|
|
78
|
-
/>
|
|
79
|
-
{errors?.email && (
|
|
80
|
-
<p className="px-1 text-xs text-red-600">
|
|
81
|
-
{errors.email.message}
|
|
82
|
-
</p>
|
|
83
|
-
)}
|
|
84
|
-
</div>
|
|
85
|
-
<Button
|
|
86
|
-
disabled={isLoading || isGoogleLoading || isGithubLoading}
|
|
87
|
-
type="submit"
|
|
88
|
-
>
|
|
89
|
-
{isLoading && (
|
|
90
|
-
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
|
|
91
|
-
)}
|
|
92
|
-
Sign in with Email
|
|
93
|
-
</Button>
|
|
94
|
-
</div>
|
|
95
|
-
</form>
|
|
96
|
-
<div className="relative">
|
|
97
|
-
<div className="absolute inset-0 flex items-center">
|
|
98
|
-
<span className="w-full border-t" />
|
|
99
|
-
</div>
|
|
100
|
-
<div className="relative flex justify-center text-xs uppercase">
|
|
101
|
-
<span className="bg-background px-2 text-muted-foreground">
|
|
102
|
-
Or continue with
|
|
103
|
-
</span>
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
<div className="grid gap-2">
|
|
107
|
-
<Button
|
|
108
|
-
variant="outline"
|
|
109
|
-
onClick={async () => {
|
|
110
|
-
setIsGoogleLoading(true)
|
|
111
|
-
await signIn('google', {
|
|
112
|
-
callbackUrl: searchParams?.get('from') || '/dashboard'
|
|
113
|
-
})
|
|
114
|
-
setIsGoogleLoading(false)
|
|
115
|
-
}}
|
|
116
|
-
disabled={isLoading || isGoogleLoading || isGithubLoading}
|
|
117
|
-
>
|
|
118
|
-
{isGoogleLoading ? (
|
|
119
|
-
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
|
|
120
|
-
) : (
|
|
121
|
-
<Icons.google className="mr-2 h-4 w-4" />
|
|
122
|
-
)}
|
|
123
|
-
Google
|
|
124
|
-
</Button>
|
|
125
|
-
<Button
|
|
126
|
-
variant="outline"
|
|
127
|
-
onClick={async () => {
|
|
128
|
-
setIsGithubLoading(true)
|
|
129
|
-
await signIn('github', {
|
|
130
|
-
callbackUrl: searchParams?.get('from') || '/dashboard'
|
|
131
|
-
})
|
|
132
|
-
setIsGithubLoading(false)
|
|
133
|
-
}}
|
|
134
|
-
disabled={isLoading || isGoogleLoading || isGithubLoading}
|
|
135
|
-
>
|
|
136
|
-
{isGithubLoading ? (
|
|
137
|
-
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
|
|
138
|
-
) : (
|
|
139
|
-
<Icons.github className="mr-2 h-4 w-4" />
|
|
140
|
-
)}
|
|
141
|
-
Github
|
|
142
|
-
</Button>
|
|
143
|
-
</div>
|
|
144
|
-
</div>
|
|
145
|
-
)
|
|
146
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import Link from 'next/link'
|
|
2
|
-
import { ReactNode } from 'react'
|
|
3
|
-
|
|
4
|
-
import { Icons } from '@/components/icons'
|
|
5
|
-
import { MainNav } from '@/components/main-nav'
|
|
6
|
-
import { SiteFooter } from '@/components/site-footer'
|
|
7
|
-
import { Button } from '@/components/ui/button'
|
|
8
|
-
import { getAuthSession } from '@/lib/auth'
|
|
9
|
-
|
|
10
|
-
const Layout = async ({ children }: { children: ReactNode }) => {
|
|
11
|
-
const session = await getAuthSession()
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<div className="flex min-h-screen flex-col space-y-4">
|
|
15
|
-
<header className="sticky top-0 z-40 border-b bg-background">
|
|
16
|
-
<div className="container flex h-16 items-center justify-between py-4">
|
|
17
|
-
<MainNav />
|
|
18
|
-
<div className="flex items-center justify-center space-x-4">
|
|
19
|
-
{session?.user ? (
|
|
20
|
-
<Link href="/dashboard">
|
|
21
|
-
<Button size="sm" variant="secondary">
|
|
22
|
-
Dashboard
|
|
23
|
-
</Button>
|
|
24
|
-
</Link>
|
|
25
|
-
) : (
|
|
26
|
-
<Link href="/login">
|
|
27
|
-
<Button size="sm" variant="secondary">
|
|
28
|
-
<Icons.login className="mr-2 h-4 w-4" />
|
|
29
|
-
Sign In
|
|
30
|
-
</Button>
|
|
31
|
-
</Link>
|
|
32
|
-
)}
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
</header>
|
|
36
|
-
<main className="flex-1">{children}</main>
|
|
37
|
-
<SiteFooter className="border-t" />
|
|
38
|
-
</div>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export default Layout
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import Image from 'next/image'
|
|
2
|
-
|
|
3
|
-
const links = [
|
|
4
|
-
{
|
|
5
|
-
href: 'https://nextjs.org',
|
|
6
|
-
title: 'Next.js',
|
|
7
|
-
description: 'Find in-depth information about Next.js features and API.'
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
href: 'https://typescriptlang.org',
|
|
11
|
-
title: 'TypeScript',
|
|
12
|
-
description:
|
|
13
|
-
'Learn about TypeScript and how it helps you write better JavaScript.'
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
href: 'https://orm.drizzle.team',
|
|
17
|
-
title: 'Drizzle',
|
|
18
|
-
description:
|
|
19
|
-
'Lightweight and performant TypeScript ORM with developer experience in mind.'
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
href: 'https://next-auth.js.org',
|
|
23
|
-
title: 'NextAuth.js',
|
|
24
|
-
description:
|
|
25
|
-
'Authentication for Next.js featuring sign in with GitHub and more.'
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
href: 'https://tailwindcss.com',
|
|
29
|
-
title: 'Tailwind CSS',
|
|
30
|
-
description:
|
|
31
|
-
'A utility-first CSS framework for rapidly building custom designs.'
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
href: 'https://shadcn.com',
|
|
35
|
-
title: 'Shadcn UI',
|
|
36
|
-
description: 'An awesome ui component library with tailwindcss.'
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
href: 'https://nextjs.org/docs/app/api-reference/functions/server-actions',
|
|
40
|
-
title: 'Server Actions',
|
|
41
|
-
description: 'Learn about Server Actions and how to use them.'
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
href: 'https://vercel.com/new',
|
|
45
|
-
title: 'Vercel',
|
|
46
|
-
description:
|
|
47
|
-
'Instantly deploy your Next.js site to a shareable URL with Vercel.'
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
const Home = () => {
|
|
52
|
-
return (
|
|
53
|
-
<div className="flex min-h-[80vh] flex-col items-center justify-between">
|
|
54
|
-
<div className="w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
|
|
55
|
-
<p className="flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
|
|
56
|
-
Get started by editing
|
|
57
|
-
<code className="font-mono font-bold">src/app/page.tsx</code>
|
|
58
|
-
</p>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<div className="before:bg-gradient-radial after:bg-gradient-conic z-[-1] mt-12 flex place-items-center before:absolute before:h-[300px] before:w-[240px] before:-translate-x-1/2 before:rounded-full before:from-white before:to-transparent before:blur-2xl before:content-[''] after:absolute after:-z-20 after:h-[180px] after:w-[120px] after:translate-x-1/3 after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[''] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700 before:dark:opacity-10 after:dark:from-sky-900 after:dark:via-[#0141ff] after:dark:opacity-40 before:lg:h-[360px]">
|
|
62
|
-
<Image
|
|
63
|
-
className="dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert"
|
|
64
|
-
src="/next.svg"
|
|
65
|
-
alt="Next.js Logo"
|
|
66
|
-
width={180}
|
|
67
|
-
height={37}
|
|
68
|
-
priority
|
|
69
|
-
/>
|
|
70
|
-
</div>
|
|
71
|
-
|
|
72
|
-
<div className="my-8 grid gap-2 text-center lg:w-full lg:max-w-5xl lg:grid-cols-4 lg:text-left">
|
|
73
|
-
{links.map((l, i) => (
|
|
74
|
-
<a
|
|
75
|
-
key={i}
|
|
76
|
-
href={l.href}
|
|
77
|
-
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
|
78
|
-
target="_blank"
|
|
79
|
-
rel="noopener noreferrer"
|
|
80
|
-
>
|
|
81
|
-
<h2 className="mb-3 text-2xl font-semibold">
|
|
82
|
-
{l.title + ' '}
|
|
83
|
-
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
84
|
-
->
|
|
85
|
-
</span>
|
|
86
|
-
</h2>
|
|
87
|
-
<p className="m-0 max-w-[30ch] text-sm opacity-50">
|
|
88
|
-
{l.description}
|
|
89
|
-
</p>
|
|
90
|
-
</a>
|
|
91
|
-
))}
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export default Home
|
|
Binary file
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
@tailwind base;
|
|
2
|
-
@tailwind components;
|
|
3
|
-
@tailwind utilities;
|
|
4
|
-
|
|
5
|
-
@layer base {
|
|
6
|
-
:root {
|
|
7
|
-
--background: 0 0% 100%;
|
|
8
|
-
--foreground: 0 0% 3.9%;
|
|
9
|
-
|
|
10
|
-
--card: 0 0% 100%;
|
|
11
|
-
--card-foreground: 0 0% 3.9%;
|
|
12
|
-
|
|
13
|
-
--popover: 0 0% 100%;
|
|
14
|
-
--popover-foreground: 0 0% 3.9%;
|
|
15
|
-
|
|
16
|
-
--primary: 0 0% 9%;
|
|
17
|
-
--primary-foreground: 0 0% 98%;
|
|
18
|
-
|
|
19
|
-
--secondary: 0 0% 96.1%;
|
|
20
|
-
--secondary-foreground: 0 0% 9%;
|
|
21
|
-
|
|
22
|
-
--muted: 0 0% 96.1%;
|
|
23
|
-
--muted-foreground: 0 0% 45.1%;
|
|
24
|
-
|
|
25
|
-
--accent: 0 0% 96.1%;
|
|
26
|
-
--accent-foreground: 0 0% 9%;
|
|
27
|
-
|
|
28
|
-
--destructive: 0 84.2% 60.2%;
|
|
29
|
-
--destructive-foreground: 0 0% 98%;
|
|
30
|
-
|
|
31
|
-
--border: 0 0% 89.8%;
|
|
32
|
-
--input: 0 0% 89.8%;
|
|
33
|
-
--ring: 0 0% 3.9%;
|
|
34
|
-
|
|
35
|
-
--radius: 0.5rem;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
.dark {
|
|
39
|
-
--background: 0 0% 3.9%;
|
|
40
|
-
--foreground: 0 0% 98%;
|
|
41
|
-
|
|
42
|
-
--card: 0 0% 3.9%;
|
|
43
|
-
--card-foreground: 0 0% 98%;
|
|
44
|
-
|
|
45
|
-
--popover: 0 0% 3.9%;
|
|
46
|
-
--popover-foreground: 0 0% 98%;
|
|
47
|
-
|
|
48
|
-
--primary: 0 0% 98%;
|
|
49
|
-
--primary-foreground: 0 0% 9%;
|
|
50
|
-
|
|
51
|
-
--secondary: 0 0% 14.9%;
|
|
52
|
-
--secondary-foreground: 0 0% 98%;
|
|
53
|
-
|
|
54
|
-
--muted: 0 0% 14.9%;
|
|
55
|
-
--muted-foreground: 0 0% 63.9%;
|
|
56
|
-
|
|
57
|
-
--accent: 0 0% 14.9%;
|
|
58
|
-
--accent-foreground: 0 0% 98%;
|
|
59
|
-
|
|
60
|
-
--destructive: 0 62.8% 30.6%;
|
|
61
|
-
--destructive-foreground: 0 0% 98%;
|
|
62
|
-
|
|
63
|
-
--border: 0 0% 14.9%;
|
|
64
|
-
--input: 0 0% 14.9%;
|
|
65
|
-
--ring: 0 0% 83.1%;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
@layer base {
|
|
70
|
-
* {
|
|
71
|
-
@apply border-border;
|
|
72
|
-
}
|
|
73
|
-
body {
|
|
74
|
-
@apply bg-background text-foreground;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { GeistSans } from 'geist/font/sans'
|
|
2
|
-
import type { Metadata } from 'next'
|
|
3
|
-
import { ThemeProvider } from 'next-themes'
|
|
4
|
-
import NextTopLoader from 'nextjs-toploader'
|
|
5
|
-
|
|
6
|
-
import { Toaster } from '@/components/ui/sonner'
|
|
7
|
-
import { siteConfig } from '@/lib/siteConfig'
|
|
8
|
-
import './globals.css'
|
|
9
|
-
|
|
10
|
-
export const metadata: Metadata = {
|
|
11
|
-
title: siteConfig.name,
|
|
12
|
-
description: siteConfig.description
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default function RootLayout({
|
|
16
|
-
children
|
|
17
|
-
}: Readonly<{
|
|
18
|
-
children: React.ReactNode
|
|
19
|
-
}>) {
|
|
20
|
-
return (
|
|
21
|
-
<html lang="en" className={GeistSans.className} suppressHydrationWarning>
|
|
22
|
-
<body className="min-h-screen bg-background font-sans antialiased">
|
|
23
|
-
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
|
24
|
-
<NextTopLoader color="#777" />
|
|
25
|
-
{children}
|
|
26
|
-
<Toaster />
|
|
27
|
-
</ThemeProvider>
|
|
28
|
-
</body>
|
|
29
|
-
</html>
|
|
30
|
-
)
|
|
31
|
-
}
|