@locusgraph/blocks 0.0.0-stage → 0.1.1
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.
- package/LICENSE +21 -0
- package/README.md +33 -2
- package/dist/billing/credit-packs.d.ts +9 -0
- package/dist/billing/credit-packs.js +6 -0
- package/dist/billing/plan-options.d.ts +8 -0
- package/dist/billing/plan-options.js +8 -0
- package/dist/billing/wallet-panel.d.ts +8 -0
- package/dist/billing/wallet-panel.js +16 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/keys/keys-table.d.ts +8 -0
- package/dist/keys/keys-table.js +6 -0
- package/dist/keys/no-keys.d.ts +8 -0
- package/dist/keys/no-keys.js +8 -0
- package/dist/sources.css +1 -0
- package/dist/types.d.ts +34 -0
- package/dist/types.js +3 -0
- package/dist/usage/quota-meter.d.ts +4 -0
- package/dist/usage/quota-meter.js +13 -0
- package/dist/usage/usage-attention.d.ts +11 -0
- package/dist/usage/usage-attention.js +20 -0
- package/dist/usage/usage-summary.d.ts +8 -0
- package/dist/usage/usage-summary.js +6 -0
- package/package.json +53 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LocusGraph
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @locusgraph/blocks
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Product sections built from `@locusgraph/components` and its Rule Grid: the same usage, billing and API-key screens in every LocusGraph app. Blocks take plain data and callbacks; they never fetch.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm add @locusgraph/blocks @locusgraph/components
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
@import "@locusgraph/components/theme.css";
|
|
11
|
+
@import "@locusgraph/blocks/sources.css";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Block | Props |
|
|
15
|
+
|---|---|
|
|
16
|
+
| `UsageSummary` | `quotas`, optional `label`, `emptyText` |
|
|
17
|
+
| `QuotaMeter` | `quota` (`key`, `period`, `used`, `included`) |
|
|
18
|
+
| `UsageAttention` | `balanceMicroUsd`, `quotas`, `paidPlan`, `buyCreditsHref`, `upgradeHref`, `linkComponent`. Renders nothing when credits are fine and no quota is over |
|
|
19
|
+
| `WalletPanel` | `balanceMicroUsd`, `debtMicroUsd`, `grants`. A Rule Grid cell |
|
|
20
|
+
| `CreditPacks` | `packs`, `onBuy(packId)`, `pending`. A Rule Grid cell |
|
|
21
|
+
| `PlanOptions` | `plans`, `actionFor(plan)` for plans that are not current |
|
|
22
|
+
| `KeysTable` | `keys`, `actions(key)` |
|
|
23
|
+
| `NoKeys` | `newKeyHref` (omit for read-only roles), `linkComponent` |
|
|
24
|
+
|
|
25
|
+
`WalletPanel` and `CreditPacks` are cells, so put them in a `RuleGrid`:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<RuleGrid columns={2}>
|
|
29
|
+
<WalletPanel balanceMicroUsd={wallet.balanceMicroUsd} grants={grants} />
|
|
30
|
+
<CreditPacks packs={wallet.packs} onBuy={buy} pending={pending} />
|
|
31
|
+
</RuleGrid>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Pass your router's link as `linkComponent` (for example Next.js `Link`); plain `<a>` is the default.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CreditPack } from "../types.js";
|
|
2
|
+
type CreditPacksProps = {
|
|
3
|
+
packs: CreditPack[];
|
|
4
|
+
onBuy: (packId: string) => void;
|
|
5
|
+
pending?: boolean;
|
|
6
|
+
description?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function CreditPacks({ packs, onBuy, pending, description, }: CreditPacksProps): import("react").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, RuleGridCell, RuleGridDescription, RuleGridLabel } from "@locusgraph/components";
|
|
3
|
+
import { centsToMicroUsd, formatCents, formatMicroUsd } from "@locusgraph/utils";
|
|
4
|
+
export function CreditPacks({ packs, onBuy, pending = false, description = "Pay once with Stripe. Credits never expire and never renew on their own.", }) {
|
|
5
|
+
return (_jsxs(RuleGridCell, { className: "gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1.5", 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: "outline", disabled: pending, onClick: () => onBuy(pack.id), children: [_jsx("span", { className: "tabular-nums", children: formatMicroUsd(pack.amountMicroUsd) }), centsToMicroUsd(pack.priceCents) !== pack.amountMicroUsd && (_jsxs("span", { className: "text-muted-foreground tabular-nums", children: ["for ", formatCents(pack.priceCents)] }))] }, pack.id))) })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import type { PlanOffer } from "../types.js";
|
|
3
|
+
type PlanOptionsProps = {
|
|
4
|
+
plans: PlanOffer[];
|
|
5
|
+
actionFor?: (plan: PlanOffer) => React.ReactNode;
|
|
6
|
+
};
|
|
7
|
+
export declare function PlanOptions({ plans, actionFor }: PlanOptionsProps): React.JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, RuleGrid, RuleGridCell, RuleGridDescription, RuleGridTitle, } from "@locusgraph/components";
|
|
3
|
+
export function PlanOptions({ plans, actionFor }) {
|
|
4
|
+
return (_jsx(RuleGrid, { columns: plans.length >= 3 ? 3 : 2, children: plans.map((plan) => {
|
|
5
|
+
const action = plan.current ? null : actionFor?.(plan);
|
|
6
|
+
return (_jsxs(RuleGridCell, { "data-current": plan.current || undefined, className: "gap-4", children: [_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsxs(RuleGridTitle, { className: "flex items-center gap-2", children: [plan.name, plan.current && _jsx(Badge, { variant: "secondary", children: "Current" })] }), _jsx(RuleGridDescription, { children: plan.price })] }), _jsx("ul", { className: "flex flex-col gap-1.5 text-sm text-muted-foreground", children: plan.points.map((point) => (_jsx("li", { children: point }, point))) }), action && _jsx("div", { className: "mt-auto", children: action })] }, plan.name));
|
|
7
|
+
}) }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { WalletGrant } from "../types.js";
|
|
2
|
+
type WalletPanelProps = {
|
|
3
|
+
balanceMicroUsd: number;
|
|
4
|
+
debtMicroUsd?: number;
|
|
5
|
+
grants: WalletGrant[];
|
|
6
|
+
};
|
|
7
|
+
export declare function WalletPanel({ balanceMicroUsd, debtMicroUsd, grants }: WalletPanelProps): import("react").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Badge, RuleGridCell, RuleGridLabel } from "@locusgraph/components";
|
|
3
|
+
import { balanceLevel, formatDate, formatMicroUsd } from "@locusgraph/utils";
|
|
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
|
+
},
|
|
11
|
+
};
|
|
12
|
+
export function WalletPanel({ balanceMicroUsd, debtMicroUsd = 0, grants }) {
|
|
13
|
+
const level = balanceLevel(balanceMicroUsd);
|
|
14
|
+
const hint = level === "fine" ? null : levelHint[level];
|
|
15
|
+
return (_jsxs(RuleGridCell, { "data-level": level, className: "gap-4", children: [_jsxs("span", { className: "flex 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", children: formatMicroUsd(balanceMicroUsd) }), hint && _jsx("p", { className: `text-sm ${hint.tone}`, children: hint.text })] }), _jsxs("div", { className: "flex flex-col gap-3 text-sm", children: [grants.length > 0 ? (_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: "text-muted-foreground tabular-nums", children: [formatMicroUsd(grant.remainingMicroUsd), " \u00B7", " ", grant.expiresAt ? `expires ${formatDate(grant.expiresAt)}` : "never expires"] })] }, `${grant.label}:${grant.expiresAt}`))) })) : (_jsx("p", { className: "text-muted-foreground", children: "No credits yet. Buy a pack to keep using paid features." })), debtMicroUsd > 0 && (_jsxs("p", { className: "text-destructive", children: [formatMicroUsd(debtMicroUsd), " owed. It comes out of the next credits you add."] }))] })] }));
|
|
16
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { CreditPacks } from "./billing/credit-packs.js";
|
|
2
|
+
export { PlanOptions } from "./billing/plan-options.js";
|
|
3
|
+
export { WalletPanel } from "./billing/wallet-panel.js";
|
|
4
|
+
export { KeysTable } from "./keys/keys-table.js";
|
|
5
|
+
export { NoKeys } from "./keys/no-keys.js";
|
|
6
|
+
export * from "./types.js";
|
|
7
|
+
export { QuotaMeter } from "./usage/quota-meter.js";
|
|
8
|
+
export { UsageAttention } from "./usage/usage-attention.js";
|
|
9
|
+
export { UsageSummary } from "./usage/usage-summary.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { CreditPacks } from "./billing/credit-packs.js";
|
|
2
|
+
export { PlanOptions } from "./billing/plan-options.js";
|
|
3
|
+
export { WalletPanel } from "./billing/wallet-panel.js";
|
|
4
|
+
export { KeysTable } from "./keys/keys-table.js";
|
|
5
|
+
export { NoKeys } from "./keys/no-keys.js";
|
|
6
|
+
export * from "./types.js";
|
|
7
|
+
export { QuotaMeter } from "./usage/quota-meter.js";
|
|
8
|
+
export { UsageAttention } from "./usage/usage-attention.js";
|
|
9
|
+
export { UsageSummary } from "./usage/usage-summary.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import type { ApiKeyRow } from "../types.js";
|
|
3
|
+
type KeysTableProps = {
|
|
4
|
+
keys: ApiKeyRow[];
|
|
5
|
+
actions?: (key: ApiKeyRow) => React.ReactNode;
|
|
6
|
+
};
|
|
7
|
+
export declare function KeysTable({ keys, actions }: KeysTableProps): React.JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@locusgraph/components";
|
|
3
|
+
import { formatDate } from "@locusgraph/utils";
|
|
4
|
+
export function KeysTable({ keys, actions }) {
|
|
5
|
+
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 sm:table-cell", children: "Created" }), _jsx(TableHead, { children: "Expires" }), actions && _jsx(TableHead, { className: "w-0" })] }) }), _jsx(TableBody, { children: keys.map((key) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: key.name ?? "Untitled" }), _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 sm:table-cell", children: formatDate(key.createdAt) }), _jsx(TableCell, { children: formatDate(key.expiresAt, "Never") }), actions && _jsx(TableCell, { className: "text-right", children: actions(key) })] }, key.id))) })] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LinkComponent } from "../types.js";
|
|
2
|
+
type NoKeysProps = {
|
|
3
|
+
newKeyHref?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
linkComponent?: LinkComponent | "a";
|
|
6
|
+
};
|
|
7
|
+
export declare function NoKeys({ newKeyHref, description, linkComponent: Link, }: NoKeysProps): import("react").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, RuleGrid, RuleGridCell, RuleGridDescription, RuleGridLabel, } from "@locusgraph/components";
|
|
3
|
+
import { KeyRoundIcon, PlusIcon } from "lucide-react";
|
|
4
|
+
export function NoKeys({ 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-3 py-12 text-center", children: [_jsx(KeyRoundIcon, { className: "size-5 text-muted-foreground" }), _jsx(RuleGridLabel, { children: "No API keys yet" }), _jsx(RuleGridDescription, { className: "max-w-sm", children: description }), newKeyHref && (_jsx(Button, { asChild: true, size: "sm", children: _jsxs(Link, { href: newKeyHref, children: [_jsx(PlusIcon, {}), "New key"] }) }))] }) }));
|
|
8
|
+
}
|
package/dist/sources.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@source "./";
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type { LinkComponent } from "@locusgraph/components";
|
|
2
|
+
export type QuotaPeriod = "month" | "lifetime";
|
|
3
|
+
export type Quota = {
|
|
4
|
+
key: string;
|
|
5
|
+
period: QuotaPeriod;
|
|
6
|
+
used: number;
|
|
7
|
+
included: number;
|
|
8
|
+
};
|
|
9
|
+
export type CreditPack = {
|
|
10
|
+
id: string;
|
|
11
|
+
priceCents: number;
|
|
12
|
+
amountMicroUsd: number;
|
|
13
|
+
};
|
|
14
|
+
export type WalletGrant = {
|
|
15
|
+
label: string;
|
|
16
|
+
remainingMicroUsd: number;
|
|
17
|
+
expiresAt: string | null;
|
|
18
|
+
};
|
|
19
|
+
export type PlanOffer = {
|
|
20
|
+
name: string;
|
|
21
|
+
price: string;
|
|
22
|
+
points: string[];
|
|
23
|
+
current: boolean;
|
|
24
|
+
};
|
|
25
|
+
export type ApiKeyRow = {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string | null;
|
|
28
|
+
maskedKey: string;
|
|
29
|
+
productLabel: string;
|
|
30
|
+
createdBy: string;
|
|
31
|
+
createdAt: string | null;
|
|
32
|
+
expiresAt: string | null;
|
|
33
|
+
};
|
|
34
|
+
export declare function periodLabel(period: QuotaPeriod): string;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Progress } from "@locusgraph/components";
|
|
3
|
+
import { formatCount, quotaPercent, quotaStatus } from "@locusgraph/utils";
|
|
4
|
+
import { periodLabel } from "../types.js";
|
|
5
|
+
const statusIndicator = {
|
|
6
|
+
ok: "",
|
|
7
|
+
near: "[&_[data-slot=progress-indicator]]:bg-warn",
|
|
8
|
+
over: "[&_[data-slot=progress-indicator]]:bg-destructive",
|
|
9
|
+
};
|
|
10
|
+
export function QuotaMeter({ quota }) {
|
|
11
|
+
const percent = quotaPercent(quota);
|
|
12
|
+
return (_jsxs("div", { "data-slot": "quota-meter", "data-status": quotaStatus(quota), className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-4 text-sm", children: [_jsx("span", { className: "font-mono", children: quota.key }), _jsxs("span", { className: "text-muted-foreground tabular-nums", children: [formatCount(quota.used), " of ", formatCount(quota.included), " ", periodLabel(quota.period)] })] }), _jsx(Progress, { value: percent, "aria-label": `${quota.key} ${percent}% of quota`, className: statusIndicator[quotaStatus(quota)] })] }));
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type LinkComponent, type Quota } from "../types.js";
|
|
2
|
+
type UsageAttentionProps = {
|
|
3
|
+
balanceMicroUsd: number | null;
|
|
4
|
+
quotas: Quota[];
|
|
5
|
+
paidPlan: boolean;
|
|
6
|
+
buyCreditsHref: string;
|
|
7
|
+
upgradeHref: string;
|
|
8
|
+
linkComponent?: LinkComponent | "a";
|
|
9
|
+
};
|
|
10
|
+
export declare function UsageAttention({ balanceMicroUsd, quotas, paidPlan, buyCreditsHref, upgradeHref, linkComponent: Link, }: UsageAttentionProps): import("react").JSX.Element | null;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Alert, AlertDescription, AlertTitle, Button } from "@locusgraph/components";
|
|
3
|
+
import { balanceLevel, formatCount, formatMicroUsd, quotaStatus } from "@locusgraph/utils";
|
|
4
|
+
import { CircleAlertIcon } from "lucide-react";
|
|
5
|
+
import { periodLabel } from "../types.js";
|
|
6
|
+
function attentionTitle(empty, overCount) {
|
|
7
|
+
if (empty)
|
|
8
|
+
return "Out of credits";
|
|
9
|
+
return overCount > 0 ? "Usage is over your plan" : "Credits are running low";
|
|
10
|
+
}
|
|
11
|
+
export function UsageAttention({ balanceMicroUsd, quotas, paidPlan, buyCreditsHref, upgradeHref, linkComponent: Link = "a", }) {
|
|
12
|
+
const level = balanceMicroUsd === null ? "fine" : balanceLevel(balanceMicroUsd);
|
|
13
|
+
const over = quotas.filter((quota) => quotaStatus(quota) === "over");
|
|
14
|
+
if (level === "fine" && over.length === 0)
|
|
15
|
+
return null;
|
|
16
|
+
const empty = level === "empty";
|
|
17
|
+
return (_jsxs(Alert, { variant: empty ? "destructive" : "default", className: empty ? undefined : "text-warn *:data-[slot=alert-description]:text-warn/85", children: [_jsx(CircleAlertIcon, {}), _jsx(AlertTitle, { children: attentionTitle(empty, over.length) }), _jsxs(AlertDescription, { className: "flex flex-wrap items-end justify-between gap-4", children: [_jsxs("ul", { className: "flex flex-col gap-1", children: [over.map((quota) => (_jsxs("li", { children: [_jsx("span", { className: "font-mono", children: quota.key }), " is at ", formatCount(quota.used), " of", " ", formatCount(quota.included), " ", periodLabel(quota.period), "."] }, quota.key))), balanceMicroUsd !== null && level !== "fine" && (_jsx("li", { children: empty
|
|
18
|
+
? "Paid usage is paused until you add credits."
|
|
19
|
+
: `${formatMicroUsd(balanceMicroUsd)} of credits left.` }))] }), _jsxs("div", { className: "flex items-center gap-4", children: [!paidPlan && (_jsx(Link, { href: upgradeHref, className: "text-sm text-foreground underline-offset-4 hover:underline", children: "Upgrade to Pro" })), _jsx(Button, { asChild: true, size: "sm", children: _jsx(Link, { href: buyCreditsHref, children: "Buy credits" }) })] })] })] }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Quota } from "../types.js";
|
|
2
|
+
type UsageSummaryProps = {
|
|
3
|
+
quotas: Quota[];
|
|
4
|
+
label?: string;
|
|
5
|
+
emptyText?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function UsageSummary({ quotas, label, emptyText, }: UsageSummaryProps): import("react").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { RuleGrid, RuleGridCell, RuleGridLabel } from "@locusgraph/components";
|
|
3
|
+
import { QuotaMeter } from "./quota-meter.js";
|
|
4
|
+
export function UsageSummary({ 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(QuotaMeter, { quota: quota }, quota.key))) : (_jsx("p", { className: "text-sm text-muted-foreground", children: emptyText }))] }) }));
|
|
6
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusgraph/blocks",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Product sections built from @locusgraph/components: usage meters and alerts, wallet, credit packs, plan options and API keys.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/locusgraph/design.git",
|
|
9
|
+
"directory": "packages/blocks"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://design.locusgraph.com",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"sideEffects": [
|
|
14
|
+
"**/*.css"
|
|
15
|
+
],
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./sources.css": "./dist/sources.css"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"registry": "https://registry.npmjs.org",
|
|
28
|
+
"access": "public",
|
|
29
|
+
"provenance": true
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@locusgraph/components": "^0.1.1",
|
|
33
|
+
"react": "^19.2.0",
|
|
34
|
+
"react-dom": "^19.2.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@locusgraph/utils": "0.1.1",
|
|
38
|
+
"lucide-react": "^1.28.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@locusgraph/components": "0.1.1",
|
|
42
|
+
"@locusgraph/config": "0.1.0",
|
|
43
|
+
"@types/node": "^22.0.0",
|
|
44
|
+
"@types/react": "^19.2.0",
|
|
45
|
+
"@types/react-dom": "^19.2.0",
|
|
46
|
+
"react": "19.2.8",
|
|
47
|
+
"react-dom": "19.2.8",
|
|
48
|
+
"typescript": "^7.0.2",
|
|
49
|
+
"vitest": "^5.0.3"
|
|
50
|
+
},
|
|
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",
|
|
53
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
54
|
+
"test": "vitest run"
|
|
55
|
+
}
|
|
6
56
|
}
|