this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 107 lines 3.6 kB view raw
1import { FileCode, Folder, Wrench } from 'lucide-react'; 2import React, { useMemo } from 'react'; 3 4interface SessionDetail { 5 sessionId: string; 6 startTime: string; 7 endTime: string; 8 shortSummary: string; 9 accomplishments: string[]; 10 filesChanged: string[]; 11 toolsUsed: string[]; 12} 13 14interface ProjectDetail { 15 name: string; 16 path: string; 17 sessions: SessionDetail[]; 18} 19 20interface Props { 21 project: ProjectDetail; 22 isNew?: boolean; 23} 24 25export default function ProjectCard({ project, isNew }: Props) { 26 const aggregated = useMemo(() => { 27 const allAccomplishments: string[] = []; 28 const allFiles = new Set<string>(); 29 const allTools = new Set<string>(); 30 31 for (const session of project.sessions) { 32 allAccomplishments.push(...session.accomplishments); 33 session.filesChanged.forEach((f) => allFiles.add(f)); 34 session.toolsUsed.forEach((t) => allTools.add(t)); 35 } 36 37 // Dedupe accomplishments (rough - exact match only) 38 const uniqueAccomplishments = [...new Set(allAccomplishments)]; 39 40 return { 41 accomplishments: uniqueAccomplishments, 42 files: [...allFiles], 43 tools: [...allTools], 44 }; 45 }, [project.sessions]); 46 47 return ( 48 <div className="bg-white rounded-lg border border-gray-200 shadow-sm overflow-hidden p-3"> 49 <div className="flex items-center gap-2 mb-2"> 50 <div className="p-1.5 bg-blue-100 text-blue-600 rounded"> 51 <Folder size={16} /> 52 </div> 53 <h3 className="text-sm font-bold text-slate-800">{project.name}</h3> 54 {isNew === true && ( 55 <span className="px-1.5 py-0.5 text-xs font-medium bg-green-100 text-green-700 rounded">NEW</span> 56 )} 57 </div> 58 59 {aggregated.accomplishments.length > 0 && ( 60 <ul className="space-y-1 mb-3"> 61 {aggregated.accomplishments.map((acc, i) => ( 62 <li key={i} className="flex items-start gap-1.5 text-slate-600 text-sm"> 63 <span className="mt-1.5 w-1 h-1 rounded-full bg-blue-400 shrink-0" /> 64 <span>{acc}</span> 65 </li> 66 ))} 67 </ul> 68 )} 69 70 {aggregated.files.length > 0 && ( 71 <div className="pt-2 border-t border-gray-100"> 72 <span className="text-xs font-semibold text-slate-400 uppercase tracking-wider flex items-center gap-1 mb-1.5"> 73 <FileCode size={12} /> Files 74 </span> 75 <div className="flex flex-wrap gap-1.5"> 76 {aggregated.files.slice(0, 8).map((f, i) => ( 77 <span 78 key={i} 79 className="text-xs px-2 py-1 bg-slate-50 text-slate-600 rounded border border-slate-100 font-mono" 80 > 81 {f.split('/').pop()} 82 </span> 83 ))} 84 {aggregated.files.length > 8 && ( 85 <span className="text-xs px-2 py-1 text-slate-400">+{aggregated.files.length - 8} more</span> 86 )} 87 </div> 88 </div> 89 )} 90 91 {aggregated.tools.length > 0 && ( 92 <div className="pt-2 border-t border-gray-100"> 93 <span className="text-xs font-semibold text-slate-400 uppercase tracking-wider flex items-center gap-1 mb-1.5"> 94 <Wrench size={12} /> Tools 95 </span> 96 <div className="flex flex-wrap gap-1.5"> 97 {aggregated.tools.map((tool, i) => ( 98 <span key={i} className="text-xs px-2 py-1 bg-purple-50 text-purple-600 rounded border border-purple-100"> 99 {tool} 100 </span> 101 ))} 102 </div> 103 </div> 104 )} 105 </div> 106 ); 107}