"feat: enhance invoice management with schema updates, seed data, and new APIs"
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
// src/app/api/product/saveProduct/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// save product to db by id
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import prisma from '../../../lib/prisma';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* PUT - change order status
|
||||
*************************************** */
|
||||
export async function PUT(req: NextRequest) {
|
||||
// logger('[Invoice] list', products.length);
|
||||
const { searchParams } = req.nextUrl;
|
||||
const invoiceId = searchParams.get('invoiceId');
|
||||
|
||||
// RULES: invoiceId must exist
|
||||
if (!invoiceId) {
|
||||
return response({ message: 'Invoice ID is required!' }, STATUS.BAD_REQUEST);
|
||||
}
|
||||
|
||||
const { data } = await req.json();
|
||||
|
||||
try {
|
||||
const order = await prisma.invoiceItem.updateMany({
|
||||
where: { id: invoiceId },
|
||||
data: { status: data.status },
|
||||
});
|
||||
|
||||
return response({ order }, STATUS.OK);
|
||||
} catch (error) {
|
||||
console.log({ data });
|
||||
return handleError('Invoice - Get list', error);
|
||||
}
|
||||
}
|
||||
|
||||
export type IProductItem = {
|
||||
id: string;
|
||||
sku: string;
|
||||
name: string;
|
||||
code: string;
|
||||
price: number;
|
||||
taxes: number;
|
||||
tags: string[];
|
||||
sizes: string[];
|
||||
publish: string;
|
||||
gender: string[];
|
||||
coverUrl: string;
|
||||
images: string[];
|
||||
colors: string[];
|
||||
quantity: number;
|
||||
category: string;
|
||||
available: number;
|
||||
totalSold: number;
|
||||
description: string;
|
||||
totalRatings: number;
|
||||
totalReviews: number;
|
||||
// createdAt: IDateValue;
|
||||
inventoryType: string;
|
||||
subDescription: string;
|
||||
priceSale: number | null;
|
||||
// reviews: IProductReview[];
|
||||
newLabel: {
|
||||
content: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
saleLabel: {
|
||||
content: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
ratings: {
|
||||
name: string;
|
||||
starCount: number;
|
||||
reviewCount: number;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type IDateValue = string | number | null;
|
||||
|
||||
export type IProductReview = {
|
||||
id: string;
|
||||
name: string;
|
||||
rating: number;
|
||||
comment: string;
|
||||
helpful: number;
|
||||
avatarUrl: string;
|
||||
postedAt: IDateValue;
|
||||
isPurchased: boolean;
|
||||
attachments?: string[];
|
||||
};
|
@@ -0,0 +1,9 @@
|
||||
###
|
||||
|
||||
PUT http://localhost:7272/api/invoice/changeStatus?orderId=1
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"data":{"status": "helloworld"}
|
||||
}
|
||||
|
@@ -0,0 +1,53 @@
|
||||
// src/app/api/user/createUser/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// create user to db
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
//
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import prisma from '../../../lib/prisma';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
***************************************
|
||||
* POST - create User
|
||||
***************************************
|
||||
*/
|
||||
export async function POST(req: NextRequest) {
|
||||
// logger('[User] list', users.length);
|
||||
const { data } = await req.json();
|
||||
const createForm: CreateUserData = data as unknown as CreateUserData;
|
||||
|
||||
try {
|
||||
const user = await prisma.userItem.create({ data: createForm });
|
||||
return response({ user }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('User - Create', error);
|
||||
}
|
||||
}
|
||||
|
||||
type CreateUserData = {
|
||||
name: string;
|
||||
city: string;
|
||||
role: string;
|
||||
email: string;
|
||||
state: string;
|
||||
status: string;
|
||||
address: string;
|
||||
country: string;
|
||||
zipCode: string;
|
||||
company: string;
|
||||
avatarUrl: string;
|
||||
phoneNumber: string;
|
||||
isVerified: boolean;
|
||||
//
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
@@ -0,0 +1,4 @@
|
||||
###
|
||||
|
||||
POST http://localhost:7272/api/user/createUser
|
||||
|
@@ -0,0 +1,47 @@
|
||||
// src/app/api/product/deleteUser/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// delete product from db by id
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { logger } from 'src/utils/logger';
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import prisma from '../../../lib/prisma';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* handle Delete Users
|
||||
*************************************** */
|
||||
export async function DELETE(req: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = req.nextUrl;
|
||||
|
||||
// RULES: userId must exist
|
||||
const userId = searchParams.get('userId');
|
||||
if (!userId) {
|
||||
return response({ message: 'User ID is required!' }, STATUS.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// NOTE: userId confirmed exist, run below
|
||||
const user = await prisma.userItem.delete({
|
||||
//
|
||||
where: { id: userId },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return response({ message: 'User not found!' }, STATUS.NOT_FOUND);
|
||||
}
|
||||
|
||||
logger('[User] details', user.id);
|
||||
|
||||
return response({ user }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('User - Get details', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
###
|
||||
|
||||
DELETE http://localhost:7272/api/user/deleteUser?userId=3f431e6f-ad05-4d60-9c25-6a7e92a954ad
|
47
03_source/cms_backend/src/app/api/invoice/details/route.ts
Normal file
47
03_source/cms_backend/src/app/api/invoice/details/route.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
// src/app/api/invoice/details/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// read invoice from db by id
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { logger } from 'src/utils/logger';
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import prisma from '../../../lib/prisma';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* GET Invoice detail
|
||||
*************************************** */
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = req.nextUrl;
|
||||
|
||||
// RULES: invoiceId must exist
|
||||
const invoiceId = searchParams.get('invoiceId');
|
||||
if (!invoiceId) {
|
||||
return response({ message: 'invoiceId is required!' }, STATUS.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// NOTE: invoiceId confirmed exist, run below
|
||||
const invoice = await prisma.invoiceItem.findFirst({
|
||||
// include: { reviews: true },
|
||||
where: { id: invoiceId.toString() },
|
||||
});
|
||||
|
||||
if (!invoice) {
|
||||
return response({ message: 'Invoice not found!' }, STATUS.NOT_FOUND);
|
||||
}
|
||||
|
||||
logger('[Invoice] details', invoice.id);
|
||||
|
||||
return response({ invoice }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('Product - Get details', error);
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
###
|
||||
|
||||
GET http://localhost:7272/api/invoice/details?invoiceId=1
|
||||
|
@@ -0,0 +1,30 @@
|
||||
// src/app/api/product/image/upload/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// handle upload product image
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
// import prisma from '../../../lib/prisma';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* GET - Products
|
||||
*************************************** */
|
||||
export async function POST(req: NextRequest) {
|
||||
try {
|
||||
const { data } = await req.json();
|
||||
console.log('helloworld');
|
||||
|
||||
return response({ hello: 'world' }, STATUS.OK);
|
||||
} catch (error) {
|
||||
console.log({ hello: 'world' });
|
||||
return handleError('Product - store product image', error);
|
||||
}
|
||||
}
|
22
03_source/cms_backend/src/app/api/invoice/list/route.ts
Normal file
22
03_source/cms_backend/src/app/api/invoice/list/route.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
// src/app/api/invoice/list/route.ts
|
||||
import { logger } from 'src/utils/logger';
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import prisma from '../../../lib/prisma';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* GET - InvoiceItem
|
||||
*************************************** */
|
||||
export async function GET() {
|
||||
try {
|
||||
const invoices = await prisma.invoiceItem.findMany();
|
||||
|
||||
logger('[Invoice] list', invoices.length);
|
||||
|
||||
return response({ invoices }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('InvoiceItem - Get list', error);
|
||||
}
|
||||
}
|
3
03_source/cms_backend/src/app/api/invoice/list/test.http
Normal file
3
03_source/cms_backend/src/app/api/invoice/list/test.http
Normal file
@@ -0,0 +1,3 @@
|
||||
###
|
||||
|
||||
GET http://localhost:7272/api/invoice/list
|
@@ -0,0 +1,98 @@
|
||||
// src/app/api/invoice/saveInvoice/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// save invoice to db by id
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import prisma from '../../../lib/prisma';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* PUT - Update invoice
|
||||
*************************************** */
|
||||
export async function PUT(req: NextRequest) {
|
||||
// logger('[Invoice] list', invoices.length);
|
||||
const { searchParams } = req.nextUrl;
|
||||
const invoiceId = searchParams.get('invoiceId');
|
||||
|
||||
// RULES: invoiceId must exist
|
||||
if (!invoiceId) {
|
||||
return response({ message: 'Invoice ID is required!' }, STATUS.BAD_REQUEST);
|
||||
}
|
||||
|
||||
const { data } = await req.json();
|
||||
|
||||
try {
|
||||
const invoice = await prisma.invoiceItem.updateMany({
|
||||
where: { id: invoiceId },
|
||||
data,
|
||||
});
|
||||
|
||||
return response({ invoice }, STATUS.OK);
|
||||
} catch (error) {
|
||||
console.log({ data });
|
||||
return handleError('Invoice - Update invoice', error);
|
||||
}
|
||||
}
|
||||
|
||||
export type IInvoiceItem = {
|
||||
id: string;
|
||||
sku: string;
|
||||
name: string;
|
||||
code: string;
|
||||
price: number;
|
||||
taxes: number;
|
||||
tags: string[];
|
||||
sizes: string[];
|
||||
publish: string;
|
||||
gender: string[];
|
||||
coverUrl: string;
|
||||
images: string[];
|
||||
colors: string[];
|
||||
quantity: number;
|
||||
category: string;
|
||||
available: number;
|
||||
totalSold: number;
|
||||
description: string;
|
||||
totalRatings: number;
|
||||
totalReviews: number;
|
||||
// createdAt: IDateValue;
|
||||
inventoryType: string;
|
||||
subDescription: string;
|
||||
priceSale: number | null;
|
||||
// reviews: IInvoiceReview[];
|
||||
newLabel: {
|
||||
content: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
saleLabel: {
|
||||
content: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
ratings: {
|
||||
name: string;
|
||||
starCount: number;
|
||||
reviewCount: number;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type IDateValue = string | number | null;
|
||||
|
||||
export type IInvoiceReview = {
|
||||
id: string;
|
||||
name: string;
|
||||
rating: number;
|
||||
comment: string;
|
||||
helpful: number;
|
||||
avatarUrl: string;
|
||||
postedAt: IDateValue;
|
||||
isPurchased: boolean;
|
||||
attachments?: string[];
|
||||
};
|
@@ -0,0 +1,58 @@
|
||||
###
|
||||
|
||||
PUT http://localhost:7272/api/invoice/saveInvoice?invoiceId=1
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"data": {
|
||||
"id": "1",
|
||||
"taxes": 10,
|
||||
"status": "paid",
|
||||
"discount": 10,
|
||||
"shipping": 10,
|
||||
"subtotal": 921.14,
|
||||
"totalAmount": 993.254,
|
||||
"items": [
|
||||
{
|
||||
"title": "Urban Explorer Sneakers 1111",
|
||||
"service": "Technology",
|
||||
"quantity": 11,
|
||||
"price": 83.74,
|
||||
"total": 921.14,
|
||||
"description": "The sun slowly set over the horizon, painting the sky in vibrant hues of orange and pink."
|
||||
},
|
||||
{
|
||||
"price": 83.74,
|
||||
"title": "Urban Explorer Sneakers 22222",
|
||||
"total": 921.14,
|
||||
"service": "Technology",
|
||||
"quantity": 11,
|
||||
"description": "The sun slowly set over the horizon, painting the sky in vibrant hues of orange and pink."
|
||||
}
|
||||
],
|
||||
"invoiceNumber": "INV-1991",
|
||||
"invoiceFrom": {
|
||||
"id": "e99f09a7-dd88-49d5-b1c8-1daf80c2d7b02",
|
||||
"name": "Lucian Obrien",
|
||||
"email": "milo.farrell@hotmail.com",
|
||||
"company": "Nikolaus - Leuschke",
|
||||
"primary": false,
|
||||
"addressType": "Office",
|
||||
"fullAddress": "1147 Rohan Drive Suite 819 - Burlington, VT / 82021",
|
||||
"phoneNumber": "+1 416-555-0198"
|
||||
},
|
||||
"invoiceTo": {
|
||||
"id": "e99f09a7-dd88-49d5-b1c8-1daf80c2d7b03",
|
||||
"name": "Deja Brady",
|
||||
"email": "violet.ratke86@yahoo.com",
|
||||
"company": "Hegmann, Kreiger and Bayer",
|
||||
"primary": false,
|
||||
"addressType": "Office",
|
||||
"fullAddress": "18605 Thompson Circle Apt. 086 - Idaho Falls, WV / 50337",
|
||||
"phoneNumber": "+44 20 7946 0958"
|
||||
},
|
||||
"sent": 10,
|
||||
"createdDate": "2025-06-15T17:07:24+08:00",
|
||||
"dueDate": "2025-06-15T17:07:24+08:00"
|
||||
}
|
||||
}
|
37
03_source/cms_backend/src/app/api/invoice/search/route.ts
Normal file
37
03_source/cms_backend/src/app/api/invoice/search/route.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { logger } from 'src/utils/logger';
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import { _products } from 'src/_mock/_product';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
/** **************************************
|
||||
* GET - Search products
|
||||
*************************************** */
|
||||
export async function GET(req: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = req.nextUrl;
|
||||
const query = searchParams.get('query')?.trim().toLowerCase();
|
||||
|
||||
if (!query) {
|
||||
return response({ results: [] }, STATUS.OK);
|
||||
}
|
||||
|
||||
const products = _products();
|
||||
|
||||
// Accept search by name or sku
|
||||
const results = products.filter(
|
||||
({ name, sku }) => name.toLowerCase().includes(query) || sku?.toLowerCase().includes(query)
|
||||
);
|
||||
|
||||
logger('[Product] search-results', results.length);
|
||||
|
||||
return response({ results }, STATUS.OK);
|
||||
} catch (error) {
|
||||
return handleError('Product - Get search', error);
|
||||
}
|
||||
}
|
@@ -66,9 +66,9 @@ export async function POST(req: NextRequest) {
|
||||
where: { id: data.id },
|
||||
});
|
||||
|
||||
return response({ hello: 'world', data }, STATUS.OK);
|
||||
return response({ data }, STATUS.OK);
|
||||
} catch (error) {
|
||||
console.log({ hello: 'world', data });
|
||||
console.log({ data });
|
||||
return handleError('Product - Get list', error);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user