Repository navigation
feat(catalog): match card sizes and add richer title pages (spec 0022) - #46
Merged
Merged
Conversation
…0022) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e this and facts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r dialog Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…true cause A 404 or a bad response from the extras read said TMDB didn't respond. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r copy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Poster cards on every grid page now come out at about the home row card's width (about 180px), because the grid steps from 2 up to 6 columns. Movie and show pages gain a Trailer, Where to watch, More like this, key crew, an age rating chip and a Facts panel. Nothing is stored and the schema does not change.
Why
This implements spec 0022, Consistent card size and richer title pages (scope feature 25). A poster used to be one size in a home row and a bigger size on its See all page. Title pages also left out data TMDB already offers, so you had to leave BeStats to find a trailer or a place to stream. The new title data comes from one extra cached TMDB read per title.
getTvShowstays as it is, so the library pages, which read up to 500 shows, cost the same as before.Changes
Card size
PosterGridtakes acolumnspreset.catalogis the default: 2 / 3 (sm) / 4 (md) / 5 (lg) / 6 (xl) columns.wideis the old 2 / 4 layout, and only the season grid uses it.POSTER_GRID_SIZESandPOSTER_GRID_EAGERmaps own each preset's imagesizesand eager count, so the four localEAGER_POSTERScopies are gone.poster-grid.test.tsxworks out the expectedsizesfrom the column rule, so a column change that skips its sizes fails the suite (AC-4, AC-5).LibrarySkeletonshows exactly one row at the current column count, so a one row library no longer shifts when it loads (AC-6)./showcaseshows 6 grid cards.TMDB reads
lib/tmdb/extras.tswithfetchMovieExtrasandfetchTvExtras. Each is one/movie/{id}or/tv/{id}call withappend_to_responsefor videos, recommendations, watch providers and release dates or content ratings. They are validated by a loose schema inschemas.tsand normalized innormalize-extras.ts.getMovieExtrasandgetTvExtrasinreads.tsareuse cachewithcacheLife("hours")(AC-8).Movieadds status, budget, revenue, production companies, directors and writers, all from the existingcreditsresponse.TvShowadds type, created by, networks and production companies. Neither makes a new request.Region
lib/catalog/region.tshas the pureparseRegion.lib/region.tshasgetVisitorRegion(), wrapped in Reactcache(), which reads Vercel'sx-vercel-ip-countryheader and falls back toUS(it also rejectsXX,T1and anythingIntl.DisplayNamescan't name). It runs only inside streamed Suspense boundaries. The cached extras hold every country, and the page picks the visitor's country after the read returns, so the region is never cached, stored, logged or sent to TMDB (AC-9, AC-10).Title pages
TrailerButton(client) opens a base-ui dialog that loads ayoutube-nocookie.comembed only while the dialog is open. It goes in the newactionsslot onDetailHero(AC-11).WhereToWatch, the age rating chip and More like this (a carousel row of home row cards with your status) are each async slots in their own Suspense boundary, with skeletons while loading and aRetryLinkerror line when the read fails (AC-12 to AC-14, AC-18).title-details.tsxadds the crew lines (Directed by, Written by, Created by) and the Facts panel (AC-15, AC-16). Both pages follow the spec's section order (AC-17).SECTION_HEADING_CLASSgives every section heading one shared style.Legal
PROCESSORS./privacygains the country sentence and the trailer sentence.JUSTWATCH_ATTRIBUTIONlives inlib/legal/operator.ts, and the boundary test guards it.LEGAL_LAST_UPDATEDis now 2026-10-12 (AC-19).How to test / verify
pnpm typecheck,pnpm lintandpnpm test. The new suites includeposter-grid.test.tsx,lib/tmdb/extras.test.ts,lib/catalog/region.test.ts,lib/region.test.ts,components/catalog/title-extras.test.tsx,title-details.test.tsx,trailer-button.test.tsx,where-to-watch.test.tsxandapp/title-pages.test.ts.pnpm build./movies/[id]and/shows/[id]should still be listed as Partial Prerender (AC-10)./movies/popularat 1280px. You should see 6 columns with cards about 187px wide. Then try 375, 640, 768 and 1024px; you should see 2, 3, 4 and 5 columns. On/shows/1396the seasons should stay at 2 and 4 columns./movies/550. You should see anRchip, a Trailer button, the crew lines, Where to watch in United States, Cast, More like this and Facts (Budget$63M, Revenue$100.9M). Until you press Trailer, the network panel should show no YouTube request.curl -s -H "x-vercel-ip-country: PL" localhost:3000/movies/550should show "Where to watch in Poland" and an18chip. The valuesXX,T1,pland no header should all fall back to United States./movies/1000010should show no Trailer, no chip, and "Not streaming in United States right now."Risk & rollout
Low risk, no migrations, no flags. The pages are public catalog pages, and nothing reads new user data. The cost is one extra TMDB request per title per hour, shared by every visitor. YouTube is a new processor, so the privacy policy changes in this same PR, as spec 0017 asks. The trailer player loads only after a click. If you roll back by reverting,
LEGAL_LAST_UPDATEDand the processor list go back with it.Notes for reviewers
34434a4) is not one of spec 0022's acceptance criteria. It makes Cast match the More like this row next to it, and it keeps spec 0006's 12 member limit.TmdbError, which has since been reverted (see verify.md).mainand its own issue./check reviewhas run on this branch yet, so there is nothing for it indocs/reviews/.🤖 Generated with Claude Code