|
|
@@ -1,9 +1,195 @@
|
|
|
<template>
|
|
|
<div>
|
|
|
<DefaultHeaderPage title="Agenda" />
|
|
|
+
|
|
|
+ <div class="q-px-md q-pb-md">
|
|
|
+ <!-- Toolbar: Hoje | ‹ data › centralizada | + Nova Atividade -->
|
|
|
+ <div class="class-toolbar q-mb-md">
|
|
|
+ <!-- Esquerda -->
|
|
|
+ <div class="row items-center no-wrap" style="gap: 8px">
|
|
|
+ <q-btn
|
|
|
+ outline
|
|
|
+ color="primary"
|
|
|
+ label="Hoje"
|
|
|
+ no-caps
|
|
|
+ dense
|
|
|
+ class="q-px-md"
|
|
|
+ @click="goToday"
|
|
|
+ />
|
|
|
+ <q-btn
|
|
|
+ v-if="view === 'day'"
|
|
|
+ flat
|
|
|
+ no-caps
|
|
|
+ dense
|
|
|
+ color="primary"
|
|
|
+ icon="mdi-calendar-month-outline"
|
|
|
+ label="Voltar ao calendário"
|
|
|
+ class="q-px-sm"
|
|
|
+ @click="backToMonth"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Centro: ‹ Junho de 2026 › -->
|
|
|
+ <div class="row items-center no-wrap justify-center" style="gap: 4px">
|
|
|
+ <q-btn flat round dense icon="mdi-chevron-left" color="primary" @click="goPrev" />
|
|
|
+ <span class="text-h6 text-primary text-weight-medium" style="min-width: 220px; text-align: center">
|
|
|
+ {{ headerLabel }}
|
|
|
+ </span>
|
|
|
+ <q-btn flat round dense icon="mdi-chevron-right" color="primary" @click="goNext" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Direita -->
|
|
|
+ <div class="row items-center no-wrap justify-end">
|
|
|
+ <q-btn
|
|
|
+ color="primary"
|
|
|
+ icon="mdi-plus"
|
|
|
+ label="Nova Atividade"
|
|
|
+ no-caps
|
|
|
+ unelevated
|
|
|
+ @click="onAddClick"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="loading" class="flex flex-center q-pa-xl">
|
|
|
+ <q-spinner color="primary" size="48px" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <template v-else>
|
|
|
+ <MonthGrid
|
|
|
+ v-if="view === 'month'"
|
|
|
+ :current-date="currentDate"
|
|
|
+ :classes="classes"
|
|
|
+ @day-click="openDay"
|
|
|
+ @class-click="onClassClick"
|
|
|
+ />
|
|
|
+
|
|
|
+ <DayTimeline
|
|
|
+ v-else
|
|
|
+ :date="currentDate"
|
|
|
+ :classes="classes"
|
|
|
+ @class-click="onClassClick"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
+import { ref, computed, onMounted, watch, defineAsyncComponent } from "vue";
|
|
|
+import { useQuasar } from "quasar";
|
|
|
+import {
|
|
|
+ addMonths,
|
|
|
+ subMonths,
|
|
|
+ addDays,
|
|
|
+ subDays,
|
|
|
+ format,
|
|
|
+} from "date-fns";
|
|
|
+import { ptBR } from "date-fns/locale";
|
|
|
+
|
|
|
import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
|
|
|
+import MonthGrid from "./components/MonthGrid.vue";
|
|
|
+import DayTimeline from "./components/DayTimeline.vue";
|
|
|
+import { getClasses } from "src/api/class";
|
|
|
+import { userStore } from "src/stores/user";
|
|
|
+
|
|
|
+const ClassAttendanceDialog = defineAsyncComponent(
|
|
|
+ () => import("./components/ClassAttendanceDialog.vue"),
|
|
|
+);
|
|
|
+const AddEditClassDialog = defineAsyncComponent(
|
|
|
+ () => import("./components/AddEditClassDialog.vue"),
|
|
|
+);
|
|
|
+
|
|
|
+const $q = useQuasar();
|
|
|
+const { selectedUnit } = userStore();
|
|
|
+
|
|
|
+const view = ref("month"); // 'month' | 'day'
|
|
|
+const currentDate = ref(new Date());
|
|
|
+const classes = ref([]);
|
|
|
+const loading = ref(false);
|
|
|
+
|
|
|
+const headerLabel = computed(() => {
|
|
|
+ if (view.value === "month") {
|
|
|
+ const label = format(currentDate.value, "MMMM 'de' yyyy", { locale: ptBR });
|
|
|
+ return label.charAt(0).toUpperCase() + label.slice(1);
|
|
|
+ }
|
|
|
+ const label = format(currentDate.value, "EEEE, d 'de' MMMM", { locale: ptBR });
|
|
|
+ return label.charAt(0).toUpperCase() + label.slice(1);
|
|
|
+});
|
|
|
+
|
|
|
+const loadClasses = async () => {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ classes.value = await getClasses();
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const goPrev = () => {
|
|
|
+ currentDate.value =
|
|
|
+ view.value === "month"
|
|
|
+ ? subMonths(currentDate.value, 1)
|
|
|
+ : subDays(currentDate.value, 1);
|
|
|
+};
|
|
|
+
|
|
|
+const goNext = () => {
|
|
|
+ currentDate.value =
|
|
|
+ view.value === "month"
|
|
|
+ ? addMonths(currentDate.value, 1)
|
|
|
+ : addDays(currentDate.value, 1);
|
|
|
+};
|
|
|
+
|
|
|
+const goToday = () => {
|
|
|
+ currentDate.value = new Date();
|
|
|
+};
|
|
|
+
|
|
|
+const openDay = (date) => {
|
|
|
+ currentDate.value = date;
|
|
|
+ view.value = "day";
|
|
|
+};
|
|
|
+
|
|
|
+const backToMonth = () => {
|
|
|
+ view.value = "month";
|
|
|
+};
|
|
|
+
|
|
|
+// Clicar numa aula abre o dialog de registrar presença
|
|
|
+const onClassClick = (item) => {
|
|
|
+ $q.dialog({
|
|
|
+ component: ClassAttendanceDialog,
|
|
|
+ componentProps: { classData: item },
|
|
|
+ }).onOk(() => loadClasses());
|
|
|
+};
|
|
|
+
|
|
|
+// Adicionar nova atividade (opcionalmente já com a data do dia clicado)
|
|
|
+const openCreate = (date = null) => {
|
|
|
+ $q.dialog({
|
|
|
+ component: AddEditClassDialog,
|
|
|
+ componentProps: {
|
|
|
+ initialDate: date ? format(date, "yyyy-MM-dd") : null,
|
|
|
+ },
|
|
|
+ }).onOk(() => loadClasses());
|
|
|
+};
|
|
|
+
|
|
|
+// Na visão de dia já pré-seleciona a data atual
|
|
|
+const onAddClick = () => openCreate(view.value === "day" ? currentDate.value : null);
|
|
|
+
|
|
|
+// Recarrega quando o usuário troca de unidade no seletor do topo
|
|
|
+watch(
|
|
|
+ () => selectedUnit?.id,
|
|
|
+ (newId, oldId) => {
|
|
|
+ if (newId !== oldId) loadClasses();
|
|
|
+ },
|
|
|
+);
|
|
|
+
|
|
|
+onMounted(loadClasses);
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.class-toolbar {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 1fr auto 1fr;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+</style>
|