WIP: feat: refs #6726 add new section "logs" #903

Draft
alexm wants to merge 1 commits from 6726-LogSection into dev
12 changed files with 382 additions and 507 deletions
Showing only changes of commit 7487a2706c - Show all commits

View File

@ -1,7 +1,7 @@
<script setup> <script setup>
import { ref, onUnmounted, watch } from 'vue'; import { ref, onUnmounted, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router'; import { useRoute } from 'vue-router';
import axios from 'axios'; import axios from 'axios';
import { date } from 'quasar'; import { date } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
@ -11,16 +11,14 @@ import { useCapitalize } from 'src/composables/useCapitalize';
import { useValidator } from 'src/composables/useValidator'; import { useValidator } from 'src/composables/useValidator';
import VnAvatar from '../ui/VnAvatar.vue'; import VnAvatar from '../ui/VnAvatar.vue';
import VnJsonValue from '../common/VnJsonValue.vue'; import VnJsonValue from '../common/VnJsonValue.vue';
import FetchData from '../FetchData.vue';
import VnSelect from './VnSelect.vue';
import VnUserLink from '../ui/VnUserLink.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 stateStore = useStateStore();
const validationsStore = useValidator(); const validationsStore = useValidator();
const { models } = validationsStore; const { models } = validationsStore;
const route = useRoute(); const route = useRoute();
const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps({ const props = defineProps({
model: { model: {
@ -35,8 +33,45 @@ const props = defineProps({
type: Function, type: Function,
default: null, 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 = { const filter = {
fields: [ fields: [
'id', 'id',
@ -67,72 +102,13 @@ const filter = {
}, },
}, },
], ],
where: { and: [{ originFk: route.params.id }] }, orderBy: ['originFk DESC', 'creationDate DESC'],
}; };
const paginate = ref(); onUnmounted(() => {
const actions = ref(); stateStore.rightDrawer = false;
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,
},
}); });
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) { function castJsonValue(value) {
return typeof value === 'string' && validDate.test(value) ? new Date(value) : 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) { function getLogTree(data) {
console.log('data: ', data);
if (!data) return;
const logs = []; const logs = [];
let originLog = null; let originLog = null;
let userLog = null; let userLog = null;
@ -232,7 +211,7 @@ function getLogTree(data) {
async function openPointRecord(id, modelLog) { async function openPointRecord(id, modelLog) {
pointRecord.value = null; 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 propNames = Object.keys(data);
const locale = validations[modelLog.model]?.locale || {}; const locale = validations[modelLog.model]?.locale || {};
pointRecord.value = parseProps(propNames, locale, data); pointRecord.value = parseProps(propNames, locale, data);
@ -248,167 +227,25 @@ function filterByRecord(modelLog) {
searchInput.value = id; searchInput.value = id;
selectedFilters.value.changedModelId = id; selectedFilters.value.changedModelId = id;
selectedFilters.value.changedModel = model; 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> </script>
<template> <template>
<FetchData <VnTable
:url="`${props.model}Logs/${route.params.id}/models`" v-if="modelName"
: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
ref="paginate" ref="paginate"
:data-key="`${model}Log`" data-key="vnLog"
:url="`${model}Logs`" :url="`${modelName}Logs`"
:filter="filter"
:skeleton="false" :skeleton="false"
auto-load :user-filter="filter"
:auto-load="!!modelName || route.query.table"
@on-fetch="setLogTree" @on-fetch="setLogTree"
search-url="logs" :columns="[]"
:right-search="false"
search-url="table"
> >
<template #body> <template #body>
{{ paginate.params }}
<div <div
class="column items-center logs origin-log q-mt-md" class="column items-center logs origin-log q-mt-md"
v-for="(originLog, originLogIndex) in logTree" 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"> <QItem class="origin-info items-center q-my-md" v-if="logTree.length > 1">
<h6 class="origin-id text-grey"> <h6 class="origin-id text-grey">
{{ useCapitalize(validations[props.model].locale.name) }} {{ useCapitalize(validations[modelName].locale.name) }}
#{{ originLog.originFk }} #{{ originLog.originFk }}
</h6> </h6>
<div class="line bg-grey"></div> <div class="line bg-grey"></div>
@ -675,181 +512,10 @@ watch(
</div> </div>
</div> </div>
</template> </template>
</VnPaginate> </VnTable>
<Teleport to="#right-panel" v-if="stateStore.isHeaderMounted()"> <Teleport to="#right-panel" v-if="stateStore.isHeaderMounted()">
<QList dense> <VnLogFilter v-model="params" data-key="vnLog" :disable-model="disableModel" />
<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>
</Teleport> </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> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.q-card { .q-card {
@ -1038,77 +704,3 @@ watch(
} }
} }
</style> </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>

View File

@ -1,64 +1,220 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnFilterPanel from 'src/components/ui/VnFilterPanel.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 { t } = useI18n();
const validationsStore = useValidator();
const { models } = validationsStore;
const modelParams = defineModel({ type: Object });
const props = defineProps({ const props = defineProps({
dataKey: { dataKey: {
type: String, type: String,
required: true, 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> </script>
<template> <template>
<FetchData <FetchData
url="Workers/activeWithInheritedRole" v-if="modelName && id"
:filter="{ where: { role: 'salesPerson' } }" :url="`${modelName}Logs/${id}/models`"
@on-fetch="(data) => (workers = data)" :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 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 }"> <template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs"> <div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong> <strong>{{ t(`params.${tag.label}`) }}: </strong>
<span>{{ formatFn(tag.value) }}</span> <span>{{ formatFn(tag.value) }}</span>
</div> </div>
</template> </template>
<template #body="{ params, searchFn }"> <template #body="{ params }">
<QDate <div class="q-gutter-y-sm q-px-sm">
v-model="params.created" <QCard class="q-pa-xs q-mb-lg q-gutter-y-xs" flat bordered>
@update:model-value="searchFn()" <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 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> </QItemSection>
</QItem> </QItem>
</template> </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> </VnFilterPanel>
</template> </template>

View File

@ -85,7 +85,7 @@ const pagination = ref({
page: 1, page: 1,
}); });
const arrayData = useArrayData(props.dataKey, { let arrayData = useArrayData(props.dataKey, {
url: props.url, url: props.url,
filter: props.filter, filter: props.filter,
userFilter: props.userFilter, userFilter: props.userFilter,
@ -97,7 +97,7 @@ const arrayData = useArrayData(props.dataKey, {
keepOpts: props.keepOpts, keepOpts: props.keepOpts,
searchUrl: props.searchUrl, searchUrl: props.searchUrl,
}); });
const store = arrayData.store; let store = arrayData.store;
onMounted(async () => { onMounted(async () => {
if (props.autoLoad) await fetch(); if (props.autoLoad) await fetch();
@ -120,14 +120,17 @@ watch(
watch( watch(
() => [props.url, props.filter], () => [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) => { const addFilter = async (filter, params) => {
await arrayData.addFilter({ filter, params }); await arrayData.addFilter({ filter, params });
}; };
async function fetch(params) { async function fetch(params) {
useArrayData(props.dataKey, params); arrayData = useArrayData(props.dataKey, params);
store = arrayData.store;
arrayData.reset(['filter.skip', 'skip']); arrayData.reset(['filter.skip', 'skip']);
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });
if (!store.hasMoreData) isLoading.value = false; if (!store.hasMoreData) isLoading.value = false;

View File

@ -106,6 +106,7 @@ globals:
weight: Weight weight: Weight
error: Ups! Something went wrong error: Ups! Something went wrong
recalc: Recalculate recalc: Recalculate
table: Table
pageTitles: pageTitles:
logIn: Login logIn: Login
addressEdit: Update address addressEdit: Update address
@ -279,6 +280,8 @@ globals:
wasteRecalc: Waste recaclulate wasteRecalc: Waste recaclulate
operator: Operator operator: Operator
parking: Parking parking: Parking
logs: Logs
logsList: Logs list
supplier: Supplier supplier: Supplier
created: Created created: Created
worker: Worker worker: Worker

View File

@ -108,6 +108,7 @@ globals:
weight: Peso weight: Peso
error: ¡Ups! Algo salió mal error: ¡Ups! Algo salió mal
recalc: Recalcular recalc: Recalcular
table: Tabla
pageTitles: pageTitles:
logIn: Inicio de sesión logIn: Inicio de sesión
addressEdit: Modificar consignatario addressEdit: Modificar consignatario
@ -283,6 +284,8 @@ globals:
wasteRecalc: Recalcular mermas wasteRecalc: Recalcular mermas
operator: Operario operator: Operario
parking: Parking parking: Parking
logs: Historiales
logsList: Listado de historiales
supplier: Proveedor supplier: Proveedor
created: Fecha creación created: Fecha creación
worker: Trabajador worker: Trabajador

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -22,6 +22,7 @@ import Account from './account';
import Monitor from './monitor'; import Monitor from './monitor';
import MailAlias from './mailAlias'; import MailAlias from './mailAlias';
import Role from './role'; import Role from './role';
import Logs from './logs';
export default [ export default [
Item, Item,
@ -48,4 +49,5 @@ export default [
MailAlias, MailAlias,
Monitor, Monitor,
Role, Role,
Logs,
]; ];

View File

@ -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'),
},
],
},
],
};

View File

@ -22,6 +22,7 @@ import zone from 'src/router/modules/zone';
import account from './modules/account'; import account from './modules/account';
import monitor from 'src/router/modules/monitor'; import monitor from 'src/router/modules/monitor';
import mailAlias from './modules/mailAlias'; import mailAlias from './modules/mailAlias';
import logs from './modules/logs';
const routes = [ const routes = [
{ {
@ -97,6 +98,7 @@ const routes = [
account, account,
role, role,
mailAlias, mailAlias,
logs,
{ {
path: '/:catchAll(.*)*', path: '/:catchAll(.*)*',
name: 'NotFound', name: 'NotFound',

View File

@ -24,6 +24,7 @@ export const useNavigationStore = defineStore('navigationStore', () => {
'account', 'account',
'wagon', 'wagon',
'zone', 'zone',
'logs',
]; ];
const pinnedModules = ref([]); const pinnedModules = ref([]);
const acl = useAcl(); const acl = useAcl();