diff --git a/public/success-white.svg b/public/success-white.svg new file mode 100644 index 00000000..1a153c73 --- /dev/null +++ b/public/success-white.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/app/(main)/home/page.tsx b/src/app/(main)/home/page.tsx index fed76a12..70667b0a 100644 --- a/src/app/(main)/home/page.tsx +++ b/src/app/(main)/home/page.tsx @@ -14,29 +14,82 @@ import { useAuth } from '@/context/AuthContext'; import { useRouter } from 'next/navigation'; import BottomButton from '@/components/(main)/home/BottomButton'; import WaitModal from '@/components/(main)/home/WaitModal'; +import JoinModal from '@/components/(main)/home/JoinModal'; import { isUserRegistered } from '@/utils/user'; import Border from '@/components/firstdate/Border'; import CustomButton from '@/components/(main)/home/CustomButton'; import Link from 'next/link'; import { getMajorNameById } from '@/utils/register'; import { getCurrentTime } from '@/utils/time'; +import { createCheckIn, fetchCheckIn } from '@/utils/checkin'; +import { CheckIn, GetCheckIn } from '@/types/checkIn'; +import toast from 'react-hot-toast'; export default function Home() { const router = useRouter(); - const { user, logout } = useAuth(); + const { user, resetContext, logout } = useAuth(); const [clientTime, setClientTime] = useState(new Date('1980-01-01')); const [qrModal, setQrModal] = useState(false); const [waitModal, setWaitModal] = useState(false); const [interestedEvent, setInterestedEvent] = useState< 'first-date' | 'rup-peun' >('first-date'); + const [joinModal, setJoinModal] = useState(false); + //const [announce, setAnnounce] = useState(false); + const [isCheckedIn, setIsCheckedIn] = useState(false); + const [isJoined, setIsJoined] = useState(false); useEffect(() => { getCurrentTime().then((res) => { setClientTime(res.currentTime); }); + + const checkedIn = async () => { + if (!user) { + return; + } + + try { + const checkedIns: GetCheckIn[] | null = await fetchCheckIn(); + if (checkedIns) { + const findEvent = !!checkedIns.find( + (checkIn) => checkIn.event === 'confirm-rpkm' + ); + setIsCheckedIn(findEvent); + } else { + throw new Error(''); + } + } catch (e) { + console.log('fetch check in', e); + } + }; + + checkedIn(); }, []); + const checkInConfirm = async () => { + if (!user) { + return; + } + + try { + const checkInConfirm: CheckIn | null = await createCheckIn( + user.id, + user.email, + 'confirm-rpkm' + ); + + if (checkInConfirm) { + setIsJoined(true); + } else { + throw new Error('Error check in'); + } + resetContext(); + } catch (e) { + toast.error('ยืนยันไม่สำเร็จ'); + } + }; + return ( <>
Rub Peun Kao Mai 2024
@@ -159,6 +214,12 @@ export default function Home() { setModal={setWaitModal} event={interestedEvent} /> + ); } diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 8ba72ec3..94d4d662 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -1,4 +1,5 @@ import Footer from '@/components/(main)/Footer'; +import BaanProvider from '@/context/BaanContext'; import React from 'react'; const layout = ({ @@ -8,7 +9,7 @@ const layout = ({ }>) => { return (
- {children} + {children}
); diff --git a/src/app/rpkm/activities/details/community/page.tsx b/src/app/rpkm/activities/details/community/page.tsx index a8adf793..08c2d4fd 100644 --- a/src/app/rpkm/activities/details/community/page.tsx +++ b/src/app/rpkm/activities/details/community/page.tsx @@ -88,6 +88,7 @@ const Page = () => { href="https://lin.ee/i9Ezhg7" borderClassName="bg-rpkm-cream" backgroundClassName="bg-rpkm-green text-center text-lg text-rpkm-gray w-[60vw]" + target="_self" > Line OA ของกิจกรรม diff --git a/src/app/rpkm/activities/map/page.tsx b/src/app/rpkm/activities/map/page.tsx index b8d1a25c..522d2e9d 100644 --- a/src/app/rpkm/activities/map/page.tsx +++ b/src/app/rpkm/activities/map/page.tsx @@ -28,7 +28,7 @@ const page = () => { '1.5px 1.5px 0 #000, -1.5px -1.5px 0 #000, 1.5px -1.5px 0 #000, -1.5px 1.5px 0 #000', }} > - ข้อมูลที่จัดแต่ละกิจกรรม + กิจกรรมชุมชน diff --git a/src/components/(main)/home/CustomButton.tsx b/src/components/(main)/home/CustomButton.tsx index b9c8c377..77dbe7e2 100644 --- a/src/components/(main)/home/CustomButton.tsx +++ b/src/components/(main)/home/CustomButton.tsx @@ -1,3 +1,4 @@ +import { useBaan } from '@/context/BaanContext'; import { cn } from '@/lib/utils'; import { createEbookCount } from '@/utils/count'; import { useRouter } from 'next/navigation'; @@ -9,8 +10,10 @@ interface CustomButtonProps { children: React.ReactNode; registered?: boolean; currentDate: Date; + isCheckedIn?: boolean; setWaitModal?: (value: boolean) => void; setEvent?: (value: 'first-date' | 'rup-peun') => void; + setJoinModal?: (value: boolean) => void; } const CustomButton: React.FC = ({ @@ -19,10 +22,13 @@ const CustomButton: React.FC = ({ children, registered, currentDate, + isCheckedIn, setWaitModal, setEvent, + setJoinModal, }) => { const router = useRouter(); + const { isConfirmed } = useBaan(); const firstdate = async () => { let firstDateDate = currentDate; const firstDate = process.env.NEXT_PUBLIC_FIRST_DATE_DATE; @@ -43,12 +49,39 @@ const CustomButton: React.FC = ({ }; const rubpeun = async () => { let rupPeunDate = currentDate; + let closedSelectionDate = currentDate; + let baanAnnounceDate = currentDate; const rupPeun = process.env.NEXT_PUBLIC_RUP_PEUN_DATE; + const closedSelection = process.env.NEXT_PUBLIC_CLOSED_BAAN_SELECTION_DATE; + const announce = process.env.NEXT_PUBLIC_BAAN_ANNOUNCEMENT_DATE; if (rupPeun) { rupPeunDate = new Date(rupPeun); } - console.log(currentDate.toISOString(), rupPeunDate.toISOString()); - if (currentDate >= rupPeunDate) { + if (closedSelection && announce) { + closedSelectionDate = new Date(closedSelection); + baanAnnounceDate = new Date(announce); + } + console.log( + currentDate.toISOString(), + rupPeunDate.toISOString(), + closedSelectionDate.toISOString(), + baanAnnounceDate.toISOString() + ); + if (currentDate >= closedSelectionDate) { + if (!isConfirmed) { + router.push('/rpkm/activities/home'); + } else if (setJoinModal) { + if (isCheckedIn) { + router.push('/rpkm/activities/home'); + } else { + setJoinModal(true); + //setBaanAnnouncementModal + } + } + } else if ( + currentDate >= rupPeunDate && + currentDate < closedSelectionDate + ) { if (registered) { router.push('/rpkm/baan/home'); } else { @@ -79,7 +112,7 @@ const CustomButton: React.FC = ({ onClick: rubpeun, }, 'e-book': { - color: 'bg-project-cream opacity-35 cursor-not-allowed', + color: 'bg-project-cream', onClick: ebook, }, 'contact-list': { diff --git a/src/components/(main)/home/JoinModal.tsx b/src/components/(main)/home/JoinModal.tsx new file mode 100644 index 00000000..291dc8bd --- /dev/null +++ b/src/components/(main)/home/JoinModal.tsx @@ -0,0 +1,92 @@ +import { useRouter } from 'next/navigation'; +import Image from 'next/image'; +import Success from '@public/success-white.svg'; +import BaseModal from '@/components/rpkm/Modal/BaseModal'; +import ModalButton from '@/components/rpkm/Modal/ModalButton'; +import modalStyles from '@/components/rpkm/Modal/ModalStyle'; + +interface JoinModalProps { + modal: boolean; + setModal: (value: boolean) => void; + isJoined: boolean; + checkInConfirm: () => void; +} + +const JoinModal: React.FC = ({ + modal, + setModal, + isJoined, + checkInConfirm, +}) => { + const router = useRouter(); + + const HandleOnClick = () => { + router.push('/rpkm/activities/home'); + }; + + if (!isJoined) { + const { button } = modalStyles['red']; + return ( + +
+

+ ยืนยันการเข้าร่วมงาน +

+

ยืนยันการเข้าร่วมงาน

+
+
+ setModal(false)} + borderClassName={button['cancel-border']} + backgroundClassName={button['cancel-background']} + > + ยกเลิก + + { + setModal(false); + checkInConfirm(); + }} + borderClassName={button['accept-border']} + backgroundClassName={button['accept-background']} + > + ยืนยัน + +
+
+ ); + } + const { button } = modalStyles['green']; + return ( + +
+ success +

ยืนยันสำเร็จ!

+

+ แล้วพบกันวันที่ 3 สิงหาคม 2567 +

+
+
+ + ต่อไป + +
+
+ ); +}; + +export default JoinModal; diff --git a/src/components/rpkm/Modal/BaseModal.tsx b/src/components/rpkm/Modal/BaseModal.tsx index b4a5fa08..30605710 100644 --- a/src/components/rpkm/Modal/BaseModal.tsx +++ b/src/components/rpkm/Modal/BaseModal.tsx @@ -3,7 +3,7 @@ import { cn } from '@/lib/utils'; import modalStyles from './ModalStyle'; interface ModalProps { - variant: 'red' | 'blue' | 'black'; + variant: 'red' | 'blue' | 'black' | 'green'; open: boolean; children: React.ReactNode; } diff --git a/src/components/rpkm/Modal/ModalStyle.ts b/src/components/rpkm/Modal/ModalStyle.ts index 57240230..14557bb0 100644 --- a/src/components/rpkm/Modal/ModalStyle.ts +++ b/src/components/rpkm/Modal/ModalStyle.ts @@ -26,6 +26,15 @@ const modalStyles = { 'cancel-border': 'bg-[#F5F5F5]', }, }, + green: { + background: 'bg-[#67AB88]', + button: { + 'accept-background': 'bg-[#C94B4B] text-[#EAE3C3]', + 'accept-border': 'bg-[#EAE3C3]', + 'cancel-background': 'bg-[#C94B4B] text-[#EAE3C3]', + 'cancel-border': 'bg-[#EAE3C3]', + }, + }, }; export default modalStyles; diff --git a/src/components/rpkm/Sidebar/Menu.tsx b/src/components/rpkm/Sidebar/Menu.tsx index 79d764eb..91ce82e7 100644 --- a/src/components/rpkm/Sidebar/Menu.tsx +++ b/src/components/rpkm/Sidebar/Menu.tsx @@ -9,8 +9,8 @@ function Menu() { return (
ลงทะเบียนเลือกบ้าน @@ -82,19 +82,19 @@ function Menu() { aria-label="Accordion 2" title={
แผนที่
} > -
+
+
+ ข้อมูลที่จัดแต่ละกิจกรรม +
- ข้อมูลที่จัดแต่ละกิจกรรม - -
กิจกรรมชุมชน -
+
diff --git a/src/context/AuthContext.tsx b/src/context/AuthContext.tsx index 83df4f6d..d522fa9e 100644 --- a/src/context/AuthContext.tsx +++ b/src/context/AuthContext.tsx @@ -121,6 +121,18 @@ const AuthProvider: React.FC<{ children: ReactNode }> = ({ ); const now = (await getCurrentTime()).currentTime; + console.log( + 'now', + now, + 'firstdate', + firstdate, + 'startRPKM', + startRPKM, + 'endBannSelect', + endBannSelect, + 'freshyNight', + freshyNight + ); //firstdate if (path.includes('firstdate')) { if (now < firstdate) { @@ -135,7 +147,7 @@ const AuthProvider: React.FC<{ children: ReactNode }> = ({ } // end baan select - if (path.includes('/baan')) { + if (path.includes('rpkm/baan')) { if (now > endBannSelect) { return router.push('/rpkm/activities/home'); } diff --git a/src/dtos/checkInsDTO.ts b/src/dtos/checkInsDTO.ts index c6e3a768..1a56ee14 100644 --- a/src/dtos/checkInsDTO.ts +++ b/src/dtos/checkInsDTO.ts @@ -1,4 +1,4 @@ -import { CheckIn, ChildCheckIn } from '@/types/checkIn'; +import { CheckIn, ChildCheckIn, GetCheckIn } from '@/types/checkIn'; export type ChildCheckInDTO = { email: string; @@ -20,6 +20,17 @@ export type CheckInDTO = { lastname: string; }; +export type GetCheckInDTO = { + checkins: { + email: string; + event: string; + id: string; + user_id: string; + timestamp: string; + is_duplicate: boolean; + }[]; +}; + export const ChildCheckInParser = ( checkinDTO: ChildCheckInDTO ): ChildCheckIn => { @@ -45,3 +56,14 @@ export const CheckInParser = (checkinDTO: CheckInDTO): CheckIn => { lastName: checkinDTO.lastname, }; }; + +export const GetCheckInParser = (checkinDTO: GetCheckInDTO): GetCheckIn[] => { + return checkinDTO.checkins.map((checkin) => ({ + email: checkin.email, + event: checkin.event, + id: checkin.id, + userId: checkin.user_id, + timestamp: checkin.timestamp, + isDuplicate: checkin.is_duplicate, + })); +}; diff --git a/src/types/checkIn.ts b/src/types/checkIn.ts index 1a2f00be..b5179782 100644 --- a/src/types/checkIn.ts +++ b/src/types/checkIn.ts @@ -17,3 +17,12 @@ export type CheckIn = { firstName: string; lastName: string; }; + +export type GetCheckIn = { + email: string; + event: string; + id: string; + userId: string; + timestamp: string; + isDuplicate: boolean; +}; diff --git a/src/utils/checkin.ts b/src/utils/checkin.ts index 0e843d98..222e8266 100644 --- a/src/utils/checkin.ts +++ b/src/utils/checkin.ts @@ -1,8 +1,12 @@ import { AxiosResponse } from 'axios'; import { apiClient } from './axios'; -import { getAccessToken } from './auth'; -import { CheckIn } from '@/types/checkIn'; -import { CheckInParser } from '@/dtos/checkInsDTO'; +import { getAccessToken, getUserId } from './auth'; +import { CheckIn, GetCheckIn } from '@/types/checkIn'; +import { + CheckInParser, + GetCheckInDTO, + GetCheckInParser, +} from '@/dtos/checkInsDTO'; export const createCheckIn = async ( userID: string, @@ -34,3 +38,26 @@ export const createCheckIn = async ( return null; } }; + +export const fetchCheckIn = async (): Promise => { + const accessToken = await getAccessToken(); + const userId = await getUserId(); + + if (!accessToken || !userId) { + return null; + } + + try { + const res: AxiosResponse = await apiClient.get( + `/checkin/${userId}`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + return GetCheckInParser(res.data); + } catch (error) { + return null; + } +};