Skip to content

Commit df0a67a

Browse files
authored
Merge pull request #382 from CodebilityDev/ian/applicant-fixes
Ian/applicant fixes
2 parents d0cb9ca + f4949e5 commit df0a67a

16 files changed

Lines changed: 712 additions & 80 deletions

‎apps/codebility/app/applicant/waiting/_components/applicantStep3.tsx‎

Lines changed: 100 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,58 @@
1-
import React from "react";
1+
import React, { useState } from "react";
22
import Link from "next/link";
33
import Logo from "@/components/shared/Logo";
44
import { Button } from "@/components/ui/button";
5+
import { Checkbox } from "@codevs/ui/checkbox";
6+
import { ApplicantType } from "../_service/type";
7+
import { applicantUpdateJoinedStatus } from "../_service/action";
58

69
export default function ApplicantStep3({
710
setActiveStep,
811
user,
12+
applicantData,
913
}: {
1014
setActiveStep: React.Dispatch<React.SetStateAction<number>>;
1115
user: any;
16+
applicantData: ApplicantType;
1217
}) {
18+
const [joinedDiscord, setJoinedDiscord] = useState(applicantData.joined_discord);
19+
const [joinedMessenger, setJoinedMessenger] = useState(applicantData.joined_messenger);
20+
const [isUpdating, setIsUpdating] = useState(false);
21+
1322
const handleTakeTest = () => {
1423
setActiveStep(1);
1524
};
1625

26+
const handleDiscordChange = async (checked: boolean) => {
27+
setIsUpdating(true);
28+
try {
29+
await applicantUpdateJoinedStatus({
30+
applicantId: applicantData.id,
31+
joinedDiscord: checked,
32+
});
33+
setJoinedDiscord(checked);
34+
} catch (error) {
35+
console.error("Error updating Discord status:", error);
36+
} finally {
37+
setIsUpdating(false);
38+
}
39+
};
40+
41+
const handleMessengerChange = async (checked: boolean) => {
42+
setIsUpdating(true);
43+
try {
44+
await applicantUpdateJoinedStatus({
45+
applicantId: applicantData.id,
46+
joinedMessenger: checked,
47+
});
48+
setJoinedMessenger(checked);
49+
} catch (error) {
50+
console.error("Error updating Messenger status:", error);
51+
} finally {
52+
setIsUpdating(false);
53+
}
54+
};
55+
1756
return (
1857
<div className="my-20 flex flex-col items-center gap-8 text-center lg:gap-10">
1958
<div className="flex flex-col items-center gap-4">
@@ -39,25 +78,67 @@ export default function ApplicantStep3({
3978
</p>
4079
</div>
4180

42-
<div className="flex flex-col gap-6 sm:flex-row sm:gap-4">
43-
<Link
44-
href={process.env.NEXT_PUBLIC_MESSENGER_WAITLIST || ""}
45-
target="_blank"
46-
>
47-
<Button className="from-customTeal to-customViolet-100 h-10 rounded-full bg-gradient-to-r via-customBlue-100 p-0.5 hover:bg-gradient-to-br xl:h-12">
48-
<span className="bg-black-100 flex h-full w-full items-center justify-center rounded-full px-4 text-lg text-white lg:text-lg">
49-
Join Waiting List
50-
</span>
51-
</Button>
52-
</Link>
81+
<div className="flex flex-col gap-8 sm:flex-row sm:gap-6">
82+
{/* Messenger Section */}
83+
<div className="flex flex-col items-center gap-5">
84+
{!joinedMessenger && (
85+
<Link
86+
href={process.env.NEXT_PUBLIC_MESSENGER_WAITLIST || ""}
87+
target="_blank"
88+
>
89+
<Button className="from-customTeal to-customViolet-100 h-10 rounded-full bg-gradient-to-r via-customBlue-100 p-0.5 hover:bg-gradient-to-br xl:h-12">
90+
<span className="bg-black-100 flex h-full w-full items-center justify-center rounded-full px-4 text-lg text-white lg:text-lg">
91+
Join Waiting List
92+
</span>
93+
</Button>
94+
</Link>
95+
)}
96+
97+
<div className="flex items-center gap-3 p-3">
98+
<Checkbox
99+
id="joined-messenger"
100+
checked={joinedMessenger}
101+
onCheckedChange={handleMessengerChange}
102+
disabled={isUpdating}
103+
className="h-5 w-5"
104+
/>
105+
<label
106+
htmlFor="joined-messenger"
107+
className="text-base text-gray-200 cursor-pointer font-medium"
108+
>
109+
I have joined the waiting list
110+
</label>
111+
</div>
112+
</div>
53113

54-
<Link href={process.env.NEXT_PUBLIC_DISCORD_LINK || ""} target="_blank">
55-
<Button className="from-customTeal to-customViolet-100 h-10 rounded-full bg-gradient-to-r via-customBlue-100 p-0.5 hover:bg-gradient-to-br xl:h-12">
56-
<span className="bg-black-100 flex h-full w-full items-center justify-center rounded-full px-4 text-lg text-white lg:text-lg">
57-
Join Discord Server
58-
</span>
59-
</Button>
60-
</Link>
114+
{/* Discord Section */}
115+
<div className="flex flex-col items-center gap-5">
116+
{!joinedDiscord && (
117+
<Link href={process.env.NEXT_PUBLIC_DISCORD_LINK || ""} target="_blank">
118+
<Button className="from-customTeal to-customViolet-100 h-10 rounded-full bg-gradient-to-r via-customBlue-100 p-0.5 hover:bg-gradient-to-br xl:h-12">
119+
<span className="bg-black-100 flex h-full w-full items-center justify-center rounded-full px-4 text-lg text-white lg:text-lg">
120+
Join Discord Server
121+
</span>
122+
</Button>
123+
</Link>
124+
)}
125+
126+
<div className="flex items-center gap-3 p-3">
127+
<Checkbox
128+
id="joined-discord"
129+
checked={joinedDiscord}
130+
onCheckedChange={handleDiscordChange}
131+
disabled={isUpdating}
132+
className="h-5 w-5"
133+
/>
134+
<label
135+
htmlFor="joined-discord"
136+
className="text-base text-gray-200 cursor-pointer font-medium"
137+
>
138+
I have joined Discord and sent the message
139+
</label>
140+
</div>
141+
</div>
61142
</div>
62143
</div>
63144
);

‎apps/codebility/app/applicant/waiting/_components/applicationSteps.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export default function ApplicationSteps({
8383
</StepperContent>
8484

8585
<StepperContent step={2}>
86-
<ApplicantStep3 setActiveStep={setActiveStep} user={user} />
86+
<ApplicantStep3 setActiveStep={setActiveStep} user={user} applicantData={applicantData} />
8787
</StepperContent>
8888
</StepperBody>
8989
</Stepper>

‎apps/codebility/app/applicant/waiting/_service/action.ts‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,44 @@ export async function applicantUpdateTestSubmission({
132132
} catch (error) {
133133
console.error("Error updating test submission:", error);
134134
}
135+
}
136+
137+
export async function applicantUpdateJoinedStatus({
138+
applicantId,
139+
joinedDiscord,
140+
joinedMessenger,
141+
}: {
142+
applicantId: string;
143+
joinedDiscord?: boolean;
144+
joinedMessenger?: boolean;
145+
}) {
146+
try {
147+
const supabase = await createClientServerComponent();
148+
149+
const updateData: any = {
150+
updated_at: new Date(),
151+
};
152+
153+
if (joinedDiscord !== undefined) {
154+
updateData.joined_discord = joinedDiscord;
155+
}
156+
157+
if (joinedMessenger !== undefined) {
158+
updateData.joined_messenger = joinedMessenger;
159+
}
160+
161+
const { data, error } = await supabase
162+
.from("applicant")
163+
.update(updateData)
164+
.eq("id", applicantId);
165+
166+
if (error) {
167+
console.error("Error updating applicant joined status:", error);
168+
return undefined;
169+
}
170+
171+
revalidatePath("/applicant/waiting");
172+
} catch (error) {
173+
console.error("Error updating joined status:", error);
174+
}
135175
}

‎apps/codebility/app/applicant/waiting/_service/type.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export const applicantSchema = z.object({
55
codev_id: z.string(),
66
test_taken: z.string().datetime({ offset: true }).nullable(),
77
fork_url: z.string().nullable(),
8+
joined_discord: z.boolean(),
9+
joined_messenger: z.boolean(),
810
created_at: z.string().datetime({ offset: true }),
911
updated_at: z.string().datetime({ offset: true }),
1012
})

‎apps/codebility/app/home/applicants/_components/_table/applicantActionButton.tsx‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ import {
4343
import {
4444
sendDenyEmail,
4545
sendFailedTestEmail,
46-
sendOnboardingReminder,
46+
sendOnboardingReminderWithUpdate,
4747
sendPassedTestEmail,
48-
sendTestReminder,
48+
sendTestReminderWithUpdate,
4949
} from "../../_service/emailAction";
5050
import { NewApplicantType } from "../../_service/types";
5151

@@ -288,7 +288,11 @@ export default function ApplicantActionButton({
288288
const handleRemindToTakeTest = async () => {
289289
setIsLoading(true);
290290
try {
291-
await sendTestReminder(applicant.email_address);
291+
await sendTestReminderWithUpdate({
292+
email: applicant.email_address,
293+
name: `${applicant.first_name} ${applicant.last_name}`,
294+
applicantId: applicant.id
295+
});
292296
setIsDialogOpen(false);
293297
setDialogState(null);
294298
toast({
@@ -310,7 +314,11 @@ export default function ApplicantActionButton({
310314
const handleRemindToOnboarding = async () => {
311315
setIsLoading(true);
312316
try {
313-
await sendOnboardingReminder(applicant.email_address);
317+
await sendOnboardingReminderWithUpdate({
318+
email: applicant.email_address,
319+
name: `${applicant.first_name} ${applicant.last_name}`,
320+
applicantId: applicant.id
321+
});
314322
setIsDialogOpen(false);
315323
setDialogState(null);
316324
toast({

‎apps/codebility/app/home/applicants/_components/_table/applicantActionConfig.ts‎

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import { ActionConfig, ActionType } from "./applicantActionTypes";
22
import {
33
acceptApplicantAction,
4-
deleteApplicantAction,
4+
deleteApplicantById,
55
denyApplicantAction,
66
moveApplicantToApplyingAction,
77
moveApplicantToOnboardingAction,
88
moveApplicantToTestingAction,
99
passApplicantTestAction,
1010
} from "../../_service/action";
1111
import {
12-
sendDenyEmail,
13-
sendFailedTestEmail,
14-
sendOnboardingReminder,
15-
sendPassedTestEmail,
16-
sendTestReminder,
12+
sendDenyEmailForConfig,
13+
sendFailedTestEmailForConfig,
14+
sendOnboardingReminderForConfig,
15+
sendPassedTestEmailForConfig,
16+
sendTestReminderForConfig,
1717
} from "../../_service/emailAction";
1818

1919
export const ACTION_CONFIG: Record<ActionType, ActionConfig> = {
@@ -49,7 +49,7 @@ export const ACTION_CONFIG: Record<ActionType, ActionConfig> = {
4949
successTitle: "Test Passed",
5050
successMessage: (firstName, lastName) => `${firstName} ${lastName} has passed the test.`,
5151
errorMessage: "Failed to mark test as passed. Please try again later.",
52-
emailAction: sendPassedTestEmail,
52+
emailAction: sendPassedTestEmailForConfig,
5353
requiresConfirmation: true,
5454
confirmTitle: "Pass Test",
5555
confirmDescription: "Are you sure you want to mark this test as passed?",
@@ -59,7 +59,7 @@ export const ACTION_CONFIG: Record<ActionType, ActionConfig> = {
5959
successTitle: "Applicant Accepted",
6060
successMessage: (firstName, lastName) => `${firstName} ${lastName} has been accepted.`,
6161
errorMessage: "Failed to accept applicant. Please try again later.",
62-
emailAction: sendPassedTestEmail,
62+
emailAction: sendPassedTestEmailForConfig,
6363
requiresConfirmation: true,
6464
confirmTitle: "Accept Applicant",
6565
confirmDescription: "Are you sure you want to accept this applicant?",
@@ -69,25 +69,25 @@ export const ACTION_CONFIG: Record<ActionType, ActionConfig> = {
6969
successTitle: "Applicant Denied",
7070
successMessage: (firstName, lastName) => `${firstName} ${lastName} has been denied.`,
7171
errorMessage: "Failed to deny applicant. Please try again later.",
72-
emailAction: sendDenyEmail,
72+
emailAction: sendDenyEmailForConfig,
7373
requiresConfirmation: true,
7474
confirmTitle: "Deny Applicant",
7575
confirmDescription: "Are you sure you want to deny this applicant?",
7676
variant: "destructive",
7777
},
7878
fail: {
79-
action: (id: string) => denyApplicantAction(id), // Assuming fail is same as deny
79+
action: denyApplicantAction,
8080
successTitle: "Test Failed",
8181
successMessage: (firstName, lastName) => `${firstName} ${lastName} has failed the test.`,
8282
errorMessage: "Failed to mark test as failed. Please try again later.",
83-
emailAction: sendFailedTestEmail,
83+
emailAction: sendFailedTestEmailForConfig,
8484
requiresConfirmation: true,
8585
confirmTitle: "Fail Test",
8686
confirmDescription: "Are you sure you want to mark this test as failed?",
8787
variant: "destructive",
8888
},
8989
delete: {
90-
action: deleteApplicantAction,
90+
action: deleteApplicantById,
9191
successTitle: "Applicant Deleted",
9292
successMessage: (firstName, lastName) => `${firstName} ${lastName} has been deleted.`,
9393
errorMessage: "Failed to delete applicant. Please try again later.",
@@ -101,34 +101,34 @@ export const ACTION_CONFIG: Record<ActionType, ActionConfig> = {
101101
successTitle: "Applicant Denied",
102102
successMessage: (firstName, lastName) => `${firstName} ${lastName} has been denied.`,
103103
errorMessage: "Failed to deny applicant. Please try again later.",
104-
emailAction: sendDenyEmail,
104+
emailAction: sendDenyEmailForConfig,
105105
requiresConfirmation: true,
106106
confirmTitle: "Deny Applicant",
107107
confirmDescription: "Are you sure you want to deny this applicant?",
108108
variant: "destructive",
109109
},
110110
remindToTakeTest: {
111-
action: () => Promise.resolve(), // No database action needed for reminders
111+
action: () => Promise.resolve(),
112112
successTitle: "Reminder Sent",
113113
successMessage: (firstName, lastName) => `Test reminder sent to ${firstName} ${lastName}.`,
114114
errorMessage: "Failed to send test reminder. Please try again later.",
115-
emailAction: sendTestReminder,
115+
emailAction: sendTestReminderForConfig,
116116
requiresConfirmation: true,
117117
confirmTitle: "Send Test Reminder",
118118
confirmDescription: "Are you sure you want to send a test reminder to this applicant?",
119119
},
120120
remindToOnboarding: {
121-
action: () => Promise.resolve(), // No database action needed for reminders
121+
action: () => Promise.resolve(),
122122
successTitle: "Reminder Sent",
123123
successMessage: (firstName, lastName) => `Onboarding reminder sent to ${firstName} ${lastName}.`,
124124
errorMessage: "Failed to send onboarding reminder. Please try again later.",
125-
emailAction: sendOnboardingReminder,
125+
emailAction: sendOnboardingReminderForConfig,
126126
requiresConfirmation: true,
127127
confirmTitle: "Send Onboarding Reminder",
128128
confirmDescription: "Are you sure you want to send an onboarding reminder to this applicant?",
129129
},
130130
viewDetails: {
131-
action: () => Promise.resolve(), // No action needed for view details
131+
action: () => Promise.resolve(),
132132
successTitle: "Details Viewed",
133133
successMessage: (firstName, lastName) => `Viewing details for ${firstName} ${lastName}.`,
134134
errorMessage: "Failed to view details. Please try again later.",

0 commit comments

Comments
 (0)