69 lines
1.7 KiB
TypeScript
69 lines
1.7 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState } from 'react';
|
||
|
|
|
||
|
|
interface Props {
|
||
|
|
onSuccess: () => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function AdminLogin({ onSuccess }: Props) {
|
||
|
|
const [password, setPassword] = useState('');
|
||
|
|
const [error, setError] = useState('');
|
||
|
|
const [loading, setLoading] = useState(false);
|
||
|
|
|
||
|
|
async function handleSubmit(e: React.FormEvent) {
|
||
|
|
e.preventDefault();
|
||
|
|
setError('');
|
||
|
|
setLoading(true);
|
||
|
|
|
||
|
|
try {
|
||
|
|
const res = await fetch('/api/admin/auth', {
|
||
|
|
method: 'POST',
|
||
|
|
headers: { 'Content-Type': 'application/json' },
|
||
|
|
body: JSON.stringify({ password }),
|
||
|
|
});
|
||
|
|
|
||
|
|
if (res.ok) {
|
||
|
|
onSuccess();
|
||
|
|
} else {
|
||
|
|
const data = await res.json();
|
||
|
|
setError(data.error || 'Login failed');
|
||
|
|
}
|
||
|
|
} catch {
|
||
|
|
setError('Connection error');
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="admin-login">
|
||
|
|
<form className="admin-login-card" onSubmit={handleSubmit}>
|
||
|
|
<div className="admin-login-header">
|
||
|
|
<h1>Immich Folio</h1>
|
||
|
|
<p>Admin Panel</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="admin-field">
|
||
|
|
<label htmlFor="admin-password">Password</label>
|
||
|
|
<input
|
||
|
|
id="admin-password"
|
||
|
|
type="password"
|
||
|
|
value={password}
|
||
|
|
onChange={(e) => setPassword(e.target.value)}
|
||
|
|
placeholder="Enter admin password"
|
||
|
|
autoFocus
|
||
|
|
disabled={loading}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{error && <div className="admin-error">{error}</div>}
|
||
|
|
|
||
|
|
<button type="submit" className="admin-btn admin-btn-primary" disabled={loading}>
|
||
|
|
{loading ? 'Signing in...' : 'Sign In'}
|
||
|
|
</button>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|