Files
rentall-app/frontend/src/components/AlphaGate.tsx
jackiettran ee3a6fd8e1 alpha
2025-10-30 15:38:57 -04:00

216 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from "react";
import axios from "axios";
const API_URL = process.env.REACT_APP_API_URL || "http://localhost:5001";
const AlphaGate: React.FC = () => {
const [code, setCode] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const handleCodeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value.toUpperCase();
// Only allow alphanumeric, max 8 characters
if (value.length <= 8 && /^[A-Z0-9]*$/.test(value)) {
setCode(value);
}
setError("");
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
if (code.length < 8) {
setError("Please enter a complete alpha access code");
return;
}
setLoading(true);
const fullCode = `ALPHA-${code}`;
try {
const response = await axios.post(
`${API_URL}/alpha/validate-code`,
{ code: fullCode },
{ withCredentials: true }
);
if (response.data.success) {
// Store alpha verification in localStorage as backup
localStorage.setItem("alphaVerified", "true");
// Reload the page to trigger access check
window.location.reload();
}
} catch (err: any) {
if (err.response?.status === 429) {
setError("Too many attempts. Please try again in a few minutes.");
} else if (err.response?.data?.error) {
setError(err.response.data.error);
} else {
setError("Failed to validate code. Please try again.");
}
} finally {
setLoading(false);
}
};
return (
<div
style={{
minHeight: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
padding: "20px",
}}
>
<div
className="card shadow-lg"
style={{ maxWidth: "500px", width: "100%" }}
>
<div className="card-body p-5">
<div className="text-center mb-4">
<h1 className="h2 mb-3" style={{ color: "#667eea" }}>
Community Rentals
</h1>
</div>
<div className="mb-4">
<ul
className="text-start text-muted mb-3"
style={{ fontSize: "0.95rem" }}
>
<li className="mb-2">
<strong>Earn</strong> extra income with the stuff you already
have
</li>
<li className="mb-2">
<strong>Find</strong> items for special events, weekend
projects, family trips and more
</li>
<li className="mb-2">
<strong>Discover</strong> affordable options
</li>
</ul>
</div>
<div className="mb-4">
<h6
className="fw-bold mb-2 text-center"
style={{ color: "#667eea" }}
>
Currently in Alpha Testing!
</h6>
<p className="text-muted small mb-0 text-center">
You're among the first to try Community Rentals! Help us create
something special by sharing your thoughts as we build this
together.
</p>
</div>
<div className="mb-4">
<p className="text-center text-muted small mb-0">
Have an alpha code? Get started below! <br></br> Want to join?{" "}
<a
href="mailto:support@communityrentals.app?subject=Alpha Access Request"
className="text-decoration-none"
style={{ color: "#667eea" }}
>
Request access
</a>
</p>
</div>
<form onSubmit={handleSubmit}>
<div className="mb-3">
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: "0.5rem",
}}
>
{/* Static ALPHA- text */}
<span
style={{
fontFamily: "monospace",
fontSize: "1.1rem",
fontWeight: "400",
letterSpacing: "2px",
color: "#495057",
}}
>
ALPHA-
</span>
{/* Input for 8 characters */}
<input
type="text"
className={`form-control form-control-lg ${
error ? "border-danger" : ""
}`}
placeholder="XXXXXXXX"
value={code}
onChange={handleCodeChange}
disabled={loading}
style={{
fontFamily: "monospace",
letterSpacing: "2px",
fontSize: "1.1rem",
textAlign: "center",
width: "calc(8ch + 16px + 2rem)",
}}
maxLength={8}
autoFocus
/>
{/* Error icon outside the input */}
{error && (
<span
style={{
color: "#dc3545",
fontSize: "1.5rem",
lineHeight: 1,
}}
>
</span>
)}
</div>
</div>
<button
type="submit"
className="btn btn-primary btn-lg w-100"
disabled={loading || code.length < 8}
style={{
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
border: "none",
}}
>
{loading ? (
<>
<span
className="spinner-border spinner-border-sm me-2"
role="status"
aria-hidden="true"
></span>
Validating...
</>
) : (
"Enter"
)}
</button>
</form>
</div>
</div>
</div>
);
};
export default AlphaGate;