Craft

A furniture web shop where one sofa can have four colours, each with its own photos, and every order has a story.

Craft Cover
Year
2026
Role
Solo project: design, frontend, backend and database
Platforms
Web
Next.jsTypeScriptPrismaPostgreSQLNextAuthZustandHeroUI

Overview

Craft sells furniture, which turned out to be a great excuse for a serious data model. One sofa comes in several colours, every colour has its own photos, some products are on sale, and people shop by room instead of by product type. Everything lives in PostgreSQL, modelled with Prisma, and the interface is built around it.

Shop by room, not by aisle

A balcony needs different things than a bedroom. Every product is tagged with the rooms it fits, so visitors can jump straight to what they are furnishing and then narrow it down further.

  • 11 room types, from living room to gaming room
  • Colour variants, each with its own ordered photo gallery
  • Sale price shown next to the original
  • Search plus filters on top of all that
Craft shop screen
Why furniture

Selling a T-shirt is easy. Selling a sofa with colours, materials, dimensions and a sale price makes the database earn its keep.

The data model

One product, many variants

A colour is a Variant and it owns its images in a fixed order. Delete a product and its variants and photos are cleaned up with it.

Prices are frozen

Each order line stores the price at the moment of purchase, so a later sale never rewrites what a customer actually paid.

One cart per account

A unique cart per user and a unique row per product and colour, so adding the same item twice raises the quantity instead of making a duplicate line.

Favourites without duplicates

The pair of user and product is unique at the database level, so no amount of double clicking can save the same item twice.

Every order tells you where it is

After checkout an order gets a timeline: pending, confirmed, shipped, delivered. When something goes wrong it ends as cancelled, and the reason is written down instead of lost in an email.

  • Delivery details and the full item list for each order
  • Cancellation reason stored with the order
  • Notifications waiting in the account
  • Favourites and order history in the same place
Craft order tracking

Behind the counter

  • Three roles: customers shop, moderators help, administrators run the place
  • Create and edit products, categories, colour variants and photos
  • Move orders through their statuses, or cancel them with a reason
  • A contact inbox where every message is marked answered or unanswered
  • Every product counts its own visitors

How I built it

PostgreSQL + Prisma

12 models and 4 enums with relations and cascades that keep carts, orders and favourites consistent. Money is stored as exact decimals, never floats.

Next.js + TypeScript

Interface and backend in one codebase, with the Prisma types flowing all the way to the components.

NextAuth

Sign-in and sessions, with roles deciding who sees the account area and who sees the admin.

Zustand

Small, predictable client state, so the cart reacts instantly.

HeroUI

Accessible components for the boring parts, so the time went into the shop itself.

Screens

Craft screenshot
Shop
Craft screenshot
Shopping cart
Craft screenshot
Account Page
Craft screenshot
Order tracking

Challenges

Problem

A price changes or a product goes on sale after someone has already ordered it.

Solution

Every order item keeps its own copy of the price, so old orders always show exactly what was paid.

Problem

Prices in floating point drift by cents, and cents are exactly what a shop cannot lose.

Solution

All money is an exact decimal column in PostgreSQL, from product price to order total.

Problem

The cart has to feel instant, but it must still be there after a refresh.

Solution

Zustand updates the interface immediately while the cart itself belongs to the user's account in the database.

What I learned

01

Draw the schema first

Once the relations were right, carts, orders and favourites were mostly plain queries.

02

Copy, don't point

Anything that must stay true later, like a price paid, should be copied at the time, not looked up again.

03

Status is a feature

A visible order timeline builds more trust than any banner on the homepage.