add: contacts module & actions to private list
This commit is contained in:
43
src/components/CustomSearchInput.vue
Normal file
43
src/components/CustomSearchInput.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<script setup>
|
||||
|
||||
const props = defineProps({
|
||||
saerch: {
|
||||
type: [String]
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
}
|
||||
})
|
||||
|
||||
defineEmits(['update:saerch'])
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mb-4">
|
||||
<input
|
||||
class="custom-search"
|
||||
type="search"
|
||||
name="search"
|
||||
:placeholder="placeholder"
|
||||
:value="saerch"
|
||||
@input="$event => $emit('update:saerch', $event.target.value)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.custom-search {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid rgb(222, 214, 214);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.custom-search:focus {
|
||||
outline: none;
|
||||
border-color: rgb(217, 202, 202);
|
||||
}
|
||||
|
||||
</style>
|
||||
93
src/components/CustomSwitch.vue
Normal file
93
src/components/CustomSwitch.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const toggle = (event) => {
|
||||
emit('update:modelValue', event.target.checked)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<label class="switch-container">
|
||||
<span v-if="label" class="custom-label label-text">{{ label }}</span>
|
||||
|
||||
<input
|
||||
type="checkbox"
|
||||
:name="name"
|
||||
:checked="modelValue"
|
||||
@change="toggle"
|
||||
>
|
||||
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.switch-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* ocultar input */
|
||||
.switch-container input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* fondo del switch */
|
||||
.slider {
|
||||
position: relative;
|
||||
width: 45px;
|
||||
height: 25px;
|
||||
background-color: #ccc;
|
||||
border-radius: 25px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
/* circulo */
|
||||
.slider::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
left: 4px;
|
||||
top: 3.5px;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
/* estado activo */
|
||||
input:checked + .slider {
|
||||
background-color: #5A67DD; /* tu color */
|
||||
}
|
||||
|
||||
input:checked + .slider::before {
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
/* hover */
|
||||
.switch-container:hover .slider {
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
@@ -159,6 +159,16 @@
|
||||
class="nav-link" :to="{name: 'calculator'}">{{ t('global.calculator') }}</RouterLink>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
v-if="permission === 'role_shipper' && jobRole !== roleCheck"
|
||||
:class="[route.name === 'groups' ? 'bg-nav-active' : '']">
|
||||
<div>
|
||||
<i class="fa-regular fa-address-book" :class="[route.name === 'groups' ? 'router-link-active' : '']"></i>
|
||||
<RouterLink
|
||||
active-class=""
|
||||
class="nav-link" :to="{name: 'groups'}">Lista privada</RouterLink>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="eta-info">
|
||||
<div class="divider"></div>
|
||||
|
||||
@@ -200,7 +200,16 @@ const router = createRouter({
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'not-found',
|
||||
component: () => import('../views/dashboard/HomeView.vue'),
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'lista-privada',
|
||||
name: 'groups',
|
||||
meta: {
|
||||
permissions: ['role_shipper'],
|
||||
roles: ['staff', 'manager', 'owner']
|
||||
},
|
||||
component: () => import('../views/contacts/ContactsView.vue'),
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
66
src/stores/contacts.js
Normal file
66
src/stores/contacts.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import { defineStore } from "pinia"
|
||||
import { ref } from "vue";
|
||||
|
||||
export const useContactsStore = defineStore('contacts', () => {
|
||||
|
||||
const contacts = ref([
|
||||
{
|
||||
"id": 13,
|
||||
"company": "Altos",
|
||||
"companyId": 929,
|
||||
"rfc": "USKSK00101",
|
||||
"category": "Agricola"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"company": "Altos logos",
|
||||
"companyId": 2018,
|
||||
"rfc": "USKSK0010a",
|
||||
"category": "Agricola"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"company": "Bravos SA",
|
||||
"companyId": 199,
|
||||
"rfc": "UJSK8991",
|
||||
"category": "Materiales"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"company": "Kolo",
|
||||
"companyId": 1993,
|
||||
"rfc": "JKDKD91001",
|
||||
"category": "Servicios"
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"company": "Altos",
|
||||
"companyId": 929,
|
||||
"rfc": "USKSK00101",
|
||||
"category": "Agricola"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"company": "Altos",
|
||||
"companyId": 929,
|
||||
"rfc": "USKSK00101",
|
||||
"category": "Agricola"
|
||||
},
|
||||
{
|
||||
"id": 934,
|
||||
"company": "Altos",
|
||||
"companyId": 929,
|
||||
"rfc": "USKSK00101",
|
||||
"category": "Agricola"
|
||||
},
|
||||
]);
|
||||
|
||||
const removeContact = (id) => {
|
||||
contacts.value = contacts.value.filter((e) => e.id !== id);
|
||||
}
|
||||
|
||||
return {
|
||||
contacts,
|
||||
removeContact
|
||||
}
|
||||
});
|
||||
54
src/views/contacts/ContactsView.vue
Normal file
54
src/views/contacts/ContactsView.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { useContactsStore } from '../../stores/contacts';
|
||||
import ContactCard from './components/ContactCard.vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import CustomSearchInput from '../../components/CustomSearchInput.vue';
|
||||
import Spiner from '../../components/ui/Spiner.vue';
|
||||
|
||||
const contactsStore = useContactsStore();
|
||||
const { t } = useI18n();
|
||||
const loading = ref(false);
|
||||
const query = ref('');
|
||||
let timeout = null;
|
||||
|
||||
|
||||
const search = () => {
|
||||
console.log('Searching:', query.value);
|
||||
};
|
||||
|
||||
|
||||
watch(query, (newValue) => {
|
||||
clearTimeout(timeout);
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
if (newValue.length >= 2) {
|
||||
search();
|
||||
}
|
||||
}, 400);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CustomSearchInput
|
||||
:placeholder="t('carriers.searchByCarrier')"
|
||||
v-model:saerch="query"
|
||||
/>
|
||||
<div class="row">
|
||||
<div v-if="loading" class="d-flex justify-content-center">
|
||||
<Spiner />
|
||||
</div>
|
||||
<ContactCard
|
||||
v-else
|
||||
v-for="contact in contactsStore.contacts"
|
||||
:key="contact.id"
|
||||
:contact="contact"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
</style>
|
||||
90
src/views/contacts/components/ContactCard.vue
Normal file
90
src/views/contacts/components/ContactCard.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useAuthStore } from '../../../stores/auth';
|
||||
import Swal from 'sweetalert2';
|
||||
import { useContactsStore } from '../../../stores/contacts';
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
contact: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const { t } = useI18n();
|
||||
const authStore = useAuthStore();
|
||||
const contactsStore = useContactsStore();
|
||||
|
||||
const handleDeleteContact = async() => {
|
||||
Swal.fire({
|
||||
title: 'Eliminar transportista',
|
||||
text: '¿Estas seguro de eliminar este transportista de la lista privada?',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: t('buttons.delete'),
|
||||
cancelButtonText: t('buttons.cancel'),
|
||||
}).then(async(result) => {
|
||||
const id = props.contact.id;
|
||||
if(result.isConfirmed) {
|
||||
Swal.fire({
|
||||
title: t('messages.loading'),
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading()
|
||||
},
|
||||
});
|
||||
let resp = null;
|
||||
setTimeout(() => {
|
||||
resp = true
|
||||
Swal.close();
|
||||
if(resp != null) {
|
||||
contactsStore.removeContact(id);
|
||||
Swal.fire({
|
||||
title: 'Transportista eliminado',
|
||||
text: 'Se ha eliminado el transportista de la lista privada',
|
||||
icon: "success"
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: t('errors.msgTitleNotDel'),
|
||||
text: 'No se pudo completar la eliminación del transportista de la lista privada',
|
||||
icon: "error"
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="col-lg-6 col-12">
|
||||
<div class="card-fixed flex-d-column">
|
||||
<div class="d-flex">
|
||||
<h2 class="flex1">{{ contact.company }}</h2>
|
||||
<button
|
||||
v-if="(authStore.user?.job_role === 'owner' || authStore.user?.job_role === 'manager')"
|
||||
class="btn-primary-sm bg-danger"
|
||||
@click="handleDeleteContact"
|
||||
>
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<p><span class="font-bold">RFC: </span> {{ contact.rfc}}</p>
|
||||
<p><span class="font-bold">{{ t('global.segments') }}:</span> {{contact.category}}</p>
|
||||
<p><span class="font-bold">{{ t('directory.typeTruckNeed') }}: </span> Torton</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.flex1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -14,9 +14,9 @@
|
||||
import { useCompanyStore } from '../../../stores/company';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed } from 'vue';
|
||||
import {getDateToLocal } from '../../../helpers/date_formats';
|
||||
import { validateEmail } from '../../../helpers/validations';
|
||||
import AddressPreview from '../../../components/AddressPreview.vue';
|
||||
import CustomSwitch from '../../../components/CustomSwitch.vue';
|
||||
|
||||
|
||||
const loadStore = useLoadsStore();
|
||||
@@ -42,7 +42,8 @@
|
||||
const destinationRef = ref('')
|
||||
const emails = ref([]);
|
||||
const emailInput = ref('');
|
||||
|
||||
const isPrivate = ref(false);
|
||||
|
||||
const errors = ref({
|
||||
segment: null,
|
||||
truckType: null,
|
||||
@@ -605,6 +606,14 @@
|
||||
<div class="modal-footer custom-footer">
|
||||
<Spiner v-if="isLoading"/>
|
||||
<div v-else class="btns-footer">
|
||||
<div class="switch-container">
|
||||
<CustomSwitch
|
||||
v-if="loadStore?.currentLoad == null || loadStore.currentLoad?.status === 'Draft'"
|
||||
label='Publicar como privada'
|
||||
v-model="isPrivate"
|
||||
name="contacts"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
@@ -651,20 +660,22 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.custom-footer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
color: red;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.custom-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.btns-footer {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chekmark {
|
||||
@@ -690,5 +701,15 @@
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.btns-footer {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.switch-container {
|
||||
flex: 1 1 100%;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,6 +2,9 @@
|
||||
import { RouterLink } from 'vue-router';
|
||||
import { getDateMonthDay } from '../../../helpers/date_formats';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Swal from 'sweetalert2';
|
||||
import { ref } from 'vue';
|
||||
import { useAuthStore } from '../../../stores/auth';
|
||||
|
||||
defineProps({
|
||||
company: {
|
||||
@@ -10,6 +13,53 @@
|
||||
}
|
||||
});
|
||||
|
||||
const authStore = useAuthStore();
|
||||
let existInPrivateList = ref(false);
|
||||
|
||||
const handleAddPrivateList = async() => {
|
||||
Swal.fire({
|
||||
title: 'Lista privadad',
|
||||
text: '¿Estas seguro de añadir a este transportista de la lista privada?',
|
||||
icon: 'info',
|
||||
showCancelButton: true,
|
||||
cancelButtonColor: "#d33",
|
||||
confirmButtonText: t('buttons.yes'),
|
||||
cancelButtonText: t('buttons.no'),
|
||||
}).then(async(result) => {
|
||||
if(result.isConfirmed) {
|
||||
Swal.fire({
|
||||
title: t('messages.loading'),
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading()
|
||||
},
|
||||
});
|
||||
let resp = null;
|
||||
setTimeout(() => {
|
||||
resp = true
|
||||
Swal.close();
|
||||
if(resp != null) {
|
||||
// contactsStore.removeContact(id);
|
||||
existInPrivateList.value = true;
|
||||
Swal.fire({
|
||||
title: 'Transportista añadido',
|
||||
text: 'Se ha añadido este transportista a la lista privada',
|
||||
icon: "success"
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: t('errors.msgTitleNotDel'),
|
||||
text: 'No se pudo completar la añadir este transportista a la lista privada',
|
||||
icon: "error"
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
@@ -30,9 +80,18 @@
|
||||
<p><span>{{ t('labels.infoCompany') }}: </span>{{company.company_description}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<div
|
||||
class="d-flex justify-content-end gap-2"
|
||||
v-if="authStore.user?.permissions === 'role_shipper'">
|
||||
<button
|
||||
v-if="!existInPrivateList"
|
||||
class="btn-primary-sm bg-dark radius-sm"
|
||||
@click="handleAddPrivateList"
|
||||
>
|
||||
Añadir a lista privada
|
||||
</button>
|
||||
<RouterLink
|
||||
class="btn-primary-sm"
|
||||
class="btn-primary-sm radius-sm"
|
||||
:to="{name: 'public-users', params: {id: company._id}}"
|
||||
>{{ t('buttons.profile') }}</RouterLink>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user