0
1
Fork 0

More linting and formatting

This commit is contained in:
William Buezas 2024-07-16 22:02:31 -03:00
parent e0cc4e40ba
commit bf2094163d
17 changed files with 1416 additions and 1367 deletions

View File

@ -15,22 +15,19 @@ module.exports = {
'vue/setup-compiler-macros': true, 'vue/setup-compiler-macros': true,
}, },
extends: [ extends: [
// Base ESLint recommended rules // Base ESLint recommended rules
// 'eslint:recommended', // 'eslint:recommended',
// Uncomment any of the lines below to choose desired strictness,
// but leave only one uncommented!
// See https://eslint.vuejs.org/rules/#available-rules
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
'standard'
],
// Uncomment any of the lines below to choose desired strictness,
// but leave only one uncommented!
// See https://eslint.vuejs.org/rules/#available-rules
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
'standard',
],
plugins: ['vue', 'prettier'], plugins: ['vue', 'prettier'],
@ -71,13 +68,14 @@ module.exports = {
// allow debugger during development only // allow debugger during development only
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
}, },
overrides: [ overrides: [
{ {
extends: ['plugin:vue/vue3-essential'], extends: ['plugin:vue/vue3-essential'],
files: ['src/**/*.{js,vue,scss}'], // Aplica ESLint solo a archivos .js, .vue y .scss dentro de src (Proyecto de quasar) files: ['src/**/*.{js,vue,scss}'], // Aplica ESLint solo a archivos .js, .vue y .scss dentro de src (Proyecto de quasar)
rules: { rules: {
semi: 'off', semi: 'off',
indent: ['error', 4, { SwitchCase: 1 }], indent: ['error', 4, { SwitchCase: 1 }],
'space-before-function-paren': 'off',
}, },
}, },
], ],

View File

@ -3,7 +3,7 @@ module.exports = {
tabWidth: 4, tabWidth: 4,
useTabs: false, useTabs: false,
singleQuote: true, singleQuote: true,
trailingComma: 'all',
bracketSpacing: true, bracketSpacing: true,
arrowParens: 'avoid', arrowParens: 'avoid',
trailingComma: 'none'
}; };

13
.vscode/settings.json vendored
View File

@ -4,14 +4,7 @@
"editor.bracketPairColorization.enabled": true, "editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": true, "editor.guides.bracketPairs": true,
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.defaultFormatter": "dbaeumer.vscode-eslint", "editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": [ "editor.codeActionsOnSave": ["source.fixAll.eslint"],
"source.fixAll.eslint" "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"]
],
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"vue"
],
} }

View File

@ -1,11 +1,11 @@
<template> <template>
<router-view /> <router-view />
</template> </template>
<script> <script>
import { defineComponent } from 'vue' import { defineComponent } from 'vue';
export default defineComponent({ export default defineComponent({
name: 'App' name: 'App'
}) });
</script> </script>

View File

@ -1,34 +1,34 @@
<template> <template>
<QLayout <QLayout
id="bg" id="bg"
class="fullscreen row justify-center items-center layout-view scroll" class="fullscreen row justify-center items-center layout-view scroll"
> >
<div class="column q-pa-md row items-center justify-center"> <div class="column q-pa-md row items-center justify-center">
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<transition> <transition>
<component :is="Component" /> <component :is="Component" />
</transition> </transition>
</router-view> </router-view>
</div> </div>
</QLayout> </QLayout>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
#bg { #bg {
background: white; background: white;
} }
.column { .column {
width: 270px; width: 270px;
overflow: hidden; overflow: hidden;
& > * { & > * {
width: 100%; width: 100%;
} }
} }
</style> </style>
<script> <script>
export default { export default {
name: 'LoginLayout' name: 'LoginLayout'
} };
</script> </script>

View File

@ -1,129 +1,131 @@
<template> <template>
<QLayout view="lHh Lpr lFf"> <QLayout view="lHh Lpr lFf">
<QHeader> <QHeader>
<QToolbar> <QToolbar>
<QBtn <QBtn
flat flat
dense dense
round round
icon="menu" icon="menu"
aria-label="Menu" aria-label="Menu"
@click="toggleLeftDrawer" @click="toggleLeftDrawer"
/> />
<QToolbarTitle> <QToolbarTitle>
{{ $app.title }} {{ $app.title }}
<div v-if="$app.subtitle" class="subtitle text-caption"> <div v-if="$app.subtitle" class="subtitle text-caption">
{{ $app.subtitle }} {{ $app.subtitle }}
</div> </div>
</QToolbarTitle> </QToolbarTitle>
<div id="actions" ref="actions"></div> <div id="actions" ref="actions"></div>
<QBtn <QBtn
v-if="$app.useRightDrawer" v-if="$app.useRightDrawer"
@click="$app.rightDrawerOpen = !$app.rightDrawerOpen" @click="$app.rightDrawerOpen = !$app.rightDrawerOpen"
aria-label="Menu" aria-label="Menu"
flat flat
dense dense
round round
> >
<QIcon name="menu" /> <QIcon name="menu" />
</QBtn> </QBtn>
</QToolbar> </QToolbar>
</QHeader> </QHeader>
<QDrawer v-model="leftDrawerOpen" :width="250" show-if-above> <QDrawer v-model="leftDrawerOpen" :width="250" show-if-above>
<QToolbar class="logo"> <QToolbar class="logo">
<img src="statics/logo-dark.svg" /> <img src="statics/logo-dark.svg" />
</QToolbar> </QToolbar>
<div class="user-info"> <div class="user-info">
<div> <div>
<span id="user-name">{{ user.nickname }}</span> <span id="user-name">{{ user.nickname }}</span>
<QBtn flat icon="logout" alt="_Exit" @click="logout()" /> <QBtn flat icon="logout" alt="_Exit" @click="logout()" />
</div> </div>
<div id="supplant" class="supplant"> <div id="supplant" class="supplant">
<span id="supplanted">{{ supplantedUser }}</span> <span id="supplanted">{{ supplantedUser }}</span>
<QBtn flat icon="logout" alt="_Exit" /> <QBtn flat icon="logout" alt="_Exit" />
</div> </div>
</div> </div>
<QList v-for="item in essentialLinks" :key="item.id"> <QList v-for="item in essentialLinks" :key="item.id">
<QItem v-if="!item.childs" :to="`/${item.path}`"> <QItem v-if="!item.childs" :to="`/${item.path}`">
<QItemSection> <QItemSection>
<QItemLabel>{{ item.description }}</QItemLabel> <QItemLabel>{{ item.description }}</QItemLabel>
</QItemSection> </QItemSection>
</QItem> </QItem>
<QExpansionItem <QExpansionItem
v-if="item.childs" v-if="item.childs"
:label="item.description" :label="item.description"
expand-separator expand-separator
> >
<QList> <QList>
<QItem <QItem
v-for="subitem in item.childs" v-for="subitem in item.childs"
:key="subitem.id" :key="subitem.id"
:to="`/${subitem.path}`" :to="`/${subitem.path}`"
class="q-pl-lg" class="q-pl-lg"
> >
<QItemSection> <QItemSection>
<QItemLabel>{{ subitem.description }}</QItemLabel> <QItemLabel>
</QItemSection> {{ subitem.description }}
</QItem> </QItemLabel>
</QList> </QItemSection>
</QExpansionItem> </QItem>
</QList> </QList>
</QDrawer> </QExpansionItem>
<QPageContainer> </QList>
<router-view /> </QDrawer>
</QPageContainer> <QPageContainer>
</QLayout> <router-view />
</QPageContainer>
</QLayout>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.q-toolbar { .q-toolbar {
min-height: 64px; min-height: 64px;
} }
.logo { .logo {
background-color: $primary; background-color: $primary;
justify-content: center; justify-content: center;
& > img { & > img {
width: 160px; width: 160px;
} }
} }
.user-info { .user-info {
margin: 25px; margin: 25px;
& > div { & > div {
display: flex;
justify-content: space-between;
align-items: center;
overflow: hidden;
border: 1px solid #eaeaea;
& > span {
padding: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: bold;
}
.q-btn {
display: block;
margin: 0;
padding: 9px;
border-radius: 0;
&:hover {
background-color: #1a1a1a;
color: white;
}
}
&.supplant {
display: none;
border-top: none;
&.show {
display: flex; display: flex;
} justify-content: space-between;
align-items: center;
overflow: hidden;
border: 1px solid #eaeaea;
& > span {
padding: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: bold;
}
.q-btn {
display: block;
margin: 0;
padding: 9px;
border-radius: 0;
&:hover {
background-color: #1a1a1a;
color: white;
}
}
&.supplant {
display: none;
border-top: none;
&.show {
display: flex;
}
}
} }
}
} }
</style> </style>
@ -131,107 +133,107 @@
@import 'src/css/responsive'; @import 'src/css/responsive';
.q-drawer { .q-drawer {
.q-item { .q-item {
padding-left: 38px; padding-left: 38px;
} }
.q-list .q-list .q-item { .q-list .q-list .q-item {
padding-left: 50px; padding-left: 50px;
} }
} }
.q-page-container > * { .q-page-container > * {
padding: 16px; padding: 16px;
} }
#actions > div { #actions > div {
display: flex; display: flex;
align-items: center; align-items: center;
} }
@include mobile { @include mobile {
#actions > div { #actions > div {
.q-btn { .q-btn {
border-radius: 50%; border-radius: 50%;
padding: 10px; padding: 10px;
&__content { &__content {
& > .q-icon { & > .q-icon {
margin-right: 0; margin-right: 0;
}
& > .block {
display: none !important;
}
}
} }
& > .block {
display: none !important;
}
}
} }
}
} }
</style> </style>
<script> <script>
import { defineComponent, ref } from 'vue' import { defineComponent, ref } from 'vue';
import { userStore } from 'stores/user' import { userStore } from 'stores/user';
export default defineComponent({ export default defineComponent({
name: 'MainLayout', name: 'MainLayout',
props: {}, props: {},
setup () { setup() {
const leftDrawerOpen = ref(false) const leftDrawerOpen = ref(false);
return { return {
user: userStore(), user: userStore(),
supplantedUser: ref(''), supplantedUser: ref(''),
essentialLinks: ref(null), essentialLinks: ref(null),
leftDrawerOpen, leftDrawerOpen,
toggleLeftDrawer () { toggleLeftDrawer() {
leftDrawerOpen.value = !leftDrawerOpen.value leftDrawerOpen.value = !leftDrawerOpen.value;
} }
} };
}, },
async mounted () { async mounted() {
this.$refs.actions.appendChild(this.$actions) this.$refs.actions.appendChild(this.$actions);
await this.user.loadData() await this.user.loadData();
await this.$app.loadConfig() await this.$app.loadConfig();
await this.fetchData() await this.fetchData();
}, },
methods: { methods: {
async fetchData () { async fetchData() {
const sections = await this.$jApi.query('SELECT * FROM myMenu') const sections = await this.$jApi.query('SELECT * FROM myMenu');
const sectionMap = new Map() const sectionMap = new Map();
for (const section of sections) { for (const section of sections) {
sectionMap.set(section.id, section) sectionMap.set(section.id, section);
} }
const sectionTree = [] const sectionTree = [];
for (const section of sections) { for (const section of sections) {
const parent = section.parentFk const parent = section.parentFk;
if (parent) { if (parent) {
const parentSection = sectionMap.get(parent) const parentSection = sectionMap.get(parent);
if (!parentSection) continue if (!parentSection) continue;
let childs = parentSection.childs let childs = parentSection.childs;
if (!childs) { if (!childs) {
childs = parentSection.childs = [] childs = parentSection.childs = [];
} }
childs.push(section) childs.push(section);
} else { } else {
sectionTree.push(section) sectionTree.push(section);
} }
} }
this.essentialLinks = sectionTree this.essentialLinks = sectionTree;
}, },
async logout () { async logout() {
this.user.logout() this.user.logout();
this.$router.push('/login') this.$router.push('/login');
} }
} }
}) });
</script> </script>
<i18n lang="yaml"> <i18n lang="yaml">
en-US: en-US:
visitor: Visitor visitor: Visitor
es-ES: es-ES:
visitor: Visitante visitor: Visitante
</i18n> </i18n>

View File

@ -1,76 +1,77 @@
<template> <template>
<div style="padding: 0"> <div style="padding: 0">
<div class="q-pa-sm row items-start"> <div class="q-pa-sm row items-start">
<div class="new-card q-pa-sm" v-for="myNew in news" :key="myNew.id"> <div class="new-card q-pa-sm" v-for="myNew in news" :key="myNew.id">
<QCard> <QCard>
<QImg :src="`${$app.imageUrl}/news/full/${myNew.image}`"> </QImg> <QImg :src="`${$app.imageUrl}/news/full/${myNew.image}`">
<QCardSection> </QImg>
<div class="text-h5">{{ myNew.title }}</div> <QCardSection>
</QCardSection> <div class="text-h5">{{ myNew.title }}</div>
<QCardSection class="new-body"> </QCardSection>
<div v-html="myNew.text" /> <QCardSection class="new-body">
</QCardSection> <div v-html="myNew.text" />
</QCard> </QCardSection>
</div> </QCard>
</div>
</div>
<QPageSticky>
<QBtn
fab
icon="add_shopping_cart"
color="accent"
to="/ecomerce/catalog"
:title="$t('startOrder')"
/>
</QPageSticky>
</div> </div>
<QPageSticky>
<QBtn
fab
icon="add_shopping_cart"
color="accent"
to="/ecomerce/catalog"
:title="$t('startOrder')"
/>
</QPageSticky>
</div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.new-card { .new-card {
width: 100%; width: 100%;
@media screen and (min-width: 800px) and (max-width: 1400px) { @media screen and (min-width: 800px) and (max-width: 1400px) {
width: 50%; width: 50%;
} }
@media screen and (min-width: 1401px) and (max-width: 1920px) { @media screen and (min-width: 1401px) and (max-width: 1920px) {
width: 33.33%; width: 33.33%;
} }
@media screen and (min-width: 19021) { @media screen and (min-width: 19021) {
width: 25%; width: 25%;
} }
} }
.new-body { .new-body {
font-family: 'Open Sans'; font-family: 'Open Sans';
} }
</style> </style>
<script> <script>
export default { export default {
name: 'PageIndex', name: 'PageIndex',
data () { data() {
return { return {
news: [] news: []
} };
}, },
async mounted () { async mounted() {
this.news = await this.$jApi.query( this.news = await this.$jApi.query(
`SELECT title, text, image, id `SELECT title, text, image, id
FROM news FROM news
ORDER BY priority, created DESC` ORDER BY priority, created DESC`
) );
} }
} };
</script> </script>
<i18n lang="yaml"> <i18n lang="yaml">
en-US: en-US:
startOrder: Start order startOrder: Start order
es-ES: es-ES:
startOrder: Empezar pedido startOrder: Empezar pedido
ca-ES: ca-ES:
startOrder: Començar comanda startOrder: Començar comanda
fr-FR: fr-FR:
startOrder: Lancer commande startOrder: Lancer commande
pt-PT: pt-PT:
startOrder: Comece uma encomenda startOrder: Comece uma encomenda
</i18n> </i18n>

File diff suppressed because it is too large Load Diff

View File

@ -1,86 +1,96 @@
<template> <template>
<Teleport :to="$actions"> <Teleport :to="$actions">
<QSelect <QSelect
v-model="year" v-model="year"
:options="years" :options="years"
color="white" color="white"
dark dark
standout standout
dense dense
rounded rounded
/> />
</Teleport> </Teleport>
<div class="vn-w-sm"> <div class="vn-w-sm">
<div <div
v-if="!invoices?.length" v-if="!invoices?.length"
class="text-subtitle1 text-center text-grey-7 q-pa-md" class="text-subtitle1 text-center text-grey-7 q-pa-md"
> >
{{ $t('noInvoicesFound') }} {{ $t('noInvoicesFound') }}
</div>
<QCard v-if="invoices?.length">
<QTable
:columns="columns"
:pagination="pagination"
:rows="invoices"
row-key="id"
hide-header
hide-bottom
>
<template v-slot:body="props">
<QTr :props="props">
<QTd key="ref" :props="props">
{{ props.row.ref }}
</QTd>
<QTd key="issued" :props="props">
{{ date(props.row.issued, 'ddd, MMMM Do') }}
</QTd>
<QTd key="amount" :props="props">
{{ currency(props.row.amount) }}
</QTd>
<QTd key="hasPdf" :props="props">
<QBtn
v-if="props.row.hasPdf"
icon="download"
:title="$t('downloadInvoicePdf')"
:href="invoiceUrl(props.row.id)"
target="_blank"
flat
round
/>
<QIcon
v-else
name="warning"
:title="$t('notDownloadable')"
color="warning"
size="24px"
/>
</QTd>
</QTr>
</template>
</QTable>
</QCard>
</div> </div>
<QCard v-if="invoices?.length">
<QTable
:columns="columns"
:pagination="pagination"
:rows="invoices"
row-key="id"
hide-header
hide-bottom
>
<template v-slot:body="props">
<QTr :props="props">
<QTd key="ref" :props="props">
{{ props.row.ref }}
</QTd>
<QTd key="issued" :props="props">
{{ date(props.row.issued, 'ddd, MMMM Do') }}
</QTd>
<QTd key="amount" :props="props">
{{ currency(props.row.amount) }}
</QTd>
<QTd key="hasPdf" :props="props">
<QBtn
v-if="props.row.hasPdf"
icon="download"
:title="$t('downloadInvoicePdf')"
:href="invoiceUrl(props.row.id)"
target="_blank"
flat
round
/>
<QIcon
v-else
name="warning"
:title="$t('notDownloadable')"
color="warning"
size="24px"
/>
</QTd>
</QTr>
</template>
</QTable>
</QCard>
</div>
</template> </template>
<script> <script>
import { date, currency } from 'src/lib/filters.js' import { date, currency } from 'src/lib/filters.js';
export default { export default {
name: 'OrdersPendingIndex', name: 'OrdersPendingIndex',
data () { data() {
const curYear = new Date().getFullYear() const curYear = new Date().getFullYear();
const years = [] const years = [];
for (let year = curYear - 5; year <= curYear; year++) { for (let year = curYear - 5; year <= curYear; year++) {
years.push(year) years.push(year);
} }
return { return {
columns: [ columns: [
{ name: 'ref', label: 'serial', field: 'ref', align: 'left' }, { name: 'ref', label: 'serial', field: 'ref', align: 'left' },
{ name: 'issued', label: 'issued', field: 'issued', align: 'left' }, {
name: 'issued',
label: 'issued',
field: 'issued',
align: 'left'
},
{ name: 'amount', label: 'amount', field: 'amount' }, { name: 'amount', label: 'amount', field: 'amount' },
{ name: 'hasPdf', label: 'download', field: 'hasPdf', align: 'center' } {
name: 'hasPdf',
label: 'download',
field: 'hasPdf',
align: 'center'
}
], ],
pagination: { pagination: {
rowsPerPage: 0 rowsPerPage: 0
@ -88,16 +98,16 @@ export default {
year: curYear, year: curYear,
years, years,
invoices: null invoices: null
} };
}, },
async mounted () { async mounted() {
await this.loadData() await this.loadData();
}, },
watch: { watch: {
async year () { async year() {
await this.loadData() await this.loadData();
} }
}, },
@ -105,11 +115,11 @@ export default {
date, date,
currency, currency,
async loadData () { async loadData() {
const params = { const params = {
from: new Date(this.year, 0), from: new Date(this.year, 0),
to: new Date(this.year, 11, 31, 23, 59, 59) to: new Date(this.year, 11, 31, 23, 59, 59)
} };
this._invoices = await this.$jApi.query( this._invoices = await this.$jApi.query(
`SELECT id, ref, issued, amount, hasPdf `SELECT id, ref, issued, amount, hasPdf
FROM myInvoice FROM myInvoice
@ -117,57 +127,57 @@ export default {
ORDER BY issued DESC ORDER BY issued DESC
LIMIT 500`, LIMIT 500`,
params params
) );
}, },
invoiceUrl (id) { invoiceUrl(id) {
return ( return (
'?' + '?' +
new URLSearchParams({ new URLSearchParams({
srv: 'rest:dms/invoice', srv: 'rest:dms/invoice',
invoice: id, invoice: id,
access_token: this.$user.token access_token: this.$user.token
}).toString() }).toString()
) );
} }
} }
} };
</script> </script>
<i18n lang="yaml"> <i18n lang="yaml">
en-US: en-US:
noInvoicesFound: No invoices found noInvoicesFound: No invoices found
serial: Serial serial: Serial
issued: Date issued: Date
amount: Import amount: Import
downloadInvoicePdf: Download invoice PDF downloadInvoicePdf: Download invoice PDF
notDownloadable: Not available for download, request the invoice to your salesperson notDownloadable: Not available for download, request the invoice to your salesperson
es-ES: es-ES:
noInvoicesFound: No se han encontrado facturas noInvoicesFound: No se han encontrado facturas
serial: Serie serial: Serie
issued: Fecha issued: Fecha
amount: Importe amount: Importe
downloadInvoicePdf: Descargar factura en PDF downloadInvoicePdf: Descargar factura en PDF
notDownloadable: No disponible para descarga, solicita la factura a tu comercial notDownloadable: No disponible para descarga, solicita la factura a tu comercial
ca-ES: ca-ES:
noInvoicesFound: No s'han trobat factures noInvoicesFound: No s'han trobat factures
serial: Sèrie serial: Sèrie
issued: Data issued: Data
amount: Import amount: Import
downloadInvoicePdf: Descarregar PDF downloadInvoicePdf: Descarregar PDF
notDownloadable: No disponible per cescarrega, sol·licita la factura al teu comercial notDownloadable: No disponible per cescarrega, sol·licita la factura al teu comercial
fr-FR: fr-FR:
noInvoicesFound: Aucune facture trouvée noInvoicesFound: Aucune facture trouvée
serial: Série serial: Série
issued: Date issued: Date
amount: Montant amount: Montant
downloadInvoicePdf: Télécharger le PDF downloadInvoicePdf: Télécharger le PDF
notDownloadable: Non disponible en téléchargement, demander la facture à votre commercial notDownloadable: Non disponible en téléchargement, demander la facture à votre commercial
pt-PT: pt-PT:
noInvoicesFound: Nenhuma fatura encontrada noInvoicesFound: Nenhuma fatura encontrada
serial: Serie serial: Serie
issued: Data issued: Data
amount: Importe amount: Importe
downloadInvoicePdf: Baixar PDF downloadInvoicePdf: Baixar PDF
notDownloadable: Não disponível para download, solicite a fatura ao seu comercial notDownloadable: Não disponível para download, solicite a fatura ao seu comercial
</i18n> </i18n>

View File

@ -1,194 +1,199 @@
<template> <template>
<Teleport :to="$actions"> <Teleport :to="$actions">
<div class="balance"> <div class="balance">
<span class="label">{{ $t('balance') }}</span> <span class="label">{{ $t('balance') }}</span>
<span class="amount" :class="{ negative: debt < 0 }"> <span class="amount" :class="{ negative: debt < 0 }">
{{ currency(debt || 0) }} {{ currency(debt || 0) }}
</span> </span>
<QIcon name="info" :title="$t('paymentInfo')" class="info" size="24px" /> <QIcon
</div> name="info"
<QBtn :title="$t('paymentInfo')"
icon="payments" class="info"
:label="$t('makePayment')" size="24px"
@click="onPayClick()" />
rounded </div>
no-caps <QBtn
/> icon="payments"
<QBtn :label="$t('makePayment')"
to="/ecomerce/basket" @click="onPayClick()"
icon="shopping_cart" rounded
:label="$t('shoppingCart')" no-caps
rounded />
no-caps <QBtn
/> to="/ecomerce/basket"
</Teleport> icon="shopping_cart"
<div class="vn-w-sm"> :label="$t('shoppingCart')"
<div rounded
v-if="!orders?.length" no-caps
class="text-subtitle1 text-center text-grey-7 q-pa-md" />
> </Teleport>
{{ $t('noOrdersFound') }} <div class="vn-w-sm">
</div> <div
<QCard v-if="orders?.length"> v-if="!orders?.length"
<QList bordered separator padding> class="text-subtitle1 text-center text-grey-7 q-pa-md"
<QItem
v-for="order in orders"
:key="order.id"
:to="`ticket/${order.id}`"
clickable
v-ripple
> >
<QItemSection> {{ $t('noOrdersFound') }}
<QItemLabel> </div>
{{ date(order.landed, 'ddd, MMMM Do') }} <QCard v-if="orders?.length">
</QItemLabel> <QList bordered separator padding>
<QItemLabel caption>#{{ order.id }}</QItemLabel> <QItem
<QItemLabel caption>{{ order.nickname }}</QItemLabel> v-for="order in orders"
<QItemLabel caption>{{ order.agency }}</QItemLabel> :key="order.id"
</QItemSection> :to="`ticket/${order.id}`"
<QItemSection side top> {{ order.total }} </QItemSection> clickable
</QItem> v-ripple
</QList> >
</QCard> <QItemSection>
<QPageSticky> <QItemLabel>
<QBtn {{ date(order.landed, 'ddd, MMMM Do') }}
fab </QItemLabel>
icon="add_shopping_cart" <QItemLabel caption>#{{ order.id }}</QItemLabel>
color="accent" <QItemLabel caption>{{ order.nickname }}</QItemLabel>
to="/ecomerce/catalog" <QItemLabel caption>{{ order.agency }}</QItemLabel>
:title="$t('startOrder')" </QItemSection>
/> <QItemSection side top> {{ order.total }} </QItemSection>
</QPageSticky> </QItem>
</div> </QList>
</QCard>
<QPageSticky>
<QBtn
fab
icon="add_shopping_cart"
color="accent"
to="/ecomerce/catalog"
:title="$t('startOrder')"
/>
</QPageSticky>
</div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.balance { .balance {
margin-right: 8px; margin-right: 8px;
white-space: nowrap; white-space: nowrap;
display: inline-block; display: inline-block;
& > * { & > * {
vertical-align: middle; vertical-align: middle;
} }
& > .amount { & > .amount {
padding: 4px; padding: 4px;
margin: 0 4px; margin: 0 4px;
&.negative { &.negative {
background-color: #e55; background-color: #e55;
border-radius: 2px; border-radius: 2px;
box-shadow: 0 0 5px #333; box-shadow: 0 0 5px #333;
}
}
& > .info {
cursor: pointer;
} }
}
& > .info {
cursor: pointer;
}
} }
</style> </style>
<script> <script>
import { date, currency } from 'src/lib/filters.js' import { date, currency } from 'src/lib/filters.js';
import { tpvStore } from 'stores/tpv' import { tpvStore } from 'stores/tpv';
export default { export default {
name: 'OrdersPendingIndex', name: 'OrdersPendingIndex',
data () { data() {
return { return {
orders: null, orders: null,
debt: 0, debt: 0,
tpv: tpvStore() tpv: tpvStore()
} };
}, },
async mounted () { async mounted() {
await this.tpv.check(this.$route) await this.tpv.check(this.$route);
this.orders = await this.$jApi.query('CALL myTicket_list(NULL, NULL)') this.orders = await this.$jApi.query('CALL myTicket_list(NULL, NULL)');
this.debt = await this.$jApi.getValue('SELECT -myClient_getDebt(NULL)') this.debt = await this.$jApi.getValue('SELECT -myClient_getDebt(NULL)');
}, },
methods: { methods: {
date, date,
currency, currency,
async onPayClick () { async onPayClick() {
let amount = -this.debt let amount = -this.debt;
amount = amount <= 0 ? null : amount amount = amount <= 0 ? null : amount;
let defaultAmountStr = '' let defaultAmountStr = '';
if (amount !== null) { if (amount !== null) {
defaultAmountStr = amount defaultAmountStr = amount;
} }
amount = prompt(this.$t('amountToPay'), defaultAmountStr) amount = prompt(this.$t('amountToPay'), defaultAmountStr);
if (amount != null) { if (amount != null) {
amount = parseFloat(amount.replace(',', '.')) amount = parseFloat(amount.replace(',', '.'));
await this.tpv.pay(amount) await this.tpv.pay(amount);
} }
} }
} }
} };
</script> </script>
<i18n lang="yaml"> <i18n lang="yaml">
en-US: en-US:
startOrder: Start order startOrder: Start order
noOrdersFound: No orders found noOrdersFound: No orders found
makePayment: Make payment makePayment: Make payment
shoppingCart: Shopping cart shoppingCart: Shopping cart
balance: 'Balance:' balance: 'Balance:'
paymentInfo: >- paymentInfo: >-
The amount shown is your slope (negative) or favorable balance today, it The amount shown is your slope (negative) or favorable balance today, it
disregards future orders. For get your order shipped, this amount must be disregards future orders. For get your order shipped, this amount must be
equal to or greater than 0. If you want to make a down payment, click the equal to or greater than 0. If you want to make a down payment, click the
payment button, delete the suggested amount and enter the amount you want. payment button, delete the suggested amount and enter the amount you want.
es-ES: es-ES:
startOrder: Empezar pedido startOrder: Empezar pedido
noOrdersFound: No se encontrado pedidos noOrdersFound: No se encontrado pedidos
makePayment: Realizar pago makePayment: Realizar pago
shoppingCart: Cesta de la compra shoppingCart: Cesta de la compra
balance: 'Saldo:' balance: 'Saldo:'
paymentInfo: >- paymentInfo: >-
La cantidad mostrada es tu saldo pendiente (negativa) o favorable a día de La cantidad mostrada es tu saldo pendiente (negativa) o favorable a día de
hoy, no tiene en cuenta pedidos del futuro. Para que tu pedido sea enviado, hoy, no tiene en cuenta pedidos del futuro. Para que tu pedido sea enviado,
esta cantidad debe ser igual o mayor que 0. Si quieres realizar una entrega a esta cantidad debe ser igual o mayor que 0. Si quieres realizar una entrega a
cuenta, pulsa el botón de pago, borra la cantidad sugerida e introduce la cuenta, pulsa el botón de pago, borra la cantidad sugerida e introduce la
cantidad que desees. cantidad que desees.
ca-ES: ca-ES:
startOrder: Començar encàrrec startOrder: Començar encàrrec
noOrdersFound: No s'han trobat comandes noOrdersFound: No s'han trobat comandes
makePayment: Realitzar pagament makePayment: Realitzar pagament
shoppingCart: Cistella de la compra shoppingCart: Cistella de la compra
balance: 'Saldo:' balance: 'Saldo:'
paymentInfo: >- paymentInfo: >-
La quantitat mostrada és el teu saldo pendent (negatiu) o favorable a dia La quantitat mostrada és el teu saldo pendent (negatiu) o favorable a dia
d'avui, no en compte comandes del futur. Perquè la teva comanda sigui d'avui, no en compte comandes del futur. Perquè la teva comanda sigui
enviat, aquesta quantitat ha de ser igual o més gran que 0. Si vols fer un enviat, aquesta quantitat ha de ser igual o més gran que 0. Si vols fer un
lliurament a compte, prem el botó de pagament, esborra la quantitat suggerida lliurament a compte, prem el botó de pagament, esborra la quantitat suggerida
e introdueix la quantitat que vulguis. e introdueix la quantitat que vulguis.
fr-FR: fr-FR:
startOrder: Acheter startOrder: Acheter
noOrdersFound: Aucune commande trouvée noOrdersFound: Aucune commande trouvée
makePayment: Effectuer un paiement makePayment: Effectuer un paiement
shoppingCart: Panier shoppingCart: Panier
balance: 'Balance:' balance: 'Balance:'
paymentInfo: >- paymentInfo: >-
Le montant indiqué est votre pente (négative) ou balance favorable Le montant indiqué est votre pente (négative) ou balance favorable
aujourd'hui, ne tient pas compte pour les commandes futures. Obtenir votre aujourd'hui, ne tient pas compte pour les commandes futures. Obtenir votre
commande est expédiée, ce montant doit être égal ou supérieur à 0. Si vous commande est expédiée, ce montant doit être égal ou supérieur à 0. Si vous
voulez faire un versement, le montant suggéré effacé et entrez le montant que voulez faire un versement, le montant suggéré effacé et entrez le montant que
vous souhaitez. vous souhaitez.
pt-PT: pt-PT:
startOrder: Iniciar encomenda startOrder: Iniciar encomenda
noOrdersFound: Nenhum pedido encontrado noOrdersFound: Nenhum pedido encontrado
makePayment: Realizar pagamento makePayment: Realizar pagamento
shoppingCart: Cesta da compra shoppingCart: Cesta da compra
balance: 'Saldo:' balance: 'Saldo:'
paymentInfo: >- paymentInfo: >-
A quantidade mostrada é seu saldo pendente (negativo) ou favorável a dia de A quantidade mostrada é seu saldo pendente (negativo) ou favorável a dia de
hoje, não se vincula a pedidos futuros. Para que seu pedido seja enviado, esta hoje, não se vincula a pedidos futuros. Para que seu pedido seja enviado, esta
quantidade deve ser igual ou superior a 0. Se queres realizar um depósito à quantidade deve ser igual ou superior a 0. Se queres realizar um depósito à
conta, clique no botão de pagamento, apague a quantidade sugerida e introduza conta, clique no botão de pagamento, apague a quantidade sugerida e introduza
a quantidade que deseje. a quantidade que deseje.
</i18n> </i18n>

View File

@ -1,136 +1,145 @@
<template> <template>
<Teleport :to="$actions"> <Teleport :to="$actions">
<QBtn <QBtn
icon="print" icon="print"
:label="$t('printDeliveryNote')" :label="$t('printDeliveryNote')"
@click="onPrintClick()" @click="onPrintClick()"
rounded rounded
no-caps no-caps
/> />
</Teleport> </Teleport>
<div> <div>
<QCard class="vn-w-sm"> <QCard class="vn-w-sm">
<QCardSection> <QCardSection>
<div class="text-h6">#{{ ticket.id }}</div> <div class="text-h6">#{{ ticket.id }}</div>
</QCardSection> </QCardSection>
<QCardSection> <QCardSection>
<div class="text-h6">{{ $t('shippingInformation') }}</div> <div class="text-h6">{{ $t('shippingInformation') }}</div>
<div> <div>
{{ $t('preparation') }} {{ date(ticket.shipped, 'ddd, MMMM Do') }} {{ $t('preparation') }}
</div> {{ date(ticket.shipped, 'ddd, MMMM Do') }}
<div> </div>
{{ $t('delivery') }} {{ date(ticket.shipped, 'ddd, MMMM Do') }} <div>
</div> {{ $t('delivery') }}
<div> {{ date(ticket.shipped, 'ddd, MMMM Do') }}
{{ $t(ticket.method != 'PICKUP' ? 'agency' : 'warehouse') }} </div>
{{ ticket.agency }} <div>
</div> {{ $t(ticket.method != 'PICKUP' ? 'agency' : 'warehouse') }}
</QCardSection> {{ ticket.agency }}
<QCardSection> </div>
<div class="text-h6">{{ $t('deliveryAddress') }}</div> </QCardSection>
<div>{{ ticket.nickname }}</div> <QCardSection>
<div>{{ ticket.street }}</div> <div class="text-h6">{{ $t('deliveryAddress') }}</div>
<div> <div>{{ ticket.nickname }}</div>
{{ ticket.postalCode }} {{ ticket.city }} ({{ ticket.province }}) <div>{{ ticket.street }}</div>
</div> <div>
</QCardSection> {{ ticket.postalCode }} {{ ticket.city }} ({{
<QSeparator inset /> ticket.province
<QList v-for="row in rows" :key="row.itemFk"> }})
<QItem> </div>
<QItemSection avatar> </QCardSection>
<QAvatar size="68px"> <QSeparator inset />
<img :src="`${$app.imageUrl}/catalog/200x200/${row.image}`" /> <QList v-for="row in rows" :key="row.itemFk">
</QAvatar> <QItem>
</QItemSection> <QItemSection avatar>
<QItemSection> <QAvatar size="68px">
<QItemLabel lines="1"> <img
{{ row.concept }} :src="`${$app.imageUrl}/catalog/200x200/${row.image}`"
</QItemLabel> />
<QItemLabel lines="1" caption> </QAvatar>
{{ row.value5 }} {{ row.value6 }} {{ row.value7 }} </QItemSection>
</QItemLabel> <QItemSection>
<QItemLabel lines="1"> <QItemLabel lines="1">
{{ row.quantity }} x {{ currency(row.price) }} {{ row.concept }}
</QItemLabel> </QItemLabel>
</QItemSection> <QItemLabel lines="1" caption>
<QItemSection side class="total"> {{ row.value5 }} {{ row.value6 }} {{ row.value7 }}
<QItemLabel> </QItemLabel>
<span class="discount" v-if="row.discount"> <QItemLabel lines="1">
{{ currency(discountSubtotal(row)) }} - {{ row.quantity }} x {{ currency(row.price) }}
{{ currency(row.discount) }} = </QItemLabel>
</span> </QItemSection>
{{ currency(subtotal(row)) }} <QItemSection side class="total">
</QItemLabel> <QItemLabel>
</QItemSection> <span class="discount" v-if="row.discount">
</QItem> {{ currency(discountSubtotal(row)) }} -
</QList> {{ currency(row.discount) }} =
</QCard> </span>
</div> {{ currency(subtotal(row)) }}
</QItemLabel>
</QItemSection>
</QItem>
</QList>
</QCard>
</div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.total { .total {
justify-content: flex-end; justify-content: flex-end;
} }
</style> </style>
<script> <script>
import { date, currency } from 'src/lib/filters.js' import { date, currency } from 'src/lib/filters.js';
export default { export default {
name: 'OrdersConfirmedView', name: 'OrdersConfirmedView',
data () { data() {
return { return {
ticket: {}, ticket: {},
rows: null, rows: null,
services: null, services: null,
packages: null packages: null
} };
}, },
async mounted () { async mounted() {
const params = { const params = {
ticket: parseInt(this.$route.params.id) ticket: parseInt(this.$route.params.id)
} };
this.ticket = await this.$jApi.getObject( this.ticket = await this.$jApi.getObject(
'CALL myTicket_get(#ticket)', 'CALL myTicket_get(#ticket)',
params params
) );
this.rows = await this.$jApi.query('CALL myTicket_getRows(#ticket)', params) this.rows = await this.$jApi.query(
'CALL myTicket_getRows(#ticket)',
params
);
this.services = await this.$jApi.query( this.services = await this.$jApi.query(
'CALL myTicket_getServices(#ticket)', 'CALL myTicket_getServices(#ticket)',
params params
) );
this.packages = await this.$jApi.query( this.packages = await this.$jApi.query(
'CALL myTicket_getPackages(#ticket)', 'CALL myTicket_getPackages(#ticket)',
params params
) );
}, },
methods: { methods: {
date, date,
currency, currency,
discountSubtotal (line) { discountSubtotal(line) {
return line.quantity * line.price return line.quantity * line.price;
}, },
subtotal (line) { subtotal(line) {
const discount = line.discount const discount = line.discount;
return this.discountSubtotal(line) * ((100 - discount) / 100) return this.discountSubtotal(line) * ((100 - discount) / 100);
}, },
onPrintClick () { onPrintClick() {
const params = new URLSearchParams({ const params = new URLSearchParams({
access_token: this.$user.token, access_token: this.$user.token,
recipientId: this.$user.id, recipientId: this.$user.id,
type: 'deliveryNote' type: 'deliveryNote'
}) });
window.open( window.open(
`/api/Tickets/${this.ticket.id}/delivery-note-pdf?${params.toString()}` `/api/Tickets/${this.ticket.id}/delivery-note-pdf?${params.toString()}`
) );
} }
} }
} };
</script> </script>

View File

@ -1,29 +1,31 @@
<template> <template>
<div <div
class="fullscreen bg-accent text-white text-center q-pa-md flex flex-center" class="fullscreen bg-accent text-white text-center q-pa-md flex flex-center"
> >
<div> <div>
<div style="font-size: 30vh">404</div> <div style="font-size: 30vh">404</div>
<div class="text-h2" style="opacity: 0.4">Oops. Nothing here...</div> <div class="text-h2" style="opacity: 0.4">
Oops. Nothing here...
</div>
<QBtn <QBtn
class="q-mt-xl" class="q-mt-xl"
color="white" color="white"
text-color="accent" text-color="accent"
unelevated unelevated
to="/" to="/"
label="Go Home" label="Go Home"
no-caps no-caps
/> />
</div>
</div> </div>
</div>
</template> </template>
<script> <script>
import { defineComponent } from 'vue' import { defineComponent } from 'vue';
export default defineComponent({ export default defineComponent({
name: 'ErrorNotFound' name: 'ErrorNotFound'
}) });
</script> </script>

View File

@ -1,17 +1,17 @@
<template> <template>
<QPage class="flex flex-center"> <QPage class="flex flex-center">
<img <img
alt="Quasar logo" alt="Quasar logo"
src="~assets/quasar-logo-vertical.svg" src="~assets/quasar-logo-vertical.svg"
style="width: 200px; height: 200px" style="width: 200px; height: 200px"
/> />
</QPage> </QPage>
</template> </template>
<script> <script>
import { defineComponent } from 'vue' import { defineComponent } from 'vue';
export default defineComponent({ export default defineComponent({
name: 'IndexPage' name: 'IndexPage'
}) });
</script> </script>

View File

@ -1,72 +1,78 @@
<template> <template>
<div class="main"> <div class="main">
<div class="header"> <div class="header">
<router-link to="/" class="block"> <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> </router-link>
</div>
<QForm @submit="onLogin" class="q-gutter-y-md">
<div class="q-gutter-y-sm">
<QInput v-model="email" :label="$t('user')" autofocus />
<QInput
v-model="password"
ref="password"
:label="$t('password')"
:type="showPwd ? 'password' : 'text'"
>
<template v-slot:append>
<QIcon
:name="showPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="showPwd = !showPwd"
/>
</template>
</QInput>
<QCheckbox
v-model="remember"
:label="$t('remindMe')"
class="remember"
dense
/>
</div>
<div class="justify-center">
<QBtn
type="submit"
:label="$t('logIn')"
class="full-width"
color="primary"
rounded
no-caps
unelevated
/>
</div>
<div class="justify-center">
<QBtn
to="/"
:label="$t('logInAsGuest')"
class="full-width"
color="primary"
rounded
no-caps
outline
/>
</div>
<p class="password-forgotten text-center q-mt-lg">
<router-link to="/remember-password" class="link">
{{ $t('haveForgottenPassword') }}
</router-link>
</p>
</QForm>
<div class="footer text-center">
<p>
{{ $t('notACustomerYet') }}
<a
href="//verdnatura.es/register/"
target="_blank"
class="link"
>
{{ $t('signUp') }}
</a>
</p>
<p class="contact">
{{ $t('loginPhone') }} · {{ $t('loginMail') }}
</p>
</div>
</div> </div>
<QForm @submit="onLogin" class="q-gutter-y-md">
<div class="q-gutter-y-sm">
<QInput v-model="email" :label="$t('user')" autofocus />
<QInput
v-model="password"
ref="password"
:label="$t('password')"
:type="showPwd ? 'password' : 'text'"
>
<template v-slot:append>
<QIcon
:name="showPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer"
@click="showPwd = !showPwd"
/>
</template>
</QInput>
<QCheckbox
v-model="remember"
:label="$t('remindMe')"
class="remember"
dense
/>
</div>
<div class="justify-center">
<QBtn
type="submit"
:label="$t('logIn')"
class="full-width"
color="primary"
rounded
no-caps
unelevated
/>
</div>
<div class="justify-center">
<QBtn
to="/"
:label="$t('logInAsGuest')"
class="full-width"
color="primary"
rounded
no-caps
outline
/>
</div>
<p class="password-forgotten text-center q-mt-lg">
<router-link to="/remember-password" class="link">
{{ $t('haveForgottenPassword') }}
</router-link>
</p>
</QForm>
<div class="footer text-center">
<p>
{{ $t('notACustomerYet') }}
<a href="//verdnatura.es/register/" target="_blank" class="link">
{{ $t('signUp') }}
</a>
</p>
<p class="contact">{{ $t('loginPhone') }} · {{ $t('loginMail') }}</p>
</div>
</div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -74,106 +80,106 @@ $login-margin-top: 50px;
$login-margin-between: 55px; $login-margin-between: 55px;
.main { .main {
max-width: 280px; max-width: 280px;
} }
a { a {
color: inherit; color: inherit;
} }
.header { .header {
margin-top: $login-margin-top; margin-top: $login-margin-top;
margin-bottom: $login-margin-between; margin-bottom: $login-margin-between;
img { img {
display: block; display: block;
margin: 0 auto; margin: 0 auto;
width: 90%; width: 90%;
} }
} }
.remember { .remember {
margin-top: 20px; margin-top: 20px;
margin-bottom: 40px; margin-bottom: 40px;
} }
.q-btn { .q-btn {
height: 50px; height: 50px;
} }
.password-forgotten { .password-forgotten {
font-size: 0.8rem; font-size: 0.8rem;
} }
.footer { .footer {
margin-bottom: $login-margin-top; margin-bottom: $login-margin-top;
margin-top: $login-margin-between; margin-top: $login-margin-between;
text-align: center; text-align: center;
font-size: 0.8rem; font-size: 0.8rem;
.contact { .contact {
margin-top: 15px; margin-top: 15px;
color: grey; color: grey;
} }
a { a {
font-weight: bold; font-weight: bold;
} }
} }
</style> </style>
<script> <script>
import { userStore } from 'stores/user' import { userStore } from 'stores/user';
export default { export default {
name: 'VnLogin', name: 'VnLogin',
data () { data() {
return { return {
user: userStore(), user: userStore(),
email: '', email: '',
password: '', password: '',
remember: false, remember: false,
showPwd: true showPwd: true
} };
}, },
mounted () { mounted() {
if (this.$route.query.emailConfirmed !== undefined) { if (this.$route.query.emailConfirmed !== undefined) {
this.$q.notify({ this.$q.notify({
message: this.$t('emailConfirmedSuccessfully'), message: this.$t('emailConfirmedSuccessfully'),
type: 'positive' type: 'positive'
}) });
} }
if (this.$route.params.email) { if (this.$route.params.email) {
this.email = this.$route.params.email this.email = this.$route.params.email;
this.$refs.password.focus() this.$refs.password.focus();
} }
}, },
methods: { methods: {
async onLogin () { async onLogin() {
await this.user.login(this.email, this.password, this.remember) await this.user.login(this.email, this.password, this.remember);
this.$router.push('/') this.$router.push('/');
} }
} }
} };
</script> </script>
<i18n lang="yaml"> <i18n lang="yaml">
en-US: en-US:
user: User user: User
password: Password password: Password
remindMe: Remind me remindMe: Remind me
logInAsGuest: Log in as guest logInAsGuest: Log in as guest
logIn: Log in logIn: Log in
loginMail: infoverdnatura.es loginMail: infoverdnatura.es
loginPhone: +34 607 562 391 loginPhone: +34 607 562 391
haveForgottenPassword: Have you forgotten your password? haveForgottenPassword: Have you forgotten your password?
notACustomerYet: Not a customer yet? notACustomerYet: Not a customer yet?
signUp: Register signUp: Register
es-ES: es-ES:
user: Usuario user: Usuario
password: Contraseña password: Contraseña
remindMe: Recuérdame remindMe: Recuérdame
logInAsGuest: Entrar como invitado logInAsGuest: Entrar como invitado
logIn: Iniciar sesión logIn: Iniciar sesión
loginMail: infoverdnatura.es loginMail: infoverdnatura.es
loginPhone: +34 963 242 100 loginPhone: +34 963 242 100
haveForgottenPassword: ¿Has olvidado tu contraseña? haveForgottenPassword: ¿Has olvidado tu contraseña?
notACustomerYet: ¿Todavía no eres cliente? notACustomerYet: ¿Todavía no eres cliente?
signUp: Registrarme signUp: Registrarme
</i18n> </i18n>

View File

@ -1,108 +1,108 @@
<template> <template>
<div class="text-center"> <div class="text-center">
<div> <div>
<QIcon <QIcon
name="contact_support" name="contact_support"
class="block q-mx-auto text-accent" class="block q-mx-auto text-accent"
style="font-size: 120px" style="font-size: 120px"
/> />
</div>
<div>
<QForm @submit="onSend" class="q-gutter-y-md text-grey-8">
<div class="text-h5">
<div>
{{ $t('dontWorry') }}
</div>
<div>
{{ $t('fillData') }}
</div>
</div>
<QInput
v-model="email"
:label="$t('user')"
:rules="[(val) => !!val || $t('inputEmail')]"
autofocus
/>
<div class="q-mt-lg">
{{ $t('weSendEmail') }}
</div> </div>
<div> <div>
<QBtn <QForm @submit="onSend" class="q-gutter-y-md text-grey-8">
type="submit" <div class="text-h5">
:label="$t('send')" <div>
class="full-width q-mt-md" {{ $t('dontWorry') }}
color="primary" </div>
rounded <div>
no-caps {{ $t('fillData') }}
unelevated </div>
/> </div>
<div class="text-center q-mt-md"> <QInput
<router-link to="/login" class="link"> v-model="email"
{{ $t('return') }} :label="$t('user')"
</router-link> :rules="[val => !!val || $t('inputEmail')]"
</div> autofocus
/>
<div class="q-mt-lg">
{{ $t('weSendEmail') }}
</div>
<div>
<QBtn
type="submit"
:label="$t('send')"
class="full-width q-mt-md"
color="primary"
rounded
no-caps
unelevated
/>
<div class="text-center q-mt-md">
<router-link to="/login" class="link">
{{ $t('return') }}
</router-link>
</div>
</div>
</QForm>
</div> </div>
</QForm>
</div> </div>
</div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
#image { #image {
height: 190px; height: 190px;
} }
.q-btn { .q-btn {
height: 50px; height: 50px;
} }
a { a {
color: inherit; color: inherit;
font-size: 0.8rem; font-size: 0.8rem;
} }
</style> </style>
<script> <script>
export default { export default {
name: 'VnRememberPasword', name: 'VnRememberPasword',
data () { data() {
return { return {
email: '' email: ''
} };
}, },
methods: { methods: {
async onSend () { async onSend() {
const params = { const params = {
email: this.email email: this.email
} };
await this.$axios.post('Users/reset', params) await this.$axios.post('Users/reset', params);
this.$q.notify({ this.$q.notify({
message: this.$t('weHaveSentEmailToRecover'), message: this.$t('weHaveSentEmailToRecover'),
type: 'positive' type: 'positive'
}) });
this.$router.push('/login') this.$router.push('/login');
} }
} }
} };
</script> </script>
<i18n lang="yaml"> <i18n lang="yaml">
en-US: en-US:
user: User user: User
inputEmail: Input email inputEmail: Input email
rememberPassword: Rememeber password rememberPassword: Rememeber password
dontWorry: Don't worry! dontWorry: Don't worry!
fillData: Fill the data fillData: Fill the data
weSendEmail: We will sent you an email to recover your password weSendEmail: We will sent you an email to recover your password
weHaveSentEmailToRecover: We've sent you an email where you can recover your password weHaveSentEmailToRecover: We've sent you an email where you can recover your password
send: Send send: Send
return: Return return: Return
es-ES: es-ES:
user: Usuario user: Usuario
inputEmail: Introduce el correo electrónico inputEmail: Introduce el correo electrónico
rememberPassword: Recordar contraseña rememberPassword: Recordar contraseña
dontWorry: ¡No te preocupes! dontWorry: ¡No te preocupes!
fillData: Rellena los datos fillData: Rellena los datos
weSendEmail: Te enviaremos un correo para restablecer tu contraseña weSendEmail: Te enviaremos un correo para restablecer tu contraseña
weHaveSentEmailToRecover: Te hemos enviado un correo donde podrás recuperar tu contraseña weHaveSentEmailToRecover: Te hemos enviado un correo donde podrás recuperar tu contraseña
send: Enviar send: Enviar
return: Volver return: Volver
</i18n> </i18n>

View File

@ -1,99 +1,106 @@
<template> <template>
<div> <div>
<QCard-section> <QCard-section>
<QIcon <QIcon
name="check" name="check"
class="block q-mx-auto text-accent" class="block q-mx-auto text-accent"
style="font-size: 120px" style="font-size: 120px"
/> />
</QCard-section> </QCard-section>
<QCard-section> <QCard-section>
<QForm @submit="onRegister" ref="form" class="q-gutter-y-md"> <QForm @submit="onRegister" ref="form" class="q-gutter-y-md">
<div class="text-grey-8 text-h5 text-center"> <div class="text-grey-8 text-h5 text-center">
{{ $t('fillData') }} {{ $t('fillData') }}
</div> </div>
<div class="q-gutter-y-sm"> <div class="q-gutter-y-sm">
<QInput <QInput
v-model="password" v-model="password"
:label="$t('password')" :label="$t('password')"
:type="showPwd ? 'password' : 'text'" :type="showPwd ? 'password' : 'text'"
autofocus autofocus
hint="" hint=""
filled filled
> >
<template v-slot:append> <template v-slot:append>
<QIcon <QIcon
:name="showPwd ? 'visibility_off' : 'visibility'" :name="
class="cursor-pointer" showPwd ? 'visibility_off' : 'visibility'
@click="showPwd = !showPwd" "
/> class="cursor-pointer"
</template> @click="showPwd = !showPwd"
</QInput> />
<QInput </template>
v-model="repeatPassword" </QInput>
:label="$t('repeatPassword')" <QInput
:type="showRpPwd ? 'password' : 'text'" v-model="repeatPassword"
:rules="[(value) => value == password || $t('repeatPasswordError')]" :label="$t('repeatPassword')"
hint="" :type="showRpPwd ? 'password' : 'text'"
filled :rules="[
> value =>
<template v-slot:append> value == password || $t('repeatPasswordError')
<QIcon ]"
:name="showRpPwd ? 'visibility_off' : 'visibility'" hint=""
class="cursor-pointer" filled
@click="showRpPwd = !showRpPwd" >
/> <template v-slot:append>
</template> <QIcon
</QInput> :name="
</div> showRpPwd ? 'visibility_off' : 'visibility'
<div> "
<QBtn class="cursor-pointer"
type="submit" @click="showRpPwd = !showRpPwd"
:label="$t('resetPassword')" />
class="full-width" </template>
color="primary" </QInput>
/> </div>
<div class="text-center q-mt-xs"> <div>
<router-link to="/login" class="link"> <QBtn
{{ $t('return') }} type="submit"
</router-link> :label="$t('resetPassword')"
</div> class="full-width"
</div> color="primary"
</QForm> />
</QCard-section> <div class="text-center q-mt-xs">
</div> <router-link to="/login" class="link">
{{ $t('return') }}
</router-link>
</div>
</div>
</QForm>
</QCard-section>
</div>
</template> </template>
<script> <script>
export default { export default {
name: 'VnRegister', name: 'VnRegister',
data () { data() {
return { return {
password: '', password: '',
repeatPassword: '', repeatPassword: '',
showPwd: true, showPwd: true,
showRpPwd: true showRpPwd: true
} };
}, },
methods: { methods: {
async onRegister () { async onRegister() {
const headers = { const headers = {
Authorization: this.$route.query.access_token Authorization: this.$route.query.access_token
} };
await this.$axios.post( await this.$axios.post(
'users/reset-password', 'users/reset-password',
{ {
newPassword: this.password newPassword: this.password
}, },
{ headers } { headers }
) );
this.$q.notify({ this.$q.notify({
message: this.$t('passwordResetSuccessfully'), message: this.$t('passwordResetSuccessfully'),
type: 'positive' type: 'positive'
}) });
this.$router.push('/login') this.$router.push('/login');
} }
} }
} };
</script> </script>

View File

@ -1,10 +1,11 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia';
import { api, jApi } from 'boot/axios' import { api, jApi } from 'boot/axios';
export const userStore = defineStore('user', { export const userStore = defineStore('user', {
state: () => { state: () => {
const token = const token =
sessionStorage.getItem('vnToken') || localStorage.getItem('vnToken') sessionStorage.getItem('vnToken') ||
localStorage.getItem('vnToken');
return { return {
token, token,
@ -12,50 +13,50 @@ export const userStore = defineStore('user', {
name: null, name: null,
nickname: null, nickname: null,
isGuest: false isGuest: false
} };
}, },
getters: { getters: {
loggedIn: (state) => state.token != null loggedIn: state => state.token != null
}, },
actions: { actions: {
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);
if (remember) { if (remember) {
localStorage.setItem('vnToken', res.data.token) localStorage.setItem('vnToken', res.data.token);
} else { } else {
sessionStorage.setItem('vnToken', res.data.token) sessionStorage.setItem('vnToken', res.data.token);
} }
this.$patch({ this.$patch({
token: res.data.token, token: res.data.token,
name: user name: user
}) });
}, },
async logout () { async logout() {
if (this.token != null) { if (this.token != null) {
try { try {
await api.post('Accounts/logout') await api.post('Accounts/logout');
} catch (e) {} } catch (e) {}
localStorage.removeItem('vnToken') localStorage.removeItem('vnToken');
sessionStorage.removeItem('vnToken') sessionStorage.removeItem('vnToken');
} }
this.$reset() this.$reset();
}, },
async loadData () { async loadData() {
const userData = await jApi.getObject( const userData = await jApi.getObject(
'SELECT id, nickname FROM account.myUser' 'SELECT id, nickname FROM account.myUser'
) );
this.$patch({ this.$patch({
id: userData.id, id: userData.id,
nickname: userData.nickname nickname: userData.nickname
}) });
} }
} }
}) });