Add PDF reports, file uploads, admin auth, and comprehensive tests

Features:
- PDF service for generating task reports with ReportLab-style formatting
- Storage service for file uploads (local and S3-compatible)
- Admin authentication middleware with JWT support
- Admin user model and repository

Infrastructure:
- Updated Docker configuration for admin panel builds
- Email service enhancements for task notifications
- Updated router with admin and file upload routes
- Environment configuration updates

Tests:
- Unit tests for handlers (auth, residence, task)
- Unit tests for models (user, residence, task)
- Unit tests for repositories (user, residence, task)
- Unit tests for services (residence, task)
- Integration test setup
- Test utilities for mocking database and services

Database:
- Admin user seed data
- Updated test data seeds

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-27 23:36:20 -06:00
parent 2817deee3c
commit 469f21a833
50 changed files with 6795 additions and 582 deletions

View File

@@ -28,6 +28,7 @@ type ContractorResponse struct {
ID uint `json:"id"`
ResidenceID uint `json:"residence_id"`
CreatedByID uint `json:"created_by_id"`
AddedBy uint `json:"added_by"` // Alias for created_by_id (KMM compatibility)
CreatedBy *ContractorUserResponse `json:"created_by,omitempty"`
Name string `json:"name"`
Company string `json:"company"`
@@ -48,13 +49,7 @@ type ContractorResponse struct {
UpdatedAt time.Time `json:"updated_at"`
}
// ContractorListResponse represents a paginated list of contractors
type ContractorListResponse struct {
Count int `json:"count"`
Next *string `json:"next"`
Previous *string `json:"previous"`
Results []ContractorResponse `json:"results"`
}
// Note: Pagination removed - list endpoints now return arrays directly
// ToggleFavoriteResponse represents the response after toggling favorite
type ToggleFavoriteResponse struct {
@@ -94,6 +89,7 @@ func NewContractorResponse(c *models.Contractor) ContractorResponse {
ID: c.ID,
ResidenceID: c.ResidenceID,
CreatedByID: c.CreatedByID,
AddedBy: c.CreatedByID, // Alias for KMM compatibility
Name: c.Name,
Company: c.Company,
Phone: c.Phone,
@@ -124,16 +120,11 @@ func NewContractorResponse(c *models.Contractor) ContractorResponse {
return resp
}
// NewContractorListResponse creates a ContractorListResponse from a slice of contractors
func NewContractorListResponse(contractors []models.Contractor) ContractorListResponse {
// NewContractorListResponse creates a list of contractor responses
func NewContractorListResponse(contractors []models.Contractor) []ContractorResponse {
results := make([]ContractorResponse, len(contractors))
for i, c := range contractors {
results[i] = NewContractorResponse(&c)
}
return ContractorListResponse{
Count: len(contractors),
Next: nil,
Previous: nil,
Results: results,
}
return results
}