Skip to content

Commit

Permalink
Show confirmation before sending mentorship request (#84)
Browse files Browse the repository at this point in the history
* Show confirmation before sending mentorship request

* Fix tests

* Show toast after sending email

* OOps

* Add report page to mentor

* Get the last reportId when sending request to mentor
  • Loading branch information
cipick authored Oct 24, 2023
1 parent e300293 commit 05e6f79
Show file tree
Hide file tree
Showing 14 changed files with 297 additions and 80 deletions.
21 changes: 17 additions & 4 deletions client/src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,36 @@ interface IButton {
onClick?: () => void;
type?: "submit" | "button";
color?: "teal" | "white";
disabled?: boolean;
}

const variation = {
teal: "rounded bg-teal-600 py-2 px-4 font-medium text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600",
teal: "whitespace-nowrap rounded bg-teal-600 py-2 px-4 font-medium text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600",
white:
"rounded bg-white py-2 px-4 font-medium text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50",
"whitespace-nowrap rounded bg-white py-2 px-4 font-medium text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50",
};

const Button = ({ children, to, onClick, type, color = "teal" }: IButton) => {
const Button = ({
children,
to,
onClick,
type,
color = "teal",
disabled,
}: IButton) => {
const className = variation[color];

return to ? (
<Link to={to} className={className}>
{children}
</Link>
) : (
<button className={className} onClick={onClick} type={type}>
<button
className={className}
onClick={onClick}
type={type}
disabled={disabled}
>
{children}
</button>
);
Expand Down
65 changes: 37 additions & 28 deletions client/src/components/Confirm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { Fragment, useRef } from "react";
import { Dialog, Transition } from "@headlessui/react";

const Confirm = ({ open, setOpen, handleComplete }) => {
const Confirm = ({
header,
body,
footer,
buttonText,
open,
setOpen,
handleComplete,
}) => {
const cancelButtonRef = useRef(null);

return (
Expand All @@ -25,7 +33,7 @@ const Confirm = ({ open, setOpen, handleComplete }) => {
</Transition.Child>

<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div className="flex min-h-full items-end justify-center p-4 sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
Expand All @@ -37,42 +45,43 @@ const Confirm = ({ open, setOpen, handleComplete }) => {
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
<div>
<div className="mt-3 text-center sm:mt-5">
<div className="mt-3 sm:mt-5">
<Dialog.Title
as="h3"
className="text-base font-semibold leading-6 text-gray-900"
>
Ești sigur că vrei să finalizezi evaluarea?
{header}
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-gray-500">
Dacă finalizezi acum, persoanele care nu au răspuns la
chestionarul de evaluare nu vor putea să mai completeze.
Asigură-te că ai toate răspunsurile înainte de a face
această acțiune
</p>
<p className="text-sm text-gray-500">{body}</p>
</div>
</div>
</div>
<div className="mt-5 sm:mt-6 sm:grid sm:grid-flow-row-dense sm:grid-cols-2 sm:gap-3">
<button
type="button"
className="inline-flex w-full justify-center rounded-md bg-teal-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600 sm:col-start-2"
onClick={() => {
handleComplete();
setOpen(false);
}}
>
Finalizează evaluarea
</button>
<button
type="button"
className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:col-start-1 sm:mt-0"
onClick={() => setOpen(false)}
ref={cancelButtonRef}
>
Renunță
</button>
{footer ? (
footer
) : (
<>
<button
type="button"
className="inline-flex w-full justify-center rounded-md bg-teal-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600 sm:col-start-2"
onClick={() => {
handleComplete();
setOpen(false);
}}
>
{buttonText}
</button>
<button
type="button"
className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:col-start-1 sm:mt-0"
onClick={() => setOpen(false)}
ref={cancelButtonRef}
>
Renunță
</button>
</>
)}
</div>
</Dialog.Panel>
</Transition.Child>
Expand Down
5 changes: 5 additions & 0 deletions client/src/components/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ const Menu = () => {
{menu.map((menuItem) => (
<li>
<NavLink
target={
menuItem.link.startsWith("https://crestem.ong")
? "_blank"
: undefined
}
to={menuItem.link}
className={({ isActive, isPending }) =>
`flex flex-wrap border-b-2 border-transparent px-3 py-2 font-medium items-center text-gray-900 border-teal-600 ${
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/ResultsByDimension/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const ResultsByDimension = ({
scoreByEvaluation,
}: {
scoreByEvaluation: {
id: number;
id: string;
name: string;
score: number;
tags: string[];
Expand Down
2 changes: 1 addition & 1 deletion client/src/lib/score.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const calcScoreByDimension = ({
matrix: Matrix;
}) => {
if (!matrix) {
return null;
return undefined;
}
const object = evaluationsCompleted.reduce(
(acc, evaluation) =>
Expand Down
118 changes: 85 additions & 33 deletions client/src/pages/authenticated/MentorsList/MentorCard.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React from "react";
import React, { useEffect, useState } from "react";
import {
EnvelopeIcon,
SignalSlashIcon,
UserIcon,
} from "@heroicons/react/20/solid";
import { Link } from "react-router-dom";
import { useCreateMentorshipRequestMutation } from "@/redux/api/userApi";
import Confirm from "@/components/Confirm";
import { useAppSelector } from "@/redux/store";
import { selectHasFinishedReports } from "@/redux/features/userSlice";
import Button from "@/components/Button";
import { toast } from "react-toastify";
import { Dimension } from "@/redux/api/types";

const MentorCard = ({
id,
Expand All @@ -14,52 +19,99 @@ const MentorCard = ({
lastName,
dimensions,
available,
}: {
id: string;
userId: string;
firstName: string;
lastName: string;
dimensions: Dimension[];
available: boolean;
}) => {
const [createMentorshipRequest] = useCreateMentorshipRequestMutation();

const [openConfirm, setOpenConfirm] = useState(false);
const [createMentorshipRequest, { isSuccess, isError }] =
useCreateMentorshipRequestMutation();
const hasFinishedReports = useAppSelector(selectHasFinishedReports);
const handleClickEmail = () => {
createMentorshipRequest({ mentor: +id, user: +userId });
setOpenConfirm(true);
};

useEffect(() => {
if (isSuccess) {
toast.success("Trimis cu succes");
}
}, [isSuccess]);

useEffect(() => {
if (isError) {
toast.error("Această acțiune nu a putut fi realizată");
}
}, [isError]);

return (
<li className="flex flex-col justify-between overflow-hidden bg-white sm:rounded-lg sm:shadow text-center divide-y ">
<Confirm
open={openConfirm}
setOpen={setOpenConfirm}
handleComplete={handleClickEmail}
header="Contactează persoana resursă"
body="Trimiteți persoanei resursă un email prin care o informați că doriți să colaborați. Emailul va conține rezultatele ultimei evaluări și adresa de email a organizației, pentru a vă putea contacta."
buttonText="Confirm"
footer={
<>
<button
type="button"
className="inline-flex w-full justify-center rounded-md bg-teal-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600 sm:col-start-2"
onClick={() => {
createMentorshipRequest({ mentor: +id, user: +userId });
setOpenConfirm(false);
}}
>
Confirm
</button>
<button
type="button"
className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:col-start-1 sm:mt-0"
onClick={() => setOpenConfirm(false)}
>
Renunță
</button>
</>
}
/>
<div className="text-center">
<UserIcon className="h-24 w-24 inline mr-2" />
<h3 className="mt-6 text-xl font-semibold leading-8 tracking-tight text-gray-900">
{firstName} {lastName}
</h3>
<p className="text-base leading-7 text-gray-500">
{dimensions?.map((dimension) => dimension.name).join("; ")}
{dimensions?.map((dimension) => dimension?.name).join("; ")}
</p>
</div>
<ul role="list" className="mt-6 flex gap-x-6 py-4 divide-x">
<li className="w-full">
<Link
to={`/users/${id}`}
className="text-teal-600 hover:text-teal-700"
<ul
role="list"
className="mt-6 flex justify-center items-center gap-x-2 py-4"
>
<Button to={`/users/${id}`}>
<span className="sr-only">Twitter</span>
<UserIcon className="h-5 w-5 inline mr-2" />
<span className="inline">Vezi profil</span>
</Button>
{available ? (
<Button
color="white"
disabled={!hasFinishedReports}
onClick={handleClickEmail}
>
<span className="sr-only">Twitter</span>
<UserIcon className="h-5 w-5 inline mr-2" />
<span className="inline">Vezi profil</span>
</Link>
</li>
<li className="w-full">
{available ? (
<div
className="text-gray-400 hover:text-gray-500 cursor-pointer"
onClick={handleClickEmail}
>
<span className="sr-only">LinkedIn</span>
<EnvelopeIcon className="h-5 w-5 inline mr-2" />
<span>Trimite email</span>
</div>
) : (
<div className="select-none">
<SignalSlashIcon className="h-5 w-5 inline mr-2 text-gray-400" />
<span className="text-sm text-gray-700 italic">Indisponibil</span>
</div>
)}
</li>
<span className="sr-only">LinkedIn</span>
<EnvelopeIcon className="h-5 w-5 inline mr-2" />
<span>Trimite email</span>
</Button>
) : (
<Button color="white" disabled>
<SignalSlashIcon className="h-5 w-5 inline mr-2 text-gray-400" />
<span className="text-sm text-gray-700 italic">Indisponibil</span>
</Button>
)}
</ul>
</li>
);
Expand Down
10 changes: 9 additions & 1 deletion client/src/pages/authenticated/Report/ReportInProgress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ const CallToAction = ({ reportId }: { reportId: number }) => {

return (
<>
<Confirm open={open} setOpen={setOpen} handleComplete={handleComplete} />
<Confirm
header="Ești sigur că vrei să finalizezi evaluarea?"
body="Dacă finalizezi acum, persoanele care nu au răspuns la chestionarul de evaluare nu vor putea să mai completeze.
Asigură-te că ai toate răspunsurile înainte de a face această acțiune"
buttonText="Finalizează evaluarea"
open={open}
setOpen={setOpen}
handleComplete={handleComplete}
/>
<Button onClick={() => setOpen(true)}>Finalizează evaluare</Button>
</>
);
Expand Down
52 changes: 52 additions & 0 deletions client/src/pages/mentor/Report/ReportResults.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from "react";
import Stats from "@/components/Stats";
import { calcScore } from "@/lib/score";
import ResultsByDimension from "@/components/ResultsByDimension";
import { Evaluation, Report } from "@/redux/api/types";

const ReportResults = ({
report,
evaluationsCompleted,
scoreByEvaluation,
}: {
report: Report;
evaluationsCompleted: Evaluation[];
scoreByEvaluation?: {
id: string;
name: string;
score: number;
tags: string[];
}[];
}) => {
const startDate = new Date(report.createdAt).getTime();
const endDate = new Date(report.deadline).getTime();

const period = Math.ceil(
Math.abs(endDate - startDate) / (1000 * 60 * 60 * 24)
);
return (
<div>
<Stats
data={[
{
label: "Perioadă de completare",
value: `${period} zile`,
},
{
label: "Total completări",
value: `${evaluationsCompleted.length || 0}`,
},
{
label: " Scor total",
value: `${calcScore(evaluationsCompleted) || 0}%`,
},
]}
/>
{scoreByEvaluation && (
<ResultsByDimension scoreByEvaluation={scoreByEvaluation} />
)}
</div>
);
};

export default ReportResults;
Loading

1 comment on commit 05e6f79

@vercel
Copy link

@vercel vercel bot commented on 05e6f79 Oct 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

crestem-ong – ./

crestem-ong-git-main-code4romania.vercel.app
app.crestem.ong
crestem-ong-code4romania.vercel.app

Please sign in to comment.