/*
Copyright (c) 2025 Tethys Plex
This file is part of Veloera.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
import React, { useEffect, useState } from 'react';
import { API, showError, showSuccess } from '../../helpers';
import {
Button,
Form,
Popconfirm,
Space,
Table,
Tag,
Tooltip,
} from '@douyinfe/semi-ui';
import { ITEMS_PER_PAGE } from '../../constants';
import CreateMessage from './CreateMessage';
import EditMessage from './EditMessage';
import { useTranslation } from 'react-i18next';
const MessageList = () => {
const { t } = useTranslation();
const columns = [
{
title: 'ID',
dataIndex: 'id',
width: 80,
},
{
title: t('标题'),
dataIndex: 'title',
render: (text) => (
{text}
),
},
{
title: t('格式'),
dataIndex: 'format',
width: 100,
render: (text) => (
{text?.toUpperCase()}
),
},
{
title: t('收件人数'),
dataIndex: 'stats',
width: 100,
render: (stats) => (
{stats?.total_recipients || 0}
),
},
{
title: t('创建时间'),
dataIndex: 'created_at',
width: 180,
render: (text) => {
return new Date(text).toLocaleString();
},
},
{
title: t('操作'),
dataIndex: 'operate',
width: 200,
render: (_, record) => (
{
setEditingMessage(record);
setShowEditMessage(true);
}}
>
{t('编辑')}
deleteMessage(record.id)}
>
{t('删除')}
),
},
];
const [messages, setMessages] = useState([]);
const [loading, setLoading] = useState(true);
const [activePage, setActivePage] = useState(1);
const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE);
const [searchKeyword, setSearchKeyword] = useState('');
const [searching, setSearching] = useState(false);
const [dateRange, setDateRange] = useState([]);
const [messageCount, setMessageCount] = useState(ITEMS_PER_PAGE);
const [showCreateMessage, setShowCreateMessage] = useState(false);
const [showEditMessage, setShowEditMessage] = useState(false);
const [editingMessage, setEditingMessage] = useState({
id: undefined,
});
const setMessageFormat = (messages) => {
for (let i = 0; i < messages.length; i++) {
messages[i].key = messages[i].id;
}
setMessages(messages);
};
const loadMessages = async (startIdx, pageSize) => {
const res = await API.get(`/api/admin/messages?p=${startIdx}&page_size=${pageSize}`);
const { success, message, data } = res.data;
if (success) {
const newPageData = data.items || [];
setActivePage(data.page || 1);
setMessageCount(data.total || 0);
setMessageFormat(newPageData);
} else {
showError(message);
}
setLoading(false);
};
useEffect(() => {
loadMessages(0, pageSize)
.then()
.catch((reason) => {
showError(reason);
});
}, []);
const deleteMessage = async (messageId) => {
const res = await API.delete(`/api/admin/messages/${messageId}`);
const { success, message } = res.data;
if (success) {
showSuccess(t('消息删除成功'));
// Remove the deleted message from the list
setMessages(messages.filter(msg => msg.id !== messageId));
setMessageCount(messageCount - 1);
} else {
showError(message);
}
};
const searchMessages = async (
startIdx,
pageSize,
searchKeyword,
dateRange,
) => {
if (searchKeyword === '' && dateRange.length === 0) {
// if keyword is blank and no date range, load messages instead.
await loadMessages(startIdx, pageSize);
return;
}
setSearching(true);
let params = new URLSearchParams({
p: startIdx,
page_size: pageSize,
});
if (searchKeyword) {
params.append('keyword', searchKeyword);
}
if (dateRange.length === 2) {
params.append('start_date', dateRange[0].toISOString());
params.append('end_date', dateRange[1].toISOString());
}
const res = await API.get(`/api/admin/messages/search?${params.toString()}`);
const { success, message, data } = res.data;
if (success) {
const newPageData = data.items || [];
setActivePage(data.page || 1);
setMessageCount(data.total || 0);
setMessageFormat(newPageData);
} else {
showError(message);
}
setSearching(false);
};
const handleKeywordChange = async (value) => {
setSearchKeyword(value.trim());
};
const handlePageChange = (page) => {
setActivePage(page);
if (searchKeyword === '' && dateRange.length === 0) {
loadMessages(page, pageSize).then();
} else {
searchMessages(page, pageSize, searchKeyword, dateRange).then();
}
};
const closeCreateMessage = () => {
setShowCreateMessage(false);
};
const closeEditMessage = () => {
setShowEditMessage(false);
setEditingMessage({
id: undefined,
});
};
const refresh = async () => {
setActivePage(1);
if (searchKeyword === '' && dateRange.length === 0) {
await loadMessages(1, pageSize);
} else {
await searchMessages(1, pageSize, searchKeyword, dateRange);
}
};
const handlePageSizeChange = async (size) => {
localStorage.setItem('message-page-size', size + '');
setPageSize(size);
setActivePage(1);
loadMessages(1, size)
.then()
.catch((reason) => {
showError(reason);
});
};
return (
<>
t('第 {{start}} - {{end}} 条,共 {{total}} 条', {
start: page.currentStart,
end: page.currentEnd,
total: messageCount,
}),
currentPage: activePage,
pageSize: pageSize,
total: messageCount,
pageSizeOpts: [10, 20, 50, 100],
showSizeChanger: true,
onPageSizeChange: (size) => {
handlePageSizeChange(size);
},
onPageChange: handlePageChange,
}}
loading={loading}
/>
>
);
};
export default MessageList;