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.

189 lines
8.6 KiB

  1. import { FormEventHandler, Fragment, PropsWithChildren, useEffect, useState } from 'react';
  2. import { Dialog, Transition } from '@headlessui/react';
  3. import { FileText, Box, Info, XCircle, ArrowRightCircle, CheckCircle, XOctagon } from 'react-feather';
  4. import { useForm } from '@inertiajs/react';
  5. import FormGroup from '@/Components/FormGroup';
  6. import InputLabel from '@/Components/InputLabel';
  7. import TextInput from '@/Components/TextInput';
  8. import InputError from '@/Components/InputError';
  9. import ErrorButton from '@/Components/ErrorButton';
  10. import PrimaryButton from '@/Components/PrimaryButton';
  11. import axios from 'axios';
  12. import Modal from '@/Components/Modal';
  13. import ModalButton from '@/Components/ModalButton';
  14. export default function AddCategoryModal({
  15. show = false,
  16. maxWidth = '2xl',
  17. closeable = true,
  18. onClose = () => {},
  19. onCategoryAdded = () => {},
  20. className = '',
  21. }: PropsWithChildren<{
  22. show: boolean;
  23. maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
  24. closeable?: boolean;
  25. onClose: CallableFunction;
  26. onCategoryAdded: CallableFunction;
  27. className?: string;
  28. }>) {
  29. const close = () => {
  30. if (closeable) {
  31. onClose();
  32. }
  33. };
  34. const maxWidthClass = {
  35. sm: 'sm:max-w-sm',
  36. md: 'sm:max-w-md',
  37. lg: 'sm:max-w-lg',
  38. xl: 'sm:max-w-xl',
  39. '2xl': 'sm:max-w-2xl',
  40. }[maxWidth];
  41. const { data, setData, errors, reset } = useForm({
  42. subjectTitle: '',
  43. description:'',
  44. });
  45. useEffect(() => {
  46. return () => {
  47. reset('subjectTitle');
  48. reset('description');
  49. };
  50. }, []);
  51. const [status, setStatus] = useState('');
  52. const [feedback, setFeedback] = useState('');
  53. const [isSuccessModalOpen, setIsSuccessModalOpen] = useState(false);
  54. const [isErrorModalOpen, setIsErrorModalOpen] = useState(false);
  55. const handleCloseSuccessModal = () => {
  56. setIsSuccessModalOpen(false);
  57. onCategoryAdded();
  58. close();
  59. }
  60. const submit: FormEventHandler = async (e) => {
  61. e.preventDefault();
  62. try {
  63. const response = await axios.post(route('category.add'), data);
  64. setStatus('Success')
  65. setFeedback(response.data.message);
  66. setIsSuccessModalOpen(true);
  67. } catch (error) {
  68. setStatus('Error')
  69. if (axios.isAxiosError(error)) {
  70. setFeedback(error.response?.data.message || 'An error occurred');
  71. } else {
  72. setFeedback('An unexpected error occurred');
  73. }
  74. setIsErrorModalOpen(true);
  75. }
  76. };
  77. return (
  78. <Transition show={show} as={Fragment} leave="duration-200">
  79. <Dialog
  80. as="div"
  81. id="modal"
  82. className="fixed inset-0 flex overflow-y-auto px-4 py-6 sm:px-0 items-center z-50 transform transition-all"
  83. onClose={close}
  84. >
  85. <Transition.Child
  86. as={Fragment}
  87. enter="ease-out duration-300"
  88. enterFrom="opacity-0"
  89. enterTo="opacity-100"
  90. leave="ease-in duration-200"
  91. leaveFrom="opacity-100"
  92. leaveTo="opacity-0"
  93. >
  94. <div className="absolute inset-0 bg-neutral-0 opacity-50" />
  95. </Transition.Child>
  96. <Transition.Child
  97. as={Fragment}
  98. enter="ease-out duration-300"
  99. enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
  100. enterTo="opacity-100 translate-y-0 sm:scale-100"
  101. leave="ease-in duration-200"
  102. leaveFrom="opacity-100 translate-y-0 sm:scale-100"
  103. leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
  104. >
  105. <Dialog.Panel
  106. className={`mb-6 bg-white border-4 rounded-lg overflow-hidden shadow-xl items-center transform transition-all sm:w-full sm:mx-auto bg-neutral-white ${maxWidthClass} ${className}`}
  107. >
  108. <div className="p-4 flex flex-col">
  109. <h1 className="font-bold text-xl text-primary-main flex items-center my-2"><FileText className="stroke-primary-main mr-2" />Add New Category</h1>
  110. <div className="divider"></div>
  111. <form id="AddCategoryForm">
  112. <div>
  113. <FormGroup>
  114. <InputLabel htmlFor="subjectTitle"><Box className='stroke-neutral-10' /></InputLabel>
  115. <label htmlFor="subjectTitle" className="mx-2 font-semibold">Subject Category Title:</label>
  116. </FormGroup>
  117. <TextInput
  118. id='subjectTitle'
  119. name='subjectTitle'
  120. value={data.subjectTitle}
  121. autoComplete='off'
  122. required
  123. onChange={(e) => setData('subjectTitle', e.target.value)}
  124. />
  125. <InputError message={errors.subjectTitle} className="mt-2" />
  126. </div>
  127. <div>
  128. <FormGroup>
  129. <InputLabel htmlFor="category"><Info className='stroke-neutral-10' /></InputLabel>
  130. <label htmlFor="category" className="mx-2 font-semibold">Description:</label>
  131. </FormGroup>
  132. <textarea className="px-2 flex-grow w-full bg-primary-background border border-primary-hover focus:outline-none focus:bg-neutral-10 focus:border-2 focus:border-primary-hover"
  133. onChange={(e) => setData('description', e.target.value)}>
  134. </textarea>
  135. </div>
  136. <div className='flex items-center justify-center mt-3 w-full'>
  137. <ErrorButton type="button" className='w-1/2' onClick={close}>
  138. <XCircle className='stroke-neutral-10' />
  139. Cancel
  140. </ErrorButton>
  141. <PrimaryButton type="button" onClick={submit} className='w-1/2'>
  142. <ArrowRightCircle className='stroke-neutral-10' />
  143. Save
  144. </PrimaryButton>
  145. </div>
  146. </form>
  147. </div>
  148. <Modal show={isSuccessModalOpen} onClose={() => setIsSuccessModalOpen(false)} maxWidth="lg" styling='success'>
  149. <div className="p-4 flex flex-col items-center">
  150. <CheckCircle className='stroke-success-main' size={80} />
  151. <h2 className="text-xl font-bold mt-2">{ status }</h2>
  152. <p className="mt-4">{ feedback }</p>
  153. <ModalButton onClick={handleCloseSuccessModal} className="bg-success-main text-white hover:bg-success-hover active:bg-success-pressed">
  154. Close
  155. </ModalButton>
  156. </div>
  157. </Modal>
  158. <Modal show={isErrorModalOpen} onClose={() => setIsErrorModalOpen(false)} maxWidth="lg" styling='error'>
  159. <div className="p-4 flex flex-col items-center">
  160. <XOctagon className='stroke-error-main' size={80} />
  161. <h2 className="text-xl font-bold mt-2">{ status }</h2>
  162. <p className="mt-4">{ feedback }</p>
  163. <ModalButton onClick={() => setIsErrorModalOpen(false)} className="bg-error-main text-white hover:bg-error-hover active:bg-error-pressed">
  164. Close
  165. </ModalButton>
  166. </div>
  167. </Modal>
  168. </Dialog.Panel>
  169. </Transition.Child>
  170. </Dialog>
  171. </Transition>
  172. );
  173. }