mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 17:38:12 +00:00
feat(auth): Implement phone number login and introduce PhoneInput component
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
import { Field, FieldLabel, FieldError, Input } from "@edr/ui-common";
|
||||||
|
|
||||||
|
interface PhoneInputProps {
|
||||||
|
disabled?: boolean;
|
||||||
|
countryCode?: React.ComponentProps<typeof Input>;
|
||||||
|
phone?: React.ComponentProps<typeof Input>;
|
||||||
|
countryCodeError?: { message?: string };
|
||||||
|
phoneError?: { message?: string };
|
||||||
|
label?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PhoneInput({
|
||||||
|
disabled,
|
||||||
|
countryCode: countryCodeProps,
|
||||||
|
phone: phoneProps,
|
||||||
|
countryCodeError,
|
||||||
|
phoneError,
|
||||||
|
label = "Phone Number",
|
||||||
|
}: PhoneInputProps) {
|
||||||
|
return (
|
||||||
|
<Field data-invalid={Boolean(countryCodeError || phoneError)}>
|
||||||
|
<FieldLabel>{label}</FieldLabel>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
disabled={disabled}
|
||||||
|
className="w-20"
|
||||||
|
aria-invalid={Boolean(countryCodeError)}
|
||||||
|
{...countryCodeProps}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
type="tel"
|
||||||
|
placeholder="912345678"
|
||||||
|
disabled={disabled}
|
||||||
|
className="flex-1"
|
||||||
|
aria-invalid={Boolean(phoneError)}
|
||||||
|
{...phoneProps}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<FieldError errors={[countryCodeError, phoneError]} />
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
FieldError,
|
FieldError,
|
||||||
FieldGroup,
|
FieldGroup,
|
||||||
} from "@edr/ui-common";
|
} from "@edr/ui-common";
|
||||||
|
import PhoneInput from "@/components/auth/PhoneInput";
|
||||||
|
|
||||||
type LoginMethod = "email" | "phone";
|
type LoginMethod = "email" | "phone";
|
||||||
|
|
||||||
@@ -19,6 +20,8 @@ export default function LoginPage() {
|
|||||||
const { login } = useAuth();
|
const { login } = useAuth();
|
||||||
const [method, setMethod] = useState<LoginMethod>("email");
|
const [method, setMethod] = useState<LoginMethod>("email");
|
||||||
const [identifier, setIdentifier] = useState("");
|
const [identifier, setIdentifier] = useState("");
|
||||||
|
const [countryCode, setCountryCode] = useState("+251");
|
||||||
|
const [phoneNumber, setPhoneNumber] = useState("");
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -28,7 +31,10 @@ export default function LoginPage() {
|
|||||||
setError(null);
|
setError(null);
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const result = await login({ email: identifier, password });
|
const loginId = method === "email"
|
||||||
|
? identifier
|
||||||
|
: `${countryCode}${phoneNumber.startsWith("0") ? phoneNumber.slice(1) : phoneNumber}`;
|
||||||
|
const result = await login({ email: loginId, password });
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
navigate("/");
|
navigate("/");
|
||||||
} else {
|
} else {
|
||||||
@@ -97,19 +103,31 @@ export default function LoginPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FieldGroup>
|
<FieldGroup>
|
||||||
<Field>
|
{method === "email" ? (
|
||||||
<FieldLabel>
|
<Field>
|
||||||
{method === "email" ? "Email Address" : "Phone Number"}
|
<FieldLabel>Email Address</FieldLabel>
|
||||||
</FieldLabel>
|
<Input
|
||||||
<Input
|
type="email"
|
||||||
type={method === "email" ? "email" : "tel"}
|
placeholder="name@company.com"
|
||||||
placeholder={method === "email" ? "name@company.com" : "+251..."}
|
value={identifier}
|
||||||
value={identifier}
|
onChange={(e) => setIdentifier(e.target.value)}
|
||||||
onChange={(e) => setIdentifier(e.target.value)}
|
required
|
||||||
required
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
) : (
|
||||||
|
<PhoneInput
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
|
countryCode={{
|
||||||
|
value: countryCode,
|
||||||
|
onChange: (e: React.ChangeEvent<HTMLInputElement>) => setCountryCode(e.target.value),
|
||||||
|
}}
|
||||||
|
phone={{
|
||||||
|
value: phoneNumber,
|
||||||
|
onChange: (e: React.ChangeEvent<HTMLInputElement>) => setPhoneNumber(e.target.value),
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Field>
|
)}
|
||||||
|
|
||||||
<Field>
|
<Field>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { userType } from "@/enums/userType";
|
|||||||
import useAuth from "@/hooks/useAuth";
|
import useAuth from "@/hooks/useAuth";
|
||||||
import type { SignupPayload } from "@/types/auth";
|
import type { SignupPayload } from "@/types/auth";
|
||||||
import AuthLayout from "@/components/auth/AuthLayout";
|
import AuthLayout from "@/components/auth/AuthLayout";
|
||||||
|
import PhoneInput from "@/components/auth/PhoneInput";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Input,
|
Input,
|
||||||
@@ -150,27 +151,13 @@ export default function SignupPage() {
|
|||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Field data-invalid={Boolean(errors.countryCode || errors.phone)}>
|
<PhoneInput
|
||||||
<FieldLabel>Phone Number</FieldLabel>
|
disabled={loading}
|
||||||
<div className="flex gap-2">
|
countryCode={{ ...register("countryCode") }}
|
||||||
<Input
|
phone={{ ...register("phone") }}
|
||||||
type="text"
|
countryCodeError={errors.countryCode}
|
||||||
disabled={loading}
|
phoneError={errors.phone}
|
||||||
className="w-20"
|
/>
|
||||||
aria-invalid={Boolean(errors.countryCode)}
|
|
||||||
{...register("countryCode")}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
type="tel"
|
|
||||||
placeholder="912345678"
|
|
||||||
disabled={loading}
|
|
||||||
className="flex-1"
|
|
||||||
aria-invalid={Boolean(errors.phone)}
|
|
||||||
{...register("phone")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<FieldError errors={[errors.countryCode, errors.phone]} />
|
|
||||||
</Field>
|
|
||||||
</FieldGroup>
|
</FieldGroup>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
Reference in New Issue
Block a user