feat: add policy pages

This commit is contained in:
gosangam
2026-06-02 01:57:08 +05:30
parent 0352cb2003
commit 91d5d272d2
168 changed files with 1326 additions and 81 deletions
+9 -2
View File
@@ -16,6 +16,7 @@
import { useState } from "react";
import Link from "next/link";
import { useAuth } from "@/components/auth/auth-provider";
import { OAuthButtons } from "@/components/auth/oauth-buttons";
import { AuthError } from "@/lib/auth";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -25,10 +26,13 @@ export function LoginForm({
onSuccess,
forgotPasswordHref = "/forgot-password",
showForgotLink = true,
oauthNext,
}: {
onSuccess?: () => void;
forgotPasswordHref?: string;
showForgotLink?: boolean;
/** Post-OAuth redirect path on the frontend. */
oauthNext?: string;
}) {
const { logIn } = useAuth();
const [email, setEmail] = useState("");
@@ -51,7 +55,9 @@ export function LoginForm({
}
return (
<form onSubmit={onSubmit} className="space-y-5">
<div className="space-y-5">
<OAuthButtons next={oauthNext} />
<form onSubmit={onSubmit} className="space-y-5">
<div className="space-y-1.5">
<Label htmlFor="login-email">Email</Label>
<Input
@@ -88,6 +94,7 @@ export function LoginForm({
<Button type="submit" disabled={submitting} className="w-full h-11 text-base">
{submitting ? "Logging in…" : "Log in"}
</Button>
</form>
</form>
</div>
);
}
+100
View File
@@ -0,0 +1,100 @@
"use client";
/**
* OAuthButtons — "Continue with Google / Facebook" row used above both
* LoginForm and SignUpForm. Clicking a button performs a top-level
* navigation to the BFF's /oauth/<provider>/start endpoint, which kicks off
* the Authorization Code flow. The BFF handles the round-trip and 302s back
* to ${frontend}${next} with the session cookie already set.
*
* Top-level navigation (not fetch) is intentional: cross-origin redirects
* to Google / Facebook can't happen from XHR, and we need the BFF to set
* the cookie before the browser lands back on the frontend.
*/
import { useMemo } from "react";
const BFF_BASE =
process.env.NEXT_PUBLIC_BFF_URL || "http://localhost:8080";
type Props = {
/** Frontend path to land on after the round-trip. Defaults to /u/me. */
next?: string;
/** Whether to render a divider with "or continue with email" below. */
showDivider?: boolean;
};
export function OAuthButtons({ next, showDivider = true }: Props) {
const startUrl = useMemo(
() => (provider: "google" | "facebook") => {
const u = new URL(`${BFF_BASE}/api/v1/auth/oauth/${provider}/start`);
if (next) u.searchParams.set("next", next);
return u.toString();
},
[next],
);
return (
<div className="space-y-3">
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
<a
href={startUrl("google")}
className="h-11 inline-flex items-center justify-center gap-2 rounded-md border border-border bg-card text-sm font-medium text-foreground hover:bg-muted transition-colors"
>
<GoogleIcon className="size-4" />
Google
</a>
<a
href={startUrl("facebook")}
className="h-11 inline-flex items-center justify-center gap-2 rounded-md border border-border bg-card text-sm font-medium text-foreground hover:bg-muted transition-colors"
>
<FacebookIcon className="size-4" />
Facebook
</a>
</div>
{showDivider ? (
<div className="relative flex items-center py-1">
<span className="flex-grow border-t border-border" />
<span className="mx-3 text-xs uppercase tracking-wider text-muted-foreground">
or
</span>
<span className="flex-grow border-t border-border" />
</div>
) : null}
</div>
);
}
function GoogleIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden>
<path
fill="#4285F4"
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.76h3.56c2.08-1.92 3.28-4.74 3.28-8.09Z"
/>
<path
fill="#34A853"
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.56-2.76c-.99.66-2.25 1.06-3.72 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84A11 11 0 0 0 12 23Z"
/>
<path
fill="#FBBC05"
d="M5.84 14.11A6.59 6.59 0 0 1 5.5 12c0-.73.13-1.45.34-2.11V7.05H2.18A11 11 0 0 0 1 12c0 1.78.43 3.46 1.18 4.95l3.66-2.84Z"
/>
<path
fill="#EA4335"
d="M12 5.38c1.62 0 3.06.56 4.21 1.65l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.05l3.66 2.84C6.71 7.31 9.14 5.38 12 5.38Z"
/>
</svg>
);
}
function FacebookIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden>
<path
fill="#1877F2"
d="M24 12a12 12 0 1 0-13.88 11.85V15.47H7.08V12h3.04V9.36c0-3 1.79-4.67 4.53-4.67 1.31 0 2.69.24 2.69.24v2.96H15.83c-1.49 0-1.95.92-1.95 1.87V12h3.32l-.53 3.47h-2.79v8.38A12 12 0 0 0 24 12Z"
/>
</svg>
);
}
+14 -3
View File
@@ -8,12 +8,20 @@
import { useState } from "react";
import { useAuth } from "@/components/auth/auth-provider";
import { OAuthButtons } from "@/components/auth/oauth-buttons";
import { AuthError } from "@/lib/auth";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
export function SignUpForm({ onSuccess }: { onSuccess?: () => void }) {
export function SignUpForm({
onSuccess,
oauthNext,
}: {
onSuccess?: () => void;
/** Post-OAuth redirect path on the frontend. */
oauthNext?: string;
}) {
const { signUp } = useAuth();
const [name, setName] = useState("");
const [email, setEmail] = useState("");
@@ -36,7 +44,9 @@ export function SignUpForm({ onSuccess }: { onSuccess?: () => void }) {
}
return (
<form onSubmit={onSubmit} className="space-y-5">
<div className="space-y-5">
<OAuthButtons next={oauthNext} />
<form onSubmit={onSubmit} className="space-y-5">
<div className="space-y-1.5">
<Label htmlFor="signup-name">Name</Label>
<Input
@@ -76,6 +86,7 @@ export function SignUpForm({ onSuccess }: { onSuccess?: () => void }) {
<Button type="submit" disabled={submitting} className="w-full h-11 text-base">
{submitting ? "Creating account…" : "Sign up"}
</Button>
</form>
</form>
</div>
);
}
+22 -1
View File
@@ -16,7 +16,7 @@ export function SiteFooter() {
>
Prabhupāda Says
</Link>
<div className="flex items-center gap-4">
<div className="flex flex-wrap items-center justify-center gap-x-3 gap-y-1">
<Link
href="/about"
className="hover:text-foreground transition-colors"
@@ -24,6 +24,27 @@ export function SiteFooter() {
About
</Link>
<span aria-hidden>·</span>
<Link
href="/privacy"
className="hover:text-foreground transition-colors"
>
Privacy
</Link>
<span aria-hidden>·</span>
<Link
href="/terms"
className="hover:text-foreground transition-colors"
>
Terms
</Link>
<span aria-hidden>·</span>
<Link
href="/data-deletion"
className="hover:text-foreground transition-colors"
>
Data Deletion
</Link>
<span aria-hidden>·</span>
<span>All glories to Śrīla Prabhupāda</span>
</div>
</div>