{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "settings",
  "title": "Settings",
  "description": "The settings-page kit: a responsive shell with sticky side tabs on desktop and a native select on mobile, a two-column label/control section idiom, and reference sections for profile, notifications, team management and destructive actions.",
  "dependencies": [
    "@untitledui/icons@^0.0.22"
  ],
  "registryDependencies": [
    "@nx-ui/avatar",
    "@nx-ui/button",
    "@nx-ui/card",
    "@nx-ui/dropdown-menu",
    "@nx-ui/input",
    "@nx-ui/label",
    "@nx-ui/native-select",
    "@nx-ui/select",
    "@nx-ui/separator",
    "@nx-ui/switch",
    "@nx-ui/textarea",
    "@nx-ui/utils"
  ],
  "files": [
    {
      "path": "components/blocks/settings/danger-zone.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { Card, CardContent } from \"@/components/ui/card\"\n\n/** One destructive action row inside {@link DangerZone}. */\nexport type DangerAction = {\n  key: string\n  title: string\n  description?: string\n  actionLabel: string\n  onAction?: () => void\n}\n\n/**\n * DangerZone — the destructive-actions card: a red-bordered {@link Card} with\n * a row per {@link DangerAction} (title/description on the left, a\n * destructive {@link Button} on the right), used for things like leaving an\n * organisation or deleting an account.\n */\nfunction DangerZone({\n  title = \"Danger zone\",\n  actions,\n  className,\n}: {\n  title?: React.ReactNode\n  actions: DangerAction[]\n  className?: string\n}) {\n  return (\n    <Card\n      data-slot=\"danger-zone\"\n      className={cn(\n        \"gap-0 border border-destructive/30 p-0 ring-destructive/20\",\n        className\n      )}\n    >\n      <div className=\"border-b border-destructive/20 p-6\">\n        <h2 className=\"text-md font-semibold text-destructive\">{title}</h2>\n      </div>\n      <CardContent className=\"flex flex-col divide-y divide-destructive/15 p-0\">\n        {actions.map((action) => (\n          <div\n            key={action.key}\n            data-slot=\"danger-zone-row\"\n            className=\"flex flex-col gap-4 px-6 py-5 sm:flex-row sm:items-center sm:justify-between\"\n          >\n            <div className=\"flex flex-col gap-0.5\">\n              <span className=\"text-sm font-semibold text-foreground\">\n                {action.title}\n              </span>\n              {action.description ? (\n                <span className=\"text-sm text-muted-foreground\">\n                  {action.description}\n                </span>\n              ) : null}\n            </div>\n            <Button\n              type=\"button\"\n              variant=\"destructive\"\n              size=\"sm\"\n              className=\"shrink-0 self-start sm:self-auto\"\n              onClick={action.onAction}\n            >\n              {action.actionLabel}\n            </Button>\n          </div>\n        ))}\n      </CardContent>\n    </Card>\n  )\n}\n\nexport { DangerZone }\n",
      "type": "registry:component",
      "target": "components/blocks/settings/danger-zone.tsx"
    },
    {
      "path": "components/blocks/settings/index.ts",
      "content": "/**\n * settings — the settings-page kit.\n *\n * {@link SettingsShell} lays out a sticky side tab nav on desktop, collapsing\n * to a `NativeSelect` on mobile. Compose {@link SettingsSection} /\n * {@link SettingsRow} for custom cards, or use the reference sections\n * ({@link ProfileSection}, {@link NotificationsSection}, {@link TeamSection},\n * {@link DangerZone}) as a starting point for a real settings page.\n */\n\nexport { SettingsShell, type SettingsTab } from \"./settings-shell\"\nexport { SettingsSection, SettingsRow } from \"./settings-section\"\nexport { ProfileSection } from \"./profile-section\"\nexport {\n  NotificationsSection,\n  type NotificationOption,\n  type NotificationGroup,\n} from \"./notifications-section\"\nexport { TeamSection, type TeamMember } from \"./team-section\"\nexport { DangerZone, type DangerAction } from \"./danger-zone\"\n",
      "type": "registry:component",
      "target": "components/blocks/settings/index.ts"
    },
    {
      "path": "components/blocks/settings/notifications-section.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Switch } from \"@/components/ui/switch\"\nimport { SettingsSection, SettingsRow } from \"./settings-section\"\n\n/** One switch row inside a {@link NotificationsSection} group. */\nexport type NotificationOption = {\n  key: string\n  label: string\n  description?: string\n  defaultChecked?: boolean\n}\n\n/** A titled group of {@link NotificationOption} switch rows. */\nexport type NotificationGroup = {\n  title: string\n  options: NotificationOption[]\n}\n\n/**\n * NotificationsSection — grouped switch rows for notification preferences,\n * each group getting its own sub-heading inside one {@link SettingsSection}\n * card. Uncontrolled by default (`defaultChecked`); pass `onOptionChange` to\n * observe toggles.\n */\nfunction NotificationsSection({\n  groups,\n  onOptionChange,\n  className,\n}: {\n  groups: NotificationGroup[]\n  onOptionChange?: (key: string, checked: boolean) => void\n  className?: string\n}) {\n  return (\n    <SettingsSection\n      title=\"Notifications\"\n      description=\"Choose which exchange and compliance events email or alert you.\"\n      className={className}\n    >\n      {groups.map((group) => (\n        <div key={group.title} className=\"flex flex-col\">\n          <div className=\"px-6 pt-5 pb-1\">\n            <h3 className=\"text-sm font-semibold text-foreground\">\n              {group.title}\n            </h3>\n          </div>\n          {group.options.map((option) => (\n            <SettingsRow\n              key={option.key}\n              label={option.label}\n              description={option.description}\n              className={cn(\"sm:items-center\")}\n            >\n              <Switch\n                defaultChecked={option.defaultChecked}\n                onCheckedChange={(checked) =>\n                  onOptionChange?.(option.key, checked)\n                }\n                aria-label={option.label}\n                className=\"ml-auto sm:ml-0\"\n              />\n            </SettingsRow>\n          ))}\n        </div>\n      ))}\n    </SettingsSection>\n  )\n}\n\nexport { NotificationsSection }\n",
      "type": "registry:component",
      "target": "components/blocks/settings/notifications-section.tsx"
    },
    {
      "path": "components/blocks/settings/profile-section.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { UploadCloud01 } from \"@untitledui/icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport { Button } from \"@/components/ui/button\"\nimport { Input } from \"@/components/ui/input\"\nimport { Label } from \"@/components/ui/label\"\nimport { Textarea } from \"@/components/ui/textarea\"\nimport { SettingsSection, SettingsRow } from \"./settings-section\"\n\n/**\n * ProfileSection — a reference {@link SettingsSection} composition: avatar\n * upload placeholder plus name/email/bio fields. Values are uncontrolled\n * (`defaultValue`) unless the consumer wires its own state via `onFieldChange`\n * — this is a layout/copy reference, not a form library.\n */\nfunction ProfileSection({\n  name = \"Priya Shah\",\n  email = \"priya@nexusregen.com\",\n  avatarSrc,\n  onAvatarChange,\n  className,\n}: {\n  name?: string\n  email?: string\n  avatarSrc?: string\n  onAvatarChange?: () => void\n  className?: string\n}) {\n  return (\n    <SettingsSection\n      title=\"Profile\"\n      description=\"This information will appear on waste transfer notes and shared with your collection partners.\"\n      className={className}\n    >\n      <SettingsRow\n        label=\"Photo\"\n        description=\"Shown on your profile and in the org member list.\"\n      >\n        <div className=\"flex items-center gap-4\">\n          <Avatar size=\"lg\">\n            {avatarSrc ? <AvatarImage src={avatarSrc} alt={name} /> : null}\n            <AvatarFallback>\n              {name\n                .split(\" \")\n                .map((part) => part[0])\n                .join(\"\")\n                .slice(0, 2)}\n            </AvatarFallback>\n          </Avatar>\n          <Button type=\"button\" variant=\"secondary\" size=\"sm\" onClick={onAvatarChange}>\n            <UploadCloud01 />\n            Upload photo\n          </Button>\n        </div>\n      </SettingsRow>\n\n      <SettingsRow label=\"Full name\">\n        <Label htmlFor=\"settings-profile-name\" className=\"sr-only\">\n          Full name\n        </Label>\n        <Input id=\"settings-profile-name\" defaultValue={name} autoComplete=\"name\" />\n      </SettingsRow>\n\n      <SettingsRow\n        label=\"Email address\"\n        description=\"Used for compliance notices and exchange notifications.\"\n      >\n        <Label htmlFor=\"settings-profile-email\" className=\"sr-only\">\n          Email address\n        </Label>\n        <Input\n          id=\"settings-profile-email\"\n          type=\"email\"\n          defaultValue={email}\n          autoComplete=\"email\"\n        />\n      </SettingsRow>\n\n      <SettingsRow\n        label=\"Bio\"\n        description=\"A short line describing your role — optional.\"\n      >\n        <Textarea\n          placeholder=\"Site manager overseeing waste transfer compliance.\"\n          className={cn(\"min-h-20\")}\n        />\n      </SettingsRow>\n    </SettingsSection>\n  )\n}\n\nexport { ProfileSection }\n",
      "type": "registry:component",
      "target": "components/blocks/settings/profile-section.tsx"
    },
    {
      "path": "components/blocks/settings/settings-section.tsx",
      "content": "import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { Separator } from \"@/components/ui/separator\"\n\n/**\n * SettingsSection — a titled card for one settings group, in the UUI\n * label-left / control-right pattern: a fixed-width description column on the\n * left (title + description), the control(s) on the right. Use {@link\n * SettingsRow} for each label/control pair inside; rows are separated by a\n * divider automatically.\n *\n * @example\n * <SettingsSection title=\"Profile\" description=\"Update your photo and personal details.\">\n *   <SettingsRow label=\"Name\" description=\"Shown across the workspace.\">\n *     <Input defaultValue=\"Priya Shah\" />\n *   </SettingsRow>\n * </SettingsSection>\n */\nfunction SettingsSection({\n  title,\n  description,\n  actions,\n  children,\n  className,\n}: {\n  title: React.ReactNode\n  description?: React.ReactNode\n  actions?: React.ReactNode\n  children: React.ReactNode\n  className?: string\n}) {\n  return (\n    <Card data-slot=\"settings-section\" className={cn(\"gap-0 p-0\", className)}>\n      <div className=\"flex flex-col gap-4 border-b border-border p-6 sm:flex-row sm:items-start sm:justify-between\">\n        <div className=\"flex max-w-sm flex-col gap-1\">\n          <h2 className=\"text-md font-semibold text-foreground\">{title}</h2>\n          {description ? (\n            <p className=\"text-sm text-muted-foreground\">{description}</p>\n          ) : null}\n        </div>\n        {actions ? (\n          <div className=\"flex shrink-0 items-center gap-3\">{actions}</div>\n        ) : null}\n      </div>\n      <CardContent className=\"flex flex-col p-0\">{children}</CardContent>\n    </Card>\n  )\n}\n\n/**\n * SettingsRow — one label/control pair inside a {@link SettingsSection}, the\n * UUI two-column idiom (label + description on the left, control on the\n * right), divided from the next row.\n */\nfunction SettingsRow({\n  label,\n  description,\n  children,\n  className,\n}: {\n  label: React.ReactNode\n  description?: React.ReactNode\n  children: React.ReactNode\n  className?: string\n}) {\n  return (\n    <>\n      <div\n        data-slot=\"settings-row\"\n        className={cn(\n          \"flex flex-col gap-4 px-6 py-5 sm:flex-row sm:items-center sm:gap-6\",\n          className\n        )}\n      >\n        <div className=\"flex flex-col gap-0.5 sm:w-56 sm:shrink-0\">\n          <span className=\"text-sm font-semibold text-foreground\">\n            {label}\n          </span>\n          {description ? (\n            <span className=\"text-sm text-muted-foreground\">\n              {description}\n            </span>\n          ) : null}\n        </div>\n        <div className=\"flex flex-1 flex-col gap-2 sm:max-w-md\">\n          {children}\n        </div>\n      </div>\n      <Separator className=\"last:hidden\" />\n    </>\n  )\n}\n\nexport { SettingsSection, SettingsRow }\n",
      "type": "registry:component",
      "target": "components/blocks/settings/settings-section.tsx"
    },
    {
      "path": "components/blocks/settings/settings-shell.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport {\n  NativeSelect,\n  NativeSelectOption,\n} from \"@/components/ui/native-select\"\n\n/** One entry in the settings side nav. */\nexport type SettingsTab = {\n  value: string\n  label: string\n  icon?: React.ComponentType<{ className?: string }>\n}\n\n/**\n * SettingsShell — the standard settings-page layout: a sticky side tab nav on\n * desktop (`sm:` and up), collapsing to a `NativeSelect` on mobile so the\n * whole page never needs horizontal tab scrolling. Controlled: pass `tabs`,\n * `value` and `onValueChange`; render the active section as `children`.\n *\n * @example\n * <SettingsShell\n *   tabs={[{ value: \"profile\", label: \"Profile\" }, { value: \"team\", label: \"Team\" }]}\n *   value={tab}\n *   onValueChange={setTab}\n * >\n *   {tab === \"profile\" ? <ProfileSection /> : <TeamSection />}\n * </SettingsShell>\n */\nfunction SettingsShell({\n  tabs,\n  value,\n  onValueChange,\n  children,\n  className,\n}: {\n  tabs: SettingsTab[]\n  value: string\n  onValueChange: (value: string) => void\n  children: React.ReactNode\n  className?: string\n}) {\n  return (\n    <div\n      data-slot=\"settings-shell\"\n      className={cn(\"flex flex-col gap-6 sm:flex-row sm:items-start sm:gap-10\", className)}\n    >\n      <div className=\"sm:hidden\">\n        <NativeSelect\n          className=\"w-full\"\n          value={value}\n          onChange={(event) => onValueChange(event.target.value)}\n          aria-label=\"Settings section\"\n        >\n          {tabs.map((tab) => (\n            <NativeSelectOption key={tab.value} value={tab.value}>\n              {tab.label}\n            </NativeSelectOption>\n          ))}\n        </NativeSelect>\n      </div>\n\n      <nav\n        data-slot=\"settings-nav\"\n        aria-label=\"Settings section\"\n        className=\"hidden shrink-0 sm:sticky sm:top-6 sm:flex sm:w-56 sm:flex-col sm:gap-0.5\"\n      >\n        {tabs.map((tab) => {\n          const Icon = tab.icon\n          const active = tab.value === value\n          return (\n            <button\n              key={tab.value}\n              type=\"button\"\n              data-slot=\"settings-nav-item\"\n              data-active={active}\n              onClick={() => onValueChange(tab.value)}\n              className={cn(\n                \"flex cursor-pointer items-center gap-2 rounded-lg px-3 py-2 text-left text-sm font-semibold text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\",\n                active && \"bg-muted text-foreground\"\n              )}\n            >\n              {Icon ? <Icon className=\"size-4 shrink-0\" /> : null}\n              {tab.label}\n            </button>\n          )\n        })}\n      </nav>\n\n      <div className=\"min-w-0 flex-1\">{children}</div>\n    </div>\n  )\n}\n\nexport { SettingsShell }\n",
      "type": "registry:component",
      "target": "components/blocks/settings/settings-shell.tsx"
    },
    {
      "path": "components/blocks/settings/team-section.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { DotsVertical, Mail01 } from \"@untitledui/icons\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport { Button } from \"@/components/ui/button\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\"\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\"\nimport { SettingsSection } from \"./settings-section\"\n\n/** A team member row in a {@link TeamSection}. */\nexport type TeamMember = {\n  id: string\n  name: string\n  email: string\n  role: string\n  avatarSrc?: string\n}\n\nconst DEFAULT_ROLES = [\"Owner\", \"Admin\", \"Member\", \"Viewer\"]\n\n/**\n * TeamSection — organisation member management: a row per {@link TeamMember}\n * (avatar, name/email, a role {@link Select} and an overflow menu), plus an\n * invite-by-email row. Role changes and removal are reported via callbacks;\n * the invite row is uncontrolled.\n */\nfunction TeamSection({\n  members,\n  roles = DEFAULT_ROLES,\n  onRoleChange,\n  onRemoveMember,\n  onInvite,\n  className,\n}: {\n  members: TeamMember[]\n  roles?: string[]\n  onRoleChange?: (memberId: string, role: string) => void\n  onRemoveMember?: (memberId: string) => void\n  onInvite?: (email: string) => void\n  className?: string\n}) {\n  const [inviteEmail, setInviteEmail] = React.useState(\"\")\n\n  return (\n    <SettingsSection\n      title=\"Team members\"\n      description=\"Manage who can access this organisation's waste-carrier licence and exchange records.\"\n      className={className}\n    >\n      <div className=\"flex flex-col divide-y divide-border\">\n        {members.map((member) => (\n          <div\n            key={member.id}\n            data-slot=\"team-member-row\"\n            className=\"flex items-center gap-3 px-6 py-4\"\n          >\n            <Avatar>\n              {member.avatarSrc ? (\n                <AvatarImage src={member.avatarSrc} alt={member.name} />\n              ) : null}\n              <AvatarFallback>\n                {member.name\n                  .split(\" \")\n                  .map((part) => part[0])\n                  .join(\"\")\n                  .slice(0, 2)}\n              </AvatarFallback>\n            </Avatar>\n            <div className=\"flex min-w-0 flex-1 flex-col\">\n              <span className=\"truncate text-sm font-semibold text-foreground\">\n                {member.name}\n              </span>\n              <span className=\"truncate text-sm text-muted-foreground\">\n                {member.email}\n              </span>\n            </div>\n            <Select\n              value={member.role}\n              onValueChange={(role) => onRoleChange?.(member.id, role as string)}\n            >\n              <SelectTrigger size=\"sm\" className=\"w-32\">\n                <SelectValue />\n              </SelectTrigger>\n              <SelectContent>\n                {roles.map((role) => (\n                  <SelectItem key={role} value={role}>\n                    {role}\n                  </SelectItem>\n                ))}\n              </SelectContent>\n            </Select>\n            <DropdownMenu>\n              <DropdownMenuTrigger\n                render={\n                  <Button\n                    type=\"button\"\n                    variant=\"ghost\"\n                    size=\"icon-sm\"\n                    aria-label={`Manage ${member.name}`}\n                  >\n                    <DotsVertical />\n                  </Button>\n                }\n              />\n              <DropdownMenuContent align=\"end\">\n                <DropdownMenuItem\n                  variant=\"destructive\"\n                  onClick={() => onRemoveMember?.(member.id)}\n                >\n                  Remove from organisation\n                </DropdownMenuItem>\n              </DropdownMenuContent>\n            </DropdownMenu>\n          </div>\n        ))}\n      </div>\n\n      <div\n        data-slot=\"team-invite-row\"\n        className={cn(\n          \"flex flex-col gap-3 border-t border-border px-6 py-4 sm:flex-row sm:items-center\"\n        )}\n      >\n        <Input\n          type=\"email\"\n          placeholder=\"colleague@nexusregen.com\"\n          value={inviteEmail}\n          onChange={(event) => setInviteEmail(event.target.value)}\n          className=\"sm:flex-1\"\n        />\n        <Button\n          type=\"button\"\n          variant=\"secondary\"\n          onClick={() => {\n            if (!inviteEmail) return\n            onInvite?.(inviteEmail)\n            setInviteEmail(\"\")\n          }}\n        >\n          <Mail01 />\n          Invite\n        </Button>\n      </div>\n    </SettingsSection>\n  )\n}\n\nexport { TeamSection }\n",
      "type": "registry:component",
      "target": "components/blocks/settings/team-section.tsx"
    }
  ],
  "type": "registry:block"
}