I am currently trying to generate a View with React Typescript where I can show all Appointments of a day (similar to outlook calendar). But I am facing an issue. When Appointments are overlapping how can I determine the with and position? So I guess that I probably need an algorithm accomplish this issue.
Here an example how it could be structured:
Every box stands for an Appointment
The classes of the Object looks like this:
class AppointmentStructured {
appointment: Appointment
width?: number
left?: number
}
class Appointment {
start: Date
end: Date
subject:string
duration: number
}
I am looking for a solution where I can determine the width (max. 1 = 100%) of the specific appointment and additionally I need the position. The easiest would be to how far it is from the left (max. 1). When there are more than 2 appointments starting at the same time, the appointments are ordered by the appointment with the longest duration.
Example the third box from the image would be:
- width: 0.333
- left: 0.666
Out of this data I can use CSS to design them. The generated CSS then would look more or less like this:
position: absolute;
width: calc(100% * 0.33333);
top: 20rem; //doesn't matter for the algorithm
left: calc(100% * 0.66666)
--Edit This is how far i came. It starts with the method generateAppointmentTile([])
export interface AppointmentStructured {
appointment: Appointment
width: string | number;
left: string | number
}
export const generateAppointmentTile = (appointments: Appointment[]): AppointmentStructured[] => {
var appointmentsStructured = initAppointmentStructured(appointments);
var response: AppointmentStructured[] = [];
for (var i = 0; i < appointmentsStructured.length; ++i) {
var width = 1;
var previous = getPreviousOverlapping(appointmentsStructured, i);
var previousWidth = previous
.map((item: AppointmentStructured): number => item.width as number)
.reduce((a: number, b: number): number => a + b, 0)
var forward = getForwardOverlapping(appointmentsStructured, i);
var forwardOverlays = appointmentOverlays(forward);
var previousHasOverlayWithForward = false;
previous.forEach((structured: AppointmentStructured) => checkAppointmentOverlaySingle(structured, forward) !== 0 ? previousHasOverlayWithForward = true : null);
width = (width - previousWidth) / (!previousHasOverlayWithForward && forwardOverlays !== 0 ? forwardOverlays : 1);
appointmentsStructured[i].width = width;
response.push(appointmentsStructured[i]);
}
response.forEach((value: AppointmentStructured): void => {
value.width = `calc((100% - 8rem) * ${value.width})`;
});
return response
}
const appointmentOverlays = (reading: AppointmentStructured[]): number => {
var highestNumber = 0;
reading.forEach((structured: AppointmentStructured): void => {
var start = checkAppointmentOverlaySingle(structured, reading) + 1;
highestNumber = start > highestNumber ? start : highestNumber;
});
return highestNumber;
}
const checkAppointmentOverlaySingle = (structured: AppointmentStructured, reading: AppointmentStructured[]): number => {
var start = 0;
reading.forEach((item: AppointmentStructured): void => {
if (item.appointment.id !== structured.appointment.id) {
if ((structured.appointment.start <= item.appointment.start && structured.appointment.end >= item.appointment.start)
|| (structured.appointment.start >= item.appointment.start && structured.appointment.start <= item.appointment.end)) {
start += 1;
}
}
});
return start;
}
const getPreviousOverlapping = (appointmentsStructured: AppointmentStructured[], index: number): AppointmentStructured[] => {
var response: AppointmentStructured[] = [];
for (var i = index - 1; i >= 0; --i) {
if (appointmentsStructured[index].appointment.start >= appointmentsStructured[i].appointment.start
&& appointmentsStructured[index].appointment.start <= appointmentsStructured[i].appointment.end) {
response.push(appointmentsStructured[i]);
}
}
return response;
}
const getForwardOverlapping = (appointmentsStructured: AppointmentStructured[], index: number): AppointmentStructured[] => {
var response: AppointmentStructured[] = [];
for (var i = index; i < appointmentsStructured.length; ++i) {
if (appointmentsStructured[index].appointment.start >= appointmentsStructured[i].appointment.start
&& appointmentsStructured[index].appointment.start <= appointmentsStructured[i].appointment.end) {
response.push(appointmentsStructured[i]);
}
}
return response;
}
const initAppointmentStructured = (appointments: Appointment[]): AppointmentStructured[] => {
var appointmentsStructured: AppointmentStructured[] = appointments
.sort((a: Appointment, b: Appointment): number => a.start.getTime() - b.start.getTime())
.map((appointment: Appointment): AppointmentStructured => ({ appointment, width: 100, left: 0 }));
var response: AppointmentStructured[] = [];
// sort in a intelligent way
for (var i = 0; i < appointmentsStructured.length; ++i) {
var duration = appointmentsStructured[i].appointment.end.getTime() - appointmentsStructured[i].appointment.start.getTime();
var sameStartAppointments = findAppointmentWithSameStart(appointmentsStructured[i], appointmentsStructured);
var hasLongerAppointment: boolean = false;
sameStartAppointments.forEach((structured: AppointmentStructured) => (structured.appointment.end.getTime() - structured.appointment.start.getTime()) > duration ? hasLongerAppointment = true : null);
if (!hasLongerAppointment) {
response.push(appointmentsStructured[i]);
appointmentsStructured.splice(i, 1);
i = -1;
}
}
return response.sort((a: AppointmentStructured, b: AppointmentStructured): number => a.appointment.start.getTime() - b.appointment.start.getTime());
}
const findAppointmentWithSameStart = (structured: AppointmentStructured, all: AppointmentStructured[]): AppointmentStructured[] => {
var response: AppointmentStructured[] = [];
all.forEach((appointmentStructured: AppointmentStructured) => appointmentStructured.appointment.start === structured.appointment.start
&& appointmentStructured.appointment.id !== structured.appointment.id ? response.push(appointmentStructured) : null)
return response;
}
Even some pseudo code would help me a lot.
from How to show boxes properly? (Algorithm, UI)
No comments:
Post a Comment