From dba045e0fe8e7a33ee4fb5bfa8d6d3c5788d89f7 Mon Sep 17 00:00:00 2001 From: susie-laptop Date: Fri, 23 May 2025 20:16:21 -0400 Subject: [PATCH] [dev]addtocampaign --- src/App.jsx | 8 ++- src/components/AddToCampaign.jsx | 87 ++++++++++++++++++++++++ src/components/NotificationBar.jsx | 44 ++++++++---- src/components/Spinning.jsx | 2 +- src/pages/Database.jsx | 9 ++- src/store/index.js | 20 +++--- src/store/slices/brandsSlice.js | 18 +++++ src/store/slices/creatorsSlice.js | 47 ++++++++++--- src/store/slices/filtersSlice.js | 10 +-- src/store/slices/notificationBarSlice.js | 12 ++-- 10 files changed, 212 insertions(+), 45 deletions(-) create mode 100644 src/components/AddToCampaign.jsx 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;