add: calendar view & calculator view
This commit is contained in:
75
package-lock.json
generated
75
package-lock.json
generated
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
|
"qalendar": "^3.7.0",
|
||||||
"sass": "^1.69.5",
|
"sass": "^1.69.5",
|
||||||
"sweetalert2": "^11.10.1",
|
"sweetalert2": "^11.10.1",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
@@ -385,6 +386,60 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fortawesome/fontawesome-common-types": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/fontawesome-svg-core": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/free-regular-svg-icons": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/free-solid-svg-icons": {
|
||||||
|
"version": "6.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz",
|
||||||
|
"integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fortawesome/vue-fontawesome": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-isZZ4+utQH9qg9cWxWYHQ9GwI3r5FeO7GnmzKYV+gbjxcptQhh+F99iZXi1Y9AvFUEgy8kRpAdvDlbb3drWFrw==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
|
||||||
|
"vue": ">= 3.0.0 < 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@googlemaps/js-api-loader": {
|
"node_modules/@googlemaps/js-api-loader": {
|
||||||
"version": "1.16.2",
|
"version": "1.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.16.2.tgz",
|
||||||
@@ -840,6 +895,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/perfect-scrollbar": {
|
||||||
|
"version": "1.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz",
|
||||||
|
"integrity": "sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g=="
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
@@ -938,6 +998,21 @@
|
|||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/qalendar": {
|
||||||
|
"version": "3.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/qalendar/-/qalendar-3.7.0.tgz",
|
||||||
|
"integrity": "sha512-V85kX4D+aKhbpvsm/4iYRXNvLFlbxUarJiJylkfH3bNOXt3mtkrToRKCAZt4Io70zcr94OA/ERpT1BlA9MMW+A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^6.3.0",
|
||||||
|
"@fortawesome/free-regular-svg-icons": "^6.3.0",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^6.3.0",
|
||||||
|
"@fortawesome/vue-fontawesome": "^3.0.3",
|
||||||
|
"perfect-scrollbar": "^1.5.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/readdirp": {
|
"node_modules/readdirp": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
|
"qalendar": "^3.7.0",
|
||||||
"sass": "^1.69.5",
|
"sass": "^1.69.5",
|
||||||
"sweetalert2": "^11.10.1",
|
"sweetalert2": "^11.10.1",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ body {
|
|||||||
background-color: #fdfcfc !important;
|
background-color: #fdfcfc !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.radius-sm {
|
||||||
|
border-radius: 8px !important;
|
||||||
|
}
|
||||||
.radius-1 {
|
.radius-1 {
|
||||||
border-radius: 1rem !important;
|
border-radius: 1rem !important;
|
||||||
}
|
}
|
||||||
@@ -247,4 +250,8 @@ td {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clear-xsm {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
67
src/components/CardBudget.vue
Normal file
67
src/components/CardBudget.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
budget: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="card-fixed card-budget">
|
||||||
|
<div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6 col-md-6 col-sm-12">
|
||||||
|
<p><span>Cliente:</span> {{budget.client}}</p>
|
||||||
|
<p v-if="budget.material"><span>Material:</span> {{budget.material.name}}</p>
|
||||||
|
<p><span>Origen:</span> {{budget.origin}}</p>
|
||||||
|
<p><span>Destino:</span> {{budget.destination}}</p>
|
||||||
|
<p><span>Tipo de camión:</span> {{budget.truck_type}}</p>
|
||||||
|
<p><span>Total KM recorridos:</span> {{budget.total_km_travel}}</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-6 col-md-6 col-sm-12">
|
||||||
|
<p><span>Total Litros De Diesel Consumidos: </span> {{parseFloat( budget.total_fuel_consumed).toFixed(2)}}</p>
|
||||||
|
<p><span>Total Costo Del Diesel:</span> {{"$" + parseFloat( budget.total_cost_fuel).toFixed(2)}}</p>
|
||||||
|
<p><span>Total Antes De Iva:</span> ${{budget.total_before_tax}}</p>
|
||||||
|
<p><span>Total Utilidad Por Kilometro:</span> {{"$" + parseFloat( budget.total_utility_per_km).toFixed(2)}}</p>
|
||||||
|
<p><span>Total Utilidad:</span> {{"$" + parseFloat( budget.total_profit).toFixed(2)}}</p>
|
||||||
|
<!-- <p>{{ $t('CALCULATOR.PROFIT_PERCENTAGE') }}: {{budget.profit_percentage}}%</p> -->
|
||||||
|
<p><span>Porcentaje De Utilidad:</span> {{parseFloat(budget.profit_percentage).toFixed(2) + "%"}}</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<button class="btn-primary-sm radius-sm">
|
||||||
|
<i class="fa-solid fa-trash" /> <span class="clear-xsm">Eliminar</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn-primary-sm radius-sm">
|
||||||
|
<i class="fa-solid fa-pen-to-square" /> <span class="clear-xsm">Editar</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-dark">
|
||||||
|
<i class="fa-solid fa-print" /> <span class="clear-xsm">Imprimir</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.card-budget {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
p span {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: end;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted, reactive } from 'vue';
|
||||||
import CardEmpty from './CardEmpty.vue';
|
import CardEmpty from './CardEmpty.vue';
|
||||||
import Spiner from './ui/Spiner.vue';
|
import Spiner from './ui/Spiner.vue';
|
||||||
|
import BadgeError from './ui/BadgeError.vue';
|
||||||
import { GoogleMap, Marker } from 'vue3-google-map';
|
import { GoogleMap, Marker } from 'vue3-google-map';
|
||||||
import useDirectionsRender from '../composables/useDirectionRender';
|
import useDirectionsRender from '../composables/useDirectionRender';
|
||||||
import { useAuthStore } from '../stores/auth';
|
import { useAuthStore } from '../stores/auth';
|
||||||
import { useVehiclesStore } from '../stores/vehicles';
|
import { useVehiclesStore } from '../stores/vehicles';
|
||||||
|
import { saveProposal } from '../services/vehicles'
|
||||||
|
import Swal from 'sweetalert2';
|
||||||
|
|
||||||
|
|
||||||
const zoom = ref(6);
|
const zoom = ref(6);
|
||||||
@@ -13,11 +16,16 @@
|
|||||||
const originCoords = ref(null);
|
const originCoords = ref(null);
|
||||||
const destinationCoords = ref(null);
|
const destinationCoords = ref(null);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
const loadingSubmit = ref(false);
|
||||||
const windowWidth = ref(window.innerWidth);
|
const windowWidth = ref(window.innerWidth);
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const vehiclesStore = useVehiclesStore();
|
const vehiclesStore = useVehiclesStore();
|
||||||
const vehicle = ref(null);
|
const msgError = ref('');
|
||||||
const comments = ref('');
|
|
||||||
|
const form = reactive({
|
||||||
|
vehicle: "",
|
||||||
|
comments: '',
|
||||||
|
});
|
||||||
|
|
||||||
const { geocodeAddress } = useDirectionsRender()
|
const { geocodeAddress } = useDirectionsRender()
|
||||||
|
|
||||||
@@ -58,6 +66,48 @@
|
|||||||
heightMap.value = 768;
|
heightMap.value = 768;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleSumit = async() => {
|
||||||
|
|
||||||
|
if(form.vehicle === ""){
|
||||||
|
msgError.value = 'Selecciona vehiculo para continuar';
|
||||||
|
setTimeout(() => {
|
||||||
|
msgError.value = '';
|
||||||
|
}, 5000);
|
||||||
|
return;
|
||||||
|
} else if (form.comments.trim().length <= 0) {
|
||||||
|
msgError.value = 'Agrega un comentario';
|
||||||
|
setTimeout(() => {
|
||||||
|
msgError.value = '';
|
||||||
|
}, 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgError.value = '';
|
||||||
|
let formData = {
|
||||||
|
carrier: authStore.user.company,
|
||||||
|
bidder : authStore.user._id,
|
||||||
|
comment: form.comments,
|
||||||
|
vehicle : form.vehicle,
|
||||||
|
load : props.load._id
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingSubmit.value = true;
|
||||||
|
const result = await saveProposal(formData);
|
||||||
|
if(result !== null) {
|
||||||
|
document.getElementById('btnClosemakeProposalModal').click();
|
||||||
|
Swal.fire({
|
||||||
|
title: '<strong>Oferta creada con éxito!</strong>',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Swal.fire({
|
||||||
|
title: '<strong>Oferta no se pudo crear!</strong>',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loadingSubmit.value = false;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -65,7 +115,7 @@
|
|||||||
<div class="modal-dialog modal-dialog-centered modal-xl" role="document">
|
<div class="modal-dialog modal-dialog-centered modal-xl" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h2 class="title mt-2 mb-3">Ofertas</h2>
|
<h2 class="title mt-2 mb-3">Realizar oferta</h2>
|
||||||
<button
|
<button
|
||||||
id="btnClosemakeProposalModal"
|
id="btnClosemakeProposalModal"
|
||||||
type="button"
|
type="button"
|
||||||
@@ -81,15 +131,15 @@
|
|||||||
<Spiner v-if="isLoading"/>
|
<Spiner v-if="isLoading"/>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div v-if="load">
|
<div v-if="load">
|
||||||
<form class="box-form mb-4">
|
<form @submit.prevent="handleSumit" class="box-form mb-4">
|
||||||
|
<BadgeError :msg="msgError"/>
|
||||||
<div class="custom-selected-field">
|
<div class="custom-selected-field">
|
||||||
<label class="custom-label" for="vehicle">Vehiculo:</label>
|
<label class="custom-label" for="vehicle">Vehiculo:</label>
|
||||||
<select
|
<select
|
||||||
class="custom-input-light"
|
class="custom-input-light"
|
||||||
name="vehicle"
|
name="vehicle"
|
||||||
id="vehicle"
|
id="vehicle"
|
||||||
:value="vehicle"
|
v-model="form.vehicle"
|
||||||
@input="$event => $emit('update:vehicle', $event.target.value)"
|
|
||||||
>
|
>
|
||||||
<option disabled value="">-- Seleccionar vehículo --</option>
|
<option disabled value="">-- Seleccionar vehículo --</option>
|
||||||
<option v-for="vehicle in vehiclesStore.vehicles" :value="vehicle._id">{{vehicle.vehicle_code}}</option>
|
<option v-for="vehicle in vehiclesStore.vehicles" :value="vehicle._id">{{vehicle.vehicle_code}}</option>
|
||||||
@@ -102,12 +152,13 @@
|
|||||||
name="comment"
|
name="comment"
|
||||||
id="comment"
|
id="comment"
|
||||||
placeholder="Escribe aqui..."
|
placeholder="Escribe aqui..."
|
||||||
:value="comments"
|
v-model="form.comments"
|
||||||
@input="$event => $emit('update:comments', $event.target.value)"
|
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-btns">
|
<div class="box-btns">
|
||||||
|
<Spiner v-if="loadingSubmit"/>
|
||||||
<input
|
<input
|
||||||
|
v-else
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn-primary-sm"
|
class="btn-primary-sm"
|
||||||
value="Enviar"
|
value="Enviar"
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ import CardEmpty from './CardEmpty.vue';
|
|||||||
|
|
||||||
if(load != null) {
|
if(load != null) {
|
||||||
const index = loadsStore.loads.findIndex((load) => load._id === load_id);
|
const index = loadsStore.loads.findIndex((load) => load._id === load_id);
|
||||||
loadsStore.loads[index] = load;
|
loadsStore.loads[index] = {
|
||||||
|
...loadsStore.loads[index],
|
||||||
|
...load
|
||||||
|
};
|
||||||
const proposal_id = proposal._id;
|
const proposal_id = proposal._id;
|
||||||
|
|
||||||
let formData = {
|
let formData = {
|
||||||
@@ -106,7 +109,10 @@ import CardEmpty from './CardEmpty.vue';
|
|||||||
let load = await loadsStore.updateLoad(load_id, loadData);
|
let load = await loadsStore.updateLoad(load_id, loadData);
|
||||||
if(load) {
|
if(load) {
|
||||||
const index = loadsStore.loads.findIndex((load) => load._id === load_id);
|
const index = loadsStore.loads.findIndex((load) => load._id === load_id);
|
||||||
loadsStore.loads[index] = load;
|
loadsStore.loads[index] = {
|
||||||
|
...loadsStore.loads[index],
|
||||||
|
...load
|
||||||
|
};
|
||||||
let formData = {
|
let formData = {
|
||||||
accepted_by : null,
|
accepted_by : null,
|
||||||
accepted_date : null,
|
accepted_date : null,
|
||||||
|
|||||||
56
src/components/ui/BadgeError.vue
Normal file
56
src/components/ui/BadgeError.vue
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
msg: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
isError: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
showIcon: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="msg.length > 0" class="badge" :class="[isError ? 'badge-error' : 'badge-success']">
|
||||||
|
<span>
|
||||||
|
<i v-if="showIcon && isError" class="fa-solid fa-circle-exclamation me-2"></i>
|
||||||
|
<i v-if="showIcon && !isError" class="fa-solid fa-circle-check"></i>
|
||||||
|
{{ msg }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.badge {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
color: #FFF;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
border-radius: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-error {
|
||||||
|
background-color: rgb(238, 101, 101);
|
||||||
|
}
|
||||||
|
.badge-success {
|
||||||
|
background-color: rgb(29, 162, 113);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.badge {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 400;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -15,8 +15,8 @@ export default function useSearchLoads() {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const endpoint = `/loads/${filterStr}`;
|
const endpoint = `/loads/${filterStr}`;
|
||||||
console.log(endpoint)
|
|
||||||
const {data} = await api.get(endpoint);
|
const {data} = await api.get(endpoint);
|
||||||
|
console.log(data);
|
||||||
loads.value = data.data;
|
loads.value = data.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
loads.value = [];
|
loads.value = [];
|
||||||
|
|||||||
62
src/data/events.json
Normal file
62
src/data/events.json
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"status": "Delivered",
|
||||||
|
"date": "2023-01-05T03:24:18.821Z",
|
||||||
|
"shipment_code": "ETA1010"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Delivered",
|
||||||
|
"date": "2023-12-05T03:24:18.821Z",
|
||||||
|
"shipment_code": "ETA1010"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Delivered",
|
||||||
|
"date": "2023-12-24T01:00:57.627Z",
|
||||||
|
"shipment_code": "ETA1007"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Delivered",
|
||||||
|
"date": "2023-12-20T19:36:13.562Z",
|
||||||
|
"shipment_code": "ETA1014"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Published",
|
||||||
|
"date": "2023-12-17T00:29:10.471Z",
|
||||||
|
"shipment_code": "ETA1021"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Delivered",
|
||||||
|
"date": "2023-12-11T02:47:28.823Z",
|
||||||
|
"shipment_code": "ETA1013"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Loading",
|
||||||
|
"date": "2023-12-12T02:47:28.823Z",
|
||||||
|
"shipment_code": "ETA1023"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Transit",
|
||||||
|
"date": "2023-12-13T02:47:28.823Z",
|
||||||
|
"shipment_code": "ETA1024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Downloading",
|
||||||
|
"date": "2023-12-13T02:47:28.823Z",
|
||||||
|
"shipment_code": "ETA1025"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Delivered",
|
||||||
|
"date": "2023-12-04T02:31:45.679Z",
|
||||||
|
"shipment_code": "ETA1006"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Published",
|
||||||
|
"date": "2023-12-21T00:42:43.588Z",
|
||||||
|
"shipment_code": "ETA1022"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"status": "Published",
|
||||||
|
"date": "2023-12-26T00:57:26.432Z",
|
||||||
|
"shipment_code": "ETA1016"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -10,3 +10,21 @@ export const getDateMonthDay = (value) => {
|
|||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getDateTime = (value, hour) => {
|
||||||
|
|
||||||
|
const date = new Date(value);
|
||||||
|
date.setHours(date.getHours() + hour);
|
||||||
|
|
||||||
|
// Obtener los componentes de la fecha
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = ('0' + (date.getMonth() + 1)).slice(-2); // Agrega cero al principio si es necesario
|
||||||
|
const day = ('0' + date.getDate()).slice(-2); // Agrega cero al principio si es necesario
|
||||||
|
const hours = ('0' + date.getHours()).slice(-2); // Agrega cero al principio si es necesario
|
||||||
|
const minutes = ('0' + date.getMinutes()).slice(-2); // Agrega cero al principio si es necesario
|
||||||
|
|
||||||
|
// Crear la cadena de fecha formateada
|
||||||
|
const dateFormat = `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||||
|
|
||||||
|
return dateFormat;
|
||||||
|
}
|
||||||
@@ -33,6 +33,41 @@ export const getStatusLoad = (load) => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const eventStatusLoad = (loadStatus) => {
|
||||||
|
let color;
|
||||||
|
let status;
|
||||||
|
switch (loadStatus) {
|
||||||
|
case 'Published':
|
||||||
|
status = "Publicado";
|
||||||
|
color = "yellow";
|
||||||
|
break;
|
||||||
|
case 'Loading':
|
||||||
|
color = "green";
|
||||||
|
status = "Cargando";
|
||||||
|
break;
|
||||||
|
case 'Transit':
|
||||||
|
status = "En Transito";
|
||||||
|
color = "red"
|
||||||
|
break;
|
||||||
|
case 'Downloading':
|
||||||
|
status = "Descargando";
|
||||||
|
color = "blue"
|
||||||
|
break;
|
||||||
|
case 'Delivered':
|
||||||
|
color = "blue";
|
||||||
|
status = "Entregado";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
color = "yellow";
|
||||||
|
status = 'Sin publicar';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
color,
|
||||||
|
status
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const getStatusPublished = (load) => {
|
export const getStatusPublished = (load) => {
|
||||||
let status;
|
let status;
|
||||||
switch (load.status) {
|
switch (load.status) {
|
||||||
|
|||||||
@@ -23,6 +23,19 @@ export const editCompany = async(companyId, formData) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getBudgets = async(filter) => {
|
||||||
|
try {
|
||||||
|
const endpoint = `/budgets/${filter}`;
|
||||||
|
console.log(endpoint);
|
||||||
|
const {data} = await api.get(endpoint);
|
||||||
|
console.log(data);
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// export const editCompany = async(companyId, formData) => {
|
// export const editCompany = async(companyId, formData) => {
|
||||||
// try {
|
// try {
|
||||||
|
|||||||
@@ -11,3 +11,14 @@ export const getVehicles = async(filter) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const saveProposal = async(formData) => {
|
||||||
|
try {
|
||||||
|
const endpoint = `/proposals`;
|
||||||
|
const {data} = await api.post(endpoint, formData);
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { getCompany } from "../services/company";
|
import { getBudgets, getCompany } from "../services/company";
|
||||||
import api from "../lib/axios";
|
import api from "../lib/axios";
|
||||||
|
|
||||||
export const useCompanyStore = defineStore('company', () => {
|
export const useCompanyStore = defineStore('company', () => {
|
||||||
|
|
||||||
const companyid = localStorage.getItem('id');
|
const companyid = localStorage.getItem('id');
|
||||||
const company = ref(null)
|
const company = ref(null)
|
||||||
|
const budgets = ref([]);
|
||||||
const proposals = ref([])
|
const proposals = ref([])
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
@@ -43,10 +44,38 @@ export const useCompanyStore = defineStore('company', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getBudgetsCompany = async(filterQuery, reload = false) => {
|
||||||
|
let filterArr = Object.values(filterQuery);
|
||||||
|
|
||||||
|
let cleanfilterArr = filterArr.filter(n=>n);
|
||||||
|
// console.log(cleanfilterArr);
|
||||||
|
var filterStr = "";
|
||||||
|
if(cleanfilterArr.length >0){
|
||||||
|
filterStr ="?"+cleanfilterArr.join("&");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(filterStr);
|
||||||
|
|
||||||
|
if(budgets.value.length <= 0 || reload === true) {
|
||||||
|
try {
|
||||||
|
const data = await getBudgets(filterStr);
|
||||||
|
if(data.total > 0) {
|
||||||
|
budgets.value = data.data;
|
||||||
|
} else {
|
||||||
|
budgets.value = [];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
budgets.value = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getCompanyData,
|
getCompanyData,
|
||||||
getProposalsCompany,
|
getProposalsCompany,
|
||||||
|
getBudgetsCompany,
|
||||||
|
budgets,
|
||||||
clear,
|
clear,
|
||||||
loading,
|
loading,
|
||||||
proposals,
|
proposals,
|
||||||
|
|||||||
@@ -1,13 +1,129 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
import { useCompanyStore } from '../stores/company';
|
||||||
|
import { useAuthStore } from '../stores/auth';
|
||||||
|
import Spiner from '../components/ui/Spiner.vue';
|
||||||
|
import CardBudget from '../components/CardBudget.vue';
|
||||||
|
import CardEmpty from '../components/CardEmpty.vue';
|
||||||
|
|
||||||
|
const companyStore = useCompanyStore();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const filterQuery = ref([]);
|
||||||
|
const query = ref('');
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getInitData();
|
||||||
|
})
|
||||||
|
|
||||||
|
const getInitData = async() => {
|
||||||
|
loading.value = true;
|
||||||
|
filterQuery.value.company = "company="+ localStorage.getItem('id');
|
||||||
|
await companyStore.getBudgetsCompany(filterQuery.value, false)
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBudgetsWithFilters = async(filter) => {
|
||||||
|
loading.value = true;
|
||||||
|
await companyStore.getBudgetsCompany(filter, true);
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(query, () => {
|
||||||
|
if(query.value.length === 0){
|
||||||
|
filterQuery.value.search = "";
|
||||||
|
getBudgetsWithFilters(filterQuery.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const search = () => {
|
||||||
|
if(query.value.length >= 2){
|
||||||
|
// filterQuery.value = "company_name[$regex]=" + query.value + "&company_name[$options]=i";
|
||||||
|
filterQuery.value.search = "client[$regex]="+query.value+"&client[$options]=i";
|
||||||
|
getBudgetsWithFilters(filterQuery.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearFilter = () => {
|
||||||
|
filterQuery.value.search = "";
|
||||||
|
filterQuery.value.company = "company="+ localStorage.getItem('id');
|
||||||
|
|
||||||
|
if(query.value == ''){
|
||||||
|
getInitData();
|
||||||
|
} else {
|
||||||
|
query.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="title">Calculadora</h2>
|
<h2 class="title my-2">Calculadora</h2>
|
||||||
|
</div>
|
||||||
|
<div class="box-filters">
|
||||||
|
<div class="box-search">
|
||||||
|
<input class="form-control custom-search" type="search" name="" placeholder="Buscar por cliente" id="" @:input="search()" v-model="query" aria-label="Search">
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="btn btn-danger bg-dark" type="button" @click="clearFilter">
|
||||||
|
<i class="fa-solid fa-arrow-rotate-right"></i>
|
||||||
|
<span class="clear-sm"> Reset</span><span class="clear-md"> filtros</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn-primary-sm radius-sm"
|
||||||
|
data-toggle="modal" data-target="#formLoadModal"
|
||||||
|
@click=""
|
||||||
|
><i class="fa-solid fa-plus"></i> <span class="clear-sm"> Crear</span><span class="clear-md"> presupuesto</span></button>
|
||||||
|
</div>
|
||||||
|
<Spiner v-if="loading"/>
|
||||||
|
<div v-else>
|
||||||
|
<CardBudget
|
||||||
|
v-if="companyStore.budgets.length > 0"
|
||||||
|
v-for="budget in companyStore.budgets"
|
||||||
|
:budget="budget"
|
||||||
|
/>
|
||||||
|
<CardEmpty
|
||||||
|
v-else
|
||||||
|
text="No hay presupuestos agregados"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.box-filters {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: end;
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 1.5rem 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-search {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
.custom-search {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.box-search {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
.box-filters {
|
||||||
|
gap: .4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
|
||||||
|
.box-search {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.box-filters {
|
||||||
|
gap: .3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,13 +1,155 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { Qalendar } from 'qalendar';
|
||||||
|
import data from '../data/events.json';
|
||||||
|
import {eventStatusLoad} from '../helpers/status';
|
||||||
|
import {getDateTime} from '../helpers/date_formats';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
const events = ref([]);
|
||||||
|
const router = useRouter();
|
||||||
|
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: {
|
||||||
|
meetings: {
|
||||||
|
color: "#fff",
|
||||||
|
backgroundColor: "#131313",
|
||||||
|
},
|
||||||
|
sports: {
|
||||||
|
color: "#fff",
|
||||||
|
backgroundColor: "#ff4081",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
eventDialog:{
|
||||||
|
isCustom: true
|
||||||
|
},
|
||||||
|
defaultMode: 'month',
|
||||||
|
isSilent: true,
|
||||||
|
// showCurrentTime: true, // Display a line indicating the current time
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
data.forEach((e, i) => {
|
||||||
|
const indicator = eventStatusLoad(e.status);
|
||||||
|
const dateStart = getDateTime(e.date, 0);
|
||||||
|
const dateEnd = getDateTime(e.date, 1);
|
||||||
|
events.value.push({
|
||||||
|
id: i,
|
||||||
|
title: e.shipment_code,
|
||||||
|
// isEditable: true,
|
||||||
|
// isCustom: true,
|
||||||
|
with: indicator.status,
|
||||||
|
description: indicator.status,
|
||||||
|
color: indicator.color,
|
||||||
|
time: {
|
||||||
|
start: dateStart,
|
||||||
|
end: dateEnd
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const redirectToTracking = (code) => {
|
||||||
|
router.push({
|
||||||
|
name: 'tracking-load',
|
||||||
|
params: {code}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="title">Calendario</h2>
|
<h2 class="title">Calendario</h2>
|
||||||
|
<div class="calendar">
|
||||||
|
<!-- <div class="calendar is-light-mode"> -->
|
||||||
|
<Qalendar
|
||||||
|
:events="events"
|
||||||
|
:config="config"
|
||||||
|
>
|
||||||
|
<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.title)">
|
||||||
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style src="qalendar/dist/style.css"></style>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.calendar {
|
||||||
|
height: calc(100vh - 150px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user