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;
|
||||
}) {
|
||||
return (
|
||||
<View className="bg-muted flex flex-row rounded-lg p-4">
|
||||
<View className="grow">
|
||||
<View className='bg-muted flex flex-row rounded-lg p-4'>
|
||||
<View className='grow'>
|
||||
<Link
|
||||
asChild
|
||||
href={{
|
||||
@@ -23,16 +23,16 @@ function PostCard(props: {
|
||||
params: { id: props.post.id },
|
||||
}}
|
||||
>
|
||||
<Pressable className="">
|
||||
<Text className="text-primary text-xl font-semibold">
|
||||
<Pressable className=''>
|
||||
<Text className='text-primary text-xl font-semibold'>
|
||||
{props.post.title}
|
||||
</Text>
|
||||
<Text className="text-foreground mt-2">{props.post.content}</Text>
|
||||
<Text className='text-foreground mt-2'>{props.post.content}</Text>
|
||||
</Pressable>
|
||||
</Link>
|
||||
</View>
|
||||
<Pressable onPress={props.onDelete}>
|
||||
<Text className="text-primary font-bold uppercase">Delete</Text>
|
||||
<Text className='text-primary font-bold uppercase'>Delete</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
@@ -55,31 +55,31 @@ function CreatePost() {
|
||||
);
|
||||
|
||||
return (
|
||||
<View className="mt-4 flex gap-2">
|
||||
<View className='mt-4 flex gap-2'>
|
||||
<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}
|
||||
onChangeText={setTitle}
|
||||
placeholder="Title"
|
||||
placeholder='Title'
|
||||
/>
|
||||
{error?.data?.zodError?.fieldErrors.title && (
|
||||
<Text className="text-destructive mb-2">
|
||||
<Text className='text-destructive mb-2'>
|
||||
{error.data.zodError.fieldErrors.title}
|
||||
</Text>
|
||||
)}
|
||||
<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}
|
||||
onChangeText={setContent}
|
||||
placeholder="Content"
|
||||
placeholder='Content'
|
||||
/>
|
||||
{error?.data?.zodError?.fieldErrors.content && (
|
||||
<Text className="text-destructive mb-2">
|
||||
<Text className='text-destructive mb-2'>
|
||||
{error.data.zodError.fieldErrors.content}
|
||||
</Text>
|
||||
)}
|
||||
<Pressable
|
||||
className="bg-primary flex items-center rounded-sm p-2"
|
||||
className='bg-primary flex items-center rounded-sm p-2'
|
||||
onPress={() => {
|
||||
mutate({
|
||||
title,
|
||||
@@ -87,10 +87,10 @@ function CreatePost() {
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Text className="text-foreground">Create</Text>
|
||||
<Text className='text-foreground'>Create</Text>
|
||||
</Pressable>
|
||||
{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
|
||||
</Text>
|
||||
)}
|
||||
@@ -103,7 +103,7 @@ function MobileAuth() {
|
||||
|
||||
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'}
|
||||
</Text>
|
||||
<Pressable
|
||||
@@ -115,7 +115,7 @@ function MobileAuth() {
|
||||
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>
|
||||
</Pressable>
|
||||
@@ -136,18 +136,18 @@ export default function Index() {
|
||||
);
|
||||
|
||||
return (
|
||||
<SafeAreaView className="bg-background">
|
||||
<SafeAreaView className='bg-background'>
|
||||
{/* Changes page title visible on the header */}
|
||||
<Stack.Screen options={{ title: 'Home Page' }} />
|
||||
<View className="bg-background h-full w-full p-4">
|
||||
<Text className="text-foreground pb-2 text-center text-5xl font-bold">
|
||||
Create <Text className="text-primary">T3</Text> Turbo
|
||||
<View className='bg-background h-full w-full p-4'>
|
||||
<Text className='text-foreground pb-2 text-center text-5xl font-bold'>
|
||||
Create <Text className='text-primary'>T3</Text> Turbo
|
||||
</Text>
|
||||
|
||||
<MobileAuth />
|
||||
|
||||
<View className="py-2">
|
||||
<Text className="text-primary font-semibold italic">
|
||||
<View className='py-2'>
|
||||
<Text className='text-primary font-semibold italic'>
|
||||
Press on a post
|
||||
</Text>
|
||||
</View>
|
||||
@@ -156,7 +156,7 @@ export default function Index() {
|
||||
data={postQuery.data ?? []}
|
||||
estimatedItemSize={20}
|
||||
keyExtractor={(item) => item.id}
|
||||
ItemSeparatorComponent={() => <View className="h-2" />}
|
||||
ItemSeparatorComponent={() => <View className='h-2' />}
|
||||
renderItem={(p) => (
|
||||
<PostCard
|
||||
post={p.item}
|
||||
|
||||
@@ -11,13 +11,13 @@ export default function Post() {
|
||||
if (!data) return null;
|
||||
|
||||
return (
|
||||
<SafeAreaView className="bg-background">
|
||||
<SafeAreaView className='bg-background'>
|
||||
<Stack.Screen options={{ title: data.title }} />
|
||||
<View className="h-full w-full p-4">
|
||||
<Text className="text-primary py-2 text-3xl font-bold">
|
||||
<View className='h-full w-full p-4'>
|
||||
<Text className='text-primary py-2 text-3xl font-bold'>
|
||||
{data.title}
|
||||
</Text>
|
||||
<Text className="text-foreground py-4">{data.content}</Text>
|
||||
<Text className='text-foreground py-4'>{data.content}</Text>
|
||||
</View>
|
||||
</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 (
|
||||
<div className="flex flex-col items-center">
|
||||
<Card className="bg-card/25 min-h-[400px] w-sm p-4 lg:w-md">
|
||||
<CardHeader className="flex flex-col items-center gap-4">
|
||||
<div className='flex flex-col items-center'>
|
||||
<Card className='bg-card/25 min-h-[400px] w-sm p-4 lg:w-md'>
|
||||
<CardHeader className='flex flex-col items-center gap-4'>
|
||||
{flow === 'reset' ? (
|
||||
<>
|
||||
<CardTitle className="text-2xl font-bold">
|
||||
<CardTitle className='text-2xl font-bold'>
|
||||
Forgot Password
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
@@ -147,7 +147,7 @@ const ForgotPassword = () => {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CardTitle className="text-2xl font-bold">
|
||||
<CardTitle className='text-2xl font-bold'>
|
||||
Reset Password
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
@@ -158,7 +158,7 @@ const ForgotPassword = () => {
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Card className="bg-card/50">
|
||||
<Card className='bg-card/50'>
|
||||
<CardContent>
|
||||
{flow === 'reset' ? (
|
||||
<Form {...forgotPasswordForm}>
|
||||
@@ -166,31 +166,31 @@ const ForgotPassword = () => {
|
||||
onSubmit={forgotPasswordForm.handleSubmit(
|
||||
handleForgotPasswordSubmit,
|
||||
)}
|
||||
className="flex flex-col space-y-4"
|
||||
className='flex flex-col space-y-4'
|
||||
>
|
||||
<FormField
|
||||
control={forgotPasswordForm.control}
|
||||
name="email"
|
||||
name='email'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-xl">Email</FormLabel>
|
||||
<FormLabel className='text-xl'>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="you@example.com"
|
||||
type='email'
|
||||
placeholder='you@example.com'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<SubmitButton
|
||||
disabled={loading}
|
||||
pendingText="Sending Email..."
|
||||
className="mx-auto w-2/3 text-xl font-semibold"
|
||||
pendingText='Sending Email...'
|
||||
className='mx-auto w-2/3 text-xl font-semibold'
|
||||
>
|
||||
Send Email
|
||||
</SubmitButton>
|
||||
@@ -202,14 +202,14 @@ const ForgotPassword = () => {
|
||||
onSubmit={resetVerificationForm.handleSubmit(
|
||||
handleResetVerificationSubmit,
|
||||
)}
|
||||
className="flex flex-col space-y-4"
|
||||
className='flex flex-col space-y-4'
|
||||
>
|
||||
<FormField
|
||||
control={resetVerificationForm.control}
|
||||
name="code"
|
||||
name='code'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-xl">Code</FormLabel>
|
||||
<FormLabel className='text-xl'>Code</FormLabel>
|
||||
<FormControl>
|
||||
<InputOTP
|
||||
maxLength={6}
|
||||
@@ -232,58 +232,58 @@ const ForgotPassword = () => {
|
||||
Please enter the one-time password sent to your
|
||||
phone.
|
||||
</FormDescription>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={resetVerificationForm.control}
|
||||
name="newPassword"
|
||||
name='newPassword'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-xl">
|
||||
<FormLabel className='text-xl'>
|
||||
New Password
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Your password"
|
||||
type='password'
|
||||
placeholder='Your password'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={resetVerificationForm.control}
|
||||
name="confirmPassword"
|
||||
name='confirmPassword'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-xl">
|
||||
<FormLabel className='text-xl'>
|
||||
Confirm Passsword
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Confirm your password"
|
||||
type='password'
|
||||
placeholder='Confirm your password'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<SubmitButton
|
||||
disabled={loading}
|
||||
pendingText="Resetting Password..."
|
||||
className="mx-auto w-2/3 text-xl font-semibold"
|
||||
pendingText='Resetting Password...'
|
||||
className='mx-auto w-2/3 text-xl font-semibold'
|
||||
>
|
||||
Reset Password
|
||||
</SubmitButton>
|
||||
|
||||
@@ -14,28 +14,34 @@ import { Card, Separator } from '@gib/ui';
|
||||
|
||||
const Profile = async () => {
|
||||
const preloadedUser = await preloadQuery(api.auth.getUser, {});
|
||||
const preloadedUserProvider = await preloadQuery(api.auth.getUserProvider, {});
|
||||
const preloadedUserProvider = await preloadQuery(
|
||||
api.auth.getUserProvider,
|
||||
{},
|
||||
);
|
||||
return (
|
||||
<main className="container mx-auto px-4 py-12 md:py-16">
|
||||
<div className="mx-auto max-w-3xl">
|
||||
<main className='container mx-auto px-4 py-12 md:py-16'>
|
||||
<div className='mx-auto max-w-3xl'>
|
||||
{/* Page Header */}
|
||||
<div className="mb-8 text-center">
|
||||
<h1 className="mb-2 text-3xl font-bold tracking-tight sm:text-4xl">
|
||||
<div className='mb-8 text-center'>
|
||||
<h1 className='mb-2 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Your Profile
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
<p className='text-muted-foreground'>
|
||||
Manage your personal information and preferences
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Profile Card */}
|
||||
<Card className="border-border/40">
|
||||
<Card className='border-border/40'>
|
||||
<ProfileHeader preloadedUser={preloadedUser} />
|
||||
<AvatarUpload preloadedUser={preloadedUser} />
|
||||
<Separator className="my-6" />
|
||||
<UserInfoForm preloadedUser={preloadedUser} preloadedProvider={preloadedUserProvider} />
|
||||
<Separator className='my-6' />
|
||||
<UserInfoForm
|
||||
preloadedUser={preloadedUser}
|
||||
preloadedProvider={preloadedUserProvider}
|
||||
/>
|
||||
<ResetPasswordForm preloadedProvider={preloadedUserProvider} />
|
||||
<Separator className="my-6" />
|
||||
<Separator className='my-6' />
|
||||
<SignOutForm />
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -179,24 +179,24 @@ const SignIn = () => {
|
||||
|
||||
if (flow === 'email-verification') {
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<Card className="bg-card/25 min-h-[720px] w-md p-4">
|
||||
<div className='flex flex-col items-center'>
|
||||
<Card className='bg-card/25 min-h-[720px] w-md p-4'>
|
||||
<CardContent>
|
||||
<div className="mb-6 text-center">
|
||||
<h2 className="text-2xl font-bold">Verify Your Email</h2>
|
||||
<p className="text-muted-foreground">We sent a code to {email}</p>
|
||||
<div className='mb-6 text-center'>
|
||||
<h2 className='text-2xl font-bold'>Verify Your Email</h2>
|
||||
<p className='text-muted-foreground'>We sent a code to {email}</p>
|
||||
</div>
|
||||
<Form {...verifyEmailForm}>
|
||||
<form
|
||||
onSubmit={verifyEmailForm.handleSubmit(handleVerifyEmail)}
|
||||
className="flex flex-col space-y-8"
|
||||
className='flex flex-col space-y-8'
|
||||
>
|
||||
<FormField
|
||||
control={verifyEmailForm.control}
|
||||
name="code"
|
||||
name='code'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-xl">Code</FormLabel>
|
||||
<FormLabel className='text-xl'>Code</FormLabel>
|
||||
<FormControl>
|
||||
<InputOTP
|
||||
maxLength={6}
|
||||
@@ -217,25 +217,25 @@ const SignIn = () => {
|
||||
<FormDescription>
|
||||
Please enter the one-time password sent to your email.
|
||||
</FormDescription>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<SubmitButton
|
||||
disabled={loading}
|
||||
pendingText="Signing Up..."
|
||||
className="mx-auto w-2/3 text-xl font-semibold"
|
||||
pendingText='Signing Up...'
|
||||
className='mx-auto w-2/3 text-xl font-semibold'
|
||||
>
|
||||
Verify Email
|
||||
</SubmitButton>
|
||||
</form>
|
||||
</Form>
|
||||
<div className="mt-4 text-center">
|
||||
<div className='mt-4 text-center'>
|
||||
<button
|
||||
onClick={() => setFlow('signUp')}
|
||||
className="text-muted-foreground text-sm hover:underline"
|
||||
className='text-muted-foreground text-sm hover:underline'
|
||||
>
|
||||
Back to Sign Up
|
||||
</button>
|
||||
@@ -247,204 +247,204 @@ const SignIn = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<Card className="bg-card/25 min-h-[720px] w-md p-4">
|
||||
<div className='flex flex-col items-center'>
|
||||
<Card className='bg-card/25 min-h-[720px] w-md p-4'>
|
||||
<Tabs
|
||||
defaultValue={flow}
|
||||
onValueChange={(value) => setFlow(value as 'signIn' | 'signUp')}
|
||||
className="items-center"
|
||||
className='items-center'
|
||||
>
|
||||
<TabsList className="py-6">
|
||||
<TabsList className='py-6'>
|
||||
<TabsTrigger
|
||||
value="signIn"
|
||||
className="cursor-pointer p-6 text-2xl font-bold"
|
||||
value='signIn'
|
||||
className='cursor-pointer p-6 text-2xl font-bold'
|
||||
>
|
||||
Sign In
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="signUp"
|
||||
className="cursor-pointer p-6 text-2xl font-bold"
|
||||
value='signUp'
|
||||
className='cursor-pointer p-6 text-2xl font-bold'
|
||||
>
|
||||
Sign Up
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="signIn">
|
||||
<Card className="bg-card/50 min-w-xs sm:min-w-sm">
|
||||
<TabsContent value='signIn'>
|
||||
<Card className='bg-card/50 min-w-xs sm:min-w-sm'>
|
||||
<CardContent>
|
||||
<Form {...signInForm}>
|
||||
<form
|
||||
onSubmit={signInForm.handleSubmit(handleSignIn)}
|
||||
className="flex flex-col space-y-8"
|
||||
className='flex flex-col space-y-8'
|
||||
>
|
||||
<FormField
|
||||
control={signInForm.control}
|
||||
name="email"
|
||||
name='email'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-xl">Email</FormLabel>
|
||||
<FormLabel className='text-xl'>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="you@example.com"
|
||||
type='email'
|
||||
placeholder='you@example.com'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={signInForm.control}
|
||||
name="password"
|
||||
name='password'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex justify-between">
|
||||
<FormLabel className="text-xl">Password</FormLabel>
|
||||
<Link href="/forgot-password">
|
||||
<div className='flex justify-between'>
|
||||
<FormLabel className='text-xl'>Password</FormLabel>
|
||||
<Link href='/forgot-password'>
|
||||
Forgot Password?
|
||||
</Link>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Your password"
|
||||
type='password'
|
||||
placeholder='Your password'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<SubmitButton
|
||||
disabled={loading}
|
||||
pendingText="Signing in..."
|
||||
className="mx-auto w-2/3 text-xl font-semibold"
|
||||
pendingText='Signing in...'
|
||||
className='mx-auto w-2/3 text-xl font-semibold'
|
||||
>
|
||||
Sign In
|
||||
</SubmitButton>
|
||||
</form>
|
||||
</Form>
|
||||
<div className="flex 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" />
|
||||
<span className="text-lg font-semibold">or</span>
|
||||
<Separator className="ml-3 py-0.5" />
|
||||
<div className='flex 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' />
|
||||
<span className='text-lg font-semibold'>or</span>
|
||||
<Separator className='ml-3 py-0.5' />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex justify-center">
|
||||
<div className='mt-3 flex justify-center'>
|
||||
<GibsAuthSignInButton />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
<TabsContent value="signUp">
|
||||
<Card className="bg-card/50 min-w-xs sm:min-w-sm">
|
||||
<TabsContent value='signUp'>
|
||||
<Card className='bg-card/50 min-w-xs sm:min-w-sm'>
|
||||
<CardContent>
|
||||
<Form {...signUpForm}>
|
||||
<form
|
||||
onSubmit={signUpForm.handleSubmit(handleSignUp)}
|
||||
className="flex flex-col space-y-8"
|
||||
className='flex flex-col space-y-8'
|
||||
>
|
||||
<FormField
|
||||
control={signUpForm.control}
|
||||
name="name"
|
||||
name='name'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-xl">Name</FormLabel>
|
||||
<FormLabel className='text-xl'>Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Full Name"
|
||||
type='text'
|
||||
placeholder='Full Name'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={signUpForm.control}
|
||||
name="email"
|
||||
name='email'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-xl">Email</FormLabel>
|
||||
<FormLabel className='text-xl'>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="you@example.com"
|
||||
type='email'
|
||||
placeholder='you@example.com'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={signUpForm.control}
|
||||
name="password"
|
||||
name='password'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-xl">Password</FormLabel>
|
||||
<FormLabel className='text-xl'>Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Your password"
|
||||
type='password'
|
||||
placeholder='Your password'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={signUpForm.control}
|
||||
name="confirmPassword"
|
||||
name='confirmPassword'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="text-xl">
|
||||
<FormLabel className='text-xl'>
|
||||
Confirm Passsword
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Confirm your password"
|
||||
type='password'
|
||||
placeholder='Confirm your password'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<FormMessage className="w-5/6 text-center" />
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
<FormMessage className='w-5/6 text-center' />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<SubmitButton
|
||||
disabled={loading}
|
||||
pendingText="Signing Up..."
|
||||
className="mx-auto w-2/3 text-xl font-semibold"
|
||||
pendingText='Signing Up...'
|
||||
className='mx-auto w-2/3 text-xl font-semibold'
|
||||
>
|
||||
Sign Up
|
||||
</SubmitButton>
|
||||
</form>
|
||||
</Form>
|
||||
<div className="my-auto flex w-2/3 justify-center">
|
||||
<div className="my-2.5 flex w-1/3 flex-row items-center">
|
||||
<Separator className="mr-3 py-0.5" />
|
||||
<span className="text-lg font-semibold">or</span>
|
||||
<Separator className="ml-3 py-0.5" />
|
||||
<div className='my-auto flex w-2/3 justify-center'>
|
||||
<div className='my-2.5 flex w-1/3 flex-row items-center'>
|
||||
<Separator className='mr-3 py-0.5' />
|
||||
<span className='text-lg font-semibold'>or</span>
|
||||
<Separator className='ml-3 py-0.5' />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex justify-center">
|
||||
<GibsAuthSignInButton type="signUp" />
|
||||
<div className='mt-3 flex justify-center'>
|
||||
<GibsAuthSignInButton type='signUp' />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
import type { Metadata, Viewport } from 'next';
|
||||
import NextError from 'next/error';
|
||||
import { Geist, Geist_Mono } from 'next/font/google';
|
||||
|
||||
import '@/app/styles.css';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import Footer from '@/components/layout/footer';
|
||||
import Header from '@/components/layout/header';
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
import { ConvexClientProvider } from '@/components/providers';
|
||||
import { generateMetadata } from '@/lib/metadata';
|
||||
import * as Sentry from '@sentry/nextjs';
|
||||
import PlausibleProvider from 'next-plausible';
|
||||
|
||||
import { Button, ThemeProvider, Toaster } from '@gib/ui';
|
||||
@@ -35,7 +37,7 @@ const geistMono = Geist_Mono({
|
||||
interface GlobalErrorProps {
|
||||
error: Error & { digest?: string };
|
||||
reset?: () => void;
|
||||
};
|
||||
}
|
||||
|
||||
const GlobalError = ({ error, reset = undefined }: GlobalErrorProps) => {
|
||||
useEffect(() => {
|
||||
@@ -43,21 +45,21 @@ const GlobalError = ({ error, reset = undefined }: GlobalErrorProps) => {
|
||||
}, [error]);
|
||||
return (
|
||||
<PlausibleProvider
|
||||
domain="convexmonorepo.gbrown.org"
|
||||
customDomain="https://plausible.gbrown.org"
|
||||
domain='convexmonorepo.gbrown.org'
|
||||
customDomain='https://plausible.gbrown.org'
|
||||
>
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<html lang='en' suppressHydrationWarning>
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
attribute='class'
|
||||
defaultTheme='system'
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<ConvexClientProvider>
|
||||
<main className="flex min-h-screen flex-col items-center">
|
||||
<main className='flex min-h-screen flex-col items-center'>
|
||||
<Header />
|
||||
<NextError statusCode={0} />
|
||||
{reset !== undefined && (
|
||||
@@ -66,7 +68,7 @@ const GlobalError = ({ error, reset = undefined }: GlobalErrorProps) => {
|
||||
<Toaster />
|
||||
<Footer />
|
||||
</main>
|
||||
<main className="flex min-h-[90vh] flex-col items-center">
|
||||
<main className='flex min-h-[90vh] flex-col items-center'>
|
||||
<Toaster />
|
||||
</main>
|
||||
</ConvexClientProvider>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Metadata, Viewport } from 'next';
|
||||
import { Geist, Geist_Mono } from 'next/font/google';
|
||||
import { env } from '@/env';
|
||||
|
||||
import '@/app/styles.css';
|
||||
|
||||
@@ -38,23 +39,23 @@ const RootLayout = ({
|
||||
return (
|
||||
<ConvexAuthNextjsServerProvider>
|
||||
<PlausibleProvider
|
||||
domain="convexmonorepo.gbrown.org"
|
||||
customDomain="https://plausible.gbrown.org"
|
||||
domain={env.NEXT_PUBLIC_SITE_URL}
|
||||
customDomain={env.NEXT_PUBLIC_PLAUSIBLE_URL}
|
||||
>
|
||||
<html lang="en">
|
||||
<html lang='en'>
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
attribute='class'
|
||||
defaultTheme='system'
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<ConvexClientProvider>
|
||||
<div className="flex min-h-screen flex-col">
|
||||
<div className='flex min-h-screen flex-col'>
|
||||
<Header />
|
||||
<div className="flex-1">{children}</div>
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
<Toaster />
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { CTA, Features, Hero, TechStack } from '@/components/landing';
|
||||
|
||||
const Home = async () => {
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col">
|
||||
<main className='flex min-h-screen flex-col'>
|
||||
<Hero />
|
||||
<Features />
|
||||
<TechStack />
|
||||
<CTA />
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
}
|
||||
|
||||
@@ -5,24 +5,24 @@ import { Button } from '@gib/ui/button';
|
||||
|
||||
export function CTA() {
|
||||
return (
|
||||
<section className="container mx-auto px-4 py-24">
|
||||
<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">
|
||||
<h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl">
|
||||
<section className='container mx-auto px-4 py-24'>
|
||||
<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'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl'>
|
||||
Ready to Build Something Amazing?
|
||||
</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
|
||||
everything pre-configured.
|
||||
</p>
|
||||
|
||||
{/* Quick Start Command */}
|
||||
<div className="mt-12">
|
||||
<p className="text-muted-foreground mb-3 text-sm font-medium">
|
||||
<div className='mt-12'>
|
||||
<p className='text-muted-foreground mb-3 text-sm font-medium'>
|
||||
Quick Start
|
||||
</p>
|
||||
<div className="border-border/40 bg-background mx-auto max-w-2xl rounded-lg border p-4">
|
||||
<code className="text-sm">
|
||||
<div className='border-border/40 bg-background mx-auto max-w-2xl rounded-lg border p-4'>
|
||||
<code className='text-sm'>
|
||||
git clone https://git.gbrown.org/gib/convex-monorepo.git
|
||||
<br />
|
||||
cd convex-monorepo
|
||||
|
||||
@@ -59,29 +59,29 @@ const features = [
|
||||
|
||||
export function Features() {
|
||||
return (
|
||||
<section id="features" className="container mx-auto px-4 py-24">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<section id='features' className='container mx-auto px-4 py-24'>
|
||||
<div className='mx-auto max-w-6xl'>
|
||||
{/* Section Header */}
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl">
|
||||
<div className='mb-16 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl'>
|
||||
Everything You Need to Ship Fast
|
||||
</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
|
||||
need for production-ready applications.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 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) => (
|
||||
<Card key={feature.title} className="border-border/40">
|
||||
<Card key={feature.title} className='border-border/40'>
|
||||
<CardHeader className='flex items-center gap-2'>
|
||||
<div className="mb-2 text-3xl">{feature.icon}</div>
|
||||
<CardTitle className="text-xl">{feature.title}</CardTitle>
|
||||
<div className='mb-2 text-3xl'>{feature.icon}</div>
|
||||
<CardTitle className='text-xl'>{feature.title}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-muted-foreground">{feature.description}</p>
|
||||
<p className='text-muted-foreground'>{feature.description}</p>
|
||||
</CardContent>
|
||||
</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 Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Button } from '@gib/ui/button';
|
||||
|
||||
const kanitSans = Kanit({
|
||||
subsets: ['latin'],
|
||||
@@ -10,36 +11,38 @@ const kanitSans = Kanit({
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<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">
|
||||
<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'>
|
||||
{/* Badge */}
|
||||
<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>
|
||||
<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>Production-ready monorepo template</span>
|
||||
</div>
|
||||
|
||||
{/* 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{' '}
|
||||
<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
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
{/* 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
|
||||
web and mobile apps faster with shared code, type-safe backend, and
|
||||
complete control over your infrastructure.
|
||||
</p>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
<div className="flex flex-col gap-3 sm:flex-row">
|
||||
<Button size="lg" variant="outline" asChild>
|
||||
<div className='flex flex-col gap-3 sm:flex-row'>
|
||||
<Button size='lg' variant='outline' asChild>
|
||||
<Link
|
||||
href="https://git.gbrown.org/gib/convex-monorepo"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href='https://git.gbrown.org/gib/convex-monorepo'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<Image
|
||||
src='/misc/gitea/gitea.svg'
|
||||
@@ -53,67 +56,67 @@ export function Hero() {
|
||||
</div>
|
||||
|
||||
{/* Features Quick List */}
|
||||
<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='text-muted-foreground mt-8 flex flex-wrap items-center justify-center gap-6 text-sm'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<svg
|
||||
className="h-5 w-5 text-green-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className='h-5 w-5 text-green-500'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
stroke='currentColor'
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth={2}
|
||||
d="M5 13l4 4L19 7"
|
||||
d='M5 13l4 4L19 7'
|
||||
/>
|
||||
</svg>
|
||||
<span>TypeScript</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className='flex items-center gap-2'>
|
||||
<svg
|
||||
className="h-5 w-5 text-green-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className='h-5 w-5 text-green-500'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
stroke='currentColor'
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth={2}
|
||||
d="M5 13l4 4L19 7"
|
||||
d='M5 13l4 4L19 7'
|
||||
/>
|
||||
</svg>
|
||||
<span>Self-Hosted</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className='flex items-center gap-2'>
|
||||
<svg
|
||||
className="h-5 w-5 text-green-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className='h-5 w-5 text-green-500'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
stroke='currentColor'
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth={2}
|
||||
d="M5 13l4 4L19 7"
|
||||
d='M5 13l4 4L19 7'
|
||||
/>
|
||||
</svg>
|
||||
<span>Real-time</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className='flex items-center gap-2'>
|
||||
<svg
|
||||
className="h-5 w-5 text-green-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className='h-5 w-5 text-green-500'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
stroke='currentColor'
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth={2}
|
||||
d="M5 13l4 4L19 7"
|
||||
d='M5 13l4 4L19 7'
|
||||
/>
|
||||
</svg>
|
||||
<span>Auth Included</span>
|
||||
|
||||
@@ -38,32 +38,32 @@ const techStack = [
|
||||
|
||||
export function TechStack() {
|
||||
return (
|
||||
<section id="tech-stack" className="border-border/40 bg-muted/30 border-t">
|
||||
<div className="container mx-auto px-4 py-24">
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<section id='tech-stack' className='border-border/40 bg-muted/30 border-t'>
|
||||
<div className='container mx-auto px-4 py-24'>
|
||||
<div className='mx-auto max-w-6xl'>
|
||||
{/* Section Header */}
|
||||
<div className="mb-16 text-center">
|
||||
<h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl">
|
||||
<div className='mb-16 text-center'>
|
||||
<h2 className='mb-4 text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl'>
|
||||
Modern Tech Stack
|
||||
</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
|
||||
and performance.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Tech Stack Grid */}
|
||||
<div className="grid gap-12 md:grid-cols-3">
|
||||
<div className='grid gap-12 md:grid-cols-3'>
|
||||
{techStack.map((stack) => (
|
||||
<div key={stack.category}>
|
||||
<h3 className="mb-6 text-xl font-semibold">{stack.category}</h3>
|
||||
<ul className="space-y-4">
|
||||
<h3 className='mb-6 text-xl font-semibold'>{stack.category}</h3>
|
||||
<ul className='space-y-4'>
|
||||
{stack.technologies.map((tech) => (
|
||||
<li key={tech.name}>
|
||||
<div className="text-foreground font-medium">
|
||||
<div className='text-foreground font-medium'>
|
||||
{tech.name}
|
||||
</div>
|
||||
<div className="text-muted-foreground text-sm">
|
||||
<div className='text-muted-foreground text-sm'>
|
||||
{tech.description}
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -21,15 +21,15 @@ export const GibsAuthSignInButton = ({
|
||||
const { signIn } = useAuthActions();
|
||||
return (
|
||||
<Button
|
||||
size="lg"
|
||||
size='lg'
|
||||
onClick={() => signIn('authentik')}
|
||||
className="text-lg font-semibold"
|
||||
className='text-lg font-semibold'
|
||||
{...buttonProps}
|
||||
>
|
||||
<div className="my-auto flex flex-row space-x-1">
|
||||
<div className='my-auto flex flex-row space-x-1'>
|
||||
<Image
|
||||
src={'/misc/auth/gibs-auth-logo.png'}
|
||||
className=""
|
||||
className=''
|
||||
alt="Gib's Auth"
|
||||
width={30}
|
||||
height={30}
|
||||
|
||||
@@ -50,7 +50,7 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
|
||||
const currentImageUrl = useQuery(
|
||||
api.files.getImageUrl,
|
||||
user?.image ? { storageId: user.image } : 'skip',
|
||||
user?.image ? { storageId: user.image as Id<'_storage'> } : 'skip',
|
||||
);
|
||||
|
||||
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -109,28 +109,28 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
|
||||
return (
|
||||
<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) */}
|
||||
{!selectedFile && (
|
||||
<div
|
||||
className="group relative cursor-pointer"
|
||||
className='group relative cursor-pointer'
|
||||
onClick={() => inputRef.current?.click()}
|
||||
>
|
||||
<BasedAvatar
|
||||
src={currentImageUrl ?? undefined}
|
||||
fullName={user?.name}
|
||||
className="h-42 w-42 text-6xl font-semibold"
|
||||
className='h-42 w-42 text-6xl font-semibold'
|
||||
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
|
||||
className="text-white opacity-0 transition-opacity group-hover:opacity-100"
|
||||
className='text-white opacity-0 transition-opacity group-hover:opacity-100'
|
||||
size={24}
|
||||
/>
|
||||
</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
|
||||
className="text-white opacity-100 transition-opacity group-hover:opacity-0"
|
||||
className='text-white opacity-100 transition-opacity group-hover:opacity-0'
|
||||
size={24}
|
||||
/>
|
||||
</div>
|
||||
@@ -140,17 +140,17 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
{/* File input (hidden) */}
|
||||
<Input
|
||||
ref={inputRef}
|
||||
id="avatar-upload"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
id='avatar-upload'
|
||||
type='file'
|
||||
accept='image/*'
|
||||
className='hidden'
|
||||
onChange={handleFileChange}
|
||||
disabled={isUploading}
|
||||
/>
|
||||
|
||||
{/* Crop UI */}
|
||||
{selectedFile && !croppedImage && (
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<div className='flex flex-col items-center gap-3'>
|
||||
<ImageCrop
|
||||
aspect={1}
|
||||
circularCrop
|
||||
@@ -158,16 +158,13 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
maxImageSize={3 * 1024 * 1024} // 3MB guard
|
||||
onCrop={setCroppedImage}
|
||||
>
|
||||
<ImageCropContent className="max-w-sm" />
|
||||
<div className="flex items-center gap-2">
|
||||
<ImageCropApply />
|
||||
<Button
|
||||
onClick={handleReset}
|
||||
size="icon"
|
||||
type="button"
|
||||
variant="ghost"
|
||||
>
|
||||
<XIcon className="size-4" />
|
||||
<ImageCropContent className='max-w-sm' />
|
||||
<div className='flex items-center gap-2'>
|
||||
<Button size='icon' variant='outline'>
|
||||
<ImageCropApply className='h-full w-full scale-150' />
|
||||
</Button>
|
||||
<Button onClick={handleReset} size='icon' variant='destructive'>
|
||||
<XIcon className='scale-150' />
|
||||
</Button>
|
||||
</div>
|
||||
</ImageCrop>
|
||||
@@ -176,19 +173,20 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
|
||||
{/* Cropped preview + actions */}
|
||||
{croppedImage && (
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<Avatar className="h-42 w-42">
|
||||
<AvatarImage alt="Cropped preview" src={croppedImage} />
|
||||
<div className='flex flex-col items-center gap-3'>
|
||||
<Avatar className='h-42 w-42'>
|
||||
<AvatarImage alt='Cropped preview' src={croppedImage} />
|
||||
</Avatar>
|
||||
<div className="flex items-center gap-1">
|
||||
<div className='flex items-center gap-1'>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={isUploading}
|
||||
className="px-4"
|
||||
variant='secondary'
|
||||
className='px-4'
|
||||
>
|
||||
{isUploading ? (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
<span className='inline-flex items-center gap-2'>
|
||||
<Loader2 className='h-4 w-4 animate-spin' />
|
||||
Saving...
|
||||
</span>
|
||||
) : (
|
||||
@@ -197,12 +195,11 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleReset}
|
||||
size="icon"
|
||||
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="secondary"
|
||||
size='icon'
|
||||
type='button'
|
||||
variant='destructive'
|
||||
>
|
||||
<XIcon className="size-4" />
|
||||
<XIcon className='size-4' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -210,8 +207,8 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
|
||||
{/* Uploading indicator */}
|
||||
{isUploading && !croppedImage && (
|
||||
<div className="mt-2 flex items-center text-sm text-gray-500">
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
<div className='mt-2 flex items-center text-sm text-gray-500'>
|
||||
<Loader2 className='mr-2 h-4 w-4 animate-spin' />
|
||||
Uploading...
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -14,9 +14,7 @@ const ProfileHeader = ({ preloadedUser }: ProfileCardProps) => {
|
||||
const user = usePreloadedQuery(preloadedUser);
|
||||
return (
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl">
|
||||
Account Settings
|
||||
</CardTitle>
|
||||
<CardTitle className='text-xl'>Account Settings</CardTitle>
|
||||
<CardDescription>
|
||||
Update your profile information and manage your account preferences
|
||||
</CardDescription>
|
||||
|
||||
@@ -68,7 +68,7 @@ const formSchema = z
|
||||
|
||||
interface ResetFormProps {
|
||||
preloadedProvider: Preloaded<typeof api.auth.getUserProvider>;
|
||||
};
|
||||
}
|
||||
|
||||
export const ResetPasswordForm = ({ preloadedProvider }: ResetFormProps) => {
|
||||
const userProvider = usePreloadedQuery(preloadedProvider);
|
||||
@@ -121,19 +121,19 @@ export const ResetPasswordForm = ({ preloadedProvider }: ResetFormProps) => {
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(handleSubmit)}
|
||||
className="space-y-4"
|
||||
className='space-y-4'
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="currentPassword"
|
||||
name='currentPassword'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Current Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
type='password'
|
||||
{...field}
|
||||
placeholder="Enter current password"
|
||||
placeholder='Enter current password'
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -142,15 +142,15 @@ export const ResetPasswordForm = ({ preloadedProvider }: ResetFormProps) => {
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="newPassword"
|
||||
name='newPassword'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>New Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
type='password'
|
||||
{...field}
|
||||
placeholder="Enter new password"
|
||||
placeholder='Enter new password'
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
@@ -163,23 +163,23 @@ export const ResetPasswordForm = ({ preloadedProvider }: ResetFormProps) => {
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="confirmPassword"
|
||||
name='confirmPassword'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Confirm New Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
type='password'
|
||||
{...field}
|
||||
placeholder="Confirm new password"
|
||||
placeholder='Confirm new password'
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex justify-end pt-2">
|
||||
<SubmitButton disabled={loading} pendingText="Updating...">
|
||||
<div className='flex justify-end pt-2'>
|
||||
<SubmitButton disabled={loading} pendingText='Updating...'>
|
||||
Update Password
|
||||
</SubmitButton>
|
||||
</div>
|
||||
|
||||
@@ -39,12 +39,12 @@ export const SignOutForm = () => {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Button
|
||||
variant="destructive"
|
||||
className="w-full"
|
||||
variant='destructive'
|
||||
className='w-full'
|
||||
onClick={handleSignOut}
|
||||
disabled={isSigningOut}
|
||||
>
|
||||
<LogOut className="mr-2 h-4 w-4" />
|
||||
<LogOut className='mr-2 h-4 w-4' />
|
||||
{isSigningOut ? 'Signing Out...' : 'Sign Out'}
|
||||
</Button>
|
||||
</CardContent>
|
||||
|
||||
@@ -43,9 +43,12 @@ const formSchema = z.object({
|
||||
interface UserInfoFormProps {
|
||||
preloadedUser: Preloaded<typeof api.auth.getUser>;
|
||||
preloadedProvider: Preloaded<typeof api.auth.getUserProvider>;
|
||||
};
|
||||
}
|
||||
|
||||
export const UserInfoForm = ({ preloadedUser, preloadedProvider }: UserInfoFormProps) => {
|
||||
export const UserInfoForm = ({
|
||||
preloadedUser,
|
||||
preloadedProvider,
|
||||
}: UserInfoFormProps) => {
|
||||
const user = usePreloadedQuery(preloadedUser);
|
||||
const userProvider = usePreloadedQuery(preloadedProvider);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -106,16 +109,16 @@ export const UserInfoForm = ({ preloadedUser, preloadedProvider }: UserInfoFormP
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(handleSubmit)}
|
||||
className="space-y-4"
|
||||
className='space-y-4'
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
name='name'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Full Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder="John Doe" />
|
||||
<Input {...field} placeholder='John Doe' />
|
||||
</FormControl>
|
||||
<FormDescription>Your public display name</FormDescription>
|
||||
<FormMessage />
|
||||
@@ -125,27 +128,33 @@ export const UserInfoForm = ({ preloadedUser, preloadedProvider }: UserInfoFormP
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
name='email'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
type="email"
|
||||
placeholder="john@example.com"
|
||||
type='email'
|
||||
placeholder='john@example.com'
|
||||
disabled={userProvider !== 'email'}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Your email address for account notifications
|
||||
</FormDescription>
|
||||
{userProvider === 'email' ? (
|
||||
<FormDescription>
|
||||
Your email address for account notifications
|
||||
</FormDescription>
|
||||
) : (
|
||||
<FormDescription>
|
||||
Email is managed through your {userProvider} account
|
||||
</FormDescription>
|
||||
)}
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className="flex justify-end pt-2">
|
||||
<SubmitButton disabled={loading} pendingText="Saving...">
|
||||
<div className='flex justify-end pt-2'>
|
||||
<SubmitButton disabled={loading} pendingText='Saving...'>
|
||||
Save Changes
|
||||
</SubmitButton>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
import { Kanit } from 'next/font/google';
|
||||
import Link from 'next/link';
|
||||
|
||||
const kanitSans = Kanit({
|
||||
subsets: ['latin'],
|
||||
@@ -8,51 +8,53 @@ const kanitSans = Kanit({
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="border-border/40 bg-muted/30 border-t">
|
||||
<div className="container mx-auto px-4 py-12">
|
||||
<div className="grid gap-8 md:grid-cols-4">
|
||||
<footer className='border-border/40 bg-muted/30 border-t'>
|
||||
<div className='container mx-auto px-4 py-12'>
|
||||
<div className='grid gap-8 md:grid-cols-4'>
|
||||
{/* Brand */}
|
||||
<div className="md:col-span-2">
|
||||
<h3 className={`mb-2 text-3xl font-bold ${kanitSans.className}`}>convex monorepo</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
A production-ready Turborepo starter with Next.js, Expo, and
|
||||
a self-hosted Convex backend, including Convex Auth with a
|
||||
custom useSend email provider to ensure everything can be
|
||||
self-hosted. Built for developers who want complete control
|
||||
without sacrificing ease of use.
|
||||
<div className='md:col-span-2'>
|
||||
<h3 className={`mb-2 text-3xl font-bold ${kanitSans.className}`}>
|
||||
convex monorepo
|
||||
</h3>
|
||||
<p className='text-muted-foreground text-sm'>
|
||||
A production-ready Turborepo starter with Next.js, Expo, and a
|
||||
self-hosted Convex backend, including Convex Auth with a custom
|
||||
useSend email provider to ensure everything can be self-hosted.
|
||||
Built for developers who want complete control without sacrificing
|
||||
ease of use.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Links */}
|
||||
<div>
|
||||
<h4 className="mb-4 text-sm font-semibold">Resources</h4>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<h4 className='mb-4 text-sm font-semibold'>Resources</h4>
|
||||
<ul className='space-y-2 text-sm'>
|
||||
<li>
|
||||
<Link
|
||||
href="https://git.gbrown.org/gib/convex-monorepo"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
href='https://git.gbrown.org/gib/convex-monorepo'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-muted-foreground hover:text-foreground transition-colors'
|
||||
>
|
||||
Gitea Repository
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://docs.convex.dev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
href='https://docs.convex.dev'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-muted-foreground hover:text-foreground transition-colors'
|
||||
>
|
||||
Convex Documentation
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://turbo.build"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
href='https://turbo.build'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-muted-foreground hover:text-foreground transition-colors'
|
||||
>
|
||||
Turborepo
|
||||
</Link>
|
||||
@@ -62,34 +64,34 @@ export default function Footer() {
|
||||
|
||||
{/* Tech */}
|
||||
<div>
|
||||
<h4 className="mb-4 text-sm font-semibold">Built With</h4>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<h4 className='mb-4 text-sm font-semibold'>Built With</h4>
|
||||
<ul className='space-y-2 text-sm'>
|
||||
<li>
|
||||
<Link
|
||||
href="https://nextjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
href='https://nextjs.org'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-muted-foreground hover:text-foreground transition-colors'
|
||||
>
|
||||
Next.js
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://expo.dev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
href='https://expo.dev'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-muted-foreground hover:text-foreground transition-colors'
|
||||
>
|
||||
Expo
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="https://ui.shadcn.com"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
||||
href='https://ui.shadcn.com'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-muted-foreground hover:text-foreground transition-colors'
|
||||
>
|
||||
shadcn/ui
|
||||
</Link>
|
||||
@@ -99,14 +101,14 @@ export default function Footer() {
|
||||
</div>
|
||||
|
||||
{/* 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>
|
||||
Built by{' '}
|
||||
<Link
|
||||
href="https://gbrown.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-foreground font-medium transition-colors"
|
||||
href='https://gbrown.org'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='hover:text-foreground font-medium transition-colors'
|
||||
>
|
||||
Gib.
|
||||
</Link>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation';
|
||||
import { useAuthActions } from '@convex-dev/auth/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 {
|
||||
BasedAvatar,
|
||||
@@ -24,26 +25,23 @@ export const AvatarDropdown = () => {
|
||||
const user = useQuery(api.auth.getUser, {});
|
||||
const currentImageUrl = useQuery(
|
||||
api.files.getImageUrl,
|
||||
user?.image ? { storageId: user.image as any } : 'skip',
|
||||
user?.image ? { storageId: user.image as Id<'_storage'> } : 'skip',
|
||||
);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<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='flex items-center gap-2'>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="ghost" size="sm" asChild>
|
||||
<Link href="/sign-in">Sign In</Link>
|
||||
</Button>
|
||||
<Button size="sm" asChild>
|
||||
<Link href="/sign-in">Get Started</Link>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Button size='sm' asChild>
|
||||
<Link href='/sign-in'>Sign In</Link>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
@@ -55,22 +53,22 @@ export const AvatarDropdown = () => {
|
||||
<BasedAvatar
|
||||
src={currentImageUrl}
|
||||
fullName={user?.name}
|
||||
className="h-9 w-9"
|
||||
className='h-9 w-9'
|
||||
fallbackProps={{ className: 'text-sm font-semibold' }}
|
||||
userIconProps={{ size: 20 }}
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuContent align='end'>
|
||||
{(user?.name ?? user?.email) && (
|
||||
<>
|
||||
<DropdownMenuLabel className="text-center font-bold">
|
||||
<DropdownMenuLabel className='text-center font-bold'>
|
||||
{user.name?.trim() ?? user.email?.trim()}
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href="/profile" className="w-full cursor-pointer">
|
||||
<Link href='/profile' className='w-full cursor-pointer'>
|
||||
Edit Profile
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
@@ -82,7 +80,7 @@ export const AvatarDropdown = () => {
|
||||
router.push('/');
|
||||
})
|
||||
}
|
||||
className="w-full cursor-pointer"
|
||||
className='w-full cursor-pointer'
|
||||
>
|
||||
Sign Out
|
||||
</button>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AvatarDropdown } from './AvatarDropdown';
|
||||
|
||||
export const Controls = (themeToggleProps?: ThemeToggleProps) => {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className='flex items-center gap-3'>
|
||||
<ThemeToggle
|
||||
size={1.1}
|
||||
buttonProps={{
|
||||
|
||||
@@ -1,65 +1,64 @@
|
||||
'use client';
|
||||
|
||||
import type { ComponentProps } from 'react';
|
||||
import { Kanit } from 'next/font/google';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { Kanit } from 'next/font/google';
|
||||
import { Coffee, Server, Wrench } from 'lucide-react';
|
||||
|
||||
import { Controls } from './controls';
|
||||
|
||||
const kanitSans = Kanit({
|
||||
subsets: ['latin'],
|
||||
weight: ['400', '500', '600', '700'],
|
||||
});
|
||||
|
||||
import { Controls } from './controls';
|
||||
|
||||
export default function Header(headerProps: ComponentProps<'header'>) {
|
||||
return (
|
||||
<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}
|
||||
>
|
||||
<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 */}
|
||||
<Link
|
||||
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]"
|
||||
href='/'
|
||||
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
|
||||
src="/misc/convex/convex-symbol-white.svg"
|
||||
alt="Convex Monorepo"
|
||||
src='/misc/convex/convex-symbol-white.svg'
|
||||
alt='Convex Monorepo'
|
||||
width={50}
|
||||
height={50}
|
||||
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
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{/* 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
|
||||
href="/#features"
|
||||
className="text-foreground/60 hover:text-foreground transition-colors flex gap-2 items-center"
|
||||
href='/#features'
|
||||
className='text-foreground/60 hover:text-foreground flex items-center gap-2 transition-colors'
|
||||
>
|
||||
<Wrench width={18} height={18} />
|
||||
Features
|
||||
</Link>
|
||||
<Link
|
||||
href="/#tech-stack"
|
||||
className="text-foreground/60 hover:text-foreground transition-colors flex gap-2 items-center"
|
||||
href='/#tech-stack'
|
||||
className='text-foreground/60 hover:text-foreground flex items-center gap-2 transition-colors'
|
||||
>
|
||||
<Server width={18} height={18} />
|
||||
Stack
|
||||
</Link>
|
||||
<Link
|
||||
href="https://git.gbrown.org/gib/convex-monorepo"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-foreground/60 hover:text-foreground transition-colors flex gap-2 items-center"
|
||||
href='https://git.gbrown.org/gib/convex-monorepo'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-foreground/60 hover:text-foreground flex items-center gap-2 transition-colors'
|
||||
>
|
||||
|
||||
<Coffee width={20} height={20} />
|
||||
Repository
|
||||
</Link>
|
||||
|
||||
23
bun.lock
23
bun.lock
@@ -1,16 +1,15 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 0,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "convex-turbo",
|
||||
"devDependencies": {
|
||||
"@gib/prettier-config": "workspace:",
|
||||
"@turbo/gen": "^2.7.3",
|
||||
"@turbo/gen": "^2.7.4",
|
||||
"baseline-browser-mapping": "^2.9.14",
|
||||
"dotenv-cli": "^10.0.0",
|
||||
"prettier": "catalog:",
|
||||
"turbo": "^2.7.3",
|
||||
"turbo": "^2.7.4",
|
||||
"typescript": "catalog:",
|
||||
},
|
||||
},
|
||||
@@ -1359,9 +1358,9 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -3013,19 +3012,19 @@
|
||||
|
||||
"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=="],
|
||||
|
||||
|
||||
@@ -55,11 +55,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gib/prettier-config": "workspace:",
|
||||
"@turbo/gen": "^2.7.3",
|
||||
"@turbo/gen": "^2.7.4",
|
||||
"baseline-browser-mapping": "^2.9.14",
|
||||
"dotenv-cli": "^10.0.0",
|
||||
"prettier": "catalog:",
|
||||
"turbo": "^2.7.3",
|
||||
"turbo": "^2.7.4",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"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>) {
|
||||
return (
|
||||
<AvatarPrimitive.Root
|
||||
data-slot="avatar"
|
||||
data-slot='avatar'
|
||||
className={cn(
|
||||
'relative flex size-8 shrink-0 overflow-hidden rounded-full',
|
||||
className,
|
||||
@@ -27,7 +27,7 @@ function AvatarImage({
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
||||
return (
|
||||
<AvatarPrimitive.Image
|
||||
data-slot="avatar-image"
|
||||
data-slot='avatar-image'
|
||||
className={cn('aspect-square size-full', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -40,7 +40,7 @@ function AvatarFallback({
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
||||
return (
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot="avatar-fallback"
|
||||
data-slot='avatar-fallback'
|
||||
className={cn(
|
||||
'bg-muted flex size-full items-center justify-center rounded-full',
|
||||
className,
|
||||
|
||||
@@ -27,7 +27,7 @@ const BasedAvatar = ({
|
||||
}: BasedAvatarProps) => {
|
||||
return (
|
||||
<AvatarPrimitive.Root
|
||||
data-slot="avatar"
|
||||
data-slot='avatar'
|
||||
className={cn(
|
||||
'relative flex size-8 shrink-0 cursor-pointer overflow-hidden rounded-full',
|
||||
className,
|
||||
@@ -43,7 +43,7 @@ const BasedAvatar = ({
|
||||
) : (
|
||||
<AvatarPrimitive.Fallback
|
||||
{...fallbackProps}
|
||||
data-slot="avatar-fallback"
|
||||
data-slot='avatar-fallback'
|
||||
className={cn(
|
||||
'bg-muted flex size-full items-center justify-center rounded-full',
|
||||
fallbackProps?.className,
|
||||
|
||||
@@ -35,7 +35,7 @@ const BasedProgress = ({
|
||||
|
||||
return (
|
||||
<ProgressPrimitive.Root
|
||||
data-slot="progress"
|
||||
data-slot='progress'
|
||||
className={cn(
|
||||
'bg-primary/20 relative h-2 w-full overflow-hidden rounded-full',
|
||||
className,
|
||||
@@ -43,8 +43,8 @@ const BasedProgress = ({
|
||||
{...props}
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
data-slot="progress-indicator"
|
||||
className="bg-primary h-full w-full flex-1 transition-all"
|
||||
data-slot='progress-indicator'
|
||||
className='bg-primary h-full w-full flex-1 transition-all'
|
||||
style={{ transform: `translateX(-${100 - (progress ?? 0)}%)` }}
|
||||
/>
|
||||
</ProgressPrimitive.Root>
|
||||
|
||||
@@ -50,7 +50,7 @@ function Button({
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="button"
|
||||
data-slot='button'
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { cn } from '@gib/ui';
|
||||
function Card({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card"
|
||||
data-slot='card'
|
||||
className={cn(
|
||||
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
|
||||
className,
|
||||
@@ -18,7 +18,7 @@ function Card({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-header"
|
||||
data-slot='card-header'
|
||||
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',
|
||||
className,
|
||||
@@ -31,7 +31,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-title"
|
||||
data-slot='card-title'
|
||||
className={cn('leading-none font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -41,7 +41,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-description"
|
||||
data-slot='card-description'
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -51,7 +51,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-action"
|
||||
data-slot='card-action'
|
||||
className={cn(
|
||||
'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
|
||||
className,
|
||||
@@ -64,7 +64,7 @@ function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-content"
|
||||
data-slot='card-content'
|
||||
className={cn('px-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -74,7 +74,7 @@ function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
data-slot='card-footer'
|
||||
className={cn('flex items-center px-6 [.border-t]:pt-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -12,7 +12,7 @@ function Checkbox({
|
||||
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
||||
return (
|
||||
<CheckboxPrimitive.Root
|
||||
data-slot="checkbox"
|
||||
data-slot='checkbox'
|
||||
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',
|
||||
className,
|
||||
@@ -20,10 +20,10 @@ function Checkbox({
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
data-slot="checkbox-indicator"
|
||||
className="flex items-center justify-center text-current transition-none"
|
||||
data-slot='checkbox-indicator'
|
||||
className='flex items-center justify-center text-current transition-none'
|
||||
>
|
||||
<CheckIcon className="size-3.5" />
|
||||
<CheckIcon className='size-3.5' />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
);
|
||||
|
||||
@@ -8,25 +8,25 @@ import { cn } from '@gib/ui';
|
||||
function Drawer({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
|
||||
return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
|
||||
return <DrawerPrimitive.Root data-slot='drawer' {...props} />;
|
||||
}
|
||||
|
||||
function DrawerTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
|
||||
return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
|
||||
return <DrawerPrimitive.Trigger data-slot='drawer-trigger' {...props} />;
|
||||
}
|
||||
|
||||
function DrawerPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
|
||||
return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
|
||||
return <DrawerPrimitive.Portal data-slot='drawer-portal' {...props} />;
|
||||
}
|
||||
|
||||
function DrawerClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Close>) {
|
||||
return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
|
||||
return <DrawerPrimitive.Close data-slot='drawer-close' {...props} />;
|
||||
}
|
||||
|
||||
function DrawerOverlay({
|
||||
@@ -35,7 +35,7 @@ function DrawerOverlay({
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
|
||||
return (
|
||||
<DrawerPrimitive.Overlay
|
||||
data-slot="drawer-overlay"
|
||||
data-slot='drawer-overlay'
|
||||
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',
|
||||
className,
|
||||
@@ -51,10 +51,10 @@ function DrawerContent({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Content>) {
|
||||
return (
|
||||
<DrawerPortal data-slot="drawer-portal">
|
||||
<DrawerPortal data-slot='drawer-portal'>
|
||||
<DrawerOverlay />
|
||||
<DrawerPrimitive.Content
|
||||
data-slot="drawer-content"
|
||||
data-slot='drawer-content'
|
||||
className={cn(
|
||||
'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',
|
||||
@@ -65,7 +65,7 @@ function DrawerContent({
|
||||
)}
|
||||
{...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}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPortal>
|
||||
@@ -75,7 +75,7 @@ function DrawerContent({
|
||||
function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="drawer-header"
|
||||
data-slot='drawer-header'
|
||||
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',
|
||||
className,
|
||||
@@ -88,7 +88,7 @@ function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="drawer-footer"
|
||||
data-slot='drawer-footer'
|
||||
className={cn('mt-auto flex flex-col gap-2 p-4', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -101,7 +101,7 @@ function DrawerTitle({
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Title>) {
|
||||
return (
|
||||
<DrawerPrimitive.Title
|
||||
data-slot="drawer-title"
|
||||
data-slot='drawer-title'
|
||||
className={cn('text-foreground font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -114,7 +114,7 @@ function DrawerDescription({
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Description>) {
|
||||
return (
|
||||
<DrawerPrimitive.Description
|
||||
data-slot="drawer-description"
|
||||
data-slot='drawer-description'
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -9,14 +9,14 @@ import { cn } from '@gib/ui';
|
||||
function DropdownMenu({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
||||
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
||||
return <DropdownMenuPrimitive.Root data-slot='dropdown-menu' {...props} />;
|
||||
}
|
||||
|
||||
function DropdownMenuPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
||||
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>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Trigger
|
||||
data-slot="dropdown-menu-trigger"
|
||||
data-slot='dropdown-menu-trigger'
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -39,7 +39,7 @@ function DropdownMenuContent({
|
||||
return (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
data-slot="dropdown-menu-content"
|
||||
data-slot='dropdown-menu-content'
|
||||
sideOffset={sideOffset}
|
||||
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',
|
||||
@@ -55,7 +55,7 @@ function DropdownMenuGroup({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
||||
<DropdownMenuPrimitive.Group data-slot='dropdown-menu-group' {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ function DropdownMenuItem({
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Item
|
||||
data-slot="dropdown-menu-item"
|
||||
data-slot='dropdown-menu-item'
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
@@ -90,7 +90,7 @@ function DropdownMenuCheckboxItem({
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
data-slot='dropdown-menu-checkbox-item'
|
||||
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",
|
||||
className,
|
||||
@@ -98,9 +98,9 @@ function DropdownMenuCheckboxItem({
|
||||
checked={checked}
|
||||
{...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>
|
||||
<CheckIcon className="size-4" />
|
||||
<CheckIcon className='size-4' />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
@@ -113,7 +113,7 @@ function DropdownMenuRadioGroup({
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.RadioGroup
|
||||
data-slot="dropdown-menu-radio-group"
|
||||
data-slot='dropdown-menu-radio-group'
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -126,16 +126,16 @@ function DropdownMenuRadioItem({
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
data-slot='dropdown-menu-radio-item'
|
||||
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",
|
||||
className,
|
||||
)}
|
||||
{...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>
|
||||
<CircleIcon className="size-2 fill-current" />
|
||||
<CircleIcon className='size-2 fill-current' />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
@@ -152,7 +152,7 @@ function DropdownMenuLabel({
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Label
|
||||
data-slot="dropdown-menu-label"
|
||||
data-slot='dropdown-menu-label'
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
'px-2 py-1.5 text-sm font-medium data-[inset]:pl-8',
|
||||
@@ -169,7 +169,7 @@ function DropdownMenuSeparator({
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
data-slot="dropdown-menu-separator"
|
||||
data-slot='dropdown-menu-separator'
|
||||
className={cn('bg-border -mx-1 my-1 h-px', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -182,7 +182,7 @@ function DropdownMenuShortcut({
|
||||
}: React.ComponentProps<'span'>) {
|
||||
return (
|
||||
<span
|
||||
data-slot="dropdown-menu-shortcut"
|
||||
data-slot='dropdown-menu-shortcut'
|
||||
className={cn(
|
||||
'text-muted-foreground ml-auto text-xs tracking-widest',
|
||||
className,
|
||||
@@ -195,7 +195,7 @@ function DropdownMenuShortcut({
|
||||
function DropdownMenuSub({
|
||||
...props
|
||||
}: 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({
|
||||
@@ -208,7 +208,7 @@ function DropdownMenuSubTrigger({
|
||||
}) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
data-slot="dropdown-menu-sub-trigger"
|
||||
data-slot='dropdown-menu-sub-trigger'
|
||||
data-inset={inset}
|
||||
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',
|
||||
@@ -217,7 +217,7 @@ function DropdownMenuSubTrigger({
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto size-4" />
|
||||
<ChevronRightIcon className='ml-auto size-4' />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
);
|
||||
}
|
||||
@@ -228,7 +228,7 @@ function DropdownMenuSubContent({
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
||||
return (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
data-slot='dropdown-menu-sub-content'
|
||||
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',
|
||||
className,
|
||||
|
||||
@@ -12,7 +12,7 @@ export function FieldSet({
|
||||
}: React.ComponentProps<'fieldset'>) {
|
||||
return (
|
||||
<fieldset
|
||||
data-slot="field-set"
|
||||
data-slot='field-set'
|
||||
className={cn(
|
||||
'flex flex-col gap-6',
|
||||
'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' }) {
|
||||
return (
|
||||
<legend
|
||||
data-slot="field-legend"
|
||||
data-slot='field-legend'
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
'mb-3 font-medium',
|
||||
@@ -49,7 +49,7 @@ export function FieldGroup({
|
||||
}: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="field-group"
|
||||
data-slot='field-group'
|
||||
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',
|
||||
className,
|
||||
@@ -90,8 +90,8 @@ export function Field({
|
||||
}: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {
|
||||
return (
|
||||
<div
|
||||
role="group"
|
||||
data-slot="field"
|
||||
role='group'
|
||||
data-slot='field'
|
||||
data-orientation={orientation}
|
||||
className={cn(fieldVariants({ orientation }), className)}
|
||||
{...props}
|
||||
@@ -105,7 +105,7 @@ export function FieldContent({
|
||||
}: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="field-content"
|
||||
data-slot='field-content'
|
||||
className={cn(
|
||||
'group/field-content flex flex-1 flex-col gap-1.5 leading-snug',
|
||||
className,
|
||||
@@ -121,7 +121,7 @@ export function FieldLabel({
|
||||
}: React.ComponentProps<typeof Label>) {
|
||||
return (
|
||||
<Label
|
||||
data-slot="field-label"
|
||||
data-slot='field-label'
|
||||
className={cn(
|
||||
'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',
|
||||
@@ -139,7 +139,7 @@ export function FieldTitle({
|
||||
}: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="field-label"
|
||||
data-slot='field-label'
|
||||
className={cn(
|
||||
'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50',
|
||||
className,
|
||||
@@ -155,7 +155,7 @@ export function FieldDescription({
|
||||
}: React.ComponentProps<'p'>) {
|
||||
return (
|
||||
<p
|
||||
data-slot="field-description"
|
||||
data-slot='field-description'
|
||||
className={cn(
|
||||
'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',
|
||||
@@ -176,7 +176,7 @@ export function FieldSeparator({
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
data-slot="field-separator"
|
||||
data-slot='field-separator'
|
||||
data-content={!!children}
|
||||
className={cn(
|
||||
'relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2',
|
||||
@@ -184,11 +184,11 @@ export function FieldSeparator({
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<Separator className="absolute inset-0 top-1/2" />
|
||||
<Separator className='absolute inset-0 top-1/2' />
|
||||
{children && (
|
||||
<span
|
||||
className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
|
||||
data-slot="field-separator-content"
|
||||
className='bg-background text-muted-foreground relative mx-auto block w-fit px-2'
|
||||
data-slot='field-separator-content'
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
@@ -221,7 +221,7 @@ export function FieldError({
|
||||
}
|
||||
|
||||
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(
|
||||
(error, index) =>
|
||||
error.message && <li key={index}>{error.message}</li>,
|
||||
@@ -236,8 +236,8 @@ export function FieldError({
|
||||
|
||||
return (
|
||||
<div
|
||||
role="alert"
|
||||
data-slot="field-error"
|
||||
role='alert'
|
||||
data-slot='field-error'
|
||||
className={cn('text-destructive text-sm font-normal', className)}
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -76,7 +76,7 @@ function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<FormItemContext.Provider value={{ id }}>
|
||||
<div
|
||||
data-slot="form-item"
|
||||
data-slot='form-item'
|
||||
className={cn('grid gap-2', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -92,7 +92,7 @@ function FormLabel({
|
||||
|
||||
return (
|
||||
<Label
|
||||
data-slot="form-label"
|
||||
data-slot='form-label'
|
||||
data-error={!!error}
|
||||
className={cn('data-[error=true]:text-destructive', className)}
|
||||
htmlFor={formItemId}
|
||||
@@ -107,7 +107,7 @@ function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
|
||||
|
||||
return (
|
||||
<Slot
|
||||
data-slot="form-control"
|
||||
data-slot='form-control'
|
||||
id={formItemId}
|
||||
aria-describedby={
|
||||
!error
|
||||
@@ -125,7 +125,7 @@ function FormDescription({ className, ...props }: React.ComponentProps<'p'>) {
|
||||
|
||||
return (
|
||||
<p
|
||||
data-slot="form-description"
|
||||
data-slot='form-description'
|
||||
id={formDescriptionId}
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
@@ -143,7 +143,7 @@ function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {
|
||||
|
||||
return (
|
||||
<p
|
||||
data-slot="form-message"
|
||||
data-slot='form-message'
|
||||
id={formMessageId}
|
||||
className={cn('text-destructive text-sm', className)}
|
||||
{...props}
|
||||
|
||||
@@ -15,7 +15,7 @@ function InputOTP({
|
||||
}) {
|
||||
return (
|
||||
<OTPInput
|
||||
data-slot="input-otp"
|
||||
data-slot='input-otp'
|
||||
containerClassName={cn(
|
||||
'flex items-center gap-2 has-disabled:opacity-50',
|
||||
containerClassName,
|
||||
@@ -29,7 +29,7 @@ function InputOTP({
|
||||
function InputOTPGroup({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="input-otp-group"
|
||||
data-slot='input-otp-group'
|
||||
className={cn('flex items-center', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -48,7 +48,7 @@ function InputOTPSlot({
|
||||
|
||||
return (
|
||||
<div
|
||||
data-slot="input-otp-slot"
|
||||
data-slot='input-otp-slot'
|
||||
data-active={isActive}
|
||||
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]',
|
||||
@@ -58,8 +58,8 @@ function InputOTPSlot({
|
||||
>
|
||||
{char}
|
||||
{hasFakeCaret && (
|
||||
<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='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>
|
||||
)}
|
||||
</div>
|
||||
@@ -68,7 +68,7 @@ function InputOTPSlot({
|
||||
|
||||
function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div data-slot="input-otp-separator" role="separator" {...props}>
|
||||
<div data-slot='input-otp-separator' role='separator' {...props}>
|
||||
<MinusIcon />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
data-slot="input"
|
||||
data-slot='input'
|
||||
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',
|
||||
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
||||
|
||||
@@ -11,7 +11,7 @@ function Label({
|
||||
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||
return (
|
||||
<LabelPrimitive.Root
|
||||
data-slot="label"
|
||||
data-slot='label'
|
||||
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',
|
||||
className,
|
||||
|
||||
@@ -11,9 +11,9 @@ import { buttonVariants, cn } from '@gib/ui';
|
||||
function Pagination({ className, ...props }: React.ComponentProps<'nav'>) {
|
||||
return (
|
||||
<nav
|
||||
role="navigation"
|
||||
aria-label="pagination"
|
||||
data-slot="pagination"
|
||||
role='navigation'
|
||||
aria-label='pagination'
|
||||
data-slot='pagination'
|
||||
className={cn('mx-auto flex w-full justify-center', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -26,7 +26,7 @@ function PaginationContent({
|
||||
}: React.ComponentProps<'ul'>) {
|
||||
return (
|
||||
<ul
|
||||
data-slot="pagination-content"
|
||||
data-slot='pagination-content'
|
||||
className={cn('flex flex-row items-center gap-1', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -34,7 +34,7 @@ function PaginationContent({
|
||||
}
|
||||
|
||||
function PaginationItem({ ...props }: React.ComponentProps<'li'>) {
|
||||
return <li data-slot="pagination-item" {...props} />;
|
||||
return <li data-slot='pagination-item' {...props} />;
|
||||
}
|
||||
|
||||
type PaginationLinkProps = {
|
||||
@@ -51,7 +51,7 @@ function PaginationLink({
|
||||
return (
|
||||
<a
|
||||
aria-current={isActive ? 'page' : undefined}
|
||||
data-slot="pagination-link"
|
||||
data-slot='pagination-link'
|
||||
data-active={isActive}
|
||||
className={cn(
|
||||
buttonVariants({
|
||||
@@ -71,13 +71,13 @@ function PaginationPrevious({
|
||||
}: React.ComponentProps<typeof PaginationLink>) {
|
||||
return (
|
||||
<PaginationLink
|
||||
aria-label="Go to previous page"
|
||||
size="default"
|
||||
aria-label='Go to previous page'
|
||||
size='default'
|
||||
className={cn('gap-1 px-2.5 sm:pl-2.5', className)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronLeftIcon />
|
||||
<span className="hidden sm:block">Previous</span>
|
||||
<span className='hidden sm:block'>Previous</span>
|
||||
</PaginationLink>
|
||||
);
|
||||
}
|
||||
@@ -88,12 +88,12 @@ function PaginationNext({
|
||||
}: React.ComponentProps<typeof PaginationLink>) {
|
||||
return (
|
||||
<PaginationLink
|
||||
aria-label="Go to next page"
|
||||
size="default"
|
||||
aria-label='Go to next page'
|
||||
size='default'
|
||||
className={cn('gap-1 px-2.5 sm:pr-2.5', className)}
|
||||
{...props}
|
||||
>
|
||||
<span className="hidden sm:block">Next</span>
|
||||
<span className='hidden sm:block'>Next</span>
|
||||
<ChevronRightIcon />
|
||||
</PaginationLink>
|
||||
);
|
||||
@@ -106,12 +106,12 @@ function PaginationEllipsis({
|
||||
return (
|
||||
<span
|
||||
aria-hidden
|
||||
data-slot="pagination-ellipsis"
|
||||
data-slot='pagination-ellipsis'
|
||||
className={cn('flex size-9 items-center justify-center', className)}
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontalIcon className="size-4" />
|
||||
<span className="sr-only">More pages</span>
|
||||
<MoreHorizontalIcon className='size-4' />
|
||||
<span className='sr-only'>More pages</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ function Progress({
|
||||
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
|
||||
return (
|
||||
<ProgressPrimitive.Root
|
||||
data-slot="progress"
|
||||
data-slot='progress'
|
||||
className={cn(
|
||||
'bg-primary/20 relative h-2 w-full overflow-hidden rounded-full',
|
||||
className,
|
||||
@@ -20,8 +20,8 @@ function Progress({
|
||||
{...props}
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
data-slot="progress-indicator"
|
||||
className="bg-primary h-full w-full flex-1 transition-all"
|
||||
data-slot='progress-indicator'
|
||||
className='bg-primary h-full w-full flex-1 transition-all'
|
||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||
/>
|
||||
</ProgressPrimitive.Root>
|
||||
|
||||
@@ -12,13 +12,13 @@ function ScrollArea({
|
||||
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
|
||||
return (
|
||||
<ScrollAreaPrimitive.Root
|
||||
data-slot="scroll-area"
|
||||
data-slot='scroll-area'
|
||||
className={cn('relative', className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.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"
|
||||
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'
|
||||
>
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
@@ -35,7 +35,7 @@ function ScrollBar({
|
||||
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
|
||||
return (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
data-slot="scroll-area-scrollbar"
|
||||
data-slot='scroll-area-scrollbar'
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
'flex touch-none p-px transition-colors select-none',
|
||||
@@ -48,8 +48,8 @@ function ScrollBar({
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb
|
||||
data-slot="scroll-area-thumb"
|
||||
className="bg-border relative flex-1 rounded-full"
|
||||
data-slot='scroll-area-thumb'
|
||||
className='bg-border relative flex-1 rounded-full'
|
||||
/>
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
);
|
||||
|
||||
@@ -13,7 +13,7 @@ function Separator({
|
||||
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
|
||||
return (
|
||||
<SeparatorPrimitive.Root
|
||||
data-slot="separator"
|
||||
data-slot='separator'
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
|
||||
@@ -259,8 +259,8 @@ export const ImageCropContent = ({
|
||||
>
|
||||
{imgSrc && (
|
||||
<img
|
||||
alt="crop"
|
||||
className="size-full"
|
||||
alt='crop'
|
||||
className='size-full'
|
||||
onLoad={onImageLoad}
|
||||
ref={imgRef}
|
||||
src={imgSrc}
|
||||
@@ -296,8 +296,8 @@ export const ImageCropApply = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Button onClick={handleClick} size="icon" variant="ghost" {...props}>
|
||||
{children ?? <CropIcon className="size-4" />}
|
||||
<Button onClick={handleClick} size='icon' variant='ghost' {...props}>
|
||||
{children ?? <CropIcon className='size-4' />}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -328,8 +328,8 @@ export const ImageCropReset = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Button onClick={handleClick} size="icon" variant="ghost" {...props}>
|
||||
{children ?? <RotateCcwIcon className="size-4" />}
|
||||
<Button onClick={handleClick} size='icon' variant='ghost' {...props}>
|
||||
{children ?? <RotateCcwIcon className='size-4' />}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
||||
return (
|
||||
<Sonner
|
||||
theme={theme as ToasterProps['theme']}
|
||||
className="toaster group"
|
||||
className='toaster group'
|
||||
style={
|
||||
{
|
||||
'--normal-bg': 'var(--popover)',
|
||||
|
||||
@@ -16,7 +16,7 @@ export const StatusMessage = ({
|
||||
textProps,
|
||||
}: StatusMessageProps) => {
|
||||
return (
|
||||
<div className="flex w-full flex-col items-center">
|
||||
<div className='flex w-full flex-col items-center'>
|
||||
{'success' in message && (
|
||||
<div
|
||||
{...containerProps}
|
||||
|
||||
@@ -26,7 +26,7 @@ export const SubmitButton = ({
|
||||
const { pending } = useFormStatus();
|
||||
return (
|
||||
<Button
|
||||
type="submit"
|
||||
type='submit'
|
||||
aria-disabled={pending}
|
||||
{...props}
|
||||
className={cn('cursor-pointer', className)}
|
||||
|
||||
@@ -11,7 +11,7 @@ function Switch({
|
||||
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
|
||||
return (
|
||||
<SwitchPrimitive.Root
|
||||
data-slot="switch"
|
||||
data-slot='switch'
|
||||
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',
|
||||
className,
|
||||
@@ -19,7 +19,7 @@ function Switch({
|
||||
{...props}
|
||||
>
|
||||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
data-slot='switch-thumb'
|
||||
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',
|
||||
)}
|
||||
|
||||
@@ -7,11 +7,11 @@ import { cn } from '@gib/ui';
|
||||
function Table({ className, ...props }: React.ComponentProps<'table'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="table-container"
|
||||
className="relative w-full overflow-x-auto"
|
||||
data-slot='table-container'
|
||||
className='relative w-full overflow-x-auto'
|
||||
>
|
||||
<table
|
||||
data-slot="table"
|
||||
data-slot='table'
|
||||
className={cn('w-full caption-bottom text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -22,7 +22,7 @@ function Table({ className, ...props }: React.ComponentProps<'table'>) {
|
||||
function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
|
||||
return (
|
||||
<thead
|
||||
data-slot="table-header"
|
||||
data-slot='table-header'
|
||||
className={cn('[&_tr]:border-b', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -32,7 +32,7 @@ function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
|
||||
function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
|
||||
return (
|
||||
<tbody
|
||||
data-slot="table-body"
|
||||
data-slot='table-body'
|
||||
className={cn('[&_tr:last-child]:border-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -42,7 +42,7 @@ function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
|
||||
function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
|
||||
return (
|
||||
<tfoot
|
||||
data-slot="table-footer"
|
||||
data-slot='table-footer'
|
||||
className={cn(
|
||||
'bg-muted/50 border-t font-medium [&>tr]:last:border-b-0',
|
||||
className,
|
||||
@@ -55,7 +55,7 @@ function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
|
||||
function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
|
||||
return (
|
||||
<tr
|
||||
data-slot="table-row"
|
||||
data-slot='table-row'
|
||||
className={cn(
|
||||
'hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors',
|
||||
className,
|
||||
@@ -68,7 +68,7 @@ function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
|
||||
function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
|
||||
return (
|
||||
<th
|
||||
data-slot="table-head"
|
||||
data-slot='table-head'
|
||||
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]',
|
||||
className,
|
||||
@@ -81,7 +81,7 @@ function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
|
||||
function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
|
||||
return (
|
||||
<td
|
||||
data-slot="table-cell"
|
||||
data-slot='table-cell'
|
||||
className={cn(
|
||||
'p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
className,
|
||||
@@ -97,7 +97,7 @@ function TableCaption({
|
||||
}: React.ComponentProps<'caption'>) {
|
||||
return (
|
||||
<caption
|
||||
data-slot="table-caption"
|
||||
data-slot='table-caption'
|
||||
className={cn('text-muted-foreground mt-4 text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -11,7 +11,7 @@ function Tabs({
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
||||
return (
|
||||
<TabsPrimitive.Root
|
||||
data-slot="tabs"
|
||||
data-slot='tabs'
|
||||
className={cn('flex flex-col gap-2', className)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -24,7 +24,7 @@ function TabsList({
|
||||
}: React.ComponentProps<typeof TabsPrimitive.List>) {
|
||||
return (
|
||||
<TabsPrimitive.List
|
||||
data-slot="tabs-list"
|
||||
data-slot='tabs-list'
|
||||
className={cn(
|
||||
'bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]',
|
||||
className,
|
||||
@@ -40,7 +40,7 @@ function TabsTrigger({
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
||||
return (
|
||||
<TabsPrimitive.Trigger
|
||||
data-slot="tabs-trigger"
|
||||
data-slot='tabs-trigger'
|
||||
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",
|
||||
className,
|
||||
@@ -56,7 +56,7 @@ function TabsContent({
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
||||
return (
|
||||
<TabsPrimitive.Content
|
||||
data-slot="tabs-content"
|
||||
data-slot='tabs-content'
|
||||
className={cn('flex-1 outline-none', className)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -49,19 +49,19 @@ const ThemeToggle = ({ size = 1, buttonProps }: ThemeToggleProps) => {
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
variant='outline'
|
||||
size='icon'
|
||||
{...buttonProps}
|
||||
onClick={toggleTheme}
|
||||
className={cn('cursor-pointer', buttonProps?.className)}
|
||||
>
|
||||
<Sun
|
||||
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
|
||||
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>
|
||||
);
|
||||
|
||||
@@ -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 } */
|
||||
const config = {
|
||||
singleQuote: true,
|
||||
jsxSingleQuote: true,
|
||||
trailingComma: 'all',
|
||||
tabWidth: 2,
|
||||
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