Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions app/(api)/_actions/auth/verifyCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,24 @@

import { updateUser } from '@actions/users/updateUser';

export default async function verifyCode(id: string, code: string) {
export default async function verifyCode(
id: string,
code: string,
optedIntoPanels?: boolean
) {
try {
const validCode = code === (process.env.CHECK_IN_CODE as string);
if (!validCode) {
throw new Error('Invalid code.');
}

const update: any = { has_checked_in: true };
if (typeof optedIntoPanels === 'boolean') {
update.opted_into_panels = optedIntoPanels;
}

const res = await updateUser(id, {
$set: {
has_checked_in: true,
},
$set: update,
});

if (!res.ok) {
Expand Down
8 changes: 7 additions & 1 deletion app/(api)/_actions/logic/assignJudgesToPanels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,13 @@ export default async function assignJudgesToPanels(panelSize: number = 5) {
};
}

const judgesRes = await GetManyUsers({ role: 'judge', has_checked_in: true });
// only judges who have checked in and explicitly opted into panels
const judgesRes = await GetManyUsers({
role: 'judge',
has_checked_in: true,
opted_into_panels: true,
});
console.log(judgesRes);
if (!judgesRes.ok || judgesRes.body.length === 0) {
return {
ok: false,
Expand Down
4 changes: 2 additions & 2 deletions app/(api)/_utils/matching/judgeToPanelAlgorithm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export default async function judgeToPanelAlgorithm(

judges = judges.sort(
(a, b) =>
(a.specialties?.indexOf(panel.domain) ?? 0) -
(b.specialties?.indexOf(panel.domain) ?? 0)
(a.specialties?.indexOf(panel.domain ?? '') ?? 0) -
(b.specialties?.indexOf(panel.domain ?? '') ?? 0)
);

panel.user_ids = judges
Expand Down
39 changes: 24 additions & 15 deletions app/(pages)/_components/AuthForm/AuthForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import hackerStyles from './HackerAuthForm.module.scss';
import judgeStyles from './JudgeAuthForm.module.scss';

type Role = 'hacker' | 'judge';
type FieldName = 'email' | 'password' | 'passwordDupe' | 'code';
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason why FieldName is no longer needed?


interface FormField {
name: FieldName;
name: string;
type: string;
label: string;
placeholder?: string;
Expand All @@ -27,8 +26,8 @@ interface AuthFormProps {
buttonText: string;
linkText?: string;
linkHref?: string;
initialValues: Record<string, string>;
onSubmit: (values: Record<string, string>) => Promise<any>;
initialValues: Record<string, any>;
onSubmit: (values: Record<string, any>) => Promise<any>;
onSuccess: () => void;
}

Expand Down Expand Up @@ -68,17 +67,27 @@ export default function AuthForm({
<p className={styles.error_msg}>{errors[field.name]}</p>
<div className={styles.input_container}>
<label htmlFor={field.name}>{field.label}</label>
<input
name={field.name}
type={field.type}
placeholder={field.placeholder}
value={formValues[field.name] || ''}
onInput={handleChange}
readOnly={field.readOnly}
style={{
cursor: field.readOnly ? 'not-allowed' : 'auto',
}}
/>
{field.type === 'checkbox' ? (
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you have a frontend example of this? just curious how it looks. Maybe during tmr (10/27) meeting you can briefly share.

<input
name={field.name}
type="checkbox"
checked={!!formValues[field.name]}
onChange={handleChange}
readOnly={field.readOnly}
/>
) : (
<input
name={field.name}
type={field.type}
placeholder={field.placeholder}
value={formValues[field.name] || ''}
onInput={handleChange}
readOnly={field.readOnly}
style={{
cursor: field.readOnly ? 'not-allowed' : 'auto',
}}
/>
)}
</div>
</div>
))}
Expand Down
10 changes: 7 additions & 3 deletions app/(pages)/_hooks/useAuthForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { useEffect, useState, ChangeEvent, FormEvent } from 'react';

interface FieldValues {
[key: string]: string;
[key: string]: any;
}

interface FieldErrors {
Expand Down Expand Up @@ -77,8 +77,12 @@ export default function useAuthForm(
};

const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFieldValue(name, value);
const { name, value, type, checked } = e.target as any;
if (type === 'checkbox') {
setFieldValue(name, checked);
} else {
setFieldValue(name, value);
}
};

const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
Expand Down
3 changes: 3 additions & 0 deletions app/(pages)/admin/_components/Judges/JudgeCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ export default function JudgeCard({
)}
</span>
<p className={styles.email}>{judge.email}</p>
<p className={styles.panel_status}>
Panel Opt-in: {judge.opted_into_panels ? 'Yes' : 'No'}
</p>
</div>
<div className={styles.header_details}>
{editable && (
Expand Down
41 changes: 36 additions & 5 deletions app/(pages)/admin/_components/Judges/JudgeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,16 @@ export default function JudgeForm({
teams.body.map((team: any) => [team._id, team])
);

if (data?._id) {
data.teams = data.teams.map((team: any) => {
return teamMap[team._id];
});
if (data?._id && data.teams) {
data.teams = data.teams
.map((team: any) => {
// Handle case where team might be just an ID string or already be a full team object
if (typeof team === 'string') {
return teamMap[team] || team;
}
return team._id ? teamMap[team._id] || team : team;
})
.filter(Boolean); // Remove any undefined teams
}

const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
Expand Down Expand Up @@ -91,6 +97,11 @@ export default function JudgeForm({
validation: (has_checked_in: any) =>
has_checked_in === true || has_checked_in === false,
},
{
field: 'opted_into_panels',
validation: (opted_into_panels: any) =>
opted_into_panels === true || opted_into_panels === false,
},
];

verificationList.forEach(({ field, validation }) => {
Expand All @@ -104,13 +115,23 @@ export default function JudgeForm({
return;
}

const { _id, name, email, role, specialties, teams, has_checked_in } = data;
const {
_id,
name,
email,
role,
specialties,
teams,
has_checked_in,
opted_into_panels,
} = data;
const body = {
name,
email,
role,
specialties,
has_checked_in,
opted_into_panels,
};

const res = await updateJudgeWithTeams(_id, { $set: body }, teams);
Expand Down Expand Up @@ -232,6 +253,16 @@ export default function JudgeForm({
{ option: 'false', value: false },
]}
/>
<DropdownInput
label="opted into panels"
value={data.opted_into_panels}
updateValue={(value: any) => updateField('opted_into_panels', value)}
width="400px"
options={[
{ option: 'true', value: true },
{ option: 'false', value: false },
]}
/>
<div className={styles.action_buttons}>
<button className={styles.submit_button} type="submit">
Submit
Expand Down
13 changes: 10 additions & 3 deletions app/(pages)/judges/_components/AuthForms/CheckInForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import AuthForm from '@components/AuthForm/AuthForm';
export default function CheckInForm({ id }: any) {
const router = useRouter();

const onSubmit = async (fields: any) => {
return verifyCode(id, fields.code);
};
const onSubmit = async (fields: any) =>
// include opt-in boolean when verifying
verifyCode(id, fields.code, !!fields.opted_into_panels);

const onSuccess = () => {
router.push('/judges');
Expand All @@ -24,6 +24,12 @@ export default function CheckInForm({ id }: any) {
placeholder: '',
readOnly: false,
},
{
name: 'opted_into_panels',
type: 'checkbox',
label: "I'd like to be on a judging panel",
readOnly: false,
},
];

return (
Expand All @@ -33,6 +39,7 @@ export default function CheckInForm({ id }: any) {
buttonText="Check in →"
initialValues={{
code: '',
opted_into_panels: false,
}}
onSubmit={onSubmit}
onSuccess={onSuccess}
Expand Down
2 changes: 1 addition & 1 deletion app/_types/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import User from './user';
interface Panel {
_id?: string;
track: string;
domain: string;
domain?: string;
Copy link
Contributor

Choose a reason for hiding this comment

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

In response to your pr description, yeah I'm not too sure either why "Best Hack for Social Good" doesn't have a domain. It was also not in the admin panel judging view and we had to judge it ourselves last year, not sure if that was intentional - may need to check with Jay or someone last year about that.

user_ids: string[];
users?: User[]; // populated by aggregation
}
Expand Down
1 change: 1 addition & 0 deletions app/_types/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface User {
position?: string; // for hackers only
is_beginner?: boolean; // for hackers only
has_checked_in: boolean;
opted_into_panels?: boolean; // for judges only
}

export default User;
Loading