diff --git a/components/beranda/UserJadwal.vue b/components/beranda/UserJadwal.vue index a71a87f..0e359e5 100644 --- a/components/beranda/UserJadwal.vue +++ b/components/beranda/UserJadwal.vue @@ -5,10 +5,23 @@ import { useKeycloakStore } from '@/@core/stores/keycloakStore' const keycloakStore = useKeycloakStore() const schedule = ref<any[]>([]) const loading = ref(false) +const viewMode = ref<'vertical' | 'horizontal'>('vertical') // ← track the mode const daysOfWeek = ['Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat'] const startHour = 7 const endHour = 21 +const currentTime = ref(new Date()) + +// Function to check if the current time is within a schedule item range +function isCurrentScheduleItem(item: any) { + const now = currentTime.value + const startTime = parseTime(item.start) + const endTime = parseTime(item.end) + const currentMinutes = (now.getHours() - startHour) * 60 + now.getMinutes() + + // Check if the current time is within the range (startTime < currentMinutes < endTime) + return currentMinutes >= startTime && currentMinutes <= endTime +} async function getData() { loading.value = true @@ -40,6 +53,8 @@ async function getData() { lecturer: Object.values(course.PENGAJAR).join(', '), // Join multiple lecturers if exist room: jadwal.NM_RUANG, building: jadwal.NM_GED, + tgl_mulai: jadwal.TGL_MULAI, + tgl_selesai: jadwal.TGL_SELESAI, })), ) } @@ -53,6 +68,8 @@ async function getData() { lecturer: item.NAMA_DOSEN, room: item.NM_RUANG, building: item.NM_GED, + tgl_mulai: item.TGL_MULAI, + tgl_selesai: item.TGL_SELESAI, })) } else if (keycloakStore.civitas === 'staf') { @@ -97,14 +114,60 @@ function calculateRowSpan(start: string, end: string) { return rowSpan } + +// Group schedule by day +function getScheduleByDay(day: string) { + return schedule.value.filter(item => item.day === day) +} + +// Get alternating color classes +function getColorClass(index: number) { + const colors = ['red', 'blue', 'green'] + + return `schedule-color-${colors[index % 3]}` +} </script> <template> - <VCard - title="Jadwal Kuliah" - class="timetable-card" - > - <VCard class="timetable"> + <VCard class="timetable-card"> + <template #title> + <div class="d-flex align-center"> + <!-- Left: Title --> + <span class="me-auto">Jadwal Kuliah</span> + + <!-- Right: Date Range Box --> + <div class="me-auto date-range-box"> + <span v-if="schedule.length > 0"> + {{ schedule[0].tgl_mulai }} / {{ schedule[0].tgl_selesai }} + </span> + </div> + + <!-- Right: Icons --> + <div class="d-flex gap-2"> + <VBtn + icon + variant="text" + :color="viewMode === 'vertical' ? 'primary' : 'default'" + @click="viewMode = 'vertical'" + > + <VIcon icon="ri-layout-vertical-line" /> + </VBtn> + <VBtn + icon + variant="text" + :color="viewMode === 'horizontal' ? 'primary' : 'default'" + @click="viewMode = 'horizontal'" + > + <VIcon icon="ri-layout-horizontal-line" /> + </VBtn> + </div> + </div> + </template> + + <VCard + v-if="viewMode === 'vertical'" + class="timetable" + > <!-- Header Row --> <div class="grid-header"> <div class="time-label"> @@ -173,10 +236,86 @@ function calculateRowSpan(start: string, end: string) { </div> </div> </VCard> + <VCard + v-else + class="timetable px-4 py-6" + > + <table class="w-100 text-left table-schedule fixed-table"> + <thead> + <tr> + <th + v-for="day in daysOfWeek" + :key="day" + class="px-2 py-2" + > + {{ day }} + </th> + </tr> + </thead> + <tbody> + <tr + v-for="rowIndex in 10" + :key="`row-${rowIndex}`" + > + <td + v-for="(day, dayIndex) in daysOfWeek" + :key="day + rowIndex" + class="align-top px-2 py-3" + > + <div + v-if="getScheduleByDay(day)[rowIndex - 1]" + class="schedule-box" + :class="[getColorClass(rowIndex - 1)]" + > + <div class="course-header mb-1"> + <span class="text-sm font-medium d-flex align-center gap-1"> + <i class="ri-time-line" /> {{ + getScheduleByDay(day)[rowIndex - 1].start + }} - {{ + getScheduleByDay(day)[rowIndex - 1].end + }} + </span> + <span class="text-xs">{{ getScheduleByDay(day)[rowIndex - 1].room }}</span> + </div> + <div class="course-title font-semibold text-sm mb-1"> + <span + v-if="getScheduleByDay(day)[rowIndex - 1].course.includes('-')" + style="text-decoration: underline;" + > + {{ getScheduleByDay(day)[rowIndex - 1].course.split(' - ')[0] }} + </span> + <span class="text-sm"> + {{ + getScheduleByDay(day)[rowIndex - 1].course.includes('-') + ? getScheduleByDay(day)[rowIndex - 1].course.split(' - ')[1] + : getScheduleByDay(day)[rowIndex - 1].course + }} + </span> + </div> + <div class="text-xs"> + {{ getScheduleByDay(day)[rowIndex - 1].building }} + </div> + </div> + </td> + </tr> + </tbody> + </table> + </VCard> </VCard> </template> <style scoped> +.date-range-box { + display: inline-block; + border-radius: 5px; + background-color: rgba(var(--v-global-theme-primary), 0.1); + font-size: 14px; + font-weight: bold; + margin-inline-start: 10px; + padding-block: 5px; + padding-inline: 10px; +} + .timetable { display: flex; flex-direction: column; @@ -219,6 +358,7 @@ function calculateRowSpan(start: string, end: string) { .course-prefix { color: color-mix(in srgb, rgba(var(--v-global-theme-primary)) 70%, black 30%); + text-decoration: underline; } .grid-body { @@ -248,13 +388,14 @@ function calculateRowSpan(start: string, end: string) { .schedule-item { display: flex; + overflow: hidden; flex-direction: column; align-items: flex-start; /* Keeps text left-aligned */ justify-content: flex-start; /* Aligns content to the top */ - padding: 4px; + padding: 6px; border-radius: 6px; margin: 2px; - background-color: color-mix(in srgb, rgba(var(--v-global-theme-primary)) 70%, white 30%); + background-color: color-mix(in srgb, rgba(var(--v-global-theme-primary)) 80%, white 20%); color: white; font-size: 12px; outline: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); @@ -297,6 +438,46 @@ function calculateRowSpan(start: string, end: string) { text-align: start; /* Aligns room to the right */ } +.fixed-table { + border-collapse: collapse; + inline-size: 100%; + table-layout: fixed; +} + +.fixed-table thead th { + background-color: rgba(var(--v-global-theme-primary)); + color: white; + font-weight: bold; + text-align: center; +} + +.fixed-table th, +.fixed-table td { + padding: 0; + margin: 0; +} + +.schedule-box { + padding: 6px; + border-radius: 8px; + color: white; + font-size: 0.85rem; + min-block-size: 90px; +} + +/* Color themes for items */ +.schedule-color-red { + background-color: #6aa1fa; +} + +.schedule-color-blue { + background-color: #46c298; +} + +.schedule-color-green { + background-color: #eda36b; +} + @media screen and (max-width: 1200px) { .course-title { border-block-end: 0 solid;