From e88b6c81e879f76683fbd817600bcac737437b2f Mon Sep 17 00:00:00 2001 From: "ognev.dev" Date: Sun, 12 Apr 2026 20:47:59 +0500 Subject: [PATCH] add release_date_sort column for accurate book sorting --- ...194301_add_released_date_sort_to_books.sql | 24 +++++++++++++++ app/ds/book.go | 16 +++++----- app/repo/book_repo.go | 28 +++++++++-------- frontend/assets/output.css | 30 +++++++++++++------ frontend/page/filter_books.templ | 4 ++- frontend/page/filter_books_templ.go | 2 +- server/handler/book_handler.go | 2 +- test/api_test/book_test.go | 20 ++++++++++--- 8 files changed, 90 insertions(+), 36 deletions(-) create mode 100644 app/db_migrations/20260412194301_add_released_date_sort_to_books.sql diff --git a/app/db_migrations/20260412194301_add_released_date_sort_to_books.sql b/app/db_migrations/20260412194301_add_released_date_sort_to_books.sql new file mode 100644 index 0000000..8c49cb3 --- /dev/null +++ b/app/db_migrations/20260412194301_add_released_date_sort_to_books.sql @@ -0,0 +1,24 @@ +ALTER TABLE books ADD COLUMN release_date_sort DATE; + +DO $$ + BEGIN + SET LOCAL lc_time = 'en_US.UTF-8'; + + UPDATE books + SET release_date_sort = CASE + -- "January 2, 2006" + WHEN release_date ~* '^[a-z]+ \d{1,2}, \d{4}$' + THEN TO_DATE(release_date, 'Month DD, YYYY') + + -- "January 2006" + WHEN release_date ~* '^[a-z]+ \d{4}$' + THEN TO_DATE(release_date, 'Month YYYY') + + -- "2006" + ELSE TO_DATE(release_date, 'YYYY') + END; + END $$; + +ALTER TABLE books ALTER COLUMN release_date_sort SET NOT NULL; + +CREATE INDEX idx_books_release_date_sort ON books (release_date_sort DESC); \ No newline at end of file diff --git a/app/ds/book.go b/app/ds/book.go index a5ad9ab..6f77517 100644 --- a/app/ds/book.go +++ b/app/ds/book.go @@ -15,12 +15,13 @@ var bookCtxKey ctxKey = "book" type Book struct { *Entity - DescriptionRaw string `json:"-"` - Description string `json:"description"` - CoverFileID ID `json:"cover_file_id"` - Authors []BookAuthor `json:"authors"` - Homepage string `json:"homepage"` - ReleaseDate string `json:"release_date"` + DescriptionRaw string `json:"-"` + Description string `json:"description"` + CoverFileID ID `json:"cover_file_id"` + Authors []BookAuthor `json:"authors"` + Homepage string `json:"homepage"` + ReleaseDate string `json:"release_date"` + ReleaseDateSort time.Time `json:"-"` } // Data returns the editable fields of the Book as a key-value map. @@ -76,8 +77,9 @@ func (b *Book) CreateRules() z.Shape { } for _, layout := range ReleaseDateLayouts { - _, err := time.Parse(layout, *val) + t, err := time.Parse(layout, *val) if err == nil { + b.ReleaseDateSort = t return true } } diff --git a/app/repo/book_repo.go b/app/repo/book_repo.go index 18f0a45..ca51e5a 100644 --- a/app/repo/book_repo.go +++ b/app/repo/book_repo.go @@ -21,13 +21,14 @@ func (r *Repo) CreateBook(ctx context.Context, b *ds.Book) error { defer span.End() return r.insert(ctx, "books", data{ - "id": b.ID, - "description_raw": b.DescriptionRaw, - "description": b.Description, - "cover_file_id": b.CoverFileID, - "authors": b.Authors, - "homepage": b.Homepage, - "release_date": b.ReleaseDate, + "id": b.ID, + "description_raw": b.DescriptionRaw, + "description": b.Description, + "cover_file_id": b.CoverFileID, + "authors": b.Authors, + "homepage": b.Homepage, + "release_date": b.ReleaseDate, + "release_date_sort": b.ReleaseDateSort, }) } @@ -82,12 +83,13 @@ func (r *Repo) UpdateBook(ctx context.Context, b *ds.Book) error { defer span.End() err := r.update(ctx, b.ID, "books", data{ - "description_raw": b.DescriptionRaw, - "description": b.Description, - "cover_file_id": b.CoverFileID, - "authors": b.Authors, - "homepage": b.Homepage, - "release_date": b.ReleaseDate, + "description_raw": b.DescriptionRaw, + "description": b.Description, + "cover_file_id": b.CoverFileID, + "authors": b.Authors, + "homepage": b.Homepage, + "release_date": b.ReleaseDate, + "release_date_sort": b.ReleaseDateSort, }) if err != nil { return fmt.Errorf("update book: %w", err) diff --git a/frontend/assets/output.css b/frontend/assets/output.css index 0eb9c68..a0a8d26 100644 --- a/frontend/assets/output.css +++ b/frontend/assets/output.css @@ -31,6 +31,7 @@ --text-lg--line-height: calc(1.75 / 1.125); --text-3xl: 1.875rem; --text-3xl--line-height: calc(2.25 / 1.875); + --font-weight-normal: 400; --font-weight-bold: 700; --radius-lg: 0.5rem; --ease-out: cubic-bezier(0, 0, 0.2, 1); @@ -3590,6 +3591,9 @@ .left-0 { left: calc(var(--spacing) * 0); } + .left-1 { + left: calc(var(--spacing) * 1); + } .left-1\/2 { left: calc(1/2 * 100%); } @@ -5920,6 +5924,9 @@ .w-6 { width: calc(var(--spacing) * 6); } + .w-11 { + width: calc(var(--spacing) * 11); + } .w-11\/12 { width: calc(11/12 * 100%); } @@ -5977,6 +5984,10 @@ .border-collapse { border-collapse: collapse; } + .-translate-x-1 { + --tw-translate-x: calc(var(--spacing) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } .-translate-x-1\/2 { --tw-translate-x: calc(calc(1/2 * 100%) * -1); translate: var(--tw-translate-x) var(--tw-translate-y); @@ -6615,6 +6626,9 @@ .border-info { border-color: var(--color-info); } + .border-neutral-content { + border-color: var(--color-neutral-content); + } .border-neutral-content\/30 { border-color: var(--color-neutral-content); @supports (color: color-mix(in lab, red, red)) { @@ -7228,9 +7242,16 @@ --tw-font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold); } + .font-normal { + --tw-font-weight: var(--font-weight-normal); + font-weight: var(--font-weight-normal); + } .text-wrap { text-wrap: wrap; } + .whitespace-nowrap { + white-space: nowrap; + } .whitespace-pre-wrap { white-space: pre-wrap; } @@ -7653,10 +7674,6 @@ } } } - .blur { - --tw-blur: blur(8px); - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } .filter { filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); } @@ -8369,11 +8386,6 @@ width: auto; } } - .lg\:grid-cols-2 { - @media (width >= 64rem) { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } .lg\:grid-cols-4 { @media (width >= 64rem) { grid-template-columns: repeat(4, minmax(0, 1fr)); diff --git a/frontend/page/filter_books.templ b/frontend/page/filter_books.templ index 5991a20..e397e5f 100644 --- a/frontend/page/filter_books.templ +++ b/frontend/page/filter_books.templ @@ -271,7 +271,7 @@ templ FilterBooksPage() {