Some checks failed
API CI / API Lint (push) Successful in 53s
API CI / API Type Check (push) Failing after 51s
API CI / API Test (push) Successful in 1m32s
API CI / API Build (push) Failing after 1m0s
API CI / Build Docker Image (push) Has been skipped
CD Pipeline / Deploy to Staging (push) Failing after 32s
CI Pipeline / Lint and Type Check (push) Failing after 33s
CI Pipeline / Build (push) Has been skipped
CI Pipeline / Test Backend (push) Failing after 2m1s
CI Pipeline / Test Frontend (push) Failing after 35s
CI Pipeline / Security Scan (push) Failing after 1m12s
Deploy to Staging / Deploy to Staging (push) Failing after 28s
Portal CI / Portal Lint (push) Failing after 19s
Portal CI / Portal Type Check (push) Failing after 18s
Portal CI / Portal Test (push) Failing after 19s
Portal CI / Portal Build (push) Failing after 18s
Test Suite / frontend-tests (push) Failing after 30s
Test Suite / api-tests (push) Failing after 44s
Test Suite / blockchain-tests (push) Failing after 27s
Type Check / type-check (map[directory:. name:root]) (push) Failing after 18s
Type Check / type-check (map[directory:api name:api]) (push) Failing after 19s
Type Check / type-check (map[directory:portal name:portal]) (push) Failing after 18s
CD Pipeline / Deploy to Production (push) Has been skipped
Add sankofa.nexus marketing site with institutional grade scorecards, server-side metadata/title, dynamic icons, favicon rewrite, and instant landing render without session-loading flash; split authenticated AppShell from unauthenticated corporate chrome. Co-authored-by: Cursor <cursoragent@cursor.com>
62 lines
2.1 KiB
TypeScript
62 lines
2.1 KiB
TypeScript
'use client';
|
|
|
|
import { signIn } from 'next-auth/react';
|
|
import { useCallback, useState, type ReactNode } from 'react';
|
|
|
|
import { CloudflareTurnstile } from '@/components/security/CloudflareTurnstile';
|
|
|
|
const TURNSTILE_SITE_KEY = process.env.NEXT_PUBLIC_CLOUDFLARE_TURNSTILE_SITE_KEY;
|
|
|
|
export interface PortalSignInCardProps {
|
|
badge?: string;
|
|
title: string;
|
|
subtitle: string;
|
|
callbackUrl?: string;
|
|
/** Extra hint under the sign-in button (e.g. dev IdP note) */
|
|
footer?: ReactNode;
|
|
}
|
|
|
|
export function PortalSignInCard({
|
|
badge = 'Sankofa Phoenix',
|
|
title,
|
|
subtitle,
|
|
callbackUrl = '/',
|
|
footer,
|
|
}: PortalSignInCardProps) {
|
|
const [turnstileToken, setTurnstileToken] = useState<string | null>(() =>
|
|
TURNSTILE_SITE_KEY ? null : ''
|
|
);
|
|
|
|
const onTurnstileToken = useCallback((token: string | null) => {
|
|
setTurnstileToken(token);
|
|
}, []);
|
|
|
|
const canSignIn = !TURNSTILE_SITE_KEY || Boolean(turnstileToken);
|
|
|
|
return (
|
|
<div className="w-full max-w-md rounded-2xl border border-gray-800 bg-gray-900/80 p-8 shadow-xl shadow-black/40 backdrop-blur-sm">
|
|
<p className="mb-1 text-center text-sm font-medium uppercase tracking-wide text-orange-400">{badge}</p>
|
|
<h1 className="mb-2 text-center text-2xl font-bold text-white">{title}</h1>
|
|
<p className="mb-8 text-center text-gray-400">{subtitle}</p>
|
|
|
|
{TURNSTILE_SITE_KEY ? (
|
|
<div className="mb-6">
|
|
<p className="mb-2 text-center text-sm text-gray-500">Verification</p>
|
|
<CloudflareTurnstile siteKey={TURNSTILE_SITE_KEY} onToken={onTurnstileToken} />
|
|
</div>
|
|
) : null}
|
|
|
|
<button
|
|
type="button"
|
|
disabled={!canSignIn}
|
|
onClick={() => signIn(undefined, { callbackUrl })}
|
|
className="w-full rounded-lg bg-gradient-to-r from-orange-500 to-amber-500 px-6 py-3 font-semibold text-gray-950 shadow-lg transition hover:from-orange-400 hover:to-amber-400 focus:outline-none focus:ring-2 focus:ring-orange-400 focus:ring-offset-2 focus:ring-offset-gray-900 disabled:cursor-not-allowed disabled:opacity-50"
|
|
>
|
|
Sign In
|
|
</button>
|
|
|
|
{footer}
|
|
</div>
|
|
);
|
|
}
|