feat: Implement initial e-learning platform frontend structure including dashboard, course management, authentication, and common UI components.
This commit is contained in:
parent
aceeb80d9a
commit
ad11c6b7c5
44 changed files with 720 additions and 578 deletions
|
|
@ -67,22 +67,22 @@ const showPassword = reactive({
|
|||
})
|
||||
|
||||
|
||||
// Rules have been moved to components
|
||||
// กฎต่างๆ ถูกย้ายไปที่คอมโพเนนต์แล้ว (Rules have been moved to components)
|
||||
|
||||
const fileInput = ref<HTMLInputElement | null>(null) // Used in view mode (outside component)
|
||||
const fileInput = ref<HTMLInputElement | null>(null) // ใช้ในโหมดมุมมอง (อยู่นอกคอมโพเนนต์) (Used in view mode (outside component))
|
||||
|
||||
const toggleEdit = (edit: boolean) => {
|
||||
isEditing.value = edit
|
||||
}
|
||||
|
||||
// Updated to accept File object directly (or Event for view mode compatibility if needed)
|
||||
// อัปเดตให้รับออบเจ็กต์ File ได้โดยตรง (หรือ Event สำหรับความเข้ากันได้ของโหมดมุมมองหากจำเป็น) (Updated to accept File object directly (or Event for view mode compatibility if needed))
|
||||
const handleFileUpload = async (fileOrEvent: File | Event) => {
|
||||
let file: File | null = null
|
||||
|
||||
if (fileOrEvent instanceof File) {
|
||||
file = fileOrEvent
|
||||
} else {
|
||||
// Fallback for native input change event
|
||||
// การทำงานสำรองสำหรับอีเวนต์ input change แบบเนทีฟ (Fallback for native input change event)
|
||||
const target = (fileOrEvent as Event).target as HTMLInputElement
|
||||
if (target.files && target.files[0]) {
|
||||
file = target.files[0]
|
||||
|
|
@ -112,7 +112,7 @@ const handleFileUpload = async (fileOrEvent: File | Event) => {
|
|||
}
|
||||
}
|
||||
|
||||
// Trigger upload for VIEW mode avatar click
|
||||
// เรียกการอัปโหลดเมื่อคลิกที่รูปโปรไฟล์ในโหมด View (Trigger upload for VIEW mode avatar click)
|
||||
const triggerUpload = () => {
|
||||
fileInput.value?.click()
|
||||
}
|
||||
|
|
@ -191,7 +191,7 @@ const handleUpdatePassword = async () => {
|
|||
isPasswordSaving.value = false
|
||||
}
|
||||
|
||||
// Watch for changes in global user state (e.g. after avatar upload or profile update)
|
||||
// เฝ้าดูการเปลี่ยนแปลงในสถานะผู้ใช้ส่วนกลาง (เช่น หลังจากอัปโหลดรูปโปรไฟล์หรืออัปเดตข้อมูลส่วนตัว) (Watch for changes in global user state (e.g. after avatar upload or profile update))
|
||||
watch(() => currentUser.value, (newUser) => {
|
||||
if (newUser) {
|
||||
userData.value.photoURL = newUser.photoURL || ''
|
||||
|
|
@ -220,7 +220,7 @@ onMounted(async () => {
|
|||
<q-spinner size="3rem" color="primary" />
|
||||
</div>
|
||||
|
||||
<!-- MAIN PROFILE SETTINGS -->
|
||||
<!-- การตั้งค่าโปรไฟล์หลัก (MAIN PROFILE SETTINGS) -->
|
||||
<div v-else class="max-w-5xl mx-auto pb-20 fade-in pt-4">
|
||||
|
||||
<!-- บัตรข้อมูลโปรไฟล์ (Profile Card) -->
|
||||
|
|
@ -255,7 +255,7 @@ onMounted(async () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Inputs (2 Column Grid) -->
|
||||
<!-- ฟิลด์ข้อมูลฟอร์ม (แบ่งเป็น 2 คอลัมน์) (Form Inputs (2 Column Grid)) -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6 mb-4">
|
||||
<div class="md:col-span-2 relative">
|
||||
<label class="block text-[13px] font-bold text-slate-700 dark:text-slate-300 mb-2">{{ $t('profile.prefix') }}</label>
|
||||
|
|
@ -300,7 +300,7 @@ onMounted(async () => {
|
|||
|
||||
</div>
|
||||
|
||||
<!-- Footer Buttons -->
|
||||
<!-- ปุ่มกดยืนยันต่างๆ (Footer Buttons) -->
|
||||
<div class="px-6 sm:px-8 py-5 border-t border-slate-200 dark:border-slate-800 flex flex-col sm:flex-row justify-center sm:justify-end gap-3 items-center bg-white dark:!bg-slate-900">
|
||||
<button class="w-full sm:w-auto text-[13px] font-bold text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white px-4 py-2 transition order-2 sm:order-1" @click="fetchUserProfile(true)">{{ $t('common.cancel') }}</button>
|
||||
<button @click="handleUpdateProfile" :disabled="isProfileSaving" class="w-full sm:w-auto bg-[#3B6BE8] hover:bg-blue-700 text-white px-6 py-2.5 rounded-lg text-[13px] font-bold transition shadow-sm disabled:opacity-50 order-1 sm:order-2">
|
||||
|
|
@ -310,7 +310,7 @@ onMounted(async () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Security Card -->
|
||||
<!-- การ์ดความปลอดภัย (Security Card) -->
|
||||
<div class="bg-white dark:!bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-2xl shadow-sm overflow-hidden">
|
||||
<div class="p-8 border-b border-slate-200 dark:border-slate-800">
|
||||
<h2 class="text-xl font-bold text-slate-900 dark:text-white">{{ $t('profile.security') }}</h2>
|
||||
|
|
@ -337,7 +337,7 @@ onMounted(async () => {
|
|||
|
||||
</div>
|
||||
|
||||
<!-- Password Modal -->
|
||||
<!-- โมดอลเปลี่ยนรหัสผ่าน (Password Modal) -->
|
||||
<q-dialog v-model="showPasswordModal">
|
||||
<q-card class="w-full max-w-md rounded-2xl p-2 dark:bg-slate-900 shadow-xl">
|
||||
<q-form @submit="handleUpdatePassword">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue