SharingStatus Component

The SharingStatus component provides a compact, informative display of access control settings with optional editing capabilities and QR code sharing.

Overview

This component translates access control configurations into user-friendly status displays. It shows sharing permissions with appropriate icons, descriptions, and interactive elements for editing permissions and generating QR codes for shareable resources.

Location

src/components/SharingStatus.tsx

Features

Status Display

  • Visual Icons: Contextual icons for each access type
  • Descriptive Text: Clear, concise permission descriptions
  • Responsive Layout: Adapts to mobile and desktop layouts
  • Hover Effects: Interactive feedback for editable states

Access Control Integration

  • Multiple Access Types: Support for all AccessControl types
  • Group Name Resolution: Displays actual group names instead of IDs
  • Domain Information: Shows domain restrictions clearly
  • User Count Display: Indicates number of users with access

Interactive Features

  • Editable Mode: Optional click-to-edit functionality
  • QR Code Generation: Share button for public/shared resources
  • Tooltip Support: Additional context on hover
  • Modal Integration: Opens editing dialogs

Props Interface

interface SharingStatusProps {
  accessControl?: AccessControl;
  className?: string;
  userDomain?: string;
  isEditable?: boolean;
  onAccessChange?: (accessControl: AccessControl) => void;
  assistantId?: string;
  assistantName?: string;
}

Usage Examples

Read-Only Display

import { SharingStatus } from '@/components/SharingStatus';

function AssistantCard({ assistant }) {
  return (
    <div className="assistant-card">
      <h3>{assistant.name}</h3>
      <SharingStatus 
        accessControl={assistant.accessControl}
        className="mt-2"
      />
    </div>
  );
}

Editable with QR Code

function AssistantHeader({ assistant, onUpdate }) {
  return (
    <div className="header">
      <h1>{assistant.name}</h1>
      <SharingStatus
        accessControl={assistant.accessControl}
        userDomain="example.com"
        isEditable={true}
        onAccessChange={onUpdate}
        assistantId={assistant.id}
        assistantName={assistant.name}
        className="ml-4"
      />
    </div>
  );
}

Access Type Displays

Private Access

Icon = Lock;
text = 'Private';
description = 'You';

Public Access

Icon = Globe;
text = 'Public';
description = 'Anyone';

Domain Access

Icon = Building;
text = 'Domain';
description = userDomain || 'your organization';

Multiple Domains

Icon = Building;
text = 'Domains';
description = `${count} domain${count !== 1 ? 's' : ''}`;

// Shows domain names if 2 or fewer
if (accessControl.domains && accessControl.domains.length <= 2) {
  description += `: ${accessControl.domains.join(', ')}`;
}

Specific Users

Icon = Users;
text = 'Specific Users';
description = `${emailCount} user${emailCount !== 1 ? 's' : ''}`;

Group Access

Icon = Users2;
text = 'Groups';
description = `${groupCount} group${groupCount !== 1 ? 's' : ''}`;

// Shows group names if available and reasonable number
if (groupNames.length <= 2) {
  description += `: ${groupNames.join(', ')}`;
}

Group Name Resolution

Fallback Strategy

// Try multiple approaches to get group names
let availableGroups = [];

try {
  availableGroups = getAllGroups();
} catch (error) {
  console.error("Error getting groups:", error);
  
  try {
    availableGroups = predefinedGroups;
  } catch (error) {
    // Last resort fallback - hardcoded group names
    availableGroups = [
      { id: 'alicante', name: 'Alicante Team' },
      { id: 'dev-team', name: 'Development Team' },
      { id: 'external', name: 'External Users' },
      { id: 'engineering-team', name: 'Engineering Team' },
      { id: 'marketing-team', name: 'Marketing Team' },
      { id: 'leadership', name: 'Leadership' }
    ];
  }
}

Name Mapping

const groupNames = accessControl.groups
  .map(groupId => {
    const group = availableGroups.find(g => g.id === groupId);
    return group?.name || groupId;
  });

Responsive Layout

Desktop Layout

<div className="hidden sm:flex sm:items-center max-w-[200px] lg:max-w-xs">
  <span className="text-xs sm:text-sm text-muted-foreground/80 whitespace-nowrap overflow-hidden text-ellipsis">
     {description}
  </span>
</div>

Mobile Layout

<div className="sm:hidden flex-1 min-w-0">
  <span className="text-xs text-muted-foreground/80 overflow-hidden text-ellipsis whitespace-nowrap block">
     {description}
  </span>
</div>

Interactive States

Clickable Indicator

const isClickable = isEditable && !!onAccessChange;
const cursorClass = isClickable ? 'cursor-pointer hover:bg-muted/40 transition-colors' : '';
const paddingClass = isClickable ? 'px-2 py-1 rounded-md' : '';

Edit Icon

{isClickable && (
  <Edit2 className="h-3.5 w-3.5 text-muted-foreground/0 group-hover:text-muted-foreground/80 transition-colors shrink-0" />
)}

QR Code Integration

QR Button Display Logic

// Show QR code button for non-private assistants
{assistantId && assistantName && accessControl.type !== 'private' && (
  <Button
    variant="ghost"
    size="sm"
    onClick={() => setQrDialogOpen(true)}
    className="h-8 w-8 p-0 text-muted-foreground hover:text-foreground"
  >
    <QrCode className="h-4 w-4" />
  </Button>
)}

QR Code Dialog

<QRCodeDialog
  isOpen={qrDialogOpen}
  onOpenChange={setQrDialogOpen}
  assistantId={assistantId}
  assistantName={assistantName}
/>

Dialog Management

Sharing Status Dialog

{isEditable && onAccessChange && (
  <SharingStatusDialog
    isOpen={dialogOpen}
    onOpenChange={setDialogOpen}
    accessControl={accessControl}
    onAccessChange={onAccessChange}
    userDomain={userDomain}
  />
)}

Click Handling

const handleClick = () => {
  if (isEditable && onAccessChange) {
    setDialogOpen(true);
  }
};

Tooltip Integration

Conditional Tooltip

{isClickable ? (
  <TooltipProvider>
    <Tooltip delayDuration={300}>
      <TooltipTrigger asChild>
        {statusComponent}
      </TooltipTrigger>
      <TooltipContent>
        Click to edit sharing settings
      </TooltipContent>
    </Tooltip>
  </TooltipProvider>
) : (
  statusComponent
)}

Unknown State Handling

if (!accessControl) {
  return (
    <div className={`flex items-center gap-2 ${className}`}>
      <Shield className="h-4 w-4 sm:h-5 sm:w-5 text-muted-foreground" />
      <span className="text-xs sm:text-sm font-medium text-muted-foreground">Unknown</span>
      <span className="text-xs sm:text-sm text-muted-foreground/80 hidden sm:inline"> Unknown sharing status</span>
    </div>
  );
}

Dependencies

  • @/lib/firebase - AccessControl type definition
  • @/lib/groups - Group name resolution
  • @/components/SharingStatusDialog - Edit functionality
  • @/components/QRCodeDialog - QR code generation
  • @/components/ui/* - Button, Tooltip components
  • lucide-react - Status icons

Accessibility

Semantic Structure

  • Button Role: Editable status has proper button semantics
  • Tab Index: Keyboard navigation support
  • ARIA Labels: Screen reader friendly descriptions

Visual Indicators

  • Hover States: Clear interaction feedback
  • Focus States: Keyboard focus indicators
  • Color Contrast: Sufficient contrast for text and icons

Performance Considerations

Group Resolution Caching

  • Error Handling: Graceful fallback for group lookup failures
  • Memoization: Could benefit from memoizing group name resolution
  • Lazy Loading: Group names resolved only when needed

Responsive Optimization

  • Text Truncation: Prevents layout overflow
  • Conditional Rendering: Shows appropriate detail level per screen size
  • Icon Sizing: Responsive icon dimensions
  • AccessSelector - Configures the access control settings
  • SharingStatusDialog - Provides editing interface
  • QRCodeDialog - Generates sharing QR codes
  • Group management interfaces

Best Practices

  1. Provide Context: Include userDomain when available for better descriptions
  2. Handle Missing Data: Graceful fallbacks for unknown access control
  3. Responsive Design: Consider mobile layout constraints
  4. Clear Interactions: Make editable states obvious to users
  5. Performance: Cache group lookups when possible
  6. Accessibility: Support keyboard navigation and screen readers