mirror of
https://github.com/Funkoala14/CreatorCenter_OOIN.git
synced 2025-06-07 22:58:14 +08:00
Compare commits
3 Commits
ce1b944103
...
a248d7dedf
Author | SHA1 | Date | |
---|---|---|---|
a248d7dedf | |||
855ea29b92 | |||
10f3420890 |
@ -1,7 +1,16 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import { useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { fetchProductDetail } from '../store/slices/productSlice';
|
||||
import { mockCreators } from '../store/slices/creatorsSlice';
|
||||
import { Accordion, Table } from 'react-bootstrap';
|
||||
|
||||
export default function ProductDetail() {
|
||||
const selectedProduct = useSelector((state) => state.brands.selectedProduct);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchProductDetail(selectedProduct.id));
|
||||
}, [selectedProduct.id]);
|
||||
|
||||
return (
|
||||
<div className='product-details shadow-xs'>
|
||||
@ -21,7 +30,9 @@ export default function ProductDetail() {
|
||||
<div className='product-detail-item-label'>Available Samples</div>
|
||||
</div>
|
||||
<div className='product-detail-item'>
|
||||
<div className='product-detail-item-value'>{selectedProduct.sales_price_max} - {selectedProduct.sales_price_min}</div>
|
||||
<div className='product-detail-item-value'>
|
||||
{selectedProduct.sales_price_max} - {selectedProduct.sales_price_min}
|
||||
</div>
|
||||
<div className='product-detail-item-label'>Sales Price</div>
|
||||
</div>
|
||||
<div className='product-detail-item'>
|
||||
@ -53,3 +64,71 @@ export default function ProductDetail() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const CampaignsCollabCreators = () => {
|
||||
const mockData = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'SUNLINK 拍拍灯',
|
||||
creatorList: mockCreators,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'SUNLINK 拍拍灯2',
|
||||
creatorList: mockCreators,
|
||||
},
|
||||
];
|
||||
|
||||
if (mockData.length === 0) {
|
||||
return <div className='text-center'>No campaigns found</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion className='campaigns-collab-creators-list' defaultActiveKey={mockData[0].id}>
|
||||
这个接口是用哪个?根据pid获取关联的活动-根据活动获取creatorList
|
||||
{mockData.map((item) => (
|
||||
<Accordion.Item eventKey={item.id} key={item.id} className='campaigns-collab-creators-item'>
|
||||
<Accordion.Header>{item.name}</Accordion.Header>
|
||||
<Accordion.Body>
|
||||
<Table responsive hover className='bg-white shadow-xs rounded overflow-hidden'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<th>Category</th>
|
||||
<th>Followers</th>
|
||||
<th>GMV Generated</th>
|
||||
<th>Views Generated</th>
|
||||
<th>Pricing</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{item?.creatorList.length > 0 &&
|
||||
item?.creatorList.map((creator) => (
|
||||
<tr key={creator.id}>
|
||||
<td>
|
||||
<div className='white-space-nowrap'>
|
||||
<img
|
||||
className='creator-avatar'
|
||||
src={creator.avatar}
|
||||
alt={creator.name}
|
||||
/>
|
||||
<span className='creator-name'>{creator.name}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{creator.category}</td>
|
||||
<td>{creator.followers || '--'}</td>
|
||||
<td>{creator.gmv || '--'}</td>
|
||||
<td>{creator.views || '--'}</td>
|
||||
<td>{creator.pricing || '--'}</td>
|
||||
<td>{creator.status || '--'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
</Accordion.Body>
|
||||
</Accordion.Item>
|
||||
))}
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
@ -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,18 +112,24 @@ 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>
|
||||
</div>
|
||||
</td>
|
||||
<td className='text-center' >
|
||||
<td className='text-center'>
|
||||
<div>{product.commission_rate}</div>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import SearchBar from '../components/SearchBar';
|
||||
import { Alert, Button, Form, Modal } from 'react-bootstrap';
|
||||
import { Folders, Hash, LinkIcon, Plus, Users } from 'lucide-react';
|
||||
import { Folders, Hash, LinkIcon, Plus, Users, X } from 'lucide-react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import CampaignList from '../components/CampaignList';
|
||||
@ -14,10 +14,11 @@ import {
|
||||
createCampaignThunk,
|
||||
} from '../store/slices/brandsSlice';
|
||||
import SlidePanel from '../components/SlidePanel';
|
||||
import ProductDetail from '../components/ProductDetail';
|
||||
import ProductDetail, { CampaignsCollabCreators } from '../components/ProductDetail';
|
||||
import { CAMPAIGN_SERVICES, CREATOR_CATEGORIES, CREATOR_LEVELS, CREATOR_TYPES, GMV_RANGES } from '../lib/constant';
|
||||
import RangeSlider from '../components/RangeSlider';
|
||||
import SpinningComponent from '../components/Spinning';
|
||||
import { setNotificationBarMessage } from '../store/slices/notificationBarSlice';
|
||||
|
||||
export default function BrandsDetail() {
|
||||
const { id } = useParams();
|
||||
@ -26,6 +27,7 @@ export default function BrandsDetail() {
|
||||
const { selectedBrand } = useSelector((state) => state.brands);
|
||||
const [showProductDetail, setShowProductDetail] = useState(false);
|
||||
const [showAddCampaignModal, setShowAddCampaignModal] = useState(false);
|
||||
const [showAddProductModal, setShowAddProductModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
@ -52,7 +54,7 @@ export default function BrandsDetail() {
|
||||
</Button>
|
||||
)}
|
||||
{activeTab === 'products' && (
|
||||
<Button>
|
||||
<Button onClick={() => setShowAddProductModal(true)}>
|
||||
<Plus />
|
||||
Add Product
|
||||
</Button>
|
||||
@ -137,6 +139,7 @@ export default function BrandsDetail() {
|
||||
<ProductsList
|
||||
products={selectedBrand?.products}
|
||||
onShowProductDetail={handleShowProductDetail}
|
||||
type='brand'
|
||||
/>
|
||||
<SlidePanel
|
||||
show={showProductDetail}
|
||||
@ -144,11 +147,15 @@ export default function BrandsDetail() {
|
||||
title='Product Detail'
|
||||
size='xxl'
|
||||
>
|
||||
<ProductDetail />
|
||||
<div className='product-details-panel'>
|
||||
<ProductDetail />
|
||||
<CampaignsCollabCreators />
|
||||
</div>
|
||||
</SlidePanel>
|
||||
</>
|
||||
)}
|
||||
<AddCampaignModal show={showAddCampaignModal} onHide={() => setShowAddCampaignModal(false)} />
|
||||
<AddProductModal show={showAddProductModal} onHide={() => setShowAddProductModal(false)} />
|
||||
</React.Fragment>
|
||||
)
|
||||
);
|
||||
@ -218,7 +225,7 @@ export function AddCampaignModal({ show, onHide }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal show={show} onHide={handleClose} >
|
||||
<Modal show={show} onHide={handleClose}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Add Campaign</Modal.Title>
|
||||
</Modal.Header>
|
||||
@ -368,3 +375,111 @@ export function AddCampaignModal({ show, onHide }) {
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export function AddProductModal({ show, onHide }) {
|
||||
const dispatch = useDispatch();
|
||||
const [productIds, setProductIds] = useState({
|
||||
0: '',
|
||||
});
|
||||
|
||||
const addProduct = () => {
|
||||
const newIndex = Object.keys(productIds).length;
|
||||
console.log(newIndex);
|
||||
|
||||
setProductIds((prev) => ({
|
||||
...prev,
|
||||
[newIndex]: '',
|
||||
}));
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setProductIds((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setProductIds([]);
|
||||
onHide();
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
dispatch(
|
||||
setNotificationBarMessage({
|
||||
message: '这个接口是哪一个?',
|
||||
variant: 'info',
|
||||
})
|
||||
);
|
||||
const form = document.getElementById('addProductForm');
|
||||
if (form.checkValidity() === false) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setValidated(true);
|
||||
return;
|
||||
}
|
||||
console.log(productIds);
|
||||
};
|
||||
|
||||
const removeProduct = (index) => {
|
||||
if (Object.keys(productIds).length === 1) {
|
||||
return;
|
||||
}
|
||||
setProductIds((prev) => {
|
||||
const updated = { ...prev };
|
||||
delete updated[index];
|
||||
return updated;
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
setProductIds({
|
||||
0: '',
|
||||
});
|
||||
}
|
||||
}, [show]);
|
||||
|
||||
return (
|
||||
<Modal show={show} onHide={handleClose}>
|
||||
<Modal.Header>
|
||||
<Modal.Title>Add Product</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Form id='addProductForm' onSubmit={handleSubmit}>
|
||||
<Modal.Body>
|
||||
{Object.entries(productIds).map(([index, productId]) => (
|
||||
<Form.Group key={Number(index)}>
|
||||
<Form.Label>Product {Number(index) + 1} PID</Form.Label>
|
||||
<div className='d-flex flex-row align-items-center justify-content-between'>
|
||||
<Form.Control
|
||||
type='text'
|
||||
name={index}
|
||||
value={productId}
|
||||
onChange={handleChange}
|
||||
placeholder='Enter Product ID'
|
||||
required
|
||||
/>
|
||||
<Button variant='link' className='border-0' onClick={() => removeProduct(index)}>
|
||||
<X />
|
||||
</Button>
|
||||
</div>
|
||||
</Form.Group>
|
||||
))}
|
||||
<Button variant='outline-primary' className='mt-3' onClick={addProduct}>
|
||||
Add Product
|
||||
</Button>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant='outline-primary' className='border-0' onClick={onHide}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant='primary' type='submit'>
|
||||
Submit
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Accordion, Button, Card, Col, Form, Row, Table } from 'react-bootstrap';
|
||||
import { Button, Card, Col, Form, Row } from 'react-bootstrap';
|
||||
import { CloudUpload, Paperclip } from 'lucide-react';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { mockCreators } from '../store/slices/creatorsSlice';
|
||||
import ProductDetail from '../components/ProductDetail';
|
||||
import ProductDetail, { CampaignsCollabCreators } from '../components/ProductDetail';
|
||||
|
||||
export default function CampaignScript() {
|
||||
const dispatch = useDispatch();
|
||||
@ -139,73 +138,6 @@ const CollabInfo = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const CampaignsCollabCreators = () => {
|
||||
const mockData = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'SUNLINK 拍拍灯',
|
||||
creatorList: mockCreators,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'SUNLINK 拍拍灯2',
|
||||
creatorList: mockCreators,
|
||||
},
|
||||
];
|
||||
|
||||
if (mockData.length === 0) {
|
||||
return <div className='text-center'>No campaigns found</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion className='campaigns-collab-creators-list' defaultActiveKey={mockData[0].id}>
|
||||
{mockData.map((item) => (
|
||||
<Accordion.Item eventKey={item.id} key={item.id} className='campaigns-collab-creators-item'>
|
||||
<Accordion.Header>{item.name}</Accordion.Header>
|
||||
<Accordion.Body>
|
||||
<Table responsive hover className='bg-white shadow-xs rounded overflow-hidden'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<th>Category</th>
|
||||
<th>Followers</th>
|
||||
<th>GMV Generated</th>
|
||||
<th>Views Generated</th>
|
||||
<th>Pricing</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{item?.creatorList.length > 0 &&
|
||||
item?.creatorList.map((creator) => (
|
||||
<tr key={creator.id}>
|
||||
<td>
|
||||
<div className='white-space-nowrap'>
|
||||
<img
|
||||
className='creator-avatar'
|
||||
src={creator.avatar}
|
||||
alt={creator.name}
|
||||
/>
|
||||
<span className='creator-name'>{creator.name}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{creator.category}</td>
|
||||
<td>{creator.followers || '--'}</td>
|
||||
<td>{creator.gmv || '--'}</td>
|
||||
<td>{creator.views || '--'}</td>
|
||||
<td>{creator.pricing || '--'}</td>
|
||||
<td>{creator.status || '--'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
</Accordion.Body>
|
||||
</Accordion.Item>
|
||||
))}
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
const FileUpload = () => {
|
||||
const [files, setFiles] = useState([]);
|
||||
|
||||
|
41
src/store/slices/chatSlice.js
Normal file
41
src/store/slices/chatSlice.js
Normal file
@ -0,0 +1,41 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
export const fetchChats = createAsyncThunk('chat/fetchChats', async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await api.get(`/chat-history/search/`);
|
||||
if (response.code === 200) {
|
||||
return response.data;
|
||||
}
|
||||
throw new Error(response.message);
|
||||
} catch (error) {
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
});
|
||||
|
||||
const initialState = {
|
||||
chats: [],
|
||||
currentChat: null,
|
||||
status: 'idle',
|
||||
error: null,
|
||||
};
|
||||
|
||||
const chatSlice = createSlice({
|
||||
name: 'chat',
|
||||
initialState,
|
||||
reducers: {},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(fetchChats.pending, (state) => {
|
||||
state.status = 'loading';
|
||||
})
|
||||
builder.addCase(fetchChats.fulfilled, (state, action) => {
|
||||
state.status = 'succeeded';
|
||||
state.chats = action.payload;
|
||||
})
|
||||
builder.addCase(fetchChats.rejected, (state, action) => {
|
||||
state.status = 'failed';
|
||||
state.error = action.payload;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default chatSlice.reducer;
|
@ -1,4 +1,6 @@
|
||||
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||
import api from '@/services/api';
|
||||
import { setNotificationBarMessage } from './notificationBarSlice';
|
||||
|
||||
const mockCreators = [
|
||||
{
|
||||
@ -22,11 +24,38 @@ const mockCreators = [
|
||||
date: '2021-01-01',
|
||||
},
|
||||
];
|
||||
export const fetchDiscovery = createAsyncThunk('discovery/fetchDiscovery', async (search) => {
|
||||
// const response = await fetch('/api/discovery');
|
||||
// return response.json();
|
||||
return mockCreators;
|
||||
});
|
||||
export const fetchDiscovery = createAsyncThunk(
|
||||
'discovery/fetchDiscovery',
|
||||
async (searchParams, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await api.post('/creators/search/', searchParams);
|
||||
if (response.code === 200) {
|
||||
return response.data;
|
||||
}
|
||||
throw new Error(response.message);
|
||||
} catch (error) {
|
||||
dispatch(setNotificationBarMessage({ message: error.message, type: 'error' }));
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const fetchDiscoveryByMode = createAsyncThunk(
|
||||
'discovery/fetchDiscoveryByMode',
|
||||
async (params, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await api.post('/discovery/creators/search_tags/', params);
|
||||
if (response.code === 200) {
|
||||
return response.data;
|
||||
}
|
||||
throw new Error(response.message);
|
||||
} catch (error) {
|
||||
dispatch(setNotificationBarMessage({ message: error.message, type: 'error' }));
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const initialState = {
|
||||
creators: [],
|
||||
status: 'idle',
|
||||
@ -49,6 +78,17 @@ const discoverySlice = createSlice({
|
||||
.addCase(fetchDiscovery.rejected, (state, action) => {
|
||||
state.status = 'failed';
|
||||
state.error = action.error.message;
|
||||
})
|
||||
.addCase(fetchDiscoveryByMode.pending, (state) => {
|
||||
state.status = 'loading';
|
||||
})
|
||||
.addCase(fetchDiscoveryByMode.fulfilled, (state, action) => {
|
||||
state.status = 'succeeded';
|
||||
state.creators = action.payload;
|
||||
})
|
||||
.addCase(fetchDiscoveryByMode.rejected, (state, action) => {
|
||||
state.status = 'failed';
|
||||
state.error = action.error.message;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||
import { format, isToday, parseISO } from 'date-fns';
|
||||
import api from '@/services/api';
|
||||
|
||||
const mockTemplates = [
|
||||
{
|
||||
id: 1,
|
||||
@ -169,13 +171,16 @@ const chatDateFormat = (date) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchInboxList = createAsyncThunk('inbox/fetchInboxList', async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
const formattedInboxList = mockInboxList.map((item) => ({
|
||||
...item,
|
||||
date: chatDateFormat(item.date),
|
||||
}));
|
||||
return formattedInboxList;
|
||||
export const fetchInboxList = createAsyncThunk('inbox/fetchInboxList', async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await api.get(`/chat-history/`);
|
||||
if (response.code === 200) {
|
||||
return response.data;
|
||||
}
|
||||
throw new Error(response.message);
|
||||
} catch (error) {
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
});
|
||||
|
||||
export const fetchChatHistory = createAsyncThunk('inbox/fetchChatHistory', async (id) => {
|
||||
|
@ -21,8 +21,20 @@ 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' }));
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
});
|
||||
|
||||
export const fetchProductDetail = createAsyncThunk('products/fetchProductDetail', async (productId, { rejectWithValue, dispatch }) => {
|
||||
try {
|
||||
const response = await api.get(`/products/${productId}/`);
|
||||
if (response.code !== 200) {
|
||||
throw new Error(response.message);
|
||||
}
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
dispatch(setNotificationBarMessage({ message: error.message, type: 'error' }));
|
||||
@ -34,6 +46,7 @@ const initialState = {
|
||||
products: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
productDetail: null,
|
||||
};
|
||||
|
||||
const productSlice = createSlice({
|
||||
@ -63,6 +76,17 @@ const productSlice = createSlice({
|
||||
state.error = action.payload;
|
||||
state.loading = false;
|
||||
})
|
||||
builder.addCase(fetchProductDetail.pending, (state) => {
|
||||
state.loading = true;
|
||||
})
|
||||
builder.addCase(fetchProductDetail.fulfilled, (state, action) => {
|
||||
state.productDetail = action.payload;
|
||||
state.loading = false;
|
||||
})
|
||||
builder.addCase(fetchProductDetail.rejected, (state, action) => {
|
||||
state.error = action.payload;
|
||||
state.loading = false;
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -411,3 +411,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#addProductForm {
|
||||
.modal-body {
|
||||
width: 400px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.product-details-panel {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user