mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 09:58:12 +00:00
fix
This commit is contained in:
@@ -34,8 +34,6 @@ import { ControlledPhoneField, isValidPhone } from "@/components/PhoneField";
|
|||||||
import { api } from "@/services/api";
|
import { api } from "@/services/api";
|
||||||
import { extractApiError } from "@/utils/result";
|
import { extractApiError } from "@/utils/result";
|
||||||
|
|
||||||
const EDR_LOGO = "/assets/edr-logo.png";
|
|
||||||
|
|
||||||
const passwordRequirements = [
|
const passwordRequirements = [
|
||||||
{ label: "At least 8 characters", test: (v: string) => v.length >= 8 },
|
{ label: "At least 8 characters", test: (v: string) => v.length >= 8 },
|
||||||
{ label: "One uppercase letter", test: (v: string) => /[A-Z]/.test(v) },
|
{ label: "One uppercase letter", test: (v: string) => /[A-Z]/.test(v) },
|
||||||
@@ -244,259 +242,270 @@ export default function SignupPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthShell
|
<AuthShell
|
||||||
tagline="Smart Freight Operations"
|
tagline= "Smart Freight Operations"
|
||||||
taglineBody="Join EDR Freight to manage shipments, track consignments, and streamline logistics workflows across Ethiopia and Djibouti."
|
taglineBody = "Join EDR Freight to manage shipments, track consignments, and streamline logistics workflows across Ethiopia and Djibouti."
|
||||||
>
|
>
|
||||||
<div className="flex w-full flex-col">
|
<div className="flex w-full flex-col" >
|
||||||
{stage === "form" ? (
|
{ stage === "form" ? (
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit(requestOtp)}
|
onSubmit= { handleSubmit(requestOtp) }
|
||||||
className="flex w-full flex-col"
|
className = "flex w-full flex-col"
|
||||||
>
|
>
|
||||||
<div className="mb-4 space-y-1.5 text-center sm:mb-5">
|
<div className="mb-4 space-y-1.5 text-center sm:mb-5" >
|
||||||
<h1 className="text-xl font-bold tracking-tight text-gray-900 sm:text-2xl">
|
<h1 className="text-xl font-bold tracking-tight text-gray-900 sm:text-2xl" >
|
||||||
Create account
|
Create account
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm leading-relaxed text-gray-500">
|
< p className = "text-sm leading-relaxed text-gray-500" >
|
||||||
Register to access EDR Freight services.
|
Register to access EDR Freight services.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Stack gap="sm">
|
< Stack gap = "sm" >
|
||||||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
|
<SimpleGrid cols={ { base: 1, sm: 2 } } spacing = "md" >
|
||||||
<TextInput
|
<TextInput
|
||||||
label="First name"
|
label="First name"
|
||||||
placeholder="John"
|
placeholder = "John"
|
||||||
required
|
required
|
||||||
disabled={sending}
|
disabled = { sending }
|
||||||
error={errors.firstName?.en?.message}
|
error = { errors.firstName?.en?.message }
|
||||||
{...register("firstName.en")}
|
{...register("firstName.en") }
|
||||||
/>
|
/>
|
||||||
<TextInput
|
< TextInput
|
||||||
label="Last name"
|
label = "Last name"
|
||||||
placeholder="Doe"
|
placeholder = "Doe"
|
||||||
required
|
required
|
||||||
disabled={sending}
|
disabled = { sending }
|
||||||
error={errors.lastName?.en?.message}
|
error = { errors.lastName?.en?.message }
|
||||||
{...register("lastName.en")}
|
{...register("lastName.en") }
|
||||||
/>
|
/>
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
|
|
||||||
<TextInput
|
< TextInput
|
||||||
label="Email"
|
label = "Email"
|
||||||
type="email"
|
type = "email"
|
||||||
placeholder="john@example.com"
|
placeholder = "john@example.com"
|
||||||
required
|
required
|
||||||
disabled={sending}
|
disabled = { sending }
|
||||||
error={errors.email?.message}
|
error = { errors.email?.message }
|
||||||
{...register("email")}
|
{...register("email") }
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ControlledPhoneField
|
< ControlledPhoneField
|
||||||
control={control}
|
control = { control }
|
||||||
name="phone"
|
name = "phone"
|
||||||
label="Phone"
|
label = "Phone"
|
||||||
required
|
required
|
||||||
disabled={sending}
|
disabled = { sending }
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5" >
|
||||||
<Text size="sm" fw={500} c="edr-text">
|
<Text size="sm" fw = { 500} c = "edr-text" >
|
||||||
Send verification code via
|
Send verification code via
|
||||||
</Text>
|
</Text>
|
||||||
<SegmentedControl
|
< SegmentedControl
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled={sending}
|
disabled = { sending }
|
||||||
value={channel}
|
value = { channel }
|
||||||
onChange={(v) => setChannel(v as OtpChannel)}
|
onChange = {(v) => setChannel(v as OtpChannel)
|
||||||
data={[
|
}
|
||||||
{
|
data = {
|
||||||
value: "phone",
|
[
|
||||||
label: (
|
{
|
||||||
<span className="flex items-center justify-center gap-1.5">
|
value: "phone",
|
||||||
<Smartphone size={14} /> Phone
|
label: (
|
||||||
</span>
|
<span className= "flex items-center justify-center gap-1.5" >
|
||||||
|
<Smartphone size={ 14} /> Phone
|
||||||
|
</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "email",
|
value: "email",
|
||||||
label: (
|
label: (
|
||||||
<span className="flex items-center justify-center gap-1.5">
|
<span className= "flex items-center justify-center gap-1.5" >
|
||||||
<Mail size={14} /> Email
|
<Mail size={ 14 } /> Email
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
< div >
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label="Password"
|
label="Password"
|
||||||
placeholder="Create a strong password"
|
placeholder = "Create a strong password"
|
||||||
required
|
required
|
||||||
disabled={sending}
|
disabled = { sending }
|
||||||
error={errors.password?.message}
|
error = { errors.password?.message }
|
||||||
{...register("password")}
|
{...register("password") }
|
||||||
/>
|
/>
|
||||||
{passwordValue.length > 0 ? (
|
{
|
||||||
<div className="mt-2 space-y-1">
|
passwordValue.length > 0 ? (
|
||||||
{passwordRequirements.map((req) => {
|
<div className= "mt-2 space-y-1" >
|
||||||
const met = req.test(passwordValue);
|
{
|
||||||
return (
|
passwordRequirements.map((req) => {
|
||||||
<div
|
const met = req.test(passwordValue);
|
||||||
key={req.label}
|
return (
|
||||||
className="flex items-center gap-2"
|
<div
|
||||||
>
|
key= { req.label }
|
||||||
<span
|
className = "flex items-center gap-2"
|
||||||
className={`flex h-4 w-4 shrink-0 items-center justify-center rounded-full ${met
|
>
|
||||||
? "bg-primary text-primary-foreground"
|
<span
|
||||||
: "bg-gray-200 text-gray-500"
|
className={
|
||||||
}`}
|
`flex h-4 w-4 shrink-0 items-center justify-center rounded-full ${met
|
||||||
|
? "bg-primary text-primary-foreground"
|
||||||
|
: "bg-gray-200 text-gray-500"
|
||||||
|
}`
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{met ? (
|
{
|
||||||
<Check className="h-2.5 w-2.5" />
|
met?(
|
||||||
) : (
|
<Check className = "h-2.5 w-2.5" />
|
||||||
<X className="h-2.5 w-2.5" />
|
): (
|
||||||
)}
|
<X className = "h-2.5 w-2.5" />
|
||||||
</span>
|
)
|
||||||
<span
|
}
|
||||||
className={`text-xs ${met ? "text-primary" : "text-gray-500"}`}
|
</span>
|
||||||
|
< span
|
||||||
|
className = {`text-xs ${met ? "text-primary" : "text-gray-500"}`
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{req.label}
|
{ req.label }
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PasswordInput
|
< PasswordInput
|
||||||
label="Confirm password"
|
label = "Confirm password"
|
||||||
placeholder="Re-enter your password"
|
placeholder = "Re-enter your password"
|
||||||
required
|
required
|
||||||
disabled={sending}
|
disabled = { sending }
|
||||||
error={errors.confirmPassword?.message}
|
error = { errors.confirmPassword?.message }
|
||||||
{...register("confirmPassword")}
|
{...register("confirmPassword") }
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{error ? (
|
{
|
||||||
<Alert
|
error ? (
|
||||||
color="red"
|
<Alert
|
||||||
variant="light"
|
color= "red"
|
||||||
icon={<AlertCircle size={18} />}
|
variant = "light"
|
||||||
|
icon = {< AlertCircle size = { 18} />}
|
||||||
>
|
>
|
||||||
{error}
|
{ error }
|
||||||
</Alert>
|
</Alert>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
color="edr-green"
|
color = "edr-green"
|
||||||
fullWidth
|
fullWidth
|
||||||
loading={sending}
|
loading = { sending }
|
||||||
rightSection={!sending ? <ArrowRight size={16} /> : undefined}
|
rightSection = {!sending ? <ArrowRight size={ 16 } /> : undefined}
|
||||||
>
|
>
|
||||||
Continue
|
Continue
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<p className="text-center text-sm text-gray-500">
|
< p className = "text-center text-sm text-gray-500" >
|
||||||
Already have an account?{" "}
|
Already have an account ? { " "}
|
||||||
<button
|
< button
|
||||||
type="button"
|
type = "button"
|
||||||
onClick={() => navigate("/login")}
|
onClick = {() => navigate("/login")}
|
||||||
className="font-semibold text-primary hover:underline"
|
className = "font-semibold text-primary hover:underline"
|
||||||
>
|
>
|
||||||
Sign In
|
Sign In
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</Stack>
|
</Stack>
|
||||||
</form>
|
</form>
|
||||||
) : (
|
) : (
|
||||||
<Stack gap="md">
|
<Stack gap= "md" >
|
||||||
<div className="mb-1 flex justify-center">
|
<div className="mb-1 flex justify-center" >
|
||||||
<span className="flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 text-primary">
|
<span className="flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 text-primary" >
|
||||||
<ShieldCheck size={22} />
|
<ShieldCheck size={ 22 } />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5 text-center">
|
< div className = "space-y-1.5 text-center" >
|
||||||
<h1 className="text-xl font-bold tracking-tight text-gray-900 sm:text-2xl">
|
<h1 className="text-xl font-bold tracking-tight text-gray-900 sm:text-2xl" >
|
||||||
Verify your {otpChannel === "email" ? "email" : "phone"}
|
Verify your { otpChannel === "email" ? "email" : "phone" }
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm leading-relaxed text-gray-500">
|
< p className = "text-sm leading-relaxed text-gray-500" >
|
||||||
We sent a 6-digit code to{" "}
|
We sent a 6 - digit code to{ " " }
|
||||||
<span className="font-medium text-gray-700">
|
<span className="font-medium text-gray-700" >
|
||||||
{otpChannel === "email"
|
{ otpChannel === "email"
|
||||||
? maskEmail(pendingData?.email ?? "")
|
? maskEmail(pendingData?.email ?? "")
|
||||||
: maskPhone(pendingData?.phone ?? "")}
|
: maskPhone(pendingData?.phone ?? "")}
|
||||||
</span>
|
</span>
|
||||||
. Enter it to finish creating your account.
|
.Enter it to finish creating your account.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{otpError ? (
|
{
|
||||||
<Alert
|
otpError ? (
|
||||||
color="red"
|
<Alert
|
||||||
variant="light"
|
color= "red"
|
||||||
icon={<AlertCircle size={18} />}
|
variant = "light"
|
||||||
|
icon = {< AlertCircle size = { 18} />}
|
||||||
>
|
>
|
||||||
{otpError}
|
{ otpError }
|
||||||
</Alert>
|
</Alert>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<Stack gap={6} align="center">
|
<Stack gap={ 6 } align = "center" >
|
||||||
<Text size="sm" fw={500} c="edr-text">
|
<Text size="sm" fw = { 500} c = "edr-text" >
|
||||||
Verification code
|
Verification code
|
||||||
</Text>
|
</Text>
|
||||||
<PinInput
|
< PinInput
|
||||||
length={6}
|
length = { 6}
|
||||||
type="number"
|
type = "number"
|
||||||
oneTimeCode
|
oneTimeCode
|
||||||
value={otpCode}
|
value = { otpCode }
|
||||||
placeholder="0"
|
placeholder = "0"
|
||||||
disabled={verifying}
|
disabled = { verifying }
|
||||||
styles={{ input: { textAlign: "center" } }}
|
styles = {{ input: { textAlign: "center" } }}
|
||||||
onChange={setOtpCode}
|
onChange = { setOtpCode }
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Button
|
< Button
|
||||||
color="edr-green"
|
color = "edr-green"
|
||||||
fullWidth
|
fullWidth
|
||||||
loading={verifying}
|
loading = { verifying }
|
||||||
disabled={verifying || otpCode.trim().length !== 6}
|
disabled = { verifying || otpCode.trim().length !== 6}
|
||||||
onClick={confirmOtp}
|
onClick = { confirmOtp }
|
||||||
>
|
>
|
||||||
Verify & create account
|
Verify & amp; create account
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
< div className = "flex items-center justify-between" >
|
||||||
<Button
|
<Button
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
color="gray"
|
color = "gray"
|
||||||
leftSection={<ArrowLeft size={14} />}
|
leftSection = {< ArrowLeft size = { 14} />}
|
||||||
disabled={sending || verifying}
|
disabled = { sending || verifying}
|
||||||
onClick={() => {
|
onClick = {() => {
|
||||||
setStage("form");
|
setStage("form");
|
||||||
setOtpError(null);
|
setOtpError(null);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
< Button
|
||||||
variant="subtle"
|
variant = "subtle"
|
||||||
color="edr-green"
|
color = "edr-green"
|
||||||
leftSection={<RotateCw size={14} />}
|
leftSection = {< RotateCw size = { 14} />}
|
||||||
disabled={resendIn > 0 || sending || verifying}
|
disabled = { resendIn > 0 || sending || verifying}
|
||||||
onClick={resendOtp}
|
onClick = { resendOtp }
|
||||||
>
|
>
|
||||||
{resendIn > 0 ? `Resend in ${resendIn}s` : "Resend code"}
|
{ resendIn > 0 ? `Resend in ${resendIn}s` : "Resend code"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</AuthShell>
|
</AuthShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export const authService = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
checkAvailability: async (params: CheckAvailabilityPayload) => {
|
checkAvailability: async (params: CheckAvailabilityPayload) => {
|
||||||
const res = await client.get<ApiResponse<CheckAvailabilityResponse>>(
|
const res = await client.get<CheckAvailabilityResponse>(
|
||||||
URL_CONSTANTS.USERS.CHECK_AVAILABILITY,
|
URL_CONSTANTS.USERS.CHECK_AVAILABILITY,
|
||||||
{ params },
|
{ params },
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user