From 9ffa53b725de947e2b38a2dbd2d58b3bce2e13ad Mon Sep 17 00:00:00 2001 From: Ivan Lim Date: Wed, 4 Sep 2024 20:30:30 +0800 Subject: [PATCH] feat(cms): Orders api config and edit function (#165) * feat(cms): Orders api config and edit function * Fix lint * Added toast for error messages, fix inconsistency issues --- apps/cms/package.json | 1 + apps/cms/src/@types/Order.ts | 17 ++- apps/cms/src/admin/views/MerchProducts.tsx | 10 +- apps/cms/src/admin/views/MerchPromotion.tsx | 10 +- apps/cms/src/admin/views/MerchSales.tsx | 140 +++++++++----------- apps/cms/src/admin/views/ViewTemplate.tsx | 2 + apps/cms/src/apis/orders.api.ts | 53 ++------ apps/cms/src/collections/Orders.ts | 6 +- apps/cms/src/types.ts | 6 +- apps/cms/src/utilities/prettifyKey.ts | 9 ++ 10 files changed, 101 insertions(+), 153 deletions(-) create mode 100644 apps/cms/src/utilities/prettifyKey.ts diff --git a/apps/cms/package.json b/apps/cms/package.json index 1585d165..41f9a087 100644 --- a/apps/cms/package.json +++ b/apps/cms/package.json @@ -30,6 +30,7 @@ "payload": "^2.27.0", "querystring-es3": "^0.2.1", "react": "^18.0.0", + "react-toastify": "10.0.5", "tsconfig": "*" }, "devDependencies": { diff --git a/apps/cms/src/@types/Order.ts b/apps/cms/src/@types/Order.ts index f860d08d..d064f7b3 100644 --- a/apps/cms/src/@types/Order.ts +++ b/apps/cms/src/@types/Order.ts @@ -4,14 +4,13 @@ export class Order { constructor( - public order_id = '', - public date = new Date(), - public order_person = '', - public image_url = '', - public item = '', - public qty = 0, - public size = '', - public colour = '' - + public id = '', + public transactionId = '', + public transactionTime = '', + public paymentMethod = '', + public customerEmail = '', + public status = '', + public updatedAt = '', + public createdAt = '' ) { } } diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx index abc5ad09..c06a44f9 100644 --- a/apps/cms/src/admin/views/MerchProducts.tsx +++ b/apps/cms/src/admin/views/MerchProducts.tsx @@ -8,6 +8,7 @@ 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 { prettifyKey } from "../../utilities/prettifyKey"; const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { // Get data from API @@ -18,15 +19,6 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => { .catch((error) => console.log(error)); }, []); - // Output human-readable table headers based on the attribute names from the API - function prettifyKey(str: string): string { - let res = ""; - for (const i of str.split("_")) { - res += i.charAt(0).toUpperCase() + i.slice(1) + " "; - } - return res; - } - // Do not load table until we receive the data if (data == null) { return
Loading...
; diff --git a/apps/cms/src/admin/views/MerchPromotion.tsx b/apps/cms/src/admin/views/MerchPromotion.tsx index 11ff14bb..ce4eeb29 100644 --- a/apps/cms/src/admin/views/MerchPromotion.tsx +++ b/apps/cms/src/admin/views/MerchPromotion.tsx @@ -9,6 +9,7 @@ import { Table } from "payload/dist/admin/components/elements/Table"; import { Promotion } from "types"; import PromotionsApi from "../../apis/promotions.api"; import './MerchPromotion.scss'; +import { prettifyKey } from "../../utilities/prettifyKey"; const MerchPromotion: AdminViewComponent = ({ user, canAccessAdmin }) => { // Get data from API @@ -19,15 +20,6 @@ const MerchPromotion: AdminViewComponent = ({ user, canAccessAdmin }) => { .catch((error) => console.log(error)); }, []); - // Output human-readable table headers based on the attribute names from the API - function prettifyKey(str: string): string { - let res = ""; - for (const i of str.split("_")) { - res += i.charAt(0).toUpperCase() + i.slice(1) + " "; - } - return res; - } - // Do not load table until we receive the data if (data == null) { return
Loading...
; diff --git a/apps/cms/src/admin/views/MerchSales.tsx b/apps/cms/src/admin/views/MerchSales.tsx index 6d9f89cb..bc012e0e 100644 --- a/apps/cms/src/admin/views/MerchSales.tsx +++ b/apps/cms/src/admin/views/MerchSales.tsx @@ -5,102 +5,93 @@ import ViewTemplate from "./ViewTemplate"; import { Column } from "payload/dist/admin/components/elements/Table/types"; import { Order } from "../../@types/Order"; import OrdersApi from "../../apis/orders.api"; -import { IOrder } from "../../@types/IOrder"; import { RenderCellFactory } from "../utils/RenderCellFactory"; import SortedColumn from "../utils/SortedColumn"; import { Table } from "payload/dist/admin/components/elements/Table"; - +import { useHistory } from 'react-router-dom'; +import { toast } from "react-toastify"; +import { prettifyKey } from "../../utilities/prettifyKey"; const MerchSales: AdminViewComponent = ({ user, canAccessAdmin }) => { - // Get data from API - const [data, setData] = useState(null); - useEffect(() => { - OrdersApi.getOrders() - .then((res: IOrder[]) => setData(res)) - .catch((error) => console.log(error)); + // Get data from API + const [data, setData] = useState(null); + const history = useHistory(); + useEffect(() => { + const fetchOrders = async () => { + try { + const orders: Order[] = await OrdersApi.getOrders(); + setData(orders); + } 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 + fetchOrders(); }, []); - // Output human-readable table headers based on the attribute names from the API - function prettifyKey(str: string): string { - let res = ""; - for (const i of str.split("_")) { - res += i.charAt(0).toUpperCase() + i.slice(1) + " "; - } - return res; - } - // Do not load table until we receive the data if (data == null) { return
Loading...
; } const tableCols = new Array(); - for (const key of Object.keys(new Order())) { - const renderCellComponent = RenderCellFactory.get(data[0], key); - const renderCell: React.FC<{ children?: React.ReactNode }> = - renderCellComponent instanceof Promise - ? renderCellComponent - : renderCellComponent; + if (data && data.length > 0) { + for (const key of Object.keys(new Order())) { + const renderCellComponent = RenderCellFactory.get(data[0], key); + const renderCell: React.FC<{ children?: React.ReactNode }> = + renderCellComponent instanceof Promise + ? renderCellComponent + : renderCellComponent; + + const col: Column = { + accessor: key, + components: { + Heading: ( + + ), + renderCell: renderCell, + }, + label: prettifyKey(key), // Assigning the prettified key to the label + name: key, + active: true, + }; + + tableCols.push(col); + } - const col: Column = { - accessor: key, + // Add the "Edit" column + const editColumn: Column = { + accessor: "edit", components: { - Heading: ( - + Heading:
Edit
, + renderCell: (data: Order) => ( + ), - renderCell: renderCell, }, - label: "", - name: "", + label: "Edit", + name: "edit", active: true, }; - tableCols.push(col); - } - const editColumn: Column = { - accessor: "edit", - components: { - Heading:
Edit
, - renderCell: ({ children }) => ( - - ), - }, - label: "Edit", - name: "edit", - active: true, - }; + tableCols.push(editColumn); - tableCols.push(editColumn); - - const deleteColumn: Column = { - accessor: "delete", - components: { - Heading:
Delete
, - renderCell: ({ children }) => ( - - ), - }, - label: "Delete", - name: "delete", - active: true, - }; - - tableCols.push(deleteColumn); - - const handleEdit = (orderId: string) => { - console.log(`Dummy. Order ID: ${orderId}`); + // Handle Edit functionality + const handleEdit = (data: Order) => { + const orderId = data.id; + // Navigate to the edit page + history.push(`/admin/collections/orders/${orderId}`); + }; } - const handleDelete = (orderId: string) => { - console.log(`Dummy. Order ID: ${orderId}`); - }; - - console.log(tableCols); - return ( { - - + {data && data.length > 0 &&
} ); }; diff --git a/apps/cms/src/admin/views/ViewTemplate.tsx b/apps/cms/src/admin/views/ViewTemplate.tsx index 734049ab..6cca24da 100644 --- a/apps/cms/src/admin/views/ViewTemplate.tsx +++ b/apps/cms/src/admin/views/ViewTemplate.tsx @@ -7,6 +7,7 @@ import { Eyebrow } from "payload/components/elements"; import { AdminViewComponent } from "payload/config"; import { useStepNav } from "payload/components/hooks"; import { Meta } from "payload/components/utilities"; +import { Slide, ToastContainer } from "react-toastify"; type ViewTemplateProps = React.ComponentProps & { description: string; @@ -54,6 +55,7 @@ const ViewTemplate = ({

{title}

{children} + ); }; diff --git a/apps/cms/src/apis/orders.api.ts b/apps/cms/src/apis/orders.api.ts index a18160f4..7dcc4b71 100644 --- a/apps/cms/src/apis/orders.api.ts +++ b/apps/cms/src/apis/orders.api.ts @@ -1,50 +1,13 @@ -import { IOrder } from "../@types/IOrder"; +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/require-await */ +import { Order } from "../@types/Order"; -// todo turn into real api class OrdersApi { - // eslint-disable-next-line @typescript-eslint/require-await - async getOrders(): Promise { - const res: IOrder[] = []; - const item1: IOrder = { - colour: "black", - date: new Date("2022-01-31"), - image_url: - "https://i.kym-cdn.com/entries/icons/original/000/033/421/cover2.jpg", - item: "graduation hat", - order_id: "1", - order_person: "kenneth west", - qty: 2, - size: "M", - }; - res.push(item1); - - const item2: IOrder = { - colour: "white", - date: new Date("2022-02-13"), - image_url: - "https://i.kym-cdn.com/photos/images/newsfeed/002/164/493/b8b.jpg", - item: "scorpion", - order_id: "2", - order_person: "aubrey graham drake", - qty: 1, - size: "L", - }; - res.push(item2); - - const item3: IOrder = { - colour: "beige", - date: new Date("2010-02-13"), - image_url: - "https://i.pinimg.com/474x/c0/f9/f1/c0f9f10a0061a8dd1080d7d9e560579c.jpg", - item: "dat stick", - order_id: "3", - order_person: "rich brian", - qty: 1, - size: "S", - }; - res.push(item3); - - return res; + async getOrders(): Promise { + const req = await fetch(`${process.env.PAYLOAD_PUBLIC_SERVER_URL}/api/orders`); + const orders = await req.json(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + return orders?.docs as Order[]; } } diff --git a/apps/cms/src/collections/Orders.ts b/apps/cms/src/collections/Orders.ts index 18f7ed1a..34424523 100644 --- a/apps/cms/src/collections/Orders.ts +++ b/apps/cms/src/collections/Orders.ts @@ -57,7 +57,7 @@ const Orders: CollectionConfig = { minRows: 1, }, { - name: "transaction_id", + name: "transactionId", label: "Transaction ID", admin: { description: "Transaction ID provided by Payment Gateway", @@ -66,7 +66,7 @@ const Orders: CollectionConfig = { required: true, }, { - name: "transaction_time", + name: "transactionTime", label: "Transaction Time", type: "date", admin: { @@ -77,7 +77,7 @@ const Orders: CollectionConfig = { required: true, }, { - name: "payment_method", + name: "paymentMethod", label: "Payment Method", type: "text", required: true, diff --git a/apps/cms/src/types.ts b/apps/cms/src/types.ts index 7fb8dfc5..87bbc7cc 100644 --- a/apps/cms/src/types.ts +++ b/apps/cms/src/types.ts @@ -144,9 +144,9 @@ export interface User { export interface Order { id: string; items?: OrderItem; - transaction_id: string; - transaction_time: string; - payment_method: string; + transactionId: string; + transactionTime: string; + paymentMethod: string; customerEmail: string; status: 'pending' | 'paid' | 'delivered'; updatedAt: string; diff --git a/apps/cms/src/utilities/prettifyKey.ts b/apps/cms/src/utilities/prettifyKey.ts new file mode 100644 index 00000000..c1df9efa --- /dev/null +++ b/apps/cms/src/utilities/prettifyKey.ts @@ -0,0 +1,9 @@ +/* +Convert a camelCase string to a human - readable format with proper spacing and capitalization +*/ + +export function prettifyKey(str: string): string { + let res = str.replace(/([A-Z])/g, ' $1'); // Add a space before each uppercase letter + res = res.charAt(0).toUpperCase() + res.slice(1); // Capitalize the first letter + return res; +} \ No newline at end of file