feat(sails): support configuring available sails list in VesselForm and interactive sails selector in LogEntryEditor

This commit is contained in:
2026-05-28 12:44:30 +02:00
parent 19082dcae1
commit e85beba2bc
5 changed files with 275 additions and 5 deletions
+69 -1
View File
@@ -44,12 +44,13 @@ interface LogEvent {
}
export default function LogEntryEditor({ entryId, logbookId, onBack }: LogEntryEditorProps) {
const { t } = useTranslation()
const { t, i18n } = useTranslation()
const { showAlert } = useDialog()
// General details state
const [date, setDate] = useState('')
const [dayOfTravel, setDayOfTravel] = useState('')
const [yachtSails, setYachtSails] = useState<string[]>([])
const [departure, setDeparture] = useState('')
const [destination, setDestination] = useState('')
@@ -120,6 +121,27 @@ export default function LogEntryEditor({ entryId, logbookId, onBack }: LogEntryE
setFuelConsumption(cons >= 0 ? String(cons) : '0')
}, [fuelMorning, fuelRefilled, fuelEvening])
// Load Yacht Sails
useEffect(() => {
async function loadYachtSails() {
try {
const masterKey = getActiveMasterKey()
if (!masterKey) return
const yacht = await db.yachts.get(logbookId)
if (yacht) {
const decrypted = await decryptJson(yacht.encryptedData, yacht.iv, yacht.tag, masterKey)
if (decrypted && decrypted.sails && Array.isArray(decrypted.sails)) {
setYachtSails(decrypted.sails)
}
}
} catch (err) {
console.error('Failed to load yacht sails in editor:', err)
}
}
loadYachtSails()
}, [logbookId])
// Load entry details
useEffect(() => {
async function loadEntry() {
@@ -324,6 +346,33 @@ export default function LogEntryEditor({ entryId, logbookId, onBack }: LogEntryE
}
}
const defaultSails = i18n.language === 'de'
? ['Großsegel', 'Genua', 'Fock', 'Spinnaker', 'Gennaker']
: ['Mainsail', 'Genoa', 'Jib', 'Spinnaker', 'Gennaker']
const toggleSailOrMotor = (item: string) => {
let currentItems = evSailsOrMotor
.split(/\s*(?:\+|\bplus\b|,)\s*/i)
.map(s => s.trim())
.filter(Boolean)
if (currentItems.some(s => s.toLowerCase() === item.toLowerCase())) {
currentItems = currentItems.filter(s => s.toLowerCase() !== item.toLowerCase())
} else {
currentItems.push(item)
}
setEvSailsOrMotor(currentItems.join(' + '))
}
const isItemActive = (item: string) => {
const currentItems = evSailsOrMotor
.split(/\s*(?:\+|\bplus\b|,)\s*/i)
.map(s => s.trim().toLowerCase())
.filter(Boolean)
return currentItems.includes(item.toLowerCase())
}
const handleAddEvent = (e: React.FormEvent) => {
e.preventDefault()
if (!evTime) return
@@ -912,6 +961,25 @@ export default function LogEntryEditor({ entryId, logbookId, onBack }: LogEntryE
onChange={(e) => setEvSailsOrMotor(e.target.value)}
disabled={saving}
/>
<div className="sails-picker-container">
<div className="sails-picker-pills">
{(yachtSails.length > 0 ? yachtSails : defaultSails).map((sail) => (
<span
key={sail}
className={`sail-pill ${isItemActive(sail) ? 'active' : ''}`}
onClick={() => toggleSailOrMotor(sail)}
>
{sail}
</span>
))}
<span
className={`sail-pill motor-pill ${isItemActive(t('logs.motor_propulsion')) ? 'active' : ''}`}
onClick={() => toggleSailOrMotor(t('logs.motor_propulsion'))}
>
{t('logs.motor_propulsion')}
</span>
</div>
</div>
</div>
<div className="input-group">