v-sistec-features 1.31.12 → 1.31.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/v-sistec-features.css +1 -1
- package/dist/vDataAgenda.d.ts +591 -17
- package/dist/vDataAgenda.js +3382 -3469
- package/package.json +1 -1
- package/src/DataAgenda/VDataAgenda.vue +122 -285
- package/src/DataAgenda/components/AgendaMatrix.vue +27 -18
- package/src/DataAgenda/components/AgendaMatrixBase.vue +93 -501
- package/src/DataAgenda/components/AgendaMatrixUnico.vue +18 -28
- package/src/DataAgenda/components/AgendaTabelaHorario.vue +105 -515
- package/src/DataAgenda/components/AgendaVueCal.vue +118 -199
- package/src/DataAgenda/components/ModalReserva.vue +200 -291
- package/src/DataAgenda/composables/useGradeCompartilhada.ts +216 -0
|
@@ -2,14 +2,25 @@
|
|
|
2
2
|
========
|
|
3
3
|
Este arquivo implementa o modelo visual 'tabela-horario' com os turnos na vertical à esquerda,
|
|
4
4
|
colunas de horários seguidas pelos dias da semana e suporte a colunas de horários específicas (como Sábado)
|
|
5
|
-
com altura proporcional, mesclagem vertical de reservas contínuas
|
|
5
|
+
com altura proporcional, mesclagem vertical de reservas contínuas, seleção vertical via BlocoArrastavel
|
|
6
|
+
e exposição da lista contendo o recurso atualmente em exibição.
|
|
6
7
|
========
|
|
7
8
|
-->
|
|
8
9
|
<script setup lang="ts">
|
|
9
|
-
import { computed,
|
|
10
|
+
import { computed, watch, inject } from 'vue';
|
|
10
11
|
import { useAgenda } from '../composables/useAgenda';
|
|
11
12
|
import { useCoresAgenda } from '../composables/useCoresAgenda';
|
|
12
13
|
import { useSelecaoArrasto } from '../composables/useSelecaoArrasto';
|
|
14
|
+
import {
|
|
15
|
+
SIGLAS_DIAS_JS,
|
|
16
|
+
TITULOS_DIAS_CABECALHO,
|
|
17
|
+
extrairDataHoraLocal,
|
|
18
|
+
extrairHoraFormatada,
|
|
19
|
+
criarDataLocal,
|
|
20
|
+
mesclarSlotsReservados,
|
|
21
|
+
useGradeSemanal,
|
|
22
|
+
useSincronizarRecursoUnico
|
|
23
|
+
} from '../composables/useGradeCompartilhada';
|
|
13
24
|
import BlocoArrastavel from './BlocoArrastavel.vue';
|
|
14
25
|
import ModalReserva from './ModalReserva.vue';
|
|
15
26
|
import ModalDetalhesReserva from './ModalDetalhesReserva.vue';
|
|
@@ -35,111 +46,23 @@ const props = defineProps<{
|
|
|
35
46
|
}>();
|
|
36
47
|
|
|
37
48
|
const api = inject('api-instance-agenda') as any;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const {
|
|
41
|
-
dadosMatrix,
|
|
42
|
-
buscarSlotsMatrix,
|
|
43
|
-
buscarReservas,
|
|
44
|
-
agendasDisponiveis,
|
|
45
|
-
agendaSelecionada,
|
|
46
|
-
recursosDisponiveis,
|
|
47
|
-
recursoSelecionado,
|
|
48
|
-
dataSelecionada,
|
|
49
|
-
converterParaUtc
|
|
50
|
-
} = useAgenda(api);
|
|
49
|
+
defineEmits(['before-save']);
|
|
51
50
|
|
|
51
|
+
const { agendaSelecionada, recursosDisponiveis, recursoSelecionado } = useAgenda(api);
|
|
52
52
|
const { theme, obterCoresBloco } = useCoresAgenda();
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
const SIGLAS_DIAS_JS = ['DOM', 'SEG', 'TER', 'QUA', 'QUI', 'SEX', 'SAB'];
|
|
56
|
-
|
|
57
|
-
// Mapeamento dos títulos de cabeçalho de cada dia da semana conforme índice do Date.getDay().
|
|
58
|
-
const TITULOS_DIAS_CABECALHO = [
|
|
59
|
-
'DOMINGO',
|
|
60
|
-
'2ª FEIRA',
|
|
61
|
-
'3ª FEIRA',
|
|
62
|
-
'4ª FEIRA',
|
|
63
|
-
'5ª FEIRA',
|
|
64
|
-
'6ª FEIRA',
|
|
65
|
-
'SÁBADO'
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
// Indica se os dados da grade estão sendo carregados da API.
|
|
69
|
-
const carregando = ref(false);
|
|
70
|
-
|
|
71
|
-
// Controla a abertura do modal de criação de reserva.
|
|
72
|
-
const modalAberto = ref(false);
|
|
73
|
-
|
|
74
|
-
// Armazena os dados preenchidos pela seleção na grade para enviar ao modal de criação.
|
|
75
|
-
const dadosParaModal = ref({});
|
|
76
|
-
|
|
77
|
-
// Controla a abertura do modal de detalhes de uma reserva existente.
|
|
78
|
-
const modalDetalhesAberto = ref(false);
|
|
79
|
-
|
|
80
|
-
// Armazena a reserva selecionada ao clicar em um bloco ocupado.
|
|
81
|
-
const reservaSelecionadaDetalhes = ref<any>(null);
|
|
82
|
-
|
|
83
|
-
// Recebe uma string ISO UTC e devolve objeto com data (YYYY-MM-DD) e hora (HH:MM) locais.
|
|
84
|
-
const extrairDataHoraLocal = (isoString: string) => {
|
|
85
|
-
const d = new Date(isoString);
|
|
86
|
-
const ano = d.getFullYear();
|
|
87
|
-
const mes = String(d.getMonth() + 1).padStart(2, '0');
|
|
88
|
-
const dia = String(d.getDate()).padStart(2, '0');
|
|
89
|
-
const horas = String(d.getHours()).padStart(2, '0');
|
|
90
|
-
const min = String(d.getMinutes()).padStart(2, '0');
|
|
91
|
-
return { data: `${ano}-${mes}-${dia}`, hora: `${horas}:${min}` };
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// Instância do controlador genérico de arrasto configurada para operar nas colunas verticais de cada dia.
|
|
95
|
-
const controladorArrastoVertical = useSelecaoArrasto({
|
|
96
|
-
aoClicarItemIndisponivel: (celula) => {
|
|
97
|
-
if (celula?.reservado_slot && celula?.detalhes_reserva) {
|
|
98
|
-
reservaSelecionadaDetalhes.value = celula.detalhes_reserva;
|
|
99
|
-
modalDetalhesAberto.value = true;
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
aoFinalizarSelecao: ({ itensSelecionados }) => {
|
|
103
|
-
if (!idRecursoAtivo.value) return;
|
|
104
|
-
const slotInicial = itensSelecionados[0];
|
|
105
|
-
const slotFinal = itensSelecionados[itensSelecionados.length - 1];
|
|
106
|
-
if (!slotInicial || !slotFinal) return;
|
|
107
|
-
|
|
108
|
-
const inicio = extrairDataHoraLocal(slotInicial.data_inicio_slot);
|
|
109
|
-
const fim = extrairDataHoraLocal(slotFinal.data_fim_slot);
|
|
110
|
-
|
|
111
|
-
dadosParaModal.value = {
|
|
112
|
-
id_recurso: idRecursoAtivo.value,
|
|
113
|
-
titulo_reserva: '',
|
|
114
|
-
data_inicio: inicio.data,
|
|
115
|
-
hora_inicio: inicio.hora,
|
|
116
|
-
data_fim: fim.data,
|
|
117
|
-
hora_fim: fim.hora
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
modalAberto.value = true;
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Sincroniza o recurso e a agenda caso o componente tenha recebido a propriedade idRecursoUnico.
|
|
125
|
-
const sincronizarRecursoUnico = () => {
|
|
126
|
-
if (!props.idRecursoUnico || recursosDisponiveis.value.length === 0) return;
|
|
127
|
-
|
|
128
|
-
const encontrado = recursosDisponiveis.value.find(
|
|
129
|
-
(r: any) => Number(r.id_recurso) === Number(props.idRecursoUnico)
|
|
130
|
-
);
|
|
54
|
+
useSincronizarRecursoUnico(api, () => props.idRecursoUnico);
|
|
131
55
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}, { immediate: true });
|
|
56
|
+
const {
|
|
57
|
+
dadosMatrix,
|
|
58
|
+
carregando,
|
|
59
|
+
modalAberto,
|
|
60
|
+
dadosParaModal,
|
|
61
|
+
modalDetalhesAberto,
|
|
62
|
+
reservaSelecionadaDetalhes,
|
|
63
|
+
obterBlocosDoDia,
|
|
64
|
+
dispararBuscaSemanal
|
|
65
|
+
} = useGradeSemanal(api, () => props.configuracaoBlocos);
|
|
143
66
|
|
|
144
67
|
// Quando a agenda muda no seletor e não há idRecursoUnico fixo, garante que o recursoSelecionado pertença à agenda ativa.
|
|
145
68
|
watch(agendaSelecionada, (novoIdAgenda) => {
|
|
@@ -157,66 +80,6 @@ watch(agendaSelecionada, (novoIdAgenda) => {
|
|
|
157
80
|
}
|
|
158
81
|
});
|
|
159
82
|
|
|
160
|
-
// Calcula a data de início da semana (Segunda-feira por padrão) com base na data selecionada.
|
|
161
|
-
const obterInicioDaSemana = (dataRef: Date, idAgenda: number | null) => {
|
|
162
|
-
const d = new Date(dataRef.getFullYear(), dataRef.getMonth(), dataRef.getDate());
|
|
163
|
-
const agendaObj = agendasDisponiveis.value.find((a: any) => Number(a.id_agenda) === Number(idAgenda));
|
|
164
|
-
const diaInicioPython = agendaObj?.detalhes?.dia_inicio_semana_agenda ?? 0;
|
|
165
|
-
const diaInicioJs = (diaInicioPython + 1) % 7;
|
|
166
|
-
const diff = (d.getDay() - diaInicioJs + 7) % 7;
|
|
167
|
-
d.setDate(d.getDate() - diff);
|
|
168
|
-
return d;
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
// Busca os slots da matriz e as reservas para os 7 dias da semana ordenada.
|
|
172
|
-
const dispararBuscaGrade = async () => {
|
|
173
|
-
const idAgenda = agendaSelecionada.value;
|
|
174
|
-
if (!idAgenda) return;
|
|
175
|
-
|
|
176
|
-
carregando.value = true;
|
|
177
|
-
|
|
178
|
-
const valorData = dataSelecionada.value || new Date();
|
|
179
|
-
let dataBase: Date;
|
|
180
|
-
|
|
181
|
-
if (typeof valorData === 'string') {
|
|
182
|
-
const apenasData = String(valorData).substring(0, 10);
|
|
183
|
-
const [ano, mes, dia] = apenasData.split('-');
|
|
184
|
-
dataBase = new Date(Number(ano), Number(mes) - 1, Number(dia));
|
|
185
|
-
} else {
|
|
186
|
-
dataBase = new Date(valorData);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const dataInicio = obterInicioDaSemana(dataBase, idAgenda);
|
|
190
|
-
const dataFim = new Date(dataInicio);
|
|
191
|
-
dataFim.setDate(dataInicio.getDate() + 6);
|
|
192
|
-
|
|
193
|
-
const formatar = (dt: Date) => {
|
|
194
|
-
const ano = dt.getFullYear();
|
|
195
|
-
const mes = String(dt.getMonth() + 1).padStart(2, '0');
|
|
196
|
-
const dia = String(dt.getDate()).padStart(2, '0');
|
|
197
|
-
return `${ano}-${mes}-${dia}`;
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
const strInicio = formatar(dataInicio);
|
|
201
|
-
const strFim = formatar(dataFim);
|
|
202
|
-
|
|
203
|
-
const inicioUtc = encodeURIComponent(converterParaUtc(strInicio, '00:00'));
|
|
204
|
-
const fimUtc = encodeURIComponent(converterParaUtc(strFim, '23:59'));
|
|
205
|
-
|
|
206
|
-
await buscarSlotsMatrix(idAgenda, inicioUtc, fimUtc);
|
|
207
|
-
await buscarReservas(`${strInicio}T00:00:00`, `${strFim}T23:59:59`);
|
|
208
|
-
carregando.value = false;
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
onMounted(() => {
|
|
212
|
-
dispararBuscaGrade();
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
watch([dataSelecionada, agendaSelecionada], () => {
|
|
216
|
-
dispararBuscaGrade();
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// Devolve a lista de recursos pertencentes à agenda selecionada para o seletor de salas.
|
|
220
83
|
const recursosDaAgendaAtual = computed(() => {
|
|
221
84
|
if (!agendaSelecionada.value) return recursosDisponiveis.value;
|
|
222
85
|
return recursosDisponiveis.value.filter(
|
|
@@ -224,7 +87,6 @@ const recursosDaAgendaAtual = computed(() => {
|
|
|
224
87
|
);
|
|
225
88
|
});
|
|
226
89
|
|
|
227
|
-
// Devolve o ID numérico do recurso ativo que está sendo exibido na grade.
|
|
228
90
|
const idRecursoAtivo = computed(() => {
|
|
229
91
|
if (props.idRecursoUnico) return Number(props.idRecursoUnico);
|
|
230
92
|
if (recursoSelecionado.value) return Number(recursoSelecionado.value);
|
|
@@ -232,54 +94,49 @@ const idRecursoAtivo = computed(() => {
|
|
|
232
94
|
return primeiro ? Number(primeiro.id_recurso) : null;
|
|
233
95
|
});
|
|
234
96
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
return
|
|
241
|
-
};
|
|
97
|
+
const recursosEmExibicao = computed(() => {
|
|
98
|
+
if (!idRecursoAtivo.value) return [];
|
|
99
|
+
const encontrado = recursosDisponiveis.value.find(
|
|
100
|
+
(r: any) => Number(r.id_recurso) === Number(idRecursoAtivo.value)
|
|
101
|
+
);
|
|
102
|
+
return encontrado ? [encontrado] : [];
|
|
103
|
+
});
|
|
242
104
|
|
|
243
|
-
|
|
244
|
-
const criarDataLocal = (isoData: string) => {
|
|
245
|
-
const apenasData = String(isoData).substring(0, 10);
|
|
246
|
-
const [ano, mes, dia] = apenasData.split('-');
|
|
247
|
-
return new Date(Number(ano), Number(mes) - 1, Number(dia));
|
|
248
|
-
};
|
|
105
|
+
defineExpose({ recursosEmExibicao });
|
|
249
106
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
107
|
+
const controladorArrastoVertical = useSelecaoArrasto({
|
|
108
|
+
aoClicarItemIndisponivel: (celula) => {
|
|
109
|
+
if (celula?.reservado_slot && celula?.detalhes_reserva) {
|
|
110
|
+
reservaSelecionadaDetalhes.value = celula.detalhes_reserva;
|
|
111
|
+
modalDetalhesAberto.value = true;
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
aoFinalizarSelecao: ({ itensSelecionados }) => {
|
|
115
|
+
if (!idRecursoAtivo.value) return;
|
|
116
|
+
const slotInicial = itensSelecionados[0];
|
|
117
|
+
const slotFinal = itensSelecionados[itensSelecionados.length - 1];
|
|
118
|
+
if (!slotInicial || !slotFinal) return;
|
|
254
119
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
120
|
+
const inicio = extrairDataHoraLocal(slotInicial.data_inicio_slot);
|
|
121
|
+
const fim = extrairDataHoraLocal(slotFinal.data_fim_slot);
|
|
258
122
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
123
|
+
dadosParaModal.value = {
|
|
124
|
+
id_recurso: idRecursoAtivo.value,
|
|
125
|
+
titulo_reserva: '',
|
|
126
|
+
data_inicio: inicio.data,
|
|
127
|
+
hora_inicio: inicio.hora,
|
|
128
|
+
data_fim: fim.data,
|
|
129
|
+
hora_fim: fim.hora
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
modalAberto.value = true;
|
|
266
133
|
}
|
|
267
|
-
return null;
|
|
268
134
|
});
|
|
269
135
|
|
|
270
136
|
// Processa a lista de horários de um grupo de dias e gera os metadados de cada linha (bloco lateral, cor e bordas de subdivisão).
|
|
271
137
|
const mapearLinhasComBlocos = (horariosBase: { horaInicio: string; horaFim: string }[], siglaDia: string) => {
|
|
272
138
|
const total = horariosBase.length;
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
let listaBlocos: any[] | null = null;
|
|
276
|
-
if (cfg) {
|
|
277
|
-
if (cfg.por_dia && cfg.por_dia[siglaDia]) {
|
|
278
|
-
listaBlocos = cfg.por_dia[siglaDia];
|
|
279
|
-
} else if (Array.isArray(cfg.blocos)) {
|
|
280
|
-
listaBlocos = cfg.blocos;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
139
|
+
const listaBlocos = obterBlocosDoDia(siglaDia);
|
|
283
140
|
|
|
284
141
|
const metadadosLinhas: MetadadoLinha[] = horariosBase.map((h) => ({
|
|
285
142
|
horaInicio: h.horaInicio,
|
|
@@ -302,8 +159,6 @@ const mapearLinhasComBlocos = (horariosBase: { horaInicio: string; horaFim: stri
|
|
|
302
159
|
listaBlocos.forEach((blocoCfg: any, idxBloco: number) => {
|
|
303
160
|
if (cursor >= total) return;
|
|
304
161
|
const qtdSolicitada = Number(blocoCfg.quantidade) || 0;
|
|
305
|
-
if (qtdSolicitada <= 0) return;
|
|
306
|
-
|
|
307
162
|
const qtdReal = Math.min(qtdSolicitada, total - cursor);
|
|
308
163
|
if (qtdReal <= 0) return;
|
|
309
164
|
|
|
@@ -331,37 +186,27 @@ const mapearLinhasComBlocos = (horariosBase: { horaInicio: string; horaFim: stri
|
|
|
331
186
|
let consumido = 0;
|
|
332
187
|
subLista.forEach((tam: number) => {
|
|
333
188
|
if (consumido >= qtdReal) return;
|
|
334
|
-
|
|
335
|
-
consumido += tamEfetivo;
|
|
189
|
+
consumido += Math.min(Number(tam) || 1, qtdReal - consumido);
|
|
336
190
|
const metaFimSub = metadadosLinhas[cursor + consumido - 1];
|
|
337
|
-
if (metaFimSub)
|
|
338
|
-
metaFimSub.fimDeSubbloco = true;
|
|
339
|
-
}
|
|
191
|
+
if (metaFimSub) metaFimSub.fimDeSubbloco = true;
|
|
340
192
|
});
|
|
341
193
|
} else if (subFixo > 0) {
|
|
342
194
|
let consumido = 0;
|
|
343
195
|
while (consumido < qtdReal) {
|
|
344
|
-
|
|
345
|
-
consumido += tamEfetivo;
|
|
196
|
+
consumido += Math.min(subFixo, qtdReal - consumido);
|
|
346
197
|
const metaFimSub = metadadosLinhas[cursor + consumido - 1];
|
|
347
|
-
if (metaFimSub)
|
|
348
|
-
metaFimSub.fimDeSubbloco = true;
|
|
349
|
-
}
|
|
198
|
+
if (metaFimSub) metaFimSub.fimDeSubbloco = true;
|
|
350
199
|
}
|
|
351
200
|
}
|
|
352
201
|
|
|
353
202
|
const metaFimPrincipal = metadadosLinhas[cursor + qtdReal - 1];
|
|
354
|
-
if (metaFimPrincipal)
|
|
355
|
-
metaFimPrincipal.fimDeBlocoPrincipal = true;
|
|
356
|
-
}
|
|
203
|
+
if (metaFimPrincipal) metaFimPrincipal.fimDeBlocoPrincipal = true;
|
|
357
204
|
cursor += qtdReal;
|
|
358
205
|
});
|
|
359
206
|
|
|
360
207
|
if (cursor < total) {
|
|
361
208
|
const restante = total - cursor;
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
const { corFundo, corBorda } = obterCoresBloco(idxExtra);
|
|
209
|
+
const { corFundo, corBorda } = obterCoresBloco(listaBlocos.length);
|
|
365
210
|
|
|
366
211
|
const metaRestante = metadadosLinhas[cursor];
|
|
367
212
|
if (metaRestante) {
|
|
@@ -379,15 +224,13 @@ const mapearLinhasComBlocos = (horariosBase: { horaInicio: string; horaFim: stri
|
|
|
379
224
|
}
|
|
380
225
|
|
|
381
226
|
const metaUltimo = metadadosLinhas[total - 1];
|
|
382
|
-
if (metaUltimo)
|
|
383
|
-
metaUltimo.fimDeBlocoPrincipal = true;
|
|
384
|
-
}
|
|
227
|
+
if (metaUltimo) metaUltimo.fimDeBlocoPrincipal = true;
|
|
385
228
|
}
|
|
386
229
|
|
|
387
230
|
return { temColunaTurno: true, linhas: metadadosLinhas };
|
|
388
231
|
};
|
|
389
232
|
|
|
390
|
-
// Constrói a estrutura completa de grupos de colunas
|
|
233
|
+
// Constrói a estrutura completa de grupos de colunas e calcula o rowspan das reservas.
|
|
391
234
|
const estruturaGradeHorario = computed(() => {
|
|
392
235
|
if (!dadosMatrix.value?.datas_matriz || !idRecursoAtivo.value) {
|
|
393
236
|
return { grupos: [], maxLinhas: 0, temColunaTurnoPrincipal: false };
|
|
@@ -430,57 +273,11 @@ const estruturaGradeHorario = computed(() => {
|
|
|
430
273
|
});
|
|
431
274
|
}
|
|
432
275
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
// Calcula a mesclagem vertical (rowspan) para reservas que ocupam múltiplos horários consecutivos.
|
|
436
|
-
const celulasProcessadas: any[] = [];
|
|
437
|
-
let i = 0;
|
|
438
|
-
while (i < slotsReservaveis.length) {
|
|
439
|
-
const slotAtual = slotsReservaveis[i];
|
|
440
|
-
const codRef = slotAtual?.detalhes_reserva?.codigo_referencia_reserva;
|
|
441
|
-
|
|
442
|
-
if (slotAtual?.reservado_slot && codRef) {
|
|
443
|
-
let span = 1;
|
|
444
|
-
while (
|
|
445
|
-
i + span < slotsReservaveis.length &&
|
|
446
|
-
slotsReservaveis[i + span]?.reservado_slot &&
|
|
447
|
-
slotsReservaveis[i + span]?.detalhes_reserva?.codigo_referencia_reserva === codRef
|
|
448
|
-
) {
|
|
449
|
-
span++;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
celulasProcessadas.push({
|
|
453
|
-
...slotAtual,
|
|
454
|
-
indiceLinha: i,
|
|
455
|
-
rowspan: span,
|
|
456
|
-
ocultaPorRowspan: false
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
for (let k = 1; k < span; k++) {
|
|
460
|
-
celulasProcessadas.push({
|
|
461
|
-
...slotsReservaveis[i + k],
|
|
462
|
-
indiceLinha: i + k,
|
|
463
|
-
rowspan: 1,
|
|
464
|
-
ocultaPorRowspan: true
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
i += span;
|
|
468
|
-
} else {
|
|
469
|
-
celulasProcessadas.push({
|
|
470
|
-
...slotAtual,
|
|
471
|
-
indiceLinha: i,
|
|
472
|
-
rowspan: 1,
|
|
473
|
-
ocultaPorRowspan: false
|
|
474
|
-
});
|
|
475
|
-
i++;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
grupo.dias.push({
|
|
276
|
+
gruposPorAssinatura.get(assinatura).dias.push({
|
|
480
277
|
data_slot: dia.data_slot,
|
|
481
278
|
tituloDia,
|
|
482
279
|
dataCurta,
|
|
483
|
-
celulas:
|
|
280
|
+
celulas: mesclarSlotsReservados(slotsReservaveis, 'rowspan')
|
|
484
281
|
});
|
|
485
282
|
});
|
|
486
283
|
|
|
@@ -504,10 +301,10 @@ const estruturaGradeHorario = computed(() => {
|
|
|
504
301
|
:usa-interceptador="props.usaInterceptador"
|
|
505
302
|
:ocultar-titulo-modal="props.ocultarTituloModal"
|
|
506
303
|
@before-save="$emit('before-save', $event)"
|
|
507
|
-
@salvo="
|
|
304
|
+
@salvo="dispararBuscaSemanal"
|
|
508
305
|
>
|
|
509
|
-
<template #
|
|
510
|
-
<slot name="
|
|
306
|
+
<template v-for="(_, nomeSlot) in $slots" :key="nomeSlot" #[nomeSlot]="slotProps">
|
|
307
|
+
<slot :name="nomeSlot" v-bind="slotProps ?? {}"></slot>
|
|
511
308
|
</template>
|
|
512
309
|
</ModalReserva>
|
|
513
310
|
|
|
@@ -515,7 +312,7 @@ const estruturaGradeHorario = computed(() => {
|
|
|
515
312
|
v-model="modalDetalhesAberto"
|
|
516
313
|
:reserva="reservaSelecionadaDetalhes"
|
|
517
314
|
:titulo-padrao="props.tituloPadrao"
|
|
518
|
-
@excluido="
|
|
315
|
+
@excluido="dispararBuscaSemanal"
|
|
519
316
|
/>
|
|
520
317
|
|
|
521
318
|
<div class="aviso-vazio" v-if="carregando">
|
|
@@ -654,246 +451,45 @@ const estruturaGradeHorario = computed(() => {
|
|
|
654
451
|
</template>
|
|
655
452
|
|
|
656
453
|
<style scoped>
|
|
657
|
-
.tabela-horario-container {
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
.
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
.
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
border-bottom: 1px solid var(--border-light);
|
|
680
|
-
font-size: 13.5px;
|
|
681
|
-
font-weight: 600;
|
|
682
|
-
color: var(--text-primary);
|
|
683
|
-
flex-shrink: 0;
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
.barra-topo-recurso select {
|
|
687
|
-
padding: 6px 12px;
|
|
688
|
-
border-radius: 6px;
|
|
689
|
-
border: 1px solid var(--border-light);
|
|
690
|
-
background: var(--bg-secondary);
|
|
691
|
-
color: var(--text-primary);
|
|
692
|
-
font-weight: 600;
|
|
693
|
-
cursor: pointer;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
.dica-arrasto {
|
|
697
|
-
margin-left: auto;
|
|
698
|
-
font-size: 12px;
|
|
699
|
-
font-weight: 500;
|
|
700
|
-
color: var(--text-secondary);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
.grade-scroll-wrapper {
|
|
704
|
-
flex-grow: 1;
|
|
705
|
-
overflow: auto;
|
|
706
|
-
padding: 16px;
|
|
707
|
-
background: var(--bg-primary);
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
.tabela-estilo-puc {
|
|
711
|
-
width: 100%;
|
|
712
|
-
border-collapse: collapse;
|
|
713
|
-
table-layout: fixed;
|
|
714
|
-
font-size: 12.5px;
|
|
715
|
-
text-align: center;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
.th-canto-vazio {
|
|
719
|
-
width: 36px;
|
|
720
|
-
min-width: 36px;
|
|
721
|
-
max-width: 36px;
|
|
722
|
-
background: transparent !important;
|
|
723
|
-
border: none !important;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
.th-col-horas {
|
|
727
|
-
width: 78px;
|
|
728
|
-
min-width: 78px;
|
|
729
|
-
max-width: 78px;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
.th-cabecalho-padrao {
|
|
733
|
-
background-color: #0066B3 !important;
|
|
734
|
-
color: #FFFFFF !important;
|
|
735
|
-
font-weight: 800;
|
|
736
|
-
padding: 8px 6px;
|
|
737
|
-
border: 1px solid #005291;
|
|
738
|
-
letter-spacing: 0.4px;
|
|
739
|
-
position: sticky;
|
|
740
|
-
top: 0;
|
|
741
|
-
z-index: 4;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
.th-cabecalho-especifico {
|
|
745
|
-
background-color: #808080 !important;
|
|
746
|
-
color: #FFFFFF !important;
|
|
747
|
-
font-weight: 800;
|
|
748
|
-
padding: 8px 6px;
|
|
749
|
-
border: 1px solid #666666;
|
|
750
|
-
letter-spacing: 0.4px;
|
|
751
|
-
position: sticky;
|
|
752
|
-
top: 0;
|
|
753
|
-
z-index: 4;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
.th-dia-nome {
|
|
757
|
-
font-size: 13px;
|
|
758
|
-
line-height: 1.2;
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
.th-dia-data {
|
|
762
|
-
font-size: 10.5px;
|
|
763
|
-
font-weight: 600;
|
|
764
|
-
opacity: 0.9;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
.td-turno-vertical {
|
|
768
|
-
width: 36px;
|
|
769
|
-
min-width: 36px;
|
|
770
|
-
max-width: 36px;
|
|
771
|
-
border: 1.5px solid;
|
|
772
|
-
padding: 4px 0;
|
|
773
|
-
vertical-align: middle;
|
|
774
|
-
text-align: center;
|
|
775
|
-
color: #111827 !important;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
.texto-turno-vertical {
|
|
779
|
-
writing-mode: vertical-rl;
|
|
780
|
-
transform: rotate(180deg);
|
|
781
|
-
font-weight: 800;
|
|
782
|
-
font-size: 13.5px;
|
|
783
|
-
letter-spacing: 1px;
|
|
784
|
-
text-transform: uppercase;
|
|
785
|
-
margin: 0 auto;
|
|
786
|
-
white-space: nowrap;
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
.td-celula-horas {
|
|
790
|
-
border: 1px solid;
|
|
791
|
-
padding: 4px 2px;
|
|
792
|
-
height: 40px;
|
|
793
|
-
color: #111827 !important;
|
|
794
|
-
font-weight: 700;
|
|
795
|
-
font-size: 11.5px;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
.horas-empilhadas {
|
|
799
|
-
display: flex;
|
|
800
|
-
flex-direction: column;
|
|
801
|
-
align-items: center;
|
|
802
|
-
justify-content: center;
|
|
803
|
-
line-height: 1.15;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
.td-slot-agendavel {
|
|
807
|
-
border: 1px solid;
|
|
808
|
-
padding: 4px 6px;
|
|
809
|
-
height: 40px;
|
|
810
|
-
cursor: pointer;
|
|
811
|
-
transition: filter 0.12s ease, box-shadow 0.12s ease;
|
|
812
|
-
vertical-align: middle;
|
|
813
|
-
color: #111827 !important;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
.td-slot-agendavel:hover {
|
|
817
|
-
filter: brightness(0.93);
|
|
818
|
-
box-shadow: inset 0 0 0 1.5px var(--color-brand);
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
.slot-selecionado-arrasto {
|
|
822
|
-
box-shadow: inset 0 0 0 2px var(--color-brand) !important;
|
|
823
|
-
}
|
|
824
|
-
|
|
454
|
+
.tabela-horario-container { width: 100%; height: 100%; background: var(--bg-primary); display: flex; flex-direction: column; user-select: none; }
|
|
455
|
+
.horario-conteudo { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
|
|
456
|
+
.barra-topo-recurso { display: flex; align-items: center; gap: 12px; padding: 10px 20px; background: var(--bg-primary); border-bottom: 1px solid var(--border-light); font-size: 13.5px; font-weight: 600; color: var(--text-primary); flex-shrink: 0; }
|
|
457
|
+
.barra-topo-recurso select { padding: 6px 12px; border-radius: 6px; border: 1px solid var(--border-light); background: var(--bg-secondary); color: var(--text-primary); font-weight: 600; cursor: pointer; }
|
|
458
|
+
.dica-arrasto { margin-left: auto; font-size: 12px; font-weight: 500; color: var(--text-secondary); }
|
|
459
|
+
.grade-scroll-wrapper { flex-grow: 1; overflow: auto; padding: 16px; background: var(--bg-primary); }
|
|
460
|
+
.tabela-estilo-puc { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: 12.5px; text-align: center; }
|
|
461
|
+
|
|
462
|
+
.th-canto-vazio { width: 36px; min-width: 36px; max-width: 36px; background: transparent !important; border: none !important; }
|
|
463
|
+
.th-col-horas { width: 78px; min-width: 78px; max-width: 78px; }
|
|
464
|
+
.th-cabecalho-padrao { background-color: #0066B3 !important; color: #FFFFFF !important; font-weight: 800; padding: 8px 6px; border: 1px solid #005291; letter-spacing: 0.4px; position: sticky; top: 0; z-index: 4; }
|
|
465
|
+
.th-cabecalho-especifico { background-color: #808080 !important; color: #FFFFFF !important; font-weight: 800; padding: 8px 6px; border: 1px solid #666666; letter-spacing: 0.4px; position: sticky; top: 0; z-index: 4; }
|
|
466
|
+
.th-dia-nome { font-size: 13px; line-height: 1.2; }
|
|
467
|
+
.th-dia-data { font-size: 10.5px; font-weight: 600; opacity: 0.9; }
|
|
468
|
+
|
|
469
|
+
.td-turno-vertical { width: 36px; min-width: 36px; max-width: 36px; border: 1.5px solid; padding: 4px 0; vertical-align: middle; text-align: center; color: #111827 !important; }
|
|
470
|
+
.texto-turno-vertical { writing-mode: vertical-rl; transform: rotate(180deg); font-weight: 800; font-size: 13.5px; letter-spacing: 1px; text-transform: uppercase; margin: 0 auto; white-space: nowrap; }
|
|
471
|
+
.td-celula-horas { border: 1px solid; padding: 4px 2px; height: 40px; color: #111827 !important; font-weight: 700; font-size: 11.5px; }
|
|
472
|
+
.horas-empilhadas { display: flex; flex-direction: column; align-items: center; justify-content: center; line-height: 1.15; }
|
|
473
|
+
.td-slot-agendavel { border: 1px solid; padding: 4px 6px; height: 40px; cursor: pointer; transition: filter 0.12s ease, box-shadow 0.12s ease; vertical-align: middle; color: #111827 !important; }
|
|
474
|
+
.td-slot-agendavel:hover { filter: brightness(0.93); box-shadow: inset 0 0 0 1.5px var(--color-brand); }
|
|
475
|
+
.slot-selecionado-arrasto { box-shadow: inset 0 0 0 2px var(--color-brand) !important; }
|
|
825
476
|
.slot-ocupado {
|
|
826
477
|
font-weight: 700;
|
|
827
|
-
/* Define uma cor de fundo cinza clara para destacar do resto do turno */
|
|
828
478
|
background-color: #e5e7eb !important;
|
|
829
|
-
|
|
830
|
-
background-image: repeating-linear-gradient(
|
|
831
|
-
45deg,
|
|
832
|
-
rgba(0, 0, 0, 0.06),
|
|
833
|
-
rgba(0, 0, 0, 0.06) 10px,
|
|
834
|
-
transparent 10px,
|
|
835
|
-
transparent 20px
|
|
836
|
-
) !important;
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
.conteudo-slot-horario {
|
|
840
|
-
display: flex;
|
|
841
|
-
align-items: center;
|
|
842
|
-
justify-content: center;
|
|
843
|
-
width: 100%;
|
|
844
|
-
height: 100%;
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
.titulo-reserva-grade {
|
|
848
|
-
font-size: 12px;
|
|
849
|
-
font-weight: 700;
|
|
850
|
-
color: #111827;
|
|
851
|
-
line-height: 1.25;
|
|
852
|
-
word-break: break-word;
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
.borda-subbloco {
|
|
856
|
-
border-bottom-width: 2px !important;
|
|
479
|
+
background-image: repeating-linear-gradient(45deg, rgba(0, 0, 0, 0.06), rgba(0, 0, 0, 0.06) 10px, transparent 10px, transparent 20px) !important;
|
|
857
480
|
}
|
|
858
481
|
|
|
859
|
-
.
|
|
860
|
-
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
.td-espaco-vazio-relativo {
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
.aviso-vazio {
|
|
870
|
-
display: flex;
|
|
871
|
-
flex-direction: column;
|
|
872
|
-
justify-content: center;
|
|
873
|
-
align-items: center;
|
|
874
|
-
flex-grow: 1;
|
|
875
|
-
color: var(--text-secondary);
|
|
876
|
-
font-weight: 500;
|
|
877
|
-
font-size: 1rem;
|
|
878
|
-
gap: 12px;
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
.spinner {
|
|
882
|
-
width: 32px;
|
|
883
|
-
height: 32px;
|
|
884
|
-
border: 3px solid var(--border-light);
|
|
885
|
-
border-top-color: var(--color-brand);
|
|
886
|
-
border-radius: 50%;
|
|
887
|
-
animation: spin 1s linear infinite;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
@keyframes spin {
|
|
891
|
-
to { transform: rotate(360deg); }
|
|
892
|
-
}
|
|
482
|
+
.conteudo-slot-horario { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; }
|
|
483
|
+
.titulo-reserva-grade { font-size: 12px; font-weight: 700; color: #111827; line-height: 1.25; word-break: break-word; }
|
|
484
|
+
.borda-subbloco { border-bottom-width: 2px !important; }
|
|
485
|
+
.borda-bloco-principal { border-bottom-width: 2.5px !important; }
|
|
486
|
+
.td-espaco-vazio-relativo { background: transparent !important; border: none !important; pointer-events: none; }
|
|
487
|
+
.aviso-vazio { display: flex; flex-direction: column; justify-content: center; align-items: center; flex-grow: 1; color: var(--text-secondary); font-weight: 500; font-size: 1rem; gap: 12px; }
|
|
488
|
+
.spinner { width: 32px; height: 32px; border: 3px solid var(--border-light); border-top-color: var(--color-brand); border-radius: 50%; animation: spin 1s linear infinite; }
|
|
489
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
893
490
|
</style>
|
|
894
491
|
|
|
895
492
|
<style>
|
|
896
|
-
/* CSS injetado globalmente para os blocos da tabela horário escurecerem o texto no dark theme */
|
|
897
493
|
[data-bs-theme="dark"] .td-turno-vertical,
|
|
898
494
|
[data-bs-theme="dark"] .texto-turno-vertical,
|
|
899
495
|
[data-bs-theme="dark"] .td-celula-horas,
|
|
@@ -905,12 +501,6 @@ const estruturaGradeHorario = computed(() => {
|
|
|
905
501
|
|
|
906
502
|
[data-bs-theme="dark"] .slot-ocupado {
|
|
907
503
|
background-color: #374151 !important;
|
|
908
|
-
background-image: repeating-linear-gradient(
|
|
909
|
-
45deg,
|
|
910
|
-
rgba(255, 255, 255, 0.03),
|
|
911
|
-
rgba(255, 255, 255, 0.03) 10px,
|
|
912
|
-
transparent 10px,
|
|
913
|
-
transparent 20px
|
|
914
|
-
) !important;
|
|
504
|
+
background-image: repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.03) 10px, transparent 10px, transparent 20px) !important;
|
|
915
505
|
}
|
|
916
506
|
</style>
|