Skip to content

Commit

Permalink
Merge pull request #238 from Dudrie/add-small-adjustments
Browse files Browse the repository at this point in the history
Add small adjustments
  • Loading branch information
Dudrie authored Feb 11, 2020
2 parents 65dcc92 + caac242 commit b45810b
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 39 deletions.
13 changes: 10 additions & 3 deletions client/src/components/SplitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
MenuList,
Paper,
Popper,
ButtonGroupProps,
} from '@material-ui/core';
import { MenuDown as ArrowDropDownIcon } from 'mdi-material-ui';
import React from 'react';
Expand All @@ -18,13 +19,13 @@ interface ButtonOption {
ButtonProps?: ButtonProps & { component?: React.ElementType; to?: string };
}

interface Props {
interface Props extends ButtonGroupProps {
options: ButtonOption[];
variant?: ButtonProps['variant'];
color?: ButtonProps['color'];
}

function SplitButton({ options, variant, color }: Props): JSX.Element {
function SplitButton({ options, variant, color, ...props }: Props): JSX.Element {
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef<HTMLDivElement>(null);
const [selectedIndex, setSelectedIndex] = React.useState(0);
Expand Down Expand Up @@ -53,7 +54,13 @@ function SplitButton({ options, variant, color }: Props): JSX.Element {

return (
<>
<ButtonGroup variant={variant} color={color} ref={anchorRef} aria-label='split button'>
<ButtonGroup
variant={variant}
color={color}
ref={anchorRef}
aria-label='split button'
{...props}
>
<Button {...buttonProps}>{options[selectedIndex].label}</Button>
<Button
size='small'
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 getClearScheinStatusPDF(): Promise<Blob> {
const response = await axios.get('/pdf/scheinoverview/', {
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 getScheinexamResultPDF(examId: string): Promise<Blob> {
const response = await axios.get(`/pdf/scheinexam/${examId}/result`, {
responseType: 'arraybuffer',
Expand Down
6 changes: 3 additions & 3 deletions client/src/hooks/fetching/Tutorial.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ScheincriteriaSummaryByStudents } from 'shared/dist/model/ScheinCriteria';
import { Student } from 'shared/dist/model/Student';
import { SubstituteDTO, Tutorial, TutorialDTO } from 'shared/dist/model/Tutorial';
import { TutorInfo, User } from 'shared/dist/model/User';
import { sortByName } from 'shared/dist/util/helpers';
import {
StudentByTutorialSlotSummaryMap,
StudentScheinCriteriaSummaryMap,
TutorialWithFetchedCorrectors,
TutorialWithFetchedStudents,
TutorialWithFetchedTutor,
Expand Down Expand Up @@ -192,8 +192,8 @@ export async function getStudentsOfTutorial(id: string): Promise<Student[]> {

export async function getScheinCriteriaSummariesOfAllStudentsOfTutorial(
id: string
): Promise<StudentScheinCriteriaSummaryMap> {
const response = await axios.get<StudentScheinCriteriaSummaryMap>(
): Promise<ScheincriteriaSummaryByStudents> {
const response = await axios.get<ScheincriteriaSummaryByStudents>(
`scheincriteria/tutorial/${id}`
);

Expand Down
4 changes: 0 additions & 4 deletions client/src/typings/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,6 @@ export interface HasPoints extends HasId {
points: PointMapDTO;
}

export interface StudentScheinCriteriaSummaryMap {
[id: string]: ScheinCriteriaSummary;
}

export interface StudentByTutorialSlotSummaryMap {
[tutorialSlot: string]: ScheinCriteriaSummary[];
}
8 changes: 3 additions & 5 deletions client/src/view/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import React, { useEffect, useState } from 'react';
import { Role } from 'shared/dist/model/Role';
import { ScheincriteriaSummaryByStudents } from 'shared/dist/model/ScheinCriteria';
import { Tutorial } from 'shared/dist/model/Tutorial';
import { LoggedInUser } from 'shared/dist/model/User';
import LoadingSpinner from '../../components/loading/LoadingSpinner';
import { getTutorial } from '../../hooks/fetching/Tutorial';
import { useAxios } from '../../hooks/FetchingService';
import { useLogin } from '../../hooks/LoginService';
import {
StudentByTutorialSlotSummaryMap,
StudentScheinCriteriaSummaryMap,
} from '../../typings/types';
import { StudentByTutorialSlotSummaryMap } from '../../typings/types';
import AdminStatsCard from './components/AdminStatsCard';
import AllTutorialStatistics from './components/AllTutorialStatistics';
import TutorialStatistics from './components/TutorialStatistics';

export interface TutorialSummaryInfo {
tutorial: Tutorial;
studentInfos: StudentScheinCriteriaSummaryMap;
studentInfos: ScheincriteriaSummaryByStudents;
}

function isAdmin(userData: LoggedInUser | undefined): boolean {
Expand Down
41 changes: 33 additions & 8 deletions client/src/view/studentmanagement/AllStudentsAdminView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Attendance } from 'shared/dist/model/Attendance';
import { PointMap } from 'shared/dist/model/Points';
import { Tutorial } from 'shared/dist/model/Tutorial';
import SubmitButton from '../../components/loading/SubmitButton';
import { getScheinStatusPDF } from '../../hooks/fetching/Files';
import { getScheinStatusPDF, getClearScheinStatusPDF } from '../../hooks/fetching/Files';
import { getAllScheinExams } from '../../hooks/fetching/ScheinExam';
import { getAllSheets } from '../../hooks/fetching/Sheet';
import { getScheinCriteriaSummaryOfAllStudents } from '../../hooks/fetching/Student';
Expand All @@ -23,6 +23,7 @@ import {
import { getPointsOfEntityAsString } from '../points-sheet/util/helper';
import Studentoverview from './student-overview/Studentoverview';
import StudentoverviewStoreProvider, { useStudentStore } from './student-store/StudentStore';
import SplitButton from '../../components/SplitButton';

const useStyles = makeStyles((theme: Theme) =>
createStyles({
Expand Down Expand Up @@ -70,6 +71,20 @@ function AdminStudentManagement(): JSX.Element {
}
}

async function printUnshortenedOverviewSheet() {
setCreatingScheinStatus(true);

try {
const blob = await getClearScheinStatusPDF();

saveBlob(blob, 'Scheinübersichtsliste_ungekürzt.pdf');
} catch {
enqueueSnackbar('Scheinübersichtsliste konnte nicht erstellt werden', { variant: 'error' });
} finally {
setCreatingScheinStatus(false);
}
}

async function generateCSVFile() {
setCreatingCSVFile(true);

Expand Down Expand Up @@ -139,16 +154,26 @@ function AdminStudentManagement(): JSX.Element {
allowChangeTutorial
additionalTopBarItem={
<div className={classes.topBar}>
<SubmitButton
<SplitButton
variant='contained'
color='primary'
isSubmitting={isCreatingScheinStatus}
className={classes.printButton}
onClick={printOverviewSheet}
disabled={students.length === 0}
>
Scheinliste ausdrucken
</SubmitButton>
disabled={students.length === 0 || isCreatingScheinStatus}
options={[
{
label: 'Scheinliste ausdrucken',
ButtonProps: {
onClick: printOverviewSheet,
},
},
{
label: 'Ungek. Liste ausdrucken',
ButtonProps: {
onClick: printUnshortenedOverviewSheet,
},
},
]}
/>

<SubmitButton
variant='contained'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import { AccountSearch as SearchIcon } from 'mdi-material-ui';
import { useSnackbar } from 'notistack';
import React, { ChangeEvent, useState } from 'react';
import { ScheinCriteriaSummary } from 'shared/dist/model/ScheinCriteria';
import { Student } from 'shared/dist/model/Student';
import { Tutorial } from 'shared/dist/model/Tutorial';
import { getNameOfEntity } from 'shared/dist/util/helpers';
Expand Down Expand Up @@ -44,8 +43,6 @@ const useStyles = makeStyles((theme: Theme) =>
})
);

type SummariesByStudent = { [studentId: string]: ScheinCriteriaSummary };

interface Props {
tutorials?: Tutorial[];
allowChangeTutorial?: boolean;
Expand All @@ -63,7 +60,7 @@ function Studentoverview({
const [sortOption, setSortOption] = useState<StudentSortOption>(StudentSortOption.ALPHABETICAL);

const dialog = useDialog();
const [{ students, teams, tutorialId, isInitialized }, dispatch] = useStudentStore();
const [{ students, teams, tutorialId, isInitialized, summaries }, dispatch] = useStudentStore();
const { enqueueSnackbar } = useSnackbar();

const handlerParams: HandlerParams = { tutorialId, dispatch, enqueueSnackbar };
Expand Down Expand Up @@ -187,6 +184,7 @@ function Studentoverview({
<StudentRow
className={classes.studentRow}
student={student}
scheinStatus={summaries[student.id]}
onEdit={openEditDialog}
onDelete={openDeleteDialog}
onChangeTutorial={allowChangeTutorial ? openChangeTutorialDialog : undefined}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import {
} from 'mdi-material-ui';
import React from 'react';
import { Link } from 'react-router-dom';
import { ScheinCriteriaSummary } from 'shared/dist/model/ScheinCriteria';
import { Student, TeamInStudent } from 'shared/dist/model/Student';
import { getNameOfEntity } from 'shared/dist/util/helpers';
import EntityListItemMenu from '../../../../components/list-item-menu/EntityListItemMenu';
import { ListItem } from '../../../../components/list-item-menu/ListItemMenu';
import PaperTableRow, { PaperTableRowProps } from '../../../../components/PaperTableRow';
import StudentAvatar from '../../../../components/student-icon/StudentAvatar';
import { getStudentInfoPath } from '../../../../routes/Routing.helpers';
import ScheinStatusBox from '../../student-info/components/ScheinStatusBox';
import { useStudentStore } from '../../student-store/StudentStore';

const useStyles = makeStyles(theme =>
Expand All @@ -39,6 +41,7 @@ interface Props extends PaperTableRowProps {
onDelete: StudentCallback;
onChangeTutorial?: StudentCallback;
subtextPrefix?: string;
scheinStatus?: ScheinCriteriaSummary;
}

interface GetSubtextParams {
Expand All @@ -63,6 +66,7 @@ function StudentRow({
onEdit,
onDelete,
onChangeTutorial,
scheinStatus,
...props
}: Props): JSX.Element {
const classes = useStyles();
Expand Down Expand Up @@ -102,6 +106,10 @@ function StudentRow({
/>
}
>
<TableCell align='right' className={classes.infoButton}>
<ScheinStatusBox scheinStatus={scheinStatus} />
</TableCell>

<TableCell align='right' className={classes.infoButton}>
<Button
variant='outlined'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ import {
deleteStudent,
editStudent,
getAllStudents,
getScheinCriteriaSummaryOfAllStudents,
} from '../../../hooks/fetching/Student';
import { createTeam, getTeamsOfTutorial } from '../../../hooks/fetching/Team';
import { getStudentsOfTutorial } from '../../../hooks/fetching/Tutorial';
import {
getStudentsOfTutorial,
getScheinCriteriaSummariesOfAllStudentsOfTutorial,
} from '../../../hooks/fetching/Tutorial';
import { AsyncDispatch } from '../../../util/AsyncReducer';
import { StudentStore } from './StudentStore';
import {
Expand Down Expand Up @@ -109,20 +113,31 @@ async function reduceReinitializeStore(
}

if (!!tutorialId) {
const [students, teams] = await Promise.all([
const [students, teams, summaries] = await Promise.all([
getStudentsOfTutorial(tutorialId),
getTeamsOfTutorial(tutorialId),
getScheinCriteriaSummariesOfAllStudentsOfTutorial(tutorialId),
]);

students.sort(sortByName);

return { ...state, students, teams, isInitialized: true, tutorialId };
return { ...state, students, teams, isInitialized: true, tutorialId, summaries };
} else {
const students = await getAllStudents();
const [students, summaries] = await Promise.all([
getAllStudents(),
getScheinCriteriaSummaryOfAllStudents(),
]);

students.sort(sortByName);

return { ...state, students, teams: undefined, tutorialId: undefined, isInitialized: true };
return {
...state,
students,
teams: undefined,
tutorialId: undefined,
isInitialized: true,
summaries,
};
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import { useAsyncReducer, AsyncDispatch } from '../../../util/AsyncReducer';
import { StudentStoreAction, StudentStoreActionType } from './StudentStore.actions';
import { Team } from 'shared/dist/model/Team';
import studentStoreReducer from './StudentStore.reducers';
import { ScheincriteriaSummaryByStudents } from 'shared/dist/model/ScheinCriteria';

export interface StudentStore {
students: Student[];
teams?: Team[];
tutorialId?: string;
isInitialized: boolean;
summaries: ScheincriteriaSummaryByStudents;
}

interface StudentContext {
Expand All @@ -22,6 +24,7 @@ const initialState: StudentStore = {
teams: undefined,
tutorialId: 'NOT_INITIALIZED',
isInitialized: false,
summaries: {},
};

const StudentStoreContext = createContext<StudentContext>({
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"private": true,
"version": "1.7.3",
"version": "1.7.5",
"workspaces": [
"client",
"server",
Expand Down
16 changes: 14 additions & 2 deletions server/src/services/pdf-service/PdfService.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,25 @@ class PdfService {
return this.attendancePDFModule.generatePDF({ tutorial, date });
}

public async generateStudentScheinOverviewPDF(): Promise<Buffer> {
public async getCensoredStudentScheinOverviewPDF(): Promise<Buffer> {
return this.generateStudentScheinOverviewPDF(true);
}

public async getClearTextStudentScheinOverviewPDF(): Promise<Buffer> {
return this.generateStudentScheinOverviewPDF(false);
}

private async generateStudentScheinOverviewPDF(shortMatriculationNo: boolean): Promise<Buffer> {
const [students, summaries] = await Promise.all([
studentService.getAllStudentsAsDocuments(),
scheincriteriaService.getCriteriaResultsOfAllStudents(),
]);

return this.scheinResultsPDFModule.generatePDF({ students, summaries });
return this.scheinResultsPDFModule.generatePDF({
students,
summaries,
enableShortMatriculatinNo: shortMatriculationNo,
});
}

public async generateCredentialsPDF(): Promise<Buffer> {
Expand Down
11 changes: 9 additions & 2 deletions server/src/services/pdf-service/PdfService.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,15 @@ pdfRouter.get(
}
);

pdfRouter.get('/scheinstatus', ...checkRoleAccess(Role.ADMIN), async (_, res) => {
const pdfBuffer = await pdfService.generateStudentScheinOverviewPDF();
pdfRouter.get('/scheinstatus', ...checkRoleAccess(Role.ADMIN), async (req, res) => {
const pdfBuffer = await pdfService.getCensoredStudentScheinOverviewPDF();

res.contentType('pdf');
res.send(pdfBuffer);
});

pdfRouter.get('/scheinoverview', ...checkRoleAccess(Role.ADMIN), async (req, res) => {
const pdfBuffer = await pdfService.getClearTextStudentScheinOverviewPDF();

res.contentType('pdf');
res.send(pdfBuffer);
Expand Down
Loading

0 comments on commit b45810b

Please sign in to comment.