0
0
Fork 0

Create Shelving list module

Adds shelving list, preview and update
This commit is contained in:
Kevin Martinez 2023-11-23 01:17:23 -03:00
parent e74fac6c6e
commit 8e0ee45901
18 changed files with 770 additions and 2 deletions

View File

@ -27,7 +27,7 @@ defineExpose({
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;

View File

@ -424,6 +424,32 @@ export default {
comercial: 'Comercial',
},
},
shelving:{
pageTitles: {
shelving: 'Shelving',
shelvingList: 'Shelving List',
createShelving: 'Create shelving',
summary: 'Summary',
basicData: 'Basic Data',
},
list: {
parking: 'Parking',
priority: 'Priority',
},
summary: {
code: 'Code',
parking: 'Parking',
priority: 'Priority',
worker: 'Worker',
recyclable: 'Recyclable'
},
basicData: {
code: 'Code',
parking: 'Parking',
priority: 'Priority',
recyclable: 'Recyclable'
}
},
worker: {
pageTitles: {
workers: 'Workers',

View File

@ -426,6 +426,32 @@ export default {
comercial: 'Comercial',
},
},
shelving:{
pageTitles: {
shelving: 'Carros',
shelvingList: 'Listado de carros',
createShelving: 'Crear carro',
summary: 'Resumen',
basicData: 'Datos básicos',
},
list: {
parking: 'Parking',
priority: 'Prioridad',
},
summary: {
code: 'Código',
parking: 'Parking',
priority: 'Prioridad',
worker: 'Trabajador',
recyclable: 'Reciclable'
},
basicData: {
code: 'Código',
parking: 'Parking',
priority: 'Prioridad',
recyclable: 'Reciclable'
}
},
worker: {
pageTitles: {
workers: 'Trabajadores',

View File

@ -0,0 +1,112 @@
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
const { t } = useI18n();
const props = defineProps({
dataKey: {
type: String,
required: true,
},
});
const workers = ref();
const parkings = ref();
function setWorkers(data) {
workers.value = data;
}
function setParkings(data) {
parkings.value = data;
}
</script>
<template>
<FetchData
url="Parkings"
:filter="{ fields: ['id', 'code'] }"
sort-by="code ASC"
@on-fetch="setParkings"
auto-load
/>
<FetchData
url="Workers/activeWithInheritedRole"
:filter="{ where: { role: 'salesPerson' } }"
sort-by="firstName ASC"
@on-fetch="setWorkers"
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 }">
<QList dense>
<QItem>
<QItemSection v-if="!parkings">
<QSkeleton type="QInput" class="full-width" />
</QItemSection>
<QItemSection v-if="parkings">
<QSelect
:label="t('params.parkingFk')"
v-model="params.parkingFk"
:options="parkings"
option-value="id"
option-label="code"
emit-value
map-options
use-input
:input-debounce="0"
/>
</QItemSection>
</QItem>
<QItem>
<QItemSection v-if="!workers">
<QSkeleton type="QInput" class="full-width" />
</QItemSection>
<QItemSection v-if="workers">
<QSelect
:label="t('params.userFk')"
v-model="params.userFk"
:options="workers"
option-value="id"
option-label="name"
emit-value
map-options
use-input
:input-debounce="0"
/>
</QItemSection>
</QItem>
<QItem class="q-mb-md">
<QItemSection>
<QCheckbox
v-model="params.isRecyclable"
:label="t('params.isRecyclable')"
toggle-indeterminate
/>
</QItemSection>
</QItem>
</QList>
</template>
</VnFilterPanel>
</template>
<i18n>
en:
params:
parkingFk: Parking
userFk: Worker
isRecyclable: Recyclable
es:
params:
parkingFk: Parking
userFk: Trabajador
isRecyclable: Reciclable
</i18n>

View File

@ -0,0 +1,120 @@
<script setup>
import VnPaginate from 'components/ui/VnPaginate.vue';
import { useStateStore } from 'stores/useStateStore';
import { useI18n } from 'vue-i18n';
import { onMounted, onUnmounted } from 'vue';
import CardList2 from 'components/ui/CardList2.vue';
import VnLv from 'components/ui/VnLv.vue';
import { useQuasar } from 'quasar';
import { useRouter } from 'vue-router';
import ShelvingFilter from 'pages/Shelving/ShelvingFilter.vue';
import ShelvingSummaryDialog from 'pages/Shelving/Summary/ShelvingSummaryDialog.vue';
import ShelvingSearchbar from 'pages/Shelving/components/ShelvingSearchbar.vue';
const stateStore = useStateStore();
const router = useRouter();
const quasar = useQuasar();
const { t } = useI18n();
const filter = {
include: [{ relation: 'parking' }],
};
onMounted(() => (stateStore.rightDrawer = true));
onUnmounted(() => (stateStore.rightDrawer = false));
function navigate(id) {
router.push({ path: `/shelving/${id}` });
}
function viewSummary(id) {
quasar.dialog({
component: ShelvingSummaryDialog,
componentProps: {
id,
},
});
}
</script>
<template>
<template v-if="stateStore.isHeaderMounted()">
<Teleport to="#searchbar">
<ShelvingSearchbar />
</Teleport>
<Teleport to="#actions-append">
<div class="row q-gutter-x-sm">
<QBtn
flat
@click="stateStore.toggleRightDrawer()"
round
dense
icon="menu"
>
<QTooltip bottom anchor="bottom right">
{{ t('globals.collapseMenu') }}
</QTooltip>
</QBtn>
</div>
</Teleport>
</template>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8">
<ShelvingFilter data-key="ShelvingList" />
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
<div class="card-list">
<VnPaginate
data-key="ShelvingList"
url="Shelvings"
:filter="filter"
auto-load
>
<template #body="{ rows }">
<CardList2
v-for="row of rows"
:key="row.id"
:title="row.code"
@click="navigate(row.id)"
>
<template #list-items>
<VnLv label="ID" :value="row.id" />
<VnLv
:label="t('shelving.list.parking')"
:title-label="t('shelving.list.parking')"
:value="row.parking?.code"
/>
<VnLv
:label="t('shelving.list.priority')"
:value="row?.priority"
/>
</template>
<template #actions>
<QBtn
:label="t('components.smartCard.openCard')"
@click.stop="navigate(row.id)"
color="primary"
type="submit"
/>
<QBtn
:label="t('components.smartCard.openSummary')"
@click.stop="viewSummary(row.id)"
color="primary"
flat
style="margin-top: 15px"
type="reset"
/>
</template>
</CardList2>
</template>
</VnPaginate>
</div>
</QPage>
</template>
<style lang="scss" scoped>
.card-list {
width: 100%;
max-width: 60em;
}
</style>

View File

@ -0,0 +1,17 @@
<script setup>
import { useStateStore } from 'stores/useStateStore';
import LeftMenu from 'src/components/LeftMenu.vue';
const stateStore = useStateStore();
</script>
<template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit text-grey-8">
<LeftMenu />
</QScrollArea>
</QDrawer>
<QPageContainer>
<RouterView></RouterView>
</QPageContainer>
</template>

View File

@ -0,0 +1,105 @@
<script setup>
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import FetchData from 'components/FetchData.vue';
import { ref } from 'vue';
const route = useRoute();
const { t } = useI18n();
const shelvingFilter = {
include: [
{
relation: 'worker',
scope: {
fields: ['id'],
include: {
relation: 'user',
scope: { fields: ['nickname'] },
},
},
},
{ relation: 'parking' },
],
};
const parkingFilter = { fields: ['id', 'code'] };
const parkingList = ref([]);
const parkingListCopy = ref([]);
const setParkingList = (data) => {
parkingList.value = data;
parkingListCopy.value = data;
};
const parkingSelectFilter = {
options: parkingList,
filterFn: (options, value) => {
const search = value.trim().toLowerCase();
if (!search || search === '') {
return parkingListCopy.value;
}
return options.value.filter((option) =>
option.code.toLowerCase().startsWith(search)
);
},
};
</script>
<template>
<FetchData
url="Parkings"
:filter="parkingFilter"
@on-fetch="setParkingList"
auto-load
/>
<FormModel
:url="`Shelvings/${route.params.id}`"
:url-update="`Shelvings/${route.params.id}`"
:filter="shelvingFilter"
model="shelving"
>
<template #form="{ data, validate, filter }">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput v-model="data.code" :label="t('shelving.basicData.code')" />
</div>
<div class="col">
<QSelect
v-model="data.parkingFk"
:options="parkingList"
option-value="id"
option-label="code"
emit-value
:label="t('shelving.basicData.parking')"
map-options
use-input
@filter="
(value, update) => filter(value, update, parkingSelectFilter)
"
:rules="validate('shelving.parkingFk')"
:input-debounce="0"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput
v-model="data.priority"
:label="t('shelving.basicData.priority')"
/>
</div>
<div class="col">
<QCheckbox
v-model="data.isRecyclable"
:label="t('shelving.basicData.recyclable')"
/>
</div>
</VnRow>
</template>
</FormModel>
</template>

View File

@ -0,0 +1,68 @@
<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue';
const $props = defineProps({
id: {
type: Number,
default: 0,
},
});
const route = useRoute();
const { t } = useI18n();
const entityId = computed(() => $props.id || route.params.id);
const filter = {
include: [
{
relation: 'worker',
scope: {
fields: ['id'],
include: {
relation: 'user',
scope: { fields: ['nickname'] },
},
},
},
{ relation: 'parking' },
],
};
</script>
<template>
<CardSummary ref="summary" :url="`Shelvings/${entityId}`" :filter="filter">
<template #header="{ entity }">
<div>{{ entity.code }}</div>
</template>
<template #body="{ entity }">
<QCard class="vn-one">
<div class="header">
{{ t('shelving.pageTitles.basicData') }}
</div>
<VnLv
:label="t('shelving.summary.code')"
:value="entity.code"
/>
<VnLv
:label="t('shelving.summary.parking')"
:value="entity.parking?.code"
/>
<VnLv
:label="t('shelving.summary.priority')"
:value="entity.priority"
/>
<VnLv
:label="t('shelving.summary.worker')"
:value="entity.worker?.user?.nickname"
/>
<VnLv
:label="t('shelving.summary.recyclable')"
:value="entity.isRecyclable"
/>
</QCard>
</template>
</CardSummary>
</template>

View File

@ -0,0 +1,29 @@
<script setup>
import { useDialogPluginComponent } from 'quasar';
import ShelvingSummary from "pages/Shelving/Summary/ShelvingSummary.vue";
const $props = defineProps({
id: {
type: Number,
required: true,
},
});
defineEmits([...useDialogPluginComponent.emits]);
const { dialogRef, onDialogHide } = useDialogPluginComponent();
</script>
<template>
<QDialog ref="dialogRef" @hide="onDialogHide">
<ShelvingSummary v-if="$props.id" :id="$props.id" />
</QDialog>
</template>
<style lang="scss">
.q-dialog .summary .header {
position: sticky;
z-index: $z-max;
top: 0;
}
</style>

View File

@ -0,0 +1,32 @@
<script setup>
import { useStateStore } from 'stores/useStateStore';
import LeftMenu from 'components/LeftMenu.vue';
import ShelvingSearchbar from "pages/Shelving/components/ShelvingSearchbar.vue";
import ShelvingDescriptor from "pages/Shelving/components/ShelvingDescriptor.vue";
const stateStore = useStateStore();
</script>
<template>
<Teleport to="#searchbar" v-if="stateStore.isHeaderMounted()">
<ShelvingSearchbar />
</Teleport>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit">
<ShelvingDescriptor />
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<QToolbar class="bg-vn-dark justify-end">
<div id="st-data"></div>
<QSpace />
<div id="st-actions"></div>
</QToolbar>
<div class="q-pa-md">
<RouterView></RouterView>
</div>
</QPage>
</QPageContainer>
</template>

View File

@ -0,0 +1,70 @@
<script setup>
import { ref, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import CardDescriptor from 'components/ui/CardDescriptor.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import useCardDescription from 'src/composables/useCardDescription';
import WorkerDescriptorProxy from "pages/Worker/Card/WorkerDescriptorProxy.vue";
import ShelvingDescriptorMenu from "pages/Shelving/components/ShelvingDescriptorMenu.vue";
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
});
const route = useRoute();
const { t } = useI18n();
const entityId = computed(() => {
return $props.id || route.params.id;
});
const filter = {
include: [
{
relation: 'worker',
scope: {
fields: ['id'],
include: {
relation: 'user',
scope: { fields: ['nickname'] },
},
},
},
{ relation: 'parking' },
],
};
const data = ref(useCardDescription());
const setData = (entity) => (data.value = useCardDescription(entity.code, entity.id));
</script>
<template>
<CardDescriptor
module="Shelving"
:url="`Shelvings/${entityId}`"
:filter="filter"
:title="data.title"
:subtitle="data.subtitle"
@on-fetch="setData"
>
<template #body="{ entity }">
<VnLv :label="t('shelving.summary.code')" :value="entity.code" />
<VnLv :label="t('shelving.summary.parking')" :value="entity.parking?.code" />
<VnLv v-if="entity.worker" :label="t('shelving.summary.worker')">
<template #value>
<span class="link">
{{ entity.worker?.user?.nickname }}
<WorkerDescriptorProxy :id="entity.worker?.id" />
</span>
</template>
</VnLv>
</template>
<template #menu="{entity}">
<ShelvingDescriptorMenu :shelving="entity" />
</template>
</CardDescriptor>
</template>

View File

@ -0,0 +1,61 @@
<script setup>
import axios from 'axios';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import VnConfirm from 'components/ui/VnConfirm.vue';
const $props = defineProps({
shelving: {
type: Object,
required: true,
},
});
const router = useRouter();
const quasar = useQuasar();
const { t } = useI18n();
function confirmRemove() {
quasar
.dialog({
component: VnConfirm,
componentProps: {
title: t('confirmDeletion'),
message: t('confirmDeletionMessage'),
promise: remove,
},
})
.onOk(async () => await router.push({ name: 'ShelvingList' }));
}
async function remove() {
if (!$props.shelving.value.id) {
return;
}
await axios.delete(`Shelvings/${$props.shelving.value.id}`);
quasar.notify({
message: t('globals.dataDeleted'),
type: 'positive',
});
}
</script>
<template>
<QItem @click="confirmRemove()" v-ripple clickable>
<QItemSection avatar>
<QIcon name="delete" />
</QItemSection>
<QItemSection>{{ t('deleteShelving') }}</QItemSection>
</QItem>
</template>
<i18n>
{
"en": {
"deleteShelving": "Delete Shelving"
},
"es": {
"deleteShelving": "Eliminar carro"
}
}
</i18n>

View File

@ -0,0 +1,15 @@
<script setup>
import ShelvingDescriptor from "pages/Shelving/components/ShelvingDescriptor.vue";
const $props = defineProps({
id: {
type: Number,
required: true,
},
});
</script>
<template>
<QPopupProxy>
<ShelvingDescriptor v-if="$props.id" :id="$props.id" />
</QPopupProxy>
</template>

View File

@ -0,0 +1,20 @@
<script setup>
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import {useI18n} from "vue-i18n";
const { t } = useI18n();
</script>
<template>
<VnSearchbar
data-key="ShelvingList"
:label="t('Search shelving')"
:info="t('You can search by search reference')"
/>
</template>
<style scoped lang="scss"></style>
<i18n>
es:
Search shelving: Buscar carros
You can search by shelving reference: Puedes buscar por referencia del carro
</i18n>

View File

@ -3,8 +3,9 @@ import Ticket from './ticket';
import Claim from './claim';
import InvoiceOut from './invoiceOut';
import Worker from './worker';
import Shelving from "./shelving";
import Wagon from './wagon';
import Route from './route';
import Supplier from './Supplier';
export default [Customer, Ticket, Claim, InvoiceOut, Worker, Wagon, Route, Supplier];
export default [Customer, Ticket, Claim, InvoiceOut, Worker, Shelving, Wagon, Route, Supplier];

View File

@ -0,0 +1,63 @@
import {RouterView} from "vue-router";
export default {
path: '/shelving',
name: 'Shelving',
meta: {
title: 'shelving',
icon: 'vn:trolley'
},
component: RouterView,
redirect: { name: 'ShelvingMain' },
menus: {
main: ['ShelvingList'],
card: ['ShelvingBasicData']
},
children: [
{
path: '',
name: 'ShelvingMain',
component: () => import('src/pages/Shelving/ShelvingMain.vue'),
redirect: { name: 'ShelvingList' },
children: [
{
path: 'list',
name: 'ShelvingList',
meta: {
title: 'shelvingList',
icon: 'vn:trolley',
},
component: () => import('src/pages/Shelving/ShelvingList.vue'),
},
],
},
{
name: 'ShelvingLayout',
path: ':id',
component: () => import('pages/Shelving/Summary/ShelvingSummaryPage.vue'),
redirect: { name: 'ShelvingSummary' },
children: [
{
name: 'ShelvingSummary',
path: 'summary',
meta: {
title: 'summary',
},
component: () =>
import('pages/Shelving/Summary/ShelvingSummary.vue'),
},
{
name: 'ShelvingBasicData',
path: 'basic-data',
meta: {
title: 'basicData',
icon: 'vn:settings',
roles: ['salesPerson'],
},
component: () => import('src/pages/Shelving/Summary/ShelvingBasicData.vue'),
},
],
},
]
};

View File

@ -6,6 +6,7 @@ import invoiceOut from './modules/invoiceOut';
import wagon from './modules/wagon';
import supplier from './modules/Supplier';
import route from './modules/route';
import shelving from "src/router/modules/shelving";
const routes = [
{
@ -49,6 +50,7 @@ const routes = [
ticket,
claim,
worker,
shelving,
invoiceOut,
wagon,
route,

View File

@ -12,6 +12,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
'ticket',
'invoiceOut',
'worker',
'shelving',
'wagon',
'route',
'supplier',