PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.5
4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 All 203 releases
betterdocs / react-src / admin / mcp / utils / format.js

format.js in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.5, at react-src/admin/mcp/utils/format.js

92 lines 3.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { __, _n, sprintf } from "@wordpress/i18n";
2
3 /**
4 * Date helpers shared by the KPI tiles and the connected-apps table.
5 *
6 * Every timestamp this page receives is a Unix time in seconds (the pairing's
7 * `connected_at` / `last_used`, each app's `created` / `last_used`), but the
8 * REST layer is free to hand back a date string, so both shapes are accepted
9 * and anything unparseable is treated as "no value" rather than printed as
10 * `Invalid Date`.
11 *
12 * @since 4.9.0
13 */
14
15 /**
16 * A timestamp in milliseconds, or `null` when there is no usable value.
17 *
18 * @param {number|string|null} value Unix seconds, a parseable date string, or nothing.
19 * @return {number|null} Milliseconds since the epoch, or `null`.
20 */
21 export function toMillis(value) {
22 if (!value) {
23 return null;
24 }
25 const ts = typeof value === "number" ? value * 1000 : Date.parse(value);
26 return Number.isNaN(ts) ? null : ts;
27 }
28
29 /**
30 * A short absolute date in the reader's own locale — "Aug 20, 2026".
31 *
32 * The year is kept: a pairing made last year is a materially different fact
33 * from one made this month, and the tile has room for four more characters.
34 *
35 * @param {number|string|null} value Unix seconds or a parseable date string.
36 * @return {string} The formatted date, or "Never".
37 */
38 export function formatDate(value) {
39 const ts = toMillis(value);
40 if (ts === null) {
41 return __("Never", "betterdocs");
42 }
43 return new Date(ts).toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric" });
44 }
45
46 /**
47 * How long ago something happened, in words.
48 *
49 * Coarse on purpose: the server records one `last_used` stamp per credential
50 * and no history at all, so "3 hours ago" is the whole of what is known. Past a
51 * week the absolute date says more than a growing day count, so it takes over.
52 *
53 * @param {number|string|null} value Unix seconds or a parseable date string.
54 * @param {number} now Milliseconds to measure from; defaults to the current time.
55 * @return {string} A relative description, or "Never".
56 */
57 export function relativeTime(value, now = Date.now()) {
58 const ts = toMillis(value);
59 if (ts === null) {
60 return __("Never", "betterdocs");
61 }
62
63 const seconds = Math.round((now - ts) / 1000);
64
65 // A clock skew between the server and the browser can put a stamp a few
66 // seconds into the future; "in 4 seconds" would be nonsense, "Just now" is
67 // both true and useful.
68 if (seconds < 60) {
69 return __("Just now", "betterdocs");
70 }
71
72 const minutes = Math.floor(seconds / 60);
73 if (minutes < 60) {
74 /* translators: %d: number of minutes. */
75 return sprintf(_n("%d minute ago", "%d minutes ago", minutes, "betterdocs"), minutes);
76 }
77
78 const hours = Math.floor(minutes / 60);
79 if (hours < 24) {
80 /* translators: %d: number of hours. */
81 return sprintf(_n("%d hour ago", "%d hours ago", hours, "betterdocs"), hours);
82 }
83
84 const days = Math.floor(hours / 24);
85 if (days < 7) {
86 /* translators: %d: number of days. */
87 return sprintf(_n("%d day ago", "%d days ago", days, "betterdocs"), days);
88 }
89
90 return formatDate(value);
91 }
92