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,
platform: template.platform,
service: template.service,
content: template.content,
content: template.preview,
title: template.title,
category: template.category,
collaboration_type: template.collaboration_type,
@ -49,7 +49,7 @@ export default function TemplateList({ activeTab, openModal, setFormData }) {
{template.mission === 'follow_up' && (
<span className='template-item-name-text-cooperation'>合作追踪</span>
)}{' '}
- {template.name}
- {template.title}
</span>
<div className='template-item-name-actions'>
<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} />
Message
</div>
<div className='value'>{template.content}</div>
<div className='value'>{template.preview}</div>
</div>
</div>
))}

View File

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

View File

@ -9,6 +9,7 @@ import authReducer from './slices/authSlice';
import discoveryReducer from './slices/discoverySlice';
import notificationBarReducer from './slices/notificationBarSlice';
import productReducer from './slices/productSlice';
import chatReducer from './slices/chatSlice';
const authPersistConfig = {
key: 'auth',
@ -24,6 +25,7 @@ const rootReducer = combineReducers({
auth: persistReducer(authPersistConfig, authReducer),
notificationBar: notificationBarReducer,
products: productReducer,
chat: chatReducer,
});
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 }) => {
try {
const response = await api.get(`/chat-history/search/`);
if (response.code === 200) {
return response.data;
export const fetchChatDetails = createAsyncThunk(
'chat/fetchChatDetails',
async (query, { rejectWithValue, dispatch }) => {
try {
const response = await api.get('/chat-history/conversation_detail/', { params: 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);
}
throw new Error(response.message);
} catch (error) {
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 = {
chats: [],
currentChat: null,
status: 'idle',
error: null,
@ -24,14 +61,14 @@ const chatSlice = createSlice({
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchChats.pending, (state) => {
builder.addCase(fetchChatDetails.pending, (state) => {
state.status = 'loading';
})
builder.addCase(fetchChats.fulfilled, (state, action) => {
});
builder.addCase(fetchChatDetails.fulfilled, (state, action) => {
state.status = 'succeeded';
state.chats = action.payload;
})
builder.addCase(fetchChats.rejected, (state, action) => {
state.currentChat = action.payload;
});
builder.addCase(fetchChatDetails.rejected, (state, action) => {
state.status = 'failed';
state.error = action.payload;
});

View File

@ -513,16 +513,20 @@ const creatorsSlice = createSlice({
state.error = action.error.message;
})
.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) => {
state.selectedCreator.followerData = action.payload;
console.log(action.payload);
state.selectedCreator.followerData = action.payload || null;
})
.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) => {
state.selectedCreator.videosData = action.payload;
console.log(action.payload);
state.selectedCreator.videosData = action.payload || null;
})
.addCase(searchCreators.pending, (state) => {
state.status = 'loading';

View File

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

View File

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

View File

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

View File

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