Compare commits

..
Author SHA1 Message Date
arraysid 29937c9296 fix: remove unused icon export from index for cleaner module structure 2025-06-01 11:27:21 +07:00
arraysid dd11bed598 fix: replace XIcon with LuX in DialogClose for consistency 2025-06-01 11:25:12 +07:00
arraysid 09a11b4aec fix: remove unused icon exports from icons 2025-06-01 11:25:06 +07:00
arraysid 16628168ce fix: remove DrawerDescription from RequestFeaturePopup for cleaner UI 2025-06-01 11:20:40 +07:00
arraysid be5e8d4b39 fix: remove DialogDescription from RequestFeaturePopup and update textarea placeholder for clarity 2025-06-01 11:09:52 +07:00
arraysid ca8d2d702f fix: update FeatureRequestCTA text for clarity and adjust page layout margins 2025-06-01 11:09:02 +07:00
arraysid ddca7a5ccf fix: replace motion span with static span for header underline 2025-06-01 08:49:07 +07:00
arraysid e871d453a8 refactor: simplify Header component by removing unused scroll handling and improving mobile menu structure 2025-06-01 08:42:59 +07:00
arraysid 07e5bab31f feat: enhance Header component with mobile menu animations and improved navigation styling 2025-06-01 07:36:40 +07:00
arraysid 98c3c2a326 feat: add Request Feature popup with responsive dialog and drawer components 2025-06-01 07:18:33 +07:00
arraysid 69c934e8c4 fix: correct spelling of 'Certificate' in ProjectsVote component 2025-06-01 05:01:08 +07:00
arraysid 522f283140 feat: add feature request and projects voting components 2025-06-01 04:53:46 +07:00
arraysid c04ca8a29d fix: correct link for Roadmap navigation item 2025-06-01 04:53:35 +07:00
20 changed files with 32 additions and 618 deletions
@@ -1,10 +0,0 @@
import { useSendOTP } from '@imphnen-frontend-service/utils';
export const useResendOtpHook = () => {
const { resendOTP, isLoading } = useSendOTP();
return {
resendOTP
};
};
@@ -1,38 +1,12 @@
import { FC, ReactElement, useEffect, useState } from 'react';
import { FC, ReactElement } 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]'>
@@ -52,13 +26,6 @@ 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>
+1 -1
View File
@@ -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',
+8 -1
View File
@@ -1,10 +1,17 @@
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="bg-background/20 relative flex min-h-[100dvh] items-center justify-center p-4">
<div
className={cn(
baiJamjureeFont.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">
@@ -32,21 +32,21 @@ export default function ProjectsVote() {
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',
title: 'IMPHNEN List Event',
description:
'Koleksi daftar event dan kolaborasi seru yang bisa kamu ikuti',
},
{
title: 'IMPHNEN Testimoni Page',
description: 'Berikan testimonial buat komunitas IMPHNEN',
title: 'IMPHNEN Testimoni',
description: 'Berikan testimonial gokil buat komunitas IMPHNEN',
},
{
title: 'IMPHNEN Roadmap by Vote',
description: 'Usulkan ide fitur seru dan ajak anggota lain buat voting',
},
];
@@ -1,343 +0,0 @@
'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,20 +1,23 @@
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-6 px-4 text-center border-b border-border">
<div className="py-16 px-4 text-center border-b border-border">
<BsChatLeftQuote className="size-14 mx-auto my-4 text-foreground" />
<h1 className="text-4xl font-bold mb-4 text-foreground">Testimonial</h1>
<p className="max-w-2xl mx-auto text-lg mb-4 text-balance text-muted-foreground">
<p className="max-w-2xl mx-auto text-lg mb-8 text-balance text-muted-foreground">
Menampilkan pengalaman dan cerita para member yang telah join ke dalam
komunitas kami
</p>
<TestimonialPopup />
<Link href="/testimonials/submit">
<Button>Tulis Testimonialmu</Button>
</Link>
</div>
<div className="grid gap-8 md:gap-6 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 my-16 container">
@@ -0,0 +1,3 @@
export default function Page() {
return <></>;
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { baiJamjureeFont } from '@/lib/fonts';
import { poppinsFont } 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(baiJamjureeFont.className, 'antialiased')}>
<body className={cn(poppinsFont.className, 'antialiased')}>
<Providers
attribute="class"
defaultTheme="system"
+1 -13
View File
@@ -3,7 +3,6 @@ import {
TLoginRequest,
TLoginResponse,
TRegisterRequest,
TSendOTPRequest,
TVerifyEmailRequest,
} from '../../types/auth';
import { TResponseMessage } from '../../types/common';
@@ -36,18 +35,7 @@ export const postVerifyEmail = async (
const { data } = await api({
method: 'POST',
url: '/auth/verify-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,
data: { otp: parseInt(payload.otp), email: payload.email },
});
return data;
};
+1 -15
View File
@@ -1,10 +1,9 @@
import { useMutation, UseMutationResult } from '@tanstack/react-query';
import { postLogin, postRegister, postSendOtp, postVerifyEmail } from '../../api/auth';
import { postLogin, postRegister, postVerifyEmail } from '../../api/auth';
import {
TLoginRequest,
TLoginResponse,
TRegisterRequest,
TSendOTPRequest,
TVerifyEmailRequest,
} from '../../types/auth';
@@ -45,16 +44,3 @@ 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),
});
};
-4
View File
@@ -27,7 +27,3 @@ export type TVerifyEmailRequest = {
email: string;
otp: string;
};
export type TSendOTPRequest = {
email: string
};
-55
View File
@@ -9,7 +9,6 @@
"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",
@@ -470,33 +469,6 @@
"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",
@@ -830,24 +802,6 @@
}
}
},
"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",
@@ -2146,15 +2100,6 @@
}
}
},
"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",
-1
View File
@@ -11,7 +11,6 @@
},
"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",
-53
View File
@@ -1,53 +0,0 @@
'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 };
-1
View File
@@ -1,4 +1,3 @@
export * from './avatar';
export * from './button';
export * from './card';
export * from './dialog';
+1 -2
View File
@@ -3,5 +3,4 @@ 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-send-otp';
export * from './use-otp';
-25
View File
@@ -1,25 +0,0 @@
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,
};
};
-46
View File
@@ -13,7 +13,6 @@
"@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",
@@ -7599,33 +7598,6 @@
"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",
@@ -7959,24 +7931,6 @@
}
}
},
"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",
-1
View File
@@ -29,7 +29,6 @@
"@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",