1+ import { keepPreviousData , useMutation , useQuery , useQueryClient } from "@tanstack/react-query" ;
12import { format } from "date-fns" ;
2- import { useCallback , useEffect , useState } from "react" ;
3+ import { useState } from "react" ;
34import { toast } from "sonner" ;
45import { Button } from "@/components/ui/button" ;
56import { Table , TableBody , TableCell , TableHead , TableHeader , TableRow } from "@/components/ui/table" ;
@@ -12,7 +13,7 @@ interface Props {
1213 verifyFilter : VerifyStatus ;
1314}
1415
15- const S3_BASE_URL = import . meta. env . VITE_S3_BASE_URL ;
16+ const S3_BASE_URL = ( import . meta. env . VITE_S3_BASE_URL as string | undefined ) || "" ;
1617
1718const LANGUAGE_TEST_OPTIONS : { value : LanguageTestType ; label : string } [ ] = [
1819 { value : "TOEIC" , label : "TOEIC" } ,
@@ -30,36 +31,46 @@ const LANGUAGE_TEST_OPTIONS: { value: LanguageTestType; label: string }[] = [
3031] ;
3132
3233export function LanguageScoreTable ( { verifyFilter } : Props ) {
33- const [ scores , setScores ] = useState < LanguageScoreWithUser [ ] > ( [ ] ) ;
34+ const queryClient = useQueryClient ( ) ;
3435 const [ page , setPage ] = useState ( 1 ) ;
35- const [ totalPages , setTotalPages ] = useState ( 1 ) ;
36- const [ loading , setLoading ] = useState ( false ) ;
3736 const [ editingId , setEditingId ] = useState < number | null > ( null ) ;
3837 const [ editingScore , setEditingScore ] = useState < string > ( "" ) ;
3938 const [ editingType , setEditingType ] = useState < LanguageTestType > ( "TOEIC" ) ;
4039
41- const fetchScores = useCallback ( async ( ) => {
42- setLoading ( true ) ;
43- try {
44- const response = await scoreApi . getLanguageScores ( { verifyStatus : verifyFilter } , page ) ;
45- setScores ( response . content ) ;
46- setTotalPages ( response . totalPages ) ;
47- } catch ( error ) {
48- console . error ( "Failed to fetch Language scores:" , error ) ;
49- } finally {
50- setLoading ( false ) ;
51- }
52- } , [ verifyFilter , page ] ) ;
40+ const { data, isLoading, isFetching } = useQuery ( {
41+ queryKey : [ "scores" , "language" , verifyFilter , page ] ,
42+ queryFn : ( ) => scoreApi . getLanguageScores ( { verifyStatus : verifyFilter } , page ) ,
43+ placeholderData : keepPreviousData ,
44+ } ) ;
5345
54- useEffect ( ( ) => {
55- fetchScores ( ) ;
56- } , [ fetchScores ] ) ;
46+ const updateLanguageMutation = useMutation ( {
47+ mutationFn : ( {
48+ id,
49+ status,
50+ reason,
51+ score,
52+ } : {
53+ id : number ;
54+ status : VerifyStatus ;
55+ reason ?: string ;
56+ score : LanguageScoreWithUser ;
57+ } ) => scoreApi . updateLanguageScore ( id , status , reason , score ) ,
58+ onSuccess : async ( ) => {
59+ await queryClient . invalidateQueries ( { queryKey : [ "scores" , "language" ] } ) ;
60+ } ,
61+ } ) ;
62+
63+ const scores = data ?. content ?? [ ] ;
64+ const totalPages = data ?. totalPages ?? 1 ;
5765
5866 const handleVerifyStatus = async ( id : number , status : VerifyStatus , reason ?: string ) => {
5967 try {
6068 const score = scores . find ( ( s ) => s . languageTestScoreStatusResponse . id === id ) ;
61- await scoreApi . updateLanguageScore ( id , status , reason , score ) ;
62- fetchScores ( ) ;
69+ if ( ! score ) {
70+ throw new Error ( "Score data is required" ) ;
71+ }
72+
73+ await updateLanguageMutation . mutateAsync ( { id, status, reason, score } ) ;
6374 } catch ( error ) {
6475 console . error ( "Failed to update Language score:" , error ) ;
6576 toast . error ( "성적 상태 업데이트에 실패했습니다" ) ;
@@ -74,11 +85,11 @@ export function LanguageScoreTable({ verifyFilter }: Props) {
7485
7586 const handleSave = async ( score : LanguageScoreWithUser ) => {
7687 try {
77- await scoreApi . updateLanguageScore (
78- score . languageTestScoreStatusResponse . id ,
79- score . languageTestScoreStatusResponse . verifyStatus ,
80- score . languageTestScoreStatusResponse . rejectedReason || undefined ,
81- {
88+ await updateLanguageMutation . mutateAsync ( {
89+ id : score . languageTestScoreStatusResponse . id ,
90+ status : score . languageTestScoreStatusResponse . verifyStatus ,
91+ reason : score . languageTestScoreStatusResponse . rejectedReason || undefined ,
92+ score : {
8293 ...score ,
8394 languageTestScoreStatusResponse : {
8495 ...score . languageTestScoreStatusResponse ,
@@ -89,9 +100,8 @@ export function LanguageScoreTable({ verifyFilter }: Props) {
89100 } ,
90101 } ,
91102 } ,
92- ) ;
103+ } ) ;
93104 setEditingId ( null ) ;
94- fetchScores ( ) ;
95105 toast . success ( "어학성적이 수정되었습니다" ) ;
96106 } catch ( error ) {
97107 console . error ( "Failed to update language score:" , error ) ;
@@ -106,6 +116,9 @@ export function LanguageScoreTable({ verifyFilter }: Props) {
106116
107117 return (
108118 < div className = "rounded-lg border border-k-100 bg-k-0" >
119+ { isFetching && ! isLoading ? (
120+ < div className = "border-b border-k-100 px-4 py-2 typo-regular-4 text-k-500" > 최신 데이터를 불러오는 중...</ div >
121+ ) : null }
109122 < div className = "overflow-x-auto" >
110123 < Table >
111124 < TableHeader >
@@ -122,7 +135,7 @@ export function LanguageScoreTable({ verifyFilter }: Props) {
122135 </ TableRow >
123136 </ TableHeader >
124137 < TableBody >
125- { loading ? (
138+ { isLoading ? (
126139 < TableRow >
127140 < TableCell colSpan = { 9 } className = "text-center" >
128141 < div className = "flex items-center justify-center" >
0 commit comments