[dev]add prod to campaign

from band page
This commit is contained in:
susie-laptop 2025-05-26 12:26:10 -04:00
parent 10f3420890
commit 855ea29b92
5 changed files with 85 additions and 17 deletions

View File

@ -1,15 +1,20 @@
import React, { useEffect, useState } from 'react';
import { Table, Form } from 'react-bootstrap';
import { Table, Form, Button, Modal } from 'react-bootstrap';
import { useSelector, useDispatch } from 'react-redux';
import '../styles/Products.scss';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import Spinning from './Spinning';
import { Plus } from 'lucide-react';
import SpinningComponent from './Spinning';
import { addProductToCampaign } from '../store/slices/productSlice';
export default function ProductsList({ products, setSelectedProduct, onShowProductDetail }) {
const { selectedBrand } = useSelector((state) => state.brands);
export default function ProductsList({ products, onShowProductDetail, type = 'default' }) {
const { selectedBrand, status } = useSelector((state) => state.brands);
const [sortField, setSortField] = useState(null);
const [sortDirection, setSortDirection] = useState('asc');
const [selectedProducts, setSelectedProducts] = useState([]);
const [showAddProductToCampaignModal, setShowAddProductToCampaignModal] = useState(false);
const [selectedProduct, setSelectedProduct] = useState(null);
const handleSort = (field) => {
return;
@ -44,10 +49,9 @@ export default function ProductsList({ products, setSelectedProduct, onShowProdu
};
if (status === 'loading') {
return <Spinning />;
return <SpinningComponent />;
}
return (
<div className='products-list rounded shadow-xs'>
<Table responsive hover className='bg-white rounded overflow-hidden m-0'>
@ -87,6 +91,7 @@ export default function ProductsList({ products, setSelectedProduct, onShowProdu
<th className='tiktokShop text-center' onClick={() => handleSort('tiktokShop')}>
TikTok Shop {renderSortIcon('tiktokShop')}
</th>
{type === 'brand' && <th className='actions text-center'>Add To Campaign</th>}
</tr>
</thead>
<tbody>
@ -107,7 +112,11 @@ export default function ProductsList({ products, setSelectedProduct, onShowProdu
/>
</td>
<td className='product-cell'>
<div className='d-flex align-items-center' onClick={() => onShowProductDetail(product)} style={{cursor: 'pointer'}}>
<div
className='d-flex align-items-center'
onClick={() => onShowProductDetail(product)}
style={{ cursor: 'pointer' }}
>
<img className='product-logo' src={product.image_url} alt={product.name} />
{/* <div className='product-logo'>{product.name.slice(0, 1)}</div> */}
<div className='product-name'>{product.name}</div>
@ -118,7 +127,9 @@ export default function ProductsList({ products, setSelectedProduct, onShowProdu
<div className='small text-muted'>Open collab. {product.open_collab}</div>
</td>
<td className='text-center'>{product.available_samples}</td>
<td className='text-center'>{product.sales_price_min} - {product.sales_price_max}</td>
<td className='text-center'>
{product.sales_price_min} - {product.sales_price_max}
</td>
<td className='text-center'>{product.stock}</td>
<td className='text-center'>{product.items_sold}</td>
<td className='text-center'>
@ -126,12 +137,74 @@ export default function ProductsList({ products, setSelectedProduct, onShowProdu
<div className='small text-muted'>{product.reviews_count} Reviews</div>
</td>
<td className='text-center'>{product.collab_creators}</td>
<td className='text-center'>{product.tiktok_shop && <FontAwesomeIcon icon='fa-brands fa-tiktok' />}</td>
<td className='text-center'>
{product.tiktok_shop && <FontAwesomeIcon icon='fa-brands fa-tiktok' />}
</td>
{type === 'brand' && (
<td className='text-center'>
<button className='border-0 bg-transparent text-primary' onClick={() => {
setSelectedProduct(product.id);
setShowAddProductToCampaignModal(true);
}}>
<Plus />
</button>
</td>
)}
</tr>
))
)}
</tbody>
</Table>
<AddProductToCampaignModal show={showAddProductToCampaignModal} onHide={() => setShowAddProductToCampaignModal(false)} productId={selectedProduct} />
</div>
);
}
function AddProductToCampaignModal({ show, onHide, productId }) {
const [campaignId, setCampaignId] = useState(null);
const { selectedBrand } = useSelector((state) => state.brands);
const dispatch = useDispatch();
const handleSubmit = async (e) => {
e.preventDefault();
console.log(campaignId, productId);
await dispatch(addProductToCampaign({ campaignId, productId })).unwrap();
handleCancel();
};
const handleCancel = () => {
setCampaignId(null);
onHide();
};
return (
<Modal show={show} onHide={onHide} size='md'>
<Modal.Header closeButton className='fw-bold'>
Add Product
</Modal.Header>
<Form onSubmit={handleSubmit}>
<Modal.Body>
<Form.Group>
<Form.Label>Campaign Name</Form.Label>
<Form.Select required value={campaignId} onChange={(e) => setCampaignId(e.target.value)}>
<option value=''>Select Campaign</option>
{selectedBrand?.campaigns?.map((campaign) => (
<option key={campaign.id} value={campaign.id}>
{campaign.name}
</option>
))}
</Form.Select>
</Form.Group>
</Modal.Body>
<Modal.Footer>
<Button variant='outline-primary' className='border-0' onClick={handleCancel}>
Cancel
</Button>
<Button variant='primary' type='submit'>
Add
</Button>
</Modal.Footer>
</Form>
</Modal>
);
}

View File

@ -139,6 +139,7 @@ export default function BrandsDetail() {
<ProductsList
products={selectedBrand?.products}
onShowProductDetail={handleShowProductDetail}
type='brand'
/>
<SlidePanel
show={showProductDetail}

View File

@ -170,7 +170,7 @@ function AddProductModal({ campaignId, show, onHide }) {
};
return (
<Modal show={show} onHide={handleCancel}>
<Modal show={show} onHide={handleCancel} size='sm'>
<Modal.Header closeButton className='fw-bold'>
Add Product
</Modal.Header>

View File

@ -21,8 +21,7 @@ export const addProductToCampaign = createAsyncThunk('products/addProductToCampa
if (response.code !== 201 && response.code !== 200) {
throw new Error(response.message);
}
console.log(response);
dispatch(setNotificationBarMessage({ message: response.message, type: 'success' }));
return response.data;
} catch (error) {
dispatch(setNotificationBarMessage({ message: error.message, type: 'error' }));

View File

@ -134,8 +134,3 @@ a {
box-shadow: 0px 0px 1px #171a1f12, 0px 0px 2px #171a1f1f; /* shadow-xs */
padding: 1rem;
}
.modal-content {
width: max-content;
max-width: 60vw;
}