-
Notifications
You must be signed in to change notification settings - Fork 0
Fix code issues #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix code issues #12
Changes from all commits
bdee92e
2ca5afe
db01315
d0135e3
bef75d9
2d28591
515caac
f884527
7ce3413
c86c899
b3d4984
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| "use client"; | ||
|
|
||
| import { useState } from "react"; | ||
| import { createClient } from "@/lib/supabase/client"; | ||
| import { useRouter } from "next/navigation"; | ||
|
|
||
| export default function LoginPage() { | ||
| const [email, setEmail] = useState(""); | ||
| const [password, setPassword] = useState(""); | ||
| const [loading, setLoading] = useState(false); | ||
| const [error, setError] = useState(""); | ||
| const router = useRouter(); | ||
| const supabase = createClient(); | ||
|
|
||
| const handleLogin = async (e: React.FormEvent) => { | ||
| e.preventDefault(); | ||
| setLoading(true); | ||
| setError(""); | ||
|
|
||
| const { error } = await supabase.auth.signInWithPassword({ | ||
| email, | ||
| password, | ||
| }); | ||
|
|
||
| if (error) { | ||
| setError(error.message); | ||
| setLoading(false); | ||
| } else { | ||
| router.push("/"); | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="min-h-screen flex items-center justify-center bg-[#0F1419]"> | ||
| <div className="bg-[#1A1F2E] p-8 rounded-lg border border-[#2D3748] w-full max-w-md"> | ||
| <h1 className="text-2xl font-bold text-white mb-6">AI Engineering Platform</h1> | ||
| <form onSubmit={handleLogin} className="space-y-4"> | ||
| <div> | ||
| <label className="block text-sm text-[#A0AEC0] mb-1">Email</label> | ||
| <input | ||
| type="email" | ||
| value={email} | ||
| onChange={(e) => setEmail(e.target.value)} | ||
| className="w-full px-4 py-2 bg-[#0F1419] border border-[#2D3748] rounded text-white focus:outline-none focus:border-[#00D4FF]" | ||
| required | ||
| /> | ||
| </div> | ||
| <div> | ||
| <label className="block text-sm text-[#A0AEC0] mb-1">Password</label> | ||
| <input | ||
| type="password" | ||
| value={password} | ||
| onChange={(e) => setPassword(e.target.value)} | ||
| className="w-full px-4 py-2 bg-[#0F1419] border border-[#2D3748] rounded text-white focus:outline-none focus:border-[#00D4FF]" | ||
| required | ||
| /> | ||
| </div> | ||
| {error && ( | ||
| <div className="text-red-500 text-sm">{error}</div> | ||
| )} | ||
| <button | ||
| type="submit" | ||
| disabled={loading} | ||
| className="w-full py-2 bg-[#00D4FF] text-[#0F1419] font-semibold rounded hover:bg-[#00D4FF]/80 transition disabled:opacity-50" | ||
| > | ||
| {loading ? "Loading..." : "Sign In"} | ||
| </button> | ||
| </form> | ||
| <p className="mt-4 text-center text-sm text-[#A0AEC0]"> | ||
| Don't have an account?{" "} | ||
| <a href="/signup" className="text-[#00D4FF] hover:underline"> | ||
| Sign Up | ||
| </a> | ||
| </p> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,4 @@ | ||
| import Dashboard from "@/components/dashboard/Dashboard"; | ||
|
|
||
| export default function Home() { | ||
| export default function Home) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This change leaves the root page as invalid TSX: Useful? React with 👍 / 👎. |
||
| return <Dashboard />; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| "use client"; | ||
|
|
||
| import { useState } from "react"; | ||
| import { createClient } from "@/lib/supabase/client"; | ||
| import { useRouter } from "next/navigation"; | ||
|
|
||
| export default function SignupPage() { | ||
| const [email, setEmail] = useState(""); | ||
| const [password, setPassword] = useState(""); | ||
| const [loading, setLoading] = useState(false); | ||
| const [error, setError] = useState(""); | ||
| const [confirmationSent, setConfirmationSent] = useState(false); | ||
| const router = useRouter(); | ||
| const supabase = createClient(); | ||
|
|
||
| const handleSignup = async (e: React.FormEvent) => { | ||
| e.preventDefault(); | ||
| setLoading(true); | ||
| setError(""); | ||
|
|
||
| if (!supabase) { | ||
| setError("Supabase is not configured. Please check your environment variables."); | ||
| setLoading(false); | ||
| return; | ||
| } | ||
|
|
||
| const { data, error } = await supabase.auth.signUp({ | ||
| email, | ||
| password, | ||
| }); | ||
|
|
||
| if (error) { | ||
| setError(error.message); | ||
| setLoading(false); | ||
| } else { | ||
| // Check if email confirmation is required | ||
| if (data?.user && !data.session) { | ||
| setConfirmationSent(true); | ||
| setLoading(false); | ||
| } else if (data?.session) { | ||
| // User is immediately signed in, redirect to home | ||
| router.push("/"); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| if (confirmationSent) { | ||
| return ( | ||
| <div className="min-h-screen flex items-center justify-center bg-[#0F1419]"> | ||
| <div className="bg-[#1A1F2E] p-8 rounded-lg border border-[#2D3748] w-full max-w-md text-center"> | ||
| <h1 className="text-2xl font-bold text-white mb-4">Check Your Email</h1> | ||
| <p className="text-[#A0AEC0] mb-4"> | ||
| We've sent a confirmation link to <strong>{email}</strong>. Please check your email and click the link to confirm your account. | ||
| </p> | ||
| <button | ||
| onClick={() => { | ||
| setConfirmationSent(false); | ||
| setEmail(""); | ||
| setPassword(""); | ||
| }} | ||
| className="w-full py-2 bg-[#00D4FF] text-[#0F1419] font-semibold rounded hover:bg-[#00D4FF]/80 transition" | ||
| > | ||
| Back to Signup | ||
| </button> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <div className="min-h-screen flex items-center justify-center bg-[#0F1419]"> | ||
| <div className="bg-[#1A1F2E] p-8 rounded-lg border border-[#2D3748] w-full max-w-md"> | ||
| <h1 className="text-2xl font-bold text-white mb-6">Create Account</h1> | ||
| <form onSubmit={handleSignup} className="space-y-4"> | ||
| <div> | ||
| <label className="block text-sm text-[#A0AEC0] mb-1">Email</label> | ||
| <input | ||
| type="email" | ||
| value={email} | ||
| onChange={(e) => setEmail(e.target.value)} | ||
| className="w-full px-4 py-2 bg-[#0F1419] border border-[#2D3748] rounded text-white focus:outline-none focus:border-[#00D4FF]" | ||
| required | ||
| /> | ||
| </div> | ||
| <div> | ||
| <label className="block text-sm text-[#A0AEC0] mb-1">Password</label> | ||
| <input | ||
| type="password" | ||
| value={password} | ||
| onChange={(e) => setPassword(e.target.value)} | ||
| className="w-full px-4 py-2 bg-[#0F1419] border border-[#2D3748] rounded text-white focus:outline-none focus:border-[#00D4FF]" | ||
| required | ||
| /> | ||
| </div> | ||
| {error && <div className="text-red-500 text-sm">{error}</div>} | ||
| <button | ||
| type="submit" | ||
| disabled={loading} | ||
| className="w-full py-2 bg-[#00D4FF] text-[#0F1419] font-semibold rounded hover:bg-[#00D4FF]/80 transition disabled:opacity-50" | ||
| > | ||
| {loading ? "Loading..." : "Sign Up"} | ||
| </button> | ||
| </form> | ||
| <p className="mt-4 text-center text-sm text-[#A0AEC0]"> | ||
| Already have an account?{" "} | ||
| <a href="/login" className="text-[#00D4FF] hover:underline"> | ||
| Sign In | ||
| </a> | ||
| </p> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,7 @@ const activity = ["Governance branch verified", "Design tokens installed", "Miss | |
| { id: "mission-1", title: "Supabase Persistence Layer", objective: "Persist generated missions, outputs, and approval state into Supabase with scoped access.", status: "In Progress" as const, complexity: "High" as const, progress: 68, completedCount: 7, nextMilestone: 10 }, | ||
| { id: "mission-2", title: "Mission Evaluation Engine", objective: "Score mission outputs across accuracy, completeness, usability, risk, and format compliance.", status: "Review" as const, complexity: "Medium" as const, progress: 82, completedCount: 8, nextMilestone: 10 }, | ||
| { id: "mission-3", title: "Workflow Library Index", objective: "Create a reusable library for saved mission patterns, prompts, and operational playbooks.", status: "Approved" as const, complexity: "Medium" as const, progress: 90, completedCount: 9, nextMilestone: 10 }, | ||
| ]; | ||
| ]]; | ||
|
|
||
| const activity = ["Schema imported into Supabase", "Mission workflow shell committed", "Settings page verified environment keys", "Dashboard route refreshed", "Approval policy registered"]; | ||
|
|
||
|
|
@@ -30,8 +30,7 @@ export default function Dashboard() { | |
| const [toastVisible, setToastVisible] = useState(true); | ||
|
|
||
| return ( | ||
| <div className="min-h-screen bg-[var(--bg-primary)] font-sans text-[var(--text-primary)]"> | ||
| <div className="min-h-screen bg-[var(--bg-primary)] font-['Inter'] text-[var(--text-primary)]"> | ||
| <div className="min-h-screen bg-[var(--bg-primary)] font-sans text-[var(--text-primary)]"> <div className="min-h-screen bg-[var(--bg-primary)] font-['Inter'] text-[var(--text-primary)]"> | ||
| <div className="flex"> | ||
| <Sidebar currentPath="/" /> | ||
| <div className="flex min-h-screen flex-1 flex-col"> | ||
|
|
@@ -58,7 +57,8 @@ export default function Dashboard() { | |
| <h2 className="mt-4 text-[48px] font-bold leading-[1.1]">Operational Mission Control</h2> | ||
| <div className="mt-6 grid gap-4 md:grid-cols-3"> | ||
| <StatusIndicator status="24 missions deployed" tone="success" /> | ||
| <StatusIndicator status="96% success rate" tone="info" /> | ||
| <StatusIndicator status="96% success ra-[; | ||
| mjku . te" tone="info" /> | ||
|
Comment on lines
+60
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the dashboard renders, this prop displays AGENTS.md reference: AGENTS.md:L25-L27 Useful? React with 👍 / 👎. |
||
| <StatusIndicator status="96% approval rate" tone="info" /> | ||
| <StatusIndicator status="12 day streak" tone="inProgress" /> | ||
| </div> | ||
|
|
@@ -84,7 +84,7 @@ export default function Dashboard() { | |
| <section className="mb-8 grid gap-6 xl:grid-cols-[1fr_320px]"> | ||
| <div> | ||
| <h3 className="mb-6 text-[24px] font-semibold leading-[1.3]">Active Missions</h3> | ||
| <div className="grid gap-6 md:grid-cols-2 2xl:grid-cols-3"> | ||
| <div className="grid gap-6 md:grid-cols-2 2xl:grid-cols-3">NM | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the Active Missions section renders, the literal AGENTS.md reference: AGENTS.md:L25-L27 Useful? React with 👍 / 👎. |
||
| {missions.map((mission) => <MissionCard key={mission.id} {...mission} />)} | ||
| </div> | ||
| </div> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,18 @@ | ||
| import { createClient } from "@supabase/supabase-js"; | ||
| import { createBrowserClient } from "@supabase/ssr"; | ||
|
|
||
| export function getBrowserSupabase() { | ||
| export function createClient() { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When this replacement lands, existing modules still import Useful? React with 👍 / 👎. |
||
| const url = process.env.NEXT_PUBLIC_SUPABASE_URL; | ||
| const anonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; | ||
|
|
||
| // Return null if keys are missing to allow fallback/error UI | ||
| if (!url || !anonKey) { | ||
| return null; | ||
| } | ||
|
|
||
| return createClient(url, anonKey); | ||
| return createBrowserClient(url, anonKey); | ||
| } | ||
|
|
||
| // Compatibility export for existing imports | ||
| export function getBrowserSupabase() { | ||
| return createClient(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { createServerClient } from "@supabase/ssr"; | ||
| import { cookies } from "next/headers"; | ||
|
|
||
| export async function createClient() { | ||
| const cookieStore = await cookies(); | ||
|
|
||
| // Return null if keys are missing | ||
| const url = process.env.NEXT_PUBLIC_SUPABASE_URL; | ||
| const anonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; | ||
|
|
||
| if (!url || !anonKey) { | ||
| return null; | ||
| } | ||
|
|
||
| return createServerClient(url, anonKey, { | ||
| cookies: { | ||
| getAll() { | ||
| return cookieStore.getAll(); | ||
| }, | ||
| setAll(cookiesToSet) { | ||
| try { | ||
| cookiesToSet.forEach(({ name, value, options }) => | ||
| cookieStore.set(name, value, options) | ||
| ); | ||
| } catch { | ||
| // Handle error | ||
| } | ||
| }, | ||
| }, | ||
| }); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { type NextRequest } from "next/server"; | ||
| import { createServerClient } from "@supabase/ssr"; | ||
| import { NextResponse } from "next/server"; | ||
|
|
||
| export async function middleware(request: NextRequest) { | ||
| // Skip middleware if Supabase keys are not configured | ||
| const url = process.env.NEXT_PUBLIC_SUPABASE_URL; | ||
| const anonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; | ||
|
|
||
| if (!url || !anonKey) { | ||
| return NextResponse.next({ request }); | ||
| } | ||
|
|
||
| // Return JSON error for API routes instead of redirecting | ||
| if (request.nextUrl.pathname.startsWith("/api/")) { | ||
| // Let the API route handle auth via createServerClient | ||
| return NextResponse.next({ request }); | ||
|
Comment on lines
+15
to
+17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In deployments with Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| let supabaseResponse = NextResponse.next({ | ||
| request, | ||
| }); | ||
|
|
||
| const supabase = createServerClient(url, anonKey, { | ||
| cookies: { | ||
| getAll() { | ||
| return request.cookies.getAll(); | ||
| }, | ||
| setAll(cookiesToSet) { | ||
| cookiesToSet.forEach(({ name, value, options }) => | ||
| request.cookies.set(name, value) | ||
| ); | ||
| supabaseResponse = NextResponse.next({ | ||
| request, | ||
| }); | ||
| cookiesToSet.forEach(({ name, value, options }) => | ||
| supabaseResponse.cookies.set(name, value, options) | ||
| ); | ||
| }, | ||
| }, | ||
| }); | ||
|
|
||
| const { | ||
| data: { user }, | ||
| } = await supabase.auth.getUser(); | ||
|
|
||
| // If no user and trying to access protected route, redirect to login | ||
| // But allow signup page for unauthenticated users | ||
| if (!user && !request.nextUrl.pathname.startsWith("/login") && !request.nextUrl.pathname.startsWith("/signup")) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This commit still edits and ships Useful? React with 👍 / 👎. |
||
| const url = request.nextUrl.clone(); | ||
| url.pathname = "/login"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a signed-out user opens a protected deep link such as AGENTS.md reference: AGENTS.md:L25-L28 Useful? React with 👍 / 👎. |
||
| return NextResponse.redirect(url); | ||
| } | ||
|
|
||
| return supabaseResponse; | ||
| } | ||
|
|
||
| export const config = { | ||
| matcher: [ | ||
| "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This matcher also runs the auth redirect on Useful? React with 👍 / 👎. |
||
| ], | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fresh evidence after the missing-key fallback fix:
createClient()now returnsnullwhenNEXT_PUBLIC_SUPABASE_URLorNEXT_PUBLIC_SUPABASE_ANON_KEYis absent, but this login handler still dereferences it. Submitting/loginin the local/demo missing-key state throws before the page can show an error, unlike/signupwhich already guards this case; add the same null check here.Useful? React with 👍 / 👎.