Feature/phone register (#11)
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local> Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local> Reviewed-on: #11
This commit was merged in pull request #11.
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Box } from '@/components/ui/box'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Field, FieldGroup, FieldLabel } from '@/components/ui/field'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import {
|
||||
getPhoneVerificationErrorMessage,
|
||||
sendAuthenticatedPhoneVerificationOtp,
|
||||
verifyAuthenticatedPhoneVerificationOtp,
|
||||
} from '@/modules/auth'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const loading = ref(false)
|
||||
const changingPhone = ref(false)
|
||||
const otpSent = ref(false)
|
||||
const phoneNumber = ref(authStore.user?.phone_number ?? '')
|
||||
const otp = ref('')
|
||||
const feedbackMessage = ref('')
|
||||
const errorMessage = ref('')
|
||||
|
||||
const isVerified = computed(() => authStore.isPhoneVerified)
|
||||
|
||||
const sectionDescription = computed(() => {
|
||||
if (changingPhone.value) {
|
||||
return 'Masukkan nombor telefon baharu dan sahkan dengan kod OTP SMS.'
|
||||
}
|
||||
|
||||
if (isVerified.value) {
|
||||
return 'Nombor telefon anda telah disahkan.'
|
||||
}
|
||||
|
||||
return 'Sahkan nombor telefon anda melalui kod OTP SMS.'
|
||||
})
|
||||
|
||||
watch(
|
||||
() => authStore.user?.phone_number,
|
||||
(value) => {
|
||||
if (!otpSent.value && !changingPhone.value) {
|
||||
phoneNumber.value = value ?? ''
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
const resetOtpState = () => {
|
||||
otp.value = ''
|
||||
otpSent.value = false
|
||||
clearMessages()
|
||||
}
|
||||
|
||||
const clearMessages = () => {
|
||||
feedbackMessage.value = ''
|
||||
errorMessage.value = ''
|
||||
}
|
||||
|
||||
const handlePhoneInput = () => {
|
||||
phoneNumber.value = phoneNumber.value.replace(/[^\d+]/g, '')
|
||||
otp.value = ''
|
||||
otpSent.value = false
|
||||
}
|
||||
|
||||
const handleOtpInput = () => {
|
||||
otp.value = otp.value.replace(/\D/g, '').slice(0, 6)
|
||||
}
|
||||
|
||||
const startChangePhone = () => {
|
||||
changingPhone.value = true
|
||||
phoneNumber.value = ''
|
||||
resetOtpState()
|
||||
}
|
||||
|
||||
const cancelChangePhone = () => {
|
||||
changingPhone.value = false
|
||||
phoneNumber.value = authStore.user?.phone_number ?? ''
|
||||
resetOtpState()
|
||||
}
|
||||
|
||||
const handleSendOtp = async () => {
|
||||
loading.value = true
|
||||
clearMessages()
|
||||
|
||||
try {
|
||||
const response = await sendAuthenticatedPhoneVerificationOtp({
|
||||
phone_number: phoneNumber.value,
|
||||
})
|
||||
|
||||
feedbackMessage.value = response.message
|
||||
otpSent.value = true
|
||||
} catch (error) {
|
||||
errorMessage.value = getPhoneVerificationErrorMessage(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleVerifyOtp = async () => {
|
||||
loading.value = true
|
||||
clearMessages()
|
||||
|
||||
try {
|
||||
const response = await verifyAuthenticatedPhoneVerificationOtp({
|
||||
phone_number: phoneNumber.value,
|
||||
otp: otp.value,
|
||||
})
|
||||
|
||||
authStore.setUserProfile(response.data)
|
||||
phoneNumber.value = response.data.phone_number ?? phoneNumber.value
|
||||
changingPhone.value = false
|
||||
otp.value = ''
|
||||
otpSent.value = false
|
||||
feedbackMessage.value = response.message
|
||||
} catch (error) {
|
||||
errorMessage.value = getPhoneVerificationErrorMessage(error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Box raised="single" class="p-6">
|
||||
<div class="mb-6">
|
||||
<h3 class="text-lg font-semibold text-slate-900">Nombor Telefon</h3>
|
||||
<p class="mt-1 text-sm text-slate-500">
|
||||
{{ sectionDescription }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-if="isVerified && !changingPhone" class="space-y-4">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<Input id="profile-phone-verified" :model-value="authStore.user?.phone_number ?? '-'" type="tel" disabled
|
||||
class="max-w-sm" />
|
||||
<Badge variant="success">Disahkan</Badge>
|
||||
</div>
|
||||
|
||||
<p v-if="feedbackMessage" class="text-sm text-slate-600">{{ feedbackMessage }}</p>
|
||||
|
||||
<Button type="button" look="outline" :disabled="loading" @click="startChangePhone">
|
||||
Tukar Nombor Telefon
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-4">
|
||||
<FieldGroup>
|
||||
<Field class="max-w-sm">
|
||||
<FieldLabel for="profile-phone-verify">No. Telefon</FieldLabel>
|
||||
<Input id="profile-phone-verify" v-model="phoneNumber" type="tel" inputmode="tel"
|
||||
placeholder="No. Telefon, contoh: 0123456790" autocomplete="tel" :disabled="loading || otpSent"
|
||||
@input="handlePhoneInput" />
|
||||
</Field>
|
||||
<Field v-if="otpSent" class="max-w-sm">
|
||||
<FieldLabel for="profile-phone-otp">Kod OTP</FieldLabel>
|
||||
<Input id="profile-phone-otp" v-model="otp" type="text" inputmode="numeric" maxlength="6" placeholder="000000"
|
||||
autocomplete="one-time-code" class="text-center tracking-[0.4em]" :disabled="loading"
|
||||
@input="handleOtpInput" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
|
||||
<p v-if="feedbackMessage" class="text-sm text-slate-600">{{ feedbackMessage }}</p>
|
||||
<p v-if="errorMessage" class="text-sm text-danger">{{ errorMessage }}</p>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<Button v-if="!otpSent" type="button" variant="primary" :disabled="loading || !phoneNumber"
|
||||
@click="handleSendOtp">
|
||||
{{ loading ? 'Menghantar...' : 'Hantar Kod OTP' }}
|
||||
</Button>
|
||||
<template v-else>
|
||||
<Button type="button" variant="primary" :disabled="loading || otp.length !== 6" @click="handleVerifyOtp">
|
||||
{{ loading ? 'Mengesahkan...' : 'Sahkan OTP' }}
|
||||
</Button>
|
||||
<Button type="button" look="outline" :disabled="loading" @click="handleSendOtp">
|
||||
Hantar Semula OTP
|
||||
</Button>
|
||||
</template>
|
||||
<Button v-if="changingPhone" type="button" look="outline" :disabled="loading" @click="cancelChangePhone">
|
||||
Batal
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
</template>
|
||||
Reference in New Issue
Block a user