2025-08-05 01:44:28 +00:00
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
|
|
|
|
import { LogOut, User, Calendar, Users, TrendingUp, Clock, MessageSquare, Plus } from 'lucide-react';
|
2025-08-29 08:37:55 +00:00
|
|
|
|
import apiClient from '../utils/apiClient';
|
2025-08-05 01:44:28 +00:00
|
|
|
|
import { Link } from 'react-router-dom';
|
|
|
|
|
|
import { buildApiUrl, API_ENDPOINTS } from '../config/api';
|
|
|
|
|
|
import MeetingTimeline from '../components/MeetingTimeline';
|
|
|
|
|
|
import './Dashboard.css';
|
|
|
|
|
|
|
|
|
|
|
|
const Dashboard = ({ user, onLogout }) => {
|
|
|
|
|
|
const [userInfo, setUserInfo] = useState(null);
|
|
|
|
|
|
const [meetings, setMeetings] = useState([]);
|
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
|
const [error, setError] = useState('');
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
fetchUserData();
|
|
|
|
|
|
}, [user.user_id]);
|
|
|
|
|
|
|
|
|
|
|
|
const fetchUserData = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
setLoading(true);
|
|
|
|
|
|
console.log('Fetching user data for user_id:', user.user_id);
|
|
|
|
|
|
|
2025-08-29 08:37:55 +00:00
|
|
|
|
const userResponse = await apiClient.get(buildApiUrl(API_ENDPOINTS.USERS.DETAIL(user.user_id)));
|
2025-08-05 01:44:28 +00:00
|
|
|
|
console.log('User response:', userResponse.data);
|
|
|
|
|
|
setUserInfo(userResponse.data);
|
|
|
|
|
|
|
2025-08-29 08:37:55 +00:00
|
|
|
|
const meetingsResponse = await apiClient.get(buildApiUrl(`${API_ENDPOINTS.MEETINGS.LIST}?user_id=${user.user_id}`));
|
2025-08-05 01:44:28 +00:00
|
|
|
|
console.log('Meetings response:', meetingsResponse.data);
|
|
|
|
|
|
setMeetings(meetingsResponse.data);
|
|
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error('Error fetching data:', err);
|
|
|
|
|
|
setError('获取数据失败,请刷新重试');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleDeleteMeeting = async (meetingId) => {
|
|
|
|
|
|
try {
|
2025-08-29 08:37:55 +00:00
|
|
|
|
await apiClient.delete(buildApiUrl(API_ENDPOINTS.MEETINGS.DELETE(meetingId)));
|
2025-08-05 01:44:28 +00:00
|
|
|
|
// Refresh meetings list
|
2025-08-29 08:37:55 +00:00
|
|
|
|
const meetingsResponse = await apiClient.get(buildApiUrl(`${API_ENDPOINTS.MEETINGS.LIST}?user_id=${user.user_id}`));
|
2025-08-05 01:44:28 +00:00
|
|
|
|
setMeetings(meetingsResponse.data);
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error('Error deleting meeting:', err);
|
|
|
|
|
|
// You might want to show an error message to the user here
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const groupMeetingsByDate = (meetings) => {
|
|
|
|
|
|
return meetings.reduce((acc, meeting) => {
|
|
|
|
|
|
const date = new Date(meeting.meeting_time || meeting.created_at).toISOString().split('T')[0];
|
|
|
|
|
|
if (!acc[date]) {
|
|
|
|
|
|
acc[date] = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
acc[date].push(meeting);
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
}, {});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const formatDate = (dateString) => {
|
|
|
|
|
|
const date = new Date(dateString);
|
|
|
|
|
|
return date.toLocaleDateString('zh-CN', {
|
|
|
|
|
|
year: 'numeric',
|
|
|
|
|
|
month: 'long',
|
|
|
|
|
|
day: 'numeric'
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (loading) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="dashboard">
|
|
|
|
|
|
<div className="loading-container">
|
|
|
|
|
|
<div className="loading-spinner"></div>
|
|
|
|
|
|
<p>加载中...</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="dashboard">
|
|
|
|
|
|
<div className="error-container">
|
|
|
|
|
|
<p>{error}</p>
|
|
|
|
|
|
<button onClick={fetchUserData} className="retry-btn">重试</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const groupedMeetings = groupMeetingsByDate(meetings);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="dashboard">
|
|
|
|
|
|
|
|
|
|
|
|
{/* Header */}
|
|
|
|
|
|
<header className="dashboard-header">
|
|
|
|
|
|
<div className="header-content">
|
|
|
|
|
|
<div className="logo">
|
|
|
|
|
|
<MessageSquare className="logo-icon" />
|
|
|
|
|
|
<span className="logo-text">iMeeting</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="user-actions">
|
|
|
|
|
|
<span className="welcome-text">欢迎,{userInfo?.caption}</span>
|
|
|
|
|
|
<button className="logout-btn" onClick={onLogout}>
|
|
|
|
|
|
<LogOut size={18} />
|
|
|
|
|
|
退出
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="dashboard-content">
|
|
|
|
|
|
{/* User Info Section */}
|
|
|
|
|
|
<section className="user-info-section">
|
|
|
|
|
|
<div className="user-card">
|
|
|
|
|
|
<div className="user-avatar">
|
|
|
|
|
|
<User size={32} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="user-details">
|
|
|
|
|
|
<h2>{userInfo?.caption}</h2>
|
|
|
|
|
|
<p className="user-email">{userInfo?.email}</p>
|
|
|
|
|
|
<p className="join-date">加入时间:{formatDate(userInfo?.created_at)}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Stats Cards */}
|
|
|
|
|
|
<div className="stats-grid">
|
|
|
|
|
|
<div className="stat-card">
|
|
|
|
|
|
<div className="stat-icon">
|
|
|
|
|
|
<Calendar className="icon" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="stat-info">
|
|
|
|
|
|
<h3>{userInfo?.meetings_created}</h3>
|
|
|
|
|
|
<p>发起的会议</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="stat-card">
|
|
|
|
|
|
<div className="stat-icon">
|
|
|
|
|
|
<Users className="icon" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="stat-info">
|
|
|
|
|
|
<h3>{userInfo?.meetings_attended}</h3>
|
|
|
|
|
|
<p>参加的会议</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="stat-card">
|
|
|
|
|
|
<div className="stat-icon">
|
|
|
|
|
|
<TrendingUp className="icon" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="stat-info">
|
|
|
|
|
|
<h3>{meetings.length}</h3>
|
|
|
|
|
|
<p>相关会议总数</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Meetings Timeline Section */}
|
|
|
|
|
|
<section className="meetings-section">
|
|
|
|
|
|
<div className="section-header">
|
|
|
|
|
|
<div className="section-title">
|
|
|
|
|
|
<h2>
|
|
|
|
|
|
<Clock size={24} />
|
|
|
|
|
|
会议时间轴
|
|
|
|
|
|
</h2>
|
|
|
|
|
|
<p>按时间顺序展示您参与的所有会议</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Link to="/meetings/create">
|
|
|
|
|
|
<span className="create-meeting-btn">
|
|
|
|
|
|
<Plus size={20} />
|
|
|
|
|
|
新建会议纪要
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</Link>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<MeetingTimeline
|
|
|
|
|
|
meetingsByDate={groupedMeetings}
|
|
|
|
|
|
currentUser={user}
|
|
|
|
|
|
onDeleteMeeting={handleDeleteMeeting}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default Dashboard;
|