diff --git a/app/(api)/_datalib/_resolvers/Order.ts b/app/(api)/_datalib/_resolvers/Order.ts
index 858b56c..0cd3593 100644
--- a/app/(api)/_datalib/_resolvers/Order.ts
+++ b/app/(api)/_datalib/_resolvers/Order.ts
@@ -1,5 +1,11 @@
import Orders from '../_services/Orders';
-import { OrderInput, Order, OrderProductInput } from '@datatypes/Order';
+import {
+ Order,
+ OrderInput,
+ OrderProductInput,
+ OrderStatus,
+ CancellationStatus,
+} from '@datatypes/Order';
import { ApolloContext } from '../apolloServer';
const resolvers = {
@@ -13,14 +19,32 @@ const resolvers = {
orders: (
_: never,
args: {
- statuses: string[];
+ statuses: OrderStatus[];
+ cancellation_statuses: CancellationStatus[];
search: string;
offset: number;
limit: number;
},
ctx: ApolloContext
) =>
- Orders.findMany(args.statuses, args.search, args.offset, args.limit, ctx),
+ Orders.findMany(
+ args.statuses,
+ args.cancellation_statuses,
+ args.search,
+ args.offset,
+ args.limit,
+ ctx
+ ),
+ ordersCount: (
+ _: never,
+ args: {
+ statuses: OrderStatus[];
+ cancellation_statuses: CancellationStatus[];
+ search: string;
+ },
+ ctx: ApolloContext
+ ) =>
+ Orders.count(args.statuses, args.cancellation_statuses, args.search, ctx),
},
Mutation: {
updateOrder: (
diff --git a/app/(api)/_datalib/_services/Orders.ts b/app/(api)/_datalib/_services/Orders.ts
index c38a7ee..616628c 100644
--- a/app/(api)/_datalib/_services/Orders.ts
+++ b/app/(api)/_datalib/_services/Orders.ts
@@ -1,6 +1,12 @@
import revalidateCache from '@actions/revalidateCache';
import prisma from '../_prisma/client';
-import { OrderInput, OrderProductInput } from '@datatypes/Order';
+import {
+ CancellationStatus,
+ OrderInput,
+ OrderProductInput,
+ OrderStatus,
+ OrderUpdateInput,
+} from '@datatypes/Order';
import { ApolloContext } from '../apolloServer';
import { Prisma } from '@prisma/client';
import Stripe from 'stripe';
@@ -14,7 +20,7 @@ export default class Orders {
data: {
...input, // Spread the input fields
total: 0,
- status: 'pending', // Default status
+ status: OrderStatus.PENDING, // Default status
created_at: new Date(), // Current timestamp
},
});
@@ -34,20 +40,41 @@ export default class Orders {
}
static async findMany(
- statuses: string[],
+ statuses: OrderStatus[],
+ cancellation_statuses: CancellationStatus[],
search: string,
offset: number,
limit: number,
ctx: ApolloContext
) {
if (!ctx.isOwner && !ctx.hasValidApiKey) return null;
-
if (offset < 0 || limit <= 0) return null;
- const whereClause: Prisma.OrderWhereInput = {};
+ const queryConditions: Prisma.OrderWhereInput[] = [];
+
+ const statusFilters: Prisma.OrderWhereInput[] = [];
+ if (statuses?.length) {
+ statusFilters.push({ status: { in: statuses } });
+ }
+ if (cancellation_statuses?.length) {
+ statusFilters.push({
+ cancellation_status: { in: cancellation_statuses },
+ });
+ }
- if (statuses && statuses.length > 0) {
- whereClause.status = { in: statuses };
+ if (statusFilters.length > 1) {
+ queryConditions.push({ OR: statusFilters });
+ } else if (statusFilters.length === 1) {
+ queryConditions.push(statusFilters[0]);
+ }
+
+ // For in progress requests, ensure there's no cancellation status.
+ const isInProgressRequest =
+ statuses?.length &&
+ !cancellation_statuses?.length &&
+ !statuses.includes(OrderStatus.DELIVERED);
+ if (isInProgressRequest) {
+ queryConditions.push({ cancellation_status: null });
}
if (search) {
@@ -75,19 +102,86 @@ export default class Orders {
});
}
- whereClause.OR = searchConditions;
+ queryConditions.push({ OR: searchConditions });
}
return prisma.order.findMany({
- where: whereClause,
+ where: { AND: queryConditions },
orderBy: {
created_at: 'desc',
},
- skip: offset * limit,
+ skip: offset,
take: limit,
});
}
+ static async count(
+ statuses: OrderStatus[],
+ cancellation_statuses: CancellationStatus[],
+ search: string,
+ ctx: ApolloContext
+ ) {
+ if (!ctx.isOwner && !ctx.hasValidApiKey) return null;
+
+ const queryConditions: Prisma.OrderWhereInput[] = [];
+
+ const statusFilters: Prisma.OrderWhereInput[] = [];
+ if (statuses?.length) {
+ statusFilters.push({ status: { in: statuses } });
+ }
+ if (cancellation_statuses?.length) {
+ statusFilters.push({
+ cancellation_status: { in: cancellation_statuses },
+ });
+ }
+
+ if (statusFilters.length > 1) {
+ queryConditions.push({ OR: statusFilters });
+ } else if (statusFilters.length === 1) {
+ queryConditions.push(statusFilters[0]);
+ }
+
+ const isInProgressRequest =
+ statuses?.length &&
+ !cancellation_statuses?.length &&
+ !statuses.includes(OrderStatus.DELIVERED);
+ if (isInProgressRequest) {
+ queryConditions.push({ cancellation_status: null });
+ }
+
+ if (search) {
+ const searchConditions: Prisma.OrderWhereInput[] = [
+ { customer_name: { contains: search, mode: 'insensitive' } },
+ { customer_email: { contains: search, mode: 'insensitive' } },
+ { customer_phone_num: { contains: search, mode: 'insensitive' } },
+ ];
+
+ const searchAsNumber = parseInt(search, 10);
+ if (!isNaN(searchAsNumber)) {
+ searchConditions.push({ id: searchAsNumber });
+ searchConditions.push({
+ id: {
+ in: await prisma.order
+ .findMany({
+ select: { id: true },
+ })
+ .then((orders) =>
+ orders
+ .filter((order) => order.id.toString().includes(search))
+ .map((order) => order.id)
+ ),
+ },
+ });
+ }
+
+ queryConditions.push({ OR: searchConditions });
+ }
+
+ return prisma.order.count({
+ where: { AND: queryConditions },
+ });
+ }
+
static async getProducts(order_id: number, ctx: ApolloContext) {
if (!ctx.isOwner && !ctx.hasValidApiKey) return null;
@@ -109,7 +203,7 @@ export default class Orders {
}
//UPDATE
- static async update(id: number, input: OrderInput, ctx: ApolloContext) {
+ static async update(id: number, input: OrderUpdateInput, ctx: ApolloContext) {
if (!ctx.isOwner && !ctx.hasValidApiKey) return null;
try {
@@ -283,12 +377,12 @@ export default class Orders {
id,
},
select: {
- status: true,
+ cancellation_status: true,
},
});
if (order) {
- if (order.status == 'refunded') {
+ if (order.cancellation_status == 'REFUNDED') {
await prisma.order.delete({
where: {
id,
@@ -302,7 +396,7 @@ export default class Orders {
id,
},
data: {
- status: 'needs refund',
+ cancellation_status: CancellationStatus.CANCELLED,
},
});
// could possibly add a message? "this order need to be refunded" or smth
@@ -321,70 +415,62 @@ export default class Orders {
products: OrderProductInput[],
ctx: ApolloContext
) {
- if (!ctx.isOwner && !ctx.hasValidApiKey) return null;
-
- try {
- const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
- apiVersion: '2025-05-28.basil', // explicitly set the API version
- });
-
- // Lookup product prices from DB
- const productIds = products.map((p) => p.product_id);
- const dbProducts = await prisma.product.findMany({
- where: { id: { in: productIds } },
- });
-
- const productMap = Object.fromEntries(dbProducts.map((p) => [p.id, p]));
-
- const total = products.reduce((sum, item) => {
- const product = productMap[item.product_id];
- return sum + (product?.price ?? 0) * item.quantity;
- }, 0);
+ if (!ctx.isOwner && !ctx.hasValidApiKey) {
+ throw new Error('Unauthorized');
+ }
- // Stripe counts payment amounts in cents
- const amountInCents = Math.round(total * 100);
+ const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, {
+ apiVersion: '2025-05-28.basil',
+ });
- // Create the order
- const createdOrder = await prisma.order.create({
- data: {
- ...input,
- total: total,
- status: 'pending',
- created_at: new Date(),
- products: {
- create: products.map((p) => ({
- quantity: p.quantity,
- product: { connect: { id: p.product_id } },
- })),
- },
- },
- include: { products: { include: { product: true } } },
- });
+ let total = 0;
+ await Promise.all(
+ products.map(async (p) => {
+ const product = await prisma.product.findUnique({
+ where: { id: p.product_id },
+ });
+ if (!product) {
+ throw new Error(`Product with ID ${p.product_id} not found`);
+ }
+ total += (product.price - (product.discount ?? 0)) * p.quantity;
+ })
+ );
- // Create Stripe PaymentIntent
- const paymentIntent = await stripe.paymentIntents.create({
- amount: amountInCents,
- currency: 'usd',
- metadata: {
- orderId: createdOrder.id,
+ const order = await prisma.order.create({
+ data: {
+ ...input,
+ total,
+ status: OrderStatus.ORDERED,
+ created_at: new Date(),
+ products: {
+ create: products.map((p) => ({
+ product_id: p.product_id,
+ quantity: p.quantity,
+ })),
},
- });
+ },
+ include: {
+ products: true,
+ },
+ });
- // Save paymentIntentId to order
- const updatedOrder = await prisma.order.update({
- where: { id: createdOrder.id },
- data: { paymentIntentId: paymentIntent.id },
- include: { products: { include: { product: true } } },
- });
+ const paymentIntent = await stripe.paymentIntents.create({
+ amount: Math.round(total * 100),
+ currency: 'usd',
+ metadata: {
+ orderId: order.id,
+ },
+ });
- revalidateCache(['orders', 'products']);
+ await prisma.order.update({
+ where: { id: order.id },
+ data: { paymentIntentId: paymentIntent.id },
+ });
- return {
- order: updatedOrder,
- clientSecret: paymentIntent.client_secret,
- };
- } catch (e) {
- return e;
- }
+ revalidateCache(['orders']);
+ return {
+ order,
+ clientSecret: paymentIntent.client_secret,
+ };
}
}
diff --git a/app/(api)/_datalib/_typeDefs/Order.ts b/app/(api)/_datalib/_typeDefs/Order.ts
index f2a1755..9ce45f8 100644
--- a/app/(api)/_datalib/_typeDefs/Order.ts
+++ b/app/(api)/_datalib/_typeDefs/Order.ts
@@ -1,6 +1,19 @@
import gql from 'graphql-tag';
const typeDefs = gql`
+ enum OrderStatus {
+ PENDING
+ ORDERED
+ SHIPPED
+ IN_TRANSIT
+ DELIVERED
+ }
+
+ enum CancellationStatus {
+ CANCELLED
+ REFUNDED
+ }
+
type Order {
id: ID!
paymentIntentId: String
@@ -19,7 +32,8 @@ const typeDefs = gql`
shipping_city: String!
shipping_zip: String!
shipping_country: String!
- status: String!
+ status: OrderStatus!
+ cancellation_status: CancellationStatus
created_at: String!
}
@@ -37,7 +51,8 @@ const typeDefs = gql`
shipping_city: String!
shipping_zip: String!
shipping_country: String!
- status: String!
+ status: OrderStatus
+ cancellation_status: CancellationStatus
}
input OrderUpdateInput {
@@ -55,7 +70,8 @@ const typeDefs = gql`
shipping_city: String
shipping_zip: String
shipping_country: String
- status: String
+ status: OrderStatus
+ cancellation_status: CancellationStatus
}
input OrderProductInput {
@@ -76,11 +92,17 @@ const typeDefs = gql`
type Query {
order(id: ID!): Order
orders(
- statuses: [String]
+ statuses: [OrderStatus]
+ cancellation_statuses: [CancellationStatus]
search: String
offset: Int!
limit: Int!
): [Order]
+ ordersCount(
+ statuses: [OrderStatus]
+ cancellation_statuses: [CancellationStatus]
+ search: String
+ ): Int
}
type Mutation {
diff --git a/app/(api)/_types/Order.ts b/app/(api)/_types/Order.ts
index e71ca62..b0bcb04 100644
--- a/app/(api)/_types/Order.ts
+++ b/app/(api)/_types/Order.ts
@@ -1,5 +1,18 @@
import { Product } from './Product';
+export enum OrderStatus {
+ PENDING = 'PENDING',
+ ORDERED = 'ORDERED',
+ SHIPPED = 'SHIPPED',
+ IN_TRANSIT = 'IN_TRANSIT',
+ DELIVERED = 'DELIVERED',
+}
+
+export enum CancellationStatus {
+ CANCELLED = 'CANCELLED',
+ REFUNDED = 'REFUNDED',
+}
+
export type Order = {
id: number;
total: number;
@@ -16,8 +29,9 @@ export type Order = {
shipping_city: string;
shipping_zip: string;
shipping_country: string;
- status: string;
- created_at: Date;
+ status: OrderStatus;
+ cancellation_status: CancellationStatus | null;
+ created_at: string;
products: ProductToOrder[];
};
@@ -53,6 +67,8 @@ export type OrderInput = {
shipping_city: string;
shipping_zip: string;
shipping_country: string;
+ status?: OrderStatus;
+ cancellation_status?: CancellationStatus;
};
//make all inputs optional
diff --git a/app/(pages)/(app)/(index-page)/_components/Hero/Hero.module.scss b/app/(pages)/(app)/(index-page)/_components/Hero/Hero.module.scss
deleted file mode 100644
index b31ed3b..0000000
--- a/app/(pages)/(app)/(index-page)/_components/Hero/Hero.module.scss
+++ /dev/null
@@ -1,41 +0,0 @@
-@use 'media';
-
-.container {
- display: flex;
- flex-direction: row;
- justify-content: center;
- height: calc(100vh - var(--navbar-height));
- width: 100%;
- position: relative;
- padding: var(--large-spacer);
- @include media.phone {
- padding: var(--medium-spacer);
- }
-
- .img_container {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: black;
- z-index: -1;
- }
-
- .welcome {
- width: 100%;
- max-width: 1620px;
- height: 100%;
- border-radius: var(--b-radius);
- border: solid 2px var(--light-background);
- display: flex;
- flex-direction: column;
- justify-content: center;
- text-align: center;
- padding: var(--spacer);
-
- > h1 {
- color: var(--text-light);
- }
- }
-}
\ No newline at end of file
diff --git a/app/(pages)/(app)/(index-page)/_components/Hero/Hero.tsx b/app/(pages)/(app)/(index-page)/_components/Hero/Hero.tsx
deleted file mode 100644
index 772990b..0000000
--- a/app/(pages)/(app)/(index-page)/_components/Hero/Hero.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import Image from 'next/image';
-
-import styles from './Hero.module.scss';
-
-export default function Hero() {
- return (
-
-
-
-
-
-
ESTORE MANAGEMENT TOOL
-
-
- );
-}
diff --git a/app/(pages)/(app)/(index-page)/page.tsx b/app/(pages)/(app)/(index-page)/page.tsx
deleted file mode 100644
index 998843f..0000000
--- a/app/(pages)/(app)/(index-page)/page.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import Hero from './_components/Hero/Hero';
-
-export default function Home() {
- return (
-
-
-
- );
-}
diff --git a/app/(pages)/(app)/(orders)/_components/OrderCard.module.scss b/app/(pages)/(app)/(orders)/_components/OrderCard.module.scss
new file mode 100644
index 0000000..5d38b74
--- /dev/null
+++ b/app/(pages)/(app)/(orders)/_components/OrderCard.module.scss
@@ -0,0 +1,192 @@
+.container {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 24px;
+ background-color: var(--background-secondary);
+ border-radius: 12px;
+ box-shadow: 0px 4px 20px rgba(51, 51, 51, 0.06);
+ margin-bottom: 16px;
+}
+
+.content {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ gap: 24px;
+}
+
+.product_image {
+ width: 240px;
+ height: 160px;
+ position: relative;
+ border-radius: 8px;
+ overflow: hidden;
+ border: 1px solid var(--gray-300);
+ flex-shrink: 0;
+
+ .image {
+ object-fit: cover;
+ }
+}
+
+.order_info {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.order_details {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.order_id {
+ font-size: 18px;
+ font-weight: 600;
+ color: var(--text-dark);
+ margin: 0;
+}
+
+.shipping_info {
+ font-size: 14px;
+ font-weight: 400;
+ color: var(--purple);
+ margin: 0;
+}
+
+.order_date {
+ font-size: 14px;
+ font-weight: 400;
+ color: var(--gray-600);
+ margin: 0;
+}
+
+.progress_section {
+ margin-top: 8px;
+}
+
+.progress_bar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ position: relative;
+ width: 100%;
+ max-width: 500px;
+
+ &::before {
+ content: '';
+ position: absolute;
+ top: 20%;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background-color: var(--gray-300);
+ z-index: 1;
+ }
+}
+
+.status_item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ position: relative;
+ z-index: 2;
+}
+
+.status_dot {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ border: 2px solid var(--gray-300);
+ background-color: var(--background-secondary);
+ position: relative;
+
+ &.completed {
+ background-color: var(--purple);
+ border-color: var(--purple);
+
+ &::after {
+ content: '✓';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ color: var(--text-light);
+ font-size: 12px;
+ font-weight: bold;
+ }
+ }
+
+ &.current {
+ border-color: var(--purple);
+ background-color: var(--purple);
+
+ &::after {
+ content: '✓';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ color: var(--text-light);
+ font-size: 12px;
+ font-weight: bold;
+ }
+ }
+
+ &.pending {
+ background-color: var(--background-secondary);
+ border-color: var(--gray-300);
+ }
+}
+
+.status_label {
+ font-size: 12px;
+ font-weight: 400;
+ color: var(--gray-600);
+ text-align: center;
+ white-space: nowrap;
+}
+
+.order_id_container {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.status_badge {
+ padding: 0.25rem 0.75rem;
+ border-radius: 1rem;
+ font-size: 0.875rem;
+ font-weight: 500;
+
+ &.refunded {
+ background-color: var(--error-light);
+ color: var(--error-dark);
+ }
+
+ &.cancelled {
+ background-color: var(--gray-200);
+ color: var(--gray-800);
+ }
+}
+
+.view_order_btn {
+ padding: 12px 24px;
+ background-color: var(--purple);
+ color: var(--text-light);
+ border: none;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+
+ &:hover {
+ background-color: #2A1B5E;
+ }
+}
\ No newline at end of file
diff --git a/app/(pages)/(app)/(orders)/_components/OrderCard.tsx b/app/(pages)/(app)/(orders)/_components/OrderCard.tsx
new file mode 100644
index 0000000..378d97e
--- /dev/null
+++ b/app/(pages)/(app)/(orders)/_components/OrderCard.tsx
@@ -0,0 +1,100 @@
+'use client';
+
+import Image from 'next/image';
+import styles from './OrderCard.module.scss';
+import { CancellationStatus, OrderStatus } from '@datatypes/Order';
+
+interface OrderCardProps {
+ id: number;
+ created_at: string;
+ status: OrderStatus;
+ cancellation_status: CancellationStatus | null;
+ image: string;
+}
+
+export default function OrderCard({
+ id,
+ created_at,
+ status,
+ cancellation_status,
+ image,
+}: OrderCardProps) {
+ const progressBarStatuses = [
+ OrderStatus.PENDING,
+ OrderStatus.ORDERED,
+ OrderStatus.SHIPPED,
+ OrderStatus.IN_TRANSIT,
+ OrderStatus.DELIVERED,
+ ];
+
+ const progressBarLabels: { [key: string]: string } = {
+ [OrderStatus.PENDING]: 'Pending',
+ [OrderStatus.ORDERED]: 'Ordered',
+ [OrderStatus.SHIPPED]: 'Shipped',
+ [OrderStatus.IN_TRANSIT]: 'In Transit',
+ [OrderStatus.DELIVERED]: 'Delivered',
+ };
+
+ const badgeStatusLabels: { [key: string]: string } = {
+ [CancellationStatus.CANCELLED]: 'Cancelled',
+ [CancellationStatus.REFUNDED]: 'Refunded',
+ };
+
+ const currentStatusIndex = progressBarStatuses.indexOf(status);
+
+ return (
+
+
+
+
+
+
+
+
+
+
Order #{id}
+ {cancellation_status && (
+
+ {badgeStatusLabels[cancellation_status]}
+
+ )}
+
+
+ Order Placed:{' '}
+ {new Date(Number(created_at)).toLocaleDateString(undefined, {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ })}
+
+
+
+
+
+ {progressBarStatuses.map((value, index) => (
+
+
+
+ {progressBarLabels[value]}
+
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/(pages)/(app)/(orders)/_components/OrderSection/OrderSection.module.scss b/app/(pages)/(app)/(orders)/_components/OrderSection/OrderSection.module.scss
new file mode 100644
index 0000000..d4cdd22
--- /dev/null
+++ b/app/(pages)/(app)/(orders)/_components/OrderSection/OrderSection.module.scss
@@ -0,0 +1,99 @@
+.container {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+ width: 100%;
+}
+
+.section_header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+
+ h2 {
+ font-size: 24px;
+ font-weight: 600;
+ color: var(--text-dark);
+ margin: 0;
+ }
+}
+
+.pagination {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ background-color: var(--background-secondary);
+ padding: 0.5rem;
+ border-radius: 2rem;
+}
+
+.pagination_btn {
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: none;
+ border-radius: 50%;
+ background-color: var(--background-primary);
+ color: var(--purple);
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:hover {
+ background-color: var(--light-purple);
+ color: var(--text-light)
+ }
+
+ &:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ background-color: var(--gray-100);
+ color: var(--gray-400);
+ }
+}
+
+.pagination_text {
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--text-dark);
+}
+
+.status_filters {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+ width: 100%;
+}
+
+.filter_btn {
+ padding: 12px 20px;
+ font-size: 14px;
+ font-weight: 500;
+ border: 1px solid var(--gray-300);
+ border-radius: 10px;
+ background-color: var(--background-secondary);
+ color: var(--gray-700);
+ cursor: pointer;
+ transition: all 0.2s ease;
+ box-shadow: 0px 4px 20px rgba(51, 51, 51, 0.06);
+
+ &:hover {
+ background-color: var(--background-primary);
+ border-color: var(--gray-400);
+ }
+
+ &.selected {
+ background-color: var(--purple);
+ color: var(--text-light);
+ border-color: var(--purple);
+ }
+}
+
+.order_cards {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ width: 100%;
+}
\ No newline at end of file
diff --git a/app/(pages)/(app)/(orders)/_components/OrderSection/OrderSection.tsx b/app/(pages)/(app)/(orders)/_components/OrderSection/OrderSection.tsx
new file mode 100644
index 0000000..011bb10
--- /dev/null
+++ b/app/(pages)/(app)/(orders)/_components/OrderSection/OrderSection.tsx
@@ -0,0 +1,88 @@
+import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
+import { Order } from '@datatypes/Order';
+import OrderCard from '../OrderCard';
+import styles from './OrderSection.module.scss';
+
+interface OrderSectionProps {
+ title: string;
+ orders: Order[];
+ statusOptions: (T | 'All')[];
+ statusClassMap: { [key: string]: string };
+ selectedStatus: T | 'All';
+ onStatusChange: (status: T | 'All') => void;
+ page: number;
+ onPageChange: (page: number) => void;
+ highestPage: number;
+}
+
+export default function OrderSection({
+ title,
+ orders,
+ statusOptions,
+ statusClassMap,
+ selectedStatus,
+ onStatusChange,
+ page,
+ onPageChange,
+ highestPage,
+}: OrderSectionProps) {
+ return (
+
+
+
{title}
+
+
+
+ {page + 1} of {highestPage + 1}
+
+
+
+
+
+
+ {statusOptions.map((status) => (
+
+ ))}
+
+
+
+ {orders.map((order) => (
+
+ ))}
+
+
+ );
+}
diff --git a/app/(pages)/(app)/(orders)/order-details/_components/OrderPart.module.scss b/app/(pages)/(app)/(orders)/order-details/_components/OrderPart.module.scss
new file mode 100644
index 0000000..abc6721
--- /dev/null
+++ b/app/(pages)/(app)/(orders)/order-details/_components/OrderPart.module.scss
@@ -0,0 +1,68 @@
+@use 'media';
+@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;700&display=swap');
+
+.container {
+ width: 882px;
+ height: 120px;
+ background-color: white;
+ padding: 20px;
+ box-shadow: 0px 4px 20px 0px #3333330F;
+ border-radius: 10px;
+ display:flex;
+ align-items: center;
+}
+.text {
+ font-size: 18px;
+ font-family: 'Manrope', sans-serif;
+}
+.material {
+ width: 300px;
+ height: 80px;
+ margin-right: 121px;
+ display: flex;
+ align-items: center;
+
+}
+.image {
+ width: 80px;
+ height: 80px;
+ margin-right: 16px;
+ border-radius: 10px;
+}
+.materialtext {
+ flex-direction: column;
+ gap: 8px;
+ width: 124px;
+ height: 52px;
+ font-size: 18px;
+ line-height: 120%;
+ font-family: 'Manrope', sans-serif;
+}
+.priceBreakdown {
+ margin-right: 121px;
+ width: 200px;
+ height: 22px;
+ font-family: 'Manrope', sans-serif;
+ font-size: 18px;
+ line-height: 120%;
+ display: flex;
+}
+.number
+{
+ width: 8px;
+ margin-right: 24px;
+}
+.times
+{
+ width: 11px;
+ margin-right: 24px;
+}
+.pricetimes {
+ width: 57px;
+}
+.price {
+ width: 100px;
+ height: 22px;
+ font-family: 'Manrope', sans-serif;
+ font-size: 18px;
+}
\ No newline at end of file
diff --git a/app/(pages)/(app)/(orders)/order-details/_components/OrderPart.tsx b/app/(pages)/(app)/(orders)/order-details/_components/OrderPart.tsx
new file mode 100644
index 0000000..e0f3f84
--- /dev/null
+++ b/app/(pages)/(app)/(orders)/order-details/_components/OrderPart.tsx
@@ -0,0 +1,31 @@
+'use client';
+import Image from 'next/image';
+import styles from './OrderPart.module.scss';
+import material from './material.jpg';
+export default function OrderPart() {
+ return (
+
+
+
+
+
Material Name
+
Category
+
+
+
+
+
+ );
+}
diff --git a/app/(pages)/(app)/(orders)/order-details/_components/material.jpg b/app/(pages)/(app)/(orders)/order-details/_components/material.jpg
new file mode 100644
index 0000000..9f9e69d
Binary files /dev/null and b/app/(pages)/(app)/(orders)/order-details/_components/material.jpg differ
diff --git a/app/(pages)/(app)/(orders)/order-details/page.module.scss b/app/(pages)/(app)/(orders)/order-details/page.module.scss
new file mode 100644
index 0000000..765aa44
--- /dev/null
+++ b/app/(pages)/(app)/(orders)/order-details/page.module.scss
@@ -0,0 +1,151 @@
+@use 'media';
+@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;700&display=swap');
+
+.container {
+ display:flex;
+ width: 1428px;
+ height: 1117px;
+ background-color: #F6F6F6;
+ margin-left: 300px;
+}
+.seccontainer {
+ width: 1268px;
+ height: 100%;
+ margin-right: 80px;
+ margin-left: 80px;
+}
+.back {
+ width: 1268px;
+ height: 35px;
+ margin-top: 48px;
+ margin-top: 48px;
+ display:flex;
+}
+.maincontainer {
+ width: 1268px;
+}
+.orderdelete {
+ display: flex;
+ width: 1268px;
+ height: 64px;
+ margin-bottom: 16px;
+}
+.info{
+ margin-top: 16px;
+ height: 580px;
+ display: flex;
+}
+.materialcost {
+ width: 882px;
+ height: 580px;
+}
+.materials {
+ flex-direction: column;
+ width: 882px;
+ height: 360px;
+ margin-bottom: 16px;
+ background-color: white;
+ border-radius: 10px;
+}
+.cost {
+ width: 882px;
+ height: 204px;
+ background-color: white;
+ border-radius: 10px;
+}
+.shipping {
+ width: 366px;
+ height: 532px;
+ background-color: white;
+ margin-left: 20px;
+ border-radius: 10px;
+ padding: 40px;
+ box-shadow: 0px 4px 20px 0px #3333330F;
+
+}
+.arrowcontainer {
+ width: 18px;
+ height: 35px;
+}
+.arrow {
+
+ border: solid black;
+ border-width: 0 3px 3px 0;
+ display: inline-block;
+
+ cursor: pointer;
+ transform: rotate(135deg);
+ -webkit-transform: rotate(135deg);
+
+}
+.left-arrow:hover {
+ background-color: #0056b3;
+}
+.text {
+ font-size: 18px;
+ font-family: 'Manrope', sans-serif;
+}
+.h1text{
+ font-size: 48px;
+ font-family: 'Manrope', sans-serif;
+ width: 634px;
+ height: 64px;
+ margin-right: 462px;
+}
+.deletebutton{
+ cursor: pointer;
+ width:172px;
+ height:64px;
+ border-radius: 10px;
+ padding:10px 40px 10px 40px;
+ background-color: white;
+ box-shadow: 0px 4px 20px 0px #3333330F;
+ border: none;
+}
+.dropdown {
+ position: relative;
+ display: inline-block;
+ }
+
+ .dropdownToggle {
+ box-shadow: 0px 4px 20px 0px #3333330F;
+ width: 200px;
+ height: 48px;
+ padding: 10px 20px;
+ background-color: white;
+ color: black;
+ border: none;
+ cursor: pointer;
+ border-radius: 4px;
+ font-size: 16px;
+ }
+
+ .dropdownToggle:hover {
+ background-color: #aeb6c0;
+ }
+
+ .dropdownMenu {
+ position: absolute;
+ top: 110%;
+ left: 0;
+ background-color: white;
+ min-width: 160px;
+ box-shadow: 0px 8px 16px rgba(0,0,0,0.2);
+ border-radius: 4px;
+ padding: 8px 0;
+ z-index: 1;
+ }
+ .dropdownMenu li {
+ list-style: none;
+ }
+
+ .dropdownMenu li a {
+ padding: 10px 16px;
+ display: block;
+ color: #333;
+ text-decoration: none;
+ }
+
+ .dropdownMenu li a:hover {
+ background-color: #f1f1f1;
+ }
\ No newline at end of file
diff --git a/app/(pages)/(app)/(orders)/order-details/page.tsx b/app/(pages)/(app)/(orders)/order-details/page.tsx
new file mode 100644
index 0000000..57fdbd3
--- /dev/null
+++ b/app/(pages)/(app)/(orders)/order-details/page.tsx
@@ -0,0 +1,57 @@
+'use client';
+import OrderPart from './_components/OrderPart';
+import React, { useState } from 'react';
+import styles from './page.module.scss';
+
+export default function OrderDetails() {
+ const [isOpen, setIsOpen] = useState(false);
+ const toggleDropdown = () => {
+ setIsOpen((prevState) => !prevState);
+ };
+ return (
+
+
+
+
+
+
Order #48219
+
+
+
+
+ {isOpen && (
+
+ )}
+
+
+
+
+
+ );
+}
diff --git a/app/(pages)/(app)/(orders)/page.module.scss b/app/(pages)/(app)/(orders)/page.module.scss
new file mode 100644
index 0000000..a57b6dc
--- /dev/null
+++ b/app/(pages)/(app)/(orders)/page.module.scss
@@ -0,0 +1,160 @@
+.page_container {
+ display: flex;
+ flex-direction: column;
+ background-color: whitesmoke;
+ padding: 32px;
+ gap: 32px;
+ min-height: 100vh;
+ width: 100%;
+}
+
+.header {
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+ width: 100%;
+}
+
+.title {
+ font-size: 48px;
+ font-weight: 700;
+ color: var(--text-dark);
+ margin: 0;
+}
+
+.searchbar {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ max-width: 600px;
+ width: 100%;
+
+ .search {
+ flex: 1;
+ padding: 12px 20px;
+ font-size: 16px;
+ border: 1px solid var(--gray-300);
+ border-radius: 50px;
+ outline: none;
+ transition: border-color 0.2s ease;
+
+ &:focus {
+ border-color: var(--purple);
+ }
+
+ &::placeholder {
+ color: var(--gray-500);
+ }
+ }
+
+ .search_submit {
+ padding: 12px 24px;
+ font-size: 16px;
+ font-weight: 600;
+ border: none;
+ border-radius: 10px;
+ background-color: var(--purple);
+ color: var(--text-light);
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+
+ &:hover {
+ background-color: #2A1B5E;
+ }
+ }
+}
+
+.pagination {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+}
+
+.pagination_btn {
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid var(--gray-300);
+ border-radius: 6px;
+ background-color: var(--background-secondary);
+ color: var(--gray-600);
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:hover {
+ background-color: var(--background-primary);
+ border-color: var(--gray-400);
+ }
+
+ &:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+}
+
+.pagination_text {
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--gray-600);
+}
+
+.status_filters {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+ width: 100%;
+}
+
+.filter_btn {
+ padding: 12px 20px;
+ font-size: 14px;
+ font-weight: 500;
+ border: 1px solid var(--gray-300);
+ border-radius: 10px;
+ background-color: var(--background-secondary);
+ color: var(--gray-700);
+ cursor: pointer;
+ transition: all 0.2s ease;
+ box-shadow: 0px 4px 20px rgba(51, 51, 51, 0.06);
+
+ &:hover {
+ background-color: var(--background-primary);
+ border-color: var(--gray-400);
+ }
+
+ &.selected {
+ background-color: var(--purple);
+ color: var(--text-light);
+ border-color: var(--purple);
+ }
+
+ // Individual button widths to match design
+ &.all-btn {
+ min-width: 81px;
+ }
+
+ &.ordered {
+ min-width: 130px;
+ }
+
+ &.packed {
+ min-width: 122px;
+ }
+
+ &.shipped {
+ min-width: 130px;
+ }
+
+ &.transit {
+ min-width: 138px;
+ }
+}
+
+.order_cards {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ width: 100%;
+}
\ No newline at end of file
diff --git a/app/(pages)/(app)/(orders)/page.tsx b/app/(pages)/(app)/(orders)/page.tsx
new file mode 100644
index 0000000..865d325
--- /dev/null
+++ b/app/(pages)/(app)/(orders)/page.tsx
@@ -0,0 +1,271 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { gql } from 'graphql-tag';
+import sendApolloRequest from '../../_utils/sendApolloRequest';
+import { Order, OrderStatus, CancellationStatus } from '@datatypes/Order';
+import OrderSection from './_components/OrderSection/OrderSection';
+import styles from './page.module.scss';
+
+const QUERY_LIMIT = 3;
+
+const ordersQuery = gql`
+ query OrderQuery(
+ $offset: Int!
+ $limit: Int!
+ $statuses: [OrderStatus!]
+ $cancellation_statuses: [CancellationStatus!]
+ $search: String
+ ) {
+ orders(
+ offset: $offset
+ limit: $limit
+ statuses: $statuses
+ cancellation_statuses: $cancellation_statuses
+ search: $search
+ ) {
+ id
+ customer_name
+ customer_email
+ customer_phone_num
+ created_at
+ status
+ cancellation_status
+ total
+ shipping_address_line_1
+ shipping_address_line_2
+ shipping_city
+ shipping_country
+ shipping_zip
+ billing_address_line_1
+ billing_address_line_2
+ billing_city
+ billing_country
+ billing_zip
+ products {
+ product {
+ id
+ name
+ price
+ discount
+ }
+ quantity
+ }
+ }
+ }
+`;
+
+const countQuery = gql`
+ query OrdersCount(
+ $statuses: [OrderStatus!]
+ $cancellation_statuses: [CancellationStatus!]
+ $search: String
+ ) {
+ ordersCount(
+ statuses: $statuses
+ cancellation_statuses: $cancellation_statuses
+ search: $search
+ )
+ }
+`;
+
+export default function ViewOrderCards() {
+ const [inProgressOrders, setInProgressOrders] = useState([]);
+ const [inProgressOrdersCount, setInProgressOrdersCount] = useState(0);
+ const [completedOrders, setCompletedOrders] = useState([]);
+ const [completedOrdersCount, setCompletedOrdersCount] = useState(0);
+ const [selectedStatus, setSelectedStatus] = useState(
+ 'All'
+ );
+ const [selectedCompletedStatus, setSelectedCompletedStatus] = useState<
+ CancellationStatus | 'All' | 'DELIVERED'
+ >('All');
+ const [inProgressPage, setInProgressPage] = useState(0);
+ const [completedPage, setCompletedPage] = useState(0);
+ const [searchInput, setSearchInput] = useState('');
+ const [searchTerm, setSearchTerm] = useState('');
+
+ const statusClassMap: { [key: string]: string } = {
+ All: 'all-btn',
+ [OrderStatus.PENDING]: 'pending',
+ [OrderStatus.ORDERED]: 'ordered',
+ [OrderStatus.SHIPPED]: 'shipped',
+ [OrderStatus.IN_TRANSIT]: 'transit',
+ };
+
+ const completedStatusClassMap: { [key: string]: string } = {
+ All: 'all-btn',
+ DELIVERED: 'delivered',
+ [CancellationStatus.CANCELLED]: 'cancelled',
+ [CancellationStatus.REFUNDED]: 'refunded',
+ };
+
+ const statusOptions: (OrderStatus | 'All')[] = [
+ 'All',
+ OrderStatus.PENDING,
+ OrderStatus.ORDERED,
+ OrderStatus.SHIPPED,
+ OrderStatus.IN_TRANSIT,
+ ];
+
+ const completedStatusOptions: (CancellationStatus | 'All' | 'DELIVERED')[] = [
+ 'All',
+ 'DELIVERED',
+ ...Object.values(CancellationStatus),
+ ];
+
+ const handleSearch = (e: React.FormEvent) => {
+ e.preventDefault();
+ setSearchTerm(searchInput);
+ setInProgressPage(0);
+ setCompletedPage(0);
+ };
+
+ useEffect(() => {
+ const fetchInProgressOrders = async () => {
+ const inProgressStatuses =
+ selectedStatus === 'All'
+ ? [
+ OrderStatus.PENDING,
+ OrderStatus.ORDERED,
+ OrderStatus.SHIPPED,
+ OrderStatus.IN_TRANSIT,
+ ]
+ : [selectedStatus];
+ const ordersPromise = sendApolloRequest({
+ request: ordersQuery,
+ variables: {
+ offset: inProgressPage * QUERY_LIMIT,
+ limit: QUERY_LIMIT,
+ statuses: inProgressStatuses,
+ search: searchTerm,
+ },
+ });
+ const countPromise = sendApolloRequest({
+ request: countQuery,
+ variables: {
+ statuses: inProgressStatuses,
+ search: searchTerm,
+ },
+ });
+ const [ordersData, countData] = await Promise.all([
+ ordersPromise,
+ countPromise,
+ ]);
+ setInProgressOrders(ordersData.data.orders);
+ setInProgressOrdersCount(countData.data.ordersCount);
+ };
+ fetchInProgressOrders();
+ }, [selectedStatus, inProgressPage, searchTerm]);
+
+ useEffect(() => {
+ const fetchCompletedOrders = async () => {
+ let completed_statuses: OrderStatus[] = [];
+ let cancellation_statuses: CancellationStatus[] = [];
+
+ if (selectedCompletedStatus === 'All') {
+ completed_statuses = [OrderStatus.DELIVERED];
+ cancellation_statuses = Object.values(CancellationStatus);
+ } else if (selectedCompletedStatus === 'DELIVERED') {
+ completed_statuses = [OrderStatus.DELIVERED];
+ } else {
+ cancellation_statuses = [selectedCompletedStatus as CancellationStatus];
+ }
+
+ const ordersPromise = sendApolloRequest({
+ request: ordersQuery,
+ variables: {
+ offset: completedPage * QUERY_LIMIT,
+ limit: QUERY_LIMIT,
+ statuses:
+ completed_statuses.length > 0 ? completed_statuses : undefined,
+ cancellation_statuses:
+ cancellation_statuses.length > 0
+ ? cancellation_statuses
+ : undefined,
+ search: searchTerm,
+ },
+ });
+ const countPromise = sendApolloRequest({
+ request: countQuery,
+ variables: {
+ statuses:
+ completed_statuses.length > 0 ? completed_statuses : undefined,
+ cancellation_statuses:
+ cancellation_statuses.length > 0
+ ? cancellation_statuses
+ : undefined,
+ search: searchTerm,
+ },
+ });
+ const [ordersData, countData] = await Promise.all([
+ ordersPromise,
+ countPromise,
+ ]);
+ setCompletedOrders(ordersData.data.orders);
+ setCompletedOrdersCount(countData.data.ordersCount);
+ };
+ fetchCompletedOrders();
+ }, [selectedCompletedStatus, completedPage, searchTerm]);
+
+ const highestInProgressPage = Math.max(
+ 0,
+ Math.ceil(inProgressOrdersCount / QUERY_LIMIT) - 1
+ );
+ const highestCompletedPage = Math.max(
+ 0,
+ Math.ceil(completedOrdersCount / QUERY_LIMIT) - 1
+ );
+
+ return (
+
+
+
Orders
+
+
+
+
+
{
+ setSelectedStatus(status);
+ setInProgressPage(0);
+ }}
+ page={inProgressPage}
+ onPageChange={setInProgressPage}
+ highestPage={highestInProgressPage}
+ />
+
+ {
+ setSelectedCompletedStatus(status);
+ setCompletedPage(0);
+ }}
+ page={completedPage}
+ onPageChange={setCompletedPage}
+ highestPage={highestCompletedPage}
+ />
+
+ );
+}
diff --git a/app/(pages)/(app)/orders/view-order/_components/ContactInfo/ContactInfo.module.scss b/app/(pages)/(app)/(orders)/view-order/_components/ContactInfo/ContactInfo.module.scss
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/ContactInfo/ContactInfo.module.scss
rename to app/(pages)/(app)/(orders)/view-order/_components/ContactInfo/ContactInfo.module.scss
diff --git a/app/(pages)/(app)/orders/view-order/_components/ContactInfo/ContactInfo.tsx b/app/(pages)/(app)/(orders)/view-order/_components/ContactInfo/ContactInfo.tsx
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/ContactInfo/ContactInfo.tsx
rename to app/(pages)/(app)/(orders)/view-order/_components/ContactInfo/ContactInfo.tsx
diff --git a/app/(pages)/(app)/orders/view-order/_components/CreditCardPayment/CreditCardPayment.module.scss b/app/(pages)/(app)/(orders)/view-order/_components/CreditCardPayment/CreditCardPayment.module.scss
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/CreditCardPayment/CreditCardPayment.module.scss
rename to app/(pages)/(app)/(orders)/view-order/_components/CreditCardPayment/CreditCardPayment.module.scss
diff --git a/app/(pages)/(app)/orders/view-order/_components/CreditCardPayment/CreditCardPayment.tsx b/app/(pages)/(app)/(orders)/view-order/_components/CreditCardPayment/CreditCardPayment.tsx
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/CreditCardPayment/CreditCardPayment.tsx
rename to app/(pages)/(app)/(orders)/view-order/_components/CreditCardPayment/CreditCardPayment.tsx
diff --git a/app/(pages)/(app)/orders/view-order/_components/Navigator/Navigator.module.scss b/app/(pages)/(app)/(orders)/view-order/_components/Navigator/Navigator.module.scss
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/Navigator/Navigator.module.scss
rename to app/(pages)/(app)/(orders)/view-order/_components/Navigator/Navigator.module.scss
diff --git a/app/(pages)/(app)/orders/view-order/_components/Navigator/Navigator.tsx b/app/(pages)/(app)/(orders)/view-order/_components/Navigator/Navigator.tsx
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/Navigator/Navigator.tsx
rename to app/(pages)/(app)/(orders)/view-order/_components/Navigator/Navigator.tsx
diff --git a/app/(pages)/(app)/orders/view-order/_components/OrderDescription/OrderDescription.module.scss b/app/(pages)/(app)/(orders)/view-order/_components/OrderDescription/OrderDescription.module.scss
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/OrderDescription/OrderDescription.module.scss
rename to app/(pages)/(app)/(orders)/view-order/_components/OrderDescription/OrderDescription.module.scss
diff --git a/app/(pages)/(app)/orders/view-order/_components/OrderDescription/OrderDescription.tsx b/app/(pages)/(app)/(orders)/view-order/_components/OrderDescription/OrderDescription.tsx
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/OrderDescription/OrderDescription.tsx
rename to app/(pages)/(app)/(orders)/view-order/_components/OrderDescription/OrderDescription.tsx
diff --git a/app/(pages)/(app)/orders/view-order/_components/ProgessBar/ProgressBar.module.scss b/app/(pages)/(app)/(orders)/view-order/_components/ProgessBar/ProgressBar.module.scss
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/ProgessBar/ProgressBar.module.scss
rename to app/(pages)/(app)/(orders)/view-order/_components/ProgessBar/ProgressBar.module.scss
diff --git a/app/(pages)/(app)/orders/view-order/_components/ProgessBar/ProgressBar.tsx b/app/(pages)/(app)/(orders)/view-order/_components/ProgessBar/ProgressBar.tsx
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/ProgessBar/ProgressBar.tsx
rename to app/(pages)/(app)/(orders)/view-order/_components/ProgessBar/ProgressBar.tsx
diff --git a/app/(pages)/(app)/orders/view-order/_components/ShippingLabel/ShippingLabel.module.scss b/app/(pages)/(app)/(orders)/view-order/_components/ShippingLabel/ShippingLabel.module.scss
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/ShippingLabel/ShippingLabel.module.scss
rename to app/(pages)/(app)/(orders)/view-order/_components/ShippingLabel/ShippingLabel.module.scss
diff --git a/app/(pages)/(app)/orders/view-order/_components/ShippingLabel/ShippingLabel.tsx b/app/(pages)/(app)/(orders)/view-order/_components/ShippingLabel/ShippingLabel.tsx
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/_components/ShippingLabel/ShippingLabel.tsx
rename to app/(pages)/(app)/(orders)/view-order/_components/ShippingLabel/ShippingLabel.tsx
diff --git a/app/(pages)/(app)/orders/view-order/page.module.scss b/app/(pages)/(app)/(orders)/view-order/page.module.scss
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/page.module.scss
rename to app/(pages)/(app)/(orders)/view-order/page.module.scss
diff --git a/app/(pages)/(app)/orders/view-order/page.tsx b/app/(pages)/(app)/(orders)/view-order/page.tsx
similarity index 100%
rename from app/(pages)/(app)/orders/view-order/page.tsx
rename to app/(pages)/(app)/(orders)/view-order/page.tsx
diff --git a/app/(pages)/(app)/orders/order-cards/_components/OrderCard.module.scss b/app/(pages)/(app)/orders/order-cards/_components/OrderCard.module.scss
deleted file mode 100644
index 2423aaa..0000000
--- a/app/(pages)/(app)/orders/order-cards/_components/OrderCard.module.scss
+++ /dev/null
@@ -1,90 +0,0 @@
-@use 'media';
-
-.container {
- width: 60%;
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- padding: var(--small-spacer) var(--medium-spacer);
- background-color: var(--secondary);
- background-color: var(--background-secondary);
- color: var(--text-light);
- border-style: solid;
- border-width: 2px; // change from hard code
- border-color: var(--gray-100);
-
- @include media.tablet {
- flex-direction: column;
- gap: var(--small-spacer);
- }
-}
-
-.item_image {
- width: 185px;
- height: 140px;
- position: relative;
- border-style: solid;
- border-width: 1px;
- border-color: var(--gray-300);
-}
-
-.item_info {
- display: flex;
- flex-direction: column;
- gap: var(--tiny-spacer);
-
- h4 {
- font-size: 1.2rem;
- font-weight: 600;
- }
-}
-
-.item_status {
- width: 90%;
- padding: var(--tiny-spacer);
- display: flex;
- justify-content: space-between;
- position: relative;
-
- .status_bar {
- height: 2px;
- background-color: var(--gray-500);
- position: absolute;
- width: 80%;
- bottom: 65%;
- left: 10%;
- right: 10%;
- }
-
- .status_options {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: var(--tiny-spacer);
- position: relative;
-
- .status_circle {
- width: 32px;
- height: 32px;
- background-color: var(--background-secondary);
- border-style: solid;
- border-width: 1px;
- border-color: var(--gray-500);
- border-radius: 50%;
- }
-
- p {
- color: var(--gray-700);
- }
- }
-}
-
-.info_button {
- height: 47px;
- width: 47px;
- cursor: pointer;
- background-color: var(--purple);
- border-style: none;
- border-radius: 10%;
-}
\ No newline at end of file
diff --git a/app/(pages)/(app)/orders/order-cards/_components/OrderCard.tsx b/app/(pages)/(app)/orders/order-cards/_components/OrderCard.tsx
deleted file mode 100644
index 8820c35..0000000
--- a/app/(pages)/(app)/orders/order-cards/_components/OrderCard.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-'use client';
-import styles from './OrderCard.module.scss';
-import Image from 'next/image';
-import arrowRight from '/public/icons/arrow_right_line.svg';
-import purpleCheck from '/public/icons/purple_check.svg';
-import issue from '/public/icons/yellow_error.svg';
-import error from '/public/icons/red_error.svg';
-import greenCheck from '/public/icons/green_check.svg';
-
-interface TextboxProps {
- title: string;
- date: Date;
- status: number;
- image: string;
- icon: number;
-}
-
-const OrderCard: React.FC = (props) => {
- const itemStatuses = [
- 'Customer',
- 'Payment',
- 'Shipping',
- 'Confirm',
- 'Delivered',
- ];
-
- function displayIcon(status: number, icon: number, index: number) {
- if (index < status) {
- return purpleCheck;
- } else if (index === status) {
- if (icon === 0) {
- return purpleCheck;
- } else if (icon === 1) {
- return issue;
- } else if (icon === 2) {
- return error;
- } else {
- return greenCheck;
- }
- } else {
- return '';
- }
- }
- return (
-
-
-
-
-
-
{props.title}
-
Order Placed: {props.date.toDateString()}
-
-
- {itemStatuses.map((status, index) => (
-
- {props.status <= 4 &&
- displayIcon(props.status, props.icon, index) !== '' ? (
-
- ) : (
-
- )}
-
{status}
-
- ))}
-
-
-
-
- );
-};
-
-export default OrderCard;
diff --git a/app/(pages)/(app)/orders/order-cards/page.module.scss b/app/(pages)/(app)/orders/order-cards/page.module.scss
deleted file mode 100644
index cfa24ed..0000000
--- a/app/(pages)/(app)/orders/order-cards/page.module.scss
+++ /dev/null
@@ -1,70 +0,0 @@
-@use 'media';
-
-.page_container {
- display: flex;
- flex-direction: column;
- background-color: var(--background-primary);
- padding: var(--small-spacer) var(--medium-spacer);
- gap: var(--small-spacer);
-
- h4 {
- font-weight: 600;
- padding: var(--small-spacer) 0;
- }
-}
-
-.order_form {
- width: 60%;
- display: grid;
- grid-template-columns: 70% 30%;
-}
-
-.order_cards {
- display: flex;
- flex-direction: column;
- gap: var(--small-spacer);
-}
-
-.searchbar {
- display: flex;
- justify-self: start;
- width: 100%;
- align-self: center;
-
- .search {
- display: inline-block;
- padding: var(--tiny-spacer);
- font-size: 1rem;
- border: 1px solid var(--gray-500);
- border-radius: 5px 0 0 5px;
- width: 100%;
- outline: none;
- }
-
- .search_submit {
- display: inline-block;
- padding: var(--tiny-spacer);
- font-size: 1rem;
- border: 1px solid var(--gray-500);
- border-left: none;
- border-radius: 0 5px 5px 0;
- background-color: var(--purple);
- color: var(--text-light);
- cursor: pointer;
- }
-}
-
-.filter {
- justify-self: end;
- align-self: center;
- width: 80%;
-
- select {
- padding: var(--tiny-spacer);
- font-size: 1rem;
- width: 100%;
- border-radius: 5px;
- border-color: var(--gray-500);
- outline: none;
- }
-}
\ No newline at end of file
diff --git a/app/(pages)/(app)/orders/order-cards/page.tsx b/app/(pages)/(app)/orders/order-cards/page.tsx
deleted file mode 100644
index 6cb6d55..0000000
--- a/app/(pages)/(app)/orders/order-cards/page.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import OrderCard from './_components/OrderCard';
-import styles from './page.module.scss';
-
-export default function ViewOrderCards() {
- const dateTime = new Date('2024-03-01T10:36:01.516Z');
- const progressList = [
- {
- name: 'Lightweight Water-Resistant Hooded Puffer Coat',
- date: dateTime,
- status: 3,
- image: '/sample-product/puffer.png',
- icon: 0,
- },
- {
- name: 'Lightweight Water-Resistant Hooded Puffer Coat',
- date: dateTime,
- status: 1,
- image: '/sample-product/watch.png',
- icon: 2,
- },
- {
- name: 'Lightweight Water-Resistant Hooded Puffer Coat',
- date: dateTime,
- status: 1,
- image: '/sample-product/sneaker.png',
- icon: 1,
- },
- {
- name: 'Lightweight Water-Resistant Hooded Puffer Coat',
- date: dateTime,
- status: 1,
- image: '/sample-product/plant.png',
- icon: 0,
- },
- ];
- const deliveredList = [
- {
- name: 'Lightweight Water-Resistant Hooded Puffer Coat',
- date: dateTime,
- status: 4,
- image: '/sample-product/cap.png',
- icon: 3,
- },
- {
- name: 'Lightweight Water-Resistant Hooded Puffer Coat',
- date: dateTime,
- status: 4,
- image: '/sample-product/bear.png',
- icon: 3,
- },
- {
- name: 'Lightweight Water-Resistant Hooded Puffer Coat',
- date: dateTime,
- status: 4,
- image: '/sample-product/hydro.png',
- icon: 3,
- },
- ];
- return (
-
-
-
-
Progress
-
- {progressList.map((item, index) => (
-
-
-
- ))}
-
-
-
-
Past Orders
-
- {deliveredList.map((item, index) => (
-
-
-
- ))}
-
-
-
- );
-}
diff --git a/app/(pages)/_components/Sidebar/Sidebar.module.scss b/app/(pages)/_components/Sidebar/Sidebar.module.scss
index de85dc4..c92d9dd 100644
--- a/app/(pages)/_components/Sidebar/Sidebar.module.scss
+++ b/app/(pages)/_components/Sidebar/Sidebar.module.scss
@@ -6,7 +6,7 @@
left: 0;
height: 100vh;
width: fit-content;
- background-color: whitesmoke;
+ background-color: white;
.header {
display: flex;
diff --git a/app/(pages)/_components/Sidebar/Sidebar.tsx b/app/(pages)/_components/Sidebar/Sidebar.tsx
index 1fb9596..c99ead7 100644
--- a/app/(pages)/_components/Sidebar/Sidebar.tsx
+++ b/app/(pages)/_components/Sidebar/Sidebar.tsx
@@ -1,12 +1,13 @@
'use client';
import { useState } from 'react';
+import { usePathname } from 'next/navigation';
import Link from 'next/link';
-import styles from './Sidebar.module.scss';
-import { CgProfile } from 'react-icons/cg';
-import { IoPricetagOutline } from 'react-icons/io5';
-import { FiShoppingCart } from 'react-icons/fi';
+import { FiEdit, FiShoppingCart } from 'react-icons/fi';
+import { RiFileList3Line } from 'react-icons/ri';
+
+import styles from './Sidebar.module.scss';
interface NavLink {
name: string;
@@ -16,24 +17,25 @@ interface NavLink {
const navLinks: NavLink[] = [
{
- name: 'Profile',
- slug: '/profile/account-settings',
- icon: ,
+ name: 'Orders',
+ slug: '/',
+ icon: ,
},
{
- name: 'Product',
+ name: 'Product Listings',
slug: '/products',
- icon: ,
+ icon: ,
},
{
- name: 'Order',
- slug: '/orders',
- icon: ,
+ name: 'Edit Store',
+ slug: '/profile/account-settings',
+ icon: ,
},
];
export default function Sidebar() {
- const [selectedSlug, setSelectedSlug] = useState(null);
+ const pathname = usePathname();
+ const [selectedSlug, setSelectedSlug] = useState(pathname);
const handleSelect = (slug: string) => {
setSelectedSlug(slug);
diff --git a/app/(pages)/_globals/styles/colors.scss b/app/(pages)/_globals/styles/colors.scss
index 3f5c1e3..a176e35 100644
--- a/app/(pages)/_globals/styles/colors.scss
+++ b/app/(pages)/_globals/styles/colors.scss
@@ -5,18 +5,23 @@
--blue: #0D6EFD;
--light-purple: #DBADED;
- --background-primary: #F4F2FA;
+ --error-light: #FFEDED;
+ --error-dark: #DE5D4B;
+
+ --background-primary: #ede8fc;
--background-secondary: #FFFFFF;
--background-tertiary: #DEE2E7;
--background-upload: #F9F9F9;
--purple-highlight: #7B61FF;
--text-dark: #1C1C1C;
+ --gray-800: #333333;
--gray-700: #4E5566;
--gray-600: #6E7485;
--gray-500: #8B96A5;
--gray-400: #CFCFCF;
--gray-300: #DEE2E7;
+ --gray-200: #DEE2E7;
--gray-100: #E9EAF0;
--text-light: white;
diff --git a/prisma/migrations/20250710064028_order_status/migration.sql b/prisma/migrations/20250710064028_order_status/migration.sql
new file mode 100644
index 0000000..d209377
--- /dev/null
+++ b/prisma/migrations/20250710064028_order_status/migration.sql
@@ -0,0 +1,12 @@
+/*
+ Warnings:
+
+ - Changed the type of `status` on the `Order` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
+
+*/
+-- CreateEnum
+CREATE TYPE "OrderStatus" AS ENUM ('PENDING', 'ORDERED', 'SHIPPED', 'IN_TRANSIT', 'DELIVERED', 'CANCELLED', 'REFUNDED');
+
+-- AlterTable
+ALTER TABLE "Order" DROP COLUMN "status",
+ADD COLUMN "status" "OrderStatus" NOT NULL;
diff --git a/prisma/migrations/20250710200308_order_cancellation_status/migration.sql b/prisma/migrations/20250710200308_order_cancellation_status/migration.sql
new file mode 100644
index 0000000..a672f1c
--- /dev/null
+++ b/prisma/migrations/20250710200308_order_cancellation_status/migration.sql
@@ -0,0 +1,20 @@
+/*
+ Warnings:
+
+ - The values [CANCELLED,REFUNDED] on the enum `OrderStatus` will be removed. If these variants are still used in the database, this will fail.
+
+*/
+-- CreateEnum
+CREATE TYPE "CancellationStatus" AS ENUM ('CANCELLED', 'REFUNDED');
+
+-- AlterEnum
+BEGIN;
+CREATE TYPE "OrderStatus_new" AS ENUM ('PENDING', 'ORDERED', 'SHIPPED', 'IN_TRANSIT', 'DELIVERED');
+ALTER TABLE "Order" ALTER COLUMN "status" TYPE "OrderStatus_new" USING ("status"::text::"OrderStatus_new");
+ALTER TYPE "OrderStatus" RENAME TO "OrderStatus_old";
+ALTER TYPE "OrderStatus_new" RENAME TO "OrderStatus";
+DROP TYPE "OrderStatus_old";
+COMMIT;
+
+-- AlterTable
+ALTER TABLE "Order" ADD COLUMN "cancellation_status" "CancellationStatus";
diff --git a/prisma/schema/Order.prisma b/prisma/schema/Order.prisma
index d599fd2..5e8b919 100644
--- a/prisma/schema/Order.prisma
+++ b/prisma/schema/Order.prisma
@@ -1,5 +1,5 @@
model Order {
- id Int @id @default(autoincrement())
+ id Int @id @default(autoincrement())
paymentIntentId String?
total Float
customer_name String
@@ -15,7 +15,21 @@ model Order {
shipping_city String
shipping_zip String
shipping_country String
- status String
+ status OrderStatus
+ cancellation_status CancellationStatus?
created_at DateTime
products ProductToOrder[]
}
+
+enum OrderStatus {
+ PENDING
+ ORDERED
+ SHIPPED
+ IN_TRANSIT
+ DELIVERED
+}
+
+enum CancellationStatus {
+ CANCELLED
+ REFUNDED
+}