- Add comprehensive naming convention (provider-region-resource-env-purpose) - Implement Terraform locals for centralized naming - Update all Terraform resources to use new naming convention - Create deployment automation framework (18 phase scripts) - Add Azure setup scripts (provider registration, quota checks) - Update deployment scripts config with naming functions - Create complete deployment documentation (guide, steps, quick reference) - Add frontend portal implementations (public and internal) - Add UI component library (18 components) - Enhance Entra VerifiedID integration with file utilities - Add API client package for all services - Create comprehensive documentation (naming, deployment, next steps) Infrastructure: - Resource groups, storage accounts with new naming - Terraform configuration updates - Outputs with naming convention examples Deployment: - Automated deployment scripts for all 15 phases - State management and logging - Error handling and validation Documentation: - Naming convention guide and implementation summary - Complete deployment guide (296 steps) - Next steps and quick start guides - Azure prerequisites and setup completion docs Note: ESLint warnings present - will be addressed in follow-up commit
119 lines
3.2 KiB
TypeScript
119 lines
3.2 KiB
TypeScript
import React, { createContext, useContext, useState, ReactNode } from 'react';
|
|
import { cn } from '../lib/utils';
|
|
|
|
interface TabsContextType {
|
|
value: string;
|
|
onValueChange: (value: string) => void;
|
|
}
|
|
|
|
const TabsContext = createContext<TabsContextType | undefined>(undefined);
|
|
|
|
export interface TabsProps {
|
|
defaultValue?: string;
|
|
value?: string;
|
|
onValueChange?: (value: string) => void;
|
|
children: ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function Tabs({ defaultValue, value: controlledValue, onValueChange, children, className }: TabsProps) {
|
|
const [internalValue, setInternalValue] = useState(defaultValue || '');
|
|
const isControlled = controlledValue !== undefined;
|
|
const currentValue = isControlled ? controlledValue : internalValue;
|
|
|
|
const handleValueChange = (newValue: string) => {
|
|
if (!isControlled) {
|
|
setInternalValue(newValue);
|
|
}
|
|
onValueChange?.(newValue);
|
|
};
|
|
|
|
return (
|
|
<TabsContext.Provider value={{ value: currentValue, onValueChange: handleValueChange }}>
|
|
<div className={cn('w-full', className)}>{children}</div>
|
|
</TabsContext.Provider>
|
|
);
|
|
}
|
|
|
|
export interface TabsListProps {
|
|
children: ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
export function TabsList({ children, className }: TabsListProps) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground',
|
|
className
|
|
)}
|
|
role="tablist"
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export interface TabsTriggerProps {
|
|
value: string;
|
|
children: 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 { value: currentValue, onValueChange } = context;
|
|
const isActive = currentValue === value;
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
aria-selected={isActive}
|
|
onClick={() => onValueChange(value)}
|
|
className={cn(
|
|
'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
isActive
|
|
? 'bg-background text-foreground shadow-sm'
|
|
: 'text-muted-foreground hover:bg-background/50',
|
|
className
|
|
)}
|
|
>
|
|
{children}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
export interface TabsContentProps {
|
|
value: string;
|
|
children: 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');
|
|
}
|
|
|
|
const { value: currentValue } = context;
|
|
if (currentValue !== value) return null;
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
|
className
|
|
)}
|
|
role="tabpanel"
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|