Skip to content

Commit

Permalink
Added internships to Calendar
Browse files Browse the repository at this point in the history
- Added "isArchived" field on internships, will be used later.
- Fixed a bug when choosing a color for departments.
  • Loading branch information
BenjaVR committed Feb 7, 2019
1 parent 8e59ec5 commit f3c045f
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 14 deletions.
7 changes: 3 additions & 4 deletions src/components/departments/DepartmentsFormModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,8 @@ class DepartmentFormModal extends React.Component<DepartmentFormModalProps, IDep
this.setState({
capacityFieldIds,
});
this.props.form.getFieldDecorator<Department>("name", {
initialValue: departmentToEdit.name,
});
const fields: Partial<Department> = {
name: departmentToEdit.name,
color: departmentToEdit.color,
};
this.setState({ selectedColor: departmentToEdit.color });
Expand Down Expand Up @@ -231,9 +229,10 @@ class DepartmentFormModal extends React.Component<DepartmentFormModalProps, IDep
isSubmitting: true,
});

const color = values[nameof<Department>("color")];
const department = new Department(
values[nameof<Department>("name")],
values[nameof<Department>("color")].hex,
color.hex === undefined ? color : color.hex,
values[nameof<Department>("capacityPerEducation")],
);

Expand Down
1 change: 1 addition & 0 deletions src/components/planning/PlanningsFormModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ class PlanningsFormModal extends React.Component<PlanningsFormModalProps, IPlann
values[nameof<Internship>("startDate")] as moment.Moment,
values[nameof<Internship>("endDate")] as moment.Moment,
values[nameof<Internship>("hours")] as number,
false,
this.props.studentToPlan!.id!,
values[nameof<Internship>("departmentId") as string],
);
Expand Down
14 changes: 14 additions & 0 deletions src/components/planning/PlanningsPage.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
60 changes: 56 additions & 4 deletions src/components/planning/PlanningsPage.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -20,13 +22,16 @@ interface IPlanningsPageState {
isCalendarLoading: boolean;
isAddInternshipModalVisible: boolean;
internshipToEdit: Internship | undefined;
internships: Internship[];
areInternshipsLoading: boolean;
departments: Department[];
areDepartmentsLoading: boolean;
}

class PlanningsPage extends React.Component<PlanningsPageProps, IPlanningsPageState> {

private calendarRef: Calendar | null = null;
private unsubscribeFromInternships: () => void;

private readonly studentLoadFailedNotificationKey = "studentLoadFailed";
private readonly departmentLoadFailedNotificationKey = "departmentLoadFailed";
Expand All @@ -42,38 +47,55 @@ class PlanningsPage extends React.Component<PlanningsPageProps, IPlanningsPageSt
isCalendarLoading: false,
isAddInternshipModalVisible: false,
internshipToEdit: undefined,
internships: [],
areInternshipsLoading: true,
departments: [],
areDepartmentsLoading: false,
};

this.unsubscribeFromInternships = () => { 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);
this.addInternship = this.addInternship.bind(this);
this.closeAddInternshipModal = this.closeAddInternshipModal.bind(this);
}

public componentWillMount(): void {
public componentDidMount(): void {
this.unsubscribeFromInternships = InternshipsRepository.subscribeToInternships((internships) => {
this.setState({
areInternshipsLoading: false,
internships,
});
});
this.loadStudents();
this.loadDepartments();
}

public componentWillUnmount(): void {
this.unsubscribeFromInternships();
}

public render(): React.ReactNode {
return (
<React.Fragment>
<Row type="flex">
<Col span={24} xl={16}>
<Col span={24} xl={16} xxl={18}>
<Spin spinning={this.state.isCalendarLoading}>
<div className={styles.buttonsInCalendarComponent}>
<Button icon="left" onClick={this.handlePrevMonth} />
<Button icon="right" onClick={this.handleNextMonth} />
</div>
<Calendar mode="month" ref={(ref) => this.calendarRef = ref} />
<Spin spinning={this.state.areInternshipsLoading}>
<Calendar mode="month" ref={(ref) => this.calendarRef = ref} dateCellRender={this.renderDateCell} />
</Spin>
</Spin>
</Col>
<Col span={24} xl={8}>
<Col span={24} xl={8} xxl={6}>
<List
dataSource={this.state.unplannedStudents}
header={this.renderStudentListHeader()}
Expand Down Expand Up @@ -133,6 +155,36 @@ class PlanningsPage extends React.Component<PlanningsPageProps, IPlanningsPageSt
);
}

private renderDateCell(date: moment.Moment): React.ReactNode {
const internshipsToday = this.state.internships.filter((internship) => {
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 (
<Tag
key={department.id}
color={department.color}
style={{ border: `2px solid ${department.color}` }}
className={classNames(styles.notClickableTag, styles.calendarTag, { [styles.calendarTagOutline]: usedCapacity < totalCapacity })}
>
{usedCapacity} / {totalCapacity}
</Tag>
);
});
return (
<React.Fragment>
{departmentsRows}
</React.Fragment>
);
}

private handleReloadStudents(): void {
this.loadStudents();
}
Expand Down
1 change: 1 addition & 0 deletions src/entities/IInternship.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
14 changes: 14 additions & 0 deletions src/models/Department.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { IDepartment, IDepartmentEducationCapacity } from "../entities/IDepartment";
import { Education } from "./Education";
import { ModelBase } from "./ModelBase";

export class Department extends ModelBase<IDepartment> {

public get totalCapacity(): number {
return this.capacityPerEducation
.map((capacity) => capacity.capacity)
.reduce((capacity, total) => capacity + total, 0);
}

constructor(
public name: string,
public color: string,
Expand All @@ -25,6 +32,13 @@ export class Department extends ModelBase<IDepartment> {
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,
Expand Down
11 changes: 7 additions & 4 deletions src/models/Internship.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class Internship extends ModelBase<IInternship> {
public startDate: moment.Moment,
public endDate: moment.Moment,
public hours: number,
public isArchived: boolean,
public studentId: string,
public departmentId: string | undefined,
) {
Expand All @@ -17,9 +18,10 @@ export class Internship extends ModelBase<IInternship> {

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,
);
Expand All @@ -30,12 +32,13 @@ export class Internship extends ModelBase<IInternship> {
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,
};
Expand Down
4 changes: 2 additions & 2 deletions src/models/ModelBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export abstract class ModelBase<T extends IFirestoreEntityBase> {
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(),
};

Expand Down
11 changes: 11 additions & 0 deletions src/services/repositories/InternshipsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ import { FirestoreRefs } from "../FirestoreRefs";

export class InternshipsRepository {

public static subscribeToInternships(onListen: (internships: Internship[]) => void): () => void {
return FirestoreRefs.getInternshipCollectionRef()
.where(nameof<IInternship>("isArchived"), "==", false)
.orderBy(nameof<IInternship>("updatedTimestamp"), "desc")
.onSnapshot((querySnapshot) => {
const internshipEntities = FirebaseModelMapper.mapDocsToObjects<IInternship>(querySnapshot.docs);
const internships = internshipEntities.map((entity) => Internship.fromEntity(entity));
onListen(internships);
});
}

public static async getInternshipsForStudent(student: Student): Promise<Internship[]> {
const querySnapshot = await FirestoreRefs.getInternshipCollectionRef()
.where(nameof<IInternship>("studentId"), "==", student.id)
Expand Down
1 change: 1 addition & 0 deletions src/services/repositories/StudentsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class StudentsRepository {

public static subscribeToStudents(onListen: (students: Student[]) => void): () => void {
return FirestoreRefs.getStudentCollectionRef()
.where(nameof<IStudent>("isArchived"), "==", false)
.orderBy(nameof<IStudent>("updatedTimestamp"), "desc")
.onSnapshot((querySnapshot) => {
const studentEntities = FirebaseModelMapper.mapDocsToObjects<IStudent>(querySnapshot.docs);
Expand Down

1 comment on commit f3c045f

@BenjaVR
Copy link
Owner

@BenjaVR BenjaVR commented on f3c045f Feb 7, 2019

Choose a reason for hiding this comment

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

#46

Please sign in to comment.