Skip to content

feat: a post is read on a page of its own, and every post in one list (ADR 0070) - #62

Merged
jagdish-kumawat merged 2 commits into
mainfrom
feat/2026-11-07-post-pages
Nov 8, 2026
Merged

jagdish-kumawat merged 2 commits into
mainfrom
feat/2026-11-07-post-pages

Conversation

@jagdish-kumawat

Copy link
Copy Markdown
Contributor

What this changes

A post's own page (/app/insights/post?path=) — opened from any post title on Insights:

  • Views since they were first counted (exactly Insights' figure), average per day, best month.
  • A chart of its views by hour, day, week, month or year over the chosen period (no Compare); All time is drawn from the record's kept months without a request.
  • Every month and year by total views or by average per day (days measured since publication); a month never counted for the post says so rather than showing nought.
  • The last 13 weeks, Monday to Sunday, with each week's average and its change on the week before.

All posts (/app/insights/posts) — reached from Insights' "See all N posts":

  • Every listed post with its all-time views, Most read (Insights' tie rule) or Newest, 25 / 50 / 100 a page.
  • Search by title or path, case-insensitive in PostgreSQL; a pasted whole http(s) address is compared by its path.
  • A post opened from the list leads back to the same page, search and order.

Live — the headline card shows the page views of the same half hour beside its visitors.

Engine

  • GET /api/sites/{id}/insights/post, GET /api/sites/{id}/insights/post/series, GET /api/sites/{id}/posts.
  • Paths asked of ClickHouse are the control-plane row's (own + same-site landed path, ≤ 2 bound values), never the request's. Unknown website, forbidden website and unlisted path answer one identical 404 without touching the telemetry store; the path is never logged or echoed.
  • Search text is bound and escaped (\ % _) for ILIKE … ESCAPE '\'; SQL-looking text finds nothing.
  • Website series goldens and the counting-rule hashes are unchanged.

ADR 0070 records the decision, what it does not do (no visitors per post, no best day, case folding limited by the database locale) and the cost per page load.

Shared pieces that change for other screens

40 px phone controls in ListSwitch / PopulationSwitch / Pagination, phone paging that keeps a short list whole, a stretched row link in ranked lists, a lone figure in a long line chart drawn as a dot, and focus never left under the sticky header.

Checks run locally

  • Free edition: Testing 11, Domain 256, Application 629, Classification 530, Architecture 32, Api 66, Sql 329, Mail 30, Integration 2,642 — all green, 0 build warnings.
  • Cloud edition against this branch: both solutions build; Ee.Integration 206 (22 skipped), Ee 198 green.
  • pnpm verify: web 2,594, tracker 84, edition 124, site 78.
  • next build free and DEWIRIDE_EDITION=cloud both list /app/insights/post, /app/insights/posts and /app/live.
  • Every new state photographed in light and dark at 390 px (touch) and 1280 px.

No migration, no retention or privacy change, no ee/ change.

🤖 Generated with Claude Code

jagdish-kumawat and others added 2 commits November 8, 2026 12:31
… (ADR 0070)

A post on Insights opens a page of its own: its views since they were first counted, on average per
day and in its best month, a chart of its views by the hour, day, week, month or year, every month
and year by total views or by average per day, and its last 13 weeks day by day. All posts lists
every post the sitemap names with how often each was read, searchable, the most read or the newest
first.

- GET /api/sites/{id}/insights/post?path= answers one listed post's record; its headline is
  Insights' own figure (kept months before the first month answered from reports, the reports from
  there on). A month never kept for the post, or kept before the post was listed, has no figure
  rather than nought.
- GET /api/sites/{id}/insights/post/series draws the post's chart, answering the website series'
  SeriesResponse.
- The paths asked about are the control-plane row's, never the request's: the post's own and the
  path a same-site redirect landed on, bound as at most two values and compared after
  reconciliation. An unknown website, a forbidden one and an unlisted path answer the same 404
  without asking the telemetry store.
- GET /api/sites/{id}/posts lists every listed post, Most read (Insights' tie rule) or Newest,
  25, 50 or 100 a page. A search matches title or path case-insensitively in PostgreSQL (ILIKE,
  with \ % _ escaped and the text bound); a whole http(s) address is compared by the path it
  names. No posts or no match is answered without the telemetry store.
- Dashboard: /app/insights/post and /app/insights/posts. Insights' post titles open the post's
  page and an arrow opens it on the website; a post opened from the list leads back to the same
  page, search and order of it. The Overview's chart pieces move unchanged into
  components/charts/cut-picture.tsx and lib/analytics/use-picture-plan.ts.
- Shared controls change on every screen that uses them: 40 px phone controls in ListSwitch,
  PopulationSwitch and Pagination, phone paging that keeps a short list whole, a stretched link on
  a ranked row, a lone figure in a long line drawn as a dot, and focus never left under the
  sticky header.

No migration, and no retention or privacy change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Live headline card shows the page views of the same half hour on its right: the sum of every
minute's page views in the answer the screen already reads, the minute running now among them. It
is not announced as the visitor count is. No engine change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jagdish-kumawat
jagdish-kumawat merged commit 35021a2 into main Nov 8, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant