mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
Copied verbatim from the pre-override branch so the approved screens are recoverable at this exact commit before any wiring changes them. Brings back the richer flows the client signed off: a four-step seafarer registration wizard with bilingual inputs and an Ethiopic date picker, the vessel-owner portal (its own register/login/dashboard), ownership transfer, and the seaman book, certificate, medical and endorsement screens. Six of these pages already call an API; ten are mockups carrying hardcoded data. Both are committed as-is here -- the wiring that follows is a separate commit so the diff shows exactly what changed from what the client approved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
129 lines
3.9 KiB
TypeScript
129 lines
3.9 KiB
TypeScript
import { useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import {
|
|
Alert,
|
|
Anchor,
|
|
Box,
|
|
Button,
|
|
Center,
|
|
Divider,
|
|
Group,
|
|
Paper,
|
|
PasswordInput,
|
|
Stack,
|
|
Text,
|
|
TextInput,
|
|
ThemeIcon,
|
|
Title,
|
|
} from '@mantine/core';
|
|
import {
|
|
IconAnchor,
|
|
IconAlertCircle,
|
|
IconLock,
|
|
IconMail,
|
|
IconShip,
|
|
} from '@tabler/icons-react';
|
|
import { useApiMutation } from '@ema-platform/api';
|
|
import { notify } from '@ema-platform/ui';
|
|
|
|
export function VesselOwnerLoginPage() {
|
|
const navigate = useNavigate();
|
|
const [email, setEmail] = useState('');
|
|
const [password, setPassword] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState('');
|
|
const [loginTrigger] = useApiMutation<{ token: string; user: { id: string; name: string } }>();
|
|
|
|
const handleLogin = async () => {
|
|
if (!email.trim() || !password.trim()) {
|
|
setError('Please enter your email and password.');
|
|
return;
|
|
}
|
|
setError('');
|
|
setLoading(true);
|
|
try {
|
|
await loginTrigger({
|
|
url: '/auth/vessel-owner/login',
|
|
method: 'POST',
|
|
body: { email, password },
|
|
}).unwrap();
|
|
notify.success('Login successful. Welcome!');
|
|
navigate('/vessel-owner/dashboard');
|
|
} catch {
|
|
setError('Invalid email or password. Please try again.');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Box style={{ minHeight: '100vh', background: 'var(--mantine-color-gray-0)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
<Stack align="center" gap="xl" w="100%" maw={440} px="md">
|
|
{/* Brand */}
|
|
<Stack align="center" gap="xs">
|
|
<ThemeIcon size={64} radius="xl" color="blue" variant="light">
|
|
<IconShip size={36} />
|
|
</ThemeIcon>
|
|
<Title order={2} ta="center">Vessel Owner Portal</Title>
|
|
<Text fz="sm" c="dimmed" ta="center">
|
|
Ethiopian Maritime Affairs Authority
|
|
</Text>
|
|
</Stack>
|
|
|
|
<Paper withBorder radius="lg" p="xl" w="100%" shadow="sm">
|
|
<Group gap="xs" mb="lg">
|
|
<IconAnchor size={20} color="var(--mantine-color-blue-6)" />
|
|
<Text fw={700} fz="lg">Sign In</Text>
|
|
</Group>
|
|
|
|
{error && (
|
|
<Alert icon={<IconAlertCircle size={16} />} color="red" mb="md">
|
|
{error}
|
|
</Alert>
|
|
)}
|
|
|
|
<Stack gap="md">
|
|
<TextInput
|
|
label="Email Address"
|
|
placeholder="owner@example.com"
|
|
leftSection={<IconMail size={16} />}
|
|
value={email}
|
|
onChange={(e) => setEmail(e.currentTarget.value)}
|
|
onKeyDown={(e) => e.key === 'Enter' && handleLogin()}
|
|
/>
|
|
<PasswordInput
|
|
label="Password"
|
|
placeholder="Enter your password"
|
|
leftSection={<IconLock size={16} />}
|
|
value={password}
|
|
onChange={(e) => setPassword(e.currentTarget.value)}
|
|
onKeyDown={(e) => e.key === 'Enter' && handleLogin()}
|
|
/>
|
|
<Anchor fz="sm" ta="right" onClick={() => navigate('/vessel-owner/forgot-password')}>
|
|
Forgot password?
|
|
</Anchor>
|
|
<Button fullWidth size="md" loading={loading} onClick={handleLogin} leftSection={<IconAnchor size={16} />}>
|
|
Sign In
|
|
</Button>
|
|
</Stack>
|
|
|
|
<Divider my="md" label="Don't have an account?" labelPosition="center" />
|
|
|
|
<Button
|
|
fullWidth
|
|
variant="light"
|
|
onClick={() => navigate('/vessel-owner/register')}
|
|
>
|
|
Create Vessel Owner Account
|
|
</Button>
|
|
</Paper>
|
|
|
|
<Text fz="xs" c="dimmed" ta="center">
|
|
This portal is exclusively for vessel owners. For seafarer services,{' '}
|
|
<Anchor fz="xs" onClick={() => navigate('/login')}>sign in here</Anchor>.
|
|
</Text>
|
|
</Stack>
|
|
</Box>
|
|
);
|
|
}
|