Learn something new with GraphQL every week

Frontend

#19 Cart summary page

Julian Mayorga
Instructor
Julian
Jamie Barton
Instructor
Jamie
githubSource code

Let's improve the cart summary page and display a list of cart items along with the cart subtotal, with pictures and prices.

This will also be a good opportunity to create a cart detail component to handle these features.

Refactor code from cart page into cart detail component

Modify the cart page by replacing the elements that show the subtotal with a CartDetail component, which you will create afterwards.

Import the CartDetail component and render it in cart.tsx. It will receive a cart property.

// ...
import { CartDetail } from "../components/CartDetail";

const Cart: NextPage<IProps> = ({ cartId }) => {
  const { data } = useGetCartQuery({ variables: { id: cartId } });
  return (
    <div className="min-h-screen flex flex-col">
      <Header />
      <main className="p-8 min-h-screen">
        <div className="mx-auto max-w-xl space-y-8">
          <h1 className="text-4xl">Cart</h1>
          <CartDetail cart={data?.cart} />
        </div>
      </main>
    </div>
  );
};

// ...

Create a file called CartDetail.tsx inside the components folder. This file exports a function called CartDetail, which receives a cart prop with a type CartFragment from the autogenerated types file.

This component displays a CartItem component for every item from the cart property. It also displays cart Subtotal and Total, with a bit of space and borders between them.

import { CartFragment } from "../types";
import { CartItem } from "./CartDetail";

export function CartDetail({
  cart,
}: {
  cart: CartFragment | null | undefined;
}) {
  return (
    <div>
      <div className={`space-y-8 relative`}>
        {cart?.items.map((item) => (
          <CartItem key={item.id} item={item} />
        ))}
      </div>
      <div className="border-t my-4 border-neutral-700 pt-4">
        <div className="flex justify-between">
          <div>Subtotal</div>
          <div>{cart?.subTotal.formatted}</div>
        </div>
      </div>
      <div className="border-t border-neutral-700 pt-4">
        <div className="flex justify-between font-bold">
          <div>Total</div>
          <div className="">{cart?.subTotal.formatted}</div>
        </div>
      </div>
    </div>
  );
}

Displaying cart items

The final step to make this cart summary page work is creating the CartItem.tsx component. It will take an item prop and use it's data to display an image, name, subtotal and quantity.

import Image from "next/image";
import { CartItem } from "../types";

export function CartItem({ item }: { item: CartItem }) {
  return (
    <div className="space-y-2">
      <div className="flex gap-4">
        <Image
          src={item.image || ""}
          width={75}
          height={75}
          alt={item.name}
          objectFit="cover"
        />
        <div className="flex justify-between items-baseline flex-1 gap-2">
          <span className="text-lg">{item.name}</span>
          <span className="text-sm font-light">{item.unitTotal.formatted}</span>
        </div>
      </div>
      <div className="flex gap-2">
        <div className="flex-1 flex">
          <div className="px-2 py-1 font-light border border-neutral-700 flex-1">
            {item.quantity}
          </div>
        </div>
      </div>
    </div>
  );
}

To try this out go to http://localhost:3000 and see what the cart item list looks like.