forked from verdnatura/salix-front
Merge pull request 'fixes #5357 Convertir sección de histórico en componente reutilizable vnLog' (!48) from 5357-vnLog-component into dev
Reviewed-on: verdnatura/salix-front#48 Reviewed-by: Juan Ferrer <juan@verdnatura.es>
This commit is contained in:
commit
06645d941b
|
@ -39,6 +39,7 @@ onMounted(async () => {
|
|||
});
|
||||
|
||||
async function fetch() {
|
||||
try {
|
||||
const filter = Object.assign({}, $props.filter);
|
||||
if ($props.where) filter.where = $props.where;
|
||||
if ($props.sortBy) filter.order = $props.sortBy;
|
||||
|
@ -49,6 +50,9 @@ async function fetch() {
|
|||
});
|
||||
|
||||
emit('onFetch', data);
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
const render = () => {
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
import VnLogFilter from 'src/components/common/VnLogFilter.vue';
|
||||
|
||||
import { toDate } from 'src/filters';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const route = useRoute();
|
||||
const session = useSession();
|
||||
const token = session.getToken();
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
model: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'property',
|
||||
label: 'Property',
|
||||
field: (row) => t(`properties.${row.property}`),
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'before',
|
||||
label: 'Before',
|
||||
field: (row) => formatValue(row.before),
|
||||
},
|
||||
{
|
||||
name: 'after',
|
||||
label: 'After',
|
||||
field: (row) => formatValue(row.after),
|
||||
},
|
||||
];
|
||||
|
||||
function formatValue(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? t('Yes') : t('No');
|
||||
}
|
||||
|
||||
if (isNaN(value) && !isNaN(Date.parse(value))) {
|
||||
return toDate(value);
|
||||
}
|
||||
|
||||
if (value === undefined) {
|
||||
return t('Nothing');
|
||||
}
|
||||
|
||||
return `"${value}"`;
|
||||
}
|
||||
|
||||
function actionColor(action) {
|
||||
if (action === 'insert') return 'positive';
|
||||
if (action === 'update') return 'positive';
|
||||
if (action === 'delete') return 'negative';
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="column items-center">
|
||||
<QTimeline class="q-pa-md">
|
||||
<QTimelineEntry heading tag="h4"> {{ t('Audit logs') }} </QTimelineEntry>
|
||||
<VnPaginate
|
||||
:data-key="`${props.model}Logs`"
|
||||
:url="`${props.model}s/${route.params.id}/logs`"
|
||||
order="id DESC"
|
||||
:offset="100"
|
||||
:limit="5"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<template v-for="log of rows" :key="log.id">
|
||||
<QTimelineEntry
|
||||
:avatar="`/api/Images/user/160x160/${log.userFk}/download?access_token=${token}`"
|
||||
>
|
||||
<template #subtitle>
|
||||
{{ log.userName }} -
|
||||
{{
|
||||
toDate(log.created, {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: 'short',
|
||||
})
|
||||
}}
|
||||
</template>
|
||||
<template #title>
|
||||
<QChip :color="actionColor(log.action)">
|
||||
{{ t(`actions.${log.action}`) }}
|
||||
</QChip>
|
||||
{{ t(`models.${log.model}`) }}
|
||||
</template>
|
||||
<QTable
|
||||
:rows="log.changes"
|
||||
:columns="columns"
|
||||
row-key="property"
|
||||
hide-pagination
|
||||
dense
|
||||
flat
|
||||
>
|
||||
<template #header="propsLabel">
|
||||
<QTr :props="propsLabel">
|
||||
<QTh
|
||||
v-for="col in propsLabel.cols"
|
||||
:key="col.name"
|
||||
:props="propsLabel"
|
||||
>
|
||||
{{ t(col.label) }}
|
||||
</QTh>
|
||||
</QTr>
|
||||
</template>
|
||||
</QTable>
|
||||
</QTimelineEntry>
|
||||
</template>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
</QTimeline>
|
||||
</div>
|
||||
<Teleport v-if="stateStore.isHeaderMounted()" to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<QBtn flat @click="stateStore.toggleRightDrawer()" round dense icon="menu">
|
||||
<QTooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</div>
|
||||
</Teleport>
|
||||
<QDrawer v-model="stateStore.rightDrawer" show-if-above side="right" :width="300">
|
||||
<QScrollArea class="fit text-grey-8">
|
||||
<VnLogFilter :data-key="`${props.model}Logs`" />
|
||||
</QScrollArea>
|
||||
</QDrawer>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.q-timeline {
|
||||
width: 100%;
|
||||
max-width: 80em;
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
en:
|
||||
actions:
|
||||
insert: Creates
|
||||
update: Updates
|
||||
delete: Deletes
|
||||
models:
|
||||
Claim: Claim
|
||||
ClaimDms: Document
|
||||
ClaimBeginning: Claimed Sales
|
||||
ClaimObservation: Observation
|
||||
properties:
|
||||
id: ID
|
||||
claimFk: Claim ID
|
||||
saleFk: Sale ID
|
||||
quantity: Quantity
|
||||
observation: Observation
|
||||
ticketCreated: Created
|
||||
created: Created
|
||||
isChargedToMana: Charged to mana
|
||||
hasToPickUp: Has to pick Up
|
||||
dmsFk: Document ID
|
||||
text: Description
|
||||
claimStateFk: Claim State
|
||||
workerFk: Worker
|
||||
clientFk: Customer
|
||||
rma: RMA
|
||||
responsibility: Responsibility
|
||||
packages: Packages
|
||||
es:
|
||||
Audit logs: Registros de auditoría
|
||||
Property: Propiedad
|
||||
Before: Antes
|
||||
After: Después
|
||||
Yes: Si
|
||||
Nothing: Nada
|
||||
actions:
|
||||
insert: Crea
|
||||
update: Actualiza
|
||||
delete: Elimina
|
||||
models:
|
||||
Claim: Reclamación
|
||||
ClaimDms: Documento
|
||||
ClaimBeginning: Línea reclamada
|
||||
ClaimObservation: Observación
|
||||
properties:
|
||||
id: ID
|
||||
claimFk: ID reclamación
|
||||
saleFk: ID linea de venta
|
||||
quantity: Cantidad
|
||||
observation: Observación
|
||||
ticketCreated: Creado
|
||||
created: Creado
|
||||
isChargedToMana: Cargado a maná
|
||||
hasToPickUp: Se debe recoger
|
||||
dmsFk: ID documento
|
||||
text: Descripción
|
||||
claimStateFk: Estado de la reclamación
|
||||
workerFk: Trabajador
|
||||
clientFk: Cliente
|
||||
rma: RMA
|
||||
responsibility: Responsabilidad
|
||||
packages: Bultos
|
||||
</i18n>
|
|
@ -1,201 +1,6 @@
|
|||
<script setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useSession } from 'src/composables/useSession';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
import VnPaginate from 'src/components/ui/VnPaginate.vue';
|
||||
import ClaimLogFilter from './ClaimLogFilter.vue';
|
||||
|
||||
import { toDate } from 'src/filters';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const route = useRoute();
|
||||
const session = useSession();
|
||||
const token = session.getToken();
|
||||
const { t } = useI18n();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'property',
|
||||
label: 'Property',
|
||||
field: (row) => t(`properties.${row.property}`),
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
name: 'before',
|
||||
label: 'Before',
|
||||
field: (row) => formatValue(row.before),
|
||||
},
|
||||
{
|
||||
name: 'after',
|
||||
label: 'After',
|
||||
field: (row) => formatValue(row.after),
|
||||
},
|
||||
];
|
||||
|
||||
function formatValue(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? t('Yes') : t('No');
|
||||
}
|
||||
|
||||
if (isNaN(value) && !isNaN(Date.parse(value))) {
|
||||
return toDate(value);
|
||||
}
|
||||
|
||||
if (value === undefined) {
|
||||
return t('Nothing');
|
||||
}
|
||||
|
||||
return `"${value}"`;
|
||||
}
|
||||
|
||||
function actionColor(action) {
|
||||
if (action === 'insert') return 'positive';
|
||||
if (action === 'update') return 'positive';
|
||||
if (action === 'delete') return 'negative';
|
||||
}
|
||||
import VnLog from 'src/components/common/VnLog.vue';
|
||||
</script>
|
||||
<template>
|
||||
<div class="column items-center">
|
||||
<QTimeline class="q-pa-md">
|
||||
<QTimelineEntry heading tag="h4"> {{ t('Audit logs') }} </QTimelineEntry>
|
||||
<VnPaginate
|
||||
data-key="ClaimLogs"
|
||||
:url="`Claims/${route.params.id}/logs`"
|
||||
order="id DESC"
|
||||
:offset="100"
|
||||
:limit="5"
|
||||
auto-load
|
||||
>
|
||||
<template #body="{ rows }">
|
||||
<template v-for="log of rows" :key="log.id">
|
||||
<QTimelineEntry
|
||||
:avatar="`/api/Images/user/160x160/${log.userFk}/download?access_token=${token}`"
|
||||
>
|
||||
<template #subtitle>
|
||||
{{ log.userName }} -
|
||||
{{
|
||||
toDate(log.created, {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: 'short',
|
||||
})
|
||||
}}
|
||||
<VnLog model="Claim"></VnLog>
|
||||
</template>
|
||||
<template #title>
|
||||
<QChip :color="actionColor(log.action)">
|
||||
{{ t(`actions.${log.action}`) }}
|
||||
</QChip>
|
||||
{{ t(`models.${log.model}`) }}
|
||||
</template>
|
||||
<QTable
|
||||
:rows="log.changes"
|
||||
:columns="columns"
|
||||
row-key="property"
|
||||
hide-pagination
|
||||
dense
|
||||
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>
|
||||
</QTimelineEntry>
|
||||
</template>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
</QTimeline>
|
||||
</div>
|
||||
<Teleport v-if="stateStore.isHeaderMounted()" to="#actions-append">
|
||||
<div class="row q-gutter-x-sm">
|
||||
<QBtn flat @click="stateStore.toggleRightDrawer()" round dense icon="menu">
|
||||
<QTooltip bottom anchor="bottom right">
|
||||
{{ t('globals.collapseMenu') }}
|
||||
</QTooltip>
|
||||
</QBtn>
|
||||
</div>
|
||||
</Teleport>
|
||||
<QDrawer v-model="stateStore.rightDrawer" show-if-above side="right" :width="300">
|
||||
<QScrollArea class="fit text-grey-8">
|
||||
<ClaimLogFilter data-key="ClaimLogs" />
|
||||
</QScrollArea>
|
||||
</QDrawer>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.q-timeline {
|
||||
width: 100%;
|
||||
max-width: 80em;
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
en:
|
||||
actions:
|
||||
insert: Creates
|
||||
update: Updates
|
||||
delete: Deletes
|
||||
models:
|
||||
Claim: Claim
|
||||
ClaimDms: Document
|
||||
ClaimBeginning: Claimed Sales
|
||||
ClaimObservation: Observation
|
||||
properties:
|
||||
id: ID
|
||||
claimFk: Claim ID
|
||||
saleFk: Sale ID
|
||||
quantity: Quantity
|
||||
observation: Observation
|
||||
ticketCreated: Created
|
||||
created: Created
|
||||
isChargedToMana: Charged to mana
|
||||
hasToPickUp: Has to pick Up
|
||||
dmsFk: Document ID
|
||||
text: Description
|
||||
claimStateFk: Claim State
|
||||
workerFk: Worker
|
||||
clientFk: Customer
|
||||
rma: RMA
|
||||
responsibility: Responsibility
|
||||
packages: Packages
|
||||
es:
|
||||
Audit logs: Registros de auditoría
|
||||
Property: Propiedad
|
||||
Before: Antes
|
||||
After: Después
|
||||
Yes: Si
|
||||
Nothing: Nada
|
||||
actions:
|
||||
insert: Crea
|
||||
update: Actualiza
|
||||
delete: Elimina
|
||||
models:
|
||||
Claim: Reclamación
|
||||
ClaimDms: Documento
|
||||
ClaimBeginning: Línea reclamada
|
||||
ClaimObservation: Observación
|
||||
properties:
|
||||
id: ID
|
||||
claimFk: ID reclamación
|
||||
saleFk: ID linea de venta
|
||||
quantity: Cantidad
|
||||
observation: Observación
|
||||
ticketCreated: Creado
|
||||
created: Creado
|
||||
isChargedToMana: Cargado a maná
|
||||
hasToPickUp: Se debe recoger
|
||||
dmsFk: ID documento
|
||||
text: Descripción
|
||||
claimStateFk: Estado de la reclamación
|
||||
workerFk: Trabajador
|
||||
clientFk: Cliente
|
||||
rma: RMA
|
||||
responsibility: Responsabilidad
|
||||
packages: Bultos
|
||||
</i18n>
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
|
||||
import { createWrapper } from 'app/test/vitest/helper';
|
||||
import VnLog from 'src/components/common/VnLog.vue';
|
||||
|
||||
describe('VnLog', () => {
|
||||
let vm;
|
||||
beforeAll(() => {
|
||||
vm = createWrapper(VnLog, {
|
||||
global: {
|
||||
stubs: ['FetchData', 'VnPaginate'],
|
||||
mocks: {
|
||||
fetch: vi.fn(),
|
||||
},
|
||||
},
|
||||
propsData: {
|
||||
model: "Claim",
|
||||
},
|
||||
}).vm;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('formatValue()', () => {
|
||||
it('should return Yes if has a true boolean', async () => {
|
||||
const result = vm.formatValue(true);
|
||||
|
||||
expect(result).toEqual('Yes');
|
||||
});
|
||||
it('should return No if has a true boolean', async () => {
|
||||
const result = vm.formatValue(false);
|
||||
|
||||
expect(result).toEqual('No');
|
||||
});
|
||||
it('should return Nothing if has no params', async () => {
|
||||
const result = vm.formatValue();
|
||||
|
||||
expect(result).toEqual('Nothing');
|
||||
});
|
||||
it('should return a string from a string value', async () => {
|
||||
const result = vm.formatValue('Something');
|
||||
|
||||
expect(result).toEqual(`"Something"`);
|
||||
});
|
||||
it('should call to format a date', async () => {
|
||||
vi.mock('src/filters', () => ({
|
||||
toDate: ()=>{
|
||||
return "Date formatted"
|
||||
},
|
||||
}));
|
||||
|
||||
const result = vm.formatValue('01-01-1970');
|
||||
expect(result).toEqual("Date formatted");
|
||||
});
|
||||
});
|
||||
|
||||
describe('actionColor()', () => {
|
||||
it('should return positive if insert', async () => {
|
||||
const result = vm.actionColor('insert');
|
||||
|
||||
expect(result).toEqual('positive');
|
||||
});
|
||||
it('should return positive if update', async () => {
|
||||
const result = vm.actionColor('update');
|
||||
|
||||
expect(result).toEqual('positive');
|
||||
});
|
||||
it('should return negative if delete', async () => {
|
||||
const result = vm.actionColor('delete');
|
||||
|
||||
expect(result).toEqual('negative');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue