diff --git a/src/components/ProductDetail.jsx b/src/components/ProductDetail.jsx
index e9d05b5..c966e94 100644
--- a/src/components/ProductDetail.jsx
+++ b/src/components/ProductDetail.jsx
@@ -1,8 +1,17 @@
-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 (
@@ -21,7 +30,9 @@ export default function ProductDetail() {
Available Samples
-
{selectedProduct.sales_price_max} - {selectedProduct.sales_price_min}
+
+ {selectedProduct.sales_price_max} - {selectedProduct.sales_price_min}
+
Sales Price
@@ -53,3 +64,71 @@ export default function ProductDetail() {
);
}
+
+export const CampaignsCollabCreators = () => {
+ const mockData = [
+ {
+ id: 1,
+ name: 'SUNLINK 拍拍灯',
+ creatorList: mockCreators,
+ },
+ {
+ id: 2,
+ name: 'SUNLINK 拍拍灯2',
+ creatorList: mockCreators,
+ },
+ ];
+
+ if (mockData.length === 0) {
+ return
No campaigns found
;
+ }
+
+ return (
+
+ 这个接口是用哪个?根据pid获取关联的活动-根据活动获取creatorList
+ {mockData.map((item) => (
+
+ {item.name}
+
+
+
+
+ Creator |
+ Category |
+ Followers |
+ GMV Generated |
+ Views Generated |
+ Pricing |
+ Status |
+
+
+
+ {item?.creatorList.length > 0 &&
+ item?.creatorList.map((creator) => (
+
+
+
+ 
+ {creator.name}
+
+ |
+ {creator.category} |
+ {creator.followers || '--'} |
+ {creator.gmv || '--'} |
+ {creator.views || '--'} |
+ {creator.pricing || '--'} |
+ {creator.status || '--'} |
+
+ ))}
+
+
+
+
+ ))}
+
+ );
+};
diff --git a/src/pages/BrandsDetail.jsx b/src/pages/BrandsDetail.jsx
index 8af5b86..caab28d 100644
--- a/src/pages/BrandsDetail.jsx
+++ b/src/pages/BrandsDetail.jsx
@@ -14,7 +14,7 @@ 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';
@@ -147,7 +147,10 @@ export default function BrandsDetail() {
title='Product Detail'
size='xxl'
>
-
+
>
)}
@@ -382,7 +385,7 @@ export function AddProductModal({ show, onHide }) {
const addProduct = () => {
const newIndex = Object.keys(productIds).length;
console.log(newIndex);
-
+
setProductIds((prev) => ({
...prev,
[newIndex]: '',
diff --git a/src/pages/CampaignScript.jsx b/src/pages/CampaignScript.jsx
index b70b597..527ac87 100644
--- a/src/pages/CampaignScript.jsx
+++ b/src/pages/CampaignScript.jsx
@@ -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
No campaigns found
;
- }
-
- return (
-
- {mockData.map((item) => (
-
- {item.name}
-
-
-
-
- Creator |
- Category |
- Followers |
- GMV Generated |
- Views Generated |
- Pricing |
- Status |
-
-
-
- {item?.creatorList.length > 0 &&
- item?.creatorList.map((creator) => (
-
-
-
- 
- {creator.name}
-
- |
- {creator.category} |
- {creator.followers || '--'} |
- {creator.gmv || '--'} |
- {creator.views || '--'} |
- {creator.pricing || '--'} |
- {creator.status || '--'} |
-
- ))}
-
-
-
-
- ))}
-
- );
-};
-
const FileUpload = () => {
const [files, setFiles] = useState([]);
diff --git a/src/store/slices/chatSlice.js b/src/store/slices/chatSlice.js
new file mode 100644
index 0000000..63dbdfb
--- /dev/null
+++ b/src/store/slices/chatSlice.js
@@ -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;
diff --git a/src/store/slices/discoverySlice.js b/src/store/slices/discoverySlice.js
index 9258733..4282b75 100644
--- a/src/store/slices/discoverySlice.js
+++ b/src/store/slices/discoverySlice.js
@@ -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;
});
},
});
diff --git a/src/store/slices/inboxSlice.js b/src/store/slices/inboxSlice.js
index 3f4f10e..73e3977 100644
--- a/src/store/slices/inboxSlice.js
+++ b/src/store/slices/inboxSlice.js
@@ -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) => {
diff --git a/src/store/slices/productSlice.js b/src/store/slices/productSlice.js
index fa4b116..2b47c2e 100644
--- a/src/store/slices/productSlice.js
+++ b/src/store/slices/productSlice.js
@@ -29,10 +29,24 @@ export const addProductToCampaign = createAsyncThunk('products/addProductToCampa
}
});
+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' }));
+ return rejectWithValue(error.message);
+ }
+});
+
const initialState = {
products: [],
loading: false,
error: null,
+ productDetail: null,
};
const productSlice = createSlice({
@@ -62,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;
+ })
},
});
diff --git a/src/styles/Brands.scss b/src/styles/Brands.scss
index 798c9b6..d81c1d7 100644
--- a/src/styles/Brands.scss
+++ b/src/styles/Brands.scss
@@ -420,3 +420,9 @@
gap: 1rem;
}
}
+
+.product-details-panel {
+ display: flex;
+ flex-flow: column nowrap;
+ gap: 1rem;
+}