Update prettier

This commit is contained in:
2026-01-14 00:33:38 -06:00
parent 4b5c12d868
commit ce2264ef6d
58 changed files with 12945 additions and 568 deletions

View File

@@ -1 +1 @@
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22"],{"key":"23","value":"24"},{"key":"25","value":"26"},{"key":"27","value":"28"},{"key":"29","value":"30"},{"key":"31","value":"32"},{"key":"33","value":"34"},{"key":"35","value":"36"},{"key":"37","value":"38"},{"key":"39","value":"40"},{"key":"41","value":"42"},{"key":"43","value":"44"},{"key":"45","value":"46"},{"key":"47","value":"48"},{"key":"49","value":"50"},{"key":"51","value":"52"},{"key":"53","value":"54"},{"key":"55","value":"56"},{"key":"57","value":"58"},{"key":"59","value":"60"},{"key":"61","value":"62"},{"key":"63","value":"64"},{"key":"65","value":"66"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/package.json",{"size":2249,"mtime":1766222924000,"hash":"67","data":"68"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/index.ts",{"size":28,"mtime":1768155639000,"hash":"69","data":"70"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/metro.config.js",{"size":511,"mtime":1768155639000,"hash":"71","data":"72"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/index.tsx",{"size":5019,"mtime":1768155639000,"hash":"73","data":"74"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/turbo.json",{"size":163,"mtime":1766222924000,"hash":"75","data":"76"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/postcss.config.js",{"size":66,"mtime":1768155639000,"hash":"77","data":"78"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/.cache/.prettiercache",{"size":4787,"mtime":1768171236840,"hash":"79"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/eas.json",{"size":567,"mtime":1766222924000,"hash":"80","data":"81"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/.expo-shared/assets.json",{"size":155,"mtime":1766222924000,"hash":"82","data":"83"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/_layout.tsx",{"size":927,"mtime":1768155639000,"hash":"84","data":"85"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/nativewind-env.d.ts",{"size":246,"mtime":1766222924000,"hash":"86","data":"87"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/auth.ts",{"size":398,"mtime":1768155639000,"hash":"88","data":"89"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/post/[id].tsx",{"size":757,"mtime":1768155639000,"hash":"90","data":"91"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/styles.css",{"size":90,"mtime":1768155639000,"hash":"92","data":"93"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/session-store.ts",{"size":272,"mtime":1768155639000,"hash":"94","data":"95"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/assets/icon-dark.png",{"size":19633,"mtime":1766222924000,"hash":"96"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/eslint.config.mts",{"size":275,"mtime":1768155639000,"hash":"97","data":"98"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/api.tsx",{"size":1326,"mtime":1768155639000,"hash":"99","data":"100"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/base-url.ts",{"size":880,"mtime":1768155639000,"hash":"101","data":"102"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/app.config.ts",{"size":1333,"mtime":1768155639000,"hash":"103","data":"104"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/assets/icon-light.png",{"size":19133,"mtime":1766222924000,"hash":"105"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/tsconfig.json",{"size":387,"mtime":1766228480000,"hash":"106","data":"107"},"d8763702c14cdc382dcfb84f6f9a068f",{"hashOfOptions":"108"},"11cdbef6afa001cd39bc187041ca6865",{"hashOfOptions":"109"},"dbe97bcde588a81538bbcd6a9befdddd",{"hashOfOptions":"110"},"1c10eb388cf5dcbc87d2d63770a227f1",{"hashOfOptions":"111"},"c7d4dcf839dfeaa02e0407adfd5e47a6",{"hashOfOptions":"112"},"b7edffce093c4c84092cc93f3dc208ef",{"hashOfOptions":"113"},"75e4e9158c89e7a7bc04e94fd2366743","a3c1487f8318513ae7c156acc857fde2",{"hashOfOptions":"114"},"0f7f54c7161b8403d3bc42d91f59cd91",{"hashOfOptions":"115"},"8e407b4b1b0c0bd9c862a00243344be3",{"hashOfOptions":"116"},"d4d589c153ac8b5e7bf0fb130a5b5a7d",{"hashOfOptions":"117"},"cecbed1604a530a7cc099fecddddd76c",{"hashOfOptions":"118"},"4fcefde979d34a7339f7a266d3ec931b",{"hashOfOptions":"119"},"52a1d72379b952dd802f47e1865bd0da",{"hashOfOptions":"120"},"1bc3e15a40c117eecc51294886ea9b38",{"hashOfOptions":"121"},"1e8ac0d261e95efb19d290ffcf70ce36","1c1710ce3de3ce02e8054cc3787c8579",{"hashOfOptions":"122"},"5ff899a601102659dcbd2900e415ce8b",{"hashOfOptions":"123"},"dd2007a211e323deabb3f7fa7d16313f",{"hashOfOptions":"124"},"4f49c6df7733f874fbe72b4e20b3092b",{"hashOfOptions":"125"},"863da15dbd856008b7c24077ca746d91","6937fb7370f1e17491df649888d6ecc9",{"hashOfOptions":"126"},"2742538293","3367041120","3963086909","3173464737","3850474653","3346849767","2045418596","3048291867","3525769784","4000169781","471581913","1427279653","2305419495","818733105","2587128410","1262173017","408469422","2836740315","2315460658"] [["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21"],{"key":"22","value":"23"},{"key":"24","value":"25"},{"key":"26","value":"27"},{"key":"28","value":"29"},{"key":"30","value":"31"},{"key":"32","value":"33"},{"key":"34","value":"35"},{"key":"36","value":"37"},{"key":"38","value":"39"},{"key":"40","value":"41"},{"key":"42","value":"43"},{"key":"44","value":"45"},{"key":"46","value":"47"},{"key":"48","value":"49"},{"key":"50","value":"51"},{"key":"52","value":"53"},{"key":"54","value":"55"},{"key":"56","value":"57"},{"key":"58","value":"59"},{"key":"60","value":"61"},{"key":"62","value":"63"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/package.json",{"size":2249,"mtime":1766222924000,"hash":"64","data":"65"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/index.ts",{"size":28,"mtime":1768155639000,"hash":"66","data":"67"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/metro.config.js",{"size":511,"mtime":1768155639000,"hash":"68","data":"69"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/index.tsx",{"size":5019,"mtime":1768372346938,"hash":"70","data":"71"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/turbo.json",{"size":163,"mtime":1766222924000,"hash":"72","data":"73"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/postcss.config.js",{"size":66,"mtime":1768155639000,"hash":"74","data":"75"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/eas.json",{"size":567,"mtime":1766222924000,"hash":"76","data":"77"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/.expo-shared/assets.json",{"size":155,"mtime":1766222924000,"hash":"78","data":"79"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/_layout.tsx",{"size":927,"mtime":1768155639000,"hash":"80","data":"81"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/nativewind-env.d.ts",{"size":246,"mtime":1766222924000,"hash":"82","data":"83"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/auth.ts",{"size":398,"mtime":1768155639000,"hash":"84","data":"85"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/post/[id].tsx",{"size":757,"mtime":1768372346967,"hash":"86","data":"87"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/styles.css",{"size":90,"mtime":1768155639000,"hash":"88","data":"89"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/session-store.ts",{"size":272,"mtime":1768155639000,"hash":"90","data":"91"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/assets/icon-dark.png",{"size":19633,"mtime":1766222924000,"hash":"92"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/eslint.config.mts",{"size":275,"mtime":1768155639000,"hash":"93","data":"94"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/api.tsx",{"size":1326,"mtime":1768155639000,"hash":"95","data":"96"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/base-url.ts",{"size":880,"mtime":1768155639000,"hash":"97","data":"98"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/app.config.ts",{"size":1333,"mtime":1768155639000,"hash":"99","data":"100"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/assets/icon-light.png",{"size":19133,"mtime":1766222924000,"hash":"101"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/tsconfig.json",{"size":387,"mtime":1766228480000,"hash":"102","data":"103"},"d8763702c14cdc382dcfb84f6f9a068f",{"hashOfOptions":"104"},"11cdbef6afa001cd39bc187041ca6865",{"hashOfOptions":"105"},"dbe97bcde588a81538bbcd6a9befdddd",{"hashOfOptions":"106"},"73c235a66242df70b69394cce29d1ed3",{"hashOfOptions":"107"},"c7d4dcf839dfeaa02e0407adfd5e47a6",{"hashOfOptions":"108"},"b7edffce093c4c84092cc93f3dc208ef",{"hashOfOptions":"109"},"a3c1487f8318513ae7c156acc857fde2",{"hashOfOptions":"110"},"0f7f54c7161b8403d3bc42d91f59cd91",{"hashOfOptions":"111"},"8e407b4b1b0c0bd9c862a00243344be3",{"hashOfOptions":"112"},"d4d589c153ac8b5e7bf0fb130a5b5a7d",{"hashOfOptions":"113"},"cecbed1604a530a7cc099fecddddd76c",{"hashOfOptions":"114"},"ead19d73283f9d8e08b55c896c9fd570",{"hashOfOptions":"115"},"52a1d72379b952dd802f47e1865bd0da",{"hashOfOptions":"116"},"1bc3e15a40c117eecc51294886ea9b38",{"hashOfOptions":"117"},"1e8ac0d261e95efb19d290ffcf70ce36","1c1710ce3de3ce02e8054cc3787c8579",{"hashOfOptions":"118"},"5ff899a601102659dcbd2900e415ce8b",{"hashOfOptions":"119"},"dd2007a211e323deabb3f7fa7d16313f",{"hashOfOptions":"120"},"4f49c6df7733f874fbe72b4e20b3092b",{"hashOfOptions":"121"},"863da15dbd856008b7c24077ca746d91","6937fb7370f1e17491df649888d6ecc9",{"hashOfOptions":"122"},"1820601142","1684748001","3531839294","2748941218","956511134","132171752","3925902565","1550174236","2506462393","526883382","4111358426","2953691686","2383171816","2740949298","3787272667","3740930138","4230803759","3315245788","3164486579"]

View File

@@ -14,8 +14,8 @@ function PostCard(props: {
onDelete: () => void; onDelete: () => void;
}) { }) {
return ( return (
<View className="bg-muted flex flex-row rounded-lg p-4"> <View className='bg-muted flex flex-row rounded-lg p-4'>
<View className="grow"> <View className='grow'>
<Link <Link
asChild asChild
href={{ href={{
@@ -23,16 +23,16 @@ function PostCard(props: {
params: { id: props.post.id }, params: { id: props.post.id },
}} }}
> >
<Pressable className=""> <Pressable className=''>
<Text className="text-primary text-xl font-semibold"> <Text className='text-primary text-xl font-semibold'>
{props.post.title} {props.post.title}
</Text> </Text>
<Text className="text-foreground mt-2">{props.post.content}</Text> <Text className='text-foreground mt-2'>{props.post.content}</Text>
</Pressable> </Pressable>
</Link> </Link>
</View> </View>
<Pressable onPress={props.onDelete}> <Pressable onPress={props.onDelete}>
<Text className="text-primary font-bold uppercase">Delete</Text> <Text className='text-primary font-bold uppercase'>Delete</Text>
</Pressable> </Pressable>
</View> </View>
); );
@@ -55,31 +55,31 @@ function CreatePost() {
); );
return ( return (
<View className="mt-4 flex gap-2"> <View className='mt-4 flex gap-2'>
<TextInput <TextInput
className="border-input bg-background text-foreground items-center rounded-md border px-3 text-lg leading-tight" className='border-input bg-background text-foreground items-center rounded-md border px-3 text-lg leading-tight'
value={title} value={title}
onChangeText={setTitle} onChangeText={setTitle}
placeholder="Title" placeholder='Title'
/> />
{error?.data?.zodError?.fieldErrors.title && ( {error?.data?.zodError?.fieldErrors.title && (
<Text className="text-destructive mb-2"> <Text className='text-destructive mb-2'>
{error.data.zodError.fieldErrors.title} {error.data.zodError.fieldErrors.title}
</Text> </Text>
)} )}
<TextInput <TextInput
className="border-input bg-background text-foreground items-center rounded-md border px-3 text-lg leading-tight" className='border-input bg-background text-foreground items-center rounded-md border px-3 text-lg leading-tight'
value={content} value={content}
onChangeText={setContent} onChangeText={setContent}
placeholder="Content" placeholder='Content'
/> />
{error?.data?.zodError?.fieldErrors.content && ( {error?.data?.zodError?.fieldErrors.content && (
<Text className="text-destructive mb-2"> <Text className='text-destructive mb-2'>
{error.data.zodError.fieldErrors.content} {error.data.zodError.fieldErrors.content}
</Text> </Text>
)} )}
<Pressable <Pressable
className="bg-primary flex items-center rounded-sm p-2" className='bg-primary flex items-center rounded-sm p-2'
onPress={() => { onPress={() => {
mutate({ mutate({
title, title,
@@ -87,10 +87,10 @@ function CreatePost() {
}); });
}} }}
> >
<Text className="text-foreground">Create</Text> <Text className='text-foreground'>Create</Text>
</Pressable> </Pressable>
{error?.data?.code === 'UNAUTHORIZED' && ( {error?.data?.code === 'UNAUTHORIZED' && (
<Text className="text-destructive mt-2"> <Text className='text-destructive mt-2'>
You need to be logged in to create a post You need to be logged in to create a post
</Text> </Text>
)} )}
@@ -103,7 +103,7 @@ function MobileAuth() {
return ( return (
<> <>
<Text className="text-foreground pb-2 text-center text-xl font-semibold"> <Text className='text-foreground pb-2 text-center text-xl font-semibold'>
{session?.user.name ? `Hello, ${session.user.name}` : 'Not logged in'} {session?.user.name ? `Hello, ${session.user.name}` : 'Not logged in'}
</Text> </Text>
<Pressable <Pressable
@@ -115,7 +115,7 @@ function MobileAuth() {
callbackURL: '/', callbackURL: '/',
}) })
} }
className="bg-primary flex items-center rounded-sm p-2" className='bg-primary flex items-center rounded-sm p-2'
> >
<Text>{session ? 'Sign Out' : 'Sign In With Discord'}</Text> <Text>{session ? 'Sign Out' : 'Sign In With Discord'}</Text>
</Pressable> </Pressable>
@@ -136,18 +136,18 @@ export default function Index() {
); );
return ( return (
<SafeAreaView className="bg-background"> <SafeAreaView className='bg-background'>
{/* Changes page title visible on the header */} {/* Changes page title visible on the header */}
<Stack.Screen options={{ title: 'Home Page' }} /> <Stack.Screen options={{ title: 'Home Page' }} />
<View className="bg-background h-full w-full p-4"> <View className='bg-background h-full w-full p-4'>
<Text className="text-foreground pb-2 text-center text-5xl font-bold"> <Text className='text-foreground pb-2 text-center text-5xl font-bold'>
Create <Text className="text-primary">T3</Text> Turbo Create <Text className='text-primary'>T3</Text> Turbo
</Text> </Text>
<MobileAuth /> <MobileAuth />
<View className="py-2"> <View className='py-2'>
<Text className="text-primary font-semibold italic"> <Text className='text-primary font-semibold italic'>
Press on a post Press on a post
</Text> </Text>
</View> </View>
@@ -156,7 +156,7 @@ export default function Index() {
data={postQuery.data ?? []} data={postQuery.data ?? []}
estimatedItemSize={20} estimatedItemSize={20}
keyExtractor={(item) => item.id} keyExtractor={(item) => item.id}
ItemSeparatorComponent={() => <View className="h-2" />} ItemSeparatorComponent={() => <View className='h-2' />}
renderItem={(p) => ( renderItem={(p) => (
<PostCard <PostCard
post={p.item} post={p.item}

View File

@@ -11,13 +11,13 @@ export default function Post() {
if (!data) return null; if (!data) return null;
return ( return (
<SafeAreaView className="bg-background"> <SafeAreaView className='bg-background'>
<Stack.Screen options={{ title: data.title }} /> <Stack.Screen options={{ title: data.title }} />
<View className="h-full w-full p-4"> <View className='h-full w-full p-4'>
<Text className="text-primary py-2 text-3xl font-bold"> <Text className='text-primary py-2 text-3xl font-bold'>
{data.title} {data.title}
</Text> </Text>
<Text className="text-foreground py-4">{data.content}</Text> <Text className='text-foreground py-4'>{data.content}</Text>
</View> </View>
</SafeAreaView> </SafeAreaView>
); );

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -132,12 +132,12 @@ const ForgotPassword = () => {
}; };
return ( return (
<div className="flex flex-col items-center"> <div className='flex flex-col items-center'>
<Card className="bg-card/25 min-h-[400px] w-sm p-4 lg:w-md"> <Card className='bg-card/25 min-h-[400px] w-sm p-4 lg:w-md'>
<CardHeader className="flex flex-col items-center gap-4"> <CardHeader className='flex flex-col items-center gap-4'>
{flow === 'reset' ? ( {flow === 'reset' ? (
<> <>
<CardTitle className="text-2xl font-bold"> <CardTitle className='text-2xl font-bold'>
Forgot Password Forgot Password
</CardTitle> </CardTitle>
<CardDescription> <CardDescription>
@@ -147,7 +147,7 @@ const ForgotPassword = () => {
</> </>
) : ( ) : (
<> <>
<CardTitle className="text-2xl font-bold"> <CardTitle className='text-2xl font-bold'>
Reset Password Reset Password
</CardTitle> </CardTitle>
<CardDescription> <CardDescription>
@@ -158,7 +158,7 @@ const ForgotPassword = () => {
)} )}
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<Card className="bg-card/50"> <Card className='bg-card/50'>
<CardContent> <CardContent>
{flow === 'reset' ? ( {flow === 'reset' ? (
<Form {...forgotPasswordForm}> <Form {...forgotPasswordForm}>
@@ -166,31 +166,31 @@ const ForgotPassword = () => {
onSubmit={forgotPasswordForm.handleSubmit( onSubmit={forgotPasswordForm.handleSubmit(
handleForgotPasswordSubmit, handleForgotPasswordSubmit,
)} )}
className="flex flex-col space-y-4" className='flex flex-col space-y-4'
> >
<FormField <FormField
control={forgotPasswordForm.control} control={forgotPasswordForm.control}
name="email" name='email'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-xl">Email</FormLabel> <FormLabel className='text-xl'>Email</FormLabel>
<FormControl> <FormControl>
<Input <Input
type="email" type='email'
placeholder="you@example.com" placeholder='you@example.com'
{...field} {...field}
/> />
</FormControl> </FormControl>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<SubmitButton <SubmitButton
disabled={loading} disabled={loading}
pendingText="Sending Email..." pendingText='Sending Email...'
className="mx-auto w-2/3 text-xl font-semibold" className='mx-auto w-2/3 text-xl font-semibold'
> >
Send Email Send Email
</SubmitButton> </SubmitButton>
@@ -202,14 +202,14 @@ const ForgotPassword = () => {
onSubmit={resetVerificationForm.handleSubmit( onSubmit={resetVerificationForm.handleSubmit(
handleResetVerificationSubmit, handleResetVerificationSubmit,
)} )}
className="flex flex-col space-y-4" className='flex flex-col space-y-4'
> >
<FormField <FormField
control={resetVerificationForm.control} control={resetVerificationForm.control}
name="code" name='code'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-xl">Code</FormLabel> <FormLabel className='text-xl'>Code</FormLabel>
<FormControl> <FormControl>
<InputOTP <InputOTP
maxLength={6} maxLength={6}
@@ -232,58 +232,58 @@ const ForgotPassword = () => {
Please enter the one-time password sent to your Please enter the one-time password sent to your
phone. phone.
</FormDescription> </FormDescription>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={resetVerificationForm.control} control={resetVerificationForm.control}
name="newPassword" name='newPassword'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-xl"> <FormLabel className='text-xl'>
New Password New Password
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
type="password" type='password'
placeholder="Your password" placeholder='Your password'
{...field} {...field}
/> />
</FormControl> </FormControl>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={resetVerificationForm.control} control={resetVerificationForm.control}
name="confirmPassword" name='confirmPassword'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-xl"> <FormLabel className='text-xl'>
Confirm Passsword Confirm Passsword
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
type="password" type='password'
placeholder="Confirm your password" placeholder='Confirm your password'
{...field} {...field}
/> />
</FormControl> </FormControl>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<SubmitButton <SubmitButton
disabled={loading} disabled={loading}
pendingText="Resetting Password..." pendingText='Resetting Password...'
className="mx-auto w-2/3 text-xl font-semibold" className='mx-auto w-2/3 text-xl font-semibold'
> >
Reset Password Reset Password
</SubmitButton> </SubmitButton>

View File

@@ -14,28 +14,34 @@ import { Card, Separator } from '@gib/ui';
const Profile = async () => { const Profile = async () => {
const preloadedUser = await preloadQuery(api.auth.getUser, {}); const preloadedUser = await preloadQuery(api.auth.getUser, {});
const preloadedUserProvider = await preloadQuery(api.auth.getUserProvider, {}); const preloadedUserProvider = await preloadQuery(
api.auth.getUserProvider,
{},
);
return ( return (
<main className="container mx-auto px-4 py-12 md:py-16"> <main className='container mx-auto px-4 py-12 md:py-16'>
<div className="mx-auto max-w-3xl"> <div className='mx-auto max-w-3xl'>
{/* Page Header */} {/* Page Header */}
<div className="mb-8 text-center"> <div className='mb-8 text-center'>
<h1 className="mb-2 text-3xl font-bold tracking-tight sm:text-4xl"> <h1 className='mb-2 text-3xl font-bold tracking-tight sm:text-4xl'>
Your Profile Your Profile
</h1> </h1>
<p className="text-muted-foreground"> <p className='text-muted-foreground'>
Manage your personal information and preferences Manage your personal information and preferences
</p> </p>
</div> </div>
{/* Profile Card */} {/* Profile Card */}
<Card className="border-border/40"> <Card className='border-border/40'>
<ProfileHeader preloadedUser={preloadedUser} /> <ProfileHeader preloadedUser={preloadedUser} />
<AvatarUpload preloadedUser={preloadedUser} /> <AvatarUpload preloadedUser={preloadedUser} />
<Separator className="my-6" /> <Separator className='my-6' />
<UserInfoForm preloadedUser={preloadedUser} preloadedProvider={preloadedUserProvider} /> <UserInfoForm
preloadedUser={preloadedUser}
preloadedProvider={preloadedUserProvider}
/>
<ResetPasswordForm preloadedProvider={preloadedUserProvider} /> <ResetPasswordForm preloadedProvider={preloadedUserProvider} />
<Separator className="my-6" /> <Separator className='my-6' />
<SignOutForm /> <SignOutForm />
</Card> </Card>
</div> </div>

View File

@@ -179,24 +179,24 @@ const SignIn = () => {
if (flow === 'email-verification') { if (flow === 'email-verification') {
return ( return (
<div className="flex flex-col items-center"> <div className='flex flex-col items-center'>
<Card className="bg-card/25 min-h-[720px] w-md p-4"> <Card className='bg-card/25 min-h-[720px] w-md p-4'>
<CardContent> <CardContent>
<div className="mb-6 text-center"> <div className='mb-6 text-center'>
<h2 className="text-2xl font-bold">Verify Your Email</h2> <h2 className='text-2xl font-bold'>Verify Your Email</h2>
<p className="text-muted-foreground">We sent a code to {email}</p> <p className='text-muted-foreground'>We sent a code to {email}</p>
</div> </div>
<Form {...verifyEmailForm}> <Form {...verifyEmailForm}>
<form <form
onSubmit={verifyEmailForm.handleSubmit(handleVerifyEmail)} onSubmit={verifyEmailForm.handleSubmit(handleVerifyEmail)}
className="flex flex-col space-y-8" className='flex flex-col space-y-8'
> >
<FormField <FormField
control={verifyEmailForm.control} control={verifyEmailForm.control}
name="code" name='code'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-xl">Code</FormLabel> <FormLabel className='text-xl'>Code</FormLabel>
<FormControl> <FormControl>
<InputOTP <InputOTP
maxLength={6} maxLength={6}
@@ -217,25 +217,25 @@ const SignIn = () => {
<FormDescription> <FormDescription>
Please enter the one-time password sent to your email. Please enter the one-time password sent to your email.
</FormDescription> </FormDescription>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<SubmitButton <SubmitButton
disabled={loading} disabled={loading}
pendingText="Signing Up..." pendingText='Signing Up...'
className="mx-auto w-2/3 text-xl font-semibold" className='mx-auto w-2/3 text-xl font-semibold'
> >
Verify Email Verify Email
</SubmitButton> </SubmitButton>
</form> </form>
</Form> </Form>
<div className="mt-4 text-center"> <div className='mt-4 text-center'>
<button <button
onClick={() => setFlow('signUp')} onClick={() => setFlow('signUp')}
className="text-muted-foreground text-sm hover:underline" className='text-muted-foreground text-sm hover:underline'
> >
Back to Sign Up Back to Sign Up
</button> </button>
@@ -247,204 +247,204 @@ const SignIn = () => {
} }
return ( return (
<div className="flex flex-col items-center"> <div className='flex flex-col items-center'>
<Card className="bg-card/25 min-h-[720px] w-md p-4"> <Card className='bg-card/25 min-h-[720px] w-md p-4'>
<Tabs <Tabs
defaultValue={flow} defaultValue={flow}
onValueChange={(value) => setFlow(value as 'signIn' | 'signUp')} onValueChange={(value) => setFlow(value as 'signIn' | 'signUp')}
className="items-center" className='items-center'
> >
<TabsList className="py-6"> <TabsList className='py-6'>
<TabsTrigger <TabsTrigger
value="signIn" value='signIn'
className="cursor-pointer p-6 text-2xl font-bold" className='cursor-pointer p-6 text-2xl font-bold'
> >
Sign In Sign In
</TabsTrigger> </TabsTrigger>
<TabsTrigger <TabsTrigger
value="signUp" value='signUp'
className="cursor-pointer p-6 text-2xl font-bold" className='cursor-pointer p-6 text-2xl font-bold'
> >
Sign Up Sign Up
</TabsTrigger> </TabsTrigger>
</TabsList> </TabsList>
<TabsContent value="signIn"> <TabsContent value='signIn'>
<Card className="bg-card/50 min-w-xs sm:min-w-sm"> <Card className='bg-card/50 min-w-xs sm:min-w-sm'>
<CardContent> <CardContent>
<Form {...signInForm}> <Form {...signInForm}>
<form <form
onSubmit={signInForm.handleSubmit(handleSignIn)} onSubmit={signInForm.handleSubmit(handleSignIn)}
className="flex flex-col space-y-8" className='flex flex-col space-y-8'
> >
<FormField <FormField
control={signInForm.control} control={signInForm.control}
name="email" name='email'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-xl">Email</FormLabel> <FormLabel className='text-xl'>Email</FormLabel>
<FormControl> <FormControl>
<Input <Input
type="email" type='email'
placeholder="you@example.com" placeholder='you@example.com'
{...field} {...field}
/> />
</FormControl> </FormControl>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={signInForm.control} control={signInForm.control}
name="password" name='password'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<div className="flex justify-between"> <div className='flex justify-between'>
<FormLabel className="text-xl">Password</FormLabel> <FormLabel className='text-xl'>Password</FormLabel>
<Link href="/forgot-password"> <Link href='/forgot-password'>
Forgot Password? Forgot Password?
</Link> </Link>
</div> </div>
<FormControl> <FormControl>
<Input <Input
type="password" type='password'
placeholder="Your password" placeholder='Your password'
{...field} {...field}
/> />
</FormControl> </FormControl>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<SubmitButton <SubmitButton
disabled={loading} disabled={loading}
pendingText="Signing in..." pendingText='Signing in...'
className="mx-auto w-2/3 text-xl font-semibold" className='mx-auto w-2/3 text-xl font-semibold'
> >
Sign In Sign In
</SubmitButton> </SubmitButton>
</form> </form>
</Form> </Form>
<div className="flex justify-center"> <div className='flex justify-center'>
<div className="mx-auto my-2.5 flex w-1/4 flex-row items-center justify-center"> <div className='mx-auto my-2.5 flex w-1/4 flex-row items-center justify-center'>
<Separator className="mr-3 py-0.5" /> <Separator className='mr-3 py-0.5' />
<span className="text-lg font-semibold">or</span> <span className='text-lg font-semibold'>or</span>
<Separator className="ml-3 py-0.5" /> <Separator className='ml-3 py-0.5' />
</div> </div>
</div> </div>
<div className="mt-3 flex justify-center"> <div className='mt-3 flex justify-center'>
<GibsAuthSignInButton /> <GibsAuthSignInButton />
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
</TabsContent> </TabsContent>
<TabsContent value="signUp"> <TabsContent value='signUp'>
<Card className="bg-card/50 min-w-xs sm:min-w-sm"> <Card className='bg-card/50 min-w-xs sm:min-w-sm'>
<CardContent> <CardContent>
<Form {...signUpForm}> <Form {...signUpForm}>
<form <form
onSubmit={signUpForm.handleSubmit(handleSignUp)} onSubmit={signUpForm.handleSubmit(handleSignUp)}
className="flex flex-col space-y-8" className='flex flex-col space-y-8'
> >
<FormField <FormField
control={signUpForm.control} control={signUpForm.control}
name="name" name='name'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-xl">Name</FormLabel> <FormLabel className='text-xl'>Name</FormLabel>
<FormControl> <FormControl>
<Input <Input
type="text" type='text'
placeholder="Full Name" placeholder='Full Name'
{...field} {...field}
/> />
</FormControl> </FormControl>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={signUpForm.control} control={signUpForm.control}
name="email" name='email'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-xl">Email</FormLabel> <FormLabel className='text-xl'>Email</FormLabel>
<FormControl> <FormControl>
<Input <Input
type="email" type='email'
placeholder="you@example.com" placeholder='you@example.com'
{...field} {...field}
/> />
</FormControl> </FormControl>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={signUpForm.control} control={signUpForm.control}
name="password" name='password'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-xl">Password</FormLabel> <FormLabel className='text-xl'>Password</FormLabel>
<FormControl> <FormControl>
<Input <Input
type="password" type='password'
placeholder="Your password" placeholder='Your password'
{...field} {...field}
/> />
</FormControl> </FormControl>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={signUpForm.control} control={signUpForm.control}
name="confirmPassword" name='confirmPassword'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-xl"> <FormLabel className='text-xl'>
Confirm Passsword Confirm Passsword
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
type="password" type='password'
placeholder="Confirm your password" placeholder='Confirm your password'
{...field} {...field}
/> />
</FormControl> </FormControl>
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
<FormMessage className="w-5/6 text-center" /> <FormMessage className='w-5/6 text-center' />
</div> </div>
</FormItem> </FormItem>
)} )}
/> />
<SubmitButton <SubmitButton
disabled={loading} disabled={loading}
pendingText="Signing Up..." pendingText='Signing Up...'
className="mx-auto w-2/3 text-xl font-semibold" className='mx-auto w-2/3 text-xl font-semibold'
> >
Sign Up Sign Up
</SubmitButton> </SubmitButton>
</form> </form>
</Form> </Form>
<div className="my-auto flex w-2/3 justify-center"> <div className='my-auto flex w-2/3 justify-center'>
<div className="my-2.5 flex w-1/3 flex-row items-center"> <div className='my-2.5 flex w-1/3 flex-row items-center'>
<Separator className="mr-3 py-0.5" /> <Separator className='mr-3 py-0.5' />
<span className="text-lg font-semibold">or</span> <span className='text-lg font-semibold'>or</span>
<Separator className="ml-3 py-0.5" /> <Separator className='ml-3 py-0.5' />
</div> </div>
</div> </div>
<div className="mt-3 flex justify-center"> <div className='mt-3 flex justify-center'>
<GibsAuthSignInButton type="signUp" /> <GibsAuthSignInButton type='signUp' />
</div> </div>
</CardContent> </CardContent>
</Card> </Card>

View File

@@ -3,13 +3,15 @@
import type { Metadata, Viewport } from 'next'; import type { Metadata, Viewport } from 'next';
import NextError from 'next/error'; import NextError from 'next/error';
import { Geist, Geist_Mono } from 'next/font/google'; import { Geist, Geist_Mono } from 'next/font/google';
import '@/app/styles.css'; import '@/app/styles.css';
import { useEffect } from 'react'; import { useEffect } from 'react';
import Footer from '@/components/layout/footer'; import Footer from '@/components/layout/footer';
import Header from '@/components/layout/header'; import Header from '@/components/layout/header';
import * as Sentry from '@sentry/nextjs';
import { ConvexClientProvider } from '@/components/providers'; import { ConvexClientProvider } from '@/components/providers';
import { generateMetadata } from '@/lib/metadata'; import { generateMetadata } from '@/lib/metadata';
import * as Sentry from '@sentry/nextjs';
import PlausibleProvider from 'next-plausible'; import PlausibleProvider from 'next-plausible';
import { Button, ThemeProvider, Toaster } from '@gib/ui'; import { Button, ThemeProvider, Toaster } from '@gib/ui';
@@ -35,7 +37,7 @@ const geistMono = Geist_Mono({
interface GlobalErrorProps { interface GlobalErrorProps {
error: Error & { digest?: string }; error: Error & { digest?: string };
reset?: () => void; reset?: () => void;
}; }
const GlobalError = ({ error, reset = undefined }: GlobalErrorProps) => { const GlobalError = ({ error, reset = undefined }: GlobalErrorProps) => {
useEffect(() => { useEffect(() => {
@@ -43,21 +45,21 @@ const GlobalError = ({ error, reset = undefined }: GlobalErrorProps) => {
}, [error]); }, [error]);
return ( return (
<PlausibleProvider <PlausibleProvider
domain="convexmonorepo.gbrown.org" domain='convexmonorepo.gbrown.org'
customDomain="https://plausible.gbrown.org" customDomain='https://plausible.gbrown.org'
> >
<html lang="en" suppressHydrationWarning> <html lang='en' suppressHydrationWarning>
<body <body
className={`${geistSans.variable} ${geistMono.variable} antialiased`} className={`${geistSans.variable} ${geistMono.variable} antialiased`}
> >
<ThemeProvider <ThemeProvider
attribute="class" attribute='class'
defaultTheme="system" defaultTheme='system'
enableSystem enableSystem
disableTransitionOnChange disableTransitionOnChange
> >
<ConvexClientProvider> <ConvexClientProvider>
<main className="flex min-h-screen flex-col items-center"> <main className='flex min-h-screen flex-col items-center'>
<Header /> <Header />
<NextError statusCode={0} /> <NextError statusCode={0} />
{reset !== undefined && ( {reset !== undefined && (
@@ -66,7 +68,7 @@ const GlobalError = ({ error, reset = undefined }: GlobalErrorProps) => {
<Toaster /> <Toaster />
<Footer /> <Footer />
</main> </main>
<main className="flex min-h-[90vh] flex-col items-center"> <main className='flex min-h-[90vh] flex-col items-center'>
<Toaster /> <Toaster />
</main> </main>
</ConvexClientProvider> </ConvexClientProvider>

View File

@@ -1,5 +1,6 @@
import type { Metadata, Viewport } from 'next'; import type { Metadata, Viewport } from 'next';
import { Geist, Geist_Mono } from 'next/font/google'; import { Geist, Geist_Mono } from 'next/font/google';
import { env } from '@/env';
import '@/app/styles.css'; import '@/app/styles.css';
@@ -38,23 +39,23 @@ const RootLayout = ({
return ( return (
<ConvexAuthNextjsServerProvider> <ConvexAuthNextjsServerProvider>
<PlausibleProvider <PlausibleProvider
domain="convexmonorepo.gbrown.org" domain={env.NEXT_PUBLIC_SITE_URL}
customDomain="https://plausible.gbrown.org" customDomain={env.NEXT_PUBLIC_PLAUSIBLE_URL}
> >
<html lang="en"> <html lang='en'>
<body <body
className={`${geistSans.variable} ${geistMono.variable} antialiased`} className={`${geistSans.variable} ${geistMono.variable} antialiased`}
> >
<ThemeProvider <ThemeProvider
attribute="class" attribute='class'
defaultTheme="system" defaultTheme='system'
enableSystem enableSystem
disableTransitionOnChange disableTransitionOnChange
> >
<ConvexClientProvider> <ConvexClientProvider>
<div className="flex min-h-screen flex-col"> <div className='flex min-h-screen flex-col'>
<Header /> <Header />
<div className="flex-1">{children}</div> {children}
<Footer /> <Footer />
</div> </div>
<Toaster /> <Toaster />

View File

@@ -1,14 +1,12 @@
import { CTA, Features, Hero, TechStack } from '@/components/landing'; import { CTA, Features, Hero, TechStack } from '@/components/landing';
const Home = async () => { export default function Home() {
return ( return (
<main className="flex min-h-screen flex-col"> <main className='flex min-h-screen flex-col'>
<Hero /> <Hero />
<Features /> <Features />
<TechStack /> <TechStack />
<CTA /> <CTA />
</main> </main>
); );
}; }
export default Home;

View File

@@ -5,24 +5,24 @@ import { Button } from '@gib/ui/button';
export function CTA() { export function CTA() {
return ( return (
<section className="container mx-auto px-4 py-24"> <section className='container mx-auto px-4 py-24'>
<div className="mx-auto max-w-4xl"> <div className='mx-auto max-w-4xl'>
<div className="border-border/40 from-muted/50 to-muted/30 rounded-2xl border bg-gradient-to-br p-8 text-center md:p-12"> <div className='border-border/40 from-muted/50 to-muted/30 rounded-2xl border bg-gradient-to-br p-8 text-center md:p-12'>
<h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl"> <h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
Ready to Build Something Amazing? Ready to Build Something Amazing?
</h2> </h2>
<p className="text-muted-foreground mb-8 text-lg"> <p className='text-muted-foreground mb-8 text-lg'>
Clone the repository and start building your next project with Clone the repository and start building your next project with
everything pre-configured. everything pre-configured.
</p> </p>
{/* Quick Start Command */} {/* Quick Start Command */}
<div className="mt-12"> <div className='mt-12'>
<p className="text-muted-foreground mb-3 text-sm font-medium"> <p className='text-muted-foreground mb-3 text-sm font-medium'>
Quick Start Quick Start
</p> </p>
<div className="border-border/40 bg-background mx-auto max-w-2xl rounded-lg border p-4"> <div className='border-border/40 bg-background mx-auto max-w-2xl rounded-lg border p-4'>
<code className="text-sm"> <code className='text-sm'>
git clone https://git.gbrown.org/gib/convex-monorepo.git git clone https://git.gbrown.org/gib/convex-monorepo.git
<br /> <br />
cd convex-monorepo cd convex-monorepo

View File

@@ -59,29 +59,29 @@ const features = [
export function Features() { export function Features() {
return ( return (
<section id="features" className="container mx-auto px-4 py-24"> <section id='features' className='container mx-auto px-4 py-24'>
<div className="mx-auto max-w-6xl"> <div className='mx-auto max-w-6xl'>
{/* Section Header */} {/* Section Header */}
<div className="mb-16 text-center"> <div className='mb-16 text-center'>
<h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl"> <h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl'>
Everything You Need to Ship Fast Everything You Need to Ship Fast
</h2> </h2>
<p className="text-muted-foreground mx-auto max-w-2xl text-lg"> <p className='text-muted-foreground mx-auto max-w-2xl text-lg'>
A complete monorepo template with all the tools and patterns you A complete monorepo template with all the tools and patterns you
need for production-ready applications. need for production-ready applications.
</p> </p>
</div> </div>
{/* Features Grid */} {/* Features Grid */}
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3"> <div className='grid gap-6 md:grid-cols-2 lg:grid-cols-3'>
{features.map((feature) => ( {features.map((feature) => (
<Card key={feature.title} className="border-border/40"> <Card key={feature.title} className='border-border/40'>
<CardHeader className='flex items-center gap-2'> <CardHeader className='flex items-center gap-2'>
<div className="mb-2 text-3xl">{feature.icon}</div> <div className='mb-2 text-3xl'>{feature.icon}</div>
<CardTitle className="text-xl">{feature.title}</CardTitle> <CardTitle className='text-xl'>{feature.title}</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<p className="text-muted-foreground">{feature.description}</p> <p className='text-muted-foreground'>{feature.description}</p>
</CardContent> </CardContent>
</Card> </Card>
))} ))}

View File

@@ -1,7 +1,8 @@
import Link from 'next/link';
import Image from 'next/image';
import { Button } from '@gib/ui/button';
import { Kanit } from 'next/font/google'; import { Kanit } from 'next/font/google';
import Image from 'next/image';
import Link from 'next/link';
import { Button } from '@gib/ui/button';
const kanitSans = Kanit({ const kanitSans = Kanit({
subsets: ['latin'], subsets: ['latin'],
@@ -10,36 +11,38 @@ const kanitSans = Kanit({
export function Hero() { export function Hero() {
return ( return (
<section className="container mx-auto px-4 py-24 md:py-32 lg:py-40"> <section className='container mx-auto px-4 py-24 md:py-32 lg:py-40'>
<div className="mx-auto flex max-w-5xl flex-col items-center gap-8 text-center"> <div className='mx-auto flex max-w-5xl flex-col items-center gap-8 text-center'>
{/* Badge */} {/* Badge */}
<div className="border-border/40 bg-muted/50 inline-flex items-center rounded-full border px-3 py-1 text-sm font-medium"> <div className='border-border/40 bg-muted/50 inline-flex items-center rounded-full border px-3 py-1 text-sm font-medium'>
<span className="mr-2">🚀</span> <span className='mr-2'>🚀</span>
<span>Production-ready monorepo template</span> <span>Production-ready monorepo template</span>
</div> </div>
{/* Heading */} {/* Heading */}
<h1 className="from-foreground to-foreground/70 bg-linear-to-br bg-clip-text text-4xl font-bold tracking-tight text-transparent sm:text-5xl md:text-6xl lg:text-7xl"> <h1 className='from-foreground to-foreground/70 bg-linear-to-br bg-clip-text text-4xl font-bold tracking-tight text-transparent sm:text-5xl md:text-6xl lg:text-7xl'>
Build Full-Stack Apps with{' '} Build Full-Stack Apps with{' '}
<span className={`${kanitSans.className} to-accent-foreground bg-linear-to-r from-[#281A65] via-[#363354] bg-clip-text text-transparent dark:from-[#bec8e6] dark:via-[#F0EEE4] dark:to-[#FFF8E7] sm:text-6xl md:text-7xl lg:text-8xl`}> <span
className={`${kanitSans.className} to-accent-foreground bg-linear-to-r from-[#281A65] via-[#363354] bg-clip-text text-transparent sm:text-6xl lg:text-7xl xl:text-8xl dark:from-[#bec8e6] dark:via-[#F0EEE4] dark:to-[#FFF8E7]`}
>
convex monorepo convex monorepo
</span> </span>
</h1> </h1>
{/* Description */} {/* Description */}
<p className="text-muted-foreground max-w-2xl text-lg md:text-xl"> <p className='text-muted-foreground max-w-2xl text-lg md:text-xl'>
A Turborepo starter with Next.js, Expo, and self-hosted Convex. Ship A Turborepo starter with Next.js, Expo, and self-hosted Convex. Ship
web and mobile apps faster with shared code, type-safe backend, and web and mobile apps faster with shared code, type-safe backend, and
complete control over your infrastructure. complete control over your infrastructure.
</p> </p>
{/* CTA Buttons */} {/* CTA Buttons */}
<div className="flex flex-col gap-3 sm:flex-row"> <div className='flex flex-col gap-3 sm:flex-row'>
<Button size="lg" variant="outline" asChild> <Button size='lg' variant='outline' asChild>
<Link <Link
href="https://git.gbrown.org/gib/convex-monorepo" href='https://git.gbrown.org/gib/convex-monorepo'
target="_blank" target='_blank'
rel="noopener noreferrer" rel='noopener noreferrer'
> >
<Image <Image
src='/misc/gitea/gitea.svg' src='/misc/gitea/gitea.svg'
@@ -53,67 +56,67 @@ export function Hero() {
</div> </div>
{/* Features Quick List */} {/* Features Quick List */}
<div className="text-muted-foreground mt-8 flex flex-wrap items-center justify-center gap-6 text-sm"> <div className='text-muted-foreground mt-8 flex flex-wrap items-center justify-center gap-6 text-sm'>
<div className="flex items-center gap-2"> <div className='flex items-center gap-2'>
<svg <svg
className="h-5 w-5 text-green-500" className='h-5 w-5 text-green-500'
fill="none" fill='none'
viewBox="0 0 24 24" viewBox='0 0 24 24'
stroke="currentColor" stroke='currentColor'
> >
<path <path
strokeLinecap="round" strokeLinecap='round'
strokeLinejoin="round" strokeLinejoin='round'
strokeWidth={2} strokeWidth={2}
d="M5 13l4 4L19 7" d='M5 13l4 4L19 7'
/> />
</svg> </svg>
<span>TypeScript</span> <span>TypeScript</span>
</div> </div>
<div className="flex items-center gap-2"> <div className='flex items-center gap-2'>
<svg <svg
className="h-5 w-5 text-green-500" className='h-5 w-5 text-green-500'
fill="none" fill='none'
viewBox="0 0 24 24" viewBox='0 0 24 24'
stroke="currentColor" stroke='currentColor'
> >
<path <path
strokeLinecap="round" strokeLinecap='round'
strokeLinejoin="round" strokeLinejoin='round'
strokeWidth={2} strokeWidth={2}
d="M5 13l4 4L19 7" d='M5 13l4 4L19 7'
/> />
</svg> </svg>
<span>Self-Hosted</span> <span>Self-Hosted</span>
</div> </div>
<div className="flex items-center gap-2"> <div className='flex items-center gap-2'>
<svg <svg
className="h-5 w-5 text-green-500" className='h-5 w-5 text-green-500'
fill="none" fill='none'
viewBox="0 0 24 24" viewBox='0 0 24 24'
stroke="currentColor" stroke='currentColor'
> >
<path <path
strokeLinecap="round" strokeLinecap='round'
strokeLinejoin="round" strokeLinejoin='round'
strokeWidth={2} strokeWidth={2}
d="M5 13l4 4L19 7" d='M5 13l4 4L19 7'
/> />
</svg> </svg>
<span>Real-time</span> <span>Real-time</span>
</div> </div>
<div className="flex items-center gap-2"> <div className='flex items-center gap-2'>
<svg <svg
className="h-5 w-5 text-green-500" className='h-5 w-5 text-green-500'
fill="none" fill='none'
viewBox="0 0 24 24" viewBox='0 0 24 24'
stroke="currentColor" stroke='currentColor'
> >
<path <path
strokeLinecap="round" strokeLinecap='round'
strokeLinejoin="round" strokeLinejoin='round'
strokeWidth={2} strokeWidth={2}
d="M5 13l4 4L19 7" d='M5 13l4 4L19 7'
/> />
</svg> </svg>
<span>Auth Included</span> <span>Auth Included</span>

View File

@@ -38,32 +38,32 @@ const techStack = [
export function TechStack() { export function TechStack() {
return ( return (
<section id="tech-stack" className="border-border/40 bg-muted/30 border-t"> <section id='tech-stack' className='border-border/40 bg-muted/30 border-t'>
<div className="container mx-auto px-4 py-24"> <div className='container mx-auto px-4 py-24'>
<div className="mx-auto max-w-6xl"> <div className='mx-auto max-w-6xl'>
{/* Section Header */} {/* Section Header */}
<div className="mb-16 text-center"> <div className='mb-16 text-center'>
<h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl"> <h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl'>
Modern Tech Stack Modern Tech Stack
</h2> </h2>
<p className="text-muted-foreground mx-auto max-w-2xl text-lg"> <p className='text-muted-foreground mx-auto max-w-2xl text-lg'>
Built with the latest and greatest tools for maximum productivity Built with the latest and greatest tools for maximum productivity
and performance. and performance.
</p> </p>
</div> </div>
{/* Tech Stack Grid */} {/* Tech Stack Grid */}
<div className="grid gap-12 md:grid-cols-3"> <div className='grid gap-12 md:grid-cols-3'>
{techStack.map((stack) => ( {techStack.map((stack) => (
<div key={stack.category}> <div key={stack.category}>
<h3 className="mb-6 text-xl font-semibold">{stack.category}</h3> <h3 className='mb-6 text-xl font-semibold'>{stack.category}</h3>
<ul className="space-y-4"> <ul className='space-y-4'>
{stack.technologies.map((tech) => ( {stack.technologies.map((tech) => (
<li key={tech.name}> <li key={tech.name}>
<div className="text-foreground font-medium"> <div className='text-foreground font-medium'>
{tech.name} {tech.name}
</div> </div>
<div className="text-muted-foreground text-sm"> <div className='text-muted-foreground text-sm'>
{tech.description} {tech.description}
</div> </div>
</li> </li>

View File

@@ -21,15 +21,15 @@ export const GibsAuthSignInButton = ({
const { signIn } = useAuthActions(); const { signIn } = useAuthActions();
return ( return (
<Button <Button
size="lg" size='lg'
onClick={() => signIn('authentik')} onClick={() => signIn('authentik')}
className="text-lg font-semibold" className='text-lg font-semibold'
{...buttonProps} {...buttonProps}
> >
<div className="my-auto flex flex-row space-x-1"> <div className='my-auto flex flex-row space-x-1'>
<Image <Image
src={'/misc/auth/gibs-auth-logo.png'} src={'/misc/auth/gibs-auth-logo.png'}
className="" className=''
alt="Gib's Auth" alt="Gib's Auth"
width={30} width={30}
height={30} height={30}

View File

@@ -50,7 +50,7 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
const currentImageUrl = useQuery( const currentImageUrl = useQuery(
api.files.getImageUrl, api.files.getImageUrl,
user?.image ? { storageId: user.image } : 'skip', user?.image ? { storageId: user.image as Id<'_storage'> } : 'skip',
); );
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => { const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
@@ -109,28 +109,28 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
return ( return (
<CardContent> <CardContent>
<div className="flex flex-col items-center gap-4"> <div className='flex flex-col items-center gap-4'>
{/* Current avatar + trigger (hidden when cropping) */} {/* Current avatar + trigger (hidden when cropping) */}
{!selectedFile && ( {!selectedFile && (
<div <div
className="group relative cursor-pointer" className='group relative cursor-pointer'
onClick={() => inputRef.current?.click()} onClick={() => inputRef.current?.click()}
> >
<BasedAvatar <BasedAvatar
src={currentImageUrl ?? undefined} src={currentImageUrl ?? undefined}
fullName={user?.name} fullName={user?.name}
className="h-42 w-42 text-6xl font-semibold" className='h-42 w-42 text-6xl font-semibold'
userIconProps={{ size: 100 }} userIconProps={{ size: 100 }}
/> />
<div className="absolute inset-0 flex items-center justify-center rounded-full bg-black/0 transition-all group-hover:bg-black/50"> <div className='absolute inset-0 flex items-center justify-center rounded-full bg-black/0 transition-all group-hover:bg-black/50'>
<Upload <Upload
className="text-white opacity-0 transition-opacity group-hover:opacity-100" className='text-white opacity-0 transition-opacity group-hover:opacity-100'
size={24} size={24}
/> />
</div> </div>
<div className="absolute inset-1 flex items-end justify-end transition-all"> <div className='absolute inset-1 flex items-end justify-end transition-all'>
<Pencil <Pencil
className="text-white opacity-100 transition-opacity group-hover:opacity-0" className='text-white opacity-100 transition-opacity group-hover:opacity-0'
size={24} size={24}
/> />
</div> </div>
@@ -140,17 +140,17 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
{/* File input (hidden) */} {/* File input (hidden) */}
<Input <Input
ref={inputRef} ref={inputRef}
id="avatar-upload" id='avatar-upload'
type="file" type='file'
accept="image/*" accept='image/*'
className="hidden" className='hidden'
onChange={handleFileChange} onChange={handleFileChange}
disabled={isUploading} disabled={isUploading}
/> />
{/* Crop UI */} {/* Crop UI */}
{selectedFile && !croppedImage && ( {selectedFile && !croppedImage && (
<div className="flex flex-col items-center gap-3"> <div className='flex flex-col items-center gap-3'>
<ImageCrop <ImageCrop
aspect={1} aspect={1}
circularCrop circularCrop
@@ -158,16 +158,13 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
maxImageSize={3 * 1024 * 1024} // 3MB guard maxImageSize={3 * 1024 * 1024} // 3MB guard
onCrop={setCroppedImage} onCrop={setCroppedImage}
> >
<ImageCropContent className="max-w-sm" /> <ImageCropContent className='max-w-sm' />
<div className="flex items-center gap-2"> <div className='flex items-center gap-2'>
<ImageCropApply /> <Button size='icon' variant='outline'>
<Button <ImageCropApply className='h-full w-full scale-150' />
onClick={handleReset} </Button>
size="icon" <Button onClick={handleReset} size='icon' variant='destructive'>
type="button" <XIcon className='scale-150' />
variant="ghost"
>
<XIcon className="size-4" />
</Button> </Button>
</div> </div>
</ImageCrop> </ImageCrop>
@@ -176,19 +173,20 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
{/* Cropped preview + actions */} {/* Cropped preview + actions */}
{croppedImage && ( {croppedImage && (
<div className="flex flex-col items-center gap-3"> <div className='flex flex-col items-center gap-3'>
<Avatar className="h-42 w-42"> <Avatar className='h-42 w-42'>
<AvatarImage alt="Cropped preview" src={croppedImage} /> <AvatarImage alt='Cropped preview' src={croppedImage} />
</Avatar> </Avatar>
<div className="flex items-center gap-1"> <div className='flex items-center gap-1'>
<Button <Button
onClick={handleSave} onClick={handleSave}
disabled={isUploading} disabled={isUploading}
className="px-4" variant='secondary'
className='px-4'
> >
{isUploading ? ( {isUploading ? (
<span className="inline-flex items-center gap-2"> <span className='inline-flex items-center gap-2'>
<Loader2 className="h-4 w-4 animate-spin" /> <Loader2 className='h-4 w-4 animate-spin' />
Saving... Saving...
</span> </span>
) : ( ) : (
@@ -197,12 +195,11 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
</Button> </Button>
<Button <Button
onClick={handleReset} onClick={handleReset}
size="icon" size='icon'
type="button" type='button'
className="hover:dark:bg-accent bg-red-400/80 hover:text-red-800/80 dark:bg-red-500/30 hover:dark:text-red-300/60" variant='destructive'
variant="secondary"
> >
<XIcon className="size-4" /> <XIcon className='size-4' />
</Button> </Button>
</div> </div>
</div> </div>
@@ -210,8 +207,8 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
{/* Uploading indicator */} {/* Uploading indicator */}
{isUploading && !croppedImage && ( {isUploading && !croppedImage && (
<div className="mt-2 flex items-center text-sm text-gray-500"> <div className='mt-2 flex items-center text-sm text-gray-500'>
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Loader2 className='mr-2 h-4 w-4 animate-spin' />
Uploading... Uploading...
</div> </div>
)} )}

View File

@@ -14,9 +14,7 @@ const ProfileHeader = ({ preloadedUser }: ProfileCardProps) => {
const user = usePreloadedQuery(preloadedUser); const user = usePreloadedQuery(preloadedUser);
return ( return (
<CardHeader> <CardHeader>
<CardTitle className="text-xl"> <CardTitle className='text-xl'>Account Settings</CardTitle>
Account Settings
</CardTitle>
<CardDescription> <CardDescription>
Update your profile information and manage your account preferences Update your profile information and manage your account preferences
</CardDescription> </CardDescription>

View File

@@ -68,7 +68,7 @@ const formSchema = z
interface ResetFormProps { interface ResetFormProps {
preloadedProvider: Preloaded<typeof api.auth.getUserProvider>; preloadedProvider: Preloaded<typeof api.auth.getUserProvider>;
}; }
export const ResetPasswordForm = ({ preloadedProvider }: ResetFormProps) => { export const ResetPasswordForm = ({ preloadedProvider }: ResetFormProps) => {
const userProvider = usePreloadedQuery(preloadedProvider); const userProvider = usePreloadedQuery(preloadedProvider);
@@ -121,19 +121,19 @@ export const ResetPasswordForm = ({ preloadedProvider }: ResetFormProps) => {
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(handleSubmit)} onSubmit={form.handleSubmit(handleSubmit)}
className="space-y-4" className='space-y-4'
> >
<FormField <FormField
control={form.control} control={form.control}
name="currentPassword" name='currentPassword'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Current Password</FormLabel> <FormLabel>Current Password</FormLabel>
<FormControl> <FormControl>
<Input <Input
type="password" type='password'
{...field} {...field}
placeholder="Enter current password" placeholder='Enter current password'
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -142,15 +142,15 @@ export const ResetPasswordForm = ({ preloadedProvider }: ResetFormProps) => {
/> />
<FormField <FormField
control={form.control} control={form.control}
name="newPassword" name='newPassword'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>New Password</FormLabel> <FormLabel>New Password</FormLabel>
<FormControl> <FormControl>
<Input <Input
type="password" type='password'
{...field} {...field}
placeholder="Enter new password" placeholder='Enter new password'
/> />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
@@ -163,23 +163,23 @@ export const ResetPasswordForm = ({ preloadedProvider }: ResetFormProps) => {
/> />
<FormField <FormField
control={form.control} control={form.control}
name="confirmPassword" name='confirmPassword'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Confirm New Password</FormLabel> <FormLabel>Confirm New Password</FormLabel>
<FormControl> <FormControl>
<Input <Input
type="password" type='password'
{...field} {...field}
placeholder="Confirm new password" placeholder='Confirm new password'
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<div className="flex justify-end pt-2"> <div className='flex justify-end pt-2'>
<SubmitButton disabled={loading} pendingText="Updating..."> <SubmitButton disabled={loading} pendingText='Updating...'>
Update Password Update Password
</SubmitButton> </SubmitButton>
</div> </div>

View File

@@ -39,12 +39,12 @@ export const SignOutForm = () => {
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<Button <Button
variant="destructive" variant='destructive'
className="w-full" className='w-full'
onClick={handleSignOut} onClick={handleSignOut}
disabled={isSigningOut} disabled={isSigningOut}
> >
<LogOut className="mr-2 h-4 w-4" /> <LogOut className='mr-2 h-4 w-4' />
{isSigningOut ? 'Signing Out...' : 'Sign Out'} {isSigningOut ? 'Signing Out...' : 'Sign Out'}
</Button> </Button>
</CardContent> </CardContent>

View File

@@ -43,9 +43,12 @@ const formSchema = z.object({
interface UserInfoFormProps { interface UserInfoFormProps {
preloadedUser: Preloaded<typeof api.auth.getUser>; preloadedUser: Preloaded<typeof api.auth.getUser>;
preloadedProvider: Preloaded<typeof api.auth.getUserProvider>; preloadedProvider: Preloaded<typeof api.auth.getUserProvider>;
}; }
export const UserInfoForm = ({ preloadedUser, preloadedProvider }: UserInfoFormProps) => { export const UserInfoForm = ({
preloadedUser,
preloadedProvider,
}: UserInfoFormProps) => {
const user = usePreloadedQuery(preloadedUser); const user = usePreloadedQuery(preloadedUser);
const userProvider = usePreloadedQuery(preloadedProvider); const userProvider = usePreloadedQuery(preloadedProvider);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -106,16 +109,16 @@ export const UserInfoForm = ({ preloadedUser, preloadedProvider }: UserInfoFormP
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(handleSubmit)} onSubmit={form.handleSubmit(handleSubmit)}
className="space-y-4" className='space-y-4'
> >
<FormField <FormField
control={form.control} control={form.control}
name="name" name='name'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Full Name</FormLabel> <FormLabel>Full Name</FormLabel>
<FormControl> <FormControl>
<Input {...field} placeholder="John Doe" /> <Input {...field} placeholder='John Doe' />
</FormControl> </FormControl>
<FormDescription>Your public display name</FormDescription> <FormDescription>Your public display name</FormDescription>
<FormMessage /> <FormMessage />
@@ -125,27 +128,33 @@ export const UserInfoForm = ({ preloadedUser, preloadedProvider }: UserInfoFormP
<FormField <FormField
control={form.control} control={form.control}
name="email" name='email'
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Email</FormLabel> <FormLabel>Email</FormLabel>
<FormControl> <FormControl>
<Input <Input
{...field} {...field}
type="email" type='email'
placeholder="john@example.com" placeholder='john@example.com'
disabled={userProvider !== 'email'} disabled={userProvider !== 'email'}
/> />
</FormControl> </FormControl>
<FormDescription> {userProvider === 'email' ? (
Your email address for account notifications <FormDescription>
</FormDescription> Your email address for account notifications
</FormDescription>
) : (
<FormDescription>
Email is managed through your {userProvider} account
</FormDescription>
)}
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<div className="flex justify-end pt-2"> <div className='flex justify-end pt-2'>
<SubmitButton disabled={loading} pendingText="Saving..."> <SubmitButton disabled={loading} pendingText='Saving...'>
Save Changes Save Changes
</SubmitButton> </SubmitButton>
</div> </div>

View File

@@ -1,5 +1,5 @@
import Link from 'next/link';
import { Kanit } from 'next/font/google'; import { Kanit } from 'next/font/google';
import Link from 'next/link';
const kanitSans = Kanit({ const kanitSans = Kanit({
subsets: ['latin'], subsets: ['latin'],
@@ -8,51 +8,53 @@ const kanitSans = Kanit({
export default function Footer() { export default function Footer() {
return ( return (
<footer className="border-border/40 bg-muted/30 border-t"> <footer className='border-border/40 bg-muted/30 border-t'>
<div className="container mx-auto px-4 py-12"> <div className='container mx-auto px-4 py-12'>
<div className="grid gap-8 md:grid-cols-4"> <div className='grid gap-8 md:grid-cols-4'>
{/* Brand */} {/* Brand */}
<div className="md:col-span-2"> <div className='md:col-span-2'>
<h3 className={`mb-2 text-3xl font-bold ${kanitSans.className}`}>convex monorepo</h3> <h3 className={`mb-2 text-3xl font-bold ${kanitSans.className}`}>
<p className="text-muted-foreground text-sm"> convex monorepo
A production-ready Turborepo starter with Next.js, Expo, and </h3>
a self-hosted Convex backend, including Convex Auth with a <p className='text-muted-foreground text-sm'>
custom useSend email provider to ensure everything can be A production-ready Turborepo starter with Next.js, Expo, and a
self-hosted. Built for developers who want complete control self-hosted Convex backend, including Convex Auth with a custom
without sacrificing ease of use. useSend email provider to ensure everything can be self-hosted.
Built for developers who want complete control without sacrificing
ease of use.
</p> </p>
</div> </div>
{/* Links */} {/* Links */}
<div> <div>
<h4 className="mb-4 text-sm font-semibold">Resources</h4> <h4 className='mb-4 text-sm font-semibold'>Resources</h4>
<ul className="space-y-2 text-sm"> <ul className='space-y-2 text-sm'>
<li> <li>
<Link <Link
href="https://git.gbrown.org/gib/convex-monorepo" href='https://git.gbrown.org/gib/convex-monorepo'
target="_blank" target='_blank'
rel="noopener noreferrer" rel='noopener noreferrer'
className="text-muted-foreground hover:text-foreground transition-colors" className='text-muted-foreground hover:text-foreground transition-colors'
> >
Gitea Repository Gitea Repository
</Link> </Link>
</li> </li>
<li> <li>
<Link <Link
href="https://docs.convex.dev" href='https://docs.convex.dev'
target="_blank" target='_blank'
rel="noopener noreferrer" rel='noopener noreferrer'
className="text-muted-foreground hover:text-foreground transition-colors" className='text-muted-foreground hover:text-foreground transition-colors'
> >
Convex Documentation Convex Documentation
</Link> </Link>
</li> </li>
<li> <li>
<Link <Link
href="https://turbo.build" href='https://turbo.build'
target="_blank" target='_blank'
rel="noopener noreferrer" rel='noopener noreferrer'
className="text-muted-foreground hover:text-foreground transition-colors" className='text-muted-foreground hover:text-foreground transition-colors'
> >
Turborepo Turborepo
</Link> </Link>
@@ -62,34 +64,34 @@ export default function Footer() {
{/* Tech */} {/* Tech */}
<div> <div>
<h4 className="mb-4 text-sm font-semibold">Built With</h4> <h4 className='mb-4 text-sm font-semibold'>Built With</h4>
<ul className="space-y-2 text-sm"> <ul className='space-y-2 text-sm'>
<li> <li>
<Link <Link
href="https://nextjs.org" href='https://nextjs.org'
target="_blank" target='_blank'
rel="noopener noreferrer" rel='noopener noreferrer'
className="text-muted-foreground hover:text-foreground transition-colors" className='text-muted-foreground hover:text-foreground transition-colors'
> >
Next.js Next.js
</Link> </Link>
</li> </li>
<li> <li>
<Link <Link
href="https://expo.dev" href='https://expo.dev'
target="_blank" target='_blank'
rel="noopener noreferrer" rel='noopener noreferrer'
className="text-muted-foreground hover:text-foreground transition-colors" className='text-muted-foreground hover:text-foreground transition-colors'
> >
Expo Expo
</Link> </Link>
</li> </li>
<li> <li>
<Link <Link
href="https://ui.shadcn.com" href='https://ui.shadcn.com'
target="_blank" target='_blank'
rel="noopener noreferrer" rel='noopener noreferrer'
className="text-muted-foreground hover:text-foreground transition-colors" className='text-muted-foreground hover:text-foreground transition-colors'
> >
shadcn/ui shadcn/ui
</Link> </Link>
@@ -99,14 +101,14 @@ export default function Footer() {
</div> </div>
{/* Bottom */} {/* Bottom */}
<div className="border-border/40 text-muted-foreground mt-12 border-t pt-8 text-center text-sm"> <div className='border-border/40 text-muted-foreground mt-12 border-t pt-8 text-center text-sm'>
<p> <p>
Built by{' '} Built by{' '}
<Link <Link
href="https://gbrown.org" href='https://gbrown.org'
target="_blank" target='_blank'
rel="noopener noreferrer" rel='noopener noreferrer'
className="hover:text-foreground font-medium transition-colors" className='hover:text-foreground font-medium transition-colors'
> >
Gib. Gib.
</Link> </Link>

View File

@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation';
import { useAuthActions } from '@convex-dev/auth/react'; import { useAuthActions } from '@convex-dev/auth/react';
import { useConvexAuth, useQuery } from 'convex/react'; import { useConvexAuth, useQuery } from 'convex/react';
import type { Id } from '@gib/backend/convex/_generated/dataModel.js';
import { api } from '@gib/backend/convex/_generated/api.js'; import { api } from '@gib/backend/convex/_generated/api.js';
import { import {
BasedAvatar, BasedAvatar,
@@ -24,26 +25,23 @@ export const AvatarDropdown = () => {
const user = useQuery(api.auth.getUser, {}); const user = useQuery(api.auth.getUser, {});
const currentImageUrl = useQuery( const currentImageUrl = useQuery(
api.files.getImageUrl, api.files.getImageUrl,
user?.image ? { storageId: user.image as any } : 'skip', user?.image ? { storageId: user.image as Id<'_storage'> } : 'skip',
); );
if (isLoading) { if (isLoading) {
return ( return (
<div className="flex items-center gap-2"> <div className='flex items-center gap-2'>
<div className="bg-muted h-8 w-16 animate-pulse rounded-md" /> <div className='bg-muted h-8 w-16 animate-pulse rounded-md' />
<div className="bg-muted h-9 w-9 animate-pulse rounded-full" /> <div className='bg-muted h-9 w-9 animate-pulse rounded-full' />
</div> </div>
); );
} }
if (!isAuthenticated) { if (!isAuthenticated) {
return ( return (
<div className="flex items-center gap-2"> <div className='flex items-center gap-2'>
<Button variant="ghost" size="sm" asChild> <Button size='sm' asChild>
<Link href="/sign-in">Sign In</Link> <Link href='/sign-in'>Sign In</Link>
</Button>
<Button size="sm" asChild>
<Link href="/sign-in">Get Started</Link>
</Button> </Button>
</div> </div>
); );
@@ -55,22 +53,22 @@ export const AvatarDropdown = () => {
<BasedAvatar <BasedAvatar
src={currentImageUrl} src={currentImageUrl}
fullName={user?.name} fullName={user?.name}
className="h-9 w-9" className='h-9 w-9'
fallbackProps={{ className: 'text-sm font-semibold' }} fallbackProps={{ className: 'text-sm font-semibold' }}
userIconProps={{ size: 20 }} userIconProps={{ size: 20 }}
/> />
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align='end'>
{(user?.name ?? user?.email) && ( {(user?.name ?? user?.email) && (
<> <>
<DropdownMenuLabel className="text-center font-bold"> <DropdownMenuLabel className='text-center font-bold'>
{user.name?.trim() ?? user.email?.trim()} {user.name?.trim() ?? user.email?.trim()}
</DropdownMenuLabel> </DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
</> </>
)} )}
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link href="/profile" className="w-full cursor-pointer"> <Link href='/profile' className='w-full cursor-pointer'>
Edit Profile Edit Profile
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
@@ -82,7 +80,7 @@ export const AvatarDropdown = () => {
router.push('/'); router.push('/');
}) })
} }
className="w-full cursor-pointer" className='w-full cursor-pointer'
> >
Sign Out Sign Out
</button> </button>

View File

@@ -7,7 +7,7 @@ import { AvatarDropdown } from './AvatarDropdown';
export const Controls = (themeToggleProps?: ThemeToggleProps) => { export const Controls = (themeToggleProps?: ThemeToggleProps) => {
return ( return (
<div className="flex items-center gap-3"> <div className='flex items-center gap-3'>
<ThemeToggle <ThemeToggle
size={1.1} size={1.1}
buttonProps={{ buttonProps={{

View File

@@ -1,65 +1,64 @@
'use client';
import type { ComponentProps } from 'react'; import type { ComponentProps } from 'react';
import { Kanit } from 'next/font/google';
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { Kanit } from 'next/font/google';
import { Coffee, Server, Wrench } from 'lucide-react'; import { Coffee, Server, Wrench } from 'lucide-react';
import { Controls } from './controls';
const kanitSans = Kanit({ const kanitSans = Kanit({
subsets: ['latin'], subsets: ['latin'],
weight: ['400', '500', '600', '700'], weight: ['400', '500', '600', '700'],
}); });
import { Controls } from './controls';
export default function Header(headerProps: ComponentProps<'header'>) { export default function Header(headerProps: ComponentProps<'header'>) {
return ( return (
<header <header
className="border-border/40 bg-background/95 supports-backdrop-filter:bg-background/60 sticky top-0 z-50 w-full border-b backdrop-blur" className='border-border/40 bg-background/95 supports-backdrop-filter:bg-background/60 sticky top-0 z-50 w-full border-b backdrop-blur'
{...headerProps} {...headerProps}
> >
<div className="container mx-auto flex h-16 items-center justify-between px-4 md:px-6"> <div className='container mx-auto flex h-16 items-center justify-between px-4 md:px-6'>
{/* Logo */} {/* Logo */}
<Link <Link
href="/" href='/'
className="flex items-center gap-2 transition-opacity hover:opacity-80 to-accent-foreground bg-linear-to-r from-[#281A65] via-[#363354] bg-clip-text text-transparent dark:from-[#bec8e6] dark:via-[#F0EEE4] dark:to-[#FFF8E7]" className='to-accent-foreground flex items-center gap-2 bg-linear-to-r from-[#281A65] via-[#363354] bg-clip-text text-transparent transition-opacity hover:opacity-80 dark:from-[#bec8e6] dark:via-[#F0EEE4] dark:to-[#FFF8E7]'
> >
<Image <Image
src="/misc/convex/convex-symbol-white.svg" src='/misc/convex/convex-symbol-white.svg'
alt="Convex Monorepo" alt='Convex Monorepo'
width={50} width={50}
height={50} height={50}
className='invert dark:invert-0' className='invert dark:invert-0'
/> />
<span className={`hidden lg:text-5xl lg:inline mb-3 font-extrabold ${kanitSans.className}`}> <span
className={`mb-3 hidden font-extrabold lg:inline lg:text-5xl ${kanitSans.className}`}
>
convex monorepo convex monorepo
</span> </span>
</Link> </Link>
{/* Navigation */} {/* Navigation */}
<nav className="hidden items-center gap-6 text-base font-medium md:flex"> <nav className='hidden items-center gap-6 text-base font-medium md:flex'>
<Link <Link
href="/#features" href='/#features'
className="text-foreground/60 hover:text-foreground transition-colors flex gap-2 items-center" className='text-foreground/60 hover:text-foreground flex items-center gap-2 transition-colors'
> >
<Wrench width={18} height={18} /> <Wrench width={18} height={18} />
Features Features
</Link> </Link>
<Link <Link
href="/#tech-stack" href='/#tech-stack'
className="text-foreground/60 hover:text-foreground transition-colors flex gap-2 items-center" className='text-foreground/60 hover:text-foreground flex items-center gap-2 transition-colors'
> >
<Server width={18} height={18} /> <Server width={18} height={18} />
Stack Stack
</Link> </Link>
<Link <Link
href="https://git.gbrown.org/gib/convex-monorepo" href='https://git.gbrown.org/gib/convex-monorepo'
target="_blank" target='_blank'
rel="noopener noreferrer" rel='noopener noreferrer'
className="text-foreground/60 hover:text-foreground transition-colors flex gap-2 items-center" className='text-foreground/60 hover:text-foreground flex items-center gap-2 transition-colors'
> >
<Coffee width={20} height={20} /> <Coffee width={20} height={20} />
Repository Repository
</Link> </Link>

View File

@@ -1,16 +1,15 @@
{ {
"lockfileVersion": 1, "lockfileVersion": 1,
"configVersion": 0,
"workspaces": { "workspaces": {
"": { "": {
"name": "convex-turbo", "name": "convex-turbo",
"devDependencies": { "devDependencies": {
"@gib/prettier-config": "workspace:", "@gib/prettier-config": "workspace:",
"@turbo/gen": "^2.7.3", "@turbo/gen": "^2.7.4",
"baseline-browser-mapping": "^2.9.14", "baseline-browser-mapping": "^2.9.14",
"dotenv-cli": "^10.0.0", "dotenv-cli": "^10.0.0",
"prettier": "catalog:", "prettier": "catalog:",
"turbo": "^2.7.3", "turbo": "^2.7.4",
"typescript": "catalog:", "typescript": "catalog:",
}, },
}, },
@@ -1359,9 +1358,9 @@
"@tsconfig/node16": ["@tsconfig/node16@1.0.4", "", {}, "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="], "@tsconfig/node16": ["@tsconfig/node16@1.0.4", "", {}, "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="],
"@turbo/gen": ["@turbo/gen@2.7.3", "", { "dependencies": { "@turbo/workspaces": "2.7.3", "commander": "10.0.0", "fs-extra": "10.1.0", "inquirer": "8.2.4", "minimatch": "9.0.0", "node-plop": "0.26.3", "picocolors": "1.0.1", "proxy-agent": "6.5.0", "ts-node": "10.9.2", "update-check": "1.5.4", "validate-npm-package-name": "5.0.0" }, "bin": { "gen": "dist/cli.js" } }, "sha512-lvjv1/fwUewFqew/qdNM//6cC32EIjEAJ2rCx5blL4+BVX16ubHroEGUJRNQegW0hpglTztdJW6DdS3hIuqoCA=="], "@turbo/gen": ["@turbo/gen@2.7.4", "", { "dependencies": { "@turbo/workspaces": "2.7.4", "commander": "10.0.0", "fs-extra": "10.1.0", "inquirer": "8.2.4", "minimatch": "9.0.0", "node-plop": "0.26.3", "picocolors": "1.0.1", "proxy-agent": "6.5.0", "ts-node": "10.9.2", "update-check": "1.5.4", "validate-npm-package-name": "5.0.0" }, "bin": { "gen": "dist/cli.js" } }, "sha512-izrkJuawtMU/Pp1457+wJ8W+DDAEu3hiYKPdJyKzYtFHMhuTL8UMLsNQ2fyjRzsJWiG4493oI2UAdniZmB7nPg=="],
"@turbo/workspaces": ["@turbo/workspaces@2.7.3", "", { "dependencies": { "commander": "10.0.0", "execa": "5.1.1", "fast-glob": "3.2.12", "fs-extra": "10.1.0", "gradient-string": "2.0.1", "inquirer": "8.2.4", "js-yaml": "4.1.0", "ora": "4.1.1", "picocolors": "1.0.1", "semver": "7.6.2", "update-check": "1.5.4" }, "bin": { "workspaces": "dist/cli.js" } }, "sha512-opYsHNZo3qhGJ+qxLSEhNcnf+e8beha/bfinwUH857NFL7oYHsOpVtNJIiwcJYKjZjJO4XtxTLsvAo3CQ5/R5A=="], "@turbo/workspaces": ["@turbo/workspaces@2.7.4", "", { "dependencies": { "commander": "10.0.0", "execa": "5.1.1", "fast-glob": "3.2.12", "fs-extra": "10.1.0", "gradient-string": "2.0.1", "inquirer": "8.2.4", "js-yaml": "4.1.0", "ora": "4.1.1", "picocolors": "1.0.1", "semver": "7.6.2", "update-check": "1.5.4" }, "bin": { "workspaces": "dist/cli.js" } }, "sha512-1jAzZzi2MkmSKHMm7SSjaADg4jPjy6tRqAZBqETdyQqFlIvfrM1uquaqaK9BPgBW3QeJlj1hIh+Eu58Xsocu9w=="],
"@tybys/wasm-util": ["@tybys/wasm-util@0.8.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q=="], "@tybys/wasm-util": ["@tybys/wasm-util@0.8.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q=="],
@@ -3013,19 +3012,19 @@
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"turbo": ["turbo@2.7.3", "", { "optionalDependencies": { "turbo-darwin-64": "2.7.3", "turbo-darwin-arm64": "2.7.3", "turbo-linux-64": "2.7.3", "turbo-linux-arm64": "2.7.3", "turbo-windows-64": "2.7.3", "turbo-windows-arm64": "2.7.3" }, "bin": { "turbo": "bin/turbo" } }, "sha512-+HjKlP4OfYk+qzvWNETA3cUO5UuK6b5MSc2UJOKyvBceKucQoQGb2g7HlC2H1GHdkfKrk4YF1VPvROkhVZDDLQ=="], "turbo": ["turbo@2.7.4", "", { "optionalDependencies": { "turbo-darwin-64": "2.7.4", "turbo-darwin-arm64": "2.7.4", "turbo-linux-64": "2.7.4", "turbo-linux-arm64": "2.7.4", "turbo-windows-64": "2.7.4", "turbo-windows-arm64": "2.7.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-bkO4AddmDishzJB2ze7aYYPaejMoJVfS0XnaR6RCdXFOY8JGJfQE+l9fKiV7uDPa5Ut44gmOWJL3894CIMeH9g=="],
"turbo-darwin-64": ["turbo-darwin-64@2.7.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-aZHhvRiRHXbJw1EcEAq4aws1hsVVUZ9DPuSFaq9VVFAKCup7niIEwc22glxb7240yYEr1vLafdQ2U294Vcwz+w=="], "turbo-darwin-64": ["turbo-darwin-64@2.7.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xDR30ltfkSsRfGzABBckvl1nz1cZ3ssTujvdj+TPwOweeDRvZ0e06t5DS0rmRBvyKpgGs42K/EK6Mn2qLlFY9A=="],
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.7.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CkVrHSq+Bnhl9sX2LQgqQYVfLTWC2gvI74C4758OmU0djfrssDKU9d4YQF0AYXXhIIRZipSXfxClQziIMD+EAg=="], "turbo-darwin-arm64": ["turbo-darwin-arm64@2.7.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P7sjqXtOL/+nYWPvcDGWhi8wf8M8mZHHB8XEzw2VX7VJrS8IGHyJHGD1AYfDvhAEcr7pnk3gGifz3/xyhI655w=="],
"turbo-linux-64": ["turbo-linux-64@2.7.3", "", { "os": "linux", "cpu": "x64" }, "sha512-GqDsCNnzzr89kMaLGpRALyigUklzgxIrSy2pHZVXyifgczvYPnLglex78Aj3T2gu+T3trPPH2iJ+pWucVOCC2Q=="], "turbo-linux-64": ["turbo-linux-64@2.7.4", "", { "os": "linux", "cpu": "x64" }, "sha512-GofFOxRO/IhG8BcPyMSSB3Y2+oKQotsaYbHxL9yD6JPb20/o35eo+zUSyazOtilAwDHnak5dorAJFoFU8MIg2A=="],
"turbo-linux-arm64": ["turbo-linux-arm64@2.7.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-NdCDTfIcIo3dWjsiaAHlxu5gW61Ed/8maah1IAF/9E3EtX0aAHNiBMbuYLZaR4vRJ7BeVkYB6xKWRtdFLZ0y3g=="], "turbo-linux-arm64": ["turbo-linux-arm64@2.7.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+RQKgNjksVPxYAyAgmDV7w/1qj++qca+nSNTAOKGOfJiDtSvRKoci89oftJ6anGs00uamLKVEQ712TI/tfNAIw=="],
"turbo-windows-64": ["turbo-windows-64@2.7.3", "", { "os": "win32", "cpu": "x64" }, "sha512-7bVvO987daXGSJVYBoG8R4Q+csT1pKIgLJYZevXRQ0Hqw0Vv4mKme/TOjYXs9Qb1xMKh51Tb3bXKDbd8/4G08g=="], "turbo-windows-64": ["turbo-windows-64@2.7.4", "", { "os": "win32", "cpu": "x64" }, "sha512-rfak1+g+ON3czs1mDYsCS4X74ZmK6gOgRQTXjDICtzvR4o61paqtgAYtNPofcVsMWeF4wvCajSeoAkkeAnQ1kg=="],
"turbo-windows-arm64": ["turbo-windows-arm64@2.7.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-nTodweTbPmkvwMu/a55XvjMsPtuyUSC+sV7f/SR57K36rB2I0YG21qNETN+00LOTUW9B3omd8XkiXJkt4kx/cw=="], "turbo-windows-arm64": ["turbo-windows-arm64@2.7.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-1ZgBNjNRbDu/fPeqXuX9i26x3CJ/Y1gcwUpQ+Vp7kN9Un6RZ9kzs164f/knrjcu5E+szCRexVjRSJay1k5jApA=="],
"tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="],

View File

@@ -55,11 +55,11 @@
}, },
"devDependencies": { "devDependencies": {
"@gib/prettier-config": "workspace:", "@gib/prettier-config": "workspace:",
"@turbo/gen": "^2.7.3", "@turbo/gen": "^2.7.4",
"baseline-browser-mapping": "^2.9.14", "baseline-browser-mapping": "^2.9.14",
"dotenv-cli": "^10.0.0", "dotenv-cli": "^10.0.0",
"prettier": "catalog:", "prettier": "catalog:",
"turbo": "^2.7.3", "turbo": "^2.7.4",
"typescript": "catalog:" "typescript": "catalog:"
}, },
"prettier": "@gib/prettier-config" "prettier": "@gib/prettier-config"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -11,7 +11,7 @@ function Avatar({
}: React.ComponentProps<typeof AvatarPrimitive.Root>) { }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
return ( return (
<AvatarPrimitive.Root <AvatarPrimitive.Root
data-slot="avatar" data-slot='avatar'
className={cn( className={cn(
'relative flex size-8 shrink-0 overflow-hidden rounded-full', 'relative flex size-8 shrink-0 overflow-hidden rounded-full',
className, className,
@@ -27,7 +27,7 @@ function AvatarImage({
}: React.ComponentProps<typeof AvatarPrimitive.Image>) { }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
return ( return (
<AvatarPrimitive.Image <AvatarPrimitive.Image
data-slot="avatar-image" data-slot='avatar-image'
className={cn('aspect-square size-full', className)} className={cn('aspect-square size-full', className)}
{...props} {...props}
/> />
@@ -40,7 +40,7 @@ function AvatarFallback({
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) { }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
return ( return (
<AvatarPrimitive.Fallback <AvatarPrimitive.Fallback
data-slot="avatar-fallback" data-slot='avatar-fallback'
className={cn( className={cn(
'bg-muted flex size-full items-center justify-center rounded-full', 'bg-muted flex size-full items-center justify-center rounded-full',
className, className,

View File

@@ -27,7 +27,7 @@ const BasedAvatar = ({
}: BasedAvatarProps) => { }: BasedAvatarProps) => {
return ( return (
<AvatarPrimitive.Root <AvatarPrimitive.Root
data-slot="avatar" data-slot='avatar'
className={cn( className={cn(
'relative flex size-8 shrink-0 cursor-pointer overflow-hidden rounded-full', 'relative flex size-8 shrink-0 cursor-pointer overflow-hidden rounded-full',
className, className,
@@ -43,7 +43,7 @@ const BasedAvatar = ({
) : ( ) : (
<AvatarPrimitive.Fallback <AvatarPrimitive.Fallback
{...fallbackProps} {...fallbackProps}
data-slot="avatar-fallback" data-slot='avatar-fallback'
className={cn( className={cn(
'bg-muted flex size-full items-center justify-center rounded-full', 'bg-muted flex size-full items-center justify-center rounded-full',
fallbackProps?.className, fallbackProps?.className,

View File

@@ -35,7 +35,7 @@ const BasedProgress = ({
return ( return (
<ProgressPrimitive.Root <ProgressPrimitive.Root
data-slot="progress" data-slot='progress'
className={cn( className={cn(
'bg-primary/20 relative h-2 w-full overflow-hidden rounded-full', 'bg-primary/20 relative h-2 w-full overflow-hidden rounded-full',
className, className,
@@ -43,8 +43,8 @@ const BasedProgress = ({
{...props} {...props}
> >
<ProgressPrimitive.Indicator <ProgressPrimitive.Indicator
data-slot="progress-indicator" data-slot='progress-indicator'
className="bg-primary h-full w-full flex-1 transition-all" className='bg-primary h-full w-full flex-1 transition-all'
style={{ transform: `translateX(-${100 - (progress ?? 0)}%)` }} style={{ transform: `translateX(-${100 - (progress ?? 0)}%)` }}
/> />
</ProgressPrimitive.Root> </ProgressPrimitive.Root>

View File

@@ -50,7 +50,7 @@ function Button({
return ( return (
<Comp <Comp
data-slot="button" data-slot='button'
className={cn(buttonVariants({ variant, size, className }))} className={cn(buttonVariants({ variant, size, className }))}
{...props} {...props}
/> />

View File

@@ -5,7 +5,7 @@ import { cn } from '@gib/ui';
function Card({ className, ...props }: React.ComponentProps<'div'>) { function Card({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="card" data-slot='card'
className={cn( className={cn(
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm', 'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
className, className,
@@ -18,7 +18,7 @@ function Card({ className, ...props }: React.ComponentProps<'div'>) {
function CardHeader({ className, ...props }: React.ComponentProps<'div'>) { function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="card-header" data-slot='card-header'
className={cn( className={cn(
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6', '@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
className, className,
@@ -31,7 +31,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
function CardTitle({ className, ...props }: React.ComponentProps<'div'>) { function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="card-title" data-slot='card-title'
className={cn('leading-none font-semibold', className)} className={cn('leading-none font-semibold', className)}
{...props} {...props}
/> />
@@ -41,7 +41,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
function CardDescription({ className, ...props }: React.ComponentProps<'div'>) { function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="card-description" data-slot='card-description'
className={cn('text-muted-foreground text-sm', className)} className={cn('text-muted-foreground text-sm', className)}
{...props} {...props}
/> />
@@ -51,7 +51,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
function CardAction({ className, ...props }: React.ComponentProps<'div'>) { function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="card-action" data-slot='card-action'
className={cn( className={cn(
'col-start-2 row-span-2 row-start-1 self-start justify-self-end', 'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
className, className,
@@ -64,7 +64,7 @@ function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
function CardContent({ className, ...props }: React.ComponentProps<'div'>) { function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="card-content" data-slot='card-content'
className={cn('px-6', className)} className={cn('px-6', className)}
{...props} {...props}
/> />
@@ -74,7 +74,7 @@ function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
function CardFooter({ className, ...props }: React.ComponentProps<'div'>) { function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="card-footer" data-slot='card-footer'
className={cn('flex items-center px-6 [.border-t]:pt-6', className)} className={cn('flex items-center px-6 [.border-t]:pt-6', className)}
{...props} {...props}
/> />

View File

@@ -12,7 +12,7 @@ function Checkbox({
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) { }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
return ( return (
<CheckboxPrimitive.Root <CheckboxPrimitive.Root
data-slot="checkbox" data-slot='checkbox'
className={cn( className={cn(
'peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50', 'peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
className, className,
@@ -20,10 +20,10 @@ function Checkbox({
{...props} {...props}
> >
<CheckboxPrimitive.Indicator <CheckboxPrimitive.Indicator
data-slot="checkbox-indicator" data-slot='checkbox-indicator'
className="flex items-center justify-center text-current transition-none" className='flex items-center justify-center text-current transition-none'
> >
<CheckIcon className="size-3.5" /> <CheckIcon className='size-3.5' />
</CheckboxPrimitive.Indicator> </CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root> </CheckboxPrimitive.Root>
); );

View File

@@ -8,25 +8,25 @@ import { cn } from '@gib/ui';
function Drawer({ function Drawer({
...props ...props
}: React.ComponentProps<typeof DrawerPrimitive.Root>) { }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
return <DrawerPrimitive.Root data-slot="drawer" {...props} />; return <DrawerPrimitive.Root data-slot='drawer' {...props} />;
} }
function DrawerTrigger({ function DrawerTrigger({
...props ...props
}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) { }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />; return <DrawerPrimitive.Trigger data-slot='drawer-trigger' {...props} />;
} }
function DrawerPortal({ function DrawerPortal({
...props ...props
}: React.ComponentProps<typeof DrawerPrimitive.Portal>) { }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />; return <DrawerPrimitive.Portal data-slot='drawer-portal' {...props} />;
} }
function DrawerClose({ function DrawerClose({
...props ...props
}: React.ComponentProps<typeof DrawerPrimitive.Close>) { }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />; return <DrawerPrimitive.Close data-slot='drawer-close' {...props} />;
} }
function DrawerOverlay({ function DrawerOverlay({
@@ -35,7 +35,7 @@ function DrawerOverlay({
}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) { }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
return ( return (
<DrawerPrimitive.Overlay <DrawerPrimitive.Overlay
data-slot="drawer-overlay" data-slot='drawer-overlay'
className={cn( className={cn(
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50', 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
className, className,
@@ -51,10 +51,10 @@ function DrawerContent({
...props ...props
}: React.ComponentProps<typeof DrawerPrimitive.Content>) { }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
return ( return (
<DrawerPortal data-slot="drawer-portal"> <DrawerPortal data-slot='drawer-portal'>
<DrawerOverlay /> <DrawerOverlay />
<DrawerPrimitive.Content <DrawerPrimitive.Content
data-slot="drawer-content" data-slot='drawer-content'
className={cn( className={cn(
'group/drawer-content bg-background fixed z-50 flex h-auto flex-col', 'group/drawer-content bg-background fixed z-50 flex h-auto flex-col',
'data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b', 'data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b',
@@ -65,7 +65,7 @@ function DrawerContent({
)} )}
{...props} {...props}
> >
<div className="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" /> <div className='bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block' />
{children} {children}
</DrawerPrimitive.Content> </DrawerPrimitive.Content>
</DrawerPortal> </DrawerPortal>
@@ -75,7 +75,7 @@ function DrawerContent({
function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) { function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="drawer-header" data-slot='drawer-header'
className={cn( className={cn(
'flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left', 'flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left',
className, className,
@@ -88,7 +88,7 @@ function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) { function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="drawer-footer" data-slot='drawer-footer'
className={cn('mt-auto flex flex-col gap-2 p-4', className)} className={cn('mt-auto flex flex-col gap-2 p-4', className)}
{...props} {...props}
/> />
@@ -101,7 +101,7 @@ function DrawerTitle({
}: React.ComponentProps<typeof DrawerPrimitive.Title>) { }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
return ( return (
<DrawerPrimitive.Title <DrawerPrimitive.Title
data-slot="drawer-title" data-slot='drawer-title'
className={cn('text-foreground font-semibold', className)} className={cn('text-foreground font-semibold', className)}
{...props} {...props}
/> />
@@ -114,7 +114,7 @@ function DrawerDescription({
}: React.ComponentProps<typeof DrawerPrimitive.Description>) { }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
return ( return (
<DrawerPrimitive.Description <DrawerPrimitive.Description
data-slot="drawer-description" data-slot='drawer-description'
className={cn('text-muted-foreground text-sm', className)} className={cn('text-muted-foreground text-sm', className)}
{...props} {...props}
/> />

View File

@@ -9,14 +9,14 @@ import { cn } from '@gib/ui';
function DropdownMenu({ function DropdownMenu({
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />; return <DropdownMenuPrimitive.Root data-slot='dropdown-menu' {...props} />;
} }
function DropdownMenuPortal({ function DropdownMenuPortal({
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return ( return (
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} /> <DropdownMenuPrimitive.Portal data-slot='dropdown-menu-portal' {...props} />
); );
} }
@@ -25,7 +25,7 @@ function DropdownMenuTrigger({
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
return ( return (
<DropdownMenuPrimitive.Trigger <DropdownMenuPrimitive.Trigger
data-slot="dropdown-menu-trigger" data-slot='dropdown-menu-trigger'
{...props} {...props}
/> />
); );
@@ -39,7 +39,7 @@ function DropdownMenuContent({
return ( return (
<DropdownMenuPrimitive.Portal> <DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content <DropdownMenuPrimitive.Content
data-slot="dropdown-menu-content" data-slot='dropdown-menu-content'
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md', 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',
@@ -55,7 +55,7 @@ function DropdownMenuGroup({
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return ( return (
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} /> <DropdownMenuPrimitive.Group data-slot='dropdown-menu-group' {...props} />
); );
} }
@@ -70,7 +70,7 @@ function DropdownMenuItem({
}) { }) {
return ( return (
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
data-slot="dropdown-menu-item" data-slot='dropdown-menu-item'
data-inset={inset} data-inset={inset}
data-variant={variant} data-variant={variant}
className={cn( className={cn(
@@ -90,7 +90,7 @@ function DropdownMenuCheckboxItem({
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
return ( return (
<DropdownMenuPrimitive.CheckboxItem <DropdownMenuPrimitive.CheckboxItem
data-slot="dropdown-menu-checkbox-item" data-slot='dropdown-menu-checkbox-item'
className={cn( className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className, className,
@@ -98,9 +98,9 @@ function DropdownMenuCheckboxItem({
checked={checked} checked={checked}
{...props} {...props}
> >
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <span className='pointer-events-none absolute left-2 flex size-3.5 items-center justify-center'>
<DropdownMenuPrimitive.ItemIndicator> <DropdownMenuPrimitive.ItemIndicator>
<CheckIcon className="size-4" /> <CheckIcon className='size-4' />
</DropdownMenuPrimitive.ItemIndicator> </DropdownMenuPrimitive.ItemIndicator>
</span> </span>
{children} {children}
@@ -113,7 +113,7 @@ function DropdownMenuRadioGroup({
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
return ( return (
<DropdownMenuPrimitive.RadioGroup <DropdownMenuPrimitive.RadioGroup
data-slot="dropdown-menu-radio-group" data-slot='dropdown-menu-radio-group'
{...props} {...props}
/> />
); );
@@ -126,16 +126,16 @@ function DropdownMenuRadioItem({
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
return ( return (
<DropdownMenuPrimitive.RadioItem <DropdownMenuPrimitive.RadioItem
data-slot="dropdown-menu-radio-item" data-slot='dropdown-menu-radio-item'
className={cn( className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className, className,
)} )}
{...props} {...props}
> >
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center"> <span className='pointer-events-none absolute left-2 flex size-3.5 items-center justify-center'>
<DropdownMenuPrimitive.ItemIndicator> <DropdownMenuPrimitive.ItemIndicator>
<CircleIcon className="size-2 fill-current" /> <CircleIcon className='size-2 fill-current' />
</DropdownMenuPrimitive.ItemIndicator> </DropdownMenuPrimitive.ItemIndicator>
</span> </span>
{children} {children}
@@ -152,7 +152,7 @@ function DropdownMenuLabel({
}) { }) {
return ( return (
<DropdownMenuPrimitive.Label <DropdownMenuPrimitive.Label
data-slot="dropdown-menu-label" data-slot='dropdown-menu-label'
data-inset={inset} data-inset={inset}
className={cn( className={cn(
'px-2 py-1.5 text-sm font-medium data-[inset]:pl-8', 'px-2 py-1.5 text-sm font-medium data-[inset]:pl-8',
@@ -169,7 +169,7 @@ function DropdownMenuSeparator({
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
return ( return (
<DropdownMenuPrimitive.Separator <DropdownMenuPrimitive.Separator
data-slot="dropdown-menu-separator" data-slot='dropdown-menu-separator'
className={cn('bg-border -mx-1 my-1 h-px', className)} className={cn('bg-border -mx-1 my-1 h-px', className)}
{...props} {...props}
/> />
@@ -182,7 +182,7 @@ function DropdownMenuShortcut({
}: React.ComponentProps<'span'>) { }: React.ComponentProps<'span'>) {
return ( return (
<span <span
data-slot="dropdown-menu-shortcut" data-slot='dropdown-menu-shortcut'
className={cn( className={cn(
'text-muted-foreground ml-auto text-xs tracking-widest', 'text-muted-foreground ml-auto text-xs tracking-widest',
className, className,
@@ -195,7 +195,7 @@ function DropdownMenuShortcut({
function DropdownMenuSub({ function DropdownMenuSub({
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />; return <DropdownMenuPrimitive.Sub data-slot='dropdown-menu-sub' {...props} />;
} }
function DropdownMenuSubTrigger({ function DropdownMenuSubTrigger({
@@ -208,7 +208,7 @@ function DropdownMenuSubTrigger({
}) { }) {
return ( return (
<DropdownMenuPrimitive.SubTrigger <DropdownMenuPrimitive.SubTrigger
data-slot="dropdown-menu-sub-trigger" data-slot='dropdown-menu-sub-trigger'
data-inset={inset} data-inset={inset}
className={cn( className={cn(
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8', 'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',
@@ -217,7 +217,7 @@ function DropdownMenuSubTrigger({
{...props} {...props}
> >
{children} {children}
<ChevronRightIcon className="ml-auto size-4" /> <ChevronRightIcon className='ml-auto size-4' />
</DropdownMenuPrimitive.SubTrigger> </DropdownMenuPrimitive.SubTrigger>
); );
} }
@@ -228,7 +228,7 @@ function DropdownMenuSubContent({
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) { }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
return ( return (
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content" data-slot='dropdown-menu-sub-content'
className={cn( className={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg', 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',
className, className,

View File

@@ -12,7 +12,7 @@ export function FieldSet({
}: React.ComponentProps<'fieldset'>) { }: React.ComponentProps<'fieldset'>) {
return ( return (
<fieldset <fieldset
data-slot="field-set" data-slot='field-set'
className={cn( className={cn(
'flex flex-col gap-6', 'flex flex-col gap-6',
'has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3', 'has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
@@ -30,7 +30,7 @@ export function FieldLegend({
}: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) { }: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {
return ( return (
<legend <legend
data-slot="field-legend" data-slot='field-legend'
data-variant={variant} data-variant={variant}
className={cn( className={cn(
'mb-3 font-medium', 'mb-3 font-medium',
@@ -49,7 +49,7 @@ export function FieldGroup({
}: React.ComponentProps<'div'>) { }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="field-group" data-slot='field-group'
className={cn( className={cn(
'group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4', 'group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4',
className, className,
@@ -90,8 +90,8 @@ export function Field({
}: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) { }: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {
return ( return (
<div <div
role="group" role='group'
data-slot="field" data-slot='field'
data-orientation={orientation} data-orientation={orientation}
className={cn(fieldVariants({ orientation }), className)} className={cn(fieldVariants({ orientation }), className)}
{...props} {...props}
@@ -105,7 +105,7 @@ export function FieldContent({
}: React.ComponentProps<'div'>) { }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="field-content" data-slot='field-content'
className={cn( className={cn(
'group/field-content flex flex-1 flex-col gap-1.5 leading-snug', 'group/field-content flex flex-1 flex-col gap-1.5 leading-snug',
className, className,
@@ -121,7 +121,7 @@ export function FieldLabel({
}: React.ComponentProps<typeof Label>) { }: React.ComponentProps<typeof Label>) {
return ( return (
<Label <Label
data-slot="field-label" data-slot='field-label'
className={cn( className={cn(
'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50', 'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50',
'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4', 'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4',
@@ -139,7 +139,7 @@ export function FieldTitle({
}: React.ComponentProps<'div'>) { }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="field-label" data-slot='field-label'
className={cn( className={cn(
'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50', 'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50',
className, className,
@@ -155,7 +155,7 @@ export function FieldDescription({
}: React.ComponentProps<'p'>) { }: React.ComponentProps<'p'>) {
return ( return (
<p <p
data-slot="field-description" data-slot='field-description'
className={cn( className={cn(
'text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance', 'text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance',
'last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5', 'last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5',
@@ -176,7 +176,7 @@ export function FieldSeparator({
}) { }) {
return ( return (
<div <div
data-slot="field-separator" data-slot='field-separator'
data-content={!!children} data-content={!!children}
className={cn( className={cn(
'relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2', 'relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2',
@@ -184,11 +184,11 @@ export function FieldSeparator({
)} )}
{...props} {...props}
> >
<Separator className="absolute inset-0 top-1/2" /> <Separator className='absolute inset-0 top-1/2' />
{children && ( {children && (
<span <span
className="bg-background text-muted-foreground relative mx-auto block w-fit px-2" className='bg-background text-muted-foreground relative mx-auto block w-fit px-2'
data-slot="field-separator-content" data-slot='field-separator-content'
> >
{children} {children}
</span> </span>
@@ -221,7 +221,7 @@ export function FieldError({
} }
return ( return (
<ul className="ml-4 flex list-disc flex-col gap-1"> <ul className='ml-4 flex list-disc flex-col gap-1'>
{errors.map( {errors.map(
(error, index) => (error, index) =>
error.message && <li key={index}>{error.message}</li>, error.message && <li key={index}>{error.message}</li>,
@@ -236,8 +236,8 @@ export function FieldError({
return ( return (
<div <div
role="alert" role='alert'
data-slot="field-error" data-slot='field-error'
className={cn('text-destructive text-sm font-normal', className)} className={cn('text-destructive text-sm font-normal', className)}
{...props} {...props}
> >

View File

@@ -76,7 +76,7 @@ function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<FormItemContext.Provider value={{ id }}> <FormItemContext.Provider value={{ id }}>
<div <div
data-slot="form-item" data-slot='form-item'
className={cn('grid gap-2', className)} className={cn('grid gap-2', className)}
{...props} {...props}
/> />
@@ -92,7 +92,7 @@ function FormLabel({
return ( return (
<Label <Label
data-slot="form-label" data-slot='form-label'
data-error={!!error} data-error={!!error}
className={cn('data-[error=true]:text-destructive', className)} className={cn('data-[error=true]:text-destructive', className)}
htmlFor={formItemId} htmlFor={formItemId}
@@ -107,7 +107,7 @@ function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
return ( return (
<Slot <Slot
data-slot="form-control" data-slot='form-control'
id={formItemId} id={formItemId}
aria-describedby={ aria-describedby={
!error !error
@@ -125,7 +125,7 @@ function FormDescription({ className, ...props }: React.ComponentProps<'p'>) {
return ( return (
<p <p
data-slot="form-description" data-slot='form-description'
id={formDescriptionId} id={formDescriptionId}
className={cn('text-muted-foreground text-sm', className)} className={cn('text-muted-foreground text-sm', className)}
{...props} {...props}
@@ -143,7 +143,7 @@ function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {
return ( return (
<p <p
data-slot="form-message" data-slot='form-message'
id={formMessageId} id={formMessageId}
className={cn('text-destructive text-sm', className)} className={cn('text-destructive text-sm', className)}
{...props} {...props}

View File

@@ -15,7 +15,7 @@ function InputOTP({
}) { }) {
return ( return (
<OTPInput <OTPInput
data-slot="input-otp" data-slot='input-otp'
containerClassName={cn( containerClassName={cn(
'flex items-center gap-2 has-disabled:opacity-50', 'flex items-center gap-2 has-disabled:opacity-50',
containerClassName, containerClassName,
@@ -29,7 +29,7 @@ function InputOTP({
function InputOTPGroup({ className, ...props }: React.ComponentProps<'div'>) { function InputOTPGroup({ className, ...props }: React.ComponentProps<'div'>) {
return ( return (
<div <div
data-slot="input-otp-group" data-slot='input-otp-group'
className={cn('flex items-center', className)} className={cn('flex items-center', className)}
{...props} {...props}
/> />
@@ -48,7 +48,7 @@ function InputOTPSlot({
return ( return (
<div <div
data-slot="input-otp-slot" data-slot='input-otp-slot'
data-active={isActive} data-active={isActive}
className={cn( className={cn(
'data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]', 'data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]',
@@ -58,8 +58,8 @@ function InputOTPSlot({
> >
{char} {char}
{hasFakeCaret && ( {hasFakeCaret && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center"> <div className='pointer-events-none absolute inset-0 flex items-center justify-center'>
<div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" /> <div className='animate-caret-blink bg-foreground h-4 w-px duration-1000' />
</div> </div>
)} )}
</div> </div>
@@ -68,7 +68,7 @@ function InputOTPSlot({
function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) { function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) {
return ( return (
<div data-slot="input-otp-separator" role="separator" {...props}> <div data-slot='input-otp-separator' role='separator' {...props}>
<MinusIcon /> <MinusIcon />
</div> </div>
); );

View File

@@ -6,7 +6,7 @@ function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
return ( return (
<input <input
type={type} type={type}
data-slot="input" data-slot='input'
className={cn( className={cn(
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm', 'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]', 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',

View File

@@ -11,7 +11,7 @@ function Label({
}: React.ComponentProps<typeof LabelPrimitive.Root>) { }: React.ComponentProps<typeof LabelPrimitive.Root>) {
return ( return (
<LabelPrimitive.Root <LabelPrimitive.Root
data-slot="label" data-slot='label'
className={cn( className={cn(
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50', 'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
className, className,

View File

@@ -11,9 +11,9 @@ import { buttonVariants, cn } from '@gib/ui';
function Pagination({ className, ...props }: React.ComponentProps<'nav'>) { function Pagination({ className, ...props }: React.ComponentProps<'nav'>) {
return ( return (
<nav <nav
role="navigation" role='navigation'
aria-label="pagination" aria-label='pagination'
data-slot="pagination" data-slot='pagination'
className={cn('mx-auto flex w-full justify-center', className)} className={cn('mx-auto flex w-full justify-center', className)}
{...props} {...props}
/> />
@@ -26,7 +26,7 @@ function PaginationContent({
}: React.ComponentProps<'ul'>) { }: React.ComponentProps<'ul'>) {
return ( return (
<ul <ul
data-slot="pagination-content" data-slot='pagination-content'
className={cn('flex flex-row items-center gap-1', className)} className={cn('flex flex-row items-center gap-1', className)}
{...props} {...props}
/> />
@@ -34,7 +34,7 @@ function PaginationContent({
} }
function PaginationItem({ ...props }: React.ComponentProps<'li'>) { function PaginationItem({ ...props }: React.ComponentProps<'li'>) {
return <li data-slot="pagination-item" {...props} />; return <li data-slot='pagination-item' {...props} />;
} }
type PaginationLinkProps = { type PaginationLinkProps = {
@@ -51,7 +51,7 @@ function PaginationLink({
return ( return (
<a <a
aria-current={isActive ? 'page' : undefined} aria-current={isActive ? 'page' : undefined}
data-slot="pagination-link" data-slot='pagination-link'
data-active={isActive} data-active={isActive}
className={cn( className={cn(
buttonVariants({ buttonVariants({
@@ -71,13 +71,13 @@ function PaginationPrevious({
}: React.ComponentProps<typeof PaginationLink>) { }: React.ComponentProps<typeof PaginationLink>) {
return ( return (
<PaginationLink <PaginationLink
aria-label="Go to previous page" aria-label='Go to previous page'
size="default" size='default'
className={cn('gap-1 px-2.5 sm:pl-2.5', className)} className={cn('gap-1 px-2.5 sm:pl-2.5', className)}
{...props} {...props}
> >
<ChevronLeftIcon /> <ChevronLeftIcon />
<span className="hidden sm:block">Previous</span> <span className='hidden sm:block'>Previous</span>
</PaginationLink> </PaginationLink>
); );
} }
@@ -88,12 +88,12 @@ function PaginationNext({
}: React.ComponentProps<typeof PaginationLink>) { }: React.ComponentProps<typeof PaginationLink>) {
return ( return (
<PaginationLink <PaginationLink
aria-label="Go to next page" aria-label='Go to next page'
size="default" size='default'
className={cn('gap-1 px-2.5 sm:pr-2.5', className)} className={cn('gap-1 px-2.5 sm:pr-2.5', className)}
{...props} {...props}
> >
<span className="hidden sm:block">Next</span> <span className='hidden sm:block'>Next</span>
<ChevronRightIcon /> <ChevronRightIcon />
</PaginationLink> </PaginationLink>
); );
@@ -106,12 +106,12 @@ function PaginationEllipsis({
return ( return (
<span <span
aria-hidden aria-hidden
data-slot="pagination-ellipsis" data-slot='pagination-ellipsis'
className={cn('flex size-9 items-center justify-center', className)} className={cn('flex size-9 items-center justify-center', className)}
{...props} {...props}
> >
<MoreHorizontalIcon className="size-4" /> <MoreHorizontalIcon className='size-4' />
<span className="sr-only">More pages</span> <span className='sr-only'>More pages</span>
</span> </span>
); );
} }

View File

@@ -12,7 +12,7 @@ function Progress({
}: React.ComponentProps<typeof ProgressPrimitive.Root>) { }: React.ComponentProps<typeof ProgressPrimitive.Root>) {
return ( return (
<ProgressPrimitive.Root <ProgressPrimitive.Root
data-slot="progress" data-slot='progress'
className={cn( className={cn(
'bg-primary/20 relative h-2 w-full overflow-hidden rounded-full', 'bg-primary/20 relative h-2 w-full overflow-hidden rounded-full',
className, className,
@@ -20,8 +20,8 @@ function Progress({
{...props} {...props}
> >
<ProgressPrimitive.Indicator <ProgressPrimitive.Indicator
data-slot="progress-indicator" data-slot='progress-indicator'
className="bg-primary h-full w-full flex-1 transition-all" className='bg-primary h-full w-full flex-1 transition-all'
style={{ transform: `translateX(-${100 - (value || 0)}%)` }} style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/> />
</ProgressPrimitive.Root> </ProgressPrimitive.Root>

View File

@@ -12,13 +12,13 @@ function ScrollArea({
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) { }: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
return ( return (
<ScrollAreaPrimitive.Root <ScrollAreaPrimitive.Root
data-slot="scroll-area" data-slot='scroll-area'
className={cn('relative', className)} className={cn('relative', className)}
{...props} {...props}
> >
<ScrollAreaPrimitive.Viewport <ScrollAreaPrimitive.Viewport
data-slot="scroll-area-viewport" data-slot='scroll-area-viewport'
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1" className='focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1'
> >
{children} {children}
</ScrollAreaPrimitive.Viewport> </ScrollAreaPrimitive.Viewport>
@@ -35,7 +35,7 @@ function ScrollBar({
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) { }: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
return ( return (
<ScrollAreaPrimitive.ScrollAreaScrollbar <ScrollAreaPrimitive.ScrollAreaScrollbar
data-slot="scroll-area-scrollbar" data-slot='scroll-area-scrollbar'
orientation={orientation} orientation={orientation}
className={cn( className={cn(
'flex touch-none p-px transition-colors select-none', 'flex touch-none p-px transition-colors select-none',
@@ -48,8 +48,8 @@ function ScrollBar({
{...props} {...props}
> >
<ScrollAreaPrimitive.ScrollAreaThumb <ScrollAreaPrimitive.ScrollAreaThumb
data-slot="scroll-area-thumb" data-slot='scroll-area-thumb'
className="bg-border relative flex-1 rounded-full" className='bg-border relative flex-1 rounded-full'
/> />
</ScrollAreaPrimitive.ScrollAreaScrollbar> </ScrollAreaPrimitive.ScrollAreaScrollbar>
); );

View File

@@ -13,7 +13,7 @@ function Separator({
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) { }: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
return ( return (
<SeparatorPrimitive.Root <SeparatorPrimitive.Root
data-slot="separator" data-slot='separator'
decorative={decorative} decorative={decorative}
orientation={orientation} orientation={orientation}
className={cn( className={cn(

View File

@@ -259,8 +259,8 @@ export const ImageCropContent = ({
> >
{imgSrc && ( {imgSrc && (
<img <img
alt="crop" alt='crop'
className="size-full" className='size-full'
onLoad={onImageLoad} onLoad={onImageLoad}
ref={imgRef} ref={imgRef}
src={imgSrc} src={imgSrc}
@@ -296,8 +296,8 @@ export const ImageCropApply = ({
} }
return ( return (
<Button onClick={handleClick} size="icon" variant="ghost" {...props}> <Button onClick={handleClick} size='icon' variant='ghost' {...props}>
{children ?? <CropIcon className="size-4" />} {children ?? <CropIcon className='size-4' />}
</Button> </Button>
); );
}; };
@@ -328,8 +328,8 @@ export const ImageCropReset = ({
} }
return ( return (
<Button onClick={handleClick} size="icon" variant="ghost" {...props}> <Button onClick={handleClick} size='icon' variant='ghost' {...props}>
{children ?? <RotateCcwIcon className="size-4" />} {children ?? <RotateCcwIcon className='size-4' />}
</Button> </Button>
); );
}; };

View File

@@ -10,7 +10,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
return ( return (
<Sonner <Sonner
theme={theme as ToasterProps['theme']} theme={theme as ToasterProps['theme']}
className="toaster group" className='toaster group'
style={ style={
{ {
'--normal-bg': 'var(--popover)', '--normal-bg': 'var(--popover)',

View File

@@ -16,7 +16,7 @@ export const StatusMessage = ({
textProps, textProps,
}: StatusMessageProps) => { }: StatusMessageProps) => {
return ( return (
<div className="flex w-full flex-col items-center"> <div className='flex w-full flex-col items-center'>
{'success' in message && ( {'success' in message && (
<div <div
{...containerProps} {...containerProps}

View File

@@ -26,7 +26,7 @@ export const SubmitButton = ({
const { pending } = useFormStatus(); const { pending } = useFormStatus();
return ( return (
<Button <Button
type="submit" type='submit'
aria-disabled={pending} aria-disabled={pending}
{...props} {...props}
className={cn('cursor-pointer', className)} className={cn('cursor-pointer', className)}

View File

@@ -11,7 +11,7 @@ function Switch({
}: React.ComponentProps<typeof SwitchPrimitive.Root>) { }: React.ComponentProps<typeof SwitchPrimitive.Root>) {
return ( return (
<SwitchPrimitive.Root <SwitchPrimitive.Root
data-slot="switch" data-slot='switch'
className={cn( className={cn(
'peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50', 'peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
className, className,
@@ -19,7 +19,7 @@ function Switch({
{...props} {...props}
> >
<SwitchPrimitive.Thumb <SwitchPrimitive.Thumb
data-slot="switch-thumb" data-slot='switch-thumb'
className={cn( className={cn(
'bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0', 'bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0',
)} )}

View File

@@ -7,11 +7,11 @@ import { cn } from '@gib/ui';
function Table({ className, ...props }: React.ComponentProps<'table'>) { function Table({ className, ...props }: React.ComponentProps<'table'>) {
return ( return (
<div <div
data-slot="table-container" data-slot='table-container'
className="relative w-full overflow-x-auto" className='relative w-full overflow-x-auto'
> >
<table <table
data-slot="table" data-slot='table'
className={cn('w-full caption-bottom text-sm', className)} className={cn('w-full caption-bottom text-sm', className)}
{...props} {...props}
/> />
@@ -22,7 +22,7 @@ function Table({ className, ...props }: React.ComponentProps<'table'>) {
function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) { function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
return ( return (
<thead <thead
data-slot="table-header" data-slot='table-header'
className={cn('[&_tr]:border-b', className)} className={cn('[&_tr]:border-b', className)}
{...props} {...props}
/> />
@@ -32,7 +32,7 @@ function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) { function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
return ( return (
<tbody <tbody
data-slot="table-body" data-slot='table-body'
className={cn('[&_tr:last-child]:border-0', className)} className={cn('[&_tr:last-child]:border-0', className)}
{...props} {...props}
/> />
@@ -42,7 +42,7 @@ function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) { function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
return ( return (
<tfoot <tfoot
data-slot="table-footer" data-slot='table-footer'
className={cn( className={cn(
'bg-muted/50 border-t font-medium [&>tr]:last:border-b-0', 'bg-muted/50 border-t font-medium [&>tr]:last:border-b-0',
className, className,
@@ -55,7 +55,7 @@ function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
function TableRow({ className, ...props }: React.ComponentProps<'tr'>) { function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
return ( return (
<tr <tr
data-slot="table-row" data-slot='table-row'
className={cn( className={cn(
'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors', 'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors',
className, className,
@@ -68,7 +68,7 @@ function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
function TableHead({ className, ...props }: React.ComponentProps<'th'>) { function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
return ( return (
<th <th
data-slot="table-head" data-slot='table-head'
className={cn( className={cn(
'text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', 'text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
className, className,
@@ -81,7 +81,7 @@ function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
function TableCell({ className, ...props }: React.ComponentProps<'td'>) { function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
return ( return (
<td <td
data-slot="table-cell" data-slot='table-cell'
className={cn( className={cn(
'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', 'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
className, className,
@@ -97,7 +97,7 @@ function TableCaption({
}: React.ComponentProps<'caption'>) { }: React.ComponentProps<'caption'>) {
return ( return (
<caption <caption
data-slot="table-caption" data-slot='table-caption'
className={cn('text-muted-foreground mt-4 text-sm', className)} className={cn('text-muted-foreground mt-4 text-sm', className)}
{...props} {...props}
/> />

View File

@@ -11,7 +11,7 @@ function Tabs({
}: React.ComponentProps<typeof TabsPrimitive.Root>) { }: React.ComponentProps<typeof TabsPrimitive.Root>) {
return ( return (
<TabsPrimitive.Root <TabsPrimitive.Root
data-slot="tabs" data-slot='tabs'
className={cn('flex flex-col gap-2', className)} className={cn('flex flex-col gap-2', className)}
{...props} {...props}
/> />
@@ -24,7 +24,7 @@ function TabsList({
}: React.ComponentProps<typeof TabsPrimitive.List>) { }: React.ComponentProps<typeof TabsPrimitive.List>) {
return ( return (
<TabsPrimitive.List <TabsPrimitive.List
data-slot="tabs-list" data-slot='tabs-list'
className={cn( className={cn(
'bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]', 'bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]',
className, className,
@@ -40,7 +40,7 @@ function TabsTrigger({
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) { }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
return ( return (
<TabsPrimitive.Trigger <TabsPrimitive.Trigger
data-slot="tabs-trigger" data-slot='tabs-trigger'
className={cn( className={cn(
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className, className,
@@ -56,7 +56,7 @@ function TabsContent({
}: React.ComponentProps<typeof TabsPrimitive.Content>) { }: React.ComponentProps<typeof TabsPrimitive.Content>) {
return ( return (
<TabsPrimitive.Content <TabsPrimitive.Content
data-slot="tabs-content" data-slot='tabs-content'
className={cn('flex-1 outline-none', className)} className={cn('flex-1 outline-none', className)}
{...props} {...props}
/> />

View File

@@ -49,19 +49,19 @@ const ThemeToggle = ({ size = 1, buttonProps }: ThemeToggleProps) => {
return ( return (
<Button <Button
variant="outline" variant='outline'
size="icon" size='icon'
{...buttonProps} {...buttonProps}
onClick={toggleTheme} onClick={toggleTheme}
className={cn('cursor-pointer', buttonProps?.className)} className={cn('cursor-pointer', buttonProps?.className)}
> >
<Sun <Sun
style={{ height: `${size}rem`, width: `${size}rem` }} style={{ height: `${size}rem`, width: `${size}rem` }}
className="scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" className='scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90'
/> />
<Moon <Moon
style={{ height: `${size}rem`, width: `${size}rem` }} style={{ height: `${size}rem`, width: `${size}rem` }}
className="absolute scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" className='absolute scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0'
/> />
</Button> </Button>
); );

View File

@@ -1 +1 @@
[["1","2","3","4","5","6"],{"key":"7","value":"8"},{"key":"9","value":"10"},{"key":"11","value":"12"},{"key":"13","value":"14"},{"key":"15","value":"16"},{"key":"17","value":"18"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/package.json",{"size":979,"mtime":1768166330000,"hash":"19","data":"20"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/nextjs.ts",{"size":440,"mtime":1768155639000,"hash":"21","data":"22"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/.cache/.prettiercache",{"size":1132,"mtime":1768171236844,"hash":"23"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/react.ts",{"size":592,"mtime":1768155639000,"hash":"24","data":"25"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/base.ts",{"size":2511,"mtime":1768155639000,"hash":"26","data":"27"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"28","data":"29"},"a5326aca75246da261fd2e354257b45a",{"hashOfOptions":"30"},"25c52c46972131dcc296288599ff108d",{"hashOfOptions":"31"},"26c40ae02d206ffdd279df170ea85cae","2292935ede6baf909f6a0c61486e15da",{"hashOfOptions":"32"},"9e432daa1849e911eb3c69df3d068c6d",{"hashOfOptions":"33"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"34"},"1786406903","2477157100","3372054421","3828872695","459831536"] [["1","2","3","4","5"],{"key":"6","value":"7"},{"key":"8","value":"9"},{"key":"10","value":"11"},{"key":"12","value":"13"},{"key":"14","value":"15"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/package.json",{"size":979,"mtime":1768166330000,"hash":"16","data":"17"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/nextjs.ts",{"size":440,"mtime":1768155639000,"hash":"18","data":"19"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/react.ts",{"size":592,"mtime":1768155639000,"hash":"20","data":"21"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/base.ts",{"size":2508,"mtime":1768320541000,"hash":"22","data":"23"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"24","data":"25"},"a5326aca75246da261fd2e354257b45a",{"hashOfOptions":"26"},"25c52c46972131dcc296288599ff108d",{"hashOfOptions":"27"},"2292935ede6baf909f6a0c61486e15da",{"hashOfOptions":"28"},"6a779439826cf31b5561a21273d134a9",{"hashOfOptions":"29"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"30"},"1249441784","4234667885","3945480854","3486090744","681484145"]

View File

@@ -1 +1 @@
[["1","2","3","4"],{"key":"5","value":"6"},{"key":"7","value":"8"},{"key":"9","value":"10"},{"key":"11","value":"12"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"13","data":"14"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/package.json",{"size":607,"mtime":1766222924000,"hash":"15","data":"16"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/.cache/.prettiercache",{"size":685,"mtime":1768171236844,"hash":"17"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/index.js",{"size":1170,"mtime":1768155639000,"hash":"18","data":"19"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"20"},"11b634ce56ac720ac9a2860d77fbd2cc",{"hashOfOptions":"21"},"bd15cce9406aeef5526bb5681494acce","d6f2202018912e47fdd2016f04a4b6eb",{"hashOfOptions":"22"},"1555666866","640383157","507270250"] [["1","2","3"],{"key":"4","value":"5"},{"key":"6","value":"7"},{"key":"8","value":"9"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"10","data":"11"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/package.json",{"size":607,"mtime":1766222924000,"hash":"12","data":"13"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/index.js",{"size":1194,"mtime":1768372320442,"hash":"14","data":"15"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"16"},"11b634ce56ac720ac9a2860d77fbd2cc",{"hashOfOptions":"17"},"ecbaa91166a940dfcec8117059f52402",{"hashOfOptions":"18"},"67859251","550572982","1674623979"]

View File

@@ -5,6 +5,7 @@
/** @type { PrettierConfig | SortImportsConfig | TailwindConfig } */ /** @type { PrettierConfig | SortImportsConfig | TailwindConfig } */
const config = { const config = {
singleQuote: true, singleQuote: true,
jsxSingleQuote: true,
trailingComma: 'all', trailingComma: 'all',
tabWidth: 2, tabWidth: 2,
printWidth: 80, printWidth: 80,

View File

@@ -1 +1 @@
[["1","2","3","4","5","6"],{"key":"7","value":"8"},{"key":"9","value":"10"},{"key":"11","value":"12"},{"key":"13","value":"14"},{"key":"15","value":"16"},{"key":"17","value":"18"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/.cache/.prettiercache",{"size":1160,"mtime":1768155639602},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/package.json",{"size":851,"mtime":1766222924000,"hash":"19","data":"20"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"21","data":"22"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/eslint.config.ts",{"size":143,"mtime":1768155639344,"hash":"23","data":"24"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/theme.css",{"size":7273,"mtime":1768320378203,"hash":"25","data":"26"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/postcss-config.js",{"size":70,"mtime":1768155639454,"hash":"27","data":"28"},"0d22e47f57739db9de04c6f8420d6fb5",{"hashOfOptions":"29"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"30"},"b8fec960cb32340eea62ca1485093e68",{"hashOfOptions":"31"},"5dd421d25d104c47e1ab36df41ed0f7d",{"hashOfOptions":"32"},"9a944fbda06979be39571bd9bd00b0d9",{"hashOfOptions":"33"},"2330637293","1661020794","1348931563","2703662435","1371209456"] [["1","2","3","4","5"],{"key":"6","value":"7"},{"key":"8","value":"9"},{"key":"10","value":"11"},{"key":"12","value":"13"},{"key":"14","value":"15"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/package.json",{"size":851,"mtime":1766222924000,"hash":"16","data":"17"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/postcss-config.js",{"size":70,"mtime":1768155639000,"hash":"18","data":"19"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/eslint.config.ts",{"size":143,"mtime":1768155639000,"hash":"20","data":"21"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/theme.css",{"size":7273,"mtime":1768320378000,"hash":"22","data":"23"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"24","data":"25"},"0d22e47f57739db9de04c6f8420d6fb5",{"hashOfOptions":"26"},"9a944fbda06979be39571bd9bd00b0d9",{"hashOfOptions":"27"},"b8fec960cb32340eea62ca1485093e68",{"hashOfOptions":"28"},"e40c2569ef375a9c828c80c0f9ce1bf2",{"hashOfOptions":"29"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"30"},"34296497","1053475182","4101125807","919761249","2958815992"]