354 lines
12 KiB
Vue
354 lines
12 KiB
Vue
<script setup>
|
|
import { Qalendar } from 'qalendar';
|
|
import {eventStatusLoad} from '../helpers/status';
|
|
import {getDateTimeFormat, formatOnlyDate} from '../helpers/date_formats';
|
|
import { onMounted, ref } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import useCalendar from '../composables/useCalendar';
|
|
import CustomPopup from '../components/CustomPopup.vue';
|
|
import { watch } from 'vue';
|
|
import { useAuthStore } from '../stores/auth';
|
|
|
|
const events = ref([]);
|
|
|
|
const { t, locale } = useI18n()
|
|
const auth = useAuthStore();
|
|
const { getCalendarDate, loading, loads } = useCalendar()
|
|
const filter = ref({value: 'my', label: t('calendar.my')})
|
|
const startDate = ref('');
|
|
const endDate = ref('');
|
|
const optionsFilter = ref([])
|
|
const openPopup = ref(false);
|
|
const jobRole = auth.user.job_role;
|
|
const roleCheck = 'store';
|
|
|
|
const config = {
|
|
week: {
|
|
startsOn: 'monday',
|
|
// Takes the values 5 or 7.
|
|
nDays: 7,
|
|
// Scroll to a certain hour on mounting a week. Takes any value from 0 to 23.
|
|
// This option is not compatible with the 'dayBoundaries'-option, and will simply be ignored if custom day boundaries are set.
|
|
scrollToHour: 0,
|
|
},
|
|
month: {
|
|
showTrailingAndLeadingDates: false
|
|
},
|
|
style: {
|
|
fontFamily: 'Nunito',
|
|
colorSchemes: {
|
|
published: {
|
|
color: "#fff",
|
|
backgroundColor: "#A9B0B2",
|
|
},
|
|
loading: {
|
|
color: "#fff",
|
|
backgroundColor: "#F44336",
|
|
},
|
|
transit: {
|
|
color: "#ffd22b",
|
|
backgroundColor: "#ffd22b",
|
|
},
|
|
downloading: {
|
|
color: "#fff",
|
|
backgroundColor: "#428502",
|
|
},
|
|
delivered: {
|
|
color: "#fff",
|
|
backgroundColor: "#1B70AF",
|
|
},
|
|
},
|
|
},
|
|
eventDialog:{
|
|
isCustom: true
|
|
},
|
|
defaultMode: 'month',
|
|
isSilent: true,
|
|
// showCurrentTime: true, // Display a line indicating the current time
|
|
}
|
|
|
|
onMounted( async() => {
|
|
const currentDate = new Date();
|
|
const year = currentDate.getFullYear();
|
|
const month = currentDate.getMonth();
|
|
|
|
const startDateTemp = new Date(year, month, 1);
|
|
const endDateTemp = new Date(year, month + 1, 0);
|
|
startDate.value = formatOnlyDate(startDateTemp);
|
|
endDate.value = formatOnlyDate(endDateTemp);
|
|
await getCalendarDate(startDate.value, endDate.value, 0);
|
|
optionsFilter.value = [
|
|
{value: 'general',label: t('calendar.general')},
|
|
{value: 'my',label: t('calendar.my')},
|
|
]
|
|
mapLoadsToEvents()
|
|
});
|
|
|
|
const redirectToTracking = (id) => {
|
|
window.open('/publico/tracking/' + id, '_blank');
|
|
}
|
|
|
|
watch(locale, () => {
|
|
optionsFilter.value = [
|
|
{value: 'general',label: t('calendar.general')},
|
|
{value: 'my',label: t('calendar.my')},
|
|
]
|
|
})
|
|
|
|
const handleUpdateMode = (ev) => {
|
|
}
|
|
|
|
const handleClickDate = (ev) => {
|
|
}
|
|
|
|
const handleUpdatedPeriod = async(ev) => {
|
|
const global = filter.value.value === 'general' ? 1 : 0;
|
|
let start = new Date(ev.start);
|
|
let end = new Date(ev.end);
|
|
startDate.value = formatOnlyDate(ev.start)
|
|
endDate.value = formatOnlyDate(ev.end)
|
|
if( (start.getMonth() === end.getMonth()) && (start.getDate() === end.getDate()) ) {
|
|
startDate.value = formatOnlyDate(start) + ' 00:00:00';
|
|
endDate.value = formatOnlyDate(end) + ' 23:59:59';
|
|
}
|
|
await getCalendarDate(startDate.value, endDate.value, global);
|
|
mapLoadsToEvents()
|
|
}
|
|
|
|
const mapLoadsToEvents = () => {
|
|
events.value = [];
|
|
loads.value.forEach((e) => {
|
|
const indicator = eventStatusLoad(e.load_status);
|
|
const dateStart = getDateTimeFormat(e.est_loading_date, 0);
|
|
const dateEnd = getDateTimeFormat(e.est_loading_date, 1);
|
|
events.value.push({
|
|
id: e._id,
|
|
title: e.shipment_code.toUpperCase(),
|
|
with: indicator.status,
|
|
start: e.est_loading_date,
|
|
end: e.est_unloading_date,
|
|
description: indicator.load_status,
|
|
colorScheme: e.load_status?.toLowerCase(),
|
|
color: indicator.color,
|
|
time: {
|
|
start: dateStart,
|
|
end: dateEnd
|
|
},
|
|
})
|
|
|
|
});
|
|
|
|
}
|
|
|
|
const closePopup = () => {
|
|
openPopup.value = false
|
|
}
|
|
|
|
const selectedFilter = async (type) => {
|
|
filter.value = type
|
|
const global = type.value === 'general' ? 1 : 0;
|
|
openPopup.value = false
|
|
await getCalendarDate(startDate.value, endDate.value, global);
|
|
mapLoadsToEvents()
|
|
}
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
v-if="openPopup"
|
|
>
|
|
<CustomPopup
|
|
:options="optionsFilter"
|
|
:value="filter"
|
|
@change-value="selectedFilter"
|
|
@close-popup="closePopup"
|
|
selected-color="#e3a11e"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<h2 class="title mb-4">{{ t('calendar.title') }}</h2>
|
|
<div class="box-indicators">
|
|
<h2>{{ t('calendar.helpText') }}</h2>
|
|
<div class="header">
|
|
<div class="indicators">
|
|
<span><i class="fa-solid fa-circle" style="color: #A9B0B2"></i> {{ t('global.published') }}</span>
|
|
<span><i class="fa-solid fa-circle" style="color: #F44336"></i> {{ t('global.loading') }}</span>
|
|
<span v-if="jobRole !== roleCheck"><i class="fa-solid fa-circle" style="color: #ffd22b"></i> {{ t('global.transit') }}</span>
|
|
<span v-if="jobRole !== roleCheck"><i class="fa-solid fa-circle" style="color: #428502"></i> {{ t('global.downloading') }}</span>
|
|
<span v-if="jobRole !== roleCheck"><i class="fa-solid fa-circle" style="color: #1B70AF"></i> {{ t('global.delivered') }}</span>
|
|
</div>
|
|
<div class="box-filter"
|
|
v-if="jobRole !== roleCheck"
|
|
@click="openPopup = true"
|
|
>
|
|
<span class="clear-sm" v-if="filter === null">{{ t('directory.directory') }}</span>
|
|
<span class="clear-sm" v-else>{{filter.label}}</span>
|
|
<i class="fa-solid fa-filter"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="calendar">
|
|
<!-- <div class="calendar is-light-mode"> -->
|
|
<Qalendar
|
|
:events="events"
|
|
:config="config"
|
|
@updated-mode="handleUpdateMode"
|
|
@updated-period="handleUpdatedPeriod"
|
|
@datetime-was-clicked="handleClickDate"
|
|
:is-loading="loading"
|
|
>
|
|
<!-- <template #dayCell="{dayData}">
|
|
<p v-if="dayData.events.length > 0" class="text-center mt-1"> {{ dayData.events[0]?.start?.substring(8, 10) }}</p>
|
|
<div v-for="(item, index) in dayData.events" :key="index">
|
|
<div
|
|
style="cursor: pointer;"
|
|
@click="event => $emit('event-was-clicked', event)"
|
|
>
|
|
<i
|
|
style="font-size: 12px;"
|
|
:style="{color: item.color}"
|
|
class="fa-solid fa-circle"></i>
|
|
<span style="margin-left: 8px; font-size: 12px;">{{ item.title }}</span>
|
|
</div>
|
|
</div>
|
|
</template> -->
|
|
<template #eventDialog="props">
|
|
<div v-if="props.eventDialogData && props.eventDialogData.title" class="event-modal">
|
|
<h2>Información del status de la carga</h2>
|
|
<!-- <p>Código de carga: <span :style="{color: props.eventDialogData.color}">{{props.eventDialogData.title}}</span></p> -->
|
|
<p>
|
|
Código de carga:
|
|
<span> {{props.eventDialogData.title}}</span>
|
|
<span
|
|
class="tracking-icon"
|
|
:style="{color: props.eventDialogData.color}"
|
|
@click="redirectToTracking(props.eventDialogData.id)">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-geo-alt-fill" viewBox="0 0 16 16">
|
|
<path d="M8 16s6-5.686 6-10A6 6 0 0 0 2 6c0 4.314 6 10 6 10zm0-7a3 3 0 1 1 0-6 3 3 0 0 1 0 6z"></path>
|
|
</svg>
|
|
</span>
|
|
</p>
|
|
<p>Estatus de la carga: <i :style="{color: props.eventDialogData.color}" class="fa-solid fa-circle"></i> <span>{{props.eventDialogData.with}}</span></p>
|
|
|
|
<button class="btn btn-dark" @click="props.closeEventDialog">
|
|
Cerrar
|
|
</button>
|
|
</div>
|
|
</template>
|
|
</Qalendar>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style src="qalendar/dist/style.css"></style>
|
|
|
|
<style lang="scss" scoped>
|
|
.calendar {
|
|
height: calc(100vh - 220px);
|
|
}
|
|
|
|
.tracking-icon {
|
|
cursor: pointer;
|
|
color: #f2a23f;
|
|
}
|
|
|
|
.tracking-icon svg{
|
|
height: 30px;
|
|
}
|
|
.tracking-icon:hover {
|
|
color: #ddb380;;
|
|
height: 150px;
|
|
}
|
|
|
|
.tracking-icon svg:hover{
|
|
height: 33px;
|
|
}
|
|
|
|
.event-modal {
|
|
padding: 12px 20px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.event-modal h2 {
|
|
color: rgb(208, 182, 182);
|
|
font-family: sans-serif;
|
|
font-size: 1.4rem;
|
|
font-weight: 900;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.event-modal p {
|
|
color: rgb(208, 182, 182);
|
|
font-family: sans-serif;
|
|
font-size: 1rem;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.box-indicators {
|
|
display: flex;
|
|
justify-content: end;
|
|
flex-direction: column;
|
|
padding: 12px 10px;
|
|
}
|
|
|
|
.box-indicators h2 {
|
|
display: flex;
|
|
justify-content: start;
|
|
font-weight: 600;
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
width: 100%;
|
|
gap: 1rem;
|
|
align-items: center;
|
|
// flex-wrap: wrap;
|
|
}
|
|
|
|
.box-filter {
|
|
padding: 12px 8px;
|
|
background-color: #FFF;
|
|
border-radius: 5px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
border: 1px rgb(186, 175, 175) solid;
|
|
gap: 1rem;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.filters {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
// flex-wrap: wrap;
|
|
flex: 1;
|
|
justify-content: end;
|
|
}
|
|
|
|
.indicators {
|
|
display: flex;
|
|
// flex: 1;
|
|
justify-content: end;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
gap: 0.7rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.calendar {
|
|
height: calc(100vh - 280px);
|
|
}
|
|
|
|
.header {
|
|
align-items: start;
|
|
}
|
|
}
|
|
|
|
</style> |