@betterstart/cli 0.1.81 → 0.1.82

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.
@@ -0,0 +1,159 @@
1
+ 'use client'
2
+
3
+ import { authClient } from '@cms/auth/client'
4
+ import { Button } from '@cms/components/ui/button'
5
+ import { Card, CardContent } from '@cms/components/ui/card'
6
+ import { Field, FieldGroup, FieldLabel } from '@cms/components/ui/field'
7
+ import { Input } from '@cms/components/ui/input'
8
+ import { cn } from '@cms/utils/cn'
9
+ import { LoaderCircle } from 'lucide-react'
10
+ import Link from 'next/link'
11
+ import { useRouter, useSearchParams } from 'next/navigation'
12
+ import * as React from 'react'
13
+
14
+ export function ResetPasswordForm({
15
+ className,
16
+ ...props
17
+ }: React.ComponentProps<'div'>) {
18
+ const router = useRouter()
19
+ const searchParams = useSearchParams()
20
+ const token = searchParams.get('token')
21
+
22
+ const [newPassword, setNewPassword] = React.useState('')
23
+ const [confirmPassword, setConfirmPassword] = React.useState('')
24
+ const [error, setError] = React.useState<string | null>(null)
25
+ const [isPending, startTransition] = React.useTransition()
26
+
27
+ if (!token) {
28
+ return (
29
+ <div className={cn('flex flex-col gap-6', className)} {...props}>
30
+ <Card className="overflow-hidden p-0">
31
+ <CardContent className="grid p-0 md:grid-cols-[5fr_7fr]">
32
+ <div className="relative hidden bg-muted md:block">
33
+ <img
34
+ src="https://assets.betterstart.dev/assets/placeholder.png"
35
+ alt="Image"
36
+ className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
37
+ />
38
+ </div>
39
+ <div className="p-6 md:p-20">
40
+ <FieldGroup className="pb-12 gap-10">
41
+ <div className="flex flex-col items-start">
42
+ <h1 className="text-xl font-medium">Invalid reset link</h1>
43
+ <p className="text-balance text-muted-foreground">
44
+ This password reset link is invalid or has expired. Please
45
+ request a new one.
46
+ </p>
47
+ </div>
48
+ <Link
49
+ href="/cms/forgot-password"
50
+ className="text-sm underline-offset-4 hover:underline"
51
+ >
52
+ Request a new reset link
53
+ </Link>
54
+ </FieldGroup>
55
+ </div>
56
+ </CardContent>
57
+ </Card>
58
+ </div>
59
+ )
60
+ }
61
+
62
+ const handleSubmit = (e: React.FormEvent) => {
63
+ e.preventDefault()
64
+ setError(null)
65
+
66
+ if (newPassword !== confirmPassword) {
67
+ setError('Passwords do not match')
68
+ return
69
+ }
70
+
71
+ if (newPassword.length < 8) {
72
+ setError('Password must be at least 8 characters')
73
+ return
74
+ }
75
+
76
+ startTransition(async () => {
77
+ try {
78
+ const { error } = await authClient.resetPassword({
79
+ newPassword,
80
+ token,
81
+ })
82
+ if (error) {
83
+ setError(error.message || 'Failed to reset password')
84
+ return
85
+ }
86
+ router.push('/cms/login?reset=success')
87
+ } catch {
88
+ setError('An error occurred. Please try again.')
89
+ }
90
+ })
91
+ }
92
+
93
+ return (
94
+ <div className={cn('flex flex-col gap-6', className)} {...props}>
95
+ <Card className="overflow-hidden p-0">
96
+ <CardContent className="grid p-0 md:grid-cols-[5fr_7fr]">
97
+ <div className="relative hidden bg-muted md:block">
98
+ <img
99
+ src="https://assets.betterstart.dev/assets/placeholder.png"
100
+ alt="Image"
101
+ className="absolute inset-0 h-full w-full object-cover dark:brightness-[0.2] dark:grayscale"
102
+ />
103
+ </div>
104
+ <form onSubmit={handleSubmit} className="p-6 md:p-20">
105
+ <FieldGroup className="pb-12 gap-10">
106
+ <div className="flex flex-col items-start">
107
+ <h1 className="text-xl font-medium">Reset password</h1>
108
+ <p className="text-balance text-muted-foreground">
109
+ Enter your new password below
110
+ </p>
111
+ </div>
112
+ {error ? (
113
+ <div className="bg-destructive/10 text-destructive text-sm p-3 rounded-md">
114
+ {error}
115
+ </div>
116
+ ) : null}
117
+ <div className="flex flex-col gap-4">
118
+ <Field>
119
+ <FieldLabel htmlFor="newPassword">New Password</FieldLabel>
120
+ <Input
121
+ id="newPassword"
122
+ type="password"
123
+ autoComplete="new-password"
124
+ placeholder="Enter new password"
125
+ value={newPassword}
126
+ onChange={(e) => setNewPassword(e.target.value)}
127
+ required
128
+ disabled={isPending}
129
+ />
130
+ </Field>
131
+ <Field>
132
+ <FieldLabel htmlFor="confirmPassword">
133
+ Confirm Password
134
+ </FieldLabel>
135
+ <Input
136
+ id="confirmPassword"
137
+ type="password"
138
+ autoComplete="new-password"
139
+ placeholder="Confirm new password"
140
+ value={confirmPassword}
141
+ onChange={(e) => setConfirmPassword(e.target.value)}
142
+ required
143
+ disabled={isPending}
144
+ />
145
+ </Field>
146
+ <Field className="pt-4">
147
+ <Button type="submit" disabled={isPending}>
148
+ {isPending && <LoaderCircle className="animate-spin" />}
149
+ {isPending ? 'Resetting...' : 'Reset Password'}
150
+ </Button>
151
+ </Field>
152
+ </div>
153
+ </FieldGroup>
154
+ </form>
155
+ </CardContent>
156
+ </Card>
157
+ </div>
158
+ )
159
+ }
@@ -0,0 +1,20 @@
1
+ import type { Metadata } from 'next'
2
+ import { Suspense } from 'react'
3
+ import { ResetPasswordForm } from './reset-password-form'
4
+
5
+ export const metadata: Metadata = {
6
+ title: 'Reset Password',
7
+ robots: { index: false, follow: false },
8
+ }
9
+
10
+ export default function ResetPasswordPage() {
11
+ return (
12
+ <div className="flex min-h-svh flex-col items-center justify-center bg-background p-6 md:p-10">
13
+ <div className="w-full max-w-sm md:max-w-4xl">
14
+ <Suspense>
15
+ <ResetPasswordForm />
16
+ </Suspense>
17
+ </div>
18
+ </div>
19
+ )
20
+ }
@@ -22,7 +22,7 @@ const buttonVariants = cva(
22
22
  },
23
23
  size: {
24
24
  default:
25
- "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
25
+ "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
26
26
  xs: "h-7 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
27
27
  sm: "h-9 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
28
28
  lg: "h-12 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
@@ -12,7 +12,7 @@ function Card({
12
12
  data-slot="card"
13
13
  data-size={size}
14
14
  className={cn(
15
- "group/card flex flex-col gap-6 overflow-hidden rounded-xl bg-card py-6 text-sm text-card-foreground has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl material-md",
15
+ "group/card flex flex-col gap-6 overflow-hidden rounded bg-card py-6 text-sm text-card-foreground has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t *:[img:last-child]:rounded-b material-sm",
16
16
  className
17
17
  )}
18
18
  {...props}
@@ -84,7 +84,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
84
84
  <div
85
85
  data-slot="card-footer"
86
86
  className={cn(
87
- "flex items-center border-t bg-muted/50 px-6 pt-4 pb-4.5 group-data-[size=sm]/card:p-3",
87
+ "flex items-center border-t bg-background px-6 pt-4 pb-4.5 group-data-[size=sm]/card:p-3",
88
88
  className
89
89
  )}
90
90
  {...props}
@@ -296,7 +296,7 @@ const SequentialEditor = ({
296
296
  >
297
297
  {items.map((item, index) => (
298
298
  <AccordionItem key={index} value={`item-${index + 1}`} className="p-0 border-none">
299
- <div className="space-y-5 rounded-lg border p-4 bg-secondary/50 corner-squircle [&_h3]:m-0 w-full">
299
+ <div className="space-y-5 rounded-lg border p-4 bg-background corner-squircle [&_h3]:m-0 w-full">
300
300
  <AccordionTrigger className="flex items-center p-0 justify-between w-full">
301
301
  <div className="flex w-full items-center justify-between">
302
302
  <h4 className="text-sm font-medium w-full">
@@ -438,7 +438,7 @@ const WeeklyEditor = ({
438
438
  value={`week-${weekIndex + 1}`}
439
439
  className="p-0 border-none"
440
440
  >
441
- <div className="space-y-5 rounded-lg border p-4 bg-secondary/50 corner-squircle [&_h3]:m-0 w-full">
441
+ <div className="space-y-5 rounded-lg border p-4 bg-background corner-squircle [&_h3]:m-0 w-full">
442
442
  <AccordionTrigger className="flex items-center p-0 justify-between w-full">
443
443
  <div className="flex w-full items-center justify-between">
444
444
  <div className="flex flex-col items-start">
@@ -277,7 +277,7 @@ export function ImageUploadField({
277
277
 
278
278
  {/* Preview or Upload Area */}
279
279
  {displayPreview ? (
280
- <div className="relative w-full rounded-lg corner-squircle border border-dashed border-border bg-secondary h-50 flex items-center justify-center p-10 group">
280
+ <div className="relative w-full rounded-lg corner-squircle border border-dashed border-border bg-background h-50 flex items-center justify-center p-10 group">
281
281
  <img
282
282
  src={displayPreview}
283
283
  alt="Preview"
@@ -320,7 +320,7 @@ export function ImageUploadField({
320
320
  onClick={handleClick}
321
321
  disabled={disabled || isUploading}
322
322
  className={cn(
323
- 'w-full rounded-lg corner-squircle border border-dashed border-border bg-secondary h-50 flex items-center justify-center p-10 group',
323
+ 'w-full rounded-lg corner-squircle border border-dashed border-border bg-background h-50 flex items-center justify-center p-10 group',
324
324
  'hover:border-primary/50 transition-colors',
325
325
  'flex flex-col items-center justify-center gap-2 p-8',
326
326
  'disabled:opacity-50 disabled:cursor-not-allowed'
@@ -1,13 +1,14 @@
1
- import { cn } from '@cms/utils/cn'
2
- import type * as React from 'react'
1
+ import * as React from "react"
3
2
 
4
- function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
3
+ import { cn } from "@cms/utils/cn"
4
+
5
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
5
6
  return (
6
7
  <input
7
8
  type={type}
8
9
  data-slot="input"
9
10
  className={cn(
10
- 'dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 file:text-foreground placeholder:text-muted-foreground h-10 w-full min-w-0 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-3 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm',
11
+ "h-9 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
11
12
  className
12
13
  )}
13
14
  {...props}
@@ -209,7 +209,7 @@ export function MediaUploadField({
209
209
 
210
210
  {/* Preview or Upload Area */}
211
211
  {previewUrl ? (
212
- <div className="relative w-full rounded-lg corner-squircle border border-dashed border-border bg-secondary/50 group">
212
+ <div className="relative w-full rounded-lg corner-squircle border border-dashed border-border bg-background group">
213
213
  {mediaType === 'video' ? (
214
214
  <video
215
215
  src={previewUrl}
@@ -265,7 +265,7 @@ export function MediaUploadField({
265
265
  onClick={handleClick}
266
266
  disabled={disabled || isUploading}
267
267
  className={cn(
268
- 'w-full rounded-lg corner-squircle border border-dashed border-border bg-secondary/50',
268
+ 'w-full rounded-lg corner-squircle border border-dashed border-border bg-background',
269
269
  'hover:border-primary/50 transition-colors',
270
270
  'flex flex-col items-center justify-center gap-2 p-8',
271
271
  'disabled:opacity-50 disabled:cursor-not-allowed'
@@ -12,7 +12,7 @@ export function Placeholder({ label, description, action, className, ...props }:
12
12
  return (
13
13
  <div
14
14
  className={cn(
15
- 'border flex items-center flex-col gap-2 border-dashed border-border rounded-lg corner-squircle p-8 bg-secondary/50',
15
+ 'border flex items-center flex-col gap-2 border-dashed border-border rounded-lg corner-squircle p-8 bg-background',
16
16
  className
17
17
  )}
18
18
  {...props}
@@ -454,7 +454,7 @@ const sidebarMenuButtonVariants = cva(
454
454
  'bg-background hover:bg-sidebar-accent hover:text-sidebar-primary shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]'
455
455
  },
456
456
  size: {
457
- default: 'h-9 text-sm',
457
+ default: 'h-8 text-sm',
458
458
  sm: 'h-7 text-xs',
459
459
  lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!'
460
460
  }
@@ -283,7 +283,7 @@ export function VideoUploadField({
283
283
 
284
284
  {/* Preview or Upload Area */}
285
285
  {displayPreview ? (
286
- <div className="relative w-full rounded-lg corner-squircle border border-dashed border-border bg-secondary h-50 flex items-center justify-center p-10 group overflow-hidden">
286
+ <div className="relative w-full rounded-lg corner-squircle border border-dashed border-border bg-background h-50 flex items-center justify-center p-10 group overflow-hidden">
287
287
  <video
288
288
  src={displayPreview}
289
289
  controls
@@ -328,7 +328,7 @@ export function VideoUploadField({
328
328
  onClick={handleClick}
329
329
  disabled={disabled || isUploading}
330
330
  className={cn(
331
- 'w-full rounded-lg corner-squircle border border-dashed border-border bg-secondary/50',
331
+ 'w-full rounded-lg corner-squircle border border-dashed border-border bg-background',
332
332
  'hover:border-primary/50 transition-colors',
333
333
  'flex flex-col items-center justify-center gap-2 p-8',
334
334
  'disabled:opacity-50 disabled:cursor-not-allowed'