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.

196 lines
8.9 KiB

  1. import { FormEventHandler, Fragment, PropsWithChildren, useEffect, useState } from 'react';
  2. import { Dialog, Transition } from '@headlessui/react';
  3. import { FileText, Box, XCircle, ArrowRightCircle, CheckCircle, XOctagon, Code } 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 AddFrameworkModal({
  15. show = false,
  16. maxWidth = '2xl',
  17. closeable = true,
  18. onClose = () => {},
  19. onFrameworkAdded = () => {},
  20. className = '',
  21. }: PropsWithChildren<{
  22. show: boolean;
  23. maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl';
  24. closeable?: boolean;
  25. onClose: CallableFunction;
  26. onFrameworkAdded: 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, reset, errors } = useForm({
  42. frameworkName: '',
  43. version:'',
  44. });
  45. useEffect(() => {
  46. return () => {
  47. reset('frameworkName');
  48. reset('version');
  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. onFrameworkAdded();
  58. close();
  59. }
  60. const submit: FormEventHandler = async (e) => {
  61. e.preventDefault();
  62. try {
  63. const response = await axios.post(route('framework.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 Framework</h1>
  110. <div className="divider"></div>
  111. <form id="AddFrameworkForm">
  112. <div>
  113. <FormGroup>
  114. <InputLabel htmlFor="frameworkName"><Box className='stroke-neutral-10' /></InputLabel>
  115. <label htmlFor="frameworkName" className="mx-2 font-semibold">App Framework Name:</label>
  116. </FormGroup>
  117. <TextInput
  118. id='frameworkName'
  119. name='frameworkName'
  120. value={data.frameworkName}
  121. autoComplete='off'
  122. required
  123. onChange={(e) => setData('frameworkName', e.target.value)}
  124. />
  125. <InputError message={errors.frameworkName} className="mt-2" />
  126. </div>
  127. <div>
  128. <FormGroup>
  129. <InputLabel htmlFor="version"><Code className='stroke-neutral-10' /></InputLabel>
  130. <label htmlFor="version" className="mx-2 font-semibold">Version:</label>
  131. </FormGroup>
  132. <TextInput
  133. id='version'
  134. name='version'
  135. value={data.version}
  136. autoComplete='off'
  137. required
  138. onChange={(e) => setData('version', e.target.value)}
  139. />
  140. <InputError message={errors.version} className="mt-2" />
  141. </div>
  142. <div className='flex items-center justify-center mt-3 w-full'>
  143. <ErrorButton type="button" className='w-1/2' onClick={close}>
  144. <XCircle className='stroke-neutral-10' />
  145. Cancel
  146. </ErrorButton>
  147. <PrimaryButton type="button" className='w-1/2' onClick={submit}>
  148. <ArrowRightCircle className='stroke-neutral-10' />
  149. Save
  150. </PrimaryButton>
  151. </div>
  152. </form>
  153. </div>
  154. <Modal show={isSuccessModalOpen} onClose={() => setIsSuccessModalOpen(false)} maxWidth="lg" styling='success'>
  155. <div className="p-4 flex flex-col items-center">
  156. <CheckCircle className='stroke-success-main' size={80} />
  157. <h2 className="text-xl font-bold mt-2">{ status }</h2>
  158. <p className="mt-4">{ feedback }</p>
  159. <ModalButton onClick={handleCloseSuccessModal} className="bg-success-main text-white hover:bg-success-hover active:bg-success-pressed">
  160. Close
  161. </ModalButton>
  162. </div>
  163. </Modal>
  164. <Modal show={isErrorModalOpen} onClose={() => setIsErrorModalOpen(false)} maxWidth="lg" styling='error'>
  165. <div className="p-4 flex flex-col items-center">
  166. <XOctagon className='stroke-error-main' size={80} />
  167. <h2 className="text-xl font-bold mt-2">{ status }</h2>
  168. <p className="mt-4">{ feedback }</p>
  169. <ModalButton onClick={() => setIsErrorModalOpen(false)} className="bg-error-main text-white hover:bg-error-hover active:bg-error-pressed">
  170. Close
  171. </ModalButton>
  172. </div>
  173. </Modal>
  174. </Dialog.Panel>
  175. </Transition.Child>
  176. </Dialog>
  177. </Transition>
  178. );
  179. }