import React, { useState, useEffect, useRef } from "react"
import type { ComponentType } from "react"
import { addPropertyControls, ControlType } from "framer"
import {
Eye,
EyeSlash,
LockSimple,
LockSimpleOpen,
ArrowUUpLeft,
} from "phosphor-react"
const ALLOWED_PASSWORDS = ["ABCD", "abcd", "Abcd"]
const TEXT_CONTENT = {
title: "PRIVATE CONTENT",
subtitle: "Enter passcode to continue (passcode: abcd)",
errorMessage: "Incorrect passcode. Please try again.",
buttonText: "Unlock",
returnButtonText: "Return to Projects",
}
const RETURN_URL = "https://www.google.com"
const STYLE_TOKENS = {
colors: {
background: "#FFFFFF",
text: "#000000",
primary: "#000000",
secondaryText: "#605D64",
buttonText: "#FFFFFF",
error: "#D8512A",
inputBorder: "#CCCCCC",
inputBorderFocus: "#000000",
inputBorderError: "#D8512A",
buttonHover: "#333333",
linkHover: "#333333",
},
fonts: {
heading: "Syncopate, sans-serif",
body: "General Sans, sans-serif",
button: "General Sans, sans-serif",
},
fontSizes: {
title: "clamp(1.25rem, 1.586vw + 1.151rem, 2rem)",
paragraph: "18px",
input: "16px",
button: "16px",
small: "14px",
},
fontWeights: {
normal: "500",
medium: "600",
bold: "800",
},
spacing: {
xs: "4px",
sm: "8px",
md: "12px",
lg: "24px",
xl: "32px",
xxl: "48px",
},
borderRadius: {
small: "8px",
},
container: {
maxWidth: "400px",
},
}
const styles = {
container: {
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
minHeight: "100vh",
backgroundColor: STYLE_TOKENS.colors.background,
fontFamily: STYLE_TOKENS.fonts.body,
color: STYLE_TOKENS.colors.text,
padding: STYLE_TOKENS.spacing.md,
},
header: {
marginBottom: STYLE_TOKENS.spacing.lg,
textAlign: "center",
},
title: {
fontSize: STYLE_TOKENS.fontSizes.title,
fontWeight: STYLE_TOKENS.fontWeights.bold,
fontFamily: STYLE_TOKENS.fonts.heading,
lineHeight: "125%",
marginBottom: STYLE_TOKENS.spacing.xs,
},
subtitle: {
fontFamily: STYLE_TOKENS.fonts.body,
fontWeight: STYLE_TOKENS.fontWeights.normal,
fontSize: STYLE_TOKENS.fontSizes.paragraph,
lineHeight: "150%",
margin: "0px",
color: STYLE_TOKENS.colors.secondaryText,
},
form: {
width: "100%",
maxWidth: STYLE_TOKENS.container.maxWidth,
},
inputContainer: {
position: "relative",
},
input: {
width: "100%",
fontFamily: STYLE_TOKENS.fonts.body,
fontSize: STYLE_TOKENS.fontSizes.paragraph,
fontWeight: STYLE_TOKENS.fontWeights.normal,
paddingTop: STYLE_TOKENS.spacing.md,
paddingBottom: STYLE_TOKENS.spacing.md,
paddingLeft: STYLE_TOKENS.spacing.md,
paddingRight: STYLE_TOKENS.spacing.xxl,
fontSize: STYLE_TOKENS.fontSizes.input,
border: `1px solid ${STYLE_TOKENS.colors.inputBorder}`,
borderRadius: STYLE_TOKENS.borderRadius.small,
outline: "none",
transition: "border-color 0.3s",
WebkitAppearance: "none",
MozAppearance: "none",
appearance: "none",
},
inputError: {
borderColor: STYLE_TOKENS.colors.inputBorderError,
},
showPasswordButton: {
position: "absolute",
right: STYLE_TOKENS.spacing.md,
top: "50%",
transform: "translateY(-50%)",
background: "none",
border: "none",
cursor: "pointer",
padding: "0",
},
button: {
width: "100%",
padding: STYLE_TOKENS.spacing.md,
marginTop: STYLE_TOKENS.spacing.lg,
fontFamily: STYLE_TOKENS.fonts.button,
fontSize: STYLE_TOKENS.fontSizes.button,
fontWeight: STYLE_TOKENS.fontWeights.medium,
color: STYLE_TOKENS.colors.buttonText,
backgroundColor: STYLE_TOKENS.colors.primary,
border: "none",
borderRadius: STYLE_TOKENS.borderRadius.small,
cursor: "pointer",
transition: "background-color 0.3s",
display: "flex",
alignItems: "center",
justifyContent: "center",
},
buttonHovered: {
backgroundColor: STYLE_TOKENS.colors.buttonHover,
},
error: {
color: STYLE_TOKENS.colors.error,
marginTop: STYLE_TOKENS.spacing.md,
fontSize: STYLE_TOKENS.fontSizes.small,
},
returnLink: {
marginTop: STYLE_TOKENS.spacing.xxl,
color: STYLE_TOKENS.colors.text,
textDecoration: "none",
fontSize: STYLE_TOKENS.fontSizes.small,
display: "flex",
alignItems: "center",
},
returnLinkHovered: {
color: STYLE_TOKENS.colors.linkHover,
},
}
export function requireAuth(Component): ComponentType {
return (props) => {
const [authenticated, setAuthenticated] = useState(false)
const [showPassword, setShowPassword] = useState(false)
const [errorMessage, setErrorMessage] = useState("")
const passwordRef = useRef(null)
const [loading, setLoading] = useState(true)
const [hovered, setHovered] = useState(false)
const validateAuth = (e) => {
e.preventDefault()
const inputPassword = e.target.elements.password.value
if (ALLOWED_PASSWORDS.includes(inputPassword)) {
setAuthenticated(true)
setErrorMessage("")
} else {
setAuthenticated(false)
setErrorMessage(TEXT_CONTENT.errorMessage)
}
e.target.elements.password.value = ""
}
useEffect(() => {
if (passwordRef.current) {
passwordRef.current.focus()
}
setTimeout(() => setLoading(false), 1000)
}, [])
if (!authenticated) {
return (
<div style={styles.container}>
<div style={styles.header}>
<h1 style={styles.title}>{TEXT_CONTENT.title}</h1>
<p style={styles.subtitle}>{TEXT_CONTENT.subtitle}</p>
</div>
<form onSubmit={validateAuth} style={styles.form}>
<div style={styles.inputContainer}>
<input
type={showPassword ? "text" : "password"}
name="password"
ref={passwordRef}
style={{
...styles.input,
...(errorMessage ? styles.inputError : {}),
}}
onFocus={(e) =>
(e.target.style.borderColor =
STYLE_TOKENS.colors.inputBorderFocus)
}
onBlur={(e) =>
(e.target.style.borderColor = errorMessage
? STYLE_TOKENS.colors.inputBorderError
: STYLE_TOKENS.colors.inputBorder)
}
autoComplete="new-password"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
style={styles.showPasswordButton}
>
{showPassword ? (
<EyeSlash
size={20}
color={
STYLE_TOKENS.colors.secondaryText
}
/>
) : (
<Eye
size={20}
color={
STYLE_TOKENS.colors.secondaryText
}
/>
)}
</button>
</div>
{errorMessage && (
<p style={styles.error}>{errorMessage}</p>
)}
<button
type="submit"
style={{
...styles.button,
...(hovered ? styles.buttonHovered : {}),
}}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{hovered ? (
<LockSimpleOpen size={20} />
) : (
<LockSimple size={20} />
)}
<span
style={{
marginLeft: STYLE_TOKENS.spacing.sm,
}}
>
{TEXT_CONTENT.buttonText}
</span>
</button>
</form>
<a
href={RETURN_URL}
style={styles.returnLink}
onMouseEnter={(e) =>
(e.currentTarget.style.color =
STYLE_TOKENS.colors.linkHover)
}
onMouseOut={(e) =>
(e.currentTarget.style.color =
STYLE_TOKENS.colors.text)
}
>
<ArrowUUpLeft
size={16}
style={{ marginRight: STYLE_TOKENS.spacing.sm }}
/>
{TEXT_CONTENT.returnButtonText}
</a>
</div>
)
}
return <Component {...props} />
}
}
addPropertyControls(requireAuth, {
})