diff --git a/03_source/cms_backend/prisma/schema.prisma b/03_source/cms_backend/prisma/schema.prisma index 5e930e9..ee4a2a5 100644 --- a/03_source/cms_backend/prisma/schema.prisma +++ b/03_source/cms_backend/prisma/schema.prisma @@ -511,7 +511,7 @@ model UserCard { } model UserItem { - id Int @id @default(autoincrement()) + id String @id @default(uuid()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // diff --git a/03_source/cms_backend/prisma/seeds/userItem.ts b/03_source/cms_backend/prisma/seeds/userItem.ts index 852ab14..bd9a055 100644 --- a/03_source/cms_backend/prisma/seeds/userItem.ts +++ b/03_source/cms_backend/prisma/seeds/userItem.ts @@ -20,13 +20,13 @@ async function userItem() { const username = `${firstName.toLowerCase()}-${lastName.toLowerCase()}`; const alice = await prisma.userItem.upsert({ - where: { id: 0 }, + where: { id: 'admin_uuid' }, update: {}, create: { - name: `${firstName} ${lastName}`, + name: `admin test`, city: '', role: '', - email: `${username}@${SEED_EMAIL_DOMAIN}`, + email: `admin@123.com`, state: '', status: '', address: '', @@ -37,8 +37,8 @@ async function userItem() { phoneNumber: '', isVerified: true, // - username, - password: await generateHash('Abc1234!'), + username: 'admin@123.com', + password: await generateHash('Aa1234567'), }, }); @@ -57,16 +57,16 @@ async function userItem() { } const randomFaker = getRandomCJKFaker(); - const alice = await prisma.userItem.upsert({ - where: { id: i }, + await prisma.userItem.upsert({ + where: { id: i.toString() }, update: {}, create: { name: randomFaker.person.fullName(), city: randomFaker.location.city(), - role: 'user', + role: ROLE[Math.floor(Math.random() * ROLE.length)], email: randomFaker.internet.email(), state: randomFaker.location.state(), - status: '', + status: STATUS[Math.floor(Math.random() * STATUS.length)], address: randomFaker.location.streetAddress(), country: randomFaker.location.country(), zipCode: randomFaker.location.zipCode(), @@ -95,3 +95,32 @@ const userItemSeed = userItem() }); export { userItemSeed }; + +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']; diff --git a/03_source/cms_backend/src/app/api/helloworld/route.ts b/03_source/cms_backend/src/app/api/helloworld/route.ts index bda5ecc..9d0cc08 100644 --- a/03_source/cms_backend/src/app/api/helloworld/route.ts +++ b/03_source/cms_backend/src/app/api/helloworld/route.ts @@ -7,7 +7,6 @@ import prisma from '../../lib/prisma'; export async function GET(req: NextRequest, res: NextResponse) { try { const users = await prisma.user.findMany(); - console.log({ users }); return response({ users }, STATUS.OK); } catch (error) { diff --git a/03_source/cms_backend/src/app/api/helloworld/test.http b/03_source/cms_backend/src/app/api/helloworld/test.http new file mode 100644 index 0000000..5b69f09 --- /dev/null +++ b/03_source/cms_backend/src/app/api/helloworld/test.http @@ -0,0 +1,4 @@ +### + +GET http://localhost:7272/api/helloworld + diff --git a/03_source/cms_backend/src/app/api/product/search/route.ts b/03_source/cms_backend/src/app/api/product/search/route.ts index bf77d2d..6583336 100644 --- a/03_source/cms_backend/src/app/api/product/search/route.ts +++ b/03_source/cms_backend/src/app/api/product/search/route.ts @@ -24,9 +24,7 @@ export async function GET(req: NextRequest) { const products = _products(); // 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); diff --git a/03_source/cms_backend/src/app/api/user/createProduct/route.ts b/03_source/cms_backend/src/app/api/user/createProduct/route.ts deleted file mode 100644 index 84f3bc8..0000000 --- a/03_source/cms_backend/src/app/api/user/createProduct/route.ts +++ /dev/null @@ -1,75 +0,0 @@ -// 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; - // }[]; -}; diff --git a/03_source/cms_backend/src/app/api/user/createUser/route.ts b/03_source/cms_backend/src/app/api/user/createUser/route.ts new file mode 100644 index 0000000..adeadf7 --- /dev/null +++ b/03_source/cms_backend/src/app/api/user/createUser/route.ts @@ -0,0 +1,53 @@ +// src/app/api/user/createUser/route.ts +// +// PURPOSE: +// create user to db +// +// RULES: +// T.B.A. +// + +import type { NextRequest } from 'next/server'; + +import { STATUS, response, handleError } from 'src/utils/response'; + +import prisma from '../../../lib/prisma'; + +// ---------------------------------------------------------------------- + +/** + *************************************** + * POST - create User + *************************************** + */ +export async function POST(req: NextRequest) { + // logger('[User] list', users.length); + const { data } = await req.json(); + const createForm: CreateUserData = data as unknown as CreateUserData; + + try { + const user = await prisma.userItem.create({ data: createForm }); + return response({ user }, STATUS.OK); + } catch (error) { + return handleError('User - Create', error); + } +} + +type CreateUserData = { + name: string; + city: string; + role: string; + email: string; + state: string; + status: string; + address: string; + country: string; + zipCode: string; + company: string; + avatarUrl: string; + phoneNumber: string; + isVerified: boolean; + // + username: string; + password: string; +}; diff --git a/03_source/cms_backend/src/app/api/user/createUser/test.http b/03_source/cms_backend/src/app/api/user/createUser/test.http new file mode 100644 index 0000000..41da9b6 --- /dev/null +++ b/03_source/cms_backend/src/app/api/user/createUser/test.http @@ -0,0 +1,4 @@ +### + +POST http://localhost:7272/api/user/createUser + diff --git a/03_source/cms_backend/src/app/api/user/deleteProduct/route.ts b/03_source/cms_backend/src/app/api/user/deleteProduct/route.ts deleted file mode 100644 index f1dcf17..0000000 --- a/03_source/cms_backend/src/app/api/user/deleteProduct/route.ts +++ /dev/null @@ -1,44 +0,0 @@ -// 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); - } -} diff --git a/03_source/cms_backend/src/app/api/user/deleteProduct/test.http b/03_source/cms_backend/src/app/api/user/deleteProduct/test.http deleted file mode 100644 index 4d6bfb6..0000000 --- a/03_source/cms_backend/src/app/api/user/deleteProduct/test.http +++ /dev/null @@ -1,3 +0,0 @@ -### - -DELETE http://localhost:7272/api/product/deleteProduct?productId=e99f09a7-dd88-49d5-b1c8-1daf80c2d7b06 diff --git a/03_source/cms_backend/src/app/api/user/deleteUser/route.ts b/03_source/cms_backend/src/app/api/user/deleteUser/route.ts new file mode 100644 index 0000000..1a684d9 --- /dev/null +++ b/03_source/cms_backend/src/app/api/user/deleteUser/route.ts @@ -0,0 +1,47 @@ +// src/app/api/product/deleteUser/route.ts +// +// PURPOSE: +// delete product from db by id +// +// RULES: +// T.B.A. + +import type { NextRequest } from 'next/server'; + +import { logger } from 'src/utils/logger'; +import { STATUS, response, handleError } from 'src/utils/response'; + +import prisma from '../../../lib/prisma'; + +// ---------------------------------------------------------------------- + +/** ************************************** + * handle Delete Users + *************************************** */ +export async function DELETE(req: NextRequest) { + try { + const { searchParams } = req.nextUrl; + + // RULES: userId must exist + const userId = searchParams.get('userId'); + if (!userId) { + return response({ message: 'User ID is required!' }, STATUS.BAD_REQUEST); + } + + // NOTE: userId confirmed exist, run below + const user = await prisma.userItem.delete({ + // + where: { id: userId }, + }); + + if (!user) { + return response({ message: 'User not found!' }, STATUS.NOT_FOUND); + } + + logger('[User] details', user.id); + + return response({ user }, STATUS.OK); + } catch (error) { + return handleError('User - Get details', error); + } +} diff --git a/03_source/cms_backend/src/app/api/user/deleteUser/test.http b/03_source/cms_backend/src/app/api/user/deleteUser/test.http new file mode 100644 index 0000000..240b86d --- /dev/null +++ b/03_source/cms_backend/src/app/api/user/deleteUser/test.http @@ -0,0 +1,3 @@ +### + +DELETE http://localhost:7272/api/user/deleteUser?userId=3f431e6f-ad05-4d60-9c25-6a7e92a954ad diff --git a/03_source/cms_backend/src/app/api/user/details/route.ts b/03_source/cms_backend/src/app/api/user/details/route.ts index f75d47c..a1165ec 100644 --- a/03_source/cms_backend/src/app/api/user/details/route.ts +++ b/03_source/cms_backend/src/app/api/user/details/route.ts @@ -1,7 +1,7 @@ // src/app/api/product/details/route.ts // // PURPOSE: -// save product to db by id +// read user from db by id // // RULES: // T.B.A. @@ -16,31 +16,31 @@ import prisma from '../../../lib/prisma'; // ---------------------------------------------------------------------- /** ************************************** - * GET Product detail + * GET User detail *************************************** */ export async function GET(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); + // RULES: userId must exist + const userId = searchParams.get('userId'); + if (!userId) { + return response({ message: 'userId is required!' }, STATUS.BAD_REQUEST); } - // NOTE: productId confirmed exist, run below - const product = await prisma.productItem.findFirst({ - include: { reviews: true }, - where: { id: productId }, + // NOTE: userId confirmed exist, run below + const user = await prisma.userItem.findFirst({ + // include: { reviews: true }, + where: { id: userId }, }); - if (!product) { - return response({ message: 'Product not found!' }, STATUS.NOT_FOUND); + if (!user) { + return response({ message: 'User not found!' }, STATUS.NOT_FOUND); } - logger('[Product] details', product.id); + logger('[User] details', user.id); - return response({ product }, STATUS.OK); + return response({ user }, STATUS.OK); } catch (error) { return handleError('Product - Get details', error); } diff --git a/03_source/cms_backend/src/app/api/user/details/test.http b/03_source/cms_backend/src/app/api/user/details/test.http new file mode 100644 index 0000000..8618820 --- /dev/null +++ b/03_source/cms_backend/src/app/api/user/details/test.http @@ -0,0 +1,4 @@ +### + +GET http://localhost:7272/api/user/details?userId=1165ce3a-29b8-4e1a-9148-1ae08d7e8e01 + diff --git a/03_source/cms_backend/src/app/api/user/list/test.http b/03_source/cms_backend/src/app/api/user/list/test.http index c763574..0de51ef 100644 --- a/03_source/cms_backend/src/app/api/user/list/test.http +++ b/03_source/cms_backend/src/app/api/user/list/test.http @@ -1,4 +1,3 @@ ### GET http://localhost:7272/api/user/list - diff --git a/03_source/cms_backend/src/app/api/user/saveProduct/route.ts b/03_source/cms_backend/src/app/api/user/saveUser/route.ts similarity index 57% rename from 03_source/cms_backend/src/app/api/user/saveProduct/route.ts rename to 03_source/cms_backend/src/app/api/user/saveUser/route.ts index fd462e5..db25997 100644 --- a/03_source/cms_backend/src/app/api/user/saveProduct/route.ts +++ b/03_source/cms_backend/src/app/api/user/saveUser/route.ts @@ -19,54 +19,40 @@ import prisma from '../../../lib/prisma'; *************************************** */ export async function POST(req: NextRequest) { // logger('[Product] list', products.length); + const { searchParams } = req.nextUrl; + const userId = searchParams.get('userId'); + + // RULES: userId must exist + if (!userId) { + return response({ message: 'Product ID is required!' }, STATUS.BAD_REQUEST); + } + const { data } = await req.json(); try { - const products = await prisma.productItem.updateMany({ + const user = await prisma.userItem.updateMany({ + where: { id: userId }, data: { + status: data.status, + avatarUrl: data.avatarUrl, + isVerified: data.isVerified, name: data.name, - sku: data.sku, - code: data.code, - price: data.price, - taxes: data.taxes, - tags: data.tags, - sizes: data.sizes, - publish: data.publish, - gender: data.gender, - coverUrl: data.coverUrl, - images: data.images, - colors: data.colors, - quantity: data.quantity, - category: data.category, - available: data.available, - totalSold: data.totalSold, - description: data.description, - totalRatings: data.totalRatings, - totalReviews: data.totalReviews, - inventoryType: data.inventoryType, - subDescription: data.subDescription, - priceSale: data.priceSale, + email: data.email, + phoneNumber: data.phoneNumber, + country: data.country, + state: data.state, + city: data.city, + address: data.address, + zipCode: data.zipCode, + company: data.company, + role: data.role, // - newLabel: { - content: data.newLabel?.content || '', - enabled: data.newLabel?.enabled ?? false, - }, - saleLabel: { - content: data.saleLabel?.content || '', - enabled: data.saleLabel?.enabled ?? false, - }, - ratings: { - set: data.ratings.map((rating: { name: string; starCount: number; reviewCount: number }) => ({ - name: rating.name, - starCount: rating.starCount, - reviewCount: rating.reviewCount, - })), - }, + username: data.username, + password: data.password, }, - where: { id: data.id }, }); - return response({ hello: 'world', data }, STATUS.OK); + return response({ user }, STATUS.OK); } catch (error) { console.log({ hello: 'world', data }); return handleError('Product - Get list', error); diff --git a/03_source/cms_backend/src/app/api/user/saveUser/test.http b/03_source/cms_backend/src/app/api/user/saveUser/test.http new file mode 100644 index 0000000..933a89e --- /dev/null +++ b/03_source/cms_backend/src/app/api/user/saveUser/test.http @@ -0,0 +1,3 @@ +### + +POST http://localhost:7272/api/user/list diff --git a/03_source/docker/dc_up.sh b/03_source/docker/00_dc_up.sh similarity index 100% rename from 03_source/docker/dc_up.sh rename to 03_source/docker/00_dc_up.sh diff --git a/03_source/frontend/prettier.config.mjs b/03_source/frontend/prettier.config.mjs index 74f1069..4a0f615 100644 --- a/03_source/frontend/prettier.config.mjs +++ b/03_source/frontend/prettier.config.mjs @@ -10,7 +10,9 @@ const config = { printWidth: 100, singleQuote: true, trailingComma: 'es5', - plugins: ['@ianvs/prettier-plugin-sort-imports'], + plugins: [ + // '@ianvs/prettier-plugin-sort-imports' + ], }; export default config; diff --git a/03_source/frontend/src/actions/user.ts b/03_source/frontend/src/actions/user.ts index 00fda7d..41d1a16 100644 --- a/03_source/frontend/src/actions/user.ts +++ b/03_source/frontend/src/actions/user.ts @@ -15,15 +15,14 @@ const swrOptions: SWRConfiguration = { // ---------------------------------------------------------------------- -type UserData = { +type UsersData = { users: IUserItem[]; }; export function useGetUsers() { - // const url = endpoints.user.list; const url = `http://localhost:7272/api/user/list`; - const { data, isLoading, error, isValidating, mutate } = useSWR( + const { data, isLoading, error, isValidating, mutate } = useSWR( url, fetcher, swrOptions @@ -46,23 +45,23 @@ export function useGetUsers() { // ---------------------------------------------------------------------- -type ProductData = { - product: IProductItem; +type UserData = { + user: IUserItem; }; -export function useGetProduct(productId: string) { - const url = productId ? [endpoints.product.details, { params: { productId } }] : ''; +export function useGetUser(userId: string) { + const url = userId ? [endpoints.user.details, { params: { userId } }] : ''; - const { data, isLoading, error, isValidating } = useSWR(url, fetcher, swrOptions); + const { data, isLoading, error, isValidating } = useSWR(url, fetcher, swrOptions); const memoizedValue = useMemo( () => ({ - product: data?.product, - productLoading: isLoading, - productError: error, - productValidating: isValidating, + user: data?.user, + userLoading: isLoading, + userError: error, + userValidating: isValidating, }), - [data?.product, error, isLoading, isValidating] + [data?.user, error, isLoading, isValidating] ); return memoizedValue; @@ -98,59 +97,42 @@ export function useSearchProducts(query: string) { // ---------------------------------------------------------------------- -type SaveProductData = { - // id: string; - sku: string; +type SaveUserData = { name: string; - code: string; - 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; - // }[]; + city: string; + role: string; + email: string; + state: string; + status: string; + address: string; + country: string; + zipCode: string; + company: string; + avatarUrl: string; + phoneNumber: string; + isVerified: boolean; + // + username: string; + password: string; }; -export async function saveProduct(productId: string, saveProductData: SaveProductData) { - console.log('save product ?'); - // const url = productId ? [endpoints.product.details, { params: { productId } }] : ''; +export async function saveUser(userId: string, saveUserData: SaveUserData) { + // const url = userId ? [endpoints.user.details, { params: { userId } }] : ''; - const res = await axiosInstance.post('http://localhost:7272/api/product/saveProduct', { - data: saveProductData, - }); + const res = await axiosInstance.post( + // + `http://localhost:7272/api/user/saveUser?userId=${userId}`, + { + data: saveUserData, + } + ); return res; } -export async function uploadProductImage(saveProductData: SaveProductData) { - console.log('save product ?'); - // const url = productId ? [endpoints.product.details, { params: { productId } }] : ''; +export async function uploadUserImage(saveUserData: SaveUserData) { + console.log('uploadUserImage ?'); + // const url = userId ? [endpoints.user.details, { params: { userId } }] : ''; const res = await axiosInstance.get('http://localhost:7272/api/product/helloworld'); @@ -159,51 +141,31 @@ export async function uploadProductImage(saveProductData: SaveProductData) { // ---------------------------------------------------------------------- -type CreateProductData = { - // id: string; - sku: string; +type CreateUserData = { name: string; - code: string; - 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; - // }[]; + city: string; + role: string; + email: string; + state: string; + status: string; + address: string; + country: string; + zipCode: string; + company: string; + avatarUrl: string; + phoneNumber: string; + isVerified: boolean; + // + username: string; + password: string; }; -export async function createProduct(createProductData: CreateProductData) { +export async function createUser(createUserData: CreateUserData) { console.log('create product ?'); // const url = productId ? [endpoints.product.details, { params: { productId } }] : ''; - const res = await axiosInstance.post('http://localhost:7272/api/product/createProduct', { - data: createProductData, + const res = await axiosInstance.post('http://localhost:7272/api/user/createUser', { + data: createUserData, }); return res; @@ -211,21 +173,20 @@ export async function createProduct(createProductData: CreateProductData) { // ---------------------------------------------------------------------- -type DeleteProductResponse = { +type DeleteUserResponse = { success: boolean; message?: string; }; -export async function deleteUser(productId: string): Promise { - const url = `http://localhost:7272/api/product/deleteProduct?productId=${productId}`; +export async function deleteUser(userId: string): Promise { + const url = `http://localhost:7272/api/user/deleteUser?userId=${userId}`; try { const res = await axiosInstance.delete(url); - console.log({ res }); return { success: true, - message: 'Product deleted successfully', + message: 'User deleted successfully', }; } catch (error) { return { diff --git a/03_source/frontend/src/components/nav-section/mini/nav-section-mini.tsx b/03_source/frontend/src/components/nav-section/mini/nav-section-mini.tsx index 78b9fdc..b67b0fd 100644 --- a/03_source/frontend/src/components/nav-section/mini/nav-section-mini.tsx +++ b/03_source/frontend/src/components/nav-section/mini/nav-section-mini.tsx @@ -26,11 +26,7 @@ export function NavSectionMini({ const cssVars = { ...navSectionCssVars.mini(theme), ...overridesVars }; return ( -