Untitled

 avatar
unknown
javascript
a year ago
14 kB
12
Indexable
import React, { useState, useEffect } from 'react';
import { collection, addDoc, deleteDoc, doc, updateDoc, getDocs } from 'firebase/firestore';
import { db } from '../../../../firebaseConfig'; // Adjust import path as needed
import { Modal, Button, Form, Input, Select, InputNumber, notification, Image, List, Upload } from 'antd';
import { UploadOutlined, DeleteOutlined, DeleteTwoTone } from '@ant-design/icons';
import { uploadImage } from '../../../../features/uploadImage'; // Adjust import path as needed

const { Option } = Select;
const { TextArea } = Input;

const Products = () => {
    const [products, setProducts] = useState([]);
    const [viewProduct, setViewProduct] = useState(null);
    const [isModalVisible, setIsModalVisible] = useState(false);
    const [isEditMode, setIsEditMode] = useState(false);
    const [form] = Form.useForm();
    const [thumbnailUrl, setThumbnailUrl] = useState('');
    const [imagesUrls, setImagesUrls] = useState([]);

    useEffect(() => {
        const fetchProducts = async () => {
            try {
                const productsCollection = collection(db, 'products');
                const productSnapshot = await getDocs(productsCollection);
                const productsArray = productSnapshot.docs.map(doc => ({
                    id: doc.id,
                    ...doc.data(),
                }));
                setProducts(productsArray);
            } catch (error) {
                notification.error({
                    message: 'Error',
                    description: 'Failed to fetch products.',
                });
            }
        };
        fetchProducts();
    }, []);

    const showModal = (product) => {
        setIsEditMode(!!product);
        setViewProduct(product || {});
        form.setFieldsValue({
            ...product,
            images: product?.images ? product.images.join(', ') : '',
            ingredients: product?.ingredients ? product.ingredients.join(', ') : ''
        });
        setThumbnailUrl(product?.thumbnail || '');
        setImagesUrls(product?.images || []);
        setIsModalVisible(true);
    };

    const handleCancel = () => {
        setIsModalVisible(false);
        form.resetFields();
        setThumbnailUrl('');
        setImagesUrls([]);
    };

    const handleDelete = async (id) => {
        try {
            await deleteDoc(doc(db, 'products', id));
            setProducts(products.filter(product => product.id !== id));
        } catch (error) {
            notification.error({
                message: 'Error',
                description: 'Failed to delete the product.',
            });
        }
    };

    const handleSave = async (values) => {
        try {
            const updatedValues = {
                ...values,
                thumbnail: thumbnailUrl,
                images: imagesUrls,
                ingredients: values.ingredients ? values.ingredients.split(',').map(ingredient => ingredient.trim()) : []
            };

            if (isEditMode) {
                await updateDoc(doc(db, 'products', viewProduct.id), updatedValues);
            } else {
                await addDoc(collection(db, 'products'), updatedValues);
            }

            const productsCollection = collection(db, 'products');
            const productSnapshot = await getDocs(productsCollection);
            const productsArray = productSnapshot.docs.map(doc => ({
                id: doc.id,
                ...doc.data(),
            }));
            setProducts(productsArray);

            setIsModalVisible(false);
            form.resetFields();
            setThumbnailUrl('');
            setImagesUrls([]);
            notification.success({
                message: 'Success',
                description: 'Product is saved successfully.',
            });
        } catch (error) {
            console.error('Error saving product:', error);
            notification.error({
                message: 'Error',
                description: 'Failed to save the product.',
            });
        }
    };

    const handleFileChange = async (info) => {
        const { fileList } = info;
        const newUrls = await Promise.all(
            fileList.map(async (file) => {
                if (file.status === 'done') {
                    return file.response; // Assuming response contains the URL
                } else if (file.status === 'uploading') {
                    try {
                        const url = await uploadImage(file.originFileObj);
                        return url;
                    } catch (error) {
                        console.error('Error uploading image:', error);
                        return null;
                    }
                }
                return null;
            })
        );
        setImagesUrls(prevUrls => [...new Set([...prevUrls, ...newUrls.filter(url => url !== null)])]);
    };

    const customRequestHandler = async ({ file, onSuccess, onError }) => {
        try {
            const url = await uploadImage(file.originFileObj);
            onSuccess(url); // Trigger onSuccess with URL
        } catch (error) {
            console.error('Error uploading image:', error);
            onError(error);
        }
    };

    const removeImage = (url) => {
        setImagesUrls(imagesUrls.filter(imageUrl => imageUrl !== url));
    };

    return (
        <div className="container mx-auto p-6">
            <h2 className="text-2xl font-bold mb-4">Products</h2>
            <Button type="primary" onClick={() => showModal(null)}>Add Product</Button>
            <table className="min-w-full divide-y divide-gray-200 mt-4">
                <thead>
                    <tr>
                        <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
                        <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
                        <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Price</th>
                        <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
                        <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
                    </tr>
                </thead>
                <tbody className="bg-white divide-y divide-gray-200">
                    {products.map(product => (
                        <tr key={product.id}>
                            <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{product.id}</td>
                            <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{product.name}</td>
                            <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${product.price}</td>
                            <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{product.status}</td>
                            <td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
                                <Button onClick={() => showModal(product)} className="mr-2">
                                    View
                                </Button>
                                <Button onClick={() => handleDelete(product.id)} type="text" danger>
                                    <DeleteOutlined />
                                </Button>
                            </td>
                        </tr>
                    ))}
                </tbody>
            </table>

            <Modal
                title={isEditMode ? "Edit Product" : "View Product"}
                visible={isModalVisible}
                onCancel={handleCancel}
                footer={[
                    <Button key="back" onClick={handleCancel}>
                        Cancel
                    </Button>,
                    <Button
                        key="submit"
                        type="primary"
                        onClick={() => form.submit()}
                    >
                        Save
                    </Button>
                ]}
            >
                <Form
                    className='h-[27rem] overflow-y-auto overflow-x-hidden'
                    form={form}
                    layout="vertical"
                    onFinish={handleSave}
                    initialValues={viewProduct}
                >
                    <Form.Item name="name" label="Name" rules={[{ required: true, message: 'Please enter the product name!' }]}>
                        <Input />
                    </Form.Item>
                    <Form.Item name="description" label="Description">
                        <TextArea rows={4} />
                    </Form.Item>
                    <Form.Item name="price" label="Price" rules={[{ required: true, message: 'Please enter the price!' }]}>
                        <InputNumber min={0} style={{ width: '100%' }} />
                    </Form.Item>
                    <Form.Item name="availability" label="Availability">
                        <Select>
                            <Option value="In Stock">In Stock</Option>
                            <Option value="Out Of Stock">Out Of Stock</Option>
                        </Select>
                    </Form.Item>
                    <Form.Item name="category" label="Category">
                        <Select mode="multiple">
                            <Option value="Pizza">Pizza</Option>
                            <Option value="Burger">Burger</Option>
                            <Option value="Pasta">Pasta</Option>
                            {/* Add more options as needed */}
                        </Select>
                    </Form.Item>
                    <Form.Item name="ingredients" label="Ingredients">
                        <Input />
                    </Form.Item>
                    <Form.Item name="preparation_time" label="Preparation Time">
                        <Input />
                    </Form.Item>
                    <Form.Item name="tags" label="Tags">
                        <Select>
                            <Option value="Vegetarian">Vegetarian</Option>
                            <Option value="Non-Vegetarian">Non-Vegetarian</Option>
                            <Option value="Vegan">Vegan</Option>
                        </Select>
                    </Form.Item>
                    <Form.Item name="status" label="Status">
                        <Select>
                            <Option value="Active">Active</Option>
                            <Option value="Inactive">Inactive</Option>
                        </Select>
                    </Form.Item>

                    <Form.Item label="Thumbnail">
                        <Upload
                            customRequest={async ({ file, onSuccess }) => {
                                try {
                                    const url = await uploadImage(file); // Upload image and get URL
                                    setThumbnailUrl(url);
                                    onSuccess(url); // Trigger onSuccess with URL
                                } catch (error) {
                                    console.error('Error uploading thumbnail:', error);
                                }
                            }}
                            showUploadList={false}
                            accept="image/*"
                        >
                            <Button icon={<UploadOutlined />}>Upload Thumbnail</Button>
                        </Upload>
                        {thumbnailUrl && (
                            <div className="mt-2 flex flex-col border shadow-dark w-max rounded">
                                <Image src={thumbnailUrl} width={100} />
                                <Button
                                    type="link"
                                    icon={<DeleteTwoTone twoToneColor="#ff0000" />}
                                    onClick={() => setThumbnailUrl('')}
                                    className='text-red-500 w-max'
                                >
                                    Remove
                                </Button>
                            </div>
                        )}
                    </Form.Item>

                    <Form.Item label="Images">
                        <Upload
                            customRequest={customRequestHandler}
                            showUploadList={false}
                            accept="image/*"
                            onChange={handleFileChange}
                            multiple
                        >
                            <Button icon={<UploadOutlined />}>Upload Images</Button>
                        </Upload>
                        {imagesUrls.length > 0 && (
                            <List
                                className='mt-4 overflow-x-hidden'
                                grid={{ gutter: 16, column: 4 }}
                                dataSource={imagesUrls}
                                renderItem={(item) => (
                                    <List.Item className='border shadow-dark rounded'>
                                        <Image src={item} height={100} width={100} />
                                        <Button
                                            type="link"
                                            icon={<DeleteTwoTone twoToneColor="#ff0000" />}
                                            onClick={() => removeImage(item)}
                                            className='text-red-500'
                                        >
                                            Remove
                                        </Button>
                                    </List.Item>
                                )}
                            />
                        )}
                    </Form.Item>
                </Form>
            </Modal>
        </div>
    );
};

export default Products;
Editor is loading...
Leave a Comment