diff --git a/src/app/rpkm/edit/layout.tsx b/src/app/rpkm/edit/layout.tsx new file mode 100644 index 00000000..8fcd3317 --- /dev/null +++ b/src/app/rpkm/edit/layout.tsx @@ -0,0 +1,19 @@ +import React, { ReactNode } from 'react'; + +import background from '@public/rpkm/freshy-night/background/bg.svg'; + +const layout = ({ children }: { children: ReactNode }) => { + return ( +
+ {children} +
+ ); +}; + +export default layout; diff --git a/src/app/rpkm/edit/page.tsx b/src/app/rpkm/edit/page.tsx new file mode 100644 index 00000000..427162fb --- /dev/null +++ b/src/app/rpkm/edit/page.tsx @@ -0,0 +1,356 @@ +'use client'; + +import { useState, ChangeEvent } from 'react'; +import { useRouter } from 'next/navigation'; +import { useAuth } from '@/context/AuthContext'; +import { getAccessToken } from '@/utils/auth'; +import { apiClient } from '@/utils/axios'; +import { + StyledInput, + StyledSelect, +} from '@/components/(main)/register/StyledComponents'; +import { major } from '@/utils/register'; +import toast from 'react-hot-toast'; +import { UserDTO } from '@/dtos/userDTO'; +import Spinner from '@/components/firstdate/Spinner'; +import UserCard from '@/components/UserCard'; + +type RegisterUser = Pick< + UserDTO, + | 'title' + | 'firstname' + | 'lastname' + | 'nickname' + | 'faculty' + | 'year' + | 'tel' + | 'parent_tel' + | 'parent' + | 'food_allergy' + | 'drug_allergy' + | 'illness' +>; + +export default function Edit() { + const router = useRouter(); + const [upload, setUpload] = useState(false); + const { user, resetContext } = useAuth(); + const userId = user?.id; + const [formData, setFormData] = useState({ + title: user?.title || '', + firstname: user?.firstname || '', + lastname: user?.lastname || '', + nickname: user?.nickname || '', + faculty: user?.faculty || '', + year: user?.year || 1, + tel: user?.tel || '', + parent_tel: user?.parentTel || '', + parent: user?.parent || '', + food_allergy: user?.foodAllergy || '', + drug_allergy: user?.drugAllergy || '', + illness: user?.illness || '', + }); + const [errors, setErrors] = useState([]); + + const handleInputChange = ( + e: ChangeEvent + ) => { + let value: string = e.target.value; + if (['tel', 'parent_tel'].includes(e.target.name)) { + value = + '0123456789'.includes(value.at(-1) || '') && value.length <= 10 + ? value + : value.slice(0, -1); + } + + setFormData({ + ...formData, + [e.target.name]: e.target.name === 'year' ? +value : value, + }); + }; + + const validateForm = (): boolean => { + const formErrors: string[] = []; + if (!formData.title) formErrors.push('title'); + if (!formData.firstname) formErrors.push('firstname'); + if (!formData.lastname) formErrors.push('lastname'); + if (!formData.nickname) formErrors.push('nickname'); + if (!formData.faculty) formErrors.push('faculty'); + if (!formData.year) formErrors.push('year'); + if (formData.tel.length != 10) formErrors.push('tel'); + if (formData.parent_tel.length != 10) formErrors.push('parent_tel'); + if (!formData.parent) formErrors.push('parent'); + setErrors(formErrors); + + const isError = formErrors.length !== 0; + if (isError) { + toast.error('โปรดกรอกข้อมูลให้ครบ'); + } + + return !isError; + }; + + async function updateUserProfile(userData: RegisterUser) { + try { + const accessToken = await getAccessToken(); + const response = await apiClient.patch( + `/user/profile/${userId}`, + userData, + { headers: { Authorization: `Bearer ${accessToken}` } } + ); + console.log('User profile updated successfully:', response.data); + } catch (error) { + console.error('Error updating user profile:', error); + } + } + + const handleSubmit = () => { + if (validateForm()) { + setUpload(true); + updateUserProfile(formData).then(async () => { + toast.success('เเก้ไขข้อมูลสำเร็จ'); + await resetContext(); + router.back(); + }); + } + }; + + return ( +
+ {upload && ( +
+ +
+ )} + +

+ ข้อมูลส่วนตัว +

+ +
+
+
+

+ ข้อมูลส่วนตัว +

+ + + + + + + + +
+ + + +
+ + +
+ +
+

+ ข้อมูลผู้ปกครอง +

+ + +
+ +
+

+ ข้อมูลด้านสุขภาพ +

+ +
+ + + + + +
+
+
+ + +
+
+
+
+ ); +} diff --git a/src/components/UserCard.tsx b/src/components/UserCard.tsx index b3ec8f2c..04201503 100644 --- a/src/components/UserCard.tsx +++ b/src/components/UserCard.tsx @@ -1,56 +1,122 @@ -import React from 'react'; +import React, { ChangeEvent, useEffect, useState } from 'react'; import TV from '@public/user-card/tv.png'; import EditIcon from '@public/user-card/edit-icon.svg'; import profilePlaceholder from '@public/placeholder.svg'; import Image from 'next/image'; import { useAuth } from '@/context/AuthContext'; import { useRouter } from 'next/navigation'; +import { apiClient } from '@/utils/axios'; +import { getAccessToken } from '@/utils/auth'; +import Spinner from './firstdate/Spinner'; -const UserCard = () => { +interface UserCradProps { + disableEditIcon?: boolean; +} + +const UserCard = ({ disableEditIcon }: UserCradProps) => { + const [uploading, setUploading] = useState(false); const router = useRouter(); - const { user } = useAuth(); + const { user, resetContext } = useAuth(); + const userId = user?.id; const name = `${user?.firstname} ${user?.lastname}`; const studentId = user?.email.split('@')[0]; - const photoUrl = user?.photoUrl; + const [currentPhotoUrl, setCurrentPhotoUrl] = useState( + user?.photoUrl + ); + + useEffect(() => setCurrentPhotoUrl(user?.photoUrl), [user]); + + const handlePhotoChange = async (e: ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + setCurrentPhotoUrl(undefined); + await handlePhotoUpload(file); + } + }; + + const handlePhotoUpload = async (file: File) => { + if (!file) return; + + const formData = new FormData(); + formData.append('picture', file); + setUploading(true); + try { + const accessToken = await getAccessToken(); + const response = await apiClient.put( + `/user/picture/${userId}`, + formData, + { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'multipart/form-data', + }, + } + ); + console.log('Photo uploaded successfully:', response.data); + setCurrentPhotoUrl(response.data.photo_url); + resetContext(); + } catch (error) { + console.error('Error uploading photo:', error); + } finally { + setUploading(false); + } + }; return ( -
- tv -
-
- { - user-picture - } -
-
-

- {name} -

-

- รหัสนิสิต {studentId} -

+ <> + {uploading && ( +
+
-
-
router.push('/edit')} - > + )} +
edit-icon +
+ {disableEditIcon && ( + + )} +
+ {currentPhotoUrl && ( + user-picture + )} +
+
+

+ {name} +

+

+ รหัสนิสิต {studentId} +

+
+
+ {!disableEditIcon && ( +
router.push('/rpkm/edit')} + > + edit-icon +
+ )}
-
+ ); }; diff --git a/src/components/rpkm/Baan/Section/BaanButtonsSection.tsx b/src/components/rpkm/Baan/Section/BaanButtonsSection.tsx index 25f5c27e..76491f47 100644 --- a/src/components/rpkm/Baan/Section/BaanButtonsSection.tsx +++ b/src/components/rpkm/Baan/Section/BaanButtonsSection.tsx @@ -8,6 +8,9 @@ import { useRouter } from 'next/navigation'; import alertImg from '@public/alert.svg'; import Image from 'next/image'; import BackToHomeBtn from '../../BackToHomeBtn'; +import BaseModal from '../../Modal/BaseModal'; +import ModalButton from '../../Modal/ModalButton'; +import modalStyles from '../../Modal/ModalStyle'; interface BaanButtonsSectionProps { mode: 'select' | 'edit'; @@ -25,13 +28,18 @@ const BaanButtonsSection: React.FC = ({ onConfirm, }) => { const { removeAllBaanSelection } = useBaan(); - const [isModalOpen, setModalOpen] = useState(false); + const [modalState, setModalState] = useState< + 'first-confirm' | 'second-confirm' | 'none' + >('none'); const router = useRouter(); - const handleConfirm = () => setModalOpen(true); - const handleModalConfirm = () => { - setModalOpen(false); + const handleConfirm = () => setModalState('first-confirm'); + const handleLastConfirm = () => { + setModalState('none'); onConfirm(); }; + const handleFirstConfirm = () => { + setModalState('second-confirm'); + }; if (isConfirmed) return null; @@ -59,6 +67,7 @@ const BaanButtonsSection: React.FC = ({ *กรุณาเลือกให้ครบ 5 บ้าน
)} +
*ระบบจะทำการบันทึกบ้านอัตโนมัติ
{selectedBaan && selectedBaan.length > 0 && (
setModalState('none')} + callBackFunction={handleFirstConfirm} >
= ({

+ +
+ alert-img +

+ โปรดอ่านอีกครั้ง!!! +

+

+ *เมื่อยืนยันแล้วจะไม่สามารถแก้ไขรายการของบ้าน + และจะไม่สามารถจับคู่กับเพื่อนได้อีก +

+
+
+ {(() => { + const { button } = modalStyles['red']; + return ( + <> + + ยืนยัน + + setModalState('none')} + borderClassName={button['cancel-border']} + backgroundClassName={button['cancel-background']} + > + ยกเลิก + + + ); + })()} +
+
)} diff --git a/src/components/rpkm/Sidebar/UserInfo.tsx b/src/components/rpkm/Sidebar/UserInfo.tsx index 96ba8df5..ee636420 100644 --- a/src/components/rpkm/Sidebar/UserInfo.tsx +++ b/src/components/rpkm/Sidebar/UserInfo.tsx @@ -27,7 +27,7 @@ function UserInfo() { {user?.baan}
diff --git a/tailwind.config.ts b/tailwind.config.ts index 7a314a66..7213d225 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -47,7 +47,7 @@ const config: Config = { sopha: ['var(--sopha)', 'system-ui'], }, dropShadow: { - text: '0px 0px 4px 0px #00000040;', + font: '0 1.2px 1.2px rgba(0,0,0,0.8)', }, animation: { shake: 'shaking 60ms infinite',