Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to "Read and Agree" with mentor and mentee guides at the application process #190

Merged
merged 5 commits into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/assets/svg/Icons/OpenIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';

const OpenIcon: React.FC = () => {
return (
<div>
<svg
className="h-5 w-5"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="none"
>
<path
className="stroke-gray-500"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M10 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4m-8-2 8-8m0 0v5m0-5h-5"
/>
</svg>
</div>
);
};

export default OpenIcon;
165 changes: 165 additions & 0 deletions src/components/TermsAgreementModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import Tooltip from '../Tooltip';
import OpenIcon from '../../assets/svg/Icons/OpenIcon';
import {
menteeTermsAgreementModalSchema,
mentorTermsAgreementModalSchema,
} from '../../schemas';

type MentorFormData = z.infer<typeof mentorTermsAgreementModalSchema>;
type MenteeFormData = z.infer<typeof menteeTermsAgreementModalSchema>;

interface TermsAgreementModalProps {
isOpen: boolean;
onClose: () => void;
onAgree: (data: MentorFormData | MenteeFormData) => void;
isMentor: boolean;
guideUrl: string;
}

const TermsAgreementModal: React.FC<TermsAgreementModalProps> = ({
isOpen,
onClose,
onAgree,
isMentor,
guideUrl,
}) => {
const schema = isMentor
? mentorTermsAgreementModalSchema
: menteeTermsAgreementModalSchema;
const {
register,
handleSubmit,
formState: { errors, isValid },
} = useForm({
resolver: zodResolver(schema),
mode: 'onChange',
defaultValues: isMentor
? { agreed: false, canCommit: false }
: { agreed: false, consentGiven: false },
});

const onSubmit = (data: MentorFormData | MenteeFormData) => {
if (isValid) {
onAgree(data);
} else {
console.error('Form is invalid. Errors:', errors);
}
};

if (!isOpen) return null;

const guideType = isMentor ? 'Mentor' : 'Mentee';
const title = isMentor
? 'One Last Step to Join as a ScholarX Mentor'
: 'ScholarX Mentee Terms and Privacy';

return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
<div className="bg-white rounded-lg shadow-xl max-w-full md:max-w-4xl w-full max-h-[95vh] overflow-y-auto">
<form className="text-gray-800" onSubmit={handleSubmit(onSubmit)}>
<div className="p-6 pb-2">
<h2 className="text-2xl font-bold mb-4 text-center text-gray-600">
<span className="inline-block sm:inline">{title}</span>
</h2>

<div className="flex items-center relative">
<p className="text-md font-semibold mt-4 mb-1 text-gray-900">
ScholarX {guideType} Guide
</p>
<div className="flex items-center justify-center absolute left-[14.2rem] top-[18.5px]">
<Tooltip content={`Open ${guideType} Guide`} isVisible={true}>
<a href={guideUrl} target="_blank" rel="noreferrer">
<OpenIcon />
</a>
</Tooltip>
</div>
</div>

<div className="sm:h-[30vh] md:h-[35vh] overflow-y-auto mb-4 border border-gray-200 rounded mt-1">
<iframe
src={`${guideUrl}/preview?rm=minimal`}
width="100%"
height="100%"
></iframe>
</div>

{!isMentor && (
<>
<p className="text-md font-semibold text-gray-900">
Privacy Statement
</p>
<p>
Sustainable Foundation Education assures that your video
submission will be used exclusively for application evaluation
purposes. We are committed to protecting your privacy and will
not use your video for any other activities, such as general
AI training or public distribution. Your personal information
and video content will be handled with the utmost
confidentiality.
</p>
</>
)}

<div className="space-y-2 mt-5">
<label className="flex items-top space-x-2">
<input
type="checkbox"
{...register('agreed')}
className="form-checkbox h-4 w-4 text-blue-600 flex-shrink-0 mt-1"
/>
<span className="text-md">
I have read and agree to the ScholarX {guideType} Guide
</span>
</label>
{errors.agreed && (
<p className="text-red-500 text-sm ml-6">
{errors.agreed.message}
</p>
)}

<label className="flex items-top space-x-2">
<input
type="checkbox"
{...register(isMentor ? 'canCommit' : 'consentGiven')}
className="form-checkbox h-4 w-4 text-blue-600 flex-shrink-0 mt-1"
/>
<span className="text-md">
{isMentor
? 'Are you able to commit to a period of 6 months for the program? (We expect a minimum of 6 calls with a mentee in a span of 6 month period)'
: 'I grant Sustainable Foundation Education permission to use my video submission solely for the internal evaluation of my application to ScholarX.'}
</span>
</label>
{(errors.canCommit ?? errors.consentGiven) && (
<p className="text-red-500 text-sm ml-6">
{errors.canCommit?.message ?? errors.consentGiven?.message}
</p>
)}
</div>
</div>

<div className="px-6 py-3 flex justify-end rounded-b-lg">
<button
type="button"
onClick={onClose}
className="bg-gray-200 hover:bg-gray-300 focus:ring-4 focus:ring-blue-300 text-gray-800 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2"
>
Cancel
</button>
<button
type="submit"
className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2"
>
Agree and Continue
</button>
</div>
</form>
</div>
</div>
);
};

export default TermsAgreementModal;
62 changes: 41 additions & 21 deletions src/pages/MenteeRegistration/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { API_URL } from '../../constants';
import useProfile from '../../hooks/useProfile';
import { MenteeApplicationSchema } from '../../schemas';
import { MenteeApplication } from '../../types';
import TermsAgreementModal from '../../components/TermsAgreementModal';

const steps = [
{
Expand All @@ -35,9 +36,11 @@ const MenteeRegistration: React.FC = () => {
setError,
clearErrors,
trigger,
getValues,
formState: { errors },
} = useForm<MenteeApplication>({
resolver: zodResolver(MenteeApplicationSchema),
mode: 'onChange',
defaultValues: {
firstName: user?.first_name,
lastName: user?.last_name,
Expand All @@ -51,6 +54,8 @@ const MenteeRegistration: React.FC = () => {
const [currentStep, setCurrentStep] = useState(0);
const [image, setImage] = useState<File | null>(null);
const [profilePic, setProfilePic] = useState(user?.image_url);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);

const handleProfilePicChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.files != null) {
Expand Down Expand Up @@ -89,7 +94,7 @@ const MenteeRegistration: React.FC = () => {
}
}

const output = await trigger(fields as [keyof MenteeApplication], {
const output = await trigger(fields as Array<keyof MenteeApplication>, {
shouldFocus: true,
});

Expand All @@ -101,10 +106,31 @@ const MenteeRegistration: React.FC = () => {
setCurrentStep((prevStep) => prevStep - 1);
};

const onSubmit: SubmitHandler<MenteeApplication> = async (data) => {
await applyForMentor(data);
if (image) {
await updateProfile({ profile: null, image });
const onSubmit: SubmitHandler<MenteeApplication> = async () => {
setIsModalOpen(true);
};

const handleModalAgree = async (agreedData: {
agreed: boolean;
consentGive?: boolean;
canCommit?: boolean;
}) => {
setIsModalOpen(false);
setIsSubmitting(true);
const formData = getValues();
try {
const validatedData = MenteeApplicationSchema.parse({
...formData,
...agreedData,
});
await applyForMentor(validatedData);
if (image) {
await updateProfile({ profile: null, image });
}
} catch (error) {
console.error('Error submitting application:', error);
} finally {
setIsSubmitting(false);
}
};

Expand Down Expand Up @@ -355,22 +381,6 @@ const MenteeRegistration: React.FC = () => {
register={register}
error={errors.submission}
/>
<FormCheckbox
name="consentGiven"
label="I, hereby grant Sustainable Foundation Education permission to use my video submission solely for the internal evaluation of my application
to ScholarX. I understand that this video will not be used for any other purposes without my explicit consent."
register={register}
error={errors.consentGiven}
/>
<p className="text-md font-semibold">Privacy Statement</p>
<p>
Sustainable Foundation Education assures that your video
submission will be used exclusively for application evaluation
purposes. We are committed to protecting your privacy and will not
use your video for any other activities, such as general AI
training or public distribution. Your personal information and
video content will be handled with the utmost confidentiality.
</p>
</>
)}
{status === 'error' ? (
Expand Down Expand Up @@ -425,6 +435,7 @@ const MenteeRegistration: React.FC = () => {
{currentStep === 2 && !applicationSuccess && (
<button
type="submit"
disabled={isSubmitting}
className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2"
>
{isApplicationPending ? 'Submitting...' : 'Submit'}
Expand All @@ -440,6 +451,15 @@ const MenteeRegistration: React.FC = () => {
)}
</div>
</form>
<TermsAgreementModal
isOpen={isModalOpen}
onClose={() => {
setIsModalOpen(false);
}}
onAgree={handleModalAgree}
isMentor={false}
guideUrl="https://docs.google.com/document/d/1gIYte14FIQtqUhGiMErZRovhNErdUrFdQ0LnCFFnfag/"
/>
</div>
);
};
Expand Down
Loading
Loading