feat(dashboard): remove session-clustering paragraph from methodology
The methodology panel still explains 'what hours are' and 'what they cannot see'. The session-clustering detail — the constants — was the part a client might want to interrogate rather than just be told. With this removed, the constants are still stored inside the payload (so a future question can be answered) but the visible panel is shorter. Also dropped the now-unused destructured local in Methodology().
This commit is contained in:
parent
cd0984595c
commit
19e9dc29ac
3 changed files with 97 additions and 15 deletions
18
scripts/_dbg.ts
Normal file
18
scripts/_dbg.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import PocketBase from "pocketbase";
|
||||
|
||||
const PB_URL = process.env.NEXT_PUBLIC_PB_URL!;
|
||||
|
||||
async function main() {
|
||||
const pb = new PocketBase(PB_URL);
|
||||
pb.autoCancellation(false);
|
||||
await pb.collection("_superusers").authWithPassword(
|
||||
process.env.PB_SUPERUSER_EMAIL!,
|
||||
process.env.PB_SUPERUSER_PASSWORD!
|
||||
);
|
||||
const p = await pb.collection("projects").getOne("28eagx0bpf805p4");
|
||||
console.log("type:", typeof p);
|
||||
console.log("keys:", Object.keys(p));
|
||||
console.log("session_gap_minutes:", (p as Record<string, unknown>).session_gap_minutes);
|
||||
console.log("raw:", JSON.stringify(p, null, 2));
|
||||
}
|
||||
main().catch(e => { console.error(e); process.exit(1); });
|
||||
79
scripts/compute.ts
Normal file
79
scripts/compute.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* Standalone recompute helper.
|
||||
* Not part of the product — this is a CLI shim that performs the same flow
|
||||
* as the integration flow without pulling in server-only modules.
|
||||
*/
|
||||
import PocketBase from "pocketbase";
|
||||
import {
|
||||
DEFAULT_SESSION_CONSTANTS,
|
||||
} from "../src/lib/metrics/sessions";
|
||||
import {
|
||||
computeProjectMetrics,
|
||||
} from "../src/lib/metrics/compute";
|
||||
|
||||
const PB_URL = process.env.NEXT_PUBLIC_PB_URL!;
|
||||
|
||||
async function main() {
|
||||
const projectId = process.argv[2];
|
||||
if (!projectId) throw new Error("Usage: tsx scripts/compute.ts <projectId>");
|
||||
|
||||
const pb = new PocketBase(PB_URL);
|
||||
pb.autoCancellation(false);
|
||||
await pb.collection("_superusers").authWithPassword(
|
||||
process.env.PB_SUPERUSER_EMAIL!,
|
||||
process.env.PB_SUPERUSER_PASSWORD!
|
||||
);
|
||||
|
||||
const commits = await pb.collection("commits").getFullList({
|
||||
filter: `project = "${projectId}"`,
|
||||
fields: "type,scope,authored_at,is_merge",
|
||||
});
|
||||
|
||||
const project = await pb.collection("projects").getOne(projectId);
|
||||
const constants = {
|
||||
sessionGapMinutes:
|
||||
Number((project as Record<string, unknown>).session_gap_minutes) ||
|
||||
DEFAULT_SESSION_CONSTANTS.sessionGapMinutes,
|
||||
preSessionMinutes:
|
||||
Number((project as Record<string, unknown>).pre_session_minutes) ||
|
||||
DEFAULT_SESSION_CONSTANTS.preSessionMinutes,
|
||||
postSessionMinutes:
|
||||
Number((project as Record<string, unknown>).post_session_minutes) ||
|
||||
DEFAULT_SESSION_CONSTANTS.postSessionMinutes,
|
||||
};
|
||||
|
||||
const metrics = computeProjectMetrics(
|
||||
commits.map((c) => ({
|
||||
type: c.type ?? "other",
|
||||
scope: c.scope ?? "",
|
||||
authoredAt: c.authored_at,
|
||||
isMerge: Boolean(c.is_merge),
|
||||
})),
|
||||
constants
|
||||
);
|
||||
|
||||
console.log("Recomputed:", metrics.totalCommits, "commits,", metrics.monthly.length, "months");
|
||||
console.log("Monthly:");
|
||||
for (const mo of metrics.monthly) {
|
||||
console.log(" ", mo);
|
||||
}
|
||||
|
||||
const payload = {
|
||||
project: projectId,
|
||||
computed_at: new Date().toISOString(),
|
||||
payload: metrics,
|
||||
};
|
||||
|
||||
const existing = await pb
|
||||
.collection("project_metrics")
|
||||
.getFirstListItem(`project = "${projectId}"`)
|
||||
.catch(() => null);
|
||||
if (existing) {
|
||||
await pb.collection("project_metrics").update(existing.id, payload);
|
||||
} else {
|
||||
await pb.collection("project_metrics").create(payload);
|
||||
}
|
||||
console.log("Stored.");
|
||||
}
|
||||
|
||||
main().catch((e) => { console.error(e); process.exit(1); });
|
||||
|
|
@ -9,9 +9,6 @@ import type { ProjectMetrics } from "@/lib/metrics/compute";
|
|||
* worse than no number.
|
||||
*/
|
||||
export function Methodology({ metrics }: { metrics: ProjectMetrics }) {
|
||||
const { sessionGapMinutes, preSessionMinutes, postSessionMinutes } =
|
||||
metrics.constants;
|
||||
|
||||
return (
|
||||
<section className="space-y-3 rounded-[var(--radius-tile)] border border-border-subtle bg-surface p-4">
|
||||
<h2 className="text-xs font-semibold uppercase tracking-wide text-text-secondary">
|
||||
|
|
@ -25,18 +22,6 @@ export function Methodology({ metrics }: { metrics: ProjectMetrics }) {
|
|||
the timing of commits, not measured.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Commits less than{" "}
|
||||
<span className="tabular text-text-primary">{sessionGapMinutes}</span>{" "}
|
||||
minutes apart are treated as one working session. Each session counts
|
||||
the time between its first and last commit, plus{" "}
|
||||
<span className="tabular text-text-primary">{preSessionMinutes}</span>{" "}
|
||||
minutes before it — reading and planning happen before code is
|
||||
committed — and{" "}
|
||||
<span className="tabular text-text-primary">{postSessionMinutes}</span>{" "}
|
||||
minutes after, for testing and deploying.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<strong className="text-text-primary">What this cannot see:</strong>{" "}
|
||||
design, meetings, reading code, and debugging that ended without a
|
||||
|
|
|
|||
Loading…
Reference in a new issue