Skip to content

Commit

Permalink
added mobile view for schedule tables
Browse files Browse the repository at this point in the history
  • Loading branch information
smeggmann99 committed Nov 2, 2024
1 parent 93c452b commit e5a1bfa
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 80 deletions.
280 changes: 208 additions & 72 deletions web/optivum-better-schedule-frontend/src/components/ScheduleTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,62 +10,121 @@
</div>
</v-slide-y-transition>
<v-fade-transition appear>
<v-card class="schedule-container grid-item pa-0" variant="outlined" elevation="8">
<v-table v-if="scheduleData && !notFound" class="schedule-table">
<thead>
<tr>
<th><span class="schedule-head">{{ t('schedule.ordered_number') }}</span></th>
<th><span class="schedule-head">{{ t('schedule.time_range') }}</span></th>
<th v-for="(day, index) in availableDayNames" :key="index">
<span class="schedule-head">{{ day }}</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(timeRange, rowIndex) in uniqueTimeRanges" :key="rowIndex"
:style="{ backgroundColor: getRowColor(rowIndex) }">
<td :style="{ backgroundColor: getRowColor(1) }"><span class="schedule-no">{{
rowIndex + 1 }}</span></td>
<td><span class="schedule-time">{{ timeRange }}</span></td>
<td v-for="(day, dayIndex) in scheduleData?.schedule.schedule_days" :key="dayIndex">
<div
v-if="day.lesson_groups.some((lg: LessonGroup) => formatTime(lg.lessons[0]?.time_range.start) + ' - ' + formatTime(lg.lessons[0]?.time_range.end) === timeRange)">
<div v-for="lesson in day.lesson_groups.find((lg: LessonGroup) => formatTime(lg.lessons[0]?.time_range.start) + ' - ' + formatTime(lg.lessons[0]?.time_range.end) === timeRange)?.lessons"
:key="lesson.full_name">
<span class="schedule-lesson-name">{{ lesson.full_name }}</span>&nbsp;
<router-link
v-if="lesson.teacher_designator && teacherIndexes[lesson.teacher_designator] !== undefined"
:to="'/teacher/' + teacherIndexes[lesson.teacher_designator]"
class="schedule-lesson-teacher">
&nbsp;{{ lesson.teacher_designator }}
</router-link>
<router-link
v-if="lesson.room_designator && roomIndexes[lesson.room_designator] !== undefined"
:to="'/room/' + roomIndexes[lesson.room_designator]"
class="schedule-lesson-room">
&nbsp;{{ lesson.room_designator }}
</router-link>
<router-link
v-if="lesson.division_designator && divisionIndexes[lesson.division_designator] !== undefined"
:to="'/division/' + divisionIndexes[lesson.division_designator]"
class="schedule-lesson-division">
&nbsp;{{ lesson.division_designator }}
</router-link>
<div class="schedule-container grid-item pa-0">
<!-- Mobile View -->
<template v-if="isMobileView">
<v-card variant="flat">
<v-tabs v-model="activeTab" :style="{ backgroundColor: getRowColor(1) }" grow center-active>
<v-tab v-for="(day, index) in availableDayNames" :key="index" class="schedule-day-tab">
{{ day }}
</v-tab>
</v-tabs>
</v-card>
<v-tabs-items v-model="activeTab">
<v-tab-item v-for="(day, dayIndex) in scheduleData?.schedule.schedule_days" :key="dayIndex">
<v-table class="schedule-table" v-if="activeTab === dayIndex">
<tbody>
<tr v-for="(lessonGroup, groupIndex) in day.lesson_groups" :key="groupIndex"
:style="{ backgroundColor: getRowColor(groupIndex) }">
<td class="narrower-column"><span class="schedule-no">{{ groupIndex + 1
}}.</span></td>
<td class="narrow-column">
<div class="stacked-time">
<span class="schedule-time">{{
formatTime(lessonGroup.lessons[0]?.time_range.start) }}</span>
<span class="schedule-time">{{
formatTime(lessonGroup.lessons[0]?.time_range.end) }}</span>
</div>
</td>
<td class="schedule-table-data">
<div v-for="lesson in lessonGroup.lessons" :key="lesson.full_name"
class="stacked-lesson">
<span class="schedule-lesson-name">{{ lesson.full_name }}</span>&nbsp;
<router-link
v-if="lesson.teacher_designator && teacherIndexes[lesson.teacher_designator] !== undefined"
:to="'/teacher/' + teacherIndexes[lesson.teacher_designator]"
class="schedule-lesson-teacher">
&nbsp;{{ lesson.teacher_designator }}
</router-link>
<router-link
v-if="lesson.room_designator && roomIndexes[lesson.room_designator] !== undefined"
:to="'/room/' + roomIndexes[lesson.room_designator]"
class="schedule-lesson-room">
&nbsp;{{ lesson.room_designator }}
</router-link>
<router-link
v-if="lesson.division_designator && divisionIndexes[lesson.division_designator] !== undefined"
:to="'/division/' + divisionIndexes[lesson.division_designator]"
class="schedule-lesson-division">
&nbsp;{{ lesson.division_designator }}
</router-link>
</div>
</td>
</tr>
</tbody>
</v-table>
</v-tab-item>
</v-tabs-items>
</template>
<!-- Desktop View -->
<template v-else-if="scheduleData && !notFound">
<v-table class="schedule-table">
<thead>
<tr>
<th><span class="schedule-head">{{ t('schedule.ordered_number') }}</span></th>
<th><span class="schedule-head">{{ t('schedule.time_range') }}</span></th>
<th v-for="(day, index) in availableDayNames" :key="index">
<span class="schedule-head">{{ day }}</span>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(timeRange, rowIndex) in uniqueTimeRanges" :key="rowIndex"
:style="{ backgroundColor: getRowColor(rowIndex) }">
<td class="schedule-table-data"><span class="schedule-no">{{ rowIndex + 1 }}</span></td>
<td class="schedule-table-data">
<span class="schedule-time">{{ timeRange.split(" - ")[0] }} - {{ timeRange.split(" - ")[1] }}</span>
</td>
<td v-for="(day, dayIndex) in scheduleData?.schedule.schedule_days" :key="dayIndex"
class="schedule-table-data">
<div
v-if="day.lesson_groups.some((lg: LessonGroup) => formatTime(lg.lessons[0]?.time_range.start) + ' - ' + formatTime(lg.lessons[0]?.time_range.end) === timeRange)">
<div v-for="lesson in day.lesson_groups.find((lg: LessonGroup) => formatTime(lg.lessons[0]?.time_range.start) + ' - ' + formatTime(lg.lessons[0]?.time_range.end) === timeRange)?.lessons"
:key="lesson.full_name" class="stacked-lesson">
<span class="schedule-lesson-name">{{ lesson.full_name }}</span>&nbsp;
<router-link
v-if="lesson.teacher_designator && teacherIndexes[lesson.teacher_designator] !== undefined"
:to="'/teacher/' + teacherIndexes[lesson.teacher_designator]"
class="schedule-lesson-teacher">
&nbsp;{{ lesson.teacher_designator }}
</router-link>
<router-link
v-if="lesson.room_designator && roomIndexes[lesson.room_designator] !== undefined"
:to="'/room/' + roomIndexes[lesson.room_designator]"
class="schedule-lesson-room">
&nbsp;{{ lesson.room_designator }}
</router-link>
</div>
</div>
</div>
<div v-else>&nbsp; <!-- Placeholder for empty cells --></div>
</td>
</tr>
</tbody>
</v-table>
</v-card>
<div v-else>&nbsp; <!-- Placeholder for empty cells --></div>
</td>
</tr>
</tbody>
</v-table>
</template>
</div>
</v-fade-transition>
</v-row>
</template>
<script setup lang="ts">
import { ref, onMounted, computed, defineProps } from 'vue';
import axios from 'axios';
Expand Down Expand Up @@ -112,12 +171,20 @@ interface DivisionData {
const { t } = useI18n();
const theme = useTheme();
const mobileViewBreakpoint = 1279;
const scheduleData = ref<DivisionData | null>(null);
const title = computed(() => scheduleData.value?.full_name ?? '');
const teacherIndexes = ref<Record<string, number>>({});
const roomIndexes = ref<Record<string, number>>({});
const divisionIndexes = ref<Record<string, number>>({});
const notFound = ref(false);
const isMobileView = ref(window.innerWidth < mobileViewBreakpoint);
const activeTab = ref(0);
window.addEventListener("resize", () => {
isMobileView.value = window.innerWidth < mobileViewBreakpoint;
});
const getRowColor = (rowIndex: number) => {
const colors = theme.current.value.colors;
Expand Down Expand Up @@ -218,25 +285,25 @@ function formatTime(time: TimeRange | undefined): string {
}
.schedule-container {
width: 100%;
overflow-x: auto;
overflow-x: visible;
}
.schedule-table {
width: 100%;
height: auto;
border-collapse: collapse;
table-layout: fixed;
font-size: 1vw;
}
.schedule-table th,
.schedule-table td {
padding: 0.5vw;
text-align: center;
text-align: left;
overflow-wrap: break-word;
word-break: break-word;
font-size: 1vw;
border: 1px solid rgba(255, 255, 255, 0.15);
white-space: normal;
}
.schedule-title-container {
Expand All @@ -253,7 +320,7 @@ function formatTime(time: TimeRange | undefined): string {
}
.schedule-title {
font-size: 2rem;
font-size: 3.5rem;
font-weight: 800;
text-align: center;
display: flex;
Expand All @@ -268,7 +335,7 @@ function formatTime(time: TimeRange | undefined): string {
.schedule-time,
.schedule-head {
font-size: 1.2rem;
font-weight: 700;
font-weight: 800;
text-align: center;
white-space: nowrap;
display: flex;
Expand All @@ -280,42 +347,111 @@ function formatTime(time: TimeRange | undefined): string {
font-weight: 600;
}
.lesson-info {
display: inline-flex;
flex-wrap: nowrap;
align-items: center;
}
.schedule-lesson-name,
.schedule-lesson-teacher,
.schedule-lesson-room,
.schedule-lesson-division,
.schedule-lesson-name {
font-size: 1rem;
white-space: nowrap;
display: inline;
.schedule-lesson-division {
font-size: 1rem;
display: inline;
white-space: nowrap;
}
.stacked-lesson {
display: block;
padding-bottom: 4px;
}
schedule-table td.schedule-table-data {
max-width: 15vw;
}
@media (max-width: 1279px) {
.division-grid {
margin-top: calc(64px + 16px);;
margin-top: calc(64px + 16px);
}
.schedule-container {
width: 100vw;
height: 100%;
}
.schedule-table th.narrow-column,
.schedule-table td.narrow-column {
width: 15%;
padding: 6px;
}
.schedule-table th.narrower-column,
.schedule-table td.narrower-column {
width: 5%;
padding: 6px;
}
.stacked-time {
display: flex;
flex-direction: column;
text-align: center;
}
.schedule-table-data {
padding: 0px !important;
}
.schedule-title {
font-size: 3rem;
}
.schedule-table {
font-size: 2vw;
}
font-size: 2vw;
}
.schedule-table th,
.schedule-table td {
padding: 0.8vw;
font-size: 2vw;
text-align: left;
border: none;
}
.schedule-title {
font-size: 4vw;
}
.schedule-table-data {
padding: 0px !important;
}
.schedule-no,
.schedule-time,
.schedule-head {
font-size: 1rem;
font-weight: 600;
text-align: left;
white-space: nowrap;
display: flex;
justify-content: flex-start;
align-items: center;
}
.schedule-no {
width: 2em;
padding-left: 0.2em;
}
.schedule-lesson-teacher,
.schedule-lesson-room,
.schedule-lesson-division,
.schedule-lesson-name {
font-size: 1rem;
font-weight: 400;
white-space: nowrap;
display: inline;
text-align: left;
}
.schedule-day-tab {
font-size: 1rem;
font-weight: 800;
}
}
</style>
Loading

0 comments on commit e5a1bfa

Please sign in to comment.