Skip to content
This repository was archived by the owner on Dec 4, 2025. It is now read-only.
Merged
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
43 changes: 37 additions & 6 deletions src/components/Judging/Admin/SuperlativeSubmissions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CardLike } from '../../Common'
import { Button } from '../../Input'
import { H1 } from '../../Typography'
import { useHackathon } from '../../../utility/HackathonProvider'
import { JUDGING_RUBRIC, calculateGrade } from '../../../utility/Constants'

const SuperlativePrize = styled.div`
${CardLike};
Expand Down Expand Up @@ -34,6 +35,37 @@ const SuperlativeSubmissions = ({ superlativePrizes }) => {
'Devpost': project.links.devpost,
'Charity choice': project.charityChoice,
}
// compute average scores per rubric and overall grade if grades exist
if (project.grades && Object.keys(project.grades).length > 0) {
const totals = {}
const gradeEntries = Object.values(project.grades)
const count = gradeEntries.length

gradeEntries.forEach(grade => {
Object.entries(grade).forEach(([key, value]) => {
if (typeof value !== 'number') return
totals[key] = (totals[key] || 0) + value
})
})

// Attach averages for each rubric item
const avgForCalc = {}
JUDGING_RUBRIC.forEach(item => {
const avg = totals[item.id] ? totals[item.id] / count : 0
projectInfo[item.id] = Number.isFinite(avg) ? avg.toFixed(2) : ''
avgForCalc[item.id] = avg
})

// Overall grade based on averaged rubric values
projectInfo['Overall grade'] = calculateGrade(avgForCalc)
} else {
// ensure rubric headers exist even if no grades present
JUDGING_RUBRIC.forEach(item => {
projectInfo[item.id] = ''
})
projectInfo['Overall grade'] = ''
}

project.teamMembers.forEach((member, index) => {
projectInfo[`Member ${index + 1} Name`] = member.name
projectInfo[`Member ${index + 1} Email`] = member.email
Expand All @@ -53,12 +85,11 @@ const SuperlativeSubmissions = ({ superlativePrizes }) => {
<SuperlativePrize key={i}>
<Accordion cursor="default" heading={prize} key={prize}>
<EntriesList>
{superlativePrizes[prize].map((submission, i) => (
<li key={i}>
{submission.title}{' '}
{submission.draftStatus === 'public' ? 'Published (Submitted)' : 'Draft Only'}
</li>
))}
{superlativePrizes[prize].map((submission, i) =>
submission && submission.draftStatus === 'public' ? (
<li key={submission.id || idx}>{submission.title}</li>
) : null
)}
</EntriesList>
<Button color="secondary" width="medium">
<LinkContainer>
Expand Down
4 changes: 3 additions & 1 deletion src/components/Judging/Admin/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import styled from 'styled-components'
import { JUDGING_RUBRIC } from '../../../utility/Constants'
import { Button } from '../../Input'
import { A } from '../../Typography'
import { useHackathon } from '../../../utility/HackathonProvider'

const StyledTable = styled.table`
width: 100%;
Expand Down Expand Up @@ -88,8 +89,9 @@ const ProjectGradeTitles = [
]

export const ProjectGradeTable = ({ data, onDisqualify }) => {
const { activeHackathon } = useHackathon()
const formattedData = data?.map(row => {
const projectLink = `/projects/${row.id}`
const projectLink = `/app/${activeHackathon}/projects/${row.id}`
return [
row.title,
projectLink,
Expand Down
35 changes: 26 additions & 9 deletions src/components/Judging/SubmissionForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,10 @@ const SubmissionForm = ({
newErrors.sourceCode = 'Please enter a valid source code URL'
}

if (links.figma && !validateURL(links.figma)) {
newErrors.figma = 'Please enter a valid URL for Figma'
}

// Validate charity selection
if (!charityChoice) {
newErrors.charity = 'Please select a charity'
Expand Down Expand Up @@ -351,6 +355,13 @@ const SubmissionForm = ({
errorMsg={errors?.youtube}
onChange={e => setLinks({ ...links, youtube: e.target.value })}
/>
<TextInputWithField
fieldName="Figma URL"
value={links?.figma}
invalid={errors?.figma}
errorMsg={errors?.figma}
onChange={e => setLinks({ ...links, figma: e.target.value })}
/>
<TextInputWithField
fieldName="Other"
value={links?.other}
Expand Down Expand Up @@ -425,18 +436,24 @@ const SubmissionForm = ({
{superlativePrizes && (
<FormSection>
<HeadingLabel>Superlative Prizes</HeadingLabel>
<P style={{ marginTop: '8px' }}>
NOTE: To be considered for "Most Accessible Design", you must include a Figma link
above.
</P>
<div>
{superlativePrizes.map(prize => {
return (
<Select
key={prize}
type="radio"
checked={superlativeSelectedPrizes.includes(prize)}
label={prize}
onChange={() =>
setSuperlativeSelectedPrizes(prev => (prev.includes(prize) ? [] : [prize]))
}
/>
<>
<Select
key={prize}
type="radio"
checked={superlativeSelectedPrizes.includes(prize)}
label={prize}
onChange={() =>
setSuperlativeSelectedPrizes(prev => (prev.includes(prize) ? [] : [prize]))
}
/>
</>
)
})}
</div>
Expand Down
14 changes: 13 additions & 1 deletion src/components/Judging/ViewProject.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ const ViewProject = ({ project, score, error, success, isSubmitting, onChange, o
const cleanedUpDevpostLink = project.links.devpost
? project.links.devpost.replace(/https?:\/\//, '')
: ''
const cleanedUpFigmaLink = project.links.figma
? project.links.figma.replace(/https?:\/\//, '')
: ''
return (
<Container>
<JudgingColumn>
Expand All @@ -128,6 +131,15 @@ const ViewProject = ({ project, score, error, success, isSubmitting, onChange, o
View source code
</StyledA>
</StyledP>
<StyledP>
{cleanedUpFigmaLink ? (
<StyledA target="_blank" rel="noreferrer noopener" href={`//${cleanedUpFigmaLink}`}>
View Figma
</StyledA>
) : (
''
)}
</StyledP>
</Card>
</JudgingColumn>
<Column>
Expand All @@ -137,7 +149,7 @@ const ViewProject = ({ project, score, error, success, isSubmitting, onChange, o
<ExternalLink
target="_blank"
rel="noreferrer noopener"
href="https://nwplus.notion.site/PUBLIC-HackCamp-2024-Peer-Judging-Rubric-23ecd01e56b04442a7dfe0c7cbabbc62"
href="https://nwplus.notion.site/PUBLIC-HackCamp-2025-Peer-Judging-Rubric-1ed14d529faa811ba7f2c17e5fa454df?pvs=74"
>
Hacker Package
</ExternalLink>
Expand Down
36 changes: 36 additions & 0 deletions src/containers/JudgingPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ const JudgingPanel = () => {
const sponsorPrizes = (await getSponsorPrizes(dbHackathonName)) || []
const projects = (await getProjectData(dbHackathonName)) || []

if (!sponsorPrizes.length) return {}

const prizesToProjectsMap = {}

for (let i = 0; i < sponsorPrizes.length; i++) {
Expand Down Expand Up @@ -251,6 +253,8 @@ const JudgingPanel = () => {
const superlativePrizes = (await getSuperlativePrizes(dbHackathonName)) || []
const projects = (await getProjectData(dbHackathonName)) || []

if (!superlativePrizes.length) return {}

const prizesToProjectsMap = {}

for (let i = 0; i < superlativePrizes.length; i++) {
Expand Down Expand Up @@ -339,6 +343,38 @@ const JudgingPanel = () => {
'Devpost': project.links.devpost,
'Charity choice': project.charityChoice,
}

// compute average scores per rubric and overall grade if grades exist
if (project.grades && Object.keys(project.grades).length > 0) {
const totals = {}
const gradeEntries = Object.values(project.grades)
const count = gradeEntries.length

gradeEntries.forEach(grade => {
Object.entries(grade).forEach(([key, value]) => {
if (typeof value !== 'number') return
totals[key] = (totals[key] || 0) + value
})
})

// Attach averages for each rubric item
const avgForCalc = {}
JUDGING_RUBRIC.forEach(item => {
const avg = totals[item.id] ? totals[item.id] / count : 0
projectInfo[item.id] = Number.isFinite(avg) ? avg.toFixed(2) : ''
avgForCalc[item.id] = avg
})

// Overall grade based on averaged rubric values
projectInfo['Overall grade'] = calculateGrade(avgForCalc)
} else {
// ensure rubric headers exist even if no grades present
JUDGING_RUBRIC.forEach(item => {
projectInfo[item.id] = ''
})
projectInfo['Overall grade'] = ''
}

project.teamMembers.forEach((member, index) => {
projectInfo[`Member ${index + 1} Name`] = member.name
projectInfo[`Member ${index + 1} Email`] = member.email
Expand Down