Skip to content

Commit

Permalink
Show warning with intersecting dates when planning over department's …
Browse files Browse the repository at this point in the history
…capacity
  • Loading branch information
BenjaVR committed Feb 24, 2019
1 parent 34af28c commit 59020af
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 29 deletions.
81 changes: 70 additions & 11 deletions src/components/planning/PlanningsFormModal.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Col, DatePicker, Form, InputNumber, Modal, Row, Select } from "antd";
import { Col, DatePicker, Form, InputNumber, Modal, notification, Row, Select } from "antd";
import { FormComponentProps } from "antd/lib/form";
import FormItem from "antd/lib/form/FormItem";
import moment from "moment";
import React from "react";
import { isMomentDayAfterOrTheSameAsOtherDay } from "../../helpers/comparers";
import { studentsPlannedFullyInRange, studentsPlannedPartiallyInRange, studentsPlannedInDay } from "../../helpers/filters";
import { nameof } from "../../helpers/nameof";
import { FormValidationTrigger } from "../../helpers/types";
import { Department } from "../../models/Department";
import { IStudentInternship, Student } from "../../models/Student";
import { StudentsRepository } from "../../services/repositories/StudentsRepository";
import styles from "./PlanningsFormModal.module.scss";

interface IPlanningsFormModalProps {
Expand Down Expand Up @@ -225,24 +227,69 @@ class PlanningsFormModal extends React.Component<PlanningsFormModalProps, IPlann
const fields: Array<keyof IStudentInternship> = ["startDate", "endDate", "hours", "departmentId"];
this.props.form.validateFieldsAndScroll(fields, (errors, values) => {
if (!errors) {
this.setState({
isSubmitting: true,
});

const internship: IStudentInternship = {
startDate: values[nameof<IStudentInternship>("startDate")] as moment.Moment,
endDate: values[nameof<IStudentInternship>("endDate")] as moment.Moment,
hours: values[nameof<IStudentInternship>("hours")] as number,
departmentId: values[nameof<IStudentInternship>("departmentId") as string],
};

this.props.submitInternship(internship)
.then(() => {
this.handleClose();
const department = this.props.departments.find((dep) => dep.id === internship.departmentId);
if (department === undefined) {
notification.warning({
message: "Er ging iets fout, probeer later opnieuw",
});
return;
}

this.setState({
isSubmitting: true,
});
StudentsRepository.getPlannedStudentsWithDepartment(department)
.then((studentsWithDepartment) => {
const students = studentsPlannedFullyInRange(studentsWithDepartment, internship.startDate, internship.endDate)
.concat(studentsPlannedPartiallyInRange(studentsWithDepartment, internship.startDate, internship.endDate));

const isCrossingTheCapacityLimits = department.totalCapacity <= department.getUsedCapacity(students);

if (isCrossingTheCapacityLimits) {
const intersectingDates: moment.Moment[] = [];
for (const m = moment(internship.startDate); m.diff(internship.endDate, "days") <= 0; m.add(1, "day")) {
if (department.totalCapacity <= department.getUsedCapacity(studentsPlannedInDay(students, m))) {
intersectingDates.push(moment(m));
}
}
Modal.confirm({
title: "Capaciteit overschreden",
content: (
<React.Fragment>
<p>Er is minstens één geselecteerde dag waar de capaciteit voor <b>{department.name}</b> werd overschreden.</p>
<p>Bent u zeker dat u wilt verdergaan?</p>
<ul>
{intersectingDates.map((intersectingDate) => {
return <li key={intersectingDate.unix()}>{intersectingDate.format("DD MMMM YYYY")}</li>;
})}
</ul>
</React.Fragment>
),
cancelText: "Terugkeren",
onCancel: () => {
this.setState({
isSubmitting: false,
});
},
okText: "Toch toevoegen",
onOk: () => {
this.doSubmit(internship);
},
});
} else {
this.doSubmit(internship);
}
})
.finally(() => {
this.setState({
isSubmitting: false,
.catch(() => {
notification.warning({
message: "Er ging iets fout, probeer later opnieuw",
});
});
}
Expand All @@ -255,6 +302,18 @@ class PlanningsFormModal extends React.Component<PlanningsFormModalProps, IPlann
selectedEndDate: null,
});
}

private async doSubmit(internship: IStudentInternship): Promise<void> {
return this.props.submitInternship(internship)
.then(() => {
this.handleClose();
})
.finally(() => {
this.setState({
isSubmitting: false,
});
});
}
}

const WrappedPlanningsFormModal = Form.create<PlanningsFormModalProps>()(PlanningsFormModal);
Expand Down
5 changes: 1 addition & 4 deletions src/components/planning/PlanningsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,7 @@ class PlanningsPage extends React.Component<PlanningsPageProps, IPlanningsPageSt
private renderDateCell(date: moment.Moment): React.ReactNode {
const plannedStudentsToday = studentsPlannedInDay(this.state.plannedStudents, date);
const departmentsRows = this.state.departments.map((department) => {
const usedCapacity = plannedStudentsToday.filter((student) => {
return student.internship !== undefined
&& student.internship.departmentId === department.id;
}).length;
const usedCapacity = department.getUsedCapacity(plannedStudentsToday);
const totalCapacity = department.totalCapacity;
return (
<div key={department.id} className={styles.calendarTagContainer}>
Expand Down
21 changes: 7 additions & 14 deletions src/components/schools/SchoolInternshipSummaryModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import FormItem from "antd/lib/form/FormItem";
import moment from "moment";
import React from "react";
import { isMomentDayAfterOrTheSameAsOtherDay } from "../../helpers/comparers";
import { studentsPlannedFullyInRange, studentsPlannedPartiallyInRange } from "../../helpers/filters";
import { nameof } from "../../helpers/nameof";
import { Department } from "../../models/Department";
import { Education } from "../../models/Education";
Expand Down Expand Up @@ -78,23 +79,15 @@ class SchoolInternshipSummaryModal extends React.Component<SchoolInternshipSumma
}

public render(): React.ReactNode {
const studentsFullyInPeriod = this.state.studentsWithInternship.filter((student) => {
return this.state.selectedStartDate !== undefined && this.state.selectedEndDate !== undefined && student.internship !== undefined
&& student.internship.startDate.isSameOrAfter(this.state.selectedStartDate)
&& student.internship.endDate.isSameOrBefore(this.state.selectedEndDate);
});
const studentsNotFullyInPeriod = this.state.studentsWithInternship.filter((student) => {
return this.state.selectedStartDate !== undefined && this.state.selectedEndDate !== undefined && student.internship !== undefined
&& ((student.internship.startDate.isBefore(this.state.selectedStartDate) && student.internship.endDate.isSameOrAfter(this.state.selectedStartDate))
|| (student.internship.endDate.isAfter(this.state.selectedEndDate) && student.internship.startDate.isSameOrBefore(this.state.selectedEndDate)));
});
const studentsFullyInPeriod = studentsPlannedFullyInRange(this.state.studentsWithInternship, this.state.selectedStartDate, this.state.selectedEndDate);
const studentsPartiallyInPeriod = studentsPlannedPartiallyInRange(this.state.studentsWithInternship, this.state.selectedStartDate, this.state.selectedEndDate);

const totalStudentsCount = studentsFullyInPeriod.length + studentsNotFullyInPeriod.length;
const totalInternshipDaysCount = [...studentsFullyInPeriod, ...studentsNotFullyInPeriod]
const totalStudentsCount = studentsFullyInPeriod.length + studentsPartiallyInPeriod.length;
const totalInternshipDaysCount = [...studentsFullyInPeriod, ...studentsPartiallyInPeriod]
.map((student) => student.getInternshipNumberOfDaysInRange(this.state.selectedStartDate, this.state.selectedEndDate))
.reduce((sum, days) => sum + days, 0);

const totalInternshipHoursCount = [...studentsFullyInPeriod, ...studentsNotFullyInPeriod]
const totalInternshipHoursCount = [...studentsFullyInPeriod, ...studentsPartiallyInPeriod]
.map((student) => student.getInternshipNumberOfHoursInRange(this.state.selectedStartDate, this.state.selectedEndDate))
.reduce((sum, hours) => sum + hours, 0);

Expand Down Expand Up @@ -173,7 +166,7 @@ class SchoolInternshipSummaryModal extends React.Component<SchoolInternshipSumma
<Card>
{this.renderStudentsFullyInPeriod(studentsFullyInPeriod)}
<br />
{this.renderStudentsNotFullyInPeriod(studentsNotFullyInPeriod)}
{this.renderStudentsNotFullyInPeriod(studentsPartiallyInPeriod)}
</Card>
</React.Fragment>
}
Expand Down
16 changes: 16 additions & 0 deletions src/helpers/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,19 @@ export function studentsPlannedInDay(students: Student[], date: moment.Moment):
);
});
}

export function studentsPlannedFullyInRange(students: Student[], from: moment.Moment | undefined, until: moment.Moment | undefined): Student[] {
return students.filter((student) => {
return from !== undefined && until !== undefined && student.internship !== undefined
&& student.internship.startDate.isSameOrAfter(from)
&& student.internship.endDate.isSameOrBefore(until);
});
}

export function studentsPlannedPartiallyInRange(students: Student[], from: moment.Moment | undefined, until: moment.Moment | undefined): Student[] {
return students.filter((student) => {
return from !== undefined && until !== undefined && student.internship !== undefined
&& ((student.internship.startDate.isBefore(from) && student.internship.endDate.isSameOrAfter(from))
|| (student.internship.endDate.isAfter(until) && student.internship.startDate.isSameOrBefore(until)));
});
}
8 changes: 8 additions & 0 deletions src/models/Department.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IDepartment, IDepartmentEducationCapacity } from "../entities/IDepartment";
import { Education } from "./Education";
import { ModelBase } from "./ModelBase";
import { Student } from "./Student";

export class Department extends ModelBase<IDepartment> {

Expand Down Expand Up @@ -32,6 +33,13 @@ export class Department extends ModelBase<IDepartment> {
return department;
}

public getUsedCapacity(students: Student[]): number {
return students.filter((student) => {
return student.internship !== undefined
&& student.internship.departmentId === this.id;
}).length;
}

public getCapacityForEducation(education: Education): number {
const educationCapacity = this.capacityPerEducation.find((capacity) => capacity.educationId === education.id);
return educationCapacity === undefined
Expand Down

1 comment on commit 59020af

@BenjaVR
Copy link
Owner

@BenjaVR BenjaVR commented on 59020af Feb 24, 2019

Choose a reason for hiding this comment

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

#64

Please sign in to comment.