export type MeetingStatus = | 'pending' | 'uploading' | 'transcribing' | 'summarizing' | 'done' | 'failed' export type Meeting = { id: string title: string date: string // ISO duration: number // seconds participants?: string[] status: MeetingStatus progress?: number // 0-100 chunksDone?: number chunksTotal?: number summary?: { keyPoints: string[] todos: { text: string; owner?: string; due?: string }[] decisions: string[] keywords: string[] preview: string } transcript?: Array<{ time: number // seconds speaker: string text: string }> } export const mockMeetings: Meeting[] = [ { id: 'm1', title: '与客户对齐定价', date: '2026-04-13T14:30:00', duration: 4980, participants: ['张三', '李四', '客户王总'], status: 'transcribing', progress: 60, chunksDone: 3, chunksTotal: 5, }, { id: 'm2', title: '团队周会', date: '2026-04-13T10:00:00', duration: 2700, participants: ['张三', '李四', '王五'], status: 'done', summary: { keyPoints: [ 'A 项目完成 80%,进入最后测试阶段', 'B 项目本周进入功能测试', '下周聚焦 A 项目上线准备,需对齐运营', ], todos: [ { text: '整理 A 项目上线 checklist', owner: '张三', due: '周五前' }, { text: '协调运营团队对接会', owner: '李四', due: '明天' }, { text: 'B 项目性能压测报告', owner: '王五' }, ], decisions: [ 'A 项目上线时间定为下周三', 'B 项目延后一周,优先保质量', ], keywords: ['周会', '进度', '上线', 'A项目', 'B项目'], preview: '本周关键进展:A 项目完成 80%,进入最后测试阶段;B 项目本周进入功能测试。下周聚焦 A 项目上线准备,需要协调运营团队对接...', }, }, { id: 'm3', title: '产品 Review', date: '2026-04-12T16:00:00', duration: 3120, participants: ['产品经理', '设计师', '工程团队'], status: 'done', summary: { keyPoints: [ '确定 Q2 产品路线图三个重点方向', '移动端体验优化优先级最高', 'AI 功能纳入 Q3 规划', ], todos: [ { text: '出 Q2 PRD 初稿', owner: '产品经理', due: '下周一' }, { text: '设计 Mobile 重构方案', owner: '设计师' }, ], decisions: ['Q2 聚焦移动端', 'AI 功能推迟到 Q3'], keywords: ['产品', 'Q2', '路线图', '移动端', 'AI'], preview: '讨论 Q2 产品路线图,确定三个重点:1) 移动端体验优化作为最高优先级;2) 后台管理 UI 重构;3) AI 辅助功能纳入 Q3 规划...', }, }, { id: 'm4', title: '架构评审 · API 网关方案', date: '2026-04-11T14:00:00', duration: 5400, participants: ['技术负责人', '后端团队'], status: 'done', summary: { keyPoints: [ '选型确定 Kong + 自研插件方案', 'Rate limiting 复用 Redis 集群', '灰度发布走 Canary', ], todos: [ { text: '写 Kong POC', owner: '小明', due: '下周五' }, { text: '梳理现有 API 清单', owner: '小红' }, ], decisions: ['不走 Envoy,Kong 生态更成熟', '灰度走 Canary 5%/25%/50%/100%'], keywords: ['架构', 'API网关', 'Kong', '灰度', 'Redis'], preview: '今天主要对齐 API 网关的选型方向,在 Kong 和 Envoy 之间做了对比。最终决定 Kong + 自研插件,主要考虑到 Kong 的生态更成熟,团队学习成本更低...', }, }, { id: 'm5', title: '用户访谈 · A 类客户', date: '2026-04-10T15:30:00', duration: 3600, status: 'failed', }, ] export function getMeeting(id: string): Meeting | undefined { return mockMeetings.find((m) => m.id === id) } export function formatDuration(seconds: number): string { const h = Math.floor(seconds / 3600) const m = Math.floor((seconds % 3600) / 60) const s = seconds % 60 if (h > 0) return `${h}h ${m}min` return `${m}min` } export function formatTime(seconds: number): string { const h = Math.floor(seconds / 3600) const m = Math.floor((seconds % 3600) / 60) const s = Math.floor(seconds % 60) if (h > 0) { return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}` } return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}` } export function groupByDate(meetings: Meeting[]): Record { const groups: Record = {} for (const m of meetings) { const day = m.date.slice(0, 10) if (!groups[day]) groups[day] = [] groups[day].push(m) } return groups } export function dateLabel(day: string): string { const today = new Date() const todayStr = today.toISOString().slice(0, 10) const yesterday = new Date(today) yesterday.setDate(today.getDate() - 1) const yesterdayStr = yesterday.toISOString().slice(0, 10) if (day === todayStr) return '今天 · ' + day if (day === yesterdayStr) return '昨天 · ' + day return day } export const mockTranscript = [ { time: 0, speaker: '张三', text: '大家好,今天主要是对齐一下 Q2 的定价方案,pipeline 基本已经 ready 了。' }, { time: 15, speaker: '李四', text: 'OK, 我这边准备了三个档位的方案,分别是 Basic / Pro / Ultra,对应不同的客户规模。' }, { time: 35, speaker: '客户王总', text: '我们比较关心 Pro 档的 SLA 具体怎么定义,还有超出 quota 之后的处理方式。' }, { time: 58, speaker: '李四', text: 'Pro 档 SLA 我们承诺 99.9% 可用性,超出 quota 按标准价格 0.01 per request 计费。' }, { time: 90, speaker: '张三', text: '关于 SLA,我建议我们再明确一下 downtime 的定义,尤其是计划内维护是否算。' }, { time: 110, speaker: '客户王总', text: '同意,计划内维护应该提前通知,并且不计入 SLA。' }, { time: 130, speaker: '李四', text: '好的,这块我回去更新一下合同模板,会议纪要里也会明确写出来。' }, { time: 155, speaker: '张三', text: '另外 Pro 档的折扣,我们能给到什么力度?客户这边期望是 15%。' }, { time: 180, speaker: '客户王总', text: '15% 是我们的底线,毕竟首年采购量会比较大。' }, { time: 205, speaker: '李四', text: '10% 是我们目前政策能给到的上限,15% 需要走特批流程。' }, { time: 230, speaker: '张三', text: '那就走特批,客户关系比单次毛利重要。我今天去找 CEO 签字。' }, { time: 260, speaker: '客户王总', text: '感谢。那就这样,合同草稿我们希望周五之前能收到。' }, { time: 280, speaker: '李四', text: '没问题,周五前一定出。' }, ]