@xk2800/nextjs-template 0.5.3

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 (199) hide show
  1. package/README.md +339 -0
  2. package/components/auth/authCard.tsx +92 -0
  3. package/components/auth/authShell.tsx +46 -0
  4. package/components/auth/deviceCheck.tsx +52 -0
  5. package/components/auth/emailPasswordLogin.tsx +136 -0
  6. package/components/auth/emailPasswordSignup.tsx +178 -0
  7. package/components/auth/forgotPasswordForm.tsx +66 -0
  8. package/components/auth/loginErrorRecovery.tsx +117 -0
  9. package/components/auth/logoutButtons.tsx +26 -0
  10. package/components/auth/oneTap.tsx +50 -0
  11. package/components/auth/passkeyLogin.tsx +50 -0
  12. package/components/auth/resetPasswordForm.tsx +112 -0
  13. package/components/auth/socialLogin.tsx +51 -0
  14. package/components/auth/twoFactorVerifyForm.tsx +100 -0
  15. package/components/dashboard/accountDetailsCard.tsx +58 -0
  16. package/components/dashboard/activityLogsCard.tsx +141 -0
  17. package/components/dashboard/activityLogsCardSkeleton.tsx +20 -0
  18. package/components/dashboard/activityLogsSection.tsx +16 -0
  19. package/components/dashboard/admin/activityLogsSection.tsx +13 -0
  20. package/components/dashboard/admin/adminActivityLogsSkeleton.tsx +34 -0
  21. package/components/dashboard/admin/adminActivityLogsTable.tsx +319 -0
  22. package/components/dashboard/admin/adminStatsGrid.tsx +30 -0
  23. package/components/dashboard/admin/adminStatsGridSkeleton.tsx +16 -0
  24. package/components/dashboard/admin/adminUserActivityLogCard.tsx +53 -0
  25. package/components/dashboard/admin/adminUserActivityLogTable.tsx +69 -0
  26. package/components/dashboard/admin/adminUserLoginInfoCard.tsx +50 -0
  27. package/components/dashboard/admin/adminUserLoginInfoTable.tsx +63 -0
  28. package/components/dashboard/admin/adminUserSessionsCard.tsx +225 -0
  29. package/components/dashboard/admin/adminUserStatusCard.tsx +57 -0
  30. package/components/dashboard/admin/adminUsersSkeleton.tsx +30 -0
  31. package/components/dashboard/admin/adminUsersTable.tsx +596 -0
  32. package/components/dashboard/admin/deviceThrottleCard.tsx +59 -0
  33. package/components/dashboard/admin/deviceThrottleTable.tsx +93 -0
  34. package/components/dashboard/admin/impersonationLogCard.tsx +73 -0
  35. package/components/dashboard/admin/newDeviceCard.tsx +35 -0
  36. package/components/dashboard/admin/newDeviceTable.tsx +71 -0
  37. package/components/dashboard/admin/revokeAllSessionsButton.tsx +77 -0
  38. package/components/dashboard/admin/sharedDeviceCard.tsx +42 -0
  39. package/components/dashboard/admin/sharedDeviceTable.tsx +75 -0
  40. package/components/dashboard/admin/systemSettingsForm.tsx +200 -0
  41. package/components/dashboard/admin/systemSettingsSection.tsx +9 -0
  42. package/components/dashboard/admin/systemSettingsSkeleton.tsx +29 -0
  43. package/components/dashboard/admin/usersSection.tsx +13 -0
  44. package/components/dashboard/adminSection.tsx +31 -0
  45. package/components/dashboard/appSidebar.tsx +170 -0
  46. package/components/dashboard/dashboardHeader.tsx +85 -0
  47. package/components/dashboard/heartbeat.tsx +22 -0
  48. package/components/dashboard/impersonationBanner.tsx +50 -0
  49. package/components/dashboard/passkeysCard.tsx +187 -0
  50. package/components/dashboard/profileCard.tsx +42 -0
  51. package/components/dashboard/sectionErrorFallback.tsx +18 -0
  52. package/components/dashboard/sessionsCard.tsx +194 -0
  53. package/components/dashboard/sessionsCardSkeleton.tsx +20 -0
  54. package/components/dashboard/sessionsSection.tsx +22 -0
  55. package/components/dashboard/twoFactorCard.tsx +344 -0
  56. package/components/dashboard/verifyEmailButton.tsx +32 -0
  57. package/components/email/email-template-2.tsx +44 -0
  58. package/components/email/email-template-new-device.tsx +80 -0
  59. package/components/email/email-template-passkey-change.tsx +81 -0
  60. package/components/email/email-template-reset-password.tsx +56 -0
  61. package/components/email/email-template-verify-email.tsx +55 -0
  62. package/components/email/email-template-welcome.tsx +53 -0
  63. package/components/email/email-template.tsx +33 -0
  64. package/components/email/send-email-button.tsx +61 -0
  65. package/components/providers/theme-provider.tsx +9 -0
  66. package/components/theme/theme-toggle.tsx +53 -0
  67. package/components/ui/alert-dialog.tsx +141 -0
  68. package/components/ui/avatar.tsx +53 -0
  69. package/components/ui/badge.tsx +46 -0
  70. package/components/ui/button.tsx +64 -0
  71. package/components/ui/card.tsx +92 -0
  72. package/components/ui/checkbox.tsx +32 -0
  73. package/components/ui/data-table.tsx +302 -0
  74. package/components/ui/dialog.tsx +143 -0
  75. package/components/ui/dropdown-menu.tsx +203 -0
  76. package/components/ui/input.tsx +21 -0
  77. package/components/ui/label.tsx +26 -0
  78. package/components/ui/password-input.tsx +38 -0
  79. package/components/ui/separator.tsx +28 -0
  80. package/components/ui/sheet.tsx +143 -0
  81. package/components/ui/sidebar.tsx +726 -0
  82. package/components/ui/skeleton.tsx +13 -0
  83. package/components/ui/switch.tsx +35 -0
  84. package/components/ui/table.tsx +116 -0
  85. package/components/ui/textarea.tsx +18 -0
  86. package/components/ui/tooltip.tsx +57 -0
  87. package/dist/activity/logger.d.mts +18 -0
  88. package/dist/activity/logger.d.ts +18 -0
  89. package/dist/activity/logger.js +289 -0
  90. package/dist/activity/logger.js.map +1 -0
  91. package/dist/activity/logger.mjs +7 -0
  92. package/dist/activity/logger.mjs.map +1 -0
  93. package/dist/activity/queries.d.mts +98 -0
  94. package/dist/activity/queries.d.ts +98 -0
  95. package/dist/activity/queries.js +372 -0
  96. package/dist/activity/queries.js.map +1 -0
  97. package/dist/activity/queries.mjs +124 -0
  98. package/dist/activity/queries.mjs.map +1 -0
  99. package/dist/admin/queries.d.mts +68 -0
  100. package/dist/admin/queries.d.ts +68 -0
  101. package/dist/admin/queries.js +450 -0
  102. package/dist/admin/queries.js.map +1 -0
  103. package/dist/admin/queries.mjs +143 -0
  104. package/dist/admin/queries.mjs.map +1 -0
  105. package/dist/auth/helpers.d.mts +111 -0
  106. package/dist/auth/helpers.d.ts +111 -0
  107. package/dist/auth/helpers.js +1023 -0
  108. package/dist/auth/helpers.js.map +1 -0
  109. package/dist/auth/helpers.mjs +67 -0
  110. package/dist/auth/helpers.mjs.map +1 -0
  111. package/dist/auth/index.d.mts +6589 -0
  112. package/dist/auth/index.d.ts +6589 -0
  113. package/dist/auth/index.js +968 -0
  114. package/dist/auth/index.js.map +1 -0
  115. package/dist/auth/index.mjs +10 -0
  116. package/dist/auth/index.mjs.map +1 -0
  117. package/dist/auth-client/index.d.mts +4864 -0
  118. package/dist/auth-client/index.d.ts +4864 -0
  119. package/dist/auth-client/index.js +55 -0
  120. package/dist/auth-client/index.js.map +1 -0
  121. package/dist/auth-client/index.mjs +49 -0
  122. package/dist/auth-client/index.mjs.map +1 -0
  123. package/dist/chunk-2FW7C3KS.mjs +556 -0
  124. package/dist/chunk-2FW7C3KS.mjs.map +1 -0
  125. package/dist/chunk-ENGYO4K7.mjs +189 -0
  126. package/dist/chunk-ENGYO4K7.mjs.map +1 -0
  127. package/dist/chunk-GTPTVNY2.mjs +40 -0
  128. package/dist/chunk-GTPTVNY2.mjs.map +1 -0
  129. package/dist/chunk-HYKUB3A2.mjs +30 -0
  130. package/dist/chunk-HYKUB3A2.mjs.map +1 -0
  131. package/dist/chunk-JIYHBWAU.mjs +79 -0
  132. package/dist/chunk-JIYHBWAU.mjs.map +1 -0
  133. package/dist/chunk-K6OJCSJS.mjs +41 -0
  134. package/dist/chunk-K6OJCSJS.mjs.map +1 -0
  135. package/dist/chunk-N6ECRCHY.mjs +61 -0
  136. package/dist/chunk-N6ECRCHY.mjs.map +1 -0
  137. package/dist/chunk-YN4436PK.mjs +33 -0
  138. package/dist/chunk-YN4436PK.mjs.map +1 -0
  139. package/dist/config/env.d.mts +15 -0
  140. package/dist/config/env.d.ts +15 -0
  141. package/dist/config/env.js +42 -0
  142. package/dist/config/env.js.map +1 -0
  143. package/dist/config/env.mjs +4 -0
  144. package/dist/config/env.mjs.map +1 -0
  145. package/dist/db/index.d.mts +10 -0
  146. package/dist/db/index.d.ts +10 -0
  147. package/dist/db/index.js +252 -0
  148. package/dist/db/index.js.map +1 -0
  149. package/dist/db/index.mjs +6 -0
  150. package/dist/db/index.mjs.map +1 -0
  151. package/dist/db/schema.d.mts +2 -0
  152. package/dist/db/schema.d.ts +2 -0
  153. package/dist/db/schema.js +186 -0
  154. package/dist/db/schema.js.map +1 -0
  155. package/dist/db/schema.mjs +4 -0
  156. package/dist/db/schema.mjs.map +1 -0
  157. package/dist/schema-CgkDv4wJ.d.mts +1758 -0
  158. package/dist/schema-CgkDv4wJ.d.ts +1758 -0
  159. package/dist/sessions/queries.d.mts +34 -0
  160. package/dist/sessions/queries.d.ts +34 -0
  161. package/dist/sessions/queries.js +268 -0
  162. package/dist/sessions/queries.js.map +1 -0
  163. package/dist/sessions/queries.mjs +23 -0
  164. package/dist/sessions/queries.mjs.map +1 -0
  165. package/dist/settings/queries.d.mts +20 -0
  166. package/dist/settings/queries.d.ts +20 -0
  167. package/dist/settings/queries.js +344 -0
  168. package/dist/settings/queries.js.map +1 -0
  169. package/dist/settings/queries.mjs +7 -0
  170. package/dist/settings/queries.mjs.map +1 -0
  171. package/dist/types/auth/loginSchema.d.mts +8 -0
  172. package/dist/types/auth/loginSchema.d.ts +8 -0
  173. package/dist/types/auth/loginSchema.js +18 -0
  174. package/dist/types/auth/loginSchema.js.map +1 -0
  175. package/dist/types/auth/loginSchema.mjs +16 -0
  176. package/dist/types/auth/loginSchema.mjs.map +1 -0
  177. package/dist/types/auth/signupSchema.d.mts +9 -0
  178. package/dist/types/auth/signupSchema.d.ts +9 -0
  179. package/dist/types/auth/signupSchema.js +21 -0
  180. package/dist/types/auth/signupSchema.js.map +1 -0
  181. package/dist/types/auth/signupSchema.mjs +19 -0
  182. package/dist/types/auth/signupSchema.mjs.map +1 -0
  183. package/dist/users/queries.d.mts +71 -0
  184. package/dist/users/queries.d.ts +71 -0
  185. package/dist/users/queries.js +293 -0
  186. package/dist/users/queries.js.map +1 -0
  187. package/dist/users/queries.mjs +47 -0
  188. package/dist/users/queries.mjs.map +1 -0
  189. package/lib/auth-client.ts +58 -0
  190. package/lib/cookie-names.ts +13 -0
  191. package/lib/device-fingerprint.ts +21 -0
  192. package/lib/formatters.ts +53 -0
  193. package/lib/recalculate-columns.ts +75 -0
  194. package/lib/request-info.ts +78 -0
  195. package/lib/utils.ts +6 -0
  196. package/package.json +301 -0
  197. package/styles/theme.css +124 -0
  198. package/types/auth/loginSchema.ts +11 -0
  199. package/types/auth/signupSchema.ts +14 -0
@@ -0,0 +1,225 @@
1
+ 'use client'
2
+
3
+ import { useState } from "react"
4
+ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "../../ui/card"
5
+ import { Button } from "../../ui/button"
6
+ import { DataTable, type Column } from "../../ui/data-table"
7
+ import {
8
+ AlertDialog,
9
+ AlertDialogAction,
10
+ AlertDialogCancel,
11
+ AlertDialogContent,
12
+ AlertDialogDescription,
13
+ AlertDialogFooter,
14
+ AlertDialogHeader,
15
+ AlertDialogTitle,
16
+ } from "../../ui/alert-dialog"
17
+ import { formatDateTime } from "../../../lib/formatters"
18
+ import { useRouter } from "next/navigation"
19
+ import { toast } from "sonner"
20
+
21
+ interface Session {
22
+ id: string
23
+ createdAt: Date
24
+ expiresAt: Date
25
+ ipAddress: string | null
26
+ userAgent: string | null
27
+ deviceLabel: string
28
+ location: string
29
+ }
30
+
31
+ interface AdminUserSessionsCardProps {
32
+ userId: string
33
+ initialSessions: Session[]
34
+ }
35
+
36
+ const baseColumns: Column<Session>[] = [
37
+ {
38
+ name: 'createdAt',
39
+ title: 'Created',
40
+ renderer: (session) => <span className="text-sm">{formatDateTime(session.createdAt)}</span>,
41
+ },
42
+ {
43
+ name: 'expiresAt',
44
+ title: 'Expires',
45
+ renderer: (session) => <span className="text-sm">{formatDateTime(session.expiresAt)}</span>,
46
+ },
47
+ {
48
+ name: 'deviceLabel',
49
+ title: 'Device',
50
+ renderer: (session) => <span className="text-sm">{session.deviceLabel}</span>,
51
+ },
52
+ {
53
+ name: 'location',
54
+ title: 'Location',
55
+ renderer: (session) => <span className="text-sm">{session.location}</span>,
56
+ },
57
+ {
58
+ name: 'ipAddress',
59
+ title: 'IP Address',
60
+ renderer: (session) => <span className="text-sm font-mono">{session.ipAddress || 'N/A'}</span>,
61
+ },
62
+ ]
63
+
64
+ export default function AdminUserSessionsCard({ userId, initialSessions }: AdminUserSessionsCardProps) {
65
+ const router = useRouter()
66
+ const [sessions, setSessions] = useState(initialSessions)
67
+ const [sessionToRevoke, setSessionToRevoke] = useState<string | null>(null)
68
+ const [isRevoking, setIsRevoking] = useState(false)
69
+ const [confirmRevokeAll, setConfirmRevokeAll] = useState(false)
70
+
71
+ const now = new Date()
72
+ const activeSessions = sessions.filter(s => new Date(s.expiresAt) > now)
73
+ const expiredSessions = sessions.filter(s => new Date(s.expiresAt) <= now)
74
+
75
+ const handleRevokeAll = async () => {
76
+ setIsRevoking(true)
77
+ try {
78
+ const response = await fetch(`/api/admin/users/${userId}/sessions`, { method: 'DELETE' })
79
+ if (!response.ok) {
80
+ throw new Error('Failed to revoke sessions')
81
+ }
82
+ toast.success('All sessions revoked')
83
+ setSessions(sessions.filter(s => new Date(s.expiresAt) <= now))
84
+ setConfirmRevokeAll(false)
85
+ router.refresh()
86
+ } catch (error) {
87
+ toast.error('Failed to revoke sessions')
88
+ console.error(error)
89
+ } finally {
90
+ setIsRevoking(false)
91
+ }
92
+ }
93
+
94
+ const handleRevokeSession = async (sessionId: string) => {
95
+ setIsRevoking(true)
96
+ try {
97
+ const response = await fetch(
98
+ `/api/admin/users/${userId}/sessions?sessionId=${sessionId}`,
99
+ { method: 'DELETE' }
100
+ )
101
+
102
+ if (!response.ok) {
103
+ throw new Error('Failed to revoke session')
104
+ }
105
+
106
+ toast.success('Session revoked successfully')
107
+ setSessions(sessions.filter(s => s.id !== sessionId))
108
+ setSessionToRevoke(null)
109
+ router.refresh()
110
+ } catch (error) {
111
+ toast.error('Failed to revoke session')
112
+ console.error(error)
113
+ } finally {
114
+ setIsRevoking(false)
115
+ }
116
+ }
117
+
118
+ return (
119
+ <>
120
+ <Card>
121
+ <CardHeader>
122
+ <div className="flex items-start justify-between gap-4">
123
+ <div>
124
+ <CardTitle>Sessions</CardTitle>
125
+ <CardDescription>
126
+ {activeSessions.length} active session(s)
127
+ </CardDescription>
128
+ </div>
129
+ {activeSessions.length > 0 && (
130
+ <Button
131
+ variant="destructive"
132
+ size="sm"
133
+ onClick={() => setConfirmRevokeAll(true)}
134
+ >
135
+ Revoke all sessions
136
+ </Button>
137
+ )}
138
+ </div>
139
+ </CardHeader>
140
+ <CardContent>
141
+ {activeSessions.length === 0 ? (
142
+ <p className="text-sm text-gray-600 dark:text-gray-400 text-center py-8">
143
+ No active sessions found
144
+ </p>
145
+ ) : (
146
+ <DataTable
147
+ columns={[
148
+ ...baseColumns,
149
+ {
150
+ name: 'actions',
151
+ title: 'Actions',
152
+ renderer: (session) => (
153
+ <Button
154
+ variant="destructive"
155
+ size="sm"
156
+ onClick={() => setSessionToRevoke(session.id)}
157
+ >
158
+ Revoke
159
+ </Button>
160
+ ),
161
+ },
162
+ ]}
163
+ data={activeSessions}
164
+ getRowId={(session) => session.id}
165
+ />
166
+ )}
167
+
168
+ {expiredSessions.length > 0 && (
169
+ <p className="text-xs text-gray-500 dark:text-gray-400 mt-4">
170
+ {expiredSessions.length} expired session(s) not shown
171
+ </p>
172
+ )}
173
+ </CardContent>
174
+ </Card>
175
+
176
+ <AlertDialog
177
+ open={!!sessionToRevoke}
178
+ onOpenChange={() => setSessionToRevoke(null)}
179
+ >
180
+ <AlertDialogContent>
181
+ <AlertDialogHeader>
182
+ <AlertDialogTitle>Revoke Session?</AlertDialogTitle>
183
+ <AlertDialogDescription>
184
+ This will log out the device associated with this session.
185
+ This action cannot be undone.
186
+ </AlertDialogDescription>
187
+ </AlertDialogHeader>
188
+ <AlertDialogFooter>
189
+ <AlertDialogCancel disabled={isRevoking}>Cancel</AlertDialogCancel>
190
+ <AlertDialogAction
191
+ onClick={() => sessionToRevoke && handleRevokeSession(sessionToRevoke)}
192
+ disabled={isRevoking}
193
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
194
+ >
195
+ {isRevoking ? "Revoking..." : "Revoke Session"}
196
+ </AlertDialogAction>
197
+ </AlertDialogFooter>
198
+ </AlertDialogContent>
199
+ </AlertDialog>
200
+
201
+ <AlertDialog open={confirmRevokeAll} onOpenChange={setConfirmRevokeAll}>
202
+ <AlertDialogContent>
203
+ <AlertDialogHeader>
204
+ <AlertDialogTitle>Revoke all sessions?</AlertDialogTitle>
205
+ <AlertDialogDescription>
206
+ Logs this account out of every device — use this if the account may be
207
+ compromised. Cached sessions may stay valid for up to 5 minutes. The
208
+ user can sign back in normally afterwards.
209
+ </AlertDialogDescription>
210
+ </AlertDialogHeader>
211
+ <AlertDialogFooter>
212
+ <AlertDialogCancel disabled={isRevoking}>Cancel</AlertDialogCancel>
213
+ <AlertDialogAction
214
+ onClick={handleRevokeAll}
215
+ disabled={isRevoking}
216
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
217
+ >
218
+ {isRevoking ? "Revoking..." : "Revoke all"}
219
+ </AlertDialogAction>
220
+ </AlertDialogFooter>
221
+ </AlertDialogContent>
222
+ </AlertDialog>
223
+ </>
224
+ )
225
+ }
@@ -0,0 +1,57 @@
1
+ import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"
2
+ import { Badge } from "../../ui/badge"
3
+ import { Separator } from "../../ui/separator"
4
+ import { formatDateTime } from "../../../lib/formatters"
5
+
6
+ type User = {
7
+ banned: boolean
8
+ bannedAt: Date | null
9
+ bannedReason: string | null
10
+ lastLoginAt: Date | null
11
+ lastActiveAt: Date | null
12
+ }
13
+
14
+ export default function AdminUserStatusCard({ user }: { user: User }) {
15
+ return (
16
+ <Card>
17
+ <CardHeader>
18
+ <CardTitle>Account Status</CardTitle>
19
+ </CardHeader>
20
+ <CardContent className="space-y-4">
21
+ <div>
22
+ <p className="text-sm font-medium text-gray-600">Status</p>
23
+ <div className="mt-1">
24
+ {user.banned ? (
25
+ <Badge variant="destructive">Banned</Badge>
26
+ ) : (
27
+ <Badge variant="outline">Active</Badge>
28
+ )}
29
+ </div>
30
+ {user.banned && user.bannedReason && (
31
+ <p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
32
+ {user.bannedReason}
33
+ </p>
34
+ )}
35
+ </div>
36
+
37
+ <Separator />
38
+
39
+ <div>
40
+ <p className="text-sm font-medium text-gray-600">Last Login</p>
41
+ <p className="text-base">
42
+ {user.lastLoginAt ? formatDateTime(user.lastLoginAt) : 'Never'}
43
+ </p>
44
+ </div>
45
+
46
+ <Separator />
47
+
48
+ <div>
49
+ <p className="text-sm font-medium text-gray-600">Last Active</p>
50
+ <p className="text-base">
51
+ {user.lastActiveAt ? formatDateTime(user.lastActiveAt) : 'Never'}
52
+ </p>
53
+ </div>
54
+ </CardContent>
55
+ </Card>
56
+ )
57
+ }
@@ -0,0 +1,30 @@
1
+ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "../../ui/card"
2
+ import { Skeleton } from "../../ui/skeleton"
3
+
4
+ export default function AdminUsersSkeleton() {
5
+ return (
6
+ <>
7
+ <Card>
8
+ <CardHeader>
9
+ <CardTitle>Search Users</CardTitle>
10
+ <CardDescription>Search by name or email</CardDescription>
11
+ </CardHeader>
12
+ <CardContent>
13
+ <Skeleton className="h-9 max-w-sm w-full" />
14
+ </CardContent>
15
+ </Card>
16
+
17
+ <Card>
18
+ <CardHeader>
19
+ <CardTitle>Users List</CardTitle>
20
+ <CardDescription>Loading...</CardDescription>
21
+ </CardHeader>
22
+ <CardContent className="space-y-4">
23
+ {[...Array(5)].map((_, i) => (
24
+ <Skeleton key={i} className="h-16 w-full" />
25
+ ))}
26
+ </CardContent>
27
+ </Card>
28
+ </>
29
+ )
30
+ }