Compare commits

..

2 Commits

Author SHA1 Message Date
karim c2dd9d3ffb ui: Journal-Karten kompakt (kein Höhen-Stretch) + Wortmark „openbureau" in Honk
Journal-Startseite:
- Karten-Kontext (.journal-list) nimmt die alten Vollflächen-Panel-Regeln
  zurück: kompakte 2-Spalten-Karten statt min-height:70vh + Hero-Padding.
- Cover-Bild als 16/9-Block OBEN statt absolutem Overlay; Text darunter,
  linksbündig, dunkle Schrift (kein Weiß-auf-Bild mehr).
- align-items:start → keine Zeilen-Stretchung; ein höherer Eintrag zieht den
  Nachbarn nicht mehr mit.

Header:
- Wortmark ist jetzt sichtbarer Kleintext „openbureau" in Honk
  (expressive COLRv1-Color-Font), self-gehostet als static/fonts/honk-latin.woff2
  + @font-face. Ersetzt das bisherige logo.svg-Hintergrundbild.

Beides nur per API/Build geprüft, nicht visuell (kein Headless-Browser).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 20:03:20 +02:00
karim 70a6798404 cms: Schema-Migration bei jedem up nachziehen (forums/threads in Bestands-DB)
Problem: Init-Scripts (docker-entrypoint-initdb.d) laufen nur beim allerersten
DB-Start. Neue Tabellen aus schema.sql (forums/threads) landeten daher nicht in
einer bereits initialisierten Produktiv-DB → "relation public.forums does not
exist" auf der Dialog-Seite.

- docker-compose.yml: neuer einmaliger `migrate`-Service spielt das idempotente
  schema.sql als supabase_admin (Superuser, keine Owner-Konflikte) bei jedem
  `up` ein und lädt den PostgREST-Cache neu; cms wartet via
  service_completed_successfully darauf.
- routes/dialog.js: fehlende Tabelle führt nicht mehr zu rohem SQL-Fehler —
  leere Liste + server-seitiges Log statt 500 auf der Seite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 18:50:07 +02:00
5 changed files with 151 additions and 22 deletions
+108 -15
View File
@@ -153,30 +153,35 @@ a:hover {
} }
/* Logo as background image (paths-only SVG, no font dependency) */ /* Logo as background image (paths-only SVG, no font dependency) */
/* Wordmark-Schrift: Honk (expressive Color-Font, COLRv1), self-gehostet.
Bei fehlender Color-Font-Unterstützung greift die Fallback-Farbe unten. */
@font-face {
font-family: 'Honk';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("/fonts/honk-latin.woff2") format("woff2");
}
.wordmark-link { .wordmark-link {
border: none; border: none;
margin: 0; margin: 0;
padding: 0; padding: 0;
display: block; display: block;
justify-self: center; justify-self: center;
width: clamp(140px, 18vw, 200px); width: auto;
height: clamp(22px, 2.8vw, 32px); height: auto;
background-image: url("/logo/logo.svg"); font-family: 'Honk', var(--font-family-display), system-ui, sans-serif;
background-repeat: no-repeat; font-weight: 400;
background-position: center; font-size: clamp(2rem, 5vw, 3.1rem);
background-size: contain;
color: transparent;
font-size: 0;
line-height: 1; line-height: 1;
letter-spacing: 0.01em;
text-transform: lowercase;
color: #fff; /* Fallback, falls Color-Font nicht unterstützt wird */
} }
.wordmark-sr { .wordmark-text { display: inline-block; }
position: absolute;
width: 1px; height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
}
.wordmark-link:hover, .wordmark-link:hover,
.wordmark-link:focus { color: #fff; border: none; opacity: 0.85; } .wordmark-link:focus { border: none; opacity: 0.85; }
.site-header .site-nav { .site-header .site-nav {
justify-self: center; justify-self: center;
@@ -778,6 +783,94 @@ a.byline-author:hover, a.journal-author:hover { color: var(--accent); }
.journal-tags { /* extends .tag-pills */ } .journal-tags { /* extends .tag-pills */ }
/* ════════════════════════════════════════════════════════════════════════
Journal-Startseite: KARTEN-Kontext. Nimmt die Vollflächen-Panel-Regeln von
oben innerhalb von .journal-list zurück — kompakte 2-Spalten-Karten:
Bild als 16/9-Block OBEN (kein absolutes Overlay), dunkle Schrift,
natürliche Höhe (align-items:start → keine Zeilen-Stretchung; ein hoher
Eintrag zieht den Nachbarn NICHT mit).
════════════════════════════════════════════════════════════════════════ */
.journal-list { align-items: start; }
.journal-list .journal-entry,
.journal-list .journal-entry--layout-image,
.journal-list .journal-entry--layout-icon,
.journal-list .journal-entry--layout-text {
margin: 0;
padding: 1.25rem;
position: relative;
overflow: hidden;
border-radius: 12px;
align-self: start;
}
.journal-list .journal-entry--layout-image {
background: var(--color-bg-secondary);
color: var(--color-text-primary);
}
.journal-list .journal-entry-link { display: block; min-height: 0; }
/* Bild: normaler Block oben statt absolutem Hintergrund-Overlay */
.journal-list .journal-bg-image {
position: static;
inset: auto;
z-index: auto;
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: 8px;
margin: 0 0 0.9rem;
}
/* Body: linksbündig, normaler Textfluss, kompakt, dunkle Schrift */
.journal-list .journal-entry-body {
position: static;
z-index: auto;
max-width: none;
margin: 0;
padding: 0;
display: grid;
justify-items: start;
text-align: left;
row-gap: 0.55rem;
color: var(--color-text-primary);
}
.journal-list .journal-entry-body > * { max-width: none; }
/* Bild-Layout: Schrift wieder dunkel (kein Weiß-auf-Bild) */
.journal-list .journal-entry--layout-image .journal-title,
.journal-list .journal-entry--layout-image .journal-rubric,
.journal-list .journal-entry--layout-image .journal-summary,
.journal-list .journal-entry--layout-image .journal-byline,
.journal-list .journal-entry--layout-image .journal-byline .journal-author,
.journal-list .journal-entry--layout-image .journal-byline .journal-date,
.journal-list .journal-entry--layout-image .journal-byline .journal-author::before {
color: var(--color-text-primary);
text-shadow: none;
}
.journal-list .journal-entry--layout-image .journal-section {
background: color-mix(in oklab, var(--section-color, var(--accent)) 35%, transparent);
color: var(--color-text-primary);
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
/* Kompaktere Karten-Typografie (nicht Hero-Größe) */
.journal-list .journal-title { font-size: 1.4rem; line-height: 1.2; font-weight: 700; letter-spacing: -0.02em; }
.journal-list .journal-summary { font-size: 1rem; line-height: 1.45; max-width: none; }
.journal-list .journal-byline { font-size: 0.9rem; }
/* Icon-Bild kleiner und linksbündig */
.journal-list .journal-icon-image { max-width: 96px; max-height: 96px; margin: 0 0 0.6rem; }
/* Tags innerhalb der Karte links, kompakt */
.journal-list .journal-entry > .tag-pills {
margin: 0.6rem 0 0;
padding: 0;
max-width: none;
text-align: left;
}
.more { .more {
margin-top: var(--spacing-md); margin-top: var(--spacing-md);
font-family: var(--font-family-mono); font-family: var(--font-family-mono);
+15 -5
View File
@@ -5,18 +5,28 @@ import {
forumsWithCounts, forumWithThreads, recentComments, createThread, recentForModeration, threadMeta, forumsWithCounts, forumWithThreads, recentComments, createThread, recentForModeration, threadMeta,
} from '../dialog-store.js'; } from '../dialog-store.js';
// Fehlt die Tabelle (Migration noch nicht eingespielt), nicht mit einem rohen
// SQL-Fehler antworten — leer zurückgeben und server-seitig laut loggen.
function softFail(c, e, fallback) {
console.error('[dialog]', e?.message || e);
return c.json(fallback);
}
// ── Öffentliche Lese-Handler ───────────────────────────────────────────── // ── Öffentliche Lese-Handler ─────────────────────────────────────────────
export async function listForums(c) { export async function listForums(c) {
try { return c.json(await forumsWithCounts()); } try { return c.json(await forumsWithCounts()); }
catch (e) { return c.json({ error: String(e) }, 500); } catch (e) { return softFail(c, e, []); }
} }
export async function showForum(c) { export async function showForum(c) {
const data = await forumWithThreads(c.req.param('slug')); try {
if (!data) return c.json({ error: 'Forum nicht gefunden' }, 404); const data = await forumWithThreads(c.req.param('slug'));
return c.json(data); if (!data) return c.json({ error: 'Forum nicht gefunden' }, 404);
return c.json(data);
} catch (e) { return softFail(c, e, { forum: null, threads: [] }); }
} }
export async function recent(c) { export async function recent(c) {
return c.json(await recentComments(Number(c.req.query('limit')) || 20)); try { return c.json(await recentComments(Number(c.req.query('limit')) || 20)); }
catch (e) { return softFail(c, e, []); }
} }
export async function threadInfo(c) { export async function threadInfo(c) {
const key = c.req.query('key'); const key = c.req.query('key');
+26
View File
@@ -41,6 +41,30 @@ services:
retries: 20 retries: 20
start_period: 30s start_period: 30s
# ════════════════════════════════════════════════════════════════════════
# Migrate — spielt das (idempotente) Schema bei jedem `up` nach, damit neue
# Tabellen/Spalten auch in eine BESTEHENDE DB kommen (Init-Scripts laufen nur
# beim allerersten Start). Läuft einmal und beendet sich. supabase_admin =
# Superuser → keine Owner-Konflikte. schema.sql ist idempotent.
# ════════════════════════════════════════════════════════════════════════
migrate:
image: supabase/postgres:15.8.1.020
container_name: openbureau-migrate
restart: "no"
depends_on:
db:
condition: service_healthy
environment:
PGPASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./db/schema.sql:/openbureau-schema.sql:ro
entrypoint: ["bash", "-c"]
command:
- >
psql -h db -U supabase_admin -d postgres -v ON_ERROR_STOP=1 -f /openbureau-schema.sql &&
psql -h db -U supabase_admin -d postgres -c "notify pgrst, 'reload schema';" &&
echo '✓ Schema migriert.'
# ════════════════════════════════════════════════════════════════════════ # ════════════════════════════════════════════════════════════════════════
# GoTrue — Auth (Login für das CMS) # GoTrue — Auth (Login für das CMS)
# ════════════════════════════════════════════════════════════════════════ # ════════════════════════════════════════════════════════════════════════
@@ -124,6 +148,8 @@ services:
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
migrate:
condition: service_completed_successfully
kong: kong:
condition: service_started condition: service_started
environment: environment:
+2 -2
View File
@@ -9,8 +9,8 @@
<body> <body>
<a href="#main-content" class="skip-link">Skip to content</a> <a href="#main-content" class="skip-link">Skip to content</a>
<header role="banner" class="site-header"> <header role="banner" class="site-header">
<a href="{{ "/" | relURL }}" class="wordmark-link" aria-label="OPENBUREAU"> <a href="{{ "/" | relURL }}" class="wordmark-link" aria-label="openbureau">
<span class="wordmark-sr">OPENBUREAU</span> <span class="wordmark-text">openbureau</span>
</a> </a>
<nav class="site-nav" aria-label="Site"> <nav class="site-nav" aria-label="Site">
{{ partial "menu.html" (dict "menuID" "main" "page" .) }} {{ partial "menu.html" (dict "menuID" "main" "page" .) }}
Binary file not shown.