diff --git a/web/src/App.tsx b/web/src/App.tsx index e31eaa51..9d851330 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -66,6 +66,9 @@ const Subdomains = lazy(() => import('@/pages/Subdomains')); const Messaging = lazy(() => import('@/pages/Messaging')); const TaxZekatPage = lazy(() => import('@/pages/finance/TaxZekatPage')); const BankPage = lazy(() => import('@/pages/finance/BankPage')); +const AssemblyPage = lazy(() => import('@/pages/governance/AssemblyPage')); +const JusticePage = lazy(() => import('@/pages/governance/JusticePage')); +const PollsPage = lazy(() => import('@/pages/governance/PollsPage')); // Network pages const Mainnet = lazy(() => import('@/pages/networks/Mainnet')); @@ -230,6 +233,9 @@ function App() { } /> } /> } /> + } /> + } /> + } /> } /> } /> } /> diff --git a/web/src/components/MobileHomeLayout.tsx b/web/src/components/MobileHomeLayout.tsx index 6d085c67..d999d89f 100644 --- a/web/src/components/MobileHomeLayout.tsx +++ b/web/src/components/MobileHomeLayout.tsx @@ -67,12 +67,12 @@ const APP_SECTIONS: AppSection[] = [ borderColor: 'border-l-red-500', apps: [ { title: 'mobile.app.president', icon: '👑', route: '/elections', requiresAuth: true }, - { title: 'mobile.app.assembly', icon: '🏛️', route: '/citizens/government', comingSoon: true }, + { title: 'mobile.app.assembly', icon: '🏛️', route: '/governance/assembly' }, { title: 'mobile.app.vote', icon: '🗳️', route: '/elections', requiresAuth: true }, { title: 'mobile.app.validators', icon: '🛡️', route: '/wallet' }, - { title: 'mobile.app.justice', icon: '⚖️', route: '/citizens/government', comingSoon: true }, + { title: 'mobile.app.justice', icon: '⚖️', route: '/governance/justice' }, { title: 'mobile.app.proposals', icon: '📜', route: '/citizens/government' }, - { title: 'mobile.app.polls', icon: '📊', route: '/citizens/government', comingSoon: true }, + { title: 'mobile.app.polls', icon: '📊', route: '/governance/polls' }, { title: 'mobile.app.identity', icon: '🆔', route: '/identity' }, ], }, diff --git a/web/src/pages/governance/AssemblyPage.tsx b/web/src/pages/governance/AssemblyPage.tsx new file mode 100644 index 00000000..7f599c6c --- /dev/null +++ b/web/src/pages/governance/AssemblyPage.tsx @@ -0,0 +1,132 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +interface Member { + id: string; + name: string; + role: string; + roleKu: string; + emoji: string; + region: string; + since: string; +} + +interface Session { + id: string; + titleKu: string; + title: string; + date: string; + status: 'upcoming' | 'completed' | 'in-session'; + agenda: string; +} + +const MEMBERS: Member[] = [ + { id: '1', name: 'Azad Kurdo', role: 'Speaker', roleKu: 'Serokê Meclîsê', emoji: '👨‍⚖️', region: 'Amed', since: '2025-06' }, + { id: '2', name: 'Rozerin Xan', role: 'Deputy Speaker', roleKu: 'Cîgirê Serokê Meclîsê', emoji: '👩‍⚖️', region: 'Hewler', since: '2025-06' }, + { id: '3', name: 'Serhat Demirtash', role: 'Finance Committee', roleKu: 'Komîteya Darayî', emoji: '👨‍💼', region: 'Diyarbekir', since: '2025-08' }, + { id: '4', name: 'Jîn Bakir', role: 'Technology Committee', roleKu: 'Komîteya Teknolojiyê', emoji: '👩‍💻', region: 'Silêmanî', since: '2025-07' }, + { id: '5', name: 'Kawa Zana', role: 'Education Committee', roleKu: 'Komîteya Perwerdê', emoji: '👨‍🎓', region: 'Wan', since: '2025-09' }, + { id: '6', name: 'Berfîn Shêx', role: 'Social Affairs', roleKu: 'Karên Civakî', emoji: '👩‍🏫', region: 'Kerkûk', since: '2025-08' }, + { id: '7', name: 'Dilovan Ehmed', role: 'Foreign Relations', roleKu: 'Têkiliyên Derve', emoji: '🧑‍💼', region: 'Qamişlo', since: '2025-10' }, +]; + +const SESSIONS: Session[] = [ + { id: 's1', titleKu: 'Civîna Budceya Q2 2026', title: 'Q2 2026 Budget Session', date: '2026-04-15', status: 'upcoming', agenda: 'Treasury allocation, staking rewards adjustment, education fund.' }, + { id: 's2', titleKu: 'Civîna Yasadanînê #12', title: 'Legislative Session #12', date: '2026-04-10', status: 'upcoming', agenda: 'Cross-chain bridge proposal, fee structure revision.' }, + { id: 's3', titleKu: 'Civîna Awarte ya Ewlehiyê', title: 'Emergency Security Session', date: '2026-03-28', status: 'completed', agenda: 'Network security audit results, validator requirements update.' }, + { id: 's4', titleKu: 'Civîna Yasadanînê #11', title: 'Legislative Session #11', date: '2026-03-15', status: 'completed', agenda: 'Citizenship criteria, NFT standards, community grants.' }, +]; + +const STATUS_CONFIG = { + upcoming: { label: 'Tê / Upcoming', cls: 'bg-blue-900/50 text-blue-400' }, + 'in-session': { label: 'Niha / In Session', cls: 'bg-green-900/50 text-green-400' }, + completed: { label: 'Qediya / Completed', cls: 'bg-gray-800 text-gray-400' }, +}; + +type Tab = 'members' | 'sessions'; + +export default function AssemblyPage() { + const navigate = useNavigate(); + const [activeTab, setActiveTab] = useState('members'); + + return ( +
+ {/* Header */} +
+
+ + Governance +
+
+ 🏛️ +

Meclîsa Kurdistanê

+

Kurdistan Digital Assembly

+
+
+ {[ + { val: MEMBERS.length, label: 'Endam / Members' }, + { val: 4, label: 'Komîte / Committees' }, + { val: 12, label: 'Civîn / Sessions' }, + ].map((stat, i) => ( +
0 ? 'border-l border-white/20' : ''}`}> +

{stat.val}

+

{stat.label}

+
+ ))} +
+
+ + {/* Tabs */} +
+
+ {(['members', 'sessions'] as Tab[]).map(tab => ( + + ))} +
+
+ +
+ {activeTab === 'members' && MEMBERS.map(m => ( +
+ {m.emoji} +
+

{m.name}

+

{m.roleKu}

+

{m.role}

+
+ 📍 {m.region} + Ji {m.since} +
+
+
+ ))} + + {activeTab === 'sessions' && SESSIONS.map(s => { + const cfg = STATUS_CONFIG[s.status]; + return ( +
+
+ + {cfg.label} + + {s.date} +
+

{s.titleKu}

+

{s.title}

+

{s.agenda}

+
+ ); + })} +
+
+
+ ); +} diff --git a/web/src/pages/governance/JusticePage.tsx b/web/src/pages/governance/JusticePage.tsx new file mode 100644 index 00000000..268dc2f7 --- /dev/null +++ b/web/src/pages/governance/JusticePage.tsx @@ -0,0 +1,151 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +interface DisputeCase { + id: string; + caseNumber: string; + titleKu: string; + title: string; + description: string; + status: 'open' | 'in-review' | 'resolved'; + category: string; + filedDate: string; + resolvedDate?: string; + resolution?: string; +} + +const CASES: DisputeCase[] = [ + { + id: '1', caseNumber: 'DKR-2026-001', + titleKu: 'Nakokiya Dravdana Token', title: 'Token Transaction Dispute', + description: 'Dispute over a failed transaction of 500 HEZ between two parties. Sender claims tokens were deducted but receiver did not receive them.', + status: 'open', category: 'Transaction', filedDate: '2026-04-02', + }, + { + id: '2', caseNumber: 'DKR-2026-002', + titleKu: 'Binpêkirina Peymana Zîrek', title: 'Smart Contract Violation', + description: 'A DeFi protocol allegedly failed to distribute staking rewards as specified in its smart contract terms.', + status: 'in-review', category: 'Smart Contract', filedDate: '2026-03-28', + }, + { + id: '3', caseNumber: 'DKR-2025-047', + titleKu: 'Destavêtina Nasnameya Dijîtal', title: 'Digital Identity Fraud', + description: 'A citizen reported unauthorized use of their digital identity credentials to access governance voting.', + status: 'resolved', category: 'Identity', filedDate: '2026-02-15', + resolvedDate: '2026-03-10', + resolution: 'Identity credentials were revoked and reissued. Fraudulent votes were invalidated. Perpetrator account suspended.', + }, + { + id: '4', caseNumber: 'DKR-2025-039', + titleKu: 'Nakokiya NFT ya Milkiyetê', title: 'NFT Ownership Dispute', + description: 'Two parties claim ownership of the same NFT certificate. Investigation revealed a minting error in the original smart contract.', + status: 'resolved', category: 'NFT / Ownership', filedDate: '2026-01-20', + resolvedDate: '2026-02-28', + resolution: 'Both parties received compensatory NFTs. Smart contract was patched to prevent duplicate minting.', + }, +]; + +const STATUS_CONFIG = { + 'open': { label: 'Vekirî / Open', cls: 'bg-red-900/50 text-red-400', dot: 'bg-red-400' }, + 'in-review': { label: 'Di lêkolînê de / In Review', cls: 'bg-yellow-900/50 text-yellow-400', dot: 'bg-yellow-400' }, + 'resolved': { label: 'Çareserkirî / Resolved', cls: 'bg-green-900/50 text-green-400', dot: 'bg-green-400' }, +}; + +export default function JusticePage() { + const navigate = useNavigate(); + const [expanded, setExpanded] = useState(null); + + const counts = { + open: CASES.filter(c => c.status === 'open').length, + 'in-review': CASES.filter(c => c.status === 'in-review').length, + resolved: CASES.filter(c => c.status === 'resolved').length, + }; + + return ( +
+ {/* Header */} +
+
+ + Governance +
+
+ ⚖️ +

Dadwerî

+

Justice & Dispute Resolution

+
+
+ +
+ {/* Stats */} +
+ {[ + { label: 'Vekirî\nOpen', val: counts['open'], color: 'border-l-red-500' }, + { label: 'Lêkolîn\nIn Review', val: counts['in-review'], color: 'border-l-yellow-500' }, + { label: 'Çareser\nResolved', val: counts['resolved'], color: 'border-l-green-500' }, + ].map((s, i) => ( +
+

{s.val}

+

{s.label}

+
+ ))} +
+ + {/* Info */} +
+

Çareserkirina Nakokiyan / Dispute Resolution

+

+ Sîstema dadweriya dijîtal a Kurdistanê nakokiyên di navbera welatiyên dijîtal de bi awayekî adil û zelal çareser dike. Hemû biryar li ser blockchain tên tomarkirin. +

+

+ Kurdistan's digital justice system resolves disputes between digital citizens fairly and transparently. All decisions are recorded on the blockchain. +

+
+ + {/* Cases */} +

Dozên Dawî / Recent Cases

+ {CASES.map(c => { + const cfg = STATUS_CONFIG[c.status]; + const isOpen = expanded === c.id; + return ( +
+
+ {c.caseNumber} + + {cfg.label} + +
+

{c.titleKu}

+

{c.title}

+
+ 📁 {c.category} + 📅 {c.filedDate} +
+ + {isOpen && ( +
+

{c.description}

+ {c.resolution && ( +
+

Biryar / Resolution:

+

{c.resolution}

+

Dîroka çareseriyê: {c.resolvedDate}

+
+ )} +
+ )} + + +
+ ); + })} +
+
+
+ ); +} diff --git a/web/src/pages/governance/PollsPage.tsx b/web/src/pages/governance/PollsPage.tsx new file mode 100644 index 00000000..6b79159d --- /dev/null +++ b/web/src/pages/governance/PollsPage.tsx @@ -0,0 +1,206 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +interface PollOption { + id: string; + text: string; + votes: number; +} + +interface Poll { + id: string; + title: string; + titleKu: string; + description: string; + status: 'active' | 'ended'; + totalVotes: number; + endsAt: string; + options: PollOption[]; + userVoted: string | null; +} + +const INITIAL_POLLS: Poll[] = [ + { + id: '1', + titleKu: 'Taybetmendiya nû ya paşîn çi be?', + title: 'What should the next new feature be?', + description: 'Vote for the feature you want to see next in PWAP.', + status: 'active', totalVotes: 342, endsAt: '2026-04-20', + options: [ + { id: '1a', text: 'NFT Marketplace', votes: 128 }, + { id: '1b', text: 'DeFi Lending', votes: 97 }, + { id: '1c', text: 'DAO Voting System', votes: 72 }, + { id: '1d', text: 'Cross-chain Bridge', votes: 45 }, + ], + userVoted: null, + }, + { + id: '2', + titleKu: 'Karmasiyonên torê kêm bibin?', + title: 'Should network fees be reduced?', + description: 'Proposal to reduce transaction fees by 50% for the next quarter.', + status: 'active', totalVotes: 521, endsAt: '2026-04-15', + options: [ + { id: '2a', text: 'Ere / Yes', votes: 389 }, + { id: '2b', text: 'Na / No', votes: 87 }, + { id: '2c', text: 'Bêalî / Abstain', votes: 45 }, + ], + userVoted: null, + }, + { + id: '3', + titleKu: 'Bernameya bursê ji bo perwerdehiyê?', + title: 'Scholarship program for education?', + description: 'Allocate 5% of treasury funds for a community education scholarship.', + status: 'active', totalVotes: 198, endsAt: '2026-04-25', + options: [ + { id: '3a', text: 'Ere, 5% / Yes, 5%', votes: 112 }, + { id: '3b', text: 'Ere, 3% / Yes, 3%', votes: 48 }, + { id: '3c', text: 'Na / No', votes: 23 }, + { id: '3d', text: 'Bêalî / Abstain', votes: 15 }, + ], + userVoted: null, + }, + { + id: '4', + titleKu: 'Logoya nû ya PWAP?', + title: 'New PWAP logo design?', + description: 'Community voted on the new logo. Results are final.', + status: 'ended', totalVotes: 876, endsAt: '2026-03-30', + options: [ + { id: '4a', text: 'Design A - Modern', votes: 412 }, + { id: '4b', text: 'Design B - Classic', votes: 298 }, + { id: '4c', text: 'Design C - Minimal', votes: 166 }, + ], + userVoted: '4a', + }, +]; + +export default function PollsPage() { + const navigate = useNavigate(); + const [polls, setPolls] = useState(INITIAL_POLLS); + + const handleVote = (pollId: string, optionId: string) => { + setPolls(prev => + prev.map(poll => { + if (poll.id !== pollId || poll.userVoted) return poll; + return { + ...poll, + totalVotes: poll.totalVotes + 1, + userVoted: optionId, + options: poll.options.map(opt => + opt.id === optionId ? { ...opt, votes: opt.votes + 1 } : opt + ), + }; + }) + ); + }; + + const pct = (votes: number, total: number) => + total === 0 ? 0 : Math.round((votes / total) * 100); + + const activePolls = polls.filter(p => p.status === 'active'); + const endedPolls = polls.filter(p => p.status === 'ended'); + + const renderPoll = (poll: Poll) => { + const showResults = poll.userVoted !== null || poll.status === 'ended'; + const maxVotes = Math.max(...poll.options.map(o => o.votes)); + + return ( +
+
+ + {poll.status === 'active' ? 'Çalak / Active' : 'Qediya / Ended'} + + {poll.totalVotes} deng +
+ +

{poll.titleKu}

+

{poll.title}

+

{poll.description}

+ +
+ {poll.options.map(option => { + const percentage = pct(option.votes, poll.totalVotes); + const isSelected = poll.userVoted === option.id; + const isWinner = poll.status === 'ended' && option.votes === maxVotes; + + return ( + + ); + })} +
+ +

+ {poll.status === 'active' ? `Dawî: ${poll.endsAt}` : `Qediya: ${poll.endsAt}`} +

+
+ ); + }; + + return ( +
+ {/* Header */} +
+
+ + Governance +
+
+ 📊 +

Rapirsî

+

Community Polls

+

{activePolls.length} çalak / active

+
+
+ +
+ {activePolls.length > 0 && ( +
+

Rapirsiyên Çalak / Active Polls

+ {activePolls.map(renderPoll)} +
+ )} + {endedPolls.length > 0 && ( +
+

Rapirsiyên Qediyayî / Ended Polls

+ {endedPolls.map(renderPoll)} +
+ )} +
+
+
+ ); +}