From be8da63df6b4cd2d079a3887258089642f0645a0 Mon Sep 17 00:00:00 2001
From: Erol Haagenrud
Date: Fri, 17 Jul 2026 21:57:44 +0200
Subject: [PATCH] =?UTF-8?q?F=C3=B8r=20grafikk?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.claude/settings.local.json | 8 +++++-
frontend/.gitignore | 3 +++
frontend/app/layout.tsx | 6 +----
frontend/components/login-form.tsx | 39 ++++++++++++++++++++++++++----
frontend/next.config.mjs | 15 +++++++++---
frontend/package.json | 1 -
frontend/pnpm-lock.yaml | 34 --------------------------
7 files changed, 57 insertions(+), 49 deletions(-)
diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index 27919a5..e5d2ab8 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -155,7 +155,13 @@
"Bash(python3)",
"Bash(docker --version)",
"Bash(docker run --rm -v /opt/teecup/frontend:/app -w /app node:22-slim bash -c ' *)",
- "Bash(git -C /opt/teecup status --short frontend/)"
+ "Bash(git -C /opt/teecup status --short frontend/)",
+ "Bash(grep -n \"CORS\\\\|cors\\\\|allow_origin\" /opt/teecup/app/main.py *)",
+ "Bash(git -C /opt/teecup ls-files frontend/.gitignore)",
+ "Bash(git -C /opt/teecup rev-parse --show-toplevel)",
+ "Bash(git -C /opt/teecup config --get core.excludesfile)",
+ "Bash(git -C /opt/teecup ls-files .claude/)",
+ "Bash(git -C /opt/teecup status --porcelain)"
],
"additionalDirectories": [
"/opt/teeoff/deploy",
diff --git a/frontend/.gitignore b/frontend/.gitignore
index 97eb8c0..5c17774 100644
--- a/frontend/.gitignore
+++ b/frontend/.gitignore
@@ -12,4 +12,7 @@ __v0_jsx-dev-runtime.ts
# Common ignores
node_modules
.next/
+.pnpm-store/
+next-env.d.ts
+pnpm-workspace.yaml
.DS_Store
\ No newline at end of file
diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx
index c98533a..140bca5 100644
--- a/frontend/app/layout.tsx
+++ b/frontend/app/layout.tsx
@@ -1,4 +1,3 @@
-import { Analytics } from '@vercel/analytics/next'
import type { Metadata, Viewport } from 'next'
import { Nunito } from 'next/font/google'
import './globals.css'
@@ -47,10 +46,7 @@ export default function RootLayout({
}>) {
return (
-
- {children}
- {process.env.NODE_ENV === 'production' && }
-
+ {children}
)
}
diff --git a/frontend/components/login-form.tsx b/frontend/components/login-form.tsx
index a2a4787..955cff5 100644
--- a/frontend/components/login-form.tsx
+++ b/frontend/components/login-form.tsx
@@ -19,6 +19,7 @@ export function LoginForm() {
const [sent, setSent] = useState(false)
const [sending, setSending] = useState(false)
const [cooldown, setCooldown] = useState(0)
+ const [error, setError] = useState(null)
const emailValid = isValidEmail(email)
const showError = touched && email.length > 0 && !emailValid
@@ -33,12 +34,25 @@ export function LoginForm() {
}, [cooldown])
async function sendLink() {
- // Mock only — pretend to dispatch a magic link.
setSending(true)
- await new Promise((r) => setTimeout(r, 700))
- setSending(false)
- setSent(true)
- setCooldown(RESEND_COOLDOWN)
+ setError(null)
+ try {
+ // Alltid samme suksess-respons uansett om e-posten finnes (anti-
+ // enumerering, se ADR-009) -- kun nettverks-/serverfeil havner i catch.
+ const res = await fetch("/auth/request-link", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ credentials: "include",
+ body: JSON.stringify({ email: email.trim(), locale: "nb" }),
+ })
+ if (!res.ok) throw new Error(`request-link: ${res.status}`)
+ setSent(true)
+ setCooldown(RESEND_COOLDOWN)
+ } catch {
+ setError("Klarte ikke å sende lenken. Sjekk tilkoblingen og prøv igjen.")
+ } finally {
+ setSending(false)
+ }
}
function handleSubmit(e: React.FormEvent) {
@@ -65,6 +79,7 @@ export function LoginForm() {
email={email}
cooldown={cooldown}
sending={sending}
+ error={error}
onResend={handleResend}
onReset={handleReset}
/>
@@ -101,6 +116,12 @@ export function LoginForm() {
)}
+ {error && (
+
+ {error}
+
+ )}
+
+ {error && (
+
+ {error}
+
+ )}
+
{cooldown > 0 ? (
diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs
index 4cd9948..0d4f1af 100644
--- a/frontend/next.config.mjs
+++ b/frontend/next.config.mjs
@@ -1,11 +1,20 @@
+// API-et proxyes server-side under samme opprinnelse (ingen CORS, cookien
+// fungerer uendret) -- se ADR-009/015. TEECUP_API_ORIGIN peker mot en lokal
+// backend i dev; i prod peker den mot teecup_api på det delte Docker-nettverket.
+const API_ORIGIN = process.env.TEECUP_API_ORIGIN || "http://localhost:8000"
+
/** @type {import('next').NextConfig} */
const nextConfig = {
- typescript: {
- ignoreBuildErrors: true,
- },
images: {
unoptimized: true,
},
+ async rewrites() {
+ return [
+ { source: "/auth/:path*", destination: `${API_ORIGIN}/auth/:path*` },
+ { source: "/orgs/:path*", destination: `${API_ORIGIN}/orgs/:path*` },
+ { source: "/health", destination: `${API_ORIGIN}/health` },
+ ]
+ },
}
export default nextConfig
diff --git a/frontend/package.json b/frontend/package.json
index bd6f76f..2ad63c3 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -10,7 +10,6 @@
},
"dependencies": {
"@base-ui/react": "^1.5.0",
- "@vercel/analytics": "1.6.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.16.0",
diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml
index ff63f67..04ae4b7 100644
--- a/frontend/pnpm-lock.yaml
+++ b/frontend/pnpm-lock.yaml
@@ -11,9 +11,6 @@ importers:
'@base-ui/react':
specifier: ^1.5.0
version: 1.5.0(@date-fns/tz@1.4.1)(@types/react@19.2.14)(date-fns@4.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
- '@vercel/analytics':
- specifier: 1.6.1
- version: 1.6.1(next@16.2.6(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -701,32 +698,6 @@ packages:
'@types/validate-npm-package-name@4.0.2':
resolution: {integrity: sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==}
- '@vercel/analytics@1.6.1':
- resolution: {integrity: sha512-oH9He/bEM+6oKlv3chWuOOcp8Y6fo6/PSro8hEkgCW3pu9/OiCXiUpRUogDh3Fs3LH2sosDrx8CxeOLBEE+afg==}
- peerDependencies:
- '@remix-run/react': ^2
- '@sveltejs/kit': ^1 || ^2
- next: '>= 13'
- react: ^18 || ^19 || ^19.0.0-rc
- svelte: '>= 4'
- vue: ^3
- vue-router: ^4
- peerDependenciesMeta:
- '@remix-run/react':
- optional: true
- '@sveltejs/kit':
- optional: true
- next:
- optional: true
- react:
- optional: true
- svelte:
- optional: true
- vue:
- optional: true
- vue-router:
- optional: true
-
accepts@2.0.0:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'}
@@ -2646,11 +2617,6 @@ snapshots:
'@types/validate-npm-package-name@4.0.2': {}
- '@vercel/analytics@1.6.1(next@16.2.6(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4)':
- optionalDependencies:
- next: 16.2.6(@babel/core@7.29.7)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
- react: 19.2.4
-
accepts@2.0.0:
dependencies:
mime-types: 3.0.2