ref #6248 breadcrumbs created #99

Merged
jorgep merged 7 commits from 6248-createBreadCrumbs into dev 2023-10-30 12:56:36 +00:00
11 changed files with 261 additions and 49 deletions
Showing only changes of commit 318e2f2277 - Show all commits

View File

@ -7,6 +7,7 @@ import { useStateStore } from 'stores/useStateStore';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import PinnedModules from './PinnedModules.vue'; import PinnedModules from './PinnedModules.vue';
import UserPanel from 'components/UserPanel.vue'; import UserPanel from 'components/UserPanel.vue';
import VnBreadCrumbs from './common/VnBreadCrumbs.vue';
const { t } = useI18n(); const { t } = useI18n();
const session = useSession(); const session = useSession();
@ -61,6 +62,7 @@ const pinnedModulesRef = ref();
{{ appName }} {{ appName }}
<QBadge label="Beta" align="top" /> <QBadge label="Beta" align="top" />
</QToolbarTitle> </QToolbarTitle>
<VnBreadCrumbs v-if="$q.screen.gt.xs" />
<QSpace /> <QSpace />
<div id="searchbar" class="searchbar"></div> <div id="searchbar" class="searchbar"></div>
<QSpace /> <QSpace />
@ -112,6 +114,7 @@ const pinnedModulesRef = ref();
<div id="actions-append"></div> <div id="actions-append"></div>
</div> </div>
</QToolbar> </QToolbar>
<VnBreadCrumbs v-if="$q.screen.xs" class="q-ml-md" />
</QHeader> </QHeader>
</template> </template>

View File

@ -0,0 +1,100 @@
<script setup>
import { useRouter } from 'vue-router';
import { ref, watchEffect } from 'vue';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { useCamelCase } from 'src/composables/useCamelCase';
const router = useRouter();
const quasar = useQuasar();
const { t } = useI18n();
let params = ref([]);
let childRoutes = ref([]);
let links = ref([]);
let root = ref([]);
let lastModel;
watchEffect(() => {
lastModel = undefined;
params.value = router.currentRoute.value.path
.split('/')
.filter((param) => param && isNaN(param));
childRoutes.value = router.options.routes[1].children;
root.value = params.value[0];
setLinks(params);
});
function setLinks(params) {
links.value.length = 0;
if (params.value.includes('dashboard')) return;
for (let index in params.value) {
const path =
index < params.value.length - 1
? '/' +
params.value.filter((item, itemIndex) => itemIndex <= index).join('/')
: undefined;
links.value.push(getLink(params.value[index], path));
}
links.value.forEach((link) => {
if (link) link.root = root;
});
}
function getLink(param, path) {
lastModel = getModel(param);
if (!lastModel) return;
const { icon, title } = lastModel.meta;
const breadcrumb = {
icon,
path,
};
if (quasar.screen.gt.xs) {
breadcrumb.name = param;
breadcrumb.title = title;
}
return breadcrumb;
}
function getModel(param) {
if (!lastModel)
return childRoutes.value.find((child) => child.path.substring(1) == param);
let result = lastModel.children.find((child) => child.path == param);
if (result) return result;
for (const child of lastModel.children) {
if (child.children) {
result = child.children.find((item) => item.path === param);
if (result) break;
}
}
return result;
}
</script>
<template>
<QBreadcrumbs v-if="links.length && $q.screen.gt.xs" class="q-pa-xs">
<QBreadcrumbsEl
v-for="(link, index) of links"
:key="index"
:icon="link.icon"
:label="
t(`${useCamelCase(link.root)}.pageTitles.${useCamelCase(link.title)}`)
"
:to="link.path"
/>
</QBreadcrumbs>
<QBreadcrumbs v-else class="q-pa-xs">
<QBreadcrumbsEl
v-for="(link, index) of links"
:key="index"
:icon="link.icon"
:to="link.path"
/>
</QBreadcrumbs>
</template>
<style scoped></style>

View File

@ -105,7 +105,11 @@ async function search() {
class="cursor-pointer" class="cursor-pointer"
/> />
<QIcon v-if="props.info" name="info" class="cursor-info"> <QIcon
v-if="props.info && $q.screen.gt.xs"
name="info"
class="cursor-info"
>
<QTooltip>{{ props.info }}</QTooltip> <QTooltip>{{ props.info }}</QTooltip>
</QIcon> </QIcon>
</template> </template>

View File

@ -0,0 +1,3 @@
export function useCamelCase(value) {
return value.replace(/[-_](.)/g, (_, char) => char.toUpperCase());
}

View File

@ -0,0 +1,3 @@
export function useFirstUpper(str) {
return str && str.charAt(0).toUpperCase() + str.substr(1);
}

View File

@ -0,0 +1,29 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import { useRoute } from 'vue-router';
import LeftMenu from 'components/LeftMenu.vue';
const stateStore = useStateStore();
const route = useRoute();
const { t } = useI18n();
</script>
<template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit">
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<QPageContainer>
<QPage>
<div class="q-pa-md"><RouterView></RouterView></div>
</QPage>
</QPageContainer>
</template>
<i18n>
es:
Search customer: Buscar cliente
You can search by customer id or name: Puedes buscar por id o nombre del cliente
</i18n>

View File

@ -0,0 +1,26 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import { useRoute } from 'vue-router';
import LeftMenu from 'components/LeftMenu.vue';
const stateStore = useStateStore();
const route = useRoute();
const { t } = useI18n();
</script>
<template>
<QDrawer v-model="stateStore.leftDrawer" show-if-above :width="256">
<QScrollArea class="fit">
<QSeparator />
<LeftMenu source="card" />
</QScrollArea>
</QDrawer>
<div><RouterView></RouterView></div>
</template>
<i18n>
es:
Search customer: Buscar cliente
You can search by customer id or name: Puedes buscar por id o nombre del cliente
</i18n>

View File

@ -21,13 +21,13 @@ export default {
redirect: { name: 'CmrList' }, redirect: { name: 'CmrList' },
children: [ children: [
{ {
path: 'cmr/list', path: 'cmr',
name: 'CmrList', name: 'CmrList',
meta: { meta: {
title: 'cmrsList', title: 'cmrsList',
icon: 'fact_check', icon: 'fact_check',
}, },
component: () => import('src/pages/Route/Cmr/CmrList.vue') component: () => import('src/pages/Route/Cmr/CmrList.vue'),
}, },
], ],
}, },

View File

@ -11,11 +11,11 @@ export default {
redirect: { name: 'WagonMain' }, redirect: { name: 'WagonMain' },
menus: { menus: {
main: ['WagonList', 'WagonTypeList'], main: ['WagonList', 'WagonTypeList'],
card: [], card: ['WagonEdit'],
}, },
children: [ children: [
{ {
path: '/wagon', path: '',
name: 'WagonMain', name: 'WagonMain',
component: () => import('src/pages/Wagon/WagonMain.vue'), component: () => import('src/pages/Wagon/WagonMain.vue'),
redirect: { name: 'WagonList' }, redirect: { name: 'WagonList' },
@ -27,7 +27,7 @@ export default {
title: 'wagonsList', title: 'wagonsList',
icon: 'vn:trolley', icon: 'vn:trolley',
}, },
component: () => import('src/pages/Wagon/WagonList.vue') component: () => import('src/pages/Wagon/WagonList.vue'),
}, },
{ {
path: 'create', path: 'create',
@ -36,53 +36,78 @@ export default {
title: 'wagonCreate', title: 'wagonCreate',
icon: 'create', icon: 'create',
}, },
component: () => import('src/pages/Wagon/WagonCreate.vue') component: () => import('src/pages/Wagon/WagonCreate.vue'),
}, },
{ {
path: ':id/edit', path: 'type',
name: 'WagonTypeMain',
meta: {
title: 'typesList',
icon: 'view_list',
},
redirect: { name: 'WagonTypeList' },
children: [
{
path: 'list',
name: 'WagonTypeList',
meta: {
title: 'typesList',
icon: 'view_list',
},
component: () =>
import('src/pages/Wagon/Type/WagonTypeList.vue'),
},
{
path: 'create',
name: 'WagonTypeCreate',
meta: {
title: 'typeCreate',
icon: 'create',
},
component: () =>
import('src/pages/Wagon/Type/WagonTypeCreate.vue'),
},
{
path: ':id',
name: 'WagonTypeCard',
component: () =>
import('src/pages/Wagon/Type/Card/WagonTypeCard.vue'),
redirect: { name: 'WagonTypeEdit' },
children: [
{
path: 'edit',
name: 'WagonTypeEdit',
meta: {
title: 'typeEdit',
icon: 'edit',
},
component: () =>
import(
'src/pages/Wagon/Type/WagonTypeCreate.vue'
),
},
],
},
],
},
],
},
{
name: 'WagonCard',
path: ':id',
component: () => import('src/pages/Wagon/Card/WagonCard.vue'),
redirect: { name: 'WagonEdit' },
children: [
{
path: 'edit',
name: 'WagonEdit', name: 'WagonEdit',
meta: { meta: {
title: 'wagonEdit', title: 'wagonEdit',
icon: 'edit', icon: 'edit',
}, },
component: () => import('src/pages/Wagon/WagonCreate.vue') component: () => import('src/pages/Wagon/WagonCreate.vue'),
}, },
], ],
}, },
{
path: '/wagon/type',
name: 'WagonTypeMain',
component: () => import('src/pages/Wagon/WagonMain.vue'),
redirect: { name: 'WagonTypeList' },
children: [
{
path: 'list',
name: 'WagonTypeList',
meta: {
title: 'typesList',
icon: 'view_list',
},
component: () => import('src/pages/Wagon/Type/WagonTypeList.vue')
},
{
path: 'create',
name: 'WagonTypeCreate',
meta: {
title: 'typeCreate',
icon: 'create',
},
component: () => import('src/pages/Wagon/Type/WagonTypeCreate.vue')
},
{
path: ':id/edit',
name: 'WagonTypeEdit',
meta: {
title: 'typeEdit',
icon: 'edit',
},
component: () => import('src/pages/Wagon/Type/WagonTypeCreate.vue')
},
],
}
], ],
}; };

View File

@ -0,0 +1,19 @@
/// <reference types="cypress" />
describe('VnBreadCrumbs', () => {
beforeEach(() => {
cy.login('developer');
cy.visit('/');
});
it('should not be breadcrumbs', () => {
cy.get('.q-breadcrumbs').should('not.exist');
});
it('should get the correct breadcrumbs', () => {
cy.get('[href="#/customer"]').click();
cy.get('.q-breadcrumbs .q-breadcrumbs--last').should('have.length', 1);
cy.get('.q-breadcrumbs .q-breadcrumbs--last').contains('Clientes');
cy.get('.q-infinite-scroll > :nth-child(1)').click();
cy.get('.q-breadcrumbs .q-breadcrumbs__el').should('have.length', 2);
cy.get('.q-breadcrumbs .q-breadcrumbs__el').eq(1).contains('Resumen');
});
});

View File

@ -8,17 +8,17 @@ describe('WorkerList', () => {
it('should load workers', () => { it('should load workers', () => {
cy.get('.card-list-body > .list-items > :nth-child(2) > .value > span') cy.get('.card-list-body > .list-items > :nth-child(2) > .value > span')
.eq(0) .eq(0)
.should('have.text', 'victorvd');
cy.get('.card-list-body > .list-items > :nth-child(2) > .value > span')
.eq(1)
.should('have.text', 'JessicaJones'); .should('have.text', 'JessicaJones');
cy.get('.card-list-body > .list-items > :nth-child(2) > .value > span') cy.get('.card-list-body > .list-items > :nth-child(2) > .value > span')
.eq(2) .eq(1)
.should('have.text', 'BruceBanner'); .should('have.text', 'BruceBanner');
cy.get('.card-list-body > .list-items > :nth-child(2) > .value > span')
.eq(2)
.should('have.text', 'CharlesXavier');
}); });
it('should open the worker summary', () => { it('should open the worker summary', () => {
cy.get('.card-list-body .actions .q-btn:nth-child(2)').eq(1).click(); cy.get('.card-list-body .actions .q-btn:nth-child(2)').eq(0).click();
cy.get('.summaryHeader div').should('have.text', '1110 - Jessica Jones'); cy.get('.summaryHeader div').should('have.text', '1110 - Jessica Jones');
cy.get('.summary .header').eq(0).invoke('text').should('include', 'Basic data'); cy.get('.summary .header').eq(0).invoke('text').should('include', 'Basic data');
cy.get('.summary .header').eq(1).should('have.text', 'User data'); cy.get('.summary .header').eq(1).should('have.text', 'User data');