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 (
{actionError ?
{actionError} setActionError(null)} className="ml-2 rounded p-0.5 hover:bg-destructive hover:text-primary-foreground">
: 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) =>
)}
: 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) =>
)}
: null}
setActiveTab("rules")} className={`flex-1 rounded-lg px-3 py-1.5 text-[12px] font-medium transition ${activeTab === "rules" ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`}>Rules ({rules.length}) setActiveTab("activity")} className={`flex-1 rounded-lg px-3 py-1.5 text-[12px] font-medium transition ${activeTab === "activity" ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`}>Activity Log ({actLog.length})
{activeTab === "rules" ? <>
- {rules.length > 0 ?
{(["all","active","paused","error"] as const).map((s) => setFilterStatus(s)} className={`rounded-full px-3 py-1.5 text-[11px] font-medium transition ${filterStatus === s ? "text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-[color:var(--border)]"}`} style={filterStatus === s ? { backgroundColor: ACCENT } : undefined}>{s === "all" ? "All" : s.charAt(0).toUpperCase() + s.slice(1)} )}
: 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) => setFilterStatus(s)} className={`rounded-full px-3 py-1.5 text-[11px] font-medium transition ${filterStatus === s ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-[color:var(--border)]"}`}>{s === "all" ? "All" : s.charAt(0).toUpperCase() + s.slice(1)} )}
: 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} setActionError(null)} className="ml-2 rounded p-0.5 hover:bg-destructive hover:text-primary-foreground">
: 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.
-
setActiveSection("rules")} className={`flex flex-1 items-center justify-center gap-2 rounded-lg px-4 py-2 text-[13px] font-medium transition ${activeSection === "rules" ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`}> Automation Rules ({rules.length})
-
setActiveSection("workflows")} className={`flex flex-1 items-center justify-center gap-2 rounded-lg px-4 py-2 text-[13px] font-medium transition ${activeSection === "workflows" ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`}> Workflows ({workflows.length})
+
setActiveSection("rules")} className={`flex flex-1 items-center justify-center gap-2 rounded-lg px-4 py-2 text-[13px] font-medium transition ${activeSection === "rules" ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`}> Automation Rules ({rules.length})
+
setActiveSection("workflows")} className={`flex flex-1 items-center justify-center gap-2 rounded-lg px-4 py-2 text-[13px] font-medium transition ${activeSection === "workflows" ? "bg-card text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`}> Workflows ({workflows.length})
{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 (
-
+
-
+
{rule.name}
diff --git a/src/renderer/components/automation/WorkflowList.tsx b/src/renderer/components/automation/WorkflowList.tsx
index 062fa44d..0135f561 100644
--- a/src/renderer/components/automation/WorkflowList.tsx
+++ b/src/renderer/components/automation/WorkflowList.tsx
@@ -3,7 +3,7 @@ import { AnimatePresence, motion } from "framer-motion"
import { ArrowsClockwise, CaretRight, Gear, Lightning, Play, Trash } from "@phosphor-icons/react"
import type { WorkflowPipelineRecord, WorkflowStep } from "@shared/types"
import { timeAgo } from "../../lib/formatters"
-import { ACCENT, LINE, triggerConfig, actionConfig, workflowStatusColors, stepStatusConfig, safeJsonParse } from "./automation-config"
+import { triggerConfig, actionConfig, workflowStatusColors, stepStatusConfig, safeJsonParse } from "./automation-config"
function StepTimeline({ steps }: { steps: WorkflowStep[] }) {
if (steps.length === 0) return
No steps defined
@@ -16,7 +16,7 @@ function StepTimeline({ steps }: { steps: WorkflowStep[] }) {
return (
-
+
{idx + 1} {step.name}
{step.status}
{info?.label ?? step.action} {step.assigneeDepartment ? <>{step.assigneeDepartment} > : null}{step.requiresApproval ? <>Requires approval > : null}{step.timeoutSec > 0 ? <>{step.timeoutSec}s timeout > : null}
@@ -34,9 +34,9 @@ function WorkflowCard({ workflow, isRunning, onDelete, onEdit, onRun }: { workfl
const steps: WorkflowStep[] = useMemo(() => { const parsed = safeJsonParse(workflow.stepsJson); return Array.isArray(parsed) ? (parsed as WorkflowStep[]) : [] }, [workflow.stepsJson])
return (
-
+
-
+
setExpanded((p) => !p)} className="min-w-0 flex-1 text-left">
{workflow.name} {workflow.status}
{triggerInfo?.label ?? workflow.triggerType}{steps.length} step{steps.length !== 1 ? "s" : ""} {workflow.runCount} run{workflow.runCount !== 1 ? "s" : ""} {workflow.lastRunAt ? <>Last run {timeAgo(workflow.lastRunAt)} > : null}
@@ -49,7 +49,7 @@ function WorkflowCard({ workflow, isRunning, onDelete, onEdit, onRun }: { workfl
- {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)" },