feat: implement responsive event cards for mobile viewports
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1862,7 +1862,9 @@ export default function LogEntryEditor({
|
||||
{events.length === 0 ? (
|
||||
<div className="dashboard-status-msg mb-6">{t('logs.no_events')}</div>
|
||||
) : (
|
||||
<div className="events-scroll-container mb-6">
|
||||
<>
|
||||
{/* Desktop view */}
|
||||
<div className="events-scroll-container mb-6 events-desktop-only">
|
||||
<table className="events-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -1948,6 +1950,147 @@ export default function LogEntryEditor({
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Mobile view */}
|
||||
<div className="events-mobile-only mb-6">
|
||||
{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 (
|
||||
<div className="event-mobile-card" key={idx}>
|
||||
<div className="event-card-header">
|
||||
<div className="event-card-meta">
|
||||
<div className="event-card-time">
|
||||
<Clock size={14} />
|
||||
<span>{ev.time}</span>
|
||||
</div>
|
||||
<CreatorAvatar
|
||||
creatorId={ev.creatorId}
|
||||
crewSnapshotsById={entryCrew.crewSnapshotsById}
|
||||
size={24}
|
||||
/>
|
||||
</div>
|
||||
{!readOnly && (
|
||||
<div className="event-card-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-icon"
|
||||
onClick={() => handleEditEvent(idx)}
|
||||
title={t('logs.edit_event')}
|
||||
disabled={editingEventIndex !== null && editingEventIndex !== idx}
|
||||
>
|
||||
<Pencil size={14} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-icon danger"
|
||||
onClick={() => handleDeleteEvent(idx)}
|
||||
title={t('logs.delete_event')}
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<hr className="event-card-divider" />
|
||||
|
||||
<div className="event-card-grid">
|
||||
{hasCourse && (
|
||||
<span className="event-card-chip" title={t('logs.event_course_section')}>
|
||||
<Compass size={12} />
|
||||
<span>
|
||||
{ev.mgk ? `MgK: ${ev.mgk}°` : ''}
|
||||
{ev.mgk && ev.rwk ? ' / ' : ''}
|
||||
{ev.rwk ? `rwK: ${ev.rwk}°` : ''}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{hasWind && (
|
||||
<span className="event-card-chip" title={t('logs.event_wind_direction')}>
|
||||
<span>
|
||||
Wind:{' '}
|
||||
{[
|
||||
ev.windDirection,
|
||||
ev.windStrength ? `${ev.windStrength} Bft` : '',
|
||||
ev.windPressure ? `${ev.windPressure} hPa` : ''
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' / ')}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{hasSeaState && (
|
||||
<span className="event-card-chip" title={t('logs.event_sea_state')}>
|
||||
<span>{t('logs.event_sea_state')}: {ev.seaState}</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{hasWeather && (
|
||||
<span className="event-card-chip" title={t('logs.event_weather')}>
|
||||
<img
|
||||
src={`https://openweathermap.org/img/wn/${ev.weatherIcon}.png`}
|
||||
alt="Weather"
|
||||
className="event-card-weather-img"
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{hasLog && (
|
||||
<span className="event-card-chip" title={t('logs.event_log')}>
|
||||
<span>Log: {ev.logReading} nm</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{hasGps && (
|
||||
<span className="event-card-chip" title={t('logs.event_gps')}>
|
||||
<MapPin size={12} />
|
||||
<span className="font-mono text-xs">{ev.gpsLat}, {ev.gpsLng}</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{hasVisibility && (
|
||||
<span className="event-card-chip" title={t('logs.event_visibility')}>
|
||||
<span>{t('logs.event_visibility')}: {ev.visibility}</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{hasHeel && (
|
||||
<span className="event-card-chip" title={t('logs.event_heel')}>
|
||||
<span>{t('logs.event_heel')}: {ev.heel}°</span>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{hasSailsOrMotor && (
|
||||
<span className="event-card-chip" title={t('logs.event_sails')}>
|
||||
<span>{ev.sailsOrMotor}</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="event-card-remarks">
|
||||
<EventRemarksCell
|
||||
event={ev}
|
||||
logbookId={logbookId}
|
||||
voiceMemoLookup={voiceMemoLookup}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Add New Event Form Sub-Card */}
|
||||
|
||||
Reference in New Issue
Block a user