closes #4834 create-worker-module #28
|
@ -9,7 +9,7 @@
|
|||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
|
||||
|
||||
const { configure } = require('quasar/wrappers');
|
||||
const VueI18nPlugin = require('@intlify/unplugin-vue-i18n/vite')
|
||||
const VueI18nPlugin = require('@intlify/unplugin-vue-i18n/vite');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = configure(function (/* ctx */) {
|
||||
|
@ -140,6 +140,8 @@ module.exports = configure(function (/* ctx */) {
|
|||
|
||||
// Quasar plugins
|
||||
plugins: ['Notify', 'Dialog'],
|
||||
//all: 'auto',
|
||||
//autoImportComponentCase: 'pascal',
|
||||
},
|
||||
|
||||
// animations: 'all', // --- includes all animations
|
||||
|
|
|
@ -21,7 +21,14 @@ onMounted(() => stateStore.setMounted());
|
|||
<template>
|
||||
<q-header class="bg-dark" color="white" elevated>
|
||||
<q-toolbar class="q-py-sm q-px-md">
|
||||
<q-btn flat @click="stateStore.toggleLeftDrawer()" round dense icon="menu">
|
||||
<q-btn
|
||||
@click="stateStore.toggleLeftDrawer()"
|
||||
icon="menu"
|
||||
class="q-mr-sm"
|
||||
round
|
||||
dense
|
||||
flat
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
|
|
|
@ -16,7 +16,7 @@ const props = defineProps({
|
|||
module: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const slots = useSlots();
|
||||
|
@ -24,14 +24,18 @@ const { t } = useI18n();
|
|||
|
||||
onMounted(() => fetch());
|
||||
|
||||
const emit = defineEmits(['onFetch']);
|
||||
|
||||
const entity = ref();
|
||||
async function fetch() {
|
||||
const params = {};
|
||||
|
||||
if (props.filter) params.filter = props.filter;
|
||||
if (props.filter) params.filter = JSON.stringify(props.filter);
|
||||
|
||||
const { data } = await axios.get(props.url, { params });
|
||||
entity.value = data;
|
||||
|
||||
emit('onFetch', data);
|
||||
}
|
||||
|
||||
watch(props, async () => {
|
||||
|
@ -46,9 +50,9 @@ watch(props, async () => {
|
|||
<div class="header bg-primary q-pa-sm">
|
||||
<router-link :to="{ name: `${module}List` }">
|
||||
<q-btn round flat dense size="md" icon="view_list" color="white">
|
||||
<q-tooltip>{{
|
||||
t('components.cardDescriptor.mainList')
|
||||
}}</q-tooltip>
|
||||
<q-tooltip>
|
||||
{{ t('components.cardDescriptor.mainList') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</router-link>
|
||||
<router-link
|
||||
|
@ -80,8 +84,9 @@ watch(props, async () => {
|
|||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
<slot name="before" />
|
||||
<div class="body q-py-sm">
|
||||
<q-list>
|
||||
<q-list dense>
|
||||
<q-item-label header class="ellipsis text-h5" :lines="1">
|
||||
<slot name="description" :entity="entity">
|
||||
<span>
|
||||
|
@ -98,6 +103,7 @@ watch(props, async () => {
|
|||
</q-list>
|
||||
<slot name="body" :entity="entity" />
|
||||
</div>
|
||||
<slot name="after" />
|
||||
</template>
|
||||
<!-- Skeleton -->
|
||||
<skeleton-descriptor v-if="!entity" />
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
<script setup>
|
||||
import { nextTick, ref } from 'vue';
|
||||
|
||||
const $props = defineProps({
|
||||
to: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const isHeaderMounted = ref(false);
|
||||
nextTick(() => {
|
||||
isHeaderMounted.value = document.querySelector($props.to) !== null;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<teleport v-if="isHeaderMounted" :to="$props.to">
|
||||
<slot />
|
||||
</teleport>
|
||||
</template>
|
|
@ -87,15 +87,29 @@ async function search() {
|
|||
</q-form>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@media screen and (max-width: $breakpoint-xs-max) {
|
||||
.q-field {
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: $breakpoint-xs-max) {
|
||||
.q-field {
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
.q-field {
|
||||
transition: width 0.36s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.cursor-info {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
#searchbar .q-field {
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
.body--light #searchbar {
|
||||
.q-field--standout.q-field--highlighted .q-field__control {
|
||||
background-color: $grey-7;
|
||||
|
|
|
@ -345,6 +345,47 @@ export default {
|
|||
totalWithVat: 'Amount',
|
||||
},
|
||||
},
|
||||
worker: {
|
||||
pageTitles: {
|
||||
workers: 'Workers',
|
||||
list: 'List',
|
||||
basicData: 'Basic data',
|
||||
summary: 'Summary',
|
||||
},
|
||||
list: {
|
||||
name: 'Name',
|
||||
email: 'Email',
|
||||
phone: 'Phone',
|
||||
mobile: 'Mobile',
|
||||
active: 'Active',
|
||||
department: 'Department',
|
||||
schedule: 'Schedule',
|
||||
},
|
||||
card: {
|
||||
workerId: 'Worker ID',
|
||||
name: 'Name',
|
||||
email: 'Email',
|
||||
phone: 'Phone',
|
||||
mobile: 'Mobile',
|
||||
active: 'Active',
|
||||
warehouse: 'Warehouse',
|
||||
agency: 'Agency',
|
||||
salesPerson: 'Sales person',
|
||||
},
|
||||
summary: {
|
||||
basicData: 'Basic data',
|
||||
boss: 'Boss',
|
||||
phoneExtension: 'Phone extension',
|
||||
entPhone: 'Enterprise phone',
|
||||
personalPhone: 'Personal phone',
|
||||
noBoss: 'No boss',
|
||||
userData: 'User data',
|
||||
userId: 'User ID',
|
||||
role: 'Role',
|
||||
sipExtension: 'Extension',
|
||||
},
|
||||
imageNotFound: 'Image not found',
|
||||
},
|
||||
components: {
|
||||
topbar: {},
|
||||
userPanel: {
|
||||
|
|
|
@ -345,6 +345,47 @@ export default {
|
|||
totalWithVat: 'Importe',
|
||||
},
|
||||
},
|
||||
worker: {
|
||||
pageTitles: {
|
||||
workers: 'Trabajadores',
|
||||
list: 'Listado',
|
||||
basicData: 'Datos básicos',
|
||||
summary: 'Resumen',
|
||||
},
|
||||
list: {
|
||||
name: 'Nombre',
|
||||
email: 'Email',
|
||||
phone: 'Teléfono',
|
||||
mobile: 'Móvil',
|
||||
active: 'Activo',
|
||||
department: 'Departamento',
|
||||
schedule: 'Horario',
|
||||
},
|
||||
card: {
|
||||
workerId: 'ID Trabajador',
|
||||
name: 'Nombre',
|
||||
email: 'Email',
|
||||
phone: 'Teléfono',
|
||||
mobile: 'Móvil',
|
||||
active: 'Activo',
|
||||
warehouse: 'Almacén',
|
||||
agency: 'Empresa',
|
||||
salesPerson: 'Comercial',
|
||||
},
|
||||
summary: {
|
||||
basicData: 'Datos básicos',
|
||||
boss: 'Jefe',
|
||||
phoneExtension: 'Extensión de teléfono',
|
||||
entPhone: 'Teléfono de empresa',
|
||||
personalPhone: 'Teléfono personal',
|
||||
noBoss: 'Sin jefe',
|
||||
userData: 'Datos de usuario',
|
||||
userId: 'ID del usuario',
|
||||
role: 'Rol',
|
||||
sipExtension: 'Extensión',
|
||||
},
|
||||
imageNotFound: 'No se ha encontrado la imagen',
|
||||
},
|
||||
components: {
|
||||
topbar: {},
|
||||
userPanel: {
|
||||
|
|
|
@ -3,20 +3,19 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useStateStore } from 'stores/useStateStore';
|
||||
import ClaimDescriptor from './ClaimDescriptor.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
<VnSearchbar
|
||||
data-key="ClaimList"
|
||||
:label="t('Search claim')"
|
||||
:info="t('You can search by claim id or customer name')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit">
|
||||
<claim-descriptor />
|
||||
|
|
|
@ -3,7 +3,6 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useRoute } from 'vue-router';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import ClaimLogFilter from './ClaimLogFilter.vue';
|
||||
|
||||
|
@ -114,7 +113,7 @@ function actionColor(action) {
|
|||
</Paginate>
|
||||
</q-timeline>
|
||||
</div>
|
||||
<TeleportSlot to="#actions-append">
|
||||
<Teleport v-if="stateStore.isHeaderMounted()" to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn flat @click="stateStore.toggleRightDrawer()" round dense icon="menu">
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
|
@ -122,7 +121,7 @@ function actionColor(action) {
|
|||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</TeleportSlot>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.rightDrawer" show-if-above side="right" :width="300">
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<ClaimLogFilter data-key="ClaimLogs" />
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { ref, computed } from 'vue';
|
||||
import { useQuasar } from 'quasar';
|
||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
import TeleportSlot from 'src/components/ui/TeleportSlot.vue';
|
||||
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { useSession } from 'composables/useSession';
|
||||
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const session = useSession();
|
||||
const token = session.getToken();
|
||||
const quasar = useQuasar();
|
||||
|
||||
const claimId = computed(() => router.currentRoute.value.params.id);
|
||||
|
||||
|
@ -239,7 +237,10 @@ function onDrag() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<teleport-slot v-if="!quasar.platform.is.mobile" to="#actions-prepend">
|
||||
<Teleport
|
||||
v-if="stateStore.isHeaderMounted() && !quasar.platform.is.mobile"
|
||||
to="#actions-prepend"
|
||||
>
|
||||
<div class="row q-gutter-x-sm">
|
||||
<label for="fileInput">
|
||||
<q-btn
|
||||
|
@ -262,27 +263,34 @@ function onDrag() {
|
|||
</label>
|
||||
<q-separator vertical />
|
||||
</div>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
|
||||
<teleport-slot to=".q-footer">
|
||||
<q-tabs align="justify" inline-label narrow-indicator>
|
||||
<q-tab
|
||||
@click="inputFile.nativeEl.click()"
|
||||
icon="add_circle"
|
||||
:label="t('globals.add')"
|
||||
>
|
||||
<q-input
|
||||
ref="inputFile"
|
||||
type="file"
|
||||
style="display: none"
|
||||
multiple
|
||||
v-model="files"
|
||||
@update:model-value="create()"
|
||||
/>
|
||||
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip>
|
||||
</q-tab>
|
||||
</q-tabs>
|
||||
</teleport-slot>
|
||||
<q-page-sticky
|
||||
v-if="quasar.platform.is.mobile"
|
||||
position="bottom"
|
||||
:offset="[0, 0]"
|
||||
expand
|
||||
>
|
||||
<q-toolbar class="bg-primary text-white q-pa-none">
|
||||
<q-tabs class="full-width" align="justify" inline-label narrow-indicator>
|
||||
<q-tab
|
||||
@click="inputFile.nativeEl.click()"
|
||||
icon="add_circle"
|
||||
:label="t('globals.add')"
|
||||
>
|
||||
<q-input
|
||||
ref="inputFile"
|
||||
type="file"
|
||||
style="display: none"
|
||||
multiple
|
||||
v-model="files"
|
||||
@update:model-value="create()"
|
||||
/>
|
||||
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip>
|
||||
</q-tab>
|
||||
</q-tabs>
|
||||
</q-toolbar>
|
||||
</q-page-sticky>
|
||||
|
||||
<!-- MULTIMEDIA DIALOG START-->
|
||||
<q-dialog
|
||||
|
|
|
@ -5,9 +5,9 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useQuasar } from 'quasar';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useArrayData } from 'src/composables/useArrayData';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnConfirm from 'src/components/ui/VnConfirm.vue';
|
||||
|
||||
import { toDate } from 'src/filters';
|
||||
|
@ -15,6 +15,7 @@ import { toDate } from 'src/filters';
|
|||
const quasar = useQuasar();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const stateStore = useStateStore();
|
||||
const arrayData = useArrayData('ClaimRma');
|
||||
|
||||
const claim = ref();
|
||||
|
@ -44,6 +45,12 @@ async function onFetch(data) {
|
|||
}
|
||||
|
||||
async function addRow() {
|
||||
if (!claim.value.rma) {
|
||||
return quasar.notify({
|
||||
message: `This claim is not associated to any RMA`,
|
||||
type: 'negative',
|
||||
});
|
||||
}
|
||||
const formData = {
|
||||
code: claim.value.rma,
|
||||
};
|
||||
|
@ -138,20 +145,31 @@ async function remove(id) {
|
|||
</paginate>
|
||||
</div>
|
||||
</div>
|
||||
<teleport-slot v-if="!quasar.platform.is.mobile" to="#actions-prepend">
|
||||
|
||||
<Teleport
|
||||
v-if="stateStore.isHeaderMounted() && !quasar.platform.is.mobile"
|
||||
to="#actions-prepend"
|
||||
>
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn @click="addRow()" icon="add" color="primary" dense rounded>
|
||||
<q-tooltip bottom> {{ t('globals.add') }} </q-tooltip>
|
||||
</q-btn>
|
||||
<q-separator vertical />
|
||||
</div>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
|
||||
<teleport-slot to=".q-footer">
|
||||
<q-tabs align="justify" inline-label narrow-indicator>
|
||||
<q-tab @click="addRow()" icon="add_circle" :label="t('globals.add')" />
|
||||
</q-tabs>
|
||||
</teleport-slot>
|
||||
<q-page-sticky
|
||||
v-if="quasar.platform.is.mobile"
|
||||
position="bottom"
|
||||
:offset="[0, 0]"
|
||||
expand
|
||||
>
|
||||
<q-toolbar class="bg-primary text-white q-pa-none">
|
||||
<q-tabs class="full-width" align="justify" inline-label narrow-indicator>
|
||||
<q-tab @click="addRow()" icon="add_circle" :label="t('globals.add')" />
|
||||
</q-tabs>
|
||||
</q-toolbar>
|
||||
</q-page-sticky>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -170,3 +188,8 @@ async function remove(id) {
|
|||
z-index: 2998;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
This claim is not associated to any RMA: Esta reclamación no está asociada a ninguna ARM
|
||||
</i18n>
|
||||
|
|
|
@ -3,12 +3,11 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useRouter } from 'vue-router';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import { toDate } from 'src/filters/index';
|
||||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import { toDate } from 'filters/index';
|
||||
import Paginate from 'components/PaginateData.vue';
|
||||
import ClaimSummaryDialog from './Card/ClaimSummaryDialog.vue';
|
||||
import CustomerDescriptorPopover from 'src/pages/Customer/Card/CustomerDescriptorPopover.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import CustomerDescriptorPopover from 'pages/Customer/Card/CustomerDescriptorPopover.vue';
|
||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||
import ClaimFilter from './ClaimFilter.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
|
@ -37,22 +36,30 @@ function viewSummary(id) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="ClaimList"
|
||||
:label="t('Search claim')"
|
||||
:info="t('You can search by claim id or customer name')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
<teleport-slot to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn flat @click="stateStore.toggleRightDrawer()" round dense icon="menu">
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</teleport-slot>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="ClaimList"
|
||||
:label="t('Search claim')"
|
||||
:info="t('You can search by claim id or customer name')"
|
||||
/>
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<ClaimFilter data-key="ClaimList" />
|
||||
|
|
|
@ -3,20 +3,19 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useStateStore } from 'stores/useStateStore';
|
||||
import CustomerDescriptor from './CustomerDescriptor.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
<VnSearchbar
|
||||
data-key="CustomerList"
|
||||
:label="t('Search customer')"
|
||||
:info="t('You can search by customer id or name')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit">
|
||||
<CustomerDescriptor />
|
||||
|
|
|
@ -4,6 +4,7 @@ import { useRoute } from 'vue-router';
|
|||
import { useI18n } from 'vue-i18n';
|
||||
import { toCurrency } from 'src/filters';
|
||||
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
|
@ -30,7 +31,10 @@ const entityId = computed(() => {
|
|||
{{ t('customer.card.salesPerson') }}
|
||||
</q-item-label>
|
||||
<q-item-label class="col q-ma-none">
|
||||
{{ entity.salesPersonUser.name }}
|
||||
<span class="link">
|
||||
{{ entity.salesPersonUser.name }}
|
||||
<WorkerDescriptorProxy :id="entity.salesPersonFk" />
|
||||
</span>
|
||||
</q-item-label>
|
||||
</q-item>
|
||||
<q-item class="row">
|
||||
|
|
|
@ -5,7 +5,6 @@ import { useQuasar } from 'quasar';
|
|||
import { useStateStore } from 'stores/useStateStore';
|
||||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import CustomerSummaryDialog from './Card/CustomerSummaryDialog.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import CustomerFilter from './CustomerFilter.vue';
|
||||
|
||||
|
@ -29,22 +28,30 @@ function viewSummary(id) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="CustomerList"
|
||||
:label="t('Search customer')"
|
||||
:info="t('You can search by customer id or name')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
<teleport-slot to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn flat @click="stateStore.toggleRightDrawer()" round dense icon="menu">
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</teleport-slot>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="CustomerList"
|
||||
:label="t('Search customer')"
|
||||
:info="t('You can search by customer id or name')"
|
||||
/>
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<CustomerFilter data-key="CustomerList" />
|
||||
|
@ -113,7 +120,7 @@ function viewSummary(id) {
|
|||
<q-btn
|
||||
flat
|
||||
round
|
||||
color="orange"
|
||||
color="primary"
|
||||
icon="arrow_circle_right"
|
||||
@click="navigate(row.id)"
|
||||
>
|
||||
|
|
|
@ -3,20 +3,19 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useStateStore } from 'stores/useStateStore';
|
||||
import InvoiceOutDescriptor from './InvoiceOutDescriptor.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'components/ui/VnSearchbar.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
<VnSearchbar
|
||||
data-key="InvoiceOutList"
|
||||
:label="t('Search invoice')"
|
||||
:info="t('You can search by invoice reference')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit">
|
||||
<InvoiceOutDescriptor />
|
||||
|
|
|
@ -7,7 +7,6 @@ import { useStateStore } from 'stores/useStateStore';
|
|||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import InvoiceOutSummaryDialog from './Card/InvoiceOutSummaryDialog.vue';
|
||||
import { toDate, toCurrency } from 'src/filters/index';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import InvoiceOutFilter from './InvoiceOutFilter.vue';
|
||||
|
||||
|
@ -34,22 +33,30 @@ function viewSummary(id) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="InvoiceOutList"
|
||||
:label="t('Search invoice')"
|
||||
:info="t('You can search by invoice reference')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
<teleport-slot to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn flat @click="stateStore.toggleRightDrawer()" round dense icon="menu">
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</teleport-slot>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="InvoiceOutList"
|
||||
:label="t('Search invoice')"
|
||||
:info="t('You can search by invoice reference')"
|
||||
/>
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<InvoiceOutFilter data-key="InvoiceOutList" />
|
||||
|
|
|
@ -3,20 +3,19 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useStateStore } from 'stores/useStateStore';
|
||||
import TicketDescriptor from './TicketDescriptor.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
<VnSearchbar
|
||||
data-key="TicketList"
|
||||
:label="t('Search ticket')"
|
||||
:info="t('You can search by ticket id or alias')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit">
|
||||
<TicketDescriptor />
|
||||
|
|
|
@ -7,8 +7,6 @@ import { useStateStore } from 'stores/useStateStore';
|
|||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import { toDate, toCurrency } from 'src/filters/index';
|
||||
import TicketSummaryDialog from './Card/TicketSummaryDialog.vue';
|
||||
|
||||
import TeleportSlot from 'components/ui/TeleportSlot.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import TicketFilter from './TicketFilter.vue';
|
||||
|
||||
|
@ -71,22 +69,30 @@ function viewSummary(id) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<teleport-slot to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="TicketList"
|
||||
:label="t('Search ticket')"
|
||||
:info="t('You can search by ticket id or alias')"
|
||||
/>
|
||||
</teleport-slot>
|
||||
<teleport-slot to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn flat @click="stateStore.toggleRightDrawer()" round dense icon="menu">
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</teleport-slot>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="TicketList"
|
||||
:label="t('Search ticket')"
|
||||
:info="t('You can search by ticket id or alias')"
|
||||
/>
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<TicketFilter data-key="TicketList" />
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import WorkerDescriptor from './WorkerDescriptor.vue';
|
||||
import LeftMenu from 'components/LeftMenu.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const { t } = useI18n();
|
||||
</script>
|
||||
<template>
|
||||
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
|
||||
<VnSearchbar
|
||||
data-key="WorkerList"
|
||||
:label="t('Search worker')"
|
||||
:info="t('You can search by worker id or name')"
|
||||
/>
|
||||
</Teleport>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit">
|
||||
<WorkerDescriptor />
|
||||
<q-separator />
|
||||
<left-menu source="card" />
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
<q-page class="q-pa-md">
|
||||
<router-view></router-view>
|
||||
</q-page>
|
||||
</q-page-container>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search worker: Buscar trabajador
|
||||
You can search by worker id or name: Puedes buscar por id o nombre del trabajador
|
||||
</i18n>
|
|
@ -0,0 +1,138 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const { getToken } = useSession();
|
||||
|
||||
const entityId = computed(() => {
|
||||
return $props.id || route.params.id;
|
||||
});
|
||||
|
||||
const worker = ref();
|
||||
const filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['email', 'name', 'nickname'],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'department',
|
||||
scope: {
|
||||
include: [
|
||||
{
|
||||
relation: 'department',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'sip',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const sip = computed(() => worker.value.sip && worker.value.sip.extension);
|
||||
|
||||
function getWorkerAvatar() {
|
||||
const token = getToken();
|
||||
return `/api/Images/user/160x160/${route.params.id}/download?access_token=${token}`;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<card-descriptor
|
||||
module="Worker"
|
||||
:url="`Workers/${entityId}`"
|
||||
:filter="filter"
|
||||
@on-fetch="(data) => (worker = data)"
|
||||
>
|
||||
<template #before>
|
||||
<q-img :src="getWorkerAvatar()" class="photo">
|
||||
<template #error>
|
||||
<div
|
||||
class="absolute-full bg-grey-10 text-center q-pa-md flex flex-center"
|
||||
>
|
||||
<div>
|
||||
<div class="text-grey-5" style="opacity: 0.4; font-size: 5vh">
|
||||
<q-icon name="vn:claims" />
|
||||
</div>
|
||||
<div class="text-grey-5" style="opacity: 0.4">
|
||||
{{ t('worker.imageNotFound') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</q-img>
|
||||
</template>
|
||||
<template #description="{ entity }">
|
||||
<span>
|
||||
{{ entity.user.nickname }}
|
||||
<q-tooltip>{{ entity.user.nickname }}</q-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<template #body="{ entity }">
|
||||
<q-list>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption> {{ t('worker.card.name') }} </q-item-label>
|
||||
<q-item-label>{{ entity.user.nickname }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.card.email') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ entity.user.email }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.list.department') }}
|
||||
</q-item-label>
|
||||
<q-item-label>
|
||||
{{ entity.department.department.name }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.card.phone') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ entity.phone }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.sipExtension') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ sip }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
</card-descriptor>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.photo {
|
||||
height: 256px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,16 @@
|
|||
<script setup>
|
||||
import WorkerDescriptor from './WorkerDescriptor.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-popup-proxy>
|
||||
<WorkerDescriptor v-if="$props.id" :id="$props.id" />
|
||||
</q-popup-proxy>
|
||||
</template>
|
|
@ -0,0 +1,292 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { ref, onMounted, computed, onUpdated } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
|
||||
import WorkerDescriptorProxy from './WorkerDescriptorProxy.vue';
|
||||
|
||||
onMounted(() => fetch());
|
||||
onUpdated(() => fetch());
|
||||
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const entityId = computed(() => $props.id || route.params.id);
|
||||
|
||||
const worker = ref(null);
|
||||
|
||||
const filter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'user',
|
||||
scope: {
|
||||
fields: ['email', 'name', 'nickname', 'roleFk'],
|
||||
include: {
|
||||
relation: 'role',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'department',
|
||||
scope: {
|
||||
include: {
|
||||
relation: 'department',
|
||||
scope: {
|
||||
fields: ['name'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
relation: 'boss',
|
||||
},
|
||||
{
|
||||
relation: 'client',
|
||||
},
|
||||
{
|
||||
relation: 'sip',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
function fetch() {
|
||||
const id = entityId.value;
|
||||
axios.get(`/Workers/${id}`, { params: { filter } }).then((response) => {
|
||||
worker.value = response.data;
|
||||
});
|
||||
}
|
||||
|
||||
function sipExtension() {
|
||||
if (worker.value.sip) return worker.value.sip.extension;
|
||||
return '-';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="summary container">
|
||||
<q-card>
|
||||
<SkeletonSummary v-if="!worker" />
|
||||
<template v-if="worker">
|
||||
<div class="header bg-primary q-pa-sm q-mb-md">
|
||||
{{ worker.id }} - {{ worker.firstName }} {{ worker.lastName }}
|
||||
</div>
|
||||
<div class="row q-pa-md q-col-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-list>
|
||||
<q-item-label header class="text-h6">
|
||||
{{ t('worker.summary.basicData') }}
|
||||
</q-item-label>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption> ID </q-item-label>
|
||||
<q-item-label>{{ worker.id }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.card.name') }}
|
||||
</q-item-label>
|
||||
<q-item-label>
|
||||
{{ worker.user.nickname }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.list.department') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
worker.department.department.name
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.list.email') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ worker.user.email }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item
|
||||
class="items-start cursor-pointer q-hoverable"
|
||||
v-if="worker.boss"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.summary.boss') }}
|
||||
</q-item-label>
|
||||
<q-item-label>
|
||||
<span class="link">
|
||||
{{ worker.boss.name }}
|
||||
<WorkerDescriptorProxy :id="worker.bossFk" />
|
||||
</span>
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.phoneExtension') }}
|
||||
</q-item-label>
|
||||
<q-item-label>
|
||||
{{
|
||||
worker.mobileExtension == ''
|
||||
? worker.mobileExtension
|
||||
: '-'
|
||||
}}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.entPhone') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
worker.phone == '' ? worker.phone : '-'
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.personalPhone') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
worker.client.phone == ''
|
||||
? worker.client.phone
|
||||
: '-'
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-list>
|
||||
<q-item-label header class="text-h6">
|
||||
{{ t('worker.summary.userData') }}
|
||||
</q-item-label>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.summary.userId') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ worker.user.id }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.card.name') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
worker.user.nickname
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.role') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
worker.user.role.name
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-item-label caption
|
||||
>{{ t('worker.summary.sipExtension') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ sipExtension() }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</q-card>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.q-card {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.negative {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.summary {
|
||||
.q-list {
|
||||
.q-item__label--header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
a {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
.row {
|
||||
flex-wrap: wrap;
|
||||
|
||||
.col {
|
||||
min-width: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
#slider-container {
|
||||
max-width: 80%;
|
||||
margin: 0 auto;
|
||||
|
||||
.q-slider {
|
||||
.q-slider__marker-labels:nth-child(1) {
|
||||
transform: none;
|
||||
}
|
||||
.q-slider__marker-labels:nth-child(2) {
|
||||
transform: none;
|
||||
left: auto !important;
|
||||
right: 0%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.q-dialog .summary {
|
||||
max-width: 1200px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,21 @@
|
|||
<script setup>
|
||||
import { useDialogPluginComponent } from 'quasar';
|
||||
import WorkerSummary from './WorkerSummary.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
defineEmits([...useDialogPluginComponent.emits]);
|
||||
|
||||
const { dialogRef, onDialogHide } = useDialogPluginComponent();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
||||
<worker-summary v-if="$props.id" :id="$props.id" />
|
||||
</q-dialog>
|
||||
</template>
|
|
@ -0,0 +1,121 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
dataKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const departments = ref();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<fetch-data url="Departments" @on-fetch="(data) => (departments = data)" auto-load />
|
||||
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ params, searchFn }">
|
||||
<q-list dense>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-input :label="t('FI')" v-model="params.fi" lazy-rules>
|
||||
<template #prepend>
|
||||
<q-icon name="badge" size="sm"></q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-input
|
||||
:label="t('First Name')"
|
||||
v-model="params.firstName"
|
||||
lazy-rules
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-input
|
||||
:label="t('Last Name')"
|
||||
v-model="params.lastName"
|
||||
lazy-rules
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section>
|
||||
<q-input
|
||||
:label="t('User Name')"
|
||||
v-model="params.userName"
|
||||
lazy-rules
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item-section v-if="!departments">
|
||||
<q-skeleton type="QInput" class="full-width" />
|
||||
</q-item-section>
|
||||
<q-item-section v-if="departments">
|
||||
<q-select
|
||||
:label="t('Department')"
|
||||
v-model="params.departmentFk"
|
||||
@update:model-value="searchFn()"
|
||||
:options="departments"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
:input-debounce="0"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item class="q-mb-md">
|
||||
<q-item-section>
|
||||
<q-input
|
||||
:label="t('Extension')"
|
||||
v-model="params.extension"
|
||||
lazy-rules
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
|
||||
<i18n>
|
||||
en:
|
||||
params:
|
||||
search: Contains
|
||||
fi: FI
|
||||
firstName: First name
|
||||
lastName: Last name
|
||||
userName: User
|
||||
extension: Extension
|
||||
es:
|
||||
params:
|
||||
search: Contiene
|
||||
fi: NIF
|
||||
firstName: Nombre
|
||||
lastName: Apellidos
|
||||
userName: Usuario
|
||||
extension: Extensión
|
||||
FI: NIF
|
||||
First Name: Nombre
|
||||
Last Name: Apellidos
|
||||
User Name: Usuario
|
||||
Department: Departamento
|
||||
Extension: Extensión
|
||||
</i18n>
|
|
@ -0,0 +1,155 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import Paginate from 'src/components/PaginateData.vue';
|
||||
import WorkerSummaryDialog from './Card/WorkerSummaryDialog.vue';
|
||||
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
|
||||
import WorkerFilter from './WorkerFilter.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const quasar = useQuasar();
|
||||
|
||||
function navigate(id) {
|
||||
router.push({ path: `/worker/${id}` });
|
||||
}
|
||||
|
||||
function viewSummary(id) {
|
||||
quasar.dialog({
|
||||
component: WorkerSummaryDialog,
|
||||
componentProps: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<template v-if="stateStore.isHeaderMounted()">
|
||||
<Teleport to="#searchbar">
|
||||
<VnSearchbar
|
||||
data-key="WorkerList"
|
||||
:label="t('Search worker')"
|
||||
:info="t('You can search by worker id or name')"
|
||||
/>
|
||||
</Teleport>
|
||||
<Teleport to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<q-btn
|
||||
flat
|
||||
@click="stateStore.toggleRightDrawer()"
|
||||
round
|
||||
dense
|
||||
icon="menu"
|
||||
>
|
||||
<q-tooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
<q-drawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<WorkerFilter data-key="WorkerList" />
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page class="column items-center q-pa-md">
|
||||
<div class="card-list">
|
||||
<paginate
|
||||
data-key="WorkerList"
|
||||
url="Workers/filter"
|
||||
order="id DESC"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card q-mb-md" v-for="row of rows" :key="row.id">
|
||||
<q-item
|
||||
class="q-pa-none items-start cursor-pointer q-hoverable"
|
||||
v-ripple
|
||||
clickable
|
||||
>
|
||||
<q-item-section class="q-pa-md" @click="navigate(row.id)">
|
||||
<q-item-label class="text-h6">
|
||||
{{ row.nickname }}
|
||||
</q-item-label>
|
||||
<q-item-label caption>#{{ row.id }}</q-item-label>
|
||||
<q-list>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.list.name') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{
|
||||
row.userName
|
||||
}}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
<q-item-label caption>
|
||||
{{ t('worker.list.email') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ row.email }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{
|
||||
t('worker.list.department')
|
||||
}}</q-item-label>
|
||||
<q-item-label>
|
||||
{{ row.department }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-item-section>
|
||||
<q-separator vertical />
|
||||
<q-card-actions vertical class="justify-between">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
color="primary"
|
||||
icon="arrow_circle_right"
|
||||
@click="navigate(row.id)"
|
||||
>
|
||||
<q-tooltip>
|
||||
{{ t('components.smartCard.openCard') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
color="grey-7"
|
||||
icon="preview"
|
||||
@click="viewSummary(row.id)"
|
||||
>
|
||||
<q-tooltip>
|
||||
{{ t('components.smartCard.openSummary') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</q-item>
|
||||
</q-card>
|
||||
</template>
|
||||
</paginate>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-list {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n>
|
||||
es:
|
||||
Search worker: Buscar trabajador
|
||||
You can search by worker id or name: Puedes buscar por id o nombre del trabajador
|
||||
</i18n>
|
|
@ -0,0 +1,17 @@
|
|||
<script setup>
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import LeftMenu from 'src/components/LeftMenu.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-drawer v-model="stateStore.leftDrawer" show-if-above :width="256">
|
||||
<q-scroll-area class="fit text-grey-8">
|
||||
<LeftMenu />
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
<router-view></router-view>
|
||||
</q-page-container>
|
||||
</template>
|
|
@ -2,10 +2,12 @@ import Customer from './customer';
|
|||
import Ticket from './ticket';
|
||||
import Claim from './claim';
|
||||
import InvoiceOut from './invoiceOut';
|
||||
import Worker from './worker';
|
||||
|
||||
export default [
|
||||
Customer,
|
||||
Ticket,
|
||||
Claim,
|
||||
InvoiceOut
|
||||
InvoiceOut,
|
||||
Worker
|
||||
]
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import { RouterView } from 'vue-router';
|
||||
|
||||
export default {
|
||||
path: '/worker',
|
||||
name: 'Worker',
|
||||
meta: {
|
||||
title: 'workers',
|
||||
icon: 'vn:worker',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'WorkerMain' },
|
||||
menus: {
|
||||
main: ['WorkerList'],
|
||||
card: [],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'WorkerMain',
|
||||
component: () => import('src/pages/Worker/WorkerMain.vue'),
|
||||
redirect: { name: 'WorkerList' },
|
||||
children: [
|
||||
{
|
||||
path: 'list',
|
||||
name: 'WorkerList',
|
||||
meta: {
|
||||
title: 'list',
|
||||
icon: 'view_list',
|
||||
},
|
||||
component: () => import('src/pages/Worker/WorkerList.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'WorkerCard',
|
||||
path: ':id',
|
||||
component: () => import('src/pages/Worker/Card/WorkerCard.vue'),
|
||||
redirect: { name: 'WorkerSummary' },
|
||||
children: [
|
||||
{
|
||||
name: 'WorkerSummary',
|
||||
path: 'summary',
|
||||
meta: {
|
||||
title: 'summary',
|
||||
},
|
||||
component: () => import('src/pages/Worker/Card/WorkerSummary.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
import customer from './modules/customer';
|
||||
import ticket from './modules/ticket';
|
||||
import claim from './modules/claim';
|
||||
import worker from './modules/worker';
|
||||
import invoiceOut from './modules/invoiceOut';
|
||||
|
||||
const routes = [
|
||||
|
@ -26,6 +27,7 @@ const routes = [
|
|||
customer,
|
||||
ticket,
|
||||
claim,
|
||||
worker,
|
||||
invoiceOut,
|
||||
{
|
||||
path: '/:catchAll(.*)*',
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useRole } from 'src/composables/useRole';
|
|||
import routes from 'src/router/modules';
|
||||
|
||||
export const useNavigationStore = defineStore('navigationStore', () => {
|
||||
const modules = ['customer', 'claim', 'ticket', 'invoiceOut'];
|
||||
const modules = ['customer', 'claim', 'ticket', 'invoiceOut', 'worker'];
|
||||
const pinnedModules = ref([]);
|
||||
const role = useRole();
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
describe('WorkerList', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(1280, 720);
|
||||
cy.login('developer');
|
||||
cy.visit('/#/worker/list');
|
||||
});
|
||||
|
||||
it('should load workers', () => {
|
||||
cy.get('div[class="q-item__label text-h6"]').eq(0).should('have.text', 'Jessica Jones');
|
||||
cy.get('div[class="q-item__label text-h6"]').eq(1).should('have.text', 'Bruce Banner');
|
||||
cy.get('div[class="q-item__label text-h6"]').eq(2).should('have.text', 'Charles Xavier');
|
||||
});
|
||||
|
||||
it('should open the worker summary', () => {
|
||||
cy.get('div[class="q-item__section column q-item__section--side justify-center q-pa-md"]').eq(0).click();
|
||||
cy.get('div[class="header bg-primary q-pa-sm q-mb-md"').should('have.text', '1110 - Jessica Jones');
|
||||
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(0).should('have.text', 'Basic data');
|
||||
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(1).should('have.text', 'User data');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe('WorkerSummary', () => {
|
||||
beforeEach(() => {
|
||||
cy.viewport(1280, 720)
|
||||
cy.login('developer')
|
||||
cy.visit('/#/worker/19/summary');
|
||||
});
|
||||
|
||||
it('should load worker summary', () => {
|
||||
cy.get('div[class="header bg-primary q-pa-sm q-mb-md"').should('have.text', '19 - salesBoss');
|
||||
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(0).should('have.text', 'Basic data');
|
||||
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(1).should('have.text', 'User data');
|
||||
cy.get('div[class="q-item__section column q-item__section--main justify-center"]').eq(0).should('have.text', 'NamesalesBossNick');
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue