feat: Implement initial frontend for admin and instructor roles, including dashboards, course management, authentication, and core services.
This commit is contained in:
parent
832a8f5067
commit
127b63de49
16 changed files with 1505 additions and 102 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { authService } from '~/services/auth.service';
|
||||
import { userService } from '~/services/user.service';
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
|
|
@ -123,6 +124,28 @@ export const useAuthStore = defineStore('auth', {
|
|||
this.logout();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async fetchUserProfile() {
|
||||
try {
|
||||
const response = await userService.getProfile();
|
||||
|
||||
// Update local user state
|
||||
this.user = {
|
||||
id: response.id.toString(),
|
||||
email: response.email,
|
||||
firstName: response.profile.first_name,
|
||||
lastName: response.profile.last_name,
|
||||
role: response.role.code as 'INSTRUCTOR' | 'ADMIN' | 'STUDENT',
|
||||
avatarUrl: response.profile.avatar_url
|
||||
};
|
||||
|
||||
// Update user cookie to keep it in sync
|
||||
const userCookie = useCookie('user');
|
||||
userCookie.value = JSON.stringify(this.user);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch user profile:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@ interface DashboardStats {
|
|||
completedStudents: number;
|
||||
}
|
||||
|
||||
interface CourseStatusCounts {
|
||||
approved: number;
|
||||
pending: number;
|
||||
draft: number;
|
||||
rejected: number;
|
||||
}
|
||||
|
||||
export const useInstructorStore = defineStore('instructor', {
|
||||
state: () => ({
|
||||
stats: {
|
||||
|
|
@ -24,6 +31,13 @@ export const useInstructorStore = defineStore('instructor', {
|
|||
completedStudents: 0
|
||||
} as DashboardStats,
|
||||
|
||||
courseStatusCounts: {
|
||||
approved: 0,
|
||||
pending: 0,
|
||||
draft: 0,
|
||||
rejected: 0
|
||||
} as CourseStatusCounts,
|
||||
|
||||
recentCourses: [] as Course[],
|
||||
loading: false
|
||||
}),
|
||||
|
|
@ -40,21 +54,64 @@ export const useInstructorStore = defineStore('instructor', {
|
|||
// Fetch real courses from API
|
||||
const courses = await instructorService.getCourses();
|
||||
|
||||
// Fetch student counts for each course
|
||||
let totalStudents = 0;
|
||||
let completedStudents = 0;
|
||||
const courseDetails: Course[] = [];
|
||||
|
||||
for (const course of courses.slice(0, 5)) {
|
||||
try {
|
||||
// Get student counts
|
||||
const studentsResponse = await instructorService.getEnrolledStudents(course.id, 1, 1);
|
||||
const courseStudents = studentsResponse.total || 0;
|
||||
totalStudents += courseStudents;
|
||||
|
||||
// Get completed count from full list (if small) or estimate
|
||||
if (courseStudents > 0 && courseStudents <= 100) {
|
||||
const allStudents = await instructorService.getEnrolledStudents(course.id, 1, 100);
|
||||
completedStudents += allStudents.data.filter(s => s.status === 'COMPLETED').length;
|
||||
}
|
||||
|
||||
// Get lesson count from course detail
|
||||
const courseDetail = await instructorService.getCourseById(course.id);
|
||||
const lessonCount = courseDetail.chapters.reduce((sum, ch) => sum + ch.lessons.length, 0);
|
||||
|
||||
courseDetails.push({
|
||||
id: course.id,
|
||||
title: course.title.th,
|
||||
students: courseStudents,
|
||||
lessons: lessonCount,
|
||||
icon: 'book',
|
||||
thumbnail: course.thumbnail_url || null
|
||||
});
|
||||
} catch (e) {
|
||||
// Course might not have students endpoint
|
||||
courseDetails.push({
|
||||
id: course.id,
|
||||
title: course.title.th,
|
||||
students: 0,
|
||||
lessons: 0,
|
||||
icon: 'book',
|
||||
thumbnail: course.thumbnail_url || null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update stats
|
||||
this.stats.totalCourses = courses.length;
|
||||
// TODO: Get real student counts from API when available
|
||||
this.stats.totalStudents = 0;
|
||||
this.stats.completedStudents = 0;
|
||||
this.stats.totalStudents = totalStudents;
|
||||
this.stats.completedStudents = completedStudents;
|
||||
|
||||
// Map to recent courses format (take first 5)
|
||||
this.recentCourses = courses.slice(0, 3).map((course, index) => ({
|
||||
id: course.id,
|
||||
title: course.title.th,
|
||||
students: 0, // TODO: Get from API
|
||||
lessons: 0, // TODO: Get from course detail API
|
||||
icon: 'book',
|
||||
thumbnail: course.thumbnail_url || null
|
||||
}));
|
||||
// Update course status counts
|
||||
this.courseStatusCounts = {
|
||||
approved: courses.filter(c => c.status === 'APPROVED').length,
|
||||
pending: courses.filter(c => c.status === 'PENDING').length,
|
||||
draft: courses.filter(c => c.status === 'DRAFT').length,
|
||||
rejected: courses.filter(c => c.status === 'REJECTED').length
|
||||
};
|
||||
|
||||
// Update recent courses (first 3)
|
||||
this.recentCourses = courseDetails.slice(0, 3);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch dashboard data:', error);
|
||||
} finally {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue