Commit Graph

19 Commits

Author SHA1 Message Date
Peter Meier af90483dc4 feat(newsletter): newsletter signup as internal component
Deploy / verify (push) Successful in 1m18s
Deploy / deploy (push) Successful in 1m34s
Wire a newsletter signup form into the CMS internal-component pattern
(form-newsletter). Posts to /api/newsletter which subscribes to listmonk
(double opt-in) and mirrors a best-effort backup record to the RustyCMS
forms plugin. Reuses the contact-form anti-spam stack: Cloudflare
Turnstile, honeypots, client-age check, origin validation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 11:02:48 +02:00
Peter Meier b29dd587b8 feat: Mitmachen-Formular nativ (löst OpnForm ab)
Deploy / verify (push) Successful in 1m8s
Deploy / deploy (push) Successful in 1m17s
- MitmachenFormComponent: Name der BI*, Anliegen*, Ansprechpartner,
  Telefon, E-Mail, Datei-Upload (max 5×5MB, JPG/PNG/WebP/GIF/PDF),
  Consent — Honeypots + Client-Age + Turnstile wie Kontaktformular
- /api/mitmachen: Multipart-Validierung (shared Modul), Forwarding an
  CMS /api/forms/mitmachen/submit-multipart
- internal_component: form-mitmachen registriert
- ~35 neue mitmachen_*-Translation-Keys

Prod braucht BODY_SIZE_LIMIT=30M (adapter-node default 512K) — via
secrets .env.prod deployt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 11:59:31 +02:00
Peter Meier 41597f87b5 feat(windkarte): InfoAccordion, anlagen-Hinweis, Tooltip ohne Icons
Deploy / verify (push) Successful in 1m6s
Deploy / deploy (push) Successful in 1m18s
- Disclaimer aus CMS-Feld (hinweis_text) via InfoAccordion statt Hardcode
- info_text-Feld für Block-Fließtext ergänzt
- anlagen_geplant_hinweis-Feld in WindArea + Panel-Anzeige
- Tooltip-Icons entfernt (square-outline/wind-turbine/map-marker inline SVGs)
- Panel: square-outline → ruler-square für Fläche
- Iconify-Subset neu generiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 12:35:29 +02:00
Peter Meier d03afe780e feat(contact): add Cloudflare Turnstile captcha
Deploy / verify (push) Failing after 44s
Deploy / deploy (push) Has been skipped
Client renders explicit Turnstile widget (flexible, light theme) when
PUBLIC_TURNSTILE_SITE_KEY is set. Token sent with form payload, server
verifies via siteverify endpoint before forwarding to CMS. Skipped
entirely when no site key configured (dev fallback). New i18n keys:
contact_captcha_pending, contact_server_error_captcha. Deploy workflow
forwards PUBLIC_TURNSTILE_SITE_KEY + TURNSTILE_SECRET.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 11:37:09 +02:00
Peter Meier 94a8feeb67 feat(contact): full validation, CMS-translated strings, smaller UI
Deploy / verify (push) Failing after 47s
Deploy / deploy (push) Has been skipped
- contact-form-validation.ts: shared client+server validator returning
  {key, params} so messages can be translated via the CMS bundle.
- ContactFormComponent: 3 honeypots, subject field, GDPR consent
  checkbox, character counter, inline per-field errors after submit,
  uses useTranslate() everywhere.
- /api/contact: origin allowlist, multiple honeypot check, client-age
  gate, server-side re-validation, forwards X-Forwarded-For + UA to
  CMS forms plugin with consent timestamp in payload.
- TextInput/Textarea: new size="sm" variant (rounded-sm, shadow-sm,
  h-10, smaller padding/text). Default "md" unchanged.
- translations.ts: 37 new contact_* keys.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 11:28:23 +02:00
Peter Meier 8e43169b23 feat(windkarte): Stellungnahme-Kriterien per Windvorranggebiet
Deploy / verify (push) Successful in 56s
Deploy / deploy (push) Successful in 1m38s
Jedes Windvorranggebiet kann jetzt 1–n text_fragment-Einträge als
Stellungnahme-Kriterien referenzieren. Das Panel lädt diese lazy beim
Öffnen und zeigt sie als aufklappbare Accordion-Sektion.

Außerdem: alle hardcodierten Strings im Karten-Block (Panel, Map,
Block) durch t()-Aufrufe ersetzt; 7 neue Translation-Keys ergänzt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 14:22:35 +02:00
Peter Meier b4581e4d4f feat(windkarte): tap-to-activate overlay for mobile scroll fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 15:31:23 +02:00
Peter Meier 7d4f52ba02 feat(windkarte): neuer Status entwurf_2_voraussichtlich (orange)
Deploy / verify (push) Successful in 56s
Deploy / deploy (push) Successful in 1m3s
Farbe #d4752a. Legende, Typ-Union, Übersetzungen ergänzt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 14:17:15 +02:00
Peter Meier ee56a457d3 feat(windkarte): alle UI-Strings auf t(T.windkarte_*) umgestellt
Deploy / verify (push) Successful in 51s
Deploy / deploy (push) Successful in 59s
Keine hardcodierten Texte mehr — alle Strings über CMS translation_bundle
(app.json5). Keys in translations.ts registriert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 11:09:09 +02:00
Peter Meier 6fb2592eae feat(calendar): add feed subscription, bulk export, share & Google Cal
Deploy / verify (push) Successful in 1m0s
Deploy / deploy (push) Successful in 1m3s
- /api/kalender.ics SSR route: ICS feed of all calendar items (webcal-subscribable, Cache 1h)
- buildICSFeed() / downloadICSFeed() for multi-event ICS export
- googleCalUrl() deep-link pre-fills Google Calendar create form
- Web Share API per event (navigator.share on mobile, clipboard fallback on desktop)
  — "Kopiert\!" toast per event for 2s after clipboard write
- Actions bar (subscribe + bulk export) only rendered after onMount
  — Apple platforms: webcal:// link opens Calendar.app natively
  — All others: direct ICS download via <a download>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 16:50:56 +02:00
Peter Meier 8db68e073c refactor(calendar): stack layout, wider grid, fixed cell height
Deploy / verify (push) Successful in 53s
Deploy / deploy (push) Successful in 57s
- Calendar widget stays stacked (calendar above events list)
- Grid widens to max-w-2xl with fixed h-10 cells (no aspect-square height growth)
- Gap between cells: 0.5 → 1.5
- Button system extracted to shared btn-* utility classes in app.css
- Replaced Google Calendar link + Google Maps with OSM
- Removed google icon from iconify subset

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 09:56:40 +02:00
Peter Meier 4790d571fd feat(strommix): hero variant, 24h sparkline, trend, polishing
- New "hero" variant between compact and full: pct + status +
  sparkline + trend, no sub-cards or accordion
- 24h Wind+Solar sparkline below hero pct, accent-colored
- Trend arrow vs previous hour with delta percentage points
- Polling pauses on tab hidden, refetches on visibility return
  if last fetch >5min old
- Skeleton loader replaces "Lade…" text (matches final layout)
- Retry button on error state (silent vs explicit fetch)
- Stale-flag now prominent red badge in header bar
- Stand-time wrapped in <time datetime title> for full-date hover
- loadMw=0 falls back to last hourly slot instead of red error
- Negative-price banner replaces standalone price card (dedup)
- Production breakdown grid: clean auto/1fr label-value alignment
- Print CSS: details auto-open, chevrons hidden
- All German strings moved to translation keys
- STROMMIX_DEFAULT_THRESHOLDS const as single source for buckets

Includes data-block="LiveStrommix" attribute for the new
identifier scheme + new RenewableSparkline.svelte component.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 15:44:57 +02:00
Peter Meier 3ded972642 feat(strommix): variant system + wald palette + UX polish
Deploy / verify (push) Successful in 52s
Deploy / deploy (push) Successful in 52s
Add full/compact variants, prominent share, source badge, big
negative-price banner with conditional disclaimer, cards-style
context row, fade-in animation, and remove em-dashes from
visible strings.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 00:43:18 +02:00
Peter Meier 9ebca1c237 fix(strommix): full i18n + drop hardcoded grid constraints
Deploy / verify (push) Successful in 48s
Deploy / deploy (push) Successful in 52s
Two issues from the audit:

1. Hardcoded `lg:col-span-8 lg:col-start-3` collided with the layout
   coming from `getBlockLayoutClasses(block.layout)`. Tailwind picked
   `lg:col-start-3` from the hardcoded class (no opposing
   `lg:col-start-1` in the layout output) so the widget started at
   column 3 with span 12 → overflowed past the 12-col grid on
   desktop, looking like it was "hanging right". Drop the hardcoded
   constraints; the layout config from the CMS controls width
   (mobile/tablet/desktop = 12 by default = full width).
2. Roughly 25 German strings were inline. Extracted them to
   translation keys (T.strommix_*, registered in translations.ts) and
   read via `useTranslate()` like ImageGalleryBlock /
   PostOverviewBlock / YoutubeVideoBlock. Includes field labels for
   the breakdown disclosure, source attribution variants, status
   bucket markers, error / loading / fallback messages, and the
   conventional formula text shown under the "Konventionell jetzt"
   number.

Default DE strings live in cms_content/windwiderstand/translation_bundle/
app.json5 (separate CMS push).
2026-05-05 00:02:27 +02:00
Peter Meier 6b2b7245f4 refactor: fold comment count into PostActions toolbar
CommentCountBadge was a separate component rendered next to PostActions
— two adjacent strips of buttons that should have been one toolbar.
Merges the count fetch + click-to-scroll behaviour into PostActions
behind `commentPageId` / `commentTargetId` props. The standalone
CommentCountBadge.svelte is now redundant; deleted.

Layout under the post hero is now a single row:
[💬 3]  [↗ Teilen]  [📋 Link]  [🖨️]  [⏱ 4 min]

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 14:52:30 +02:00
Peter Meier 3318fe9e17 feat(post): post actions toolbar (share / copy / print / reading time)
Adds a small toolbar next to the comment-count badge with four actions:

- Share — uses `navigator.share()` on supporting devices, falls back
  to copy-link otherwise
- Copy link — clipboard API with `<input>` + `execCommand` fallback,
  flashes a checkmark for 1.5s on success
- Print — `window.print()`, hidden via `print:hidden` so it doesn't
  show up in the printed output itself
- Reading time — server-computed from headline+body HTML at ~200 wpm,
  hidden when 0

Reading time + canonical URL come from `+page.server.ts` so SSR
output is identical and shareable URLs render correctly.

All labels go through the translations layer (5 new
`post_action_*` keys).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 14:42:16 +02:00
Peter Meier e4804ce673 i18n(comments): all UI strings through translations
Pulls every German string out of Comments.svelte, PostCard.svelte and
CommentCountBadge.svelte and routes it through `useTranslate()` +
the `T.*` key registry. New keys (`comments_*`, `post_overview` count
aria, `comments_jump_to`) are registered in `lib/translations.ts`.

Default DE values land in
`cms_content/windwiderstand/de/translation_bundle/app.json5` (committed
separately) so the CMS-loaded bundle keeps the same look without each
component falling back to its raw key.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 14:34:48 +02:00
Peter Meier 998fba8904 feat(gallery): grid title, subtle download icon, full i18n
Deploy / verify (push) Successful in 38s
Deploy / deploy (push) Successful in 59s
- Render block.title as <h3> above the grid when set; the carousel
  variant is unchanged.
- Download CTA in the modal shrunk to a 9×9 icon-only button with
  text-white/80 hover-tint instead of the dark green link colour the
  app's `main a` rule was forcing in. Adds `no-underline` so the
  global underline doesn't sneak back in.
- All previously hardcoded German strings (Download, Schließen,
  modal aria, open aria) now go through translations.ts → CMS
  translation_bundle. Cms_content app.json5 ships the matching keys.

Open follow-up: per-image title/alt/caption is empty on the existing
windwiderstand mediathek gallery → editor needs to fill those if
hover labels are wanted.
2026-04-25 10:44:30 +02:00
Peter Meier 2fef91a548 Initial SvelteKit frontend port of windwiderstand.de
Deploy / verify (push) Failing after 32s
Deploy / deploy (push) Has been skipped
Full parity with Astro site: content rows, post/tag routes, pagination,
event badges + OSM map, comments, Live-Search via /api/search-index,
CMS image proxy, RSS, sitemap.

Deploy: Dockerfile + docker-compose.prod.yml + Gitea Actions workflow
to build + push to git.pm86.de registry and ssh-deploy to Contabo.
2026-04-17 22:01:30 +02:00