fix those failed test case

This commit is contained in:
rgrgogu
2026-07-16 01:36:16 +08:00
parent 5c391e7abd
commit 9b1805da8f
3 changed files with 151 additions and 35 deletions
+107 -33
View File
@@ -15,7 +15,7 @@
* May 23, 2026 lash0000 001 Initial creation - STAR Phase 1 Project
* May 23, 2026 lash0000 002 birthday + occupation required; all calls via useAuth (register, verifyOTP, resendOTP)
***********************************************************************************************************************************************************************/
import { useState } from 'react'
import { useState, useEffect } from 'react'
import { useNavigate, Link, useSearchParams } from 'react-router-dom'
import { useForm, Controller } from 'react-hook-form'
import { useAuth } from '@/contexts/AuthContext'
@@ -154,6 +154,27 @@ export function RegisterForm({ className, ...props }) {
// Accumulated data across steps
const [personalData, setPersonalData] = useState({})
// ── Pending-verification resume (email already has an unverified account) ──
const [resumeDialogOpen, setResumeDialogOpen] = useState(false)
const [isResuming, setIsResuming] = useState(false)
const [pendingCreds, setPendingCreds] = useState(null)
// ── Warn before the tab is closed/reloaded mid-registration ────────────────
// Once the user reaches Credentials, submitting can already create a
// server-side account + send an OTP — losing the tab here (slow internet,
// accidental reload) is what leaves an orphaned unverified account behind.
useEffect(() => {
if (step === 0) return
const handleBeforeUnload = (e) => {
e.preventDefault()
e.returnValue = ''
}
window.addEventListener('beforeunload', handleBeforeUnload)
return () => window.removeEventListener('beforeunload', handleBeforeUnload)
}, [step])
// ── Step 1: Personal info ─────────────────────────────────────────────────
const {
register: regPersonal,
@@ -180,45 +201,58 @@ export function RegisterForm({ className, ...props }) {
defaultValues: { email: '', password: '', confirm_password: '', group_code: groupCode },
})
const onCredentialsSubmit = async ({ email, password, group_code }) => {
const buildPersonalInfo = () => ({
name: {
given_name: personalData.given_name,
middle_name: personalData.middle_name || '',
last_name: personalData.last_name,
extension_name: personalData.extension_name || '',
full_name: [
`${personalData.last_name},`,
personalData.given_name,
personalData.middle_name || '',
personalData.extension_name || '',
].filter(Boolean).join(' ').trim(),
},
date_of_birth: personalData.date_of_birth,
occupation: personalData.occupation,
phone_number: personalData.phone
? (() => {
const digits = personalData.phone.replace(/\D/g, '')
const number = digits.startsWith('63')
? digits.slice(2) // strip country code if user typed +63...
: digits.replace(/^0/, '') // strip leading 0 if user typed 09...
return [{
number,
country_code: '63',
full_number: `63${number}`,
phone_type: 'mobile',
}]
})()
: [],
addresses: [],
})
const submitRegistration = async ({ email, password, group_code }, { confirmResume = false } = {}) => {
const result = await authRegister({
email,
password,
personal_info: {
name: {
given_name: personalData.given_name,
middle_name: personalData.middle_name || '',
last_name: personalData.last_name,
extension_name: personalData.extension_name || '',
full_name: [
`${personalData.last_name},`,
personalData.given_name,
personalData.middle_name || '',
personalData.extension_name || '',
].filter(Boolean).join(' ').trim(),
},
date_of_birth: personalData.date_of_birth,
occupation: personalData.occupation,
phone_number: personalData.phone
? (() => {
const digits = personalData.phone.replace(/\D/g, '')
const number = digits.startsWith('63')
? digits.slice(2) // strip country code if user typed +63...
: digits.replace(/^0/, '') // strip leading 0 if user typed 09...
return [{
number,
country_code: '63',
full_number: `63${number}`,
phone_type: 'mobile',
}]
})()
: [],
addresses: [],
},
personal_info: buildPersonalInfo(),
...(group_code ? { group_code } : {}),
...(confirmResume ? { confirm_resume: true } : {}),
})
if (!result.success) {
if (result.errors?.pendingVerification && !confirmResume) {
// Same email already has an unverified account sitting server-side —
// likely this same user retrying after a dropped connection. Ask
// before resending the OTP and reusing that account rather than
// dead-ending on a generic "already registered" error.
setPendingCreds({ email, password, group_code })
setResumeDialogOpen(true)
return
}
setErrorMessage(result.message)
setErrorDialogOpen(true)
return
@@ -228,6 +262,19 @@ export function RegisterForm({ className, ...props }) {
setStep(2)
}
const onCredentialsSubmit = (data) => submitRegistration(data)
const handleConfirmResume = async () => {
if (!pendingCreds) return
setIsResuming(true)
try {
await submitRegistration(pendingCreds, { confirmResume: true })
} finally {
setIsResuming(false)
setResumeDialogOpen(false)
}
}
// ─────────────────────────────────────────────────────────────────────────
return (
<>
@@ -576,6 +623,33 @@ export function RegisterForm({ className, ...props }) {
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog open={resumeDialogOpen} onOpenChange={(open) => !isResuming && setResumeDialogOpen(open)}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Pending verification found</AlertDialogTitle>
<AlertDialogDescription>
This email already started registration but was never verified — likely from a dropped
connection. Resend the verification code and continue with the details you just entered?
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<Button variant="outline" disabled={isResuming} onClick={() => setResumeDialogOpen(false)}>
Cancel
</Button>
<AlertDialogAction onClick={handleConfirmResume} disabled={isResuming}>
{isResuming ? (
<span className="flex items-center gap-2">
<LoaderCircle className="size-4 animate-spin" />
Sending...
</span>
) : (
'Resend code'
)}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
)
}