Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions client/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,68 @@
margin: 0;
}

/* Felles utseende for runde og avlange knapper over kartet. */
.map-control {
display: inline-grid;
place-items: center;
border: 2px solid var(--navy-900);
background: var(--surface);
color: var(--navy-900);
font: inherit;
font-weight: 700;
cursor: pointer;
transition:
background 0.15s,
color 0.15s,
transform 0.15s,
box-shadow 0.15s;
}

.map-control:hover:not(:disabled) {
background: var(--navy-900);
color: var(--eggshell);
transform: translateY(-2px);
box-shadow: 0 0.35rem 0.75rem rgb(10 34 64 / 0.25);
}

.map-control:active:not(:disabled) {
transform: translateY(0);
box-shadow: none;
}

.home-link {
position: absolute;
top: 0.5rem;
left: 3.75rem;
z-index: 3;
grid-auto-flow: column;
gap: 0.5rem;
height: 2.75rem;
padding-inline: 1rem;
border-radius: 999px;
text-decoration: none;
}

.home-link svg {
width: 1.25rem;
height: 1.25rem;
}

.map-control:disabled {
opacity: 0.4;
cursor: default;
}

/* På den mørke headeren skal knappene ha lys kant og lys utheving. */
.site-header .map-control {
border-color: var(--blue-300);
}

.site-header .map-control:hover:not(:disabled) {
background: var(--blue-300);
color: var(--navy-900);
}

/* ---------- Navigasjon ---------- */

.main-nav ul {
Expand Down
7 changes: 5 additions & 2 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import './App.css'
import { useRoute } from './hooks/useRoute'
import { HomePage } from './pages/HomePage'
import { MapPage } from './pages/MapPage'
import { UserPage } from './pages/UserPage'

const pages = { home: HomePage, map: MapPage, user: UserPage }

function App() {
const route = useRoute()
const Page = pages[useRoute()]

return route === 'map' ? <MapPage /> : <HomePage />
return <Page />
}

export default App
17 changes: 17 additions & 0 deletions client/src/api/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { mockUser } from '../data/mockUser'
import type { User } from '../types/user'

/** Simulerer nettverksforsinkelse så lastetilstander kan sees i grensesnittet. */
const LATENCY_MS = 200

/**
* Henter den innloggede brukeren.
*
* Når innlogging og GraphQL-backenden er på plass, byttes innholdet her ut med
* et kall mot serveren (med samme signatur).
*/
export async function fetchCurrentUser(): Promise<User> {
await new Promise((resolve) => setTimeout(resolve, LATENCY_MS))

return mockUser
}
35 changes: 35 additions & 0 deletions client/src/components/Gulls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/** Dekor: noen måker over havet, én gruppe på hver side av tittelen. */
export function Gulls() {
return (
<>
<svg
className="gulls gulls-left"
viewBox="0 0 60 28"
aria-hidden="true"
focusable="false"
>
<path
d="M4 20q5-7 10 0 5-7 10 0M34 8q4-5 8 0 4-5 8 0"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
<svg
className="gulls gulls-right"
viewBox="0 0 60 28"
aria-hidden="true"
focusable="false"
>
<path
d="M6 8q4-5 8 0 4-5 8 0M32 20q5-7 10 0 5-7 10 0"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
</>
)
}
23 changes: 23 additions & 0 deletions client/src/components/HomeLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { HOME_HREF } from '../hooks/useRoute'

/** Knappen øverst til venstre som tar deg tilbake til hovedsiden. */
export function HomeLink() {
return (
<a className="map-control home-link" href={HOME_HREF}>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
focusable="false"
>
<circle cx="12" cy="5" r="2" />
<path d="M12 7v13M8 11h8M5 14c0 3.500 3 6 7 6s7-2.500 7-6" />
</svg>
Hovedside
</a>
)
}
6 changes: 4 additions & 2 deletions client/src/components/MainNav.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ReactNode } from 'react'
import { HOME_HREF, MAP_HREF, USER_HREF } from '../hooks/useRoute'

/** Felles tegning for ikonene: små, enkle strek-ikoner. */
function Icon({ children }: { children: ReactNode }) {
Expand Down Expand Up @@ -28,7 +29,7 @@ const links: {
}[] = [
{
label: 'Utforsk',
href: '#/kart',
href: MAP_HREF,
// Kompass
icon: (
<Icon>
Expand All @@ -50,7 +51,7 @@ const links: {
},
{
label: 'Hjem',
href: '#/',
href: HOME_HREF,
current: true,
// Anker
icon: (
Expand All @@ -71,6 +72,7 @@ const links: {
},
{
label: 'Bruker',
href: USER_HREF,
// Person
icon: (
<Icon>
Expand Down
31 changes: 2 additions & 29 deletions client/src/components/SiteHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Gulls } from './Gulls'
import { HelpButton } from './HelpButton'
import { MainNav } from './MainNav'
import { MenuButton } from './MenuButton'
Expand All @@ -17,35 +18,7 @@ export function SiteHeader() {
skjule dem igjen. Under «Utforsk» ser du stedene på et kart.
</HelpButton>

{/* Dekor: noen måker over havet, én gruppe på hver side av tittelen. */}
<svg
className="gulls gulls-left"
viewBox="0 0 60 28"
aria-hidden="true"
focusable="false"
>
<path
d="M4 20q5-7 10 0 5-7 10 0M34 8q4-5 8 0 4-5 8 0"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
<svg
className="gulls gulls-right"
viewBox="0 0 60 28"
aria-hidden="true"
focusable="false"
>
<path
d="M6 8q4-5 8 0 4-5 8 0M32 20q5-7 10 0 5-7 10 0"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
<Gulls />

<hgroup className="site-title">
<h1>
Expand Down
27 changes: 27 additions & 0 deletions client/src/components/user/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
type AvatarProps = {
name: string
url: string | null
}

/** Profilbildet, eller en enkel plassholder når bildet mangler. */
export function Avatar({ name, url }: AvatarProps) {
if (url) {
return <img className="avatar" src={url} alt={`Profilbilde av ${name}`} />
}

return (
<svg
className="avatar"
viewBox="0 0 64 64"
aria-hidden="true"
focusable="false"
>
<circle cx="32" cy="32" r="32" className="avatar-bg" />
<circle cx="32" cy="25" r="11" className="avatar-figure" />
<path
d="M10 56c2-13 11-20 22-20s20 7 22 20a32 32 0 0 1-44 0Z"
className="avatar-figure"
/>
</svg>
)
}
21 changes: 21 additions & 0 deletions client/src/components/user/BadgeSlots.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const SLOTS = [0, 1, 2, 3, 4]

/** Tomme plasser for badges. Selve badgene kommer når utfordringene er laget. */
export function BadgeSlots() {
return (
<>
<ul className="badge-list" aria-hidden="true">
{SLOTS.map((slot) => (
<li key={slot}>
<svg viewBox="0 0 100 100" focusable="false" className="badge-slot">
<polygon points="50,4 91,27 91,73 50,96 9,73 9,27" />
</svg>
</li>
))}
</ul>
<p className="profile-empty">
Badges får du ved å fullføre utfordringer. De dukker opp her.
</p>
</>
)
}
Loading