diff --git a/src/components/CreatorList.jsx b/src/components/CreatorList.jsx
index 72f2eb0..843ed26 100644
--- a/src/components/CreatorList.jsx
+++ b/src/components/CreatorList.jsx
@@ -25,6 +25,7 @@ export default function CreatorList({ path }) {
hasMore,
isLoadingMore,
pagination,
+ error,
} = useSelector((state) => state.creators);
const { sortBy, sortDirection } = useSelector((state) => state.filters);
const observer = useRef();
@@ -49,7 +50,8 @@ export default function CreatorList({ path }) {
}, [path, dispatch]);
useEffect(() => {
- }, [publicCreators]);
+ console.log(publicCreators, status);
+ }, [publicCreators, status]);
// 处理全选/取消全选
const handleSelectAll = (e) => {
@@ -67,11 +69,13 @@ export default function CreatorList({ path }) {
// 处理排序
const handleSort = (field) => {
+ return;
dispatch(setSortBy(field));
};
// 渲染排序图标
const renderSortIcon = (field) => {
+ return;
if (sortBy === field) {
return sortDirection === 'asc' ? : ;
}
@@ -93,7 +97,7 @@ export default function CreatorList({ path }) {
};
// 如果正在加载且没有数据,显示加载中
- if (status === 'loading' && (!publicCreators || publicCreators.length === 0)) {
+ if (status === 'loading' && !isLoadingMore) {
return (
@@ -105,7 +109,7 @@ export default function CreatorList({ path }) {
// 如果加载失败,显示错误信息
if (status === 'failed') {
- return Failed to load creators. Please try again later.
;
+ return {error || 'Failed to load creators. Please try again later.'}
;
}
return (
@@ -117,7 +121,9 @@ export default function CreatorList({ path }) {
0}
+ checked={
+ selectedCreators.length === publicCreators.length && publicCreators.length > 0
+ }
onChange={handleSelectAll}
/>
|
diff --git a/src/components/DatabaseFilter.jsx b/src/components/DatabaseFilter.jsx
index 02e8578..409803a 100644
--- a/src/components/DatabaseFilter.jsx
+++ b/src/components/DatabaseFilter.jsx
@@ -10,8 +10,9 @@ import {
toggleGmvRange,
setViewsRange,
setPricingRange,
+ setPlatform,
} from '../store/slices/filtersSlice';
-import { fetchCreators } from '../store/slices/creatorsSlice';
+import { fetchCreators, resetCreators } from '../store/slices/creatorsSlice';
import '../styles/DatabaseFilter.scss';
export default function DatabaseFilter({ path, pageType = 'database' }) {
@@ -63,23 +64,23 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
};
// 本地状态用于表单控制
- const [minViews, setMinViews] = useState(filters.viewsRange[0]);
- const [maxViews, setMaxViews] = useState(filters.viewsRange[1]);
- const [minPricing, setMinPricing] = useState(filters.pricingRange[0]);
- const [maxPricing, setMaxPricing] = useState(filters.pricingRange[1]);
+ const [minViews, setMinViews] = useState(filters.views_range[0]);
+ const [maxViews, setMaxViews] = useState(filters.views_range[1]);
+ const [minPricing, setMinPricing] = useState(filters.pricing[0]);
+ const [maxPricing, setMaxPricing] = useState(filters.pricing[1]);
// 监听Redux状态变化,更新本地表单状态
useEffect(() => {
- setMinViews(filters.viewsRange[0]);
- setMaxViews(filters.viewsRange[1]);
- setMinPricing(filters.pricingRange[0]);
- setMaxPricing(filters.pricingRange[1]);
- }, [filters.viewsRange, filters.pricingRange]);
+ setMinViews(filters.views_range[0]);
+ setMaxViews(filters.views_range[1]);
+ setMinPricing(filters.pricing[0]);
+ setMaxPricing(filters.pricing[1]);
+ }, [filters.views_range, filters.pricing]);
// 组件加载时获取数据
useEffect(() => {
-
- }, [dispatch, filters, pageType, path]);
+ dispatch(setPlatform(path));
+ }, [dispatch, path]);
// 处理类别选择
const handleCategorySelect = (category) => {
@@ -104,6 +105,7 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
// 处理视图范围更新
const handleViewsRangeChange = (newRange) => {
dispatch(setViewsRange(newRange));
+ resetAndFetch();
};
// 处理min input变更
@@ -120,6 +122,7 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
const handlePricingRangeChange = (newRange) => {
dispatch(setPricingRange(newRange));
+ resetAndFetch();
};
// 处理min pricing input变更
@@ -145,7 +148,7 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
const finalValue = Math.min(discreteValue, maxViews);
setMinViews(finalValue);
- dispatch(setViewsRange([finalValue, filters.viewsRange[1]]));
+ dispatch(setViewsRange([finalValue, filters.views_range[1]]));
} else {
// 找到最接近的离散值
const closestIndex = findClosestDiscreteIndex(maxViews);
@@ -155,7 +158,7 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
const finalValue = Math.max(discreteValue, minViews);
setMaxViews(finalValue);
- dispatch(setViewsRange([filters.viewsRange[0], finalValue]));
+ dispatch(setViewsRange([filters.views_range[0], finalValue]));
}
};
@@ -169,7 +172,7 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
const finalValue = Math.min(discreteValue, maxPricing);
setMinPricing(finalValue);
- dispatch(setPricingRange([finalValue, filters.pricingRange[1]]));
+ dispatch(setPricingRange([finalValue, filters.pricing[1]]));
} else {
const closestIndex = findClosestDiscreteIndex(maxPricing);
const discreteValue = discretePricingValues[closestIndex];
@@ -178,7 +181,7 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
const finalValue = Math.max(discreteValue, minPricing);
setMaxPricing(finalValue);
- dispatch(setPricingRange([filters.pricingRange[0], finalValue]));
+ dispatch(setPricingRange([filters.pricing[0], finalValue]));
}
};
@@ -193,6 +196,43 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
return value;
};
+ const resetAndFetch = () => {
+ dispatch(resetCreators());
+ if (pageType === 'private') {
+ dispatch(fetchPrivateCreators({ page: 1 }));
+ } else {
+ dispatch(fetchCreators({ page: 1 }));
+ }
+ };
+
+ const onChange = (e) => {
+ const name = e.target.name;
+ const value = e.target.value;
+ switch (name) {
+ case 'category':
+ handleCategorySelect(value);
+ break;
+ case 'e_commerce_level':
+ handleEcommerceRatingSelect(value);
+ break;
+ case 'exposure_level':
+ handleExposureRatingSelect(value);
+ break;
+ case 'gmv_range':
+ handleGmvRangeSelect(value);
+ break;
+ case 'views_range':
+ handleViewsRangeChange(value);
+ break;
+ case 'pricing':
+ handlePricingRangeChange(value);
+ break;
+ default:
+ break;
+ }
+ resetAndFetch();
+ };
+
return (
@@ -207,7 +247,9 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
key={category}
variant={filters.category.includes(category) ? 'primary' : 'light'}
className='rounded-pill'
- onClick={() => handleCategorySelect(category)}
+ onClick={onChange}
+ name='category'
+ value={category}
>
{category}
@@ -222,9 +264,11 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
{ecommerceRatings.map((rating) => (
@@ -239,9 +283,11 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
{exposureRatings.map((rating) => (
@@ -256,9 +302,11 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
{gmvRanges.map((range) => (
@@ -273,7 +321,7 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
@@ -313,7 +361,7 @@ export default function DatabaseFilter({ path, pageType = 'database' }) {
diff --git a/src/components/NotificationBar.jsx b/src/components/NotificationBar.jsx
new file mode 100644
index 0000000..bd9ab7f
--- /dev/null
+++ b/src/components/NotificationBar.jsx
@@ -0,0 +1,21 @@
+import { Check, CircleAlert, Info, X } from 'lucide-react';
+import { useDispatch } from 'react-redux';
+
+export default function NotificationBar() {
+ const { message, type, show } = useSelector((state) => state.notificationBar);
+ const dispatch = useDispatch();
+
+ return (
+
+ {type === 'success' &&
}
+ {type === 'warning' &&
}
+ {type === 'error' &&
}
+ {type === 'info' &&
}
+
{message}
+
+
+ );
+}
diff --git a/src/components/PrivateCreatorList.jsx b/src/components/PrivateCreatorList.jsx
index 2c2f001..ae12900 100644
--- a/src/components/PrivateCreatorList.jsx
+++ b/src/components/PrivateCreatorList.jsx
@@ -15,7 +15,7 @@ import { Link } from 'react-router-dom';
export default function PrivateCreatorList({ path }) {
const dispatch = useDispatch();
- const { privateCreators, status, selectedCreators, hasMore, isLoadingMore, pagination } = useSelector(
+ const { privateCreators, status, selectedCreators, hasMore, isLoadingMore, pagination, error } = useSelector(
(state) => state.creators
);
const { sortBy, sortDirection } = useSelector((state) => state.filters);
@@ -60,11 +60,13 @@ export default function PrivateCreatorList({ path }) {
// 处理排序
const handleSort = (field) => {
+ return;
dispatch(setSortBy(field));
};
// 渲染排序图标
const renderSortIcon = (field) => {
+ return;
if (sortBy === field) {
return sortDirection === 'asc' ? : ;
}
@@ -86,7 +88,7 @@ export default function PrivateCreatorList({ path }) {
};
// 如果正在加载且没有数据,显示加载中
- if (status === 'loading' && (!privateCreators || privateCreators.length === 0)) {
+ if (status === 'loading' && !isLoadingMore) {
return (
@@ -98,7 +100,7 @@ export default function PrivateCreatorList({ path }) {
// 如果加载失败,显示错误信息
if (status === 'failed') {
- return Failed to load creators. Please try again later.
;
+ return {error || 'Failed to load creators. Please try again later.'}
;
}
return (
diff --git a/src/store/slices/creatorsSlice.js b/src/store/slices/creatorsSlice.js
index 51587ff..7125708 100644
--- a/src/store/slices/creatorsSlice.js
+++ b/src/store/slices/creatorsSlice.js
@@ -216,24 +216,47 @@ const initialState = {
isLoadingMore: false,
};
-export const fetchCreators = createAsyncThunk('creators/fetchCreators', async ({ path, page = 1 }, { getState }) => {
- const state = getState();
- const filters = state.filters;
+export const fetchCreators = createAsyncThunk(
+ 'creators/fetchCreators',
+ async ({ page = 1 }, { getState, rejectWithValue }) => {
+ try {
+ const state = getState();
+ const filters = state.filters;
- const response = await api.get(`/daren_detail/public/creators`, { params: { page } });
- console.log(response);
- return response;
-});
+ const { code, data, message, pagination } = await api.post(
+ `/daren_detail/public/creators/filter/?page=${page}`,
+ filters
+ );
+ if (code === 200) {
+ return { data, pagination };
+ } else {
+ throw new Error(message);
+ }
+ } catch (error) {
+ return rejectWithValue(error.message);
+ }
+ }
+);
export const fetchPrivateCreators = createAsyncThunk(
'creators/fetchPrivateCreators',
- async ({ path, page = 1 }, { getState }) => {
- const state = getState();
- const filters = state.filters;
+ async ({ page = 1 }, { getState, rejectWithValue, dispatch }) => {
+ try {
+ const state = getState();
+ const filters = state.filters;
- const queryParams = { pool_id: 1, page };
- const response = await api.get(`/daren_detail/private/pools/creators`, { params: queryParams });
- return response;
+ const { code, data, message, pagination } = await api.post(
+ `/daren_detail/private/pools/creators/filter/?page=${page}`,
+ { pool_id: 1, filters }
+ );
+ if (code === 200) {
+ return { data, pagination };
+ } else {
+ throw new Error(message);
+ }
+ } catch (error) {
+ return rejectWithValue(error.message);
+ }
}
);
@@ -283,8 +306,8 @@ const creatorsSlice = createSlice({
extraReducers: (builder) => {
builder
.addCase(fetchCreators.pending, (state) => {
+ state.status = 'loading';
if (state.publicCreators.length === 0) {
- state.status = 'loading';
} else {
state.isLoadingMore = true;
}
@@ -304,7 +327,7 @@ const creatorsSlice = createSlice({
.addCase(fetchCreators.rejected, (state, action) => {
state.status = 'failed';
state.isLoadingMore = false;
- state.error = action.error.message;
+ state.error = action.payload;
})
.addCase(fetchPrivateCreators.pending, (state) => {
if (state.privateCreators?.length === 0) {
@@ -330,7 +353,7 @@ const creatorsSlice = createSlice({
console.log('fetchPrivateCreators.rejected', action);
state.status = 'failed';
state.isLoadingMore = false;
- state.error = action.error.message;
+ state.error = action.payload;
})
.addCase(fetchCreatorDetail.pending, (state) => {
state.status = 'loading';
diff --git a/src/store/slices/filtersSlice.js b/src/store/slices/filtersSlice.js
index 0b9c9a9..2ffac92 100644
--- a/src/store/slices/filtersSlice.js
+++ b/src/store/slices/filtersSlice.js
@@ -2,13 +2,14 @@ import { createSlice } from '@reduxjs/toolkit';
const initialState = {
category: ['Homes Supplies'],
- ecommerceRatings: ['L2', 'L3'],
- exposureRatings: [],
- gmvRanges: ['$5k - $25k', '$25k - $60k'],
- viewsRange: [0, 100000],
- pricingRange: [0, 3000],
+ e_commerce_level: ['L2', 'L3'],
+ exposure_level: [],
+ gmv_range: ['$5k - $25k', '$25k - $60k'],
+ views_range: [0, 100000],
+ pricing: [0, 3000],
sortBy: 'followers',
sortDirection: 'desc',
+ platform: '',
};
const filtersSlice = createSlice({
name: 'filters',
@@ -24,33 +25,33 @@ const filtersSlice = createSlice({
},
toggleEcommerceRating: (state, action) => {
const rating = action.payload;
- if (state.ecommerceRatings.includes(rating)) {
- state.ecommerceRatings = state.ecommerceRatings.filter((r) => r !== rating);
+ if (state.e_commerce_level.includes(rating)) {
+ state.e_commerce_level = state.e_commerce_level.filter((r) => r !== rating);
} else {
- state.ecommerceRatings.push(rating);
+ state.e_commerce_level.push(rating);
}
},
toggleExposureRating: (state, action) => {
const rating = action.payload;
- if (state.exposureRatings.includes(rating)) {
- state.exposureRatings = state.exposureRatings.filter((r) => r !== rating);
+ if (state.exposure_level.includes(rating)) {
+ state.exposure_level = state.exposure_level.filter((r) => r !== rating);
} else {
- state.exposureRatings.push(rating);
+ state.exposure_level.push(rating);
}
},
toggleGmvRange: (state, action) => {
const range = action.payload;
- if (state.gmvRanges.includes(range)) {
- state.gmvRanges = state.gmvRanges.filter((r) => r !== range);
+ if (state.gmv_range.includes(range)) {
+ state.gmv_range = state.gmv_range.filter((r) => r !== range);
} else {
- state.gmvRanges.push(range);
+ state.gmv_range.push(range);
}
},
setViewsRange: (state, action) => {
- state.viewsRange = action.payload;
+ state.views_range = action.payload;
},
setPricingRange: (state, action) => {
- state.pricingRange = action.payload;
+ state.pricing = action.payload;
},
setSortBy: (state, action) => {
// 如果选择了当前已激活的排序项,则切换排序方向
@@ -62,6 +63,9 @@ const filtersSlice = createSlice({
state.sortDirection = 'desc';
}
},
+ setPlatform: (state, action) => {
+ state.platform = action.payload;
+ },
resetFilters: () => {
return initialState;
},
@@ -76,6 +80,7 @@ export const {
setViewsRange,
setPricingRange,
setSortBy,
+ setPlatform,
resetFilters,
} = filtersSlice.actions;
diff --git a/src/store/slices/notificationBarSlice.js b/src/store/slices/notificationBarSlice.js
new file mode 100644
index 0000000..4ae76f2
--- /dev/null
+++ b/src/store/slices/notificationBarSlice.js
@@ -0,0 +1,27 @@
+const initialState = {
+ message: '',
+ show: false,
+ type: 'success', // success, warning, error, info
+};
+
+const notificationBarSlice = createSlice({
+ name: 'notificationBar',
+ initialState,
+ reducers: {
+ setNotificationBarMessage: (state, action) => {
+ state.message = action.payload;
+ },
+ setNotificationBarShow: (state, action) => {
+ state.show = action.payload;
+ },
+ resetNotificationBar: (state) => {
+ state.message = '';
+ state.show = false;
+ state.type = 'success';
+ },
+ },
+});
+
+export const { setNotificationBarMessage, setNotificationBarShow, resetNotificationBar } = notificationBarSlice.actions;
+
+export default notificationBarSlice.reducer;