refactor(automations): enhance UI layout and styling for automation components, including adjustments to spacing, alignment, and badge presentation

This commit is contained in:
Anish Sarkar 2026-06-03 19:47:33 +05:30
parent 75c8063bea
commit 14f339bba0
10 changed files with 50 additions and 75 deletions

View file

@ -8,7 +8,6 @@ import { updateAutomationMutationAtom } from "@/atoms/automations/automations-mu
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import type { Automation } from "@/contracts/types/automation.types";
import { AutomationStatusBadge } from "../../components/automation-status-badge";
import { DeleteAutomationDialog } from "../../components/delete-automation-dialog";
interface AutomationDetailHeaderProps {
@ -70,12 +69,9 @@ export function AutomationDetailHeader({
<div className="flex items-start justify-between gap-4 flex-wrap">
<div className="space-y-2 min-w-0 flex-1">
<div className="flex items-center gap-3 flex-wrap">
<h1 className="text-xl md:text-2xl font-semibold text-foreground break-words">
{automation.name}
</h1>
<AutomationStatusBadge status={automation.status} />
</div>
<h1 className="text-xl md:text-2xl font-semibold text-foreground break-words">
{automation.name}
</h1>
{automation.description && (
<p className="text-sm text-muted-foreground max-w-3xl">{automation.description}</p>
)}

View file

@ -27,7 +27,6 @@ export function AutomationRunsSection({ automationId }: AutomationRunsSectionPro
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-4">
<div className="space-y-1">
<CardTitle className="text-base font-semibold inline-flex items-center gap-2">
<History className="h-4 w-4 text-muted-foreground" aria-hidden />
Recent runs
</CardTitle>
<p className="text-xs text-muted-foreground">

View file

@ -8,7 +8,6 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import type { AutomationSummary } from "@/contracts/types/automation.types";
@ -58,25 +57,21 @@ export function AutomationRowActions({
<Button
variant="ghost"
size="icon"
className="h-8 w-8"
className="h-6 w-6 hover:bg-transparent"
aria-label={`Actions for ${automation.name}`}
>
<MoreHorizontal className="h-4 w-4" />
<MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40">
<DropdownMenuContent align="end" className="w-32 z-80">
{canToggle && (
<DropdownMenuItem onSelect={handleTogglePause} disabled={updating}>
<PauseIcon className="mr-2 h-4 w-4" />
{pauseLabel}
</DropdownMenuItem>
)}
{canToggle && canDelete && <DropdownMenuSeparator />}
{canDelete && (
<DropdownMenuItem
onSelect={() => setDeleteOpen(true)}
className="text-destructive focus:text-destructive"
>
<DropdownMenuItem onSelect={() => setDeleteOpen(true)}>
<Trash2 className="mr-2 h-4 w-4" />
Delete
</DropdownMenuItem>

View file

@ -26,35 +26,30 @@ export function AutomationRow({
canDelete,
}: AutomationRowProps) {
return (
<TableRow className="border-b border-border/60 hover:bg-muted/40">
<TableCell className="px-4 md:px-6 py-3 border-r border-border/60">
<div className="flex flex-col gap-0.5 min-w-0">
<Link
href={`/dashboard/${searchSpaceId}/automations/${automation.id}`}
className="text-sm font-medium text-foreground hover:underline truncate"
>
{automation.name}
</Link>
{automation.description && (
<span className="text-xs text-muted-foreground line-clamp-1">
{automation.description}
</span>
)}
</div>
<TableRow className="h-12 border-b border-border/60 hover:bg-muted/40">
<TableCell className="px-4 md:px-6 py-2.5 border-r border-border/60 align-middle">
<Link
href={`/dashboard/${searchSpaceId}/automations/${automation.id}`}
className="block truncate text-sm font-medium text-foreground hover:underline"
>
{automation.name}
</Link>
</TableCell>
<TableCell className="px-4 py-3 border-r border-border/60 w-32">
<TableCell className="px-4 py-2.5 border-r border-border/60 w-32 align-middle">
<AutomationStatusBadge status={automation.status} />
</TableCell>
<TableCell className="hidden md:table-cell px-4 py-3 border-r border-border/60 w-40 text-xs text-muted-foreground">
<TableCell className="hidden md:table-cell px-4 py-2.5 border-r border-border/60 w-40 align-middle text-xs text-muted-foreground">
{formatRelativeDate(automation.updated_at)}
</TableCell>
<TableCell className="px-4 md:px-6 py-3 w-16 text-right">
<AutomationRowActions
automation={automation}
searchSpaceId={searchSpaceId}
canUpdate={canUpdate}
canDelete={canDelete}
/>
<TableCell className="px-4 md:px-6 py-2.5 w-16 align-middle">
<div className="flex justify-end">
<AutomationRowActions
automation={automation}
searchSpaceId={searchSpaceId}
canUpdate={canUpdate}
canDelete={canDelete}
/>
</div>
</TableCell>
</TableRow>
);

View file

@ -1,5 +1,4 @@
"use client";
import { Archive, CircleDot, Pause } from "lucide-react";
import type { AutomationStatus } from "@/contracts/types/automation.types";
import { cn } from "@/lib/utils";
@ -8,41 +7,37 @@ interface AutomationStatusBadgeProps {
className?: string;
}
// Color + icon per status. Active = green, paused = amber, archived = muted.
// Small borderless status pills, matching model-selector badges.
const STATUS_STYLES: Record<
AutomationStatus,
{ label: string; icon: typeof CircleDot; classes: string }
{ label: string; classes: string }
> = {
active: {
label: "Active",
icon: CircleDot,
classes:
"bg-emerald-50 text-emerald-700 border border-emerald-200 dark:bg-emerald-950/40 dark:text-emerald-300 dark:border-emerald-900/50",
"bg-emerald-100 text-emerald-700 dark:bg-emerald-900/50 dark:text-emerald-300",
},
paused: {
label: "Paused",
icon: Pause,
classes:
"bg-amber-50 text-amber-700 border border-amber-200 dark:bg-amber-950/40 dark:text-amber-300 dark:border-amber-900/50",
"bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-300",
},
archived: {
label: "Archived",
icon: Archive,
classes: "bg-muted text-muted-foreground border border-border/60",
classes: "bg-muted text-muted-foreground",
},
};
export function AutomationStatusBadge({ status, className }: AutomationStatusBadgeProps) {
const { label, icon: Icon, classes } = STATUS_STYLES[status];
const { label, classes } = STATUS_STYLES[status];
return (
<span
className={cn(
"inline-flex items-center gap-1.5 rounded-md px-2 py-0.5 text-xs font-medium",
"inline-flex items-center rounded-md border-0 px-1.5 py-0 text-sm font-medium leading-5",
classes,
className
)}
>
<Icon className="h-3 w-3" aria-hidden />
{label}
</span>
);

View file

@ -1,5 +1,5 @@
"use client";
import { Activity, CalendarDays, Workflow } from "lucide-react";
import { CalendarDays, Info, Workflow } from "lucide-react";
import { Table, TableBody, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import type { AutomationSummary } from "@/contracts/types/automation.types";
import { AutomationRow } from "./automation-row";
@ -37,7 +37,7 @@ export function AutomationsTable({
</TableHead>
<TableHead className="border-r border-border/60 w-32">
<span className="flex items-center gap-1.5 text-sm font-medium text-muted-foreground/70">
<Activity size={14} className="opacity-60 text-muted-foreground" />
<Info size={14} className="opacity-60 text-muted-foreground" />
Status
</span>
</TableHead>

View file

@ -364,16 +364,12 @@ export function AutomationBuilderForm({
)}
{activeMode === "json" ? (
<Card className="rounded-md border-accent bg-accent/20">
<CardContent className="pt-6">
<JsonModePanel
value={jsonValue}
issues={jsonIssues}
notice={jsonNotice}
onChange={setJsonValue}
/>
</CardContent>
</Card>
<JsonModePanel
value={jsonValue}
issues={jsonIssues}
notice={jsonNotice}
onChange={setJsonValue}
/>
) : (
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3">
<div className="lg:col-span-2">

View file

@ -26,14 +26,13 @@ export function JsonModePanel({ value, issues, notice, onChange }: JsonModePanel
</Alert>
)}
<div className="rounded-md border border-input bg-background px-3 py-2 max-h-144 overflow-auto">
<JsonView
src={value}
editable
onChange={(next) => onChange(next as Record<string, unknown>)}
collapsed={false}
/>
</div>
<JsonView
src={value}
editable
onChange={(next) => onChange(next as Record<string, unknown>)}
collapsed={false}
className="max-h-144 overflow-auto rounded-md border border-accent bg-accent/20"
/>
{issues.length > 0 && (
<Alert variant="destructive">

View file

@ -81,7 +81,7 @@ export function ScheduleSection({
type="button"
variant="ghost"
size="icon"
className="h-6 w-6 shrink-0 text-muted-foreground hover:text-destructive"
className="h-6 w-6 shrink-0 text-muted-foreground hover:text-foreground"
aria-label="Remove schedule"
onClick={() => onScheduleChange(null)}
>

View file

@ -8,7 +8,7 @@ export default async function AutomationsPage({
const { search_space_id } = await params;
return (
<div className="w-full space-y-6">
<div className="mx-auto w-full max-w-5xl space-y-6">
<AutomationsContent searchSpaceId={Number(search_space_id)} />
</div>
);