← back to ClawCoder

src/app/login/page.tsx

145 lines

'use client'

import { useState, FormEvent, Suspense } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'

function LoginForm() {
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [error, setError] = useState('')
  const [loading, setLoading] = useState(false)
  const router = useRouter()
  const searchParams = useSearchParams()

  async function handleSubmit(e: FormEvent) {
    e.preventDefault()
    setLoading(true)
    setError('')

    const res = await fetch('/api/auth', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password }),
    })

    if (res.ok) {
      const redirect = searchParams.get('redirect') || '/'
      window.location.href = redirect
    } else {
      setError('Invalid credentials')
      setLoading(false)
    }
  }

  return (
    <div style={{
      minHeight: '100vh',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
      background: '#0a0a0a',
      fontFamily: 'system-ui, -apple-system, sans-serif',
    }}>
      <form onSubmit={handleSubmit} style={{
        background: '#141414',
        border: '1px solid #222',
        borderRadius: '12px',
        padding: '40px',
        width: '100%',
        maxWidth: '380px',
      }}>
        <h1 style={{
          fontSize: '20px',
          fontWeight: 600,
          color: '#e5e5e5',
          marginBottom: '8px',
        }}>ClawCoder</h1>
        <p style={{
          fontSize: '13px',
          color: '#666',
          marginBottom: '28px',
        }}>Sign in to continue</p>

        {error && (
          <div style={{
            background: '#1a0000',
            border: '1px solid #3a0000',
            borderRadius: '6px',
            padding: '10px 14px',
            fontSize: '13px',
            color: '#ff4444',
            marginBottom: '20px',
          }}>{error}</div>
        )}

        <label style={{ display: 'block', marginBottom: '16px' }}>
          <span style={{ fontSize: '12px', color: '#888', display: 'block', marginBottom: '6px' }}>Username</span>
          <input
            type="text"
            value={username}
            onChange={e => setUsername(e.target.value)}
            autoFocus
            required
            style={{
              width: '100%',
              padding: '10px 12px',
              background: '#0a0a0a',
              border: '1px solid #333',
              borderRadius: '6px',
              color: '#e5e5e5',
              fontSize: '14px',
              outline: 'none',
              boxSizing: 'border-box',
            }}
          />
        </label>

        <label style={{ display: 'block', marginBottom: '24px' }}>
          <span style={{ fontSize: '12px', color: '#888', display: 'block', marginBottom: '6px' }}>Password</span>
          <input
            type="password"
            value={password}
            onChange={e => setPassword(e.target.value)}
            required
            style={{
              width: '100%',
              padding: '10px 12px',
              background: '#0a0a0a',
              border: '1px solid #333',
              borderRadius: '6px',
              color: '#e5e5e5',
              fontSize: '14px',
              outline: 'none',
              boxSizing: 'border-box',
            }}
          />
        </label>

        <button
          type="submit"
          disabled={loading}
          style={{
            width: '100%',
            padding: '11px',
            background: loading ? '#333' : '#e5e5e5',
            color: '#0a0a0a',
            border: 'none',
            borderRadius: '6px',
            fontSize: '14px',
            fontWeight: 600,
            cursor: loading ? 'wait' : 'pointer',
          }}
        >{loading ? 'Signing in...' : 'Sign In'}</button>
      </form>
    </div>
  )
}

export default function LoginPage() {
  return (
    <Suspense>
      <LoginForm />
    </Suspense>
  )
}