Navbar Components
The Navbar components provide application-wide navigation, user management, and switching functionality between different portals, companies, and countries. These components handle user authentication, permissions, and multi-tenant operations.
Overview
This document covers all navbar-related components that provide navigation, user profile management, company/country switching, and portal management within the WorkPayCore Frontend application.
Components Overview
Main Navigation
- Navbar - Main application navbar with user profile and navigation
- GetHelpDrawer - Help and support drawer component
Switching Components
- SwitchCompanyModal - Company and branch switching modal
- SwitchCountryModal - Country switching modal
- EorPortalSwitcher - EOR portal switching functionality
Utility Components
- EWASignupGuide - EWA (Employee Wallet App) signup guide
- SwitchedCountryFlag - Country flag indicator
Navbar
The main application navbar providing user profile access, navigation controls, and switching functionality.
Component Location
import Navbar from 'components/Navbar';
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| onDrawerOpen | function | - | - | Callback function to open mobile drawer |
TypeScript Interface
interface NavbarProps {
onDrawerOpen?: () => void;
}
Features
User Profile Management
- User avatar and profile access
- Account settings navigation
- Profile information display
Company/Portal Switching
- Company and branch switching
- Country switching (Kenya/Nigeria)
- EOR portal switching
- Legacy system access
Navigation Controls
- Hamburger menu for mobile
- Admin portal switcher
- Time tracker integration
- Plan details banner
Support Features
- Help drawer integration
- Changelog widget (for specific users)
- Support channel access
Usage Examples
Basic Usage
import Navbar from 'components/Navbar';
function AppLayout() {
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
return (
<Box>
<Navbar onDrawerOpen={() => setIsDrawerOpen(true)} />
<MainContent />
</Box>
);
}
With Drawer Integration
import Navbar from 'components/Navbar';
import { useDisclosure } from '@chakra-ui/react';
function AppWithDrawer() {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<Box>
<Navbar onDrawerOpen={onOpen} />
<Drawer isOpen={isOpen} onClose={onClose}>
<DrawerContent>
<SidebarContent />
</DrawerContent>
</Drawer>
<MainContent />
</Box>
);
}
Navbar Menu Items
The navbar provides contextual menu items based on user permissions:
Employee/Admin Items
- My Profile: Navigate to employee profile
- Account Settings: User account management
- Switch Company: Company/branch switching
- Switch to/from EOR: EOR portal switching
Product Admin Items
- Switch to Customer's Company: Client company access
Permission-Based Display
The navbar adapts its content based on user permissions:
// Example of permission-based navigation
const navbarItems = [
{
title: 'My profile',
icon: <UserOutline />,
props: { as: Link, to: `/employees/${userPermissions.employee_id}` },
view: isEmployeeAdmin() || isEmployeeOnly(),
},
{
title: 'Account Settings',
icon: <SettingsOutline />,
props: { as: Link, to: '/profile/user-account' },
view: !isEorPortalView(),
},
// ... more items
];
Responsive Design
The navbar adapts to different screen sizes:
- Desktop: Full navbar with all features
- Mobile: Hamburger menu with drawer navigation
- Tablet: Adapted layout with essential features
Integration Features
Time Tracking
- Displays active time tracker when available
- Integrates with timesheet system
Plan Management
- Shows plan details banner
- Subscription status indicators
Country/Regional Features
- Country flag indicators
- Regional feature toggles
- Multi-country support
SwitchCompanyModal
Modal component for switching between companies and branches within the system.
Component Location
import SwitchCompanyModal from 'components/Navbar/SwitchCompanyModal';
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| isOpen | boolean | ✓ | - | Modal open state |
| onClose | function | ✓ | - | Modal close handler |
| companies | object | ✓ | - | Companies data object |
TypeScript Interface
interface SwitchCompanyModalProps {
isOpen: boolean;
onClose: () => void;
companies: {
data: {
data: {
data: Company[];
};
};
};
}
interface Company {
id: number;
name: string;
branches?: Branch[];
country_id: number;
country_name: string;
}
interface Branch {
id: number;
name: string;
}
Features
Company Selection
- Dropdown with all accessible companies
- Search functionality
- Current company highlighting
Branch Selection
- Branches for selected company
- Head office option
- Branch-specific switching
Global vs Local Switching
- Handles global entity switching
- Local branch switching within company
- Cross-country company switching
Usage Examples
Basic Usage
import SwitchCompanyModal from 'components/Navbar/SwitchCompanyModal';
import { useDisclosure } from '@chakra-ui/react';
function CompanySwitcher() {
const { isOpen, onOpen, onClose } = useDisclosure();
const companies = useCompanies();
return (
<>
<Button onClick={onOpen}>Switch Company</Button>
<SwitchCompanyModal
isOpen={isOpen}
onClose={onClose}
companies={companies}
/>
</>
);
}
With Loading States
import SwitchCompanyModal from 'components/Navbar/SwitchCompanyModal';
function CompanySwitcherWithLoading() {
const [isLoading, setIsLoading] = useState(false);
const { isOpen, onOpen, onClose } = useDisclosure();
const handleSwitch = async companyData => {
setIsLoading(true);
try {
await switchCompany(companyData);
onClose();
} finally {
setIsLoading(false);
}
};
return (
<SwitchCompanyModal
isOpen={isOpen}
onClose={onClose}
companies={companies}
isLoading={isLoading}
/>
);
}
Switching Logic
The modal handles different switching scenarios:
Global Company Switching
function onSubmitGlobal(values: any) {
const payload = {
id:
values.id?.value === 1 || !values.id?.value
? values.auth_company_id?.value
: values.id?.value,
};
mutate(payload, {
onSuccess: data => {
if (data?.data?.success) {
onClose();
}
},
});
}
Local/Cross-Country Switching
function onSubmitLocal() {
mutateCountry(
{ name: selectedSwitchedCompany?.data?.country_name },
{
onSuccess: data => {
if (data?.data?.success) {
// Update local storage with new country
window.localStorage.setItem(
'SWITCHED_COUNTRY_KEY',
JSON.stringify({
country_name: data?.data?.data?.name,
country_code: data?.data?.data?.alpha_two_code,
country_id: data?.data?.data?.country_id,
}),
);
mutate({ id: selectedSwitchedCompany?.value });
onClose();
}
},
},
);
}
SwitchCountryModal
Modal component for switching between different countries (Kenya/Nigeria).
Component Location
import SwitchCountryModal from 'components/Navbar/SwitchCountryModal';
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| isOpen | boolean | ✓ | - | Modal open state |
| onClose | function | ✓ | - | Modal close handler |
TypeScript Interface
interface SwitchCountryModalProps {
isOpen: boolean;
onClose: () => void;
}
Supported Countries
The modal supports switching between:
- Kenya: Complete WorkPay functionality
- Nigeria: Full feature set with local compliance
Features
Country Selection
- Flag-based country display
- Search functionality
- Current country highlighting
Local Storage Management
- Updates country preferences
- Maintains session state
- Handles country-specific data
Usage Examples
Basic Usage
import SwitchCountryModal from 'components/Navbar/SwitchCountryModal';
import { useDisclosure } from '@chakra-ui/react';
function CountrySwitcher() {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<>
<Button onClick={onOpen}>Switch Country</Button>
<SwitchCountryModal isOpen={isOpen} onClose={onClose} />
</>
);
}
With Current Country Display
import SwitchCountryModal from 'components/Navbar/SwitchCountryModal';
import { authSwitchedCountry } from 'utils/helpers';
function CountrySwitcherWithCurrent() {
const currentCountry = authSwitchedCountry();
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<HStack>
<Text>Current: {currentCountry?.country_name}</Text>
<Button onClick={onOpen}>Switch Country</Button>
<SwitchCountryModal isOpen={isOpen} onClose={onClose} />
</HStack>
);
}
Country Options
The modal provides predefined country options:
const countryOptions = [
{
value: 'Kenya',
label: 'Kenya',
flag: <KenyanFlagIcon boxSize={5} />,
},
{
value: 'Nigeria',
label: 'Nigeria',
flag: <NigerianFlagIcon boxSize={5} />,
},
];
Switching Process
The country switching process:
- Select Country: User selects from available countries
- Validation: System validates country access
- Update Storage: Local storage updated with new country
- Company Switch: Associated company data updated
- Session Refresh: Application state refreshed
EorPortalSwitcher
Utility functions for switching between EOR (Employer of Record) portal and other portals.
Component Location
import {
eorPortalSwitcher,
getUndoEorPortalName,
} from 'components/Navbar/EorPortalSwitcher';
Functions
eorPortalSwitcher
Main function to switch between portals:
function eorPortalSwitcher(): void;
getUndoEorPortalName
Gets the name of the previous portal for switching back:
function getUndoEorPortalName(): string;
Features
Portal Management
- Switches between EOR and other portals
- Maintains portal history
- Handles portal-specific cleanup
State Management
- Local storage management
- Portal preferences
- Session state handling
User Feedback
- Toast notifications
- Success/error messages
- Loading states
Usage Examples
Basic Portal Switching
import { eorPortalSwitcher } from 'components/Navbar/EorPortalSwitcher';
function PortalSwitcher() {
const handleSwitchPortal = () => {
eorPortalSwitcher();
};
return <Button onClick={handleSwitchPortal}>Switch to EOR Portal</Button>;
}
With Portal Name Display
import {
eorPortalSwitcher,
getUndoEorPortalName,
} from 'components/Navbar/EorPortalSwitcher';
import { isEorPortalView } from 'utils/helpers';
function DynamicPortalSwitcher() {
const previousPortal = getUndoEorPortalName();
const isEorView = isEorPortalView();
return (
<Button onClick={eorPortalSwitcher}>
{isEorView ? `Switch back to ${previousPortal}` : 'Switch to EOR Portal'}
</Button>
);
}
Portal Switching Logic
The switching logic handles:
EOR Portal Entry
- Save Current Portal: Store current portal reference
- Switch to EOR: Update portal state
- Update Storage: Save portal preferences
- Refresh Application: Reload with new portal context
EOR Portal Exit
- Get Previous Portal: Retrieve saved portal reference
- Cleanup EOR State: Remove EOR-specific data
- Switch Back: Return to previous portal
- Clean Storage: Remove temporary portal data
SwitchedCountryFlag
Utility component for displaying the current country flag in the navbar.
Component Location
// Internal component - part of Navbar
function SwitchedCountryFlag(): JSX.Element;
Supported Flags
- Kenya: KenyanFlagIcon
- Nigeria: NigerianFlagIcon
- Default: Avatar with country name
Features
Dynamic Flag Display
- Shows flag based on current country
- Fallback to text avatar
- Responsive sizing
Integration
- Integrates with country switching
- Shows current country state
- Handles country transitions
Usage Examples
Implementation (Internal)
function SwitchedCountryFlag() {
const switchedCountry = authSwitchedCountry();
switch (switchedCountry?.country_name) {
case 'Kenya':
return <KenyanFlagIcon boxSize={7} />;
case 'Nigeria':
return <NigerianFlagIcon boxSize={7} />;
default:
return <Avatar size='sm' name={switchedCountry?.country_name} />;
}
}
Usage in Navbar
// Part of main navbar component
{
SwitchedCountryFlag() && !remoteEnabled && (
<IconButton
bgColor='transparent'
size='md'
isRound
_hover={{ bgColor: 'background' }}
icon={SwitchedCountryFlag()}
onClick={countryModalOnOpen}
overflow='hidden'
/>
);
}
Integration Patterns
Layout Integration
The navbar integrates with application layouts:
import Navbar from 'components/Navbar';
import Sidebar from 'components/Sidebar';
function AppLayout({ children }) {
const { isOpen, onOpen, onClose } = useDisclosure();
return (
<Box>
<Navbar onDrawerOpen={onOpen} />
<Flex>
<Sidebar display={{ base: 'none', md: 'block' }} />
<Drawer isOpen={isOpen} onClose={onClose}>
<DrawerContent>
<Sidebar />
</DrawerContent>
</Drawer>
<Box flex={1} p={4}>
{children}
</Box>
</Flex>
</Box>
);
}
Permission Integration
Components integrate with permission systems:
// Example permission checks in navbar
const navbarItems = [
{
title: 'My profile',
icon: <UserOutline />,
props: { as: Link, to: `/employees/${userPermissions.employee_id}` },
view: isEmployeeAdmin() || isEmployeeOnly(),
},
{
title: 'Switch company',
icon: <RefreshOutline />,
props: { onClick: switchCompanyDisclosure.onOpen },
view: canSwitchCompany(),
},
];
State Management Integration
// Integration with global state
function NavbarWithState() {
const user = useSelector(state => state.auth.user);
const dispatch = useDispatch();
const handleLogout = () => {
dispatch(logoutHandler());
};
return <Navbar user={user} onLogout={handleLogout} />;
}
Styling
Common Styling Patterns
Navbar Styling
- Height:
70px - Background:
white - Padding:
5px 8px - Drop shadow:
0px 1px 0px rgba(11, 15, 20, 0.12)
Button Styling
- Solid buttons:
green-darkbackground - Outline buttons:
greenborder - Icon buttons:
transparentbackground with hover states
Modal Styling
- Consistent modal sizing
- Form layouts with proper spacing
- Button groups with consistent styling
Responsive Design
Mobile Adaptations
- Hamburger menu for mobile navigation
- Drawer-based navigation
- Responsive spacing and sizing
Tablet Adaptations
- Adaptive layout components
- Responsive text sizing
- Optimized touch targets
Custom Styling
Components support custom styling:
// Custom navbar styling
<Navbar
style={{
backgroundColor: 'custom.color',
borderBottom: '1px solid custom.border',
}}
/>
// Custom modal styling
<SwitchCompanyModal
modalProps={{
size: 'lg',
isCentered: true,
}}
/>
Accessibility
Keyboard Navigation
- Tab Navigation: Proper tab order for all interactive elements
- Enter/Space: Button activation
- Escape: Modal and drawer closing
- Arrow Keys: Menu navigation
Screen Reader Support
- ARIA Labels: Descriptive labels for all interactive elements
- Role Attributes: Proper semantic roles
- Live Regions: Dynamic content announcements
- Focus Management: Proper focus handling in modals
Visual Accessibility
- High Contrast: Sufficient color contrast ratios
- Focus Indicators: Clear focus indicators
- Text Scaling: Components work with text scaling
- Reduced Motion: Respects motion preferences
Performance Considerations
Optimization Techniques
- Lazy Loading: Components loaded on demand
- Memoization: Expensive calculations memoized
- Debouncing: API calls debounced appropriately
- Caching: User preferences cached locally
Best Practices
- Efficient Updates: Minimal re-renders
- Proper Dependencies: Correct useEffect dependencies
- Memory Management: Cleanup of event listeners
- Bundle Optimization: Code splitting where appropriate
Testing Strategies
Unit Testing
import { render, screen, fireEvent } from '@testing-library/react';
import Navbar from 'components/Navbar';
describe('Navbar', () => {
it('renders user menu', () => {
render(<Navbar />);
expect(
screen.getByRole('button', { name: /user menu/i }),
).toBeInTheDocument();
});
it('opens drawer on mobile menu click', () => {
const onDrawerOpen = jest.fn();
render(<Navbar onDrawerOpen={onDrawerOpen} />);
fireEvent.click(screen.getByRole('button', { name: /hamburger menu/i }));
expect(onDrawerOpen).toHaveBeenCalled();
});
});
Integration Testing
- Permission Integration: Test with different permission levels
- State Management: Test with various application states
- Modal Integration: Test modal interactions
- Responsive Testing: Test across screen sizes
E2E Testing
- User Workflows: Test complete navigation flows
- Company Switching: Test switching between companies
- Portal Switching: Test EOR portal switching
- Cross-browser Testing: Test across browsers
Migration Notes
From Legacy Navbar
Update Navbar Props
// Old approach
<Navbar user={user} onLogout={handleLogout} />
// New approach
<Navbar onDrawerOpen={handleDrawerOpen} />
Update Modal Usage
// Old approach
<CompanyModal isOpen={isOpen} onClose={onClose} />
// New approach
<SwitchCompanyModal
isOpen={isOpen}
onClose={onClose}
companies={companies}
/>
Breaking Changes
- Props Structure: Navbar props simplified
- Modal API: Modal props updated
- Event Handlers: Event signatures changed
Upgrade Paths
- Update Imports: Change component imports
- Update Props: Modify props to match new API
- Update Handlers: Update event handlers
- Test Integration: Verify functionality works
- Update Styling: Migrate custom styles
Best Practices
Component Usage
- Use Navbar as the main application navigation
- Use SwitchCompanyModal for company switching workflows
- Use SwitchCountryModal for country switching
- Use EorPortalSwitcher for portal management
Permission Management
- Check permissions before rendering sensitive components
- Implement proper guards for protected actions
- Provide feedback for unauthorized actions
- Handle permission changes gracefully
State Management
- Manage local storage consistently
- Handle state updates properly
- Implement proper cleanup for component unmounting
- Cache user preferences appropriately
User Experience
- Provide clear feedback for all actions
- Implement loading states for async operations
- Handle errors gracefully with user-friendly messages
- Maintain consistent navigation patterns
Common Issues and Solutions
Permission-Related Issues
// Problem: Component renders for unauthorized users
// Solution: Implement proper permission checks
const shouldShowComponent = usePermissions(['required-permission']);
if (!shouldShowComponent) {
return null;
}
State Management Issues
// Problem: State not updating after company switch
// Solution: Refresh relevant queries after switching
const handleCompanySwitch = async () => {
await switchCompany();
queryClient.invalidateQueries(['user-data']);
queryClient.invalidateQueries(['company-data']);
};
Performance Issues
// Problem: Navbar re-renders frequently
// Solution: Memoize expensive operations
const memoizedNavbarItems = useMemo(() => {
return computeNavbarItems(userPermissions);
}, [userPermissions]);
Mobile Navigation Issues
// Problem: Drawer not closing properly
// Solution: Implement proper event handlers
const handleDrawerClose = useCallback(() => {
onClose();
// Additional cleanup if needed
}, [onClose]);