mod-portal-v2/app/pages/index.vue

192 lines
5.7 KiB
Vue
Raw Normal View History

2026-07-27 10:34:50 +02:00
<script setup>
2026-07-27 23:23:02 +02:00
import { onMounted, ref } from 'vue'
import { useAuth } from '~/composables/useAuth.js'
2026-07-27 10:34:50 +02:00
2026-07-27 23:23:02 +02:00
const {
isAuthenticated,
isInitialized,
userProfile,
2026-08-03 05:39:08 +02:00
initializeAuth,
2026-07-27 23:23:02 +02:00
logout
} = useAuth()
const route = useRoute()
2026-07-27 10:34:50 +02:00
2026-08-05 06:36:58 +02:00
const recentActions = ref([])
const isLoadingActions = ref(true)
const FIELD_LABELS = {
account_level: 'Access Level',
country: 'Country',
language: 'Language',
timezone: 'Timezone',
gender: 'Gender',
email: 'Email',
marketing_allowed: 'Marketing Allowed',
off_device_allowed: 'Off-Device Allowed'
}
const formatDate = (isoStr) => {
if (!isoStr) return 'N/A'
try {
return new Date(isoStr).toLocaleString('en-US', {
dateStyle: 'short',
timeStyle: 'short'
})
} catch {
return isoStr
}
}
const parseLogChanges = (rawChanges) => {
if (!rawChanges) return { changes: [] }
if (typeof rawChanges === 'string') {
try {
return JSON.parse(rawChanges)
} catch {
return { changes: [] }
}
}
return rawChanges
}
const fetchRecentActions = async () => {
isLoadingActions.value = true
try {
const response = await $fetch('/api/admin/audit-logs?limit=3&page=1')
recentActions.value = response.logs || []
} catch (err) {
console.error('Failed to fetch recent actions:', err)
} finally {
isLoadingActions.value = false
}
}
2026-07-27 10:34:50 +02:00
const handleLogout = () => {
logout()
}
const handleNavigate = (path) => {
navigateTo(path)
}
2026-07-27 23:23:02 +02:00
onMounted(async () => {
2026-08-03 05:39:08 +02:00
await initializeAuth({ code: route.query.code, redirectTo: '/oauth' })
2026-08-05 06:36:58 +02:00
if (isAuthenticated.value) {
await fetchRecentActions()
}
2026-07-27 23:23:02 +02:00
})
2026-07-27 10:34:50 +02:00
</script>
<template>
<div>
<div v-if="!isInitialized" class="global-loader-wrapper">
<div class="spinner lg"></div>
<p style="color: #8b949e; margin-top: 1rem; font-size: 0.95rem;">
Connecting to Keycloak...
</p>
</div>
<div v-else-if="isAuthenticated" class="settings-container">
<Header
:user-profile="userProfile"
active-tab="welcome"
@logout="handleLogout"
@navigate="handleNavigate"
/>
<main class="settings-main-content">
<section class="settings-panel">
<div class="panel-header archivo-black">
<span>PORTAL OVERVIEW</span>
</div>
<div class="panel-body grid-layout">
<div class="welcome-left-col">
<div class="image-wrapper">
<img
src="/welcome7.png"
alt="Welcome to Mod Portal"
class="welcome-image"
/>
</div>
<h2 class="welcome-text">
Welcome to the <em>new</em> mod portal!
</h2>
</div>
<div class="logins-right-col">
<div class="logins-card">
2026-08-05 06:36:58 +02:00
<div class="logins-header archivo-black" style="display: flex; justify-content: space-between; align-items: center;">
<span>RECENT ACTIONS</span>
<NuxtLink to="/audit/log" class="view-all-link">View All </NuxtLink>
</div>
<div v-if="isLoadingActions" class="loading-state">
<div class="spinner sm"></div>
<span>Loading recent actions...</span>
2026-07-27 10:34:50 +02:00
</div>
2026-08-05 06:36:58 +02:00
<ul v-else-if="recentActions.length > 0" class="logins-list">
<li
v-for="log in recentActions"
:key="log.log_id || log.id"
class="login-item action-item"
@click="handleNavigate(`/audit/log/${log.log_id || log.id}`)"
>
<div class="action-top">
<div class="user-block">
<img
2026-08-09 21:57:29 +02:00
:src="log.editor_pid ? `https://mii.spbr.net/${log.editor_pid}/main.png` : '/img_unknown_MiiIcon.png'"
2026-08-05 06:36:58 +02:00
class="avatar-xs"
@error="(e) => e.target.src = '/img_unknown_MiiIcon.png'"
/>
<span class="editor-name">{{ log.editor_sfid || 'System' }}</span>
</div>
<span class="action-time">{{ formatDate(log.created_at) }}</span>
</div>
<div class="action-summary">
<span class="target-name">Modified {{ log.target_name }}</span>
<span v-if="log.ban_level !== undefined && log.ban_level !== null" class="badge-ban">
Banned
</span>
<span v-else class="changes-tags">
<span
v-for="change in parseLogChanges(log.changes).changes.slice(0, 2)"
:key="change.field"
class="change-chip"
>
{{ FIELD_LABELS[change.field] || change.field }}
</span>
</span>
2026-07-27 10:34:50 +02:00
</div>
</li>
</ul>
2026-08-05 06:36:58 +02:00
<div v-else class="empty-state">
<p>No recent actions logged.</p>
</div>
<div class="card-footer">
<button
type="button"
class="btn-action secondary sm"
style="width: 100%;"
@click="handleNavigate('/audit/log')"
>
Go to Audit Log Center
</button>
</div>
2026-07-27 10:34:50 +02:00
</div>
</div>
2026-08-05 06:36:58 +02:00
2026-07-27 10:34:50 +02:00
</div>
</section>
</main>
</div>
</div>
2026-08-05 06:36:58 +02:00
</template>