Skip to content

Commit

Permalink
Add educations relation to students table/form/page
Browse files Browse the repository at this point in the history
  • Loading branch information
BenjaVR committed Jan 9, 2019
1 parent e5087e9 commit a5c7c35
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 1 deletion.
33 changes: 32 additions & 1 deletion web/src/components/students/StudentsFormModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FormComponentProps } from "antd/lib/form";
import FormItem from "antd/lib/form/FormItem";
import React from "react";
import { FormValidationTrigger } from "../../helpers/types";
import { IEducation } from "../../models/Education";
import { ISchool } from "../../models/School";
import { IStudent } from "../../models/Student";

Expand All @@ -13,6 +14,8 @@ interface IStudentsFormModalProps {
studentToEdit: IStudent | undefined;
schools: ISchool[];
isLoadingSchools: boolean;
educations: IEducation[];
isLoadingEducations: boolean;
onCloseRequest: () => void;
submitStudent(student: IStudent): Promise<void>;
}
Expand Down Expand Up @@ -52,10 +55,19 @@ class StudentsFormModal extends React.Component<StudentFormModalProps, IStudents
: undefined;
}

let educationId;
const studentEducationId = this.props.studentToEdit.educationId;
if (studentEducationId !== undefined) {
educationId = this.props.educations.some((e) => e.id === studentEducationId)
? studentEducationId
: undefined;
}

this.props.form.setFieldsValue({ // TODO: fill these keys dynamically
firstName: this.props.studentToEdit.firstName,
lastName: this.props.studentToEdit.lastName,
schoolId,
educationId,
});
}
}
Expand Down Expand Up @@ -109,6 +121,19 @@ class StudentsFormModal extends React.Component<StudentFormModalProps, IStudents
</Select>,
)}
</FormItem>
<FormItem label="Opleiding">
{getFieldDecorator<IStudent>("educationId", {
validateTrigger: this.state.formValidateTrigger,
})(
<Select
disabled={this.state.isSubmitting}
loading={this.props.isLoadingEducations}
allowClear={true}
>
{this.props.educations.map(this.renderEducationSelectOption)}
</Select>,
)}
</FormItem>
</Form>
</Modal>
);
Expand All @@ -120,6 +145,12 @@ class StudentsFormModal extends React.Component<StudentFormModalProps, IStudents
);
}

private renderEducationSelectOption(education: IEducation): React.ReactNode {
return (
<Select.Option key={education.id} value={education.id}>{education.name}</Select.Option>
);
}

private doClose(): void {
this.props.onCloseRequest();
}
Expand All @@ -130,7 +161,7 @@ class StudentsFormModal extends React.Component<StudentFormModalProps, IStudents
formValidateTrigger: "onChange",
});

const fields: Array<keyof IStudent> = ["firstName", "lastName", "schoolId"];
const fields: Array<keyof IStudent> = ["firstName", "lastName", "schoolId", "educationId"];
this.props.form.validateFieldsAndScroll(fields, (errors, values) => {
if (!errors) {
this.setState({
Expand Down
19 changes: 19 additions & 0 deletions web/src/components/students/StudentsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Col, notification, Row } from "antd";
import React from "react";
import { IEducation } from "../../models/Education";
import { ISchool } from "../../models/School";
import { IStudent } from "../../models/Student";
import { RoutePageComponentProps, routes } from "../../routes";
import { EducationsService } from "../../services/EducationsService";
import { SchoolsService } from "../../services/SchoolsService";
import { StudentsService } from "../../services/StudentsService";
import AuthenticatedLayout from "../layouts/AuthenticatedLayout";
Expand All @@ -19,12 +21,15 @@ interface IStudentsPageState {
studentToEdit: IStudent | undefined;
schools: ISchool[];
isFetchingSchools: boolean;
educations: IEducation[];
isFetchingEducations: boolean;
}

export default class StudentsPage extends React.Component<StudentsPageProps, IStudentsPageState> {

private readonly studentsService = new StudentsService();
private readonly schoolsService = new SchoolsService();
private readonly educationsService = new EducationsService();

constructor(props: StudentsPageProps) {
super(props);
Expand All @@ -37,6 +42,8 @@ export default class StudentsPage extends React.Component<StudentsPageProps, ISt
studentToEdit: undefined,
schools: [],
isFetchingSchools: true,
educations: [],
isFetchingEducations: true,
};

this.openAddStudentModal = this.openAddStudentModal.bind(this);
Expand All @@ -61,6 +68,12 @@ export default class StudentsPage extends React.Component<StudentsPageProps, ISt
schools,
});
});
this.educationsService.subscribe((educations) => {
this.setState({
isFetchingEducations: false,
educations,
});
});
}

public render(): React.ReactNode {
Expand All @@ -77,6 +90,8 @@ export default class StudentsPage extends React.Component<StudentsPageProps, ISt
onEditStudentRequest={this.openEditStudentModal}
schools={this.state.schools}
isLoadingSchools={this.state.isFetchingSchools}
educations={this.state.educations}
isLoadingEducations={this.state.isFetchingEducations}
/>
</Col>
</Row>
Expand All @@ -90,6 +105,8 @@ export default class StudentsPage extends React.Component<StudentsPageProps, ISt
studentToEdit={undefined}
schools={this.state.schools}
isLoadingSchools={this.state.isFetchingSchools}
educations={this.state.educations}
isLoadingEducations={this.state.isFetchingEducations}
/>
<StudentFormModal
title="Student bewerken"
Expand All @@ -100,6 +117,8 @@ export default class StudentsPage extends React.Component<StudentsPageProps, ISt
studentToEdit={this.state.studentToEdit}
schools={this.state.schools}
isLoadingSchools={this.state.isFetchingSchools}
educations={this.state.educations}
isLoadingEducations={this.state.isFetchingEducations}
/>
</React.Fragment>
);
Expand Down
51 changes: 51 additions & 0 deletions web/src/components/students/StudentsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ColumnFilterItem, ColumnProps } from "antd/lib/table";
import React from "react";
import { emptyFilterOptionValue, exactMatchOrDefaultOptionFilter, hasElementWithId } from "../../helpers/filters";
import { stringSorter } from "../../helpers/sorters";
import { IEducation } from "../../models/Education";
import { ISchool } from "../../models/School";
import { IStudent } from "../../models/Student";
import styles from "../DataTable.module.scss";
Expand All @@ -12,6 +13,8 @@ interface IStudentsTableProps {
students: IStudent[];
schools: ISchool[];
isLoadingSchools: boolean;
educations: IEducation[];
isLoadingEducations: boolean;
deleteStudent: (student: IStudent) => Promise<void>;
onAddStudentRequest: () => void;
onEditStudentRequest: (student: IStudent) => void;
Expand Down Expand Up @@ -57,6 +60,18 @@ class StudentsTable extends React.Component<IStudentsTableProps> {
return this.getSchoolNameForStudent(student);
}

private renderEducationName(student: IStudent): React.ReactNode {
if (this.props.isLoadingEducations) {
return <Spin size="small" spinning={true} />;
}

if (student.educationId === undefined) {
return "";
}

return this.getEducationNameForStudent(student);
}

private renderActions(student: IStudent): React.ReactNode {
const deleteFunc = () => this.props.deleteStudent(student);
const editFunc = () => this.props.onEditStudentRequest(student);
Expand Down Expand Up @@ -115,6 +130,15 @@ class StudentsTable extends React.Component<IStudentsTableProps> {
return school.name;
}

private getEducationNameForStudent(student: IStudent): string {
const education = this.props.educations.find((e) => student.educationId !== undefined && student.educationId === e.id);
if (education === undefined) {
return "";
}

return education.name;
}

private getTableColumns(): Array<ColumnProps<IStudent>> {
return [
{
Expand All @@ -136,6 +160,19 @@ class StudentsTable extends React.Component<IStudentsTableProps> {
: undefined);
},
},
{
title: "Opleiding",
key: "education",
render: (record: IStudent) => this.renderEducationName(record),
sorter: (a, b) => stringSorter(this.getEducationNameForStudent(a), this.getEducationNameForStudent(b)),
filters: this.getEducationFilters(),
onFilter: (value, record: IStudent) => {
return exactMatchOrDefaultOptionFilter(value,
hasElementWithId(this.props.educations, record.educationId)
? record.educationId
: undefined);
},
},
{
title: "Acties",
key: "actions",
Expand All @@ -159,6 +196,20 @@ class StudentsTable extends React.Component<IStudentsTableProps> {
});
return filters;
}

private getEducationFilters(): ColumnFilterItem[] {
const filters: ColumnFilterItem[] = this.props.educations.map((education) => {
return {
text: education.name,
value: education.id!,
};
});
filters.unshift({
text: "Zonder opleiding",
value: emptyFilterOptionValue,
});
return filters;
}
}

export default StudentsTable;
1 change: 1 addition & 0 deletions web/src/models/Student.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export interface IStudent extends IFirebaseTable {
firstName: string;
lastName?: string;
schoolId?: string;
educationId?: string;
}

2 comments on commit a5c7c35

@BenjaVR
Copy link
Owner

@BenjaVR BenjaVR commented on a5c7c35 Jan 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#17

@BenjaVR
Copy link
Owner

@BenjaVR BenjaVR commented on a5c7c35 Jan 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#18

Please sign in to comment.