Add compact mode to mobile checkout#11979
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR implements a collapsible mobile order summary bar for checkout. A new state hook determines when to collapse the summary based on product type and trial status. The mobile bar component displays compact trial pricing or formatted totals. Pricing components support compact rendering with shorter labels and omitted dates. The main Checkout component integrates this flow with conditional rendering and visibility toggling. Supporting infrastructure adds package exports and translations across twelve languages. ChangesMobile Checkout Summary Flow
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
|
I think so too, and I also think the expanded view doesn't feel super expanded by/linked to the summary because of the visual separation. |
|
@emilwidlund stupid refactor broke the arrow, will fix! @pieterbeulque yeah agreed, I'll see what I can do! |
|
Preview Environment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
clients/apps/web/src/components/Checkout/Checkout.tsx (1)
88-502: ⚖️ Poor tradeoffConsider refactoring to stay within the 250-line limit.
The
Checkout.tsxfile is 505 lines, which exceeds the project's 250-line limit for files inclients/**/*.{js,jsx,ts,tsx}. While this PR's additions (~30 lines) are modest, the file would benefit from extraction into smaller modules.Potential refactoring opportunities:
- Extract the embed mode rendering into a separate component
- Split the two-column layout sections into dedicated components
- Move the organization header logic to a helper component
As per coding guidelines: "Enforce max-lines ESLint rule limiting files to 250 lines of code (excluding blanks and comments). Refactor large files into smaller pieces by extracting sub-components, splitting hooks, or moving helpers to separate modules."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@clients/apps/web/src/components/Checkout/Checkout.tsx` around lines 88 - 502, The Checkout component exceeds the 250-line limit and should be split into smaller pieces: extract the embed rendering block (the embed === true return) into a new EmbedCheckout component, move the orgHeader JSX into an OrganizationHeader component, and factor the left/right column sections (product summary + media and the CheckoutForm column) into ProductSummary and CheckoutSide components; update Checkout to import and render EmbedCheckout, OrganizationHeader, ProductSummary, and CheckoutSide and keep hook calls (useCheckoutForm, useMobileSummaryState, useCheckoutFulfillmentListener, useCheckoutConfirmedRedirect) in Checkout if their shared state is required, or move related hooks into the new components if they only concern that subtree (e.g., mobile summary state into ProductSummary). Ensure props and callback names (update, confirm, form, checkout, themePreset, locale, disableCheckout, isUpdatePending) are preserved when passing to the new components.clients/packages/checkout/src/components/CheckoutTrialHeroPrice.tsx (1)
87-105: ⚡ Quick winConsider migrating to Orbit primitives instead of adding more Tailwind classes.
The new
compactmode conditionally appliesfont-semibold(line 89) andtext-xs(line 95) viaclassName, and retainsdark:text-polar-500color variants. As per coding guidelines, when touching an existing Tailwind component, prefer migrating it to<Box />and<Text />from@polar-sh/orbitrather than adding more Tailwind on top. Use typed props (e.g.,fontWeight,fontSize) instead ofclassNamefor typography and avoiddark:variants in favor of design tokens.♻️ Suggested Orbit-based implementation
Replace the spans with
<Box>and<Text>using typed props:+import { Box } from '`@polar-sh/orbit/Box`' +import { Text } from '`@polar-sh/orbit`' + return ( - <div className="flex flex-col gap-y-1"> - <span className={compact ? 'font-semibold' : undefined}> + <Box display="flex" flexDirection="column" gap="xs"> + <Text fontWeight={compact ? 'semibold' : 'normal'}> {trialLabel} - </span> - <span - className={cn( - 'dark:text-polar-500 text-sm font-normal text-gray-500', - compact && 'text-xs', - )} - > + </Text> + <Text + fontSize={compact ? 'xs' : 'sm'} + fontWeight="normal" + color="secondary" + > {t('checkout.trial.hero.then')}{' '} <strong className="font-semibold">{priceStr}</strong> {dateStr ? ` ${t('checkout.trial.hero.startingDate', { date: dateStr })}` : ''} - </span> - </div> + </Text> + </Box> )As per coding guidelines: "When touching an existing Tailwind component, prefer migrating it to Box in the same change rather than adding more Tailwind on top" and "Use from
@polar-sh/orbitfor typography rather than Tailwind text classes."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@clients/packages/checkout/src/components/CheckoutTrialHeroPrice.tsx` around lines 87 - 105, The two <span> elements in CheckoutTrialHeroPrice should be migrated to Orbit primitives: replace the outer span showing {trialLabel} with <Text> (or <Box><Text>) using typed props like fontWeight to apply the compact conditional (compact ? 'semibold' : 'normal') and replace the second span with <Text> using fontSize (compact ? 'xs' : 'sm'), color tokens instead of the dark: Tailwind class, and embed the priceStr/dateStr content there; update imports from `@polar-sh/orbit` and remove the conditional Tailwind classes ('font-semibold', 'text-xs', 'dark:text-polar-500', etc.), but keep the same logic for compact, trialLabel, priceStr, and dateStr when building the displayed string.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@clients/apps/web/src/components/Checkout/MobileOrderSummaryBar.tsx`:
- Around line 28-54: Replace the Tailwind-based <button> and inner <span>s in
MobileOrderSummaryBar with the Orbit Box/Text primitives: use <Box as="button">
(preserving props onToggle, aria-expanded={isOpen}) and map the visual classes
to typed Box props (display="flex", alignItems="center",
justifyContent="spaceBetween", padding, gap, marginX, background, borderColor,
borderY, responsive visibility) instead of className; replace the two inner
spans with <Box as="span"> or <Text> and set their alignment/typography via
typed props while keeping the conditional rendering of CheckoutHeroPrice and the
formatted currency (formatCurrency(checkout...)), and keep the ChevronDown +
twMerge usage for rotation as-is; remove the Tailwind className string and
import Box/Text from `@polar-sh/orbit`.
---
Nitpick comments:
In `@clients/apps/web/src/components/Checkout/Checkout.tsx`:
- Around line 88-502: The Checkout component exceeds the 250-line limit and
should be split into smaller pieces: extract the embed rendering block (the
embed === true return) into a new EmbedCheckout component, move the orgHeader
JSX into an OrganizationHeader component, and factor the left/right column
sections (product summary + media and the CheckoutForm column) into
ProductSummary and CheckoutSide components; update Checkout to import and render
EmbedCheckout, OrganizationHeader, ProductSummary, and CheckoutSide and keep
hook calls (useCheckoutForm, useMobileSummaryState,
useCheckoutFulfillmentListener, useCheckoutConfirmedRedirect) in Checkout if
their shared state is required, or move related hooks into the new components if
they only concern that subtree (e.g., mobile summary state into ProductSummary).
Ensure props and callback names (update, confirm, form, checkout, themePreset,
locale, disableCheckout, isUpdatePending) are preserved when passing to the new
components.
In `@clients/packages/checkout/src/components/CheckoutTrialHeroPrice.tsx`:
- Around line 87-105: The two <span> elements in CheckoutTrialHeroPrice should
be migrated to Orbit primitives: replace the outer span showing {trialLabel}
with <Text> (or <Box><Text>) using typed props like fontWeight to apply the
compact conditional (compact ? 'semibold' : 'normal') and replace the second
span with <Text> using fontSize (compact ? 'xs' : 'sm'), color tokens instead of
the dark: Tailwind class, and embed the priceStr/dateStr content there; update
imports from `@polar-sh/orbit` and remove the conditional Tailwind classes
('font-semibold', 'text-xs', 'dark:text-polar-500', etc.), but keep the same
logic for compact, trialLabel, priceStr, and dateStr when building the displayed
string.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: dd723720-5fb2-4710-8690-c1cd23557d96
📒 Files selected for processing (21)
clients/apps/web/src/components/Checkout/Checkout.tsxclients/apps/web/src/components/Checkout/MobileOrderSummaryBar.test.tsxclients/apps/web/src/components/Checkout/MobileOrderSummaryBar.tsxclients/apps/web/src/components/Checkout/useMobileSummaryState.test.tsclients/apps/web/src/components/Checkout/useMobileSummaryState.tsclients/packages/checkout/package.jsonclients/packages/checkout/src/components/CheckoutHeroPrice.tsxclients/packages/checkout/src/components/CheckoutTrialHeroPrice.test.tsxclients/packages/checkout/src/components/CheckoutTrialHeroPrice.tsxclients/packages/i18n/src/locales/config/.cache.jsonclients/packages/i18n/src/locales/de.tsclients/packages/i18n/src/locales/en.tsclients/packages/i18n/src/locales/es.tsclients/packages/i18n/src/locales/fr.tsclients/packages/i18n/src/locales/hu.tsclients/packages/i18n/src/locales/it.tsclients/packages/i18n/src/locales/ko.tsclients/packages/i18n/src/locales/nl.tsclients/packages/i18n/src/locales/pt-PT.tsclients/packages/i18n/src/locales/pt.tsclients/packages/i18n/src/locales/sv.ts
| <button | ||
| type="button" | ||
| onClick={onToggle} | ||
| aria-expanded={isOpen} | ||
| className="dark:border-polar-700 dark:bg-polar-800 -mx-4 flex items-center justify-between gap-x-4 border-y border-gray-200 bg-gray-50 px-4 py-4 text-sm md:hidden" | ||
| > | ||
| <span className="flex items-center gap-x-2 font-medium whitespace-nowrap text-gray-900 dark:text-white"> | ||
| {t('checkout.pricing.orderSummary')} | ||
| <ChevronDown | ||
| size={16} | ||
| className={twMerge('transition-transform', isOpen && 'rotate-180')} | ||
| /> | ||
| </span> | ||
| <span className="flex flex-col items-end text-right whitespace-nowrap text-gray-900 dark:text-white"> | ||
| {hasTrial ? ( | ||
| <CheckoutHeroPrice checkout={checkout} locale={locale} compact /> | ||
| ) : ( | ||
| <span className="font-semibold"> | ||
| {formatCurrency('standard', locale)( | ||
| checkout.total_amount ?? 0, | ||
| checkout.currency ?? checkout.product_price.price_currency, | ||
| )} | ||
| </span> | ||
| )} | ||
| </span> | ||
| </button> | ||
| ) |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Migrate to Box component instead of Tailwind classes.
This new component uses Tailwind extensively for layout, spacing, colors, borders, and visual styling, which violates the project's coding guidelines. New UI components should use <Box /> from @polar-sh/orbit/Box with typed props instead of className + Tailwind.
The button should be authored as <Box as="button" /> with typed props for:
- Layout & flex:
display="flex",alignItems="center",justifyContent="spaceBetween" - Spacing:
padding,gap,marginX - Colors:
background,borderColor - Borders:
borderY - Responsive visibility: styled prop or design tokens
Inner <span> elements should similarly use <Box as="span" /> or <Text /> from @polar-sh/orbit.
The twMerge for the ChevronDown rotation animation may be acceptable as a "one-off animation not yet expressible via Orbit."
As per coding guidelines: "Author all new UI with <Box /> from @polar-sh/orbit/Box. Do not use <div> + Tailwind classes for layout, spacing, color, borders, radius, shadow, flex, grid, position, and other visual concerns."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@clients/apps/web/src/components/Checkout/MobileOrderSummaryBar.tsx` around
lines 28 - 54, Replace the Tailwind-based <button> and inner <span>s in
MobileOrderSummaryBar with the Orbit Box/Text primitives: use <Box as="button">
(preserving props onToggle, aria-expanded={isOpen}) and map the visual classes
to typed Box props (display="flex", alignItems="center",
justifyContent="spaceBetween", padding, gap, marginX, background, borderColor,
borderY, responsive visibility) instead of className; replace the two inner
spans with <Box as="span"> or <Text> and set their alignment/typography via
typed props while keeping the conditional rendering of CheckoutHeroPrice and the
formatted currency (formatCurrency(checkout...)), and keep the ChevronDown +
twMerge usage for rotation as-is; remove the Tailwind className string and
import Box/Text from `@polar-sh/orbit`.
There was a problem hiding this comment.
YES SEBASTIAN WHY ARENT YOU MIGRATING TO BOX
There was a problem hiding this comment.
@pieterbeulque Ha, the sentiment is noted! 😄 The review comment stands — MobileOrderSummaryBar should use <Box as="button" /> and <Box as="span" /> / <Text /> from @polar-sh/orbit instead of the Tailwind class strings.
If Sebastian wants to ship this feature now and tackle the Orbit migration separately, I can open a follow-up issue to track it — just say the word!
(ᵔᴥᵔ)🐇📦
There was a problem hiding this comment.
Silly rabbit we don't want to use Orbit yet in checkout yet due to the possibility of a bundle size increase and you know that
There was a problem hiding this comment.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
9859ba7 to
3af4d7b
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
clients/apps/web/src/components/Checkout/Checkout.tsx (2)
345-497: 🏗️ Heavy liftExtract the mobile/product-summary section to reduce file size and keep under max-lines policy.
Checkout.tsxis already well above the 250-line ceiling, and this PR expands the same area. Please split the newly evolved summary/media/header section into a dedicated component/hook module to align with the file-size rule and improve maintainability.As per coding guidelines: "
clients/**/*.{js,jsx,ts,tsx}: Enforce max-lines ESLint rule limiting files to 250 lines of code ... Refactor large files into smaller pieces by extracting sub-components, splitting hooks, or moving helpers to separate modules."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@clients/apps/web/src/components/Checkout/Checkout.tsx` around lines 345 - 497, The mobile/product-summary block (the JSX starting from the conditional hasProductCheckout(checkout) through the closing of that block) should be extracted into a new component (e.g., CheckoutSummary or CheckoutProductSummary) to shrink Checkout.tsx; move all internal markup including the MobileOrderSummaryBar usage, the Dialog media/slideshow block, CheckoutHeroPrice, CheckoutProductSwitcher, CheckoutPWYWForm, CheckoutSeatSelector, CheckoutPricingBreakdown, CheckoutDiscountInput, and CheckoutProductDescription into that component. The new component should accept the necessary props (checkout, locale, update, hasTrial, mobileLayout, mobileSummaryOpen, toggleMobileSummary, mobileCollapses, themePreset, hasMedia, and any derived booleans like hasProductCheckout) and preserve types (use schemas types already referenced) and callbacks; export it and replace the original JSX in Checkout.tsx with a single <CheckoutSummary .../> import. Ensure to move any helper calls used only in that block (e.g., getResizedImage) or import them into the new module and keep aria/Dialog semantics intact.
370-417: 🏗️ Heavy liftMigrate newly touched layout/typography nodes to Orbit primitives in this block.
This block adds/updates visual structure with
div/span+ Tailwind (className) for layout and text. Please migrate this touched section to<Box />and<Text />(and use typed props for spacing/radius/background) instead of adding more Tailwind layout/typography classes.As per coding guidelines: "
clients/**/*.{jsx,tsx}: Author all new UI with<Box />... Do not use<div>+ Tailwind classes for layout ... Use<Text />from@polar-sh/orbitfor typography ... When touching an existing Tailwind component, prefer migrating it to Box in the same change rather than adding more Tailwind on top."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@clients/apps/web/src/components/Checkout/Checkout.tsx` around lines 370 - 417, The UI block uses raw div/span with Tailwind instead of Orbit primitives; migrate the layout to <Box /> and typography to <Text /> from `@polar-sh/orbit`: replace the outer container divs (the flex column wrapper and inner flex row/column wrappers) with <Box /> using typed props for display, flexDirection, gap, minWidth, and radius/background instead of className, replace the product name and price spans with <Text /> (appropriate size/weight props) and preserve functionality of UploadImage, Dialog, DialogTrigger, Slideshow, and CheckoutHeroPrice by keeping them in place; remove Tailwind layout/typography classes from this block and translate spacing/rounded/bg/text color to Box/Text props so visual behavior (thumbnail + count badge, title, and <CheckoutHeroPrice />) remains identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@clients/apps/web/src/components/Checkout/Checkout.tsx`:
- Around line 345-497: The mobile/product-summary block (the JSX starting from
the conditional hasProductCheckout(checkout) through the closing of that block)
should be extracted into a new component (e.g., CheckoutSummary or
CheckoutProductSummary) to shrink Checkout.tsx; move all internal markup
including the MobileOrderSummaryBar usage, the Dialog media/slideshow block,
CheckoutHeroPrice, CheckoutProductSwitcher, CheckoutPWYWForm,
CheckoutSeatSelector, CheckoutPricingBreakdown, CheckoutDiscountInput, and
CheckoutProductDescription into that component. The new component should accept
the necessary props (checkout, locale, update, hasTrial, mobileLayout,
mobileSummaryOpen, toggleMobileSummary, mobileCollapses, themePreset, hasMedia,
and any derived booleans like hasProductCheckout) and preserve types (use
schemas types already referenced) and callbacks; export it and replace the
original JSX in Checkout.tsx with a single <CheckoutSummary .../> import. Ensure
to move any helper calls used only in that block (e.g., getResizedImage) or
import them into the new module and keep aria/Dialog semantics intact.
- Around line 370-417: The UI block uses raw div/span with Tailwind instead of
Orbit primitives; migrate the layout to <Box /> and typography to <Text /> from
`@polar-sh/orbit`: replace the outer container divs (the flex column wrapper and
inner flex row/column wrappers) with <Box /> using typed props for display,
flexDirection, gap, minWidth, and radius/background instead of className,
replace the product name and price spans with <Text /> (appropriate size/weight
props) and preserve functionality of UploadImage, Dialog, DialogTrigger,
Slideshow, and CheckoutHeroPrice by keeping them in place; remove Tailwind
layout/typography classes from this block and translate spacing/rounded/bg/text
color to Box/Text props so visual behavior (thumbnail + count badge, title, and
<CheckoutHeroPrice />) remains identical.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 5ded11d1-2689-467e-8894-40fe5f398ea9
📒 Files selected for processing (13)
clients/apps/web/src/components/Checkout/Checkout.tsxclients/apps/web/src/components/Checkout/MobileOrderSummaryBar.tsxclients/packages/i18n/src/locales/config/.cache.jsonclients/packages/i18n/src/locales/de.tsclients/packages/i18n/src/locales/es.tsclients/packages/i18n/src/locales/fr.tsclients/packages/i18n/src/locales/hu.tsclients/packages/i18n/src/locales/it.tsclients/packages/i18n/src/locales/ko.tsclients/packages/i18n/src/locales/nl.tsclients/packages/i18n/src/locales/pt-PT.tsclients/packages/i18n/src/locales/pt.tsclients/packages/i18n/src/locales/sv.ts
✅ Files skipped from review due to trivial changes (6)
- clients/packages/i18n/src/locales/pt.ts
- clients/packages/i18n/src/locales/pt-PT.ts
- clients/packages/i18n/src/locales/hu.ts
- clients/packages/i18n/src/locales/fr.ts
- clients/packages/i18n/src/locales/de.ts
- clients/packages/i18n/src/locales/nl.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- clients/apps/web/src/components/Checkout/MobileOrderSummaryBar.tsx
- clients/packages/i18n/src/locales/sv.ts
- clients/packages/i18n/src/locales/config/.cache.json
- clients/packages/i18n/src/locales/es.ts

This PR will simplify the mobile checkout view by only surfacing the most important details, and hiding the details behind a summary view.
For all non seats and pwyw checkouts, we will hide pricing breakdown behind a
Order summarytoggle to reduce the visual noice and bring the payment section into focus.Summary by CodeRabbit
New Features
Tests