diff --git a/src/App.jsx b/src/App.jsx
index 8b2347a..bbd1e4a 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -5,6 +5,7 @@ import Router from './router';
import './styles/Campaign.scss';
import '@/styles/custom-theme.scss';
import { Chart as ChartJS, ArcElement, Tooltip, Legend, BarElement, LinearScale, CategoryScale } from 'chart.js';
+import NotificationBar from './components/NotificationBar';
ChartJS.register(ArcElement, Tooltip, Legend, BarElement, LinearScale, CategoryScale);
@@ -12,7 +13,12 @@ ChartJS.register(ArcElement, Tooltip, Legend, BarElement, LinearScale, CategoryS
library.add(faTiktok, fas, faYoutube, faInstagram);
function App() {
- return ;
+ return (
+ <>
+
+
+ >
+ );
}
export default App;
diff --git a/src/components/AddToCampaign.jsx b/src/components/AddToCampaign.jsx
new file mode 100644
index 0000000..b4facb1
--- /dev/null
+++ b/src/components/AddToCampaign.jsx
@@ -0,0 +1,87 @@
+import { useEffect, useState } from 'react';
+import { Button, Form, Modal } from 'react-bootstrap';
+import { useDispatch, useSelector } from 'react-redux';
+import { fetchCampaigns } from '../store/slices/brandsSlice';
+import SpinningComponent from './Spinning';
+import { addCreatorsToCampaign } from '../store/slices/creatorsSlice';
+import { setNotificationBarMessage } from '../store/slices/notificationBarSlice';
+
+export default function AddToCampaign({ show, onHide }) {
+ const dispatch = useDispatch();
+ const { campaigns, status } = useSelector((state) => state.brands);
+ const { selectedCreators } = useSelector((state) => state.creators);
+ const [campaignId, setCampaignId] = useState(null);
+ const [validated, setValidated] = useState(false);
+
+ useEffect(() => {
+ if (show) {
+ dispatch(fetchCampaigns());
+ }
+
+ return () => {
+ setCampaignId(null);
+ setValidated(false);
+ };
+ }, [show, dispatch]);
+
+ const handleSubmit = async (e) => {
+ const form = document.getElementById('campaignForm');
+ if (form.checkValidity() === false) {
+ e.preventDefault();
+ e.stopPropagation();
+ setValidated(true);
+ return;
+ }
+
+ await dispatch(addCreatorsToCampaign({ creatorIds: selectedCreators, campaignId })).unwrap();
+ onHide();
+ };
+
+ if (status === 'loading') {
+ return ;
+ }
+
+ return (
+
+
+ Add to Campaign
+
+
+
+ Campaign Name
+ {
+ setCampaignId(e.target.value);
+ }}
+ >
+
+ {campaigns.map((campaign) => (
+
+ ))}
+
+
+ Please select a campaign.
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/NotificationBar.jsx b/src/components/NotificationBar.jsx
index bd9ab7f..e0b9350 100644
--- a/src/components/NotificationBar.jsx
+++ b/src/components/NotificationBar.jsx
@@ -1,21 +1,41 @@
import { Check, CircleAlert, Info, X } from 'lucide-react';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
+import { resetNotificationBar } from '../store/slices/notificationBarSlice';
+import { useEffect } from 'react';
export default function NotificationBar() {
const { message, type, show } = useSelector((state) => state.notificationBar);
const dispatch = useDispatch();
+ const setTimeOut = () => {
+ setTimeout(() => {
+ dispatch(resetNotificationBar());
+ }, 3000);
+ };
+
+ const handleClose = () => {
+ dispatch(resetNotificationBar());
+ };
+
+ useEffect(() => {
+ console.log(message);
+ setTimeOut();
+ }, [message]);
+
return (
-
- {type === 'success' &&
}
- {type === 'warning' &&
}
- {type === 'error' &&
}
- {type === 'info' &&
}
-
{message}
-
-
+ show && (
+
+ {type === 'success' &&
}
+ {type === 'warning' &&
}
+ {type === 'error' &&
}
+ {type === 'info' &&
}
+
{message}
+
+
+ )
);
}
diff --git a/src/components/Spinning.jsx b/src/components/Spinning.jsx
index 995dcbf..76d6f4b 100644
--- a/src/components/Spinning.jsx
+++ b/src/components/Spinning.jsx
@@ -1,6 +1,6 @@
import { Spinner } from "react-bootstrap";
-export default function Spinning() {
+export default function SpinningComponent() {
return (
diff --git a/src/pages/Database.jsx b/src/pages/Database.jsx
index 175e756..8d97a58 100644
--- a/src/pages/Database.jsx
+++ b/src/pages/Database.jsx
@@ -1,14 +1,18 @@
-import React from 'react';
+import React, { useState } from 'react';
import DatabaseFilter from '../components/DatabaseFilter';
import CreatorList from '../components/CreatorList';
import SearchBar from '../components/SearchBar';
import { Button } from 'react-bootstrap';
+import AddToCampaign from '../components/AddToCampaign';
+
export default function Database({ path }) {
+ const [showAddToCampaignModal, setShowAddToCampaignModal] = useState(false);
+
return (
-
+
Creator Database
@@ -18,6 +22,7 @@ export default function Database({ path }) {
+
setShowAddToCampaignModal(false)} onSubmit={() => setShowAddToCampaignModal(false)} />
);
}
diff --git a/src/store/index.js b/src/store/index.js
index 438d211..c033483 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -7,25 +7,25 @@ import sessionStorage from 'redux-persist/es/storage/session';
import inboxReducer from './slices/inboxSlice';
import authReducer from './slices/authSlice';
import discoveryReducer from './slices/discoverySlice';
+import notificationBarReducer from './slices/notificationBarSlice';
-const reducers = combineReducers({
+const authPersistConfig = {
+ key: 'auth',
+ storage: sessionStorage,
+};
+
+const rootReducer = combineReducers({
creators: creatorsReducer,
filters: filtersReducer,
brands: brandsReducer,
inbox: inboxReducer,
- auth: authReducer,
discovery: discoveryReducer,
+ auth: persistReducer(authPersistConfig, authReducer),
+ notificationBar: notificationBarReducer,
});
-const persistConfig = {
- key: 'root',
- storage: sessionStorage,
-};
-
-const persistedReducer = persistReducer(persistConfig, reducers);
-
const store = configureStore({
- reducer: persistedReducer,
+ reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
diff --git a/src/store/slices/brandsSlice.js b/src/store/slices/brandsSlice.js
index cf8e52a..311a6f1 100644
--- a/src/store/slices/brandsSlice.js
+++ b/src/store/slices/brandsSlice.js
@@ -123,8 +123,15 @@ export const fetchBrandProducts = createAsyncThunk('brands/fetchBrandProducts',
return response.data;
});
+export const fetchCampaigns = createAsyncThunk('brands/fetchCampaigns', async () => {
+ const response = await api.get('/campaigns/');
+ console.log(response);
+ return response.data;
+});
+
const initialState = {
brands: [],
+ campaigns: [],
status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'
error: null,
selectedBrand: {},
@@ -192,6 +199,17 @@ const brandsSlice = createSlice({
.addCase(fetchBrandProducts.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
+ })
+ .addCase(fetchCampaigns.pending, (state) => {
+ state.status = 'loading';
+ })
+ .addCase(fetchCampaigns.fulfilled, (state, action) => {
+ state.status = 'succeeded';
+ state.campaigns = action.payload;
+ })
+ .addCase(fetchCampaigns.rejected, (state, action) => {
+ state.status = 'failed';
+ state.error = action.error.message;
});
},
});
diff --git a/src/store/slices/creatorsSlice.js b/src/store/slices/creatorsSlice.js
index 7125708..55dc569 100644
--- a/src/store/slices/creatorsSlice.js
+++ b/src/store/slices/creatorsSlice.js
@@ -221,11 +221,11 @@ export const fetchCreators = createAsyncThunk(
async ({ page = 1 }, { getState, rejectWithValue }) => {
try {
const state = getState();
- const filters = state.filters;
+ const { pricing, views_range, ...filter } = state.filters;
const { code, data, message, pagination } = await api.post(
`/daren_detail/public/creators/filter/?page=${page}`,
- filters
+ { filter }
);
if (code === 200) {
return { data, pagination };
@@ -243,11 +243,11 @@ export const fetchPrivateCreators = createAsyncThunk(
async ({ page = 1 }, { getState, rejectWithValue, dispatch }) => {
try {
const state = getState();
- const filters = state.filters;
+ const { pricing, views_range, ...filter } = state.filters;
const { code, data, message, pagination } = await api.post(
`/daren_detail/private/pools/creators/filter/?page=${page}`,
- { pool_id: 1, filters }
+ { pool_id: 1, filter }
);
if (code === 200) {
return { data, pagination };
@@ -262,9 +262,38 @@ export const fetchPrivateCreators = createAsyncThunk(
export const fetchCreatorDetail = createAsyncThunk(
'creators/fetchCreatorDetail',
- async ({ creatorId }, { getState }) => {
- const response = await api.get(`/daren_detail/creators/${creatorId}`);
- return response;
+ async ({ creatorId }, { getState, rejectWithValue }) => {
+ try {
+ const response = await api.get(`/daren_detail/creators/${creatorId}`);
+ if (response.code === 200) {
+ return response;
+ } else {
+ throw new Error(response.message);
+ }
+ } catch (error) {
+ return rejectWithValue(error.message);
+ }
+ }
+);
+
+export const addCreatorsToCampaign = createAsyncThunk(
+ 'creators/addCreatorsToCampaign',
+ async ({ creatorIds, campaignId }, { getState, rejectWithValue }) => {
+ try {
+ const response = await api.post(`/daren_detail/campaigns/add/`, {
+ creator_ids: creatorIds,
+ campaign_id: campaignId,
+ });
+ if (response.code === 200) {
+ dispatch(setNotificationBarMessage({ message: 'Creators added to campaign successfully', type: 'success' }));
+ return response;
+ } else {
+ dispatch(setNotificationBarMessage({ message: response.message, type: 'error' }));
+ throw new Error(response.message);
+ }
+ } catch (error) {
+ return rejectWithValue(error.message);
+ }
}
);
@@ -284,9 +313,9 @@ const creatorsSlice = createSlice({
},
selectAllCreators: (state, action) => {
if (action.payload === 'database') {
- state.selectedCreators = state.publicCreators.map((creator) => creator.id);
+ state.selectedCreators = state.publicCreators.map((creator) => creator.creator_id);
} else {
- state.selectedCreators = state.privateCreators.map((creator) => creator.id);
+ state.selectedCreators = state.privateCreators.map((creator) => creator.creator_id);
}
},
clearCreatorSelection: (state) => {
diff --git a/src/store/slices/filtersSlice.js b/src/store/slices/filtersSlice.js
index 2ffac92..09e3d52 100644
--- a/src/store/slices/filtersSlice.js
+++ b/src/store/slices/filtersSlice.js
@@ -1,14 +1,14 @@
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
- category: ['Homes Supplies'],
- e_commerce_level: ['L2', 'L3'],
+ category: [],
+ e_commerce_level: [],
exposure_level: [],
- gmv_range: ['$5k - $25k', '$25k - $60k'],
+ gmv_range: [],
views_range: [0, 100000],
pricing: [0, 3000],
- sortBy: 'followers',
- sortDirection: 'desc',
+ // sortBy: 'followers',
+ // sortDirection: 'desc',
platform: '',
};
const filtersSlice = createSlice({
diff --git a/src/store/slices/notificationBarSlice.js b/src/store/slices/notificationBarSlice.js
index 4ae76f2..cfc515a 100644
--- a/src/store/slices/notificationBarSlice.js
+++ b/src/store/slices/notificationBarSlice.js
@@ -1,3 +1,5 @@
+import { createSlice } from "@reduxjs/toolkit";
+
const initialState = {
message: '',
show: false,
@@ -9,10 +11,10 @@ const notificationBarSlice = createSlice({
initialState,
reducers: {
setNotificationBarMessage: (state, action) => {
- state.message = action.payload;
- },
- setNotificationBarShow: (state, action) => {
- state.show = action.payload;
+ const { message, type } = action.payload;
+ state.message = message;
+ state.type = type;
+ state.show = true;
},
resetNotificationBar: (state) => {
state.message = '';
@@ -22,6 +24,6 @@ const notificationBarSlice = createSlice({
},
});
-export const { setNotificationBarMessage, setNotificationBarShow, resetNotificationBar } = notificationBarSlice.actions;
+export const { setNotificationBarMessage, resetNotificationBar } = notificationBarSlice.actions;
export default notificationBarSlice.reducer;