this repo has no description
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}