From 7d6f381f556993c5a7eb41c3fd3a0dd56b195c7f Mon Sep 17 00:00:00 2001 From: elpatron Date: Sat, 6 Jun 2026 20:58:04 +0200 Subject: [PATCH] feat: implement responsive event cards for mobile viewports --- client/src/App.css | 111 +++++++++ client/src/components/LogEntryEditor.tsx | 275 +++++++++++++++++------ 2 files changed, 320 insertions(+), 66 deletions(-) diff --git a/client/src/App.css b/client/src/App.css index 25036a9..23cd564 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -6310,3 +6310,114 @@ body.app-tour-active .feedback-modal-overlay--tour .disclaimer-modal-panel { .crew-selection-item input { flex-shrink: 0; } + +/* Responsive Event Cards */ +.events-desktop-only { + display: block; +} + +.events-mobile-only { + display: none; +} + +@media (max-width: 768px) { + .events-desktop-only { + display: none !important; + } + + .events-mobile-only { + display: flex !important; + flex-direction: column; + gap: 12px; + } +} + +.event-mobile-card { + background: var(--app-surface-alt); + border: 1px solid var(--app-border-subtle); + border-radius: 12px; + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 12px; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.event-mobile-card:hover { + border-color: var(--app-border); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); +} + +.event-card-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.event-card-meta { + display: flex; + align-items: center; + gap: 10px; +} + +.event-card-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.event-card-time { + color: #fbbf24; + font-weight: 600; + font-family: monospace; + font-size: 15px; + display: flex; + align-items: center; + gap: 4px; +} + +.event-card-divider { + height: 1px; + background: var(--app-border-subtle); + margin: 0; + border: none; + opacity: 0.5; +} + +.event-card-grid { + display: flex; + flex-wrap: wrap; + gap: 8px 12px; +} + +.event-card-chip { + background: var(--app-surface-hover, rgba(255, 255, 255, 0.03)); + border: 1px solid var(--app-border-muted); + border-radius: 8px; + padding: 4px 8px; + font-size: 13px; + color: #cbd5e1; + display: inline-flex; + align-items: center; + gap: 6px; +} + +.event-card-chip svg { + color: var(--app-text-muted, #94a3b8); +} + +.event-card-weather-img { + width: 24px; + height: 24px; + object-fit: contain; +} + +.event-card-remarks { + background: var(--app-surface-inset, rgba(11, 12, 16, 0.2)); + border-left: 3px solid var(--app-accent, #fbbf24); + padding: 8px 12px; + border-radius: 0 8px 8px 0; + font-size: 13.5px; + color: #e2e8f0; + word-break: break-word; +} diff --git a/client/src/components/LogEntryEditor.tsx b/client/src/components/LogEntryEditor.tsx index 9434a18..c960258 100644 --- a/client/src/components/LogEntryEditor.tsx +++ b/client/src/components/LogEntryEditor.tsx @@ -1862,67 +1862,124 @@ export default function LogEntryEditor({ {events.length === 0 ? (
{t('logs.no_events')}
) : ( -
- - - - - - - - - - - - - - - {!readOnly && } - - - - {events.map((ev, idx) => ( - - - - - - - - - - )} - - ))} - -
{t('logs.event_time')}{t('logs.event_creator')}{t('logs.event_mgk')}{t('logs.event_rwk')}{t('logs.event_wind_direction')}{t('logs.event_wind_strength')}{t('logs.event_sea_state')}{t('logs.event_weather')}{t('logs.event_log')}{t('logs.event_gps')}{t('logs.event_remarks')}
{ev.time} - - {ev.mgk ? `${ev.mgk}°` : '—'}{ev.rwk ? `${ev.rwk}°` : '—'}{ev.windDirection || '—'}{ev.windStrength || '—'}{ev.seaState || '—'} - {ev.weatherIcon ? ( - Weather + {/* Desktop view */} +
+ + + + + + + + + + + + + + + {!readOnly && } + + + + {events.map((ev, idx) => ( + + + + + + + + + + + + + {!readOnly && ( + )} - - - - - {!readOnly && ( - + ))} + +
{t('logs.event_time')}{t('logs.event_creator')}{t('logs.event_mgk')}{t('logs.event_rwk')}{t('logs.event_wind_direction')}{t('logs.event_wind_strength')}{t('logs.event_sea_state')}{t('logs.event_weather')}{t('logs.event_log')}{t('logs.event_gps')}{t('logs.event_remarks')}
{ev.time} + - ) : ( - '—' + {ev.mgk ? `${ev.mgk}°` : '—'}{ev.rwk ? `${ev.rwk}°` : '—'}{ev.windDirection || '—'}{ev.windStrength || '—'}{ev.seaState || '—'} + {ev.weatherIcon ? ( + Weather + ) : ( + '—' + )} + {ev.logReading ? `${ev.logReading} nm` : '—'} + {ev.gpsLat && ev.gpsLng ? `${ev.gpsLat}, ${ev.gpsLng}` : '—'} + + + +
+ + +
+
{ev.logReading ? `${ev.logReading} nm` : '—'} - {ev.gpsLat && ev.gpsLng ? `${ev.gpsLat}, ${ev.gpsLng}` : '—'} - - - -
+
+
+ + {/* Mobile view */} +
+ {events.map((ev, idx) => { + const hasCourse = ev.mgk || ev.rwk; + const hasWind = ev.windDirection || ev.windStrength || ev.windPressure; + const hasSeaState = ev.seaState; + const hasWeather = ev.weatherIcon; + const hasLog = ev.logReading; + const hasGps = ev.gpsLat && ev.gpsLng; + const hasVisibility = ev.visibility; + const hasHeel = ev.heel; + const hasSailsOrMotor = ev.sailsOrMotor; + + return ( +
+
+
+
+ + {ev.time} +
+ +
+ {!readOnly && ( +
-
-
+ )} + + +
+ +
+ {hasCourse && ( + + + + {ev.mgk ? `MgK: ${ev.mgk}°` : ''} + {ev.mgk && ev.rwk ? ' / ' : ''} + {ev.rwk ? `rwK: ${ev.rwk}°` : ''} + + + )} + + {hasWind && ( + + + Wind:{' '} + {[ + ev.windDirection, + ev.windStrength ? `${ev.windStrength} Bft` : '', + ev.windPressure ? `${ev.windPressure} hPa` : '' + ] + .filter(Boolean) + .join(' / ')} + + + )} + + {hasSeaState && ( + + {t('logs.event_sea_state')}: {ev.seaState} + + )} + + {hasWeather && ( + + Weather + + )} + + {hasLog && ( + + Log: {ev.logReading} nm + + )} + + {hasGps && ( + + + {ev.gpsLat}, {ev.gpsLng} + + )} + + {hasVisibility && ( + + {t('logs.event_visibility')}: {ev.visibility} + + )} + + {hasHeel && ( + + {t('logs.event_heel')}: {ev.heel}° + + )} + + {hasSailsOrMotor && ( + + {ev.sailsOrMotor} + + )} +
+ +
+ +
+ + ); + })} + + )} {/* Add New Event Form Sub-Card */}