mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-27 22:59:22 +00:00

🔄 Dynamic Processing Strategies - Adaptive routing: Same tool uses different backend endpoints based on file analysis - Combined vs separate processing: Intelligently chooses between merge operations and individual file processing - Cross-format workflows: Enable complex conversions like "mixed files → PDF" that other tools can't handle ⚙️ Format-Specific Intelligence Each conversion type gets tailored options: - HTML/ZIP → PDF: Zoom controls (0.1-3.0 increments) with live preview - Email → PDF: Attachment handling, size limits, recipient control - PDF → PDF/A: Digital signature detection with warnings - Images → PDF: Smart combining vs individual file options File Architecture Core Implementation: ├── Convert.tsx # Main stepped workflow UI ├── ConvertSettings.tsx # Centralized settings with smart detection ├── GroupedFormatDropdown.tsx # Enhanced format selector with grouping ├── useConvertParameters.ts # Smart detection & parameter management ├── useConvertOperation.ts # Multi-strategy processing logic └── Settings Components: ├── ConvertFromWebSettings.tsx # HTML zoom controls ├── ConvertFromEmailSettings.tsx # Email attachment options ├── ConvertToPdfaSettings.tsx # PDF/A with signature detection ├── ConvertFromImageSettings.tsx # Image PDF options └── ConvertToImageSettings.tsx # PDF to image options Utility Layer Utils & Services: ├── convertUtils.ts # Format detection & endpoint routing ├── fileResponseUtils.ts # Generic API response handling └── setupTests.ts # Enhanced test environment with crypto mocks Testing & Quality Comprehensive Test Coverage Test Suite: ├── useConvertParameters.test.ts # Parameter logic & smart detection ├── useConvertParametersAutoDetection.test.ts # File type analysis ├── ConvertIntegration.test.tsx # End-to-end conversion workflows ├── ConvertSmartDetectionIntegration.test.tsx # Mixed file scenarios ├── ConvertE2E.spec.ts # Playwright browser tests ├── convertUtils.test.ts # Utility function validation └── fileResponseUtils.test.ts # API response handling Advanced Test Features - Crypto API mocking: Proper test environment for file hashing - File.arrayBuffer() polyfills: Complete browser API simulation - Multi-file scenario testing: Complex batch processing validation - CI/CD integration: Vitest runs in GitHub Actions with proper artifacts --------- Co-authored-by: Connor Yoh <connor@stirlingpdf.com> Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com>
114 lines
3.4 KiB
TypeScript
114 lines
3.4 KiB
TypeScript
import { Grid, Paper, Box, Image, Text, Loader, Stack, Center } from '@mantine/core';
|
|
|
|
export interface ResultFile {
|
|
file: File;
|
|
thumbnail?: string;
|
|
}
|
|
|
|
export interface ResultsPreviewProps {
|
|
files: ResultFile[];
|
|
isGeneratingThumbnails?: boolean;
|
|
onFileClick?: (file: File) => void;
|
|
title?: string;
|
|
emptyMessage?: string;
|
|
loadingMessage?: string;
|
|
}
|
|
|
|
const ResultsPreview = ({
|
|
files,
|
|
isGeneratingThumbnails = false,
|
|
onFileClick,
|
|
title,
|
|
emptyMessage = "No files to preview",
|
|
loadingMessage = "Generating previews..."
|
|
}: ResultsPreviewProps) => {
|
|
const formatSize = (size: number) => {
|
|
if (size > 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
if (size > 1024) return `${(size / 1024).toFixed(1)} KB`;
|
|
return `${size} B`;
|
|
};
|
|
|
|
if (files.length === 0 && !isGeneratingThumbnails) {
|
|
return (
|
|
<Text size="sm" c="dimmed">
|
|
{emptyMessage}
|
|
</Text>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Box mt="lg" p="md" style={{ backgroundColor: 'var(--mantine-color-gray-0)', borderRadius: 8 }} data-testid="results-preview-container">
|
|
{title && (
|
|
<Text fw={500} size="md" mb="sm" data-testid="results-preview-title">
|
|
{title} ({files.length} files)
|
|
</Text>
|
|
)}
|
|
|
|
{isGeneratingThumbnails ? (
|
|
<Center p="lg" data-testid="results-preview-loading">
|
|
<Stack align="center" gap="sm">
|
|
<Loader size="sm" />
|
|
<Text size="sm" c="dimmed">{loadingMessage}</Text>
|
|
</Stack>
|
|
</Center>
|
|
) : (
|
|
<Grid data-testid="results-preview-grid">
|
|
{files.map((result, index) => (
|
|
<Grid.Col span={{ base: 6, sm: 4, md: 3 }} key={index}>
|
|
<Paper
|
|
p="xs"
|
|
withBorder
|
|
onClick={() => onFileClick?.(result.file)}
|
|
data-testid={`results-preview-thumbnail-${index}`}
|
|
style={{
|
|
textAlign: 'center',
|
|
height: '10rem',
|
|
width:'5rem',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
cursor: onFileClick ? 'pointer' : 'default',
|
|
transition: 'all 0.2s ease'
|
|
}}
|
|
>
|
|
<Box style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
{result.thumbnail ? (
|
|
<Image
|
|
src={result.thumbnail}
|
|
alt={`Preview of ${result.file.name}`}
|
|
style={{
|
|
maxWidth: '100%',
|
|
maxHeight: '9rem',
|
|
objectFit: 'contain'
|
|
}}
|
|
/>
|
|
) : (
|
|
<Text size="xs" c="dimmed">No preview</Text>
|
|
)}
|
|
</Box>
|
|
<Text
|
|
size="xs"
|
|
c="dimmed"
|
|
mt="xs"
|
|
style={{
|
|
overflow: 'hidden',
|
|
textOverflow: 'ellipsis',
|
|
whiteSpace: 'nowrap'
|
|
}}
|
|
title={result.file.name}
|
|
>
|
|
{result.file.name}
|
|
</Text>
|
|
<Text size="xs" c="dimmed">
|
|
{formatSize(result.file.size)}
|
|
</Text>
|
|
</Paper>
|
|
</Grid.Col>
|
|
))}
|
|
</Grid>
|
|
)}
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
export default ResultsPreview;
|