Initial commit - docs.artetui.de SPAs (ohne Subprojekte)
This commit is contained in:
855
fotoupload/index.html
Normal file
855
fotoupload/index.html
Normal file
@@ -0,0 +1,855 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>PowerToolsX - Foto-Upload</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg-primary: #1E1E1E;
|
||||
--bg-secondary: #2D2D30;
|
||||
--bg-tertiary: #3C3C3C;
|
||||
--text-primary: #E0E0E0;
|
||||
--text-secondary: #A0A0A0;
|
||||
--accent: #0E639C;
|
||||
--accent-hover: #1177BB;
|
||||
--success: #00B454;
|
||||
--warning: #FFB900;
|
||||
--error: #E81123;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
header .projekt-name {
|
||||
color: var(--accent);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.section {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* IFC-Hierarchie Dropdowns */
|
||||
.ifc-select-group {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.ifc-select-group label {
|
||||
display: block;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.ifc-select-group select {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid #555;
|
||||
border-radius: 6px;
|
||||
color: var(--text-primary);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.ifc-select-group select:disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.ifc-path {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* PropertySets */
|
||||
.pset-group {
|
||||
border: 1px solid #444;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.pset-group h4 {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 8px;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.pset-property {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.pset-property label {
|
||||
display: block;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.pset-property select,
|
||||
.pset-property input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid #555;
|
||||
border-radius: 4px;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* Foto-Capture */
|
||||
.capture-area {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.camera-preview {
|
||||
width: 100%;
|
||||
max-height: 300px;
|
||||
background: #000;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preview-image {
|
||||
width: 100%;
|
||||
max-height: 300px;
|
||||
object-fit: contain;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 14px 24px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--accent-hover);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
background: #555;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: var(--success);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.btn-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.btn-row .btn {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Beschreibung */
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid #555;
|
||||
border-radius: 6px;
|
||||
color: var(--text-primary);
|
||||
font-size: 1rem;
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
/* Status */
|
||||
.status {
|
||||
text-align: center;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.status.success {
|
||||
background: rgba(0, 180, 84, 0.2);
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.status.error {
|
||||
background: rgba(232, 17, 35, 0.2);
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
.status.info {
|
||||
background: rgba(14, 99, 156, 0.2);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Upload Counter */
|
||||
.upload-counter {
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.upload-counter strong {
|
||||
color: var(--success);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
/* Hidden file input */
|
||||
#fileInput {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Loading Overlay */
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.loading-overlay.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 4px solid var(--bg-tertiary);
|
||||
border-top-color: var(--accent);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* No Session */
|
||||
.no-session {
|
||||
text-align: center;
|
||||
padding: 48px 24px;
|
||||
}
|
||||
|
||||
.no-session h2 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>PowerToolsX Foto-Upload</h1>
|
||||
<div class="projekt-name" id="projektName">Wird geladen...</div>
|
||||
</header>
|
||||
|
||||
<div id="noSession" class="no-session" style="display: none;">
|
||||
<h2>Keine aktive Session</h2>
|
||||
<p>Bitte scannen Sie den QR-Code in PowerToolsX um eine Upload-Session zu starten.</p>
|
||||
</div>
|
||||
|
||||
<div id="mainContent" style="display: none;">
|
||||
<!-- IFC-Struktur Auswahl -->
|
||||
<div class="section">
|
||||
<div class="section-title">Ort im Bauwerk</div>
|
||||
|
||||
<div class="ifc-select-group">
|
||||
<label>Gebaude</label>
|
||||
<select id="selectBuilding" onchange="onBuildingChanged()">
|
||||
<option value="">-- Gebaude wahlen --</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="ifc-select-group">
|
||||
<label>Geschoss</label>
|
||||
<select id="selectStorey" onchange="onStoreyChanged()" disabled>
|
||||
<option value="">-- Geschoss wahlen --</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="ifc-select-group">
|
||||
<label>Raum</label>
|
||||
<select id="selectSpace" onchange="onSpaceChanged()" disabled>
|
||||
<option value="">-- Raum wahlen --</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="ifc-path" id="ifcPath" style="display: none;">
|
||||
<strong>Pfad:</strong> <span id="pathText"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PropertySets -->
|
||||
<div class="section" id="propertySetsSection">
|
||||
<div class="section-title">Eigenschaften</div>
|
||||
<div id="propertySetsContainer"></div>
|
||||
</div>
|
||||
|
||||
<!-- Beschreibung -->
|
||||
<div class="section">
|
||||
<div class="section-title">Beschreibung</div>
|
||||
<textarea id="description" placeholder="Optionale Beschreibung zum Foto..."></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Foto aufnehmen -->
|
||||
<div class="section">
|
||||
<div class="section-title">Foto</div>
|
||||
<div class="capture-area">
|
||||
<video id="cameraPreview" class="camera-preview" autoplay playsinline></video>
|
||||
<img id="previewImage" class="preview-image" alt="Vorschau">
|
||||
|
||||
<input type="file" id="fileInput" accept="image/*" capture="environment" onchange="onFileSelected(event)">
|
||||
|
||||
<button class="btn btn-primary" id="btnCapture" onclick="openCamera()">
|
||||
<span>Foto aufnehmen</span>
|
||||
</button>
|
||||
|
||||
<div class="btn-row" id="previewButtons" style="display: none;">
|
||||
<button class="btn btn-secondary" onclick="resetCapture()">Neu</button>
|
||||
<button class="btn btn-success" onclick="uploadPhoto()">Hochladen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<div id="statusMessage" class="status info" style="display: none;"></div>
|
||||
|
||||
<!-- Upload Counter -->
|
||||
<div class="upload-counter">
|
||||
Hochgeladene Fotos: <strong id="uploadCount">0</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Loading Overlay -->
|
||||
<div class="loading-overlay" id="loadingOverlay">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Konfiguration
|
||||
const DATA_URL = 'https://docs.artetui.de/backups/fotoupload';
|
||||
|
||||
// Session-Daten
|
||||
let sessionId = null;
|
||||
let strukturData = null;
|
||||
let selectedElement = null;
|
||||
let capturedImageData = null;
|
||||
let uploadCount = 0;
|
||||
|
||||
// Initialisierung
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Session-ID aus URL holen
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
sessionId = params.get('session');
|
||||
|
||||
if (!sessionId) {
|
||||
document.getElementById('noSession').style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
// Struktur laden
|
||||
await loadStruktur();
|
||||
});
|
||||
|
||||
// IFC-Struktur vom Server laden
|
||||
async function loadStruktur() {
|
||||
showLoading(true);
|
||||
try {
|
||||
const response = await fetch(`${DATA_URL}/struktur_${sessionId}.json`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Struktur nicht gefunden');
|
||||
}
|
||||
|
||||
strukturData = await response.json();
|
||||
|
||||
// UI aktualisieren
|
||||
document.getElementById('projektName').textContent = strukturData.ProjektName || 'Projekt';
|
||||
document.getElementById('mainContent').style.display = 'block';
|
||||
|
||||
// Gebaude-Dropdown fullen
|
||||
populateBuildingSelect();
|
||||
|
||||
// PropertySets generieren
|
||||
generatePropertySets();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Struktur:', error);
|
||||
document.getElementById('projektName').textContent = 'Fehler beim Laden';
|
||||
showStatus('Struktur konnte nicht geladen werden. Bitte erneut versuchen.', 'error');
|
||||
document.getElementById('mainContent').style.display = 'block';
|
||||
} finally {
|
||||
showLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Gebaude-Dropdown fullen
|
||||
function populateBuildingSelect() {
|
||||
const select = document.getElementById('selectBuilding');
|
||||
select.innerHTML = '<option value="">-- Gebaude wahlen --</option>';
|
||||
|
||||
if (!strukturData || !strukturData.Elemente) return;
|
||||
|
||||
// Finde alle IfcBuilding Elemente
|
||||
const buildings = findElementsByType(strukturData.Elemente, 'IfcBuilding');
|
||||
|
||||
buildings.forEach(b => {
|
||||
const option = document.createElement('option');
|
||||
option.value = b.GlobalId;
|
||||
option.textContent = `${b.Icon || ''} ${b.Name}`;
|
||||
option.dataset.element = JSON.stringify(b);
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
// Rekursiv Elemente nach Typ finden
|
||||
function findElementsByType(elements, type) {
|
||||
let result = [];
|
||||
for (const el of elements) {
|
||||
if (el.IfcType === type) {
|
||||
result.push(el);
|
||||
}
|
||||
if (el.Children && el.Children.length > 0) {
|
||||
result = result.concat(findElementsByType(el.Children, type));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Element nach GlobalId finden
|
||||
function findElementById(elements, globalId) {
|
||||
for (const el of elements) {
|
||||
if (el.GlobalId === globalId) {
|
||||
return el;
|
||||
}
|
||||
if (el.Children && el.Children.length > 0) {
|
||||
const found = findElementById(el.Children, globalId);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Gebaude geandert
|
||||
function onBuildingChanged() {
|
||||
const select = document.getElementById('selectBuilding');
|
||||
const storeySelect = document.getElementById('selectStorey');
|
||||
const spaceSelect = document.getElementById('selectSpace');
|
||||
|
||||
// Geschoss und Raum zurucksetzen
|
||||
storeySelect.innerHTML = '<option value="">-- Geschoss wahlen --</option>';
|
||||
storeySelect.disabled = true;
|
||||
spaceSelect.innerHTML = '<option value="">-- Raum wahlen --</option>';
|
||||
spaceSelect.disabled = true;
|
||||
selectedElement = null;
|
||||
updatePath();
|
||||
|
||||
if (!select.value) return;
|
||||
|
||||
// Gebaude finden
|
||||
const building = findElementById(strukturData.Elemente, select.value);
|
||||
if (!building) return;
|
||||
|
||||
// Geschosse dieses Gebaudes finden
|
||||
const storeys = findElementsByType(building.Children || [], 'IfcBuildingStorey');
|
||||
|
||||
storeys.forEach(s => {
|
||||
const option = document.createElement('option');
|
||||
option.value = s.GlobalId;
|
||||
option.textContent = `${s.Icon || ''} ${s.Name}`;
|
||||
option.dataset.element = JSON.stringify(s);
|
||||
storeySelect.appendChild(option);
|
||||
});
|
||||
|
||||
storeySelect.disabled = storeys.length === 0;
|
||||
}
|
||||
|
||||
// Geschoss geandert
|
||||
function onStoreyChanged() {
|
||||
const buildingSelect = document.getElementById('selectBuilding');
|
||||
const storeySelect = document.getElementById('selectStorey');
|
||||
const spaceSelect = document.getElementById('selectSpace');
|
||||
|
||||
// Raum zurucksetzen
|
||||
spaceSelect.innerHTML = '<option value="">-- Raum wahlen --</option>';
|
||||
spaceSelect.disabled = true;
|
||||
|
||||
if (!storeySelect.value) {
|
||||
selectedElement = null;
|
||||
updatePath();
|
||||
return;
|
||||
}
|
||||
|
||||
// Gebaude und Geschoss finden
|
||||
const building = findElementById(strukturData.Elemente, buildingSelect.value);
|
||||
const storey = findElementById(building?.Children || [], storeySelect.value);
|
||||
|
||||
if (!storey) {
|
||||
selectedElement = null;
|
||||
updatePath();
|
||||
return;
|
||||
}
|
||||
|
||||
// Raume dieses Geschosses finden
|
||||
const spaces = findElementsByType(storey.Children || [], 'IfcSpace');
|
||||
|
||||
spaces.forEach(s => {
|
||||
const option = document.createElement('option');
|
||||
option.value = s.GlobalId;
|
||||
option.textContent = `${s.Icon || ''} ${s.Name}`;
|
||||
option.dataset.element = JSON.stringify(s);
|
||||
spaceSelect.appendChild(option);
|
||||
});
|
||||
|
||||
spaceSelect.disabled = spaces.length === 0;
|
||||
|
||||
// Geschoss als ausgewahltes Element setzen (falls kein Raum gewahlt)
|
||||
selectedElement = storey;
|
||||
updatePath();
|
||||
}
|
||||
|
||||
// Raum geandert
|
||||
function onSpaceChanged() {
|
||||
const buildingSelect = document.getElementById('selectBuilding');
|
||||
const storeySelect = document.getElementById('selectStorey');
|
||||
const spaceSelect = document.getElementById('selectSpace');
|
||||
|
||||
if (!spaceSelect.value) {
|
||||
// Zurück zum Geschoss
|
||||
const building = findElementById(strukturData.Elemente, buildingSelect.value);
|
||||
selectedElement = findElementById(building?.Children || [], storeySelect.value);
|
||||
} else {
|
||||
// Raum finden
|
||||
const building = findElementById(strukturData.Elemente, buildingSelect.value);
|
||||
const storey = findElementById(building?.Children || [], storeySelect.value);
|
||||
selectedElement = findElementById(storey?.Children || [], spaceSelect.value);
|
||||
}
|
||||
updatePath();
|
||||
}
|
||||
|
||||
// Pfad-Anzeige aktualisieren
|
||||
function updatePath() {
|
||||
const pathDiv = document.getElementById('ifcPath');
|
||||
const pathText = document.getElementById('pathText');
|
||||
|
||||
if (!selectedElement) {
|
||||
pathDiv.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
// Pfad aus den Dropdowns bauen
|
||||
const parts = [];
|
||||
const buildingSelect = document.getElementById('selectBuilding');
|
||||
const storeySelect = document.getElementById('selectStorey');
|
||||
const spaceSelect = document.getElementById('selectSpace');
|
||||
|
||||
if (buildingSelect.value) {
|
||||
const opt = buildingSelect.selectedOptions[0];
|
||||
parts.push(opt.textContent.trim());
|
||||
}
|
||||
if (storeySelect.value) {
|
||||
const opt = storeySelect.selectedOptions[0];
|
||||
parts.push(opt.textContent.trim());
|
||||
}
|
||||
if (spaceSelect.value) {
|
||||
const opt = spaceSelect.selectedOptions[0];
|
||||
parts.push(opt.textContent.trim());
|
||||
}
|
||||
|
||||
pathText.textContent = parts.join(' > ');
|
||||
pathDiv.style.display = 'block';
|
||||
}
|
||||
|
||||
// PropertySets generieren
|
||||
function generatePropertySets() {
|
||||
const container = document.getElementById('propertySetsContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!strukturData || !strukturData.PropertySets) {
|
||||
document.getElementById('propertySetsSection').style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
strukturData.PropertySets.forEach(pset => {
|
||||
const group = document.createElement('div');
|
||||
group.className = 'pset-group';
|
||||
group.innerHTML = `<h4>${pset.Label}</h4>`;
|
||||
|
||||
pset.Properties.forEach(prop => {
|
||||
const propDiv = document.createElement('div');
|
||||
propDiv.className = 'pset-property';
|
||||
|
||||
let inputHtml = '';
|
||||
if (prop.Type === 'select' && prop.Options) {
|
||||
inputHtml = `<select id="pset_${pset.Name}_${prop.Name}">
|
||||
<option value="">-- Wahlen --</option>
|
||||
${prop.Options.map(o => `<option value="${o}">${o}</option>`).join('')}
|
||||
</select>`;
|
||||
} else {
|
||||
inputHtml = `<input type="text" id="pset_${pset.Name}_${prop.Name}" placeholder="${prop.Label}">`;
|
||||
}
|
||||
|
||||
propDiv.innerHTML = `
|
||||
<label>${prop.Label}</label>
|
||||
${inputHtml}
|
||||
`;
|
||||
group.appendChild(propDiv);
|
||||
});
|
||||
|
||||
container.appendChild(group);
|
||||
});
|
||||
|
||||
document.getElementById('propertySetsSection').style.display = 'block';
|
||||
}
|
||||
|
||||
// PropertySet-Werte sammeln
|
||||
function collectPropertyValues() {
|
||||
const values = {};
|
||||
if (!strukturData || !strukturData.PropertySets) return values;
|
||||
|
||||
strukturData.PropertySets.forEach(pset => {
|
||||
pset.Properties.forEach(prop => {
|
||||
const input = document.getElementById(`pset_${pset.Name}_${prop.Name}`);
|
||||
if (input && input.value) {
|
||||
values[`${pset.Name}.${prop.Name}`] = input.value;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
// Kamera offnen
|
||||
function openCamera() {
|
||||
document.getElementById('fileInput').click();
|
||||
}
|
||||
|
||||
// Datei ausgewahlt
|
||||
function onFileSelected(event) {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
capturedImageData = e.target.result;
|
||||
|
||||
// Vorschau anzeigen
|
||||
const img = document.getElementById('previewImage');
|
||||
img.src = capturedImageData;
|
||||
img.style.display = 'block';
|
||||
|
||||
// Buttons umschalten
|
||||
document.getElementById('btnCapture').style.display = 'none';
|
||||
document.getElementById('previewButtons').style.display = 'flex';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
// Aufnahme zurucksetzen
|
||||
function resetCapture() {
|
||||
capturedImageData = null;
|
||||
document.getElementById('previewImage').style.display = 'none';
|
||||
document.getElementById('previewImage').src = '';
|
||||
document.getElementById('btnCapture').style.display = 'block';
|
||||
document.getElementById('previewButtons').style.display = 'none';
|
||||
document.getElementById('fileInput').value = '';
|
||||
}
|
||||
|
||||
// Foto hochladen
|
||||
async function uploadPhoto() {
|
||||
if (!capturedImageData) {
|
||||
showStatus('Bitte erst ein Foto aufnehmen', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading(true);
|
||||
try {
|
||||
// Upload-Daten zusammenstellen
|
||||
const timestamp = Date.now().toString(16);
|
||||
const fileName = `${sessionId}_${timestamp}.json`;
|
||||
|
||||
// Pfad bauen
|
||||
const pathParts = [];
|
||||
const buildingSelect = document.getElementById('selectBuilding');
|
||||
const storeySelect = document.getElementById('selectStorey');
|
||||
const spaceSelect = document.getElementById('selectSpace');
|
||||
|
||||
if (buildingSelect.value) pathParts.push(buildingSelect.selectedOptions[0].textContent.trim());
|
||||
if (storeySelect.value) pathParts.push(storeySelect.selectedOptions[0].textContent.trim());
|
||||
if (spaceSelect.value) pathParts.push(spaceSelect.selectedOptions[0].textContent.trim());
|
||||
|
||||
const uploadData = {
|
||||
Timestamp: new Date().toISOString(),
|
||||
Filename: `foto_${Date.now()}.jpg`,
|
||||
Description: document.getElementById('description').value,
|
||||
Base64: capturedImageData,
|
||||
Size: capturedImageData.length,
|
||||
Type: 'image/jpeg',
|
||||
ElementGlobalId: selectedElement?.GlobalId || null,
|
||||
ElementPath: pathParts.join(' > '),
|
||||
Properties: collectPropertyValues()
|
||||
};
|
||||
|
||||
// Per WebDAV PUT hochladen
|
||||
const response = await fetch(`${DATA_URL}/${fileName}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(uploadData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Upload fehlgeschlagen: ${response.status}`);
|
||||
}
|
||||
|
||||
// Erfolg
|
||||
uploadCount++;
|
||||
document.getElementById('uploadCount').textContent = uploadCount;
|
||||
showStatus('Foto erfolgreich hochgeladen!', 'success');
|
||||
|
||||
// Zurucksetzen
|
||||
resetCapture();
|
||||
document.getElementById('description').value = '';
|
||||
|
||||
} catch (error) {
|
||||
console.error('Upload-Fehler:', error);
|
||||
showStatus('Upload fehlgeschlagen: ' + error.message, 'error');
|
||||
} finally {
|
||||
showLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Status-Meldung anzeigen
|
||||
function showStatus(message, type) {
|
||||
const statusDiv = document.getElementById('statusMessage');
|
||||
statusDiv.textContent = message;
|
||||
statusDiv.className = `status ${type}`;
|
||||
statusDiv.style.display = 'block';
|
||||
|
||||
// Nach 5 Sekunden ausblenden
|
||||
setTimeout(() => {
|
||||
statusDiv.style.display = 'none';
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Loading-Overlay
|
||||
function showLoading(show) {
|
||||
document.getElementById('loadingOverlay').classList.toggle('active', show);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<footer style="text-align:center;padding:1rem;margin-top:2rem;border-top:1px solid #e5e7eb;font-size:0.85rem;color:#6b7280;">
|
||||
<a href="#" onclick="openImpressum();return false;" style="color:#6b7280;text-decoration:none;">Impressum</a>
|
||||
<span style="color:#d1d5db;margin:0 0.5rem;">|</span>
|
||||
<a href="#" onclick="openDatenschutz();return false;" style="color:#6b7280;text-decoration:none;">Datenschutz</a>
|
||||
</footer>
|
||||
|
||||
|
||||
<!-- IMPRESSUM MODAL -->
|
||||
<div class="legal-modal" id="impressumModal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:9999;align-items:center;justify-content:center;">
|
||||
<div style="background:white;width:90%;max-width:900px;height:90vh;border-radius:12px;margin:20px;overflow:hidden;display:flex;flex-direction:column;">
|
||||
<div style="padding:0.75rem 1.5rem;border-bottom:1px solid #e5e7eb;display:flex;justify-content:space-between;align-items:center;flex-shrink:0;">
|
||||
<h2 style="margin:0;font-size:1.25rem;color:#1f2937;">Impressum</h2>
|
||||
<button onclick="closeImpressum()" style="background:none;border:none;font-size:1.5rem;cursor:pointer;color:#6b7280;line-height:1;">×</button>
|
||||
</div>
|
||||
<iframe src="/legal/impressum.html" style="flex:1;width:100%;border:none;"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DATENSCHUTZ MODAL -->
|
||||
<div class="legal-modal" id="datenschutzModal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:9999;align-items:center;justify-content:center;">
|
||||
<div style="background:white;width:90%;max-width:900px;height:90vh;border-radius:12px;margin:20px;overflow:hidden;display:flex;flex-direction:column;">
|
||||
<div style="padding:0.75rem 1.5rem;border-bottom:1px solid #e5e7eb;display:flex;justify-content:space-between;align-items:center;flex-shrink:0;">
|
||||
<h2 style="margin:0;font-size:1.25rem;color:#1f2937;">Datenschutz</h2>
|
||||
<button onclick="closeDatenschutz()" style="background:none;border:none;font-size:1.5rem;cursor:pointer;color:#6b7280;line-height:1;">×</button>
|
||||
</div>
|
||||
<iframe src="/legal/datenschutz.html" style="flex:1;width:100%;border:none;"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function openImpressum(){document.getElementById("impressumModal").style.display="flex";}
|
||||
function closeImpressum(){document.getElementById("impressumModal").style.display="none";}
|
||||
function openDatenschutz(){document.getElementById("datenschutzModal").style.display="flex";}
|
||||
function closeDatenschutz(){document.getElementById("datenschutzModal").style.display="none";}
|
||||
document.addEventListener("keydown",function(e){if(e.key==="Escape"){closeImpressum();closeDatenschutz();}});
|
||||
["impressumModal","datenschutzModal"].forEach(function(id){
|
||||
var el=document.getElementById(id);
|
||||
if(el)el.addEventListener("click",function(e){if(e.target===this)this.style.display="none";});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user