mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-06 20:15:17 +02:00
refactor(automations): enhance UI layout and styling for automation components, including adjustments to spacing, alignment, and badge presentation
This commit is contained in:
parent
75c8063bea
commit
14f339bba0
10 changed files with 50 additions and 75 deletions
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue