@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,100 @@
1
+ 'use client'
2
+
3
+ import React, { useState } from 'react'
4
+ import { useRouter } from 'next/navigation'
5
+ import { Button } from '../ui/button'
6
+ import { Input } from '../ui/input'
7
+ import { Label } from '../ui/label'
8
+ import { Checkbox } from '../ui/checkbox'
9
+ import { authClient } from '../../lib/auth-client'
10
+ import { toast } from 'sonner'
11
+
12
+ type Props = {
13
+ callbackUrl: string
14
+ }
15
+
16
+ // Second-factor challenge, shown after a correct password when the account
17
+ // has TOTP enabled. `signIn.email` returns `twoFactorRedirect: true` instead
18
+ // of a session and sets a short-lived cookie; verifying here finalizes it.
19
+ const TwoFactorVerifyForm = ({ callbackUrl }: Props) => {
20
+ const router = useRouter()
21
+ const [mode, setMode] = useState<'totp' | 'backup'>('totp')
22
+ const [code, setCode] = useState('')
23
+ const [trustDevice, setTrustDevice] = useState(false)
24
+ const [isSubmitting, setIsSubmitting] = useState(false)
25
+
26
+ const handleSubmit = async (e: React.FormEvent) => {
27
+ e.preventDefault()
28
+ const trimmed = code.trim()
29
+ if (!trimmed) return
30
+
31
+ setIsSubmitting(true)
32
+ try {
33
+ const { error } =
34
+ mode === 'totp'
35
+ ? await authClient.twoFactor.verifyTotp({ code: trimmed, trustDevice })
36
+ : await authClient.twoFactor.verifyBackupCode({ code: trimmed, trustDevice })
37
+
38
+ if (error) {
39
+ toast.error(error.message || 'Invalid code')
40
+ return
41
+ }
42
+ router.push(callbackUrl)
43
+ router.refresh()
44
+ } catch (err) {
45
+ toast.error(err instanceof Error ? err.message : 'Failed to verify code')
46
+ } finally {
47
+ setIsSubmitting(false)
48
+ }
49
+ }
50
+
51
+ return (
52
+ <div>
53
+ <h1 className="text-3xl font-bold tracking-tight">Two-factor authentication</h1>
54
+ <p className="mt-2 text-sm text-muted-foreground">
55
+ {mode === 'totp'
56
+ ? 'Enter the 6-digit code from your authenticator app.'
57
+ : 'Enter one of your backup codes.'}
58
+ </p>
59
+
60
+ <form onSubmit={handleSubmit} className="mt-8 flex flex-col gap-4">
61
+ <div className="flex flex-col gap-2">
62
+ <Label htmlFor="code">{mode === 'totp' ? 'Authenticator code' : 'Backup code'}</Label>
63
+ <Input
64
+ id="code"
65
+ inputMode={mode === 'totp' ? 'numeric' : 'text'}
66
+ autoComplete="one-time-code"
67
+ autoFocus
68
+ value={code}
69
+ onChange={(e) => setCode(e.target.value)}
70
+ />
71
+ </div>
72
+
73
+ <label className="flex items-center gap-2 text-sm text-muted-foreground">
74
+ <Checkbox
75
+ checked={trustDevice}
76
+ onCheckedChange={(v) => setTrustDevice(v === true)}
77
+ />
78
+ Trust this device for 30 days
79
+ </label>
80
+
81
+ <Button type="submit" className="w-full" disabled={isSubmitting}>
82
+ {isSubmitting ? 'Verifying...' : 'Verify'}
83
+ </Button>
84
+ </form>
85
+
86
+ <button
87
+ type="button"
88
+ onClick={() => {
89
+ setMode((m) => (m === 'totp' ? 'backup' : 'totp'))
90
+ setCode('')
91
+ }}
92
+ className="mt-6 text-sm text-muted-foreground underline underline-offset-4 hover:text-foreground"
93
+ >
94
+ {mode === 'totp' ? 'Use a backup code instead' : 'Use your authenticator app instead'}
95
+ </button>
96
+ </div>
97
+ )
98
+ }
99
+
100
+ export default TwoFactorVerifyForm
@@ -0,0 +1,58 @@
1
+ import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"
2
+ import { Badge } from "../ui/badge"
3
+ import { Separator } from "../ui/separator"
4
+ import { formatDate } from "../../lib/formatters"
5
+ import VerifyEmailButton from "./verifyEmailButton"
6
+
7
+ type User = {
8
+ id: string
9
+ name: string
10
+ email: string
11
+ emailVerified: boolean
12
+ role: string | null | undefined
13
+ createdAt: Date
14
+ updatedAt: Date
15
+ }
16
+
17
+ export default function AccountDetailsCard({ user }: { user: User }) {
18
+ return (
19
+ <Card>
20
+ <CardHeader>
21
+ <CardTitle>Account Details</CardTitle>
22
+ </CardHeader>
23
+ <CardContent className="space-y-4">
24
+ <div>
25
+ <p className="text-sm font-medium text-gray-600">Role</p>
26
+ <p className="text-base capitalize">{user.role || 'user'}</p>
27
+ </div>
28
+
29
+ <Separator />
30
+
31
+ <div>
32
+ <p className="text-sm font-medium text-gray-600">Email Status</p>
33
+ <div className="flex items-center gap-2 mt-1 flex-wrap">
34
+ <p className="text-base">{user.email}</p>
35
+ <Badge variant={user.emailVerified ? 'default' : 'destructive'}>
36
+ {user.emailVerified ? 'Verified' : 'Unverified'}
37
+ </Badge>
38
+ {!user.emailVerified && <VerifyEmailButton email={user.email} />}
39
+ </div>
40
+ </div>
41
+
42
+ <Separator />
43
+
44
+ <div>
45
+ <p className="text-sm font-medium text-gray-600">Member Since</p>
46
+ <p className="text-base">{formatDate(user.createdAt)}</p>
47
+ </div>
48
+
49
+ <Separator />
50
+
51
+ <div>
52
+ <p className="text-sm font-medium text-gray-600">Last Updated</p>
53
+ <p className="text-base">{formatDate(user.updatedAt)}</p>
54
+ </div>
55
+ </CardContent>
56
+ </Card>
57
+ )
58
+ }
@@ -0,0 +1,141 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useState } from 'react'
4
+ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '../ui/card'
5
+ import { Badge } from '../ui/badge'
6
+ import { Skeleton } from '../ui/skeleton'
7
+ import { DataTable, type Column } from '../ui/data-table'
8
+ import { formatDateTime, actionBadgeVariant } from '../../lib/formatters'
9
+
10
+ interface ActivityLog {
11
+ id: string
12
+ action: string
13
+ description: string
14
+ ipAddress: string | null
15
+ userAgent: string | null
16
+ deviceLabel?: string
17
+ location?: string
18
+ createdAt: Date | string
19
+ userId: string
20
+ userName?: string | null
21
+ userEmail?: string | null
22
+ }
23
+
24
+ interface ActivityLogsCardProps {
25
+ isAdmin?: boolean
26
+ initialLogs?: ActivityLog[]
27
+ }
28
+
29
+ const baseColumns: Column<ActivityLog>[] = [
30
+ {
31
+ name: 'action',
32
+ title: 'Action',
33
+ sticky: true,
34
+ renderer: (log) => (
35
+ <Badge variant={actionBadgeVariant[log.action] || 'outline'}>
36
+ {log.action.replace(/_/g, ' ')}
37
+ </Badge>
38
+ ),
39
+ },
40
+ {
41
+ name: 'description',
42
+ title: 'Description',
43
+ renderer: (log) => <span className="text-sm max-w-xs truncate block">{log.description}</span>,
44
+ },
45
+ {
46
+ name: 'deviceLabel',
47
+ title: 'Device',
48
+ renderer: (log) => <span className="text-sm">{log.deviceLabel || 'N/A'}</span>,
49
+ },
50
+ {
51
+ name: 'location',
52
+ title: 'Location',
53
+ renderer: (log) => <span className="text-sm">{log.location || 'N/A'}</span>,
54
+ },
55
+ {
56
+ name: 'ipAddress',
57
+ title: 'IP Address',
58
+ renderer: (log) => <span className="text-sm font-mono">{log.ipAddress || 'N/A'}</span>,
59
+ },
60
+ {
61
+ name: 'createdAt',
62
+ title: 'Time',
63
+ renderer: (log) => (
64
+ <span className="text-sm text-gray-600 dark:text-gray-400">
65
+ {formatDateTime(log.createdAt)}
66
+ </span>
67
+ ),
68
+ },
69
+ ]
70
+
71
+ const userColumn: Column<ActivityLog> = {
72
+ name: 'user',
73
+ title: 'User',
74
+ renderer: (log) => (
75
+ <div className="text-sm">
76
+ <div className="font-medium">{log.userName || 'N/A'}</div>
77
+ <div className="text-xs text-gray-500">{log.userEmail}</div>
78
+ </div>
79
+ ),
80
+ }
81
+
82
+ export default function ActivityLogsCard({ isAdmin = false, initialLogs = [] }: ActivityLogsCardProps) {
83
+ const [logs, setLogs] = useState<ActivityLog[]>(initialLogs)
84
+ const [loading, setLoading] = useState(false)
85
+
86
+ useEffect(() => {
87
+ const fetchLogs = async () => {
88
+ try {
89
+ const params = new URLSearchParams({
90
+ limit: '20',
91
+ offset: '0',
92
+ })
93
+ const response = await fetch(`/api/activity-logs?${params}`)
94
+ if (!response.ok) throw new Error('Failed to fetch logs')
95
+ const data = await response.json()
96
+ setLogs(data.logs)
97
+ } catch (error) {
98
+ console.error('Failed to fetch activity logs:', error)
99
+ } finally {
100
+ setLoading(false)
101
+ }
102
+ }
103
+
104
+ fetchLogs()
105
+ // Refresh logs every 30 seconds
106
+ const interval = setInterval(fetchLogs, 30000)
107
+ return () => clearInterval(interval)
108
+ }, [])
109
+
110
+ return (
111
+ <Card>
112
+ <CardHeader>
113
+ <CardTitle>Activity Logs</CardTitle>
114
+ <CardDescription>
115
+ {isAdmin ? 'Recent activity from all users' : 'Your recent activity'}
116
+ </CardDescription>
117
+ </CardHeader>
118
+ <CardContent>
119
+ {loading ? (
120
+ <div className="space-y-4">
121
+ {[...Array(5)].map((_, i) => (
122
+ <Skeleton key={i} className="h-16 w-full" />
123
+ ))}
124
+ </div>
125
+ ) : logs.length === 0 ? (
126
+ <p className="text-center text-gray-500 py-8">No activity logs found</p>
127
+ ) : (
128
+ <DataTable
129
+ columns={
130
+ isAdmin
131
+ ? [baseColumns[0], userColumn, ...baseColumns.slice(1)]
132
+ : baseColumns
133
+ }
134
+ data={logs}
135
+ getRowId={(log) => log.id}
136
+ />
137
+ )}
138
+ </CardContent>
139
+ </Card>
140
+ )
141
+ }
@@ -0,0 +1,20 @@
1
+ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "../ui/card"
2
+ import { Skeleton } from "../ui/skeleton"
3
+
4
+ export default function ActivityLogsCardSkeleton() {
5
+ return (
6
+ <Card>
7
+ <CardHeader>
8
+ <CardTitle>Activity Logs</CardTitle>
9
+ <CardDescription>Loading...</CardDescription>
10
+ </CardHeader>
11
+ <CardContent>
12
+ <div className="space-y-4">
13
+ {[...Array(5)].map((_, i) => (
14
+ <Skeleton key={i} className="h-16 w-full" />
15
+ ))}
16
+ </div>
17
+ </CardContent>
18
+ </Card>
19
+ )
20
+ }
@@ -0,0 +1,16 @@
1
+ import { getUserActivityLogs, getAllActivityLogs } from "@xk2800/nextjs-template/activity/queries"
2
+ import ActivityLogsCard from "./activityLogsCard"
3
+
4
+ export default async function ActivityLogsSection({
5
+ userId,
6
+ isAdmin,
7
+ }: {
8
+ userId: string
9
+ isAdmin: boolean
10
+ }) {
11
+ const logs = isAdmin
12
+ ? await getAllActivityLogs(20, 0)
13
+ : await getUserActivityLogs(userId, 20)
14
+
15
+ return <ActivityLogsCard initialLogs={logs} isAdmin={isAdmin} />
16
+ }
@@ -0,0 +1,13 @@
1
+ import { getActivityLogsFiltered } from "@xk2800/nextjs-template/activity/queries"
2
+ import AdminActivityLogsTable from "./adminActivityLogsTable"
3
+
4
+ export default async function ActivityLogsSection() {
5
+ const { logs, total, page, limit, pages } = await getActivityLogsFiltered({ page: 1, limit: 20 })
6
+
7
+ return (
8
+ <AdminActivityLogsTable
9
+ initialLogs={logs}
10
+ initialPagination={{ page, limit, total, pages }}
11
+ />
12
+ )
13
+ }
@@ -0,0 +1,34 @@
1
+ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "../../ui/card"
2
+ import { Skeleton } from "../../ui/skeleton"
3
+
4
+ export default function AdminActivityLogsSkeleton() {
5
+ return (
6
+ <>
7
+ <Card>
8
+ <CardHeader>
9
+ <CardTitle>Filters</CardTitle>
10
+ <CardDescription>Search and narrow down activity events</CardDescription>
11
+ </CardHeader>
12
+ <CardContent>
13
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4">
14
+ {[...Array(5)].map((_, i) => (
15
+ <Skeleton key={i} className="h-9 w-full" />
16
+ ))}
17
+ </div>
18
+ </CardContent>
19
+ </Card>
20
+
21
+ <Card>
22
+ <CardHeader>
23
+ <CardTitle>Events</CardTitle>
24
+ <CardDescription>Loading...</CardDescription>
25
+ </CardHeader>
26
+ <CardContent className="space-y-4">
27
+ {[...Array(5)].map((_, i) => (
28
+ <Skeleton key={i} className="h-16 w-full" />
29
+ ))}
30
+ </CardContent>
31
+ </Card>
32
+ </>
33
+ )
34
+ }