2025-05-09 10:18:49 +08:00
|
|
|
|
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
2025-05-23 09:22:51 +08:00
|
|
|
|
import api from '@/services/api';
|
2025-05-29 04:26:34 +08:00
|
|
|
|
import { setNotificationBarMessage } from './notificationBarSlice';
|
2025-05-23 09:22:51 +08:00
|
|
|
|
|
2025-05-21 22:49:54 +08:00
|
|
|
|
const mockVideos = [
|
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
title: 'Collagen + Biotin = your beauty routine’s new besties. For hair, skin, nails, and join...',
|
|
|
|
|
picture: 'https://api.dicebear.com/7.x/micah/svg?seed=1',
|
|
|
|
|
releaseTime: '2025-01-01',
|
|
|
|
|
views: 1000,
|
|
|
|
|
likes: 100,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 2,
|
|
|
|
|
title: 'Collagen + Biotin = your beauty routine’s new besties. For hair, skin, nails, and join...',
|
|
|
|
|
picture: 'https://api.dicebear.com/7.x/micah/svg?seed=2',
|
|
|
|
|
releaseTime: '2025-01-01',
|
|
|
|
|
views: 1000,
|
|
|
|
|
likes: 100,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 3,
|
|
|
|
|
title: 'Collagen + Biotin = your beauty routine’s new besties. For hair, skin, nails, and join...',
|
|
|
|
|
picture: 'https://api.dicebear.com/7.x/micah/svg?seed=3',
|
|
|
|
|
releaseTime: '2025-01-01',
|
|
|
|
|
views: 1000,
|
|
|
|
|
likes: 100,
|
|
|
|
|
},
|
|
|
|
|
];
|
2025-05-09 10:18:49 +08:00
|
|
|
|
// 模拟创作者数据,实际项目中会从API获取
|
2025-05-23 05:00:59 +08:00
|
|
|
|
export const mockCreators = [
|
2025-05-09 10:18:49 +08:00
|
|
|
|
{
|
|
|
|
|
id: 1,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=1',
|
|
|
|
|
category: 'Phones & Electronics',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
e_commerce_level: 'L2',
|
|
|
|
|
exposure_level: 'KOC-1',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
avg_video_views: '1.9k',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
hasEcommerce: true,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
verified: true,
|
2025-05-21 22:49:54 +08:00
|
|
|
|
videos: mockVideos,
|
|
|
|
|
videosWithProduct: mockVideos,
|
2025-05-09 10:18:49 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 2,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=2',
|
|
|
|
|
category: 'Womenswear & Underwear',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
e_commerce_level: 'L3',
|
|
|
|
|
exposure_level: 'KOL-3',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
avg_video_views: '1.9k',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
hasEcommerce: false,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
verified: false,
|
2025-05-21 22:49:54 +08:00
|
|
|
|
videos: mockVideos,
|
|
|
|
|
videosWithProduct: mockVideos,
|
2025-05-09 10:18:49 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 3,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=3',
|
|
|
|
|
category: 'Sports & Outdoor',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
e_commerce_level: 'L4',
|
|
|
|
|
exposure_level: 'KOC-2',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
avg_video_views: '1.9k',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
hasEcommerce: true,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
verified: false,
|
2025-05-21 22:49:54 +08:00
|
|
|
|
videos: mockVideos,
|
|
|
|
|
videosWithProduct: mockVideos,
|
2025-05-09 10:18:49 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 4,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=4',
|
|
|
|
|
category: 'Food & Beverage',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
e_commerce_level: 'L1',
|
|
|
|
|
exposure_level: 'KOC-2',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
avg_video_views: '1.9k',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
hasEcommerce: true,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
hasInstagram: true,
|
|
|
|
|
hasYoutube: true,
|
|
|
|
|
verified: true,
|
2025-05-21 22:49:54 +08:00
|
|
|
|
videos: mockVideos,
|
|
|
|
|
videosWithProduct: mockVideos,
|
2025-05-09 10:18:49 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 5,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=5',
|
|
|
|
|
category: 'Health',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
e_commerce_level: 'L5',
|
|
|
|
|
exposure_level: 'KOL-2',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
avg_video_views: '1.9k',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
hasEcommerce: false,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
hasInstagram: true,
|
|
|
|
|
hasYoutube: true,
|
|
|
|
|
verified: true,
|
2025-05-21 22:49:54 +08:00
|
|
|
|
videos: mockVideos,
|
|
|
|
|
videosWithProduct: mockVideos,
|
2025-05-09 10:18:49 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 6,
|
|
|
|
|
name: 'name',
|
|
|
|
|
avatar: 'https://api.dicebear.com/7.x/micah/svg?seed=6',
|
|
|
|
|
category: 'Kitchenware',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
e_commerce_level: 'New tag',
|
|
|
|
|
exposure_level: 'New tag',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
followers: '162.2k',
|
|
|
|
|
gmv: '$534.1k',
|
|
|
|
|
soldPercentage: '18.1%',
|
2025-05-23 08:53:43 +08:00
|
|
|
|
avg_video_views: '1.9k',
|
2025-05-09 10:18:49 +08:00
|
|
|
|
hasEcommerce: true,
|
|
|
|
|
hasTiktok: true,
|
|
|
|
|
hasInstagram: true,
|
|
|
|
|
hasYoutube: true,
|
|
|
|
|
verified: false,
|
2025-05-21 22:49:54 +08:00
|
|
|
|
videos: mockVideos,
|
|
|
|
|
videosWithProduct: mockVideos,
|
2025-05-09 10:18:49 +08:00
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 模拟API获取数据的异步Thunk
|
2025-05-23 08:53:43 +08:00
|
|
|
|
// export const fetchCreators = createAsyncThunk('creators/fetchCreators', async ({ path }, { getState }) => {
|
|
|
|
|
// // 模拟API调用延迟
|
|
|
|
|
// await new Promise((resolve) => setTimeout(resolve, 500));
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
2025-05-23 08:53:43 +08:00
|
|
|
|
// // 获取当前的筛选条件
|
|
|
|
|
// const state = getState();
|
|
|
|
|
// const filters = state.filters;
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
2025-05-23 08:53:43 +08:00
|
|
|
|
// // 应用筛选逻辑(实际项目中可能在服务器端进行)
|
|
|
|
|
// let filteredCreators = [...mockCreators];
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
2025-05-23 08:53:43 +08:00
|
|
|
|
// // 如果有选定的类别,进行筛选
|
|
|
|
|
// if (filters.category.length > 0) {
|
|
|
|
|
// filteredCreators = filteredCreators.filter((creator) => filters.category.includes(creator.category));
|
|
|
|
|
// }
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
2025-05-23 08:53:43 +08:00
|
|
|
|
// // 如果有选定的电商评级,进行筛选
|
|
|
|
|
// if (filters.ecommerceRatings.length > 0) {
|
|
|
|
|
// filteredCreators = filteredCreators.filter((creator) =>
|
|
|
|
|
// filters.ecommerceRatings.includes(creator.e_commerce_level)
|
|
|
|
|
// );
|
|
|
|
|
// }
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
2025-05-23 08:53:43 +08:00
|
|
|
|
// // 如果有选定的曝光评级,进行筛选
|
|
|
|
|
// if (filters.exposureRatings.length > 0) {
|
|
|
|
|
// filteredCreators = filteredCreators.filter((creator) =>
|
|
|
|
|
// filters.exposureRatings.includes(creator.exposure_level)
|
|
|
|
|
// );
|
|
|
|
|
// }
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
2025-05-23 08:53:43 +08:00
|
|
|
|
// // 筛选观看量范围
|
|
|
|
|
// if (filters.viewsRange.length === 2) {
|
|
|
|
|
// const minViews = filters.viewsRange[0];
|
|
|
|
|
// const maxViews = filters.viewsRange[1];
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
2025-05-23 08:53:43 +08:00
|
|
|
|
// filteredCreators = filteredCreators.filter((creator) => {
|
|
|
|
|
// // 将带k的字符串转换为数字
|
|
|
|
|
// const viewsStr = creator.avg_video_views;
|
|
|
|
|
// let views = parseFloat(viewsStr);
|
|
|
|
|
// if (viewsStr.includes('k')) {
|
|
|
|
|
// views *= 1000;
|
|
|
|
|
// } else if (viewsStr.includes('M')) {
|
|
|
|
|
// views *= 1000000;
|
|
|
|
|
// }
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
2025-05-23 08:53:43 +08:00
|
|
|
|
// return views >= minViews && views <= maxViews;
|
|
|
|
|
// });
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// return filteredCreators;
|
|
|
|
|
// });
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
|
|
|
|
const initialState = {
|
2025-05-23 08:53:43 +08:00
|
|
|
|
publicCreators: [],
|
|
|
|
|
privateCreators: [],
|
|
|
|
|
publicTiktokCreators: [],
|
|
|
|
|
publicInstagramCreators: [],
|
|
|
|
|
publicYoutubeCreators: [],
|
|
|
|
|
privateTiktokCreators: [],
|
|
|
|
|
privateInstagramCreators: [],
|
|
|
|
|
privateYoutubeCreators: [],
|
2025-05-09 10:18:49 +08:00
|
|
|
|
status: 'idle', // 'idle' | 'loading' | 'succeeded' | 'failed'
|
|
|
|
|
error: null,
|
|
|
|
|
selectedCreators: [],
|
2025-05-16 09:22:08 +08:00
|
|
|
|
selectedCreator: null,
|
2025-05-23 08:53:43 +08:00
|
|
|
|
pagination: {
|
|
|
|
|
current_page: 1,
|
|
|
|
|
total_pages: 0,
|
|
|
|
|
total_count: 0,
|
|
|
|
|
has_next: false,
|
|
|
|
|
has_prev: false,
|
|
|
|
|
},
|
|
|
|
|
hasMore: true,
|
|
|
|
|
isLoadingMore: false,
|
2025-05-09 10:18:49 +08:00
|
|
|
|
};
|
|
|
|
|
|
2025-05-23 22:27:14 +08:00
|
|
|
|
export const fetchCreators = createAsyncThunk(
|
|
|
|
|
'creators/fetchCreators',
|
|
|
|
|
async ({ page = 1 }, { getState, rejectWithValue }) => {
|
|
|
|
|
try {
|
|
|
|
|
const state = getState();
|
2025-05-30 05:13:50 +08:00
|
|
|
|
const {pricing, ...filter} = state.filters;
|
2025-05-23 08:53:43 +08:00
|
|
|
|
|
2025-05-23 22:27:14 +08:00
|
|
|
|
const { code, data, message, pagination } = await api.post(
|
|
|
|
|
`/daren_detail/public/creators/filter/?page=${page}`,
|
2025-05-24 08:16:21 +08:00
|
|
|
|
{ filter }
|
2025-05-23 22:27:14 +08:00
|
|
|
|
);
|
|
|
|
|
if (code === 200) {
|
|
|
|
|
return { data, pagination };
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return rejectWithValue(error.message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
2025-05-23 08:53:43 +08:00
|
|
|
|
|
|
|
|
|
export const fetchPrivateCreators = createAsyncThunk(
|
|
|
|
|
'creators/fetchPrivateCreators',
|
2025-05-23 22:27:14 +08:00
|
|
|
|
async ({ page = 1 }, { getState, rejectWithValue, dispatch }) => {
|
|
|
|
|
try {
|
|
|
|
|
const state = getState();
|
2025-05-29 04:26:34 +08:00
|
|
|
|
const filter = state.filters;
|
2025-05-23 08:53:43 +08:00
|
|
|
|
|
2025-05-23 22:27:14 +08:00
|
|
|
|
const { code, data, message, pagination } = await api.post(
|
|
|
|
|
`/daren_detail/private/pools/creators/filter/?page=${page}`,
|
2025-05-24 08:16:21 +08:00
|
|
|
|
{ pool_id: 1, filter }
|
2025-05-23 22:27:14 +08:00
|
|
|
|
);
|
|
|
|
|
if (code === 200) {
|
|
|
|
|
return { data, pagination };
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return rejectWithValue(error.message);
|
|
|
|
|
}
|
2025-05-23 08:53:43 +08:00
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2025-05-29 04:26:34 +08:00
|
|
|
|
/**
|
|
|
|
|
* 搜索公有达人库达人
|
|
|
|
|
* @param {Object} params
|
|
|
|
|
* @param {string} params.q 搜索关键词
|
|
|
|
|
* @param {string} params.mode 搜索类型 OR 和 AND 固定or
|
|
|
|
|
* @param {number} params.page 页码
|
|
|
|
|
* @param {number} params.page_size 每页条数
|
|
|
|
|
*/
|
|
|
|
|
export const searchCreators = createAsyncThunk(
|
|
|
|
|
'creators/searchPublicCreators',
|
|
|
|
|
async (params, { rejectWithValue, dispatch }) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await api.get(`/daren_detail/creators/search/`, { params });
|
|
|
|
|
if (response.code === 200) {
|
|
|
|
|
return response;
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(response.message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return rejectWithValue(error.message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 搜索私有达人库达人
|
|
|
|
|
* @param {Object} params
|
|
|
|
|
* @param {string} params.q 搜索关键词
|
|
|
|
|
* @param {string} params.mode 搜索类型 OR 和 AND 固定or
|
|
|
|
|
* @param {number} params.page 页码
|
|
|
|
|
* @param {number} params.page_size 每页条数
|
|
|
|
|
*/
|
|
|
|
|
export const searchPrivateCreators = createAsyncThunk(
|
|
|
|
|
'creators/searchPrivateCreators',
|
|
|
|
|
async (params, { rejectWithValue, dispatch }) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await api.get(`/daren_detail/private/creators/search/`, { params });
|
|
|
|
|
if (response.code === 200) {
|
|
|
|
|
return response;
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(response.message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return rejectWithValue(error.message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2025-05-23 08:53:43 +08:00
|
|
|
|
export const fetchCreatorDetail = createAsyncThunk(
|
|
|
|
|
'creators/fetchCreatorDetail',
|
2025-05-24 22:40:29 +08:00
|
|
|
|
async ({ creatorId }, { dispatch, rejectWithValue }) => {
|
2025-05-24 08:16:21 +08:00
|
|
|
|
try {
|
|
|
|
|
const response = await api.get(`/daren_detail/creators/${creatorId}`);
|
|
|
|
|
if (response.code === 200) {
|
|
|
|
|
return response;
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(response.message);
|
|
|
|
|
}
|
2025-05-24 22:40:29 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log('fetchCreatorDetail.rejected', error);
|
|
|
|
|
return rejectWithValue(error.message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const fetchCreatorMetrics = createAsyncThunk(
|
|
|
|
|
'creators/fetchCreatorMetrics',
|
|
|
|
|
async ({ creatorId }, { rejectWithValue }) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await api.get(`/daren_detail/creators/${creatorId}/metrics`);
|
|
|
|
|
if (response.code === 200 || response.code === 201) {
|
|
|
|
|
return response;
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(response.message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return rejectWithValue(error.message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const fetchCreatorFollowers = createAsyncThunk(
|
|
|
|
|
'creators/fetchCreatorFollowers',
|
|
|
|
|
async ({ creatorId }, { rejectWithValue }) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await api.get(`/daren_detail/creator/${creatorId}/followers`);
|
|
|
|
|
if (response.code === 200) {
|
|
|
|
|
return response;
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(response.message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return rejectWithValue(error.message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const fetchCreatorTrends = createAsyncThunk(
|
|
|
|
|
'creators/fetchCreatorTrends',
|
|
|
|
|
async ({ creatorId }, { rejectWithValue }) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await api.get(`/daren_detail/creator/${creatorId}/trends`);
|
|
|
|
|
if (response.code === 200) {
|
|
|
|
|
return response;
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(response.message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return rejectWithValue(error.message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
export const fetchCreatorVideos = createAsyncThunk(
|
|
|
|
|
'creators/fetchCreatorVideos',
|
|
|
|
|
async ({ creatorId }, { rejectWithValue }) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await api.get(`/daren_detail/creator/${creatorId}/videos`);
|
|
|
|
|
if (response.code === 200) {
|
|
|
|
|
return response;
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(response.message);
|
|
|
|
|
}
|
2025-05-24 08:16:21 +08:00
|
|
|
|
} 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) {
|
2025-05-24 22:40:29 +08:00
|
|
|
|
dispatch(
|
|
|
|
|
setNotificationBarMessage({ message: 'Creators added to campaign successfully', type: 'success' })
|
|
|
|
|
);
|
2025-05-24 08:16:21 +08:00
|
|
|
|
return response;
|
|
|
|
|
} else {
|
|
|
|
|
dispatch(setNotificationBarMessage({ message: response.message, type: 'error' }));
|
|
|
|
|
throw new Error(response.message);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return rejectWithValue(error.message);
|
|
|
|
|
}
|
2025-05-23 08:53:43 +08:00
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2025-05-09 10:18:49 +08:00
|
|
|
|
const creatorsSlice = createSlice({
|
|
|
|
|
name: 'creators',
|
|
|
|
|
initialState,
|
|
|
|
|
reducers: {
|
|
|
|
|
toggleCreatorSelection: (state, action) => {
|
|
|
|
|
const creatorId = action.payload;
|
|
|
|
|
const isSelected = state.selectedCreators.includes(creatorId);
|
|
|
|
|
|
|
|
|
|
if (isSelected) {
|
2025-05-16 09:22:08 +08:00
|
|
|
|
state.selectedCreators = state.selectedCreators.filter((id) => id.toString() !== creatorId.toString());
|
2025-05-09 10:18:49 +08:00
|
|
|
|
} else {
|
|
|
|
|
state.selectedCreators.push(creatorId);
|
|
|
|
|
}
|
|
|
|
|
},
|
2025-05-23 09:58:48 +08:00
|
|
|
|
selectAllCreators: (state, action) => {
|
|
|
|
|
if (action.payload === 'database') {
|
2025-05-24 08:16:21 +08:00
|
|
|
|
state.selectedCreators = state.publicCreators.map((creator) => creator.creator_id);
|
2025-05-23 09:58:48 +08:00
|
|
|
|
} else {
|
2025-05-24 08:16:21 +08:00
|
|
|
|
state.selectedCreators = state.privateCreators.map((creator) => creator.creator_id);
|
2025-05-23 09:58:48 +08:00
|
|
|
|
}
|
2025-05-09 10:18:49 +08:00
|
|
|
|
},
|
|
|
|
|
clearCreatorSelection: (state) => {
|
|
|
|
|
state.selectedCreators = [];
|
|
|
|
|
},
|
2025-05-16 09:22:08 +08:00
|
|
|
|
clearCreator: (state) => {
|
|
|
|
|
state.selectedCreator = null;
|
|
|
|
|
},
|
2025-05-23 08:53:43 +08:00
|
|
|
|
resetCreators: (state) => {
|
|
|
|
|
state.publicCreators = [];
|
|
|
|
|
state.privateCreators = [];
|
|
|
|
|
state.pagination = initialState.pagination;
|
|
|
|
|
state.hasMore = true;
|
|
|
|
|
state.isLoadingMore = false;
|
|
|
|
|
},
|
2025-05-09 10:18:49 +08:00
|
|
|
|
},
|
|
|
|
|
extraReducers: (builder) => {
|
|
|
|
|
builder
|
|
|
|
|
.addCase(fetchCreators.pending, (state) => {
|
2025-05-23 22:27:14 +08:00
|
|
|
|
state.status = 'loading';
|
2025-05-23 09:58:48 +08:00
|
|
|
|
if (state.publicCreators.length === 0) {
|
2025-05-23 08:53:43 +08:00
|
|
|
|
} else {
|
|
|
|
|
state.isLoadingMore = true;
|
|
|
|
|
}
|
2025-05-09 10:18:49 +08:00
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreators.fulfilled, (state, action) => {
|
|
|
|
|
state.status = 'succeeded';
|
2025-05-23 08:53:43 +08:00
|
|
|
|
state.isLoadingMore = false;
|
|
|
|
|
const { data, pagination } = action.payload;
|
|
|
|
|
if (pagination.current_page === 1) {
|
|
|
|
|
state.publicCreators = data;
|
|
|
|
|
} else {
|
|
|
|
|
state.publicCreators = [...state.publicCreators, ...data];
|
|
|
|
|
}
|
|
|
|
|
state.pagination = pagination;
|
|
|
|
|
state.hasMore = pagination.has_next;
|
2025-05-09 10:18:49 +08:00
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreators.rejected, (state, action) => {
|
2025-05-23 08:53:43 +08:00
|
|
|
|
state.status = 'failed';
|
|
|
|
|
state.isLoadingMore = false;
|
2025-05-23 22:27:14 +08:00
|
|
|
|
state.error = action.payload;
|
2025-05-23 08:53:43 +08:00
|
|
|
|
})
|
|
|
|
|
.addCase(fetchPrivateCreators.pending, (state) => {
|
2025-05-23 09:58:48 +08:00
|
|
|
|
if (state.privateCreators?.length === 0) {
|
2025-05-23 08:53:43 +08:00
|
|
|
|
state.status = 'loading';
|
|
|
|
|
} else {
|
|
|
|
|
state.isLoadingMore = true;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.addCase(fetchPrivateCreators.fulfilled, (state, action) => {
|
|
|
|
|
state.status = 'succeeded';
|
|
|
|
|
state.isLoadingMore = false;
|
|
|
|
|
const { data, pagination } = action.payload;
|
|
|
|
|
|
|
|
|
|
if (pagination.current_page === 1) {
|
|
|
|
|
state.privateCreators = data;
|
|
|
|
|
} else {
|
|
|
|
|
state.privateCreators = [...state.privateCreators, ...data];
|
|
|
|
|
}
|
|
|
|
|
state.pagination = pagination;
|
|
|
|
|
state.hasMore = pagination.has_next;
|
|
|
|
|
})
|
|
|
|
|
.addCase(fetchPrivateCreators.rejected, (state, action) => {
|
|
|
|
|
console.log('fetchPrivateCreators.rejected', action);
|
|
|
|
|
state.status = 'failed';
|
|
|
|
|
state.isLoadingMore = false;
|
2025-05-23 22:27:14 +08:00
|
|
|
|
state.error = action.payload;
|
2025-05-23 08:53:43 +08:00
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreatorDetail.pending, (state) => {
|
|
|
|
|
state.status = 'loading';
|
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreatorDetail.fulfilled, (state, action) => {
|
|
|
|
|
state.status = 'succeeded';
|
2025-05-23 09:22:51 +08:00
|
|
|
|
const { data } = action.payload;
|
2025-05-23 08:53:43 +08:00
|
|
|
|
state.selectedCreator = data;
|
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreatorDetail.rejected, (state, action) => {
|
2025-05-09 10:18:49 +08:00
|
|
|
|
state.status = 'failed';
|
|
|
|
|
state.error = action.error.message;
|
2025-05-24 22:40:29 +08:00
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreatorMetrics.fulfilled, (state, action) => {
|
|
|
|
|
state.selectedCreator.metricsData = action.payload.data;
|
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreatorFollowers.fulfilled, (state, action) => {
|
|
|
|
|
state.selectedCreator.followerData = action.payload.data;
|
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreatorTrends.fulfilled, (state, action) => {
|
|
|
|
|
state.selectedCreator.trendsData = action.payload.data;
|
|
|
|
|
})
|
|
|
|
|
.addCase(fetchCreatorVideos.fulfilled, (state, action) => {
|
|
|
|
|
state.selectedCreator.videosData = action.payload.data;
|
2025-05-29 04:26:34 +08:00
|
|
|
|
})
|
|
|
|
|
.addCase(searchCreators.pending, (state) => {
|
|
|
|
|
state.status = 'loading';
|
|
|
|
|
})
|
|
|
|
|
.addCase(searchPrivateCreators.pending, (state) => {
|
|
|
|
|
state.status = 'loading';
|
|
|
|
|
})
|
|
|
|
|
.addCase(searchCreators.fulfilled, (state, action) => {
|
|
|
|
|
state.publicCreators = action.payload.data;
|
|
|
|
|
state.status = 'succeeded';
|
|
|
|
|
state.error = null;
|
|
|
|
|
})
|
|
|
|
|
.addCase(searchPrivateCreators.fulfilled, (state, action) => {
|
|
|
|
|
state.privateCreators = action.payload.data;
|
|
|
|
|
state.status = 'succeeded';
|
|
|
|
|
state.error = null;
|
|
|
|
|
})
|
|
|
|
|
.addCase(searchCreators.rejected, (state, action) => {
|
|
|
|
|
state.status = 'failed';
|
|
|
|
|
state.error = action.payload;
|
|
|
|
|
console.log('searchCreators.rejected', action);
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
.addCase(searchPrivateCreators.rejected, (state, action) => {
|
|
|
|
|
state.status = 'failed';
|
|
|
|
|
state.error = action.payload;
|
2025-05-09 10:18:49 +08:00
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-05-23 08:53:43 +08:00
|
|
|
|
export const {
|
|
|
|
|
toggleCreatorSelection,
|
|
|
|
|
selectAllCreators,
|
|
|
|
|
clearCreatorSelection,
|
|
|
|
|
clearCreator,
|
|
|
|
|
setCreators,
|
|
|
|
|
resetCreators,
|
|
|
|
|
} = creatorsSlice.actions;
|
2025-05-09 10:18:49 +08:00
|
|
|
|
|
|
|
|
|
export default creatorsSlice.reducer;
|