feat: implement product save functionality with frontend-backend integration
This commit is contained in:
129
03_source/cms_backend/src/app/api/product/saveProduct/route.ts
Normal file
129
03_source/cms_backend/src/app/api/product/saveProduct/route.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
// src/app/api/product/saveProduct/route.ts
|
||||
//
|
||||
// PURPOSE:
|
||||
// save product to db by id
|
||||
//
|
||||
// RULES:
|
||||
// T.B.A.
|
||||
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
import { STATUS, response, handleError } from 'src/utils/response';
|
||||
|
||||
import prisma from '../../../lib/prisma';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/** **************************************
|
||||
* GET - Products
|
||||
*************************************** */
|
||||
export async function POST(req: NextRequest) {
|
||||
// logger('[Product] list', products.length);
|
||||
const { data } = await req.json();
|
||||
|
||||
try {
|
||||
const products = await prisma.productItem.update({
|
||||
data: {
|
||||
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,
|
||||
//
|
||||
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,
|
||||
})),
|
||||
},
|
||||
},
|
||||
where: { id: data.id },
|
||||
});
|
||||
|
||||
return response({ hello: 'world', data }, STATUS.OK);
|
||||
} catch (error) {
|
||||
console.log({ hello: 'world', data });
|
||||
return handleError('Product - Get list', error);
|
||||
}
|
||||
}
|
||||
|
||||
export type IProductItem = {
|
||||
id: string;
|
||||
sku: string;
|
||||
name: string;
|
||||
code: string;
|
||||
price: number;
|
||||
taxes: number;
|
||||
tags: string[];
|
||||
sizes: string[];
|
||||
publish: string;
|
||||
gender: string[];
|
||||
coverUrl: string;
|
||||
images: string[];
|
||||
colors: string[];
|
||||
quantity: number;
|
||||
category: string;
|
||||
available: number;
|
||||
totalSold: number;
|
||||
description: string;
|
||||
totalRatings: number;
|
||||
totalReviews: number;
|
||||
// createdAt: IDateValue;
|
||||
inventoryType: string;
|
||||
subDescription: string;
|
||||
priceSale: number | null;
|
||||
// reviews: IProductReview[];
|
||||
newLabel: {
|
||||
content: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
saleLabel: {
|
||||
content: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
ratings: {
|
||||
name: string;
|
||||
starCount: number;
|
||||
reviewCount: number;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type IDateValue = string | number | null;
|
||||
|
||||
export type IProductReview = {
|
||||
id: string;
|
||||
name: string;
|
||||
rating: number;
|
||||
comment: string;
|
||||
helpful: number;
|
||||
avatarUrl: string;
|
||||
postedAt: IDateValue;
|
||||
isPurchased: boolean;
|
||||
attachments?: string[];
|
||||
};
|
@@ -4,7 +4,7 @@ import type { IProductItem } from 'src/types/product';
|
||||
import useSWR from 'swr';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { fetcher, endpoints } from 'src/lib/axios';
|
||||
import axiosInstance, { fetcher, endpoints } from 'src/lib/axios';
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@@ -90,3 +90,74 @@ export function useSearchProducts(query: string) {
|
||||
|
||||
return memoizedValue;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
type SaveProductData = {
|
||||
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 | null;
|
||||
newLabel: {
|
||||
content: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
saleLabel: {
|
||||
content: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
ratings: {
|
||||
name: string;
|
||||
starCount: number;
|
||||
reviewCount: number;
|
||||
}[];
|
||||
};
|
||||
export async function saveProduct(productId: string, saveProductData: SaveProductData) {
|
||||
console.log('save product ?');
|
||||
// const url = productId ? [endpoints.product.details, { params: { productId } }] : '';
|
||||
|
||||
const res = await axiosInstance.post('http://localhost:7272/api/product/saveProduct', {
|
||||
data: saveProductData,
|
||||
});
|
||||
|
||||
return res;
|
||||
|
||||
// const url = productId ? [endpoints.product.details, { params: { productId } }] : '';
|
||||
|
||||
// const { data, isLoading, error, isValidating } = useSWR<SaveProductData>(
|
||||
// url,
|
||||
// fetcher,
|
||||
// swrOptions
|
||||
// );
|
||||
|
||||
// const memoizedValue = useMemo(
|
||||
// () => ({
|
||||
// product: data?.product,
|
||||
// productLoading: isLoading,
|
||||
// productError: error,
|
||||
// productValidating: isValidating,
|
||||
// }),
|
||||
// [data?.product, error, isLoading, isValidating]
|
||||
// );
|
||||
|
||||
// return memoizedValue;
|
||||
}
|
||||
|
@@ -6,11 +6,11 @@ import Typography from '@mui/material/Typography';
|
||||
|
||||
import { RouterLink } from 'src/routes/components';
|
||||
|
||||
import { NavUl } from './nav-elements';
|
||||
import { Iconify } from '../../iconify';
|
||||
import { NavSubList } from './nav-sub-list';
|
||||
import { megaMenuClasses } from '../styles';
|
||||
import { NavCarousel } from './nav-carousel';
|
||||
import { NavUl } from './nav-elements';
|
||||
|
||||
import type { NavListProps } from '../types';
|
||||
|
||||
|
@@ -13,6 +13,7 @@ export default function Page() {
|
||||
const { id = '' } = useParams();
|
||||
|
||||
const { product } = useGetProduct(id);
|
||||
console.log({ id });
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import type FullCalendar from '@fullcalendar/react';
|
||||
import type { EventResizeDoneArg } from '@fullcalendar/interaction/index.js';
|
||||
import type { EventDropArg, DateSelectArg, EventClickArg } from '@fullcalendar/core/index.js';
|
||||
import type { ICalendarView, ICalendarRange, ICalendarEvent } from 'src/types/calendar';
|
||||
import type { EventDropArg, DateSelectArg, EventClickArg } from '@fullcalendar/core/index.js';
|
||||
|
||||
import { useRef, useState, useCallback } from 'react';
|
||||
|
||||
|
@@ -2,13 +2,13 @@ import type { Theme, SxProps } from '@mui/material/styles';
|
||||
import type { ICalendarEvent, ICalendarFilters } from 'src/types/calendar';
|
||||
|
||||
import Calendar from '@fullcalendar/react';
|
||||
import { useEffect, startTransition } from 'react';
|
||||
import listPlugin from '@fullcalendar/list/index.js';
|
||||
import dayGridPlugin from '@fullcalendar/daygrid/index.js';
|
||||
import { useEffect, startTransition } from 'react';
|
||||
import timeGridPlugin from '@fullcalendar/timegrid/index.js';
|
||||
import timelinePlugin from '@fullcalendar/timeline/index.js';
|
||||
import interactionPlugin from '@fullcalendar/interaction/index.js';
|
||||
import { useBoolean, useSetState } from 'minimal-shared/hooks';
|
||||
import interactionPlugin from '@fullcalendar/interaction/index.js';
|
||||
|
||||
import Box from '@mui/material/Box';
|
||||
import Card from '@mui/material/Card';
|
||||
|
@@ -23,6 +23,7 @@ import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import { paths } from 'src/routes/paths';
|
||||
import { useRouter } from 'src/routes/hooks';
|
||||
|
||||
import { saveProduct } from 'src/actions/product';
|
||||
import {
|
||||
_tags,
|
||||
PRODUCT_SIZE_OPTIONS,
|
||||
@@ -44,7 +45,7 @@ export const NewProductSchema = zod.object({
|
||||
description: schemaHelper
|
||||
.editor({ message: 'Description is required!' })
|
||||
.min(100, { message: 'Description must be at least 100 characters' })
|
||||
.max(500, { message: 'Description must be less than 500 characters' }),
|
||||
.max(50000, { message: 'Description must be less than 50000 characters' }),
|
||||
images: schemaHelper.files({ message: 'Images is required!' }),
|
||||
code: zod.string().min(1, { message: 'Product code is required!' }),
|
||||
sku: zod.string().min(1, { message: 'Product sku is required!' }),
|
||||
@@ -127,6 +128,8 @@ export function ProductNewEditForm({ currentProduct }: Props) {
|
||||
|
||||
const values = watch();
|
||||
|
||||
// const saveProduct = useSaveProduct('e99f09a7-dd88-49d5-b1c8-1daf80c2d7b01');
|
||||
|
||||
const onSubmit = handleSubmit(async (data) => {
|
||||
const updatedData = {
|
||||
...data,
|
||||
@@ -136,7 +139,14 @@ export function ProductNewEditForm({ currentProduct }: Props) {
|
||||
try {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
reset();
|
||||
|
||||
if (currentProduct) {
|
||||
console.log('save product');
|
||||
await saveProduct(currentProduct.id, values);
|
||||
}
|
||||
|
||||
toast.success(currentProduct ? 'Update success!' : 'Create success!');
|
||||
|
||||
router.push(paths.dashboard.product.root);
|
||||
console.info('DATA', updatedData);
|
||||
} catch (error) {
|
||||
|
Reference in New Issue
Block a user