@betterstart/cli 0.1.81 → 0.1.83
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/dist/cli.js +176 -305
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/init/components/data-table/data-table.tsx +1 -1
- package/templates/init/components/layout/cms-search.tsx +6 -5
- package/templates/init/components/layout/cms-sidebar.tsx +3 -7
- package/templates/init/components/shared/page-header.tsx +5 -5
- package/templates/init/data/navigation.ts +6 -6
- package/templates/init/lib/actions/profile.ts +4 -0
- package/templates/init/lib/auth/auth.ts +17 -0
- package/templates/init/pages/dashboard-page.tsx +1 -1
- package/templates/init/pages/forgot-password-form.tsx +130 -0
- package/templates/init/pages/forgot-password-page.tsx +20 -0
- package/templates/init/pages/login-form.tsx +17 -1
- package/templates/init/pages/login-page.tsx +12 -2
- package/templates/init/pages/profile/profile-form.tsx +65 -137
- package/templates/init/pages/reset-password-form.tsx +159 -0
- package/templates/init/pages/reset-password-page.tsx +20 -0
- package/templates/init/pages/users/columns.tsx +13 -10
- package/templates/init/pages/users/users-page-content.tsx +9 -2
- package/templates/init/pages/users/users-page.tsx +1 -1
- package/templates/init/pages/users/users-table.tsx +2 -2
- package/templates/ui/badge.tsx +2 -2
- package/templates/ui/button.tsx +2 -2
- package/templates/ui/card.tsx +2 -2
- package/templates/ui/curriculum-editor.tsx +2 -2
- package/templates/ui/image-upload-field.tsx +2 -2
- package/templates/ui/input.tsx +5 -4
- package/templates/ui/media-upload-field.tsx +2 -2
- package/templates/ui/placeholder.tsx +1 -1
- package/templates/ui/sidebar.tsx +3 -3
- package/templates/ui/table.tsx +10 -4
- package/templates/ui/video-upload-field.tsx +2 -2
|
@@ -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
|
+
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
AlertDialogTitle,
|
|
13
13
|
AlertDialogTrigger,
|
|
14
14
|
} from '@cms/components/ui/alert-dialog'
|
|
15
|
-
import { Avatar, AvatarFallback } from '@cms/components/ui/avatar'
|
|
15
|
+
import { Avatar, AvatarFallback, AvatarImage } from '@cms/components/ui/avatar'
|
|
16
16
|
import { Badge } from '@cms/components/ui/badge'
|
|
17
17
|
import { Button } from '@cms/components/ui/button'
|
|
18
18
|
import {
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
import type { UserData } from '@cms/types/auth'
|
|
27
27
|
import { useQueryClient } from '@tanstack/react-query'
|
|
28
28
|
import type { ColumnDef } from '@tanstack/react-table'
|
|
29
|
-
import { ArrowUpDown, Edit, MoreHorizontal, Trash } from 'lucide-react'
|
|
29
|
+
import { ArrowUpDown, Edit, MoreHorizontal, ShieldCheck, Trash } from 'lucide-react'
|
|
30
30
|
import React from 'react'
|
|
31
31
|
import { toast } from 'sonner'
|
|
32
32
|
import { EditRoleDialog } from './edit-role-dialog'
|
|
@@ -117,10 +117,10 @@ export const columns: ColumnDef<UserData>[] = [
|
|
|
117
117
|
<Button
|
|
118
118
|
variant="ghost"
|
|
119
119
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
120
|
-
className="hover:bg-
|
|
120
|
+
className="px-0 text-muted-foreground hover:text-foreground w-full hover:bg-transparent justify-start"
|
|
121
121
|
>
|
|
122
122
|
User
|
|
123
|
-
<ArrowUpDown className="size-
|
|
123
|
+
<ArrowUpDown className="size-3" />
|
|
124
124
|
</Button>
|
|
125
125
|
),
|
|
126
126
|
cell: ({ row }) => {
|
|
@@ -145,8 +145,9 @@ export const columns: ColumnDef<UserData>[] = [
|
|
|
145
145
|
cell: ({ row }) => {
|
|
146
146
|
const verified = row.getValue('emailVerified') as boolean
|
|
147
147
|
return (
|
|
148
|
-
<Badge variant=
|
|
149
|
-
|
|
148
|
+
<Badge variant="outline">
|
|
149
|
+
|
|
150
|
+
{verified ? <span className="flex items-center gap-1"><ShieldCheck className="size-3.5" />Verified</span> : <span className="flex items-center gap-1"><ShieldCheck className="size-3.5" />Unverified</span>}
|
|
150
151
|
</Badge>
|
|
151
152
|
)
|
|
152
153
|
},
|
|
@@ -174,8 +175,10 @@ export const columns: ColumnDef<UserData>[] = [
|
|
|
174
175
|
userName={row.original.name}
|
|
175
176
|
>
|
|
176
177
|
<Badge variant="outline" className="capitalize cursor-pointer">
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
<span className="flex items-center gap-1">
|
|
179
|
+
{role}
|
|
180
|
+
<Edit className="size-3" />
|
|
181
|
+
</span>
|
|
179
182
|
</Badge>
|
|
180
183
|
</EditRoleDialog>
|
|
181
184
|
)
|
|
@@ -187,10 +190,10 @@ export const columns: ColumnDef<UserData>[] = [
|
|
|
187
190
|
<Button
|
|
188
191
|
variant="ghost"
|
|
189
192
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
|
190
|
-
className="hover:bg-
|
|
193
|
+
className="px-0 text-muted-foreground hover:text-foreground w-full hover:bg-transparent justify-start"
|
|
191
194
|
>
|
|
192
195
|
Joined
|
|
193
|
-
<ArrowUpDown className="size-
|
|
196
|
+
<ArrowUpDown className="size-3" />
|
|
194
197
|
</Button>
|
|
195
198
|
),
|
|
196
199
|
cell: ({ row }) => {
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import React from 'react'
|
|
3
4
|
import type { ColumnDef } from '@tanstack/react-table'
|
|
5
|
+
import { ChevronLeft } from 'lucide-react'
|
|
6
|
+
import { useRouter } from 'next/navigation'
|
|
4
7
|
import { PageHeader } from '@cms/components/shared/page-header'
|
|
5
8
|
import type { UserData } from '@cms/types/auth'
|
|
9
|
+
import { Button } from '@cms/components/ui/button'
|
|
6
10
|
import { CreateUserDialog } from './create-user-dialog'
|
|
7
11
|
import { UsersTable } from './users-table'
|
|
8
12
|
|
|
@@ -11,10 +15,13 @@ interface UsersPageContentProps<TValue> {
|
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
export function UsersPageContent<TValue>({ columns }: UsersPageContentProps<TValue>) {
|
|
18
|
+
const router = useRouter()
|
|
19
|
+
|
|
14
20
|
return (
|
|
15
|
-
|
|
21
|
+
<React.Fragment>
|
|
16
22
|
<PageHeader
|
|
17
23
|
title="Users"
|
|
24
|
+
back={<Button variant="ghost" size="icon" onClick={() => router.back()}><ChevronLeft /></Button>}
|
|
18
25
|
actions={
|
|
19
26
|
<div className="flex items-center gap-2">
|
|
20
27
|
<CreateUserDialog />
|
|
@@ -24,6 +31,6 @@ export function UsersPageContent<TValue>({ columns }: UsersPageContentProps<TVal
|
|
|
24
31
|
<main className="space-y-6 p-6">
|
|
25
32
|
<UsersTable columns={columns} />
|
|
26
33
|
</main>
|
|
27
|
-
|
|
34
|
+
</React.Fragment>
|
|
28
35
|
)
|
|
29
36
|
}
|
|
@@ -126,7 +126,7 @@ export function UsersTable<TValue>({ columns }: UsersTableProps<TValue>) {
|
|
|
126
126
|
<div className="space-y-6">
|
|
127
127
|
<div className="bg-card border overflow-hidden rounded-lg">
|
|
128
128
|
<Table>
|
|
129
|
-
<TableHeader
|
|
129
|
+
<TableHeader>
|
|
130
130
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
131
131
|
<TableRow key={headerGroup.id}>
|
|
132
132
|
{headerGroup.headers.map((header) => (
|
|
@@ -180,7 +180,7 @@ export function UsersTable<TValue>({ columns }: UsersTableProps<TValue>) {
|
|
|
180
180
|
value={pageSize === -1 ? 'all' : String(pageSize)}
|
|
181
181
|
onValueChange={handlePageSizeChange}
|
|
182
182
|
>
|
|
183
|
-
<SelectTrigger className="w-
|
|
183
|
+
<SelectTrigger className="w-25 h-8">
|
|
184
184
|
<SelectValue />
|
|
185
185
|
</SelectTrigger>
|
|
186
186
|
<SelectContent>
|
package/templates/ui/badge.tsx
CHANGED
|
@@ -4,12 +4,12 @@ import { Slot } from 'radix-ui'
|
|
|
4
4
|
import type * as React from 'react'
|
|
5
5
|
|
|
6
6
|
const badgeVariants = cva(
|
|
7
|
-
'h-
|
|
7
|
+
'h-6 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive overflow-hidden group/badge',
|
|
8
8
|
{
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
11
|
default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
|
|
12
|
-
secondary: 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
|
|
12
|
+
secondary: 'bg-secondary border-border text-secondary-foreground [a]:hover:bg-secondary/80',
|
|
13
13
|
destructive:
|
|
14
14
|
'bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20',
|
|
15
15
|
outline: 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
|
package/templates/ui/button.tsx
CHANGED
|
@@ -11,7 +11,7 @@ const buttonVariants = cva(
|
|
|
11
11
|
variant: {
|
|
12
12
|
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
13
13
|
outline:
|
|
14
|
-
"border-border bg-
|
|
14
|
+
"border-border bg-white hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
15
15
|
secondary:
|
|
16
16
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
17
17
|
ghost:
|
|
@@ -22,7 +22,7 @@ const buttonVariants = cva(
|
|
|
22
22
|
},
|
|
23
23
|
size: {
|
|
24
24
|
default:
|
|
25
|
-
"h-
|
|
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",
|
package/templates/ui/card.tsx
CHANGED
|
@@ -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
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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'
|
package/templates/ui/input.tsx
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type * as React from 'react'
|
|
1
|
+
import * as React from "react"
|
|
3
2
|
|
|
4
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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}
|
package/templates/ui/sidebar.tsx
CHANGED
|
@@ -160,7 +160,7 @@ function Sidebar({
|
|
|
160
160
|
<div
|
|
161
161
|
data-slot="sidebar"
|
|
162
162
|
className={cn(
|
|
163
|
-
'bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col',
|
|
163
|
+
'bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col relative z-11',
|
|
164
164
|
className
|
|
165
165
|
)}
|
|
166
166
|
{...props}
|
|
@@ -198,7 +198,7 @@ function Sidebar({
|
|
|
198
198
|
|
|
199
199
|
return (
|
|
200
200
|
<div
|
|
201
|
-
className="group peer text-sidebar-foreground hidden md:block"
|
|
201
|
+
className="group peer text-sidebar-foreground hidden md:block z-11 relative"
|
|
202
202
|
data-state={state}
|
|
203
203
|
data-collapsible={state === 'collapsed' ? collapsible : ''}
|
|
204
204
|
data-variant={variant}
|
|
@@ -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-
|
|
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
|
}
|
package/templates/ui/table.tsx
CHANGED
|
@@ -16,7 +16,13 @@ function Table({ className, ...props }: React.ComponentProps<'table'>) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
|
|
19
|
-
return
|
|
19
|
+
return (
|
|
20
|
+
<thead
|
|
21
|
+
data-slot="table-header"
|
|
22
|
+
className={cn('[&_tr]:border-b [&_tr:hover]:bg-transparent! bg-background [&_th]:text-muted-foreground', className)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
|
|
@@ -33,7 +39,7 @@ function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
|
|
|
33
39
|
return (
|
|
34
40
|
<tfoot
|
|
35
41
|
data-slot="table-footer"
|
|
36
|
-
className={cn('bg-
|
|
42
|
+
className={cn('bg-background border-t font-medium [&>tr]:last:border-b-0', className)}
|
|
37
43
|
{...props}
|
|
38
44
|
/>
|
|
39
45
|
)
|
|
@@ -44,7 +50,7 @@ function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
|
|
|
44
50
|
<tr
|
|
45
51
|
data-slot="table-row"
|
|
46
52
|
className={cn(
|
|
47
|
-
'hover:bg-
|
|
53
|
+
'hover:bg-background data-[state=selected]:bg-background border-b transition-colors',
|
|
48
54
|
className
|
|
49
55
|
)}
|
|
50
56
|
{...props}
|
|
@@ -69,7 +75,7 @@ function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
|
|
|
69
75
|
return (
|
|
70
76
|
<td
|
|
71
77
|
data-slot="table-cell"
|
|
72
|
-
className={cn('px-4 py-
|
|
78
|
+
className={cn('px-4 py-3.5 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0', className)}
|
|
73
79
|
{...props}
|
|
74
80
|
/>
|
|
75
81
|
)
|
|
@@ -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-
|
|
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-
|
|
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'
|