diff --git a/client/.env.example b/client/.env.example
index f40b204..0ba98e5 100644
--- a/client/.env.example
+++ b/client/.env.example
@@ -1,2 +1,2 @@
# Adressen til Graphql backend. Kopier dette til .env.local
-VITE_GRAPHQL_URL=http://localhost:3001/graphql
\ No newline at end of file
+VITE_GRAPHQL_URL=/project2/graphql
\ No newline at end of file
diff --git a/client/src/App.tsx b/client/src/App.tsx
index e6777b2..4723f7e 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -1,13 +1,16 @@
-import { Route, Routes } from 'react-router'
-import Layout from './components/layout/Layout'
-import HomePage from './routes/HomePage'
-import RecipeListPage from './routes/RecipeListPage'
-import RecipeDetailPage from './routes/RecipeDetailPage'
-import FavoritesPage from './routes/FavoritesPage'
-import ShoppingListPage from './routes/ShoppingListPage'
-import ProfilePage from './routes/ProfilePage'
-import NotFoundPage from './routes/NotFoundPage'
-import DesignTestPage from './routes/DesignTestPage'
+import { Route, Routes } from 'react-router';
+import Layout from './components/layout/Layout';
+import HomePage from './routes/HomePage';
+import RecipeListPage from './routes/RecipeListPage';
+import RecipeDetailPage from './routes/RecipeDetailPage';
+import FavoritesPage from './routes/FavoritesPage';
+import ShoppingListPage from './routes/ShoppingListPage';
+import ProfilePage from './routes/ProfilePage';
+import NotFoundPage from './routes/NotFoundPage';
+import DesignTestPage from './routes/DesignTestPage';
+import LoginPage from './routes/LoginPage';
+import TestUserLoginPage from './routes/TestUserLoginPage';
+import RequireAuth from './components/layout/RequireAuth';
// Flyttet testsiden til DesignTestPage under routes
export default function App() {
@@ -21,9 +24,16 @@ export default function App() {
} />
} />
- } />
} />
- } />
+
+ } />
+ } />
+
+ {/* alle sidene som krever innlogging */}
+ }>
+ } />
+ } />
+
{/* Testside for designsystemet */}
{import.meta.env.DEV && (
@@ -34,5 +44,5 @@ export default function App() {
} />
- )
-}
\ No newline at end of file
+ );
+}
diff --git a/client/src/components/layout/Header.tsx b/client/src/components/layout/Header.tsx
index 654a49e..032d8cc 100644
--- a/client/src/components/layout/Header.tsx
+++ b/client/src/components/layout/Header.tsx
@@ -1,48 +1,50 @@
-import { useState } from 'react'
-import { Link, NavLink, useSearchParams } from 'react-router'
-import SearchBar from './SearchBar'
-import { Button } from '../ui/Button'
-import { Dialog } from '../ui/Dialog'
-import NewRecipePage from '../../routes/NewRecipePage'
-import { RecipeStepIndicator } from '../new-recipe/RecipeStepIndicator'
+import { useState } from 'react';
+import { Link, NavLink, useSearchParams } from 'react-router';
+import SearchBar from './SearchBar';
+import { Button } from '../ui/Button';
+import { Dialog } from '../ui/Dialog';
+import NewRecipePage from '../../routes/NewRecipePage';
+import { RecipeStepIndicator } from '../new-recipe/RecipeStepIndicator';
+import { useCurrentUser } from '../../lib/useCurrentUser';
// Fokusmarkering ved tastaturnavigasjon
const focusRing =
- 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary'
+ 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary';
// Aktiv lenke markeres med både farge og understrek
function navLinkClass({ isActive }: { isActive: boolean }) {
- const base = `whitespace-nowrap font-body transition-colors hover:text-primary ${focusRing}`
+ const base = `whitespace-nowrap font-body transition-colors hover:text-primary ${focusRing}`;
return isActive
? `${base} text-primary underline underline-offset-4`
- : `${base} text-text`
+ : `${base} text-text`;
}
// Styling for knapper/lenker i header
-const buttonBase = `whitespace-nowrap rounded-medium px-3 py-2 text-sm font-body transition-colors ${focusRing}`
+const buttonBase = `whitespace-nowrap rounded-medium px-3 py-2 text-sm font-body transition-colors ${focusRing}`;
// Brukerknapp
function userClass({ isActive }: { isActive: boolean }) {
- const base = `${buttonBase} border border-primary`
+ const base = `${buttonBase} border border-primary`;
return isActive
? `${base} bg-primary text-background`
- : `${base} text-primary hover:bg-pink-light`
+ : `${base} text-primary hover:bg-pink-light`;
}
export default function Header() {
+ //hvem som er logget inn
+ const { user } = useCurrentUser();
// Søkeordet fra URL-en
- const [searchParams] = useSearchParams()
- const query = searchParams.get('q') ?? ''
+ const [searchParams] = useSearchParams();
+ const query = searchParams.get('q') ?? '';
// Holder styr på hvilket steg i Ny oppskrift brukeren er på
- const [currentStep, setCurrentStep] = useState(1)
+ const [currentStep, setCurrentStep] = useState(1);
return (
-
{/* Logo */}
{/* Søkefelt */}
-
+
{/* Navigasjon */}
- )
-}
\ No newline at end of file
+ );
+}
diff --git a/client/src/components/layout/RequireAuth.tsx b/client/src/components/layout/RequireAuth.tsx
new file mode 100644
index 0000000..7d355ed
--- /dev/null
+++ b/client/src/components/layout/RequireAuth.tsx
@@ -0,0 +1,20 @@
+import { Navigate, Outlet } from 'react-router';
+import { useCurrentUser } from '../../lib/useCurrentUser';
+
+// Sender utloggede brukere til innloggingssiden
+export default function RequireAuth() {
+ const { user, loading } = useCurrentUser();
+
+ // venter på svar om hvem som logger inn
+ if (loading) {
+ return null;
+ }
+
+ // hvis ikke innlogget, sender til innloggingsside
+ if (!user) {
+ return ;
+ }
+
+ // hvis innlogget, vis side
+ return ;
+}
diff --git a/client/src/lib/apolloClient.ts b/client/src/lib/apolloClient.ts
index 860cdd4..f4dbc89 100644
--- a/client/src/lib/apolloClient.ts
+++ b/client/src/lib/apolloClient.ts
@@ -1,6 +1,5 @@
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client';
-
// adressen til graphql backend
const httpLink = new HttpLink({
uri: import.meta.env.VITE_GRAPHQL_URL,
diff --git a/client/src/lib/useCurrentUser.ts b/client/src/lib/useCurrentUser.ts
new file mode 100644
index 0000000..b10dd7e
--- /dev/null
+++ b/client/src/lib/useCurrentUser.ts
@@ -0,0 +1,19 @@
+import type { User } from '../types/user';
+
+// Midlertidig bruker til backend er klar. Sett til null for å teste som utlogget.
+
+const MOCK_USER: User | null = null;
+
+/* const MOCK_USER: User | null = {
+ id: '1',
+ name: 'testesen',
+ authProvider: 'TEST',
+}; */
+
+// Hvem som er innlogget
+export function useCurrentUser() {
+ return {
+ user: MOCK_USER,
+ loading: false,
+ };
+}
diff --git a/client/src/routes/LoginPage.tsx b/client/src/routes/LoginPage.tsx
new file mode 100644
index 0000000..97731a4
--- /dev/null
+++ b/client/src/routes/LoginPage.tsx
@@ -0,0 +1,60 @@
+import { useNavigate } from 'react-router';
+import { Button } from '../components/ui/Button';
+
+// Innloggingsside
+export default function LoginPage() {
+ const navigate = useNavigate();
+
+ return (
+
+
Velkommen tilbake
+
+ Logg inn med Google, utforsk oppskriftene som gjest eller testbruker.
+
+
+ {/* Midlertidig: Google og gjest kobles til backend senere */}
+
+
+
+ Lagre favorittene dine og del egne oppskrifter.
+
+
+
+ {/* Skiller Google innlogging fra de andre valgene */}
+
+
+ eller
+
+
+
+
+
+
Se oppskrifter uten å logge inn.
+
+
+
+ {/* Går til logg inn siden for testbrukeren */}
+
+
Prøv med en ferdig testkonto.
+
+
+ );
+}
diff --git a/client/src/routes/TestUserLoginPage.tsx b/client/src/routes/TestUserLoginPage.tsx
new file mode 100644
index 0000000..ff341e1
--- /dev/null
+++ b/client/src/routes/TestUserLoginPage.tsx
@@ -0,0 +1,85 @@
+import { useState, type SubmitEvent } from 'react';
+import { Link } from 'react-router';
+import VisibilityIcon from '@mui/icons-material/Visibility';
+import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
+import { Button } from '../components/ui/Button';
+import { Field } from '../components/ui/Field';
+import { Input } from '../components/ui/Input';
+
+// Innlogging for testbrukeren
+export default function TestUserLoginPage() {
+ const [username, setUsername] = useState('');
+ const [password, setPassword] = useState('');
+ // Om passordet vises som tekst eller prikker
+ const [showPassword, setShowPassword] = useState(false);
+
+ function handleSubmit(event: SubmitEvent) {
+ // Hindrer at siden lastes på nytt
+ event.preventDefault();
+
+ // Midlertidig: kobles til backend senere
+ console.log('testbruker', username);
+ }
+
+ return (
+
+
Innlogging
+
+ Logg inn med testbrukeren for en ferdiglaget bruker med oppskrifter.
+
+
+
+
+
+ Tilbake til innloggingsvalg
+
+
+ );
+}
diff --git a/client/src/types/user.ts b/client/src/types/user.ts
new file mode 100644
index 0000000..0098090
--- /dev/null
+++ b/client/src/types/user.ts
@@ -0,0 +1,7 @@
+export type AuthProvider = 'GOOGLE' | 'GUEST' | 'TEST';
+
+export type User = {
+ id: string;
+ name: string;
+ authProvider: AuthProvider;
+};
diff --git a/client/vite.config.ts b/client/vite.config.ts
index 3f961fd..f2af69c 100644
--- a/client/vite.config.ts
+++ b/client/vite.config.ts
@@ -6,4 +6,13 @@ import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [react(), tailwindcss()],
base: '/project2/',
+ // sender graphql videre til backend
+ server: {
+ proxy: {
+ '/project2/graphql': {
+ target: 'http://localhost:3001',
+ rewrite: (path) => path.replace(/^\/project2/, ''),
+ },
+ },
+ },
});