From d0c3ff4ad6ae21a4740d766d4f4f4aaab7a34f4e Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Fri, 13 Sep 2024 23:06:14 +0800 Subject: [PATCH 01/16] Implemented edit and delete feature for products --- apps/cms/src/@types/Product.ts | 10 ++ apps/cms/src/admin/views/MerchProducts.tsx | 138 +++++++++++++-------- apps/cms/src/apis/products.api.ts | 78 ++++-------- 3 files changed, 118 insertions(+), 108 deletions(-) create mode 100644 apps/cms/src/@types/Product.ts diff --git a/apps/cms/src/@types/Product.ts b/apps/cms/src/@types/Product.ts new file mode 100644 index 00000000..d5fe3e74 --- /dev/null +++ b/apps/cms/src/@types/Product.ts @@ -0,0 +1,10 @@ +export class Product { + constructor( + public id = '', + public name = '', + public colors= '', + public sizes = '', + public price = '', + public category = '' + ) { } +} \ No newline at end of file diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx index c06a44f9..86cebf17 100644 --- a/apps/cms/src/admin/views/MerchProducts.tsx +++ b/apps/cms/src/admin/views/MerchProducts.tsx @@ -3,21 +3,36 @@ import { Button } from "payload/components/elements"; import { AdminViewComponent } from "payload/config"; import ViewTemplate from "./ViewTemplate"; import { Column } from "payload/dist/admin/components/elements/Table/types"; +import { Product } from "../../@types/Product"; +import ProductsApi from "../../apis/products.api"; import { RenderCellFactory } from "../utils/RenderCellFactory"; import SortedColumn from "../utils/SortedColumn"; import { Table } from "payload/dist/admin/components/elements/Table"; -import { Product } from "types"; -import ProductsApi from "../../apis/products.api"; +import { useHistory } from 'react-router-dom'; +import { toast } from "react-toastify"; import { prettifyKey } from "../../utilities/prettifyKey"; const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { // Get data from API const [data, setData] = useState(null); + const history = useHistory(); useEffect(() => { - ProductsApi.getProducts() - .then((res: Product[]) => setData(res)) - .catch((error) => console.log(error)); - }, []); + const fetchProducts = async () => { + try { + const products: Product[] = await ProductsApi.getProducts(); + setData(products); + } catch (error) { + setData([]); + if (error instanceof Error) { + toast.error(error.message); + } else { + toast.error("An unknown error occurred"); + } + } + }; + // eslint-disable-next-line @typescript-eslint/no-floating-promises + fetchProducts(); + }, []); // Do not load table until we receive the data if (data == null) { @@ -25,12 +40,14 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { } const tableCols = new Array(); - if (data && data.length > 0) { - const sampleProduct = data[0]; - const keys = Object.keys(sampleProduct); - for (const key of keys) { + if (data && data.length > 0) { + for (const key of Object.keys(new Product())) { + const renderCellComponent = RenderCellFactory.get(data[0], key); const renderCell: React.FC<{ children?: React.ReactNode }> = - RenderCellFactory.get(sampleProduct, key); + renderCellComponent instanceof Promise + ? renderCellComponent + : renderCellComponent; + const col: Column = { accessor: key, components: { @@ -43,53 +60,73 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { ), renderCell: renderCell, }, - label: "", - name: "", + label: prettifyKey(key), // Assigning the prettified key to the label + name: key, active: true, }; + tableCols.push(col); } - } + + const editColumn: Column = { + accessor: "edit", + components: { + Heading:
Edit
, + renderCell: (data: Product) => ( + + ), + }, + label: "Edit", + name: "edit", + active: true, + }; - const editColumn: Column = { - accessor: "edit", - components: { - Heading:
Edit
, - renderCell: ({ children }) => ( - - ), - }, - label: "Edit", - name: "edit", - active: true, - }; + tableCols.push(editColumn); - tableCols.push(editColumn); + const handleEdit = (data: Product) => { + const productId = data.id; + // Navigate to the edit page + history.push(`/admin/collections/products/${productId}`); + }; - const deleteColumn: Column = { - accessor: "delete", - components: { - Heading:
Delete
, - renderCell: ({ children }) => ( - - ), - }, - label: "Delete", - name: "delete", - active: true, - }; + const deleteColumn: Column = { + accessor: "delete", + components: { + Heading:
Delete
, + renderCell: (data: Product) => ( + + ), + }, + label: "Delete", + name: "delete", + active: true, + }; - tableCols.push(deleteColumn); + tableCols.push(deleteColumn); - const handleEdit = (orderId: string) => { - console.log(`Dummy. Order ID: ${orderId}`); - }; - - const handleDelete = (orderId: string) => { - console.log(`Dummy. Order ID: ${orderId}`); - }; - - console.log(tableCols); + const handleDelete = async (data: Product) => { + const productId = data.id; + try { + // Show a confirmation prompt (optional) + if (window.confirm('Are you sure you want to delete this product?')) { + // Call the delete API + await ProductsApi.deleteProduct(productId); + + // After deletion, update the data state to reflect the removal + setData((prevData) => prevData.filter((product) => product.id !== productId)); + + // Optionally, show a success message + toast.success('Product deleted successfully'); + } + } catch (error) { + if (error instanceof Error) { + toast.error(error.message); + } else { + toast.error('An unknown error occurred'); + } + } + }; + } return ( { - - + {data && data.length > 0 &&
} ); }; diff --git a/apps/cms/src/apis/products.api.ts b/apps/cms/src/apis/products.api.ts index 62e10ef7..f7dcd837 100644 --- a/apps/cms/src/apis/products.api.ts +++ b/apps/cms/src/apis/products.api.ts @@ -1,64 +1,28 @@ -import { Product } from "types"; +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/require-await */ +import { Product } from "../@types/Product"; // todo turn into real api class ProductsApi { - // eslint-disable-next-line @typescript-eslint/require-await - async getProducts(): Promise { - const res: Product[] = [ - { - id: "1", - name: "product1", - price: 1000, - images: [ - "https://i.kym-cdn.com/entries/icons/original/000/033/421/cover2.jpg", - "https://i.pinimg.com/474x/c0/f9/f1/c0f9f10a0061a8dd1080d7d9e560579c.jpg", - ], - sizes: ["s", "m", "l", "xl"], - category: "shirt", - is_available: true, - colors: ["black,white,blue"], - stock: { - black: { S: 10, M: 15, L: 20, XL: 5 }, - white: { S: 12, M: 17, L: 22, XL: 7 }, - blue: { S: 8, M: 13, L: 18, XL: 3 } - }, - }, - { - id: "2", - name: "product2", - price: 2000, - images: [ - "https://i.kym-cdn.com/photos/images/newsfeed/002/164/493/b8b.jpg", - "https://i.kym-cdn.com/entries/icons/original/000/033/421/cover2.jpg", - ], - sizes: ["s", "m"], - category: "sweater", - is_available: true, - colors: ["blue"], - stock: { - blue: { S: 8, M: 13, L: 18, XL: 3 } - }, - }, - { - id: "3", - name: "product3", - price: 3000, - images: [ - "https://i.kym-cdn.com/entries/icons/original/000/033/421/cover2.jpg", - "https://i.kym-cdn.com/photos/images/newsfeed/002/164/493/b8b.jpg", - "https://i.pinimg.com/474x/c0/f9/f1/c0f9f10a0061a8dd1080d7d9e560579c.jpg", - ], - sizes: ["xs", "s", "m", "l"], - category: "hat", - is_available: false, - colors: ["white"], - stock: { - white: { S: 12, M: 17, L: 22, XL: 7 } - }, - }, - ]; + async getProducts(): Promise { + const req = await fetch(`${process.env.PAYLOAD_PUBLIC_SERVER_URL}/api/products`); + const products = await req.json(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + return products?.docs as Product[]; + } - return res; + async deleteProduct(id: string): Promise { + try { + const response = await fetch(`${process.env.PAYLOAD_PUBLIC_SERVER_URL}/api/products/${id}`, { + method: 'DELETE', + }); + if (!response.ok) { + throw new Error('Failed to delete product'); + } + } catch (error) { + throw new Error(error instanceof Error ? error.message : 'An unknown error occurred'); + } } + } export default new ProductsApi(); From e8d372ea0446cc7ff07def0f11df53d6a15d2e20 Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Fri, 13 Sep 2024 23:46:58 +0800 Subject: [PATCH 02/16] Fix lint issue --- apps/cms/src/admin/views/MerchProducts.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx index 86cebf17..6d234ad3 100644 --- a/apps/cms/src/admin/views/MerchProducts.tsx +++ b/apps/cms/src/admin/views/MerchProducts.tsx @@ -43,6 +43,7 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { if (data && data.length > 0) { for (const key of Object.keys(new Product())) { const renderCellComponent = RenderCellFactory.get(data[0], key); + // eslint-disable-next-line @typescript-eslint/no-floating-promises const renderCell: React.FC<{ children?: React.ReactNode }> = renderCellComponent instanceof Promise ? renderCellComponent From 04c5484fdacc0b7614d0d629df545c8f1bbdc9db Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Fri, 13 Sep 2024 23:56:16 +0800 Subject: [PATCH 03/16] Fix lint issue --- apps/cms/src/admin/views/MerchProducts.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx index 6d234ad3..d653c0cf 100644 --- a/apps/cms/src/admin/views/MerchProducts.tsx +++ b/apps/cms/src/admin/views/MerchProducts.tsx @@ -43,7 +43,6 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { if (data && data.length > 0) { for (const key of Object.keys(new Product())) { const renderCellComponent = RenderCellFactory.get(data[0], key); - // eslint-disable-next-line @typescript-eslint/no-floating-promises const renderCell: React.FC<{ children?: React.ReactNode }> = renderCellComponent instanceof Promise ? renderCellComponent @@ -95,7 +94,13 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { components: { Heading:
Delete
, renderCell: (data: Product) => ( - + ), }, label: "Delete", From 9919a345724603bbec8478b0ca97be6ed152440c Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Sat, 14 Sep 2024 00:00:40 +0800 Subject: [PATCH 04/16] Fix lint issue --- apps/cms/src/admin/views/MerchProducts.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx index d653c0cf..a6216bd6 100644 --- a/apps/cms/src/admin/views/MerchProducts.tsx +++ b/apps/cms/src/admin/views/MerchProducts.tsx @@ -93,14 +93,9 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { accessor: "delete", components: { Heading:
Delete
, + // eslint-disable-next-line @typescript-eslint/no-misused-promises renderCell: (data: Product) => ( - + ), }, label: "Delete", From c8ab47320648250ba9e3f002b65d6a8ed27c9386 Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Sat, 14 Sep 2024 00:05:32 +0800 Subject: [PATCH 05/16] Fix lint issue --- apps/cms/src/admin/views/MerchProducts.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx index a6216bd6..0a92d764 100644 --- a/apps/cms/src/admin/views/MerchProducts.tsx +++ b/apps/cms/src/admin/views/MerchProducts.tsx @@ -19,6 +19,7 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { useEffect(() => { const fetchProducts = async () => { try { + // eslint-disable-next-line @typescript-eslint/no-misused-promises const products: Product[] = await ProductsApi.getProducts(); setData(products); } catch (error) { From 3bad8eee97e34db2a13c1f29d6c1c3bb804f11c6 Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Sat, 14 Sep 2024 00:17:43 +0800 Subject: [PATCH 06/16] Fix lint issue --- apps/cms/src/admin/views/MerchProducts.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx index 0a92d764..cb4f6ba2 100644 --- a/apps/cms/src/admin/views/MerchProducts.tsx +++ b/apps/cms/src/admin/views/MerchProducts.tsx @@ -19,7 +19,6 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { useEffect(() => { const fetchProducts = async () => { try { - // eslint-disable-next-line @typescript-eslint/no-misused-promises const products: Product[] = await ProductsApi.getProducts(); setData(products); } catch (error) { @@ -94,8 +93,8 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { accessor: "delete", components: { Heading:
Delete
, - // eslint-disable-next-line @typescript-eslint/no-misused-promises renderCell: (data: Product) => ( + // eslint-disable-next-line @typescript-eslint/no-floating-promises ), }, From 955fd71f3265214d0ae3ae35f192282bf354c5dd Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Sat, 14 Sep 2024 00:24:25 +0800 Subject: [PATCH 07/16] Fix lint issue --- packages/eslint-config-custom/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/eslint-config-custom/index.js b/packages/eslint-config-custom/index.js index 813bb43c..b032163d 100644 --- a/packages/eslint-config-custom/index.js +++ b/packages/eslint-config-custom/index.js @@ -58,6 +58,14 @@ module.exports = { format: ["camelCase", "PascalCase"], }, ], + "@typescript-eslint/no-misused-promises": [ + "error", + { + "checksVoidReturn": { + "arguments": false + } + }, + ] }, parserOptions: { tsconfigRootDir: __dirname, From 4ad76397c90740fc40470350d2f8d460b8d01541 Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Sat, 14 Sep 2024 10:42:16 +0800 Subject: [PATCH 08/16] Fix lint issue --- apps/cms/src/admin/views/MerchProducts.tsx | 10 ++++++++-- packages/eslint-config-custom/index.js | 8 -------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx index cb4f6ba2..994f35b7 100644 --- a/apps/cms/src/admin/views/MerchProducts.tsx +++ b/apps/cms/src/admin/views/MerchProducts.tsx @@ -94,8 +94,14 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { components: { Heading:
Delete
, renderCell: (data: Product) => ( - // eslint-disable-next-line @typescript-eslint/no-floating-promises - + // + ), }, label: "Delete", diff --git a/packages/eslint-config-custom/index.js b/packages/eslint-config-custom/index.js index b032163d..813bb43c 100644 --- a/packages/eslint-config-custom/index.js +++ b/packages/eslint-config-custom/index.js @@ -58,14 +58,6 @@ module.exports = { format: ["camelCase", "PascalCase"], }, ], - "@typescript-eslint/no-misused-promises": [ - "error", - { - "checksVoidReturn": { - "arguments": false - } - }, - ] }, parserOptions: { tsconfigRootDir: __dirname, From 7cc2d647a893cae6f5c4ac5a57a2321c94a65e27 Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Sat, 14 Sep 2024 10:53:57 +0800 Subject: [PATCH 09/16] Fix lint issue --- packages/eslint-config-custom/index.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/eslint-config-custom/index.js b/packages/eslint-config-custom/index.js index 813bb43c..5dc0e585 100644 --- a/packages/eslint-config-custom/index.js +++ b/packages/eslint-config-custom/index.js @@ -46,6 +46,11 @@ module.exports = { ], plugins: ["@typescript-eslint"], rules: { + "@typescript-eslint/no-misused-promises": ["error", { + "checksVoidReturn": { + "attributes": false + } + }], "@typescript-eslint/no-empty-interface": ["off", "never"], "@typescript-eslint/no-unused-vars": [ "error", From 86c8b93b8e0d7ae13dc66cd1da16d7e78773d894 Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Sat, 14 Sep 2024 11:01:09 +0800 Subject: [PATCH 10/16] Fix lint issue --- apps/cms/src/admin/views/MerchProducts.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx index 994f35b7..8b09b768 100644 --- a/apps/cms/src/admin/views/MerchProducts.tsx +++ b/apps/cms/src/admin/views/MerchProducts.tsx @@ -94,10 +94,8 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { components: { Heading:
Delete
, renderCell: (data: Product) => ( - // From 3a2101cae27836e0b6ffc5bc3a0a10b6faf9cf9f Mon Sep 17 00:00:00 2001 From: ZhiXuan Date: Mon, 23 Sep 2024 15:19:59 +0800 Subject: [PATCH 16/16] Fix error --- apps/cms/src/admin/views/MerchProducts.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx index c791064a..718210fd 100644 --- a/apps/cms/src/admin/views/MerchProducts.tsx +++ b/apps/cms/src/admin/views/MerchProducts.tsx @@ -95,7 +95,7 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { Heading:
Delete
, renderCell: (data: Product) => (