opcotech/elemo

View on GitHub
web/components/auth/SignInForm.tsx

Summary

Maintainability
A
1 hr
Test Coverage
'use client';

import { useSearchParams } from 'next/navigation';
import { useEffect, useRef, useState } from 'react';
import { Button } from '@/components/blocks/Button';
import { getCsrfToken } from 'next-auth/react';

export type SignInErrorTypes =
  | 'Signin'
  | 'OAuthSignin'
  | 'OAuthCallback'
  | 'OAuthCreateAccount'
  | 'EmailCreateAccount'
  | 'Callback'
  | 'OAuthAccountNotLinked'
  | 'EmailSignin'
  | 'CredentialsSignin'
  | 'SessionRequired'
  | 'default';

const ERRORS: Record<SignInErrorTypes, string> = {
  Signin: 'Try signing in with a different account.',
  OAuthSignin: 'Try signing in with a different account.',
  OAuthCallback: 'Try signing in with a different account.',
  OAuthCreateAccount: 'Try signing in with a different account.',
  EmailCreateAccount: 'Try signing in with a different account.',
  Callback: 'Try signing in with a different account.',
  OAuthAccountNotLinked: 'To confirm your identity, sign in with the same account you used originally.',
  EmailSignin: 'The e-mail could not be sent.',
  CredentialsSignin: 'Sign in failed. Check the details you provided are correct.',
  SessionRequired: 'Please sign in to access this page.',
  default: 'Unable to sign in.'
};

export function SignInForm() {
  const form = useRef<HTMLFormElement>(null);
  const [csrfToken, setCSRFToken] = useState('');
  const [submitting, setSubmitting] = useState(false);

  const searchParams = useSearchParams();
  const error = searchParams.get('error') as SignInErrorTypes | null;

  useEffect(() => {
    getCsrfToken().then((token) => setCSRFToken(token || ''));
  }, []);

  async function handleSubmit() {
    setSubmitting(true);
    form.current?.submit();
  }

  return (
    <div className="mt-8">
      {error && (
        <div className="rounded-md bg-red-50 p-4">
          <div className="">
            <h3 className="text-base font-medium text-red-800">Failed to sign in!</h3>
            <div className="mt-2 text-base text-red-700">
              <p>{ERRORS[error]}</p>
            </div>
          </div>
        </div>
      )}

      <div className="mt-6">
        <form ref={form} className="space-y-6" method="POST" action="/api/auth/callback/credentials">
          <input name="csrfToken" type="hidden" defaultValue={csrfToken} />
          <div>
            <label htmlFor="username" className="block font-medium leading-6 text-gray-900">
              Email address
            </label>
            <div className="mt-2">
              <input
                id="username"
                name="username"
                type="email"
                required
                disabled={submitting}
                autoComplete="email"
                placeholder={'you@company.com'}
                className="block w-full rounded-md border-0 py-1.5 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:leading-6"
              />
            </div>
          </div>

          <div className="space-y-1">
            <label htmlFor="password" className="block font-medium leading-6 text-gray-900">
              Password
            </label>
            <div className="mt-2">
              <input
                id="password"
                name="password"
                type="password"
                required
                disabled={submitting}
                autoComplete="current-password"
                placeholder={'********'}
                className="block w-full rounded-md border-0 py-1.5 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:leading-6"
              />
            </div>
          </div>

          <div className="flex items-center justify-between">
            <div className="flex items-center">
              <input
                id="remember-me"
                name="remember-me"
                type="checkbox"
                className="h-4 w-4 rounded border-gray-300 text-blue-500 focus:ring-blue-600"
                disabled={submitting}
              />
              <label htmlFor="remember-me" className="ml-2 block text-gray-900">
                Remember me
              </label>
            </div>

            <div>
              <a href="#" className="font-medium text-blue-500 hover:text-blue-600">
                Forgot your password?
              </a>
            </div>
          </div>

          <div>
            <Button type="submit" loading={submitting} className="w-full" onSubmit={handleSubmit}>
              Sign in
            </Button>
          </div>
        </form>
      </div>
    </div>
  );
}