Familienangebote im August: Unterschied zwischen den Versionen
Aus Bündnis für Familie Tübingen.
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
| Zeile 1: | Zeile 1: | ||
<div id=" | <html lang="de"> | ||
<head> | |||
<meta charset="UTF-8"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
<title>Familienangebote August 2026</title> | |||
<style> | |||
body{ | |||
font-family: Arial, sans-serif; | |||
margin:20px; | |||
background:#f5f5f5; | |||
} | |||
h1{ | |||
text-align:center; | |||
} | |||
.toolbar{ | |||
display:flex; | |||
gap:10px; | |||
margin-bottom:20px; | |||
justify-content:center; | |||
} | |||
button{ | |||
cursor:pointer; | |||
} | |||
.calendar{ | |||
display:grid; | |||
grid-template-columns:repeat(7,1fr); | |||
gap:10px; | |||
} | |||
.weekday{ | |||
background:#003366; | |||
color:white; | |||
padding:10px; | |||
text-align:center; | |||
font-weight:bold; | |||
} | |||
.day{ | |||
background:white; | |||
min-height:150px; | |||
border-radius:8px; | |||
padding:8px; | |||
box-shadow:0 2px 5px rgba(0,0,0,.15); | |||
cursor:pointer; | |||
} | |||
.day-number{ | |||
font-weight:bold; | |||
margin-bottom:8px; | |||
} | |||
.entry{ | |||
background:#e8f2ff; | |||
padding:4px; | |||
margin-bottom:4px; | |||
border-radius:4px; | |||
font-size:12px; | |||
} | |||
.modal{ | |||
display:none; | |||
position:fixed; | |||
inset:0; | |||
background:rgba(0,0,0,.5); | |||
justify-content:center; | |||
align-items:center; | |||
} | |||
.modal-content{ | |||
background:white; | |||
width:90%; | |||
max-width:900px; | |||
max-height:90vh; | |||
overflow:auto; | |||
padding:20px; | |||
border-radius:10px; | |||
} | |||
label{ | |||
display:block; | |||
margin-top:10px; | |||
font-weight:bold; | |||
} | |||
input, textarea{ | |||
width:100%; | |||
padding:8px; | |||
box-sizing:border-box; | |||
} | |||
.entry-card{ | |||
border:1px solid #ccc; | |||
padding:10px; | |||
margin:10px 0; | |||
border-radius:6px; | |||
} | |||
.actions{ | |||
display:flex; | |||
gap:10px; | |||
margin-top:10px; | |||
} | |||
.close-btn{ | |||
float:right; | |||
} | |||
.empty{ | |||
background:transparent; | |||
} | |||
</style> | |||
</head> | |||
<body> | |||
<h1>Familienangebote – August 2026</h1> | |||
<div class="toolbar"> | |||
<button onclick="exportData()">Export JSON</button> | |||
<button onclick="exportPDF()">Export PDF</button> | |||
<button onclick="exportExcel()">Export Excel</button> | |||
<input type="file" id="importFile" onchange="importData(event)"> | |||
</div> | |||
<div class="calendar" id="calendar"></div> | |||
<div class="modal" id="modal"> | |||
<div class="modal-content"> | |||
<button class="close-btn" onclick="closeModal()">Schließen</button> | |||
<h2 id="modalDate"></h2> | |||
<button onclick="addEntry()">+ Neuer Eintrag</button> | |||
<div id="entriesContainer"></div> | |||
</div> | |||
</div> | |||
<script> | <script> | ||
let data = { | |||
"1": [ | "1": [ | ||
{ | { | ||
| Zeile 259: | Zeile 402: | ||
}; | }; | ||
let selectedDay = null; | |||
function saveData() { | |||
renderCalendar(); | |||
} | } | ||
. | const weekdays = ["Mo","Di","Mi","Do","Fr","Sa","So"]; | ||
function renderCalendar(){ | |||
const cal = document.getElementById("calendar"); | |||
cal.innerHTML = ""; | |||
weekdays.forEach(day=>{ | |||
const div = document.createElement("div"); | |||
div.className = "weekday"; | |||
div.textContent = day; | |||
cal.appendChild(div); | |||
}); | |||
const firstDay = new Date(2026,7,1); | |||
let offset = firstDay.getDay(); | |||
offset = offset === 0 ? 6 : offset - 1; | |||
for(let i=0;i<offset;i++){ | |||
const empty = document.createElement("div"); | |||
empty.className = "empty"; | |||
cal.appendChild(empty); | |||
} | |||
for(let day=1; day<=31; day++){ | |||
const div = document.createElement("div"); | |||
div.className = "day"; | |||
div.onclick = () => openDay(day); | |||
const dayNumber = document.createElement("div"); | |||
dayNumber.className = "day-number"; | |||
dayNumber.textContent = day; | |||
div.appendChild(dayNumber); | |||
const entries = data[day] || []; | |||
entries.forEach(entry=>{ | |||
const e = document.createElement("div"); | |||
e.className = "entry"; | |||
e.textContent = entry.angebot || "(ohne Titel)"; | |||
div.appendChild(e); | |||
}); | |||
cal.appendChild(div); | |||
} | |||
} | } | ||
function openDay(day){ | |||
selectedDay = day; | |||
document.getElementById("modal").style.display="flex"; | |||
document.getElementById("modalDate").textContent = | |||
`Samstag, ${day}. August 2026`.replace("Samstag", new Date(2026,7,day) | |||
.toLocaleDateString("de-DE",{weekday:"long"})); | |||
renderEntries(); | |||
} | } | ||
function closeModal(){ | |||
document.getElementById("modal").style.display="none"; | |||
} | } | ||
function renderEntries(){ | |||
const container = document.getElementById("entriesContainer"); | |||
container.innerHTML = ""; | |||
if(!data[selectedDay]){ | |||
} | data[selectedDay] = []; | ||
} | |||
data[selectedDay].forEach((entry,index)=>{ | |||
const card = document.createElement("div"); | |||
card.className = "entry-card"; | |||
card.innerHTML = ` | |||
<label>Angebot</label> | |||
<input value="${entry.angebot || ''}" onchange="updateField(${index},'angebot',this.value)"> | |||
<label>Uhrzeit</label> | |||
<input value="${entry.uhrzeit || ''}" onchange="updateField(${index},'uhrzeit',this.value)"> | |||
<label>Treffpunkt/Ort</label> | |||
<input value="${entry.ort || ''}" onchange="updateField(${index},'ort',this.value)"> | |||
. | <label>Zielgruppe</label> | ||
<input value="${entry.zielgruppe || ''}" onchange="updateField(${index},'zielgruppe',this.value)"> | |||
. | <label>Anmeldung</label> | ||
<input value="${entry.anmeldung || ''}" onchange="updateField(${index},'anmeldung',this.value)"> | |||
} | |||
. | <label>Link zum Angebot</label> | ||
<input value="${entry.link || ''}" onchange="updateField(${index},'link',this.value)"> | |||
} | |||
< | <label>Beschreibung</label> | ||
<textarea rows="4" onchange="updateField(${index},'beschreibung',this.value)">${entry.beschreibung || ''}</textarea> | |||
<div class=" | <div class="actions"> | ||
<button onclick="copyEntryToDate(${index})"> | |||
Auf anderes Datum kopieren | |||
</button> | |||
<button onclick="deleteEntry(${index})"> | |||
Löschen | |||
</button> | |||
</div> | </div> | ||
`; | `; | ||
container.appendChild(card); | |||
const | }); | ||
} | |||
function copyEntryToDate(index){ | |||
const targetDay = prompt( | |||
"Auf welchen Tag im August kopieren? (1-31)" | |||
); | |||
if(!targetDay) return; | |||
const | const day = parseInt(targetDay); | ||
if(day < 1 || day > 31){ | |||
alert("Ungültiger Tag."); | |||
return; | |||
} | } | ||
if(!data[day]){ | |||
data[day] = []; | |||
} | |||
const | const copy = JSON.parse( | ||
JSON.stringify(data[selectedDay][index]) | |||
); | |||
data[day].push(copy); | |||
saveData(); | |||
alert( | |||
"Termin wurde auf den " | |||
+ day + | |||
". August kopiert." | |||
); | |||
} | |||
function addEntry(){ | |||
data[selectedDay].push({ | |||
angebot:"", | |||
uhrzeit:"", | |||
ort:"", | |||
zielgruppe:"", | |||
anmeldung:"", | |||
link:"", | |||
beschreibung:"" | |||
}); | }); | ||
saveData(); | |||
renderEntries(); | |||
} | |||
function updateField(index, field, value){ | |||
data[selectedDay][index][field] = value; | |||
saveData(); | |||
} | } | ||
function | function deleteEntry(index){ | ||
if(confirm("Eintrag löschen?")){ | |||
data[selectedDay].splice(index,1); | |||
saveData(); | |||
renderEntries(); | |||
} | |||
} | |||
function copyEntry(index){ | |||
const copy = JSON.parse( | |||
JSON.stringify(data[selectedDay][index]) | |||
); | |||
data[selectedDay].push(copy); | |||
saveData(); | |||
renderEntries(); | |||
} | |||
function exportData(){ | |||
const blob = new Blob( | |||
[JSON.stringify(data,null,2)], | |||
{type:"application/json"} | |||
); | |||
document. | const a = document.createElement("a"); | ||
a.href = URL.createObjectURL(blob); | |||
a.download = "familienangebote_august_2026.json"; | |||
a.click(); | |||
} | } | ||
function | function importData(event){ | ||
const file = event.target.files[0]; | |||
if(!file) return; | |||
const reader = new FileReader(); | |||
reader.onload = e => { | |||
data = JSON.parse(e.target.result); | |||
saveData(); | |||
alert("Import erfolgreich."); | |||
}; | |||
reader.readAsText(file); | |||
} | } | ||
renderCalendar(); | |||
</script> | </script> | ||
</body> | |||
</html> | |||
Version vom 10. Juni 2026, 09:37 Uhr