{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "enterprise-workspace-switcher",
  "title": "Enterprise Workspace Switcher",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/enterprise-workspace-switcher/EnterpriseWorkspaceSwitcher.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport { Building2, Check, ChevronsUpDown, Globe, LogOut, Plus, Settings, UserPlus } from 'lucide-vue-next'\nimport { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from '@/components/ui/dialog'\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuGroup,\n  DropdownMenuItem,\n  DropdownMenuLabel,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu'\nimport { Input } from '@/components/ui/input'\nimport { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'\nimport { cn } from '@/lib/utils'\n\nexport interface WorkspaceItem {\n  id: string\n  name: string\n  slug?: string\n  plan: string\n  planTier: string\n  members: number\n  region: string\n  regionCode: string\n  role: 'Owner' | 'Admin' | 'Member' | 'Personal'\n  color: string\n  initials: string\n}\n\nexport interface UserInfo {\n  name: string\n  email: string\n  avatar?: string\n}\n\ninterface Props {\n  initialWorkspaces?: WorkspaceItem[]\n  defaultActiveId?: string\n  user?: UserInfo\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  initialWorkspaces: () => [\n    {\n      id: 'ws-1',\n      name: 'UIPKGE Enterprise Inc.',\n      slug: 'uipkge-enterprise',\n      plan: 'Enterprise Scale',\n      planTier: 'Enterprise Plan',\n      members: 64,\n      region: 'US East (N. Virginia)',\n      regionCode: 'US-East',\n      role: 'Owner',\n      color: 'bg-primary/10 text-primary border border-primary/20',\n      initials: 'UE',\n    },\n    {\n      id: 'ws-2',\n      name: 'Acme Design Systems Lab',\n      slug: 'acme-design',\n      plan: 'Pro Team',\n      planTier: 'Pro Team',\n      members: 18,\n      region: 'EU Central (Frankfurt)',\n      regionCode: 'EU-Central',\n      role: 'Admin',\n      color: 'bg-info/10 text-info border border-info/20',\n      initials: 'AD',\n    },\n    {\n      id: 'ws-3',\n      name: 'Personal Sandbox',\n      slug: 'personal-sandbox',\n      plan: 'Developer Tier',\n      planTier: 'Free Developer Tier',\n      members: 1,\n      region: 'Global Edge',\n      regionCode: 'Global',\n      role: 'Personal',\n      color: 'bg-success/10 text-success border border-success/20',\n      initials: 'PS',\n    },\n  ],\n  defaultActiveId: 'ws-1',\n  user: () => ({\n    name: 'Elena Rostova',\n    email: 'elena@uipkge.dev',\n    avatar: '',\n  }),\n})\n\nconst emit = defineEmits<{\n  (e: 'change', workspace: WorkspaceItem): void\n  (e: 'create', workspace: WorkspaceItem): void\n  (e: 'action', key: 'settings' | 'billing' | 'invite' | 'logout'): void\n}>()\n\nconst workspaces = ref<WorkspaceItem[]>([...props.initialWorkspaces])\nconst activeWorkspaceId = ref<string>(props.defaultActiveId)\nconst isCreateDialogOpen = ref(false)\n\nconst activeWorkspace = computed(() => {\n  return (\n    workspaces.value.find((w) => w.id === activeWorkspaceId.value) ||\n    workspaces.value[0] || {\n      id: 'ws-fallback',\n      name: 'Workspace',\n      plan: 'Standard',\n      planTier: 'Standard',\n      members: 1,\n      region: 'US-East',\n      regionCode: 'US-East',\n      role: 'Owner' as const,\n      color: 'bg-muted text-foreground border border-border',\n      initials: 'WS',\n    }\n  )\n})\n\n// Dialog Form State\nconst newOrgName = ref('Stripe Developer Platform')\nconst newOrgRegion = ref('us-east')\nconst newOrgPlan = ref('pro')\n\nconst slugPreview = computed(() => {\n  const cleaned = newOrgName.value\n    .trim()\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '-')\n    .replace(/^-+|-+$/g, '')\n  return cleaned || 'stripe-dev'\n})\n\nfunction selectWorkspace(workspace: WorkspaceItem) {\n  activeWorkspaceId.value = workspace.id\n  emit('change', workspace)\n}\n\nfunction handleOpenCreateDialog() {\n  isCreateDialogOpen.value = true\n}\n\nfunction handleCreateWorkspace() {\n  if (!newOrgName.value.trim()) return\n\n  const regionMap: Record<string, { full: string; code: string }> = {\n    'us-east': { full: 'US East (N. Virginia)', code: 'US-East' },\n    'eu-central': { full: 'EU Central (Frankfurt)', code: 'EU-Central' },\n    'ap-south': { full: 'AP South (Mumbai)', code: 'AP-South' },\n  }\n\n  const regionInfo = regionMap[newOrgRegion.value] || {\n    full: 'US East (N. Virginia)',\n    code: 'US-East',\n  }\n\n  const parts = newOrgName.value.trim().split(/\\s+/)\n  const initials =\n    parts.length > 1\n      ? `${parts[0]?.[0] || ''}${parts[1]?.[0] || ''}`.toUpperCase()\n      : (parts[0]?.slice(0, 2) || 'WS').toUpperCase()\n\n  const newWs: WorkspaceItem = {\n    id: `ws-${Date.now()}`,\n    name: newOrgName.value.trim(),\n    slug: slugPreview.value,\n    plan: newOrgPlan.value === 'enterprise' ? 'Enterprise Scale' : 'Pro Team',\n    planTier: newOrgPlan.value === 'enterprise' ? 'Enterprise Custom' : 'Pro Team',\n    members: 1,\n    region: regionInfo.full,\n    regionCode: regionInfo.code,\n    role: 'Owner',\n    color:\n      newOrgPlan.value === 'enterprise'\n        ? 'bg-primary/10 text-primary border border-primary/20'\n        : 'bg-info/10 text-info border border-info/20',\n    initials,\n  }\n\n  workspaces.value.unshift(newWs)\n  activeWorkspaceId.value = newWs.id\n  emit('create', newWs)\n  emit('change', newWs)\n\n  // Reset form\n  newOrgName.value = ''\n  isCreateDialogOpen.value = false\n}\n</script>\n\n<template>\n  <div data-slot=\"enterprise-workspace-switcher\" :class=\"cn('w-full', props.class)\">\n    <DropdownMenu>\n      <DropdownMenuTrigger as-child>\n        <button\n          type=\"button\"\n          class=\"group border-border/80 bg-card hover:bg-accent/40 focus-visible:ring-ring flex w-full items-center gap-3 rounded-xl border p-2.5 text-left shadow-xs transition-[background-color,box-shadow] duration-150 focus-visible:ring-2 focus-visible:outline-none\"\n          aria-label=\"Select organization workspace\"\n        >\n          <!-- Org Avatar / Monogram with Emerald Ring -->\n          <div class=\"relative size-10 shrink-0\">\n            <div\n              :class=\"\n                cn(\n                  'flex size-10 items-center justify-center rounded-lg text-sm font-semibold shadow-2xs',\n                  activeWorkspace.color,\n                )\n              \"\n            >\n              {{ activeWorkspace.initials }}\n            </div>\n            <span\n              class=\"border-card bg-success absolute -top-0.5 -right-0.5 size-2.5 rounded-full border-2\"\n              title=\"Active Organization\"\n              aria-label=\"Active workspace status: Online\"\n            />\n          </div>\n\n          <!-- Active Org Info & Role Badge -->\n          <div class=\"min-w-0 flex-1\">\n            <div class=\"flex items-center gap-1.5\">\n              <span class=\"text-foreground truncate text-sm font-semibold tracking-tight\">\n                {{ activeWorkspace.name }}\n              </span>\n              <Badge variant=\"secondary\" class=\"shrink-0 px-1.5 py-0 text-xs font-medium\">\n                {{ activeWorkspace.role }}\n              </Badge>\n            </div>\n            <div class=\"text-muted-foreground mt-0.5 flex items-center gap-1.5 truncate text-xs\">\n              <span class=\"truncate font-medium\">Tier: {{ activeWorkspace.plan }}</span>\n              <span class=\"shrink-0\">{{ activeWorkspace.regionCode }}</span>\n            </div>\n          </div>\n\n          <!-- Chevron Icon -->\n          <ChevronsUpDown\n            class=\"text-muted-foreground/70 group-hover:text-foreground size-4 shrink-0 transition-colors\"\n          />\n        </button>\n      </DropdownMenuTrigger>\n\n      <DropdownMenuContent\n        class=\"border-border bg-popover w-80 rounded-xl p-2 shadow-xl sm:w-96\"\n        align=\"start\"\n        :side-offset=\"6\"\n      >\n        <!-- User Header Profile Info -->\n        <DropdownMenuLabel class=\"p-0 font-normal\">\n          <div class=\"bg-muted/40 flex items-center gap-2.5 rounded-lg px-2.5 py-2 text-left\">\n            <Avatar class=\"size-8 rounded-full border\">\n              <AvatarImage v-if=\"user.avatar\" :src=\"user.avatar\" :alt=\"user.name\" />\n              <AvatarFallback class=\"bg-primary/10 text-primary text-xs font-bold\">\n                {{\n                  user.name\n                    .split(' ')\n                    .map((p) => p[0])\n                    .join('')\n                    .toUpperCase() || 'U'\n                }}\n              </AvatarFallback>\n            </Avatar>\n            <div class=\"grid min-w-0 flex-1 text-left leading-tight\">\n              <span class=\"text-muted-foreground text-xs font-medium\">Signed in as</span>\n              <span class=\"text-foreground truncate text-xs font-semibold\">{{ user.email }}</span>\n            </div>\n            <div class=\"flex items-center gap-1\">\n              <span class=\"bg-success size-2 rounded-full\" />\n              <span class=\"text-muted-foreground text-xs font-medium\">Ready</span>\n            </div>\n          </div>\n        </DropdownMenuLabel>\n\n        <DropdownMenuSeparator class=\"my-1.5\" />\n\n        <!-- Workspaces Section -->\n        <div class=\"flex items-center justify-between px-2 py-1\">\n          <span class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\"> Workspaces </span>\n          <span class=\"text-muted-foreground/70 text-xs\">{{ workspaces.length }} total</span>\n        </div>\n\n        <div class=\"my-1 space-y-1\">\n          <DropdownMenuItem\n            v-for=\"ws in workspaces\"\n            :key=\"ws.id\"\n            class=\"hover:bg-accent focus:bg-accent flex cursor-pointer items-center gap-3 rounded-lg p-2 transition-colors\"\n            @select=\"selectWorkspace(ws)\"\n          >\n            <div\n              :class=\"\n                cn(\n                  'flex size-8 shrink-0 items-center justify-center rounded-lg text-xs font-semibold shadow-2xs',\n                  ws.color,\n                )\n              \"\n            >\n              {{ ws.initials }}\n            </div>\n\n            <div class=\"min-w-0 flex-1\">\n              <div class=\"flex items-center gap-1.5\">\n                <span\n                  :class=\"\n                    cn(\n                      'truncate text-sm font-medium',\n                      activeWorkspaceId === ws.id ? 'text-foreground font-semibold' : 'text-foreground/90',\n                    )\n                  \"\n                >\n                  {{ ws.name }}\n                </span>\n                <Badge v-if=\"ws.role === 'Owner'\" variant=\"secondary\" class=\"shrink-0 px-1 py-0 text-xs font-medium\">\n                  Owner\n                </Badge>\n                <Badge v-else-if=\"ws.role === 'Admin'\" variant=\"outline\" class=\"shrink-0 px-1 py-0 text-xs font-medium\">\n                  Admin\n                </Badge>\n              </div>\n              <p class=\"text-muted-foreground truncate text-xs\">\n                {{ ws.planTier }} · {{ ws.members }} Member{{ ws.members === 1 ? '' : 's' }} ·\n                {{ ws.regionCode }}\n              </p>\n            </div>\n\n            <Check v-if=\"activeWorkspaceId === ws.id\" class=\"text-success size-4 shrink-0\" aria-hidden=\"true\" />\n          </DropdownMenuItem>\n        </div>\n\n        <DropdownMenuSeparator class=\"my-1.5\" />\n\n        <!-- Actions -->\n        <DropdownMenuGroup>\n          <DropdownMenuItem\n            class=\"text-foreground hover:bg-accent focus:bg-accent flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 text-sm transition-colors\"\n            @select=\"handleOpenCreateDialog\"\n          >\n            <div\n              class=\"border-primary/30 bg-primary/10 text-primary flex size-6 items-center justify-center rounded-md border\"\n            >\n              <Plus class=\"size-3.5\" />\n            </div>\n            <span class=\"font-medium\">Create New Workspace</span>\n          </DropdownMenuItem>\n\n          <DropdownMenuItem\n            class=\"text-muted-foreground hover:text-foreground hover:bg-accent focus:bg-accent flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 text-sm transition-colors\"\n            @select=\"emit('action', 'settings')\"\n          >\n            <div class=\"border-border bg-muted flex size-6 items-center justify-center rounded-md border\">\n              <Settings class=\"size-3.5\" />\n            </div>\n            <span>Organization Settings & Billing</span>\n          </DropdownMenuItem>\n\n          <DropdownMenuItem\n            class=\"text-muted-foreground hover:text-foreground hover:bg-accent focus:bg-accent flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 text-sm transition-colors\"\n            @select=\"emit('action', 'invite')\"\n          >\n            <div class=\"border-border bg-muted flex size-6 items-center justify-center rounded-md border\">\n              <UserPlus class=\"size-3.5\" />\n            </div>\n            <span>Invite Team Members</span>\n          </DropdownMenuItem>\n        </DropdownMenuGroup>\n\n        <DropdownMenuSeparator class=\"my-1.5\" />\n\n        <!-- Logout -->\n        <DropdownMenuItem\n          class=\"text-destructive hover:bg-destructive/10 focus:bg-destructive/10 focus:text-destructive flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 text-sm font-medium transition-colors\"\n          @select=\"emit('action', 'logout')\"\n        >\n          <div\n            class=\"border-destructive/20 bg-destructive/10 text-destructive flex size-6 items-center justify-center rounded-md border\"\n          >\n            <LogOut class=\"size-3.5\" />\n          </div>\n          <span>Log out</span>\n        </DropdownMenuItem>\n      </DropdownMenuContent>\n    </DropdownMenu>\n\n    <!-- Create New Workspace Modal Dialog -->\n    <Dialog v-model:open=\"isCreateDialogOpen\">\n      <DialogContent class=\"border-border bg-background rounded-2xl p-6 shadow-sm sm:max-w-lg\">\n        <DialogHeader class=\"space-y-2 text-left\">\n          <div\n            class=\"border-primary/20 bg-primary/10 text-primary flex size-10 items-center justify-center rounded-xl border shadow-xs\"\n          >\n            <Building2 class=\"size-5\" />\n          </div>\n          <DialogTitle class=\"text-foreground text-lg font-bold tracking-tight\"> Create New Workspace </DialogTitle>\n          <DialogDescription class=\"text-muted-foreground text-sm leading-relaxed\">\n            Spin up a dedicated organization workspace with multi-region routing and isolated billing.\n          </DialogDescription>\n        </DialogHeader>\n\n        <form class=\"space-y-4 py-2\" @submit.prevent=\"handleCreateWorkspace\">\n          <!-- Workspace Name -->\n          <div class=\"space-y-1.5\">\n            <label for=\"new-workspace-name\" class=\"text-foreground text-sm font-medium\"> Workspace Name </label>\n            <Input\n              id=\"new-workspace-name\"\n              v-model=\"newOrgName\"\n              placeholder=\"e.g. Stripe Developer Platform\"\n              autocomplete=\"off\"\n              class=\"h-10 text-sm\"\n              required\n            />\n          </div>\n\n          <!-- URL Slug Preview -->\n          <div class=\"space-y-1.5\">\n            <label class=\"text-muted-foreground text-xs font-medium\"> Workspace URL Slug </label>\n            <div class=\"border-border bg-muted/40 flex items-center gap-2 rounded-lg border px-3 py-2 text-xs\">\n              <Globe class=\"text-muted-foreground size-4 shrink-0\" aria-hidden=\"true\" />\n              <span class=\"text-muted-foreground\">uipkge.dev/</span>\n              <span class=\"text-foreground truncate font-semibold\">{{ slugPreview }}</span>\n            </div>\n          </div>\n\n          <!-- Primary Region Selection -->\n          <div class=\"space-y-1.5\">\n            <label class=\"text-foreground text-sm font-medium\"> Primary Region </label>\n            <Select v-model=\"newOrgRegion\">\n              <SelectTrigger class=\"h-10 w-full\">\n                <SelectValue placeholder=\"Select primary region\" />\n              </SelectTrigger>\n              <SelectContent>\n                <SelectItem value=\"us-east\"> US East (N. Virginia) </SelectItem>\n                <SelectItem value=\"eu-central\"> EU Central (Frankfurt) </SelectItem>\n                <SelectItem value=\"ap-south\"> AP South (Mumbai) </SelectItem>\n              </SelectContent>\n            </Select>\n            <p class=\"text-muted-foreground text-xs\">\n              Primary cluster location for low-latency queries and database replication.\n            </p>\n          </div>\n\n          <!-- Plan Selection Radios -->\n          <div class=\"space-y-2\">\n            <label class=\"text-foreground text-sm font-medium\"> Choose Workspace Plan </label>\n            <RadioGroup v-model=\"newOrgPlan\" class=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n              <label\n                for=\"plan-pro\"\n                :class=\"\n                  cn(\n                    'border-border/80 hover:border-primary/50 relative flex cursor-pointer flex-col justify-between rounded-xl border p-3.5 shadow-xs transition-colors',\n                    newOrgPlan === 'pro' && 'border-primary bg-primary/5 ring-primary/40 shadow-sm ring-1',\n                  )\n                \"\n              >\n                <div class=\"flex items-start justify-between gap-2\">\n                  <div class=\"space-y-0.5\">\n                    <span class=\"text-foreground text-sm font-semibold\">Pro Team</span>\n                    <p class=\"text-primary text-xs font-medium\">$29 / month</p>\n                  </div>\n                  <RadioGroupItem id=\"plan-pro\" value=\"pro\" class=\"mt-0.5\" />\n                </div>\n                <p class=\"text-muted-foreground mt-2 text-xs leading-relaxed\">\n                  Up to 25 members, standard 99.9% SLA, and daily automated backups.\n                </p>\n              </label>\n\n              <label\n                for=\"plan-enterprise\"\n                :class=\"\n                  cn(\n                    'border-border/80 hover:border-primary/50 relative flex cursor-pointer flex-col justify-between rounded-xl border p-3.5 shadow-xs transition-colors',\n                    newOrgPlan === 'enterprise' && 'border-primary bg-primary/5 ring-primary/40 shadow-sm ring-1',\n                  )\n                \"\n              >\n                <div class=\"flex items-start justify-between gap-2\">\n                  <div class=\"space-y-0.5\">\n                    <span class=\"text-foreground text-sm font-semibold\">Enterprise Scale</span>\n                    <p class=\"text-primary text-xs font-medium\">Custom billing</p>\n                  </div>\n                  <RadioGroupItem id=\"plan-enterprise\" value=\"enterprise\" class=\"mt-0.5\" />\n                </div>\n                <p class=\"text-muted-foreground mt-2 text-xs leading-relaxed\">\n                  Unlimited members, dedicated VPC peering, 99.99% uptime SLA & SSO.\n                </p>\n              </label>\n            </RadioGroup>\n          </div>\n\n          <DialogFooter class=\"border-border/60 flex gap-2 border-t pt-4 sm:justify-end\">\n            <Button type=\"button\" variant=\"outline\" @click=\"isCreateDialogOpen = false\"> Cancel </Button>\n            <Button type=\"submit\" :disabled=\"!newOrgName.trim()\"> Create Workspace </Button>\n          </DialogFooter>\n        </form>\n      </DialogContent>\n    </Dialog>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/EnterpriseWorkspaceSwitcher.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/avatar.json",
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/dialog.json",
    "https://uipkge.dev/r/vue/dropdown-menu.json",
    "https://uipkge.dev/r/vue/input.json",
    "https://uipkge.dev/r/vue/radio-group.json",
    "https://uipkge.dev/r/vue/select.json"
  ],
  "description": "Multi-organization workspace switcher dropdown and creation modal inspired by Portico/Riter. Features active organization display with emerald ring, 3 tier levels (Enterprise Scale, Pro Team, Free Sandbox), team role badges, member counts, and an organization creation dialog with dynamic slug preview, region selector, and tier plan radios.",
  "categories": [
    "layout",
    "dashboard",
    "navigation",
    "overlay"
  ]
}