Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement edit and delete feature for products #168

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
10 changes: 10 additions & 0 deletions apps/cms/src/@types/Product.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export class Product {
constructor(
public id = '',
public name = '',
public colors= '',
public sizes = '',
public price = '',
public category = ''
) { }
}
143 changes: 92 additions & 51 deletions apps/cms/src/admin/views/MerchProducts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,51 @@ 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<Product[]>(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) {
return <div> Loading... </div>;
}

const tableCols = new Array<Column>();
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: {
Expand All @@ -43,53 +60,78 @@ 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: <div>Edit</div>,
renderCell: (data: Product) => (
<Button onClick={() => handleEdit(data)}>Edit</Button>
),
},
label: "Edit",
name: "edit",
active: true,
};

const editColumn: Column = {
accessor: "edit",
components: {
Heading: <div>Edit</div>,
renderCell: ({ children }) => (
<Button onClick={() => handleEdit(children as string)}>Edit</Button>
),
},
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: <div>Delete</div>,
renderCell: ({ children }) => (
<Button onClick={() => handleDelete(children as string)}>Delete</Button>
),
},
label: "Delete",
name: "delete",
active: true,
};
const deleteColumn: Column = {
accessor: "delete",
components: {
Heading: <div>Delete</div>,
renderCell: (data: Product) => (
<Button onClick={() => {
void (() => {
handleDelete(data);
})();
}}
>Delete</Button>
),
},
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 = (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
// Implement delete API

// 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 (
<ViewTemplate
Expand All @@ -102,8 +144,7 @@ const MerchProducts: AdminViewComponent = ({ user, canAccessAdmin }) => {
<Button el="link" to={"/admin"} buttonStyle="primary">
Go to Main Admin View
</Button>

<Table data={data} columns={tableCols} />
{data && data.length > 0 && <Table data={data} columns={tableCols} />}
</ViewTemplate>
);
};
Expand Down
105 changes: 52 additions & 53 deletions apps/cms/src/apis/products.api.ts
spaceman03 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,64 +1,63 @@
import { Product } from "types";
// todo turn into real api
class ProductsApi {
// eslint-disable-next-line @typescript-eslint/require-await
async getProducts(): Promise<any> {
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 }
// eslint-disable-next-line @typescript-eslint/require-await
async getProducts(): Promise<any> {
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: "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 }
{
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;
},
];
return res;
}
}

export default new ProductsApi();
export default new ProductsApi();
7 changes: 7 additions & 0 deletions packages/eslint-config-custom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -58,6 +63,8 @@ module.exports = {
format: ["camelCase", "PascalCase"],
},
],
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-call": "off",
},
parserOptions: {
tsconfigRootDir: __dirname,
Expand Down
Loading