Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
122038123c | ||
|
|
19fc222fb3 | ||
|
|
e047a627a8 | ||
|
|
668aee4e22 | ||
|
|
2e2121515e | ||
|
|
3c3f16ea1b | ||
|
|
2d01ec52fe | ||
|
|
6e2d304b35 | ||
|
|
43d8afc6cd | ||
|
|
9b31dc6f68 | ||
|
|
0e8b11504e | ||
|
|
59cb0e3439 |
@@ -243,7 +243,7 @@ type Querier interface {
|
||||
GetRefreshToken(ctx context.Context, token pgtype.UUID) (GetRefreshTokenRow, error)
|
||||
GetSavedFilterByID(ctx context.Context, arg GetSavedFilterByIDParams) (SavedFilters, error)
|
||||
GetSavedFilters(ctx context.Context, arg GetSavedFiltersParams) ([]SavedFilters, error)
|
||||
GetSeriesBooks(ctx context.Context, arg GetSeriesBooksParams) ([]MediaItems, error)
|
||||
GetSeriesBooks(ctx context.Context, series pgtype.Text) ([]MediaItems, error)
|
||||
GetSeriesCovers(ctx context.Context, arg GetSeriesCoversParams) ([]GetSeriesCoversRow, error)
|
||||
GetStuckSyncQueueItems(ctx context.Context) ([]SyncQueue, error)
|
||||
GetSyncConflict(ctx context.Context, id pgtype.UUID) (SyncConflicts, error)
|
||||
|
||||
@@ -2116,7 +2116,7 @@ const GetCollectionItemsForDashboard = `-- name: GetCollectionItemsForDashboard
|
||||
SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence, ci.excluded FROM media_items mi
|
||||
INNER JOIN collection_items ci ON ci.media_item_id = mi.id
|
||||
WHERE ci.collection_id = $1
|
||||
AND mi.library_id = $2
|
||||
AND ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||
ORDER BY ci.added_at DESC
|
||||
LIMIT $3
|
||||
`
|
||||
@@ -2124,7 +2124,7 @@ LIMIT $3
|
||||
type GetCollectionItemsForDashboardParams struct {
|
||||
CollectionID pgtype.UUID `db:"collection_id" json:"collection_id"`
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
Limit int32 `db:"limit" json:"limit"`
|
||||
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||
}
|
||||
|
||||
type GetCollectionItemsForDashboardRow struct {
|
||||
@@ -2411,25 +2411,25 @@ SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.fi
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT ON (media_item_id) media_item_id, last_read_at
|
||||
FROM reading_progress
|
||||
WHERE user_id = $2
|
||||
WHERE user_id = $1
|
||||
AND percentage > 0
|
||||
AND percentage < 1
|
||||
ORDER BY media_item_id, last_read_at DESC
|
||||
) rp ON rp.media_item_id = mi.id
|
||||
WHERE mi.library_id = $1
|
||||
WHERE ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||
ORDER BY rp.last_read_at DESC
|
||||
LIMIT $3
|
||||
`
|
||||
|
||||
type GetContinueReadingItemsParams struct {
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
Limit int32 `db:"limit" json:"limit"`
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||
}
|
||||
|
||||
// Smart section queries (for system collections)
|
||||
func (q *Queries) GetContinueReadingItems(ctx context.Context, arg GetContinueReadingItemsParams) ([]MediaItems, error) {
|
||||
rows, err := q.db.Query(ctx, GetContinueReadingItems, arg.LibraryID, arg.UserID, arg.Limit)
|
||||
rows, err := q.db.Query(ctx, GetContinueReadingItems, arg.UserID, arg.LibraryID, arg.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2520,7 +2520,7 @@ WITH user_series_progress AS (
|
||||
WHERE rp.user_id = $2
|
||||
AND rp.percentage > 0
|
||||
AND mi.series IS NOT NULL AND mi.series != ''
|
||||
AND mi.library_id = $1
|
||||
AND ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
||||
GROUP BY mi.series
|
||||
),
|
||||
next_books AS (
|
||||
@@ -2528,19 +2528,19 @@ next_books AS (
|
||||
usp.last_read_at
|
||||
FROM media_items mi
|
||||
JOIN user_series_progress usp ON mi.series = usp.series
|
||||
WHERE mi.library_id = $1
|
||||
WHERE ($3::uuid IS NULL OR mi.library_id = $3::uuid)
|
||||
AND (mi.series_number > usp.max_read_number OR usp.max_read_number IS NULL)
|
||||
ORDER BY mi.series, mi.series_number ASC NULLS LAST
|
||||
)
|
||||
SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence, last_read_at FROM next_books
|
||||
ORDER BY last_read_at DESC NULLS LAST
|
||||
LIMIT $3
|
||||
LIMIT $1
|
||||
`
|
||||
|
||||
type GetContinueSeriesItemsParams struct {
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
Limit int32 `db:"limit" json:"limit"`
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
}
|
||||
|
||||
type GetContinueSeriesItemsRow struct {
|
||||
@@ -2609,7 +2609,7 @@ type GetContinueSeriesItemsRow struct {
|
||||
}
|
||||
|
||||
func (q *Queries) GetContinueSeriesItems(ctx context.Context, arg GetContinueSeriesItemsParams) ([]GetContinueSeriesItemsRow, error) {
|
||||
rows, err := q.db.Query(ctx, GetContinueSeriesItems, arg.LibraryID, arg.UserID, arg.Limit)
|
||||
rows, err := q.db.Query(ctx, GetContinueSeriesItems, arg.Limit, arg.UserID, arg.LibraryID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2694,7 +2694,7 @@ func (q *Queries) GetContinueSeriesItems(ctx context.Context, arg GetContinueSer
|
||||
const GetDashboardPreferences = `-- name: GetDashboardPreferences :one
|
||||
|
||||
SELECT id, user_id, library_id, hidden_collections, collection_order, items_per_section, created_at, updated_at FROM user_dashboard_preferences
|
||||
WHERE user_id = $1 AND library_id = $2
|
||||
WHERE user_id = $1 AND ($2::uuid IS NULL OR library_id = $2::uuid)
|
||||
`
|
||||
|
||||
type GetDashboardPreferencesParams struct {
|
||||
@@ -3115,16 +3115,16 @@ SELECT series, COUNT(*) as book_count,
|
||||
MAX(series_count) as total_in_series,
|
||||
MAX(created_at) as last_entry_at
|
||||
FROM media_items
|
||||
WHERE library_id = $1 AND series IS NOT NULL AND series != ''
|
||||
WHERE ($1::uuid IS NULL OR library_id = $1::uuid) AND series IS NOT NULL AND series != ''
|
||||
GROUP BY series
|
||||
ORDER BY MAX(created_at) DESC
|
||||
LIMIT $2 OFFSET $3
|
||||
LIMIT $3 OFFSET $2
|
||||
`
|
||||
|
||||
type GetDistinctSeriesParams struct {
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
Limit int32 `db:"limit" json:"limit"`
|
||||
Offset int32 `db:"offset" json:"offset"`
|
||||
Offset pgtype.Int4 `db:"offset" json:"offset"`
|
||||
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||
}
|
||||
|
||||
type GetDistinctSeriesRow struct {
|
||||
@@ -3135,7 +3135,7 @@ type GetDistinctSeriesRow struct {
|
||||
}
|
||||
|
||||
func (q *Queries) GetDistinctSeries(ctx context.Context, arg GetDistinctSeriesParams) ([]GetDistinctSeriesRow, error) {
|
||||
rows, err := q.db.Query(ctx, GetDistinctSeries, arg.LibraryID, arg.Limit, arg.Offset)
|
||||
rows, err := q.db.Query(ctx, GetDistinctSeries, arg.LibraryID, arg.Offset, arg.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -3162,7 +3162,7 @@ func (q *Queries) GetDistinctSeries(ctx context.Context, arg GetDistinctSeriesPa
|
||||
const GetDistinctSeriesCount = `-- name: GetDistinctSeriesCount :one
|
||||
SELECT COUNT(DISTINCT series)::int
|
||||
FROM media_items
|
||||
WHERE library_id = $1 AND series IS NOT NULL AND series != ''
|
||||
WHERE ($1::uuid IS NULL OR library_id = $1::uuid) AND series IS NOT NULL AND series != ''
|
||||
`
|
||||
|
||||
func (q *Queries) GetDistinctSeriesCount(ctx context.Context, libraryID pgtype.UUID) (int32, error) {
|
||||
@@ -3781,7 +3781,7 @@ func (q *Queries) GetLibraryFolders(ctx context.Context, libraryID pgtype.UUID)
|
||||
|
||||
const GetLibraryItems = `-- name: GetLibraryItems :many
|
||||
SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi
|
||||
WHERE mi.library_id = $1
|
||||
WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid)
|
||||
ORDER BY mi.created_at DESC
|
||||
`
|
||||
|
||||
@@ -4967,7 +4967,7 @@ func (q *Queries) GetNextRetryTime(ctx context.Context) (interface{}, error) {
|
||||
|
||||
const GetNotStartedItems = `-- name: GetNotStartedItems :many
|
||||
SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi
|
||||
WHERE mi.library_id = $1
|
||||
WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM reading_progress rp
|
||||
WHERE rp.media_item_id = mi.id
|
||||
@@ -4981,7 +4981,7 @@ LIMIT $3
|
||||
type GetNotStartedItemsParams struct {
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
Limit int32 `db:"limit" json:"limit"`
|
||||
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetNotStartedItems(ctx context.Context, arg GetNotStartedItemsParams) ([]MediaItems, error) {
|
||||
@@ -5366,14 +5366,14 @@ func (q *Queries) GetReadingSpeed(ctx context.Context, arg GetReadingSpeedParams
|
||||
|
||||
const GetRecentlyAddedItems = `-- name: GetRecentlyAddedItems :many
|
||||
SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.imported_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.manga_type, mi.reading_direction, mi.series_count, mi.volume, mi.imprint, mi.age_rating, mi.web_url, mi.story_arc, mi.is_black_and_white, mi.metadata_notes, mi.community_rating, mi.alternate_info, mi.scan_information, mi.summary, mi.chapter_metadata, mi.library_type_name, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi
|
||||
WHERE mi.library_id = $1
|
||||
WHERE ($1::uuid IS NULL OR mi.library_id = $1::uuid)
|
||||
ORDER BY mi.imported_at DESC NULLS LAST, mi.created_at DESC
|
||||
LIMIT $2
|
||||
`
|
||||
|
||||
type GetRecentlyAddedItemsParams struct {
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
Limit int32 `db:"limit" json:"limit"`
|
||||
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetRecentlyAddedItems(ctx context.Context, arg GetRecentlyAddedItemsParams) ([]MediaItems, error) {
|
||||
@@ -5463,23 +5463,23 @@ SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.fi
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT ON (media_item_id) media_item_id, last_read_at
|
||||
FROM reading_progress
|
||||
WHERE user_id = $2
|
||||
WHERE user_id = $1
|
||||
AND percentage >= 1
|
||||
ORDER BY media_item_id, last_read_at DESC
|
||||
) rp ON rp.media_item_id = mi.id
|
||||
WHERE mi.library_id = $1
|
||||
WHERE ($2::uuid IS NULL OR mi.library_id = $2::uuid)
|
||||
ORDER BY rp.last_read_at DESC
|
||||
LIMIT $3
|
||||
`
|
||||
|
||||
type GetRecentlyReadItemsParams struct {
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||
Limit int32 `db:"limit" json:"limit"`
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetRecentlyReadItems(ctx context.Context, arg GetRecentlyReadItemsParams) ([]MediaItems, error) {
|
||||
rows, err := q.db.Query(ctx, GetRecentlyReadItems, arg.LibraryID, arg.UserID, arg.Limit)
|
||||
rows, err := q.db.Query(ctx, GetRecentlyReadItems, arg.UserID, arg.LibraryID, arg.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -5662,17 +5662,12 @@ func (q *Queries) GetSavedFilters(ctx context.Context, arg GetSavedFiltersParams
|
||||
|
||||
const GetSeriesBooks = `-- name: GetSeriesBooks :many
|
||||
SELECT id, library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id, created_at, imported_at, updated_at, format_group, format_mimetype, is_reflowable, has_fixed_layout, total_characters, chapter_count, entitlement_id, revision_number, kobo_content_id, kobo_metadata, manga_type, reading_direction, series_count, volume, imprint, age_rating, web_url, story_arc, is_black_and_white, metadata_notes, community_rating, alternate_info, scan_information, summary, chapter_metadata, library_type_name, tags_search, contributors_search, file_sha256, opf_identifier, opf_uuid, hash_confidence FROM media_items
|
||||
WHERE library_id = $1 AND series = $2
|
||||
WHERE series = $1
|
||||
ORDER BY series_number ASC NULLS LAST
|
||||
`
|
||||
|
||||
type GetSeriesBooksParams struct {
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
Series pgtype.Text `db:"series" json:"series"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetSeriesBooks(ctx context.Context, arg GetSeriesBooksParams) ([]MediaItems, error) {
|
||||
rows, err := q.db.Query(ctx, GetSeriesBooks, arg.LibraryID, arg.Series)
|
||||
func (q *Queries) GetSeriesBooks(ctx context.Context, series pgtype.Text) ([]MediaItems, error) {
|
||||
rows, err := q.db.Query(ctx, GetSeriesBooks, series)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -5756,7 +5751,7 @@ func (q *Queries) GetSeriesBooks(ctx context.Context, arg GetSeriesBooksParams)
|
||||
const GetSeriesCovers = `-- name: GetSeriesCovers :many
|
||||
SELECT cover_image_path, library_id
|
||||
FROM media_items
|
||||
WHERE library_id = $1 AND series = $2 AND cover_image_path IS NOT NULL AND cover_image_path != ''
|
||||
WHERE ($1::uuid IS NULL OR library_id = $1::uuid) AND series = $2 AND cover_image_path IS NOT NULL AND cover_image_path != ''
|
||||
ORDER BY series_number ASC NULLS LAST
|
||||
LIMIT $3
|
||||
`
|
||||
@@ -5764,7 +5759,7 @@ LIMIT $3
|
||||
type GetSeriesCoversParams struct {
|
||||
LibraryID pgtype.UUID `db:"library_id" json:"library_id"`
|
||||
Series pgtype.Text `db:"series" json:"series"`
|
||||
Limit int32 `db:"limit" json:"limit"`
|
||||
Limit pgtype.Int4 `db:"limit" json:"limit"`
|
||||
}
|
||||
|
||||
type GetSeriesCoversRow struct {
|
||||
|
||||
@@ -1830,7 +1830,7 @@ LIMIT $1 OFFSET $2;
|
||||
-- Dashboard preferences queries
|
||||
-- name: GetDashboardPreferences :one
|
||||
SELECT * FROM user_dashboard_preferences
|
||||
WHERE user_id = $1 AND library_id = $2;
|
||||
WHERE user_id = sqlc.narg('user_id') AND (sqlc.narg('library_id')::uuid IS NULL OR library_id = sqlc.narg('library_id')::uuid);
|
||||
|
||||
-- name: UpsertDashboardPreferences :one
|
||||
INSERT INTO user_dashboard_preferences (user_id, library_id, hidden_collections, collection_order, items_per_section)
|
||||
@@ -1894,57 +1894,57 @@ SELECT mi.* FROM media_items mi
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT ON (media_item_id) media_item_id, last_read_at
|
||||
FROM reading_progress
|
||||
WHERE user_id = $2
|
||||
WHERE user_id = sqlc.narg('user_id')
|
||||
AND percentage > 0
|
||||
AND percentage < 1
|
||||
ORDER BY media_item_id, last_read_at DESC
|
||||
) rp ON rp.media_item_id = mi.id
|
||||
WHERE mi.library_id = $1
|
||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||
ORDER BY rp.last_read_at DESC
|
||||
LIMIT $3;
|
||||
LIMIT sqlc.narg('limit');
|
||||
|
||||
-- name: GetRecentlyAddedItems :many
|
||||
SELECT mi.* FROM media_items mi
|
||||
WHERE mi.library_id = $1
|
||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||
ORDER BY mi.imported_at DESC NULLS LAST, mi.created_at DESC
|
||||
LIMIT $2;
|
||||
LIMIT sqlc.narg('limit');
|
||||
|
||||
-- name: GetRecentlyReadItems :many
|
||||
SELECT mi.* FROM media_items mi
|
||||
INNER JOIN (
|
||||
SELECT DISTINCT ON (media_item_id) media_item_id, last_read_at
|
||||
FROM reading_progress
|
||||
WHERE user_id = $2
|
||||
WHERE user_id = sqlc.narg('user_id')
|
||||
AND percentage >= 1
|
||||
ORDER BY media_item_id, last_read_at DESC
|
||||
) rp ON rp.media_item_id = mi.id
|
||||
WHERE mi.library_id = $1
|
||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||
ORDER BY rp.last_read_at DESC
|
||||
LIMIT $3;
|
||||
LIMIT sqlc.narg('limit');
|
||||
|
||||
-- name: GetNotStartedItems :many
|
||||
SELECT mi.* FROM media_items mi
|
||||
WHERE mi.library_id = $1
|
||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM reading_progress rp
|
||||
WHERE rp.media_item_id = mi.id
|
||||
AND rp.user_id = $2
|
||||
AND rp.user_id = sqlc.narg('user_id')
|
||||
AND rp.percentage > 0
|
||||
)
|
||||
ORDER BY mi.created_at DESC
|
||||
LIMIT $3;
|
||||
LIMIT sqlc.narg('limit');
|
||||
|
||||
-- name: GetCollectionItemsForDashboard :many
|
||||
SELECT mi.*, ci.excluded FROM media_items mi
|
||||
INNER JOIN collection_items ci ON ci.media_item_id = mi.id
|
||||
WHERE ci.collection_id = $1
|
||||
AND mi.library_id = $2
|
||||
WHERE ci.collection_id = sqlc.narg('collection_id')
|
||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||
ORDER BY ci.added_at DESC
|
||||
LIMIT $3;
|
||||
LIMIT sqlc.narg('limit');
|
||||
|
||||
-- name: GetLibraryItems :many
|
||||
SELECT mi.* FROM media_items mi
|
||||
WHERE mi.library_id = $1
|
||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||
ORDER BY mi.created_at DESC;
|
||||
|
||||
-- name: GetDistinctSeries :many
|
||||
@@ -1952,26 +1952,26 @@ SELECT series, COUNT(*) as book_count,
|
||||
MAX(series_count) as total_in_series,
|
||||
MAX(created_at) as last_entry_at
|
||||
FROM media_items
|
||||
WHERE library_id = $1 AND series IS NOT NULL AND series != ''
|
||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR library_id = sqlc.narg('library_id')::uuid) AND series IS NOT NULL AND series != ''
|
||||
GROUP BY series
|
||||
ORDER BY MAX(created_at) DESC
|
||||
LIMIT $2 OFFSET $3;
|
||||
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');
|
||||
|
||||
-- name: GetDistinctSeriesCount :one
|
||||
SELECT COUNT(DISTINCT series)::int
|
||||
FROM media_items
|
||||
WHERE library_id = $1 AND series IS NOT NULL AND series != '';
|
||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR library_id = sqlc.narg('library_id')::uuid) AND series IS NOT NULL AND series != '';
|
||||
|
||||
-- name: GetSeriesCovers :many
|
||||
SELECT cover_image_path, library_id
|
||||
FROM media_items
|
||||
WHERE library_id = $1 AND series = $2 AND cover_image_path IS NOT NULL AND cover_image_path != ''
|
||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR library_id = sqlc.narg('library_id')::uuid) AND series = sqlc.narg('series') AND cover_image_path IS NOT NULL AND cover_image_path != ''
|
||||
ORDER BY series_number ASC NULLS LAST
|
||||
LIMIT $3;
|
||||
LIMIT sqlc.narg('limit');
|
||||
|
||||
-- name: GetSeriesBooks :many
|
||||
SELECT * FROM media_items
|
||||
WHERE library_id = $1 AND series = $2
|
||||
WHERE series = sqlc.narg('series')
|
||||
ORDER BY series_number ASC NULLS LAST;
|
||||
|
||||
-- name: GetContinueSeriesItems :many
|
||||
@@ -1981,10 +1981,10 @@ WITH user_series_progress AS (
|
||||
MAX(rp.last_read_at) as last_read_at
|
||||
FROM reading_progress rp
|
||||
JOIN media_items mi ON mi.id = rp.media_item_id
|
||||
WHERE rp.user_id = $2
|
||||
WHERE rp.user_id = sqlc.narg('user_id')
|
||||
AND rp.percentage > 0
|
||||
AND mi.series IS NOT NULL AND mi.series != ''
|
||||
AND mi.library_id = $1
|
||||
AND (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||
GROUP BY mi.series
|
||||
),
|
||||
next_books AS (
|
||||
@@ -1992,13 +1992,13 @@ next_books AS (
|
||||
usp.last_read_at
|
||||
FROM media_items mi
|
||||
JOIN user_series_progress usp ON mi.series = usp.series
|
||||
WHERE mi.library_id = $1
|
||||
WHERE (sqlc.narg('library_id')::uuid IS NULL OR mi.library_id = sqlc.narg('library_id')::uuid)
|
||||
AND (mi.series_number > usp.max_read_number OR usp.max_read_number IS NULL)
|
||||
ORDER BY mi.series, mi.series_number ASC NULLS LAST
|
||||
)
|
||||
SELECT * FROM next_books
|
||||
ORDER BY last_read_at DESC NULLS LAST
|
||||
LIMIT $3;
|
||||
LIMIT sqlc.narg('limit');
|
||||
|
||||
-- name: GetSavedFilters :many
|
||||
SELECT * FROM saved_filters
|
||||
|
||||
@@ -138,6 +138,7 @@ func (h *CollectionHandler) CreateCollection(c *echo.Context) error {
|
||||
func (h *CollectionHandler) GetCollections(c *echo.Context) error {
|
||||
includeAuto := c.QueryParam("include_auto") == "true"
|
||||
sortBy := c.QueryParam("sort_by")
|
||||
libraryID := c.QueryParam("library_id")
|
||||
|
||||
collections, err := h.GetCollectionsData(c)
|
||||
if err != nil {
|
||||
@@ -152,6 +153,7 @@ func (h *CollectionHandler) GetCollections(c *echo.Context) error {
|
||||
Icon string `json:"icon"`
|
||||
AutoAssignRules json.RawMessage `json:"auto_assign_rules"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
BookCount int `json:"book_count"`
|
||||
}
|
||||
|
||||
response := make([]CollectionResponse, 0, len(collections))
|
||||
@@ -159,6 +161,26 @@ func (h *CollectionHandler) GetCollections(c *echo.Context) error {
|
||||
if !includeAuto && len(col.AutoAssignRules) > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
bookCount := 0
|
||||
if libraryID != "" {
|
||||
libUUID, libErr := uuid.Parse(libraryID)
|
||||
if libErr == nil {
|
||||
items, countErr := h.db.GetCollectionItemsForDashboard(c.Request().Context(), database.GetCollectionItemsForDashboardParams{
|
||||
CollectionID: pgtype.UUID{Bytes: col.ID.Bytes, Valid: true},
|
||||
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
|
||||
Limit: pgtype.Int4{Int32: 10000, Valid: true},
|
||||
})
|
||||
if countErr == nil {
|
||||
for _, item := range items {
|
||||
if !item.Excluded.Valid || !item.Excluded.Bool {
|
||||
bookCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = append(response, CollectionResponse{
|
||||
ID: col.ID.Bytes,
|
||||
Name: col.Name,
|
||||
@@ -167,6 +189,7 @@ func (h *CollectionHandler) GetCollections(c *echo.Context) error {
|
||||
Icon: textToString(col.Icon),
|
||||
AutoAssignRules: col.AutoAssignRules,
|
||||
CreatedAt: col.CreatedAt.Time.String(),
|
||||
BookCount: bookCount,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -197,19 +220,84 @@ func (h *CollectionHandler) GetCollection(c *echo.Context) error {
|
||||
return c.JSON(http.StatusNotFound, map[string]string{"error": "collection not found"})
|
||||
}
|
||||
|
||||
books, err := h.GetCollectionBooksData(c, collectionID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
libraryID := c.QueryParam("library_id")
|
||||
var bookList []BookInfo
|
||||
|
||||
var libUUID pgtype.UUID
|
||||
if libraryID != "" {
|
||||
parsed, parseErr := uuid.Parse(libraryID)
|
||||
if parseErr != nil {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
|
||||
}
|
||||
libUUID = pgtype.UUID{Bytes: parsed, Valid: true}
|
||||
}
|
||||
|
||||
bookList := make([]BookInfo, 0, len(books))
|
||||
for _, book := range books {
|
||||
bookList = append(bookList, BookInfo{
|
||||
MediaItemID: uuid.UUID(book.MediaItemID.Bytes).String(),
|
||||
Title: book.Title,
|
||||
Author: textToString(book.Author),
|
||||
CoverImagePath: utils.ResolveMediaURL(book.LibraryID, book.CoverImagePath),
|
||||
})
|
||||
if collection.QueryType.Valid && collection.QueryType.String != "" {
|
||||
user := c.Get("user").(database.Users)
|
||||
userUUID := uuid.UUID(user.ID.Bytes)
|
||||
dashboardSvc := services.NewDashboardService(h.db)
|
||||
sections, secErr := dashboardSvc.GetDashboardSections(c.Request().Context(), userUUID, libUUID, 1000, []string{}, []string{})
|
||||
if secErr != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": secErr.Error()})
|
||||
}
|
||||
for _, section := range sections {
|
||||
if section.CollectionID == collectionID {
|
||||
bookCards := make([]BookInfo, len(section.Items))
|
||||
for i, item := range section.Items {
|
||||
itemUUID, _ := uuid.FromBytes(item.ID.Bytes[0:16])
|
||||
bookCards[i] = BookInfo{
|
||||
MediaItemID: itemUUID.String(),
|
||||
Title: item.Title,
|
||||
Author: textToString(item.Author),
|
||||
CoverImagePath: utils.ResolveMediaURL(item.LibraryID, item.CoverImagePath),
|
||||
}
|
||||
}
|
||||
bookList = bookCards
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if libUUID.Valid {
|
||||
collItems, collErr := h.db.GetCollectionItemsForDashboard(c.Request().Context(),
|
||||
database.GetCollectionItemsForDashboardParams{
|
||||
CollectionID: pgtype.UUID{Bytes: collectionID, Valid: true},
|
||||
LibraryID: libUUID,
|
||||
Limit: pgtype.Int4{Int32: 10000, Valid: true},
|
||||
})
|
||||
if collErr != nil {
|
||||
bookList = []BookInfo{}
|
||||
} else {
|
||||
var validItems []database.GetCollectionItemsForDashboardRow
|
||||
for _, item := range collItems {
|
||||
if !item.Excluded.Valid || !item.Excluded.Bool {
|
||||
validItems = append(validItems, item)
|
||||
}
|
||||
}
|
||||
bookCards := make([]BookInfo, len(validItems))
|
||||
for i, item := range validItems {
|
||||
itemUUID, _ := uuid.FromBytes(item.ID.Bytes[0:16])
|
||||
bookCards[i] = BookInfo{
|
||||
MediaItemID: itemUUID.String(),
|
||||
Title: item.Title,
|
||||
Author: textToString(item.Author),
|
||||
CoverImagePath: utils.ResolveMediaURL(item.LibraryID, item.CoverImagePath),
|
||||
}
|
||||
}
|
||||
bookList = bookCards
|
||||
}
|
||||
} else {
|
||||
books, booksErr := h.GetCollectionBooksData(c, collectionID)
|
||||
if booksErr != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": booksErr.Error()})
|
||||
}
|
||||
bookList = make([]BookInfo, 0, len(books))
|
||||
for _, book := range books {
|
||||
bookList = append(bookList, BookInfo{
|
||||
MediaItemID: uuid.UUID(book.MediaItemID.Bytes).String(),
|
||||
Title: book.Title,
|
||||
Author: textToString(book.Author),
|
||||
CoverImagePath: utils.ResolveMediaURL(book.LibraryID, book.CoverImagePath),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var viewSettings map[string]interface{}
|
||||
|
||||
@@ -30,12 +30,13 @@ func (h *DashboardHandler) GetSections(c *echo.Context) error {
|
||||
userUUID := uuid.UUID(user.ID.Bytes)
|
||||
|
||||
libraryID := c.QueryParam("library_id")
|
||||
if libraryID == "" {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"})
|
||||
}
|
||||
libUUID, err := uuid.Parse(libraryID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
|
||||
var libUUID pgtype.UUID
|
||||
if libraryID != "" {
|
||||
parsed, err := uuid.Parse(libraryID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
|
||||
}
|
||||
libUUID = pgtype.UUID{Bytes: parsed, Valid: true}
|
||||
}
|
||||
|
||||
prefs, _ := h.dashboardService.GetDashboardPreferences(c.Request().Context(), userUUID, libUUID)
|
||||
@@ -202,14 +203,15 @@ func (h *DashboardHandler) GetPreferences(c *echo.Context) error {
|
||||
userUUID := uuid.UUID(user.ID.Bytes)
|
||||
libraryID := c.QueryParam("library_id")
|
||||
|
||||
if libraryID == "" {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"})
|
||||
var libUUID pgtype.UUID
|
||||
if libraryID != "" {
|
||||
parsed, err := uuid.Parse(libraryID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
|
||||
}
|
||||
libUUID = pgtype.UUID{Bytes: parsed, Valid: true}
|
||||
}
|
||||
|
||||
libUUID, err := uuid.Parse(libraryID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
|
||||
}
|
||||
prefs, err := h.dashboardService.GetDashboardPreferences(c.Request().Context(), userUUID, libUUID)
|
||||
if err != nil {
|
||||
// Return default preferences instead of 404 when none exist
|
||||
|
||||
+10
-17
@@ -9,6 +9,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
@@ -24,12 +25,13 @@ func NewSeriesHandler(db *database.Queries) *SeriesHandler {
|
||||
|
||||
func (h *SeriesHandler) GetSeries(c *echo.Context) error {
|
||||
libraryID := c.QueryParam("library_id")
|
||||
if libraryID == "" {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"})
|
||||
}
|
||||
libUUID, err := uuid.Parse(libraryID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
|
||||
var libUUID pgtype.UUID
|
||||
if libraryID != "" {
|
||||
parsed, err := uuid.Parse(libraryID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
|
||||
}
|
||||
libUUID = pgtype.UUID{Bytes: parsed, Valid: true}
|
||||
}
|
||||
|
||||
limit := 20
|
||||
@@ -81,21 +83,12 @@ func (h *SeriesHandler) GetSeries(c *echo.Context) error {
|
||||
}
|
||||
|
||||
func (h *SeriesHandler) GetSeriesBooks(c *echo.Context) error {
|
||||
libraryID := c.QueryParam("library_id")
|
||||
if libraryID == "" {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "library_id required"})
|
||||
}
|
||||
libUUID, err := uuid.Parse(libraryID)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid library_id"})
|
||||
}
|
||||
|
||||
seriesName := c.QueryParam("name")
|
||||
if seriesName == "" {
|
||||
return c.JSON(http.StatusBadRequest, map[string]string{"error": "name required"})
|
||||
}
|
||||
|
||||
books, err := h.seriesService.GetSeriesBooks(c.Request().Context(), libUUID, seriesName)
|
||||
books, err := h.seriesService.GetSeriesBooks(c.Request().Context(), seriesName)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to load series books"})
|
||||
}
|
||||
@@ -118,7 +111,7 @@ func (h *SeriesHandler) GetSeriesBooks(c *echo.Context) error {
|
||||
})
|
||||
}
|
||||
|
||||
func GetSeriesCardsData(ctx context.Context, db *database.Queries, libraryID uuid.UUID, limit, offset int) ([]services.SeriesInfo, int, error) {
|
||||
func GetSeriesCardsData(ctx context.Context, db *database.Queries, libraryID pgtype.UUID, limit, offset int) ([]services.SeriesInfo, int, error) {
|
||||
svc := services.NewSeriesService(db)
|
||||
return svc.GetSeriesPage(ctx, libraryID, limit, offset)
|
||||
}
|
||||
|
||||
+135
-296
@@ -111,14 +111,6 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
// Protected frontend routes (no /api prefix)
|
||||
frontendProtected := e.Group("", jwtMiddleware, ensureUserExistsMiddleware(cfg))
|
||||
|
||||
// Helper to extract text from pgtype.Text
|
||||
getText := func(t pgtype.Text) string {
|
||||
if t.Valid {
|
||||
return t.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Series browse page
|
||||
frontendProtected.GET("/series", func(c *echo.Context) error {
|
||||
user, err := getTemplateUserWithTheme(c, cfg)
|
||||
@@ -128,36 +120,11 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
|
||||
var errorMsg string
|
||||
|
||||
libraryID := c.QueryParam("library_id")
|
||||
userUUID, _ := uuid.Parse(user.ID)
|
||||
if libraryID == "" {
|
||||
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
|
||||
if err == nil && len(libraries) > 0 {
|
||||
libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16])
|
||||
libraryID = libUUID.String()
|
||||
} else {
|
||||
errorMsg = "No libraries available"
|
||||
}
|
||||
}
|
||||
|
||||
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
|
||||
if err != nil {
|
||||
log.Printf("GetUserVisibleLibraries failed: %v", err)
|
||||
libraries = []database.GetUserVisibleLibrariesRow{}
|
||||
if errorMsg == "" {
|
||||
errorMsg = "Error loading libraries"
|
||||
}
|
||||
}
|
||||
|
||||
libData := make([]templates.LibraryData, len(libraries))
|
||||
for i, lib := range libraries {
|
||||
libUUID, _ := uuid.FromBytes(lib.ID.Bytes[0:16])
|
||||
libData[i] = templates.LibraryData{
|
||||
ID: libUUID.String(),
|
||||
Name: lib.Name,
|
||||
Description: getText(lib.Description),
|
||||
TypeName: lib.TypeName,
|
||||
}
|
||||
libRes := resolveLibrary(c, cfg, user.ID)
|
||||
libraryID := libRes.LibraryID
|
||||
libData := libRes.Libraries
|
||||
if libRes.IsAll {
|
||||
errorMsg = ""
|
||||
}
|
||||
|
||||
perSeriesPage := 24
|
||||
@@ -172,31 +139,28 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
var seriesCards []templates.SeriesCardData
|
||||
totalPages := 1
|
||||
|
||||
if libraryID != "" && errorMsg == "" {
|
||||
libUUID, err := uuid.Parse(libraryID)
|
||||
if err == nil {
|
||||
seriesList, total, err := handlers.GetSeriesCardsData(c.Request().Context(), cfg.Queries, libUUID, perSeriesPage, offset)
|
||||
if err != nil {
|
||||
log.Printf("GetSeriesCardsData failed: %v", err)
|
||||
errorMsg = "Error loading series"
|
||||
} else {
|
||||
totalPages = (total + perSeriesPage - 1) / perSeriesPage
|
||||
if totalPages < 1 {
|
||||
totalPages = 1
|
||||
}
|
||||
seriesCards = make([]templates.SeriesCardData, 0, len(seriesList))
|
||||
for _, s := range seriesList {
|
||||
covers := s.CoverPaths
|
||||
if covers == nil {
|
||||
covers = []string{}
|
||||
}
|
||||
seriesCards = append(seriesCards, templates.SeriesCardData{
|
||||
Name: s.Name,
|
||||
BookCount: s.BookCount,
|
||||
TotalInSeries: s.TotalInSeries,
|
||||
CoverPaths: covers,
|
||||
})
|
||||
if errorMsg == "" {
|
||||
seriesList, total, err := handlers.GetSeriesCardsData(c.Request().Context(), cfg.Queries, libRes.LibUUID, perSeriesPage, offset)
|
||||
if err != nil {
|
||||
log.Printf("GetSeriesCardsData failed: %v", err)
|
||||
errorMsg = "Error loading series"
|
||||
} else {
|
||||
totalPages = (total + perSeriesPage - 1) / perSeriesPage
|
||||
if totalPages < 1 {
|
||||
totalPages = 1
|
||||
}
|
||||
seriesCards = make([]templates.SeriesCardData, 0, len(seriesList))
|
||||
for _, s := range seriesList {
|
||||
covers := s.CoverPaths
|
||||
if covers == nil {
|
||||
covers = []string{}
|
||||
}
|
||||
seriesCards = append(seriesCards, templates.SeriesCardData{
|
||||
Name: s.Name,
|
||||
BookCount: s.BookCount,
|
||||
TotalInSeries: s.TotalInSeries,
|
||||
CoverPaths: covers,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,40 +191,23 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
return renderErrorPage(c, "Series name required", "bad_request")
|
||||
}
|
||||
|
||||
libraryID := c.QueryParam("library_id")
|
||||
userUUID, _ := uuid.Parse(user.ID)
|
||||
if libraryID == "" {
|
||||
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
|
||||
if err == nil && len(libraries) > 0 {
|
||||
libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16])
|
||||
libraryID = libUUID.String()
|
||||
} else {
|
||||
errorMsg = "No libraries available"
|
||||
}
|
||||
}
|
||||
|
||||
var bookInfoList []handlers.BookInfo
|
||||
|
||||
if libraryID != "" && errorMsg == "" {
|
||||
libUUID, err := uuid.Parse(libraryID)
|
||||
if err == nil {
|
||||
svc := services.NewSeriesService(cfg.Queries)
|
||||
books, err := svc.GetSeriesBooks(c.Request().Context(), libUUID, seriesName)
|
||||
if err != nil {
|
||||
log.Printf("GetSeriesBooks failed: %v", err)
|
||||
errorMsg = "Error loading series books"
|
||||
} else {
|
||||
bookInfoList = make([]handlers.BookInfo, 0, len(books))
|
||||
for _, item := range books {
|
||||
itemUUID, _ := uuid.FromBytes(item.ID.Bytes[0:16])
|
||||
bookInfoList = append(bookInfoList, handlers.BookInfo{
|
||||
MediaItemID: itemUUID.String(),
|
||||
Title: item.Title,
|
||||
Author: textToString(item.Author),
|
||||
CoverImagePath: utils.ResolveMediaURL(item.LibraryID, item.CoverImagePath),
|
||||
})
|
||||
}
|
||||
}
|
||||
svc := services.NewSeriesService(cfg.Queries)
|
||||
books, err := svc.GetSeriesBooks(c.Request().Context(), seriesName)
|
||||
if err != nil {
|
||||
log.Printf("GetSeriesBooks failed: %v", err)
|
||||
errorMsg = "Error loading series books"
|
||||
} else {
|
||||
bookInfoList = make([]handlers.BookInfo, 0, len(books))
|
||||
for _, item := range books {
|
||||
itemUUID, _ := uuid.FromBytes(item.ID.Bytes[0:16])
|
||||
bookInfoList = append(bookInfoList, handlers.BookInfo{
|
||||
MediaItemID: itemUUID.String(),
|
||||
Title: item.Title,
|
||||
Author: textToString(item.Author),
|
||||
CoverImagePath: utils.ResolveMediaURL(item.LibraryID, item.CoverImagePath),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +216,7 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = templates.BrowseDetail(user, "📚", "Series", seriesName, seriesName, "/series", "All Series", "📚", "This series doesn't have any books in this library yet", bookInfoList, errorMsg).Render(c.Request().Context(), &buf)
|
||||
err = templates.BrowseDetail(user, "📚", "Series", seriesName, seriesName, "/series", "All Series", "📚", "This series doesn't have any books yet", bookInfoList, errorMsg).Render(c.Request().Context(), &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -289,41 +236,29 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
return renderErrorPage(c, "Tag name required", "bad_request")
|
||||
}
|
||||
|
||||
libraryID := c.QueryParam("library_id")
|
||||
userUUID, _ := uuid.Parse(user.ID)
|
||||
if libraryID == "" {
|
||||
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
|
||||
if err == nil && len(libraries) > 0 {
|
||||
libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16])
|
||||
libraryID = libUUID.String()
|
||||
} else {
|
||||
errorMsg = "No libraries available"
|
||||
}
|
||||
}
|
||||
libRes := resolveLibrary(c, cfg, user.ID)
|
||||
libraryID := libRes.LibraryID
|
||||
|
||||
var bookInfoList []handlers.BookInfo
|
||||
|
||||
if libraryID != "" && errorMsg == "" {
|
||||
libUUID, err := uuid.Parse(libraryID)
|
||||
if err == nil {
|
||||
books, err := cfg.Queries.GetBooksByTag(c.Request().Context(), database.GetBooksByTagParams{
|
||||
LibraryID: uuidToPGType(libUUID),
|
||||
Column2: tagName,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("GetBooksByTag failed: %v", err)
|
||||
errorMsg = "Error loading tag books"
|
||||
} else {
|
||||
bookInfoList = make([]handlers.BookInfo, 0, len(books))
|
||||
for _, item := range books {
|
||||
itemUUID, _ := uuid.FromBytes(item.ID.Bytes[0:16])
|
||||
bookInfoList = append(bookInfoList, handlers.BookInfo{
|
||||
MediaItemID: itemUUID.String(),
|
||||
Title: item.Title,
|
||||
Author: textToString(item.Author),
|
||||
CoverImagePath: utils.ResolveMediaURL(item.LibraryID, item.CoverImagePath),
|
||||
})
|
||||
}
|
||||
books, err := cfg.Queries.GetBooksByTag(c.Request().Context(), database.GetBooksByTagParams{
|
||||
LibraryID: libRes.LibUUID,
|
||||
Column2: tagName,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("GetBooksByTag failed: %v", err)
|
||||
errorMsg = "Error loading tag books"
|
||||
} else {
|
||||
bookInfoList = make([]handlers.BookInfo, 0, len(books))
|
||||
for _, item := range books {
|
||||
itemUUID, _ := uuid.FromBytes(item.ID.Bytes[0:16])
|
||||
bookInfoList = append(bookInfoList, handlers.BookInfo{
|
||||
MediaItemID: itemUUID.String(),
|
||||
Title: item.Title,
|
||||
Author: textToString(item.Author),
|
||||
CoverImagePath: utils.ResolveMediaURL(item.LibraryID, item.CoverImagePath),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,52 +283,20 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
|
||||
var errorMsg string
|
||||
|
||||
// Get library_id from query param or user's first library
|
||||
libraryID := c.QueryParam("library_id")
|
||||
if libraryID == "" {
|
||||
userUUID, _ := uuid.Parse(user.ID)
|
||||
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
|
||||
if err == nil && len(libraries) > 0 {
|
||||
libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16])
|
||||
libraryID = libUUID.String()
|
||||
} else {
|
||||
errorMsg = "No libraries available"
|
||||
}
|
||||
}
|
||||
libRes := resolveLibrary(c, cfg, user.ID)
|
||||
libraryID := libRes.LibraryID
|
||||
libData := libRes.Libraries
|
||||
|
||||
// Get libraries for dropdown
|
||||
// Fetch saved filters for SSR
|
||||
userUUID, _ := uuid.Parse(user.ID)
|
||||
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
|
||||
if err != nil {
|
||||
log.Printf("GetUserVisibleLibraries failed: %v", err)
|
||||
libraries = []database.GetUserVisibleLibrariesRow{}
|
||||
if errorMsg == "" {
|
||||
errorMsg = "Error loading libraries"
|
||||
}
|
||||
}
|
||||
|
||||
libData := make([]templates.LibraryData, len(libraries))
|
||||
for i, lib := range libraries {
|
||||
libUUID, _ := uuid.FromBytes(lib.ID.Bytes[0:16])
|
||||
libData[i] = templates.LibraryData{
|
||||
ID: libUUID.String(),
|
||||
Name: lib.Name,
|
||||
Description: getText(lib.Description),
|
||||
TypeName: lib.TypeName,
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch saved filters for SSR (using existing query)
|
||||
var savedFilters []database.SavedFilters
|
||||
if libraryID != "" && errorMsg == "" {
|
||||
savedFilters, err = cfg.Queries.GetSavedFilters(c.Request().Context(), database.GetSavedFiltersParams{
|
||||
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
|
||||
ResourceType: "media-items",
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("GetSavedFilters failed: %v", err)
|
||||
savedFilters = []database.SavedFilters{}
|
||||
}
|
||||
savedFilters, err = cfg.Queries.GetSavedFilters(c.Request().Context(), database.GetSavedFiltersParams{
|
||||
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
|
||||
ResourceType: "media-items",
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("GetSavedFilters failed: %v", err)
|
||||
savedFilters = []database.SavedFilters{}
|
||||
}
|
||||
|
||||
// Fetch first page of books for SSR
|
||||
@@ -402,64 +305,51 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
limit := 50
|
||||
offset := 0
|
||||
|
||||
if libraryID != "" && errorMsg == "" {
|
||||
libUUID, err := uuid.Parse(libraryID)
|
||||
if err == nil {
|
||||
// Check URL params for pagination
|
||||
if limitStr := c.QueryParam("limit"); limitStr != "" {
|
||||
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 100 {
|
||||
limit = l
|
||||
if errorMsg == "" {
|
||||
// Check URL params for pagination
|
||||
if limitStr := c.QueryParam("limit"); limitStr != "" {
|
||||
if l, err := strconv.Atoi(limitStr); err == nil && l > 0 && l <= 100 {
|
||||
limit = l
|
||||
}
|
||||
}
|
||||
if offsetStr := c.QueryParam("offset"); offsetStr != "" {
|
||||
if o, err := strconv.Atoi(offsetStr); err == nil && o >= 0 {
|
||||
offset = o
|
||||
}
|
||||
}
|
||||
|
||||
params := services.SearchParams{
|
||||
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
|
||||
LibraryID: libRes.LibUUID,
|
||||
SearchQuery: "",
|
||||
AuthorFilter: "",
|
||||
SeriesFilter: "",
|
||||
GenreFilter: "",
|
||||
TagsFilter: "",
|
||||
LanguageFilter: "",
|
||||
YearMin: 0,
|
||||
YearMax: 0,
|
||||
HasCover: pgtype.Bool{Valid: false},
|
||||
Sort: "created_at DESC",
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
}
|
||||
var results []database.SearchMediaItemsUnifiedRow
|
||||
results, totalCount, err = cfg.MediaHandler.ExecuteSearch(c.Request().Context(), params)
|
||||
if err != nil {
|
||||
log.Printf("ExecuteSearch failed: %v", err)
|
||||
} else {
|
||||
bookInfoList = make([]handlers.BookInfo, len(results))
|
||||
for i, book := range results {
|
||||
bookUUID, _ := uuid.FromBytes(book.ID.Bytes[0:16])
|
||||
bookLibUUID, _ := uuid.FromBytes(book.LibraryID.Bytes[0:16])
|
||||
bookInfoList[i] = handlers.BookInfo{
|
||||
MediaItemID: bookUUID.String(),
|
||||
Title: book.Title,
|
||||
Author: textToString(book.Author),
|
||||
CoverImagePath: utils.ResolveMediaURL(pgtype.UUID{Bytes: bookLibUUID, Valid: true}, book.CoverImagePath),
|
||||
}
|
||||
}
|
||||
if offsetStr := c.QueryParam("offset"); offsetStr != "" {
|
||||
if o, err := strconv.Atoi(offsetStr); err == nil && o >= 0 {
|
||||
offset = o
|
||||
}
|
||||
}
|
||||
// Convert user.ID (string) to pgtype.UUID for service layer
|
||||
userUUID, err := uuid.Parse(user.ID)
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse user ID: %v", err)
|
||||
return renderErrorPage(c, "Error loading user", "user_id_error")
|
||||
}
|
||||
// Build search params (same as search.go:76-91)
|
||||
params := services.SearchParams{
|
||||
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
|
||||
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
|
||||
SearchQuery: "", // Empty for initial SSR load
|
||||
AuthorFilter: "",
|
||||
SeriesFilter: "",
|
||||
GenreFilter: "",
|
||||
TagsFilter: "",
|
||||
LanguageFilter: "",
|
||||
YearMin: 0,
|
||||
YearMax: 0,
|
||||
HasCover: pgtype.Bool{Valid: false},
|
||||
Sort: "created_at DESC",
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
}
|
||||
// Execute search using the same handler as API (search.go:93)
|
||||
var results []database.SearchMediaItemsUnifiedRow
|
||||
results, totalCount, err = cfg.MediaHandler.ExecuteSearch(c.Request().Context(), params)
|
||||
if err != nil {
|
||||
log.Printf("ExecuteSearch failed: %v", err)
|
||||
// Continue without books - will show empty state
|
||||
} else {
|
||||
// Convert to BookInfo (same as search.go:99-109)
|
||||
bookInfoList = make([]handlers.BookInfo, len(results))
|
||||
for i, book := range results {
|
||||
bookUUID, _ := uuid.FromBytes(book.ID.Bytes[0:16])
|
||||
bookLibUUID, _ := uuid.FromBytes(book.LibraryID.Bytes[0:16])
|
||||
bookInfoList[i] = handlers.BookInfo{
|
||||
MediaItemID: bookUUID.String(),
|
||||
Title: book.Title,
|
||||
Author: textToString(book.Author),
|
||||
CoverImagePath: utils.ResolveMediaURL(pgtype.UUID{Bytes: bookLibUUID, Valid: true}, book.CoverImagePath),
|
||||
}
|
||||
}
|
||||
log.Printf("SSR: fetched %d books for library %s", len(bookInfoList), libraryID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,20 +370,13 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
|
||||
var errorMsg string
|
||||
|
||||
libraryID := c.QueryParam("library_id")
|
||||
if libraryID == "" {
|
||||
userUUID, _ := uuid.Parse(user.ID)
|
||||
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
|
||||
if err == nil && len(libraries) > 0 {
|
||||
libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16])
|
||||
libraryID = libUUID.String()
|
||||
}
|
||||
}
|
||||
libRes := resolveLibrary(c, cfg, user.ID)
|
||||
libraryID := libRes.LibraryID
|
||||
|
||||
libUUID, _ := uuid.Parse(libraryID)
|
||||
userUUID, _ := uuid.Parse(user.ID)
|
||||
pgLibUUID := libRes.LibUUID
|
||||
|
||||
prefs, err := cfg.DashboardService.GetDashboardPreferences(c.Request().Context(), userUUID, libUUID)
|
||||
prefs, err := cfg.DashboardService.GetDashboardPreferences(c.Request().Context(), userUUID, pgLibUUID)
|
||||
if err != nil {
|
||||
log.Printf("GetDashboardPreferences failed: %v", err)
|
||||
prefs = database.UserDashboardPreferences{
|
||||
@@ -511,7 +394,7 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
allSections, err := cfg.DashboardService.GetDashboardSections(
|
||||
c.Request().Context(),
|
||||
userUUID,
|
||||
libUUID,
|
||||
pgLibUUID,
|
||||
limit,
|
||||
prefs.CollectionOrder,
|
||||
[]string{}, // No filtering - get all sections
|
||||
@@ -525,32 +408,11 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
// Get only visible sections for the dashboard display
|
||||
visibleSections := cfg.DashboardService.FilterHiddenCollections(allSections, prefs.HiddenCollections)
|
||||
|
||||
userUUID2, _ := uuid.Parse(user.ID)
|
||||
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID2))
|
||||
if err != nil {
|
||||
log.Printf("GetUserVisibleLibraries failed: %v", err)
|
||||
libraries = []database.GetUserVisibleLibrariesRow{}
|
||||
if errorMsg == "" {
|
||||
errorMsg = "Error loading libraries"
|
||||
}
|
||||
}
|
||||
|
||||
libData := make([]templates.LibraryData, len(libraries))
|
||||
for i, lib := range libraries {
|
||||
libUUID, _ := uuid.FromBytes(lib.ID.Bytes[0:16])
|
||||
libData[i] = templates.LibraryData{
|
||||
ID: libUUID.String(),
|
||||
Name: lib.Name,
|
||||
Description: getText(lib.Description),
|
||||
TypeName: lib.TypeName,
|
||||
}
|
||||
}
|
||||
|
||||
sectionData := handlers.BuildSections(visibleSections, libraryID)
|
||||
allSectionsData := handlers.BuildSections(allSections, libraryID)
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = templates.Dashboard(user, sectionData, allSectionsData, libData, libraryID, prefs.HiddenCollections, limit, errorMsg).Render(c.Request().Context(), &buf)
|
||||
err = templates.Dashboard(user, sectionData, allSectionsData, libRes.Libraries, libraryID, prefs.HiddenCollections, limit, errorMsg).Render(c.Request().Context(), &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -674,30 +536,18 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
userUUID, _ := uuid.Parse(user.ID)
|
||||
var books []handlers.BookInfo
|
||||
|
||||
if collection.QueryType.Valid && collection.QueryType.String != "" {
|
||||
// System collection - use query type
|
||||
// System collection - need library_id for system collections
|
||||
// Get library_id from query param or default to user's first library
|
||||
libraryID := c.QueryParam("library_id")
|
||||
if libraryID == "" {
|
||||
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userUUID))
|
||||
if err == nil && len(libraries) > 0 {
|
||||
libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16])
|
||||
libraryID = libUUID.String()
|
||||
}
|
||||
}
|
||||
libRes := resolveLibrary(c, cfg, user.ID)
|
||||
libraryID := libRes.LibraryID
|
||||
|
||||
libUUID, _ := uuid.Parse(libraryID)
|
||||
if collection.QueryType.Valid && collection.QueryType.String != "" {
|
||||
dashboardSvc := services.NewDashboardService(cfg.Queries)
|
||||
sections, err := dashboardSvc.GetDashboardSections(c.Request().Context(), userUUID, libUUID, 1000, []string{}, []string{})
|
||||
if err != nil {
|
||||
sections, secErr := dashboardSvc.GetDashboardSections(c.Request().Context(), userUUID, libRes.LibUUID, 1000, []string{}, []string{})
|
||||
if secErr != nil {
|
||||
return renderErrorPage(c, "Error loading books", "books_load_error")
|
||||
}
|
||||
|
||||
// Find the matching section and convert items
|
||||
for _, section := range sections {
|
||||
if section.CollectionID.String() == collectionID {
|
||||
// Convert []database.MediaItems to []handlers.BookInfo
|
||||
bookCards := make([]handlers.BookInfo, len(section.Items))
|
||||
for i, item := range section.Items {
|
||||
itemUUID, _ := uuid.FromBytes(item.ID.Bytes[0:16])
|
||||
@@ -713,27 +563,21 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// User collection - check if library_id filter is present
|
||||
libraryID := c.QueryParam("library_id")
|
||||
|
||||
if libraryID != "" {
|
||||
// Filter by library - reuse dashboard query
|
||||
libUUID, err := uuid.Parse(libraryID)
|
||||
if err != nil {
|
||||
if libraryID != "" && !libRes.IsAll {
|
||||
libUUID, parseErr := uuid.Parse(libraryID)
|
||||
if parseErr != nil {
|
||||
return renderErrorPage(c, "Invalid library ID", "invalid_library_id")
|
||||
}
|
||||
|
||||
// Use GetCollectionItemsForDashboard for library-filtered results
|
||||
collItems, err := cfg.Queries.GetCollectionItemsForDashboard(c.Request().Context(),
|
||||
collItems, collErr := cfg.Queries.GetCollectionItemsForDashboard(c.Request().Context(),
|
||||
database.GetCollectionItemsForDashboardParams{
|
||||
CollectionID: pgtype.UUID{Bytes: collUUID, Valid: true},
|
||||
LibraryID: pgtype.UUID{Bytes: libUUID, Valid: true},
|
||||
Limit: 1000,
|
||||
Limit: pgtype.Int4{Int32: 1000, Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
if collErr != nil {
|
||||
books = []handlers.BookInfo{}
|
||||
} else {
|
||||
// Convert to BookInfo format (non-excluded only)
|
||||
var validItems []database.GetCollectionItemsForDashboardRow
|
||||
for _, item := range collItems {
|
||||
if !item.Excluded.Valid || !item.Excluded.Bool {
|
||||
@@ -754,13 +598,11 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
books = bookCards
|
||||
}
|
||||
} else {
|
||||
// No library filter - show all books in collection
|
||||
collItems, err := cfg.Queries.GetCollectionItems(c.Request().Context(), pgtype.UUID{Bytes: collUUID, Valid: true})
|
||||
if err != nil {
|
||||
collItems, collErr := cfg.Queries.GetCollectionItems(c.Request().Context(), pgtype.UUID{Bytes: collUUID, Valid: true})
|
||||
if collErr != nil {
|
||||
books = []handlers.BookInfo{}
|
||||
}
|
||||
|
||||
// Convert to BookInfo format
|
||||
bookCards := make([]handlers.BookInfo, len(collItems))
|
||||
for i, item := range collItems {
|
||||
itemUUID, _ := uuid.FromBytes(item.MediaItemID.Bytes[0:16])
|
||||
@@ -783,11 +625,8 @@ func registerFrontendRoutes(cfg *Config) {
|
||||
Icon: collection.Icon.String,
|
||||
}
|
||||
|
||||
// Get library_id from query params for template
|
||||
libraryID := c.QueryParam("library_id")
|
||||
// Render the CollectionDetail template
|
||||
var buf bytes.Buffer
|
||||
err = templates.CollectionDetail(user, colData, books, libraryID).Render(c.Request().Context(), &buf)
|
||||
err = templates.CollectionDetail(user, colData, books, libraryID, libRes.Libraries).Render(c.Request().Context(), &buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package router
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/url"
|
||||
|
||||
"bookhoard/internal/database"
|
||||
"bookhoard/templates"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -86,3 +88,77 @@ func parseUUID(s string) (uuid.UUID, error) {
|
||||
func uuidToPGType(u uuid.UUID) pgtype.UUID {
|
||||
return pgtype.UUID{Bytes: u, Valid: true}
|
||||
}
|
||||
|
||||
const selectedLibraryCookie = "selectedLibrary"
|
||||
const allLibrariesSentinel = "__all__"
|
||||
|
||||
type LibraryResolution struct {
|
||||
LibraryID string
|
||||
IsAll bool
|
||||
LibUUID pgtype.UUID
|
||||
Libraries []templates.LibraryData
|
||||
FirstID string
|
||||
}
|
||||
|
||||
func getText(t pgtype.Text) string {
|
||||
if t.Valid {
|
||||
return t.String
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func resolveLibrary(c *echo.Context, cfg *Config, userUUID string) LibraryResolution {
|
||||
res := LibraryResolution{}
|
||||
|
||||
userU, _ := uuid.Parse(userUUID)
|
||||
libraries, err := cfg.Queries.GetUserVisibleLibraries(c.Request().Context(), uuidToPGType(userU))
|
||||
if err != nil {
|
||||
log.Printf("GetUserVisibleLibraries failed: %v", err)
|
||||
libraries = []database.GetUserVisibleLibrariesRow{}
|
||||
}
|
||||
|
||||
res.Libraries = make([]templates.LibraryData, len(libraries))
|
||||
for i, lib := range libraries {
|
||||
libUUID, _ := uuid.FromBytes(lib.ID.Bytes[0:16])
|
||||
res.Libraries[i] = templates.LibraryData{
|
||||
ID: libUUID.String(),
|
||||
Name: lib.Name,
|
||||
Description: getText(lib.Description),
|
||||
TypeName: lib.TypeName,
|
||||
}
|
||||
}
|
||||
|
||||
if len(libraries) > 0 {
|
||||
libUUID, _ := uuid.FromBytes(libraries[0].ID.Bytes[0:16])
|
||||
res.FirstID = libUUID.String()
|
||||
}
|
||||
|
||||
libraryID := c.QueryParam("library_id")
|
||||
if libraryID == "" {
|
||||
if cookie, err := c.Cookie(selectedLibraryCookie); err == nil {
|
||||
val, _ := url.QueryUnescape(cookie.Value)
|
||||
if val == allLibrariesSentinel {
|
||||
res.IsAll = true
|
||||
res.LibraryID = ""
|
||||
return res
|
||||
}
|
||||
if _, parseErr := uuid.Parse(val); parseErr == nil {
|
||||
libraryID = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if libraryID == "" {
|
||||
res.LibraryID = res.FirstID
|
||||
if res.LibraryID != "" {
|
||||
parsed, _ := uuid.Parse(res.LibraryID)
|
||||
res.LibUUID = pgtype.UUID{Bytes: parsed, Valid: true}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
res.LibraryID = libraryID
|
||||
parsed, _ := uuid.Parse(libraryID)
|
||||
res.LibUUID = pgtype.UUID{Bytes: parsed, Valid: true}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -135,7 +135,8 @@ type DashboardSection struct {
|
||||
|
||||
func (s *DashboardService) GetDashboardSections(
|
||||
ctx context.Context,
|
||||
userID, libraryID uuid.UUID,
|
||||
userID uuid.UUID,
|
||||
libraryID pgtype.UUID,
|
||||
limit int,
|
||||
collectionOrder []string,
|
||||
hiddenCollections []string,
|
||||
@@ -267,36 +268,36 @@ func (s *DashboardService) sortByPriority(sections []DashboardSection) []Dashboa
|
||||
return sorted
|
||||
}
|
||||
|
||||
func (s *DashboardService) getCollectionItemsByQueryType(ctx context.Context, coll database.Collections, userID, libraryID uuid.UUID, limit int) ([]database.MediaItems, error) {
|
||||
func (s *DashboardService) getCollectionItemsByQueryType(ctx context.Context, coll database.Collections, userID uuid.UUID, libraryID pgtype.UUID, limit int) ([]database.MediaItems, error) {
|
||||
switch coll.QueryType.String {
|
||||
case "continue-reading":
|
||||
return s.db.GetContinueReadingItems(ctx, database.GetContinueReadingItemsParams{
|
||||
UserID: pgtype.UUID{Bytes: userID, Valid: true},
|
||||
LibraryID: pgtype.UUID{Bytes: libraryID, Valid: true},
|
||||
Limit: int32(limit),
|
||||
LibraryID: libraryID,
|
||||
Limit: pgtype.Int4{Int32: int32(limit), Valid: true},
|
||||
})
|
||||
case "recently-added":
|
||||
return s.db.GetRecentlyAddedItems(ctx, database.GetRecentlyAddedItemsParams{
|
||||
LibraryID: pgtype.UUID{Bytes: libraryID, Valid: true},
|
||||
Limit: int32(limit),
|
||||
LibraryID: libraryID,
|
||||
Limit: pgtype.Int4{Int32: int32(limit), Valid: true},
|
||||
})
|
||||
case "recently-read":
|
||||
return s.db.GetRecentlyReadItems(ctx, database.GetRecentlyReadItemsParams{
|
||||
UserID: pgtype.UUID{Bytes: userID, Valid: true},
|
||||
LibraryID: pgtype.UUID{Bytes: libraryID, Valid: true},
|
||||
Limit: int32(limit),
|
||||
LibraryID: libraryID,
|
||||
Limit: pgtype.Int4{Int32: int32(limit), Valid: true},
|
||||
})
|
||||
case "not-started":
|
||||
return s.db.GetNotStartedItems(ctx, database.GetNotStartedItemsParams{
|
||||
UserID: pgtype.UUID{Bytes: userID, Valid: true},
|
||||
LibraryID: pgtype.UUID{Bytes: libraryID, Valid: true},
|
||||
Limit: int32(limit),
|
||||
LibraryID: libraryID,
|
||||
Limit: pgtype.Int4{Int32: int32(limit), Valid: true},
|
||||
})
|
||||
case "continue-series":
|
||||
rows, err := s.db.GetContinueSeriesItems(ctx, database.GetContinueSeriesItemsParams{
|
||||
LibraryID: pgtype.UUID{Bytes: libraryID, Valid: true},
|
||||
LibraryID: libraryID,
|
||||
UserID: pgtype.UUID{Bytes: userID, Valid: true},
|
||||
Limit: int32(limit),
|
||||
Limit: pgtype.Int4{Int32: int32(limit), Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -311,13 +312,13 @@ func (s *DashboardService) getCollectionItemsByQueryType(ctx context.Context, co
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DashboardService) getUserCollectionItems(ctx context.Context, coll database.Collections, userID, libraryID uuid.UUID, limit int) ([]database.MediaItems, error) {
|
||||
func (s *DashboardService) getUserCollectionItems(ctx context.Context, coll database.Collections, userID uuid.UUID, libraryID pgtype.UUID, limit int) ([]database.MediaItems, error) {
|
||||
collUUID, _ := uuid.FromBytes(coll.ID.Bytes[0:16])
|
||||
|
||||
manualItems, err := s.db.GetCollectionItemsForDashboard(ctx, database.GetCollectionItemsForDashboardParams{
|
||||
CollectionID: pgtype.UUID{Bytes: collUUID, Valid: true},
|
||||
LibraryID: pgtype.UUID{Bytes: libraryID, Valid: true},
|
||||
Limit: int32(limit),
|
||||
LibraryID: libraryID,
|
||||
Limit: pgtype.Int4{Int32: int32(limit), Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -334,7 +335,7 @@ func (s *DashboardService) getUserCollectionItems(ctx context.Context, coll data
|
||||
if len(coll.AutoAssignRules) > 0 {
|
||||
var rules []Rule
|
||||
if err := json.Unmarshal(coll.AutoAssignRules, &rules); err == nil && len(rules) > 0 {
|
||||
allLibraryItems, err := s.db.GetLibraryItems(ctx, pgtype.UUID{Bytes: libraryID, Valid: true})
|
||||
allLibraryItems, err := s.db.GetLibraryItems(ctx, libraryID)
|
||||
if err == nil {
|
||||
for _, item := range allLibraryItems {
|
||||
alreadyInCollection := false
|
||||
@@ -374,10 +375,10 @@ func (s *DashboardService) getUserCollectionItems(ctx context.Context, coll data
|
||||
return finalItems, nil
|
||||
}
|
||||
|
||||
func (s *DashboardService) GetDashboardPreferences(ctx context.Context, userID, libraryID uuid.UUID) (database.UserDashboardPreferences, error) {
|
||||
func (s *DashboardService) GetDashboardPreferences(ctx context.Context, userID uuid.UUID, libraryID pgtype.UUID) (database.UserDashboardPreferences, error) {
|
||||
return s.db.GetDashboardPreferences(ctx, database.GetDashboardPreferencesParams{
|
||||
UserID: pgtype.UUID{Bytes: userID, Valid: true},
|
||||
LibraryID: pgtype.UUID{Bytes: libraryID, Valid: true},
|
||||
LibraryID: libraryID,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"bookhoard/internal/utils"
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
@@ -25,18 +24,16 @@ func NewSeriesService(db *database.Queries) *SeriesService {
|
||||
return &SeriesService{db: db}
|
||||
}
|
||||
|
||||
func (s *SeriesService) GetSeriesPage(ctx context.Context, libraryID uuid.UUID, limit, offset int) ([]SeriesInfo, int, error) {
|
||||
libUUID := pgtype.UUID{Bytes: libraryID, Valid: true}
|
||||
|
||||
totalCount, err := s.db.GetDistinctSeriesCount(ctx, libUUID)
|
||||
func (s *SeriesService) GetSeriesPage(ctx context.Context, libraryID pgtype.UUID, limit, offset int) ([]SeriesInfo, int, error) {
|
||||
totalCount, err := s.db.GetDistinctSeriesCount(ctx, libraryID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
rows, err := s.db.GetDistinctSeries(ctx, database.GetDistinctSeriesParams{
|
||||
LibraryID: libUUID,
|
||||
Limit: int32(limit),
|
||||
Offset: int32(offset),
|
||||
LibraryID: libraryID,
|
||||
Limit: pgtype.Int4{Int32: int32(limit), Valid: true},
|
||||
Offset: pgtype.Int4{Int32: int32(offset), Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
@@ -88,11 +85,11 @@ func (s *SeriesService) GetSeriesPage(ctx context.Context, libraryID uuid.UUID,
|
||||
return series, int(totalCount), nil
|
||||
}
|
||||
|
||||
func (s *SeriesService) GetSeriesCovers(ctx context.Context, libraryID uuid.UUID, seriesName string, limit int) ([]string, error) {
|
||||
func (s *SeriesService) GetSeriesCovers(ctx context.Context, libraryID pgtype.UUID, seriesName string, limit int) ([]string, error) {
|
||||
covers, err := s.db.GetSeriesCovers(ctx, database.GetSeriesCoversParams{
|
||||
LibraryID: pgtype.UUID{Bytes: libraryID, Valid: true},
|
||||
LibraryID: libraryID,
|
||||
Series: pgtype.Text{String: seriesName, Valid: true},
|
||||
Limit: int32(limit),
|
||||
Limit: pgtype.Int4{Int32: int32(limit), Valid: true},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -109,11 +106,8 @@ func (s *SeriesService) GetSeriesCovers(ctx context.Context, libraryID uuid.UUID
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func (s *SeriesService) GetSeriesBooks(ctx context.Context, libraryID uuid.UUID, seriesName string) ([]database.MediaItems, error) {
|
||||
return s.db.GetSeriesBooks(ctx, database.GetSeriesBooksParams{
|
||||
LibraryID: pgtype.UUID{Bytes: libraryID, Valid: true},
|
||||
Series: pgtype.Text{String: seriesName, Valid: true},
|
||||
})
|
||||
func (s *SeriesService) GetSeriesBooks(ctx context.Context, seriesName string) ([]database.MediaItems, error) {
|
||||
return s.db.GetSeriesBooks(ctx, pgtype.Text{String: seriesName, Valid: true})
|
||||
}
|
||||
|
||||
func continueSeriesRowToMediaItems(row database.GetContinueSeriesItemsRow) database.MediaItems {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -110,11 +110,11 @@ func AdminLibrary(user User, libraries []LibraryData, users []User) templ.Compon
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(library.ID)
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(library.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_library.templ`, Line: 53, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -123,11 +123,11 @@ func AdminLibrary(user User, libraries []LibraryData, users []User) templ.Compon
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(library.ID)
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(library.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_library.templ`, Line: 54, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -136,11 +136,11 @@ func AdminLibrary(user User, libraries []LibraryData, users []User) templ.Compon
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(library.ID)
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(library.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_library.templ`, Line: 55, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -149,11 +149,11 @@ func AdminLibrary(user User, libraries []LibraryData, users []User) templ.Compon
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue("library-folders-" + library.ID)
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs("library-folders-" + library.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_library.templ`, Line: 58, Col: 53}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
|
||||
package templates
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -73,11 +73,11 @@ func AdminSettings(user User, systemConfig map[string]string, errorMessage strin
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(systemConfig["base_url"])
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(systemConfig["base_url"])
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_settings.templ`, Line: 40, Col: 42}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -43,11 +43,11 @@ func AdminSidebar(user User, currentPath string) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var2).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_sidebar.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -65,11 +65,11 @@ func AdminSidebar(user User, currentPath string) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var4).String())
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var4).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_sidebar.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -87,11 +87,11 @@ func AdminSidebar(user User, currentPath string) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var6).String())
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var6).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_sidebar.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -109,11 +109,11 @@ func AdminSidebar(user User, currentPath string) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var8).String())
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var8).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_sidebar.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -55,11 +55,11 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue("user-" + user.ID)
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs("user-" + user.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_users.templ`, Line: 37, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -114,11 +114,11 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue("/api/auth/profile/" + user.ID)
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs("/api/auth/profile/" + user.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_users.templ`, Line: 70, Col: 52}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -127,11 +127,11 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue("#role-result-" + user.ID)
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs("#role-result-" + user.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_users.templ`, Line: 72, Col: 50}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -160,11 +160,11 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue("role-result-" + user.ID)
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs("role-result-" + user.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_users.templ`, Line: 84, Col: 46}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -191,11 +191,11 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue("/admin/users/" + user.ID + "/profile-modal")
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs("/admin/users/" + user.ID + "/profile-modal")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_users.templ`, Line: 94, Col: 65}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -214,11 +214,11 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.ResolveAttributeValue("/api/auth/profile/" + user.ID)
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs("/api/auth/profile/" + user.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_users.templ`, Line: 113, Col: 55}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -227,11 +227,11 @@ func AdminUsers(currentUser User, users []User, adminCount int) templ.Component
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.ResolveAttributeValue("#user-" + user.ID)
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs("#user-" + user.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/admin_users.templ`, Line: 115, Col: 43}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -354,11 +354,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.CoverImagePath.String)
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(book.CoverImagePath.String)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 184, Col: 40}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -367,11 +367,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.Title)
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(book.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 185, Col: 24}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -385,11 +385,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 string
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.Title)
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(book.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 191, Col: 24}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -403,11 +403,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.Title)
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(book.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 245, Col: 58}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var22)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -416,11 +416,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.Author))
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.Author))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 251, Col: 74}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -460,11 +460,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.ResolveAttributeValue(tag)
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(tag)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 283, Col: 36}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -478,11 +478,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var27 string
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%.1f", book.CommunityRating.Float64))
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f", book.CommunityRating.Float64))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 318, Col: 66}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var27)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -491,11 +491,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.Publisher))
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.Publisher))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 335, Col: 80}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -504,11 +504,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var29 string
|
||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(formatDateForInput(book.DatePublished))
|
||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(formatDateForInput(book.DatePublished))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 342, Col: 55}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -517,11 +517,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var30 string
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.Edition))
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.Edition))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 348, Col: 76}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var30)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -530,11 +530,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var31 string
|
||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.Language))
|
||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.Language))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 354, Col: 78}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var31)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -543,11 +543,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var32 string
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.Genre))
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.Genre))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 360, Col: 72}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var32)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -556,11 +556,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var33 string
|
||||
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", book.CopyrightYear.Int32))
|
||||
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", book.CopyrightYear.Int32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 367, Col: 60}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -569,11 +569,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var34 string
|
||||
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.Series))
|
||||
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.Series))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 384, Col: 74}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -582,11 +582,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var35 string
|
||||
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", book.SeriesNumber.Int32))
|
||||
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", book.SeriesNumber.Int32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 392, Col: 60}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -595,11 +595,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var36 string
|
||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", book.SeriesCount.Int32))
|
||||
templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", book.SeriesCount.Int32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 399, Col: 59}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var36)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -608,11 +608,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var37 string
|
||||
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", book.Volume.Int32))
|
||||
templ_7745c5c3_Var37, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", book.Volume.Int32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 406, Col: 54}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var37)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var37))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -621,11 +621,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var38 string
|
||||
templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.Isbn))
|
||||
templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.Isbn))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 424, Col: 70}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var38)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -634,11 +634,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var39 string
|
||||
templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.Asin))
|
||||
templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.Asin))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 430, Col: 70}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var39)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -647,11 +647,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var40 string
|
||||
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.GoodreadsID))
|
||||
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.GoodreadsID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 436, Col: 85}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var40)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -660,11 +660,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var41 string
|
||||
templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.OpenlibraryID))
|
||||
templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.OpenlibraryID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 442, Col: 89}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var41)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -673,11 +673,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var42 string
|
||||
templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.GoogleBooksID))
|
||||
templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.GoogleBooksID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 448, Col: 90}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var42)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -686,11 +686,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var43 string
|
||||
templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.WebUrl))
|
||||
templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.WebUrl))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 454, Col: 74}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var43)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -779,11 +779,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var44 string
|
||||
templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.AgeRating))
|
||||
templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.AgeRating))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 493, Col: 81}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var44)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -792,11 +792,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var45 string
|
||||
templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.StoryArc))
|
||||
templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.StoryArc))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 499, Col: 79}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var45)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -805,11 +805,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var46 string
|
||||
templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.Imprint))
|
||||
templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.Imprint))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 505, Col: 76}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var46)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -818,11 +818,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var47 string
|
||||
templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.ScanInformation))
|
||||
templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.ScanInformation))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 511, Col: 93}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var47)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -854,11 +854,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var49 string
|
||||
templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%d", book.PageCount.Int32))
|
||||
templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", book.PageCount.Int32))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 542, Col: 56}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var49)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -867,11 +867,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var50 string
|
||||
templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.ResolveAttributeValue(stringSliceToString(book.Contributors))
|
||||
templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(stringSliceToString(book.Contributors))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 548, Col: 93}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var50)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -880,11 +880,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var51 string
|
||||
templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.ResolveAttributeValue(textToString(book.MimeType))
|
||||
templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(textToString(book.MimeType))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 555, Col: 63}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var51)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -893,11 +893,11 @@ func MetadataEditorModal(book handlers.MediaDetail) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var52 string
|
||||
templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%.1f MB", float64(book.FileSize.Int64)/1024/1024))
|
||||
templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.1f MB", float64(book.FileSize.Int64)/1024/1024))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail_modals.templ`, Line: 561, Col: 98}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var52)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -56,11 +56,11 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.FormatGroup)
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(book.FormatGroup)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 22, Col: 93}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -69,11 +69,11 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(uuidToString(book.LibraryID))
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(uuidToString(book.LibraryID))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 22, Col: 142}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -95,11 +95,11 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.CoverImagePath.String)
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(book.CoverImagePath.String)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 38, Col: 40}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -108,11 +108,11 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.Title)
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(book.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 39, Col: 24}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -167,11 +167,11 @@ func BookDetail(user User, book handlers.MediaDetail, errorMessage string) templ
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.ResolveAttributeValue("window.location.href = '/readers/" + uuidToString(book.ID) + "'")
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs("window.location.href = '/readers/" + uuidToString(book.ID) + "'")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/book_detail.templ`, Line: 64, Col: 82}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var9)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -46,13 +46,18 @@ templ BookShelf(
|
||||
</label>
|
||||
<select
|
||||
id="library-select"
|
||||
name="library"
|
||||
name="library_id"
|
||||
class="w-full px-3 py-2 border rounded-lg"
|
||||
style="background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);"
|
||||
>
|
||||
if len(libraries) == 0 {
|
||||
<option value="">No libraries available</option>
|
||||
} else {
|
||||
if currentLibraryID == "" {
|
||||
<option value="" selected>All Books</option>
|
||||
} else {
|
||||
<option value="">All Books</option>
|
||||
}
|
||||
for _, lib := range libraries {
|
||||
if lib.ID == currentLibraryID {
|
||||
<option value={ lib.ID } selected>{ lib.Name }</option>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -54,11 +54,11 @@ func CollectionModal(collection CollectionData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue("/api/collections/" + collection.ID)
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs("/api/collections/" + collection.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collection_modal.templ`, Line: 16, Col: 49}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -67,11 +67,11 @@ func CollectionModal(collection CollectionData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(collection.ID)
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(collection.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collection_modal.templ`, Line: 19, Col: 57}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -80,11 +80,11 @@ func CollectionModal(collection CollectionData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(collection.Name)
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collection_modal.templ`, Line: 25, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -106,11 +106,11 @@ func CollectionModal(collection CollectionData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(collection.Icon)
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Icon)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collection_modal.templ`, Line: 56, Col: 83}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -119,11 +119,11 @@ func CollectionModal(collection CollectionData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(collection.Color)
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Color)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collection_modal.templ`, Line: 75, Col: 86}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -137,11 +137,11 @@ func CollectionModal(collection CollectionData) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(collection.Icon)
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Icon)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collection_modal.templ`, Line: 122, Col: 83}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -12,7 +12,7 @@ templ Collection(user User, collections []CollectionData, errorMessage string) {
|
||||
<script src="/static/htmx.min.js"></script>
|
||||
<link href="/static/style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body x-data="collections" x-init="initializeCollectionWebSocket(); setupHTMXModalInit()" class="theme-{ user.Theme }">
|
||||
<body x-data="collections" x-init="initCollectionsPage()" class="theme-{ user.Theme }">
|
||||
@Header(user, "/collections")
|
||||
<!-- Modal Container -->
|
||||
<div id="modal-container"></div>
|
||||
@@ -100,7 +100,7 @@ templ Collection(user User, collections []CollectionData, errorMessage string) {
|
||||
</html>
|
||||
}
|
||||
|
||||
templ CollectionDetail(user User, collection CollectionData, books []handlers.BookInfo, libraryID string) {
|
||||
templ CollectionDetail(user User, collection CollectionData, books []handlers.BookInfo, libraryID string, libData []LibraryData) {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -110,13 +110,14 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
<script src="/static/htmx.min.js"></script>
|
||||
<link href="/static/style.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body x-data="collections" x-init="initializeCollectionWebSocket()" class="theme-{ user.Theme }">
|
||||
<body x-data="collections" x-init="initCollectionsPage()" class="theme-{ user.Theme }">
|
||||
@Header(user, "/collections")
|
||||
@LibrarySwitcher(libData, libraryID)
|
||||
<div class="w-full px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="mb-6">
|
||||
<button @click="backToCollections" class="btn-secondary px-4 py-2 rounded-lg mb-4">
|
||||
<a href="/collections" class="btn-secondary px-4 py-2 rounded-lg mb-4 inline-block">
|
||||
← Back to Collections
|
||||
</button>
|
||||
</a>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="text-4xl" style="color: { collection.Color }">{ collection.Icon }</div>
|
||||
<div>
|
||||
@@ -169,7 +170,6 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
style="background-color: var(--bg-secondary); border-color: var(--border);"
|
||||
>
|
||||
<div class="flex gap-4">
|
||||
<!-- Checkbox and Book Info -->
|
||||
<div class="flex-shrink-0 pt-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -193,7 +193,6 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
<!-- Book Cover -->
|
||||
<div class="flex-shrink-0 w-16 sm:w-20">
|
||||
if book.CoverImagePath != "" {
|
||||
<img
|
||||
@@ -211,7 +210,6 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Remove Button -->
|
||||
<div class="mt-3 pt-3 border-t" style="border-color: var(--border);">
|
||||
<button
|
||||
@click="removeBook('{ book.MediaItemID }')"
|
||||
@@ -227,16 +225,12 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
</div>
|
||||
</div>
|
||||
<!-- Book Picker Modal -->
|
||||
<!-- CRITICAL: Selection state stored in Alpine.store to persist across HTMX swaps -->
|
||||
<div
|
||||
x-data="bookPicker"
|
||||
@keyup.escape.window="$store.bookPicker.close()"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center"
|
||||
style="display: none;"
|
||||
>
|
||||
<!-- Modal content with same filtering UI as bookshelf + multi-select -->
|
||||
<!-- CRITICAL: x-show manages visibility, style="display: none;" prevents FOUC -->
|
||||
<!-- Following ALPINE_COMPLETION_GUIDE.md principles: Alpine state for UI, not class="hidden" -->
|
||||
<div
|
||||
@click.stop
|
||||
x-show="$store.bookPicker.isOpen"
|
||||
@@ -262,13 +256,11 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
style="color: var(--text-primary)"
|
||||
>✕</button>
|
||||
</div>
|
||||
<!-- Filter Bar (same as bookshelf) -->
|
||||
<div
|
||||
class="p-4 border-b"
|
||||
style="border-color: var(--border);"
|
||||
>
|
||||
<div class="flex flex-wrap gap-4 items-center">
|
||||
<!-- Search -->
|
||||
<div class="flex-1 min-w-[200px]">
|
||||
<input
|
||||
type="text"
|
||||
@@ -282,7 +274,6 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
hx-include="#book-picker-filters"
|
||||
/>
|
||||
</div>
|
||||
<!-- Author -->
|
||||
<div class="flex-1 min-w-[150px]">
|
||||
<input
|
||||
type="text"
|
||||
@@ -296,7 +287,6 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
hx-include="#book-picker-filters"
|
||||
/>
|
||||
</div>
|
||||
<!-- Genre -->
|
||||
<div class="flex-1 min-w-[150px]">
|
||||
<input
|
||||
type="text"
|
||||
@@ -310,7 +300,6 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
hx-include="#book-picker-filters"
|
||||
/>
|
||||
</div>
|
||||
<!-- Clear -->
|
||||
<div>
|
||||
<button
|
||||
@click="$store.bookPicker.clearFilters()"
|
||||
@@ -321,23 +310,16 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Hidden form for HTMX -->
|
||||
<!-- NOTE: class="hidden" is acceptable here because form is never shown to user -->
|
||||
<!-- HTMX uses it for parameter inclusion only -->
|
||||
<form id="filter-form" class="hidden">
|
||||
<input type="hidden" name="limit" value="50"/>
|
||||
<input type="hidden" name="offset" value="0"/>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Books Grid with Multi-select -->
|
||||
<!-- CRITICAL: HTMX swaps this div's content, but Alpine.store preserves selection state -->
|
||||
<div
|
||||
id="book-picker-grid"
|
||||
class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4 p-4 max-h-96 overflow-y-auto"
|
||||
>
|
||||
<!-- Books loaded via HTMX -->
|
||||
</div>
|
||||
<!-- Selected Count & Submit -->
|
||||
<div
|
||||
class="p-4 border-t flex justify-between items-center"
|
||||
style="border-color: var(--border);"
|
||||
@@ -367,8 +349,8 @@ templ CollectionDetail(user User, collection CollectionData, books []handlers.Bo
|
||||
</body>
|
||||
<div
|
||||
id="collection-data"
|
||||
data-id="{collection.ID}"
|
||||
data-library-id="{ libraryID }"
|
||||
data-id={ collection.ID }
|
||||
data-library-id={ libraryID }
|
||||
style="display: none;"
|
||||
></div>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -31,7 +31,7 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Collections - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body x-data=\"collections\" x-init=\"initializeCollectionWebSocket(); setupHTMXModalInit()\" class=\"theme-{ user.Theme }\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Collections - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body x-data=\"collections\" x-init=\"initCollectionsPage()\" class=\"theme-{ user.Theme }\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -55,11 +55,11 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue("/collections/" + col.ID)
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs("/collections/" + col.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 62, Col: 82}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -68,11 +68,11 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(col.Color)
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(col.Color)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 66, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -94,11 +94,11 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue("/collections/" + col.ID + "/edit-modal")
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs("/collections/" + col.ID + "/edit-modal")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 72, Col: 60}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -107,11 +107,11 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue("/api/collections/" + col.ID + "")
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs("/api/collections/" + col.ID + "")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 81, Col: 56}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -162,7 +162,7 @@ func Collection(user User, collections []CollectionData, errorMessage string) te
|
||||
})
|
||||
}
|
||||
|
||||
func CollectionDetail(user User, collection CollectionData, books []handlers.BookInfo, libraryID string) templ.Component {
|
||||
func CollectionDetail(user User, collection CollectionData, books []handlers.BookInfo, libraryID string, libData []LibraryData) templ.Component {
|
||||
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
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
@@ -196,7 +196,7 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body x-data=\"collections\" x-init=\"initializeCollectionWebSocket()\" class=\"theme-{ user.Theme }\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " - Bookhoard</title><script src=\"/static/htmx.min.js\"></script><link href=\"/static/style.css\" rel=\"stylesheet\"></head><body x-data=\"collections\" x-init=\"initCollectionsPage()\" class=\"theme-{ user.Theme }\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -204,14 +204,18 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<div class=\"w-full px-4 sm:px-6 lg:px-8 py-8\"><div class=\"mb-6\"><button @click=\"backToCollections\" class=\"btn-secondary px-4 py-2 rounded-lg mb-4\">← Back to Collections</button><div class=\"flex items-center gap-4\"><div class=\"text-4xl\" style=\"color: { collection.Color }\">")
|
||||
templ_7745c5c3_Err = LibrarySwitcher(libData, libraryID).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<div class=\"w-full px-4 sm:px-6 lg:px-8 py-8\"><div class=\"mb-6\"><a href=\"/collections\" class=\"btn-secondary px-4 py-2 rounded-lg mb-4 inline-block\">← Back to Collections</a><div class=\"flex items-center gap-4\"><div class=\"text-4xl\" style=\"color: { collection.Color }\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Icon)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 121, Col: 81}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 122, Col: 81}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -224,7 +228,7 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 123, Col: 90}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 124, Col: 90}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -237,7 +241,7 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(collection.Description)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 124, Col: 71}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 125, Col: 71}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@@ -261,13 +265,13 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
|
||||
var templ_7745c5c3_Var14 templ.SafeURL
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinURLErrs("/media/" + book.MediaItemID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 166, Col: 44}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 167, Col: 44}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\"><div class=\"card p-4 rounded-lg border hover:shadow-lg transition-shadow\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex gap-4\"><!-- Checkbox and Book Info --><div class=\"flex-shrink-0 pt-1\"><input type=\"checkbox\" onchange=\"toggleBookForRemoval('{ book.MediaItemID }')\" class=\"w-5 h-5\"></div><div class=\"flex-1 min-w-0\"><h3 class=\"font-semibold text-lg mb-1 line-clamp-2\" style=\"color: var(--text-primary)\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\"><div class=\"card p-4 rounded-lg border hover:shadow-lg transition-shadow\" style=\"background-color: var(--bg-secondary); border-color: var(--border);\"><div class=\"flex gap-4\"><div class=\"flex-shrink-0 pt-1\"><input type=\"checkbox\" onchange=\"toggleBookForRemoval('{ book.MediaItemID }')\" class=\"w-5 h-5\"></div><div class=\"flex-1 min-w-0\"><h3 class=\"font-semibold text-lg mb-1 line-clamp-2\" style=\"color: var(--text-primary)\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -303,7 +307,7 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><!-- Book Cover --><div class=\"flex-shrink-0 w-16 sm:w-20\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><div class=\"flex-shrink-0 w-16 sm:w-20\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -313,11 +317,11 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(book.CoverImagePath)
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(book.CoverImagePath)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 200, Col: 37}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 199, Col: 37}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -331,12 +335,38 @@ func CollectionDetail(user User, collection CollectionData, books []handlers.Boo
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</div></div><!-- Remove Button --><div class=\"mt-3 pt-3 border-t\" style=\"border-color: var(--border);\"><button @click=\"removeBook('{ book.MediaItemID }')\" class=\"px-3 py-1 text-sm border rounded hover:opacity-80\" style=\"border-color: var(--border); color: var(--text-secondary);\">🗑️ Remove from Collection</button></div></div></a>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</div></div><div class=\"mt-3 pt-3 border-t\" style=\"border-color: var(--border);\"><button @click=\"removeBook('{ book.MediaItemID }')\" class=\"px-3 py-1 text-sm border rounded hover:opacity-80\" style=\"border-color: var(--border); color: var(--text-secondary);\">🗑️ Remove from Collection</button></div></div></a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</div></div><!-- Book Picker Modal --><!-- CRITICAL: Selection state stored in Alpine.store to persist across HTMX swaps --><div x-data=\"bookPicker\" @keyup.escape.window=\"$store.bookPicker.close()\" class=\"fixed inset-0 z-50 flex items-center justify-center\" style=\"display: none;\"><!-- Modal content with same filtering UI as bookshelf + multi-select --><!-- CRITICAL: x-show manages visibility, style=\"display: none;\" prevents FOUC --><!-- Following ALPINE_COMPLETION_GUIDE.md principles: Alpine state for UI, not class=\"hidden\" --><div @click.stop x-show=\"$store.bookPicker.isOpen\" x-transition:enter=\"transition ease-out duration-200\" x-transition:enter-start=\"opacity-0 scale-95\" x-transition:enter-end=\"opacity-100 scale-100\" x-transition:leave=\"transition ease-in duration-150\" x-transition:leave-start=\"opacity-100 scale-100\" x-transition:leave-end=\"opacity-0 scale-95\" class=\"card rounded-lg w-full max-w-6xl mx-4 my-8\" style=\"background-color: var(--bg-secondary); border-color: var(--border); display: none;\"><div class=\"flex justify-between items-center p-6 border-b\" style=\"border-color: var(--border);\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Add Books to Collection</h2><button @click=\"$store.bookPicker.close()\" class=\"p-2 hover:opacity-80 rounded\" style=\"color: var(--text-primary)\">✕</button></div><!-- Filter Bar (same as bookshelf) --><div class=\"p-4 border-b\" style=\"border-color: var(--border);\"><div class=\"flex flex-wrap gap-4 items-center\"><!-- Search --><div class=\"flex-1 min-w-[200px]\"><input type=\"text\" name=\"search\" placeholder=\"Search books...\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\" hx-get=\"/api/media-items/filtered?show_checkbox=true&collection_id={ collection.ID }\" hx-target=\"#book-picker-grid\" hx-trigger=\"keyup changed delay:300ms\" hx-include=\"#book-picker-filters\"></div><!-- Author --><div class=\"flex-1 min-w-[150px]\"><input type=\"text\" name=\"author_filter\" placeholder=\"Author\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\" hx-get=\"/api/media-items/filtered?show_checkbox=true&collection_id={ collection.ID }\" hx-target=\"#book-picker-grid\" hx-trigger=\"change\" hx-include=\"#book-picker-filters\"></div><!-- Genre --><div class=\"flex-1 min-w-[150px]\"><input type=\"text\" name=\"genre_filter\" placeholder=\"Genre\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\" hx-get=\"/api/media-items/filtered?show_checkbox=true&collection_id={ collection.ID }\" hx-target=\"#book-picker-grid\" hx-trigger=\"change\" hx-include=\"#book-picker-filters\"></div><!-- Clear --><div><button @click=\"$store.bookPicker.clearFilters()\" class=\"px-4 py-2 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary);\">✕ Clear</button></div></div><!-- Hidden form for HTMX --><!-- NOTE: class=\"hidden\" is acceptable here because form is never shown to user --><!-- HTMX uses it for parameter inclusion only --><form id=\"filter-form\" class=\"hidden\"><input type=\"hidden\" name=\"limit\" value=\"50\"> <input type=\"hidden\" name=\"offset\" value=\"0\"></form></div><!-- Books Grid with Multi-select --><!-- CRITICAL: HTMX swaps this div's content, but Alpine.store preserves selection state --><div id=\"book-picker-grid\" class=\"grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4 p-4 max-h-96 overflow-y-auto\"><!-- Books loaded via HTMX --></div><!-- Selected Count & Submit --><div class=\"p-4 border-t flex justify-between items-center\" style=\"border-color: var(--border);\"><div class=\"text-sm\" style=\"color: var(--text-secondary);\"><span x-text=\"$store.bookPicker.selectedCount\"></span> books selected</div><div class=\"flex gap-2\"><button @click=\"$store.bookPicker.close()\" class=\"px-4 py-2 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary);\">Cancel</button> <button @click=\"$store.bookPicker.submit()\" class=\"px-4 py-2 rounded-lg font-medium\" style=\"background-color: var(--accent); color: var(--bg-primary);\">Add Selected Books</button></div></div></div></div></body><div id=\"collection-data\" data-id=\"{collection.ID}\" data-library-id=\"{ libraryID }\" style=\"display: none;\"></div></html>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</div></div><!-- Book Picker Modal --><div x-data=\"bookPicker\" @keyup.escape.window=\"$store.bookPicker.close()\" class=\"fixed inset-0 z-50 flex items-center justify-center\" style=\"display: none;\"><div @click.stop x-show=\"$store.bookPicker.isOpen\" x-transition:enter=\"transition ease-out duration-200\" x-transition:enter-start=\"opacity-0 scale-95\" x-transition:enter-end=\"opacity-100 scale-100\" x-transition:leave=\"transition ease-in duration-150\" x-transition:leave-start=\"opacity-100 scale-100\" x-transition:leave-end=\"opacity-0 scale-95\" class=\"card rounded-lg w-full max-w-6xl mx-4 my-8\" style=\"background-color: var(--bg-secondary); border-color: var(--border); display: none;\"><div class=\"flex justify-between items-center p-6 border-b\" style=\"border-color: var(--border);\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Add Books to Collection</h2><button @click=\"$store.bookPicker.close()\" class=\"p-2 hover:opacity-80 rounded\" style=\"color: var(--text-primary)\">✕</button></div><div class=\"p-4 border-b\" style=\"border-color: var(--border);\"><div class=\"flex flex-wrap gap-4 items-center\"><div class=\"flex-1 min-w-[200px]\"><input type=\"text\" name=\"search\" placeholder=\"Search books...\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\" hx-get=\"/api/media-items/filtered?show_checkbox=true&collection_id={ collection.ID }\" hx-target=\"#book-picker-grid\" hx-trigger=\"keyup changed delay:300ms\" hx-include=\"#book-picker-filters\"></div><div class=\"flex-1 min-w-[150px]\"><input type=\"text\" name=\"author_filter\" placeholder=\"Author\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\" hx-get=\"/api/media-items/filtered?show_checkbox=true&collection_id={ collection.ID }\" hx-target=\"#book-picker-grid\" hx-trigger=\"change\" hx-include=\"#book-picker-filters\"></div><div class=\"flex-1 min-w-[150px]\"><input type=\"text\" name=\"genre_filter\" placeholder=\"Genre\" class=\"w-full px-3 py-2 border rounded-lg\" style=\"background-color: var(--bg-primary); color: var(--text-primary); border-color: var(--border);\" hx-get=\"/api/media-items/filtered?show_checkbox=true&collection_id={ collection.ID }\" hx-target=\"#book-picker-grid\" hx-trigger=\"change\" hx-include=\"#book-picker-filters\"></div><div><button @click=\"$store.bookPicker.clearFilters()\" class=\"px-4 py-2 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary);\">✕ Clear</button></div></div><form id=\"filter-form\" class=\"hidden\"><input type=\"hidden\" name=\"limit\" value=\"50\"> <input type=\"hidden\" name=\"offset\" value=\"0\"></form></div><div id=\"book-picker-grid\" class=\"grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4 p-4 max-h-96 overflow-y-auto\"></div><div class=\"p-4 border-t flex justify-between items-center\" style=\"border-color: var(--border);\"><div class=\"text-sm\" style=\"color: var(--text-secondary);\"><span x-text=\"$store.bookPicker.selectedCount\"></span> books selected</div><div class=\"flex gap-2\"><button @click=\"$store.bookPicker.close()\" class=\"px-4 py-2 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary);\">Cancel</button> <button @click=\"$store.bookPicker.submit()\" class=\"px-4 py-2 rounded-lg font-medium\" style=\"background-color: var(--accent); color: var(--bg-primary);\">Add Selected Books</button></div></div></div></div></body><div id=\"collection-data\" data-id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(collection.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 352, Col: 26}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "\" data-library-id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(libraryID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/collections.templ`, Line: 353, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\" style=\"display: none;\"></div></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -47,11 +47,11 @@ func CustomSectionBuilder(user User, libraries []LibraryData, errorMessage strin
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(lib.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/custom_section.templ`, Line: 68, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -17,53 +17,7 @@ templ Dashboard(user User, sections []handlers.SectionData, allSections []handle
|
||||
</head>
|
||||
<body x-data="dashboard" x-init="initDashboard()" class="theme-{ user.Theme }">
|
||||
@Header(user, "/dashboard")
|
||||
<!-- Sticky Library Selector -->
|
||||
<div class="sticky top-0 z-40 bg-opacity-95 backdrop-blur border-b" style="background-color: var(--bg-primary);">
|
||||
<div class="w-full px-4 py-3 flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="text-sm font-medium" style="color: var(--text-secondary)">Library:</label>
|
||||
<select
|
||||
id="library-select"
|
||||
name="library_id"
|
||||
class="px-4 py-2 rounded-lg border focus:ring-2 focus:ring-blue-500"
|
||||
style="background-color: var(--bg-secondary); color: var(--text-primary);"
|
||||
>
|
||||
for _, lib := range libData {
|
||||
if lib.ID == currentLibraryID {
|
||||
<option value={ lib.ID } selected>{ lib.Name }</option>
|
||||
} else {
|
||||
<option value={ lib.ID }>{ lib.Name }</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
data-action="open-dashboard-settings"
|
||||
class="p-2 rounded-lg hover:bg-gray-700 transition-colors"
|
||||
style="background-color: var(--bg-secondary);"
|
||||
title="Customize Dashboard"
|
||||
>
|
||||
⚙️
|
||||
</button>
|
||||
<button
|
||||
data-action="reload-page"
|
||||
class="p-2 rounded-lg hover:bg-gray-700 transition-colors"
|
||||
style="background-color: var(--bg-secondary);"
|
||||
title="Refresh"
|
||||
>
|
||||
🔄
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="loading-spinner"
|
||||
class="hidden fixed inset-0 bg-opacity-50 flex items-center justify-center z-50"
|
||||
style="background-color: var(--bg-primary);"
|
||||
>
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-b-2" style="border-color: var(--accent);"></div>
|
||||
</div>
|
||||
</div>
|
||||
@LibrarySwitcher(libData, currentLibraryID, DashboardActions())
|
||||
<!-- Collections Container -->
|
||||
<main id="collections-container" class="w-full px-4 py-8">
|
||||
for _, section := range sections {
|
||||
|
||||
+210
-275
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -42,76 +42,11 @@ func Dashboard(user User, sections []handlers.SectionData, allSections []handler
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<!-- Sticky Library Selector --><div class=\"sticky top-0 z-40 bg-opacity-95 backdrop-blur border-b\" style=\"background-color: var(--bg-primary);\"><div class=\"w-full px-4 py-3 flex items-center justify-between\"><div class=\"flex items-center gap-4\"><label class=\"text-sm font-medium\" style=\"color: var(--text-secondary)\">Library:</label> <select id=\"library-select\" name=\"library_id\" class=\"px-4 py-2 rounded-lg border focus:ring-2 focus:ring-blue-500\" style=\"background-color: var(--bg-secondary); color: var(--text-primary);\">")
|
||||
templ_7745c5c3_Err = LibrarySwitcher(libData, currentLibraryID, DashboardActions()).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, lib := range libData {
|
||||
if lib.ID == currentLibraryID {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<option value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 33, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" selected>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 33, Col: 53}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</option>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<option value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 35, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 35, Col: 44}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</option>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</select></div><div class=\"flex items-center gap-2\"><button data-action=\"open-dashboard-settings\" class=\"p-2 rounded-lg hover:bg-gray-700 transition-colors\" style=\"background-color: var(--bg-secondary);\" title=\"Customize Dashboard\">⚙️</button> <button data-action=\"reload-page\" class=\"p-2 rounded-lg hover:bg-gray-700 transition-colors\" style=\"background-color: var(--bg-secondary);\" title=\"Refresh\">🔄</button></div></div><div id=\"loading-spinner\" class=\"hidden fixed inset-0 bg-opacity-50 flex items-center justify-center z-50\" style=\"background-color: var(--bg-primary);\"><div class=\"animate-spin rounded-full h-12 w-12 border-b-2\" style=\"border-color: var(--accent);\"></div></div></div><!-- Collections Container --><main id=\"collections-container\" class=\"w-full px-4 py-8\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<!-- Collections Container --><main id=\"collections-container\" class=\"w-full px-4 py-8\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -121,7 +56,7 @@ func Dashboard(user User, sections []handlers.SectionData, allSections []handler
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</main><!-- Dashboard Settings Modal -->")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</main><!-- Dashboard Settings Modal -->")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -133,7 +68,7 @@ func Dashboard(user User, sections []handlers.SectionData, allSections []handler
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</body></html>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -157,136 +92,136 @@ func CollectionCarousel(section handlers.SectionData) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var6 == nil {
|
||||
templ_7745c5c3_Var6 = templ.NopComponent
|
||||
templ_7745c5c3_Var2 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var2 == nil {
|
||||
templ_7745c5c3_Var2 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"dashboard-collection mb-8\" data-collection-id=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<div class=\"dashboard-collection mb-8\" data-collection-id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(section.ID)
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(section.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 83, Col: 33}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 37, Col: 33}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" data-is-system=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" data-is-system=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue(section.IsSystem)
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(section.IsSystem)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 84, Col: 35}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 38, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\"><!-- Collection Header --><div class=\"flex items-center justify-between mb-4\"><div class=\"flex items-center gap-3\"><span class=\"text-2xl\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\"><!-- Collection Header --><div class=\"flex items-center justify-between mb-4\"><div class=\"flex items-center gap-3\"><span class=\"text-2xl\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(section.Icon)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 43, Col: 41}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</span><div><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(section.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 45, Col: 85}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</h2>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if section.Description != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<p class=\"text-sm\" style=\"color: var(--text-secondary)\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(section.Description)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 47, Col: 83}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if section.ViewAllURL != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 templ.SafeURL
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinURLErrs(section.ViewAllURL)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 53, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\" class=\"text-sm font-medium hover:underline transition-colors\" style=\"color: var(--accent);\">View All →</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</div><!-- Carousel --><div class=\"carousel-container relative group\"><button class=\"carousel-nav-left absolute left-0 top-1/2 -translate-y-1/2 z-10 w-12 h-full bg-gradient-to-r from-gray-900 to-transparent flex items-center justify-start opacity-0 group-hover:opacity-100 transition-opacity duration-200\" data-action=\"scroll-carousel\" data-collection-id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(section.Icon)
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(section.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 89, Col: 41}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 69, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</span><div><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\" data-direction=\"-1\" aria-label=\"Scroll left\"><span class=\"text-3xl pl-2\" style=\"color: var(--text-primary);\">‹</span></button><div id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var10 string
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(section.Title)
|
||||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs("carousel-track-" + section.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 91, Col: 85}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 76, Col: 39}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</h2>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if section.Description != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<p class=\"text-sm\" style=\"color: var(--text-secondary)\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(section.Description)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 93, Col: 83}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if section.ViewAllURL != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 templ.SafeURL
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinURLErrs(section.ViewAllURL)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 99, Col: 30}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" class=\"text-sm font-medium hover:underline transition-colors\" style=\"color: var(--accent);\">View All →</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</div><!-- Carousel --><div class=\"carousel-container relative group\"><button class=\"carousel-nav-left absolute left-0 top-1/2 -translate-y-1/2 z-10 w-12 h-full bg-gradient-to-r from-gray-900 to-transparent flex items-center justify-start opacity-0 group-hover:opacity-100 transition-opacity duration-200\" data-action=\"scroll-carousel\" data-collection-id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(section.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 115, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" data-direction=\"-1\" aria-label=\"Scroll left\"><span class=\"text-3xl pl-2\" style=\"color: var(--text-primary);\">‹</span></button><div id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue("carousel-track-" + section.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 122, Col: 39}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\" class=\"carousel-track flex gap-4 overflow-x-auto scroll-smooth snap-x snap-mandatory px-12 pb-4\" style=\"scrollbar-width: none; -ms-overflow-style: none;\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\" class=\"carousel-track flex gap-4 overflow-x-auto scroll-smooth snap-x snap-mandatory px-12 pb-4\" style=\"scrollbar-width: none; -ms-overflow-style: none;\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -297,25 +232,25 @@ func CollectionCarousel(section handlers.SectionData) templ.Component {
|
||||
}
|
||||
}
|
||||
if len(section.Items) == 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div class=\"text-center py-8 w-full\" style=\"color: var(--text-secondary);\"><p>No items in this collection</p></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "<div class=\"text-center py-8 w-full\" style=\"color: var(--text-secondary);\"><p>No items in this collection</p></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><button class=\"carousel-nav-right absolute right-0 top-1/2 -translate-y-1/2 z-10 w-12 h-full bg-gradient-to-l from-gray-900 to-transparent flex items-center justify-end opacity-0 group-hover:opacity-100 transition-opacity duration-200\" data-action=\"scroll-carousel\" data-collection-id=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</div><button class=\"carousel-nav-right absolute right-0 top-1/2 -translate-y-1/2 z-10 w-12 h-full bg-gradient-to-l from-gray-900 to-transparent flex items-center justify-end opacity-0 group-hover:opacity-100 transition-opacity duration-200\" data-action=\"scroll-carousel\" data-collection-id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(section.ID)
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(section.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 143, Col: 35}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 97, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" data-direction=\"1\" aria-label=\"Scroll right\"><span class=\"text-3xl pr-2\" style=\"color: var(--text-primary);\">›</span></button></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\" data-direction=\"1\" aria-label=\"Scroll right\"><span class=\"text-3xl pr-2\" style=\"color: var(--text-primary);\">›</span></button></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -339,167 +274,167 @@ func BookCard(item handlers.BookInfo) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var16 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var16 == nil {
|
||||
templ_7745c5c3_Var16 = templ.NopComponent
|
||||
templ_7745c5c3_Var12 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var12 == nil {
|
||||
templ_7745c5c3_Var12 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<a href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 templ.SafeURL
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinURLErrs("/media/" + item.MediaItemID)
|
||||
var templ_7745c5c3_Var13 templ.SafeURL
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinURLErrs("/media/" + item.MediaItemID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 154, Col: 39}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 108, Col: 39}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" title=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" title=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue(item.Title)
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 154, Col: 60}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 108, Col: 60}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\"><div class=\"book-card flex-shrink-0 w-36 rounded-lg overflow-hidden snap-start cursor-pointer transition-transform duration-200 hover:scale-105\" tabindex=\"0\" role=\"button\" aria-label=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\"><div class=\"book-card flex-shrink-0 w-36 rounded-lg overflow-hidden snap-start cursor-pointer transition-transform duration-200 hover:scale-105\" tabindex=\"0\" role=\"button\" aria-label=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue("View " + item.Title)
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs("View " + item.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 160, Col: 36}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 114, Col: 36}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\"><div class=\"aspect-[2/3] overflow-hidden shadow-lg bg-gradient-to-br from-gray-700 to-gray-900\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\"><div class=\"aspect-[2/3] overflow-hidden shadow-lg bg-gradient-to-br from-gray-700 to-gray-900\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if item.CoverImagePath != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<img src=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<img src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.ResolveAttributeValue(item.CoverImagePath)
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(item.CoverImagePath)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 168, Col: 31}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 122, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\" alt=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\" alt=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var21 string
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.ResolveAttributeValue(item.Title)
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 169, Col: 22}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 123, Col: 22}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\" class=\"w-full h-full object-cover\" loading=\"lazy\" onerror=\"this.src='/static/placeholder-book.svg'\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" class=\"w-full h-full object-cover\" loading=\"lazy\" onerror=\"this.src='/static/placeholder-book.svg'\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<img src=\"/static/placeholder-book.svg\" alt=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<img src=\"/static/placeholder-book.svg\" alt=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.ResolveAttributeValue(item.Title)
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 177, Col: 22}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 131, Col: 22}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var22)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "\" class=\"w-full h-full object-cover\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" class=\"w-full h-full object-cover\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "</div><div class=\"book-card-text px-2 py-1 bg-[color-mix(in_srgb,var(--wood-border)_40%,transparent)]\"><h3 class=\"font-semibold text-base line-clamp-2\" style=\"color: var(--text-primary)\" title=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</div><div class=\"book-card-text px-2 py-1 bg-[color-mix(in_srgb,var(--wood-border)_40%,transparent)]\"><h3 class=\"font-semibold text-base line-clamp-2\" style=\"color: var(--text-primary)\" title=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var23 string
|
||||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.ResolveAttributeValue(item.Title)
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 183, Col: 106}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 137, Col: 106}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(item.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 184, Col: 17}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 138, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</h3>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</h3>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if item.Author != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<p class=\"text-sm line-clamp-1\" style=\"color: var(--text-secondary)\" title=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "<p class=\"text-sm line-clamp-1\" style=\"color: var(--text-secondary)\" title=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.ResolveAttributeValue(item.Author)
|
||||
var templ_7745c5c3_Var21 string
|
||||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(item.Author)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 187, Col: 93}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 141, Col: 93}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(item.Author)
|
||||
var templ_7745c5c3_Var22 string
|
||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(item.Author)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 188, Col: 19}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 142, Col: 19}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "</div></div></a>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</div></div></a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -523,143 +458,143 @@ func DashboardSettingsModal(sections []handlers.SectionData, hiddenCollections [
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var27 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var27 == nil {
|
||||
templ_7745c5c3_Var27 = templ.NopComponent
|
||||
templ_7745c5c3_Var23 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var23 == nil {
|
||||
templ_7745c5c3_Var23 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "<div id=\"dashboard-settings-modal\" class=\"hidden fixed inset-0 z-50 flex items-center justify-center\" style=\"background-color: rgba(0, 0, 0, 0.7);\"><div class=\"rounded-lg p-6 w-full max-w-2xl mx-4 shadow-2xl\" style=\"background-color: var(--bg-secondary);\"><div class=\"flex justify-between items-center mb-6\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Customize Dashboard</h2><button data-action=\"close-dashboard-settings\" class=\"p-2 hover:bg-gray-700 rounded transition-colors\">✕</button></div><p class=\"text-sm mb-4\" style=\"color: var(--text-secondary);\">Drag to reorder collections, toggle visibility with the switch.</p><!-- Draggable Collection List --><div id=\"collection-list\" class=\"space-y-2 mb-6\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div id=\"dashboard-settings-modal\" class=\"hidden fixed inset-0 z-50 flex items-center justify-center\" style=\"background-color: rgba(0, 0, 0, 0.7);\"><div class=\"rounded-lg p-6 w-full max-w-2xl mx-4 shadow-2xl\" style=\"background-color: var(--bg-secondary);\"><div class=\"flex justify-between items-center mb-6\"><h2 class=\"text-xl font-bold\" style=\"color: var(--text-primary)\">Customize Dashboard</h2><button data-action=\"close-dashboard-settings\" class=\"p-2 hover:bg-gray-700 rounded transition-colors\">✕</button></div><p class=\"text-sm mb-4\" style=\"color: var(--text-secondary);\">Drag to reorder collections, toggle visibility with the switch.</p><!-- Draggable Collection List --><div id=\"collection-list\" class=\"space-y-2 mb-6\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, section := range sections {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<div class=\"collection-item flex items-center justify-between p-3 rounded border cursor-move select-none\" data-collection-id=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "<div class=\"collection-item flex items-center justify-between p-3 rounded border cursor-move select-none\" data-collection-id=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.ResolveAttributeValue(section.ID)
|
||||
var templ_7745c5c3_Var24 string
|
||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(section.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 224, Col: 37}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 178, Col: 37}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\" data-is-system=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "\" data-is-system=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var29 string
|
||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.ResolveAttributeValue(fmt.Sprintf("%v", section.IsSystem))
|
||||
var templ_7745c5c3_Var25 string
|
||||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%v", section.IsSystem))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 225, Col: 58}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 179, Col: 58}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var29)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "\" draggable=\"true\" style=\"background-color: var(--bg-primary); border-color: var(--border);\"><div class=\"flex items-center gap-3\"><span class=\"text-xl\" style=\"color: var(--text-secondary);\">☰</span> <span class=\"text-xl\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "\" draggable=\"true\" style=\"background-color: var(--bg-primary); border-color: var(--border);\"><div class=\"flex items-center gap-3\"><span class=\"text-xl\" style=\"color: var(--text-secondary);\">☰</span> <span class=\"text-xl\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var30 string
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(section.Icon)
|
||||
var templ_7745c5c3_Var26 string
|
||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(section.Icon)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 231, Col: 43}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 185, Col: 43}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</span><div><span class=\"font-medium\" style=\"color: var(--text-primary);\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "</span><div><span class=\"font-medium\" style=\"color: var(--text-primary);\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var31 string
|
||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(section.Title)
|
||||
var templ_7745c5c3_Var27 string
|
||||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(section.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 233, Col: 85}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 187, Col: 85}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</span> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if section.IsSystem {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<span class=\"text-xs ml-2 px-2 py-1 rounded\" style=\"background-color: var(--accent);\">System</span>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<span class=\"text-xs ml-2 px-2 py-1 rounded\" style=\"background-color: var(--accent);\">System</span>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</div></div><div class=\"flex items-center gap-3\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</div></div><div class=\"flex items-center gap-3\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if section.IsSystem {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "<button data-action=\"restore-system-collection\" data-collection-name=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "<button data-action=\"restore-system-collection\" data-collection-name=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var32 string
|
||||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.ResolveAttributeValue(section.ID)
|
||||
var templ_7745c5c3_Var28 string
|
||||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(section.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 243, Col: 42}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 197, Col: 42}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var32)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "\" class=\"text-xs px-3 py-1 rounded border hover:opacity-80 transition-opacity\" style=\"border-color: var(--border); color: var(--text-secondary);\" title=\"Restore { section.Title } to defaults\">Restore</button> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "\" class=\"text-xs px-3 py-1 rounded border hover:opacity-80 transition-opacity\" style=\"border-color: var(--border); color: var(--text-secondary);\" title=\"Restore { section.Title } to defaults\">Restore</button> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<label class=\"relative inline-flex items-center cursor-pointer\"><input type=\"checkbox\" class=\"sr-only peer\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<label class=\"relative inline-flex items-center cursor-pointer\"><input type=\"checkbox\" class=\"sr-only peer\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !ContainsString(hiddenCollections, section.ID) {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, " checked")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, " checked")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "><div class=\"w-11 h-6 bg-gray-600 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-800 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600\"></div></label></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "><div class=\"w-11 h-6 bg-gray-600 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-800 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600\"></div></label></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</div><!-- Items Per Section Slider --><div class=\"mb-6\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Items per Collection: <span id=\"items-count-display\" class=\"font-bold\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</div><!-- Items Per Section Slider --><div class=\"mb-6\"><label class=\"block text-sm font-medium mb-2\" style=\"color: var(--text-secondary)\">Items per Collection: <span id=\"items-count-display\" class=\"font-bold\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var33 string
|
||||
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(itemsPerSection)
|
||||
var templ_7745c5c3_Var29 string
|
||||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(itemsPerSection)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 274, Col: 93}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 228, Col: 93}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</span></label> <input type=\"range\" min=\"10\" max=\"50\" step=\"5\" value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</span></label> <input type=\"range\" min=\"10\" max=\"50\" step=\"5\" value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var34 string
|
||||
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.ResolveAttributeValue(itemsPerSection)
|
||||
var templ_7745c5c3_Var30 string
|
||||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(itemsPerSection)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 281, Col: 28}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/dashboard.templ`, Line: 235, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "\" class=\"w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer\" data-input-action=\"update-items-count\" target=\"items-count-display\"></div><div class=\"flex justify-end gap-3\"><button data-action=\"close-dashboard-settings\" class=\"px-4 py-2 rounded-lg border hover:bg-gray-700 transition-colors\" style=\"border-color: var(--border); color: var(--text-primary);\">Cancel</button> <button data-action=\"save-dashboard-settings\" class=\"px-4 py-2 rounded-lg text-white font-medium hover:opacity-90 transition-opacity\" style=\"background-color: var(--accent);\">Save Changes</button></div></div></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\" class=\"w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer\" data-input-action=\"update-items-count\" target=\"items-count-display\"></div><div class=\"flex justify-end gap-3\"><button data-action=\"close-dashboard-settings\" class=\"px-4 py-2 rounded-lg border hover:bg-gray-700 transition-colors\" style=\"border-color: var(--border); color: var(--text-primary);\">Cancel</button> <button data-action=\"save-dashboard-settings\" class=\"px-4 py-2 rounded-lg text-white font-medium hover:opacity-90 transition-opacity\" style=\"background-color: var(--accent);\">Save Changes</button></div></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -191,11 +191,11 @@ func Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []Pe
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(baseURL + "/api/sync/kobo/" + device.AuthToken)
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(baseURL + "/api/sync/kobo/" + device.AuthToken)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 109, Col: 68}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -210,11 +210,11 @@ func Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []Pe
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue(device.AuthToken)
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(device.AuthToken)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 133, Col: 38}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -228,11 +228,11 @@ func Devices(user User, devices []handlers.DeviceInfo, pendingRegistrations []Pe
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.ResolveAttributeValue("/api/devices/" + device.ID.String() + "/regenerate-token")
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs("/api/devices/" + device.ID.String() + "/regenerate-token")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/devices.templ`, Line: 150, Col: 78}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var8)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -58,11 +58,11 @@ func DocsLayout(nav Navigation, doc Document, user User, currentPath string) tem
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var3).String())
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var3).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/docs.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -35,11 +35,11 @@ func FilterItem(id string, name string) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(id)
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(id)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/filter_item.templ`, Line: 8, Col: 21}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package templates
|
||||
|
||||
templ LibrarySwitcher(libData []LibraryData, currentLibraryID string, actions ...templ.Component) {
|
||||
<div class="sticky top-0 z-40 bg-opacity-95 backdrop-blur border-b" style="background-color: var(--bg-primary);">
|
||||
<div class="w-full px-4 py-3 flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="text-sm font-medium" style="color: var(--text-secondary)">Library:</label>
|
||||
<select
|
||||
id="library-select"
|
||||
name="library_id"
|
||||
class="px-4 py-2 rounded-lg border focus:ring-2 focus:ring-blue-500"
|
||||
style="background-color: var(--bg-secondary); color: var(--text-primary);"
|
||||
>
|
||||
<option value="">All Libraries</option>
|
||||
for _, lib := range libData {
|
||||
if lib.ID == currentLibraryID {
|
||||
<option value={ lib.ID } selected>{ lib.Name }</option>
|
||||
} else {
|
||||
<option value={ lib.ID }>{ lib.Name }</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
if len(actions) > 0 {
|
||||
<div class="flex items-center gap-2">
|
||||
for _, action := range actions {
|
||||
@action
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
id="loading-spinner"
|
||||
class="hidden fixed inset-0 bg-opacity-50 flex items-center justify-center z-50"
|
||||
style="background-color: var(--bg-primary);"
|
||||
>
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-b-2" style="border-color: var(--accent);"></div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ DashboardActions() {
|
||||
<button
|
||||
data-action="open-dashboard-settings"
|
||||
class="p-2 rounded-lg hover:bg-gray-700 transition-colors"
|
||||
style="background-color: var(--bg-secondary);"
|
||||
title="Customize Dashboard"
|
||||
>
|
||||
⚙️
|
||||
</button>
|
||||
<button
|
||||
data-action="reload-page"
|
||||
class="p-2 rounded-lg hover:bg-gray-700 transition-colors"
|
||||
style="background-color: var(--bg-secondary);"
|
||||
title="Refresh"
|
||||
>
|
||||
🔄
|
||||
</button>
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
func LibrarySwitcher(libData []LibraryData, currentLibraryID string, actions ...templ.Component) templ.Component {
|
||||
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
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"sticky top-0 z-40 bg-opacity-95 backdrop-blur border-b\" style=\"background-color: var(--bg-primary);\"><div class=\"w-full px-4 py-3 flex items-center justify-between\"><div class=\"flex items-center gap-4\"><label class=\"text-sm font-medium\" style=\"color: var(--text-secondary)\">Library:</label> <select id=\"library-select\" name=\"library_id\" class=\"px-4 py-2 rounded-lg border focus:ring-2 focus:ring-blue-500\" style=\"background-color: var(--bg-secondary); color: var(--text-primary);\"><option value=\"\">All Libraries</option> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, lib := range libData {
|
||||
if lib.ID == currentLibraryID {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<option value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(lib.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 17, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" selected>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 17, Col: 51}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "</option>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<option value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(lib.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 19, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/library_switcher.templ`, Line: 19, Col: 42}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</option>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</select></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if len(actions) > 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<div class=\"flex items-center gap-2\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, action := range actions {
|
||||
templ_7745c5c3_Err = action.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</div><div id=\"loading-spinner\" class=\"hidden fixed inset-0 bg-opacity-50 flex items-center justify-center z-50\" style=\"background-color: var(--bg-primary);\"><div class=\"animate-spin rounded-full h-12 w-12 border-b-2\" style=\"border-color: var(--accent);\"></div></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func DashboardActions() templ.Component {
|
||||
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
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var6 == nil {
|
||||
templ_7745c5c3_Var6 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<button data-action=\"open-dashboard-settings\" class=\"p-2 rounded-lg hover:bg-gray-700 transition-colors\" style=\"background-color: var(--bg-secondary);\" title=\"Customize Dashboard\">⚙️</button> <button data-action=\"reload-page\" class=\"p-2 rounded-lg hover:bg-gray-700 transition-colors\" style=\"background-color: var(--bg-secondary);\" title=\"Refresh\">🔄</button>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -34,11 +34,11 @@ func ProfileForm(user User, actionURL string, requireCurrentPassword bool, showR
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(actionURL)
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(actionURL)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/profile_form.templ`, Line: 5, Col: 20}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -47,11 +47,11 @@ func ProfileForm(user User, actionURL string, requireCurrentPassword bool, showR
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(user.Username)
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(user.Username)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/profile_form.templ`, Line: 20, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -60,11 +60,11 @@ func ProfileForm(user User, actionURL string, requireCurrentPassword bool, showR
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(user.Email)
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(user.Email)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/profile_form.templ`, Line: 30, Col: 24}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -73,11 +73,11 @@ func ProfileForm(user User, actionURL string, requireCurrentPassword bool, showR
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.ResolveAttributeValue(user.FirstName)
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(user.FirstName)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/profile_form.templ`, Line: 40, Col: 28}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -86,11 +86,11 @@ func ProfileForm(user User, actionURL string, requireCurrentPassword bool, showR
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.ResolveAttributeValue(user.LastName)
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(user.LastName)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/profile_form.templ`, Line: 51, Col: 27}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var6)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -449,11 +449,11 @@ func ProfileForm(user User, actionURL string, requireCurrentPassword bool, showR
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.ResolveAttributeValue("/api/auth/password/" + user.ID)
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs("/api/auth/password/" + user.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/profile_form.templ`, Line: 199, Col: 46}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var7)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -70,11 +70,11 @@ func Reader(user User, metadata ReaderMetadata, progress ReadingProgress, bookma
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.ResolveAttributeValue(readerInitExpr(metadata, progress))
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(readerInitExpr(metadata, progress))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 41, Col: 46}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -349,11 +349,11 @@ func ReaderBookmarksPanel(bookmarks []Bookmark) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(bookmark.CfiPosition)
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(bookmark.CfiPosition)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/reader.templ`, Line: 382, Col: 38}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
+4
-32
@@ -16,35 +16,7 @@ templ Series(user User, seriesList []SeriesCardData, libData []LibraryData, curr
|
||||
</head>
|
||||
<body x-data="seriesPage" x-init="initSeriesPage()" class="theme-{ user.Theme }">
|
||||
@Header(user, "/series")
|
||||
<!-- Sticky Library Selector -->
|
||||
<div class="sticky top-0 z-40 bg-opacity-95 backdrop-blur border-b" style="background-color: var(--bg-primary);">
|
||||
<div class="w-full px-4 py-3 flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="text-sm font-medium" style="color: var(--text-secondary)">Library:</label>
|
||||
<select
|
||||
id="library-select"
|
||||
name="library_id"
|
||||
class="px-4 py-2 rounded-lg border focus:ring-2 focus:ring-blue-500"
|
||||
style="background-color: var(--bg-secondary); color: var(--text-primary);"
|
||||
>
|
||||
for _, lib := range libData {
|
||||
if lib.ID == currentLibraryID {
|
||||
<option value={ lib.ID } selected>{ lib.Name }</option>
|
||||
} else {
|
||||
<option value={ lib.ID }>{ lib.Name }</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="loading-spinner"
|
||||
class="hidden fixed inset-0 bg-opacity-50 flex items-center justify-center z-50"
|
||||
style="background-color: var(--bg-primary);"
|
||||
>
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-b-2" style="border-color: var(--accent);"></div>
|
||||
</div>
|
||||
@LibrarySwitcher(libData, currentLibraryID)
|
||||
<!-- Series Grid -->
|
||||
<div id="series-container">
|
||||
<main class="w-full px-4 py-8">
|
||||
@@ -58,7 +30,7 @@ templ Series(user User, seriesList []SeriesCardData, libData []LibraryData, curr
|
||||
}
|
||||
<div id="series-grid" class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-6">
|
||||
for _, series := range seriesList {
|
||||
@SeriesCard(series, currentLibraryID)
|
||||
@SeriesCard(series)
|
||||
}
|
||||
</div>
|
||||
<!-- Pagination -->
|
||||
@@ -94,8 +66,8 @@ templ Series(user User, seriesList []SeriesCardData, libData []LibraryData, curr
|
||||
</html>
|
||||
}
|
||||
|
||||
templ SeriesCard(series SeriesCardData, libraryID string) {
|
||||
<a href={ "/series/detail?name=" + url.QueryEscape(series.Name) + "&library_id=" + libraryID } class="block">
|
||||
templ SeriesCard(series SeriesCardData) {
|
||||
<a href={ "/series/detail?name=" + url.QueryEscape(series.Name) } class="block">
|
||||
<div class="series-card rounded-lg overflow-hidden cursor-pointer hover:shadow-lg transition-shadow" style="background-color: var(--bg-secondary);">
|
||||
<div class={ "stacked-covers cover-count-" + fmt.Sprintf("%d", len(series.CoverPaths)) }>
|
||||
for i, cover := range series.CoverPaths {
|
||||
|
||||
+129
-194
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -42,178 +42,113 @@ func Series(user User, seriesList []SeriesCardData, libData []LibraryData, curre
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<!-- Sticky Library Selector --><div class=\"sticky top-0 z-40 bg-opacity-95 backdrop-blur border-b\" style=\"background-color: var(--bg-primary);\"><div class=\"w-full px-4 py-3 flex items-center justify-between\"><div class=\"flex items-center gap-4\"><label class=\"text-sm font-medium\" style=\"color: var(--text-secondary)\">Library:</label> <select id=\"library-select\" name=\"library_id\" class=\"px-4 py-2 rounded-lg border focus:ring-2 focus:ring-blue-500\" style=\"background-color: var(--bg-secondary); color: var(--text-primary);\">")
|
||||
templ_7745c5c3_Err = LibrarySwitcher(libData, currentLibraryID).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, lib := range libData {
|
||||
if lib.ID == currentLibraryID {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<option value=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<!-- Series Grid --><div id=\"series-container\"><main class=\"w-full px-4 py-8\"><h1 class=\"text-3xl font-bold mb-6\" style=\"color: var(--text-primary)\">Series</h1>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if len(seriesList) == 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<div id=\"series-empty\" class=\"text-center py-16\" style=\"color: var(--text-secondary)\"><div class=\"text-6xl mb-4\">📚</div><h3 class=\"text-xl font-semibold mb-2\" style=\"color: var(--text-primary)\">No Series Found</h3><p>Books with series metadata will appear here</p></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "<div id=\"series-grid\" class=\"grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-6\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, series := range seriesList {
|
||||
templ_7745c5c3_Err = SeriesCard(series).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</div><!-- Pagination -->")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if totalPages > 1 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<div id=\"series-pagination\" class=\"flex justify-center items-center gap-4 mt-8\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if currentPage > 1 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
||||
var templ_7745c5c3_Var2 templ.SafeURL
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs(fmt.Sprintf("/series?library_id=%s&page=%d", currentLibraryID, currentPage-1))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 32, Col: 31}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 41, Col: 93}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" selected>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\" class=\"px-4 py-2 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary);\">← Previous</a> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 32, Col: 53}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<span class=\"text-sm\" style=\"color: var(--text-secondary)\">Page ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", currentPage))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 49, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, " of ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", totalPages))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 49, Col: 82}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if currentPage < totalPages {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</option>")
|
||||
var templ_7745c5c3_Var5 templ.SafeURL
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinURLErrs(fmt.Sprintf("/series?library_id=%s&page=%d", currentLibraryID, currentPage+1))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
} else {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<option value=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.ResolveAttributeValue(lib.ID)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 34, Col: 31}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(lib.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 34, Col: 44}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 53, Col: 93}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</option>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\" class=\"px-4 py-2 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary);\">Next →</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</select></div></div></div><div id=\"loading-spinner\" class=\"hidden fixed inset-0 bg-opacity-50 flex items-center justify-center z-50\" style=\"background-color: var(--bg-primary);\"><div class=\"animate-spin rounded-full h-12 w-12 border-b-2\" style=\"border-color: var(--accent);\"></div></div><!-- Series Grid --><div id=\"series-container\"><main class=\"w-full px-4 py-8\"><h1 class=\"text-3xl font-bold mb-6\" style=\"color: var(--text-primary)\">Series</h1>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if len(seriesList) == 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div id=\"series-empty\" class=\"text-center py-16\" style=\"color: var(--text-secondary)\"><div class=\"text-6xl mb-4\">📚</div><h3 class=\"text-xl font-semibold mb-2\" style=\"color: var(--text-primary)\">No Series Found</h3><p>Books with series metadata will appear here</p></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<div id=\"series-grid\" class=\"grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-6\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, series := range seriesList {
|
||||
templ_7745c5c3_Err = SeriesCard(series, currentLibraryID).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div><!-- Pagination -->")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if totalPages > 1 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<div id=\"series-pagination\" class=\"flex justify-center items-center gap-4 mt-8\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if currentPage > 1 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 templ.SafeURL
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs(fmt.Sprintf("/series?library_id=%s&page=%d", currentLibraryID, currentPage-1))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 69, Col: 93}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\" class=\"px-4 py-2 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary);\">← Previous</a> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "<span class=\"text-sm\" style=\"color: var(--text-secondary)\">Page ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", currentPage))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 77, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " of ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", totalPages))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 77, Col: 82}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "</span> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if currentPage < totalPages {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 templ.SafeURL
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinURLErrs(fmt.Sprintf("/series?library_id=%s&page=%d", currentLibraryID, currentPage+1))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 81, Col: 93}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\" class=\"px-4 py-2 rounded-lg border\" style=\"border-color: var(--border); color: var(--text-primary);\">Next →</a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "</main></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</main></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -221,7 +156,7 @@ func Series(user User, seriesList []SeriesCardData, libData []LibraryData, curre
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</body></html>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@@ -229,7 +164,7 @@ func Series(user User, seriesList []SeriesCardData, libData []LibraryData, curre
|
||||
})
|
||||
}
|
||||
|
||||
func SeriesCard(series SeriesCardData, libraryID string) templ.Component {
|
||||
func SeriesCard(series SeriesCardData) templ.Component {
|
||||
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
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
@@ -245,146 +180,146 @@ func SeriesCard(series SeriesCardData, libraryID string) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var10 == nil {
|
||||
templ_7745c5c3_Var10 = templ.NopComponent
|
||||
templ_7745c5c3_Var6 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var6 == nil {
|
||||
templ_7745c5c3_Var6 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<a href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var11 templ.SafeURL
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinURLErrs("/series/detail?name=" + url.QueryEscape(series.Name) + "&library_id=" + libraryID)
|
||||
var templ_7745c5c3_Var7 templ.SafeURL
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs("/series/detail?name=" + url.QueryEscape(series.Name))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 98, Col: 93}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 70, Col: 64}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\" class=\"block\"><div class=\"series-card rounded-lg overflow-hidden cursor-pointer hover:shadow-lg transition-shadow\" style=\"background-color: var(--bg-secondary);\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\" class=\"block\"><div class=\"series-card rounded-lg overflow-hidden cursor-pointer hover:shadow-lg transition-shadow\" style=\"background-color: var(--bg-secondary);\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var12 = []any{"stacked-covers cover-count-" + fmt.Sprintf("%d", len(series.CoverPaths))}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
|
||||
var templ_7745c5c3_Var8 = []any{"stacked-covers cover-count-" + fmt.Sprintf("%d", len(series.CoverPaths))}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var8...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<div class=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "<div class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var12).String())
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var8).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for i, cover := range series.CoverPaths {
|
||||
var templ_7745c5c3_Var14 = []any{"cover-img cover-" + fmt.Sprintf("%d", i)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var14...)
|
||||
var templ_7745c5c3_Var10 = []any{"cover-img cover-" + fmt.Sprintf("%d", i)}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "<img src=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<img src=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(cover)
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(cover)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 103, Col: 17}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 75, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "\" class=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(templ.CSSClasses(templ_7745c5c3_Var14).String())
|
||||
var templ_7745c5c3_Var12 string
|
||||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var10).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\" alt=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" alt=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var17 string
|
||||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(series.Name)
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(series.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 105, Col: 23}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 77, Col: 23}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "\" loading=\"lazy\" onerror=\"this.src='/static/placeholder-book.svg'\"> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\" loading=\"lazy\" onerror=\"this.src='/static/placeholder-book.svg'\"> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if len(series.CoverPaths) == 0 {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<div class=\"cover-img cover-0 flex items-center justify-center\" style=\"background: var(--bg-primary);\"><span class=\"text-3xl\">📚</span></div>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "<div class=\"cover-img cover-0 flex items-center justify-center\" style=\"background: var(--bg-primary);\"><span class=\"text-3xl\">📚</span></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</div><div class=\"series-card-info px-3 py-2\"><h3 class=\"font-semibold text-sm line-clamp-2\" style=\"color: var(--text-primary)\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</div><div class=\"series-card-info px-3 py-2\"><h3 class=\"font-semibold text-sm line-clamp-2\" style=\"color: var(--text-primary)\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var18 string
|
||||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(series.Name)
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(series.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 117, Col: 99}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 89, Col: 99}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</h3><p class=\"text-xs mt-1\" style=\"color: var(--text-secondary)\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "</h3><p class=\"text-xs mt-1\" style=\"color: var(--text-secondary)\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var19 string
|
||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", series.BookCount))
|
||||
var templ_7745c5c3_Var15 string
|
||||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", series.BookCount))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 119, Col: 42}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 91, Col: 42}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, " of ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, " of ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var20 string
|
||||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", series.TotalInSeries))
|
||||
var templ_7745c5c3_Var16 string
|
||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", series.TotalInSeries))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 119, Col: 89}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/series.templ`, Line: 91, Col: 89}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, " books</p></div></div></a>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, " books</p></div></div></a>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
@@ -35,11 +35,11 @@ func ErrorToast(message string) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.ResolveAttributeValue("showErrorToast('" + message + "')")
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs("showErrorToast('" + message + "')")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/toast.templ`, Line: 5, Col: 71}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.1020
|
||||
// templ: version: v0.3.1001
|
||||
package templates
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
@@ -337,16 +337,14 @@ Alpine.data("bookshelf", () => ({
|
||||
const currentLibraryId = (
|
||||
document.getElementById("library-select") as HTMLSelectElement
|
||||
)?.value;
|
||||
if (!currentLibraryId) {
|
||||
console.error("No library selected");
|
||||
return;
|
||||
}
|
||||
|
||||
const libParam = currentLibraryId ? `&library_id=${currentLibraryId}` : "";
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/media-items/search?${field}=${encodeURIComponent(
|
||||
search,
|
||||
)}&library_id=${currentLibraryId}&limit=50`,
|
||||
)}${libParam}&limit=50`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Alpine } from "./alpine";
|
||||
import { getSelectedLibrary } from "./storage";
|
||||
import { showToast } from "./toast";
|
||||
|
||||
// ============================================================
|
||||
@@ -37,7 +38,11 @@ function setupEventDelegation(): void {
|
||||
|
||||
function backToCollection(): void {
|
||||
if (!collectionId) return;
|
||||
window.location.href = `/collections/${collectionId}`;
|
||||
const libraryId = getSelectedLibrary();
|
||||
const url = libraryId
|
||||
? `/collections/${collectionId}?library_id=${libraryId}`
|
||||
: `/collections/${collectionId}`;
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
function getFieldLabel(field: string): string {
|
||||
|
||||
+127
-48
@@ -1,6 +1,11 @@
|
||||
import { Alpine } from "./alpine";
|
||||
import { showToast } from "./toast";
|
||||
import { createWebSocket } from "./websocket";
|
||||
import {
|
||||
getCurrentLibraryId,
|
||||
initLibrarySwitcher,
|
||||
switchWithTransition,
|
||||
} from "./library-switcher";
|
||||
|
||||
let collectionId: string | null = null;
|
||||
|
||||
@@ -60,39 +65,104 @@ async function loadCollections(): Promise<void> {
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
renderCollections(data.collections || []);
|
||||
renderCollectionsGrid(data.collections || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load collections:", error);
|
||||
}
|
||||
}
|
||||
|
||||
function renderCollections(collections: CollectionData[]): void {
|
||||
function renderCollectionsGrid(collections: CollectionData[]): void {
|
||||
const container = document.getElementById("collections-list");
|
||||
if (!container) return;
|
||||
|
||||
if (collections.length === 0) {
|
||||
container.innerHTML =
|
||||
'<p class="text-center p-4" style="color: var(--text-secondary)">No collections yet</p>';
|
||||
container.innerHTML = `
|
||||
<div class="text-center py-16 col-span-full" style="color: var(--text-secondary)">
|
||||
<div class="text-6xl mb-4">📚</div>
|
||||
<h3 class="text-xl font-semibold mb-2" style="color: var(--text-primary)">No Collections Yet</h3>
|
||||
<p class="mb-4">Create collections to organize your books</p>
|
||||
<button hx-get="/collections/create-modal" hx-target="#modal-container" hx-swap="innerHTML"
|
||||
class="btn-primary px-4 py-2 rounded-lg">Create Your First Collection</button>
|
||||
</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const libraryId = getCurrentLibraryId();
|
||||
const libraryParam = libraryId ? `?library_id=${libraryId}` : "";
|
||||
|
||||
container.innerHTML = collections
|
||||
.map(
|
||||
(collection) => `
|
||||
<a href="/collections/${collection.id}" class="block p-4 rounded-lg border transition-colors hover:border-opacity-50"
|
||||
style="background-color: var(--bg-secondary); border-color: var(--border)">
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="text-2xl">${collection.icon || "📁"}</span>
|
||||
<div>
|
||||
<h3 class="font-medium" style="color: var(--text-primary)">${collection.name}</h3>
|
||||
${collection.description ? `<p class="text-sm" style="color: var(--text-secondary)">${collection.description}</p>` : ""}
|
||||
</div>
|
||||
(col) => `
|
||||
<div @click="navigateToCollection($el)" data-href="/collections/${col.id}${libraryParam}" class="block">
|
||||
<div class="card p-6 rounded-lg border-l-4 cursor-pointer hover:shadow-lg transition-shadow"
|
||||
style="background-color: var(--bg-secondary);"
|
||||
data-color="${col.color}">
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div class="text-3xl">${col.icon}</div>
|
||||
<div class="flex space-x-2">
|
||||
<button hx-get="/collections/${col.id}/edit-modal" hx-target="#modal-container"
|
||||
hx-swap="innerHTML" class="p-2 hover:opacity-80 rounded"
|
||||
style="color: var(--text-secondary); background-color: var(--bg-primary);">✏️</button>
|
||||
<button hx-delete="/api/collections/${col.id}" hx-redirect="/collections"
|
||||
hx-confirm="Are you sure you want to delete this collection?"
|
||||
class="p-2 hover:opacity-80 rounded"
|
||||
style="color: var(--text-secondary); background-color: var(--bg-primary);">🗑️</button>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<h3 class="text-lg font-semibold mb-2" style="color: var(--text-primary)">${col.name}</h3>
|
||||
<p class="text-sm mb-4" style="color: var(--text-secondary)">${col.description}</p>
|
||||
${col.book_count > 0 ? `<p class="text-xs" style="color: var(--text-secondary)">${col.book_count} books</p>` : ""}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
|
||||
initColorSelection();
|
||||
Alpine.initTree(container);
|
||||
}
|
||||
|
||||
function renderCollectionBooks(books: BookInfo[]): void {
|
||||
const container = document.getElementById("books-container");
|
||||
if (!container) return;
|
||||
|
||||
if (books.length === 0) {
|
||||
container.innerHTML = `<div id="empty-state" class="col-span-full text-center py-16" style="color: var(--text-secondary)">No books in this collection yet.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = books
|
||||
.map(
|
||||
(book) => `
|
||||
<a href="/media/${book.media_item_id}">
|
||||
<div class="card p-4 rounded-lg border hover:shadow-lg transition-shadow"
|
||||
style="background-color: var(--bg-secondary); border-color: var(--border);">
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-shrink-0 pt-1">
|
||||
<input type="checkbox" onchange="toggleBookForRemoval('${book.media_item_id}')" class="w-5 h-5"/>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-semibold text-lg mb-1 line-clamp-2" style="color: var(--text-primary)">${book.title}</h3>
|
||||
${book.author ? `<p class="text-sm line-clamp-1" style="color: var(--text-secondary)">by ${book.author}</p>` : ""}
|
||||
</div>
|
||||
<div class="flex-shrink-0 w-16 sm:w-20">
|
||||
<img src="${book.cover_image_path || "/static/placeholder-book.svg"}" alt="Cover"
|
||||
class="w-full aspect-[3/4] object-cover rounded shadow-md"
|
||||
onerror="this.src='/static/placeholder-book.svg'"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 pt-3 border-t" style="border-color: var(--border);">
|
||||
<button @click="removeBook('${book.media_item_id}')" class="px-3 py-1 text-sm border rounded hover:opacity-80"
|
||||
style="border-color: var(--border); color: var(--text-secondary);">🗑️ Remove from Collection</button>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
|
||||
Alpine.initTree(container);
|
||||
}
|
||||
|
||||
async function loadCollectionRules(collectionId: string): Promise<void> {
|
||||
@@ -240,7 +310,6 @@ function renderTestResults(results: unknown[]): void {
|
||||
container.innerHTML = `<p class="p-2 text-sm" style="color: var(--text-secondary)">${Array.isArray(results) ? results.length : 0} matching books</p>`;
|
||||
}
|
||||
|
||||
// Add authorization header to all HTMX requests
|
||||
function setupHTMXAuth(): void {
|
||||
document.body.addEventListener("htmx:configRequest", function (evt: Event) {
|
||||
const token = localStorage.getItem("token");
|
||||
@@ -249,27 +318,20 @@ function setupHTMXAuth(): void {
|
||||
}
|
||||
|
||||
function navigateToCollection(element: HTMLElement): void {
|
||||
// Check if the click target is a button
|
||||
const event = window.event as Event;
|
||||
if (event && event.target instanceof HTMLElement) {
|
||||
const target = event.target as HTMLElement;
|
||||
|
||||
// If user clicked a button or button icon, don't navigate
|
||||
if (target.tagName === "BUTTON" || target.closest("button") !== null) {
|
||||
return; // Let HTMX handle the button action
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only navigate if clicking the card body
|
||||
const href = element.getAttribute("data-href");
|
||||
if (href) {
|
||||
window.location.href = href;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Collection Modal UI Helpers
|
||||
// ============================================================================
|
||||
const borderClasses: Record<string, string> = {
|
||||
blue: "border-blue-500",
|
||||
red: "border-red-500",
|
||||
@@ -278,7 +340,6 @@ const borderClasses: Record<string, string> = {
|
||||
purple: "border-purple-500",
|
||||
};
|
||||
|
||||
// Color selection for create/edit modal
|
||||
function selectColor(color: string): void {
|
||||
const colorInput = document.getElementById(
|
||||
"collection-color",
|
||||
@@ -286,7 +347,6 @@ function selectColor(color: string): void {
|
||||
if (colorInput) {
|
||||
colorInput.value = color;
|
||||
}
|
||||
// Update visual selection
|
||||
document.querySelectorAll(".color-option").forEach((btn) => {
|
||||
(btn as HTMLElement).style.outline = "none";
|
||||
(btn as HTMLElement).style.outlineOffset = "0";
|
||||
@@ -299,7 +359,7 @@ function selectColor(color: string): void {
|
||||
selectedBtn.style.outlineOffset = "3px";
|
||||
}
|
||||
}
|
||||
// Close modal (removes from DOM)
|
||||
|
||||
function closeCollectionModal(): void {
|
||||
const modal = document.querySelector(".fixed.inset-0");
|
||||
if (modal) {
|
||||
@@ -307,7 +367,6 @@ function closeCollectionModal(): void {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize color selection on page load
|
||||
function initColorSelection(): void {
|
||||
const colorInput = document.getElementById(
|
||||
"collection-color",
|
||||
@@ -316,21 +375,17 @@ function initColorSelection(): void {
|
||||
selectColor(colorInput.value);
|
||||
}
|
||||
|
||||
// Apply border color classes to collection cards
|
||||
document.querySelectorAll("[data-color]").forEach((card) => {
|
||||
const color = (card as HTMLElement).getAttribute("data-color");
|
||||
if (color && borderClasses[color]) {
|
||||
// Remove old border color classes
|
||||
Object.values(borderClasses).forEach((cls) => {
|
||||
(card as HTMLElement).classList.remove(cls);
|
||||
});
|
||||
// Add new border color class
|
||||
(card as HTMLElement).classList.add(borderClasses[color]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize icon grid when modal is loaded via HTMX
|
||||
function setupHTMXModalInit(): void {
|
||||
document.body.addEventListener("htmx:afterSwap", function (evt: CustomEvent) {
|
||||
const target = evt.detail.target;
|
||||
@@ -342,10 +397,8 @@ function setupHTMXModalInit(): void {
|
||||
}
|
||||
|
||||
function showAllIcons(): void {
|
||||
// Populate grid if empty
|
||||
populateIconGrid();
|
||||
|
||||
// Clear search filter
|
||||
const searchInput = document.getElementById(
|
||||
"icon-search",
|
||||
) as HTMLInputElement;
|
||||
@@ -353,7 +406,6 @@ function showAllIcons(): void {
|
||||
searchInput.value = "";
|
||||
}
|
||||
|
||||
// Show all icons
|
||||
const iconGrid = document.getElementById("icon-grid");
|
||||
if (!iconGrid) return;
|
||||
|
||||
@@ -363,9 +415,7 @@ function showAllIcons(): void {
|
||||
});
|
||||
}
|
||||
|
||||
// Icon data with keywords (single source of truth)
|
||||
const iconData: Record<string, string[]> = {
|
||||
// Books & Reading
|
||||
"📚": ["book", "books", "library", "read", "reading"],
|
||||
"📖": ["book", "open", "read", "reading"],
|
||||
"📝": ["memo", "note", "write", "writing", "edit"],
|
||||
@@ -379,8 +429,6 @@ const iconData: Record<string, string[]> = {
|
||||
"📗": ["book", "read", "green"],
|
||||
"📘": ["book", "read", "blue"],
|
||||
"📙": ["book", "read", "orange"],
|
||||
|
||||
// Favorites & Activities
|
||||
"⭐": ["star", "favorite", "like", "rating"],
|
||||
"❤️": ["heart", "love", "favorite", "like"],
|
||||
"🔥": ["fire", "hot", "popular", "trending", "flame"],
|
||||
@@ -392,8 +440,6 @@ const iconData: Record<string, string[]> = {
|
||||
"❌": ["cross", "x", "wrong", "error", "fail"],
|
||||
"⚡️": ["bolt", "fast", "quick", "energy"],
|
||||
"🚀": ["rocket", "fast", "launch", "space"],
|
||||
|
||||
// Places & Objects
|
||||
"💎": ["gem", "diamond", "stone", "rich"],
|
||||
"👍": ["thumb", "up", "good", "yes", "like"],
|
||||
"👎": ["thumb", "down", "bad", "no", "dislike"],
|
||||
@@ -403,14 +449,11 @@ const iconData: Record<string, string[]> = {
|
||||
"✈️": ["plane", "airplane", "fly", "travel"],
|
||||
"🎮": ["game", "play", "video", "gaming"],
|
||||
};
|
||||
// Helper: Get just the emoji list
|
||||
|
||||
function populateIconGrid(): void {
|
||||
const iconGrid = document.getElementById("icon-grid");
|
||||
if (!iconGrid) return;
|
||||
// Clear any existing content
|
||||
iconGrid.innerHTML = "";
|
||||
// Generate buttons from iconData
|
||||
Object.entries(iconData).forEach(([emoji, keywords]) => {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
@@ -433,7 +476,6 @@ function selectIcon(icon: string): void {
|
||||
if (iconInput) iconInput.value = icon;
|
||||
if (searchInput) searchInput.value = icon;
|
||||
|
||||
// Visual feedback
|
||||
document.querySelectorAll(".icon-btn").forEach((btn) => {
|
||||
(btn as HTMLElement).style.outline = "none";
|
||||
(btn as HTMLElement).style.backgroundColor = "";
|
||||
@@ -447,12 +489,12 @@ function selectIcon(icon: string): void {
|
||||
selectedBtn.style.backgroundColor = "var(--bg-secondary)";
|
||||
}
|
||||
}
|
||||
|
||||
function filterIcons(searchTerm: string): void {
|
||||
const iconGrid = document.getElementById("icon-grid");
|
||||
const iconInput = document.getElementById(
|
||||
"collection-icon",
|
||||
) as HTMLInputElement;
|
||||
// Update hidden input with typed value
|
||||
if (iconInput) iconInput.value = searchTerm;
|
||||
if (!iconGrid) return;
|
||||
const buttons = iconGrid.querySelectorAll(".icon-btn");
|
||||
@@ -462,7 +504,6 @@ function filterIcons(searchTerm: string): void {
|
||||
const emoji = (btn as HTMLElement).textContent || "";
|
||||
const keywords = iconData[emoji] || [];
|
||||
|
||||
// Search in keywords OR emoji itself
|
||||
const matches =
|
||||
searchTerm === "" ||
|
||||
emoji.includes(searchTerm) ||
|
||||
@@ -474,7 +515,44 @@ function filterIcons(searchTerm: string): void {
|
||||
});
|
||||
}
|
||||
|
||||
// Export functions globally
|
||||
function isDetailPage(): boolean {
|
||||
const dataEl = document.getElementById("collection-data");
|
||||
return !!dataEl?.dataset.id;
|
||||
}
|
||||
|
||||
function initCollectionsPage(): void {
|
||||
if (isDetailPage()) {
|
||||
const dataEl = document.getElementById("collection-data");
|
||||
const collId = dataEl?.dataset.id || "";
|
||||
|
||||
initLibrarySwitcher({
|
||||
onSwitch: async (libraryId) => {
|
||||
const dataEl = document.getElementById("collection-data");
|
||||
if (dataEl) dataEl.dataset.libraryId = libraryId;
|
||||
|
||||
await switchWithTransition("books-container", async () => {
|
||||
const param = libraryId ? `?library_id=${libraryId}` : "";
|
||||
const response = await fetch(
|
||||
`/api/collections/${collId}${param}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
if (!response.ok) throw new Error("Failed to load collection");
|
||||
const data = await response.json();
|
||||
renderCollectionBooks(data.books || []);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
initializeCollectionWebSocket();
|
||||
setupHTMXModalInit();
|
||||
}
|
||||
|
||||
export {
|
||||
closeCollectionModal,
|
||||
createRule,
|
||||
@@ -482,6 +560,7 @@ export {
|
||||
filterIcons,
|
||||
initColorSelection,
|
||||
initializeCollectionWebSocket,
|
||||
initCollectionsPage,
|
||||
loadCollectionRules,
|
||||
loadCollections,
|
||||
navigateToCollection,
|
||||
@@ -495,13 +574,13 @@ export {
|
||||
};
|
||||
|
||||
Alpine.data("collections", () => ({
|
||||
// Collection Methods
|
||||
closeCollectionModal,
|
||||
createRule,
|
||||
deleteRule,
|
||||
filterIcons,
|
||||
initColorSelection,
|
||||
initializeCollectionWebSocket,
|
||||
initCollectionsPage,
|
||||
loadCollectionRules,
|
||||
loadCollections,
|
||||
navigateToCollection,
|
||||
|
||||
+39
-112
@@ -1,9 +1,7 @@
|
||||
import { Alpine } from "./alpine";
|
||||
import { apiPost, apiPut } from "./api";
|
||||
import { showToast } from "./toast";
|
||||
|
||||
// Dashboard functionality with unified collections architecture
|
||||
// Procedural/imperative style (no OOP)
|
||||
import { initLibrarySwitcher, switchWithTransition } from "./library-switcher";
|
||||
|
||||
const SCROLL_AMOUNT = 300;
|
||||
|
||||
@@ -23,7 +21,7 @@ async function openDashboardSettings(): Promise<void> {
|
||||
) as HTMLSelectElement;
|
||||
const libraryId = librarySelect?.value;
|
||||
if (!libraryId) {
|
||||
showToast("No library selected", "error");
|
||||
showToast("Select a specific library to customize preferences", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,7 +38,7 @@ async function openDashboardSettings(): Promise<void> {
|
||||
} else {
|
||||
showToast("Failed to load library preferences", "error");
|
||||
console.error("API Error:", response.status, response.statusText);
|
||||
return; // Don't open modal with stale data.
|
||||
return;
|
||||
}
|
||||
|
||||
const modal = document.getElementById(
|
||||
@@ -64,7 +62,6 @@ function applyPreferencesToModal(prefs: any): void {
|
||||
display.textContent = String(prefs.items_per_section);
|
||||
}
|
||||
|
||||
// Update checkboxes and reorder items
|
||||
const items = collectionList.querySelectorAll(
|
||||
"[data-collection-id]",
|
||||
) as NodeListOf<HTMLElement>;
|
||||
@@ -76,11 +73,9 @@ function applyPreferencesToModal(prefs: any): void {
|
||||
'input[type="checkbox"]',
|
||||
) as HTMLInputElement;
|
||||
|
||||
// Update checkbox state
|
||||
const isHidden = prefs.hidden_collections.includes(collectionId);
|
||||
if (checkbox) checkbox.checked = !isHidden;
|
||||
|
||||
// Sort according to collection_order
|
||||
const orderIndex = prefs.collection_order.indexOf(collectionId);
|
||||
if (orderIndex !== -1) {
|
||||
orderedItems[orderIndex] = item;
|
||||
@@ -89,7 +84,6 @@ function applyPreferencesToModal(prefs: any): void {
|
||||
}
|
||||
});
|
||||
|
||||
// Reorder in DOM
|
||||
orderedItems.forEach((item) => collectionList.appendChild(item));
|
||||
}
|
||||
|
||||
@@ -99,6 +93,23 @@ function closeDashboardSettings(): void {
|
||||
) as HTMLElement;
|
||||
modal?.classList.add("hidden");
|
||||
}
|
||||
|
||||
async function fetchAndRenderSections(libraryId: string): Promise<void> {
|
||||
const param = libraryId ? `library_id=${libraryId}` : "";
|
||||
const response = await fetch(
|
||||
`/api/dashboard/sections?${param}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
if (!response.ok) throw new Error("Failed to load sections");
|
||||
const data = await response.json();
|
||||
renderDashboardCollections(data.sections);
|
||||
}
|
||||
|
||||
async function saveDashboardSettings(): Promise<void> {
|
||||
const collectionList = document.getElementById(
|
||||
"collection-list",
|
||||
@@ -138,33 +149,18 @@ async function saveDashboardSettings(): Promise<void> {
|
||||
if (response.ok) {
|
||||
showToast("Dashboard settings saved", "success");
|
||||
closeDashboardSettings();
|
||||
// Fetch updated sections and re-render (like switchLibrary does)
|
||||
if (!libraryId) {
|
||||
showToast("Unable to refresh dashboard - no library selected", "error");
|
||||
showToast("Select a specific library to customize preferences", "error");
|
||||
return;
|
||||
}
|
||||
const sectionResponse = await fetch(
|
||||
`/api/dashboard/sections?library_id=${libraryId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
if (sectionResponse.ok) {
|
||||
const data = await sectionResponse.json();
|
||||
renderDashboardCollections(data.sections);
|
||||
localStorage.setItem("selectedLibrary", libraryId);
|
||||
} else {
|
||||
showToast("Failed to refresh sections", "error");
|
||||
}
|
||||
await fetchAndRenderSections(libraryId);
|
||||
}
|
||||
} catch (error) {
|
||||
showToast("Failed to save settings", "error");
|
||||
console.error("Save dashboard settings error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function restoreSystemCollection(
|
||||
collectionName: string,
|
||||
collectionTitle: string,
|
||||
@@ -192,50 +188,6 @@ async function restoreSystemCollection(
|
||||
}
|
||||
}
|
||||
|
||||
async function switchLibrary(libraryId: string): Promise<void> {
|
||||
const container = document.getElementById(
|
||||
"collections-container",
|
||||
) as HTMLElement;
|
||||
const loading = document.getElementById("loading-spinner") as HTMLElement;
|
||||
|
||||
if (!container || !loading) return;
|
||||
|
||||
try {
|
||||
// Step 1: Fade out current content (150ms)
|
||||
container.classList.add("opacity-0", "transition-opacity", "duration-150");
|
||||
|
||||
// Wait for fade-out to complete
|
||||
await new Promise((resolve) => setTimeout(resolve, 150));
|
||||
|
||||
// Step 2: Show loading spinner
|
||||
loading.classList.remove("hidden");
|
||||
|
||||
// Step 3: Fetch new data
|
||||
const response = await fetch(
|
||||
`/api/dashboard/sections?library_id=${libraryId}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to load sections");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
renderDashboardCollections(data.sections);
|
||||
localStorage.setItem("selectedLibrary", libraryId);
|
||||
} catch (error) {
|
||||
showToast("Failed to load library", "error");
|
||||
console.error("Switch library error:", error);
|
||||
} finally {
|
||||
loading.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function renderSectionHTML(section: SectionData): string {
|
||||
return `
|
||||
<div class="dashboard-collection mb-8" data-collection-id="${section.id}">
|
||||
@@ -264,8 +216,8 @@ function renderSectionHTML(section: SectionData): string {
|
||||
|
||||
<div id="carousel-track-${section.id}"
|
||||
class="carousel-track flex gap-4 overflow-x-auto
|
||||
scroll-smooth snap-x snap-mandatory
|
||||
px-12 pb-4"
|
||||
scroll-smooth snap-x snap-mandatory
|
||||
px-12 pb-4"
|
||||
style="scrollbar-width: none; -ms-overflow-style: none;">
|
||||
${
|
||||
section.items.length > 0
|
||||
@@ -297,23 +249,17 @@ function renderDashboardCollections(sections: SectionData[]): void {
|
||||
) as HTMLElement;
|
||||
if (!container) return;
|
||||
|
||||
// Preserve wood paneling attribute
|
||||
const currentWood = container.getAttribute("data-wood");
|
||||
|
||||
// Replace content while still invisible (opacity-0 from switchLibrary)
|
||||
container.innerHTML = sections.map((section) => renderSectionHTML(section)).join("");
|
||||
|
||||
// Step 5: Fade in new content (300ms for smoother entrance)
|
||||
container.classList.remove("duration-150");
|
||||
container.classList.add("duration-300");
|
||||
|
||||
// Trigger reflow to ensure transition happens
|
||||
void container.offsetHeight;
|
||||
|
||||
// Fade to visible
|
||||
container.classList.remove("opacity-0");
|
||||
|
||||
// Clean up transition classes after animation completes
|
||||
setTimeout(() => {
|
||||
container.classList.remove("transition-opacity", "duration-300");
|
||||
}, 300);
|
||||
@@ -352,21 +298,15 @@ function renderBookCard(book: BookInfo): string {
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
async function reloadPage(): Promise<void> {
|
||||
//Get current library from dropdown
|
||||
const librarySelect = document.getElementById(
|
||||
"library-select",
|
||||
) as HTMLSelectElement;
|
||||
const currentLibraryId = librarySelect?.value;
|
||||
const libraryId = librarySelect?.value || "";
|
||||
|
||||
if (!currentLibraryId) {
|
||||
showToast("No library selected", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reuse switchLibrary logic - it handles the fade transition
|
||||
await switchLibrary(currentLibraryId);
|
||||
await switchWithTransition("collections-container", () =>
|
||||
fetchAndRenderSections(libraryId),
|
||||
);
|
||||
}
|
||||
|
||||
function updateItemsCount(
|
||||
@@ -427,6 +367,13 @@ function initDragAndDrop(): void {
|
||||
function initDashboard() {
|
||||
initDragAndDrop();
|
||||
|
||||
initLibrarySwitcher({
|
||||
onSwitch: (libraryId) =>
|
||||
switchWithTransition("collections-container", () =>
|
||||
fetchAndRenderSections(libraryId),
|
||||
),
|
||||
});
|
||||
|
||||
document.addEventListener("click", (e: Event) => {
|
||||
const target = e.target as HTMLElement;
|
||||
const actionElem = target.closest("[data-action]") as HTMLElement;
|
||||
@@ -466,18 +413,10 @@ function initDashboard() {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case "reload-page":
|
||||
reloadPage();
|
||||
break;
|
||||
|
||||
case "switch-library": {
|
||||
const select = target as HTMLSelectElement;
|
||||
if (select.value) switchLibrary(select.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
document.addEventListener("input", (e: Event) => {
|
||||
@@ -495,30 +434,18 @@ function initDashboard() {
|
||||
}
|
||||
});
|
||||
|
||||
const librarySelect = document.getElementById(
|
||||
"library-select",
|
||||
) as HTMLSelectElement;
|
||||
if (librarySelect) {
|
||||
librarySelect.addEventListener("change", (e) => {
|
||||
const target = e.target as HTMLSelectElement;
|
||||
if (target.value) {
|
||||
switchLibrary(target.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener("bookhoard:scan-complete", async () => {
|
||||
console.log("[dashboard] scan-complete event received");
|
||||
const librarySelect = document.getElementById(
|
||||
"library-select",
|
||||
) as HTMLSelectElement;
|
||||
const libraryId = librarySelect?.value;
|
||||
const libraryId = librarySelect?.value || "";
|
||||
console.log("[dashboard] libraryId:", libraryId);
|
||||
if (!libraryId) return;
|
||||
|
||||
try {
|
||||
const param = libraryId ? `library_id=${libraryId}` : "";
|
||||
const response = await fetch(
|
||||
`/api/dashboard/sections?library_id=${libraryId}`,
|
||||
`/api/dashboard/sections?${param}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Alpine } from "./alpine";
|
||||
import { ALL_LIBRARIES, getSelectedLibrary, setSelectedLibrary } from "./storage";
|
||||
import { showToast } from "./toast";
|
||||
|
||||
export function getCurrentLibraryId(): string {
|
||||
return getSelectedLibrary();
|
||||
}
|
||||
|
||||
export async function switchWithTransition(
|
||||
containerId: string,
|
||||
fetchFn: () => Promise<void>,
|
||||
): Promise<void> {
|
||||
const container = document.getElementById(containerId);
|
||||
const loading = document.getElementById("loading-spinner");
|
||||
|
||||
if (!container || !loading) return;
|
||||
|
||||
try {
|
||||
container.classList.add(
|
||||
"opacity-0",
|
||||
"transition-opacity",
|
||||
"duration-150",
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 150));
|
||||
|
||||
loading.classList.remove("hidden");
|
||||
|
||||
await fetchFn();
|
||||
|
||||
container.classList.remove("duration-150");
|
||||
container.classList.add("duration-300");
|
||||
void container.offsetHeight;
|
||||
container.classList.remove("opacity-0");
|
||||
|
||||
setTimeout(() => {
|
||||
container.classList.remove("transition-opacity", "duration-300");
|
||||
}, 300);
|
||||
} catch (error) {
|
||||
container.classList.remove(
|
||||
"opacity-0",
|
||||
"transition-opacity",
|
||||
"duration-150",
|
||||
"duration-300",
|
||||
);
|
||||
showToast("Failed to load library", "error");
|
||||
console.error("Switch library error:", error);
|
||||
} finally {
|
||||
loading.classList.add("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
export interface LibrarySwitcherOptions {
|
||||
onSwitch: (libraryId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export function initLibrarySwitcher(options: LibrarySwitcherOptions): void {
|
||||
const librarySelect = document.getElementById(
|
||||
"library-select",
|
||||
) as HTMLSelectElement;
|
||||
if (!librarySelect) return;
|
||||
|
||||
const stored = localStorage.getItem("selectedLibrary");
|
||||
if (stored === ALL_LIBRARIES) {
|
||||
librarySelect.value = "";
|
||||
} else if (stored) {
|
||||
const option = librarySelect.querySelector(
|
||||
`option[value="${stored}"]`,
|
||||
);
|
||||
if (option) {
|
||||
librarySelect.value = stored;
|
||||
}
|
||||
}
|
||||
|
||||
librarySelect.addEventListener("change", async (e) => {
|
||||
const target = e.target as HTMLSelectElement;
|
||||
const libraryId = target.value;
|
||||
|
||||
setSelectedLibrary(libraryId);
|
||||
await options.onSwitch(libraryId);
|
||||
});
|
||||
}
|
||||
|
||||
Alpine.data("librarySwitcher", () => ({}));
|
||||
@@ -21,6 +21,7 @@ import "./events";
|
||||
import "./header";
|
||||
import "./index";
|
||||
import "./library";
|
||||
import "./library-switcher";
|
||||
import "./linking";
|
||||
import "./login";
|
||||
import "./password_validation";
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
import { Alpine } from "./alpine";
|
||||
import { setSelectedLibrary } from "./storage";
|
||||
|
||||
let searchInputTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
const SEARCH_DEBOUNCE_MS = 300;
|
||||
@@ -298,7 +299,7 @@ function searchEscapeHtml(text: string): string {
|
||||
}
|
||||
|
||||
function selectLibraryAndBook(libraryId: string, bookId: string): void {
|
||||
localStorage.setItem("selectedLibrary", libraryId);
|
||||
setSelectedLibrary(libraryId);
|
||||
localStorage.setItem("selectedBook", bookId);
|
||||
hideSearchResults();
|
||||
}
|
||||
|
||||
+18
-64
@@ -1,6 +1,6 @@
|
||||
import { Alpine } from "./alpine";
|
||||
import { showToast } from "./toast";
|
||||
import { setSelectedLibrary } from "./storage";
|
||||
import { initLibrarySwitcher, switchWithTransition } from "./library-switcher";
|
||||
|
||||
interface SeriesItem {
|
||||
name: string;
|
||||
@@ -10,8 +10,8 @@ interface SeriesItem {
|
||||
last_entry_at: string;
|
||||
}
|
||||
|
||||
function renderSeriesCard(series: SeriesItem, libraryId: string): string {
|
||||
const href = `/series/detail?name=${encodeURIComponent(series.name)}&library_id=${libraryId}`;
|
||||
function renderSeriesCard(series: SeriesItem): string {
|
||||
const href = `/series/detail?name=${encodeURIComponent(series.name)}`;
|
||||
const coverCount = series.cover_paths.length;
|
||||
const coverClass = `cover-count-${coverCount}`;
|
||||
|
||||
@@ -58,17 +58,18 @@ function renderSeriesContent(
|
||||
</main>`;
|
||||
}
|
||||
|
||||
const cardsHtml = seriesList.map((s) => renderSeriesCard(s, libraryId)).join("");
|
||||
const cardsHtml = seriesList.map((s) => renderSeriesCard(s)).join("");
|
||||
|
||||
let paginationHtml = "";
|
||||
if (totalPages > 1) {
|
||||
const libParam = libraryId ? `library_id=${libraryId}&` : "";
|
||||
const prevLink =
|
||||
currentPage > 1
|
||||
? `<a href="/series?library_id=${libraryId}&page=${currentPage - 1}" class="px-4 py-2 rounded-lg border" style="border-color: var(--border); color: var(--text-primary);">← Previous</a>`
|
||||
? `<a href="/series?${libParam}page=${currentPage - 1}" class="px-4 py-2 rounded-lg border" style="border-color: var(--border); color: var(--text-primary);">← Previous</a>`
|
||||
: "";
|
||||
const nextLink =
|
||||
currentPage < totalPages
|
||||
? `<a href="/series?library_id=${libraryId}&page=${currentPage + 1}" class="px-4 py-2 rounded-lg border" style="border-color: var(--border); color: var(--text-primary);">Next →</a>`
|
||||
? `<a href="/series?${libParam}page=${currentPage + 1}" class="px-4 py-2 rounded-lg border" style="border-color: var(--border); color: var(--text-primary);">Next →</a>`
|
||||
: "";
|
||||
paginationHtml = `
|
||||
<div class="flex justify-center items-center gap-4 mt-8">
|
||||
@@ -89,19 +90,10 @@ function renderSeriesContent(
|
||||
}
|
||||
|
||||
async function switchLibrary(libraryId: string): Promise<void> {
|
||||
const container = document.getElementById("series-container") as HTMLElement;
|
||||
const loading = document.getElementById("loading-spinner") as HTMLElement;
|
||||
|
||||
if (!container || !loading) return;
|
||||
|
||||
try {
|
||||
container.classList.add("opacity-0", "transition-opacity", "duration-150");
|
||||
await new Promise((resolve) => setTimeout(resolve, 150));
|
||||
|
||||
loading.classList.remove("hidden");
|
||||
|
||||
await switchWithTransition("series-container", async () => {
|
||||
const param = libraryId ? `library_id=${libraryId}&` : "";
|
||||
const response = await fetch(
|
||||
`/api/series?library_id=${libraryId}&limit=24&offset=0`,
|
||||
`/api/series?${param}limit=24&offset=0`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
||||
@@ -119,55 +111,17 @@ async function switchLibrary(libraryId: string): Promise<void> {
|
||||
const total: number = data.total || 0;
|
||||
const totalPages = Math.max(1, Math.ceil(total / 24));
|
||||
|
||||
container.innerHTML = renderSeriesContent(seriesList, libraryId, totalPages, 1);
|
||||
setSelectedLibrary(libraryId);
|
||||
} catch (error) {
|
||||
showToast("Failed to load series", "error");
|
||||
console.error("Switch library error:", error);
|
||||
} finally {
|
||||
loading.classList.add("hidden");
|
||||
|
||||
container.classList.remove("duration-150");
|
||||
container.classList.add("duration-300");
|
||||
void container.offsetHeight;
|
||||
container.classList.remove("opacity-0");
|
||||
|
||||
setTimeout(() => {
|
||||
container.classList.remove("transition-opacity", "duration-300");
|
||||
}, 300);
|
||||
}
|
||||
const container = document.getElementById("series-container");
|
||||
if (container) {
|
||||
container.innerHTML = renderSeriesContent(seriesList, libraryId, totalPages, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Alpine.data("seriesPage", () => ({
|
||||
initSeriesPage() {
|
||||
const librarySelect = document.getElementById(
|
||||
"library-select",
|
||||
) as HTMLSelectElement;
|
||||
if (librarySelect) {
|
||||
librarySelect.addEventListener("change", () => {
|
||||
if (librarySelect.value) {
|
||||
switchLibrary(librarySelect.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
Alpine.data("seriesDetailPage", () => ({
|
||||
initSeriesDetailPage() {
|
||||
const librarySelect = document.getElementById(
|
||||
"library-select",
|
||||
) as HTMLSelectElement;
|
||||
if (librarySelect) {
|
||||
librarySelect.addEventListener("change", () => {
|
||||
if (librarySelect.value) {
|
||||
const url = new URL(window.location.href);
|
||||
const currentLib = url.searchParams.get("library_id");
|
||||
if (currentLib === librarySelect.value) return;
|
||||
url.searchParams.set("library_id", librarySelect.value);
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
initLibrarySwitcher({
|
||||
onSwitch: switchLibrary,
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
+11
-3
@@ -1,3 +1,5 @@
|
||||
const ALL_LIBRARIES = "__all__";
|
||||
|
||||
function getToken(): string | null {
|
||||
return localStorage.getItem("token");
|
||||
}
|
||||
@@ -30,12 +32,17 @@ function setTheme(theme: string): void {
|
||||
localStorage.setItem("theme", theme);
|
||||
}
|
||||
|
||||
function getSelectedLibrary(): string | null {
|
||||
return localStorage.getItem("selectedLibrary");
|
||||
function getSelectedLibrary(): string {
|
||||
const stored = localStorage.getItem("selectedLibrary");
|
||||
if (stored === ALL_LIBRARIES) return "";
|
||||
if (stored) return stored;
|
||||
return "";
|
||||
}
|
||||
|
||||
function setSelectedLibrary(libraryId: string): void {
|
||||
localStorage.setItem("selectedLibrary", libraryId);
|
||||
const value = libraryId === "" ? ALL_LIBRARIES : libraryId;
|
||||
localStorage.setItem("selectedLibrary", value);
|
||||
document.cookie = `selectedLibrary=${encodeURIComponent(value)};path=/;max-age=${365 * 24 * 60 * 60};samesite=lax`;
|
||||
}
|
||||
|
||||
function getSelectedBook(): string | null {
|
||||
@@ -51,6 +58,7 @@ function clearAll(): void {
|
||||
}
|
||||
|
||||
export {
|
||||
ALL_LIBRARIES,
|
||||
clearAll,
|
||||
getRefreshToken,
|
||||
getSelectedBook,
|
||||
|
||||
Vendored
+1
@@ -90,6 +90,7 @@ interface CollectionData {
|
||||
icon: string;
|
||||
auto_assign_rules?: unknown;
|
||||
created_at: string;
|
||||
book_count?: number;
|
||||
}
|
||||
|
||||
// Matches handlers.BookInfo JSON response (internal/handlers/collections.go:66-71)
|
||||
|
||||
Reference in New Issue
Block a user