Continue frontend building
This commit is contained in:
241
packages/sshecret-frontend/src/components/audit/AuditTable.vue
Normal file
241
packages/sshecret-frontend/src/components/audit/AuditTable.vue
Normal file
@ -0,0 +1,241 @@
|
||||
<template>
|
||||
<table v-if="auditEntries" class="min-w-full divide-y divide-gray-200 dark:divide-gray-600">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Timestamp
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Subsystem
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Operation
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Client
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Secret
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Message
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white"
|
||||
>
|
||||
Origin
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800">
|
||||
<template v-for="entry in auditEntries" :key="entry.id">
|
||||
<tr class="auditRow hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||
<td>
|
||||
<sl-icon-button
|
||||
name="chevron-right"
|
||||
@click="toggle(entry.id)"
|
||||
:class="{ 'rotate-90': isExpanded(entry.id) }"
|
||||
></sl-icon-button>
|
||||
</td>
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
{{ entry.timestamp }}
|
||||
</td>
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
{{ entry.subsystem }}
|
||||
</td>
|
||||
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
{{ entry.operation }}
|
||||
</td>
|
||||
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<abbr :title="entry.client_id" v-if="entry.client_name">{{ entry.client_name }}</abbr>
|
||||
</td>
|
||||
<td class="p-4 text-sm font-normal text-gray-900 whitespace-nowrap dark:text-white">
|
||||
<abbr :title="entry.secret_id" v-if="entry.secret_name">{{ entry.secret_name }}</abbr>
|
||||
</td>
|
||||
<td class="p-4 text-sm font-normal text-gray-500 whitespace-nowrap dark:text-gray-400">
|
||||
{{ entry.message }}
|
||||
</td>
|
||||
<td class="p-4 text-sm font-normal text-gray-500 whitespace-nowrap dark:text-gray-400">
|
||||
{{ entry.origin }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="isExpanded(entry.id)" class="auditRow">
|
||||
<td></td>
|
||||
<td colspan="8">
|
||||
<dl
|
||||
class="max-w-md text-gray-900 divide-y divide-gray-200 dark:text-white dark:divide-gray-700 px-2 py-2"
|
||||
>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">ID</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.id }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Subsystem</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.subsystem }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Timestamp</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.timestamp }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Operation</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.operation }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client ID</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.client_id }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Client Name</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.client_name }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret ID</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.secret_id }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Secret Name</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.secret_name }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Message</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.message }}</dd>
|
||||
</div>
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">Origin</dt>
|
||||
<dd class="text-xs font-semibold">{{ entry.origin }}</dd>
|
||||
</div>
|
||||
<template v-if="entry.data">
|
||||
<template v-for="(value, key) in entry.data">
|
||||
<div class="flex flex-col pb-3">
|
||||
<dt class="mb-1 text-gray-500 md:text-xs dark:text-gray-400">{{ key }}</dt>
|
||||
<dd class="text-xs font-semibold">{{ value }}</dd>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</dl>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
<div
|
||||
class="sticky bottom-0 right-0 items-center w-full p-4 bg-white border-t border-gray-200 sm:flex sm:justify-between dark:bg-gray-800 dark:border-gray-700"
|
||||
v-if="totalPages > 0"
|
||||
>
|
||||
<div class="flex items-center mb-4 sm:mb-0">
|
||||
<span class="text-sm font-normal text-gray-500 dark:text-gray-400">
|
||||
Showing
|
||||
<span class="font-semibold text-gray-900 dark:text-white" v-if="totalEntries < lastResult">
|
||||
{{ firstResult }}-{{ TotalEntries }}
|
||||
</span>
|
||||
<span class="font-semibold text-gray-900 dark:text-white" v-else>
|
||||
{{ firstResult }}-{{ lastResult }}
|
||||
</span>
|
||||
of
|
||||
<span class="font-semibold text-gray-900 dark:text-white">
|
||||
{{ totalEntries }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="flex space-x-1">
|
||||
<PageNumbers
|
||||
@next="nextPage"
|
||||
@previous="prevPage"
|
||||
@goto="goToPage"
|
||||
:pageNum="pageNum"
|
||||
:totalPages="totalPages"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, reactive, onMounted, watch, toRef } from 'vue'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import { usePagination } from '@/composables/usePagination'
|
||||
import { SshecretAdmin, GetAuditLogApiV1AuditGetData } from '@/client'
|
||||
import type { AuditListResult } from '@/client'
|
||||
import type { AuditFilter } from '@/api/types'
|
||||
import PageNumbers from '@/components/common/PageNumbers.vue'
|
||||
|
||||
const props = defineProps<{ auditFilter: AuditFilter }>()
|
||||
|
||||
const auditFilter = toRef(() => props.auditFilter)
|
||||
console.log(auditFilter.value)
|
||||
const auditList = ref<AuditListResult>([])
|
||||
const auditEntries = computed(() => auditList.value?.results)
|
||||
const totalEntries = computed(() => auditList.value?.total)
|
||||
|
||||
const perPage = props.auditFilter.limit ?? 25
|
||||
const { pageNum, offset, firstResult, lastResult, totalPages, nextPage, prevPage, goToPage } =
|
||||
usePagination(totalEntries, perPage)
|
||||
|
||||
const queryInput = computed<GetAuditLogApiV1AuditGetData['query']>(() => {
|
||||
return {
|
||||
...props.auditFilter,
|
||||
offset: offset.value,
|
||||
limit: perPage,
|
||||
}
|
||||
})
|
||||
|
||||
async function loadLogs() {
|
||||
const response = await SshecretAdmin.getAuditLogApiV1AuditGet({
|
||||
query: queryInput.value,
|
||||
})
|
||||
auditList.value = response.data
|
||||
}
|
||||
|
||||
const expanded = ref(new Set<string>())
|
||||
|
||||
function toggle(id: string) {
|
||||
if (expanded.value.has(id)) {
|
||||
expanded.value.delete(id)
|
||||
} else {
|
||||
expanded.value.add(id)
|
||||
}
|
||||
}
|
||||
|
||||
function isExpanded(id: string) {
|
||||
return expanded.value.has(id)
|
||||
}
|
||||
|
||||
watch([offset, pageNum, auditFilter], loadLogs)
|
||||
onMounted(loadLogs)
|
||||
</script>
|
||||
<style>
|
||||
tr.auditRow {
|
||||
background-color: var(--color-white);
|
||||
}
|
||||
tr.auditRow:nth-child(even) {
|
||||
background-color: var(--color-gray-50);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user