@elsapiens/shell
v0.1.5
Published
Shell components for elSapiens SDK - AuthGuard, ErrorBoundary, OrgSwitcher, etc.
Downloads
32
Readme
@elsapiens/shell
Higher-level wrapper components for common application patterns.
Installation
npm install @elsapiens/shell @elsapiens/ui @elsapiens/utils
# or
pnpm add @elsapiens/shell @elsapiens/ui @elsapiens/utilsFeatures
- Authentication guard for protected routes
- Error boundary with fallback UI
- Organization/workspace switcher
- Application selector for multi-app environments
- Service unavailability page
Usage
AuthGuard
Protect routes that require authentication:
import { AuthGuard } from '@elsapiens/shell';
// Basic usage
<AuthGuard>
<ProtectedPage />
</AuthGuard>
// With custom redirect
<AuthGuard redirectTo="/login">
<ProtectedPage />
</AuthGuard>
// With custom loading
<AuthGuard
loadingFallback={<CustomLoader />}
unauthorizedFallback={<AccessDenied />}
>
<ProtectedPage />
</AuthGuard>
// Role-based access
<AuthGuard requiredRoles={['admin', 'manager']}>
<AdminDashboard />
</AuthGuard>Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | - | Protected content |
| redirectTo | string | '/login' | Redirect path |
| loadingFallback | ReactNode | <Spinner /> | Loading component |
| unauthorizedFallback | ReactNode | - | Unauthorized component |
| requiredRoles | string[] | - | Required user roles |
ErrorBoundary
Catch and handle errors gracefully:
import { ErrorBoundary } from '@elsapiens/shell';
// Basic usage
<ErrorBoundary>
<App />
</ErrorBoundary>
// With custom fallback
<ErrorBoundary
fallback={<CustomErrorPage />}
onError={(error, errorInfo) => {
logErrorToService(error, errorInfo);
}}
>
<App />
</ErrorBoundary>
// With reset capability
<ErrorBoundary
fallback={({ error, resetError }) => (
<div>
<h2>Something went wrong</h2>
<p>{error.message}</p>
<button onClick={resetError}>Try Again</button>
</div>
)}
>
<App />
</ErrorBoundary>Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | - | App content |
| fallback | ReactNode \| Function | - | Error UI |
| onError | function | - | Error callback |
| onReset | function | - | Reset callback |
OrgSwitcher
Organization/workspace switcher:
import { OrgSwitcher } from '@elsapiens/shell';
<OrgSwitcher
organizations={[
{ id: '1', name: 'Acme Corp', logo: '/acme.png' },
{ id: '2', name: 'Widgets Inc', logo: '/widgets.png' },
]}
currentOrg={currentOrg}
onSwitch={(org) => switchOrganization(org.id)}
onCreateNew={() => openCreateOrgModal()}
/>Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| organizations | Org[] | - | Available organizations |
| currentOrg | Org | - | Currently selected org |
| onSwitch | function | - | Switch handler |
| onCreateNew | function | - | Create new org handler |
| showCreateButton | boolean | true | Show create button |
| className | string | - | Additional classes |
AppSelector
Application selector for multi-app environments:
import { AppSelector } from '@elsapiens/shell';
<AppSelector
apps={[
{ id: 'accounting', name: 'Accounting', icon: <Calculator />, url: '/accounting' },
{ id: 'inventory', name: 'Inventory', icon: <Package />, url: '/inventory' },
{ id: 'crm', name: 'CRM', icon: <Users />, url: '/crm' },
]}
currentApp="accounting"
onSelect={(app) => navigateTo(app.url)}
/>Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| apps | App[] | - | Available applications |
| currentApp | string | - | Current app ID |
| onSelect | function | - | Selection handler |
| className | string | - | Additional classes |
ServicesDownPage
Display when services are unavailable:
import { ServicesDownPage } from '@elsapiens/shell';
// In your error handling
if (serviceUnavailable) {
return (
<ServicesDownPage
title="Service Temporarily Unavailable"
message="We're performing scheduled maintenance. Please try again later."
retryAction={() => window.location.reload()}
contactEmail="[email protected]"
/>
);
}Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| title | string | 'Service Unavailable' | Page title |
| message | string | - | Error message |
| retryAction | function | - | Retry button handler |
| retryLabel | string | 'Try Again' | Retry button text |
| contactEmail | string | - | Support email |
| showStatus | boolean | true | Show status link |
| statusUrl | string | - | Status page URL |
Complete Example
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import {
AuthGuard,
ErrorBoundary,
OrgSwitcher,
AppSelector,
ServicesDownPage,
} from '@elsapiens/shell';
import { AppShell } from '@elsapiens/layout';
function App() {
const { isServiceDown } = useHealthCheck();
if (isServiceDown) {
return <ServicesDownPage />;
}
return (
<ErrorBoundary
onError={(error) => logError(error)}
fallback={({ resetError }) => (
<ErrorPage onRetry={resetError} />
)}
>
<BrowserRouter>
<AuthGuard redirectTo="/login">
<AppShell
topBarContent={
<>
<AppSelector apps={apps} currentApp="main" />
<OrgSwitcher
organizations={orgs}
currentOrg={currentOrg}
onSwitch={switchOrg}
/>
</>
}
>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/admin" element={
<AuthGuard requiredRoles={['admin']}>
<AdminPanel />
</AuthGuard>
} />
</Routes>
</AppShell>
</AuthGuard>
</BrowserRouter>
</ErrorBoundary>
);
}Types
interface Organization {
id: string;
name: string;
logo?: string;
role?: string;
}
interface AppDefinition {
id: string;
name: string;
icon?: ReactNode;
url: string;
description?: string;
}License
MIT
