Add tests result
|
|
@ -343,10 +343,12 @@ const save = async () => {
|
|||
saving.value = true;
|
||||
try {
|
||||
// Convert local datetime to ISO string to preserve timezone
|
||||
const payload = { ...form.value };
|
||||
const payload: any = { ...form.value };
|
||||
if (payload.published_at) {
|
||||
const localDate = new Date(payload.published_at.replace(' ', 'T'));
|
||||
payload.published_at = localDate.toISOString();
|
||||
} else {
|
||||
delete payload.published_at;
|
||||
}
|
||||
|
||||
if (editing.value) {
|
||||
|
|
|
|||
|
|
@ -34,11 +34,11 @@ export default defineConfig({
|
|||
use: {
|
||||
baseURL: 'http://localhost:3000/',// ปรับเป็น URL ที่ทดสอบ
|
||||
headless: false, // false = เห็น browser ขณะรัน
|
||||
screenshot: 'only-on-failure', // เก็บ screenshot เมื่อ fail
|
||||
screenshot: 'on', // เก็บ screenshot
|
||||
trace: 'retain-on-failure', // เก็บ trace เพื่อดีบักเมื่อ fail
|
||||
// launchOptions: {
|
||||
// slowMo: 1000,
|
||||
// }, // ช้าลง 10 วินาที
|
||||
launchOptions: {
|
||||
slowMo: 500,
|
||||
}, // ช้าลง 10 วินาที
|
||||
},
|
||||
|
||||
/* ──── Setup Projects: login ครั้งเดียว แล้ว save cookies ──── */
|
||||
|
|
|
|||
|
|
@ -610,6 +610,19 @@ export const instructorService = {
|
|||
{ method: 'DELETE' }
|
||||
);
|
||||
},
|
||||
async getMyStudentsStats(): Promise<{ total_students: number; total_completed: number }> {
|
||||
const response = await authRequest<{
|
||||
code: number;
|
||||
message: string;
|
||||
total_students: number;
|
||||
total_completed: number;
|
||||
}>('/api/instructors/courses/my-students');
|
||||
return {
|
||||
total_students: response.total_students,
|
||||
total_completed: response.total_completed
|
||||
};
|
||||
},
|
||||
|
||||
async getCourseApprovalHistory(courseId: number): Promise<ApprovalHistory[]> {
|
||||
const response = await authRequest<{
|
||||
code: number;
|
||||
|
|
|
|||
|
|
@ -51,56 +51,16 @@ export const useInstructorStore = defineStore('instructor', {
|
|||
async fetchDashboardData() {
|
||||
this.loading = true;
|
||||
try {
|
||||
// Fetch real courses from API
|
||||
const courses = await instructorService.getCourses();
|
||||
// Fetch courses and student stats in parallel
|
||||
const [courses, studentStats] = await Promise.all([
|
||||
instructorService.getCourses(),
|
||||
instructorService.getMyStudentsStats()
|
||||
]);
|
||||
|
||||
// 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
|
||||
// Update student stats from dedicated API
|
||||
this.stats.totalCourses = courses.length;
|
||||
this.stats.totalStudents = totalStudents;
|
||||
this.stats.completedStudents = completedStudents;
|
||||
this.stats.totalStudents = studentStats.total_students;
|
||||
this.stats.completedStudents = studentStats.total_completed;
|
||||
|
||||
// Update course status counts
|
||||
this.courseStatusCounts = {
|
||||
|
|
@ -110,8 +70,15 @@ export const useInstructorStore = defineStore('instructor', {
|
|||
rejected: courses.filter(c => c.status === 'REJECTED').length
|
||||
};
|
||||
|
||||
// Update recent courses (first 3)
|
||||
this.recentCourses = courseDetails.slice(0, 3);
|
||||
// Build recent courses list (first 3) from existing data
|
||||
this.recentCourses = courses.slice(0, 3).map(course => ({
|
||||
id: course.id,
|
||||
title: course.title.th,
|
||||
students: 0,
|
||||
lessons: 0,
|
||||
icon: 'book',
|
||||
thumbnail: course.thumbnail_url || null
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch dashboard data:', error);
|
||||
} finally {
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 145 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 201 KiB |
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 150 KiB |