{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "team-members-table",
  "title": "Team Members Table",
  "description": "Workspace member management with roles, two-factor status, last activity and a remove action.",
  "registryDependencies": [
    "@spectrumui/data-table"
  ],
  "files": [
    {
      "path": "components/spectrumui/blocks/tables/team-members-table.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { DataTable, type DataTableColumn } from '@/components/spectrumui/data-table';\n\ntype IconProps = React.SVGProps<SVGSVGElement>;\n\nfunction IconShield(props: IconProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n      <path\n        transform=\"translate(3.5, 2)\"\n        d=\"M9.071,0.1 L15.708,2.327 C16.451,2.575 16.954,3.257 16.958,4.022 L17,10.663 C17.013,12.676 16.279,14.628 14.935,16.158 C14.317,16.86 13.525,17.463 12.513,18.002 L8.945,19.91 C8.833,19.969 8.71,19.999 8.587,20 C8.463,20.001 8.339,19.972 8.228,19.914 L4.627,18.051 C3.604,17.52 2.805,16.926 2.181,16.233 C0.814,14.719 0.055,12.776 0.042,10.76 L0,4.124 C-0.004,3.358 0.489,2.671 1.228,2.413 L7.841,0.106 C8.233,-0.033 8.671,-0.036 9.071,0.1 Z M12.245,7.219 C11.948,6.933 11.47,6.935 11.177,7.225 L11.177,7.225 L7.808,10.545 L6.429,9.219 C6.132,8.934 5.655,8.937 5.361,9.226 C5.068,9.515 5.071,9.98 5.368,10.265 L5.368,10.265 L7.284,12.109 C7.433,12.252 7.626,12.323 7.819,12.321 C8.012,12.32 8.205,12.247 8.352,12.102 L8.352,12.102 L12.251,8.258 C12.544,7.969 12.541,7.504 12.245,7.219 Z\"\n      />\n    </svg>\n  );\n}\n\nfunction IconMore(props: IconProps) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n      <path\n        transform=\"translate(2, 2)\"\n        d=\"M10,0 C15.52,0 20,4.48 20,10 C20,15.52 15.52,20 10,20 C4.47,20 0,15.52 0,10 C0,4.48 4.47,0 10,0 Z M14.48,8.801 C13.81,8.801 13.28,9.34 13.28,10 C13.28,10.66 13.81,11.2 14.48,11.2 C15.14,11.2 15.67,10.66 15.67,10 C15.67,9.34 15.14,8.801 14.48,8.801 Z M10,8.801 C9.34,8.801 8.8,9.34 8.8,10 C8.8,10.66 9.34,11.2 10,11.2 C10.66,11.2 11.19,10.66 11.19,10 C11.19,9.34 10.66,8.801 10,8.801 Z M5.52,8.801 C4.86,8.801 4.32,9.34 4.32,10 C4.32,10.66 4.86,11.2 5.52,11.2 C6.18,11.2 6.71,10.66 6.71,10 C6.71,9.34 6.18,8.801 5.52,8.801 Z\"\n      />\n    </svg>\n  );\n}\n\ntype Role = 'Owner' | 'Admin' | 'Editor' | 'Viewer';\n\ninterface Member {\n  id: string;\n  name: string;\n  email: string;\n  avatar: string;\n  role: Role;\n  twoFactor: boolean;\n  lastActive: string;\n}\n\nconst MEMBERS: Member[] = [\n  {\n    id: 'u1',\n    name: 'Freya Lindgren',\n    email: 'freya@driftlab.se',\n    avatar: '/avatars/people/01.jpg',\n    role: 'Owner',\n    twoFactor: true,\n    lastActive: 'Now',\n  },\n  {\n    id: 'u2',\n    name: 'Mateo Alvarez',\n    email: 'mateo@driftlab.se',\n    avatar: '/avatars/people/03.jpg',\n    role: 'Admin',\n    twoFactor: true,\n    lastActive: '5 min ago',\n  },\n  {\n    id: 'u3',\n    name: 'Aiko Watanabe',\n    email: 'aiko@driftlab.se',\n    avatar: '/avatars/people/06.jpg',\n    role: 'Editor',\n    twoFactor: true,\n    lastActive: '1 hour ago',\n  },\n  {\n    id: 'u4',\n    name: 'Jonas Keller',\n    email: 'jonas@driftlab.se',\n    avatar: '/avatars/people/09.jpg',\n    role: 'Editor',\n    twoFactor: false,\n    lastActive: 'Yesterday',\n  },\n  {\n    id: 'u5',\n    name: 'Zara Osei',\n    email: 'zara@driftlab.se',\n    avatar: '/avatars/people/12.jpg',\n    role: 'Editor',\n    twoFactor: true,\n    lastActive: '2 days ago',\n  },\n  {\n    id: 'u6',\n    name: 'Nils Bakker',\n    email: 'nils@driftlab.se',\n    avatar: '/avatars/people/13.jpg',\n    role: 'Viewer',\n    twoFactor: false,\n    lastActive: 'Aug 30',\n  },\n  {\n    id: 'u7',\n    name: 'Leila Haddad',\n    email: 'leila@driftlab.se',\n    avatar: '/avatars/people/05.jpg',\n    role: 'Viewer',\n    twoFactor: true,\n    lastActive: 'Aug 27',\n  },\n];\n\nconst ROLE_RANK: Record<Role, number> = { Owner: 0, Admin: 1, Editor: 2, Viewer: 3 };\n\nconst columns: DataTableColumn<Member>[] = [\n  {\n    id: 'name',\n    header: 'Member',\n    sortable: true,\n    value: (row) => row.name,\n    cell: (row) => (\n      <div className=\"flex items-center gap-2.5\">\n        {/* eslint-disable-next-line @next/next/no-img-element */}\n        <img\n          src={row.avatar}\n          alt=\"\"\n          loading=\"lazy\"\n          width={28}\n          height={28}\n          className=\"size-7 shrink-0 rounded-full object-cover outline outline-1 -outline-offset-1 outline-black/10 dark:outline-white/10\"\n        />\n        <span className=\"min-w-0\">\n          <span className=\"block truncate\">{row.name}</span>\n          <span className=\"block truncate text-xs font-normal text-neutral-500 dark:text-neutral-400\">\n            {row.email}\n          </span>\n        </span>\n      </div>\n    ),\n  },\n  {\n    id: 'role',\n    header: 'Role',\n    sortable: true,\n    value: (row) => ROLE_RANK[row.role],\n    cell: (row) => (\n      <span className=\"inline-flex rounded-full bg-neutral-100 px-2 py-0.5 text-xs font-medium text-neutral-700 ring-1 ring-inset ring-neutral-500/15 dark:bg-neutral-100/10 dark:text-neutral-300 dark:ring-white/15\">\n        {row.role}\n      </span>\n    ),\n  },\n  {\n    id: 'twoFactor',\n    header: '2FA',\n    sortable: true,\n    hideBelow: 'sm',\n    value: (row) => row.twoFactor,\n    cell: (row) =>\n      row.twoFactor ? (\n        <span className=\"inline-flex items-center gap-1.5 text-emerald-700 dark:text-emerald-300\">\n          <IconShield className=\"size-4\" />\n          Enabled\n        </span>\n      ) : (\n        <span className=\"text-neutral-400 dark:text-neutral-500\">Off</span>\n      ),\n  },\n  {\n    id: 'lastActive',\n    header: 'Last active',\n    hideBelow: 'md',\n    value: (row) => row.lastActive,\n  },\n];\n\nexport function TeamMembersTable({\n  variant = 'Comfortable',\n}: {\n  variant?: 'Comfortable' | 'Compact';\n}) {\n  const [members, setMembers] = React.useState(MEMBERS);\n  const removed = MEMBERS.length - members.length;\n\n  return (\n    <div className=\"mx-auto w-full max-w-4xl px-4 py-10 sm:px-6\">\n      <DataTable\n        data={members}\n        columns={columns}\n        rowId={(row) => row.id}\n        rowLabel={(row) => row.name}\n        caption=\"Workspace members with role, two-factor status and last activity.\"\n        density={variant === 'Compact' ? 'compact' : 'default'}\n        title=\"Members\"\n        quickFilter={{\n          columnId: 'role',\n          label: 'Filter by role',\n          getValue: (row) => row.role,\n        }}\n        selectable\n        defaultSort={{ columnId: 'role', direction: 'asc' }}\n        onDelete={(ids) => setMembers((current) => current.filter((row) => !ids.includes(row.id)))}\n        toolbar={\n          removed > 0 ? (\n            <button\n              type=\"button\"\n              onClick={() => setMembers(MEMBERS)}\n              className=\"rounded-full px-2 text-sm font-medium text-neutral-500 underline underline-offset-2 transition-colors duration-100 ease-out hover:text-neutral-900 focus-visible:ring-1 focus-visible:ring-neutral-950 focus-visible:outline-hidden dark:text-neutral-400 dark:hover:text-neutral-100 dark:focus-visible:ring-neutral-300\"\n            >\n              Restore {removed}\n            </button>\n          ) : null\n        }\n        rowActions={(row) => (\n          <button\n            type=\"button\"\n            aria-label={`Open actions for ${row.name}`}\n            className=\"grid size-8 place-items-center rounded-xl text-neutral-400 transition-colors duration-100 ease-out hover:bg-neutral-100 hover:text-neutral-900 focus-visible:ring-1 focus-visible:ring-neutral-950 focus-visible:outline-hidden dark:hover:bg-neutral-800 dark:hover:text-neutral-100 dark:focus-visible:ring-neutral-300\"\n          >\n            <IconMore className=\"size-[18px]\" />\n          </button>\n        )}\n        bulkActions={({ ids, remove }) => (\n          <button\n            type=\"button\"\n            onClick={remove}\n            className=\"rounded-full px-2.5 py-1.5 text-sm font-medium text-rose-300 transition-colors duration-100 ease-out hover:bg-white/10 focus-visible:ring-1 focus-visible:ring-current focus-visible:outline-hidden dark:text-rose-700 dark:hover:bg-black/10\"\n          >\n            Remove {ids.length === 1 ? 'member' : `${ids.length} members`}\n          </button>\n        )}\n      />\n    </div>\n  );\n}\n",
      "type": "registry:block",
      "target": "components/spectrumui/blocks/tables/team-members-table.tsx"
    }
  ],
  "type": "registry:block"
}
