mirror of
https://github.com/ramvignesh-b/pi-ku.git
synced 2026-05-04 08:56:52 +00:00
feat: implement registration page with form validation and add custom typography fonts
This commit is contained in:
@@ -5,6 +5,11 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "frontend",
|
"name": "frontend",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fontsource-variable/jost": "^5.2.8",
|
||||||
|
"@fontsource-variable/playfair-display": "^5.2.8",
|
||||||
|
"@fontsource-variable/playwrite-hr-lijeva": "^5.2.7",
|
||||||
|
"@fontsource/cutive-mono": "^5.2.8",
|
||||||
|
"@fontsource/knewave": "^5.2.7",
|
||||||
"@hookform/resolvers": "^5.2.2",
|
"@hookform/resolvers": "^5.2.2",
|
||||||
"@phosphor-icons/react": "^2.1.10",
|
"@phosphor-icons/react": "^2.1.10",
|
||||||
"@tailwindcss/vite": "^4.2.2",
|
"@tailwindcss/vite": "^4.2.2",
|
||||||
@@ -53,6 +58,16 @@
|
|||||||
|
|
||||||
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
|
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
|
||||||
|
|
||||||
|
"@fontsource-variable/jost": ["@fontsource-variable/jost@5.2.8", "", {}, "sha512-+VDDHpbhgZ9A8KeHb7/LUMRR1LILVKIscNhVRSDzn3tFXoi1mFA/OhO8CZL/u2OujoGjZANjOdUZIgaxclxyjw=="],
|
||||||
|
|
||||||
|
"@fontsource-variable/playfair-display": ["@fontsource-variable/playfair-display@5.2.8", "", {}, "sha512-ZzVIXPOrL85yyOvZYoBzUszIJM+xKkHqni4IYn2CVLaGQQdJR8sBeC8yFNgjxSJ7ludTwta8qpULeOFuk5X75A=="],
|
||||||
|
|
||||||
|
"@fontsource-variable/playwrite-hr-lijeva": ["@fontsource-variable/playwrite-hr-lijeva@5.2.7", "", {}, "sha512-cQqbD8HHZDpiKdtgwUxgwAY76TC+GI9iZOxHSW0XkV/L8lA0X18z1wzR+J8yv9XZQYgLJ5WfzBGwzMSLnSLdPA=="],
|
||||||
|
|
||||||
|
"@fontsource/cutive-mono": ["@fontsource/cutive-mono@5.2.8", "", {}, "sha512-Y8PKAYfbpl9Empbb1HZBoirlj4W7RtU+G4EhvX27pHzO6RE1sO0I1ElZQH5DMCTS+MSJkMmQT33sJ0+Ji9U8eQ=="],
|
||||||
|
|
||||||
|
"@fontsource/knewave": ["@fontsource/knewave@5.2.7", "", {}, "sha512-uzx8jgcTiQgAwKvQ/hWdX7lOQPwS+K74Eij/WCVzYvAkCX7GRTnWnbxXXx0XsKR6UIN16kH/u40LW4K8aHJb1w=="],
|
||||||
|
|
||||||
"@hookform/resolvers": ["@hookform/resolvers@5.2.2", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA=="],
|
"@hookform/resolvers": ["@hookform/resolvers@5.2.2", "", { "dependencies": { "@standard-schema/utils": "^0.3.0" }, "peerDependencies": { "react-hook-form": "^7.55.0" } }, "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA=="],
|
||||||
|
|
||||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||||
|
|||||||
@@ -12,6 +12,11 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fontsource-variable/jost": "^5.2.8",
|
||||||
|
"@fontsource-variable/playfair-display": "^5.2.8",
|
||||||
|
"@fontsource-variable/playwrite-hr-lijeva": "^5.2.7",
|
||||||
|
"@fontsource/cutive-mono": "^5.2.8",
|
||||||
|
"@fontsource/knewave": "^5.2.7",
|
||||||
"@hookform/resolvers": "^5.2.2",
|
"@hookform/resolvers": "^5.2.2",
|
||||||
"@phosphor-icons/react": "^2.1.10",
|
"@phosphor-icons/react": "^2.1.10",
|
||||||
"@tailwindcss/vite": "^4.2.2",
|
"@tailwindcss/vite": "^4.2.2",
|
||||||
|
|||||||
@@ -167,7 +167,7 @@
|
|||||||
|
|
||||||
&::before,
|
&::before,
|
||||||
&::after {
|
&::after {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -4.5px;
|
top: -4.5px;
|
||||||
border: 5px solid transparent;
|
border: 5px solid transparent;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
|
import { BrowserRouter, Navigate, Route, Routes } from "react-router-dom";
|
||||||
import Home from './pages/Home';
|
import Activate from "./pages/Activate";
|
||||||
import Register from './pages/Register';
|
import Home from "./pages/Home";
|
||||||
import VerifyEmail from './pages/VerifyEmail';
|
import Register from "./pages/Register";
|
||||||
import Activate from './pages/Activate';
|
import VerifyEmail from "./pages/VerifyEmail";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
@@ -10,7 +10,7 @@ export default function App() {
|
|||||||
<div className="min-h-screen bg-base-200 p-8 flex items-center justify-center">
|
<div className="min-h-screen bg-base-200 p-8 flex items-center justify-center">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route path="/register" element={<Register />} />
|
<Route path="/onboard" element={<Register />} />
|
||||||
<Route path="/verify-email" element={<VerifyEmail />} />
|
<Route path="/verify-email" element={<VerifyEmail />} />
|
||||||
<Route path="/activate/:uidb64/:token" element={<Activate />} />
|
<Route path="/activate/:uidb64/:token" element={<Activate />} />
|
||||||
<Route path="*" element={<Navigate to="/" replace />} />
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { DotIcon } from "@phosphor-icons/react"
|
||||||
|
import "@fontsource/knewave"
|
||||||
|
|
||||||
|
export default function Logo() {
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-baseline leading-none align-baseline">
|
||||||
|
<span className="text-2xl font-light font-logo text-accent"> Pi</span>
|
||||||
|
<DotIcon weight="fill" size={12} className="text-accent translate-y-px" />
|
||||||
|
<span className="text-2xl font-light font-logo text-accent ml-0.5">Ku</span>
|
||||||
|
<DotIcon weight="fill" size={12} className="text-accent translate-y-px" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
+17
-3
@@ -14,8 +14,8 @@
|
|||||||
--color-primary-content: #c8b890;
|
--color-primary-content: #c8b890;
|
||||||
--color-secondary: #2a1e13;
|
--color-secondary: #2a1e13;
|
||||||
--color-secondary-content: #e8d5b0;
|
--color-secondary-content: #e8d5b0;
|
||||||
--color-accent: #3d2e1e;
|
--color-accent: #c8903a;
|
||||||
--color-accent-content: #c8903a;
|
--color-accent-content: #3d2e1e;
|
||||||
--color-neutral: #2e2820;
|
--color-neutral: #2e2820;
|
||||||
--color-neutral-content: oklch(98% 0.016 73.684);
|
--color-neutral-content: oklch(98% 0.016 73.684);
|
||||||
--color-info: #2a3040;
|
--color-info: #2a3040;
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
--color-success-content: #8aaa8a;
|
--color-success-content: #8aaa8a;
|
||||||
--color-warning: #4a3010;
|
--color-warning: #4a3010;
|
||||||
--color-warning-content: #d4a050;
|
--color-warning-content: #d4a050;
|
||||||
--color-error: #3d1e1a;
|
--color-error: #3d1e1f;
|
||||||
--color-error-content: #c07060;
|
--color-error-content: #c07060;
|
||||||
--radius-selector: 1rem;
|
--radius-selector: 1rem;
|
||||||
--radius-field: 0rem;
|
--radius-field: 0rem;
|
||||||
@@ -35,3 +35,17 @@
|
|||||||
--depth: 0;
|
--depth: 0;
|
||||||
--noise: 0;
|
--noise: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--font-logo: "Knewave", system-ui;
|
||||||
|
--font-display: "Playwrite HR Lijeva Variable", cursive;
|
||||||
|
--font-sans: "Jost Variable", sans-serif;
|
||||||
|
--font-serif: "Playfair Display Variable", serif;
|
||||||
|
--color-glass-bg: rgba(35, 30, 24, 0.4);
|
||||||
|
--shadow-warm: 0 20px 50px -12px rgba(42, 30, 19, 0.5);
|
||||||
|
--radius-xl: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-card {
|
||||||
|
@apply bg-glass-bg backdrop-blur-xl border border-white/5 shadow-warm rounded-xl;
|
||||||
|
}
|
||||||
|
|||||||
+12
-6
@@ -1,10 +1,16 @@
|
|||||||
import { StrictMode } from 'react'
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from "react-dom/client";
|
||||||
import './index.css'
|
import "./index.css";
|
||||||
import App from './App.tsx'
|
import "@fontsource-variable/playwrite-hr-lijeva/wght.css";
|
||||||
|
import "@fontsource-variable/jost/wght.css";
|
||||||
|
import "@fontsource-variable/playfair-display/wght.css";
|
||||||
|
import App from "./App.tsx";
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
const root = document.getElementById("root");
|
||||||
|
if (root) {
|
||||||
|
createRoot(root).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
import Logo from "../components/Logo";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Pi. Ku.</h1>
|
<Logo/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,112 @@
|
|||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { InfoIcon } from "@phosphor-icons/react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { z } from "zod";
|
||||||
|
import Logo from "../components/Logo";
|
||||||
|
|
||||||
|
// validation logic
|
||||||
|
const registerSchema = z
|
||||||
|
.object({
|
||||||
|
email: z.email("Please enter a valid email"),
|
||||||
|
password: z
|
||||||
|
.string()
|
||||||
|
.check(z.minLength(8, "Password must be at least 8 characters")),
|
||||||
|
confirm_password: z.string(),
|
||||||
|
})
|
||||||
|
.refine((data) => data.password === data.confirm_password, {
|
||||||
|
message: "Passwords don't match",
|
||||||
|
path: ["confirm_password"],
|
||||||
|
});
|
||||||
|
|
||||||
|
type RegisterInputs = z.infer<typeof registerSchema>;
|
||||||
|
|
||||||
export default function Register() {
|
export default function Register() {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<RegisterInputs>({
|
||||||
|
resolver: zodResolver(registerSchema),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = (data: RegisterInputs) => {
|
||||||
|
console.log("Form Data:", data);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="glass-card w-full max-w-sm p-2 transition-all duration-500 hover:shadow-2xl">
|
||||||
<h1>Register</h1>
|
<form onSubmit={handleSubmit(onSubmit)} className="card-body gap-4">
|
||||||
|
<h2 className="card-title font-display text-2xl font-bold justify-center text-primary tracking-tight">
|
||||||
|
Create a <Logo /> Account
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="form-control">
|
||||||
|
<label htmlFor="email" className="label font-bold font-display py-1">
|
||||||
|
Email
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
{...register("email")}
|
||||||
|
type="email"
|
||||||
|
placeholder="you@email.com"
|
||||||
|
className={`input input-bordered focus:input-primary ${errors.email ? "input-error" : ""}`}
|
||||||
|
/>
|
||||||
|
{errors.email && (
|
||||||
|
<p className="text-error-content text-xs mt-1">{errors.email.message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-control">
|
||||||
|
<label htmlFor="password" className="label font-bold font-display py-1">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
{...register("password")}
|
||||||
|
type="password"
|
||||||
|
placeholder="••••••••"
|
||||||
|
className={`input input-bordered focus:input-primary ${errors.password ? "input-error" : ""}`}
|
||||||
|
/>
|
||||||
|
{errors.password && (
|
||||||
|
<p className="text-error-content text-xs mt-1">{errors.password.message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Confirm Pass */}
|
||||||
|
<div className="form-control">
|
||||||
|
<label htmlFor="confirm_password" className="label font-bold font-display py-1">
|
||||||
|
Confirm Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
{...register("confirm_password")}
|
||||||
|
type="password"
|
||||||
|
placeholder="••••••••"
|
||||||
|
className={`input input-bordered focus:input-primary ${errors.confirm_password ? "input-error" : ""}`}
|
||||||
|
/>
|
||||||
|
{errors.confirm_password && (
|
||||||
|
<p className="text-error-content text-xs mt-1">
|
||||||
|
{errors.confirm_password.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Warning */}
|
||||||
|
<div className="bg-warning/10 border-l-2 border-warning p-3 rounded-r-md flex gap-2">
|
||||||
|
<InfoIcon
|
||||||
|
size={20}
|
||||||
|
weight="duotone"
|
||||||
|
className="text-warning mt-0.5 shrink-0"
|
||||||
|
/>
|
||||||
|
<p className="text-sm text-warning-content font-medium opacity-90">
|
||||||
|
Choose a password you won't forget. <br />
|
||||||
|
<span className="font-semibold underline">There is no reset.</span> If you lose it, your letters cannot be recovered.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card-actions mt-4">
|
||||||
|
<button type="submit" className="btn btn-primary w-full shadow-lg">
|
||||||
|
Register
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user