mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 14:19:24 +00:00
suggested tool structure
This commit is contained in:
parent
ee6f7a2939
commit
fa4098504a
@ -1,25 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, Box, Flex, ActionIcon, Tooltip } from '@mantine/core';
|
import { Text } from '@mantine/core';
|
||||||
import PushPinIcon from '@mui/icons-material/PushPin';
|
|
||||||
import PushPinOutlinedIcon from '@mui/icons-material/PushPinOutlined';
|
|
||||||
import { useFileContext } from '../../../contexts/FileContext';
|
|
||||||
|
|
||||||
export interface FileStatusIndicatorProps {
|
export interface FileStatusIndicatorProps {
|
||||||
selectedFiles?: File[];
|
selectedFiles?: File[];
|
||||||
isCompleted?: boolean;
|
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
showFileName?: boolean;
|
|
||||||
showPinControls?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FileStatusIndicator = ({
|
const FileStatusIndicator = ({
|
||||||
selectedFiles = [],
|
selectedFiles = [],
|
||||||
isCompleted = false,
|
placeholder = "Select a PDF file in the main view to get started"
|
||||||
placeholder = "Select a PDF file in the main view to get started",
|
|
||||||
showFileName = true,
|
|
||||||
showPinControls = true
|
|
||||||
}: FileStatusIndicatorProps) => {
|
}: FileStatusIndicatorProps) => {
|
||||||
const { pinFile, unpinFile, isFilePinned } = useFileContext();
|
|
||||||
|
// Only show content when no files are selected
|
||||||
if (selectedFiles.length === 0) {
|
if (selectedFiles.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
@ -28,19 +20,8 @@ const FileStatusIndicator = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCompleted) {
|
// Return nothing when files are selected
|
||||||
return (
|
return null;
|
||||||
<Text size="sm" c="green">
|
|
||||||
✓ Selected: {showFileName ? selectedFiles[0]?.name : `${selectedFiles.length} file${selectedFiles.length > 1 ? 's' : ''}`}
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Text size="sm" c="blue">
|
|
||||||
Selected: {showFileName ? selectedFiles[0]?.name : `${selectedFiles.length} file${selectedFiles.length > 1 ? 's' : ''}`}
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FileStatusIndicator;
|
export default FileStatusIndicator;
|
@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Stack, Text, Divider, SimpleGrid, Card, Group } from '@mantine/core';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useSuggestedTools } from '../../../hooks/useSuggestedTools';
|
||||||
|
export interface SuggestedToolsSectionProps {}
|
||||||
|
|
||||||
|
export function SuggestedToolsSection(): React.ReactElement {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const suggestedTools = useSuggestedTools();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack gap="md">
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<Text size="lg" fw={600}>
|
||||||
|
{t('editYourNewFiles', 'Edit your new File(s)')}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<SimpleGrid cols={2} spacing="sm">
|
||||||
|
{suggestedTools.map((tool) => {
|
||||||
|
const IconComponent = tool.icon;
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
key={tool.name}
|
||||||
|
p="sm"
|
||||||
|
withBorder
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={tool.navigate}
|
||||||
|
>
|
||||||
|
<Group gap="xs">
|
||||||
|
<IconComponent fontSize="small" />
|
||||||
|
<Text size="sm" fw={500}>
|
||||||
|
{tool.title}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import DownloadIcon from '@mui/icons-material/Download';
|
import DownloadIcon from '@mui/icons-material/Download';
|
||||||
import ErrorNotification from './ErrorNotification';
|
import ErrorNotification from './ErrorNotification';
|
||||||
import ReviewPanel from './ReviewPanel';
|
import ReviewPanel from './ReviewPanel';
|
||||||
|
import { SuggestedToolsSection } from './SuggestedToolsSection';
|
||||||
import { ToolOperationHook } from '../../../hooks/tools/shared/useToolOperation';
|
import { ToolOperationHook } from '../../../hooks/tools/shared/useToolOperation';
|
||||||
|
|
||||||
export interface ReviewToolStepProps<TParams = any> {
|
export interface ReviewToolStepProps<TParams = any> {
|
||||||
@ -31,10 +32,6 @@ export function createReviewToolStep<TParams = any>(
|
|||||||
_noPadding: true
|
_noPadding: true
|
||||||
}, (
|
}, (
|
||||||
<Stack gap="sm" >
|
<Stack gap="sm" >
|
||||||
{operation.status && (
|
|
||||||
<Text size="sm" c="dimmed">{operation.status}</Text>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<ErrorNotification
|
<ErrorNotification
|
||||||
error={operation.errorMessage}
|
error={operation.errorMessage}
|
||||||
onClose={operation.clearError}
|
onClose={operation.clearError}
|
||||||
@ -62,6 +59,8 @@ export function createReviewToolStep<TParams = any>(
|
|||||||
{t("download", "Download")}
|
{t("download", "Download")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<SuggestedToolsSection />
|
||||||
</Stack>
|
</Stack>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Stack } from '@mantine/core';
|
||||||
import { createToolSteps, ToolStepProvider } from './ToolStep';
|
import { createToolSteps, ToolStepProvider } from './ToolStep';
|
||||||
import OperationButton from './OperationButton';
|
import OperationButton from './OperationButton';
|
||||||
import { ToolOperationHook } from '../../../hooks/tools/shared/useToolOperation';
|
import { ToolOperationHook } from '../../../hooks/tools/shared/useToolOperation';
|
||||||
@ -58,6 +59,7 @@ export function createToolFlow(config: ToolFlowConfig) {
|
|||||||
const steps = createToolSteps();
|
const steps = createToolSteps();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Stack gap="sm" p="sm" style={{ height: '100vh', overflow: 'auto' }}>
|
||||||
<ToolStepProvider>
|
<ToolStepProvider>
|
||||||
{/* Files Step */}
|
{/* Files Step */}
|
||||||
{steps.createFilesStep({
|
{steps.createFilesStep({
|
||||||
@ -97,5 +99,6 @@ export function createToolFlow(config: ToolFlowConfig) {
|
|||||||
onFileClick: config.review.onFileClick
|
onFileClick: config.review.onFileClick
|
||||||
})}
|
})}
|
||||||
</ToolStepProvider>
|
</ToolStepProvider>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
59
frontend/src/hooks/useSuggestedTools.ts
Normal file
59
frontend/src/hooks/useSuggestedTools.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useToolWorkflow } from '../contexts/ToolWorkflowContext';
|
||||||
|
|
||||||
|
// Material UI Icons
|
||||||
|
import CompressIcon from '@mui/icons-material/Compress';
|
||||||
|
import SwapHorizIcon from '@mui/icons-material/SwapHoriz';
|
||||||
|
import CleaningServicesIcon from '@mui/icons-material/CleaningServices';
|
||||||
|
import CropIcon from '@mui/icons-material/Crop';
|
||||||
|
import TextFieldsIcon from '@mui/icons-material/TextFields';
|
||||||
|
|
||||||
|
export interface SuggestedTool {
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
icon: React.ComponentType<any>;
|
||||||
|
navigate: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ALL_SUGGESTED_TOOLS: Omit<SuggestedTool, 'navigate'>[] = [
|
||||||
|
{
|
||||||
|
name: 'compress',
|
||||||
|
title: 'Compress',
|
||||||
|
icon: CompressIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'convert',
|
||||||
|
title: 'Convert',
|
||||||
|
icon: SwapHorizIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sanitize',
|
||||||
|
title: 'Sanitize',
|
||||||
|
icon: CleaningServicesIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'split',
|
||||||
|
title: 'Split',
|
||||||
|
icon: CropIcon
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ocr',
|
||||||
|
title: 'OCR',
|
||||||
|
icon: TextFieldsIcon
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export function useSuggestedTools(): SuggestedTool[] {
|
||||||
|
const { handleToolSelect, selectedToolKey } = useToolWorkflow();
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
// Filter out the current tool
|
||||||
|
const filteredTools = ALL_SUGGESTED_TOOLS.filter(tool => tool.name !== selectedToolKey);
|
||||||
|
|
||||||
|
// Add navigation function to each tool
|
||||||
|
return filteredTools.map(tool => ({
|
||||||
|
...tool,
|
||||||
|
navigate: () => handleToolSelect(tool.name)
|
||||||
|
}));
|
||||||
|
}, [selectedToolKey, handleToolSelect]);
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { Stack } from "@mantine/core";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
||||||
import { useFileContext } from "../contexts/FileContext";
|
import { useFileContext } from "../contexts/FileContext";
|
||||||
@ -63,9 +62,7 @@ const Compress = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const hasResults = compressOperation.files.length > 0 || compressOperation.downloadUrl !== null;
|
const hasResults = compressOperation.files.length > 0 || compressOperation.downloadUrl !== null;
|
||||||
const settingsCollapsed = !hasFiles || hasResults;
|
const settingsCollapsed = !hasFiles || hasResults;
|
||||||
|
|
||||||
return (
|
return createToolFlow({
|
||||||
<Stack gap="sm" p="sm" style={{ height: '100vh', overflow: 'auto' }}>
|
|
||||||
{createToolFlow({
|
|
||||||
files: {
|
files: {
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: hasFiles && !hasResults,
|
isCollapsed: hasFiles && !hasResults,
|
||||||
@ -96,9 +93,7 @@ const Compress = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
title: t("compress.title", "Compression Results"),
|
title: t("compress.title", "Compression Results"),
|
||||||
onFileClick: handleThumbnailClick
|
onFileClick: handleThumbnailClick
|
||||||
}
|
}
|
||||||
})}
|
});
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { Stack } from "@mantine/core";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
||||||
import { useFileContext } from "../contexts/FileContext";
|
import { useFileContext } from "../contexts/FileContext";
|
||||||
@ -100,9 +99,7 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
setCurrentMode('convert');
|
setCurrentMode('convert');
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return createToolFlow({
|
||||||
<Stack gap="sm" p="sm" style={{ height: '100vh', overflow: 'auto' }}>
|
|
||||||
{createToolFlow({
|
|
||||||
files: {
|
files: {
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: filesCollapsed,
|
isCollapsed: filesCollapsed,
|
||||||
@ -137,9 +134,7 @@ const Convert = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
onFileClick: handleThumbnailClick,
|
onFileClick: handleThumbnailClick,
|
||||||
testId: "conversion-results"
|
testId: "conversion-results"
|
||||||
}
|
}
|
||||||
})}
|
});
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Convert;
|
export default Convert;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { Stack } from "@mantine/core";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
||||||
import { useFileContext } from "../contexts/FileContext";
|
import { useFileContext } from "../contexts/FileContext";
|
||||||
@ -80,15 +79,12 @@ const OCR = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const filesCollapsed = expandedStep !== 'files';
|
|
||||||
const settingsCollapsed = expandedStep !== 'settings';
|
const settingsCollapsed = expandedStep !== 'settings';
|
||||||
|
|
||||||
return (
|
return createToolFlow({
|
||||||
<Stack gap="sm" p="sm" style={{ height: '100vh', overflow: 'auto' }}>
|
|
||||||
{createToolFlow({
|
|
||||||
files: {
|
files: {
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: hasFiles && !hasResults && filesCollapsed,
|
isCollapsed: hasFiles || hasResults,
|
||||||
},
|
},
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
@ -137,9 +133,7 @@ const OCR = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
title: t("ocr.results.title", "OCR Results"),
|
title: t("ocr.results.title", "OCR Results"),
|
||||||
onFileClick: handleThumbnailClick
|
onFileClick: handleThumbnailClick
|
||||||
}
|
}
|
||||||
})}
|
});
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OCR;
|
export default OCR;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { Stack } from "@mantine/core";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
||||||
import { useToolFileSelection } from "../contexts/FileSelectionContext";
|
import { useToolFileSelection } from "../contexts/FileSelectionContext";
|
||||||
@ -64,9 +63,7 @@ const Sanitize = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const filesCollapsed = hasFiles || hasResults;
|
const filesCollapsed = hasFiles || hasResults;
|
||||||
const settingsCollapsed = !hasFiles || hasResults;
|
const settingsCollapsed = !hasFiles || hasResults;
|
||||||
|
|
||||||
return (
|
return createToolFlow({
|
||||||
<Stack gap="sm" p="sm" style={{ height: '100vh', overflow: 'auto' }}>
|
|
||||||
{createToolFlow({
|
|
||||||
files: {
|
files: {
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: filesCollapsed,
|
isCollapsed: filesCollapsed,
|
||||||
@ -97,9 +94,7 @@ const Sanitize = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
title: t("sanitize.sanitizationResults", "Sanitization Results"),
|
title: t("sanitize.sanitizationResults", "Sanitization Results"),
|
||||||
onFileClick: handleThumbnailClick
|
onFileClick: handleThumbnailClick
|
||||||
}
|
}
|
||||||
})}
|
});
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Sanitize;
|
export default Sanitize;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { Stack } from "@mantine/core";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
import { useEndpointEnabled } from "../hooks/useEndpointConfig";
|
||||||
import { useFileContext } from "../contexts/FileContext";
|
import { useFileContext } from "../contexts/FileContext";
|
||||||
@ -63,9 +62,7 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
const filesCollapsed = hasFiles;
|
const filesCollapsed = hasFiles;
|
||||||
const settingsCollapsed = !hasFiles || hasResults;
|
const settingsCollapsed = !hasFiles || hasResults;
|
||||||
|
|
||||||
return (
|
return createToolFlow({
|
||||||
<Stack gap="sm" h="100%" p="sm" style={{ overflow: 'auto' }}>
|
|
||||||
{createToolFlow({
|
|
||||||
files: {
|
files: {
|
||||||
selectedFiles,
|
selectedFiles,
|
||||||
isCollapsed: filesCollapsed,
|
isCollapsed: filesCollapsed,
|
||||||
@ -96,9 +93,7 @@ const Split = ({ onPreviewFile, onComplete, onError }: BaseToolProps) => {
|
|||||||
title: "Split Results",
|
title: "Split Results",
|
||||||
onFileClick: handleThumbnailClick
|
onFileClick: handleThumbnailClick
|
||||||
}
|
}
|
||||||
})}
|
});
|
||||||
</Stack>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Split;
|
export default Split;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user