diff --git a/apps/cms/src/admin/utils/RenderCellFactory.tsx b/apps/cms/src/admin/utils/RenderCellFactory.tsx
index 97ec1ef0..1216fcc9 100644
--- a/apps/cms/src/admin/utils/RenderCellFactory.tsx
+++ b/apps/cms/src/admin/utils/RenderCellFactory.tsx
@@ -2,42 +2,122 @@ import React from "react";
import payload from "payload";
export class RenderCellFactory {
-
static get(element: unknown, key: string) {
- console.log(key)
if (element[key] == undefined) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
- payload.logger.error(`Attribute ${key} cannot be found in element ${element.toString()}`);
+ payload.logger.error(
+ `Attribute ${key} cannot be found in element ${element.toString()}`
+ );
return null;
}
const isImageUrl = new RegExp("http(s?):\\/\\/.*.(jpg|png|jpeg)$");
+
+ if (Array.isArray(element[key])) {
+ if (
+ (element[key] as string[]).every((item: string) =>
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
+ isImageUrl.test((item as string).toString())
+ )
+ ) {
+ // If the element is an array, render images accordingly
+ const ImagesComponent: React.FC<{ children?: React.ReactNode[] }> = ({
+ children,
+ }) => (
+
+ {children.map((imageUrl: string, index: number) => (
+
+ ))}
+
+ );
+ const ImagesComponentCell = (row, data) => (
+ {data}
+ );
+ return ImagesComponentCell;
+ } else {
+ // If the element is an array of strings, render them
+ const StringsComponent: React.FC<{ children?: React.ReactNode[] }> = ({
+ children,
+ }) => (
+
+ {children.map((text: string, index: number) => (
+
+ {index > 0 && ", "} {text}
+
+ ))}
+
+ );
+ const StringsComponentCell = (row, data) => (
+ {data}
+ );
+ return StringsComponentCell;
+ }
+ }
+
if (isImageUrl.test((element[key] as string).toString())) {
- const ImageComponent: React.FC<{children?: React.ReactNode}> = ({ children }) => (
+ const ImageComponent: React.FC<{ children?: React.ReactNode }> = ({
+ children,
+ }) => (
-
+
);
- const ImageComponentCell = (row, data) => {data};
+ const ImageComponentCell = (row, data) => (
+ {data}
+ );
return ImageComponentCell;
}
+ if (key === "stock") {
+ const ObjectComponent: React.FC<{ data: string }> = ({ data }) => (
+
+ {Object.entries(data).map(([subKey, value], index) => (
+
+ {subKey}:{" "}
+
+ {typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value)}
+
+
+ ))}
+
+ );
+ const ObjectComponentCell = (row, data: string) => (
+
+ );
+ return ObjectComponentCell;
+
+ }
+ if (typeof element[key] == "object") {
+ const DateComponent: React.FC<{ children?: React.ReactNode }> = ({
+ children,
+ }) => {(children as unknown as Date).toDateString()};
+ const DateComponentCell = (row, data) => (
+ {data}
+ );
+ return DateComponentCell;
+ }
- if (typeof element[key] == 'object') {
- const DateComponent: React.FC<{children?: React.ReactNode}> = ({ children }) => (
-
- {(children as unknown as Date).toDateString()}
-
+ if (typeof element[key] === "boolean") {
+ // If the element is a boolean, render "Yes" or "No"
+ const BooleanComponent: React.FC<{ children?: React.ReactNode }> = ({
+ children,
+ }) => {children ? "Yes" : "No"};
+ const BooleanComponentCell = (row, data) => (
+ {data}
);
- const DateComponentCell = (row, data) => {data};
- return DateComponentCell
+ return BooleanComponentCell;
}
- const TextComponent: React.FC<{children?: React.ReactNode}> = ({ children }) => (
-
- {children}
-
+ const TextComponent: React.FC<{ children?: React.ReactNode }> = ({
+ children,
+ }) => {children};
+ const TextComponentCell = (row, data) => (
+ {data}
);
- const TextComponentCell = (row, data) => {data};
- return TextComponentCell
+ return TextComponentCell;
}
}
diff --git a/apps/cms/src/admin/views/MerchProducts.tsx b/apps/cms/src/admin/views/MerchProducts.tsx
index 07faa685..fbfce61e 100644
--- a/apps/cms/src/admin/views/MerchProducts.tsx
+++ b/apps/cms/src/admin/views/MerchProducts.tsx
@@ -1,9 +1,103 @@
-import React from "react";
+import React, { useEffect, useState } from "react";
import { Button } from "payload/components/elements";
import { AdminView } from "payload/config";
import ViewTemplate from "./ViewTemplate";
+import { Column } from "payload/dist/admin/components/elements/Table/types";
+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";
const MerchProducts: AdminView = ({ user, canAccessAdmin }) => {
+ // Get data from API
+ const [data, setData] = useState(null);
+ useEffect(() => {
+ ProductsApi.getProducts()
+ .then((res: Product[]) => setData(res))
+ .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...
;
+ }
+
+ const tableCols = new Array();
+ if (data && data.length > 0) {
+ const sampleProduct = data[0];
+ const keys = Object.keys(sampleProduct);
+ for (const key of keys) {
+ const renderCell: React.FC<{ children?: React.ReactNode }> = RenderCellFactory.get(sampleProduct, key);
+ const col: Column = {
+ accessor: key,
+ components: {
+ Heading: (
+
+ ),
+ renderCell: renderCell,
+ },
+ label: "",
+ name: "",
+ active: true,
+ };
+ tableCols.push(col);
+ }
+ }
+
+ const editColumn: Column = {
+ accessor: "edit",
+ components: {
+ Heading: Edit
,
+ renderCell: ({ children }) => (
+
+ ),
+ },
+ label: "Edit",
+ name: "edit",
+ active: true,
+ };
+
+ 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}`);
+ };
+
+ const handleDelete = (orderId: string) => {
+ console.log(`Dummy. Order ID: ${orderId}`);
+ };
+
+ console.log(tableCols);
+
return (
{
keywords=""
title="Merchandise Products"
>
-
- Here is a custom route that was added in the Payload config. It uses the
- Default Template, so the sidebar is rendered.
-
+
+
);
};
diff --git a/apps/cms/src/apis/products.api.ts b/apps/cms/src/apis/products.api.ts
new file mode 100644
index 00000000..62e10ef7
--- /dev/null
+++ b/apps/cms/src/apis/products.api.ts
@@ -0,0 +1,64 @@
+import { Product } from "types";
+// 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 }
+ },
+ },
+ ];
+
+ return res;
+ }
+}
+
+export default new ProductsApi();