b05e074456
Co-authored-by: ISMAIL MASSERAN <topaz@ISMAILs-Macbook.local> Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local> Reviewed-on: #11
185 lines
5.5 KiB
Vue
185 lines
5.5 KiB
Vue
<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>
|