Apply space-y-8 rhythm to admin page sections (no destructive actions currently present in this view — no confirm dialog needed yet). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
80 lines
2.8 KiB
Vue
80 lines
2.8 KiB
Vue
<script setup lang="ts">
|
|
import { ref, onMounted } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import { adminApi } from '@/api/endpoints/admin'
|
|
import Card from '@/components/ui/Card.vue'
|
|
import CardContent from '@/components/ui/CardContent.vue'
|
|
import Badge from '@/components/ui/Badge.vue'
|
|
import Spinner from '@/components/ui/Spinner.vue'
|
|
import { formatDate } from '@/lib/utils'
|
|
import type { UserOut } from '@/types'
|
|
|
|
const authStore = useAuthStore()
|
|
const router = useRouter()
|
|
const users = ref<UserOut[]>([])
|
|
const loading = ref(false)
|
|
|
|
onMounted(async () => {
|
|
if (!authStore.isAdmin) {
|
|
router.push('/')
|
|
return
|
|
}
|
|
loading.value = true
|
|
try {
|
|
const res = await adminApi.users()
|
|
users.value = res.data
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="p-6 space-y-8">
|
|
<h2 class="text-lg font-semibold text-foreground">Admin — Users</h2>
|
|
|
|
<div v-if="loading" class="flex items-center justify-center h-20">
|
|
<Spinner class="text-primary" />
|
|
</div>
|
|
|
|
<Card v-else>
|
|
<CardContent class="p-0">
|
|
<table class="w-full">
|
|
<thead>
|
|
<tr class="border-b border-border">
|
|
<th class="text-left text-xs font-medium text-muted-foreground px-4 py-3">User</th>
|
|
<th class="text-left text-xs font-medium text-muted-foreground px-4 py-3">Email</th>
|
|
<th class="text-left text-xs font-medium text-muted-foreground px-4 py-3">Role</th>
|
|
<th class="text-left text-xs font-medium text-muted-foreground px-4 py-3">Status</th>
|
|
<th class="text-left text-xs font-medium text-muted-foreground px-4 py-3">Joined</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr
|
|
v-for="user in users"
|
|
:key="user.id"
|
|
class="border-b border-border last:border-0 hover:bg-muted/30"
|
|
>
|
|
<td class="px-4 py-3">
|
|
<p class="text-sm font-medium text-foreground">{{ user.username }}</p>
|
|
</td>
|
|
<td class="px-4 py-3 text-sm text-muted-foreground">{{ user.email }}</td>
|
|
<td class="px-4 py-3">
|
|
<Badge :variant="user.role === 'admin' ? 'default' : 'secondary'" class="text-xs">
|
|
{{ user.role }}
|
|
</Badge>
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
<Badge :variant="user.is_active ? 'success' : 'outline'" class="text-xs">
|
|
{{ user.is_active ? 'Active' : 'Inactive' }}
|
|
</Badge>
|
|
</td>
|
|
<td class="px-4 py-3 text-xs text-muted-foreground">{{ formatDate(user.created_at) }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</template>
|