{"name":"resource-management","type":"registry:block","title":"Resource management","description":"Filter and sort projects, inspect a project, and confirm archiving. Resource management — complete application example.","docs":"https://ui.siteplane.io/docs-markdown/examples/resource-management.md","categories":["Examples"],"registryDependencies":["@siteplane/alert-dialog","@siteplane/badge","@siteplane/button","@siteplane/data-list","@siteplane/detail-drawer","@siteplane/filter-bar","@siteplane/input","@siteplane/select","@siteplane/table"],"dependencies":[],"files":[{"path":"apps/site/src/examples/resource-management/default.tsx","type":"registry:file","target":"~/src/components/resource-management.tsx","content":"\"use client\";\n\nimport {\n  AlertDialog,\n  AlertDialogClose,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogPopup,\n  AlertDialogTitle,\n  AlertDialogTrigger,\n} from \"@/siteplane/components/alert-dialog/index\";\nimport { Badge } from \"@/siteplane/components/badge/index\";\nimport { Button } from \"@/siteplane/components/button/index\";\nimport {\n  DataList,\n  DataListItem,\n  DataListLabel,\n  DataListValue,\n} from \"@/siteplane/components/data-list/index\";\nimport {\n  DetailDrawer,\n  DetailDrawerBody,\n  DetailDrawerClose,\n  DetailDrawerDescription,\n  DetailDrawerFooter,\n  DetailDrawerHeader,\n  DetailDrawerPopup,\n  DetailDrawerTitle,\n} from \"@/siteplane/components/detail-drawer/index\";\nimport {\n  FilterBar,\n  FilterBarControls,\n  FilterBarFilters,\n  FilterBarSearch,\n} from \"@/siteplane/components/filter-bar/index\";\nimport { Input } from \"@/siteplane/components/input/index\";\nimport {\n  Select,\n  SelectItem,\n  SelectPopup,\n  SelectTrigger,\n  SelectValue,\n} from \"@/siteplane/components/select/index\";\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n  type SortDescriptor,\n} from \"@/siteplane/components/table/index\";\nimport { useState } from \"react\";\n\nconst statuses = [\n  { value: \"All\", label: \"All statuses\" },\n  { value: \"Active\", label: \"Active\" },\n  { value: \"Archived\", label: \"Archived\" },\n];\n\nexport default function Example() {\n  const [projects, setProjects] = useState([\n    {\n      id: \"website\",\n      name: \"Website redesign\",\n      owner: \"Mara Ellis\",\n      status: \"Active\",\n    },\n    {\n      id: \"mobile\",\n      name: \"Mobile application\",\n      owner: \"Ben Carter\",\n      status: \"Active\",\n    },\n    {\n      id: \"research\",\n      name: \"Customer research\",\n      owner: \"Sofia Nguyen\",\n      status: \"Archived\",\n    },\n  ]);\n  const [query, setQuery] = useState(\"\");\n  const [status, setStatus] = useState(\"All\");\n  const [sort, setSort] = useState<SortDescriptor>({\n    column: \"name\",\n    direction: \"ascending\",\n  });\n  const [activeId, setActiveId] = useState<string>();\n  const [open, setOpen] = useState(false);\n  const [confirm, setConfirm] = useState(false);\n  const active = projects.find((project) => project.id === activeId);\n  const visible = projects\n    .filter(\n      (project) =>\n        project.name.toLowerCase().includes(query.toLowerCase()) &&\n        (status === \"All\" || project.status === status),\n    )\n    .sort((a, b) => {\n      const key = sort.column === \"owner\" ? \"owner\" : \"name\";\n      return (\n        a[key].localeCompare(b[key]) * (sort.direction === \"ascending\" ? 1 : -1)\n      );\n    });\n  return (\n    <div className=\"grid w-full min-w-0 gap-4\">\n      <div>\n        <h2 className=\"text-lg font-semibold\">Projects</h2>\n        <p className=\"text-sm text-foreground\">\n          Find a project, review its details, or archive it.\n        </p>\n      </div>\n      <FilterBar aria-label=\"Project filters\">\n        <FilterBarControls>\n          <FilterBarSearch>\n            <Input\n              aria-label=\"Search projects\"\n              type=\"search\"\n              placeholder=\"Search projects…\"\n              value={query}\n              onChange={(event) => setQuery(event.target.value)}\n            />\n          </FilterBarSearch>\n          <FilterBarFilters>\n            <Select\n              items={statuses}\n              value={status}\n              onValueChange={(value) => {\n                if (value) setStatus(value);\n              }}\n            >\n              <SelectTrigger aria-label=\"Filter by status\">\n                <SelectValue />\n              </SelectTrigger>\n              <SelectPopup>\n                {statuses.map((item) => (\n                  <SelectItem key={item.value} value={item.value}>\n                    {item.label}\n                  </SelectItem>\n                ))}\n              </SelectPopup>\n            </Select>\n          </FilterBarFilters>\n        </FilterBarControls>\n      </FilterBar>\n      <Table aria-label=\"Projects\" sortDescriptor={sort} onSortChange={setSort}>\n        <TableHeader>\n          <TableHead id=\"name\" isRowHeader allowsSorting>\n            Project\n          </TableHead>\n          <TableHead id=\"owner\" allowsSorting>\n            Owner\n          </TableHead>\n          <TableHead id=\"status\">Status</TableHead>\n          <TableHead id=\"actions\">Actions</TableHead>\n        </TableHeader>\n        <TableBody renderEmptyState={() => \"No projects match your filters.\"}>\n          {visible.map((project) => (\n            <TableRow id={project.id} key={project.id} textValue={project.name}>\n              <TableCell>{project.name}</TableCell>\n              <TableCell>{project.owner}</TableCell>\n              <TableCell>\n                <Badge\n                  variant={\n                    project.status === \"Active\" ? \"success\" : \"secondary\"\n                  }\n                >\n                  {project.status}\n                </Badge>\n              </TableCell>\n              <TableCell>\n                <Button\n                  variant=\"ghost\"\n                  size=\"sm\"\n                  aria-label={`View ${project.name}`}\n                  onClick={() => {\n                    setActiveId(project.id);\n                    setOpen(true);\n                  }}\n                >\n                  View\n                </Button>\n              </TableCell>\n            </TableRow>\n          ))}\n        </TableBody>\n      </Table>\n      <DetailDrawer open={open} onOpenChange={setOpen}>\n        <DetailDrawerPopup>\n          <DetailDrawerHeader>\n            <DetailDrawerTitle>\n              {active?.name ?? \"Project details\"}\n            </DetailDrawerTitle>\n            <DetailDrawerDescription>\n              Owner and current status.\n            </DetailDrawerDescription>\n          </DetailDrawerHeader>\n          <DetailDrawerBody>\n            <DataList orientation=\"vertical\">\n              <DataListItem>\n                <DataListLabel>Owner</DataListLabel>\n                <DataListValue>{active?.owner}</DataListValue>\n              </DataListItem>\n              <DataListItem>\n                <DataListLabel>Status</DataListLabel>\n                <DataListValue>{active?.status}</DataListValue>\n              </DataListItem>\n            </DataList>\n          </DetailDrawerBody>\n          <DetailDrawerFooter>\n            <DetailDrawerClose render={<Button variant=\"ghost\" />}>\n              Close\n            </DetailDrawerClose>\n            <AlertDialog open={confirm} onOpenChange={setConfirm}>\n              <AlertDialogTrigger\n                render={\n                  <Button\n                    variant=\"destructive\"\n                    disabled={active?.status !== \"Active\"}\n                  />\n                }\n              >\n                Archive project\n              </AlertDialogTrigger>\n              <AlertDialogPopup>\n                <AlertDialogHeader>\n                  <AlertDialogTitle>Archive {active?.name}?</AlertDialogTitle>\n                  <AlertDialogDescription>\n                    Archived projects no longer appear under Active. You can\n                    still find them under Archived.\n                  </AlertDialogDescription>\n                </AlertDialogHeader>\n                <AlertDialogFooter>\n                  <AlertDialogClose render={<Button variant=\"ghost\" />}>\n                    Cancel\n                  </AlertDialogClose>\n                  <Button\n                    variant=\"destructive\"\n                    onClick={() => {\n                      setProjects((current) =>\n                        current.map((project) =>\n                          project.id === activeId\n                            ? { ...project, status: \"Archived\" }\n                            : project,\n                        ),\n                      );\n                      setConfirm(false);\n                      setOpen(false);\n                    }}\n                  >\n                    Archive\n                  </Button>\n                </AlertDialogFooter>\n              </AlertDialogPopup>\n            </AlertDialog>\n          </DetailDrawerFooter>\n        </DetailDrawerPopup>\n      </DetailDrawer>\n    </div>\n  );\n}\n"}]}