feat: Implement admin user and pending course management, instructor course listing, and a dedicated admin service.
All checks were successful
Build and Deploy Frontend Management to Dev Server / Build Frontend Management Docker Image (push) Successful in 42s
Build and Deploy Frontend Management to Dev Server / Deploy E-learning Frontend Management to Dev Server (push) Successful in 4s
Build and Deploy Frontend Management to Dev Server / Notify Deployment Status (push) Successful in 1s
All checks were successful
Build and Deploy Frontend Management to Dev Server / Build Frontend Management Docker Image (push) Successful in 42s
Build and Deploy Frontend Management to Dev Server / Deploy E-learning Frontend Management to Dev Server (push) Successful in 4s
Build and Deploy Frontend Management to Dev Server / Notify Deployment Status (push) Successful in 1s
This commit is contained in:
parent
5ad7184e6c
commit
9dc8636d31
4 changed files with 212 additions and 46 deletions
|
|
@ -31,33 +31,48 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search -->
|
||||
<!-- Search & View Toggle -->
|
||||
<div class="bg-white rounded-xl shadow-sm p-4 mb-6">
|
||||
<q-input
|
||||
v-model="searchQuery"
|
||||
placeholder="ค้นหาชื่อคอร์ส, ผู้สอน..."
|
||||
outlined
|
||||
dense
|
||||
bg-color="grey-1"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
<template v-slot:append v-if="searchQuery">
|
||||
<q-icon name="close" class="cursor-pointer" @click="searchQuery = ''" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="flex gap-4 items-center">
|
||||
<div class="flex-1">
|
||||
<q-input
|
||||
v-model="searchQuery"
|
||||
placeholder="ค้นหาชื่อคอร์ส, ผู้สอน..."
|
||||
outlined
|
||||
dense
|
||||
bg-color="grey-1"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="search" />
|
||||
</template>
|
||||
<template v-slot:append v-if="searchQuery">
|
||||
<q-icon name="close" class="cursor-pointer" @click="searchQuery = ''" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mb-6">
|
||||
<q-btn-toggle
|
||||
<q-btn-toggle
|
||||
v-model="viewMode"
|
||||
toggle-color="primary"
|
||||
:options="[
|
||||
{ label: 'การ์ด', value: 'card' },
|
||||
{ label: 'ตาราง', value: 'table' }
|
||||
{ value: 'card', slot: 'card' },
|
||||
{ value: 'table', slot: 'table' }
|
||||
]"
|
||||
/>
|
||||
dense
|
||||
rounded
|
||||
unelevated
|
||||
class="border"
|
||||
>
|
||||
<template v-slot:card>
|
||||
<q-icon name="view_stream" size="20px" />
|
||||
<q-tooltip>มุมมองการ์ด</q-tooltip>
|
||||
</template>
|
||||
<template v-slot:table>
|
||||
<q-icon name="view_list" size="20px" />
|
||||
<q-tooltip>มุมมองตาราง</q-tooltip>
|
||||
</template>
|
||||
</q-btn-toggle>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pending Courses List -->
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { useQuasar } from 'quasar';
|
||||
import { adminService, type AdminUserResponse } from '~/services/admin.service';
|
||||
import { adminService, type AdminUserResponse, type RoleResponse } from '~/services/admin.service';
|
||||
import { useAuthStore } from '~/stores/auth';
|
||||
|
||||
definePageMeta({
|
||||
|
|
@ -228,6 +228,7 @@ const $q = useQuasar();
|
|||
|
||||
// Data
|
||||
const users = ref<AdminUserResponse[]>([]);
|
||||
const roles = ref<RoleResponse[]>([]);
|
||||
const loading = ref(true);
|
||||
const searchQuery = ref('');
|
||||
const filterRole = ref<string | null>(null);
|
||||
|
|
@ -286,6 +287,14 @@ const filteredUsers = computed(() => {
|
|||
});
|
||||
|
||||
// Methods
|
||||
const fetchRoles = async () => {
|
||||
try {
|
||||
roles.value = await adminService.getRoles();
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch roles:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchUsers = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
|
|
@ -328,24 +337,32 @@ const viewUser = (user: AdminUserResponse) => {
|
|||
showViewModal.value = true;
|
||||
};
|
||||
|
||||
const changeRole = (user: AdminUserResponse) => {
|
||||
const roleIds: Record<string, number> = {
|
||||
INSTRUCTOR: 1,
|
||||
STUDENT: 2,
|
||||
ADMIN: 3
|
||||
const getRoleLabel = (code: string): string => {
|
||||
const labels: Record<string, string> = {
|
||||
INSTRUCTOR: 'Instructor',
|
||||
STUDENT: 'Student',
|
||||
ADMIN: 'Admin'
|
||||
};
|
||||
return labels[code] || code;
|
||||
};
|
||||
|
||||
const changeRole = (user: AdminUserResponse) => {
|
||||
// Find current role ID from fetched roles
|
||||
const currentRole = roles.value.find(r => r.code === user.role.code);
|
||||
|
||||
// Build items from API roles
|
||||
const roleItems = roles.value.map(r => ({
|
||||
label: getRoleLabel(r.code),
|
||||
value: r.id
|
||||
}));
|
||||
|
||||
$q.dialog({
|
||||
title: 'เปลี่ยน Role',
|
||||
message: `เลือก Role ใหม่สำหรับ ${user.profile.first_name}`,
|
||||
options: {
|
||||
type: 'radio',
|
||||
model: roleIds[user.role.code] as any,
|
||||
items: [
|
||||
{ label: 'Instructor', value: 1 },
|
||||
{ label: 'Student', value: 2 },
|
||||
{ label: 'Admin', value: 3 }
|
||||
]
|
||||
model: (currentRole?.id ?? 0) as any,
|
||||
items: roleItems
|
||||
},
|
||||
cancel: true,
|
||||
persistent: true
|
||||
|
|
@ -415,6 +432,7 @@ const exportExcel = () => {
|
|||
|
||||
// Lifecycle
|
||||
onMounted(() => {
|
||||
fetchRoles();
|
||||
fetchUsers();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue