Compare commits

..

No commits in common. "115f2667f6dca9f9f2aa1498356f34fe6b25b67a" and "9749d5658cc8ab67be4eb43e9dead0087cb2cb7a" have entirely different histories.

26 changed files with 63 additions and 320 deletions

View File

@ -5,7 +5,7 @@ import (
)
type Activity struct {
EntityBase
ID ulid.ULID
Name string
BillingRate float64
}

View File

@ -1,14 +0,0 @@
package entities
import (
"time"
"github.com/oklog/ulid/v2"
)
type EntityBase struct {
ID ulid.ULID
CreatedAt time.Time
UpdatedAt time.Time
LastEditorID ulid.ULID
}

View File

@ -3,7 +3,7 @@ package entities
import "github.com/oklog/ulid/v2"
type Company struct {
EntityBase
ID ulid.ULID
Name string
}

View File

@ -3,7 +3,7 @@ package entities
import "github.com/oklog/ulid/v2"
type Customer struct {
EntityBase
ID ulid.ULID
Name string
CompanyID int
}

View File

@ -3,7 +3,7 @@ package entities
import "github.com/oklog/ulid/v2"
type Project struct {
EntityBase
ID ulid.ULID
Name string
CustomerID int
}

View File

@ -7,7 +7,7 @@ import (
)
type TimeEntry struct {
EntityBase
ID ulid.ULID
UserID int
ProjectID int
ActivityID int

View File

@ -3,7 +3,7 @@ package entities
import "github.com/oklog/ulid/v2"
type User struct {
EntityBase
ID ulid.ULID
Username string
Password string
Role string

View File

@ -1,18 +1,13 @@
package dto
import (
"time"
"github.com/oklog/ulid/v2"
)
type ActivityDto struct {
ID ulid.ULID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastEditorID ulid.ULID `json:"lastEditorID"`
Name string `json:"name"`
BillingRate float64 `json:"billingRate"`
ID ulid.ULID `json:"id"`
Name string `json:"name"`
BillingRate float64 `json:"billingRate"`
}
type ActivityCreateDto struct {
@ -21,10 +16,7 @@ type ActivityCreateDto struct {
}
type ActivityUpdateDto struct {
ID ulid.ULID `json:"id"`
CreatedAt *time.Time `json:"createdAt"`
UpdatedAt *time.Time `json:"updatedAt"`
LastEditorID *ulid.ULID `json:"lastEditorID"`
Name *string `json:"name"`
BillingRate *float64 `json:"billingRate"`
ID ulid.ULID `json:"id"`
Name *string `json:"name"`
BillingRate *float64 `json:"billingRate"`
}

View File

@ -1,17 +1,12 @@
package dto
import (
"time"
"github.com/oklog/ulid/v2"
)
type CompanyDto struct {
ID ulid.ULID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastEditorID ulid.ULID `json:"lastEditorID"`
Name string `json:"name"`
ID ulid.ULID `json:"id"`
Name string `json:"name"`
}
type CompanyCreateDto struct {
@ -19,9 +14,6 @@ type CompanyCreateDto struct {
}
type CompanyUpdateDto struct {
ID ulid.ULID `json:"id"`
CreatedAt *time.Time `json:"createdAt"`
UpdatedAt *time.Time `json:"updatedAt"`
LastEditorID *ulid.ULID `json:"lastEditorID"`
Name *string `json:"name"`
ID ulid.ULID `json:"id"`
Name *string `json:"name"`
}

View File

@ -1,18 +1,13 @@
package dto
import (
"time"
"github.com/oklog/ulid/v2"
)
type CustomerDto struct {
ID ulid.ULID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastEditorID ulid.ULID `json:"lastEditorID"`
Name string `json:"name"`
CompanyID int `json:"companyId"`
ID ulid.ULID `json:"id"`
Name string `json:"name"`
CompanyID int `json:"companyId"`
}
type CustomerCreateDto struct {
@ -21,10 +16,7 @@ type CustomerCreateDto struct {
}
type CustomerUpdateDto struct {
ID ulid.ULID `json:"id"`
CreatedAt *time.Time `json:"createdAt"`
UpdatedAt *time.Time `json:"updatedAt"`
LastEditorID *ulid.ULID `json:"lastEditorID"`
Name *string `json:"name"`
CompanyID *int `json:"companyId"`
ID ulid.ULID `json:"id"`
Name *string `json:"name"`
CompanyID *int `json:"companyId"`
}

View File

@ -1,18 +1,13 @@
package dto
import (
"time"
"github.com/oklog/ulid/v2"
)
type ProjectDto struct {
ID ulid.ULID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastEditorID ulid.ULID `json:"lastEditorID"`
Name string `json:"name"`
CustomerID int `json:"customerId"`
ID ulid.ULID `json:"id"`
Name string `json:"name"`
CustomerID int `json:"customerId"`
}
type ProjectCreateDto struct {
@ -21,10 +16,7 @@ type ProjectCreateDto struct {
}
type ProjectUpdateDto struct {
ID ulid.ULID `json:"id"`
CreatedAt *time.Time `json:"createdAt"`
UpdatedAt *time.Time `json:"updatedAt"`
LastEditorID *ulid.ULID `json:"lastEditorID"`
Name *string `json:"name"`
CustomerID *int `json:"customerId"`
ID ulid.ULID `json:"id"`
Name *string `json:"name"`
CustomerID *int `json:"customerId"`
}

View File

@ -7,17 +7,14 @@ import (
)
type TimeEntryDto struct {
ID ulid.ULID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastEditorID ulid.ULID `json:"lastEditorID"`
UserID int `json:"userId"`
ProjectID int `json:"projectId"`
ActivityID int `json:"activityId"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
Description string `json:"description"`
Billable int `json:"billable"` // Percentage (0-100)
ID ulid.ULID `json:"id"`
UserID int `json:"userId"`
ProjectID int `json:"projectId"`
ActivityID int `json:"activityId"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
Description string `json:"description"`
Billable int `json:"billable"` // Percentage (0-100)
}
type TimeEntryCreateDto struct {
@ -31,15 +28,12 @@ type TimeEntryCreateDto struct {
}
type TimeEntryUpdateDto struct {
ID ulid.ULID `json:"id"`
CreatedAt *time.Time `json:"createdAt"`
UpdatedAt *time.Time `json:"updatedAt"`
LastEditorID *ulid.ULID `json:"lastEditorID"`
UserID *int `json:"userId"`
ProjectID *int `json:"projectId"`
ActivityID *int `json:"activityId"`
Start *time.Time `json:"start"`
End *time.Time `json:"end"`
Description *string `json:"description"`
Billable *int `json:"billable"` // Percentage (0-100)
ID ulid.ULID `json:"id"`
UserID *int `json:"userId"`
ProjectID *int `json:"projectId"`
ActivityID *int `json:"activityId"`
Start *time.Time `json:"start"`
End *time.Time `json:"end"`
Description *string `json:"description"`
Billable *int `json:"billable"` // Percentage (0-100)
}

View File

@ -1,21 +1,16 @@
package dto
import (
"time"
"github.com/oklog/ulid/v2"
)
type UserDto struct {
ID ulid.ULID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
LastEditorID ulid.ULID `json:"lastEditorID"`
Username string `json:"username"`
Password string `json:"password"` // Note: In a real application, you would NEVER send the password in a DTO. This is just for demonstration.
Role string `json:"role"`
CompanyID int `json:"companyId"`
HourlyRate float64 `json:"hourlyRate"`
ID ulid.ULID `json:"id"`
Username string `json:"username"`
Password string `json:"password"` // Note: In a real application, you would NEVER send the password in a DTO. This is just for demonstration.
Role string `json:"role"`
CompanyID int `json:"companyId"`
HourlyRate float64 `json:"hourlyRate"`
}
type UserCreateDto struct {
@ -27,13 +22,10 @@ type UserCreateDto struct {
}
type UserUpdateDto struct {
ID ulid.ULID `json:"id"`
CreatedAt *time.Time `json:"createdAt"`
UpdatedAt *time.Time `json:"updatedAt"`
LastEditorID *ulid.ULID `json:"lastEditorID"`
Username *string `json:"username"`
Password *string `json:"password"` // Note: In a real application, you would NEVER send the password in a DTO. This is just for demonstration.
Role *string `json:"role"`
CompanyID *int `json:"companyId"`
HourlyRate *float64 `json:"hourlyRate"`
ID ulid.ULID `json:"id"`
Username *string `json:"username"`
Password *string `json:"password"` // Note: In a real application, you would NEVER send the password in a DTO. This is just for demonstration.
Role *string `json:"role"`
CompanyID *int `json:"companyId"`
HourlyRate *float64 `json:"hourlyRate"`
}

View File

@ -13,8 +13,6 @@ This document provides an overview of the Time Tracking and Management System. F
- [Extensibility and Integrations](extensibility_integrations.md)
- [LLM Guidance](llm_guidance.md)
- [Roadmap](Roadmap.md)
- [Domain Types](domain_types.md)
- [DTOs](dtos.md)
## Code Examples
- [GORM Entities](code_examples/gorm_entities.go)

View File

@ -13,40 +13,6 @@ CREATE TABLE companies (
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- Go structs for creating and updating customers
-- type CustomerCreate struct {
-- Name string
-- CompanyID int
-- }
-- type CustomerUpdate struct {
-- ID ulid.ULID
-- Name *string
-- CompanyID *int
-- }
-- Go structs for creating and updating companies
-- type CompanyCreate struct {
-- Name string
-- }
-- type CompanyUpdate struct {
-- ID ulid.ULID
-- Name *string
-- }
-- Go structs for creating and updating activities
-- type ActivityCreate struct {
-- Name string
-- BillingRate float64
-- }
-- type ActivityUpdate struct {
-- ID ulid.ULID
-- Name *string
-- BillingRate *float64
-- }
-- Users and Roles
CREATE TABLE roles (
id SERIAL PRIMARY KEY,

View File

@ -1,27 +0,0 @@
# Domain Types
This document describes the domain types used in the Time Tracker application. Domain types represent the core business concepts and are used throughout the application.
## Activity
The `Activity` type represents a specific activity that can be tracked, such as "Development", "Meeting", or "Bug Fixing".
## Company
The `Company` type represents a tenant in the multi-tenant application. Each company has its own set of users, customers, projects, and activities.
## Customer
The `Customer` type represents a customer of a company.
## Project
The `Project` type represents a project for a specific customer.
## TimeEntry
The `TimeEntry` type represents a time booking for a specific user, project, and activity.
## User
The `User` type represents a user of the application. Each user belongs to a company and has a specific role.

View File

@ -1,27 +0,0 @@
# Data Transfer Objects (DTOs)
This document describes the Data Transfer Objects (DTOs) used in the Time Tracker application. DTOs are used to transfer data between the backend and frontend, and between different layers of the backend.
## ActivityDto
The `ActivityDto` type represents a specific activity that can be tracked, such as "Development", "Meeting", or "Bug Fixing". It is used to transfer activity data between the backend and frontend.
## CompanyDto
The `CompanyDto` type represents a tenant in the multi-tenant application. Each company has its own set of users, customers, projects, and activities. It is used to transfer company data between the backend and frontend.
## CustomerDto
The `CustomerDto` type represents a customer of a company. It is used to transfer customer data between the backend and frontend.
## ProjectDto
The `ProjectDto` type represents a project for a specific customer. It is used to transfer project data between the backend and frontend.
## TimeEntryDto
The `TimeEntryDto` type represents a time booking for a specific user, project, and activity. It is used to transfer time entry data between the backend and frontend.
## UserDto
The `UserDto` type represents a user of the application. Each user belongs to a company and has a specific role. It is used to transfer user data between the backend and frontend.

View File

@ -1,17 +1,9 @@
import { BaseEntity } from "./base";
import { ActivityDto, ActivityCreateDto, ActivityUpdateDto } from "./dto";
import { UserId, ActivityId } from "./value-ids";
export type Activity = Omit<ActivityDto, "id" | "createdAt" | "updatedAt" | "lastEditorID"> & {
id: ActivityId;
} & BaseEntity;
export type Activity = ActivityDto;
export const mapActivityDtoToActivity = (dto: ActivityDto): Activity => ({
...dto,
id: dto.id as ActivityId,
createdAt: new Date(dto.createdAt),
updatedAt: new Date(dto.updatedAt),
lastEditorID: dto.lastEditorID as UserId,
});
export type ActivityCreate = ActivityCreateDto;

View File

@ -1,8 +0,0 @@
import { UserId } from "./value-ids";
export type BaseEntity = {
createdAt: Date;
updatedAt: Date;
lastEditorID: UserId;
};

View File

@ -1,17 +1,9 @@
import { BaseEntity } from "./base";
import { CompanyDto, CompanyCreateDto, CompanyUpdateDto } from "./dto";
import { UserId, CompanyId } from "./value-ids";
export type Company = Omit<CompanyDto, "id" | "createdAt" | "updatedAt" | "lastEditorID"> & {
id: CompanyId;
} & BaseEntity;
export type Company = CompanyDto;
export const mapCompanyDtoToCompany = (dto: CompanyDto): Company => ({
...dto,
id: dto.id as CompanyId,
createdAt: new Date(dto.createdAt),
updatedAt: new Date(dto.updatedAt),
lastEditorID: dto.lastEditorID as UserId,
});
export type CompanyCreate = CompanyCreateDto;

View File

@ -1,17 +1,9 @@
import { BaseEntity } from "./base";
import { CustomerDto, CustomerCreateDto, CustomerUpdateDto } from "./dto";
import { UserId, CustomerId } from "./value-ids";
export type Customer = Omit<CustomerDto, "id" | "createdAt" | "updatedAt" | "lastEditorID"> & {
id: CustomerId;
} & BaseEntity;
export type Customer = CustomerDto;
export const mapCustomerDtoToCustomer = (dto: CustomerDto): Customer => ({
...dto,
id: dto.id as CustomerId,
createdAt: new Date(dto.createdAt),
updatedAt: new Date(dto.updatedAt),
lastEditorID: dto.lastEditorID as UserId,
});
export type CustomerCreate = CustomerCreateDto;

View File

@ -5,9 +5,6 @@
export interface ActivityDto {
id: string;
createdAt: string;
updatedAt: string;
lastEditorID: string;
name: string;
billingRate: number /* float64 */;
}
@ -17,9 +14,6 @@ export interface ActivityCreateDto {
}
export interface ActivityUpdateDto {
id: string;
createdAt?: string;
updatedAt?: string;
lastEditorID?: string;
name?: string;
billingRate?: number /* float64 */;
}
@ -29,9 +23,6 @@ export interface ActivityUpdateDto {
export interface CompanyDto {
id: string;
createdAt: string;
updatedAt: string;
lastEditorID: string;
name: string;
}
export interface CompanyCreateDto {
@ -39,9 +30,6 @@ export interface CompanyCreateDto {
}
export interface CompanyUpdateDto {
id: string;
createdAt?: string;
updatedAt?: string;
lastEditorID?: string;
name?: string;
}
@ -50,9 +38,6 @@ export interface CompanyUpdateDto {
export interface CustomerDto {
id: string;
createdAt: string;
updatedAt: string;
lastEditorID: string;
name: string;
companyId: number /* int */;
}
@ -62,9 +47,6 @@ export interface CustomerCreateDto {
}
export interface CustomerUpdateDto {
id: string;
createdAt?: string;
updatedAt?: string;
lastEditorID?: string;
name?: string;
companyId?: number /* int */;
}
@ -74,9 +56,6 @@ export interface CustomerUpdateDto {
export interface ProjectDto {
id: string;
createdAt: string;
updatedAt: string;
lastEditorID: string;
name: string;
customerId: number /* int */;
}
@ -86,9 +65,6 @@ export interface ProjectCreateDto {
}
export interface ProjectUpdateDto {
id: string;
createdAt?: string;
updatedAt?: string;
lastEditorID?: string;
name?: string;
customerId?: number /* int */;
}
@ -98,9 +74,6 @@ export interface ProjectUpdateDto {
export interface TimeEntryDto {
id: string;
createdAt: string;
updatedAt: string;
lastEditorID: string;
userId: number /* int */;
projectId: number /* int */;
activityId: number /* int */;
@ -120,9 +93,6 @@ export interface TimeEntryCreateDto {
}
export interface TimeEntryUpdateDto {
id: string;
createdAt?: string;
updatedAt?: string;
lastEditorID?: string;
userId?: number /* int */;
projectId?: number /* int */;
activityId?: number /* int */;
@ -137,9 +107,6 @@ export interface TimeEntryUpdateDto {
export interface UserDto {
id: string;
createdAt: string;
updatedAt: string;
lastEditorID: string;
username: string;
password: string; // Note: In a real application, you would NEVER send the password in a DTO. This is just for demonstration.
role: string;
@ -155,9 +122,6 @@ export interface UserCreateDto {
}
export interface UserUpdateDto {
id: string;
createdAt?: string;
updatedAt?: string;
lastEditorID?: string;
username?: string;
password?: string; // Note: In a real application, you would NEVER send the password in a DTO. This is just for demonstration.
role?: string;

View File

@ -1,17 +1,9 @@
import { BaseEntity } from "./base";
import { ProjectDto, ProjectCreateDto, ProjectUpdateDto } from "./dto";
import { UserId, ProjectId } from "./value-ids";
export type Project = Omit<ProjectDto, "id" | "createdAt" | "updatedAt" | "lastEditorID"> & {
id: ProjectId;
} & BaseEntity;
export type Project = ProjectDto;
export const mapProjectDtoToProject = (dto: ProjectDto): Project => ({
...dto,
id: dto.id as ProjectId,
createdAt: new Date(dto.createdAt),
updatedAt: new Date(dto.updatedAt),
lastEditorID: dto.lastEditorID as UserId,
});
export type ProjectCreate = ProjectCreateDto;

View File

@ -1,27 +1,14 @@
import { BaseEntity } from "./base";
import { TimeEntryDto, TimeEntryCreateDto, TimeEntryUpdateDto } from "./dto";
import { UserId, ProjectId, ActivityId, TimeEntryId } from "./value-ids";
export type TimeEntry = Omit<TimeEntryDto, "id" | "start" | "end" | "userId" | "projectId" | "activityId" | "lastEditorID" | "createdAt" | "updatedAt"> & {
id: TimeEntryId;
export type TimeEntry = Omit<TimeEntryDto, "start" | "end"> & {
start: Date;
end: Date;
userId: UserId;
projectId: ProjectId;
activityId: ActivityId;
} & BaseEntity;
};
export const mapTimeEntryDtoToTimeEntry = (dto: TimeEntryDto): TimeEntry => ({
...dto,
id: dto.id as TimeEntryId,
start: new Date(dto.start),
end: new Date(dto.end),
userId: dto.userId.toString() as UserId,
projectId: dto.projectId.toString() as ProjectId,
activityId: dto.activityId.toString() as ActivityId,
createdAt: new Date(dto.createdAt),
updatedAt: new Date(dto.updatedAt),
lastEditorID: dto.lastEditorID as UserId,
});
export type TimeEntryCreate = Omit<TimeEntryCreateDto, "start" | "end"> & {

View File

@ -1,17 +1,9 @@
import { BaseEntity } from "./base";
import { UserDto, UserCreateDto, UserUpdateDto } from "./dto";
import { UserId } from "./value-ids";
export type User = Omit<UserDto, "id" | "createdAt" | "updatedAt" | "lastEditorID"> & {
id: UserId;
} & BaseEntity;
export type User = Omit<UserDto, never>;
export const mapUserDtoToUser = (dto: UserDto): User => ({
...dto,
id: dto.id as UserId,
createdAt: new Date(dto.createdAt),
updatedAt: new Date(dto.updatedAt),
lastEditorID: dto.lastEditorID as UserId,
});
export type UserCreate = Omit<UserCreateDto, never>;

View File

@ -1,10 +0,0 @@
export type ValueId<T = string> = string & { __valueId: T };
export type CustomerId = ValueId<"CustomerId">;
export type ProjectId = ValueId<"ProjectId">;
export type TimeEntryId = ValueId<"TimeEntryId">;
export type CompanyId = ValueId<"CompanyId">;
export type UserId = ValueId<"UserId">;
export type RoleId = ValueId<"RoleId">;
export type PermissionId = ValueId<"PermissionId">;
export type ActivityId = ValueId<"ActivityId">;