@@ -100,3 +100,110 @@ Follow the deployment guides for [Vercel](https://create.t3.gg/en/deployment/ver
This project is MIT-licensed and is free to use and modify for your own projects. Check the [LICENSE](./LICENSE) file for details.
Created by [Amirhossein Mohammadi](https://github.com/sesto-dev).
+
+
+
+
+## 📘 Developer Assessment Additions (Harold)
+
+### Introduction
+This project was developed as part of my Developer Assessment Exam. The goal of the assessment is to extend the existing next-prisma-tailwind-ecommerce application by implementing new features that enhance both the storefront and the admin panel.
+
+The assessment focuses on three major enhancements:
+
+### 1. Rebuilt Product Page Filter
+
+#### Objective
+Allow users to filter products by text, price, category, brand, and order.
+
+#### Implementation Details
+1) The filtering interface was redesigned to improve usability. Instead of placing all filters directly on the page—which can appear cluttered—secondary filters such as categories, brand, and price range were moved into a popover component. Users typically arrive intending to browse products immediately, so the search bar remains the primary, always-visible control. To address the drawback of hidden filters, a number badge was added to the filter button showing how many filters are currently active.
+
+2) New filtering inputs were implemented for Sort By, Category, Price Range, and Brand. Dispatch was used to batch state updates to prevent excessive re-fetches, particularly during text search.
+
+3) Pagination was added to avoid loading all products at once, improving performance and reducing layout shifts.
+
+#### Testing
+- Filtering across multiple categories, search queries, brand selection, and minimum/maximum price was validated.
+- Responsiveness was checked to ensure proper layout behavior across different screen sizes.
+- The page was tested on multiple devices to confirm consistent performance and functionality.
+- Product listings now update dynamically based on user selected filters without requiring a full page refresh.
+
+### 2. Admin Reporting Page
+
+#### Objective
+The reporting page provides administrators with tools to view and analyze order data, including:
+- Order summaries grouped by date to track sales over time.
+- Top‑selling products based on overall sales volume.
+- Filter options for date range, product categories, and brand.
+
+#### Implementation Details
+1. Added filter inputs for Category, Brand, Date From, and Date To, including validation that prevents Date From being later than Date To, and vice versa.
+2. Implemented a Prisma query that fetches orders based on active filters and groups them by date.
+3. Added pagination to improve performance and ensure efficient retrieval of large datasets.
+4. Developed a flexible top‑selling product function. The current configuration displays the top 5 products, but the limit can be easily changed.
+5. Added a summary card showing total revenue and total order count, dynamically updated based on filters.
+6. Designed the UI in Figma and implemented it in the Admin panel, including a copy‑to‑clipboard feature for quick access to order IDs.
+
+#### Testing
+1. **Filter Functionality**
+ - Verified that filtering by Category limits results to products within the selected category.
+ - Confirmed that Brand filtering shows only orders containing matching products.
+ - Validated date range logic so Date From cannot exceed Date To.
+ - Confirmed combined filters (e.g., Category + Date Range) produce accurate results.
+
+2. **Order Grouping**
+ - Ensured orders are grouped correctly by date.
+ - Cross‑checked totals against sample seed data.
+
+3. **Pagination**
+ - Verified smooth navigation between pages.
+
+4. **Top‑Selling Products**
+ - Confirmed the Top 5 products display accurately.
+ - Adjusted limits (Top 3, Top 10) to validate flexibility.
+
+5. **Summary Cards**
+ - Confirmed Total Revenue calculations are accurate.
+ - Verified Total Order Count updates with filters.
+
+6. **UI & Responsiveness**
+ - Matched the final UI to the Figma design.
+ - Tested responsiveness across desktop, tablet, and mobile.
+ - Confirmed the order ID copy feature works reliably.
+
+### 3. Extended Prisma Database Model & Cross‑Sell Functionality
+- Updated the Prisma `Product` model to include an optional `crossSellProducts` field enabling product‑to‑product relationships.
+- Storefront now displays cross‑sell product suggestions on product detail pages.
+- The cart UI has been enhanced to show dynamic cross‑sell suggestions along with improved, user‑friendly feedback and notifications.
+
+
+
+### Extend Prisma DB Model & Cross-Sell Functionality
+
+#### Objective
+Extended the Prisma Product model by adding an optional `crossSellProducts` field to support linking related products. This allows the storefront to display cross-sell suggestions on product detail and cart pages, paired with improved cart feedback to strengthen product discovery.
+
+#### Implementation Details
+1. **Schema Update**
+ - Modified the Product model to include a self-relation many-to-many field.
+ - Added two relational fields, `crossSellProducts` and `crossSellOf`, enabling bidirectional product linking.
+
+2. **Seed Update**
+ - Updated the seed script to include products with cross-sell relationships.
+ - Ensured multiple products can be linked without affecting existing product data.
+
+3. **API Integration**
+ - Implemented an API endpoint that retrieves all `crossSellProducts` for the provided product IDs.
+
+4. **Displaying Suggested Products**
+ - Added a product card carousel on the product page to show cross-sell items.
+ - Added another carousel in the cart to highlight related suggestions for items currently in the cart.
+
+5. **Frontend Enhancements**
+ - Displayed each product’s stock count; items marked `isAvailable = false` are shown as Out of Stock and cannot be added to the cart.
+ - Improved Add to Cart flow by allowing users to enter quantity before adding, reducing repeated requests.
+ - Added a popup on the cart icon to acknowledge successful additions.
+ - After adding a product, a redirect button with a badge appears, showing how many items were added.
+ - Added quantity validation based on available stock.
+ - Enhanced the cart page to allow quantity edits in a single update action rather than incremental changes.
diff --git a/apps/admin/bun.lockb b/apps/admin/bun.lockb
index bb505b93..45d65b3c 100644
Binary files a/apps/admin/bun.lockb and b/apps/admin/bun.lockb differ
diff --git a/apps/admin/package.json b/apps/admin/package.json
index 37024013..0792886a 100644
--- a/apps/admin/package.json
+++ b/apps/admin/package.json
@@ -16,6 +16,9 @@
"db:seed": "npx prisma db seed",
"db:studio": "npx prisma studio"
},
+ "prisma": {
+ "seed": "bun run prisma/seed.ts"
+ },
"dependencies": {
"@hookform/resolvers": "^3.9.0",
"@persepolis/mail": "^1.0.9",
@@ -32,10 +35,10 @@
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
- "@radix-ui/react-popover": "^1.1.2",
+ "@radix-ui/react-popover": "^1.1.15",
"@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.4",
"@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-toast": "^1.2.2",
@@ -51,6 +54,7 @@
"next-themes": "^0.3.0",
"nodemailer": "^6.9.15",
"react": "18.3.1",
+ "react-day-picker": "^9.11.1",
"react-dom": "18.3.1",
"react-hook-form": "^7.53.0",
"react-hot-toast": "^2.4.1",
diff --git a/apps/admin/prisma/schema.prisma b/apps/admin/prisma/schema.prisma
index 453393d4..d1070584 100644
--- a/apps/admin/prisma/schema.prisma
+++ b/apps/admin/prisma/schema.prisma
@@ -123,6 +123,10 @@ model Product {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
+ crossSellProducts Product[] @relation("CrossSell")
+ crossSellOf Product[] @relation("CrossSell")
+
+
@@index([brandId])
}
diff --git a/apps/admin/prisma/seed.ts b/apps/admin/prisma/seed.ts
index b90aa004..183c1174 100644
--- a/apps/admin/prisma/seed.ts
+++ b/apps/admin/prisma/seed.ts
@@ -267,6 +267,39 @@ async function main() {
console.error('Could not create products...')
}
+ try {
+ console.log('Assigning random cross-sell products...')
+
+ for (const product of createdProducts) {
+ // filter out the product itself
+ const otherProducts = createdProducts.filter(
+ (p) => p.id !== product.id
+ )
+
+ // choose a random number from 1 to 5 (or less if fewer products exist)
+ const maxCrossSells = Math.min(5, otherProducts.length)
+ const randomCount = Math.floor(Math.random() * maxCrossSells) + 1
+
+ // shuffle + slice based on randomCount
+ const randomCrossSells = otherProducts
+ .sort(() => 0.5 - Math.random())
+ .slice(0, randomCount)
+
+ await prisma.product.update({
+ where: { id: product.id },
+ data: {
+ crossSellProducts: {
+ connect: randomCrossSells.map((p) => ({ id: p.id })),
+ },
+ },
+ })
+ }
+
+ console.log('Random cross-sell assignment done!')
+ } catch (error) {
+ console.error('Could not assign cross-sell products...', error)
+ }
+
try {
await prisma.author.create({
data: {
diff --git a/apps/admin/src/app/(dashboard)/(routes)/products/[productId]/components/product-form.tsx b/apps/admin/src/app/(dashboard)/(routes)/products/[productId]/components/product-form.tsx
index 967cf171..e6a0cf57 100644
--- a/apps/admin/src/app/(dashboard)/(routes)/products/[productId]/components/product-form.tsx
+++ b/apps/admin/src/app/(dashboard)/(routes)/products/[productId]/components/product-form.tsx
@@ -28,7 +28,7 @@ import { zodResolver } from '@hookform/resolvers/zod'
import { Category } from '@prisma/client'
import { Trash } from 'lucide-react'
import { useParams, useRouter } from 'next/navigation'
-import { useState } from 'react'
+import React, { useState } from 'react'
import { useForm } from 'react-hook-form'
import { toast } from 'react-hot-toast'
import * as z from 'zod'
diff --git a/apps/admin/src/app/(dashboard)/(routes)/reports/components/chart-component.tsx b/apps/admin/src/app/(dashboard)/(routes)/reports/components/chart-component.tsx
new file mode 100644
index 00000000..4f3a9ed0
--- /dev/null
+++ b/apps/admin/src/app/(dashboard)/(routes)/reports/components/chart-component.tsx
@@ -0,0 +1,78 @@
+import { Card, CardContent, CardHeader } from '@/components/ui/card'
+import { Separator } from '@/components/ui/separator'
+import { format } from 'date-fns'
+import { ChevronsRight, Coins, ScrollText } from 'lucide-react'
+import React from 'react'
+
+export default function ChartComponent({
+ orderCount = 0,
+ startDate,
+ endDate,
+ totalRevenue,
+}: {
+ orderCount: number
+ startDate: string
+ endDate: string
+ totalRevenue: number
+}) {
+ return (
+
+ {(startDate || endDate) && (
+ <>
+
+