Syed Hasnain

Job Progress

Component 1

About

A weighted multi-stage progress surface with pause and resume, stage durations, and cancel confirmation.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Job Progress to your project and fits it to your theme.

Code

How Job Progress is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import { useEffect, useState } from 'react'
import JobProgress from '@/components/ui/job-progress'

const TOTAL_FILES = 3700

const START_FILES = 1240

const FILES_PER_TICK = 140

const VERIFY_PER_TICK = 0.08

export default function Demo() {
  const [files, setFiles] = useState(START_FILES)
  const [verify, setVerify] = useState(0)
  const [paused, setPaused] = useState(false)
  const [cancelled, setCancelled] = useState(false)
  const done = files >= TOTAL_FILES && verify >= 1

  useEffect(() => {
    if (paused || cancelled) return
    const timer = window.setTimeout(
      () => {
        if (done) {
          setFiles(START_FILES)
          setVerify(0)
        } else if (files < TOTAL_FILES) {
          setFiles((current) => Math.min(current + FILES_PER_TICK, TOTAL_FILES))
        } else {
          setVerify((current) => Math.min(current + VERIFY_PER_TICK, 1))
        }
      },
      done ? 2000 : 300,
    )
    return () => window.clearTimeout(timer)
  }, [cancelled, done, files, paused, verify])

  const embedding = files < TOTAL_FILES
  const remainingWeight = 4 * (1 - files / TOTAL_FILES) + 2 * (1 - verify)
  const minutes = Math.ceil(remainingWeight / 2)

  return (
    <JobProgress
      className="w-full max-w-lg"
      eta={done ? undefined : `${Math.max(1, minutes)} min`}
      onCancel={() => {
        setCancelled(true)
        setTimeout(() => {
          setFiles(START_FILES)
          setVerify(0)
          setPaused(false)
          setCancelled(false)
        }, 1500)
      }}
      onPausedChange={setPaused}
      paused={paused || cancelled}
      stages={[
        {
          id: 'scan',
          label: 'Scan the repository',
          status: 'complete',
          weight: 1,
          duration: '12s',
        },
        {
          id: 'index',
          label: 'Build the semantic index',
          status: 'complete',
          weight: 2,
          duration: '48s',
        },
        {
          id: 'embed',
          label: 'Generate embeddings',
          status: embedding ? 'running' : 'complete',
          weight: 4,
          duration: '1m 36s',
          progress: files / TOTAL_FILES,
          detail: `${files.toLocaleString()} of ${TOTAL_FILES.toLocaleString()} files`,
        },
        {
          id: 'verify',
          label: 'Verify retrieval quality',
          status: embedding ? 'pending' : verify >= 1 ? 'complete' : 'running',
          weight: 2,
          duration: '22s',
          progress: verify,
          detail: `${Math.round(verify * 24)} of 24 test queries`,
        },
      ]}
      title={cancelled ? 'Index repository (stopped)' : 'Index repository'}
    />
  )
}

Dependencies

More by Syed Hasnain

View all
  • Color Picker

    Color Picker

    78
  • Cost Meter

    Cost Meter

    29
  • Quota Banner

    Quota Banner

    21
  • Message Scroller

    Message Scroller

    21
  • Commit

    Commit

    17
  • Image Generation Loader

    Image Generation Loader

    15
  • Attachments

    Attachments

    15
  • Citations

    Citations

    14

Explore more

Part of Pasta UI — browse the full library on 21st.dev.