Skip to main content

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

<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)

<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)

<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

<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

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

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).