1+
12import { Link } from "react-router" ;
23import { motion } from "framer-motion" ;
34import { Briefcase , MapPin , Building2 , ArrowUpRight , Clock , Search , ExternalLink , X } from "lucide-react" ;
@@ -8,7 +9,7 @@ import { queryKeys } from "../../../lib/query-keys";
89import type { Application } from "../../../lib/types" ;
910import { LoadingScreen } from "../../../components/LoadingScreen" ;
1011import { SEO } from "../../../components/SEO" ;
11-
12+ import toast from "@/components/ui/toast" ;
1213interface ExternalApplication {
1314 id : number ;
1415 studentId : number ;
@@ -214,13 +215,53 @@ const ExternalApplicationCard = React.memo(function ExternalApplicationCard({
214215} ) ;
215216
216217const PAGE_SIZE = 10 ;
218+ function WithdrawModal ( {
219+ open,
220+ onCancel,
221+ onConfirm,
222+ } : {
223+ open : boolean ;
224+ onCancel : ( ) => void ;
225+ onConfirm : ( ) => void ;
226+ } ) {
227+ if ( ! open ) return null ;
228+ return (
229+ < div className = "fixed inset-0 z-50 flex items-center justify-center bg-black/50" >
230+ < div className = "bg-white dark:bg-stone-900 border border-stone-200 dark:border-white/10 rounded-md p-6 max-w-sm w-full mx-4 space-y-4" >
231+ < h3 className = "text-base font-bold text-stone-900 dark:text-stone-50" >
232+ Withdraw Application?
233+ </ h3 >
234+ < p className = "text-sm text-stone-500" >
235+ Are you sure you want to withdraw this application? This action cannot be undone.
236+ </ p >
237+ < div className = "flex gap-3" >
238+ < button
239+ onClick = { onCancel }
240+ className = "flex-1 px-4 py-2 rounded-md text-xs font-mono uppercase tracking-widest text-stone-600 dark:text-stone-400 border border-stone-200 dark:border-white/10 hover:border-stone-400 transition-colors bg-transparent cursor-pointer"
241+ >
242+ Cancel
243+ </ button >
244+ < button
245+ onClick = { onConfirm }
246+ className = "flex-1 px-4 py-2 rounded-md text-xs font-mono uppercase tracking-widest text-white bg-red-500 hover:bg-red-600 transition-colors cursor-pointer border-0"
247+ >
248+ Withdraw
249+ </ button >
250+ </ div >
251+ </ div >
252+ </ div >
253+ ) ;
254+ }
255+
256+
217257
218258export default function MyApplicationsPage ( ) {
219259 const queryClient = useQueryClient ( ) ;
220260 const [ search , setSearch ] = useState ( "" ) ;
221261 const [ debouncedSearch , setDebouncedSearch ] = useState ( "" ) ;
222- const [ page , setPage ] = useState ( 1 ) ;
223-
262+ const [ page , setPage ] = useState ( 1 ) ;
263+ const [ withdrawId , setWithdrawId ] = useState < number | null > ( null ) ;
264+ const [ showWithdrawModal , setShowWithdrawModal ] = useState ( false ) ;
224265 useEffect ( ( ) => {
225266 const t = setTimeout ( ( ) => setDebouncedSearch ( search ) , 200 ) ;
226267 return ( ) => clearTimeout ( t ) ;
@@ -263,26 +304,50 @@ export default function MyApplicationsPage() {
263304
264305 const handleWithdraw = useCallback (
265306 async ( id : number ) => {
266- if ( ! confirm ( "Are you sure you want to withdraw this application?" ) ) return ;
267- try {
268- await api . delete ( `/student/applications/${ id } ` ) ;
269- queryClient . setQueryData < Application [ ] > ( queryKeys . applications . mine ( ) , ( old ) =>
270- ( old ?? [ ] ) . map ( ( a ) => ( a . id === id ? { ...a , status : "WITHDRAWN" as const } : a ) )
271- ) ;
272- } catch {
273- alert ( "Failed to withdraw" ) ;
274- }
307+ setWithdrawId ( id ) ;
308+ setShowWithdrawModal ( true ) ;
275309 } ,
276- [ queryClient ]
310+ [ ]
277311 ) ;
278312
313+ const confirmWithdraw = useCallback ( async ( ) => {
314+ if ( ! withdrawId ) return ;
315+ const idToWithdraw = withdrawId ;
316+ setShowWithdrawModal ( false ) ;
317+ setWithdrawId ( null ) ;
318+ try {
319+ await api . delete ( `/student/applications/${ idToWithdraw } ` ) ;
320+ queryClient . setQueryData < {
321+ applications : Application [ ] ;
322+ externalApplications : ExternalApplication [ ] ;
323+ } > ( queryKeys . applications . mine ( ) , ( old ) => {
324+ if ( ! old ) return old ;
325+ return {
326+ ...old ,
327+ applications : old . applications . map ( ( a ) =>
328+ a . id === idToWithdraw ? { ...a , status : "WITHDRAWN" as const } : a
329+ ) ,
330+ } ;
331+ } ) ;
332+ toast . success ( "Application withdrawn successfully" ) ;
333+ } catch {
334+ toast . error ( "Failed to withdraw application" ) ;
335+ }
336+ } , [ withdrawId , queryClient ] ) ;
279337 if ( isLoading ) return < LoadingScreen /> ;
280338
339+
340+
281341 const hasSearch = search . trim ( ) . length > 0 ;
282342
283343 return (
284344 < div className = "relative pb-16" >
285345 < SEO title = "My Applications" noIndex />
346+ < WithdrawModal
347+ open = { showWithdrawModal }
348+ onCancel = { ( ) => setShowWithdrawModal ( false ) }
349+ onConfirm = { confirmWithdraw }
350+ />
286351
287352 { /* Header */ }
288353 < motion . div
0 commit comments