@locusgraph/blocks 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,26 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  import { formatCents } from "@locusgraph/utils";
3
- const intervalLabel = { month: "/month", year: "/year" };
4
- export function PlanPrice({ plan }) {
5
- if (plan.amountCents === undefined) {
6
- return (_jsx("span", { className: "font-heading text-xl font-semibold tracking-[-0.02em] tabular-nums", children: plan.price }));
7
- }
8
- return (_jsxs("span", { className: "flex items-baseline gap-1", children: [_jsx("span", { className: "font-heading text-2xl font-semibold tracking-[-0.02em] tabular-nums", children: formatCents(plan.amountCents) }), plan.interval && (_jsx("span", { className: "text-sm text-muted-foreground", children: intervalLabel[plan.interval] }))] }));
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ const intervalLabel = {
4
+ month: "/month",
5
+ year: "/year"
6
+ };
7
+ export function PlanPrice({
8
+ plan
9
+ }) {
10
+ if (plan.amountCents === undefined) {
11
+ return /*#__PURE__*/_jsx("span", {
12
+ className: "font-heading text-xl font-semibold tracking-[-0.02em] tabular-nums",
13
+ children: plan.price
14
+ });
15
+ }
16
+ return /*#__PURE__*/_jsxs("span", {
17
+ className: "flex items-baseline gap-1",
18
+ children: [/*#__PURE__*/_jsx("span", {
19
+ className: "font-heading text-2xl font-semibold tracking-[-0.02em] tabular-nums",
20
+ children: formatCents(plan.amountCents)
21
+ }), plan.interval && /*#__PURE__*/_jsx("span", {
22
+ className: "text-sm text-muted-foreground",
23
+ children: intervalLabel[plan.interval]
24
+ })]
25
+ });
9
26
  }
@@ -1,9 +1,41 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Badge, RuleGrid, RuleGridCell, RuleGridFooter, RuleGridTitle, } from "@locusgraph/components";
1
+ import { Badge, RuleGrid, RuleGridCell, RuleGridFooter, RuleGridTitle } from "@locusgraph/components";
3
2
  import { PlanPrice } from "./plan-price.js";
4
- export function Plans({ plans, actionFor }) {
5
- return (_jsx(RuleGrid, { columns: plans.length >= 3 ? 3 : 2, children: plans.map((plan) => {
6
- const action = plan.current ? null : actionFor?.(plan);
7
- return (_jsxs(RuleGridCell, { "data-current": plan.current || undefined, className: "gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs(RuleGridTitle, { className: "flex min-h-6 items-center gap-2", children: [plan.name, plan.current && _jsx(Badge, { variant: "secondary", children: "Current" })] }), _jsx(PlanPrice, { plan: plan })] }), _jsx("ul", { className: "flex flex-col gap-1.5 text-sm text-muted-foreground", children: plan.points.map((point) => (_jsx("li", { children: point }, point))) }), _jsx(RuleGridFooter, { className: "mt-auto", children: plan.current ? (_jsx("p", { className: "flex min-h-8 items-center text-sm text-muted-foreground", children: "Your plan" })) : (action) })] }, plan.name));
8
- }) }));
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ export function Plans({
5
+ plans,
6
+ actionFor
7
+ }) {
8
+ return /*#__PURE__*/_jsx(RuleGrid, {
9
+ columns: plans.length >= 3 ? 3 : 2,
10
+ children: plans.map(plan => {
11
+ const action = plan.current ? null : actionFor?.(plan);
12
+ return /*#__PURE__*/_jsxs(RuleGridCell, {
13
+ "data-current": plan.current || undefined,
14
+ className: "gap-4",
15
+ children: [/*#__PURE__*/_jsxs("div", {
16
+ className: "flex flex-col gap-2",
17
+ children: [/*#__PURE__*/_jsxs(RuleGridTitle, {
18
+ className: "flex min-h-6 items-center gap-2",
19
+ children: [plan.name, plan.current && /*#__PURE__*/_jsx(Badge, {
20
+ variant: "secondary",
21
+ children: "Current"
22
+ })]
23
+ }), /*#__PURE__*/_jsx(PlanPrice, {
24
+ plan: plan
25
+ })]
26
+ }), /*#__PURE__*/_jsx("ul", {
27
+ className: "flex flex-col gap-1.5 text-sm text-muted-foreground",
28
+ children: plan.points.map(point => /*#__PURE__*/_jsx("li", {
29
+ children: point
30
+ }, point))
31
+ }), /*#__PURE__*/_jsx(RuleGridFooter, {
32
+ className: "mt-auto",
33
+ children: plan.current ? /*#__PURE__*/_jsx("p", {
34
+ className: "flex min-h-8 items-center text-sm text-muted-foreground",
35
+ children: "Your plan"
36
+ }) : action
37
+ })]
38
+ }, plan.name);
39
+ })
40
+ });
9
41
  }
@@ -1,12 +1,48 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, RuleGridCell, RuleGridDescription, RuleGridLabel, Spinner, } from "@locusgraph/components";
1
+ import { Button, RuleGridCell, RuleGridDescription, RuleGridLabel, Spinner } from "@locusgraph/components";
3
2
  import { centsToMicroUsd, formatCents, formatMicroUsd } from "@locusgraph/utils";
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  function hasBonus(pack) {
5
- return centsToMicroUsd(pack.priceCents) !== pack.amountMicroUsd;
5
+ return centsToMicroUsd(pack.priceCents) !== pack.amountMicroUsd;
6
6
  }
7
- export function TopUp({ packs, onBuy, pending = false, pendingPackId, highlightedPackId, description = "Pay once with Stripe. Credits never expire and never renew on their own.", }) {
8
- const busy = pending || pendingPackId !== undefined;
9
- return (_jsxs(RuleGridCell, { className: "gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: "flex min-h-6 items-center", children: _jsx(RuleGridLabel, { children: "Buy credits" }) }), _jsx(RuleGridDescription, { children: description })] }), _jsx("div", { className: "flex flex-wrap gap-2", children: packs.map((pack) => (_jsxs(Button, { variant: pack.id === highlightedPackId ? "default" : "outline", disabled: busy, "aria-busy": pack.id === pendingPackId || undefined, onClick: () => onBuy(pack.id), children: [pack.id === pendingPackId && _jsx(Spinner, { "aria-hidden": "true", role: undefined }), _jsx("span", { className: "tabular-nums", children: formatMicroUsd(pack.amountMicroUsd) }), hasBonus(pack) && (_jsxs("span", { className: pack.id === highlightedPackId
10
- ? "tabular-nums opacity-75"
11
- : "font-medium text-good tabular-nums", children: ["for ", formatCents(pack.priceCents)] }))] }, pack.id))) })] }));
7
+ export function TopUp({
8
+ packs,
9
+ onBuy,
10
+ pending = false,
11
+ pendingPackId,
12
+ highlightedPackId,
13
+ description = "Pay once with Stripe. Credits never expire and never renew on their own."
14
+ }) {
15
+ const busy = pending || pendingPackId !== undefined;
16
+ return /*#__PURE__*/_jsxs(RuleGridCell, {
17
+ className: "gap-4",
18
+ children: [/*#__PURE__*/_jsxs("div", {
19
+ className: "flex flex-col gap-1.5",
20
+ children: [/*#__PURE__*/_jsx("span", {
21
+ className: "flex min-h-6 items-center",
22
+ children: /*#__PURE__*/_jsx(RuleGridLabel, {
23
+ children: "Buy credits"
24
+ })
25
+ }), /*#__PURE__*/_jsx(RuleGridDescription, {
26
+ children: description
27
+ })]
28
+ }), /*#__PURE__*/_jsx("div", {
29
+ className: "flex flex-wrap gap-2",
30
+ children: packs.map(pack => /*#__PURE__*/_jsxs(Button, {
31
+ variant: pack.id === highlightedPackId ? "default" : "outline",
32
+ disabled: busy,
33
+ "aria-busy": pack.id === pendingPackId || undefined,
34
+ onClick: () => onBuy(pack.id),
35
+ children: [pack.id === pendingPackId && /*#__PURE__*/_jsx(Spinner, {
36
+ "aria-hidden": "true",
37
+ role: undefined
38
+ }), /*#__PURE__*/_jsx("span", {
39
+ className: "tabular-nums",
40
+ children: formatMicroUsd(pack.amountMicroUsd)
41
+ }), hasBonus(pack) && /*#__PURE__*/_jsxs("span", {
42
+ className: pack.id === highlightedPackId ? "tabular-nums opacity-75" : "font-medium text-good tabular-nums",
43
+ children: ["for ", formatCents(pack.priceCents)]
44
+ })]
45
+ }, pack.id))
46
+ })]
47
+ });
12
48
  }
@@ -1,19 +1,79 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  import { Badge, RuleGridCell, RuleGridLabel } from "@locusgraph/components";
3
2
  import { balanceLevel, formatDate, formatMicroUsd } from "@locusgraph/utils";
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  const levelHint = {
5
- low: { badge: "Low", text: "Add credits before usage stops.", tone: "text-warn" },
6
- empty: {
7
- badge: "Empty",
8
- text: "Paid usage is paused until you add credits.",
9
- tone: "text-destructive",
10
- },
5
+ low: {
6
+ badge: "Low",
7
+ text: "Add credits before usage stops.",
8
+ tone: "text-warn"
9
+ },
10
+ empty: {
11
+ badge: "Empty",
12
+ text: "Paid usage is paused until you add credits.",
13
+ tone: "text-destructive"
14
+ }
11
15
  };
12
- function GrantList({ grants }) {
13
- return (_jsx("ul", { className: "flex flex-col divide-y divide-line", children: grants.map((grant) => (_jsxs("li", { className: "flex justify-between gap-4 py-2", children: [_jsx("span", { children: grant.label }), _jsxs("span", { className: "flex items-baseline gap-2 tabular-nums", children: [_jsx("span", { className: "font-medium text-foreground", children: formatMicroUsd(grant.remainingMicroUsd) }), _jsx("span", { className: "text-muted-foreground", children: grant.expiresAt ? `expires ${formatDate(grant.expiresAt)}` : "never expires" })] })] }, `${grant.label}:${grant.expiresAt}`))) }));
16
+ function GrantList({
17
+ grants
18
+ }) {
19
+ return /*#__PURE__*/_jsx("ul", {
20
+ className: "flex flex-col divide-y divide-line",
21
+ children: grants.map(grant => /*#__PURE__*/_jsxs("li", {
22
+ className: "flex justify-between gap-4 py-2",
23
+ children: [/*#__PURE__*/_jsx("span", {
24
+ children: grant.label
25
+ }), /*#__PURE__*/_jsxs("span", {
26
+ className: "flex items-baseline gap-2 tabular-nums",
27
+ children: [/*#__PURE__*/_jsx("span", {
28
+ className: "font-medium text-foreground",
29
+ children: formatMicroUsd(grant.remainingMicroUsd)
30
+ }), /*#__PURE__*/_jsx("span", {
31
+ className: "text-muted-foreground",
32
+ children: grant.expiresAt ? `expires ${formatDate(grant.expiresAt)}` : "never expires"
33
+ })]
34
+ })]
35
+ }, `${grant.label}:${grant.expiresAt}`))
36
+ });
14
37
  }
15
- export function Wallet({ balanceMicroUsd, debtMicroUsd = 0, grants }) {
16
- const level = balanceLevel(balanceMicroUsd);
17
- const hint = level === "fine" ? null : levelHint[level];
18
- return (_jsxs(RuleGridCell, { "data-level": level, className: "gap-4", children: [_jsxs("span", { className: "flex min-h-6 items-center justify-between", children: [_jsx(RuleGridLabel, { children: "Prepaid credits" }), hint && (_jsx(Badge, { variant: level === "empty" ? "destructive" : "secondary", className: level === "low" ? "text-warn" : undefined, children: hint.badge }))] }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("span", { className: `font-heading text-3xl font-semibold tracking-[-0.02em] tabular-nums ${hint?.tone ?? ""}`, children: formatMicroUsd(balanceMicroUsd) }), hint && _jsx("p", { className: "text-sm text-muted-foreground", children: hint.text })] }), _jsxs("div", { className: "flex flex-col gap-3 text-sm", children: [grants.length > 0 && _jsx(GrantList, { grants: grants }), grants.length === 0 && level !== "empty" && (_jsx("p", { className: "text-muted-foreground", children: "No credit grants to show." })), debtMicroUsd > 0 && (_jsxs("p", { className: "text-destructive", children: [formatMicroUsd(debtMicroUsd), " owed. It comes out of the next credits you add."] }))] })] }));
38
+ export function Wallet({
39
+ balanceMicroUsd,
40
+ debtMicroUsd = 0,
41
+ grants
42
+ }) {
43
+ const level = balanceLevel(balanceMicroUsd);
44
+ const hint = level === "fine" ? null : levelHint[level];
45
+ return /*#__PURE__*/_jsxs(RuleGridCell, {
46
+ "data-level": level,
47
+ className: "gap-4",
48
+ children: [/*#__PURE__*/_jsxs("span", {
49
+ className: "flex min-h-6 items-center justify-between",
50
+ children: [/*#__PURE__*/_jsx(RuleGridLabel, {
51
+ children: "Prepaid credits"
52
+ }), hint && /*#__PURE__*/_jsx(Badge, {
53
+ variant: level === "empty" ? "destructive" : "secondary",
54
+ className: level === "low" ? "text-warn" : undefined,
55
+ children: hint.badge
56
+ })]
57
+ }), /*#__PURE__*/_jsxs("div", {
58
+ className: "flex flex-col gap-1",
59
+ children: [/*#__PURE__*/_jsx("span", {
60
+ className: `font-heading text-3xl font-semibold tracking-[-0.02em] tabular-nums ${hint?.tone ?? ""}`,
61
+ children: formatMicroUsd(balanceMicroUsd)
62
+ }), hint && /*#__PURE__*/_jsx("p", {
63
+ className: "text-sm text-muted-foreground",
64
+ children: hint.text
65
+ })]
66
+ }), /*#__PURE__*/_jsxs("div", {
67
+ className: "flex flex-col gap-3 text-sm",
68
+ children: [grants.length > 0 && /*#__PURE__*/_jsx(GrantList, {
69
+ grants: grants
70
+ }), grants.length === 0 && level !== "empty" && /*#__PURE__*/_jsx("p", {
71
+ className: "text-muted-foreground",
72
+ children: "No credit grants to show."
73
+ }), debtMicroUsd > 0 && /*#__PURE__*/_jsxs("p", {
74
+ className: "text-destructive",
75
+ children: [formatMicroUsd(debtMicroUsd), " owed. It comes out of the next credits you add."]
76
+ })]
77
+ })]
78
+ });
19
79
  }
@@ -1,10 +1,54 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+
3
+ import { c as _c } from "react/compiler-runtime";
3
4
  import { useEffect, useState } from "react";
4
- const utcFormat = new Intl.DateTimeFormat("en-US", { dateStyle: "medium", timeZone: "UTC" });
5
- const localFormat = new Intl.DateTimeFormat("en-US", { dateStyle: "medium" });
6
- export function LocalDate({ value }) {
7
- const [hydrated, setHydrated] = useState(false);
8
- useEffect(() => setHydrated(true), []);
9
- return (_jsx("time", { dateTime: value, children: (hydrated ? localFormat : utcFormat).format(new Date(value)) }));
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ const utcFormat = new Intl.DateTimeFormat("en-US", {
7
+ dateStyle: "medium",
8
+ timeZone: "UTC"
9
+ });
10
+ const localFormat = new Intl.DateTimeFormat("en-US", {
11
+ dateStyle: "medium"
12
+ });
13
+ export function LocalDate(t0) {
14
+ const $ = _c(8);
15
+ const {
16
+ value
17
+ } = t0;
18
+ const [hydrated, setHydrated] = useState(false);
19
+ let t1;
20
+ let t2;
21
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22
+ t1 = () => setHydrated(true);
23
+ t2 = [];
24
+ $[0] = t1;
25
+ $[1] = t2;
26
+ } else {
27
+ t1 = $[0];
28
+ t2 = $[1];
29
+ }
30
+ useEffect(t1, t2);
31
+ const t3 = hydrated ? localFormat : utcFormat;
32
+ let t4;
33
+ if ($[2] !== t3 || $[3] !== value) {
34
+ t4 = t3.format(new Date(value));
35
+ $[2] = t3;
36
+ $[3] = value;
37
+ $[4] = t4;
38
+ } else {
39
+ t4 = $[4];
40
+ }
41
+ let t5;
42
+ if ($[5] !== t4 || $[6] !== value) {
43
+ t5 = /*#__PURE__*/_jsx("time", {
44
+ dateTime: value,
45
+ children: t4
46
+ });
47
+ $[5] = t4;
48
+ $[6] = value;
49
+ $[7] = t5;
50
+ } else {
51
+ t5 = $[7];
52
+ }
53
+ return t5;
10
54
  }
@@ -1,8 +1,31 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  import { Password as KeyRoundIcon, Add as PlusIcon } from "@carbon/icons-react";
3
- import { Button, RuleGrid, RuleGridCell, RuleGridDescription, RuleGridTitle, } from "@locusgraph/components";
4
- export function KeysEmpty({ newKeyHref, description = newKeyHref
5
- ? "Create a key to let a service call LocusGraph products for this organization."
6
- : "Ask an owner to create a key for this organization.", linkComponent: Link = "a", }) {
7
- return (_jsx(RuleGrid, { columns: 1, children: _jsxs(RuleGridCell, { className: "items-center gap-2 py-12 text-center", children: [_jsx(KeyRoundIcon, { className: "mb-1 size-5 text-muted-foreground" }), _jsx(RuleGridTitle, { children: "No API keys yet" }), _jsx(RuleGridDescription, { className: "max-w-sm", children: description }), newKeyHref && (_jsx(Button, { asChild: true, size: "sm", className: "mt-2", children: _jsxs(Link, { href: newKeyHref, children: [_jsx(PlusIcon, {}), "New key"] }) }))] }) }));
2
+ import { Button, RuleGrid, RuleGridCell, RuleGridDescription, RuleGridTitle } from "@locusgraph/components";
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ export function KeysEmpty({
5
+ newKeyHref,
6
+ description = newKeyHref ? "Create a key to let a service call LocusGraph products for this organization." : "Ask an owner to create a key for this organization.",
7
+ linkComponent: Link = "a"
8
+ }) {
9
+ return /*#__PURE__*/_jsx(RuleGrid, {
10
+ columns: 1,
11
+ children: /*#__PURE__*/_jsxs(RuleGridCell, {
12
+ className: "items-center gap-2 py-12 text-center",
13
+ children: [/*#__PURE__*/_jsx(KeyRoundIcon, {
14
+ className: "mb-1 size-5 text-muted-foreground"
15
+ }), /*#__PURE__*/_jsx(RuleGridTitle, {
16
+ children: "No API keys yet"
17
+ }), /*#__PURE__*/_jsx(RuleGridDescription, {
18
+ className: "max-w-sm",
19
+ children: description
20
+ }), newKeyHref && /*#__PURE__*/_jsx(Button, {
21
+ asChild: true,
22
+ size: "sm",
23
+ className: "mt-2",
24
+ children: /*#__PURE__*/_jsxs(Link, {
25
+ href: newKeyHref,
26
+ children: [/*#__PURE__*/_jsx(PlusIcon, {}), "New key"]
27
+ })
28
+ })]
29
+ })
30
+ });
8
31
  }
package/dist/keys/keys.js CHANGED
@@ -1,9 +1,69 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@locusgraph/components";
1
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@locusgraph/components";
3
2
  import { LocalDate } from "../dates/local-date.js";
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  function dateOr(value, empty) {
5
- return value ? _jsx(LocalDate, { value: value }) : empty;
5
+ return value ? /*#__PURE__*/_jsx(LocalDate, {
6
+ value: value
7
+ }) : empty;
6
8
  }
7
- export function Keys({ keys, actions, highlightedId }) {
8
- return (_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: "Name" }), _jsx(TableHead, { className: "hidden md:table-cell", children: "Key" }), _jsx(TableHead, { children: "Product" }), _jsx(TableHead, { className: "hidden md:table-cell", children: "Created by" }), _jsx(TableHead, { className: "hidden md:table-cell", children: "Created" }), _jsx(TableHead, { children: "Expires" }), actions && _jsx(TableHead, { className: "w-0" })] }) }), _jsx(TableBody, { children: keys.map((key) => (_jsxs(TableRow, { "data-new": key.id === highlightedId ? "" : undefined, className: "transition-colors duration-1000 data-new:bg-accent", children: [_jsxs(TableCell, { className: "max-w-40", children: [_jsx("span", { className: "block truncate font-medium", children: key.name ?? "Untitled" }), _jsx("span", { className: "block truncate font-mono text-xs text-muted-foreground md:hidden", children: key.maskedKey })] }), _jsx(TableCell, { className: "hidden font-mono text-muted-foreground md:table-cell", children: key.maskedKey }), _jsx(TableCell, { children: key.productLabel }), _jsx(TableCell, { className: "hidden md:table-cell", children: key.createdBy }), _jsx(TableCell, { className: "hidden md:table-cell", children: dateOr(key.createdAt, "—") }), _jsx(TableCell, { children: dateOr(key.expiresAt, "Never") }), actions && (_jsx(TableCell, { className: "w-0 pr-2 text-right whitespace-nowrap", children: actions(key) }))] }, key.id))) })] }));
9
+ export function Keys({
10
+ keys,
11
+ actions,
12
+ highlightedId
13
+ }) {
14
+ return /*#__PURE__*/_jsxs(Table, {
15
+ children: [/*#__PURE__*/_jsx(TableHeader, {
16
+ children: /*#__PURE__*/_jsxs(TableRow, {
17
+ children: [/*#__PURE__*/_jsx(TableHead, {
18
+ children: "Name"
19
+ }), /*#__PURE__*/_jsx(TableHead, {
20
+ className: "hidden md:table-cell",
21
+ children: "Key"
22
+ }), /*#__PURE__*/_jsx(TableHead, {
23
+ children: "Product"
24
+ }), /*#__PURE__*/_jsx(TableHead, {
25
+ className: "hidden md:table-cell",
26
+ children: "Created by"
27
+ }), /*#__PURE__*/_jsx(TableHead, {
28
+ className: "hidden md:table-cell",
29
+ children: "Created"
30
+ }), /*#__PURE__*/_jsx(TableHead, {
31
+ children: "Expires"
32
+ }), actions && /*#__PURE__*/_jsx(TableHead, {
33
+ className: "w-0"
34
+ })]
35
+ })
36
+ }), /*#__PURE__*/_jsx(TableBody, {
37
+ children: keys.map(key => /*#__PURE__*/_jsxs(TableRow, {
38
+ "data-new": key.id === highlightedId ? "" : undefined,
39
+ className: "transition-colors duration-1000 data-new:bg-accent",
40
+ children: [/*#__PURE__*/_jsxs(TableCell, {
41
+ className: "max-w-40",
42
+ children: [/*#__PURE__*/_jsx("span", {
43
+ className: "block truncate font-medium",
44
+ children: key.name ?? "Untitled"
45
+ }), /*#__PURE__*/_jsx("span", {
46
+ className: "block truncate font-mono text-xs text-muted-foreground md:hidden",
47
+ children: key.maskedKey
48
+ })]
49
+ }), /*#__PURE__*/_jsx(TableCell, {
50
+ className: "hidden font-mono text-muted-foreground md:table-cell",
51
+ children: key.maskedKey
52
+ }), /*#__PURE__*/_jsx(TableCell, {
53
+ children: key.productLabel
54
+ }), /*#__PURE__*/_jsx(TableCell, {
55
+ className: "hidden md:table-cell",
56
+ children: key.createdBy
57
+ }), /*#__PURE__*/_jsx(TableCell, {
58
+ className: "hidden md:table-cell",
59
+ children: dateOr(key.createdAt, "—")
60
+ }), /*#__PURE__*/_jsx(TableCell, {
61
+ children: dateOr(key.expiresAt, "Never")
62
+ }), actions && /*#__PURE__*/_jsx(TableCell, {
63
+ className: "w-0 pr-2 text-right whitespace-nowrap",
64
+ children: actions(key)
65
+ })]
66
+ }, key.id))
67
+ })]
68
+ });
9
69
  }
@@ -1,18 +1,85 @@
1
+ import { SkeletonBar, SkeletonLoadingLabel, skeletonRootProps, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@locusgraph/components";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { SkeletonBar, SkeletonLoadingLabel, skeletonRootProps, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@locusgraph/components";
3
- const columns = [
4
- { name: "Key", visibility: "hidden md:table-cell", bar: "w-36" },
5
- { name: "Product", visibility: "", bar: "w-20" },
6
- { name: "Created by", visibility: "hidden md:table-cell", bar: "w-32" },
7
- { name: "Created", visibility: "hidden md:table-cell", bar: "w-20" },
8
- { name: "Expires", visibility: "", bar: "w-20" },
9
- ];
3
+ const columns = [{
4
+ name: "Key",
5
+ visibility: "hidden md:table-cell",
6
+ bar: "w-36"
7
+ }, {
8
+ name: "Product",
9
+ visibility: "",
10
+ bar: "w-20"
11
+ }, {
12
+ name: "Created by",
13
+ visibility: "hidden md:table-cell",
14
+ bar: "w-32"
15
+ }, {
16
+ name: "Created",
17
+ visibility: "hidden md:table-cell",
18
+ bar: "w-20"
19
+ }, {
20
+ name: "Expires",
21
+ visibility: "",
22
+ bar: "w-20"
23
+ }];
10
24
  function NameCellSkeleton() {
11
- return (_jsx(TableCell, { className: "max-w-40", children: _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(SkeletonBar, { className: "h-4 w-28" }), _jsx(SkeletonBar, { className: "h-3 w-20 md:hidden" })] }) }));
25
+ return /*#__PURE__*/_jsx(TableCell, {
26
+ className: "max-w-40",
27
+ children: /*#__PURE__*/_jsxs("div", {
28
+ className: "flex flex-col gap-1",
29
+ children: [/*#__PURE__*/_jsx(SkeletonBar, {
30
+ className: "h-4 w-28"
31
+ }), /*#__PURE__*/_jsx(SkeletonBar, {
32
+ className: "h-3 w-20 md:hidden"
33
+ })]
34
+ })
35
+ });
12
36
  }
13
- export function KeysSkeleton({ rows = 3, actions = false }) {
14
- const count = Math.max(1, Math.floor(rows));
15
- return (_jsxs("div", { "data-slot": "keys-skeleton", ...skeletonRootProps, className: "w-full", children: [_jsxs(Table, { "aria-hidden": "true", children: [_jsx(TableHeader, { children: _jsxs(TableRow, { className: "hover:bg-transparent", children: [_jsx(TableHead, { children: "Name" }), columns.map((column) => (_jsx(TableHead, { className: column.visibility, children: column.name }, column.name))), actions && _jsx(TableHead, { className: "w-0" })] }) }), _jsx(TableBody, { children: Array.from({ length: count }, (_, row) => (_jsxs(TableRow
16
- // biome-ignore lint/suspicious/noArrayIndexKey: placeholder rows have no identity
17
- , { "data-slot": "keys-skeleton-row", className: "hover:bg-transparent", children: [_jsx(NameCellSkeleton, {}), columns.map((column) => (_jsx(TableCell, { className: column.visibility, children: _jsx(SkeletonBar, { className: `h-4 ${column.bar}` }) }, column.name))), actions && (_jsx(TableCell, { className: "w-0 pr-2 text-right", children: _jsx(SkeletonBar, { className: "ml-auto h-8 w-16 rounded-full" }) }))] }, row))) })] }), _jsx(SkeletonLoadingLabel, { children: "Loading API keys\u2026" })] }));
37
+ export function KeysSkeleton({
38
+ rows = 3,
39
+ actions = false
40
+ }) {
41
+ const count = Math.max(1, Math.floor(rows));
42
+ return /*#__PURE__*/_jsxs("div", {
43
+ "data-slot": "keys-skeleton",
44
+ ...skeletonRootProps,
45
+ className: "w-full",
46
+ children: [/*#__PURE__*/_jsxs(Table, {
47
+ "aria-hidden": "true",
48
+ children: [/*#__PURE__*/_jsx(TableHeader, {
49
+ children: /*#__PURE__*/_jsxs(TableRow, {
50
+ className: "hover:bg-transparent",
51
+ children: [/*#__PURE__*/_jsx(TableHead, {
52
+ children: "Name"
53
+ }), columns.map(column => /*#__PURE__*/_jsx(TableHead, {
54
+ className: column.visibility,
55
+ children: column.name
56
+ }, column.name)), actions && /*#__PURE__*/_jsx(TableHead, {
57
+ className: "w-0"
58
+ })]
59
+ })
60
+ }), /*#__PURE__*/_jsx(TableBody, {
61
+ children: Array.from({
62
+ length: count
63
+ }, (_, row) => /*#__PURE__*/_jsxs(TableRow
64
+ // biome-ignore lint/suspicious/noArrayIndexKey: placeholder rows have no identity
65
+ , {
66
+ "data-slot": "keys-skeleton-row",
67
+ className: "hover:bg-transparent",
68
+ children: [/*#__PURE__*/_jsx(NameCellSkeleton, {}), columns.map(column => /*#__PURE__*/_jsx(TableCell, {
69
+ className: column.visibility,
70
+ children: /*#__PURE__*/_jsx(SkeletonBar, {
71
+ className: `h-4 ${column.bar}`
72
+ })
73
+ }, column.name)), actions && /*#__PURE__*/_jsx(TableCell, {
74
+ className: "w-0 pr-2 text-right",
75
+ children: /*#__PURE__*/_jsx(SkeletonBar, {
76
+ className: "ml-auto h-8 w-16 rounded-full"
77
+ })
78
+ })]
79
+ }, row))
80
+ })]
81
+ }), /*#__PURE__*/_jsx(SkeletonLoadingLabel, {
82
+ children: "Loading API keys\u2026"
83
+ })]
84
+ });
18
85
  }
@@ -1,9 +1,54 @@
1
+ import { RuleGrid, RuleGridCell, SkeletonBar, SkeletonLoadingLabel, skeletonRootProps } from "@locusgraph/components";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { RuleGrid, RuleGridCell, SkeletonBar, SkeletonLoadingLabel, skeletonRootProps, } from "@locusgraph/components";
3
- export function PlansSkeleton({ plans = 2, points = 2, currentIndex = 0 }) {
4
- const planCount = Math.max(1, Math.floor(plans));
5
- const pointCount = Math.max(0, Math.floor(points));
6
- return (_jsxs(RuleGrid, { columns: planCount >= 3 ? 3 : 2, "data-slot": "plans-skeleton", ...skeletonRootProps, children: [Array.from({ length: planCount }, (_, plan) => (_jsxs(RuleGridCell
7
- // biome-ignore lint/suspicious/noArrayIndexKey: placeholder plans have no identity
8
- , { "data-slot": "plan-skeleton", "aria-hidden": "true", className: "gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("div", { className: "flex min-h-6 items-center", children: _jsx(SkeletonBar, { className: "h-5 w-20" }) }), _jsx(SkeletonBar, { className: "h-8 w-28" })] }), _jsx("div", { className: "flex flex-col gap-1.5", children: Array.from({ length: pointCount }, (_, point) => (_jsx(SkeletonBar, { className: "h-4 w-40" }, point))) }), plan === currentIndex ? (_jsx("div", { className: "mt-auto flex min-h-8 items-center", children: _jsx(SkeletonBar, { className: "h-4 w-16" }) })) : (_jsx(SkeletonBar, { className: "mt-auto h-8 w-28 rounded-full" }))] }, plan))), _jsx(SkeletonLoadingLabel, { children: "Loading plans\u2026" })] }));
3
+ export function PlansSkeleton({
4
+ plans = 2,
5
+ points = 2,
6
+ currentIndex = 0
7
+ }) {
8
+ const planCount = Math.max(1, Math.floor(plans));
9
+ const pointCount = Math.max(0, Math.floor(points));
10
+ return /*#__PURE__*/_jsxs(RuleGrid, {
11
+ columns: planCount >= 3 ? 3 : 2,
12
+ "data-slot": "plans-skeleton",
13
+ ...skeletonRootProps,
14
+ children: [Array.from({
15
+ length: planCount
16
+ }, (_, plan) => /*#__PURE__*/_jsxs(RuleGridCell
17
+ // biome-ignore lint/suspicious/noArrayIndexKey: placeholder plans have no identity
18
+ , {
19
+ "data-slot": "plan-skeleton",
20
+ "aria-hidden": "true",
21
+ className: "gap-4",
22
+ children: [/*#__PURE__*/_jsxs("div", {
23
+ className: "flex flex-col gap-2",
24
+ children: [/*#__PURE__*/_jsx("div", {
25
+ className: "flex min-h-6 items-center",
26
+ children: /*#__PURE__*/_jsx(SkeletonBar, {
27
+ className: "h-5 w-20"
28
+ })
29
+ }), /*#__PURE__*/_jsx(SkeletonBar, {
30
+ className: "h-8 w-28"
31
+ })]
32
+ }), /*#__PURE__*/_jsx("div", {
33
+ className: "flex flex-col gap-1.5",
34
+ children: Array.from({
35
+ length: pointCount
36
+ }, (_, point) =>
37
+ /*#__PURE__*/
38
+ // biome-ignore lint/suspicious/noArrayIndexKey: placeholder points have no identity
39
+ _jsx(SkeletonBar, {
40
+ className: "h-4 w-40"
41
+ }, point))
42
+ }), plan === currentIndex ? /*#__PURE__*/_jsx("div", {
43
+ className: "mt-auto flex min-h-8 items-center",
44
+ children: /*#__PURE__*/_jsx(SkeletonBar, {
45
+ className: "h-4 w-16"
46
+ })
47
+ }) : /*#__PURE__*/_jsx(SkeletonBar, {
48
+ className: "mt-auto h-8 w-28 rounded-full"
49
+ })]
50
+ }, plan)), /*#__PURE__*/_jsx(SkeletonLoadingLabel, {
51
+ children: "Loading plans\u2026"
52
+ })]
53
+ });
9
54
  }
@@ -1,8 +1,38 @@
1
+ import { RuleGridCell, RuleGridLabel, SkeletonBar, SkeletonLoadingLabel, skeletonRootProps } from "@locusgraph/components";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { RuleGridCell, RuleGridLabel, SkeletonBar, SkeletonLoadingLabel, skeletonRootProps, } from "@locusgraph/components";
3
- export function TopUpSkeleton({ packs = 3 }) {
4
- const count = Math.max(1, Math.floor(packs));
5
- return (_jsxs(RuleGridCell, { "data-slot": "top-up-skeleton", ...skeletonRootProps, className: "gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: "flex min-h-6 items-center", children: _jsx(RuleGridLabel, { children: "Buy credits" }) }), _jsx(SkeletonBar, { className: "h-4 w-full max-w-72" }), _jsx(SkeletonBar, { className: "h-4 w-3/4 max-w-56" })] }), _jsx("div", { className: "flex flex-wrap gap-2", "aria-hidden": "true", children: Array.from({ length: count }, (_, index) => (_jsx(SkeletonBar
6
- // biome-ignore lint/suspicious/noArrayIndexKey: placeholder packs have no identity
7
- , { "data-slot": "credit-pack-skeleton", className: "h-9 w-24 rounded-full" }, index))) }), _jsx(SkeletonLoadingLabel, { children: "Loading credit packs\u2026" })] }));
3
+ export function TopUpSkeleton({
4
+ packs = 3
5
+ }) {
6
+ const count = Math.max(1, Math.floor(packs));
7
+ return /*#__PURE__*/_jsxs(RuleGridCell, {
8
+ "data-slot": "top-up-skeleton",
9
+ ...skeletonRootProps,
10
+ className: "gap-4",
11
+ children: [/*#__PURE__*/_jsxs("div", {
12
+ className: "flex flex-col gap-1.5",
13
+ children: [/*#__PURE__*/_jsx("span", {
14
+ className: "flex min-h-6 items-center",
15
+ children: /*#__PURE__*/_jsx(RuleGridLabel, {
16
+ children: "Buy credits"
17
+ })
18
+ }), /*#__PURE__*/_jsx(SkeletonBar, {
19
+ className: "h-4 w-full max-w-72"
20
+ }), /*#__PURE__*/_jsx(SkeletonBar, {
21
+ className: "h-4 w-3/4 max-w-56"
22
+ })]
23
+ }), /*#__PURE__*/_jsx("div", {
24
+ className: "flex flex-wrap gap-2",
25
+ "aria-hidden": "true",
26
+ children: Array.from({
27
+ length: count
28
+ }, (_, index) => /*#__PURE__*/_jsx(SkeletonBar
29
+ // biome-ignore lint/suspicious/noArrayIndexKey: placeholder packs have no identity
30
+ , {
31
+ "data-slot": "credit-pack-skeleton",
32
+ className: "h-9 w-24 rounded-full"
33
+ }, index))
34
+ }), /*#__PURE__*/_jsx(SkeletonLoadingLabel, {
35
+ children: "Loading credit packs\u2026"
36
+ })]
37
+ });
8
38
  }
@@ -1,9 +1,43 @@
1
+ import { RuleGrid, RuleGridCell, RuleGridLabel, SkeletonBar, SkeletonLoadingLabel, skeletonRootProps } from "@locusgraph/components";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { RuleGrid, RuleGridCell, RuleGridLabel, SkeletonBar, SkeletonLoadingLabel, skeletonRootProps, } from "@locusgraph/components";
3
3
  function QuotaSkeleton() {
4
- return (_jsxs("div", { "data-slot": "quota-skeleton", "aria-hidden": "true", className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-4", children: [_jsx(SkeletonBar, { className: "h-4 w-28" }), _jsx(SkeletonBar, { className: "h-4 w-36" })] }), _jsx(SkeletonBar, { className: "h-2 w-full rounded-full" })] }));
4
+ return /*#__PURE__*/_jsxs("div", {
5
+ "data-slot": "quota-skeleton",
6
+ "aria-hidden": "true",
7
+ className: "flex flex-col gap-2",
8
+ children: [/*#__PURE__*/_jsxs("div", {
9
+ className: "flex items-baseline justify-between gap-4",
10
+ children: [/*#__PURE__*/_jsx(SkeletonBar, {
11
+ className: "h-4 w-28"
12
+ }), /*#__PURE__*/_jsx(SkeletonBar, {
13
+ className: "h-4 w-36"
14
+ })]
15
+ }), /*#__PURE__*/_jsx(SkeletonBar, {
16
+ className: "h-2 w-full rounded-full"
17
+ })]
18
+ });
5
19
  }
6
- export function UsageSkeleton({ quotas = 2, label = "Usage" }) {
7
- const count = Math.max(1, Math.floor(quotas));
8
- return (_jsx(RuleGrid, { columns: 1, "data-slot": "usage-skeleton", ...skeletonRootProps, children: _jsxs(RuleGridCell, { className: "gap-5", children: [_jsx(RuleGridLabel, { children: label }), Array.from({ length: count }, (_, index) => (_jsx(QuotaSkeleton, {}, index))), _jsx(SkeletonLoadingLabel, { children: "Loading usage\u2026" })] }) }));
20
+ export function UsageSkeleton({
21
+ quotas = 2,
22
+ label = "Usage"
23
+ }) {
24
+ const count = Math.max(1, Math.floor(quotas));
25
+ return /*#__PURE__*/_jsx(RuleGrid, {
26
+ columns: 1,
27
+ "data-slot": "usage-skeleton",
28
+ ...skeletonRootProps,
29
+ children: /*#__PURE__*/_jsxs(RuleGridCell, {
30
+ className: "gap-5",
31
+ children: [/*#__PURE__*/_jsx(RuleGridLabel, {
32
+ children: label
33
+ }), Array.from({
34
+ length: count
35
+ }, (_, index) =>
36
+ /*#__PURE__*/
37
+ // biome-ignore lint/suspicious/noArrayIndexKey: placeholder meters have no identity
38
+ _jsx(QuotaSkeleton, {}, index)), /*#__PURE__*/_jsx(SkeletonLoadingLabel, {
39
+ children: "Loading usage\u2026"
40
+ })]
41
+ })
42
+ });
9
43
  }
@@ -1,6 +1,40 @@
1
+ import { RuleGridCell, RuleGridLabel, SkeletonBar, SkeletonLoadingLabel, skeletonRootProps } from "@locusgraph/components";
1
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { RuleGridCell, RuleGridLabel, SkeletonBar, SkeletonLoadingLabel, skeletonRootProps, } from "@locusgraph/components";
3
- export function WalletSkeleton({ grants = 1 }) {
4
- const count = Math.max(0, Math.floor(grants));
5
- return (_jsxs(RuleGridCell, { "data-slot": "wallet-skeleton", ...skeletonRootProps, className: "gap-4", children: [_jsx("span", { className: "flex min-h-6 items-center justify-between", children: _jsx(RuleGridLabel, { children: "Prepaid credits" }) }), _jsx("div", { className: "flex flex-col gap-1", "aria-hidden": "true", children: _jsx(SkeletonBar, { className: "h-9 w-32" }) }), _jsx("ul", { className: "flex flex-col divide-y divide-line", "aria-hidden": "true", children: Array.from({ length: count }, (_, index) => (_jsxs("li", { "data-slot": "wallet-grant-skeleton", className: "flex justify-between gap-4 py-2", children: [_jsx(SkeletonBar, { className: "h-4 w-24" }), _jsx(SkeletonBar, { className: "h-4 w-40" })] }, index))) }), _jsx(SkeletonLoadingLabel, { children: "Loading credits\u2026" })] }));
3
+ export function WalletSkeleton({
4
+ grants = 1
5
+ }) {
6
+ const count = Math.max(0, Math.floor(grants));
7
+ return /*#__PURE__*/_jsxs(RuleGridCell, {
8
+ "data-slot": "wallet-skeleton",
9
+ ...skeletonRootProps,
10
+ className: "gap-4",
11
+ children: [/*#__PURE__*/_jsx("span", {
12
+ className: "flex min-h-6 items-center justify-between",
13
+ children: /*#__PURE__*/_jsx(RuleGridLabel, {
14
+ children: "Prepaid credits"
15
+ })
16
+ }), /*#__PURE__*/_jsx("div", {
17
+ className: "flex flex-col gap-1",
18
+ "aria-hidden": "true",
19
+ children: /*#__PURE__*/_jsx(SkeletonBar, {
20
+ className: "h-9 w-32"
21
+ })
22
+ }), /*#__PURE__*/_jsx("ul", {
23
+ className: "flex flex-col divide-y divide-line",
24
+ "aria-hidden": "true",
25
+ children: Array.from({
26
+ length: count
27
+ }, (_, index) => /*#__PURE__*/_jsxs("li", {
28
+ "data-slot": "wallet-grant-skeleton",
29
+ className: "flex justify-between gap-4 py-2",
30
+ children: [/*#__PURE__*/_jsx(SkeletonBar, {
31
+ className: "h-4 w-24"
32
+ }), /*#__PURE__*/_jsx(SkeletonBar, {
33
+ className: "h-4 w-40"
34
+ })]
35
+ }, index))
36
+ }), /*#__PURE__*/_jsx(SkeletonLoadingLabel, {
37
+ children: "Loading credits\u2026"
38
+ })]
39
+ });
6
40
  }
package/dist/types.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export function periodLabel(period) {
2
- return period === "month" ? "this month" : "lifetime";
2
+ return period === "month" ? "this month" : "lifetime";
3
3
  }
@@ -1,24 +1,57 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  import { Progress } from "@locusgraph/components";
3
2
  import { formatCount, quotaPercent, quotaStatus } from "@locusgraph/utils";
4
3
  import { periodLabel } from "../types.js";
4
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
5
5
  const statusIndicator = {
6
- ok: "",
7
- near: "[&_[data-slot=progress-indicator]]:bg-warn",
8
- over: "[&_[data-slot=progress-indicator]]:bg-destructive",
6
+ ok: "",
7
+ near: "[&_[data-slot=progress-indicator]]:bg-warn",
8
+ over: "[&_[data-slot=progress-indicator]]:bg-destructive"
9
9
  };
10
- function QuotaStatusNote({ quota }) {
11
- const status = quotaStatus(quota);
12
- if (status === "over") {
13
- return (_jsxs("span", { className: "font-medium text-destructive", children: [formatCount(quota.used - quota.included), " over"] }));
14
- }
15
- if (status === "near") {
16
- return _jsxs("span", { className: "font-medium text-warn", children: [quotaPercent(quota), "%"] });
17
- }
18
- return null;
10
+ function QuotaStatusNote({
11
+ quota
12
+ }) {
13
+ const status = quotaStatus(quota);
14
+ if (status === "over") {
15
+ return /*#__PURE__*/_jsxs("span", {
16
+ className: "font-medium text-destructive",
17
+ children: [formatCount(quota.used - quota.included), " over"]
18
+ });
19
+ }
20
+ if (status === "near") {
21
+ return /*#__PURE__*/_jsxs("span", {
22
+ className: "font-medium text-warn",
23
+ children: [quotaPercent(quota), "%"]
24
+ });
25
+ }
26
+ return null;
19
27
  }
20
- export function Quota({ quota }) {
21
- const percent = quotaPercent(quota);
22
- const name = quota.label ?? quota.key;
23
- return (_jsxs("div", { "data-slot": "quota", "data-status": quotaStatus(quota), className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1 text-sm", children: [_jsx("span", { className: quota.label ? "text-muted-foreground" : "font-mono text-muted-foreground", children: name }), _jsxs("span", { className: "flex items-baseline gap-2 tabular-nums", children: [_jsxs("span", { className: "font-medium text-foreground", children: [formatCount(quota.used), " of ", formatCount(quota.included), " ", periodLabel(quota.period)] }), _jsx(QuotaStatusNote, { quota: quota })] })] }), _jsx(Progress, { value: percent, "aria-label": `${name} ${percent}% of quota`, className: statusIndicator[quotaStatus(quota)] })] }));
28
+ export function Quota({
29
+ quota
30
+ }) {
31
+ const percent = quotaPercent(quota);
32
+ const name = quota.label ?? quota.key;
33
+ return /*#__PURE__*/_jsxs("div", {
34
+ "data-slot": "quota",
35
+ "data-status": quotaStatus(quota),
36
+ className: "flex flex-col gap-2",
37
+ children: [/*#__PURE__*/_jsxs("div", {
38
+ className: "flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1 text-sm",
39
+ children: [/*#__PURE__*/_jsx("span", {
40
+ className: quota.label ? "text-muted-foreground" : "font-mono text-muted-foreground",
41
+ children: name
42
+ }), /*#__PURE__*/_jsxs("span", {
43
+ className: "flex items-baseline gap-2 tabular-nums",
44
+ children: [/*#__PURE__*/_jsxs("span", {
45
+ className: "font-medium text-foreground",
46
+ children: [formatCount(quota.used), " of ", formatCount(quota.included), " ", periodLabel(quota.period)]
47
+ }), /*#__PURE__*/_jsx(QuotaStatusNote, {
48
+ quota: quota
49
+ })]
50
+ })]
51
+ }), /*#__PURE__*/_jsx(Progress, {
52
+ value: percent,
53
+ "aria-label": `${name} ${percent}% of quota`,
54
+ className: statusIndicator[quotaStatus(quota)]
55
+ })]
56
+ });
24
57
  }
@@ -1,29 +1,66 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  import { Warning as CircleAlertIcon } from "@carbon/icons-react";
3
2
  import { Alert, AlertDescription, AlertTitle, Button } from "@locusgraph/components";
4
- import { balanceLevel, formatCount, formatMicroUsd, quotaStatus, } from "@locusgraph/utils";
3
+ import { balanceLevel, formatCount, formatMicroUsd, quotaStatus } from "@locusgraph/utils";
5
4
  import { periodLabel } from "../types.js";
5
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  function attentionTitle(level, overCount) {
7
- if (level === "empty")
8
- return "Out of credits";
9
- if (overCount === 0)
10
- return "Credits are running low";
11
- return level === "low"
12
- ? "Usage is over your plan and credits are low"
13
- : "Usage is over your plan";
7
+ if (level === "empty") return "Out of credits";
8
+ if (overCount === 0) return "Credits are running low";
9
+ return level === "low" ? "Usage is over your plan and credits are low" : "Usage is over your plan";
14
10
  }
15
11
  const toneClasses = {
16
- warn: "bg-warn/5 [&>svg]:text-warn",
17
- destructive: "bg-destructive/5 text-card-foreground [&>svg]:text-destructive",
12
+ warn: "bg-warn/5 [&>svg]:text-warn",
13
+ destructive: "bg-destructive/5 text-card-foreground [&>svg]:text-destructive"
18
14
  };
19
- export function UsageAlert({ balanceMicroUsd, quotas, paidPlan, buyCreditsHref, upgradeHref, linkComponent: Link = "a", }) {
20
- const level = balanceMicroUsd === null ? "fine" : balanceLevel(balanceMicroUsd);
21
- const over = quotas.filter((quota) => quotaStatus(quota) === "over");
22
- if (level === "fine" && over.length === 0)
23
- return null;
24
- const empty = level === "empty";
25
- const upgradeFirst = !paidPlan && over.length > 0;
26
- return (_jsxs(Alert, { className: toneClasses[empty ? "destructive" : "warn"], children: [_jsx(CircleAlertIcon, {}), _jsx(AlertTitle, { children: attentionTitle(level, over.length) }), _jsxs(AlertDescription, { className: "flex flex-col items-start gap-3", children: [_jsxs("ul", { className: "flex flex-col gap-1", children: [over.map((quota) => (_jsxs("li", { children: [_jsx("span", { className: quota.label ? undefined : "font-mono", children: quota.label ?? quota.key }), " ", "is at ", formatCount(quota.used), " of ", formatCount(quota.included), " ", periodLabel(quota.period), "."] }, quota.key))), balanceMicroUsd !== null && level !== "fine" && (_jsx("li", { children: empty
27
- ? "Paid usage is paused until you add credits."
28
- : `${formatMicroUsd(balanceMicroUsd)} of credits left.` }))] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [!paidPlan && (_jsx(Button, { asChild: true, size: "sm", variant: upgradeFirst ? "default" : "ghost", children: _jsx(Link, { href: upgradeHref, children: "Upgrade to Pro" }) })), _jsx(Button, { asChild: true, size: "sm", variant: upgradeFirst ? "ghost" : "default", children: _jsx(Link, { href: buyCreditsHref, children: "Buy credits" }) })] })] })] }));
15
+ export function UsageAlert({
16
+ balanceMicroUsd,
17
+ quotas,
18
+ paidPlan,
19
+ buyCreditsHref,
20
+ upgradeHref,
21
+ linkComponent: Link = "a"
22
+ }) {
23
+ const level = balanceMicroUsd === null ? "fine" : balanceLevel(balanceMicroUsd);
24
+ const over = quotas.filter(quota => quotaStatus(quota) === "over");
25
+ if (level === "fine" && over.length === 0) return null;
26
+ const empty = level === "empty";
27
+ const upgradeFirst = !paidPlan && over.length > 0;
28
+ return /*#__PURE__*/_jsxs(Alert, {
29
+ className: toneClasses[empty ? "destructive" : "warn"],
30
+ children: [/*#__PURE__*/_jsx(CircleAlertIcon, {}), /*#__PURE__*/_jsx(AlertTitle, {
31
+ children: attentionTitle(level, over.length)
32
+ }), /*#__PURE__*/_jsxs(AlertDescription, {
33
+ className: "flex flex-col items-start gap-3",
34
+ children: [/*#__PURE__*/_jsxs("ul", {
35
+ className: "flex flex-col gap-1",
36
+ children: [over.map(quota => /*#__PURE__*/_jsxs("li", {
37
+ children: [/*#__PURE__*/_jsx("span", {
38
+ className: quota.label ? undefined : "font-mono",
39
+ children: quota.label ?? quota.key
40
+ }), " ", "is at ", formatCount(quota.used), " of ", formatCount(quota.included), " ", periodLabel(quota.period), "."]
41
+ }, quota.key)), balanceMicroUsd !== null && level !== "fine" && /*#__PURE__*/_jsx("li", {
42
+ children: empty ? "Paid usage is paused until you add credits." : `${formatMicroUsd(balanceMicroUsd)} of credits left.`
43
+ })]
44
+ }), /*#__PURE__*/_jsxs("div", {
45
+ className: "flex flex-wrap items-center gap-2",
46
+ children: [!paidPlan && /*#__PURE__*/_jsx(Button, {
47
+ asChild: true,
48
+ size: "sm",
49
+ variant: upgradeFirst ? "default" : "ghost",
50
+ children: /*#__PURE__*/_jsx(Link, {
51
+ href: upgradeHref,
52
+ children: "Upgrade to Pro"
53
+ })
54
+ }), /*#__PURE__*/_jsx(Button, {
55
+ asChild: true,
56
+ size: "sm",
57
+ variant: upgradeFirst ? "ghost" : "default",
58
+ children: /*#__PURE__*/_jsx(Link, {
59
+ href: buyCreditsHref,
60
+ children: "Buy credits"
61
+ })
62
+ })]
63
+ })]
64
+ })]
65
+ });
29
66
  }
@@ -1,6 +1,23 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
1
  import { RuleGrid, RuleGridCell, RuleGridLabel } from "@locusgraph/components";
3
2
  import { Quota } from "./quota.js";
4
- export function Usage({ quotas, label = "Usage", emptyText = "No plan includes metered usage yet.", }) {
5
- return (_jsx(RuleGrid, { columns: 1, children: _jsxs(RuleGridCell, { className: "gap-5", children: [_jsx(RuleGridLabel, { children: label }), quotas.length > 0 ? (quotas.map((quota) => _jsx(Quota, { quota: quota }, quota.key))) : (_jsx("p", { className: "text-sm text-muted-foreground", children: emptyText }))] }) }));
3
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
+ export function Usage({
5
+ quotas,
6
+ label = "Usage",
7
+ emptyText = "No plan includes metered usage yet."
8
+ }) {
9
+ return /*#__PURE__*/_jsx(RuleGrid, {
10
+ columns: 1,
11
+ children: /*#__PURE__*/_jsxs(RuleGridCell, {
12
+ className: "gap-5",
13
+ children: [/*#__PURE__*/_jsx(RuleGridLabel, {
14
+ children: label
15
+ }), quotas.length > 0 ? quotas.map(quota => /*#__PURE__*/_jsx(Quota, {
16
+ quota: quota
17
+ }, quota.key)) : /*#__PURE__*/_jsx("p", {
18
+ className: "text-sm text-muted-foreground",
19
+ children: emptyText
20
+ })]
21
+ })
22
+ });
6
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@locusgraph/blocks",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Product sections built from @locusgraph/components: usage meters and alerts, wallet, credit packs, plan options and API keys.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "provenance": true
30
30
  },
31
31
  "peerDependencies": {
32
- "@locusgraph/components": "^0.2.0",
32
+ "@locusgraph/components": "^0.2.2",
33
33
  "react": "^19.2.0",
34
34
  "react-dom": "^19.2.0"
35
35
  },
@@ -38,7 +38,7 @@
38
38
  "@locusgraph/utils": "0.1.1"
39
39
  },
40
40
  "devDependencies": {
41
- "@locusgraph/components": "0.2.0",
41
+ "@locusgraph/components": "0.2.2",
42
42
  "@locusgraph/config": "0.1.1",
43
43
  "@types/node": "^22.0.0",
44
44
  "@types/react": "^19.2.0",
@@ -49,7 +49,7 @@
49
49
  "vitest": "^5.0.3"
50
50
  },
51
51
  "scripts": {
52
- "build": "rm -rf dist && tsc -p tsconfig.build.json && cp src/sources.css dist/sources.css && node ../../scripts/esm-extensions.mjs dist",
52
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && cp src/sources.css dist/sources.css && node ../../scripts/react-compiler.mjs dist && node ../../scripts/esm-extensions.mjs dist",
53
53
  "typecheck": "tsc -p tsconfig.json --noEmit",
54
54
  "test": "vitest run"
55
55
  }