@betterstart/cli 0.1.79 → 0.1.81

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@betterstart/cli",
3
- "version": "0.1.79",
3
+ "version": "0.1.81",
4
4
  "description": "Scaffold a full-featured CMS into any Next.js 16 application",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -9,6 +9,7 @@ import { updateEmail } from '@cms/actions/profile'
9
9
  import { Avatar, AvatarFallback, AvatarImage } from '@cms/components/ui/avatar'
10
10
  import { Button } from '@cms/components/ui/button'
11
11
  import { Input } from '@cms/components/ui/input'
12
+ import { LoaderCircle } from 'lucide-react'
12
13
  import {
13
14
  Form,
14
15
  FormControl,
@@ -18,7 +19,14 @@ import {
18
19
  FormMessage,
19
20
  } from '@cms/components/ui/form'
20
21
  import { ImageUploadField } from '@cms/components/ui/image-upload-field'
21
- import { Separator } from '@cms/components/ui/separator'
22
+ import {
23
+ Card,
24
+ CardContent,
25
+ CardDescription,
26
+ CardFooter,
27
+ CardHeader,
28
+ CardTitle,
29
+ } from '@cms/components/ui/card'
22
30
  import { useRouter } from 'next/navigation'
23
31
  import { useState } from 'react'
24
32
 
@@ -80,12 +88,12 @@ export function ProfileForm({ user }: ProfileFormProps) {
80
88
  })
81
89
 
82
90
  const emailDirty = profileForm.watch('email') !== user.email
91
+ const imageValue = profileForm.watch('image')
83
92
 
84
93
  async function onProfileSubmit(values: ProfileValues) {
85
94
  setProfilePending(true)
86
95
 
87
96
  try {
88
- // Update name and image via Better Auth
89
97
  const { error } = await authClient.updateUser({
90
98
  name: values.name,
91
99
  image: values.image ?? null,
@@ -97,7 +105,6 @@ export function ProfileForm({ user }: ProfileFormProps) {
97
105
  return
98
106
  }
99
107
 
100
- // If email changed, update via server action
101
108
  if (values.email !== user.email) {
102
109
  if (!values.currentPasswordForEmail) {
103
110
  profileForm.setError('currentPasswordForEmail', {
@@ -158,202 +165,195 @@ export function ProfileForm({ user }: ProfileFormProps) {
158
165
  }
159
166
  }
160
167
 
161
- const imageValue = profileForm.watch('image')
162
-
163
168
  return (
164
169
  <div className="space-y-6">
165
- {/* Profile Section */}
166
170
  <Form {...profileForm}>
167
- <form
168
- onSubmit={profileForm.handleSubmit(onProfileSubmit)}
169
- className="space-y-6 p-6 rounded-2xl border bg-card"
170
- >
171
- <div className="flex items-center gap-4">
172
- <Avatar className="size-16">
173
- <AvatarImage src={imageValue || undefined} />
174
- <AvatarFallback className="text-lg font-semibold">
175
- {user.name?.charAt(0) ?? '?'}
176
- </AvatarFallback>
177
- </Avatar>
178
- <div>
179
- <h2 className="text-lg font-semibold">Profile</h2>
180
- <p className="text-sm text-muted-foreground">
181
- Update your name, email, and profile picture
182
- </p>
183
- </div>
184
- </div>
185
-
186
- <Separator />
187
-
188
- <FormField
189
- control={profileForm.control}
190
- name="image"
191
- render={({ field: formField }) => (
192
- <FormItem>
193
- <FormLabel>Profile Picture</FormLabel>
194
- <FormControl>
195
- <ImageUploadField
196
- value={formField.value}
197
- onChange={formField.onChange}
198
- onBlur={formField.onBlur}
199
- disabled={profilePending}
200
- maxSizeInMB={5}
201
- label=""
202
- />
203
- </FormControl>
204
- <FormMessage />
205
- </FormItem>
206
- )}
207
- />
208
-
209
- <FormField
210
- control={profileForm.control}
211
- name="name"
212
- render={({ field: formField }) => (
213
- <FormItem>
214
- <FormLabel>Name</FormLabel>
215
- <FormControl>
216
- <Input
217
- type="text"
218
- placeholder="Your name"
219
- disabled={profilePending}
220
- {...formField}
221
- />
222
- </FormControl>
223
- <FormMessage />
224
- </FormItem>
225
- )}
226
- />
227
-
228
- <FormField
229
- control={profileForm.control}
230
- name="email"
231
- render={({ field: formField }) => (
232
- <FormItem>
233
- <FormLabel>Email</FormLabel>
234
- <FormControl>
235
- <Input
236
- type="email"
237
- placeholder="you@example.com"
238
- disabled={profilePending}
239
- {...formField}
240
- />
241
- </FormControl>
242
- <FormMessage />
243
- </FormItem>
244
- )}
245
- />
246
-
247
- {emailDirty && (
248
- <FormField
249
- control={profileForm.control}
250
- name="currentPasswordForEmail"
251
- render={({ field: formField }) => (
252
- <FormItem>
253
- <FormLabel>Current Password</FormLabel>
254
- <FormControl>
255
- <Input
256
- type="password"
257
- placeholder="Enter current password to change email"
258
- autoComplete="current-password"
259
- disabled={profilePending}
260
- {...formField}
261
- />
262
- </FormControl>
263
- <FormMessage />
264
- </FormItem>
171
+ <form onSubmit={profileForm.handleSubmit(onProfileSubmit)}>
172
+ <Card className="material-sm!">
173
+ <CardHeader>
174
+ <div className="flex items-center gap-4">
175
+ <Avatar className="size-12">
176
+ <AvatarImage src={imageValue || undefined} />
177
+ <AvatarFallback className="text-lg font-semibold">
178
+ {user.name?.charAt(0) ?? '?'}
179
+ </AvatarFallback>
180
+ </Avatar>
181
+ <div>
182
+ <CardTitle>Profile</CardTitle>
183
+ <CardDescription>
184
+ Update your name, email, and profile picture
185
+ </CardDescription>
186
+ </div>
187
+ </div>
188
+ </CardHeader>
189
+ <CardContent className="space-y-6">
190
+ <FormField
191
+ control={profileForm.control}
192
+ name="name"
193
+ render={({ field: formField }) => (
194
+ <FormItem>
195
+ <FormLabel>Name</FormLabel>
196
+ <FormControl>
197
+ <Input
198
+ type="text"
199
+ placeholder="Your name"
200
+ disabled={profilePending}
201
+ {...formField}
202
+ />
203
+ </FormControl>
204
+ <FormMessage />
205
+ </FormItem>
206
+ )}
207
+ />
208
+
209
+ <FormField
210
+ control={profileForm.control}
211
+ name="email"
212
+ render={({ field: formField }) => (
213
+ <FormItem>
214
+ <FormLabel>Email</FormLabel>
215
+ <FormControl>
216
+ <Input
217
+ type="email"
218
+ placeholder="you@example.com"
219
+ disabled={profilePending}
220
+ {...formField}
221
+ />
222
+ </FormControl>
223
+ <FormMessage />
224
+ </FormItem>
225
+ )}
226
+ />
227
+
228
+ {emailDirty && (
229
+ <FormField
230
+ control={profileForm.control}
231
+ name="currentPasswordForEmail"
232
+ render={({ field: formField }) => (
233
+ <FormItem>
234
+ <FormLabel>Current Password</FormLabel>
235
+ <FormControl>
236
+ <Input
237
+ type="password"
238
+ placeholder="Enter current password to change email"
239
+ autoComplete="current-password"
240
+ disabled={profilePending}
241
+ {...formField}
242
+ />
243
+ </FormControl>
244
+ <FormMessage />
245
+ </FormItem>
246
+ )}
247
+ />
265
248
  )}
266
- />
267
- )}
268
249
 
269
- <div className="flex justify-end">
270
- <Button type="submit" disabled={profilePending}>
271
- {profilePending ? 'Saving...' : 'Save Profile'}
272
- </Button>
273
- </div>
250
+ <FormField
251
+ control={profileForm.control}
252
+ name="image"
253
+ render={({ field: formField }) => (
254
+ <FormItem>
255
+ <FormLabel>Profile Picture</FormLabel>
256
+ <FormControl>
257
+ <ImageUploadField
258
+ value={formField.value}
259
+ onChange={formField.onChange}
260
+ onBlur={formField.onBlur}
261
+ disabled={profilePending}
262
+ maxSizeInMB={5}
263
+ label=""
264
+ />
265
+ </FormControl>
266
+ <FormMessage />
267
+ </FormItem>
268
+ )}
269
+ />
270
+ </CardContent>
271
+ <CardFooter>
272
+ <Button type="submit" disabled={profilePending || !profileForm.formState.isDirty} size="sm" className="ml-auto min-w-25">
273
+ {profilePending && <LoaderCircle className="animate-spin" />}
274
+ {profilePending ? 'Saving...' : 'Save'}
275
+ </Button>
276
+ </CardFooter>
277
+ </Card>
274
278
  </form>
275
279
  </Form>
276
280
 
277
- {/* Password Section */}
278
281
  <Form {...passwordForm}>
279
- <form
280
- onSubmit={passwordForm.handleSubmit(onPasswordSubmit)}
281
- className="space-y-6 p-6 rounded-2xl border bg-card"
282
- >
283
- <div>
284
- <h2 className="text-lg font-semibold">Change Password</h2>
285
- <p className="text-sm text-muted-foreground">
286
- Update your password to keep your account secure
287
- </p>
288
- </div>
289
-
290
- <Separator />
291
-
292
- <FormField
293
- control={passwordForm.control}
294
- name="currentPassword"
295
- render={({ field: formField }) => (
296
- <FormItem>
297
- <FormLabel>Current Password</FormLabel>
298
- <FormControl>
299
- <Input
300
- type="password"
301
- placeholder="Enter current password"
302
- autoComplete="current-password"
303
- disabled={passwordPending}
304
- {...formField}
305
- />
306
- </FormControl>
307
- <FormMessage />
308
- </FormItem>
309
- )}
310
- />
311
-
312
- <FormField
313
- control={passwordForm.control}
314
- name="newPassword"
315
- render={({ field: formField }) => (
316
- <FormItem>
317
- <FormLabel>New Password</FormLabel>
318
- <FormControl>
319
- <Input
320
- type="password"
321
- placeholder="Enter new password"
322
- autoComplete="new-password"
323
- disabled={passwordPending}
324
- {...formField}
325
- />
326
- </FormControl>
327
- <FormMessage />
328
- </FormItem>
329
- )}
330
- />
331
-
332
- <FormField
333
- control={passwordForm.control}
334
- name="confirmPassword"
335
- render={({ field: formField }) => (
336
- <FormItem>
337
- <FormLabel>Confirm New Password</FormLabel>
338
- <FormControl>
339
- <Input
340
- type="password"
341
- placeholder="Confirm new password"
342
- autoComplete="new-password"
343
- disabled={passwordPending}
344
- {...formField}
345
- />
346
- </FormControl>
347
- <FormMessage />
348
- </FormItem>
349
- )}
350
- />
351
-
352
- <div className="flex justify-end">
353
- <Button type="submit" disabled={passwordPending}>
354
- {passwordPending ? 'Changing...' : 'Change Password'}
355
- </Button>
356
- </div>
282
+ <form onSubmit={passwordForm.handleSubmit(onPasswordSubmit)}>
283
+ <Card className="material-sm!">
284
+ <CardHeader>
285
+ <CardTitle>Change Password</CardTitle>
286
+ <CardDescription>
287
+ Update your password to keep your account secure
288
+ </CardDescription>
289
+ </CardHeader>
290
+ <CardContent className="space-y-6">
291
+ <FormField
292
+ control={passwordForm.control}
293
+ name="currentPassword"
294
+ render={({ field: formField }) => (
295
+ <FormItem>
296
+ <FormLabel>Current Password</FormLabel>
297
+ <FormControl>
298
+ <Input
299
+ type="password"
300
+ placeholder="Enter current password"
301
+ autoComplete="current-password"
302
+ disabled={passwordPending}
303
+ {...formField}
304
+ />
305
+ </FormControl>
306
+ <FormMessage />
307
+ </FormItem>
308
+ )}
309
+ />
310
+
311
+ <FormField
312
+ control={passwordForm.control}
313
+ name="newPassword"
314
+ render={({ field: formField }) => (
315
+ <FormItem>
316
+ <FormLabel>New Password</FormLabel>
317
+ <FormControl>
318
+ <Input
319
+ type="password"
320
+ placeholder="Enter new password"
321
+ autoComplete="new-password"
322
+ disabled={passwordPending}
323
+ {...formField}
324
+ />
325
+ </FormControl>
326
+ <FormMessage />
327
+ </FormItem>
328
+ )}
329
+ />
330
+
331
+ <FormField
332
+ control={passwordForm.control}
333
+ name="confirmPassword"
334
+ render={({ field: formField }) => (
335
+ <FormItem>
336
+ <FormLabel>Confirm New Password</FormLabel>
337
+ <FormControl>
338
+ <Input
339
+ type="password"
340
+ placeholder="Confirm new password"
341
+ autoComplete="new-password"
342
+ disabled={passwordPending}
343
+ {...formField}
344
+ />
345
+ </FormControl>
346
+ <FormMessage />
347
+ </FormItem>
348
+ )}
349
+ />
350
+ </CardContent>
351
+ <CardFooter>
352
+ <Button type="submit" disabled={passwordPending || !passwordForm.formState.isDirty} size="sm" className="ml-auto min-w-25">
353
+ {passwordPending ? 'Changing...' : 'Change Password'}
354
+ </Button>
355
+ </CardFooter>
356
+ </Card>
357
357
  </form>
358
358
  </Form>
359
359
  </div>
@@ -20,7 +20,7 @@ export default async function ProfilePage() {
20
20
  return (
21
21
  <>
22
22
  {showPageHeader && <PageHeader title="Profile" />}
23
- <main className="container mx-auto max-w-2xl p-6 pb-20">
23
+ <main className="container mx-auto max-w-5xl pt-10 pb-20 w-full">
24
24
  <ProfileForm
25
25
  user={{
26
26
  name: session.user.name,