refactor(01-06): decompose social.tsx and runs.tsx into focused sub-components
- social.tsx (541 lines) -> 5 files: SocialPanel, AccountCard, AddAccountDialog, ActionHistory, social-config - runs.tsx (506 lines) -> 4 files: RunCenter, RunQueue, RunDetail, run-helpers - All sub-component files under 300 lines - Backward-compatible re-export barrels preserve existing App.tsx imports
This commit is contained in:
1 parent
d77486504d
commit
93e599befd
11 files changed
+1147
-1047
No files matched your search
@@ -0,0 +1,122 @@
|
||||
import {
|
||||
ArrowSquareOut,
|
||||
ClockCounterClockwise,
|
||||
Robot,
|
||||
} from "@phosphor-icons/react";
|
||||
import type { BrowserActionRecord } from "@shared/types";
|
||||
import { timeAgo } from "../../lib/formatters";
|
||||
import { actionTypeLabels } from "./social-config";
|
||||
|
||||
interface ActionHistoryProps {
|
||||
actions: BrowserActionRecord[];
|
||||
agents: Array<{ id: string; name: string }>;
|
||||
onOpenPath: (path: string) => Promise<void>;
|
||||
onCancelAction: (actionId: string) => Promise<void>;
|
||||
onNavigate: (section: "tasks" | "approvals", entityId: string) => void;
|
||||
}
|
||||
|
||||
export function ActionHistory({ actions, agents, onOpenPath, onCancelAction, onNavigate }: ActionHistoryProps) {
|
||||
if (actions.length === 0) {
|
||||
return (
|
||||
<div className="py-8 text-center text-[13px] text-[color:var(--muted)]">
|
||||
No browser actions yet. Agents will create actions when working on social media tasks.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-1.5">
|
||||
{actions.slice(0, 30).map((action) => {
|
||||
const config = actionTypeLabels[action.actionType] ?? actionTypeLabels.custom_script;
|
||||
const Icon = config.icon;
|
||||
const agent = agents.find((a) => a.id === action.agentId);
|
||||
const isSuccess = action.status === "succeeded";
|
||||
const isFailed = action.status === "failed";
|
||||
const isRunning = action.status === "running";
|
||||
|
||||
return (
|
||||
<div
|
||||
key={action.id}
|
||||
className="flex items-center gap-3 rounded-[8px] border border-[color:var(--line)] bg-[color:var(--panel)] px-4 py-2.5 text-[12px]"
|
||||
>
|
||||
<Icon size={16} className={
|
||||
isSuccess ? "text-[color:var(--success)]"
|
||||
: isFailed ? "text-[color:var(--danger)]"
|
||||
: isRunning ? "text-[color:var(--accent)]"
|
||||
: "text-[color:var(--muted)]"
|
||||
} />
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-[color:var(--text)]">{config.label}</span>
|
||||
{agent ? (
|
||||
<span className="flex items-center gap-1 text-[color:var(--muted)]">
|
||||
<Robot size={10} />
|
||||
{agent.name}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
{action.resultSummary ? (
|
||||
<div className="mt-0.5 truncate text-[11px] text-[color:var(--muted)]">{action.resultSummary}</div>
|
||||
) : null}
|
||||
{action.errorMessage ? (
|
||||
<div className="mt-0.5 truncate text-[11px] text-[color:var(--danger)]">{action.errorMessage}</div>
|
||||
) : null}
|
||||
<div className="mt-1 flex flex-wrap items-center gap-2">
|
||||
{action.taskId ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onNavigate("tasks", action.taskId!)}
|
||||
className="rounded-[5px] bg-[color:var(--panel-soft)] px-2 py-1 text-[10px] font-medium text-[color:var(--muted-strong)] transition hover:text-[color:var(--text)]"
|
||||
>
|
||||
Open task
|
||||
</button>
|
||||
) : null}
|
||||
{action.approvalId ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onNavigate("approvals", action.approvalId!)}
|
||||
className="rounded-[5px] bg-[color:var(--panel-soft)] px-2 py-1 text-[10px] font-medium text-[color:var(--muted-strong)] transition hover:text-[color:var(--text)]"
|
||||
>
|
||||
Open approval
|
||||
</button>
|
||||
) : null}
|
||||
{action.screenshotPath ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void onOpenPath(action.screenshotPath!)}
|
||||
className="inline-flex items-center gap-1 rounded-[5px] bg-[color:var(--panel-soft)] px-2 py-1 text-[10px] font-medium text-[color:var(--muted-strong)] transition hover:text-[color:var(--text)]"
|
||||
>
|
||||
<ArrowSquareOut size={10} />
|
||||
Screenshot
|
||||
</button>
|
||||
) : null}
|
||||
{["queued", "running", "approval_required"].includes(action.status) ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void onCancelAction(action.id)}
|
||||
className="inline-flex items-center gap-1 rounded-[5px] bg-[color:var(--danger-soft)] px-2 py-1 text-[10px] font-medium text-[color:var(--danger)] transition hover:brightness-95"
|
||||
>
|
||||
<ClockCounterClockwise size={10} />
|
||||
Cancel
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="shrink-0 text-[10px] text-[color:var(--muted)]">
|
||||
{timeAgo(action.createdAt)}
|
||||
</div>
|
||||
<span className={`shrink-0 rounded-full px-2 py-0.5 text-[9px] font-bold ${
|
||||
isSuccess ? "bg-[color:var(--success-soft)] text-[color:var(--success)]"
|
||||
: isFailed ? "bg-[color:var(--danger-soft)] text-[color:var(--danger)]"
|
||||
: isRunning ? "bg-[color:var(--accent-soft)] text-[color:var(--accent)]"
|
||||
: action.status === "approval_required" ? "bg-[color:var(--warn-soft)] text-[color:var(--warn)]"
|
||||
: "bg-[color:var(--panel-soft)] text-[color:var(--muted)]"
|
||||
}`}>
|
||||
{action.status.replace("_", " ")}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user