/******************************************************************************** * Copyright (c) 2026 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at * https://www.eclipse.org/legal/epl-2.0 * * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ import { FunctionComponent, useState, useEffect } from 'react'; import { Card, CardContent, Box } from '@mui/material'; import { useTheme } from '@mui/material/styles'; import { ScanResult, useScanContext } from '../../../context/scan-admin'; import { ScanCardHeader } from './scan-card-header'; import { ScanCardContent } from './scan-card-content'; import { ScanCardExpandStrip } from './scan-card-expand-strip'; import { ScanCardExpandedContent } from './scan-card-expanded-content'; import { useScanCardState } from '../../../hooks/scan-admin'; import { ICON_SIZE, shouldShowStriped, getStatusBarColor, hasFailedScannerJobs, isRunning, } from './utils'; interface ScanCardProps { scan: ScanResult; showCheckbox?: boolean; onCheckboxChange?: (id: string, checked: boolean) => void; checked?: boolean; } /** * ScanCard component displays information about an extension scan. * * Sub-components: * - ScanCardHeader: Icon, display name, namespace, status badge * - ScanCardContent: Publisher, version, download, scan times, checkbox * - ScanCardExpandStrip: Collapsible trigger bar with badges * - ScanCardExpandedContent: Threats and validation failures * * State is managed via the useScanCardState hook. */ export const ScanCard: FunctionComponent = ({ scan, showCheckbox, onCheckboxChange, checked, }) => { const theme = useTheme(); const { actions } = useScanContext(); const [collapseComplete, setCollapseComplete] = useState(true); const [isRetryingFailedScannerJobs, setIsRetryingFailedScannerJobs] = useState(false); const { expanded, handleExpandClick, showExpandButton, badges, liveDuration, cardRef, } = useScanCardState(scan); const canRetryFailedScannerJobs = !isRunning(scan.status) && hasFailedScannerJobs(scan); const handleRetryFailedScannerJobs = async () => { setIsRetryingFailedScannerJobs(true); try { await actions.retryFailedScannerJobs(scan.id); } finally { setIsRetryingFailedScannerJobs(false); } }; // Reset collapseComplete when expanding useEffect(() => { if (expanded) { setCollapseComplete(false); } }, [expanded]); return ( {/* 3 Row x 6 Column Grid Layout */} {/* Expandable strip with badges */} {showExpandButton && ( )} {/* Expanded content */} {showExpandButton && ( setCollapseComplete(true)} /> )} ); };