WIP: feat: refs #6726 add new section "logs" #903
|
@ -1,7 +1,7 @@
|
|||
<script setup>
|
||||
import { ref, onUnmounted, watch } from 'vue';
|
||||
import { ref, onUnmounted, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useRoute } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
import { date } from 'quasar';
|
||||
import { useStateStore } from 'stores/useStateStore';
|
||||
|
@ -11,16 +11,14 @@ import { useCapitalize } from 'src/composables/useCapitalize';
|
|||
import { useValidator } from 'src/composables/useValidator';
|
||||
import VnAvatar from '../ui/VnAvatar.vue';
|
||||
import VnJsonValue from '../common/VnJsonValue.vue';
|
||||
import FetchData from '../FetchData.vue';
|
||||
import VnSelect from './VnSelect.vue';
|
||||
import VnUserLink from '../ui/VnUserLink.vue';
|
||||
import VnPaginate from '../ui/VnPaginate.vue';
|
||||
import VnLogFilter from './VnLogFilter.vue';
|
||||
import VnTable from '../VnTable/VnTable.vue';
|
||||
|
||||
const stateStore = useStateStore();
|
||||
const validationsStore = useValidator();
|
||||
const { models } = validationsStore;
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
model: {
|
||||
|
@ -35,8 +33,45 @@ const props = defineProps({
|
|||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
disableModel: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const paginate = ref({});
|
||||
const searchInput = ref();
|
||||
const modelName = computed(() => params.value.model || props.model);
|
||||
const selectedFilters = ref({});
|
||||
const params = ref({ originFk: paginate.value?.params?.originFk ?? route.params.id });
|
||||
let validations = models;
|
||||
let pointRecord = ref(null);
|
||||
let byRecord = ref(false);
|
||||
const logTree = ref([]);
|
||||
|
||||
const actionsText = {
|
||||
insert: 'Creates',
|
||||
update: 'Edits',
|
||||
delete: 'Deletes',
|
||||
select: 'Accesses',
|
||||
};
|
||||
const actionsClass = {
|
||||
insert: 'success',
|
||||
update: 'warning',
|
||||
delete: 'alert',
|
||||
select: 'notice',
|
||||
};
|
||||
const actionsIcon = {
|
||||
insert: 'add',
|
||||
update: 'edit',
|
||||
delete: 'remove',
|
||||
select: 'visibility',
|
||||
};
|
||||
const validDate = new RegExp(
|
||||
/^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])/.source +
|
||||
/T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/.source
|
||||
);
|
||||
|
||||
const filter = {
|
||||
fields: [
|
||||
'id',
|
||||
|
@ -67,72 +102,13 @@ const filter = {
|
|||
},
|
||||
},
|
||||
],
|
||||
where: { and: [{ originFk: route.params.id }] },
|
||||
orderBy: ['originFk DESC', 'creationDate DESC'],
|
||||
};
|
||||
|
||||
const paginate = ref();
|
||||
const actions = ref();
|
||||
const changeInput = ref();
|
||||
const searchInput = ref();
|
||||
const userRadio = ref();
|
||||
const userSelect = ref();
|
||||
const dateFrom = ref();
|
||||
const dateFromDialog = ref(false);
|
||||
const dateTo = ref();
|
||||
const dateToDialog = ref(false);
|
||||
const selectedFilters = ref({});
|
||||
const userTypes = [
|
||||
{ label: 'All', value: undefined },
|
||||
{ label: 'User', value: { neq: null } },
|
||||
{ label: 'System', value: null },
|
||||
];
|
||||
const checkboxOptions = ref({
|
||||
insert: {
|
||||
label: 'Creates',
|
||||
selected: false,
|
||||
},
|
||||
update: {
|
||||
label: 'Edits',
|
||||
selected: false,
|
||||
},
|
||||
delete: {
|
||||
label: 'Deletes',
|
||||
selected: false,
|
||||
},
|
||||
select: {
|
||||
label: 'Accesses',
|
||||
selected: false,
|
||||
},
|
||||
onUnmounted(() => {
|
||||
stateStore.rightDrawer = false;
|
||||
});
|
||||
|
||||
let validations = models;
|
||||
let pointRecord = ref(null);
|
||||
let byRecord = ref(false);
|
||||
const logTree = ref([]);
|
||||
|
||||
const actionsText = {
|
||||
insert: 'Creates',
|
||||
update: 'Edits',
|
||||
delete: 'Deletes',
|
||||
select: 'Accesses',
|
||||
};
|
||||
const actionsClass = {
|
||||
insert: 'success',
|
||||
update: 'warning',
|
||||
delete: 'alert',
|
||||
select: 'notice',
|
||||
};
|
||||
const actionsIcon = {
|
||||
insert: 'add',
|
||||
update: 'edit',
|
||||
delete: 'remove',
|
||||
select: 'visibility',
|
||||
};
|
||||
const validDate = new RegExp(
|
||||
/^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])/.source +
|
||||
/T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$/.source
|
||||
);
|
||||
|
||||
function castJsonValue(value) {
|
||||
return typeof value === 'string' && validDate.test(value) ? new Date(value) : value;
|
||||
}
|
||||
|
@ -168,6 +144,9 @@ function parseProps(propNames, locale, vals, olds) {
|
|||
}
|
||||
|
||||
function getLogTree(data) {
|
||||
console.log('data: ', data);
|
||||
if (!data) return;
|
||||
|
||||
const logs = [];
|
||||
let originLog = null;
|
||||
let userLog = null;
|
||||
|
@ -232,7 +211,7 @@ function getLogTree(data) {
|
|||
|
||||
async function openPointRecord(id, modelLog) {
|
||||
pointRecord.value = null;
|
||||
const { data } = await axios.get(`${props.model}Logs/${id}/pitInstance`);
|
||||
const { data } = await axios.get(`${modelName.value}Logs/${id}/pitInstance`);
|
||||
const propNames = Object.keys(data);
|
||||
const locale = validations[modelLog.model]?.locale || {};
|
||||
pointRecord.value = parseProps(propNames, locale, data);
|
||||
|
@ -248,167 +227,25 @@ function filterByRecord(modelLog) {
|
|||
searchInput.value = id;
|
||||
selectedFilters.value.changedModelId = id;
|
||||
selectedFilters.value.changedModel = model;
|
||||
applyFilter();
|
||||
// applyFilter();
|
||||
}
|
||||
|
||||
async function applyFilter() {
|
||||
filter.where = { and: [] };
|
||||
if (
|
||||
!selectedFilters.value.changedModel ||
|
||||
(!selectedFilters.value.changedModelValue &&
|
||||
!selectedFilters.value.changedModelId)
|
||||
)
|
||||
byRecord.value = false;
|
||||
|
||||
if (!byRecord.value) filter.where.and.push({ originFk: route.params.id });
|
||||
|
||||
if (Object.keys(selectedFilters.value).length) {
|
||||
filter.where.and.push(selectedFilters.value);
|
||||
}
|
||||
|
||||
paginate.value.fetch(filter);
|
||||
}
|
||||
|
||||
function setDate(type) {
|
||||
let from = dateFrom.value
|
||||
? date.formatDate(dateFrom.value.split('-').reverse().join('-'), 'YYYY-MM-DD')
|
||||
: undefined;
|
||||
from = date.adjustDate(from, { hour: 0, minute: 0, second: 0, millisecond: 0 }, true);
|
||||
|
||||
let to = dateTo.value
|
||||
? date.formatDate(dateTo.value.split('-').reverse().join('-'), 'YYYY-MM-DD')
|
||||
: date.formatDate(dateFrom.value.split('-').reverse().join('-'), 'YYYY-MM-DD');
|
||||
to = date.adjustDate(
|
||||
to,
|
||||
{ hour: 21, minute: 59, second: 59, millisecond: 999 },
|
||||
true
|
||||
);
|
||||
|
||||
switch (type) {
|
||||
case 'from':
|
||||
return { between: [from, to] };
|
||||
case 'to': {
|
||||
if (dateFrom.value) {
|
||||
return {
|
||||
between: [from, to],
|
||||
};
|
||||
}
|
||||
return { lte: to };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function selectFilter(type, dateType) {
|
||||
const filter = {};
|
||||
const actions = { inq: [] };
|
||||
let reload = true;
|
||||
|
||||
if (type === 'search') {
|
||||
if (/^\s*[0-9]+\s*$/.test(searchInput.value) || props.byRecord) {
|
||||
selectedFilters.value.changedModelId = searchInput.value.trim();
|
||||
} else if (!searchInput.value) {
|
||||
selectedFilters.value.changedModelId = undefined;
|
||||
selectedFilters.value.changedModelValue = undefined;
|
||||
} else {
|
||||
selectedFilters.value.changedModelValue = { like: `%${searchInput.value}%` };
|
||||
}
|
||||
}
|
||||
if (type === 'action' && selectedFilters.value.changedModel === null) {
|
||||
selectedFilters.value.changedModel = undefined;
|
||||
}
|
||||
if (type === 'userRadio') {
|
||||
selectedFilters.value.userFk = userRadio.value;
|
||||
}
|
||||
if (type === 'change') {
|
||||
if (changeInput.value)
|
||||
selectedFilters.value.or = [
|
||||
{ oldJson: { like: `%${changeInput.value}%` } },
|
||||
{ newJson: { like: `%${changeInput.value}%` } },
|
||||
{ description: { like: `%${changeInput.value}%` } },
|
||||
];
|
||||
else selectedFilters.value.or = undefined;
|
||||
}
|
||||
if (type === 'userSelect') {
|
||||
selectedFilters.value.userFk =
|
||||
userSelect.value !== null ? userSelect.value : undefined;
|
||||
}
|
||||
if (type === 'date') {
|
||||
if (!dateFrom.value && !dateTo.value) {
|
||||
selectedFilters.value.creationDate = undefined;
|
||||
} else if (dateType === 'to') {
|
||||
selectedFilters.value.creationDate = setDate('to');
|
||||
} else if (dateType === 'from') {
|
||||
selectedFilters.value.creationDate = setDate('from');
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(checkboxOptions.value).forEach((key) => {
|
||||
if (checkboxOptions.value[key].selected) actions.inq.push(key);
|
||||
});
|
||||
selectedFilters.value.action = actions.inq.length ? actions : undefined;
|
||||
|
||||
Object.keys(selectedFilters.value).forEach((key) => {
|
||||
if (selectedFilters.value[key]) filter[key] = selectedFilters.value[key];
|
||||
});
|
||||
|
||||
if (reload) applyFilter(filter);
|
||||
}
|
||||
|
||||
async function clearFilter() {
|
||||
selectedFilters.value = {};
|
||||
byRecord.value = false;
|
||||
userSelect.value = undefined;
|
||||
searchInput.value = undefined;
|
||||
changeInput.value = undefined;
|
||||
dateFrom.value = undefined;
|
||||
dateTo.value = undefined;
|
||||
userRadio.value = undefined;
|
||||
Object.keys(checkboxOptions.value).forEach(
|
||||
(opt) => (checkboxOptions.value[opt].selected = false)
|
||||
);
|
||||
await applyFilter();
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
stateStore.rightDrawer = false;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => router.currentRoute.value.params.id,
|
||||
() => {
|
||||
applyFilter();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<FetchData
|
||||
:url="`${props.model}Logs/${route.params.id}/models`"
|
||||
:filter="{ order: ['changedModel'] }"
|
||||
@on-fetch="
|
||||
(data) =>
|
||||
(actions = data.map((item) => {
|
||||
const changedModel = item.changedModel;
|
||||
return {
|
||||
locale: useCapitalize(
|
||||
validations[changedModel]?.locale?.name ?? changedModel
|
||||
),
|
||||
value: changedModel,
|
||||
};
|
||||
}))
|
||||
"
|
||||
auto-load
|
||||
/>
|
||||
<VnPaginate
|
||||
<VnTable
|
||||
v-if="modelName"
|
||||
ref="paginate"
|
||||
:data-key="`${model}Log`"
|
||||
:url="`${model}Logs`"
|
||||
:filter="filter"
|
||||
data-key="vnLog"
|
||||
:url="`${modelName}Logs`"
|
||||
:skeleton="false"
|
||||
auto-load
|
||||
:user-filter="filter"
|
||||
:auto-load="!!modelName || route.query.table"
|
||||
@on-fetch="setLogTree"
|
||||
search-url="logs"
|
||||
:columns="[]"
|
||||
:right-search="false"
|
||||
search-url="table"
|
||||
>
|
||||
<template #body>
|
||||
{{ paginate.params }}
|
||||
<div
|
||||
class="column items-center logs origin-log q-mt-md"
|
||||
v-for="(originLog, originLogIndex) in logTree"
|
||||
|
@ -416,7 +253,7 @@ watch(
|
|||
>
|
||||
<QItem class="origin-info items-center q-my-md" v-if="logTree.length > 1">
|
||||
<h6 class="origin-id text-grey">
|
||||
{{ useCapitalize(validations[props.model].locale.name) }}
|
||||
{{ useCapitalize(validations[modelName].locale.name) }}
|
||||
#{{ originLog.originFk }}
|
||||
</h6>
|
||||
<div class="line bg-grey"></div>
|
||||
|
@ -675,181 +512,10 @@ watch(
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</VnPaginate>
|
||||
</VnTable>
|
||||
<Teleport to="#right-panel" v-if="stateStore.isHeaderMounted()">
|
||||
<QList dense>
|
||||
<QSeparator />
|
||||
<QItem class="q-mt-sm">
|
||||
<QInput
|
||||
:label="t('globals.search')"
|
||||
v-model="searchInput"
|
||||
class="full-width"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
@keyup.enter="() => selectFilter('search')"
|
||||
@focusout="() => selectFilter('search')"
|
||||
@clear="() => selectFilter('search')"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="info" class="cursor-pointer">
|
||||
<QTooltip>{{ t('tooltips.search') }}</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
</QItem>
|
||||
<QItem>
|
||||
<VnSelect
|
||||
class="full-width"
|
||||
:label="t('globals.entity')"
|
||||
v-model="selectedFilters.changedModel"
|
||||
option-label="locale"
|
||||
option-value="value"
|
||||
:options="actions"
|
||||
@update:model-value="selectFilter('action')"
|
||||
hide-selected
|
||||
/>
|
||||
</QItem>
|
||||
<QItem class="q-mt-sm">
|
||||
<QOptionGroup
|
||||
size="sm"
|
||||
v-model="userRadio"
|
||||
:options="userTypes"
|
||||
color="primary"
|
||||
@update:model-value="selectFilter('userRadio')"
|
||||
right-label
|
||||
>
|
||||
<template #label="{ label }">
|
||||
{{ t(`Users.${label}`) }}
|
||||
</template>
|
||||
</QOptionGroup>
|
||||
</QItem>
|
||||
<QItem class="q-mt-sm">
|
||||
<QItemSection v-if="userRadio !== null">
|
||||
<VnSelect
|
||||
class="full-width"
|
||||
:label="t('globals.user')"
|
||||
v-model="userSelect"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
:url="`${model}Logs/${$route.params.id}/editors`"
|
||||
:fields="['id', 'nickname', 'name', 'image']"
|
||||
sort-by="nickname"
|
||||
@update:model-value="selectFilter('userSelect')"
|
||||
hide-selected
|
||||
>
|
||||
<template #option="{ opt, itemProps }">
|
||||
<QItem v-bind="itemProps" class="q-pa-xs row items-center">
|
||||
<QItemSection class="col-3 items-center">
|
||||
<VnAvatar :worker-id="opt.id" />
|
||||
</QItemSection>
|
||||
<QItemSection class="col-9 justify-center">
|
||||
<span>{{ opt.name }}</span>
|
||||
<span class="text-grey">{{ opt.nickname }}</span>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
<QItem class="q-mt-sm">
|
||||
<QInput
|
||||
:label="t('globals.changes')"
|
||||
v-model="changeInput"
|
||||
class="full-width"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
@keyup.enter="selectFilter('change')"
|
||||
@focusout="selectFilter('change')"
|
||||
@clear="selectFilter('change')"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="info" class="cursor-pointer">
|
||||
<QTooltip max-width="250px">{{
|
||||
t('tooltips.changes')
|
||||
}}</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
</QItem>
|
||||
<QItem
|
||||
:class="index == 'create' ? 'q-mt-md' : 'q-mt-xs'"
|
||||
v-for="(checkboxOption, index) in checkboxOptions"
|
||||
:key="index"
|
||||
>
|
||||
<QCheckbox
|
||||
size="sm"
|
||||
v-model="checkboxOption.selected"
|
||||
:label="t(`actions.${checkboxOption.label}`)"
|
||||
@update:model-value="selectFilter"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem class="q-mt-sm">
|
||||
<QInput
|
||||
class="full-width"
|
||||
:label="t('globals.date')"
|
||||
@click="dateFromDialog = true"
|
||||
@focus="(evt) => evt.target.blur()"
|
||||
@clear="selectFilter('date', 'to')"
|
||||
v-model="dateFrom"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
/>
|
||||
</QItem>
|
||||
<QItem class="q-mt-sm">
|
||||
<QInput
|
||||
class="full-width"
|
||||
:label="t('to')"
|
||||
@click="dateToDialog = true"
|
||||
@focus="(evt) => evt.target.blur()"
|
||||
@clear="selectFilter('date', 'from')"
|
||||
v-model="dateTo"
|
||||
clearable
|
||||
clear-icon="close"
|
||||
/>
|
||||
</QItem>
|
||||
</QList>
|
||||
<VnLogFilter v-model="params" data-key="vnLog" :disable-model="disableModel" />
|
||||
</Teleport>
|
||||
<QDialog v-model="dateFromDialog">
|
||||
<QDate
|
||||
:years-in-month-view="false"
|
||||
v-model="dateFrom"
|
||||
dense
|
||||
flat
|
||||
minimal
|
||||
@update:model-value="
|
||||
(value) => {
|
||||
dateFromDialog = false;
|
||||
dateFrom = date.formatDate(value, 'DD-MM-YYYY');
|
||||
selectFilter('date', 'from');
|
||||
}
|
||||
"
|
||||
/>
|
||||
</QDialog>
|
||||
<QDialog v-model="dateToDialog">
|
||||
<QDate
|
||||
v-model="dateTo"
|
||||
dense
|
||||
flat
|
||||
minimal
|
||||
@update:model-value="
|
||||
(value) => {
|
||||
dateToDialog = false;
|
||||
dateTo = date.formatDate(value, 'DD-MM-YYYY');
|
||||
selectFilter('date', 'to');
|
||||
}
|
||||
"
|
||||
/>
|
||||
</QDialog>
|
||||
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||
<QBtn
|
||||
v-if="Object.values(selectedFilters).some((filter) => filter !== undefined)"
|
||||
color="primary"
|
||||
icon="filter_alt_off"
|
||||
size="md"
|
||||
round
|
||||
@click="clearFilter"
|
||||
/>
|
||||
</QPageSticky>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.q-card {
|
||||
|
@ -1038,77 +704,3 @@ watch(
|
|||
}
|
||||
}
|
||||
</style>
|
||||
<i18n>
|
||||
en:
|
||||
to: To
|
||||
pointRecord: View record at this point in time
|
||||
recordChanges: show all record changes
|
||||
tooltips:
|
||||
search: Search by id or concept
|
||||
changes: Search by changes
|
||||
actions:
|
||||
Creates: Creates
|
||||
Edits: Edits
|
||||
Deletes: Deletes
|
||||
Accesses: Accesses
|
||||
Users:
|
||||
User: Usuario
|
||||
All: Todo
|
||||
System: Sistema
|
||||
properties:
|
||||
id: ID
|
||||
claimFk: Claim ID
|
||||
saleFk: Sale ID
|
||||
quantity: Quantity
|
||||
observation: Observation
|
||||
ticketCreated: Created
|
||||
created: Created
|
||||
isChargedToMana: Charged to mana
|
||||
pickup: Type of pickup
|
||||
dmsFk: Document ID
|
||||
text: Description
|
||||
claimStateFk: Claim State
|
||||
workerFk: Worker
|
||||
clientFk: Customer
|
||||
responsibility: Responsibility
|
||||
packages: Packages
|
||||
es:
|
||||
to: Hasta
|
||||
pointRecord: Ver el registro en este punto
|
||||
recordChanges: Mostrar todos los cambios realizados en el registro
|
||||
tooltips:
|
||||
search: Buscar por identificador o concepto
|
||||
changes: Buscar por cambios. Los atributos deben buscarse por su nombre interno, para obtenerlo situar el cursor sobre el atributo.
|
||||
Audit logs: Historial
|
||||
Property: Propiedad
|
||||
Before: Antes
|
||||
After: Después
|
||||
Yes: Si
|
||||
Nothing: Nada
|
||||
actions:
|
||||
Creates: Crea
|
||||
Edits: Modifica
|
||||
Deletes: Elimina
|
||||
Accesses: Accede
|
||||
Users:
|
||||
User: Usuario
|
||||
All: Todo
|
||||
System: Sistema
|
||||
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á
|
||||
pickup: Se debe recoger
|
||||
dmsFk: ID documento
|
||||
text: Descripción
|
||||
claimStateFk: Estado de la reclamación
|
||||
workerFk: Trabajador
|
||||
clientFk: Cliente
|
||||
responsibility: Responsabilidad
|
||||
packages: Bultos
|
||||
</i18n>
|
||||
|
|
|
@ -1,64 +1,220 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import FetchData from 'components/FetchData.vue';
|
||||
import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
|
||||
import VnInput from './VnInput.vue';
|
||||
import VnSelect from './VnSelect.vue';
|
||||
import { useValidator } from 'src/composables/useValidator';
|
||||
import VnAvatar from '../ui/VnAvatar.vue';
|
||||
import FetchData from '../FetchData.vue';
|
||||
import VnInputDate from './VnInputDate.vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
const validationsStore = useValidator();
|
||||
const { models } = validationsStore;
|
||||
|
||||
const modelParams = defineModel({ type: Object });
|
||||
const props = defineProps({
|
||||
dataKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
disableModel: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const workers = ref();
|
||||
const userTypes = [
|
||||
{ label: 'All', value: undefined },
|
||||
{ label: 'User', value: { neq: null } },
|
||||
{ label: 'System', value: null },
|
||||
];
|
||||
const actions = ref();
|
||||
const checkboxOptions = ref({
|
||||
insert: {
|
||||
label: 'Creates',
|
||||
selected: false,
|
||||
},
|
||||
update: {
|
||||
label: 'Edits',
|
||||
selected: false,
|
||||
},
|
||||
delete: {
|
||||
label: 'Deletes',
|
||||
selected: false,
|
||||
},
|
||||
select: {
|
||||
label: 'Accesses',
|
||||
selected: false,
|
||||
},
|
||||
});
|
||||
|
||||
const exprBuilder = (param, value) => {
|
||||
if (param == 'originFk') return { originFk: value };
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FetchData
|
||||
url="Workers/activeWithInheritedRole"
|
||||
:filter="{ where: { role: 'salesPerson' } }"
|
||||
@on-fetch="(data) => (workers = data)"
|
||||
v-if="modelName && id"
|
||||
:url="`${modelName}Logs/${id}/models`"
|
||||
:filter="{ order: ['changedModel'] }"
|
||||
@on-fetch="
|
||||
(data) =>
|
||||
(actions = data.map((item) => {
|
||||
const changedModel = item.changedModel;
|
||||
return {
|
||||
locale: useCapitalize(
|
||||
validations[changedModel]?.locale?.name ?? changedModel
|
||||
),
|
||||
value: changedModel,
|
||||
};
|
||||
}))
|
||||
"
|
||||
auto-load
|
||||
/>
|
||||
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
|
||||
{{ modelParams }}
|
||||
<VnFilterPanel
|
||||
v-model="modelParams"
|
||||
:data-key="props.dataKey"
|
||||
:search-button="true"
|
||||
:hidden-tags="disableModel && ['modelName', 'id']"
|
||||
search-url="table"
|
||||
:redirect="false"
|
||||
:expr-builder="exprBuilder"
|
||||
>
|
||||
<template #tags="{ tag, formatFn }">
|
||||
<div class="q-gutter-x-xs">
|
||||
<strong>{{ t(`params.${tag.label}`) }}: </strong>
|
||||
<span>{{ formatFn(tag.value) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #body="{ params, searchFn }">
|
||||
<QDate
|
||||
v-model="params.created"
|
||||
@update:model-value="searchFn()"
|
||||
<template #body="{ params }">
|
||||
<div class="q-gutter-y-sm q-px-sm">
|
||||
<QCard class="q-pa-xs q-mb-lg q-gutter-y-xs" flat bordered>
|
||||
<VnSelect
|
||||
:label="$t('globals.table')"
|
||||
:options="
|
||||
Object.keys(models)
|
||||
?.filter((key) => key.endsWith('Log'))
|
||||
?.map((key) => key.slice(0, -3))
|
||||
"
|
||||
v-model="params.model"
|
||||
:disable="props.disableModel"
|
||||
filled
|
||||
dense
|
||||
flat
|
||||
minimal
|
||||
>
|
||||
</QDate>
|
||||
<QSeparator />
|
||||
<QItem>
|
||||
<QItemSection v-if="!workers">
|
||||
<QSkeleton type="QInput" class="full-width" />
|
||||
</QItemSection>
|
||||
<QItemSection v-if="workers">
|
||||
<QSelect
|
||||
:label="t('User')"
|
||||
v-model="params.userFk"
|
||||
@update:model-value="searchFn()"
|
||||
:options="workers"
|
||||
option-value="id"
|
||||
option-label="name"
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
:input-debounce="0"
|
||||
/>
|
||||
<VnInput
|
||||
:label="$t('globals.id')"
|
||||
v-model="params.originFk"
|
||||
:disable="props.disableModel"
|
||||
filled
|
||||
dense
|
||||
/>
|
||||
</QCard>
|
||||
<VnInput
|
||||
:label="t('globals.search')"
|
||||
v-model="params.searchInput"
|
||||
filled
|
||||
dense
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="info" class="cursor-pointer">
|
||||
<QTooltip>{{ t('tooltips.search') }}</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</VnInput>
|
||||
<VnSelect
|
||||
:label="t('globals.entity')"
|
||||
v-model="params.changedModel"
|
||||
option-label="locale"
|
||||
option-value="value"
|
||||
:options="actions"
|
||||
hide-selected
|
||||
filled
|
||||
dense
|
||||
/>
|
||||
<QOptionGroup
|
||||
size="sm"
|
||||
v-model="params.userRadio"
|
||||
:options="userTypes"
|
||||
color="primary"
|
||||
right-label
|
||||
filled
|
||||
dense
|
||||
>
|
||||
<template #label="{ label }">
|
||||
{{ t(`Users.${label}`) }}
|
||||
</template>
|
||||
</QOptionGroup>
|
||||
<QItemSection v-if="params.userRadio !== null">
|
||||
<VnSelect
|
||||
:label="t('globals.user')"
|
||||
v-model="params.userSelect"
|
||||
option-label="name"
|
||||
option-value="id"
|
||||
:url="`${params.modelName}Logs/${params.id}/editors`"
|
||||
:fields="['id', 'nickname', 'name', 'image']"
|
||||
sort-by="nickname"
|
||||
hide-selected
|
||||
filled
|
||||
dense
|
||||
>
|
||||
<template #option="{ opt, itemProps }">
|
||||
<QItem v-bind="itemProps" class="q-pa-xs row items-center">
|
||||
<QItemSection class="col-3 items-center">
|
||||
<VnAvatar :worker-id="opt.id" />
|
||||
</QItemSection>
|
||||
<QItemSection class="col-9 justify-center">
|
||||
<span>{{ opt.name }}</span>
|
||||
<span class="text-grey">{{ opt.nickname }}</span>
|
||||
</QItemSection>
|
||||
</QItem>
|
||||
</template>
|
||||
</VnSelect>
|
||||
</QItemSection>
|
||||
<VnInput
|
||||
:label="t('globals.changes')"
|
||||
v-model="params.changeInput"
|
||||
filled
|
||||
dense
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="info" class="cursor-pointer">
|
||||
<QTooltip max-width="250px">{{
|
||||
t('tooltips.changes')
|
||||
}}</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</VnInput>
|
||||
<div v-for="(checkboxOption, index) in checkboxOptions" :key="index">
|
||||
<QCheckbox
|
||||
size="sm"
|
||||
v-model="params.checkboxOption"
|
||||
:label="t(`actions.${checkboxOption.label}`)"
|
||||
filled
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
<VnInputDate
|
||||
:label="t('globals.date')"
|
||||
@click="dateFromDialog = true"
|
||||
@focus="(evt) => evt.target.blur()"
|
||||
v-model="params.dateFrom"
|
||||
filled
|
||||
dense
|
||||
/>
|
||||
<VnInputDate
|
||||
:label="t('to')"
|
||||
@click="dateToDialog = true"
|
||||
@focus="(evt) => evt.target.blur()"
|
||||
v-model="params.dateTo"
|
||||
filled
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</VnFilterPanel>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ const pagination = ref({
|
|||
page: 1,
|
||||
});
|
||||
|
||||
const arrayData = useArrayData(props.dataKey, {
|
||||
let arrayData = useArrayData(props.dataKey, {
|
||||
url: props.url,
|
||||
filter: props.filter,
|
||||
userFilter: props.userFilter,
|
||||
|
@ -97,7 +97,7 @@ const arrayData = useArrayData(props.dataKey, {
|
|||
keepOpts: props.keepOpts,
|
||||
searchUrl: props.searchUrl,
|
||||
});
|
||||
const store = arrayData.store;
|
||||
let store = arrayData.store;
|
||||
|
||||
onMounted(async () => {
|
||||
if (props.autoLoad) await fetch();
|
||||
|
@ -120,14 +120,17 @@ watch(
|
|||
|
||||
watch(
|
||||
() => [props.url, props.filter],
|
||||
([url, filter]) => mounted.value && fetch({ url, filter })
|
||||
async ([url, filter]) => {
|
||||
mounted.value && (await fetch({ url, filter }));
|
||||
}
|
||||
);
|
||||
const addFilter = async (filter, params) => {
|
||||
await arrayData.addFilter({ filter, params });
|
||||
};
|
||||
|
||||
async function fetch(params) {
|
||||
useArrayData(props.dataKey, params);
|
||||
arrayData = useArrayData(props.dataKey, params);
|
||||
store = arrayData.store;
|
||||
arrayData.reset(['filter.skip', 'skip']);
|
||||
await arrayData.fetch({ append: false });
|
||||
if (!store.hasMoreData) isLoading.value = false;
|
||||
|
|
|
@ -106,6 +106,7 @@ globals:
|
|||
weight: Weight
|
||||
error: Ups! Something went wrong
|
||||
recalc: Recalculate
|
||||
table: Table
|
||||
pageTitles:
|
||||
logIn: Login
|
||||
addressEdit: Update address
|
||||
|
@ -279,6 +280,8 @@ globals:
|
|||
wasteRecalc: Waste recaclulate
|
||||
operator: Operator
|
||||
parking: Parking
|
||||
logs: Logs
|
||||
logsList: Logs list
|
||||
supplier: Supplier
|
||||
created: Created
|
||||
worker: Worker
|
||||
|
|
|
@ -108,6 +108,7 @@ globals:
|
|||
weight: Peso
|
||||
error: ¡Ups! Algo salió mal
|
||||
recalc: Recalcular
|
||||
table: Tabla
|
||||
pageTitles:
|
||||
logIn: Inicio de sesión
|
||||
addressEdit: Modificar consignatario
|
||||
|
@ -283,6 +284,8 @@ globals:
|
|||
wasteRecalc: Recalcular mermas
|
||||
operator: Operario
|
||||
parking: Parking
|
||||
logs: Historiales
|
||||
logsList: Listado de historiales
|
||||
supplier: Proveedor
|
||||
created: Fecha creación
|
||||
worker: Trabajador
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<script setup>
|
||||
import RightMenu from 'src/components/common/RightMenu.vue';
|
||||
import VnLog from 'src/components/common/VnLog.vue';
|
||||
</script>
|
||||
<template>
|
||||
<RightMenu />
|
||||
<VnLog :disable-model="false" />
|
||||
</template>
|
|
@ -0,0 +1,32 @@
|
|||
to: To
|
||||
pointRecord: View record at this point in time
|
||||
recordChanges: show all record changes
|
||||
tooltips:
|
||||
search: Search by id or concept
|
||||
changes: Search by changes
|
||||
actions:
|
||||
Creates: Creates
|
||||
Edits: Edits
|
||||
Deletes: Deletes
|
||||
Accesses: Accesses
|
||||
Users:
|
||||
User: Usuario
|
||||
All: Todo
|
||||
System: Sistema
|
||||
properties:
|
||||
id: ID
|
||||
claimFk: Claim ID
|
||||
saleFk: Sale ID
|
||||
quantity: Quantity
|
||||
observation: Observation
|
||||
ticketCreated: Created
|
||||
created: Created
|
||||
isChargedToMana: Charged to mana
|
||||
pickup: Type of pickup
|
||||
dmsFk: Document ID
|
||||
text: Description
|
||||
claimStateFk: Claim State
|
||||
workerFk: Worker
|
||||
clientFk: Customer
|
||||
responsibility: Responsibility
|
||||
packages: Packages
|
|
@ -0,0 +1,38 @@
|
|||
to: Hasta
|
||||
pointRecord: Ver el registro en este punto
|
||||
recordChanges: Mostrar todos los cambios realizados en el registro
|
||||
tooltips:
|
||||
search: Buscar por identificador o concepto
|
||||
changes: Buscar por cambios. Los atributos deben buscarse por su nombre interno, para obtenerlo situar el cursor sobre el atributo.
|
||||
Audit logs: Historial
|
||||
Property: Propiedad
|
||||
Before: Antes
|
||||
After: Después
|
||||
Yes: Si
|
||||
Nothing: Nada
|
||||
actions:
|
||||
Creates: Crea
|
||||
Edits: Modifica
|
||||
Deletes: Elimina
|
||||
Accesses: Accede
|
||||
Users:
|
||||
User: Usuario
|
||||
All: Todo
|
||||
System: Sistema
|
||||
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á
|
||||
pickup: Se debe recoger
|
||||
dmsFk: ID documento
|
||||
text: Descripción
|
||||
claimStateFk: Estado de la reclamación
|
||||
workerFk: Trabajador
|
||||
clientFk: Cliente
|
||||
responsibility: Responsabilidad
|
||||
packages: Bultos
|
|
@ -22,6 +22,7 @@ import Account from './account';
|
|||
import Monitor from './monitor';
|
||||
import MailAlias from './mailAlias';
|
||||
import Role from './role';
|
||||
import Logs from './logs';
|
||||
|
||||
export default [
|
||||
Item,
|
||||
|
@ -48,4 +49,5 @@ export default [
|
|||
MailAlias,
|
||||
Monitor,
|
||||
Role,
|
||||
Logs,
|
||||
];
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { RouterView } from 'vue-router';
|
||||
|
||||
export default {
|
||||
path: '/logs',
|
||||
name: 'Logs',
|
||||
meta: {
|
||||
title: 'logs',
|
||||
icon: 'vn:History',
|
||||
moduleName: 'Logs',
|
||||
},
|
||||
component: RouterView,
|
||||
redirect: { name: 'LogsMain' },
|
||||
menus: {
|
||||
main: ['LogsList'],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/logs',
|
||||
name: 'LogsMain',
|
||||
component: () => import('src/components/common/VnSectionMain.vue'),
|
||||
redirect: { name: 'LogsList' },
|
||||
children: [
|
||||
{
|
||||
path: 'list',
|
||||
name: 'LogsList',
|
||||
meta: {
|
||||
title: 'logsList',
|
||||
icon: 'vn:History',
|
||||
},
|
||||
component: () => import('src/pages/Logs/LogsList.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
|
@ -22,6 +22,7 @@ import zone from 'src/router/modules/zone';
|
|||
import account from './modules/account';
|
||||
import monitor from 'src/router/modules/monitor';
|
||||
import mailAlias from './modules/mailAlias';
|
||||
import logs from './modules/logs';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
|
@ -97,6 +98,7 @@ const routes = [
|
|||
account,
|
||||
role,
|
||||
mailAlias,
|
||||
logs,
|
||||
{
|
||||
path: '/:catchAll(.*)*',
|
||||
name: 'NotFound',
|
||||
|
|
|
@ -24,6 +24,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
|
|||
'account',
|
||||
'wagon',
|
||||
'zone',
|
||||
'logs',
|
||||
];
|
||||
const pinnedModules = ref([]);
|
||||
const acl = useAcl();
|
||||
|
|
Loading…
Reference in New Issue