Files
HOA_Financial_Platform/frontend/src/pages/auth/LoginPage.tsx
olsch01 a32d4cc179 Add comprehensive platform administration panel
- Database: Add login_history, ai_recommendation_log tables; is_platform_owner
  column on users; subscription fields on organizations (payment_date,
  confirmation_number, renewal_date)
- Backend: New AdminAnalyticsService with platform metrics, tenant detail, and
  health score calculations (0-100 based on activity, budget, transactions,
  members, AI usage)
- Backend: Login/org-switch now records to login_history; AI recommendations
  logged to ai_recommendation_log; platform owner protected from superadmin toggle
- Frontend: 4-tab admin panel (Dashboard, Organizations, Users, Tenant Health)
  with tenant detail drawer, subscription management, health scoring visualization
- Platform owner account (admin@hoaledgeriq.com) auto-redirects to admin panel
- Seed data includes platform owner account and sample login history

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 08:51:39 -05:00

98 lines
2.8 KiB
TypeScript

import { useState } from 'react';
import {
Center,
Container,
Paper,
Text,
TextInput,
PasswordInput,
Button,
Anchor,
Stack,
Alert,
} from '@mantine/core';
import { useForm } from '@mantine/form';
import { IconAlertCircle } from '@tabler/icons-react';
import { useNavigate, Link } from 'react-router-dom';
import api from '../../services/api';
import { useAuthStore } from '../../stores/authStore';
import logoSrc from '../../assets/logo.svg';
export function LoginPage() {
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const navigate = useNavigate();
const setAuth = useAuthStore((s) => s.setAuth);
const form = useForm({
initialValues: { email: '', password: '' },
validate: {
email: (v) => (/^\S+@\S+$/.test(v) ? null : 'Invalid email'),
password: (v) => (v.length >= 1 ? null : 'Password required'),
},
});
const handleSubmit = async (values: typeof form.values) => {
setLoading(true);
setError('');
try {
const { data } = await api.post('/auth/login', values);
setAuth(data.accessToken, data.user, data.organizations);
// Platform owner / superadmin with no orgs → admin panel
if (data.user?.isSuperadmin && data.organizations.length === 0) {
navigate('/admin');
} else if (data.organizations.length >= 1) {
// Always go through org selection to ensure correct JWT with orgSchema
navigate('/select-org');
} else {
navigate('/');
}
} catch (err: any) {
setError(err.response?.data?.message || 'Login failed');
} finally {
setLoading(false);
}
};
return (
<Container size={420} my={80}>
<Center>
<img src={logoSrc} alt="HOA LedgerIQ" style={{ height: 60 }} />
</Center>
<Text c="dimmed" size="sm" ta="center" mt={5}>
Don&apos;t have an account?{' '}
<Anchor component={Link} to="/register" size="sm">
Register
</Anchor>
</Text>
<Paper withBorder shadow="md" p={30} mt={30} radius="md">
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack>
{error && (
<Alert icon={<IconAlertCircle size={16} />} color="red" variant="light">
{error}
</Alert>
)}
<TextInput
label="Email"
placeholder="your@email.com"
required
{...form.getInputProps('email')}
/>
<PasswordInput
label="Password"
placeholder="Your password"
required
{...form.getInputProps('password')}
/>
<Button type="submit" fullWidth loading={loading}>
Sign in
</Button>
</Stack>
</form>
</Paper>
</Container>
);
}