betterstart-cli 0.0.22 → 0.0.23

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 (92) hide show
  1. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-branding-skeleton.tsx +1 -1
  2. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-user-menu-skeleton.tsx +1 -1
  3. package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-user-menu.tsx +1 -1
  4. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/lifecycle-hooks-tab.tsx +2 -2
  5. package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/snippets-tab.tsx +1 -1
  6. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-version-item.tsx +1 -1
  7. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-current-row.tsx +1 -1
  8. package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-drawer.tsx +3 -3
  9. package/dist/assets/adapters/next/templates/init/components/shared/media/edit-media-dialog-content.tsx +2 -2
  10. package/dist/assets/adapters/next/templates/init/components/shared/media/media-delete-drawer.tsx +3 -3
  11. package/dist/assets/adapters/next/templates/init/components/shared/media/media-gallery-dialog.tsx +62 -64
  12. package/dist/assets/adapters/next/templates/init/components/shared/media/media-grid-item.tsx +1 -1
  13. package/dist/assets/adapters/next/templates/init/components/shared/media/media-url-importer.tsx +1 -1
  14. package/dist/assets/adapters/next/templates/init/data/navigation.ts +1 -3
  15. package/dist/assets/adapters/next/templates/init/lib/actions/auth/auth.ts +8 -0
  16. package/dist/assets/adapters/next/templates/init/lib/actions/profile/change-own-password.ts +59 -0
  17. package/dist/assets/adapters/next/templates/init/lib/actions/profile/index.ts +6 -1
  18. package/dist/assets/adapters/next/templates/init/lib/actions/profile/types.ts +10 -0
  19. package/dist/assets/adapters/next/templates/init/lib/actions/users/change-user-password.ts +77 -0
  20. package/dist/assets/adapters/next/templates/init/lib/actions/users/index.ts +3 -0
  21. package/dist/assets/adapters/next/templates/init/lib/actions/users/types.ts +10 -0
  22. package/dist/assets/adapters/next/templates/init/lib/db/core/schema.ts +4 -0
  23. package/dist/assets/adapters/next/templates/init/pages/dashboard-page.tsx +1 -1
  24. package/dist/assets/adapters/next/templates/init/pages/forgot-password-page-skeleton.tsx +1 -1
  25. package/dist/assets/adapters/next/templates/init/pages/login-page-skeleton.tsx +1 -1
  26. package/dist/assets/adapters/next/templates/init/pages/media/media-page-content.tsx +13 -18
  27. package/dist/assets/adapters/next/templates/init/pages/profile/profile-form.tsx +133 -1
  28. package/dist/assets/adapters/next/templates/init/pages/reset-password-page-skeleton.tsx +1 -1
  29. package/dist/assets/adapters/next/templates/init/pages/users/change-password-dialog.tsx +229 -0
  30. package/dist/assets/adapters/next/templates/init/pages/users/columns.tsx +22 -14
  31. package/dist/assets/adapters/next/templates/init/pages/users/create-user-dialog.tsx +99 -93
  32. package/dist/assets/adapters/next/templates/init/pages/users/delete-user-dialog.tsx +17 -25
  33. package/dist/assets/adapters/next/templates/init/pages/users/users-table.tsx +5 -4
  34. package/dist/assets/shared-assets/react-admin/custom/content-editor/math-bubble-menu.tsx +1 -1
  35. package/dist/assets/shared-assets/react-admin/custom/content-editor/remove-table-part-icon.tsx +1 -1
  36. package/dist/assets/shared-assets/react-admin/custom/content-editor/selection-bubble-menu.tsx +2 -2
  37. package/dist/assets/shared-assets/react-admin/custom/content-editor/table-bubble-menu.tsx +1 -1
  38. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-button/color-highlight-button.tsx +2 -2
  39. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover-content.tsx +1 -1
  40. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/color-highlight-popover.tsx +1 -1
  41. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/highlight-color-button.tsx +2 -2
  42. package/dist/assets/shared-assets/react-admin/custom/content-editor/tiptap-ui/color-highlight-popover/source-color-highlight-popover.tsx +1 -1
  43. package/dist/assets/shared-assets/react-admin/custom/date-range-picker.tsx +1 -1
  44. package/dist/assets/shared-assets/react-admin/custom/media-field-empty-state.tsx +19 -12
  45. package/dist/assets/shared-assets/react-admin/custom/media-field.tsx +2 -2
  46. package/dist/assets/shared-assets/react-admin/custom/nested-object-list-field.tsx +1 -1
  47. package/dist/assets/shared-assets/react-admin/custom/placeholder-card.tsx +1 -1
  48. package/dist/assets/shared-assets/react-admin/custom/placeholder.tsx +1 -1
  49. package/dist/assets/shared-assets/react-admin/ui/accordion.tsx +1 -1
  50. package/dist/assets/shared-assets/react-admin/ui/alert-dialog.tsx +15 -4
  51. package/dist/assets/shared-assets/react-admin/ui/alert.tsx +1 -1
  52. package/dist/assets/shared-assets/react-admin/ui/avatar.tsx +5 -5
  53. package/dist/assets/shared-assets/react-admin/ui/badge.tsx +1 -1
  54. package/dist/assets/shared-assets/react-admin/ui/button-group.tsx +3 -3
  55. package/dist/assets/shared-assets/react-admin/ui/button.tsx +6 -6
  56. package/dist/assets/shared-assets/react-admin/ui/card.tsx +4 -4
  57. package/dist/assets/shared-assets/react-admin/ui/carousel.tsx +2 -2
  58. package/dist/assets/shared-assets/react-admin/ui/chart.tsx +1 -1
  59. package/dist/assets/shared-assets/react-admin/ui/combobox.tsx +4 -4
  60. package/dist/assets/shared-assets/react-admin/ui/command.tsx +3 -3
  61. package/dist/assets/shared-assets/react-admin/ui/context-menu.tsx +6 -6
  62. package/dist/assets/shared-assets/react-admin/ui/dialog.tsx +29 -6
  63. package/dist/assets/shared-assets/react-admin/ui/drawer.tsx +2 -2
  64. package/dist/assets/shared-assets/react-admin/ui/dropdown-menu.tsx +6 -6
  65. package/dist/assets/shared-assets/react-admin/ui/empty.tsx +2 -2
  66. package/dist/assets/shared-assets/react-admin/ui/field.tsx +1 -1
  67. package/dist/assets/shared-assets/react-admin/ui/hover-card.tsx +1 -1
  68. package/dist/assets/shared-assets/react-admin/ui/input-group.tsx +5 -6
  69. package/dist/assets/shared-assets/react-admin/ui/input-otp.tsx +2 -2
  70. package/dist/assets/shared-assets/react-admin/ui/input.tsx +1 -1
  71. package/dist/assets/shared-assets/react-admin/ui/item.tsx +2 -2
  72. package/dist/assets/shared-assets/react-admin/ui/menubar.tsx +8 -8
  73. package/dist/assets/shared-assets/react-admin/ui/native-select.tsx +1 -1
  74. package/dist/assets/shared-assets/react-admin/ui/navigation-menu.tsx +4 -4
  75. package/dist/assets/shared-assets/react-admin/ui/popover.tsx +1 -1
  76. package/dist/assets/shared-assets/react-admin/ui/progress.tsx +1 -1
  77. package/dist/assets/shared-assets/react-admin/ui/radio-group.tsx +2 -2
  78. package/dist/assets/shared-assets/react-admin/ui/resizable.tsx +1 -1
  79. package/dist/assets/shared-assets/react-admin/ui/scroll-area.tsx +1 -1
  80. package/dist/assets/shared-assets/react-admin/ui/select.tsx +3 -3
  81. package/dist/assets/shared-assets/react-admin/ui/sidebar.tsx +10 -10
  82. package/dist/assets/shared-assets/react-admin/ui/skeleton.tsx +1 -1
  83. package/dist/assets/shared-assets/react-admin/ui/slider.tsx +2 -2
  84. package/dist/assets/shared-assets/react-admin/ui/switch.tsx +2 -2
  85. package/dist/assets/shared-assets/react-admin/ui/tabs.tsx +3 -3
  86. package/dist/assets/shared-assets/react-admin/ui/textarea.tsx +1 -1
  87. package/dist/assets/shared-assets/react-admin/ui/toggle-group.tsx +2 -2
  88. package/dist/assets/shared-assets/react-admin/ui/toggle.tsx +1 -1
  89. package/dist/assets/shared-assets/react-admin/ui/tooltip.tsx +1 -1
  90. package/dist/cli.js +5372 -4243
  91. package/dist/cli.js.map +1 -1
  92. package/package.json +1 -1
@@ -1,6 +1,11 @@
1
1
  'use client'
2
2
 
3
- import { invalidateUsersCache, isEmailConfigured, updateEmail } from '@admin/actions/profile'
3
+ import {
4
+ changeOwnPassword,
5
+ invalidateUsersCache,
6
+ isEmailConfigured,
7
+ updateEmail
8
+ } from '@admin/actions/profile'
4
9
  import { authClient } from '@admin/auth/client'
5
10
  import { MediaField } from '@admin/components/custom/media-field'
6
11
  import { Avatar, AvatarFallback, AvatarImage } from '@admin/components/ui/avatar'
@@ -16,6 +21,7 @@ import {
16
21
  import {
17
22
  Form,
18
23
  FormControl,
24
+ FormDescription,
19
25
  FormField,
20
26
  FormItem,
21
27
  FormLabel,
@@ -40,7 +46,19 @@ const profileSchema = z.object({
40
46
  currentPasswordForEmail: z.string().optional()
41
47
  })
42
48
 
49
+ const passwordSchema = z
50
+ .object({
51
+ currentPassword: z.string().min(1, 'Current password is required'),
52
+ newPassword: z.string().min(8, 'Password must be at least 8 characters'),
53
+ confirmPassword: z.string().min(1, 'Confirm the new password')
54
+ })
55
+ .refine((values) => values.newPassword === values.confirmPassword, {
56
+ message: 'Passwords do not match',
57
+ path: ['confirmPassword']
58
+ })
59
+
43
60
  type ProfileValues = z.infer<typeof profileSchema>
61
+ type PasswordValues = z.infer<typeof passwordSchema>
44
62
 
45
63
  interface ProfileFormProps {
46
64
  user: {
@@ -53,6 +71,7 @@ interface ProfileFormProps {
53
71
  export function ProfileForm({ user }: ProfileFormProps) {
54
72
  const router = useRouter()
55
73
  const [profilePending, startProfileTransition] = React.useTransition()
74
+ const [passwordPending, startPasswordTransition] = React.useTransition()
56
75
  const [resetPending, startResetTransition] = React.useTransition()
57
76
 
58
77
  const profileForm = useForm<ProfileValues>({
@@ -64,6 +83,16 @@ export function ProfileForm({ user }: ProfileFormProps) {
64
83
  currentPasswordForEmail: ''
65
84
  }
66
85
  })
86
+ const passwordForm = useForm<PasswordValues>({
87
+ resolver: standardSchemaResolver(passwordSchema),
88
+ defaultValues: {
89
+ currentPassword: '',
90
+ newPassword: '',
91
+ confirmPassword: ''
92
+ },
93
+ mode: 'onBlur',
94
+ reValidateMode: 'onChange'
95
+ })
67
96
 
68
97
  const emailDirty = profileForm.watch('email') !== user.email
69
98
  const imageValue = profileForm.watch('image')
@@ -115,6 +144,31 @@ export function ProfileForm({ user }: ProfileFormProps) {
115
144
  })
116
145
  }
117
146
 
147
+ function onPasswordSubmit(values: PasswordValues) {
148
+ startPasswordTransition(async () => {
149
+ try {
150
+ const result = await changeOwnPassword({
151
+ currentPassword: values.currentPassword,
152
+ newPassword: values.newPassword
153
+ })
154
+
155
+ if (!result.success) {
156
+ toast.error(result.error || 'Failed to change password')
157
+ return
158
+ }
159
+
160
+ toast.success('Password changed successfully')
161
+ passwordForm.reset({
162
+ currentPassword: '',
163
+ newPassword: '',
164
+ confirmPassword: ''
165
+ })
166
+ } catch {
167
+ toast.error('An error occurred while changing your password')
168
+ }
169
+ })
170
+ }
171
+
118
172
  function handleResetPassword() {
119
173
  startResetTransition(async () => {
120
174
  try {
@@ -253,6 +307,84 @@ export function ProfileForm({ user }: ProfileFormProps) {
253
307
  </form>
254
308
  </Form>
255
309
 
310
+ <Form {...passwordForm}>
311
+ <form onSubmit={passwordForm.handleSubmit(onPasswordSubmit)}>
312
+ <Card>
313
+ <CardHeader>
314
+ <CardTitle>Change Password</CardTitle>
315
+ <CardDescription>Update the password for your account</CardDescription>
316
+ </CardHeader>
317
+ <CardContent className="space-y-5">
318
+ <FormField
319
+ control={passwordForm.control}
320
+ name="currentPassword"
321
+ render={({ field }) => (
322
+ <FormItem>
323
+ <FormLabel>Current Password</FormLabel>
324
+ <FormControl>
325
+ <Input
326
+ type="password"
327
+ autoComplete="current-password"
328
+ disabled={passwordPending}
329
+ {...field}
330
+ />
331
+ </FormControl>
332
+ <FormMessage />
333
+ </FormItem>
334
+ )}
335
+ />
336
+ <FormField
337
+ control={passwordForm.control}
338
+ name="newPassword"
339
+ render={({ field }) => (
340
+ <FormItem>
341
+ <FormLabel>New Password</FormLabel>
342
+ <FormControl>
343
+ <Input
344
+ type="password"
345
+ autoComplete="new-password"
346
+ disabled={passwordPending}
347
+ {...field}
348
+ />
349
+ </FormControl>
350
+ <FormDescription>Use at least 8 characters.</FormDescription>
351
+ <FormMessage />
352
+ </FormItem>
353
+ )}
354
+ />
355
+ <FormField
356
+ control={passwordForm.control}
357
+ name="confirmPassword"
358
+ render={({ field }) => (
359
+ <FormItem>
360
+ <FormLabel>Confirm Password</FormLabel>
361
+ <FormControl>
362
+ <Input
363
+ type="password"
364
+ autoComplete="new-password"
365
+ disabled={passwordPending}
366
+ {...field}
367
+ />
368
+ </FormControl>
369
+ <FormMessage />
370
+ </FormItem>
371
+ )}
372
+ />
373
+ </CardContent>
374
+ <CardFooter>
375
+ <Button
376
+ type="submit"
377
+ disabled={passwordPending || !passwordForm.formState.isDirty}
378
+ className="ml-auto min-w-25"
379
+ >
380
+ {passwordPending && <LoaderCircle className="animate-spin" />}
381
+ {passwordPending ? 'Changing...' : 'Change Password'}
382
+ </Button>
383
+ </CardFooter>
384
+ </Card>
385
+ </form>
386
+ </Form>
387
+
256
388
  <Card>
257
389
  <CardHeader>
258
390
  <CardTitle>Reset Password</CardTitle>
@@ -1,6 +1,6 @@
1
1
  export function ResetPasswordPageSkeleton() {
2
2
  return (
3
- <div className="overflow-hidden rounded-lg border bg-card">
3
+ <div className="overflow-hidden rounded-sm border bg-card">
4
4
  <div className="grid md:grid-cols-[5fr_7fr]">
5
5
  <div className="hidden min-h-[520px] bg-muted md:block" />
6
6
  <div className="flex min-h-[520px] flex-col gap-10 p-6 md:p-20">
@@ -0,0 +1,229 @@
1
+ 'use client'
2
+
3
+ import { changeOwnPassword } from '@admin/actions/profile'
4
+ import { changeUserPassword } from '@admin/actions/users'
5
+ import { Button } from '@admin/components/ui/button'
6
+ import {
7
+ Dialog,
8
+ DialogBody,
9
+ DialogContent,
10
+ DialogDescription,
11
+ DialogFooter,
12
+ DialogHeader,
13
+ DialogTitle,
14
+ DialogTrigger
15
+ } from '@admin/components/ui/dialog'
16
+ import { DropdownMenuItem } from '@admin/components/ui/dropdown-menu'
17
+ import {
18
+ Form,
19
+ FormControl,
20
+ FormDescription,
21
+ FormField,
22
+ FormItem,
23
+ FormLabel,
24
+ FormMessage
25
+ } from '@admin/components/ui/form'
26
+ import { Input } from '@admin/components/ui/input'
27
+ import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
28
+ import { useMutation, useQueryClient } from '@tanstack/react-query'
29
+ import { KeySquare, LoaderCircle } from 'lucide-react'
30
+ import * as React from 'react'
31
+ import { useForm } from 'react-hook-form'
32
+ import { toast } from 'sonner'
33
+ import { z } from 'zod/v3'
34
+
35
+ const passwordFieldsSchema = z.object({
36
+ newPassword: z.string().min(8, 'Password must be at least 8 characters'),
37
+ confirmPassword: z.string().min(1, 'Confirm the new password')
38
+ })
39
+
40
+ const ownPasswordSchema = passwordFieldsSchema
41
+ .extend({
42
+ currentPassword: z.string().min(1, 'Current password is required')
43
+ })
44
+ .refine((values) => values.newPassword === values.confirmPassword, {
45
+ message: 'Passwords do not match',
46
+ path: ['confirmPassword']
47
+ })
48
+
49
+ const userPasswordSchema = passwordFieldsSchema
50
+ .extend({
51
+ currentPassword: z.string().optional()
52
+ })
53
+ .refine((values) => values.newPassword === values.confirmPassword, {
54
+ message: 'Passwords do not match',
55
+ path: ['confirmPassword']
56
+ })
57
+
58
+ type ChangePasswordFormValues = z.infer<typeof userPasswordSchema>
59
+
60
+ const defaultValues: ChangePasswordFormValues = {
61
+ currentPassword: '',
62
+ newPassword: '',
63
+ confirmPassword: ''
64
+ }
65
+
66
+ interface ChangePasswordDialogProps {
67
+ userId: string
68
+ userName: string
69
+ userEmail: string
70
+ isCurrentUser: boolean
71
+ }
72
+
73
+ export function ChangePasswordDialog({
74
+ userId,
75
+ userName,
76
+ userEmail,
77
+ isCurrentUser
78
+ }: ChangePasswordDialogProps) {
79
+ const [open, setOpen] = React.useState(false)
80
+ const queryClient = useQueryClient()
81
+ const form = useForm<ChangePasswordFormValues>({
82
+ resolver: standardSchemaResolver(isCurrentUser ? ownPasswordSchema : userPasswordSchema),
83
+ defaultValues,
84
+ mode: 'onBlur',
85
+ reValidateMode: 'onChange'
86
+ })
87
+
88
+ const mutation = useMutation({
89
+ mutationFn: async (values: ChangePasswordFormValues) => {
90
+ const result = isCurrentUser
91
+ ? await changeOwnPassword({
92
+ currentPassword: values.currentPassword ?? '',
93
+ newPassword: values.newPassword
94
+ })
95
+ : await changeUserPassword({
96
+ userId,
97
+ newPassword: values.newPassword
98
+ })
99
+
100
+ if (!result.success) {
101
+ throw new Error(result.error || 'Failed to change password')
102
+ }
103
+
104
+ return result
105
+ },
106
+ onSuccess: async () => {
107
+ toast.success(
108
+ isCurrentUser ? 'Password changed successfully' : `Password changed for ${userName}`
109
+ )
110
+ await queryClient.invalidateQueries({ queryKey: ['users'] })
111
+ resetFormState()
112
+ setOpen(false)
113
+ },
114
+ onError: (error: Error) => {
115
+ toast.error(error.message || 'An unexpected error occurred')
116
+ }
117
+ })
118
+
119
+ const isPending = mutation.isPending
120
+
121
+ function resetFormState() {
122
+ form.reset(defaultValues)
123
+ mutation.reset()
124
+ }
125
+
126
+ function handleOpenChange(nextOpen: boolean) {
127
+ setOpen(nextOpen)
128
+
129
+ if (!nextOpen) {
130
+ resetFormState()
131
+ }
132
+ }
133
+
134
+ return (
135
+ <Dialog open={open} onOpenChange={handleOpenChange}>
136
+ <DialogTrigger asChild>
137
+ <DropdownMenuItem onSelect={(event) => event.preventDefault()}>
138
+ <KeySquare className="size-4" />
139
+ Change password
140
+ </DropdownMenuItem>
141
+ </DialogTrigger>
142
+ <DialogContent className="sm:max-w-135">
143
+ <Form {...form}>
144
+ <form onSubmit={form.handleSubmit((values) => mutation.mutate(values))}>
145
+ <DialogHeader>
146
+ <DialogTitle>Change Password</DialogTitle>
147
+ <DialogDescription>
148
+ {isCurrentUser
149
+ ? 'Set a new password for your account.'
150
+ : `Set a new password for ${userName || userEmail}.`}
151
+ </DialogDescription>
152
+ </DialogHeader>
153
+
154
+ <DialogBody>
155
+ <div className="space-y-4">
156
+ {isCurrentUser ? (
157
+ <FormField
158
+ control={form.control}
159
+ name="currentPassword"
160
+ render={({ field }) => (
161
+ <FormItem>
162
+ <FormLabel>Current Password</FormLabel>
163
+ <FormControl>
164
+ <Input
165
+ type="password"
166
+ autoComplete="current-password"
167
+ disabled={isPending}
168
+ autoFocus
169
+ {...field}
170
+ />
171
+ </FormControl>
172
+ <FormMessage />
173
+ </FormItem>
174
+ )}
175
+ />
176
+ ) : null}
177
+ <FormField
178
+ control={form.control}
179
+ name="newPassword"
180
+ render={({ field }) => (
181
+ <FormItem>
182
+ <FormLabel>New Password</FormLabel>
183
+ <FormControl>
184
+ <Input
185
+ type="password"
186
+ autoComplete="new-password"
187
+ disabled={isPending}
188
+ autoFocus={!isCurrentUser}
189
+ {...field}
190
+ />
191
+ </FormControl>
192
+ <FormDescription>Use at least 8 characters.</FormDescription>
193
+ <FormMessage />
194
+ </FormItem>
195
+ )}
196
+ />
197
+ <FormField
198
+ control={form.control}
199
+ name="confirmPassword"
200
+ render={({ field }) => (
201
+ <FormItem>
202
+ <FormLabel>Confirm Password</FormLabel>
203
+ <FormControl>
204
+ <Input
205
+ type="password"
206
+ autoComplete="new-password"
207
+ disabled={isPending}
208
+ {...field}
209
+ />
210
+ </FormControl>
211
+ <FormMessage />
212
+ </FormItem>
213
+ )}
214
+ />
215
+ </div>
216
+ </DialogBody>
217
+
218
+ <DialogFooter showCloseButton={true}>
219
+ <Button type="submit" disabled={isPending} size="lg">
220
+ {isPending ? <LoaderCircle className="animate-spin" /> : null}
221
+ {isPending ? 'Changing...' : 'Change Password'}
222
+ </Button>
223
+ </DialogFooter>
224
+ </form>
225
+ </Form>
226
+ </DialogContent>
227
+ </Dialog>
228
+ )
229
+ }
@@ -5,13 +5,13 @@ import { Button } from '@admin/components/ui/button'
5
5
  import {
6
6
  DropdownMenu,
7
7
  DropdownMenuContent,
8
- DropdownMenuItem,
9
8
  DropdownMenuTrigger
10
9
  } from '@admin/components/ui/dropdown-menu'
11
- import type { User } from '@admin/types/auth'
10
+ import { type User, UserRole } from '@admin/types/auth'
12
11
  import { getUserInitials } from '@admin/utils/user/user'
13
12
  import type { ColumnDef } from '@tanstack/react-table'
14
- import { Edit, KeySquare, MoreHorizontal, ShieldCheck } from 'lucide-react'
13
+ import { Edit, MoreHorizontal, ShieldCheck } from 'lucide-react'
14
+ import { ChangePasswordDialog } from './change-password-dialog'
15
15
  import { DeleteUserDialog } from './delete-user-dialog'
16
16
  import { EditRoleDialog } from './edit-role-dialog'
17
17
 
@@ -138,9 +138,11 @@ export const columns: ColumnDef<User>[] = [
138
138
  meta: { width: '80px' },
139
139
  cell: ({ row, table }) => {
140
140
  const currentUser = table.options.meta?.currentUser
141
- const isCurrentUser = currentUser?.email === row.original.email
141
+ const isCurrentUser =
142
+ currentUser?.id === row.original.id || currentUser?.email === row.original.email
143
+ const canChangePassword = isCurrentUser || currentUser?.role === UserRole.ADMIN
142
144
 
143
- if (isCurrentUser) return null
145
+ if (!isCurrentUser && !currentUser) return null
144
146
 
145
147
  return (
146
148
  <div className="flex justify-end">
@@ -152,15 +154,21 @@ export const columns: ColumnDef<User>[] = [
152
154
  </Button>
153
155
  </DropdownMenuTrigger>
154
156
  <DropdownMenuContent align="end">
155
- <DeleteUserDialog
156
- userId={row.original.id}
157
- userName={row.original.name}
158
- isCurrentUser={isCurrentUser}
159
- />
160
- <DropdownMenuItem className="justify-between">
161
- <span>Change Password</span>
162
- <KeySquare />
163
- </DropdownMenuItem>
157
+ {!isCurrentUser ? (
158
+ <DeleteUserDialog
159
+ userId={row.original.id}
160
+ userName={row.original.name}
161
+ isCurrentUser={isCurrentUser}
162
+ />
163
+ ) : null}
164
+ {canChangePassword ? (
165
+ <ChangePasswordDialog
166
+ userId={row.original.id}
167
+ userName={row.original.name}
168
+ userEmail={row.original.email}
169
+ isCurrentUser={isCurrentUser}
170
+ />
171
+ ) : null}
164
172
  </DropdownMenuContent>
165
173
  </DropdownMenu>
166
174
  </div>