Update prettier
This commit is contained in:
@@ -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"]
|
||||||
@@ -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}
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 />
|
||||||
|
|||||||
@@ -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;
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
{userProvider === 'email' ? (
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Your email address for account notifications
|
Your email address for account notifications
|
||||||
</FormDescription>
|
</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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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={{
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
23
bun.lock
23
bun.lock
@@ -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=="],
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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]',
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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)',
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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)}
|
||||||
|
|||||||
@@ -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',
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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"]
|
||||||
@@ -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"]
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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"]
|
||||||
Reference in New Issue
Block a user