{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "task-queue-01",
  "type": "registry:block",
  "title": "Processing Task Queue",
  "description": "A compact task queue with progress, status labels, and row actions for media processing workflows.",
  "author": "UIBlocks contributors",
  "registryDependencies": [],
  "dependencies": [
    "lucide-react@0.577.0"
  ],
  "files": [
    {
      "path": "licenses/task-queue-01/LICENSE.txt",
      "type": "registry:file",
      "target": "@components/uiblocks/task-queue-01/LICENSE.txt",
      "content": "UIBlocks original block: Copyright (c) 2026 UIBlocks contributors. Licensed under the MIT License below.\n\nIncludes source from Blocks.so and its bundled primitives.\nSource: https://github.com/ephraimduncan/blocks\nRevision: 3f759d5171c2391253ec4c772e9e5b3db2bc7c7f\n\nMIT License\n\nCopyright (c) 2025 Ephraim Duncan\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
    },
    {
      "path": "content/components/workspace/task-queue-01.tsx",
      "type": "registry:component",
      "target": "@components/uiblocks/task-queue-01/task-queue-01.tsx",
      "content": "\"use client\";\n\nimport {\n  AlertCircle,\n  ArrowRight,\n  CheckCircle2,\n  Clock3,\n  FileVideo2,\n  Inbox,\n  LoaderCircle,\n  RotateCcw,\n} from \"lucide-react\";\nimport { useId, useState } from \"react\";\n\nexport type DemoTask = {\n  id: string;\n  name: string;\n  detail: string;\n  status: \"queued\" | \"processing\" | \"completed\" | \"error\";\n  progress?: number;\n};\nexport type TaskQueue01Props = {\n  title?: string;\n  tasks?: DemoTask[];\n  searchQuery?: string;\n  onRetry?: (id: string) => void;\n};\nconst sampleTasks: DemoTask[] = [\n  {\n    id: \"sample-1\",\n    name: \"Autumn-campaign.mp4\",\n    detail: \"MP4 · Web ready\",\n    status: \"processing\",\n    progress: 64,\n  },\n  {\n    id: \"sample-2\",\n    name: \"Product-walkthrough.mov\",\n    detail: \"MP4 · Web ready\",\n    status: \"queued\",\n  },\n  {\n    id: \"sample-3\",\n    name: \"Studio-notes.mp4\",\n    detail: \"MP4 · Web ready\",\n    status: \"completed\",\n  },\n];\nconst statusLabels = {\n  queued: \"In queue\",\n  processing: \"Preparing\",\n  completed: \"Ready\",\n  error: \"Needs attention\",\n};\n\n/** Task queue with status filtering, search, progress, empty and error states. */\nexport default function TaskQueue01({\n  title = \"Your task queue\",\n  tasks = sampleTasks,\n  searchQuery = \"\",\n  onRetry,\n}: TaskQueue01Props = {}) {\n  const [filter, setFilter] = useState<\"all\" | \"active\" | \"completed\">(\"all\");\n  const [notice, setNotice] = useState(\"\");\n  const id = useId();\n  const visible = tasks.filter(\n    (task) =>\n      task.name.toLowerCase().includes(searchQuery.toLowerCase()) &&\n      (filter === \"all\" ||\n        (filter === \"active\"\n          ? task.status === \"queued\" || task.status === \"processing\"\n          : task.status === \"completed\"))\n  );\n\n  return (\n    <section\n      aria-labelledby={id}\n      className=\"w-full overflow-hidden rounded-2xl border border-zinc-200 bg-white text-zinc-950 shadow-sm shadow-zinc-950/[0.02] dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50\"\n    >\n      <div className=\"flex flex-wrap items-center justify-between gap-3 border-b border-zinc-100 px-5 py-5 sm:px-6 dark:border-zinc-800\">\n        <div className=\"flex items-center gap-2\">\n          <h2 className=\"text-sm font-semibold\" id={id}>\n            {title}\n          </h2>\n          <span className=\"rounded-md bg-zinc-100 px-1.5 py-0.5 text-[10px] text-zinc-500 dark:bg-zinc-800\">\n            {tasks.length}\n          </span>\n        </div>\n        <span className=\"inline-flex items-center gap-1.5 text-[10px] text-zinc-400\">\n          <span className=\"size-1.5 rounded-full bg-emerald-500\" /> Demo\n          activity\n        </span>\n      </div>\n      <div\n        aria-label=\"Filter task queue\"\n        className=\"flex gap-5 border-b border-zinc-100 px-5 sm:px-6 dark:border-zinc-800\"\n        role=\"group\"\n      >\n        {([\"all\", \"active\", \"completed\"] as const).map((value) => (\n          <button\n            aria-pressed={filter === value}\n            className={`relative min-h-11 text-xs font-medium capitalize transition-colors ${filter === value ? \"text-violet-600 after:absolute after:inset-x-0 after:bottom-0 after:h-0.5 after:bg-violet-600 dark:text-violet-400\" : \"text-zinc-400 hover:text-zinc-700 dark:hover:text-zinc-200\"}`}\n            key={value}\n            onClick={() => setFilter(value)}\n            type=\"button\"\n          >\n            {value === \"all\" ? \"All files\" : value}\n          </button>\n        ))}\n      </div>\n      {visible.length ? (\n        <ul className=\"divide-y divide-zinc-100 dark:divide-zinc-800\">\n          {visible.map((task) => (\n            <li\n              className=\"flex items-center gap-3 px-5 py-4 sm:px-6\"\n              key={task.id}\n            >\n              <span className=\"grid size-10 shrink-0 place-items-center rounded-xl border border-zinc-100 bg-zinc-50 text-zinc-400 dark:border-zinc-800 dark:bg-zinc-900\">\n                <FileVideo2 aria-hidden=\"true\" className=\"size-4\" />\n              </span>\n              <div className=\"min-w-0 flex-1\">\n                <p className=\"truncate text-xs font-medium\">{task.name}</p>\n                <p className=\"mt-1 text-[10px] text-zinc-400\">{task.detail}</p>\n                {task.status === \"processing\" && (\n                  <div\n                    aria-label={`${task.name} progress`}\n                    aria-valuemax={100}\n                    aria-valuemin={0}\n                    aria-valuenow={task.progress ?? 0}\n                    className=\"mt-2 h-1 max-w-48 overflow-hidden rounded-full bg-zinc-100 dark:bg-zinc-800\"\n                    role=\"progressbar\"\n                  >\n                    <div\n                      className=\"h-full rounded-full bg-violet-500 transition-all duration-500\"\n                      style={{ width: `${task.progress ?? 0}%` }}\n                    />\n                  </div>\n                )}\n              </div>\n              <div\n                className={`flex shrink-0 items-center gap-1.5 text-[10px] ${task.status === \"completed\" ? \"text-emerald-600 dark:text-emerald-400\" : task.status === \"error\" ? \"text-red-500\" : task.status === \"processing\" ? \"text-violet-600 dark:text-violet-400\" : \"text-zinc-400\"}`}\n              >\n                {task.status === \"processing\" ? (\n                  <LoaderCircle\n                    aria-hidden=\"true\"\n                    className=\"size-3 animate-spin\"\n                  />\n                ) : task.status === \"completed\" ? (\n                  <CheckCircle2 aria-hidden=\"true\" className=\"size-3\" />\n                ) : task.status === \"error\" ? (\n                  <AlertCircle aria-hidden=\"true\" className=\"size-3\" />\n                ) : (\n                  <Clock3 aria-hidden=\"true\" className=\"size-3\" />\n                )}\n                <span className=\"hidden sm:inline\">\n                  {statusLabels[task.status]}\n                </span>\n                {task.status === \"processing\" && (\n                  <span>{task.progress ?? 0}%</span>\n                )}\n                {task.status === \"error\" && (\n                  <button\n                    aria-label={`Retry ${task.name}`}\n                    className=\"ml-1 rounded-md p-1.5 hover:bg-red-50 dark:hover:bg-red-950/40\"\n                    onClick={() => {\n                      if (onRetry) onRetry(task.id);\n                      else\n                        setNotice(\n                          \"Retry selected. This standalone queue is a browser-only demo.\"\n                        );\n                    }}\n                    type=\"button\"\n                  >\n                    <RotateCcw aria-hidden=\"true\" className=\"size-3\" />\n                  </button>\n                )}\n              </div>\n            </li>\n          ))}\n        </ul>\n      ) : (\n        <div className=\"flex min-h-44 flex-col items-center justify-center px-6 py-8 text-center\">\n          <Inbox\n            aria-hidden=\"true\"\n            className=\"size-7 text-zinc-300 dark:text-zinc-600\"\n          />\n          <p className=\"mt-3 text-xs font-medium\">\n            {searchQuery\n              ? \"No matching files\"\n              : filter === \"all\"\n                ? \"A fresh start\"\n                : \"Nothing here yet\"}\n          </p>\n          <p className=\"mt-1 text-[11px] text-zinc-400\">\n            {searchQuery\n              ? \"Try another file name.\"\n              : \"Your demo tasks will appear here.\"}\n          </p>\n        </div>\n      )}\n      {notice && (\n        <p\n          className=\"px-5 py-3 text-xs text-violet-600 dark:text-violet-400\"\n          role=\"status\"\n        >\n          {notice}\n        </p>\n      )}\n      <div className=\"flex items-center justify-between gap-2 border-t border-zinc-100 bg-zinc-50/50 px-5 py-3 text-[10px] text-zinc-400 sm:px-6 dark:border-zinc-800 dark:bg-zinc-900/30\">\n        <span>\n          {tasks.filter((task) => task.status === \"completed\").length} completed\n          ·{\" \"}\n          {\n            tasks.filter(\n              (task) => task.status === \"queued\" || task.status === \"processing\"\n            ).length\n          }{\" \"}\n          in progress\n        </span>\n        <span className=\"inline-flex items-center gap-1\">\n          Demo queue <ArrowRight aria-hidden=\"true\" className=\"size-3\" />\n        </span>\n      </div>\n    </section>\n  );\n}\n"
    }
  ],
  "categories": [
    "workspace",
    "application",
    "clean-saas"
  ],
  "docs": "Import the default component from @components/uiblocks/task-queue-01/task-queue-01.tsx. React 19, Tailwind CSS 4, and the standard shadcn CSS theme variables are required. This block is framework-independent React and works in TanStack Start, Vite, and Next.js projects. Connect links or navigation callbacks to your host application. Demo avatars use public image URLs; replace them with your own assets. Demo handlers must be connected to your product. See https://uiblocks.org/workspace/task-queue-01 for content guidelines and source attribution.",
  "meta": {
    "kind": "block",
    "version": "0.1.0",
    "source": {
      "name": "UIBlocks",
      "url": "https://uiblocks.org",
      "license": "MIT",
      "author": "UIBlocks contributors",
      "revision": "uiblocks-v0.1.0"
    },
    "license": "MIT",
    "style": "clean-saas",
    "pageType": "application",
    "featured": true,
    "tags": [
      "queue",
      "tasks",
      "jobs",
      "progress",
      "status",
      "队列",
      "任务",
      "进度",
      "状态",
      "批量"
    ],
    "contentGuidelines": [
      "Use a stable identifier and explicit status for each task.",
      "Connect cancellation and retry to actual background job controls."
    ],
    "recommendedWith": [
      "upload-workflow-01",
      "results-gallery-01"
    ],
    "entry": "@components/uiblocks/task-queue-01/task-queue-01.tsx",
    "exportName": "default",
    "frameworkIndependent": true,
    "frameworkDependencies": [],
    "compatibleHosts": [
      "react",
      "vite",
      "tanstack-start",
      "nextjs"
    ],
    "requirements": {
      "react": ">=19",
      "tailwind": ">=4"
    },
    "previewUrl": "https://uiblocks.org/preview/task-queue-01"
  }
}
