diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx
index 112c8fb..b903c7f 100644
--- a/src/app/dashboard/page.tsx
+++ b/src/app/dashboard/page.tsx
@@ -1,6 +1,6 @@
'use client';
-import { useState, useEffect, useCallback, useMemo } from 'react';
+import { useState, useEffect, useCallback, useMemo, type ReactNode } from 'react';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import { Loader2, Plus, Search } from 'lucide-react';
@@ -129,6 +129,17 @@ function formatUtc(date: string) {
return `${String(d.getUTCHours()).padStart(2, '0')}:${String(d.getUTCMinutes()).padStart(2, '0')} UTC`;
}
+function MobileCardRow({ label, children }: { label: string; children: ReactNode }) {
+ return (
+
+
+ {label}
+
+ {children}
+
+ );
+}
+
export default function DashboardPage() {
const [contacts, setContacts] = useState([]);
const [loading, setLoading] = useState(true);
@@ -290,7 +301,7 @@ export default function DashboardPage() {
-
+
@@ -343,19 +354,16 @@ export default function DashboardPage() {
{/* Map + Quick Log */}
-
+
{error && (
{error}
)}
-
-
-
+
+
+
Worldmap
@@ -367,7 +375,7 @@ export default function DashboardPage() {
-
+
Quick log
@@ -428,7 +436,7 @@ export default function DashboardPage() {
onChange={setBandRange}
/>
-
+
{BAND_ORDER.map((band) => {
const count = bandActivity[band] ?? 0;
const filled = activityBars(count);
@@ -437,7 +445,7 @@ export default function DashboardPage() {
{/* Log + DXpeditions */}
-
+
-
-
-
+
+
+
setTableSearch(e.target.value)}
placeholder="Search callsign, name, grid, frequency…"
- className="flex-1 bg-transparent border-0 outline-none text-fg text-[15px] placeholder:text-fg-3"
+ className="flex-1 min-w-0 bg-transparent border-0 outline-none text-fg text-[15px] placeholder:text-fg-3"
/>
/
@@ -496,105 +501,168 @@ export default function DashboardPage() {
) : (
-
-
-
- Callsign
- When
- Band / Mode
- Freq
- RST
- Operator
- QSL
-
-
-
+ <>
+ {/* Desktop: classic table */}
+
+
+
+
+ Callsign
+ When
+ Band / Mode
+ Freq
+ RST
+ Operator
+ QSL
+
+
+
+ {loading ? (
+
+
+
+
+ Loading contacts…
+
+
+
+ ) : filteredContacts.length === 0 ? (
+
+
+ No contacts match those filters.
+
+
+ ) : (
+ filteredContacts.map((contact) => (
+ handleContactClick(contact)}
+ >
+
+
+ {contact.callsign}
+
+
+
+ {formatUtc(contact.datetime)}
+
+
+ {formatRelativeTime(contact.datetime)}
+
+
+
+
+ {contact.band}
+ {contact.mode}
+
+
+
+
+ {contact.frequency}
+
+
+
+
+ {contact.rst_sent ?? '-'} / {contact.rst_received ?? '-'}
+
+
+
+ {contact.name ?? '—'}
+ {contact.qth ? (
+ · {contact.qth}
+ ) : null}
+
+
+ e.stopPropagation()}>
+ {qslChip(contact)}
+
+ fetchContacts()}
+ size="sm"
+ />
+
+
+
+
+
+ ))
+ )}
+
+
+
+
+ {/* Mobile: stacked QSO cards */}
+
{loading ? (
-
-
-
-
- Loading contacts…
-
-
-
+
+
+ Loading contacts…
+
) : filteredContacts.length === 0 ? (
-
-
- No contacts match those filters.
-
-
+
+ No contacts match those filters.
+
) : (
filteredContacts.map((contact) => (
-
handleContactClick(contact)}
+ className="rounded-xl border border-line bg-bg-1 p-3.5 text-left cursor-pointer hover:border-line-hi transition-colors"
>
-
-
+
+
{contact.callsign}
-
-
- {formatUtc(contact.datetime)}
-
-
- {formatRelativeTime(contact.datetime)}
+ {qslChip(contact)}
+
+
+
+ {formatUtc(contact.datetime)}{' '}
+ · {formatRelativeTime(contact.datetime)}
-
-
-
+
+
+
{contact.band}
{contact.mode}
-
-
-
-
- {contact.frequency}
-
-
+
+
+ {contact.frequency}
+
+
{contact.rst_sent ?? '-'} / {contact.rst_received ?? '-'}
-
-
- {contact.name ?? '—'}
- {contact.qth ? (
- · {contact.qth}
- ) : null}
-
-
- e.stopPropagation()}>
- {qslChip(contact)}
-
- fetchContacts()}
- size="sm"
- />
-
-
-
-
-
+
+
+
+ {contact.name ?? '—'}
+ {contact.qth ? (
+ · {contact.qth}
+ ) : null}
+
+
+
))
)}
-
-
+
+ >
)}
{pagination.pages > 1 && (
diff --git a/src/app/new-contact/page.tsx b/src/app/new-contact/page.tsx
index db6dbb0..6290753 100644
--- a/src/app/new-contact/page.tsx
+++ b/src/app/new-contact/page.tsx
@@ -424,7 +424,7 @@ export default function NewContactPage() {