From 582463a3683412c1f85ae2f7eea78f6d947ef991 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Sat, 15 Nov 2025 01:26:24 +0000 Subject: [PATCH] Create ProjectPassport component to display team/project information cgen-65133a1e838e4427bfe6fd7e458f78ab --- .../components/passport/ProjectPassport.tsx | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 client/components/passport/ProjectPassport.tsx diff --git a/client/components/passport/ProjectPassport.tsx b/client/components/passport/ProjectPassport.tsx new file mode 100644 index 00000000..13c9c026 --- /dev/null +++ b/client/components/passport/ProjectPassport.tsx @@ -0,0 +1,215 @@ +import { ExternalLink, Users, Calendar, Badge } from "lucide-react"; +import Layout from "@/components/Layout"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; +import { Link } from "react-router-dom"; + +interface ProjectOwner { + id: string; + username: string; + full_name: string; + avatar_url: string | null; +} + +interface Project { + id: string; + title: string; + slug: string; + description: string | null; + user_id: string; + created_at: string; + updated_at: string; + status?: string; + image_url?: string; + website?: string; +} + +interface ProjectPassportProps { + project: Project; + owner?: ProjectOwner; + isSubdomain?: boolean; +} + +const ProjectPassport = ({ + project, + owner, + isSubdomain = false, +}: ProjectPassportProps) => { + const formatDate = (dateString: string | null | undefined) => { + if (!dateString) return "Recently"; + try { + return new Date(dateString).toLocaleDateString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + }); + } catch { + return "Recently"; + } + }; + + const statusColors: Record = { + draft: "bg-gray-500/10 text-gray-300 border-gray-500/30", + active: "bg-emerald-500/10 text-emerald-300 border-emerald-500/30", + archived: "bg-slate-500/10 text-slate-300 border-slate-500/30", + planning: "bg-blue-500/10 text-blue-300 border-blue-500/30", + "in-progress": "bg-yellow-500/10 text-yellow-300 border-yellow-500/30", + completed: "bg-emerald-500/10 text-emerald-300 border-emerald-500/30", + }; + + const statusLabel = project.status || "active"; + const statusClass = + statusColors[statusLabel] || statusColors["active"]; + + return ( + +
+ {/* Hero Section */} + {project.image_url && ( +
+ {project.title} +
+ )} + + {/* Project Header */} +
+
+

+ {project.title} +

+
+ + {statusLabel} + + {isSubdomain && ( + + 🌐 Public Portfolio + + )} +
+
+ + {project.description && ( +

+ {project.description} +

+ )} +
+ + {/* Owner Card */} + {owner && ( + + + Project Lead + + +
+
+ + + {owner.username[0]} + +
+

+ {owner.full_name} +

+

+ @{owner.username} +

+
+
+ + + +
+
+
+ )} + + {/* Metadata Cards */} +
+ + +
+
+ + Created +
+

+ {formatDate(project.created_at)} +

+
+
+
+ + {project.website && ( + + +
+
+ + Website +
+ + Visit Project + +
+
+
+ )} +
+ + {/* Call to Action */} + + +
+
+ +

+ Get Involved +

+
+

+ Interested in this project? Explore opportunities, connect with + the team, and contribute to the AeThex ecosystem. +

+
+ + + + + + +
+
+
+
+
+
+ ); +}; + +export default ProjectPassport;