diff --git a/ui/src/app/admin/login/page.tsx b/ui/src/app/admin/login/page.tsx
new file mode 100644
index 0000000..e192838
--- /dev/null
+++ b/ui/src/app/admin/login/page.tsx
@@ -0,0 +1,163 @@
+'use client';
+
+import { useState } from 'react';
+import { useRouter } from 'next/navigation';
+import {
+ TextInput,
+ Button,
+ Text,
+ Title,
+ Group,
+ Stack,
+ Center,
+ PasswordInput,
+ Badge
+} from '@mantine/core';
+import { useForm } from '@mantine/form';
+import { AuthLayout } from '../../../components/AuthLayout';
+import { AuthCard } from '../../../components/AuthCard';
+import { IconShield } from '@tabler/icons-react';
+import Link from 'next/link';
+
+export default function AdminLoginPage() {
+ const router = useRouter();
+ const [isLoading, setIsLoading] = useState(false);
+
+ const form = useForm({
+ initialValues: {
+ email: '',
+ password: '',
+ },
+
+ validate: {
+ email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'),
+ password: (value) => (value.length < 6 ? 'Password must be at least 6 characters' : null),
+ },
+ });
+
+ const handleSubmit = async (values: typeof form.values) => {
+ setIsLoading(true);
+ // TODO: Implement backend functionality for admin authentication
+ console.log('Admin login attempt with:', values.email);
+
+ // Simulate API call and redirect to admin panel
+ setTimeout(() => {
+ setIsLoading(false);
+ // Redirect to admin proposals page after successful login
+ router.push('/admin/proposals');
+ }, 1500);
+ };
+
+ return (
+
+
+
+
+ {/* Admin Badge */}
+
+ }
+ styles={{
+ root: {
+ textTransform: 'none',
+ },
+ }}
+ >
+ Admin Access
+
+
+
+
+ Admin Sign In
+
+
+
+ Enter your admin credentials to access the management panel.
+
+
+
+
+
+
+
+ );
+}
diff --git a/ui/src/app/admin/page.tsx b/ui/src/app/admin/page.tsx
index 026ea72..45cf4a6 100644
--- a/ui/src/app/admin/page.tsx
+++ b/ui/src/app/admin/page.tsx
@@ -8,8 +8,20 @@ export default function AdminPage() {
const router = useRouter();
useEffect(() => {
- // Redirect to admin proposals by default
- router.push('/admin/proposals');
+ // Check if admin is authenticated
+ // For now, redirect to admin login
+ // Later you can add authentication check here
+ // If authenticated, redirect to /admin/proposals
+ // If not authenticated, redirect to /admin/login
+
+ // Simulate authentication check
+ const isAdminAuthenticated = false; // Change this based on actual auth state
+
+ if (isAdminAuthenticated) {
+ router.push('/admin/proposals');
+ } else {
+ router.push('/admin/login');
+ }
}, [router]);
return (
diff --git a/ui/src/app/admin/proposals/page.tsx b/ui/src/app/admin/proposals/page.tsx
index c775ca7..2f5772e 100644
--- a/ui/src/app/admin/proposals/page.tsx
+++ b/ui/src/app/admin/proposals/page.tsx
@@ -1,6 +1,5 @@
'use client';
-// import { MockProposal } from '@/components/MockProposal';
import { AdminLayout } from '../../../components/AdminLayout';
import { useRouter } from 'next/navigation';
import {
@@ -14,47 +13,15 @@ import {
Button
} from '@mantine/core';
import { IconPlus } from '@tabler/icons-react';
-import { ProposalCard } from '@/components/ProposalCard';
-
-// TODO: Replace with real data from contract
-const mockProposals = [
- {
- id: 1,
- title: 'Community Garden Initiative',
- author: 'Alice Johnson',
- status: 'pending',
- submittedAt: '2024-08-20',
- description: 'Proposal to establish a community garden in the downtown area to promote sustainable living and community engagement.',
- },
- {
- id: 2,
- title: 'Youth Coding Bootcamp',
- author: 'Bob Smith',
- status: 'pending',
- submittedAt: '2024-08-19',
- description: 'Free coding bootcamp for underprivileged youth aged 14-18 to provide them with valuable tech skills.',
- },
- {
- id: 3,
- title: 'Local Art Festival',
- author: 'Carol Davis',
- status: 'approved',
- submittedAt: '2024-08-15',
- description: 'Annual art festival showcasing local artists and promoting cultural activities in the community.',
- },
- {
- id: 4,
- title: 'Senior Citizens Support Program',
- author: 'David Wilson',
- status: 'pending',
- submittedAt: '2024-08-18',
- description: 'Support program providing assistance and social activities for senior citizens in the community.',
- },
-];
+import { ProposalCard } from '../../../components/ProposalCard';
+import { mockProposals } from '../../../utils/mockData';
export default function AdminProposalsPage() {
const router = useRouter();
- const pendingCount = mockProposals.filter(p => p.status === 'pending').length;
+ const pendingCount = mockProposals.filter(p => {
+ const now = new Date();
+ return now >= p.startDate && now <= p.endDate;
+ }).length;
const handleCreateProposal = () => {
router.push('/admin/proposals/new');
@@ -68,9 +35,9 @@ export default function AdminProposalsPage() {
Proposals Management
-
- {pendingCount} pending review
-
+ {/*
+ {pendingCount} in progress
+ */}
}
onClick={handleCreateProposal}
@@ -84,7 +51,7 @@ export default function AdminProposalsPage() {
{mockProposals.map((proposal) => (
-
+
))}
diff --git a/ui/src/app/claims/page.tsx b/ui/src/app/claims/page.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/ui/src/app/login/page.tsx b/ui/src/app/login/page.tsx
index d014c14..9d5f70d 100644
--- a/ui/src/app/login/page.tsx
+++ b/ui/src/app/login/page.tsx
@@ -95,6 +95,23 @@ export default function LoginPage() {
Sign up!
+
+
+
+ Admin access:
+
+
+ Admin Login
+
+
diff --git a/ui/src/app/proposals/page.tsx b/ui/src/app/proposals/page.tsx
index 6a82f8a..4f6c0ff 100644
--- a/ui/src/app/proposals/page.tsx
+++ b/ui/src/app/proposals/page.tsx
@@ -1,26 +1,42 @@
'use client';
import { DashboardLayout } from '../../components/DashboardLayout';
-import { Container, Title, Text, Paper, Group, Badge } from '@mantine/core';
+import { Container, Title, Text, Paper, Group, Badge, Stack } from '@mantine/core';
+import { ProposalCard } from '../../components/ProposalCard';
+import { mockProposals } from '../../utils/mockData';
export default function ProposalsPage() {
+ // Filter proposals that are active (not finished)
+ const activeProposals = mockProposals.filter(proposal => {
+ const now = new Date();
+ return now <= proposal.endDate;
+ });
+
return (
- My Proposals
+ Community Proposals
-
- 0 proposals
+
+ {activeProposals.length} active proposals
-
-
- No claims submitted yet. Your claim history will appear here.
-
-
+
+ {activeProposals.map((proposal) => (
+
+ ))}
+
+
+ {activeProposals.length === 0 && (
+
+
+ No active proposals at the moment. Check back later for new community proposals.
+
+
+ )}
);
diff --git a/ui/src/components/AdminLayout.tsx b/ui/src/components/AdminLayout.tsx
index 30368f2..cf8f2be 100644
--- a/ui/src/components/AdminLayout.tsx
+++ b/ui/src/components/AdminLayout.tsx
@@ -157,8 +157,8 @@ export function AdminLayout({ children, activeTab, username = 'Admin' }: AdminLa
padding: '12px 16px',
borderRadius: '8px',
marginBottom: '4px',
- backgroundColor: isActive ? '#fff3cd' : 'transparent',
- border: isActive ? '1px solid #ffc107' : '1px solid transparent',
+ backgroundColor: isActive ? 'var(--mantine-color-blue-0)' : 'transparent',
+ border: isActive ? '1px solid var(--mantine-color-blue-6)' : '1px solid transparent',
cursor: 'pointer',
transition: 'all 0.2s ease',
}}
@@ -177,12 +177,12 @@ export function AdminLayout({ children, activeTab, username = 'Admin' }: AdminLa
{item.label}
@@ -191,7 +191,7 @@ export function AdminLayout({ children, activeTab, username = 'Admin' }: AdminLa
{
- console.log(`Approving proposal ${id}`);
- // TODO: Implement approval logic
+export function ProposalCard({ proposal, isAdmin = false }: ProposalCardProps) {
+ const handleViewDetails = (id: number) => {
+ console.log(`Viewing proposal details ${id}`);
+ // TODO: Implement view details navigation
+ };
+
+ const handleParticipate = (id: number) => {
+ console.log(`Participating in proposal ${id}`);
+ // TODO: Implement participation logic
};
- const handleReject = (id: number) => {
- console.log(`Rejecting proposal ${id}`);
- // TODO: Implement rejection logic
+ const handleReadDocument = (id: number) => {
+ console.log(`Reading document for proposal ${id}`);
+ // TODO: Implement document reading
};
- const handleView = (id: number) => {
- console.log(`Viewing proposal ${id}`);
- // TODO: Implement view logic
+ const getDateStatus = (startDate: Date, endDate: Date) => {
+ const now = new Date();
+ if (now < startDate) {
+ return 'pending';
+ } else if (now >= startDate && now <= endDate) {
+ return 'inProgress';
+ } else {
+ return 'finished';
+ }
};
- const getStatusColor = (status: string) => {
+ const getDateStatusColor = (status: string) => {
switch (status) {
case 'pending':
return 'orange';
case 'inProgress':
- return 'blue';
+ return 'var(--mantine-color-blue-6)';
+ case 'finished':
+ return 'gray';
default:
return 'gray';
}
};
- const getStatusIcon = (status: string) => {
+ const getDateStatusIcon = (status: string) => {
switch (status) {
case 'pending':
return ;
+ case 'inProgress':
+ return ;
+ case 'finished':
+ return ;
+ default:
+ return null;
+ }
+ };
+
+ const getProposalStatusColor = (status: string) => {
+ switch (status) {
+ case 'approved':
+ return 'green';
+ case 'rejected':
+ return 'red';
+ case 'dismissed':
+ return 'gray';
+ default:
+ return 'gray';
+ }
+ };
+
+ const getProposalStatusIcon = (status: string) => {
+ switch (status) {
case 'approved':
return ;
case 'rejected':
return ;
+ case 'dismissed':
+ return ;
default:
return null;
}
};
- return (
+ const dateStatus = getDateStatus(proposal.startDate, proposal.endDate);
+ const participationPercentage = proposal.expectedVoters > 0
+ ? Math.round((proposal.actualVoters / proposal.expectedVoters) * 100)
+ : 0;
+ const formatDate = (date: Date) => {
+ return date.toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ });
+ };
-
-
+
+ {/* Title with Date Status Badge */}
-
+
{proposal.title}
- {proposal.status.charAt(0).toUpperCase() + proposal.status.slice(1)}
+ {dateStatus === 'inProgress' ? 'In Progress' : dateStatus.charAt(0).toUpperCase() + dateStatus.slice(1)}
-
+ {/* Description */}
+
{proposal.description}
-
+ {/* Start and End Dates */}
+
-
-
- {proposal.author}
-
+
+
+
+ Start Date
+
+
+ {formatDate(proposal.startDate)}
+
+
-
-
- {new Date(proposal.submittedAt).toLocaleDateString("es-AR")}
-
+
+
+
+ End Date
+
+
+ {formatDate(proposal.endDate)}
+
+
+ {/* Participation Percentage - Only for Admin View */}
+ {isAdmin && (
+
+
+
+
+ Participation
+
+
+ {proposal.actualVoters} / {proposal.expectedVoters} voters
+
+
+
+ )}
-
-
-
- handleView(proposal.id)}
- >
-
-
-
-
- {proposal.status === 'pending' && (
- <>
-
- handleApprove(proposal.id)}
- >
-
-
-
-
-
-
+
+ Result:
+
+
+ {proposal.status.charAt(0).toUpperCase() + proposal.status.slice(1)}
+
+
+ )}
+
+ {/* Participation Percentage - Only for Admin View */}
+ {/* {isAdmin && (
+
+
+
+
+
+ Participation
+
+
+ {proposal.actualVoters} / {proposal.expectedVoters} voters
+
+
+
+ = 50 ? 'green' : participationPercentage >= 25 ? 'yellow' : 'red'
+ }
+ ]}
+ label={
+
+ {participationPercentage}%
+
+ }
+ />
+
+
+
+ )} */}
+
+ {/* Action Buttons */}
+
+ {isAdmin ? (
+ }
+ onClick={() => handleViewDetails(proposal.id)}
+ >
+ View Details
+
+ ) : (
+ <>
+
+ Read Document
+
+ {dateStatus === 'inProgress' && (
+ }
+ onClick={() => handleParticipate(proposal.id)}
+ color="blue"
+ >
+ Participate
+
+ )}
+ >
+ )}
+
+
- )
+ );
}
\ No newline at end of file
diff --git a/ui/src/utils/mockData.ts b/ui/src/utils/mockData.ts
new file mode 100644
index 0000000..ccc8db2
--- /dev/null
+++ b/ui/src/utils/mockData.ts
@@ -0,0 +1,216 @@
+// Mock data for proposals to be used across the application
+export interface ProposalData {
+ id: number;
+ title: string;
+ author: string;
+ startDate: Date;
+ endDate: Date;
+ status: 'approved' | 'rejected' | 'dismissed' | 'pending';
+ submittedAt: string;
+ description: string;
+ expectedVoters: number;
+ actualVoters: number;
+}
+
+// Helper function to create dates relative to today
+const createDate = (daysOffset: number, hoursOffset: number = 0) => {
+ const date = new Date();
+ date.setDate(date.getDate() + daysOffset);
+ date.setHours(date.getHours() + hoursOffset);
+ return date;
+};
+
+// Mock proposals data
+export const mockProposals: ProposalData[] = [
+ {
+ id: 1,
+ title: 'Community Garden Initiative',
+ author: 'Alice Johnson',
+ startDate: createDate(-5), // Started 5 days ago
+ endDate: createDate(10), // Ends in 10 days
+ status: 'pending',
+ submittedAt: '2024-08-15',
+ description: 'Proposal to establish a community garden in the downtown area to promote sustainable living and community engagement. The garden would include plots for residents, educational workshops, and composting facilities.',
+ expectedVoters: 150,
+ actualVoters: 89,
+ },
+ {
+ id: 2,
+ title: 'Youth Coding Bootcamp Funding',
+ author: 'Bob Smith',
+ startDate: createDate(2), // Starts in 2 days
+ endDate: createDate(15), // Ends in 15 days
+ status: 'pending',
+ submittedAt: '2024-08-18',
+ description: 'Free coding bootcamp for underprivileged youth aged 14-18 to provide them with valuable tech skills. The program includes web development, mobile app creation, and career mentorship.',
+ expectedVoters: 200,
+ actualVoters: 0,
+ },
+ {
+ id: 3,
+ title: 'Annual Local Art Festival',
+ author: 'Carol Davis',
+ startDate: createDate(-20), // Started 20 days ago
+ endDate: createDate(-5), // Ended 5 days ago
+ status: 'approved',
+ submittedAt: '2024-07-25',
+ description: 'Annual art festival showcasing local artists and promoting cultural activities in the community. Features live music, art exhibitions, food vendors, and interactive workshops for all ages.',
+ expectedVoters: 180,
+ actualVoters: 142,
+ },
+ {
+ id: 4,
+ title: 'Senior Citizens Support Program',
+ author: 'David Wilson',
+ startDate: createDate(-10), // Started 10 days ago
+ endDate: createDate(5), // Ends in 5 days
+ status: 'pending',
+ submittedAt: '2024-08-10',
+ description: 'Support program providing assistance and social activities for senior citizens in the community. Includes meal delivery, transportation services, and social meetups.',
+ expectedVoters: 120,
+ actualVoters: 67,
+ },
+ {
+ id: 5,
+ title: 'Public Library Renovation Project',
+ author: 'Emma Thompson',
+ startDate: createDate(-30), // Started 30 days ago
+ endDate: createDate(-10), // Ended 10 days ago
+ status: 'rejected',
+ submittedAt: '2024-07-15',
+ description: 'Comprehensive renovation of the public library including new technology center, expanded reading areas, and improved accessibility features. The project aims to modernize our community library.',
+ expectedVoters: 250,
+ actualVoters: 98,
+ },
+ {
+ id: 6,
+ title: 'Downtown Bike Lane Infrastructure',
+ author: 'Frank Miller',
+ startDate: createDate(7), // Starts in 7 days
+ endDate: createDate(20), // Ends in 20 days
+ status: 'pending',
+ submittedAt: '2024-08-20',
+ description: 'Installation of dedicated bike lanes throughout downtown area to promote sustainable transportation and reduce traffic congestion. Includes bike parking stations and safety improvements.',
+ expectedVoters: 300,
+ actualVoters: 0,
+ },
+ {
+ id: 7,
+ title: 'Community Solar Panel Initiative',
+ author: 'Grace Lee',
+ startDate: createDate(-15), // Started 15 days ago
+ endDate: createDate(-2), // Ended 2 days ago
+ status: 'approved',
+ submittedAt: '2024-08-01',
+ description: 'Community-owned solar panel installation on public buildings to reduce energy costs and promote renewable energy. The project includes educational programs about sustainable energy.',
+ expectedVoters: 220,
+ actualVoters: 187,
+ },
+ {
+ id: 8,
+ title: 'Food Truck Festival Organization',
+ author: 'Henry Garcia',
+ startDate: createDate(-25), // Started 25 days ago
+ endDate: createDate(-15), // Ended 15 days ago
+ status: 'dismissed',
+ submittedAt: '2024-07-20',
+ description: 'Monthly food truck festival in Central Park to support local food businesses and create community gathering opportunities. Features diverse cuisines and live entertainment.',
+ expectedVoters: 160,
+ actualVoters: 45,
+ },
+ {
+ id: 9,
+ title: 'After-School Tutoring Program',
+ author: 'Isabel Rodriguez',
+ startDate: createDate(-3), // Started 3 days ago
+ endDate: createDate(12), // Ends in 12 days
+ status: 'pending',
+ submittedAt: '2024-08-19',
+ description: 'Free after-school tutoring program for K-12 students staffed by volunteer community members and college students. Focuses on math, science, and reading comprehension.',
+ expectedVoters: 140,
+ actualVoters: 78,
+ },
+ {
+ id: 10,
+ title: 'Community Composting Program',
+ author: 'Jack Wilson',
+ startDate: createDate(5), // Starts in 5 days
+ endDate: createDate(18), // Ends in 18 days
+ status: 'pending',
+ submittedAt: '2024-08-21',
+ description: 'Neighborhood composting program with collection points and educational workshops about waste reduction. Includes distribution of compost to community members for gardening.',
+ expectedVoters: 110,
+ actualVoters: 0,
+ },
+ {
+ id: 11,
+ title: 'Outdoor Movie Nights Series',
+ author: 'Kate Brown',
+ startDate: createDate(-40), // Started 40 days ago
+ endDate: createDate(-25), // Ended 25 days ago
+ status: 'approved',
+ submittedAt: '2024-07-05',
+ description: 'Summer outdoor movie screening series in the community park. Family-friendly films with popcorn and refreshment stands. Creates opportunities for neighbors to connect and socialize.',
+ expectedVoters: 190,
+ actualVoters: 156,
+ },
+ {
+ id: 12,
+ title: 'Digital Literacy Classes for Seniors',
+ author: 'Liam Anderson',
+ startDate: createDate(-8), // Started 8 days ago
+ endDate: createDate(7), // Ends in 7 days
+ status: 'pending',
+ submittedAt: '2024-08-12',
+ description: 'Technology education classes specifically designed for senior community members. Covers smartphone usage, online safety, video calling, and basic computer skills.',
+ expectedVoters: 100,
+ actualVoters: 73,
+ }
+];
+
+// Helper functions to filter proposals by status
+export const getPendingProposals = () =>
+ mockProposals.filter(proposal => {
+ const now = new Date();
+ return now >= proposal.startDate && now <= proposal.endDate;
+ });
+
+export const getFinishedProposals = () =>
+ mockProposals.filter(proposal => {
+ const now = new Date();
+ return now > proposal.endDate;
+ });
+
+export const getUpcomingProposals = () =>
+ mockProposals.filter(proposal => {
+ const now = new Date();
+ return now < proposal.startDate;
+ });
+
+export const getProposalsByStatus = (status: ProposalData['status']) =>
+ mockProposals.filter(proposal => proposal.status === status);
+
+// Helper function to get a proposal by ID
+export const getProposalById = (id: number) =>
+ mockProposals.find(proposal => proposal.id === id);
+
+// Statistics helpers
+export const getProposalStats = () => {
+ const total = mockProposals.length;
+ const pending = getPendingProposals().length;
+ const finished = getFinishedProposals().length;
+ const upcoming = getUpcomingProposals().length;
+ const approved = getProposalsByStatus('approved').length;
+ const rejected = getProposalsByStatus('rejected').length;
+ const dismissed = getProposalsByStatus('dismissed').length;
+
+ return {
+ total,
+ pending,
+ finished,
+ upcoming,
+ approved,
+ rejected,
+ dismissed
+ };
+};