{"id":118876,"date":"2025-07-20T13:45:16","date_gmt":"2025-07-20T11:45:16","guid":{"rendered":"https:\/\/www.jung-pfalz-huette.de\/wp\/?page_id=118876"},"modified":"2025-07-20T13:47:35","modified_gmt":"2025-07-20T11:47:35","slug":"dienstplan-2026","status":"publish","type":"page","link":"https:\/\/www.jung-pfalz-huette.de\/wp\/hutten-infos\/dienstplan-2026\/","title":{"rendered":"H\u00fcttendienstplan 2026"},"content":{"rendered":"<h2>Unser H\u00fcttendienst \u2013 Mit Herz und Engagement f\u00fcr Euch da<\/h2>\n<p>Unsere H\u00fctte wird vollst\u00e4ndig von engagierten Ehrenamtlichen betrieben, die ihre Freizeit opfern, um Euch ein sch\u00f6nes Wochenende in der Pf\u00e4lzer Natur zu erm\u00f6glichen. Diese Teams arbeiten mit viel Herzblut und freiwilligem Einsatz, um den H\u00fcttenbetrieb am Laufen zu halten. Ohne sie w\u00e4re dieser besondere Ort nicht das, was er ist.<\/p>\n<p>Wir bitten um Euer Verst\u00e4ndnis und Eure Wertsch\u00e4tzung f\u00fcr die Arbeit unserer Helfer, die ihren Einsatz ohne finanziellen Gewinn und aus purer Leidenschaft f\u00fcr die Pf\u00e4lzer H\u00fcttenkultur leisten. Unterst\u00fctzt sie gerne durch freundliche Worte und R\u00fccksichtnahme w\u00e4hrend Eures Aufenthaltes.<\/p>\n<p>&nbsp;<\/p>\n<div id=\"jph-dienstplan-2026\" style=\"margin: 10px 0;\">\r\n  <div style=\"padding:10px;border:1px solid #ccc;background:#f7f7f7;\">\r\n    Dienstplan wird geladen \u2026\r\n  <\/div>\r\n<\/div><script>\r\n(() => {\r\n  const year = 2026;\r\n  const apiUrl = \"https:\\\/\\\/api.jungpfalz.de\\\/dienstplan\\\/2026\";\r\n  const el = document.getElementById(\"jph-dienstplan-2026\");\r\n\r\n  const MONTHS_DE = [\r\n    \"\", \"Januar\",\"Februar\",\"M\u00e4rz\",\"April\",\"Mai\",\"Juni\",\"Juli\",\"August\",\"September\",\"Oktober\",\"November\",\"Dezember\"\r\n  ];\r\n\r\n  function escapeText(text) {\r\n    \/\/ Wir setzen sp\u00e4ter sowieso textContent, das ist schon sicher. Trotzdem null-safe:\r\n    return (text === null || text === undefined) ? \"\" : String(text);\r\n  }\r\n\r\n  function fmtDay(dateStr) {\r\n    const d = new Date(dateStr + \"T00:00:00\");\r\n    const dd = String(d.getDate()).padStart(2, \"0\");\r\n    return dd + \".\";\r\n  }\r\n\r\n  function monthNumber(dateStr) {\r\n    const d = new Date(dateStr + \"T00:00:00\");\r\n    return d.getMonth() + 1;\r\n  }\r\n\r\n  function sameKey(a, b) {\r\n    return (escapeText(a.team) === escapeText(b.team)) &&\r\n           (escapeText(a.label) === escapeText(b.label)) &&\r\n           (a.isOpen === b.isOpen);\r\n  }\r\n\r\n  function isNextDay(prevDateStr, currDateStr) {\r\n    const prev = new Date(prevDateStr + \"T00:00:00\").getTime();\r\n    const curr = new Date(currDateStr + \"T00:00:00\").getTime();\r\n    return (curr - prev) === 86400000; \/\/ 1 Tag\r\n  }\r\n\r\n  function groupConsecutive(days) {\r\n    \/\/ days: sortiert nach date\r\n    const groups = [];\r\n    let current = null;\r\n\r\n    for (const entry of days) {\r\n      if (!entry.date) continue;\r\n\r\n      if (current && sameKey(current.entry, entry)) {\r\n        const prevDate = current.dates[current.dates.length - 1];\r\n        if (isNextDay(prevDate, entry.date)) {\r\n          current.dates.push(entry.date);\r\n        } else {\r\n          groups.push(current);\r\n          current = { entry, dates: [entry.date] };\r\n        }\r\n      } else {\r\n        if (current) groups.push(current);\r\n        current = { entry, dates: [entry.date] };\r\n      }\r\n    }\r\n    if (current) groups.push(current);\r\n    return groups;\r\n  }\r\n\r\n  function render(data) {\r\n    if (!data || !Array.isArray(data.days)) {\r\n      el.innerHTML = \"<div style='padding:10px;border:1px solid #ccc;background:#fff3f3;color:#b71c1c;'>Keine Daten gefunden.<\/div>\";\r\n      return;\r\n    }\r\n\r\n    \/\/ Nach Monaten gruppieren\r\n    const months = {};\r\n    for (const day of data.days) {\r\n      if (!day.date) continue;\r\n      const m = monthNumber(day.date);\r\n      (months[m] ||= []).push(day);\r\n    }\r\n\r\n    \/\/ Container leeren\r\n    el.innerHTML = \"\";\r\n\r\n    \/\/ Monate sortieren\r\n    const monthKeys = Object.keys(months).map(Number).sort((a,b)=>a-b);\r\n\r\n    for (const m of monthKeys) {\r\n      const days = months[m].slice().sort((a,b) => (a.date||\"\").localeCompare(b.date||\"\"));\r\n\r\n      const details = document.createElement(\"details\");\r\n      details.style.marginBottom = \"40px\";\r\n\r\n      const summary = document.createElement(\"summary\");\r\n      const strong = document.createElement(\"strong\");\r\n      strong.textContent = MONTHS_DE[m] || (\"Monat \" + m);\r\n      summary.appendChild(strong);\r\n      details.appendChild(summary);\r\n\r\n      const table = document.createElement(\"table\");\r\n      table.style.width = \"100%\";\r\n      table.style.borderCollapse = \"collapse\";\r\n      table.style.tableLayout = \"fixed\";\r\n      table.style.marginTop = \"10px\";\r\n\r\n      const thead = document.createElement(\"thead\");\r\n      thead.style.backgroundColor = \"#2f613f\";\r\n      thead.style.color = \"#fff\";\r\n\r\n      const trh = document.createElement(\"tr\");\r\n      const th1 = document.createElement(\"th\");\r\n      th1.textContent = \"Datum\";\r\n      th1.style.width = \"20%\";\r\n      th1.style.padding = \"8px\";\r\n      th1.style.borderBottom = \"1px solid #ccc\";\r\n      const th2 = document.createElement(\"th\");\r\n      th2.textContent = \"Team\";\r\n      th2.style.width = \"40%\";\r\n      th2.style.padding = \"8px\";\r\n      th2.style.borderBottom = \"1px solid #ccc\";\r\n      const th3 = document.createElement(\"th\");\r\n      th3.textContent = \"Hinweise\";\r\n      th3.style.width = \"40%\";\r\n      th3.style.padding = \"8px\";\r\n      th3.style.borderBottom = \"1px solid #ccc\";\r\n\r\n      trh.append(th1, th2, th3);\r\n      thead.appendChild(trh);\r\n      table.appendChild(thead);\r\n\r\n      const tbody = document.createElement(\"tbody\");\r\n\r\n      const groups = groupConsecutive(days);\r\n\r\n      for (const g of groups) {\r\n        const entry = g.entry;\r\n\r\n        const dates = g.dates.map(fmtDay);\r\n        const datumText = (dates.length > 1) ? dates.join(\" + \") : (dates[0] || \"\");\r\n\r\n        let team = entry.team;\r\n        let label = entry.label;\r\n\r\n        let rowStyle = \"\";\r\n        if (entry.isOpen === false) {\r\n          team = \"H\u00fctte geschlossen\";\r\n          rowStyle = \"color:#b71c1c;\";\r\n        } else if (entry.isOpen === true && (!team || String(team).trim() === \"\")) {\r\n          team = \"Noch kein Team\";\r\n          rowStyle = \"color:#b71c1c;\";\r\n        }\r\n\r\n        const tr = document.createElement(\"tr\");\r\n        if (rowStyle) tr.setAttribute(\"style\", rowStyle);\r\n\r\n        const td1 = document.createElement(\"td\");\r\n        td1.style.width = \"20%\";\r\n        td1.style.padding = \"8px\";\r\n        td1.textContent = datumText;\r\n\r\n        const td2 = document.createElement(\"td\");\r\n        td2.style.width = \"40%\";\r\n        td2.style.padding = \"8px\";\r\n        td2.textContent = escapeText(team);\r\n\r\n        const td3 = document.createElement(\"td\");\r\n        td3.style.width = \"40%\";\r\n        td3.style.padding = \"8px\";\r\n        td3.textContent = escapeText(label);\r\n\r\n        tr.append(td1, td2, td3);\r\n        tbody.appendChild(tr);\r\n      }\r\n\r\n      table.appendChild(tbody);\r\n      details.appendChild(table);\r\n      el.appendChild(details);\r\n    }\r\n  }\r\n\r\n  \/\/ Optional: Browser-Cache (localStorage) als Fallback\r\n  const cacheKey = \"jph_dienstplan_cache_\" + year;\r\n  const cacheMaxAgeMs = 6 * 60 * 60 * 1000; \/\/ 6h\r\n\r\n  function loadFromCache() {\r\n    try {\r\n      const raw = localStorage.getItem(cacheKey);\r\n      if (!raw) return null;\r\n      const obj = JSON.parse(raw);\r\n      if (!obj || !obj.savedAt || !obj.data) return null;\r\n      if ((Date.now() - obj.savedAt) > cacheMaxAgeMs) return null;\r\n      return obj.data;\r\n    } catch { return null; }\r\n  }\r\n\r\n  function saveToCache(data) {\r\n    try {\r\n      localStorage.setItem(cacheKey, JSON.stringify({ savedAt: Date.now(), data }));\r\n    } catch {}\r\n  }\r\n\r\n  \/\/ Erst Cache zeigen (wenn da), dann live versuchen\r\n  const cached = loadFromCache();\r\n  if (cached) {\r\n    render(cached);\r\n  }\r\n\r\n  fetch(apiUrl, { method: \"GET\", headers: { \"Accept\": \"application\/json\" }})\r\n    .then(r => {\r\n      if (!r.ok) throw new Error(\"HTTP \" + r.status);\r\n      return r.json();\r\n    })\r\n    .then(data => {\r\n      saveToCache(data);\r\n      render(data);\r\n    })\r\n    .catch(err => {\r\n      if (!cached) {\r\n        el.innerHTML = \"<div style='padding:10px;border:1px solid #ccc;background:#fff3f3;color:#b71c1c;'>\" +\r\n          \"<strong>Dienstplan derzeit nicht verf\u00fcgbar.<\/strong><br>\" +\r\n          \"Technischer Hinweis (Browser): \" + String(err) +\r\n          \"<\/div>\";\r\n      }\r\n      \/\/ Wenn Cache da war, lassen wir den stehen (besser als Fehlerbox).\r\n      console.warn(\"Dienstplan Fetch failed:\", err);\r\n    });\r\n\r\n})();\r\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Unser H\u00fcttendienst \u2013 Mit Herz und Engagement f\u00fcr Euch da Unsere H\u00fctte wird vollst\u00e4ndig von engagierten Ehrenamtlichen betrieben, die ihre Freizeit opfern, um Euch ein sch\u00f6nes Wochenende in der Pf\u00e4lzer Natur zu erm\u00f6glichen. Diese Teams arbeiten mit viel Herzblut und&#8230; <a class=\"more-link\" href=\"https:\/\/www.jung-pfalz-huette.de\/wp\/hutten-infos\/dienstplan-2026\/\">Weiterlesen &rarr;<\/a><\/p>\n","protected":false},"author":14,"featured_media":0,"parent":11,"menu_order":7,"comment_status":"closed","ping_status":"closed","template":"","meta":{"ngg_post_thumbnail":0,"footnotes":""},"class_list":["post-118876","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.jung-pfalz-huette.de\/wp\/wp-json\/wp\/v2\/pages\/118876","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.jung-pfalz-huette.de\/wp\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.jung-pfalz-huette.de\/wp\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.jung-pfalz-huette.de\/wp\/wp-json\/wp\/v2\/users\/14"}],"replies":[{"embeddable":true,"href":"https:\/\/www.jung-pfalz-huette.de\/wp\/wp-json\/wp\/v2\/comments?post=118876"}],"version-history":[{"count":2,"href":"https:\/\/www.jung-pfalz-huette.de\/wp\/wp-json\/wp\/v2\/pages\/118876\/revisions"}],"predecessor-version":[{"id":118879,"href":"https:\/\/www.jung-pfalz-huette.de\/wp\/wp-json\/wp\/v2\/pages\/118876\/revisions\/118879"}],"up":[{"embeddable":true,"href":"https:\/\/www.jung-pfalz-huette.de\/wp\/wp-json\/wp\/v2\/pages\/11"}],"wp:attachment":[{"href":"https:\/\/www.jung-pfalz-huette.de\/wp\/wp-json\/wp\/v2\/media?parent=118876"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}