|
|
@@ -1,164 +1,201 @@
|
|
|
<template>
|
|
|
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
|
|
<q-card class="q-dialog-plugin feriados-dialog">
|
|
|
- <DefaultDialogHeader title="Feriados" @close="onDialogCancel" />
|
|
|
-
|
|
|
- <q-card-section class="row q-col-gutter-md q-pt-none">
|
|
|
- <!-- Calendário -->
|
|
|
- <div class="col-12 col-sm-7">
|
|
|
- <!-- Navegação mês/ano -->
|
|
|
- <div class="row items-center justify-between q-mb-sm">
|
|
|
- <q-btn
|
|
|
- flat
|
|
|
- round
|
|
|
- dense
|
|
|
- icon="mdi-chevron-left"
|
|
|
- color="grey-7"
|
|
|
- @click="prevMonth"
|
|
|
- />
|
|
|
- <div class="row q-gutter-sm items-center">
|
|
|
- <q-select
|
|
|
- v-model="currentMonth"
|
|
|
- :options="monthOptions"
|
|
|
- emit-value
|
|
|
- map-options
|
|
|
+ <template v-if="view === 'calendar'">
|
|
|
+ <DefaultDialogHeader title="Feriados" @close="onClose" />
|
|
|
+
|
|
|
+ <q-card-section class="row q-col-gutter-md q-pt-none">
|
|
|
+ <div class="col-12 col-sm-7">
|
|
|
+ <div class="row items-center justify-between q-mb-md">
|
|
|
+ <q-btn
|
|
|
+ flat
|
|
|
+ round
|
|
|
dense
|
|
|
- outlined
|
|
|
- style="min-width: 100px"
|
|
|
+ icon="mdi-chevron-left"
|
|
|
+ color="grey-7"
|
|
|
+ @click="prevMonth"
|
|
|
/>
|
|
|
- <q-select
|
|
|
- v-model="currentYear"
|
|
|
- :options="yearOptions"
|
|
|
- emit-value
|
|
|
- map-options
|
|
|
+ <div class="row items-center q-gutter-xs">
|
|
|
+ <q-select
|
|
|
+ v-model="currentMonth"
|
|
|
+ :options="monthOptions"
|
|
|
+ emit-value
|
|
|
+ map-options
|
|
|
+ dense
|
|
|
+ outlined
|
|
|
+ style="min-width: 100px"
|
|
|
+ />
|
|
|
+ <q-select
|
|
|
+ v-model="currentYear"
|
|
|
+ :options="yearOptions"
|
|
|
+ emit-value
|
|
|
+ map-options
|
|
|
+ dense
|
|
|
+ outlined
|
|
|
+ style="min-width: 80px"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <q-btn
|
|
|
+ flat
|
|
|
+ round
|
|
|
dense
|
|
|
- outlined
|
|
|
- style="min-width: 80px"
|
|
|
+ icon="mdi-chevron-right"
|
|
|
+ color="grey-7"
|
|
|
+ @click="nextMonth"
|
|
|
/>
|
|
|
</div>
|
|
|
- <q-btn
|
|
|
- flat
|
|
|
- round
|
|
|
- dense
|
|
|
- icon="mdi-chevron-right"
|
|
|
- color="grey-7"
|
|
|
- @click="nextMonth"
|
|
|
- />
|
|
|
- </div>
|
|
|
|
|
|
- <!-- Cabeçalho dias da semana -->
|
|
|
- <div class="calendar-grid q-mb-xs">
|
|
|
- <div
|
|
|
- v-for="day in weekDays"
|
|
|
- :key="day"
|
|
|
- class="calendar-header-cell text-caption text-grey-6 text-center"
|
|
|
- >
|
|
|
- {{ day }}
|
|
|
+ <div class="calendar-grid q-mb-xs">
|
|
|
+ <div
|
|
|
+ v-for="day in weekDays"
|
|
|
+ :key="day"
|
|
|
+ class="cal-header text-caption text-grey-6 text-center text-weight-medium"
|
|
|
+ >
|
|
|
+ {{ day }}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- Dias do mês -->
|
|
|
- <div class="calendar-grid">
|
|
|
- <div
|
|
|
- v-for="(cell, index) in calendarCells"
|
|
|
- :key="index"
|
|
|
- class="calendar-cell"
|
|
|
- :class="{
|
|
|
- 'calendar-cell--empty': !cell.day,
|
|
|
- 'calendar-cell--holiday': cell.isHoliday,
|
|
|
- 'calendar-cell--selected': cell.day === selectedDay && cell.day !== null,
|
|
|
- }"
|
|
|
- @click="cell.day && onDayClick(cell)"
|
|
|
- >
|
|
|
- <span v-if="cell.day">{{ cell.day }}</span>
|
|
|
+ <div class="calendar-grid">
|
|
|
+ <div
|
|
|
+ v-for="(cell, index) in calendarCells"
|
|
|
+ :key="index"
|
|
|
+ class="cal-cell"
|
|
|
+ :class="{
|
|
|
+ 'cal-cell--empty': !cell.day,
|
|
|
+ 'cal-cell--holiday': cell.isHoliday,
|
|
|
+ }"
|
|
|
+ @click="cell.day && !cell.isHoliday && openNewRecord(cell.day)"
|
|
|
+ >
|
|
|
+ <span v-if="cell.day" class="cal-cell__number">{{
|
|
|
+ cell.day
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- Input inline para adicionar feriado -->
|
|
|
- <div v-if="selectedDay !== null && !selectedDayHasHoliday" class="q-mt-md">
|
|
|
- <div class="text-caption text-grey-6 q-mb-xs">
|
|
|
- Descrição para {{ selectedDay }}/{{ String(currentMonth).padStart(2, '0') }}/{{ currentYear }}
|
|
|
+ <div class="col-12 col-sm-5">
|
|
|
+ <div class="text-subtitle2 q-mb-sm">Resumo</div>
|
|
|
+ <q-separator class="q-mb-sm" />
|
|
|
+
|
|
|
+ <div v-if="loadingHolidays" class="flex flex-center q-py-lg">
|
|
|
+ <q-spinner color="primary" size="24px" />
|
|
|
</div>
|
|
|
- <div class="row q-gutter-sm">
|
|
|
- <q-input
|
|
|
- v-model="newDescription"
|
|
|
- dense
|
|
|
- outlined
|
|
|
- placeholder="Ex: Independência do Brasil"
|
|
|
- class="col"
|
|
|
- autofocus
|
|
|
- @keyup.enter="addHoliday"
|
|
|
- />
|
|
|
- <q-btn
|
|
|
- unelevated
|
|
|
- color="primary"
|
|
|
- label="Adicionar"
|
|
|
- no-caps
|
|
|
- :disable="!newDescription.trim()"
|
|
|
- @click="addHoliday"
|
|
|
- />
|
|
|
+
|
|
|
+ <div
|
|
|
+ v-else-if="monthHolidays.length === 0"
|
|
|
+ class="text-caption text-grey-5 text-center q-mt-lg q-px-sm"
|
|
|
+ >
|
|
|
+ Clique em um dia no calendário para adicionar um registro.
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- Resumo -->
|
|
|
- <div class="col-12 col-sm-5">
|
|
|
- <div class="text-subtitle2 q-mb-sm">Resumo</div>
|
|
|
- <q-separator class="q-mb-sm" />
|
|
|
+ <q-list v-else separator>
|
|
|
+ <q-item
|
|
|
+ v-for="holiday in monthHolidays"
|
|
|
+ :key="holiday.id"
|
|
|
+ class="q-pa-sm"
|
|
|
+ >
|
|
|
+ <q-item-section avatar>
|
|
|
+ <q-avatar
|
|
|
+ size="36px"
|
|
|
+ color="deep-orange"
|
|
|
+ text-color="white"
|
|
|
+ class="text-weight-bold"
|
|
|
+ >
|
|
|
+ {{ holiday.day }}
|
|
|
+ </q-avatar>
|
|
|
+ </q-item-section>
|
|
|
+ <q-item-section>
|
|
|
+ <q-item-label class="text-body2">{{
|
|
|
+ holiday.description
|
|
|
+ }}</q-item-label>
|
|
|
+ <q-item-label caption>{{
|
|
|
+ typeLabel(holiday.type)
|
|
|
+ }}</q-item-label>
|
|
|
+ </q-item-section>
|
|
|
+ <q-item-section side>
|
|
|
+ <q-btn
|
|
|
+ flat
|
|
|
+ round
|
|
|
+ dense
|
|
|
+ icon="mdi-trash-can-outline"
|
|
|
+ color="grey-5"
|
|
|
+ size="sm"
|
|
|
+ :loading="deletingId === holiday.id"
|
|
|
+ @click="removeHoliday(holiday)"
|
|
|
+ />
|
|
|
+ </q-item-section>
|
|
|
+ </q-item>
|
|
|
+ </q-list>
|
|
|
|
|
|
- <div v-if="monthHolidays.length === 0" class="text-caption text-grey-5 text-center q-mt-lg">
|
|
|
- Clique em um dia no calendário para adicionar um registro.
|
|
|
- </div>
|
|
|
-
|
|
|
- <q-list v-else separator>
|
|
|
- <q-item
|
|
|
- v-for="holiday in monthHolidays"
|
|
|
- :key="holiday._key"
|
|
|
- class="q-pa-sm"
|
|
|
+ <div
|
|
|
+ v-if="monthHolidays.length > 0"
|
|
|
+ class="text-caption text-grey-5 text-center q-mt-md q-px-sm"
|
|
|
>
|
|
|
- <q-item-section avatar>
|
|
|
- <q-avatar
|
|
|
- size="36px"
|
|
|
- color="deep-orange"
|
|
|
- text-color="white"
|
|
|
- class="text-weight-bold"
|
|
|
- >
|
|
|
- {{ holiday.day }}
|
|
|
- </q-avatar>
|
|
|
- </q-item-section>
|
|
|
- <q-item-section>
|
|
|
- <q-item-label class="text-body2">{{ holiday.description }}</q-item-label>
|
|
|
- <q-item-label caption>Feriado</q-item-label>
|
|
|
- </q-item-section>
|
|
|
- <q-item-section side>
|
|
|
- <q-btn
|
|
|
- flat
|
|
|
- round
|
|
|
- dense
|
|
|
- icon="mdi-trash-can-outline"
|
|
|
- color="grey-5"
|
|
|
- size="sm"
|
|
|
- @click="removeHoliday(holiday)"
|
|
|
- />
|
|
|
- </q-item-section>
|
|
|
- </q-item>
|
|
|
- </q-list>
|
|
|
- </div>
|
|
|
- </q-card-section>
|
|
|
-
|
|
|
- <q-separator />
|
|
|
-
|
|
|
- <q-card-actions align="right">
|
|
|
- <q-btn outline color="primary" label="CANCELAR" no-caps @click="onDialogCancel" />
|
|
|
- <q-btn
|
|
|
- unelevated
|
|
|
- color="primary"
|
|
|
- label="SALVAR"
|
|
|
- no-caps
|
|
|
- :loading="saving"
|
|
|
- @click="onSave"
|
|
|
+ Clique em um dia no calendário para adicionar um registro.
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </q-card-section>
|
|
|
+
|
|
|
+ <q-separator />
|
|
|
+
|
|
|
+ <q-card-actions align="right">
|
|
|
+ <q-btn
|
|
|
+ outline
|
|
|
+ color="primary"
|
|
|
+ label="FECHAR"
|
|
|
+ no-caps
|
|
|
+ @click="onClose"
|
|
|
+ />
|
|
|
+ </q-card-actions>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-else>
|
|
|
+ <DefaultDialogHeader
|
|
|
+ :title="`Novo Registro: ${formattedSelectedDate}`"
|
|
|
+ @close="cancelNewRecord"
|
|
|
/>
|
|
|
- </q-card-actions>
|
|
|
+
|
|
|
+ <q-card-section class="column q-gutter-md q-pt-sm">
|
|
|
+ <DefaultInput
|
|
|
+ v-model="newDescription"
|
|
|
+ label="Nome do Evento"
|
|
|
+ placeholder="Ex: Ponto facultativo, Natal..."
|
|
|
+ icon="mdi-pencil-outline"
|
|
|
+ outlined
|
|
|
+ autofocus
|
|
|
+ />
|
|
|
+
|
|
|
+ <DefaultSelect
|
|
|
+ v-model="newType"
|
|
|
+ label="Selecione o Tipo."
|
|
|
+ :options="typeOptions"
|
|
|
+ emit-value
|
|
|
+ map-options
|
|
|
+ outlined
|
|
|
+ />
|
|
|
+ </q-card-section>
|
|
|
+
|
|
|
+ <q-separator />
|
|
|
+
|
|
|
+ <q-card-actions align="right">
|
|
|
+ <q-btn
|
|
|
+ outline
|
|
|
+ color="primary"
|
|
|
+ label="CANCELAR"
|
|
|
+ no-caps
|
|
|
+ @click="cancelNewRecord"
|
|
|
+ />
|
|
|
+ <q-btn
|
|
|
+ unelevated
|
|
|
+ color="primary"
|
|
|
+ label="SALVAR"
|
|
|
+ no-caps
|
|
|
+ :loading="saving"
|
|
|
+ :disable="!newDescription.trim()"
|
|
|
+ @click="saveNewRecord"
|
|
|
+ />
|
|
|
+ </q-card-actions>
|
|
|
+ </template>
|
|
|
</q-card>
|
|
|
</q-dialog>
|
|
|
</template>
|
|
|
@@ -167,26 +204,28 @@
|
|
|
import { ref, computed, onMounted } from "vue";
|
|
|
import { useDialogPluginComponent, useQuasar } from "quasar";
|
|
|
import DefaultDialogHeader from "src/components/defaults/DefaultDialogHeader.vue";
|
|
|
+import DefaultInput from "src/components/defaults/DefaultInput.vue";
|
|
|
+import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
|
|
|
import { getHolidays, createHoliday, deleteHoliday } from "src/api/holiday";
|
|
|
|
|
|
defineEmits([...useDialogPluginComponent.emits]);
|
|
|
|
|
|
-const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent();
|
|
|
+const { dialogRef, onDialogHide, onDialogOK } = useDialogPluginComponent();
|
|
|
const $q = useQuasar();
|
|
|
|
|
|
const now = new Date();
|
|
|
-const currentMonth = ref(now.getMonth() + 1); // 1-12
|
|
|
+const currentMonth = ref(now.getMonth() + 1);
|
|
|
const currentYear = ref(now.getFullYear());
|
|
|
+
|
|
|
+const view = ref("calendar");
|
|
|
const selectedDay = ref(null);
|
|
|
const newDescription = ref("");
|
|
|
-const saving = ref(false);
|
|
|
+const newType = ref("feriado");
|
|
|
|
|
|
-// Holidays loaded from API
|
|
|
-const existingHolidays = ref([]); // { id, holiday_date, description }
|
|
|
-// Pending additions (not yet saved)
|
|
|
-const pendingAdditions = ref([]); // { _key, holiday_date, description, day }
|
|
|
-// IDs to delete on save
|
|
|
-const pendingDeletions = ref([]); // ids
|
|
|
+const existingHolidays = ref([]);
|
|
|
+const loadingHolidays = ref(false);
|
|
|
+const saving = ref(false);
|
|
|
+const deletingId = ref(null);
|
|
|
|
|
|
const weekDays = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
|
|
|
|
@@ -205,6 +244,11 @@ const monthOptions = [
|
|
|
{ label: "Dec", value: 12 },
|
|
|
];
|
|
|
|
|
|
+const typeOptions = [
|
|
|
+ { label: "Feriado", value: "feriado" },
|
|
|
+ { label: "Ponto Facultativo", value: "facultativo" },
|
|
|
+];
|
|
|
+
|
|
|
const yearOptions = computed(() => {
|
|
|
const base = now.getFullYear();
|
|
|
return Array.from({ length: 7 }, (_, i) => {
|
|
|
@@ -213,58 +257,48 @@ const yearOptions = computed(() => {
|
|
|
});
|
|
|
});
|
|
|
|
|
|
-// All holidays for current view (existing - deleted + pending)
|
|
|
-const allHolidays = computed(() => {
|
|
|
- const active = existingHolidays.value
|
|
|
- .filter((h) => !pendingDeletions.value.includes(h.id))
|
|
|
+const monthHolidays = computed(() =>
|
|
|
+ existingHolidays.value
|
|
|
+ .filter((h) => {
|
|
|
+ const d = new Date(h.holiday_date + "T00:00:00");
|
|
|
+ return (
|
|
|
+ d.getMonth() + 1 === currentMonth.value &&
|
|
|
+ d.getFullYear() === currentYear.value
|
|
|
+ );
|
|
|
+ })
|
|
|
.map((h) => {
|
|
|
const d = new Date(h.holiday_date + "T00:00:00");
|
|
|
- return {
|
|
|
- _key: `existing-${h.id}`,
|
|
|
- id: h.id,
|
|
|
- isPending: false,
|
|
|
- holiday_date: h.holiday_date,
|
|
|
- day: d.getDate(),
|
|
|
- month: d.getMonth() + 1,
|
|
|
- year: d.getFullYear(),
|
|
|
- description: h.description,
|
|
|
- };
|
|
|
- });
|
|
|
- const additions = pendingAdditions.value.map((h) => ({ ...h, isPending: true }));
|
|
|
- return [...active, ...additions];
|
|
|
-});
|
|
|
-
|
|
|
-// Holidays for selected month
|
|
|
-const monthHolidays = computed(() => {
|
|
|
- return allHolidays.value
|
|
|
- .filter((h) => h.month === currentMonth.value && h.year === currentYear.value)
|
|
|
- .sort((a, b) => a.day - b.day);
|
|
|
-});
|
|
|
+ return { ...h, day: d.getDate() };
|
|
|
+ })
|
|
|
+ .sort((a, b) => a.day - b.day),
|
|
|
+);
|
|
|
|
|
|
-// Compute calendar cells
|
|
|
const calendarCells = computed(() => {
|
|
|
const year = currentYear.value;
|
|
|
const month = currentMonth.value;
|
|
|
- const firstDay = new Date(year, month - 1, 1).getDay(); // 0=Sun
|
|
|
+ const firstDay = new Date(year, month - 1, 1).getDay();
|
|
|
const daysInMonth = new Date(year, month, 0).getDate();
|
|
|
-
|
|
|
const holidayDays = new Set(monthHolidays.value.map((h) => h.day));
|
|
|
|
|
|
const cells = [];
|
|
|
- for (let i = 0; i < firstDay; i++) {
|
|
|
+ for (let i = 0; i < firstDay; i++)
|
|
|
cells.push({ day: null, isHoliday: false });
|
|
|
- }
|
|
|
- for (let d = 1; d <= daysInMonth; d++) {
|
|
|
+ for (let d = 1; d <= daysInMonth; d++)
|
|
|
cells.push({ day: d, isHoliday: holidayDays.has(d) });
|
|
|
- }
|
|
|
return cells;
|
|
|
});
|
|
|
|
|
|
-const selectedDayHasHoliday = computed(() => {
|
|
|
- if (selectedDay.value === null) return false;
|
|
|
- return monthHolidays.value.some((h) => h.day === selectedDay.value);
|
|
|
+const formattedSelectedDate = computed(() => {
|
|
|
+ if (!selectedDay.value) return "";
|
|
|
+ const dd = String(selectedDay.value).padStart(2, "0");
|
|
|
+ const mm = String(currentMonth.value).padStart(2, "0");
|
|
|
+ return `${dd}/${mm}/${currentYear.value}`;
|
|
|
});
|
|
|
|
|
|
+function typeLabel(type) {
|
|
|
+ return type === "facultativo" ? "Ponto Facultativo" : "Feriado";
|
|
|
+}
|
|
|
+
|
|
|
function prevMonth() {
|
|
|
if (currentMonth.value === 1) {
|
|
|
currentMonth.value = 12;
|
|
|
@@ -272,8 +306,6 @@ function prevMonth() {
|
|
|
} else {
|
|
|
currentMonth.value -= 1;
|
|
|
}
|
|
|
- selectedDay.value = null;
|
|
|
- newDescription.value = "";
|
|
|
}
|
|
|
|
|
|
function nextMonth() {
|
|
|
@@ -283,22 +315,21 @@ function nextMonth() {
|
|
|
} else {
|
|
|
currentMonth.value += 1;
|
|
|
}
|
|
|
- selectedDay.value = null;
|
|
|
- newDescription.value = "";
|
|
|
}
|
|
|
|
|
|
-function onDayClick(cell) {
|
|
|
- if (selectedDayHasHoliday.value && selectedDay.value === cell.day) {
|
|
|
- // Toggle off
|
|
|
- selectedDay.value = null;
|
|
|
- newDescription.value = "";
|
|
|
- return;
|
|
|
- }
|
|
|
- selectedDay.value = cell.day;
|
|
|
+function openNewRecord(day) {
|
|
|
+ selectedDay.value = day;
|
|
|
newDescription.value = "";
|
|
|
+ newType.value = "feriado";
|
|
|
+ view.value = "new-record";
|
|
|
+}
|
|
|
+
|
|
|
+function cancelNewRecord() {
|
|
|
+ view.value = "calendar";
|
|
|
+ selectedDay.value = null;
|
|
|
}
|
|
|
|
|
|
-function addHoliday() {
|
|
|
+async function saveNewRecord() {
|
|
|
const desc = newDescription.value.trim();
|
|
|
if (!desc || selectedDay.value === null) return;
|
|
|
|
|
|
@@ -306,59 +337,59 @@ function addHoliday() {
|
|
|
const dd = String(selectedDay.value).padStart(2, "0");
|
|
|
const dateStr = `${currentYear.value}-${mm}-${dd}`;
|
|
|
|
|
|
- pendingAdditions.value.push({
|
|
|
- _key: `pending-${Date.now()}`,
|
|
|
- id: null,
|
|
|
- isPending: true,
|
|
|
- holiday_date: dateStr,
|
|
|
- day: selectedDay.value,
|
|
|
- month: currentMonth.value,
|
|
|
- year: currentYear.value,
|
|
|
- description: desc,
|
|
|
- });
|
|
|
-
|
|
|
- selectedDay.value = null;
|
|
|
- newDescription.value = "";
|
|
|
-}
|
|
|
-
|
|
|
-function removeHoliday(holiday) {
|
|
|
- if (holiday.isPending) {
|
|
|
- pendingAdditions.value = pendingAdditions.value.filter((h) => h._key !== holiday._key);
|
|
|
- } else {
|
|
|
- pendingDeletions.value.push(holiday.id);
|
|
|
- }
|
|
|
- if (selectedDay.value === holiday.day) {
|
|
|
+ saving.value = true;
|
|
|
+ try {
|
|
|
+ await createHoliday({
|
|
|
+ holiday_date: dateStr,
|
|
|
+ description: desc,
|
|
|
+ type: newType.value,
|
|
|
+ });
|
|
|
+ await loadHolidays();
|
|
|
+ view.value = "calendar";
|
|
|
selectedDay.value = null;
|
|
|
- newDescription.value = "";
|
|
|
+ } catch {
|
|
|
+ $q.notify({
|
|
|
+ type: "negative",
|
|
|
+ message: "Erro ao salvar feriado. Tente novamente.",
|
|
|
+ });
|
|
|
+ } finally {
|
|
|
+ saving.value = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-async function onSave() {
|
|
|
- saving.value = true;
|
|
|
+async function removeHoliday(holiday) {
|
|
|
+ deletingId.value = holiday.id;
|
|
|
try {
|
|
|
- // Delete first
|
|
|
- for (const id of pendingDeletions.value) {
|
|
|
- await deleteHoliday(id);
|
|
|
- }
|
|
|
- // Then create
|
|
|
- for (const h of pendingAdditions.value) {
|
|
|
- await createHoliday({ holiday_date: h.holiday_date, description: h.description });
|
|
|
- }
|
|
|
- onDialogOK(true);
|
|
|
+ await deleteHoliday(holiday.id);
|
|
|
+ existingHolidays.value = existingHolidays.value.filter(
|
|
|
+ (h) => h.id !== holiday.id,
|
|
|
+ );
|
|
|
} catch {
|
|
|
- $q.notify({ type: "negative", message: "Erro ao salvar feriados. Tente novamente." });
|
|
|
+ $q.notify({
|
|
|
+ type: "negative",
|
|
|
+ message: "Erro ao remover feriado. Tente novamente.",
|
|
|
+ });
|
|
|
} finally {
|
|
|
- saving.value = false;
|
|
|
+ deletingId.value = null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-onMounted(async () => {
|
|
|
+async function loadHolidays() {
|
|
|
+ loadingHolidays.value = true;
|
|
|
try {
|
|
|
existingHolidays.value = await getHolidays();
|
|
|
} catch {
|
|
|
$q.notify({ type: "negative", message: "Erro ao carregar feriados." });
|
|
|
+ } finally {
|
|
|
+ loadingHolidays.value = false;
|
|
|
}
|
|
|
-});
|
|
|
+}
|
|
|
+
|
|
|
+function onClose() {
|
|
|
+ onDialogOK(true);
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(loadHolidays);
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
@@ -370,47 +401,53 @@ onMounted(async () => {
|
|
|
.calendar-grid {
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(7, 1fr);
|
|
|
- gap: 4px;
|
|
|
+ gap: 2px;
|
|
|
}
|
|
|
|
|
|
-.calendar-header-cell {
|
|
|
- padding: 4px 0;
|
|
|
+.cal-header {
|
|
|
+ padding: 6px 0 4px;
|
|
|
font-weight: 600;
|
|
|
+ text-align: center;
|
|
|
}
|
|
|
|
|
|
-.calendar-cell {
|
|
|
+.cal-cell {
|
|
|
aspect-ratio: 1;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- border-radius: 8px;
|
|
|
+ border-radius: 50%;
|
|
|
cursor: pointer;
|
|
|
- font-size: 14px;
|
|
|
transition: background 0.15s;
|
|
|
}
|
|
|
|
|
|
-.calendar-cell:hover:not(.calendar-cell--empty) {
|
|
|
+.cal-cell:hover:not(.cal-cell--empty):not(.cal-cell--holiday) {
|
|
|
background: #f5f5f5;
|
|
|
}
|
|
|
|
|
|
-.calendar-cell--empty {
|
|
|
+.cal-cell--empty {
|
|
|
cursor: default;
|
|
|
pointer-events: none;
|
|
|
}
|
|
|
|
|
|
-.calendar-cell--holiday {
|
|
|
+.cal-cell__number {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ border-radius: 50%;
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.cal-cell--holiday .cal-cell__number {
|
|
|
background: #e64a19;
|
|
|
color: #fff;
|
|
|
font-weight: 700;
|
|
|
}
|
|
|
|
|
|
-.calendar-cell--holiday:hover {
|
|
|
- background: #bf360c !important;
|
|
|
-}
|
|
|
-
|
|
|
-.calendar-cell--selected:not(.calendar-cell--holiday) {
|
|
|
- background: #ffe0d6;
|
|
|
- color: #e64a19;
|
|
|
- font-weight: 700;
|
|
|
+.cal-cell--holiday {
|
|
|
+ cursor: default;
|
|
|
}
|
|
|
</style>
|