docs: update README, add session handoff, fix deploy-script executable bit

README: document today's shipped work (story reactions/comments, per-pet
insights dashboard, DESIGN.md/PRODUCT.md design system, admin panel visual
pass) in "What's Shipped" and the admin panel section.

docker/rolling-deploy.sh and start.sh were tracked as 100644 (no executable
bit) since this repo is worked from Windows, which never records it — every
fresh git pull onto the Linux NAS made them non-executable again. Fixed via
git update-index --chmod=+x so this doesn't recur.
This commit is contained in:
admin committed 2026-08-25 19:24:52 +02:00
1 parent 641a58a1b5
commit 1b83bb9665
4 files changed
+102

No files matched your search

@@ -0,0 +1,97 @@
# Session Handoff: Design System Bootstrap, Landing/Legal Rebuild, Admin Panel Visual Pass
**Date:** 2026-08-25
**Project:** T:\CC-Projekte\OnlyPets (PawFeed)
**Session Duration:** Continuation of the 2026-08-24 session (see that day's handoff for #29/#30 story-reactions/insights work) — this document covers everything after that point.
## Current State
**Task:** Bootstrap a design system via the Impeccable skill, rebuild the public landing page + header + legal pages against it, then audit and fix the admin panel (deferred to Phase 1+2 of a 4-phase plan), then three small admin follow-ups (dashboard color-coding, clickable attention stats, a button legend on the Posts page).
**Phase:** Complete for everything scoped this session.
**Progress:** 100% of what was asked — all changes committed, pushed to Gitea, and deployed live via `rolling-deploy.sh`. Phase 3/4 of the admin-panel plan (shared `AdminCard`/`AdminStat` primitives applied to the other 11 admin pages) remain explicitly deferred, not started.
## What We Did
1. **`/impeccable init`** → `PRODUCT.md` (platform, users, positioning, brand commitments, launch date 2026-09-01) built from a structured interview, not invented.
2. **`/impeccable document`** → `DESIGN.md` + `.impeccable/design.json` sidecar, capturing PawFeed's existing brand as **"The Spotlight Porch"**: one marigold-orange accent used sparingly, ring-defined surfaces (no shadows at rest), soft consistent rounding, Inter doing all typographic work.
3. **Landing page rebuild** (`src/app/page.tsx`) against `DESIGN.md` and `craft-floor.md`'s ban list: removed the full-bleed gradient hero and the uniform 4-card feature grid, replaced with a restrained hero + one dominant feature with a large decorative `PawPrint` icon + a quiet 3-item list. Fixed a real bug along the way: the old decorative paw motif was hand-drawn SVG circles whose toe-beans clipped past the viewBox — replaced with the actual `PawPrint` icon matching the header logo exactly (user had flagged both the clipping and the mismatch).
4. **Header critique-then-fix cycle**: user asked directly whether the navbar "still looks like slop" — gave an honest critique (glass-blur + shadowed pill CTA contradicting the just-documented Ring-Not-Shadow rule, inconsistent orange shade), fixed all three, verified on a real emulated mobile viewport. Then added two small "delight" details on request (paw-spring hover on the logo, a warm gradient hairline under the header) and, in a follow-up, an active-page indicator (small dot under the current nav link) via a new `LandingNavLinks.tsx` client component.
5. **Legal pages redesign** (`LegalLayout`/`LegalSection` + all 3 pages): user called the numbered-card layout "massiv nach Slop" — rewrote to a flowing typographic document (plain `<h1>`/`<h2>`, `divide-y` instead of individual cards, section numbers used only as anchor ids, never displayed). Reordered the Sponsoring section in `nutzungsbedingungen` to sit right after "Nutzerinhalte & Urheberrecht" instead of last, using a sub-numbered id (`3a`) so the existing "gemäß Abschnitt 5" cross-reference elsewhere in the same document stayed correct — same pattern already used in `datenschutz`'s pre-existing `4a` section.
6. **Deployed** the above to the NAS (see Deploy Notes below — this is where the session's most important operational discovery happened).
7. **Admin panel audit** (forked subagent, 13 files) found: hardcoded `zinc-*` classes instead of semantic tokens, a redundant "PawFeed Admin" kicker label, a 12-card stat wall on the dashboard with no hierarchy, and no current-page indicator in the sidebar. Proposed a 4-phase plan; user approved **Phase 1+2 only** plus an extra explicit requirement (active-page sidebar highlighting).
8. **Phase 1 (admin shell)**: `layout.tsx` forces `dark` via a literal class (deliberate — admin stays permanently dark regardless of the site-wide toggle) instead of hardcoded `bg-zinc-950`; removed the kicker label; new `AdminSidebarNav.tsx` highlights the current page (desktop); `AdminMobileNav.tsx` got the same active-state logic plus a dynamic topbar label instead of a static title; `GranularityToggle.tsx`/`TimeSeriesChart.tsx` token cleanup.
9. **Phase 2 (dashboard declutter)**: replaced the 12-stat-card wall with 2 `AttentionStat` tiles (reports, pending verifications) + a flat `Overview` list, converted the 5 chart containers to the shared `Card` component.
10. **Follow-up 1**: user asked to color-code the Overview stats by category. Split into three labeled `StatGroup`s — **Activity** (emerald: owners/pets/posts/DAU/MAU), **Violations** (rose: banned/shadowbanned/blacklist — only the label is always colored, individual values only pick up rose when actually `> 0`), **Info** (sky: active ads/verifications closed). Also switched every remaining `border` to `ring-1` across the dashboard (maintenance banner, AttentionStat) so the whole page shares the `Card` component's depth convention instead of two different systems.
11. **Follow-up 2**: user noticed "Open reports"/"Verifications pending" tiles weren't links — made `AttentionStat` a real `<Link>` to `${pathname}/reports` / `${pathname}/verification` with hover/focus states and a chevron affordance.
12. **Follow-up 3**: user asked for a button legend on the Posts admin page (comments/hide/pin/AI-disclosure/delete icon row had no explanation). Added a `HelpCircle` trigger next to the "Posts" heading opening a `Popover` listing icon + action name.
13. **Fixed a real deploy-blocking bug**: `docker/rolling-deploy.sh` and `docker/start.sh` were tracked in git as `100644` (no executable bit) — since this repo is worked from Windows, the bit was never recorded, so every fresh `git pull` onto the Linux NAS makes them non-executable again (`Keine Berechtigung` on `./rolling-deploy.sh`). Fixed with `git update-index --chmod=+x` on both files so the executable bit is now actually tracked — this should not recur.
14. Updated `README.md` ("What's Shipped" + admin panel section) and wrote this handoff.
## Decisions Made
- **Multiple hues for the admin dashboard's Overview categories, despite the app-wide "one accent color" rule in `DESIGN.md`** — deliberate exception. `operate.md` (Impeccable's Operate-mode guidance) explicitly names "a dashboard where one category color carries a report" as the sanctioned case for earning "Committed" color in an otherwise-Restrained product surface, and the user gave this as a direct, explicit brief ("nach Aktivität, Verletzungen, Infos"). The brief wins over the general rule per Impeccable's own stated precedence.
- **Rose values only colored when `> 0`, never as a flat wash** — same "act on this" convention already established for `AttentionStat`, kept consistent rather than inventing a second color rule.
- **Ring (`ring-1 ring-foreground/10`) instead of `border` everywhere on the dashboard** — matches the shared `Card` component's own depth convention (`ring-1 ring-foreground/10`, no border), so the whole page now uses one visual vocabulary instead of the dashboard's ad-hoc borders next to the charts' Card-based rings.
- **`AttentionStat` links via `usePathname()` + relative suffix, not a hardcoded secret prop** — the dashboard page IS the `base` route (`/p/[secret]`), so `usePathname()` already returns exactly `base`; no need to thread the secret through as a prop.
- **Posts-page legend is a `Popover`, not a modal or always-visible caption row** — `operate.md` explicitly flags "modal as first thought" as a product-UI failure mode; a popover is the progressive-disclosure alternative that doesn't interrupt the grid.
- **Executable bit fixed via `git update-index --chmod=+x`, not by re-running `chmod` on the server every deploy** — the server-side `chmod +x` from earlier in the session was a one-time workaround; fixing it in git is the actual root-cause fix so it doesn't recur on the next fresh clone or CI-driven deploy.
## Code Changes
**Design system + landing/legal (commit `2cb54d4`):**
- `PRODUCT.md`, `DESIGN.md`, `.impeccable/design.json` (new)
- `src/app/page.tsx`, `src/app/globals.css` — hero/features rebuild, `.porch-glow`/`.landing-logo` hover/`.landing-header-hairline` additions
- `src/components/landing/LandingHeader.tsx`, `src/components/landing/LandingNavLinks.tsx` (new)
- `src/components/legal/LegalLayout.tsx`, `src/app/impressum/page.tsx`, `src/app/datenschutz/page.tsx`, `src/app/nutzungsbedingungen/page.tsx`
**Admin panel Phase 1+2 (commit `f12f4ed`):**
- `src/app/p/[secret]/layout.tsx`, `src/components/admin/AdminMobileNav.tsx`, `src/components/admin/AdminSidebarNav.tsx` (new), `src/components/admin/GranularityToggle.tsx`, `src/components/admin/TimeSeriesChart.tsx`, `src/app/p/[secret]/page.tsx`
**Dashboard color-coding (commit `2f7cacf`):**
- `src/app/p/[secret]/page.tsx` — `StatGroup` component, `STAT_GROUP_COLORS` map, ring-based depth unification
**Clickable attention stats (commit `06a25b4`):**
- `src/app/p/[secret]/page.tsx` — `AttentionStat` now a `Link`, `usePathname()` for the base route
**Posts page legend (commit `641a58a`):**
- `src/app/p/[secret]/posts/page.tsx` — `POST_ACTION_LEGEND`, `Popover` next to the "Posts" heading
**Deploy-script fix (uncommitted as of writing this handoff, staged next):**
- `docker/rolling-deploy.sh`, `docker/start.sh` — executable bit now tracked in git (`100755`)
**Docs (uncommitted as of writing this handoff, staged next):**
- `README.md` — "What's Shipped" + admin panel section updated
## Open Questions
None blocking — everything explicitly asked for this session shipped and was confirmed live by the user ("perfekt", "sieht gut aus").
## Blockers / Issues
- **Clerk redirects `localhost:3000` sessions to the production domain** — discovered this session while trying to verify the admin panel changes live before committing. Hitting `localhost:3000/p/[secret]` (or any authenticated route) in a real logged-in Chrome profile bounces through `accounts.pawfeed.org` and lands on `pawfeed.org` instead of staying on localhost — repro'd twice, in both a fresh tab and an already-authenticated one. Almost certainly because Clerk is configured with **live** keys (`pk_live_`/`sk_live_`, a deliberate prior decision, see `project_deployment.md` memory) and treats `localhost` as a foreign origin it can't safely return to. **Consequence:** live browser verification of authenticated (owner-app or admin) routes against the dev server is currently not reliably possible via browser automation — user explicitly chose "commit without visual verification, I'll check after deploy" as the workaround this session. Not something to fix casually (would mean either a Clerk dev instance/test keys for local work, or a satellite-domain config) — flag to the user if this keeps blocking future sessions.
- **`git`/SSH commands run directly by Claude via the Bash tool against the NAS are blocked by the local auto-mode classifier** — confirmed again this session (same finding as 2026-08-24's handoff, now confirmed for plain `git pull`/`ssh` too, not just other git subcommands). **What actually works:** give the user the exact `ssh daniel@192.168.1.222 "..."` command and have them run it themselves via the `!` prefix — its output lands in the conversation like any tool result. This is now the established, working deploy loop (see Context to Remember) and is simpler than the previous session's `cat|ssh` per-file upload workaround, now that the executable-bit issue is fixed and the server's git repo is caught up.
- **The NAS's `/Dockers/PawFeed` git repo was ~9 commits behind `origin/main`** (stuck at `1293f7c`) **with a large amount of uncommitted local drift** (~120 files) before this session's first deploy — almost certainly because prior sessions used FTP or manual file copies without ever committing on the server side. Resolved via `git stash -u` (preserves everything, discards nothing) then `git pull` (clean fast-forward to `f12f4ed`). **The stash is still sitting on the server, unreviewed** (`git stash list` would show it) — nobody has actually diffed what was in it (e.g., whether `docker-compose.yml` had any manual NAS-specific tweaks that never made it into git). Low risk since the site has been healthy through 5 subsequent deploys, but worth a `git stash show -p` sometime to confirm nothing load-bearing was silently dropped, then `git stash drop`.
- **Pre-existing `react-hooks/set-state-in-effect` lint debt, not touched:** now also confirmed present in `src/app/p/[secret]/posts/page.tsx:51` (the grid-size-from-localStorage effect) — same established, un-fixed pattern as `StoryViewer.tsx`/`notifications/page.tsx` noted in the prior handoff. Left as-is again, consistent with that precedent.
- **`glitchtip-issue.txt`** still sits untracked in the repo root — still deliberately excluded from every commit, still not something to clean up unprompted.
- **Admin panel Phase 3/4 not started** — shared `AdminCard`/`AdminStat`/`AdminRow` primitives, and applying the shell/dashboard fix pattern to the other 11 admin pages (`users`, `posts` beyond today's legend, `reports`, `moderators`, `ads`, `invites`, `broadcasts`, `blacklist`, `legal`, `log`, `messages`, `verification`) — explicitly deferred by the user, needs a fresh go-ahead before starting.
## Context to Remember
- **Current working deploy loop** (supersedes the 2026-08-24 handoff's `cat|ssh` per-file method now that the server's git repo is caught up and the executable-bit issue is fixed): local `tsc --noEmit` + `eslint` clean → `git add`/`commit`/`git push origin main` (Gitea, works fine directly from Claude's Bash) → give the user this exact command to run themselves via the `!` prefix: `ssh daniel@192.168.1.222 "cd /Dockers/PawFeed && git pull && cd docker && ./rolling-deploy.sh"` → its stdout/stderr comes back in the conversation, confirm "Rolling deploy complete" and no errors.
- **Why the user has to run the SSH command, not Claude**: any `ssh daniel@192.168.1.222 "..."` call attempted directly via the Bash tool gets denied by the local Claude Code auto-mode classifier ("Blocked by classifier"), regardless of what the remote command actually does. This is a standing constraint for this project/session type, not a one-off fluke — don't spend a turn re-trying it directly.
- **`/impeccable` skill state**: `PRODUCT.md`/`DESIGN.md`/`.impeccable/design.json` now exist and are the source of truth for any future visual work on this codebase — check them before freehanding new UI, and load `craft-floor.md`/`operate.md` per the skill's own routing before editing (Persuade mode for anything public-facing, Operate mode for the admin panel).
- **`ADMIN_SECRET`** (needed for any future `/p/[secret]` work) lives in `.env.local`, currently `1fa187a1661f4e1fb6c3524b67790aeb`.
## Next Steps
1. [ ] Commit + push + deploy the two currently-uncommitted changes (`docker/rolling-deploy.sh`/`start.sh` executable-bit fix, `README.md` update, this handoff file) — planned as the very next action after this handoff is written.
2. [ ] Optionally, sometime: `ssh daniel@192.168.1.222 "cd /Dockers/PawFeed && git stash show -p"` to confirm nothing load-bearing was in the stashed server drift, then `git stash drop`.
3. [ ] If the user wants to continue the admin-panel work: Phase 3 (extract `AdminCard`/`AdminStat`/`AdminRow`) then Phase 4 (apply to the remaining 11 pages) are ready to resume, not yet started.
4. [ ] If local browser verification of authenticated routes becomes a recurring need, raise the Clerk live-keys-on-localhost redirect issue with the user as its own topic — today's workaround (skip visual verification, check after deploy) is fine for small changes but won't scale to bigger ones.
## Files to Review on Resume
- `DESIGN.md` / `PRODUCT.md` — the design system, read before any new UI work.
- `src/app/p/[secret]/page.tsx` — dashboard, most heavily iterated file this session (StatGroup, AttentionStat, category colors).
- `src/components/admin/AdminSidebarNav.tsx` — new, reusable active-page-highlight pattern if it needs extending to other nav-like components.
- `docker/rolling-deploy.sh` — now executable in git; if a *new* shell script is ever added to `docker/`, remember to `git update-index --chmod=+x` it too since Windows checkouts never set the bit automatically.
+5
View File
@@ -347,6 +347,8 @@ A daily cron (`/api/cron/trim-feeds`) trims each pet's Redis feed sorted set to
Gated by path secret (`ADMIN_SECRET`, see `proxy.ts`) **and** by role at the tRPC layer: `admin.ts`'s `assertAdmin()` allows either `ADMIN_OWNER_ID` (env-bootstrapped super-admin) or an `AdminRole` row (`SUPER_ADMIN` / `MODERATOR`). Every action is written to `ModerationLog`. Covers users, moderators, posts, reports, ads, invites, verification requests, blacklist entries, and system-wide broadcast announcements (`createBroadcast`/`deleteBroadcast`, both `SUPER_ADMIN`-gated, deletion requires a reason for the audit trail).
The panel is permanently dark (forced via a literal `dark` class on the shell, independent of the site-wide theme toggle) with its own active-page nav highlighting (`AdminSidebarNav`, mobile drawer mirrors it). The dashboard groups its overview stats by category — Activity (emerald), Violations (rose, only lights up when actually > 0), Info (sky) — instead of one flat wall of identical cards; only Phase 1+2 of the visual pass are done (shell tokens + dashboard), Phase 3/4 (shared `AdminCard`/`AdminStat` primitives applied to the remaining 11 pages) are deferred.
### Invite system
`InviteCode` (word-prefix + 4-char code, e.g. `PAWS-7K3M`) gates sign-up via a `pf_invite` cookie set by `/join` and consumed by `/api/auth/consume-invite` right after sign-up. `INVITE_REQUIRED=false` turns the whole gate off (the current production setting, since 2026-08-10).
@@ -440,6 +442,9 @@ Foundation → content core → engagement → notifications → video → messa
- **Ads** — sponsored posts with species/breed targeting, ad-specific reactions/comments/reposts
- **Sponsoring** — voluntary Stripe payments (one-time/monthly, LIVE mode) grant a Sponsor badge shown on the pet profile and every post
- **Broadcasts** — admin-authored system-wide announcements with an owner-facing banner, per-owner delivered/read receipts, and audited creation/deletion
- **Story engagement** — paw reactions and comments on Stories (`StoryReaction`/`StoryComment`, mirroring the Post engagement model), comments render as a translucent overlay on the *running* story, not a pause-and-read sheet
- **Per-pet Insights dashboard** — owner-facing analytics at `/pets/[petId]/insights` (total reactions/comments, top post/story by each metric), spotlight cards open the real post/story
- **Design system** — `PRODUCT.md`/`DESIGN.md` ("The Spotlight Porch": one marigold-orange accent, ring-defined surfaces, no drop shadows at rest) captured from the existing brand and used to rebuild the landing page, header, and all 3 legal pages
- **Security hardening** — Redis-backed rate limiting, enforced CSP (nonce + `strict-dynamic`, Mozilla Observatory A+), HSTS/security headers, constant-time secret comparison, self-hosted error monitoring
- **Self-hosted deployment** — Docker on an OpenMediaVault NAS behind Nginx Proxy Manager + Cloudflare, 3 load-balanced replicas, zero-downtime rolling deploys
Regular → Executable
View File
File mode changed.
Regular → Executable
View File
File mode changed.