diff --git a/src/renderer/components/automation/AutomationPanel.tsx b/src/renderer/components/automation/AutomationPanel.tsx index c623e058..c91a7a70 100644 --- a/src/renderer/components/automation/AutomationPanel.tsx +++ b/src/renderer/components/automation/AutomationPanel.tsx @@ -4,7 +4,7 @@ import { ArrowRight, ArrowsClockwise, Lightning, Plus, Robot, X } from "@phospho import type { AutomationAction, AutomationRuleRecord, AutomationStatus, AutomationTrigger, WorkflowPipelineRecord } from "@shared/types" import { ActionButton, ConfirmDialog } from "../ui" import { useT } from "../../i18n" -import { ACCENT, LINE, triggerConfig, actionConfig, type AutomationLogEntry } from "./automation-config" +import { triggerConfig, actionConfig, type AutomationLogEntry } from "./automation-config" import { timeAgo } from "../../lib/formatters" import { RuleCard } from "./RuleCard" import { RuleFormModal } from "./RuleEditor" @@ -16,7 +16,7 @@ function AutomationActivityLog({ entries }: { entries: AutomationLogEntry[] }) { return (
{entries.map((entry, idx) => { const ti = triggerConfig[entry.trigger as AutomationTrigger]; const ai = actionConfig[entry.action as AutomationAction]; const TIcon = ti?.icon ?? Lightning - return
{entry.ruleName}
{ti?.label ?? entry.trigger}{ai?.label ?? entry.action}
{timeAgo(entry.executedAt)}
+ return
{entry.ruleName}
{ti?.label ?? entry.trigger}{ai?.label ?? entry.action}
{timeAgo(entry.executedAt)}
})}
) } @@ -35,12 +35,12 @@ function RulesPanel({ rules, companyId, onRefresh }: { rules: AutomationRuleReco return (
{actionError ?
{actionError}
: null} -

Automation Rules

{counts.active} active, {counts.paused} paused{counts.error > 0 ? `, ${counts.error} errored` : ""}

setShowCreate(true)} icon={Plus} tone="accent" />
- {rules.length > 0 ?
{[{ l: "Total Rules", v: counts.total, c: undefined }, { l: "Total Executions", v: rules.reduce((s, r) => s + r.executionCount, 0), c: ACCENT }, { l: "Recent Activity", v: actLog.length, c: "var(--primary)" }].map((s) =>
{s.l}
{s.v}
)}
: null} +

Automation Rules

{counts.active} active, {counts.paused} paused{counts.error > 0 ? `, ${counts.error} errored` : ""}

setShowCreate(true)} icon={Plus} tone="accent" />
+ {rules.length > 0 ?
{[{ l: "Total Rules", v: counts.total, accent: false }, { l: "Total Executions", v: rules.reduce((s, r) => s + r.executionCount, 0), accent: true }, { l: "Recent Activity", v: actLog.length, accent: true }].map((s) =>
{s.l}
{s.v}
)}
: null}
{activeTab === "rules" ? <> - {rules.length > 0 ?
{(["all","active","paused","error"] as const).map((s) => )}
: null} - {filtered.length === 0 && rules.length === 0 ?
No automation rules
Create your first rule to make the company autonomous.
setShowCreate(true)} icon={Lightning} tone="accent" />
+ {rules.length > 0 ?
{(["all","active","paused","error"] as const).map((s) => )}
: null} + {filtered.length === 0 && rules.length === 0 ?
No automation rules
Create your first rule to make the company autonomous.
setShowCreate(true)} icon={Lightning} tone="accent" />
: filtered.length === 0 ?
No rules match the current filter.
:
{filtered.map((r) => void handleToggle(r)} onDelete={() => setDelTarget(r)} onEdit={() => setEditRule(r)} />)}
} : } @@ -59,8 +59,8 @@ function WorkflowsPanel({ workflows, companyId, onRefresh }: { workflows: Workfl return (
{actionError ?
{actionError}
: null} -

Workflow Pipelines

{counts.active} active, {counts.draft} draft{counts.total > counts.active + counts.draft ? `, ${counts.total - counts.active - counts.draft} other` : ""}

setShowCreate(true)} icon={Plus} tone="accent" />
- {workflows.length === 0 ?
No workflow pipelines
Workflows chain actions into multi-step pipelines.
setShowCreate(true)} icon={ArrowsClockwise} tone="accent" />
+

Workflow Pipelines

{counts.active} active, {counts.draft} draft{counts.total > counts.active + counts.draft ? `, ${counts.total - counts.active - counts.draft} other` : ""}

setShowCreate(true)} icon={Plus} tone="accent" />
+ {workflows.length === 0 ?
No workflow pipelines
Workflows chain actions into multi-step pipelines.
setShowCreate(true)} icon={ArrowsClockwise} tone="accent" />
:
{workflows.map((wf) => setDelTarget(wf)} onEdit={() => setEditWf(wf)} onRun={() => void handleRun(wf)} />)}
} {showCreate ? setShowCreate(false)} onSaved={onRefresh} /> : null}{editWf ? setEditWf(null)} onSaved={onRefresh} /> : null} void handleDelete()} onCancel={() => setDelTarget(null)} /> @@ -72,10 +72,10 @@ export function AutomationPanel({ rules, workflows, companyId, onRefresh }: { ru const t = useT(); const [activeSection, setActiveSection] = useState<"rules"|"workflows">("rules") return (
-
{t("auto.automationEngine")}
Rules trigger actions based on company events. Workflows chain multiple steps into pipelines. Together, they let your AI company run fully autonomously.
+
{t("auto.automationEngine")}
Rules trigger actions based on company events. Workflows chain multiple steps into pipelines. Together, they let your AI company run fully autonomously.
- - + +
{activeSection === "rules" ? : }
diff --git a/src/renderer/components/automation/RuleCard.tsx b/src/renderer/components/automation/RuleCard.tsx index d10c61dd..ed016ab3 100644 --- a/src/renderer/components/automation/RuleCard.tsx +++ b/src/renderer/components/automation/RuleCard.tsx @@ -2,16 +2,16 @@ import { motion } from "framer-motion" import { ArrowRight, CaretRight, Gear, Lightning, Pause, Play, Trash } from "@phosphor-icons/react" import type { AutomationRuleRecord } from "@shared/types" import { timeAgo } from "../../lib/formatters" -import { ACCENT, LINE, triggerConfig, actionConfig, statusColors } from "./automation-config" +import { triggerConfig, actionConfig, statusColors } from "./automation-config" export function RuleCard({ rule, onToggle, onDelete, onEdit }: { rule: AutomationRuleRecord; onToggle: () => void; onDelete: () => void; onEdit: () => void }) { const triggerInfo = triggerConfig[rule.trigger]; const actionInfo = actionConfig[rule.action] const TriggerIcon = triggerInfo?.icon ?? Lightning; const colors = statusColors[rule.status] return ( - +
-
+
- {expanded ?
{workflow.description ?

{workflow.description}

: null}
: null} + {expanded ?
{workflow.description ?

{workflow.description}

: null}
: null}
) diff --git a/src/renderer/components/automation/automation-config.ts b/src/renderer/components/automation/automation-config.ts index 9cb07032..1b467eb1 100644 --- a/src/renderer/components/automation/automation-config.ts +++ b/src/renderer/components/automation/automation-config.ts @@ -2,12 +2,6 @@ import type { AutomationAction, AutomationStatus, AutomationTrigger, WorkflowSta import { ArrowsClockwise, Eye, Funnel, Lightning, Pause, Play, Plus, Robot, Timer, Trash } from "@phosphor-icons/react" import { DEPARTMENT_OPTIONS } from "@shared/constants" -// ACCENT/LINE used in template literals for opacity suffixes like `${ACCENT}15` -// Must be hex values (not CSS vars) so hex alpha notation works. -// These are the primary/border colors from the OKLCH design tokens converted to hex. -export const ACCENT = "#343434" -export const LINE = "#ebebeb" - export const triggerConfig: Record = { task_status_changed: { label: "Task Status Changed", description: "When a task transitions to a new status", icon: ArrowsClockwise }, task_created: { label: "Task Created", description: "When a new task is added to the system", icon: Plus }, @@ -63,7 +57,7 @@ export const workflowStatusColors: Record = { pending: { bg: "var(--muted-foreground)", border: "var(--border)", text: "var(--muted-foreground)" }, - running: { bg: "var(--primary)", border: ACCENT, text: ACCENT }, + running: { bg: "var(--primary)", border: "var(--primary)", text: "var(--primary)" }, completed: { bg: "var(--muted-foreground)", border: "var(--primary)", text: "var(--primary)" }, failed: { bg: "var(--destructive)", border: "var(--destructive)", text: "var(--destructive)" }, skipped: { bg: "var(--muted-foreground)", border: "var(--border)", text: "var(--muted-foreground)" },