Create PassportSummary component
cgen-18f6b0d790c9472fb7f611149391f9a7
This commit is contained in:
parent
019945863e
commit
190691bdfd
1 changed files with 269 additions and 0 deletions
269
client/components/passport/PassportSummary.tsx
Normal file
269
client/components/passport/PassportSummary.tsx
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { Sparkles, ShieldCheck, Trophy, Users } from "lucide-react";
|
||||
import type {
|
||||
AethexAchievement,
|
||||
AethexUserProfile,
|
||||
} from "@/lib/aethex-database-adapter";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const realmMeta: Record<
|
||||
string,
|
||||
{ label: string; description: string; gradient: string }
|
||||
> = {
|
||||
game_developer: {
|
||||
label: "Development Forge",
|
||||
description: "Building immersive experiences and advanced systems.",
|
||||
gradient: "from-neon-purple to-aethex-500",
|
||||
},
|
||||
client: {
|
||||
label: "Strategist Nexus",
|
||||
description: "Partner in high-impact delivery and technical direction.",
|
||||
gradient: "from-neon-blue to-aethex-400",
|
||||
},
|
||||
community_member: {
|
||||
label: "Innovation Commons",
|
||||
description: "Connecting innovators, researchers, and trailblazers.",
|
||||
gradient: "from-neon-green to-aethex-600",
|
||||
},
|
||||
customer: {
|
||||
label: "Experience Hub",
|
||||
description: "Unlocking AeThex products and premium adventures.",
|
||||
gradient: "from-amber-400 to-aethex-700",
|
||||
},
|
||||
};
|
||||
|
||||
const providerMeta: Record<
|
||||
string,
|
||||
{ label: string; color: string; accent: string }
|
||||
> = {
|
||||
google: {
|
||||
label: "Google",
|
||||
color: "bg-red-500/10 text-red-300",
|
||||
accent: "border-red-500/40",
|
||||
},
|
||||
github: {
|
||||
label: "GitHub",
|
||||
color: "bg-slate-500/10 text-slate-100",
|
||||
accent: "border-slate-500/40",
|
||||
},
|
||||
};
|
||||
|
||||
interface PassportSummaryProps {
|
||||
profile: Partial<AethexUserProfile> & { email?: string | null };
|
||||
achievements: AethexAchievement[];
|
||||
interests?: string[];
|
||||
isSelf: boolean;
|
||||
linkedProviders?: Array<{ provider: string; lastSignInAt?: string }>;
|
||||
}
|
||||
|
||||
const MAX_HERO_ACHIEVEMENTS = 3;
|
||||
|
||||
const PassportSummary = ({
|
||||
profile,
|
||||
achievements,
|
||||
interests,
|
||||
isSelf,
|
||||
linkedProviders,
|
||||
}: PassportSummaryProps) => {
|
||||
const realm = realmMeta[(profile as any)?.user_type || "community_member"];
|
||||
const level = profile.level ?? 1;
|
||||
const totalXp = profile.total_xp ?? 0;
|
||||
const loyaltyPoints = (profile as any)?.loyalty_points ?? 0;
|
||||
const progressToNextLevel = Math.min(((totalXp % 1000) / 1000) * 100, 100);
|
||||
const featureAchievements = achievements.slice(0, MAX_HERO_ACHIEVEMENTS);
|
||||
|
||||
return (
|
||||
<Card className="bg-gradient-to-br from-slate-950/90 via-slate-900/70 to-slate-950/90 border border-slate-800 shadow-2xl">
|
||||
<CardHeader className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
|
||||
<div className="space-y-3">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"w-fit border-aethex-500/50 text-xs uppercase tracking-[0.2em] text-aethex-100",
|
||||
)}
|
||||
>
|
||||
AeThex Passport
|
||||
</Badge>
|
||||
<div className="space-y-2">
|
||||
<CardTitle className="text-3xl font-bold text-white">
|
||||
{profile.full_name || profile.username || "AeThex Explorer"}
|
||||
</CardTitle>
|
||||
{profile.email && (
|
||||
<CardDescription className="text-slate-300">
|
||||
{profile.email}
|
||||
</CardDescription>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Badge
|
||||
className={cn(
|
||||
"text-white shadow-lg",
|
||||
realm?.gradient ? `bg-gradient-to-r ${realm.gradient}` : "bg-aethex-500",
|
||||
)}
|
||||
>
|
||||
{realm?.label || "Innovation Commons"}
|
||||
</Badge>
|
||||
<Badge variant="outline" className="border-slate-600/60 text-slate-200">
|
||||
Level {level}
|
||||
</Badge>
|
||||
<Badge variant="outline" className="border-slate-600/60 text-slate-200">
|
||||
{totalXp} XP
|
||||
</Badge>
|
||||
<Badge variant="outline" className="border-slate-600/60 text-slate-200">
|
||||
{loyaltyPoints} Loyalty
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full max-w-xs space-y-3 rounded-xl bg-slate-900/60 p-4 border border-slate-800">
|
||||
<div className="flex items-center justify-between text-slate-200">
|
||||
<span className="text-sm font-medium uppercase tracking-wider">
|
||||
Progress to Level {level + 1}
|
||||
</span>
|
||||
<span className="text-sm text-slate-300">
|
||||
{(progressToNextLevel || 0).toFixed(0)}%
|
||||
</span>
|
||||
</div>
|
||||
<Progress value={progressToNextLevel} className="h-2" />
|
||||
{realm && (
|
||||
<p className="text-xs text-slate-400 leading-relaxed">
|
||||
{realm.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-8">
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||
<div className="rounded-xl border border-slate-800 bg-slate-900/40 p-4">
|
||||
<div className="flex items-center justify-between text-slate-300">
|
||||
<span className="text-sm font-medium">Realm Alignment</span>
|
||||
<Sparkles className="h-4 w-4 text-aethex-300" />
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-slate-200">
|
||||
{(profile as any)?.user_type?.replace("_", " ") || "community member"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-xl border border-slate-800 bg-slate-900/40 p-4">
|
||||
<div className="flex items-center justify-between text-slate-300">
|
||||
<span className="text-sm font-medium">Projects Completed</span>
|
||||
<Trophy className="h-4 w-4 text-aethex-300" />
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-slate-200">
|
||||
{(profile as any)?.completed_projects ?? 0}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-xl border border-slate-800 bg-slate-900/40 p-4">
|
||||
<div className="flex items-center justify-between text-slate-300">
|
||||
<span className="text-sm font-medium">Community Reach</span>
|
||||
<Users className="h-4 w-4 text-aethex-300" />
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-slate-200">
|
||||
{(profile as any)?.community_reach ?? "Growing"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{featureAchievements.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-semibold text-slate-200 uppercase tracking-wider">
|
||||
Featured Achievements
|
||||
</h3>
|
||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{featureAchievements.map((achievement) => (
|
||||
<div
|
||||
key={achievement.id}
|
||||
className="rounded-xl border border-slate-800 bg-slate-900/40 p-4"
|
||||
>
|
||||
<div className="flex items-center gap-2 text-slate-100">
|
||||
<span className="text-xl">
|
||||
{achievement.icon || "✨"}
|
||||
</span>
|
||||
<span className="text-sm font-medium">
|
||||
{achievement.name}
|
||||
</span>
|
||||
</div>
|
||||
{achievement.description && (
|
||||
<p className="mt-2 text-xs text-slate-400">
|
||||
{achievement.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{interests && interests.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-semibold text-slate-200 uppercase tracking-wider">
|
||||
Interests & Focus Areas
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{interests.map((interest) => (
|
||||
<Badge key={interest} variant="outline" className="border-slate-600/60 text-slate-200">
|
||||
{interest}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{linkedProviders && linkedProviders.length > 0 && (
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-semibold text-slate-200 uppercase tracking-wider">
|
||||
Linked Accounts
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{linkedProviders.map((provider) => {
|
||||
const meta = providerMeta[provider.provider] || {
|
||||
label: provider.provider,
|
||||
color: "bg-slate-500/10 text-slate-100",
|
||||
accent: "border-slate-500/40",
|
||||
};
|
||||
return (
|
||||
<Badge
|
||||
key={provider.provider}
|
||||
variant="outline"
|
||||
className={cn(meta.color, meta.accent, "border text-xs")}
|
||||
>
|
||||
<ShieldCheck className="mr-1 h-3 w-3" />
|
||||
{meta.label}
|
||||
</Badge>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isSelf && (
|
||||
<>
|
||||
<Separator className="border-slate-800" />
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Button asChild variant="outline" className="border-slate-700 text-slate-100">
|
||||
<Link to="/dashboard?tab=profile">Edit Profile</Link>
|
||||
</Button>
|
||||
<Button
|
||||
asChild
|
||||
className="bg-gradient-to-r from-aethex-500 to-neon-blue hover:from-aethex-600 hover:to-neon-blue/90"
|
||||
>
|
||||
<Link to="/dashboard?tab=connections">Manage Passport Links</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default PassportSummary;
|
||||
Loading…
Reference in a new issue