Skip to content

Commit

Permalink
improve registrations overview
Browse files Browse the repository at this point in the history
adds infoboxes to students who have dietary restrictions, need to leave overnight, or have requested a laptop

add "survey response breakdown" component, displaying statistics on how students filled out the post-registration survey
  • Loading branch information
oohwooh committed May 14, 2024
1 parent 6966f73 commit 884afa6
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 4 deletions.
1 change: 0 additions & 1 deletion src/components/RegistrationGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export default function RegistrationGraph({ event, children, ...props }) {
const graphStart = now.clone().subtract(DAYS, 'days');
const data = [];
const ticketsWithDate = event.tickets.map((e) => ({ ...e, createdAt: moment(e.createdAt) }));

for (let current = graphStart.clone(); current < now; current = current.add({ day: 1 })) {
data.push({
x: -1 * Math.floor(now.diff(current, 'days')),
Expand Down
2 changes: 2 additions & 0 deletions src/components/Ticket.gql
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ fragment Ticket on ClearTicket {
checkedIn
checkedOut
vaccineVerified: getMetadata(key: "vaccineVerified")
surveyResponses
promoCode {
code
}
event {
id
overnightMinAge
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/components/Ticket.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Box, Button, Text} from "@codeday/topo/Atom";
import {useToasts} from '@codeday/topo/utils';
import {checkin, checkout, sendWaiverReminder} from './Ticket.gql';
import Badge from "./Badge";
import Alert, {GoodAlert} from "./Alert";
import Alert, {GoodAlert, InfoAlert} from "./Alert";
import {useFetcher} from '../fetch';
import InfoBox from "./InfoBox";
import * as Icon from "@codeday/topocons/Icon";
Expand Down Expand Up @@ -92,6 +92,10 @@ export default function Ticket({ticket, eventId, ...props}) {
<br />
</Box>
)}
{(ticket.surveyResponses?.dietary || 'none') !== 'none' && <InfoAlert fontSize="sm">Dietary restrictions: {ticket.surveyResponses.dietary}</InfoAlert>}
{ticket.surveyResponses?.laptop == 'yes' && <InfoAlert fontSize="sm">Requested laptop</InfoAlert>}
{(ticket.event.overnightMinAge && ticket.age < ticket.event.overnightMinAge) && <InfoAlert>Can't stay overnight</InfoAlert>}
<br />
{session && (
<Button
isLoading={loading}
Expand Down
6 changes: 6 additions & 0 deletions src/components/TicketSurveyBreakdown.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fragment TicketSurveyBreakdown on ClearTicket {
id
age
surveyResponses
type
}
69 changes: 69 additions & 0 deletions src/components/TicketSurveyBreakdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import {
Text, List, ListItem,
} from '@codeday/topo/Atom';
import {
Accordion, AccordionButton, AccordionIcon, AccordionItem, AccordionPanel,
} from '@chakra-ui/react';
import InfoBox from './InfoBox';

export default function TicketSurveyBreakdown({ tickets, children, ...props }) {
const multiAnswerQuestions = ['ethnicity', 'interests', 'workshops'];
// structure: { question: { answer1: 5, answer2: 10 } }
const countByValueByKey = {};
tickets.forEach((ticket) => {
Object.keys(ticket.surveyResponses || {}).forEach((q) => {
let responses = [];
if (multiAnswerQuestions.includes(q)) {
responses = ticket.surveyResponses[q].split(',');
} else {
responses = [ticket.surveyResponses[q]];
}
// remove empty-like responses
responses.filter((r) => r)
.forEach((r) => {
if (!countByValueByKey[q]) countByValueByKey[q] = {};
if (!countByValueByKey[q][r]) countByValueByKey[q][r] = 0;
countByValueByKey[q][r] += 1;
});
});
});
return (
<InfoBox heading="Survey response breakdown">
<Accordion allowToggle>
<AccordionItem>
<AccordionButton>
(click to show/hide)
<AccordionIcon />
</AccordionButton>
<AccordionPanel>
{Object.keys(countByValueByKey).map((question) => (
<Accordion allowMultiple>
<AccordionItem>

<AccordionButton>
<Text bold>{question}</Text>
<AccordionIcon />
</AccordionButton>
<AccordionPanel>

<List styleType="disc" ml={4}>
{Object.keys(countByValueByKey[question])
.sort((a, b) => countByValueByKey[question][b] - countByValueByKey[question][a])
.map((answer) => {
const num = countByValueByKey[question][answer];
return (
<ListItem>{answer}: {num} ({(num / tickets.length * 100).toFixed(2)}%)</ListItem>
);
})}
</List>
</AccordionPanel>
</AccordionItem>
</Accordion>
))}
</AccordionPanel>
</AccordionItem>
</Accordion>
</InfoBox>
);
}
2 changes: 2 additions & 0 deletions src/pages/events/[event]/tickets/index.gql
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "../../../../components/Ticket.gql"
#import "../../../../components/TicketSurveyBreakdown.gql"
query getEvent($data: ClearEventWhereUniqueInput!) {
clear {
event(where: $data) {
Expand All @@ -13,6 +14,7 @@ query getTickets($data: ClearEventWhereUniqueInput!) {
event(where: $data) {
tickets(orderBy:[{lastName:asc}]) {
...Ticket
...TicketSurveyBreakdown
id
createdAt
firstName
Expand Down
7 changes: 5 additions & 2 deletions src/pages/events/[event]/tickets/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { CSVLink } from "react-csv";
import { UiDownload, Camera } from "@codeday/topocons/Icon";
import { useRouter } from "next/router";
import { Icon } from "@chakra-ui/react";
import TicketSurveyBreakdown from "../../../../components/TicketSurveyBreakdown";

function sortFn(sort, tickets) {
switch (sort) {
Expand Down Expand Up @@ -163,9 +164,9 @@ export default function Tickets({ event }) {
.filter((ticket) => ticket.type == "STUDENT")
.reduce((partialSum, ticket) => partialSum + ticket.age, 0) /
(data?.clear?.event?.tickets.filter((ticket) => ticket.type == "STUDENT") || [])
.length) *
.length) *
10
) / 10}
) / 10}
</Text>
<Text>Total Tickets:&nbsp;{tickets.length}</Text>
<Text>
Expand All @@ -177,13 +178,15 @@ export default function Tickets({ event }) {
{tickets.filter((ticket) => ticket.type != "STUDENT").length}
</Text>
</HStack>
<TicketSurveyBreakdown tickets={tickets || []} />
<Grid
templateColumns={{
base: "1fr",
md: "repeat(2, 1fr)",
lg: "repeat(3, 1fr)",
}}
>
{/* TODO: figure out css to make the vertical spacing look less bad with mismatched Ticket heights */}
{tickets.map((ticket) => (
<Ticket id={ticket.id} ticket={ticket} />
))}
Expand Down

0 comments on commit 884afa6

Please sign in to comment.