add Jobs components

This commit is contained in:
bluemoehre
2026-07-01 00:19:15 +02:00
parent cb8adac08f
commit 44a21f9ea8
5 changed files with 159 additions and 2 deletions
+25
View File
@@ -0,0 +1,25 @@
import { JobListItem, type JobListItemProps } from "@/components/JobListItem"
import { cn } from "@/lib/utils"
export interface JobListProps {
jobs: JobListItemProps[]
className?: string
}
export function JobList({ jobs, className }: JobListProps) {
if (jobs.length === 0) {
return (
<div className={cn("flex flex-col gap-2", className)}>
<p className="text-sm text-muted-foreground">No running jobs.</p>
</div>
)
}
return (
<div className={cn("flex flex-col gap-2", className)}>
{jobs.map((job, index) => (
<JobListItem key={index} {...job} />
))}
</div>
)
}
+30
View File
@@ -0,0 +1,30 @@
import { Progress } from "@/components/ui/progress"
import { cn } from "@/lib/utils"
import { formatTime } from "@/utils/datetime"
export interface JobListItemProps {
title: string
runningTime: string
progress: number
startTime?: string
eta?: string
className?: string
}
export function JobListItem({ title, runningTime, progress, startTime, eta, className }: JobListItemProps) {
return (
<div className={cn("flex flex-col gap-2 rounded-lg border p-3", className)}>
<div className="flex items-center justify-between">
<span className="text-sm font-medium">{title}</span>
<span className="text-xs text-muted-foreground tabular-nums">{runningTime}</span>
</div>
<Progress value={progress} />
{(startTime || eta) && (
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground tabular-nums">{startTime ? formatTime(startTime) : ""} started</span>
<span className="text-xs text-muted-foreground tabular-nums">{eta ? `ETA ${formatTime(eta)}` : ""}</span>
</div>
)}
</div>
)
}
+18
View File
@@ -0,0 +1,18 @@
import * as React from "react"
import { cn } from "@/lib/utils"
function Label({ className, ...props }: React.ComponentProps<"label">) {
return (
<label
data-slot="label"
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className
)}
{...props}
/>
)
}
export { Label }
+81
View File
@@ -0,0 +1,81 @@
import { Progress as ProgressPrimitive } from "@base-ui/react/progress"
import { cn } from "@/lib/utils"
function Progress({
className,
children,
value,
...props
}: ProgressPrimitive.Root.Props) {
return (
<ProgressPrimitive.Root
value={value}
data-slot="progress"
className={cn("flex flex-wrap gap-3", className)}
{...props}
>
{children}
<ProgressTrack>
<ProgressIndicator />
</ProgressTrack>
</ProgressPrimitive.Root>
)
}
function ProgressTrack({ className, ...props }: ProgressPrimitive.Track.Props) {
return (
<ProgressPrimitive.Track
className={cn(
"relative flex h-1 w-full items-center overflow-x-hidden rounded-full bg-muted",
className
)}
data-slot="progress-track"
{...props}
/>
)
}
function ProgressIndicator({
className,
...props
}: ProgressPrimitive.Indicator.Props) {
return (
<ProgressPrimitive.Indicator
data-slot="progress-indicator"
className={cn("h-full bg-primary transition-all", className)}
{...props}
/>
)
}
function ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {
return (
<ProgressPrimitive.Label
className={cn("text-sm font-medium", className)}
data-slot="progress-label"
{...props}
/>
)
}
function ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {
return (
<ProgressPrimitive.Value
className={cn(
"ml-auto text-sm text-muted-foreground tabular-nums",
className
)}
data-slot="progress-value"
{...props}
/>
)
}
export {
Progress,
ProgressTrack,
ProgressIndicator,
ProgressLabel,
ProgressValue,
}
+5 -2
View File
@@ -1,7 +1,10 @@
import { JobList } from "@/components/JobList"
import mockJobs from "@/mocks/jobs.json"
export function JobsView() {
return (
<div className="flex flex-1 flex-col gap-1 p-4">
<p className="text-sm text-muted-foreground">Running jobs coming soon.</p>
<div className="flex flex-1 flex-col gap-1 mt-4 mb-4">
<JobList jobs={mockJobs} />
</div>
)
}