Fixes varios #2 #88

Merged
jsegarra merged 17 commits from wbuezas/hedera-web-mindshore:bugfix/fixes-2 into 4922-vueMigration 2024-10-04 18:14:44 +00:00
27 changed files with 613 additions and 336 deletions

View File

@ -82,7 +82,7 @@ const props = defineProps({
} }
}); });
const emit = defineEmits(['onDataSaved']); const emit = defineEmits(['onDataSaved', 'onDataFetched']);
const { t } = useI18n(); const { t } = useI18n();
const jApi = inject('jApi'); const jApi = inject('jApi');
@ -133,6 +133,7 @@ const fetchFormData = async () => {
formData.value = { ...modelInfo.value.data[0] }; formData.value = { ...modelInfo.value.data[0] };
loading.value = false; loading.value = false;
emit('onDataFetched', formData.value);
}; };
const onSubmitSuccess = () => { const onSubmitSuccess = () => {
@ -200,10 +201,7 @@ defineExpose({
</script> </script>
<template> <template>
<QCard <QCard class="form-container" v-bind="$attrs">
class="form-container"
v-bind="$attrs"
>
<QForm <QForm
v-if="!loading" v-if="!loading"
ref="addressFormRef" ref="addressFormRef"
@ -213,14 +211,8 @@ defineExpose({
<span v-if="title" class="text-h6 text-bold"> <span v-if="title" class="text-h6 text-bold">
{{ title }} {{ title }}
</span> </span>
<slot <slot name="form" :data="formData" />
name="form" <slot name="extraForm" :data="formData" />
:data="formData"
/>
<slot
name="extraForm"
:data="formData"
/>
<component <component
v-if="isHeaderMounted" v-if="isHeaderMounted"
:is="showBottomActions ? 'div' : Teleport" :is="showBottomActions ? 'div' : Teleport"
@ -254,24 +246,21 @@ defineExpose({
<slot name="actions" :data="formData" /> <slot name="actions" :data="formData" />
</component> </component>
</QForm> </QForm>
<QSpinner <QSpinner v-else color="primary" size="3em" :thickness="2" />
v-else
color="primary"
size="3em"
:thickness="2"
/>
</QCard> </QCard>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.no-form-container { .no-form-container {
padding: 0 !important; padding: 0 !important;
box-shadow: none; box-shadow: none;
border: none; border: none;
} }
.form-container { .form-container {
width: 100%; width: 100%;
height: max-content; height: max-content;
padding: 0 !important; padding: 32px;
max-width: 544px; max-width: 544px;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -280,7 +269,6 @@ defineExpose({
.form { .form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 32px;
width: 100%; width: 100%;
} }
</style> </style>

View File

@ -0,0 +1,38 @@
<script setup>
import { useI18n } from 'vue-i18n';
defineProps({
emptyMessage: {
type: String,
default: 'emptyList'
},
emptyIcon: {
type: String,
default: 'block'
},
rows: {
type: Array,
default: () => []
},
loading: {
type: Boolean,
default: false
}
});
const { t } = useI18n();
</script>
<template>
<QList v-bind="$attrs" class="column items-center">
<span
v-if="!rows?.length"
class="flex items-center q-pa-md justify-center items-center"
>
<QIcon :name="emptyIcon" size="sm" class="q-mr-sm" />
{{ t(emptyMessage) }}
</span>
<QSpinner v-if="loading" color="primary" size="3em" :thickness="2" />
<slot v-else />
</QList>
</template>

View File

@ -70,6 +70,7 @@ onMounted(() => {
bg-color="white" bg-color="white"
is-outlined is-outlined
:clearable="false" :clearable="false"
class="searchbar"
> >
<template #prepend> <template #prepend>
<QIcon name="search" class="cursor-pointer" @click="search()" /> <QIcon name="search" class="cursor-pointer" @click="search()" />
@ -77,6 +78,15 @@ onMounted(() => {
</VnInput> </VnInput>
</template> </template>
<style lang="scss" scoped>
@import 'src/css/responsive';
.searchbar {
@include mobile {
max-width: 120px;
}
}
</style>
<i18n lang="yaml"> <i18n lang="yaml">
en-US: en-US:
search: Search search: Search

View File

@ -1,5 +1,17 @@
@mixin mobile { @mixin mobile {
@media screen and (max-width: 1023px) { @media screen and (max-width: 768px) {
@content; @content;
} }
}
@mixin tablet {
@media screen and (min-width: 769px) and (max-width: 1024px) {
@content;
}
}
@mixin desktop {
@media screen and (min-width: 1025px) {
@content;
}
} }

View File

@ -51,34 +51,59 @@ export default {
of: 'de', of: 'de',
// Sections titles // Sections titles
Home: 'Inici', titles: {
Orders: 'Comandes', Home: 'Inici',
Ticket: `Detall de l'encarrec`, Orders: 'Comandes',
'Pending orders': 'Comandes pendents', Ticket: `Detall de l'encarrec`,
'Last orders': 'Comandes confirmades', 'Pending orders': 'Comandes pendents',
Invoices: 'Factures', 'Last orders': 'Comandes confirmades',
Basket: 'Cistella', Invoices: 'Factures',
Catalog: 'Catàleg', Basket: 'Cistella',
Administration: 'Administració', Catalog: 'Catàleg',
'Control panel': 'Panell de control', Administration: 'Administració',
Users: 'Usuaris', 'Control panel': 'Panell de control',
Connections: 'Connexions', Users: 'Usuaris',
Visits: 'Visites', Connections: 'Connexions',
News: 'Gestió de noticies', Visits: 'Visites',
Photos: 'Imatges', News: 'Gestió de noticies',
Images: 'Imatges', Photos: 'Imatges',
Items: 'Articles', Images: 'Imatges',
Agencies: 'Agències', Items: 'Articles',
Reports: 'Informes', Agencies: 'Paquets per agència',
Configuration: 'Configuració', Reports: 'Informes',
Shelves: 'Prestatgeries', Configuration: 'Configuració',
Account: 'Compte', Shelves: 'Prestatgeries',
Addresses: 'Adreces', Account: 'Compte',
OrderSummary: 'Resum de la comanda', Addresses: 'Adreces',
Checkout: `Configurar l'encarrec`, OrderSummary: 'Resum de la comanda',
'Address details': 'Configuració', Checkout: `Configurar l'encarrec`,
'Admin news details': `Afegir o editar notícia`, 'Address details': 'Configuració',
'Access log': 'Registre daccés', 'Admin news details': `Afegir o editar notícia`,
'Access log': 'Registre daccés'
},
menuTitles: {
Home: 'Inici',
Orders: 'Comandes',
Basket: 'Cistella',
Catalog: 'Catàleg',
Administration: 'Administració',
Agencies: 'Agències',
Reports: 'Informes',
Shelves: 'Prestatgeries',
Configuration: 'Configuració',
'Pending orders': 'Pendents',
'Last orders': 'Confirmades',
Invoices: 'Factures',
'Control panel': 'Panell de control',
Users: 'Usuaris',
Connections: 'Connexions',
Visits: 'Visites',
News: 'Gestió de notícies',
Photos: 'Imatges',
Items: 'Articles',
Account: 'Compte',
Addresses: 'Adreces'
},
// //
orderLoadedIntoBasket: 'Comanda carregada a la cistella!', orderLoadedIntoBasket: 'Comanda carregada a la cistella!',
loadAnOrder: loadAnOrder:
@ -94,6 +119,7 @@ export default {
confirmDelete: 'Estàs segur que vols esborrar la línia?', confirmDelete: 'Estàs segur que vols esborrar la línia?',
emptyList: 'Llista buida', emptyList: 'Llista buida',
logInAsGuest: `Accedir com a convidat`, logInAsGuest: `Accedir com a convidat`,
logIn: 'Iniciar sessió',
haveForgottenPassword: '¿Has oblidat la teva contrasenya?', haveForgottenPassword: '¿Has oblidat la teva contrasenya?',
signUp: 'Registrar-me', signUp: 'Registrar-me',
notACustomerYet: `Encara no ets client?`, notACustomerYet: `Encara no ets client?`,
@ -106,6 +132,8 @@ export default {
shoppingCart: 'Cistella de la compra', shoppingCart: 'Cistella de la compra',
available: 'Disponible', available: 'Disponible',
minQuantity: 'Quantitat mínima', minQuantity: 'Quantitat mínima',
introduceSearchTerm: 'Introdueix un terme de cerca',
noOrdersFound: `No s'han trobat comandes`,
// Image related translations // Image related translations
'Cant lock cache': 'No es pot bloquejar la memòria cau', 'Cant lock cache': 'No es pot bloquejar la memòria cau',
'Bad file format': 'Format de fitxer no reconegut', 'Bad file format': 'Format de fitxer no reconegut',

View File

@ -63,34 +63,60 @@ export default {
}, },
// Sections titles // Sections titles
Home: 'Home', titles: {
Orders: 'Orders', Home: 'Home',
Ticket: 'Detalle del pedido', Orders: 'Orders',
'Pending orders': 'Pending orders', Ticket: 'Detalle del pedido',
'Last orders': 'Confirmed orders', 'Pending orders': 'Pending orders',
Invoices: 'Invoices', 'Last orders': 'Confirmed orders',
Basket: 'Basket', Invoices: 'Invoices',
Catalog: 'Catalog', Basket: 'Basket',
Administration: 'Administration', Catalog: 'Catalog',
'Control panel': 'Control panel', Administration: 'Administration',
Users: 'Users', 'Control panel': 'Control panel',
Connections: 'Connections', Users: 'Users',
Visits: 'Visits', Connections: 'Connections',
News: 'News management', Visits: 'Visits',
Photos: 'Images', News: 'News management',
Images: 'Images', Photos: 'Images',
Items: 'Items', Images: 'Images',
Agencies: 'Agencies', Items: 'Items',
Reports: 'Reports', Agencies: 'Bundles by agency',
Configuration: 'Configuration', Reports: 'Reports',
Shelves: 'Shelves', Configuration: 'Configuration',
Account: 'Account', Shelves: 'Shelves',
Addresses: 'Addresses', Account: 'Account',
OrderSummary: 'Order summary', Addresses: 'Addresses',
Checkout: 'Configure order', OrderSummary: 'Order summary',
'Address details': 'Configuration', Checkout: 'Configure order',
'Admin news details': 'Add or edit new', 'Address details': 'Configuration',
'Access log': 'Access log', 'Admin news details': 'Add or edit new',
'Access log': 'Access log'
},
menuTitles: {
Home: 'Home',
Orders: 'Orders',
Basket: 'Basket',
Catalog: 'Catalog',
Administration: 'Administration',
Agencies: 'Agencies',
Reports: 'Reports',
Shelves: 'Shelves',
Configuration: 'Configuration',
'Pending orders': 'Pending',
'Last orders': 'Confirmed',
Invoices: 'Invoices',
'Control panel': 'Control panel',
Users: 'Users',
Connections: 'Connections',
Visits: 'Visits',
News: 'News',
Photos: 'Images',
Items: 'Items',
Account: 'Account',
Addresses: 'Addresses'
},
// //
orderLoadedIntoBasket: 'Order loaded into basket!', orderLoadedIntoBasket: 'Order loaded into basket!',
loadAnOrder: 'Please load a pending order to the cart or start a new one', loadAnOrder: 'Please load a pending order to the cart or start a new one',
@ -127,7 +153,8 @@ export default {
save: 'Save', save: 'Save',
cancel: 'Cancel', cancel: 'Cancel',
of: 'of', of: 'of',
loginAsGuest: 'Login as guest', logInAsGuest: 'Login as guest',
logIn: 'Log in',
haveForgottenPassword: 'Have you forgotten your password?', haveForgottenPassword: 'Have you forgotten your password?',
signUp: 'Sign up', signUp: 'Sign up',
notACustomerYet: 'Not a customer yet?', notACustomerYet: 'Not a customer yet?',
@ -139,6 +166,8 @@ export default {
shoppingCart: 'Shopping cart', shoppingCart: 'Shopping cart',
available: 'Available', available: 'Available',
minQuantity: 'Minimum quantity', minQuantity: 'Minimum quantity',
introduceSearchTerm: 'Enter a search term',
noOrdersFound: 'No orders found',
// Image related translations // Image related translations
'Cant lock cache': 'The cache could not be blocked', 'Cant lock cache': 'The cache could not be blocked',
'Bad file format': 'Unrecognized file format', 'Bad file format': 'Unrecognized file format',

View File

@ -60,34 +60,60 @@ export default {
}, },
// Sections titles // Sections titles
Home: 'Inicio', titles: {
Orders: 'Pedidos', Home: 'Inicio',
Ticket: 'Pedido', Orders: 'Pedidos',
'Pending orders': 'Pedidos pendientes', Ticket: 'Pedido',
'Last orders': 'Pedidos confirmados', 'Pending orders': 'Pedidos pendientes',
Invoices: 'Facturas', 'Last orders': 'Pedidos confirmados',
Basket: 'Cesta', Invoices: 'Facturas',
Catalog: 'Catálogo', Basket: 'Cesta',
Administration: 'Administración', Catalog: 'Catálogo',
'Control panel': 'Panel de control', Administration: 'Administración',
Users: 'Usuarios', 'Control panel': 'Panel de control',
Connections: 'Conexiones', Users: 'Usuarios',
Visits: 'Visitas', Connections: 'Conexiones',
News: 'Gestión de noticias', Visits: 'Visitas',
Photos: 'Imágenes', News: 'Gestión de noticias',
Images: 'Imágenes', Photos: 'Imágenes',
Items: 'Artículos', Images: 'Imágenes',
Agencies: 'Agencias', Items: 'Artículos',
Reports: 'Informes', Agencies: 'Bultos por agencia',
Configuration: 'Configuración', Reports: 'Informes',
Shelves: 'Estanterías', Configuration: 'Configuración',
Account: 'Cuenta', Shelves: 'Estanterías',
Addresses: 'Direcciones', Account: 'Cuenta',
OrderSummary: 'Resumen del pedido', Addresses: 'Direcciones',
Checkout: 'Configurar pedido', OrderSummary: 'Resumen del pedido',
'Address details': 'Configuración', Checkout: 'Configurar pedido',
'Admin news details': 'Añadir o editar noticia', 'Address details': 'Configuración',
'Access log': 'Registro de accesos', 'Admin news details': 'Añadir o editar noticia',
'Access log': 'Registro de accesos'
},
menuTitles: {
Home: 'Inicio',
Orders: 'Pedidos',
Basket: 'Cesta',
Catalog: 'Catálogo',
Administration: 'Administración',
Agencies: 'Agencias',
Reports: 'Informes',
Shelves: 'Estanterías',
Configuration: 'Configuración',
'Pending orders': 'Pendientes',
'Last orders': 'Confirmados',
Invoices: 'Facturas',
'Control panel': 'Panel de control',
Users: 'Usuarios',
Connections: 'Conexiones',
Visits: 'Visitas',
News: 'Noticias',
Photos: 'Imágenes',
Items: 'Artículos',
Account: 'Cuenta',
Addresses: 'Direcciones'
},
// //
orderLoadedIntoBasket: '¡Pedido cargado en la cesta!', orderLoadedIntoBasket: '¡Pedido cargado en la cesta!',
loadAnOrder: loadAnOrder:
@ -138,6 +164,8 @@ export default {
shoppingCart: 'Cesta de la compra', shoppingCart: 'Cesta de la compra',
available: 'Disponible', available: 'Disponible',
minQuantity: 'Cantidad mínima', minQuantity: 'Cantidad mínima',
introduceSearchTerm: 'Introduce un término de búsqueda',
noOrdersFound: 'No se encontrado pedidos',
// Image related translations // Image related translations
'Cant lock cache': 'La caché no pudo ser bloqueada', 'Cant lock cache': 'La caché no pudo ser bloqueada',
'Bad file format': 'Formato de archivo no reconocido', 'Bad file format': 'Formato de archivo no reconocido',

View File

@ -51,34 +51,60 @@ export default {
of: 'de', of: 'de',
// Sections titles // Sections titles
Home: 'Accueil', titles: {
Orders: 'Commandes', Home: 'Accueil',
Ticket: 'Détail de la commande', Orders: 'Commandes',
'Pending orders': 'Commandes en attente', Ticket: 'Détail de la commande',
'Last orders': 'Commandes confirmées', 'Pending orders': 'Commandes en attente',
Invoices: 'Factures', 'Last orders': 'Commandes confirmées',
Basket: 'Panier', Invoices: 'Factures',
Catalog: 'Catalogue', Basket: 'Panier',
Administration: 'Administration', Catalog: 'Catalogue',
'Control panel': 'Panneau de configuration', Administration: 'Administration',
Users: 'Utilisateurs', 'Control panel': 'Panneau de configuration',
Connections: 'Connexions', Users: 'Utilisateurs',
Visits: 'Visites', Connections: 'Connexions',
News: 'Gestion des nouvelles', Visits: 'Visites',
Photos: 'Images', News: 'Gestion des nouvelles',
Images: 'Images', Photos: 'Images',
Items: 'Articles', Images: 'Images',
Agencies: 'Agences', Items: 'Articles',
Reports: 'Rapports', Agencies: 'Liste par agence',
Configuration: 'Configuration', Reports: 'Rapports',
Shelves: 'Étagères', Configuration: 'Configuration',
Account: 'Compte', Shelves: 'Étagères',
Addresses: 'Adresses', Account: 'Compte',
OrderSummary: 'Résumé de la commande', Addresses: 'Adresses',
Checkout: 'Configurer la commande', OrderSummary: 'Résumé de la commande',
'Address details': 'Configuration', Checkout: 'Configurer la commande',
'Admin news details': 'Ajouter ou éditer une nouvelle', 'Address details': 'Configuration',
'Access log': "Journal d'accès", 'Admin news details': 'Ajouter ou éditer une nouvelle',
'Access log': "Journal d'accès"
},
menuTitles: {
Home: 'Accueil',
Orders: 'Commandes',
Basket: 'Panier',
Catalog: 'Catalogue',
Administration: 'Administration',
Agencies: 'Agences',
Reports: 'Rapports',
Shelves: 'Étagères',
Configuration: 'Configuration',
'Pending orders': 'En attente',
'Last orders': 'Confirmées',
Invoices: 'Factures',
'Control panel': 'Panneau de configuration',
Users: 'Utilisateurs',
Connections: 'Connexions',
Visits: 'Visites',
News: 'Nouvelles',
Photos: 'Images',
Items: 'Articles',
Account: 'Compte',
Addresses: 'Adresses'
},
// //
orderLoadedIntoBasket: 'Commande chargée dans le panier!', orderLoadedIntoBasket: 'Commande chargée dans le panier!',
loadAnOrder: loadAnOrder:
@ -94,6 +120,7 @@ export default {
emptyList: 'Vider la liste', emptyList: 'Vider la liste',
confirmDelete: 'Voulez-vous vraiment supprimer la ligne?', confirmDelete: 'Voulez-vous vraiment supprimer la ligne?',
logInAsGuest: `Entrez en tant qu'invité`, logInAsGuest: `Entrez en tant qu'invité`,
logIn: 'Se connecter',
haveForgottenPassword: 'Avez-vous oublié votre mot de passe?', haveForgottenPassword: 'Avez-vous oublié votre mot de passe?',
signUp: `S'inscrire`, signUp: `S'inscrire`,
notACustomerYet: `Pas encore client?`, notACustomerYet: `Pas encore client?`,
@ -106,6 +133,8 @@ export default {
shoppingCart: 'Panier', shoppingCart: 'Panier',
available: 'Disponible', available: 'Disponible',
minQuantity: 'Quantité minimum', minQuantity: 'Quantité minimum',
introduceSearchTerm: 'Entrez un terme de recherche',
noOrdersFound: 'Aucune commande trouvée',
// Image related translations // Image related translations
'Cant lock cache': "Le cache n'a pas pu être verrouillé", 'Cant lock cache': "Le cache n'a pas pu être verrouillé",
'Bad file format': 'Format de fichier non reconnu', 'Bad file format': 'Format de fichier non reconnu',

View File

@ -50,34 +50,59 @@ export default {
}, },
of: 'de', of: 'de',
// Sections titles // Sections titles
Home: 'Início', titles: {
Orders: 'Pedidos', Home: 'Início',
Ticket: 'Detalhe do pedido', Orders: 'Pedidos',
'Pending orders': 'Pedidos pendentes', Ticket: 'Detalhe do pedido',
'Last orders': 'Pedidos confirmados', 'Pending orders': 'Pedidos pendentes',
Invoices: 'Faturas', 'Last orders': 'Pedidos confirmados',
Basket: 'Carrinho', Invoices: 'Faturas',
Catalog: 'Catálogo', Basket: 'Carrinho',
Administration: 'Administração', Catalog: 'Catálogo',
'Control panel': 'Painel de controle', Administration: 'Administração',
Users: 'Usuários', 'Control panel': 'Painel de controle',
Connections: 'Conexões', Users: 'Usuários',
Visits: 'Visitas', Connections: 'Conexões',
News: 'Gestão de noticias', Visits: 'Visitas',
Photos: 'Imagens', News: 'Gestão de noticias',
Images: 'Imagens', Photos: 'Imagens',
Items: 'Artigos', Images: 'Imagens',
Agencies: 'Agências', Items: 'Artigos',
Reports: 'Informes', Agencies: 'Bultos por agencia',
Configuration: 'Configuração', Reports: 'Informes',
Shelves: 'Estantes', Configuration: 'Configuração',
Account: 'Conta', Shelves: 'Estantes',
Addresses: 'Moradas', Account: 'Conta',
OrderSummary: 'Resumo da encomenda', Addresses: 'Moradas',
Checkout: 'Configurar encomenda', OrderSummary: 'Resumo da encomenda',
'Address details': 'Configuração', Checkout: 'Configurar encomenda',
'Admin news details': 'Adicionar ou editar notícia', 'Address details': 'Configuração',
'Access log': 'Registo de acessos', 'Admin news details': 'Adicionar ou editar notícia',
'Access log': 'Registo de acessos'
},
menuTitles: {
Home: 'Início',
Orders: 'Pedidos',
Basket: 'Carrinho',
Catalog: 'Catálogo',
Administration: 'Administração',
Agencies: 'Agências',
Reports: 'Informes',
Shelves: 'Estantes',
Configuration: 'Configuração',
'Pending orders': 'Pendentes',
'Last orders': 'Confirmados',
Invoices: 'Faturas',
'Control panel': 'Painel de controle',
Users: 'Usuários',
Connections: 'Conexões',
Visits: 'Visitas',
News: 'Notícias',
Photos: 'Imagens',
Items: 'Artigos',
Account: 'Conta',
Addresses: 'Moradas'
},
// //
orderLoadedIntoBasket: 'Pedido carregado na cesta!', orderLoadedIntoBasket: 'Pedido carregado na cesta!',
loadAnOrder: 'Carregue um pedido pendente no carrinho ou inicie um novo', loadAnOrder: 'Carregue um pedido pendente no carrinho ou inicie um novo',
@ -92,6 +117,7 @@ export default {
confirmDelete: 'Tens certeza que queres eliminar esta linha?', confirmDelete: 'Tens certeza que queres eliminar esta linha?',
emptyList: 'Lista vazia', emptyList: 'Lista vazia',
logInAsGuest: 'Entrar como convidado', logInAsGuest: 'Entrar como convidado',
logIn: 'Iniciar sessão',
haveForgottenPassword: 'Esqueceu a senha?', haveForgottenPassword: 'Esqueceu a senha?',
signUp: 'Registar', signUp: 'Registar',
notACustomerYet: 'Ainda não é cliente?', notACustomerYet: 'Ainda não é cliente?',
@ -104,6 +130,8 @@ export default {
shoppingCart: 'Cesta da compra', shoppingCart: 'Cesta da compra',
available: 'Disponível', available: 'Disponível',
minQuantity: 'Quantidade mínima', minQuantity: 'Quantidade mínima',
introduceSearchTerm: 'Digite um termo de pesquisa',
noOrdersFound: 'Nenhum pedido encontrado',
// Image related translations // Image related translations
'Cant lock cache': 'O cache não pôde ser bloqueado', 'Cant lock cache': 'O cache não pôde ser bloqueado',
'Bad file format': 'Formato de arquivo inválido', 'Bad file format': 'Formato de arquivo inválido',

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, onMounted } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
@ -10,13 +10,19 @@ import { useAppStore } from 'stores/app';
const router = useRouter(); const router = useRouter();
const userStore = useUserStore(); const userStore = useUserStore();
const appStore = useAppStore(); const appStore = useAppStore();
const hiddenMenuItems = new Set(['Reports']);
const refreshContentKey = ref(0);
const { user, supplantedUser } = storeToRefs(userStore); const { user, supplantedUser } = storeToRefs(userStore);
const { menuEssentialLinks, title, subtitle, useRightDrawer, rightDrawerOpen } = const { menuEssentialLinks, title, subtitle, useRightDrawer, rightDrawerOpen } =
storeToRefs(appStore); storeToRefs(appStore);
const actions = ref(null); const actions = ref(null);
const leftDrawerOpen = ref(false); const leftDrawerOpen = ref(false);
const filteredMenuItems = computed(() =>
menuEssentialLinks.value.filter(
item => !hiddenMenuItems.has(item.description)
)
);
const toggleLeftDrawer = () => { const toggleLeftDrawer = () => {
leftDrawerOpen.value = !leftDrawerOpen.value; leftDrawerOpen.value = !leftDrawerOpen.value;
}; };
@ -35,6 +41,7 @@ const logout = async () => {
const logoutSupplantedUser = async () => { const logoutSupplantedUser = async () => {
await userStore.logoutSupplantedUser(); await userStore.logoutSupplantedUser();
await appStore.getMenuLinks(); await appStore.getMenuLinks();
refreshContentKey.value++;
}; };
</script> </script>
@ -90,15 +97,17 @@ const logoutSupplantedUser = async () => {
/> />
</div> </div>
</div> </div>
<QList v-for="item in menuEssentialLinks" :key="item.id"> <QList v-for="item in filteredMenuItems" :key="item.id">
<QItem v-if="!item.childs" :to="`/${item.path}`"> <QItem v-if="!item.childs" :to="`/${item.path}`">
<QItemSection> <QItemSection>
<QItemLabel>{{ $t(item.description) }}</QItemLabel> <QItemLabel>{{
$t(`menuTitles.${item.description}`)
}}</QItemLabel>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QExpansionItem <QExpansionItem
v-if="item.childs" v-if="item.childs"
:label="$t(item.description)" :label="$t(`menuTitles.${item.description}`)"
expand-separator expand-separator
> >
<QList> <QList>
@ -110,7 +119,9 @@ const logoutSupplantedUser = async () => {
> >
<QItemSection> <QItemSection>
<QItemLabel> <QItemLabel>
{{ $t(subitem.description) }} {{
$t(`menuTitles.${subitem.description}`)
}}
</QItemLabel> </QItemLabel>
</QItemSection> </QItemSection>
</QItem> </QItem>
@ -118,7 +129,7 @@ const logoutSupplantedUser = async () => {
</QExpansionItem> </QExpansionItem>
</QList> </QList>
</QDrawer> </QDrawer>
<QPageContainer> <QPageContainer :key="refreshContentKey">
<router-view /> <router-view />
</QPageContainer> </QPageContainer>
</QLayout> </QLayout>

View File

@ -53,6 +53,10 @@ const updateUserNickname = async nickname => {
} }
}; };
const formatMailData = data => {
data.isToBeMailed = Boolean(data.isToBeMailed);
};
onMounted(() => fetchLanguagesSql()); onMounted(() => fetchLanguagesSql());
</script> </script>
@ -116,18 +120,20 @@ onMounted(() => fetchLanguagesSql());
</template> </template>
<template #extraForm> <template #extraForm>
<VnForm <VnForm
class="no-form-container" class="no-form-container q-mt-md"
ref="vnFormRef2" ref="vnFormRef2"
:pks="pks" :pks="pks"
table="myClient" table="myClient"
schema="hedera" schema="hedera"
:fetch-form-data-sql="fetchConfigDataSql" :fetch-form-data-sql="fetchConfigDataSql"
:default-actions="false" :default-actions="false"
@on-data-fetched="$event => formatMailData($event)"
> >
<template #form="{ data }"> <template #form="{ data }">
<QCheckbox <QCheckbox
v-model="data.isToBeMailed" v-model="data.isToBeMailed"
:label="t('isToBeMailed')" :label="t('isToBeMailed')"
:toggle-indeterminate="false"
@update:model-value="vnFormRef2.submit()" @update:model-value="vnFormRef2.submit()"
dense dense
/> />

View File

@ -90,20 +90,12 @@ onMounted(() => getCountries());
@on-data-saved="goBack()" @on-data-saved="goBack()"
> >
<template #form="{ data }"> <template #form="{ data }">
<VnInput <VnInput v-model="data.nickname" :label="t('name')" />
v-model="data.nickname" <VnInput v-model="data.street" :label="t('address')" />
:label="t('name')" <VnInput v-model="data.city" :label="t('city')" />
/>
<VnInput
v-model="data.street"
:label="t('address')"
/>
<VnInput
v-model="data.city"
:label="t('city')"
/>
<VnInput <VnInput
v-model="data.postalCode" v-model="data.postalCode"
type="number"
:label="t('postalCode')" :label="t('postalCode')"
/> />
<VnSelect <VnSelect

View File

@ -4,6 +4,7 @@ import { ref, onMounted, inject } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import CardList from 'src/components/ui/CardList.vue'; import CardList from 'src/components/ui/CardList.vue';
import VnList from 'src/components/ui/VnList.vue';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import { useVnConfirm } from 'src/composables/useVnConfirm.js'; import { useVnConfirm } from 'src/composables/useVnConfirm.js';
@ -104,7 +105,11 @@ onMounted(async () => {
</QBtn> </QBtn>
</Teleport> </Teleport>
<QPage class="vn-w-sm"> <QPage class="vn-w-sm">
<QList class="rounded-borders shadow-1 shadow-transition" separator> <VnList
class="rounded-borders shadow-1 shadow-transition"
separator
:rows="addresses"
>
<CardList <CardList
v-for="(address, index) in addresses" v-for="(address, index) in addresses"
:key="index" :key="index"
@ -158,7 +163,7 @@ onMounted(async () => {
</QBtn> </QBtn>
</template> </template>
</CardList> </CardList>
</QList> </VnList>
</QPage> </QPage>
</template> </template>

View File

@ -3,6 +3,7 @@ import { onMounted, inject, ref } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import CardList from 'src/components/ui/CardList.vue'; import CardList from 'src/components/ui/CardList.vue';
import VnList from 'src/components/ui/VnList.vue';
import { formatDateTitle } from 'src/lib/filters.js'; import { formatDateTitle } from 'src/lib/filters.js';
@ -72,7 +73,7 @@ onMounted(async () => {
<span>{{ user?.phone }} </span> <span>{{ user?.phone }} </span>
</template> </template>
</CardList> </CardList>
<QList> <VnList :rows="accessLogs">
<CardList <CardList
v-for="(accessLog, index) in accessLogs" v-for="(accessLog, index) in accessLogs"
:key="index" :key="index"
@ -101,6 +102,6 @@ onMounted(async () => {
</span> </span>
</template> </template>
</CardList> </CardList>
</QList> </VnList>
</QPage> </QPage>
</template> </template>

View File

@ -4,6 +4,7 @@ import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import CardList from 'src/components/ui/CardList.vue'; import CardList from 'src/components/ui/CardList.vue';
import VnList from 'src/components/ui/VnList.vue';
import { date as qdate } from 'quasar'; import { date as qdate } from 'quasar';
import { useUserStore } from 'stores/user'; import { useUserStore } from 'stores/user';
@ -80,15 +81,12 @@ onBeforeUnmount(() => clearInterval(intervalId.value));
</div> </div>
</Teleport> </Teleport>
<QPage class="vn-w-xs"> <QPage class="vn-w-xs">
<QList class="flex justify-center"> <VnList
<QSpinner class="flex justify-center"
v-if="loading" :loading="loading"
color="primary" :rows="connections"
size="3em" >
:thickness="2"
/>
<CardList <CardList
v-else
v-for="(connection, index) in connections" v-for="(connection, index) in connections"
:key="index" :key="index"
:to="{ name: 'accessLog', params: { id: connection.userId } }" :to="{ name: 'accessLog', params: { id: connection.userId } }"
@ -133,8 +131,7 @@ onBeforeUnmount(() => clearInterval(intervalId.value));
</QBtn> </QBtn>
</template> </template>
</CardList> </CardList>
<pre>{{ connections }}</pre> </VnList>
</QList>
</QPage> </QPage>
</template> </template>

View File

@ -1,15 +1,14 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import CardList from 'src/components/ui/CardList.vue'; import CardList from 'src/components/ui/CardList.vue';
import VnImg from 'src/components/ui/VnImg.vue'; import VnImg from 'src/components/ui/VnImg.vue';
import VnSearchBar from 'src/components/ui/VnSearchBar.vue'; import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
import VnList from 'src/components/ui/VnList.vue';
import { useAppStore } from 'stores/app'; import { useAppStore } from 'stores/app';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
const { t } = useI18n();
const appStore = useAppStore(); const appStore = useAppStore();
const { isHeaderMounted } = storeToRefs(appStore); const { isHeaderMounted } = storeToRefs(appStore);
@ -39,19 +38,14 @@ const onSearch = data => (items.value = data || []);
/> />
</Teleport> </Teleport>
<QPage class="vn-w-xs"> <QPage class="vn-w-xs">
<QList class="flex justify-center"> <VnList
<span v-if="!loading && !items.length" class="flex items-center"> class="flex justify-center"
<QIcon name="refresh" size="sm" class="q-mr-sm" /> empty-message="introduceSearchTerm"
{{ t('introduceSearchTerm') }} empty-icon="refresh"
</span> :loading="loading"
<QSpinner :rows="items"
v-if="loading" >
color="primary"
size="3em"
:thickness="2"
/>
<CardList <CardList
v-else
v-for="(item, index) in items" v-for="(item, index) in items"
:key="index" :key="index"
:clickable="false" :clickable="false"
@ -82,19 +76,6 @@ const onSearch = data => (items.value = data || []);
<span>{{ item.image }}</span> <span>{{ item.image }}</span>
</template> </template>
</CardList> </CardList>
</QList> </VnList>
</QPage> </QPage>
</template> </template>
<i18n lang="yaml">
en-US:
introduceSearchTerm: Enter a search term
es-ES:
introduceSearchTerm: Introduce un término de búsqueda
ca-ES:
introduceSearchTerm: Introdueix un terme de cerca
fr-FR:
introduceSearchTerm: Entrez un terme de recherche
pt-PT:
introduceSearchTerm: Digite um termo de pesquisa
</i18n>

View File

@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
import CardList from 'src/components/ui/CardList.vue'; import CardList from 'src/components/ui/CardList.vue';
import VnImg from 'src/components/ui/VnImg.vue'; import VnImg from 'src/components/ui/VnImg.vue';
import VnList from 'src/components/ui/VnList.vue';
import { useAppStore } from 'stores/app'; import { useAppStore } from 'stores/app';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@ -22,12 +23,14 @@ const news = ref([]);
const getNews = async () => { const getNews = async () => {
try { try {
loading.value = true;
news.value = await jApi.query( news.value = await jApi.query(
`SELECT n.id, u.nickname, n.priority, n.image, n.title `SELECT n.id, u.nickname, n.priority, n.image, n.title
FROM news n FROM news n
JOIN account.user u ON u.id = n.userFk JOIN account.user u ON u.id = n.userFk
ORDER BY priority, n.created DESC` ORDER BY priority, n.created DESC`
); );
loading.value = false;
} catch (error) { } catch (error) {
console.error('Error getting news:', error); console.error('Error getting news:', error);
} }
@ -66,15 +69,8 @@ onMounted(async () => getNews());
</QBtn> </QBtn>
</Teleport> </Teleport>
<QPage class="vn-w-sm"> <QPage class="vn-w-sm">
<QList class="flex justify-center"> <VnList class="flex justify-center" :loading="loading" :rows="news">
<QSpinner
v-if="loading"
color="primary"
size="3em"
:thickness="2"
/>
<CardList <CardList
v-else
v-for="(newsItem, index) in news" v-for="(newsItem, index) in news"
:key="index" :key="index"
:to="{ name: 'adminNewsDetails', params: { id: newsItem.id } }" :to="{ name: 'adminNewsDetails', params: { id: newsItem.id } }"
@ -115,7 +111,7 @@ onMounted(async () => getNews());
</QBtn> </QBtn>
</template> </template>
</CardList> </CardList>
</QList> </VnList>
</QPage> </QPage>
</template> </template>

View File

@ -5,6 +5,7 @@ import { useRouter } from 'vue-router';
import CardList from 'src/components/ui/CardList.vue'; import CardList from 'src/components/ui/CardList.vue';
import VnSearchBar from 'src/components/ui/VnSearchBar.vue'; import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
import VnList from 'src/components/ui/VnList.vue';
import { useAppStore } from 'stores/app'; import { useAppStore } from 'stores/app';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@ -49,19 +50,14 @@ const supplantUser = async user => {
/> />
</Teleport> </Teleport>
<QPage class="vn-w-xs"> <QPage class="vn-w-xs">
<QList class="flex justify-center"> <VnList
<span v-if="!loading && !users.length" class="flex items-center"> class="flex justify-center"
<QIcon name="refresh" size="sm" class="q-mr-sm" /> empty-message="noData"
{{ t('noData') }} empty-icon="refresh"
</span> :loading="loading"
<QSpinner :rows="users"
v-if="loading" >
color="primary"
size="3em"
:thickness="2"
/>
<CardList <CardList
v-else
v-for="(user, index) in users" v-for="(user, index) in users"
:key="index" :key="index"
:to="{ name: 'accessLog', params: { id: user.id } }" :to="{ name: 'accessLog', params: { id: user.id } }"
@ -87,7 +83,7 @@ const supplantUser = async user => {
<QBadge v-else color="negative">{{ t('Disabled') }}</QBadge> <QBadge v-else color="negative">{{ t('Disabled') }}</QBadge>
</template> </template>
</CardList> </CardList>
</QList> </VnList>
</QPage> </QPage>
</template> </template>

View File

@ -1,6 +1,6 @@
<template> <template>
<Teleport v-if="isHeaderMounted" to="#actions"> <Teleport v-if="isHeaderMounted" to="#actions">
<div class="q-gutter-x-sm row"> <div class="row">
<VnSearchBar :search-term="search" @on-search-error="items = []" /> <VnSearchBar :search-term="search" @on-search-error="items = []" />
<QBtn <QBtn
:icon="viewTypeButtonContent.icon" :icon="viewTypeButtonContent.icon"
@ -24,6 +24,15 @@
{{ t('shoppingCart') }} {{ t('shoppingCart') }}
</QTooltip> </QTooltip>
</QBtn> </QBtn>
<QBtn
v-if="!isDesktop"
flat
dense
round
icon="menu"
aria-label="Menu"
@click="toggleRightDrawer()"
/>
</div> </div>
</Teleport> </Teleport>
<div style="padding-bottom: 5em"> <div style="padding-bottom: 5em">
@ -290,7 +299,7 @@ const appStore = useAppStore();
const userStore = useUserStore(); const userStore = useUserStore();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const { isHeaderMounted, rightDrawerOpen, basketOrderId } = const { isHeaderMounted, rightDrawerOpen, basketOrderId, isDesktop } =
storeToRefs(appStore); storeToRefs(appStore);
const { isGuest } = storeToRefs(userStore); const { isGuest } = storeToRefs(userStore);
const { notify } = useNotify(); const { notify } = useNotify();
@ -915,6 +924,10 @@ const redirectToBasket = () => {
router.push({ name: 'basket' }); router.push({ name: 'basket' });
}; };
const toggleRightDrawer = () => {
rightDrawerOpen.value = !rightDrawerOpen.value;
};
watch( watch(
() => route.query.search, () => route.query.search,
val => { val => {

View File

@ -16,10 +16,11 @@ const route = useRoute();
const router = useRouter(); const router = useRouter();
const { notify } = useNotify(); const { notify } = useNotify();
const appStore = useAppStore(); const appStore = useAppStore();
const { localeDates } = storeToRefs(appStore); const { localeDates, isMobile } = storeToRefs(appStore);
const stepperRef = ref(null); const stepperRef = ref(null);
const showNavigationButtons = ref(true);
const loading = ref(false); const loading = ref(false);
const today = ref(null); const today = ref(null);
const addresses = ref([]); const addresses = ref([]);
@ -210,6 +211,13 @@ const getWarehouses = async () => {
} }
}; };
const hideNavigationButtons = () => {
showNavigationButtons.value = false;
setTimeout(() => {
showNavigationButtons.value = true;
}, 350);
};
const onNextStep = async stepIndex => { const onNextStep = async stepIndex => {
const currentStep = steps[orderForm.value.method][stepIndex]; const currentStep = steps[orderForm.value.method][stepIndex];
if (currentStep.onBeforeNextStep) { if (currentStep.onBeforeNextStep) {
@ -220,6 +228,8 @@ const onNextStep = async stepIndex => {
return; return;
} }
hideNavigationButtons();
currentStep.stepDone = true; currentStep.stepDone = true;
await stepperRef.value.next(); await stepperRef.value.next();
@ -230,6 +240,7 @@ const onNextStep = async stepIndex => {
}; };
const onPreviousStep = async stepIndex => { const onPreviousStep = async stepIndex => {
hideNavigationButtons();
await stepperRef.value.previous(); await stepperRef.value.previous();
const previousStep = steps[orderForm.value.method][stepIndex - 1]; const previousStep = steps[orderForm.value.method][stepIndex - 1];
@ -301,15 +312,16 @@ onMounted(async () => {
</script> </script>
<template> <template>
<QPage class="vn-w-sm"> <QPage class="page-container">
<QStepper <QStepper
v-if="steps[orderForm.method] && steps[orderForm.method].length" v-if="steps[orderForm.method] && steps[orderForm.method].length"
v-model="currentStep" v-model="currentStep"
ref="stepperRef" ref="stepperRef"
animated animated
keep-alive keep-alive
:flat="isMobile"
contracted contracted
class="default-radius" class="default-radius stepper-container"
> >
<QStep <QStep
v-for="(step, stepIndex) in steps[orderForm.method]" v-for="(step, stepIndex) in steps[orderForm.method]"
@ -317,6 +329,7 @@ onMounted(async () => {
:name="step.name" :name="step.name"
:done="step.stepDone" :done="step.stepDone"
done-color="accent" done-color="accent"
class="step-container full-height"
> >
<!-- Method step --> <!-- Method step -->
<div <div
@ -436,33 +449,88 @@ onMounted(async () => {
<span>{{ confirmPlaceText }}</span> <span>{{ confirmPlaceText }}</span>
</div> </div>
</div> </div>
<QStepperNavigation class="flex justify-between"> <QBtn
<QBtn v-if="showNavigationButtons"
flat color="primary"
color="primary" @click="onPreviousStep(stepIndex)"
@click="onPreviousStep(stepIndex)" :disabled="currentStep === 'method'"
:label="step.backButtonLabel || t('back')" icon="arrow_back"
class="q-ml-sm" dense
:class="{ invisible: currentStep === 'method' }" class="left-navigation-button"
/> >
<QBtn <QTooltip>
@click="onNextStep(stepIndex)" {{ t(`${step.backButtonLabel || 'back'}`) }}
color="primary" </QTooltip>
:label="step.nextButtonLabel || t('next')" </QBtn>
/> <QBtn
</QStepperNavigation> v-if="showNavigationButtons"
@click="onNextStep(stepIndex)"
:color="currentStep === 'confirm' ? 'accent ' : 'primary'"
:icon="
currentStep === 'confirm' ? 'check' : 'arrow_forward'
"
dense
class="right-navigation-button"
>
<QTooltip>
{{ t(`${step.nextButtonLabel || 'next'}`) }}
</QTooltip>
</QBtn>
</QStep> </QStep>
</QStepper> </QStepper>
</QPage> </QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss">
@import 'src/css/responsive';
.step-title { .step-title {
min-width: 100%; min-width: 100%;
margin-bottom: 16px; margin-bottom: 16px;
text-align: center; text-align: center;
font-weight: bold; font-weight: bold;
} }
.page-container {
max-width: 544px;
margin: auto;
@include mobile {
max-height: calc(100svh - 64px);
padding: 0 !important;
}
}
.stepper-container > * {
@include mobile {
max-height: calc(100svh - 136px);
}
}
.step-container {
@include mobile {
.q-stepper__step-content {
height: calc(100svh - 64px);
}
}
}
.left-navigation-button {
position: absolute;
left: 5px;
top: 50%;
@include mobile {
top: 35%;
}
}
.right-navigation-button {
position: absolute;
right: 5px;
top: 50%;
@include mobile {
top: 35%;
}
}
</style> </style>
<i18n lang="yaml"> <i18n lang="yaml">

View File

@ -114,15 +114,9 @@ onMounted(async () => {
{{ t('downloadInvoicePdf') }} {{ t('downloadInvoicePdf') }}
</QTooltip> </QTooltip>
</QBtn> </QBtn>
<QIcon <QIcon v-else name="warning" color="warning" size="sm">
v-else
name="warning"
:title="t('notDownloadable')"
color="warning"
size="sm"
>
<QTooltip> <QTooltip>
{{ t('requestTheInvoiceToComercial') }} {{ t('notDownloadable') }}
</QTooltip> </QTooltip>
</QIcon> </QIcon>
</QTd> </QTd>

View File

@ -6,6 +6,7 @@ import { useI18n } from 'vue-i18n';
import CardList from 'src/components/ui/CardList.vue'; import CardList from 'src/components/ui/CardList.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
import VnList from 'src/components/ui/VnList.vue';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import { currency, formatDateTitle } from 'src/lib/filters.js'; import { currency, formatDateTitle } from 'src/lib/filters.js';
@ -90,13 +91,7 @@ const onConfirmPay = async () => {
</QBtn> </QBtn>
</Teleport> </Teleport>
<QPage class="vn-w-sm"> <QPage class="vn-w-sm">
<div <VnList empty-message="noOrdersFound" :loading="loading" :rows="orders">
v-if="!orders?.length"
class="text-subtitle1 text-center text-grey-7 q-pa-md"
>
{{ t('noOrdersFound') }}
</div>
<QList v-if="orders?.length">
<CardList <CardList
v-for="order in orders" v-for="order in orders"
:key="order.id" :key="order.id"
@ -115,7 +110,7 @@ const onConfirmPay = async () => {
<QItemLabel>{{ order.agency }}</QItemLabel> <QItemLabel>{{ order.agency }}</QItemLabel>
</template> </template>
</CardList> </CardList>
</QList> </VnList>
<QPageSticky> <QPageSticky>
<QBtn <QBtn
fab fab
@ -127,7 +122,7 @@ const onConfirmPay = async () => {
</QPageSticky> </QPageSticky>
<VnConfirm <VnConfirm
v-model="showAmountToPayDialog" v-model="showAmountToPayDialog"
:message="t('amountToPay')" message=" "
:promise="onConfirmPay" :promise="onConfirmPay"
> >
<template #customHTML> <template #customHTML>
@ -138,7 +133,9 @@ const onConfirmPay = async () => {
type="number" type="number"
min="0" min="0"
:max="debt * -1" :max="debt * -1"
/> >
<template #append></template>
</VnInput>
</template> </template>
</VnConfirm> </VnConfirm>
</QPage> </QPage>
@ -172,7 +169,6 @@ const onConfirmPay = async () => {
<i18n lang="yaml"> <i18n lang="yaml">
en-US: en-US:
startOrder: Start order startOrder: Start order
noOrdersFound: No orders found
makePayment: Make payment makePayment: Make payment
balance: 'Balance:' balance: 'Balance:'
paymentInfo: >- paymentInfo: >-
@ -184,7 +180,6 @@ en-US:
amountError: The amount must be a positive number less than or equal to the outstanding amount amountError: The amount must be a positive number less than or equal to the outstanding amount
es-ES: es-ES:
startOrder: Empezar pedido startOrder: Empezar pedido
noOrdersFound: No se encontrado pedidos
makePayment: Realizar pago makePayment: Realizar pago
balance: 'Saldo:' balance: 'Saldo:'
paymentInfo: >- paymentInfo: >-
@ -197,7 +192,6 @@ es-ES:
amountError: La cantidad debe ser un número positivo e inferior o igual al importe pendiente amountError: La cantidad debe ser un número positivo e inferior o igual al importe pendiente
ca-ES: ca-ES:
startOrder: Començar encàrrec startOrder: Començar encàrrec
noOrdersFound: No s'han trobat comandes
makePayment: Realitzar pagament makePayment: Realitzar pagament
balance: 'Saldo:' balance: 'Saldo:'
paymentInfo: >- paymentInfo: >-
@ -210,7 +204,6 @@ ca-ES:
amountError: La quantitat ha de ser un nombre positiu i inferior o igual a l'import pendent amountError: La quantitat ha de ser un nombre positiu i inferior o igual a l'import pendent
fr-FR: fr-FR:
startOrder: Acheter startOrder: Acheter
noOrdersFound: Aucune commande trouvée
makePayment: Effectuer un paiement makePayment: Effectuer un paiement
balance: 'Balance:' balance: 'Balance:'
paymentInfo: >- paymentInfo: >-
@ -223,7 +216,6 @@ fr-FR:
amountError: La quantité doit être un neméro positif et inférieur ou égal à la somme restant à payer amountError: La quantité doit être un neméro positif et inférieur ou égal à la somme restant à payer
pt-PT: pt-PT:
startOrder: Iniciar encomenda startOrder: Iniciar encomenda
noOrdersFound: Nenhum pedido encontrado
makePayment: Realizar pagamento makePayment: Realizar pagamento
balance: 'Saldo:' balance: 'Saldo:'
paymentInfo: >- paymentInfo: >-

View File

@ -4,6 +4,8 @@ import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import CardList from 'src/components/ui/CardList.vue'; import CardList from 'src/components/ui/CardList.vue';
import VnList from 'src/components/ui/VnList.vue';
import { currency, formatDateTitle } from 'src/lib/filters.js'; import { currency, formatDateTitle } from 'src/lib/filters.js';
import { useVnConfirm } from 'src/composables/useVnConfirm.js'; import { useVnConfirm } from 'src/composables/useVnConfirm.js';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
@ -18,10 +20,12 @@ const appStore = useAppStore();
const { isHeaderMounted } = storeToRefs(appStore); const { isHeaderMounted } = storeToRefs(appStore);
const router = useRouter(); const router = useRouter();
const loading = ref(false);
const orders = ref([]); const orders = ref([]);
const getOrders = async () => { const getOrders = async () => {
try { try {
loading.value = true;
orders.value = await jApi.query( orders.value = await jApi.query(
`SELECT o.id, o.sent, o.deliveryMethodFk, o.taxableBase, `SELECT o.id, o.sent, o.deliveryMethodFk, o.taxableBase,
a.nickname, am.description agency a.nickname, am.description agency
@ -31,6 +35,7 @@ const getOrders = async () => {
WHERE NOT o.isConfirmed WHERE NOT o.isConfirmed
ORDER BY o.sent DESC` ORDER BY o.sent DESC`
); );
loading.value = false;
} catch (error) { } catch (error) {
console.error('Error getting orders:', error); console.error('Error getting orders:', error);
} }
@ -78,45 +83,47 @@ onMounted(async () => {
</QBtn> </QBtn>
</Teleport> </Teleport>
<QPage class="vn-w-sm"> <QPage class="vn-w-sm">
<CardList <VnList :rows="orders" :loading="loading">
v-for="(order, index) in orders" <CardList
:key="index" v-for="(order, index) in orders"
:to="{ name: 'basket', params: { id: order.id } }" :key="index"
> :to="{ name: 'basket', params: { id: order.id } }"
<template #content> >
<QItemLabel class="text-bold q-mb-sm"> <template #content>
{{ formatDateTitle(order.sent) }} <QItemLabel class="text-bold q-mb-sm">
</QItemLabel> {{ formatDateTitle(order.sent) }}
<QItemLabel> #{{ order.id }} </QItemLabel> </QItemLabel>
<QItemLabel>{{ order.nickname }}</QItemLabel> <QItemLabel> #{{ order.id }} </QItemLabel>
<QItemLabel>{{ order.agency }}</QItemLabel> <QItemLabel>{{ order.nickname }}</QItemLabel>
<QItemLabel>{{ currency(order.taxableBase) }}</QItemLabel> <QItemLabel>{{ order.agency }}</QItemLabel>
</template> <QItemLabel>{{ currency(order.taxableBase) }}</QItemLabel>
<template #actions> </template>
<QBtn <template #actions>
icon="delete" <QBtn
flat icon="delete"
rounded flat
@click.stop.prevent=" rounded
openConfirmationModal( @click.stop.prevent="
null, openConfirmationModal(
t('areYouSureDeleteOrder'), null,
() => removeOrder(order.id, index) t('areYouSureDeleteOrder'),
) () => removeOrder(order.id, index)
" )
> "
<QTooltip>{{ t('deleteOrder') }}</QTooltip> >
</QBtn> <QTooltip>{{ t('deleteOrder') }}</QTooltip>
<QBtn </QBtn>
icon="shopping_bag" <QBtn
flat icon="shopping_bag"
rounded flat
@click.stop.prevent="loadOrder(order.id)" rounded
> @click.stop.prevent="loadOrder(order.id)"
<QTooltip>{{ t('loadOrderIntoCart') }}</QTooltip> >
</QBtn> <QTooltip>{{ t('loadOrderIntoCart') }}</QTooltip>
</template> </QBtn>
</CardList> </template>
</CardList>
</VnList>
</QPage> </QPage>
</template> </template>

View File

@ -63,9 +63,7 @@ const loginAsGuest = async () => {
<template> <template>
<div class="main"> <div class="main">
<div class="header"> <div class="header">
<router-link to="/" class="block"> <img src="statics/logo.svg" alt="Verdnatura" class="block" />
<img src="statics/logo.svg" alt="Verdnatura" class="block" />
</router-link>
</div> </div>
<QForm @submit="login()" class="q-gutter-y-md"> <QForm @submit="login()" class="q-gutter-y-md">
<div class="q-gutter-y-sm"> <div class="q-gutter-y-sm">

View File

@ -41,7 +41,9 @@ export default route(function (/* { store, ssrContext } */) {
if (from.name === to.name) return; if (from.name === to.name) return;
const app = useAppStore(); const app = useAppStore();
app.$patch({ app.$patch({
title: i18n.global.t(to.meta.title || 'home'), title: i18n.global.t(
to.meta.title ? `titles.${to.meta.title}` : 'titles.Home'
),
subtitle: null, subtitle: null,
useRightDrawer: false, useRightDrawer: false,
rightDrawerOpen: true rightDrawerOpen: true

View File

@ -68,6 +68,7 @@ export const useAppStore = defineStore('hedera', {
}, },
async init() { async init() {
// this.router.push({ name: 'login' });
this.getBasketOrderId(); this.getBasketOrderId();
this.getLocaleDates(); this.getLocaleDates();
}, },
@ -150,6 +151,14 @@ export const useAppStore = defineStore('hedera', {
isMobile() { isMobile() {
const $q = useQuasar(); const $q = useQuasar();
return $q?.screen?.width <= 768; return $q?.screen?.width <= 768;
},
isTablet() {
const $q = useQuasar();
return $q?.screen?.width <= 1024;
},
isDesktop() {
const $q = useQuasar();
return $q?.screen?.width > 1024;
} }
} }
}); });

View File

@ -37,6 +37,10 @@ export const useUserStore = defineStore('user', {
actions: { actions: {
async init() { async init() {
this.isGuest = localStorage.getItem('hederaGuest') || false; this.isGuest = localStorage.getItem('hederaGuest') || false;
const autoLoginStatus = await this.tryAutoLogin();
if (!autoLoginStatus) {
this.router.push({ name: 'login' });
}
await this.fetchUser(); await this.fetchUser();
await this.supplantInit(); await this.supplantInit();
this.updateSiteLocale(); this.updateSiteLocale();
@ -48,6 +52,21 @@ export const useUserStore = defineStore('user', {
localStorage.getItem('vnToken'); localStorage.getItem('vnToken');
}, },
async tryAutoLogin() {
const guest = localStorage.getItem('hederaGuest');
if (this.isGuest || guest) {
localStorage.setItem('hederaGuest', true);
return true;
}
if (!this.token) this.getToken();
if (this.token) return true;
return false;
},
async login(user, password, remember) { async login(user, password, remember) {
const params = { user, password }; const params = { user, password };
const res = await api.post('Accounts/login', params); const res = await api.post('Accounts/login', params);