Refactor: Eliminate duplicate types - Use handler types directly
- Deleted templates.CollectionDetailData - using templates.CollectionData everywhere - Deleted templates.BookData - using handlers.BookInfo everywhere - Deleted templates.DeviceData - using handlers.DeviceInfo everywhere - Deleted templates.ProgressItemData - using handlers.ProgressWithMedia everywhere - Deleted templates.convertDevices() helper - Use handlers types directly in templates - Enhanced handlers.ProgressWithMedia with device metadata fields - Added handlers.getDeviceIcon() helper - Updated all templates to import handlers package - Cleaned up unused imports This aligns codebase with templ's design philosophy (use Go types directly, no parallel type system)
This commit is contained in:
+41
-17
@@ -42,6 +42,7 @@
|
|||||||
- `internal/router/device.go` - Add regenerate token route
|
- `internal/router/device.go` - Add regenerate token route
|
||||||
- `internal/handlers/devices.go` - Add regenerate token handler
|
- `internal/handlers/devices.go` - Add regenerate token handler
|
||||||
- `templates/devices.templ` - Add copy/regenerate UI
|
- `templates/devices.templ` - Add copy/regenerate UI
|
||||||
|
- `web/src/device-management.ts` - Add device management TypeScript
|
||||||
- `bruno/sync-kobo/api.bru` - Add URL path token requests
|
- `bruno/sync-kobo/api.bru` - Add URL path token requests
|
||||||
- `bruno/devices/regenerate-*.bru` - New token regeneration test files
|
- `bruno/devices/regenerate-*.bru` - New token regeneration test files
|
||||||
- `bruno/opds/*-*.bru` - New OPDS authentication test files
|
- `bruno/opds/*-*.bru` - New OPDS authentication test files
|
||||||
@@ -86,14 +87,6 @@ SET
|
|||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
-- name: RevokeDevice :exec
|
|
||||||
UPDATE devices
|
|
||||||
SET
|
|
||||||
auth_token = NULL,
|
|
||||||
sync_enabled = false,
|
|
||||||
updated_at = NOW()
|
|
||||||
WHERE id = $1;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Verification**: Run `go build ./...` after adding this query to ensure sqlc generates the new function correctly.
|
**Verification**: Run `go build ./...` after adding this query to ensure sqlc generates the new function correctly.
|
||||||
@@ -504,11 +497,7 @@ func (h *DeviceHandler) RegenerateDeviceToken(c echo.Context) error {
|
|||||||
3. Add "Regenerate Token" button for each device
|
3. Add "Regenerate Token" button for each device
|
||||||
4. Add JavaScript functions for copy and regenerate
|
4. Add JavaScript functions for copy and regenerate
|
||||||
|
|
||||||
#### 1.6.1 Update DeviceData Struct
|
**ADD FIELD** to struct:
|
||||||
|
|
||||||
**Location**: Find `DeviceData` struct (near top of file)
|
|
||||||
|
|
||||||
**Current struct** (lines vary, find structure):
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type DeviceData struct {
|
type DeviceData struct {
|
||||||
@@ -522,6 +511,31 @@ type DeviceData struct {
|
|||||||
SyncFrequency int
|
SyncFrequency int
|
||||||
CreatedAt string
|
CreatedAt string
|
||||||
DeviceMetadata json.RawMessage
|
DeviceMetadata json.RawMessage
|
||||||
|
AuthToken string // NEW: Device API key for authentication
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1.6.3 Update Collection Detail Template
|
||||||
|
|
||||||
|
**File**: `templates/collection.templ`
|
||||||
|
|
||||||
|
**Location**: Line 213 (function signature)
|
||||||
|
|
||||||
|
**Current**:
|
||||||
|
```templ
|
||||||
|
templ CollectionDetail(user User, collection CollectionDetailData, books []BookData) {
|
||||||
|
```
|
||||||
|
|
||||||
|
**CHANGE** (use handlers.BookInfo):
|
||||||
|
|
||||||
|
```templ
|
||||||
|
templ CollectionDetail(user User, collection CollectionData, books []handlers.BookInfo) {
|
||||||
|
```
|
||||||
|
|
||||||
|
**ADD FIELD** to struct:
|
||||||
|
|
||||||
|
```go
|
||||||
|
AuthToken string // NEW: Device API key for authentication
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -872,7 +886,7 @@ put {
|
|||||||
docs {
|
docs {
|
||||||
## Regenerate Device Token
|
## Regenerate Device Token
|
||||||
|
|
||||||
Regenerates the auth token for a device, invalidating the old token immediately.
|
Regenerates auth token for a device, invalidating old token immediately.
|
||||||
|
|
||||||
**Method:** PUT
|
**Method:** PUT
|
||||||
|
|
||||||
@@ -899,7 +913,7 @@ docs {
|
|||||||
**Important Notes:**
|
**Important Notes:**
|
||||||
- Old token stops working immediately
|
- Old token stops working immediately
|
||||||
- Device must be updated with new token to resume syncing
|
- Device must be updated with new token to resume syncing
|
||||||
- No data loss - device ID remains the same
|
- No data loss - device ID remains same
|
||||||
|
|
||||||
**Example Response:**
|
**Example Response:**
|
||||||
```json
|
```json
|
||||||
@@ -911,14 +925,24 @@ docs {
|
|||||||
"device_name": "My Kobo Clara",
|
"device_name": "My Kobo Clara",
|
||||||
"device_type": "kobo",
|
"device_type": "kobo",
|
||||||
"sync_enabled": true,
|
"sync_enabled": true,
|
||||||
"auto_sync": true
|
"auto_sync": true,
|
||||||
|
"sync_frequency_minutes": 5,
|
||||||
|
"created_at": "2026-02-12T10:00:00Z",
|
||||||
|
"device_metadata": "{...}"
|
||||||
},
|
},
|
||||||
"sync_urls": {
|
"sync_urls": {
|
||||||
"sync_url": "http://localhost:8765/api/sync/kobo/dev_new_token",
|
"sync_url": "http://localhost:8765/api/sync/kobo/dev_new_token",
|
||||||
"markup": "http://localhost:8765/api/sync/kobo/dev_new_token/markup"
|
"markup": "http://localhost:8765/api/sync/kobo/dev_new_token/markup",
|
||||||
|
"bookmark": "http://localhost:8765/api/sync/kobo/dev_new_token/bookmark",
|
||||||
|
"init": "http://localhost:8765/api/sync/kobo/dev_new_token/v1/initialization"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Important Notes:**
|
||||||
|
- Old token stops working immediately
|
||||||
|
- Device must be updated with new token to resume syncing
|
||||||
|
- No data loss - device ID remains same
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,474 @@
|
|||||||
|
# Type Duplication Refactoring Plan
|
||||||
|
|
||||||
|
**Document Purpose**: Consolidate duplicate type definitions across templates/ and handlers/ packages to align with templ's design philosophy and reduce maintenance burden.
|
||||||
|
|
||||||
|
**Status**: Planning Phase
|
||||||
|
**Last Updated**: 2026-02-12
|
||||||
|
**Separation from**: IMPLEMENTATION_EXACT.md (this is independent refactoring)
|
||||||
|
|
||||||
|
**Core Principle**: **API wins** - When there's ambiguity between template types and API/handler types, the API/handler types take precedence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
**Problem**: Codebase maintains parallel type systems:
|
||||||
|
- `templates/types.go` - Template-specific types
|
||||||
|
- `handlers/*.go` - API response types
|
||||||
|
|
||||||
|
**Core Principle**: **API wins - when in doubt, API/handler types take precedence**
|
||||||
|
|
||||||
|
### Why "API wins"?
|
||||||
|
|
||||||
|
1. **API is public contract**: Handler types define your public API surface - these are more stable
|
||||||
|
2. **Templates are presentation**: HTML templates can format any Go type - they're flexible
|
||||||
|
3. **Single source of truth**: One authoritative type definition vs two parallel definitions
|
||||||
|
4. **Json serialization**: Handler types already have `json:"..."` tags for API responses
|
||||||
|
5. **Future API clients**: If you add mobile apps or CLI tools, they need handler types anyway
|
||||||
|
|
||||||
|
### Trade-offs
|
||||||
|
|
||||||
|
**Pro**: Simpler codebase, one type to maintain, automatic API/HTML consistency
|
||||||
|
**Con**: Templates may have unused fields visible in scope (acceptable - templ handles this well)
|
||||||
|
|
||||||
|
**Impact**:
|
||||||
|
- ~40 lines of duplicate type definitions
|
||||||
|
- Unnecessary conversion functions
|
||||||
|
- Maintenance burden (update 2 places for 1 change)
|
||||||
|
- Violates templ's design philosophy
|
||||||
|
|
||||||
|
**Solution**: Use handler types directly in templates, eliminate template-specific duplicates.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current State Analysis
|
||||||
|
|
||||||
|
### Duplicates Found
|
||||||
|
|
||||||
|
| Template Type | Handler Type | Overlap | Action |
|
||||||
|
|---------------|---------------|----------|--------|
|
||||||
|
| `DeviceData` | `DeviceInfo` | 90% | **DELETE** DeviceData |
|
||||||
|
| `BookData` | `collections.BookInfo` | 100% | **DELETE** BookData |
|
||||||
|
| `CollectionDetailData` | `CollectionData` | 100% | **DELETE** CollectionDetailData |
|
||||||
|
| `ProgressItemData` | `ProgressWithMedia` | 70% | **MERGE** (different fields) |
|
||||||
|
| `QueueItemData` | `QueueItemResponse` | 60% | **KEEP** (template uses subset) |
|
||||||
|
|
||||||
|
### Already Using Handler Types (Good Patterns)
|
||||||
|
|
||||||
|
✅ `Conflicts` template uses `handlers.ConflictDetailResponse`
|
||||||
|
✅ `Queue` template uses `handlers.QueueItemResponse`
|
||||||
|
|
||||||
|
**This is the pattern we want everywhere.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Refactoring Plan
|
||||||
|
|
||||||
|
### Phase 1: Eliminate Complete Duplicates
|
||||||
|
|
||||||
|
#### 1.1 Delete CollectionDetailData
|
||||||
|
|
||||||
|
**Files to Modify**:
|
||||||
|
- `templates/types.go` (lines 26-32) - DELETE struct
|
||||||
|
- `templates/collection_rules.templ` (line 3) - Change signature
|
||||||
|
- `templates/collections.templ` (line 213) - Change signature
|
||||||
|
|
||||||
|
**Changes**:
|
||||||
|
|
||||||
|
**Delete** from `templates/types.go`:
|
||||||
|
```go
|
||||||
|
type CollectionDetailData struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
Color string
|
||||||
|
Icon string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update** `templates/collection_rules.templ:3`:
|
||||||
|
```templ
|
||||||
|
// BEFORE:
|
||||||
|
templ CollectionRules(user User, collection CollectionDetailData) {
|
||||||
|
|
||||||
|
// AFTER:
|
||||||
|
templ CollectionRules(user User, collection templates.CollectionData) {
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update** `templates/collections.templ:213`:
|
||||||
|
```templ
|
||||||
|
// BEFORE:
|
||||||
|
templ CollectionDetail(user User, collection CollectionDetailData, books []BookData) {
|
||||||
|
|
||||||
|
// AFTER:
|
||||||
|
templ CollectionDetail(user User, collection templates.CollectionData, books []collections.BookInfo) {
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test**: `go test ./templates/...` after template regeneration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 1.2 Delete BookData
|
||||||
|
|
||||||
|
**Files to Modify**:
|
||||||
|
- `templates/types.go` (lines 34-39) - DELETE struct
|
||||||
|
- `templates/collections.templ` (line 213) - Change books param
|
||||||
|
|
||||||
|
**Changes**:
|
||||||
|
|
||||||
|
**Delete** from `templates/types.go`:
|
||||||
|
```go
|
||||||
|
type BookData struct {
|
||||||
|
MediaItemID string
|
||||||
|
Title string
|
||||||
|
Author string
|
||||||
|
CoverImagePath string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update** `templates/collections.templ:213`:
|
||||||
|
```templ
|
||||||
|
// BEFORE:
|
||||||
|
templ CollectionDetail(user User, collection templates.CollectionData, books []BookData) {
|
||||||
|
|
||||||
|
// AFTER:
|
||||||
|
templ CollectionDetail(user User, collection templates.CollectionData, books []collections.BookInfo) {
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test**: View collection detail page, verify books display correctly
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 1.3 Delete DeviceData and ConvertDevices
|
||||||
|
|
||||||
|
**Files to Modify**:
|
||||||
|
- `templates/types.go` (lines 48-57) - DELETE struct
|
||||||
|
- `templates/devices.templ` (line 3) - Change signature
|
||||||
|
- `internal/router/helpers.go` (lines 46-67) - DELETE function
|
||||||
|
- `internal/router/frontend.go` (lines 161-169) - Remove conversion
|
||||||
|
|
||||||
|
**Changes**:
|
||||||
|
|
||||||
|
**Delete** from `templates/types.go`:
|
||||||
|
```go
|
||||||
|
type DeviceData struct {
|
||||||
|
ID string
|
||||||
|
DeviceName string
|
||||||
|
DeviceType string
|
||||||
|
SyncEnabled bool
|
||||||
|
AutoSync bool
|
||||||
|
SyncFrequency int32
|
||||||
|
LastSync string
|
||||||
|
LastSeen string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Delete** from `internal/router/helpers.go`:
|
||||||
|
```go
|
||||||
|
func convertDevices(deviceInfos []handlers.DeviceInfo) []templates.DeviceData {
|
||||||
|
// ... entire function (lines 46-67)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update** `templates/devices.templ:3`:
|
||||||
|
```templ
|
||||||
|
// BEFORE:
|
||||||
|
templ Devices(user User, devices []DeviceData, pendingRegistrations []PendingRegistrationData) {
|
||||||
|
|
||||||
|
// AFTER:
|
||||||
|
templ Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []PendingRegistrationData) {
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update** `templates/devices.templ` template logic:
|
||||||
|
|
||||||
|
Find these patterns and update:
|
||||||
|
```templ
|
||||||
|
// BEFORE:
|
||||||
|
if device.LastSync != "" {
|
||||||
|
<span>{ device.LastSync }</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
// AFTER:
|
||||||
|
if device.LastSync != nil {
|
||||||
|
<span>{ device.LastSync.Format("2006-01-02 15:04") }</span>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update** `internal/router/frontend.go:161-169`:
|
||||||
|
```go
|
||||||
|
// BEFORE:
|
||||||
|
deviceData, err := cfg.DeviceHandler.GetDevicesData(c)
|
||||||
|
if err != nil {
|
||||||
|
return c.HTML(http.StatusInternalServerError, "Error loading devices")
|
||||||
|
}
|
||||||
|
pendingData, err := cfg.DeviceHandler.GetPendingRegistrationsData(c)
|
||||||
|
if err != nil {
|
||||||
|
return c.HTML(http.StatusInternalServerError, "Error loading pending")
|
||||||
|
}
|
||||||
|
deviesList := convertDevices(deviceData)
|
||||||
|
pendingList := convertPending(pendingData)
|
||||||
|
|
||||||
|
// AFTER:
|
||||||
|
deviceData, err := cfg.DeviceHandler.GetDevicesData(c)
|
||||||
|
if err != nil {
|
||||||
|
return c.HTML(http.StatusInternalServerError, "Error loading devices")
|
||||||
|
}
|
||||||
|
pendingData, err := cfg.DeviceHandler.GetPendingRegistrationsData(c)
|
||||||
|
if err != nil {
|
||||||
|
return c.HTML(http.StatusInternalServerError, "Error loading pending")
|
||||||
|
}
|
||||||
|
// No conversion - use handler data directly
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test**: Devices page renders correctly with proper date formatting
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 2: Eliminate Remaining Template Types
|
||||||
|
|
||||||
|
#### 2.1 ProgressItemData → Use ProgressWithMedia
|
||||||
|
|
||||||
|
**Current Issue**:
|
||||||
|
- `handlers.ProgressWithMedia` exists as API response type
|
||||||
|
- `templates.ProgressItemData` duplicates it with minor variations
|
||||||
|
|
||||||
|
**Decision**: **API wins** - Use `handlers.ProgressWithMedia` everywhere
|
||||||
|
|
||||||
|
**Files to Modify**:
|
||||||
|
- `internal/handlers/progress.go` - Update `ProgressWithMedia` struct
|
||||||
|
- `templates/types.go` - DELETE `ProgressItemData`
|
||||||
|
- `templates/progress.templ` - Use `handlers.ProgressWithMedia`
|
||||||
|
|
||||||
|
**Changes**:
|
||||||
|
|
||||||
|
**Add fields to** `internal/handlers/progress.go:ProgressWithMedia`:
|
||||||
|
```go
|
||||||
|
// ADD fields to existing struct:
|
||||||
|
type ProgressWithMedia struct {
|
||||||
|
MediaItemID uuid.UUID `json:"media_item_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
CoverImagePath string `json:"cover_image_path"`
|
||||||
|
Percentage float64 `json:"percentage"`
|
||||||
|
CurrentPage int32 `json:"current_page"`
|
||||||
|
TotalPages int32 `json:"total_pages"`
|
||||||
|
LastReadAt time.Time `json:"last_read_at"`
|
||||||
|
Epubcfi string `json:"epubcfi"`
|
||||||
|
LastSyncDevice string `json:"last_sync_device"`
|
||||||
|
DeviceName string `json:"device_name,omitempty"` // ADD
|
||||||
|
DeviceType string `json:"device_type,omitempty"` // ADD
|
||||||
|
DeviceIcon string `json:"device_icon,omitempty"` // ADD
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update** `internal/handlers/progress.go:GetAllProgress()` to populate new device fields.
|
||||||
|
|
||||||
|
**Delete** from `templates/types.go` (lines 99-112):
|
||||||
|
```go
|
||||||
|
type ProgressItemData struct {
|
||||||
|
// ... delete entire struct
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update** `templates/progress.templ:3`:
|
||||||
|
```templ
|
||||||
|
// BEFORE:
|
||||||
|
templ Progress(user User, progressData []ProgressItemData) {
|
||||||
|
|
||||||
|
// AFTER:
|
||||||
|
templ Progress(user User, progressData []handlers.ProgressWithMedia) {
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test**: Progress page displays device names and icons
|
||||||
|
|
||||||
|
**Delete** from `templates/types.go` (lines 99-112):
|
||||||
|
```go
|
||||||
|
type ProgressItemData struct {
|
||||||
|
// ... delete entire struct
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update** `templates/progress.templ:3`:
|
||||||
|
```templ
|
||||||
|
// BEFORE:
|
||||||
|
templ Progress(user User, progressData []ProgressItemData) {
|
||||||
|
|
||||||
|
// AFTER:
|
||||||
|
templ Progress(user User, progressData []handlers.ProgressWithMedia) {
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test**: Progress page displays device names and icons
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 3: Update Get*Data Functions
|
||||||
|
|
||||||
|
**Issue**: Several handler functions return handler types but aren't used directly by templates due to conversion layer.
|
||||||
|
|
||||||
|
**Files to Modify**:
|
||||||
|
- `internal/router/frontend.go` - Remove conversions
|
||||||
|
|
||||||
|
**Changes**:
|
||||||
|
|
||||||
|
**Devices** (already done in Phase 1.3):
|
||||||
|
- `GetDevicesData()` returns `[]handlers.DeviceInfo`
|
||||||
|
- Template uses directly
|
||||||
|
|
||||||
|
**Progress**:
|
||||||
|
```go
|
||||||
|
// frontend.go - update progress route
|
||||||
|
protected.GET("/progress-page", func(c echo.Context) error {
|
||||||
|
// ...
|
||||||
|
progressData, err := cfg.Handler.GetAllProgressData(c)
|
||||||
|
// Returns []handlers.ProgressWithMedia directly - no conversion
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Collections**:
|
||||||
|
```go
|
||||||
|
// collections.go - update to return handler types
|
||||||
|
func (h *CollectionHandler) GetCollectionBooksData(c echo.Context) ([]collections.BookInfo, error) {
|
||||||
|
// Already returns BookInfo - just update template
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary Table
|
||||||
|
|
||||||
|
| Phase | Files Changed | Lines Removed | Lines Added | Net Change |
|
||||||
|
|-------|--------------|---------------|--------------|-------------|
|
||||||
|
| 1.1 CollectionDetailData | 3 | 7 | 2 | -5 |
|
||||||
|
| 1.2 BookData | 3 | 6 | 0 | -6 |
|
||||||
|
| 1.3 DeviceData | 4 | 24 | 5 | -19 |
|
||||||
|
| 2.1 ProgressItemData | 4 | 14 | 3 | -11 |
|
||||||
|
| **Total** | **14** | **51** | **10** | **-41** |
|
||||||
|
|
||||||
|
**Note**: QueueItemData already uses handler type (good pattern). Conflicts already uses handler type (good pattern).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing Checklist
|
||||||
|
|
||||||
|
After each phase:
|
||||||
|
|
||||||
|
### Phase 1.1 (CollectionDetailData)
|
||||||
|
- [ ] View collection rules page
|
||||||
|
- [ ] Edit collection rules
|
||||||
|
- [ ] View collection detail page
|
||||||
|
- [ ] Add books to collection
|
||||||
|
|
||||||
|
### Phase 1.2 (BookData)
|
||||||
|
- [ ] View collection detail page
|
||||||
|
- [ ] Browse books in collection
|
||||||
|
- [ ] Book covers display correctly
|
||||||
|
|
||||||
|
### Phase 1.3 (DeviceData)
|
||||||
|
- [ ] Devices page loads
|
||||||
|
- [ ] Device cards display
|
||||||
|
- [ ] Last sync/last seen dates formatted
|
||||||
|
- [ ] Device type icons show correctly
|
||||||
|
|
||||||
|
### Phase 2.1 (ProgressItemData)
|
||||||
|
- [ ] Progress page loads
|
||||||
|
- [ ] Progress entries show device names/icons
|
||||||
|
- [ ] Percentage display works
|
||||||
|
- [ ] Cover images display
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
If refactoring breaks functionality:
|
||||||
|
|
||||||
|
1. **Git stash** changes before starting
|
||||||
|
2. **Commit after each successful phase**
|
||||||
|
3. **Revert individual commits** if needed
|
||||||
|
4. **Keep conversion functions** commented out temporarily
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Before starting
|
||||||
|
git stash push -m "pre-refactor-state"
|
||||||
|
|
||||||
|
# After Phase 1.1
|
||||||
|
git commit -m "refactor: delete CollectionDetailData, use CollectionData"
|
||||||
|
|
||||||
|
# If Phase 1.2 breaks
|
||||||
|
git revert HEAD
|
||||||
|
# Continue with 1.3
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
### Code Quality
|
||||||
|
- ✅ Aligns with templ's design philosophy
|
||||||
|
- ✅ Single source of truth for types
|
||||||
|
- ✅ Reduced code duplication
|
||||||
|
- ✅ **API-first**: Handler types are authoritative
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
- ✅ Update types in 1 place, not 2
|
||||||
|
- ✅ Less conversion logic to maintain
|
||||||
|
- ✅ Clearer data flow
|
||||||
|
- ✅ **Template changes automatically get API updates**
|
||||||
|
|
||||||
|
### Consistency
|
||||||
|
- ✅ All templates use handlers types (like Queue/Conflicts already do)
|
||||||
|
- ✅ API and HTML use same type definitions
|
||||||
|
- ✅ 41 fewer lines of code
|
||||||
|
- ✅ **"API wins"** principle eliminates ambiguity
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future Considerations
|
||||||
|
|
||||||
|
### For IMPLEMENTATION_EXACT.md
|
||||||
|
After this refactoring, adding `AuthToken` to devices is simpler:
|
||||||
|
- Add `AuthToken string` to `handlers.DeviceInfo` struct
|
||||||
|
- Add to `GetDevicesData()` function return values
|
||||||
|
- Template automatically gets it (no conversion layer)
|
||||||
|
|
||||||
|
This **solves the critical issue** found in IMPLEMENTATION_EXACT.md analysis automatically.
|
||||||
|
|
||||||
|
### For New Features
|
||||||
|
- Define handler types with json tags first
|
||||||
|
- Templates use same types directly
|
||||||
|
- Only create template-specific types for:
|
||||||
|
- Computed/derived fields not in API
|
||||||
|
- Combining data from multiple sources
|
||||||
|
- Simplifying complex nested structures
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Order
|
||||||
|
|
||||||
|
1. **Run tests** to ensure baseline: `go test ./...`
|
||||||
|
2. **Create feature branch**: `git checkout -b refactor/types-consolidation`
|
||||||
|
3. **Execute Phase 1.1** (CollectionDetailData) - DELETE template type, use API type
|
||||||
|
4. **Commit and test**
|
||||||
|
5. **Execute Phase 1.2** (BookData) - DELETE template type, use API type
|
||||||
|
6. **Commit and test**
|
||||||
|
7. **Execute Phase 1.3** (DeviceData) - DELETE template type, use API type
|
||||||
|
8. **Commit and test**
|
||||||
|
9. **Execute Phase 2.1** (ProgressItemData) - DELETE template type, enhance API type
|
||||||
|
10. **Final commit and test**
|
||||||
|
11. **Update IMPLEMENTATION_EXACT.md** to reference handler types only
|
||||||
|
|
||||||
|
**Total Estimated Time**: 2-3 hours with testing
|
||||||
|
|
||||||
|
**Key Mindset**: Every change removes a template type, never the reverse. API types are authoritative.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Questions for User
|
||||||
|
|
||||||
|
1. **Progress template updates**: Since we're using `handlers.ProgressWithMedia` (API type) in templates, any template formatting changes (like date formatting) should be done in the template itself. Is this acceptable?
|
||||||
|
|
||||||
|
2. **Testing**: Do you have automated template tests, or is this manual testing only?
|
||||||
|
|
||||||
|
3. **Implementation plan**: Should I update IMPLEMENTATION_EXACT.md after this refactoring to reference `handlers.DeviceInfo` instead of `DeviceData`?
|
||||||
|
|
||||||
|
4. **Json tags on handler types**: Templates don't need `json:"..."` tags - should we document that these are for API responses only?
|
||||||
@@ -63,6 +63,13 @@ type UpdateDeviceMappingRequest struct {
|
|||||||
SyncDirection string `json:"sync_direction" validate:"required,oneof=bidirectional book_to_device device_to_book none"`
|
SyncDirection string `json:"sync_direction" validate:"required,oneof=bidirectional book_to_device device_to_book none"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BookInfo struct {
|
||||||
|
MediaItemID string `json:"media_item_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
CoverImagePath string `json:"cover_image_path"`
|
||||||
|
}
|
||||||
|
|
||||||
func (h *CollectionHandler) CreateCollection(c echo.Context) error {
|
func (h *CollectionHandler) CreateCollection(c echo.Context) error {
|
||||||
user := c.Get("user").(database.Users)
|
user := c.Get("user").(database.Users)
|
||||||
userUUID := uuid.UUID(user.ID.Bytes)
|
userUUID := uuid.UUID(user.ID.Bytes)
|
||||||
@@ -171,13 +178,6 @@ func (h *CollectionHandler) GetCollection(c echo.Context) error {
|
|||||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||||
}
|
}
|
||||||
|
|
||||||
type BookInfo struct {
|
|
||||||
MediaItemID string `json:"media_item_id"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Author string `json:"author"`
|
|
||||||
CoverImagePath string `json:"cover_image_path"`
|
|
||||||
}
|
|
||||||
|
|
||||||
bookList := make([]BookInfo, 0, len(books))
|
bookList := make([]BookInfo, 0, len(books))
|
||||||
for _, book := range books {
|
for _, book := range books {
|
||||||
bookList = append(bookList, BookInfo{
|
bookList = append(bookList, BookInfo{
|
||||||
|
|||||||
@@ -14,6 +14,20 @@ import (
|
|||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// getDeviceIcon returns an emoji icon for device type
|
||||||
|
func getDeviceIcon(deviceType string) string {
|
||||||
|
switch deviceType {
|
||||||
|
case "kobo":
|
||||||
|
return "📚"
|
||||||
|
case "koreader":
|
||||||
|
return "📖"
|
||||||
|
case "kindle":
|
||||||
|
return "📱"
|
||||||
|
default:
|
||||||
|
return "📚"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetUniversalProgress retrieves progress with all location references
|
// GetUniversalProgress retrieves progress with all location references
|
||||||
func (h *Handler) GetUniversalProgress(c echo.Context) error {
|
func (h *Handler) GetUniversalProgress(c echo.Context) error {
|
||||||
user := MustGetAuthenticatedUser(c)
|
user := MustGetAuthenticatedUser(c)
|
||||||
@@ -229,16 +243,22 @@ func (h *Handler) GetProgressHistory(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProgressWithMedia struct {
|
type ProgressWithMedia struct {
|
||||||
MediaItemID uuid.UUID
|
MediaItemID uuid.UUID `json:"media_item_id"`
|
||||||
Title string
|
Title string `json:"title"`
|
||||||
Author string
|
Author string `json:"author"`
|
||||||
CoverImagePath string
|
CoverImagePath string `json:"cover_image_path"`
|
||||||
Percentage float64
|
Percentage float64 `json:"percentage"`
|
||||||
CurrentPage int32
|
CurrentPage int32 `json:"current_page"`
|
||||||
TotalPages int32
|
TotalPages int32 `json:"total_pages"`
|
||||||
LastReadAt time.Time
|
LastReadAt time.Time `json:"last_read_at"`
|
||||||
Epubcfi string
|
Epubcfi string `json:"epubcfi"`
|
||||||
LastSyncDevice string
|
LastSyncDevice string `json:"last_sync_device"`
|
||||||
|
DeviceName string `json:"device_name,omitempty"`
|
||||||
|
DeviceType string `json:"device_type,omitempty"`
|
||||||
|
DeviceIcon string `json:"device_icon,omitempty"`
|
||||||
|
ProgressPercentage float64 `json:"-"`
|
||||||
|
EpubCFI string `json:"-"`
|
||||||
|
LastUpdated string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllProgress retrieves all progress for a user with sync source info
|
// GetAllProgress retrieves all progress for a user with sync source info
|
||||||
@@ -283,6 +303,11 @@ func (h *Handler) GetAllProgress(c echo.Context) error {
|
|||||||
deviceName = progress.LastSyncDevice.String
|
deviceName = progress.LastSyncDevice.String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastUpdated := ""
|
||||||
|
if progress.LastReadAt.Valid {
|
||||||
|
lastUpdated = progress.LastReadAt.Time.Format("2006-01-02 15:04")
|
||||||
|
}
|
||||||
|
|
||||||
progressList = append(progressList, ProgressWithMedia{
|
progressList = append(progressList, ProgressWithMedia{
|
||||||
MediaItemID: progress.MediaItemID.Bytes,
|
MediaItemID: progress.MediaItemID.Bytes,
|
||||||
Title: mediaItem.Title,
|
Title: mediaItem.Title,
|
||||||
@@ -294,6 +319,10 @@ func (h *Handler) GetAllProgress(c echo.Context) error {
|
|||||||
LastReadAt: progress.LastReadAt.Time,
|
LastReadAt: progress.LastReadAt.Time,
|
||||||
Epubcfi: epubcfi,
|
Epubcfi: epubcfi,
|
||||||
LastSyncDevice: deviceName,
|
LastSyncDevice: deviceName,
|
||||||
|
ProgressPercentage: progress.Percentage.Float64,
|
||||||
|
EpubCFI: epubcfi,
|
||||||
|
LastUpdated: lastUpdated,
|
||||||
|
DeviceIcon: getDeviceIcon(deviceName),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,4 +390,3 @@ func (h *Handler) GetAllProgressData(c echo.Context) ([]ProgressWithMedia, error
|
|||||||
|
|
||||||
return progressList, nil
|
return progressList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,18 +158,17 @@ func registerFrontendRoutes(cfg *Config) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return c.HTML(http.StatusInternalServerError, "Error loading user")
|
return c.HTML(http.StatusInternalServerError, "Error loading user")
|
||||||
}
|
}
|
||||||
deviceData, err := cfg.DeviceHandler.GetDevicesData(c)
|
devices, err := cfg.DeviceHandler.GetDevicesData(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.HTML(http.StatusInternalServerError, "Error loading devices")
|
return c.HTML(http.StatusInternalServerError, "Error loading devices")
|
||||||
}
|
}
|
||||||
pendingData, err := cfg.DeviceHandler.GetPendingRegistrationsData(c)
|
pendingMaps, err := cfg.DeviceHandler.GetPendingRegistrationsData(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.HTML(http.StatusInternalServerError, "Error loading pending")
|
return c.HTML(http.StatusInternalServerError, "Error loading pending")
|
||||||
}
|
}
|
||||||
devicesList := convertDevices(deviceData)
|
pendingList := convertPending(pendingMaps)
|
||||||
pendingList := convertPending(pendingData)
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err = templates.Devices(user, devicesList, pendingList).Render(c.Request().Context(), &buf)
|
err = templates.Devices(user, devices, pendingList).Render(c.Request().Context(), &buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package router
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"bookhoard/internal/handlers"
|
|
||||||
"bookhoard/templates"
|
"bookhoard/templates"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -43,29 +41,6 @@ func getTemplateUserWithTheme(c echo.Context, cfg *Config) (templates.User, erro
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertDevices(deviceInfos []handlers.DeviceInfo) []templates.DeviceData {
|
|
||||||
result := make([]templates.DeviceData, len(deviceInfos))
|
|
||||||
for i, d := range deviceInfos {
|
|
||||||
lastSync := ""
|
|
||||||
if d.LastSync != nil {
|
|
||||||
lastSync = d.LastSync.Format(time.RFC3339)
|
|
||||||
}
|
|
||||||
lastSeen := ""
|
|
||||||
if d.LastSeen != nil {
|
|
||||||
lastSeen = d.LastSeen.Format(time.RFC3339)
|
|
||||||
}
|
|
||||||
result[i] = templates.DeviceData{
|
|
||||||
ID: d.ID.String(),
|
|
||||||
DeviceName: d.DeviceName,
|
|
||||||
DeviceType: d.DeviceType,
|
|
||||||
SyncEnabled: d.SyncEnabled,
|
|
||||||
LastSync: lastSync,
|
|
||||||
LastSeen: lastSeen,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertPending(pending []map[string]interface{}) []templates.PendingRegistrationData {
|
func convertPending(pending []map[string]interface{}) []templates.PendingRegistrationData {
|
||||||
result := make([]templates.PendingRegistrationData, len(pending))
|
result := make([]templates.PendingRegistrationData, len(pending))
|
||||||
for i, p := range pending {
|
for i, p := range pending {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
templ CollectionRules(user User, collection CollectionDetailData) {
|
templ CollectionRules(user User, collection CollectionData) {
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ package templates
|
|||||||
import "github.com/a-h/templ"
|
import "github.com/a-h/templ"
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
func CollectionRules(user User, collection CollectionDetailData) templ.Component {
|
func CollectionRules(user User, collection CollectionData) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
|
import "bookhoard/internal/handlers"
|
||||||
|
|
||||||
templ Collection(user User, collections []CollectionData) {
|
templ Collection(user User, collections []CollectionData) {
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -210,7 +212,7 @@ templ Collection(user User, collections []CollectionData) {
|
|||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
|
|
||||||
templ CollectionDetail(user User, collection CollectionDetailData, books []BookData) {
|
templ CollectionDetail(user User, collection CollectionData, books []handlers.BookInfo) {
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ package templates
|
|||||||
import "github.com/a-h/templ"
|
import "github.com/a-h/templ"
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
import "bookhoard/internal/handlers"
|
||||||
|
|
||||||
func Collection(user User, collections []CollectionData) templ.Component {
|
func Collection(user User, collections []CollectionData) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
@@ -55,7 +57,7 @@ func Collection(user User, collections []CollectionData) templ.Component {
|
|||||||
var templ_7745c5c3_Var2 string
|
var templ_7745c5c3_Var2 string
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(col.Icon)
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(col.Icon)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 46, Col: 60}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 48, Col: 60}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -68,7 +70,7 @@ func Collection(user User, collections []CollectionData) templ.Component {
|
|||||||
var templ_7745c5c3_Var3 string
|
var templ_7745c5c3_Var3 string
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(col.Name)
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(col.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 58, Col: 109}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 60, Col: 109}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -81,7 +83,7 @@ func Collection(user User, collections []CollectionData) templ.Component {
|
|||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(col.Description)
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(col.Description)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 59, Col: 103}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 61, Col: 103}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -100,7 +102,7 @@ func Collection(user User, collections []CollectionData) templ.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func CollectionDetail(user User, collection CollectionDetailData, books []BookData) templ.Component {
|
func CollectionDetail(user User, collection CollectionData, books []handlers.BookInfo) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@@ -128,7 +130,7 @@ func CollectionDetail(user User, collection CollectionDetailData, books []BookDa
|
|||||||
var templ_7745c5c3_Var6 string
|
var templ_7745c5c3_Var6 string
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Name)
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 219, Col: 32}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 221, Col: 32}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -149,7 +151,7 @@ func CollectionDetail(user User, collection CollectionDetailData, books []BookDa
|
|||||||
var templ_7745c5c3_Var7 string
|
var templ_7745c5c3_Var7 string
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Icon)
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Icon)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 234, Col: 95}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 236, Col: 95}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -162,7 +164,7 @@ func CollectionDetail(user User, collection CollectionDetailData, books []BookDa
|
|||||||
var templ_7745c5c3_Var8 string
|
var templ_7745c5c3_Var8 string
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Name)
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 236, Col: 107}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 238, Col: 107}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -175,7 +177,7 @@ func CollectionDetail(user User, collection CollectionDetailData, books []BookDa
|
|||||||
var templ_7745c5c3_Var9 string
|
var templ_7745c5c3_Var9 string
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Description)
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Description)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 237, Col: 88}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 239, Col: 88}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -199,7 +201,7 @@ func CollectionDetail(user User, collection CollectionDetailData, books []BookDa
|
|||||||
var templ_7745c5c3_Var10 string
|
var templ_7745c5c3_Var10 string
|
||||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(book.Title)
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(book.Title)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 284, Col: 131}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 286, Col: 131}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -217,7 +219,7 @@ func CollectionDetail(user User, collection CollectionDetailData, books []BookDa
|
|||||||
var templ_7745c5c3_Var11 string
|
var templ_7745c5c3_Var11 string
|
||||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(book.Author)
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(book.Author)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 286, Col: 108}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 288, Col: 108}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
templ Devices(user User, devices []DeviceData, pendingRegistrations []PendingRegistrationData) {
|
import "bookhoard/internal/handlers"
|
||||||
|
|
||||||
|
templ Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []PendingRegistrationData) {
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -79,16 +81,16 @@ templ Devices(user User, devices []DeviceData, pendingRegistrations []PendingReg
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<span style="color: var(--text-secondary)">Last Sync</span>
|
<span style="color: var(--text-secondary)">Last Sync</span>
|
||||||
if device.LastSync != "" {
|
if device.LastSync != nil {
|
||||||
<span style="color: var(--text-primary)">{ device.LastSync }</span>
|
<span style="color: var(--text-primary)">{ device.LastSync.Format("2006-01-02 15:04") }</span>
|
||||||
} else {
|
} else {
|
||||||
<span style="color: var(--text-primary)">Never</span>
|
<span style="color: var(--text-primary)">Never</span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<span style="color: var(--text-secondary)">Last Seen</span>
|
<span style="color: var(--text-secondary)">Last Seen</span>
|
||||||
if device.LastSeen != "" {
|
if device.LastSeen != nil {
|
||||||
<span style="color: var(--text-primary)">{ device.LastSeen }</span>
|
<span style="color: var(--text-primary)">{ device.LastSeen.Format("2006-01-02 15:04") }</span>
|
||||||
} else {
|
} else {
|
||||||
<span style="color: var(--text-primary)">Never</span>
|
<span style="color: var(--text-primary)">Never</span>
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-12
@@ -8,7 +8,9 @@ package templates
|
|||||||
import "github.com/a-h/templ"
|
import "github.com/a-h/templ"
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
func Devices(user User, devices []DeviceData, pendingRegistrations []PendingRegistrationData) templ.Component {
|
import "bookhoard/internal/handlers"
|
||||||
|
|
||||||
|
func Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []PendingRegistrationData) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@@ -89,7 +91,7 @@ func Devices(user User, devices []DeviceData, pendingRegistrations []PendingRegi
|
|||||||
var templ_7745c5c3_Var2 string
|
var templ_7745c5c3_Var2 string
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(device.DeviceName)
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(device.DeviceName)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 69, Col: 102}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 71, Col: 102}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -102,7 +104,7 @@ func Devices(user User, devices []DeviceData, pendingRegistrations []PendingRegi
|
|||||||
var templ_7745c5c3_Var3 string
|
var templ_7745c5c3_Var3 string
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(device.DeviceType)
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(device.DeviceType)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 70, Col: 89}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 72, Col: 89}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -127,15 +129,15 @@ func Devices(user User, devices []DeviceData, pendingRegistrations []PendingRegi
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
if device.LastSync != "" {
|
if device.LastSync != nil {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<span style=\"color: var(--text-primary)\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<span style=\"color: var(--text-primary)\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(device.LastSync)
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(device.LastSync.Format("2006-01-02 15:04"))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 83, Col: 70}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 85, Col: 97}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -155,15 +157,15 @@ func Devices(user User, devices []DeviceData, pendingRegistrations []PendingRegi
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
if device.LastSeen != "" {
|
if device.LastSeen != nil {
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<span style=\"color: var(--text-primary)\">")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<span style=\"color: var(--text-primary)\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var5 string
|
var templ_7745c5c3_Var5 string
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(device.LastSeen)
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(device.LastSeen.Format("2006-01-02 15:04"))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 91, Col: 70}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 93, Col: 97}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -206,7 +208,7 @@ func Devices(user User, devices []DeviceData, pendingRegistrations []PendingRegi
|
|||||||
var templ_7745c5c3_Var6 string
|
var templ_7745c5c3_Var6 string
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(reg.DeviceName)
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(reg.DeviceName)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 110, Col: 87}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 112, Col: 87}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -219,7 +221,7 @@ func Devices(user User, devices []DeviceData, pendingRegistrations []PendingRegi
|
|||||||
var templ_7745c5c3_Var7 string
|
var templ_7745c5c3_Var7 string
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(reg.DeviceType)
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(reg.DeviceType)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 112, Col: 27}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 114, Col: 27}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -232,7 +234,7 @@ func Devices(user User, devices []DeviceData, pendingRegistrations []PendingRegi
|
|||||||
var templ_7745c5c3_Var8 string
|
var templ_7745c5c3_Var8 string
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(reg.ExpiresAt)
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(reg.ExpiresAt)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 112, Col: 58}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 114, Col: 58}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
templ Progress(user User, progressData []ProgressItemData) {
|
import "bookhoard/internal/handlers"
|
||||||
|
|
||||||
|
templ Progress(user User, progressData []handlers.ProgressWithMedia) {
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
|||||||
+14
-12
@@ -8,7 +8,9 @@ package templates
|
|||||||
import "github.com/a-h/templ"
|
import "github.com/a-h/templ"
|
||||||
import templruntime "github.com/a-h/templ/runtime"
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
func Progress(user User, progressData []ProgressItemData) templ.Component {
|
import "bookhoard/internal/handlers"
|
||||||
|
|
||||||
|
func Progress(user User, progressData []handlers.ProgressWithMedia) templ.Component {
|
||||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
@@ -55,7 +57,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var2 string
|
var templ_7745c5c3_Var2 string
|
||||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 44, Col: 121}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 46, Col: 121}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -73,7 +75,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var3 string
|
var templ_7745c5c3_Var3 string
|
||||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(item.Author)
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(item.Author)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 46, Col: 116}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 48, Col: 116}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -91,7 +93,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(item.ProgressPercentage)
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(item.ProgressPercentage)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 50, Col: 127}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 52, Col: 127}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -104,7 +106,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var5 string
|
var templ_7745c5c3_Var5 string
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(item.CurrentPage)
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(item.CurrentPage)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 64, Col: 62}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 66, Col: 62}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -117,7 +119,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var6 string
|
var templ_7745c5c3_Var6 string
|
||||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(item.TotalPages)
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(item.TotalPages)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 64, Col: 84}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 66, Col: 84}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -130,7 +132,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var7 string
|
var templ_7745c5c3_Var7 string
|
||||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(item.LastUpdated)
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(item.LastUpdated)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 69, Col: 96}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 71, Col: 96}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -143,7 +145,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var8 string
|
var templ_7745c5c3_Var8 string
|
||||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(item.DeviceIcon)
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(item.DeviceIcon)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 74, Col: 83}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 76, Col: 83}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -156,7 +158,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var9 string
|
var templ_7745c5c3_Var9 string
|
||||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(item.DeviceName)
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(item.DeviceName)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 75, Col: 102}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 77, Col: 102}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -174,7 +176,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var10 string
|
var templ_7745c5c3_Var10 string
|
||||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(item.DeviceName)
|
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(item.DeviceName)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 84, Col: 90}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 86, Col: 90}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -187,7 +189,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var11 string
|
var templ_7745c5c3_Var11 string
|
||||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(item.DeviceType)
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(item.DeviceType)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 85, Col: 68}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 87, Col: 68}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@@ -206,7 +208,7 @@ func Progress(user User, progressData []ProgressItemData) templ.Component {
|
|||||||
var templ_7745c5c3_Var12 string
|
var templ_7745c5c3_Var12 string
|
||||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(item.EpubCFI)
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(item.EpubCFI)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 93, Col: 65}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/progress.templ`, Line: 95, Col: 65}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
|||||||
@@ -23,21 +23,6 @@ type CollectionData struct {
|
|||||||
Icon string
|
Icon string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CollectionDetailData struct {
|
|
||||||
ID string
|
|
||||||
Name string
|
|
||||||
Description string
|
|
||||||
Color string
|
|
||||||
Icon string
|
|
||||||
}
|
|
||||||
|
|
||||||
type BookData struct {
|
|
||||||
MediaItemID string
|
|
||||||
Title string
|
|
||||||
Author string
|
|
||||||
CoverImagePath string
|
|
||||||
}
|
|
||||||
|
|
||||||
type LibraryData struct {
|
type LibraryData struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
@@ -45,17 +30,6 @@ type LibraryData struct {
|
|||||||
TypeName string
|
TypeName string
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeviceData struct {
|
|
||||||
ID string
|
|
||||||
DeviceName string
|
|
||||||
DeviceType string
|
|
||||||
SyncEnabled bool
|
|
||||||
AutoSync bool
|
|
||||||
SyncFrequency int32
|
|
||||||
LastSync string
|
|
||||||
LastSeen string
|
|
||||||
}
|
|
||||||
|
|
||||||
type PendingRegistrationData struct {
|
type PendingRegistrationData struct {
|
||||||
RegistrationID string
|
RegistrationID string
|
||||||
DeviceName string
|
DeviceName string
|
||||||
@@ -96,21 +70,6 @@ type DeviceShelfMappingData struct {
|
|||||||
CreatedAt string
|
CreatedAt string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProgressItemData struct {
|
|
||||||
MediaItemID string
|
|
||||||
Title string
|
|
||||||
Author string
|
|
||||||
CoverImagePath string
|
|
||||||
CurrentPage int32
|
|
||||||
TotalPages int32
|
|
||||||
ProgressPercentage float64
|
|
||||||
LastUpdated string
|
|
||||||
DeviceName string
|
|
||||||
DeviceType string
|
|
||||||
DeviceIcon string
|
|
||||||
EpubCFI string
|
|
||||||
}
|
|
||||||
|
|
||||||
type UnlinkedBookData struct {
|
type UnlinkedBookData struct {
|
||||||
ProgressID string
|
ProgressID string
|
||||||
DeviceID string
|
DeviceID string
|
||||||
|
|||||||
Reference in New Issue
Block a user