diff options
Diffstat (limited to 'internal')
42 files changed, 194 insertions, 195 deletions
diff --git a/internal/actions/actions.go b/internal/actions/actions.go index d3e1460..61d007b 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -3,7 +3,7 @@ package actions import ( "context" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) // Actions provides high-level business operations for time tracking diff --git a/internal/actions/clients.go b/internal/actions/clients.go index c71ef4a..1a99d59 100644 --- a/internal/actions/clients.go +++ b/internal/actions/clients.go @@ -8,7 +8,7 @@ import ( "strconv" "strings" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) // CreateClient creates a new client with the given name and optional email/rate diff --git a/internal/actions/projects.go b/internal/actions/projects.go index 21f5ef5..4cb4638 100644 --- a/internal/actions/projects.go +++ b/internal/actions/projects.go @@ -6,7 +6,7 @@ import ( "fmt" "strconv" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) // CreateProject creates a new project for the specified client diff --git a/internal/actions/timer.go b/internal/actions/timer.go index 9f29c51..a7e7bbb 100644 --- a/internal/actions/timer.go +++ b/internal/actions/timer.go @@ -6,7 +6,7 @@ import ( "errors" "fmt" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) // PunchIn starts a timer for the specified client/project diff --git a/internal/commands/add_client.go b/internal/commands/add_client.go index 98aec3d..8b1bb0d 100644 --- a/internal/commands/add_client.go +++ b/internal/commands/add_client.go @@ -3,8 +3,8 @@ package commands import ( "fmt" - "punchcard/internal/actions" - "punchcard/internal/context" + "git.tjp.lol/punchcard/internal/actions" + "git.tjp.lol/punchcard/internal/context" "github.com/spf13/cobra" ) diff --git a/internal/commands/add_client_test.go b/internal/commands/add_client_test.go index 23c6e71..0373002 100644 --- a/internal/commands/add_client_test.go +++ b/internal/commands/add_client_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) func TestAddClientCommand(t *testing.T) { diff --git a/internal/commands/add_project.go b/internal/commands/add_project.go index 1ed42db..6aede2a 100644 --- a/internal/commands/add_project.go +++ b/internal/commands/add_project.go @@ -3,8 +3,8 @@ package commands import ( "fmt" - "punchcard/internal/actions" - punchctx "punchcard/internal/context" + "git.tjp.lol/punchcard/internal/actions" + punchctx "git.tjp.lol/punchcard/internal/context" "github.com/spf13/cobra" ) diff --git a/internal/commands/add_project_test.go b/internal/commands/add_project_test.go index c41bd5c..1857d9e 100644 --- a/internal/commands/add_project_test.go +++ b/internal/commands/add_project_test.go @@ -5,7 +5,7 @@ import ( "database/sql" "testing" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) func TestAddProjectCommand(t *testing.T) { diff --git a/internal/commands/billable_rate_test.go b/internal/commands/billable_rate_test.go index 335eb07..228e29d 100644 --- a/internal/commands/billable_rate_test.go +++ b/internal/commands/billable_rate_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) func TestTimeEntryBillableRateCoalescing(t *testing.T) { diff --git a/internal/commands/helpers.go b/internal/commands/helpers.go index a0c572b..cf2747d 100644 --- a/internal/commands/helpers.go +++ b/internal/commands/helpers.go @@ -3,8 +3,9 @@ package commands import ( "context" "errors" - "punchcard/internal/actions" - "punchcard/internal/queries" + + "git.tjp.lol/punchcard/internal/actions" + "git.tjp.lol/punchcard/internal/queries" ) // ErrNoActiveTimer is returned when trying to punch out but no timer is active @@ -29,4 +30,5 @@ func findProject(ctx context.Context, q *queries.Queries, projectRef string) (qu return queries.Project{}, err } return *project, nil -}
\ No newline at end of file +} + diff --git a/internal/commands/import.go b/internal/commands/import.go index acbb0d6..28862b2 100644 --- a/internal/commands/import.go +++ b/internal/commands/import.go @@ -9,9 +9,9 @@ import ( "strings" "time" - punchctx "punchcard/internal/context" - "punchcard/internal/database" - "punchcard/internal/queries" + punchctx "git.tjp.lol/punchcard/internal/context" + "git.tjp.lol/punchcard/internal/database" + "git.tjp.lol/punchcard/internal/queries" "github.com/spf13/cobra" ) diff --git a/internal/commands/import_test.go b/internal/commands/import_test.go index ed59f92..6907b23 100644 --- a/internal/commands/import_test.go +++ b/internal/commands/import_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) func TestImportCommand(t *testing.T) { diff --git a/internal/commands/in.go b/internal/commands/in.go index e7847f6..8c5025a 100644 --- a/internal/commands/in.go +++ b/internal/commands/in.go @@ -3,8 +3,8 @@ package commands import ( "fmt" - "punchcard/internal/actions" - punchctx "punchcard/internal/context" + "git.tjp.lol/punchcard/internal/actions" + punchctx "git.tjp.lol/punchcard/internal/context" "github.com/spf13/cobra" ) diff --git a/internal/commands/in_test.go b/internal/commands/in_test.go index eac70a2..7d11a29 100644 --- a/internal/commands/in_test.go +++ b/internal/commands/in_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) func TestInCommand(t *testing.T) { diff --git a/internal/commands/out.go b/internal/commands/out.go index 1355f3d..a10bb6e 100644 --- a/internal/commands/out.go +++ b/internal/commands/out.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "punchcard/internal/actions" - punchctx "punchcard/internal/context" + "git.tjp.lol/punchcard/internal/actions" + punchctx "git.tjp.lol/punchcard/internal/context" "github.com/spf13/cobra" ) diff --git a/internal/commands/out_test.go b/internal/commands/out_test.go index aeb2359..03a9b73 100644 --- a/internal/commands/out_test.go +++ b/internal/commands/out_test.go @@ -6,7 +6,7 @@ import ( "errors" "testing" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) func TestOutCommand(t *testing.T) { diff --git a/internal/commands/report.go b/internal/commands/report.go index 30972b4..530a237 100644 --- a/internal/commands/report.go +++ b/internal/commands/report.go @@ -4,9 +4,9 @@ import ( "fmt" "time" - punchctx "punchcard/internal/context" - "punchcard/internal/database" - "punchcard/internal/reports" + punchctx "git.tjp.lol/punchcard/internal/context" + "git.tjp.lol/punchcard/internal/database" + "git.tjp.lol/punchcard/internal/reports" "github.com/spf13/cobra" ) diff --git a/internal/commands/report_coalescing_test.go b/internal/commands/report_coalescing_test.go index 8f4d221..fd0172c 100644 --- a/internal/commands/report_coalescing_test.go +++ b/internal/commands/report_coalescing_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) func TestReportRateCoalescing(t *testing.T) { @@ -248,4 +248,4 @@ func TestReportRateCoalescingWithoutProject(t *testing.T) { if entry.EntryBillableRate.Valid { t.Errorf("Expected entry billable rate to be NULL when no explicit override, got %d", entry.EntryBillableRate.Int64) } -}
\ No newline at end of file +} diff --git a/internal/commands/root.go b/internal/commands/root.go index f3611ac..9ac0790 100644 --- a/internal/commands/root.go +++ b/internal/commands/root.go @@ -4,8 +4,8 @@ import ( "context" "database/sql" - punchctx "punchcard/internal/context" - "punchcard/internal/database" + punchctx "git.tjp.lol/punchcard/internal/context" + "git.tjp.lol/punchcard/internal/database" "github.com/spf13/cobra" ) diff --git a/internal/commands/set.go b/internal/commands/set.go index 32f3b96..b6e6cb5 100644 --- a/internal/commands/set.go +++ b/internal/commands/set.go @@ -7,9 +7,9 @@ import ( "strconv" "strings" - punchctx "punchcard/internal/context" - "punchcard/internal/database" - "punchcard/internal/queries" + punchctx "git.tjp.lol/punchcard/internal/context" + "git.tjp.lol/punchcard/internal/database" + "git.tjp.lol/punchcard/internal/queries" "github.com/spf13/cobra" ) @@ -330,4 +330,3 @@ func setContractorValues(q *queries.Queries, updates map[string]string) error { return nil } - diff --git a/internal/commands/status.go b/internal/commands/status.go index 626b258..0f3dc29 100644 --- a/internal/commands/status.go +++ b/internal/commands/status.go @@ -8,8 +8,8 @@ import ( "strconv" "time" - punchctx "punchcard/internal/context" - "punchcard/internal/queries" + punchctx "git.tjp.lol/punchcard/internal/context" + "git.tjp.lol/punchcard/internal/queries" "github.com/spf13/cobra" ) diff --git a/internal/commands/status_test.go b/internal/commands/status_test.go index d7c2ac8..ef80e2d 100644 --- a/internal/commands/status_test.go +++ b/internal/commands/status_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) func TestStatusCommand(t *testing.T) { diff --git a/internal/commands/test_utils.go b/internal/commands/test_utils.go index 282e472..c53578f 100644 --- a/internal/commands/test_utils.go +++ b/internal/commands/test_utils.go @@ -6,9 +6,9 @@ import ( "database/sql" "testing" - punchctx "punchcard/internal/context" - "punchcard/internal/database" - "punchcard/internal/queries" + punchctx "git.tjp.lol/punchcard/internal/context" + "git.tjp.lol/punchcard/internal/database" + "git.tjp.lol/punchcard/internal/queries" ) func setupTestDB(t *testing.T) (*queries.Queries, func()) { diff --git a/internal/commands/tui.go b/internal/commands/tui.go index 529e937..774e982 100644 --- a/internal/commands/tui.go +++ b/internal/commands/tui.go @@ -3,8 +3,8 @@ package commands import ( "fmt" - punchctx "punchcard/internal/context" - "punchcard/internal/tui" + punchctx "git.tjp.lol/punchcard/internal/context" + "git.tjp.lol/punchcard/internal/tui" "github.com/spf13/cobra" ) diff --git a/internal/context/db.go b/internal/context/db.go index a9f53d3..044dc28 100644 --- a/internal/context/db.go +++ b/internal/context/db.go @@ -3,7 +3,7 @@ package context import ( "context" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) type dbContextKey struct{} @@ -20,4 +20,3 @@ func GetDB(ctx context.Context) *queries.Queries { } return nil } - diff --git a/internal/database/db.go b/internal/database/db.go index f699d14..27fb472 100644 --- a/internal/database/db.go +++ b/internal/database/db.go @@ -7,7 +7,7 @@ import ( "os" "path/filepath" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" _ "modernc.org/sqlite" ) diff --git a/internal/reports/api.go b/internal/reports/api.go index 0db8672..90b066b 100644 --- a/internal/reports/api.go +++ b/internal/reports/api.go @@ -7,7 +7,7 @@ import ( "path/filepath" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) type ReportParams struct { @@ -497,4 +497,3 @@ func findProjectByName(ctx context.Context, q *queries.Queries, projectName stri } return projects[0], nil } - diff --git a/internal/reports/invoice.go b/internal/reports/invoice.go index 73235d5..4ac5eb4 100644 --- a/internal/reports/invoice.go +++ b/internal/reports/invoice.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) type InvoiceData struct { diff --git a/internal/reports/pdf.go b/internal/reports/pdf.go index 8434f07..b83436f 100644 --- a/internal/reports/pdf.go +++ b/internal/reports/pdf.go @@ -9,8 +9,8 @@ import ( "path/filepath" "time" - "punchcard/internal/queries" - "punchcard/templates" + "git.tjp.lol/punchcard/internal/queries" + "git.tjp.lol/punchcard/templates" ) // RecordInvoice records the invoice in the database after successful generation @@ -229,16 +229,16 @@ type UnifiedJSONData struct { ContractorName string `json:"contractor_name"` ContractorLabel string `json:"contractor_label"` ContractorEmail string `json:"contractor_email"` - + // Invoice-specific fields InvoiceNumber string `json:"invoice_number"` LineItems []LineItem `json:"line_items"` TotalAmount float64 `json:"total_amount"` - + // Timesheet-specific fields - Entries []TimesheetEntry `json:"entries"` - Timezone string `json:"timezone"` - + Entries []TimesheetEntry `json:"entries"` + Timezone string `json:"timezone"` + // Shared field with same value TotalHours float64 `json:"total_hours"` } @@ -268,7 +268,7 @@ func GenerateUnifiedPDF(unifiedData *UnifiedReportData, outputPath string) error ContractorLabel: unifiedData.InvoiceData.ContractorLabel, ContractorEmail: unifiedData.InvoiceData.ContractorEmail, TotalHours: unifiedData.InvoiceData.TotalHours, // Should match timesheet total - + // Invoice-specific fields InvoiceNumber: fmt.Sprintf("%04d-%02d-%03d", unifiedData.InvoiceData.DateRange.Start.Year(), @@ -277,7 +277,7 @@ func GenerateUnifiedPDF(unifiedData *UnifiedReportData, outputPath string) error ), LineItems: unifiedData.InvoiceData.LineItems, TotalAmount: unifiedData.InvoiceData.TotalAmount, - + // Timesheet-specific fields Entries: unifiedData.TimesheetData.Entries, Timezone: unifiedData.TimesheetData.Timezone, @@ -296,7 +296,7 @@ func GenerateUnifiedPDF(unifiedData *UnifiedReportData, outputPath string) error // Create unified template by combining invoice and timesheet templates unifiedTemplate := templates.InvoiceTemplate + "\n\n#pagebreak()\n\n" + templates.TimesheetTemplate - + // Write Typst template file typstFile := filepath.Join(tempDir, "unified.typ") if err := os.WriteFile(typstFile, []byte(unifiedTemplate), 0o644); err != nil { diff --git a/internal/reports/pdf_test.go b/internal/reports/pdf_test.go index b31112e..1f2e968 100644 --- a/internal/reports/pdf_test.go +++ b/internal/reports/pdf_test.go @@ -4,9 +4,10 @@ import ( "os" "os/exec" "path/filepath" - "punchcard/templates" "testing" "time" + + "git.tjp.lol/punchcard/templates" ) // Helper function for tests @@ -39,13 +40,13 @@ func TestTypstTemplateCompilation(t *testing.T) { } dataFile := filepath.Join(tempDir, "data.json") - if err := os.WriteFile(dataFile, testData, 0644); err != nil { + if err := os.WriteFile(dataFile, testData, 0o644); err != nil { t.Fatalf("Failed to write test data file: %v", err) } // Write Typst template to temp directory typstFile := filepath.Join(tempDir, "invoice.typ") - if err := os.WriteFile(typstFile, []byte(templates.InvoiceTemplate), 0644); err != nil { + if err := os.WriteFile(typstFile, []byte(templates.InvoiceTemplate), 0o644); err != nil { t.Fatalf("Failed to write Typst template: %v", err) } diff --git a/internal/reports/timesheet.go b/internal/reports/timesheet.go index a40d8ae..d9ad4b8 100644 --- a/internal/reports/timesheet.go +++ b/internal/reports/timesheet.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) type TimesheetData struct { @@ -33,14 +33,14 @@ type TimesheetEntry struct { } type timesheetEntryData struct { - TimeEntryID int64 - StartTime time.Time - EndTime sql.NullTime - Description sql.NullString - ClientID int64 - ClientName string - ProjectID sql.NullInt64 - ProjectName sql.NullString + TimeEntryID int64 + StartTime time.Time + EndTime sql.NullTime + Description sql.NullString + ClientID int64 + ClientName string + ProjectID sql.NullInt64 + ProjectName sql.NullString DurationSeconds int64 } @@ -59,28 +59,28 @@ func GenerateTimesheetData( case []queries.GetTimesheetDataByClientRow: for _, entry := range e { timeEntries = append(timeEntries, timesheetEntryData{ - TimeEntryID: entry.TimeEntryID, - StartTime: entry.StartTime, - EndTime: entry.EndTime, - Description: entry.Description, - ClientID: entry.ClientID, - ClientName: entry.ClientName, - ProjectID: entry.ProjectID, - ProjectName: entry.ProjectName, + TimeEntryID: entry.TimeEntryID, + StartTime: entry.StartTime, + EndTime: entry.EndTime, + Description: entry.Description, + ClientID: entry.ClientID, + ClientName: entry.ClientName, + ProjectID: entry.ProjectID, + ProjectName: entry.ProjectName, DurationSeconds: entry.DurationSeconds, }) } case []queries.GetTimesheetDataByProjectRow: for _, entry := range e { timeEntries = append(timeEntries, timesheetEntryData{ - TimeEntryID: entry.TimeEntryID, - StartTime: entry.StartTime, - EndTime: entry.EndTime, - Description: entry.Description, - ClientID: entry.ClientID, - ClientName: entry.ClientName, - ProjectID: sql.NullInt64{Int64: entry.ProjectID, Valid: true}, - ProjectName: sql.NullString{String: entry.ProjectName, Valid: true}, + TimeEntryID: entry.TimeEntryID, + StartTime: entry.StartTime, + EndTime: entry.EndTime, + Description: entry.Description, + ClientID: entry.ClientID, + ClientName: entry.ClientName, + ProjectID: sql.NullInt64{Int64: entry.ProjectID, Valid: true}, + ProjectName: sql.NullString{String: entry.ProjectName, Valid: true}, DurationSeconds: entry.DurationSeconds, }) } @@ -134,27 +134,27 @@ func convertToTimesheetEntries(entries []timesheetEntryData, loc *time.Location) // Convert UTC times to specified timezone localStartTime := entry.StartTime.In(loc) localEndTime := entry.EndTime.Time.In(loc) - + // Format date as YYYY-MM-DD date := localStartTime.Format("2006-01-02") - + // Format times as HH:MM startTime := localStartTime.Format("15:04") endTime := localEndTime.Format("15:04") - + // Format duration as HH:MM duration := formatDuration(entry.DurationSeconds) - + // Calculate hours as decimal, rounded to nearest minute - totalMinutes := (entry.DurationSeconds + 30) / 60 // Round to nearest minute + totalMinutes := (entry.DurationSeconds + 30) / 60 // Round to nearest minute hours := float64(totalMinutes) / 60.0 - + // Get project name projectName := "" if entry.ProjectName.Valid { projectName = entry.ProjectName.String } - + // Get description description := "" if entry.Description.Valid { @@ -177,9 +177,8 @@ func convertToTimesheetEntries(entries []timesheetEntryData, loc *time.Location) func formatDuration(seconds int64) string { // Round to nearest minute - totalMinutes := (seconds + 30) / 60 // Add 30 seconds for rounding + totalMinutes := (seconds + 30) / 60 // Add 30 seconds for rounding hours := totalMinutes / 60 minutes := totalMinutes % 60 return fmt.Sprintf("%d:%02d", hours, minutes) } - diff --git a/internal/reports/timesheet_test.go b/internal/reports/timesheet_test.go index 8c0ac52..ed35c86 100644 --- a/internal/reports/timesheet_test.go +++ b/internal/reports/timesheet_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "punchcard/internal/queries" - "punchcard/templates" + "git.tjp.lol/punchcard/internal/queries" + "git.tjp.lol/punchcard/templates" ) func TestGenerateTimesheetData(t *testing.T) { @@ -217,7 +217,7 @@ func TestConvertToTimesheetEntries(t *testing.T) { entries: []timesheetEntryData{ { TimeEntryID: 2, - StartTime: mustParseTime("2025-07-10T18:00:00Z"), // 6:00 PM UTC + StartTime: mustParseTime("2025-07-10T18:00:00Z"), // 6:00 PM UTC EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T19:00:00Z"), Valid: true}, // 7:00 PM UTC Description: sql.NullString{String: "Meeting", Valid: true}, DurationSeconds: 3600, // 1 hour @@ -373,13 +373,13 @@ func TestTimesheetTypstTemplateCompilation(t *testing.T) { }` dataFile := filepath.Join(tempDir, "data.json") - if err := os.WriteFile(dataFile, []byte(testData), 0644); err != nil { + if err := os.WriteFile(dataFile, []byte(testData), 0o644); err != nil { t.Fatalf("Failed to write test data file: %v", err) } // Write Typst template to temp directory typstFile := filepath.Join(tempDir, "timesheet.typ") - if err := os.WriteFile(typstFile, []byte(templates.TimesheetTemplate), 0644); err != nil { + if err := os.WriteFile(typstFile, []byte(templates.TimesheetTemplate), 0o644); err != nil { t.Fatalf("Failed to write Typst template: %v", err) } @@ -502,12 +502,12 @@ func TestGenerateDefaultTimesheetFilename(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := GenerateDefaultTimesheetFilename(tt.clientName, tt.projectName, dateRange) - + // Check that the filename starts with the expected pattern if len(result) < len(tt.want) || result[:len(tt.want)] != tt.want { t.Errorf("GenerateDefaultTimesheetFilename() prefix = %s, want prefix %s", result, tt.want) } - + // Check that it ends with .pdf if result[len(result)-4:] != ".pdf" { t.Errorf("GenerateDefaultTimesheetFilename() should end with .pdf, got %s", result) @@ -538,4 +538,5 @@ func abs(x float64) float64 { return -x } return x -}
\ No newline at end of file +} + diff --git a/internal/reports/unified.go b/internal/reports/unified.go index a6eb8c4..07507ae 100644 --- a/internal/reports/unified.go +++ b/internal/reports/unified.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) type UnifiedReportData struct { @@ -80,4 +80,5 @@ func GenerateUnifiedReportData( InvoiceData: invoiceData, TimesheetData: timesheetData, }, nil -}
\ No newline at end of file +} + diff --git a/internal/reports/unified_test.go b/internal/reports/unified_test.go index 64d0b3f..cf18350 100644 --- a/internal/reports/unified_test.go +++ b/internal/reports/unified_test.go @@ -5,10 +5,9 @@ import ( "testing" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) - func TestGenerateUnifiedReportData(t *testing.T) { tests := []struct { name string @@ -29,30 +28,30 @@ func TestGenerateUnifiedReportData(t *testing.T) { name: "client entries with UTC timezone", entries: []queries.GetInvoiceDataByClientRow{ { - TimeEntryID: 1, - StartTime: mustParseTime("2025-07-10T14:55:00Z"), - EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true}, - Description: sql.NullString{String: "GL closing", Valid: true}, - ClientID: 1, - ClientName: "Test Client", - ProjectID: sql.NullInt64{Int64: 1, Valid: true}, - ProjectName: sql.NullString{String: "Test Project", Valid: true}, - DurationSeconds: 11400, // 3:10 + TimeEntryID: 1, + StartTime: mustParseTime("2025-07-10T14:55:00Z"), + EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true}, + Description: sql.NullString{String: "GL closing", Valid: true}, + ClientID: 1, + ClientName: "Test Client", + ProjectID: sql.NullInt64{Int64: 1, Valid: true}, + ProjectName: sql.NullString{String: "Test Project", Valid: true}, + DurationSeconds: 11400, // 3:10 EntryBillableRate: sql.NullInt64{Int64: 150, Valid: true}, ClientBillableRate: sql.NullInt64{Int64: 150, Valid: true}, ProjectBillableRate: sql.NullInt64{Int64: 150, Valid: true}, RateSource: "entry", }, { - TimeEntryID: 2, - StartTime: mustParseTime("2025-07-10T18:42:00Z"), - EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T20:04:00Z"), Valid: true}, - Description: sql.NullString{String: "GL closing", Valid: true}, - ClientID: 1, - ClientName: "Test Client", - ProjectID: sql.NullInt64{Int64: 1, Valid: true}, - ProjectName: sql.NullString{String: "Test Project", Valid: true}, - DurationSeconds: 4920, // 1:22 + TimeEntryID: 2, + StartTime: mustParseTime("2025-07-10T18:42:00Z"), + EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T20:04:00Z"), Valid: true}, + Description: sql.NullString{String: "GL closing", Valid: true}, + ClientID: 1, + ClientName: "Test Client", + ProjectID: sql.NullInt64{Int64: 1, Valid: true}, + ProjectName: sql.NullString{String: "Test Project", Valid: true}, + DurationSeconds: 4920, // 1:22 EntryBillableRate: sql.NullInt64{Int64: 150, Valid: true}, ClientBillableRate: sql.NullInt64{Int64: 150, Valid: true}, ProjectBillableRate: sql.NullInt64{Int64: 150, Valid: true}, @@ -66,13 +65,13 @@ func TestGenerateUnifiedReportData(t *testing.T) { Label: "Software Development", Email: "travis@example.com", }, - invoiceNumber: 123, + invoiceNumber: 123, dateRange: DateRange{ Start: mustParseTime("2025-07-01T00:00:00Z"), End: mustParseTime("2025-07-31T23:59:59Z"), }, timezone: time.UTC, - wantEntries: 1, // Both entries have same rate so grouped together + wantEntries: 1, // Both entries have same rate so grouped together wantHours: 4.5333, // 16320 seconds / 3600 wantTotalAmount: 6.80, // 4.5333 * 1.50 }, @@ -80,14 +79,14 @@ func TestGenerateUnifiedReportData(t *testing.T) { name: "project entries with local timezone", entries: []queries.GetInvoiceDataByProjectRow{ { - TimeEntryID: 3, - StartTime: mustParseTime("2025-07-11T13:55:00Z"), - EndTime: sql.NullTime{Time: mustParseTime("2025-07-11T18:35:00Z"), Valid: true}, - Description: sql.NullString{String: "Development work", Valid: true}, - ClientID: 1, - ClientName: "Test Client", - ProjectID: 1, - ProjectName: "Test Project", + TimeEntryID: 3, + StartTime: mustParseTime("2025-07-11T13:55:00Z"), + EndTime: sql.NullTime{Time: mustParseTime("2025-07-11T18:35:00Z"), Valid: true}, + Description: sql.NullString{String: "Development work", Valid: true}, + ClientID: 1, + ClientName: "Test Client", + ProjectID: 1, + ProjectName: "Test Project", DurationSeconds: 16800, // 4:40 EntryBillableRate: sql.NullInt64{Int64: 125, Valid: true}, ClientBillableRate: sql.NullInt64{Int64: 125, Valid: true}, @@ -117,15 +116,15 @@ func TestGenerateUnifiedReportData(t *testing.T) { name: "entries with different timezone", entries: []queries.GetInvoiceDataByClientRow{ { - TimeEntryID: 4, - StartTime: mustParseTime("2025-07-15T00:09:00Z"), - EndTime: sql.NullTime{Time: mustParseTime("2025-07-15T00:13:00Z"), Valid: true}, - Description: sql.NullString{String: "Quick fix", Valid: true}, - ClientID: 1, - ClientName: "Test Client", - ProjectID: sql.NullInt64{Int64: 1, Valid: true}, - ProjectName: sql.NullString{String: "Test Project", Valid: true}, - DurationSeconds: 240, // 4 minutes + TimeEntryID: 4, + StartTime: mustParseTime("2025-07-15T00:09:00Z"), + EndTime: sql.NullTime{Time: mustParseTime("2025-07-15T00:13:00Z"), Valid: true}, + Description: sql.NullString{String: "Quick fix", Valid: true}, + ClientID: 1, + ClientName: "Test Client", + ProjectID: sql.NullInt64{Int64: 1, Valid: true}, + ProjectName: sql.NullString{String: "Test Project", Valid: true}, + DurationSeconds: 240, // 4 minutes EntryBillableRate: sql.NullInt64{Int64: 200, Valid: true}, ClientBillableRate: sql.NullInt64{Int64: 200, Valid: true}, ProjectBillableRate: sql.NullInt64{Int64: 200, Valid: true}, @@ -278,14 +277,14 @@ func TestUnifiedReportDataConsistency(t *testing.T) { // Test that unified report produces consistent data between invoice and timesheet components entries := []queries.GetInvoiceDataByClientRow{ { - TimeEntryID: 1, - StartTime: mustParseTime("2025-07-10T14:55:00Z"), - EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true}, - Description: sql.NullString{String: "Development work", Valid: true}, - ClientID: 1, - ClientName: "Test Client", - ProjectID: sql.NullInt64{Int64: 1, Valid: true}, - ProjectName: sql.NullString{String: "Test Project", Valid: true}, + TimeEntryID: 1, + StartTime: mustParseTime("2025-07-10T14:55:00Z"), + EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true}, + Description: sql.NullString{String: "Development work", Valid: true}, + ClientID: 1, + ClientName: "Test Client", + ProjectID: sql.NullInt64{Int64: 1, Valid: true}, + ProjectName: sql.NullString{String: "Test Project", Valid: true}, DurationSeconds: 11400, // 3:10 EntryBillableRate: sql.NullInt64{Int64: 150, Valid: true}, ClientBillableRate: sql.NullInt64{Int64: 150, Valid: true}, @@ -293,14 +292,14 @@ func TestUnifiedReportDataConsistency(t *testing.T) { RateSource: "entry", }, { - TimeEntryID: 2, - StartTime: mustParseTime("2025-07-10T18:42:00Z"), - EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T20:04:00Z"), Valid: true}, - Description: sql.NullString{String: "Code review", Valid: true}, - ClientID: 1, - ClientName: "Test Client", - ProjectID: sql.NullInt64{Int64: 1, Valid: true}, - ProjectName: sql.NullString{String: "Test Project", Valid: true}, + TimeEntryID: 2, + StartTime: mustParseTime("2025-07-10T18:42:00Z"), + EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T20:04:00Z"), Valid: true}, + Description: sql.NullString{String: "Code review", Valid: true}, + ClientID: 1, + ClientName: "Test Client", + ProjectID: sql.NullInt64{Int64: 1, Valid: true}, + ProjectName: sql.NullString{String: "Test Project", Valid: true}, DurationSeconds: 4920, // 1:22 EntryBillableRate: sql.NullInt64{Int64: 150, Valid: true}, ClientBillableRate: sql.NullInt64{Int64: 150, Valid: true}, @@ -330,7 +329,6 @@ func TestUnifiedReportDataConsistency(t *testing.T) { dateRange, time.UTC, ) - if err != nil { t.Fatalf("GenerateUnifiedReportData() error = %v", err) } @@ -385,15 +383,15 @@ func TestUnifiedReportEntryTypeConversion(t *testing.T) { name: "client entries conversion", entries: []queries.GetInvoiceDataByClientRow{ { - TimeEntryID: 1, - StartTime: mustParseTime("2025-07-10T14:55:00Z"), - EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true}, - Description: sql.NullString{String: "Work", Valid: true}, - ClientID: 1, - ClientName: "Test Client", - ProjectID: sql.NullInt64{Int64: 1, Valid: true}, - ProjectName: sql.NullString{String: "Test Project", Valid: true}, - DurationSeconds: 11400, + TimeEntryID: 1, + StartTime: mustParseTime("2025-07-10T14:55:00Z"), + EndTime: sql.NullTime{Time: mustParseTime("2025-07-10T18:05:00Z"), Valid: true}, + Description: sql.NullString{String: "Work", Valid: true}, + ClientID: 1, + ClientName: "Test Client", + ProjectID: sql.NullInt64{Int64: 1, Valid: true}, + ProjectName: sql.NullString{String: "Test Project", Valid: true}, + DurationSeconds: 11400, EntryBillableRate: sql.NullInt64{Int64: 150, Valid: true}, ClientBillableRate: sql.NullInt64{Int64: 150, Valid: true}, ProjectBillableRate: sql.NullInt64{Int64: 150, Valid: true}, @@ -406,14 +404,14 @@ func TestUnifiedReportEntryTypeConversion(t *testing.T) { name: "project entries conversion", entries: []queries.GetInvoiceDataByProjectRow{ { - TimeEntryID: 2, - StartTime: mustParseTime("2025-07-11T13:55:00Z"), - EndTime: sql.NullTime{Time: mustParseTime("2025-07-11T18:35:00Z"), Valid: true}, - Description: sql.NullString{String: "Work", Valid: true}, - ClientID: 1, - ClientName: "Test Client", - ProjectID: 1, - ProjectName: "Test Project", + TimeEntryID: 2, + StartTime: mustParseTime("2025-07-11T13:55:00Z"), + EndTime: sql.NullTime{Time: mustParseTime("2025-07-11T18:35:00Z"), Valid: true}, + Description: sql.NullString{String: "Work", Valid: true}, + ClientID: 1, + ClientName: "Test Client", + ProjectID: 1, + ProjectName: "Test Project", DurationSeconds: 16800, EntryBillableRate: sql.NullInt64{Int64: 125, Valid: true}, ClientBillableRate: sql.NullInt64{Int64: 125, Valid: true}, @@ -526,7 +524,6 @@ func TestUnifiedReportEmptyEntries(t *testing.T) { dateRange, time.UTC, ) - if err != nil { t.Errorf("GenerateUnifiedReportData() error = %v", err) return @@ -559,4 +556,5 @@ func TestUnifiedReportEmptyEntries(t *testing.T) { } }) } -}
\ No newline at end of file +} + diff --git a/internal/tui/app.go b/internal/tui/app.go index 4b31c38..f434034 100644 --- a/internal/tui/app.go +++ b/internal/tui/app.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss/v2" diff --git a/internal/tui/commands.go b/internal/tui/commands.go index 9a836c5..657b3f5 100644 --- a/internal/tui/commands.go +++ b/internal/tui/commands.go @@ -6,9 +6,9 @@ import ( "strings" "time" - "punchcard/internal/actions" - "punchcard/internal/queries" - "punchcard/internal/reports" + "git.tjp.lol/punchcard/internal/actions" + "git.tjp.lol/punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/reports" tea "github.com/charmbracelet/bubbletea" ) diff --git a/internal/tui/form.go b/internal/tui/form.go index d70fb8b..09db989 100644 --- a/internal/tui/form.go +++ b/internal/tui/form.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "punchcard/internal/reports" + "git.tjp.lol/punchcard/internal/reports" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" @@ -104,8 +104,8 @@ func newDateRangeField(label string) FormField { func newReportTypeField(label string) FormField { f := FormField{ - Model: textinput.New(), - label: label, + Model: textinput.New(), + label: label, suggestions: suggestReportType, } f.Validate = func(s string) error { diff --git a/internal/tui/history_box.go b/internal/tui/history_box.go index 01ea59b..b4cccf2 100644 --- a/internal/tui/history_box.go +++ b/internal/tui/history_box.go @@ -6,7 +6,7 @@ import ( "strconv" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" "github.com/charmbracelet/bubbles/viewport" "github.com/charmbracelet/lipgloss/v2" diff --git a/internal/tui/modal.go b/internal/tui/modal.go index 7660243..77b3fa9 100644 --- a/internal/tui/modal.go +++ b/internal/tui/modal.go @@ -7,9 +7,9 @@ import ( "strconv" "time" - "punchcard/internal/actions" - "punchcard/internal/queries" - "punchcard/internal/reports" + "git.tjp.lol/punchcard/internal/actions" + "git.tjp.lol/punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/reports" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss/v2" diff --git a/internal/tui/projects_box.go b/internal/tui/projects_box.go index 9c62d4d..3bf44b5 100644 --- a/internal/tui/projects_box.go +++ b/internal/tui/projects_box.go @@ -4,7 +4,7 @@ import ( "fmt" "strconv" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" "github.com/charmbracelet/lipgloss/v2" ) diff --git a/internal/tui/shared.go b/internal/tui/shared.go index 7ef7772..c79560a 100644 --- a/internal/tui/shared.go +++ b/internal/tui/shared.go @@ -8,7 +8,7 @@ import ( "slices" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" "github.com/charmbracelet/lipgloss/v2" ) diff --git a/internal/tui/timer_box.go b/internal/tui/timer_box.go index 09a42c7..891004a 100644 --- a/internal/tui/timer_box.go +++ b/internal/tui/timer_box.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "punchcard/internal/queries" + "git.tjp.lol/punchcard/internal/queries" ) // TimerInfo holds information about the current or most recent timer state |