Ajuste usuarios - Inicio agenda
This commit is contained in:
@@ -1,10 +1,209 @@
|
||||
<!-- src/features/agenda/components/AgendaCalendar.vue -->
|
||||
<script setup>
|
||||
defineProps({})
|
||||
defineEmits([])
|
||||
import { computed, ref, watch, onMounted } from 'vue'
|
||||
|
||||
import FullCalendar from '@fullcalendar/vue3'
|
||||
import timeGridPlugin from '@fullcalendar/timegrid'
|
||||
import interactionPlugin from '@fullcalendar/interaction'
|
||||
import dayGridPlugin from '@fullcalendar/daygrid'
|
||||
|
||||
import ProgressSpinner from 'primevue/progressspinner'
|
||||
|
||||
const props = defineProps({
|
||||
// UI
|
||||
view: { type: String, default: 'day' }, // 'day' | 'week'
|
||||
mode: { type: String, default: 'work_hours' }, // 'full_24h' | 'work_hours'
|
||||
|
||||
// calendar behavior
|
||||
timezone: { type: String, default: 'America/Sao_Paulo' },
|
||||
slotDuration: { type: String, default: '00:30:00' },
|
||||
slotMinTime: { type: String, default: '06:00:00' },
|
||||
slotMaxTime: { type: String, default: '22:00:00' },
|
||||
businessHours: { type: [Array, Object], default: () => [] },
|
||||
|
||||
// data
|
||||
events: { type: Array, default: () => [] },
|
||||
loading: { type: Boolean, default: false }
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'rangeChange',
|
||||
'selectTime',
|
||||
'eventClick',
|
||||
'eventDrop',
|
||||
'eventResize'
|
||||
])
|
||||
|
||||
const fcRef = ref(null)
|
||||
|
||||
const initialView = computed(() => (props.view === 'week' ? 'timeGridWeek' : 'timeGridDay'))
|
||||
|
||||
function getApi () {
|
||||
const inst = fcRef.value
|
||||
return inst?.getApi?.() || null
|
||||
}
|
||||
|
||||
function emitRange () {
|
||||
const api = getApi()
|
||||
if (!api) return
|
||||
const v = api.view
|
||||
emit('rangeChange', { start: v.activeStart, end: v.activeEnd })
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// Calendar options
|
||||
// -----------------------------
|
||||
const calendarOptions = computed(() => {
|
||||
const isWorkHours = props.mode !== 'full_24h'
|
||||
|
||||
// No modo 24h, não recorta — mas mantemos min/max caso você queira ainda controlar
|
||||
const minTime = isWorkHours ? props.slotMinTime : '00:00:00'
|
||||
// FullCalendar timeGrid costuma aceitar 24:00:00, mas 23:59:59 evita edge-case
|
||||
const maxTime = isWorkHours ? props.slotMaxTime : '23:59:59'
|
||||
|
||||
return {
|
||||
plugins: [timeGridPlugin, interactionPlugin, dayGridPlugin],
|
||||
initialView: initialView.value,
|
||||
|
||||
timeZone: props.timezone,
|
||||
|
||||
// Header desativado (você controla no Toolbar)
|
||||
headerToolbar: false,
|
||||
|
||||
// Visão “produto”: blocos com linhas suaves
|
||||
nowIndicator: true,
|
||||
allDaySlot: false,
|
||||
expandRows: true,
|
||||
height: 'auto',
|
||||
|
||||
// Seleção / DnD / Resize
|
||||
selectable: true,
|
||||
selectMirror: true,
|
||||
editable: true,
|
||||
eventStartEditable: true,
|
||||
eventDurationEditable: true,
|
||||
|
||||
// Intervalos visuais
|
||||
slotDuration: props.slotDuration,
|
||||
slotMinTime: minTime,
|
||||
slotMaxTime: maxTime,
|
||||
slotLabelFormat: {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
},
|
||||
|
||||
// Horário “verdadeiro” de funcionamento (se você usar)
|
||||
businessHours: props.businessHours,
|
||||
|
||||
// Dados
|
||||
events: props.events,
|
||||
|
||||
// Melhor UX
|
||||
weekends: true,
|
||||
firstDay: 1, // segunda
|
||||
|
||||
// Callbacks
|
||||
datesSet: () => {
|
||||
// dispara quando muda o intervalo exibido (prev/next/today/view)
|
||||
emitRange()
|
||||
},
|
||||
|
||||
select: (selection) => {
|
||||
// selection: { start, end, allDay, ... }
|
||||
emit('selectTime', selection)
|
||||
},
|
||||
|
||||
eventClick: (info) => emit('eventClick', info),
|
||||
eventDrop: (info) => emit('eventDrop', info),
|
||||
eventResize: (info) => emit('eventResize', info)
|
||||
}
|
||||
})
|
||||
|
||||
// -----------------------------
|
||||
// Exposed methods (para Toolbar/Page)
|
||||
// -----------------------------
|
||||
function goToday () { getApi()?.today?.() }
|
||||
function prev () { getApi()?.prev?.() }
|
||||
function next () { getApi()?.next?.() }
|
||||
|
||||
function setView (v) {
|
||||
const api = getApi()
|
||||
if (!api) return
|
||||
api.changeView(v === 'week' ? 'timeGridWeek' : 'timeGridDay')
|
||||
}
|
||||
|
||||
defineExpose({ goToday, prev, next, setView })
|
||||
|
||||
// Se a prop view mudar, sincroniza
|
||||
watch(
|
||||
() => props.view,
|
||||
(v) => setView(v)
|
||||
)
|
||||
|
||||
// Emite o range inicial assim que montar
|
||||
onMounted(() => {
|
||||
// garante que o FullCalendar já criou a view
|
||||
setTimeout(() => emitRange(), 0)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-4 rounded-xl border border-[var(--surface-border)] bg-[var(--surface-card)]">
|
||||
<b>AgendaCalendar (placeholder)</b>
|
||||
<div class="agenda-calendar-wrap">
|
||||
<div v-if="loading" class="agenda-calendar-loading">
|
||||
<ProgressSpinner strokeWidth="3" />
|
||||
<div class="text-sm mt-2" style="color: var(--text-color-secondary);">
|
||||
Carregando agenda…
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FullCalendar
|
||||
ref="fcRef"
|
||||
:options="calendarOptions"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.agenda-calendar-wrap{
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.agenda-calendar-loading{
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 20;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(3px);
|
||||
background: color-mix(in srgb, var(--surface-card) 85%, transparent);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
/* Deixa o calendário “respirar” dentro de cards/layouts */
|
||||
:deep(.fc){
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
:deep(.fc .fc-timegrid-slot){
|
||||
height: 2.2rem;
|
||||
}
|
||||
|
||||
:deep(.fc .fc-timegrid-now-indicator-line){
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
:deep(.fc .fc-scrollgrid){
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--surface-border);
|
||||
}
|
||||
|
||||
:deep(.fc .fc-timegrid-axis-cushion),
|
||||
:deep(.fc .fc-timegrid-slot-label-cushion){
|
||||
color: var(--text-color-secondary);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user