Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions ui/src/app/admin/login/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<AuthLayout>
<Center style={{ minHeight: '100vh', padding: '20px' }}>
<AuthCard>
<Stack gap="md">
{/* Admin Badge */}
<Group justify="center" mb="sm">
<Badge
size="lg"
variant="filled"
color="blue"
leftSection={<IconShield size={16} />}
styles={{
root: {
textTransform: 'none',
},
}}
>
Admin Access
</Badge>
</Group>

<Title order={2} ta="center" fw={600} c="dark">
Admin Sign In
</Title>

<Text size="sm" c="dimmed" ta="center">
Enter your admin credentials to access the management panel.
</Text>

<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack gap="lg">
<TextInput
label="Admin Email"
placeholder="admin@socialcap.com"
size="md"
radius="md"
{...form.getInputProps('email')}
styles={{
input: {
backgroundColor: 'rgba(248, 249, 250, 0.8)',
border: '1px solid #e9ecef',
'&:focus': {
borderColor: '#dc2626',
},
},
}}
/>

<PasswordInput
label="Password"
placeholder="Enter your admin password"
size="md"
radius="md"
{...form.getInputProps('password')}
styles={{
input: {
backgroundColor: 'rgba(248, 249, 250, 0.8)',
border: '1px solid #e9ecef',
'&:focus': {
borderColor: '#dc2626',
},
},
}}
/>

<Button
type="submit"
size="md"
radius="md"
loading={isLoading}
style={{
background: 'var(--mantine-color-blue-7)',
border: 'none',
}}
fullWidth
>
{isLoading ? 'Authenticating...' : 'Sign In to Admin Panel'}
</Button>

<Group justify="center" gap="xs">
<Text size="sm" c="dimmed">
Not an admin?
</Text>
<Link
href="/login"
style={{
color: 'var(--mantine-color-blue-8)',
textDecoration: 'none',
fontWeight: 500
}}
>
Regular Sign In
</Link>
</Group>

{/* Security Notice */}
<Text size="xs" c="dimmed" ta="center" mt="md" style={{
padding: '8px 12px',
backgroundColor: 'rgba(220, 38, 38, 0.1)',
borderRadius: '8px',
border: '1px solid rgba(220, 38, 38, 0.2)'
}}>
⚠️ This is a secure admin area. All activities are logged and monitored.
</Text>
</Stack>
</form>
</Stack>
</AuthCard>
</Center>
</AuthLayout>
);
}
16 changes: 14 additions & 2 deletions ui/src/app/admin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
53 changes: 10 additions & 43 deletions ui/src/app/admin/proposals/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';

// import { MockProposal } from '@/components/MockProposal';
import { AdminLayout } from '../../../components/AdminLayout';
import { useRouter } from 'next/navigation';
import {
Expand All @@ -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');
Expand All @@ -68,9 +35,9 @@ export default function AdminProposalsPage() {
Proposals Management
</Title>
<Group gap="md">
<Badge size="lg" variant="filled" color="orange">
{pendingCount} pending review
</Badge>
{/* <Badge size="lg" variant="filled" color="var(--mantine-color-green-9)">
{pendingCount} in progress
</Badge> */}
<Button
leftSection={<IconPlus size={16} />}
onClick={handleCreateProposal}
Expand All @@ -84,7 +51,7 @@ export default function AdminProposalsPage() {

<Stack gap="md">
{mockProposals.map((proposal) => (
<ProposalCard proposal={proposal} key={proposal.id} />
<ProposalCard proposal={proposal} key={proposal.id} isAdmin={true} />
))}
</Stack>

Expand Down
Empty file added ui/src/app/claims/page.tsx
Empty file.
17 changes: 17 additions & 0 deletions ui/src/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,23 @@ export default function LoginPage() {
Sign up!
</Link>
</Group>

<Group justify="center" gap="xs" mt="sm">
<Text size="xs" c="dimmed">
Admin access:
</Text>
<Link
href="/admin/login"
style={{
color: '#dc2626',
textDecoration: 'none',
fontWeight: 500,
fontSize: '12px'
}}
>
Admin Login
</Link>
</Group>
</Stack>
</form>
</Stack>
Expand Down
34 changes: 25 additions & 9 deletions ui/src/app/proposals/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<DashboardLayout activeTab="proposals" username="User">
<Container size="lg">
<Group mb="lg" justify="space-between">
<Title order={1} size="h2">
My Proposals
Community Proposals
</Title>
<Badge size="lg" variant="outline" color="green">
0 proposals
<Badge size="lg" variant="outline" color="blue">
{activeProposals.length} active proposals
</Badge>
</Group>

<Paper p="xl" shadow="sm" radius="md">
<Text c="dimmed" ta="center" size="lg">
No claims submitted yet. Your claim history will appear here.
</Text>
</Paper>
<Stack gap="md">
{activeProposals.map((proposal) => (
<ProposalCard proposal={proposal} key={proposal.id} isAdmin={false} />
))}
</Stack>

{activeProposals.length === 0 && (
<Paper p="xl" shadow="sm" radius="md">
<Text c="dimmed" ta="center" size="lg">
No active proposals at the moment. Check back later for new community proposals.
</Text>
</Paper>
)}
</Container>
</DashboardLayout>
);
Expand Down
10 changes: 5 additions & 5 deletions ui/src/components/AdminLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}}
Expand All @@ -177,12 +177,12 @@ export function AdminLayout({ children, activeTab, username = 'Admin' }: AdminLa
<Group gap="sm">
<Icon
size={20}
color={isActive ? '#ffc107' : '#6c757d'}
color={isActive ? 'var(--mantine-color-blue-6)' : '#6c757d'}
/>
<Text
size="sm"
fw={isActive ? 600 : 400}
c={isActive ? '#ffc107' : 'dark'}
c={isActive ? 'var(--mantine-color-blue-6)' : 'dark'}
>
{item.label}
</Text>
Expand All @@ -191,7 +191,7 @@ export function AdminLayout({ children, activeTab, username = 'Admin' }: AdminLa
<Badge
size="sm"
variant="filled"
color={item.key === 'activity' ? 'orange' : 'gray'}
color={item.key === 'activity' ? 'blue' : 'gray'}
styles={{
root: {
minWidth: '20px',
Expand Down
Loading