SportJacks Storefront
Completed
Product · Web

SportJacks Storefront

Frontend contributor — pincode handling & location modal · 2024

Sports e-commerce front end on the Next.js App Router, calling ten separate services through a gateway: Redux Toolkit holding cart items and totals for render, NextAuth as an identity broker over credentials and Google plus a phone-and-OTP path handled by a utility service, a server-side wishlist, Cashfree checkout, zod-validated forms and pincode-based delivery lookup.

Built with
Next.jsNext.js
ReactReact
MUIMUI
Redux ToolkitRedux Toolkit
NextAuthNextAuth
CashfreeCashfree
zodzod
SassSass
Project Details

STATUS
Completed
ROLE

Frontend contributor — pincode handling & location modal

YEAR

2024

TYPE

Product · Web

TAGS
E-commerce
Microservices

This storefront owns no data. Everything behind it is a separate service — products, orders, master data, identity, utilities, sellers, website users — reached through one gateway host, and the front end's job is to stitch seven of them into something that reads as one shop. The interesting work is in the seams: where the session lives, which service answers a screen, and what happens when a payment modal closes.

That is a different problem from a monolith-backed store. In a monolith, a product page is one call. Here it is a product from the product service, a brand from the master service, a wishlist flag from a third endpoint and a delivery estimate from the seller service, and any one of them can be slow, down, or answer in a shape nobody documented. The gateway is a path-prefix proxy across ten Node services, so the front end's service map is literally a copy of the gateway's routing table with a base URL in front of it. Getting that map wrong does not fail loudly; it 404s one widget on one screen.

What the front end handles

  • Seven microservice base URLs in one endpoint map, with every call routed through a single fetch wrapper that attaches auth and centralises error toasts.
  • NextAuth purely as an identity broker. The real session is the backend's own token pair. Credentials login returns those directly; Google login does not, so the JWT callback re-authenticates server side to trade the Google identity for backend tokens.
  • Tokens as AES envelopes, not opaque strings — an IV plus ciphertext, held as four separate cookies and sent as two headers.
  • A phone-and-OTP path built on a service-mesh callback: the frontend tells the utility service which downstream service and endpoint to hit once verification succeeds, rather than chaining two round trips itself.
  • Cart state split deliberately. Redux holds items and totals for render; persistence is hand-rolled localStorage keyed on product and variant id, with quantity merged additively for user adds and assigned outright when hydrating from the API.
  • Wishlist entirely server side, on an endpoint whose type discriminator also backs save-for-later.
  • Device-forked product pages. /product/[id] splits into DesktopLayout and MobileLayout directories rather than branching on CSS, keeping desktop gallery code out of the mobile bundle.
  • A hand-built query builder that repeats a key per selected filter value and only appends price, rating, page and sort when they differ from their sentinels.
  • Order lifecycle screens across my-orders, tracking, cancel, return with reason codes, replace.
  • Server actions as the data layer. Around forty files under ServerActions/, grouped by screen rather than by service, each one a thin typed call through the shared fetch wrapper.

Which service answers which screen

Worth enumerating, because the mapping is the architecture. The RBAC service owns /auth/authenticate and nothing else the storefront touches. The utility service owns /otp/send-otp and /otp/verify-otp. The product service is the biggest surface: public products, groups and collections, coupon validation and the wishlist. The master service serves brands, sports and brand advertisements, all under a public prefix that needs no token. The order service carries the entire transactional half — cart add, add-many, count, remove, clear, customer addresses, place, track, cancel, return, return reasons — and the seller service answers exactly one question, what shipping costs for a given cart to a given pincode. The website-user service lists coaches and academies, which is the part of the storefront that is not a shop at all.

The query builder deserves a note because its sentinels are hardcoded. Categories, sports and brands repeat their key once per selected value; minimum price is omitted at 0, maximum price at 99999999999, rating at 0 and page at 1. That is a deliberate choice to keep shareable URLs short at the cost of encoding the backend's defaults in the client, and it means changing a default on the server silently changes what a saved filter URL means.

Payment and pincode

Checkout uses the Cashfree SDK with redirectTarget: "_modal". That choice is load-bearing: an in-page overlay means React state survives the payment, so the flow is a straight line — create the order, get a session id, open the modal, verify, clear the cart, route to orders. A full redirect away and back needs a whole recovery page to reconstruct where the user was.

The verify step is the fragile link. The modal resolves with a payment details object, and the code gates on its paymentMessage string being exactly "Payment finished. Check status." before it calls verify-payment with the order id. Only if that returns 200 does it clear localStorage, dispatch clearCart and push to /myorders. Gating on a human-readable message from a third-party SDK works right up until the SDK rewords it.

Pincode appears twice and means two different things. In the app bar it is the delivery location, validated as six digits, persisted to localStorage, and settable from the browser geolocation API through a reverse-geocode endpoint. At checkout it is a shipping input: cart id and destination pincode are quoted against the seller service, which implies a multi-seller split-shipment model rather than one warehouse. The app-bar pincode handling and the location modal refactor were mine.

How it deploys

next start runs on port 3002 so it can sit next to the other services locally. Environment comes from a dotenv file in development and, going by the TEST file in the repo root, from AWS Parameter Store paths in the deployed environment, one path per variable rather than one blob. Images are unrestricted in next.config.mjs, with remotePatterns set to any HTTPS host, which is convenient while product imagery is still moving between buckets and is the first thing to tighten afterwards. Middleware runs on every non-static route but currently enforces one rule, bouncing an authenticated user away from /login, with an explicit TODO where authorization should go.

Honest state of the branch

This is the branch I worked on and it reads like one. The order service base URL is hardcoded to a localhost port while that service was being built, with the environment variable sitting unused next to it, and the two checkout calls repeat that literal inline with the env-based version commented out above them. The single zod schema covers email and password only, so the OTP branch of the same authorize function cannot clear validation without a discriminated union on login type.

There are smaller rough edges I would fix before calling any of it finished. The cookie helpers are asymmetric: tokens are written as four cookies with _iv and _data suffixes, but the logout and read helpers look for plain accessToken and refreshToken, names nothing ever writes, so signing out does not clear anything. In practice the fetch wrapper reads the token out of the Redux store rather than the cookies at all, which makes the cookie pair write-only on this branch. The cart reducer takes a discount in its payload and then hardcodes it to zero. The address input map types the pincode field as email and the flat field as tel. The reverse-geocode handler sets state from the resolved pincode but writes the stale typed value to localStorage. And TRACK_ORDER is declared twice in the same endpoint object. It was a team repo with per-developer branches feeding a shared development branch, and my work landed as PRs from mine.

Project Details

STATUS
Completed
ROLE

Frontend contributor — pincode handling & location modal

YEAR

2024

TYPE

Product · Web

TAGS
E-commerce
Microservices