All files / src/components/shared/Company CompanyLogo.tsx

84.61% Statements 11/13
84.61% Branches 22/26
100% Functions 1/1
84.61% Lines 11/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126                                                              17x               58x       58x 49x                             9x 58x       58x 4x 4x                                               5x                             5x                                        
/**
 * CompanyLogo
 *
 * Shared component for displaying a company logo in two variants:
 *
 * - "avatar": small circular Avatar + optional name text (used in table cells, lists)
 * - "full":   full-size img with objectFit:contain + name as tooltip (used in cards, topic table)
 *
 * Both variants use React Query (10-min stale time via useCompany) so logos are cached
 * across all usages and fetched only once per company per session.
 */
 
import React from 'react';
import { Avatar, Box, Skeleton, Tooltip, Typography } from '@mui/material';
import { Business as BusinessIcon } from '@mui/icons-material';
import { useCompany } from '@/hooks/useCompany/useCompany';
 
interface CompanyLogoProps {
  companyName: string;
  /** 'avatar' = circular Avatar + name text; 'full' = full img with tooltip. Default: 'avatar' */
  variant?: 'avatar' | 'full';
  /** Avatar diameter in px (avatar variant only). Default: 24 */
  avatarSize?: number;
  /** Max width in px (full variant only). Default: 80 */
  maxWidth?: number;
  /** Max height in px (full variant only). Default: 48 */
  maxHeight?: number;
  /** Show display name alongside the avatar (avatar variant only). Default: true */
  showName?: boolean;
}
 
const CompanyLogo: React.FC<CompanyLogoProps> = ({
  companyName,
  variant = 'avatar',
  avatarSize = 24,
  maxWidth = 80,
  maxHeight = 48,
  showName = true,
}) => {
  const { data: company, isLoading, isError } = useCompany(companyName, { expand: ['logo'] });
 
  // ─── Loading ───────────────────────────────────────────────────────────────
 
  if (isLoading) {
    return variant === 'full' ? (
      <Skeleton
        variant="rectangular"
        width={maxWidth}
        height={maxHeight / 2}
        sx={{ borderRadius: 1 }}
      />
    ) : (
      <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
        <Skeleton variant="circular" width={avatarSize} height={avatarSize} />
        {showName && <Skeleton variant="text" width={80} />}
      </Box>
    );
  }
 
  const logoUrl = company?.logo?.url;
  const displayName = company?.displayName || company?.name || companyName;
 
  // ─── Full variant ──────────────────────────────────────────────────────────
 
  if (variant === 'full') {
    Eif (!logoUrl) {
      return (
        <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
          <BusinessIcon sx={{ fontSize: 20, color: 'grey.400' }} />
          <Typography variant="body2" color="text.secondary">
            {displayName}
          </Typography>
        </Box>
      );
    }
    return (
      <Tooltip title={displayName}>
        <Box
          component="img"
          src={logoUrl}
          alt={displayName}
          loading="lazy"
          sx={{ maxWidth, maxHeight, objectFit: 'contain', display: 'block' }}
        />
      </Tooltip>
    );
  }
 
  // ─── Avatar variant ────────────────────────────────────────────────────────
 
  Iif (isError || !company) {
    return (
      <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
        <Avatar sx={{ width: avatarSize, height: avatarSize, bgcolor: 'grey.300' }}>
          <BusinessIcon sx={{ fontSize: avatarSize * 0.65 }} />
        </Avatar>
        {showName && (
          <Typography variant="body2" color="text.secondary">
            {companyName}
          </Typography>
        )}
      </Box>
    );
  }
 
  return (
    <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
      <Avatar
        src={logoUrl}
        alt={displayName}
        sx={{ width: avatarSize, height: avatarSize, bgcolor: 'grey.100' }}
        slotProps={{ img: { loading: 'lazy' } }}
      >
        <BusinessIcon sx={{ fontSize: avatarSize * 0.65, color: 'grey.500' }} />
      </Avatar>
      {showName && (
        <Typography variant="body2" color="text.secondary" noWrap>
          {displayName}
        </Typography>
      )}
    </Box>
  );
};
 
export default CompanyLogo;