diff --git a/docu/README.md b/docu/README.md new file mode 100644 index 0000000..f242092 --- /dev/null +++ b/docu/README.md @@ -0,0 +1,22 @@ +# Time Tracking and Management System Documentation + +# Time Tracking and Management System Documentation + +This document provides an overview of the Time Tracking and Management System. For detailed information, please refer to the following documents: + +- [General Overview](general_overview.md) +- [Backend Architecture](backend_architecture.md) +- [Frontend Architecture](frontend_architecture.md) +- [Database Schema](database_schema.md) +- [Deployment and DevOps](deployment_devops.md) +- [Security and Privacy](security_privacy.md) +- [Extensibility and Integrations](extensibility_integrations.md) +- [LLM Guidance](llm_guidance.md) +- [Roadmap](Roadmap.md) + +## Code Examples +- [GORM Entities](code_examples/gorm_entities.go) +- [FPGO Repository](code_examples/fpgo_repository.go) +- [FPGO Service](code_examples/fpgo_service.go) +- [API Handler](code_examples/api_handler.go) +- [React Component](code_examples/react_component.tsx) diff --git a/docu/Roadmap.md b/docu/Roadmap.md new file mode 100644 index 0000000..b9a8289 --- /dev/null +++ b/docu/Roadmap.md @@ -0,0 +1,153 @@ +# Time Tracking System Roadmap + +This document outlines the roadmap for the development of the Time Tracking and Management System. + +## Project Phases + +### Phase 1: MVP (Minimum Viable Product) + +**Goal:** A functional system with core time tracking capabilities, user authentication, and basic project/activity management. + +**Sub-phases:** + +1. **1.1 Core Backend Setup:** + - Project initialization (Go, Next.js). + - Database setup (PostgreSQL, GORM). + - Basic API structure (Gin). + - Authentication setup (JWT). + +2. **1.2 User Management:** + - User registration, login, and password management. + - Implementation of basic user roles (User, Admin). + +3. **1.3 Company Management:** + - Company (tenant) creation and basic management (Admin only). + +4. **1.4 Project and Activity Management:** + - Project creation/editing/deletion (name, description, company association). + - Activity creation/editing/deletion (name, billing rate, company association). + +5. **1.5 Time Entry Core:** + - Time entry creation (start/stop timer functionality). + - Manual time entry input. + +6. **1.6 Basic Frontend:** + - Basic UI for time entry. + - Basic UI for project/activity listing. + +7. **1.7 Basic Listing and Filtering:** + - Time entry listing. + - Basic filtering (by user, project, date range). + +8. **1.8 Simple Dashboard:** + - Display of recent time entries. + +### Phase 2: Version 1 + +**Goal:** Full implementation of the features described in the initial version of the specification. + +**Sub-phases:** + +1. **2.1 Expanded Roles:** + - Implement Manager, Auditor, and Company roles. + +2. **2.2 Client and Employee Management:** + - Client creation/editing/deletion. + - Employee creation/editing/deletion (with hourly rates). + +3. **2.3 Detailed Time Entry:** + - Add fields for location, purpose, description, and billable percentage to time entries. + +4. **2.4 Reporting Engine:** + - Implement report generation (by project, employee, client, time period). + - PDF export for reports. + +5. **2.5 Dashboard Enhancements:** + - Add graphical dashboards with summaries and visualizations. + +6. **2.6 Tracker Improvement:** + - Pre-populate tracker with data from the last booking. + +7. **2.7 Multi-Tenancy Refinement:** + - Ensure complete data isolation between tenants. + +8. **2.8 RBAC Implementation:** + - Implement fine-grained Role-Based Access Control. + +9. **2.9 API Documentation:** + - Create comprehensive API documentation using Swagger. + +10. **2.10 CI/CD Setup:** + - Implement a basic CI/CD pipeline for automated testing and deployment. + +### Phase 3: Version 2 + +**Goal:** Implement the "Future Enhancements" outlined in the specification. + +**Sub-phases:** + +1. **3.1 Sprint and Task Management:** + - Add sprint and task entities to the data model. + - Implement sprint and task creation/editing/deletion. + +2. **3.2 Kanban Boards:** + - Develop Kanban board UI components. + - Implement drag-and-drop functionality for task management. + +3. **3.3 Task-Time Entry Linking:** + - Allow users to select a specific task when creating a time entry. + +### Phase 4: Future Considerations + +**Goal:** Expand the system with additional features and integrations. + +**Sub-phases (Not exhaustive, can be further broken down):** + +1. **4.1 Integrations:** + - Billing systems integration. + - Calendar integrations (Google Calendar, Outlook). + - Project management tool integrations (Jira, Trello). + - Webhook implementation. + - SSO implementation. + +2. **4.2 Extensibility:** + - Develop a plugin system. + - Implement custom fields. + +3. **4.3 Mobile App:** + - Develop responsive web design. + - Explore native mobile app development. + +4. **4.4 Internationalization:** + - Implement multilingual support. + - Add localized date/time formats. + - Add currency conversion. + +5. **4.5 Billing and Invoicing:** + - Implement invoice generation. + - Add billing data export functionality. + - Implement reminders for unrecorded time. + +6. **4.6 Notification System:** + - Implement email and in-app notifications. + - Add reminders and approval requests. + +7. **4.7 Analytics:** + - Implement advanced analytics and reporting. + - Add trend analysis and forecasting. + - Implement resource utilization tracking. + - Add export options for BI tools. + +8. **4.8 Offline Functionality** + - Implement PWA features. + - Add local storage and synchronization. + +9. **4.9 Enhanced Security:** + - Implement more detailed audit trails. + +10. **4.10 Error Handling:** + - Implement a unified error handling and validation strategy. + +11. **4.11 Performance:** + - Implement Caching (Redis). + - Optimize database with indexing. diff --git a/docu/backend_architecture.md b/docu/backend_architecture.md new file mode 100644 index 0000000..0ebd94a --- /dev/null +++ b/docu/backend_architecture.md @@ -0,0 +1,144 @@ +# Backend Architecture (Go) + +**Note:** This document describes a *conceptual* architecture and is not a final, binding requirement. + +The backend is written in Go and follows the principles of **Clean Architecture** and **Domain-Driven Design (DDD)**. + +## Project Structure +``` +timetracker-backend/ +├── cmd/ # Einstiegspunkte +│ ├── api/ +│ │ └── main.go +│ └── worker/ +│ └── main.go +├── internal/ # Interner Code +│ ├── domain/ # Domain Layer (DDD) +│ │ ├── entities/ # Domain Entitäten +│ │ │ ├── user.go +│ │ │ ├── company.go +│ │ │ ├── project.go +│ │ │ ├── timeentry.go +│ │ │ ├── customer.go +│ │ │ ├── activity.go +│ │ │ ├── sprint.go +│ │ │ ├── task.go +│ │ │ └── kanban.go +│ │ ├── repositories/ # Repository Interfaces +│ │ │ ├── user_repository.go +│ │ │ ├── company_repository.go +│ │ │ ├── project_repository.go +│ │ │ ├── timeentry_repository.go +│ │ │ └── ... +│ │ ├── services/ # Service Interfaces und Implementierungen +│ │ │ ├── auth_service.go +│ │ │ ├── user_service.go +│ │ │ ├── timetracking_service.go +│ │ │ ├── reporting_service.go +│ │ │ └── ... +│ │ └── valueobjects/ # Value Objects +│ │ ├── money.go +│ │ ├── duration.go +│ │ └── ... +│ ├── application/ # Anwendungsfälle +│ │ ├── auth/ # Authentication Use Cases +│ │ │ ├── login.go +│ │ │ ├── register.go +│ │ │ └── ... +│ │ ├── timetracking/ # Zeiterfassung Use Cases +│ │ │ ├── create_time_entry.go +│ │ │ ├── update_time_entry.go +│ │ │ ├── list_time_entries.go +│ │ │ └── ... +│ │ ├── projects/ # Projektverwaltung Use Cases +│ │ │ └── ... +│ │ ├── customers/ # Kundenverwaltung Use Cases +│ │ │ └── ... +│ │ └── reporting/ # Berichtswesen Use Cases +│ │ └── ... +│ ├── infrastructure/ # Infrastruktur +│ │ ├── persistence/ # Datenbankzugriff +│ │ │ ├── postgres/ # PostgreSQL Implementierung +│ │ │ │ ├── connection.go +│ │ │ │ ├── user_repository.go +│ │ │ │ ├── company_repository.go +│ │ │ │ └── ... +│ │ │ └── migrations/ +│ │ ├── security/ # Sicherheitsimplementierungen +│ │ │ ├── jwt.go +│ │ │ ├── password.go +│ │ │ └── rbac.go +│ │ ├── messaging/ # Messaging-Implementierungen +│ │ │ ├── email.go +│ │ │ └── notification.go +│ │ └── external/ # Externe Dienste +│ │ ├── calendar/ +│ │ ├── pdf/ +│ │ └── webhook/ +│ └── interfaces/ # Interfaces +│ ├── http/ # HTTP-API +│ │ ├── handlers/ # API Handler +│ │ │ ├── auth_handler.go +│ │ │ ├── user_handler.go +│ │ │ ├── time_entry_handler.go +│ │ │ └── ... +│ │ ├── middleware/ # Middleware +│ │ │ ├── auth_middleware.go +│ │ │ ├── tenant_middleware.go +│ │ │ ├── logging_middleware.go +│ │ │ └── ... +│ │ ├── dto/ # Data Transfer Objects +│ │ │ ├── auth_dto.go +│ │ │ ├── user_dto.go +│ │ │ ├── time_entry_dto.go +│ │ │ └── ... +│ │ └── routes/ # API Routes +│ │ └── router.go +│ └── grpc/ # gRPC-API (Optional für Microservice-Kommunikation) +├── pkg/ # Öffentliche Pakete +│ ├── functional/ # FPGO Utilities +│ │ ├── option.go +│ │ ├── result.go +│ │ └── ... +│ ├── validator/ # Validierung +│ │ └── validator.go +│ ├── logger/ # Logging +│ │ └── logger.go +│ ├── errors/ # Fehlerbehandlung +│ │ └── errors.go +│ └── utils/ # Allgemeine Utilities +│ ├── date.go +│ ├── crypto.go +│ └── ... +├── api/ # API Definitionen +│ └── swagger/ # Swagger Dokumentation +│ └── swagger.yaml +├── deployments/ # Deployment-Konfigurationen +│ ├── docker/ +│ │ ├── Dockerfile +│ │ └── docker-compose.yml +│ └── kubernetes/ +│ ├── api.yaml +│ └── worker.yaml +├── test/ # Tests +│ ├── unit/ +│ ├── integration/ +│ └── e2e/ +├── scripts/ # Skripte +│ ├── migration.sh +│ ├── seed.sh +│ └── ... +├── docu/ # Projektdokumentation +│ ├── general_overview.md +│ ├── backend_architecture.md +│ ├── frontend_architecture.md +│ ├── database_schema.md +│ ├── security_privacy.md +│ ├── extensibility_integrations.md +│ ├── deployment_devops.md +│ ├── llm_guidance.md +│ └── README.md +├── go.mod +├── go.sum +└── README.md +``` diff --git a/docu/code_examples/api_handler.go b/docu/code_examples/api_handler.go new file mode 100644 index 0000000..b83fac6 --- /dev/null +++ b/docu/code_examples/api_handler.go @@ -0,0 +1,101 @@ +// interfaces/http/handlers/time_entry_handler.go +package main + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/username/timetracker/internal/application/timetracking" + "github.com/username/timetracker/internal/interfaces/http/dto" + "github.com/username/timetracker/internal/interfaces/http/middleware" +) + +// TimeEntryHandler behandelt HTTP-Anfragen für Zeitbuchungen +type TimeEntryHandler struct { + createTimeEntryUseCase *timetracking.CreateTimeEntryUseCase + updateTimeEntryUseCase *timetracking.UpdateTimeEntryUseCase + listTimeEntriesUseCase *timetracking.ListTimeEntriesUseCase + deleteTimeEntryUseCase *timetracking.DeleteTimeEntryUseCase +} + +// NewTimeEntryHandler erstellt einen neuen TimeEntryHandler +func NewTimeEntryHandler( + createTimeEntryUseCase *timetracking.CreateTimeEntryUseCase, + updateTimeEntryUseCase *timetracking.UpdateTimeEntryUseCase, + listTimeEntriesUseCase *timetracking.ListTimeEntriesUseCase, + deleteTimeEntryUseCase *timetracking.DeleteTimeEntryUseCase, +) *TimeEntryHandler { + return &TimeEntryHandler{ + createTimeEntryUseCase: createTimeEntryUseCase, + updateTimeEntryUseCase: updateTimeEntryUseCase, + listTimeEntriesUseCase: listTimeEntriesUseCase, + deleteTimeEntryUseCase: deleteTimeEntryUseCase, + } +} + +// RegisterRoutes registriert die Routen am Router +func (h *TimeEntryHandler) RegisterRoutes(router *gin.RouterGroup) { + timeEntries := router.Group("/time-entries") + { + timeEntries.GET("", h.ListTimeEntries) + timeEntries.POST("", h.CreateTimeEntry) + timeEntries.GET("/:id", h.GetTimeEntry) + timeEntries.PUT("/:id", h.UpdateTimeEntry) + timeEntries.DELETE("/:id", h.DeleteTimeEntry) + } +} + +// CreateTimeEntry behandelt die Erstellung einer neuen Zeitbuchung +func (h *TimeEntryHandler) CreateTimeEntry(c *gin.Context) { + var req dto.CreateTimeEntryRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Tenant-ID aus dem Kontext extrahieren + companyID, exists := middleware.GetCompanyID(c) + if !exists { + c.JSON(http.StatusBadRequest, gin.H{"error": "Company ID not found"}) + return + } + + // Benutzer-ID aus dem Kontext oder Request + var userID uuid.UUID + if req.UserID != nil { + userID = *req.UserID + } else { + currentUserID, exists := middleware.GetUserID(c) + if !exists { + c.JSON(http.StatusBadRequest, gin.H{"error": "User ID not found"}) + return + } + userID = currentUserID + } + + // Command erstellen + cmd := timetracking.CreateTimeEntryCommand{ + UserID: userID, + ProjectID: req.ProjectID, + ActivityID: req.ActivityID, + TaskID: req.TaskID, + StartTime: req.StartTime, + EndTime: req.EndTime, + Description: req.Description, + BillablePercentage: req.BillablePercentage, + } + + // UseCase ausführen + result := h.createTimeEntryUseCase.Execute(c.Request.Context(), companyID, cmd) + if result.IsFailure() { + c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error().Error()}) + return + } + + // TimeEntry in Response-DTO umwandeln + timeEntry := result.Value() + response := dto.MapTimeEntryToDTO(*timeEntry) + + c.JSON(http.StatusCreated, response) +} diff --git a/docu/code_examples/fpgo_repository.go b/docu/code_examples/fpgo_repository.go new file mode 100644 index 0000000..e8c693b --- /dev/null +++ b/docu/code_examples/fpgo_repository.go @@ -0,0 +1,52 @@ +// domain/repositories/time_entry_repository.go +package repositories + +import ( + "context" + "time" + + "github.com/google/uuid" + "github.com/username/timetracker/internal/domain/entities" + "github.com/username/timetracker/pkg/functional" +) + +// TimeEntryFilter enthält Filter für die Suche nach Zeitbuchungen +type TimeEntryFilter struct { + UserID *uuid.UUID + ProjectID *uuid.UUID + CustomerID *uuid.UUID + StartDate *time.Time + EndDate *time.Time + ActivityID *uuid.UUID + TaskID *uuid.UUID +} + +// TimeEntryRepository Interface für den Zugriff auf Zeitbuchungen +type TimeEntryRepository interface { + // FindByID sucht eine Zeitbuchung anhand ihrer ID + FindByID(ctx context.Context, companyID, id uuid.UUID) functional.Result[*entities.TimeEntry] + + // FindAll sucht alle Zeitbuchungen mit optionalen Filtern + FindAll(ctx context.Context, companyID uuid.UUID, filter TimeEntryFilter) functional.Result[[]entities.TimeEntry] + + // Create erstellt eine neue Zeitbuchung + Create(ctx context.Context, entry *entities.TimeEntry) functional.Result[*entities.TimeEntry] + + // Update aktualisiert eine bestehende Zeitbuchung + Update(ctx context.Context, entry *entities.TimeEntry) functional.Result[*entities.TimeEntry] + + // Delete löscht eine Zeitbuchung + Delete(ctx context.Context, companyID, id uuid.UUID) functional.Result[bool] + + // GetSummary berechnet eine Zusammenfassung der Zeitbuchungen + GetSummary(ctx context.Context, companyID uuid.UUID, filter TimeEntryFilter) functional.Result[TimeEntrySummary] +} + +// TimeEntrySummary enthält zusammengefasste Informationen über Zeitbuchungen +type TimeEntrySummary struct { + TotalDuration int + TotalBillableDuration int + TotalAmount float64 + TotalBillableAmount float64 + EntriesCount int +} diff --git a/docu/code_examples/fpgo_service.go b/docu/code_examples/fpgo_service.go new file mode 100644 index 0000000..f633bde --- /dev/null +++ b/docu/code_examples/fpgo_service.go @@ -0,0 +1,99 @@ +// application/timetracking/create_time_entry.go +package main + +import ( + "context" + "time" + + "github.com/google/uuid" + "github.com/username/timetracker/internal/domain/entities" + "github.com/username/timetracker/internal/domain/repositories" + "github.com/username/timetracker/pkg/functional" + "github.com/username/timetracker/pkg/validator" +) + +// CreateTimeEntryCommand enthält die Daten zum Erstellen einer Zeitbuchung +type CreateTimeEntryCommand struct { + UserID uuid.UUID + ProjectID uuid.UUID + ActivityID uuid.UUID + TaskID *uuid.UUID + StartTime time.Time + EndTime time.Time + Description string + BillablePercentage int +} + +// CreateTimeEntryUseCase repräsentiert den Anwendungsfall zum Erstellen einer Zeitbuchung +type CreateTimeEntryUseCase struct { + timeEntryRepo repositories.TimeEntryRepository + projectRepo repositories.ProjectRepository + activityRepo repositories.ActivityRepository + userRepo repositories.UserRepository +} + +// NewCreateTimeEntryUseCase erstellt eine neue Instanz des UseCase +func NewCreateTimeEntryUseCase( + timeEntryRepo repositories.TimeEntryRepository, + projectRepo repositories.ProjectRepository, + activityRepo repositories.ActivityRepository, + userRepo repositories.UserRepository, +) *CreateTimeEntryUseCase { + return &CreateTimeEntryUseCase{ + timeEntryRepo: timeEntryRepo, + projectRepo: projectRepo, + activityRepo: activityRepo, + userRepo: userRepo, + } +} + +// Execute führt den Anwendungsfall aus +func (uc *CreateTimeEntryUseCase) Execute(ctx context.Context, companyID uuid.UUID, cmd CreateTimeEntryCommand) functional.Result[*entities.TimeEntry] { + // Validierung + if err := validator.ValidateStruct(cmd); err != nil { + return functional.Failure[*entities.TimeEntry](err) + } + + // Überprüfen, ob Projekt existiert und zum gleichen Tenant gehört + projectResult := uc.projectRepo.FindByID(ctx, companyID, cmd.ProjectID) + if projectResult.IsFailure() { + return functional.Failure[*entities.TimeEntry](projectResult.Error()) + } + + // Überprüfen, ob Activity existiert und zum gleichen Tenant gehört + activityResult := uc.activityRepo.FindByID(ctx, companyID, cmd.ActivityID) + if activityResult.IsFailure() { + return functional.Failure[*entities.TimeEntry](activityResult.Error()) + } + activity := activityResult.Value() + + // Benutzer abrufen für den Stundensatz + userResult := uc.userRepo.FindByID(ctx, companyID, cmd.UserID) + if userResult.IsFailure() { + return functional.Failure[*entities.TimeEntry](userResult.Error()) + } + user := userResult.Value() + + // Berechnung der Dauer in Minuten + durationMinutes := int(cmd.EndTime.Sub(cmd.StartTime).Minutes()) + + // TimeEntry erstellen + timeEntry := &entities.TimeEntry{ + TenantEntity: entities.TenantEntity{ + CompanyID: companyID, + }, + UserID: cmd.UserID, + ProjectID: cmd.ProjectID, + ActivityID: cmd.ActivityID, + TaskID: cmd.TaskID, + StartTime: cmd.StartTime, + EndTime: cmd.EndTime, + DurationMinutes: durationMinutes, + Description: cmd.Description, + BillablePercentage: cmd.BillablePercentage, + BillingRate: activity.BillingRate, + } + + // Speichern der TimeEntry + return uc.timeEntryRepo.Create(ctx, timeEntry) +} diff --git a/docu/code_examples/gorm_entities.go b/docu/code_examples/gorm_entities.go new file mode 100644 index 0000000..0ae6192 --- /dev/null +++ b/docu/code_examples/gorm_entities.go @@ -0,0 +1,104 @@ +package entities + +import ( + "time" + + "github.com/google/uuid" + "gorm.io/gorm" +) + +// BaseEntity enthält gemeinsame Felder für alle Entitäten +type BaseEntity struct { + ID uuid.UUID `gorm:"type:uuid;primary_key"` + CreatedAt time.Time `gorm:"autoCreateTime"` + UpdatedAt time.Time `gorm:"autoUpdateTime"` + DeletedAt gorm.DeletedAt `gorm:"index"` +} + +// BeforeCreate setzt eine neue UUID vor dem Erstellen +func (base *BaseEntity) BeforeCreate(tx *gorm.DB) error { + if base.ID == uuid.Nil { + base.ID = uuid.New() + } + return nil +} + +// TenantEntity erweitert BaseEntity um Company-ID für Multi-Tenancy +type TenantEntity struct { + BaseEntity + CompanyID uuid.UUID `gorm:"type:uuid;index:idx_tenant"` +} + +// Role repräsentiert eine Benutzerrolle +type Role struct { + BaseEntity + Name string `gorm:"unique;not null"` + Description string + Permissions []Permission `gorm:"many2many:role_permissions;"` +} + +// Permission repräsentiert eine einzelne Berechtigung +type Permission struct { + BaseEntity + Resource string `gorm:"not null"` + Action string `gorm:"not null"` + UniqueID string `gorm:"uniqueIndex"` +} + +// User repräsentiert einen Benutzer im System +type User struct { + TenantEntity + Email string `gorm:"uniqueIndex;not null"` + FirstName string + LastName string + PasswordHash string `gorm:"not null"` + RoleID uuid.UUID `gorm:"type:uuid"` + Role Role `gorm:"foreignKey:RoleID"` + HourlyRate float64 + IsActive bool `gorm:"default:true"` +} + +// FullName gibt den vollständigen Namen des Benutzers zurück +func (u User) FullName() string { + return u.FirstName + " " + u.LastName +} + +// TimeEntry repräsentiert eine Zeitbuchung +type TimeEntry struct { + TenantEntity + UserID uuid.UUID `gorm:"type:uuid;index:idx_user"` + User User `gorm:"foreignKey:UserID"` + ProjectID uuid.UUID `gorm:"type:uuid;index:idx_project"` + Project Project `gorm:"foreignKey:ProjectID"` + ActivityID uuid.UUID `gorm:"type:uuid"` + Activity Activity `gorm:"foreignKey:ActivityID"` + TaskID *uuid.UUID `gorm:"type:uuid;null"` + Task *Task `gorm:"foreignKey:TaskID"` + StartTime time.Time `gorm:"not null;index:idx_time_range"` + EndTime time.Time `gorm:"not null;index:idx_time_range"` + DurationMinutes int `gorm:"not null"` + Description string + BillablePercentage int `gorm:"default:100"` + BillingRate float64 +} + +type Project struct { + TenantEntity + Name string +} + +type Activity struct { + TenantEntity + Name string +} + +type Task struct { + TenantEntity + Name string +} + +// CalculateBillableAmount berechnet den abrechenbaren Betrag +func (t TimeEntry) CalculateBillableAmount() float64 { + hours := float64(t.DurationMinutes) / 60.0 + return hours * t.BillingRate * (float64(t.BillablePercentage) / 100.0) +} diff --git a/docu/code_examples/react_component.tsx b/docu/code_examples/react_component.tsx new file mode 100644 index 0000000..905f65f --- /dev/null +++ b/docu/code_examples/react_component.tsx @@ -0,0 +1,136 @@ +// presentation/components/timeTracker/Timer/Timer.tsx +import React, { useState, useEffect } from 'react'; +import { useTimeTracking } from '../../../hooks/useTimeTracking'; +import { pipe, Option, fromNullable } from '../../../../utils/fp/option'; +import { Button } from '../../common/Button'; +import { formatDuration } from '../../../../utils/date/dateUtils'; + +interface TimerProps { + onComplete?: (duration: number) => void; +} + +export const Timer: React.FC = ({ onComplete }) => { + const [isRunning, setIsRunning] = useState(false); + const [startTime, setStartTime] = useState>(Option.none()); + const [elapsedTime, setElapsedTime] = useState(0); + const [selectedProject, setSelectedProject] = useState>(Option.none()); + const [selectedActivity, setSelectedActivity] = useState>(Option.none()); + + const { lastTimeEntry, projects, activities } = useTimeTracking(); + + // Beim ersten Rendering die letzte Zeitbuchung laden + useEffect(() => { + pipe( + fromNullable(lastTimeEntry), + Option.map(entry => { + setSelectedProject(Option.some(entry.projectId)); + setSelectedActivity(Option.some(entry.activityId)); + }) + ); + }, [lastTimeEntry]); + + // Timer-Logik + useEffect(() => { + let interval: NodeJS.Timeout | null = null; + + if (isRunning) { + interval = setInterval(() => { + const now = new Date(); + pipe( + startTime, + Option.map(start => { + const diff = now.getTime() - start.getTime(); + setElapsedTime(Math.floor(diff / 1000)); + }) + ); + }, 1000); + } else if (interval) { + clearInterval(interval); + } + + return () => { + if (interval) clearInterval(interval); + }; + }, [isRunning, startTime]); + + // Timer starten + const handleStart = () => { + setStartTime(Option.some(new Date())); + setIsRunning(true); + }; + + // Timer stoppen + const handleStop = () => { + setIsRunning(false); + + // Prüfen, ob Projekt und Aktivität ausgewählt wurden + const projectId = pipe( + selectedProject, + Option.getOrElse(() => '') + ); + + const activityId = pipe( + selectedActivity, + Option.getOrElse(() => '') + ); + + if (projectId && activityId && onComplete) { + onComplete(elapsedTime); + } + + // Timer zurücksetzen + setElapsedTime(0); + setStartTime(Option.none()); + }; + + return ( +
+
+ {formatDuration(elapsedTime)} +
+ +
+
+ + +
+ +
+ + +
+
+ +
+
+
+ ); +}; \ No newline at end of file diff --git a/docu/concept/files.md b/docu/concept/files.md deleted file mode 100644 index fdb2919..0000000 --- a/docu/concept/files.md +++ /dev/null @@ -1,103 +0,0 @@ -timetracker-frontend/ -├── public/ # Statische Dateien -│ ├── assets/ -│ │ ├── images/ -│ │ ├── icons/ -│ │ └── fonts/ -│ └── locales/ # Mehrsprachige Inhalte -│ ├── de/ -│ └── en/ -├── src/ -│ ├── app/ # Next.js 13+ App Router -│ │ ├── api/ # API-Routen (falls nötig) -│ │ ├── (auth)/ # Authentifizierungsseiten -│ │ │ ├── login/ -│ │ │ └── register/ -│ │ ├── dashboard/ # Dashboard-Seiten -│ │ │ ├── page.tsx -│ │ │ └── layout.tsx -│ │ ├── time-tracking/ # Zeiterfassungsseiten -│ │ │ ├── page.tsx -│ │ │ ├── [id]/ -│ │ │ └── components/ -│ │ ├── projects/ # Projektseiten -│ │ │ ├── page.tsx -│ │ │ └── [id]/ -│ │ ├── customers/ # Kundenseiten -│ │ │ ├── page.tsx -│ │ │ └── [id]/ -│ │ ├── reports/ # Berichtsseiten -│ │ │ ├── page.tsx -│ │ │ └── [type]/ -│ │ ├── admin/ # Administrationsseiten -│ │ │ ├── users/ -│ │ │ ├── companies/ -│ │ │ └── settings/ -│ │ ├── kanban/ # Kanban-Boards (Version 2) -│ │ │ ├── page.tsx -│ │ │ └── [id]/ -│ │ ├── layout.tsx -│ │ └── page.tsx -│ ├── components/ # Wiederverwendbare Komponenten -│ │ ├── common/ # Allgemeine Komponenten -│ │ │ ├── Button.tsx -│ │ │ ├── Card.tsx -│ │ │ ├── Input.tsx -│ │ │ └── ... -│ │ ├── layout/ # Layout-Komponenten -│ │ │ ├── Navbar.tsx -│ │ │ ├── Sidebar.tsx -│ │ │ ├── Footer.tsx -│ │ │ └── ... -│ │ ├── dashboard/ # Dashboard-Komponenten -│ │ │ ├── ActivityChart.tsx -│ │ │ ├── RecentEntries.tsx -│ │ │ └── ... -│ │ ├── timetracker/ # Zeiterfassungskomponenten -│ │ │ ├── Timer.tsx -│ │ │ ├── EntryForm.tsx -│ │ │ └── ... -│ │ ├── reports/ # Berichtskomponenten -│ │ │ ├── ReportFilter.tsx -│ │ │ ├── Chart.tsx -│ │ │ └── ... -│ │ └── kanban/ # Kanban-Komponenten (Version 2) -│ │ ├── Board.tsx -│ │ ├── Column.tsx -│ │ ├── Card.tsx -│ │ └── ... -│ ├── hooks/ # Custom React Hooks -│ │ ├── useAuth.ts -│ │ ├── useTimeTracking.ts -│ │ ├── useProjects.ts -│ │ └── ... -│ ├── lib/ # Hilfsfunktionen und Bibliotheken -│ │ ├── api.ts # API Client -│ │ ├── auth.ts # Auth-Utilities -│ │ ├── date-utils.ts # Date-Helpers -│ │ └── ... -│ ├── types/ # TypeScript-Typdefinitionen -│ │ ├── auth.ts -│ │ ├── user.ts -│ │ ├── timeTracking.ts -│ │ ├── project.ts -│ │ └── ... -│ ├── store/ # State Management (falls benötigt) -│ │ ├── slices/ -│ │ └── index.ts -│ ├── styles/ # CSS/SCSS Styles -│ │ ├── globals.css -│ │ └── theme.ts -│ └── utils/ # Allgemeine Hilfsfunktionen -│ ├── format.ts -│ ├── validation.ts -│ └── ... -├── .env.local.example -├── .eslintrc.json -├── next.config.js -├── package.json -├── tailwind.config.js -├── tsconfig.json -├── jest.config.js # Test-Konfiguration -├── postcss.config.js # PostCSS-Konfiguration -└── README.md \ No newline at end of file diff --git a/docu/database_schema.md b/docu/database_schema.md new file mode 100644 index 0000000..2d45884 --- /dev/null +++ b/docu/database_schema.md @@ -0,0 +1,160 @@ +# Database Schema (PostgreSQL) + +```sql +-- Multi-Tenant +CREATE TABLE companies ( + id UUID PRIMARY KEY, + name VARCHAR(255) NOT NULL, + address TEXT, + contact_email VARCHAR(255), + contact_phone VARCHAR(50), + logo_url TEXT, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Users and Roles +CREATE TABLE roles ( + id SERIAL PRIMARY KEY, + name VARCHAR(50) UNIQUE NOT NULL, + permissions JSONB +); + +CREATE TABLE users ( + id UUID PRIMARY KEY, + company_id UUID REFERENCES companies(id), + email VARCHAR(255) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, + first_name VARCHAR(100), + last_name VARCHAR(100), + role_id INTEGER REFERENCES roles(id), + hourly_rate DECIMAL(10, 2), + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Customers +CREATE TABLE customers ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + name VARCHAR(255) NOT NULL, + contact_person VARCHAR(255), + email VARCHAR(255), + phone VARCHAR(50), + address TEXT, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Projects +CREATE TABLE projects ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + customer_id UUID REFERENCES customers(id), + name VARCHAR(255) NOT NULL, + description TEXT, + start_date DATE, + end_date DATE, + status VARCHAR(50), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Activities +CREATE TABLE activities ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + name VARCHAR(255) NOT NULL, + description TEXT, + billing_rate DECIMAL(10, 2), + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Time bookings +CREATE TABLE time_entries ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + user_id UUID NOT NULL REFERENCES users(id), + project_id UUID NOT NULL REFERENCES projects(id), + activity_id UUID NOT NULL REFERENCES activities(id), + start_time TIMESTAMP NOT NULL, + end_time TIMESTAMP NOT NULL, + duration INTEGER NOT NULL, -- in minutes + description TEXT, + billable_percentage INTEGER NOT NULL DEFAULT 100, + billing_rate DECIMAL(10, 2), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Version 2: Sprint/Task Management +CREATE TABLE sprints ( + id UUID PRIMARY KEY, + project_id UUID NOT NULL REFERENCES projects(id), + name VARCHAR(255) NOT NULL, + start_date DATE NOT NULL, + end_date DATE NOT NULL, + status VARCHAR(50), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE TABLE task_statuses ( + id SERIAL PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + name VARCHAR(100) NOT NULL, + color VARCHAR(7), + position INTEGER NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE TABLE tasks ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + project_id UUID NOT NULL REFERENCES projects(id), + sprint_id UUID REFERENCES sprints(id), + title VARCHAR(255) NOT NULL, + description TEXT, + assignee_id UUID REFERENCES users(id), + status_id INTEGER REFERENCES task_statuses(id), + priority VARCHAR(50), + estimate INTEGER, -- in minutes + due_date TIMESTAMP, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE TABLE kanban_boards ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + project_id UUID NOT NULL REFERENCES projects(id), + name VARCHAR(255) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE TABLE kanban_columns ( + id UUID PRIMARY KEY, + board_id UUID NOT NULL REFERENCES kanban_boards(id), + name VARCHAR(100) NOT NULL, + position INTEGER NOT NULL, + task_status_id INTEGER REFERENCES task_statuses(id), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Linking time entries and tasks +ALTER TABLE time_entries ADD COLUMN task_id UUID REFERENCES tasks(id); + +-- Indexes for performance +CREATE INDEX idx_time_entries_user ON time_entries(user_id); +CREATE INDEX idx_time_entries_project ON time_entries(project_id); +CREATE INDEX idx_time_entries_date ON time_entries(start_time); +CREATE INDEX idx_projects_company ON projects(company_id); +CREATE INDEX idx_users_company ON users(company_id); +CREATE INDEX idx_tasks_project ON tasks(project_id); +CREATE INDEX idx_tasks_sprint ON tasks(sprint_id); diff --git a/docu/deployment_devops.md b/docu/deployment_devops.md new file mode 100644 index 0000000..c757f62 --- /dev/null +++ b/docu/deployment_devops.md @@ -0,0 +1,17 @@ +# Deployment and DevOps + +## Containerization +- Docker containers for backend and frontend +- Docker Compose for development environment +- Kubernetes manifests for production environment + +## CI/CD Pipeline +- Automated tests (Unit, Integration, E2E) +- Automated deployment +- Version management + +## Monitoring and Logging +- Prometheus for metrics +- Grafana for visualization +- ELK Stack or similar for logging +- Alerting for critical events diff --git a/docu/concept/Readme.md b/docu/draft/concept/Readme.md similarity index 100% rename from docu/concept/Readme.md rename to docu/draft/concept/Readme.md diff --git a/docu/draft/concept/claude.md b/docu/draft/concept/claude.md new file mode 100644 index 0000000..40a50ef --- /dev/null +++ b/docu/draft/concept/claude.md @@ -0,0 +1,389 @@ +# Zeiterfassungstool - Projektstruktur + +## Backend (Go) + +``` +timetracker-backend/ +├── cmd/ # Einstiegspunkte für die Anwendung +│ ├── api/ # API-Server +│ │ └── main.go +│ └── worker/ # Hintergrundprozesse (Reports, Benachrichtigungen usw.) +│ └── main.go +├── internal/ # Interner Code, nicht exportierbar +│ ├── auth/ # Authentifizierung und Autorisierung +│ │ ├── jwt.go +│ │ ├── middleware.go +│ │ ├── permissions.go +│ │ └── service.go +│ ├── tenant/ # Multi-Tenant-Funktionalität +│ │ ├── middleware.go +│ │ └── service.go +│ ├── user/ # Benutzerverwaltung +│ │ ├── model.go +│ │ ├── repository.go +│ │ └── service.go +│ ├── company/ # Unternehmensverwaltung +│ │ ├── model.go +│ │ ├── repository.go +│ │ └── service.go +│ ├── customer/ # Kundenverwaltung +│ │ ├── model.go +│ │ ├── repository.go +│ │ └── service.go +│ ├── project/ # Projektverwaltung +│ │ ├── model.go +│ │ ├── repository.go +│ │ └── service.go +│ ├── activity/ # Tätigkeitsverwaltung +│ │ ├── model.go +│ │ ├── repository.go +│ │ └── service.go +│ ├── timetracking/ # Zeiterfassung +│ │ ├── model.go +│ │ ├── repository.go +│ │ └── service.go +│ ├── billing/ # Abrechnung +│ │ ├── model.go +│ │ ├── repository.go +│ │ └── service.go +│ ├── reporting/ # Berichtswesen +│ │ ├── generator.go +│ │ ├── pdf.go +│ │ └── service.go +│ ├── notification/ # Benachrichtigungen +│ │ ├── email.go +│ │ ├── inapp.go +│ │ └── service.go +│ ├── kanban/ # Kanban-Board (Version 2) +│ │ ├── model.go +│ │ ├── repository.go +│ │ └── service.go +│ ├── task/ # Task-Management (Version 2) +│ │ ├── model.go +│ │ ├── repository.go +│ │ └── service.go +│ ├── sprint/ # Sprint-Management (Version 2) +│ │ ├── model.go +│ │ ├── repository.go +│ │ └── service.go +│ ├── config/ # Konfiguration +│ │ └── config.go +│ └── database/ # Datenbankzugriff +│ ├── migrations/ +│ ├── seeds/ +│ └── connection.go +├── pkg/ # Öffentliche Pakete, die exportiert werden können +│ ├── apierror/ # API-Fehlerobjekte +│ │ └── error.go +│ ├── logger/ # Logging-Funktionalität +│ │ └── logger.go +│ ├── validator/ # Validierungsfunktionen +│ │ └── validator.go +│ └── utils/ # Allgemeine Hilfsfunktionen +│ ├── dates.go +│ ├── encryption.go +│ └── helpers.go +├── api/ # API-Definitionen +│ ├── handlers/ # API-Handler +│ │ ├── auth.go +│ │ ├── user.go +│ │ ├── company.go +│ │ ├── customer.go +│ │ ├── project.go +│ │ ├── timetracking.go +│ │ ├── reporting.go +│ │ └── task.go +│ ├── middleware/ # API-Middleware +│ │ ├── auth.go +│ │ ├── tenant.go +│ │ └── logging.go +│ ├── routes/ # API-Routen +│ │ └── routes.go +│ └── swagger/ # Swagger-Dokumentation +│ └── swagger.yaml +├── test/ # Tests +│ ├── integration/ +│ ├── unit/ +│ └── mocks/ +├── scripts/ # Skripte für Build, Deployment usw. +│ ├── build.sh +│ ├── migrate.sh +│ └── seed.sh +├── docker/ # Docker-Konfiguration +│ ├── Dockerfile +│ └── docker-compose.yml +├── go.mod +├── go.sum +├── .env.example +└── README.md +``` + +## Frontend (NextJS) + +``` +timetracker-frontend/ +├── public/ # Statische Dateien +│ ├── assets/ +│ │ ├── images/ +│ │ ├── icons/ +│ │ └── fonts/ +│ └── locales/ # Mehrsprachige Inhalte +│ ├── de/ +│ └── en/ +├── src/ +│ ├── app/ # Next.js 13+ App Router +│ │ ├── api/ # API-Routen (falls nötig) +│ │ ├── (auth)/ # Authentifizierungsseiten +│ │ │ ├── login/ +│ │ │ └── register/ +│ │ ├── dashboard/ # Dashboard-Seiten +│ │ │ ├── page.tsx +│ │ │ └── layout.tsx +│ │ ├── time-tracking/ # Zeiterfassungsseiten +│ │ │ ├── page.tsx +│ │ │ ├── [id]/ +│ │ │ └── components/ +│ │ ├── projects/ # Projektseiten +│ │ │ ├── page.tsx +│ │ │ └── [id]/ +│ │ ├── customers/ # Kundenseiten +│ │ │ ├── page.tsx +│ │ │ └── [id]/ +│ │ ├── reports/ # Berichtsseiten +│ │ │ ├── page.tsx +│ │ │ └── [type]/ +│ │ ├── admin/ # Administrationsseiten +│ │ │ ├── users/ +│ │ │ ├── companies/ +│ │ │ └── settings/ +│ │ ├── kanban/ # Kanban-Boards (Version 2) +│ │ │ ├── page.tsx +│ │ │ └── [id]/ +│ │ ├── layout.tsx +│ │ └── page.tsx +│ ├── components/ # Wiederverwendbare Komponenten +│ │ ├── common/ # Allgemeine Komponenten +│ │ │ ├── Button.tsx +│ │ │ ├── Card.tsx +│ │ │ ├── Input.tsx +│ │ │ └── ... +│ │ ├── layout/ # Layout-Komponenten +│ │ │ ├── Navbar.tsx +│ │ │ ├── Sidebar.tsx +│ │ │ ├── Footer.tsx +│ │ │ └── ... +│ │ ├── dashboard/ # Dashboard-Komponenten +│ │ │ ├── ActivityChart.tsx +│ │ │ ├── RecentEntries.tsx +│ │ │ └── ... +│ │ ├── timetracker/ # Zeiterfassungskomponenten +│ │ │ ├── Timer.tsx +│ │ │ ├── EntryForm.tsx +│ │ │ └── ... +│ │ ├── reports/ # Berichtskomponenten +│ │ │ ├── ReportFilter.tsx +│ │ │ ├── Chart.tsx +│ │ │ └── ... +│ │ └── kanban/ # Kanban-Komponenten (Version 2) +│ │ ├── Board.tsx +│ │ ├── Column.tsx +│ │ ├── Card.tsx +│ │ └── ... +│ ├── hooks/ # Custom React Hooks +│ │ ├── useAuth.ts +│ │ ├── useTimeTracking.ts +│ │ ├── useProjects.ts +│ │ └── ... +│ ├── lib/ # Hilfsfunktionen und Bibliotheken +│ │ ├── api.ts # API Client +│ │ ├── auth.ts # Auth-Utilities +│ │ ├── date-utils.ts # Date-Helpers +│ │ └── ... +│ ├── types/ # TypeScript-Typdefinitionen +│ │ ├── auth.ts +│ │ ├── user.ts +│ │ ├── timeTracking.ts +│ │ ├── project.ts +│ │ └── ... +│ ├── store/ # State Management (falls benötigt) +│ │ ├── slices/ +│ │ └── index.ts +│ ├── styles/ # CSS/SCSS Styles +│ │ ├── globals.css +│ │ └── theme.ts +│ └── utils/ # Allgemeine Hilfsfunktionen +│ ├── format.ts +│ ├── validation.ts +│ └── ... +├── .env.local.example +├── .eslintrc.json +├── next.config.js +├── package.json +├── tailwind.config.js +├── tsconfig.json +├── jest.config.js # Test-Konfiguration +├── postcss.config.js # PostCSS-Konfiguration +└── README.md +``` + +## Datenbankschema (PostgreSQL) + +```sql +-- Multi-Tenant +CREATE TABLE companies ( + id UUID PRIMARY KEY, + name VARCHAR(255) NOT NULL, + address TEXT, + contact_email VARCHAR(255), + contact_phone VARCHAR(50), + logo_url TEXT, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Benutzer und Rollen +CREATE TABLE roles ( + id SERIAL PRIMARY KEY, + name VARCHAR(50) UNIQUE NOT NULL, + permissions JSONB +); + +CREATE TABLE users ( + id UUID PRIMARY KEY, + company_id UUID REFERENCES companies(id), + email VARCHAR(255) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, + first_name VARCHAR(100), + last_name VARCHAR(100), + role_id INTEGER REFERENCES roles(id), + hourly_rate DECIMAL(10, 2), + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Kunden +CREATE TABLE customers ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + name VARCHAR(255) NOT NULL, + contact_person VARCHAR(255), + email VARCHAR(255), + phone VARCHAR(50), + address TEXT, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Projekte +CREATE TABLE projects ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + customer_id UUID REFERENCES customers(id), + name VARCHAR(255) NOT NULL, + description TEXT, + start_date DATE, + end_date DATE, + status VARCHAR(50), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Tätigkeiten +CREATE TABLE activities ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + name VARCHAR(255) NOT NULL, + description TEXT, + billing_rate DECIMAL(10, 2), + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Zeitbuchungen +CREATE TABLE time_entries ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + user_id UUID NOT NULL REFERENCES users(id), + project_id UUID NOT NULL REFERENCES projects(id), + activity_id UUID NOT NULL REFERENCES activities(id), + start_time TIMESTAMP NOT NULL, + end_time TIMESTAMP NOT NULL, + duration INTEGER NOT NULL, -- in minutes + description TEXT, + billable_percentage INTEGER NOT NULL DEFAULT 100, + billing_rate DECIMAL(10, 2), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Version 2: Sprint/Task Management +CREATE TABLE sprints ( + id UUID PRIMARY KEY, + project_id UUID NOT NULL REFERENCES projects(id), + name VARCHAR(255) NOT NULL, + start_date DATE NOT NULL, + end_date DATE NOT NULL, + status VARCHAR(50), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE TABLE task_statuses ( + id SERIAL PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + name VARCHAR(100) NOT NULL, + color VARCHAR(7), + position INTEGER NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE TABLE tasks ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + project_id UUID NOT NULL REFERENCES projects(id), + sprint_id UUID REFERENCES sprints(id), + title VARCHAR(255) NOT NULL, + description TEXT, + assignee_id UUID REFERENCES users(id), + status_id INTEGER REFERENCES task_statuses(id), + priority VARCHAR(50), + estimate INTEGER, -- in minutes + due_date TIMESTAMP, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE TABLE kanban_boards ( + id UUID PRIMARY KEY, + company_id UUID NOT NULL REFERENCES companies(id), + project_id UUID NOT NULL REFERENCES projects(id), + name VARCHAR(255) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE TABLE kanban_columns ( + id UUID PRIMARY KEY, + board_id UUID NOT NULL REFERENCES kanban_boards(id), + name VARCHAR(100) NOT NULL, + position INTEGER NOT NULL, + task_status_id INTEGER REFERENCES task_statuses(id), + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +-- Verknüpfung zwischen Zeitbuchungen und Tasks +ALTER TABLE time_entries ADD COLUMN task_id UUID REFERENCES tasks(id); + +-- Indizes für Performance +CREATE INDEX idx_time_entries_user ON time_entries(user_id); +CREATE INDEX idx_time_entries_project ON time_entries(project_id); +CREATE INDEX idx_time_entries_date ON time_entries(start_time); +CREATE INDEX idx_projects_company ON projects(company_id); +CREATE INDEX idx_users_company ON users(company_id); +CREATE INDEX idx_tasks_project ON tasks(project_id); +CREATE INDEX idx_tasks_sprint ON tasks(sprint_id); +``` \ No newline at end of file diff --git a/docu/concept/structure.md b/docu/draft/concept/structure.md similarity index 100% rename from docu/concept/structure.md rename to docu/draft/concept/structure.md diff --git a/docu/extensibility_integrations.md b/docu/extensibility_integrations.md new file mode 100644 index 0000000..3f761c4 --- /dev/null +++ b/docu/extensibility_integrations.md @@ -0,0 +1,6 @@ +# Extensibility and Integrations + +- API for external billing systems and calendar integration +- Single Sign-On (Google, Microsoft, SAML) +- Plugin system and custom fields for extensibility +- Analytics and BI export diff --git a/docu/frontend_architecture.md b/docu/frontend_architecture.md new file mode 100644 index 0000000..ea52051 --- /dev/null +++ b/docu/frontend_architecture.md @@ -0,0 +1,102 @@ +# Frontend Architecture (Next.js) + +**Note:** This document describes a *conceptual* architecture and is not a final, binding requirement. + +The frontend uses **Next.js** with **TypeScript** and **FPTS** for functional programming. + +## Project Structure + +``` +timetracker-frontend/ +├── public/ # Statische Dateien +│ ├── assets/ +│ └── locales/ # i18n Übersetzungen +├── src/ +│ ├── app/ # Next.js App Router +│ │ ├── api/ # API Routes +│ │ ├── (auth)/ # Auth Pages +│ │ ├── dashboard/ +│ │ ├── time-tracking/ +│ │ ├── projects/ +│ │ └── ... +│ ├── domain/ # Domain Model (analog zu Backend) +│ │ ├── entities/ # Domain Entities +│ │ │ ├── user.ts +│ │ │ ├── timeEntry.ts +│ │ │ ├── project.ts +│ │ │ └── ... +│ │ └── valueObjects/ # Value Objects +│ │ ├── money.ts +│ │ ├── duration.ts +│ │ └── ... +│ ├── application/ # Anwendungsfälle +│ │ ├── auth/ # Auth Use Cases +│ │ ├── timeTracking/ # Time Tracking Use Cases +│ │ ├── projects/ # Projects Use Cases +│ │ └── ... +│ ├── infrastructure/ # Infrastruktur +│ │ ├── api/ # API Client +│ │ │ ├── apiClient.ts # Axios Konfiguration +│ │ │ ├── endpoints.ts # API Endpunkte +│ │ │ └── interceptors.ts # Request/Response Interceptors +│ │ ├── storage/ # Lokale Speicherung +│ │ │ ├── localStorage.ts +│ │ │ └── sessionStorage.ts +│ │ └── external/ # Externe Dienste +│ │ └── ... +│ ├── presentation/ # Präsentationsschicht +│ │ ├── components/ # UI Komponenten +│ │ │ ├── common/ # Allgemeine Komponenten +│ │ │ │ ├── Button/ +│ │ │ │ ├── Card/ +│ │ │ │ └── ... +│ │ │ ├── layout/ # Layout Komponenten +│ │ │ │ ├── Navbar/ +│ │ │ │ ├── Sidebar/ +│ │ │ │ └── ... +│ │ │ ├── timeTracker/ # Zeiterfassung Komponenten +│ │ │ │ ├── Timer/ +│ │ │ │ ├── EntryForm/ +│ │ │ │ └── ... +│ │ │ └── ... +│ │ ├── hooks/ # Custom React Hooks +│ │ │ ├── useAuth.ts +│ │ │ ├── useTimeTracking.ts +│ │ │ └── ... +│ │ ├── providers/ # Context Provider +│ │ │ ├── AuthProvider.tsx +│ │ │ ├── ThemeProvider.tsx +│ │ │ └── ... +│ │ └── pages/ # Page Components (für App Router) +│ │ ├── DashboardPage.tsx +│ │ ├── TimeTrackingPage.tsx +│ │ └── ... +│ ├── utils/ # Utilities +│ │ ├── fp/ # Funktionale Programmierung (FPTS) +│ │ │ ├── option.ts +│ │ │ ├── result.ts +│ │ │ └── ... +│ │ ├── date/ # Datumsfunktionen +│ │ │ └── dateUtils.ts +│ │ ├── formatting/ # Formattierung +│ │ │ ├── numberFormat.ts +│ │ │ └── ... +│ │ └── validation/ # Validierung +│ │ └── validators.ts +│ ├── types/ # TypeScript Typendefinitionen +│ │ ├── api.ts # API Response/Request Typen +│ │ ├── auth.ts # Auth Typen +│ │ └── ... +│ └── styles/ # Globale Styles +│ ├── globals.css +│ └── theme.ts +├── docu/ # Frontend-spezifische Dokumentation +│ ├── component_library.md +│ ├── state_management.md +│ └── ... +├── next.config.js +├── tailwind.config.js +├── tsconfig.json +├── package.json +└── README.md +``` diff --git a/docu/general_overview.md b/docu/general_overview.md new file mode 100644 index 0000000..e310848 --- /dev/null +++ b/docu/general_overview.md @@ -0,0 +1,40 @@ +# General Overview + +## Technology Stack + +### Backend +- Go with fp-go +- PostgreSQL with ORM (GORM or Bun) +- Swagger/OpenAPI +- JWT-based Authentication + +### Frontend +- Next.js with fp-ts +- React and Tailwind CSS +- Responsive Webdesign +- Progressive Web App (PWA) + +## Multi-Tenancy & Roles +- **Multi-Tenancy**: Isolation of data per company (tenant). +- **Roles**: Admin, Company-Admin, Manager, Auditor, User. + +## Data Models +- **Company** (Tenant) +- **User** (with roles, hourly rate) +- **Customer** +- **Project** (with customer reference) +- **Activity** (billing rate) +- **Booking** (time, user, project, activity, optional description, billability 0-100%) + +## Functional Requirements +### Version 1 +- Master data management (customers, projects, activities, users) +- Time tracking with start/stop function and parameter inheritance from the last booking +- Clear dashboard with recent bookings and tracker +- Aggregated reports by period, project, customer, employee with PDF export +- Graphical dashboards (diagrams) + +### Version 2 +- Project management with sprints, tasks +- Kanban boards for task management +- Direct linking of tasks with time bookings diff --git a/docu/llm_guidance.md b/docu/llm_guidance.md new file mode 100644 index 0000000..070d71e --- /dev/null +++ b/docu/llm_guidance.md @@ -0,0 +1,92 @@ +# LLM Guidance for the Time Tracking Project + +This document provides guidance for Large Language Models (LLMs) working on the Time Tracking and Management System. + +## File Locations + +Here's a guide to finding information within the project: + +**Documentation:** + +- **General Project Overview:** `docu/general_overview.md` +- **Backend Architecture (Go):** `docu/backend_architecture.md` +- **Frontend Architecture (Next.js):** `docu/frontend_architecture.md` +- **Database Schema (PostgreSQL):** `docu/database_schema.md` +- **Deployment and DevOps:** `docu/deployment_devops.md` +- **Security and Privacy:** `docu/security_privacy.md` +- **Extensibility and Integrations:** `docu/extensibility_integrations.md` +- **This LLM Guidance Document:** `docu/llm_guidance.md` +- **Top Level Documentation Index:** `docu/README.md` + +**Code (Conceptual - see architecture documents for details):** + +- **Backend (Go):** (Refer to `docu/backend_architecture.md` for the conceptual structure. Actual code structure may differ.) + - **Conceptual File Locations:** + - `backend-go/internal/domain/entities/`: Core domain entities (User, Company, Project, etc.) + - `backend-go/internal/domain/repositories/`: Interfaces for data access. + - `backend-go/internal/domain/services/`: Business logic implementation. + - `backend-go/internal/infrastructure/persistence/`: Database interactions (GORM). + - `backend-go/internal/interfaces/http/handlers/`: API endpoint handlers. + - `backend-go/internal/interfaces/http/middleware/`: Authentication and tenant isolation middleware. + - **Code Examples:** + - `docu/code_examples/gorm_entities.go`: Example GORM entity definitions. + - `docu/code_examples/fpgo_repository.go`: Example FPGO repository interface. + - `docu/code_examples/fpgo_service.go`: Example FPGO service implementation. + - `docu/code_examples/api_handler.go`: Example API handler. + +- **Frontend (Next.js):** (Refer to `docu/frontend_architecture.md` for the conceptual structure. Actual code structure may differ.) + - **Conceptual File Locations:** + - `frontend-nextjs/pages/`: Application pages. + - `frontend-nextjs/components/`: Reusable UI components. + - `frontend-nextjs/lib/`: API interaction and helper functions. + - `frontend-nextjs/types/`: TypeScript type definitions. + - **Code Examples:** + - `docu/code_examples/react_component.tsx`: Example React component. + +**Important Note about Code Examples:** The files in `docu/code_examples/` are for illustrative purposes *only*. They do *not* represent a runnable project structure. Treat each file as an isolated example. The package declarations within these files (e.g., `package entities`, `package repositories`, `package main`) are conceptual and should be interpreted in the context of the described architecture, *not* as a literal directory structure. Do not attempt to run `go get` or similar commands based on these examples, as the necessary project structure and dependencies are not present. + +## Rules and Guidelines + +1. **Clean Architecture:** Adhere to Clean Architecture principles. Separate business logic (domain) from infrastructure concerns (database, UI). +2. **Domain-Driven Design (DDD):** Use DDD concepts to model the domain accurately. +3. **Multi-Tenancy:** Ensure all operations are scoped to the correct tenant (company). Use the `company_id` for data isolation. +4. **FPGO and FPTS:** Favor functional programming paradigms in both Go (FPGO) and TypeScript (FPTS). +5. **Modularity:** Keep code modular and well-organized. +6. **Error Handling:** Implement robust error handling and validation. +7. **Security:** Prioritize security. Use JWT for authentication and RBAC for authorization. Sanitize inputs to prevent injection attacks. +8. **Testing:** Write unit and integration tests for all new code. +9. **Documentation**: Keep the documentation up to date. +10. **Conceptual Architecture**: The architecture files are a *concept*, not a final implementation. + +## Ideas to Follow + +- **Small, Focused Modules:** Break down tasks into small, well-defined modules. +- **Descriptive Naming:** Use clear and descriptive names for variables, functions, and files. +- **Comments:** Add comments to explain complex logic. +- **Consistency:** Maintain consistent coding style and patterns. +- **Leverage Libraries:** Use existing libraries (FPGO, FPTS, GORM, Gin, Axios, etc.) where appropriate. +- **Ask for Clarification:** If unsure about any aspect of the project, use the `ask_followup_question` tool. +- **Visually Appealing Design:** (Especially for Claude models) Strive for a visually appealing and user-friendly interface. Consider using appropriate colors, layout, and components. +- **Prefer Replacements:** When making changes to existing files, favor the `replace_in_file` tool over `write_to_file` to minimize context usage and cost. Only use `write_to_file` for creating new files or making extensive, non-localized changes. + +## Workflow for Limited Context + +If you encounter context limitations or are instructed to work in smaller chunks, follow this workflow: + +1. **Request a Summary:** If needed, ask for a brief summary of the current task and relevant file content. +2. **Plan in Chunks:** Divide the task into smaller, manageable sub-tasks. +3. **Focus on One Chunk:** Work on one sub-task at a time. +4. **Use `replace_in_file`:** Make targeted changes using `replace_in_file`. +5. **Iterate:** Repeat steps 3 and 4 for each sub-task. +6. **Provide Context Briefing:** When moving to a new chunk or file, provide a concise summary of the previous work and the current objective. This helps maintain context across multiple interactions. + +## Code Organization and Style + +- **Functional File Division:** Divide functionalities logically into separate files. This improves readability, maintainability, and helps manage context size. +- **Code Reusability:** Design code to be reusable and extensible. Anticipate future requirements and create components or functions that can be adapted for different purposes. +- **Common Sense:** Apply general good coding practices, even if they seem obvious. This includes things like: + - Using meaningful variable and function names. + - Adding comments to explain non-obvious logic. + - Handling edge cases and potential errors. + - Keeping functions and components concise and focused. + - Following consistent indentation and formatting. diff --git a/docu/security_privacy.md b/docu/security_privacy.md new file mode 100644 index 0000000..ebceeb3 --- /dev/null +++ b/docu/security_privacy.md @@ -0,0 +1,6 @@ +# Security and Data Privacy + +- Tenant isolation +- TLS encryption and encrypted storage of sensitive data +- Audit logs (comprehensive logging) +- GDPR compliant (data export, right to be forgotten)