fixes #5056 Nuevo sistema de sacado de pedidos: Gestión de vagones #43

Merged
alexandre merged 16 commits from 5056-gestion-vagones into dev 2023-04-17 05:04:30 +00:00
90 changed files with 3805 additions and 2862 deletions
Showing only changes of commit 4e8e2b2e82 - Show all commits

View File

@ -140,8 +140,8 @@ module.exports = configure(function (/* ctx */) {
// Quasar plugins // Quasar plugins
plugins: ['Notify', 'Dialog'], plugins: ['Notify', 'Dialog'],
//all: 'auto', all: 'auto',
//autoImportComponentCase: 'pascal', autoImportComponentCase: 'pascal',
}, },
// animations: 'all', // --- includes all animations // animations: 'all', // --- includes all animations

View File

@ -36,7 +36,7 @@ quasar.iconMapFn = (iconName) => {
</script> </script>
<template> <template>
<router-view /> <RouterView />
</template> </template>
<style lang="scss"> <style lang="scss">

View File

@ -99,16 +99,16 @@ watch(formUrl, async () => {
}); });
</script> </script>
<template> <template>
<q-banner v-if="hasChanges" class="text-white bg-warning"> <QBanner v-if="hasChanges" class="text-white bg-warning">
<q-icon name="warning" size="md" class="q-mr-md" /> <QIcon name="warning" size="md" class="q-mr-md" />
<span>{{ t('globals.changesToSave') }}</span> <span>{{ t('globals.changesToSave') }}</span>
</q-banner> </QBanner>
<q-form v-if="formData" @submit="save" @reset="reset" class="q-pa-md"> <QForm v-if="formData" @submit="save" @reset="reset" class="q-pa-md">
<slot name="form" :data="formData" :validate="validate" :filter="filter"></slot> <slot name="form" :data="formData" :validate="validate" :filter="filter"></slot>
<div class="q-mt-lg"> <div class="q-mt-lg">
<slot name="actions"> <slot name="actions">
<q-btn :label="t('globals.save')" type="submit" color="primary" /> <QBtn :label="t('globals.save')" type="submit" color="primary" />
<q-btn <QBtn
:label="t('globals.reset')" :label="t('globals.reset')"
type="reset" type="reset"
class="q-ml-sm" class="q-ml-sm"
@ -118,7 +118,11 @@ watch(formUrl, async () => {
/> />
</slot> </slot>
</div> </div>
</q-form> </QForm>
<skeleton-form v-if="!formData" /> <SkeletonForm v-if="!formData" />
<q-inner-loading :showing="isLoading" :label="t('globals.pleaseWait')" color="primary" /> <QInnerLoading
:showing="isLoading"
:label="t('globals.pleaseWait')"
color="primary"
/>
</template> </template>

View File

@ -2,7 +2,7 @@
import axios from 'axios'; import axios from 'axios';
import { onMounted, ref, computed } from 'vue'; import { onMounted, ref, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { QSeparator, useQuasar } from 'quasar';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useNavigationStore } from 'src/stores/useNavigationStore'; import { useNavigationStore } from 'src/stores/useNavigationStore';
import { toLowerCamel } from 'src/filters'; import { toLowerCamel } from 'src/filters';
@ -65,7 +65,9 @@ function getRoutes() {
const modules = Object.assign([], navigation.getModules().value); const modules = Object.assign([], navigation.getModules().value);
for (const item of modules) { for (const item of modules) {
const moduleDef = routes.find((route) => toLowerCamel(route.name) === item.module); const moduleDef = routes.find(
(route) => toLowerCamel(route.name) === item.module
);
item.children = []; item.children = [];
if (!moduleDef) continue; if (!moduleDef) continue;
@ -79,7 +81,9 @@ function getRoutes() {
if (props.source === 'card') { if (props.source === 'card') {
const currentRoute = route.matched[1]; const currentRoute = route.matched[1];
const currentModule = toLowerCamel(currentRoute.name); const currentModule = toLowerCamel(currentRoute.name);
const moduleDef = routes.find((route) => toLowerCamel(route.name) === currentModule); const moduleDef = routes.find(
(route) => toLowerCamel(route.name) === currentModule
);
if (!moduleDef) return; if (!moduleDef) return;
@ -111,16 +115,16 @@ async function togglePinned(item, event) {
</script> </script>
<template> <template>
<q-list padding> <QList padding>
<template v-if="$props.source === 'main'"> <template v-if="$props.source === 'main'">
<q-item-label header> <QItemLabel header>
{{ t('globals.pinnedModules') }} {{ t('globals.pinnedModules') }}
</q-item-label> </QItemLabel>
<template v-for="item in pinnedItems" :key="item.name"> <template v-for="item in pinnedItems" :key="item.name">
<template v-if="item.children"> <template v-if="item.children">
<left-menu-item-group :item="item" group="pinnedModules" class="pinned"> <LeftMenuItemGroup :item="item" group="pinnedModules" class="pinned">
<template #side> <template #side>
<q-btn <QBtn
v-if="item.isPinned === true" v-if="item.isPinned === true"
@click="togglePinned(item, $event)" @click="togglePinned(item, $event)"
icon="vn:pin_off" icon="vn:pin_off"
@ -128,9 +132,11 @@ async function togglePinned(item, event) {
flat flat
round round
> >
<q-tooltip>{{ t('components.leftMenu.removeFromPinned') }}</q-tooltip> <QTooltip>{{
</q-btn> t('components.leftMenu.removeFromPinned')
<q-btn }}</QTooltip>
</QBtn>
<QBtn
v-if="item.isPinned === false" v-if="item.isPinned === false"
@click="togglePinned(item, $event)" @click="togglePinned(item, $event)"
icon="vn:pin" icon="vn:pin"
@ -138,21 +144,23 @@ async function togglePinned(item, event) {
flat flat
round round
> >
<q-tooltip>{{ t('components.leftMenu.addToPinned') }}</q-tooltip> <QTooltip>{{
</q-btn> t('components.leftMenu.addToPinned')
}}</QTooltip>
</QBtn>
</template> </template>
</left-menu-item-group> </LeftMenuItemGroup>
</template> </template>
<left-menu-item v-if="!item.children" :item="item" /> <LeftMenuItem v-if="!item.children" :item="item" />
</template> </template>
<q-separator /> <QSeparator />
<q-expansion-item :label="t('moduleIndex.allModules')"> <QExpansionItem :label="t('moduleIndex.allModules')">
<template v-for="item in items" :key="item.name"> <template v-for="item in items" :key="item.name">
<template v-if="item.children"> <template v-if="item.children">
<left-menu-item-group :item="item" group="modules"> <LeftMenuItemGroup :item="item" group="modules">
<template #side> <template #side>
<q-btn <QBtn
v-if="item.isPinned === true" v-if="item.isPinned === true"
@click="togglePinned(item, $event)" @click="togglePinned(item, $event)"
icon="vn:pin_off" icon="vn:pin_off"
@ -160,9 +168,11 @@ async function togglePinned(item, event) {
flat flat
round round
> >
<q-tooltip>{{ t('components.leftMenu.removeFromPinned') }}</q-tooltip> <QTooltip>
</q-btn> {{ t('components.leftMenu.removeFromPinned') }}
<q-btn </QTooltip>
</QBtn>
<QBtn
v-if="item.isPinned === false" v-if="item.isPinned === false"
@click="togglePinned(item, $event)" @click="togglePinned(item, $event)"
icon="vn:pin" icon="vn:pin"
@ -170,21 +180,23 @@ async function togglePinned(item, event) {
flat flat
round round
> >
<q-tooltip>{{ t('components.leftMenu.addToPinned') }}</q-tooltip> <QTooltip>
</q-btn> {{ t('components.leftMenu.addToPinned') }}
</QTooltip>
</QBtn>
</template> </template>
</left-menu-item-group> </LeftMenuItemGroup>
</template> </template>
</template> </template>
</q-expansion-item> </QExpansionItem>
<q-separator /> <QSeparator />
</template> </template>
<template v-if="$props.source === 'card'"> <template v-if="$props.source === 'card'">
<template v-for="item in items" :key="item.name"> <template v-for="item in items" :key="item.name">
<left-menu-item v-if="!item.children" :item="item" /> <LeftMenuItem v-if="!item.children" :item="item" />
</template> </template>
</template> </template>
</q-list> </QList>
</template> </template>
<style> <style>

View File

@ -14,13 +14,13 @@ const props = defineProps({
const item = computed(() => props.item); const item = computed(() => props.item);
</script> </script>
<template> <template>
<q-item active-class="text-primary" :to="{ name: item.name }" clickable v-ripple> <QItem active-class="text-primary" :to="{ name: item.name }" clickable v-ripple>
<q-item-section avatar v-if="item.icon"> <QItemSection avatar v-if="item.icon">
<q-icon :name="item.icon" /> <QIcon :name="item.icon" />
</q-item-section> </QItemSection>
<q-item-section avatar v-if="!item.icon"> <QItemSection avatar v-if="!item.icon">
<q-icon name="disabled_by_default" /> <QIcon name="disabled_by_default" />
</q-item-section> </QItemSection>
<q-item-section>{{ t(item.title) }}</q-item-section> <QItemSection>{{ t(item.title) }}</QItemSection>
</q-item> </QItem>
</template> </template>

View File

@ -27,7 +27,7 @@ const isOpened = computed(() => {
}); });
</script> </script>
<template> <template>
<q-expansion-item <QExpansionItem
:group="props.group" :group="props.group"
active-class="text-primary" active-class="text-primary"
:label="item.title" :label="item.title"
@ -36,16 +36,16 @@ const isOpened = computed(() => {
:default-opened="isOpened" :default-opened="isOpened"
> >
<template #header> <template #header>
<q-item-section avatar> <QItemSection avatar>
<q-icon :name="item.icon"></q-icon> <QIcon :name="item.icon"></QIcon>
</q-item-section> </QItemSection>
<q-item-section>{{ t(item.title) }}</q-item-section> <QItemSection>{{ t(item.title) }}</QItemSection>
<q-item-section side> <QItemSection side>
<slot name="side" :item="item" /> <slot name="side" :item="item" />
</q-item-section> </QItemSection>
</template> </template>
<template v-for="section in item.children" :key="section.name"> <template v-for="section in item.children" :key="section.name">
<left-menu-item :item="section" /> <LeftMenuItem :item="section" />
</template> </template>
</q-expansion-item> </QExpansionItem>
</template> </template>

View File

@ -19,9 +19,9 @@ onMounted(() => stateStore.setMounted());
</script> </script>
<template> <template>
<q-header class="bg-dark" color="white" elevated> <QHeader class="bg-dark" color="white" elevated>
<q-toolbar class="q-py-sm q-px-md"> <QToolbar class="q-py-sm q-px-md">
<q-btn <QBtn
@click="stateStore.toggleLeftDrawer()" @click="stateStore.toggleLeftDrawer()"
icon="menu" icon="menu"
class="q-mr-sm" class="q-mr-sm"
@ -29,54 +29,54 @@ onMounted(() => stateStore.setMounted());
dense dense
flat flat
> >
<q-tooltip bottom anchor="bottom right"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
<router-link to="/"> <RouterLink to="/">
<q-btn class="q-ml-xs" color="primary" v-if="$q.screen.gt.xs" flat round> <QBtn class="q-ml-xs" color="primary" flat round>
<q-avatar square size="md"> <QAvatar square size="md">
<q-img <QImg
src="~/assets/logo_icon.svg" src="~/assets/logo_icon.svg"
:alt="appName" :alt="appName"
spinner-color="primary" spinner-color="primary"
/> />
</q-avatar> </QAvatar>
<q-tooltip bottom> <QTooltip bottom>
{{ t('globals.backToDashboard') }} {{ t('globals.backToDashboard') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</router-link> </RouterLink>
<q-toolbar-title shrink class="text-weight-bold" v-if="$q.screen.gt.xs"> <QToolbarTitle shrink class="text-weight-bold" v-if="$q.screen.gt.sm">
{{ appName }} {{ appName }}
<q-badge label="Beta" align="top" /> <QBadge label="Beta" align="top" />
</q-toolbar-title> </QToolbarTitle>
<q-space></q-space> <QSpace />
<div id="searchbar"></div> <div id="searchbar"></div>
<q-space></q-space> <QSpace />
<div class="q-pl-sm q-gutter-sm row items-center no-wrap"> <div class="q-pl-sm q-gutter-sm row items-center no-wrap">
<div id="actions-prepend"></div> <div id="actions-prepend"></div>
<q-btn id="pinnedModules" icon="apps" flat dense rounded> <QBtn id="pinnedModules" icon="apps" flat dense rounded>
<q-tooltip bottom> <QTooltip bottom>
{{ t('globals.pinnedModules') }} {{ t('globals.pinnedModules') }}
</q-tooltip> </QTooltip>
<PinnedModules /> <PinnedModules />
</q-btn> </QBtn>
<q-btn rounded dense flat no-wrap id="user"> <QBtn rounded dense flat no-wrap id="user">
<q-avatar size="lg"> <QAvatar size="lg">
<q-img <QImg
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`" :src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
spinner-color="primary" spinner-color="primary"
> >
</q-img> </QImg>
</q-avatar> </QAvatar>
<q-tooltip bottom> <QTooltip bottom>
{{ t('globals.userPanel') }} {{ t('globals.userPanel') }}
</q-tooltip> </QTooltip>
<UserPanel /> <UserPanel />
</q-btn> </QBtn>
<div id="actions-append"></div> <div id="actions-append"></div>
</div> </div>
</q-toolbar> </QToolbar>
</q-header> </QHeader>
</template> </template>

View File

@ -14,10 +14,19 @@ const pinnedModules = computed(() => navigation.getPinnedModules());
</script> </script>
<template> <template>
<q-menu anchor="bottom left" class="row q-pa-md q-col-gutter-lg" max-width="350px" max-height="400px"> <QMenu
anchor="bottom left"
class="row q-pa-md q-col-gutter-lg"
max-width="350px"
max-height="400px"
>
<template v-if="pinnedModules.length"> <template v-if="pinnedModules.length">
<div v-for="item of pinnedModules" :key="item.title" class="row no-wrap q-pa-xs flex-item"> <div
<q-btn v-for="item of pinnedModules"
:key="item.title"
class="row no-wrap q-pa-xs flex-item"
>
<QBtn
align="evenly" align="evenly"
padding="16px" padding="16px"
flat flat
@ -31,15 +40,18 @@ const pinnedModules = computed(() => navigation.getPinnedModules());
<div class="text-center text-primary button-text"> <div class="text-center text-primary button-text">
{{ t(item.title) }} {{ t(item.title) }}
</div> </div>
</q-btn> </QBtn>
</div> </div>
</template> </template>
<template v-else> <template v-else>
<div class="row no-wrap q-pa-xs flex-item text-center text-grey-5" style="min-width: 200px"> <div
class="row no-wrap q-pa-xs flex-item text-center text-grey-5"
style="min-width: 200px"
>
{{ t('globals.noPinnedModules') }} {{ t('globals.noPinnedModules') }}
</div> </div>
</template> </template>
</q-menu> </QMenu>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -84,11 +84,13 @@ function logout() {
</script> </script>
<template> <template>
<q-menu anchor="bottom left"> <QMenu anchor="bottom left">
<div class="row no-wrap q-pa-md"> <div class="row no-wrap q-pa-md">
<div class="column panel"> <div class="column panel">
<div class="text-h6 q-mb-md">{{ t('components.userPanel.settings') }}</div> <div class="text-h6 q-mb-md">
<q-toggle {{ t('components.userPanel.settings') }}
</div>
<QToggle
v-model="userLocale" v-model="userLocale"
@update:model-value="saveLanguage" @update:model-value="saveLanguage"
:label="t(`globals.lang['${userLocale}']`)" :label="t(`globals.lang['${userLocale}']`)"
@ -97,7 +99,7 @@ function logout() {
false-value="es" false-value="es"
true-value="en" true-value="en"
/> />
<q-toggle <QToggle
v-model="darkMode" v-model="darkMode"
@update:model-value="saveDarkMode" @update:model-value="saveDarkMode"
:label="t(`globals.darkMode`)" :label="t(`globals.darkMode`)"
@ -107,22 +109,22 @@ function logout() {
/> />
</div> </div>
<q-separator vertical inset class="q-mx-lg" /> <QSeparator vertical inset class="q-mx-lg" />
<div class="column items-center panel"> <div class="column items-center panel">
<q-avatar size="80px"> <QAvatar size="80px">
<q-img <QImg
:src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`" :src="`/api/Images/user/160x160/${user.id}/download?access_token=${token}`"
spinner-color="white" spinner-color="white"
/> />
</q-avatar> </QAvatar>
<div class="text-subtitle1 q-mt-md"> <div class="text-subtitle1 q-mt-md">
<strong>{{ user.nickname }}</strong> <strong>{{ user.nickname }}</strong>
</div> </div>
<div class="text-subtitle3 text-grey-7 q-mb-xs">@{{ user.name }}</div> <div class="text-subtitle3 text-grey-7 q-mb-xs">@{{ user.name }}</div>
<q-btn <QBtn
id="logout" id="logout"
color="orange" color="orange"
flat flat
@ -134,7 +136,7 @@ function logout() {
/> />
</div> </div>
</div> </div>
</q-menu> </QMenu>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -40,31 +40,31 @@ async function confirm() {
} }
</script> </script>
<template> <template>
<q-dialog ref="dialogRef" persistent> <QDialog ref="dialogRef" persistent>
<q-card class="q-pa-sm"> <QCard class="q-pa-sm">
<q-card-section class="row items-center q-pb-none"> <QCardSection class="row items-center q-pb-none">
<span class="text-h6 text-grey">{{ t('Send email notification') }}</span> <span class="text-h6 text-grey">{{ t('Send email notification') }}</span>
<q-space /> <QSpace />
<q-btn icon="close" flat round dense v-close-popup /> <QBtn icon="close" flat round dense v-close-popup />
</q-card-section> </QCardSection>
<q-card-section class="row items-center"> <QCardSection class="row items-center">
{{ t('The notification will be sent to the following address') }} {{ t('The notification will be sent to the following address') }}
</q-card-section> </QCardSection>
<q-card-section class="q-pt-none"> <QCardSection class="q-pt-none">
<q-input dense v-model="address" rounded outlined autofocus /> <QInput dense v-model="address" rounded outlined autofocus />
</q-card-section> </QCardSection>
<q-card-actions align="right"> <QCardActions align="right">
<q-btn :label="t('globals.cancel')" color="primary" flat v-close-popup /> <QBtn :label="t('globals.cancel')" color="primary" flat v-close-popup />
<q-btn <QBtn
:label="t('globals.confirm')" :label="t('globals.confirm')"
color="primary" color="primary"
:loading="isLoading" :loading="isLoading"
@click="confirm" @click="confirm"
unelevated unelevated
/> />
</q-card-actions> </QCardActions>
</q-card> </QCard>
</q-dialog> </QDialog>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -83,27 +83,27 @@ async function send() {
</script> </script>
<template> <template>
<q-dialog ref="dialogRef" persistent> <QDialog ref="dialogRef" persistent>
<q-card class="q-pa-sm"> <QCard class="q-pa-sm">
<q-card-section class="row items-center q-pb-none"> <QCardSection class="row items-center q-pb-none">
<span class="text-h6 text-grey"> <span class="text-h6 text-grey">
{{ t('Send SMS') }} {{ t('Send SMS') }}
</span> </span>
<q-space /> <QSpace />
<q-btn icon="close" :disable="isLoading" flat round dense v-close-popup /> <QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
</q-card-section> </QCardSection>
<q-card-section v-if="props.locale"> <QCardSection v-if="props.locale">
<q-banner class="bg-amber text-white" rounded dense> <QBanner class="bg-amber text-white" rounded dense>
<template #avatar> <template #avatar>
<q-icon name="warning" /> <QIcon name="warning" />
</template> </template>
<span <span
v-html="t('CustomerDefaultLanguage', { locale: t(props.locale) })" v-html="t('CustomerDefaultLanguage', { locale: t(props.locale) })"
></span> ></span>
</q-banner> </QBanner>
</q-card-section> </QCardSection>
<q-card-section class="q-pb-xs"> <QCardSection class="q-pb-xs">
<q-select <QSelect
:label="t('Language')" :label="t('Language')"
:options="languages" :options="languages"
v-model="locale" v-model="locale"
@ -115,9 +115,9 @@ async function send() {
outlined outlined
dense dense
/> />
</q-card-section> </QCardSection>
<q-card-section class="q-pb-xs"> <QCardSection class="q-pb-xs">
<q-input <QInput
:label="t('Phone')" :label="t('Phone')"
v-model="phone" v-model="phone"
rounded rounded
@ -125,9 +125,9 @@ async function send() {
autofocus autofocus
dense dense
/> />
</q-card-section> </QCardSection>
<q-card-section class="q-pb-xs"> <QCardSection class="q-pb-xs">
<q-input <QInput
:label="t('Subject')" :label="t('Subject')"
v-model="subject" v-model="subject"
rounded rounded
@ -135,9 +135,9 @@ async function send() {
autofocus autofocus
dense dense
/> />
</q-card-section> </QCardSection>
<q-card-section class="q-mb-md" q-input> <QCardSection class="q-mb-md" q-input>
<q-input <QInput
:label="t('Message')" :label="t('Message')"
v-model="message" v-model="message"
type="textarea" type="textarea"
@ -151,7 +151,7 @@ async function send() {
autofocus autofocus
> >
<template #append> <template #append>
<q-icon <QIcon
v-if="message !== ''" v-if="message !== ''"
name="close" name="close"
@click="message = ''" @click="message = ''"
@ -159,30 +159,30 @@ async function send() {
/> />
</template> </template>
<template #counter> <template #counter>
<q-chip :color="color" dense> <QChip :color="color" dense>
{{ totalLength }}/{{ maxLength }} {{ totalLength }}/{{ maxLength }}
</q-chip> </QChip>
</template> </template>
</q-input> </QInput>
</q-card-section> </QCardSection>
<q-card-actions align="right"> <QCardActions align="right">
<q-btn <QBtn
:label="t('globals.cancel')" :label="t('globals.cancel')"
color="primary" color="primary"
:disable="isLoading" :disable="isLoading"
flat flat
v-close-popup v-close-popup
/> />
<q-btn <QBtn
:label="t('globals.confirm')" :label="t('globals.confirm')"
@click="send()" @click="send()"
:loading="isLoading" :loading="isLoading"
color="primary" color="primary"
unelevated unelevated
/> />
</q-card-actions> </QCardActions>
</q-card> </QCard>
</q-dialog> </QDialog>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -0,0 +1,139 @@
<script setup>
import { onMounted, ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { toCurrency } from 'filters/index';
const { t } = useI18n();
const props = defineProps({
quantity: {
type: Number,
required: true,
},
price: {
type: Number,
required: true,
},
discount: {
type: Number,
required: false,
default: 0,
},
mana: {
type: Number,
required: true,
},
promise: {
type: Function,
required: true,
},
data: {
type: Object,
required: false,
default: null,
},
});
const emit = defineEmits(['onUpdate']);
const discount = ref(0);
let canceller;
onMounted(() => {
if (props.discount) {
discount.value = props.discount;
}
});
const total = computed(() => {
const amount = props.price * props.quantity;
const appliedDiscount = (discount.value * amount) / 100;
return amount - appliedDiscount;
});
const newAmount = computed(() => `${t('New amount')}: ${toCurrency(total.value)}`);
const isLoading = ref(false);
async function save({ set }) {
isLoading.value = true;
const response = {
...props.data,
...{
discount: parseInt(discount.value),
},
};
if (props.promise) {
canceller = new AbortController();
Object.assign(response, { canceller });
await props.promise(response);
}
if (set) set();
emit('onUpdate', response);
isLoading.value = false;
}
function cancel({ cancel }) {
if (canceller) {
canceller.abort();
canceller = null;
}
discount.value = props.discount;
if (isLoading.value === true) isLoading.value = false;
if (cancel) cancel();
}
</script>
<template>
<QPopupEdit
v-model="discount"
v-slot="scope"
:title="t('Update discount')"
@cancel="cancel"
>
<QBanner class="bg-primary text-center q-mb-md" rounded dense>
{{ t('Mana') }} {{ toCurrency(props.mana) }}
</QBanner>
<QInput
v-model="scope.value"
type="number"
dense
autofocus
@keyup.enter="save(scope)"
@update:model-value="discount = scope.value"
@focus="($event) => $event.target.select()"
min="0"
max="100"
:disable="isLoading"
:hint="newAmount"
:rules="[
(value) => value.length > 0 || t('Enter a value'),
(value) => (value >= 0 && value <= 100) || t('Invalid discount amount'),
]"
/>
<QCardActions class="q-px-none q-mt-sm" align="right">
<QBtn :label="t('Cancel')" color="primary" flat @click="cancel(scope)" />
<QBtn
:label="t('Update')"
color="primary"
:loading="isLoading"
unelevated
@click="save(scope)"
/>
</QCardActions>
</QPopupEdit>
</template>
<i18n>
es:
New amount: Nuevo importe
Update discount: Actualizar descuento
Mana: Maná
Enter a value: Introduce un valor
Invalid discount amount: Cantidad de descuento incorrecta
Cancel: Cancelar
Update: Actualizar
</i18n>

View File

@ -48,24 +48,22 @@ watch(props, async () => {
<div class="descriptor"> <div class="descriptor">
<template v-if="entity"> <template v-if="entity">
<div class="header bg-primary q-pa-sm"> <div class="header bg-primary q-pa-sm">
<router-link :to="{ name: `${module}List` }"> <RouterLink :to="{ name: `${module}List` }">
<q-btn round flat dense size="md" icon="view_list" color="white"> <QBtn round flat dense size="md" icon="view_list" color="white">
<q-tooltip> <QTooltip>
{{ t('components.cardDescriptor.mainList') }} {{ t('components.cardDescriptor.mainList') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</router-link> </RouterLink>
<router-link <RouterLink :to="{ name: `${module}Summary`, params: { id: entity.id } }">
:to="{ name: `${module}Summary`, params: { id: entity.id } }" <QBtn round flat dense size="md" icon="launch" color="white">
> <QTooltip>
<q-btn round flat dense size="md" icon="launch" color="white">
<q-tooltip>
{{ t('components.cardDescriptor.summary') }} {{ t('components.cardDescriptor.summary') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</router-link> </RouterLink>
<q-btn <QBtn
v-if="slots.menu" v-if="slots.menu"
size="md" size="md"
icon="more_vert" icon="more_vert"
@ -74,39 +72,39 @@ watch(props, async () => {
flat flat
dense dense
> >
<q-tooltip> <QTooltip>
{{ t('components.cardDescriptor.moreOptions') }} {{ t('components.cardDescriptor.moreOptions') }}
</q-tooltip> </QTooltip>
<q-menu> <QMenu>
<q-list> <QList>
<slot name="menu" :entity="entity" /> <slot name="menu" :entity="entity" />
</q-list> </QList>
</q-menu> </QMenu>
</q-btn> </QBtn>
</div> </div>
<slot name="before" /> <slot name="before" />
<div class="body q-py-sm"> <div class="body q-py-sm">
<q-list dense> <QList dense>
<q-item-label header class="ellipsis text-h5" :lines="1"> <QItemLabel header class="ellipsis text-h5" :lines="1">
<slot name="description" :entity="entity"> <slot name="description" :entity="entity">
<span> <span>
{{ entity.name }} {{ entity.name }}
<q-tooltip>{{ entity.name }}</q-tooltip> <QTooltip>{{ entity.name }}</QTooltip>
</span> </span>
</slot> </slot>
</q-item-label> </QItemLabel>
<q-item dense> <QItem dense>
<q-item-label class="text-subtitle2" caption> <QItemLabel class="text-subtitle2" caption>
#{{ entity.id }} #{{ entity.id }}
</q-item-label> </QItemLabel>
</q-item> </QItem>
</q-list> </QList>
<slot name="body" :entity="entity" /> <slot name="body" :entity="entity" />
</div> </div>
<slot name="after" /> <slot name="after" />
</template> </template>
<!-- Skeleton --> <!-- Skeleton -->
<skeleton-descriptor v-if="!entity" /> <SkeletonDescriptor v-if="!entity" />
</div> </div>
</template> </template>

View File

@ -41,8 +41,8 @@ watch(props, async () => {
<template> <template>
<div class="summary container"> <div class="summary container">
<q-card> <QCard>
<skeleton-summary v-if="!entity" /> <SkeletonSummary v-if="!entity" />
<template v-if="entity"> <template v-if="entity">
<div class="header bg-primary q-pa-sm q-mb-md"> <div class="header bg-primary q-pa-sm q-mb-md">
<slot name="header" :entity="entity"> <slot name="header" :entity="entity">
@ -53,7 +53,7 @@ watch(props, async () => {
<slot name="body" :entity="entity" /> <slot name="body" :entity="entity" />
</div> </div>
</template> </template>
</q-card> </QCard>
</div> </div>
</template> </template>

View File

@ -1,19 +1,19 @@
<template> <template>
<div id="descriptor-skeleton"> <div id="descriptor-skeleton">
<div class="col q-pl-sm q-pa-sm"> <div class="col q-pl-sm q-pa-sm">
<q-skeleton type="text" square height="45px" /> <QSkeleton type="text" square height="45px" />
<q-skeleton type="text" square height="18px" /> <QSkeleton type="text" square height="18px" />
<q-skeleton type="text" square height="18px" /> <QSkeleton type="text" square height="18px" />
<q-skeleton type="text" square height="18px" /> <QSkeleton type="text" square height="18px" />
</div> </div>
<q-card-actions> <QCardActions>
<q-skeleton size="40px" /> <QSkeleton size="40px" />
<q-skeleton size="40px" /> <QSkeleton size="40px" />
<q-skeleton size="40px" /> <QSkeleton size="40px" />
<q-skeleton size="40px" /> <QSkeleton size="40px" />
<q-skeleton size="40px" /> <QSkeleton size="40px" />
</q-card-actions> </QCardActions>
</div> </div>
</template> </template>

View File

@ -2,31 +2,31 @@
<div class="q-pa-md"> <div class="q-pa-md">
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-skeleton type="QInput" square /> <QSkeleton type="QInput" square />
</div> </div>
<div class="col"> <div class="col">
<q-skeleton type="QInput" square /> <QSkeleton type="QInput" square />
</div> </div>
</div> </div>
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-skeleton type="QInput" square /> <QSkeleton type="QInput" square />
</div> </div>
<div class="col"> <div class="col">
<q-skeleton type="QInput" square /> <QSkeleton type="QInput" square />
</div> </div>
</div> </div>
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-skeleton type="QInput" square /> <QSkeleton type="QInput" square />
</div> </div>
<div class="col"> <div class="col">
<q-skeleton type="QInput" square /> <QSkeleton type="QInput" square />
</div> </div>
</div> </div>
<div class="row q-gutter-md"> <div class="row q-gutter-md">
<q-skeleton type="QBtn" /> <QSkeleton type="QBtn" />
<q-skeleton type="QBtn" /> <QSkeleton type="QBtn" />
</div> </div>
</div> </div>
</template> </template>

View File

@ -1,47 +1,47 @@
<template> <template>
<div class="header bg-primary q-pa-sm q-mb-md"> <div class="header bg-primary q-pa-sm q-mb-md">
<q-skeleton type="rect" square /> <QSkeleton type="rect" square />
</div> </div>
<div class="row q-pa-md q-col-gutter-md q-mb-md"> <div class="row q-pa-md q-col-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-skeleton type="rect" class="q-mb-md" square /> <QSkeleton type="rect" class="q-mb-md" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
</div> </div>
<div class="col"> <div class="col">
<q-skeleton type="rect" class="q-mb-md" square /> <QSkeleton type="rect" class="q-mb-md" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
</div> </div>
<div class="col"> <div class="col">
<q-skeleton type="rect" class="q-mb-md" square /> <QSkeleton type="rect" class="q-mb-md" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
</div> </div>
<div class="col"> <div class="col">
<q-skeleton type="rect" class="q-mb-md" square /> <QSkeleton type="rect" class="q-mb-md" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
</div> </div>
<div class="col"> <div class="col">
<q-skeleton type="rect" class="q-mb-md" square /> <QSkeleton type="rect" class="q-mb-md" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
</div> </div>
</div> </div>
</template> </template>

View File

@ -51,10 +51,10 @@ async function confirm() {
} }
</script> </script>
<template> <template>
<q-dialog ref="dialogRef" persistent> <QDialog ref="dialogRef" persistent>
<q-card class="q-pa-sm"> <QCard class="q-pa-sm">
<q-card-section class="row items-center q-pb-none q-gutter-md"> <QCardSection class="row items-center q-pb-none">
<q-avatar <QAvatar
:icon="icon" :icon="icon"
color="primary" color="primary"
text-color="white" text-color="white"
@ -62,21 +62,21 @@ async function confirm() {
v-if="icon" v-if="icon"
/> />
<span class="text-h6 text-grey">{{ title }}</span> <span class="text-h6 text-grey">{{ title }}</span>
<q-space /> <QSpace />
<q-btn icon="close" :disable="isLoading" flat round dense v-close-popup /> <QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
</q-card-section> </QCardSection>
<q-card-section class="row items-center"> <QCardSection class="row items-center">
{{ message }} <span v-html="message"></span>
</q-card-section> </QCardSection>
<q-card-actions align="right"> <QCardActions align="right">
<q-btn <QBtn
:label="t('globals.cancel')" :label="t('globals.cancel')"
color="primary" color="primary"
:disable="isLoading" :disable="isLoading"
flat flat
v-close-popup v-close-popup
/> />
<q-btn <QBtn
:label="t('globals.confirm')" :label="t('globals.confirm')"
color="primary" color="primary"
:loading="isLoading" :loading="isLoading"
@ -84,9 +84,9 @@ async function confirm() {
unelevated unelevated
autofocus autofocus
/> />
</q-card-actions> </QCardActions>
</q-card> </QCard>
</q-dialog> </QDialog>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -99,21 +99,17 @@ function formatValue(value) {
} }
</script> </script>
<template> <template>
<q-form @submit="search"> <QForm @submit="search">
<q-list dense> <QList dense>
<q-item class="q-mt-xs"> <QItem class="q-mt-xs">
<q-item-section top> <QItemSection top>
<q-item-label <QItemLabel header lines="1" class="text-uppercase q-py-xs q-px-none">
header
lines="1"
class="text-uppercase q-py-xs q-px-none"
>
{{ t('Applied filters') }} {{ t('Applied filters') }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section top side> <QItemSection top side>
<div class="q-gutter-xs"> <div class="q-gutter-xs">
<q-btn <QBtn
@click="clearFilters" @click="clearFilters"
icon="filter_list_off" icon="filter_list_off"
color="primary" color="primary"
@ -123,9 +119,9 @@ function formatValue(value) {
flat flat
dense dense
> >
<q-tooltip>{{ t('Remove filters') }}</q-tooltip> <QTooltip>{{ t('Remove filters') }}</QTooltip>
</q-btn> </QBtn>
<q-btn <QBtn
@click="reload" @click="reload"
icon="refresh" icon="refresh"
color="primary" color="primary"
@ -135,12 +131,12 @@ function formatValue(value) {
flat flat
dense dense
> >
<q-tooltip>{{ t('Refresh') }}</q-tooltip> <QTooltip>{{ t('Refresh') }}</QTooltip>
</q-btn> </QBtn>
</div> </div>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<div <div
v-if="tags.length === 0" v-if="tags.length === 0"
class="text-grey font-xs text-center full-width" class="text-grey font-xs text-center full-width"
@ -148,7 +144,7 @@ function formatValue(value) {
{{ t(`No filters applied`) }} {{ t(`No filters applied`) }}
</div> </div>
<div> <div>
<q-chip <QChip
v-for="chip of tags" v-for="chip of tags"
:key="chip.label" :key="chip.label"
@remove="remove(chip.label)" @remove="remove(chip.label)"
@ -164,14 +160,14 @@ function formatValue(value) {
<span>"{{ chip.value }}"</span> <span>"{{ chip.value }}"</span>
</div> </div>
</slot> </slot>
</q-chip> </QChip>
</div> </div>
</q-item> </QItem>
<q-separator /> <QSeparator />
<template v-if="props.searchButton"> <template v-if="props.searchButton">
<q-item> <QItem>
<q-item-section class="q-py-sm"> <QItemSection class="q-py-sm">
<q-btn <QBtn
:label="t('Search')" :label="t('Search')"
type="submit" type="submit"
color="primary" color="primary"
@ -181,14 +177,14 @@ function formatValue(value) {
rounded rounded
dense dense
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-separator /> <QSeparator />
</template> </template>
</q-list> </QList>
<slot name="body" :params="userParams" :search-fn="search"></slot> <slot name="body" :params="userParams" :search-fn="search"></slot>
</q-form> </QForm>
<q-inner-loading <QInnerLoading
:showing="isLoading" :showing="isLoading"
:label="t('globals.pleaseWait')" :label="t('globals.pleaseWait')"
color="primary" color="primary"

View File

@ -145,31 +145,31 @@ async function onLoad(...params) {
</h5> </h5>
</div> </div>
<div v-if="props.autoLoad && !store.data" class="card-list q-gutter-y-md"> <div v-if="props.autoLoad && !store.data" class="card-list q-gutter-y-md">
<q-card class="card" v-for="$index in $props.limit" :key="$index"> <QCard class="card" v-for="$index in $props.limit" :key="$index">
<q-item v-ripple class="q-pa-none items-start cursor-pointer q-hoverable"> <QItem v-ripple class="q-pa-none items-start cursor-pointer q-hoverable">
<q-item-section class="q-pa-md"> <QItemSection class="q-pa-md">
<q-skeleton type="rect" class="q-mb-md" square /> <QSkeleton type="rect" class="q-mb-md" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" class="q-mb-md" square /> <QSkeleton type="text" class="q-mb-md" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
<q-skeleton type="text" square /> <QSkeleton type="text" square />
</q-item-section> </QItemSection>
<q-separator vertical /> <QSeparator vertical />
<q-card-actions vertical class="justify-between"> <QCardActions vertical class="justify-between">
<q-skeleton type="circle" class="q-mb-md" size="40px" /> <QSkeleton type="circle" class="q-mb-md" size="40px" />
<q-skeleton type="circle" class="q-mb-md" size="40px" /> <QSkeleton type="circle" class="q-mb-md" size="40px" />
<q-skeleton type="circle" class="q-mb-md" size="40px" /> <QSkeleton type="circle" class="q-mb-md" size="40px" />
</q-card-actions> </QCardActions>
</q-item> </QItem>
</q-card> </QCard>
</div> </div>
</div> </div>
<q-infinite-scroll v-if="store.data" @load="onLoad" :offset="offset"> <QInfiniteScroll v-if="store.data" @load="onLoad" :offset="offset">
<slot name="body" :rows="store.data"></slot> <slot name="body" :rows="store.data"></slot>
<div v-if="isLoading" class="info-row q-pa-md text-center"> <div v-if="isLoading" class="info-row q-pa-md text-center">
<q-spinner color="orange" size="md" /> <QSpinner color="orange" size="md" />
</div> </div>
</q-infinite-scroll> </QInfiniteScroll>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -83,8 +83,8 @@ async function search() {
</script> </script>
<template> <template>
<q-form @submit="search"> <QForm @submit="search">
<q-input <QInput
id="searchbar" id="searchbar"
v-model="searchText" v-model="searchText"
:placeholder="props.label" :placeholder="props.label"
@ -93,32 +93,30 @@ async function search() {
autofocus autofocus
> >
<template #prepend> <template #prepend>
<q-icon name="search" /> <QIcon name="search" />
</template> </template>
<template #append> <template #append>
<q-icon <QIcon
v-if="searchText !== ''" v-if="searchText !== ''"
name="close" name="close"
@click="searchText = ''" @click="searchText = ''"
class="cursor-pointer" class="cursor-pointer"
/> />
<q-icon v-if="props.info" name="info" class="cursor-info"> <QIcon v-if="props.info" name="info" class="cursor-info">
<q-tooltip>{{ props.info }}</q-tooltip> <QTooltip>{{ props.info }}</QTooltip>
</q-icon> </QIcon>
</template> </template>
</q-input> </QInput>
</q-form> </QForm>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@media screen and (max-width: $breakpoint-xs-max) { .q-field {
.q-field { width: 250px;
width: 250px;
}
} }
@media screen and (min-width: $breakpoint-xs-max) { @media screen and (min-width: $breakpoint-sm-max) {
.q-field { .q-field {
width: 400px; width: 400px;
} }

View File

@ -241,6 +241,7 @@ export default {
rmaList: 'RMA', rmaList: 'RMA',
summary: 'Summary', summary: 'Summary',
basicData: 'Basic Data', basicData: 'Basic Data',
lines: 'Lines',
rma: 'RMA', rma: 'RMA',
photos: 'Photos', photos: 'Photos',
log: 'Audit logs', log: 'Audit logs',

View File

@ -240,6 +240,7 @@ export default {
rmaList: 'RMA', rmaList: 'RMA',
summary: 'Resumen', summary: 'Resumen',
basicData: 'Datos básicos', basicData: 'Datos básicos',
lines: 'Líneas',
rma: 'RMA', rma: 'RMA',
photos: 'Fotos', photos: 'Fotos',
log: 'Registros de auditoría', log: 'Registros de auditoría',

View File

@ -6,11 +6,11 @@ const quasar = useQuasar();
</script> </script>
<template> <template>
<q-layout view="hHh LpR fFf"> <QLayout view="hHh LpR fFf">
<Navbar /> <Navbar />
<router-view></router-view> <RouterView></RouterView>
<q-footer v-if="quasar.platform.is.mobile"></q-footer> <QFooter v-if="quasar.platform.is.mobile"></QFooter>
</q-layout> </QLayout>
</template> </template>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -73,17 +73,17 @@ const statesFilter = {
}; };
</script> </script>
<template> <template>
<fetch-data <FetchData
url="Workers/activeWithInheritedRole" url="Workers/activeWithInheritedRole"
:filter="{ where: { role: 'salesPerson' } }" :filter="{ where: { role: 'salesPerson' } }"
@on-fetch="setWorkers" @on-fetch="setWorkers"
auto-load auto-load
/> />
<fetch-data url="ClaimStates" @on-fetch="setClaimStates" auto-load /> <FetchData url="ClaimStates" @on-fetch="setClaimStates" auto-load />
<div class="column items-center"> <div class="column items-center">
<q-card> <QCard>
<form-model <FormModel
:url="`Claims/${route.params.id}`" :url="`Claims/${route.params.id}`"
:filter="claimFilter" :filter="claimFilter"
model="claim" model="claim"
@ -91,48 +91,48 @@ const statesFilter = {
<template #form="{ data, validate, filter }"> <template #form="{ data, validate, filter }">
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-input <QInput
v-model="data.client.name" v-model="data.client.name"
:label="t('claim.basicData.customer')" :label="t('claim.basicData.customer')"
disable disable
/> />
</div> </div>
<div class="col"> <div class="col">
<q-input <QInput
v-model="data.created" v-model="data.created"
mask="####-##-##" mask="####-##-##"
fill-mask="_" fill-mask="_"
autofocus autofocus
> >
<template #append> <template #append>
<q-icon name="event" class="cursor-pointer"> <QIcon name="event" class="cursor-pointer">
<q-popup-proxy <QPopupProxy
cover cover
transition-show="scale" transition-show="scale"
transition-hide="scale" transition-hide="scale"
> >
<q-date <QDate
v-model="data.created" v-model="data.created"
mask="YYYY-MM-DD" mask="YYYY-MM-DD"
> >
<div class="row items-center justify-end"> <div class="row items-center justify-end">
<q-btn <QBtn
v-close-popup v-close-popup
label="Close" label="Close"
color="primary" color="primary"
flat flat
/> />
</div> </div>
</q-date> </QDate>
</q-popup-proxy> </QPopupProxy>
</q-icon> </QIcon>
</template> </template>
</q-input> </QInput>
</div> </div>
</div> </div>
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-select <QSelect
v-model="data.workerFk" v-model="data.workerFk"
:options="workers" :options="workers"
option-value="id" option-value="id"
@ -148,18 +148,18 @@ const statesFilter = {
:input-debounce="0" :input-debounce="0"
> >
<template #before> <template #before>
<q-avatar color="orange"> <QAvatar color="orange">
<q-img <QImg
v-if="data.workerFk" v-if="data.workerFk"
:src="`/api/Images/user/160x160/${data.workerFk}/download?access_token=${token}`" :src="`/api/Images/user/160x160/${data.workerFk}/download?access_token=${token}`"
spinner-color="white" spinner-color="white"
/> />
</q-avatar> </QAvatar>
</template> </template>
</q-select> </QSelect>
</div> </div>
<div class="col"> <div class="col">
<q-select <QSelect
v-model="data.claimStateFk" v-model="data.claimStateFk"
:options="claimStates" :options="claimStates"
option-value="id" option-value="id"
@ -174,19 +174,19 @@ const statesFilter = {
:rules="validate('claim.claimStateFk')" :rules="validate('claim.claimStateFk')"
:input-debounce="0" :input-debounce="0"
> >
</q-select> </QSelect>
</div> </div>
</div> </div>
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-input <QInput
v-model="data.packages" v-model="data.packages"
:label="t('claim.basicData.packages')" :label="t('claim.basicData.packages')"
:rules="validate('claim.packages')" :rules="validate('claim.packages')"
/> />
</div> </div>
<div class="col"> <div class="col">
<q-input <QInput
v-model="data.rma" v-model="data.rma"
:label="t('claim.basicData.returnOfMaterial')" :label="t('claim.basicData.returnOfMaterial')"
:rules="validate('claim.rma')" :rules="validate('claim.rma')"
@ -195,15 +195,15 @@ const statesFilter = {
</div> </div>
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-checkbox <QCheckbox
v-model="data.hasToPickUp" v-model="data.hasToPickUp"
:label="t('claim.basicData.picked')" :label="t('claim.basicData.picked')"
/> />
</div> </div>
</div> </div>
</template> </template>
</form-model> </FormModel>
</q-card> </QCard>
</div> </div>
</template> </template>

View File

@ -17,18 +17,18 @@ const { t } = useI18n();
:info="t('You can search by claim id or customer name')" :info="t('You can search by claim id or customer name')"
/> />
</Teleport> </Teleport>
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<q-scroll-area class="fit"> <QScrollArea class="fit">
<claim-descriptor /> <ClaimDescriptor />
<q-separator /> <QSeparator />
<left-menu source="card" /> <LeftMenu source="card" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<q-page class="q-pa-md"> <QPage class="q-pa-md">
<router-view></router-view> <RouterView></RouterView>
</q-page> </QPage>
</q-page-container> </QPageContainer>
</template> </template>
<i18n> <i18n>

View File

@ -47,77 +47,77 @@ function stateColor(code) {
</script> </script>
<template> <template>
<card-descriptor <CardDescriptor
ref="descriptor" ref="descriptor"
:url="`Claims/${entityId}`" :url="`Claims/${entityId}`"
:filter="filter" :filter="filter"
module="Claim" module="Claim"
> >
<template #menu="{ entity }"> <template #menu="{ entity }">
<claim-descriptor-menu :claim="entity" /> <ClaimDescriptorMenu :claim="entity" />
</template> </template>
<template #description="{ entity }"> <template #description="{ entity }">
<span> <span>
{{ entity.client.name }} {{ entity.client.name }}
<q-tooltip>{{ entity.client.name }}</q-tooltip> <QTooltip>{{ entity.client.name }}</QTooltip>
</span> </span>
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<q-list> <QList>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ t('claim.card.created') }}</q-item-label> <QItemLabel caption>{{ t('claim.card.created') }}</QItemLabel>
<q-item-label>{{ toDate(entity.created) }}</q-item-label> <QItemLabel>{{ toDate(entity.created) }}</QItemLabel>
</q-item-section> </QItemSection>
<q-item-section v-if="entity.claimState"> <QItemSection v-if="entity.claimState">
<q-item-label caption>{{ t('claim.card.state') }}</q-item-label> <QItemLabel caption>{{ t('claim.card.state') }}</QItemLabel>
<q-item-label> <QItemLabel>
<q-chip :color="stateColor(entity.claimState.code)" dense> <QBadge :color="stateColor(entity.claimState.code)" dense>
{{ entity.claimState.description }} {{ entity.claimState.description }}
</q-chip> </QBadge>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('claim.card.ticketId') }} {{ t('claim.card.ticketId') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
<span class="link"> <span class="link">
{{ entity.ticketFk }} {{ entity.ticketFk }}
<TicketDescriptorProxy :id="entity.ticketFk" /> <TicketDescriptorProxy :id="entity.ticketFk" />
</span> </span>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section v-if="entity.worker"> <QItemSection v-if="entity.worker">
<q-item-label caption> <QItemLabel caption>
{{ t('claim.card.assignedTo') }} {{ t('claim.card.assignedTo') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.worker.user.name }}</q-item-label> <QItemLabel>{{ entity.worker.user.name }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
<q-card-actions> <QCardActions>
<q-btn <QBtn
size="md" size="md"
icon="vn:client" icon="vn:client"
color="primary" color="primary"
:to="{ name: 'CustomerCard', params: { id: entity.clientFk } }" :to="{ name: 'CustomerCard', params: { id: entity.clientFk } }"
> >
<q-tooltip>{{ t('claim.card.customerSummary') }}</q-tooltip> <QTooltip>{{ t('claim.card.customerSummary') }}</QTooltip>
</q-btn> </QBtn>
<q-btn <QBtn
size="md" size="md"
icon="vn:ticket" icon="vn:ticket"
color="primary" color="primary"
:to="{ name: 'TicketCard', params: { id: entity.ticketFk } }" :to="{ name: 'TicketCard', params: { id: entity.ticketFk } }"
> >
<q-tooltip>{{ t('claim.card.claimedTicket') }}</q-tooltip> <QTooltip>{{ t('claim.card.claimedTicket') }}</QTooltip>
</q-btn> </QBtn>
</q-card-actions> </QCardActions>
</template> </template>
</card-descriptor> </CardDescriptor>
</template> </template>

View File

@ -69,43 +69,43 @@ async function remove() {
await axios.delete(`Claims/${id}`); await axios.delete(`Claims/${id}`);
quasar.notify({ quasar.notify({
message: t('globals.dataDeleted'), message: t('globals.dataDeleted'),
type: 'positive' type: 'positive',
}); });
} }
</script> </script>
<template> <template>
<q-item v-ripple clickable> <QItem v-ripple clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="summarize" /> <QIcon name="summarize" />
</q-item-section> </QItemSection>
<q-item-section>{{ t('pickupOrder') }}</q-item-section> <QItemSection>{{ t('pickupOrder') }}</QItemSection>
<q-item-section side> <QItemSection side>
<q-icon name="keyboard_arrow_right" /> <QIcon name="keyboard_arrow_right" />
</q-item-section> </QItemSection>
<q-menu anchor="top end" self="top start" auto-close> <QMenu anchor="top end" self="top start" auto-close>
<q-list> <QList>
<q-item @click="openPickupOrder" v-ripple clickable> <QItem @click="openPickupOrder" v-ripple clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="picture_as_pdf" /> <QIcon name="picture_as_pdf" />
</q-item-section> </QItemSection>
<q-item-section>{{ t('openPickupOrder') }}</q-item-section> <QItemSection>{{ t('openPickupOrder') }}</QItemSection>
</q-item> </QItem>
<q-item @click="confirmPickupOrder" v-ripple clickable> <QItem @click="confirmPickupOrder" v-ripple clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="send" /> <QIcon name="send" />
</q-item-section> </QItemSection>
<q-item-section>{{ t('sendPickupOrder') }}</q-item-section> <QItemSection>{{ t('sendPickupOrder') }}</QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-menu> </QMenu>
</q-item> </QItem>
<q-separator /> <QSeparator />
<q-item @click="confirmRemove()" v-ripple clickable> <QItem @click="confirmRemove()" v-ripple clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="delete" /> <QIcon name="delete" />
</q-item-section> </QItemSection>
<q-item-section>{{ t('deleteClaim') }}</q-item-section> <QItemSection>{{ t('deleteClaim') }}</QItemSection>
</q-item> </QItem>
</template> </template>
<i18n> <i18n>

View File

@ -0,0 +1,440 @@
<script setup>
import axios from 'axios';
import { ref, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { useRoute } from 'vue-router';
import { useArrayData } from 'composables/useArrayData';
import { useStateStore } from 'stores/useStateStore';
import VnPaginate from 'components/ui/VnPaginate.vue';
import FetchData from 'components/FetchData.vue';
import VnConfirm from 'components/ui/VnConfirm.vue';
import { toDate, toCurrency, toPercentage } from 'filters/index';
import VnDiscount from 'components/common/vnDiscount.vue';
import ClaimLinesImport from './ClaimLinesImport.vue';
const quasar = useQuasar();
const route = useRoute();
const { t } = useI18n();
const stateStore = useStateStore();
const arrayData = useArrayData('ClaimLines');
const store = arrayData.store;
const claimFilter = {
fields: ['ticketFk'],
};
const linesFilter = {
include: {
relation: 'sale',
scope: {
fields: ['concept', 'ticketFk', 'price', 'quantity', 'discount', 'itemFk'],
include: {
relation: 'ticket',
},
},
},
};
const claim = ref(null);
async function onFetchClaim(data) {
claim.value = data;
fetchMana();
}
const amount = ref(0);
const amountClaimed = ref(0);
async function onFetch(rows) {
amount.value = rows.reduce(
(acumulator, { sale }) => acumulator + sale.price * sale.quantity,
0
);
amountClaimed.value = rows.reduce(
(acumulator, line) => acumulator + line.sale.price * line.quantity,
0
);
}
const columns = computed(() => [
{
name: 'dated',
label: t('Delivered'),
field: ({ sale: { ticket } }) => toDate(ticket.landed),
sortable: true,
},
{
name: 'quantity',
label: t('Quantity'),
field: ({ sale }) => sale.quantity,
sortable: true,
},
{
name: 'claimed',
label: t('Claimed'),
field: (row) => row.quantity,
sortable: true,
},
{
name: 'description',
label: t('Description'),
field: ({ sale }) => sale.concept,
},
{
name: 'price',
label: t('Price'),
field: ({ sale }) => sale.price,
format: (value) => toCurrency(value),
sortable: true,
},
{
name: 'discount',
label: t('Discount'),
field: ({ sale }) => sale.discount,
format: (value) => toPercentage(value / 100),
sortable: true,
},
{
name: 'total',
label: t('Total'),
field: ({ sale }) => {
const amount = sale.price * sale.quantity;
const appliedDiscount = (sale.discount * amount) / 100;
return amount - appliedDiscount;
},
format: (value) => toCurrency(value),
sortable: true,
},
]);
const selected = ref([]);
const mana = ref(0);
async function fetchMana() {
const ticketId = claim.value.ticketFk;
const response = await axios.get(`Tickets/${ticketId}/getSalesPersonMana`);
mana.value = response.data;
}
async function updateQuantity({ id, quantity }) {
if (!id) return;
await axios.patch(`ClaimBeginnings/${id}`, { quantity });
}
async function updateDiscount({ saleFk, discount, canceller }) {
const body = { salesIds: [saleFk], newDiscount: discount };
const claimId = claim.value.ticketFk;
const query = `Tickets/${claimId}/updateDiscount`;
await axios.post(query, body, {
signal: canceller.signal,
});
}
function onUpdateDiscount(response) {
const row = store.data[response.rowIndex];
row.sale.discount = response.discount;
quasar.notify({
message: t('Discount updated'),
type: 'positive',
});
}
async function confirmRemove() {
const rows = selected.value;
const count = rows.length;
if (count === 0) {
return quasar.notify({
message: 'You must select at least one row',
type: 'warning',
});
}
quasar
.dialog({
component: VnConfirm,
componentProps: {
title: t('Delete claimed sales'),
message: t('You are about to remove {count} rows', count, { count }),
data: { rows },
promise: remove,
},
})
.onOk(() => {
for (const row of rows) {
const orgData = store.data;
const index = orgData.findIndex((item) => item.id === row.id);
store.data.splice(index, 1);
selected.value = [];
}
});
}
async function remove({ rows }) {
if (!rows.length) return;
const body = { deletes: rows.map((row) => row.id) };
await axios.post(`ClaimBeginnings/crud`, body);
quasar.notify({
type: 'positive',
message: t('globals.rowRemoved'),
});
}
function showImportDialog() {
quasar
.dialog({
component: ClaimLinesImport,
})
.onOk(() => arrayData.refresh());
}
</script>
<template>
<QPageSticky position="top" :offset="[0, 0]" expand>
<QToolbar class="bg-dark text-white">
<QToolbarTitle> {{ t('Claimed lines') }} </QToolbarTitle>
<QSpace />
<div class="row q-gutter-md">
<div>
{{ t('Amount') }}
<QChip :dense="$q.screen.lt.sm">
{{ toCurrency(amount) }}
</QChip>
</div>
<QSeparator dark vertical />
<div>
{{ t('Amount Claimed') }}
<QChip color="positive" :dense="$q.screen.lt.sm">
{{ toCurrency(amountClaimed) }}
</QChip>
</div>
</div>
</QToolbar>
</QPageSticky>
<FetchData
:url="`Claims/${route.params.id}`"
:filter="claimFilter"
@on-fetch="onFetchClaim"
auto-load
/>
<div class="column items-center">
<div class="list">
<VnPaginate
data-key="ClaimLines"
:url="`Claims/${route.params.id}/lines`"
:filter="linesFilter"
@on-fetch="onFetch"
auto-load
>
<template #body="{ rows }">
<QTable
:columns="columns"
:rows="rows"
:dense="$q.screen.lt.md"
:pagination="{ rowsPerPage: 0 }"
row-key="id"
selection="multiple"
v-model:selected="selected"
hide-pagination
:grid="$q.screen.lt.md"
>
<template #body-cell-claimed="{ row, value }">
<QTd auto-width align="right" class="text-primary">
<span>{{ value }}</span>
<QPopupEdit
v-model="row.quantity"
v-slot="scope"
:title="t('Claimed quantity')"
@update:model-value="updateQuantity(row)"
buttons
>
<QInput
v-model="scope.value"
type="number"
dense
autofocus
@keyup.enter="scope.set"
@focus="($event) => $event.target.select()"
/>
</QPopupEdit>
</QTd>
</template>
<template #body-cell-discount="{ row, value, rowIndex }">
<QTd auto-width align="right" class="text-primary">
{{ value }}
<VnDiscount
:quantity="row.quantity"
:price="row.sale.price"
:discount="row.sale.discount"
:mana="mana"
:promise="updateDiscount"
:data="{ saleFk: row.sale.id, rowIndex: rowIndex }"
@on-update="onUpdateDiscount"
/>
</QTd>
</template>
<!-- View for grid mode -->
<template #item="props">
<div
class="q-mb-md col-12 grid-style-transition"
:style="props.selected ? 'transform: scale(0.95);' : ''"
>
<QCard>
<QCardSection>
<QCheckbox v-model="props.selected" />
</QCardSection>
<QSeparator inset />
<QList dense>
<QItem
v-for="column of props.cols"
:key="column.name"
>
<QItemSection>
<QItemLabel caption>
{{ column.label }}
</QItemLabel>
</QItemSection>
<QItemSection side>
<template
v-if="column.name === 'claimed'"
>
<QItemLabel class="text-primary">
{{ column.value }}
<QPopupEdit
v-model="props.row.quantity"
v-slot="scope"
:title="t('Claimed quantity')"
@update:model-value="
updateQuantity(props.row)
"
buttons
>
<QInput
v-model="scope.value"
type="number"
dense
autofocus
@keyup.enter="scope.set"
@focus="
($event) =>
$event.target.select()
"
/>
</QPopupEdit>
</QItemLabel>
</template>
<template
v-else-if="column.name === 'discount'"
>
<QItemLabel class="text-primary">
{{ column.value }}
<VnDiscount
:quantity="props.row.quantity"
:price="props.row.sale.price"
:discount="
props.row.sale.discount
"
:mana="mana"
:promise="updateDiscount"
:data="{
saleFk: props.row.sale.id,
rowIndex: props.rowIndex,
}"
@on-update="onUpdateDiscount"
/>
</QItemLabel>
</template>
<template v-else>
<QItemLabel>
{{ column.value }}
</QItemLabel>
</template>
</QItemSection>
</QItem>
</QList>
</QCard>
</div>
</template>
</QTable>
</template>
</VnPaginate>
</div>
</div>
<Teleport
v-if="stateStore.isHeaderMounted() && !$q.screen.lt.sm"
to="#actions-prepend"
>
<div class="row q-gutter-x-sm">
<QBtn
v-if="selected.length > 0"
@click="confirmRemove"
icon="delete"
color="primary"
flat
dense
rounded
>
<QTooltip bottom> {{ t('globals.remove') }} </QTooltip>
</QBtn>
<QBtn @click="showImportDialog" icon="add" color="primary" flat dense rounded>
<QTooltip bottom> {{ t('globals.add') }} </QTooltip>
</QBtn>
<QSeparator vertical />
</div>
</Teleport>
<!-- v-if="quasar.platform.is.mobile" -->
<QPageSticky v-if="$q.screen.lt.sm" position="bottom" :offset="[0, 0]" expand>
<QToolbar class="bg-primary text-white q-pa-none">
<QTabs class="full-width" align="justify" inline-label narrow-indicator>
<QTab @click="showImportDialog" icon="add" :label="t('globals.add')" />
<QSeparator vertical inset />
<QTab
@click="confirmRemove"
icon="delete"
:label="t('globals.remove')"
:disable="selected.length === 0"
/>
</QTabs>
</QToolbar>
</QPageSticky>
</template>
<style lang="scss" scoped>
.list {
padding-top: 50px;
max-width: 900px;
width: 100%;
}
.grid-style-transition {
transition: transform 0.28s, background-color 0.28s;
}
</style>
<i18n>
en:
You are about to remove {count} rows: '
You are about to remove <strong>{count}</strong> row |
You are about to remove <strong>{count}</strong> rows'
es:
Claimed lines: Líneas reclamadas
Delivered: Entregado
Quantity: Cantidad
Claimed: Reclamada
Description: Descripción
Price: Precio
Discount: Descuento
Actions: Acciones
Amount: Total
Amount Claimed: Cantidad reclamada
Delete claimed sales: Eliminar ventas reclamadas
Discount updated: Descuento actualizado
Claimed quantity: Cantidad reclamada
You are about to remove {count} rows: '
Vas a eliminar <strong>{count}</strong> línea |
Vas a eliminar <strong>{count}</strong> líneas'
</i18n>

View File

@ -0,0 +1,182 @@
<script setup>
import { ref, computed } from 'vue';
import { useQuasar, useDialogPluginComponent } from 'quasar';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import { toDate, toCurrency, toPercentage } from 'filters/index';
import axios from 'axios';
defineEmits([...useDialogPluginComponent.emits]);
const { dialogRef, onDialogOK, onDialogCancel } = useDialogPluginComponent();
const route = useRoute();
const quasar = useQuasar();
const { t } = useI18n();
const columns = computed(() => [
{
name: 'delivered',
label: t('Delivered'),
field: (row) => row.landed,
format: (value) => toDate(value),
sortable: true,
},
{
name: 'quantity',
label: t('Quantity'),
field: (row) => row.quantity,
sortable: true,
},
{
name: 'description',
label: t('Description'),
field: (row) => row.concept,
},
{
name: 'price',
label: t('Price'),
field: (row) => row.price,
format: (value) => toCurrency(value),
sortable: true,
},
{
name: 'discount',
label: t('Discount'),
field: (row) => row.discount,
format: (value) => toPercentage(value),
sortable: true,
},
]);
const selected = ref([]);
const claimableSales = ref([]);
const isLoading = ref(false);
let canceller;
async function importLines() {
const sales = selected.value;
if (!sales.length) {
return quasar.notify({
message: 'You must select at least one',
type: 'warning',
});
}
const body = sales.map((row) => ({
claimFk: route.params.id,
saleFk: row.saleFk,
quantity: row.quantity,
}));
canceller = new AbortController();
isLoading.value = true;
const { data } = await axios.post('ClaimBeginnings', body, {
signal: canceller.signal,
});
quasar.notify({
message: 'Lines added to claim',
type: 'positive',
});
onDialogOK(data);
canceller = null;
isLoading.value = false;
}
function cancel() {
if (canceller) {
canceller.abort();
canceller = null;
}
onDialogCancel();
}
</script>
<template>
<FetchData
url="Sales/getClaimableFromTicket?ticketFk=16"
@on-fetch="(data) => (claimableSales = data)"
auto-load
/>
<QDialog ref="dialogRef" persistent>
<QCard>
<QCardSection class="row items-center">
<span class="text-h6 text-grey">{{ t('Available sales lines') }}</span>
<QSpace />
<QBtn icon="close" flat round dense v-close-popup />
</QCardSection>
<QTable
class="my-sticky-header-table"
:columns="columns"
:rows="claimableSales"
:pagination="{ rowsPerPage: 10 }"
row-key="saleFk"
selection="multiple"
v-model:selected="selected"
square
flat
/>
<QSeparator />
<QCardActions align="right">
<QBtn :label="t('globals.cancel')" color="primary" flat @click="cancel" />
<QBtn
:label="t('globals.confirm')"
color="primary"
:loading="isLoading"
@click="importLines"
unelevated
/>
</QCardActions>
</QCard>
</QDialog>
</template>
<style lang="scss" scoped>
.q-card {
max-width: 800px;
}
</style>
<style lang="scss">
.my-sticky-header-table {
height: 400px;
thead tr th {
position: sticky;
z-index: 1;
}
thead tr:first-child th {
/* this is when the loading indicator appears */
top: 0;
}
&.q-table--loading thead tr:last-child th {
/* height of all previous header rows */
top: 48px;
}
// /* prevent scrolling behind sticky top row on focus */
tbody {
/* height of all previous header rows */
scroll-margin-top: 48px;
}
}
</style>
<i18n>
es:
Available sales lines: Líneas de venta disponibles
Delivered: Entrega
Quantity: Cantidad
Description: Descripción
Price: Precio
Discount: Descuento
Lines added to claim: Lineas añadidas a la reclamación
You must select at least one: Debes seleccionar al menos una
</i18n>

View File

@ -3,7 +3,7 @@ import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useSession } from 'src/composables/useSession'; import { useSession } from 'src/composables/useSession';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import Paginate from 'src/components/PaginateData.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import ClaimLogFilter from './ClaimLogFilter.vue'; import ClaimLogFilter from './ClaimLogFilter.vue';
import { toDate } from 'src/filters'; import { toDate } from 'src/filters';
@ -57,9 +57,9 @@ function actionColor(action) {
</script> </script>
<template> <template>
<div class="column items-center"> <div class="column items-center">
<q-timeline class="q-pa-md"> <QTimeline class="q-pa-md">
<q-timeline-entry heading tag="h4"> {{ t('Audit logs') }} </q-timeline-entry> <QTimelineEntry heading tag="h4"> {{ t('Audit logs') }} </QTimelineEntry>
<Paginate <VnPaginate
data-key="ClaimLogs" data-key="ClaimLogs"
:url="`Claims/${route.params.id}/logs`" :url="`Claims/${route.params.id}/logs`"
order="id DESC" order="id DESC"
@ -69,7 +69,7 @@ function actionColor(action) {
> >
<template #body="{ rows }"> <template #body="{ rows }">
<template v-for="log of rows" :key="log.id"> <template v-for="log of rows" :key="log.id">
<q-timeline-entry <QTimelineEntry
:avatar="`/api/Images/user/160x160/${log.userFk}/download?access_token=${token}`" :avatar="`/api/Images/user/160x160/${log.userFk}/download?access_token=${token}`"
> >
<template #subtitle> <template #subtitle>
@ -82,12 +82,12 @@ function actionColor(action) {
}} }}
</template> </template>
<template #title> <template #title>
<q-chip :color="actionColor(log.action)"> <QChip :color="actionColor(log.action)">
{{ t(`actions.${log.action}`) }} {{ t(`actions.${log.action}`) }}
</q-chip> </QChip>
{{ t(`models.${log.model}`) }} {{ t(`models.${log.model}`) }}
</template> </template>
<q-table <QTable
:rows="log.changes" :rows="log.changes"
:columns="columns" :columns="columns"
row-key="property" row-key="property"
@ -96,37 +96,37 @@ function actionColor(action) {
flat flat
> >
<template #header="props"> <template #header="props">
<q-tr :props="props"> <QTr :props="props">
<q-th <QTh
v-for="col in props.cols" v-for="col in props.cols"
:key="col.name" :key="col.name"
:props="props" :props="props"
> >
{{ t(col.label) }} {{ t(col.label) }}
</q-th> </QTh>
</q-tr> </QTr>
</template> </template>
</q-table> </QTable>
</q-timeline-entry> </QTimelineEntry>
</template> </template>
</template> </template>
</Paginate> </VnPaginate>
</q-timeline> </QTimeline>
</div> </div>
<Teleport v-if="stateStore.isHeaderMounted()" to="#actions-append"> <Teleport v-if="stateStore.isHeaderMounted()" to="#actions-append">
<div class="row q-gutter-x-sm"> <div class="row q-gutter-x-sm">
<q-btn flat @click="stateStore.toggleRightDrawer()" round dense icon="menu"> <QBtn flat @click="stateStore.toggleRightDrawer()" round dense icon="menu">
<q-tooltip bottom anchor="bottom right"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</div> </div>
</Teleport> </Teleport>
<q-drawer v-model="stateStore.rightDrawer" show-if-above side="right" :width="300"> <QDrawer v-model="stateStore.rightDrawer" show-if-above side="right" :width="300">
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<ClaimLogFilter data-key="ClaimLogs" /> <ClaimLogFilter data-key="ClaimLogs" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -16,7 +16,7 @@ const workers = ref();
</script> </script>
<template> <template>
<fetch-data <FetchData
url="Workers/activeWithInheritedRole" url="Workers/activeWithInheritedRole"
:filter="{ where: { role: 'salesPerson' } }" :filter="{ where: { role: 'salesPerson' } }"
@on-fetch="(data) => (workers = data)" @on-fetch="(data) => (workers = data)"
@ -30,22 +30,22 @@ const workers = ref();
</div> </div>
</template> </template>
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<q-date <QDate
v-model="params.created" v-model="params.created"
@update:model-value="searchFn()" @update:model-value="searchFn()"
dense dense
flat flat
minimal minimal
> >
</q-date> </QDate>
<q-list dense> <QList dense>
<q-separator /> <QSeparator />
<q-item> <QItem>
<q-item-section v-if="!workers"> <QItemSection v-if="!workers">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="workers"> <QItemSection v-if="workers">
<q-select <QSelect
:label="t('User')" :label="t('User')"
v-model="params.userFk" v-model="params.userFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -57,9 +57,9 @@ const workers = ref();
use-input use-input
:input-debounce="0" :input-debounce="0"
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>

View File

@ -147,7 +147,7 @@ function onDrag() {
} }
</script> </script>
<template> <template>
<fetch-data <FetchData
url="Claims" url="Claims"
:filter="claimDmsFilter" :filter="claimDmsFilter"
@on-fetch="([data]) => setClaimDms(data)" @on-fetch="([data]) => setClaimDms(data)"
@ -155,13 +155,13 @@ function onDrag() {
auto-load auto-load
ref="claimDmsRef" ref="claimDmsRef"
/> />
<fetch-data <FetchData
url="DmsTypes/findOne" url="DmsTypes/findOne"
:filter="{ where: { code: 'claim' } }" :filter="{ where: { code: 'claim' } }"
@on-fetch="(data) => (dmsType = data)" @on-fetch="(data) => (dmsType = data)"
auto-load auto-load
/> />
<fetch-data <FetchData
url="UserConfigs/getUserConfig" url="UserConfigs/getUserConfig"
@on-fetch="(data) => (config = data)" @on-fetch="(data) => (config = data)"
auto-load auto-load
@ -176,7 +176,7 @@ function onDrag() {
class="flex flex-center items-center text-grey q-mt-md column" class="flex flex-center items-center text-grey q-mt-md column"
v-if="dragFile" v-if="dragFile"
> >
<q-icon size="xl" name="file_download" /> <QIcon size="xl" name="file_download" />
<h5> <h5>
{{ t('claim.photo.dragDrop') }} {{ t('claim.photo.dragDrop') }}
</h5> </h5>
@ -186,8 +186,8 @@ function onDrag() {
v-if="!claimDms?.length && !dragFile" v-if="!claimDms?.length && !dragFile"
@click="inputFile.nativeEl.click()" @click="inputFile.nativeEl.click()"
> >
<q-icon size="xl" name="image"></q-icon> <QIcon size="xl" name="image"></QIcon>
<q-icon size="xl" name="movie"></q-icon> <QIcon size="xl" name="movie"></QIcon>
<h5> <h5>
{{ t('claim.photo.noData') }} {{ t('claim.photo.noData') }}
</h5> </h5>
@ -198,7 +198,7 @@ function onDrag() {
:key="index" :key="index"
class="relative-position" class="relative-position"
> >
<q-btn <QBtn
icon="delete" icon="delete"
color="primary" color="primary"
text-color="white" text-color="white"
@ -207,7 +207,7 @@ function onDrag() {
@click.stop="viewDeleteDms(index)" @click.stop="viewDeleteDms(index)"
round round
/> />
<q-icon <QIcon
name="play_circle" name="play_circle"
color="primary" color="primary"
size="xl" size="xl"
@ -215,16 +215,16 @@ function onDrag() {
v-if="media.isVideo" v-if="media.isVideo"
@click.stop="openDialog(media.dmsFk)" @click.stop="openDialog(media.dmsFk)"
> >
<q-tooltip>Video</q-tooltip> <QTooltip>Video</QTooltip>
</q-icon> </QIcon>
<q-card class="multimedia relative-position"> <QCard class="multimedia relative-position">
<q-img <QImg
:src="media.url" :src="media.url"
class="rounded-borders cursor-pointer fit" class="rounded-borders cursor-pointer fit"
@click="openDialog(media.dmsFk)" @click="openDialog(media.dmsFk)"
v-if="!media.isVideo" v-if="!media.isVideo"
> >
</q-img> </QImg>
<video <video
:src="media.url" :src="media.url"
class="rounded-borders cursor-pointer fit" class="rounded-borders cursor-pointer fit"
@ -232,7 +232,7 @@ function onDrag() {
v-if="media.isVideo" v-if="media.isVideo"
@click="openDialog(media.dmsFk)" @click="openDialog(media.dmsFk)"
/> />
</q-card> </QCard>
</div> </div>
</div> </div>
</div> </div>
@ -243,14 +243,14 @@ function onDrag() {
> >
<div class="row q-gutter-x-sm"> <div class="row q-gutter-x-sm">
<label for="fileInput"> <label for="fileInput">
<q-btn <QBtn
@click="inputFile.nativeEl.click()" @click="inputFile.nativeEl.click()"
icon="add" icon="add"
color="primary" color="primary"
dense dense
rounded rounded
> >
<q-input <QInput
ref="inputFile" ref="inputFile"
type="file" type="file"
style="display: none" style="display: none"
@ -258,27 +258,27 @@ function onDrag() {
v-model="files" v-model="files"
@update:model-value="create()" @update:model-value="create()"
/> />
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip> <QTooltip bottom> {{ t('globals.add') }} </QTooltip>
</q-btn> </QBtn>
</label> </label>
<q-separator vertical /> <QSeparator vertical />
</div> </div>
</Teleport> </Teleport>
<q-page-sticky <QPageSticky
v-if="quasar.platform.is.mobile" v-if="quasar.platform.is.mobile"
position="bottom" position="bottom"
:offset="[0, 0]" :offset="[0, 0]"
expand expand
> >
<q-toolbar class="bg-primary text-white q-pa-none"> <QToolbar class="bg-primary text-white q-pa-none">
<q-tabs class="full-width" align="justify" inline-label narrow-indicator> <QTabs class="full-width" align="justify" inline-label narrow-indicator>
<q-tab <QTab
@click="inputFile.nativeEl.click()" @click="inputFile.nativeEl.click()"
icon="add_circle" icon="add_circle"
:label="t('globals.add')" :label="t('globals.add')"
> >
<q-input <QInput
ref="inputFile" ref="inputFile"
type="file" type="file"
style="display: none" style="display: none"
@ -286,29 +286,29 @@ function onDrag() {
v-model="files" v-model="files"
@update:model-value="create()" @update:model-value="create()"
/> />
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip> <QTooltip bottom> {{ t('globals.add') }} </QTooltip>
</q-tab> </QTab>
</q-tabs> </QTabs>
</q-toolbar> </QToolbar>
</q-page-sticky> </QPageSticky>
<!-- MULTIMEDIA DIALOG START--> <!-- MULTIMEDIA DIALOG START-->
<q-dialog <QDialog
v-model="multimediaDialog" v-model="multimediaDialog"
transition-show="slide-up" transition-show="slide-up"
transition-hide="slide-down" transition-hide="slide-down"
> >
<q-toolbar class="absolute zindex close-button"> <QToolbar class="absolute zindex close-button">
<q-space /> <QSpace />
<q-btn icon="close" color="primary" round dense v-close-popup /> <QBtn icon="close" color="primary" round dense v-close-popup />
</q-toolbar> </QToolbar>
<q-carousel swipeable animated v-model="multimediaSlide" arrows class="fit"> <QCarousel swipeable animated v-model="multimediaSlide" arrows class="fit">
<q-carousel-slide <QCarouselSlide
v-for="media of claimDms" v-for="media of claimDms"
:key="media.dmsFk" :key="media.dmsFk"
:name="media.dmsFk" :name="media.dmsFk"
> >
<q-img <QImg
:src="media.url" :src="media.url"
class="fit" class="fit"
fit="scale-down" fit="scale-down"
@ -317,9 +317,9 @@ function onDrag() {
<video class="q-ma-none fit" v-if="media.isVideo" controls muted autoplay> <video class="q-ma-none fit" v-if="media.isVideo" controls muted autoplay>
<source :src="media.url" type="video/mp4" /> <source :src="media.url" type="video/mp4" />
</video> </video>
</q-carousel-slide> </QCarouselSlide>
</q-carousel> </QCarousel>
</q-dialog> </QDialog>
<!-- MULTIMEDIA DIALOG END--> <!-- MULTIMEDIA DIALOG END-->
</template> </template>

View File

@ -6,7 +6,7 @@ import { useQuasar } from 'quasar';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import Paginate from 'src/components/PaginateData.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
@ -86,7 +86,7 @@ async function remove({ id }) {
} }
</script> </script>
<template> <template>
<fetch-data <FetchData
:url="`Claims/${route.params.id}`" :url="`Claims/${route.params.id}`"
:filter="claimFilter" :filter="claimFilter"
@on-fetch="onFetch" @on-fetch="onFetch"
@ -94,56 +94,56 @@ async function remove({ id }) {
/> />
<div class="column items-center"> <div class="column items-center">
<div class="list"> <div class="list">
<paginate data-key="ClaimRma" url="ClaimRmas"> <VnPaginate data-key="ClaimRma" url="ClaimRmas">
<template #body="{ rows }"> <template #body="{ rows }">
<q-card class="card"> <QCard class="card">
<template v-for="(row, index) of rows" :key="row.id"> <template v-for="(row, index) of rows" :key="row.id">
<q-item class="q-pa-none items-start"> <QItem class="q-pa-none items-start">
<q-item-section class="q-pa-md"> <QItemSection class="q-pa-md">
<q-list> <QList>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('claim.rma.user') }} {{ t('claim.rma.user') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ row.worker.user.name }} {{ row.worker.user.name }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('claim.rma.created') }} {{ t('claim.rma.created') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ {{
toDate(row.created, { toDate(row.created, {
timeStyle: 'medium', timeStyle: 'medium',
}) })
}} }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-item-section> </QItemSection>
<q-card-actions vertical class="justify-between"> <QCardActions vertical class="justify-between">
<q-btn <QBtn
flat flat
round round
color="orange" color="orange"
icon="vn:bin" icon="vn:bin"
@click="confirmRemove(row.id)" @click="confirmRemove(row.id)"
> >
<q-tooltip>{{ t('globals.remove') }}</q-tooltip> <QTooltip>{{ t('globals.remove') }}</QTooltip>
</q-btn> </QBtn>
</q-card-actions> </QCardActions>
</q-item> </QItem>
<q-separator v-if="index !== rows.length - 1" /> <QSeparator v-if="index !== rows.length - 1" />
</template> </template>
</q-card> </QCard>
</template> </template>
</paginate> </VnPaginate>
</div> </div>
</div> </div>
@ -152,25 +152,25 @@ async function remove({ id }) {
to="#actions-prepend" to="#actions-prepend"
> >
<div class="row q-gutter-x-sm"> <div class="row q-gutter-x-sm">
<q-btn @click="addRow()" icon="add" color="primary" dense rounded> <QBtn @click="addRow()" icon="add" color="primary" dense rounded>
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip> <QTooltip bottom> {{ t('globals.add') }} </QTooltip>
</q-btn> </QBtn>
<q-separator vertical /> <QSeparator vertical />
</div> </div>
</Teleport> </Teleport>
<q-page-sticky <QPageSticky
v-if="quasar.platform.is.mobile" v-if="quasar.platform.is.mobile"
position="bottom" position="bottom"
:offset="[0, 0]" :offset="[0, 0]"
expand expand
> >
<q-toolbar class="bg-primary text-white q-pa-none"> <QToolbar class="bg-primary text-white q-pa-none">
<q-tabs class="full-width" align="justify" inline-label narrow-indicator> <QTabs class="full-width" align="justify" inline-label narrow-indicator>
<q-tab @click="addRow()" icon="add_circle" :label="t('globals.add')" /> <QTab @click="addRow()" icon="add_circle" :label="t('globals.add')" />
</q-tabs> </QTabs>
</q-toolbar> </QToolbar>
</q-page-sticky> </QPageSticky>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -79,87 +79,83 @@ function stateColor(code) {
</script> </script>
<template> <template>
<card-summary ref="summary" :url="`Claims/${entityId}/getSummary`"> <CardSummary ref="summary" :url="`Claims/${entityId}/getSummary`">
<template #header="{ entity: { claim } }"> <template #header="{ entity: { claim } }">
{{ claim.id }} - {{ claim.client.name }} {{ claim.id }} - {{ claim.client.name }}
</template> </template>
<template #body="{ entity: { claim, salesClaimed } }"> <template #body="{ entity: { claim, salesClaimed } }">
<q-card-section class="row q-pa-none q-col-gutter-md"> <QCardSection class="row q-pa-none q-col-gutter-md">
<div class="col"> <div class="col">
<q-list> <QList>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('claim.summary.created') }} {{ t('claim.summary.created') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ toDate(claim.created) }}</q-item-label> <QItemLabel>{{ toDate(claim.created) }}</QItemLabel>
</q-item-section> </QItemSection>
<q-item-section v-if="claim.claimState"> <QItemSection v-if="claim.claimState">
<q-item-label caption>{{ <QItemLabel caption>
t('claim.summary.state') {{ t('claim.summary.state') }}
}}</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
<q-chip <QChip
:color="stateColor(claim.claimState.code)" :color="stateColor(claim.claimState.code)"
dense dense
> >
{{ claim.claimState.description }} {{ claim.claimState.description }}
</q-chip> </QChip>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="claim.worker && claim.worker.user"> <QItemSection v-if="claim.worker && claim.worker.user">
<q-item-label caption> <QItemLabel caption>
{{ t('claim.summary.assignedTo') }} {{ t('claim.summary.assignedTo') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
<span class="link"> <span class="link">
{{ claim.worker.user.nickname }} {{ claim.worker.user.nickname }}
<WorkerDescriptorProxy :id="claim.workerFk" /> <WorkerDescriptorProxy :id="claim.workerFk" />
</span> </span>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section <QItemSection
v-if="claim.client && claim.client.salesPersonUser" v-if="claim.client && claim.client.salesPersonUser"
> >
<q-item-label caption> <QItemLabel caption>
{{ t('claim.summary.attendedBy') }} {{ t('claim.summary.attendedBy') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
<span class="link"> <span class="link">
{{ claim.client.salesPersonUser.name }} {{ claim.client.salesPersonUser.name }}
<WorkerDescriptorProxy <WorkerDescriptorProxy
:id="claim.client.salesPersonFk" :id="claim.client.salesPersonFk"
/> />
</span> </span>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
</q-card-section> </QCardSection>
<q-card-section class="q-pa-md"> <QCardSection class="q-pa-md">
<h6>{{ t('claim.summary.details') }}</h6> <h6>{{ t('claim.summary.details') }}</h6>
<q-table :columns="detailsColumns" :rows="salesClaimed" flat> <QTable :columns="detailsColumns" :rows="salesClaimed" flat>
<template #header="props"> <template #header="props">
<q-tr :props="props"> <QTr :props="props">
<q-th <QTh v-for="col in props.cols" :key="col.name" :props="props">
v-for="col in props.cols"
:key="col.name"
:props="props"
>
{{ t(col.label) }} {{ t(col.label) }}
</q-th> </QTh>
</q-tr> </QTr>
</template> </template>
</q-table> </QTable>
</q-card-section> </QCardSection>
<q-card-section class="q-pa-md"> <QCardSection class="q-pa-md">
<h6>{{ t('claim.summary.actions') }}</h6> <h6>{{ t('claim.summary.actions') }}</h6>
<q-separator /> <QSeparator />
<div id="slider-container"> <div id="slider-container">
<q-slider <QSlider
v-model="claim.responsibility" v-model="claim.responsibility"
label label
:label-value="t('claim.summary.responsibility')" :label-value="t('claim.summary.responsibility')"
@ -175,7 +171,7 @@ function stateColor(code) {
readonly readonly
/> />
</div> </div>
</q-card-section> </QCardSection>
</template> </template>
</card-summary> </CardSummary>
</template> </template>

View File

@ -15,9 +15,9 @@ const { dialogRef, onDialogHide } = useDialogPluginComponent();
</script> </script>
<template> <template>
<q-dialog ref="dialogRef" @hide="onDialogHide"> <QDialog ref="dialogRef" @hide="onDialogHide">
<claim-summary v-if="$props.id" :id="$props.id" /> <ClaimSummary v-if="$props.id" :id="$props.id" />
</q-dialog> </QDialog>
</template> </template>
<style lang="scss"> <style lang="scss">

View File

@ -17,8 +17,8 @@ const states = ref();
</script> </script>
<template> <template>
<fetch-data url="ClaimStates" @on-fetch="(data) => (states = data)" auto-load /> <FetchData url="ClaimStates" @on-fetch="(data) => (states = data)" auto-load />
<fetch-data <FetchData
url="Workers/activeWithInheritedRole" url="Workers/activeWithInheritedRole"
:filter="{ where: { role: 'salesPerson' } }" :filter="{ where: { role: 'salesPerson' } }"
@on-fetch="(data) => (workers = data)" @on-fetch="(data) => (workers = data)"
@ -32,35 +32,35 @@ const states = ref();
</div> </div>
</template> </template>
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<q-list dense> <QList dense>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Customer ID')" :label="t('Customer ID')"
v-model="params.clientFk" v-model="params.clientFk"
lazy-rules lazy-rules
> >
<template #prepend> <template #prepend>
<q-icon name="badge" size="sm"></q-icon> <QIcon name="badge" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Client Name')" :label="t('Client Name')"
v-model="params.clientName" v-model="params.clientName"
lazy-rules lazy-rules
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!workers"> <QItemSection v-if="!workers">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="workers"> <QItemSection v-if="workers">
<q-select <QSelect
:label="t('Salesperson')" :label="t('Salesperson')"
v-model="params.salesPersonFk" v-model="params.salesPersonFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -72,14 +72,14 @@ const states = ref();
use-input use-input
:input-debounce="0" :input-debounce="0"
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!workers"> <QItemSection v-if="!workers">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="workers"> <QItemSection v-if="workers">
<q-select <QSelect
:label="t('Attender')" :label="t('Attender')"
v-model="params.attenderFk" v-model="params.attenderFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -91,14 +91,14 @@ const states = ref();
use-input use-input
:input-debounce="0" :input-debounce="0"
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!workers"> <QItemSection v-if="!workers">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="workers"> <QItemSection v-if="workers">
<q-select <QSelect
:label="t('Responsible')" :label="t('Responsible')"
v-model="params.claimResponsibleFk" v-model="params.claimResponsibleFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -110,14 +110,14 @@ const states = ref();
use-input use-input
:input-debounce="0" :input-debounce="0"
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-mb-md"> <QItem class="q-mb-md">
<q-item-section v-if="!states"> <QItemSection v-if="!states">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="states"> <QItemSection v-if="states">
<q-select <QSelect
:label="t('State')" :label="t('State')"
v-model="params.claimStateFk" v-model="params.claimStateFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -127,13 +127,13 @@ const states = ref();
emit-value emit-value
map-options map-options
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-separator /> <QSeparator />
<q-expansion-item :label="t('More options')" expand-separator> <QExpansionItem :label="t('More options')" expand-separator>
<!-- <q-item> <!-- <QItem>
<q-item-section> <QItemSection>
<q-select <qSelect
:label="t('Item')" :label="t('Item')"
v-model="params.itemFk" v-model="params.itemFk"
:options="items" :options="items"
@ -145,41 +145,41 @@ const states = ref();
emit-value emit-value
map-options map-options
/> />
</q-item-section> </QItemSection>
</q-item> --> </QItem> -->
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
v-model="params.created" v-model="params.created"
:label="t('Created')" :label="t('Created')"
autofocus autofocus
readonly readonly
> >
<template #append> <template #append>
<q-icon name="event" class="cursor-pointer"> <QIcon name="event" class="cursor-pointer">
<q-popup-proxy <QPopupProxy
cover cover
transition-show="scale" transition-show="scale"
transition-hide="scale" transition-hide="scale"
> >
<q-date v-model="params.created"> <QDate v-model="params.created">
<div class="row items-center justify-end"> <div class="row items-center justify-end">
<q-btn <QBtn
v-close-popup v-close-popup
label="Close" label="Close"
color="primary" color="primary"
flat flat
/> />
</div> </div>
</q-date> </QDate>
</q-popup-proxy> </QPopupProxy>
</q-icon> </QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-expansion-item> </QExpansionItem>
</q-list> </QList>
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>

View File

@ -4,7 +4,7 @@ import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { toDate } from 'filters/index'; import { toDate } from 'filters/index';
import Paginate from 'components/PaginateData.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import ClaimSummaryDialog from './Card/ClaimSummaryDialog.vue'; import ClaimSummaryDialog from './Card/ClaimSummaryDialog.vue';
import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue'; import VnSearchbar from 'components/ui/VnSearchbar.vue';
@ -46,143 +46,148 @@ function viewSummary(id) {
</Teleport> </Teleport>
<Teleport to="#actions-append"> <Teleport to="#actions-append">
<div class="row q-gutter-x-sm"> <div class="row q-gutter-x-sm">
<q-btn <QBtn
flat flat
@click="stateStore.toggleRightDrawer()" @click="stateStore.toggleRightDrawer()"
round round
dense dense
icon="menu" icon="menu"
> >
<q-tooltip bottom anchor="bottom right"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</div> </div>
</Teleport> </Teleport>
</template> </template>
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<ClaimFilter data-key="ClaimList" /> <ClaimFilter data-key="ClaimList" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<div class="card-list"> <div class="card-list">
<paginate data-key="ClaimList" url="Claims/filter" order="id DESC" auto-load> <VnPaginate
data-key="ClaimList"
url="Claims/filter"
order="id DESC"
auto-load
>
<template #body="{ rows }"> <template #body="{ rows }">
<q-card class="card q-mb-md" v-for="row of rows" :key="row.id"> <QCard class="card q-mb-md" v-for="row of rows" :key="row.id">
<q-item <QItem
class="q-pa-none items-start cursor-pointer q-hoverable" class="q-pa-none items-start cursor-pointer q-hoverable"
v-ripple v-ripple
clickable clickable
> >
<q-item-section class="q-pa-md" @click="navigate(row.id)"> <QItemSection class="q-pa-md" @click="navigate(row.id)">
<div class="text-h6 link"> <div class="text-h6 link">
{{ row.clientName }} {{ row.clientName }}
</div> </div>
<q-item-label caption>#{{ row.id }}</q-item-label> <QItemLabel caption>#{{ row.id }}</QItemLabel>
<q-list> <QList>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('claim.list.customer') }} {{ t('claim.list.customer') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ row.clientName }} {{ row.clientName }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('claim.list.assignedTo') }} {{ t('claim.list.assignedTo') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ row.workerName }} {{ row.workerName }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('claim.list.created') }} {{ t('claim.list.created') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toDate(row.created) }} {{ toDate(row.created) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('claim.list.state') }} {{ t('claim.list.state') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
<q-badge <QBadge
:color="stateColor(row.stateCode)" :color="stateColor(row.stateCode)"
class="q-ma-none" class="q-ma-none"
dense dense
> >
{{ row.stateDescription }} {{ row.stateDescription }}
</q-badge> </QBadge>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-item-section> </QItemSection>
<q-separator vertical /> <QSeparator vertical />
<q-card-actions vertical class="justify-between"> <QCardActions vertical class="justify-between">
<!-- <q-btn color="grey-7" round flat icon="more_vert"> <!-- <QBtn color="grey-7" round flat icon="more_vert">
<q-tooltip>{{ t('customer.list.moreOptions') }}</q-tooltip> <QTooltip>{{ t('customer.list.moreOptions') }}</QTooltip>
<q-menu cover auto-close> <QMenu cover auto-close>
<q-list> <QList>
<q-item clickable> <QItem clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="add" /> <QIcon name="add" />
</q-item-section> </QItemSection>
<q-item-section>Add a note</q-item-section> <QItemSection>Add a note</QItemSection>
</q-item> </QItem>
<q-item clickable> <QItem clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="logs" /> <QIcon name="logs" />
</q-item-section> </QItemSection>
<q-item-section>Display claim logs</q-item-section> <QItemSection>Display claim logs</QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-menu> </QMenu>
</q-btn> --> </QBtn> -->
<q-btn <QBtn
flat flat
round round
color="orange" color="orange"
icon="arrow_circle_right" icon="arrow_circle_right"
@click="navigate(row.id)" @click="navigate(row.id)"
> >
<q-tooltip> <QTooltip>
{{ t('components.smartCard.openCard') }} {{ t('components.smartCard.openCard') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
<q-btn <QBtn
flat flat
round round
color="grey-7" color="grey-7"
icon="preview" icon="preview"
@click="viewSummary(row.id)" @click="viewSummary(row.id)"
> >
<q-tooltip> <QTooltip>
{{ t('components.smartCard.openSummary') }} {{ t('components.smartCard.openSummary') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
<q-btn flat round color="grey-7" icon="vn:client"> <QBtn flat round color="grey-7" icon="vn:client">
<q-tooltip> <QTooltip>
{{ t('components.smartCard.viewDescription') }} {{ t('components.smartCard.viewDescription') }}
</q-tooltip> </QTooltip>
<CustomerDescriptorProxy :id="row.clientFk" /> <CustomerDescriptorProxy :id="row.clientFk" />
</q-btn> </QBtn>
</q-card-actions> </QCardActions>
</q-item> </QItem>
</q-card> </QCard>
</template> </template>
</paginate> </VnPaginate>
</div> </div>
</q-page> </QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -6,12 +6,12 @@ const stateStore = useStateStore();
</script> </script>
<template> <template>
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<LeftMenu /> <LeftMenu />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<router-view></router-view> <RouterView></RouterView>
</q-page-container> </QPageContainer>
</template> </template>

View File

@ -3,7 +3,7 @@ import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import axios from 'axios'; import axios from 'axios';
import Paginate from 'src/components/PaginateData.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import VnConfirm from 'src/components/ui/VnConfirm.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue';
@ -61,11 +61,11 @@ async function remove({ id }) {
</script> </script>
<template> <template>
<q-page class="column items-center q-pa-md sticky"> <QPage class="column items-center q-pa-md sticky">
<q-page-sticky expand position="top" :offset="[16, 16]"> <QPageSticky expand position="top" :offset="[16, 16]">
<q-card class="card q-pa-md"> <QCard class="card q-pa-md">
<q-form @submit="submit"> <QForm @submit="submit">
<q-input <QInput
ref="input" ref="input"
v-model="newRma.code" v-model="newRma.code"
:label="t('claim.rmaList.code')" :label="t('claim.rmaList.code')"
@ -78,11 +78,11 @@ async function remove({ id }) {
<div class="text-caption"> <div class="text-caption">
{{ arrayData.totalRows }} {{ t('claim.rmaList.records') }} {{ arrayData.totalRows }} {{ t('claim.rmaList.records') }}
</div> </div>
</q-form> </QForm>
</q-card> </QCard>
</q-page-sticky> </QPageSticky>
<div class="card-list"> <div class="card-list">
<paginate <VnPaginate
data-key="ClaimRmaList" data-key="ClaimRmaList"
url="ClaimRmas" url="ClaimRmas"
order="id DESC" order="id DESC"
@ -90,68 +90,66 @@ async function remove({ id }) {
auto-load auto-load
> >
<template #body="{ rows }"> <template #body="{ rows }">
<q-card class="card"> <QCard class="card">
<template v-if="isLoading"> <template v-if="isLoading">
<q-item class="q-pa-none items-start"> <QItem class="q-pa-none items-start">
<q-item-section class="q-pa-md"> <QItemSection class="q-pa-md">
<q-list> <QList>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
<q-skeleton /> <QSkeleton />
</q-item-label> </QItemLabel>
<q-item-label <QItemLabel>
><q-skeleton type="text" <QSkeleton type="text" />
/></q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-item-section> </QItemSection>
<q-card-actions vertical class="justify-between"> <QCardActions vertical class="justify-between">
<q-skeleton <QSkeleton
type="circle" type="circle"
class="q-mb-md" class="q-mb-md"
size="40px" size="40px"
/> />
</q-card-actions> </QCardActions>
</q-item> </QItem>
<q-separator /> <QSeparator />
</template> </template>
<template v-for="row of rows" :key="row.id"> <template v-for="row of rows" :key="row.id">
<q-item class="q-pa-none items-start"> <QItem class="q-pa-none items-start">
<q-item-section class="q-pa-md"> <QItemSection class="q-pa-md">
<q-list> <QList>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('claim.rmaList.code') t('claim.rmaList.code')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{ row.code }}</QItemLabel>
row.code </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> </QList>
</q-item> </QItemSection>
</q-list> <QCardActions vertical class="justify-between">
</q-item-section> <QBtn
<q-card-actions vertical class="justify-between">
<q-btn
flat flat
round round
color="primary" color="primary"
icon="vn:bin" icon="vn:bin"
@click="confirm(row.id)" @click="confirm(row.id)"
> >
<q-tooltip>{{ t('globals.remove') }}</q-tooltip> <QTooltip>{{ t('globals.remove') }}</QTooltip>
</q-btn> </QBtn>
</q-card-actions> </QCardActions>
</q-item> </QItem>
<q-separator /> <QSeparator />
</template> </template>
</q-card> </QCard>
</template> </template>
</paginate> </VnPaginate>
</div> </div>
</q-page> </QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -159,7 +157,8 @@ async function remove({ id }) {
padding-top: 156px; padding-top: 156px;
} }
.card-list, .card { .card-list,
.card {
width: 100%; width: 100%;
max-width: 60em; max-width: 60em;
} }

View File

@ -59,12 +59,12 @@ const filterOptions = {
auto-load auto-load
/> />
<div class="column items-center"> <div class="column items-center">
<q-card> <QCard>
<form-model :url="`Clients/${route.params.id}`" model="customer"> <FormModel :url="`Clients/${route.params.id}`" model="customer">
<template #form="{ data, validate, filter }"> <template #form="{ data, validate, filter }">
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-input <QInput
v-model="data.socialName" v-model="data.socialName"
:label="t('customer.basicData.socialName')" :label="t('customer.basicData.socialName')"
:rules="validate('client.socialName')" :rules="validate('client.socialName')"
@ -72,7 +72,7 @@ const filterOptions = {
/> />
</div> </div>
<div class="col"> <div class="col">
<q-select <QSelect
v-model="data.businessTypeFk" v-model="data.businessTypeFk"
:options="businessTypes" :options="businessTypes"
option-value="code" option-value="code"
@ -87,7 +87,7 @@ const filterOptions = {
</div> </div>
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-input <QInput
v-model="data.contact" v-model="data.contact"
:label="t('customer.basicData.contact')" :label="t('customer.basicData.contact')"
:rules="validate('client.contact')" :rules="validate('client.contact')"
@ -95,7 +95,7 @@ const filterOptions = {
/> />
</div> </div>
<div class="col"> <div class="col">
<q-input <QInput
v-model="data.email" v-model="data.email"
type="email" type="email"
:label="t('customer.basicData.email')" :label="t('customer.basicData.email')"
@ -106,7 +106,7 @@ const filterOptions = {
</div> </div>
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-input <QInput
v-model="data.phone" v-model="data.phone"
:label="t('customer.basicData.phone')" :label="t('customer.basicData.phone')"
:rules="validate('client.phone')" :rules="validate('client.phone')"
@ -114,7 +114,7 @@ const filterOptions = {
/> />
</div> </div>
<div class="col"> <div class="col">
<q-input <QInput
v-model="data.mobile" v-model="data.mobile"
:label="t('customer.basicData.mobile')" :label="t('customer.basicData.mobile')"
:rules="validate('client.mobile')" :rules="validate('client.mobile')"
@ -124,7 +124,7 @@ const filterOptions = {
</div> </div>
<div class="row q-gutter-md q-mb-md"> <div class="row q-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-select <QSelect
v-model="data.salesPersonFk" v-model="data.salesPersonFk"
:options="workers" :options="workers"
option-value="id" option-value="id"
@ -141,18 +141,18 @@ const filterOptions = {
:input-debounce="0" :input-debounce="0"
> >
<template #prepend> <template #prepend>
<q-avatar color="orange"> <QAvatar color="orange">
<q-img <QImg
v-if="data.salesPersonFk" v-if="data.salesPersonFk"
:src="`/api/Images/user/160x160/${data.salesPersonFk}/download?access_token=${token}`" :src="`/api/Images/user/160x160/${data.salesPersonFk}/download?access_token=${token}`"
spinner-color="white" spinner-color="white"
/> />
</q-avatar> </QAvatar>
</template> </template>
</q-select> </QSelect>
</div> </div>
<div class="col"> <div class="col">
<q-select <QSelect
v-model="data.contactChannelFk" v-model="data.contactChannelFk"
:options="contactChannels" :options="contactChannels"
option-value="id" option-value="id"
@ -166,8 +166,8 @@ const filterOptions = {
</div> </div>
</div> </div>
</template> </template>
</form-model> </FormModel>
</q-card> </QCard>
</div> </div>
</template> </template>

View File

@ -17,18 +17,18 @@ const { t } = useI18n();
:info="t('You can search by customer id or name')" :info="t('You can search by customer id or name')"
/> />
</Teleport> </Teleport>
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<q-scroll-area class="fit"> <QScrollArea class="fit">
<CustomerDescriptor /> <CustomerDescriptor />
<q-separator /> <QSeparator />
<left-menu source="card" /> <LeftMenu source="card" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<q-page class="q-pa-md"> <QPage class="q-pa-md">
<router-view></router-view> <RouterView></RouterView>
</q-page> </QPage>
</q-page-container> </QPageContainer>
</template> </template>
<i18n> <i18n>

View File

@ -23,97 +23,97 @@ const entityId = computed(() => {
</script> </script>
<template> <template>
<card-descriptor module="Customer" :url="`Clients/${entityId}/getCard`"> <CardDescriptor module="Customer" :url="`Clients/${entityId}/getCard`">
<template #body="{ entity }"> <template #body="{ entity }">
<q-list dense> <QList dense>
<q-item v-if="entity.salesPersonUser" class="row"> <QItem v-if="entity.salesPersonUser" class="row">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.card.salesPerson') }} {{ t('customer.card.salesPerson') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none"> <QItemLabel class="col q-ma-none">
<span class="link"> <span class="link">
{{ entity.salesPersonUser.name }} {{ entity.salesPersonUser.name }}
<WorkerDescriptorProxy :id="entity.salesPersonFk" /> <WorkerDescriptorProxy :id="entity.salesPersonFk" />
</span> </span>
</q-item-label> </QItemLabel>
</q-item> </QItem>
<q-item class="row"> <QItem class="row">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.card.credit') }} {{ t('customer.card.credit') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none"> <QItemLabel class="col q-ma-none">
{{ toCurrency(entity.credit) }} {{ toCurrency(entity.credit) }}
</q-item-label> </QItemLabel>
</q-item> </QItem>
<q-item class="row"> <QItem class="row">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.card.securedCredit') }} {{ t('customer.card.securedCredit') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none"> <QItemLabel class="col q-ma-none">
{{ toCurrency(entity.creditInsurance) }} {{ toCurrency(entity.creditInsurance) }}
</q-item-label> </QItemLabel>
</q-item> </QItem>
<q-item v-if="entity.payMethod" class="row"> <QItem v-if="entity.payMethod" class="row">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.card.payMethod') }} {{ t('customer.card.payMethod') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none"> <QItemLabel class="col q-ma-none">
{{ entity.payMethod.name }} {{ entity.payMethod.name }}
</q-item-label> </QItemLabel>
</q-item> </QItem>
<q-item class="row"> <QItem class="row">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.card.debt') }} {{ t('customer.card.debt') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none"> <QItemLabel class="col q-ma-none">
{{ toCurrency(entity.debt) }} {{ toCurrency(entity.debt) }}
</q-item-label> </QItemLabel>
</q-item> </QItem>
</q-list> </QList>
<q-card-actions class="q-gutter-md"> <QCardActions class="q-gutter-md">
<q-icon <QIcon
v-if="entity.isActive == false" v-if="entity.isActive == false"
name="vn:disabled" name="vn:disabled"
size="xs" size="xs"
color="primary" color="primary"
> >
<q-tooltip>{{ t('customer.card.isDisabled') }}</q-tooltip> <QTooltip>{{ t('customer.card.isDisabled') }}</QTooltip>
</q-icon> </QIcon>
<q-icon <QIcon
v-if="entity.isFreezed == true" v-if="entity.isFreezed == true"
name="vn:frozen" name="vn:frozen"
size="xs" size="xs"
color="primary" color="primary"
> >
<q-tooltip>{{ t('customer.card.isFrozen') }}</q-tooltip> <QTooltip>{{ t('customer.card.isFrozen') }}</QTooltip>
</q-icon> </QIcon>
<q-icon <QIcon
v-if="entity.debt > entity.credit" v-if="entity.debt > entity.credit"
name="vn:risk" name="vn:risk"
size="xs" size="xs"
color="primary" color="primary"
> >
<q-tooltip>{{ t('customer.card.hasDebt') }}</q-tooltip> <QTooltip>{{ t('customer.card.hasDebt') }}</QTooltip>
</q-icon> </QIcon>
<q-icon <QIcon
v-if="entity.isTaxDataChecked == false" v-if="entity.isTaxDataChecked == false"
name="vn:no036" name="vn:no036"
size="xs" size="xs"
color="primary" color="primary"
> >
<q-tooltip>{{ t('customer.card.notChecked') }}</q-tooltip> <QTooltip>{{ t('customer.card.notChecked') }}</QTooltip>
</q-icon> </QIcon>
<q-icon <QIcon
v-if="entity.account && entity.account.active == false" v-if="entity.account && entity.account.active == false"
name="vn:noweb" name="vn:noweb"
size="xs" size="xs"
color="primary" color="primary"
> >
<q-tooltip>{{ t('customer.card.noWebAccess') }}</q-tooltip> <QTooltip>{{ t('customer.card.noWebAccess') }}</QTooltip>
</q-icon> </QIcon>
</q-card-actions> </QCardActions>
<q-card-actions> <QCardActions>
<q-btn <QBtn
:to="{ :to="{
name: 'TicketList', name: 'TicketList',
query: { params: JSON.stringify({ clientFk: entity.id }) }, query: { params: JSON.stringify({ clientFk: entity.id }) },
@ -122,9 +122,9 @@ const entityId = computed(() => {
icon="vn:ticket" icon="vn:ticket"
color="primary" color="primary"
> >
<q-tooltip>{{ t('ticketList') }}</q-tooltip> <QTooltip>{{ t('ticketList') }}</QTooltip>
</q-btn> </QBtn>
<q-btn <QBtn
:to="{ :to="{
name: 'InvoiceOutList', name: 'InvoiceOutList',
query: { params: JSON.stringify({ clientFk: entity.id }) }, query: { params: JSON.stringify({ clientFk: entity.id }) },
@ -133,23 +133,23 @@ const entityId = computed(() => {
icon="vn:invoice-out" icon="vn:invoice-out"
color="primary" color="primary"
> >
<q-tooltip>{{ t('invoiceOutList') }}</q-tooltip> <QTooltip>{{ t('invoiceOutList') }}</QTooltip>
</q-btn> </QBtn>
<!-- <!--
<q-btn size="md" icon="vn:basketadd" color="primary"> <QBtn size="md" icon="vn:basketadd" color="primary">
<q-tooltip>Order list</q-tooltip> <QTooltip>Order list</QTooltip>
</q-btn> </QBtn>
<q-btn size="md" icon="face" color="primary"> <QBtn size="md" icon="face" color="primary">
<q-tooltip>View user</q-tooltip> <QTooltip>View user</QTooltip>
</q-btn> </QBtn>
<q-btn size="md" icon="expand_more" color="primary"> <QBtn size="md" icon="expand_more" color="primary">
<q-tooltip>More options</q-tooltip> <QTooltip>More options</QTooltip>
</q-btn> --> </QBtn> -->
</q-card-actions> </QCardActions>
</template> </template>
</card-descriptor> </CardDescriptor>
</template> </template>
<i18n> <i18n>

View File

@ -9,7 +9,7 @@ const $props = defineProps({
}); });
</script> </script>
<template> <template>
<q-popup-proxy> <QPopupProxy>
<CustomerDescriptor v-if="$props.id" :id="$props.id" /> <CustomerDescriptor v-if="$props.id" :id="$props.id" />
</q-popup-proxy> </QPopupProxy>
</template> </template>

View File

@ -51,478 +51,476 @@ const creditWarning = computed(() => {
</script> </script>
<template> <template>
<card-summary ref="summary" :url="`Clients/${entityId}/summary`"> <CardSummary ref="summary" :url="`Clients/${entityId}/summary`">
<template #body="{ entity }"> <template #body="{ entity }">
<q-card-section class="row q-pa-none q-col-gutter-md"> <QCardSection class="row q-pa-none QCol-gutter-md">
<div class="col"> <div class="col">
<q-list dense> <QList dense>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('customer.summary.basicData') }} {{ t('customer.summary.basicData') }}
<router-link <RouterLink
:to="{ :to="{
name: 'CustomerBasicData', name: 'CustomerBasicData',
params: { id: entity.id }, params: { id: entity.id },
}" }"
target="_blank" target="_blank"
> >
<q-icon name="open_in_new" /> <QIcon name="open_in_new" />
</router-link> </RouterLink>
</q-item-label> </QItemLabel>
<q-separator class="q-mb-md" /> <QSeparator class="q-mb-md" />
<q-item class="row col"> <QItem class="row col">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.summary.customerId') }} {{ t('customer.summary.customerId') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none"> <QItemLabel class="col q-ma-none">
{{ entity.id }} {{ entity.id }}
</q-item-label> </QItemLabel>
</q-item> </QItem>
<q-item class="row col"> <QItem class="row col">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.summary.name') }} {{ t('customer.summary.name') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none">{{ <QItemLabel class="col q-ma-none">
entity.name {{ entity.name }}
}}</q-item-label> </QItemLabel>
</q-item> </QItem>
<q-item class="row col"> <QItem class="row col">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.summary.contact') }} {{ t('customer.summary.contact') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none">{{ <QItemLabel class="col q-ma-none">
entity.contact {{ entity.contact }}
}}</q-item-label> </QItemLabel>
</q-item> </QItem>
<q-item v-if="entity.salesPersonUser" class="row col"> <QItem v-if="entity.salesPersonUser" class="row col">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.summary.salesPerson') }} {{ t('customer.summary.salesPerson') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none"> <QItemLabel class="col q-ma-none">
{{ entity.salesPersonUser.name }} {{ entity.salesPersonUser.name }}
</q-item-label> </QItemLabel>
</q-item> </QItem>
<q-item class="row col"> <QItem class="row col">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.summary.phone') }} {{ t('customer.summary.phone') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none">{{ <QItemLabel class="col q-ma-none">
entity.phone {{ entity.phone }}
}}</q-item-label> </QItemLabel>
</q-item> </QItem>
<q-item class="row col"> <QItem class="row col">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.summary.mobile') }} {{ t('customer.summary.mobile') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none">{{ <QItemLabel class="col q-ma-none">{{
entity.mobile entity.mobile
}}</q-item-label> }}</QItemLabel>
</q-item> </QItem>
<q-item v-if="entity.contactChannel" class="row col"> <QItem v-if="entity.contactChannel" class="row col">
<q-item-label class="col" caption> <QItemLabel class="col" caption>
{{ t('customer.summary.contactChannel') }} {{ t('customer.summary.contactChannel') }}
</q-item-label> </QItemLabel>
<q-item-label class="col q-ma-none"> <QItemLabel class="col q-ma-none">
{{ entity.contactChannel.name }} {{ entity.contactChannel.name }}
</q-item-label> </QItemLabel>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.email') }} {{ t('customer.summary.email') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.email }}</q-item-label> <QItemLabel>{{ entity.email }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col"> <div class="col">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('customer.summary.fiscalAddress') }} {{ t('customer.summary.fiscalAddress') }}
</q-item-label> </QItemLabel>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.socialName') }} {{ t('customer.summary.socialName') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.socialName }}</q-item-label> <QItemLabel>{{ entity.socialName }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.fiscalId') }} {{ t('customer.summary.fiscalId') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.fi }}</q-item-label> <QItemLabel>{{ entity.fi }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.postcode') }} {{ t('customer.summary.postcode') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.postcode }}</q-item-label> <QItemLabel>{{ entity.postcode }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="entity.province"> <QItem v-if="entity.province">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.province') }} {{ t('customer.summary.province') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.province.name }}</q-item-label> <QItemLabel>{{ entity.province.name }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="entity.country"> <QItem v-if="entity.country">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.country') }} {{ t('customer.summary.country') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.country.country }}</q-item-label> <QItemLabel>{{ entity.country.country }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.street') }} {{ t('customer.summary.street') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.street }}</q-item-label> <QItemLabel>{{ entity.street }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col"> <div class="col">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('customer.summary.fiscalData') }} {{ t('customer.summary.fiscalData') }}
</q-item-label> </QItemLabel>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.isEqualizated" v-model="entity.isEqualizated"
:label="t('customer.summary.isEqualizated')" :label="t('customer.summary.isEqualizated')"
disable disable
/> />
</q-item> </QItem>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.isActive" v-model="entity.isActive"
:label="t('customer.summary.isActive')" :label="t('customer.summary.isActive')"
disable disable
/> />
</q-item> </QItem>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.hasToInvoiceByAddress" v-model="entity.hasToInvoiceByAddress"
:label="t('customer.summary.invoiceByAddress')" :label="t('customer.summary.invoiceByAddress')"
disable disable
/> />
</q-item> </QItem>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.isTaxDataChecked" v-model="entity.isTaxDataChecked"
:label="t('customer.summary.verifiedData')" :label="t('customer.summary.verifiedData')"
disable disable
/> />
</q-item> </QItem>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.hasToInvoice" v-model="entity.hasToInvoice"
:label="t('customer.summary.hasToInvoice')" :label="t('customer.summary.hasToInvoice')"
disable disable
/> />
</q-item> </QItem>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.isToBeMailed" v-model="entity.isToBeMailed"
:label="t('customer.summary.notifyByEmail')" :label="t('customer.summary.notifyByEmail')"
disable disable
/> />
</q-item> </QItem>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.isVies" v-model="entity.isVies"
:label="t('customer.summary.vies')" :label="t('customer.summary.vies')"
disable disable
/> />
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col"> <div class="col">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('customer.summary.billingData') }} {{ t('customer.summary.billingData') }}
</q-item-label> </QItemLabel>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.payMethod') }} {{ t('customer.summary.payMethod') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.payMethod.name }}</q-item-label> <QItemLabel>{{ entity.payMethod.name }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.bankAccount') }} {{ t('customer.summary.bankAccount') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.iban }}</q-item-label> <QItemLabel>{{ entity.iban }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.dueDay') }} {{ t('customer.summary.dueDay') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.dueDay }}</q-item-label> <QItemLabel>{{ entity.dueDay }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.hasLcr" v-model="entity.hasLcr"
:label="t('customer.summary.hasLcr')" :label="t('customer.summary.hasLcr')"
disable disable
/> />
</q-item> </QItem>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.hasCoreVnl" v-model="entity.hasCoreVnl"
:label="t('customer.summary.hasCoreVnl')" :label="t('customer.summary.hasCoreVnl')"
disable disable
/> />
</q-item> </QItem>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.hasSepaVnl" v-model="entity.hasSepaVnl"
:label="t('customer.summary.hasB2BVnl')" :label="t('customer.summary.hasB2BVnl')"
disable disable
/> />
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col" v-if="entity.defaultAddress"> <div class="col" v-if="entity.defaultAddress">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('customer.summary.consignee') }} {{ t('customer.summary.consignee') }}
</q-item-label> </QItemLabel>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.addressName') }} {{ t('customer.summary.addressName') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ entity.defaultAddress.nickname }} {{ entity.defaultAddress.nickname }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.addressCity') }} {{ t('customer.summary.addressCity') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ <QItemLabel>{{ entity.defaultAddress.city }}</QItemLabel>
entity.defaultAddress.city </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem>
</q-item> <QItemSection>
<q-item> <QItemLabel caption>
<q-item-section>
<q-item-label caption>
{{ t('customer.summary.addressStreet') }} {{ t('customer.summary.addressStreet') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ entity.defaultAddress.street }} {{ entity.defaultAddress.street }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col" v-if="entity.account"> <div class="col" v-if="entity.account">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('customer.summary.webAccess') }} {{ t('customer.summary.webAccess') }}
</q-item-label> </QItemLabel>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.username') }} {{ t('customer.summary.username') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.account.name }}</q-item-label> <QItemLabel>{{ entity.account.name }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item dense> <QItem dense>
<q-checkbox <QCheckbox
v-model="entity.account.active" v-model="entity.account.active"
:label="t('customer.summary.webAccess')" :label="t('customer.summary.webAccess')"
disable disable
/> />
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col"> <div class="col">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('customer.summary.businessData') }} {{ t('customer.summary.businessData') }}
</q-item-label> </QItemLabel>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.totalGreuge') }} {{ t('customer.summary.totalGreuge') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toCurrency(entity.totalGreuge) }} {{ toCurrency(entity.totalGreuge) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="entity.mana"> <QItem v-if="entity.mana">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.mana') }} {{ t('customer.summary.mana') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toCurrency(entity.mana.mana) }} {{ toCurrency(entity.mana.mana) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="entity.claimsRatio"> <QItem v-if="entity.claimsRatio">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.priceIncreasingRate') }} {{ t('customer.summary.priceIncreasingRate') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toPercentage(priceIncreasingRate) }} {{ toPercentage(priceIncreasingRate) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="entity.averageInvoiced"> <QItem v-if="entity.averageInvoiced">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.averageInvoiced') }} {{ t('customer.summary.averageInvoiced') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toCurrency(entity.averageInvoiced.invoiced) }} {{ toCurrency(entity.averageInvoiced.invoiced) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="entity.claimsRatio"> <QItem v-if="entity.claimsRatio">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.claimRate') }} {{ t('customer.summary.claimRate') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ toPercentage(claimRate) }}</q-item-label> <QItemLabel>{{ toPercentage(claimRate) }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col"> <div class="col">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('customer.summary.financialData') }} {{ t('customer.summary.financialData') }}
</q-item-label> </QItemLabel>
<q-item v-if="entity.debt"> <QItem v-if="entity.debt">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.risk') }} {{ t('customer.summary.risk') }}
</q-item-label> </QItemLabel>
<q-item-label :class="debtWarning"> <QItemLabel :class="debtWarning">
{{ toCurrency(entity.debt.debt) }} {{ toCurrency(entity.debt.debt) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section side> <QItemSection side>
<q-icon name="vn:info"> <QIcon name="vn:info">
<q-tooltip> <QTooltip>
{{ t('customer.summary.riskInfo') }} {{ t('customer.summary.riskInfo') }}
</q-tooltip> </QTooltip>
</q-icon> </QIcon>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.credit') }} {{ t('customer.summary.credit') }}
</q-item-label> </QItemLabel>
<q-item-label :class="creditWarning"> <QItemLabel :class="creditWarning">
{{ toCurrency(entity.credit) }} {{ toCurrency(entity.credit) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section side> <QItemSection side>
<q-icon name="vn:info"> <QIcon name="vn:info">
<q-tooltip> <QTooltip>
{{ t('customer.summary.creditInfo') }} {{ t('customer.summary.creditInfo') }}
</q-tooltip> </QTooltip>
</q-icon> </QIcon>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="entity.creditInsurance"> <QItem v-if="entity.creditInsurance">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.securedCredit') }} {{ t('customer.summary.securedCredit') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toCurrency(entity.creditInsurance) }} {{ toCurrency(entity.creditInsurance) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section side> <QItemSection side>
<q-icon name="vn:info"> <QIcon name="vn:info">
<q-tooltip> <QTooltip>
{{ t('customer.summary.securedCreditInfo') }} {{ t('customer.summary.securedCreditInfo') }}
</q-tooltip> </QTooltip>
</q-icon> </QIcon>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.balance') }} {{ t('customer.summary.balance') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toCurrency(entity.sumRisk) || toCurrency(0) }} {{ toCurrency(entity.sumRisk) || toCurrency(0) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section side> <QItemSection side>
<q-icon name="vn:info"> <QIcon name="vn:info">
<q-tooltip> <QTooltip>
{{ t('customer.summary.balanceInfo') }} {{ t('customer.summary.balanceInfo') }}
</q-tooltip> </QTooltip>
</q-icon> </QIcon>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="entity.defaulters"> <QItem v-if="entity.defaulters">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.balanceDue') }} {{ t('customer.summary.balanceDue') }}
</q-item-label> </QItemLabel>
<q-item-label :class="balanceDueWarning"> <QItemLabel :class="balanceDueWarning">
{{ toCurrency(balanceDue) }} {{ toCurrency(balanceDue) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section side> <QItemSection side>
<q-icon name="vn:info"> <QIcon name="vn:info">
<q-tooltip> <QTooltip>
{{ t('customer.summary.balanceDueInfo') }} {{ t('customer.summary.balanceDueInfo') }}
</q-tooltip> </QTooltip>
</q-icon> </QIcon>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="entity.recovery"> <QItem v-if="entity.recovery">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.summary.recoverySince') }} {{ t('customer.summary.recoverySince') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toDate(entity.recovery.started) }} {{ toDate(entity.recovery.started) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
</q-card-section> </QCardSection>
</template> </template>
</card-summary> </CardSummary>
</template> </template>
<style lang="scss"> <style lang="scss">

View File

@ -15,9 +15,9 @@ const { dialogRef, onDialogHide } = useDialogPluginComponent();
</script> </script>
<template> <template>
<q-dialog ref="dialogRef" @hide="onDialogHide"> <QDialog ref="dialogRef" @hide="onDialogHide">
<customer-summary v-if="$props.id" :id="$props.id" /> <CustomerSummary v-if="$props.id" :id="$props.id" />
</q-dialog> </QDialog>
</template> </template>
<style lang="scss"> <style lang="scss">

View File

@ -14,10 +14,10 @@ watch(
</script> </script>
<template> <template>
<q-page class="q-pa-md"> <QPage class="q-pa-md">
<q-card class="q-pa-md"> <QCard class="q-pa-md">
<q-form @submit="onSubmit" @reset="onReset" class="q-gutter-md"> <QForm @submit="onSubmit" @reset="onReset" class="q-gutter-md">
<q-input <QInput
filled filled
v-model="customer.name" v-model="customer.name"
label="Your name *" label="Your name *"
@ -26,7 +26,7 @@ watch(
:rules="[(val) => (val && val.length > 0) || 'Please type something']" :rules="[(val) => (val && val.length > 0) || 'Please type something']"
/> />
<q-input <QInput
filled filled
type="number" type="number"
v-model="age" v-model="age"
@ -39,12 +39,18 @@ watch(
/> />
<div> <div>
<q-btn label="Submit" type="submit" color="primary" /> <QBtn label="Submit" type="submit" color="primary" />
<q-btn label="Reset" type="reset" color="primary" flat class="q-ml-sm" /> <QBtn
label="Reset"
type="reset"
color="primary"
flat
class="q-ml-sm"
/>
</div> </div>
</q-form> </QForm>
</q-card> </QCard>
</q-page> </QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -18,9 +18,9 @@ const zones = ref();
</script> </script>
<template> <template>
<fetch-data url="Provinces" @on-fetch="(data) => (provinces = data)" auto-load /> <FetchData url="Provinces" @on-fetch="(data) => (provinces = data)" auto-load />
<fetch-data url="Zones" @on-fetch="(data) => (zones = data)" auto-load /> <FetchData url="Zones" @on-fetch="(data) => (zones = data)" auto-load />
<fetch-data <FetchData
url="Workers/activeWithInheritedRole" url="Workers/activeWithInheritedRole"
:filter="{ where: { role: 'salesPerson' } }" :filter="{ where: { role: 'salesPerson' } }"
@on-fetch="(data) => (workers = data)" @on-fetch="(data) => (workers = data)"
@ -34,36 +34,36 @@ const zones = ref();
</div> </div>
</template> </template>
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<q-list dense> <QList dense>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input :label="t('FI')" v-model="params.fi" lazy-rules> <QInput :label="t('FI')" v-model="params.fi" lazy-rules>
<template #prepend> <template #prepend>
<q-icon name="badge" size="sm"></q-icon> <QIcon name="badge" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input :label="t('Name')" v-model="params.name" lazy-rules /> <QInput :label="t('Name')" v-model="params.name" lazy-rules />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Social Name')" :label="t('Social Name')"
v-model="params.socialName" v-model="params.socialName"
lazy-rules lazy-rules
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!workers"> <QItemSection v-if="!workers">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="workers"> <QItemSection v-if="workers">
<q-select <QSelect
:label="t('Salesperson')" :label="t('Salesperson')"
v-model="params.salesPersonFk" v-model="params.salesPersonFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -75,14 +75,14 @@ const zones = ref();
use-input use-input
:input-debounce="0" :input-debounce="0"
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!provinces"> <QItemSection v-if="!provinces">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="provinces"> <QItemSection v-if="provinces">
<q-select <QSelect
:label="t('Province')" :label="t('Province')"
v-model="params.provinceFk" v-model="params.provinceFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -92,47 +92,39 @@ const zones = ref();
emit-value emit-value
map-options map-options
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-mb-md"> <QItem class="q-mb-md">
<q-item-section> <QItemSection>
<q-input :label="t('City')" v-model="params.city" lazy-rules /> <QInput :label="t('City')" v-model="params.city" lazy-rules />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-separator /> <QSeparator />
<q-expansion-item :label="t('More options')" expand-separator> <QExpansionItem :label="t('More options')" expand-separator>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput :label="t('Phone')" v-model="params.phone" lazy-rules>
:label="t('Phone')"
v-model="params.phone"
lazy-rules
>
<template #prepend> <template #prepend>
<q-icon name="phone" size="sm"></q-icon> <QIcon name="phone" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput :label="t('Email')" v-model="params.email" lazy-rules>
:label="t('Email')"
v-model="params.email"
lazy-rules
>
<template #prepend> <template #prepend>
<q-icon name="email" size="sm"></q-icon> <QIcon name="email" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!zones"> <QItemSection v-if="!zones">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="zones"> <QItemSection v-if="zones">
<q-select <QSelect
:label="t('Zone')" :label="t('Zone')"
v-model="params.zoneFk" v-model="params.zoneFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -142,19 +134,19 @@ const zones = ref();
emit-value emit-value
map-options map-options
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Postcode')" :label="t('Postcode')"
v-model="params.postcode" v-model="params.postcode"
lazy-rules lazy-rules
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-expansion-item> </QExpansionItem>
</q-list> </QList>
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>

View File

@ -3,7 +3,7 @@ import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import Paginate from 'src/components/PaginateData.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import CustomerSummaryDialog from './Card/CustomerSummaryDialog.vue'; import CustomerSummaryDialog from './Card/CustomerSummaryDialog.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import CustomerFilter from './CustomerFilter.vue'; import CustomerFilter from './CustomerFilter.vue';
@ -38,117 +38,117 @@ function viewSummary(id) {
</Teleport> </Teleport>
<Teleport to="#actions-append"> <Teleport to="#actions-append">
<div class="row q-gutter-x-sm"> <div class="row q-gutter-x-sm">
<q-btn <QBtn
flat flat
@click="stateStore.toggleRightDrawer()" @click="stateStore.toggleRightDrawer()"
round round
dense dense
icon="menu" icon="menu"
> >
<q-tooltip bottom anchor="bottom right"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</div> </div>
</Teleport> </Teleport>
</template> </template>
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<CustomerFilter data-key="CustomerList" /> <CustomerFilter data-key="CustomerList" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<div class="card-list"> <div class="card-list">
<paginate <VnPaginate
data-key="CustomerList" data-key="CustomerList"
url="/Clients/filter" url="/Clients/filter"
order="id DESC" order="id DESC"
auto-load auto-load
> >
<template #body="{ rows }"> <template #body="{ rows }">
<q-card class="card q-mb-md" v-for="row of rows" :key="row.id"> <QCard class="card q-mb-md" v-for="row of rows" :key="row.id">
<q-item <QItem
class="q-pa-none items-start cursor-pointer q-hoverable" class="q-pa-none items-start cursor-pointer q-hoverable"
v-ripple v-ripple
clickable clickable
> >
<q-item-section class="q-pa-md" @click="navigate(row.id)"> <QItemSection class="q-pa-md" @click="navigate(row.id)">
<div class="text-h6">{{ row.name }}</div> <div class="text-h6">{{ row.name }}</div>
<q-item-label caption>#{{ row.id }}</q-item-label> <QItemLabel caption>#{{ row.id }}</QItemLabel>
<q-list> <QList>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.list.email') }} {{ t('customer.list.email') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ row.email }}</q-item-label> <QItemLabel>{{ row.email }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('customer.list.phone') }} {{ t('customer.list.phone') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ row.phone }}</q-item-label> <QItemLabel>{{ row.phone }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-item-section> </QItemSection>
<q-separator vertical /> <QSeparator vertical />
<q-card-actions vertical class="justify-between"> <QCardActions vertical class="justify-between">
<!-- <q-btn color="grey-7" round flat icon="more_vert"> <!-- <QBtn color="grey-7" round flat icon="more_vert">
<q-tooltip>{{ t('customer.list.moreOptions') }}</q-tooltip> <QTooltip>{{ t('customer.list.moreOptions') }}</QTooltip>
<q-menu cover auto-close> <QMenu cover auto-close>
<q-list> <QList>
<q-item clickable> <QItem clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="add" /> <QIcon name="add" />
</q-item-section> </QItemSection>
<q-item-section>Add a note</q-item-section> <QItemSection>Add a note</QItemSection>
</q-item> </QItem>
<q-item clickable> <QItem clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="history" /> <QIcon name="history" />
</q-item-section> </QItemSection>
<q-item-section>Display customer history</q-item-section> <QItemSection>Display customer history</QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-menu> </QMenu>
</q-btn> --> </QBtn> -->
<q-btn <QBtn
flat flat
round round
color="primary" color="primary"
icon="arrow_circle_right" icon="arrow_circle_right"
@click="navigate(row.id)" @click="navigate(row.id)"
> >
<q-tooltip> <QTooltip>
{{ t('components.smartCard.openCard') }} {{ t('components.smartCard.openCard') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
<q-btn <QBtn
flat flat
round round
color="grey-7" color="grey-7"
icon="preview" icon="preview"
@click="viewSummary(row.id)" @click="viewSummary(row.id)"
> >
<q-tooltip> <QTooltip>
{{ t('components.smartCard.openSummary') }} {{ t('components.smartCard.openSummary') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
<!-- <q-btn flat round color="grey-7" icon="vn:ticket"> <!-- <QBtn flat round color="grey-7" icon="vn:ticket">
<q-tooltip>{{ t('customer.list.customerOrders') }}</q-tooltip> <QTooltip>{{ t('customer.list.customerOrders') }}</QTooltip>
</q-btn> --> </QBtn> -->
</q-card-actions> </QCardActions>
</q-item> </QItem>
</q-card> </QCard>
</template> </template>
</paginate> </VnPaginate>
</div> </div>
</q-page> </QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -6,14 +6,14 @@ const stateStore = useStateStore();
</script> </script>
<template> <template>
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<LeftMenu /> <LeftMenu />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<router-view></router-view> <RouterView></RouterView>
</q-page-container> </QPageContainer>
</template> </template>
<style lang="scss"> <style lang="scss">

View File

@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useArrayData } from 'composables/useArrayData'; import { useArrayData } from 'composables/useArrayData';
import Paginate from 'components/PaginateData.vue'; import VnPaginate from 'components/ui/VnPaginate.vue';
import VnConfirm from 'components/ui/VnConfirm.vue'; import VnConfirm from 'components/ui/VnConfirm.vue';
import CustomerDescriptorProxy from './Card/CustomerDescriptorProxy.vue'; import CustomerDescriptorProxy from './Card/CustomerDescriptorProxy.vue';
import { toDate, toCurrency } from 'filters/index'; import { toDate, toCurrency } from 'filters/index';
@ -96,30 +96,30 @@ function stateColor(row) {
<template v-if="stateStore.isHeaderMounted()"> <template v-if="stateStore.isHeaderMounted()">
<Teleport to="#actions-append"> <Teleport to="#actions-append">
<div class="row q-gutter-x-sm"> <div class="row q-gutter-x-sm">
<q-btn <QBtn
flat flat
@click="stateStore.toggleRightDrawer()" @click="stateStore.toggleRightDrawer()"
round round
dense dense
icon="menu" icon="menu"
> >
<q-tooltip bottom anchor="bottom right"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</div> </div>
</Teleport> </Teleport>
</template> </template>
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<CustomerPaymentsFilter data-key="CustomerTransactions" /> <CustomerPaymentsFilter data-key="CustomerTransactions" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<div class="card-list"> <div class="card-list">
<q-toolbar class="q-pa-none"> <QToolbar class="q-pa-none">
<q-toolbar-title>{{ t('Web Payments') }}</q-toolbar-title> <QToolbarTitle>{{ t('Web Payments') }}</QToolbarTitle>
<q-btn <QBtn
@click="arrayData.refresh()" @click="arrayData.refresh()"
:loading="isLoading" :loading="isLoading"
icon="refresh" icon="refresh"
@ -127,12 +127,12 @@ function stateColor(row) {
class="q-mr-sm" class="q-mr-sm"
round round
dense dense
></q-btn> ></QBtn>
<q-btn @click="grid = !grid" icon="list" color="primary" round dense> <QBtn @click="grid = !grid" icon="list" color="primary" round dense>
<q-tooltip>{{ t('Change view') }}</q-tooltip> <QTooltip>{{ t('Change view') }}</QTooltip>
</q-btn> </QBtn>
</q-toolbar> </QToolbar>
<paginate <VnPaginate
data-key="CustomerTransactions" data-key="CustomerTransactions"
url="Clients/transactions" url="Clients/transactions"
order="created DESC" order="created DESC"
@ -141,7 +141,7 @@ function stateColor(row) {
auto-load auto-load
> >
<template #body="{ rows }"> <template #body="{ rows }">
<q-table <QTable
:dense="$q.screen.lt.md" :dense="$q.screen.lt.md"
:columns="columns" :columns="columns"
:rows="rows" :rows="rows"
@ -152,8 +152,8 @@ function stateColor(row) {
hide-pagination hide-pagination
> >
<template #body-cell-actions="{ row }"> <template #body-cell-actions="{ row }">
<q-td auto-width class="text-center"> <QTd auto-width class="text-center">
<q-btn <QBtn
v-if="!row.isConfirmed" v-if="!row.isConfirmed"
icon="check" icon="check"
@click="confirm(row)" @click="confirm(row)"
@ -163,87 +163,75 @@ function stateColor(row) {
flat flat
dense dense
> >
<q-tooltip>{{ t('Confirm transaction') }}</q-tooltip> <QTooltip>{{ t('Confirm transaction') }}</QTooltip>
</q-btn> </QBtn>
</q-td> </QTd>
</template> </template>
<template #body-cell-customerId="{ row }"> <template #body-cell-customerId="{ row }">
<q-td align="right"> <QTd align="right">
<span class="link"> <span class="link">
{{ row.clientFk }} {{ row.clientFk }}
<CustomerDescriptorProxy :id="row.clientFk" /> <CustomerDescriptorProxy :id="row.clientFk" />
</span> </span>
</q-td> </QTd>
</template> </template>
<template #body-cell-state="{ row }"> <template #body-cell-state="{ row }">
<q-td auto-width class="text-center"> <QTd auto-width class="text-center">
<q-badge :color="stateColor(row)"> <QBadge :color="stateColor(row)">
{{ {{
row.isConfirmed row.isConfirmed
? t('Confirmed') ? t('Confirmed')
: t('Unconfirmed') : t('Unconfirmed')
}} }}
</q-badge> </QBadge>
</q-td> </QTd>
</template> </template>
<template #item="{ cols, row }"> <template #item="{ cols, row }">
<div class="q-mb-md col-12"> <div class="q-mb-md col-12">
<q-card> <QCard>
<q-item class="q-pa-none items-start"> <QItem class="q-pa-none items-start">
<q-item-section class="q-pa-md"> <QItemSection class="q-pa-md">
<q-list> <QList>
<template <template
v-for="col of cols" v-for="col of cols"
:key="col.name" :key="col.name"
> >
<q-item <QItem
v-if="col.grid !== false" v-if="col.grid !== false"
class="q-pa-none" class="q-pa-none"
> >
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ col.label }} {{ col.label }}
</q-item-label> </QItemLabel>
<q-item-label <QItemLabel
v-if="col.name == 'state'" v-if="col.name == 'state'"
> >
<q-badge <QBadge
:color=" :color="
stateColor(row) stateColor(row)
" "
> >
{{ col.value }} {{ col.value }}
</q-badge> </QBadge>
</q-item-label> </QItemLabel>
<q-item-label <QItemLabel
v-if="col.name != 'state'" v-if="col.name != 'state'"
> >
{{ col.value }} {{ col.value }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</template> </template>
</q-list> </QList>
<!-- <q-list> </QItemSection>
<q-item class="q-pa-none">
<q-item-section>
<q-item-label caption>
<q-skeleton />
</q-item-label>
<q-item-label
><q-skeleton type="text"
/></q-item-label>
</q-item-section>
</q-item>
</q-list> -->
</q-item-section>
<template v-if="!row.isConfirmed"> <template v-if="!row.isConfirmed">
<q-separator vertical /> <QSeparator vertical />
<q-card-actions <QCardActions
vertical vertical
class="justify-between" class="justify-between"
> >
<q-btn <QBtn
icon="check" icon="check"
@click="confirm(row)" @click="confirm(row)"
color="primary" color="primary"
@ -252,21 +240,21 @@ function stateColor(row) {
flat flat
dense dense
> >
<q-tooltip>{{ <QTooltip>
t('Confirm transaction') {{ t('Confirm transaction') }}
}}</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</q-card-actions> </QCardActions>
</template> </template>
</q-item> </QItem>
</q-card> </QCard>
</div> </div>
</template> </template>
</q-table> </QTable>
</template> </template>
</paginate> </VnPaginate>
</div> </div>
</q-page> </QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -20,43 +20,43 @@ const props = defineProps({
</div> </div>
</template> </template>
<template #body="{ params }"> <template #body="{ params }">
<q-list dense> <QList dense>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Order ID')" :label="t('Order ID')"
v-model="params.orderFk" v-model="params.orderFk"
lazy-rules lazy-rules
> >
<template #prepend> <template #prepend>
<q-icon name="vn:basket" size="sm"></q-icon> <QIcon name="vn:basket" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Customer ID')" :label="t('Customer ID')"
v-model="params.clientFk" v-model="params.clientFk"
lazy-rules lazy-rules
> >
<template #prepend> <template #prepend>
<q-icon name="vn:client" size="sm"></q-icon> <QIcon name="vn:client" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input :label="t('Amount')" v-model="params.amount" lazy-rules> <QInput :label="t('Amount')" v-model="params.amount" lazy-rules>
<template #prepend> <template #prepend>
<q-icon name="euro" size="sm"></q-icon> <QIcon name="euro" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>

View File

@ -15,24 +15,24 @@ const pinnedModules = computed(() => navigation.getPinnedModules());
</script> </script>
<template> <template>
<q-drawer <QDrawer
v-model="stateStore.leftDrawer" v-model="stateStore.leftDrawer"
show-if-above show-if-above
:width="256" :width="256"
:breakpoint="1000" :breakpoint="1000"
> >
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<LeftMenu /> <LeftMenu />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<q-page class="q-pa-md"> <QPage class="q-pa-md">
<div class="row items-start wrap q-col-gutter-md q-mb-lg"> <div class="row items-start wrap q-col-gutter-md q-mb-lg">
<div class="col-12 col-md"> <div class="col-12 col-md">
<div class="text-h6 text-grey-8 q-mb-sm"> <div class="text-h6 text-grey-8 q-mb-sm">
{{ t('globals.pinnedModules') }} {{ t('globals.pinnedModules') }}
</div> </div>
<q-card class="row flex-container q-pa-md"> <QCard class="row flex-container q-pa-md">
<div class="text-grey-5" v-if="pinnedModules.length === 0"> <div class="text-grey-5" v-if="pinnedModules.length === 0">
{{ t('pinnedInfo') }} {{ t('pinnedInfo') }}
</div> </div>
@ -42,7 +42,7 @@ const pinnedModules = computed(() => navigation.getPinnedModules());
:key="item.title" :key="item.title"
class="row no-wrap q-pa-xs flex-item" class="row no-wrap q-pa-xs flex-item"
> >
<q-btn <QBtn
align="evenly" align="evenly"
padding="16px" padding="16px"
flat flat
@ -56,14 +56,14 @@ const pinnedModules = computed(() => navigation.getPinnedModules());
<div class="text-center text-primary button-text"> <div class="text-center text-primary button-text">
{{ t(item.title) }} {{ t(item.title) }}
</div> </div>
</q-btn> </QBtn>
</div> </div>
</template> </template>
</q-card> </QCard>
</div> </div>
</div> </div>
</q-page> </QPage>
</q-page-container> </QPageContainer>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -17,18 +17,18 @@ const { t } = useI18n();
:info="t('You can search by invoice reference')" :info="t('You can search by invoice reference')"
/> />
</Teleport> </Teleport>
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<q-scroll-area class="fit"> <QScrollArea class="fit">
<InvoiceOutDescriptor /> <InvoiceOutDescriptor />
<q-separator /> <QSeparator />
<left-menu source="card" /> <LeftMenu source="card" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<q-page class="q-pa-md"> <QPage class="q-pa-md">
<router-view></router-view> <RouterView></RouterView>
</q-page> </QPage>
</q-page-container> </QPageContainer>
</template> </template>
<i18n> <i18n>

View File

@ -45,7 +45,7 @@ function ticketFilter(invoice) {
</script> </script>
<template> <template>
<card-descriptor <CardDescriptor
ref="descriptor" ref="descriptor"
module="InvoiceOut" module="InvoiceOut"
:url="`InvoiceOuts/${entityId}`" :url="`InvoiceOuts/${entityId}`"
@ -54,54 +54,54 @@ function ticketFilter(invoice) {
<template #description="{ entity }"> <template #description="{ entity }">
<span> <span>
{{ entity.ref }} {{ entity.ref }}
<q-tooltip>{{ entity.ref }}</q-tooltip> <QTooltip>{{ entity.ref }}</QTooltip>
</span> </span>
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<q-list> <QList>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('invoiceOut.card.issued') }} {{ t('invoiceOut.card.issued') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ toDate(entity.issued) }}</q-item-label> <QItemLabel>{{ toDate(entity.issued) }}</QItemLabel>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('invoiceOut.card.amount') }} {{ t('invoiceOut.card.amount') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ toCurrency(entity.amount) }}</q-item-label> <QItemLabel>{{ toCurrency(entity.amount) }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="entity.client"> <QItemSection v-if="entity.client">
<q-item-label caption> <QItemLabel caption>
{{ t('invoiceOut.card.client') }} {{ t('invoiceOut.card.client') }}
</q-item-label> </QItemLabel>
<q-item-label class="link"> <QItemLabel class="link">
{{ entity.client.name }} {{ entity.client.name }}
<CustomerDescriptorProxy :id="entity.client.id" /> <CustomerDescriptorProxy :id="entity.client.id" />
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section v-if="entity.company"> <QItemSection v-if="entity.company">
<q-item-label caption>{{ <QItemLabel caption>{{
t('invoiceOut.card.company') t('invoiceOut.card.company')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ entity.company.code }}</q-item-label> <QItemLabel>{{ entity.company.code }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
<q-card-actions> <QCardActions>
<q-btn <QBtn
v-if="entity.client" v-if="entity.client"
size="md" size="md"
icon="vn:client" icon="vn:client"
color="primary" color="primary"
:to="{ name: 'CustomerCard', params: { id: entity.client.id } }" :to="{ name: 'CustomerCard', params: { id: entity.client.id } }"
> >
<q-tooltip>{{ t('invoiceOut.card.customerCard') }}</q-tooltip> <QTooltip>{{ t('invoiceOut.card.customerCard') }}</QTooltip>
</q-btn> </QBtn>
<q-btn <QBtn
size="md" size="md"
icon="vn:ticket" icon="vn:ticket"
color="primary" color="primary"
@ -110,9 +110,9 @@ function ticketFilter(invoice) {
query: { q: ticketFilter(entity) }, query: { q: ticketFilter(entity) },
}" }"
> >
<q-tooltip>{{ t('invoiceOut.card.ticketList') }}</q-tooltip> <QTooltip>{{ t('invoiceOut.card.ticketList') }}</QTooltip>
</q-btn> </QBtn>
</q-card-actions> </QCardActions>
</template> </template>
</card-descriptor> </CardDescriptor>
</template> </template>

View File

@ -9,7 +9,7 @@ const $props = defineProps({
}); });
</script> </script>
<template> <template>
<q-popup-proxy> <QPopupProxy>
<InvoiceOutDescriptor v-if="$props.id" :id="$props.id" /> <InvoiceOutDescriptor v-if="$props.id" :id="$props.id" />
</q-popup-proxy> </QPopupProxy>
</template> </template>

View File

@ -96,66 +96,84 @@ const ticketsColumns = ref([
<template> <template>
<div class="summary container"> <div class="summary container">
<q-card> <QCard>
<skeleton-summary v-if="!invoiceOut" /> <SkeletonSummary v-if="!invoiceOut" />
<template v-if="invoiceOut"> <template v-if="invoiceOut">
<div class="header bg-primary q-pa-sm q-mb-md"> <div class="header bg-primary q-pa-sm q-mb-md">
{{ invoiceOut.ref }} - {{ invoiceOut.client.socialName }} {{ invoiceOut.ref }} - {{ invoiceOut.client.socialName }}
</div> </div>
<q-list> <QList>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ t('invoiceOut.summary.issued') }}</q-item-label> <QItemLabel caption>{{
<q-item-label>{{ toDate(invoiceOut.issued) }}</q-item-label> t('invoiceOut.summary.issued')
</q-item-section> }}</QItemLabel>
<q-item-section> <QItemLabel>{{ toDate(invoiceOut.issued) }}</QItemLabel>
<q-item-label caption>{{ t('invoiceOut.summary.dued') }}</q-item-label> </QItemSection>
<q-item-label>{{ toDate(invoiceOut.dued) }}</q-item-label> <QItemSection>
</q-item-section> <QItemLabel caption>{{
</q-item> t('invoiceOut.summary.dued')
<q-item> }}</QItemLabel>
<q-item-section> <QItemLabel>{{ toDate(invoiceOut.dued) }}</QItemLabel>
<q-item-label caption>{{ t('invoiceOut.summary.created') }}</q-item-label> </QItemSection>
<q-item-label>{{ toDate(invoiceOut.created) }}</q-item-label> </QItem>
</q-item-section> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ t('invoiceOut.summary.booked') }}</q-item-label> <QItemLabel caption>{{
<q-item-label>{{ toDate(invoiceOut.booked) }}</q-item-label> t('invoiceOut.summary.created')
</q-item-section> }}</QItemLabel>
</q-item> <QItemLabel>{{ toDate(invoiceOut.created) }}</QItemLabel>
<q-item> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption>{{ t('invoiceOut.summary.company') }}</q-item-label> <QItemLabel caption>{{
<q-item-label>{{ invoiceOut.company.code }}</q-item-label> t('invoiceOut.summary.booked')
</q-item-section> }}</QItemLabel>
</q-item> <QItemLabel>{{ toDate(invoiceOut.booked) }}</QItemLabel>
</q-list> </QItemSection>
<q-card-section class="q-pa-md"> </QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('invoiceOut.summary.company')
}}</QItemLabel>
<QItemLabel>{{ invoiceOut.company.code }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
<QCardSection class="q-pa-md">
<h6>{{ t('invoiceOut.summary.taxBreakdown') }}</h6> <h6>{{ t('invoiceOut.summary.taxBreakdown') }}</h6>
<q-table :columns="taxColumns" :rows="tax" flat> <QTable :columns="taxColumns" :rows="tax" flat>
<template #header="props"> <template #header="props">
<q-tr :props="props"> <QTr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props"> <QTh
v-for="col in props.cols"
:key="col.name"
:props="props"
>
{{ t(col.label) }} {{ t(col.label) }}
</q-th> </QTh>
</q-tr> </QTr>
</template> </template>
</q-table> </QTable>
</q-card-section> </QCardSection>
<q-card-section class="q-pa-md"> <QCardSection class="q-pa-md">
<h6>{{ t('invoiceOut.summary.tickets') }}</h6> <h6>{{ t('invoiceOut.summary.tickets') }}</h6>
<q-table :columns="ticketsColumns" :rows="tikets" flat> <QTable :columns="ticketsColumns" :rows="tikets" flat>
<template #header="props"> <template #header="props">
<q-tr :props="props"> <QTr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props"> <QTh
v-for="col in props.cols"
:key="col.name"
:props="props"
>
{{ t(col.label) }} {{ t(col.label) }}
</q-th> </QTh>
</q-tr> </QTr>
</template> </template>
</q-table> </QTable>
</q-card-section> </QCardSection>
</template> </template>
</q-card> </QCard>
</div> </div>
</template> </template>

View File

@ -15,9 +15,9 @@ const { dialogRef, onDialogHide } = useDialogPluginComponent();
</script> </script>
<template> <template>
<q-dialog ref="dialogRef" @hide="onDialogHide"> <QDialog ref="dialogRef" @hide="onDialogHide">
<invoiceOut-summary v-if="$props.id" :id="$props.id" /> <InvoiceOutSummary v-if="$props.id" :id="$props.id" />
</q-dialog> </QDialog>
</template> </template>
<style lang="scss"> <style lang="scss">

View File

@ -23,8 +23,8 @@ function setWorkers(data) {
</script> </script>
<template> <template>
<fetch-data url="ClaimStates" @on-fetch="(data) => (states = data)" auto-load /> <FetchData url="ClaimStates" @on-fetch="(data) => (states = data)" auto-load />
<fetch-data <FetchData
url="Workers/activeWithInheritedRole" url="Workers/activeWithInheritedRole"
:filter="{ where: { role: 'salesPerson' } }" :filter="{ where: { role: 'salesPerson' } }"
@on-fetch="setWorkers" @on-fetch="setWorkers"
@ -38,101 +38,101 @@ function setWorkers(data) {
</div> </div>
</template> </template>
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<q-list dense> <QList dense>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Customer ID')" :label="t('Customer ID')"
v-model="params.clientFk" v-model="params.clientFk"
lazy-rules lazy-rules
> >
<template #prepend> <template #prepend>
<q-icon name="vn:client" size="sm"></q-icon> <QIcon name="vn:client" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input :label="t('FI')" v-model="params.fi" lazy-rules> <QInput :label="t('FI')" v-model="params.fi" lazy-rules>
<template #prepend> <template #prepend>
<q-icon name="badge" size="sm"></q-icon> <QIcon name="badge" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input :label="t('Amount')" v-model="params.amount" lazy-rules> <QInput :label="t('Amount')" v-model="params.amount" lazy-rules>
<template #prepend> <template #prepend>
<q-icon name="euro" size="sm"></q-icon> <QIcon name="euro" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Min')" :label="t('Min')"
type="number" type="number"
v-model.number="params.min" v-model.number="params.min"
lazy-rules lazy-rules
> >
<template #prepend> <template #prepend>
<q-icon name="euro" size="sm"></q-icon> <QIcon name="euro" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Max')" :label="t('Max')"
type="number" type="number"
v-model.number="params.max" v-model.number="params.max"
lazy-rules lazy-rules
> >
<template #prepend> <template #prepend>
<q-icon name="euro" size="sm"></q-icon> <QIcon name="euro" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-mb-md"> <QItem class="q-mb-md">
<q-item-section> <QItemSection>
<q-checkbox <QCheckbox
v-model="params.hasPdf" v-model="params.hasPdf"
@update:model-value="searchFn()" @update:model-value="searchFn()"
:label="t('Has PDF')" :label="t('Has PDF')"
toggle-indeterminate toggle-indeterminate
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-separator /> <QSeparator />
<q-expansion-item :label="t('More options')" expand-separator> <QExpansionItem :label="t('More options')" expand-separator>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Issued')" :label="t('Issued')"
v-model="params.issued" v-model="params.issued"
mask="date" mask="date"
> >
<template #append> <template #append>
<q-icon name="event" class="cursor-pointer"> <QIcon name="event" class="cursor-pointer">
<q-popup-proxy <QPopupProxy
cover cover
transition-show="scale" transition-show="scale"
transition-hide="scale" transition-hide="scale"
> >
<q-date v-model="params.issued" landscape> <QDate v-model="params.issued" landscape>
<div <div
class="row items-center justify-end q-gutter-sm" class="row items-center justify-end q-gutter-sm"
> >
<q-btn <QBtn
:label="t('globals.cancel')" :label="t('globals.cancel')"
color="primary" color="primary"
flat flat
v-close-popup v-close-popup
/> />
<q-btn <QBtn
:label="t('globals.confirm')" :label="t('globals.confirm')"
color="primary" color="primary"
flat flat
@ -140,38 +140,38 @@ function setWorkers(data) {
v-close-popup v-close-popup
/> />
</div> </div>
</q-date> </QDate>
</q-popup-proxy> </QPopupProxy>
</q-icon> </QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Created')" :label="t('Created')"
v-model="params.created" v-model="params.created"
mask="date" mask="date"
> >
<template #append> <template #append>
<q-icon name="event" class="cursor-pointer"> <QIcon name="event" class="cursor-pointer">
<q-popup-proxy <QPopupProxy
cover cover
transition-show="scale" transition-show="scale"
transition-hide="scale" transition-hide="scale"
> >
<q-date v-model="params.created" landscape> <QDate v-model="params.created" landscape>
<div <div
class="row items-center justify-end q-gutter-sm" class="row items-center justify-end q-gutter-sm"
> >
<q-btn <QBtn
:label="t('globals.cancel')" :label="t('globals.cancel')"
color="primary" color="primary"
flat flat
v-close-popup v-close-popup
/> />
<q-btn <QBtn
:label="t('globals.confirm')" :label="t('globals.confirm')"
color="primary" color="primary"
flat flat
@ -179,34 +179,34 @@ function setWorkers(data) {
v-close-popup v-close-popup
/> />
</div> </div>
</q-date> </QDate>
</q-popup-proxy> </QPopupProxy>
</q-icon> </QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input :label="t('Dued')" v-model="params.dued" mask="date"> <QInput :label="t('Dued')" v-model="params.dued" mask="date">
<template #append> <template #append>
<q-icon name="event" class="cursor-pointer"> <QIcon name="event" class="cursor-pointer">
<q-popup-proxy <QPopupProxy
cover cover
transition-show="scale" transition-show="scale"
transition-hide="scale" transition-hide="scale"
> >
<q-date v-model="params.dued" landscape> <QDate v-model="params.dued" landscape>
<div <div
class="row items-center justify-end q-gutter-sm" class="row items-center justify-end q-gutter-sm"
> >
<q-btn <QBtn
:label="t('globals.cancel')" :label="t('globals.cancel')"
color="primary" color="primary"
flat flat
v-close-popup v-close-popup
/> />
<q-btn <QBtn
:label="t('globals.confirm')" :label="t('globals.confirm')"
color="primary" color="primary"
flat flat
@ -214,15 +214,15 @@ function setWorkers(data) {
v-close-popup v-close-popup
/> />
</div> </div>
</q-date> </QDate>
</q-popup-proxy> </QPopupProxy>
</q-icon> </QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-expansion-item> </QExpansionItem>
</q-list> </QList>
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import Paginate from 'src/components/PaginateData.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import InvoiceOutSummaryDialog from './Card/InvoiceOutSummaryDialog.vue'; import InvoiceOutSummaryDialog from './Card/InvoiceOutSummaryDialog.vue';
import { toDate, toCurrency } from 'src/filters/index'; import { toDate, toCurrency } from 'src/filters/index';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
@ -19,7 +19,7 @@ onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false)); onUnmounted(() => (stateStore.rightDrawer = false));
function navigate(id) { function navigate(id) {
router.push({ path: `/invoiceOut/${id}` }); router.push({ path: `/invoice-out/${id}` });
} }
function viewSummary(id) { function viewSummary(id) {
@ -43,131 +43,129 @@ function viewSummary(id) {
</Teleport> </Teleport>
<Teleport to="#actions-append"> <Teleport to="#actions-append">
<div class="row q-gutter-x-sm"> <div class="row q-gutter-x-sm">
<q-btn <QBtn
flat flat
@click="stateStore.toggleRightDrawer()" @click="stateStore.toggleRightDrawer()"
round round
dense dense
icon="menu" icon="menu"
> >
<q-tooltip bottom anchor="bottom right"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</div> </div>
</Teleport> </Teleport>
</template> </template>
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<InvoiceOutFilter data-key="InvoiceOutList" /> <InvoiceOutFilter data-key="InvoiceOutList" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<div class="card-list"> <div class="card-list">
<paginate <VnPaginate
data-key="InvoiceOutList" data-key="InvoiceOutList"
url="InvoiceOuts/filter" url="InvoiceOuts/filter"
order="issued DESC, id DESC" order="issued DESC, id DESC"
auto-load auto-load
> >
<template #body="{ rows }"> <template #body="{ rows }">
<q-card class="card q-mb-md" v-for="row of rows" :key="row.id"> <QCard class="card q-mb-md" v-for="row of rows" :key="row.id">
<q-item <QItem
class="q-pa-none items-start cursor-pointer q-hoverable" class="q-pa-none items-start cursor-pointer q-hoverable"
v-ripple v-ripple
clickable clickable
> >
<q-item-section class="q-pa-md" @click="navigate(row.id)"> <QItemSection class="q-pa-md" @click="navigate(row.id)">
<div class="text-h6">{{ row.ref }}</div> <div class="text-h6">{{ row.ref }}</div>
<q-item-label caption>#{{ row.id }}</q-item-label> <QItemLabel caption>#{{ row.id }}</QItemLabel>
<q-list> <QList>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('invoiceOut.list.issued') }} {{ t('invoiceOut.list.issued') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toDate(row.issued) }} {{ toDate(row.issued) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('invoiceOut.list.amount') }} {{ t('invoiceOut.list.amount') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toCurrency(row.amount) }} {{ toCurrency(row.amount) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('invoiceOut.list.client') }} {{ t('invoiceOut.list.client') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ row.clientSocialName }} {{ row.clientSocialName }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('invoiceOut.list.created') }} {{ t('invoiceOut.list.created') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toDate(row.created) }} {{ toDate(row.created) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('invoiceOut.list.company') }} {{ t('invoiceOut.list.company') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ <QItemLabel>{{ row.companyCode }}</QItemLabel>
row.companyCode </QItemSection>
}}</q-item-label> <QItemSection>
</q-item-section> <QItemLabel caption>
<q-item-section>
<q-item-label caption>
{{ t('invoiceOut.list.dued') }} {{ t('invoiceOut.list.dued') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toDate(row.dued) }} {{ toDate(row.dued) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-item-section> </QItemSection>
<q-separator vertical /> <QSeparator vertical />
<q-card-actions vertical class="justify-between"> <QCardActions vertical class="justify-between">
<q-btn <QBtn
flat flat
round round
color="orange" color="orange"
icon="arrow_circle_right" icon="arrow_circle_right"
@click="navigate(row.id)" @click="navigate(row.id)"
> >
<q-tooltip> <QTooltip>
{{ t('components.smartCard.openCard') }} {{ t('components.smartCard.openCard') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
<q-btn <QBtn
flat flat
round round
color="grey-7" color="grey-7"
icon="preview" icon="preview"
@click="viewSummary(row.id)" @click="viewSummary(row.id)"
> >
<q-tooltip> <QTooltip>
{{ t('components.smartCard.openSummary') }} {{ t('components.smartCard.openSummary') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</q-card-actions> </QCardActions>
</q-item> </QItem>
</q-card> </QCard>
</template> </template>
</paginate> </VnPaginate>
</div> </div>
</q-page> </QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -6,12 +6,12 @@ const stateStore = useStateStore();
</script> </script>
<template> <template>
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<LeftMenu /> <LeftMenu />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<router-view></router-view> <RouterView></RouterView>
</q-page-container> </QPageContainer>
</template> </template>

View File

@ -72,12 +72,12 @@ async function onSubmit() {
</script> </script>
<template> <template>
<q-layout> <QLayout>
<q-page-container> <QPageContainer>
<q-page id="login"> <QPage id="login">
<q-page-sticky position="top-right"> <QPageSticky position="top-right">
<q-toolbar> <QToolbar>
<q-btn <QBtn
id="switchLanguage" id="switchLanguage"
:label="t('globals.language')" :label="t('globals.language')"
icon="translate" icon="translate"
@ -86,55 +86,55 @@ async function onSubmit() {
flat flat
rounded rounded
> >
<q-menu auto-close> <QMenu auto-close>
<q-list dense> <QList dense>
<q-item <QItem
@click="userLocale = 'en'" @click="userLocale = 'en'"
:active="userLocale == 'en'" :active="userLocale == 'en'"
v-ripple v-ripple
clickable clickable
> >
{{ t('globals.lang.en') }} {{ t('globals.lang.en') }}
</q-item> </QItem>
<q-item <QItem
@click="userLocale = 'es'" @click="userLocale = 'es'"
:active="userLocale == 'es'" :active="userLocale == 'es'"
v-ripple v-ripple
clickable clickable
> >
{{ t('globals.lang.es') }} {{ t('globals.lang.es') }}
</q-item> </QItem>
</q-list> </QList>
</q-menu> </QMenu>
</q-btn> </QBtn>
<q-list> <QList>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>
t(`globals.darkMode`) {{ t(`globals.darkMode`) }}
}}</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section side> <QItemSection side>
<q-toggle <QToggle
v-model="darkMode" v-model="darkMode"
checked-icon="dark_mode" checked-icon="dark_mode"
unchecked-icon="light_mode" unchecked-icon="light_mode"
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-toolbar> </QToolbar>
</q-page-sticky> </QPageSticky>
<div class="login-form q-pa-xl"> <div class="login-form q-pa-xl">
<q-img <QImg
src="~/assets/logo.svg" src="~/assets/logo.svg"
alt="Logo" alt="Logo"
fit="contain" fit="contain"
:ratio="16 / 9" :ratio="16 / 9"
class="q-mb-md" class="q-mb-md"
/> />
<q-form @submit="onSubmit" class="q-gutter-md"> <QForm @submit="onSubmit" class="q-gutter-md">
<q-input <QInput
v-model="username" v-model="username"
:label="t('login.username')" :label="t('login.username')"
lazy-rules lazy-rules
@ -143,7 +143,7 @@ async function onSubmit() {
(val && val.length > 0) || t('login.fieldRequired'), (val && val.length > 0) || t('login.fieldRequired'),
]" ]"
/> />
<q-input <QInput
type="password" type="password"
v-model="password" v-model="password"
:label="t('login.password')" :label="t('login.password')"
@ -153,10 +153,10 @@ async function onSubmit() {
(val && val.length > 0) || t('login.fieldRequired'), (val && val.length > 0) || t('login.fieldRequired'),
]" ]"
/> />
<q-toggle v-model="keepLogin" :label="t('login.keepLogin')" /> <QToggle v-model="keepLogin" :label="t('login.keepLogin')" />
<div> <div>
<q-btn <QBtn
:label="t('login.submit')" :label="t('login.submit')"
type="submit" type="submit"
color="primary" color="primary"
@ -165,11 +165,11 @@ async function onSubmit() {
unelevated unelevated
/> />
</div> </div>
</q-form> </QForm>
</div> </div>
</q-page> </QPage>
</q-page-container> </QPageContainer>
</q-layout> </QLayout>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -9,12 +9,14 @@ const { t } = useI18n();
<div class="text-center q-pa-md flex flex-center"> <div class="text-center q-pa-md flex flex-center">
<div> <div>
<div class="text-grey-5" style="opacity: 0.4; font-size: 30vh"> <div class="text-grey-5" style="opacity: 0.4; font-size: 30vh">
<q-icon name="vn:claims" /> <QIcon name="vn:claims" />
</div> </div>
<div class="text-h2 text-grey-5" style="opacity: 0.4">{{ t('notFound') }}</div> <div class="text-h2 text-grey-5" style="opacity: 0.4">
{{ t('notFound') }}
</div>
<q-btn <QBtn
@click="router.go(-1)" @click="router.go(-1)"
:label="t('globals.back')" :label="t('globals.back')"
class="q-mt-xl" class="q-mt-xl"

View File

@ -1,3 +1,3 @@
<template> <template>
<q-card>Basic Data</q-card> <QCard>Basic Data</QCard>
</template> </template>

View File

@ -80,15 +80,17 @@ async function getVideoList(expeditionId, timed) {
<template> <template>
<teleport to=".q-layout"> <teleport to=".q-layout">
<q-drawer show-if-above side="right"> <QDrawer show-if-above side="right">
<q-scroll-area class="fit"> <QScrollArea class="fit">
<q-list bordered separator style="max-width: 318px"> <QList bordered separator style="max-width: 318px">
<q-item v-if="lastExpedition && videoList.length"> <QItem v-if="lastExpedition && videoList.length">
<q-item-section> <QItemSection>
<q-item-label class="text-h6"> <QItemLabel class="text-h6">
{{ t('ticket.boxing.selectTime') }} ({{ time.min }}-{{ time.max }}) {{ t('ticket.boxing.selectTime') }} ({{ time.min }}-{{
</q-item-label> time.max
<q-range }})
</QItemLabel>
<QRange
v-model="time" v-model="time"
@change="getVideoList(lastExpedition, time)" @change="getVideoList(lastExpedition, time)"
:min="0" :min="0"
@ -101,11 +103,11 @@ async function getVideoList(expeditionId, timed) {
snap snap
color="orange" color="orange"
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="lastExpedition && videoList.length"> <QItem v-if="lastExpedition && videoList.length">
<q-item-section> <QItemSection>
<q-select <QSelect
color="orange" color="orange"
v-model="slide" v-model="slide"
:options="videoList" :options="videoList"
@ -114,42 +116,55 @@ async function getVideoList(expeditionId, timed) {
map-options map-options
> >
<template #prepend> <template #prepend>
<q-icon name="schedule" /> <QIcon name="schedule" />
</template> </template>
</q-select> </QSelect>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item <QItem
v-for="expedition in expeditions" v-for="expedition in expeditions"
:key="expedition.id" :key="expedition.id"
@click="getVideoList(expedition.id)" @click="getVideoList(expedition.id)"
clickable clickable
v-ripple v-ripple
> >
<q-item-section> <QItemSection>
<q-item-label class="text-h6">#{{ expedition.id }}</q-item-label> <QItemLabel class="text-h6">#{{ expedition.id }}</QItemLabel>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption>{{ t('ticket.boxing.created') }}</q-item-label> <QItemLabel caption>{{
<q-item-label> t('ticket.boxing.created')
{{ date.formatDate(expedition.created, 'YYYY-MM-DD HH:mm:ss') }} }}</QItemLabel>
</q-item-label> <QItemLabel>
<q-item-label caption>{{ t('ticket.boxing.item') }}</q-item-label> {{
<q-item-label>{{ expedition.packagingItemFk }}</q-item-label> date.formatDate(
<q-item-label caption>{{ t('ticket.boxing.worker') }}</q-item-label> expedition.created,
<q-item-label>{{ expedition.userName }}</q-item-label> 'YYYY-MM-DD HH:mm:ss'
</q-item-section> )
</q-item> }}
</q-list> </QItemLabel>
</q-scroll-area> <QItemLabel caption>{{ t('ticket.boxing.item') }}</QItemLabel>
</q-drawer> <QItemLabel>{{ expedition.packagingItemFk }}</QItemLabel>
<QItemLabel caption>{{
t('ticket.boxing.worker')
}}</QItemLabel>
<QItemLabel>{{ expedition.userName }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</QScrollArea>
</QDrawer>
</teleport> </teleport>
<q-card> <QCard>
<q-carousel animated v-model="slide" height="max-content"> <QCarousel animated v-model="slide" height="max-content">
<q-carousel-slide v-for="video in videoList" :key="video.value" :name="video.value"> <QCarouselSlide
<q-video :src="video.url" :ratio="16 / 9" /> v-for="video in videoList"
</q-carousel-slide> :key="video.value"
</q-carousel> :name="video.value"
</q-card> >
<QVideo :src="video.url" :ratio="16 / 9" />
</QCarouselSlide>
</QCarousel>
</QCard>
</template> </template>

View File

@ -17,18 +17,18 @@ const { t } = useI18n();
:info="t('You can search by ticket id or alias')" :info="t('You can search by ticket id or alias')"
/> />
</Teleport> </Teleport>
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<q-scroll-area class="fit"> <QScrollArea class="fit">
<TicketDescriptor /> <TicketDescriptor />
<q-separator /> <QSeparator />
<left-menu source="card" /> <LeftMenu source="card" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<q-page class="q-pa-md"> <QPage class="q-pa-md">
<router-view></router-view> <RouterView></RouterView>
</q-page> </QPage>
</q-page-container> </QPageContainer>
</template> </template>
<style lang="scss"> <style lang="scss">

View File

@ -75,91 +75,91 @@ function stateColor(state) {
</script> </script>
<template> <template>
<card-descriptor module="Ticket" :url="`Tickets/${entityId}`" :filter="filter"> <CardDescriptor module="Ticket" :url="`Tickets/${entityId}`" :filter="filter">
<template #menu="{ entity }"> <template #menu="{ entity }">
<TicketDescriptorMenu :ticket="entity" /> <TicketDescriptorMenu :ticket="entity" />
</template> </template>
<template #description="{ entity }"> <template #description="{ entity }">
<span> <span>
{{ entity.client.name }} {{ entity.client.name }}
<q-tooltip>{{ entity.client.name }}</q-tooltip> <QTooltip>{{ entity.client.name }}</QTooltip>
</span> </span>
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<q-list> <QList>
<q-item> <QItem>
<q-item-section v-if="entity.ticketState"> <QItemSection v-if="entity.ticketState">
<q-item-label caption>{{ t('ticket.card.state') }}</q-item-label> <QItemLabel caption>{{ t('ticket.card.state') }}</QItemLabel>
<q-item-label :class="stateColor(entity.ticketState.state)"> <QItemLabel :class="stateColor(entity.ticketState.state)">
{{ entity.ticketState.state.name }} {{ entity.ticketState.state.name }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('ticket.card.shipped') }} {{ t('ticket.card.shipped') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ toDate(entity.shipped) }}</q-item-label> <QItemLabel>{{ toDate(entity.shipped) }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('ticket.card.customerId') }} {{ t('ticket.card.customerId') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
<span class="link"> <span class="link">
{{ entity.clientFk }} {{ entity.clientFk }}
<CustomerDescriptorProxy :id="entity.client.id" /> <CustomerDescriptorProxy :id="entity.client.id" />
</span> </span>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section v-if="entity.client && entity.client.salesPersonUser"> <QItemSection v-if="entity.client && entity.client.salesPersonUser">
<q-item-label caption> <QItemLabel caption>
{{ t('ticket.card.salesPerson') }} {{ t('ticket.card.salesPerson') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ entity.client.salesPersonUser.name }} {{ entity.client.salesPersonUser.name }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="entity.warehouse"> <QItemSection v-if="entity.warehouse">
<q-item-label caption> <QItemLabel caption>
{{ t('ticket.card.warehouse') }} {{ t('ticket.card.warehouse') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.warehouse.name }}</q-item-label> <QItemLabel>{{ entity.warehouse.name }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item v-if="entity.agencyMode"> <QItem v-if="entity.agencyMode">
<q-item-section> <QItemSection>
<q-item-label caption>{{ t('ticket.card.agency') }}</q-item-label> <QItemLabel caption>{{ t('ticket.card.agency') }}</QItemLabel>
<q-item-label>{{ entity.agencyMode.name }}</q-item-label> <QItemLabel>{{ entity.agencyMode.name }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
<q-card-actions class="q-gutter-md"> <QCardActions class="q-gutter-md">
<q-icon <QIcon
v-if="entity.isDeleted == true" v-if="entity.isDeleted == true"
name="vn:deletedTicket" name="vn:deletedTicket"
size="xs" size="xs"
color="primary" color="primary"
> >
<q-tooltip>{{ t('This ticket is deleted') }}</q-tooltip> <QTooltip>{{ t('This ticket is deleted') }}</QTooltip>
</q-icon> </QIcon>
</q-card-actions> </QCardActions>
<q-card-actions> <QCardActions>
<q-btn <QBtn
size="md" size="md"
icon="vn:client" icon="vn:client"
color="primary" color="primary"
:to="{ name: 'CustomerCard', params: { id: entity.clientFk } }" :to="{ name: 'CustomerCard', params: { id: entity.clientFk } }"
> >
<q-tooltip>{{ t('ticket.card.customerCard') }}</q-tooltip> <QTooltip>{{ t('ticket.card.customerCard') }}</QTooltip>
</q-btn> </QBtn>
</q-card-actions> </QCardActions>
</template> </template>
</card-descriptor> </CardDescriptor>
</template> </template>
<i18n> <i18n>

View File

@ -135,106 +135,106 @@ async function remove() {
} }
</script> </script>
<template> <template>
<q-item v-ripple clickable> <QItem v-ripple clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="picture_as_pdf" /> <QIcon name="picture_as_pdf" />
</q-item-section> </QItemSection>
<q-item-section>{{ t('Open Delivery Note...') }}</q-item-section> <QItemSection>{{ t('Open Delivery Note...') }}</QItemSection>
<q-item-section side> <QItemSection side>
<q-icon name="keyboard_arrow_right" /> <QIcon name="keyboard_arrow_right" />
</q-item-section> </QItemSection>
<q-menu anchor="top end" self="top start" auto-close bordered> <QMenu anchor="top end" self="top start" auto-close bordered>
<q-list> <QList>
<q-item @click="openDeliveryNote('deliveryNote')" v-ripple clickable> <QItem @click="openDeliveryNote('deliveryNote')" v-ripple clickable>
<q-item-section>{{ t('With prices') }}</q-item-section> <QItemSection>{{ t('With prices') }}</QItemSection>
</q-item> </QItem>
<q-item @click="openDeliveryNote('withoutPrices')" v-ripple clickable> <QItem @click="openDeliveryNote('withoutPrices')" v-ripple clickable>
<q-item-section>{{ t('Without prices') }}</q-item-section> <QItemSection>{{ t('Without prices') }}</QItemSection>
</q-item> </QItem>
<q-item <QItem
@click="openDeliveryNote('deliveryNote', 'csv')" @click="openDeliveryNote('deliveryNote', 'csv')"
v-ripple v-ripple
clickable clickable
> >
<q-item-section>{{ t('As CSV') }}</q-item-section> <QItemSection>{{ t('As CSV') }}</QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-menu> </QMenu>
</q-item> </QItem>
<q-item v-ripple clickable> <QItem v-ripple clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="send" /> <QIcon name="send" />
</q-item-section> </QItemSection>
<q-item-section>{{ t('Send Delivery Note...') }}</q-item-section> <QItemSection>{{ t('Send Delivery Note...') }}</QItemSection>
<q-item-section side> <QItemSection side>
<q-icon name="keyboard_arrow_right" /> <QIcon name="keyboard_arrow_right" />
</q-item-section> </QItemSection>
<q-menu anchor="top end" self="top start" auto-close> <QMenu anchor="top end" self="top start" auto-close>
<q-list> <QList>
<q-item <QItem
@click="sendDeliveryNoteConfirmation('deliveryNote')" @click="sendDeliveryNoteConfirmation('deliveryNote')"
v-ripple v-ripple
clickable clickable
> >
<q-item-section>{{ t('With prices') }}</q-item-section> <QItemSection>{{ t('With prices') }}</QItemSection>
</q-item> </QItem>
<q-item <QItem
@click="sendDeliveryNoteConfirmation('withoutPrices')" @click="sendDeliveryNoteConfirmation('withoutPrices')"
v-ripple v-ripple
clickable clickable
> >
<q-item-section>{{ t('Without prices') }}</q-item-section> <QItemSection>{{ t('Without prices') }}</QItemSection>
</q-item> </QItem>
<q-item <QItem
@click="sendDeliveryNoteConfirmation('deliveryNote', 'csv')" @click="sendDeliveryNoteConfirmation('deliveryNote', 'csv')"
v-ripple v-ripple
clickable clickable
> >
<q-item-section>{{ t('As CSV') }}</q-item-section> <QItemSection>{{ t('As CSV') }}</QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-menu> </QMenu>
</q-item> </QItem>
<q-item @click="openDeliveryNote('proforma')" v-ripple clickable> <QItem @click="openDeliveryNote('proforma')" v-ripple clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="receipt" /> <QIcon name="receipt" />
</q-item-section> </QItemSection>
<q-item-section>{{ t('Open Proforma Invoice') }}</q-item-section> <QItemSection>{{ t('Open Proforma Invoice') }}</QItemSection>
</q-item> </QItem>
<q-item v-ripple clickable> <QItem v-ripple clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="sms" /> <QIcon name="sms" />
</q-item-section> </QItemSection>
<q-item-section>{{ t('Send SMS...') }}</q-item-section> <QItemSection>{{ t('Send SMS...') }}</QItemSection>
<q-item-section side> <QItemSection side>
<q-icon name="keyboard_arrow_right" /> <QIcon name="keyboard_arrow_right" />
</q-item-section> </QItemSection>
<q-menu anchor="top end" self="top start" auto-close> <QMenu anchor="top end" self="top start" auto-close>
<q-list> <QList>
<q-item @click="showSmsDialog('pendingPayment')" v-ripple clickable> <QItem @click="showSmsDialog('pendingPayment')" v-ripple clickable>
<q-item-section>{{ t('Pending payment') }}</q-item-section> <QItemSection>{{ t('Pending payment') }}</QItemSection>
</q-item> </QItem>
<q-item @click="showSmsDialog('minAmount')" v-ripple clickable> <QItem @click="showSmsDialog('minAmount')" v-ripple clickable>
<q-item-section>{{ t('Minimum amount') }}</q-item-section> <QItemSection>{{ t('Minimum amount') }}</QItemSection>
</q-item> </QItem>
<q-item <QItem
@click="showSmsDialogWithChanges('orderChanges')" @click="showSmsDialogWithChanges('orderChanges')"
v-ripple v-ripple
clickable clickable
> >
<q-item-section>{{ t('Order changes') }}</q-item-section> <QItemSection>{{ t('Order changes') }}</QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-menu> </QMenu>
</q-item> </QItem>
<template v-if="!ticket.isDeleted"> <template v-if="!ticket.isDeleted">
<q-separator /> <QSeparator />
<q-item @click="confirmDelete()" v-ripple clickable> <QItem @click="confirmDelete()" v-ripple clickable>
<q-item-section avatar> <QItemSection avatar>
<q-icon name="delete" /> <QIcon name="delete" />
</q-item-section> </QItemSection>
<q-item-section>{{ t('Delete ticket') }}</q-item-section> <QItemSection>{{ t('Delete ticket') }}</QItemSection>
</q-item> </QItem>
</template> </template>
</template> </template>

View File

@ -9,7 +9,7 @@ const $props = defineProps({
}); });
</script> </script>
<template> <template>
<q-popup-proxy> <QPopupProxy>
<TicketDescriptor v-if="$props.id" :id="$props.id" /> <TicketDescriptor v-if="$props.id" :id="$props.id" />
</q-popup-proxy> </QPopupProxy>
</template> </template>

View File

@ -81,14 +81,14 @@ async function changeState(value) {
</script> </script>
<template> <template>
<fetch-data <FetchData
url="States/editableStates" url="States/editableStates"
@on-fetch="(data) => (editableStates = data)" @on-fetch="(data) => (editableStates = data)"
auto-load auto-load
/> />
<div class="summary container"> <div class="summary container">
<q-card> <QCard>
<skeleton-summary v-if="!ticket" /> <SkeletonSummary v-if="!ticket" />
<template v-if="ticket"> <template v-if="ticket">
<div class="header bg-primary q-pa-sm q-mb-md"> <div class="header bg-primary q-pa-sm q-mb-md">
<span> <span>
@ -97,7 +97,7 @@ async function changeState(value) {
}}) - }}) -
{{ ticket.nickname }} {{ ticket.nickname }}
</span> </span>
<q-btn-dropdown <QBtnDropdown
side side
top top
color="orange-11" color="orange-11"
@ -105,284 +105,270 @@ async function changeState(value) {
:label="t('ticket.summary.changeState')" :label="t('ticket.summary.changeState')"
:disable="!isEditable()" :disable="!isEditable()"
> >
<q-list> <QList>
<q-virtual-scroll <QVirtualScroll
style="max-height: 300px" style="max-height: 300px"
:items="editableStates" :items="editableStates"
separator separator
v-slot="{ item, index }" v-slot="{ item, index }"
> >
<q-item <QItem
:key="index" :key="index"
dense dense
clickable clickable
v-close-popup v-close-popup
@click="changeState(item.code)" @click="changeState(item.code)"
> >
<q-item-section> <QItemSection>
<q-item-label>{{ item.name }}</q-item-label> <QItemLabel>{{ item.name }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-virtual-scroll> </QVirtualScroll>
</q-list> </QList>
</q-btn-dropdown> </QBtnDropdown>
</div> </div>
<div class="row q-pa-md q-col-gutter-md q-mb-md"> <div class="row q-pa-md q-col-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-list> <QList>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>
t('ticket.summary.state') {{ t('ticket.summary.state') }}
}}</q-item-label> </QItemLabel>
<q-item-label <QItemLabel
:class="stateColor(ticket.ticketState.state)" :class="stateColor(ticket.ticketState.state)"
> >
{{ ticket.ticketState.state.name }} {{ ticket.ticketState.state.name }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('ticket.summary.salesPerson') t('ticket.summary.salesPerson')
}}</q-item-label> }}</QItemLabel>
<q-item-label> <QItemLabel>
<span class="link"> <span class="link">
{{ ticket.client.salesPersonUser.name }} {{ ticket.client.salesPersonUser.name }}
<WorkerDescriptorProxy <WorkerDescriptorProxy
:id="ticket.client.salesPersonFk" :id="ticket.client.salesPersonFk"
/> />
</span> </span>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('ticket.summary.agency') t('ticket.summary.agency')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{ ticket.agencyMode.name }}</QItemLabel>
ticket.agencyMode.name </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem>
</q-item> <QItemSection>
<q-item> <QItemLabel caption>{{
<q-item-section>
<q-item-label caption>{{
t('ticket.summary.zone') t('ticket.summary.zone')
}}</q-item-label> }}</QItemLabel>
<q-item-label class="link">{{ <QItemLabel class="link">{{
ticket.routeFk ticket.routeFk
}}</q-item-label> }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('ticket.summary.warehouse') t('ticket.summary.warehouse')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{ ticket.warehouse.name }}</QItemLabel>
ticket.warehouse.name </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem>
</q-item> <QItemSection>
<q-item> <QItemLabel caption>{{
<q-item-section>
<q-item-label caption>{{
t('ticket.summary.invoice') t('ticket.summary.invoice')
}}</q-item-label> }}</QItemLabel>
<q-item-label v-if="ticket.refFk"> <QItemLabel v-if="ticket.refFk">
<span class="link"> <span class="link">
{{ ticket.refFk }} {{ ticket.refFk }}
<InvoiceOutDescriptorProxy :id="ticket.id" /> <InvoiceOutDescriptorProxy :id="ticket.id" />
</span> </span>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col"> <div class="col">
<q-list> <QList>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('ticket.summary.shipped') t('ticket.summary.shipped')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{ toDate(ticket.shipped) }}</QItemLabel>
toDate(ticket.shipped) </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem>
</q-item> <QItemSection>
<q-item> <QItemLabel caption>{{
<q-item-section>
<q-item-label caption>{{
t('ticket.summary.landed') t('ticket.summary.landed')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{ toDate(ticket.landed) }}</QItemLabel>
toDate(ticket.landed) </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem>
</q-item> <QItemSection>
<q-item> <QItemLabel caption>{{
<q-item-section>
<q-item-label caption>{{
t('ticket.summary.packages') t('ticket.summary.packages')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ ticket.packages }}</q-item-label> <QItemLabel>{{ ticket.packages }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('ticket.summary.consigneePhone') t('ticket.summary.consigneePhone')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{ ticket.address.phone }}</QItemLabel>
ticket.address.phone </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem>
</q-item> <QItemSection>
<q-item> <QItemLabel caption>{{
<q-item-section>
<q-item-label caption>{{
t('ticket.summary.consigneeMobile') t('ticket.summary.consigneeMobile')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{ ticket.address.mobile }}</QItemLabel>
ticket.address.mobile </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem>
</q-item> <QItemSection>
<q-item> <QItemLabel caption>{{
<q-item-section>
<q-item-label caption>{{
t('ticket.summary.clientPhone') t('ticket.summary.clientPhone')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ ticket.client.phone }}</q-item-label> <QItemLabel>{{ ticket.client.phone }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('ticket.summary.clientMobile') t('ticket.summary.clientMobile')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{ ticket.client.mobile }}</QItemLabel>
ticket.client.mobile </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem>
</q-item> <QItemSection>
<q-item> <QItemLabel caption>{{
<q-item-section>
<q-item-label caption>{{
t('ticket.summary.consignee') t('ticket.summary.consignee')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ formattedAddress() }}</q-item-label> <QItemLabel>{{ formattedAddress() }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col"> <div class="col">
<q-list> <QList>
<q-item v-for="note in ticket.notes" :key="note.id"> <QItem v-for="note in ticket.notes" :key="note.id">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ note.observationType.description }} {{ note.observationType.description }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ note.description }} {{ note.description }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col"> <div class="col">
<q-list class="taxes"> <QList class="taxes">
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('ticket.summary.subtotal') t('ticket.summary.subtotal')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{
toCurrency(ticket.totalWithoutVat) toCurrency(ticket.totalWithoutVat)
}}</q-item-label> }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('ticket.summary.vat') t('ticket.summary.vat')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{
toCurrency( toCurrency(
ticket.totalWithVat - ticket.totalWithoutVat ticket.totalWithVat - ticket.totalWithoutVat
) )
}}</q-item-label> }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('ticket.summary.total') t('ticket.summary.total')
}}</q-item-label> }}</QItemLabel>
<q-item-label>{{ <QItemLabel>{{
toCurrency(ticket.totalWithVat) toCurrency(ticket.totalWithVat)
}}</q-item-label> }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
</div> </div>
<div class="row q-pa-md" v-if="salesLines.length > 0"> <div class="row q-pa-md" v-if="salesLines.length > 0">
<div class="col"> <div class="col">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('ticket.summary.saleLines') }} {{ t('ticket.summary.saleLines') }}
<router-link <RouterLink
:to="{ :to="{
name: 'TicketBasicData', name: 'TicketBasicData',
params: { id: entityId }, params: { id: entityId },
}" }"
target="_blank" target="_blank"
> >
<q-icon name="open_in_new" /> <QIcon name="open_in_new" />
</router-link> </RouterLink>
</q-item-label> </QItemLabel>
<q-table :rows="ticket.sales" flat> <QTable :rows="ticket.sales" flat>
<template #header="props"> <template #header="props">
<q-tr :props="props"> <QTr :props="props">
<q-th auto-width></q-th> <QTh auto-width></QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.item') t('ticket.summary.item')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.visible') t('ticket.summary.visible')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.available') t('ticket.summary.available')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.quantity') t('ticket.summary.quantity')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.description') t('ticket.summary.description')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.price') t('ticket.summary.price')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.discount') t('ticket.summary.discount')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.amount') t('ticket.summary.amount')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.packing') t('ticket.summary.packing')
}}</q-th> }}</QTh>
</q-tr> </QTr>
</template> </template>
<template #body="props"> <template #body="props">
<q-tr :props="props"> <QTr :props="props">
<q-td> <QTd>
<q-btn <QBtn
flat flat
round round
size="xs" size="xs"
@ -396,14 +382,14 @@ async function changeState(value) {
}, },
}" }"
> >
<q-tooltip <QTooltip
>{{ t('ticket.summary.claim') }}: >{{ t('ticket.summary.claim') }}:
{{ {{
props.row.claim.claimFk props.row.claim.claimFk
}}</q-tooltip }}</QTooltip
> >
</q-btn> </QBtn>
<q-btn <QBtn
flat flat
round round
size="xs" size="xs"
@ -418,60 +404,64 @@ async function changeState(value) {
}, },
}" }"
> >
<q-tooltip <QTooltip
>{{ t('ticket.summary.claim') }}: >{{ t('ticket.summary.claim') }}:
{{ {{
props.row.claimBeginning.claimFk props.row.claimBeginning.claimFk
}}</q-tooltip }}</QTooltip
> >
</q-btn> </QBtn>
<q-icon <QIcon
name="warning" name="warning"
v-show="props.row.visible < 0" v-show="props.row.visible < 0"
size="xs" size="xs"
color="primary" color="primary"
> >
<q-tooltip <QTooltip
>{{ t('ticket.summary.visible') }}: >{{ t('ticket.summary.visible') }}:
{{ props.row.visible }}</q-tooltip {{ props.row.visible }}</QTooltip
> >
</q-icon> </QIcon>
<q-icon <QIcon
name="vn:reserva" name="vn:reserva"
v-show="props.row.reserved" v-show="props.row.reserved"
size="xs" size="xs"
color="primary" color="primary"
> >
<q-tooltip>{{ <QTooltip>
t('ticket.summary.reserved') {{ t('ticket.summary.reserved') }}
}}</q-tooltip> </QTooltip>
</q-icon> </QIcon>
<q-icon <QIcon
name="vn:unavailable" name="vn:unavailable"
v-show="props.row.itemShortage" v-show="props.row.itemShortage"
size="xs" size="xs"
color="primary" color="primary"
> >
<q-tooltip>{{ <QTooltip>
t('ticket.summary.itemShortage') {{ t('ticket.summary.itemShortage') }}
}}</q-tooltip> </QTooltip>
</q-icon> </QIcon>
<q-icon <QIcon
name="vn:components" name="vn:components"
v-show="props.row.hasComponentLack" v-show="props.row.hasComponentLack"
size="xs" size="xs"
color="primary" color="primary"
> >
<q-tooltip>{{ <QTooltip>
t('ticket.summary.hasComponentLack') {{
}}</q-tooltip> t(
</q-icon> 'ticket.summary.hasComponentLack'
</q-td> )
<q-td class="link">{{ props.row.itemFk }}</q-td> }}
<q-td>{{ props.row.visible }}</q-td> </QTooltip>
<q-td>{{ props.row.available }}</q-td> </QIcon>
<q-td>{{ props.row.quantity }}</q-td> </QTd>
<q-td> <QTd class="link">{{ props.row.itemFk }}</QTd>
<QTd>{{ props.row.visible }}</QTd>
<QTd>{{ props.row.available }}</QTd>
<QTd>{{ props.row.quantity }}</QTd>
<QTd>
<div class="fetched-tags"> <div class="fetched-tags">
<span>{{ props.row.item.name }}</span> <span>{{ props.row.item.name }}</span>
<span <span
@ -484,10 +474,10 @@ async function changeState(value) {
:item="props.row.item" :item="props.row.item"
:max-length="5" :max-length="5"
></fetched-tags> ></fetched-tags>
</q-td> </QTd>
<q-td>{{ props.row.price }}</q-td> <QTd>{{ props.row.price }}</QTd>
<q-td>{{ props.row.discount }} %</q-td> <QTd>{{ props.row.discount }} %</QTd>
<q-td <QTd
>{{ >{{
toCurrency( toCurrency(
props.row.quantity * props.row.quantity *
@ -495,14 +485,14 @@ async function changeState(value) {
((100 - props.row.discount) / 100) ((100 - props.row.discount) / 100)
) )
}} }}
</q-td> </QTd>
<q-td>{{ <QTd>{{
dashIfEmpty(props.row.item.itemPackingTypeFk) dashIfEmpty(props.row.item.itemPackingTypeFk)
}}</q-td> }}</QTd>
</q-tr> </QTr>
</template> </template>
</q-table> </QTable>
</q-list> </QList>
</div> </div>
</div> </div>
<div <div
@ -510,137 +500,137 @@ async function changeState(value) {
v-if="ticket.packagings.length > 0 || ticket.services.length > 0" v-if="ticket.packagings.length > 0 || ticket.services.length > 0"
> >
<div class="col" v-if="ticket.packagings.length > 0"> <div class="col" v-if="ticket.packagings.length > 0">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('ticket.summary.packages') }} {{ t('ticket.summary.packages') }}
<q-icon name="open_in_new" /> <QIcon name="open_in_new" />
</q-item-label> </QItemLabel>
<q-table :rows="ticket.packagings" flat> <QTable :rows="ticket.packagings" flat>
<template #header="props"> <template #header="props">
<q-tr :props="props"> <QTr :props="props">
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.created') t('ticket.summary.created')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.package') t('ticket.summary.package')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.quantity') t('ticket.summary.quantity')
}}</q-th> }}</QTh>
</q-tr> </QTr>
</template> </template>
<template #body="props"> <template #body="props">
<q-tr :props="props"> <QTr :props="props">
<q-td>{{ toDate(props.row.created) }}</q-td> <QTd>{{ toDate(props.row.created) }}</QTd>
<q-td>{{ props.row.packaging.item.name }}</q-td> <QTd>{{ props.row.packaging.item.name }}</QTd>
<q-td>{{ props.row.quantity }}</q-td> <QTd>{{ props.row.quantity }}</QTd>
</q-tr> </QTr>
</template> </template>
</q-table> </QTable>
</q-list> </QList>
</div> </div>
<div class="col" v-if="ticket.services.length > 0"> <div class="col" v-if="ticket.services.length > 0">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('ticket.summary.services') }} {{ t('ticket.summary.services') }}
<q-icon name="open_in_new" /> <QIcon name="open_in_new" />
</q-item-label> </QItemLabel>
<q-table :rows="ticket.services" flat> <QTable :rows="ticket.services" flat>
<template #header="props"> <template #header="props">
<q-tr :props="props"> <QTr :props="props">
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.quantity') t('ticket.summary.quantity')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.description') t('ticket.summary.description')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.price') t('ticket.summary.price')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.taxClass') t('ticket.summary.taxClass')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.amount') t('ticket.summary.amount')
}}</q-th> }}</QTh>
</q-tr> </QTr>
</template> </template>
<template #body="props"> <template #body="props">
<q-tr :props="props"> <QTr :props="props">
<q-td>{{ props.row.quantity }}</q-td> <QTd>{{ props.row.quantity }}</QTd>
<q-td>{{ props.row.description }}</q-td> <QTd>{{ props.row.description }}</QTd>
<q-td>{{ toCurrency(props.row.price) }}</q-td> <QTd>{{ toCurrency(props.row.price) }}</QTd>
<q-td>{{ props.row.taxClass.description }}</q-td> <QTd>{{ props.row.taxClass.description }}</QTd>
<q-td>{{ <QTd>{{
toCurrency( toCurrency(
props.row.quantity * props.row.price props.row.quantity * props.row.price
) )
}}</q-td> }}</QTd>
</q-tr> </QTr>
</template> </template>
</q-table> </QTable>
</q-list> </QList>
</div> </div>
</div> </div>
<div class="row q-pa-md" v-if="ticket.requests.length > 0"> <div class="row q-pa-md" v-if="ticket.requests.length > 0">
<div class="col"> <div class="col">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('ticket.summary.request') }} {{ t('ticket.summary.request') }}
<q-icon name="open_in_new" /> <QIcon name="open_in_new" />
</q-item-label> </QItemLabel>
<q-table :rows="ticket.requests" flat> <QTable :rows="ticket.requests" flat>
<template #header="props"> <template #header="props">
<q-tr :props="props"> <QTr :props="props">
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.description') t('ticket.summary.description')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.created') t('ticket.summary.created')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.requester') t('ticket.summary.requester')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.atender') t('ticket.summary.atender')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.quantity') t('ticket.summary.quantity')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.price') t('ticket.summary.price')
}}</q-th> }}</QTh>
<q-th auto-width>{{ <QTh auto-width>{{
t('ticket.summary.item') t('ticket.summary.item')
}}</q-th> }}</QTh>
<q-th auto-width>Ok</q-th> <QTh auto-width>Ok</QTh>
</q-tr> </QTr>
</template> </template>
<template #body="props"> <template #body="props">
<q-tr :props="props"> <QTr :props="props">
<q-td>{{ props.row.description }}</q-td> <QTd>{{ props.row.description }}</QTd>
<q-td>{{ toDate(props.row.created) }}</q-td> <QTd>{{ toDate(props.row.created) }}</QTd>
<q-td>{{ props.row.requester.user.name }}</q-td> <QTd>{{ props.row.requester.user.name }}</QTd>
<q-td>{{ props.row.atender.user.name }}</q-td> <QTd>{{ props.row.atender.user.name }}</QTd>
<q-td>{{ props.row.quantity }}</q-td> <QTd>{{ props.row.quantity }}</QTd>
<q-td>{{ toCurrency(props.row.price) }}</q-td> <QTd>{{ toCurrency(props.row.price) }}</QTd>
<q-td v-if="!props.row.sale">-</q-td> <QTd v-if="!props.row.sale">-</QTd>
<q-td v-if="props.row.sale" class="link">{{ <QTd v-if="props.row.sale" class="link">{{
props.row.sale.itemFk props.row.sale.itemFk
}}</q-td> }}</QTd>
<q-td <QTd
><q-checkbox ><QCheckbox
v-model="props.row.isOk" v-model="props.row.isOk"
:disable="true" :disable="true"
/></q-td> /></QTd>
</q-tr> </QTr>
</template> </template>
</q-table> </QTable>
</q-list> </QList>
</div> </div>
</div> </div>
</template> </template>
</q-card> </QCard>
</div> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -15,9 +15,9 @@ const { dialogRef, onDialogHide } = useDialogPluginComponent();
</script> </script>
<template> <template>
<q-dialog ref="dialogRef" @hide="onDialogHide"> <QDialog ref="dialogRef" @hide="onDialogHide">
<ticket-summary v-if="$props.id" :id="$props.id" /> <TicketSummary v-if="$props.id" :id="$props.id" />
</q-dialog> </QDialog>
</template> </template>
<style lang="scss"> <style lang="scss">

View File

@ -13,9 +13,9 @@ const props = defineProps({
}, },
}); });
const from = Date.vnNew() const from = Date.vnNew();
const to = Date.vnNew(); const to = Date.vnNew();
to.setDate(to.getDate() + 1) to.setDate(to.getDate() + 1);
const defaultParams = { const defaultParams = {
from: toDateString(from), from: toDateString(from),
@ -30,11 +30,11 @@ const warehouses = ref();
</script> </script>
<template> <template>
<fetch-data url="Provinces" @on-fetch="(data) => (provinces = data)" auto-load /> <FetchData url="Provinces" @on-fetch="(data) => (provinces = data)" auto-load />
<fetch-data url="States" @on-fetch="(data) => (states = data)" auto-load /> <FetchData url="States" @on-fetch="(data) => (states = data)" auto-load />
<fetch-data url="AgencyModes" @on-fetch="(data) => (agencies = data)" auto-load /> <FetchData url="AgencyModes" @on-fetch="(data) => (agencies = data)" auto-load />
<fetch-data url="Warehouses" @on-fetch="(data) => (warehouses = data)" auto-load /> <FetchData url="Warehouses" @on-fetch="(data) => (warehouses = data)" auto-load />
<fetch-data <FetchData
url="Workers/activeWithInheritedRole" url="Workers/activeWithInheritedRole"
:filter="{ where: { role: 'salesPerson' } }" :filter="{ where: { role: 'salesPerson' } }"
@on-fetch="(data) => (workers = data)" @on-fetch="(data) => (workers = data)"
@ -52,76 +52,76 @@ const warehouses = ref();
</div> </div>
</template> </template>
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<q-list dense> <QList dense>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
v-model="params.clientFk" v-model="params.clientFk"
:label="t('Customer ID')" :label="t('Customer ID')"
lazy-rules lazy-rules
/> />
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-input <QInput
v-model="params.orderFk" v-model="params.orderFk"
:label="t('Order ID')" :label="t('Order ID')"
lazy-rules lazy-rules
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input v-model="params.from" :label="t('From')" mask="date"> <QInput v-model="params.from" :label="t('From')" mask="date">
<template #append> <template #append>
<q-icon name="event" class="cursor-pointer"> <QIcon name="event" class="cursor-pointer">
<q-popup-proxy <QPopupProxy
cover cover
transition-show="scale" transition-show="scale"
transition-hide="scale" transition-hide="scale"
> >
<q-date v-model="params.from" landscape> <QDate v-model="params.from" landscape>
<div <div
class="row items-center justify-end q-gutter-sm" class="row items-center justify-end q-gutter-sm"
> >
<q-btn <QBtn
:label="t('globals.cancel')" :label="t('globals.cancel')"
color="primary" color="primary"
flat flat
v-close-popup v-close-popup
/> />
<q-btn <QBtn
:label="t('globals.confirm')" :label="t('globals.confirm')"
color="primary" color="primary"
flat flat
v-close-popup v-close-popup
/> />
</div> </div>
</q-date> </QDate>
</q-popup-proxy> </QPopupProxy>
</q-icon> </QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-input v-model="params.to" :label="t('To')" mask="date"> <QInput v-model="params.to" :label="t('To')" mask="date">
<template #append> <template #append>
<q-icon name="event" class="cursor-pointer"> <QIcon name="event" class="cursor-pointer">
<q-popup-proxy <QPopupProxy
cover cover
transition-show="scale" transition-show="scale"
transition-hide="scale" transition-hide="scale"
> >
<q-date v-model="params.to" landscape> <QDate v-model="params.to" landscape>
<div <div
class="row items-center justify-end q-gutter-sm" class="row items-center justify-end q-gutter-sm"
> >
<q-btn <QBtn
:label="t('globals.cancel')" :label="t('globals.cancel')"
color="primary" color="primary"
flat flat
v-close-popup v-close-popup
/> />
<q-btn <QBtn
:label="t('globals.confirm')" :label="t('globals.confirm')"
color="primary" color="primary"
flat flat
@ -129,19 +129,19 @@ const warehouses = ref();
v-close-popup v-close-popup
/> />
</div> </div>
</q-date> </QDate>
</q-popup-proxy> </QPopupProxy>
</q-icon> </QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!workers"> <QItemSection v-if="!workers">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="workers"> <QItemSection v-if="workers">
<q-select <QSelect
:label="t('Salesperson')" :label="t('Salesperson')"
v-model="params.salesPersonFk" v-model="params.salesPersonFk"
:options="workers" :options="workers"
@ -152,14 +152,14 @@ const warehouses = ref();
use-input use-input
:input-debounce="0" :input-debounce="0"
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!states"> <QItemSection v-if="!states">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="states"> <QItemSection v-if="states">
<q-select <QSelect
:label="t('State')" :label="t('State')"
v-model="params.stateFk" v-model="params.stateFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -169,71 +169,71 @@ const warehouses = ref();
emit-value emit-value
map-options map-options
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
v-model="params.refFk" v-model="params.refFk"
:label="t('Invoice Ref.')" :label="t('Invoice Ref.')"
lazy-rules lazy-rules
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-checkbox <QCheckbox
v-model="params.myTeam" v-model="params.myTeam"
@update:model-value="searchFn()" @update:model-value="searchFn()"
:label="t('My team')" :label="t('My team')"
toggle-indeterminate toggle-indeterminate
/> />
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-checkbox <QCheckbox
v-model="params.pending" v-model="params.pending"
@update:model-value="searchFn()" @update:model-value="searchFn()"
:label="t('Pending')" :label="t('Pending')"
toggle-indeterminate toggle-indeterminate
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-checkbox <QCheckbox
v-model="params.hasInvoice" v-model="params.hasInvoice"
@update:model-value="searchFn()" @update:model-value="searchFn()"
:label="t('Invoiced')" :label="t('Invoiced')"
toggle-indeterminate toggle-indeterminate
/> />
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-checkbox <QCheckbox
v-model="params.hasRoute" v-model="params.hasRoute"
@update:model-value="searchFn()" @update:model-value="searchFn()"
:label="t('Routed')" :label="t('Routed')"
toggle-indeterminate toggle-indeterminate
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-checkbox <QCheckbox
v-model="params.problems" v-model="params.problems"
@update:model-value="searchFn()" @update:model-value="searchFn()"
:label="t('With problems')" :label="t('With problems')"
toggle-indeterminate toggle-indeterminate
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-separator /> <QSeparator />
<q-expansion-item :label="t('More options')" expand-separator> <QExpansionItem :label="t('More options')" expand-separator>
<q-item> <QItem>
<q-item-section v-if="!provinces"> <QItemSection v-if="!provinces">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="provinces"> <QItemSection v-if="provinces">
<q-select <QSelect
:label="t('Province')" :label="t('Province')"
v-model="params.provinceFk" v-model="params.provinceFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -243,14 +243,14 @@ const warehouses = ref();
emit-value emit-value
map-options map-options
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!states"> <QItemSection v-if="!agencies">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="!states"> <QItemSection v-if="agencies">
<q-select <QSelect
:label="t('Agency')" :label="t('Agency')"
v-model="params.agencyModeFk" v-model="params.agencyModeFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -260,14 +260,14 @@ const warehouses = ref();
emit-value emit-value
map-options map-options
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!warehouses"> <QItemSection v-if="!warehouses">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="warehouses"> <QItemSection v-if="warehouses">
<q-select <QSelect
:label="t('Warehouse')" :label="t('Warehouse')"
v-model="params.warehouseFk" v-model="params.warehouseFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -277,10 +277,10 @@ const warehouses = ref();
emit-value emit-value
map-options map-options
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-expansion-item> </QExpansionItem>
</q-list> </QList>
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import Paginate from 'src/components/PaginateData.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import { toDate, toDateString, toCurrency } from 'src/filters/index'; import { toDate, toDateString, toCurrency } from 'src/filters/index';
import TicketSummaryDialog from './Card/TicketSummaryDialog.vue'; import TicketSummaryDialog from './Card/TicketSummaryDialog.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
@ -88,28 +88,28 @@ function viewSummary(id) {
</Teleport> </Teleport>
<Teleport to="#actions-append"> <Teleport to="#actions-append">
<div class="row q-gutter-x-sm"> <div class="row q-gutter-x-sm">
<q-btn <QBtn
flat flat
@click="stateStore.toggleRightDrawer()" @click="stateStore.toggleRightDrawer()"
round round
dense dense
icon="menu" icon="menu"
> >
<q-tooltip bottom anchor="bottom right"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</div> </div>
</Teleport> </Teleport>
</template> </template>
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<TicketFilter data-key="TicketList" /> <TicketFilter data-key="TicketList" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<div class="card-list"> <div class="card-list">
<paginate <VnPaginate
data-key="TicketList" data-key="TicketList"
url="Tickets/filter" url="Tickets/filter"
:filter="filter" :filter="filter"
@ -118,109 +118,109 @@ function viewSummary(id) {
auto-load auto-load
> >
<template #body="{ rows }"> <template #body="{ rows }">
<q-card class="card q-mb-md" v-for="row of rows" :key="row.id"> <QCard class="card q-mb-md" v-for="row of rows" :key="row.id">
<q-item <QItem
class="q-pa-none items-start cursor-pointer q-hoverable" class="q-pa-none items-start cursor-pointer q-hoverable"
v-ripple v-ripple
clickable clickable
> >
<q-item-section class="q-pa-md" @click="navigate(row.id)"> <QItemSection class="q-pa-md" @click="navigate(row.id)">
<div class="text-h6">{{ row.name }}</div> <div class="text-h6">{{ row.name }}</div>
<q-item-label caption>#{{ row.id }}</q-item-label> <QItemLabel caption>#{{ row.id }}</QItemLabel>
<q-list> <QList>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('ticket.list.nickname') }} {{ t('ticket.list.nickname') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ row.nickname }} {{ row.nickname }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('ticket.list.state') }} {{ t('ticket.list.state') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
<q-badge <QBadge
:color="stateColor(row)" :color="stateColor(row)"
class="q-ma-none" class="q-ma-none"
dense dense
> >
{{ row.state }} {{ row.state }}
</q-badge> </QBadge>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('ticket.list.shipped') }} {{ t('ticket.list.shipped') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toDate(row.shipped) }} {{ toDate(row.shipped) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('Zone') }} {{ t('Zone') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ row.zoneName }} {{ row.zoneName }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('ticket.list.salesPerson') }} {{ t('ticket.list.salesPerson') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ row.salesPerson }} {{ row.salesPerson }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('ticket.list.total') }} {{ t('ticket.list.total') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ toCurrency(row.totalWithVat) }} {{ toCurrency(row.totalWithVat) }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-item-section> </QItemSection>
<q-separator vertical /> <QSeparator vertical />
<q-card-actions vertical class="justify-between"> <QCardActions vertical class="justify-between">
<q-btn <QBtn
flat flat
round round
color="orange" color="orange"
icon="arrow_circle_right" icon="arrow_circle_right"
@click="navigate(row.id)" @click="navigate(row.id)"
> >
<q-tooltip> <QTooltip>
{{ t('components.smartCard.openCard') }} {{ t('components.smartCard.openCard') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
<q-btn <QBtn
flat flat
round round
color="grey-7" color="grey-7"
icon="preview" icon="preview"
@click="viewSummary(row.id)" @click="viewSummary(row.id)"
> >
<q-tooltip> <QTooltip>
{{ t('components.smartCard.openSummary') }} {{ t('components.smartCard.openSummary') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</q-card-actions> </QCardActions>
</q-item> </QItem>
</q-card> </QCard>
</template> </template>
</paginate> </VnPaginate>
</div> </div>
</q-page> </QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -6,12 +6,12 @@ const stateStore = useStateStore();
</script> </script>
<template> <template>
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<LeftMenu /> <LeftMenu />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<router-view></router-view> <RouterView></RouterView>
</q-page-container> </QPageContainer>
</template> </template>

View File

@ -17,18 +17,18 @@ const { t } = useI18n();
:info="t('You can search by worker id or name')" :info="t('You can search by worker id or name')"
/> />
</Teleport> </Teleport>
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<q-scroll-area class="fit"> <QScrollArea class="fit">
<WorkerDescriptor /> <WorkerDescriptor />
<q-separator /> <QSeparator />
<left-menu source="card" /> <LeftMenu source="card" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<q-page class="q-pa-md"> <QPage class="q-pa-md">
<router-view></router-view> <RouterView></RouterView>
</q-page> </QPage>
</q-page-container> </QPageContainer>
</template> </template>
<i18n> <i18n>

View File

@ -54,21 +54,21 @@ function getWorkerAvatar() {
} }
</script> </script>
<template> <template>
<card-descriptor <CardDescriptor
module="Worker" module="Worker"
:url="`Workers/${entityId}`" :url="`Workers/${entityId}`"
:filter="filter" :filter="filter"
@on-fetch="(data) => (worker = data)" @on-fetch="(data) => (worker = data)"
> >
<template #before> <template #before>
<q-img :src="getWorkerAvatar()" class="photo"> <QImg :src="getWorkerAvatar()" class="photo">
<template #error> <template #error>
<div <div
class="absolute-full bg-grey-10 text-center q-pa-md flex flex-center" class="absolute-full bg-grey-10 text-center q-pa-md flex flex-center"
> >
<div> <div>
<div class="text-grey-5" style="opacity: 0.4; font-size: 5vh"> <div class="text-grey-5" style="opacity: 0.4; font-size: 5vh">
<q-icon name="vn:claims" /> <QIcon name="vn:claims" />
</div> </div>
<div class="text-grey-5" style="opacity: 0.4"> <div class="text-grey-5" style="opacity: 0.4">
{{ t('worker.imageNotFound') }} {{ t('worker.imageNotFound') }}
@ -76,59 +76,59 @@ function getWorkerAvatar() {
</div> </div>
</div> </div>
</template> </template>
</q-img> </QImg>
</template> </template>
<template #description="{ entity }"> <template #description="{ entity }">
<span> <span>
{{ entity.user.nickname }} {{ entity.user.nickname }}
<q-tooltip>{{ entity.user.nickname }}</q-tooltip> <QTooltip>{{ entity.user.nickname }}</QTooltip>
</span> </span>
</template> </template>
<template #body="{ entity }"> <template #body="{ entity }">
<q-list> <QList>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> {{ t('worker.card.name') }} </q-item-label> <QItemLabel caption> {{ t('worker.card.name') }} </QItemLabel>
<q-item-label>{{ entity.user.nickname }}</q-item-label> <QItemLabel>{{ entity.user.nickname }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('worker.card.email') }} {{ t('worker.card.email') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.user.email }}</q-item-label> <QItemLabel>{{ entity.user.email }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('worker.list.department') }} {{ t('worker.list.department') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ entity.department.department.name }} {{ entity.department.department.name }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('worker.card.phone') }} {{ t('worker.card.phone') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ entity.phone }}</q-item-label> <QItemLabel>{{ entity.phone }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption <QItemLabel caption
>{{ t('worker.summary.sipExtension') }} >{{ t('worker.summary.sipExtension') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ sip }}</q-item-label> <QItemLabel>{{ sip }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</template> </template>
</card-descriptor> </CardDescriptor>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -10,7 +10,7 @@ const $props = defineProps({
</script> </script>
<template> <template>
<q-popup-proxy> <QPopupProxy>
<WorkerDescriptor v-if="$props.id" :id="$props.id" /> <WorkerDescriptor v-if="$props.id" :id="$props.id" />
</q-popup-proxy> </QPopupProxy>
</template> </template>

View File

@ -75,7 +75,7 @@ function sipExtension() {
<template> <template>
<div class="summary container"> <div class="summary container">
<q-card> <QCard>
<SkeletonSummary v-if="!worker" /> <SkeletonSummary v-if="!worker" />
<template v-if="worker"> <template v-if="worker">
<div class="header bg-primary q-pa-sm q-mb-md"> <div class="header bg-primary q-pa-sm q-mb-md">
@ -83,144 +83,140 @@ function sipExtension() {
</div> </div>
<div class="row q-pa-md q-col-gutter-md q-mb-md"> <div class="row q-pa-md q-col-gutter-md q-mb-md">
<div class="col"> <div class="col">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('worker.summary.basicData') }} {{ t('worker.summary.basicData') }}
</q-item-label> </QItemLabel>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> ID </q-item-label> <QItemLabel caption> ID </QItemLabel>
<q-item-label>{{ worker.id }}</q-item-label> <QItemLabel>{{ worker.id }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption <QItemLabel caption
>{{ t('worker.card.name') }} >{{ t('worker.card.name') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ worker.user.nickname }} {{ worker.user.nickname }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption <QItemLabel caption
>{{ t('worker.list.department') }} >{{ t('worker.list.department') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ <QItemLabel>{{
worker.department.department.name worker.department.department.name
}}</q-item-label> }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption <QItemLabel caption
>{{ t('worker.list.email') }} >{{ t('worker.list.email') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ worker.user.email }}</q-item-label> <QItemLabel>{{ worker.user.email }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item <QItem
class="items-start cursor-pointer q-hoverable" class="items-start cursor-pointer q-hoverable"
v-if="worker.boss" v-if="worker.boss"
> >
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('worker.summary.boss') }} {{ t('worker.summary.boss') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
<span class="link"> <span class="link">
{{ worker.boss.name }} {{ worker.boss.name }}
<WorkerDescriptorProxy :id="worker.bossFk" /> <WorkerDescriptorProxy :id="worker.bossFk" />
</span> </span>
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption <QItemLabel caption
>{{ t('worker.summary.phoneExtension') }} >{{ t('worker.summary.phoneExtension') }}
</q-item-label> </QItemLabel>
<q-item-label> <QItemLabel>
{{ {{
worker.mobileExtension == '' worker.mobileExtension == ''
? worker.mobileExtension ? worker.mobileExtension
: '-' : '-'
}} }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption <QItemLabel caption
>{{ t('worker.summary.entPhone') }} >{{ t('worker.summary.entPhone') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ <QItemLabel>{{
worker.phone == '' ? worker.phone : '-' worker.phone == '' ? worker.phone : '-'
}}</q-item-label> }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption <QItemLabel caption
>{{ t('worker.summary.personalPhone') }} >{{ t('worker.summary.personalPhone') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ <QItemLabel>{{
worker.client.phone == '' worker.client.phone == ''
? worker.client.phone ? worker.client.phone
: '-' : '-'
}}</q-item-label> }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
<div class="col"> <div class="col">
<q-list> <QList>
<q-item-label header class="text-h6"> <QItemLabel header class="text-h6">
{{ t('worker.summary.userData') }} {{ t('worker.summary.userData') }}
</q-item-label> </QItemLabel>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('worker.summary.userId') }} {{ t('worker.summary.userId') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ worker.user.id }}</q-item-label> <QItemLabel>{{ worker.user.id }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-item-label caption <QItemLabel caption
>{{ t('worker.card.name') }} >{{ t('worker.card.name') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ <QItemLabel>{{ worker.user.nickname }}</QItemLabel>
worker.user.nickname </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem>
</q-item> <QItemSection>
<q-item> <QItemLabel caption
<q-item-section>
<q-item-label caption
>{{ t('worker.summary.role') }} >{{ t('worker.summary.role') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ <QItemLabel>{{ worker.user.role.name }}</QItemLabel>
worker.user.role.name </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem>
</q-item> <QItemSection>
<q-item> <QItemLabel caption
<q-item-section>
<q-item-label caption
>{{ t('worker.summary.sipExtension') }} >{{ t('worker.summary.sipExtension') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ sipExtension() }}</q-item-label> <QItemLabel>{{ sipExtension() }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</div> </div>
</div> </div>
</template> </template>
</q-card> </QCard>
</div> </div>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -15,7 +15,7 @@ const { dialogRef, onDialogHide } = useDialogPluginComponent();
</script> </script>
<template> <template>
<q-dialog ref="dialogRef" @hide="onDialogHide"> <QDialog ref="dialogRef" @hide="onDialogHide">
<worker-summary v-if="$props.id" :id="$props.id" /> <WorkerSummary v-if="$props.id" :id="$props.id" />
</q-dialog> </QDialog>
</template> </template>

View File

@ -16,7 +16,7 @@ const departments = ref();
</script> </script>
<template> <template>
<fetch-data url="Departments" @on-fetch="(data) => (departments = data)" auto-load /> <FetchData url="Departments" @on-fetch="(data) => (departments = data)" auto-load />
<VnFilterPanel :data-key="props.dataKey" :search-button="true"> <VnFilterPanel :data-key="props.dataKey" :search-button="true">
<template #tags="{ tag, formatFn }"> <template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs"> <div class="q-gutter-x-xs">
@ -25,49 +25,49 @@ const departments = ref();
</div> </div>
</template> </template>
<template #body="{ params, searchFn }"> <template #body="{ params, searchFn }">
<q-list dense> <QList dense>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input :label="t('FI')" v-model="params.fi" lazy-rules> <QInput :label="t('FI')" v-model="params.fi" lazy-rules>
<template #prepend> <template #prepend>
<q-icon name="badge" size="sm"></q-icon> <QIcon name="badge" size="sm"></QIcon>
</template> </template>
</q-input> </QInput>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('First Name')" :label="t('First Name')"
v-model="params.firstName" v-model="params.firstName"
lazy-rules lazy-rules
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Last Name')" :label="t('Last Name')"
v-model="params.lastName" v-model="params.lastName"
lazy-rules lazy-rules
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('User Name')" :label="t('User Name')"
v-model="params.userName" v-model="params.userName"
lazy-rules lazy-rules
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item> <QItem>
<q-item-section v-if="!departments"> <QItemSection v-if="!departments">
<q-skeleton type="QInput" class="full-width" /> <QSkeleton type="QInput" class="full-width" />
</q-item-section> </QItemSection>
<q-item-section v-if="departments"> <QItemSection v-if="departments">
<q-select <QSelect
:label="t('Department')" :label="t('Department')"
v-model="params.departmentFk" v-model="params.departmentFk"
@update:model-value="searchFn()" @update:model-value="searchFn()"
@ -79,18 +79,18 @@ const departments = ref();
use-input use-input
:input-debounce="0" :input-debounce="0"
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-mb-md"> <QItem class="q-mb-md">
<q-item-section> <QItemSection>
<q-input <QInput
:label="t('Extension')" :label="t('Extension')"
v-model="params.extension" v-model="params.extension"
lazy-rules lazy-rules
/> />
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</template> </template>
</VnFilterPanel> </VnFilterPanel>
</template> </template>

View File

@ -3,7 +3,7 @@ import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import Paginate from 'src/components/PaginateData.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
import WorkerSummaryDialog from './Card/WorkerSummaryDialog.vue'; import WorkerSummaryDialog from './Card/WorkerSummaryDialog.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import WorkerFilter from './WorkerFilter.vue'; import WorkerFilter from './WorkerFilter.vue';
@ -38,107 +38,105 @@ function viewSummary(id) {
</Teleport> </Teleport>
<Teleport to="#actions-append"> <Teleport to="#actions-append">
<div class="row q-gutter-x-sm"> <div class="row q-gutter-x-sm">
<q-btn <QBtn
flat flat
@click="stateStore.toggleRightDrawer()" @click="stateStore.toggleRightDrawer()"
round round
dense dense
icon="menu" icon="menu"
> >
<q-tooltip bottom anchor="bottom right"> <QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }} {{ t('globals.collapseMenu') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</div> </div>
</Teleport> </Teleport>
</template> </template>
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<WorkerFilter data-key="WorkerList" /> <WorkerFilter data-key="WorkerList" />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page class="column items-center q-pa-md"> <QPage class="column items-center q-pa-md">
<div class="card-list"> <div class="card-list">
<paginate <VnPaginate
data-key="WorkerList" data-key="WorkerList"
url="Workers/filter" url="Workers/filter"
order="id DESC" order="id DESC"
auto-load auto-load
> >
<template #body="{ rows }"> <template #body="{ rows }">
<q-card class="card q-mb-md" v-for="row of rows" :key="row.id"> <QCard class="card q-mb-md" v-for="row of rows" :key="row.id">
<q-item <QItem
class="q-pa-none items-start cursor-pointer q-hoverable" class="q-pa-none items-start cursor-pointer q-hoverable"
v-ripple v-ripple
clickable clickable
> >
<q-item-section class="q-pa-md" @click="navigate(row.id)"> <QItemSection class="q-pa-md" @click="navigate(row.id)">
<q-item-label class="text-h6"> <QItemLabel class="text-h6">
{{ row.nickname }} {{ row.nickname }}
</q-item-label> </QItemLabel>
<q-item-label caption>#{{ row.id }}</q-item-label> <QItemLabel caption>#{{ row.id }}</QItemLabel>
<q-list> <QList>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption> <QItemLabel caption>
{{ t('worker.list.name') }} {{ t('worker.list.name') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ <QItemLabel>{{ row.userName }}</QItemLabel>
row.userName </QItemSection>
}}</q-item-label> </QItem>
</q-item-section> <QItem class="q-pa-none">
</q-item> <QItemSection>
<q-item class="q-pa-none"> <QItemLabel caption>
<q-item-section>
<q-item-label caption>
{{ t('worker.list.email') }} {{ t('worker.list.email') }}
</q-item-label> </QItemLabel>
<q-item-label>{{ row.email }}</q-item-label> <QItemLabel>{{ row.email }}</QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
<q-item class="q-pa-none"> <QItem class="q-pa-none">
<q-item-section> <QItemSection>
<q-item-label caption>{{ <QItemLabel caption>{{
t('worker.list.department') t('worker.list.department')
}}</q-item-label> }}</QItemLabel>
<q-item-label> <QItemLabel>
{{ row.department }} {{ row.department }}
</q-item-label> </QItemLabel>
</q-item-section> </QItemSection>
</q-item> </QItem>
</q-list> </QList>
</q-item-section> </QItemSection>
<q-separator vertical /> <QSeparator vertical />
<q-card-actions vertical class="justify-between"> <QCardActions vertical class="justify-between">
<q-btn <QBtn
flat flat
round round
color="primary" color="primary"
icon="arrow_circle_right" icon="arrow_circle_right"
@click="navigate(row.id)" @click="navigate(row.id)"
> >
<q-tooltip> <QTooltip>
{{ t('components.smartCard.openCard') }} {{ t('components.smartCard.openCard') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
<q-btn <QBtn
flat flat
round round
color="grey-7" color="grey-7"
icon="preview" icon="preview"
@click="viewSummary(row.id)" @click="viewSummary(row.id)"
> >
<q-tooltip> <QTooltip>
{{ t('components.smartCard.openSummary') }} {{ t('components.smartCard.openSummary') }}
</q-tooltip> </QTooltip>
</q-btn> </QBtn>
</q-card-actions> </QCardActions>
</q-item> </QItem>
</q-card> </QCard>
</template> </template>
</paginate> </VnPaginate>
</div> </div>
</q-page> </QPage>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -6,12 +6,12 @@ const stateStore = useStateStore();
</script> </script>
<template> <template>
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256"> <QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<q-scroll-area class="fit text-grey-8"> <QScrollArea class="fit text-grey-8">
<LeftMenu /> <LeftMenu />
</q-scroll-area> </QScrollArea>
</q-drawer> </QDrawer>
<q-page-container> <QPageContainer>
<router-view></router-view> <RouterView></RouterView>
</q-page-container> </QPageContainer>
</template> </template>

View File

@ -11,7 +11,7 @@ export default {
redirect: { name: 'ClaimMain' }, redirect: { name: 'ClaimMain' },
menus: { menus: {
main: ['ClaimList', 'ClaimRmaList'], main: ['ClaimList', 'ClaimRmaList'],
card: ['ClaimBasicData', 'ClaimRma', 'ClaimPhotos', 'ClaimLog'], card: ['ClaimBasicData', 'ClaimLines', 'ClaimRma', 'ClaimPhotos', 'ClaimLog'],
}, },
children: [ children: [
{ {
@ -66,6 +66,15 @@ export default {
}, },
component: () => import('src/pages/Claim/Card/ClaimBasicData.vue'), component: () => import('src/pages/Claim/Card/ClaimBasicData.vue'),
}, },
{
name: 'ClaimLines',
path: 'lines',
meta: {
title: 'lines',
icon: 'vn:details',
},
component: () => import('src/pages/Claim/Card/ClaimLines.vue'),
},
{ {
name: 'ClaimRma', name: 'ClaimRma',
path: 'rma', path: 'rma',

View File

@ -1,7 +1,7 @@
import { RouterView } from 'vue-router'; import { RouterView } from 'vue-router';
export default { export default {
path: '/invoiceOut', path: '/invoice-out',
name: 'InvoiceOut', name: 'InvoiceOut',
meta: { meta: {
title: 'invoiceOuts', title: 'invoiceOuts',
@ -10,7 +10,8 @@ export default {
component: RouterView, component: RouterView,
redirect: { name: 'InvoiceOutMain' }, redirect: { name: 'InvoiceOutMain' },
menus: { menus: {
main: ['InvoiceOutList'] main: ['InvoiceOutList'],
card: [],
}, },
children: [ children: [
{ {

View File

@ -1,7 +1,7 @@
<template> <template>
<q-layout> <QLayout>
<component :is="component" v-bind="$attrs" /> <component :is="component" v-bind="$attrs" />
</q-layout> </QLayout>
</template> </template>
<script> <script>

View File

@ -1,8 +1,8 @@
import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest'; import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
import { createWrapper, axios } from 'app/test/vitest/helper'; import { createWrapper, axios } from 'app/test/vitest/helper';
import Paginate from 'components/PaginateData.vue'; import VnPaginate from 'src/components/ui/VnPaginate.vue';
describe('Paginate', () => { describe('VnPaginate', () => {
const expectedUrl = '/api/customers'; const expectedUrl = '/api/customers';
let vm; let vm;
@ -15,7 +15,7 @@ describe('Paginate', () => {
limit: 3, limit: 3,
}, },
}; };
vm = createWrapper(Paginate, options).vm; vm = createWrapper(VnPaginate, options).vm;
}); });
afterEach(() => { afterEach(() => {

View File

@ -0,0 +1,94 @@
import { vi, describe, expect, it, beforeAll, beforeEach, afterEach } from 'vitest';
import { createWrapper, axios } from 'app/test/vitest/helper';
import ClaimLines from 'pages/Claim/Card/ClaimLines.vue';
describe('ClaimLines', () => {
let vm;
beforeAll(() => {
vm = createWrapper(ClaimLines, {
global: {
stubs: ['FetchData', 'VnPaginate'],
mocks: {
fetch: vi.fn(),
},
}
}).vm;
});
beforeEach(() => {
vm.claim = {
id: 1,
ticketFk: 1
}
vm.store.data = [
{
id: 1,
quantity: 10,
sale: {
id: 1, discount: 0
}
}
]
})
afterEach(() => {
vi.clearAllMocks();
});
describe('updateDiscount()', () => {
it('should make a POST request to endpoint "updateDiscount"', async () => {
vi.spyOn(axios, 'post').mockResolvedValue({ data: true });
vi.spyOn(vm.quasar, 'notify');
const canceller = new AbortController()
await vm.updateDiscount({ saleFk: 1, discount: 5, canceller });
const expectedData = { salesIds: [1], newDiscount: 5 }
expect(axios.post).toHaveBeenCalledWith('Tickets/1/updateDiscount', expectedData, {
signal: canceller.signal
})
});
});
describe('onUpdateDiscount()', () => {
it('should make a POST request and then set the discount on the original row', async () => {
vi.spyOn(vm.quasar, 'notify');
vm.onUpdateDiscount({ discount: 5, rowIndex: 0 });
const firstRow = vm.store.data[0]
expect(firstRow.sale.discount).toEqual(5)
expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Discount updated',
type: 'positive'
})
);
});
});
describe('remove()', () => {
it('should make a POST request and then call to the quasar notify() method', async () => {
vi.spyOn(axios, 'post').mockResolvedValue({ data: true });
vi.spyOn(vm.quasar, 'notify');
await vm.remove({
rows: [
{ id: 1 }
]
});
const expectedData = { deletes: [1] }
expect(axios.post).toHaveBeenCalledWith('ClaimBeginnings/crud', expectedData)
expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Row removed',
type: 'positive'
})
);
});
});
});

View File

@ -0,0 +1,50 @@
import { vi, describe, expect, it, beforeAll, beforeEach, afterEach } from 'vitest';
import { createWrapper, axios } from 'app/test/vitest/helper';
import ClaimLinesImport from 'pages/Claim/Card/ClaimLinesImport.vue';
describe('ClaimLinesImport', () => {
let vm;
beforeAll(() => {
vm = createWrapper(ClaimLinesImport, {
global: {
stubs: ['FetchData'],
mocks: {
fetch: vi.fn(),
},
}
}).vm;
});
afterEach(() => {
vi.clearAllMocks();
});
describe('importLines()', () => {
it('should make a POST request and then call to the quasar notify() method', async () => {
vi.spyOn(axios, 'post').mockResolvedValue({ data: true });
vi.spyOn(vm.quasar, 'notify');
vm.selected = [
{ id: 1, saleFk: 1, claimFk: 1, quantity: 10 }
]
vm.route.params.id = 1
await vm.importLines();
const expectedData = [{ saleFk: 1, claimFk: 1, quantity: 10 }]
expect(axios.post).toHaveBeenCalledWith('ClaimBeginnings', expectedData, {
signal: expect.any(Object)
})
expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({
message: 'Lines added to claim',
type: 'positive'
})
);
expect(vm.canceller).toEqual(null)
});
});
});

View File

@ -9,7 +9,7 @@ describe('CustomerPayments', () => {
beforeAll(() => { beforeAll(() => {
vm = createWrapper(CustomerPayments, { vm = createWrapper(CustomerPayments, {
global: { global: {
stubs: ['Paginate'], stubs: ['VnPaginate'],
mocks: { mocks: {
fetch: vi.fn(), fetch: vi.fn(),
}, },

View File

@ -18,9 +18,12 @@ export default defineConfig({
}, },
plugins: [ plugins: [
vue({ vue({
template: { transformAssetUrls }, template: {
transformAssetUrls
},
}), }),
quasar({ quasar({
autoImportComponentCase: 'pascal',
sassVariables: 'src/quasar-variables.scss', sassVariables: 'src/quasar-variables.scss',
}), }),
VueI18nPlugin({ VueI18nPlugin({