-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Schedule Generator] Schedules Component (#904)
* add schedules and courses sections * fix indent * chore: flesh out component for LHS of schedgen modal * fixes * schedule * refactoring * calculate minutes and labels * fix lint --------- Co-authored-by: Simon Ilincev <trilogicworlds@gmail.com>
- Loading branch information
1 parent
ecea9df
commit e4d01eb
Showing
2 changed files
with
338 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,204 @@ | ||
<template> | ||
<div class="generated-schedule-schedule"> | ||
<div>hello</div> | ||
<div class="schedule-main"> | ||
<div class="schedule-body"> | ||
<div class="schedule-hours"> | ||
<div class="schedule-hour" :key="hour" v-for="hour in hoursRange">{{ hour }}</div> | ||
</div> | ||
<div class="schedule-week"> | ||
<div class="schedule-day" :key="day" v-for="day in days"> | ||
<span class="schedule-day-label"> | ||
{{ day }} | ||
</span> | ||
<div | ||
class="schedule-course" | ||
:key="cls.name" | ||
v-for="cls in classesSchedule[day]" | ||
:style="getStyle(cls.color, cls.timeStart, cls.timeEnd)" | ||
> | ||
<div class="schedule-course-info"> | ||
<span class="schedule-course-name"> | ||
{{ cls.name }} | ||
</span> | ||
<span> {{ cls.timeStart }} - {{ cls.timeEnd }} </span> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { defineComponent } from 'vue'; | ||
import { PropType, defineComponent } from 'vue'; | ||
export default defineComponent({}); | ||
type Course = { | ||
color: string; | ||
title: string; | ||
name: string; | ||
timeStart: string; | ||
timeEnd: string; | ||
}; | ||
type Time = { | ||
hours: number; | ||
minutes: number; | ||
}; | ||
type MinMaxHour = { | ||
minHour: number; | ||
maxHour: number; | ||
}; | ||
const totalPixels = 610; | ||
export default defineComponent({ | ||
props: { | ||
classesSchedule: { | ||
type: Object as PropType<{ [key: string]: Array<Course> }>, | ||
required: true, | ||
}, | ||
}, | ||
data() { | ||
return { | ||
days: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'], | ||
}; | ||
}, | ||
computed: { | ||
hoursRange(): string[] { | ||
const { minHour, maxHour } = this.getMinMaxHours(); | ||
const hoursArray: string[] = []; | ||
for (let hour = minHour; hour <= maxHour; hour += 1) { | ||
// Determine AM or PM | ||
const suffix = hour < 12 ? 'am' : 'pm'; | ||
// Convert hour to 12-hour format and create the string | ||
const formattedHour = `${hour <= 12 ? hour : hour - 12}${suffix}`; | ||
hoursArray.push(formattedHour); | ||
} | ||
return hoursArray; | ||
}, | ||
totalMinutes(): number { | ||
const total = this.getTotalMinutes(); | ||
return total; | ||
}, | ||
}, | ||
methods: { | ||
parseTimeString(time: string): Time { | ||
const parts = time.match(/(\d+):(\d+)(AM|PM)/i); | ||
if (!parts) { | ||
throw new Error('Invalid time format'); | ||
} | ||
let hours = parseInt(parts[1], 10); | ||
const minutes = parseInt(parts[2], 10); | ||
const ampm = parts[3]; | ||
if (ampm.toUpperCase() === 'PM' && hours < 12) { | ||
hours += 12; | ||
} else if (ampm.toUpperCase() === 'AM' && hours === 12) { | ||
hours = 0; | ||
} | ||
return { hours, minutes }; | ||
}, | ||
getMinMaxHours(): MinMaxHour { | ||
let minHour = 23; | ||
let maxHour = 0; | ||
this.days.forEach(day => { | ||
const classes = this.classesSchedule[day]; | ||
classes.forEach(cls => { | ||
// Extract hours from timeStart and timeEnd and convert them to numbers | ||
const startHour = this.parseTimeString(cls.timeStart).hours; | ||
const { hours: endHour, minutes: endMinutes } = this.parseTimeString(cls.timeEnd); | ||
// Update min and max hours | ||
minHour = Math.min(minHour, startHour); | ||
if (maxHour < endHour) { | ||
maxHour = endHour + (endMinutes > 0 ? 1 : 0); | ||
} | ||
}); | ||
}); | ||
return { minHour, maxHour }; | ||
}, | ||
getTotalMinutes(): number { | ||
// Initial min and max values set to the opposite extremes | ||
const { maxHour, minHour } = this.getMinMaxHours(); | ||
return (maxHour - minHour) * 60; | ||
}, | ||
getPixels(time: string): number { | ||
const { hours, minutes } = this.parseTimeString(time); | ||
const { minHour } = this.getMinMaxHours(); | ||
return ( | ||
Math.round((((hours - minHour) * 60 + minutes) / this.totalMinutes) * totalPixels) + 50 | ||
); | ||
}, | ||
getStyle(color: string, timeStart: string, timeEnd: string): Record<string, string> { | ||
return { | ||
borderColor: color, | ||
top: `${this.getPixels(timeStart).toString()}px`, | ||
height: `${(this.getPixels(timeEnd) - this.getPixels(timeStart)).toString()}px`, | ||
}; | ||
}, | ||
}, | ||
}); | ||
</script> | ||
|
||
<style scoped lang="scss"> | ||
@import '@/assets/scss/_variables.scss'; | ||
.generated-schedule { | ||
&-schedule { | ||
.schedule { | ||
&-main { | ||
border-color: $inactiveGray; | ||
border: 1px solid $inactiveGray; | ||
box-sizing: border-box; | ||
border-radius: 4px; | ||
height: 780px; | ||
padding: 2rem 3rem 2rem; | ||
padding: 2rem 0.5rem 1rem 1.5rem; | ||
} | ||
&-week { | ||
display: flex; | ||
flex-direction: row; | ||
color: $secondaryGray; | ||
} | ||
&-day { | ||
width: 6rem; | ||
position: relative; | ||
display: grid; | ||
&-label { | ||
margin-bottom: 1.8rem; | ||
justify-self: center; | ||
} | ||
border-left: 1px solid $inactiveGray; | ||
} | ||
&-hours { | ||
display: flex; | ||
flex-direction: column; | ||
color: $secondaryGray; | ||
justify-content: space-between; | ||
margin-right: 2rem; | ||
} | ||
&-hour { | ||
margin-top: 40px; | ||
} | ||
&-body { | ||
display: flex; | ||
flex-direction: row; | ||
} | ||
&-course { | ||
font-size: 12px; | ||
border-left-width: 4px; | ||
border-left-style: solid; | ||
padding-left: 8px; | ||
height: 70px; | ||
width: 85px; | ||
position: absolute; | ||
&-info { | ||
display: flex; | ||
flex-direction: column; | ||
color: black; | ||
} | ||
&-name { | ||
font-weight: 900; | ||
} | ||
} | ||
} | ||
</style> |
Oops, something went wrong.