Skip to content

Commit

Permalink
Add revoke mentee application option (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
anjula-sack authored Sep 4, 2024
1 parent bb5931a commit f79046e
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 68 deletions.
7 changes: 4 additions & 3 deletions src/components/ConfirmButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import ConfirmationModal from '../ConfirmationModal';
import InformationModal from '../InformationModal';

interface ConfirmButtonProps {
buttonText: string;
Expand Down Expand Up @@ -35,10 +35,11 @@ const ConfirmButton: React.FC<ConfirmButtonProps> = ({
{buttonText}
</button>
{isModalOpen && (
<ConfirmationModal
<InformationModal
isOpen={isModalOpen}
message={confirmMessage}
headline={confirmMessage}
onConfirm={handleConfirm}
body="An email will be sent to the applicant."
onClose={handleCloseModal}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ import React, { useState } from 'react';
import WarningIcon from '../../assets/svg/Icons/WarningIcon';
import CloseIcon from '../../assets/svg/Icons/CloseIcon';

interface ConfirmationModalProps {
interface InformationModalProps {
isOpen: boolean;
onClose: () => void;
onConfirm: () => Promise<void>;
message: string;
headline: string;
body: string;
}

const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
const InformationModal: React.FC<InformationModalProps> = ({
isOpen,
onClose,
onConfirm,
message,
headline,
body,
}) => {
if (!isOpen) return null;
const [isLoading, setIsLoading] = useState(false);
Expand All @@ -22,6 +24,7 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
setIsLoading(true);
await onConfirm();
setIsLoading(false);
onClose();
};

return (
Expand Down Expand Up @@ -57,12 +60,10 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
className="text-lg leading-6 font-medium text-gray-900"
id="modal-headline"
>
{message}
{headline}
</h3>
<div className="mt-2">
<p className="text-sm text-gray-500">
An email will be sent to the applicant.
</p>
<p className="text-sm text-gray-500">{body}</p>
</div>
</div>
</div>
Expand All @@ -88,4 +89,4 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
);
};

export default ConfirmationModal;
export default InformationModal;
4 changes: 1 addition & 3 deletions src/contexts/UserContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ export const UserProvider: React.FC<{ children: React.ReactNode }> = ({
) ?? null;
const isMenteeApplicationsDisabled =
user?.mentee?.some(
(mentee) =>
mentee.state === ApplicationStatus.PENDING ||
mentee.state === ApplicationStatus.APPROVED
(mentee) => mentee.state === ApplicationStatus.PENDING
) ?? false;
const isUserMentee =
user?.mentee?.some(
Expand Down
26 changes: 24 additions & 2 deletions src/hooks/useMentee.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { useQuery } from '@tanstack/react-query';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { API_URL } from '../constants';
import axios from 'axios';
import { type Mentee } from '../types';

const useMentee = (menteeId: string | undefined) => {
const useMentee = (menteeId?: string | undefined) => {
const queryClient = useQueryClient();

const { isLoading, error, data } = useQuery({
queryKey: ['mentee', menteeId],
initialData: null,
Expand All @@ -18,10 +20,30 @@ const useMentee = (menteeId: string | undefined) => {
},
});

const { mutateAsync: revokeApplication, isPending: isRevoking } = useMutation(
{
mutationFn: async () => {
const response = await axios.put(
`${API_URL}/mentees/revoke-application`,
null,
{
withCredentials: true,
}
);
return response.data;
},
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: ['profile'] });
},
}
);

return {
isLoading,
error,
data,
revokeApplication,
isRevoking,
};
};

Expand Down
21 changes: 21 additions & 0 deletions src/hooks/useModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useState } from 'react';

const useModal = () => {
const [isOpen, setIsOpen] = useState(false);

const openModal = () => {
setIsOpen(true);
};

const closeModal = () => {
setIsOpen(false);
};

return {
isOpen,
openModal,
closeModal,
};
};

export default useModal;
13 changes: 8 additions & 5 deletions src/pages/MenteeRegistration/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,14 @@ const MenteeRegistration: React.FC = () => {
className="mt-1 p-2 w-1/2 border rounded-md"
{...register('mentorId')}
>
{mentors?.map((mentor) => (
<option key={mentor.uuid} value={mentor.uuid}>
{mentor.application.firstName} {mentor.application.lastName}
</option>
))}
{mentors
?.filter((mentor) => mentor.availability)
.map((mentor) => (
<option key={mentor.uuid} value={mentor.uuid}>
{mentor.application.firstName}{' '}
{mentor.application.lastName}
</option>
))}
</select>
</div>
<FormInput
Expand Down
34 changes: 25 additions & 9 deletions src/pages/MentorProfile/MentorProfile.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@ import Tooltip from '../../components/Tooltip';
import Loading from '../../assets/svg/Loading';
import MenteeCard from '../../components/MenteeCard';
import ProfilePic from '../../components/ProfilePic';
import InformationModal from '../../components/InformationModal';
import useModal from '../../hooks/useModal';
import useMentee from '../../hooks/useMentee';

const MentorProfile: React.FC = () => {
const { mentorId } = useParams();
const navigate = useNavigate();

const { handleLoginModalOpen } = useLoginModalContext();
const { user, isUserMentor, isMenteeApplicationsDisabled } = useContext(
UserContext
) as UserContextType;
const { user, isUserMentor, isMenteeApplicationsDisabled, isUserMentee } =
useContext(UserContext) as UserContextType;
const [isURLCopied, setIsURLCopied] = useState(false);
const { isOpen, openModal, closeModal } = useModal();

const shareUrl = `${window.location.origin}${location.pathname}`;

const onApply = () => {
Expand All @@ -32,6 +37,7 @@ const MentorProfile: React.FC = () => {
};

const { data: mentor, isLoading } = useMentor(mentorId as string);
const { revokeApplication } = useMentee();

if (isLoading) {
return (
Expand Down Expand Up @@ -104,23 +110,24 @@ const MentorProfile: React.FC = () => {
</div>
</div>

<div className="flex-shrink-0 md:mt-4 md:mt-0">
<div className="flex-shrink-0 md:mt-4">
{!isUserMentor && mentor?.availability && (
<Tooltip
isVisible={isMenteeApplicationsDisabled}
isVisible={isUserMentee}
content="You can apply only for one mentor at a time"
>
<button
className={`text-white font-medium rounded-lg text-sm px-20 md:px-5 py-2.5
${
isMenteeApplicationsDisabled
isUserMentee
? 'bg-gray-400'
: 'bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300'
}`}
onClick={onApply}
disabled={isMenteeApplicationsDisabled}
onClick={isMenteeApplicationsDisabled ? openModal : onApply}
>
Apply
{isMenteeApplicationsDisabled
? 'Withdraw application'
: 'Apply'}
</button>
</Tooltip>
)}
Expand Down Expand Up @@ -222,6 +229,15 @@ const MentorProfile: React.FC = () => {
</div>
</div>
)}
<InformationModal
isOpen={isOpen}
headline="Withdraw your current application"
body={`Are you sure you want to withdraw your current application and apply for ${
mentor?.application.firstName ?? ''
} ${mentor?.application.lastName ?? ''}?`}
onConfirm={revokeApplication}
onClose={closeModal}
/>
</>
);
};
Expand Down
77 changes: 40 additions & 37 deletions src/pages/MyMentees/MyMentees.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,48 @@ const MyMentees: React.FC = () => {
{menteeApplications?.length} Application(s)
</h3>
<div className="h-96 overflow-y-auto md:overflow-y-visible">
{menteeApplications?.map((mentee) => (
<Link
key={mentee?.uuid}
className="bg-white border-sky-100 rounded border p-2 text-black flex items-center cursor-pointer"
to={`/mentor/my-mentees/${mentee?.uuid}`}
>
<div className="flex items-center">
<div>
{mentee.profile.image_url !== '' ? (
<img
src={mentee.profile.image_url}
alt="Mentee Avatar"
className="w-12 h-12 rounded-full mx-auto mr-3 object-cover"
/>
) : (
<div className="w-12 h-12 bg-gray-200 rounded-full mx-auto mr-3 flex items-center justify-center">
<UserIcon />
</div>
)}
</div>
<div>
<p className="font-semibold mb-1">
{mentee.application.firstName} {mentee.application.lastName}
</p>
{mentee.application.isUndergrad ? (
<p className="text-sm mb-2">
{mentee.application.university}
</p>
) : (
<p className="text-sm mb-2">
{mentee.application.position}
{', '}
{mentee.application.company}
{menteeApplications
?.filter((mentee) => mentee.state !== 'revoked')
.map((mentee) => (
<Link
key={mentee?.uuid}
className="bg-white border-sky-100 rounded border p-2 text-black flex items-center cursor-pointer"
to={`/mentor/my-mentees/${mentee?.uuid}`}
>
<div className="flex items-center">
<div>
{mentee.profile.image_url !== '' ? (
<img
src={mentee.profile.image_url}
alt="Mentee Avatar"
className="w-12 h-12 rounded-full mx-auto mr-3 object-cover"
/>
) : (
<div className="w-12 h-12 bg-gray-200 rounded-full mx-auto mr-3 flex items-center justify-center">
<UserIcon />
</div>
)}
</div>
<div>
<p className="font-semibold mb-1">
{mentee.application.firstName}{' '}
{mentee.application.lastName}
</p>
)}
{mentee.application.isUndergrad ? (
<p className="text-sm mb-2">
{mentee.application.university}
</p>
) : (
<p className="text-sm mb-2">
{mentee.application.position}
{', '}
{mentee.application.company}
</p>
)}
</div>
</div>
</div>
</Link>
))}
</Link>
))}
</div>
</div>

Expand Down

0 comments on commit f79046e

Please sign in to comment.