Skip to content

Commit c125c30

Browse files
committed
Add survey responding
1 parent b0f0ba0 commit c125c30

File tree

8 files changed

+741
-140
lines changed

8 files changed

+741
-140
lines changed

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
"start": "next start"
99
},
1010
"dependencies": {
11+
"@chakra-ui/icons": "^2.0.2",
1112
"@chakra-ui/react": "~1.6.10",
1213
"@codeday/topo": "^7.0.12",
1314
"@codeday/topocons": "^1.3.0",
1415
"@emotion/react": "^11",
1516
"@emotion/styled": "^11",
17+
"@rjsf/chakra-ui": "^4.2.0",
18+
"@rjsf/core": "^4.2.0",
1619
"@tylermenezes/cognitoforms-react": "^2.0.14",
1720
"ag-grid-community": "^25.3.0",
1821
"ag-grid-react": "^25.3.0",
@@ -24,7 +27,7 @@
2427
"jsonwebtoken": "^8.5.1",
2528
"lodash.get": "^4.4.2",
2629
"luxon": "^1.25.0",
27-
"next": "^10.0.3",
30+
"next": "11",
2831
"next-absolute-url": "^1.2.2",
2932
"next-auth": "^3.1.0",
3033
"next-seo": "^4.7.3",

src/pages/dash/s/[token]/index.gql

+11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ query StudentDashboardQuery {
66
id
77
}
88
}
9+
surveys {
10+
id
11+
name
12+
occurrences {
13+
id
14+
dueAt
15+
surveyResponses {
16+
authorStudentId
17+
}
18+
}
19+
}
920
student {
1021
id
1122
status

src/pages/dash/s/[token]/index.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import { print } from 'graphql';
22
import { useRouter } from 'next/router';
33
import { useEffect } from 'react';
44
import { Content } from '@codeday/topo/Molecule';
5-
import { Text, Heading, Link, Button, Spinner, Box, Grid } from '@codeday/topo/Atom';
5+
import { Text, Heading, Link, Button, Spinner, Box, Grid, List, ListItem } from '@codeday/topo/Atom';
66
import Page from '../../../../components/Page';
77
import { useSwr } from '../../../../dashboardFetch';
88
import { StudentDashboardQuery } from './index.gql'
99
import { Match } from '../../../../components/Dashboard/Match';
10+
import { DateTime } from 'luxon';
1011

1112
export default function Dashboard() {
1213
const { query } = useRouter();
@@ -78,6 +79,25 @@ export default function Dashboard() {
7879
))}
7980
</Box>
8081

82+
{data?.labs?.surveys && (
83+
<>
84+
<Heading as="h3" fontSize="md" mb={2}>Surveys</Heading>
85+
<List mb={8} styleType="disc" pl={6}>
86+
{data.labs.surveys.flatMap((s) => s.occurrences.map((o) => {
87+
if (o.surveyResponses.filter((r) => r.authorStudentId === data?.labs?.student?.id).length > 0) return <></>;
88+
return (
89+
<ListItem key={o.id}>
90+
<Link href={`/dash/s/${query.token}/survey/${s.id}/${o.id}`} target="_blank">
91+
<Text d="inline" fontWeight="bold">{s.name}</Text>
92+
<Text fontSize="sm">due {DateTime.fromISO(o.dueAt).toLocaleString(DateTime.DATE_MED)}</Text>
93+
</Link>
94+
</ListItem>
95+
);
96+
}))}
97+
</List>
98+
</>
99+
)}
100+
81101
<Heading as="h3" fontSize="md" mb={4}>Additional Resources</Heading>
82102
<Button as="a" href={`/dash/s/${query?.token}/onboarding`}>Onboarding Week Assignments</Button>
83103
</Box>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
query SurveyQuery ($surveyId: String!) {
2+
labs {
3+
student {
4+
id
5+
status
6+
projects {
7+
id
8+
status
9+
students {
10+
id
11+
status
12+
givenName
13+
surname
14+
}
15+
mentors {
16+
id
17+
status
18+
givenName
19+
surname
20+
}
21+
}
22+
}
23+
survey(survey: $surveyId) {
24+
id
25+
name
26+
selfUi
27+
selfSchema
28+
projectUi
29+
projectSchema
30+
peerUi
31+
peerSchema
32+
mentorSchema
33+
mentorUi
34+
}
35+
}
36+
}
37+
38+
mutation SurveyRespondMutation ($occurrenceId: String!, $responses: [LabsSurveyRespondInput!]!) {
39+
labs {
40+
surveyRespond(occurrence: $occurrenceId, responses: $responses)
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import React, { useState } from 'react';
2+
import { Heading, Button } from '@codeday/topo/Atom';
3+
import { Content } from '@codeday/topo/Molecule';
4+
import { Form } from '@rjsf/chakra-ui';
5+
import Page from '../../../../../../../components/Page';
6+
import { useSurveyResponses } from '../../../../../../../utils';
7+
import { SurveyQuery, SurveyRespondMutation } from './index.gql';
8+
import { apiFetch } from '@codeday/topo/utils';
9+
10+
export default function SurveyPage({ student, survey, token, surveyId, occurrenceId }) {
11+
const [isLoading, setIsLoading] = useState(false);
12+
const [isSubmitted, setIsSubmitted] = useState(false);
13+
const [responses, setResponse, getResponse] = useSurveyResponses();
14+
15+
const projects = student.projects.filter((p) => p.status === 'MATCHED');
16+
const mentors = projects
17+
.map((p) => p.mentors.map((s) => ({ ...s, projectId: p.id })))
18+
.flat();
19+
const teammates = projects
20+
.map((p) => p.students.map((s) => ({ ...s, projectId: p.id })))
21+
.flat()
22+
.filter((s) => s.id !== student.id);
23+
24+
if (isSubmitted) {
25+
return (
26+
<Page slug={`/dash/s/${token}/survey/${surveyId}/${occurrenceId}`} title="Form Submission">
27+
<Content>
28+
Thanks for submitting this survey. You can now close the page.
29+
</Content>
30+
</Page>
31+
);
32+
}
33+
34+
return (
35+
<Page slug={`/dash/s/${token}/survey/${surveyId}/${occurrenceId}`} title="Form Submission">
36+
{survey?.selfSchema && (
37+
<Content>
38+
<Heading fontSize="5xl">Self-Reflection</Heading>
39+
<Form
40+
schema={survey.selfSchema}
41+
uiSchema={survey.selfUi}
42+
onChange={(e) => setResponse({ response: e.formData, student: student.id })}
43+
formData={getResponse({ student: student.id })?.response || {}}
44+
disabled={isLoading}
45+
children={true}
46+
/>
47+
</Content>
48+
)}
49+
{survey?.projectSchema && projects.map((p) => (
50+
<Content>
51+
<Heading fontSize="5xl">
52+
Project Reflection: Your project with {[...p.mentors, ...p.students].map(m => m.givenName).join('/')}
53+
</Heading>
54+
<Form
55+
schema={survey.projectSchema}
56+
uiSchema={survey.projectUi}
57+
onChange={(e) => setResponse({ response: e.formData, project: p.id })}
58+
formData={getResponse({ project: p.id })?.response || {}}
59+
disabled={isLoading}
60+
children={true}
61+
/>
62+
</Content>
63+
))}
64+
{survey?.peerSchema && teammates.map((p) => (
65+
<Content>
66+
<Heading fontSize="5xl">Peer Reflection: {p.givenName} {p.surname}</Heading>
67+
<Form
68+
schema={survey.peerSchema}
69+
uiSchema={survey.peerUi}
70+
onChange={(e) => setResponse({ response: e.formData, student: p.id })}
71+
formData={getResponse({ student: p.id })?.response || {}}
72+
disabled={isLoading}
73+
children={true}
74+
/>
75+
</Content>
76+
))}
77+
{survey?.mentorSchema && mentors.map((m) => (
78+
<Content>
79+
<Heading fontSize="5xl">Mentor Reflection: {m.givenName} {m.surname}</Heading>
80+
<Form
81+
schema={survey.mentorSchema}
82+
uiSchema={survey.mentorUi}
83+
onChange={(e) => setResponse({ response: e.formData, mentor: m.id })}
84+
formData={getResponse({ mentor: m.id })?.response || {}}
85+
disabled={isLoading}
86+
children={true}
87+
/>
88+
</Content>
89+
))}
90+
<Content textAlign="center" mt={8}>
91+
<Button
92+
isLoading={isLoading}
93+
size="lg"
94+
colorScheme="green"
95+
onClick={async () => {
96+
setIsLoading(true);
97+
try {
98+
const res = await apiFetch(
99+
SurveyRespondMutation,
100+
{ occurrenceId, responses },
101+
{ 'X-Labs-Authorization': `Bearer ${token}` },
102+
);
103+
setIsSubmitted(true);
104+
} catch (ex) { console.error(ex); }
105+
setIsLoading(false);
106+
}}
107+
>
108+
Submit
109+
</Button>
110+
</Content>
111+
</Page>
112+
)
113+
}
114+
115+
export async function getServerSideProps({ params: { token, surveyId, occurrenceId }}) {
116+
const res = await apiFetch(SurveyQuery, { surveyId }, { 'X-Labs-Authorization': `Bearer ${token}` });
117+
if (res?.labs?.student?.status !== 'ACCEPTED') throw new Error('Not accepted.');
118+
if (!res?.labs?.survey) throw new Error('No survey.');
119+
return {
120+
props: {
121+
student: res.labs.student,
122+
survey: res.labs.survey,
123+
surveyId,
124+
occurrenceId,
125+
token,
126+
},
127+
};
128+
}

src/utils/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './useSurveyResponses';

src/utils/useSurveyResponses.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { useReducer } from 'react';
2+
3+
export function useSurveyResponses() {
4+
const [e, r] = useReducer((_prev, { response, ...keys }) => {
5+
return {
6+
..._prev,
7+
[JSON.stringify(keys)]: { response, ...keys }
8+
};
9+
}, {});
10+
return [Object.values(e), r, (keys) => e[JSON.stringify(keys)]];
11+
}

0 commit comments

Comments
 (0)