added "add media" buttons to prevent media picker opening automatically and added status captions
Sonarqube Scanner / Build and analyze (push) Successful in 1m2s
Sonarqube Scanner / Build and analyze (push) Successful in 1m2s
This commit is contained in:
@@ -173,6 +173,49 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.editor-media-actions {
|
||||
display: grid;
|
||||
gap: .75rem;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.editor-media-button {
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(49, 93, 58, .35);
|
||||
border-radius: .85rem;
|
||||
color: #233326;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
font: inherit;
|
||||
font-weight: 700;
|
||||
gap: .65rem;
|
||||
justify-content: center;
|
||||
min-height: 3.5rem;
|
||||
padding: .65rem .8rem;
|
||||
}
|
||||
|
||||
.editor-media-button:hover {
|
||||
background: #eef5ef;
|
||||
border-color: #315d3a;
|
||||
}
|
||||
|
||||
.editor-media-button:focus-visible {
|
||||
outline: .2rem solid #f2ca6b;
|
||||
outline-offset: .15rem;
|
||||
}
|
||||
|
||||
.editor-media-icon {
|
||||
fill: none;
|
||||
flex: 0 0 2rem;
|
||||
height: 2rem;
|
||||
stroke: currentColor;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-width: 2;
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.editor-round-button {
|
||||
align-items: center;
|
||||
background: #315d3a;
|
||||
@@ -408,3 +451,9 @@
|
||||
grid-template-columns: max-content minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 34rem) {
|
||||
.editor-media-actions {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
+20
-4
@@ -401,10 +401,26 @@
|
||||
<input id="editor-poi-radius" type="number" inputmode="numeric" min="1" step="1" value="25">
|
||||
</div>
|
||||
|
||||
<label for="editor-poi-audio">Audiodatei verknüpfen</label>
|
||||
<input id="editor-poi-audio" type="file" accept="audio/*">
|
||||
<label for="editor-poi-images">Bilder verknüpfen</label>
|
||||
<input id="editor-poi-images" type="file" accept="image/*" multiple>
|
||||
<div class="editor-media-actions" aria-label="Medien zur Station hinzufügen">
|
||||
<button id="editor-poi-audio-add" class="editor-media-button" type="button" title="Audio hinzufügen">
|
||||
<svg class="editor-media-icon" viewBox="0 0 32 32" aria-hidden="true" focusable="false">
|
||||
<path d="M18 5v15.2a5 5 0 1 1-2-4V9l10-2v9.2a5 5 0 1 1-2-4V4.6z"/>
|
||||
<path d="M6 4v6M3 7h6"/>
|
||||
</svg>
|
||||
<span>Audio hinzufügen</span>
|
||||
</button>
|
||||
<button id="editor-poi-images-add" class="editor-media-button" type="button" title="Bild hinzufügen">
|
||||
<svg class="editor-media-icon" viewBox="0 0 32 32" aria-hidden="true" focusable="false">
|
||||
<path d="M6 10h4l2-3h8l2 3h4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V12a2 2 0 0 1 2-2z"/>
|
||||
<circle cx="16" cy="18" r="5"/>
|
||||
<path d="M6 3v6M3 6h6"/>
|
||||
</svg>
|
||||
<span>Bild hinzufügen</span>
|
||||
</button>
|
||||
</div>
|
||||
<input id="editor-poi-audio" type="file" accept="audio/*" hidden>
|
||||
<input id="editor-poi-images" type="file" accept="image/*" multiple hidden>
|
||||
<p id="editor-poi-media-status" class="editor-muted" aria-live="polite"></p>
|
||||
|
||||
<button id="editor-poi-save" class="editor-primary-button" type="submit">Station speichern</button>
|
||||
</form>
|
||||
|
||||
+41
-6
@@ -11,6 +11,7 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
||||
dialogTitle: 'editor-dialog-title',
|
||||
newRoute: 'editor-new-route',
|
||||
poiAudio: 'editor-poi-audio',
|
||||
poiAudioAdd: 'editor-poi-audio-add',
|
||||
poiCancel: 'editor-poi-cancel',
|
||||
poiDescription: 'editor-poi-description',
|
||||
poiForm: 'editor-poi-form',
|
||||
@@ -18,9 +19,11 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
||||
poiFormView: 'editor-poi-form-view',
|
||||
poiId: 'editor-poi-id',
|
||||
poiImages: 'editor-poi-images',
|
||||
poiImagesAdd: 'editor-poi-images-add',
|
||||
poiLat: 'editor-poi-lat',
|
||||
poiList: 'editor-poi-list',
|
||||
poiLon: 'editor-poi-lon',
|
||||
poiMediaStatus: 'editor-poi-media-status',
|
||||
poiRadius: 'editor-poi-radius',
|
||||
poiSave: 'editor-poi-save',
|
||||
poiTitle: 'editor-poi-title',
|
||||
@@ -49,6 +52,9 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
||||
form: 'editor-form',
|
||||
iconButton: 'editor-icon-button',
|
||||
iconRow: 'editor-icon-row',
|
||||
mediaActions: 'editor-media-actions',
|
||||
mediaButton: 'editor-media-button',
|
||||
mediaIcon: 'editor-media-icon',
|
||||
muted: 'editor-muted',
|
||||
poiItem: 'editor-poi-item',
|
||||
poiList: 'editor-poi-list',
|
||||
@@ -449,6 +455,29 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
||||
}
|
||||
}
|
||||
|
||||
function updatePoiMediaStatus(poi = state.currentPoi) {
|
||||
const status = byId(ids.poiMediaStatus);
|
||||
if (!status) return;
|
||||
const selectedAudio = byId(ids.poiAudio)?.files?.[0];
|
||||
const selectedImages = Array.from(byId(ids.poiImages)?.files || []);
|
||||
const hasExistingAudio = Boolean(poi?.audio || poi?.audioUrl || poi?.audio_path);
|
||||
const existingImages = Array.isArray(poi?.images) ? poi.images.length : 0;
|
||||
const parts = [];
|
||||
|
||||
if (selectedAudio) parts.push(`Audio ausgewählt: ${selectedAudio.name || 'Audiodatei'}`);
|
||||
else if (hasExistingAudio) parts.push('Audio vorhanden');
|
||||
|
||||
if (selectedImages.length) parts.push(`${selectedImages.length} Bild${selectedImages.length === 1 ? '' : 'er'} ausgewählt`);
|
||||
else if (existingImages) parts.push(`${existingImages} Bild${existingImages === 1 ? '' : 'er'} vorhanden`);
|
||||
|
||||
status.textContent = parts.length ? parts.join(' · ') : 'Noch keine neuen Medien ausgewählt.';
|
||||
}
|
||||
|
||||
function openFilePicker(inputId) {
|
||||
const input = byId(inputId);
|
||||
if (input && typeof input.click === 'function') input.click();
|
||||
}
|
||||
|
||||
function fillPoiForm(poi) {
|
||||
byId(ids.poiId).value = poiId(poi);
|
||||
byId(ids.poiTitle).value = poiTitle(poi) === 'Unbenannte Station' ? '' : poiTitle(poi);
|
||||
@@ -458,6 +487,7 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
||||
byId(ids.poiRadius).value = text(poi?.triggerRadiusM ?? poi?.radiusM ?? poi?.radius ?? 25);
|
||||
byId(ids.poiAudio).value = '';
|
||||
byId(ids.poiImages).value = '';
|
||||
updatePoiMediaStatus(poi);
|
||||
}
|
||||
|
||||
async function loadRoute(route) {
|
||||
@@ -540,9 +570,9 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
||||
state.currentRoute = { ...saved.route, id: saved.id, gpx: saved.gpx, _source: 'local' };
|
||||
state.currentSource = 'local';
|
||||
state.pois = saved.pois;
|
||||
setStatus('Lokaler Entwurf gespeichert. Es wurden keine Daten an den Server übertragen.');
|
||||
await loadRoutes();
|
||||
await loadRoute({ id: saved.id, _source: 'local' });
|
||||
setStatus('Strecke erfolgreich lokal gespeichert. Es wurden keine Daten an den Server übertragen.');
|
||||
}
|
||||
|
||||
async function saveServerRoute(values) {
|
||||
@@ -555,9 +585,9 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
||||
: await sendJson(path, 'PUT', values);
|
||||
const saved = payload.route || payload;
|
||||
state.currentRoute = { ...saved, _source: 'server' };
|
||||
setStatus('Veröffentlichte Strecke gespeichert. activated_at wurde nicht verändert.');
|
||||
await loadRoutes();
|
||||
await loadRoute({ id, _source: 'server' });
|
||||
setStatus('Strecke erfolgreich gespeichert. activated_at wurde nicht verändert.');
|
||||
}
|
||||
|
||||
async function saveRoute() {
|
||||
@@ -643,18 +673,19 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
||||
await loadRoute({ id: routeId(state.currentRoute), _source: 'local' });
|
||||
}
|
||||
|
||||
async function uploadOne(path, file) {
|
||||
async function uploadOne(path, fieldName, file) {
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
data.append(fieldName, file);
|
||||
return sendForm(path, 'POST', data);
|
||||
}
|
||||
|
||||
async function uploadPoiMedia(currentRouteId, currentPoiId) {
|
||||
const audio = byId(ids.poiAudio)?.files?.[0];
|
||||
const images = Array.from(byId(ids.poiImages)?.files || []);
|
||||
if (audio) await uploadOne(`/routes/${encodeURIComponent(currentRouteId)}/pois/${encodeURIComponent(currentPoiId)}/audio`, audio);
|
||||
const basePath = `/routes/${encodeURIComponent(currentRouteId)}/pois/${encodeURIComponent(currentPoiId)}`;
|
||||
if (audio) await uploadOne(`${basePath}/audio`, 'audio', audio);
|
||||
for (const image of images) {
|
||||
await uploadOne(`/routes/${encodeURIComponent(currentRouteId)}/pois/${encodeURIComponent(currentPoiId)}/images`, image);
|
||||
await uploadOne(`${basePath}/pictures`, 'picture', image);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,6 +795,10 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
|
||||
activate(byId(ids.newRoute), newRoute);
|
||||
activate(byId(ids.routeBack), () => guarded(loadRoutes).then(showListView));
|
||||
activate(byId(ids.addPoi), newPoi);
|
||||
activate(byId(ids.poiAudioAdd), () => openFilePicker(ids.poiAudio));
|
||||
activate(byId(ids.poiImagesAdd), () => openFilePicker(ids.poiImages));
|
||||
byId(ids.poiAudio)?.addEventListener('change', () => updatePoiMediaStatus());
|
||||
byId(ids.poiImages)?.addEventListener('change', () => updatePoiMediaStatus());
|
||||
activate(byId(ids.poiCancel), showRouteView);
|
||||
activate(byId(ids.routeList), event => guarded(() => routeListAction(event)));
|
||||
activate(byId(ids.poiList), poiListAction);
|
||||
|
||||
Reference in New Issue
Block a user