Files
invoice-master-poc-v2/frontend/src/components/Login.tsx
Yaojia Wang 58bf75db68 WIP
2026-01-27 00:47:10 +01:00

189 lines
6.4 KiB
TypeScript

import React, { useState } from 'react'
import { Button } from './Button'
interface LoginProps {
onLogin: (token: string) => void
}
export const Login: React.FC<LoginProps> = ({ onLogin }) => {
const [token, setToken] = useState('')
const [name, setName] = useState('')
const [description, setDescription] = useState('')
const [isCreating, setIsCreating] = useState(false)
const [error, setError] = useState('')
const [createdToken, setCreatedToken] = useState('')
const handleLoginWithToken = () => {
if (!token.trim()) {
setError('Please enter a token')
return
}
localStorage.setItem('admin_token', token.trim())
onLogin(token.trim())
}
const handleCreateToken = async () => {
if (!name.trim()) {
setError('Please enter a token name')
return
}
setIsCreating(true)
setError('')
try {
const response = await fetch('http://localhost:8000/api/v1/admin/auth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: name.trim(),
description: description.trim() || undefined,
}),
})
if (!response.ok) {
throw new Error('Failed to create token')
}
const data = await response.json()
setCreatedToken(data.token)
setToken(data.token)
setError('')
} catch (err) {
setError('Failed to create token. Please check your connection.')
console.error(err)
} finally {
setIsCreating(false)
}
}
const handleUseCreatedToken = () => {
if (createdToken) {
localStorage.setItem('admin_token', createdToken)
onLogin(createdToken)
}
}
return (
<div className="min-h-screen bg-warm-bg flex items-center justify-center p-4">
<div className="bg-warm-card border border-warm-border rounded-lg shadow-modal p-8 max-w-md w-full">
<h1 className="text-2xl font-bold text-warm-text-primary mb-2">
Admin Authentication
</h1>
<p className="text-sm text-warm-text-muted mb-6">
Sign in with an admin token to access the document management system
</p>
{error && (
<div className="mb-4 p-3 bg-red-50 border border-red-200 text-red-800 rounded text-sm">
{error}
</div>
)}
{createdToken && (
<div className="mb-4 p-3 bg-green-50 border border-green-200 rounded">
<p className="text-sm font-medium text-green-800 mb-2">Token created successfully!</p>
<div className="bg-white border border-green-300 rounded p-2 mb-3">
<code className="text-xs font-mono text-warm-text-primary break-all">
{createdToken}
</code>
</div>
<p className="text-xs text-green-700 mb-3">
Save this token securely. You won't be able to see it again.
</p>
<Button onClick={handleUseCreatedToken} className="w-full">
Use This Token
</Button>
</div>
)}
<div className="space-y-6">
{/* Login with existing token */}
<div>
<h2 className="text-sm font-semibold text-warm-text-secondary mb-3">
Sign in with existing token
</h2>
<div className="space-y-3">
<div>
<label className="block text-sm text-warm-text-secondary mb-1">
Admin Token
</label>
<input
type="text"
value={token}
onChange={(e) => setToken(e.target.value)}
placeholder="Enter your admin token"
className="w-full px-3 py-2 border border-warm-border rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-warm-state-info font-mono"
onKeyDown={(e) => e.key === 'Enter' && handleLoginWithToken()}
/>
</div>
<Button onClick={handleLoginWithToken} className="w-full">
Sign In
</Button>
</div>
</div>
<div className="relative">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-warm-border"></div>
</div>
<div className="relative flex justify-center text-xs">
<span className="px-2 bg-warm-card text-warm-text-muted">OR</span>
</div>
</div>
{/* Create new token */}
<div>
<h2 className="text-sm font-semibold text-warm-text-secondary mb-3">
Create new admin token
</h2>
<div className="space-y-3">
<div>
<label className="block text-sm text-warm-text-secondary mb-1">
Token Name <span className="text-red-500">*</span>
</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="e.g., my-laptop"
className="w-full px-3 py-2 border border-warm-border rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-warm-state-info"
/>
</div>
<div>
<label className="block text-sm text-warm-text-secondary mb-1">
Description (optional)
</label>
<input
type="text"
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="e.g., Personal laptop access"
className="w-full px-3 py-2 border border-warm-border rounded-md text-sm focus:outline-none focus:ring-1 focus:ring-warm-state-info"
/>
</div>
<Button
onClick={handleCreateToken}
variant="secondary"
disabled={isCreating}
className="w-full"
>
{isCreating ? 'Creating...' : 'Create Token'}
</Button>
</div>
</div>
</div>
<div className="mt-6 pt-4 border-t border-warm-border">
<p className="text-xs text-warm-text-muted">
Admin tokens are used to authenticate with the document management API.
Keep your tokens secure and never share them.
</p>
</div>
</div>
</div>
)
}