Compare commits
10
Commits
0bc7d084d5
...
5aa66540ba
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5aa66540ba | ||
|
|
01da8339f4 | ||
|
|
e13e5b4de2 | ||
|
|
08b4c39c03 | ||
|
|
610a1b7556 | ||
|
|
f5f715175c | ||
|
|
1ddb2f72c6 | ||
|
|
b04f687199 | ||
|
|
50cc24e46f | ||
|
|
4c7bb18a4f |
@@ -62,3 +62,9 @@ storybook-static
|
||||
# Next.js
|
||||
.next
|
||||
out
|
||||
|
||||
.cursor/rules/nx-rules.mdc
|
||||
.github/instructions/nx.instructions.md
|
||||
|
||||
.cursor/rules/nx-rules.mdc
|
||||
.github/instructions/nx.instructions.md
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
SiCplusplus,
|
||||
SiCss3,
|
||||
SiGo,
|
||||
SiHtml5,
|
||||
SiJavascript,
|
||||
SiPhp,
|
||||
SiPython,
|
||||
SiRuby,
|
||||
SiRust,
|
||||
SiSwift,
|
||||
SiTypescript,
|
||||
} from 'react-icons/si';
|
||||
|
||||
export function AnimatedBackground() {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
const iconColorMap = useMemo(
|
||||
() => [
|
||||
{ Icon: SiJavascript, color: '#F7DF1E' },
|
||||
{ Icon: SiTypescript, color: '#3178C6' },
|
||||
{ Icon: SiPython, color: '#3776AB' },
|
||||
{ Icon: SiCplusplus, color: '#00599C' },
|
||||
{ Icon: SiRuby, color: '#CC342D' },
|
||||
{ Icon: SiSwift, color: '#F05138' },
|
||||
{ Icon: SiRust, color: '#000000' },
|
||||
{ Icon: SiGo, color: '#00ADD8' },
|
||||
{ Icon: SiPhp, color: '#777BB4' },
|
||||
{ Icon: SiHtml5, color: '#E34F26' },
|
||||
{ Icon: SiCss3, color: '#1572B6' },
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
const getRandom = (min: number, max: number) =>
|
||||
Math.random() * (max - min) + min;
|
||||
|
||||
const floatingIcons = useMemo(() => {
|
||||
if (!isClient) return [];
|
||||
|
||||
return Array.from({ length: 40 }).map((_, i) => {
|
||||
const { Icon, color } = iconColorMap[i % iconColorMap.length];
|
||||
return (
|
||||
<motion.div
|
||||
key={i}
|
||||
className="pointer-events-none absolute"
|
||||
style={{
|
||||
top: `${getRandom(0, 100)}%`,
|
||||
left: `${getRandom(0, 100)}%`,
|
||||
fontSize: `${getRandom(16, 32)}px`,
|
||||
color: color,
|
||||
opacity: getRandom(0.1, 0.2),
|
||||
rotate: getRandom(-180, 180),
|
||||
}}
|
||||
animate={{
|
||||
y: [0, getRandom(-100, 100), 0],
|
||||
x: [0, getRandom(-50, 50), 0],
|
||||
rotate: getRandom(-180, 180),
|
||||
}}
|
||||
transition={{
|
||||
duration: getRandom(15, 25),
|
||||
repeat: Infinity,
|
||||
repeatType: 'loop',
|
||||
ease: 'easeInOut',
|
||||
}}
|
||||
>
|
||||
<Icon className="h-full w-full" />
|
||||
</motion.div>
|
||||
);
|
||||
});
|
||||
}, [isClient, iconColorMap]);
|
||||
|
||||
if (!isClient) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-0 overflow-hidden">{floatingIcons}</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,19 @@
|
||||
import { Card } from '@components/atoms';
|
||||
import { ReactNode } from 'react';
|
||||
import { AnimatedBackground } from './_components/animated-background';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return <>{children}</>;
|
||||
return (
|
||||
<div className="bg-background/20 relative flex min-h-[100dvh] items-center justify-center p-4">
|
||||
<AnimatedBackground />
|
||||
|
||||
<div className="z-10 w-full">
|
||||
<Card className="bg-background/90 mx-auto w-full max-w-[360px] p-6 shadow-xl backdrop-blur-lg sm:max-w-md sm:p-8">
|
||||
<div className="flex flex-col items-center space-y-6">
|
||||
<div className="w-full space-y-4">{children}</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
'use server';
|
||||
|
||||
import { setAccessToken, setRefreshToken } from '@/lib/cookies';
|
||||
import { fetchPostSignin } from '../_http/fetch-post-signin';
|
||||
import {
|
||||
type SignInValidationType,
|
||||
signInValidationSchema,
|
||||
} from '../_validation/signin-validation';
|
||||
|
||||
export async function SigninAction(request: SignInValidationType) {
|
||||
console.log(request);
|
||||
|
||||
const validRequest = signInValidationSchema.parse(request);
|
||||
|
||||
const { data } = await fetchPostSignin(validRequest);
|
||||
|
||||
const accessToken = data.token.access_token;
|
||||
const refreshToken = data.token.refresh_token;
|
||||
|
||||
await setAccessToken(accessToken);
|
||||
await setRefreshToken(refreshToken);
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
Input,
|
||||
} from '@components/atoms';
|
||||
import { LuLoader } from 'react-icons/lu';
|
||||
import { useFormSignin } from '../_hooks/use-form-signin';
|
||||
import { usePostSignin } from '../_hooks/use-post-signin';
|
||||
import { type SignInValidationType } from '../_validation/signin-validation';
|
||||
|
||||
export function SigninForm() {
|
||||
const form = useFormSignin();
|
||||
const { mutate, isPending, error } = usePostSignin(form);
|
||||
|
||||
const onSubmit = (values: SignInValidationType) => {
|
||||
mutate(values);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
{error && (
|
||||
<div className="p-2 text-xs bg-red-50 border border-red-200 text-red-800 rounded-sm">
|
||||
{(error as Error).message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="emailmu@mail.com" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" placeholder="••••••••" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isPending}
|
||||
className="w-full hover:bg-[#5fbaef] bg-[#22a5f1] font-bold"
|
||||
>
|
||||
{isPending ? <LuLoader className="h-5 w-5 animate-spin" /> : 'Masuk'}
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import {
|
||||
signInValidationSchema,
|
||||
type SignInValidationType,
|
||||
} from '../_validation/signin-validation';
|
||||
|
||||
export function useFormSignin() {
|
||||
return useForm<SignInValidationType>({
|
||||
resolver: zodResolver(signInValidationSchema),
|
||||
defaultValues: {
|
||||
email: '',
|
||||
password: '',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { SigninAction } from '../_actions/signin-action';
|
||||
import { useFormSignin } from './use-form-signin';
|
||||
|
||||
export function usePostSignin(form: ReturnType<typeof useFormSignin>) {
|
||||
const router = useRouter();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: SigninAction,
|
||||
onSuccess: () => {
|
||||
router.push('/');
|
||||
},
|
||||
onError: () => {
|
||||
form.resetField('password');
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { fetcher } from '@/lib/openapi';
|
||||
import { SignInValidationType } from '../_validation/signin-validation';
|
||||
|
||||
export async function fetchPostSignin({
|
||||
email,
|
||||
password,
|
||||
}: SignInValidationType) {
|
||||
const { data, error, response } = await fetcher.POST('/v1/auth/login', {
|
||||
body: {
|
||||
email,
|
||||
password,
|
||||
},
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Someting went wrong, please try again later');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const signInValidationSchema = z.object({
|
||||
email: z
|
||||
.string({
|
||||
required_error: 'Email tidak boleh kosong',
|
||||
invalid_type_error: 'Email harus berupa string',
|
||||
})
|
||||
.min(1, 'Email tidak boleh kosong')
|
||||
.email('Email harus valid'),
|
||||
password: z
|
||||
.string({
|
||||
required_error: 'Password tidak boleh kosong',
|
||||
invalid_type_error: 'Password harus berupa string',
|
||||
})
|
||||
.min(1, 'Password tidak boleh kosong'),
|
||||
});
|
||||
export type SignInValidationType = z.infer<typeof signInValidationSchema>;
|
||||
@@ -1,3 +1,38 @@
|
||||
import { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
import { LogoSimple } from '../../_components/logo';
|
||||
import { SigninForm } from './_components/signin-form';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'IMPHNEN - Signin',
|
||||
description:
|
||||
'Komunitas Ingin Menjadi Programmer Handal Namung Enggan Ngonding',
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<LogoSimple />
|
||||
|
||||
<p className="text-muted-foreground text-center text-sm sm:text-base">
|
||||
Masuk untuk mengakses akunmu
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<SigninForm />
|
||||
|
||||
<div className="w-full space-y-4">
|
||||
<p className="text-muted-foreground px-4 text-center text-xs leading-5 text-balance sm:text-sm">
|
||||
Belum punya akun?{' '}
|
||||
<Link
|
||||
href="/signup"
|
||||
className="text-[#22a5f1] font-bold hover:underline hover:underline-offset-4 transition-colors"
|
||||
>
|
||||
Daftar
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
'use server';
|
||||
|
||||
import { z } from 'zod';
|
||||
import { fetchPostSignin } from '../_http/fetch-post-signup';
|
||||
import { signupValidationSchema } from '../_validation/signup-validation';
|
||||
|
||||
export async function SignupAction(
|
||||
request: z.infer<typeof signupValidationSchema>
|
||||
) {
|
||||
const validRequest = signupValidationSchema.parse(request);
|
||||
|
||||
const data = await fetchPostSignin({ ...validRequest });
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
Input,
|
||||
} from '@components/atoms';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Turnstile } from '@marsidev/react-turnstile';
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { LuLoader } from 'react-icons/lu';
|
||||
import { z } from 'zod';
|
||||
import { SignupAction } from '../_actions/signup-action';
|
||||
import {
|
||||
signupValidationSchema,
|
||||
stepOneSignupValidationSchema,
|
||||
stepTwoSignupValidationSchema,
|
||||
} from '../_validation/signup-validation';
|
||||
|
||||
export function SignupForm() {
|
||||
const router = useRouter();
|
||||
const [step, setStep] = useState(1);
|
||||
const [stepOneData, setStepOneData] = useState<z.infer<
|
||||
typeof stepOneSignupValidationSchema
|
||||
> | null>(null);
|
||||
|
||||
const { mutate, isPending, error } = useMutation({
|
||||
mutationFn: async (data: z.infer<typeof signupValidationSchema>) => {
|
||||
const result = await SignupAction(data);
|
||||
|
||||
return {
|
||||
...result,
|
||||
email: data.email,
|
||||
};
|
||||
},
|
||||
onSuccess: ({ email }) => {
|
||||
router.push(`/verification?ref=${email}`);
|
||||
},
|
||||
onError: () => {
|
||||
secondForm.reset();
|
||||
},
|
||||
});
|
||||
|
||||
const firstForm = useForm<z.infer<typeof stepOneSignupValidationSchema>>({
|
||||
resolver: zodResolver(stepOneSignupValidationSchema),
|
||||
defaultValues: {
|
||||
email: '',
|
||||
fullname: '',
|
||||
password: '',
|
||||
confirm_password: '',
|
||||
},
|
||||
});
|
||||
|
||||
const secondForm = useForm<z.infer<typeof stepTwoSignupValidationSchema>>({
|
||||
resolver: zodResolver(stepTwoSignupValidationSchema),
|
||||
defaultValues: {
|
||||
phone_number: '',
|
||||
referral_code: '',
|
||||
referred_by: '',
|
||||
student_type: '',
|
||||
token: '',
|
||||
},
|
||||
});
|
||||
|
||||
const handleFirstSubmit = (
|
||||
values: z.infer<typeof stepOneSignupValidationSchema>
|
||||
) => {
|
||||
setStepOneData(values);
|
||||
setStep(2);
|
||||
};
|
||||
|
||||
const handleSecondSubmit = (
|
||||
values: z.infer<typeof stepTwoSignupValidationSchema>
|
||||
) => {
|
||||
if (stepOneData) {
|
||||
mutate({ ...stepOneData, ...values });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{step === 1 && (
|
||||
<Form {...firstForm}>
|
||||
<form
|
||||
onSubmit={firstForm.handleSubmit(handleFirstSubmit)}
|
||||
className="space-y-4"
|
||||
>
|
||||
<FormField
|
||||
control={firstForm.control}
|
||||
name="fullname"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Full Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Nama Lengkap" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={firstForm.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="emailmu@mail.com" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={firstForm.control}
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" placeholder="••••••••" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={firstForm.control}
|
||||
name="confirm_password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Confirm Password</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" placeholder="••••••••" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full bg-[#5fbaef] hover:bg-[#22a5f1]"
|
||||
>
|
||||
Selanjutnya
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
)}
|
||||
|
||||
{step === 2 && (
|
||||
<Form {...secondForm}>
|
||||
<form
|
||||
onSubmit={secondForm.handleSubmit(handleSecondSubmit)}
|
||||
className="space-y-4"
|
||||
>
|
||||
{error && (
|
||||
<div className="p-2 text-xs bg-red-50 border border-red-200 text-red-800 rounded-sm">
|
||||
{(error as Error).message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={secondForm.control}
|
||||
name="phone_number"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nomor Telepon</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="081234567890" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex gap-x-4">
|
||||
<FormField
|
||||
control={secondForm.control}
|
||||
name="referral_code"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Kode Referral</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="ABCD" maxLength={4} {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={secondForm.control}
|
||||
name="referred_by"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Referrer</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Referral ID" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
control={secondForm.control}
|
||||
name="student_type"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Status</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="e.g., Undergraduate" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Turnstile
|
||||
siteKey="1x00000000000000000000AA"
|
||||
onSuccess={(token) => secondForm.setValue('token', token)}
|
||||
options={{
|
||||
theme: 'light',
|
||||
size: 'flexible',
|
||||
language: 'id',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => setStep(1)}
|
||||
>
|
||||
Kembali
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isPending || !secondForm.watch('token')}
|
||||
className="flex items-center gap-2 hover:bg-[#5fbaef] bg-[#22a5f1]"
|
||||
>
|
||||
{isPending ? (
|
||||
<LuLoader className="h-5 w-5 animate-spin" />
|
||||
) : (
|
||||
'Daftar'
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { fetcher } from '@/lib/openapi';
|
||||
import { SignupValidationSchema } from '../_validation/signup-validation';
|
||||
|
||||
export async function fetchPostSignin({
|
||||
email,
|
||||
password,
|
||||
fullname,
|
||||
phone_number,
|
||||
student_type,
|
||||
referral_code,
|
||||
referred_by,
|
||||
}: SignupValidationSchema) {
|
||||
const { data, error, response } = await fetcher.POST('/v1/auth/register', {
|
||||
body: {
|
||||
email,
|
||||
password,
|
||||
fullname,
|
||||
phone_number,
|
||||
student_type,
|
||||
referral_code,
|
||||
referred_by,
|
||||
},
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Someting went wrong, please try again later');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const stepOneSignupValidationSchema = z
|
||||
.object({
|
||||
email: z
|
||||
.string({
|
||||
required_error: 'Email tidak boleh kosong',
|
||||
invalid_type_error: 'Email harus berupa string',
|
||||
})
|
||||
.min(1, 'Email tidak boleh kosong')
|
||||
.email('Email harus valid'),
|
||||
fullname: z
|
||||
.string({
|
||||
required_error: 'Nama tidak boleh kosong',
|
||||
invalid_type_error: 'Nama harus berupa string',
|
||||
})
|
||||
.min(1, 'Nama tidak boleh kosong')
|
||||
.max(50, 'Nama tidak boleh lebih dari 50 karakter'),
|
||||
password: z
|
||||
.string({
|
||||
required_error: 'Password tidak boleh kosong',
|
||||
invalid_type_error: 'Password harus berupa string',
|
||||
})
|
||||
.min(1, 'Password tidak boleh kosong')
|
||||
.min(8, 'Password harus lebih dari 8 karakter')
|
||||
.max(50, 'Password tidak boleh lebih dari 50 karakter')
|
||||
.regex(
|
||||
/(?=.*[A-Z])/,
|
||||
'Password harus mengandung setidaknya satu huruf kapital'
|
||||
)
|
||||
.regex(
|
||||
/(?=.*[a-z])/,
|
||||
'Password harus mengandung setidaknya satu huruf kecil'
|
||||
)
|
||||
.regex(/(?=.*\d)/, 'Password harus mengandung setidaknya satu angka')
|
||||
.regex(
|
||||
/(?=.*[!@#$%^&*()_\-+={}[\]|\\:;"'<>,.?/~`])/,
|
||||
'Password harus mengandung setidaknya satu karakter spesial'
|
||||
),
|
||||
confirm_password: z
|
||||
.string({
|
||||
required_error: 'Konfirmasi password tidak boleh kosong',
|
||||
})
|
||||
.min(1, 'Konfirmasi password tidak boleh kosong')
|
||||
.max(50, 'Konfirmasi password tidak boleh lebih dari 50 karakter'),
|
||||
})
|
||||
.refine((data) => data.password === data.confirm_password, {
|
||||
message: 'Password dan Konfirmasi Password harus sama',
|
||||
path: ['confirm_password'],
|
||||
});
|
||||
|
||||
export const stepTwoSignupValidationSchema = z.object({
|
||||
phone_number: z
|
||||
.string({
|
||||
required_error: 'Nomor telepon tidak boleh kosong',
|
||||
invalid_type_error: 'Nomor telepon harus berupa string',
|
||||
})
|
||||
.min(10, 'Nomor telepon tidak boleh kurang dari 10 karakter')
|
||||
.max(15, 'Nomor telepon tidak boleh lebih dari 15 karakter')
|
||||
.regex(/^\d+$/, 'Nomor telepon hanya boleh berisi angka'),
|
||||
referral_code: z
|
||||
.string()
|
||||
.max(4, 'Kode referral tidak boleh lebih dari 4 karakter')
|
||||
.optional(),
|
||||
referred_by: z
|
||||
.string()
|
||||
.max(50, 'Kode referral tidak boleh lebih dari 50 karakter')
|
||||
.optional(),
|
||||
student_type: z.string(),
|
||||
token: z.string(),
|
||||
});
|
||||
|
||||
export const signupValidationSchema = stepOneSignupValidationSchema.and(
|
||||
stepTwoSignupValidationSchema
|
||||
);
|
||||
export type SignupValidationSchema = z.infer<typeof signupValidationSchema>
|
||||
@@ -1,3 +1,18 @@
|
||||
import { LogoSimple } from '../../_components/logo';
|
||||
import { SignupForm } from './_components/signup-form';
|
||||
|
||||
export default function Page() {
|
||||
return <></>;
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<LogoSimple />
|
||||
|
||||
<p className="text-muted-foreground text-center text-sm sm:text-base">
|
||||
Buat akunmu sekarang
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<SignupForm />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
'use server';
|
||||
|
||||
import { fetcher } from '@/lib/openapi';
|
||||
|
||||
export async function resendOTPAction(email: string) {
|
||||
const data = await fetcher.POST('/v1/auth/send-otp', {
|
||||
body: {
|
||||
email,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(data);
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
'use server';
|
||||
|
||||
import { fetchPostVerifyEmail } from '../_http/fetch-post-verify-email';
|
||||
import {
|
||||
type VerifyEmailValidationType,
|
||||
verifyEmailSchema,
|
||||
} from '../_validation/verify-email-validation';
|
||||
|
||||
export async function verifyEmailAction(request: VerifyEmailValidationType) {
|
||||
const validRequest = verifyEmailSchema.parse(request);
|
||||
|
||||
const data = await fetchPostVerifyEmail(validRequest);
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
'use client';
|
||||
|
||||
import { Button } from '@components/atoms';
|
||||
import { usePostResendOTP } from '../_hooks/use-post-resend-otp';
|
||||
|
||||
export function ButtonResendOTP({ email }: { email: string }) {
|
||||
const { mutate } = usePostResendOTP();
|
||||
|
||||
const handleResendOTP = () => mutate(email);
|
||||
|
||||
return (
|
||||
<Button onClick={handleResendOTP} className="w-full -mt-2">
|
||||
Kirim Ulang Kode OTP
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
Input,
|
||||
} from '@components/atoms';
|
||||
import { LuLoader } from 'react-icons/lu';
|
||||
import { useFormVerifyEmail } from '../_hooks/use-form-verify-email';
|
||||
import { usePostVerifyEmail } from '../_hooks/use-post-verify-email';
|
||||
import { type VerifyEmailValidationType } from '../_validation/verify-email-validation';
|
||||
import { ButtonResendOTP } from './button-resend-otp';
|
||||
|
||||
export function VerifyEmailForm() {
|
||||
const form = useFormVerifyEmail();
|
||||
const { mutate, isPending, error } = usePostVerifyEmail(form);
|
||||
|
||||
const onSubmit = (values: VerifyEmailValidationType) => {
|
||||
mutate(values);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
{error && (
|
||||
<div className="p-2 text-xs bg-red-50 border border-red-200 text-red-800 rounded-sm">
|
||||
{(error as Error).message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="emailmu@mail.com" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="otp"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>OTP</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="123456" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isPending}
|
||||
className="w-full hover:bg-[#5fbaef] bg-[#22a5f1] font-bold"
|
||||
>
|
||||
{isPending ? (
|
||||
<LuLoader className="h-5 w-5 animate-spin" />
|
||||
) : (
|
||||
'Verifikasi'
|
||||
)}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<ButtonResendOTP email={form.watch('email')} />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import {
|
||||
verifyEmailSchema,
|
||||
type VerifyEmailValidationType,
|
||||
} from '../_validation/verify-email-validation';
|
||||
|
||||
export function useFormVerifyEmail() {
|
||||
const searchParams = useSearchParams();
|
||||
const email = searchParams.get('ref');
|
||||
|
||||
return useForm<VerifyEmailValidationType>({
|
||||
resolver: zodResolver(verifyEmailSchema),
|
||||
defaultValues: {
|
||||
email: email ?? '',
|
||||
otp: '',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { resendOTPAction } from '../_actions/resend-otp-action';
|
||||
|
||||
export function usePostResendOTP() {
|
||||
const router = useRouter();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: resendOTPAction,
|
||||
onSuccess: () => router.replace('/verification?success=true'),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { verifyEmailAction } from '../_actions/verify-email-action';
|
||||
import { useFormVerifyEmail } from './use-form-verify-email';
|
||||
|
||||
export function usePostVerifyEmail(
|
||||
form: ReturnType<typeof useFormVerifyEmail>
|
||||
) {
|
||||
const router = useRouter();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: verifyEmailAction,
|
||||
onSuccess: () => {
|
||||
router.push('/feedback');
|
||||
},
|
||||
onError: () => {
|
||||
form.resetField('otp');
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { fetcher } from '@/lib/openapi';
|
||||
import { VerifyEmailValidationType } from '../_validation/verify-email-validation';
|
||||
|
||||
export async function fetchPostVerifyEmail({
|
||||
email,
|
||||
otp,
|
||||
}: VerifyEmailValidationType) {
|
||||
const formattedOTP = parseInt(otp);
|
||||
|
||||
const { data, error, response } = await fetcher.POST(
|
||||
'/v1/auth/verify-email',
|
||||
{
|
||||
body: {
|
||||
email,
|
||||
otp: formattedOTP,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Something went wrong, please try again later');
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
|
||||
return data;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const verifyEmailSchema = z.object({
|
||||
email: z.string().email({ message: 'Format email tidak valid' }),
|
||||
otp: z
|
||||
.string()
|
||||
.min(6, { message: 'OTP harus terdiri dari 6 digit' })
|
||||
.max(6, { message: 'OTP harus terdiri dari 6 digit' }),
|
||||
});
|
||||
export type VerifyEmailValidationType = z.infer<typeof verifyEmailSchema>;
|
||||
@@ -0,0 +1,18 @@
|
||||
import { LogoSimple } from '../../_components/logo';
|
||||
import { VerifyEmailForm } from './_components/verify-email-form';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<LogoSimple />
|
||||
|
||||
<p className="text-muted-foreground text-center text-sm sm:text-base">
|
||||
Verifikasi akun mu sekarang
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<VerifyEmailForm />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,194 @@
|
||||
export default function Page() {
|
||||
return <></>;
|
||||
'use client';
|
||||
|
||||
import events from '@/data/events.json';
|
||||
import { buttonVariants } from '@components/atoms';
|
||||
import { cn } from '@utils/ui';
|
||||
import Image from 'next/image';
|
||||
import { HiCalendar, HiClock, HiLocationMarker } from 'react-icons/hi';
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleDateString('id-ID', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
});
|
||||
};
|
||||
|
||||
const formatTime = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleTimeString('id-ID', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
timeZone: 'Asia/Jakarta',
|
||||
});
|
||||
};
|
||||
|
||||
const getEventStatus = (endDate: string) => {
|
||||
const now = new Date();
|
||||
const end = new Date(endDate);
|
||||
return end > now ? 'upcoming' : 'past';
|
||||
};
|
||||
|
||||
export default function EventsPage() {
|
||||
const sortedEvents = [...events].sort(
|
||||
(a, b) =>
|
||||
new Date(b.start_date).getTime() - new Date(a.start_date).getTime()
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<div className="max-w-7xl mx-auto px-4 py-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{/* Featured Card */}
|
||||
<div className="col-span-full">
|
||||
<div className="rounded-xl shadow-sm hover:shadow-md transition-shadow duration-200 overflow-hidden bg-card">
|
||||
<div className="grid md:grid-cols-2">
|
||||
<div className="min-h-96 bg-muted relative">
|
||||
<Image
|
||||
src={sortedEvents[0].thumbnail}
|
||||
alt={sortedEvents[0].name}
|
||||
fill
|
||||
className="object-cover object-top"
|
||||
sizes="(max-width: 768px) 100vw, 50vw"
|
||||
priority
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
<div className="p-8 flex flex-col">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="bg-primary/20 text-primary text-xs px-2.5 py-1 rounded-full">
|
||||
Event Terbaru
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
'px-2 py-1 rounded-full text-xs',
|
||||
getEventStatus(sortedEvents[0].end_date) === 'upcoming'
|
||||
? 'bg-primary/20 text-primary'
|
||||
: 'bg-muted text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{getEventStatus(sortedEvents[0].end_date) === 'upcoming'
|
||||
? 'Upcoming'
|
||||
: 'Selesai'}
|
||||
</span>
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold mb-4 text-foreground">
|
||||
{sortedEvents[0].name}
|
||||
</h2>
|
||||
<div className="space-y-3 mb-6 text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-2">
|
||||
<HiCalendar className="w-4 h-4" />
|
||||
<span>{formatDate(sortedEvents[0].start_date)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<HiClock className="w-4 h-4" />
|
||||
<span>
|
||||
{formatTime(sortedEvents[0].start_date)} -{' '}
|
||||
{formatTime(sortedEvents[0].end_date)} WIB
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<HiLocationMarker className="w-4 h-4" />
|
||||
<span>
|
||||
{sortedEvents[0].type === 'online'
|
||||
? 'Online'
|
||||
: sortedEvents[0].location}
|
||||
</span>
|
||||
</div>
|
||||
{sortedEvents[0].price > 0 && (
|
||||
<div className="mt-1 font-medium">
|
||||
Rp {sortedEvents[0].price.toLocaleString('id-ID')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-muted-foreground mb-6 line-clamp-4">
|
||||
{sortedEvents[0].description}
|
||||
</p>
|
||||
<a
|
||||
href={sortedEvents[0].detail_link}
|
||||
target="_blank"
|
||||
className={cn(
|
||||
buttonVariants({ variant: 'default' }),
|
||||
'mt-auto w-full md:w-fit'
|
||||
)}
|
||||
>
|
||||
Lihat Detail
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Regular Cards */}
|
||||
{sortedEvents.slice(1).map((event) => (
|
||||
<div
|
||||
key={event.name}
|
||||
className="rounded-xl shadow-sm hover:shadow-md transition-shadow duration-200 bg-card"
|
||||
>
|
||||
<div className="h-48 bg-muted relative">
|
||||
<Image
|
||||
src={event.thumbnail}
|
||||
alt={event.name}
|
||||
fill
|
||||
className="object-cover object-top rounded-t-xl"
|
||||
sizes="(max-width: 768px) 100vw, 33vw"
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<span
|
||||
className={cn(
|
||||
'px-2 py-1 rounded-full text-xs',
|
||||
getEventStatus(event.end_date) === 'upcoming'
|
||||
? 'bg-primary/20 text-primary'
|
||||
: 'bg-muted text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{getEventStatus(event.end_date) === 'upcoming'
|
||||
? 'Upcoming'
|
||||
: 'Selesai'}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold mb-3 text-foreground">
|
||||
{event.name}
|
||||
</h3>
|
||||
<div className="space-y-2 mb-4 text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-2">
|
||||
<HiCalendar className="w-4 h-4" />
|
||||
<span>{formatDate(event.start_date)}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<HiLocationMarker className="w-4 h-4" />
|
||||
<span>
|
||||
{event.type === 'online' ? 'Online' : event.location}
|
||||
</span>
|
||||
</div>
|
||||
{event.price > 0 && (
|
||||
<div className="font-medium">
|
||||
Rp {event.price.toLocaleString('id-ID')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-muted-foreground text-sm mb-4 line-clamp-3">
|
||||
{event.description}
|
||||
</p>
|
||||
<a
|
||||
href={event.detail_link}
|
||||
target="_blank"
|
||||
className={cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'w-full text-sm'
|
||||
)}
|
||||
>
|
||||
Lihat Detail
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,114 @@
|
||||
export default function Page() {
|
||||
return <></>;
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@components/atoms';
|
||||
import Link from 'next/link';
|
||||
import { BsChatLeftQuote } from 'react-icons/bs';
|
||||
|
||||
const testimonials = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'User 1',
|
||||
date: 'April 2025',
|
||||
text: 'Bergabung dengan komunitas ini memberikan saya banyak inspirasi. Saya belajar banyak hal baru dan bertemu dengan orang-orang yang luar biasa.',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'User 2',
|
||||
date: 'Maret 2025',
|
||||
text: 'Pengalaman yang sangat berharga. Saya mendapatkan banyak wawasan baru dan dapat mengembangkan keterampilan menulis saya dengan lebih baik.',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'User 3',
|
||||
date: 'Maret 2025',
|
||||
text: 'Komunitas ini sangat mendukung dan memotivasi. Saya senang bisa menjadi bagian dari perjalanan ini dan berbagi pengalaman dengan sesama anggota.',
|
||||
},
|
||||
];
|
||||
|
||||
export default function TestimonialPage() {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
{/* Hero Section */}
|
||||
<div className="py-16 px-4 text-center border-b border-border">
|
||||
<BsChatLeftQuote className="size-14 mx-auto my-4 text-foreground" />
|
||||
<h1 className="text-4xl font-bold mb-4 text-foreground">Testimonial</h1>
|
||||
<p className="max-w-2xl mx-auto text-lg mb-8 text-balance text-muted-foreground">
|
||||
Menampilkan pengalaman dan cerita para member yang telah join ke dalam
|
||||
komunitas kami
|
||||
</p>
|
||||
<Link href="/testimonial/submit">
|
||||
<Button>Tulis Testimonialmu</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Search and Filter */}
|
||||
<div className="max-w-7xl mx-auto px-4 py-6 border-b border-border">
|
||||
<div className="flex flex-col md:flex-row gap-4 justify-between">
|
||||
<div className="relative w-full md:w-96">
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Cari berdasarkan topik..."
|
||||
className="w-full bg-background"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<Select defaultValue="all-time">
|
||||
<SelectTrigger className="w-[180px] bg-background">
|
||||
<SelectValue placeholder="Semua Periode" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-popover text-popover-foreground">
|
||||
<SelectItem value="all-time">Semua Periode</SelectItem>
|
||||
<SelectItem value="this-month">Bulan Ini</SelectItem>
|
||||
<SelectItem value="last-month">Bulan Lalu</SelectItem>
|
||||
<SelectItem value="this-year">Tahun Ini</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select defaultValue="all-topics">
|
||||
<SelectTrigger className="w-[180px] bg-background">
|
||||
<SelectValue placeholder="Semua Topik" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-popover text-popover-foreground">
|
||||
<SelectItem value="all-topics">Semua Topik</SelectItem>
|
||||
<SelectItem value="writing">Menulis</SelectItem>
|
||||
<SelectItem value="community">Komunitas</SelectItem>
|
||||
<SelectItem value="learning">Pembelajaran</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Testimonial Cards */}
|
||||
<div className="max-w-7xl mx-auto px-4 py-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{testimonials.map((testimonial) => (
|
||||
<div
|
||||
key={testimonial.id}
|
||||
className="border rounded-lg p-6 shadow-sm bg-card border-border"
|
||||
>
|
||||
<div className="flex items-center mb-4">
|
||||
<div>
|
||||
<h3 className="font-semibold text-foreground">
|
||||
{testimonial.name}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Lulus: {testimonial.date}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-foreground/80 italic">
|
||||
"{testimonial.text}"
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import Link from 'next/link';
|
||||
export function DesktopNavigation() {
|
||||
return (
|
||||
<nav className="hidden md:flex items-center gap-8">
|
||||
<Link href="/about" className="text-sm font-medium relative group">
|
||||
<span className="transition-colors hover:text-primary">About</span>
|
||||
<Link href="/" className="text-sm font-medium relative group">
|
||||
<span className="transition-colors hover:text-primary">Home</span>
|
||||
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
||||
</Link>
|
||||
<Link href="/events" className="text-sm font-medium relative group">
|
||||
@@ -17,6 +17,12 @@ export function DesktopNavigation() {
|
||||
</span>
|
||||
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
||||
</Link>
|
||||
<Link href="/request" className="text-sm font-medium relative group">
|
||||
<span className="transition-colors hover:text-primary">
|
||||
Request Fitur
|
||||
</span>
|
||||
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
||||
</Link>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { HeaderWrapper } from './header-wrapper';
|
||||
import { Logo } from './logo';
|
||||
import { MobileMenuHamburger } from './mobile-menu-hamburger';
|
||||
import { MobileNavigation } from './mobile-navigation';
|
||||
import { SimpleThemeToggle } from './simple-theme-toggle';
|
||||
|
||||
export function Header() {
|
||||
const mobileMenuOpen = useMobileMenuStore((s) => s.mobileMenuOpen);
|
||||
@@ -23,7 +22,6 @@ export function Header() {
|
||||
<div className="flex items-center gap-x-2">
|
||||
<ButtonSignin />
|
||||
<ButtonSignup />
|
||||
<SimpleThemeToggle />
|
||||
<MobileMenuHamburger />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { useMobileMenuStore } from '@/stores/mobile-menu-store';
|
||||
import { Button, MenuIcon, XIcon } from '@components/atoms';
|
||||
import { Button } from '@components/atoms';
|
||||
import { LuMenu, LuX } from 'react-icons/lu';
|
||||
|
||||
export function MobileMenuHamburger() {
|
||||
const mobileMenuOpen = useMobileMenuStore((s) => s.mobileMenuOpen);
|
||||
@@ -15,9 +16,9 @@ export function MobileMenuHamburger() {
|
||||
onClick={toggleMobileMenu}
|
||||
>
|
||||
{mobileMenuOpen ? (
|
||||
<XIcon className="h-6 w-6" />
|
||||
<LuX className="h-6 w-6" />
|
||||
) : (
|
||||
<MenuIcon className="h-6 w-6" />
|
||||
<LuMenu className="h-6 w-6" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -5,10 +5,10 @@ export function MobileNavigation() {
|
||||
<div className="md:hidden border-t bg-background/95 backdrop-blur-md">
|
||||
<nav className="container flex flex-col py-4 text-center">
|
||||
<Link
|
||||
href="/about"
|
||||
href="/"
|
||||
className="py-3 text-sm font-medium border-b border-border/50"
|
||||
>
|
||||
About
|
||||
Home
|
||||
</Link>
|
||||
<Link
|
||||
href="/events"
|
||||
@@ -22,6 +22,12 @@ export function MobileNavigation() {
|
||||
>
|
||||
Testimonial
|
||||
</Link>
|
||||
<Link href="/request" className="text-sm font-medium relative group">
|
||||
<span className="transition-colors hover:text-primary">
|
||||
Request Fitur
|
||||
</span>
|
||||
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary transition-all duration-300 group-hover:w-full"></span>
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { Button, MoonIcon, SunIcon } from '@components/atoms';
|
||||
import { Button } from '@components/atoms';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { LuMoon, LuSun } from 'react-icons/lu';
|
||||
|
||||
export function SimpleThemeToggle() {
|
||||
const { theme, setTheme } = useTheme();
|
||||
@@ -28,9 +29,9 @@ export function SimpleThemeToggle() {
|
||||
className="focus-visible:ring-0 cursor-pointer"
|
||||
>
|
||||
{theme === 'dark' ? (
|
||||
<SunIcon className="h-[1.2rem] w-[1.2rem]" />
|
||||
<LuSun className="h-[1.2rem] w-[1.2rem]" />
|
||||
) : (
|
||||
<MoonIcon className="h-[1.2rem] w-[1.2rem]" />
|
||||
<LuMoon className="h-[1.2rem] w-[1.2rem]" />
|
||||
)}
|
||||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import { QueryProvider } from '@utils/ui';
|
||||
import type { ThemeProviderProps } from 'next-themes';
|
||||
import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
||||
|
||||
export function Providers({ children, ...props }: ThemeProviderProps) {
|
||||
return (
|
||||
<QueryProvider>
|
||||
<NextThemesProvider {...props}>{children}</NextThemesProvider>
|
||||
</QueryProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import type { ThemeProviderProps } from 'next-themes';
|
||||
import { ThemeProvider as NextThemesProvider } from 'next-themes';
|
||||
|
||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { poppins } from '@/lib/fonts';
|
||||
import '@/styles/globals.css';
|
||||
import { type Metadata } from 'next';
|
||||
import { ThemeProvider } from './_components/theme-provider';
|
||||
import { Providers } from './_components/providers';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'IMPHNEN - Ingin Menjadi Programmer Handal Namung Enggan Ngonding',
|
||||
title: 'IMPHNEN - Ingin Menjadi Programmer Handal Namun Enggan Ngonding',
|
||||
description:
|
||||
'Komunitas Ingin Menjadi Programmer Handal Namung Enggan Ngonding',
|
||||
};
|
||||
@@ -17,14 +17,14 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="id" suppressHydrationWarning>
|
||||
<body className={poppins.className}>
|
||||
<ThemeProvider
|
||||
<Providers
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
defaultTheme="light"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</Providers>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
[
|
||||
{
|
||||
"name": "IMPHNEN X GDG Medan",
|
||||
"description": "Join us for the Google Cloud Roadshow: Build with AI in Medan! This is your chance to dive deep into the latest advancements in cloud technology and AI, guided by industry experts.",
|
||||
"start_date": "2025-04-26T13:00:00+07:00",
|
||||
"end_date": "2025-04-26T17:00:00+07:00",
|
||||
"thumbnail": "https://scontent.fsoc1-2.fna.fbcdn.net/v/t39.30808-6/492363939_2132158393873766_2304913620264481070_n.jpg?_nc_cat=105&ccb=1-7&_nc_sid=127cfc&_nc_eui2=AeFe43oCdy2FOIVI6pr_grkO_q5XyVVxt5v-rlfJVXG3m9KgINIp9lo_vzXP4CtAAFJviDNJdDPaPyGnz7B8-gfE&_nc_ohc=bv3GExoL4pUQ7kNvwEpV7xQ&_nc_oc=Adk45dIpzgFxCHgtQKDTo99Sewj0ZMO4LX1cNEI2Dj7VeQDp8jd1RzYnG_UwsKkaTug&_nc_zt=23&_nc_ht=scontent.fsoc1-2.fna&_nc_gid=ZPKg-Y9FLRXcDiSak5WcLA&oh=00_AfL7BFg5g-q6MALMRFUlgaUWK8jHxQVTHdZ_1ezxBthXfw&oe=68354615",
|
||||
"type": "onsite",
|
||||
"price": 0,
|
||||
"location": "Magnificient, Medan",
|
||||
"detail_link": "https://n8n.gdgmedan.com/form/8466f60d-94b0-42a6-93aa-8738ae4ab5df?fbclid=IwY2xjawKcQoNleHRuA2FlbQIxMABicmlkETFnQ1RvSU5aWVhpWldaOXBjAR7LUWa_npCLb-QSfk1QFTl12tyw_eBhNO8oI8N2LvhwweeE5_41uQsARsEQGw_aem_1IP2vqrQcvjsvLCBy2C6Hg"
|
||||
},
|
||||
{
|
||||
"name": "IMPHNEN X Connect Citcom",
|
||||
"description": "Unleash the Future with AI: Decode the potential of artificial intelligence to revolutionize your strategies, unchain innovation, and gain a powerful competitive edge in today's fast-evolving business landscape. Harness the transformative capabilities of AI to outsmart competitors, optimize operations, and drive sustainable growth.",
|
||||
"start_date": "2025-04-22T13:00:00+07:00",
|
||||
"end_date": "2025-04-22T17:00:00+07:00",
|
||||
"thumbnail": "https://scontent.fsoc1-2.fna.fbcdn.net/v/t39.30808-6/490432625_2122941998128739_5372979458124718061_n.jpg?stp=dst-jpg_p526x296_tt6&_nc_cat=105&ccb=1-7&_nc_sid=aa7b47&_nc_eui2=AeGc_W_0Gd3XY9X9ImKz1h1HpYKlinPYX-qlgqWKc9hf6k75HObQiJ8A8m6BZOIe2OrVQjjyXnHq08Rc4JuQWeCQ&_nc_ohc=uoSae0n37k0Q7kNvwHch8E5&_nc_oc=AdngSKtrCdZ4H3hlHh-9cttOnaKyrwiFEcm88kjp0gaflvQ65LUp4OT45-XVGDAoIo8&_nc_zt=23&_nc_ht=scontent.fsoc1-2.fna&_nc_gid=YGkoLGAxNuB0uF3C9hSYGQ&oh=00_AfLRkrzG7DRYa3HRuy9aXfUfG6zLkRBDj9LxqXKOfF9x7Q&oe=683546E0",
|
||||
"type": "onsite",
|
||||
"price": 0,
|
||||
"location": "El Hotel, Bandung",
|
||||
"detail_link": "/events/1"
|
||||
},
|
||||
{
|
||||
"name": "JVM Meetup #64",
|
||||
"description": "Join us for an insightful talk where we’ll explore how AI is reshaping the payment industry. Whether you’re into tech, AI, or just curious about the future of transactions, this is a must-attend event!",
|
||||
"start_date": "2025-04-30T13:00:00+07:00",
|
||||
"end_date": "2025-04-30T17:00:00+07:00",
|
||||
"thumbnail": "https://scontent.fsoc1-1.fna.fbcdn.net/v/t39.30808-6/494158983_2136830150073257_5259003432367969457_n.jpg?_nc_cat=100&ccb=1-7&_nc_sid=833d8c&_nc_eui2=AeGcneO8ihnXyQ_7WV93z7Onz0UPVTr-bf_PRQ9VOv5t_wI-el8xouZ-Z4TKGMMlBPi21d9AA55SP54MHkzO4rx1&_nc_ohc=3Q9NKGIkpT4Q7kNvwHKzwXr&_nc_oc=AdkRJ--zwRGcfX_F2eIgx0qhK-N3OkhwqRwujFhgSRWy5mH_SdIyBmwW8PHHXp71ZUM&_nc_zt=23&_nc_ht=scontent.fsoc1-1.fna&_nc_gid=Xi30SjzSBAU1XN9rRxp0VA&oh=00_AfKvjd9LTMfOwfPLD43U8lKYEr4KABecj336J8hAY8ekMg&oe=68353105",
|
||||
"type": "onsite",
|
||||
"price": 0,
|
||||
"location": "El Hotel, Bandung",
|
||||
"detail_link": "https://lu.ma/422jkgz0?utm_campaign=jvmmeetup&utm_medium=social%2C%20event%2C%20meetup&utm_source=google%2C%20facebook%2C%20linkedin%2C%20instagram"
|
||||
},
|
||||
{
|
||||
"name": "JVM Meetup #65",
|
||||
"description": "Di era digital yang terus berkembang, Artificial Intelligence (AI) bukan lagi teknologi masa depantapi alat bantu masa kini yang siap mendongkrak efisiensi, kreativitas, dan produktivitas kita semua, terutama para penggiat IT dan pelaku usaha!",
|
||||
"start_date": "2025-05-15T13:00:00+07:00",
|
||||
"end_date": "2025-05-15T17:00:00+07:00",
|
||||
"thumbnail": "https://scontent.fsoc1-1.fna.fbcdn.net/v/t39.30808-6/495382435_2144684952621110_8304851620615091109_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=833d8c&_nc_eui2=AeHwnU4KixfUsslo3M90ahT8GwMEI7zWSnQbAwQjvNZKdP3RHiUjdDZtuF1dIMvorL0nW0rDGOD6tPDi0i9ERK96&_nc_ohc=R1NyXnu-FKEQ7kNvwGbO-iN&_nc_oc=Adluf-uw1tmzkNME9vNhwkTv5-x_GHMfsLj1bzGyYds_ODq3UnWMq2DthyUisF9e3gU&_nc_zt=23&_nc_ht=scontent.fsoc1-1.fna&_nc_gid=kykMk9nPtcrtk-InL6tF6w&oh=00_AfLZSrEPpV83lVFRMz19n9RzfnsM4o7K7TijpdAjWYViiA&oe=6835231B",
|
||||
"type": "onsite",
|
||||
"price": 0,
|
||||
"location": "Telkom Landmark Tower (Lt.31)",
|
||||
"detail_link": "s.id/jvm65"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
const REFRESH_TOKEN_NAME = '__imphnen_refresh_token__';
|
||||
const ACCESS_TOKEN_NAME = '__imphnen_access_token__';
|
||||
|
||||
export async function setRefreshToken(refreshToken: string) {
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.set(REFRESH_TOKEN_NAME, refreshToken);
|
||||
}
|
||||
|
||||
export async function setAccessToken(accessToken: string) {
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.set(ACCESS_TOKEN_NAME, accessToken);
|
||||
}
|
||||
|
||||
export async function getRefreshToken() {
|
||||
const cookieStore = await cookies();
|
||||
return cookieStore.get(REFRESH_TOKEN_NAME)?.value;
|
||||
}
|
||||
|
||||
export async function getAccessToken() {
|
||||
const cookieStore = await cookies();
|
||||
return cookieStore.get(ACCESS_TOKEN_NAME)?.value;
|
||||
}
|
||||
@@ -116,6 +116,166 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/gacha/claims/create": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post: operations["post_create_gacha_claim"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/gacha/claims/detail/{id}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: operations["get_detail_gacha_claim"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/gacha/items": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: operations["get_gacha_item_list"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/gacha/items/create": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post: operations["post_create_gacha_item"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/gacha/items/delete/{id}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete: operations["delete_gacha_item"];
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/gacha/items/detail/{id}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: operations["get_gacha_item_by_id"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/gacha/items/update/{id}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put: operations["put_update_gacha_item"];
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/gacha/rolls/create": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post: operations["post_create_gacha_roll"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/gacha/rolls/detail/{id}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: operations["get_detail_gacha_roll"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/gacha/rolls/execute": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post: operations["post_execute_gacha_roll"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/permissions": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -414,7 +574,7 @@ export interface components {
|
||||
};
|
||||
AuthLoginResponsetDto: {
|
||||
token: components["schemas"]["TokenDto"];
|
||||
user: components["schemas"]["AuthUserItemDto"];
|
||||
user: components["schemas"]["UsersDetailItemDto"];
|
||||
};
|
||||
AuthNewPasswordRequestDto: {
|
||||
password: string;
|
||||
@@ -428,29 +588,56 @@ export interface components {
|
||||
fullname: string;
|
||||
password: string;
|
||||
phone_number: string;
|
||||
referral_code?: string | null;
|
||||
referred_by?: string | null;
|
||||
student_type: string;
|
||||
};
|
||||
AuthResendOtpRequestDto: {
|
||||
email: string;
|
||||
};
|
||||
AuthUserItemDto: {
|
||||
avatar?: string | null;
|
||||
birthdate?: string | null;
|
||||
email: string;
|
||||
fullname: string;
|
||||
gender?: string | null;
|
||||
id: string;
|
||||
is_active: boolean;
|
||||
phone_number: string;
|
||||
role: components["schemas"]["RolesItemDto"];
|
||||
};
|
||||
AuthVerifyEmailRequestDto: {
|
||||
email: string;
|
||||
/** Format: int32 */
|
||||
otp: number;
|
||||
};
|
||||
GachaClaimItemDto: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
is_deleted: boolean;
|
||||
item: components["schemas"]["GachaItemDto"];
|
||||
updated_at?: string | null;
|
||||
user: components["schemas"]["UsersDetailItemDto"];
|
||||
};
|
||||
GachaClaimRequestDto: {
|
||||
item_id: string;
|
||||
user_id: string;
|
||||
};
|
||||
GachaItemDto: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
is_deleted: boolean;
|
||||
name: string;
|
||||
updated_at?: string | null;
|
||||
};
|
||||
GachaItemRequestDto: {
|
||||
image_url: string;
|
||||
name: string;
|
||||
};
|
||||
GachaRollItemDto: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
is_deleted: boolean;
|
||||
item: components["schemas"]["GachaItemDto"];
|
||||
/** Format: int32 */
|
||||
quantity: number;
|
||||
updated_at?: string | null;
|
||||
/** Format: float */
|
||||
weight: number;
|
||||
};
|
||||
GachaRollRequestDto: {
|
||||
item_id: string;
|
||||
/** Format: int32 */
|
||||
quantity: number;
|
||||
/** Format: float */
|
||||
weight: number;
|
||||
};
|
||||
MessageResponseDto: {
|
||||
message: string;
|
||||
version: string;
|
||||
@@ -483,6 +670,16 @@ export interface components {
|
||||
PermissionsRequestDto: {
|
||||
name: string;
|
||||
};
|
||||
ResponseListSuccessDto_Vec_GachaItemDto: {
|
||||
data: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
is_deleted: boolean;
|
||||
name: string;
|
||||
updated_at?: string | null;
|
||||
}[];
|
||||
meta?: null | components["schemas"]["MetaResponseDto"];
|
||||
};
|
||||
ResponseListSuccessDto_Vec_PermissionsItemDto: {
|
||||
data: {
|
||||
created_at?: string | null;
|
||||
@@ -492,12 +689,12 @@ export interface components {
|
||||
}[];
|
||||
meta?: null | components["schemas"]["MetaResponseDto"];
|
||||
};
|
||||
ResponseListSuccessDto_Vec_RolesItemDto: {
|
||||
ResponseListSuccessDto_Vec_RolesListItemDto: {
|
||||
data: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
name: string;
|
||||
permissions: components["schemas"]["PermissionsItemDto"][];
|
||||
permissions_count: number;
|
||||
updated_at?: string | null;
|
||||
}[];
|
||||
meta?: null | components["schemas"]["MetaResponseDto"];
|
||||
@@ -519,7 +716,39 @@ export interface components {
|
||||
ResponseSuccessDto_AuthLoginResponsetDto: {
|
||||
data: {
|
||||
token: components["schemas"]["TokenDto"];
|
||||
user: components["schemas"]["AuthUserItemDto"];
|
||||
user: components["schemas"]["UsersDetailItemDto"];
|
||||
};
|
||||
};
|
||||
ResponseSuccessDto_GachaClaimItemDto: {
|
||||
data: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
is_deleted: boolean;
|
||||
item: components["schemas"]["GachaItemDto"];
|
||||
updated_at?: string | null;
|
||||
user: components["schemas"]["UsersDetailItemDto"];
|
||||
};
|
||||
};
|
||||
ResponseSuccessDto_GachaItemDto: {
|
||||
data: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
is_deleted: boolean;
|
||||
name: string;
|
||||
updated_at?: string | null;
|
||||
};
|
||||
};
|
||||
ResponseSuccessDto_GachaRollItemDto: {
|
||||
data: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
is_deleted: boolean;
|
||||
item: components["schemas"]["GachaItemDto"];
|
||||
/** Format: int32 */
|
||||
quantity: number;
|
||||
updated_at?: string | null;
|
||||
/** Format: float */
|
||||
weight: number;
|
||||
};
|
||||
};
|
||||
ResponseSuccessDto_PermissionsItemDto: {
|
||||
@@ -530,10 +759,11 @@ export interface components {
|
||||
updated_at?: string | null;
|
||||
};
|
||||
};
|
||||
ResponseSuccessDto_RolesItemDto: {
|
||||
ResponseSuccessDto_RolesDetailItemDto: {
|
||||
data: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
is_deleted: boolean;
|
||||
name: string;
|
||||
permissions: components["schemas"]["PermissionsItemDto"][];
|
||||
updated_at?: string | null;
|
||||
@@ -549,20 +779,30 @@ export interface components {
|
||||
data: {
|
||||
avatar?: string | null;
|
||||
birthdate?: string | null;
|
||||
created_at: string;
|
||||
email: string;
|
||||
fullname: string;
|
||||
gender?: string | null;
|
||||
id: string;
|
||||
is_active: boolean;
|
||||
phone_number: string;
|
||||
role: components["schemas"]["RolesItemDto"];
|
||||
role: components["schemas"]["RolesDetailItemDto"];
|
||||
updated_at: string;
|
||||
};
|
||||
};
|
||||
RolesItemDto: {
|
||||
RolesDetailItemDto: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
is_deleted: boolean;
|
||||
name: string;
|
||||
permissions: components["schemas"]["PermissionsItemDto"][];
|
||||
updated_at?: string | null;
|
||||
};
|
||||
RolesListItemDto: {
|
||||
created_at?: string | null;
|
||||
id: string;
|
||||
name: string;
|
||||
permissions: components["schemas"]["PermissionsItemDto"][];
|
||||
permissions_count: number;
|
||||
updated_at?: string | null;
|
||||
};
|
||||
RolesRequestCreateDto: {
|
||||
@@ -571,6 +811,7 @@ export interface components {
|
||||
};
|
||||
RolesRequestUpdateDto: {
|
||||
name?: string | null;
|
||||
overwrite?: boolean | null;
|
||||
permissions?: string[] | null;
|
||||
};
|
||||
TokenDto: {
|
||||
@@ -589,17 +830,6 @@ export interface components {
|
||||
role_id: string;
|
||||
};
|
||||
UsersDetailItemDto: {
|
||||
avatar?: string | null;
|
||||
birthdate?: string | null;
|
||||
email: string;
|
||||
fullname: string;
|
||||
gender?: string | null;
|
||||
id: string;
|
||||
is_active: boolean;
|
||||
phone_number: string;
|
||||
role: components["schemas"]["RolesItemDto"];
|
||||
};
|
||||
UsersItemDto: {
|
||||
avatar?: string | null;
|
||||
birthdate?: string | null;
|
||||
created_at: string;
|
||||
@@ -608,10 +838,8 @@ export interface components {
|
||||
gender?: string | null;
|
||||
id: string;
|
||||
is_active: boolean;
|
||||
is_deleted: boolean;
|
||||
password: string;
|
||||
phone_number: string;
|
||||
role: components["schemas"]["RolesItemDto"];
|
||||
role: components["schemas"]["RolesDetailItemDto"];
|
||||
updated_at: string;
|
||||
};
|
||||
UsersListItemDto: {
|
||||
@@ -875,6 +1103,250 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
post_create_gacha_claim: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["GachaClaimRequestDto"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Create new gacha claim */
|
||||
201: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["MessageResponseDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
get_detail_gacha_claim: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description Gacha Claim ID */
|
||||
id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Get Gacha Claim by ID */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ResponseSuccessDto_GachaClaimItemDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
get_gacha_item_list: {
|
||||
parameters: {
|
||||
query?: {
|
||||
/** @description Page number */
|
||||
page?: number;
|
||||
/** @description Items per page */
|
||||
per_page?: number;
|
||||
/** @description Search keyword */
|
||||
search?: string;
|
||||
/** @description Sort by field */
|
||||
sort_by?: string;
|
||||
/** @description Order ASC or DESC */
|
||||
order?: string;
|
||||
/** @description Filter value */
|
||||
filter?: string;
|
||||
/** @description Field to filter by */
|
||||
filter_by?: string;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Get gacha item list */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ResponseListSuccessDto_Vec_GachaItemDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
post_create_gacha_item: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["GachaItemRequestDto"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Create gacha item */
|
||||
201: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["MessageResponseDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
delete_gacha_item: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Delete gacha item */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["MessageResponseDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
get_gacha_item_by_id: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description Gacha Item ID */
|
||||
id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Get gacha item by ID */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ResponseSuccessDto_GachaItemDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
put_update_gacha_item: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["GachaItemRequestDto"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Update gacha item */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["MessageResponseDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
post_create_gacha_roll: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["GachaRollRequestDto"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description Create new gacha roll */
|
||||
201: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["MessageResponseDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
get_detail_gacha_roll: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description Gacha Roll ID */
|
||||
id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Get Gacha Roll by ID */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ResponseSuccessDto_GachaRollItemDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
post_execute_gacha_roll: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Execute and get 1 gacha result */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ResponseSuccessDto_GachaRollItemDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
get_permission_list: {
|
||||
parameters: {
|
||||
query?: {
|
||||
@@ -1035,7 +1507,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ResponseListSuccessDto_Vec_RolesItemDto"];
|
||||
"application/json": components["schemas"]["ResponseListSuccessDto_Vec_RolesListItemDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1104,7 +1576,7 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ResponseSuccessDto_RolesItemDto"];
|
||||
"application/json": components["schemas"]["ResponseSuccessDto_RolesDetailItemDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"paths": {
|
||||
"@components/atoms": ["../../libs/shadcn-ui/src/atoms/index.ts"],
|
||||
"@utils/ui": ["../../libs/utils/src/index.ts"],
|
||||
"@schemas": ["../../libs/service/src/schemas/index.ts"],
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
|
||||
Generated
+36
-2
@@ -9,14 +9,16 @@
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-avatar": "^1.1.9",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@radix-ui/react-label": "^2.1.6",
|
||||
"@radix-ui/react-slot": "^1.2.2",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.503.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tailwindcss": "^4.1.4"
|
||||
"tailwindcss": "^4.1.4",
|
||||
"zod": "^3.24.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tw-animate-css": "^1.2.8"
|
||||
@@ -504,6 +506,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-label": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.6.tgz",
|
||||
"integrity": "sha512-S/hv1mTlgcPX2gCTJrWuTjSXf7ER3Zf7zWGtOprxhIIY93Qin3n5VgNA0Ez9AgrK/lEtlYgzLd4f5x6AVar4Yw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz",
|
||||
@@ -1792,6 +1817,15 @@
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.24.4",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz",
|
||||
"integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,16 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-avatar": "^1.1.9",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@radix-ui/react-label": "^2.1.6",
|
||||
"@radix-ui/react-slot": "^1.2.2",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.503.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tailwindcss": "^4.1.4"
|
||||
"tailwindcss": "^4.1.4",
|
||||
"zod": "^3.24.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tw-animate-css": "^1.2.8"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
||||
import * as React from 'react';
|
||||
import { cn } from './className';
|
||||
import { cn } from './cn';
|
||||
|
||||
const Avatar = React.forwardRef<
|
||||
React.ElementRef<typeof AvatarPrimitive.Root>,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
import * as React from 'react';
|
||||
import { cn } from './className';
|
||||
import { cn } from './cn';
|
||||
|
||||
const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import * as React from 'react';
|
||||
import { cn } from './cn';
|
||||
|
||||
const Card = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'rounded-xl border bg-card text-card-foreground shadow',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Card.displayName = 'Card';
|
||||
|
||||
const CardHeader = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardHeader.displayName = 'CardHeader';
|
||||
|
||||
const CardTitle = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('font-semibold leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardTitle.displayName = 'CardTitle';
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardDescription.displayName = 'CardDescription';
|
||||
|
||||
const CardContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
||||
));
|
||||
CardContent.displayName = 'CardContent';
|
||||
|
||||
const CardFooter = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('flex items-center p-6 pt-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
CardFooter.displayName = 'CardFooter';
|
||||
|
||||
export {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
};
|
||||
@@ -0,0 +1,167 @@
|
||||
'use client';
|
||||
|
||||
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Controller,
|
||||
FormProvider,
|
||||
useFormContext,
|
||||
useFormState,
|
||||
type ControllerProps,
|
||||
type FieldPath,
|
||||
type FieldValues,
|
||||
} from 'react-hook-form';
|
||||
import { cn } from './cn';
|
||||
import { Label } from './label';
|
||||
|
||||
const Form = FormProvider;
|
||||
|
||||
type FormFieldContextValue<
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||
> = {
|
||||
name: TName;
|
||||
};
|
||||
|
||||
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
||||
{} as FormFieldContextValue
|
||||
);
|
||||
|
||||
const FormField = <
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
|
||||
>({
|
||||
...props
|
||||
}: ControllerProps<TFieldValues, TName>) => {
|
||||
return (
|
||||
<FormFieldContext.Provider value={{ name: props.name }}>
|
||||
<Controller {...props} />
|
||||
</FormFieldContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useFormField = () => {
|
||||
const fieldContext = React.useContext(FormFieldContext);
|
||||
const itemContext = React.useContext(FormItemContext);
|
||||
const { getFieldState } = useFormContext();
|
||||
const formState = useFormState({ name: fieldContext.name });
|
||||
const fieldState = getFieldState(fieldContext.name, formState);
|
||||
|
||||
if (!fieldContext) {
|
||||
throw new Error('useFormField should be used within <FormField>');
|
||||
}
|
||||
|
||||
const { id } = itemContext;
|
||||
|
||||
return {
|
||||
id,
|
||||
name: fieldContext.name,
|
||||
formItemId: `${id}-form-item`,
|
||||
formDescriptionId: `${id}-form-item-description`,
|
||||
formMessageId: `${id}-form-item-message`,
|
||||
...fieldState,
|
||||
};
|
||||
};
|
||||
|
||||
type FormItemContextValue = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const FormItemContext = React.createContext<FormItemContextValue>(
|
||||
{} as FormItemContextValue
|
||||
);
|
||||
|
||||
function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
const id = React.useId();
|
||||
|
||||
return (
|
||||
<FormItemContext.Provider value={{ id }}>
|
||||
<div
|
||||
data-slot="form-item"
|
||||
className={cn('grid gap-2', className)}
|
||||
{...props}
|
||||
/>
|
||||
</FormItemContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
function FormLabel({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||
const { error, formItemId } = useFormField();
|
||||
|
||||
return (
|
||||
<Label
|
||||
data-slot="form-label"
|
||||
data-error={!!error}
|
||||
className={cn('data-[error=true]:text-rose-500', className)}
|
||||
htmlFor={formItemId}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
|
||||
const { error, formItemId, formDescriptionId, formMessageId } =
|
||||
useFormField();
|
||||
|
||||
return (
|
||||
<Slot
|
||||
data-slot="form-control"
|
||||
id={formItemId}
|
||||
aria-describedby={
|
||||
!error
|
||||
? `${formDescriptionId}`
|
||||
: `${formDescriptionId} ${formMessageId}`
|
||||
}
|
||||
aria-invalid={!!error}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function FormDescription({ className, ...props }: React.ComponentProps<'p'>) {
|
||||
const { formDescriptionId } = useFormField();
|
||||
|
||||
return (
|
||||
<p
|
||||
data-slot="form-description"
|
||||
id={formDescriptionId}
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {
|
||||
const { error, formMessageId } = useFormField();
|
||||
const body = error ? String(error?.message ?? '') : props.children;
|
||||
|
||||
if (!body) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<p
|
||||
data-slot="form-message"
|
||||
id={formMessageId}
|
||||
className={cn('text-rose-500 text-xs', className)}
|
||||
{...props}
|
||||
>
|
||||
{body}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
useFormField,
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
import {
|
||||
LuArrowDown as ArrowDownIcon,
|
||||
LuBookOpen as BookOpenIcon,
|
||||
LuCode as CodeIcon,
|
||||
LuFacebook as FacebookIcon,
|
||||
LuInstagram as InstagramIcon,
|
||||
LuLaptop as LaptopIcon,
|
||||
LuMenu as MenuIcon,
|
||||
LuMessageCircle as MessageCircleIcon,
|
||||
LuMoon as MoonIcon,
|
||||
LuQuote as QuoteIcon,
|
||||
LuShare2 as Share2Icon,
|
||||
LuSparkles as SparklesIcon,
|
||||
LuSun as SunIcon,
|
||||
LuUsers as UsersIcon,
|
||||
LuVideo as VideoIcon,
|
||||
LuX as XIcon,
|
||||
} from 'react-icons/lu';
|
||||
|
||||
export {
|
||||
ArrowDownIcon,
|
||||
BookOpenIcon,
|
||||
CodeIcon,
|
||||
FacebookIcon,
|
||||
InstagramIcon,
|
||||
LaptopIcon,
|
||||
MenuIcon,
|
||||
MessageCircleIcon,
|
||||
MoonIcon,
|
||||
QuoteIcon,
|
||||
Share2Icon,
|
||||
SparklesIcon,
|
||||
SunIcon,
|
||||
UsersIcon,
|
||||
VideoIcon,
|
||||
XIcon,
|
||||
};
|
||||
@@ -1,3 +1,7 @@
|
||||
export * from './avatar';
|
||||
export * from './button';
|
||||
export * from './icons';
|
||||
export * from './card';
|
||||
export * from './form';
|
||||
export * from './input';
|
||||
export * from './label';
|
||||
export * from './select';
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as React from 'react';
|
||||
import { cn } from './cn';
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
Input.displayName = 'Input';
|
||||
|
||||
export { Input };
|
||||
@@ -0,0 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||
import * as React from 'react';
|
||||
import { cn } from './cn';
|
||||
|
||||
function Label({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
||||
return (
|
||||
<LabelPrimitive.Root
|
||||
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
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Label };
|
||||
@@ -0,0 +1,158 @@
|
||||
'use client';
|
||||
|
||||
import * as SelectPrimitive from '@radix-ui/react-select';
|
||||
import { Check, ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
import { cn } from './cn';
|
||||
|
||||
const Select = SelectPrimitive.Root;
|
||||
|
||||
const SelectGroup = SelectPrimitive.Group;
|
||||
|
||||
const SelectValue = SelectPrimitive.Value;
|
||||
|
||||
const SelectTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
));
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
||||
|
||||
const SelectScrollUpButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default items-center justify-center py-1',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUp className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
));
|
||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
||||
|
||||
const SelectScrollDownButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default items-center justify-center py-1',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
));
|
||||
SelectScrollDownButton.displayName =
|
||||
SelectPrimitive.ScrollDownButton.displayName;
|
||||
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, position = 'popper', ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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 origin-[--radix-select-content-transform-origin]',
|
||||
position === 'popper' &&
|
||||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
className
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
'p-1',
|
||||
position === 'popper' &&
|
||||
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
));
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
||||
|
||||
const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn('px-2 py-1.5 text-sm font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
||||
|
||||
const SelectItem = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<Check className="h-4 w-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
));
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
||||
|
||||
const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectScrollDownButton,
|
||||
SelectScrollUpButton,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
@import 'tailwindcss';
|
||||
@import "tw-animate-css";
|
||||
@import 'tw-animate-css';
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@@ -123,8 +123,8 @@
|
||||
.container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
padding-left: 4rem;
|
||||
padding-right: 4rem;
|
||||
width: 100%;
|
||||
}
|
||||
@media (min-width: 1536px) {
|
||||
|
||||
Generated
+874
@@ -12,11 +12,13 @@
|
||||
"@ant-design/icons": "^6.0.0",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@iconify/react": "^6.0.0",
|
||||
"@marsidev/react-turnstile": "^1.1.0",
|
||||
"@payloadcms/db-postgres": "^3.37.0",
|
||||
"@payloadcms/next": "^3.37.0",
|
||||
"@payloadcms/richtext-lexical": "^3.37.0",
|
||||
"@payloadcms/storage-s3": "^3.37.0",
|
||||
"@radix-ui/react-avatar": "^1.1.9",
|
||||
"@radix-ui/react-select": "^2.2.5",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@smithy/node-http-handler": "^4.0.4",
|
||||
"@tanstack/react-query": "^5.74.4",
|
||||
@@ -6085,6 +6087,16 @@
|
||||
"yjs": ">=13.5.22"
|
||||
}
|
||||
},
|
||||
"node_modules/@marsidev/react-turnstile": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@marsidev/react-turnstile/-/react-turnstile-1.1.0.tgz",
|
||||
"integrity": "sha512-X7bP9ZYutDd+E+klPYF+/BJHqEyyVkN4KKmZcNRr84zs3DcMoftlMAuoKqNSnqg0HE7NQ1844+TLFSJoztCdSA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^17.0.2 || ^18.0.0 || ^19.0",
|
||||
"react-dom": "^17.0.2 || ^18.0.0 || ^19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mdx-js/react": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz",
|
||||
@@ -9957,6 +9969,82 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/number": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
|
||||
"integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
|
||||
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/react-arrow": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
|
||||
"integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-avatar": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.9.tgz",
|
||||
@@ -9984,6 +10072,73 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-collection": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
|
||||
"integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
@@ -10014,6 +10169,326 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-direction": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
||||
"integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dismissable-layer": {
|
||||
"version": "1.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
|
||||
"integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.2",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-escape-keydown": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-focus-guards": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz",
|
||||
"integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-focus-scope": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
||||
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-focus-scope/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-id": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
||||
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popper": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz",
|
||||
"integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.0.0",
|
||||
"@radix-ui/react-arrow": "1.1.7",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1",
|
||||
"@radix-ui/react-use-rect": "1.1.1",
|
||||
"@radix-ui/react-use-size": "1.1.1",
|
||||
"@radix-ui/rect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-portal": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
||||
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz",
|
||||
@@ -10037,6 +10512,90 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-select": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz",
|
||||
"integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/number": "1.1.1",
|
||||
"@radix-ui/primitive": "1.1.2",
|
||||
"@radix-ui/react-collection": "1.1.7",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-direction": "1.1.1",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.10",
|
||||
"@radix-ui/react-focus-guards": "1.1.2",
|
||||
"@radix-ui/react-focus-scope": "1.1.7",
|
||||
"@radix-ui/react-id": "1.1.1",
|
||||
"@radix-ui/react-popper": "1.2.7",
|
||||
"@radix-ui/react-portal": "1.1.9",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-slot": "1.2.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1",
|
||||
"@radix-ui/react-use-previous": "1.1.1",
|
||||
"@radix-ui/react-visually-hidden": "1.2.3",
|
||||
"aria-hidden": "^1.2.4",
|
||||
"react-remove-scroll": "^2.6.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz",
|
||||
@@ -10070,6 +10629,61 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-effect-event": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
|
||||
"integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-escape-keydown": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
|
||||
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-is-hydrated": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz",
|
||||
@@ -10103,6 +10717,127 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-previous": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
|
||||
"integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-rect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
|
||||
"integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/rect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-size": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
|
||||
"integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-visually-hidden": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
|
||||
"integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/rect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
|
||||
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rc-component/util": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.2.1.tgz",
|
||||
@@ -16485,6 +17220,18 @@
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/aria-hidden": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
||||
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/aria-query": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
||||
@@ -20115,6 +20862,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-node-es": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-port": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz",
|
||||
@@ -23495,6 +24248,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-nonce": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
||||
"integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/get-package-type": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
|
||||
@@ -33594,6 +34356,53 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.0.tgz",
|
||||
"integrity": "sha512-sGsQtcjMqdQyijAHytfGEELB8FufGbfXIsvUTe+NLx1GDRJCXtCFLBLUI1eyZCKXXvbEU2C6gai0PZKoIE9Vbg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-remove-scroll-bar": "^2.3.7",
|
||||
"react-style-singleton": "^2.2.3",
|
||||
"tslib": "^2.1.0",
|
||||
"use-callback-ref": "^1.3.3",
|
||||
"use-sidecar": "^1.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-remove-scroll-bar": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
|
||||
"integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-style-singleton": "^2.2.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "7.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.2.tgz",
|
||||
@@ -33663,6 +34472,28 @@
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-style-singleton": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
|
||||
"integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"get-nonce": "^1.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
@@ -38479,6 +39310,27 @@
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-callback-ref": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
|
||||
"integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-context-selector": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/use-context-selector/-/use-context-selector-2.0.0.tgz",
|
||||
@@ -38503,6 +39355,28 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sidecar": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||
"integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-node-es": "^1.1.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
|
||||
|
||||
@@ -30,11 +30,13 @@
|
||||
"@ant-design/icons": "^6.0.0",
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@iconify/react": "^6.0.0",
|
||||
"@marsidev/react-turnstile": "^1.1.0",
|
||||
"@payloadcms/db-postgres": "^3.37.0",
|
||||
"@payloadcms/next": "^3.37.0",
|
||||
"@payloadcms/richtext-lexical": "^3.37.0",
|
||||
"@payloadcms/storage-s3": "^3.37.0",
|
||||
"@radix-ui/react-avatar": "^1.1.9",
|
||||
"@radix-ui/react-select": "^2.2.5",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@smithy/node-http-handler": "^4.0.4",
|
||||
"@tanstack/react-query": "^5.74.4",
|
||||
|
||||
Reference in New Issue
Block a user