279 lines
7.8 KiB
Vue
279 lines
7.8 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* @file register.vue
|
|
* @description User Registration Page.
|
|
* Features a multi-step style form (though currently single view) for creating a new account.
|
|
*/
|
|
|
|
definePageMeta({
|
|
layout: "auth",
|
|
middleware: "auth",
|
|
});
|
|
|
|
useHead({
|
|
title: "สมัครสมาชิก - e-Learning",
|
|
});
|
|
|
|
const router = useRouter();
|
|
const { register } = useAuth(); // Import register from useAuth
|
|
const { errors, validate, clearFieldError } = useFormValidation();
|
|
|
|
const isLoading = ref(false);
|
|
|
|
// Reactive form state
|
|
const registerForm = reactive({
|
|
prefix: "นาย",
|
|
username: "",
|
|
firstName: "",
|
|
lastName: "",
|
|
phone: "",
|
|
email: "",
|
|
password: "",
|
|
confirmPassword: "",
|
|
});
|
|
|
|
// Validation rules
|
|
const registerRules = {
|
|
username: { rules: { required: true, minLength: 4 }, label: "ชื่อผู้ใช้" },
|
|
firstName: { rules: { required: true, minLength: 2 }, label: "ชื่อ" },
|
|
lastName: { rules: { required: true, minLength: 2 }, label: "นามสกุล" },
|
|
phone: {
|
|
rules: { required: true, pattern: /^0[0-9]{8,9}$/ },
|
|
label: "เบอร์โทรศัพท์",
|
|
},
|
|
email: { rules: { required: true, email: true }, label: "อีเมล" },
|
|
password: { rules: { required: true, minLength: 8 }, label: "รหัสผ่าน" },
|
|
confirmPassword: {
|
|
rules: { required: true, match: "password" },
|
|
label: "ยืนยันรหัสผ่าน",
|
|
},
|
|
};
|
|
|
|
const handleRegister = async () => {
|
|
if (!validate(registerForm, registerRules)) return;
|
|
|
|
isLoading.value = true;
|
|
|
|
// Map prefix to { th, en }
|
|
const prefixMap: Record<string, string> = {
|
|
'นาย': 'Mr.',
|
|
'นาง': 'Mrs.',
|
|
'นางสาว': 'Ms.'
|
|
};
|
|
|
|
const payload = {
|
|
username: registerForm.username,
|
|
email: registerForm.email,
|
|
password: registerForm.password,
|
|
first_name: registerForm.firstName,
|
|
last_name: registerForm.lastName,
|
|
prefix: {
|
|
th: registerForm.prefix,
|
|
en: prefixMap[registerForm.prefix] || 'Mr.'
|
|
},
|
|
phone: registerForm.phone
|
|
};
|
|
|
|
const result = await register(payload);
|
|
|
|
isLoading.value = false;
|
|
|
|
if (result.success) {
|
|
alert('สมัครสมาชิกสำเร็จ! กรุณาเข้าสู่ระบบ');
|
|
router.push("/auth/login");
|
|
} else {
|
|
alert(result.error || 'การลงทะเบียนล้มเหลว');
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="card auth-card" style="width: 100%; max-width: 480px">
|
|
<!-- Loading Overlay -->
|
|
<LoadingSpinner v-if="isLoading" full-page text="กำลังดำเนินการ..." />
|
|
|
|
<!-- Title Area -->
|
|
<div class="text-center mb-8">
|
|
<h1 class="text-3xl font-bold text-white mb-2">e-Learning Platform</h1>
|
|
<p class="text-gray-400 text-sm">
|
|
ยินดีต้อนรับกลับ! กรุณากรอกข้อมูลของคุณ
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Tabs Removed -->
|
|
|
|
<!-- REGISTER FORM -->
|
|
<form @submit.prevent="handleRegister">
|
|
<!-- Username -->
|
|
<FormInput
|
|
v-model="registerForm.username"
|
|
label="ชื่อผู้ใช้"
|
|
placeholder="username"
|
|
:error="errors.username"
|
|
required
|
|
class="dark-form-input"
|
|
@update:model-value="clearFieldError('username')"
|
|
/>
|
|
<!-- Email -->
|
|
<FormInput
|
|
v-model="registerForm.email"
|
|
label="อีเมล"
|
|
type="email"
|
|
placeholder="student@example.com"
|
|
:error="errors.email"
|
|
required
|
|
class="dark-form-input"
|
|
@update:model-value="clearFieldError('email')"
|
|
/>
|
|
|
|
<!-- Password Fields -->
|
|
<FormInput
|
|
v-model="registerForm.password"
|
|
label="รหัสผ่าน"
|
|
type="password"
|
|
placeholder="สร้างรหัสผ่าน (อย่างน้อย 8 ตัวอักษร)"
|
|
:error="errors.password"
|
|
required
|
|
class="dark-form-input"
|
|
@update:model-value="clearFieldError('password')"
|
|
/>
|
|
|
|
<FormInput
|
|
v-model="registerForm.confirmPassword"
|
|
label="ยืนยันรหัสผ่าน"
|
|
type="password"
|
|
placeholder="ยืนยันรหัสผ่านอีกครั้ง"
|
|
:error="errors.confirmPassword"
|
|
required
|
|
class="dark-form-input"
|
|
@update:model-value="clearFieldError('confirmPassword')"
|
|
/>
|
|
|
|
<!-- Name Fields (Split Row) -->
|
|
<div class="grid-12" style="gap: 16px; margin-bottom: 0">
|
|
<div class="col-span-4">
|
|
<label class="input-label text-gray-300">คำนำหน้า</label>
|
|
<select
|
|
v-model="registerForm.prefix"
|
|
class="input-field dark-input"
|
|
style="height: 42px"
|
|
>
|
|
<option value="นาย">นาย</option>
|
|
<option value="นาง">นาง</option>
|
|
<option value="นางสาว">นางสาว</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-span-8">
|
|
<FormInput
|
|
v-model="registerForm.firstName"
|
|
label="ชื่อ"
|
|
placeholder=""
|
|
:error="errors.firstName"
|
|
required
|
|
class="dark-form-input"
|
|
@update:model-value="clearFieldError('firstName')"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<FormInput
|
|
v-model="registerForm.lastName"
|
|
label="นามสกุล"
|
|
placeholder=""
|
|
:error="errors.lastName"
|
|
required
|
|
class="dark-form-input"
|
|
@update:model-value="clearFieldError('lastName')"
|
|
/>
|
|
|
|
<FormInput
|
|
v-model="registerForm.phone"
|
|
label="เบอร์โทรศัพท์"
|
|
type="tel"
|
|
placeholder=""
|
|
:error="errors.phone"
|
|
required
|
|
class="dark-form-input"
|
|
@update:model-value="clearFieldError('phone')"
|
|
/>
|
|
|
|
<!-- Submit Button -->
|
|
<button
|
|
type="submit"
|
|
class="btn btn-primary w-full mb-4 mt-2"
|
|
:disabled="isLoading"
|
|
style="height: 44px; font-size: 16px"
|
|
>
|
|
<LoadingSpinner v-if="isLoading" size="sm" />
|
|
<span v-else>สร้างบัญชี</span>
|
|
</button>
|
|
|
|
<!-- Toggle to Login -->
|
|
<div class="text-center mt-6 text-sm">
|
|
<span class="text-muted">มีบัญชีอยู่แล้ว? </span>
|
|
<NuxtLink to="/auth/login" class="font-bold text-primary hover:underline">เข้าสู่ระบบ</NuxtLink>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* Force Dark Theme for Card */
|
|
.auth-card {
|
|
background-color: #1e293b; /* Slate 800 */
|
|
border-color: transparent;
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.5),
|
|
0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
color: #f8fafc;
|
|
}
|
|
|
|
|
|
|
|
/* Input Overrides for Dark Mode integration within this page */
|
|
.dark-input {
|
|
background-color: #334155; /* Slate 700 */
|
|
border-color: #475569; /* Slate 600 */
|
|
color: #f1f5f9;
|
|
}
|
|
|
|
.dark-input:focus {
|
|
border-color: #3b82f6;
|
|
outline: none;
|
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
|
|
}
|
|
|
|
/* Deep selector for FormInput component internals */
|
|
:deep(.input-field) {
|
|
background-color: #334155 !important;
|
|
border-color: #475569 !important;
|
|
color: #f1f5f9 !important;
|
|
}
|
|
|
|
:deep(.input-field::placeholder) {
|
|
color: #94a3b8;
|
|
}
|
|
|
|
:deep(.input-label) {
|
|
color: #cbd5e1 !important;
|
|
}
|
|
|
|
:deep(.input-label span.required-mark) {
|
|
color: #ef4444;
|
|
}
|
|
|
|
/* Select option styling */
|
|
option {
|
|
background-color: #334155;
|
|
color: white;
|
|
}
|
|
</style>
|
|
|
|
<style>
|
|
/*
|
|
Global Override for Auth Layout Background on this page context.
|
|
Ensures the entire page background matches the design requirement.
|
|
*/
|
|
.auth-shell {
|
|
background-color: #0f172a !important; /* Slate 900 */
|
|
}
|
|
</style>
|