DONE: first init

This commit is contained in:
ISMAIL MASSERAN
2026-07-20 16:10:22 +08:00
commit 3189e3a1e3
272 changed files with 48853 additions and 0 deletions
@@ -0,0 +1,66 @@
.auth-container-reg *{
box-sizing: border-box;
}
.auth-container-reg{
font-family: "Poppins", sans-serif;
min-height: 100vh;
display: grid;
place-items: center;
}
.authForm-reg {
border: 1px solid black;
padding: 0px 40px 40px 40px;
width: 1000px;
background-color: white;
}
.QRCodeContainer {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
margin-bottom: 20px;
}
.QRCodeContainer h1 {
margin-bottom: 30px;
}
.inputs-reg {
display: grid;
gap: 20px;
grid-template-columns: repeat(6, 1fr);
margin-bottom: 20px;
}
.inputs-reg > * {
border: 1px solid black;
width: 100%;
padding: 40px;
text-align: center;
font-size: 40px;
line-height: 1;
}
button {
width: 100%;
margin-top: 20px;
padding: 24px;
background-color: #548CA8;
border: none;
color: white;
font-size: 32px;
font-weight: bold;
border-radius: 5px;
cursor: pointer;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: background-color 0.3s ease;
}
button:hover {
background-color: #334257;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}
@@ -0,0 +1,236 @@
import React, { useEffect, useRef, useReducer, useState } from "react";
import "./RegistrationAuth.css";
import { SERVER_URL } from "../../constants";
function doSubmit(submittedValues) {
console.log(`Submitted: ${submittedValues.join("")}`);
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1500);
});
}
function clampIndex(index) {
if (index > 6) {
return 6;
} else if (index < 0) {
return 0;
} else {
return index;
}
}
function reducer(state, action) {
switch (action.type) {
case "INPUT":
return {
...state,
inputValues: [
...state.inputValues.slice(0, action.payload.index),
action.payload.value,
...state.inputValues.slice(action.payload.index + 1)
],
focusedIndex: clampIndex(state.focusedIndex + 1)
};
case "BACK":
return {
...state,
focusedIndex: clampIndex(state.focusedIndex - 1)
};
case "PASTE":
return {
...state,
inputValues: state.inputValues.map(
(_, index) => action.payload.pastedValue[index] || ""
)
};
case "FOCUS":
return {
...state,
focusedIndex: action.payload.focusedIndex
};
case "VERIFY":
return {
...state,
status: "pending"
};
case "VERIFY_SUCCESS":
return {
...state,
status: "idle"
};
case "RESET_INPUTS":
return {
...state,
inputValues: Array(6).fill(""),
focusedIndex: 0,
status: "idle"
};
default:
throw new Error("unknown action");
}
}
const initialState = {
inputValues: Array(6).fill(""),
focusedIndex: 0,
status: "idle"
};
export default function RefistrationAuth( {qrCode, email} ) {
const [{ inputValues, focusedIndex, status }, dispatch] = useReducer(
reducer,
initialState
);
function handleInput(index, value) {
dispatch({ type: "INPUT", payload: { index, value } });
}
function handleBack() {
dispatch({ type: "BACK" });
}
function handlePaste(pastedValue) {
dispatch({ type: "PASTE", payload: { pastedValue } });
if (pastedValue.length === 6) {
dispatch({ type: "VERIFY" });
doSubmit(pastedValue.split("")).then(() =>
dispatch({ type: "VERIFY_SUCCESS" })
);
}
}
function handleFocus(focusedIndex) {
dispatch({ type: "FOCUS", payload: { focusedIndex } });
}
function handleSubmit(e) {
e.preventDefault();
dispatch({ type: "VERIFY" });
doSubmit(inputValues).then(() => dispatch({ type: "VERIFY_SUCCESS" }));
}
const checkInputValues = async () =>{
const enteredValues = inputValues.join("");
const requestBody = {
code: enteredValues,
email: email
}
try{
const response = await fetch(SERVER_URL + '/api/v1/auth/tfa', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
});
if (response.ok) {
dispatch({ type: "VERIFY_SUCCESS" });
} else {
throw new Error("Code could not be verified. It is incorrect or expired.");
}
} catch (error) {
alert("Code could not be verified. It is incorrect.");
resetInputs();
}
}
function resetInputs() {
dispatch({ type: "RESET_INPUTS" });
}
return (
<div className="auth-container-reg">
<form className="authForm-reg" onSubmit={handleSubmit}>
<div className="QRCodeContainer">
<h1>Scan QR code</h1>
<img src={qrCode} height='300px'></img>
</div>
<div className="inputs-reg">
{inputValues.map((value, index) => {
return (
<Input
key={index}
index={index}
value={value}
onChange={handleInput}
onBackspace={handleBack}
onPaste={handlePaste}
isFocused={index === focusedIndex}
onFocus={handleFocus}
isDisabled={status === "pending"}
/>
);
})}
</div>
<button onClick={checkInputValues} disabled={status === "pending"}>
{status === "pending" ? "VERIFYING..." : "VERIFY"}
</button>
</form>
</div>
);
}
function Input({
index,
value,
onChange,
onPaste,
onBackspace,
isFocused,
onFocus,
isDisabled
}) {
const ref = useRef();
useEffect(() => {
requestAnimationFrame(() => {
if (ref.current !== document.activeElement && isFocused) {
ref.current.focus();
}
});
}, [isFocused]);
function handleChange(e) {
onChange(index, e.target.value);
}
function handlePaste(e) {
onPaste(e.clipboardData.getData("text"));
}
function handleKeyDown(e) {
if (e.key === "Backspace") {
onBackspace();
}
}
function handleFocus(e) {
e.target.setSelectionRange(0, 1);
onFocus(index);
}
return (
<input
ref={ref}
type="text"
value={value}
onChange={handleChange}
onPaste={handlePaste}
onKeyDown={handleKeyDown}
maxLength="1"
onFocus={handleFocus}
disabled={isDisabled}
/>
);
}