feat: Add initial frontend setup including authentication, instructor, and admin course management modules.
This commit is contained in:
parent
9fd217e1db
commit
19844f343b
16 changed files with 2065 additions and 293 deletions
|
|
@ -11,15 +11,32 @@
|
|||
<div class="flex flex-col md:flex-row gap-8">
|
||||
<!-- Avatar Section -->
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="w-32 h-32 bg-primary-100 rounded-full flex items-center justify-center text-6xl mb-4">
|
||||
{{ profile.avatar }}
|
||||
<div class="w-32 h-32 rounded-full flex items-center justify-center text-6xl mb-4 overflow-hidden bg-primary-100">
|
||||
<img
|
||||
v-if="profile.avatarUrl"
|
||||
:key="profile.avatarUrl"
|
||||
:src="profile.avatarUrl"
|
||||
alt=""
|
||||
class="w-full h-full object-cover"
|
||||
@error="onAvatarError"
|
||||
/>
|
||||
<span v-else>{{ profile.avatar }}</span>
|
||||
</div>
|
||||
<q-btn
|
||||
outline
|
||||
color="primary"
|
||||
label="เปลี่ยนรูป"
|
||||
icon="photo_camera"
|
||||
@click="handleAvatarUpload"
|
||||
:loading="uploadingAvatar"
|
||||
@click="triggerAvatarUpload"
|
||||
/>
|
||||
<p class="text-xs text-gray-400 mt-2">ขนาดไม่เกิน 5MB</p>
|
||||
<input
|
||||
ref="avatarInputRef"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
class="hidden"
|
||||
@change="handleAvatarUpload"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -283,6 +300,7 @@ const profile = ref({
|
|||
role: '',
|
||||
roleName: '',
|
||||
avatar: '👨🏫',
|
||||
avatarUrl: '' as string | null,
|
||||
createdAt: ''
|
||||
});
|
||||
|
||||
|
|
@ -330,12 +348,67 @@ const formatDate = (date: string) => {
|
|||
});
|
||||
};
|
||||
|
||||
const handleAvatarUpload = () => {
|
||||
$q.notify({
|
||||
type: 'info',
|
||||
message: 'ฟีเจอร์อัพโหลดรูปภาพจะพร้อมใช้งานเร็วๆ นี้',
|
||||
position: 'top'
|
||||
});
|
||||
// Avatar upload
|
||||
const avatarInputRef = ref<HTMLInputElement | null>(null);
|
||||
const uploadingAvatar = ref(false);
|
||||
|
||||
const triggerAvatarUpload = () => {
|
||||
avatarInputRef.value?.click();
|
||||
};
|
||||
|
||||
const onAvatarError = () => {
|
||||
// Fallback to emoji if image fails
|
||||
profile.value.avatarUrl = null;
|
||||
};
|
||||
|
||||
const handleAvatarUpload = async (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const file = input.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
// Validate file type
|
||||
if (!file.type.startsWith('image/')) {
|
||||
$q.notify({
|
||||
type: 'warning',
|
||||
message: 'กรุณาเลือกไฟล์รูปภาพเท่านั้น',
|
||||
position: 'top'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate file size (max 5MB)
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
$q.notify({
|
||||
type: 'warning',
|
||||
message: 'ไฟล์มีขนาดใหญ่เกิน 5MB',
|
||||
position: 'top'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
uploadingAvatar.value = true;
|
||||
try {
|
||||
await userService.uploadAvatar(file);
|
||||
|
||||
// Re-fetch profile to get presigned URL from backend
|
||||
await fetchProfile();
|
||||
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
message: 'อัพโหลดรูปโปรไฟล์สำเร็จ',
|
||||
position: 'top'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to upload avatar:', error);
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'เกิดข้อผิดพลาดในการอัพโหลดรูป',
|
||||
position: 'top'
|
||||
});
|
||||
} finally {
|
||||
uploadingAvatar.value = false;
|
||||
input.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateProfile = async () => {
|
||||
|
|
@ -432,7 +505,8 @@ const fetchProfile = async () => {
|
|||
phone: data.profile.phone || '',
|
||||
role: data.role.code,
|
||||
roleName: data.role.name.th,
|
||||
avatar: data.profile.avatar_url || '👨🏫',
|
||||
avatar: '👨🏫',
|
||||
avatarUrl: data.profile.avatar_url,
|
||||
createdAt: data.created_at
|
||||
};
|
||||
} catch (error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue