11import { useEffect , useState } from "react" ;
2- import { useQuery } from "@tanstack/react-query" ;
2+ import { useQuery , useMutation , useQueryClient } from "@tanstack/react-query" ;
33import { useAuth } from "@/hooks/useAuth" ;
4- import { clearAuth } from "@/lib/authUtils" ;
4+ import { clearAuth , isUnauthorizedError } from "@/lib/authUtils" ;
55import { apiClient } from "@/lib/apiClient" ;
6+ import { useToast } from "@/hooks/use-toast" ;
67import { Badge } from "@/components/ui/badge" ;
78import { Button } from "@/components/ui/button" ;
89import {
@@ -16,20 +17,24 @@ import {
1617 DialogContent ,
1718 DialogHeader ,
1819 DialogTitle ,
20+ DialogDescription ,
1921} from "@/components/ui/dialog" ;
2022import OtherCalculationModal from "@/components/modals/other-calculation-modal" ;
2123import ReferenceModal from "@/components/modals/reference-modal" ;
2224import ReferenceViewModal from "@/components/modals/reference-view-modal" ;
2325
2426export default function Dashboard ( ) {
2527 const { user, isAuthenticated, isLoading } = useAuth ( ) ;
28+ const { toast } = useToast ( ) ;
29+ const queryClient = useQueryClient ( ) ;
2630 const [ otherModalOpen , setOtherModalOpen ] = useState ( false ) ;
2731 const [ referenceModalOpen , setReferenceModalOpen ] = useState ( false ) ;
2832 const [ viewModalOpen , setViewModalOpen ] = useState ( false ) ;
2933 const [ selectedActivity , setSelectedActivity ] = useState ( null ) ;
3034 const [ referenceViewModal , setReferenceViewModal ] = useState < any > ( null ) ;
3135 const [ activeFilter , setActiveFilter ] = useState < string > ( 'all' ) ;
3236 const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
37+ const [ deleteModalOpen , setDeleteModalOpen ] = useState < any > ( null ) ;
3338
3439 // This page is only rendered when authenticated, no need for redirect logic
3540
@@ -72,6 +77,48 @@ export default function Dashboard() {
7277 window . location . href = "/" ;
7378 } ;
7479
80+ // Delete reference mutation
81+ const deleteMutation = useMutation ( {
82+ mutationFn : async ( referenceId : string ) => {
83+ return await apiClient . delete ( `/api/references/${ referenceId } ` ) ;
84+ } ,
85+ onSuccess : ( ) => {
86+ queryClient . invalidateQueries ( { queryKey : [ "/api/dashboard/activities" ] } ) ;
87+ queryClient . invalidateQueries ( { queryKey : [ "/api/dashboard/stats" ] } ) ;
88+ toast ( {
89+ title : "Reference Deleted" ,
90+ description : "The reference has been successfully deleted." ,
91+ } ) ;
92+ setDeleteModalOpen ( null ) ;
93+ } ,
94+ onError : ( error ) => {
95+ if ( isUnauthorizedError ( error ) ) {
96+ toast ( {
97+ title : "Unauthorized" ,
98+ description : "You are logged out. Logging in again..." ,
99+ variant : "destructive" ,
100+ } ) ;
101+ setTimeout ( ( ) => {
102+ window . location . href = "/" ;
103+ } , 500 ) ;
104+ return ;
105+ }
106+ toast ( {
107+ title : "Error" ,
108+ description : "Failed to delete reference. Please try again." ,
109+ variant : "destructive" ,
110+ } ) ;
111+ } ,
112+ } ) ;
113+
114+ const confirmDeleteActivity = ( ) => {
115+ if ( deleteModalOpen ) {
116+ // Activity IDs are prefixed (e.g. "ref-sent-<uuid>"), extract the actual reference UUID
117+ const referenceId = deleteModalOpen . id . replace ( / ^ r e f - ( s e n t | r e c e i v e d ) - / , '' ) ;
118+ deleteMutation . mutate ( referenceId ) ;
119+ }
120+ } ;
121+
75122 const handleViewActivity = ( activity : any ) => {
76123 // For reference activities, show reference details modal
77124 if ( activity . type === 'reference' || activity . activity === 'Reference Provided' || activity . activity === 'Reference Received' ) {
@@ -580,6 +627,17 @@ export default function Dashboard() {
580627 </ svg >
581628 View Details
582629 </ DropdownMenuItem >
630+ { activity . activity === 'Reference Provided' && (
631+ < DropdownMenuItem
632+ className = "text-red-600"
633+ onClick = { ( ) => setDeleteModalOpen ( activity ) }
634+ >
635+ < svg className = "w-4 h-4 mr-2" fill = "currentColor" viewBox = "0 0 20 20" >
636+ < path fillRule = "evenodd" d = "M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule = "evenodd" />
637+ </ svg >
638+ Delete
639+ </ DropdownMenuItem >
640+ ) }
583641 </ DropdownMenuContent >
584642 </ DropdownMenu >
585643 </ td >
@@ -626,6 +684,17 @@ export default function Dashboard() {
626684 </ svg >
627685 View Details
628686 </ DropdownMenuItem >
687+ { activity . activity === 'Reference Provided' && (
688+ < DropdownMenuItem
689+ className = "text-red-600"
690+ onClick = { ( ) => setDeleteModalOpen ( activity ) }
691+ >
692+ < svg className = "w-4 h-4 mr-2" fill = "currentColor" viewBox = "0 0 20 20" >
693+ < path fillRule = "evenodd" d = "M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule = "evenodd" />
694+ </ svg >
695+ Delete
696+ </ DropdownMenuItem >
697+ ) }
629698 </ DropdownMenuContent >
630699 </ DropdownMenu >
631700 < div className = "flex items-center gap-2" >
@@ -915,6 +984,40 @@ export default function Dashboard() {
915984 ) : null }
916985 </ DialogContent >
917986 </ Dialog >
987+
988+ { /* Delete Reference Confirmation Modal */ }
989+ < Dialog open = { ! ! deleteModalOpen } onOpenChange = { ( open ) => ! open && setDeleteModalOpen ( null ) } >
990+ < DialogContent className = "w-full max-w-sm sm:max-w-md mx-4 sm:mx-auto bg-fig-10 border-2 border-fig/20 shadow-2xl rounded-xl" >
991+ < DialogHeader className = "text-center" >
992+ < div className = "mx-auto w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mb-4" >
993+ < svg className = "w-8 h-8 text-red-600" fill = "currentColor" viewBox = "0 0 20 20" >
994+ < path fillRule = "evenodd" d = "M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule = "evenodd" />
995+ </ svg >
996+ </ div >
997+ < DialogTitle className = "text-xl font-black text-fig" > Delete Reference</ DialogTitle >
998+ < DialogDescription className = "text-fig/70 text-sm font-medium mt-2" >
999+ Are you sure you want to permanently delete the reference for < strong > { deleteModalOpen ?. target } </ strong > ? This action cannot be undone.
1000+ </ DialogDescription >
1001+ </ DialogHeader >
1002+
1003+ < div className = "flex gap-3 mt-6" >
1004+ < Button
1005+ variant = "outline"
1006+ onClick = { ( ) => setDeleteModalOpen ( null ) }
1007+ className = "flex-1 border-2 border-fig/30 text-fig/70 hover:bg-fig-10 hover:border-fig/40 font-bold h-11"
1008+ >
1009+ Cancel
1010+ </ Button >
1011+ < Button
1012+ onClick = { confirmDeleteActivity }
1013+ disabled = { deleteMutation . isPending }
1014+ className = "flex-1 bg-red-600 hover:bg-red-700 text-white font-bold h-11"
1015+ >
1016+ { deleteMutation . isPending ? 'Deleting...' : 'Delete' }
1017+ </ Button >
1018+ </ div >
1019+ </ DialogContent >
1020+ </ Dialog >
9181021 </ div >
9191022 ) ;
9201023}
0 commit comments