sposchedule
315 строк · 8.5 Кб
1<script setup lang="ts">
2import DatePicker from 'primevue/datepicker';
3import ChangesScheduleItem from '@/components/schedule/AdminChangesScheduleItem.vue';
4import { computed, onMounted, ref, watch } from 'vue';
5import {
6useChangesSchedulesQuery,
7useCoursesQuery,
8} from '@/queries/schedules';
9import { useDateFormat } from '@vueuse/core';
10import { useScheduleStore } from '@/stores/schedule';
11import { storeToRefs } from 'pinia';
12import router from '@/router';
13import { useRoute } from 'vue-router';
14import Select from 'primevue/select';
15import Button from 'primevue/button';
16import { useBuildingsQuery } from '@/queries/buildings';
17import { useGroupsPublicQuery } from '@/queries/groups';
18import { reducedWeekDays, dateRegex } from '@/composables/constants';
19import BlockUI from 'primevue/blockui';
20
21const route = useRoute();
22const scheduleStore = useScheduleStore();
23const { course, date, schedulesChanges } = storeToRefs(scheduleStore);
24const { setSchedulesChanges } = scheduleStore;
25
26const isoDate = computed(() => {
27return date.value ? useDateFormat(date.value, 'DD.MM.YYYY').value : null;
28});
29
30const selectedGroup = ref();
31const building = ref(null);
32
33const { data: courses } = useCoursesQuery(building);
34
35const coursesWithLabel = computed(() => {
36return (
37courses.value?.map(course => ({
38label: `${course.course} курс`,
39value: course.course,
40})) || []
41);
42});
43
44const selectedCourse = computed(() => {
45return course.value;
46});
47
48const updateQueryParams = () => {
49router.replace({
50query: {
51...route.query,
52date: isoDate.value || undefined,
53building: building.value || undefined,
54course: selectedCourse.value || undefined,
55group: selectedGroup.value || undefined,
56},
57});
58};
59
60const {
61data: changesSchedules,
62isError,
63isFetching,
64isSuccess,
65} = useChangesSchedulesQuery(
66isoDate,
67building,
68selectedCourse,
69selectedGroup
70);
71
72watch(
73changesSchedules,
74newData => {
75if (newData) {
76setSchedulesChanges(newData);
77}
78},
79{ deep: true }
80);
81
82watch(
83[isoDate, building, selectedCourse, selectedGroup],
84() => {
85updateQueryParams();
86},
87{ deep: true }
88);
89
90watch(
91building,
92() => {
93course.value = null;
94selectedGroup.value = null;
95},
96{ flush: 'sync' }
97);
98
99watch(
100course,
101() => {
102selectedGroup.value = null;
103},
104{ flush: 'sync' }
105);
106
107onMounted(() => {
108if (route.query.date && dateRegex.test(route.query.date as string)) {
109// Если дата есть в query параметрах, используем ее
110const [day, month, year] = (route.query.date as string)
111.split('.')
112.map(Number);
113date.value = new Date(year, month - 1, day);
114} else {
115// Если нет даты ни в query, ни в localStorage, используем текущую дату
116date.value = new Date();
117}
118if (route.query.building) {
119building.value = route.query.building as string;
120}
121if (route.query.course) {
122// Если курс есть в query параметрах, используем его
123course.value = Number(route.query.course as string);
124}
125
126if (route.query.group) {
127selectedGroup.value = route.query.group as string;
128}
129
130updateQueryParams();
131});
132
133const { data: groups } = useGroupsPublicQuery(
134selectedGroup,
135building,
136course
137);
138
139const { data: buildingsFethed } = useBuildingsQuery();
140const buildings = computed(() => {
141return (
142buildingsFethed.value?.map(building => ({
143value: building.name,
144label: `${building.name} корпус`,
145})) || []
146);
147});
148
149function handleDatePickerBtns(day) {
150switch (day) {
151case 'today':
152date.value = new Date();
153break;
154
155case 'tomorrow':
156const tomorrow = new Date();
157tomorrow.setDate(tomorrow.getDate() + 1);
158date.value = tomorrow;
159break;
160}
161}
162</script>
163
164<template>
165<div class="flex flex-col gap-4">
166<div class="flex flex-wrap justify-between items-baseline">
167<h1 class="text-2xl">Расписание (изменения)</h1>
168</div>
169<div
170class="flex items-center justify-between gap-4 p-4 rounded-lg bg-surface-100 dark:bg-surface-800"
171>
172<div class="flex gap-2 items-center flex-wrap w-full">
173<DatePicker
174v-model="date"
175append-to="self"
176class="shrink-0"
177show-icon
178icon-display="input"
179:invalid="isError"
180date-format="dd.mm.yy"
181>
182<template #inputicon="slotProps">
183<div
184class="flex gap-2 justify-between items-center"
185@click="slotProps.clickCallback"
186>
187<small>{{
188reducedWeekDays[
189useDateFormat(date, 'dddd', {
190locales: 'ru-RU',
191}).value
192]
193}}</small>
194<small>{{ schedulesChanges?.week_type }}</small>
195</div>
196</template>
197<template #footer="slotProps">
198<div class="flex justify-between pt-1">
199<Button
200severity="secondary"
201size="small"
202label="Сегодня"
203@click="handleDatePickerBtns('today')"
204/>
205<Button
206severity="secondary"
207size="small"
208label="Завтра"
209@click="handleDatePickerBtns('tomorrow')"
210/>
211</div>
212</template>
213</DatePicker>
214<Select
215v-model="building"
216title="Корпус"
217show-clear
218:options="buildings"
219option-label="label"
220option-value="value"
221placeholder="Корпус"
222/>
223<Select
224v-model="course"
225show-clear
226:options="coursesWithLabel"
227option-label="label"
228option-value="value"
229placeholder="Курс"
230/>
231<Select
232v-model="selectedGroup"
233empty-filter-message="Группы не найдены"
234filter
235show-clear
236option-value="name"
237:options="groups"
238option-label="name"
239placeholder="Группа"
240/>
241<Button
242target="_blank"
243icon="pi pi-print"
244as="router-link"
245:to="{
246path: '/print/changes',
247query: {
248date: isoDate,
249},
250}"
251/>
252<div class="ml-auto self-center">
253<div
254v-if="schedulesChanges?.last_updated"
255class="flex gap-1 flex-row items-center lg:flex-col lg:gap-0 lg:items-end flex-wrap"
256>
257<span class="text-xs text-surface-400 leading-none"
258>Последние обновление:</span
259>
260<time
261title="Последние обновление"
262class="text-sm text-right text-surface-400"
263:datetime="schedulesChanges?.last_updated"
264>{{
265useDateFormat(
266schedulesChanges?.last_updated,
267'DD.MM.YYYY HH:mm:ss'
268)
269}}</time
270>
271</div>
272</div>
273</div>
274</div>
275
276<span v-if="isError"
277>Семестра на данную дату не найдено, чтобы добавить перейдите на экран
278добавления
279<RouterLink class="underline" to="/admin/semesters">семестра</RouterLink>
280</span>
281<div v-if="isSuccess">
282<BlockUI class="schedules" :blocked="isFetching">
283<ChangesScheduleItem
284v-for="(item, index) in schedulesChanges?.schedules"
285:key="index"
286class="schedule"
287:date="isoDate"
288:schedule="item?.schedule"
289:semester="item?.semester"
290:type="item?.schedule?.type"
291:group="item?.group"
292:lessons="item?.schedule?.lessons"
293:week-type="item?.week_type"
294:published="item?.schedule?.published"
295/>
296</BlockUI>
297</div>
298</div>
299</template>
300
301<style scoped>
302.schedules {
303display: flex;
304/* flex-direction: column; */
305flex-wrap: wrap;
306row-gap: 2rem;
307column-gap: 10px;
308justify-content: space-between;
309}
310
311.schedule {
312min-width: 440px;
313flex: 0 1 calc(25% - 10px);
314}
315</style>
316