Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,111 @@
This project implements all three parts of the technical challenge using **Next.js**, **Prisma**, **Tailwind CSS**, **shadcn/ui**, and React best practices.

## 🚧 Setting things up

To make sure project runs as expected a few steps first:

- Clone the repository.

```bash
git clone https://github.com/sesto-dev/next-prisma-tailwind-ecommerce
```

- Checkout to Assessment branch.

```bash
git checkout arthur-test
```

- Create your own .env files in both admin and storefront projects. (Some variables won't be used here)

- The next few commands should be used in both admin and storefront projects. Install dependencies.

```bash
bun install
```

- Setup a project in Supabase (recommended) or PostgreSQL database of your choice

- Create an account or login with your account in Supabase
- Create a new project, saving project's password (you'll need that in next step)
- In order to make Prisma terminal commands work, first I followed steps 1 and 3 in this [connecting Prisma to Supabase tutorial](https://supabase.com/docs/guides/database/prisma).

- After setting up your DATABASE_URL in .env file run this command to create tables in database.

```bash
bun run db:push
```

- Then run this to pupulate tables.

```bash
bun run db:seed
```

- Now you should be able to run each project with.

```bash
bun run dev
```

Note: I took some time trying to make SMTP and JWT variables to work, but then realized they weren't necessary to this accessment. As for part 2 in admin report page, tested whole page without permission control, then added it to `middleware.ts` matcher list.

---

## 1️⃣ Rebuild product filters on the storefront page

- Fixed **Brand** and **Categories** filters as they were using conflicting versions of `cmdk` and shadcn's `CommandItem` style.
- Made so **Categories** filter can handle more than one item selected and added new A-Z and Z-A options to **Order** filter.
- Created new **TextSearch** and **PriceRange** filters. Those filters use a debounce feature so user won't have to type enter to filter page with them.
- Updated the Prisma query to handle:
- Filtering products by selected categories (`category.title IN [...]`).
- Filtering products by title, price range and two new orderBy options (A-Z and Z-A).
- Used `next/navigation` hooks (`useSearchParams`, `useRouter`, `usePathname`) to synchronize filters with the URL query string.
- Ensured the page updates dynamically as filters change, without full reload.
- Note: Removed "Featured" option in SortBy because it wasn't asked for in assessment notes, but kept `AvailableToggle` just because I thought it was a nice filter.

---

## 2️⃣ Build an admin reports page with charts or tables

- Created a new `ReportsPage` under the admin app.
- Ensured that page is only accessible to administrators.
- Displayed two main data tables:
- **Last Orders**: showing recent orders with date, total, and customer email.
- **Top-Selling Products**: ranked list of products sorted by total quantity sold.
- Pulled data from Prisma with:
- Relations: included `orderItems.product` and `user`.
- Ordering: sorted orders by `createdAt DESC`.
- Formatted data client-side for easy table consumption.
- Added **Brand** and **Categories** filters, the same ones used in storefront products page.
- Created new **DateRange** filter based on shadcn docs. Had to install shadcn's `Calendar` for that.
- Ensured the page updates dynamically as filters change, without full reload.
- Applied a small fix with `date-fns` to ensure the end date (`to`) includes the full day (i.e., `endOfDay(to)`).

---

## 3️⃣ Extend the Product model for cross-sell recommendations

- Updated the Prisma schema to add a **crossSellProducts** relation on the `Product` model.
- Added crossSell in `seed.ts` so after running migration and database commands products are randomly given crossSell relations. Tested field in Supabase pannel.
- Added a **You Might Also Like** section:
- On the **Product Details** page: showing related products to encourage cross-selling.
- On the **Cart** page: displaying recommended products alongside the shopping cart. Here ensured that this section showed crossSell products related to all items in cart while also preventing duplicated suggestions and items already in cart to appear there.
- Ensured crossSell suggestions are clickable and take the user to product page while suggestions list is aligned with project design and responsiveness.
- Implemented better UX feedback when adding products to the cart:
- Integrated a `toast` notification using `shadcn/ui`. Chose that one because it was already installed in project and I'm used to work with it.
- Gave users immediate visual confirmation of their action when a product is added or removed from cart. Toast will also appear if there's a `try/catch` error when adding/removing items from cart.

---

## 🚀 Final Touches

- Used TypeScript types, especially on date-related states (`DateRange`).
- Ensured no runtime errors when filters are partially filled (e.g., only `from` date selected).
- Maintained consistent design and UX patterns across storefront and admin pages.

### Original README from here

![Screenshot](https://github.com/sesto-dev/next-prisma-tailwind-ecommerce/assets/45223699/00444538-a496-4f90-814f-7e57a580ad17)

<div align="center"><h3>Full-Stack E-Commerce Platform</h3><p>Built using Typescript with Next.js, Prisma ORM and TailwindCSS.</p></div>
Expand Down
Binary file modified apps/admin/bun.lockb
100644 → 100755
Binary file not shown.
15 changes: 10 additions & 5 deletions apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
"name": "next-prisma-tailwind-ecommerce-cms",
"version": "0.1.0",
"private": true,
"prisma": {
"seed": "node --loader ts-node/esm prisma/seed.ts"
},
"scripts": {
"dev": "next dev -p 8888",
"dev": "next dev -p 8888 -H 0.0.0.0",
"build": "next build",
"start": "next start",
"lint": "next lint",
Expand Down Expand Up @@ -35,14 +38,14 @@
"@radix-ui/react-popover": "^1.1.2",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-toast": "^1.2.2",
"@tanstack/react-table": "^8.20.5",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
"cmdk": "^1.1.1",
"date-fns": "^4.1.0",
"jose": "^5.9.4",
"lucide-react": "^0.452.0",
Expand All @@ -51,6 +54,7 @@
"next-themes": "^0.3.0",
"nodemailer": "^6.9.15",
"react": "18.3.1",
"react-day-picker": "8.10.1",
"react-dom": "18.3.1",
"react-hook-form": "^7.53.0",
"react-hot-toast": "^2.4.1",
Expand All @@ -60,7 +64,7 @@
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/node": "22.7.5",
"@types/node": "^22.15.21",
"@types/react": "18.3.11",
"@types/react-dom": "18.3.1",
"autoprefixer": "10.4.20",
Expand All @@ -73,6 +77,7 @@
"tailwind-merge": "^2.5.3",
"tailwindcss": "3.4.13",
"tailwindcss-animate": "^1.0.7",
"typescript": "5.6.3"
"ts-node": "^10.9.2",
"typescript": "^5.8.3"
}
}
Loading