- Create BeneficiaryDetailPage with Overview, Sensors, and Activity tabs - Add StatusBadge and SensorStatusBadge UI components - Add Tabs component (Tabs, TabsList, TabsTrigger, TabsContent) - Add getBeneficiary API method - Include comprehensive tests for all new components - Update ESLint config with root:true to prevent config inheritance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
88 lines
2.1 KiB
TypeScript
88 lines
2.1 KiB
TypeScript
import React, { useState, createContext, useContext } from 'react';
|
|
|
|
interface TabsContextType {
|
|
activeTab: string;
|
|
setActiveTab: (tab: string) => void;
|
|
}
|
|
|
|
const TabsContext = createContext<TabsContextType | null>(null);
|
|
|
|
interface TabsProps {
|
|
defaultValue: string;
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function Tabs({ defaultValue, children, className = '' }: TabsProps) {
|
|
const [activeTab, setActiveTab] = useState(defaultValue);
|
|
|
|
return (
|
|
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
|
|
<div className={className}>{children}</div>
|
|
</TabsContext.Provider>
|
|
);
|
|
}
|
|
|
|
interface TabsListProps {
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function TabsList({ children, className = '' }: TabsListProps) {
|
|
return (
|
|
<div
|
|
className={`flex border-b border-gray-200 ${className}`}
|
|
role="tablist"
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface TabsTriggerProps {
|
|
value: string;
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function TabsTrigger({ value, children, className = '' }: TabsTriggerProps) {
|
|
const context = useContext(TabsContext);
|
|
if (!context) throw new Error('TabsTrigger must be used within Tabs');
|
|
|
|
const isActive = context.activeTab === value;
|
|
|
|
return (
|
|
<button
|
|
role="tab"
|
|
aria-selected={isActive}
|
|
onClick={() => context.setActiveTab(value)}
|
|
className={`px-4 py-3 text-sm font-medium border-b-2 transition-colors ${
|
|
isActive
|
|
? 'border-primary text-primary'
|
|
: 'border-transparent text-textSecondary hover:text-textPrimary hover:border-gray-300'
|
|
} ${className}`}
|
|
>
|
|
{children}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
interface TabsContentProps {
|
|
value: string;
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function TabsContent({ value, children, className = '' }: TabsContentProps) {
|
|
const context = useContext(TabsContext);
|
|
if (!context) throw new Error('TabsContent must be used within Tabs');
|
|
|
|
if (context.activeTab !== value) return null;
|
|
|
|
return (
|
|
<div role="tabpanel" className={`pt-4 ${className}`}>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|