11import { useMemo , useState } from "react" ;
22import { useParams , Link } from "react-router-dom" ;
3- import { ArrowLeft , Clock , DollarSign , Calendar , Plus , Pencil , Trash2 } from "lucide-react" ;
3+ import { ArrowLeft , Clock , DollarSign , Calendar , Plus , Pencil , Trash2 , CheckCircle2 } from "lucide-react" ;
44import { Button } from "@/components/ui/button" ;
55import { Card , CardContent } from "@/components/ui/card" ;
66import { Badge } from "@/components/ui/badge" ;
@@ -21,6 +21,24 @@ function formatDuration(seconds: number): string {
2121 return "0m" ;
2222}
2323
24+ function capitalizeStatus ( status : string ) : string {
25+ return status . charAt ( 0 ) . toUpperCase ( ) + status . slice ( 1 ) ;
26+ }
27+
28+ function getStatusBadgeVariant ( status : string ) : "default" | "secondary" | "destructive" | "outline" {
29+ switch ( status ) {
30+ case "active" :
31+ return "default" ;
32+ case "completed" :
33+ return "outline" ;
34+ case "cancelled" :
35+ return "destructive" ;
36+ case "paused" :
37+ default :
38+ return "secondary" ;
39+ }
40+ }
41+
2442export function ProjectDetailPage ( ) {
2543 const { id } = useParams < { id : string } > ( ) ;
2644 const { projects, sessions, clients, getClientById, addSession, updateSession, deleteSession } = useAppData ( ) ;
@@ -49,6 +67,16 @@ export function ProjectDetailPage() {
4967 return project . fixed_budget || 0 ;
5068 } , [ project , totalSeconds ] ) ;
5169
70+ const totalPaidEarned = useMemo ( ( ) => {
71+ if ( ! project ) return 0 ;
72+ if ( project . billing_type === "hourly" && project . rate ) {
73+ return projectSessions
74+ . filter ( ( s ) => s . payment_status === "paid" )
75+ . reduce ( ( sum , s ) => sum + ( ( s . duration_seconds / 3600 ) * project . rate ! ) , 0 ) ;
76+ }
77+ return project . fixed_budget || 0 ;
78+ } , [ project , projectSessions ] ) ;
79+
5280 if ( ! project ) {
5381 return (
5482 < div className = { sh . page } >
@@ -71,7 +99,7 @@ export function ProjectDetailPage() {
7199 < div >
72100 < h1 className = { s . projectName } > { project . name } </ h1 >
73101 < p className = { s . meta } >
74- { client ?. name || "—" } · < Badge variant = "secondary" > { project . status } </ Badge > · { project . billing_type === "hourly" ? `$${ project . rate } /hr` : `$${ project . fixed_budget } fixed` }
102+ { client ?. name || "—" } · < Badge variant = { getStatusBadgeVariant ( project . status ) } > { capitalizeStatus ( project . status ) } </ Badge > · { project . billing_type === "hourly" ? `$${ project . rate } /hr` : `$${ project . fixed_budget } fixed` }
75103 </ p >
76104 </ div >
77105 < div className = { s . headerActions } >
@@ -103,6 +131,15 @@ export function ProjectDetailPage() {
103131 </ div >
104132 </ CardContent >
105133 </ Card >
134+ < Card >
135+ < CardContent className = { s . statCard } >
136+ < CheckCircle2 className = { s . statIcon } style = { { color : "var(--green-500)" } } />
137+ < div >
138+ < p className = { s . statValue } > ${ totalPaidEarned . toFixed ( 0 ) } </ p >
139+ < p className = { s . statLabel } > { t ( "invoices.paid" ) } </ p >
140+ </ div >
141+ </ CardContent >
142+ </ Card >
106143 < Card >
107144 < CardContent className = { s . statCard } >
108145 < Calendar className = { s . statIcon } style = { { color : "var(--violet-400)" } } />
0 commit comments