add: observer input & tooltips
This commit is contained in:
@@ -231,6 +231,47 @@ td {
|
||||
border: 1px solid #FBBA33 !important;
|
||||
}
|
||||
|
||||
.box-observers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.box-input {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
.input-observer {
|
||||
flex: 1;
|
||||
}
|
||||
.box-emails {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
border-radius: 5px;
|
||||
padding: 0.5rem;
|
||||
background-color: #FFF;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.observer-email {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #e0f7fa;
|
||||
padding: 0.5rem;
|
||||
border-radius: 5px;
|
||||
margin-right: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.icon-delete {
|
||||
cursor: pointer;
|
||||
color: #FF0000;
|
||||
font-size: 1.5rem;
|
||||
margin-left: 0.5rem;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
th {
|
||||
font-size: 13px;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { useCompanyStore } from '../stores/company';
|
||||
import Swal from 'sweetalert2';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { validateEmail } from '../helpers/validations';
|
||||
|
||||
const props = defineProps({
|
||||
location: {
|
||||
@@ -27,6 +28,8 @@
|
||||
const { t } = useI18n()
|
||||
|
||||
const loading = ref(false);
|
||||
const emails = ref([]);
|
||||
const emailInput = ref('');
|
||||
|
||||
const title = computed(() => {
|
||||
return (props.location) ? t('directory.editLocation') : t('directory.createLocation');
|
||||
@@ -139,6 +142,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
const addObserver = () => {
|
||||
const trimmedEmail = emailInput.value.trim()
|
||||
|
||||
if (trimmedEmail && validateEmail(trimmedEmail)) {
|
||||
if(emails.value.includes(trimmedEmail)) {
|
||||
Swal.fire({
|
||||
title: t('errors.emailExist'),
|
||||
icon: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
emails.value.push(trimmedEmail)
|
||||
emailInput.value = ''
|
||||
} else if (trimmedEmail) {
|
||||
Swal.fire({
|
||||
title: t('errors.email'),
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const removeObserver = (email) => {
|
||||
emails.value = emails.value.filter((e) => e !== email)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const validations = () => {
|
||||
errors.value = {
|
||||
branch_name: locationForm.branch_name.length < 2 ? t('errors.nameRequired') : null,
|
||||
@@ -251,6 +281,42 @@
|
||||
v-model="locationForm.description"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="divider mt-4"></div>
|
||||
<div class="box-observers mt-4">
|
||||
<div class="box-input">
|
||||
<div class="input-observer">
|
||||
<CustomInput
|
||||
label="Agregar observador de bodega"
|
||||
name="email"
|
||||
type="email"
|
||||
v-model:field="emailInput"
|
||||
:filled="false"
|
||||
:tooltip="t('messages.observerWarehouse')"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
@click="addObserver"
|
||||
class="btn btn-dark"
|
||||
>
|
||||
Agregar
|
||||
</button>
|
||||
</div>
|
||||
<h5 v-if="emails.length > 0">Lista de observadores:</h5>
|
||||
<div class="box-emails" v-if="emails.length > 0">
|
||||
<div
|
||||
v-for="(email, index) in emails"
|
||||
:key="email"
|
||||
class="observer-email"
|
||||
>
|
||||
<span>{{ email }}</span>
|
||||
<i
|
||||
@click="removeObserver(email)"
|
||||
class="fa-solid fa-xmark icon-delete">
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 text-center">
|
||||
<Spiner v-if="loading"/>
|
||||
<button
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed } from 'vue';
|
||||
import {getDateTime } from '../helpers/date_formats';
|
||||
import { validateEmail } from '../helpers/validations';
|
||||
|
||||
|
||||
const loadStore = useLoadsStore();
|
||||
@@ -43,6 +44,8 @@
|
||||
const locationDownloadSelected = ref(null)
|
||||
const originRef = ref('')
|
||||
const destinationRef = ref('')
|
||||
const emails = ref([]);
|
||||
const emailInput = ref('');
|
||||
|
||||
const errors = ref({
|
||||
segment: null,
|
||||
@@ -362,6 +365,31 @@
|
||||
? t('loads.create')
|
||||
: t('loads.edit')
|
||||
)
|
||||
|
||||
const addObserver = () => {
|
||||
const trimmedEmail = emailInput.value.trim()
|
||||
|
||||
if (trimmedEmail && validateEmail(trimmedEmail)) {
|
||||
if(emails.value.includes(trimmedEmail)) {
|
||||
Swal.fire({
|
||||
title: t('errors.emailExist'),
|
||||
icon: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
emails.value.push(trimmedEmail)
|
||||
emailInput.value = ''
|
||||
} else if (trimmedEmail) {
|
||||
Swal.fire({
|
||||
title: t('errors.email'),
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const removeObserver = (email) => {
|
||||
emails.value = emails.value.filter((e) => e !== email)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -425,14 +453,14 @@
|
||||
:error="(submited && !formLoad.weight) ? t('errors.weight') : null"
|
||||
v-model:field="formLoad.weight"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-section">
|
||||
<div class="mb-4 mt-3">
|
||||
<label class="custom-label">{{ t('loads.product') }}</label>
|
||||
<Products
|
||||
v-model="formLoad.terms"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-section">
|
||||
<Custominput
|
||||
:label="t('loads.labelPrice')"
|
||||
type="Number"
|
||||
@@ -455,6 +483,41 @@
|
||||
name="owner"
|
||||
v-model:field="formLoad.owner"
|
||||
/>
|
||||
<div class="box-observers mt-4">
|
||||
<div class="box-input">
|
||||
<div class="input-observer">
|
||||
<Custominput
|
||||
label="Agregar cliente observador"
|
||||
name="email"
|
||||
type="email"
|
||||
v-model:field="emailInput"
|
||||
:filled="false"
|
||||
:tooltip="t('messages.observerClient')"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
@click="addObserver"
|
||||
class="btn btn-dark"
|
||||
>
|
||||
Agregar
|
||||
</button>
|
||||
</div>
|
||||
<h5 v-if="emails.length > 0">Lista de observadores:</h5>
|
||||
<div class="box-emails" v-if="emails.length > 0">
|
||||
<div
|
||||
v-for="(email, index) in emails"
|
||||
:key="email"
|
||||
class="observer-email"
|
||||
>
|
||||
<span>{{ email }}</span>
|
||||
<i
|
||||
@click="removeObserver(email)"
|
||||
class="fa-solid fa-xmark icon-delete">
|
||||
</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-box">
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
step: {
|
||||
type: Number,
|
||||
default: 0.1
|
||||
},
|
||||
tooltip: {
|
||||
type: String,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -48,11 +51,24 @@
|
||||
|
||||
<template>
|
||||
<div class="d-flex flex-column gap-2 mb-4">
|
||||
<label
|
||||
<!-- <label
|
||||
class="custom-label"
|
||||
:class="[label.includes('*') ? 'required' : '']"
|
||||
:for="name"
|
||||
>{{ label }}</label>
|
||||
>{{ label }}</label> -->
|
||||
<div class="label-box">
|
||||
<label
|
||||
class="custom-label label-text"
|
||||
:class="[label.includes('*') ? 'required' : '']"
|
||||
:for="name"
|
||||
>{{ label }}</label>
|
||||
<div class="help-icon" v-if="tooltip">
|
||||
❓
|
||||
<div class="tooltip">
|
||||
{{ tooltip }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
class="custom-input"
|
||||
:class="[
|
||||
@@ -86,4 +102,50 @@
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.label-box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Estilo del ícono de ayuda */
|
||||
.help-icon {
|
||||
position: relative;
|
||||
font-size: 20px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Tooltip oculto inicialmente */
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
width: 180px;
|
||||
padding: 8px;
|
||||
background: #333;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateX(-50%);
|
||||
margin-top: 8px;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
/* Mostrar tooltip al pasar el mouse */
|
||||
.help-icon:hover .tooltip,
|
||||
.help-icon:active .tooltip {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -113,6 +113,7 @@ const en = {
|
||||
required: 'Field is required',
|
||||
requireds: "All fields required",
|
||||
email: "Email is not valid",
|
||||
emailExist: 'Email already exists',
|
||||
weakPassword: "Weak password",
|
||||
matchPassword: "Passwords do not match",
|
||||
code: 'Enter valid code',
|
||||
@@ -156,6 +157,8 @@ const en = {
|
||||
msgCreatedUser: 'When creating a new user, you inform the account beneficiary that they must use their email to set a password in the <span class="font-bold">Forgot my password</span> section so they can log in.',
|
||||
loading: 'Please wait!',
|
||||
savingChanes: 'Saving changes',
|
||||
observerWarehouse: 'Warehouse observers will receive notifications about the load location, including arrival and departure times, and will be able to view the cargo on the warehouse dashboard.',
|
||||
observerClient: 'Observers will receive notifications about the load, including the loading and unloading date, as well as any changes in the load status. They can monitor the cargo on their customer dashboard.'
|
||||
},
|
||||
global: {
|
||||
signIn: "Sign In",
|
||||
@@ -177,7 +180,7 @@ const en = {
|
||||
footer: 'ETA VIAPORTE ALL RIGHTS RESERVED',
|
||||
company: 'Company',
|
||||
users: 'Users',
|
||||
directory: 'Internal directory',
|
||||
directory: 'Warehouses',
|
||||
publications: 'Publications',
|
||||
calendar: 'Calendar',
|
||||
carriers: 'Carriers',
|
||||
|
||||
@@ -116,6 +116,7 @@ const es = {
|
||||
required: 'Campo es requerido',
|
||||
requireds: 'Todos los campos con obligatorios',
|
||||
email: 'Correo electrónico no es valido',
|
||||
emailExist: 'Este correo ya fue agregado',
|
||||
weakPassword: 'Contraseña poco segura',
|
||||
matchPassword: 'Las contraseñas no coinciden',
|
||||
code: 'Ingresa código valido',
|
||||
@@ -159,6 +160,8 @@ const es = {
|
||||
msgCreatedUser: 'Al crear un nuevo usuario, informa al beneficiario de la cuenta, que debe utilizar su correo electrónico para establecer una contraseña en la sección <span class="font-bold">Olvidé mi contraseña</span> y así poder iniciar sesión.',
|
||||
loading: 'Por favor espere!',
|
||||
savingChanes: 'Guardando cambios',
|
||||
observerWarehouse: 'Los observadores de bodega recibirán notificaciones sobre la ubicación de la carga, incluyendo la hora de llegada y salida, podran visualizar la carga en el panel de bodega.',
|
||||
observerClient: 'Los observadores recibirán notificaciones sobre la carga, incluyendo la fecha de carga y descarga, así como cualquier cambio en el estado de la carga. podran monitorer la carga en el panel de clientes.'
|
||||
},
|
||||
global: {
|
||||
signIn: 'Ingresar',
|
||||
@@ -180,7 +183,7 @@ const es = {
|
||||
footer: 'ETA VIAPORTE TODOS LOS DERECHOS RESERVADOS',
|
||||
company: 'Empresa',
|
||||
users: 'Usuarios',
|
||||
directory: 'Directorio interno',
|
||||
directory: 'Bodegas',
|
||||
publications: 'Publicaciones',
|
||||
calendar: 'Calendario',
|
||||
carriers: 'Transportistas',
|
||||
|
||||
Reference in New Issue
Block a user