feat: Implement core user profile, dashboard, course browsing pages, common components, and internationalization.

This commit is contained in:
supalerk-ar66 2026-01-19 16:43:05 +07:00
parent 01978f9438
commit 11d714c632
10 changed files with 289 additions and 99 deletions

View file

@ -73,7 +73,7 @@ const categories = [
"ถ่ายภาพ & วิดีโอ",
"ภาษา",
"Lifestyles",
"คอร์สฟรี",
];
// Category Visibility State
@ -114,7 +114,7 @@ const filteredCourses = computed(() => {
style="flex-wrap: wrap; gap: 16px"
>
<h1 class="text-[28px] font-bold text-slate-900 dark:text-white">
รายการคอรสทงหมด
{{ $t('discovery.title') }}
</h1>
<div class="flex gap-3" style="flex-wrap: wrap">
<!-- Search Input -->
@ -123,7 +123,7 @@ const filteredCourses = computed(() => {
v-model="searchQuery"
type="text"
class="input-field text-slate-900 dark:text-white bg-white dark:bg-slate-800 placeholder:text-slate-500"
placeholder="ค้นหาคอร์ส..."
:placeholder="$t('discovery.searchPlaceholder')"
style="padding-left: 36px; width: 240px"
/>
</div>
@ -132,8 +132,8 @@ const filteredCourses = computed(() => {
class="input-field bg-white dark:bg-slate-800"
style="width: auto; color: #0f172a"
>
<option style="color: #0f172a">เรยงตาม: าส</option>
<option style="color: #0f172a">ยอดนยม</option>
<option style="color: #0f172a">{{ $t('discovery.sortRecent') }}</option>
<option style="color: #0f172a">{{ $t('discovery.sortPopular') }}</option>
</select>
</div>
</div>
@ -149,7 +149,7 @@ const filteredCourses = computed(() => {
@click="isCategoryOpen = !isCategoryOpen"
>
<h4 class="text-lg font-bold text-slate-900 dark:text-white">
หมวดหม ({{ categories.length }})
{{ $t('discovery.categoryTitle') }} ({{ categories.length }})
</h4>
<svg
xmlns="http://www.w3.org/2000/svg"
@ -188,7 +188,7 @@ const filteredCourses = computed(() => {
class="text-primary text-sm mt-4 font-medium hover:underline flex items-center gap-1"
@click="showAllCategories = !showAllCategories"
>
{{ showAllCategories ? "แสดงน้อยลง" : "แสดงเพิ่มเติม" }}
{{ showAllCategories ? $t('discovery.showLess') : $t('discovery.showMore') }}
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4 transition-transform duration-200"
@ -233,12 +233,12 @@ const filteredCourses = computed(() => {
class="empty-state"
style="grid-column: 1 / -1"
>
<h3 class="empty-state-title">ไมพบผลการคนหา</h3>
<h3 class="empty-state-title">{{ $t('discovery.emptyTitle') }}</h3>
<p class="empty-state-description">
ลองใชคำคนหาอ หรอตรวจดความถกตองของตวอกษรอกคร
{{ $t('discovery.emptyDesc') }}
</p>
<button class="btn btn-secondary" @click="searchQuery = ''">
แสดงทงหมด
{{ $t('discovery.showAll') }}
</button>
</div>
@ -252,7 +252,7 @@ const filteredCourses = computed(() => {
background: rgba(255, 255, 255, 0.05);
"
>
โหลดเพมเต
{{ $t('discovery.loadMore') }}
</button>
</div>
</div>
@ -265,7 +265,7 @@ const filteredCourses = computed(() => {
@click="showDetail = false"
class="btn btn-secondary mb-6 inline-flex items-center gap-2"
>
<span></span> กลบหนารายการคอร
<span></span> {{ $t('discovery.backToCatalog') }}
</button>
<div v-if="isLoadingDetail" class="flex justify-center py-20">
@ -339,7 +339,7 @@ const filteredCourses = computed(() => {
<!-- Learning Objectives -->
<div class="card mb-6">
<h3 class="font-bold mb-4 text-slate-900 dark:text-white">
งทณจะไดเรยนร
{{ $t('course.whatYouWillLearn') }}
</h3>
<ul
class="grid-12"
@ -367,7 +367,7 @@ const filteredCourses = computed(() => {
<!-- Course Syllabus / Outline -->
<div class="card">
<h3 class="font-bold mb-4 text-slate-900 dark:text-white">
เนอหาในคอร
{{ $t('course.courseContent') }}
</h3>
<!-- Chapter 1 -->
<div class="mb-4">
@ -376,10 +376,10 @@ const filteredCourses = computed(() => {
style="background: #f3f4f6; border: 1px solid #e5e7eb"
>
<span class="font-bold text-slate-900 dark:text-slate-900"
>01. บทนำ</span
>01. {{ $t('course.introduction') }}</span
>
<span class="text-sm text-slate-600 dark:text-slate-400"
>3 บทเรยน</span
>3 {{ $t('course.lessons') }}</span
>
</div>
<div style="padding-left: 16px">
@ -430,7 +430,7 @@ const filteredCourses = computed(() => {
class="btn btn-primary w-full mb-4 text-white"
style="height: 48px; font-size: 16px"
>
ลงทะเบยนเรยนทนท
{{ $t('course.enrollNow') }}
</NuxtLink>
<div class="text-sm text-slate-600 dark:text-slate-400 mb-4">
@ -445,8 +445,8 @@ const filteredCourses = computed(() => {
class="flex justify-between py-2 border-b"
style="border-color: var(--neutral-100)"
>
<span>ใบประกาศ</span>
<span class="font-bold"></span>
<span>{{ $t('course.certificate') }}</span>
<span class="font-bold">{{ $t('course.available') }}</span>
</div>
</div>
</div>

View file

@ -51,8 +51,8 @@ const recommendedCourses = [
<div class="welcome-section mb-10 overflow-hidden relative rounded-[2.5rem] p-10 md:p-14 text-white shadow-lg dark:shadow-2xl dark:shadow-blue-900/20 transition-all">
<div class="relative z-10 flex flex-col md:flex-row justify-between items-center gap-8">
<div>
<h1 class="text-4xl md:text-5xl font-black mb-3 slide-up tracking-tight text-white dark:text-white">นดอนรบกล, {{ currentUser?.firstName }}!</h1>
<p class="text-lg slide-up font-medium text-blue-100" style="animation-delay: 0.1s;">นนเปนวนทสำหรบการเรยนรงใหม มาเกบความรเพมกนเถอะ</p>
<h1 class="text-4xl md:text-5xl font-black mb-3 slide-up tracking-tight text-white dark:text-white">{{ $t('dashboard.welcomeTitle') }}, {{ currentUser?.firstName }}!</h1>
<p class="text-lg slide-up font-medium text-blue-100" style="animation-delay: 0.1s;">{{ $t('dashboard.welcomeSubtitle') }}</p>
</div>
<div class="stats-mini flex gap-6 slide-up" style="animation-delay: 0.2s;"/>
</div>
@ -68,9 +68,9 @@ const recommendedCourses = [
<div class="flex items-center justify-between mb-8">
<h2 class="text-2xl font-black flex items-center gap-3 tracking-tight text-slate-900 dark:text-white">
<span class="w-1.5 h-8 bg-blue-500 rounded-full shadow-[0_0_15px_rgba(59,130,246,0.5)]"/>
เรยนตอจากเด
{{ $t('menu.continueLearning') }}
</h2>
<NuxtLink to="/classroom/learning" class="text-sm font-black text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 transition-colors uppercase tracking-widest">เขาสบทเรยนเตมต </NuxtLink>
<NuxtLink to="/classroom/learning" class="text-sm font-black text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 transition-colors uppercase tracking-widest">{{ $t('menu.goToLesson') }} </NuxtLink>
</div>
<!-- Featured Current Course Card -->
@ -83,14 +83,14 @@ const recommendedCourses = [
</div>
<!-- Course Details & Progress -->
<div class="p-8 md:p-10 flex-1 flex flex-col justify-center" style="background-color: var(--bg-surface);">
<span class="text-[10px] font-black uppercase tracking-[0.2em] text-blue-700 dark:text-blue-400 mb-3">Currently Learning</span>
<span class="text-[10px] font-black uppercase tracking-[0.2em] text-blue-700 dark:text-blue-400 mb-3">{{ $t('course.currentlyLearning') }}</span>
<h3 class="text-3xl font-black mb-2 leading-tight text-slate-900 dark:text-white group-hover:text-blue-700 dark:group-hover:text-blue-400 transition-colors">{{ recentCourse.title }}</h3>
<p class="text-slate-700 dark:text-slate-400 text-base mb-8 font-medium">{{ recentCourse.lesson }}</p>
<!-- Progress Bar -->
<div class="mt-auto bg-slate-100 dark:bg-slate-900/50 p-6 rounded-3xl border border-slate-200 dark:border-white/5">
<div class="flex justify-between items-center mb-3">
<span class="text-xs font-black text-slate-800 dark:text-slate-500 uppercase tracking-widest">Progress</span>
<span class="text-xs font-black text-slate-800 dark:text-slate-500 uppercase tracking-widest">{{ $t('course.progress') }}</span>
<span class="text-sm font-black text-blue-700 dark:text-blue-400">{{ recentCourse.progress }}%</span>
</div>
<div class="h-2.5 w-full bg-slate-300 dark:bg-slate-700 rounded-full overflow-hidden shadow-inner">
@ -105,7 +105,7 @@ const recommendedCourses = [
<div class="mb-8">
<h2 class="text-2xl font-black flex items-center gap-3 tracking-tight text-slate-900 dark:text-white">
<span class="w-1.5 h-8 bg-emerald-500 rounded-full shadow-[0_0_15px_rgba(16,185,129,0.5)]"/>
คอรสเรยนแนะนำ
{{ $t('menu.recommendedCourses') }}
</h2>
</div>
@ -125,7 +125,7 @@ const recommendedCourses = [
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-slate-600 dark:text-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
{{ course.duration }}
</span>
<button class="text-[11px] font-black text-blue-700 dark:text-blue-500 uppercase tracking-widest hover:text-blue-800 dark:hover:text-blue-400 transition-colors">รายละเอยด</button>
<button class="text-[11px] font-black text-blue-700 dark:text-blue-500 uppercase tracking-widest hover:text-blue-800 dark:hover:text-blue-400 transition-colors">{{ $t('menu.viewDetails') }}</button>
</div>
</div>
</div>

View file

@ -71,7 +71,7 @@ const downloadCertificate = () => {
<div>
<!-- Page Header & Filters -->
<div class="flex justify-between items-center mb-6 mobile-stack">
<h1 class="text-[28px] font-bold text-slate-900 dark:text-white">คอรสของฉ</h1>
<h1 class="text-[28px] font-bold text-slate-900 dark:text-white">{{ $t('sidebar.myCourses') }}</h1>
<!-- Filter Tabs -->
<div class="flex gap-2" style="overflow-x: auto; padding-bottom: 4px; width: 100%; justify-content: flex-start;">
<button
@ -79,21 +79,21 @@ const downloadCertificate = () => {
style="white-space: nowrap;"
@click="filterCourses('all')"
>
งหมด
{{ $t('myCourses.filterAll') }}
</button>
<button
:class="activeFilter === 'progress' ? 'btn btn-primary' : 'btn btn-secondary'"
style="white-space: nowrap;"
@click="filterCourses('progress')"
>
กำลงเรยน
{{ $t('myCourses.filterProgress') }}
</button>
<button
:class="activeFilter === 'completed' ? 'btn btn-primary' : 'btn btn-secondary'"
style="white-space: nowrap;"
@click="filterCourses('completed')"
>
เรยนจบแล
{{ $t('myCourses.filterCompleted') }}
</button>
</div>
</div>
@ -123,9 +123,9 @@ const downloadCertificate = () => {
<!-- Empty State -->
<div v-if="filteredCourses.length === 0" class="empty-state">
<div class="empty-state-icon">📚</div>
<h3 class="empty-state-title">งไมคอรสในหมวดหม</h3>
<p class="empty-state-description">ณยงไมคอรสเรยนในสวนน ลองเลอกดคอรสทาสนใจในระบบของเรา</p>
<NuxtLink to="/browse/discovery" class="btn btn-primary">ไปทรายการคอร</NuxtLink>
<h3 class="empty-state-title">{{ $t('myCourses.emptyTitle') }}</h3>
<p class="empty-state-description">{{ $t('myCourses.emptyDesc') }}</p>
<NuxtLink to="/browse/discovery" class="btn btn-primary">{{ $t('myCourses.goToDiscovery') }}</NuxtLink>
</div>
<!-- MODAL: Enrollment Success -->
@ -137,11 +137,11 @@ const downloadCertificate = () => {
<div style="width: 64px; height: 64px; background: var(--success); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32px; margin: 0 auto 24px;">
</div>
<h2 class="font-bold mb-2">ลงทะเบยนสำเร!</h2>
<p class="text-muted mb-6">ณไดลงทะเบยนคอร <strong>เบองตนการออกแบบ UX/UI</strong> เรยบรอยแล</p>
<h2 class="font-bold mb-2">{{ $t('enrollment.successTitle') }}</h2>
<p class="text-muted mb-6">{{ $t('enrollment.successDesc') }}</p>
<div class="flex flex-col gap-2">
<NuxtLink to="/classroom/learning" class="btn btn-primary w-full">เรมเรยนทนท</NuxtLink>
<button class="btn btn-secondary w-full" @click="showEnrollModal = false">ไวหล</button>
<NuxtLink to="/classroom/learning" class="btn btn-primary w-full">{{ $t('enrollment.startNow') }}</NuxtLink>
<button class="btn btn-secondary w-full" @click="showEnrollModal = false">{{ $t('enrollment.later') }}</button>
</div>
</div>
</div>
@ -156,14 +156,14 @@ const downloadCertificate = () => {
<button style="position: absolute; top: 15px; right: 20px; border: none; background: none; font-size: 32px; cursor: pointer; color: #1E293B; z-index: 10;" @click="showCertModal = false">&times;</button>
<div class="cert-inner">
<h1 class="cert-title">ใบประกาศนยบตรจบหลกสตร</h1>
<h1 class="cert-title">{{ $t('certificate.title') }}</h1>
<div style="width: 100px; height: 2px; background: #D4AF37; margin: 0 auto 24px;"/>
<p style="color: #64748B; margin-bottom: 16px; font-size: 16px;">ขอมอบใบประกาศนเพอแสดงว</p>
<p style="color: #64748B; margin-bottom: 16px; font-size: 16px;">{{ $t('certificate.presentedTo') }}</p>
<h2 class="cert-name">สมชาย ใจด</h2>
<h2 class="cert-name">{{ $t('userMenu.home') === 'Home' ? 'Somchai Jaidee' : 'สมชาย ใจดี' }}</h2>
<p style="color: #64748B; margin-bottom: 16px; font-size: 16px;">ไดานการอบรมและทดสอบความรในหลกสตร</p>
<p style="color: #64748B; margin-bottom: 16px; font-size: 16px;">{{ $t('certificate.completedDesc') }}</p>
<h3 style="font-size: 24px; font-weight: 700; color: #3B82F6; margin-bottom: 30px;">HTML5 นฐาน</h3>
@ -171,25 +171,25 @@ const downloadCertificate = () => {
<div class="cert-footer">
<div style="text-align: center;">
<div style="width: 150px; border-bottom: 1px solid #1E293B; margin-bottom: 8px; padding-bottom: 8px; font-style: italic; margin-left: auto; margin-right: auto;">Somchai K.</div>
<div style="font-size: 12px; color: #64748B;">ลายเซนผอำนวยการ</div>
<div style="font-size: 12px; color: #64748B;">{{ $t('certificate.directorSignature') }}</div>
</div>
<!-- Golden Seal -->
<div style="width: 80px; height: 80px; background: #D4AF37; border-radius: 50%; display: flex; flex-direction: column; align-items: center; justify-content: center; color: white; border: 4px double white; box-shadow: 0 0 0 4px #D4AF37; transform: rotate(-5deg); flex-shrink: 0;">
<div style="font-size: 10px; font-weight: bold;">Certified</div>
<div style="font-size: 16px; font-weight: 900;">าน</div>
<div style="font-size: 16px; font-weight: 900;">{{ $t('certificate.passed') }}</div>
</div>
<div style="text-align: center;">
<div style="width: 150px; border-bottom: 1px solid #1E293B; margin-bottom: 8px; padding-bottom: 8px; margin-left: auto; margin-right: auto;">15 นวาคม 2024</div>
<div style="font-size: 12px; color: #64748B;">นทออกใบประกาศ</div>
<div style="width: 150px; border-bottom: 1px solid #1E293B; margin-bottom: 8px; padding-bottom: 8px; margin-left: auto; margin-right: auto;">15/12/2026</div>
<div style="font-size: 12px; color: #64748B;">{{ $t('certificate.issueDate') }}</div>
</div>
</div>
<!-- Download Button -->
<div style="margin-top: 32px; text-align: center;">
<button class="btn btn-primary" @click="downloadCertificate">
ดาวนโหลด PDF
{{ $t('certificate.downloadPDF') }}
</button>
</div>
</div>

View file

@ -163,13 +163,13 @@ const saveProfile = async () => {
<div class="profile-page max-w-4xl mx-auto px-4 py-8">
<!-- Header: Title and Edit action -->
<div class="flex items-center justify-between mb-10">
<h1 class="text-3xl font-black text-slate-900 dark:text-white">โปรไฟลของฉ</h1>
<h1 class="text-3xl font-black text-slate-900 dark:text-white">{{ $t('profile.myProfile') }}</h1>
<div class="flex items-center gap-6">
<button v-if="!isEditing" class="btn-premium-edit" @click="toggleEdit(true)">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
</svg>
แกไขโปรไฟล
{{ $t('profile.editProfile') }}
</button>
</div>
</div>
@ -200,15 +200,15 @@ const saveProfile = async () => {
<!-- Info Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<div class="info-group">
<span class="label">เมล</span>
<span class="label">{{ $t('profile.email') }}</span>
<p class="value">{{ userData.email }}</p>
</div>
<div class="info-group">
<span class="label">เบอรโทรศพท</span>
<span class="label">{{ $t('profile.phone') }}</span>
<p class="value">{{ userData.phone }}</p>
</div>
<div class="info-group">
<span class="label">สมครสมาชกเม</span>
<span class="label">{{ $t('profile.joinedAt') }}</span>
<p class="value">{{ userData.createdAt }}</p>
</div>
</div>
@ -224,7 +224,7 @@ const saveProfile = async () => {
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
</button>
<h2 class="text-2xl font-black text-slate-900 dark:text-white">แกไขขอมลสวนต</h2>
<h2 class="text-2xl font-black text-slate-900 dark:text-white">{{ $t('profile.editPersonalDesc') }}</h2>
</div>
<!-- Avatar Upload Section -->
@ -246,16 +246,16 @@ const saveProfile = async () => {
<input ref="fileInput" type="file" class="hidden" accept="image/*" @change="handleFileUpload" >
</div>
<div class="text-center md:text-left">
<h3 class="font-black text-slate-900 dark:text-white mb-2">ปโปรไฟลของค</h3>
<p class="text-xs text-slate-500 mb-4 uppercase tracking-widest font-bold">เฉพาะไฟล png , jpg</p>
<button class="btn-upload" @click="triggerUpload">อัปโหลดรูปใหม่</button>
<h3 class="font-black text-slate-900 dark:text-white mb-2">{{ $t('profile.yourAvatar') }}</h3>
<p class="text-xs text-slate-500 mb-4 uppercase tracking-widest font-bold">{{ $t('profile.avatarHint') }}</p>
<button class="btn-upload" @click="triggerUpload">{{ $t('profile.uploadNew') }}</button>
</div>
</div>
<!-- Form Inputs -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-10">
<div class="space-y-2">
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">คำนำหน</label>
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">{{ $t('profile.prefix') }}</label>
<select v-model="userData.prefix" class="premium-input w-full">
<option>นาย</option>
<option>นาง</option>
@ -264,7 +264,7 @@ const saveProfile = async () => {
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300"></label>
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">{{ $t('profile.firstName') }}</label>
<input
v-model="userData.firstName"
type="text"
@ -275,7 +275,7 @@ const saveProfile = async () => {
<span v-if="errors.firstName" class="text-red-500 text-[10px] mt-1 font-bold">{{ errors.firstName }}</span>
</div>
<div class="space-y-2">
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">นามสก</label>
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">{{ $t('profile.lastName') }}</label>
<input
v-model="userData.lastName"
type="text"
@ -287,7 +287,7 @@ const saveProfile = async () => {
</div>
</div>
<div class="space-y-2">
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">เมล</label>
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">{{ $t('profile.email') }}</label>
<input
v-model="userData.email"
type="email"
@ -298,7 +298,7 @@ const saveProfile = async () => {
<span v-if="errors.email" class="text-red-500 text-[10px] mt-1 font-bold">{{ errors.email }}</span>
</div>
<div class="space-y-2">
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">เบอรโทรศพท</label>
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">{{ $t('profile.phone') }}</label>
<input
v-model="userData.phone"
type="text"
@ -312,10 +312,10 @@ const saveProfile = async () => {
<!-- Security Section -->
<div class="border-t border-slate-200 dark:border-white/5 pt-10 mb-10">
<h3 class="text-lg font-black text-slate-900 dark:text-white mb-6">ความปลอดภ</h3>
<h3 class="text-lg font-black text-slate-900 dark:text-white mb-6">{{ $t('profile.security') }}</h3>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="space-y-2">
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">รหสผานปจจ</label>
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">{{ $t('profile.currentPassword') }}</label>
<div class="relative">
<input
v-model="passwordForm.currentPassword"
@ -340,7 +340,7 @@ const saveProfile = async () => {
</div>
</div>
<div class="space-y-2">
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">รหสผานใหม</label>
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">{{ $t('profile.newPassword') }}</label>
<div class="relative">
<input
v-model="passwordForm.newPassword"
@ -367,7 +367,7 @@ const saveProfile = async () => {
<span v-if="errors.newPassword" class="text-red-500 text-[10px] mt-1 font-bold">{{ errors.newPassword }}</span>
</div>
<div class="space-y-2">
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">นยนรหสผานใหม</label>
<label class="text-xs font-black uppercase tracking-widest text-slate-500 dark:text-slate-300">{{ $t('profile.confirmNewPassword') }}</label>
<div class="relative">
<input
v-model="passwordForm.confirmPassword"
@ -398,8 +398,8 @@ const saveProfile = async () => {
<!-- Actions -->
<div class="flex flex-col md:flex-row gap-4 pt-6 border-t border-white/5">
<button class="btn-save-premium flex-1" @click="saveProfile">บันทึกข้อมูล</button>
<button class="btn-cancel-premium md:w-32" @click="toggleEdit(false)">ยกเลิก</button>
<button class="btn-save-premium flex-1" @click="saveProfile">{{ $t('save') }}</button>
<button class="btn-cancel-premium md:w-32" @click="toggleEdit(false)">{{ $t('cancel') }}</button>
</div>
</div>
</div>