diff --git a/src/components/departments/DepartmentsFormModal.tsx b/src/components/departments/DepartmentsFormModal.tsx index 3b8a803..3a21926 100644 --- a/src/components/departments/DepartmentsFormModal.tsx +++ b/src/components/departments/DepartmentsFormModal.tsx @@ -68,10 +68,8 @@ class DepartmentFormModal extends React.Component("name", { - initialValue: departmentToEdit.name, - }); const fields: Partial = { + name: departmentToEdit.name, color: departmentToEdit.color, }; this.setState({ selectedColor: departmentToEdit.color }); @@ -231,9 +229,10 @@ class DepartmentFormModal extends React.Component("color")]; const department = new Department( values[nameof("name")], - values[nameof("color")].hex, + color.hex === undefined ? color : color.hex, values[nameof("capacityPerEducation")], ); diff --git a/src/components/planning/PlanningsFormModal.tsx b/src/components/planning/PlanningsFormModal.tsx index 631dda4..5be9c30 100644 --- a/src/components/planning/PlanningsFormModal.tsx +++ b/src/components/planning/PlanningsFormModal.tsx @@ -185,6 +185,7 @@ class PlanningsFormModal extends React.Component("startDate")] as moment.Moment, values[nameof("endDate")] as moment.Moment, values[nameof("hours")] as number, + false, this.props.studentToPlan!.id!, values[nameof("departmentId") as string], ); diff --git a/src/components/planning/PlanningsPage.module.scss b/src/components/planning/PlanningsPage.module.scss index 3c0c723..da582d9 100644 --- a/src/components/planning/PlanningsPage.module.scss +++ b/src/components/planning/PlanningsPage.module.scss @@ -4,7 +4,21 @@ pointer-events: none; } +.calendarTag { + display: block !important; + font-size: 0.7rem !important; + height: 15px !important; + margin: 1px 0 !important; + line-height: 10px !important; + + &Outline { + background-color: transparent !important; + color: black !important; + } +} + .buttonsInCalendarComponent { + z-index: 100; padding: 11px 16px; position: absolute; diff --git a/src/components/planning/PlanningsPage.tsx b/src/components/planning/PlanningsPage.tsx index a8b959d..8a8afe2 100644 --- a/src/components/planning/PlanningsPage.tsx +++ b/src/components/planning/PlanningsPage.tsx @@ -1,4 +1,6 @@ import { Button, Calendar, Col, List, notification, Row, Spin, Tag, Tooltip } from "antd"; +import classNames from "classnames"; +import moment from "moment"; import React from "react"; import { Department } from "../../models/Department"; import { Internship } from "../../models/Internship"; @@ -20,6 +22,8 @@ interface IPlanningsPageState { isCalendarLoading: boolean; isAddInternshipModalVisible: boolean; internshipToEdit: Internship | undefined; + internships: Internship[]; + areInternshipsLoading: boolean; departments: Department[]; areDepartmentsLoading: boolean; } @@ -27,6 +31,7 @@ interface IPlanningsPageState { class PlanningsPage extends React.Component { private calendarRef: Calendar | null = null; + private unsubscribeFromInternships: () => void; private readonly studentLoadFailedNotificationKey = "studentLoadFailed"; private readonly departmentLoadFailedNotificationKey = "departmentLoadFailed"; @@ -42,12 +47,17 @@ class PlanningsPage extends React.Component { return; }; + this.renderStudentListHeader = this.renderStudentListHeader.bind(this); this.renderStudentListItem = this.renderStudentListItem.bind(this); + this.renderDateCell = this.renderDateCell.bind(this); this.handleReloadStudents = this.handleReloadStudents.bind(this); this.handleNextMonth = this.handleNextMonth.bind(this); this.handlePrevMonth = this.handlePrevMonth.bind(this); @@ -55,25 +65,37 @@ class PlanningsPage extends React.Component { + this.setState({ + areInternshipsLoading: false, + internships, + }); + }); this.loadStudents(); this.loadDepartments(); } + public componentWillUnmount(): void { + this.unsubscribeFromInternships(); + } + public render(): React.ReactNode { return ( - +
- this.calendarRef = ref} /> + + this.calendarRef = ref} dateCellRender={this.renderDateCell} /> +
- + { + return date.startOf("day").isBetween( + internship.startDate.startOf("day"), + internship.endDate.startOf("day"), + "day", + "[]", + ); + }); + const departmentsRows = this.state.departments.map((department) => { + const usedCapacity = internshipsToday.filter((internship) => internship.departmentId === department.id).length; + const totalCapacity = department.totalCapacity; + return ( + + {usedCapacity} / {totalCapacity} + + ); + }); + return ( + + {departmentsRows} + + ); + } + private handleReloadStudents(): void { this.loadStudents(); } diff --git a/src/entities/IInternship.ts b/src/entities/IInternship.ts index a8c8d7e..f7f5a90 100644 --- a/src/entities/IInternship.ts +++ b/src/entities/IInternship.ts @@ -4,6 +4,7 @@ export interface IInternship extends IFirestoreEntityBase { startDate: firebase.firestore.Timestamp; endDate: firebase.firestore.Timestamp; hours: number; + isArchived: boolean; studentId: string | undefined; departmentId: string | undefined; } diff --git a/src/models/Department.ts b/src/models/Department.ts index 9417574..52c8749 100644 --- a/src/models/Department.ts +++ b/src/models/Department.ts @@ -1,8 +1,15 @@ import { IDepartment, IDepartmentEducationCapacity } from "../entities/IDepartment"; +import { Education } from "./Education"; import { ModelBase } from "./ModelBase"; export class Department extends ModelBase { + public get totalCapacity(): number { + return this.capacityPerEducation + .map((capacity) => capacity.capacity) + .reduce((capacity, total) => capacity + total, 0); + } + constructor( public name: string, public color: string, @@ -25,6 +32,13 @@ export class Department extends ModelBase { return department; } + public getCapacityForEducation(education: Education): number { + const educationCapacity = this.capacityPerEducation.find((capacity) => capacity.educationId === education.id); + return educationCapacity === undefined + ? 0 + : educationCapacity.capacity; + } + protected getEntityInternal(): IDepartment { return { name: this.name, diff --git a/src/models/Internship.ts b/src/models/Internship.ts index 3c43985..fecb640 100644 --- a/src/models/Internship.ts +++ b/src/models/Internship.ts @@ -9,6 +9,7 @@ export class Internship extends ModelBase { public startDate: moment.Moment, public endDate: moment.Moment, public hours: number, + public isArchived: boolean, public studentId: string, public departmentId: string | undefined, ) { @@ -17,9 +18,10 @@ export class Internship extends ModelBase { public static fromEntity(entity: IInternship): Internship { const internship = new Internship( - moment.utc(entity.startDate.toDate()), - moment.utc(entity.endDate.toDate()), + moment(entity.startDate.toDate()), + moment(entity.endDate.toDate()), entity.hours || 0, + entity.isArchived || false, entity.studentId!, entity.departmentId, ); @@ -30,12 +32,13 @@ export class Internship extends ModelBase { protected getEntityInternal(): IInternship { return { startDate: Firebase.firestore.Timestamp.fromDate( - moment.utc(this.startDate.toDate()).startOf("day").toDate(), + this.startDate.startOf("day").toDate(), ), endDate: Firebase.firestore.Timestamp.fromDate( - moment.utc(this.endDate.toDate()).startOf("day").toDate(), + this.endDate.startOf("day").toDate(), ), hours: this.hours, + isArchived: this.isArchived, studentId: this.studentId, departmentId: this.departmentId, }; diff --git a/src/models/ModelBase.ts b/src/models/ModelBase.ts index 700d3b0..5bb0336 100644 --- a/src/models/ModelBase.ts +++ b/src/models/ModelBase.ts @@ -15,10 +15,10 @@ export abstract class ModelBase { id: this.id, createdTimestamp: this.createdDate === undefined ? null - : Firebase.firestore.Timestamp.fromDate(this.createdDate.utc().toDate()), + : Firebase.firestore.Timestamp.fromDate(this.createdDate.toDate()), updatedTimestamp: this.updatedDate === undefined ? null - : Firebase.firestore.Timestamp.fromDate(this.updatedDate.utc().toDate()), + : Firebase.firestore.Timestamp.fromDate(this.updatedDate.toDate()), ...this.getEntityInternal(), }; diff --git a/src/services/repositories/InternshipsRepository.ts b/src/services/repositories/InternshipsRepository.ts index 89ed227..b6c59a6 100644 --- a/src/services/repositories/InternshipsRepository.ts +++ b/src/services/repositories/InternshipsRepository.ts @@ -9,6 +9,17 @@ import { FirestoreRefs } from "../FirestoreRefs"; export class InternshipsRepository { + public static subscribeToInternships(onListen: (internships: Internship[]) => void): () => void { + return FirestoreRefs.getInternshipCollectionRef() + .where(nameof("isArchived"), "==", false) + .orderBy(nameof("updatedTimestamp"), "desc") + .onSnapshot((querySnapshot) => { + const internshipEntities = FirebaseModelMapper.mapDocsToObjects(querySnapshot.docs); + const internships = internshipEntities.map((entity) => Internship.fromEntity(entity)); + onListen(internships); + }); + } + public static async getInternshipsForStudent(student: Student): Promise { const querySnapshot = await FirestoreRefs.getInternshipCollectionRef() .where(nameof("studentId"), "==", student.id) diff --git a/src/services/repositories/StudentsRepository.ts b/src/services/repositories/StudentsRepository.ts index b1d7aec..2c19c73 100644 --- a/src/services/repositories/StudentsRepository.ts +++ b/src/services/repositories/StudentsRepository.ts @@ -11,6 +11,7 @@ export class StudentsRepository { public static subscribeToStudents(onListen: (students: Student[]) => void): () => void { return FirestoreRefs.getStudentCollectionRef() + .where(nameof("isArchived"), "==", false) .orderBy(nameof("updatedTimestamp"), "desc") .onSnapshot((querySnapshot) => { const studentEntities = FirebaseModelMapper.mapDocsToObjects(querySnapshot.docs);