refactor(ui): promote BudgetBar and InlineConfirmDelete to shared ui/
BudgetBar and ConfirmDelete were only sitting in organization/helpers.tsx but have no organization-specific behaviour. Move them under components/ui/ and keep the old exports as re-exports so existing importers continue to resolve. HeartbeatSection stays in helpers.tsx because it owns agent-specific heartbeat IPC.
This commit is contained in:
1 parent
61aba41e77
commit
dd007760b8
4 files changed
+51
-39
No files matched your search
@@ -1,44 +1,13 @@
|
|||||||
import { Heartbeat, Lightning, Trash, XCircle } from "@phosphor-icons/react";
|
import { Heartbeat, Lightning } from "@phosphor-icons/react";
|
||||||
import type { AgentRecord } from "@shared/types";
|
import type { AgentRecord } from "@shared/types";
|
||||||
import { ActionButton, Select } from "../ui";
|
import { ActionButton, BudgetBar, InlineConfirmDelete, Select } from "../ui";
|
||||||
import { formatMoney, formatTime } from "../../lib/formatters";
|
import { formatTime } from "../../lib/formatters";
|
||||||
import { unwrap } from "../../lib/desktop";
|
import { unwrap } from "../../lib/desktop";
|
||||||
|
|
||||||
export function BudgetBar({ spent, budget }: { spent: number; budget: number }) {
|
// Re-exports kept so existing importers continue to work while BudgetBar and
|
||||||
const pct = budget > 0 ? Math.min((spent / budget) * 100, 100) : 0;
|
// the inline-delete confirmation live in the shared UI library.
|
||||||
const overBudget = spent > budget && budget > 0;
|
export { BudgetBar };
|
||||||
return (
|
export const ConfirmDelete = InlineConfirmDelete;
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<div className="h-1.5 w-20 overflow-hidden rounded-full bg-[color:var(--border)]">
|
|
||||||
<div
|
|
||||||
className={`h-full rounded-full transition-all ${overBudget ? "bg-destructive" : "bg-success"}`}
|
|
||||||
style={{ width: `${pct}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className={`text-[11px] ${overBudget ? "text-destructive" : "text-muted-foreground"}`}>
|
|
||||||
{formatMoney(spent)} / {formatMoney(budget)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ConfirmDelete({
|
|
||||||
entityLabel,
|
|
||||||
onConfirm,
|
|
||||||
onCancel,
|
|
||||||
}: {
|
|
||||||
entityLabel: string;
|
|
||||||
onConfirm: () => void;
|
|
||||||
onCancel: () => void;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-3 rounded-lg border border-[color:var(--destructive)] bg-destructive-muted px-4 py-3">
|
|
||||||
<span className="text-[13px] text-destructive">Delete {entityLabel}?</span>
|
|
||||||
<ActionButton label="Yes, delete" tone="danger" onClick={onConfirm} icon={Trash} />
|
|
||||||
<ActionButton label="Cancel" onClick={onCancel} icon={XCircle} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const HEARTBEAT_INTERVALS = [
|
const HEARTBEAT_INTERVALS = [
|
||||||
{ value: "30", label: "30 seconds" },
|
{ value: "30", label: "30 seconds" },
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { formatMoney } from "../../lib/formatters";
|
||||||
|
|
||||||
|
export function BudgetBar({ spent, budget }: { spent: number; budget: number }) {
|
||||||
|
const pct = budget > 0 ? Math.min((spent / budget) * 100, 100) : 0;
|
||||||
|
const overBudget = spent > budget && budget > 0;
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="h-1.5 w-20 overflow-hidden rounded-full bg-[color:var(--border)]">
|
||||||
|
<div
|
||||||
|
className={`h-full rounded-full transition-all ${overBudget ? "bg-destructive" : "bg-success"}`}
|
||||||
|
style={{ width: `${pct}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className={`text-[11px] ${overBudget ? "text-destructive" : "text-muted-foreground"}`}>
|
||||||
|
{formatMoney(spent)} / {formatMoney(budget)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, type ReactNode } from "react";
|
import { useEffect, type ReactNode } from "react";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
|
import { Trash, XCircle } from "@phosphor-icons/react";
|
||||||
import { ActionButton } from "./primitives";
|
import { ActionButton } from "./primitives";
|
||||||
import { useFocusTrap } from "./hooks";
|
import { useFocusTrap } from "./hooks";
|
||||||
|
|
||||||
@@ -137,6 +138,28 @@ export function ConfirmDialog({ open, title, message, errorMessage, onConfirm, o
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* InlineConfirmDelete */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
export function InlineConfirmDelete({
|
||||||
|
entityLabel,
|
||||||
|
onConfirm,
|
||||||
|
onCancel,
|
||||||
|
}: {
|
||||||
|
entityLabel: string;
|
||||||
|
onConfirm: () => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-3 rounded-lg border border-[color:var(--destructive)] bg-destructive-muted px-4 py-3">
|
||||||
|
<span className="text-[13px] text-destructive">Delete {entityLabel}?</span>
|
||||||
|
<ActionButton label="Yes, delete" tone="danger" onClick={onConfirm} icon={Trash} />
|
||||||
|
<ActionButton label="Cancel" onClick={onCancel} icon={XCircle} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
/* EmptyState */
|
/* EmptyState */
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export { Badge, SectionButton, Panel, Field, Input, TextArea, Select, ActionButton, Spinner, Eyebrow, Card } from "./primitives";
|
export { Badge, SectionButton, Panel, Field, Input, TextArea, Select, ActionButton, Spinner, Eyebrow, Card } from "./primitives";
|
||||||
export { StatusPill, InlineNotice, ConfirmDialog, EmptyState } from "./feedback";
|
export { StatusPill, InlineNotice, ConfirmDialog, InlineConfirmDelete, EmptyState } from "./feedback";
|
||||||
|
export { BudgetBar } from "./BudgetBar";
|
||||||
export { Modal, ListFrame, ListRow } from "./layout";
|
export { Modal, ListFrame, ListRow } from "./layout";
|
||||||
export { useFocusTrap, useReducedMotion, ErrorBoundary } from "./hooks";
|
export { useFocusTrap, useReducedMotion, ErrorBoundary } from "./hooks";
|
||||||
export { AttentionRow } from "./AttentionRow";
|
export { AttentionRow } from "./AttentionRow";
|
||||||
|
|||||||
Reference in new issue
Block a user