All files / src/features/dashboard/components summary-cards.tsx

94.73% Statements 18/19
91.66% Branches 22/24
87.5% Functions 7/8
94.73% Lines 18/19

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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176                                                                            2x                   12x     12x 1x                 11x                         8x       22x       1x                   2x                     21x           21x 1x       20x 1x 1x 1x       20x                                 18x                                                                          
import { ChevronDown } from 'lucide-react';
import { CardTitle } from '@/components/card';
import {
  DashboardCardWrapper,
  SummaryLoadingSkeleton,
} from './shared/dashboard-card-wrapper';
import { DashboardStateRenderer } from './shared/dashboard-state-renderer';
import { createDefaultActionItems } from './shared/dashboard-types';
import growUpChartImage from '@/assets/images/grow-up-chart.webp';
import { useSummary } from '../hooks/use-summary';
import { useCardVisibility } from '../hooks/use-card-visibility';
import { CustomActiveShapePieChart } from '@/components/custom-active-shape-pie-chart';
import type { CardType, TrendType } from '@/constants/dashboard';
 
// Enhanced dashboard card props with individual error state management
export interface SummaryCardProps {
  title: string;
  value?: string;
  percentage?: string;
  trend?: TrendType;
  color?: CardType;
  actions?: boolean;
  loading?: boolean;
  error?: string | null;
  onRetry?: () => void;
  onHide?: () => void;
}
 
export interface SummaryCardsProps {
  data: SummaryCardProps[];
  loading?: boolean;
  error?: string | null;
  onRetry?: () => void;
  hiddenCards?: Set<number>;
  onHideCard?: (index: number) => void;
  className?: string;
}
 
export const SummaryCards = ({
  hiddenCards,
  onHideCard,
  className,
}: SummaryCardsProps) => {
  const {
    data: dashboardCards,
    loading: overviewLoading,
    error: overviewError,
    refetch: refetchOverview,
  } = useSummary();
 
  // If loading, show skeleton loading state
  if (overviewLoading) {
    return (
      <div
        className={`grid grid-cols-1 md:grid-cols-3 gap-6 mb-8 ${className || ''}`}
      >
        <SummaryLoadingSkeleton count={3} />
      </div>
    );
  }
 
  return (
    <DashboardStateRenderer
      loading={false} // We handle loading above
      error={overviewError}
      data={dashboardCards}
      isEmpty={cards => (cards as SummaryCardProps[]).length === 0}
      onRetry={refetchOverview}
      loadingMessage="Loading summary cards..."
      errorTitle="Summary"
      emptyMessage="No summary data available"
      className="col-span-full"
    >
      {summaryData => (
        <div
          className={`grid grid-cols-1 md:grid-cols-3 gap-6 ${className || ''}`}
        >
          {(summaryData as SummaryCardProps[]).map((cardProps, index) =>
            hiddenCards?.has(index) ? null : (
              <SummaryCard
                key={index}
                {...cardProps}
                onHide={() => onHideCard?.(index)}
              />
            )
          )}
        </div>
      )}
    </DashboardStateRenderer>
  );
};
 
export const SummaryCard = ({
  title,
  value,
  percentage,
  trend = 'up',
  actions = true,
  loading = false,
  error = null,
  onRetry,
  onHide,
}: SummaryCardProps) => {
  const { isVisible, hideCard } = useCardVisibility({
    id: `summary-card-${title}`,
    defaultVisible: true,
  });
 
  // If the card is not visible, don't render it
  if (!isVisible) {
    return null;
  }
 
  // Call the parent's onHide handler when hiding the card
  const handleHide = () => {
    hideCard();
    Eif (onHide) {
      onHide();
    }
  };
 
  return (
    <DashboardCardWrapper
      actionItems={actions ? createDefaultActionItems(handleHide) : undefined}
      contentClassName="p-6 flex"
      title={undefined}
    >
      <DashboardStateRenderer
        loading={loading}
        error={error}
        data={{ title, value, percentage, trend }}
        onRetry={onRetry}
        loadingMessage=""
        errorTitle=""
        className="h-24"
      >
        {cardData => (
          // Success state - render card content
          <div className="flex items-center justify-between">
            <div className="flex items-center">
              <CustomActiveShapePieChart
                data={[
                  {
                    name: (cardData as any).title,
                    value: (cardData as any).value,
                  },
                ]}
              />
              <div className="ml-4">
                <CardTitle className="text-sm font-bold text-default">
                  {(cardData as any).title}
                </CardTitle>
                <div className="flex items-center space-x-2">
                  <span
                    className={`text-sm font-medium ${(cardData as any).trend === 'up' ? 'text-green-600' : 'text-red-600'}`}
                  >
                    {(cardData as any).percentage}
                  </span>
                  <ChevronDown
                    className={`w-4 h-4 ${(cardData as any).trend === 'up' ? 'text-green-600' : 'text-red-600'} ${(cardData as any).trend === 'up' ? 'transform rotate-180' : ''}`}
                  />
                  <img
                    src={growUpChartImage}
                    alt="Growth chart"
                    className="w-[63px] h-[26px] ml-2"
                  />
                </div>
              </div>
            </div>
          </div>
        )}
      </DashboardStateRenderer>
    </DashboardCardWrapper>
  );
};