feat(entities): add JSON tags and analytics types to domain entities
- commit
- 1cb11c48331a20dc727219a634c9063d5e4f438c
- author
- brice · · 2025-12-16 19:44 UTC
- committer
- brice · · 2025-12-16 21:03 UTC
- parent
- 5740371
- signature
- unsigned
2 files · +65 -27
M internal/domain/entity/favorite.go +20 -20
| @@ -13,16 +13,16 @@ type TagID int64 | |||
| 13 | 13 | ||
| 14 | 14 | // Favorite represents a bookmarked URL. | |
| 15 | 15 | type Favorite struct { | |
| 16 | - | ID FavoriteID | |
| 17 | - | URL string | |
| 18 | - | Title string | |
| 19 | - | FaviconURL string | |
| 20 | - | FolderID *FolderID // nil = root level | |
| 21 | - | ShortcutKey *int // 1-9 for quick access (Alt+1 through Alt+9) | |
| 22 | - | Position int // Order within folder | |
| 23 | - | Tags []Tag | |
| 24 | - | CreatedAt time.Time | |
| 25 | - | UpdatedAt time.Time | |
| 16 | + | ID FavoriteID `json:"id"` | |
| 17 | + | URL string `json:"url"` | |
| 18 | + | Title string `json:"title"` | |
| 19 | + | FaviconURL string `json:"favicon_url"` | |
| 20 | + | FolderID *FolderID `json:"folder_id"` // nil = root level | |
| 21 | + | ShortcutKey *int `json:"shortcut_key"` // 1-9 for quick access (Alt+1 through Alt+9) | |
| 22 | + | Position int `json:"position"` // Order within folder | |
| 23 | + | Tags []Tag `json:"tags,omitempty"` | |
| 24 | + | CreatedAt time.Time `json:"created_at"` | |
| 25 | + | UpdatedAt time.Time `json:"updated_at"` | |
| 26 | 26 | } | |
| 27 | 27 | ||
| 28 | 28 | // NewFavorite creates a new favorite for a URL. | |
| @@ -58,12 +58,12 @@ func (f *Favorite) HasTag(tagID TagID) bool { | |||
| 58 | 58 | ||
| 59 | 59 | // Folder represents a container for organizing favorites. | |
| 60 | 60 | type Folder struct { | |
| 61 | - | ID FolderID | |
| 62 | - | Name string | |
| 63 | - | Icon string // Optional icon identifier | |
| 64 | - | ParentID *FolderID // nil = root level | |
| 65 | - | Position int // Order within parent | |
| 66 | - | CreatedAt time.Time | |
| 61 | + | ID FolderID `json:"id"` | |
| 62 | + | Name string `json:"name"` | |
| 63 | + | Icon string `json:"icon"` // Optional icon identifier | |
| 64 | + | ParentID *FolderID `json:"parent_id"` // nil = root level | |
| 65 | + | Position int `json:"position"` // Order within parent | |
| 66 | + | CreatedAt time.Time `json:"created_at"` | |
| 67 | 67 | } | |
| 68 | 68 | ||
| 69 | 69 | // NewFolder creates a new folder. | |
| @@ -81,10 +81,10 @@ func (f *Folder) IsRoot() bool { | |||
| 81 | 81 | ||
| 82 | 82 | // Tag represents a label that can be applied to favorites. | |
| 83 | 83 | type Tag struct { | |
| 84 | - | ID TagID | |
| 85 | - | Name string | |
| 86 | - | Color string // Hex color code (e.g., "#FF5733") | |
| 87 | - | CreatedAt time.Time | |
| 84 | + | ID TagID `json:"id"` | |
| 85 | + | Name string `json:"name"` | |
| 86 | + | Color string `json:"color"` // Hex color code (e.g., "#FF5733") | |
| 87 | + | CreatedAt time.Time `json:"created_at"` | |
| 88 | 88 | } | |
| 89 | 89 | ||
| 90 | 90 | // NewTag creates a new tag with default color. | |
| @@ -13,16 +13,16 @@ type TagID int64 | |||||
| 13 | 13 | ||||
| 14 | // Favorite represents a bookmarked URL. | 14 | // Favorite represents a bookmarked URL. | ||
| 15 | type Favorite struct { | 15 | type Favorite struct { | ||
| 16 | - | ID FavoriteID | 16 | + | ID FavoriteID `json:"id"` |
| 17 | - | URL string | 17 | + | URL string `json:"url"` |
| 18 | - | Title string | 18 | + | Title string `json:"title"` |
| 19 | - | FaviconURL string | 19 | + | FaviconURL string `json:"favicon_url"` |
| 20 | - | FolderID *FolderID // nil = root level | 20 | + | FolderID *FolderID `json:"folder_id"` // nil = root level |
| 21 | - | ShortcutKey *int // 1-9 for quick access (Alt+1 through Alt+9) | 21 | + | ShortcutKey *int `json:"shortcut_key"` // 1-9 for quick access (Alt+1 through Alt+9) |
| 22 | - | Position int // Order within folder | 22 | + | Position int `json:"position"` // Order within folder |
| 23 | - | Tags []Tag | 23 | + | Tags []Tag `json:"tags,omitempty"` |
| 24 | - | CreatedAt time.Time | 24 | + | CreatedAt time.Time `json:"created_at"` |
| 25 | - | UpdatedAt time.Time | 25 | + | UpdatedAt time.Time `json:"updated_at"` |
| 26 | } | 26 | } | ||
| 27 | 27 | ||||
| 28 | // NewFavorite creates a new favorite for a URL. | 28 | // NewFavorite creates a new favorite for a URL. | ||
| @@ -58,12 +58,12 @@ func (f *Favorite) HasTag(tagID TagID) bool { | |||||
| 58 | 58 | ||||
| 59 | // Folder represents a container for organizing favorites. | 59 | // Folder represents a container for organizing favorites. | ||
| 60 | type Folder struct { | 60 | type Folder struct { | ||
| 61 | - | ID FolderID | 61 | + | ID FolderID `json:"id"` |
| 62 | - | Name string | 62 | + | Name string `json:"name"` |
| 63 | - | Icon string // Optional icon identifier | 63 | + | Icon string `json:"icon"` // Optional icon identifier |
| 64 | - | ParentID *FolderID // nil = root level | 64 | + | ParentID *FolderID `json:"parent_id"` // nil = root level |
| 65 | - | Position int // Order within parent | 65 | + | Position int `json:"position"` // Order within parent |
| 66 | - | CreatedAt time.Time | 66 | + | CreatedAt time.Time `json:"created_at"` |
| 67 | } | 67 | } | ||
| 68 | 68 | ||||
| 69 | // NewFolder creates a new folder. | 69 | // NewFolder creates a new folder. | ||
| @@ -81,10 +81,10 @@ func (f *Folder) IsRoot() bool { | |||||
| 81 | 81 | ||||
| 82 | // Tag represents a label that can be applied to favorites. | 82 | // Tag represents a label that can be applied to favorites. | ||
| 83 | type Tag struct { | 83 | type Tag struct { | ||
| 84 | - | ID TagID | 84 | + | ID TagID `json:"id"` |
| 85 | - | Name string | 85 | + | Name string `json:"name"` |
| 86 | - | Color string // Hex color code (e.g., "#FF5733") | 86 | + | Color string `json:"color"` // Hex color code (e.g., "#FF5733") |
| 87 | - | CreatedAt time.Time | 87 | + | CreatedAt time.Time `json:"created_at"` |
| 88 | } | 88 | } | ||
| 89 | 89 | ||||
| 90 | // NewTag creates a new tag with default color. | 90 | // NewTag creates a new tag with default color. | ||
M internal/domain/entity/history.go +45 -7
| @@ -4,13 +4,13 @@ import "time" | |||
| 4 | 4 | ||
| 5 | 5 | // HistoryEntry represents a visited URL in browsing history. | |
| 6 | 6 | type HistoryEntry struct { | |
| 7 | - | ID int64 | |
| 8 | - | URL string | |
| 9 | - | Title string | |
| 10 | - | FaviconURL string | |
| 11 | - | VisitCount int64 | |
| 12 | - | LastVisited time.Time | |
| 13 | - | CreatedAt time.Time | |
| 7 | + | ID int64 `json:"id"` | |
| 8 | + | URL string `json:"url"` | |
| 9 | + | Title string `json:"title"` | |
| 10 | + | FaviconURL string `json:"favicon_url"` | |
| 11 | + | VisitCount int64 `json:"visit_count"` | |
| 12 | + | LastVisited time.Time `json:"last_visited"` | |
| 13 | + | CreatedAt time.Time `json:"created_at"` | |
| 14 | 14 | } | |
| 15 | 15 | ||
| 16 | 16 | // NewHistoryEntry creates a new history entry for a URL. | |
| @@ -37,3 +37,41 @@ type HistoryMatch struct { | |||
| 37 | 37 | Entry *HistoryEntry | |
| 38 | 38 | Score float64 // Match score (higher is better) | |
| 39 | 39 | } | |
| 40 | + | ||
| 41 | + | // HistoryStats contains aggregated history statistics. | |
| 42 | + | type HistoryStats struct { | |
| 43 | + | TotalEntries int64 `json:"total_entries"` | |
| 44 | + | TotalVisits int64 `json:"total_visits"` | |
| 45 | + | UniqueDays int64 `json:"unique_days"` | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | // DomainStat contains per-domain visit statistics. | |
| 49 | + | type DomainStat struct { | |
| 50 | + | Domain string `json:"domain"` | |
| 51 | + | PageCount int64 `json:"page_count"` | |
| 52 | + | TotalVisits int64 `json:"total_visits"` | |
| 53 | + | LastVisit time.Time `json:"last_visit"` | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | // HourlyDistribution contains visit counts by hour of day. | |
| 57 | + | type HourlyDistribution struct { | |
| 58 | + | Hour int `json:"hour"` | |
| 59 | + | VisitCount int64 `json:"visit_count"` | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | // DailyVisitCount contains visit counts by day. | |
| 63 | + | type DailyVisitCount struct { | |
| 64 | + | Day string `json:"day"` | |
| 65 | + | Entries int64 `json:"entries"` | |
| 66 | + | Visits int64 `json:"visits"` | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | // HistoryAnalytics contains all analytics data for the homepage. | |
| 70 | + | type HistoryAnalytics struct { | |
| 71 | + | TotalEntries int64 `json:"total_entries"` | |
| 72 | + | TotalVisits int64 `json:"total_visits"` | |
| 73 | + | UniqueDays int64 `json:"unique_days"` | |
| 74 | + | TopDomains []*DomainStat `json:"top_domains"` | |
| 75 | + | DailyVisits []*DailyVisitCount `json:"daily_visits"` | |
| 76 | + | HourlyDistribution []*HourlyDistribution `json:"hourly_distribution"` | |
| 77 | + | } | |
| @@ -4,13 +4,13 @@ import "time" | |||||
| 4 | 4 | ||||
| 5 | // HistoryEntry represents a visited URL in browsing history. | 5 | // HistoryEntry represents a visited URL in browsing history. | ||
| 6 | type HistoryEntry struct { | 6 | type HistoryEntry struct { | ||
| 7 | - | ID int64 | 7 | + | ID int64 `json:"id"` |
| 8 | - | URL string | 8 | + | URL string `json:"url"` |
| 9 | - | Title string | 9 | + | Title string `json:"title"` |
| 10 | - | FaviconURL string | 10 | + | FaviconURL string `json:"favicon_url"` |
| 11 | - | VisitCount int64 | 11 | + | VisitCount int64 `json:"visit_count"` |
| 12 | - | LastVisited time.Time | 12 | + | LastVisited time.Time `json:"last_visited"` |
| 13 | - | CreatedAt time.Time | 13 | + | CreatedAt time.Time `json:"created_at"` |
| 14 | } | 14 | } | ||
| 15 | 15 | ||||
| 16 | // NewHistoryEntry creates a new history entry for a URL. | 16 | // NewHistoryEntry creates a new history entry for a URL. | ||
| @@ -37,3 +37,41 @@ type HistoryMatch struct { | |||||
| 37 | Entry *HistoryEntry | 37 | Entry *HistoryEntry | ||
| 38 | Score float64 // Match score (higher is better) | 38 | Score float64 // Match score (higher is better) | ||
| 39 | } | 39 | } | ||
| 40 | + | ||||
| 41 | + | // HistoryStats contains aggregated history statistics. | |||
| 42 | + | type HistoryStats struct { | |||
| 43 | + | TotalEntries int64 `json:"total_entries"` | |||
| 44 | + | TotalVisits int64 `json:"total_visits"` | |||
| 45 | + | UniqueDays int64 `json:"unique_days"` | |||
| 46 | + | } | |||
| 47 | + | ||||
| 48 | + | // DomainStat contains per-domain visit statistics. | |||
| 49 | + | type DomainStat struct { | |||
| 50 | + | Domain string `json:"domain"` | |||
| 51 | + | PageCount int64 `json:"page_count"` | |||
| 52 | + | TotalVisits int64 `json:"total_visits"` | |||
| 53 | + | LastVisit time.Time `json:"last_visit"` | |||
| 54 | + | } | |||
| 55 | + | ||||
| 56 | + | // HourlyDistribution contains visit counts by hour of day. | |||
| 57 | + | type HourlyDistribution struct { | |||
| 58 | + | Hour int `json:"hour"` | |||
| 59 | + | VisitCount int64 `json:"visit_count"` | |||
| 60 | + | } | |||
| 61 | + | ||||
| 62 | + | // DailyVisitCount contains visit counts by day. | |||
| 63 | + | type DailyVisitCount struct { | |||
| 64 | + | Day string `json:"day"` | |||
| 65 | + | Entries int64 `json:"entries"` | |||
| 66 | + | Visits int64 `json:"visits"` | |||
| 67 | + | } | |||
| 68 | + | ||||
| 69 | + | // HistoryAnalytics contains all analytics data for the homepage. | |||
| 70 | + | type HistoryAnalytics struct { | |||
| 71 | + | TotalEntries int64 `json:"total_entries"` | |||
| 72 | + | TotalVisits int64 `json:"total_visits"` | |||
| 73 | + | UniqueDays int64 `json:"unique_days"` | |||
| 74 | + | TopDomains []*DomainStat `json:"top_domains"` | |||
| 75 | + | DailyVisits []*DailyVisitCount `json:"daily_visits"` | |||
| 76 | + | HourlyDistribution []*HourlyDistribution `json:"hourly_distribution"` | |||
| 77 | + | } | |||