Skip to content

Commit

Permalink
Merge pull request #421 from bettersg/develop
Browse files Browse the repository at this point in the history
3.2.5
  • Loading branch information
sarge1989 authored Aug 18, 2024
2 parents 002f92a + 64d1978 commit 3ed083c
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 28 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,11 @@ We aim to document all these flows eventually, but for now do look at the code t

We currently have 3 environments, prod, uat, and local. The `/integration-tests` folder contains a 4th mocked environment, SIT, which is run in the CI pipeline. Generally, you'd do most of your feature development on the local dev environment, and in a feature branch. Once the feature branch is ready, make a PR to the `develop` branch. Successful merge into the `develop` branch will trigger a UAT deployment. Successful merge from `develop` into `main` will in turn trigger a prod deployment. All these happen through Github Actions.

## Software Requirements
1. `npm`
2. `git`
3. `node >= 18 && <= 20`

### First Time Setup

1. `git clone https://github.com/CheckMateSG/checkMate.git`
Expand All @@ -542,7 +547,7 @@ We currently have 3 environments, prod, uat, and local. The `/integration-tests`

### First time testing (once all above steps are done)

1. Execute the steps in the below section "Each time developing"
1. Execute the steps in the below section [Each Time Developing](#each-time-developing)
2. Go to the chat with the WhatsApp User bot non-prod number and send in `/mockdb`
3. Ensure that the [Firestore Emulator](http://127.0.0.1:4000/firestore) has been populated with some data
4. Send "hi" to the WhatsApp User bot non-prod number. This should trigger the first usage onboarding
Expand Down Expand Up @@ -591,7 +596,7 @@ In the event the Makefile doesn't work,
- Firebase Console - https://console.firebase.google.com/, login with your bettersg email. Go here to manage the product resources
- Adding subcollections - https://stackoverflow.com/questions/47514419/how-to-add-subcollection-to-a-document-in-firebase-cloud-firestore
- Getting started with firestore and firebasehttps://firebase.google.com/docs/functions/get-started
- Getting started with firestore and firebase https://firebase.google.com/docs/functions/get-started
- WhatsApp send message API documentation - https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages
- WhatsApp webhook object documentation - https://developers.facebook.com/docs/whatsapp/cloud-api/webhooks/components
- Telegram Bot API documentation - https://core.telegram.org/bots/api
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
76 changes: 67 additions & 9 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 the
CheckMate Checker's 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 @@ -131,11 +157,43 @@ export default function Dashboard() {
current={programStats.numReferrals}
target={programStats.numReferralTarget}
tooltip_header="Number of Referrals"
tooltip_description="Your accuracy for votes that did not end up as the unsure category"
tooltip_description="Number of people you have referred to join CheckMate"
referral_code={referralCode}
/>
)}
</div>
<Typography className="text-primary-color">
Not keen on the program?{" "}
<a
href="#"
onClick={handleOpen}
className="text-blue-500 underline"
>
Withdraw
</a>{" "}
to participate in just the regular checking.
</Typography>
<Dialog open={dialogOpen} handler={handleOpen}>
<DialogHeader>Withdrawal Confirmation</DialogHeader>
<DialogBody>
Please confirm that you would like to withdraw from the program.
If you are keen to restart the program in 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 3ed083c

Please sign in to comment.