fix: issues ux
This commit is contained in:
@@ -85,6 +85,18 @@ body {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn-primary-nav {
|
||||
background-color: #FBBA33;
|
||||
padding: 8px 16px;
|
||||
color: #323030;
|
||||
font-size: 1.1rem;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
border-radius: 8px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn-primary-sm:hover {
|
||||
background-color: #e3a11e;
|
||||
transition: background-color 300ms ease;
|
||||
|
||||
@@ -164,7 +164,6 @@
|
||||
Evidencias
|
||||
</button>
|
||||
<button
|
||||
v-if="load.load_status !== 'Delivered'"
|
||||
class="btn-primary-sm"
|
||||
data-toggle="modal" data-target="#formLoadModal"
|
||||
@click="openEditModal"
|
||||
|
||||
@@ -25,16 +25,16 @@
|
||||
onMounted(() => {
|
||||
|
||||
props.data.forEach(item => {
|
||||
const index = dataMap.value.findIndex((e) => e.label === item);
|
||||
|
||||
if(index === -1) {
|
||||
const index = dataMap.value.findIndex((e) => e.name?.toUpperCase() === item?.toUpperCase());
|
||||
if(index === -1) { // Lo agrega
|
||||
if(props.dataModel) {
|
||||
const itemModel = props.dataModel.find((e) => e[props.targetFind] === item);
|
||||
dataMap.value.push({
|
||||
const itemModel = props.dataModel.find((e) => e[props.targetFind].toUpperCase() === item.toUpperCase());
|
||||
const itemTem = {
|
||||
label: (props.targetLabel) ? itemModel[props.targetLabel] : item,
|
||||
data: 1,
|
||||
...itemModel
|
||||
})
|
||||
};
|
||||
dataMap.value.push(itemTem)
|
||||
} else {
|
||||
dataMap.value.push({
|
||||
label: item,
|
||||
@@ -42,11 +42,10 @@
|
||||
color: 'green'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
} else { // Lo actualiza
|
||||
dataMap.value[index].data += 1;
|
||||
}
|
||||
});
|
||||
|
||||
chartData.value = {
|
||||
labels: dataMap.value.map((e) => e.label),
|
||||
datasets: [{
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
<RouterLink
|
||||
v-if="auth.user?.permissions.includes('role_carrier')"
|
||||
active-class="router-link-active"
|
||||
class="nav-link" :to="{name: 'search-loads'}">Cargas</RouterLink>
|
||||
class="nav-link" :to="{name: 'search-loads'}"> <i class="fa-solid fa-truck-ramp-box me-1"></i> Cargas</RouterLink>
|
||||
<RouterLink
|
||||
v-if="auth.user?.permissions.includes('role_carrier')"
|
||||
active-class="router-link-active"
|
||||
class="nav-link" :to="{name: 'shippers'}">Embarcadores</RouterLink>
|
||||
class="nav-link" :to="{name: 'shippers'}"><i class="fa-solid fa-book me-1"></i> Embarcadores</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -40,17 +40,21 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.custom-navbar {
|
||||
display: block;
|
||||
width: calc(100vw - 220px);
|
||||
background-color: #FFF;
|
||||
padding: 16px 0px;
|
||||
}
|
||||
|
||||
.nav-options {
|
||||
margin-left: 32px;
|
||||
display: flex;
|
||||
margin-left: 32px;
|
||||
gap: 1rem;
|
||||
margin-right: 32px;
|
||||
}
|
||||
|
||||
.btn-menu {
|
||||
@@ -59,8 +63,8 @@
|
||||
|
||||
.nav-link{
|
||||
cursor: pointer;
|
||||
color: #413f3c;
|
||||
font-size: 1.2rem;
|
||||
color: #FBBA33;
|
||||
font-size: 1.3rem;
|
||||
margin-right: 1.2rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -83,6 +87,18 @@
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
||||
.nav-options {
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-link{
|
||||
font-size: 1.1rem;
|
||||
margin-right: 1.2rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.custom-navbar {
|
||||
width: 100vw !important;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
<nav id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<div class="logo">
|
||||
<a href="https://etaviaporte.com/" target="_blank">
|
||||
<img src="/images/logo-eta.png" alt="Eta viaporte" width="120">
|
||||
</a>
|
||||
</div>
|
||||
<h2 class="my-4">COVO</h2>
|
||||
<p><i class="fa-solid fa-user"></i> <span class="ms-2">{{ auth.user?.first_name + ' ' + auth.user?.last_name }}</span></p>
|
||||
@@ -84,7 +86,7 @@
|
||||
<i class="fa-solid fa-bullhorn" :class="[route.name === 'published-trucks' ? 'router-link-active' : '']"></i>
|
||||
<RouterLink
|
||||
active-class=""
|
||||
class="nav-link" :to="{name: 'published-trucks'}">Publicaciones</RouterLink>
|
||||
class="nav-link" :to="{name: 'published-trucks'}">Ofertas aceptadas</RouterLink>
|
||||
</div>
|
||||
</li>
|
||||
<li :class="[route.name === 'calendar' ? 'bg-nav-active' : '']">
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
import axios from "axios";
|
||||
|
||||
const baseUrl = import.meta.env.VITE_API_URL;
|
||||
const accessToken = localStorage.getItem('access');
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: baseUrl,
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + accessToken
|
||||
},
|
||||
baseURL: baseUrl
|
||||
});
|
||||
|
||||
// Interceptar las solicitudes antes de enviarlas
|
||||
api.interceptors.request.use(
|
||||
(config) => {
|
||||
// Obtener el token de acceso actualizado del localStorage
|
||||
const accessToken = localStorage.getItem('access');
|
||||
|
||||
// Verificar si hay un token de acceso y agregarlo al encabezado de autorización
|
||||
if (accessToken) {
|
||||
config.headers.Authorization = `Bearer ${accessToken}`;
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
// Manejar errores de solicitud
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default api;
|
||||
9
src/lib/axiosPublic.js
Normal file
9
src/lib/axiosPublic.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import axios from "axios";
|
||||
|
||||
const baseUrl = import.meta.env.VITE_API_URL;
|
||||
|
||||
const apiPublic = axios.create({
|
||||
baseURL: baseUrl,
|
||||
});
|
||||
|
||||
export default apiPublic;
|
||||
@@ -1,12 +1,10 @@
|
||||
import api from "../lib/axios";
|
||||
import apiPublic from "../lib//axiosPublic";
|
||||
import {messagesError} from '../helpers/validations';
|
||||
|
||||
export const login = async(body) => {
|
||||
try {
|
||||
const endpoint = "/v1/account/authorize";
|
||||
const {data} = await api.post(endpoint, body);
|
||||
console.log(data);
|
||||
console.log(data.accessToken);
|
||||
const {data} = await apiPublic.post(endpoint, body);
|
||||
if(data.accessToken !== null){
|
||||
if(data.user.job_role !== 'driver'){
|
||||
//TODO: Guardar token y datos del usuario
|
||||
@@ -37,7 +35,7 @@ export const renewToken = async() => {
|
||||
const session = localStorage.getItem('session');
|
||||
try {
|
||||
const endpoint = `/v1/account/authorize/${session}`;
|
||||
const {data} = await api.get(endpoint);
|
||||
const {data} = await apiPublic.get(endpoint);
|
||||
console.log(data);
|
||||
if(data.accessToken !== null){
|
||||
return {
|
||||
@@ -63,7 +61,7 @@ export const renewToken = async() => {
|
||||
export const regiter = async(body) => {
|
||||
try {
|
||||
const endpoint = "/v1/account/signup";
|
||||
const {data} = await api.post(endpoint, body);
|
||||
const {data} = await apiPublic.post(endpoint, body);
|
||||
return {
|
||||
msg: 'success',
|
||||
data
|
||||
@@ -79,7 +77,7 @@ export const regiter = async(body) => {
|
||||
export const regiterConfirm = async(body) => {
|
||||
try {
|
||||
const endpoint = "/v1/account/signup";
|
||||
const {data} = await api.patch(endpoint, body);
|
||||
const {data} = await apiPublic.patch(endpoint, body);
|
||||
return {
|
||||
msg: 'success',
|
||||
data
|
||||
@@ -103,7 +101,7 @@ export const regiterConfirm = async(body) => {
|
||||
export const recoveryPassword = async(body) => {
|
||||
try {
|
||||
const endpoint = "/v1/account/recover";
|
||||
const {data} = await api.post(endpoint, body);
|
||||
const {data} = await apiPublic.post(endpoint, body);
|
||||
return {
|
||||
msg: 'success',
|
||||
data
|
||||
@@ -120,7 +118,7 @@ export const recoveryPassword = async(body) => {
|
||||
export const recoveryPasswordConfirm = async(body) => {
|
||||
try {
|
||||
const endpoint = "/v1/account/recover";
|
||||
const {data} = await api.patch(endpoint, body);
|
||||
const {data} = await apiPublic.patch(endpoint, body);
|
||||
return {
|
||||
msg: 'success',
|
||||
data
|
||||
|
||||
@@ -5,6 +5,7 @@ import { renewToken } from '../services/auth';
|
||||
import {useNotificationsStore} from './notifications';
|
||||
import {useCompanyStore} from './company';
|
||||
import { useLoadsStore } from "./loads";
|
||||
import { useVehiclesStore } from "./vehicles";
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
|
||||
@@ -12,6 +13,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
const noty = useNotificationsStore();
|
||||
const company = useCompanyStore();
|
||||
const loadStore = useLoadsStore();
|
||||
const vehiclesStore = useVehiclesStore();
|
||||
const sesion = ref('')
|
||||
const checking = ref(false);
|
||||
const authStatus = ref('checking');
|
||||
@@ -54,16 +56,20 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
});
|
||||
|
||||
const logout = () => {
|
||||
console.log('logoo....');
|
||||
sesion.value = '';
|
||||
token.value = '';
|
||||
company.clear();
|
||||
localStorage.clear();
|
||||
user.value = null;
|
||||
console.log(company.company);
|
||||
localStorage.removeItem('access');
|
||||
localStorage.removeItem('id');
|
||||
localStorage.removeItem('session');
|
||||
|
||||
sesion.value = '';
|
||||
token.value = '';
|
||||
user.value = null;
|
||||
checking.value = false;
|
||||
authStatus.value = 'checking'
|
||||
|
||||
company.clear();
|
||||
loadStore.clear();
|
||||
vehiclesStore.clear();
|
||||
|
||||
router.push({name: 'login'});
|
||||
}
|
||||
|
||||
|
||||
@@ -139,15 +139,19 @@ export const useCompanyStore = defineStore('company', () => {
|
||||
const clear = () => { //Cuando se cierra la sesion
|
||||
company.value = null;
|
||||
users.value = [];
|
||||
drivers.value = [];
|
||||
usersTotal.value = 0;
|
||||
usersCurrentPage.value = 1;
|
||||
drivers.value = [];
|
||||
budgets.value = [];
|
||||
proposals.value = [];
|
||||
budgetsTotal.value = 0;
|
||||
budgetsCurrentPage.value = 1;
|
||||
locations.value = [];
|
||||
locationsLoads.value = [];
|
||||
locationsTotal.value = 0;
|
||||
locationsCurrentPage.value = 1;
|
||||
proposals.value = [];
|
||||
proposalsTotal.value = 0;
|
||||
proposalsCurrentPage.value = 1;
|
||||
// companyid = null;
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export const useLoadsStore = defineStore('load', () => {
|
||||
|
||||
const currentLoad = ref(null);
|
||||
const loads = ref([])
|
||||
const loadsDashboard = ref([]);
|
||||
const loadsTotal = ref(0)
|
||||
const loadsCurrentPage = ref(1)
|
||||
const proposalsOfLoads = ref([]);
|
||||
@@ -13,6 +14,32 @@ export const useLoadsStore = defineStore('load', () => {
|
||||
const openAttachmentsModal = ref(false);
|
||||
const openProposalsModal = ref(false);
|
||||
|
||||
const getLoadsAll = async(reload = false) => {
|
||||
const companyid = localStorage.getItem('id');
|
||||
if(loadsDashboard.value.length <= 0 || reload) {
|
||||
try {
|
||||
const endpoint = `/loads?company=${companyid}`;
|
||||
const {data} = await api.get(endpoint);
|
||||
loadsDashboard.value = data.data;
|
||||
} catch (error) {
|
||||
loadsDashboard.value = [];
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getProposalCompanyAll = async(reload = false) => {
|
||||
const companyId = localStorage.getItem('id');
|
||||
try {
|
||||
if(loadsDashboard.value.length <= 0 || reload) {
|
||||
const endpoint = `/proposals?carrier=${companyId}`;
|
||||
const {data} = await api.get(endpoint);
|
||||
loadsDashboard.value = data.data.map( (e) => e.load);
|
||||
}
|
||||
} catch (error) {
|
||||
loadsDashboard.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
const getCompanyLoads = async(filterQuery, reload = false) => {
|
||||
const companyid = localStorage.getItem('id');
|
||||
@@ -123,6 +150,9 @@ export const useLoadsStore = defineStore('load', () => {
|
||||
const clear = () => {
|
||||
currentLoad.value = null;
|
||||
loads.value = [];
|
||||
loadsDashboard.value = [];
|
||||
loadsTotal.value = 0;
|
||||
loadsCurrentPage.value = 1;
|
||||
proposalsOfLoads.value = [];
|
||||
openModalEdit.value = false;
|
||||
openAttachmentsModal.value = false;
|
||||
@@ -137,6 +167,9 @@ export const useLoadsStore = defineStore('load', () => {
|
||||
openAttachmentsModal,
|
||||
getProposalsOfLoads,
|
||||
getCompanyLoads,
|
||||
getLoadsAll,
|
||||
getProposalCompanyAll,
|
||||
loadsDashboard,
|
||||
deleteLoad,
|
||||
getLoad,
|
||||
saveLoad,
|
||||
|
||||
@@ -68,6 +68,11 @@ export const useVehiclesStore = defineStore('vehicles', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
vehicles.value = [];
|
||||
vehiclesTotal.value = 0;
|
||||
vehiclesCurrentPage.value = 1;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
@@ -77,6 +82,7 @@ export const useVehiclesStore = defineStore('vehicles', () => {
|
||||
fetchVehicles,
|
||||
createVehicleCompany,
|
||||
updateVehicleCompany,
|
||||
deleteVehicleCompany
|
||||
deleteVehicleCompany,
|
||||
clear
|
||||
}
|
||||
});
|
||||
@@ -3,16 +3,80 @@
|
||||
import loadsType from '../data/loadsType.json';
|
||||
import BarChartStatistics from '../components/BarChartStatistics.vue';
|
||||
import DoughnutChartStatistics from '../components/DoughnutChartStatistics.vue';
|
||||
import { useLoadsStore } from '../stores/loads';
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import Spiner from '../components/ui/Spiner.vue';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
|
||||
const auth = useAuthStore();
|
||||
const { checking, user } = storeToRefs(auth);
|
||||
const loads = useLoadsStore();
|
||||
const loading = ref(false);
|
||||
const loadsData = ref([]);
|
||||
const segmentsData = ref([]);
|
||||
const cities = ref([]);
|
||||
const states = ref([]);
|
||||
const vehicles = ref([]);
|
||||
const nOfLoads = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
if(user.value) {
|
||||
getData();
|
||||
}
|
||||
})
|
||||
|
||||
watch(user, async() => {
|
||||
if(user.value) {
|
||||
getData();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const getData = async() => {
|
||||
loading.value = true;
|
||||
if(user.value?.permissions?.includes("role_carrier")) {
|
||||
await loads.getProposalCompanyAll();
|
||||
} else {
|
||||
await loads.getLoadsAll();
|
||||
}
|
||||
dataMap();
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
|
||||
const dataMap = () => {
|
||||
nOfLoads.value = loads.loadsDashboard.length;
|
||||
loads.loadsDashboard.map((e) => {
|
||||
if(e?.load_status) {
|
||||
loadsData.value.push(e.load_status);
|
||||
}
|
||||
if(user.value?.permissions?.includes("role_shipper") && e?.categories) {
|
||||
segmentsData.value.push(e?.categories[0].name)
|
||||
}
|
||||
if(user.value?.permissions?.includes("role_shipper") && e?.origin?.city) {
|
||||
cities.value.push(e?.origin.city)
|
||||
}
|
||||
if(e?.origin?.state){
|
||||
states.value.push(e?.origin.state)
|
||||
}
|
||||
if(e?.truck_type){
|
||||
vehicles.value.push(e?.truck_type)
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="title my-4">Dashboard Administrativo</h1>
|
||||
<div class="container-dashboard">
|
||||
<div class="card-fixed card-dashboard">
|
||||
<div class="card-fixed"
|
||||
:class="[user?.permissions?.includes('role_shipper') ? 'card-dashboard' : 'card-dashboard-carrier']"
|
||||
>
|
||||
<h3>Total de cargas este mes</h3>
|
||||
<div class="main-info">
|
||||
35
|
||||
{{ nOfLoads }}
|
||||
<div class="indicator-text" style="color: green;">
|
||||
<i class="fa-solid fa-arrow-up"></i>
|
||||
<!-- <i class="fa-solid fa-arrow-down"></i> -->
|
||||
@@ -21,22 +85,30 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- <ChartLoad/> -->
|
||||
<div class="card-fixed card-dashboard">
|
||||
<div class="card-fixed"
|
||||
:class="[user?.permissions?.includes('role_shipper') ? 'card-dashboard' : 'card-dashboard-carrier']"
|
||||
>
|
||||
<h3>Cargas activas</h3>
|
||||
<div class="card-chart">
|
||||
<Spiner v-if="loading"/>
|
||||
<DoughnutChartStatistics
|
||||
:data="['Published', 'Transit', 'Delivered', 'Published', 'Downloading', 'Loading']"
|
||||
v-else
|
||||
:data="loadsData"
|
||||
:data-model="loadsType"
|
||||
target-find="name"
|
||||
target-label="status"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-fixed card-dashboard">
|
||||
<div class="card-fixed"
|
||||
:class="[user?.permissions?.includes('role_shipper') ? 'card-dashboard' : 'card-dashboard-carrier']"
|
||||
v-if="user?.permissions?.includes('role_shipper')">
|
||||
<h3>Segmentos más usados</h3>
|
||||
<div class="card-chart">
|
||||
<Spiner v-if="loading"/>
|
||||
<DoughnutChartStatistics
|
||||
:data="['Agricola', 'Agricola', 'Cemento', 'Agricola', 'Intermoadal', 'Agricola']"
|
||||
v-else
|
||||
:data="segmentsData"
|
||||
:data-model="segments"
|
||||
target-find="name"
|
||||
/>
|
||||
@@ -44,30 +116,42 @@
|
||||
</div>
|
||||
<!-- </div>
|
||||
<div class="container-dashboard"> -->
|
||||
<div class="card-fixed card-dashboard">
|
||||
<div class="card-fixed"
|
||||
:class="[user?.permissions?.includes('role_shipper') ? 'card-dashboard' : 'card-dashboard-carrier']"
|
||||
>
|
||||
<h3>Estados más usados</h3>
|
||||
<div class="card-chart">
|
||||
<Spiner v-if="loading"/>
|
||||
<BarChartStatistics
|
||||
label="Ciuades"
|
||||
:data="['Yucatán', 'Guadalajara', 'Colima', 'Yucatán', 'Nuevo león', 'Yucatán', 'Guadalajara']"
|
||||
v-else
|
||||
label="Estados"
|
||||
:data="states"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-fixed card-dashboard">
|
||||
<div class="card-fixed"
|
||||
:class="[user?.permissions?.includes('role_shipper') ? 'card-dashboard' : 'card-dashboard-carrier']"
|
||||
v-if="user?.permissions?.includes('role_shipper')">
|
||||
<h3>Ciudades más usadas</h3>
|
||||
<div class="card-chart">
|
||||
<Spiner v-if="loading"/>
|
||||
<BarChartStatistics
|
||||
label="Estados"
|
||||
:data="['Mérida', 'Guadalajara', 'Colima', 'Guadalajara', 'Monterrey', 'Izamal', 'Mérida']"
|
||||
v-else
|
||||
label="Ciudades"
|
||||
:data="cities"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-fixed card-dashboard">
|
||||
<div class="card-fixed"
|
||||
:class="[user?.permissions?.includes('role_shipper') ? 'card-dashboard' : 'card-dashboard-carrier']"
|
||||
>
|
||||
<h3>Tipo de transporte más usados</h3>
|
||||
<div class="card-chart">
|
||||
<Spiner v-if="loading"/>
|
||||
<BarChartStatistics
|
||||
v-else
|
||||
label="Vehiculos"
|
||||
:data="['FULL / DOBLE REMOLQUE', 'FULL', 'FULL / DOBLE REMOLQUE', 'FULL', 'FULL / DOBLE REMOLQUE', 'FULL', 'FULL / DOBLE REMOLQUE', 'Auto']"
|
||||
:data="vehicles"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,8 +176,17 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-dashboard-carrier {
|
||||
width: 48%;
|
||||
min-height: 300px;
|
||||
max-height: 500px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-chart {
|
||||
width: 100%;
|
||||
/* max-height: 350px; */
|
||||
/* max-width: 333px; */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -138,5 +231,9 @@
|
||||
.card-dashboard {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-dashboard-carrier {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user