diff --git a/package.json b/package.json index 27ba190a6..74f83334c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salix-front", - "version": "24.8.0", + "version": "24.10.0", "description": "Salix frontend", "productName": "Salix", "author": "Verdnatura", diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue index 8012c3c4e..eeb7aebd1 100644 --- a/src/components/common/VnLocation.vue +++ b/src/components/common/VnLocation.vue @@ -50,7 +50,10 @@ const value = computed({ return $props.modelValue; }, set(value) { - emit('update:modelValue', value); + emit( + 'update:modelValue', + postcodesOptions.value.find((p) => p.code === value) + ); }, }); @@ -101,16 +104,11 @@ function handleFetch(data) { :label="t('Location')" :placeholder="t('search_by_postalcode')" @input-value="locationFilter" - :default-filter="true" + :default-filter="false" :input-debounce="300" :class="{ required: $attrs.required }" v-bind="$attrs" - emit-value - map-options - use-input clearable - hide-selected - fill-input > <template #form> <CreateNewPostcode @on-data-saved="locationFilter()" /> diff --git a/src/components/common/VnSelectFilter.vue b/src/components/common/VnSelectFilter.vue index 5c26443f2..4903a5327 100644 --- a/src/components/common/VnSelectFilter.vue +++ b/src/components/common/VnSelectFilter.vue @@ -116,14 +116,14 @@ async function fetchFilter(val) { } async function filterHandler(val, update) { + if (!$props.defaultFilter) return update(); + let newOptions; + if ($props.url) { + newOptions = await fetchFilter(val); + } else newOptions = filter(val, myOptionsOriginal.value); update( - async () => { - if (!$props.defaultFilter) return; - if ($props.url) { - myOptions.value = await fetchFilter(val); - return; - } - myOptions.value = filter(val, myOptionsOriginal.value); + () => { + myOptions.value = newOptions; }, (ref) => { if (val !== '' && ref.options.length > 0) { diff --git a/src/components/ui/CardDescriptor.vue b/src/components/ui/CardDescriptor.vue index 0e7218892..241937a4e 100644 --- a/src/components/ui/CardDescriptor.vue +++ b/src/components/ui/CardDescriptor.vue @@ -1,5 +1,5 @@ <script setup> -import { onMounted, useSlots, watch, computed } from 'vue'; +import { onMounted, useSlots, watch, computed, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import SkeletonDescriptor from 'components/ui/SkeletonDescriptor.vue'; import { useArrayData } from 'composables/useArrayData'; @@ -39,6 +39,7 @@ const slots = useSlots(); const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); const entity = computed(() => useArrayData($props.dataKey).store.data); +const isLoading = ref(false); defineExpose({ getData, @@ -60,15 +61,20 @@ async function getData() { filter: $props.filter, skip: 0, }); - const { data } = await arrayData.fetch({ append: false, updateRouter: false }); - emit('onFetch', data); + isLoading.value = true; + try { + const { data } = await arrayData.fetch({ append: false, updateRouter: false }); + emit('onFetch', data); + } finally { + isLoading.value = false; + } } const emit = defineEmits(['onFetch']); </script> <template> <div class="descriptor"> - <template v-if="entity"> + <template v-if="entity && !isLoading"> <div class="header bg-primary q-pa-sm justify-between"> <slot name="header-extra-action" /> <QBtn @@ -154,8 +160,13 @@ const emit = defineEmits(['onFetch']); <slot name="after" /> </template> <!-- Skeleton --> - <SkeletonDescriptor v-if="!entity" /> + <SkeletonDescriptor v-if="!entity || isLoading" /> </div> + <QInnerLoading + :label="t('globals.pleaseWait')" + :showing="isLoading" + color="primary" + /> </template> <style lang="scss"> @@ -210,11 +221,8 @@ const emit = defineEmits(['onFetch']); margin-bottom: 15px; } .list-box { - width: 90%; background-color: var(--vn-gray); - margin: 10px auto; - padding: 10px 5px 10px 0px; - border-radius: 8px; + .q-item__label { color: var(--vn-label); } diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue index 33a6980d5..cb2c97746 100644 --- a/src/components/ui/CardSummary.vue +++ b/src/components/ui/CardSummary.vue @@ -90,17 +90,16 @@ watch(props, async () => { background-color: var(--vn-gray); > .q-card.vn-one { - width: 350px; flex: 1; } > .q-card.vn-two { - flex: 2; + flex: 40%; } > .q-card.vn-three { - flex: 4; + flex: 75%; } > .q-card.vn-max { - width: 100%; + flex: 100%; } > .q-card { diff --git a/src/components/ui/SkeletonDescriptor.vue b/src/components/ui/SkeletonDescriptor.vue index 470d47e49..9679751f5 100644 --- a/src/components/ui/SkeletonDescriptor.vue +++ b/src/components/ui/SkeletonDescriptor.vue @@ -1,10 +1,39 @@ <template> <div id="descriptor-skeleton"> - <div class="col q-pl-sm q-pa-sm"> - <QSkeleton type="text" square height="45px" /> - <QSkeleton type="text" square height="18px" /> - <QSkeleton type="text" square height="18px" /> - <QSkeleton type="text" square height="18px" /> + <div class="row justify-between q-pa-sm"> + <QSkeleton square size="40px" /> + <QSkeleton square size="40px" /> + <QSkeleton square height="40px" width="20px" /> + </div> + <div class="col justify-between q-pa-sm q-gutter-y-xs"> + <QSkeleton square height="40px" width="150px" /> + <QSkeleton square height="30px" width="70px" /> + </div> + <div class="col q-pl-sm q-pa-sm q-mb-md"> + <div class="row justify-between"> + <QSkeleton type="text" square height="30px" width="20%" /> + <QSkeleton type="text" square height="30px" width="60%" /> + </div> + <div class="row justify-between"> + <QSkeleton type="text" square height="30px" width="20%" /> + <QSkeleton type="text" square height="30px" width="60%" /> + </div> + <div class="row justify-between"> + <QSkeleton type="text" square height="30px" width="20%" /> + <QSkeleton type="text" square height="30px" width="60%" /> + </div> + <div class="row justify-between"> + <QSkeleton type="text" square height="30px" width="20%" /> + <QSkeleton type="text" square height="30px" width="60%" /> + </div> + <div class="row justify-between"> + <QSkeleton type="text" square height="30px" width="20%" /> + <QSkeleton type="text" square height="30px" width="60%" /> + </div> + <div class="row justify-between"> + <QSkeleton type="text" square height="30px" width="20%" /> + <QSkeleton type="text" square height="30px" width="60%" /> + </div> </div> <QCardActions> diff --git a/src/components/ui/VnNotes.vue b/src/components/ui/VnNotes.vue index fac74837f..6890b52c3 100644 --- a/src/components/ui/VnNotes.vue +++ b/src/components/ui/VnNotes.vue @@ -39,14 +39,14 @@ async function insert() { ref="vnPaginateRef" > <template #body="{ rows }"> - <QCard class="q-pa-md q-mb-md" v-for="(note, index) in rows" :key="index"> + <QCard class="q-pa-xs q-mb-md" v-for="(note, index) in rows" :key="index"> <QCardSection horizontal> <slot name="picture"> <VnAvatar :descriptor="false" :worker-id="note.workerFk" /> </slot> <QItem class="full-width justify-between items-start"> <VnUserLink - :name="`${note.worker.firstName} ${note.worker.lastName}`" + :name="`${note.worker.user.nickname}`" :worker-id="note.worker.id" /> @@ -55,7 +55,7 @@ async function insert() { </slot> </QItem> </QCardSection> - <QCardSection> + <QCardSection class="q-pa-sm"> <slot name="text"> {{ note.text }} </slot> @@ -63,15 +63,8 @@ async function insert() { </QCard> </template> </VnPaginate> - <QPageSticky position="bottom-right" :offset="[25, 25]"> - <QBtn - v-if="addNote" - color="primary" - icon="add" - size="lg" - round - @click="noteModal = true" - /> + <QPageSticky position="bottom-right" :offset="[25, 25]" v-if="addNote"> + <QBtn color="primary" icon="add" size="lg" round @click="noteModal = true" /> </QPageSticky> <QDialog v-model="noteModal" @hide="newNote = ''"> <QCard> diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index 3f031f1d1..b6b81f2d5 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -105,7 +105,8 @@ export function useArrayData(key, userOptions) { for (const row of response.data) store.data.push(row); } else { store.data = response.data; - updateRouter && updateStateParams(); + if (!document.querySelectorAll('[role="dialog"]')) + updateRouter && updateStateParams(); } store.isLoading = false; diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index 6d584b4e6..9e0ad7c9b 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -547,6 +547,7 @@ export default { responsible: 'Responsible', worker: 'Worker', redelivery: 'Redelivery', + returnOfMaterial: 'RMA', }, basicData: { customer: 'Customer', diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index 2000171aa..6083dfad7 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -546,6 +546,7 @@ export default { responsible: 'Responsable', worker: 'Trabajador', redelivery: 'DevoluciĆ³n', + returnOfMaterial: 'RMA', }, basicData: { customer: 'Cliente', diff --git a/src/pages/Claim/Card/ClaimAction.vue b/src/pages/Claim/Card/ClaimAction.vue index 59409e432..ef45bf3dc 100644 --- a/src/pages/Claim/Card/ClaimAction.vue +++ b/src/pages/Claim/Card/ClaimAction.vue @@ -135,7 +135,7 @@ async function regularizeClaim() { message: t('globals.dataSaved'), type: 'positive', }); - await onUpdateGreugeAccept(); + if (multiplicatorValue.value) await onUpdateGreugeAccept(); } async function onUpdateGreugeAccept() { diff --git a/src/pages/Claim/Card/ClaimDescriptor.vue b/src/pages/Claim/Card/ClaimDescriptor.vue index deaed94f8..3827a70d4 100644 --- a/src/pages/Claim/Card/ClaimDescriptor.vue +++ b/src/pages/Claim/Card/ClaimDescriptor.vue @@ -1,5 +1,5 @@ <script setup> -import { ref, computed } from 'vue'; +import { ref, computed, onMounted } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; import { toDate, toPercentage } from 'src/filters'; @@ -10,6 +10,7 @@ import CardDescriptor from 'components/ui/CardDescriptor.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import useCardDescription from 'src/composables/useCardDescription'; import VnUserLink from 'src/components/ui/VnUserLink.vue'; +import { getUrl } from 'src/composables/getUrl'; const $props = defineProps({ id: { @@ -22,7 +23,7 @@ const $props = defineProps({ const route = useRoute(); const state = useState(); const { t } = useI18n(); - +const salixUrl = ref(); const entityId = computed(() => { return $props.id || route.params.id; }); @@ -71,11 +72,10 @@ const filter = { }; const STATE_COLOR = { - pending: 'positive', - managed: 'warning', - resolved: 'negative', + pending: 'warning', + managed: 'info', + resolved: 'positive', }; - function stateColor(code) { return STATE_COLOR[code]; } @@ -85,6 +85,9 @@ const setData = (entity) => { data.value = useCardDescription(entity.client.name, entity.id); state.set('ClaimDescriptor', entity); }; +onMounted(async () => { + salixUrl.value = await getUrl(''); +}); </script> <template> @@ -167,6 +170,20 @@ const setData = (entity) => { > <QTooltip>{{ t('claim.card.claimedTicket') }}</QTooltip> </QBtn> + <QBtn + size="md" + icon="assignment" + color="primary" + :href="salixUrl + 'ticket/' + entity.ticketFk + '/sale-tracking'" + > + </QBtn> + <QBtn + size="md" + icon="visibility" + color="primary" + :href="salixUrl + 'ticket/' + entity.ticketFk + '/tracking/index'" + > + </QBtn> </QCardActions> </template> </CardDescriptor> diff --git a/src/pages/Claim/Card/ClaimLines.vue b/src/pages/Claim/Card/ClaimLines.vue index 5190c9932..90dd31199 100644 --- a/src/pages/Claim/Card/ClaimLines.vue +++ b/src/pages/Claim/Card/ClaimLines.vue @@ -11,6 +11,7 @@ import CrudModel from 'components/CrudModel.vue'; import FetchData from 'components/FetchData.vue'; import VnDiscount from 'components/common/vnDiscount.vue'; import ClaimLinesImport from './ClaimLinesImport.vue'; +import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; const quasar = useQuasar(); const route = useRoute(); @@ -229,7 +230,14 @@ function showImportDialog() { </QPopupEdit> </QTd> </template> - + <template #body-cell-description="{ row, value }"> + <QTd auto-width align="right" class="text-primary"> + {{ value }} + <ItemDescriptorProxy + :id="row.sale.itemFk" + ></ItemDescriptorProxy> + </QTd> + </template> <template #body-cell-discount="{ row, value, rowIndex }"> <QTd auto-width align="right" class="text-primary"> {{ value }} diff --git a/src/pages/Claim/Card/ClaimNotes.vue b/src/pages/Claim/Card/ClaimNotes.vue index eeaffc85a..ed4c2040d 100644 --- a/src/pages/Claim/Card/ClaimNotes.vue +++ b/src/pages/Claim/Card/ClaimNotes.vue @@ -19,6 +19,12 @@ const claimFilter = { relation: 'worker', scope: { fields: ['id', 'firstName', 'lastName'], + include: { + relation: 'user', + scope: { + fields: ['id', 'nickname'], + }, + }, }, }, }; @@ -30,7 +36,8 @@ const body = { </script> <template> <div class="column items-center"> - <VnNotes style="overflow-y: scroll;" + <VnNotes + style="overflow-y: scroll" :add-note="$props.addNote" :id="id" url="claimObservations" diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue index dcd236027..cdc1f15be 100644 --- a/src/pages/Claim/Card/ClaimSummary.vue +++ b/src/pages/Claim/Card/ClaimSummary.vue @@ -10,6 +10,7 @@ import { useSession } from 'src/composables/useSession'; import VnLv from 'src/components/ui/VnLv.vue'; import ClaimNotes from 'src/pages/Claim/Card/ClaimNotes.vue'; import VnUserLink from 'src/components/ui/VnUserLink.vue'; +import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; const route = useRoute(); const { t } = useI18n(); @@ -42,11 +43,6 @@ onMounted(async () => { claimUrl.value = salixUrl.value + `claim/${entityId.value}/`; }); -watch(entityId, async (id) => { - claimDmsFilter.value.where = { claimFk: id }; - await claimDmsRef.value.fetch(); -}); - const detailsColumns = ref([ { name: 'item', @@ -101,11 +97,9 @@ const detailsColumns = ref([ ]); const STATE_COLOR = { - pending: 'positive', - - managed: 'warning', - - resolved: 'negative', + pending: 'warning', + managed: 'info', + resolved: 'positive', }; function stateColor(code) { return STATE_COLOR[code]; @@ -170,7 +164,6 @@ function openDialog(dmsId) { :filter="claimDmsFilter" @on-fetch="(data) => setClaimDms(data)" limit="20" - auto-load ref="claimDmsRef" /> <CardSummary ref="summary" :url="`Claims/${entityId}/getSummary`"> @@ -210,15 +203,29 @@ function openDialog(dmsId) { /> </template> </VnLv> + <VnLv :label="t('claim.summary.customer')"> + <template #value> + <VnUserLink + :name="claim.client?.name" + :worker-id="claim.client?.id" + /> + </template> + </VnLv> + <VnLv :label="t('claim.summary.returnOfMaterial')" :value="claim.rma" /> + <QCheckbox + :align-items="right" + :label="t('claim.basicData.picked')" + v-model="claim.hasToPickUp" + /> </QCard> - <QCard class="vn-max claimVnNotes"> + <QCard class="vn-three claimVnNotes full-height"> <a class="header" :href="`#/claim/${entityId}/notes`"> {{ t('claim.summary.notes') }} <QIcon name="open_in_new" color="primary" /> </a> <ClaimNotes :add-note="false" style="height: 350px" order="created ASC" /> </QCard> - <QCard class="vn-max" v-if="salesClaimed.length > 0"> + <QCard class="vn-two" v-if="salesClaimed.length > 0"> <a class="header" :href="`#/claim/${entityId}/lines`"> {{ t('claim.summary.details') }} <QIcon name="open_in_new" color="primary" /> @@ -231,8 +238,43 @@ function openDialog(dmsId) { </QTh> </QTr> </template> + <template #body="props"> + <QTr :props="props"> + <QTh v-for="col in props.cols" :key="col.name" :props="props"> + <span v-if="col.name != 'description'">{{ + t(col.value) + }}</span> + <QBtn + v-if="col.name == 'description'" + flat + color="blue" + >{{ col.value }}</QBtn + > + <ItemDescriptorProxy + v-if="col.name == 'description'" + :id="2" + ></ItemDescriptorProxy> + </QTh> + </QTr> + </template> </QTable> </QCard> + <QCard class="vn-two" v-if="developments.length > 0"> + <a class="header" :href="claimUrl + 'development'"> + {{ t('claim.summary.development') }} + <QIcon name="open_in_new" color="primary" /> + </a> + <QTable :columns="developmentColumns" :rows="developments" flat> + <template #header="props"> + <QTr :props="props"> + <QTh v-for="col in props.cols" :key="col.name" :props="props"> + {{ t(col.label) }} + </QTh> + </QTr> + </template> + </QTable> + </QCard> + <QCard class="vn-max" v-if="claimDms.length > 0"> <a class="header" :href="`#/claim/${entityId}/photos`"> {{ t('claim.summary.photos') }} @@ -275,22 +317,8 @@ function openDialog(dmsId) { </div> </div> </QCard> - <QCard class="vn-two" v-if="developments.length > 0"> - <a class="header" :href="claimUrl + 'development'"> - {{ t('claim.summary.development') }} - <QIcon name="open_in_new" color="primary" /> - </a> - <QTable :columns="developmentColumns" :rows="developments" flat> - <template #header="props"> - <QTr :props="props"> - <QTh v-for="col in props.cols" :key="col.name" :props="props"> - {{ t(col.label) }} - </QTh> - </QTr> - </template> - </QTable> - </QCard> - <QCard class="vn-max" v-if="developments.length > 0"> + + <QCard class="vn-max"> <a class="header" :href="claimUrl + 'action'"> {{ t('claim.summary.actions') }} <QIcon name="open_in_new" color="primary" /> diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue index e9462e7a9..322055b13 100644 --- a/src/pages/Claim/ClaimList.vue +++ b/src/pages/Claim/ClaimList.vue @@ -12,6 +12,7 @@ import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorP import VnUserLink from 'src/components/ui/VnUserLink.vue'; import ClaimSummary from './Card/ClaimSummary.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; +import { getUrl } from 'src/composables/getUrl'; const stateStore = useStateStore(); const router = useRouter(); @@ -19,14 +20,19 @@ const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); const STATE_COLOR = { - pending: 'positive', - managed: 'warning', - resolved: 'negative', + pending: 'warning', + managed: 'info', + resolved: 'positive', }; +function getApiUrl() { + return new URL(window.location).origin; +} function stateColor(code) { return STATE_COLOR[code]; } -function navigate(id) { +function navigate(event, id) { + if (event.ctrlKey || event.metaKey) + return window.open(`${getApiUrl()}/#/claim/${id}/summary`); router.push({ path: `/claim/${id}` }); } </script> @@ -66,7 +72,7 @@ function navigate(id) { <VnPaginate data-key="ClaimList" url="Claims/filter" - order="claimStateFk" + :order="['priority ASC', 'created DESC']" auto-load > <template #body="{ rows }"> @@ -74,7 +80,7 @@ function navigate(id) { :id="row.id" :key="row.id" :title="row.clientName" - @click="navigate(row.id)" + @click="navigate($event, row.id)" v-for="row of rows" > <template #list-items> @@ -109,12 +115,6 @@ function navigate(id) { </VnLv> </template> <template #actions> - <QBtn - :label="t('components.smartCard.openCard')" - @click.stop="navigate(row.id)" - class="bg-vn-dark" - outline - /> <QBtn :label="t('components.smartCard.viewDescription')" @click.stop diff --git a/src/pages/Customer/Card/CustomerDescriptor.vue b/src/pages/Customer/Card/CustomerDescriptor.vue index 222406d8a..2e8770fe7 100644 --- a/src/pages/Customer/Card/CustomerDescriptor.vue +++ b/src/pages/Customer/Card/CustomerDescriptor.vue @@ -19,10 +19,8 @@ const $props = defineProps({ default: null, }, }); - const route = useRoute(); const { t } = useI18n(); - const entityId = computed(() => { return $props.id || route.params.id; }); diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index c969c9b27..2a29a3d0e 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -186,6 +186,7 @@ async function upsert() { url="Suppliers" :fields="['id', 'nickname']" sort-by="nickname" + :is-clearable="false" > <template #option="scope"> <QItem v-bind="scope.itemProps"> diff --git a/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js b/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js index 7617a69d1..20f137aeb 100644 --- a/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js +++ b/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js @@ -1,6 +1,6 @@ /// <reference types="cypress" /> describe('InvoiceInBasicData', () => { - const selects = '.q-form .q-select'; + const selects = ':nth-child(1) > :nth-child(1) > .q-field'; const appendBtns = 'label button'; const dialogAppendBtns = '.q-dialog label button'; const dialogInputs = '.q-dialog input'; @@ -12,9 +12,7 @@ describe('InvoiceInBasicData', () => { }); it('should edit the provideer and supplier ref', () => { - cy.get(selects).eq(0).click(); - cy.get(selects).eq(0).type('Bros'); - cy.get(selects).eq(0).type('{enter}'); + cy.selectOption(selects, 'Bros'); cy.get('[title="Reset"]').click(); cy.get(appendBtns).eq(0).click(); diff --git a/test/cypress/integration/invoiceIn/invoiceInList.spec.js b/test/cypress/integration/invoiceIn/invoiceInList.spec.js index b96fd0cf2..a7d59883c 100644 --- a/test/cypress/integration/invoiceIn/invoiceInList.spec.js +++ b/test/cypress/integration/invoiceIn/invoiceInList.spec.js @@ -7,6 +7,7 @@ describe('InvoiceInList', () => { const summaryHeaders = '.summaryBody .header'; beforeEach(() => { + cy.viewport(1920, 1080); cy.login('developer'); cy.visit(`/#/invoice-in/list`); }); diff --git a/test/vitest/__tests__/pages/Claims/ClaimLines.spec.js b/test/vitest/__tests__/pages/Claims/ClaimLines.spec.js index 6dd6b89e6..2f2c0e298 100644 --- a/test/vitest/__tests__/pages/Claims/ClaimLines.spec.js +++ b/test/vitest/__tests__/pages/Claims/ClaimLines.spec.js @@ -1,6 +1,6 @@ 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'; +import ClaimLines from '/src/pages/Claim/Card/ClaimLines.vue'; describe('ClaimLines', () => { let vm;