feat: xadmin log, fix language switch (core)
parent
1416dee41e
commit
bf864b9b3c
|
|
@ -32,7 +32,7 @@
|
||||||
"chart.js": "^4.4.0",
|
"chart.js": "^4.4.0",
|
||||||
"cookie-es": "^1.0.0",
|
"cookie-es": "^1.0.0",
|
||||||
"date-fns": "^3.0.6",
|
"date-fns": "^3.0.6",
|
||||||
"export-from-json": "^1.7.3",
|
"export-from-json": "^1.7.4",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"mapbox-gl": "2.15.0",
|
"mapbox-gl": "2.15.0",
|
||||||
"ofetch": "^1.3.3",
|
"ofetch": "^1.3.3",
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,8 @@ dependencies:
|
||||||
specifier: ^3.0.6
|
specifier: ^3.0.6
|
||||||
version: 3.0.6
|
version: 3.0.6
|
||||||
export-from-json:
|
export-from-json:
|
||||||
specifier: ^1.7.3
|
specifier: ^1.7.4
|
||||||
version: 1.7.3
|
version: 1.7.4
|
||||||
jwt-decode:
|
jwt-decode:
|
||||||
specifier: ^3.1.2
|
specifier: ^3.1.2
|
||||||
version: 3.1.2
|
version: 3.1.2
|
||||||
|
|
@ -4119,8 +4119,8 @@ packages:
|
||||||
strip-final-newline: 3.0.0
|
strip-final-newline: 3.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/export-from-json@1.7.3:
|
/export-from-json@1.7.4:
|
||||||
resolution: {integrity: sha512-Xg0L0saYz+CBz2MnaZvSEAHr17hWtHAfFWXw/frllG9t6aijuQukiU40ElOeM9nDTrtQPhLJMLN0q8lo897FYg==}
|
resolution: {integrity: sha512-FjmpluvZS2PTYyhkoMfQoyEJMfe2bfAyNpa5Apa6C9n7SWUWyJkG/VFnzERuj3q9Jjo3iwBjwVsDQ7Z7sczthA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/external-editor@3.1.0:
|
/external-editor@3.1.0:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
|
import flatpickr from 'flatpickr'
|
||||||
|
import { French } from 'flatpickr/dist/l10n/fr'
|
||||||
import { useTheme } from 'vuetify'
|
import { useTheme } from 'vuetify'
|
||||||
import { useConfigStore } from '@core/stores/config'
|
|
||||||
import { cookieRef, namespaceConfig } from '@layouts/stores/config'
|
|
||||||
import { themeConfig } from '@themeConfig'
|
import { themeConfig } from '@themeConfig'
|
||||||
|
import { cookieRef, namespaceConfig } from '@layouts/stores/config'
|
||||||
|
import { useConfigStore } from '@core/stores/config'
|
||||||
|
|
||||||
const _syncAppRtl = () => {
|
const _syncAppRtl = () => {
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
|
|
@ -14,6 +16,15 @@ const _syncAppRtl = () => {
|
||||||
if (locale.value !== storedLang.value && storedLang.value)
|
if (locale.value !== storedLang.value && storedLang.value)
|
||||||
locale.value = storedLang.value
|
locale.value = storedLang.value
|
||||||
|
|
||||||
|
if (locale.value === 'fr') {
|
||||||
|
if (flatpickr.l10ns.fr)
|
||||||
|
flatpickr.localize(French)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (flatpickr.l10ns.en)
|
||||||
|
flatpickr.localize(flatpickr.l10ns.en)
|
||||||
|
}
|
||||||
|
|
||||||
// watch and change lang attribute of html on language change
|
// watch and change lang attribute of html on language change
|
||||||
watch(
|
watch(
|
||||||
locale,
|
locale,
|
||||||
|
|
@ -25,6 +36,15 @@ const _syncAppRtl = () => {
|
||||||
// Store selected language in cookie
|
// Store selected language in cookie
|
||||||
storedLang.value = val as string
|
storedLang.value = val as string
|
||||||
|
|
||||||
|
if (storedLang.value === 'fr') {
|
||||||
|
if (flatpickr.l10ns.fr)
|
||||||
|
flatpickr.localize(flatpickr.l10ns.fr)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (flatpickr.l10ns.en)
|
||||||
|
flatpickr.localize(flatpickr.l10ns.en)
|
||||||
|
}
|
||||||
|
|
||||||
// set isAppRtl value based on selected language
|
// set isAppRtl value based on selected language
|
||||||
if (themeConfig.app.i18n.langConfig && themeConfig.app.i18n.langConfig.length) {
|
if (themeConfig.app.i18n.langConfig && themeConfig.app.i18n.langConfig.length) {
|
||||||
themeConfig.app.i18n.langConfig.forEach(lang => {
|
themeConfig.app.i18n.langConfig.forEach(lang => {
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ export default [
|
||||||
icon: { icon: 'tabler-database-star' },
|
icon: { icon: 'tabler-database-star' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: 'Application Log',
|
title: 'Logs',
|
||||||
to: { name: 'xadmin-application-log' },
|
to: { name: 'xadmin-log' },
|
||||||
icon: { icon: 'tabler-bug' },
|
icon: { icon: 'tabler-bug' },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ export default [
|
||||||
icon: { icon: 'tabler-database-star' },
|
icon: { icon: 'tabler-database-star' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: 'Application Log',
|
title: 'Logs',
|
||||||
to: { name: 'xadmin-application-log' },
|
to: { name: 'xadmin-log' },
|
||||||
icon: { icon: 'tabler-bug' },
|
icon: { icon: 'tabler-bug' },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -125,9 +125,9 @@ const reloadStores = async () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- 👉 stores -->
|
<!-- 👉 filters -->
|
||||||
<VCard
|
<VCard
|
||||||
:title="$t('List of stores')"
|
:title="$t('Filters')"
|
||||||
class="mb-6"
|
class="mb-6"
|
||||||
>
|
>
|
||||||
<VCardText>
|
<VCardText>
|
||||||
|
|
@ -175,9 +175,12 @@ const reloadStores = async () => {
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
<VDivider class="my-4" />
|
<!-- 👉 datatable, search & export & reload -->
|
||||||
|
<VCard
|
||||||
|
:title="$t('List of stores')"
|
||||||
|
class="mb-6"
|
||||||
|
>
|
||||||
<div class="d-flex flex-wrap gap-4 mx-5">
|
<div class="d-flex flex-wrap gap-4 mx-5">
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
<!-- 👉 Search -->
|
<!-- 👉 Search -->
|
||||||
|
|
@ -252,7 +255,7 @@ const reloadStores = async () => {
|
||||||
</IconBtn>
|
</IconBtn>
|
||||||
</template>
|
</template>
|
||||||
</VDataTable>
|
</VDataTable>
|
||||||
|
<!-- 👉 Dialog page for line actions -->
|
||||||
<VDialog
|
<VDialog
|
||||||
v-model="isDialogVisible"
|
v-model="isDialogVisible"
|
||||||
max-width="600"
|
max-width="600"
|
||||||
|
|
@ -261,7 +264,7 @@ const reloadStores = async () => {
|
||||||
<DialogCloseBtn @click="isDialogVisible = !isDialogVisible" />
|
<DialogCloseBtn @click="isDialogVisible = !isDialogVisible" />
|
||||||
|
|
||||||
<!-- Dialog Content -->
|
<!-- Dialog Content -->
|
||||||
<VCard title="Connect to invidual POS">
|
<VCard :title="t('Connect to invidual POS')">
|
||||||
<VCardText>
|
<VCardText>
|
||||||
<div v-if="selectedStore && (selectedStore as Store).caisses">
|
<div v-if="selectedStore && (selectedStore as Store).caisses">
|
||||||
<div
|
<div
|
||||||
|
|
@ -289,12 +292,12 @@ const reloadStores = async () => {
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@click="isDialogVisible = false"
|
@click="isDialogVisible = false"
|
||||||
>
|
>
|
||||||
Close
|
{{ $t("Close") }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
</VCard>
|
</VCard>
|
||||||
</VDialog>
|
</VDialog>
|
||||||
</VCol>
|
</VCol>
|
||||||
</vcard>
|
</VCard>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,243 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { VDataTable } from 'vuetify/labs/VDataTable'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const headers = computed(() => [
|
|
||||||
{ title: '', key: 'data-table-expand' },
|
|
||||||
{ title: t('businessDate'), key: 'businessDate' },
|
|
||||||
{ title: t('createDate'), key: 'createDate' },
|
|
||||||
{ title: t('createUserId'), key: 'createUserId' },
|
|
||||||
{ title: t('rtlLocId'), key: 'rtlLocId' },
|
|
||||||
{ title: t('wkstnId'), key: 'wkstnId' },
|
|
||||||
{ title: t('logLevel'), key: 'logLevel' },
|
|
||||||
{ title: t('threadName'), key: 'threadName' },
|
|
||||||
{ title: '', key: 'actions', sortable: false },
|
|
||||||
])
|
|
||||||
|
|
||||||
const selectedCountry = ref()
|
|
||||||
const selectedBrand = ref()
|
|
||||||
const selectedNbPos = ref()
|
|
||||||
|
|
||||||
// for reload process
|
|
||||||
const storesList = ref<any>('')
|
|
||||||
const isLoading = ref(false)
|
|
||||||
|
|
||||||
const searchQuery = ref<any>('')
|
|
||||||
|
|
||||||
// Data table options
|
|
||||||
const { data: dtListData } = await useApi<any>(createUrl('/xadmin/application/log/5108?AWkstnId=20&BeginDate=20231221&EndDate=20231221'))
|
|
||||||
|
|
||||||
storesList.value = dtListData.value
|
|
||||||
|
|
||||||
const options = ref({ page: 1, itemsPerPage: 10, sortBy: [''], sortDesc: [false] })
|
|
||||||
|
|
||||||
const country = computed(() => {
|
|
||||||
const allItems = storesList.value.map((store: { pays: any }) => store.pays)
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
const uniqueItems = allItems.filter((country: any, index: any, self: string | any[]) => self.indexOf(country) === index)
|
|
||||||
const sortedItems = uniqueItems.sort()
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
return sortedItems.map((country: any) => ({ title: country, value: country }))
|
|
||||||
})
|
|
||||||
|
|
||||||
const brand = computed(() => {
|
|
||||||
const allItems = storesList.value.map((store: { enseigne: any }) => store.enseigne)
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
const uniqueItems = allItems.filter((brand: any, index: any, self: string | any[]) => self.indexOf(brand) === index)
|
|
||||||
const sortedItems = uniqueItems.sort()
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
return sortedItems.map((brand: any) => ({ title: brand, value: brand }))
|
|
||||||
})
|
|
||||||
|
|
||||||
const nbPos = computed(() => {
|
|
||||||
const allItems = storesList.value.map((store: { nbcaisses: number }) => store.nbcaisses)
|
|
||||||
const uniqueItems = Array.from(new Set(allItems)) as number[] // Utilisez une assertion de type
|
|
||||||
const sortedItems = uniqueItems.sort((a, b) => a - b) // Triez les nombres en ordre croissant
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
return sortedItems.map((nbPos: number) => ({ title: nbPos, value: nbPos }))
|
|
||||||
})
|
|
||||||
|
|
||||||
const filteredData = computed(() => {
|
|
||||||
let filtered = storesList.value
|
|
||||||
|
|
||||||
// If a brand is selected, filter the records for this brand
|
|
||||||
if (selectedBrand.value)
|
|
||||||
filtered = filtered.filter((store: { enseigne: any }) => store.enseigne === selectedBrand.value)
|
|
||||||
|
|
||||||
if (selectedCountry.value)
|
|
||||||
filtered = filtered.filter((store: { pays: any }) => store.pays === selectedCountry.value)
|
|
||||||
|
|
||||||
if (selectedNbPos.value)
|
|
||||||
filtered = filtered.filter((store: { nbcaisses: any }) => store.nbcaisses === selectedNbPos.value)
|
|
||||||
|
|
||||||
// If a search query is provided, filter the records for this query
|
|
||||||
if (searchQuery.value) {
|
|
||||||
filtered = filtered.filter((store: { [s: string]: unknown } | ArrayLike<unknown>) =>
|
|
||||||
Object.values(store).some(value =>
|
|
||||||
String(value).toLowerCase().includes(searchQuery.value.toLowerCase()),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return filtered
|
|
||||||
})
|
|
||||||
|
|
||||||
const selectedStore = ref(null)
|
|
||||||
const isDialogVisible = ref(false)
|
|
||||||
|
|
||||||
const openPosList = (store: any) => {
|
|
||||||
selectedStore.value = store
|
|
||||||
isDialogVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Store {
|
|
||||||
id_structure: number
|
|
||||||
caisses: Caisse[]
|
|
||||||
}
|
|
||||||
interface Caisse {
|
|
||||||
id_caisse: number
|
|
||||||
ip: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const reloadStores = async () => {
|
|
||||||
isLoading.value = true
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const { data, error } = await useApi<any>(createUrl('/stores/reload'))
|
|
||||||
|
|
||||||
if (!isEmpty(error.value))
|
|
||||||
console.error('Error loading store data:', error.value)
|
|
||||||
|
|
||||||
// Reload the store data
|
|
||||||
const { data: storeData, error: storeError } = await useApi<any>(createUrl('/stores'))
|
|
||||||
if (!isEmpty(storeError.value))
|
|
||||||
console.error('Error loading store data:', storeError.value)
|
|
||||||
else
|
|
||||||
storesList.value = storeData.value
|
|
||||||
|
|
||||||
isLoading.value = false
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<!-- 👉 stores -->
|
|
||||||
<VCard
|
|
||||||
:title="$t('List of stores')"
|
|
||||||
class="mb-6"
|
|
||||||
>
|
|
||||||
<VCardText>
|
|
||||||
<VRow>
|
|
||||||
<!-- 👉 Select country -->
|
|
||||||
<VCol
|
|
||||||
cols="12"
|
|
||||||
sm="4"
|
|
||||||
>
|
|
||||||
<AppSelect
|
|
||||||
v-model="selectedCountry"
|
|
||||||
:placeholder="$t('Country')"
|
|
||||||
:items="country"
|
|
||||||
clearable
|
|
||||||
clear-icon="tabler-x"
|
|
||||||
/>
|
|
||||||
</VCol>
|
|
||||||
|
|
||||||
<!-- 👉 Select Brand -->
|
|
||||||
<VCol
|
|
||||||
cols="12"
|
|
||||||
sm="4"
|
|
||||||
>
|
|
||||||
<AppSelect
|
|
||||||
v-model="selectedBrand"
|
|
||||||
:placeholder="$t('Brand')"
|
|
||||||
:items="brand"
|
|
||||||
clearable
|
|
||||||
clear-icon="tabler-x"
|
|
||||||
/>
|
|
||||||
</VCol>
|
|
||||||
|
|
||||||
<!-- 👉 Select Multi POS -->
|
|
||||||
<VCol
|
|
||||||
cols="12"
|
|
||||||
sm="4"
|
|
||||||
>
|
|
||||||
<AppSelect
|
|
||||||
v-model="selectedNbPos"
|
|
||||||
:placeholder="$t('Multi POS')"
|
|
||||||
:items="nbPos"
|
|
||||||
clearable
|
|
||||||
clear-icon="tabler-x"
|
|
||||||
/>
|
|
||||||
</VCol>
|
|
||||||
</VRow>
|
|
||||||
</VCardText>
|
|
||||||
|
|
||||||
<VDivider class="my-4" />
|
|
||||||
|
|
||||||
<div class="d-flex flex-wrap gap-4 mx-5">
|
|
||||||
<div class="d-flex align-center">
|
|
||||||
<!-- 👉 Search -->
|
|
||||||
<AppTextField
|
|
||||||
v-model="searchQuery"
|
|
||||||
:placeholder="$t('Search')"
|
|
||||||
density="compact"
|
|
||||||
style="inline-size: 200px;"
|
|
||||||
class="me-3"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<VSpacer />
|
|
||||||
<div class="d-flex gap-4 flex-wrap align-center">
|
|
||||||
<VBtn
|
|
||||||
color="primary"
|
|
||||||
prepend-icon="tabler-reload"
|
|
||||||
@click="reloadStores"
|
|
||||||
>
|
|
||||||
{{ t("Reload") }}
|
|
||||||
</VBtn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<VDivider class="mt-4" />
|
|
||||||
|
|
||||||
<VCol cols="12">
|
|
||||||
<div v-if="isLoading">
|
|
||||||
<VProgressCircular
|
|
||||||
indeterminate
|
|
||||||
color="primary"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- 👉 Datatable -->
|
|
||||||
<VDataTable
|
|
||||||
v-else
|
|
||||||
class="dt-row-striped"
|
|
||||||
:headers="headers"
|
|
||||||
:items="filteredData"
|
|
||||||
:items-per-page="options.itemsPerPage"
|
|
||||||
:page="options.page"
|
|
||||||
:options="options"
|
|
||||||
expand-on-click
|
|
||||||
>
|
|
||||||
<!-- Expanded Row Data -->
|
|
||||||
<template #expanded-row="slotProps">
|
|
||||||
<tr class="v-data-table__tr">
|
|
||||||
<td :colspan="headers.length">
|
|
||||||
<p class="my-1">
|
|
||||||
City: {{ slotProps.item.logMessage }}
|
|
||||||
</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Store details hyperlink -->
|
|
||||||
<template #item.businessDate="{ item }">
|
|
||||||
<span> {{ formatDate(item.businessDate) }}</span>
|
|
||||||
</template>
|
|
||||||
</VDataTable>
|
|
||||||
</VCol>
|
|
||||||
</vcard>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
@ -0,0 +1,485 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { endOfDay, format } from 'date-fns'
|
||||||
|
import exportFromJSON from 'export-from-json'
|
||||||
|
import { VDataTable } from 'vuetify/labs/VDataTable'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const headers = computed(() => [
|
||||||
|
{ title: '', key: 'data-table-expand' },
|
||||||
|
{ title: t('Store'), key: 'storeName' },
|
||||||
|
{ title: t('WSK'), key: 'wkstnId' },
|
||||||
|
{ title: t('Level'), key: 'logLevel' },
|
||||||
|
{ title: t('Category'), key: 'loggerCategory' },
|
||||||
|
{ title: t('Thread'), key: 'threadName' },
|
||||||
|
{ title: t('Date'), key: 'createDate' },
|
||||||
|
{ title: t('User'), key: 'createUserId' },
|
||||||
|
])
|
||||||
|
|
||||||
|
const selectedStore = ref(null)
|
||||||
|
const selectedWkStn = ref(null)
|
||||||
|
const selectedLogLevel = ref(null)
|
||||||
|
const selectedCategory = ref(null)
|
||||||
|
const selectedDateRange = ref(null)
|
||||||
|
const searchQuery = ref('')
|
||||||
|
|
||||||
|
const isSnackbarExport = ref(false)
|
||||||
|
const isSnackbarRange = ref(false)
|
||||||
|
|
||||||
|
// Data table options
|
||||||
|
const options = ref({ page: 1, itemsPerPage: 10, sortBy: ['createDate'], sortDesc: [true] })
|
||||||
|
const isLoading = ref(false)
|
||||||
|
interface dtListDataType {
|
||||||
|
businessDate: string
|
||||||
|
createDate: Date
|
||||||
|
createUserId: string
|
||||||
|
rtlLocId: number
|
||||||
|
wkstnId: number
|
||||||
|
logLevel: string
|
||||||
|
threadName: string
|
||||||
|
logMessage: string
|
||||||
|
loggerCategory: string
|
||||||
|
storeName: string
|
||||||
|
}
|
||||||
|
const dtListData = ref<dtListDataType[]>([])
|
||||||
|
const levelCounts = reactive({ INFO: 0, WARN: 0, ERROR: 0, FATAL: 0 })
|
||||||
|
|
||||||
|
const widgetData = computed(() => [
|
||||||
|
{ title: 'INFO', value: levelCounts.INFO, icon: 'tabler-info-circle' },
|
||||||
|
{ title: 'WARN', value: levelCounts.WARN, icon: 'tabler-alert-triangle' },
|
||||||
|
{ title: 'ERRROR', value: levelCounts.ERROR, icon: 'tabler-exclamation-circle' },
|
||||||
|
{ title: 'FATAL', value: levelCounts.FATAL, icon: 'tabler-bug' },
|
||||||
|
])
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
isLoading.value = true
|
||||||
|
isSnackbarExport.value = false
|
||||||
|
isSnackbarRange.value = false
|
||||||
|
|
||||||
|
const rangeDate = selectedDateRange.value || ''
|
||||||
|
let beginDate
|
||||||
|
let endDate
|
||||||
|
|
||||||
|
if (rangeDate.length === 8) {
|
||||||
|
beginDate = rangeDate.substring(0, 8)
|
||||||
|
endDate = beginDate
|
||||||
|
}
|
||||||
|
else if (rangeDate.length > 0) {
|
||||||
|
beginDate = rangeDate.substring(0, 8)
|
||||||
|
endDate = rangeDate.substring(rangeDate.length - 8)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
beginDate = format(endOfDay(new Date()), 'yyyyMMdd')
|
||||||
|
endDate = beginDate
|
||||||
|
}
|
||||||
|
|
||||||
|
let paramUrl = `?beginDate=${beginDate}&endDate=${endDate}`
|
||||||
|
|
||||||
|
if (selectedStore && selectedStore.value) {
|
||||||
|
const matchedStore: dtListDataType | undefined = dtListData.value.find((item: dtListDataType) => item.storeName === selectedStore.value)
|
||||||
|
|
||||||
|
if (matchedStore && matchedStore.rtlLocId)
|
||||||
|
paramUrl += `&storeId=${matchedStore.rtlLocId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedWkStn && selectedWkStn.value)
|
||||||
|
paramUrl += `&wkstnId=${selectedWkStn.value}`
|
||||||
|
|
||||||
|
if (searchQuery.value)
|
||||||
|
paramUrl += `&searchText=${searchQuery.value}`
|
||||||
|
|
||||||
|
levelCounts.INFO = 0
|
||||||
|
levelCounts.WARN = 0
|
||||||
|
levelCounts.ERROR = 0
|
||||||
|
levelCounts.FATAL = 0
|
||||||
|
|
||||||
|
if (beginDate !== endDate && (searchQuery.value.length < 5)) {
|
||||||
|
isSnackbarRange.value = true
|
||||||
|
dtListData.value = []
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const response = await useApi<any>(createUrl(`/xadmin/log${paramUrl}`))
|
||||||
|
|
||||||
|
if (response.data.value) {
|
||||||
|
dtListData.value = response.data.value
|
||||||
|
|
||||||
|
// Calculate the number of occurrences of each level
|
||||||
|
dtListData.value.forEach((item: { logLevel: string }) => {
|
||||||
|
const logLevel = item.logLevel as keyof typeof levelCounts
|
||||||
|
|
||||||
|
if (logLevel in levelCounts)
|
||||||
|
levelCounts[logLevel]++
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else { dtListData.value = [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = computed(() => {
|
||||||
|
const allItems = dtListData.value.map(({ storeName, rtlLocId }: { storeName: any; rtlLocId: any }) => ({ storeName, rtlLocId }))
|
||||||
|
|
||||||
|
const uniqueItems = allItems.filter((item: any, index: any, self: any[]) =>
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
index === self.findIndex(t => (
|
||||||
|
t.storeName === item.storeName && t.rtlLocId === item.rtlLocId
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
|
||||||
|
const sortedItems = uniqueItems.sort((a: { rtlLocId: number }, b: { rtlLocId: number }) => a.rtlLocId - b.rtlLocId)
|
||||||
|
|
||||||
|
return sortedItems.map(({ storeName }: { storeName: any }) => ({ title: storeName, value: storeName }))
|
||||||
|
})
|
||||||
|
|
||||||
|
const wkstn = computed(() => {
|
||||||
|
const allItems = dtListData.value.map((list: { wkstnId: any }) => list.wkstnId)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
const uniqueItems = allItems.filter((wkstn: any, index: any, self: string | any[]) => self.indexOf(wkstn) === index)
|
||||||
|
const sortedItems = uniqueItems.sort()
|
||||||
|
|
||||||
|
return sortedItems.map((list: any) => ({ title: list, value: list }))
|
||||||
|
})
|
||||||
|
|
||||||
|
const level = computed(() => {
|
||||||
|
const allItems = dtListData.value.map((list: { logLevel: any }) => list.logLevel)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
const uniqueItems = allItems.filter((level: any, index: any, self: string | any[]) => self.indexOf(level) === index)
|
||||||
|
const sortedItems = uniqueItems.sort()
|
||||||
|
|
||||||
|
return sortedItems.map((list: any) => ({ title: list, value: list }))
|
||||||
|
})
|
||||||
|
|
||||||
|
const category = computed(() => {
|
||||||
|
const allItems = dtListData.value.map((list: { loggerCategory: any }) => list.loggerCategory)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||||
|
const uniqueItems = allItems.filter((category: any, index: any, self: string | any[]) => self.indexOf(category) === index)
|
||||||
|
const sortedItems = uniqueItems.sort()
|
||||||
|
|
||||||
|
return sortedItems.map((list: any) => ({ title: list, value: list }))
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredLogList = computed(() => {
|
||||||
|
let filtered = dtListData.value
|
||||||
|
|
||||||
|
// If a store is selected, filter the records for this store
|
||||||
|
if (selectedStore.value !== undefined && selectedStore.value !== null)
|
||||||
|
filtered = filtered.filter((list: { storeName: any }) => list.storeName === selectedStore.value)
|
||||||
|
|
||||||
|
// If a workstation is selected, filter the records for this number
|
||||||
|
if (selectedWkStn.value !== undefined && selectedWkStn.value !== null)
|
||||||
|
filtered = filtered.filter((list: { wkstnId: any }) => list.wkstnId === selectedWkStn.value)
|
||||||
|
|
||||||
|
// If a loglevel is selected, filter the records for this Loglevel
|
||||||
|
if (selectedLogLevel.value !== undefined && selectedLogLevel.value !== null)
|
||||||
|
filtered = filtered.filter((list: { logLevel: any }) => list.logLevel === selectedLogLevel.value)
|
||||||
|
|
||||||
|
// If a loggerCategory is selected, filter the records for this loggerCategory
|
||||||
|
if (selectedCategory.value !== undefined && selectedCategory.value !== null)
|
||||||
|
filtered = filtered.filter((list: { loggerCategory: any }) => list.loggerCategory === selectedCategory.value)
|
||||||
|
|
||||||
|
// If a search query is provided, filter the records for this query
|
||||||
|
if (searchQuery.value) {
|
||||||
|
filtered = filtered.filter((log: { [s: string]: unknown } | ArrayLike<unknown>) =>
|
||||||
|
Object.values(log).some(value =>
|
||||||
|
String(value).toLowerCase().includes(searchQuery.value.toLowerCase()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
})
|
||||||
|
|
||||||
|
const ExcelField = [
|
||||||
|
'rtlLocId',
|
||||||
|
'storeName',
|
||||||
|
'wkstnId',
|
||||||
|
'logLevel',
|
||||||
|
'threadName',
|
||||||
|
'logMessage',
|
||||||
|
'loggerCategory',
|
||||||
|
'createDate',
|
||||||
|
'createUserId',
|
||||||
|
]
|
||||||
|
|
||||||
|
const ExcelData = computed(() => {
|
||||||
|
return dtListData.value.map((item: dtListDataType) => {
|
||||||
|
const orderedItem: { [key: string]: string } = {}
|
||||||
|
|
||||||
|
ExcelField.forEach(field => {
|
||||||
|
if (field === 'createDate') {
|
||||||
|
const date = new Date(item[field as keyof dtListDataType])
|
||||||
|
|
||||||
|
orderedItem[field] = format(date, 'dd/MM/yyyy HH:mm:ss')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
orderedItem[field] = String(item[field as keyof dtListDataType])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return orderedItem
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const exportEXCEL = () => {
|
||||||
|
const fileName = `LOGS_XADMIN-${format(new Date(), 'yyyyMMdd')}`
|
||||||
|
|
||||||
|
if (ExcelData.value.length > 0) {
|
||||||
|
exportFromJSON({
|
||||||
|
data: ExcelData.value,
|
||||||
|
fileName,
|
||||||
|
exportType: exportFromJSON.types.xls,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
isSnackbarExport.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 👉 Statistics Widgets -->
|
||||||
|
<VCard class="mb-6">
|
||||||
|
<VCardText>
|
||||||
|
<VRow>
|
||||||
|
<template
|
||||||
|
v-for="(data, id) in widgetData"
|
||||||
|
:key="id"
|
||||||
|
>
|
||||||
|
<VCol
|
||||||
|
cols="12"
|
||||||
|
sm="6"
|
||||||
|
md="3"
|
||||||
|
class="px-6"
|
||||||
|
>
|
||||||
|
<div class="d-flex justify-space-between">
|
||||||
|
<div class="d-flex flex-column gap-y-1">
|
||||||
|
<h4
|
||||||
|
class="text-h4 text-high-emphasis"
|
||||||
|
:class="{ 'text-error': id === 3 }"
|
||||||
|
>
|
||||||
|
{{ data.value }}
|
||||||
|
</h4>
|
||||||
|
<div class="text-body-1 text-capitalize">
|
||||||
|
{{ data.title }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<VAvatar
|
||||||
|
color="rgba(var(--v-theme-on-background), var(--v-hover-opacity))"
|
||||||
|
rounded
|
||||||
|
class="text-high-emphasis"
|
||||||
|
size="38"
|
||||||
|
>
|
||||||
|
<VIcon
|
||||||
|
:icon="data.icon"
|
||||||
|
size="26"
|
||||||
|
/>
|
||||||
|
</VAvatar>
|
||||||
|
</div>
|
||||||
|
</VCol>
|
||||||
|
<VDivider
|
||||||
|
v-if="$vuetify.display.mdAndUp ? id !== widgetData.length - 1
|
||||||
|
: $vuetify.display.smAndUp ? id % 2 === 0
|
||||||
|
: false"
|
||||||
|
vertical
|
||||||
|
inset
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VRow>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
<!-- 👉 filters -->
|
||||||
|
<VCard
|
||||||
|
:title="$t('Filters')"
|
||||||
|
class="mb-6"
|
||||||
|
>
|
||||||
|
<VCardText>
|
||||||
|
<VRow>
|
||||||
|
<!-- 👉 Select workstation -->
|
||||||
|
<VCol
|
||||||
|
cols="12"
|
||||||
|
sm="4"
|
||||||
|
>
|
||||||
|
<AppSelect
|
||||||
|
v-model="selectedStore"
|
||||||
|
:placeholder="$t('Store')"
|
||||||
|
:items="store"
|
||||||
|
clearable
|
||||||
|
clear-icon="tabler-x"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<!-- 👉 Select workstation -->
|
||||||
|
<VCol
|
||||||
|
cols="12"
|
||||||
|
sm="2"
|
||||||
|
>
|
||||||
|
<AppSelect
|
||||||
|
v-model="selectedWkStn"
|
||||||
|
:placeholder="$t('WorkStation')"
|
||||||
|
:items="wkstn"
|
||||||
|
clearable
|
||||||
|
clear-icon="tabler-x"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<!-- 👉 Select log level -->
|
||||||
|
<VCol
|
||||||
|
cols="12"
|
||||||
|
sm="2"
|
||||||
|
>
|
||||||
|
<AppSelect
|
||||||
|
v-model="selectedLogLevel"
|
||||||
|
:placeholder="$t('Level')"
|
||||||
|
:items="level"
|
||||||
|
clearable
|
||||||
|
clear-icon="tabler-x"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
|
||||||
|
<!-- 👉 Select logger category -->
|
||||||
|
<VCol
|
||||||
|
cols="12"
|
||||||
|
sm="4"
|
||||||
|
>
|
||||||
|
<AppSelect
|
||||||
|
v-model="selectedCategory"
|
||||||
|
:placeholder="$t('loggerCategory')"
|
||||||
|
:items="category"
|
||||||
|
clearable
|
||||||
|
clear-icon="tabler-x"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
<VRow>
|
||||||
|
<!-- 👉 Date log -->
|
||||||
|
<VCol sm="4">
|
||||||
|
<AppDateTimePicker
|
||||||
|
v-model="selectedDateRange"
|
||||||
|
label=""
|
||||||
|
:placeholder="$t('Select date')"
|
||||||
|
:config="{ mode: 'range', altFormat: 'J M Y', altInput: true, dateFormat: 'Ymd' }"
|
||||||
|
/>
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
<!-- 👉 datatable, search & export & reload -->
|
||||||
|
<VCard
|
||||||
|
:title="$t('Logs list')"
|
||||||
|
class="mb-6"
|
||||||
|
>
|
||||||
|
<div class="d-flex flex-wrap gap-4 mx-5">
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<!-- 👉 Search -->
|
||||||
|
<AppTextField
|
||||||
|
v-model="searchQuery"
|
||||||
|
:placeholder="$t('Search')"
|
||||||
|
density="compact"
|
||||||
|
style="inline-size: 200px;"
|
||||||
|
class="me-3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<VSpacer />
|
||||||
|
<div class="d-flex gap-1 flex-wrap aalign-center">
|
||||||
|
<!-- 👉 Export button -->
|
||||||
|
<VBtn
|
||||||
|
variant="tonal"
|
||||||
|
color="primary"
|
||||||
|
prepend-icon="tabler-upload"
|
||||||
|
@click="exportEXCEL"
|
||||||
|
>
|
||||||
|
Export
|
||||||
|
</VBtn>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-4 flex-wrap align-center">
|
||||||
|
<VBtn
|
||||||
|
color="primary"
|
||||||
|
prepend-icon="tabler-reload"
|
||||||
|
@click="fetchData"
|
||||||
|
>
|
||||||
|
{{ t("Reload") }}
|
||||||
|
</VBtn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<VDivider class="mt-4" />
|
||||||
|
|
||||||
|
<VCol cols="12">
|
||||||
|
<div v-if="isLoading">
|
||||||
|
<VProgressCircular
|
||||||
|
indeterminate
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<!-- 👉 Datatable -->
|
||||||
|
<VDataTable
|
||||||
|
v-else
|
||||||
|
class="dt-row-striped"
|
||||||
|
:headers="headers"
|
||||||
|
:items="filteredLogList"
|
||||||
|
:items-per-page="options.itemsPerPage"
|
||||||
|
:page="options.page"
|
||||||
|
:options="options"
|
||||||
|
density="compact"
|
||||||
|
expand-on-click
|
||||||
|
>
|
||||||
|
<!-- format date log -->
|
||||||
|
<template #item.createDate="{ item }">
|
||||||
|
{{ format(new Date(item.createDate), 'dd/MM/yyyy HH:mm:ss') }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- format business date -->
|
||||||
|
<template #item.businessDate="{ item }">
|
||||||
|
{{ format(new Date(item.businessDate), 'dd/MM/yyyy') }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Expanded Row Data -->
|
||||||
|
<template #expanded-row="slotProps">
|
||||||
|
<tr class="v-data-table__tr">
|
||||||
|
<td :colspan="headers.length">
|
||||||
|
<p class="my-1">
|
||||||
|
{{ slotProps.item.logMessage }}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</VDataTable>
|
||||||
|
</VCol>
|
||||||
|
</VCard>
|
||||||
|
</div>
|
||||||
|
<!-- Snackbar Export -->
|
||||||
|
<VSnackbar
|
||||||
|
v-model="isSnackbarExport"
|
||||||
|
location="center"
|
||||||
|
>
|
||||||
|
{{ $t('ExcelData is empty, cannot export') }}
|
||||||
|
|
||||||
|
<template #actions>
|
||||||
|
<VBtn
|
||||||
|
color="error"
|
||||||
|
@click="isSnackbarExport = false"
|
||||||
|
>
|
||||||
|
{{ $t("Close") }}
|
||||||
|
</VBtn>
|
||||||
|
</template>
|
||||||
|
</VSnackbar>
|
||||||
|
<!-- Snackbar Export -->
|
||||||
|
<VSnackbar
|
||||||
|
v-model="isSnackbarRange"
|
||||||
|
location="center"
|
||||||
|
>
|
||||||
|
{{ $t('Dates range error') }}
|
||||||
|
|
||||||
|
<template #actions>
|
||||||
|
<VBtn
|
||||||
|
color="error"
|
||||||
|
@click="isSnackbarRange = false"
|
||||||
|
>
|
||||||
|
{{ $t("Close") }}
|
||||||
|
</VBtn>
|
||||||
|
</template>
|
||||||
|
</VSnackbar>
|
||||||
|
</template>
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
"Minimum 5 characters long": "Minimum 5 characters long",
|
"Minimum 5 characters long": "Minimum 5 characters long",
|
||||||
"Filters": "Filters",
|
"Filters": "Filters",
|
||||||
"XADMIN": "XADMIN",
|
"XADMIN": "XADMIN",
|
||||||
"Application Log": "Application Log",
|
"Logs": "Logs",
|
||||||
"Level": "level",
|
"Level": "level",
|
||||||
"loggerCategory": "loggerCategory",
|
"loggerCategory": "loggerCategory",
|
||||||
"Thread": "Thread",
|
"Thread": "Thread",
|
||||||
|
|
@ -65,6 +65,10 @@
|
||||||
"Select date": "Select date",
|
"Select date": "Select date",
|
||||||
"ExcelData is empty, cannot export": "ExcelData is empty, cannot export",
|
"ExcelData is empty, cannot export": "ExcelData is empty, cannot export",
|
||||||
"Logs list": "Logs list",
|
"Logs list": "Logs list",
|
||||||
|
"Business Date": "Business Date",
|
||||||
|
"Dates range error": "With a date range, a text of at least 5 characters must be specified",
|
||||||
|
"Close": "Close",
|
||||||
|
"Connect to invidual POS": "Connect to invidual POS",
|
||||||
"---------------------------": "---------------------------",
|
"---------------------------": "---------------------------",
|
||||||
"UI Elements": "عناصر واجهة المستخدم",
|
"UI Elements": "عناصر واجهة المستخدم",
|
||||||
"Forms & Tables": "النماذج والجداول",
|
"Forms & Tables": "النماذج والجداول",
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
"Minimum 5 characters long": "Minimum 5 characters long",
|
"Minimum 5 characters long": "Minimum 5 characters long",
|
||||||
"Filters": "Filters",
|
"Filters": "Filters",
|
||||||
"XADMIN": "XADMIN",
|
"XADMIN": "XADMIN",
|
||||||
"Application Log": "Application Log",
|
"Logs": "Logs",
|
||||||
"Level": "level",
|
"Level": "level",
|
||||||
"loggerCategory": "loggerCategory",
|
"loggerCategory": "loggerCategory",
|
||||||
"Thread": "Thread",
|
"Thread": "Thread",
|
||||||
|
|
@ -65,6 +65,10 @@
|
||||||
"Select date": "Select date",
|
"Select date": "Select date",
|
||||||
"ExcelData is empty, cannot export": "No data, cannot export",
|
"ExcelData is empty, cannot export": "No data, cannot export",
|
||||||
"Logs list": "Logs list",
|
"Logs list": "Logs list",
|
||||||
|
"Business Date": "Business Date",
|
||||||
|
"Dates range error": "With a date range, a text of at least 5 characters must be specified",
|
||||||
|
"Close": "Close",
|
||||||
|
"Connect to invidual POS": "Connect to invidual POS",
|
||||||
"---------------------------": "---------------------------",
|
"---------------------------": "---------------------------",
|
||||||
"UI Elements": "UI Elements",
|
"UI Elements": "UI Elements",
|
||||||
"Forms & Tables": "Forms & Tables",
|
"Forms & Tables": "Forms & Tables",
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
"Minimum 5 characters long": "Minimum 5 caractères",
|
"Minimum 5 characters long": "Minimum 5 caractères",
|
||||||
"Filters": "Filtres",
|
"Filters": "Filtres",
|
||||||
"XADMIN": "XADMIN",
|
"XADMIN": "XADMIN",
|
||||||
"Application Log": "Application Log",
|
"Logs": "Logs",
|
||||||
"Level": "Niveau",
|
"Level": "Niveau",
|
||||||
"loggerCategory": "Categorie log",
|
"loggerCategory": "Categorie log",
|
||||||
"Thread": "Thread",
|
"Thread": "Thread",
|
||||||
|
|
@ -65,6 +65,10 @@
|
||||||
"Select date": "Selectionner une date",
|
"Select date": "Selectionner une date",
|
||||||
"ExcelData is empty, cannot export": "Aucune donnée, impossible d'exporter",
|
"ExcelData is empty, cannot export": "Aucune donnée, impossible d'exporter",
|
||||||
"Logs list": "Liste des logs",
|
"Logs list": "Liste des logs",
|
||||||
|
"Business Date": "Date ouverture",
|
||||||
|
"Dates range error": "Dans le cas d'une plage de dates, un texte d'au moins 5 caractères doit être spécifié.",
|
||||||
|
"Close": "Fermer",
|
||||||
|
"Connect to invidual POS": "Connexion à une caisse individuelle",
|
||||||
"---------------------------": "---------------------------",
|
"---------------------------": "---------------------------",
|
||||||
"UI Elements": "ÉLÉMENTS DE L'UI",
|
"UI Elements": "ÉLÉMENTS DE L'UI",
|
||||||
"Forms & Tables": "Formulaires et tableaux",
|
"Forms & Tables": "Formulaires et tableaux",
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { endOfDay, format } from 'date-fns'
|
import { endOfDay, format } from 'date-fns'
|
||||||
import exportFromJSON from 'export-from-json'
|
import exportFromJSON from 'export-from-json'
|
||||||
import { French } from 'flatpickr/dist/l10n/fr'
|
|
||||||
import { VDataTable } from 'vuetify/labs/VDataTable'
|
import { VDataTable } from 'vuetify/labs/VDataTable'
|
||||||
import type { StoreData } from '@/models/storeData'
|
import type { StoreData } from '@/models/storeData'
|
||||||
|
|
||||||
|
|
@ -14,9 +13,10 @@ const headers = computed(() => [
|
||||||
{ title: '', key: 'data-table-expand' },
|
{ title: '', key: 'data-table-expand' },
|
||||||
{ title: t('WSK'), key: 'wkstnId' },
|
{ title: t('WSK'), key: 'wkstnId' },
|
||||||
{ title: t('Level'), key: 'logLevel' },
|
{ title: t('Level'), key: 'logLevel' },
|
||||||
{ title: t('Thread'), key: 'threadName' },
|
|
||||||
{ title: t('Category'), key: 'loggerCategory' },
|
{ title: t('Category'), key: 'loggerCategory' },
|
||||||
|
{ title: t('Thread'), key: 'threadName' },
|
||||||
{ title: t('Date'), key: 'createDate' },
|
{ title: t('Date'), key: 'createDate' },
|
||||||
|
{ title: t('Business Date'), key: 'businessDate' },
|
||||||
{ title: t('User'), key: 'createUserId' },
|
{ title: t('User'), key: 'createUserId' },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
@ -30,18 +30,18 @@ interface Props {
|
||||||
storeData: StoreData
|
storeData: StoreData
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSnackbarVisibility = ref(false)
|
const isSnackbarExport = ref(false)
|
||||||
|
|
||||||
// Data table options
|
// Data table options
|
||||||
// TODO SortBy ne marche pas
|
// TODO SortBy ne marche pas
|
||||||
const options = ref({ page: 1, itemsPerPage: 10, sortBy: ['createDate'], sortDesc: [true] })
|
const options = ref({ page: 1, itemsPerPage: 10, sortBy: ['createDate'], sortDesc: [true] })
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
|
||||||
const dtListData = ref([]) // Initialisez data comme un tableau vide
|
const dtListData = ref([])
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
isSnackbarVisibility.value = false
|
isSnackbarExport.value = false
|
||||||
|
|
||||||
let logDate
|
let logDate
|
||||||
if (selectedDate.value)
|
if (selectedDate.value)
|
||||||
|
|
@ -143,12 +143,7 @@ const ExcelData = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const exportEXCEL = () => {
|
const exportEXCEL = () => {
|
||||||
const date = new Date()
|
const fileName = `LOGS_${props.storeData.store.id_structure}-${format(new Date(), 'yyyyMMdd')}`
|
||||||
const year = date.getFullYear()
|
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
||||||
const day = String(date.getDate()).padStart(2, '0')
|
|
||||||
|
|
||||||
const fileName = `LOGS_${props.storeData.store.id_structure}-${year}${month}${day}`
|
|
||||||
|
|
||||||
if (ExcelData.value.length > 0) {
|
if (ExcelData.value.length > 0) {
|
||||||
exportFromJSON({
|
exportFromJSON({
|
||||||
|
|
@ -158,14 +153,14 @@ const exportEXCEL = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
isSnackbarVisibility.value = true
|
isSnackbarExport.value = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- 👉 logs -->
|
<!-- 👉 filters -->
|
||||||
<VCard
|
<VCard
|
||||||
:title="$t('Filters')"
|
:title="$t('Filters')"
|
||||||
class="mb-6"
|
class="mb-6"
|
||||||
|
|
@ -220,14 +215,13 @@ const exportEXCEL = () => {
|
||||||
<AppDateTimePicker
|
<AppDateTimePicker
|
||||||
v-model="selectedDate"
|
v-model="selectedDate"
|
||||||
label=""
|
label=""
|
||||||
:locale="French"
|
|
||||||
:placeholder="$t('Select date')"
|
:placeholder="$t('Select date')"
|
||||||
/>
|
/>
|
||||||
</VCol>
|
</VCol>
|
||||||
</VRow>
|
</VRow>
|
||||||
</VCardText>
|
</VCardText>
|
||||||
</VCard>
|
</VCard>
|
||||||
|
<!-- 👉 datatable, search & export & reload -->
|
||||||
<VCard
|
<VCard
|
||||||
:title="$t('Logs list')"
|
:title="$t('Logs list')"
|
||||||
class="mb-6"
|
class="mb-6"
|
||||||
|
|
@ -293,6 +287,11 @@ const exportEXCEL = () => {
|
||||||
{{ format(new Date(item.createDate), 'dd/MM/yyyy HH:mm:ss') }}
|
{{ format(new Date(item.createDate), 'dd/MM/yyyy HH:mm:ss') }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- format business date -->
|
||||||
|
<template #item.businessDate="{ item }">
|
||||||
|
{{ format(new Date(item.businessDate), 'dd/MM/yyyy') }}
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- Expanded Row Data -->
|
<!-- Expanded Row Data -->
|
||||||
<template #expanded-row="slotProps">
|
<template #expanded-row="slotProps">
|
||||||
<tr class="v-data-table__tr">
|
<tr class="v-data-table__tr">
|
||||||
|
|
@ -307,9 +306,9 @@ const exportEXCEL = () => {
|
||||||
</VCol>
|
</VCol>
|
||||||
</VCard>
|
</VCard>
|
||||||
</div>
|
</div>
|
||||||
<!-- Snackbar -->
|
<!-- Snackbar Export -->
|
||||||
<VSnackbar
|
<VSnackbar
|
||||||
v-model="isSnackbarVisibility"
|
v-model="isSnackbarExport"
|
||||||
location="center"
|
location="center"
|
||||||
>
|
>
|
||||||
{{ $t('ExcelData is empty, cannot export') }}
|
{{ $t('ExcelData is empty, cannot export') }}
|
||||||
|
|
@ -317,9 +316,9 @@ const exportEXCEL = () => {
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<VBtn
|
<VBtn
|
||||||
color="error"
|
color="error"
|
||||||
@click="isSnackbarVisibility = false"
|
@click="isSnackbarExport = false"
|
||||||
>
|
>
|
||||||
Close
|
{{ $t("Close") }}
|
||||||
</VBtn>
|
</VBtn>
|
||||||
</template>
|
</template>
|
||||||
</VSnackbar>
|
</VSnackbar>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export const { themeConfig, layoutConfig } = defineThemeConfig({
|
||||||
overlayNavFromBreakpoint: breakpointsVuetify.md + 16, // 16 for scrollbar. Docs: https://next.vuetifyjs.com/en/features/display-and-platform/
|
overlayNavFromBreakpoint: breakpointsVuetify.md + 16, // 16 for scrollbar. Docs: https://next.vuetifyjs.com/en/features/display-and-platform/
|
||||||
i18n: {
|
i18n: {
|
||||||
enable: true,
|
enable: true,
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'fr',
|
||||||
langConfig: [
|
langConfig: [
|
||||||
{
|
{
|
||||||
label: 'English',
|
label: 'English',
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ declare module 'vue-router/auto/routes' {
|
||||||
'login': RouteRecordInfo<'login', '/login', Record<never, never>, Record<never, never>>,
|
'login': RouteRecordInfo<'login', '/login', Record<never, never>, Record<never, never>>,
|
||||||
'store-details': RouteRecordInfo<'store-details', '/store/details', Record<never, never>, Record<never, never>>,
|
'store-details': RouteRecordInfo<'store-details', '/store/details', Record<never, never>, Record<never, never>>,
|
||||||
'store-list': RouteRecordInfo<'store-list', '/store/list', Record<never, never>, Record<never, never>>,
|
'store-list': RouteRecordInfo<'store-list', '/store/list', Record<never, never>, Record<never, never>>,
|
||||||
'xadmin-application-log': RouteRecordInfo<'xadmin-application-log', '/xadmin/application/log', Record<never, never>, Record<never, never>>,
|
'xadmin-log': RouteRecordInfo<'xadmin-log', '/xadmin/log', Record<never, never>, Record<never, never>>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue