Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3bf575402 | ||
|
|
74046efebe | ||
|
|
b7282f9c99 | ||
|
|
2c347d1cc5 | ||
|
|
791f43986d |
@@ -0,0 +1,10 @@
|
||||
import { useSendOTP } from '@imphnen-frontend-service/utils';
|
||||
|
||||
export const useResendOtpHook = () => {
|
||||
|
||||
const { resendOTP, isLoading } = useSendOTP();
|
||||
|
||||
return {
|
||||
resendOTP
|
||||
};
|
||||
};
|
||||
@@ -1,12 +1,38 @@
|
||||
import { FC, ReactElement } from 'react';
|
||||
import { FC, ReactElement, useEffect, useState } from 'react';
|
||||
import { ControlledInputField, RegisterResetBanner } from "@imphnen-frontend-service/ui/organisms";
|
||||
import { useOtpHook } from '../../../_hooks/use-otp';
|
||||
import { Button } from '@imphnen-frontend-service/ui/atoms';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useResendOtpHook } from '../../../_hooks/use-resend-otp';
|
||||
|
||||
export const Components: FC = (): ReactElement => {
|
||||
const { form, onSubmit, isLoading } = useOtpHook()
|
||||
const [ searchParams ] = useSearchParams()
|
||||
const [ disabled, setDisabled] = useState(true);
|
||||
const [ timeLeft, setTimeLeft ] = useState(5 * 60);
|
||||
const { resendOTP } = useResendOtpHook()
|
||||
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
const interval = setInterval(() => {
|
||||
setTimeLeft(prev => {
|
||||
if (prev <= 1) {
|
||||
clearInterval(interval);
|
||||
setDisabled(false);
|
||||
return 0;
|
||||
}
|
||||
return prev - 1;
|
||||
});
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}, [disabled]);
|
||||
|
||||
const formatTime = (seconds: number) => {
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = seconds % 60;
|
||||
return `${m}:${s.toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='flex flex-col justify-center items-center min-h-screen py-[60px] px-[80px]'>
|
||||
@@ -26,6 +52,13 @@ export const Components: FC = (): ReactElement => {
|
||||
maxLength={6}
|
||||
control={form.control}
|
||||
/>
|
||||
<Button type="button" className="mt-2" disabled={disabled} onClick={
|
||||
() => {
|
||||
resendOTP({email: searchParams.get("email") || ""})
|
||||
setDisabled(true)
|
||||
setTimeLeft(5 * 60)
|
||||
}
|
||||
}>Kirim ulang otp {disabled? `(${formatTime(timeLeft)})`: ""}</Button>
|
||||
<Button className="xl:w-full mt-2" disabled={(!form.formState.isValid || isLoading)}>Linked Start !!!</Button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ const mappingPublicRoutes = [
|
||||
'/auth/login',
|
||||
'/auth/forgot',
|
||||
'/auth/forgot/otp',
|
||||
'/auth/register',
|
||||
'/auth/register',
|
||||
'/auth/register/otp',
|
||||
'/auth/register/success',
|
||||
'/auth/new-password',
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import { baiJamjureeFont } from '@/lib/fonts';
|
||||
import { Card } from '@components';
|
||||
import { cn } from '@utils';
|
||||
import { ReactNode } from 'react';
|
||||
import { AnimatedBackground } from './_components/animated-background';
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
baiJamjureeFont.className,
|
||||
'bg-background/20 relative flex min-h-[100dvh] items-center justify-center p-4'
|
||||
)}
|
||||
>
|
||||
<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">
|
||||
|
||||
@@ -4,107 +4,170 @@ import { LogoSimple } from '@/app/_components/logo';
|
||||
import NAVIGATIONS from '@/data/navigations.json';
|
||||
import { Button } from '@components';
|
||||
import { cn } from '@utils';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { LuMenu, LuX } from 'react-icons/lu';
|
||||
|
||||
export function Header() {
|
||||
const router = useRouter();
|
||||
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const pathname = usePathname();
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setIsScrolled(window.scrollY > 10);
|
||||
if (mobileMenuOpen) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = 'auto';
|
||||
}
|
||||
return () => {
|
||||
document.body.style.overflow = 'auto';
|
||||
};
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => window.removeEventListener('scroll', handleScroll);
|
||||
}, []);
|
||||
}, [mobileMenuOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
setMobileMenuOpen(false);
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<header
|
||||
className={cn(
|
||||
'sticky top-0 z-50 w-full transition-all duration-300',
|
||||
isScrolled
|
||||
? 'bg-background/80 backdrop-blur-md border-b shadow-sm'
|
||||
: 'bg-transparent'
|
||||
)}
|
||||
>
|
||||
<div className="container flex h-16 items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative overflow-hidden rounded">
|
||||
<Link href="/">
|
||||
<LogoSimple className="w-[80px]" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<header className="sticky top-0 w-full z-50 bg-background/70">
|
||||
<div className="container flex h-20 items-center justify-between">
|
||||
<Link
|
||||
href="/"
|
||||
className="relative overflow-hidden rounded z-50"
|
||||
aria-label="Home"
|
||||
>
|
||||
<LogoSimple className="w-24 md:w-28" />
|
||||
</Link>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden md:flex items-center gap-8">
|
||||
{NAVIGATIONS.map(({ link, title }) => (
|
||||
<Link
|
||||
key={link}
|
||||
href={link}
|
||||
className="text-sm font-medium relative group"
|
||||
className={cn(
|
||||
'text-sm font-medium relative group px-1 py-2',
|
||||
pathname === link ? 'text-primary' : 'text-foreground/90'
|
||||
)}
|
||||
>
|
||||
<span className="transition-colors hover:text-primary">
|
||||
<span className="transition-colors hover:text-primary block">
|
||||
{title}
|
||||
</span>
|
||||
<span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-primary-500 transition-all duration-300 group-hover:w-full"></span>
|
||||
{pathname === link && (
|
||||
<span className="absolute bottom-0 left-0 w-full h-0.5 bg-primary-500" />
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-x-2">
|
||||
<div className="hidden md:flex items-center gap-x-3">
|
||||
<Button
|
||||
onClick={() => router.push('/signin')}
|
||||
className="hidden md:flex"
|
||||
className="px-5 py-2 text-sm font-medium"
|
||||
>
|
||||
Masuk
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
variant="bordered"
|
||||
onClick={() => router.push('/signup')}
|
||||
className="hidden md:flex"
|
||||
className="px-5 py-2 text-sm font-medium shadow-lg shadow-primary/20 hover:shadow-primary/30"
|
||||
>
|
||||
Daftar
|
||||
</Button>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
className="flex md:hidden"
|
||||
>
|
||||
{mobileMenuOpen ? (
|
||||
<LuX className="size-5" />
|
||||
) : (
|
||||
<LuMenu className="size-5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu */}
|
||||
{mobileMenuOpen && (
|
||||
<div className="md:hidden border-t bg-background/95 backdrop-blur-md">
|
||||
<nav className="container flex flex-col py-4 text-center">
|
||||
{NAVIGATIONS.map(({ link, title }) => (
|
||||
<Link
|
||||
key={link}
|
||||
href={link}
|
||||
className="py-3 text-sm font-medium border-b border-border/50"
|
||||
onClick={() => router.push(link)}
|
||||
<button
|
||||
onClick={() => setMobileMenuOpen((prev) => !prev)}
|
||||
className="flex md:hidden relative z-50 p-2 rounded-lg hover:bg-muted transition-colors"
|
||||
aria-label={mobileMenuOpen ? 'Tutup menu' : 'Buka menu'}
|
||||
>
|
||||
{mobileMenuOpen ? (
|
||||
<LuX className="size-6" />
|
||||
) : (
|
||||
<LuMenu className="size-6" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
<AnimatePresence>
|
||||
{mobileMenuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="fixed inset-0 z-60 bg-background flex flex-col"
|
||||
>
|
||||
<div className="container flex h-20 items-center justify-between">
|
||||
<Link
|
||||
href="/"
|
||||
className="relative overflow-hidden rounded"
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
<LogoSimple className="w-24" />
|
||||
</Link>
|
||||
|
||||
<button
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className="p-2 rounded-lg hover:bg-muted transition-colors"
|
||||
aria-label="Tutup menu"
|
||||
>
|
||||
<LuX className="size-6" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<motion.nav
|
||||
className="flex-1 flex flex-col items-center justify-center gap-6 py-10"
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 0.1 }}
|
||||
>
|
||||
{title}
|
||||
</Link>
|
||||
))}
|
||||
{NAVIGATIONS.map(({ link, title }) => (
|
||||
<Link
|
||||
key={link}
|
||||
href={link}
|
||||
className={cn(
|
||||
'text-2xl font-medium py-2 px-6 rounded-lg transition-colors',
|
||||
pathname === link
|
||||
? 'text-primary bg-primary/10'
|
||||
: 'text-foreground hover:bg-muted'
|
||||
)}
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
{title}
|
||||
</Link>
|
||||
))}
|
||||
</motion.nav>
|
||||
|
||||
<Button onClick={() => router.push('/signin')}>Login</Button>
|
||||
</nav>
|
||||
</div>
|
||||
)}
|
||||
<motion.div
|
||||
className="container space-y-4 pb-10"
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setMobileMenuOpen(false);
|
||||
router.push('/signin');
|
||||
}}
|
||||
className="w-full py-4 text-base"
|
||||
>
|
||||
Masuk
|
||||
</Button>
|
||||
<Button
|
||||
variant="bordered"
|
||||
onClick={() => {
|
||||
setMobileMenuOpen(false);
|
||||
router.push('/signup');
|
||||
}}
|
||||
className="w-full py-4 text-base shadow-lg shadow-primary/20"
|
||||
>
|
||||
Daftar
|
||||
</Button>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { Button, MoonIcon, SunIcon } from '@components';
|
||||
import { Button } from '@components';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { LuMoon, LuSun } from 'react-icons/lu';
|
||||
|
||||
export function ThemeToggle() {
|
||||
const { theme, setTheme } = useTheme();
|
||||
@@ -28,9 +29,9 @@ export function ThemeToggle() {
|
||||
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>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { RequestFeaturePopup } from './request-feature-popup';
|
||||
|
||||
export function FeatureRequestCTA() {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4 }}
|
||||
className="bg-primary-500 rounded-xl px-4 py-8 md:p-8 mb-8 text-center mx-auto max-w-7xl"
|
||||
>
|
||||
<div className="max-w-3xl mx-auto">
|
||||
<motion.h1
|
||||
className="text-2xl md:text-3xl font-bold text-white mb-3"
|
||||
initial={{ scale: 0.95 }}
|
||||
animate={{ scale: 1 }}
|
||||
transition={{ delay: 0.2, type: 'spring' }}
|
||||
>
|
||||
Punya Ide Bagus untuk IMPHNEN?
|
||||
</motion.h1>
|
||||
<motion.p
|
||||
className="text-white/90 mb-6 text-lg text-balance"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
>
|
||||
Bagikan ide fitur yang kamu inginkan dan vote untuk membuatnya nyata!
|
||||
</motion.p>
|
||||
<RequestFeaturePopup />
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
'use client';
|
||||
|
||||
import { Button, Card, CardContent, CardHeader, CardTitle } from '@components';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { useState } from 'react';
|
||||
import { BiUpvote } from 'react-icons/bi';
|
||||
import { FiCheckCircle } from 'react-icons/fi';
|
||||
import { MdOutlineOpenInNew } from 'react-icons/md';
|
||||
|
||||
export default function ProjectsVote() {
|
||||
const [upcomingItems, setUpcomingItems] = useState([
|
||||
{
|
||||
title: 'IMPHNEN Project Showcase',
|
||||
description: 'Showcase projectmu ke member lain dan dapatkan feedback',
|
||||
votes: 42,
|
||||
voted: false,
|
||||
},
|
||||
{
|
||||
title: 'IMPHNEN Meme Generator',
|
||||
description: 'Bikin meme kocak kapanpun dengan mudah',
|
||||
votes: 42,
|
||||
voted: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const inProgressItems = [
|
||||
{
|
||||
title: 'IMPHNEN Twibbon',
|
||||
description: 'Buat Twibbon kece untuk profile media sosialmu',
|
||||
},
|
||||
{
|
||||
title: 'IMPHNEN Certificate',
|
||||
description: 'Cetak sertifikat keren secara instan untuk anggota IMPHNEN',
|
||||
},
|
||||
{
|
||||
title: 'IMPHNEN Roadmap by Vote',
|
||||
description: 'Usulkan ide fitur seru dan ajak anggota lain buat voting',
|
||||
},
|
||||
];
|
||||
|
||||
const completedItems = [
|
||||
{
|
||||
title: 'IMPHNEN Events Page',
|
||||
description:
|
||||
'Koleksi daftar event dan kolaborasi seru yang bisa kamu ikuti',
|
||||
},
|
||||
{
|
||||
title: 'IMPHNEN Testimoni Page',
|
||||
description: 'Berikan testimonial buat komunitas IMPHNEN',
|
||||
},
|
||||
];
|
||||
|
||||
const handleVote = (index: number) => {
|
||||
const newItems = [...upcomingItems];
|
||||
newItems[index] = {
|
||||
...newItems[index],
|
||||
votes: newItems[index].voted
|
||||
? newItems[index].votes - 1
|
||||
: newItems[index].votes + 1,
|
||||
voted: !newItems[index].voted,
|
||||
};
|
||||
setUpcomingItems(newItems);
|
||||
};
|
||||
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.05,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const itemVariants = {
|
||||
hidden: { opacity: 0, y: 10 },
|
||||
visible: { opacity: 1, y: 0, transition: { duration: 0.2 } },
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 max-w-7xl mx-auto">
|
||||
{/* Vote Now Column */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-3 px-2">
|
||||
<div className="flex items-center justify-center w-10 h-10 bg-amber-100 text-amber-600 rounded-lg">
|
||||
🔥
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold text-gray-900">Vote Now</h2>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
className="space-y-5"
|
||||
>
|
||||
{upcomingItems.map((item, index) => (
|
||||
<motion.div key={index} variants={itemVariants} layout>
|
||||
<Card className="bg-white border border-gray-200 hover:border-primary-200 shadow-sm hover:shadow-md transition-all h-full flex flex-col">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg font-semibold text-gray-900 flex items-start">
|
||||
{item.title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-grow">
|
||||
<p className="text-gray-600 text-sm mb-4">
|
||||
{item.description}
|
||||
</p>
|
||||
<div className="flex items-center justify-between border-t border-gray-100 pt-3">
|
||||
<motion.button
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={() => handleVote(index)}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-all ${
|
||||
item.voted
|
||||
? 'bg-primary-500 text-white hover:bg-primary-600'
|
||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
<motion.span
|
||||
animate={{ scale: item.voted ? [1, 1.2, 1] : 1 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<BiUpvote
|
||||
className={`w-4 h-4 ${
|
||||
item.voted ? 'text-white' : 'text-gray-600'
|
||||
}`}
|
||||
/>
|
||||
</motion.span>
|
||||
<span>{item.voted ? 'Voted' : 'Vote'}</span>
|
||||
</motion.button>
|
||||
<div className="flex items-center gap-2 bg-gray-50 px-3 py-1.5 rounded-lg">
|
||||
<BiUpvote className="w-4 h-4 text-gray-500" />
|
||||
<span className="text-sm font-medium text-gray-700">
|
||||
{item.votes}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
{/* In Progress Column */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-3 px-2">
|
||||
<div className="flex items-center justify-center w-10 h-10 bg-primary-100 text-primary-600 rounded-lg">
|
||||
🧑🔧
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold text-gray-900">In Progress</h2>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
className="space-y-5"
|
||||
>
|
||||
{inProgressItems.map((item, index) => (
|
||||
<motion.div key={index} variants={itemVariants} layout>
|
||||
<Card className="bg-white border border-gray-200 hover:border-primary-200 shadow-sm hover:shadow-md transition-all h-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg font-semibold text-gray-900 flex items-start">
|
||||
{item.title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-gray-600 text-sm">{item.description}</p>
|
||||
<div className="mt-4 pt-3 border-t border-gray-100">
|
||||
<div className="w-full bg-gray-200 rounded-full h-1.5">
|
||||
<div
|
||||
className="bg-primary-500 h-1.5 rounded-full"
|
||||
style={{ width: `${(index + 1) * 33}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 mt-2">
|
||||
{index === 0 ? 'Development started' : 'In development'}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
{/* Completed Column */}
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-3 px-2">
|
||||
<div className="flex items-center justify-center w-10 h-10 bg-green-100 text-green-600 rounded-lg">
|
||||
🤓
|
||||
</div>
|
||||
<h2 className="text-xl font-semibold text-gray-900">Completed</h2>
|
||||
</div>
|
||||
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
className="space-y-5"
|
||||
>
|
||||
{completedItems.map((item, index) => (
|
||||
<motion.div key={index} variants={itemVariants} layout>
|
||||
<Card className="bg-white border border-gray-200 hover:border-green-200 shadow-sm hover:shadow-md transition-all h-full">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-lg font-semibold text-gray-900 flex items-start">
|
||||
{item.title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-gray-600 text-sm mb-3">
|
||||
{item.description}
|
||||
</p>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center gap-2 text-green-600">
|
||||
<FiCheckCircle className="w-4 h-4" />
|
||||
<span className="text-sm font-medium">Implemented</span>
|
||||
</div>
|
||||
<Button size="sm" className="flex gap-x-2">
|
||||
Coba sekarang
|
||||
<MdOutlineOpenInNew className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
import { useMediaQuery } from '@/hooks/use-media-query';
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
Input,
|
||||
Label,
|
||||
Textarea,
|
||||
} from '@components';
|
||||
import { cn } from '@utils';
|
||||
|
||||
export function RequestFeaturePopup() {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const isDesktop = useMediaQuery('(min-width: 768px)');
|
||||
|
||||
if (isDesktop) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="secondary">Request Fitur</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[600px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-2xl">Request Fitur</DialogTitle>
|
||||
</DialogHeader>
|
||||
<ProfileForm />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer open={open} onOpenChange={setOpen}>
|
||||
<DrawerTrigger asChild>
|
||||
<Button variant="secondary">Request Fitur</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent>
|
||||
<DrawerHeader className="text-left">
|
||||
<DrawerTitle>Request Fitur</DrawerTitle>
|
||||
</DrawerHeader>
|
||||
<ProfileForm className="px-4" />
|
||||
<DrawerFooter className="pt-2">
|
||||
<DrawerClose asChild>
|
||||
<Button variant="bordered">Cancel</Button>
|
||||
</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
interface ProfileFormProps extends React.ComponentProps<'form'> {
|
||||
showFooterButton?: boolean;
|
||||
}
|
||||
|
||||
function ProfileForm({
|
||||
className,
|
||||
showFooterButton = false,
|
||||
}: ProfileFormProps) {
|
||||
const [description, setDescription] = React.useState('');
|
||||
const maxDescriptionLength = 10000;
|
||||
|
||||
return (
|
||||
<form className={cn('grid items-start gap-8', className)}>
|
||||
<div className="grid gap-4">
|
||||
<Label htmlFor="namaFitur" className="font-medium">
|
||||
Nama Fitur *
|
||||
</Label>
|
||||
<Input
|
||||
id="namaFitur"
|
||||
placeholder="Masukkan nama fitur yang diinginkan"
|
||||
className="h-11"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<Label htmlFor="deskripsiFitur" className="font-medium">
|
||||
Deskripsi Fitur *
|
||||
</Label>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{description.length}/{maxDescriptionLength}
|
||||
</span>
|
||||
</div>
|
||||
<Textarea
|
||||
id="deskripsiFitur"
|
||||
placeholder="Jelaskan fitur yang Anda inginkan secara detail"
|
||||
maxLength={maxDescriptionLength}
|
||||
rows={6}
|
||||
className="min-h-[150px]"
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
/>
|
||||
<p className="text-sm text-muted-foreground -mt-2">
|
||||
Jelaskan manfaat dan cara kerja fitur yang Anda inginkan
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{!showFooterButton && (
|
||||
<Button type="submit" size="lg" className="mt-2">
|
||||
Kirim Permintaan
|
||||
</Button>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { FeatureRequestCTA } from './_components/feature-request-cta';
|
||||
import ProjectsVote from './_components/projects-vote';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="pt-4 md:pt-6 pb-56 bg-gray-50 min-h-screen mx-4 lg:mx-0">
|
||||
<FeatureRequestCTA />
|
||||
<ProjectsVote />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export default function Page() {
|
||||
return <></>;
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
'use client';
|
||||
|
||||
import { useMediaQuery } from '@/hooks/use-media-query';
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
Input,
|
||||
Label,
|
||||
Textarea,
|
||||
} from '@components';
|
||||
import { cn } from '@utils';
|
||||
import * as React from 'react';
|
||||
import { useRef, useState } from 'react';
|
||||
import { LuArrowLeft, LuUpload, LuUser, LuX } from 'react-icons/lu';
|
||||
|
||||
export function TestimonialPopup() {
|
||||
const [open, setOpen] = useState(false);
|
||||
const isDesktop = useMediaQuery('(min-width: 768px)');
|
||||
|
||||
if (isDesktop) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button>Tambah Testimoni</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader className="pb-2">
|
||||
<DialogTitle>Tulis Testimoni</DialogTitle>
|
||||
</DialogHeader>
|
||||
<TestimonialForm setOpen={setOpen} />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer open={open} onOpenChange={setOpen}>
|
||||
<DrawerTrigger asChild>
|
||||
<Button>Tambah Testimoni</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent className="max-h-[85vh]">
|
||||
<DrawerHeader className="pb-0">
|
||||
<DrawerTitle>Tulis Testimoni</DrawerTitle>
|
||||
</DrawerHeader>
|
||||
<div className="overflow-y-auto px-4">
|
||||
<TestimonialForm setOpen={setOpen} className="py-2" />
|
||||
</div>
|
||||
<DrawerFooter className="pt-2 pb-4">
|
||||
<DrawerClose asChild>
|
||||
<Button variant="bordered">Batal</Button>
|
||||
</DrawerClose>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
interface TestimonialFormProps extends React.ComponentProps<'div'> {
|
||||
setOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
function TestimonialForm({ className, setOpen }: TestimonialFormProps) {
|
||||
const [step, setStep] = useState(1);
|
||||
const [testimonialError, setTestimonialError] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [avatarPreview, setAvatarPreview] = useState<string | null>(null);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
role: '',
|
||||
testimonial: '',
|
||||
});
|
||||
|
||||
const handleInputChange = (field: string, value: string) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
if (!file.type.startsWith('image/')) {
|
||||
alert('Hanya file gambar yang diizinkan');
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
if (event.target?.result) {
|
||||
setAvatarPreview(event.target.result as string);
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
const removeAvatar = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
setAvatarPreview(null);
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
const triggerFileInput = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
const validateStep1 = () => {
|
||||
return formData.name.trim() !== '' && formData.email.trim() !== '';
|
||||
};
|
||||
|
||||
const handleNextStep = () => {
|
||||
if (validateStep1()) setStep(2);
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
setTestimonialError(false);
|
||||
|
||||
if (formData.testimonial.trim() === '') {
|
||||
setTestimonialError(true);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Testimonial submitted:', formData);
|
||||
setOpen(false);
|
||||
resetForm();
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setStep(1);
|
||||
setTestimonialError(false);
|
||||
setAvatarPreview(null);
|
||||
setFormData({
|
||||
name: '',
|
||||
email: '',
|
||||
role: '',
|
||||
testimonial: '',
|
||||
});
|
||||
if (fileInputRef.current) fileInputRef.current.value = '';
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('w-full', className)}>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="relative">
|
||||
{step === 1 && (
|
||||
<div className="w-full">
|
||||
{/* Section header with step info aligned using justify-between */}
|
||||
<div className="flex justify-between items-center pb-3 border-b mb-4">
|
||||
<h3 className="text-base font-semibold">Lengkapi Data Diri</h3>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Langkah 1 dari 2
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col items-center gap-3 mb-4">
|
||||
<div className="relative">
|
||||
<Avatar className="w-20 h-20 border-2 border-dashed rounded-full">
|
||||
{avatarPreview ? (
|
||||
<AvatarImage
|
||||
src={avatarPreview}
|
||||
alt="Preview"
|
||||
className="object-cover"
|
||||
/>
|
||||
) : (
|
||||
<AvatarFallback className="bg-secondary">
|
||||
<LuUser className="w-8 h-8 text-muted-foreground" />
|
||||
</AvatarFallback>
|
||||
)}
|
||||
</Avatar>
|
||||
{avatarPreview && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={removeAvatar}
|
||||
className="absolute -top-1 -right-1 bg-background rounded-full p-1 shadow-md border"
|
||||
aria-label="Hapus foto"
|
||||
>
|
||||
<LuX className="w-3 h-3" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<input
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
className="hidden"
|
||||
accept="image/*"
|
||||
onChange={handleFileChange}
|
||||
aria-label="Unggah foto"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="bordered"
|
||||
className="border-dashed h-8 px-3 text-xs"
|
||||
onClick={triggerFileInput}
|
||||
>
|
||||
<LuUpload className="w-3 h-3 mr-1" />
|
||||
{avatarPreview ? 'Ganti Foto' : 'Unggah Foto'}
|
||||
</Button>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Maks. 2MB (JPEG, PNG)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
{[
|
||||
{
|
||||
id: 'name',
|
||||
label: 'Nama',
|
||||
placeholder: 'Nama lengkap',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'email',
|
||||
label: 'Email',
|
||||
placeholder: 'email@contoh.com',
|
||||
type: 'email',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'role',
|
||||
label: 'Role',
|
||||
placeholder: 'Backend Developer',
|
||||
required: true,
|
||||
},
|
||||
].map((field) => (
|
||||
<div key={field.id} className="space-y-1">
|
||||
<Label htmlFor={field.id} className="text-sm">
|
||||
{field.label}
|
||||
</Label>
|
||||
<Input
|
||||
id={field.id}
|
||||
type={field.type || 'text'}
|
||||
placeholder={field.placeholder}
|
||||
value={formData[field.id as keyof typeof formData]}
|
||||
onChange={(e) =>
|
||||
handleInputChange(field.id, e.target.value)
|
||||
}
|
||||
required={field.required}
|
||||
className="py-2"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="pt-2">
|
||||
<Button
|
||||
type="button"
|
||||
size="lg"
|
||||
className="w-full"
|
||||
onClick={handleNextStep}
|
||||
disabled={!validateStep1()}
|
||||
>
|
||||
Lanjut
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{step === 2 && (
|
||||
<div className="w-full">
|
||||
{/* Section header with step info aligned using justify-between */}
|
||||
<div className="flex justify-between items-center pb-3 border-b mb-4">
|
||||
<h3 className="text-base font-semibold">Ulasan</h3>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Langkah 2 dari 2
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<Label htmlFor="testimonial" className="text-sm">
|
||||
Testimoni
|
||||
</Label>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{formData.testimonial.length}/500
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Textarea
|
||||
id="testimonial"
|
||||
placeholder="Bagaimana pengalaman Anda bersama IMPHNEN?"
|
||||
className={cn(
|
||||
'min-h-[140px]',
|
||||
testimonialError && 'border-destructive'
|
||||
)}
|
||||
value={formData.testimonial}
|
||||
onChange={(e) =>
|
||||
handleInputChange('testimonial', e.target.value)
|
||||
}
|
||||
maxLength={500}
|
||||
/>
|
||||
{testimonialError && (
|
||||
<div className="text-destructive text-xs mt-1">
|
||||
Harap isi testimonial
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3 pt-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="bordered"
|
||||
className="flex-1"
|
||||
onClick={() => setStep(1)}
|
||||
>
|
||||
<LuArrowLeft className="w-4 h-4 mr-1" />
|
||||
Kembali
|
||||
</Button>
|
||||
<Button type="submit" size="lg" className="flex-1">
|
||||
Kirim
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
import TESTIMONIALS from '@/data/testimonials.json';
|
||||
import { Button } from '@components';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { BsChatLeftQuote } from 'react-icons/bs';
|
||||
import { FaQuoteLeft } from 'react-icons/fa';
|
||||
import { TestimonialPopup } from './_components/testimonials-popup';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<div className="py-16 px-4 text-center border-b border-border">
|
||||
<div className="py-6 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">
|
||||
<p className="max-w-2xl mx-auto text-lg mb-4 text-balance text-muted-foreground">
|
||||
Menampilkan pengalaman dan cerita para member yang telah join ke dalam
|
||||
komunitas kami
|
||||
</p>
|
||||
<Link href="/testimonials/submit">
|
||||
<Button>Tulis Testimonialmu</Button>
|
||||
</Link>
|
||||
<TestimonialPopup />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-8 md:gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 my-16 container">
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export default function Page() {
|
||||
return <></>;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { poppinsFont } from '@/lib/fonts';
|
||||
import { baiJamjureeFont } from '@/lib/fonts';
|
||||
import '@/styles/globals.css';
|
||||
import { cn } from '@utils';
|
||||
import { type Metadata } from 'next';
|
||||
@@ -17,7 +17,7 @@ export default function RootLayout({
|
||||
}) {
|
||||
return (
|
||||
<html lang="id" suppressHydrationWarning>
|
||||
<body className={cn(poppinsFont.className, 'antialiased')}>
|
||||
<body className={cn(baiJamjureeFont.className, 'antialiased')}>
|
||||
<Providers
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
},
|
||||
{
|
||||
"title": "Roadmap",
|
||||
"link": "/roadmaps"
|
||||
"link": "/roadmap"
|
||||
},
|
||||
{
|
||||
"title": "Artikel",
|
||||
"link": "/articles"
|
||||
"title": "Team",
|
||||
"link": "/teams"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export function useMediaQuery(query: string): boolean {
|
||||
// 1) Always default to `false`—this guarantees SSR and the very first client render match.
|
||||
const [matches, setMatches] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
const mediaQueryList = window.matchMedia(query);
|
||||
|
||||
// 2) On mount, immediately set to the real match value (true or false).
|
||||
setMatches(mediaQueryList.matches);
|
||||
|
||||
// 3) Subscribe to changes.
|
||||
const listener = (event: MediaQueryListEvent) => {
|
||||
setMatches(event.matches);
|
||||
};
|
||||
mediaQueryList.addEventListener('change', listener);
|
||||
|
||||
return () => {
|
||||
mediaQueryList.removeEventListener('change', listener);
|
||||
};
|
||||
}, [query]);
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
export default useMediaQuery;
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
TLoginRequest,
|
||||
TLoginResponse,
|
||||
TRegisterRequest,
|
||||
TSendOTPRequest,
|
||||
TVerifyEmailRequest,
|
||||
} from '../../types/auth';
|
||||
import { TResponseMessage } from '../../types/common';
|
||||
@@ -35,7 +36,18 @@ export const postVerifyEmail = async (
|
||||
const { data } = await api({
|
||||
method: 'POST',
|
||||
url: '/auth/verify-email',
|
||||
data: { otp: parseInt(payload.otp), email: payload.email },
|
||||
data: {otp: parseInt(payload.otp), email: payload.email},
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const postSendOtp = async (
|
||||
payload: TSendOTPRequest
|
||||
): Promise<TResponseMessage> => {
|
||||
const { data } = await api({
|
||||
method: 'POST',
|
||||
url: '/auth/send-otp',
|
||||
data: payload,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useMutation, UseMutationResult } from '@tanstack/react-query';
|
||||
import { postLogin, postRegister, postVerifyEmail } from '../../api/auth';
|
||||
import { postLogin, postRegister, postSendOtp, postVerifyEmail } from '../../api/auth';
|
||||
import {
|
||||
TLoginRequest,
|
||||
TLoginResponse,
|
||||
TRegisterRequest,
|
||||
TSendOTPRequest,
|
||||
TVerifyEmailRequest,
|
||||
} from '../../types/auth';
|
||||
|
||||
@@ -44,3 +45,16 @@ export const usePostVerifyEmail = (): UseMutationResult<
|
||||
mutationFn: async (payload) => await postVerifyEmail(payload),
|
||||
});
|
||||
};
|
||||
|
||||
export const usePostSendOTP = (): UseMutationResult<
|
||||
TResponseMessage,
|
||||
TResponseError,
|
||||
TSendOTPRequest,
|
||||
unknown
|
||||
> => {
|
||||
return useMutation({
|
||||
mutationKey: ['post-send-otp'],
|
||||
mutationFn: async (payload) => await postSendOtp(payload),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -27,3 +27,7 @@ export type TVerifyEmailRequest = {
|
||||
email: string;
|
||||
otp: string;
|
||||
};
|
||||
|
||||
export type TSendOTPRequest = {
|
||||
email: string
|
||||
};
|
||||
Generated
+489
@@ -9,6 +9,7 @@
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
@@ -19,6 +20,7 @@
|
||||
"react-icons": "^5.5.0",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.25.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -462,6 +464,39 @@
|
||||
"react-hook-form": "^7.55.0"
|
||||
}
|
||||
},
|
||||
"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-avatar": {
|
||||
"version": "1.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz",
|
||||
"integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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-is-hydrated": "0.1.0",
|
||||
"@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-compose-refs": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
@@ -477,6 +512,142 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||
"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-dialog": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz",
|
||||
"integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.2",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@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-portal": "1.1.9",
|
||||
"@radix-ui/react-presence": "1.1.4",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-slot": "1.2.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"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-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-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-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-label": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz",
|
||||
@@ -500,6 +671,54 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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-presence": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz",
|
||||
"integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@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-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
@@ -541,6 +760,109 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||
"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-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",
|
||||
"integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "^1.5.0"
|
||||
},
|
||||
"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-layout-effect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||
"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/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz",
|
||||
@@ -1088,6 +1410,18 @@
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"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/class-variance-authority": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||
@@ -1118,6 +1452,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"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/enhanced-resolve": {
|
||||
"version": "5.18.1",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
|
||||
@@ -1202,6 +1542,15 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"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/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
@@ -1570,6 +1919,75 @@
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"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-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/rollup": {
|
||||
"version": "4.40.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz",
|
||||
@@ -1669,6 +2087,12 @@
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tw-animate-css": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.8.tgz",
|
||||
@@ -1679,6 +2103,71 @@
|
||||
"url": "https://github.com/sponsors/Wombosvideo"
|
||||
}
|
||||
},
|
||||
"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-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",
|
||||
"integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vaul": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz",
|
||||
"integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.3.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.3.tgz",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
@@ -21,6 +22,7 @@
|
||||
"react-icons": "^5.5.0",
|
||||
"tailwind-merge": "^3.2.0",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.25.28"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
'use client';
|
||||
|
||||
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '../lib';
|
||||
|
||||
function Avatar({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Root>) {
|
||||
return (
|
||||
<AvatarPrimitive.Root
|
||||
data-slot="avatar"
|
||||
className={cn(
|
||||
'relative flex size-8 shrink-0 overflow-hidden rounded-full',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarImage({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
||||
return (
|
||||
<AvatarPrimitive.Image
|
||||
data-slot="avatar-image"
|
||||
className={cn('aspect-square size-full', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarFallback({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
||||
return (
|
||||
<AvatarPrimitive.Fallback
|
||||
data-slot="avatar-fallback"
|
||||
className={cn(
|
||||
'bg-muted flex size-full items-center justify-center rounded-full',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Avatar, AvatarFallback, AvatarImage };
|
||||
@@ -0,0 +1,142 @@
|
||||
'use client';
|
||||
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import * as React from 'react';
|
||||
import { LuX } from 'react-icons/lu';
|
||||
import { cn } from '../lib';
|
||||
|
||||
function Dialog({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
||||
}
|
||||
|
||||
function DialogTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
||||
}
|
||||
|
||||
function DialogPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
||||
}
|
||||
|
||||
function DialogClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
|
||||
}
|
||||
|
||||
function DialogOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||
return (
|
||||
<DialogPrimitive.Overlay
|
||||
data-slot="dialog-overlay"
|
||||
className={cn(
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogContent({
|
||||
className,
|
||||
children,
|
||||
showCloseButton = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
||||
showCloseButton?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<DialogPortal data-slot="dialog-portal">
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
className={cn(
|
||||
'bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{showCloseButton && (
|
||||
<DialogPrimitive.Close
|
||||
data-slot="dialog-close"
|
||||
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||
>
|
||||
<LuX />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-header"
|
||||
className={cn('flex flex-col gap-2 text-center sm:text-left', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={cn(
|
||||
'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||
return (
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn('text-lg leading-none font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { Drawer as DrawerPrimitive } from 'vaul';
|
||||
import { cn } from '../lib';
|
||||
|
||||
function Drawer({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
|
||||
return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
|
||||
}
|
||||
|
||||
function DrawerTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
|
||||
return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
|
||||
}
|
||||
|
||||
function DrawerPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
|
||||
return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
|
||||
}
|
||||
|
||||
function DrawerClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Close>) {
|
||||
return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
|
||||
}
|
||||
|
||||
function DrawerOverlay({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
|
||||
return (
|
||||
<DrawerPrimitive.Overlay
|
||||
data-slot="drawer-overlay"
|
||||
className={cn(
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DrawerContent({
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Content>) {
|
||||
return (
|
||||
<DrawerPortal data-slot="drawer-portal">
|
||||
<DrawerOverlay />
|
||||
<DrawerPrimitive.Content
|
||||
data-slot="drawer-content"
|
||||
className={cn(
|
||||
'group/drawer-content bg-background fixed z-50 flex h-auto flex-col',
|
||||
'data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b',
|
||||
'data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t',
|
||||
'data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm',
|
||||
'data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
|
||||
{children}
|
||||
</DrawerPrimitive.Content>
|
||||
</DrawerPortal>
|
||||
);
|
||||
}
|
||||
|
||||
function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="drawer-header"
|
||||
className={cn('flex flex-col gap-1.5 p-4', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="drawer-footer"
|
||||
className={cn('mt-auto flex flex-col gap-2 p-4', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DrawerTitle({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Title>) {
|
||||
return (
|
||||
<DrawerPrimitive.Title
|
||||
data-slot="drawer-title"
|
||||
className={cn('text-foreground font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DrawerDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DrawerPrimitive.Description>) {
|
||||
return (
|
||||
<DrawerPrimitive.Description
|
||||
data-slot="drawer-description"
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
DrawerContent,
|
||||
DrawerDescription,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerOverlay,
|
||||
DrawerPortal,
|
||||
DrawerTitle,
|
||||
DrawerTrigger,
|
||||
};
|
||||
@@ -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,6 +1,9 @@
|
||||
export * from './avatar';
|
||||
export * from './button';
|
||||
export * from './card';
|
||||
export * from './dialog';
|
||||
export * from './drawer';
|
||||
export * from './form';
|
||||
export * from './icons';
|
||||
export * from './input';
|
||||
export * from './label';
|
||||
export * from './textarea';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '../lib';
|
||||
|
||||
function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
|
||||
return (
|
||||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
'border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Textarea };
|
||||
@@ -3,4 +3,5 @@ export * from './use-auth-store';
|
||||
export * from './use-modal-login';
|
||||
export * from './use-session';
|
||||
export * from './use-register';
|
||||
export * from './use-otp';
|
||||
export * from './use-otp';
|
||||
export * from './use-send-otp';
|
||||
@@ -0,0 +1,25 @@
|
||||
import { TSendOTPRequest, usePostSendOTP } from '@imphnen-frontend-service/service';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export const useSendOTP = () => {
|
||||
const { mutate, isPending } = usePostSendOTP();
|
||||
|
||||
const resendOTP = (payload: TSendOTPRequest) => {
|
||||
mutate(payload, {
|
||||
onSuccess: (data) => {
|
||||
toast.success('Berhasil Mengirim Ulang OTP');
|
||||
},
|
||||
onError: (err) => {
|
||||
toast.error(
|
||||
err?.response?.data?.message ??
|
||||
'Terjadi Kesalahan yang tidak diketahui'
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
resendOTP,
|
||||
isLoading: isPending,
|
||||
};
|
||||
};
|
||||
Generated
+474
@@ -13,6 +13,7 @@
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@iconify/react": "^6.0.0",
|
||||
"@marsidev/react-turnstile": "^1.1.0",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@redocly/ajv": "^8.11.2",
|
||||
@@ -37,6 +38,7 @@
|
||||
"sharp": "^0.34.1",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.24.2",
|
||||
"zustand": "^5.0.5"
|
||||
},
|
||||
@@ -7591,6 +7593,39 @@
|
||||
"dev": true,
|
||||
"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-avatar": {
|
||||
"version": "1.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz",
|
||||
"integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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-is-hydrated": "0.1.0",
|
||||
"@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-compose-refs": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
@@ -7606,6 +7641,142 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||
"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-dialog": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz",
|
||||
"integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.2",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@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-portal": "1.1.9",
|
||||
"@radix-ui/react-presence": "1.1.4",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-slot": "1.2.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"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-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-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-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-label": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz",
|
||||
@@ -7629,6 +7800,54 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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-presence": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz",
|
||||
"integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@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-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
@@ -7670,6 +7889,109 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||
"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-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",
|
||||
"integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"use-sync-external-store": "^1.5.0"
|
||||
},
|
||||
"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-layout-effect": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||
"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/@rc-component/util": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.2.1.tgz",
|
||||
@@ -13378,6 +13700,18 @@
|
||||
"dev": true,
|
||||
"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",
|
||||
@@ -16863,6 +17197,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",
|
||||
@@ -19589,6 +19929,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",
|
||||
@@ -28359,6 +28708,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",
|
||||
@@ -28407,6 +28803,28 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"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/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@@ -33090,6 +33508,49 @@
|
||||
"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-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",
|
||||
@@ -33203,6 +33664,19 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/vaul": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz",
|
||||
"integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/verdaccio": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/verdaccio/-/verdaccio-6.1.2.tgz",
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"@hookform/resolvers": "^5.0.1",
|
||||
"@iconify/react": "^6.0.0",
|
||||
"@marsidev/react-turnstile": "^1.1.0",
|
||||
"@radix-ui/react-avatar": "^1.1.10",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@redocly/ajv": "^8.11.2",
|
||||
@@ -53,6 +54,7 @@
|
||||
"sharp": "^0.34.1",
|
||||
"sonner": "^2.0.3",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.24.2",
|
||||
"zustand": "^5.0.5"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user