diff --git a/app/chat-list/_components/ScreenChatList.tsx b/app/chat-list/_components/ScreenChatList.tsx new file mode 100644 index 0000000..65ca74a --- /dev/null +++ b/app/chat-list/_components/ScreenChatList.tsx @@ -0,0 +1,181 @@ +"use client"; + +import Image from "next/image"; +import Link from "next/link"; +import { BackButton } from "@/components/ui/BackButton"; +import { useChatRooms, type ChatRoom } from "@/hooks/useChatRooms"; +import { useEffect, useMemo } from "react"; + +type ChatListItem = { + id: string; + roomId: string; + name: string; + detail: string; + preview: string; + time: string; + unread?: boolean; + avatar: string; +}; + +const pickedItems: ChatListItem[] = [ + { + id: "picked-1", + roomId: "6969e61b866d67f1c3b68106", + name: "username", + detail: "22세, 정보통신전자공학부", + preview: "아직 연락 안한 분들께 용기내어 연락해봐요!", + time: "방금", + unread: false, + avatar: "/profile/default-profile.svg", + }, +]; + +function ChatListRow({ item }: { item: ChatListItem }) { + return ( + +
+ {item.name} +
+ +
+
+ + {item.name} + + + {item.detail} + +
+ +
+ + {item.preview} + + + + {item.time} + +
+
+ + - 현재 740명 참여중이에요! + 현재{" "} + + {participantsCount?.data?.count ?? 0}명 + {" "} + 참여중이에요! ); diff --git a/app/main/_components/ProfileCard.tsx b/app/main/_components/ProfileCard.tsx index bdc996d..90cfc89 100644 --- a/app/main/_components/ProfileCard.tsx +++ b/app/main/_components/ProfileCard.tsx @@ -6,12 +6,7 @@ import { Send } from "lucide-react"; import React, { useRef } from "react"; import { getContactFrequencyLabel } from "@/lib/utils/profile"; - -/* ── 유틸 함수 ── */ -const getAge = (birthDate?: string) => { - if (!birthDate) return "?? "; - return new Date().getFullYear() - new Date(birthDate).getFullYear() + 1; -}; +import { getAge } from "@/lib/utils/date"; /* ── 태그 컴포넌트 ── */ const Tag = ({ text }: { text: string }) => ( @@ -74,7 +69,7 @@ const ProfileStats = ({ profile }: { profile: ProfileData }) => (
나이 - {getAge(profile.birthDate)} + {profile.age || getAge(profile.birthDate)}
diff --git a/app/main/_components/ProfileSlider.tsx b/app/main/_components/ProfileSlider.tsx index 00b7acc..8a52dd6 100644 --- a/app/main/_components/ProfileSlider.tsx +++ b/app/main/_components/ProfileSlider.tsx @@ -14,11 +14,11 @@ const CARD_GAP = 8; // 카드 사이 간격 (px) const ProfileSlider = ({ profiles }: ProfileSliderProps) => { const [activeIndex, setActiveIndex] = useState(0); - const [expandedCardId, setExpandedCardId] = useState(null); + const [isAllExpanded, setIsAllExpanded] = useState(false); const scrollRef = useRef(null); - const toggleExpanded = useCallback((id: number) => { - setExpandedCardId((prev) => (prev === id ? null : id)); + const toggleExpanded = useCallback(() => { + setIsAllExpanded((prev) => !prev); }, []); useEffect(() => { @@ -60,8 +60,8 @@ const ProfileSlider = ({ profiles }: ProfileSliderProps) => { > toggleExpanded(profile.memberId)} + isExpanded={isAllExpanded} + onToggleExpanded={toggleExpanded} />
))} diff --git a/app/main/_components/ScreenMainPage.tsx b/app/main/_components/ScreenMainPage.tsx index 51816c0..0979334 100644 --- a/app/main/_components/ScreenMainPage.tsx +++ b/app/main/_components/ScreenMainPage.tsx @@ -20,34 +20,48 @@ import { MatchingPartner, } from "@/hooks/useMatchingHistory"; import { useRequestStatus } from "@/hooks/useRequestStatus"; +import { useActiveNotices, Notice } from "@/hooks/useActiveNotices"; const ScreenMainPage = () => { - // 실제 서비스 시에는 서버에서 받아온 데이터(notice)가 있는지 여부에 따라 렌더링을 결정할 수 있습니다. - const noticeData = { - id: "match-notice-001", // 공지사항 고유 ID (데이터를 받아올 때 id가 포함되어 있다고 가정) - title: "매칭 안내문", - detail: - "현재 많은 수요로 인해 일부 유형에 이용자가 몰리는 현상이 일어나고 있습니다. 원하는 유형이 나오지 않을 수도 있으니 이 점 양해 부탁드립니다. 코매칭을 이용해 주셔서 감사합니다.", - }; + const [noticePopup, setNoticePopup] = useState<{ + active: Notice | null; + isVisible: boolean; + }>({ active: null, isVisible: false }); - const [isNoticeVisible, setIsNoticeVisible] = useState(false); + const { data: noticesData } = useActiveNotices(); const { data: historyData, isLoading } = useMatchingHistory(); const { isPurchasePending } = useRequestStatus(); useEffect(() => { - // 로컬스토리지에 해당 공지 ID가 저장되어 있는지 확인 - const confirmed = localStorage.getItem(`notice_confirmed_${noticeData.id}`); - if (!confirmed) { - // 컴포넌트 마운트 직후 상태 변경으로 인한 cascading render 린트 에러 방지를 위해 비동기 처리 - const timeoutId = setTimeout(() => setIsNoticeVisible(true), 0); - return () => clearTimeout(timeoutId); + if (noticesData?.data && noticesData.data.length > 0) { + const firstUnconfirmed = noticesData.data.find( + (notice) => + !localStorage.getItem(`notice_confirmed_${notice.noticeId}`), + ); + + if (firstUnconfirmed) { + // eslint-disable-next-line react-hooks/set-state-in-effect + setNoticePopup({ active: firstUnconfirmed, isVisible: true }); + } else { + setNoticePopup((prev) => + prev.isVisible ? { ...prev, isVisible: false } : prev, + ); + } + } else { + setNoticePopup((prev) => + prev.isVisible ? { ...prev, isVisible: false } : prev, + ); } - }, [noticeData.id]); + }, [noticesData]); const handleNoticeClose = () => { - // 확인 버튼 클릭 시 로컬스토리지에 해당 공지 ID 저장 - localStorage.setItem(`notice_confirmed_${noticeData.id}`, "true"); - setIsNoticeVisible(false); + if (noticePopup.active) { + localStorage.setItem( + `notice_confirmed_${noticePopup.active.noticeId}`, + "true", + ); + setNoticePopup((prev) => ({ ...prev, isVisible: false })); + } }; // 매칭 히스토리 데이터에서 파트너 정보를 추출하여 프로필 목록 생성 @@ -60,6 +74,7 @@ const ScreenMainPage = () => { nickname: partner.nickname, gender: partner.gender, birthDate: partner.birthDate ?? undefined, + age: partner.age ?? undefined, mbti: partner.mbti, intro: partner.intro ?? undefined, profileImageUrl: @@ -82,10 +97,10 @@ const ScreenMainPage = () => { {isPurchasePending && } - {isNoticeVisible && noticeData && ( + {noticePopup.isVisible && noticePopup.active && ( )} diff --git a/app/main/page.tsx b/app/main/page.tsx index 1382257..092f5e2 100644 --- a/app/main/page.tsx +++ b/app/main/page.tsx @@ -10,6 +10,7 @@ import ScreenMainPage from "./_components/ScreenMainPage"; import { ItemsResponse } from "@/hooks/useItems"; import { MatchingHistoryResponse } from "@/hooks/useMatchingHistory"; import { RequestStatusResponse } from "@/hooks/useRequestStatus"; +import { NoticeResponse } from "@/hooks/useActiveNotices"; export default async function MainPage() { const queryClient = new QueryClient(); @@ -47,6 +48,15 @@ export default async function MainPage() { }, initialPageParam: 0, }), + queryClient.prefetchQuery({ + queryKey: ["activeNotices"], + queryFn: async () => { + const res = await serverApi.get({ + path: "/api/v1/notices/active", + }); + return res.data; + }, + }), ]); return ( diff --git a/app/matching-list/_components/MatchingListCard.tsx b/app/matching-list/_components/MatchingListCard.tsx index 6dcdcfd..1621c54 100644 --- a/app/matching-list/_components/MatchingListCard.tsx +++ b/app/matching-list/_components/MatchingListCard.tsx @@ -7,8 +7,12 @@ import { import Image from "next/image"; import { Send, Star, ChevronDown } from "lucide-react"; import React, { useRef, useState } from "react"; +import { useRouter } from "next/navigation"; import { getAge } from "@/lib/utils/date"; -import { getContactFrequencyLabel } from "@/lib/utils/profile"; +import { + getContactFrequencyLabel, + getProfileImageUrl, +} from "@/lib/utils/profile"; /* ── 태그 컴포넌트 ── */ const Tag = ({ text }: { text: string }) => ( @@ -24,17 +28,19 @@ const CardHeader = ({ partner, isFavorite, onFavoriteToggle, + onChatClick, }: { partner: MatchingPartner; isFavorite: boolean; onFavoriteToggle?: () => void; + onChatClick?: () => void; }) => (
{/* 프로필 이미지 (48x48 container, 44x44 image) */}
{`${partner.nickname @@ -74,7 +78,10 @@ const CardHeader = ({ type="button" aria-label="메시지 보내기" className="flex h-4 w-4 items-center justify-center" - onClick={(e) => e.stopPropagation()} + onClick={(e) => { + e.stopPropagation(); + onChatClick?.(); + }} > @@ -98,7 +105,7 @@ const CardStats = ({ partner }: { partner: MatchingPartner }) => (
나이 - {getAge(partner.birthDate)} + {partner.age || getAge(partner.birthDate)}
@@ -213,6 +220,7 @@ const MatchingListCard = ({ }: MatchingListCardProps) => { const [isExpanded, setIsExpanded] = useState(false); const touchStartTime = useRef(0); + const router = useRouter(); const handleCardClick = () => { const touchDuration = Date.now() - touchStartTime.current; @@ -221,6 +229,14 @@ const MatchingListCard = ({ } }; + const handleChatClick = () => { + if (item.chatRoomId) { + router.push(`/chat/${item.chatRoomId}`); + } else { + alert("채팅방 정보를 찾을 수 없습니다."); + } + }; + return (
{/* 카드 본체 */} @@ -244,6 +260,7 @@ const MatchingListCard = ({ partner={item.partner} isFavorite={item.favorite} onFavoriteToggle={() => onFavoriteToggle?.(item.historyId)} + onChatClick={handleChatClick} />
diff --git a/app/matching-list/_components/ScreenMatchingList.tsx b/app/matching-list/_components/ScreenMatchingList.tsx index 4d53433..d22b09d 100644 --- a/app/matching-list/_components/ScreenMatchingList.tsx +++ b/app/matching-list/_components/ScreenMatchingList.tsx @@ -1,20 +1,39 @@ "use client"; import { BackButton } from "@/components/ui/BackButton"; -import React, { useMemo } from "react"; -import { useMatchingHistory } from "@/hooks/useMatchingHistory"; +import React, { useMemo, useEffect } from "react"; +import { + useMatchingHistory, + useUpdateFavorite, +} from "@/hooks/useMatchingHistory"; import NoMatchingList from "./NoMatchingList"; import YesMatchingList from "./YesMatchingList"; const ScreenMatchingList = () => { const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useMatchingHistory(); + const { mutate: toggleFavorite } = useUpdateFavorite(); + + const handleFavoriteToggle = ( + historyId: number, + currentFavorite: boolean, + ) => { + toggleFavorite({ historyId, favorite: !currentFavorite }); + }; // 모든 페이지의 content를 하나의 배열로 평탄화 const allHistory = useMemo(() => { return data?.pages.flatMap((page) => page?.data?.content || []) ?? []; }, [data]); + // DEBUG: 매칭 내역 데이터 로그 출력 + useEffect(() => { + if (data) { + console.log("📂 [Matching History Raw Data]:", data); + console.log("📜 [Matching History List]:", allHistory); + } + }, [data, allHistory]); + return (
@@ -35,6 +54,7 @@ const ScreenMatchingList = () => { fetchNextPage={fetchNextPage} hasNextPage={hasNextPage} isFetchingNextPage={isFetchingNextPage} + onFavoriteToggle={handleFavoriteToggle} />
)} diff --git a/app/matching-list/_components/YesMatchingList.tsx b/app/matching-list/_components/YesMatchingList.tsx index b51c3c6..5547951 100644 --- a/app/matching-list/_components/YesMatchingList.tsx +++ b/app/matching-list/_components/YesMatchingList.tsx @@ -12,7 +12,6 @@ import { Search, ArrowUpNarrowWide, Loader2 } from "lucide-react"; import MatchingListCard from "./MatchingListCard"; import { getAge } from "@/lib/utils/date"; import SortDrawer, { SortOrder } from "./SortDrawer"; -import { CURRENT_YEAR } from "@/lib/constants/date"; const SORT_LABELS: Record = { oldest: "오래된 순", @@ -25,6 +24,7 @@ interface YesMatchingListProps { fetchNextPage: () => void; hasNextPage: boolean; isFetchingNextPage: boolean; + onFavoriteToggle?: (historyId: number, currentFavorite: boolean) => void; } const YesMatchingList = ({ @@ -32,6 +32,7 @@ const YesMatchingList = ({ fetchNextPage, hasNextPage, isFetchingNextPage, + onFavoriteToggle, }: YesMatchingListProps) => { const [searchQuery, setSearchQuery] = useState(""); const [showFavoritesOnly, setShowFavoritesOnly] = useState(false); @@ -49,8 +50,7 @@ const YesMatchingList = ({ p.nickname.toLowerCase().includes(query) || p.mbti.toLowerCase().includes(query) || p.major.toLowerCase().includes(query) || - (p.birthDate && - String(getAge(p.birthDate, CURRENT_YEAR)).includes(query)) + (p.birthDate && String(getAge(p.birthDate)).includes(query)) ); }); } @@ -63,13 +63,11 @@ const YesMatchingList = ({ // 정렬 result.sort((a, b) => { if (sortOrder === "age") { - const ageA = a.partner.birthDate - ? new Date(a.partner.birthDate).getTime() - : 0; - const ageB = b.partner.birthDate - ? new Date(b.partner.birthDate).getTime() - : 0; - // birthDate가 작을수록(오래될수록) 나이가 많음 → 오름차순 + const rawAgeA = a.partner.age || getAge(a.partner.birthDate); + const rawAgeB = b.partner.age || getAge(b.partner.birthDate); + const ageA = typeof rawAgeA === "number" ? rawAgeA : 999; + const ageB = typeof rawAgeB === "number" ? rawAgeB : 999; + // 나이 오름차순 (어린 순), 모르는 나이는 맨 뒤로 return ageA - ageB; } const dateA = new Date(a.matchedAt).getTime(); @@ -108,7 +106,7 @@ const YesMatchingList = ({ return (
{/* 검색 바 */} -
+
{/* 필터 바 */} -
+
{/* 즐겨찾기 필터 */}
diff --git a/app/profile-builder/_components/ScreenProfileBuilder.tsx b/app/profile-builder/_components/ScreenProfileBuilder.tsx index dd3042a..996f346 100644 --- a/app/profile-builder/_components/ScreenProfileBuilder.tsx +++ b/app/profile-builder/_components/ScreenProfileBuilder.tsx @@ -147,6 +147,21 @@ export const ScreenProfileBuilder = () => { }; const handleComplete = () => { + // 나이(출생연도) 검증 (2000년생~2007년생) + if (!selectedBirthYear) { + alert("출생연도를 선택해 주세요."); + return; + } + const birthYearNum = parseInt(selectedBirthYear, 10); + if ( + Number.isNaN(birthYearNum) || + birthYearNum < 2000 || + birthYearNum > 2007 + ) { + alert("2000년생부터 2007년생까지만 가입 가능합니다."); + return; + } + const normalizedMBTI = selectedMBTI.toUpperCase(); const profileData: Partial = { diff --git a/app/profile-builder/_lib/options.ts b/app/profile-builder/_lib/options.ts index 0776dbc..88f372b 100644 --- a/app/profile-builder/_lib/options.ts +++ b/app/profile-builder/_lib/options.ts @@ -12,7 +12,7 @@ type MajorCategory = { }; export const getYearOptions = (): Option[] => - Array.from({ length: 11 }, (_, index) => ({ + Array.from({ length: 8 }, (_, index) => ({ value: String(2000 + index), label: `${2000 + index}년`, })); diff --git a/components/common/ChatSocketInitializer.tsx b/components/common/ChatSocketInitializer.tsx new file mode 100644 index 0000000..a9b7885 --- /dev/null +++ b/components/common/ChatSocketInitializer.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { useEffect } from "react"; +import { useChatSocketStore } from "@/stores/chat-socket-store"; + +export default function ChatSocketInitializer() { + const connect = useChatSocketStore((state) => state.connect); + const disconnect = useChatSocketStore((state) => state.disconnect); + + useEffect(() => { + connect(); + return () => { + // 앱이 종료되거나 새로고침될 때 연결 해제 + // 사실 Root Layout에 두면 탭을 닫기 전까지는 유지됨 + disconnect(); + }; + }, [connect, disconnect]); + + return null; +} diff --git a/components/common/charge-confirm/ConfirmChargeDrawer.tsx b/components/common/charge-confirm/ConfirmChargeDrawer.tsx index 9b32294..902e670 100644 --- a/components/common/charge-confirm/ConfirmChargeDrawer.tsx +++ b/components/common/charge-confirm/ConfirmChargeDrawer.tsx @@ -3,7 +3,7 @@ import React from "react"; import { AxiosError } from "axios"; import { cn } from "@/lib/utils"; -import { X, PencilLine, Check } from "lucide-react"; +import { X, Check } from "lucide-react"; import { Drawer, DrawerClose, @@ -16,6 +16,9 @@ import { import { useQueryClient } from "@tanstack/react-query"; import Button from "@/components/ui/Button"; import { BANK_INFO } from "@/lib/constants/charge"; +import { usePurchaseProduct } from "@/hooks/usePurchaseProduct"; +import { useRealName } from "@/hooks/useRealName"; +import { ChargeDrawerContext } from "@/components/common/ChargeDrawer"; /* ── Props ── */ interface ConfirmChargeDrawerProps { @@ -28,31 +31,29 @@ interface ConfirmChargeDrawerProps { depositorName?: string; } -/* ────────────────────────────────────── */ - -import { usePurchaseProduct } from "@/hooks/usePurchaseProduct"; -import { ChargeDrawerContext } from "@/components/common/ChargeDrawer"; - export default function ConfirmChargeDrawer({ trigger, productId, amount, - depositorName = "천승환", + depositorName, }: ConfirmChargeDrawerProps) { const queryClient = useQueryClient(); const drawerContext = React.useContext(ChargeDrawerContext); const { mutate: purchase, isPending } = usePurchaseProduct(); + const { data: realNameData } = useRealName(); const [open, setOpen] = React.useState(false); const [agreed, setAgreed] = React.useState(false); - const [name, setName] = React.useState(depositorName); - const [isEditingName, setIsEditingName] = React.useState(false); - const inputRef = React.useRef(null); + const [name, setName] = React.useState( + depositorName || realNameData?.data?.realName || "", + ); - /* 입금자명 수정 시 input focus */ + // 실명이 로드되면 이름 업데이트 React.useEffect(() => { - if (isEditingName) inputRef.current?.focus(); - }, [isEditingName]); + if (realNameData?.data?.realName && !depositorName && !name) { + setName(realNameData.data.realName); + } + }, [realNameData, depositorName, name]); /* 계좌번호 복사 */ const handleCopy = async () => { @@ -74,10 +75,7 @@ export default function ConfirmChargeDrawer({ const tossDeepLink = `supertoss://send?accountNo=${accountNo}&bankCode=${bankCode}&amount=${amount}&message=${encodeURIComponent(message)}`; if (/Android|iPhone|iPad/i.test(navigator.userAgent)) { - // 토스 이동 상태 기록 (기존 코드 참고) localStorage.setItem("tossPaymentInProgress", "1"); - - // 딥링크 이동 const a = document.createElement("a"); a.href = tossDeepLink; a.style.display = "none"; @@ -93,8 +91,14 @@ export default function ConfirmChargeDrawer({ const handleConfirm = () => { purchase(productId, { onSuccess: () => { - alert("충전 요청이 완료되었습니다!"); + alert( + "충전 요청이 완료되었습니다! 토스가 설치되어 있다면 토스로 바로 이동합니다.", + ); setOpen(false); + queryClient.invalidateQueries({ queryKey: ["myProfile"] }); + + // 토스 송금 앱으로 즉시 자동 연동 + handleTossTransfer(); }, onError: (error: AxiosError<{ code?: string; message?: string }>) => { const errorData = error.response?.data; @@ -104,7 +108,6 @@ export default function ConfirmChargeDrawer({ } else if (errorData?.code === "PAY-004") { alert("먼저 입금자명을 설정해 주세요."); setOpen(false); - // 실명 설정 탭으로 이동 (TABS[2]가 입금자명 설정) drawerContext?.setActiveTab(2); } else { alert( @@ -129,10 +132,10 @@ export default function ConfirmChargeDrawer({ className="rounded-t-[24px] bg-white outline-none" showHandle={false} > -
-
+
+
{/* ── Header ── */} - +
계좌이체 @@ -161,16 +164,13 @@ export default function ConfirmChargeDrawer({
{/* 계좌 정보 박스 */} -
+
{BANK_INFO.bank} - + {BANK_INFO.account} - - 예금주 : {BANK_INFO.holder} -
@@ -179,33 +179,11 @@ export default function ConfirmChargeDrawer({ 입금자명 - {isEditingName ? ( -
- setName(e.target.value)} - onBlur={() => setIsEditingName(false)} - onKeyDown={(e) => { - if (e.key === "Enter") setIsEditingName(false); - }} - className="typo-16-700 text-color-gray-900 h-[24px] w-[90px] bg-transparent text-center outline-none" - maxLength={6} - /> -
- ) : ( - - )} +
+ + {name || "미지정"} + +
{/* 입금액 */} @@ -251,7 +229,6 @@ export default function ConfirmChargeDrawer({ {isPending ? "요청 중..." : "충전 요청 보내기"} - {/* Toss 링크 */} + + )} +
+ ); +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +}; diff --git a/hooks/useActiveNotices.ts b/hooks/useActiveNotices.ts new file mode 100644 index 0000000..7551dc4 --- /dev/null +++ b/hooks/useActiveNotices.ts @@ -0,0 +1,30 @@ +import { api } from "@/lib/axios"; +import { useQuery } from "@tanstack/react-query"; + +export interface Notice { + noticeId: number; + title: string; + content: string; +} + +export interface NoticeResponse { + code: string; + status: number; + message: string; + data: Notice[]; +} + +/** 활성 공지사항 조회 API */ +export const fetchActiveNotices = async (): Promise => { + const { data } = await api.get("/api/v1/notices/active"); + return data; +}; + +/** 활성 공지사항 조회 훅 */ +export const useActiveNotices = () => { + return useQuery({ + queryKey: ["activeNotices"], + queryFn: fetchActiveNotices, + staleTime: 1000 * 60 * 5, // 5분 + }); +}; diff --git a/hooks/useChatMemberProfile.ts b/hooks/useChatMemberProfile.ts new file mode 100644 index 0000000..b77ffd8 --- /dev/null +++ b/hooks/useChatMemberProfile.ts @@ -0,0 +1,37 @@ +import { api } from "@/lib/axios"; +import { useQuery } from "@tanstack/react-query"; + +export interface ChatMemberProfile { + memberId: number; + nickname: string; + profileImageUrl: string; + major: string; + historyId: number | null; + favorite: boolean; +} + +export interface ChatMemberProfileResponse { + code: string; + status: number; + message: string; + data: ChatMemberProfile; +} + +export const fetchChatMemberProfile = async ( + memberId: number, +): Promise => { + const { data } = await api.get( + `/api/chat/members/${memberId}/profile`, + ); + return data; +}; + +export const useChatMemberProfile = (memberId?: number) => { + return useQuery({ + queryKey: ["chatMemberProfile", memberId], + queryFn: () => fetchChatMemberProfile(memberId!), + enabled: !!memberId, + staleTime: 1000 * 60 * 60, // 📡 상대방 프로필 정보는 1시간 동안 완전 신선(Fresh)하다고 판단하여 API 재호출 완벽 차단 + gcTime: 1000 * 60 * 90, // 🧠 1시간 30분 동안 메모리에 든든하게 캐시 보관 + }); +}; diff --git a/hooks/useChatMessages.ts b/hooks/useChatMessages.ts new file mode 100644 index 0000000..d71a5dd --- /dev/null +++ b/hooks/useChatMessages.ts @@ -0,0 +1,37 @@ +import { api } from "@/lib/axios"; +import { useQuery } from "@tanstack/react-query"; + +export type ChatMessagePayload = { + id: string; + roomId: string; + senderId: number; + content: string; + type: "TALK" | "ENTER" | "LEAVE" | "READ"; + createdAt: string; + readCount: number; +}; + +export type ChatMessagesResponse = { + code: string; + status: number; + message: string; + data: ChatMessagePayload[]; +}; + +export const fetchChatMessages = async ( + roomId: string, +): Promise => { + const { data } = await api.get( + `/api/chat/rooms/${roomId}/messages`, + ); + return data; +}; + +export const useChatMessages = (roomId: string) => { + return useQuery({ + queryKey: ["chatMessages", roomId], + queryFn: () => fetchChatMessages(roomId), + enabled: !!roomId, + staleTime: 1000 * 60, // 1분간 캐시 유지 + }); +}; diff --git a/hooks/useChatRoomSocket.ts b/hooks/useChatRoomSocket.ts new file mode 100644 index 0000000..d4e6c99 --- /dev/null +++ b/hooks/useChatRoomSocket.ts @@ -0,0 +1,96 @@ +"use client"; + +import { useEffect, useState, useCallback } from "react"; +import { useChatSocketStore } from "@/stores/chat-socket-store"; +import { IMessage } from "@stomp/stompjs"; + +export type ChatMessagePayload = { + id: string; + roomId: string; + senderId: number; + content: string; + type: "TALK" | "ENTER" | "LEAVE" | "READ"; + createdAt: string; + readCount: number; +}; + +export function useChatRoomSocket(roomId: string) { + const { client, status } = useChatSocketStore(); + const [messages, setMessages] = useState([]); + + const handleMessage = useCallback( + (message: IMessage) => { + try { + const payload: ChatMessagePayload = JSON.parse(message.body); + console.log(`[ChatRoom:${roomId}] New event received:`, payload); + + // TALK일 경우에만 메시지 리스트에 추가 (READ는 다른 로직 처리 가능) + if (payload.type === "TALK") { + setMessages((prev) => [...prev, payload]); + } + } catch (error) { + console.error("[ChatRoom] Failed to parse message:", error); + } + }, + [roomId], + ); + + const sendReadReceipt = useCallback(() => { + if (!client || !client.connected) return; + + client.publish({ + destination: "/app/chat/message", + body: JSON.stringify({ + roomId, + content: "", + type: "READ", + }), + }); + console.log(`[ChatRoom:${roomId}] Read receipt sent`); + }, [client, roomId]); + + useEffect(() => { + if (!client || status !== "connected" || !roomId) return; + + const destination = `/topic/chat.room.${roomId}`; + console.log(`[ChatRoom:${roomId}] Subscribing to ${destination}...`); + const subscription = client.subscribe(destination, handleMessage); + + // 구독 후 바로 읽음 처리 신호 전송 + sendReadReceipt(); + + return () => { + console.log(`[ChatRoom:${roomId}] Unsubscribing...`); + subscription.unsubscribe(); + }; + }, [client, status, roomId, handleMessage, sendReadReceipt]); + + const sendMessage = useCallback( + (content: string) => { + if (!client || !client.connected) { + console.error("[ChatRoom] Client not connected"); + return; + } + + const payload = { + roomId, + content, + type: "TALK", + }; + + client.publish({ + destination: "/app/chat/message", + body: JSON.stringify(payload), + }); + }, + [client, roomId], + ); + + return { + messages, + setMessages, + sendMessage, + sendReadReceipt, + isConnected: status === "connected", + }; +} diff --git a/hooks/useChatRooms.ts b/hooks/useChatRooms.ts new file mode 100644 index 0000000..7dc76f4 --- /dev/null +++ b/hooks/useChatRooms.ts @@ -0,0 +1,56 @@ +import { api } from "@/lib/axios"; +import { useQuery } from "@tanstack/react-query"; +import { MatchingPartner } from "./useMatchingHistory"; + +export type ChatRoom = { + id: string; // 채팅방 ID + matchingId: number; // 매칭 ID + initiatorUserId: number; + targetUserId: number; + otherUser: { + memberId: number; + nickname: string; + profileImageUrl: string; + university: string; + major: string; + age: number; + gender?: import("@/lib/types/profile").Gender; + }; + lastMessage: string | null; + lastMessageTime: string | null; + unreadCount: number; +}; + +export type ChatRoomsResponse = { + code: string; + status: number; + message: string; + data: ChatRoom[]; +}; + +export const fetchChatRooms = async (): Promise => { + console.log("📡 [fetchChatRooms] API 호출 시작..."); + try { + const { data } = await api.get( + "/api/chat/rooms", + ); + console.log("📡 [fetchChatRooms] API 호출 성공:", data); + + // 백엔드 응답이 { data: [...] } 형태인지 아니면 배열 그 자체인지 확인하여 처리 + if (Array.isArray(data)) return data; + return data.data || []; + } catch (error) { + console.error("❌ [fetchChatRooms] API 호출 실패:", error); + throw error; + } +}; + +export const useChatRooms = () => { + return useQuery({ + queryKey: ["chatRooms"], + queryFn: fetchChatRooms, + staleTime: 0, // 항상 최신 데이터를 유지하도록 설정 + gcTime: 0, // 메모리 캐싱을 아예 하지 않고 컴포넌트가 꺼지면 즉시 캐시 소멸 + // refetchInterval: 1000 * 5, // 5초마다 주기적으로 서버에서 데이터를 갱신하여 상대방 메시지 실시간 반영 + }); +}; diff --git a/hooks/useMatching.ts b/hooks/useMatching.ts index dc4f35b..09e8ee8 100644 --- a/hooks/useMatching.ts +++ b/hooks/useMatching.ts @@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useMatchingStore } from "@/stores/matching-store"; import { useRouter } from "next/navigation"; import { postMatchingAction } from "@/lib/actions/matchingAction"; +import { MatchingRequest } from "@/lib/types/matching"; /** * 매칭 실행 Mutation 훅 @@ -13,7 +14,10 @@ export const useMatching = () => { const { setResult, setLastPayload, setIsMatching } = useMatchingStore(); return useMutation({ - mutationFn: postMatchingAction, + mutationFn: (payload: MatchingRequest) => { + console.log("🚀 [Matching Request] Sending payload:", payload); + return postMatchingAction(payload); + }, onMutate: () => { setIsMatching(true); }, diff --git a/hooks/useMatchingHistory.ts b/hooks/useMatchingHistory.ts index c52022a..119a91c 100644 --- a/hooks/useMatchingHistory.ts +++ b/hooks/useMatchingHistory.ts @@ -3,7 +3,6 @@ import { Gender, MBTI, SocialType, - Hobby, ContactFrequency, } from "@/lib/types/profile"; import { @@ -19,6 +18,7 @@ export interface MatchingPartner { nickname: string; gender: Gender; birthDate: string | null; + age: number | null; mbti: MBTI; intro: string | null; profileImageUrl: string | null; @@ -36,6 +36,7 @@ export interface MatchingPartner { export interface MatchingHistoryItem { historyId: number; + chatRoomId?: string; partner: MatchingPartner; favorite: boolean; matchedAt: string; @@ -63,18 +64,29 @@ export const fetchMatchingHistoryPage = async ( page: number = 0, size: number = 30, ): Promise => { - const { data } = await api.get( - "/api/matching/history", - { - params: { - page, - size, - sort: "matchedAt,desc", - }, - }, + console.log( + `📡 [fetchMatchingHistoryPage] 매칭 내역 API 호출 시작... (pageParam: ${page}, size: ${size})`, ); - console.log("Matching History Data:", data); - return data; + try { + const { data } = await api.get( + "/api/matching/history", + { + params: { + page, + size, + sort: "matchedAt,desc", + }, + }, + ); + console.log("✅ [fetchMatchingHistoryPage] 매칭 내역 API 호출 성공:", data); + return data; + } catch (error) { + console.error( + "❌ [fetchMatchingHistoryPage] 매칭 내역 API 호출 실패:", + error, + ); + throw error; + } }; /** React Query useInfiniteQuery 훅 */ @@ -88,8 +100,8 @@ export const useMatchingHistory = () => { ? lastPage.data.currentPage + 1 : undefined; }, - staleTime: Infinity, - gcTime: 1000 * 60 * 60, + staleTime: 0, + gcTime: 0, }); }; @@ -154,6 +166,7 @@ export const useUpdateFavorite = () => { onSettled: () => { // 최종적으로 무효화 queryClient.invalidateQueries({ queryKey: ["matchingHistory"] }); + queryClient.invalidateQueries({ queryKey: ["chatMemberProfile"] }); }, }); }; diff --git a/hooks/useRequestStatus.ts b/hooks/useRequestStatus.ts index c1b703d..d0a8128 100644 --- a/hooks/useRequestStatus.ts +++ b/hooks/useRequestStatus.ts @@ -1,6 +1,6 @@ import { api } from "@/lib/axios"; import { useQuery, useQueryClient } from "@tanstack/react-query"; -import React, { useEffect, useRef } from "react"; +import { useEffect, useRef } from "react"; import { AxiosError } from "axios"; export interface RequestStatusResponse { diff --git a/lib/constants/matching.ts b/lib/constants/matching.ts new file mode 100644 index 0000000..ba06f06 --- /dev/null +++ b/lib/constants/matching.ts @@ -0,0 +1,5 @@ +/** 매칭 나이 설정 관련 상수 */ +export const MATCHING_AGE_LIMITS = { + MIN: 20, + MAX: 27, +} as const; diff --git a/lib/types/profile.ts b/lib/types/profile.ts index 30ada38..a97bc92 100644 --- a/lib/types/profile.ts +++ b/lib/types/profile.ts @@ -32,6 +32,7 @@ export interface ProfileData { nickname?: string; gender?: Gender; birthDate?: string; // YYYY-MM-DD 형식 + age?: number; mbti?: MBTI; intro?: string; profileImageUrl?: string; diff --git a/lib/utils/profile.ts b/lib/utils/profile.ts index 6056cc6..bc11b93 100644 --- a/lib/utils/profile.ts +++ b/lib/utils/profile.ts @@ -1,4 +1,5 @@ -import { ContactFrequency } from "../types/profile"; +import { DEFAULT_PROFILE_ASSETS } from "../constants/defaultProfiles"; +import { ContactFrequency, Gender } from "../types/profile"; export const getContactFrequencyLabel = (freq?: ContactFrequency | string) => { switch (freq) { @@ -12,3 +13,48 @@ export const getContactFrequencyLabel = (freq?: ContactFrequency | string) => { return freq || ""; } }; + +/** + * 서버에서 내려온 프로필 이미지 URL/키를 실제 이미지 경로로 변환 + */ +export const getProfileImageUrl = ( + url: string | null | undefined, + gender?: Gender | string, +) => { + if (!url || url === "default") return "/default-profile.png"; + + // 파일명 부분 추출 (URL인 경우 마지막 세그먼트) + const filename = url.split("/").pop() || ""; + + // animal_ 또는 default_ 접두사가 붙은 경우 로컬 에셋에서 찾음 + if ( + url.startsWith("animal_") || + url.startsWith("default_") || + filename.startsWith("animal_") || + filename.startsWith("default_") + ) { + const animalId = filename + .replace("animal_", "") + .replace("default_", "") + .replace(/\d+.*$/, "") // animal_dinosaur1.png -> dinosaur + .replace(/\..*$/, ""); // 확장자 제거 + + const asset = DEFAULT_PROFILE_ASSETS.find( + (p) => p.id.toLowerCase() === animalId.toLowerCase(), + ); + + if (asset) { + if (gender === "MALE" && asset.maleImage) return asset.maleImage; + if (gender === "FEMALE" && asset.femaleImage) return asset.femaleImage; + return ( + asset.fallbackImage || + asset.maleImage || + asset.femaleImage || + "/default-profile.png" + ); + } + } + + // 그 외에는 서버 URL 그대로 사용 + return url; +}; diff --git a/next.config.ts b/next.config.ts index c55c39e..ae1e456 100644 --- a/next.config.ts +++ b/next.config.ts @@ -32,6 +32,10 @@ const nextConfig: NextConfig = { protocol: "https", hostname: "comatching.site", }, + { + protocol: "https", + hostname: "comatching5.s3.ap-northeast-2.amazonaws.com", + }, ], }, }; diff --git a/package.json b/package.json index b51bbea..5563cb2 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "lucide-react": "^0.562.0", "motion": "^12.38.0", "next": "16.1.3", + "radix-ui": "^1.4.3", "react": "19.2.3", "react-dom": "19.2.3", "sockjs-client": "^1.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fe8038..c1a578a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: next: specifier: 16.1.3 version: 16.1.3(@babel/core@7.28.6)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + radix-ui: + specifier: ^1.4.3 + version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: specifier: 19.2.3 version: 19.2.3 @@ -61,7 +64,7 @@ importers: version: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) zustand: specifier: ^5.0.12 - version: 5.0.12(@types/react@19.2.10)(react@19.2.3) + version: 5.0.12(@types/react@19.2.10)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) devDependencies: '@tailwindcss/postcss': specifier: ^4.1.18 @@ -439,6 +442,21 @@ packages: '@firebase/webchannel-wrapper@1.0.5': resolution: {integrity: sha512-+uGNN7rkfn41HLO0vekTFhTxk61eKa8mTpRGLO0QSqlQdKvIoGAvLp3ppdVIWbTGYJWM6Kp0iN+PjMIOcnVqTw==} + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + '@grpc/grpc-js@1.9.15': resolution: {integrity: sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==} engines: {node: ^8.13.0 || >=10.10.0} @@ -720,9 +738,129 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + '@radix-ui/number@1.1.1': + resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + '@radix-ui/react-accessible-icon@1.1.7': + resolution: {integrity: sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==} + 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 + + '@radix-ui/react-accordion@1.2.12': + resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + 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 + + '@radix-ui/react-alert-dialog@1.1.15': + resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} + 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 + + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + 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 + + '@radix-ui/react-aspect-ratio@1.1.7': + resolution: {integrity: sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==} + 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 + + '@radix-ui/react-avatar@1.1.10': + resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} + 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 + + '@radix-ui/react-checkbox@1.3.3': + resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} + 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 + + '@radix-ui/react-collapsible@1.1.12': + resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + 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 + + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + 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 + '@radix-ui/react-compose-refs@1.1.2': resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: @@ -732,6 +870,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-context-menu@2.2.16': + resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==} + 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 + '@radix-ui/react-context@1.1.2': resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: @@ -754,6 +905,15 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dismissable-layer@1.1.11': resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} peerDependencies: @@ -767,6 +927,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dropdown-menu@2.1.16': + resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} + 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 + '@radix-ui/react-focus-guards@1.1.3': resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} peerDependencies: @@ -789,6 +962,32 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-form@0.1.8': + resolution: {integrity: sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==} + 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 + + '@radix-ui/react-hover-card@1.1.15': + resolution: {integrity: sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==} + 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 + '@radix-ui/react-id@1.1.1': resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: @@ -798,8 +997,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-portal@1.1.9': - resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + '@radix-ui/react-label@2.1.7': + resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -811,8 +1010,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + '@radix-ui/react-menu@2.1.16': + resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -824,8 +1023,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + '@radix-ui/react-menubar@1.1.16': + resolution: {integrity: sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -837,76 +1036,414 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + '@radix-ui/react-navigation-menu@1.2.14': + resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==} 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 - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + '@radix-ui/react-one-time-password-field@0.1.8': + resolution: {integrity: sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg==} 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 - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + '@radix-ui/react-password-toggle-field@0.1.3': + resolution: {integrity: sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw==} 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 - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} 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 - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} 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 - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} 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 - '@rtsao/scc@1.1.0': - resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + 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 - '@stomp/stompjs@7.3.0': - resolution: {integrity: sha512-nKMLoFfJhrQAqkvvKd1vLq/cVBGCMwPRCD0LqW7UT1fecRx9C3GoKEIR2CYwVuErGeZu8w0kFkl2rlhPlqHVgQ==} + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + 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 - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@radix-ui/react-progress@1.1.7': + resolution: {integrity: sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==} + 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 - '@tailwindcss/node@4.1.18': - resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} + '@radix-ui/react-radio-group@1.3.8': + resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} + 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 - '@tailwindcss/oxide-android-arm64@4.1.18': - resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} - engines: {node: '>= 10'} - cpu: [arm64] + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + 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 + + '@radix-ui/react-scroll-area@1.2.10': + resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} + 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 + + '@radix-ui/react-select@2.2.6': + resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} + 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 + + '@radix-ui/react-separator@1.1.7': + resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} + 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 + + '@radix-ui/react-slider@1.3.6': + resolution: {integrity: sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==} + 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 + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.2.6': + resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} + 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 + + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + 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 + + '@radix-ui/react-toast@1.2.15': + resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==} + 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 + + '@radix-ui/react-toggle-group@1.1.11': + resolution: {integrity: sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==} + 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 + + '@radix-ui/react-toggle@1.1.10': + resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==} + 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 + + '@radix-ui/react-toolbar@1.1.11': + resolution: {integrity: sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==} + 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 + + '@radix-ui/react-tooltip@1.2.8': + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} + 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 + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.0': + resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + 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 + + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@stomp/stompjs@7.3.0': + resolution: {integrity: sha512-nKMLoFfJhrQAqkvvKd1vLq/cVBGCMwPRCD0LqW7UT1fecRx9C3GoKEIR2CYwVuErGeZu8w0kFkl2rlhPlqHVgQ==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.1.18': + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} + + '@tailwindcss/oxide-android-arm64@4.1.18': + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} + engines: {node: '>= 10'} + cpu: [arm64] os: [android] '@tailwindcss/oxide-darwin-arm64@4.1.18': @@ -2417,6 +2954,19 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + radix-ui@1.4.3: + resolution: {integrity: sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA==} + 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 + react-dom@19.2.3: resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: @@ -2777,6 +3327,11 @@ packages: '@types/react': optional: true + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + vaul@1.1.2: resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} peerDependencies: @@ -3362,6 +3917,23 @@ snapshots: '@firebase/webchannel-wrapper@1.0.5': {} + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/react-dom@2.1.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@floating-ui/dom': 1.7.6 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + + '@floating-ui/utils@0.2.11': {} + '@grpc/grpc-js@1.9.15': dependencies: '@grpc/proto-loader': 0.7.15 @@ -3558,51 +4130,543 @@ snapshots: '@protobufjs/codegen@2.0.4': {} - '@protobufjs/eventemitter@1.1.0': {} + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@radix-ui/number@1.1.1': {} + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.10)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.10 + + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-context@1.1.2(@types/react@19.2.10)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.10 + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + aria-hidden: 1.2.6 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.10)(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-direction@1.1.1(@types/react@19.2.10)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.10 + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.10)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.10 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-id@1.1.1(@types/react@19.2.10)(react@19.2.3)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.10 + + '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + aria-hidden: 1.2.6 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.10)(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@protobufjs/fetch@1.1.0': + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/inquire': 1.1.0 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + aria-hidden: 1.2.6 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-remove-scroll: 2.7.2(@types/react@19.2.10)(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@protobufjs/float@1.0.2': {} + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/rect': 1.1.1 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@protobufjs/inquire@1.1.0': {} + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@protobufjs/path@1.1.2': {} + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@protobufjs/pool@1.1.0': {} + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@protobufjs/utf8@1.1.0': {} + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/primitive@1.1.3': {} + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.10)(react@19.2.3)': + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-context@1.1.2(@types/react@19.2.10)(react@19.2.3)': + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.10)(react@19.2.3) '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-slot': 1.2.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) aria-hidden: 1.2.6 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) @@ -3611,78 +4675,152 @@ snapshots: '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.10)(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.10)(react@19.2.3)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.10)(react@19.2.3)': dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) react: 19.2.3 optionalDependencies: '@types/react': 19.2.10 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/primitive': 1.1.3 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.10)(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-id@1.1.1(@types/react@19.2.10)(react@19.2.3)': + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-slot@1.2.3(@types/react@19.2.10)(react@19.2.3)': + '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: + '@radix-ui/primitive': 1.1.3 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.10)(react@19.2.3)': dependencies: @@ -3712,12 +4850,50 @@ snapshots: optionalDependencies: '@types/react': 19.2.10 + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.10)(react@19.2.3)': + dependencies: + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.10)(react@19.2.3)': dependencies: react: 19.2.3 optionalDependencies: '@types/react': 19.2.10 + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.10)(react@19.2.3)': + dependencies: + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.10 + + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.10)(react@19.2.3)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.10 + + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.10)(react@19.2.3)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + react: 19.2.3 + optionalDependencies: + '@types/react': 19.2.10 + + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + + '@radix-ui/rect@1.1.1': {} + '@rtsao/scc@1.1.0': {} '@stomp/stompjs@7.3.0': {} @@ -5320,6 +6496,69 @@ snapshots: queue-microtask@1.2.3: {} + radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.10)(react@19.2.3) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + react-dom@19.2.3(react@19.2.3): dependencies: react: 19.2.3 @@ -5787,6 +7026,10 @@ snapshots: optionalDependencies: '@types/react': 19.2.10 + use-sync-external-store@1.6.0(react@19.2.3): + dependencies: + react: 19.2.3 + vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -5891,7 +7134,8 @@ snapshots: zod@4.3.6: {} - zustand@5.0.12(@types/react@19.2.10)(react@19.2.3): + zustand@5.0.12(@types/react@19.2.10)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)): optionalDependencies: '@types/react': 19.2.10 react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) diff --git a/public/chat/heart.png b/public/chat/heart.png new file mode 100644 index 0000000..4453c5e Binary files /dev/null and b/public/chat/heart.png differ diff --git a/stores/chat-socket-store.ts b/stores/chat-socket-store.ts new file mode 100644 index 0000000..201eef4 --- /dev/null +++ b/stores/chat-socket-store.ts @@ -0,0 +1,70 @@ +"use client"; + +import { create } from "zustand"; +import { Client } from "@stomp/stompjs"; +import SockJS from "sockjs-client"; + +type ConnectionStatus = "connected" | "disconnected" | "reconnecting"; + +interface ChatSocketStore { + client: Client | null; + status: ConnectionStatus; + connect: () => void; + disconnect: () => void; +} + +export const useChatSocketStore = create((set, get) => ({ + client: null, + status: "disconnected", + + connect: () => { + // 이미 연결 중이거나 연결된 경우 중복 실행 방지 + if (get().client?.active) return; + + const API_URL = process.env.NEXT_PUBLIC_API_URL; + if (!API_URL) { + console.error("[ChatSTOMP] NEXT_PUBLIC_API_URL is not defined"); + return; + } + + const wsUrl = `${API_URL}/ws/chat`; + + const client = new Client({ + webSocketFactory: () => new SockJS(wsUrl) as unknown as WebSocket, + reconnectDelay: 5000, + heartbeatIncoming: 10000, + heartbeatOutgoing: 10000, + + onConnect: () => { + console.log("✅ [ChatSTOMP] Connected (Zustand)"); + set({ status: "connected" }); + }, + + onDisconnect: () => { + console.log("🔌 [ChatSTOMP] Disconnected (Zustand)"); + set({ status: "disconnected" }); + }, + + onStompError: (frame) => { + console.error("❌ [ChatSTOMP] Error:", frame.headers?.message || frame); + set({ status: "disconnected" }); + }, + + onWebSocketClose: () => { + console.log("🔄 [ChatSTOMP] WebSocket closed"); + set({ status: "reconnecting" }); + }, + }); + + client.activate(); + set({ client }); + }, + + disconnect: () => { + const { client } = get(); + if (client) { + client.deactivate(); + set({ client: null, status: "disconnected" }); + } + }, +}));