You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

173 lines
8.6 KiB

  1. import { useEffect, FormEventHandler, useState } from 'react';
  2. import Checkbox from '@/Components/Checkbox';
  3. import GuestLayout from '@/Layouts/GuestLayout';
  4. import InputError from '@/Components/InputError';
  5. import InputLabel from '@/Components/InputLabel';
  6. import PrimaryButton from '@/Components/PrimaryButton';
  7. import TextInput from '@/Components/TextInput';
  8. import { Head, Link, useForm } from '@inertiajs/react';
  9. import { AtSign, Lock, Eye, EyeOff, LogIn, CheckCircle, XOctagon } from 'react-feather';
  10. import FormGroup from '@/Components/FormGroup';
  11. import ProjectBanner from '@/Components/ProjectBanner';
  12. import Modal from '@/Components/Modal';
  13. import axios from 'axios';
  14. import ModalButton from '@/Components/ModalButton';
  15. export default function Login({ status, canResetPassword }: { status?: string, canResetPassword: boolean }) {
  16. const { data, setData, processing, errors, reset } = useForm({
  17. username: '',
  18. password: '',
  19. remember: false,
  20. });
  21. useEffect(() => {
  22. return () => {
  23. reset('password');
  24. };
  25. }, []);
  26. const [showPassword, setShowPassword] = useState(false);
  27. const toggleShowPassword = () => {
  28. setShowPassword((prevShowPassword) => !prevShowPassword);
  29. };
  30. const [statusTitle, setStatusTitle] = useState('');
  31. const [feedback, setFeedback] = useState('');
  32. const [isSuccessModalOpen, setIsSuccessModalOpen] = useState(false);
  33. const [isErrorModalOpen, setIsErrorModalOpen] = useState(false);
  34. const handleCloseSuccessModal = () => {
  35. setIsSuccessModalOpen(false);
  36. window.location.href = '/login';
  37. };
  38. const submit: FormEventHandler = async (e) => {
  39. e.preventDefault();
  40. try {
  41. const response = await axios.post(route('login'), data);
  42. setStatusTitle('Success')
  43. setFeedback(response.data.message);
  44. setIsSuccessModalOpen(true);
  45. } catch (error) {
  46. setStatusTitle('Error')
  47. if (axios.isAxiosError(error)) {
  48. setFeedback(error.response?.data.message || 'An error occurred');
  49. } else {
  50. setFeedback('An unexpected error occurred');
  51. }
  52. setIsErrorModalOpen(true);
  53. }
  54. };
  55. return (
  56. <GuestLayout>
  57. <Head title="Log in" />
  58. {status && <div className="mb-4 font-medium text-sm text-green-600">{status}</div>}
  59. <div id="Login" className="hero min-h-screen bg-secondary-main">
  60. <div className="hero-content flex-row">
  61. <ProjectBanner />
  62. <div className="card w-104 shrink-0 bg-primary-background">
  63. <div className="card-body justify-center items-center">
  64. <img src="assets/images/img-login.png" alt="An image that represents a login form from StorySet" width="250" height="250" />
  65. <form id="SignInForm" onSubmit={ submit }>
  66. <FormGroup>
  67. <InputLabel htmlFor='username'>
  68. <AtSign className='stroke-neutral-10' />
  69. </InputLabel>
  70. <TextInput
  71. id='username'
  72. name='username'
  73. value={data.username}
  74. autoComplete='off'
  75. placeholder='Username'
  76. required
  77. onChange={(e) => setData('username', e.target.value)}
  78. />
  79. <InputError message={errors.username} className="mt-2" />
  80. </FormGroup>
  81. <FormGroup>
  82. <InputLabel htmlFor='password'>
  83. <Lock className='stroke-neutral-10' />
  84. </InputLabel>
  85. <TextInput
  86. type={showPassword ? "text" : "password"}
  87. id='password'
  88. name='password'
  89. value={data.password}
  90. autoComplete='off'
  91. placeholder='Password'
  92. required
  93. onChange={(e) => setData('password', e.target.value)}
  94. className='border-r-0'
  95. />
  96. <label htmlFor="showPassword" className="swap items-center border border-primary-hover border-l-0">
  97. <input type="checkbox" name="showPassword" id="showPassword" onChange={toggleShowPassword} checked={showPassword} className='hidden' />
  98. <Eye className='swap-off px-1 stroke-secondary-main' />
  99. <EyeOff className='swap-on px-1 stroke-secondary-main' />
  100. </label>
  101. <InputError message={errors.password} className="mt-2" />
  102. </FormGroup>
  103. <div className="flex items-center justify-between">
  104. <label className="flex items-center">
  105. <Checkbox
  106. name="remember"
  107. checked={data.remember}
  108. onChange={(e) => setData('remember', e.target.checked)}
  109. />
  110. <span className="ml-1 text-secondary-main">Remember me</span>
  111. </label>
  112. {canResetPassword && (
  113. <Link
  114. href={route('password.request')}
  115. className="btn btn-link text-secondary-main m-0 p-0"
  116. >
  117. Forgot password...
  118. </Link>
  119. )}
  120. </div>
  121. <div className="flex items-center justify-end mt-4">
  122. <p className="invisible">No account yet? Register instead.</p>
  123. <PrimaryButton type='submit' disabled={processing}>
  124. Log In
  125. <LogIn className='stroke-neutral-10' />
  126. </PrimaryButton>
  127. </div>
  128. </form>
  129. </div>
  130. </div>
  131. </div>
  132. <Modal show={isSuccessModalOpen} onClose={() => setIsSuccessModalOpen(false)} maxWidth="lg" styling='success'>
  133. <div className="p-4 flex flex-col items-center">
  134. <CheckCircle className='stroke-success-main' size={80} />
  135. <h2 className="text-xl font-bold mt-2">{ statusTitle }</h2>
  136. <p className="mt-4">{ feedback }</p>
  137. <ModalButton onClick={handleCloseSuccessModal} className="bg-success-main text-white hover:bg-success-hover active:bg-success-pressed">
  138. Go to Dashboard
  139. </ModalButton>
  140. </div>
  141. </Modal>
  142. <Modal show={isErrorModalOpen} onClose={() => setIsErrorModalOpen(false)} maxWidth="lg" styling='error'>
  143. <div className="p-4 flex flex-col items-center">
  144. <XOctagon className='stroke-error-main' size={80} />
  145. <h2 className="text-xl font-bold mt-2">{ statusTitle }</h2>
  146. <p className="mt-4">{ feedback }</p>
  147. <ModalButton onClick={() => setIsErrorModalOpen(false)} className="bg-error-main text-white hover:bg-error-hover active:bg-error-pressed">
  148. Close
  149. </ModalButton>
  150. </div>
  151. </Modal>
  152. </div>
  153. </GuestLayout>
  154. );
  155. }