Docs
Organization Switcher
Experimental
Organization Switcher
Enables users to easily switch between different organizations or create new ones.
Installation
Install the following dependencies:
npx shadcn-ui@latest add avatar button command popover
Copy and paste the following code into your project.
"use client";
import { Check, ChevronsUpDown } from "lucide-react";
import * as React from "react";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
interface KeyValueMap {
[key: string]: any;
}
type PopoverTriggerProps = React.ComponentPropsWithoutRef<
typeof PopoverTrigger
>;
type Organization = {
id: string;
name: string;
display_name: string;
picture: string;
};
// Ref: https://auth0.com/docs/manage-users/organizations/configure-organizations/define-organization-behavior
enum OrganizationTypeOfUsers {
Deny = "deny",
Require = "require",
Allow = "allow",
}
type SubtitleHandler = string | ((organization: any) => string);
interface OrganizationSwitcherProps extends PopoverTriggerProps {
user: KeyValueMap;
availableOrganizations: Organization[];
loginUrl?: string;
typeOfUsers?: OrganizationTypeOfUsers;
subtitle?: SubtitleHandler;
showBorder?: boolean;
organizationsLabel?: string;
personalAccountLabel?: string;
addOrganizationLabel?: string;
createOrganizationUrl?: string;
returnTo?: string;
}
export default function OrganizationSwitcher({
user,
loginUrl = "/api/auth/login",
typeOfUsers = OrganizationTypeOfUsers.Allow,
subtitle,
showBorder = true,
availableOrganizations,
organizationsLabel = "Organizations",
personalAccountLabel = "Personal Account",
addOrganizationLabel = "Add Organization",
createOrganizationUrl,
returnTo = "/",
}: OrganizationSwitcherProps) {
const groups = [
{
label: personalAccountLabel,
organizations: [
{
type: "personal",
label: user.name,
value: user.sub,
picture: user.picture,
},
],
},
{
label: organizationsLabel,
organizations: availableOrganizations.map((org: Organization) => ({
type: "organization",
label: org.display_name,
value: org.id,
picture: org.picture,
})) as [],
},
];
if (typeOfUsers === "require") {
groups.shift();
}
const [open, setOpen] = React.useState(false);
const [selectedOrg, setSelectedOrg] = React.useState<
(typeof groups)[number]["organizations"][number]
>(
user.org_id
? groups[groups.length - 1].organizations.filter(
(org) => org.value === user.org_id
)[0]
: groups[0].organizations[0]
);
return (
typeOfUsers !== OrganizationTypeOfUsers.Deny && (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
aria-label="Select a team"
className={`w-full justify-between pr-1 pl-2 ${
!showBorder && "border-0"
}`}
>
<div className="flex flex-col items-start">
<span className="text-sm">{selectedOrg.label}</span>
{subtitle && (
<span className="text-gray-500 font-light text-xs">
{typeof subtitle === "string"
? subtitle
: subtitle(selectedOrg)}
</span>
)}
</div>
<ChevronsUpDown size={14} />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command>
<CommandList>
<CommandInput placeholder="Search ..." />
<CommandEmpty>
No {organizationsLabel.toLowerCase()} found.
</CommandEmpty>
</CommandList>
<CommandList>
{groups.map((group) => (
<CommandGroup key={group.label} heading={group.label}>
{group.organizations.map(
(org: {
value: string;
label: string;
picture: string;
type: string;
}) => (
<CommandItem
key={org.value}
onSelect={() => {
setSelectedOrg(org);
setOpen(false);
}}
className="text-sm"
>
<a
href={
org.type === "personal"
? `${loginUrl}?returnTo=${returnTo}`
: `${loginUrl}?organization=${org.value}&returnTo=${returnTo}`
}
className="flex w-full flex items-center"
>
{org.label}
{selectedOrg.value === org.value && (
<Check className={"ml-auto h-4 w-4"} />
)}
</a>
</CommandItem>
)
)}
</CommandGroup>
))}
</CommandList>
{createOrganizationUrl && (
<>
<CommandSeparator />
<CommandList>
<CommandGroup>
<CommandItem>
<a
href={createOrganizationUrl}
className="flex items-center justify-between gap-3 w-full block text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
>
{addOrganizationLabel}
<div className="RightSlot">+</div>
</a>
</CommandItem>
</CommandGroup>
</CommandList>
</>
)}
</Command>
</PopoverContent>
</Popover>
)
);
}