Shared app switcher and multi-account components for AdPal products
npm install @adpal/app-switcherShared app switcher and multi-account components for AdPal products. Provides a Google Workspace-style header with app grid and multi-account support.
- AppSwitcher - Google Workspace-style app grid for switching between AdPal products
- AccountDropdown - Multi-account support with account switching
- AccountAvatar - Reusable avatar component with loading states and fallbacks
- Header - Combined header component with app switcher and account menu
- useLinkedAccounts - Hook for cross-subdomain account persistence
- Custom SVG Icons - Gradient icons with glow effects (BizIntel, Data, Voice, MCP)
``bashFrom GitHub (recommended for private repos)
npm install github:Data-Subsystems/adpal-app-switcher
Peer Dependencies
-
react >= 18.0.0
- react-dom >= 18.0.0
- next >= 14.0.0
- next-auth >= 4.0.0Quick Start
$3
`bash
npm install github:Data-Subsystems/adpal-app-switcher
`$3
`tsx
// components/layout/Header.tsx
'use client';import { useSession, signOut } from 'next-auth/react';
import { useRouter } from 'next/navigation';
import { Header as SharedHeader, themes } from '@adpal/app-switcher';
export function Header() {
const router = useRouter();
const { data: session, status } = useSession();
const handleSwitchAccount = async (account: { email: string }) => {
await signOut({ redirect: false });
const response = await fetch('/api/auth/switch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: account.email }),
});
if (response.ok) {
const { redirectUrl } = await response.json();
window.location.href = redirectUrl;
}
};
const handleAddAccount = async () => {
const response = await fetch('/api/auth/switch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ addNew: true }),
});
if (response.ok) {
const { redirectUrl } = await response.json();
window.location.href = redirectUrl;
}
};
return (
user={session?.user ?? null}
isLoading={status === 'loading'}
theme={themes.blue} // or themes.cyan
heightClass="h-14"
appSwitcher={{
currentAppId: 'bizintel', // or 'data'
}}
accountDropdown={{
onSwitchAccount: handleSwitchAccount,
onAddAccount: handleAddAccount,
onSignOut: () => signOut({ callbackUrl: '/auth/signin' }),
}}
/>
);
}
`$3
`typescript
// app/api/auth/switch/route.ts
import { NextRequest, NextResponse } from 'next/server';export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { email, addNew } = body;
const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000';
const params = new URLSearchParams({ callbackUrl:
${baseUrl}/ }); if (email && !addNew) {
params.set('login_hint', email);
}
const redirectUrl =
${baseUrl}/api/auth/signin/google?${params.toString()};
return NextResponse.json({ redirectUrl });
} catch {
return NextResponse.json({ error: 'Invalid request' }, { status: 400 });
}
}
`$3
`typescript
// lib/auth-options.ts
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
authorization: {
params: {
prompt: 'select_account', // Enable account picker
},
},
}),
`$3
`typescript
// middleware.ts
const publicPaths = [
// ... existing paths
'/api/auth/accounts',
'/api/auth/switch',
];
`Available Apps
| ID | Name | URL | Icon Color |
|----|------|-----|------------|
|
bizintel | Business Intelligence | https://b.adpal.com | Gold |
| data | Data Processing | https://d.adpal.com | Cyan |
| voice | Voice Assistant | https://voice.adpal.com | Green |
| mcp-demo | MCP Demo | https://mcp-demo.adpal.com | Purple |Themes
`typescript
import { themes } from '@adpal/app-switcher';// Available themes
themes.blue // Default - blue accent
themes.cyan // Cyan accent (#00BCD4)
themes.emerald // Green accent
// Or create custom
const customTheme = {
accent: '#FF5722',
ringColor: 'ring-orange-200',
currentBg: 'bg-orange-50',
};
`Components API
$3
`tsx
user={session?.user ?? null}
isLoading={status === 'loading'}
theme={themes.blue}
heightClass="h-14" // Header height (h-12, h-14, h-16)
sidebarOffset="left-64" // Left offset for sidebar
appSwitcher={{
currentAppId: 'bizintel',
showHeader: true,
showFooter: true,
columns: 3,
}}
accountDropdown={{
onSwitchAccount: (account) => {},
onAddAccount: () => {},
onSignOut: () => {},
showManageAccount: true,
}}
/>
`$3
`tsx
currentAppId="bizintel" // Highlight current app
apps={ADPAL_APPS} // Custom apps list
showInternalApps={true} // Show internal-only apps
iconSize={48} // Icon size in pixels
columns={3} // Grid columns (2 or 3)
showHeader={true} // Show "Adpal Apps" header
showFooter={true} // Show "More from Adpal" footer
onAppClick={(app) => {}} // Custom click handler
/>
`$3
`tsx
user={{ id, email, name, image }}
isLoading={false}
onSwitchAccount={(account) => {}}
onAddAccount={() => {}}
onSignOut={() => {}}
showManageAccount={true}
manageAccountUrl="https://myaccount.google.com"
/>
`$3
`tsx
const {
accounts, // LinkedAccount[]
isLoaded, // boolean
addAccount, // (account) => void
removeAccount, // (accountId) => void
updateLastUsed, // (accountId) => void
clearAllAccounts, // () => void
} = useLinkedAccounts({
cookieDomain: '.adpal.com', // Optional
maxAccounts: 5, // Optional
});
`Cross-Subdomain Account Persistence
Linked accounts are stored in a cookie with domain
.adpal.com, enabling account persistence across all AdPal subdomains:- Cookie name:
adpal-linked-accounts
- Max accounts: 5
- Expiry: 30 days
- Fallback: localStorage (if cookie exceeds 4KB)Projects Using This Package
| Project | Subdomain | Theme | Current App ID |
|---------|-----------|-------|----------------|
| adpal-ai-bizintel | b.adpal.com |
themes.blue | bizintel |
| adpal-ai-data | d.adpal.com | themes.cyan | data |Development
`bash
Install dependencies
npm installBuild
npm run buildWatch mode
npm run devType check
npm run typecheck
`Updating the Package
After making changes:
`bash
Build and commit
npm run build
git add -A
git commit -m "description"
git pushUpdate in consuming projects
cd ../adpal-ai-data
npm update @adpal/app-switchercd ../adpal-ai-bizintel
npm update @adpal/app-switcher
``https://github.com/Data-Subsystems/adpal-app-switcher
MIT