This commit is contained in:
2026-02-11 17:46:22 +03:00
commit eacfacb13b
266 changed files with 51337 additions and 0 deletions

View File

@@ -0,0 +1,341 @@
<template>
<q-page class="act-page with-bg">
<!-- =======================================================
🔍 FILTER BAR
======================================================= -->
<div class="act-filter-bar">
<div class="act-filter-row">
<q-input
dense
filled
v-model="store.filters.username"
label="Kullanıcı"
clearable
class="act-filter-input"
@keyup.enter="store.fetchLogs()"
/>
<q-select
dense
filled
v-model="store.filters.actionCategory"
:options="categoryOptions"
label="Kategori"
clearable
emit-value
map-options
class="act-filter-input"
@update:model-value="store.fetchLogs()"
/>
<q-select
dense
filled
v-model="store.filters.actionType"
:options="actionTypeOptions"
label="Action"
clearable
emit-value
map-options
class="act-filter-input"
@update:model-value="store.fetchLogs()"
/>
<q-select
dense
filled
v-model="store.filters.success"
:options="successOptions"
label="Sonuç"
clearable
emit-value
map-options
class="act-filter-input"
@update:model-value="store.fetchLogs()"
/>
<q-input
dense
filled
type="date"
v-model="store.filters.dateFrom"
label="Başlangıç"
class="act-filter-input"
@update:model-value="store.fetchLogs()"
/>
<q-input
dense
filled
type="date"
v-model="store.filters.dateTo"
label="Bitiş"
class="act-filter-input"
@update:model-value="store.fetchLogs()"
/>
<div class="act-filter-actions">
<q-btn color="primary" unelevated label="Ara" @click="store.fetchLogs()" />
<q-btn flat label="Temizle" @click="store.resetFilters()" />
<!-- YENİ -->
<q-btn
outline
color="secondary"
label="Rol Değişimleri"
@click="store.quickRoleChange()"
/>
</div>
</div>
</div>
<!-- =======================================================
📊 LOG TABLE
======================================================= -->
<q-table
class="act-table sticky-table"
row-key="created_at"
:rows="store.rows"
:columns="columns"
:loading="store.loading"
binary-state-sort
>
<!-- ================= CHANGE DIFF MODAL ================= -->
<q-dialog v-model="diffDialog">
<q-card style="min-width:700px">
<q-card-section class="text-h6">
Rol Değişiklik Detayı
</q-card-section>
<q-separator />
<q-card-section>
<div class="row q-col-gutter-md">
<div class="col-6">
<div class="text-bold q-mb-sm">Önce</div>
<q-banner class="bg-grey-2 text-black">
<pre>{{ selectedDiff.before }}</pre>
</q-banner>
</div>
<div class="col-6">
<div class="text-bold q-mb-sm">Sonra</div>
<q-banner class="bg-green-1 text-black">
<pre>{{ selectedDiff.after }}</pre>
</q-banner>
</div>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="Kapat" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
<!-- Zaman -->
<template #body-cell-created_at="props">
<q-td :props="props">
{{ formatDate(props.row.created_at) }}
</q-td>
</template>
<!-- Route -->
<template #body-cell-action_target="props">
<q-td :props="props" class="act-col-route">
{{ props.row.action_target }}
</q-td>
</template>
<!-- HTTP -->
<template #body-cell-http_status="props">
<q-td :props="props">
<q-badge
v-if="props.row.http_status"
:label="props.row.http_status"
:class="props.row.http_status < 300 ? 'act-badge-ok' : 'act-badge-fail'"
/>
</q-td>
</template>
<!-- Diff -->
<template #body-cell-diff="props">
<q-td :props="props">
<q-btn
v-if="props.row.change_before || props.row.change_after"
dense
flat
color="primary"
icon="compare_arrows"
@click="openDiff(props.row)"
/>
<span v-else>-</span>
</q-td>
</template>
<!-- Sonuç -->
<template #body-cell-is_success="props">
<q-td :props="props">
<q-badge
:label="props.row.is_success ? 'OK' : 'FAIL'"
:class="props.row.is_success ? 'act-badge-ok' : 'act-badge-fail'"
/>
</q-td>
</template>
<template #no-data>
<div class="full-width row flex-center q-pa-md text-grey-6">
Log kaydı bulunamadı.
</div>
</template>
</q-table>
</q-page>
</template>
<script setup>
import { ref,onMounted, watch } from 'vue'
import { date } from 'quasar'
import { useActivityLogStore } from 'src/stores/activityLogStore'
import { useAuthStore } from 'stores/authStore.js'
import { usePermission } from 'src/composables/usePermission'
const { canRead, canWrite, canUpdate } = usePermission()
const canReadOrder = canRead('order')
const canWriteOrder = canWrite('order')
const canUpdateOrder = canUpdate('order')
const diffDialog = ref(false)
const selectedDiff = ref({
before: '',
after: ''
})
const store = useActivityLogStore()
const auth = useAuthStore()
const categoryOptions = [
{ label: 'Auth', value: 'auth' },
{ label: 'Navigation', value: 'nav' },
{ label: 'Yetkilendirme (User)', value: 'user_permission' },
{ label: 'Yetkilendirme (Role)', value: 'role_permission' },
{ label: 'Genel Yetki', value: 'permission' }
]
const actionTypeOptions = [
// USER
{
label: 'User Permission Change',
value: 'user_permission_change'
},
// ROLE
{
label: 'Role Permission Change',
value: 'permission_change'
},
{
label: 'Role + Dept Change',
value: 'role_department_permission_change'
},
// AUTH
{
label: 'Login',
value: 'login'
},
{
label: 'Logout',
value: 'logout'
}
]
const successOptions = [
{ label: 'Başarılı', value: true },
{ label: 'Hatalı', value: false }
]
const columns = [
{ name: 'created_at', label: 'Zaman', field: 'created_at', sortable: true },
{ name: 'username', label: 'İşlemi Yapan', field: 'username', sortable: true },
{ name: 'target_username', label: 'Hedef Kullanıcı', field: 'target_username' },
{ name: 'role_code', label: 'Rol', field: 'role_code' },
{ name: 'action_category', label: 'Kategori', field: 'action_category' },
{ name: 'action_type', label: 'Action', field: 'action_type' },
{ name: 'action_target', label: 'Route', field: 'action_target' },
{ name: 'http_status', label: 'HTTP', field: 'http_status' },
{ name: 'duration_ms', label: 'Süre (ms)', field: 'duration_ms' },
// 🔥 field önemli değil ama name = diff şart
{ name: 'diff', label: 'Değişiklik' },
{ name: 'is_success', label: 'Sonuç', field: 'is_success' }
]
function formatDate(v) {
if (!v) return '-'
return date.formatDate(v, 'YYYY-MM-DD HH:mm:ss')
}
/* =======================================================
🔐 TOKEN HAZIR OLDUĞUNDA LOG ÇEK
======================================================= */
function safeFetch() {
if (auth.isAuthenticated && auth.token) {
store.fetchLogs()
}
}
function openDiff (row) {
selectedDiff.value = {
before: pretty(row.change_before),
after: pretty(row.change_after)
}
diffDialog.value = true
}
function pretty (v) {
if (!v) return '-'
try {
return JSON.stringify(JSON.parse(v), null, 2)
} catch {
return v
}
}
onMounted(() => {
safeFetch()
})
watch(
() => auth.token,
(token) => {
if (token) {
safeFetch()
}
},
{ immediate: true }
)
</script>