feat: Implement initial frontend for admin and instructor roles, including dashboards, course management, authentication, and core services.

This commit is contained in:
Missez 2026-02-06 14:58:41 +07:00
parent 832a8f5067
commit 127b63de49
16 changed files with 1505 additions and 102 deletions

View file

@ -11,7 +11,8 @@
<div class="flex flex-col md:flex-row gap-6">
<!-- Thumbnail -->
<div
class="w-full md:w-48 h-32 bg-gray-100 rounded-lg flex items-center justify-center flex-shrink-0 relative group cursor-pointer overflow-hidden border border-gray-200"
class="bg-gray-100 rounded-lg flex items-center justify-center flex-shrink-0 relative group cursor-pointer overflow-hidden border border-gray-200"
style="width: 192px; height: 128px;"
@click="triggerThumbnailUpload"
>
<img
@ -73,7 +74,7 @@
</div>
<div class="flex items-center gap-1">
<q-icon name="people" size="20px" />
<span>0 เรยน</span>
<span>{{ totalStudentsCount }} เรยน</span>
</div>
</div>
</div>
@ -166,6 +167,7 @@ const course = ref<CourseDetailResponse | null>(null);
const activeTab = ref('structure');
const uploadingThumbnail = ref(false);
const thumbnailInputRef = ref<HTMLInputElement | null>(null);
const totalStudentsCount = ref(0);
// Computed
const totalLessons = computed(() => {
@ -178,7 +180,13 @@ const fetchCourse = async () => {
loading.value = true;
try {
const courseId = parseInt(route.params.id as string);
course.value = await instructorService.getCourseById(courseId);
const [courseData, studentsData] = await Promise.all([
instructorService.getCourseById(courseId),
instructorService.getEnrolledStudents(courseId, 1, 1)
]);
course.value = courseData;
totalStudentsCount.value = studentsData.total;
} catch (error) {
console.error('Failed to fetch course:', error);
$q.notify({ type: 'negative', message: 'ไม่สามารถโหลดข้อมูลหลักสูตรได้', position: 'top' });
@ -187,6 +195,7 @@ const fetchCourse = async () => {
}
};
const getStatusColor = (status: string) => {
const colors: Record<string, string> = {
APPROVED: 'green',
@ -238,8 +247,8 @@ const handleThumbnailUpload = async (event: Event) => {
const requestApproval = async () => {
if (!course.value) return;
try {
await instructorService.submitCourseForApproval(course.value.id);
$q.notify({ type: 'positive', message: 'ส่งขออนุมัติสำเร็จ', position: 'top' });
const response = await instructorService.submitCourseForApproval(course.value.id);
$q.notify({ type: 'positive', message: response.message || 'ส่งขออนุมัติสำเร็จ', position: 'top' });
fetchCourse();
} catch (error: any) {
$q.notify({

View file

@ -86,12 +86,39 @@
<!-- Chart and Recent Courses -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Chart Placeholder -->
<!-- Course Status Breakdown -->
<q-card>
<q-card-section>
<h3 class="text-xl font-semibold mb-4"> สถสมครรวม (รายเดอน)</h3>
<div class="bg-gray-100 rounded-lg p-12 text-center text-gray-500">
[กราฟแสดงสถสมครรวม (รายเดอน)]
<h3 class="text-xl font-semibold mb-4">สถานะหลกสตร</h3>
<div class="space-y-3">
<div class="flex items-center justify-between p-3 bg-green-50 rounded-lg">
<div class="flex items-center gap-3">
<q-icon name="check_circle" color="green" size="24px" />
<span class="font-medium text-gray-700">เผยแพรแล</span>
</div>
<span class="text-2xl font-bold text-green-600">{{ instructorStore.courseStatusCounts.approved }}</span>
</div>
<div class="flex items-center justify-between p-3 bg-orange-50 rounded-lg">
<div class="flex items-center gap-3">
<q-icon name="hourglass_empty" color="orange" size="24px" />
<span class="font-medium text-gray-700">รอตรวจสอบ</span>
</div>
<span class="text-2xl font-bold text-orange-600">{{ instructorStore.courseStatusCounts.pending }}</span>
</div>
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<div class="flex items-center gap-3">
<q-icon name="edit_note" color="grey" size="24px" />
<span class="font-medium text-gray-700">แบบราง</span>
</div>
<span class="text-2xl font-bold text-gray-600">{{ instructorStore.courseStatusCounts.draft }}</span>
</div>
<div v-if="instructorStore.courseStatusCounts.rejected > 0" class="flex items-center justify-between p-3 bg-red-50 rounded-lg">
<div class="flex items-center gap-3">
<q-icon name="cancel" color="red" size="24px" />
<span class="font-medium text-gray-700">กปฏเสธ</span>
</div>
<span class="text-2xl font-bold text-red-600">{{ instructorStore.courseStatusCounts.rejected }}</span>
</div>
</div>
</q-card-section>
</q-card>
@ -170,5 +197,6 @@ const handleLogout = () => {
// Fetch dashboard data on mount
onMounted(() => {
instructorStore.fetchDashboardData();
authStore.fetchUserProfile();
});
</script>