← back to IWasCute

src/app/login/page.tsx

243 lines

'use client'

import { useState } from 'react'
import { motion } from 'framer-motion'
import { Mail, Lock, Eye, EyeOff, ArrowRight } from 'lucide-react'
import Link from 'next/link'

export default function LoginPage() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [showPassword, setShowPassword] = useState(false)
  const [mode, setMode] = useState<'login' | 'signup'>('login')

  return (
    <div
      className="min-h-screen flex flex-col items-center justify-center px-4 py-12 relative overflow-hidden"
      style={{ background: 'var(--color-cream)' }}
    >
      {/* Decorative blobs */}
      <div
        className="absolute -top-32 -left-32 w-96 h-96 rounded-full opacity-20 blur-3xl pointer-events-none"
        style={{ background: 'var(--color-peach)' }}
      />
      <div
        className="absolute -bottom-40 -right-32 w-[30rem] h-[30rem] rounded-full opacity-15 blur-3xl pointer-events-none"
        style={{ background: 'var(--color-sage)' }}
      />

      {/* Logo */}
      <div className="mb-10 text-center">
        <Link
          href="/"
          className="text-3xl font-black tracking-tighter transition-opacity hover:opacity-70"
          style={{ color: 'var(--color-brown)' }}
        >
          i was cute
        </Link>
      </div>

      {/* Card */}
      <div
        className="relative z-10 w-full max-w-sm rounded-3xl p-8"
        style={{ background: 'rgba(255,255,255,0.7)', border: '1px solid var(--color-cream-dark)' }}
      >
        {/* Mode toggle */}
        <div
          className="flex gap-1 p-1 rounded-2xl mb-8"
          style={{ background: 'var(--color-cream-dark)' }}
        >
          {(['login', 'signup'] as const).map((m) => (
            <button
              key={m}
              onClick={() => setMode(m)}
              className="relative flex-1 py-2.5 text-sm font-semibold rounded-xl transition-all"
              style={{
                color: mode === m ? 'var(--color-brown)' : 'var(--color-warm-gray)',
              }}
            >
              {mode === m && (
                <motion.div
                  layoutId="auth-tab"
                  className="absolute inset-0 rounded-xl"
                  style={{ background: 'white', boxShadow: '0 1px 3px rgba(0,0,0,0.08)' }}
                  transition={{ type: 'spring', stiffness: 380, damping: 30 }}
                />
              )}
              <span className="relative z-10">
                {m === 'login' ? 'Sign In' : 'Sign Up'}
              </span>
            </button>
          ))}
        </div>

        <form
          onSubmit={(e) => {
            e.preventDefault()
            // TODO: wire up NextAuth.js
          }}
          className="flex flex-col gap-4"
        >
          {/* Email */}
          <div className="flex flex-col gap-1.5">
            <label
              htmlFor="email"
              className="text-sm font-semibold"
              style={{ color: 'var(--color-brown)' }}
            >
              Email
            </label>
            <div className="relative">
              <Mail
                size={16}
                className="absolute left-3.5 top-1/2 -translate-y-1/2 pointer-events-none"
                style={{ color: 'var(--color-warm-gray)' }}
              />
              <input
                id="email"
                type="email"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                placeholder="you@example.com"
                autoComplete="email"
                required
                className="w-full pl-10 pr-4 py-3 rounded-2xl text-sm border outline-none transition-all focus:ring-2"
                style={{
                  background: 'rgba(255,255,255,0.8)',
                  borderColor: 'var(--color-cream-dark)',
                  color: 'var(--color-brown)',
                }}
              />
            </div>
          </div>

          {/* Password */}
          <div className="flex flex-col gap-1.5">
            <label
              htmlFor="password"
              className="text-sm font-semibold"
              style={{ color: 'var(--color-brown)' }}
            >
              Password
            </label>
            <div className="relative">
              <Lock
                size={16}
                className="absolute left-3.5 top-1/2 -translate-y-1/2 pointer-events-none"
                style={{ color: 'var(--color-warm-gray)' }}
              />
              <input
                id="password"
                type={showPassword ? 'text' : 'password'}
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                placeholder={mode === 'signup' ? 'Create a password' : 'Your password'}
                autoComplete={mode === 'signup' ? 'new-password' : 'current-password'}
                required
                className="w-full pl-10 pr-12 py-3 rounded-2xl text-sm border outline-none transition-all focus:ring-2"
                style={{
                  background: 'rgba(255,255,255,0.8)',
                  borderColor: 'var(--color-cream-dark)',
                  color: 'var(--color-brown)',
                }}
              />
              <button
                type="button"
                onClick={() => setShowPassword(!showPassword)}
                className="absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-lg transition-all hover:scale-110"
                style={{ color: 'var(--color-warm-gray)' }}
                aria-label={showPassword ? 'Hide password' : 'Show password'}
              >
                {showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
              </button>
            </div>
          </div>

          {mode === 'signup' && (
            <motion.div
              initial={{ opacity: 0, height: 0 }}
              animate={{ opacity: 1, height: 'auto' }}
              className="overflow-hidden"
            >
              <label className="flex items-start gap-3 cursor-pointer">
                <input
                  type="checkbox"
                  required
                  className="mt-1"
                />
                <p className="text-xs leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
                  I am 18 years or older and agree to the{' '}
                  <Link href="/terms" className="underline" style={{ color: 'var(--color-peach-dark)' }}>
                    Terms of Service
                  </Link>{' '}
                  and{' '}
                  <Link href="/privacy" className="underline" style={{ color: 'var(--color-peach-dark)' }}>
                    Privacy Policy
                  </Link>
                  .
                </p>
              </label>
            </motion.div>
          )}

          {/* Submit */}
          <motion.button
            whileHover={{ scale: 1.02 }}
            whileTap={{ scale: 0.98 }}
            type="submit"
            className="flex items-center justify-center gap-2 w-full py-3.5 rounded-3xl text-sm font-semibold tracking-wide mt-2 transition-all"
            style={{ background: 'var(--color-peach)', color: 'var(--color-brown)' }}
          >
            {mode === 'login' ? 'Sign In' : 'Create Account'}
            <ArrowRight size={14} />
          </motion.button>
        </form>

        {/* Divider */}
        <div className="flex items-center gap-3 my-6">
          <div className="flex-1 h-px" style={{ background: 'var(--color-cream-dark)' }} />
          <span className="text-xs font-medium" style={{ color: 'var(--color-warm-gray)' }}>
            or
          </span>
          <div className="flex-1 h-px" style={{ background: 'var(--color-cream-dark)' }} />
        </div>

        {/* Google OAuth */}
        <button
          className="flex items-center justify-center gap-3 w-full py-3 rounded-2xl text-sm font-medium border transition-all hover:scale-[1.02]"
          style={{
            background: 'rgba(255,255,255,0.8)',
            borderColor: 'var(--color-cream-dark)',
            color: 'var(--color-brown)',
          }}
        >
          <svg width="18" height="18" viewBox="0 0 24 24">
            <path
              d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"
              fill="#4285F4"
            />
            <path
              d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
              fill="#34A853"
            />
            <path
              d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
              fill="#FBBC05"
            />
            <path
              d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
              fill="#EA4335"
            />
          </svg>
          Continue with Google
        </button>
      </div>

      {/* Footer note */}
      <p className="mt-8 text-xs text-center" style={{ color: 'var(--color-warm-gray)' }}>
        Must be 18+ to create an account. We never sell your data.
      </p>
    </div>
  )
}