Skip to content

Commit

Permalink
Merge pull request #220 from Dudrie/add-downloadable-exam-results
Browse files Browse the repository at this point in the history
Add downloadable exam results
  • Loading branch information
Dudrie authored Jan 18, 2020
2 parents 2d8fc73 + 3f1c1b2 commit d98db88
Show file tree
Hide file tree
Showing 43 changed files with 841 additions and 365 deletions.
2 changes: 1 addition & 1 deletion client/src/components/Placeholder.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Typography } from '@material-ui/core';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import React from 'react';
import LoadingSpinner from './LoadingSpinner';
import LoadingSpinner from './loading/LoadingSpinner';
import clsx from 'clsx';

const useStyles = makeStyles((theme: Theme) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import React, { useState, useRef } from 'react';
import { FormikSubmitCallback } from '../../../types';
import FormikTextField from '../../forms/components/FormikTextField';
import SubmitButton from '../../forms/components/SubmitButton';
import SubmitButton from '../../loading/SubmitButton';
import clsx from 'clsx';

const useStyles = makeStyles(theme =>
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/forms/ChangePasswordForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
import { Formik } from 'formik';
import { Paper, Typography, Button } from '@material-ui/core';
import FormikTextField from './components/FormikTextField';
import SubmitButton from './components/SubmitButton';
import SubmitButton from '../loading/SubmitButton';
import * as Yup from 'yup';
import { FormikSubmitCallback } from '../../types';
import clsx from 'clsx';
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/forms/FormikBaseForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import clsx from 'clsx';
import { Formik, FormikConfig } from 'formik';
import React from 'react';
import FormikDebugDisplay from './components/FormikDebugDisplay';
import SubmitButton from './components/SubmitButton';
import SubmitButton from '../loading/SubmitButton';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/forms/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import React from 'react';
import * as Yup from 'yup';
import { FormikSubmitCallback } from '../../types';
import FormikTextField from './components/FormikTextField';
import SubmitButton from './components/SubmitButton';
import SubmitButton from '../loading/SubmitButton';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
Expand Down
70 changes: 70 additions & 0 deletions client/src/components/loading/LoadingModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { makeStyles, createStyles } from '@material-ui/core/styles';
import {
Modal,
CircularProgress,
Typography,
ModalProps,
CircularProgressProps,
} from '@material-ui/core';
import clsx from 'clsx';

const useStyles = makeStyles(theme =>
createStyles({
spinner: {
marginRight: theme.spacing(1),
},
modal: {
color: theme.palette.common.white,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
modalContent: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
outline: 'none',
},
modalText: {
marginTop: theme.spacing(2),
},
})
);

interface Props extends Omit<ModalProps, 'children'> {
modalText: string;
open: boolean;
CircularProgressProps?: CircularProgressProps;
}

function LoadingModal({
modalText,
CircularProgressProps,
className,
...props
}: Props): JSX.Element {
const classes = useStyles();

return (
<Modal {...props} className={clsx(className, classes.modal)}>
<div className={classes.modalContent} tabIndex={-1}>
<CircularProgress
size={56}
color='inherit'
className={clsx(
CircularProgressProps && CircularProgressProps.className,
classes.spinner
)}
/>

<Typography variant='h4' className={classes.modalText}>
{modalText}
</Typography>
</div>
</Modal>
);
}

export default LoadingModal;
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { CircularProgress, Modal, Tooltip, Typography } from '@material-ui/core';
import { CircularProgress, Tooltip } from '@material-ui/core';
import Button, { ButtonProps } from '@material-ui/core/Button';
import { CircularProgressProps } from '@material-ui/core/CircularProgress';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import clsx from 'clsx';
import React from 'react';
import LoadingModal from './LoadingModal';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
Expand Down Expand Up @@ -72,22 +73,13 @@ function SubmitButton({
ButtomComp
)}

<Modal open={!!modalText && isSubmitting} className={classes.modal}>
<div className={classes.modalContent} tabIndex={-1}>
<CircularProgress
size={56}
color='inherit'
className={clsx(
CircularProgressProps && CircularProgressProps.className,
classes.spinner
)}
/>

<Typography variant='h4' className={classes.modalText}>
{modalText}
</Typography>
</div>
</Modal>
{modalText && (
<LoadingModal
modalText={modalText}
open={isSubmitting}
CircularProgressProps={CircularProgressProps}
/>
)}
</>
);
}
Expand Down
15 changes: 15 additions & 0 deletions client/src/hooks/fetching/Files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ export async function getScheinStatusPDF(): Promise<Blob> {
return Promise.reject(`Wrong response code (${response.status})`);
}

export async function getScheinexamResultPDF(examId: string): Promise<Blob> {
const response = await axios.get(`/pdf/scheinexam/${examId}/result`, {
responseType: 'arraybuffer',
headers: {
Accept: 'application/pdf',
},
});

if (response.status === 200) {
return new Blob([response.data], { type: 'application/pdf' });
}

return Promise.reject(`Wrong response code (${response.status})`);
}

export async function getCredentialsPDF(): Promise<Blob> {
const response = await axios.get('/pdf/credentials/', {
responseType: 'arraybuffer',
Expand Down
2 changes: 1 addition & 1 deletion client/src/view/AppBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import React, { useState } from 'react';
import { matchPath, useLocation } from 'react-router';
import { LoggedInUserTutorial } from 'shared/dist/model/Tutorial';
import { useChangeTheme } from '../components/ContextWrapper';
import SubmitButton from '../components/forms/components/SubmitButton';
import SubmitButton from '../components/loading/SubmitButton';
import { getTutorialXLSX } from '../hooks/fetching/Files';
import { useLogin } from '../hooks/LoginService';
import { getDisplayStringForTutorial, saveBlob } from '../util/helperFunctions';
Expand Down
4 changes: 2 additions & 2 deletions client/src/view/attendance/AttendanceManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { Student, StudentStatus } from 'shared/dist/model/Student';
import { LoggedInUser, TutorInfo } from 'shared/dist/model/User';
import CustomSelect from '../../components/CustomSelect';
import DateOfTutorialSelection from '../../components/DateOfTutorialSelection';
import SubmitButton from '../../components/forms/components/SubmitButton';
import LoadingSpinner from '../../components/LoadingSpinner';
import SubmitButton from '../../components/loading/SubmitButton';
import LoadingSpinner from '../../components/loading/LoadingSpinner';
import TableWithPadding from '../../components/TableWithPadding';
import { useAxios } from '../../hooks/FetchingService';
import { useLogin } from '../../hooks/LoginService';
Expand Down
2 changes: 1 addition & 1 deletion client/src/view/attendance/AttendanceView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { RouteComponentProps, withRouter } from 'react-router';
import { useAxios } from '../../hooks/FetchingService';
import { TutorialWithFetchedStudents } from '../../typings/types';
import AttendanceManager from './AttendanceManager';
import LoadingSpinner from '../../components/LoadingSpinner';
import LoadingSpinner from '../../components/loading/LoadingSpinner';

const useStyles = makeStyles(() =>
createStyles({
Expand Down
2 changes: 1 addition & 1 deletion client/src/view/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import { Role } from 'shared/dist/model/Role';
import { Tutorial } from 'shared/dist/model/Tutorial';
import { LoggedInUser } from 'shared/dist/model/User';
import LoadingSpinner from '../../components/LoadingSpinner';
import LoadingSpinner from '../../components/loading/LoadingSpinner';
import { getTutorial } from '../../hooks/fetching/Tutorial';
import { useAxios } from '../../hooks/FetchingService';
import { useLogin } from '../../hooks/LoginService';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
getPointsOfAllExercises,
convertExercisePointInfoToString,
} from 'shared/dist/model/Points';
import SubmitButton from '../../../../components/forms/components/SubmitButton';
import SubmitButton from '../../../../components/loading/SubmitButton';
import { useDialog } from '../../../../hooks/DialogService';
import FormikDebugDisplay from '../../../../components/forms/components/FormikDebugDisplay';
import { useKeyboardShortcut } from '../../../../hooks/useKeyboardShortcut';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from 'shared/dist/model/Points';
import { Exercise, Sheet } from 'shared/dist/model/Sheet';
import FormikDebugDisplay from '../../../../components/forms/components/FormikDebugDisplay';
import SubmitButton from '../../../../components/forms/components/SubmitButton';
import SubmitButton from '../../../../components/loading/SubmitButton';
import { useDialog } from '../../../../hooks/DialogService';
import { HasPoints } from '../../../../typings/types';
import { getPointsFromState as getAchievedPointsFromState } from '../EnterPoints.helpers';
Expand Down
2 changes: 1 addition & 1 deletion client/src/view/points-sheet/overview/PointsOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useParams, useHistory } from 'react-router';
import { Sheet } from 'shared/dist/model/Sheet';
import { Team } from 'shared/dist/model/Team';
import CustomSelect from '../../../components/CustomSelect';
import SubmitButton from '../../../components/forms/components/SubmitButton';
import SubmitButton from '../../../components/loading/SubmitButton';
import { getAllSheets } from '../../../hooks/fetching/Sheet';
import { getTeamsOfTutorial } from '../../../hooks/fetching/Team';
import { useErrorSnackbar } from '../../../hooks/useErrorSnackbar';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ScheinCriteriaForm, {
ScheinCriteriaFormCallback,
} from '../../components/forms/ScheinCriteriaForm';
import { FormDataResponse } from '../../components/generatedForm/types/FieldData';
import LoadingSpinner from '../../components/LoadingSpinner';
import LoadingSpinner from '../../components/loading/LoadingSpinner';
import TableWithForm from '../../components/TableWithForm';
import { useDialog } from '../../hooks/DialogService';
import { useAxios } from '../../hooks/FetchingService';
Expand Down
66 changes: 45 additions & 21 deletions client/src/view/scheinexam-management/ScheinExamManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ import ScheinExamForm, {
ScheinExamFormState,
ScheinExamFormSubmitCallback,
} from '../../components/forms/ScheinExamForm';
import LoadingSpinner from '../../components/LoadingSpinner';
import { convertFormExercisesToDTOs } from '../../components/forms/SheetForm';
import LoadingModal from '../../components/loading/LoadingModal';
import LoadingSpinner from '../../components/loading/LoadingSpinner';
import TableWithForm from '../../components/TableWithForm';
import { useDialog } from '../../hooks/DialogService';
import { useAxios } from '../../hooks/FetchingService';
import { getDisplayStringOfScheinExam } from '../../util/helperFunctions';
import ScheinExamRow from './components/ScheinExamRow';
import { getScheinexamResultPDF } from '../../hooks/fetching/Files';
import {
createScheinExam,
deleteScheinExam,
editScheinExam,
getAllScheinExams,
} from '../../hooks/fetching/ScheinExam';
import { getDisplayStringOfScheinExam, saveBlob } from '../../util/helperFunctions';
import { getDuplicateExerciseName } from '../points-sheet/util/helper';
import { convertFormExercisesToDTOs } from '../../components/forms/SheetForm';
import ScheinExamRow from './components/ScheinExamRow';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
Expand Down Expand Up @@ -44,17 +51,17 @@ function ScheinExamManagement({ enqueueSnackbar }: Props): JSX.Element {
const classes = useStyles();

const [isLoading, setIsLoading] = useState(false);
const [isGeneratingResults, setGeneratingResults] = useState(false);
const [exams, setExams] = useState<ScheinExam[]>([]);
const dialog = useDialog();
const { getAllScheinExams, createScheinExam, editScheinExam, deleteScheinExam } = useAxios();

useEffect(() => {
setIsLoading(true);
getAllScheinExams().then(exams => {
setExams(exams);
setIsLoading(false);
});
}, [getAllScheinExams]);
}, []);

const handleSubmit: ScheinExamFormSubmitCallback = async (
values,
Expand Down Expand Up @@ -87,6 +94,15 @@ function ScheinExamManagement({ enqueueSnackbar }: Props): JSX.Element {
}
};

const handleGenerateResultPDF: (exam: ScheinExam) => void = async exam => {
setGeneratingResults(true);

const blob = await getScheinexamResultPDF(exam.id);
saveBlob(blob, `Scheinklausur_${exam.scheinExamNo}_Ergebnis`);

setGeneratingResults(false);
};

const editExam: (exam: ScheinExam) => ScheinExamFormSubmitCallback = exam => async (
values,
{ setSubmitting }
Expand Down Expand Up @@ -123,6 +139,9 @@ function ScheinExamManagement({ enqueueSnackbar }: Props): JSX.Element {
content: (
<ScheinExamForm exam={exam} onSubmit={editExam(exam)} onCancelClicked={dialog.hide} />
),
DialogProps: {
maxWidth: 'lg',
},
});
}

Expand Down Expand Up @@ -153,20 +172,25 @@ function ScheinExamManagement({ enqueueSnackbar }: Props): JSX.Element {
{isLoading ? (
<LoadingSpinner />
) : (
<TableWithForm
title='Neue Scheinklausur erstellen'
form={<ScheinExamForm exams={exams} onSubmit={handleSubmit} />}
items={exams}
createRowFromItem={exam => (
<ScheinExamRow
key={exam.id}
exam={exam}
onEditExamClicked={handleEditExam}
onDeleteExamClicked={handleDeleteExam}
/>
)}
placeholder='Keine Scheinklausuren vorhanden.'
/>
<>
<TableWithForm
title='Neue Scheinklausur erstellen'
form={<ScheinExamForm exams={exams} onSubmit={handleSubmit} />}
items={exams}
createRowFromItem={exam => (
<ScheinExamRow
key={exam.id}
exam={exam}
onEditExamClicked={handleEditExam}
onHandleGenerateResultPDFClicked={handleGenerateResultPDF}
onDeleteExamClicked={handleDeleteExam}
/>
)}
placeholder='Keine Scheinklausuren vorhanden.'
/>

<LoadingModal modalText='Erstelle Ergebnisliste...' open={isGeneratingResults} />
</>
)}
</div>
);
Expand Down
Loading

0 comments on commit d98db88

Please sign in to comment.