11import React , {
2- createContext ,
3- useContext ,
4- useState ,
5- useEffect ,
6- useCallback ,
7- useRef
8- } from 'react' ;
9- import { DEFAULT_CATEGORIES , TaskCategory } from '@/config/categories' ;
10- import { DEFAULT_PROJECTS , ProjectCategory } from '@/config/projects' ;
11- import { useAuth } from '@/hooks/useAuth' ;
12- import { createDataService , DataService } from '@/services/dataService' ;
13- import { useRealtimeSync } from '@/hooks/useRealtimeSync' ;
2+ createContext ,
3+ useContext ,
4+ useState ,
5+ useEffect ,
6+ useCallback ,
7+ useRef
8+ } from "react" ;
9+ import { DEFAULT_CATEGORIES , TaskCategory } from "@/config/categories" ;
10+ import { DEFAULT_PROJECTS , ProjectCategory } from "@/config/projects" ;
11+ import { useAuth } from "@/hooks/useAuth" ;
12+ import { createDataService , DataService } from "@/services/dataService" ;
13+ import { useRealtimeSync } from "@/hooks/useRealtimeSync" ;
14+ import { generateDailySummary } from "@/utils/timeUtil" ;
1415
1516export interface Task {
1617 id : string ;
@@ -58,14 +59,15 @@ export interface TimeEntry {
5859}
5960
6061export interface InvoiceData {
61- client : string ;
62- period : { startDate : Date ; endDate : Date } ;
63- projects : { [ key : string ] : { hours : number ; rate : number ; amount : number } } ;
64- summary : {
65- totalHours : number ;
66- totalAmount : number ;
67- } ;
68- tasks : Task [ ] ;
62+ client : string ;
63+ period : { startDate : Date ; endDate : Date } ;
64+ projects : { [ key : string ] : { hours : number ; rate : number ; amount : number } } ;
65+ summary : {
66+ totalHours : number ;
67+ totalAmount : number ;
68+ } ;
69+ tasks : ( Task & { dayId : string ; dayDate : string ; dailySummary : string } ) [ ] ;
70+ dailySummaries : { [ dayId : string ] : { date : string ; summary : string } } ;
6971}
7072
7173interface TimeTrackingContextType {
@@ -1032,11 +1034,18 @@ export const TimeTrackingProvider: React.FC<{ children: React.ReactNode }> = ({
10321034 'day_record_id' ,
10331035 'is_current' ,
10341036 'inserted_at' ,
1035- 'updated_at'
1037+ 'updated_at' ,
1038+ 'daily_summary'
10361039 ] ;
10371040 const rows = [ headers . join ( ',' ) ] ;
10381041
10391042 filteredDays . forEach ( ( day ) => {
1043+ // Generate daily summary once per day
1044+ const dayDescriptions = day . tasks
1045+ . filter ( ( t ) => t . description )
1046+ . map ( ( t ) => t . description ! ) ;
1047+ const dailySummary = generateDailySummary ( dayDescriptions ) ;
1048+
10401049 day . tasks . forEach ( ( task ) => {
10411050 if ( task . duration ) {
10421051 const project = projects . find ( ( p ) => p . name === task . project ) ;
@@ -1066,7 +1075,8 @@ export const TimeTrackingProvider: React.FC<{ children: React.ReactNode }> = ({
10661075 `"${ day . id } "` , // day_record_id
10671076 'false' , // is_current - archived tasks are not current
10681077 `"${ insertedAtISO } "` , // inserted_at - actual database timestamp
1069- `"${ updatedAtISO } "` // updated_at - actual database timestamp
1078+ `"${ updatedAtISO } "` , // updated_at - actual database timestamp
1079+ `"${ dailySummary . replace ( / " / g, '""' ) } "` // daily_summary - escape quotes for CSV
10701080 ] ;
10711081 rows . push ( row . join ( ',' ) ) ;
10721082 }
@@ -1086,6 +1096,19 @@ export const TimeTrackingProvider: React.FC<{ children: React.ReactNode }> = ({
10861096 } ) ;
10871097 }
10881098
1099+ // Add daily summary to each day
1100+ const daysWithSummary = filteredDays . map ( ( day ) => {
1101+ const dayDescriptions = day . tasks
1102+ . filter ( ( t ) => t . description )
1103+ . map ( ( t ) => t . description ! ) ;
1104+ const dailySummary = generateDailySummary ( dayDescriptions ) ;
1105+
1106+ return {
1107+ ...day ,
1108+ dailySummary
1109+ } ;
1110+ } ) ;
1111+
10891112 const exportData = {
10901113 exportDate : new Date ( ) . toISOString ( ) ,
10911114 period : {
@@ -1103,7 +1126,7 @@ export const TimeTrackingProvider: React.FC<{ children: React.ReactNode }> = ({
11031126 endDate || new Date ( )
11041127 )
11051128 } ,
1106- days : filteredDays ,
1129+ days : daysWithSummary ,
11071130 projects : projects
11081131 } ;
11091132
@@ -1124,26 +1147,49 @@ export const TimeTrackingProvider: React.FC<{ children: React.ReactNode }> = ({
11241147 return dayDate >= startDate && dayDate <= endDate ;
11251148 } ) ;
11261149
1150+ // Generate daily summaries for all days in the period
1151+ const dailySummaries : { [ dayId : string ] : { date : string ; summary : string } } = { } ;
1152+ filteredDays . forEach ( ( day ) => {
1153+ const dayDescriptions = day . tasks
1154+ . filter ( ( t ) => t . description )
1155+ . map ( ( t ) => t . description ! ) ;
1156+ const summary = generateDailySummary ( dayDescriptions ) ;
1157+
1158+ if ( summary ) {
1159+ dailySummaries [ day . id ] = {
1160+ date : day . date ,
1161+ summary
1162+ } ;
1163+ }
1164+ } ) ;
1165+
11271166 const clientTasks = filteredDays . flatMap ( ( day ) =>
1128- day . tasks . filter ( ( task ) => {
1129- if ( ! task . client || task . client !== clientName || ! task . duration ) {
1130- return false ;
1131- }
1167+ day . tasks
1168+ . filter ( ( task ) => {
1169+ if ( ! task . client || task . client !== clientName || ! task . duration ) {
1170+ return false ;
1171+ }
11321172
1133- // Only include billable tasks in invoices
1134- if ( task . project && task . category ) {
1135- const project = projectMap . get ( task . project ) ;
1136- const category = categoryMap . get ( task . category ) ;
1173+ // Only include billable tasks in invoices
1174+ if ( task . project && task . category ) {
1175+ const project = projectMap . get ( task . project ) ;
1176+ const category = categoryMap . get ( task . category ) ;
11371177
1138- const projectIsBillable = project ?. isBillable !== false ;
1139- const categoryIsBillable = category ?. isBillable !== false ;
1178+ const projectIsBillable = project ?. isBillable !== false ;
1179+ const categoryIsBillable = category ?. isBillable !== false ;
11401180
1141- // Task must be billable to appear on invoice
1142- return projectIsBillable && categoryIsBillable ;
1143- }
1181+ // Task must be billable to appear on invoice
1182+ return projectIsBillable && categoryIsBillable ;
1183+ }
11441184
1145- return false ;
1146- } )
1185+ return false ;
1186+ } )
1187+ . map ( ( task ) => ( {
1188+ ...task ,
1189+ dayId : day . id ,
1190+ dayDate : day . date ,
1191+ dailySummary : dailySummaries [ day . id ] ?. summary || ""
1192+ } ) )
11471193 ) ;
11481194
11491195 const projectSummary : {
@@ -1181,7 +1227,8 @@ export const TimeTrackingProvider: React.FC<{ children: React.ReactNode }> = ({
11811227 totalHours : Math . round ( totalHours * 100 ) / 100 ,
11821228 totalAmount : Math . round ( totalAmount * 100 ) / 100
11831229 } ,
1184- tasks : clientTasks
1230+ tasks : clientTasks ,
1231+ dailySummaries
11851232 } ;
11861233 } ;
11871234
0 commit comments