auth0 login
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
@@ -15,10 +16,255 @@
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-family: 'Inter', Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Auth0 Login Page Styles */
|
||||
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
box-sizing: border-box;
|
||||
background-color: #1a1e27;
|
||||
}
|
||||
|
||||
.loading-state, .error-state {
|
||||
background-color: #2d313c;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
|
||||
padding: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 500;
|
||||
color: #a0aec0;
|
||||
animation: pulse 1.5s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.error-state {
|
||||
background-color: #c53030;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 2.8rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.error-sub-message {
|
||||
font-size: 1rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.main-card-wrapper {
|
||||
background-color: #262a33;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
padding: 3rem;
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
animation: fadeInScale 0.8s ease-out forwards;
|
||||
}
|
||||
|
||||
.auth0-logo {
|
||||
width: 160px;
|
||||
margin-bottom: 1.5rem;
|
||||
opacity: 0;
|
||||
animation: slideInDown 1s ease-out forwards 0.2s;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 2.8rem;
|
||||
font-weight: 700;
|
||||
color: #f7fafc;
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
|
||||
opacity: 0;
|
||||
animation: fadeIn 1s ease-out forwards 0.4s;
|
||||
}
|
||||
|
||||
.action-card {
|
||||
background-color: #2d313c;
|
||||
border-radius: 15px;
|
||||
box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3);
|
||||
padding: 2.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1.8rem;
|
||||
width: calc(100% - 2rem);
|
||||
opacity: 0;
|
||||
animation: fadeIn 1s ease-out forwards 0.6s;
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 1.25rem;
|
||||
color: #cbd5e0;
|
||||
text-align: center;
|
||||
line-height: 1.6;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 1.1rem 2.8rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.button:focus {
|
||||
box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5);
|
||||
}
|
||||
|
||||
.button.login {
|
||||
background-color: #63b3ed;
|
||||
color: #1a1e27;
|
||||
}
|
||||
|
||||
.button.login:hover {
|
||||
background-color: #4299e1;
|
||||
transform: translateY(-5px) scale(1.03);
|
||||
box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.button.logout {
|
||||
background-color: #fc8181;
|
||||
color: #1a1e27;
|
||||
padding: 0.5rem 1.5rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.button.logout:hover {
|
||||
background-color: #e53e3e;
|
||||
transform: translateY(-2px) scale(1.02);
|
||||
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.logged-in-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logged-in-message {
|
||||
font-size: 1.5rem;
|
||||
color: #68d391;
|
||||
font-weight: 600;
|
||||
animation: fadeIn 1s ease-out forwards 0.8s;
|
||||
}
|
||||
|
||||
.profile-section-title {
|
||||
font-size: 2.2rem;
|
||||
animation: slideInUp 1s ease-out forwards 1s;
|
||||
}
|
||||
|
||||
.profile-card {
|
||||
padding: 2.2rem;
|
||||
animation: scaleIn 0.8s ease-out forwards 1.2s;
|
||||
}
|
||||
|
||||
.profile-picture {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.profile-picture:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.profile-name {
|
||||
font-size: 2rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.profile-email {
|
||||
font-size: 1.15rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fadeInScale {
|
||||
from { opacity: 0; transform: scale(0.95); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
@keyframes slideInDown {
|
||||
from { opacity: 0; transform: translateY(-70px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes slideInUp {
|
||||
from { opacity: 0; transform: translateY(50px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.6; }
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from { opacity: 0; transform: scale(0.8); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.main-card-wrapper {
|
||||
padding: 2rem;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 0.9rem 2rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.auth0-logo {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
import { CopilotKit } from "@copilotkit/react-core";
|
||||
import { Auth0Provider } from "@auth0/nextjs-auth0/client";
|
||||
import "./globals.css";
|
||||
import "@copilotkit/react-ui/styles.css";
|
||||
|
||||
@@ -17,9 +18,11 @@ export default function RootLayout({
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={"antialiased"}>
|
||||
<CopilotKit runtimeUrl="/api/copilotkit" agent="sample_agent">
|
||||
{children}
|
||||
</CopilotKit>
|
||||
<Auth0Provider>
|
||||
<CopilotKit runtimeUrl="/api/copilotkit" agent="sample_agent">
|
||||
{children}
|
||||
</CopilotKit>
|
||||
</Auth0Provider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import { useCopilotAction } from "@copilotkit/react-core";
|
||||
import { useCopilotAction, useUser as useCopilotUser } from "@copilotkit/react-core";
|
||||
import { CopilotKitCSSProperties, CopilotChat } from "@copilotkit/react-ui";
|
||||
import { useState } from "react";
|
||||
import { useUser } from "@auth0/nextjs-auth0/client";
|
||||
import LoginButton from "@/components/LoginButton";
|
||||
import LogoutButton from "@/components/LogoutButton";
|
||||
import Profile from "@/components/Profile";
|
||||
|
||||
export default function CopilotKitPage() {
|
||||
const [themeColor, setThemeColor] = useState("#6366f1");
|
||||
const { user, isLoading } = useUser();
|
||||
|
||||
useCopilotAction({
|
||||
name: "setThemeColor",
|
||||
@@ -19,20 +24,78 @@ export default function CopilotKitPage() {
|
||||
},
|
||||
});
|
||||
|
||||
// Show loading state while checking authentication
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="app-container">
|
||||
<div className="loading-state">
|
||||
<div className="loading-text">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// If not authenticated, show login page
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="app-container">
|
||||
<div className="main-card-wrapper">
|
||||
<img
|
||||
src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png"
|
||||
alt="Auth0 Logo"
|
||||
className="auth0-logo"
|
||||
/>
|
||||
<h1 className="main-title">Cavepedia</h1>
|
||||
|
||||
<div className="action-card">
|
||||
<p className="action-text">
|
||||
Welcome! Please log in to access the AI Cave Chat.
|
||||
</p>
|
||||
<LoginButton />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// If authenticated, show the CopilotKit chat with user profile
|
||||
return (
|
||||
<main
|
||||
style={{ "--copilot-kit-primary-color": themeColor } as CopilotKitCSSProperties}
|
||||
className="h-screen w-screen flex justify-center bg-gray-50 py-8 px-2"
|
||||
className="h-screen w-screen flex flex-col bg-gray-50"
|
||||
>
|
||||
<div className="h-full w-full max-w-5xl flex flex-col">
|
||||
<CopilotChat
|
||||
instructions={"You assist with looking up any relevant information to caving. This includes but is not limited to Cave Locations, Cave Surveying, Cave History."}
|
||||
labels={{
|
||||
title: "AI Cartwright",
|
||||
initial: "Would you like to lookup a cave location today?",
|
||||
}}
|
||||
className="h-full w-full"
|
||||
/>
|
||||
{/* Header with user profile and logout */}
|
||||
<div className="w-full bg-white shadow-sm border-b border-gray-200 px-4 py-3">
|
||||
<div className="max-w-7xl mx-auto flex justify-between items-center">
|
||||
<div className="flex items-center gap-4">
|
||||
<h1 className="text-xl font-semibold text-gray-900">Cavepedia</h1>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
{user.picture && (
|
||||
<img
|
||||
src={user.picture}
|
||||
alt={user.name || 'User'}
|
||||
className="w-8 h-8 rounded-full"
|
||||
/>
|
||||
)}
|
||||
<span className="text-sm text-gray-700">{user.name}</span>
|
||||
<LogoutButton />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CopilotKit Chat */}
|
||||
<div className="flex-1 flex justify-center py-8 px-2 overflow-hidden">
|
||||
<div className="h-full w-full max-w-5xl flex flex-col">
|
||||
<CopilotChat
|
||||
instructions={"You assist with looking up any relevant information to caving. This includes but is not limited to Cave Locations, Cave Surveying, Cave History."}
|
||||
labels={{
|
||||
title: "AI Cartwright",
|
||||
initial: "Would you like to lookup a cave location today?",
|
||||
}}
|
||||
className="h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
12
web/src/components/LoginButton.tsx
Normal file
12
web/src/components/LoginButton.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
"use client";
|
||||
|
||||
export default function LoginButton() {
|
||||
return (
|
||||
<a
|
||||
href="/auth/login"
|
||||
className="button login"
|
||||
>
|
||||
Log In
|
||||
</a>
|
||||
);
|
||||
}
|
||||
12
web/src/components/LogoutButton.tsx
Normal file
12
web/src/components/LogoutButton.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
"use client";
|
||||
|
||||
export default function LogoutButton() {
|
||||
return (
|
||||
<a
|
||||
href="/auth/logout"
|
||||
className="button logout"
|
||||
>
|
||||
Log Out
|
||||
</a>
|
||||
);
|
||||
}
|
||||
35
web/src/components/Profile.tsx
Normal file
35
web/src/components/Profile.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
"use client";
|
||||
|
||||
import { useUser } from "@auth0/nextjs-auth0/client";
|
||||
|
||||
export default function Profile() {
|
||||
const { user, isLoading } = useUser();
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="loading-state">
|
||||
<div className="loading-text">Loading user profile...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="profile-card action-card">
|
||||
<img
|
||||
src={user.picture || `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='50' fill='%2363b3ed'/%3E%3Cpath d='M50 45c7.5 0 13.64-6.14 13.64-13.64S57.5 17.72 50 17.72s-13.64 6.14-13.64 13.64S42.5 45 50 45zm0 6.82c-9.09 0-27.28 4.56-27.28 13.64v3.41c0 1.88 1.53 3.41 3.41 3.41h47.74c1.88 0 3.41-1.53 3.41-3.41v-3.41c0-9.08-18.19-13.64-27.28-13.64z' fill='%23fff'/%3E%3C/svg%3E`}
|
||||
alt={user.name || 'User profile'}
|
||||
className="profile-picture"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.src = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='50' fill='%2363b3ed'/%3E%3Cpath d='M50 45c7.5 0 13.64-6.14 13.64-13.64S57.5 17.72 50 17.72s-13.64 6.14-13.64 13.64S42.5 45 50 45zm0 6.82c-9.09 0-27.28 4.56-27.28 13.64v3.41c0 1.88 1.53 3.41 3.41 3.41h47.74c1.88 0 3.41-1.53 3.41-3.41v-3.41c0-9.08-18.19-13.64-27.28-13.64z' fill='%23fff'/%3E%3C/svg%3E`;
|
||||
}}
|
||||
/>
|
||||
<h2 className="profile-name">{user.name}</h2>
|
||||
<p className="profile-email">{user.email}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
3
web/src/lib/auth0.ts
Normal file
3
web/src/lib/auth0.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { Auth0Client } from '@auth0/nextjs-auth0/server';
|
||||
|
||||
export const auth0 = new Auth0Client();
|
||||
18
web/src/middleware.ts
Normal file
18
web/src/middleware.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { NextRequest } from "next/server";
|
||||
import { auth0 } from "./lib/auth0";
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
return await auth0.middleware(request);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
/*
|
||||
* Match all request paths except for the ones starting with:
|
||||
* - _next/static (static files)
|
||||
* - _next/image (image optimization files)
|
||||
* - favicon.ico, sitemap.xml, robots.txt (metadata files)
|
||||
*/
|
||||
"/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user