550 lines
25 KiB
TypeScript
Raw Permalink Normal View History

2025-03-25 03:52:30 -04:00
"use client"
import React from "react"
import { useRouter } from "next/navigation"
import { redirect } from "next/navigation"
import { useWalletStore } from "@/lib/wallet-store"
import { useTrustData } from "@/hooks/use-trust-data"
import { InfoCircle, ExternalLink, Shield, LockKeyhole, Award, CoinsStacked } from "@/icons"
import { ArrowRight, CheckCircle, Clock, Edit, MessageSquare, VoteIcon, User, Info } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { ConnectWalletButton } from "@/components/connect-wallet-button"
// Mock data
const mockPerspectives = [
{
id: 1,
title: "Improve bike lane safety",
issue: "Infrastructure",
date: "2024-02-10",
status: "Synthesized",
},
{
id: 2,
title: "Expand library hours",
issue: "Education",
date: "2024-01-25",
status: "Synthesized",
},
{
id: 3,
title: "Community garden proposal",
issue: "Environmental Policy",
date: "2024-01-15",
status: "Pending",
},
]
const mockVotes = [
{
id: 1,
insight: "Increase Park Funding",
vote: "Yes",
date: "2024-02-15",
},
{
id: 2,
insight: "Public Transportation Expansion",
vote: "Yes",
date: "2024-02-10",
},
{
id: 3,
insight: "After-School Programs",
vote: "Yes",
date: "2024-01-28",
},
{
id: 4,
insight: "Bike Lane Network",
vote: "No",
date: "2024-01-20",
},
]
export default function ProfilePage() {
const router = useRouter()
const {
walletConnected,
walletAddress,
username,
isVerified,
country,
citizenshipVerified,
eligibilityVerified,
verificationStatus,
perspectivesSubmittedToday,
connectWallet
} = useWalletStore()
const { trustScore, dailyLimit, trustActions, isLoading } = useTrustData()
// Redirect to login if not connected
if (!walletConnected) {
return (
<div className="flex flex-col items-center justify-center min-h-screen p-6 space-y-8 bg-gray-50">
<div className="p-8 bg-white rounded-lg shadow-md w-full max-w-md">
<h1 className="text-2xl font-bold text-center mb-6">Connect Wallet to View Profile</h1>
<p className="text-gray-600 mb-6 text-center">
Please connect your wallet to access your profile and view your participation metrics.
</p>
<button
onClick={() => connectWallet()}
className="w-full py-3 px-4 bg-blue-600 text-white font-medium rounded-md hover:bg-blue-700 transition duration-200"
>
Connect Wallet
</button>
</div>
</div>
)
}
const getTrustLevel = (score: number) => {
if (score >= 80) return "Gold"
if (score >= 60) return "Silver"
if (score >= 40) return "Bronze"
if (score >= 20) return "Starter"
return "New"
}
const trustLevel = getTrustLevel(trustScore)
const trustColor = {
Gold: "text-yellow-600 bg-yellow-100 border-yellow-300",
Silver: "text-gray-600 bg-gray-100 border-gray-300",
Bronze: "text-amber-700 bg-amber-100 border-amber-300",
Starter: "text-blue-600 bg-blue-100 border-blue-300",
New: "text-green-600 bg-green-100 border-green-300"
}[trustLevel]
const progressPercent = Math.min(100, Math.max(0, trustScore))
// Get the badge style based on verification status
const getVerificationBadge = () => {
if (!isVerified) {
return (
<button
onClick={() => router.push('/profile/verify')}
className="flex items-center px-3 py-1 text-orange-600 bg-orange-100 rounded-full border border-orange-300 hover:bg-orange-200 transition dark:bg-orange-950 dark:border-orange-800 dark:text-orange-400 dark:hover:bg-orange-900"
>
<LockKeyhole className="w-4 h-4 mr-1" />
<span className="text-sm font-medium">Verify Now</span>
</button>
);
}
if (verificationStatus === "Fully Eligible") {
return (
<div className="flex items-center px-3 py-1 text-green-600 bg-green-100 rounded-full border border-green-300 dark:bg-green-950 dark:border-green-800 dark:text-green-400">
<Shield className="w-4 h-4 mr-1" />
<span className="text-sm font-medium">Fully Eligible</span>
</div>
);
}
if (verificationStatus === "Verified" || citizenshipVerified) {
return (
<div className="flex items-center px-3 py-1 text-blue-600 bg-blue-100 rounded-full border border-blue-300 dark:bg-blue-950 dark:border-blue-800 dark:text-blue-400">
<Shield className="w-4 h-4 mr-1" />
<span className="text-sm font-medium">Citizenship Verified</span>
</div>
);
}
return (
<button
onClick={() => router.push('/profile/verify')}
className="flex items-center px-3 py-1 text-orange-600 bg-orange-100 rounded-full border border-orange-300 hover:bg-orange-200 transition dark:bg-orange-950 dark:border-orange-800 dark:text-orange-400 dark:hover:bg-orange-900"
>
<LockKeyhole className="w-4 h-4 mr-1" />
<span className="text-sm font-medium">Verify Now</span>
</button>
);
};
return (
<div className="container mx-auto px-4 py-8 max-w-5xl">
{!isVerified && (
<div className="mb-6 rounded-lg p-4 border border-amber-300 bg-amber-50 dark:bg-amber-950/30 dark:border-amber-800">
<div className="flex items-start">
<Info className="h-5 w-5 text-amber-600 dark:text-amber-400 mt-0.5 flex-shrink-0" />
<div className="ml-3">
<h3 className="text-sm font-medium text-amber-800 dark:text-amber-400">Verify to participate fully</h3>
<p className="mt-1 text-sm text-amber-700 dark:text-amber-500">
Without verification, you're in read-only mode with limited actions. Verify your identity to unlock all features.
</p>
<div className="mt-3">
<Button
variant="outline"
size="sm"
onClick={() => router.push('/profile/verify')}
className="bg-amber-600 text-white hover:bg-amber-700 border-amber-700 dark:bg-amber-800 dark:border-amber-700 dark:hover:bg-amber-700 dark:text-white"
>
Verify Identity
</Button>
</div>
</div>
</div>
</div>
)}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Profile Info Section */}
<div className="lg:col-span-1">
<div className="bg-card rounded-lg shadow-md p-6 mb-6 dark:border dark:border-border">
<div className="flex flex-col items-center mb-6">
<div className="w-24 h-24 bg-muted rounded-full mb-4 flex items-center justify-center text-muted-foreground text-xl">
{username ? username[0].toUpperCase() : "?"}
</div>
<h2 className="text-xl font-bold">{username || "Anonymous User"}</h2>
<p className="text-muted-foreground text-sm mb-2 break-all">{walletAddress}</p>
<div className="flex items-center mt-2">
{getVerificationBadge()}
</div>
{country && (
<div className="mt-3 text-sm text-muted-foreground">
Country: {country}
</div>
)}
</div>
{/* Trust Score Section */}
<div className="mb-6">
<h3 className="text-lg font-semibold mb-3">Trust Score</h3>
<div className="flex items-center mb-2">
<span className="font-medium text-lg">{trustScore}</span>
<span className="text-muted-foreground text-sm ml-1">/ 100</span>
</div>
<div className="w-full bg-muted rounded-full h-2.5">
<div
className="bg-primary h-2.5 rounded-full"
style={{ width: `${progressPercent}%` }}
></div>
</div>
<div className="mt-4">
<div className="flex justify-between text-sm text-muted-foreground mb-2">
<span>Daily Perspective Limit:</span>
<span className="font-medium">{dailyLimit}</span>
</div>
<div className="flex justify-between text-sm text-muted-foreground">
<span>Perspectives Submitted Today:</span>
<span className="font-medium">{perspectivesSubmittedToday}</span>
</div>
</div>
</div>
{/* How to earn trust section for unverified users */}
{!isVerified && (
<div className="border-t pt-4 dark:border-border">
<h3 className="text-lg font-semibold mb-3 flex items-center">
<InfoCircle className="w-4 h-4 mr-1 text-blue-500" />
How to Earn Trust
</h3>
<ul className="space-y-2">
{trustActions.map((action, index) => (
<li key={index} className="flex justify-between text-sm">
<span>{action.action}</span>
<span className="font-medium text-green-600 dark:text-green-400">+{action.points} points</span>
</li>
))}
</ul>
<div className="mt-4 p-3 bg-blue-50 rounded-md text-sm text-blue-700 dark:bg-blue-950 dark:text-blue-400 dark:border dark:border-blue-900">
<p className="flex items-start">
<InfoCircle className="w-4 h-4 mr-1 mt-0.5 flex-shrink-0" />
<span>Higher trust scores increase your daily submission limit. Verified users have unlimited submissions.</span>
</p>
</div>
</div>
)}
{/* Verification status details for verified users */}
{isVerified && (
<div className="border-t pt-4 dark:border-border">
<h3 className="text-lg font-semibold mb-3 flex items-center">
<Shield className="w-4 h-4 mr-1 text-green-500" />
Verification Status
</h3>
<ul className="space-y-2">
<li className="flex items-start text-sm">
<CheckCircle className={`w-4 h-4 mr-2 ${citizenshipVerified ? "text-green-600" : "text-gray-400"} dark:${citizenshipVerified ? "text-green-400" : "text-gray-600"} mt-0.5`} />
<span>Citizenship Verification: {citizenshipVerified ? "Verified" : "Not Verified"}</span>
</li>
<li className="flex items-start text-sm">
<CheckCircle className={`w-4 h-4 mr-2 ${eligibilityVerified ? "text-green-600" : "text-gray-400"} dark:${eligibilityVerified ? "text-green-400" : "text-gray-600"} mt-0.5`} />
<span>Eligibility Verification: {eligibilityVerified ? "Verified" : "Not Verified"}</span>
</li>
</ul>
{!eligibilityVerified && citizenshipVerified && (
<div className="mt-4">
<Button
variant="outline"
size="sm"
onClick={() => router.push('/profile/verify')}
className="w-full"
>
Complete Eligibility Verification
</Button>
</div>
)}
</div>
)}
{/* Privileges for verified users */}
{isVerified && (
<div className="border-t pt-4 dark:border-border">
<h3 className="text-lg font-semibold mb-3 flex items-center">
<Shield className="w-4 h-4 mr-1 text-green-500" />
Verified Privileges
</h3>
<ul className="space-y-2">
<li className="flex items-start text-sm">
<CoinsStacked className="w-4 h-4 mr-2 text-green-600 dark:text-green-400 mt-0.5" />
<span>Unlimited daily perspective submissions</span>
</li>
<li className="flex items-start text-sm">
<CoinsStacked className="w-4 h-4 mr-2 text-green-600 dark:text-green-400 mt-0.5" />
<span>Ability to moderate flagged content</span>
</li>
<li className="flex items-start text-sm">
<CoinsStacked className="w-4 h-4 mr-2 text-green-600 dark:text-green-400 mt-0.5" />
<span>Higher voting power in consensus</span>
</li>
<li className="flex items-start text-sm">
<CoinsStacked className="w-4 h-4 mr-2 text-green-600 dark:text-green-400 mt-0.5" />
<span>Access to verified-only discussions</span>
</li>
</ul>
<button
onClick={() => { }}
className="w-full mt-4 py-2 px-4 bg-gradient-to-r from-green-500 to-green-600 text-white rounded-md text-sm font-medium hover:from-green-600 hover:to-green-700 transition dark:from-green-600 dark:to-green-700 dark:hover:from-green-700 dark:hover:to-green-800"
>
View Moderation Dashboard
</button>
</div>
)}
</div>
</div>
{/* Activity Section */}
<div className="lg:col-span-2">
<div className="bg-card rounded-lg shadow-md p-6 mb-6 dark:border dark:border-border">
<h2 className="text-xl font-bold mb-6">Your Activity</h2>
{/* Tabs */}
<div className="border-b mb-6 dark:border-border">
<ul className="flex flex-wrap -mb-px">
<li className="mr-2">
<a href="#" className="inline-block p-4 text-primary border-b-2 border-primary rounded-t-lg">
My Perspectives
</a>
</li>
<li className="mr-2">
<a href="#" className="inline-block p-4 text-muted-foreground hover:text-foreground border-b-2 border-transparent rounded-t-lg">
My Votes
</a>
</li>
<li className="mr-2">
<a href="#" className="inline-block p-4 text-muted-foreground hover:text-foreground border-b-2 border-transparent rounded-t-lg">
Delegation
</a>
</li>
</ul>
</div>
{/* Perspectives Table */}
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-border">
<thead className="bg-muted">
<tr>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider">
Title
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider">
Date
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider">
Status
</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody className="divide-y divide-border">
{mockPerspectives.map((perspective) => (
<tr key={perspective.id}>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm font-medium">{perspective.title}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<div className="text-sm text-muted-foreground">{perspective.date}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${perspective.status === "Synthesized"
? "bg-green-100 text-green-800 dark:bg-green-950 dark:text-green-400"
: "bg-blue-100 text-blue-800 dark:bg-blue-950 dark:text-blue-400"
}`}>
{perspective.status}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
<a href="#" className="text-primary hover:text-primary/80 flex items-center">
View <ExternalLink className="w-3 h-3 ml-1" />
</a>
</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Call to Action for creating new perspectives */}
<div className="mt-6 p-4 bg-primary/10 rounded-lg border border-primary/20">
<div className="flex items-start">
<div className="flex-shrink-0">
<InfoCircle className="h-5 w-5 text-primary" />
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-foreground dark:text-primary-foreground">Share your perspective</h3>
<div className="mt-2 text-sm text-muted-foreground dark:text-primary-foreground/80">
<p>Have something to say on a current topic? Share your perspective and contribute to the discourse.</p>
</div>
<div className="mt-3">
<a
href="/submit"
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"
>
Submit New Perspective
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/* New section: User Privileges based on verification status */}
<div className="mt-8 bg-card rounded-lg shadow-md p-6 dark:border dark:border-border">
<h2 className="text-xl font-bold mb-6">Platform Privileges</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* Unverified privileges */}
<div className="border rounded-lg p-4 dark:border-border">
<div className="flex items-center mb-3">
<div className="h-8 w-8 rounded-full bg-gray-200 dark:bg-gray-800 flex items-center justify-center mr-3">
<User className="h-4 w-4 text-gray-600 dark:text-gray-400" />
</div>
<h3 className="text-md font-semibold">Unverified User</h3>
</div>
<ul className="space-y-2 text-sm">
<li className="flex items-start">
<CheckCircle className={`w-4 h-4 mr-2 ${!isVerified ? "text-gray-600" : "text-gray-400"} mt-0.5`} />
<span>View public perspectives and insights</span>
</li>
<li className="flex items-start">
<CheckCircle className={`w-4 h-4 mr-2 ${!isVerified ? "text-gray-600" : "text-gray-400"} mt-0.5`} />
<span>Submit {dailyLimit} perspectives per day</span>
</li>
<li className="flex items-start">
<CheckCircle className={`w-4 h-4 mr-2 ${!isVerified ? "text-gray-600" : "text-gray-400"} mt-0.5`} />
<span>Limited voting power (0.5x)</span>
</li>
</ul>
{!isVerified && (
<div className="mt-4">
<Badge variant="outline" className="text-gray-600 dark:text-gray-400 border-gray-400">
Current Status
</Badge>
</div>
)}
</div>
{/* Verified (citizenship only) privileges */}
<div className="border rounded-lg p-4 dark:border-border" style={{
borderColor: citizenshipVerified && !eligibilityVerified ? 'rgb(217, 119, 6)' : undefined,
backgroundColor: citizenshipVerified && !eligibilityVerified ? 'rgba(251, 191, 36, 0.1)' : undefined
}}>
<div className="flex items-center mb-3">
<div className="h-8 w-8 rounded-full bg-amber-200 dark:bg-amber-800 flex items-center justify-center mr-3">
<Shield className="h-4 w-4 text-amber-600 dark:text-amber-400" />
</div>
<h3 className="text-md font-semibold">Verified Citizen</h3>
</div>
<ul className="space-y-2 text-sm">
<li className="flex items-start">
<CheckCircle className={`w-4 h-4 mr-2 ${citizenshipVerified && !eligibilityVerified ? "text-amber-600" : "text-gray-400"} dark:${citizenshipVerified && !eligibilityVerified ? "text-amber-400" : "text-gray-600"} mt-0.5`} />
<span>All unverified privileges</span>
</li>
<li className="flex items-start">
<CheckCircle className={`w-4 h-4 mr-2 ${citizenshipVerified && !eligibilityVerified ? "text-amber-600" : "text-gray-400"} dark:${citizenshipVerified && !eligibilityVerified ? "text-amber-400" : "text-gray-600"} mt-0.5`} />
<span>Submit up to 20 perspectives per day</span>
</li>
<li className="flex items-start">
<CheckCircle className={`w-4 h-4 mr-2 ${citizenshipVerified && !eligibilityVerified ? "text-amber-600" : "text-gray-400"} dark:${citizenshipVerified && !eligibilityVerified ? "text-amber-400" : "text-gray-600"} mt-0.5`} />
<span>Standard voting power (1x)</span>
</li>
</ul>
{citizenshipVerified && !eligibilityVerified && (
<div className="mt-4">
<Badge variant="outline" className="text-amber-700 dark:text-amber-400 border-amber-500">
Current Status
</Badge>
</div>
)}
</div>
{/* Fully Eligible privileges */}
<div className="border rounded-lg p-4 dark:border-border" style={{
borderColor: citizenshipVerified && eligibilityVerified ? 'rgb(22, 163, 74)' : undefined,
backgroundColor: citizenshipVerified && eligibilityVerified ? 'rgba(34, 197, 94, 0.1)' : undefined
}}>
<div className="flex items-center mb-3">
<div className="h-8 w-8 rounded-full bg-green-200 dark:bg-green-800 flex items-center justify-center mr-3">
<Shield className="h-4 w-4 text-green-600 dark:text-green-400" />
</div>
<h3 className="text-md font-semibold">Fully Eligible</h3>
</div>
<ul className="space-y-2 text-sm">
<li className="flex items-start">
<CheckCircle className={`w-4 h-4 mr-2 ${citizenshipVerified && eligibilityVerified ? "text-green-600" : "text-gray-400"} dark:${citizenshipVerified && eligibilityVerified ? "text-green-400" : "text-gray-600"} mt-0.5`} />
<span>All verified privileges</span>
</li>
<li className="flex items-start">
<CheckCircle className={`w-4 h-4 mr-2 ${citizenshipVerified && eligibilityVerified ? "text-green-600" : "text-gray-400"} dark:${citizenshipVerified && eligibilityVerified ? "text-green-400" : "text-gray-600"} mt-0.5`} />
<span>Unlimited perspective submissions</span>
</li>
<li className="flex items-start">
<CheckCircle className={`w-4 h-4 mr-2 ${citizenshipVerified && eligibilityVerified ? "text-green-600" : "text-gray-400"} dark:${citizenshipVerified && eligibilityVerified ? "text-green-400" : "text-gray-600"} mt-0.5`} />
<span>Enhanced voting power (1.5x)</span>
</li>
<li className="flex items-start">
<CheckCircle className={`w-4 h-4 mr-2 ${citizenshipVerified && eligibilityVerified ? "text-green-600" : "text-gray-400"} dark:${citizenshipVerified && eligibilityVerified ? "text-green-400" : "text-gray-600"} mt-0.5`} />
<span>Access to moderation tools</span>
</li>
</ul>
{citizenshipVerified && eligibilityVerified && (
<div className="mt-4">
<Badge variant="outline" className="text-green-700 dark:text-green-400 border-green-500">
Current Status
</Badge>
</div>
)}
</div>
</div>
</div>
</div>
)
}