Compare commits

..

7 Commits

1347 changed files with 11713 additions and 17746 deletions

View File

@@ -1,9 +0,0 @@
---
tags: frontend, side-menu
---
# REQ0181 frontend side menu configuration
## sources
`src/layouts/nav-config-dashboard.tsx`

View File

@@ -1,11 +0,0 @@
---
tags: frontend, product, details
---
# REQ0182 frontend product details
frontend page to display product details
## sources
T.B.A.

View File

@@ -1,15 +0,0 @@
---
tags: frontend, product, new
---
# REQ0183 frontend product new
frontend page to create new product
## sources
T.B.A.
## branch
develop/requirements/REQ0183

View File

@@ -1,17 +0,0 @@
---
tags: frontend, product, delete
---
# REQ0184 frontend product delete
frontend page to delete product
list page
## sources
T.B.A.
## branch
develop/requirements/REQ0184

View File

@@ -1,18 +0,0 @@
---
tags: frontend, party-event
---
# REQ0185 frontend party-event
frontend page to handle party-event (CRUD)
edit page T.B.A.
## sources
T.B.A.
## branch
develop/frontend/party-event/trunk
develop/requirements/REQ0185

View File

@@ -1,17 +0,0 @@
---
tags: frontend, product, update
---
# REQ0185 frontend product update
frontend page to update product
edit page T.B.A.
## sources
T.B.A.
## branch
develop/requirements/REQ0185

View File

@@ -1,23 +0,0 @@
---
tags: frontend, party-order
---
# REQ0185 frontend party-order
frontend page to handle party-order (CRUD)
edit page T.B.A.
## TODO
- remove detail in left nav bar
- implement `changeStatus`
## sources
T.B.A.
## branch
develop/frontend/party-order/trunk
develop/requirements/REQ0187

View File

@@ -1,20 +0,0 @@
---
tags: frontend, party-user
---
# REQ0188 frontend party-user
frontend page to handle party-user (CRUD)
edit page T.B.A.
## TODO
## sources
T.B.A.
## branch
develop/requirements/REQ0188
develop/frontend/party-user/trunk

View File

@@ -158,8 +158,3 @@
- [REQ0169: DemoStorageExample](./REQ0169/index.md) - [REQ0169: DemoStorageExample](./REQ0169/index.md)
- [REQ0170: DemoWeatherAppUi](./REQ0170/index.md) - [REQ0170: DemoWeatherAppUi](./REQ0170/index.md)
- [REQ0180: REQ0180 service port schedule](./REQ0180/index.md) - [REQ0180: REQ0180 service port schedule](./REQ0180/index.md)
- [REQ0181: REQ0181 frontend side menu configuration](./REQ0181/index.md)
- [REQ0182: REQ0182 frontend product details](./REQ0182/index.md)
- [REQ0183: REQ0183 frontend product new](./REQ0183/index.md)
- [REQ0184: REQ0184 frontend product delete](./REQ0184/index.md)
- [REQ0185: REQ0185 frontend product update](./REQ0185/index.md)

View File

@@ -1,7 +1,3 @@
**/*del
**/*bak
**/*copy*
# Logs # Logs
logs logs
*.log *.log

View File

@@ -1,14 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
npm i -D
clear
while true; do while true; do
npm run dev yarn --dev
yarn dev
killall node
killall yarn
echo "restarting..." echo "restarting..."
sleep 1 sleep 1

View File

@@ -6,7 +6,6 @@ RUN npm install -g pnpm
RUN apt-get update -y RUN apt-get update -y
RUN apt-get install -y openssl RUN apt-get install -y openssl
RUN apt-get install -qqy psmisc
# Set working directory # Set working directory
WORKDIR /app WORKDIR /app

View File

@@ -5,7 +5,6 @@
"description": "Mock server & assets", "description": "Mock server & assets",
"private": true, "private": true,
"scripts": { "scripts": {
"dev:check": "yarn tsc:w",
"dev": "next dev -p 7272 -H 0.0.0.0", "dev": "next dev -p 7272 -H 0.0.0.0",
"start": "next start -p 7272 -H 0.0.0.0", "start": "next start -p 7272 -H 0.0.0.0",
"build": "next build", "build": "next build",
@@ -20,10 +19,8 @@
"re:build": "yarn clean && yarn install && yarn build", "re:build": "yarn clean && yarn install && yarn build",
"re:build-npm": "npm run clean && npm install && npm run build", "re:build-npm": "npm run clean && npm install && npm run build",
"tsc:dev": "yarn dev & yarn tsc:watch", "tsc:dev": "yarn dev & yarn tsc:watch",
"tsc:print": "npx tsc --showConfig",
"tsc:w": "npx nodemon --delay 1 --ext ts,tsx --exec \"yarn tsc\"",
"tsc:watch": "tsc --noEmit --watch", "tsc:watch": "tsc --noEmit --watch",
"tsc": "tsc --noEmit", "tsc:print": "npx tsc --showConfig",
"migrate": "npx prisma migrate dev --skip-seed", "migrate": "npx prisma migrate dev --skip-seed",
"seed": "tsx ./prisma/seed.ts", "seed": "tsx ./prisma/seed.ts",
"seed:w": "npx nodemon --ext \"ts,tsx,json\" -w prisma --exec \"yarn seed\"", "seed:w": "npx nodemon --ext \"ts,tsx,json\" -w prisma --exec \"yarn seed\"",
@@ -32,9 +29,7 @@
"db:push": "prisma db push --force-reset", "db:push": "prisma db push --force-reset",
"db:push:w": "npx nodemon --delay 1 --watch prisma --ext \"ts,tsx,prisma\" --exec \"yarn db:push && yarn seed\"", "db:push:w": "npx nodemon --delay 1 --watch prisma --ext \"ts,tsx,prisma\" --exec \"yarn db:push && yarn seed\"",
"db:studio": "prisma studio", "db:studio": "prisma studio",
"db:studio:w": "npx nodemon --delay 1 --watch prisma --ext \"prisma\" --exec \"yarn db:studio\"", "db:studio:w": "npx nodemon --delay 1 --watch prisma --ext \"prisma\" --exec \"yarn db:studio\""
"db:dev": "yarn db:push && yarn seed && yarn dev",
"db:dev:w": "npx nodemon --delay 3 --ext \"ts,tsx,prisma\" --exec \"yarn db:dev\""
}, },
"engines": { "engines": {
"node": ">=20" "node": ">=20"
@@ -48,7 +43,6 @@
"@next-auth/prisma-adapter": "^1.0.7", "@next-auth/prisma-adapter": "^1.0.7",
"@prisma/adapter-pg": "^6.8.2", "@prisma/adapter-pg": "^6.8.2",
"@prisma/client": "^5.6.0", "@prisma/client": "^5.6.0",
"@types/bcrypt": "^5.0.2",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
@@ -71,7 +65,6 @@
"@types/react": "^18.3.20", "@types/react": "^18.3.20",
"@types/react-dom": "^18.3.5", "@types/react-dom": "^18.3.5",
"@typescript-eslint/parser": "^8.28.0", "@typescript-eslint/parser": "^8.28.0",
"concurrently": "^9.1.2",
"eslint": "^9.23.0", "eslint": "^9.23.0",
"eslint-import-resolver-typescript": "^4.2.2", "eslint-import-resolver-typescript": "^4.2.2",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",

View File

@@ -31,21 +31,17 @@ model Account {
oauth_token_secret String? oauth_token_secret String?
oauth_token String? oauth_token String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
PartyUser PartyUser? @relation(fields: [partyUserId], references: [id])
partyUserId String?
@@unique([provider, providerAccountId]) @@unique([provider, providerAccountId])
} }
model Session { model Session {
id String @id @default(cuid()) id String @id @default(cuid())
sessionToken String @unique @map("session_token") sessionToken String @unique @map("session_token")
userId String @map("user_id") userId String @map("user_id")
expires DateTime expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade)
PartyUser PartyUser? @relation(fields: [partyUserId], references: [id])
partyUserId String?
} }
model User { model User {
@@ -199,32 +195,32 @@ model ProductItem {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// //
available Int
category String
code String
colors String[]
coverUrl String
description String
gender String[]
images String[]
inventoryType String
name String
newLabel Json
price Float
priceSale Float?
publish String
quantity Int
ratings Json[]
reviews ProductReview[]
saleLabel Json
sizes String[]
sku String sku String
subDescription String name String
tags String[] code String
price Float
taxes Float taxes Float
tags String[]
sizes String[]
publish String
gender String[]
coverUrl String
images String[]
colors String[]
quantity Int
category String
available Int
totalSold Int
description String
totalRatings Float totalRatings Float
totalReviews Int totalReviews Int
totalSold Int inventoryType String
subDescription String
priceSale Float?
newLabel Json
saleLabel Json
ratings Json[]
reviews ProductReview[]
} }
model MailSender { model MailSender {
@@ -1148,62 +1144,49 @@ model EventReview {
eventItemId String? eventItemId String?
} }
// NOTE: need to consider with Event
// mapped to IEventItem // mapped to IEventItem
// a.k.a. PartyEvent party-event
model EventItem { model EventItem {
id String @id @default(uuid()) id String @id @default(uuid())
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
// //
available Int @default(99) sku String
category String name String
code String @default("") code String
colors String[] price Float
coverUrl String taxes Float
description String tags String[]
gender String[] sizes String[]
images String[] publish String
inventoryType String @default("") gender String[]
name String @default("") coverUrl String
newLabel Json @default("{}") images String[]
price Float @default(999.9) colors String[]
priceSale Float? @default(111.1) quantity Int
publish String @default("") category String
quantity Int @default(99) available Int
ratings Json[] totalSold Int
saleLabel Json @default("{}") description String
sizes String[] @default([""]) totalRatings Float
sku String @default("") totalReviews Int
subDescription String @default("") inventoryType String
tags String[] @default([""]) subDescription String
taxes Float @default(5.0) priceSale Float?
totalRatings Float @default(5.0) newLabel Json
totalReviews Int @default(10) saleLabel Json
totalSold Int @default(10) ratings Json[]
// //
ageBottom Float @default(-1) eventDate DateTime @default(now())
ageTop Float @default(-1) joinMembers Json[]
avatar String[] @default([""]) title String
currency String @default("HKD") currency String
capacity Int @default(10) duration_m Float
duration_m Float @default(180) ageBottom Float
endDate String? @default("") ageTop Float
eventDate DateTime @default(now()) location String
isFeatured Boolean @default(false) avatar String[]
joinMembers Json[] @default([])
location String @default("HK")
organizer String @default("")
registrationDeadline String @default("")
requirements String @default("")
schedule String @default("")
speakers String[] @default([])
sponsors String[] @default([])
startDate String? @default("")
status String? @default("")
title String @default("")
// //
reviews EventReview[] reviews EventReview[]
} }
model AppLog { model AppLog {
@@ -1234,60 +1217,3 @@ model AccessLog {
@@index([timestamp]) @@index([timestamp])
@@index([userId]) @@index([userId])
} }
model PartyOrderItem {
id String @id @default(uuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
//
taxes Float
status String
shipping Float
discount Float
subtotal Float
orderNumber String
totalAmount Float
totalQuantity Float
history Json
payment Json
customer Json
delivery Json
items Json[]
shippingAddress Json
// OrderProductItem OrderProductItem[]
// OrderHistory OrderHistory[]
// OrderDelivery OrderDelivery[]
// OrderCustomer OrderCustomer[]
// OrderPayment OrderPayment[]
// OrderShippingAddress OrderShippingAddress[]
}
model PartyUser {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
//
username String? @unique
password String?
//
name String?
email String @unique
emailVerified DateTime?
avatarUrl String?
bucketImage String?
admin Boolean @default(false)
accounts Account[]
sessions Session[]
info Json?
phoneNumber String @default("")
company String @default("")
status String @default("pending")
role String @default("")
isVerified Boolean @default(false)
//
country String @default("")
state String @default("")
city String @default("")
address String @default("")
zipCode String @default("")
}

View File

@@ -31,9 +31,6 @@ import { EventReviewSeed } from './seeds/eventReview';
import { appLogSeed } from './seeds/AppLog'; import { appLogSeed } from './seeds/AppLog';
import { accessLogSeed } from './seeds/AccessLog'; import { accessLogSeed } from './seeds/AccessLog';
import { userMetaSeed } from './seeds/userMeta'; import { userMetaSeed } from './seeds/userMeta';
//
import { partyOrderItemSeed } from './seeds/partyOrderItem';
import { partyUserSeed } from './seeds/partyUser';
// //
// import { Blog } from './seeds/blog'; // import { Blog } from './seeds/blog';
@@ -62,9 +59,6 @@ import { partyUserSeed } from './seeds/partyUser';
// //
await appLogSeed; await appLogSeed;
await accessLogSeed; await accessLogSeed;
//
await partyOrderItemSeed;
await partyUserSeed;
// await Blog; // await Blog;
// await Mail; // await Mail;

View File

@@ -125,7 +125,7 @@ const generateRatings = () =>
const generateImages = () => Array.from({ length: 8 }, (_, index) => _mock.image.event(index)); const generateImages = () => Array.from({ length: 8 }, (_, index) => _mock.image.event(index));
const _events = () => const _events = () =>
Array.from({ length: 5 }, (_, index) => { Array.from({ length: 2 }, (_, index) => {
const reviews = generateReviews(); const reviews = generateReviews();
const images = generateImages(); const images = generateImages();
const ratings = generateRatings(); const ratings = generateRatings();

View File

@@ -1,94 +0,0 @@
import { PrismaClient } from '@prisma/client';
import { _mock } from './_mock';
const prisma = new PrismaClient();
const ITEMS = Array.from({ length: 3 }, (_, index) => ({
id: _mock.id(index),
sku: `16H9UR${index}`,
quantity: index + 1,
name: _mock.productName(index),
coverUrl: _mock.image.product(index),
price: _mock.number.price(index),
}));
async function partyOrderItem() {
await prisma.partyOrderItem.deleteMany({});
for (let index = 1; index < 20 + 1; index++) {
const shipping = 10;
const discount = 10;
const taxes = 10;
const items = (index % 2 && ITEMS.slice(0, 1)) || (index % 3 && ITEMS.slice(1, 3)) || ITEMS;
const totalQuantity = items.reduce((accumulator, item) => accumulator + item.quantity, 0);
const subtotal = items.reduce((accumulator, item) => accumulator + item.price * item.quantity, 0);
const totalAmount = subtotal - shipping - discount + taxes;
const customer = {
id: _mock.id(index),
name: _mock.fullName(index),
email: _mock.email(index),
avatarUrl: _mock.image.avatar(index),
ipAddress: '192.158.1.38',
};
const delivery = { shipBy: 'DHL', speedy: 'Standard', trackingNumber: 'SPX037739199373' };
const history = {
orderTime: _mock.time(1),
paymentTime: _mock.time(2),
deliveryTime: _mock.time(3),
completionTime: _mock.time(4),
timeline: [
{ title: 'Delivery successful', time: _mock.time(1) },
{ title: 'Transporting to [2]', time: _mock.time(2) },
{ title: 'Transporting to [1]', time: _mock.time(3) },
{ title: 'The shipping unit has picked up the goods', time: _mock.time(4) },
{ title: 'Order has been created', time: _mock.time(5) },
],
};
const temp = await prisma.partyOrderItem.upsert({
where: { id: index.toString() },
update: {},
create: {
id: _mock.id(index),
orderNumber: `#601${index}`,
taxes,
items,
history,
subtotal: items.reduce((accumulator, item) => accumulator + item.price * item.quantity, 0),
shipping,
discount,
customer,
delivery,
totalAmount,
totalQuantity,
shippingAddress: {
fullAddress: '19034 Verna Unions Apt. 164 - Honolulu, RI / 87535',
phoneNumber: '365-374-4961',
},
payment: {
//
cardType: 'mastercard',
cardNumber: '4111 1111 1111 1111',
},
status: (index % 2 && 'completed') || (index % 3 && 'pending') || (index % 4 && 'cancelled') || 'refunded',
},
});
}
console.log('seed partyOrderItemSeed done');
}
const partyOrderItemSeed = partyOrderItem()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
export { partyOrderItemSeed };

View File

@@ -1,122 +0,0 @@
import { faker as enFaker } from '@faker-js/faker/locale/en_US';
import { faker as zhFaker } from '@faker-js/faker/locale/zh_CN';
import { faker as jaFaker } from '@faker-js/faker/locale/ja';
import { faker as koFaker } from '@faker-js/faker/locale/ko';
import { faker as twFaker } from '@faker-js/faker/locale/zh_TW';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const ROLE = [
`CEO`,
`CTO`,
`Project Coordinator`,
`Team Leader`,
`Software Developer`,
`Marketing Strategist`,
`Data Analyst`,
`Product Owner`,
`Graphic Designer`,
`Operations Manager`,
`Customer Support Specialist`,
`Sales Manager`,
`HR Recruiter`,
`Business Consultant`,
`Financial Planner`,
`Network Engineer`,
`Content Creator`,
`Quality Assurance Tester`,
`Public Relations Officer`,
`IT Administrator`,
`Compliance Officer`,
`Event Planner`,
`Legal Counsel`,
`Training Coordinator`,
];
const STATUS = ['active', 'pending', 'banned'];
async function partyUser() {
const alice = await prisma.partyUser.upsert({
where: { email: 'alice@prisma.io' },
update: {},
create: {
email: 'alice@prisma.io',
name: 'Alice',
username: 'pualice',
password: 'Aa12345678',
emailVerified: new Date(),
phoneNumber: '+85291234567',
company: 'helloworld company',
status: STATUS[0],
role: ROLE[0],
isVerified: true,
},
});
await prisma.partyUser.upsert({
where: { email: 'demo@minimals.cc' },
update: {},
create: {
email: 'demo@minimals.cc',
name: 'Demo',
username: 'pudemo',
password: '@2Minimal',
emailVerified: new Date(),
phoneNumber: '+85291234568',
company: 'helloworld company',
status: STATUS[1],
role: ROLE[1],
isVerified: true,
},
});
for (let i = 0; i < 5; i++) {
const CJK_LOCALES = {
en: enFaker,
zh: zhFaker,
ja: jaFaker,
ko: koFaker,
tw: twFaker,
};
function getRandomCJKFaker() {
const locales = Object.keys(CJK_LOCALES);
const randomKey = locales[Math.floor(Math.random() * locales.length)] as keyof typeof CJK_LOCALES;
return CJK_LOCALES[randomKey];
}
const randomFaker = getRandomCJKFaker();
await prisma.partyUser.upsert({
where: { email: `party_user${i}@prisma.io` },
update: {},
create: {
email: `party_user${i}@prisma.io`,
name: `Party Dummy ${i}`,
username: `pu${i.toString()}`,
password: 'Aa12345678',
emailVerified: new Date(),
phoneNumber: `+8529123456${i.toString()}`,
company: randomFaker.company.name(),
role: ROLE[Math.floor(Math.random() * ROLE.length)],
status: STATUS[Math.floor(Math.random() * STATUS.length)],
isVerified: true,
},
});
}
console.log('seed partyUser done');
}
const partyUserSeed = partyUser()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
export { partyUserSeed };

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env bash
yarn --dev
clear
while true; do
yarn db:studio &
npx nodemon --ext ts,tsx,prisma --exec "yarn dev"
# npx nodemon --ext ts,tsx,prisma --exec "yarn db:push && yarn seed && yarn dev"
# yarn dev
killall node
killall yarn
echo "restarting..."
sleep 1
done

View File

@@ -1,9 +0,0 @@
#!/usr/bin/env bash
yarn --dev
clear
yarn db:push && yarn seed
echo "done"

View File

@@ -1,20 +0,0 @@
#!/usr/bin/env bash
set -x
killall node
killall yarn
rm -rf ./**/*Zone.Identifier
# yarn db:push
# npx nodemon --ext ts,tsx --exec "yarn tsc"
set -ex
yarn fm:fix
yarn tsc
yarn build
echo "done"

View File

@@ -1,5 +0,0 @@
#!/usr/bin/env bash
set -x
npm run start

View File

@@ -16,7 +16,11 @@ export const _contacts = () =>
address: _mock.fullAddress(index), address: _mock.fullAddress(index),
avatarUrl: _mock.image.avatar(index), avatarUrl: _mock.image.avatar(index),
phoneNumber: _mock.phoneNumber(index), phoneNumber: _mock.phoneNumber(index),
status: ([0, 1, 6, 12].includes(index) && 'online') || ([3, 8, 14].includes(index) && 'offline') || ([4, 10, 16].includes(index) && 'busy') || 'always', status:
([0, 1, 6, 12].includes(index) && 'online') ||
([3, 8, 14].includes(index) && 'offline') ||
([4, 10, 16].includes(index) && 'busy') ||
'always',
})); }));
export const _conversations = () => { export const _conversations = () => {

View File

@@ -6,7 +6,8 @@ import { _tags } from './assets';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
const generateAttachments = () => Array.from({ length: 20 }, (_, index) => _mock.image.cover(index)); const generateAttachments = () =>
Array.from({ length: 20 }, (_, index) => _mock.image.cover(index));
const generateAssignees = () => const generateAssignees = () =>
Array.from({ length: 20 }, (_, index) => ({ Array.from({ length: 20 }, (_, index) => ({
@@ -67,14 +68,24 @@ const createTask = (index: number, status: string) => {
assignee: assignedUser.slice(0, index), assignee: assignedUser.slice(0, index),
description: _mock.description(index), description: _mock.description(index),
due: [fAdd({ days: index + 1 }), fAdd({ days: index + 2 })], due: [fAdd({ days: index + 1 }), fAdd({ days: index + 2 })],
priority: ([1, 3].includes(index) && PRIORITY_LEVEL.hight) || ([2, 4].includes(index) && PRIORITY_LEVEL.medium) || PRIORITY_LEVEL.low, priority:
attachments: (index === 1 && attachmentList.slice(11, 15)) || (index === 5 && attachmentList.slice(4, 9)) || [], ([1, 3].includes(index) && PRIORITY_LEVEL.hight) ||
([2, 4].includes(index) && PRIORITY_LEVEL.medium) ||
PRIORITY_LEVEL.low,
attachments:
(index === 1 && attachmentList.slice(11, 15)) ||
(index === 5 && attachmentList.slice(4, 9)) ||
[],
status, status,
}; };
}; };
const tasks = () => ({ const tasks = () => ({
[COLUMN_IDS.id1]: [createTask(1, COLUMN_NAMES.name1), createTask(2, COLUMN_NAMES.name1), createTask(3, COLUMN_NAMES.name1)], [COLUMN_IDS.id1]: [
createTask(1, COLUMN_NAMES.name1),
createTask(2, COLUMN_NAMES.name1),
createTask(3, COLUMN_NAMES.name1),
],
[COLUMN_IDS.id2]: [createTask(4, COLUMN_NAMES.name2), createTask(5, COLUMN_NAMES.name2)], [COLUMN_IDS.id2]: [createTask(4, COLUMN_NAMES.name2), createTask(5, COLUMN_NAMES.name2)],
[COLUMN_IDS.id3]: [], [COLUMN_IDS.id3]: [],
[COLUMN_IDS.id4]: [createTask(6, COLUMN_NAMES.name4)], [COLUMN_IDS.id4]: [createTask(6, COLUMN_NAMES.name4)],

View File

@@ -28,11 +28,20 @@ export const _mails = () =>
Array.from({ length: 9 }, (_, index) => { Array.from({ length: 9 }, (_, index) => {
const files = _files(); const files = _files();
const attachments = (index === 1 && files.slice(0, 2)) || (index === 2 && files.slice(0, 4)) || (index === 5 && files.slice(4, 10)) || []; const attachments =
(index === 1 && files.slice(0, 2)) ||
(index === 2 && files.slice(0, 4)) ||
(index === 5 && files.slice(4, 10)) ||
[];
const folder = ([1, 2].includes(index) && 'spam') || ([3, 4].includes(index) && 'sent') || 'inbox'; const folder =
([1, 2].includes(index) && 'spam') || ([3, 4].includes(index) && 'sent') || 'inbox';
const labelIds = (index === 1 && ['promotions', 'forums']) || (index === 2 && ['forums']) || (index === 5 && ['social']) || []; const labelIds =
(index === 1 && ['promotions', 'forums']) ||
(index === 2 && ['forums']) ||
(index === 5 && ['social']) ||
[];
const from = { const from = {
name: _mock.fullName(index), name: _mock.fullName(index),

View File

@@ -78,8 +78,11 @@ export const _mock = {
avatar: (index: number) => `${CONFIG.basePath}/assets/images/avatar/avatar-${index + 1}.webp`, avatar: (index: number) => `${CONFIG.basePath}/assets/images/avatar/avatar-${index + 1}.webp`,
travel: (index: number) => `${CONFIG.basePath}/assets/images/travel/travel-${index + 1}.webp`, travel: (index: number) => `${CONFIG.basePath}/assets/images/travel/travel-${index + 1}.webp`,
course: (index: number) => `${CONFIG.basePath}/assets/images/course/course-${index + 1}.webp`, course: (index: number) => `${CONFIG.basePath}/assets/images/course/course-${index + 1}.webp`,
company: (index: number) => `${CONFIG.basePath}/assets/images/company/company-${index + 1}.webp`, company: (index: number) =>
product: (index: number) => `${CONFIG.basePath}/assets/images/m-product/product-${index + 1}.webp`, `${CONFIG.basePath}/assets/images/company/company-${index + 1}.webp`,
portrait: (index: number) => `${CONFIG.basePath}/assets/images/portrait/portrait-${index + 1}.webp`, product: (index: number) =>
`${CONFIG.basePath}/assets/images/m-product/product-${index + 1}.webp`,
portrait: (index: number) =>
`${CONFIG.basePath}/assets/images/portrait/portrait-${index + 1}.webp`,
}, },
}; };

View File

@@ -3,7 +3,16 @@ import { _tags } from './assets';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
const COLORS = ['#FF4842', '#1890FF', '#FFC0CB', '#00AB55', '#FFC107', '#7F00FF', '#000000', '#FFFFFF']; const COLORS = [
'#FF4842',
'#1890FF',
'#FFC0CB',
'#00AB55',
'#FFC107',
'#7F00FF',
'#000000',
'#FFFFFF',
];
const DESCRIPTION = ` const DESCRIPTION = `
<h6>Specifications</h6> <h6>Specifications</h6>
@@ -84,7 +93,8 @@ const DESCRIPTION = `
`; `;
const generateAttachments = () => Array.from({ length: 20 }, (_, index) => _mock.image.product(index)); const generateAttachments = () =>
Array.from({ length: 20 }, (_, index) => _mock.image.product(index));
const generateReviews = () => { const generateReviews = () => {
const attachments = generateAttachments(); const attachments = generateAttachments();
@@ -98,7 +108,11 @@ const generateReviews = () => {
rating: _mock.number.rating(index), rating: _mock.number.rating(index),
avatarUrl: _mock.image.avatar(index), avatarUrl: _mock.image.avatar(index),
helpful: _mock.number.nativeL(index), helpful: _mock.number.nativeL(index),
attachments: (index === 1 && attachments.slice(0, 1)) || (index === 3 && attachments.slice(2, 4)) || (index === 5 && attachments.slice(5, 8)) || [], attachments:
(index === 1 && attachments.slice(0, 1)) ||
(index === 3 && attachments.slice(2, 4)) ||
(index === 5 && attachments.slice(5, 8)) ||
[],
})); }));
}; };
@@ -178,6 +192,7 @@ export const _products = () =>
newLabel: { enabled: [1, 2, 3].includes(index), content: 'NEW' }, newLabel: { enabled: [1, 2, 3].includes(index), content: 'NEW' },
saleLabel: { enabled: [4, 5].includes(index), content: 'SALE' }, saleLabel: { enabled: [4, 5].includes(index), content: 'SALE' },
sizes: ['6', '7', '8', '8.5', '9', '9.5', '10', '10.5', '11', '11.5', '12', '13'], sizes: ['6', '7', '8', '8.5', '9', '9.5', '10', '10.5', '11', '11.5', '12', '13'],
subDescription: 'Featuring the original ripple design inspired by Japanese bullet trains, the Nike Air Max 97 lets you push your style full-speed ahead.', subDescription:
'Featuring the original ripple design inspired by Japanese bullet trains, the Nike Air Max 97 lets you push your style full-speed ahead.',
}; };
}); });

View File

@@ -1,6 +1,9 @@
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export const _id = Array.from({ length: 40 }, (_, index) => `e99f09a7-dd88-49d5-b1c8-1daf80c2d7b${index + 1}`); export const _id = Array.from(
{ length: 40 },
(_, index) => `e99f09a7-dd88-49d5-b1c8-1daf80c2d7b${index + 1}`
);
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -34,24 +37,36 @@ export const _booleans = [
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export const _prices = [ export const _prices = [
83.74, 97.14, 68.71, 85.21, 52.17, 25.18, 43.84, 60.98, 98.42, 53.37, 72.75, 56.61, 64.55, 77.32, 60.62, 79.81, 93.68, 47.44, 76.24, 92.87, 72.91, 20.54, 83.74, 97.14, 68.71, 85.21, 52.17, 25.18, 43.84, 60.98, 98.42, 53.37, 72.75, 56.61, 64.55, 77.32,
94.25, 37.51, 60.62, 79.81, 93.68, 47.44, 76.24, 92.87, 72.91, 20.54, 94.25, 37.51,
]; ];
export const _ratings = [4.2, 3.7, 4.5, 3.5, 0.5, 3.0, 2.5, 2.8, 4.9, 3.6, 2.5, 1.7, 3.9, 2.8, 4.1, 4.5, 2.2, 3.2, 0.6, 1.3, 3.8, 3.8, 3.8, 2.0]; export const _ratings = [
4.2, 3.7, 4.5, 3.5, 0.5, 3.0, 2.5, 2.8, 4.9, 3.6, 2.5, 1.7, 3.9, 2.8, 4.1, 4.5, 2.2, 3.2, 0.6,
1.3, 3.8, 3.8, 3.8, 2.0,
];
export const _ages = [30, 26, 59, 47, 29, 46, 18, 56, 39, 19, 45, 18, 46, 56, 38, 41, 44, 48, 32, 45, 42, 60, 33, 57]; export const _ages = [
30, 26, 59, 47, 29, 46, 18, 56, 39, 19, 45, 18, 46, 56, 38, 41, 44, 48, 32, 45, 42, 60, 33, 57,
];
export const _percents = [ export const _percents = [
10.1, 13.6, 28.2, 42.1, 37.2, 18.5, 40.1, 94.8, 91.4, 53.0, 25.4, 62.9, 86.6, 62.4, 35.4, 17.6, 52.0, 6.8, 95.3, 26.6, 69.9, 92.1, 46.2, 85.6, 10.1, 13.6, 28.2, 42.1, 37.2, 18.5, 40.1, 94.8, 91.4, 53.0, 25.4, 62.9, 86.6, 62.4, 35.4, 17.6,
52.0, 6.8, 95.3, 26.6, 69.9, 92.1, 46.2, 85.6,
]; ];
export const _nativeS = [11, 10, 7, 10, 12, 5, 10, 1, 8, 8, 10, 11, 12, 8, 4, 11, 8, 9, 4, 9, 2, 6, 3, 7]; export const _nativeS = [
11, 10, 7, 10, 12, 5, 10, 1, 8, 8, 10, 11, 12, 8, 4, 11, 8, 9, 4, 9, 2, 6, 3, 7,
];
export const _nativeM = [497, 763, 684, 451, 433, 463, 951, 194, 425, 435, 807, 521, 538, 839, 394, 269, 453, 821, 364, 849, 804, 776, 263, 239]; export const _nativeM = [
497, 763, 684, 451, 433, 463, 951, 194, 425, 435, 807, 521, 538, 839, 394, 269, 453, 821, 364,
849, 804, 776, 263, 239,
];
export const _nativeL = [ export const _nativeL = [
9911, 1947, 9124, 6984, 8488, 2034, 3364, 8401, 8996, 5271, 8478, 1139, 8061, 3035, 6733, 3952, 2405, 3127, 6843, 4672, 6995, 6053, 5192, 9686, 9911, 1947, 9124, 6984, 8488, 2034, 3364, 8401, 8996, 5271, 8478, 1139, 8061, 3035, 6733, 3952,
2405, 3127, 6843, 4672, 6995, 6053, 5192, 9686,
]; ];
export const _fullAddress = [ export const _fullAddress = [

View File

@@ -10,8 +10,7 @@ import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response'; import { STATUS, response, handleError } from 'src/utils/response';
// import { AccessLogService } from '../../../../modules/AccessLog/AccessLog.service'; import { AccessLogService } from '../../../../modules/AccessLog/AccessLog.service';
import { getAccessLogById } from 'src/app/services/access-log.service';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -28,7 +27,7 @@ export async function GET(req: NextRequest) {
const accessLogId = searchParams.get('accessLogId'); const accessLogId = searchParams.get('accessLogId');
if (!accessLogId) return response({ message: 'accessLogId is required!' }, STATUS.BAD_REQUEST); if (!accessLogId) return response({ message: 'accessLogId is required!' }, STATUS.BAD_REQUEST);
const accessLog = await getAccessLogById(accessLogId); const accessLog = await AccessLogService.findById(accessLogId);
if (!accessLog) return response({ message: 'AccessLog not found!' }, STATUS.NOT_FOUND); if (!accessLog) return response({ message: 'AccessLog not found!' }, STATUS.NOT_FOUND);

View File

@@ -2,7 +2,7 @@ import type { NextRequest, NextResponse } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response'; import { STATUS, response, handleError } from 'src/utils/response';
import { listAccessLogs } from 'src/app/services/access-log.service'; import { listAccessLogs } from 'src/app/services/AccessLog.service';
// import prisma from '../../lib/prisma'; // import prisma from '../../lib/prisma';

View File

@@ -2,7 +2,7 @@ import type { NextRequest, NextResponse } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response'; import { STATUS, response, handleError } from 'src/utils/response';
import { listAppLogs, deleteAppLog, createNewAppLog } from 'src/app/services/app-log.service'; import { listAppLogs, deleteAppLog, updateAppLog, createNewAppLog } from 'src/app/services/AppLog.service';
// import prisma from '../../lib/prisma'; // import prisma from '../../lib/prisma';
@@ -34,29 +34,28 @@ export async function POST(req: NextRequest) {
} }
} }
// TODO: delete `update AppLog` /**
// /** ***************************************
// *************************************** * PUT - update AppLog
// * PUT - update AppLog ***************************************
// *************************************** */
// */ export async function PUT(req: NextRequest) {
// export async function PUT(req: NextRequest) { const { searchParams } = req.nextUrl;
// const { searchParams } = req.nextUrl; const appLogId = searchParams.get('appLogId');
// const appLogId = searchParams.get('appLogId');
// const { data } = await req.json(); const { data } = await req.json();
// try { try {
// if (!appLogId) throw new Error('appLogId cannot null'); if (!appLogId) throw new Error('appLogId cannot null');
// const id: number = parseInt(appLogId); const id: number = parseInt(appLogId);
// const updateResult = await updateAppLog(id, data); const updateResult = await updateAppLog(id, data);
// return response(updateResult, STATUS.OK); return response(updateResult, STATUS.OK);
// } catch (error) { } catch (error) {
// return handleError('AppLog - Update', error); return handleError('AppLog - Update', error);
// } }
// } }
/** /**
*************************************** ***************************************
@@ -73,7 +72,7 @@ export async function DELETE(req: NextRequest) {
if (!appLogId) throw new Error('appLogId cannot null'); if (!appLogId) throw new Error('appLogId cannot null');
const id: number = parseInt(appLogId); const id: number = parseInt(appLogId);
const deleteResult = await deleteAppLog(id.toString()); const deleteResult = await deleteAppLog(id);
return response(deleteResult, STATUS.OK); return response(deleteResult, STATUS.OK);
} catch (error) { } catch (error) {

View File

@@ -1,14 +0,0 @@
Hi,
i copied from
`03_source/cms_backend/src/app/api/party-event`
to
`03_source/cms_backend/src/app/api/party-order`
with knowledge in `schema.prisma` file, and take a look into the sibling files in the same directory.
i want you to update `03_source/cms_backend/src/app/api/party-order` content to handle `party-order` (the purchase order of the party)
`party-order.service.ts` is already prepared and you can use it
please maintain same format and level of detail when you edit.
thanks.

View File

@@ -8,7 +8,7 @@ import { STATUS, response, handleError } from 'src/utils/response';
import { JWT_SECRET } from 'src/_mock/_auth'; import { JWT_SECRET } from 'src/_mock/_auth';
import { getUserById } from 'src/app/services/user.service'; import { getUserById } from 'src/app/services/user.service';
import { createAccessLog } from 'src/app/services/access-log.service'; import { createAccessLog } from 'src/app/services/AccessLog.service';
import { flattenNextjsRequest } from '../sign-in/flattenNextjsRequest'; import { flattenNextjsRequest } from '../sign-in/flattenNextjsRequest';

View File

@@ -4,7 +4,7 @@ import { sign } from 'src/utils/jwt';
import { STATUS, response, handleError } from 'src/utils/response'; import { STATUS, response, handleError } from 'src/utils/response';
import { JWT_SECRET, JWT_EXPIRES_IN } from 'src/_mock/_auth'; import { JWT_SECRET, JWT_EXPIRES_IN } from 'src/_mock/_auth';
import { createAccessLog } from 'src/app/services/access-log.service'; import { createAccessLog } from 'src/app/services/AccessLog.service';
import prisma from '../../../lib/prisma'; import prisma from '../../../lib/prisma';
import { flattenNextjsRequest } from './flattenNextjsRequest'; import { flattenNextjsRequest } from './flattenNextjsRequest';

View File

@@ -12,7 +12,7 @@ import { STATUS, response, handleError } from 'src/utils/response';
import { L_INFO, L_ERROR } from 'src/constants'; import { L_INFO, L_ERROR } from 'src/constants';
import { getEvent } from 'src/app/services/eventItem.service'; import { getEvent } from 'src/app/services/eventItem.service';
import { createAppLog } from 'src/app/services/app-log.service'; import { createAppLog } from 'src/app/services/AppLog.service';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@@ -7,28 +7,29 @@ import { _events } from 'src/_mock/_event';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// export const runtime = 'edge'; export const runtime = 'edge';
/** ************************************** /** **************************************
* GET - Search events * GET - Search events
*************************************** */ *************************************** */
export async function GET(req: NextRequest) { export async function GET(req: NextRequest) {
// try { try {
// const { searchParams } = req.nextUrl;
// TODO: implement search events const query = searchParams.get('query')?.trim().toLowerCase();
//
// const { searchParams } = req.nextUrl;
// const query = searchParams.get('query')?.trim().toLowerCase();
// if (!query) {
// return response({ results: [] }, STATUS.OK);
// }
// const events = _events();
// // Accept search by name or sku
// const results = events.filter(({ name, sku }) => name.toLowerCase().includes(query) || sku?.toLowerCase().includes(query));
// logger('[Event] search-results', results.length);
// return response({ results }, STATUS.OK);
// } catch (error) { if (!query) {
return handleError('Event - Get search not implemented', {}); return response({ results: [] }, STATUS.OK);
// } }
const events = _events();
// Accept search by name or sku
const results = events.filter(({ name, sku }) => name.toLowerCase().includes(query) || sku?.toLowerCase().includes(query));
logger('[Event] search-results', results.length);
return response({ results }, STATUS.OK);
} catch (error) {
return handleError('Event - Get search', error);
}
} }

View File

@@ -11,7 +11,7 @@ import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response'; import { STATUS, response, handleError } from 'src/utils/response';
import { L_INFO, L_ERROR } from 'src/constants'; import { L_INFO, L_ERROR } from 'src/constants';
import { createAppLog } from 'src/app/services/app-log.service'; import { createAppLog } from 'src/app/services/AppLog.service';
import prisma from '../../../lib/prisma'; import prisma from '../../../lib/prisma';
import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest'; import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest';

View File

@@ -24,7 +24,9 @@ export async function GET(req: NextRequest) {
const products = _products(); const products = _products();
// Accept search by name or sku // Accept search by name or sku
const results = products.filter(({ name, sku }) => name.toLowerCase().includes(query) || sku?.toLowerCase().includes(query)); const results = products.filter(
({ name, sku }) => name.toLowerCase().includes(query) || sku?.toLowerCase().includes(query)
);
logger('[Product] search-results', results.length); logger('[Product] search-results', results.length);

View File

@@ -25,8 +25,13 @@ const ENDPOINTS = {
}; };
function loggerData(action?: string, value?: unknown) { function loggerData(action?: string, value?: unknown) {
const columnsWithTasks = boardData.columns.map((col) => `${col.name} (${boardData.tasks[col.id].length} tasks)`); const columnsWithTasks = boardData.columns.map(
logger('[Kanban] get-board', `columns (${boardData.columns.length}): ${JSON.stringify(columnsWithTasks, null, 2)}`); (col) => `${col.name} (${boardData.tasks[col.id].length} tasks)`
);
logger(
'[Kanban] get-board',
`columns (${boardData.columns.length}): ${JSON.stringify(columnsWithTasks, null, 2)}`
);
if (value || action) { if (value || action) {
logger(`[Kanban] ${action}`, value); logger(`[Kanban] ${action}`, value);
} }
@@ -121,7 +126,9 @@ async function updateColumn(req: NextRequest) {
// Find and update the specified column. // Find and update the specified column.
updateBoardData({ updateBoardData({
columns: boardData.columns.map((col) => (col.id === columnId ? { ...col, name: columnName } : col)), columns: boardData.columns.map((col) =>
col.id === columnId ? { ...col, name: columnName } : col
),
}); });
loggerData('updated-column', columnName); loggerData('updated-column', columnName);
@@ -215,7 +222,9 @@ async function updateTask(req: NextRequest) {
updateBoardData({ updateBoardData({
tasks: { tasks: {
...boardData.tasks, ...boardData.tasks,
[columnId]: boardData.tasks[columnId].map((task) => (task.id === taskData.id ? { ...task, ...taskData } : task)), [columnId]: boardData.tasks[columnId].map((task) =>
task.id === taskData.id ? { ...task, ...taskData } : task
),
}, },
}); });

View File

@@ -31,7 +31,10 @@ export async function GET(req: NextRequest) {
} }
// Get filtered mails // Get filtered mails
const filteredMails = label.type === 'custom' ? mails.filter((mail) => mail.labelIds.includes(labelId!)) : filterMailsByLabelId(mails, labelId); const filteredMails =
label.type === 'custom'
? mails.filter((mail) => mail.labelIds.includes(labelId!))
: filterMailsByLabelId(mails, labelId);
logger(`[Mail] label-[${labelId}]`, filteredMails.length); logger(`[Mail] label-[${labelId}]`, filteredMails.length);

View File

@@ -28,7 +28,7 @@ export async function POST(req: NextRequest) {
try { try {
const order = await createOrder(data); const order = await createOrder(data);
return response(order, STATUS.OK); return response(order, STATUS.CREATED);
} catch (error) { } catch (error) {
return handleError('Order - Create', error); return handleError('Order - Create', error);
} }
@@ -48,7 +48,7 @@ export async function PUT(req: NextRequest) {
if (!orderId) throw new Error('orderId cannot be null'); if (!orderId) throw new Error('orderId cannot be null');
const id: number = parseInt(orderId); const id: number = parseInt(orderId);
const updatedOrder = await updateOrder(id.toString(), data); const updatedOrder = await updateOrder(id, data);
return response(updatedOrder, STATUS.OK); return response(updatedOrder, STATUS.OK);
} catch (error) { } catch (error) {
return handleError('Order - Update', error); return handleError('Order - Update', error);
@@ -68,7 +68,7 @@ export async function DELETE(req: NextRequest) {
if (!orderId) throw new Error('orderId cannot be null'); if (!orderId) throw new Error('orderId cannot be null');
const id: number = parseInt(orderId); const id: number = parseInt(orderId);
await deleteOrder(id.toString()); await deleteOrder(id);
return response({ success: true }, STATUS.OK); return response({ success: true }, STATUS.OK);
} catch (error) { } catch (error) {
return handleError('Order - Delete', error); return handleError('Order - Delete', error);

View File

@@ -24,7 +24,9 @@ export async function GET(req: NextRequest) {
const products = _products(); const products = _products();
// Accept search by name or sku // Accept search by name or sku
const results = products.filter(({ name, sku }) => name.toLowerCase().includes(query) || sku?.toLowerCase().includes(query)); const results = products.filter(
({ name, sku }) => name.toLowerCase().includes(query) || sku?.toLowerCase().includes(query)
);
logger('[Product] search-results', results.length); logger('[Product] search-results', results.length);

View File

@@ -47,7 +47,9 @@ export async function GET(req: NextRequest) {
products: paginatedProducts, products: paginatedProducts,
totalPages, totalPages,
totalItems, totalItems,
categoryOptions: Array.from(new Set(_products.map(({ category: c_category }) => c_category))), // Remove duplicate categories categoryOptions: Array.from(
new Set(_products.map(({ category: c_category }) => c_category))
), // Remove duplicate categories
}, },
STATUS.OK STATUS.OK
); );
@@ -68,7 +70,9 @@ function paginateProducts(products: Products, page: number, perPage: number) {
function filterProducts(products: Products, searchQuery: string, category: string) { function filterProducts(products: Products, searchQuery: string, category: string) {
return products.filter(({ id, name, category: prodCategory }) => { return products.filter(({ id, name, category: prodCategory }) => {
// Accept search by id or name // Accept search by id or name
const matchesSearch = searchQuery ? id.includes(searchQuery) || name.toLowerCase().includes(searchQuery) : true; const matchesSearch = searchQuery
? id.includes(searchQuery) || name.toLowerCase().includes(searchQuery)
: true;
const matchesCategory = category ? prodCategory === category : true; const matchesCategory = category ? prodCategory === category : true;
return matchesSearch && matchesCategory; return matchesSearch && matchesCategory;

View File

@@ -1,35 +0,0 @@
<!-- AI: please maintain same format and level of detail when edit this file -->
# GUIDELINE
- Party Event API endpoint for managing party events
- Handles CRUD operations for party events
- Follows single file for single db table/collection pattern
## `route.ts`
Handles HTTP methods:
- `GET` - Retrieve party events
- `POST` - Create new party event
- `PUT` - Update existing party event
- `DELETE` - Remove party event
## `test.http`
Contains test requests for:
- Listing all party events
- Creating new party event
- Updating existing party event
- Deleting party event
## `../../services/party-event.service.ts`
Party Event CRUD operations:
`listPartyEvents` - List all party events with optional filters
`getPartyEvent` - Get single party event by ID
`createNewPartyEvent` - Create new party event record
`updatePartyEvent` - Update existing party event by ID
`deletePartyEvent` - Delete party event by ID

View File

@@ -1,40 +0,0 @@
// src/app/api/party-event/create/route.ts
//
// PURPOSE:
// Create new party event in db
//
// RULES:
// T.B.A.
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { isDev } from 'src/constants';
import { createEvent } from 'src/app/services/party-event.service';
// ----------------------------------------------------------------------
/** **************************************
* POST - Create PartyEvent
*************************************** */
export async function POST(req: NextRequest) {
const { partyEventData } = await req.json();
try {
if (isDev) {
console.log({ partyEventData });
}
const created = await createEvent(partyEventData);
if (isDev) {
console.log('Event created successfully');
}
return response(created, STATUS.OK);
} catch (error) {
console.error('Error creating event:', { partyEventData });
return handleError('PartyEvent - Create', error);
}
}

View File

@@ -1,34 +0,0 @@
###
POST http://localhost:7272/api/party-event/create
Content-Type: application/json
{
"partyEventData": {
"title": "Summer Music Festival",
"description": "Annual summer music festival featuring local bands and artists",
"startDate": "2024-07-15T18:00:00Z",
"endDate": "2024-07-15T23:00:00Z",
"location": "Central Park, Hong Kong",
"coverUrl": "",
"images": [
"data:image/png;base64,C",
"data:image/png;base64,C"
],
"tags": [
"Music",
"Festival"
],
"status": "upcoming",
"capacity": 500,
"price": 150.00,
"organizer": "HK Music Society",
"category": "Music",
"isFeatured": true,
"registrationDeadline": "2024-07-10T00:00:00Z",
"requirements": "Age 18+",
"schedule": "18:00 Doors open\n19:00 First performance\n21:00 Main act",
"speakers": ["DJ Lee", "Band XYZ"],
"sponsors": ["HK Radio", "Music Magazine"]
}
}

View File

@@ -1,22 +0,0 @@
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { deleteEvent } from 'src/app/services/party-event.service';
/** **************************************
* PATCH - Delete PartyEvent
*************************************** */
export async function PATCH(req: NextRequest) {
try {
const { partyEventId } = await req.json();
if (!partyEventId) throw new Error('partyEventId cannot be null');
await deleteEvent(partyEventId);
return response({ partyEventId }, STATUS.OK);
} catch (error) {
return handleError('PartyEvent - Delete', error);
}
}

View File

@@ -1,8 +0,0 @@
###
PATCH http://localhost:7272/api/party-event/delete
Content-Type: application/json
{
"partyEventId": "e99f09a7-dd88-49d5-b1c8-1daf80c2d7b01"
}

View File

@@ -1,56 +0,0 @@
// src/app/api/party-event/details/route.ts
//
// PURPOSE:
// Get party event 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 { L_INFO, L_ERROR } from 'src/constants';
import { getEvent } from 'src/app/services/party-event.service';
import { createAppLog } from 'src/app/services/app-log.service';
import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest';
// ----------------------------------------------------------------------
/**
**************************************
* GET PartyEvent detail
***************************************
*/
export async function GET(req: NextRequest) {
const debug = { 'req.headers': flattenNextjsRequest(req) };
try {
const { searchParams } = req.nextUrl;
// RULES: eventId must exist
const partyEventId = searchParams.get('partyEventId');
if (!partyEventId) {
return response({ message: 'PartyEvent ID is required!' }, STATUS.BAD_REQUEST);
}
// NOTE: eventId confirmed exist, run below
const partyEvent = await getEvent(partyEventId);
if (!partyEvent) {
return response({ message: 'PartyEvent not found!' }, STATUS.NOT_FOUND);
}
logger('[PartyEvent] details', partyEvent.id);
createAppLog(L_INFO, 'Get event detail OK', debug);
return response({ partyEvent }, STATUS.OK);
} catch (error) {
createAppLog(L_ERROR, 'event detail error', debug);
return handleError('PartyEvent - Get details', error);
}
}

View File

@@ -1,9 +0,0 @@
###
# Get details for a specific party event
GET http://localhost:7272/api/party-event/details?partyEventId=e99f09a7-dd88-49d5-b1c8-1daf80c2d7b01
###
# Alternative format with different ID
GET http://localhost:7272/api/party-event/details?eventId=evt_987654321

View File

@@ -1,16 +0,0 @@
import type { NextRequest, NextResponse } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
/**
***************************************
* GET - helloworld
***************************************
*/
export async function GET(req: NextRequest, res: NextResponse) {
try {
return response({ helloworld: 'party-event' }, STATUS.OK);
} catch (error) {
return handleError('Helloworld - Get all', error);
}
}

View File

@@ -1,4 +0,0 @@
###
GET /api/party-event/helloworld HTTP/1.1
Host: localhost:7272

View File

@@ -1,38 +0,0 @@
// src/app/api/party-event/list/route.ts
//
// PURPOSE:
// List all party events from db
//
// RULES:
// T.B.A.
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { L_INFO, L_ERROR } from 'src/constants';
import { createAppLog } from 'src/app/services/app-log.service';
import { listEvents } from 'src/app/services/party-event.service';
import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest';
// ----------------------------------------------------------------------
/** **************************************
* GET - PartyEvents list
*************************************** */
export async function GET(req: NextRequest) {
const debug = { 'req.headers': flattenNextjsRequest(req) };
try {
const partyEvents = await listEvents();
createAppLog(L_INFO, 'party-event list ok', {});
return response({ partyEvents }, STATUS.OK);
} catch (error) {
createAppLog(L_ERROR, 'party-event list error', debug);
return handleError('PartyEvent - Get list', error);
}
}

View File

@@ -1,14 +0,0 @@
###
# Basic list all party events
GET http://localhost:7272/api/party-event/list
###
# List upcoming party events
GET http://localhost:7272/api/party-event/list?status=upcoming
###
# List featured party events
GET http://localhost:7272/api/party-event/list?isFeatured=true

View File

@@ -1,75 +0,0 @@
import type { NextRequest, NextResponse } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { listEvents, deleteEvent, updateEvent, createEvent } from 'src/app/services/party-event.service';
/**
**************************************
* GET - PartyEvent
***************************************
*/
export async function GET(req: NextRequest, res: NextResponse) {
try {
const events = await listEvents();
return response(events, STATUS.OK);
} catch (error) {
return handleError('PartyEvent - Get list', error);
}
}
/**
***************************************
* POST - Create PartyEvent
***************************************
*/
export async function POST(req: NextRequest) {
const OPERATION = 'PartyEvent - Create';
const { data } = await req.json();
try {
const event = await createEvent(data);
return response(OPERATION, STATUS.OK);
} catch (error) {
return handleError(OPERATION, error);
}
}
/**
***************************************
* PUT - Update PartyEvent
***************************************
*/
export async function PUT(req: NextRequest) {
const { searchParams } = req.nextUrl;
const eventId = searchParams.get('eventId');
const { data } = await req.json();
try {
if (!eventId) throw new Error('eventId cannot be null');
const result = await updateEvent(eventId, data);
return response(result, STATUS.OK);
} catch (error) {
return handleError('PartyEvent - Update', error);
}
}
/**
***************************************
* DELETE - Delete PartyEvent
***************************************
*/
export async function DELETE(req: NextRequest) {
const { searchParams } = req.nextUrl;
const eventId = searchParams.get('eventId');
try {
if (!eventId) throw new Error('eventId cannot be null');
await deleteEvent(eventId);
return response({ success: true }, STATUS.OK);
} catch (error) {
return handleError('PartyEvent - Delete', error);
}
}

View File

@@ -1,34 +0,0 @@
import type { NextRequest } from 'next/server';
import { logger } from 'src/utils/logger';
import { STATUS, response, handleError } from 'src/utils/response';
import { IEventItem } from '../update/route';
// import { searchEvents } from 'src/app/services/party-event.service';
// ----------------------------------------------------------------------
/** **************************************
* GET - Search PartyEvents
*************************************** */
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 results: IEventItem[] = [];
// TODO: search party event not implemented
console.log('search party event not implemented');
// const results = await searchEvents(query);
logger('[PartyEvent] search-results', results?.length);
return response({ results }, STATUS.OK);
} catch (error) {
return handleError('PartyEvent - Get search', error);
}
}

View File

@@ -1,24 +0,0 @@
###
# Search party events by title
GET http://localhost:7272/api/party-event/search?query=Music
###
# Search party events by location
GET http://localhost:7272/api/party-event/search?query=Central+Park
###
# Search party events by tag
GET http://localhost:7272/api/party-event/search?query=Festival
###
# Combined search with multiple parameters
GET http://localhost:7272/api/party-event/search?query=Summer&location=Hong+Kong&category=Music
###
# No results expected
GET http://localhost:7272/api/party-event/search?query=zzzzzz

View File

@@ -1,57 +0,0 @@
// src/app/api/party-event/update/route.ts
//
// PURPOSE:
// Update party event in db by id
//
// RULES:
// T.B.A.
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { updateEvent } from 'src/app/services/party-event.service';
// ----------------------------------------------------------------------
/** **************************************
* PUT - Update PartyEvent
*************************************** */
export async function PUT(req: NextRequest) {
const { partyEventData } = await req.json();
const { id } = partyEventData;
if (!id) return response({ message: 'id not found' }, STATUS.ERROR);
try {
const result = await updateEvent(id, partyEventData);
return response({ result }, STATUS.OK);
} catch (error) {
console.error('Error updating event:', { partyEventData });
return handleError('PartyEvent - Update', error);
}
}
export type IEventItem = {
id: string;
title: string;
description: string;
startDate: Date;
endDate: Date;
location: string;
coverUrl: string;
images: string[];
tags: string[];
status: string;
capacity: number;
price: number;
organizer: string;
category: string;
isFeatured: boolean;
registrationDeadline: Date;
requirements: string[];
schedule: string;
speakers: string[];
sponsors: string[];
};

View File

@@ -1,31 +0,0 @@
###
PUT http://localhost:7272/api/party-event/update
Content-Type: application/json
{
"partyEventData": {
"id":"e99f09a7-dd88-49d5-b1c8-1daf80c2d7b01",
"title": "Summer Music Festival 111",
"name": "Summer Music Festival 111",
"description": "Annual summer music festival featuring local bands and artists",
"startDate": "2024-07-15T18:00:00Z",
"endDate": "2024-07-15T23:00:00Z",
"location": "Central Park, Hong Kong",
"coverUrl": "",
"images": [ "data:image/png;base64,C", "data:image/png;base64,C" ],
"tags": [ "Music", "Festival" ],
"status": "upcoming",
"capacity": 500,
"price": 150.00,
"organizer": "HK Music Society",
"category": "Music",
"isFeatured": true,
"registrationDeadline": "2024-07-10T00:00:00Z",
"requirements": "Age 18+",
"schedule": "18:00 Doors open\n19:00 First performance\n21:00 Main act",
"speakers": ["DJ Lee", "Band XYZ"],
"sponsors": ["HK Radio", "Music Magazine"],
"reviews":[]
}
}

View File

@@ -1,35 +0,0 @@
<!-- AI: please maintain same format and level of detail when edit this file -->
# GUIDELINE
- Party Order API endpoint for managing party orders
- Handles CRUD operations for party orders
- Follows single file for single db table/collection pattern
## `route.ts`
Handles HTTP methods:
- `GET` - Retrieve party orders
- `POST` - Create new party order
- `PUT` - Update existing party order
- `DELETE` - Remove party order
## `test.http`
Contains test requests for:
- Listing all party orders
- Creating new party order
- Updating existing party order
- Deleting party order
## `../../services/party-order.service.ts`
Party Order CRUD operations:
`listPartyOrders` - List all party orders with optional filters
`getPartyOrder` - Get single party order by ID
`createNewPartyOrder` - Create new party order record
`updatePartyOrder` - Update existing party order by ID
`deletePartyOrder` - Delete party order by ID

View File

@@ -1,40 +0,0 @@
// src/app/api/party-order/create/route.ts
//
// PURPOSE:
// Create new party order in db
//
// RULES:
// T.B.A.
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { isDev } from 'src/constants';
import { createOrder } from 'src/app/services/party-order.service';
// ----------------------------------------------------------------------
/** **************************************
* POST - Create PartyOrder
*************************************** */
export async function POST(req: NextRequest) {
const { partyOrderData } = await req.json();
try {
if (isDev) {
console.log({ partyOrderData });
}
const created = await createOrder(partyOrderData);
if (isDev) {
console.log('Order created successfully');
}
return response(created, STATUS.OK);
} catch (error) {
console.error('Error creating order:', { partyOrderData });
return handleError('PartyOrder - Create', error);
}
}

View File

@@ -1,31 +0,0 @@
###
POST http://localhost:7272/api/party-order/create
Content-Type: application/json
{
"partyOrderData": {
"taxes": 15.00,
"status": "pending",
"shipping": 10.00,
"discount": 20.00,
"subtotal": 290.00,
"orderNumber": "ORD_20231115001",
"totalAmount": 300.00,
"totalQuantity": 2.0,
"history": "{\"actions\":[{\"timestamp\":\"2023-11-15T10:00:00Z\",\"action\":\"order_created\"}]}",
"payment": "{\"method\":\"credit_card\",\"status\":\"unpaid\",\"transaction_id\":\"txn_123456\"}",
"customer": "{\"name\":\"John Doe\",\"email\":\"john.doe@example.com\",\"phone\":\"+1234567890\"}",
"delivery": "{\"method\":\"courier\",\"estimated_delivery\":\"2023-11-18\"}",
"items": [
{
"id": "ticket_001",
"name": "General Admission",
"quantity": 2,
"price": 150.00
}
],
"shippingAddress": "{\"street\":\"123 Main St\",\"city\":\"New York\",\"state\":\"NY\",\"zip\":\"10001\",\"country\":\"USA\"}"
}
}

View File

@@ -1,22 +0,0 @@
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { deleteOrder } from 'src/app/services/party-order.service';
/** **************************************
* PATCH - Delete PartyOrder
*************************************** */
export async function PATCH(req: NextRequest) {
try {
const { partyOrderId } = await req.json();
if (!partyOrderId) throw new Error('orderId cannot be null');
await deleteOrder(partyOrderId);
return response({ partyOrderId }, STATUS.OK);
} catch (error) {
return handleError('PartyOrder - Delete', error);
}
}

View File

@@ -1,8 +0,0 @@
###
PATCH http://localhost:7272/api/party-order/delete
Content-Type: application/json
{
"partyOrderId": "e99f09a7-dd88-49d5-b1c8-1daf80c2d7b02"
}

View File

@@ -1,56 +0,0 @@
// src/app/api/party-order/details/route.ts
//
// PURPOSE:
// Get party order from db by id
//
// RULES:
// Do not modify the format and level of details thanks.
import type { NextRequest } from 'next/server';
import { logger } from 'src/utils/logger';
import { STATUS, response, handleError } from 'src/utils/response';
import { L_INFO, L_ERROR } from 'src/constants';
import { getOrder } from 'src/app/services/party-order.service';
import { createAppLog } from 'src/app/services/app-log.service';
import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest';
// ----------------------------------------------------------------------
/**
**************************************
* GET PartyOrder detail
***************************************
*/
export async function GET(req: NextRequest) {
const debug = { 'req.headers': flattenNextjsRequest(req) };
try {
const { searchParams } = req.nextUrl;
// RULES: partyOrderId must exist
const partyOrderId = searchParams.get('partyOrderId');
if (!partyOrderId) {
return response({ message: 'Order ID is required!' }, STATUS.BAD_REQUEST);
}
// NOTE: partyOrderId confirmed exist, run below
const partyOrder = await getOrder(partyOrderId);
if (!partyOrder) {
return response({ message: 'Order not found!' }, STATUS.NOT_FOUND);
}
logger('[PartyOrder] details', partyOrder.id);
createAppLog(L_INFO, 'Get order detail OK', debug);
return response({ partyOrder }, STATUS.OK);
} catch (error) {
createAppLog(L_ERROR, 'order detail error', debug);
return handleError('PartyOrder - Get details', error);
}
}

View File

@@ -1,9 +0,0 @@
###
# Get details for a specific party order
GET http://localhost:7272/api/party-order/details?partyOrderId=e99f09a7-dd88-49d5-b1c8-1daf80c2d7b13
###
# Alternative format with different ID
GET http://localhost:7272/api/party-order/details?id=ord_987654321

View File

@@ -1,16 +0,0 @@
import type { NextRequest, NextResponse } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
/**
***************************************
* GET - helloworld
***************************************
*/
export async function GET(req: NextRequest, res: NextResponse) {
try {
return response({ helloworld: 'party-order' }, STATUS.OK);
} catch (error) {
return handleError('Helloworld - Get all', error);
}
}

View File

@@ -1,2 +0,0 @@
###
GET http://localhost:7272/api/party-order/helloworld

View File

@@ -1,38 +0,0 @@
// src/app/api/party-order/list/route.ts
//
// PURPOSE:
// List all party orders from db
//
// RULES:
// T.B.A.
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { L_INFO, L_ERROR } from 'src/constants';
import { createAppLog } from 'src/app/services/app-log.service';
import { listPartyOrders } from 'src/app/services/party-order.service';
import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest';
// ----------------------------------------------------------------------
/** **************************************
* GET - PartyOrders list
*************************************** */
export async function GET(req: NextRequest) {
const debug = { 'req.headers': flattenNextjsRequest(req) };
try {
const partyOrders = await listPartyOrders();
createAppLog(L_INFO, 'party-order list ok', {});
return response({ partyOrders }, STATUS.OK);
} catch (error) {
createAppLog(L_ERROR, 'party-order list error', debug);
return handleError('PartyOrder - Get list', error);
}
}

View File

@@ -1,19 +0,0 @@
###
# Basic list all orders
GET http://localhost:7272/api/party-order/list
###
# List orders by status
GET http://localhost:7272/api/party-order/list?status=completed
###
# List orders by user
GET http://localhost:7272/api/party-order/list?userId=usr_987654321
###
# List orders by payment status
GET http://localhost:7272/api/party-order/list?paymentStatus=paid

View File

@@ -1,75 +0,0 @@
import type { NextRequest, NextResponse } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { listPartyOrders, deleteOrder, updateOrder, createOrder } from 'src/app/services/party-order.service';
/**
**************************************
* GET - PartyOrder
***************************************
*/
export async function GET(req: NextRequest, res: NextResponse) {
try {
const orders = await listPartyOrders();
return response(orders, STATUS.OK);
} catch (error) {
return handleError('PartyOrder - Get list', error);
}
}
/**
***************************************
* POST - Create PartyOrder
***************************************
*/
export async function POST(req: NextRequest) {
const OPERATION = 'PartyOrder - Create';
const { data } = await req.json();
try {
const order = await createOrder(data);
return response(OPERATION, STATUS.OK);
} catch (error) {
return handleError(OPERATION, error);
}
}
/**
***************************************
* PUT - Update PartyOrder
***************************************
*/
export async function PUT(req: NextRequest) {
const { searchParams } = req.nextUrl;
const orderId = searchParams.get('orderId');
const { data } = await req.json();
try {
if (!orderId) throw new Error('orderId cannot be null');
const result = await updateOrder(orderId, data);
return response(result, STATUS.OK);
} catch (error) {
return handleError('PartyOrder - Update', error);
}
}
/**
***************************************
* DELETE - Delete PartyOrder
***************************************
*/
export async function DELETE(req: NextRequest) {
const { searchParams } = req.nextUrl;
const orderId = searchParams.get('orderId');
try {
if (!orderId) throw new Error('orderId cannot be null');
await deleteOrder(orderId);
return response({ success: true }, STATUS.OK);
} catch (error) {
return handleError('PartyOrder - Delete', error);
}
}

View File

@@ -1,35 +0,0 @@
import type { NextRequest } from 'next/server';
import { logger } from 'src/utils/logger';
import { STATUS, response, handleError } from 'src/utils/response';
import type { IOrderItem } from '../update/route';
// import { searchEvents } from 'src/app/services/party-event.service';
// ----------------------------------------------------------------------
/** **************************************
* GET - Search PartyEvents
*************************************** */
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 results: IOrderItem[] = [];
// TODO: search party event not implemented
console.log('search party event not implemented');
// const results = await searchEvents(query);
logger('[PartyEvent] search-results', results?.length);
return response({ results }, STATUS.OK);
} catch (error) {
return handleError('PartyEvent - Get search', error);
}
}

View File

@@ -1,24 +0,0 @@
###
# Search party events by title
GET http://localhost:7272/api/party-event/search?query=Music
###
# Search party events by location
GET http://localhost:7272/api/party-event/search?query=Central+Park
###
# Search party events by tag
GET http://localhost:7272/api/party-event/search?query=Festival
###
# Combined search with multiple parameters
GET http://localhost:7272/api/party-event/search?query=Summer&location=Hong+Kong&category=Music
###
# No results expected
GET http://localhost:7272/api/party-event/search?query=zzzzzz

View File

@@ -1,51 +0,0 @@
// src/app/api/party-order/update/route.ts
//
// PURPOSE:
// Update party order in db by id
//
// RULES:
// T.B.A.
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { updateOrder } from 'src/app/services/party-order.service';
// ----------------------------------------------------------------------
/** **************************************
* PUT - Update PartyOrder
*************************************** */
export async function PUT(req: NextRequest) {
const { orderData } = await req.json();
const { id } = orderData;
if (!id) return response({ message: 'id not found' }, STATUS.ERROR);
try {
const result = await updateOrder(id, orderData);
return response({ result }, STATUS.OK);
} catch (error) {
console.error('Error updating order:', { orderData });
return handleError('PartyOrder - Update', error);
}
}
export type IOrderItem = {
id: string;
eventId: string;
userId: string;
status: string;
paymentStatus: string;
totalAmount: number;
items: {
id: string;
name: string;
quantity: number;
price: number;
}[];
createdAt: Date;
updatedAt: Date;
};

View File

@@ -1,51 +0,0 @@
###
PUT http://localhost:7272/api/party-order/update
Content-Type: application/json
{
"orderData": {
"id":"e99f09a7-dd88-49d5-b1c8-1daf80c2d7b02",
"taxes": 10.50,
"status": "completed",
"shipping": 5.00,
"discount": 20.00,
"subtotal": 290.00,
"orderNumber": "ORD-2023-001",
"totalAmount": 300.00,
"totalQuantity": 2,
"history": {
"payment": "2023-01-01",
"status_changes": [
"pending",
"paid"
]
},
"payment": {
"method": "credit_card",
"card_last4": "4321"
},
"customer": {
"name": "John Doe",
"email": "john@example.com"
},
"delivery": {
"method": "express",
"tracking_number": "TRK123456"
},
"items": [
{
"id": "ticket_001",
"name": "General Admission",
"quantity": 2,
"price": 150.00,
"status": "used"
}
],
"shippingAddress": {
"street": "123 Main St",
"city": "New York",
"zip": "10001"
}
}
}

View File

@@ -1,60 +0,0 @@
<!-- NOTES to AI: please maintain same format and level of detail when edit this file -->
# GUIDELINE
- Party-user Management API endpoint for managing party-user accounts
- Handles party-user CRUD operations and profile updates
- Follows Next.js API route conventions
## Endpoints
### `create/route.ts`
Creates new party-user account with required details
### `delete/route.ts`
Deletes party-user account by ID
### `details/route.ts`
Gets detailed party-user information
### `helloworld/route.ts`
Simple test endpoint returning "Hello World"
### `list/route.ts`
Lists all party-users with pagination support
### `search/route.ts`
Searches party-users by name or email
### `update/route.ts`
Updates party-user profile information
## Testing
Test files are available per endpoint in their respective directories:
- `create/test.http`
- `delete/test.http`
- `details/test.http`
- `helloworld/test.http`
- `list/test.http`
- `update/test.http`
## Related Services
`../../services/party-user.service.ts` (assumed) would handle:
`createUser` - Register new party-user account
`updateUser` - Update party-user profile
`deleteUser` - Remove party-user account
`listUsers` - Get paginated party-user list
`searchUsers` - Search party-users by criteria
`changeUserRole` - Modify party-user permissions
`uploadUserImage` - Handle profile picture uploads

View File

@@ -1,34 +0,0 @@
// src/app/api/party-user/create/route.ts
//
// PURPOSE:
// create new party user in db
//
// RULES:
// 1. Validates input data shape
// 2. Returns created party user data
//
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { createPartyUser } from 'src/app/services/party-user.service';
// ----------------------------------------------------------------------
/**
***************************************
* POST - create PartyUser
***************************************
*/
export async function POST(req: NextRequest) {
const { partyUserData } = await req.json();
try {
const partyUser = await createPartyUser(partyUserData);
return response({ partyUser }, STATUS.OK);
} catch (error) {
return handleError('PartyUser - Create', error);
}
}

View File

@@ -1,19 +0,0 @@
###
POST http://localhost:7272/api/party-user/create
Content-Type: application/json
{
"partyUserData": {
"name": "Alice 123321",
"username": null,
"email": "alice@123111321.io",
"emailVerified": "2025-06-15T17:47:23.919Z",
"password": "Aa12345678",
"bucketImage": null,
"admin": false,
"info": null,
"phoneNumber": "+85291234567",
"avatarUrl": ""
}
}

View File

@@ -1,35 +0,0 @@
// src/app/api/party-user/delete/route.ts
//
// PURPOSE:
// delete party user from db by id
//
// RULES:
// 1. Requires valid party user ID
// 2. Returns deleted party user ID
// 3. Handles errors appropriately
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { deletePartyUser } from 'src/app/services/party-user.service';
/**
**************************************
* PATCH - Delete party user
***************************************
*/
export async function PATCH(req: NextRequest) {
try {
const { partyUserId } = await req.json();
if (!partyUserId) throw new Error('partyUserId cannot be null');
await deletePartyUser(partyUserId);
return response({ partyUserId }, STATUS.OK);
} catch (error) {
return handleError('PartyUser - Delete', error);
}
}

View File

@@ -1,8 +0,0 @@
###
PATCH http://localhost:7272/api/party-user/delete
Content-Type: application/json
{
"partyUserId": "cmbxz8t2b000oiigxib3o4jla"
}

View File

@@ -1,43 +0,0 @@
// src/app/api/party-user/details/route.ts
//
// PURPOSE:
// get party user details from db by id
//
// RULES:
// 1. Requires valid partyUserId parameter
// 2. Returns party user details if found
// 3. Handles not found case appropriately
// 4. Logs the operation
import type { NextRequest } from 'next/server';
import { logger } from 'src/utils/logger';
import { STATUS, response, handleError } from 'src/utils/response';
import { getPartyUser } from 'src/app/services/party-user.service';
// ----------------------------------------------------------------------
/** **************************************
* GET PartyUser detail
*************************************** */
export async function GET(req: NextRequest) {
try {
const { searchParams } = req.nextUrl;
// RULES: userId must exist
const partyUserId = searchParams.get('partyUserId');
if (!partyUserId) return response({ message: 'partyUserId is required!' }, STATUS.BAD_REQUEST);
// NOTE: userId confirmed exist, run below
const partyUser = await getPartyUser(partyUserId);
if (!partyUser) return response({ message: 'User not found!' }, STATUS.NOT_FOUND);
logger('[User] details', partyUser.id);
return response({ partyUser }, STATUS.OK);
} catch (error) {
return handleError('PartyUser - Get details', error);
}
}

View File

@@ -1,4 +0,0 @@
###
GET http://localhost:7272/api/party-user/details?partyUserId=cmbxziat6000b715hmhrnwlx2

View File

@@ -1,11 +0,0 @@
import type { NextRequest, NextResponse } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
export async function GET(req: NextRequest, res: NextResponse) {
try {
return response({ helloworld: 'party-user' }, STATUS.OK);
} catch (error) {
return handleError('GET - helloworld', error);
}
}

View File

@@ -1,3 +0,0 @@
###
GET http://localhost:7272/api/party-user/helloworld

View File

@@ -1,33 +0,0 @@
// src/app/api/party-user/update/route.ts
//
// PURPOSE:
// update existing party user in db
//
// RULES:
// 1. Requires valid party user ID
// 2. Validates input data shape
//
import { logger } from 'src/utils/logger';
import { STATUS, response, handleError } from 'src/utils/response';
import { listPartyUsers } from 'src/app/services/party-user.service';
// ----------------------------------------------------------------------
/**
***************************************
* GET - Products
***************************************
*/
export async function GET() {
try {
const partyUsers = await listPartyUsers();
logger('[User] list', partyUsers.length);
return response({ partyUsers }, STATUS.OK);
} catch (error) {
return handleError('Product - Get list', error);
}
}

View File

@@ -1,2 +0,0 @@
###
GET http://localhost:7272/api/party-user/list

View File

@@ -1,35 +0,0 @@
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);
}
}

View File

@@ -1,88 +0,0 @@
// src/app/api/party-user/update/route.ts
//
// PURPOSE:
// update existing party user in db
//
// RULES:
// 1. Requires valid party user ID
// 2. Validates input data shape
// 3. Returns updated party user data
// 4. Handles errors appropriately
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { updatePartyUser } from 'src/app/services/party-user.service';
// ----------------------------------------------------------------------
/** **************************************
* PUT - Update PartyUser
*************************************** */
export async function PUT(req: NextRequest) {
// logger('[Product] list', products.length);
const { partyUserData } = await req.json();
try {
await updatePartyUser(partyUserData.id, partyUserData);
return response({ partyUserData }, STATUS.OK);
} catch (error) {
return handleError('PartyUser - Update', 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[];
};

View File

@@ -1,22 +0,0 @@
###
PUT http://localhost:7272/api/party-user/update
Content-Type: application/json
{
"partyUserData": {
"id": "cmc0cedkx000boln3viy77598",
"createdAt": "2025-06-15T17:47:24.547Z",
"updatedAt": "2025-06-15T17:47:24.547Z",
"name": "Alice 123321",
"username": null,
"email": "alice@prisma.io",
"emailVerified": "2025-06-15T17:47:23.919Z",
"password": "Aa12345678",
"image": null,
"bucketImage": null,
"admin": false,
"info": null,
"phoneNumber": "+85291234567"
}
}

View File

@@ -24,7 +24,10 @@ export async function GET(req: NextRequest) {
const posts = _posts(); const posts = _posts();
// Accept search by title or description // Accept search by title or description
const results = posts.filter(({ title, description }) => title.toLowerCase().includes(query) || description?.toLowerCase().includes(query)); const results = posts.filter(
({ title, description }) =>
title.toLowerCase().includes(query) || description?.toLowerCase().includes(query)
);
logger('[Post] search-results', results.length); logger('[Post] search-results', results.length);

View File

@@ -1,43 +0,0 @@
// src/app/api/product/createProduct/route.ts
// REQ0183 frontend product new
//
// PURPOSE:
// create product to db
//
// RULES:
// T.B.A.
//
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { isDev } from 'src/constants';
import { createProduct } from 'src/app/services/product.service';
// ----------------------------------------------------------------------
/** **************************************
* POST - Products
*************************************** */
export async function POST(req: NextRequest) {
const { productData } = await req.json();
try {
if (isDev) {
console.log({ productData });
}
const created = await createProduct(productData);
// const created = await prisma.productItem.create({ data: productData });
if (isDev) {
console.log('create done');
}
return response(created, STATUS.OK);
} catch (error) {
console.log({ hello: 'world', productData });
return handleError('Product - Create', error);
}
}

View File

@@ -1,51 +0,0 @@
###
POST http://localhost:7272/api/product/create
Content-Type: application/json
{
"productData": {
"available": 99,
"category": "T-shirts",
"code": "PD-12345",
"colors": [
"Red"
],
"coverUrl": "",
"description": "this is description, 会員管理機能は、単なるデータ管理ツール以上の価値を持ちます。 企業は顧客の情報や動向を深く理解し、長期的な顧客関係の構築やロイヤルティの確立、そして迅速な市場変動への対応に直結します。 会員管理機能は、現代のビジネスにおいて企業の競争力を高める基盤として欠かせないものとなっています。",
"gender": [
"Men"
],
"images": [
"data:image/png;base64,C",
"data:image/png;base64,C"
],
"inventoryType": "test",
"name": "hello product",
"newLabel": {
"enabled": false,
"content": ""
},
"price": 99.99,
"priceSale": null,
"publish": "yes",
"quantity": 99,
"saleLabel": {
"enabled": false,
"content": ""
},
"sizes": [
"7"
],
"sku": "SK-122345",
"subDescription": "this is test sub-description",
"tags": [
"Travel",
"Finance"
],
"taxes": 0,
"totalRatings": 1,
"totalReviews": 1,
"totalSold": 1
}
}

View File

@@ -0,0 +1,75 @@
// src/app/api/product/createProduct/route.ts
//
// PURPOSE:
// create product 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 - Products
*************************************** */
export async function POST(req: NextRequest) {
// logger('[Product] list', products.length);
const { data } = await req.json();
const createForm: CreateProductData = data as unknown as CreateProductData;
console.log({ createForm });
try {
console.log({ data });
await prisma.productItem.create({ data: createForm });
return response({ hello: 'world' }, STATUS.OK);
} catch (error) {
console.log({ hello: 'world', data });
return handleError('Product - Create', error);
}
}
type CreateProductData = {
// 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;
inventoryType: string;
subDescription: string;
priceSale: number;
newLabel: {
content: string;
enabled: boolean;
};
saleLabel: {
content: string;
enabled: boolean;
};
// ratings: {
// name: string;
// starCount: number;
// reviewCount: number;
// }[];
};

View File

@@ -1,22 +0,0 @@
import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
import { deleteProduct } from 'src/app/services/product.service';
/** **************************************
* PATCH - Delete product
*************************************** */
export async function PATCH(req: NextRequest) {
try {
const { productId } = await req.json();
if (!productId) throw new Error('productId cannot be null');
await deleteProduct(productId);
return response({ productId }, STATUS.OK);
} catch (error) {
return handleError('Product - Delete', error);
}
}

View File

@@ -1,8 +0,0 @@
###
PATCH http://localhost:7272/api/product/delete
Content-Type: application/json
{
"productId" :"e99f09a7-dd88-49d5-b1c8-1daf80c2d7b06"
}

View File

@@ -0,0 +1,44 @@
// src/app/api/product/deleteProduct/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 Products
*************************************** */
export async function DELETE(req: NextRequest) {
try {
const { searchParams } = req.nextUrl;
// RULES: productId must exist
const productId = searchParams.get('productId');
if (!productId) {
return response({ message: 'Product ID is required!' }, STATUS.BAD_REQUEST);
}
// NOTE: productId confirmed exist, run below
const product = await prisma.productItem.delete({ where: { id: productId } });
if (!product) {
return response({ message: 'Product not found!' }, STATUS.NOT_FOUND);
}
logger('[Product] details', product.id);
return response({ product }, STATUS.OK);
} catch (error) {
return handleError('Product - Get details', error);
}
}

View File

@@ -0,0 +1,3 @@
###
DELETE http://localhost:7272/api/product/deleteProduct?productId=e99f09a7-dd88-49d5-b1c8-1daf80c2d7b06

View File

@@ -1,7 +1,5 @@
// src/app/api/product/details/route.ts // src/app/api/product/details/route.ts
// //
// REQ0182 frontend product details
//
// PURPOSE: // PURPOSE:
// get product from db by id // get product from db by id
// //
@@ -15,7 +13,7 @@ import { STATUS, response, handleError } from 'src/utils/response';
import { L_INFO, L_ERROR } from 'src/constants'; import { L_INFO, L_ERROR } from 'src/constants';
import { getProduct } from 'src/app/services/product.service'; import { getProduct } from 'src/app/services/product.service';
import { createAppLog } from 'src/app/services/app-log.service'; import { createAppLog } from 'src/app/services/AppLog.service';
import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest'; import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest';

View File

@@ -1,5 +1,3 @@
### ###
GET http://localhost:7272/api/product/details?productId=e99f09a7-dd88-49d5-b1c8-1daf80c2d7b01
###
GET http://localhost:7272/api/product/details?productId=e99f09a7-dd88-49d5-b1c8-1daf80c2d7b01 GET http://localhost:7272/api/product/details?productId=e99f09a7-dd88-49d5-b1c8-1daf80c2d7b01

View File

@@ -1,16 +0,0 @@
import type { NextRequest, NextResponse } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response';
/**
***************************************
* GET - helloworld
***************************************
*/
export async function GET(req: NextRequest, res: NextResponse) {
try {
return response({ helloworld: 'product' }, STATUS.OK);
} catch (error) {
return handleError('Helloworld - Get all', error);
}
}

Some files were not shown because too many files have changed in this diff Show More