Save file

This commit is contained in:
Reece 2025-09-26 01:49:33 +01:00
parent a8a0808274
commit 023fd43b72
3 changed files with 155 additions and 38 deletions

View File

@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
import { Stack, TextInput, FileInput, Paper, Group, Button, Text, Alert, Modal, ColorSwatch, Menu, ActionIcon, Slider, Select, Combobox, useCombobox, ColorPicker, Tabs } from '@mantine/core';
import ButtonSelector from "../../shared/ButtonSelector";
import { SignParameters } from "../../../hooks/tools/sign/useSignParameters";
import { SuggestedToolsSection } from "../shared/SuggestedToolsSection";
interface SignSettingsProps {
parameters: SignParameters;
@ -14,9 +15,10 @@ interface SignSettingsProps {
onUpdateDrawSettings?: (color: string, size: number) => void;
onUndo?: () => void;
onRedo?: () => void;
onSave?: () => void;
}
const SignSettings = ({ parameters, onParameterChange, disabled = false, onActivateDrawMode, onActivateSignaturePlacement, onDeactivateSignature, onUpdateDrawSettings, onUndo, onRedo }: SignSettingsProps) => {
const SignSettings = ({ parameters, onParameterChange, disabled = false, onActivateDrawMode, onActivateSignaturePlacement, onDeactivateSignature, onUpdateDrawSettings, onUndo, onRedo, onSave }: SignSettingsProps) => {
const { t } = useTranslation();
const canvasRef = useRef<HTMLCanvasElement>(null);
const [isDrawing, setIsDrawing] = useState(false);
@ -439,9 +441,6 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
onChange={(value) => onParameterChange('signatureType', value as 'image' | 'text' | 'draw' | 'canvas')}
>
<Tabs.List grow>
<Tabs.Tab value="draw" style={{ fontSize: '0.8rem' }}>
{t('sign.type.draw', 'Draw')}
</Tabs.Tab>
<Tabs.Tab value="canvas" style={{ fontSize: '0.8rem' }}>
{t('sign.type.canvas', 'Canvas')}
</Tabs.Tab>
@ -451,6 +450,9 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
<Tabs.Tab value="text" style={{ fontSize: '0.8rem' }}>
{t('sign.type.text', 'Text')}
</Tabs.Tab>
<Tabs.Tab value="draw" style={{ fontSize: '0.8rem' }}>
{t('sign.type.draw', 'Draw')}
</Tabs.Tab>
</Tabs.List>
</Tabs>
@ -472,6 +474,7 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
</Button>
</Group>
{/* Signature Creation based on type */}
{parameters.signatureType === 'canvas' && (
<Paper withBorder p="md">
@ -958,6 +961,21 @@ const SignSettings = ({ parameters, onParameterChange, disabled = false, onActiv
</Group>
</Stack>
</Modal>
{/* Save Button */}
{onSave && (
<Button
onClick={onSave}
color="green"
variant="filled"
fullWidth
>
{t('save', 'Save')}
</Button>
)}
{/* Suggested Tools Section */}
<SuggestedToolsSection />
</Stack>
);
};

View File

@ -20,7 +20,7 @@ export interface SignParameters {
}
export const DEFAULT_PARAMETERS: SignParameters = {
signatureType: 'draw',
signatureType: 'canvas',
reason: 'Document signing',
location: 'Digital',
signerName: '',

View File

@ -1,4 +1,4 @@
import { useEffect, useCallback } from "react";
import { useEffect, useCallback, useRef } from "react";
import { useTranslation } from "react-i18next";
import { createToolFlow } from "../components/tools/shared/createToolFlow";
import { useSignParameters } from "../hooks/tools/sign/useSignParameters";
@ -8,11 +8,22 @@ import { BaseToolProps, ToolComponent } from "../types/tool";
import SignSettings from "../components/tools/sign/SignSettings";
import { useNavigation } from "../contexts/NavigationContext";
import { useSignature } from "../contexts/SignatureContext";
import { useFileActions, useFileContext } from "../contexts/FileContext";
import { useViewer } from "../contexts/ViewerContext";
import { generateThumbnailWithMetadata } from "../utils/thumbnailUtils";
import { createNewStirlingFileStub, createStirlingFile, StirlingFileStub, StirlingFile, FileId, extractFiles } from "../types/fileContext";
import { createProcessedFile } from "../contexts/file/fileActions";
const Sign = (props: BaseToolProps) => {
const { t } = useTranslation();
const { setWorkbench } = useNavigation();
const { setSignatureConfig, activateDrawMode, activateSignaturePlacementMode, deactivateDrawMode, updateDrawSettings, undo, redo } = useSignature();
const { setSignatureConfig, activateDrawMode, activateSignaturePlacementMode, deactivateDrawMode, updateDrawSettings, undo, redo, isPlacementMode } = useSignature();
const { actions } = useFileActions();
const { consumeFiles, selectors } = useFileContext();
const { exportActions } = useViewer();
// Track which signature mode was active for reactivation after save
const activeModeRef = useRef<'draw' | 'placement' | null>(null);
// Manual sync function
const syncSignatureConfig = () => {
@ -47,30 +58,125 @@ const Sign = (props: BaseToolProps) => {
setSignatureConfig(base.params.parameters);
}, [base.params.parameters, setSignatureConfig]);
// Save signed files to the system - apply signatures using EmbedPDF and replace original
const handleSaveToSystem = useCallback(async () => {
try {
console.log('Save started - attempting to get PDF from viewer...');
// Use EmbedPDF's saveAsCopy to apply signatures and get ArrayBuffer
const pdfArrayBuffer = await exportActions.saveAsCopy();
console.log('Got PDF ArrayBuffer:', pdfArrayBuffer ? `${pdfArrayBuffer.byteLength} bytes` : 'null');
console.log('Checking conditions - ArrayBuffer exists:', !!pdfArrayBuffer, 'Selected files:', base.selectedFiles.length);
if (pdfArrayBuffer) {
console.log('Conditions met, starting file processing...');
// Convert ArrayBuffer to File
const blob = new Blob([pdfArrayBuffer], { type: 'application/pdf' });
// Get the current file - try from base.selectedFiles first, then from all files
let originalFile = null;
if (base.selectedFiles.length > 0) {
originalFile = base.selectedFiles[0];
} else {
const allFileIds = selectors.getAllFileIds();
if (allFileIds.length > 0) {
const fileStub = selectors.getStirlingFileStub(allFileIds[0]);
const fileObject = selectors.getFile(allFileIds[0]);
if (fileStub && fileObject) {
originalFile = createStirlingFile(fileObject, allFileIds[0]);
}
}
}
if (!originalFile) {
console.error('No file available to replace');
return;
}
console.log('Original file:', originalFile.name, 'ID:', originalFile.fileId);
const signedFile = new File([blob], originalFile.name, { type: 'application/pdf' });
console.log('Created signed file:', signedFile.name, 'Size:', signedFile.size);
console.log('Processing signed file...');
// Generate thumbnail and metadata for the signed file
const thumbnailResult = await generateThumbnailWithMetadata(signedFile);
const processedFileMetadata = createProcessedFile(thumbnailResult.pageCount, thumbnailResult.thumbnail);
// Prepare input file data for replacement
const inputFileIds: FileId[] = [originalFile.fileId];
const inputStirlingFileStubs: StirlingFileStub[] = [];
console.log('Original file ID:', originalFile.fileId);
const record = selectors.getStirlingFileStub(originalFile.fileId);
if (record) {
inputStirlingFileStubs.push(record);
console.log('Found file record for replacement');
} else {
console.error('No file record found for:', originalFile.fileId);
}
// Create output stub and file
const outputStub = createNewStirlingFileStub(signedFile, undefined, thumbnailResult.thumbnail, processedFileMetadata);
const outputStirlingFile = createStirlingFile(signedFile, outputStub.id);
console.log('Created new file with ID:', outputStub.id);
// Replace the original file with the signed version
console.log('Replacing file in context...');
await consumeFiles(inputFileIds, [outputStirlingFile], [outputStub]);
console.log('File replacement complete');
// Reactivate the signature mode that was active before save
setTimeout(() => {
if (activeModeRef.current === 'draw') {
console.log('Reactivating draw mode');
activateDrawMode();
} else if (activeModeRef.current === 'placement') {
console.log('Reactivating placement mode');
handleSignaturePlacement();
}
}, 200);
} else {
console.log('Save aborted - conditions not met');
if (!pdfArrayBuffer) console.log('No PDF ArrayBuffer received');
if (base.selectedFiles.length === 0) console.log('No selected files');
}
} catch (error) {
console.error('Error saving signed document:', error);
}
}, [exportActions, base.selectedFiles, selectors, consumeFiles]);
const getSteps = () => {
const steps = [];
// Step 1: Signature Configuration
if (base.selectedFiles.length > 0 || base.operation.files.length > 0) {
steps.push({
title: t('sign.steps.configure', 'Configure Signature'),
isCollapsed: base.operation.files.length > 0,
onCollapsedClick: base.operation.files.length > 0 ? base.handleSettingsReset : undefined,
content: (
<SignSettings
parameters={base.params.parameters}
onParameterChange={base.params.updateParameter}
disabled={base.endpointLoading}
onActivateDrawMode={() => activateDrawMode()}
onActivateSignaturePlacement={handleSignaturePlacement}
onDeactivateSignature={deactivateDrawMode}
onUpdateDrawSettings={updateDrawSettings}
onUndo={undo}
onRedo={redo}
/>
),
});
}
// Step 1: Signature Configuration - Always visible
steps.push({
title: t('sign.steps.configure', 'Configure Signature'),
isCollapsed: false,
onCollapsedClick: undefined,
content: (
<SignSettings
parameters={base.params.parameters}
onParameterChange={base.params.updateParameter}
disabled={base.endpointLoading}
onActivateDrawMode={() => {
activeModeRef.current = 'draw';
activateDrawMode();
}}
onActivateSignaturePlacement={() => {
activeModeRef.current = 'placement';
handleSignaturePlacement();
}}
onDeactivateSignature={deactivateDrawMode}
onUpdateDrawSettings={updateDrawSettings}
onUndo={undo}
onRedo={redo}
onSave={handleSaveToSystem}
/>
),
});
return steps;
};
@ -81,19 +187,12 @@ const Sign = (props: BaseToolProps) => {
isCollapsed: base.operation.files.length > 0,
},
steps: getSteps(),
executeButton: {
text: t('sign.submit', 'Sign Document'),
isVisible: false, // Hide the execute button - signatures are placed directly
loadingText: t('loading'),
onClick: base.handleExecute,
disabled: !base.params.validateParameters() || base.selectedFiles.length === 0 || !base.endpointEnabled,
},
review: {
isVisible: base.operation.files.length > 0,
isVisible: false, // Hide review section - save moved to configure section
operation: base.operation,
title: t('sign.results.title', 'Signature Results'),
onFileClick: base.handleThumbnailClick,
onUndo: base.handleUndo,
onUndo: () => {},
},
forceStepNumbers: true,
});
@ -102,7 +201,7 @@ const Sign = (props: BaseToolProps) => {
// Add the required static methods for automation
Sign.tool = () => useSignOperation;
Sign.getDefaultParameters = () => ({
signatureType: 'draw',
signatureType: 'canvas',
reason: 'Document signing',
location: 'Digital',
signerName: '',