Mejoras sección pedidos #79

Merged
jsegarra merged 7 commits from wbuezas/hedera-web-mindshore:feature/MejorasPedidos into 4922-vueMigration 2024-08-23 12:05:15 +00:00
29 changed files with 606 additions and 255 deletions

View File

@ -22,8 +22,8 @@ module.exports = {
// Uncomment any of the lines below to choose desired strictness, // Uncomment any of the lines below to choose desired strictness,
// but leave only one uncommented! // but leave only one uncommented!
// See https://eslint.vuejs.org/rules/#available-rules // See https://eslint.vuejs.org/rules/#available-rules
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention) // 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability) 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead) // 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
'standard' 'standard'
@ -66,7 +66,18 @@ module.exports = {
'prefer-promise-reject-errors': 'off', 'prefer-promise-reject-errors': 'off',
semi: 'off', semi: 'off',
// 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',
'vue/html-indent': [
'error',
4,
{
attribute: 1,
baseIndent: 1,
closeBracket: 0,
alignAttributesVertically: true,
ignores: []
}
]
}, },
overrides: [ overrides: [
{ {

View File

@ -3,8 +3,11 @@
"eslint.autoFixOnSave": true, "eslint.autoFixOnSave": true,
"editor.bracketPairColorization.enabled": true, "editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": true, "editor.guides.bracketPairs": true,
"editor.formatOnSave": true, "editor.formatOnSave": false,
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": null,
"editor.codeActionsOnSave": ["source.fixAll.eslint"], "editor.codeActionsOnSave": ["source.fixAll.eslint"],
"eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"] "eslint.validate": ["javascript", "javascriptreact", "typescript", "vue"],
"[sql]": {
"editor.formatOnSave": true
}
} }

View File

@ -15,6 +15,12 @@ const { configure } = require('quasar/wrappers');
module.exports = configure(function (ctx) { module.exports = configure(function (ctx) {
return { return {
// fix: true,
// include = [],
// exclude = [],
// rawOptions = {},
warnings: true,
errors: true,
// https://v2.quasar.dev/quasar-cli-webpack/supporting-ts // https://v2.quasar.dev/quasar-cli-webpack/supporting-ts
supportTS: false, supportTS: false,
@ -102,7 +108,7 @@ module.exports = configure(function (ctx) {
proxy: { proxy: {
'/api': 'http://localhost:3000', '/api': 'http://localhost:3000',
'/': { '/': {
target: 'http://localhost:3002', target: 'http://localhost:3001',
bypass: req => (req.path !== '/' ? req.path : null) bypass: req => (req.path !== '/' ? req.path : null)
} }
} }

View File

@ -21,7 +21,7 @@ const onRequestError = error => {
}; };
const onResponseError = error => { const onResponseError = error => {
let message = ''; let message = error.message;
const response = error.response; const response = error.response;
const responseData = response && response.data; const responseData = response && response.data;
@ -47,6 +47,7 @@ export default boot(({ app }) => {
api.interceptors.response.use(response => response, onResponseError); api.interceptors.response.use(response => response, onResponseError);
jApi.use(addToken); jApi.use(addToken);
jApi.useErrorInterceptor(onResponseError);
// for use inside Vue files (Options API) through this.$axios and this.$api // for use inside Vue files (Options API) through this.$axios and this.$api

View File

@ -179,7 +179,10 @@ defineExpose({
</script> </script>
<template> <template>
<QCard class="form-container" v-bind="$attrs"> <QCard
class="form-container"
v-bind="$attrs"
>
<QForm <QForm
v-if="!loading" v-if="!loading"
ref="addressFormRef" ref="addressFormRef"
@ -188,7 +191,14 @@ defineExpose({
<span class="text-h6 text-bold"> <span class="text-h6 text-bold">
{{ title }} {{ title }}
</span> </span>
<slot name="form" :data="formData" /> <slot
name="form"
:data="formData"
/>
<slot
name="extraForm"
:data="formData"
/>
<component <component
:is="showBottomActions ? 'div' : Teleport" :is="showBottomActions ? 'div' : Teleport"
:to="$actions" :to="$actions"
@ -217,11 +227,20 @@ defineExpose({
<slot name="actions" /> <slot name="actions" />
</component> </component>
</QForm> </QForm>
<QSpinner v-else color="primary" size="3em" :thickness="2" /> <QSpinner
v-else
color="primary"
size="3em"
:thickness="2"
/>
</QCard> </QCard>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.no-form-container {
padding: 0 !important;
box-shadow: none;
border: none;
}
.form-container { .form-container {
width: 100%; width: 100%;
height: max-content; height: max-content;

View File

@ -56,10 +56,13 @@ defineExpose({
const inputRules = [ const inputRules = [
val => { val => {
const { min } = vnInputRef.value.$attrs; const { min, max } = vnInputRef.value.$attrs;
if (min >= 0) { if (min >= 0) {
if (Math.floor(val) < min) return t('inputMin', { value: min }); if (Math.floor(val) < min) return t('inputMin', { value: min });
} }
if (max > 0) {
if (Math.floor(val) > max) return t('inputMax', { value: max });
}
} }
]; ];
</script> </script>
@ -82,11 +85,17 @@ const inputRules = [
hide-bottom-space hide-bottom-space
@keyup.enter="emit('keyup.enter')" @keyup.enter="emit('keyup.enter')"
> >
<template v-if="$slots.prepend" #prepend> <template
v-if="$slots.prepend"
#prepend
>
<slot name="prepend" /> <slot name="prepend" />
</template> </template>
<template #append> <template #append>
<slot v-if="$slots.append && !$attrs.disabled" name="append" /> <slot
v-if="$slots.append && !$attrs.disabled"
name="append"
/>
<QIcon <QIcon
v-if="hover && value && !$attrs.disabled && props.clearable" v-if="hover && value && !$attrs.disabled && props.clearable"
name="close" name="close"
@ -98,7 +107,10 @@ const inputRules = [
} }
" "
/> />
<QIcon v-if="info" name="info"> <QIcon
v-if="info"
name="info"
>
<QTooltip max-width="350px"> <QTooltip max-width="350px">
{{ info }} {{ info }}
</QTooltip> </QTooltip>
@ -111,12 +123,17 @@ const inputRules = [
<i18n lang="yaml"> <i18n lang="yaml">
en-US: en-US:
inputMin: Must be more than {value} inputMin: Must be more than {value}
inputMax: Must be less than {value}
es-ES: es-ES:
inputMin: Must be more than {value} inputMin: Debe ser mayor a {value}
inputMax: Debe ser menor a {value}
ca-ES: ca-ES:
inputMin: Ha de ser més gran que {value} inputMin: Ha de ser més gran que {value}
inputMax: Ha de ser menys que {value}
fr-FR: fr-FR:
inputMin: Doit être supérieur à {value} inputMin: Doit être supérieur à {value}
inputMax: Doit être supérieur à {value}
pt-PT: pt-PT:
inputMin: Deve ser maior que {value} inputMin: Deve ser maior que {value}
inputMax: Deve ser maior que {value}
</i18n> </i18n>

View File

@ -162,7 +162,10 @@ async function filterHandler(val, update) {
:rules="$attrs.required ? [requiredFieldRule] : null" :rules="$attrs.required ? [requiredFieldRule] : null"
virtual-scroll-slice-size="options.length" virtual-scroll-slice-size="options.length"
> >
<template v-if="isClearable" #append> <template
v-if="isClearable"
#append
>
<QIcon <QIcon
v-show="value" v-show="value"
name="close" name="close"
@ -176,7 +179,11 @@ async function filterHandler(val, update) {
#[slotName]="slotData" #[slotName]="slotData"
:key="slotName" :key="slotName"
> >
<slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" /> <slot
:name="slotName"
v-bind="slotData ?? {}"
:key="slotName"
/>
</template> </template>
</QSelect> </QSelect>
</template> </template>

View File

@ -30,7 +30,10 @@ const handleClick = () => {
</div> </div>
</div> </div>
</QItemSection> </QItemSection>
<QItemSection class="no-padding" side> <QItemSection
class="no-padding"
side
>
<slot name="actions" /> <slot name="actions" />
</QItemSection> </QItemSection>
</QItem> </QItem>

View File

@ -98,12 +98,12 @@ onMounted(async () => {
<VnForm <VnForm
ref="vnFormRef" ref="vnFormRef"
:title="t('changePassword')" :title="t('changePassword')"
:formInitialData="formData" :form-initial-data="formData"
:saveFn="changePassword" :save-fn="changePassword"
showBottomActions show-bottom-actions
:defaultActions="false" :default-actions="false"
style="max-width: 300px" style="max-width: 300px"
@onDataSaved="onPasswordChanged()" @on-data-saved="onPasswordChanged()"
> >
<template #form> <template #form>
<VnInput <VnInput
@ -112,38 +112,42 @@ onMounted(async () => {
v-model="formData.oldPassword" v-model="formData.oldPassword"
:type="!showOldPwd ? 'password' : 'text'" :type="!showOldPwd ? 'password' : 'text'"
:label="t('oldPassword')" :label="t('oldPassword')"
> >
<template #append> <template #append>
<QIcon <QIcon
:name="showOldPwd ? 'visibility_off' : 'visibility'" :name="showOldPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer" class="cursor-pointer"
@click="showOldPwd = !showOldPwd" @click="showOldPwd = !showOldPwd"
/> />
</template> </template>
</VnInput> </VnInput>
<VnInput <VnInput
ref="newPasswordRef" ref="newPasswordRef"
v-model="formData.newPassword" v-model="formData.newPassword"
:type="!showNewPwd ? 'password' : 'text'" :type="!showNewPwd ? 'password' : 'text'"
:label="t('newPassword')" :label="t('newPassword')"
><template #append> >
<QIcon <template #append>
:name="showNewPwd ? 'visibility_off' : 'visibility'" <QIcon
class="cursor-pointer" :name="showNewPwd ? 'visibility_off' : 'visibility'"
@click="showNewPwd = !showNewPwd" class="cursor-pointer"
/> @click="showNewPwd = !showNewPwd"
</template></VnInput> />
</template>
</VnInput>
<VnInput <VnInput
v-model="repeatPassword" v-model="repeatPassword"
:type="!showCopyPwd ? 'password' : 'text'" :type="!showCopyPwd ? 'password' : 'text'"
:label="t('repeatPassword')" :label="t('repeatPassword')"
><template #append> >
<QIcon <template #append>
:name="showCopyPwd ? 'visibility_off' : 'visibility'" <QIcon
class="cursor-pointer" :name="showCopyPwd ? 'visibility_off' : 'visibility'"
@click="showCopyPwd = !showCopyPwd" class="cursor-pointer"
/> @click="showCopyPwd = !showCopyPwd"
</template></VnInput> />
</template>
</VnInput>
</template> </template>
<template #actions> <template #actions>
<QBtn <QBtn
@ -153,7 +157,13 @@ onMounted(async () => {
flat flat
@click="passwordRequirementsDialogRef.show()" @click="passwordRequirementsDialogRef.show()"
/> />
<QBtn :label="t('modify')" type="submit" rounded no-caps flat /> <QBtn
:label="t('modify')"
type="submit"
rounded
no-caps
flat
/>
</template> </template>
</VnForm> </VnForm>
<QDialog ref="passwordRequirementsDialogRef"> <QDialog ref="passwordRequirementsDialogRef">
@ -161,7 +171,10 @@ onMounted(async () => {
<span class="text-h6 text-bold q-mb-md"> <span class="text-h6 text-bold q-mb-md">
{{ t('passwordRequirements') }} {{ t('passwordRequirements') }}
</span> </span>
<div class="column" style="max-width: max-content"> <div
class="column"
style="max-width: max-content"
>
<span> <span>
{{ {{
t('charactersLong', { t('charactersLong', {

View File

@ -73,8 +73,8 @@ async function confirm() {
/> />
</QCardSection> </QCardSection>
<QCardSection class="row items-center"> <QCardSection class="row items-center">
<span v-html="message"></span> <span v-html="message" />
<slot name="customHTML"></slot> <slot name="customHTML" />
</QCardSection> </QCardSection>
<QCardActions align="right"> <QCardActions align="right">
<QBtn <QBtn

View File

@ -43,7 +43,10 @@ const url = computed(() => {
@click="show = !show" @click="show = !show"
spinner-color="primary" spinner-color="primary"
/> />
<QDialog v-model="show" v-if="props.zoomSize"> <QDialog
v-model="show"
v-if="props.zoomSize"
>
<QImg <QImg
:src="url" :src="url"
size="full" size="full"

View File

@ -27,8 +27,14 @@ const props = defineProps({
:rows-per-page-options="props.rowsPerPageOptions" :rows-per-page-options="props.rowsPerPageOptions"
table-header-class="vntable-header-default" table-header-class="vntable-header-default"
> >
<template v-for="(_, slotName) in $slots" v-slot:[slotName]="slotProps"> <template
<slot :name="slotName" v-bind="slotProps" /> v-for="(_, slotName) in $slots"
#[slotName]="slotProps"
>
<slot
:name="slotName"
v-bind="slotProps"
/>
</template> </template>
</QTable> </QTable>
</template> </template>

View File

@ -42,3 +42,12 @@ a.link {
.no-padding { .no-padding {
padding: 0 !important; padding: 0 !important;
} }
input[type='number'] {
-moz-appearance: textfield;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}

View File

@ -1,70 +1,75 @@
import { VnObject } from './object' import { VnObject } from './object';
import { JsonException } from './json-exception' import { JsonException } from './json-exception';
/** /**
* Handler for JSON rest connections. * Handler for JSON rest connections.
*/ */
export class JsonConnection extends VnObject { export class JsonConnection extends VnObject {
_connected = false _connected = false;
_requestsCount = 0 _requestsCount = 0;
token = null token = null;
interceptors = [] interceptors = [];
errorInterceptor = null;
use (fn) { use(fn) {
this.interceptors.push(fn) this.interceptors.push(fn);
}
useErrorInterceptor(fn) {
this.errorInterceptor = fn;
} }
/** /**
* Executes the specified REST service with the given params and calls * Executes the specified REST service with the given params and calls
* the callback when response is received. * the callback when response is received.
* *
* @param {String} url The service path * @param {String} url The service path
* @param {Object} params The params to pass to the service * @param {Object} params The params to pass to the service
* @return {Object} The parsed JSON response * @return {Object} The parsed JSON response
*/ */
async send (url, params) { async send(url, params) {
if (!params) params = {} if (!params) params = {};
params.srv = `json:${url}` params.srv = `json:${url}`;
return this.sendWithUrl('POST', '.', params) return this.sendWithUrl('POST', '.', params);
} }
async sendForm (form) { async sendForm(form) {
const params = {} const params = {};
const elements = form.elements const elements = form.elements;
for (let i = 0; i < elements.length; i++) { for (let i = 0; i < elements.length; i++) {
if (elements[i].name) { if (elements[i].name) {
params[elements[i].name] = elements[i].value params[elements[i].name] = elements[i].value;
} }
} }
return this.sendWithUrl('POST', form.action, params) return this.sendWithUrl('POST', form.action, params);
} }
async sendFormMultipart (form) { async sendFormMultipart(form) {
return this.request({ return this.request({
method: 'POST', method: 'POST',
url: form.action, url: form.action,
data: new FormData(form) data: new FormData(form)
}) });
} }
async sendFormData (formData) { async sendFormData(formData) {
return this.request({ return this.request({
method: 'POST', method: 'POST',
url: '', url: '',
data: formData data: formData
}) });
} }
/* /*
* Called when REST response is received. * Called when REST response is received.
*/ */
async sendWithUrl (method, url, params) { async sendWithUrl(method, url, params) {
const urlParams = new URLSearchParams() const urlParams = new URLSearchParams();
for (const key in params) { for (const key in params) {
if (params[key] != null) { if (params[key] != null) {
urlParams.set(key, params[key]) urlParams.set(key, params[key]);
} }
} }
@ -75,126 +80,129 @@ export class JsonConnection extends VnObject {
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded' 'Content-Type': 'application/x-www-form-urlencoded'
} }
}) });
} }
async request (config) { async request(config) {
const request = new XMLHttpRequest() const request = new XMLHttpRequest();
request.open(config.method, config.url, true) request.open(config.method, config.url, true);
for (const fn of this.interceptors) { for (const fn of this.interceptors) {
config = fn(config) config = fn(config);
} }
const headers = config.headers const headers = config.headers;
if (headers) { if (headers) {
for (const header in headers) { for (const header in headers) {
request.setRequestHeader(header, headers[header]) request.setRequestHeader(header, headers[header]);
} }
} }
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
request.onreadystatechange = () => request.onreadystatechange = () =>
this._onStateChange(request, resolve, reject) this._onStateChange(request, resolve, reject);
}) });
request.send(config.data) request.send(config.data);
this._requestsCount++ this._requestsCount++;
if (this._requestsCount === 1) { if (this._requestsCount === 1) {
this.emit('loading-changed', true) this.emit('loading-changed', true);
} }
return promise return promise;
} }
_onStateChange (request, resolve, reject) { _onStateChange(request, resolve, reject) {
if (request.readyState !== 4) { if (request.readyState !== 4) {
return return;
} }
this._requestsCount-- this._requestsCount--;
if (this._requestsCount === 0) { if (this._requestsCount === 0) {
this.emit('loading-changed', false) this.emit('loading-changed', false);
} }
let data = null let data = null;
let error = null let error = null;
try { try {
if (request.status === 0) { if (request.status === 0) {
const err = new JsonException() const err = new JsonException();
err.message = err.message =
'The server does not respond, please check your Internet connection' 'The server does not respond, please check your Internet connection';
err.statusCode = request.status err.statusCode = request.status;
throw err throw err;
} }
let contentType = null let contentType = null;
try { try {
contentType = request contentType = request
.getResponseHeader('Content-Type') .getResponseHeader('Content-Type')
.split(';')[0] .split(';')[0]
.trim() .trim();
} catch (err) { } catch (err) {
console.warn(err) console.warn(err);
} }
if (contentType !== 'application/json') { if (contentType !== 'application/json') {
const err = new JsonException() const err = new JsonException();
err.message = request.statusText err.message = request.statusText;
err.statusCode = request.status err.statusCode = request.status;
throw err throw err;
} }
let json let json;
let jsData let jsData;
if (request.responseText) { if (request.responseText) {
json = JSON.parse(request.responseText) json = JSON.parse(request.responseText);
} }
if (json) { if (json) {
jsData = json.data || json jsData = json.data || json;
} }
if (request.status >= 200 && request.status < 300) { if (request.status >= 200 && request.status < 300) {
data = jsData data = jsData;
} else { } else {
let exception = jsData.exception let exception = jsData.exception;
const err = new JsonException() const err = new JsonException();
err.statusCode = request.status err.statusCode = request.status;
if (exception) { if (exception) {
exception = exception exception = exception
.replace(/\\/g, '.') .replace(/\\/g, '.')
.replace(/Exception$/, '') .replace(/Exception$/, '')
.replace(/^Vn\.Web\./, '') .replace(/^Vn\.Web\./, '');
err.exception = exception err.exception = exception;
err.message = jsData.message err.message = jsData.message;
err.code = jsData.code err.code = jsData.code;
err.file = jsData.file err.file = jsData.file;
err.line = jsData.line err.line = jsData.line;
err.trace = jsData.trace err.trace = jsData.trace;
} else { } else {
err.message = request.statusText err.message = request.statusText;
} }
throw err throw err;
} }
} catch (e) { } catch (e) {
data = null data = null;
error = e error = e;
} }
if (error) { if (error) {
this.emit('error', error) if (this.errorInterceptor) {
reject(error) this.errorInterceptor(error);
}
this.emit('error', error);
reject(error);
} else { } else {
resolve(data) resolve(data);
} }
} }
} }

View File

@ -12,11 +12,17 @@
/> />
<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"
/>
<QBtn <QBtn
v-if="$app.useRightDrawer" v-if="$app.useRightDrawer"
@click="$app.rightDrawerOpen = !$app.rightDrawerOpen" @click="$app.rightDrawerOpen = !$app.rightDrawerOpen"
@ -29,22 +35,44 @@
</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
<QItem v-if="!item.childs" :to="`/${item.path}`"> v-for="item in essentialLinks"
:key="item.id"
>
<QItem
v-if="!item.childs"
:to="`/${item.path}`"
>
<QItemSection> <QItemSection>
<QItemLabel>{{ item.description }}</QItemLabel> <QItemLabel>{{ item.description }}</QItemLabel>
</QItemSection> </QItemSection>

View File

@ -13,6 +13,7 @@ const { t } = useI18n();
const jApi = inject('jApi'); const jApi = inject('jApi');
const vnFormRef = ref(null); const vnFormRef = ref(null);
const vnFormRef2 = ref(null);
const changePasswordFormDialog = ref(null); const changePasswordFormDialog = ref(null);
const showChangePasswordForm = ref(false); const showChangePasswordForm = ref(false);
const langOptions = ref([]); const langOptions = ref([]);
@ -63,11 +64,11 @@ onMounted(() => fetchLanguagesSql());
<VnForm <VnForm
ref="vnFormRef" ref="vnFormRef"
:title="t('personalInformation')" :title="t('personalInformation')"
:fetchFormDataSql="fetchConfigDataSql" :fetch-form-data-sql="fetchConfigDataSql"
:pks="pks" :pks="pks"
table="myUser" table="myUser"
schema="account" schema="account"
:defaultActions="false" :default-actions="false"
> >
<template #form="{ data }"> <template #form="{ data }">
<VnInput <VnInput
@ -94,9 +95,29 @@ onMounted(() => fetchLanguagesSql());
option-label="name" option-label="name"
option-value="code" option-value="code"
:options="langOptions" :options="langOptions"
@update:modelValue="vnFormRef.submit()" @update:model-value="vnFormRef.submit()"
/> />
</template> </template>
<template #extraForm>
<VnForm
class="no-form-container"
ref="vnFormRef2"
:pks="pks"
table="myClient"
schema="hedera"
:fetch-form-data-sql="fetchConfigDataSql"
:default-actions="false"
>
<template #form="{ data }">
<QCheckbox
v-model="data.isToBeMailed"
:label="t('isToBeMailed')"
@update:model-value="vnFormRef2.submit()"
dense
/>
</template>
</VnForm>
</template>
</VnForm> </VnForm>
</QPage> </QPage>
<QDialog <QDialog
@ -113,6 +134,7 @@ onMounted(() => fetchLanguagesSql());
<i18n lang="yaml"> <i18n lang="yaml">
en-US: en-US:
personalInformation: Personal Information personalInformation: Personal Information
isToBeMailed: Receive invoices by email
name: Name name: Name
email: Email email: Email
nickname: Display name nickname: Display name
@ -122,6 +144,7 @@ en-US:
changePassword: Change password changePassword: Change password
es-ES: es-ES:
personalInformation: Datos personales personalInformation: Datos personales
isToBeMailed: Recibir facturas por correo electrónico
name: Nombre name: Nombre
email: Correo electrónico email: Correo electrónico
nickname: Nombre a mostrar nickname: Nombre a mostrar
@ -131,6 +154,7 @@ es-ES:
changePassword: Cambiar contraseña changePassword: Cambiar contraseña
ca-ES: ca-ES:
personalInformation: Dades personals personalInformation: Dades personals
isToBeMailed: Rebre factures per correu electrònic
name: Nom name: Nom
email: Correu electrònic email: Correu electrònic
nickname: Nom a mostrar nickname: Nom a mostrar
@ -140,6 +164,7 @@ ca-ES:
changePassword: Canviar contrasenya changePassword: Canviar contrasenya
fr-FR: fr-FR:
personalInformation: Informations personnelles personalInformation: Informations personnelles
isToBeMailed: Recevoir des factures par e-mail
name: Nom name: Nom
email: E-mail email: E-mail
nickname: Nom à afficher nickname: Nom à afficher
@ -149,6 +174,7 @@ fr-FR:
changePassword: Changer le mot de passe changePassword: Changer le mot de passe
pt-PT: pt-PT:
personalInformation: Dados pessoais personalInformation: Dados pessoais
isToBeMailed: Receber facturas por e-mail
name: Nome name: Nome
email: E-mail email: E-mail
nickname: Nom à afficher nickname: Nom à afficher

View File

@ -67,29 +67,41 @@ onMounted(() => getCountries());
</Teleport> </Teleport>
<VnForm <VnForm
ref="vnFormRef" ref="vnFormRef"
:fetchFormDataSql="fetchAddressDataSql" :fetch-form-data-sql="fetchAddressDataSql"
:columnsToIgnoreUpdate="['countryFk']" :columns-to-ignore-update="['countryFk']"
:createModelDefault="{ :create-model-default="{
field: 'clientFk', field: 'clientFk',
value: 'account.myUser_getId()' value: 'account.myUser_getId()'
}" }"
:pks="pks" :pks="pks"
:isEditMode="isEditMode" :is-edit-mode="isEditMode"
:title="t('addEditAddress')" :title="t(isEditMode ? 'editAddress' : 'addAddress')"
table="myAddress" table="myAddress"
schema="hedera" schema="hedera"
@onDataSaved="goBack()" @on-data-saved="goBack()"
> >
<template #form="{ data }"> <template #form="{ data }">
<VnInput v-model="data.nickname" :label="t('name')" /> <VnInput
<VnInput v-model="data.street" :label="t('address')" /> v-model="data.nickname"
<VnInput v-model="data.city" :label="t('city')" /> :label="t('name')"
<VnInput v-model="data.postalCode" :label="t('postalCode')" /> />
<VnInput
v-model="data.street"
:label="t('address')"
/>
<VnInput
v-model="data.city"
:label="t('city')"
/>
<VnInput
v-model="data.postalCode"
:label="t('postalCode')"
/>
<VnSelect <VnSelect
v-model="data.countryFk" v-model="data.countryFk"
:label="t('country')" :label="t('country')"
:options="countriesOptions" :options="countriesOptions"
@update:modelValue="data.provinceFk = null" @update:model-value="data.provinceFk = null"
/> />
<VnSelect <VnSelect
v-model="data.provinceFk" v-model="data.provinceFk"
@ -114,7 +126,6 @@ onMounted(() => getCountries());
en-US: en-US:
back: Back back: Back
accept: Accept accept: Accept
addEditAddress: Add or edit address
name: Consignee name: Consignee
address: Address address: Address
city: City city: City
@ -122,21 +133,23 @@ en-US:
country: Country country: Country
province: Province province: Province
addressChangedSuccessfully: Address changed successfully addressChangedSuccessfully: Address changed successfully
addAddress: Add address
editAddress: Edit address
es-ES: es-ES:
back: Volver back: Volver
accept: Aceptar accept: Aceptar
addEditAddress: Añadir o modificar dirección
name: Consignatario name: Consignatario
address: Morada address: Dirección
city: Ciudad city: Ciudad
postalCode: Código postal postalCode: Código postal
country: País country: País
province: Distrito province: Provincia
addressChangedSuccessfully: Dirección modificada correctamente addressChangedSuccessfully: Dirección modificada correctamente
addAddress: Añadir dirección
editAddress: Modificar dirección
ca-ES: ca-ES:
back: Tornar back: Tornar
accept: Acceptar accept: Acceptar
addEditAddress: Afegir o modificar adreça
name: Consignatari name: Consignatari
address: Direcció address: Direcció
city: Ciutat city: Ciutat
@ -144,10 +157,11 @@ ca-ES:
country: País country: País
province: Província province: Província
addressChangedSuccessfully: Adreça modificada correctament addressChangedSuccessfully: Adreça modificada correctament
addAddress: Afegir adreça
editAddress: Modificar adreça
fr-FR: fr-FR:
back: Retour back: Retour
accept: Accepter accept: Accepter
addEditAddress: Ajouter ou modifier l'adresse
name: Destinataire name: Destinataire
address: Numéro Rue address: Numéro Rue
city: Ville city: Ville
@ -155,10 +169,11 @@ fr-FR:
country: Pays country: Pays
province: Province province: Province
addressChangedSuccessfully: Adresse modifié avec succès addressChangedSuccessfully: Adresse modifié avec succès
addAddress: Ajouter adresse
editAddress: Modifier adresse
pt-PT: pt-PT:
back: Voltar back: Voltar
accept: Aceitar accept: Aceitar
addEditAddress: Adicionar ou modificar morada
name: Consignatario name: Consignatario
address: Morada address: Morada
city: Concelho city: Concelho
@ -166,4 +181,6 @@ pt-PT:
country: País country: País
province: Distrito province: Distrito
addressChangedSuccessfully: Morada modificada corretamente addressChangedSuccessfully: Morada modificada corretamente
addAddress: Adicionar morada
editAddress: Modificar morada
</i18n> </i18n>

View File

@ -96,7 +96,10 @@ onMounted(async () => {
/> />
</Teleport> </Teleport>
<QPage class="vn-w-sm"> <QPage class="vn-w-sm">
<QList class="rounded-borders shadow-1 shadow-transition" separator> <QList
class="rounded-borders shadow-1 shadow-transition"
separator
>
<CardList <CardList
v-for="(address, index) in addresses" v-for="(address, index) in addresses"
:key="index" :key="index"
@ -133,13 +136,21 @@ onMounted(async () => {
() => removeAddress(address.id) () => removeAddress(address.id)
) )
" "
/> >
<QTooltip>
{{ t('deleteAddress') }}
</QTooltip>
</QBtn>
<QBtn <QBtn
icon="edit" icon="edit"
flat flat
rounded rounded
@click.stop="goToAddressDetails(address.id)" @click.stop="goToAddressDetails(address.id)"
/> >
<QTooltip>
{{ t('editAddress') }}
</QTooltip>
</QBtn>
</template> </template>
</CardList> </CardList>
</QList> </QList>
@ -151,20 +162,30 @@ en-US:
addAddress: Add address addAddress: Add address
defaultAddressModified: Default address modified defaultAddressModified: Default address modified
confirmDeleteAddress: Are you sure you want to delete the address? confirmDeleteAddress: Are you sure you want to delete the address?
editAddress: Edit address
deleteAddress: Delete address
es-ES: es-ES:
addAddress: Añadir dirección addAddress: Añadir dirección
defaultAddressModified: Dirección por defecto modificada defaultAddressModified: Dirección por defecto modificada
confirmDeleteAddress: ¿Estás seguro de que quieres borrar la dirección? confirmDeleteAddress: ¿Estás seguro de que quieres borrar la dirección?
editAddress: Modificar dirección
deleteAddress: Borrar dirección
ca-ES: ca-ES:
addAddress: Afegir adreça addAddress: Afegir adreça
defaultAddressModified: Adreça per defecte modificada defaultAddressModified: Adreça per defecte modificada
confirmDeleteAddress: Estàs segur que vols esborrar l'adreça? confirmDeleteAddress: Estàs segur que vols esborrar l'adreça?
editAddress: Modificar adreça
deleteAddress: Esborrar adreça
fr-FR: fr-FR:
addAddress: Ajouter une adresse addAddress: Ajouter une adresse
defaultAddressModified: Adresse par défaut modifiée defaultAddressModified: Adresse par défaut modifiée
confirmDeleteAddress: Êtes-vous sûr de vouloir supprimer l'adresse? confirmDeleteAddress: Êtes-vous sûr de vouloir supprimer l'adresse?
editAddress: Modifier adresse
deleteAddress: Supprimer adresse
pt-PT: pt-PT:
addAddress: Adicionar Morada addAddress: Adicionar Morada
defaultAddressModified: Endereço padrão modificado defaultAddressModified: Endereço padrão modificado
confirmDeleteAddress: Tem a certeza de que deseja excluir o endereço? confirmDeleteAddress: Tem a certeza de que deseja excluir o endereço?
editAddress: Modificar morada
deleteAddress: Eliminar morada
</i18n> </i18n>

View File

@ -20,15 +20,27 @@ onMounted(async () => await fetchData());
<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>
<VnImg :id="myNew.image" storage="news" /> <VnImg
:id="myNew.image"
storage="news"
/>
<QCardSection> <QCardSection>
<div class="text-h5">{{ myNew.title }}</div> <div class="text-h5">
{{ myNew.title }}
</div>
</QCardSection> </QCardSection>
<QCardSection class="new-body"> <QCardSection class="new-body">
<div v-html="myNew.text" class="card-text" /> <div
v-html="myNew.text"
class="card-text"
/>
</QCardSection> </QCardSection>
</QCard> </QCard>
</div> </div>
@ -43,7 +55,10 @@ onMounted(async () => await fetchData());
/> />
</QPageSticky> </QPageSticky>
</div> </div>
<QDialog v-model="showPreview" @hide="selectedImageSrc = ''"> <QDialog
v-model="showPreview"
@hide="selectedImageSrc = ''"
>
<QImg :src="selectedImageSrc" /> <QImg :src="selectedImageSrc" />
</QDialog> </QDialog>
</template> </template>

View File

@ -10,8 +10,11 @@
dense dense
standout standout
> >
<template v-slot:prepend> <template #prepend>
<QIcon v-if="search === ''" name="search" /> <QIcon
v-if="search === ''"
name="search"
/>
<QIcon <QIcon
v-else v-else
name="clear" name="clear"
@ -29,7 +32,11 @@
/> />
</Teleport> </Teleport>
<div style="padding-bottom: 5em"> <div style="padding-bottom: 5em">
<QDrawer v-model="$app.rightDrawerOpen" side="right" :width="250"> <QDrawer
v-model="$app.rightDrawerOpen"
side="right"
:width="250"
>
<div class="q-pa-md"> <div class="q-pa-md">
<div class="basket-info"> <div class="basket-info">
<p>{{ date(new Date()) }}</p> <p>{{ date(new Date()) }}</p>
@ -37,7 +44,11 @@
{{ $t('warehouse') }} {{ $t('warehouse') }}
{{ 'Algemesi' }} {{ 'Algemesi' }}
</p> </p>
<QBtn flat rounded no-caps> <QBtn
flat
rounded
no-caps
>
{{ $t('modify') }} {{ $t('modify') }}
</QBtn> </QBtn>
</div> </div>
@ -66,11 +77,14 @@
:title="cat.name" :title="cat.name"
:to="{ params: { category: cat.id, type: null } }" :to="{ params: { category: cat.id, type: null } }"
> >
<img :src="`statics/category/${cat.code}.svg`" /> <img :src="`statics/category/${cat.code}.svg`">
</QBtn> </QBtn>
</div> </div>
</div> </div>
<div class="q-mt-md" v-if="category || search"> <div
class="q-mt-md"
v-if="category || search"
>
<div class="q-mb-xs text-grey-7"> <div class="q-mb-xs text-grey-7">
{{ $t('filterBy') }} {{ $t('filterBy') }}
</div> </div>
@ -95,8 +109,15 @@
/> />
</div> </div>
</div> </div>
<div class="q-pa-md" v-if="typeId || search"> <div
<div class="q-mb-md" v-for="tag in tags" :key="tag.uid"> class="q-pa-md"
v-if="typeId || search"
>
<div
class="q-mb-md"
v-for="tag in tags"
:key="tag.uid"
>
<div class="q-mb-xs text-caption text-grey-7"> <div class="q-mb-xs text-caption text-grey-7">
{{ tag.name }} {{ tag.name }}
<QIcon <QIcon
@ -166,8 +187,11 @@
:disable="disableScroll" :disable="disableScroll"
> >
<div class="q-pa-md row justify-center q-gutter-md"> <div class="q-pa-md row justify-center q-gutter-md">
<QSpinner v-if="isLoading" color="primary" size="50px"> <QSpinner
</QSpinner> v-if="isLoading"
color="primary"
size="50px"
/>
<div <div
v-if="items && !items.length" v-if="items && !items.length"
class="text-subtitle1 text-grey-7 q-pa-md" class="text-subtitle1 text-grey-7 q-pa-md"
@ -180,19 +204,26 @@
> >
{{ $t('pleaseSetFilter') }} {{ $t('pleaseSetFilter') }}
</div> </div>
<QCard class="my-card" v-for="item in items" :key="item.id"> <QCard
<img :src="`${$imageBase}/catalog/200x200/${item.image}`" /> class="my-card"
v-for="_item in items"
:key="_item.id"
>
<img :src="`${$imageBase}/catalog/200x200/${_item.image}`">
<QCardSection> <QCardSection>
<div class="name text-subtitle1"> <div class="name text-subtitle1">
{{ item.longName }} {{ _item.longName }}
</div> </div>
<div <div
class="sub-name text-uppercase text-subtitle1 text-grey-7 ellipsize q-pt-xs" class="sub-name text-uppercase text-subtitle1 text-grey-7 ellipsize q-pt-xs"
> >
{{ item.subName }} {{ _item.subName }}
</div> </div>
<div class="tags q-pt-xs"> <div class="tags q-pt-xs">
<div v-for="tag in item.tags" :key="tag.tagFk"> <div
v-for="tag in _item.tags"
:key="tag.tagFk"
>
<span class="text-grey-7">{{ <span class="text-grey-7">{{
tag.tag.name tag.tag.name
}}</span> }}</span>
@ -203,26 +234,29 @@
<QCardActions class="actions justify-between"> <QCardActions class="actions justify-between">
<div class="q-pl-sm"> <div class="q-pl-sm">
<span class="available bg-green text-white">{{ <span class="available bg-green text-white">{{
item.available _item.available
}}</span> }}</span>
{{ $t('from') }} {{ $t('from') }}
<span class="price">{{ <span class="price">{{
currency(item.buy?.price3) currency(_item.buy?.price3)
}}</span> }}</span>
</div> </div>
<QBtn <QBtn
icon="add_shopping_cart" icon="add_shopping_cart"
:title="$t('buy')" :title="$t('buy')"
@click="showItem(item)" @click="showItem(_item)"
flat flat
> />
</QBtn>
</QCardActions> </QCardActions>
</QCard> </QCard>
</div> </div>
<template v-slot:loading> <template #loading>
<div class="row justify-center q-my-md"> <div class="row justify-center q-my-md">
<QSpinner color="primary" name="dots" size="40px" /> <QSpinner
color="primary"
name="dots"
size="40px"
/>
</div> </div>
</template> </template>
</QInfiniteScroll> </QInfiniteScroll>
@ -244,19 +278,30 @@
> >
{{ item.subName }} {{ item.subName }}
</div> </div>
<div class="text-grey-7">#{{ item.id }}</div> <div class="text-grey-7">
#{{ item.id }}
</div>
</QCardSection> </QCardSection>
<QCardSection> <QCardSection>
<div v-for="tag in item.tags" :key="tag.tagFk"> <div
v-for="tag in item.tags"
:key="tag.tagFk"
>
<span class="text-grey-7">{{ tag.tag.name }}</span> <span class="text-grey-7">{{ tag.tag.name }}</span>
{{ tag.value }} {{ tag.value }}
</div> </div>
</QCardSection> </QCardSection>
<QCardActions align="right"> <QCardActions align="right">
<QBtn @click="showItemDialog = false" flat> <QBtn
@click="showItemDialog = false"
flat
>
{{ $t('cancel') }} {{ $t('cancel') }}
</QBtn> </QBtn>
<QBtn @click="showItemDialog = false" flat> <QBtn
@click="showItemDialog = false"
flat
>
{{ $t('accept') }} {{ $t('accept') }}
</QBtn> </QBtn>
</QCardActions> </QCardActions>

View File

@ -17,18 +17,26 @@ const years = ref([]);
const invoices = ref([]); const invoices = ref([]);
const columns = computed(() => [ const columns = computed(() => [
{ name: 'ref', label: t('invoice'), field: 'ref', align: 'left' }, {
name: 'ref',
label: t('invoice'),
field: 'ref',
align: 'left',
sortable: true
},
{ {
name: 'issued', name: 'issued',
label: t('issued'), label: t('issued'),
field: 'issued', field: 'issued',
align: 'left', align: 'left',
sortable: true,
format: val => formatDate(val, 'D MMM YYYY') format: val => formatDate(val, 'D MMM YYYY')
}, },
{ {
name: 'amount', name: 'amount',
label: t('amount'), label: t('amount'),
field: 'amount', field: 'amount',
sortable: true,
format: val => currency(val) format: val => currency(val)
}, },
{ {

View File

@ -7,12 +7,14 @@ 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 useNotify from 'src/composables/useNotify.js';
import { currency, formatDateTitle } from 'src/lib/filters.js'; import { currency, formatDateTitle } from 'src/lib/filters.js';
import { tpvStore } from 'stores/tpv'; import { tpvStore } from 'stores/tpv';
const { t } = useI18n(); const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const jApi = inject('jApi'); const jApi = inject('jApi');
const { notify } = useNotify();
const showAmountToPayDialog = ref(null); const showAmountToPayDialog = ref(null);
const amountToPay = ref(null); const amountToPay = ref(null);
@ -36,6 +38,10 @@ const onPayClick = async () => {
}; };
const onConfirmPay = async () => { const onConfirmPay = async () => {
if (amountToPay.value <= 0) {
notify(t('amountError'), 'negative');
return;
}
if (amountToPay.value) { if (amountToPay.value) {
const amount = amountToPay.value.toString().replace('.', ','); const amount = amountToPay.value.toString().replace('.', ',');
amountToPay.value = parseFloat(amount); amountToPay.value = parseFloat(amount);
@ -48,10 +54,17 @@ const onConfirmPay = async () => {
<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" class="info" size="sm"> <QIcon
name="info"
class="info"
size="sm"
>
<QTooltip max-width="450px"> <QTooltip max-width="450px">
{{ t('paymentInfo') }} {{ t('paymentInfo') }}
</QTooltip> </QTooltip>
@ -126,6 +139,9 @@ const onConfirmPay = async () => {
v-model="amountToPay" v-model="amountToPay"
:clearable="false" :clearable="false"
class="full-width" class="full-width"
type="number"
min="0"
:max="debt * -1"
/> />
</template> </template>
</VnConfirm> </VnConfirm>
@ -170,6 +186,7 @@ en-US:
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.
amountToPay: 'Amount to pay (€):' amountToPay: 'Amount to pay (€):'
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 noOrdersFound: No se encontrado pedidos
@ -183,6 +200,7 @@ es-ES:
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.
amountToPay: 'Cantidad a pagar (€):' amountToPay: 'Cantidad a pagar (€):'
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 noOrdersFound: No s'han trobat comandes
@ -196,6 +214,7 @@ ca-ES:
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.
amountToPay: 'Quantitat a pagar (€):' amountToPay: 'Quantitat a pagar (€):'
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 noOrdersFound: Aucune commande trouvée
@ -209,6 +228,7 @@ fr-FR:
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.
amountToPay: 'Montant à payer (€):' amountToPay: 'Montant à 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 noOrdersFound: Nenhum pedido encontrado
@ -222,4 +242,5 @@ pt-PT:
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.
amountToPay: 'Valor a pagar (€):' amountToPay: 'Valor a pagar (€):'
amountError: A quantidade deve ser um número positivo e inferior ou igual ao importe pendiente
</i18n> </i18n>

View File

@ -82,9 +82,11 @@ onMounted(async () => {
:to="{ name: 'basket', params: { id: order.id } }" :to="{ name: 'basket', params: { id: order.id } }"
> >
<template #content> <template #content>
<QItemLabel class="text-bold q-mb-sm">{{ <QItemLabel class="text-bold q-mb-sm">
formatDateTitle(order.sent) {{
}}</QItemLabel> formatDateTitle(order.sent)
}}
</QItemLabel>
<QItemLabel> #{{ order.id }} </QItemLabel> <QItemLabel> #{{ order.id }} </QItemLabel>
<QItemLabel>{{ order.nickname }}</QItemLabel> <QItemLabel>{{ order.nickname }}</QItemLabel>
<QItemLabel>{{ order.agency }}</QItemLabel> <QItemLabel>{{ order.agency }}</QItemLabel>

View File

@ -25,9 +25,14 @@ const lineSubtotal = line =>
</script> </script>
<template> <template>
<QCard class="vn-w-sm" style="padding: 32px"> <QCard
class="vn-w-sm"
style="padding: 32px"
>
<QCardSection class="no-padding q-mb-md"> <QCardSection class="no-padding q-mb-md">
<div class="text-h6">#{{ ticket.id }}</div> <div class="text-h6">
#{{ ticket.id }}
</div>
</QCardSection> </QCardSection>
<QCardSection class="no-padding q-mb-md q-gutter-y-xs"> <QCardSection class="no-padding q-mb-md q-gutter-y-xs">
<div class="text-subtitle1 text-bold"> <div class="text-subtitle1 text-bold">
@ -69,7 +74,10 @@ const lineSubtotal = line =>
</span> </span>
</QCardSection> </QCardSection>
<QSeparator inset /> <QSeparator inset />
<QList v-for="row in rows" :key="row.itemFk"> <QList
v-for="row in rows"
:key="row.itemFk"
>
<QItem> <QItem>
<QItemSection avatar> <QItemSection avatar>
<VnImg <VnImg
@ -83,16 +91,25 @@ const lineSubtotal = line =>
<QItemLabel lines="1"> <QItemLabel lines="1">
{{ row.concept }} {{ row.concept }}
</QItemLabel> </QItemLabel>
<QItemLabel lines="1" caption> <QItemLabel
lines="1"
caption
>
{{ row.value5 }} {{ row.value6 }} {{ row.value7 }} {{ row.value5 }} {{ row.value6 }} {{ row.value7 }}
</QItemLabel> </QItemLabel>
<QItemLabel lines="1"> <QItemLabel lines="1">
{{ row.quantity }} x {{ currency(row.price) }} {{ row.quantity }} x {{ currency(row.price) }}
</QItemLabel> </QItemLabel>
</QItemSection> </QItemSection>
<QItemSection side class="total"> <QItemSection
side
class="total"
>
<QItemLabel> <QItemLabel>
<span class="discount" v-if="row.discount"> <span
class="discount"
v-if="row.discount"
>
{{ currency(lineDiscountSubtotal(row)) }} - {{ currency(lineDiscountSubtotal(row)) }} -
{{ currency(row.discount) }} = {{ currency(row.discount) }} =
</span> </span>

View File

@ -3,7 +3,7 @@ import { onMounted, inject, ref } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import TicketDetails from 'src/components/ui/TicketDetails.vue'; import TicketDetails from 'src/pages/Ecomerce/TicketDetails.vue';
import { userStore as useUserStore } from 'stores/user'; import { userStore as useUserStore } from 'stores/user';
@ -56,7 +56,10 @@ const onPrintClick = () => {
/> />
</Teleport> </Teleport>
<QPage> <QPage>
<TicketDetails :rows="rows" :ticket="ticket" /> <TicketDetails
:rows="rows"
:ticket="ticket"
/>
</QPage> </QPage>
</template> </template>

View File

@ -3,9 +3,14 @@
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"> <div
class="text-h2"
style="opacity: 0.4"
>
Oops. Nothing here... Oops. Nothing here...
</div> </div>

View File

@ -37,19 +37,33 @@ async function onLogin() {
<template> <template>
<div class="main"> <div class="main">
<div class="header"> <div class="header">
<router-link to="/" class="block"> <router-link
<img src="statics/logo.svg" alt="Verdnatura" class="block" /> to="/"
class="block"
>
<img
src="statics/logo.svg"
alt="Verdnatura"
class="block"
>
</router-link> </router-link>
</div> </div>
<QForm @submit="onLogin" class="q-gutter-y-md"> <QForm
@submit="onLogin"
class="q-gutter-y-md"
>
<div class="q-gutter-y-sm"> <div class="q-gutter-y-sm">
<QInput v-model="email" :label="$t('user')" autofocus /> <QInput
v-model="email"
:label="$t('user')"
autofocus
/>
<QInput <QInput
v-model="password" v-model="password"
:label="$t('password')" :label="$t('password')"
:type="!showPwd ? 'password' : 'text'" :type="!showPwd ? 'password' : 'text'"
> >
<template v-slot:append> <template #append>
<QIcon <QIcon
:name="showPwd ? 'visibility_off' : 'visibility'" :name="showPwd ? 'visibility_off' : 'visibility'"
class="cursor-pointer" class="cursor-pointer"
@ -57,31 +71,38 @@ async function onLogin() {
/> />
</template> </template>
</QInput> </QInput>
<div class=" text-center"> <QCheckbox <div class="text-center">
v-model="remember" <QCheckbox
:label="$t('remindMe')" v-model="remember"
dense :label="$t('remindMe')"
/> <QBtn dense
id="switchLanguage" />
:label="$t('language')" <QBtn
icon="translate" id="switchLanguage"
color="primary" :label="$t('language')"
size="sm" icon="translate"
flat color="primary"
rounded size="sm"
> flat
<QMenu auto-close> rounded
<QList dense v-for="lang in langs" :key="lang"> >
<QItem <QMenu auto-close>
disabled <QList
v-ripple dense
clickable v-for="lang in langs"
:key="lang"
> >
{{ $t(`langs.${lang}`) }} <QItem
</QItem> disabled
</QList> v-ripple
</QMenu> clickable
</QBtn></div> >
{{ $t(`langs.${lang}`) }}
</QItem>
</QList>
</QMenu>
</QBtn>
</div>
</div> </div>
<div class="justify-center"> <div class="justify-center">
<QBtn <QBtn
@ -106,7 +127,10 @@ async function onLogin() {
/> />
</div> </div>
<p class="password-forgotten text-center q-mt-lg"> <p class="password-forgotten text-center q-mt-lg">
<router-link to="/remember-password" class="link"> <router-link
to="/remember-password"
class="link"
>
{{ $t('haveForgottenPassword') }} {{ $t('haveForgottenPassword') }}
</router-link> </router-link>
</p> </p>

View File

@ -8,7 +8,10 @@
/> />
</div> </div>
<div> <div>
<QForm @submit="onSend" class="q-gutter-y-md text-grey-8"> <QForm
@submit="onSend"
class="q-gutter-y-md text-grey-8"
>
<div class="text-h5"> <div class="text-h5">
<div> <div>
{{ $t('dontWorry') }} {{ $t('dontWorry') }}
@ -37,7 +40,10 @@
unelevated unelevated
/> />
<div class="text-center q-mt-md"> <div class="text-center q-mt-md">
<router-link to="/login" class="link"> <router-link
to="/login"
class="link"
>
{{ $t('return') }} {{ $t('return') }}
</router-link> </router-link>
</div> </div>

View File

@ -8,7 +8,11 @@
/> />
</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>
@ -21,7 +25,7 @@
hint="" hint=""
filled filled
> >
<template v-slot:append> <template #append>
<QIcon <QIcon
:name=" :name="
showPwd ? 'visibility_off' : 'visibility' showPwd ? 'visibility_off' : 'visibility'
@ -42,7 +46,7 @@
hint="" hint=""
filled filled
> >
<template v-slot:append> <template #append>
<QIcon <QIcon
:name=" :name="
showRpPwd ? 'visibility_off' : 'visibility' showRpPwd ? 'visibility_off' : 'visibility'
@ -61,7 +65,10 @@
color="primary" color="primary"
/> />
<div class="text-center q-mt-xs"> <div class="text-center q-mt-xs">
<router-link to="/login" class="link"> <router-link
to="/login"
class="link"
>
{{ $t('return') }} {{ $t('return') }}
</router-link> </router-link>
</div> </div>