Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/app/(user)/edit/_components/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { SubmitHandler, useForm } from 'react-hook-form';
import { useAuth } from '@/contexts/auth';
import { useLiff } from '@/contexts/liff';
import toast from 'react-hot-toast';
import Consent from '../../register/_components/policy/consent';

export default function Form() {
const { editError, edit, isEditing, user: defaultUser } = useAuth();
Expand All @@ -48,6 +49,7 @@ export default function Form() {
graduatedYear: defaultUser.graduatedYear,
faculty: defaultUser.faculty,
age: defaultUser.age,
isAcroPhobia: defaultUser.isAcroPhobia,
sizeJersey: defaultUser.sizeJersey,
foodLimitation: defaultUser.foodLimitation,
chronicDisease: defaultUser.chronicDisease,
Expand Down Expand Up @@ -252,6 +254,13 @@ export default function Form() {
<div className="relative space-y-1">
<Label>โรคประจำตัว</Label>
<TextInput {...register('chronicDisease')} />
<div className="pt-0.5"></div>
<Consent
value={user.isAcroPhobia}
setValue={(val: boolean) => setValue('isAcroPhobia', val)}
label="คุณกลัวความสูงหรือไม่"
isRequired={false}
/>
<ErrorMsgFloat>{errors.chronicDisease?.message}</ErrorMsgFloat>
</div>

Expand Down
10 changes: 5 additions & 5 deletions src/app/(user)/register/_components/subpages/two.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { universities } from '@/const/universities';
import { faculties } from '@/const/faculties';
import { sizeJersey } from '@/const/size';
import { statusMap } from '@/const/status';
// import Consent from '../policy/consent';
import Consent from '../policy/consent';

interface TwoProps {
setStep: (value: number) => void;
Expand Down Expand Up @@ -255,13 +255,13 @@ export default function Two({ setStep, form }: TwoProps) {
<div className="relative space-y-1">
<Label>โรคประจำตัว</Label>
<TextInput {...register('chronicDisease')} />
{/* <div className='pt-0.5'></div>
<div className="pt-0.5"></div>
<Consent
value={true}
setValue={(val: boolean) => {}}
value={user.isAcroPhobia}
setValue={(val: boolean) => setValue('isAcroPhobia', val)}
label="คุณกลัวความสูงหรือไม่"
isRequired={false}
/> */}
/>
<ErrorMsgFloat>{errors.chronicDisease?.message}</ErrorMsgFloat>
</div>

Expand Down
2 changes: 1 addition & 1 deletion src/app/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const config = {
liffId: process.env.NEXT_PUBLIC_LIFF_ID || '',
baseURL: process.env.NEXT_PUBLIC_BASE_URL || '',
baseURL: process.env.NEXT_PUBLIC_BASE_URL || ''
};
2 changes: 1 addition & 1 deletion src/components/profile/Ticket/EditButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function Edit() {
<p className="text-center font-medium text-white">บัญชีของฉัน</p>
</div>
<Link
href={'user/edit'}
href={'/edit'}
className="flex h-12 w-[272px] justify-center rounded-full bg-white text-lg text-dark-pink"
>
<div className="flex flex-row items-center justify-center gap-1">
Expand Down
9 changes: 6 additions & 3 deletions src/components/staff/qr/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getImageURL } from '@/utils/image';

interface ModalProps {
modalType: 'confirm' | 'invalid' | 'already';
userInfo: string | null;
userInfo: string | undefined;
scanAgain: () => void;
closeFn: () => void;
time?: string; // Optional prop for 'already' type
Expand Down Expand Up @@ -111,10 +111,13 @@ const Modal: React.FC<ModalProps> = ({
};

return (
<div className={`${modalClasses}`}>
<div className={`${modalClasses}`} onClick={closeFn}>
<div className={`flex h-full items-center`}>
<div className="flex flex-row items-center justify-center">
<div className="flex h-full w-72 flex-col items-center justify-center rounded-lg bg-white">
<div
className="flex h-full w-72 flex-col items-center justify-center rounded-lg bg-white"
onClick={e => e.stopPropagation()}
>
{renderContent()}
</div>
</div>
Expand Down
79 changes: 33 additions & 46 deletions src/components/staff/qr/qrbutton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,74 +3,61 @@ import { useState } from 'react';
import { useLiff } from '@/contexts/liff';
import Modal from './Modal';
import { ScanLine } from 'lucide-react';
import { apiClient } from '@/utils/axios';
import { useAuth } from '@/contexts/auth';
import { scanQR } from '@/utils/qr';

export default function QrButton() {
const { client } = useLiff();
const { token } = useAuth();
const [qrCodeValue, setQrCodeValue] = useState<string | null>(null);
const [modalType, setModalType] = useState<
'confirm' | 'invalid' | 'already' | null
>(null);
const [isModalOpen, setIsModalOpen] = useState(true);
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalType, setModalType] = useState<'confirm' | 'invalid' | 'already'>(
'invalid',
);
const [userInfo, setUserInfo] = useState<string>();
const [time, setTime] = useState<string>();

const openQRScanner = async () => {
if (!client?.isInClient()) {
alert('Please use this feature inside the LINE app.');
return;
}
try {
const result = await client.scanCodeV2();
const value = result.value ?? '';

// Post the QR code result to the API
const status = await postQrCodeToApi(value ?? '');
setModalType(status?.toString() as 'confirm' | 'invalid' | 'already');
setQrCodeValue(value);
setIsModalOpen(true);
} catch (error) {
console.error('Error scanning QR code:', error);
alert('Failed to scan QR code. Please try again.');
setModalType('invalid');
setIsModalOpen(true);
if (!token?.accessToken) {
console.error('Failed scaning QR: no accessToken');
return;
}
};

const postQrCodeToApi = async (value: string) => {
try {
const response = await apiClient.post(
`/users/qr/${value}`,
{},
{
headers: {
Authorization: `Bearer ${token?.accessToken}`,
},
},
);
if (response.status === 200) {
return 'confirm';
} else if (response.status === 400) {
return 'already';
} else if (response.status === 500) {
return 'invalid';
} else {
return 'invalid';
}
} catch (error) {
console.error('Error posting QR code to API:', error);
return error;
const scanResult = await client.scanCodeV2();
const userId = scanResult.value;

if (!userId) {
console.error('Failed scaning QR: No userId');
return;
}

const { modalType, time, userInfo } = await scanQR(
userId,
token.accessToken,
);

setIsModalOpen(true);
setModalType(modalType);
setTime(time);
setUserInfo(userInfo);
};

return (
<div className="mt-12 flex justify-center">
<div className="mt-12 flex cursor-pointer justify-center">
<div className="flex h-12 w-72 flex-row justify-center rounded-full bg-white px-4 py-2 text-lg text-dark-pink">
<ScanLine size={26} className="mr-2 mt-1" />
<p className="mt-1">คลิกเพื่อสแกน</p>
<p className="mt-1" onClick={openQRScanner}>
คลิกเพื่อสแกน
</p>
</div>
{isModalOpen && modalType != null && (
<Modal
userInfo={qrCodeValue}
userInfo={userInfo}
time={time}
scanAgain={openQRScanner}
closeFn={() => setIsModalOpen(false)}
modalType={modalType}
Expand Down
3 changes: 1 addition & 2 deletions src/const/timer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ if (!rawTargetDate) {
throw new Error('Environment variable NEXT_PUBLIC_TARGET_DATE is missing.');
}

const t = rawTargetDate.split(/[^0-9]/).map(t => parseInt(t));
const parsedDate = new Date(t[0], t[1] - 1, t[2]);
const parsedDate = new Date(rawTargetDate);
if (isNaN(parsedDate.getTime())) {
throw new Error(
`Invalid date format in NEXT_PUBLIC_TARGET_DATE: ${rawTargetDate}`,
Expand Down
1 change: 1 addition & 0 deletions src/schema/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const EditSchema = z.object({
sizeJersey: z.enum(sizeJersey, {
message: 'กรุณาเลือกขนาดเสื้อ',
}),
isAcroPhobia: z.boolean(),
education: z.enum(educations, { message: 'กรุณาเลือกการศึกษา' }),
foodLimitation: z.string(),
chronicDisease: z.string(),
Expand Down
1 change: 1 addition & 0 deletions src/schema/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const RegisterSchema = z.object({
sizeJersey: z.enum(sizeJersey, {
message: 'กรุณาเลือกขนาดเสื้อ',
}),
isAcroPhobia: z.boolean().default(false),
education: z.enum(educations, { message: 'กรุณาเลือกการศึกษา' }),
foodLimitation: z.string(),
chronicDisease: z.string(),
Expand Down
1 change: 1 addition & 0 deletions src/schema/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const UserSchema = z.object({
imageURL: z.string(),
invitationCode: z.string(),
lastEntered: z.string(),
isAcroPhobia: z.boolean(),
phone: z.string(),
role: z.enum(roles),
sizeJersey: z.enum(sizeJersey),
Expand Down
66 changes: 66 additions & 0 deletions src/utils/qr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import axios from 'axios';
import { apiClient } from './axios';

export interface ScanQRResp {
age: 'string';
chronicDisease: 'string';
drugAllergy: 'string';
education: 'studying';
email: 'string';
faculty: 'string';
foodLimitation: 'string';
graduatedYear: 'string';
id: 'string';
imageUrl: 'string';
invitationCode: 'string';
lastEntered: 'string';
name: 'string';
phone: 'string';
role: 'member';
sizeJersey: 'string';
status: 'chula_student';
uid: 'string';
university: 'string';
}

export interface ScanQRResult {
modalType: 'confirm' | 'invalid' | 'already';
userInfo?: string;
time?: string;
}

export interface ScanQRError {
error: string;
message: string;
}

export async function scanQR(
userId: string,
accessToken: string,
): Promise<ScanQRResult> {
try {
const resp = await apiClient.post<ScanQRResp>(
`/users/qr/${userId}`,
{},
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
);
return { modalType: 'confirm', userInfo: resp.data.name };
} catch (err: unknown) {
if (axios.isAxiosError<ScanQRError>(err)) {
switch (err.response?.status) {
case 400:
return {
modalType: 'already',
time: new Date(err.response.data.message).toLocaleString(),
};
default:
return { modalType: 'invalid' };
}
}
return { modalType: 'invalid' };
}
}