Skip to content

Commit

Permalink
Merge pull request #419 from bettersg/feat/checker-program-ux-improve…
Browse files Browse the repository at this point in the history
…ments

checker UX improvements
  • Loading branch information
sarge1989 authored Aug 18, 2024
2 parents 7ee9b76 + 1f6509d commit 375c74a
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 25 deletions.
73 changes: 58 additions & 15 deletions checkers-app/src/components/dashboard/ProgressCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,26 @@ interface PropType {
img_src: string;
current: number;
target: number;
isPercentageTarget?: boolean;
tooltip_header: string;
tooltip_description: string | React.ReactNode;
referral_code?: string | null;
}

export default function ProgressCard(Prop: PropType) {
const progressValue = Prop.isPercentageTarget
? Math.min(Prop.current, 100)
: Math.min((Prop.current / Prop.target) * 100, 100);
const targetPosition = Math.min((Prop.target / 100) * 100, 100);

const targetString = `${Prop.current} / ${Prop.target}`;

const barColor = Prop.isPercentageTarget
? Prop.current < Prop.target
? "red"
: "green"
: "orange";

return (
<Card className="dark:bg-dark-component-color dark:shadow-dark-component-color/20">
<CardBody className="flex flex-col place-items-center">
Expand All @@ -38,22 +52,51 @@ export default function ProgressCard(Prop: PropType) {
)}
</div>

<div className="mb-2 flex items-center justify-between gap-4">
<Typography className="text-primary-color" variant="h6">
{Prop.current / Prop.target < 0.75
? "Some way to go..."
: Prop.current / Prop.target < 1
? "Almost there..."
: "Completed ✅"}
</Typography>
<Typography className="text-primary-color" variant="h6">
{Prop.current} / {Prop.target}
</Typography>
{Prop.isPercentageTarget && (
<div className="mb-2 flex items-center justify-between gap-4">
<div className="relative w-full pb-5">
<Typography
className="absolute transform -translate-x-1/2 text-primary-color"
style={{ left: `${targetPosition}%` }}
variant="h6"
>
{`${Prop.target}%`}
</Typography>
</div>
</div>
)}

{!Prop.isPercentageTarget && (
<div className="mb-2 flex items-center justify-between gap-4">
<Typography className="text-primary-color" variant="h6">
{Prop.current / Prop.target < 0.75
? "Some way to go..."
: Prop.current / Prop.target < 1
? "Almost there..."
: "Completed ✅"}
</Typography>
<Typography className="text-primary-color" variant="h6">
{targetString}
</Typography>
</div>
)}

<div className="relative w-full">
<Progress
value={progressValue}
color={barColor}
className="relative z-10"
size="lg"
label={Prop.isPercentageTarget}
/>
{/* Vertical Line */}
{Prop.isPercentageTarget && (
<div
className="absolute top-0 left-0 h-full border-l-4 border-primary-color z-20"
style={{ left: `${targetPosition - 1}%` }}
></div>
)}
</div>
<Progress
value={Math.min((Prop.current / Prop.target) * 100, 100)}
color="orange"
/>
</div>
</CardBody>
</Card>
Expand Down
74 changes: 66 additions & 8 deletions checkers-app/src/components/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@ import { useUser } from "../../providers/UserContext";
import StatCard from "./StatsCard";
import ProgressCard from "./ProgressCard";
import { Checker, ProgramStats } from "../../types";
import { getChecker } from "../../services/api";
import { getChecker, withdrawCheckerProgram } from "../../services/api";
import Loading from "../common/Loading";
import { CelebrationDialog } from "./CelebrationDialog";
import { useNavigate } from "react-router-dom";
import {
Button,
Dialog,
DialogHeader,
DialogBody,
DialogFooter,
} from "@material-tailwind/react";
//TODO: link to firebase

export default function Dashboard() {
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(false);
const { checkerDetails, setCheckerDetails } = useUser();
const [totalVotes, setTotalVotes] = useState<number>(0);
Expand All @@ -22,6 +31,16 @@ export default function Dashboard() {
useState<boolean>(false);
const [programStats, setProgramStats] = useState<null | ProgramStats>(null);
const [referralCode, setReferralCode] = useState<string | null>(null);
const [dialogOpen, setDialogOpen] = useState(false);
const handleOpen = () => setDialogOpen(!dialogOpen);
const handleConfirm = async () => {
if (!checkerDetails.checkerId) {
return;
}
await withdrawCheckerProgram(checkerDetails.checkerId);
setDialogOpen(false);
navigate(0);
};
const isProd = import.meta.env.MODE === "production";

useEffect(() => {
Expand Down Expand Up @@ -75,17 +94,19 @@ export default function Dashboard() {
)} */}
{isOnProgram && programStats ? (
<div>
<Typography variant="h5" className="text-primary-color">
CheckMate Program Progress
<Typography variant="h6" className="text-primary-color">
Up for a challenge? Attain these 3 milestones to finish our checker
program and get certified.
</Typography>
<div className="my-6 flex flex-col gap-y-4 mx-2">
<ProgressCard
name="Messages Voted On"
img_src="/votes.png"
current={programStats.numVotes}
target={programStats.numVotesTarget}
isPercentageTarget={false}
tooltip_header="Messages Voted On"
tooltip_description="Number of messages that you have voted on (passing does not count)"
tooltip_description={`Number of messages that you have voted on (passing does not count). You need to vote on at least ${programStats.numVotesTarget} messages.`}
/>
<ProgressCard
name="Voting Accuracy"
Expand All @@ -94,19 +115,23 @@ export default function Dashboard() {
programStats.accuracy === null ? 0 : programStats.accuracy * 100
}
target={programStats.accuracyTarget * 100}
isPercentageTarget={true}
tooltip_header="Voting Accuracy (%)"
tooltip_description="Your accuracy for votes that did not end up as the unsure category"
tooltip_description={`% of your votes that match the majority vote. You need to obtain at least ${
programStats.accuracyTarget * 100
}% accuracy. Messages where the majority category does not receive 50% of the votes are excluded from this calculation.`}
/>
{programStats.numReportTarget > 0 && (
{programStats.numReportTarget > -1 && (
<ProgressCard
name="Messages Reported"
img_src="/message.png"
current={programStats.numReports}
target={programStats.numReportTarget}
isPercentageTarget={false}
tooltip_header="Messages Reported"
tooltip_description={
<>
Report messages via our{" "}
Number of messages that you have submitted to our{" "}
<a
href={
isProd
Expand All @@ -119,7 +144,8 @@ export default function Dashboard() {
>
WhatsApp Bot
</a>
.
You need to submit at least ${programStats.numReportTarget}{" "}
messages
</>
}
/>
Expand All @@ -136,6 +162,38 @@ export default function Dashboard() {
/>
)}
</div>
<Typography className="text-primary-color">
Not keen on the program?{" "}
<a
href="#"
onClick={handleOpen}
className="text-blue-500 underline"
>
Withdraw
</a>{" "}
to just perform the regular checking.
</Typography>
<Dialog open={dialogOpen} handler={handleOpen}>
<DialogHeader>Confirmation</DialogHeader>
<DialogBody>
Please confirm that you would like to withdraw from the program.
If you are keen to restart the program in the future, you can do
so at the menu bar accessible from the top right.
</DialogBody>
<DialogFooter>
<Button
variant="text"
color="red"
onClick={handleOpen}
className="mr-1"
>
<span>Cancel</span>
</Button>
<Button variant="gradient" color="green" onClick={handleConfirm}>
<span>Confirm</span>
</Button>
</DialogFooter>
</Dialog>
</div>
) : (
<div>
Expand Down
12 changes: 12 additions & 0 deletions checkers-app/src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,18 @@ export const resetCheckerProgram = async (checkerId: string) => {
).data;
};

export const withdrawCheckerProgram = async (checkerId: string) => {
if (!checkerId) {
throw new Error("Checker ID missing in resetCheckerProgram.");
}
const checkerUpdateData: updateChecker = {
programData: "withdraw",
};
return (
await axiosInstance.patch(`/api/checkers/${checkerId}`, checkerUpdateData)
).data;
};

export const activateChecker = async (checkerId: string) => {
if (!checkerId) {
throw new Error("Checker ID missing in activateChecker.");
Expand Down
26 changes: 25 additions & 1 deletion functions/src/definitions/api/handlers/patchChecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ const patchCheckerHandler = async (req: Request, res: Response) => {
if (keys.includes("programData")) {
if (
typeof body.programData === "string" &&
(body.programData === "reset" || body.programData === "complete")
(body.programData === "reset" ||
body.programData === "complete" ||
body.programData === "withdraw")
) {
if (body.programData === "reset") {
const thresholds = await getThresholds()
Expand Down Expand Up @@ -83,6 +85,28 @@ const patchCheckerHandler = async (req: Request, res: Response) => {
//delete programdata from body
delete body.programData
body["programData.isOnProgram"] = false
} else if (body.programData === "withdraw") {
const thresholds = await getThresholds()
body.programData = {
isOnProgram: false,
programStart: null,
programEnd: null,
numVotesTarget: thresholds.volunteerProgramVotesRequirement ?? 0, //target number of messages voted on to complete program
numReferralTarget:
thresholds.volunteerProgramReferralRequirement ?? 0, //target number of referrals made to complete program
numReportTarget: thresholds.volunteerProgramReportRequirement ?? 0, //number of non-trivial messages sent in to complete program
accuracyTarget: thresholds.volunteerProgramAccuracyRequirement ?? 0, //target accuracy of non-unsure votes
numVotesAtProgramStart: checker.numVoted ?? 0,
numReferralsAtProgramStart: checker.numReferred ?? 0,
numReportsAtProgramStart: checker.numReported ?? 0,
numCorrectVotesAtProgramStart: checker.numCorrectVotes ?? 0,
numNonUnsureVotesAtProgramStart: checker.numNonUnsureVotes ?? 0,
numVotesAtProgramEnd: null,
numReferralsAtProgramEnd: null,
numReportsAtProgramEnd: null,
numCorrectVotesAtProgramEnd: null,
numNonUnsureVotesAtProgramEnd: null,
}
}
} else {
return res
Expand Down
2 changes: 1 addition & 1 deletion functions/src/definitions/api/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ interface updateChecker {
numVerifiedLinks?: number
preferredPlatform?: string | null
lastVotedTimestamp?: null
programData?: "reset" | "complete"
programData?: "reset" | "complete" | "withdraw"
}

interface Checker {
Expand Down

0 comments on commit 375c74a

Please sign in to comment.