Compare commits

...

2 Commits

Author SHA1 Message Date
099d11ed9f Update chatSlice.js 2025-06-03 09:56:22 -04:00
8ee06cceaa [dev]template 2025-06-03 09:56:18 -04:00
10 changed files with 100 additions and 44 deletions

View File

@ -22,7 +22,7 @@ export default function TemplateList({ activeTab, openModal, setFormData }) {
mission: template.type, mission: template.type,
platform: template.platform, platform: template.platform,
service: template.service, service: template.service,
content: template.content, content: template.preview,
title: template.title, title: template.title,
category: template.category, category: template.category,
collaboration_type: template.collaboration_type, collaboration_type: template.collaboration_type,
@ -49,7 +49,7 @@ export default function TemplateList({ activeTab, openModal, setFormData }) {
{template.mission === 'follow_up' && ( {template.mission === 'follow_up' && (
<span className='template-item-name-text-cooperation'>合作追踪</span> <span className='template-item-name-text-cooperation'>合作追踪</span>
)}{' '} )}{' '}
- {template.name} - {template.title}
</span> </span>
<div className='template-item-name-actions'> <div className='template-item-name-actions'>
<Button variant='outline-primary' className='border-0' size='sm' onClick={() => handleEdit(template)}> <Button variant='outline-primary' className='border-0' size='sm' onClick={() => handleEdit(template)}>
@ -77,7 +77,7 @@ export default function TemplateList({ activeTab, openModal, setFormData }) {
<FileText size={20} /> <FileText size={20} />
Message Message
</div> </div>
<div className='value'>{template.content}</div> <div className='value'>{template.preview}</div>
</div> </div>
</div> </div>
))} ))}

View File

@ -151,3 +151,13 @@ export const TEMPLATE_MISSIONS = [
{ value: 'script', name: '脚本邮件' }, { value: 'script', name: '脚本邮件' },
{ value: 'follow_up', name: '合作追踪' }, { value: 'follow_up', name: '合作追踪' },
]; ];
export const STATUS_OPTIONS = [
{ value: 'brand_review', name: '品牌回顾' },
{ value: 'price_negotiation', name: '价格谈判' },
{ value: 'contract_review', name: '合同确认' },
{ value: 'draft_ready', name: '准备合同' },
{ value: 'draft_approved', name: '合同提交' },
{ value: 'accepted', name: '已接受' },
{ value: 'abandoned', name: '已放弃' },
];

View File

@ -23,7 +23,11 @@ export default function InboxTemplate() {
const dispatch = useDispatch(); const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
dispatch(fetchTemplates(activeTab)); if (activeTab === 'all') {
dispatch(fetchTemplates());
} else {
dispatch(fetchTemplates({ mission: activeTab }));
}
}, [dispatch, activeTab]); }, [dispatch, activeTab]);
return ( return (
@ -46,14 +50,14 @@ export default function InboxTemplate() {
全部 全部
</div> </div>
<div <div
className={`tab-switch-item ${activeTab === 'initial' ? 'active' : ''}`} className={`tab-switch-item ${activeTab === 'initial_contact' ? 'active' : ''}`}
onClick={() => setActiveTab('initial')} onClick={() => setActiveTab('initial_contact')}
> >
初步建联 初步建联
</div> </div>
<div <div
className={`tab-switch-item ${activeTab === 'bargain' ? 'active' : ''}`} className={`tab-switch-item ${activeTab === 'negotiation' ? 'active' : ''}`}
onClick={() => setActiveTab('bargain')} onClick={() => setActiveTab('negotiation')}
> >
砍价邮件 砍价邮件
</div> </div>
@ -64,8 +68,8 @@ export default function InboxTemplate() {
脚本邮件 脚本邮件
</div> </div>
<div <div
className={`tab-switch-item ${activeTab === 'cooperation' ? 'active' : ''}`} className={`tab-switch-item ${activeTab === 'follow_up' ? 'active' : ''}`}
onClick={() => setActiveTab('cooperation')} onClick={() => setActiveTab('follow_up')}
> >
合作追踪 合作追踪
</div> </div>
@ -104,7 +108,6 @@ function AddTemplateModal({ show, formData, setFormData, handleClose, type = 'ad
}; };
const handleSubmit = () => { const handleSubmit = () => {
console.log(formData);
if (type === 'add') { if (type === 'add') {
dispatch(addTemplateThunk(formData)); dispatch(addTemplateThunk(formData));
} else { } else {
@ -146,11 +149,11 @@ function AddTemplateModal({ show, formData, setFormData, handleClose, type = 'ad
<option value='' disabled> <option value='' disabled>
Select mission Select mission
</option> </option>
{ {TEMPLATE_MISSIONS.map((mission) => (
TEMPLATE_MISSIONS.map((mission) => ( <option key={mission.value} value={mission.value}>
<option value={mission.value}>{mission.name}</option> {mission.name}
)) </option>
} ))}
</Form.Select> </Form.Select>
</Form.Group> </Form.Group>
<Form.Group className='col' controlId='formBasicEmail'> <Form.Group className='col' controlId='formBasicEmail'>
@ -185,7 +188,9 @@ function AddTemplateModal({ show, formData, setFormData, handleClose, type = 'ad
Select service Select service
</option> </option>
{CAMPAIGN_SERVICES.map((service) => ( {CAMPAIGN_SERVICES.map((service) => (
<option value={service.value}>{service.name}</option> <option key={service.value} value={service.value}>
{service.name}
</option>
))} ))}
</Form.Select> </Form.Select>
</Form.Group> </Form.Group>

View File

@ -9,6 +9,7 @@ import authReducer from './slices/authSlice';
import discoveryReducer from './slices/discoverySlice'; import discoveryReducer from './slices/discoverySlice';
import notificationBarReducer from './slices/notificationBarSlice'; import notificationBarReducer from './slices/notificationBarSlice';
import productReducer from './slices/productSlice'; import productReducer from './slices/productSlice';
import chatReducer from './slices/chatSlice';
const authPersistConfig = { const authPersistConfig = {
key: 'auth', key: 'auth',
@ -24,6 +25,7 @@ const rootReducer = combineReducers({
auth: persistReducer(authPersistConfig, authReducer), auth: persistReducer(authPersistConfig, authReducer),
notificationBar: notificationBarReducer, notificationBar: notificationBarReducer,
products: productReducer, products: productReducer,
chat: chatReducer,
}); });
const store = configureStore({ const store = configureStore({

View File

@ -1,19 +1,56 @@
import { createSlice } from '@reduxjs/toolkit'; import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import api from '@/services/api';
import { setNotificationBarMessage } from './notificationBarSlice';
export const fetchChats = createAsyncThunk('chat/fetchChats', async (_, { rejectWithValue }) => { export const fetchChatDetails = createAsyncThunk(
'chat/fetchChatDetails',
async (query, { rejectWithValue, dispatch }) => {
try { try {
const response = await api.get(`/chat-history/search/`); const response = await api.get('/chat-history/conversation_detail/', { params: query });
if (response.code === 200) { if (response.code === 200) {
return response.data; return response.data;
} }
throw new Error(response.message); throw new Error(response.message);
} catch (error) { } catch (error) {
dispatch(setNotificationBarMessage({ message: error.message, type: 'error' }));
return rejectWithValue(error.message); return rejectWithValue(error.message);
} }
}); }
);
export const sendEMailToCreator = createAsyncThunk(
'chat/sendEMailToCreator',
async (query, { rejectWithValue, dispatch }) => {
try {
const response = await api.post('/chat-history/send_email_to_creator/', query);
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 createConversation = createAsyncThunk(
'chat/createConversation',
async (id, { rejectWithValue, dispatch }) => {
try {
const response = await api.post('/chat-history/create_conversation/', { negotiation_id: id });
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 = { const initialState = {
chats: [],
currentChat: null, currentChat: null,
status: 'idle', status: 'idle',
error: null, error: null,
@ -24,14 +61,14 @@ const chatSlice = createSlice({
initialState, initialState,
reducers: {}, reducers: {},
extraReducers: (builder) => { extraReducers: (builder) => {
builder.addCase(fetchChats.pending, (state) => { builder.addCase(fetchChatDetails.pending, (state) => {
state.status = 'loading'; state.status = 'loading';
}) });
builder.addCase(fetchChats.fulfilled, (state, action) => { builder.addCase(fetchChatDetails.fulfilled, (state, action) => {
state.status = 'succeeded'; state.status = 'succeeded';
state.chats = action.payload; state.currentChat = action.payload;
}) });
builder.addCase(fetchChats.rejected, (state, action) => { builder.addCase(fetchChatDetails.rejected, (state, action) => {
state.status = 'failed'; state.status = 'failed';
state.error = action.payload; state.error = action.payload;
}); });

View File

@ -513,16 +513,20 @@ const creatorsSlice = createSlice({
state.error = action.error.message; state.error = action.error.message;
}) })
.addCase(fetchCreatorMetrics.fulfilled, (state, action) => { .addCase(fetchCreatorMetrics.fulfilled, (state, action) => {
state.selectedCreator.metricsData = action.payload; console.log(action.payload);
state.selectedCreator.metricsData = action.payload || null;
}) })
.addCase(fetchCreatorFollowers.fulfilled, (state, action) => { .addCase(fetchCreatorFollowers.fulfilled, (state, action) => {
state.selectedCreator.followerData = action.payload; console.log(action.payload);
state.selectedCreator.followerData = action.payload || null;
}) })
.addCase(fetchCreatorTrends.fulfilled, (state, action) => { .addCase(fetchCreatorTrends.fulfilled, (state, action) => {
state.selectedCreator.trendsData = action.payload; console.log(action.payload);
state.selectedCreator.trendsData = action.payload || null;
}) })
.addCase(fetchCreatorVideos.fulfilled, (state, action) => { .addCase(fetchCreatorVideos.fulfilled, (state, action) => {
state.selectedCreator.videosData = action.payload; console.log(action.payload);
state.selectedCreator.videosData = action.payload || null;
}) })
.addCase(searchCreators.pending, (state) => { .addCase(searchCreators.pending, (state) => {
state.status = 'loading'; state.status = 'loading';

View File

@ -191,9 +191,9 @@ export const fetchChatHistory = createAsyncThunk('inbox/fetchChatHistory', async
return { chatHistory: mockChatHistory, chatId: id }; return { chatHistory: mockChatHistory, chatId: id };
}); });
export const fetchTemplates = createAsyncThunk('inbox/fetchTemplates', async (_, { rejectWithValue }) => { export const fetchTemplates = createAsyncThunk('inbox/fetchTemplates', async (query, { rejectWithValue }) => {
try { try {
const response = await api.get('/template/'); const response = await api.get('/template/', { params: query });
if (response.code === 200) { if (response.code === 200) {
return response.data.results; return response.data.results;
} else { } else {
@ -226,7 +226,7 @@ export const addTemplateThunk = createAsyncThunk(
async (formData, { rejectWithValue, dispatch }) => { async (formData, { rejectWithValue, dispatch }) => {
try { try {
const response = await api.post('/template/', formData); const response = await api.post('/template/', formData);
if (response.code === 200) { if (response.code === 200 || response.code === 201) {
return response.data; return response.data;
} else { } else {
throw new Error(response.message); throw new Error(response.message);

View File

@ -414,7 +414,6 @@
#addProductForm { #addProductForm {
.modal-body { .modal-body {
width: 400px;
display: flex; display: flex;
flex-flow: column nowrap; flex-flow: column nowrap;
gap: 1rem; gap: 1rem;

View File

@ -12,7 +12,6 @@
position: relative; position: relative;
} }
.creator-database-table { .creator-database-table {
height: 100%;
position: relative; position: relative;
.creator-cell { .creator-cell {

View File

@ -366,7 +366,7 @@
gap: 1rem; gap: 1rem;
margin-top: 1rem; margin-top: 1rem;
overflow-y: auto; overflow-y: auto;
height: calc(100% - 85px); max-height: calc(100% - 85px);
padding-bottom: 1rem; padding-bottom: 1rem;
.template-item { .template-item {