From d09ea1c27cae3eea7dc93be8c860b173279fc0d5 Mon Sep 17 00:00:00 2001 From: MUGISHA Joseph <143373965+mugishaj092@users.noreply.github.com> Date: Tue, 8 Oct 2024 10:27:21 +0200 Subject: [PATCH] 154 crud application form (#167) * Will add create application feature * Will add update application form feature * Will add view application form feature * Will add dark mode card * Will add create application feature * Will add update application form feature * Will add view application form feature * Will add dark mode card --- .gitignore | 1 + package.json | 1 + src/components/GoogleForm/ApplicationForm.tsx | 6 +- .../GoogleForm/DeleteConfirmationModal.tsx | 62 ++++ src/components/GoogleForm/InputField.tsx | 47 +++ src/components/GoogleForm/RecentForms.tsx | 210 ++++++------ src/components/GoogleForm/SaveForm.tsx | 311 ++++++------------ src/components/GoogleForm/SelectField.tsx | 49 +++ src/components/GoogleForm/UpdateSavedForm.tsx | 228 ++++++------- .../GoogleForm/ViewApplicationModal.tsx | 63 ++++ src/components/GoogleForm/customModal.tsx | 40 +++ src/components/ReusableComponents/Select.tsx | 2 +- .../ApplicationForms/UpdateSavedForm.tsx | 2 +- 13 files changed, 570 insertions(+), 452 deletions(-) create mode 100644 src/components/GoogleForm/DeleteConfirmationModal.tsx create mode 100644 src/components/GoogleForm/InputField.tsx create mode 100644 src/components/GoogleForm/SelectField.tsx create mode 100644 src/components/GoogleForm/ViewApplicationModal.tsx create mode 100644 src/components/GoogleForm/customModal.tsx diff --git a/.gitignore b/.gitignore index db5330e1c..cd34dcb4e 100755 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,6 @@ coverage dist buildcoverage package-lock.json +yarn.lock .DS_Store build/ \ No newline at end of file diff --git a/package.json b/package.json index 4358a2787..3c0a73255 100755 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "react-icons": "^4.6.0", "react-js-pagination": "^3.0.3", "react-loader-spinner": "^6.1.6", + "react-modal": "^3.16.1", "react-paginate": "^8.1.3", "react-redux": "^8.0.4", "react-render-html": "^0.6.0", diff --git a/src/components/GoogleForm/ApplicationForm.tsx b/src/components/GoogleForm/ApplicationForm.tsx index bd3406c7c..0f438c276 100644 --- a/src/components/GoogleForm/ApplicationForm.tsx +++ b/src/components/GoogleForm/ApplicationForm.tsx @@ -10,9 +10,9 @@ const ApplicationForm = () => { }; return ( -
-
-
+
+
+
+ +
+
+
+
+
+ ); +}; + +export default DeleteConfirmationModal; diff --git a/src/components/GoogleForm/InputField.tsx b/src/components/GoogleForm/InputField.tsx new file mode 100644 index 000000000..bdf44dc8d --- /dev/null +++ b/src/components/GoogleForm/InputField.tsx @@ -0,0 +1,47 @@ +interface InputFieldProps { + id: string; + name: string; + label: string; + type?: string; + value: string; + onChange: (e: React.ChangeEvent) => void; + onBlur: (e: React.FocusEvent) => void; + error?: string; + touched?: boolean; + classname?: string +} + +const InputField = ({ + id, + name, + label, + type = 'text', + value, + onChange, + onBlur, + error, + touched, + classname +}: InputFieldProps) => ( +
+ +
+ + {touched && error &&
{error}
} +
+
+); + +export default InputField; diff --git a/src/components/GoogleForm/RecentForms.tsx b/src/components/GoogleForm/RecentForms.tsx index 3ec2945d3..1a4c05305 100644 --- a/src/components/GoogleForm/RecentForms.tsx +++ b/src/components/GoogleForm/RecentForms.tsx @@ -2,8 +2,14 @@ import { useEffect, useState } from 'react'; import axios from '../../redux/actions/axiosconfig'; import * as icons from 'react-icons/ai'; import { Link } from 'react-router-dom'; +import CustomModal from './customModal'; +import { useFormik } from 'formik'; +import * as Yup from 'yup'; +import { toast } from 'react-toastify'; +import ViewApplicationModal from './ViewApplicationModal'; +import DeleteConfirmationModal from './DeleteConfirmationModal'; -interface Application { +export interface Application { id: string; link: string; title: string; @@ -14,16 +20,19 @@ interface Application { const RecentForms = () => { const [applications, setApplications] = useState([]); const [loading, setLoading] = useState(true); - const [jobposts, setjobposts] = useState([]); + const [updateLoading, setUpdateLoading] = useState(false) + const [jobposts, setJobPosts] = useState([]); const [error, setError] = useState(''); + const [viewApplicationModalIsOpen, setViewApplicationModalIsOpen] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); - const [applicationToDelete, setApplicationToDelete] = useState( - null - ); + const [applicationToDelete, setApplicationToDelete] = useState(null); + const [modalIsOpen, setIsOpen] = useState(false); + const [selectedApplication, setSelectedApplication] = useState(null); + const [viewedApplication, setViewedApplication] = useState(null); useEffect(() => { - const fetchjobposts = async () => { + const fetchJobPosts = async () => { const graphqlQuery = ` query GetAllJobApplication($input: pagination) { getAllJobApplication(input: $input) { @@ -49,7 +58,7 @@ const RecentForms = () => { } if (response.data.data && response.data.data.getAllJobApplication) { - setjobposts(response.data.data.getAllJobApplication); + setJobPosts(response.data.data.getAllJobApplication); } } catch (error: any) { console.error('An error occurred:', error); @@ -57,38 +66,40 @@ const RecentForms = () => { } }; - fetchjobposts(); + fetchJobPosts(); }, []); useEffect(() => { - axios - .post(`${process.env.BACKEND_URL}`, { - query: ` - query { - getAllApplications { - id - link - title - jobpost - description + const fetchApplications = async () => { + try { + const response = await axios.post(`${process.env.BACKEND_URL}`, { + query: ` + query { + getAllApplications { + id + link + title + jobpost + description + } } - } - `, - }) - .then((response) => { + `, + }); const data = response.data?.data?.getAllApplications ?? []; setApplications(data); console.log(response); - setLoading(false); - }) - .catch((error) => { + } catch (error) { console.error('Error fetching applications:', error); setLoading(false); - }); + } + }; + + fetchApplications(); }, []); - const handleDelete = async (id: string) => { + + const handleDelete= async (id: string) : Promise => { try { await axios.post(`${process.env.BACKEND_URL}`, { query: ` @@ -110,6 +121,36 @@ const RecentForms = () => { } }; + const openViewApplicationModal = async (id: string) => { + try { + const response = await axios.post(`${process.env.BACKEND_URL}`, { + query: ` + query GetApplication($id: ID!) { + getApplication(id: $id) { + id + link + title + jobpost + description + } + } + `, + variables: { id }, + }); + + const applicationData = response.data?.data?.getApplication; + setViewedApplication(applicationData); + setViewApplicationModalIsOpen(true); + } catch (error) { + console.error('Error fetching application:', error); + } + }; + + const closeViewApplicationModal = () => { + setViewedApplication(null); + setViewApplicationModalIsOpen(false); + }; + const showDeleteConfirmation = (id: string) => { setApplicationToDelete(id); setShowDeleteModal(true); @@ -121,9 +162,7 @@ const RecentForms = () => { }; function findJobPostTitle(application: Application) { - const jobpostObject = jobposts.find( - (jobpost) => jobpost.id === application.jobpost - ); + const jobpostObject = jobposts.find((jobpost) => jobpost.id === application.jobpost); return jobpostObject ? jobpostObject.title : 'Loading...'; } @@ -134,63 +173,63 @@ const RecentForms = () => {
-
-

- Saved Application Forms +
+

+ Application Forms

-
+
{!loading && ( -
    +
      {reversedApplications.map((application) => (
    • -
      -

      + className='flex flex-col col-span-1 text-center bg-white divide-y divide-gray-200 rounded-lg shadow dark:bg-dark-tertiary'> +
      +

      {application.title}

      -
      -
      - {application.description} +
      +
      + {application.description.length > 155 + ? `${application.description.substring(0, 155)}...` + : application.description}
      - + {findJobPostTitle(application)}
      -
      -
      - +
      +
      -
      + -
      +
      @@ -202,55 +241,22 @@ const RecentForms = () => { )}
      {showDeleteModal && ( -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      - -
      -
      -
      - Delete the Form -
      -
      -

      - Are you sure you want to delete this item? All of - the form data will be permanently removed. This - action cannot be undone. -

      -
      -
      -
      -
      -
      - - -
      -
      -
      -
      -
      + handleDelete(applicationToDelete!)} + /> )}
      +
      ); }; -export default RecentForms; +export default RecentForms; \ No newline at end of file diff --git a/src/components/GoogleForm/SaveForm.tsx b/src/components/GoogleForm/SaveForm.tsx index 232f0b568..9b47f3472 100644 --- a/src/components/GoogleForm/SaveForm.tsx +++ b/src/components/GoogleForm/SaveForm.tsx @@ -3,7 +3,8 @@ import axios from '../../redux/actions/axiosconfig'; import { showSuccessToast, showErrorToast } from './../../utils/toast'; import { useFormik } from 'formik'; import * as Yup from 'yup'; -import SelectField from "../ReusableComponents/Select" +import InputField from './InputField'; +import SelectField from './SelectField'; const validationSchema = Yup.object().shape({ link: Yup.string().required('Please enter a Google Form link'), @@ -16,10 +17,10 @@ function SaveFormDetails() { const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(false); - const [jobposts, setjobposts] = useState([]); + const [jobposts, setJobposts] = useState([]); useEffect(() => { - const fetchjobposts = async () => { + const fetchJobPosts = async () => { const graphqlQuery = ` query GetAllJobApplication($input: pagination) { getAllJobApplication(input: $input) { @@ -28,45 +29,29 @@ function SaveFormDetails() { } } `; - try { const response = await axios.post('/', { query: graphqlQuery, - variables: { - input: { - All: true, - page: 1, - }, - }, + variables: { input: { All: true, page: 1 } }, }); - - if (response.data.errors) { + if (response.data.data) { + setJobposts(response.data.data.getAllJobApplication); + } else { throw new Error(response.data.errors[0].message); } - - if (response.data.data && response.data.data.getAllJobApplication) { - setjobposts(response.data.data.getAllJobApplication); - } - } catch (error: any) { - console.error('An error occurred:', error); - setError(`Error fetching job posts: ${error}`); + } catch (err: any) { + console.error('Error fetching job posts:', err); + setError(err.message); } }; - - fetchjobposts(); + fetchJobPosts(); }, []); const formik = useFormik({ - initialValues: { - link: '', - title: '', - jobpost: '', - description: '', - }, + initialValues: { link: '', title: '', jobpost: '', description: '' }, validationSchema, onSubmit: async (values) => { setLoading(true); - const graphqlQuery = ` mutation CreateApplication($link: String!, $title: String!, $jobpost: String!, $description: String!) { createApplication(link: $link, title: $title, jobpost: $jobpost, description: $description) { @@ -76,211 +61,103 @@ function SaveFormDetails() { jobpost description } - } - `; - - const variables = { - link: values.link, - title: values.title, - jobpost: values.jobpost, - description: values.description, - }; + }`; try { const response = await axios.post('/', { query: graphqlQuery, - variables, + variables: values, }); - if (response.data.errors) { - throw new Error(response.data.errors[0].message); - } - - if (response.data.data && response.data.data.createApplication) { + const errorMessage = response.data.errors[0].error; + if (errorMessage.toLowerCase().includes("a record with link")) + showErrorToast('The link is already in use'); + } else { setSuccess(true); - - console.log('Application created successfully'); - formik.resetForm(); showSuccessToast('Application created successfully!'); - } else { - setError( - `Error creating application: ${response.data.errors[0].message}` - ); - } - window.location.href = '/#/view-forms'; - } catch (error: any) { - console.error('An error occurred:', error); - if (error.response) { - if (error.response.status === 403) { - setError('You do not have permission to perform this action'); - } else if (error.response.status === 400) { - setError('Invalid request'); - } else if (error.response.status === 500) { - setError('Server error'); - } else { - setError('An error occurred while submitting the form.'); - } - } else { - setError(`Error creating application: ${error}`); + formik.resetForm(); + window.location.href = '/#/admin/view-forms'; } + } catch (err: any) { + console.error('Error submitting form:', err); + showErrorToast('An error occurred'); } finally { setLoading(false); - if (success) { - showSuccessToast('Application created successfully!'); - } else if (error) { - showErrorToast(error); - } } }, }); return ( -
      -
      -
      -

      - Save Application Form -

      - -

      - Copy Google Form URL and paste it in the input field below. -

      -
      - -
      -
      -
      - - -
      - - {formik.touched.title && formik.errors.title && ( -
      {formik.errors.title}
      - )} -
      -
      - -
      - - -
      -
      - - ( - - )) - } - /> -
      - {formik.touched.jobpost && formik.errors.jobpost && ( -
      {formik.errors.jobpost}
      - )} -
      -
      - -
      - - -
      - - {formik.touched.link && formik.errors.link && ( -
      {formik.errors.link}
      - )} -
      -
      - -
      - - -
      -