Skip to content

feat: migrate composite components to TypeScript#28

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1776395723-migrate-composite-components-ts
Open

feat: migrate composite components to TypeScript#28
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1776395723-migrate-composite-components-ts

Conversation

@devin-ai-integration
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot commented Apr 17, 2026

Summary

Full TypeScript migration of the project's component, context, and lib layers. Since no TypeScript foundation existed on main, this PR includes the complete setup:

  • TypeScript foundation: tsconfig.json, next-env.d.ts, types/index.ts with shared interfaces (SanityProduct, CartItem, SanityBanner)
  • Dependency changes: Added typescript, @types/react@17, @types/react-dom@17, @types/node, @types/canvas-confetti. Removed @babel/core and @babel/preset-react (Next.js 12 uses SWC by default). Deleted .babelrc.
  • lib/ layer: client.ts (typed urlFor with SanityImageSource), getStripe.ts (typed Stripe | null promise), utils.ts (typed params)
  • context/ layer: StateContext.tsx with full StateContextType interface and typed useStateContext hook (throws if used outside provider)
  • All 7 components migrated from .jsx.tsx with typed props, React.FC, and proper generics
  • Barrel file: components/index.jscomponents/index.ts

Note: pages/ directory files remain as .js (out of scope); allowJs: true is set in tsconfig to support this.

Behavioral changes beyond renaming (review carefully)

File Change Why
Cart.tsx response.statusCoderesponse.status Bug fix: Fetch API uses .status, not .statusCode
Cart.tsx stripe.redirectToCheckoutstripe?.redirectToCheckout getStripe() can return null; avoids potential runtime throw
Cart.tsx Removed onClick="" on quantity <span> Invalid handler removed
Multiple components urlFor(...)urlFor(...).url() Explicitly converts ImageUrlBuilder to string URL
StateContext.tsx .map() callback now returns cartProduct for non-matching items Fixes bug where non-matching items returned undefined
StateContext.tsx product.quantity = quantity → spread { ...product, quantity } Avoids mutating the input parameter

Review & Testing Checklist for Human

  • Verify urlFor(...).url() produces correct image URLs — previously the ImageUrlBuilder object was passed directly as src (likely relying on implicit .toString()). Confirm images still render correctly on the home page, product pages, and cart.
  • Check StateContext.tsx behavioral fixes — the .map() fix (returning cartProduct for non-matches) and the mutation removal (product.quantity = quantity → spread) change runtime behavior. Verify add-to-cart works correctly, especially adding a product that's already in the cart.
  • Verify Stripe checkout flow — the response.statusCode → response.status and stripe?.redirectToCheckout changes touch the payment path. Test a checkout to confirm it still redirects properly.
  • Confirm non-null assertions (!) on foundProduct in toggleCartItemQuanitity and onRemove — these assume the product is always found in the cart. If it's possible for it to be missing, these will throw at runtime.

Suggested test plan: Run npm run dev, browse products, add items to cart, adjust quantities, remove items, and attempt checkout. Confirm all images load and the cart state behaves correctly.

Notes

  • npx tsc --noEmit passes with zero errors
  • npm run build succeeds (warnings are pre-existing, unrelated to this PR)
  • No .jsx or .js files remain in components/
  • TypeScript 6.0.3 is installed; ignoreDeprecations: "6.0" is set in tsconfig to silence the moduleResolution: "node" deprecation warning

Link to Devin session: https://app.devin.ai/sessions/ccd8d5b815264c1e80ea23b4c327b300
Requested by: @WesternConcrete


Open with Devin

- Set up TypeScript foundation: tsconfig.json, types/index.ts, next-env.d.ts
- Remove .babelrc and babel dependencies (replaced by Next.js SWC compiler)
- Install typescript, @types/react, @types/react-dom, @types/node, @types/canvas-confetti
- Migrate lib/ layer: client.ts, getStripe.ts, utils.ts with proper type annotations
- Migrate context/ layer: StateContext.tsx with full StateContextType interface
- Migrate leaf components: Footer.tsx, Product.tsx, HeroBanner.tsx, FooterBanner.tsx with typed props
- Migrate composite components: Cart.tsx (typed useRef<HTMLDivElement>, async handleCheckout), Navbar.tsx, Layout.tsx (typed children: React.ReactNode)
- Migrate components/index.js to components/index.ts barrel re-export

Verification:
- npx tsc --noEmit passes with zero errors
- npm run build succeeds
- No .jsx or .js files remain in components/

Co-Authored-By: Wes Convery <2wconvery@gmail.com>
@devin-ai-integration
Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 5 additional findings.

Open in Devin Review

@devin-ai-integration
Copy link
Copy Markdown
Author

E2E Test Results

Ran the dev server locally and tested all critical user flows in the browser to verify the TypeScript migration didn't break runtime behavior.

All 9 assertions passed.

Core Functionality Tests
Test Result
Home page renders with all images (hero banner, 5 product cards, footer banner from cdn.sanity.io) PASSED
Navbar and footer render correctly (logo, cart icon qty 0, copyright) PASSED
Product detail page loads with image, name, price, details, qty selector, buttons PASSED
Add to cart shows toast ("1 Speaker added to the cart.") and updates badge to 1 PASSED
Cart sidebar shows product image, name ($56), qty (1), subtotal ($56), Pay with Stripe PASSED
Cart quantity +: qty 1→2, subtotal $56→$112 PASSED
Cart quantity -: qty 2→1, subtotal $112→$56 PASSED
Duplicate product merges into single entry with qty=2 (StateContext .map() fix verified) PASSED
Item removal shows "Your shopping bag is empty", 0 items, Continue Shopping button PASSED
Key Screenshots

Home Page

Home page

Product Detail Page

Product detail

Add to Cart Toast

Add to cart

Cart Sidebar

Cart sidebar

Quantity Increment

Qty increment

Empty Cart After Removal

Empty cart

Devin session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant