versao feriado finalizada

This commit is contained in:
ka-lucas 2025-04-29 22:10:43 -03:00
parent e0aef3cb8f
commit 326276d4a4
3 changed files with 304 additions and 195 deletions

View File

@ -1,35 +1,56 @@
<template> <template>
<v-dialog v-model="dialogVisible" max-width="500px"> <v-dialog v-model="dialogVisible" max-width="600px">
<v-card class="pa-6 rounded-xl elevation-2 card-modal"> <v-card class="pa-6 rounded-xl elevation-2 card-modal">
<v-card-title class="text-h5 font-weight-bold pb-0"> <v-card-title class="pb-1">
<span class="text-h5">Editar Feriado</span> <span class="modal-title">Editar Feriado</span>
</v-card-title> </v-card-title>
<v-card-subtitle class="modal-subtitle mb-6">
Altere os dados desejados e clique em salvar.
</v-card-subtitle>
<v-card-text> <v-card-text>
<v-container> <v-container>
<v-row> <v-row>
<v-col cols="12"> <v-col cols="12">
<v-text-field v-model="localHoliday.name" label="Feriado *" clearable required /> <div class="form-group">
<label class="form-label">Nome do Feriado</label>
<v-text-field v-model="localHoliday.name" variant="outlined" hide-details="auto" clearable />
</div>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<v-text-field v-model="localHoliday.type" label="Tipo do Feriado *" clearable required /> <div class="form-group">
<label class="form-label">Data</label>
<v-text-field v-model="localHoliday.date" type="date" variant="outlined" hide-details="auto" :rules="[rules.required]" clearable />
</div>
</v-col> </v-col>
<v-col cols="12">
<v-text-field v-model="localHoliday.date" label="Data do feriado *" type="date" clearable <v-col cols="12" md="6">
:rules="[v => !!v || 'Data é obrigatória']" required /> <div class="form-group">
<label class="form-label">Tipo</label>
<v-select v-model="localHoliday.type" :items="['Nacional', 'Estadual', 'Ponto Facultativo', 'Outro']" variant="outlined" hide-details="auto" />
</div>
</v-col> </v-col>
<v-col cols="12">
<v-text-field v-model="localHoliday.municipio" label="Municipio" type="municipio" clearable /> <v-col cols="12" md="6">
<div class="form-group">
<label class="form-label">Adicional Hora Extra</label>
<v-select v-model="localHoliday.adicional_he" :items="['0%', '25%','50%','75%', '100%']" variant="outlined" hide-details="auto" />
</div>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<v-text-field v-model="localHoliday.estado" label="Estado" type="estado" clearable /> <v-checkbox v-model="localHoliday.recorrente" label="Feriado recorrente (repete todos os anos)" color="primary" />
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer></v-spacer> <v-spacer></v-spacer>
<v-btn text color="blue-darken-1" @click="$emit('cancel')">Cancelar</v-btn> <v-btn text class="cancel-btn" @click="$emit('cancel')">Cancelar</v-btn>
<v-btn text class="text-white salvar-btn" @click="handleSave" :loading="holidayStore.loading"> <v-btn class="add-btn" @click="handleSave" :loading="holidayStore.loading">
Salvar Salvar
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
@ -48,153 +69,86 @@ const props = defineProps({
}); });
const emit = defineEmits(['update:modalValue', 'save']); const emit = defineEmits(['update:modalValue', 'save']);
const rules = {
required: (value) => !!value || 'Campo obrigatório'
};
const holidayStore = useHolidayStore(); const holidayStore = useHolidayStore();
const authStore = useAuthStore(); const authStore = useAuthStore();
// Controle local da visibilidade do diálogo
const dialogVisible = ref(false); const dialogVisible = ref(false);
// Estado local do feriado
const localHoliday = ref({ const localHoliday = ref({
id: null,
name: '', name: '',
type: '', type: '',
date: '', date: '',
estado: '', recorrente: true,
municipio: '', adicional_he: 0,
parent_id: authStore.userId, parent_id: authStore.userId,
service_instance_id: authStore.service_instance_id || 2 service_instance_id: authStore.service_instance_id || 2
}); });
// Carregar dados do feriado para edição
const loadHolidayData = async () => { const loadHolidayData = async () => {
console.log('Carregando dados do feriado ID:', props.holiday?.id);
try { try {
if (!props.holiday || !props.holiday.id) { if (!props.holiday || !props.holiday.id) return;
console.error('Feriado não encontrado ou sem ID');
return;
}
// Carregar dados completos do feriado
const holidayData = await holidayStore.fetchHolidayById(props.holiday.id); const holidayData = await holidayStore.fetchHolidayById(props.holiday.id);
if (holidayData) { if (holidayData) {
console.log('Dados carregados com sucesso:', holidayData);
// Preencher dados do formulário
localHoliday.value = { localHoliday.value = {
id: holidayData.id, id: holidayData.id,
name: holidayData.name || '', name: holidayData.name || '',
type: holidayData.type || '', type: holidayData.type || '',
date: holidayData.date date: holidayData.date ? new Date(holidayData.date).toISOString().split('T')[0] : '',
? new Date(holidayData.date).toISOString().split('T')[0] recorrente: holidayData.recorrente ?? true,
: '', adicional_he: Number(holidayData.adicional_he) || 0,
estado: holidayData.estado || '', parent_id: holidayData.parent_id || authStore.userId,
municipio: holidayData.municipio || '', service_instance_id: holidayData.service_instance_id || authStore.service_instance_id
parent_id: authStore.userId,
service_instance_id: authStore.service_instance_id || 2,
adicional_he: Number(localHoliday.value.adicional_he), // Garante tipo numérico
recorrente: localHoliday.value.recorrente
}; };
} else if (props.holiday) {
// Fallback para os dados recebidos via props
console.log('Usando dados de holiday props como fallback');
localHoliday.value = {
...props.holiday,
date: props.holiday.date
? new Date(props.holiday.date).toISOString().split('T')[0]
: ''
};
} else {
console.error('Não foi possível carregar dados do feriado');
} }
} catch (error) { } catch (error) {
console.error('Erro ao carregar dados do feriado:', error); console.error('Erro ao carregar feriado:', error);
// Tenta usar os dados do props como fallback
if (props.holiday) {
localHoliday.value = {
...props.holiday,
date: props.holiday.date
? new Date(props.holiday.date).toISOString().split('T')[0]
: ''
};
}
} }
}; };
// Observar mudanças no modalValue
watch(() => props.modalValue, async (newVal) => { watch(() => props.modalValue, async (newVal) => {
console.log('EditModal - modalValue mudou para:', newVal);
dialogVisible.value = newVal; dialogVisible.value = newVal;
if (newVal) await loadHolidayData();
if (newVal) {
// Espera até o holiday ter ID antes de tentar carregar
const waitForHoliday = async () => {
const maxTries = 10;
let tries = 0;
while (!props.holiday?.id && tries < maxTries) {
await new Promise(resolve => setTimeout(resolve, 100)); // espera 100ms
tries++;
}
if (props.holiday?.id) {
console.log('Feriado encontrado após espera:', props.holiday.id);
loadHolidayData();
} else {
console.warn('Holiday não disponível após espera.');
}
};
waitForHoliday();
}
}); });
// Observar mudanças no objeto holiday
watch(() => props.holiday, (newVal) => { watch(() => props.holiday, (newVal) => {
console.log('Props holiday mudou:', newVal); if (newVal && newVal.id) loadHolidayData();
if (newVal && newVal.id) {
loadHolidayData();
}
}, { deep: true }); }, { deep: true });
// Atualizar o prop quando o diálogo local mudar
watch(dialogVisible, (newVal) => { watch(dialogVisible, (newVal) => {
if (props.modalValue !== newVal) { if (props.modalValue !== newVal) emit('update:modalValue', newVal);
emit('update:modalValue', newVal);
}
}); });
// Fechar o modal
const closeModal = () => { const closeModal = () => {
console.log('Fechando modal de edição');
dialogVisible.value = false; dialogVisible.value = false;
emit('update:modalValue', false); emit('update:modalValue', false);
}; };
// Salvar as alterações no feriado
const handleSave = async () => { const handleSave = async () => {
try { try {
const holidayData = { ...localHoliday.value }; const data = {
console.log('Atualizando feriado:', holidayData); ...localHoliday.value,
adicional_he: parseInt(localHoliday.value.adicional_he) || 0,
await holidayStore.updateHoliday(holidayData.id, holidayData); parent_id: authStore.userId,
service_instance_id: authStore.service_instance_id
emit('save', holidayData); };
await holidayStore.updateHoliday(data.id, data);
emit('save', data);
closeModal(); closeModal();
} catch (error) { } catch (error) {
console.error('Erro ao atualizar feriado:', error); console.error('Erro ao salvar feriado:', error);
} }
}; };
// Inicialização do componente
onMounted(() => { onMounted(() => {
console.log('Modal de edição montado - holiday:', props.holiday);
dialogVisible.value = props.modalValue; dialogVisible.value = props.modalValue;
if (props.modalValue && props.holiday?.id) loadHolidayData();
if (props.modalValue && props.holiday && props.holiday.id) {
loadHolidayData();
}
}); });
</script> </script>
@ -202,6 +156,11 @@ onMounted(() => {
.card-modal { .card-modal {
background-color: #ffffff; background-color: #ffffff;
border-radius: 16px; border-radius: 16px;
max-height: 70 vh;
overflow-y: auto;
}
.v-card-text {
padding-bottom: 0px !important; /* ou 0px */
} }
.salvar-btn { .salvar-btn {
@ -210,4 +169,59 @@ onMounted(() => {
text-transform: none; text-transform: none;
font-weight: 500; font-weight: 500;
} }
.modal-title {
font-size: 25px;
font-weight: 600;
color: #1e293b;
}
.modal-subtitle {
font-size: 14px;
color: #64748b;
}
.form-group {
display: flex;
flex-direction: column;
margin-bottom: 16px;
}
.form-label {
display: block;
font-size: 18px;
font-weight: 500;
color: #1e293b;
margin-bottom: 4px;
margin-left: 4px;
}
.card-modal {
background-color: #ffffff;
border-radius: 16px;
max-height: 70 vh;
overflow-y: auto;
}
.cancel-btn {
border-radius: 8px;
font-weight: 500;
padding: 0 20px;
height: 44px;
color: #1e293b;
border: 1px solid #d1d5db;
background-color: #fff;
}
/* Borda azul ao passar o mouse */
::v-deep(.v-field.v-field--variant-outlined:hover) {
border-color: #2eb6ff79 !important;
box-shadow: 0 0 0 1px #54d2ff6b !important;
}
.add-btn {
border-radius: 8px;
font-weight: 500;
padding: 0 20px;
height: 44px;
color: white;
background-color: #2563eb;
}
</style> </style>

View File

@ -1,68 +1,56 @@
<template> <template>
<v-dialog :model-value="modalValue" @update:model-value="updateModalValue" max-width="500px"> <v-dialog :model-value="modalValue" @update:model-value="updateModalValue" max-width="600px">
<v-form ref="form" v-model="formValid"> <v-form ref="form" v-model="formValid">
<v-card class="pa-6 rounded-xl elevation-2 card-modal"> <v-card class="pa-6 rounded-xl elevation-2 card-modal">
<v-card-title class="text-h5 font-weight-bold pb-0"> <v-card-title class="pb-1">
<span class="text-h5">Adicionar Novo Feriado</span> <span class="modal-title">Adicionar Feriado</span>
</v-card-title> </v-card-title>
<v-card-subtitle class="modal-subtitle mb-6">
Gerencie os feriados e dias especiais do sistema
</v-card-subtitle>
<v-card-text> <v-card-text>
<v-container> <v-container>
<v-row> <v-row>
<v-col cols="12"> <v-col cols="12">
<v-text-field v-model="localHoliday.name" label="Feriado *" clearable :rules="[rules.required]" required /> <div class="form-group">
<label class="form-label">Nome do Feriado</label>
<v-text-field v-model="localHoliday.name" variant="outlined" hide-details="auto" :rules="[rules.required]" clearable />
</div>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<v-text-field v-model="localHoliday.type" label="Tipo do Feriado *" clearable :rules="[rules.required]" required /> <div class="form-group">
<label class="form-label">Data</label>
<v-text-field v-model="localHoliday.date" type="date" variant="outlined" hide-details="auto" :rules="[rules.required]" clearable />
</div>
</v-col> </v-col>
<v-col cols="12">
<v-text-field v-model="localHoliday.date" label="Data do feriado *" type="date" clearable <v-col cols="12" md="6">
:rules="[v => !!v || 'Data é obrigatória']" required /> <div class="form-group">
<label class="form-label">Tipo</label>
<v-select v-model="localHoliday.type" :items="['Nacional', 'Estadual', 'Ponto Facultativo', 'Outro']" variant="outlined" hide-details="auto" />
</div>
</v-col> </v-col>
<v-col cols="12">
<v-text-field v-model="localHoliday.municipio" label="Municipio" type="municipio" /> <v-col cols="12" md="6">
<div class="form-group">
<label class="form-label">Adicional Hora Extra</label>
<v-select v-model="localHoliday.adicional_he" :items="['0%', '25%','50%','75%', '100%']" variant="outlined" hide-details="auto" />
</div>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
<v-text-field v-model="localHoliday.estado" label="Estado" type="estado" clearable /> <v-checkbox v-model="localHoliday.recorrente" label="Feriado recorrente (repete todos os anos)" color="primary" />
</v-col>
<v-col cols="12">
<v-switch
v-model="localHoliday.recorrente"
label="Feriado Recorrente (Anual)"
color="primary"
:true-value="true"
:false-value="false"
/>
</v-col>
<v-col cols="12">
<v-text-field
v-model.number="localHoliday.adicional_he"
label="Adicional de HE (%) *"
type="number"
min="0"
max="100"
:rules="[
v => v !== null && v !== undefined || 'Campo obrigatório',
v => (v >= 0 && v <= 100) || 'Valor entre 0 e 100'
]"
required
@input="form?.validate()"
/>
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer></v-spacer> <v-btn text
<v-btn text color="blue-darken-1" @click="$emit('cancel')">Cancelar</v-btn> class="cancel-btn" @click="$emit('cancel')">Cancelar</v-btn>
<v-btn <v-btn color="primary" @click="handleSave" :disabled="!formValid" class="add-btn">Adicionar</v-btn>
text
class="text-white salvar-btn"
:disabled="!formValid"
@click="handleSave"
:loading="holidayStore.loading"
>
Salvar
</v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-form> </v-form>
@ -146,26 +134,24 @@ const closeModal = () => {
}; };
// Salvar o novo feriado // Salvar o novo feriado
const handleSave = async (event) => { const handleSave = async () => {
event.preventDefault();
// Valida apenas o formulário (sem verificações extras)
const { valid } = await form.value.validate(); const { valid } = await form.value.validate();
if (!valid) {
if (valid) { // Só depende da validação do formulário console.warn('Formulário inválido');
try { return;
await holidayStore.createHoliday({ }
...localHoliday.value,
parent_id: authStore.userId, try {
service_instance_id: authStore.service_instance_id const data = {
}); ...localHoliday.value,
adicional_he: parseInt(localHoliday.value.adicional_he) || 0,
emit('save'); parent_id: authStore.userId,
emit('update:modalValue', false); service_instance_id: authStore.service_instance_id
resetLocalHoliday(); };
} catch (error) {
console.error('Erro ao criar feriado:', error); emit('save', data); // Quem chama decide quando fechar o modal
} } catch (error) {
console.error('Erro ao salvar feriado:', error);
} }
}; };
@ -179,9 +165,73 @@ onMounted(() => {
</script> </script>
<style scoped> <style scoped>
.v-card-text {
padding-bottom: 0px !important; /* ou 0px */
}
.form-group {
display: flex;
flex-direction: column;
margin-bottom: 16px;
}
.form-label {
display: block;
font-size: 18px;
font-weight: 500;
color: #1e293b;
margin-bottom: 4px;
margin-left: 4px;
}
.cancel-btn {
border-radius: 8px;
font-weight: 500;
padding: 0 20px;
height: 44px;
color: #1e293b;
border: 1px solid #d1d5db;
background-color: #fff;
}
.add-btn {
border-radius: 8px;
font-weight: 500;
padding: 0 20px ;
height: 44px;
color: white !important;
background-color: #2563eb;
}
.v-card-title,
.v-card-subtitle {
padding-left: 24px !important;
padding-right: 24px !important;
}
.modal-title {
font-size: 25px;
font-weight: 600;
color: #1e293b;
}
.modal-subtitle {
font-size: 14px;
color: #64748b; /* Cinza mais escuro */
}
/* Borda azul ao passar o mouse */
::v-deep(.v-field.v-field--variant-outlined:hover) {
border-color: #2eb6ff79 !important;
box-shadow: 0 0 0 1px #54d2ff6b !important;
}
.card-modal { .card-modal {
background-color: #ffffff; background-color: #ffffff;
border-radius: 16px; border-radius: 16px;
max-height: 70 vh;
overflow-y: auto;
} }
.salvar-btn { .salvar-btn {
@ -190,4 +240,5 @@ onMounted(() => {
text-transform: none; text-transform: none;
font-weight: 500; font-weight: 500;
} }
</style> </style>

View File

@ -59,11 +59,12 @@
<!-- Slot para campo tipo (adicional) --> <!-- Slot para campo tipo (adicional) -->
<template v-slot:item.type="{ item }"> <template v-slot:item.type="{ item }">
<v-chip <v-chip
:color="getTypeColor(item.type)" class="holiday-type-chip"
:class="getTypeClass(item.type)"
text-color="white" text-color="white"
size="small" size="small"
class="type-chip" label
> >
{{ item.type || 'Nacional' }} {{ item.type || 'Nacional' }}
</v-chip> </v-chip>
</template> </template>
@ -119,7 +120,6 @@
@save="SubmitFormCreate" @save="SubmitFormCreate"
@cancel="closeCreateModal" @cancel="closeCreateModal"
/> />
<!-- Diálogo de Confirmação de Exclusão --> <!-- Diálogo de Confirmação de Exclusão -->
<v-dialog v-model="dialogs.delete" max-width="400px"> <v-dialog v-model="dialogs.delete" max-width="400px">
<v-card> <v-card>
@ -254,14 +254,19 @@ export default {
}); });
// Método para determinar a cor do chip baseado no tipo // Método para determinar a cor do chip baseado no tipo
const getTypeColor = (type) => { const getTypeClass = (type) => {
const typeColors = { switch (type?.toLowerCase()) {
'Nacional': 'primary', case 'nacional':
'Estadual': 'success', return 'chip-nacional';
'Municipal': 'info', case 'estadual':
'Ponto Facultativo': 'warning' return 'chip-estadual';
}; case 'ponto facultativo':
return typeColors[type] || 'primary'; return 'chip-facultativo';
case 'municipal':
return 'chip-municipal';
default:
return 'chip-outro';
}
}; };
// Computed properties // Computed properties
@ -409,15 +414,32 @@ export default {
dialogs.value.holiday = false; dialogs.value.holiday = false;
} }
}; };
const loadHolidays = async () => {
try {
loading.value.holidays = true;
const holidaysData = await holidayStore.fetchHolidays();
holidays.value = holidaysData.map(holiday => ({
...holiday,
type: holiday.type || 'Nacional'
}));
} catch (error) {
showNotification('Erro ao carregar dados da API', 'error');
console.error('Error fetching feriados:', error);
} finally {
loading.value.holidays = false;
}
};
const SubmitFormCreate = async (holidayData) => { const SubmitFormCreate = async (holidayData) => {
loading.value.submit = true; loading.value.submit = true;
try { try {
const newItem = await holidayStore.createHoliday(holidayData); await holidayStore.createHoliday(holidayData);
holidays.value.push(newItem); await loadHolidays(); // 🔁 Recarrega a lista a partir da fonte oficial
showNotification('Feriado cadastrado com sucesso!'); showNotification('Feriado cadastrado com sucesso!');
} catch (error) { } catch (error) {
console.error('Error submitting form:', error); console.error('Error submitting form:', error);
showNotification('Erro ao cadastrar feriado', 'error');
} finally { } finally {
loading.value.submit = false; loading.value.submit = false;
dialogs.value.createholiday = false; dialogs.value.createholiday = false;
@ -444,6 +466,9 @@ export default {
loading.value.holidays = false; loading.value.holidays = false;
} }
}); });
onMounted(() => {
loadHolidays();
});
return { return {
// States // States
@ -464,7 +489,7 @@ export default {
filteredholidays, filteredholidays,
// Methods // Methods
getTypeColor, getTypeClass,
confirmDelete, confirmDelete,
submitForm, submitForm,
SubmitFormCreate, SubmitFormCreate,
@ -637,19 +662,38 @@ export default {
} }
/* Estilos para os diferentes tipos de feriados */ /* Estilos para os diferentes tipos de feriados */
.holiday-type-nacional { .holiday-type-chip {
background-color: #1976d2; font-size: 13px;
font-weight: 500;
border-radius: 999px;
padding: 4px 12px;
text-transform: none;
} }
.holiday-type-estadual { /* Cores por tipo */
background-color: #2e7d32; .chip-nacional {
background-color: #2563eb; /* azul */
color: white;
} }
.holiday-type-municipal { .chip-estadual {
background-color: #0288d1; background-color: #22c55e; /* verde */
color: white;
} }
.holiday-type-facultativo { .chip-facultativo {
background-color: #ff9800; background-color: #f59e0b; /* laranja */
color: white;
} }
.chip-municipal {
background-color: #0d9488; /* teal */
color: white;
}
.chip-outro {
background-color: #6b7280; /* cinza */
color: white;
}
</style> </style>