Skip to content

Commit 8386357

Browse files
committed
docs(dashboard): what the overview counts, how its breakdowns cut, and what a filter does
The dashboard page had one paragraph for the whole board and no account of what any number means. It now says how the page reads, what each headline metric counts and does not count, and what each breakdown is a cut of. The filters get their own part, because the new one is the only interaction on the board that is not self-evident: a row is the door, the marks on the bar are what you picked, and a filter selects sessions rather than pageviews. That last one is the sentence that saves the support thread — keeping the visits from Google shows every page they went on to read, not only the page they landed on. Pages are not a filter door, and the reason is written down rather than left as an absence. The 92-day limit is stated where somebody meets it, with the two exits the cards offer. SELF-HOSTING gains one line: 0.5.0 to 0.6.0 is `./upgrade.sh` and nothing else, because both migrations it carries only add columns.
1 parent 74a1b8e commit 8386357

3 files changed

Lines changed: 101 additions & 6 deletions

File tree

‎SELF-HOSTING.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,10 @@ standing on, takes a snapshot, points `.env` at the new images, pulls them and
661661
brings everything up. On an architecture with no published images,
662662
`./upgrade.sh --from-source` builds instead.
663663

664+
**0.5.0 → 0.6.0 asks nothing else of you.** Its two ClickHouse migrations
665+
only add columns, and the migrate container applies them while the upgrade
666+
runs.
667+
664668
**There are no down migrations, and that is a decision rather than an omission.**
665669
A reverse migration is code that runs once, in an emergency, having never been
666670
run before — and the alternative is honest: an upgrade takes a backup first, and

‎apps/web/app/docs/dashboard/page.tsx‎

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import {
22
Code,
33
DocArticle,
44
DocCode,
5+
DocHeading,
56
DocLink,
67
DocList,
78
DocNote,
89
DocSection,
10+
DocTable,
911
} from "@/components/docs/doc-prose";
1012
import { docPage, docsMetadata } from "@/lib/docs";
1113

@@ -15,16 +17,95 @@ export const metadata = docsMetadata(page);
1517
export default function DashboardDocsPage() {
1618
return (
1719
<DocArticle page={page}>
18-
<DocSection title="The layout">
20+
<DocSection title="How the overview reads">
1921
<p>
20-
A site&apos;s overview reads top to bottom: the headline totals
21-
(visitors, pageviews, bounce rate, average visit), the traffic
22-
chart, then the breakdowns: pages, sources, geography, devices and
23-
custom events. Every number answers the range picked in the
24-
header, and every breakdown is the same range cut a different way.
22+
A site&apos;s overview reads top to bottom. The headline totals come
23+
first (visitors, pageviews, bounce rate, average visit), then the
24+
traffic chart, then the breakdowns: pages, sources, geography,
25+
devices and custom events. Every number answers the range picked in
26+
the header, and every breakdown is that same range cut a different
27+
way, so nothing on the page is measuring a different window than
28+
anything else.
2529
</p>
2630
</DocSection>
2731

32+
<DocSection title="The headline numbers">
33+
<DocTable
34+
head={["Metric", "What it counts"]}
35+
rows={[
36+
[
37+
"Visitors",
38+
"Unique people in the range, counted with the daily-rotating identifier. Never summed from days, so a 30-day count is not the sum of 30 daily counts.",
39+
],
40+
["Pageviews", "Every page load and client-side route change."],
41+
[
42+
"Bounce rate",
43+
"The share of sessions with a single interaction, rebuilt server-side from the 30-minute session rule.",
44+
],
45+
[
46+
"Average visit",
47+
"Mean session duration, from engagement time rather than raw tab-open time.",
48+
],
49+
]}
50+
/>
51+
</DocSection>
52+
53+
<DocSection title="The breakdowns">
54+
<DocHeading>Pages</DocHeading>
55+
<p>
56+
Top paths by pageviews, with entries and exits, so you can see where
57+
people land and where they leave. The column you rank by is a
58+
question put to the server, not a re-sort of the rows on screen: a
59+
top-100-by-views page re-sorted by exits would present that
60+
page&apos;s biggest exits as the site&apos;s biggest.
61+
</p>
62+
<DocHeading>Sources</DocHeading>
63+
<p>
64+
Where visits come from, canonicalised: your own domain is dropped as
65+
a referrer, and hosts are folded to a channel name with a favicon, so
66+
Google&apos;s many subdomains read as one Google. Visits that arrive
67+
with no referrer at all read as Direct &mdash; tagging your links
68+
(just below) is how you give those a name. See{" "}
69+
<DocLink slug="troubleshooting">troubleshooting</DocLink> if a number
70+
looks lower than another tool&apos;s.
71+
</p>
72+
<DocHeading>Geography and devices</DocHeading>
73+
<p>
74+
Country-level geography from a privacy-preserving lookup, and device,
75+
browser and OS breakdowns, each over the same range.
76+
</p>
77+
<DocHeading>Custom events</DocHeading>
78+
<p>
79+
The events you defined, with counts, when they last fired and a
80+
sample of their properties. See{" "}
81+
<DocLink slug="custom-events">custom events</DocLink> to add them.
82+
</p>
83+
<DocHeading>Filtering by a row</DocHeading>
84+
<p>
85+
Clicking a row in Sources, Locations or the device cards keeps only
86+
the sessions that match it, and the rest of the overview re-reads
87+
with that filter applied. There is no separate filter picker: the row
88+
is the door, which is why its name underlines under the pointer. What
89+
you have picked appears as marks on the bar at the bottom of the
90+
screen &mdash; a favicon, a flag, a device glyph &mdash; and each
91+
mark clears on its own.
92+
</p>
93+
<DocList
94+
items={[
95+
"A filter selects sessions, and the report then describes everything those sessions did. Keeping the visits from Google shows every page they went on to read, not only the page they landed on.",
96+
"Two picks in one breakdown mean either (Google or GitHub); picks in two breakdowns mean both (Google and mobile).",
97+
"The four things you can filter by are all about the visit rather than the page: source, country, city and device. Pages are deliberately not one of them, because “filter by page” would have to choose between the sessions that touched a page and the pageviews of it, and those are different questions.",
98+
"Custom events and performance answer the range but not the filter, and leave the marks alone rather than pretending to narrow.",
99+
]}
100+
/>
101+
<DocNote>
102+
A filtered view is rebuilt from raw events rather than read off a
103+
rollup, so it covers at most 92 days where the unfiltered report
104+
answers a year. Past that the cards say so and offer the two ways
105+
out: shorten the range, or drop the filters and keep it.
106+
</DocNote>
107+
</DocSection>
108+
28109
<DocSection title="Tag your own links">
29110
<p>
30111
Browsers send less and less referrer information every year, and

‎apps/web/components/docs/doc-prose.tsx‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ export function DocSection({
5151
);
5252
}
5353

54+
/** A named cut inside a section, for a section that covers several of them
55+
* and would read as one wall of prose without the names. */
56+
export function DocHeading({ children }: { children: React.ReactNode }) {
57+
return (
58+
<h3 className="mt-2 text-[15px] font-medium tracking-tight text-foreground">
59+
{children}
60+
</h3>
61+
);
62+
}
63+
5464
export function DocList({ items }: { items: React.ReactNode[] }) {
5565
return (
5666
<ul className="flex list-disc flex-col gap-1.5 pl-5">

0 commit comments

Comments
 (0)