Modulo Administración #78
|
@ -1,10 +1,10 @@
|
|||
import { boot } from 'quasar/wrappers'
|
||||
import { appStore } from 'stores/app'
|
||||
import { userStore } from 'stores/user'
|
||||
import { boot } from 'quasar/wrappers';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { useUserStore } from 'stores/user';
|
||||
|
||||
export default boot(({ app }) => {
|
||||
const props = app.config.globalProperties
|
||||
props.$app = appStore()
|
||||
props.$user = userStore()
|
||||
props.$actions = document.createElement('div')
|
||||
})
|
||||
const props = app.config.globalProperties;
|
||||
const userStore = useUserStore();
|
||||
props.$app = useAppStore();
|
||||
props.$user = userStore.user;
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { boot } from 'quasar/wrappers';
|
||||
import { Connection } from '../js/db/connection';
|
||||
import { userStore } from 'stores/user';
|
||||
import { useUserStore } from 'stores/user';
|
||||
import axios from 'axios';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
|
||||
|
@ -36,10 +36,10 @@ const onResponseError = error => {
|
|||
};
|
||||
|
||||
export default boot(({ app }) => {
|
||||
const user = userStore();
|
||||
const userStore = useUserStore();
|
||||
function addToken(config) {
|
||||
if (user.token) {
|
||||
config.headers.Authorization = user.token;
|
||||
if (userStore.token) {
|
||||
config.headers.Authorization = userStore.token;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
import { ref, inject, onMounted, computed, Teleport } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import {
|
||||
generateUpdateSqlQuery,
|
||||
|
@ -81,6 +83,8 @@ const emit = defineEmits(['onDataSaved']);
|
|||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const { notify } = useNotify();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const loading = ref(false);
|
||||
const formData = ref({});
|
||||
|
@ -191,13 +195,14 @@ defineExpose({
|
|||
</span>
|
||||
<slot name="form" :data="formData" />
|
||||
<component
|
||||
v-if="isHeaderMounted"
|
||||
:is="showBottomActions ? 'div' : Teleport"
|
||||
:to="$actions"
|
||||
to="#actions"
|
||||
class="flex row justify-end q-gutter-x-sm"
|
||||
:class="{ 'q-mt-md': showBottomActions }"
|
||||
>
|
||||
<QBtn
|
||||
v-if="defaultActions"
|
||||
v-if="defaultActions && showBottomActions"
|
||||
:label="t('cancel')"
|
||||
:icon="showBottomActions ? undefined : 'check'"
|
||||
rounded
|
||||
|
|
|
@ -9,7 +9,7 @@ const emit = defineEmits([
|
|||
'remove'
|
||||
]);
|
||||
|
||||
const $props = defineProps({
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
|
@ -33,7 +33,7 @@ const requiredFieldRule = val => !!val || t('globals.fieldRequired');
|
|||
const vnInputRef = ref(null);
|
||||
const value = computed({
|
||||
get() {
|
||||
return $props.modelValue;
|
||||
return props.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value);
|
||||
|
@ -41,7 +41,7 @@ const value = computed({
|
|||
});
|
||||
const hover = ref(false);
|
||||
const styleAttrs = computed(() => {
|
||||
return $props.isOutlined
|
||||
return props.isOutlined
|
||||
? { dense: true, outlined: true, rounded: true }
|
||||
: {};
|
||||
});
|
||||
|
@ -88,9 +88,7 @@ const inputRules = [
|
|||
<template #append>
|
||||
<slot v-if="$slots.append && !$attrs.disabled" name="append" />
|
||||
<QIcon
|
||||
v-if="
|
||||
hover && value && !$attrs.disabled && $props.clearable
|
||||
"
|
||||
v-if="hover && value && !$attrs.disabled && props.clearable"
|
||||
name="close"
|
||||
size="xs"
|
||||
@click="
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<script setup>
|
||||
const props = defineProps({
|
||||
clickable: { type: Boolean, default: true },
|
||||
rounded: { type: Boolean, default: true }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['click']);
|
||||
|
||||
const handleClick = () => {
|
||||
if (props.clickable) {
|
||||
emit('click');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QItem
|
||||
v-bind="$attrs"
|
||||
v-ripple="clickable"
|
||||
:clickable="clickable"
|
||||
class="full-width row items-center justify-between card no-border-radius bg-white"
|
||||
:class="{ 'cursor-pointer': clickable, 'no-radius': !rounded }"
|
||||
@click="handleClick()"
|
||||
>
|
||||
<QItemSection class="no-padding">
|
||||
<div class="row no-wrap">
|
||||
<slot name="prepend" />
|
||||
<div class="column full-width">
|
||||
<slot name="content" />
|
||||
</div>
|
||||
</div>
|
||||
</QItemSection>
|
||||
<QItemSection class="no-padding" side>
|
||||
<slot name="actions" />
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
border-bottom: 1px solid $gray-light;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
|
@ -5,8 +5,10 @@ import { useI18n } from 'vue-i18n';
|
|||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnForm from 'src/components/common/VnForm.vue';
|
||||
|
||||
import { userStore as useUserStore } from 'stores/user';
|
||||
import { useUserStore } from 'stores/user';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const props = defineProps({
|
||||
verificationToken: {
|
||||
|
@ -24,6 +26,8 @@ const { t } = useI18n();
|
|||
const api = inject('api');
|
||||
const userStore = useUserStore();
|
||||
const { notify } = useNotify();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const oldPasswordRef = ref(null);
|
||||
const newPasswordRef = ref(null);
|
||||
|
@ -33,7 +37,7 @@ const repeatPassword = ref('');
|
|||
const passwordRequirements = ref(null);
|
||||
|
||||
const formData = ref({
|
||||
userId: userStore.id,
|
||||
userId: userStore.user.id,
|
||||
oldPassword: '',
|
||||
newPassword: ''
|
||||
});
|
||||
|
@ -75,7 +79,7 @@ const getPasswordRequirements = async () => {
|
|||
};
|
||||
|
||||
const login = async () => {
|
||||
await userStore.login(userStore.name, formData.value.newPassword);
|
||||
await userStore.login(userStore.user.name, formData.value.newPassword);
|
||||
};
|
||||
|
||||
const onPasswordChanged = async () => {
|
||||
|
@ -112,40 +116,40 @@ onMounted(async () => {
|
|||
v-model="formData.oldPassword"
|
||||
:type="!showOldPwd ? 'password' : 'text'"
|
||||
:label="t('oldPassword')"
|
||||
>
|
||||
>
|
||||
<template #append>
|
||||
<QIcon
|
||||
:name="showOldPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showOldPwd = !showOldPwd"
|
||||
/>
|
||||
</template>
|
||||
</VnInput>
|
||||
<QIcon
|
||||
:name="showOldPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showOldPwd = !showOldPwd"
|
||||
/>
|
||||
</template>
|
||||
</VnInput>
|
||||
<VnInput
|
||||
ref="newPasswordRef"
|
||||
v-model="formData.newPassword"
|
||||
:type="!showNewPwd ? 'password' : 'text'"
|
||||
:label="t('newPassword')"
|
||||
><template #append>
|
||||
<QIcon
|
||||
:name="showNewPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showNewPwd = !showNewPwd"
|
||||
/>
|
||||
</template></VnInput>
|
||||
><template #append>
|
||||
<QIcon
|
||||
:name="showNewPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showNewPwd = !showNewPwd"
|
||||
/> </template
|
||||
></VnInput>
|
||||
<VnInput
|
||||
v-model="repeatPassword"
|
||||
:type="!showCopyPwd ? 'password' : 'text'"
|
||||
:label="t('repeatPassword')"
|
||||
><template #append>
|
||||
<QIcon
|
||||
:name="showCopyPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showCopyPwd = !showCopyPwd"
|
||||
/>
|
||||
</template></VnInput>
|
||||
><template #append>
|
||||
<QIcon
|
||||
:name="showCopyPwd ? 'visibility_off' : 'visibility'"
|
||||
class="cursor-pointer"
|
||||
@click="showCopyPwd = !showCopyPwd"
|
||||
/> </template
|
||||
></VnInput>
|
||||
</template>
|
||||
<template #actions>
|
||||
<template v-if="isHeaderMounted" #actions>
|
||||
<QBtn
|
||||
:label="t('requirements')"
|
||||
rounded
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { appStore } from 'stores/app';
|
||||
import { useAppStore } from 'stores/app';
|
||||
|
||||
const $props = defineProps({
|
||||
const props = defineProps({
|
||||
baseURL: {
|
||||
type: String,
|
||||
default: null
|
||||
|
@ -23,23 +23,27 @@ const $props = defineProps({
|
|||
id: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
rounded: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
const app = appStore();
|
||||
const app = useAppStore();
|
||||
const show = ref(false);
|
||||
const url = computed(() => {
|
||||
return `${$props.baseURL ?? app.imageUrl}/${$props.storage}/${$props.size}/${$props.id}`;
|
||||
return `${props.baseURL ?? app.imageUrl}/${props.storage}/${props.size}/${props.id}`;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<QImg
|
||||
:class="{ zoomIn: $props.zoomSize }"
|
||||
:class="{ zoomIn: props.zoomSize, rounded: props.rounded }"
|
||||
:src="url"
|
||||
v-bind="$attrs"
|
||||
@click="show = !show"
|
||||
spinner-color="primary"
|
||||
/>
|
||||
<QDialog v-model="show" v-if="$props.zoomSize">
|
||||
<QDialog v-model="show" v-if="props.zoomSize">
|
||||
<QImg
|
||||
:src="url"
|
||||
size="full"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// app global css in SCSS form
|
||||
|
||||
@font-face {
|
||||
font-family: Poppins;
|
||||
src: url(./poppins.ttf) format('truetype');
|
||||
font-family: Poppins;
|
||||
src: url(./poppins.ttf) format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
|
@ -36,3 +36,9 @@ a.link {
|
|||
.q-page-sticky.fixed-bottom-right {
|
||||
margin: 18px;
|
||||
}
|
||||
.no-border-radius {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.no-padding {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
$primary: #1a1a1a;
|
||||
$secondary: #26a69a;
|
||||
$accent: #8cc63f;
|
||||
|
||||
$gray-light: #ddd;
|
||||
$dark: #1d1d1d;
|
||||
$dark-page: #121212;
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
export default {
|
||||
date: {
|
||||
days: [
|
||||
'Diumenge',
|
||||
'Dilluns',
|
||||
'Dimarts',
|
||||
'Dimecres',
|
||||
'Dijous',
|
||||
'Divendres',
|
||||
'Dissabte'
|
||||
],
|
||||
daysShort: ['Dg', 'Dl', 'Dt', 'Dc', 'Dj', 'Dv', 'Ds'],
|
||||
months: [
|
||||
'Gener',
|
||||
'Febrer',
|
||||
'Març',
|
||||
'Abril',
|
||||
'Maig',
|
||||
'Juny',
|
||||
'Juliol',
|
||||
'Agost',
|
||||
'Setembre',
|
||||
'Octubre',
|
||||
'Novembre',
|
||||
'Desembre'
|
||||
],
|
||||
shortMonths: [
|
||||
'Gen',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Des'
|
||||
]
|
||||
},
|
||||
of: 'de',
|
||||
// Menu
|
||||
home: 'Inici',
|
||||
catalog: 'Catàleg',
|
||||
pendingOrders: 'Comandes pendents',
|
||||
confirmedOrders: 'Comandes confirmades',
|
||||
invoices: 'Factures',
|
||||
agencyPackages: 'Paquets per agència',
|
||||
accountConfig: 'Configuració',
|
||||
addressesList: 'Adreces',
|
||||
addressDetails: 'Configuració',
|
||||
checkout: 'Configurar encàrrec',
|
||||
controlPanel: 'Panell de control',
|
||||
adminConnections: 'Connexions',
|
||||
//
|
||||
orderLoadedIntoBasket: 'Comanda carregada a la cistella!'
|
||||
};
|
|
@ -56,6 +56,19 @@ export default {
|
|||
// menu
|
||||
home: 'Home',
|
||||
catalog: 'Catalog',
|
||||
pendingOrders: 'Pending orders',
|
||||
confirmedOrders: 'Confirmed orders',
|
||||
invoices: 'Invoices',
|
||||
agencyPackages: 'Bundles by agency',
|
||||
accountConfig: 'Configuration',
|
||||
addressesList: 'Addresses',
|
||||
addressDetails: 'Configuration',
|
||||
checkout: 'Configure order',
|
||||
controlPanel: 'Control panel',
|
||||
adminConnections: 'Connections',
|
||||
//
|
||||
orderLoadedIntoBasket: 'Order loaded into basket!',
|
||||
|
||||
orders: 'Orders',
|
||||
order: 'Pending order',
|
||||
ticket: 'Order',
|
||||
|
@ -76,5 +89,6 @@ export default {
|
|||
addressEdit: 'Edit address',
|
||||
dataSaved: 'Data saved',
|
||||
save: 'Save',
|
||||
cancel: 'Cancel'
|
||||
cancel: 'Cancel',
|
||||
of: 'of'
|
||||
};
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
// This is just an example,
|
||||
// so you can safely delete all default props below
|
||||
|
||||
export default {
|
||||
failed: 'Acción fallida',
|
||||
success: 'Acción exitosa',
|
||||
|
@ -65,6 +62,19 @@ export default {
|
|||
// Menu
|
||||
home: 'Inicio',
|
||||
catalog: 'Catálogo',
|
||||
pendingOrders: 'Pedidos pendientes',
|
||||
confirmedOrders: 'Pedidos confirmados',
|
||||
invoices: 'Facturas',
|
||||
agencyPackages: 'Bultos por agencia',
|
||||
accountConfig: 'Configuración',
|
||||
addressesList: 'Direcciones',
|
||||
addressDetails: 'Configuración',
|
||||
checkout: 'Configurar pedido',
|
||||
controlPanel: 'Panel de control',
|
||||
adminConnections: 'Conexiones',
|
||||
//
|
||||
orderLoadedIntoBasket: '¡Pedido cargado en la cesta!',
|
||||
|
||||
orders: 'Pedidos',
|
||||
order: 'Pedido pendiente',
|
||||
ticket: 'Pedido',
|
||||
|
@ -94,5 +104,6 @@ export default {
|
|||
addressEdit: 'Editar dirección',
|
||||
dataSaved: 'Datos guardados',
|
||||
save: 'Guardar',
|
||||
cancel: 'Cancelar'
|
||||
cancel: 'Cancelar',
|
||||
of: 'de'
|
||||
};
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
export default {
|
||||
date: {
|
||||
days: [
|
||||
'Dimanche',
|
||||
'Lundi',
|
||||
'Mardi',
|
||||
'Mercredi',
|
||||
'Jeudi',
|
||||
'Vendredi',
|
||||
'Samedi'
|
||||
],
|
||||
daysShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
|
||||
months: [
|
||||
'Janvier',
|
||||
'Février',
|
||||
'Mars',
|
||||
'Avril',
|
||||
'Mai',
|
||||
'Juin',
|
||||
'Juillet',
|
||||
'Août',
|
||||
'Septembre',
|
||||
'Octobre',
|
||||
'Novembre',
|
||||
'Décembre'
|
||||
],
|
||||
shortMonths: [
|
||||
'Jan',
|
||||
'Fév',
|
||||
'Mar',
|
||||
'Avr',
|
||||
'Mai',
|
||||
'Juin',
|
||||
'Juil',
|
||||
'Aoû',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Déc'
|
||||
]
|
||||
},
|
||||
of: 'de',
|
||||
// Menu
|
||||
home: 'Accueil',
|
||||
catalog: 'Catalogue',
|
||||
pendingOrders: 'Commandes en attente',
|
||||
confirmedOrders: 'Commandes confirmées',
|
||||
invoices: 'Factures',
|
||||
agencyPackages: 'Liste par agence',
|
||||
accountConfig: 'Configuration',
|
||||
addressesList: 'Adresses',
|
||||
addressDetails: 'Configuration',
|
||||
checkout: "Définissez l'ordre",
|
||||
controlPanel: 'Panneau de configuration',
|
||||
adminConnections: 'Connexions',
|
||||
//
|
||||
orderLoadedIntoBasket: 'Commande chargée dans le panier!'
|
||||
};
|
|
@ -1,7 +1,13 @@
|
|||
import enUS from './en-US'
|
||||
import esES from './es-ES'
|
||||
import enUS from './en-US';
|
||||
import esES from './es-ES';
|
||||
import frFR from './fr-FR';
|
||||
import ptPT from './pt-PT';
|
||||
import caES from './ca-ES';
|
||||
|
||||
export default {
|
||||
'en-US': enUS,
|
||||
'es-ES': esES
|
||||
}
|
||||
'es-ES': esES,
|
||||
'fr-FR': frFR,
|
||||
'pt-PT': ptPT,
|
||||
'ca-ES': caES
|
||||
};
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
export default {
|
||||
date: {
|
||||
days: [
|
||||
'Domingo',
|
||||
'Segunda-feira',
|
||||
'Terça-feira',
|
||||
'Quarta-feira',
|
||||
'Quinta-feira',
|
||||
'Sexta-feira',
|
||||
'Sábado'
|
||||
],
|
||||
daysShort: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'],
|
||||
months: [
|
||||
'Janeiro',
|
||||
'Fevereiro',
|
||||
'Março',
|
||||
'Abril',
|
||||
'Maio',
|
||||
'Junho',
|
||||
'Julho',
|
||||
'Agosto',
|
||||
'Setembro',
|
||||
'Outubro',
|
||||
'Novembro',
|
||||
'Dezembro'
|
||||
],
|
||||
shortMonths: [
|
||||
'Jan',
|
||||
'Fev',
|
||||
'Mar',
|
||||
'Abr',
|
||||
'Mai',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Ago',
|
||||
'Set',
|
||||
'Out',
|
||||
'Nov',
|
||||
'Dez'
|
||||
]
|
||||
},
|
||||
of: 'de',
|
||||
|
||||
// Menu
|
||||
home: 'Principio',
|
||||
catalog: 'Catálogo',
|
||||
pendingOrders: 'Pedidos pendentes',
|
||||
confirmedOrders: 'Pedidos confirmados',
|
||||
invoices: 'Facturas',
|
||||
agencyPackages: 'Bultos por agencia',
|
||||
accountConfig: 'Configuração',
|
||||
addressesList: 'Moradas',
|
||||
addressDetails: 'Configuração',
|
||||
checkout: 'Configurar encomenda',
|
||||
controlPanel: 'Painel de controle',
|
||||
adminConnections: 'Conexões',
|
||||
//
|
||||
orderLoadedIntoBasket: 'Pedido carregado na cesta!'
|
||||
};
|
|
@ -1,3 +1,45 @@
|
|||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { useUserStore } from 'stores/user';
|
||||
import { useAppStore } from 'stores/app';
|
||||
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const { user, supplantedUser } = storeToRefs(userStore);
|
||||
const { menuEssentialLinks, title, subtitle, useRightDrawer, rightDrawerOpen } =
|
||||
storeToRefs(appStore);
|
||||
const actions = ref(null);
|
||||
const leftDrawerOpen = ref(false);
|
||||
|
||||
const toggleLeftDrawer = () => {
|
||||
leftDrawerOpen.value = !leftDrawerOpen.value;
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
appStore.isHeaderMounted = true;
|
||||
await userStore.fetchUser();
|
||||
await appStore.loadConfig();
|
||||
await userStore.supplantInit();
|
||||
await appStore.getMenuLinks();
|
||||
});
|
||||
|
||||
const logout = async () => {
|
||||
await userStore.logout();
|
||||
router.push('/login');
|
||||
};
|
||||
|
||||
const logoutSupplantedUser = async () => {
|
||||
await userStore.logoutSupplantedUser();
|
||||
await appStore.getMenuLinks();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QLayout view="lHh Lpr lFf">
|
||||
<QHeader>
|
||||
|
@ -11,15 +53,15 @@
|
|||
@click="toggleLeftDrawer"
|
||||
/>
|
||||
<QToolbarTitle>
|
||||
{{ $app.title }}
|
||||
<div v-if="$app.subtitle" class="subtitle text-caption">
|
||||
{{ $app.subtitle }}
|
||||
{{ title }}
|
||||
<div v-if="subtitle" class="subtitle text-caption">
|
||||
{{ subtitle }}
|
||||
</div>
|
||||
</QToolbarTitle>
|
||||
<div id="actions" ref="actions"></div>
|
||||
<div id="actions" ref="actions" class="flex items-center"></div>
|
||||
<QBtn
|
||||
v-if="$app.useRightDrawer"
|
||||
@click="$app.rightDrawerOpen = !$app.rightDrawerOpen"
|
||||
v-if="useRightDrawer"
|
||||
@click="rightDrawerOpen = !rightDrawerOpen"
|
||||
aria-label="Menu"
|
||||
flat
|
||||
dense
|
||||
|
@ -35,15 +77,22 @@
|
|||
</QToolbar>
|
||||
<div class="user-info">
|
||||
<div>
|
||||
<span id="user-name">{{ user.nickname }}</span>
|
||||
<span id="user-name">{{ user?.nickname }}</span>
|
||||
<QBtn flat icon="logout" alt="_Exit" @click="logout()" />
|
||||
</div>
|
||||
<div id="supplant" class="supplant">
|
||||
<span id="supplanted">{{ supplantedUser }}</span>
|
||||
<QBtn flat icon="logout" alt="_Exit" />
|
||||
<div v-if="supplantedUser" id="supplant" class="supplant">
|
||||
<span id="supplanted">
|
||||
{{ supplantedUser?.nickname }}
|
||||
</span>
|
||||
<QBtn
|
||||
flat
|
||||
icon="logout"
|
||||
alt="_Exit"
|
||||
@click="logoutSupplantedUser()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<QList v-for="item in essentialLinks" :key="item.id">
|
||||
<QList v-for="item in menuEssentialLinks" :key="item.id">
|
||||
<QItem v-if="!item.childs" :to="`/${item.path}`">
|
||||
<QItemSection>
|
||||
<QItemLabel>{{ item.description }}</QItemLabel>
|
||||
|
@ -118,12 +167,7 @@
|
|||
}
|
||||
}
|
||||
&.supplant {
|
||||
display: none;
|
||||
border-top: none;
|
||||
|
||||
&.show {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,10 +187,7 @@
|
|||
.q-page-container > * {
|
||||
padding: 16px;
|
||||
}
|
||||
#actions > div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@include mobile {
|
||||
#actions > div {
|
||||
.q-btn {
|
||||
|
@ -166,71 +207,6 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { userStore } from 'stores/user';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MainLayout',
|
||||
props: {},
|
||||
|
||||
setup() {
|
||||
const leftDrawerOpen = ref(false);
|
||||
|
||||
return {
|
||||
user: userStore(),
|
||||
supplantedUser: ref(''),
|
||||
essentialLinks: ref(null),
|
||||
leftDrawerOpen,
|
||||
toggleLeftDrawer() {
|
||||
leftDrawerOpen.value = !leftDrawerOpen.value;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
this.$refs.actions.appendChild(this.$actions);
|
||||
await this.user.loadData();
|
||||
await this.$app.loadConfig();
|
||||
await this.fetchData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async fetchData() {
|
||||
const sections = await this.$jApi.query('SELECT * FROM myMenu');
|
||||
|
||||
const sectionMap = new Map();
|
||||
for (const section of sections) {
|
||||
sectionMap.set(section.id, section);
|
||||
}
|
||||
|
||||
const sectionTree = [];
|
||||
for (const section of sections) {
|
||||
const parent = section.parentFk;
|
||||
if (parent) {
|
||||
const parentSection = sectionMap.get(parent);
|
||||
if (!parentSection) continue;
|
||||
let childs = parentSection.childs;
|
||||
if (!childs) {
|
||||
childs = parentSection.childs = [];
|
||||
}
|
||||
childs.push(section);
|
||||
} else {
|
||||
sectionTree.push(section);
|
||||
}
|
||||
}
|
||||
|
||||
this.essentialLinks = sectionTree;
|
||||
},
|
||||
|
||||
async logout() {
|
||||
this.user.logout();
|
||||
this.$router.push('/login');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
visitor: Visitor
|
||||
|
|
|
@ -1,73 +1,87 @@
|
|||
import { date as qdate, format } from 'quasar'
|
||||
const { pad } = format
|
||||
import { i18n } from 'src/boot/i18n';
|
||||
import { date as qdate, format } from 'quasar';
|
||||
const { pad } = format;
|
||||
|
||||
export function currency (val) {
|
||||
return typeof val === 'number' ? val.toFixed(2) + '€' : val
|
||||
export function currency(val) {
|
||||
return typeof val === 'number' ? val.toFixed(2) + '€' : val;
|
||||
}
|
||||
|
||||
export function date (val, format) {
|
||||
if (val == null) return val
|
||||
export function date(val, format) {
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val)
|
||||
val = new Date(val);
|
||||
}
|
||||
return qdate.formatDate(val, format, window.i18n.tm('date'))
|
||||
return qdate.formatDate(val, format, i18n.global.tm('date'));
|
||||
}
|
||||
|
||||
export function relDate (val) {
|
||||
if (val == null) return val
|
||||
export const formatDateTitle = timeStamp => {
|
||||
const { t, messages, locale } = i18n.global;
|
||||
const formattedString = qdate.formatDate(
|
||||
timeStamp,
|
||||
`dddd, D [${t('of')}] MMMM [${t('of')}] YYYY`,
|
||||
{
|
||||
days: messages.value[locale.value].date.days,
|
||||
months: messages.value[locale.value].date.months
|
||||
}
|
||||
);
|
||||
return formattedString;
|
||||
};
|
||||
|
||||
export function relDate(val) {
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val)
|
||||
val = new Date(val);
|
||||
}
|
||||
|
||||
const dif = qdate.getDateDiff(new Date(), val, 'days')
|
||||
let day
|
||||
const dif = qdate.getDateDiff(new Date(), val, 'days');
|
||||
let day;
|
||||
|
||||
switch (dif) {
|
||||
case 0:
|
||||
day = 'today'
|
||||
break
|
||||
day = 'today';
|
||||
break;
|
||||
case 1:
|
||||
day = 'yesterday'
|
||||
break
|
||||
day = 'yesterday';
|
||||
break;
|
||||
case -1:
|
||||
day = 'tomorrow'
|
||||
break
|
||||
day = 'tomorrow';
|
||||
break;
|
||||
}
|
||||
|
||||
if (day) {
|
||||
day = window.i18n.t(day)
|
||||
day = i18n.global.t(day);
|
||||
} else {
|
||||
if (dif > 0 && dif <= 7) {
|
||||
day = qdate.formatDate(val, 'ddd', window.i18n.tm('date'))
|
||||
day = qdate.formatDate(val, 'ddd', i18n.global.tm('date'));
|
||||
} else {
|
||||
day = qdate.formatDate(val, 'ddd, MMMM Do', window.i18n.tm('date'))
|
||||
day = qdate.formatDate(val, 'ddd, MMMM Do', i18n.global.tm('date'));
|
||||
}
|
||||
}
|
||||
|
||||
return day
|
||||
return day;
|
||||
}
|
||||
|
||||
export function relTime (val) {
|
||||
if (val == null) return val
|
||||
export function relTime(val) {
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val)
|
||||
val = new Date(val);
|
||||
}
|
||||
return relDate(val) + ' ' + qdate.formatDate(val, 'H:mm:ss')
|
||||
return relDate(val) + ' ' + qdate.formatDate(val, 'H:mm:ss');
|
||||
}
|
||||
|
||||
export function elapsedTime (val) {
|
||||
if (val == null) return val
|
||||
export function elapsedTime(val) {
|
||||
if (val == null) return val;
|
||||
if (!(val instanceof Date)) {
|
||||
val = new Date(val)
|
||||
val = new Date(val);
|
||||
}
|
||||
const now = new Date().getTime()
|
||||
val = Math.floor((now - val.getTime()) / 1000)
|
||||
const now = new Date().getTime();
|
||||
val = Math.floor((now - val.getTime()) / 1000);
|
||||
|
||||
const hours = Math.floor(val / 3600)
|
||||
val -= hours * 3600
|
||||
const minutes = Math.floor(val / 60)
|
||||
val -= minutes * 60
|
||||
const seconds = val
|
||||
const hours = Math.floor(val / 3600);
|
||||
val -= hours * 3600;
|
||||
const minutes = Math.floor(val / 60);
|
||||
val -= minutes * 60;
|
||||
const seconds = val;
|
||||
|
||||
return `${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}`
|
||||
return `${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}`;
|
||||
}
|
||||
|
|
|
@ -6,17 +6,21 @@ import VnSelect from 'src/components/common/VnSelect.vue';
|
|||
import VnForm from 'src/components/common/VnForm.vue';
|
||||
import ChangePasswordForm from 'src/components/ui/ChangePasswordForm.vue';
|
||||
|
||||
import { userStore as useUserStore } from 'stores/user';
|
||||
import { useUserStore } from 'stores/user';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const vnFormRef = ref(null);
|
||||
const changePasswordFormDialog = ref(null);
|
||||
const showChangePasswordForm = ref(false);
|
||||
const langOptions = ref([]);
|
||||
const pks = computed(() => ({ id: userStore.id }));
|
||||
const pks = computed(() => ({ id: userStore.user.id }));
|
||||
const fetchConfigDataSql = {
|
||||
query: `
|
||||
SELECT u.id, u.name, u.email, u.nickname,
|
||||
|
@ -44,13 +48,13 @@ onMounted(() => fetchLanguagesSql());
|
|||
<template>
|
||||
<QPage>
|
||||
<QPage class="q-pa-md flex justify-center">
|
||||
<Teleport :to="$actions">
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QBtn
|
||||
:label="t('addresses')"
|
||||
icon="location_on"
|
||||
rounded
|
||||
no-caps
|
||||
:to="{ name: 'AddressesList' }"
|
||||
:to="{ name: 'addressesList' }"
|
||||
/>
|
||||
<QBtn
|
||||
:label="t('changePassword')"
|
||||
|
|
|
@ -7,10 +7,15 @@ import VnInput from 'src/components/common/VnInput.vue';
|
|||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnForm from 'src/components/common/VnForm.vue';
|
||||
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const vnFormRef = ref(null);
|
||||
const countriesOptions = ref([]);
|
||||
|
@ -32,7 +37,7 @@ watch(
|
|||
async val => await getProvinces(val)
|
||||
);
|
||||
|
||||
const goBack = () => router.push({ name: 'AddressesList' });
|
||||
const goBack = () => router.push({ name: 'addressesList' });
|
||||
|
||||
const getCountries = async () => {
|
||||
countriesOptions.value = await jApi.query(
|
||||
|
@ -56,7 +61,7 @@ onMounted(() => getCountries());
|
|||
|
||||
<template>
|
||||
<QPage class="q-pa-md flex justify-center">
|
||||
<Teleport :to="$actions">
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QBtn
|
||||
:label="t('back')"
|
||||
icon="close"
|
||||
|
|
|
@ -5,19 +5,23 @@ import { useRouter } from 'vue-router';
|
|||
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { useVnConfirm } from 'src/composables/useVnConfirm.js';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const router = useRouter();
|
||||
const jApi = inject('jApi');
|
||||
const { notify } = useNotify();
|
||||
const { t } = useI18n();
|
||||
const { openConfirmationModal } = useVnConfirm();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const addresses = ref([]);
|
||||
const defaultAddress = ref(null);
|
||||
const clientId = ref(null);
|
||||
|
||||
const goToAddressDetails = (id = 0) =>
|
||||
router.push({ name: 'AddressDetails', params: { id } });
|
||||
router.push({ name: 'addressDetails', params: { id } });
|
||||
|
||||
const getDefaultAddress = async () => {
|
||||
try {
|
||||
|
@ -84,7 +88,7 @@ onMounted(async () => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QBtn
|
||||
:label="t('addAddress')"
|
||||
icon="add"
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, inject, onBeforeUnmount } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import CardList from 'src/components/ui/CardList.vue';
|
||||
|
||||
import { date as qdate } from 'quasar';
|
||||
import { useUserStore } from 'stores/user';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
|
||||
const connections = ref([]);
|
||||
const loading = ref(false);
|
||||
const intervalId = ref(null);
|
||||
|
||||
const getConnections = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
connections.value = await jApi.query(
|
||||
`SELECT vu.userFk userId, vu.stamp, u.nickname, s.lastUpdate,
|
||||
a.platform, a.browser, a.version, u.name user
|
||||
FROM userSession s
|
||||
JOIN visitUser vu ON vu.id = s.userVisitFk
|
||||
JOIN visitAccess ac ON ac.id = vu.accessFk
|
||||
JOIN visitAgent a ON a.id = ac.agentFk
|
||||
JOIN visit v ON v.id = a.visitFk
|
||||
JOIN account.user u ON u.id = vu.userFk
|
||||
ORDER BY lastUpdate DESC`
|
||||
);
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.error('Error getting connections:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const supplantUser = async user => {
|
||||
try {
|
||||
await userStore.supplantUser(user);
|
||||
await appStore.getMenuLinks();
|
||||
router.push({ name: 'confirmedOrders' });
|
||||
} catch (error) {
|
||||
console.error('Error supplanting user:', error);
|
||||
}
|
||||
};
|
||||
onMounted(async () => {
|
||||
getConnections();
|
||||
intervalId.value = setInterval(getConnections, 60000);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => clearInterval(intervalId.value));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<div class="flex">
|
||||
<QBtn
|
||||
:label="t('refresh')"
|
||||
icon="refresh"
|
||||
@click="getConnections()"
|
||||
rounded
|
||||
no-caps
|
||||
class="q-mr-sm"
|
||||
/>
|
||||
<QBadge class="q-pa-sm" v-if="connections.length" color="blue">
|
||||
{{ connections?.length }} {{ t('connections') }}
|
||||
</QBadge>
|
||||
</div>
|
||||
</Teleport>
|
||||
<QPage class="vn-w-xs">
|
||||
<QList class="flex justify-center">
|
||||
<QSpinner
|
||||
v-if="loading"
|
||||
color="primary"
|
||||
size="3em"
|
||||
:thickness="2"
|
||||
/>
|
||||
<CardList
|
||||
v-else
|
||||
v-for="(connection, index) in connections"
|
||||
:key="index"
|
||||
>
|
||||
<template #content>
|
||||
<span class="text-bold q-mb-sm">
|
||||
{{ connection.nickname }}
|
||||
</span>
|
||||
<span>
|
||||
{{
|
||||
qdate.formatDate(connection.stamp, 'dd, hh:mm:ss A')
|
||||
}}
|
||||
-
|
||||
{{
|
||||
qdate.formatDate(
|
||||
connection.lastUpdate,
|
||||
'hh:mm:ss A'
|
||||
)
|
||||
}}</span
|
||||
>
|
||||
<span
|
||||
v-if="
|
||||
connection.platform &&
|
||||
connection.browser &&
|
||||
connection.version
|
||||
"
|
||||
>
|
||||
{{ connection.platform }} - {{ connection.browser }} -
|
||||
{{ connection.version }}
|
||||
</span>
|
||||
</template>
|
||||
<template #actions>
|
||||
<QBtn
|
||||
icon="people"
|
||||
flat
|
||||
rounded
|
||||
@click="supplantUser(connection.user)"
|
||||
/>
|
||||
</template>
|
||||
</CardList>
|
||||
</QList>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-container {
|
||||
width: 140px;
|
||||
height: 170px;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.card-description {
|
||||
font-size: 0.65rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
refresh: Refresh
|
||||
connections: Connections
|
||||
es-ES:
|
||||
refresh: Actualizar
|
||||
connections: Conexiones
|
||||
ca-ES:
|
||||
refresh: Actualitzar
|
||||
connections: Connexions
|
||||
fr-FR:
|
||||
refresh: Rafraîchir
|
||||
connections: Connexions
|
||||
pt-PT:
|
||||
refresh: Atualizar
|
||||
connections: Conexões
|
||||
</i18n>
|
|
@ -0,0 +1,79 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, inject } from 'vue';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
|
||||
const links = ref([]);
|
||||
|
||||
const getLinks = async () => {
|
||||
try {
|
||||
links.value = await jApi.query(
|
||||
`SELECT image, name, description, link FROM link
|
||||
ORDER BY name`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error getting links:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => getLinks());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage>
|
||||
<QList class="flex justify-center">
|
||||
<QItem
|
||||
v-for="(link, index) in links"
|
||||
:key="index"
|
||||
:href="link.link"
|
||||
target="_blank"
|
||||
class="flex"
|
||||
>
|
||||
<QCard class="card-container">
|
||||
<QImg
|
||||
:src="`http://cdn.verdnatura.es/image/link/full/${link.image}`"
|
||||
width="60px"
|
||||
height="60px"
|
||||
/>
|
||||
<span class="card-title q-mt-md">{{ link.name }}</span>
|
||||
<p class="card-description">{{ link.description }}</p>
|
||||
</QCard>
|
||||
</QItem>
|
||||
</QList>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-container {
|
||||
width: 140px;
|
||||
height: 170px;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 0.7rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.card-description {
|
||||
font-size: 0.65rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
addAddress: Add address
|
||||
es-ES:
|
||||
addAddress: Añadir dirección
|
||||
ca-ES:
|
||||
addAddress: Afegir adreça
|
||||
fr-FR:
|
||||
addAddress: Ajouter une adresse
|
||||
pt-PT:
|
||||
addAddress: Adicionar Morada
|
||||
</i18n>
|
|
@ -0,0 +1,36 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, inject } from 'vue';
|
||||
|
||||
const jApi = inject('jApi');
|
||||
|
||||
const users = ref([]);
|
||||
|
||||
const getUsers = async () => {
|
||||
try {
|
||||
users.value = await jApi.query(
|
||||
`SELECT u.id, u.name, u.nickname, u.active
|
||||
FROM account.user u
|
||||
WHERE u.name LIKE CONCAT('%', #user, '%')
|
||||
OR u.nickname LIKE CONCAT('%', #user, '%')
|
||||
OR u.id = #user
|
||||
ORDER BY u.name LIMIT 200`,
|
||||
{ user: 9 }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error getting users:', error);
|
||||
}
|
||||
};
|
||||
onMounted(async () => getUsers());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QPage>
|
||||
<QList class="flex justify-center">
|
||||
<!-- TODO: WIP -->
|
||||
</QList>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
<i18n lang="yaml"></i18n>
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QInput
|
||||
:placeholder="$t('search')"
|
||||
v-model="search"
|
||||
|
@ -346,11 +346,18 @@
|
|||
import { date, currency } from 'src/lib/filters.js';
|
||||
import { date as qdate } from 'quasar';
|
||||
import axios from 'axios';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
const CancelToken = axios.CancelToken;
|
||||
|
||||
export default {
|
||||
name: 'HederaCatalog',
|
||||
setup() {
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
return { isHeaderMounted };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uid: 0,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QSelect
|
||||
v-model="year"
|
||||
:options="years"
|
||||
|
@ -64,9 +64,16 @@
|
|||
|
||||
<script>
|
||||
import { date, currency } from 'src/lib/filters.js';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
export default {
|
||||
name: 'OrdersPendingIndex',
|
||||
setup() {
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
return { isHeaderMounted };
|
||||
},
|
||||
data() {
|
||||
const curYear = new Date().getFullYear();
|
||||
const years = [];
|
||||
|
@ -102,12 +109,12 @@ export default {
|
|||
},
|
||||
|
||||
async mounted() {
|
||||
await this.loadData();
|
||||
await this.fetchUser();
|
||||
},
|
||||
|
||||
watch: {
|
||||
async year() {
|
||||
await this.loadData();
|
||||
await this.fetchUser();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -115,7 +122,7 @@ export default {
|
|||
date,
|
||||
currency,
|
||||
|
||||
async loadData() {
|
||||
async fetchUser() {
|
||||
const params = {
|
||||
from: new Date(this.year, 0),
|
||||
to: new Date(this.year, 11, 31, 23, 59, 59)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<div class="balance">
|
||||
<span class="label">{{ $t('balance') }}</span>
|
||||
<span class="amount" :class="{ negative: debt < 0 }">
|
||||
|
@ -95,9 +95,16 @@
|
|||
<script>
|
||||
import { date, currency } from 'src/lib/filters.js';
|
||||
import { tpvStore } from 'stores/tpv';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
export default {
|
||||
name: 'OrdersPendingIndex',
|
||||
setup() {
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
return { isHeaderMounted };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
orders: null,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||
<QBtn
|
||||
icon="print"
|
||||
:label="$t('printDeliveryNote')"
|
||||
|
@ -82,10 +82,16 @@
|
|||
|
||||
<script>
|
||||
import { date, currency } from 'src/lib/filters.js';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
export default {
|
||||
name: 'OrdersConfirmedView',
|
||||
|
||||
setup() {
|
||||
const appStore = useAppStore();
|
||||
const { isHeaderMounted } = storeToRefs(appStore);
|
||||
return { isHeaderMounted };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ticket: {},
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { userStore } from 'stores/user';
|
||||
import { useUserStore } from 'stores/user';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
const { notify } = useNotify();
|
||||
|
||||
const t = useI18n();
|
||||
const user = userStore();
|
||||
const userStore = useUserStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const email = ref(null);
|
||||
|
@ -29,7 +29,7 @@ onMounted(() => {
|
|||
}
|
||||
});
|
||||
async function onLogin() {
|
||||
await user.login(email.value, password.value, remember.value);
|
||||
await userStore.login(email.value, password.value, remember.value);
|
||||
router.push('/');
|
||||
}
|
||||
</script>
|
||||
|
@ -57,31 +57,30 @@ async function onLogin() {
|
|||
/>
|
||||
</template>
|
||||
</QInput>
|
||||
<div class=" text-center"> <QCheckbox
|
||||
v-model="remember"
|
||||
:label="$t('remindMe')"
|
||||
dense
|
||||
/> <QBtn
|
||||
id="switchLanguage"
|
||||
:label="$t('language')"
|
||||
icon="translate"
|
||||
color="primary"
|
||||
size="sm"
|
||||
flat
|
||||
rounded
|
||||
>
|
||||
<QMenu auto-close>
|
||||
<QList dense v-for="lang in langs" :key="lang">
|
||||
<QItem
|
||||
disabled
|
||||
v-ripple
|
||||
clickable
|
||||
>
|
||||
{{ $t(`langs.${lang}`) }}
|
||||
</QItem>
|
||||
</QList>
|
||||
</QMenu>
|
||||
</QBtn></div>
|
||||
<div class="text-center">
|
||||
<QCheckbox
|
||||
v-model="remember"
|
||||
:label="$t('remindMe')"
|
||||
dense
|
||||
/>
|
||||
<QBtn
|
||||
id="switchLanguage"
|
||||
:label="$t('language')"
|
||||
icon="translate"
|
||||
color="primary"
|
||||
size="sm"
|
||||
flat
|
||||
rounded
|
||||
>
|
||||
<QMenu auto-close>
|
||||
<QList dense v-for="lang in langs" :key="lang">
|
||||
<QItem disabled v-ripple clickable>
|
||||
{{ $t(`langs.${lang}`) }}
|
||||
</QItem>
|
||||
</QList>
|
||||
</QMenu>
|
||||
</QBtn>
|
||||
</div>
|
||||
</div>
|
||||
<div class="justify-center">
|
||||
<QBtn
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { route } from 'quasar/wrappers'
|
||||
import { appStore } from 'stores/app'
|
||||
import { route } from 'quasar/wrappers';
|
||||
import { useAppStore } from 'stores/app';
|
||||
import {
|
||||
createRouter,
|
||||
createMemoryHistory,
|
||||
createWebHistory,
|
||||
createWebHashHistory
|
||||
} from 'vue-router'
|
||||
import routes from './routes'
|
||||
} from 'vue-router';
|
||||
import routes from './routes';
|
||||
import { i18n } from 'src/boot/i18n';
|
||||
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
|
@ -22,7 +23,7 @@ export default route(function (/* { store, ssrContext } */) {
|
|||
? createMemoryHistory
|
||||
: process.env.VUE_ROUTER_MODE === 'history'
|
||||
? createWebHistory
|
||||
: createWebHashHistory
|
||||
: createWebHashHistory;
|
||||
|
||||
const Router = createRouter({
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
|
@ -34,18 +35,18 @@ export default route(function (/* { store, ssrContext } */) {
|
|||
history: createHistory(
|
||||
process.env.MODE === 'ssr' ? void 0 : process.env.VUE_ROUTER_BASE
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
Router.afterEach((to, from) => {
|
||||
if (from.name === to.name) return
|
||||
const app = appStore()
|
||||
if (from.name === to.name) return;
|
||||
const app = useAppStore();
|
||||
app.$patch({
|
||||
title: window.i18n.t(to.name || 'home'),
|
||||
title: i18n.global.t(to.name || 'home'),
|
||||
subtitle: null,
|
||||
useRightDrawer: false,
|
||||
rightDrawerOpen: true
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
return Router
|
||||
})
|
||||
return Router;
|
||||
});
|
||||
|
|
|
@ -4,7 +4,7 @@ const routes = [
|
|||
component: () => import('layouts/LoginLayout.vue'),
|
||||
children: [
|
||||
{
|
||||
name: 'Login',
|
||||
name: 'login',
|
||||
path: '/login/:email?',
|
||||
component: () => import('pages/Login/LoginView.vue')
|
||||
},
|
||||
|
@ -35,19 +35,9 @@ const routes = [
|
|||
component: () => import('src/pages/Cms/HomeView.vue')
|
||||
},
|
||||
{
|
||||
name: 'orders',
|
||||
name: 'confirmedOrders',
|
||||
path: '/ecomerce/orders',
|
||||
component: () => import('pages/Ecomerce/Orders.vue')
|
||||
},
|
||||
{
|
||||
name: 'ticket',
|
||||
path: '/ecomerce/ticket/:id',
|
||||
component: () => import('pages/Ecomerce/Ticket.vue')
|
||||
},
|
||||
{
|
||||
name: 'invoices',
|
||||
path: '/ecomerce/invoices',
|
||||
component: () => import('pages/Ecomerce/Invoices.vue')
|
||||
component: () => import('src/pages/Ecomerce/Orders.vue')
|
||||
},
|
||||
{
|
||||
name: 'catalog',
|
||||
|
@ -55,24 +45,59 @@ const routes = [
|
|||
component: () => import('pages/Ecomerce/Catalog.vue')
|
||||
},
|
||||
{
|
||||
name: 'packages',
|
||||
name: 'agencyPackages',
|
||||
path: '/agencies/packages',
|
||||
component: () => import('src/pages/Agencies/PackagesView.vue')
|
||||
},
|
||||
{
|
||||
name: 'Account',
|
||||
name: 'accountConfig',
|
||||
path: '/account/conf',
|
||||
component: () => import('pages/Account/AccountConfig.vue')
|
||||
},
|
||||
{
|
||||
name: 'AddressesList',
|
||||
name: 'addressesList',
|
||||
path: '/account/address-list',
|
||||
component: () => import('pages/Account/AddressList.vue')
|
||||
},
|
||||
{
|
||||
name: 'AddressDetails',
|
||||
name: 'addressDetails',
|
||||
path: '/account/address/:id?',
|
||||
component: () => import('pages/Account/AddressDetails.vue')
|
||||
},
|
||||
{
|
||||
name: 'controlPanel',
|
||||
path: 'admin/links',
|
||||
component: () => import('pages/Admin/LinksView.vue')
|
||||
},
|
||||
{
|
||||
name: 'adminUsers',
|
||||
path: 'admin/users',
|
||||
component: () => import('pages/Admin/UsersView.vue')
|
||||
},
|
||||
{
|
||||
name: 'adminConnections',
|
||||
path: 'admin/connections',
|
||||
component: () => import('pages/Admin/ConnectionsView.vue')
|
||||
},
|
||||
{
|
||||
name: 'adminVisits',
|
||||
path: 'admin/visits'
|
||||
// component: () => import('pages/Admin/VisitsView.vue')
|
||||
},
|
||||
{
|
||||
name: 'adminNews',
|
||||
path: 'admin/news'
|
||||
// component: () => import('pages/Admin/NewsView.vue')
|
||||
},
|
||||
{
|
||||
name: 'adminPhotos',
|
||||
path: 'admin/photos'
|
||||
// component: () => import('pages/Admin/PhotosView.vue')
|
||||
},
|
||||
{
|
||||
name: 'adminItems',
|
||||
path: 'admin/items'
|
||||
// component: () => import('pages/Admin/ItemsView.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -1,19 +1,46 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { jApi } from 'boot/axios'
|
||||
import { defineStore } from 'pinia';
|
||||
import { jApi } from 'boot/axios';
|
||||
|
||||
export const appStore = defineStore('hedera', {
|
||||
export const useAppStore = defineStore('hedera', {
|
||||
state: () => ({
|
||||
title: null,
|
||||
subtitle: null,
|
||||
imageUrl: '',
|
||||
useRightDrawer: false,
|
||||
rightDrawerOpen: false
|
||||
rightDrawerOpen: false,
|
||||
isHeaderMounted: false,
|
||||
menuEssentialLinks: []
|
||||
}),
|
||||
|
||||
actions: {
|
||||
async loadConfig () {
|
||||
const imageUrl = await jApi.getValue('SELECT url FROM imageConfig')
|
||||
this.$patch({ imageUrl })
|
||||
async getMenuLinks() {
|
||||
const sections = await jApi.query('SELECT * FROM myMenu');
|
||||
const sectionMap = new Map();
|
||||
for (const section of sections) {
|
||||
sectionMap.set(section.id, section);
|
||||
}
|
||||
|
||||
const sectionTree = [];
|
||||
for (const section of sections) {
|
||||
const parent = section.parentFk;
|
||||
if (parent) {
|
||||
const parentSection = sectionMap.get(parent);
|
||||
if (!parentSection) continue;
|
||||
let childs = parentSection.childs;
|
||||
if (!childs) {
|
||||
childs = parentSection.childs = [];
|
||||
}
|
||||
childs.push(section);
|
||||
} else {
|
||||
sectionTree.push(section);
|
||||
}
|
||||
}
|
||||
|
||||
this.menuEssentialLinks = sectionTree;
|
||||
},
|
||||
async loadConfig() {
|
||||
const imageUrl = await jApi.getValue('SELECT url FROM imageConfig');
|
||||
this.$patch({ imageUrl });
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { store } from 'quasar/wrappers'
|
||||
import { createPinia } from 'pinia'
|
||||
import { store } from 'quasar/wrappers';
|
||||
import { createPinia } from 'pinia';
|
||||
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
|
@ -11,10 +11,10 @@ import { createPinia } from 'pinia'
|
|||
*/
|
||||
|
||||
export default store((/* { ssrContext } */) => {
|
||||
const pinia = createPinia()
|
||||
const pinia = createPinia();
|
||||
|
||||
// You can add Pinia plugins here
|
||||
// pinia.use(SomePiniaPlugin)
|
||||
|
||||
return pinia
|
||||
})
|
||||
return pinia;
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { api, jApi } from 'boot/axios';
|
||||
|
||||
export const userStore = defineStore('user', {
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: () => {
|
||||
const token =
|
||||
sessionStorage.getItem('vnToken') ||
|
||||
|
@ -9,10 +9,9 @@ export const userStore = defineStore('user', {
|
|||
|
||||
return {
|
||||
token,
|
||||
id: null,
|
||||
name: null,
|
||||
nickname: null,
|
||||
isGuest: false
|
||||
isGuest: false,
|
||||
user: null,
|
||||
supplantedUser: null
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -21,6 +20,11 @@ export const userStore = defineStore('user', {
|
|||
},
|
||||
|
||||
actions: {
|
||||
async getToken() {
|
||||
this.token =
|
||||
sessionStorage.getItem('vnToken') ||
|
||||
localStorage.getItem('vnToken');
|
||||
},
|
||||
async login(user, password, remember) {
|
||||
const params = { user, password };
|
||||
const res = await api.post('Accounts/login', params);
|
||||
|
@ -36,7 +40,6 @@ export const userStore = defineStore('user', {
|
|||
name: user
|
||||
});
|
||||
},
|
||||
|
||||
async logout() {
|
||||
if (this.token != null) {
|
||||
try {
|
||||
|
@ -48,16 +51,38 @@ export const userStore = defineStore('user', {
|
|||
this.$reset();
|
||||
},
|
||||
|
||||
async loadData() {
|
||||
const userData = await jApi.getObject(
|
||||
'SELECT id, nickname, name FROM account.myUser'
|
||||
);
|
||||
async fetchUser(userType = 'user') {
|
||||
try {
|
||||
const userData = await jApi.getObject(
|
||||
'SELECT id, nickname, name FROM account.myUser'
|
||||
);
|
||||
this.$patch({ [userType]: userData });
|
||||
} catch (error) {
|
||||
console.error('Error fetching user: ', error);
|
||||
}
|
||||
},
|
||||
|
||||
this.$patch({
|
||||
id: userData.id,
|
||||
nickname: userData.nickname,
|
||||
name: userData.name
|
||||
async supplantUser(supplantUser) {
|
||||
const json = await jApi.send('client/supplant', {
|
||||
supplantUser
|
||||
});
|
||||
this.token = json;
|
||||
sessionStorage.setItem('supplantUser', supplantUser);
|
||||
await this.fetchUser('supplantedUser');
|
||||
},
|
||||
|
||||
async supplantInit() {
|
||||
const user = sessionStorage.getItem('supplantUser');
|
||||
if (user == null) return;
|
||||
await this.supplantUser(user);
|
||||
},
|
||||
|
||||
async logoutSupplantedUser() {
|
||||
sessionStorage.removeItem('supplantUser');
|
||||
this.supplantedUser = null;
|
||||
await api.post('Accounts/logout');
|
||||
this.getToken();
|
||||
await this.fetchUser();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue