- {['학습일', '시작 시간', '종료 시간', '출석 여부'].map((data: string) => (
+ {ATTENDANCE_TABLE_HEADER.map((data: string) => (
{data}
diff --git a/apps/client/src/pages/Profile/ui/UserInfo.tsx b/apps/client/src/pages/Profile/ui/UserInfo.tsx
index 437d980..910c7e1 100644
--- a/apps/client/src/pages/Profile/ui/UserInfo.tsx
+++ b/apps/client/src/pages/Profile/ui/UserInfo.tsx
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { ErrorCharacter, LoadingCharacter, BlogIcon, EditIcon, GithubIcon, LinkedInIcon, MailIcon } from '@/shared/ui';
import { Avatar, AvatarFallback, AvatarImage } from '@/shared/ui/shadcn/avatar';
-import { UserData } from '../model';
+import { UserData } from '@/entities/user';
type UserInfoProps = Readonly<{
userData: UserData | undefined;
diff --git a/apps/client/src/pages/Record/RecordPage.tsx b/apps/client/src/pages/Record/RecordPage.tsx
index add84e2..5d2aa58 100644
--- a/apps/client/src/pages/Record/RecordPage.tsx
+++ b/apps/client/src/pages/Record/RecordPage.tsx
@@ -1,12 +1,6 @@
import { useState } from 'react';
import { RecordInfo, RecordList, RecordPlayer } from './ui';
-
-export type RecordData = {
- recordId: number;
- title: string;
- video: string;
- date: string;
-};
+import { RecordData } from '@/entities/record';
export function RecordPage() {
const [nowPlaying, setNowPlaying] = useState
({ recordId: 0, title: '', video: '', date: '' });
diff --git a/apps/client/src/pages/Record/ui/RecordList.tsx b/apps/client/src/pages/Record/ui/RecordList.tsx
index 9590d40..79dac64 100644
--- a/apps/client/src/pages/Record/ui/RecordList.tsx
+++ b/apps/client/src/pages/Record/ui/RecordList.tsx
@@ -1,28 +1,34 @@
-import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
-import { PlayIcon, ErrorCharacter } from '@/shared/ui';
-import { RecordData } from '../RecordPage';
-import { axiosInstance } from '@/shared/api';
+import { PlayIcon, ErrorCharacter, LoadingCharacter } from '@/shared/ui';
+import { RecordData, useRecordList } from '@/entities/record';
type RecordListProps = Readonly<{
onClickList: (data: RecordData) => void;
}>;
export function RecordList({ onClickList }: RecordListProps) {
- const [recordList, setRecordList] = useState([]);
const { attendanceId } = useParams<{ attendanceId: string }>();
- const [error, setError] = useState('');
+ const { data: recordList, isLoading, isError } = useRecordList(attendanceId);
- useEffect(() => {
- axiosInstance.get(`/v1/records/${attendanceId}`).then(response => {
- if (response.data.success) setRecordList(response.data.data.records);
- else setError(response.data.message);
- });
- }, [attendanceId]);
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ if (isError) {
+ return (
+
+
+
+ );
+ }
return (
- {error ? (
+ {isError ? (
diff --git a/apps/client/src/shared/api/axios.ts b/apps/client/src/shared/api/axios.ts
index ed2abf6..85adcfa 100644
--- a/apps/client/src/shared/api/axios.ts
+++ b/apps/client/src/shared/api/axios.ts
@@ -19,3 +19,18 @@ axiosInstance.interceptors.request.use(
},
error => Promise.reject(error instanceof Error ? error : new Error(error)),
);
+
+axiosInstance.interceptors.response.use(
+ response => {
+ if (!response.data.success) {
+ throw new Error(response.data.message);
+ }
+ return response;
+ },
+ error => {
+ if (axios.isAxiosError(error) && error.response?.data) {
+ throw new Error(error.response.data.message);
+ }
+ throw new Error('서버와 통신 중 오류가 발생했습니다.');
+ },
+);
diff --git a/apps/client/src/shared/api/index.ts b/apps/client/src/shared/api/index.ts
index a2123fd..c3b123d 100644
--- a/apps/client/src/shared/api/index.ts
+++ b/apps/client/src/shared/api/index.ts
@@ -1,2 +1 @@
export { axiosInstance } from './axios';
-export { useAPI } from './useAPI';
diff --git a/apps/client/src/shared/api/useAPI.ts b/apps/client/src/shared/api/useAPI.ts
deleted file mode 100644
index 32fe424..0000000
--- a/apps/client/src/shared/api/useAPI.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { useCallback, useEffect, useState } from 'react';
-import { axiosInstance } from '@/shared/api';
-
-type APIOptions = {
- method?: 'GET' | 'POST' | 'DELETE' | 'PUT' | 'PATCH';
- params?: Record
;
- data?: unknown;
-};
-
-type APIQueryState = {
- data: T | null;
- fetchData: () => Promise;
- isLoading: boolean;
- error: Error | null;
-};
-
-export const useAPI = (endpoint: string, options: APIOptions = {}): APIQueryState => {
- const [isLoading, setIsLoading] = useState(true);
- const [error, setError] = useState(null);
- const [data, setData] = useState(null);
-
- const fetchData = useCallback(async () => {
- setIsLoading(true);
- try {
- const result = await axiosInstance.request({
- url: endpoint,
- method: options.method ?? 'GET',
- params: options.params,
- data: options.data,
- });
- setData(result.data.data);
- setError(null);
- } catch (err) {
- setError(err instanceof Error ? err : new Error('Failed to fetch data.'));
- } finally {
- setIsLoading(false);
- }
- }, [endpoint, options.method, options.params, options.data]);
-
- useEffect(() => {
- fetchData();
- }, [fetchData]);
-
- return { data, fetchData, isLoading, error };
-};
diff --git a/apps/client/src/shared/lib/index.ts b/apps/client/src/shared/lib/index.ts
index 1f4c153..6a71a2e 100644
--- a/apps/client/src/shared/lib/index.ts
+++ b/apps/client/src/shared/lib/index.ts
@@ -3,3 +3,4 @@ export { useTheme } from './useTheme';
export { useToast } from './useToast';
export { getRtpCapabilities, createDevice, connectTransport } from './mediasoupHelpers';
export { cn, checkDependencies } from './utils';
+export { useIntersect } from './useIntersect';
diff --git a/apps/client/src/pages/Home/model/useIntersect.ts b/apps/client/src/shared/lib/useIntersect.ts
similarity index 74%
rename from apps/client/src/pages/Home/model/useIntersect.ts
rename to apps/client/src/shared/lib/useIntersect.ts
index 361be51..dcd6f57 100644
--- a/apps/client/src/pages/Home/model/useIntersect.ts
+++ b/apps/client/src/shared/lib/useIntersect.ts
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useRef } from 'react';
+import { useCallback, useEffect, useRef, useState } from 'react';
type IntersectHandler = (entry: IntersectionObserverEntry, observer: IntersectionObserver) => void;
@@ -8,11 +8,15 @@ type UseIntersectProps = {
};
export const useIntersect = ({ onIntersect, options }: UseIntersectProps) => {
+ const [inView, setInView] = useState(false);
const ref = useRef(null);
const callback = useCallback(
(entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
entries.forEach(entry => {
- if (entry.isIntersecting) onIntersect(entry, observer);
+ setInView(entry.isIntersecting);
+ if (entry.isIntersecting) {
+ onIntersect(entry, observer);
+ }
});
},
[onIntersect],
@@ -27,5 +31,5 @@ export const useIntersect = ({ onIntersect, options }: UseIntersectProps) => {
};
}, [ref, options, callback]);
- return ref;
+ return { ref, inView };
};
diff --git a/apps/client/src/shared/types/index.ts b/apps/client/src/shared/types/index.ts
index e69de29..cea55c0 100644
--- a/apps/client/src/shared/types/index.ts
+++ b/apps/client/src/shared/types/index.ts
@@ -0,0 +1,2 @@
+export type { TransportInfo, ConnectTransportResponse } from './mediasoupTypes';
+export type { Field } from './sharedTypes';
diff --git a/apps/client/src/shared/ui/IconButton.tsx b/apps/client/src/shared/ui/IconButton.tsx
index 62fe9d4..9562b1c 100644
--- a/apps/client/src/shared/ui/IconButton.tsx
+++ b/apps/client/src/shared/ui/IconButton.tsx
@@ -1,3 +1,5 @@
+import { ButtonHTMLAttributes } from 'react';
+
type IconButtonProps = Readonly<{
children: React.ReactNode;
title?: string;
@@ -5,9 +7,10 @@ type IconButtonProps = Readonly<{
onClick?: () => void;
disabled?: boolean;
className?: string;
-}>;
+}> &
+ ButtonHTMLAttributes;
-export function IconButton({ children, title, ariaLabel, onClick, disabled, className }: IconButtonProps) {
+export function IconButton({ children, title, ariaLabel, onClick, disabled, className, ...props }: IconButtonProps) {
return (
diff --git a/apps/client/src/widgets/Banner/Banner.tsx b/apps/client/src/widgets/Banner/Banner.tsx
index 34b5330..17f1866 100644
--- a/apps/client/src/widgets/Banner/Banner.tsx
+++ b/apps/client/src/widgets/Banner/Banner.tsx
@@ -1,5 +1,5 @@
import { MoveCharacter } from '@/shared/ui';
-import { Bookmark } from './Bookmark';
+import { Bookmark } from '@/features/bookmark';
export function Banner() {
return (
diff --git a/apps/client/src/widgets/Header/Header.tsx b/apps/client/src/widgets/Header/Header.tsx
index 3e5ea73..b4241e7 100644
--- a/apps/client/src/widgets/Header/Header.tsx
+++ b/apps/client/src/widgets/Header/Header.tsx
@@ -1,20 +1,20 @@
-import { useContext, useEffect, useRef, useState } from 'react';
+import { useContext, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Avatar, AvatarFallback, AvatarImage } from '@/shared/ui/shadcn/avatar';
-import { cn } from '@/shared/lib';
-import { AuthContext, useAuth } from '@/features/auth';
-import { axiosInstance } from '@/shared/api';
import { Button } from '@/shared/ui/shadcn/button';
+import { cn } from '@/shared/lib';
+import { useProfileImage } from '@/entities/user';
+import { AuthContext, useAuth } from '@/features';
import { LogoButton } from './LogoButton';
import { LogInButton } from './LogInButton';
export function Header() {
const [isCheckedIn, setIsCheckedIn] = useState(false);
- const [profileImgUrl, setProfileImgUrl] = useState('');
const broadcastRef = useRef(null);
const { isLoggedIn } = useContext(AuthContext);
const { logout } = useAuth();
const navigate = useNavigate();
+ const { profileImgUrl } = useProfileImage(isLoggedIn);
const handleCheckInClick = () => {
if (broadcastRef.current && !broadcastRef.current.closed) {
@@ -54,14 +54,6 @@ export function Header() {
navigate('/');
};
- useEffect(() => {
- if (!isLoggedIn) return;
- axiosInstance.get('/v1/members/profile-image').then(response => {
- if (!response.data.success) return;
- setProfileImgUrl(response.data.data.profileImage);
- });
- }, [isLoggedIn]);
-
return (
diff --git a/apps/client/src/widgets/LiveList/LiveList.tsx b/apps/client/src/widgets/LiveList/LiveList.tsx
deleted file mode 100644
index e3cd189..0000000
--- a/apps/client/src/widgets/LiveList/LiveList.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { useCallback, useEffect, useState } from 'react';
-import { axiosInstance } from '@/shared/api';
-import { FieldFilter } from './FieldFilter';
-import { LiveCard } from './LiveCard';
-import { LivePreviewInfo } from '@/pages/Home/model/homeTypes';
-import { Search } from './Search';
-import { Field } from '@/shared/types/sharedTypes';
-import { useIntersect } from '@/pages/Home/model';
-
-const LIMIT = 12;
-
-export function LiveList() {
- const [liveList, setLiveList] = useState([]);
- const [hasNext, setHasNext] = useState(true);
- const [cursor, setCursor] = useState(null);
- const [field, setField] = useState('');
-
- const getLiveList = useCallback(() => {
- axiosInstance.get('/v1/broadcasts', { params: { field, cursor, limit: LIMIT } }).then(response => {
- if (response.data.success) {
- const { broadcasts, nextCursor } = response.data.data;
- setLiveList(prev => [...prev, ...broadcasts]);
- setCursor(nextCursor);
- if (!nextCursor) setHasNext(false);
- }
- });
- }, [field, cursor]);
-
- const ref = useIntersect({
- onIntersect: (entry, observer) => {
- observer.unobserve(entry.target);
- if (hasNext && cursor) getLiveList();
- },
- options: { threshold: 0.3 },
- });
-
- const hanldeFilterField = (selectedField: Field) => {
- axiosInstance
- .get('/v1/broadcasts', { params: { field: selectedField, cursor: null, limit: LIMIT } })
- .then(response => {
- if (response.data.success) {
- const { broadcasts, nextCursor } = response.data.data;
- setLiveList(broadcasts);
- setCursor(nextCursor);
- setHasNext(!!nextCursor);
- }
- });
- };
-
- const handleSearch = (keyword: string) => {
- setField('');
- setCursor(null);
- axiosInstance.get('/v1/broadcasts/search', { params: { keyword: keyword.trim() } }).then(response => {
- if (response.data.success) {
- setLiveList(response.data.data);
- }
- });
- };
-
- useEffect(() => {
- getLiveList();
- }, [getLiveList]);
-
- return (
-
-
-
-
-
-
-
- {liveList ? (
- liveList.map(data => {
- const { broadcastId, broadcastTitle, camperId, profileImage, thumbnail } = data;
- return (
-
-
-
- );
- })
- ) : (
-
방송 정보가 없습니다.
- )}
-
-
-
-
- );
-}
diff --git a/apps/client/src/widgets/LiveList/index.ts b/apps/client/src/widgets/LiveList/index.ts
deleted file mode 100644
index e761f10..0000000
--- a/apps/client/src/widgets/LiveList/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { LiveList } from './LiveList';
diff --git a/apps/client/src/widgets/LivePreviewList/LivePreviewList.tsx b/apps/client/src/widgets/LivePreviewList/LivePreviewList.tsx
new file mode 100644
index 0000000..2d70d95
--- /dev/null
+++ b/apps/client/src/widgets/LivePreviewList/LivePreviewList.tsx
@@ -0,0 +1,87 @@
+import { useEffect, useState } from 'react';
+import { FieldFilter, LivePreviewCard, Search, LivePreviewInfo } from '@/features/livePreview';
+import { Field } from '@/shared/types';
+import { useIntersect } from '@/shared/lib';
+import { useLivePreviewList, useSearchLivePreviewList } from '@/features/livePreview/model/queries';
+
+export function LivePreviewList() {
+ const [field, setField] = useState('');
+ const [searchKeyword, setSearchKeyword] = useState('');
+ const [isSearching, setIsSearching] = useState(false);
+ const [liveList, setLiveList] = useState([]);
+
+ const { data: infiniteData, fetchNextPage, hasNextPage, isFetching } = useLivePreviewList(field);
+
+ const { data: searchData } = useSearchLivePreviewList(searchKeyword);
+
+ const { ref } = useIntersect({
+ onIntersect: (entry, observer) => {
+ observer.unobserve(entry.target);
+ if (hasNextPage && !isFetching && !isSearching) {
+ fetchNextPage();
+ }
+ },
+ options: { threshold: 0.3 },
+ });
+
+ useEffect(() => {
+ if (!isSearching && infiniteData) {
+ const newList = infiniteData.pages.flatMap(page => page.broadcasts);
+ setLiveList(newList);
+ }
+ }, [infiniteData, isSearching]);
+
+ useEffect(() => {
+ if (isSearching && searchData) {
+ setLiveList(searchData);
+ }
+ }, [searchData, isSearching]);
+
+ const handleFilterField = (selectedField: Field) => {
+ setField(selectedField);
+ setIsSearching(false);
+ setSearchKeyword('');
+ };
+
+ const handleSearch = (keyword: string) => {
+ if (keyword.trim() === '') {
+ setIsSearching(false);
+ setSearchKeyword('');
+ }
+ setSearchKeyword(keyword);
+ setField('');
+ setIsSearching(true);
+ };
+
+ return (
+
+
+
+
+
+
+
+ {liveList ? (
+ liveList.map(data => {
+ const { broadcastId, broadcastTitle, camperId, profileImage, thumbnail } = data;
+ return (
+
+
+
+ );
+ })
+ ) : (
+
방송 정보가 없습니다.
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/client/src/widgets/LivePreviewList/index.ts b/apps/client/src/widgets/LivePreviewList/index.ts
new file mode 100644
index 0000000..06d02ea
--- /dev/null
+++ b/apps/client/src/widgets/LivePreviewList/index.ts
@@ -0,0 +1 @@
+export { LivePreviewList } from './LivePreviewList';
diff --git a/apps/client/src/widgets/index.ts b/apps/client/src/widgets/index.ts
index 635bafa..92b6db0 100644
--- a/apps/client/src/widgets/index.ts
+++ b/apps/client/src/widgets/index.ts
@@ -1,3 +1,3 @@
export { Banner } from './Banner';
-export { LiveList } from './LiveList';
+export { LivePreviewList } from './LivePreviewList';
export { Header } from './Header';
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3ad0663..1b66d4a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -299,6 +299,12 @@ importers:
'@radix-ui/react-toast':
specifier: ^1.2.2
version: 1.2.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@tanstack/react-query':
+ specifier: ^5.66.0
+ version: 5.66.0(react@18.3.1)
+ '@tanstack/react-query-devtools':
+ specifier: ^5.66.0
+ version: 5.66.0(@tanstack/react-query@5.66.0(react@18.3.1))(react@18.3.1)
axios:
specifier: ^1.7.7
version: 1.7.7
@@ -339,6 +345,9 @@ importers:
specifier: ^1.0.7
version: 1.0.7(tailwindcss@3.4.14(ts-node@10.9.2(@swc/core@1.8.0)(@types/node@20.17.6)(typescript@5.6.3)))
devDependencies:
+ '@tanstack/eslint-plugin-query':
+ specifier: ^5.66.1
+ version: 5.66.1(eslint@8.57.1)(typescript@5.6.3)
'@types/node':
specifier: ^20.3.1
version: 20.17.6
@@ -2684,6 +2693,28 @@ packages:
'@swc/types@0.1.14':
resolution: {integrity: sha512-PbSmTiYCN+GMrvfjrMo9bdY+f2COnwbdnoMw7rqU/PI5jXpKjxOGZ0qqZCImxnT81NkNsKnmEpvu+hRXLBeCJg==}
+ '@tanstack/eslint-plugin-query@5.66.1':
+ resolution: {integrity: sha512-pYMVTGgJ7yPk9Rm6UWEmbY6TX0EmMmxJqYkthgeDCwEznToy2m+W928nUODFirtZBZlhBsqHy33LO0kyTlgf0w==}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+
+ '@tanstack/query-core@5.66.0':
+ resolution: {integrity: sha512-J+JeBtthiKxrpzUu7rfIPDzhscXF2p5zE/hVdrqkACBP8Yu0M96mwJ5m/8cPPYQE9aRNvXztXHlNwIh4FEeMZw==}
+
+ '@tanstack/query-devtools@5.65.0':
+ resolution: {integrity: sha512-g5y7zc07U9D3esMdqUfTEVu9kMHoIaVBsD0+M3LPdAdD710RpTcLiNvJY1JkYXqkq9+NV+CQoemVNpQPBXVsJg==}
+
+ '@tanstack/react-query-devtools@5.66.0':
+ resolution: {integrity: sha512-uB57wA2YZaQ2fPcFW0E9O1zAGDGSbRKRx84uMk/86VyU9jWVxvJ3Uzp+zNm+nZJYsuekCIo2opTdgNuvM3cKgA==}
+ peerDependencies:
+ '@tanstack/react-query': ^5.66.0
+ react: ^18 || ^19
+
+ '@tanstack/react-query@5.66.0':
+ resolution: {integrity: sha512-z3sYixFQJe8hndFnXgWu7C79ctL+pI0KAelYyW+khaNJ1m22lWrhJU2QrsTcRKMuVPtoZvfBYrTStIdKo+x0Xw==}
+ peerDependencies:
+ react: ^18 || ^19
+
'@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
@@ -2926,6 +2957,10 @@ packages:
resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==}
engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/scope-manager@8.24.0':
+ resolution: {integrity: sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@typescript-eslint/type-utils@6.21.0':
resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -2954,6 +2989,10 @@ packages:
resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==}
engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/types@8.24.0':
+ resolution: {integrity: sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@typescript-eslint/typescript-estree@6.21.0':
resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -2972,6 +3011,12 @@ packages:
typescript:
optional: true
+ '@typescript-eslint/typescript-estree@8.24.0':
+ resolution: {integrity: sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <5.8.0'
+
'@typescript-eslint/utils@6.21.0':
resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -2984,6 +3029,13 @@ packages:
peerDependencies:
eslint: ^8.56.0
+ '@typescript-eslint/utils@8.24.0':
+ resolution: {integrity: sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <5.8.0'
+
'@typescript-eslint/visitor-keys@6.21.0':
resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -2992,6 +3044,10 @@ packages:
resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==}
engines: {node: ^18.18.0 || >=20.0.0}
+ '@typescript-eslint/visitor-keys@8.24.0':
+ resolution: {integrity: sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
'@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
@@ -4096,6 +4152,10 @@ packages:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ eslint-visitor-keys@4.2.0:
+ resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
eslint@8.57.1:
resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -6428,6 +6488,12 @@ packages:
peerDependencies:
typescript: '>=4.2.0'
+ ts-api-utils@2.0.1:
+ resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
@@ -8341,7 +8407,7 @@ snapshots:
'@eslint/eslintrc@2.1.4':
dependencies:
ajv: 6.12.6
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.3.7(supports-color@9.4.0)
espree: 9.6.1
globals: 13.24.0
ignore: 5.3.2
@@ -8409,7 +8475,7 @@ snapshots:
'@humanwhocodes/config-array@0.13.0':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.3.7(supports-color@9.4.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -9720,6 +9786,29 @@ snapshots:
dependencies:
'@swc/counter': 0.1.3
+ '@tanstack/eslint-plugin-query@5.66.1(eslint@8.57.1)(typescript@5.6.3)':
+ dependencies:
+ '@typescript-eslint/utils': 8.24.0(eslint@8.57.1)(typescript@5.6.3)
+ eslint: 8.57.1
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@tanstack/query-core@5.66.0': {}
+
+ '@tanstack/query-devtools@5.65.0': {}
+
+ '@tanstack/react-query-devtools@5.66.0(@tanstack/react-query@5.66.0(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@tanstack/query-devtools': 5.65.0
+ '@tanstack/react-query': 5.66.0(react@18.3.1)
+ react: 18.3.1
+
+ '@tanstack/react-query@5.66.0(react@18.3.1)':
+ dependencies:
+ '@tanstack/query-core': 5.66.0
+ react: 18.3.1
+
'@tsconfig/node10@1.0.11': {}
'@tsconfig/node12@1.0.11': {}
@@ -10017,7 +10106,7 @@ snapshots:
'@typescript-eslint/types': 7.18.0
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3)
'@typescript-eslint/visitor-keys': 7.18.0
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.3.7(supports-color@9.4.0)
eslint: 8.57.1
optionalDependencies:
typescript: 5.6.3
@@ -10034,6 +10123,11 @@ snapshots:
'@typescript-eslint/types': 7.18.0
'@typescript-eslint/visitor-keys': 7.18.0
+ '@typescript-eslint/scope-manager@8.24.0':
+ dependencies:
+ '@typescript-eslint/types': 8.24.0
+ '@typescript-eslint/visitor-keys': 8.24.0
+
'@typescript-eslint/type-utils@6.21.0(eslint@8.57.1)(typescript@5.3.3)':
dependencies:
'@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3)
@@ -10050,7 +10144,7 @@ snapshots:
dependencies:
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3)
'@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.6.3)
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.3.7(supports-color@9.4.0)
eslint: 8.57.1
ts-api-utils: 1.4.0(typescript@5.6.3)
optionalDependencies:
@@ -10062,6 +10156,8 @@ snapshots:
'@typescript-eslint/types@7.18.0': {}
+ '@typescript-eslint/types@8.24.0': {}
+
'@typescript-eslint/typescript-estree@6.21.0(typescript@5.3.3)':
dependencies:
'@typescript-eslint/types': 6.21.0
@@ -10081,7 +10177,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 7.18.0
'@typescript-eslint/visitor-keys': 7.18.0
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.3.7(supports-color@9.4.0)
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.5
@@ -10092,6 +10188,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@typescript-eslint/typescript-estree@8.24.0(typescript@5.6.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.24.0
+ '@typescript-eslint/visitor-keys': 8.24.0
+ debug: 4.3.7(supports-color@9.4.0)
+ fast-glob: 3.3.2
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.6.3
+ ts-api-utils: 2.0.1(typescript@5.6.3)
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
'@typescript-eslint/utils@6.21.0(eslint@8.57.1)(typescript@5.3.3)':
dependencies:
'@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1)
@@ -10117,6 +10227,17 @@ snapshots:
- supports-color
- typescript
+ '@typescript-eslint/utils@8.24.0(eslint@8.57.1)(typescript@5.6.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1)
+ '@typescript-eslint/scope-manager': 8.24.0
+ '@typescript-eslint/types': 8.24.0
+ '@typescript-eslint/typescript-estree': 8.24.0(typescript@5.6.3)
+ eslint: 8.57.1
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
'@typescript-eslint/visitor-keys@6.21.0':
dependencies:
'@typescript-eslint/types': 6.21.0
@@ -10127,6 +10248,11 @@ snapshots:
'@typescript-eslint/types': 7.18.0
eslint-visitor-keys: 3.4.3
+ '@typescript-eslint/visitor-keys@8.24.0':
+ dependencies:
+ '@typescript-eslint/types': 8.24.0
+ eslint-visitor-keys: 4.2.0
+
'@ungap/structured-clone@1.2.0': {}
'@vitejs/plugin-react-swc@3.7.1(vite@5.4.10(@types/node@20.17.6)(terser@5.36.0))':
@@ -11118,7 +11244,7 @@ snapshots:
engine.io-client@6.6.2(bufferutil@4.0.8)(utf-8-validate@5.0.10):
dependencies:
'@socket.io/component-emitter': 3.1.2
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.3.7(supports-color@9.4.0)
engine.io-parser: 5.2.3
ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)
xmlhttprequest-ssl: 2.1.2
@@ -11440,6 +11566,8 @@ snapshots:
eslint-visitor-keys@3.4.3: {}
+ eslint-visitor-keys@4.2.0: {}
+
eslint@8.57.1:
dependencies:
'@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1)
@@ -11453,7 +11581,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.3.7(supports-color@9.4.0)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
@@ -13862,7 +13990,7 @@ snapshots:
socket.io-client@4.8.1(bufferutil@4.0.8)(utf-8-validate@5.0.10):
dependencies:
'@socket.io/component-emitter': 3.1.2
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.3.7(supports-color@9.4.0)
engine.io-client: 6.6.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)
socket.io-parser: 4.2.4
transitivePeerDependencies:
@@ -13873,7 +14001,7 @@ snapshots:
socket.io-parser@4.2.4:
dependencies:
'@socket.io/component-emitter': 3.1.2
- debug: 4.3.7(supports-color@5.5.0)
+ debug: 4.3.7(supports-color@9.4.0)
transitivePeerDependencies:
- supports-color
@@ -14251,6 +14379,10 @@ snapshots:
dependencies:
typescript: 5.6.3
+ ts-api-utils@2.0.1(typescript@5.6.3):
+ dependencies:
+ typescript: 5.6.3
+
ts-interface-checker@0.1.13: {}
ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@20.17.6)(ts-node@10.9.2(@swc/core@1.8.0)(@types/node@20.17.6)(typescript@5.3.3)))(typescript@5.3.3):