diff --git a/src/components/planning/PlanningsFormModal.tsx b/src/components/planning/PlanningsFormModal.tsx index 71ebf43..abfdd97 100644 --- a/src/components/planning/PlanningsFormModal.tsx +++ b/src/components/planning/PlanningsFormModal.tsx @@ -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 { @@ -225,10 +227,6 @@ class PlanningsFormModal extends React.Component = ["startDate", "endDate", "hours", "departmentId"]; this.props.form.validateFieldsAndScroll(fields, (errors, values) => { if (!errors) { - this.setState({ - isSubmitting: true, - }); - const internship: IStudentInternship = { startDate: values[nameof("startDate")] as moment.Moment, endDate: values[nameof("endDate")] as moment.Moment, @@ -236,13 +234,62 @@ class PlanningsFormModal extends React.Component("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: ( + +

Er is minstens één geselecteerde dag waar de capaciteit voor {department.name} werd overschreden.

+

Bent u zeker dat u wilt verdergaan?

+
    + {intersectingDates.map((intersectingDate) => { + return
  • {intersectingDate.format("DD MMMM YYYY")}
  • ; + })} +
+
+ ), + 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", }); }); } @@ -255,6 +302,18 @@ class PlanningsFormModal extends React.Component { + return this.props.submitInternship(internship) + .then(() => { + this.handleClose(); + }) + .finally(() => { + this.setState({ + isSubmitting: false, + }); + }); + } } const WrappedPlanningsFormModal = Form.create()(PlanningsFormModal); diff --git a/src/components/planning/PlanningsPage.tsx b/src/components/planning/PlanningsPage.tsx index 6b41d8c..7f7b155 100644 --- a/src/components/planning/PlanningsPage.tsx +++ b/src/components/planning/PlanningsPage.tsx @@ -214,10 +214,7 @@ class PlanningsPage extends React.Component { - 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 (
diff --git a/src/components/schools/SchoolInternshipSummaryModal.tsx b/src/components/schools/SchoolInternshipSummaryModal.tsx index f213319..6db6161 100644 --- a/src/components/schools/SchoolInternshipSummaryModal.tsx +++ b/src/components/schools/SchoolInternshipSummaryModal.tsx @@ -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"; @@ -78,23 +79,15 @@ class SchoolInternshipSummaryModal extends React.Component { - 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); @@ -173,7 +166,7 @@ class SchoolInternshipSummaryModal extends React.Component {this.renderStudentsFullyInPeriod(studentsFullyInPeriod)}
- {this.renderStudentsNotFullyInPeriod(studentsNotFullyInPeriod)} + {this.renderStudentsNotFullyInPeriod(studentsPartiallyInPeriod)} } diff --git a/src/helpers/filters.ts b/src/helpers/filters.ts index ec2f324..1cdab62 100644 --- a/src/helpers/filters.ts +++ b/src/helpers/filters.ts @@ -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))); + }); +} diff --git a/src/models/Department.ts b/src/models/Department.ts index 42a8764..444a9f1 100644 --- a/src/models/Department.ts +++ b/src/models/Department.ts @@ -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 { @@ -32,6 +33,13 @@ export class Department extends ModelBase { 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