All checks were successful
Build and Deploy Frontend Management to Dev Server / Build Frontend Management Docker Image (push) Successful in 34s
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
235 lines
6.6 KiB
Vue
235 lines
6.6 KiB
Vue
<template>
|
|
<div>
|
|
<!-- Header -->
|
|
<div class="flex items-center gap-4 mb-6">
|
|
<q-btn
|
|
flat
|
|
round
|
|
icon="arrow_back"
|
|
@click="navigateTo('/instructor/courses')"
|
|
/>
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-primary-600">สร้างหลักสูตรใหม่</h1>
|
|
<p class="text-gray-600 mt-1">กรอกข้อมูลหลักสูตรของคุณ</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form -->
|
|
<div class="bg-white rounded-xl shadow-sm p-6">
|
|
<q-form @submit="handleSubmit">
|
|
<!-- Basic Info -->
|
|
<h2 class="text-lg font-semibold text-primary-600 mb-4">ข้อมูลพื้นฐาน</h2>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
|
<q-input
|
|
v-model="form.title.th"
|
|
label="ชื่อหลักสูตร (ภาษาไทย) *"
|
|
outlined
|
|
:rules="[val => !!val || 'กรุณากรอกชื่อหลักสูตร']"
|
|
lazy-rules="ondemand"
|
|
hide-bottom-space
|
|
/>
|
|
<q-input
|
|
v-model="form.title.en"
|
|
label="ชื่อหลักสูตร (English) *"
|
|
outlined
|
|
:rules="[val => !!val || 'Please enter course title']"
|
|
lazy-rules="ondemand"
|
|
hide-bottom-space
|
|
/>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
|
<q-input
|
|
v-model="form.slug"
|
|
label="Slug (URL) *"
|
|
outlined
|
|
hint="ใช้สำหรับ URL เช่น javascript-basics"
|
|
:rules="[val => !!val || 'กรุณากรอก slug']"
|
|
lazy-rules="ondemand"
|
|
hide-bottom-space
|
|
/>
|
|
<q-select
|
|
v-model="form.category_id"
|
|
:options="categoryOptions"
|
|
label="หมวดหมู่ *"
|
|
outlined
|
|
emit-value
|
|
map-options
|
|
:rules="[val => !!val || 'กรุณาเลือกหมวดหมู่']"
|
|
/>
|
|
</div>
|
|
|
|
<div class="mb-6">
|
|
<q-input
|
|
v-model="form.description.th"
|
|
label="คำอธิบาย (ภาษาไทย) *"
|
|
type="textarea"
|
|
outlined
|
|
autogrow
|
|
rows="3"
|
|
:rules="[val => !!val || 'กรุณากรอกคำอธิบาย']"
|
|
lazy-rules="ondemand"
|
|
hide-bottom-space
|
|
/>
|
|
</div>
|
|
|
|
<div class="mb-6">
|
|
<q-input
|
|
v-model="form.description.en"
|
|
label="คำอธิบาย (English) *"
|
|
type="textarea"
|
|
outlined
|
|
autogrow
|
|
rows="3"
|
|
:rules="[val => !!val || 'Please enter description']"
|
|
lazy-rules="ondemand"
|
|
hide-bottom-space
|
|
/>
|
|
</div>
|
|
|
|
<!-- Pricing -->
|
|
<q-separator class="my-6" />
|
|
<h2 class="text-lg font-semibold text-primary-600 mb-4">ราคาและการตั้งค่า</h2>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
|
<q-toggle
|
|
v-model="form.is_free"
|
|
label="หลักสูตรฟรี"
|
|
color="primary"
|
|
/>
|
|
<q-input
|
|
v-if="!form.is_free"
|
|
v-model.number="form.price"
|
|
label="ราคา (บาท)"
|
|
type="number"
|
|
outlined
|
|
prefix="฿"
|
|
:rules="[val => form.is_free || val > 0 || 'กรุณากรอกราคา']"
|
|
lazy-rules="ondemand"
|
|
hide-bottom-space
|
|
/>
|
|
<q-toggle
|
|
v-model="form.have_certificate"
|
|
label="มีใบประกาศนียบัตร"
|
|
color="primary"
|
|
/>
|
|
</div>
|
|
|
|
|
|
<!-- Actions -->
|
|
<div class="flex justify-end gap-3 mt-8">
|
|
<q-btn
|
|
flat
|
|
label="ยกเลิก"
|
|
color="grey-7"
|
|
@click="navigateTo('/instructor/courses')"
|
|
/>
|
|
<q-btn
|
|
type="submit"
|
|
label="สร้างหลักสูตร"
|
|
color="primary"
|
|
:loading="saving"
|
|
/>
|
|
</div>
|
|
</q-form>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { useQuasar } from 'quasar';
|
|
import { instructorService, type CreateCourseRequest } from '~/services/instructor.service';
|
|
import { adminService, type CategoryResponse } from '~/services/admin.service';
|
|
|
|
definePageMeta({
|
|
layout: 'instructor',
|
|
middleware: ['auth']
|
|
});
|
|
|
|
const $q = useQuasar();
|
|
const router = useRouter();
|
|
|
|
// Data
|
|
const saving = ref(false);
|
|
const categories = ref<CategoryResponse[]>([]);
|
|
|
|
// Form
|
|
const form = ref<CreateCourseRequest>({
|
|
category_id: 1,
|
|
title: { th: '', en: '' },
|
|
slug: '',
|
|
description: { th: '', en: '' },
|
|
price: 0,
|
|
is_free: true,
|
|
have_certificate: true
|
|
});
|
|
|
|
// Category options
|
|
const categoryOptions = computed(() =>
|
|
categories.value.map(cat => ({
|
|
label: cat.name.th,
|
|
value: cat.id
|
|
}))
|
|
);
|
|
|
|
// Auto-generate slug from Thai title
|
|
watch(() => form.value.title.th, (newTitle) => {
|
|
if (newTitle && !form.value.slug) {
|
|
// Simple slug generation - replace spaces with dashes
|
|
form.value.slug = newTitle
|
|
.toLowerCase()
|
|
.replace(/\s+/g, '-')
|
|
.replace(/[^\u0E00-\u0E7Fa-z0-9-]/g, '');
|
|
}
|
|
});
|
|
|
|
// Methods
|
|
const fetchCategories = async () => {
|
|
try {
|
|
categories.value = await adminService.getCategories();
|
|
} catch (error) {
|
|
console.error('Failed to fetch categories');
|
|
}
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
saving.value = true;
|
|
try {
|
|
// Set price to 0 if free
|
|
if (form.value.is_free) {
|
|
form.value.price = 0;
|
|
}
|
|
|
|
const response = await instructorService.createCourse(form.value);
|
|
|
|
|
|
$q.notify({
|
|
type: 'positive',
|
|
message: response.message,
|
|
position: 'top'
|
|
});
|
|
|
|
// Redirect to course edit page
|
|
// Note: Assuming response.data contains the created course with ID
|
|
if (response.data && response.data.id) {
|
|
await navigateTo(`/instructor/courses/${response.data.id}`);
|
|
} else {
|
|
await navigateTo('/instructor/courses');
|
|
}
|
|
} catch (error: any) {
|
|
$q.notify({
|
|
type: 'negative',
|
|
message: error.data?.error?.message || error.data?.message || 'เกิดข้อผิดพลาด กรุณาลองใหม่',
|
|
position: 'top'
|
|
});
|
|
} finally {
|
|
saving.value = false;
|
|
}
|
|
};
|
|
|
|
// Lifecycle
|
|
onMounted(() => {
|
|
fetchCategories();
|
|
});
|
|
</script>
|