Compare commits

..

7 Commits

1187 changed files with 11647 additions and 5753 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,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

@@ -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,9 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
yarn build
while true; do while true; do
yarn start yarn --dev
yarn dev
echo "restarting..." echo "restarting..."
sleep 1 sleep 1

View File

@@ -19,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 3 --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\"",
@@ -45,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",

View File

@@ -195,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 {
@@ -1144,7 +1144,6 @@ model EventReview {
eventItemId String? eventItemId String?
} }
// NOTE: need to consider with Event
// mapped to IEventItem // mapped to IEventItem
model EventItem { model EventItem {
id String @id @default(uuid()) id String @id @default(uuid())

View File

@@ -1,18 +0,0 @@
#!/usr/bin/env bash
yarn --dev
clear
while true; do
yarn db:push
yarn seed
yarn db:studio &
yarn dev
echo "restarting..."
sleep 1
done

View File

@@ -1,18 +0,0 @@
#!/usr/bin/env bash
set -x
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

@@ -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

@@ -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,46 +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 prisma from '../../../lib/prisma';
import { createProduct } from 'src/app/services/product.service';
// 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

@@ -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);
}
}

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 { listProducts } from 'src/app/services/product.service'; import { listProducts } from 'src/app/services/product.service';
import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest'; import { flattenNextjsRequest } from '../../auth/sign-in/flattenNextjsRequest';

View File

@@ -0,0 +1,23 @@
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 - Products
*************************************** */
export async function GET() {
try {
const products = _products();
logger('[Product] list', products.length);
return response({ products }, STATUS.OK);
} catch (error) {
return handleError('Product - Get list', error);
}
}

View File

@@ -24,14 +24,13 @@ export async function GET(req: NextRequest, res: NextResponse) {
*************************************** ***************************************
*/ */
export async function POST(req: NextRequest) { export async function POST(req: NextRequest) {
const OPERATION = 'Product - Create';
const { data } = await req.json(); const { data } = await req.json();
try { try {
const product = await createProduct(data); const product = await createProduct(data);
return response(OPERATION, STATUS.OK); return response(product, STATUS.CREATED);
} catch (error) { } catch (error) {
return handleError(OPERATION, error); return handleError('Product - Create', error);
} }
} }

View File

@@ -17,58 +17,58 @@ import prisma from '../../../lib/prisma';
/** ************************************** /** **************************************
* GET - Products * GET - Products
*************************************** */ *************************************** */
export async function PUT(req: NextRequest) { export async function POST(req: NextRequest) {
// logger('[Product] list', products.length); // logger('[Product] list', products.length);
const { productData } = await req.json(); const { data } = await req.json();
try { try {
const products = await prisma.productItem.updateMany({ const products = await prisma.productItem.updateMany({
data: { data: {
name: productData.name, name: data.name,
sku: productData.sku, sku: data.sku,
code: productData.code, code: data.code,
price: productData.price, price: data.price,
taxes: productData.taxes, taxes: data.taxes,
tags: productData.tags, tags: data.tags,
sizes: productData.sizes, sizes: data.sizes,
publish: productData.publish, publish: data.publish,
gender: productData.gender, gender: data.gender,
coverUrl: productData.coverUrl, coverUrl: data.coverUrl,
images: productData.images, images: data.images,
colors: productData.colors, colors: data.colors,
quantity: productData.quantity, quantity: data.quantity,
category: productData.category, category: data.category,
available: productData.available, available: data.available,
totalSold: productData.totalSold, totalSold: data.totalSold,
description: productData.description, description: data.description,
totalRatings: productData.totalRatings, totalRatings: data.totalRatings,
totalReviews: productData.totalReviews, totalReviews: data.totalReviews,
inventoryType: productData.inventoryType, inventoryType: data.inventoryType,
subDescription: productData.subDescription, subDescription: data.subDescription,
priceSale: productData.priceSale, priceSale: data.priceSale,
// //
newLabel: { newLabel: {
content: productData.newLabel?.content || '', content: data.newLabel?.content || '',
enabled: productData.newLabel?.enabled ?? false, enabled: data.newLabel?.enabled ?? false,
}, },
saleLabel: { saleLabel: {
content: productData.saleLabel?.content || '', content: data.saleLabel?.content || '',
enabled: productData.saleLabel?.enabled ?? false, enabled: data.saleLabel?.enabled ?? false,
}, },
ratings: { ratings: {
set: productData.ratings.map((rating: { name: string; starCount: number; reviewCount: number }) => ({ set: data.ratings.map((rating: { name: string; starCount: number; reviewCount: number }) => ({
name: rating.name, name: rating.name,
starCount: rating.starCount, starCount: rating.starCount,
reviewCount: rating.reviewCount, reviewCount: rating.reviewCount,
})), })),
}, },
}, },
where: { id: productData.id }, where: { id: data.id },
}); });
return response({ data: productData }, STATUS.OK); return response({ data }, STATUS.OK);
} catch (error) { } catch (error) {
console.log({ data: productData }); console.log({ data });
return handleError('Product - Get list', error); return handleError('Product - Get list', error);
} }
} }

View File

@@ -3,11 +3,11 @@ import type { NextRequest } from 'next/server';
import { logger } from 'src/utils/logger'; import { logger } from 'src/utils/logger';
import { STATUS, response, handleError } from 'src/utils/response'; import { STATUS, response, handleError } from 'src/utils/response';
import { getProductBySkuOrName } from 'src/app/services/product.service'; import { _products } from 'src/_mock/_product';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
// export const runtime = 'edge'; export const runtime = 'edge';
/** ************************************** /** **************************************
* GET - Search products * GET - Search products
@@ -21,9 +21,12 @@ export async function GET(req: NextRequest) {
return response({ results: [] }, STATUS.OK); return response({ results: [] }, STATUS.OK);
} }
const results = await getProductBySkuOrName(query); const products = _products();
logger('[Product] search-results', results?.length); // 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); return response({ results }, STATUS.OK);
} catch (error) { } catch (error) {

View File

@@ -1,8 +0,0 @@
###
GET http://localhost:7272/api/product/search?query=B
###
GET http://localhost:7272/api/product/search?query=Classic
###
GET http://localhost:7272/api/product/search?query=zzzzzz

View File

@@ -1,52 +0,0 @@
###
PUT http://localhost:7272/api/product/update
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,
"ratings":[],
"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

@@ -1,7 +1,6 @@
import prisma from '@/lib/prisma';
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import prisma from 'src/app/lib/prisma';
// GET: 获取所有学生 // GET: 获取所有学生
export async function GET() { export async function GET() {
try { try {

View File

@@ -4,7 +4,7 @@ import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response'; import { STATUS, response, handleError } from 'src/utils/response';
import { changeToAdmin } from 'src/app/services/user-item.service'; import { changeToAdmin } from 'src/app/services/userItem.service';
/** /**
*************************************** ***************************************

View File

@@ -4,7 +4,7 @@ import type { NextRequest } from 'next/server';
import { STATUS, response, handleError } from 'src/utils/response'; import { STATUS, response, handleError } from 'src/utils/response';
import { changeToUser } from 'src/app/services/user-item.service'; import { changeToUser } from 'src/app/services/userItem.service';
/** /**
*************************************** ***************************************

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 { isAdmin } from 'src/app/services/user-item.service'; import { isAdmin } from 'src/app/services/userItem.service';
// import prisma from '../../lib/prisma'; // import prisma from '../../lib/prisma';
@@ -14,8 +14,8 @@ export async function GET(req: NextRequest, res: NextResponse) {
if (!userId) throw new Error('userId cannot be null'); if (!userId) throw new Error('userId cannot be null');
const result = await isAdmin(userId); const result = await isAdmin(userId);
return response(result ? 'true' : 'false', STATUS.OK); return response(result, STATUS.OK);
} catch (error) { } catch (error) {
return handleError('GET - checkAdmin', error); return handleError('Post - Get latest', error);
} }
} }

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 { isAdmin } from 'src/app/services/user-item.service'; import { isAdmin } from 'src/app/services/userItem.service';
// import prisma from '../../lib/prisma'; // import prisma from '../../lib/prisma';
@@ -14,8 +14,8 @@ export async function GET(req: NextRequest, res: NextResponse) {
if (!userId) throw new Error('userId cannot be null'); if (!userId) throw new Error('userId cannot be null');
const result = await isAdmin(userId); const result = await isAdmin(userId);
return response('GET - helloworld', STATUS.OK); return response(result, STATUS.OK);
} catch (error) { } catch (error) {
return handleError('GET - helloworld', error); return handleError('Post - Get latest', error);
} }
} }

View File

@@ -3,7 +3,7 @@
import { logger } from 'src/utils/logger'; import { logger } from 'src/utils/logger';
import { STATUS, response, handleError } from 'src/utils/response'; import { STATUS, response, handleError } from 'src/utils/response';
import { listUsers } from 'src/app/services/user-item.service'; import { listUsers } from 'src/app/services/userItem.service';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

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 { listUsers, deleteUser, updateUser, createNewUser } from 'src/app/services/user-item.service'; import { listUsers, deleteUser, updateUser, createNewUser } from 'src/app/services/userItem.service';
// import prisma from '../../lib/prisma'; // import prisma from '../../lib/prisma';
@@ -22,17 +22,14 @@ export async function GET(req: NextRequest, res: NextResponse) {
*************************************** ***************************************
*/ */
export async function POST(req: NextRequest) { export async function POST(req: NextRequest) {
const OPERATION = 'User - Create';
const { data } = await req.json(); const { data } = await req.json();
try { try {
// TODO: temporary ignore output from function due to `createNewUser` is still a dummy const createResult = await createNewUser(data);
// const createResult = await createNewUser(data);
await createNewUser(data);
return response(OPERATION, STATUS.OK); return response(createResult, STATUS.OK);
} catch (error) { } catch (error) {
return handleError(OPERATION, error); return handleError('User - Create', error);
} }
} }
@@ -51,7 +48,7 @@ export async function PUT(req: NextRequest) {
if (!userId) throw new Error('userId cannot null'); if (!userId) throw new Error('userId cannot null');
const id: number = parseInt(userId); const id: number = parseInt(userId);
const updateResult = await updateUser(id.toString(), data); const updateResult = await updateUser(id, data);
return response(updateResult, STATUS.OK); return response(updateResult, STATUS.OK);
} catch (error) { } catch (error) {
@@ -74,10 +71,10 @@ export async function DELETE(req: NextRequest) {
if (!userId) throw new Error('userId cannot null'); if (!userId) throw new Error('userId cannot null');
const id: number = parseInt(userId); const id: number = parseInt(userId);
await deleteUser(id); const deleteResult = await deleteUser(id);
return response('User - Delete', STATUS.OK); return response(deleteResult, STATUS.OK);
} catch (error) { } catch (error) {
return handleError('User - Delete', error); return handleError('User - Update', 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

@@ -30,15 +30,10 @@ async function listAccessLogs(): Promise<AccessLog[]> {
}); });
} }
// TODO: obsoleted getAccessLog, use getAccessLogById instead
async function getAccessLog(id: string): Promise<AccessLog | null> { async function getAccessLog(id: string): Promise<AccessLog | null> {
return prisma.accessLog.findUnique({ where: { id } }); return prisma.accessLog.findUnique({ where: { id } });
} }
async function getAccessLogById(id: string): Promise<AccessLog | null> {
return prisma.accessLog.findUnique({ where: { id } });
}
async function createAccessLog(userId?: string, message?: string, metadata?: Record<string, any>): Promise<AccessLog> { async function createAccessLog(userId?: string, message?: string, metadata?: Record<string, any>): Promise<AccessLog> {
return prisma.accessLog.create({ return prisma.accessLog.create({
data: { data: {
@@ -49,10 +44,6 @@ async function createAccessLog(userId?: string, message?: string, metadata?: Rec
}); });
} }
function helloworld(): string {
return 'helloworld';
}
// async function update(id: string, data: UpdateAccessLog): Promise<AccessLog> { // async function update(id: string, data: UpdateAccessLog): Promise<AccessLog> {
// return prisma.accessLog.update({ // return prisma.accessLog.update({
// where: { id }, // where: { id },
@@ -72,7 +63,4 @@ export {
getAccessLog, getAccessLog,
listAccessLogs, listAccessLogs,
createAccessLog, createAccessLog,
getAccessLogById,
//
helloworld,
}; };

View File

@@ -6,14 +6,11 @@
// RULES: // RULES:
// - Follows same pattern as helloworld.service.ts // - Follows same pattern as helloworld.service.ts
// //
import type { Event } from '@prisma/client';
import prisma from '../lib/prisma';
type CreateEvent = { type CreateEvent = {
eventDate: Date; eventDate: DateTime;
title: string; title: string;
joinMembers?: JSON[]; joinMembers?: Json[];
price: number; price: number;
currency: string; currency: string;
duration_m: number; duration_m: number;
@@ -25,9 +22,9 @@ type CreateEvent = {
}; };
type UpdateEvent = { type UpdateEvent = {
eventDate?: Date; eventDate?: DateTime;
title?: string; title?: string;
joinMembers?: JSON[]; joinMembers?: Json[];
price?: number; price?: number;
currency?: string; currency?: string;
duration_m?: number; duration_m?: number;
@@ -38,9 +35,9 @@ type UpdateEvent = {
memberId?: number; memberId?: number;
}; };
async function listEvents(): Promise<Event[]> { // async function listEvents(): Promise<Event[]> {
return prisma.event.findMany(); // return prisma.event.findMany();
} // }
// async function getEvent(eventId: number) { // async function getEvent(eventId: number) {
// return prisma.event.findFirst({ where: { id: eventId } }); // return prisma.event.findFirst({ where: { id: eventId } });

View File

@@ -12,9 +12,9 @@ import type { EventItem } from '@prisma/client';
import prisma from '../lib/prisma'; import prisma from '../lib/prisma';
type CreateEvent = { type CreateEvent = {
eventDate: Date; eventDate: DateTime;
title: string; title: string;
joinMembers?: JSON[]; joinMembers?: Json[];
price: number; price: number;
currency: string; currency: string;
duration_m: number; duration_m: number;
@@ -26,9 +26,9 @@ type CreateEvent = {
}; };
type UpdateEvent = { type UpdateEvent = {
eventDate?: Date; eventDate?: DateTime;
title?: string; title?: string;
joinMembers?: JSON[]; joinMembers?: Json[];
price?: number; price?: number;
currency?: string; currency?: string;
duration_m?: number; duration_m?: number;

View File

@@ -11,24 +11,9 @@ import type { OrderItem } from '@prisma/client';
import prisma from '../lib/prisma'; import prisma from '../lib/prisma';
type CreateOrderItem = { type CreateOrderItem = {
// orderNumber?: string; orderNumber?: string;
// status?: string; // status?: string;
// eventIds?: number[]; // eventIds?: number[];
taxes: number;
status: string;
shipping: number;
discount: number;
subtotal: number;
orderNumber: string;
totalAmount: number;
totalQuantity: number;
history: Record<string, any>;
payment: Record<string, any>;
customer: Record<string, any>;
delivery: Record<string, any>;
items: Record<string, any>[];
shippingAddress: Record<string, any>;
}; };
type UpdateOrderItem = { type UpdateOrderItem = {

View File

@@ -1,7 +1,5 @@
// src/app/services/product.service.ts // src/app/services/product.service.ts
// //
// REQ0182 frontend product details
//
// PURPOSE: // PURPOSE:
// - Service for handling ProductItem Record // - Service for handling ProductItem Record
// //
@@ -67,29 +65,39 @@ type UpdateProduct = {
}; };
async function listProducts(): Promise<ProductItem[]> { async function listProducts(): Promise<ProductItem[]> {
return prisma.productItem.findMany({ return prisma.productItem.findMany();
include: { reviews: true },
});
} }
async function getProduct(productId: string): Promise<ProductItem | null> { async function getProduct(productId: string): Promise<ProductItem | null> {
return prisma.productItem.findUnique({ return prisma.productItem.findUnique({ where: { id: productId } });
where: { id: productId },
include: { reviews: true },
//
});
} }
async function getProductBySkuOrName(searchText: string): Promise<ProductItem[] | null> { async function createProduct(createForm: CreateProduct) {
return prisma.productItem.findMany({ // return prisma.productItem.create({
where: { OR: [{ sku: { contains: searchText, mode: 'insensitive' } }, { name: { contains: searchText, mode: 'insensitive' } }] }, // data: {
include: { reviews: true }, // ...createForm,
// // code: createForm.code || '',
}); // taxes: createForm.taxes || 0,
} // tags: createForm.tags || [],
// sizes: createForm.sizes || [],
async function createProduct(productData: any) { // gender: createForm.gender || [],
return await prisma.productItem.create({ data: productData }); // colors: createForm.colors || [],
// category: createForm.category || '',
// quantity: createForm.quantity || 0,
// available: createForm.available || 0,
// coverUrl: createForm.coverUrl || '',
// images: createForm.images || [],
// description: createForm.description || '',
// subDescription: createForm.subDescription || '',
// publish: createForm.publish || 'published',
// totalSold: createForm.totalSold || 0,
// totalRatings: createForm.totalRatings || 0,
// totalReviews: createForm.totalReviews || 0,
// inventoryType: createForm.inventoryType || '',
// ratings: createForm.ratings || [],
// reviews: createForm.reviews || [],
// },
// });
} }
async function updateProduct(productId: string, updateForm: UpdateProduct) { async function updateProduct(productId: string, updateForm: UpdateProduct) {
@@ -103,4 +111,4 @@ async function deleteProduct(productId: string) {
return prisma.productItem.delete({ where: { id: productId } }); return prisma.productItem.delete({ where: { id: productId } });
} }
export { getProduct, listProducts, createProduct, updateProduct, deleteProduct, getProductBySkuOrName, type CreateProduct, type UpdateProduct }; export { getProduct, listProducts, createProduct, updateProduct, deleteProduct, type CreateProduct, type UpdateProduct };

View File

@@ -38,7 +38,7 @@ async function getUserItem(userId: string): Promise<UserItem | null> {
return prisma.userItem.findFirst({ where: { id: userId } }); return prisma.userItem.findFirst({ where: { id: userId } });
} }
async function updateUser(userId: string, updateForm: UpdateUser): Promise<UserItem> { async function updateUser(userId: string, updateForm: UpdateUser): Promise<User> {
return prisma.userItem.update({ return prisma.userItem.update({
where: { id: userId }, where: { id: userId },
data: updateForm, data: updateForm,
@@ -82,7 +82,7 @@ async function changeToUser(userIdToPromote: string, userIdOfApplicant: string)
return promoteResult; return promoteResult;
} }
async function getUserById(id: string): Promise<UserItem | null> { async function getUserById1(id: string): Promise<UserItem | null> {
return prisma.userItem.findFirst({ where: { id } }); return prisma.userItem.findFirst({ where: { id } });
} }

View File

@@ -1,9 +1,7 @@
const isDev = process.env.NODE_ENV === 'development';
const L_ERROR = 0; const L_ERROR = 0;
const L_WARN = 1; const L_WARN = 1;
const L_INFO = 2; const L_INFO = 2;
const L_DEBUG = 3; const L_DEBUG = 3;
const L_TRACE = 4; const L_TRACE = 4;
export { L_WARN, L_INFO, L_ERROR, L_DEBUG, L_TRACE, isDev }; export { L_WARN, L_INFO, L_ERROR, L_DEBUG, L_TRACE };

View File

@@ -13,7 +13,8 @@ type ConfigType = {
export const CONFIG: ConfigType = { export const CONFIG: ConfigType = {
appVersion: packageJson.version, appVersion: packageJson.version,
basePath: process.env.NODE_ENV === 'production' ? process.env.PRODUCTION_API : process.env.DEV_API, basePath:
process.env.NODE_ENV === 'production' ? process.env.PRODUCTION_API : process.env.DEV_API,
cors: { cors: {
/** /**
* [] = allow all origins * [] = allow all origins

View File

@@ -2,6 +2,7 @@ import bcrypt from 'bcrypt';
const generateHash = async (password: string) => bcrypt.hash(password, await bcrypt.genSalt(10)); const generateHash = async (password: string) => bcrypt.hash(password, await bcrypt.genSalt(10));
const validatePassword = async (password: string, hash: string) => bcrypt.compare(password, hash as string); const validatePassword = async (password: string, hash: string) =>
bcrypt.compare(password, hash as string);
export { generateHash, validatePassword }; export { generateHash, validatePassword };

View File

@@ -40,7 +40,11 @@ export function logger(label: string, value: unknown, breakLine: boolean = false
if (value === null || value === undefined) { if (value === null || value === undefined) {
formattedValue = String(value); formattedValue = String(value);
} else if (type === 'object') { } else if (type === 'object') {
formattedValue = JSON.stringify(value, null, breakLine || JSON.stringify(value).length > 50 ? 2 : 0); formattedValue = JSON.stringify(
value,
null,
breakLine || JSON.stringify(value).length > 50 ? 2 : 0
);
} else { } else {
formattedValue = String(value); formattedValue = String(value);
} }

View File

@@ -15,8 +15,10 @@ export function setDate(now: Date, options: { days?: number; hours?: number; min
return new Date(`${year}-${month}-${days ?? today} ${hours}:${minutes}`).toJSON(); return new Date(`${year}-${month}-${days ?? today} ${hours}:${minutes}`).toJSON();
} }
export const subHours = (value: number, option: 'years' | 'months' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds') => export const subHours = (
dayjs().subtract(value, option).format(); value: number,
option: 'years' | 'months' | 'days' | 'hours' | 'minutes' | 'seconds' | 'milliseconds'
) => dayjs().subtract(value, option).format();
// years, // years,
// months, // months,
@@ -36,7 +38,15 @@ export type DurationProps = {
milliseconds?: number; milliseconds?: number;
}; };
export function fSub({ years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0, milliseconds = 0 }: DurationProps) { export function fSub({
years = 0,
months = 0,
days = 0,
hours = 0,
minutes = 0,
seconds = 0,
milliseconds = 0,
}: DurationProps) {
const result = dayjs() const result = dayjs()
.subtract( .subtract(
dayjs.duration({ dayjs.duration({
@@ -54,7 +64,15 @@ export function fSub({ years = 0, months = 0, days = 0, hours = 0, minutes = 0,
return result; return result;
} }
export function fAdd({ years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0, milliseconds = 0 }: DurationProps) { export function fAdd({
years = 0,
months = 0,
days = 0,
hours = 0,
minutes = 0,
seconds = 0,
milliseconds = 0,
}: DurationProps) {
const result = dayjs() const result = dayjs()
.add( .add(
dayjs.duration({ dayjs.duration({

View File

@@ -34,16 +34,16 @@
"node_modules", "node_modules",
".next", ".next",
// //
"**/* copy *.*", "**/* copy *.tsx",
"**/* copy.*", "**/* copy.tsx",
"**/*.bak",
"**/*.bak", "**/*.bak",
"**/*.bug", "**/*.bug",
"**/*.del", "**/*.del",
"**/*.draft", "**/*.draft",
"**/*.log", "**/*.log",
"**/*.tmp", "**/*.tmp",
"**/*del", "**/*del"
"prisma/*"
], ],
"include": [ "include": [
"next-env.d.ts", "next-env.d.ts",

View File

@@ -770,13 +770,6 @@
dependencies: dependencies:
tslib "^2.4.0" tslib "^2.4.0"
"@types/bcrypt@^5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@types/bcrypt/-/bcrypt-5.0.2.tgz#22fddc11945ea4fbc3655b3e8b8847cc9f811477"
integrity sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==
dependencies:
"@types/node" "*"
"@types/estree@^1.0.6": "@types/estree@^1.0.6":
version "1.0.7" version "1.0.7"
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz" resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz"
@@ -797,13 +790,6 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.17.tgz#fb85a04f47e9e4da888384feead0de05f7070355" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.17.tgz#fb85a04f47e9e4da888384feead0de05f7070355"
integrity sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ== integrity sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==
"@types/node@*":
version "24.0.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.1.tgz#e9bfcb1c35547437c294403b7bec497772a88b0a"
integrity sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==
dependencies:
undici-types "~7.8.0"
"@types/node@^22.13.13": "@types/node@^22.13.13":
version "22.13.13" version "22.13.13"
resolved "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz" resolved "https://registry.npmjs.org/@types/node/-/node-22.13.13.tgz"
@@ -3500,11 +3486,6 @@ undici-types@~6.20.0:
resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz" resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz"
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
undici-types@~7.8.0:
version "7.8.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.8.0.tgz#de00b85b710c54122e44fbfd911f8d70174cd294"
integrity sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==
unique-names-generator@^4.7.1: unique-names-generator@^4.7.1:
version "4.7.1" version "4.7.1"
resolved "https://registry.yarnpkg.com/unique-names-generator/-/unique-names-generator-4.7.1.tgz#966407b12ba97f618928f77322cfac8c80df5597" resolved "https://registry.yarnpkg.com/unique-names-generator/-/unique-names-generator-4.7.1.tgz#966407b12ba97f618928f77322cfac8c80df5597"

View File

@@ -1,23 +1,14 @@
#
# REQ0180 service port schedule
#
services: services:
frontend: frontend:
command: "sleep infinity" command: 'sleep infinity'
ports:
- 8080:8080
mobile: mobile:
command: "sleep infinity" command: 'sleep infinity'
ports:
- 8081:3000
cms_backend: cms_backend:
command: "sleep infinity" command: 'sleep infinity'
ports:
- 7272:7272
- 5555:5555
postgres: postgres:
# container_name: postgres
ports: ports:
- 5432:5432 - '5432:5432'

View File

@@ -1,6 +1,3 @@
#
# REQ0180 service port schedule
#
volumes: volumes:
db: db:
driver: local driver: local
@@ -14,8 +11,8 @@ services:
- 10001:8080 - 10001:8080
volumes: volumes:
- ../frontend:/app - ../frontend:/app
working_dir: "/app" working_dir: '/app'
command: "./scripts/20_prod.sh" command: './dev.sh'
mobile: mobile:
image: 192.168.10.61:5000/hksingleparty_mobile image: 192.168.10.61:5000/hksingleparty_mobile
@@ -25,8 +22,8 @@ services:
- 10004:3000 - 10004:3000
volumes: volumes:
- ../mobile:/app - ../mobile:/app
working_dir: "/app" working_dir: '/app'
command: "./scripts/20_prod.sh" command: './dev.sh'
cms_backend: cms_backend:
image: 192.168.10.61:5000/demo_minimal_kit_backend image: 192.168.10.61:5000/demo_minimal_kit_backend
@@ -39,8 +36,8 @@ services:
- 10003:5555 - 10003:5555
volumes: volumes:
- ../cms_backend:/app - ../cms_backend:/app
working_dir: "/app" working_dir: '/app'
command: "./scripts/20_prod.sh" command: './dev.sh'
postgres: postgres:
image: postgres:14.1-alpine image: postgres:14.1-alpine
@@ -48,6 +45,6 @@ services:
env_file: env_file:
- .env - .env
expose: expose:
- "5432" - '5432'
volumes: volumes:
- db:/var/lib/postgresql/data - db:/var/lib/postgresql/data

View File

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

View File

@@ -2,9 +2,8 @@
yarn --dev yarn --dev
clear
while true; do while true; do
# yarn tsc:print # yarn tsc:print
yarn lint:print yarn lint:print

View File

@@ -92,8 +92,6 @@ const sortImportsRules = () => {
}; };
return { return {
'perfectionist/sort-named-imports': [1, { type: 'line-length', order: 'asc' }],
'perfectionist/sort-named-exports': [1, { type: 'line-length', order: 'asc' }],
'perfectionist/sort-exports': [ 'perfectionist/sort-exports': [
1, 1,
{ {
@@ -102,6 +100,8 @@ const sortImportsRules = () => {
groupKind: 'values-first', groupKind: 'values-first',
}, },
], ],
'perfectionist/sort-named-imports': [1, { type: 'line-length', order: 'asc' }],
'perfectionist/sort-named-exports': [1, { type: 'line-length', order: 'asc' }],
'perfectionist/sort-imports': [ 'perfectionist/sort-imports': [
2, 2,
{ {

View File

@@ -21,10 +21,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 3 --ext ts,tsx --exec \"yarn tsc\"",
"tsc:watch": "tsc --noEmit --watch", "tsc:watch": "tsc --noEmit --watch",
"tsc": "tsc --noEmit" "tsc:print": "npx tsc --showConfig"
}, },
"engines": { "engines": {
"node": ">=20" "node": ">=20"
@@ -45,7 +43,6 @@
"@fontsource-variable/noto-sans-sc": "^5.2.5", "@fontsource-variable/noto-sans-sc": "^5.2.5",
"@fontsource-variable/noto-sans-tc": "^5.2.5", "@fontsource-variable/noto-sans-tc": "^5.2.5",
"@fontsource-variable/nunito-sans": "^5.2.5", "@fontsource-variable/nunito-sans": "^5.2.5",
"@fontsource-variable/public-sans": "^5.2.5",
"@fontsource/barlow": "^5.2.5", "@fontsource/barlow": "^5.2.5",
"@fullcalendar/core": "^6.1.15", "@fullcalendar/core": "^6.1.15",
"@fullcalendar/daygrid": "^6.1.15", "@fullcalendar/daygrid": "^6.1.15",
@@ -55,7 +52,7 @@
"@fullcalendar/timegrid": "^6.1.15", "@fullcalendar/timegrid": "^6.1.15",
"@fullcalendar/timeline": "^6.1.15", "@fullcalendar/timeline": "^6.1.15",
"@hookform/resolvers": "^4.1.3", "@hookform/resolvers": "^4.1.3",
"@ianvs/prettier-plugin-sort-imports": "^4.4.2", "@ianvs/prettier-plugin-sort-imports": "^4.4.1",
"@iconify/react": "^5.2.0", "@iconify/react": "^5.2.0",
"@mui/lab": "^7.0.0-beta.10", "@mui/lab": "^7.0.0-beta.10",
"@mui/material": "^7.0.1", "@mui/material": "^7.0.1",

View File

@@ -12,7 +12,7 @@ const config = {
trailingComma: 'es5', trailingComma: 'es5',
plugins: [ plugins: [
// //
'@ianvs/prettier-plugin-sort-imports', // '@ianvs/prettier-plugin-sort-imports',
], ],
}; };

View File

@@ -1,15 +0,0 @@
#!/usr/bin/env bash
set -x
rm -rf ./**/*Zone.Identifier
set -ex
yarn fm:fix
yarn tsc
yarn build
echo "done"

View File

@@ -1,4 +1,4 @@
import { error, info, primary, secondary, success, warning } from 'src/theme/core'; import { info, error, primary, success, warning, secondary } from 'src/theme/core';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@@ -1,5 +1,5 @@
import { _mock } from './_mock'; import { _mock } from './_mock';
import { _fileNames, _tags } from './assets'; import { _tags, _fileNames } from './assets';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@@ -1,7 +1,8 @@
import { fAdd, fSub } from 'src/utils/format-time'; import { fSub, fAdd } from 'src/utils/format-time';
import { _mock } from './_mock'; import { _mock } from './_mock';
import { _addressBooks } from './_others';
import { _tags } from './assets'; import { _tags } from './assets';
import { _addressBooks } from './_others';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@@ -1,34 +1,36 @@
import { CONFIG } from 'src/global-config';
import { fSub } from 'src/utils/format-time'; import { fSub } from 'src/utils/format-time';
import { CONFIG } from 'src/global-config';
import { import {
_ages,
_booleans,
_companyNames,
_countryNames,
_courseNames,
_descriptions,
_emails,
_eventNames,
_fileNames,
_firstNames,
_fullAddress,
_fullNames,
_id, _id,
_jobTitles, _ages,
_lastNames,
_nativeL,
_nativeM,
_nativeS,
_percents,
_phoneNumbers,
_postTitles,
_prices,
_productNames,
_ratings,
_roles, _roles,
_prices,
_emails,
_ratings,
_nativeS,
_nativeM,
_nativeL,
_percents,
_booleans,
_sentences, _sentences,
_taskNames, _lastNames,
_fullNames,
_tourNames, _tourNames,
_jobTitles,
_taskNames,
_fileNames,
_postTitles,
_firstNames,
_eventNames,
_courseNames,
_fullAddress,
_companyNames,
_productNames,
_descriptions,
_phoneNumbers,
_countryNames,
} from './assets'; } from './assets';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@@ -1,5 +1,7 @@
import { CONFIG } from 'src/global-config';
import { today } from 'src/utils/format-time'; import { today } from 'src/utils/format-time';
import { CONFIG } from 'src/global-config';
import { _mock } from './_mock'; import { _mock } from './_mock';
// APP // APP

View File

@@ -1,8 +1,10 @@
import { useMemo } from 'react';
import { endpoints, fetcher } from 'src/lib/axios';
import type { IPostItem } from 'src/types/blog';
import type { SWRConfiguration } from 'swr'; import type { SWRConfiguration } from 'swr';
import type { IPostItem } from 'src/types/blog';
import useSWR from 'swr'; import useSWR from 'swr';
import { useMemo } from 'react';
import { fetcher, endpoints } from 'src/lib/axios';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@@ -1,9 +1,11 @@
import { useMemo } from 'react';
import axios, { endpoints, fetcher } from 'src/lib/axios';
import type { ICalendarEvent } from 'src/types/calendar';
import type { SWRConfiguration } from 'swr'; import type { SWRConfiguration } from 'swr';
import type { ICalendarEvent } from 'src/types/calendar';
import { useMemo } from 'react';
import useSWR, { mutate } from 'swr'; import useSWR, { mutate } from 'swr';
import axios, { fetcher, endpoints } from 'src/lib/axios';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
const enableServer = false; const enableServer = false;

View File

@@ -1,10 +1,12 @@
import { keyBy } from 'es-toolkit';
import { useMemo } from 'react';
import axios, { endpoints, fetcher } from 'src/lib/axios';
import type { IChatConversation, IChatMessage, IChatParticipant } from 'src/types/chat';
import type { SWRConfiguration } from 'swr'; import type { SWRConfiguration } from 'swr';
import type { IChatMessage, IChatParticipant, IChatConversation } from 'src/types/chat';
import { useMemo } from 'react';
import { keyBy } from 'es-toolkit';
import useSWR, { mutate } from 'swr'; import useSWR, { mutate } from 'swr';
import axios, { fetcher, endpoints } from 'src/lib/axios';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
const enableServer = false; const enableServer = false;

View File

@@ -1,9 +1,11 @@
import type { UniqueIdentifier } from '@dnd-kit/core';
import { startTransition, useMemo } from 'react';
import axios, { endpoints, fetcher } from 'src/lib/axios';
import type { IKanban, IKanbanColumn, IKanbanTask } from 'src/types/kanban';
import type { SWRConfiguration } from 'swr'; import type { SWRConfiguration } from 'swr';
import type { UniqueIdentifier } from '@dnd-kit/core';
import type { IKanban, IKanbanTask, IKanbanColumn } from 'src/types/kanban';
import useSWR, { mutate } from 'swr'; import useSWR, { mutate } from 'swr';
import { useMemo, startTransition } from 'react';
import axios, { fetcher, endpoints } from 'src/lib/axios';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@@ -1,9 +1,11 @@
import { keyBy } from 'es-toolkit';
import { useMemo } from 'react';
import { endpoints, fetcher } from 'src/lib/axios';
import type { IMail, IMailLabel } from 'src/types/mail';
import type { SWRConfiguration } from 'swr'; import type { SWRConfiguration } from 'swr';
import type { IMail, IMailLabel } from 'src/types/mail';
import useSWR from 'swr'; import useSWR from 'swr';
import { useMemo } from 'react';
import { keyBy } from 'es-toolkit';
import { fetcher, endpoints } from 'src/lib/axios';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------

View File

@@ -1,8 +1,8 @@
// src/actions/order.ts // src/actions/order.ts
import { useMemo } from 'react'; import { useMemo } from 'react';
import axiosInstance, { endpoints, fetcher } from 'src/lib/axios'; import axiosInstance, { endpoints, fetcher } from 'src/lib/axios';
import type { IOrderItem } from 'src/types/order';
import type { IProductItem } from 'src/types/product'; import type { IProductItem } from 'src/types/product';
import type { IOrderItem } from 'src/types/order';
import type { SWRConfiguration } from 'swr'; import type { SWRConfiguration } from 'swr';
import useSWR from 'swr'; import useSWR from 'swr';

View File

@@ -1,10 +1,9 @@
// src/actions/product.ts // src/actions/product.ts
//
import { useMemo } from 'react'; import { useMemo } from 'react';
import axiosInstance, { endpoints, fetcher } from 'src/lib/axios'; import axiosInstance, { endpoints, fetcher } from 'src/lib/axios';
import type { IProductItem } from 'src/types/product'; import type { IProductItem } from 'src/types/product';
import type { SWRConfiguration } from 'swr'; import type { SWRConfiguration } from 'swr';
import useSWR, { mutate } from 'swr'; import useSWR from 'swr';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -23,7 +22,11 @@ type ProductsData = {
export function useGetProducts() { export function useGetProducts() {
const url = endpoints.product.list; const url = endpoints.product.list;
const { data, isLoading, error, isValidating } = useSWR<ProductsData>(url, fetcher, swrOptions); const { data, isLoading, error, isValidating, mutate } = useSWR<ProductsData>(
url,
fetcher,
swrOptions
);
const memoizedValue = useMemo( const memoizedValue = useMemo(
() => ({ () => ({
@@ -32,8 +35,9 @@ export function useGetProducts() {
productsError: error, productsError: error,
productsValidating: isValidating, productsValidating: isValidating,
productsEmpty: !isLoading && !isValidating && !data?.products.length, productsEmpty: !isLoading && !isValidating && !data?.products.length,
mutate,
}), }),
[data?.products, error, isLoading, isValidating] [data?.products, error, isLoading, isValidating, mutate]
); );
return memoizedValue; return memoizedValue;
@@ -52,11 +56,10 @@ export function useGetProduct(productId: string) {
const memoizedValue = useMemo( const memoizedValue = useMemo(
() => ({ () => ({
product: data?.product, currentProduct: data?.product,
productLoading: isLoading, productLoading: isLoading,
productError: error, productError: error,
productValidating: isValidating, productValidating: isValidating,
mutate,
}), }),
[data?.product, error, isLoading, isValidating] [data?.product, error, isLoading, isValidating]
); );
@@ -94,82 +97,140 @@ export function useSearchProducts(query: string) {
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export async function createProduct(productData: IProductItem) { type SaveProductData = {
/** // id: string;
* Work on server
*/
const data = { productData };
const {
data: { id },
} = await axiosInstance.post(endpoints.product.create, data);
/** sku: string;
* Work in local name: string;
*/ code: string;
mutate( price: number | null;
endpoints.product.list, taxes: number | null;
(currentData: any) => { tags: string[];
const currentProducts: IProductItem[] = currentData?.products; sizes: string[];
// publish: string;
gender: string[];
// coverUrl: string;
images: (string | File)[];
colors: string[];
quantity: number | null;
category: string;
// available: number;
// totalSold: number;
description: string;
// totalRatings: number;
// totalReviews: number;
// inventoryType: string;
subDescription: string;
priceSale: number | null;
newLabel: {
content: string;
enabled: boolean;
};
saleLabel: {
content: string;
enabled: boolean;
};
// ratings: {
// name: string;
// starCount: number;
// reviewCount: number;
// }[];
};
const products = [...currentProducts, { ...productData, id }]; export async function saveProduct(productId: string, saveProductData: SaveProductData) {
console.log('save product ?');
// const url = productId ? [endpoints.product.details, { params: { productId } }] : '';
return { ...currentData, products }; const res = await axiosInstance.post('http://localhost:7272/api/product/saveProduct', {
}, data: saveProductData,
false });
);
return res;
}
export async function uploadProductImage(saveProductData: SaveProductData) {
console.log('save product ?');
// const url = productId ? [endpoints.product.details, { params: { productId } }] : '';
const res = await axiosInstance.get('http://localhost:7272/api/product/helloworld');
return res;
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export async function updateProduct(productData: Partial<IProductItem>) { type CreateProductData = {
/** // id: string;
* Work on server sku: string;
*/ name: string;
const data = { productData }; code: string;
await axiosInstance.put(endpoints.product.update, data); price: number | null;
taxes: number | null;
tags: string[];
sizes: string[];
publish: string;
gender: string[];
coverUrl: string;
images: (string | File)[];
colors: string[];
quantity: number | null;
category: string;
available: number;
totalSold: number;
description: string;
totalRatings: number;
totalReviews: number;
inventoryType: string;
subDescription: string;
priceSale: number | null;
newLabel: {
content: string;
enabled: boolean;
};
saleLabel: {
content: string;
enabled: boolean;
};
// ratings: {
// name: string;
// starCount: number;
// reviewCount: number;
// }[];
};
/** export async function createProduct(createProductData: CreateProductData) {
* Work in local console.log('create product ?');
*/ // const url = productId ? [endpoints.product.details, { params: { productId } }] : '';
mutate( const res = await axiosInstance.post('http://localhost:7272/api/product/createProduct', {
endpoints.product.list, data: createProductData,
(currentData: any) => { });
const currentProducts: IProductItem[] = currentData?.products;
const products = currentProducts.map((product) => return res;
product.id === productData.id ? { ...product, ...productData } : product
);
return { ...currentData, products };
},
false
);
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export async function deleteProduct(productId: string) { type DeleteProductResponse = {
/** success: boolean;
* Work on server message?: string;
*/ };
const data = { productId };
await axiosInstance.patch(endpoints.product.delete, data);
/** export async function deleteProduct(productId: string): Promise<DeleteProductResponse> {
* Work in local const url = `http://localhost:7272/api/product/deleteProduct?productId=${productId}`;
*/
mutate( try {
endpoints.product.list, const res = await axiosInstance.delete(url);
(currentData: any) => { console.log({ res });
console.log({ currentData });
const currentProducts: IProductItem[] = currentData?.products;
const products = currentProducts.filter((product) => product.id !== productId); return {
success: true,
return { ...currentData, products }; message: 'Product deleted successfully',
}, };
false } catch (error) {
); return {
success: false,
message: error instanceof Error ? error.message : 'Failed to delete product',
};
}
} }

View File

@@ -1,7 +1,9 @@
import type { SvgIconProps } from '@mui/material/SvgIcon'; import type { SvgIconProps } from '@mui/material/SvgIcon';
import SvgIcon from '@mui/material/SvgIcon';
import { memo } from 'react'; import { memo } from 'react';
import SvgIcon from '@mui/material/SvgIcon';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
function EmailInboxIcon({ sx, ...other }: SvgIconProps) { function EmailInboxIcon({ sx, ...other }: SvgIconProps) {

View File

@@ -1,7 +1,9 @@
import type { SvgIconProps } from '@mui/material/SvgIcon'; import type { SvgIconProps } from '@mui/material/SvgIcon';
import SvgIcon from '@mui/material/SvgIcon';
import { memo } from 'react'; import { memo } from 'react';
import SvgIcon from '@mui/material/SvgIcon';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
function NewPasswordIcon({ sx, ...other }: SvgIconProps) { function NewPasswordIcon({ sx, ...other }: SvgIconProps) {

View File

@@ -1,7 +1,9 @@
import type { SvgIconProps } from '@mui/material/SvgIcon'; import type { SvgIconProps } from '@mui/material/SvgIcon';
import SvgIcon from '@mui/material/SvgIcon';
import { memo } from 'react'; import { memo } from 'react';
import SvgIcon from '@mui/material/SvgIcon';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
function PasswordIcon({ sx, ...other }: SvgIconProps) { function PasswordIcon({ sx, ...other }: SvgIconProps) {

View File

@@ -1,7 +1,9 @@
import type { SvgIconProps } from '@mui/material/SvgIcon'; import type { SvgIconProps } from '@mui/material/SvgIcon';
import SvgIcon from '@mui/material/SvgIcon';
import { memo } from 'react'; import { memo } from 'react';
import SvgIcon from '@mui/material/SvgIcon';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
function PlanFreeIcon({ sx, ...other }: SvgIconProps) { function PlanFreeIcon({ sx, ...other }: SvgIconProps) {

View File

@@ -1,7 +1,9 @@
import type { SvgIconProps } from '@mui/material/SvgIcon'; import type { SvgIconProps } from '@mui/material/SvgIcon';
import SvgIcon from '@mui/material/SvgIcon';
import { memo } from 'react'; import { memo } from 'react';
import SvgIcon from '@mui/material/SvgIcon';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
function PlanPremiumIcon({ sx, ...other }: SvgIconProps) { function PlanPremiumIcon({ sx, ...other }: SvgIconProps) {

View File

@@ -1,7 +1,9 @@
import type { SvgIconProps } from '@mui/material/SvgIcon'; import type { SvgIconProps } from '@mui/material/SvgIcon';
import SvgIcon from '@mui/material/SvgIcon';
import { memo } from 'react'; import { memo } from 'react';
import SvgIcon from '@mui/material/SvgIcon';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
function PlanStarterIcon({ sx, ...other }: SvgIconProps) { function PlanStarterIcon({ sx, ...other }: SvgIconProps) {

View File

@@ -1,7 +1,9 @@
import type { SvgIconProps } from '@mui/material/SvgIcon'; import type { SvgIconProps } from '@mui/material/SvgIcon';
import SvgIcon from '@mui/material/SvgIcon';
import { memo } from 'react'; import { memo } from 'react';
import SvgIcon from '@mui/material/SvgIcon';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
function SentIcon({ sx, ...other }: SvgIconProps) { function SentIcon({ sx, ...other }: SvgIconProps) {

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