# Page: Notifications

# Page Spec: Notifications

## Route
`/notifications`

## Architecture Status
**Core**

## Figma Reference
**No Figma — designed from architecture**

## Visual Description

The notifications page displays transaction alerts, system messages, and payment confirmations:

- **Top bar:** Left side has a back arrow (ArrowLeft, NOT chevron). "Varsler" heading in bold **sans-serif** (NOT serif). No right-side icons.
- **Notification list:** Vertically stacked notification cards, each in its own white rounded card:
  - **Unread notifications:** Bold title text with a green dot indicator (left edge)
  - **Read notifications:** Regular title text without indicator
  - Each card contains:
    - **Icon:** Type-based icon in colored circle (green for transfers, yellow for QR payments, blue for system messages)
    - **Title:** Bold notification heading
    - **Description:** Muted text with details
    - **Timestamp:** Muted text showing relative time ("2 timer siden", "I går 14:30")
- **Empty state:** When no notifications exist:
  - Large bell icon with slash (gray, centered)
  - "Ingen varsler" heading
  - "Du vil motta varsler om transaksjoner og viktige meldinger her" description text
- **Bottom navigation:** 5 tabs — Hjem (house outline, gray), Send (paper plane outline, gray), Skann (QR outline, gray), Kort (card outline, gray), Profil (person outline, gray). No tab highlighted as active.

Background is light gray (#EEEEEE). All cards are white with rounded corners (#FFFFFF, rounded-2xl). Unread notifications appear at the top.

## Page Layout

```
App page WITH bottom nav
├── Top Bar
│   ├── Left: Back arrow (chevron left)
│   └── Center: "Varsler" heading
├── Notification List (or Empty State)
│   └── Notification cards (sorted by date, unread first)
│       ├── Unread indicator (green dot)
│       ├── Icon (type-based color)
│       ├── Title (bold if unread)
│       ├── Description (muted text)
│       └── Timestamp (relative time)
└── Bottom Nav (no active tab)
```

## Components

### Top Bar
```tsx
<div className="flex items-center gap-3 px-6 pt-6">
  <Link href="/dashboard">
    <button className="rounded-lg p-2 hover:bg-white/80">
      <ArrowLeft className="h-5 w-5 text-[#6B7280]" />
    </button>
  </Link>
  <h1 className="text-xl font-bold text-[#1A1A1A]">
    Varsler
  </h1>
</div>
```

### Notification Card (Unread)
```tsx
<div
  onClick={() => markAsRead(notification.id)}
  className="relative flex items-start gap-3 rounded-2xl bg-white p-4 shadow-sm cursor-pointer hover:bg-[#F9FAFB] transition-colors"
>
  {/* Unread indicator dot */}
  <div className="absolute left-0 top-1/2 -translate-y-1/2 h-2 w-2 rounded-full bg-[#0B6E35]" />

  {/* Icon */}
  <div className={`flex h-10 w-10 items-center justify-center rounded-full ${iconBgColor}`}>
    {getNotificationIcon(notification.type)}
  </div>

  {/* Content */}
  <div className="flex-1">
    <p className="text-sm font-bold text-[#1A1A1A]">{notification.title}</p>
    <p className="text-xs text-[#6B7280] mt-0.5">{notification.description}</p>
    <p className="text-xs text-[#9CA3AF] mt-1">{formatTimestamp(notification.created_at)}</p>
  </div>
</div>
```

### Notification Card (Read)
```tsx
<div
  className="flex items-start gap-3 rounded-2xl bg-white p-4 shadow-sm"
>
  {/* Icon */}
  <div className={`flex h-10 w-10 items-center justify-center rounded-full ${iconBgColor}`}>
    {getNotificationIcon(notification.type)}
  </div>

  {/* Content */}
  <div className="flex-1">
    <p className="text-sm font-semibold text-[#1A1A1A]">{notification.title}</p>
    <p className="text-xs text-[#6B7280] mt-0.5">{notification.description}</p>
    <p className="text-xs text-[#9CA3AF] mt-1">{formatTimestamp(notification.created_at)}</p>
  </div>
</div>
```

### Empty State
```tsx
<div className="flex flex-col items-center justify-center px-6 py-16">
  <div className="flex h-16 w-16 items-center justify-center rounded-full bg-[#E5E7EB]">
    <BellOff className="h-8 w-8 text-[#9CA3AF]" />
  </div>
  <p className="mt-4 text-lg font-bold text-[#1A1A1A]">Ingen varsler</p>
  <p className="mt-2 text-center text-sm text-[#6B7280] max-w-xs">
    Du vil motta varsler om transaksjoner og viktige meldinger her
  </p>
</div>
```

### Notification Icon Logic
```tsx
function getNotificationIcon(type: string) {
  switch (type) {
    case 'transaction_sent':
    case 'transaction_received':
      return <Send className="h-5 w-5 text-[#0B6E35]" />;
    case 'qr_payment':
      return <QrCode className="h-5 w-5 text-[#D4A017]" />;
    case 'system_message':
    case 'account_linked':
    case 'security_alert':
      return <AlertCircle className="h-5 w-5 text-[#3B82F6]" />;
    default:
      return <Bell className="h-5 w-5 text-[#6B7280]" />;
  }
}

function getIconBgColor(type: string) {
  switch (type) {
    case 'transaction_sent':
    case 'transaction_received':
      return 'bg-[#0B6E35]/10';
    case 'qr_payment':
      return 'bg-[#D4A017]/10';
    case 'system_message':
    case 'account_linked':
    case 'security_alert':
      return 'bg-[#3B82F6]/10';
    default:
      return 'bg-[#E5E7EB]';
  }
}
```

### Timestamp Formatter
```tsx
function formatTimestamp(timestamp: string): string {
  const now = new Date();
  const then = new Date(timestamp);
  const diffMs = now.getTime() - then.getTime();
  const diffMins = Math.floor(diffMs / 60000);
  const diffHours = Math.floor(diffMs / 3600000);
  const diffDays = Math.floor(diffMs / 86400000);

  if (diffMins < 1) return 'Akkurat nå';
  if (diffMins < 60) return `${diffMins} ${diffMins === 1 ? 'minutt' : 'minutter'} siden`;
  if (diffHours < 24) return `${diffHours} ${diffHours === 1 ? 'time' : 'timer'} siden`;
  if (diffDays === 1) return `I går ${then.toLocaleTimeString('nb-NO', { hour: '2-digit', minute: '2-digit' })}`;
  if (diffDays < 7) return `${diffDays} dager siden`;

  return then.toLocaleDateString('nb-NO', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' });
}
```

## Data Displayed

| Data | Source | API |
|------|--------|-----|
| Notification list | Notifications table | GET `/api/notifications` |
| Notification title | Notification record | From notifications table |
| Notification description | Notification record | From notifications table |
| Notification type | Notification record | transaction_sent / transaction_received / qr_payment / system_message / account_linked / security_alert |
| Notification read status | Notification record | is_read boolean |
| Notification timestamp | Notification record | created_at |

## User Interactions

| Element | Action | Result |
|---------|--------|--------|
| Back arrow | Click | Navigate back to previous page (dashboard) |
| Unread notification card | Click | Mark notification as read, update UI to show read state |
| Read notification card | Click | Optional — navigate to related resource (e.g., transaction detail) |
| Bottom nav tabs | Click | Navigate to respective page |

## Norwegian Labels

| Element | Norwegian Text |
|---------|---------------|
| Page heading | Varsler |
| Empty state heading | Ingen varsler |
| Empty state description | Du vil motta varsler om transaksjoner og viktige meldinger her |
| Timestamp: Now | Akkurat nå |
| Timestamp: Minutes | {n} minutt siden / {n} minutter siden |
| Timestamp: Hours | {n} time siden / {n} timer siden |
| Timestamp: Yesterday | I går {time} |
| Timestamp: Days | {n} dager siden |
| Notification: Transaction sent | Overføring sendt |
| Notification: Transaction received | Penger mottatt |
| Notification: QR Payment | QR-betaling fullført |
| Notification: System message | Systemmelding |
| Notification: Account linked | Bankkonto tilkoblet |
| Notification: Security alert | Sikkerhetsvarsel |

## Design Tokens

| Token | Value |
|-------|-------|
| Page bg | `#EEEEEE` |
| Card bg | `#FFFFFF` |
| Primary | `#0B6E35` |
| Primary hover | `#095C2C` |
| Accent/Gold | `#D4A017` |
| Info blue | `#3B82F6` |
| Text primary | `#1A1A1A` |
| Text muted | `#6B7280` |
| Text light | `#9CA3AF` |
| Border | `#E5E7EB` |
| Unread indicator | `#0B6E35` (green dot) |
| Brand font | `font-[family-name:var(--font-fraunces)]` |
| Card radius | `rounded-2xl` |
| Icon circle radius | `rounded-full` |
| Hover bg | `#F9FAFB` |

## Bottom Navigation

**Yes** — No tab active (all tabs gray, outline icons).