From 2060432c3e287af643b1d22d2b5a251dd1ad6c0c Mon Sep 17 00:00:00 2001 From: PCBZ Date: Thu, 24 Apr 2025 00:20:01 -0700 Subject: [PATCH 1/4] refine comments in server --- Server/src/routes/donor.js | 6 +-- Server/src/routes/donorList.js | 18 +++---- Server/src/routes/event.js | 92 +++++++++++++++++----------------- 3 files changed, 57 insertions(+), 59 deletions(-) diff --git a/Server/src/routes/donor.js b/Server/src/routes/donor.js index 00ba3e86..c084e144 100644 --- a/Server/src/routes/donor.js +++ b/Server/src/routes/donor.js @@ -160,8 +160,8 @@ router.get('/', protect, async (req, res) => { // Get total count for pagination const total = await prisma.donor.count({ where }); - // 转换下划线格式的排序字段为驼峰命名 - // 映射查询参数中的蛇形命名到数据库模型的驼峰命名 + // Convert underscore format sort field to camelCase + // Map snake_case in query parameters to camelCase in database model const sortMapping = { 'first_name': 'firstName', 'last_name': 'lastName', @@ -171,7 +171,7 @@ router.get('/', protect, async (req, res) => { 'last_gift_date': 'lastGiftDate' }; - // 使用映射表或直接使用字段名(如果已经是驼峰命名) + // Use mapping table or directly use field name (if already in camelCase) const sortField = sortMapping[sort] || sort; // Get donors with pagination, sorting, and filtering diff --git a/Server/src/routes/donorList.js b/Server/src/routes/donorList.js index 1104c838..bb0df868 100644 --- a/Server/src/routes/donorList.js +++ b/Server/src/routes/donorList.js @@ -341,13 +341,13 @@ router.post('/:id/donors', protect, async (req, res) => { return res.status(400).json({ message: 'Invalid donor IDs array' }); } - // 确保所有 ID 都是数字类型 + // Ensure all IDs are numeric const numericDonorIds = donorIds.map(id => Number(id)); if (numericDonorIds.some(isNaN)) { return res.status(400).json({ message: 'Invalid donor ID format' }); } - // Check if list exists + // Verify if the donor list exists const list = await prisma.eventDonorList.findUnique({ where: { id: listId } }); @@ -750,7 +750,7 @@ router.get('/:id/donors', protect, async (req, res) => { const limitNum = parseInt(limit); const skip = (pageNum - 1) * limitNum; - // 验证捐赠者列表是否存在 + // Verify if the donor list exists const list = await prisma.eventDonorList.findUnique({ where: { id: listId } }); @@ -759,17 +759,17 @@ router.get('/:id/donors', protect, async (req, res) => { return res.status(404).json({ message: 'Donor list not found' }); } - // 构建查询条件 + // Build query conditions const where = { donorListId: listId }; - // 添加状态过滤 + // Add status filter if (status) { where.status = status; } - // 添加名称搜索过滤(搜索关联的捐赠者的名称) + // Add name search filter (search for associated donor names) if (search) { where.donor = { OR: [ @@ -780,10 +780,10 @@ router.get('/:id/donors', protect, async (req, res) => { }; } - // 获取总记录数 + // Get total record count const total = await prisma.eventDonor.count({ where }); - // 获取捐赠者数据 + // Get donor data const eventDonors = await prisma.eventDonor.findMany({ where, skip, @@ -804,7 +804,7 @@ router.get('/:id/donors', protect, async (req, res) => { } }); - // 转换数据为API期望的格式 + // Convert data to API expected format const formattedDonors = eventDonors.map(ed => ({ id: ed.id, donor_id: ed.donorId, diff --git a/Server/src/routes/event.js b/Server/src/routes/event.js index 12ea8cea..501b37a0 100644 --- a/Server/src/routes/event.js +++ b/Server/src/routes/event.js @@ -107,16 +107,14 @@ router.get('/', protect, async (req, res) => { isDeleted: false }; - // 将status字符串值转换为Prisma的EventStatus枚举值 + // Convert status string value to Prisma EventStatus enum + // Valid values: Planning, ListGeneration, Review, Ready, Complete if (status) { - // 有效值: Planning, ListGeneration, Review, Ready, Complete - const validStatuses = ['Planning', 'ListGeneration', 'Review', 'Ready', 'Complete']; - - if (validStatuses.includes(status)) { - where.status = status; - } else if (status === 'active') { - // 特殊情况: 'active' 可以映射到多个"活跃"状态 + // Special case: 'active' can map to multiple "active" states + if (status === 'active') { where.status = { in: ['Planning', 'ListGeneration', 'Review', 'Ready'] }; + } else { + where.status = status; } } @@ -287,7 +285,7 @@ router.post('/', protect, async (req, res) => { const donorList = await prisma.eventDonorList.create({ data: { eventId: event.id, - name: `${event.name} - 捐赠者名单`, + name: `${event.name} - Donor List`, totalDonors: 0, approved: 0, excluded: 0, @@ -907,7 +905,7 @@ router.get('/:id/donors', protect, async (req, res) => { return res.status(404).json({ message: 'Event not found' }); } - // 设置分页参数 + // Set pagination parameters const { page = '1', limit = '20', @@ -919,7 +917,7 @@ router.get('/:id/donors', protect, async (req, res) => { const limitNum = parseInt(limit); const skip = (pageNum - 1) * limitNum; - // 如果事件没有捐赠者列表,返回空结果 + // If event has no donor list, return empty result if (!event.donorLists || event.donorLists.length === 0) { return res.json({ donors: [], @@ -932,20 +930,20 @@ router.get('/:id/donors', protect, async (req, res) => { }); } - // 获取事件的第一个捐赠者列表ID + // Get the first donor list ID of the event const donorListId = event.donorLists[0].id; - // 构建查询条件 + // Build query conditions const where = { donorListId: donorListId }; - // 添加状态过滤 + // Add status filter if (status) { where.status = status; } - // 添加名称搜索过滤 + // Add name search filter if (search) { const searchLower = search.toLowerCase(); where.donor = { @@ -957,10 +955,10 @@ router.get('/:id/donors', protect, async (req, res) => { }; } - // 获取总记录数 + // Get total record count const total = await prisma.eventDonor.count({ where }); - // 获取捐赠者数据 + // Get donors already in the event const eventDonors = await prisma.eventDonor.findMany({ where, skip, @@ -985,7 +983,7 @@ router.get('/:id/donors', protect, async (req, res) => { } }); - // 格式化数据响应 + // Format response const formattedDonors = eventDonors.map(ed => ({ id: ed.id, donor_id: ed.donorId, @@ -1067,7 +1065,7 @@ router.get('/:id/donors', protect, async (req, res) => { */ router.post('/:id/donors', protect, async (req, res) => { try { - // 验证事件ID + // Validate event ID let eventId; try { eventId = parseInt(req.params.id); @@ -1078,7 +1076,7 @@ router.post('/:id/donors', protect, async (req, res) => { return res.status(400).json({ message: 'Invalid event ID format' }); } - // 验证捐赠者ID + // Validate donor ID const { donorId } = req.body; if (!donorId) { return res.status(400).json({ message: 'Donor ID is required' }); @@ -1089,7 +1087,7 @@ router.post('/:id/donors', protect, async (req, res) => { return res.status(400).json({ message: 'Invalid donor ID format' }); } - // 验证事件是否存在 + // Validate if event exists const event = await prisma.event.findUnique({ where: { id: eventId }, include: { @@ -1107,7 +1105,7 @@ router.post('/:id/donors', protect, async (req, res) => { return res.status(404).json({ message: 'Event not found' }); } - // 验证捐赠者是否存在 + // Validate if donor exists const donor = await prisma.donor.findUnique({ where: { id: donorIdInt } }); @@ -1116,13 +1114,13 @@ router.post('/:id/donors', protect, async (req, res) => { return res.status(404).json({ message: 'Donor not found' }); } - // 获取或创建事件的捐赠者列表 + // Get or create event's donor list let donorList = event.donorLists && event.donorLists.length > 0 ? event.donorLists[0] : null; if (!donorList) { - // 创建捐赠者列表 + // Create donor list donorList = await prisma.eventDonorList.create({ data: { name: `${event.name} - Donor List`, @@ -1138,7 +1136,7 @@ router.post('/:id/donors', protect, async (req, res) => { }); } - // 检查捐赠者是否已在列表中 + // Check if donor is already in the list const existingEventDonor = await prisma.eventDonor.findFirst({ where: { donorListId: donorList.id, @@ -1154,7 +1152,7 @@ router.post('/:id/donors', protect, async (req, res) => { }); } - // 添加捐赠者到列表 + // Add donor to list const eventDonor = await prisma.eventDonor.create({ data: { donorListId: donorList.id, @@ -1164,7 +1162,7 @@ router.post('/:id/donors', protect, async (req, res) => { } }); - // 更新列表统计数据 + // Update list statistics await prisma.eventDonorList.update({ where: { id: donorList.id }, data: { @@ -1173,7 +1171,7 @@ router.post('/:id/donors', protect, async (req, res) => { } }); - // 获取更新后的捐赠者列表 + // Get updated donor list const updatedDonorList = await prisma.eventDonorList.findUnique({ where: { id: donorList.id }, select: { @@ -1234,7 +1232,7 @@ router.post('/:id/donors', protect, async (req, res) => { */ router.delete('/:id/donors/:donorId', protect, async (req, res) => { try { - // 验证事件ID + // Validate event ID let eventId; try { eventId = parseInt(req.params.id); @@ -1245,7 +1243,7 @@ router.delete('/:id/donors/:donorId', protect, async (req, res) => { return res.status(400).json({ message: 'Invalid event ID format' }); } - // 验证捐赠者ID + // Validate donor ID let donorId; try { donorId = parseInt(req.params.donorId); @@ -1256,7 +1254,7 @@ router.delete('/:id/donors/:donorId', protect, async (req, res) => { return res.status(400).json({ message: 'Invalid donor ID format' }); } - // 验证事件是否存在并获取捐赠者列表ID + // Validate if event exists and get donor list ID const event = await prisma.event.findUnique({ where: { id: eventId }, include: { @@ -1274,14 +1272,14 @@ router.delete('/:id/donors/:donorId', protect, async (req, res) => { return res.status(404).json({ message: 'Event not found' }); } - // 检查事件是否有捐赠者列表 + // Check if event has donor list if (!event.donorLists || event.donorLists.length === 0) { return res.status(404).json({ message: 'Event has no donor list' }); } const donorListId = event.donorLists[0].id; - // 查找捐赠者在列表中的记录 + // Find donor record in the list const eventDonor = await prisma.eventDonor.findFirst({ where: { donorListId: donorListId, @@ -1293,22 +1291,22 @@ router.delete('/:id/donors/:donorId', protect, async (req, res) => { return res.status(404).json({ message: 'Donor not found in this event' }); } - // 获取捐赠者状态,用于更新统计信息 + // Get donor status for updating statistics const donorStatus = eventDonor.status; - // 删除捐赠者记录 + // Delete donor record await prisma.eventDonor.delete({ where: { id: eventDonor.id } }); - // 更新列表统计信息 + // Update list statistics const updateData = { totalDonors: { decrement: 1 } }; - // 根据捐赠者状态更新对应的计数 + // Update corresponding count based on donor status if (donorStatus === 'Pending') { updateData.pending = { decrement: 1 }; } else if (donorStatus === 'Approved') { @@ -1319,13 +1317,13 @@ router.delete('/:id/donors/:donorId', protect, async (req, res) => { updateData.autoExcluded = { decrement: 1 }; } - // 更新捐赠者列表统计数据 + // Update donor list statistics await prisma.eventDonorList.update({ where: { id: donorListId }, data: updateData }); - // 获取更新后的捐赠者列表 + // Get updated donor list const updatedDonorList = await prisma.eventDonorList.findUnique({ where: { id: donorListId }, select: { @@ -1703,7 +1701,7 @@ router.get('/:id/available-donors', protect, async (req, res) => { try { const eventId = parseInt(req.params.id); - // Step 1: 获取事件信息 + // Step 1: Get event information const event = await prisma.event.findUnique({ where: { id: eventId } }); @@ -1712,7 +1710,7 @@ router.get('/:id/available-donors', protect, async (req, res) => { return res.status(404).json({ message: 'Event not found' }); } - // Step 2: 获取事件中已有的捐赠者 + // Step 2: Get existing donors in the event const existingDonors = await prisma.eventDonor.findMany({ where: { donorList: { @@ -1720,28 +1718,28 @@ router.get('/:id/available-donors', protect, async (req, res) => { } }, include: { - donor: true // 包含完整的捐赠者信息 + donor: true // Include full donor information } }); - // 处理多种ID字段并确保唯一性 + // Process multiple ID fields and ensure uniqueness const currentEventDonorIds = [...new Set( existingDonors .map(donor => { - // 处理可能的不同ID字段 + // Process possible different ID fields const id = donor.donorId || donor.donor_id || donor.donor?.id; if (!id) { console.warn(`Warning: Donor record missing ID:`, donor); } return id; }) - .filter(id => id != null) // 过滤掉null值 + .filter(id => id != null) // Filter out null values )]; - // 如果没有找到捐赠者列表 + // If no donors found in the event if (!existingDonors && !currentEventDonorIds.length) { console.log(`No donors found for event ${eventId}, creating empty list`); - // 继续处理,但使用空数组 + // Continue processing, but use empty array } // Get pagination and filter parameters From 40f1b0a3d62b0d585f5534c43c56a242dc1c5942 Mon Sep 17 00:00:00 2001 From: PCBZ Date: Thu, 24 Apr 2025 00:26:15 -0700 Subject: [PATCH 2/4] reset comments in client --- client/src/components/Dashboard.jsx | 14 +- client/src/components/donors/Donors.jsx | 16 +- client/src/components/donors/EventDetail.jsx | 8 +- .../components/donors/EventKeywordAnalyzer.js | 192 +++++++----------- .../src/components/donors/RelatedEvents.jsx | 20 +- .../src/components/events/CreateNewEvent.css | 2 +- .../src/components/events/CreateNewEvent.jsx | 6 +- client/src/components/events/DonorCard.css | 90 -------- client/src/components/events/DonorCard.jsx | 45 ---- 9 files changed, 101 insertions(+), 292 deletions(-) delete mode 100644 client/src/components/events/DonorCard.css delete mode 100644 client/src/components/events/DonorCard.jsx diff --git a/client/src/components/Dashboard.jsx b/client/src/components/Dashboard.jsx index d05e6cfe..add75ed9 100644 --- a/client/src/components/Dashboard.jsx +++ b/client/src/components/Dashboard.jsx @@ -16,12 +16,12 @@ const Dashboard = () => { }); const [events, setEvents] = useState([]); const [error, setError] = useState(null); - // 添加组件挂载状态引用 + // Add component mount state reference const isMountedRef = React.useRef(true); - // 使用 useNavigate 钩子 + // Use useNavigate hook const navigate = useNavigate(); - // 在组件级别定义fetchDashboardData函数 + // Define fetchDashboardData function at component level const fetchDashboardData = async () => { try { if (isMountedRef.current) setLoading(true); @@ -34,7 +34,7 @@ const Dashboard = () => { const eventsResult = await getEvents({ status: 'active', limit: 10 }); const activeEvents = eventsResult.data || []; - // 在设置状态前检查组件是否仍然挂载 + // Check if component is still mounted before setting state if (isMountedRef.current) { setEvents(activeEvents.slice(0, 3)); // Only show the first three events @@ -56,16 +56,16 @@ const Dashboard = () => { }; useEffect(() => { - // 组件挂载时,初始化用户数据 + // Initialize user data when component mounts const userData = getCurrentUser(); if (userData && isMountedRef.current) { setUser(userData); } - // 调用fetchDashboardData函数获取数据 + // Call fetchDashboardData function to get data fetchDashboardData(); - // 清理函数,在组件卸载时执行 + // Cleanup function, executed when component unmounts return () => { isMountedRef.current = false; }; diff --git a/client/src/components/donors/Donors.jsx b/client/src/components/donors/Donors.jsx index b1151efb..85e4263e 100644 --- a/client/src/components/donors/Donors.jsx +++ b/client/src/components/donors/Donors.jsx @@ -296,7 +296,7 @@ const Donors = () => { const handleSearch = async (e) => { const newSearchQuery = e.target.value; setSearchQuery(newSearchQuery); - setCurrentPage(1); // 重置到第一页 + setCurrentPage(1); // Reset to the first page if (!selectedEvent) return; @@ -591,7 +591,7 @@ const Donors = () => { const handleStatusFilter = async (status) => { const newStatus = status === statusFilter ? '' : status; setStatusFilter(newStatus); - setCurrentPage(1); // 重置到第一页 + setCurrentPage(1); // Reset to the first page if (!selectedEvent) return; @@ -599,7 +599,7 @@ const Donors = () => { setLoading(prev => ({ ...prev, donors: true })); setError(prev => ({ ...prev, donors: null })); - // 获取新的捐赠者数据 + // Fetch new donor data const response = await getEventDonors(selectedEvent.id, { page: 1, limit: itemsPerPage, @@ -620,11 +620,11 @@ const Donors = () => { const handleDonorAdded = async () => { try { - // 重新获取活动的捐赠者列表 + // Refresh the event's donor list await fetchEventDonors(); - // 更新统计信息 + // Update statistics await fetchEventStats(); - // 显示成功消息 + // Show success message setSuccess('Donor added successfully!'); setTimeout(() => setSuccess(''), 3000); } catch (error) { @@ -869,8 +869,8 @@ const Donors = () => { error={error} fetchEvents={fetchEvents} formatDate={formatDate} - events={events} // 添加所有事件数据 - onEventSelect={handleRelatedEventSelect} // 添加事件选择处理函数 + events={events} // Add all event data + onEventSelect={handleRelatedEventSelect} // Add event selection handler /> diff --git a/client/src/components/donors/EventDetail.jsx b/client/src/components/donors/EventDetail.jsx index d4598274..eedb3bad 100644 --- a/client/src/components/donors/EventDetail.jsx +++ b/client/src/components/donors/EventDetail.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { FaCalendarAlt, FaMapMarkerAlt, FaUsers, FaClock, FaSpinner } from 'react-icons/fa'; import './EventDetail.css'; -import RelatedEvents from './RelatedEvents'; // 导入相关活动组件 +import RelatedEvents from './RelatedEvents'; // Import related events component const EventDetail = ({ selectedEvent, @@ -9,8 +9,8 @@ const EventDetail = ({ error, fetchEvents, formatDate, - events, // 添加events参数接收所有活动数据 - onEventSelect // 添加onEventSelect参数接收事件选择处理函数 + events, // Add events parameter to receive all event data + onEventSelect // Add onEventSelect parameter to receive event selection handler }) => { return (
@@ -55,7 +55,7 @@ const EventDetail = ({ )}
- {/* 相关活动组件 - 在Event Details区域下方添加 */} + {/* Related events component - Added below Event Details section */} {selectedEvent && events && events.length > 0 && ( { if (!event) return []; - // 将活动名称和描述合并,转为小写以便匹配 + // Combine event name and description, convert to lowercase for matching const text = `${event.name || ''} ${event.description || ''} ${event.type || ''}`.toLowerCase(); const results = []; - // 遍历每个关键词类别 + // Iterate through each keyword category for (const [category, keywords] of Object.entries(KEYWORD_CATEGORIES)) { - // 找出所有匹配的关键词 + // Find all matching keywords const matches = keywords.filter(keyword => { - // 如果关键词以 \b 开头,说明是正则表达式模式 + // If keyword starts with \b, it's a regex pattern if (keyword.startsWith('\\b')) { const regex = new RegExp(keyword, 'i'); return regex.test(text); } - // 否则使用普通的字符串包含检查 + // Otherwise use normal string inclusion check return text.includes(keyword.toLowerCase()); }); if (matches.length > 0) { - // 获取类别权重,如果未定义则使用默认权重 1.0 + // Get category weight, use default weight 1.0 if not defined const weight = CATEGORY_WEIGHTS[category] || 1.0; - // 计算分数:匹配数量 * 权重 + // Calculate score: number of matches * weight const score = matches.length * weight; - // 标题中出现关键词时额外加分 + // Extra points for keywords appearing in title const titleBonus = matches.some(match => { if (match.startsWith('\\b')) { const regex = new RegExp(match, 'i'); @@ -182,14 +182,14 @@ export const analyzeEventKeywords = (event) => { } } - // 按分数降序排序 + // Sort by score in descending order return results.sort((a, b) => b.score - a.score); }; /** - * 分析事件上下文 - * @param {Object} event - 活动对象 - * @returns {Object} - 返回上下文分析结果 + * Analyze event context + * @param {Object} event - Event object + * @returns {Object} - Returns context analysis results */ export const analyzeEventContext = (event) => { const text = `${event.name || ''} ${event.description || ''}`.toLowerCase(); @@ -198,12 +198,12 @@ export const analyzeEventContext = (event) => { for (const [context, patterns] of Object.entries(CONTEXT_PATTERNS)) { let score = 0; - // 检查主要指标 + // Check main indicators patterns.indicators.forEach(indicator => { if (text.includes(indicator)) score += 2; }); - // 检查相关词 + // Check related words patterns.related.forEach(related => { if (text.includes(related)) score += 1; }); @@ -217,29 +217,29 @@ export const analyzeEventContext = (event) => { }; /** - * 找出与给定活动相似的活动 - * @param {Object} targetEvent - 目标活动 - * @param {Array} allEvents - 所有活动的数组 - * @param {number} maxResults - 最大返回结果数 - * @returns {Array} - 相似活动数组,按相似度排序 + * Find events similar to the given event + * @param {Object} targetEvent - Target event + * @param {Array} allEvents - Array of all events + * @param {number} maxResults - Maximum number of results to return + * @returns {Array} - Array of similar events, sorted by similarity */ export const findSimilarEvents = (targetEvent, allEvents, maxResults = 5) => { if (!targetEvent || !allEvents || allEvents.length === 0) { return []; } - // 分析目标活动 + // Analyze target event const targetKeywords = analyzeEventKeywords(targetEvent); const targetContext = analyzeEventContext(targetEvent); - // 对其他活动进行相似度评分 + // Score similarity for other events const similarEvents = allEvents - .filter(event => event.id !== targetEvent.id) // 排除目标活动本身 + .filter(event => event.id !== targetEvent.id) // Exclude target event itself .map(event => { const eventKeywords = analyzeEventKeywords(event); const eventContext = analyzeEventContext(event); - // 计算关键词匹配分数 + // Calculate keyword matching score const keywordScore = eventKeywords.reduce((score, keyword) => { const targetMatch = targetKeywords.find(tk => tk.category === keyword.category); if (targetMatch) { @@ -248,93 +248,37 @@ export const findSimilarEvents = (targetEvent, allEvents, maxResults = 5) => { return score; }, 0); - // 计算上下文相似度 - const contextScore = Object.keys(targetContext).reduce((score, context) => { - if (eventContext[context]) { - return score + Math.min(targetContext[context], eventContext[context]); - } - return score; + // Calculate context matching score + const contextScore = Object.entries(eventContext).reduce((score, [context, value]) => { + const targetValue = targetContext[context] || 0; + return score + (value * targetValue); }, 0); - // 计算最终相似度分数 - const totalScore = (keywordScore * 0.7) + (contextScore * 0.3); - return { event, - similarityScore: totalScore, - details: { - keywordScore, - contextScore, - matchedCategories: eventKeywords - .filter(k => targetKeywords.some(tk => tk.category === k.category)) - .map(k => k.category) - } + similarityScore: keywordScore + contextScore, + matchingCategories: eventKeywords.map(k => k.category), + contextMatches: Object.keys(eventContext) }; }) - .filter(item => item.similarityScore > 0) // 只保留有相似度的活动 - .sort((a, b) => b.similarityScore - a.similarityScore) // 按相似度降序排序 - .slice(0, maxResults); // 只返回指定数量的结果 - + .filter(result => result.similarityScore > 0) + .sort((a, b) => b.similarityScore - a.similarityScore) + .slice(0, maxResults); + return similarEvents; }; /** - * 根据关键词类别获取类别的显示名称 - * @param {string} categoryKey - 类别键名 - * @returns {string} - 格式化的类别名称 + * Get display name for a category + * @param {string} categoryKey - Category key + * @returns {string} - Formatted display name */ export const getCategoryDisplayName = (categoryKey) => { - const displayNames = { - 'breast-cancer': 'Breast Cancer', - 'prostate-cancer': 'Prostate Cancer', - 'lung-cancer': 'Lung Cancer', - 'leukemia': 'Leukemia', - 'lymphoma': 'Lymphoma', - 'pediatric-cancer': 'Pediatric Cancer', - 'fundraising': 'Fundraising', - 'research': 'Research', - 'awareness': 'Awareness & Education', - 'memorial': 'Memorial', - 'run-walk': 'Run/Walk', - 'cycling': 'Cycling', - 'survivors': 'Survivors', - 'women-health': 'Women\'s Health', - 'men-health': 'Men\'s Health', - 'youth': 'Youth', - 'seniors': 'Seniors', - 'patient-support': 'Patient Support', - 'family-support': 'Family Support', - 'emotional-support': 'Emotional Support', - 'clinical-trials': 'Clinical Trials', - 'annual': 'Annual Event', - 'spring': 'Spring', - 'summer': 'Summer', - 'fall': 'Fall', - 'holiday': 'Holiday', - 'vancouver': 'Vancouver', - 'victoria': 'Victoria', - 'kelowna': 'Kelowna', - 'abbotsford': 'Abbotsford', - 'rural': 'Rural/Remote', - 'gala': 'Gala/Dinner', - 'concert': 'Concert', - 'virtual': 'Virtual', - 'golf': 'Golf', - 'community': 'Community', - 'precision-medicine': 'Precision Medicine', - 'immunotherapy': 'Immunotherapy', - 'radiation-therapy': 'Radiation Therapy', - 'bc-cancer-research': 'BC Cancer Research', - 'screening-prevention': 'Screening & Prevention', - 'planned-giving': 'Planned Giving', - 'monthly-giving': 'Monthly Giving', - 'major-gifts': 'Major Gifts', - 'medical-equipment': 'Medical Equipment', - 'support-programs': 'Support Programs', - 'signature-events': 'Signature Events' - }; - - return displayNames[categoryKey] || categoryKey.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); + // Convert snake_case to Title Case + return categoryKey + .split('-') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); }; export default { diff --git a/client/src/components/donors/RelatedEvents.jsx b/client/src/components/donors/RelatedEvents.jsx index 91e98797..a6f580bc 100644 --- a/client/src/components/donors/RelatedEvents.jsx +++ b/client/src/components/donors/RelatedEvents.jsx @@ -4,16 +4,16 @@ import { findSimilarEvents, getCategoryDisplayName } from './EventKeywordAnalyze import './RelatedEvents.css'; /** - * 相关活动组件 - * 根据关键词分析显示与当前活动相似的其他活动 + * Related Events Component + * Displays events similar to the current event based on keyword analysis * * @param {Object} props - * @param {Object} props.currentEvent - 当前活动对象 - * @param {Array} props.allEvents - 所有活动的数组 - * @param {Function} props.formatDate - 日期格式化函数 - * @param {Function} props.onEventSelect - 活动选择回调函数 - * @param {number} props.maxEvents - 最大显示活动数量,默认为3 - * @param {boolean} props.loading - 加载状态 + * @param {Object} props.currentEvent - Current event object + * @param {Array} props.allEvents - Array of all events + * @param {Function} props.formatDate - Date formatting function + * @param {Function} props.onEventSelect - Event selection callback function + * @param {number} props.maxEvents - Maximum number of events to display, default is 3 + * @param {boolean} props.loading - Loading state */ const RelatedEvents = ({ currentEvent, @@ -26,7 +26,7 @@ const RelatedEvents = ({ const [similarEvents, setSimilarEvents] = useState([]); const [showTooltip, setShowTooltip] = useState(false); - // 当当前活动或所有活动改变时,重新计算相似活动 + // Recalculate similar events when current event or all events change useEffect(() => { if (currentEvent && allEvents && allEvents.length > 0) { const similar = findSimilarEvents(currentEvent, allEvents, maxEvents); @@ -36,7 +36,7 @@ const RelatedEvents = ({ } }, [currentEvent, allEvents, maxEvents]); - // 如果没有相似活动,不显示组件 + // Don't display component if there are no similar events if (similarEvents.length === 0 && !loading) { return null; } diff --git a/client/src/components/events/CreateNewEvent.css b/client/src/components/events/CreateNewEvent.css index 783abcb5..a4b12ed9 100644 --- a/client/src/components/events/CreateNewEvent.css +++ b/client/src/components/events/CreateNewEvent.css @@ -167,7 +167,7 @@ margin-top: 0.75rem; } -/* 日历选择器样式 */ +/* Calendar selector styles */ .create-event-date-input { position: relative; } diff --git a/client/src/components/events/CreateNewEvent.jsx b/client/src/components/events/CreateNewEvent.jsx index e83ceaff..59370a3a 100644 --- a/client/src/components/events/CreateNewEvent.jsx +++ b/client/src/components/events/CreateNewEvent.jsx @@ -39,7 +39,7 @@ function CreateNewEvent({ onClose, onEventCreated }) { endDate: useRef(null) }; - // 处理点击外部关闭日历 + // Handle click outside to close calendar useEffect(() => { function handleClickOutside(event) { Object.keys(calendarRefs).forEach(key => { @@ -55,7 +55,7 @@ function CreateNewEvent({ onClose, onEventCreated }) { }; }, []); - // 获取当前月份的日历数据 + // Get calendar days for current month const getCalendarDays = (date) => { const year = date.getFullYear(); const month = date.getMonth(); @@ -63,7 +63,7 @@ function CreateNewEvent({ onClose, onEventCreated }) { const lastDay = new Date(year, month + 1, 0); const days = []; - // 添加上个月的日期 + // Add days from previous month const firstDayWeekday = firstDay.getDay(); for (let i = firstDayWeekday - 1; i >= 0; i--) { const date = new Date(year, month, -i); diff --git a/client/src/components/events/DonorCard.css b/client/src/components/events/DonorCard.css deleted file mode 100644 index 89eccacc..00000000 --- a/client/src/components/events/DonorCard.css +++ /dev/null @@ -1,90 +0,0 @@ -.donor-card { - margin-bottom: 15px; -} - -.donor-info { - border-radius: 8px; - padding: 20px; - border-left: 4px solid #2563eb; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - background-color: white; -} - -.donor-info.excluded { - border-left: 4px solid #aaa; -} - -.donor-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 12px; -} - -.donor-name { - font-size: 18px; - font-weight: 600; - color: #333; - margin: 0; -} - -.auto-excluded { - background-color: #ddd; - color: #666; - padding: 4px 8px; - border-radius: 4px; - font-size: 14px; -} - -.donor-type { - display: inline-block; - font-size: 14px; - color: #666; - margin-bottom: 8px; -} - -.priority-tag { - display: inline-block; - background-color: #e3f2fd; - color: #0c63e4; - padding: 6px 12px; - border-radius: 20px; - margin-right: 8px; - margin-bottom: 8px; - font-size: 14px; -} - -.interest-tag { - display: inline-block; - background-color: #f0f2ff; - color: #4a5568; - padding: 6px 12px; - border-radius: 20px; - margin-right: 8px; - margin-bottom: 8px; - font-size: 14px; -} - -.flag-tag { - display: inline-block; - background-color: #ffe6e6; - color: #e53e3e; - padding: 6px 12px; - border-radius: 20px; - margin-right: 8px; - margin-bottom: 8px; - font-size: 14px; -} - -.relationships, .previous-events { - margin-top: 12px; - font-size: 14px; - color: #555; - display: flex; - align-items: flex-start; -} - -.label { - font-weight: 500; - margin-right: 5px; -} \ No newline at end of file diff --git a/client/src/components/events/DonorCard.jsx b/client/src/components/events/DonorCard.jsx deleted file mode 100644 index ec1ea4ec..00000000 --- a/client/src/components/events/DonorCard.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react'; -import './DonorCard.css'; - -const DonorCard = ({ donor }) => { - return ( -
-
-
-

{donor.name}

- {donor.autoExcluded && Auto-Excluded} -
- - {donor.type === 'Corporate' && {donor.type}} - - {donor.priority && ( - {donor.priority} - )} - - {donor.interests && donor.interests.map((interest, index) => ( - {interest} - ))} - - {donor.flag && ( - {donor.flag} - )} - - {donor.relationships && ( -
- Relationships: - {donor.relationships} -
- )} - - {donor.previousEvents && donor.previousEvents.length > 0 && ( -
- Previous Events: - {donor.previousEvents.join(', ')} -
- )} -
-
- ); -}; - -export default DonorCard; \ No newline at end of file From 4799d2d84c062ffcc9b9c1ea3d5e4b5c3246d9b7 Mon Sep 17 00:00:00 2001 From: PCBZ Date: Thu, 24 Apr 2025 00:29:26 -0700 Subject: [PATCH 3/4] fix crash --- client/src/components/donors/RelatedEvents.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/components/donors/RelatedEvents.jsx b/client/src/components/donors/RelatedEvents.jsx index a6f580bc..e94230ff 100644 --- a/client/src/components/donors/RelatedEvents.jsx +++ b/client/src/components/donors/RelatedEvents.jsx @@ -69,7 +69,7 @@ const RelatedEvents = ({ ) : (
- {similarEvents.map(({ event, details }) => ( + {similarEvents.map(({ event, matchingCategories }) => (
- {details.matchedCategories.slice(0, 3).map(category => ( + {matchingCategories.slice(0, 3).map(category => (
{getCategoryDisplayName(category)}
))} - {details.matchedCategories.length > 3 && ( -
+{details.matchedCategories.length - 3} more
+ {matchingCategories.length > 3 && ( +
+{matchingCategories.length - 3} more
)}
From 668614158a520bbc94e3cba8454dc0571fc271eb Mon Sep 17 00:00:00 2001 From: PCBZ Date: Thu, 24 Apr 2025 00:36:02 -0700 Subject: [PATCH 4/4] resolve conflicts --- Server/src/routes/event.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Server/src/routes/event.js b/Server/src/routes/event.js index 4e8a2b15..31cb25d2 100644 --- a/Server/src/routes/event.js +++ b/Server/src/routes/event.js @@ -110,7 +110,6 @@ router.get('/', protect, async (req, res) => { // Convert status string value to Prisma EventStatus enum // Valid values: Planning, ListGeneration, Review, Ready, Complete if (status) { - // 有效值: Planning, ListGeneration, Review, Ready, Complete const validStatuses = ['Planning', 'ListGeneration', 'Review', 'Ready', 'Complete']; if (validStatuses.includes(status)) {