forked from verdnatura/salix-front
Merge branch 'test' of https://gitea.verdnatura.es/verdnatura/salix-front
This commit is contained in:
commit
95158b5bae
|
@ -61,7 +61,7 @@ function responseError(error) {
|
|||
router.push({ path: '/login' });
|
||||
}
|
||||
|
||||
return Promise.resolve(error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
axios.interceptors.response.use((response) => {
|
||||
|
|
|
@ -52,8 +52,7 @@ describe('App', () => {
|
|||
}
|
||||
};
|
||||
|
||||
await vm.responseError(response);
|
||||
|
||||
expect(vm.responseError(response)).rejects.toEqual(expect.objectContaining(response));
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(expect.objectContaining(
|
||||
{
|
||||
type: 'negative',
|
||||
|
@ -73,8 +72,7 @@ describe('App', () => {
|
|||
}
|
||||
};
|
||||
|
||||
await vm.responseError(response);
|
||||
|
||||
expect(vm.responseError(response)).rejects.toEqual(expect.objectContaining(response));
|
||||
expect(vm.quasar.notify).toHaveBeenCalledWith(expect.objectContaining(
|
||||
{
|
||||
type: 'negative',
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
<script setup>
|
||||
import { h, onMounted } from 'vue';
|
||||
import axios from 'axios';
|
||||
|
||||
const $props = defineProps({
|
||||
autoLoad: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
filter: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
where: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
sortBy: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
limit: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
defineExpose({ fetch });
|
||||
const emit = defineEmits(['onFetch']);
|
||||
|
||||
onMounted(async () => {
|
||||
if ($props.autoLoad) {
|
||||
await fetch();
|
||||
}
|
||||
});
|
||||
|
||||
async function fetch() {
|
||||
const filter = Object.assign({}, $props.filter);
|
||||
if ($props.where) filter.where = $props.where;
|
||||
if ($props.sortBy) filter.order = $props.sortBy;
|
||||
if ($props.limit) filter.limit = $props.limit;
|
||||
|
||||
const { data } = await axios.get($props.url, {
|
||||
params: { filter },
|
||||
});
|
||||
|
||||
emit('onFetch', data);
|
||||
}
|
||||
|
||||
const render = () => {
|
||||
return h('div', []);
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<render />
|
||||
</template>
|
|
@ -0,0 +1,118 @@
|
|||
<script setup>
|
||||
import { onMounted, onUnmounted, computed, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import axios from 'axios';
|
||||
|
||||
import { useState } from 'src/composables/useState';
|
||||
import { useValidator } from 'src/composables/useValidator';
|
||||
import SkeletonForm from 'src/components/SkeletonForm.vue';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
const state = useState();
|
||||
const { validate } = useValidator();
|
||||
|
||||
const $props = defineProps({
|
||||
url: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
model: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
filter: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onFetch']);
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
});
|
||||
|
||||
onMounted(async () => await fetch());
|
||||
|
||||
onUnmounted(() => {
|
||||
state.unset($props.model);
|
||||
});
|
||||
|
||||
const isLoading = ref(false);
|
||||
const hasChanges = ref(false);
|
||||
const formData = computed(() => state.get($props.model));
|
||||
const originalData = ref();
|
||||
|
||||
async function fetch() {
|
||||
const { data } = await axios.get($props.url, {
|
||||
params: { filter: $props.filter },
|
||||
});
|
||||
|
||||
state.set($props.model, data);
|
||||
originalData.value = Object.assign({}, data);
|
||||
|
||||
watch(formData.value, () => (hasChanges.value = true));
|
||||
|
||||
emit('onFetch', state.get($props.model));
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (!hasChanges.value) {
|
||||
return quasar.notify({
|
||||
type: 'negative',
|
||||
message: t('globals.noChanges'),
|
||||
});
|
||||
}
|
||||
isLoading.value = true;
|
||||
await axios.patch($props.url, formData.value);
|
||||
|
||||
originalData.value = formData.value;
|
||||
hasChanges.value = false;
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
state.set($props.model, originalData.value);
|
||||
hasChanges.value = false;
|
||||
}
|
||||
|
||||
function filter(value, update, filterOptions) {
|
||||
update(
|
||||
() => {
|
||||
const { options, filterFn } = filterOptions;
|
||||
|
||||
options.value = filterFn(options, value);
|
||||
},
|
||||
(ref) => {
|
||||
ref.setOptionIndex(-1);
|
||||
ref.moveOptionSelection(1, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<q-banner v-if="hasChanges" class="text-white bg-warning">
|
||||
<q-icon name="warning" size="md" class="q-mr-md" />
|
||||
<span>{{ t('globals.changesToSave') }}</span>
|
||||
</q-banner>
|
||||
<q-form v-if="formData" @submit="save" @reset="reset" class="q-pa-md">
|
||||
<slot name="form" :data="formData" :validate="validate" :filter="filter"></slot>
|
||||
<div class="q-mt-lg">
|
||||
<slot name="actions">
|
||||
<q-btn :label="t('globals.save')" type="submit" color="primary" />
|
||||
<q-btn
|
||||
:label="t('globals.reset')"
|
||||
type="reset"
|
||||
class="q-ml-sm"
|
||||
color="primary"
|
||||
flat
|
||||
:disable="!hasChanges"
|
||||
/>
|
||||
</slot>
|
||||
</div>
|
||||
</q-form>
|
||||
<skeleton-form v-if="!formData" />
|
||||
<q-inner-loading :showing="isLoading" :label="t('globals.pleaseWait')" color="primary" />
|
||||
</template>
|
|
@ -1,27 +1,39 @@
|
|||
<script setup>
|
||||
import axios from 'axios';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const $props = defineProps({
|
||||
autoLoad: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
filter: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
autoLoad: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
where: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
sortBy: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
limit: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
rowsPerPage: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
|
@ -33,7 +45,18 @@ const $props = defineProps({
|
|||
});
|
||||
|
||||
defineEmits(['onNavigate']);
|
||||
defineExpose({ fetch });
|
||||
defineExpose({ refresh });
|
||||
|
||||
onMounted(() => {
|
||||
if ($props.autoLoad) paginate();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => $props.data,
|
||||
() => {
|
||||
rows.value = $props.data;
|
||||
}
|
||||
);
|
||||
|
||||
const isLoading = ref(false);
|
||||
const hasMoreData = ref(false);
|
||||
|
@ -42,18 +65,12 @@ const pagination = ref({
|
|||
rowsPerPage: $props.rowsPerPage,
|
||||
page: 1,
|
||||
});
|
||||
|
||||
const rows = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
if ($props.autoLoad) fetch();
|
||||
else rows.value = [];
|
||||
});
|
||||
|
||||
async function fetch() {
|
||||
const { page, rowsPerPage, sortBy, descending } = pagination.value;
|
||||
const { page, rowsPerPage, sortBy } = pagination.value;
|
||||
|
||||
isLoading.value = true;
|
||||
if (!$props.url) return;
|
||||
|
||||
const filter = {
|
||||
limit: rowsPerPage,
|
||||
|
@ -62,12 +79,26 @@ async function fetch() {
|
|||
|
||||
Object.assign(filter, $props.filter);
|
||||
|
||||
if ($props.where) filter.where = $props.where;
|
||||
if ($props.sortBy) filter.order = $props.sortBy;
|
||||
if ($props.limit) filter.limit = $props.limit;
|
||||
|
||||
if (sortBy) filter.order = sortBy;
|
||||
|
||||
const { data } = await axios.get($props.url, {
|
||||
params: { filter },
|
||||
});
|
||||
|
||||
isLoading.value = false;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async function paginate() {
|
||||
const { page, rowsPerPage, sortBy, descending } = pagination.value;
|
||||
|
||||
const data = await fetch();
|
||||
|
||||
if (!data) {
|
||||
isLoading.value = false;
|
||||
return;
|
||||
|
@ -87,13 +118,31 @@ async function fetch() {
|
|||
isLoading.value = false;
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
const { rowsPerPage } = pagination.value;
|
||||
|
||||
const data = await fetch();
|
||||
|
||||
if (!data) {
|
||||
isLoading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
hasMoreData.value = data.length === rowsPerPage;
|
||||
|
||||
if (!rows.value) rows.value = [];
|
||||
rows.value = data;
|
||||
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
async function onLoad(...params) {
|
||||
const done = params[1];
|
||||
if (!rows.value || rows.value.length === 0) return done(false);
|
||||
if (!rows.value || rows.value.length === 0 || !$props.url) return done(false);
|
||||
|
||||
pagination.value.page = pagination.value.page + 1;
|
||||
|
||||
await fetch();
|
||||
await paginate();
|
||||
|
||||
const endOfPages = !hasMoreData.value;
|
||||
done(endOfPages);
|
||||
|
@ -103,34 +152,7 @@ async function onLoad(...params) {
|
|||
<template>
|
||||
<q-infinite-scroll @load="onLoad" :offset="offset" class="column items-center">
|
||||
<div v-if="rows" class="card-list q-gutter-y-md">
|
||||
<q-card class="card" v-for="row of rows" :key="row.id">
|
||||
<q-item v-ripple class="q-pa-none items-start cursor-pointer q-hoverable">
|
||||
<q-item-section class="q-pa-md" @click="$emit('onNavigate', row.id)">
|
||||
<slot name="header" :row="row">
|
||||
<div class="text-h6">{{ row.name }}</div>
|
||||
<q-item-label caption>#{{ row.id }}</q-item-label>
|
||||
</slot>
|
||||
<slot name="labels" :row="row"></slot>
|
||||
</q-item-section>
|
||||
<q-separator vertical />
|
||||
<q-card-actions vertical class="justify-between">
|
||||
<slot name="actions" :row="row">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
color="orange"
|
||||
icon="arrow_circle_right"
|
||||
@click="$emit('onNavigate', row.id)"
|
||||
>
|
||||
<q-tooltip>{{ t('components.smartCard.openCard') }}</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn flat round color="grey-7" icon="preview">
|
||||
<q-tooltip>{{ t('components.smartCard.openSummary') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</slot>
|
||||
</q-card-actions>
|
||||
</q-item>
|
||||
</q-card>
|
||||
<slot name="body" :rows="rows"></slot>
|
||||
<div v-if="!rows.length && !isLoading" class="info-row q-pa-md text-center">
|
||||
<h5>
|
||||
{{ t('components.smartCard.noData') }}
|
|
@ -1,4 +1,5 @@
|
|||
<template>
|
||||
<div class="q-pa-md">
|
||||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-skeleton type="QInput" square />
|
||||
|
@ -27,4 +28,5 @@
|
|||
<q-skeleton type="QBtn" />
|
||||
<q-skeleton type="QBtn" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { jest, describe, expect, it, beforeAll } from '@jest/globals';
|
||||
import { createWrapper, axios } from 'app/tests/jest/jestHelpers';
|
||||
import SmartCard from '../SmartCard.vue';
|
||||
import Paginate from '../Paginate.vue';
|
||||
|
||||
const mockPush = jest.fn();
|
||||
|
||||
|
@ -11,7 +11,7 @@ jest.mock('vue-router', () => ({
|
|||
}),
|
||||
}));
|
||||
|
||||
describe('SmartCard', () => {
|
||||
describe('Paginate', () => {
|
||||
const expectedUrl = '/api/customers';
|
||||
let vm;
|
||||
beforeAll(() => {
|
||||
|
@ -22,7 +22,7 @@ describe('SmartCard', () => {
|
|||
rowsPerPage: 3
|
||||
}
|
||||
};
|
||||
vm = createWrapper(SmartCard, options).vm;
|
||||
vm = createWrapper(Paginate, options).vm;
|
||||
|
||||
jest.spyOn(axios, 'get').mockResolvedValue({
|
||||
data: [
|
||||
|
@ -39,8 +39,8 @@ describe('SmartCard', () => {
|
|||
vm.hasMoreData = true;
|
||||
})
|
||||
|
||||
describe('fetch()', () => {
|
||||
it('should call to the fetch() method and set the data on the rows property', async () => {
|
||||
describe('paginate()', () => {
|
||||
it('should call to the paginate() method and set the data on the rows property', async () => {
|
||||
const expectedOptions = {
|
||||
params: {
|
||||
filter: {
|
||||
|
@ -51,13 +51,13 @@ describe('SmartCard', () => {
|
|||
}
|
||||
};
|
||||
|
||||
await vm.fetch();
|
||||
await vm.paginate();
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith(expectedUrl, expectedOptions);
|
||||
expect(vm.rows.length).toEqual(3);
|
||||
});
|
||||
|
||||
it('should call to the fetch() method and then call it again to paginate', async () => {
|
||||
it('should call to the paginate() method and then call it again to paginate', async () => {
|
||||
const expectedOptions = {
|
||||
params: {
|
||||
filter: {
|
||||
|
@ -68,7 +68,7 @@ describe('SmartCard', () => {
|
|||
}
|
||||
};
|
||||
|
||||
await vm.fetch();
|
||||
await vm.paginate();
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith(expectedUrl, expectedOptions);
|
||||
expect(vm.rows.length).toEqual(3);
|
||||
|
@ -85,7 +85,7 @@ describe('SmartCard', () => {
|
|||
|
||||
vm.pagination.page = 2;
|
||||
|
||||
await vm.fetch();
|
||||
await vm.paginate();
|
||||
|
||||
expect(axios.get).toHaveBeenCalledWith(expectedUrl, expectedOptionsPaginated);
|
||||
expect(vm.rows.length).toEqual(6);
|
|
@ -1,5 +1,7 @@
|
|||
import { ref, computed } from 'vue';
|
||||
|
||||
const state = ref({});
|
||||
|
||||
const user = ref({
|
||||
id: 0,
|
||||
name: '',
|
||||
|
@ -44,11 +46,27 @@ export function useState() {
|
|||
roles.value = data;
|
||||
}
|
||||
|
||||
function set(name, data) {
|
||||
state.value[name] = ref(data);
|
||||
}
|
||||
|
||||
function get(name) {
|
||||
return state.value[name];
|
||||
}
|
||||
|
||||
function unset(name) {
|
||||
delete state.value[name];
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
getUser,
|
||||
setUser,
|
||||
getRoles,
|
||||
setRoles,
|
||||
set,
|
||||
get,
|
||||
unset,
|
||||
drawer
|
||||
};
|
||||
}
|
||||
|
|
|
@ -33,10 +33,6 @@ export function useValidator() {
|
|||
return validations(validation)[validation.validation];
|
||||
});
|
||||
|
||||
if (property === 'socialName') {
|
||||
console.log(modelValidations[property])
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,11 @@ export default {
|
|||
yes: 'Yes',
|
||||
no: 'No',
|
||||
noChanges: 'No changes to save',
|
||||
changesToSave: 'You have changes pending to save',
|
||||
confirmRemove: 'You are about to delete this row. Are you sure?',
|
||||
rowAdded: 'Row added',
|
||||
rowRemoved: 'Row removed'
|
||||
rowRemoved: 'Row removed',
|
||||
pleaseWait: 'Please wait...'
|
||||
},
|
||||
moduleIndex: {
|
||||
allModules: 'All modules'
|
||||
|
@ -150,7 +152,8 @@ export default {
|
|||
list: 'List',
|
||||
createTicket: 'Create ticket',
|
||||
summary: 'Summary',
|
||||
basicData: 'Basic Data'
|
||||
basicData: 'Basic Data',
|
||||
boxing: 'Boxing'
|
||||
},
|
||||
list: {
|
||||
nickname: 'Nickname',
|
||||
|
@ -197,8 +200,7 @@ export default {
|
|||
state: 'State'
|
||||
},
|
||||
rmaList: {
|
||||
code: 'Code',
|
||||
newRma: 'New RMA...'
|
||||
code: 'Code'
|
||||
},
|
||||
rma: {
|
||||
user: 'User',
|
||||
|
|
|
@ -21,9 +21,11 @@ export default {
|
|||
yes: 'Si',
|
||||
no: 'No',
|
||||
noChanges: 'Sin cambios que guardar',
|
||||
changesToSave: 'Tienes cambios pendientes de guardar',
|
||||
confirmRemove: 'Vas a eliminar este registro. ¿Continuar?',
|
||||
rowAdded: 'Fila añadida',
|
||||
rowRemoved: 'Fila eliminada'
|
||||
rowRemoved: 'Fila eliminada',
|
||||
pleaseWait: 'Por favor, espera...'
|
||||
},
|
||||
moduleIndex: {
|
||||
allModules: 'Todos los módulos'
|
||||
|
@ -149,7 +151,8 @@ export default {
|
|||
list: 'Listado',
|
||||
createTicket: 'Crear ticket',
|
||||
summary: 'Resumen',
|
||||
basicData: 'Datos básicos'
|
||||
basicData: 'Datos básicos',
|
||||
boxing: 'Encajado'
|
||||
},
|
||||
list: {
|
||||
nickname: 'Alias',
|
||||
|
@ -196,8 +199,7 @@ export default {
|
|||
state: 'Estado'
|
||||
},
|
||||
rmaList: {
|
||||
code: 'Código',
|
||||
newRma: 'Nuevo RMA...'
|
||||
code: 'Código'
|
||||
},
|
||||
rma: {
|
||||
user: 'Usuario',
|
||||
|
|
|
@ -1,33 +1,18 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import axios from 'axios';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { useValidator } from 'src/composables/useValidator';
|
||||
import SkeletonForm from 'src/components/SkeletonForm.vue';
|
||||
|
||||
onMounted(() => {
|
||||
fetch();
|
||||
fetchWorkers();
|
||||
fetchClaimStates();
|
||||
});
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import FormModel from 'src/components/FormModel.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
const { validate } = useValidator();
|
||||
const session = useSession();
|
||||
const token = session.getToken();
|
||||
|
||||
const claim = ref(null);
|
||||
const claimCopy = ref(null);
|
||||
const hasChanges = ref(false);
|
||||
|
||||
function fetch() {
|
||||
const id = route.params.id;
|
||||
const filter = {
|
||||
const claimFilter = {
|
||||
include: [
|
||||
{
|
||||
relation: 'client',
|
||||
|
@ -36,117 +21,80 @@ function fetch() {
|
|||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const options = { params: { filter } };
|
||||
axios.get(`Claims/${id}`, options).then(({ data }) => {
|
||||
claim.value = data;
|
||||
claimCopy.value = Object.assign({}, data);
|
||||
|
||||
watch(claim.value, () => (hasChanges.value = true));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const workers = ref([]);
|
||||
const workersCopy = ref([]);
|
||||
function fetchWorkers() {
|
||||
const filter = {
|
||||
where: {
|
||||
role: 'salesPerson',
|
||||
},
|
||||
};
|
||||
const options = { params: { filter } };
|
||||
axios.get(`Workers/activeWithRole`, options).then(({ data }) => {
|
||||
workers.value = data;
|
||||
workersCopy.value = data;
|
||||
});
|
||||
}
|
||||
|
||||
const claimStates = ref([]);
|
||||
const claimStatesCopy = ref([]);
|
||||
function fetchClaimStates() {
|
||||
axios.get(`ClaimStates`).then(({ data }) => {
|
||||
|
||||
function setWorkers(data) {
|
||||
workers.value = data;
|
||||
workersCopy.value = data;
|
||||
}
|
||||
|
||||
function setClaimStates(data) {
|
||||
claimStates.value = data;
|
||||
claimStatesCopy.value = data;
|
||||
});
|
||||
}
|
||||
|
||||
function filter(value, update, options, originalOptions, filter) {
|
||||
update(
|
||||
() => {
|
||||
if (value === '') {
|
||||
options.value = originalOptions.value;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
options.value = options.value.filter(filter);
|
||||
},
|
||||
(ref) => {
|
||||
ref.setOptionIndex(-1);
|
||||
ref.moveOptionSelection(1, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function filterWorkers(value, update) {
|
||||
const workerFilter = {
|
||||
options: workers,
|
||||
filterFn: (options, value) => {
|
||||
const search = value.toLowerCase();
|
||||
|
||||
filter(value, update, workers, workersCopy, (row) => {
|
||||
if (value === '') return workersCopy.value;
|
||||
|
||||
return options.value.filter((row) => {
|
||||
const id = row.id;
|
||||
const name = row.name.toLowerCase();
|
||||
|
||||
const idMatch = id == search;
|
||||
const nameMatch = name.indexOf(search) > -1;
|
||||
const idMatches = id == search;
|
||||
const nameMatches = name.indexOf(search) > -1;
|
||||
|
||||
return idMatch || nameMatch;
|
||||
return idMatches || nameMatches;
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function filterStates(value, update) {
|
||||
const statesFilter = {
|
||||
options: claimStates,
|
||||
filterFn: (options, value) => {
|
||||
const search = value.toLowerCase();
|
||||
|
||||
filter(value, update, claimStates, claimStatesCopy, (row) => {
|
||||
if (value === '') return claimStatesCopy.value;
|
||||
|
||||
return options.value.filter((row) => {
|
||||
const description = row.description.toLowerCase();
|
||||
|
||||
return description.indexOf(search) > -1;
|
||||
});
|
||||
}
|
||||
|
||||
function save() {
|
||||
const id = route.params.id;
|
||||
const formData = claim.value;
|
||||
|
||||
if (!hasChanges.value) {
|
||||
return quasar.notify({
|
||||
type: 'negative',
|
||||
message: t('globals.noChanges'),
|
||||
});
|
||||
}
|
||||
|
||||
axios.patch(`Claims/${id}`, formData).then((hasChanges.value = false));
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
claim.value = claimCopy.value;
|
||||
hasChanges.value = false;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<fetch-data
|
||||
url="Workers/activeWithInheritedRole"
|
||||
:filter="{ where: { role: 'salesPerson' } }"
|
||||
@on-fetch="setWorkers"
|
||||
auto-load
|
||||
/>
|
||||
<fetch-data url="ClaimStates" @on-fetch="setClaimStates" auto-load />
|
||||
|
||||
<div class="container">
|
||||
<q-card class="q-pa-md">
|
||||
<skeleton-form v-if="!claim" />
|
||||
<q-form v-if="claim" @submit="save" @reset="onReset" greedy>
|
||||
<q-card>
|
||||
<form-model :url="`Claims/${route.params.id}`" :filter="claimFilter" model="claim">
|
||||
<template #form="{ data, validate, filter }">
|
||||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-input v-model="claim.client.name" :label="t('claim.basicData.customer')" disable />
|
||||
<q-input v-model="data.client.name" :label="t('claim.basicData.customer')" disable />
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input v-model="claim.created" mask="####-##-##" fill-mask="_" autofocus>
|
||||
<q-input v-model="data.created" mask="####-##-##" fill-mask="_" autofocus>
|
||||
<template #append>
|
||||
<q-icon name="event" class="cursor-pointer">
|
||||
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||
<q-date v-model="claim.created" mask="YYYY-MM-DD">
|
||||
<q-date v-model="data.created" mask="YYYY-MM-DD">
|
||||
<div class="row items-center justify-end">
|
||||
<q-btn v-close-popup label="Close" color="primary" flat />
|
||||
</div>
|
||||
|
@ -160,7 +108,7 @@ function onReset() {
|
|||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-select
|
||||
v-model="claim.workerFk"
|
||||
v-model="data.workerFk"
|
||||
:options="workers"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
|
@ -168,15 +116,15 @@ function onReset() {
|
|||
:label="t('claim.basicData.assignedTo')"
|
||||
map-options
|
||||
use-input
|
||||
@filter="filterWorkers"
|
||||
@filter="(value, update) => filter(value, update, workerFilter)"
|
||||
:rules="validate('claim.claimStateFk')"
|
||||
:input-debounce="0"
|
||||
>
|
||||
<template #before>
|
||||
<q-avatar color="orange">
|
||||
<q-img
|
||||
v-if="claim.workerFk"
|
||||
:src="`/api/Images/user/160x160/${claim.workerFk}/download?access_token=${token}`"
|
||||
v-if="data.workerFk"
|
||||
:src="`/api/Images/user/160x160/${data.workerFk}/download?access_token=${token}`"
|
||||
spinner-color="white"
|
||||
/>
|
||||
</q-avatar>
|
||||
|
@ -185,7 +133,7 @@ function onReset() {
|
|||
</div>
|
||||
<div class="col">
|
||||
<q-select
|
||||
v-model="claim.claimStateFk"
|
||||
v-model="data.claimStateFk"
|
||||
:options="claimStates"
|
||||
option-value="id"
|
||||
option-label="description"
|
||||
|
@ -193,7 +141,7 @@ function onReset() {
|
|||
:label="t('claim.basicData.state')"
|
||||
map-options
|
||||
use-input
|
||||
@filter="filterStates"
|
||||
@filter="(value, update) => filter(value, update, statesFilter)"
|
||||
:rules="validate('claim.claimStateFk')"
|
||||
:input-debounce="0"
|
||||
>
|
||||
|
@ -203,14 +151,14 @@ function onReset() {
|
|||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="claim.packages"
|
||||
v-model="data.packages"
|
||||
:label="t('claim.basicData.packages')"
|
||||
:rules="validate('claim.packages')"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="claim.rma"
|
||||
v-model="data.rma"
|
||||
:label="t('claim.basicData.returnOfMaterial')"
|
||||
:rules="validate('claim.rma')"
|
||||
/>
|
||||
|
@ -218,17 +166,13 @@ function onReset() {
|
|||
</div>
|
||||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-checkbox v-model="claim.hasToPickUp" :label="t('claim.basicData.picked')" />
|
||||
<q-checkbox v-model="data.hasToPickUp" :label="t('claim.basicData.picked')" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<q-btn :label="t('globals.save')" type="submit" color="primary" />
|
||||
<q-btn :label="t('globals.reset')" type="reset" class="q-ml-sm" color="primary" flat />
|
||||
</div>
|
||||
</q-form>
|
||||
</template>
|
||||
</form-model>
|
||||
</q-card>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -163,7 +163,9 @@ function stateColor(code) {
|
|||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
<router-view v-if="claim.id" :claim="claim"></router-view>
|
||||
<q-page class="q-pa-md">
|
||||
<router-view></router-view>
|
||||
</q-page>
|
||||
</q-page-container>
|
||||
</template>
|
||||
|
||||
|
@ -189,7 +191,7 @@ function stateColor(code) {
|
|||
}
|
||||
|
||||
#descriptor-skeleton .q-card__actions {
|
||||
justify-content: space-around;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
<script setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { useRoute } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
import SmartCard from 'src/components/SmartCard.vue';
|
||||
import Paginate from 'src/components/Paginate.vue';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import { toDate } from 'src/filters';
|
||||
|
||||
onMounted(() => fetch());
|
||||
|
||||
const $props = defineProps({
|
||||
claim: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const quasar = useQuasar();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
const claim = ref([]);
|
||||
const fetcher = ref();
|
||||
|
||||
const filter = {
|
||||
include: {
|
||||
relation: 'rmas',
|
||||
scope: {
|
||||
include: {
|
||||
relation: 'worker',
|
||||
scope: {
|
||||
|
@ -27,27 +27,24 @@ const filter = {
|
|||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
code: $props.claim.rma,
|
||||
order: 'created DESC',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function fetch() {
|
||||
//console.log($props.claim);
|
||||
}
|
||||
|
||||
function addRow() {
|
||||
async function addRow() {
|
||||
const formData = {
|
||||
code: $props.claim.rma,
|
||||
code: claim.value.rma,
|
||||
};
|
||||
|
||||
axios.post(`ClaimRmas`, formData).then(() => {
|
||||
await axios.post(`ClaimRmas`, formData);
|
||||
await fetcher.value.fetch();
|
||||
|
||||
quasar.notify({
|
||||
type: 'positive',
|
||||
message: t('globals.rowAdded'),
|
||||
icon: 'check',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const confirmShown = ref(false);
|
||||
|
@ -57,9 +54,11 @@ function confirmRemove(id) {
|
|||
rmaId.value = id;
|
||||
}
|
||||
|
||||
function remove() {
|
||||
async function remove() {
|
||||
const id = rmaId.value;
|
||||
axios.delete(`ClaimRmas/${id}`).then(() => {
|
||||
|
||||
await axios.delete(`ClaimRmas/${id}`);
|
||||
await fetcher.value.fetch();
|
||||
confirmShown.value = false;
|
||||
|
||||
quasar.notify({
|
||||
|
@ -67,7 +66,6 @@ function remove() {
|
|||
message: t('globals.rowRemoved'),
|
||||
icon: 'check',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function hide() {
|
||||
|
@ -75,7 +73,14 @@ function hide() {
|
|||
}
|
||||
</script>
|
||||
<template>
|
||||
<q-page class="q-pa-md sticky">
|
||||
<fetch-data
|
||||
ref="fetcher"
|
||||
:url="`Claims/${route.params.id}`"
|
||||
:filter="filter"
|
||||
@on-fetch="($data) => (claim = $data)"
|
||||
auto-load
|
||||
/>
|
||||
<div class="sticky-page">
|
||||
<q-page-sticky expand position="top">
|
||||
<q-toolbar class="bg-grey-9">
|
||||
<q-space />
|
||||
|
@ -85,28 +90,41 @@ function hide() {
|
|||
</q-toolbar>
|
||||
</q-page-sticky>
|
||||
|
||||
<smart-card ref="card" url="/ClaimRmas" :filter="filter" sort-by="id DESC" auto-load>
|
||||
<template #header="{ row }">
|
||||
<q-item-label caption>{{ t('claim.rma.user') }}</q-item-label>
|
||||
<q-item-label>{{ row.worker.user.name }}</q-item-label>
|
||||
</template>
|
||||
<template #labels="{ row }">
|
||||
<paginate :data="claim.rmas">
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card">
|
||||
<template v-for="row of rows" :key="row.id">
|
||||
<q-item class="q-pa-none items-start">
|
||||
<q-item-section class="q-pa-md">
|
||||
<q-list>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t('claim.rma.user') }}</q-item-label>
|
||||
<q-item-label>{{ row.worker.user.name }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t('claim.rma.created') }}</q-item-label>
|
||||
<q-item-label>{{ toDate(row.created, { timeStyle: 'medium' }) }}</q-item-label>
|
||||
<q-item-label>
|
||||
{{ toDate(row.created, { timeStyle: 'medium' }) }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
</q-item-section>
|
||||
<q-card-actions vertical class="justify-between">
|
||||
<q-btn flat round color="orange" icon="vn:bin" @click="confirmRemove(row.id)">
|
||||
<q-tooltip>{{ t('globals.remove') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
</smart-card>
|
||||
</q-page>
|
||||
</q-card>
|
||||
</template>
|
||||
</paginate>
|
||||
</div>
|
||||
<q-dialog v-model="confirmShown" persistent @hide="hide">
|
||||
<q-card>
|
||||
<q-card-section class="row items-center">
|
||||
|
@ -126,7 +144,7 @@ function hide() {
|
|||
.q-toolbar {
|
||||
background-color: $grey-9;
|
||||
}
|
||||
.sticky {
|
||||
.sticky-page {
|
||||
padding-top: 66px;
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,6 @@ function stateColor(code) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<div class="summary container">
|
||||
<q-card>
|
||||
<skeleton-summary v-if="!claim" />
|
||||
|
@ -158,7 +157,6 @@ function stateColor(code) {
|
|||
</template>
|
||||
</q-card>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -194,4 +192,8 @@ function stateColor(code) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.q-dialog .summary {
|
||||
max-width: 1200px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
<script setup>
|
||||
import { reactive, watch } from 'vue'
|
||||
|
||||
const customer = reactive({
|
||||
name: '',
|
||||
});
|
||||
|
||||
watch(() => customer.name, () => {
|
||||
console.log('customer.name changed');
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<q-card class="q-pa-md">
|
||||
<q-form @submit="onSubmit" @reset="onReset" class="q-gutter-md">
|
||||
<q-input
|
||||
filled
|
||||
v-model="customer.name"
|
||||
label="Your name *"
|
||||
hint="Name and surname"
|
||||
lazy-rules
|
||||
:rules="[val => val && val.length > 0 || 'Please type something']"
|
||||
/>
|
||||
|
||||
<q-input
|
||||
filled
|
||||
type="number"
|
||||
v-model="age"
|
||||
label="Your age *"
|
||||
lazy-rules
|
||||
:rules="[
|
||||
val => val !== null && val !== '' || 'Please type your age',
|
||||
val => val > 0 && val < 100 || 'Please type a real age'
|
||||
]"
|
||||
/>
|
||||
|
||||
<div>
|
||||
<q-btn label="Submit" type="submit" color="primary" />
|
||||
<q-btn label="Reset" type="reset" color="primary" flat class="q-ml-sm" />
|
||||
</div>
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
width: 100%;
|
||||
max-width: 60em;
|
||||
}
|
||||
</style>
|
|
@ -2,7 +2,7 @@
|
|||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import SmartCard from 'src/components/SmartCard.vue';
|
||||
import Paginate from 'src/components/Paginate.vue';
|
||||
import { toDate } from 'src/filters/index';
|
||||
import ClaimSummary from './Card/ClaimSummary.vue';
|
||||
|
||||
|
@ -50,8 +50,13 @@ function showPreview(id) {
|
|||
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<smart-card url="/Claims" :filter="filter" sort-by="id DESC" @on-navigate="navigate" auto-load>
|
||||
<template #labels="{ row }">
|
||||
<paginate url="/Claims" :filter="filter" sort-by="id DESC" auto-load>
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card" 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)">
|
||||
<div class="text-h6">{{ row.name }}</div>
|
||||
<q-item-label caption>#{{ row.id }}</q-item-label>
|
||||
<q-list>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
|
@ -78,8 +83,9 @@ function showPreview(id) {
|
|||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
</q-item-section>
|
||||
<q-separator vertical />
|
||||
<q-card-actions vertical class="justify-between">
|
||||
<q-btn color="grey-7" round flat icon="more_vert">
|
||||
<q-tooltip>{{ t('customer.list.moreOptions') }}</q-tooltip>
|
||||
<q-menu cover auto-close>
|
||||
|
@ -106,8 +112,11 @@ function showPreview(id) {
|
|||
<q-btn flat round color="grey-7" icon="preview" @click="showPreview(row.id)">
|
||||
<q-tooltip>{{ t('components.smartCard.openSummary') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</q-item>
|
||||
</q-card>
|
||||
</template>
|
||||
</smart-card>
|
||||
</paginate>
|
||||
</q-page>
|
||||
<q-dialog v-model="preview.shown">
|
||||
<claim-summary :claim-id="preview.data.claimId" />
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ref } from 'vue';
|
|||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import axios from 'axios';
|
||||
import SmartCard from 'src/components/SmartCard.vue';
|
||||
import Paginate from 'src/components/Paginate.vue';
|
||||
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
|
@ -26,7 +26,7 @@ function submit() {
|
|||
crated: new Date(),
|
||||
};
|
||||
})
|
||||
.then(() => card.value.fetch());
|
||||
.then(() => card.value.refresh());
|
||||
}
|
||||
|
||||
const confirmShown = ref(false);
|
||||
|
@ -38,7 +38,9 @@ function confirm(id) {
|
|||
|
||||
function remove() {
|
||||
const id = rmaId.value;
|
||||
axios.delete(`ClaimRmas/${id}`).then(() => {
|
||||
axios
|
||||
.delete(`ClaimRmas/${id}`)
|
||||
.then(() => {
|
||||
confirmShown.value = false;
|
||||
|
||||
quasar.notify({
|
||||
|
@ -46,7 +48,8 @@ function remove() {
|
|||
message: 'Entry deleted',
|
||||
icon: 'check',
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => card.value.refresh());
|
||||
}
|
||||
|
||||
function hide() {
|
||||
|
@ -59,14 +62,18 @@ function hide() {
|
|||
<q-page-sticky expand position="top" :offset="[16, 16]">
|
||||
<q-card class="card q-pa-md">
|
||||
<q-form @submit="submit">
|
||||
<q-input v-model="newRma.code" :label="t('claim.rmaList.newRma')" class="q-mb-md" />
|
||||
<div class="text-caption">$(0) entries</div>
|
||||
<q-input v-model="newRma.code" :label="t('claim.rmaList.code')" class="q-mb-md" autofocus />
|
||||
<!-- <div class="text-caption">$(0) entries</div> -->
|
||||
</q-form>
|
||||
</q-card>
|
||||
</q-page-sticky>
|
||||
|
||||
<smart-card ref="card" url="/ClaimRmas" sort-by="id DESC" auto-load>
|
||||
<template #labels="{ row }">
|
||||
<paginate ref="card" url="/ClaimRmas" sort-by="id DESC" auto-load>
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card">
|
||||
<template v-for="row of rows" :key="row.id">
|
||||
<q-item class="q-pa-none items-start">
|
||||
<q-item-section class="q-pa-md" @click="navigate(row.id)">
|
||||
<q-list>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
|
@ -75,13 +82,18 @@ function hide() {
|
|||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
</q-item-section>
|
||||
<q-card-actions vertical class="justify-between">
|
||||
<q-btn flat round color="primary" icon="vn:bin" @click="confirm(row.id)">
|
||||
<q-tooltip>{{ t('globals.remove') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</q-item>
|
||||
<q-separator />
|
||||
</template>
|
||||
</smart-card>
|
||||
</q-card>
|
||||
</template>
|
||||
</paginate>
|
||||
</q-page>
|
||||
|
||||
<q-dialog v-model="confirmShown" persistent @hide="hide">
|
||||
|
|
|
@ -1,135 +1,64 @@
|
|||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useQuasar } from 'quasar';
|
||||
import axios from 'axios';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { useValidator } from 'src/composables/useValidator';
|
||||
import SkeletonForm from 'src/components/SkeletonForm.vue';
|
||||
|
||||
onMounted(() => {
|
||||
fetch();
|
||||
fetchWorkers();
|
||||
fetchBusinessTypes();
|
||||
fetchContactChannels();
|
||||
});
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import FetchData from 'src/components/FetchData.vue';
|
||||
import FormModel from 'src/components/FormModel.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const quasar = useQuasar();
|
||||
const { t } = useI18n();
|
||||
const { validate } = useValidator();
|
||||
const session = useSession();
|
||||
const token = session.getToken();
|
||||
|
||||
const customer = ref(null);
|
||||
const customerCopy = ref(null);
|
||||
const hasChanges = ref(false);
|
||||
|
||||
function fetch() {
|
||||
const id = route.params.id;
|
||||
const filter = {
|
||||
include: [],
|
||||
};
|
||||
const options = { params: { filter } };
|
||||
axios.get(`Clients/${id}`, options).then(({ data }) => {
|
||||
customer.value = data;
|
||||
customerCopy.value = Object.assign({}, data);
|
||||
|
||||
watch(customer.value, () => (hasChanges.value = true));
|
||||
});
|
||||
}
|
||||
|
||||
const businessTypes = ref([]);
|
||||
function fetchBusinessTypes() {
|
||||
axios.get(`BusinessTypes`).then(({ data }) => {
|
||||
businessTypes.value = data;
|
||||
});
|
||||
}
|
||||
|
||||
const contactChannels = ref([]);
|
||||
function fetchContactChannels() {
|
||||
axios.get(`ContactChannels`).then(({ data }) => {
|
||||
contactChannels.value = data;
|
||||
});
|
||||
}
|
||||
|
||||
const workers = ref([]);
|
||||
const workersCopy = ref([]);
|
||||
function fetchWorkers() {
|
||||
const filter = {
|
||||
where: {
|
||||
role: 'salesPerson',
|
||||
},
|
||||
};
|
||||
const options = { params: { filter } };
|
||||
axios.get(`Workers/activeWithRole`, options).then(({ data }) => {
|
||||
const businessTypes = ref([]);
|
||||
const contactChannels = ref([]);
|
||||
|
||||
function setWorkers(data) {
|
||||
workers.value = data;
|
||||
workersCopy.value = data;
|
||||
});
|
||||
}
|
||||
|
||||
function filter(value, update, options, originalOptions, filter) {
|
||||
update(
|
||||
() => {
|
||||
if (value === '') {
|
||||
options.value = originalOptions.value;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
options.value = options.value.filter(filter);
|
||||
},
|
||||
(ref) => {
|
||||
ref.setOptionIndex(-1);
|
||||
ref.moveOptionSelection(1, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function filterWorkers(value, update) {
|
||||
const filterOptions = {
|
||||
options: workers,
|
||||
filterFn: (options, value) => {
|
||||
const search = value.toLowerCase();
|
||||
|
||||
filter(value, update, workers, workersCopy, (row) => {
|
||||
if (value === '') return workersCopy.value;
|
||||
|
||||
return options.value.filter((row) => {
|
||||
const id = row.id;
|
||||
const name = row.name.toLowerCase();
|
||||
|
||||
const idMatch = id == search;
|
||||
const nameMatch = name.indexOf(search) > -1;
|
||||
const idMatches = id == search;
|
||||
const nameMatches = name.indexOf(search) > -1;
|
||||
|
||||
return idMatch || nameMatch;
|
||||
return idMatches || nameMatches;
|
||||
});
|
||||
}
|
||||
|
||||
function save() {
|
||||
const id = route.params.id;
|
||||
const formData = customer.value;
|
||||
|
||||
if (!hasChanges.value) {
|
||||
return quasar.notify({
|
||||
type: 'negative',
|
||||
message: t('globals.noChanges'),
|
||||
});
|
||||
}
|
||||
|
||||
axios.patch(`Clients/${id}`, formData).then((hasChanges.value = false));
|
||||
}
|
||||
|
||||
function onReset() {
|
||||
customer.value = customerCopy.value;
|
||||
hasChanges.value = false;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<fetch-data
|
||||
url="Workers/activeWithInheritedRole"
|
||||
:filter="{ where: { role: 'salesPerson' } }"
|
||||
@on-fetch="setWorkers"
|
||||
auto-load
|
||||
/>
|
||||
<fetch-data url="ContactChannels" @on-fetch="($data) => (contactChannels = $data)" auto-load />
|
||||
<fetch-data url="BusinessTypes" @on-fetch="($data) => (businessTypes = $data)" auto-load />
|
||||
|
||||
<div class="container">
|
||||
<q-card class="q-pa-md">
|
||||
<skeleton-form v-if="!customer" />
|
||||
<q-form v-if="customer" @submit="save" @reset="onReset" greedy>
|
||||
<q-card>
|
||||
<form-model :url="`Clients/${route.params.id}`" model="customer">
|
||||
<template #form="{ data, validate, filter }">
|
||||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="customer.socialName"
|
||||
v-model="data.socialName"
|
||||
:label="t('customer.basicData.socialName')"
|
||||
:rules="validate('client.socialName')"
|
||||
autofocus
|
||||
|
@ -137,7 +66,7 @@ function onReset() {
|
|||
</div>
|
||||
<div class="col">
|
||||
<q-select
|
||||
v-model="customer.businessTypeFk"
|
||||
v-model="data.businessTypeFk"
|
||||
:options="businessTypes"
|
||||
option-value="code"
|
||||
option-label="description"
|
||||
|
@ -152,7 +81,7 @@ function onReset() {
|
|||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="customer.contact"
|
||||
v-model="data.contact"
|
||||
:label="t('customer.basicData.contact')"
|
||||
:rules="validate('client.contact')"
|
||||
clearable
|
||||
|
@ -160,7 +89,7 @@ function onReset() {
|
|||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="customer.email"
|
||||
v-model="data.email"
|
||||
type="email"
|
||||
:label="t('customer.basicData.email')"
|
||||
:rules="validate('client.email')"
|
||||
|
@ -171,7 +100,7 @@ function onReset() {
|
|||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="customer.phone"
|
||||
v-model="data.phone"
|
||||
:label="t('customer.basicData.phone')"
|
||||
:rules="validate('client.phone')"
|
||||
clearable
|
||||
|
@ -179,7 +108,7 @@ function onReset() {
|
|||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="customer.mobile"
|
||||
v-model="data.mobile"
|
||||
:label="t('customer.basicData.mobile')"
|
||||
:rules="validate('client.mobile')"
|
||||
clearable
|
||||
|
@ -189,7 +118,7 @@ function onReset() {
|
|||
<div class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<q-select
|
||||
v-model="customer.salesPersonFk"
|
||||
v-model="data.salesPersonFk"
|
||||
:options="workers"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
|
@ -197,15 +126,15 @@ function onReset() {
|
|||
:label="t('customer.basicData.salesPerson')"
|
||||
map-options
|
||||
use-input
|
||||
@filter="filterWorkers"
|
||||
@filter="(value, update) => filter(value, update, filterOptions)"
|
||||
:rules="validate('client.salesPersonFk')"
|
||||
:input-debounce="0"
|
||||
>
|
||||
<template #before>
|
||||
<q-avatar color="orange">
|
||||
<q-img
|
||||
v-if="customer.salesPersonFk"
|
||||
:src="`/api/Images/user/160x160/${customer.salesPersonFk}/download?access_token=${token}`"
|
||||
v-if="data.salesPersonFk"
|
||||
:src="`/api/Images/user/160x160/${data.salesPersonFk}/download?access_token=${token}`"
|
||||
spinner-color="white"
|
||||
/>
|
||||
</q-avatar>
|
||||
|
@ -214,7 +143,7 @@ function onReset() {
|
|||
</div>
|
||||
<div class="col">
|
||||
<q-select
|
||||
v-model="customer.contactChannelFk"
|
||||
v-model="data.contactChannelFk"
|
||||
:options="contactChannels"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
|
@ -226,14 +155,10 @@ function onReset() {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<q-btn :label="t('globals.save')" type="submit" color="primary" />
|
||||
<q-btn :label="t('globals.reset')" type="reset" class="q-ml-sm" color="primary" flat />
|
||||
</div>
|
||||
</q-form>
|
||||
</template>
|
||||
</form-model>
|
||||
</q-card>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -174,7 +174,9 @@ async function fetch() {
|
|||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
<q-page class="q-pa-md">
|
||||
<router-view></router-view>
|
||||
</q-page>
|
||||
</q-page-container>
|
||||
</template>
|
||||
|
||||
|
@ -192,5 +194,9 @@ async function fetch() {
|
|||
.q-card__actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#descriptor-skeleton .q-card__actions {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -29,9 +29,7 @@ function fetch() {
|
|||
}
|
||||
|
||||
const balanceDue = computed(() => {
|
||||
const [defaulter] = customer.value.defaulters;
|
||||
|
||||
return defaulter.amount;
|
||||
return customer.value.defaulters.length && customer.value.defaulters[0].amount;
|
||||
});
|
||||
|
||||
const balanceDueWarning = computed(() => (balanceDue.value ? 'negative' : ''));
|
||||
|
@ -64,7 +62,6 @@ const creditWarning = computed(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<div class="summary container">
|
||||
<q-card>
|
||||
<skeleton-summary v-if="!customer" />
|
||||
|
@ -75,7 +72,10 @@ const creditWarning = computed(() => {
|
|||
<q-list>
|
||||
<q-item-label header class="text-h6">
|
||||
{{ t('customer.summary.basicData') }}
|
||||
<router-link :to="{ name: 'CustomerBasicData' }">
|
||||
<router-link
|
||||
:to="{ name: 'CustomerBasicData', params: { id: entityId } }"
|
||||
target="_blank"
|
||||
>
|
||||
<q-icon name="open_in_new" />
|
||||
</router-link>
|
||||
</q-item-label>
|
||||
|
@ -98,7 +98,7 @@ const creditWarning = computed(() => {
|
|||
<q-item-label>{{ customer.contact }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item v-if="customer.salesPersonUser">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t('customer.summary.salesPerson') }}</q-item-label>
|
||||
<q-item-label>{{ customer.salesPersonUser.name }}</q-item-label>
|
||||
|
@ -122,7 +122,7 @@ const creditWarning = computed(() => {
|
|||
<q-item-label>{{ customer.email }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item v-if="customer.contactChannel">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t('customer.summary.contactChannel') }}</q-item-label>
|
||||
<q-item-label>{{ customer.contactChannel.name }}</q-item-label>
|
||||
|
@ -153,13 +153,13 @@ const creditWarning = computed(() => {
|
|||
<q-item-label>{{ customer.postcode }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item v-if="customer.province">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t('customer.summary.province') }}</q-item-label>
|
||||
<q-item-label>{{ customer.province.name }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item v-if="customer.country">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t('customer.summary.country') }}</q-item-label>
|
||||
<q-item-label>{{ customer.country.country }}</q-item-label>
|
||||
|
@ -249,11 +249,7 @@ const creditWarning = computed(() => {
|
|||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item dense>
|
||||
<q-checkbox
|
||||
v-model="customer.hasLcr"
|
||||
:label="t('customer.summary.hasLcr')"
|
||||
disable
|
||||
/>
|
||||
<q-checkbox v-model="customer.hasLcr" :label="t('customer.summary.hasLcr')" disable />
|
||||
</q-item>
|
||||
<q-item dense>
|
||||
<q-checkbox
|
||||
|
@ -271,7 +267,7 @@ const creditWarning = computed(() => {
|
|||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="col" v-if="customer.defaultAddress">
|
||||
<q-list>
|
||||
<q-item-label header class="text-h6">
|
||||
{{ t('customer.summary.consignee') }}
|
||||
|
@ -296,7 +292,7 @@ const creditWarning = computed(() => {
|
|||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="col" v-if="customer.account">
|
||||
<q-list>
|
||||
<q-item-label header class="text-h6">
|
||||
{{ t('customer.summary.webAccess') }}
|
||||
|
@ -333,11 +329,11 @@ const creditWarning = computed(() => {
|
|||
<q-item-label>{{ toCurrency(customer.mana.mana) }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item v-if="customer.claimsRatio">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{
|
||||
t('customer.summary.priceIncreasingRate')
|
||||
}}</q-item-label>
|
||||
<q-item-label caption>
|
||||
{{ t('customer.summary.priceIncreasingRate') }}
|
||||
</q-item-label>
|
||||
<q-item-label>{{ toPercentage(priceIncreasingRate) }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
|
@ -347,7 +343,7 @@ const creditWarning = computed(() => {
|
|||
<q-item-label>{{ toCurrency(customer.averageInvoiced.invoiced) }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item v-if="customer.claimsRatio">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t('customer.summary.claimRate') }}</q-item-label>
|
||||
<q-item-label>{{ toPercentage(claimRate) }}</q-item-label>
|
||||
|
@ -360,7 +356,7 @@ const creditWarning = computed(() => {
|
|||
<q-item-label header class="text-h6">
|
||||
{{ t('customer.summary.financialData') }}
|
||||
</q-item-label>
|
||||
<q-item>
|
||||
<q-item v-if="customer.debt">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t('customer.summary.risk') }}</q-item-label>
|
||||
<q-item-label :class="debtWarning">
|
||||
|
@ -386,7 +382,7 @@ const creditWarning = computed(() => {
|
|||
</q-icon>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item v-if="customer.creditInsurance">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t('customer.summary.securedCredit') }}</q-item-label>
|
||||
<q-item-label>{{ toCurrency(customer.creditInsurance) }}</q-item-label>
|
||||
|
@ -408,7 +404,7 @@ const creditWarning = computed(() => {
|
|||
</q-icon>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item>
|
||||
<q-item v-if="customer.defaulters">
|
||||
<q-item-section>
|
||||
<q-item-label caption>{{ t('customer.summary.balanceDue') }}</q-item-label>
|
||||
<q-item-label :class="balanceDueWarning">
|
||||
|
@ -433,9 +429,7 @@ const creditWarning = computed(() => {
|
|||
</template>
|
||||
</q-card>
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
|
@ -491,4 +485,8 @@ const creditWarning = computed(() => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.q-dialog .summary {
|
||||
max-width: 1200px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import SmartCard from 'src/components/SmartCard.vue';
|
||||
import Paginate from 'src/components/Paginate.vue';
|
||||
import CustomerSummary from './Card/CustomerSummary.vue';
|
||||
|
||||
const router = useRouter();
|
||||
|
@ -26,8 +26,13 @@ function showPreview(id) {
|
|||
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<smart-card url="/Clients" sort-by="id DESC" @on-navigate="navigate" auto-load>
|
||||
<template #labels="{ row }">
|
||||
<paginate url="/Clients" sort-by="id DESC" auto-load>
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card" 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)">
|
||||
<div class="text-h6">{{ row.name }}</div>
|
||||
<q-item-label caption>#{{ row.id }}</q-item-label>
|
||||
<q-list>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
|
@ -42,8 +47,9 @@ function showPreview(id) {
|
|||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
</q-item-section>
|
||||
<q-separator vertical />
|
||||
<q-card-actions vertical class="justify-between">
|
||||
<q-btn color="grey-7" round flat icon="more_vert">
|
||||
<q-tooltip>{{ t('customer.list.moreOptions') }}</q-tooltip>
|
||||
<q-menu cover auto-close>
|
||||
|
@ -73,8 +79,11 @@ function showPreview(id) {
|
|||
<q-btn flat round color="grey-7" icon="vn:ticket">
|
||||
<q-tooltip>{{ t('customer.list.customerOrders') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</q-item>
|
||||
</q-card>
|
||||
</template>
|
||||
</smart-card>
|
||||
</paginate>
|
||||
</q-page>
|
||||
<q-dialog v-model="preview.shown">
|
||||
<customer-summary :customer-id="preview.data.customerId" />
|
||||
|
|
|
@ -79,8 +79,7 @@ async function getVideoList(expeditionId, timed) {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<q-layout view="hhh lpr ffr" class="fit">
|
||||
<q-drawer show-if-above side="right" bordered>
|
||||
<q-drawer show-if-above side="right">
|
||||
<q-scroll-area class="fit">
|
||||
<q-list bordered separator style="max-width: 318px">
|
||||
<q-item v-if="lastExpedition && videoList.length">
|
||||
|
@ -144,8 +143,6 @@ async function getVideoList(expeditionId, timed) {
|
|||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
|
||||
<q-page-container>
|
||||
<q-page>
|
||||
<q-card>
|
||||
<q-carousel animated v-model="slide" height="max-content">
|
||||
<q-carousel-slide v-for="video in videoList" :key="video.value" :name="video.value">
|
||||
|
@ -153,7 +150,4 @@ async function getVideoList(expeditionId, timed) {
|
|||
</q-carousel-slide>
|
||||
</q-carousel>
|
||||
</q-card>
|
||||
</q-page>
|
||||
</q-page-container>
|
||||
</q-layout>
|
||||
</template>
|
||||
|
|
|
@ -147,17 +147,19 @@ function stateColor(state) {
|
|||
<q-separator />
|
||||
|
||||
<q-list>
|
||||
<!-- <q-item :to="{ name: 'TicketBasicData' }" clickable v-ripple>
|
||||
<q-item :to="{ name: 'TicketBoxing' }" clickable v-ripple>
|
||||
<q-item-section avatar>
|
||||
<q-icon name="vn:settings" />
|
||||
<q-icon name="vn:package" />
|
||||
</q-item-section>
|
||||
<q-item-section>{{ t('ticket.pageTitles.basicData') }}</q-item-section>
|
||||
</q-item> -->
|
||||
<q-item-section>{{ t('ticket.pageTitles.boxing') }}</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-scroll-area> </q-drawer
|
||||
>-->
|
||||
</q-scroll-area>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
<q-page class="q-pa-md">
|
||||
<router-view></router-view>
|
||||
</q-page>
|
||||
</q-page-container>
|
||||
</template>
|
||||
|
||||
|
@ -181,5 +183,9 @@ function stateColor(state) {
|
|||
.q-card__actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#descriptor-skeleton .q-card__actions {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import SmartCard from 'src/components/SmartCard.vue';
|
||||
import Paginate from 'src/components/Paginate.vue';
|
||||
import { toDate, toCurrency } from 'src/filters/index';
|
||||
// import CustomerSummary from './Card/CustomerSummary.vue';
|
||||
// import TicketSummary from './Card/TicketSummary.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
|
@ -62,8 +62,13 @@ function showPreview(id) {
|
|||
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<smart-card url="/Tickets" :filter="filter" sort-by="id DESC" @on-navigate="navigate" auto-load>
|
||||
<template #labels="{ row }">
|
||||
<paginate url="/Tickets" :filter="filter" sort-by="id DESC" auto-load>
|
||||
<template #body="{ rows }">
|
||||
<q-card class="card" 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)">
|
||||
<div class="text-h6">{{ row.name }}</div>
|
||||
<q-item-label caption>#{{ row.id }}</q-item-label>
|
||||
<q-list>
|
||||
<q-item class="q-pa-none">
|
||||
<q-item-section>
|
||||
|
@ -100,8 +105,9 @@ function showPreview(id) {
|
|||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
</q-item-section>
|
||||
<q-separator vertical />
|
||||
<q-card-actions vertical class="justify-between">
|
||||
<q-btn color="grey-7" round flat icon="more_vert">
|
||||
<q-tooltip>{{ t('customer.list.moreOptions') }}</q-tooltip>
|
||||
<q-menu cover auto-close>
|
||||
|
@ -131,8 +137,11 @@ function showPreview(id) {
|
|||
<q-btn flat round color="grey-7" icon="vn:ticket">
|
||||
<q-tooltip>{{ t('customer.list.customerOrders') }}</q-tooltip>
|
||||
</q-btn>
|
||||
</q-card-actions>
|
||||
</q-item>
|
||||
</q-card>
|
||||
</template>
|
||||
</smart-card>
|
||||
</paginate>
|
||||
</q-page>
|
||||
<!-- <q-dialog v-model="preview.shown">
|
||||
<customer-summary :customer-id="preview.data.customerId" />
|
||||
|
|
|
@ -34,15 +34,6 @@ export default {
|
|||
roles: ['claimManager']
|
||||
},
|
||||
component: () => import('src/pages/Claim/ClaimRmaList.vue'),
|
||||
},
|
||||
{
|
||||
name: 'ClaimCreate',
|
||||
path: 'create',
|
||||
meta: {
|
||||
title: 'createClaim',
|
||||
icon: 'vn:addperson',
|
||||
},
|
||||
component: () => import('src/pages/Claim/ClaimCreate.vue'),
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -76,8 +67,7 @@ export default {
|
|||
title: 'rma',
|
||||
roles: ['claimManager']
|
||||
},
|
||||
component: () => import('src/pages/Claim/Card/ClaimRma.vue'),
|
||||
props: { claim: true }
|
||||
component: () => import('src/pages/Claim/Card/ClaimRma.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue