fetch file form the docqube
This commit is contained in:
+14
-2
@@ -1452,6 +1452,16 @@ function App() {
|
|||||||
<p className="text-[14px] font-semibold text-[#18212e]">Import Error</p>
|
<p className="text-[14px] font-semibold text-[#18212e]">Import Error</p>
|
||||||
<p className="text-[13px]">{importError}</p>
|
<p className="text-[13px]">{importError}</p>
|
||||||
</div>
|
</div>
|
||||||
|
) : isRemote && !activeDoc && !importError ? (
|
||||||
|
<div className="flex h-full flex-col items-center justify-center gap-4 text-[#98a1ad]">
|
||||||
|
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-blue-50/50">
|
||||||
|
<div className="h-6 w-6 animate-spin rounded-full border-2 border-[#2563eb] border-t-transparent"></div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-[14px] font-semibold text-[#18212e]">Loading Document</p>
|
||||||
|
<p className="text-[12px] mt-1">Preparing your document for editing...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex h-full flex-col items-center justify-center gap-3 text-[#98a1ad]">
|
<div className="flex h-full flex-col items-center justify-center gap-3 text-[#98a1ad]">
|
||||||
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-[#edeff2]">
|
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-[#edeff2]">
|
||||||
@@ -1637,10 +1647,12 @@ function App() {
|
|||||||
|
|
||||||
<MergePDFModal
|
<MergePDFModal
|
||||||
isOpen={mergeModalOpen}
|
isOpen={mergeModalOpen}
|
||||||
onClose={() => setMergeModalOpen(false)}
|
onClose={() => { setMergeModalOpen(false); setMergeInitialFile(null); }}
|
||||||
onMergeComplete={handleMergeCompleted}
|
onMergeComplete={(docInfo) => { setMergeModalOpen(false); forceOpenDocument(docInfo.id); }}
|
||||||
apiBaseUrl={import.meta.env.VITE_GATEWAY_URL || 'http://localhost:8000'}
|
apiBaseUrl={import.meta.env.VITE_GATEWAY_URL || 'http://localhost:8000'}
|
||||||
initialFile={mergeInitialFile}
|
initialFile={mergeInitialFile}
|
||||||
|
parentDocumentId={selectedDocId}
|
||||||
|
isRemote={isRemote}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ interface MergePDFModalProps {
|
|||||||
onMergeComplete: (docInfo: any) => void;
|
onMergeComplete: (docInfo: any) => void;
|
||||||
apiBaseUrl?: string;
|
apiBaseUrl?: string;
|
||||||
initialFile?: File | null;
|
initialFile?: File | null;
|
||||||
|
parentDocumentId?: string;
|
||||||
|
isRemote?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MergePDFModal: React.FC<MergePDFModalProps> = ({
|
export const MergePDFModal: React.FC<MergePDFModalProps> = ({
|
||||||
@@ -24,9 +26,12 @@ export const MergePDFModal: React.FC<MergePDFModalProps> = ({
|
|||||||
onMergeComplete,
|
onMergeComplete,
|
||||||
apiBaseUrl = 'http://localhost:8000',
|
apiBaseUrl = 'http://localhost:8000',
|
||||||
initialFile = null,
|
initialFile = null,
|
||||||
|
parentDocumentId,
|
||||||
|
isRemote = false,
|
||||||
}) => {
|
}) => {
|
||||||
const [files, setFiles] = useState<FileItem[]>([]);
|
const [files, setFiles] = useState<FileItem[]>([]);
|
||||||
const [outputFilename, setOutputFilename] = useState<string>('merged_document.pdf');
|
const [outputFilename, setOutputFilename] = useState<string>('merged_document.pdf');
|
||||||
|
const [isDownloading, setIsDownloading] = useState(false);
|
||||||
const [isMerging, setIsMerging] = useState<boolean>(false);
|
const [isMerging, setIsMerging] = useState<boolean>(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [mergedResult, setMergedResult] = useState<any | null>(null);
|
const [mergedResult, setMergedResult] = useState<any | null>(null);
|
||||||
@@ -51,9 +56,7 @@ export const MergePDFModal: React.FC<MergePDFModalProps> = ({
|
|||||||
}
|
}
|
||||||
}, [isOpen, initialFile]);
|
}, [isOpen, initialFile]);
|
||||||
|
|
||||||
if (!isOpen) return null;
|
const handleAddFiles = React.useCallback((selectedFiles: FileList | null) => {
|
||||||
|
|
||||||
const handleAddFiles = (selectedFiles: FileList | null) => {
|
|
||||||
if (!selectedFiles || selectedFiles.length === 0) return;
|
if (!selectedFiles || selectedFiles.length === 0) return;
|
||||||
setError(null);
|
setError(null);
|
||||||
const newItems: FileItem[] = Array.from(selectedFiles).map((file) => ({
|
const newItems: FileItem[] = Array.from(selectedFiles).map((file) => ({
|
||||||
@@ -63,7 +66,38 @@ export const MergePDFModal: React.FC<MergePDFModalProps> = ({
|
|||||||
customPages: '',
|
customPages: '',
|
||||||
}));
|
}));
|
||||||
setFiles((prev) => [...prev, ...newItems]);
|
setFiles((prev) => [...prev, ...newItems]);
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!isRemote) return;
|
||||||
|
|
||||||
|
const handleMessage = async (event: MessageEvent) => {
|
||||||
|
if (event.data?.type === 'DOCQUBE_FILE_SELECTED') {
|
||||||
|
const { stream_url, token, filename } = event.data.payload;
|
||||||
|
try {
|
||||||
|
setIsDownloading(true);
|
||||||
|
const response = await fetch(stream_url, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch file');
|
||||||
|
const blob = await response.blob();
|
||||||
|
const file = new File([blob], filename || 'document.pdf', { type: 'application/pdf' });
|
||||||
|
handleAddFiles([file] as unknown as FileList);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to download file from DocQube:', err);
|
||||||
|
setError('Failed to download file from DocQube');
|
||||||
|
} finally {
|
||||||
|
setIsDownloading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('message', handleMessage);
|
||||||
|
return () => window.removeEventListener('message', handleMessage);
|
||||||
|
}, [isRemote, handleAddFiles]);
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
const handleMoveUp = (index: number) => {
|
const handleMoveUp = (index: number) => {
|
||||||
if (index <= 0) return;
|
if (index <= 0) return;
|
||||||
@@ -132,6 +166,9 @@ export const MergePDFModal: React.FC<MergePDFModalProps> = ({
|
|||||||
});
|
});
|
||||||
formData.append('manifest', JSON.stringify(manifest));
|
formData.append('manifest', JSON.stringify(manifest));
|
||||||
formData.append('output_filename', outputFilename || 'merged_document.pdf');
|
formData.append('output_filename', outputFilename || 'merged_document.pdf');
|
||||||
|
if (parentDocumentId) {
|
||||||
|
formData.append('parent_document_id', parentDocumentId);
|
||||||
|
}
|
||||||
|
|
||||||
const response = await fetch(`${apiBaseUrl}/documents/merge`, {
|
const response = await fetch(`${apiBaseUrl}/documents/merge`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -247,29 +284,58 @@ export const MergePDFModal: React.FC<MergePDFModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Upload Dropzone */}
|
{/* Upload Dropzones */}
|
||||||
<div
|
<div className="flex flex-col sm:flex-row gap-4 w-full">
|
||||||
onClick={() => fileInputRef.current?.click()}
|
<div
|
||||||
onDragOver={(e) => e.preventDefault()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
onDrop={(e) => {
|
onDragOver={(e) => e.preventDefault()}
|
||||||
e.preventDefault();
|
onDrop={(e) => {
|
||||||
handleAddFiles(e.dataTransfer.files);
|
e.preventDefault();
|
||||||
}}
|
handleAddFiles(e.dataTransfer.files);
|
||||||
className="flex flex-col items-center justify-center p-6 border-2 border-dashed border-border-primary hover:border-brand-primary rounded-xl cursor-pointer bg-bg-secondary/40 hover:bg-bg-secondary transition-all text-center group"
|
}}
|
||||||
>
|
className="flex flex-col items-center justify-center p-6 border-2 border-dashed border-border-primary hover:border-brand-primary rounded-xl cursor-pointer bg-bg-secondary/40 hover:bg-bg-secondary transition-all text-center group flex-1"
|
||||||
<input
|
>
|
||||||
ref={fileInputRef}
|
<input
|
||||||
type="file"
|
ref={fileInputRef}
|
||||||
multiple
|
type="file"
|
||||||
accept=".pdf,image/*"
|
multiple
|
||||||
className="hidden"
|
accept=".pdf,image/*"
|
||||||
onChange={(e) => handleAddFiles(e.target.files)}
|
className="hidden"
|
||||||
/>
|
onChange={(e) => handleAddFiles(e.target.files)}
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-brand-primary/10 text-brand-primary group-hover:scale-110 transition-transform">
|
/>
|
||||||
<UploadIcon size={22} />
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-brand-primary/10 text-brand-primary group-hover:scale-110 transition-transform">
|
||||||
|
<UploadIcon size={22} />
|
||||||
|
</div>
|
||||||
|
<span className="mt-3 text-sm font-bold text-text-primary">Click or drop PDF files here</span>
|
||||||
|
<span className="text-xs text-text-secondary mt-0.5">Select multiple PDF files to combine</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="mt-3 text-sm font-bold text-text-primary">Click or drop PDF files here</span>
|
|
||||||
<span className="text-xs text-text-secondary mt-0.5">Select multiple PDF files to combine</span>
|
{isRemote && (
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
if (!isDownloading) {
|
||||||
|
window.parent.postMessage({ type: 'DOCQUBE_REQUEST_FILE_PICKER' }, '*');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={`flex flex-col items-center justify-center p-6 border-2 border-dashed border-border-primary hover:border-brand-primary rounded-xl bg-bg-secondary/40 hover:bg-bg-secondary transition-all text-center flex-1 ${isDownloading ? 'opacity-70 cursor-not-allowed' : 'cursor-pointer group'}`}
|
||||||
|
>
|
||||||
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-brand-primary/10 text-brand-primary group-hover:scale-110 transition-transform">
|
||||||
|
{isDownloading ? (
|
||||||
|
<SpinnerIcon size={22} className="animate-spin" />
|
||||||
|
) : (
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2.5} strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="mt-3 text-sm font-bold text-text-primary">
|
||||||
|
{isDownloading ? 'Downloading...' : 'Pull from DocQube'}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-text-secondary mt-0.5">
|
||||||
|
{isDownloading ? 'Please wait' : 'Select a PDF from your Drive'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* File Queue List */}
|
{/* File Queue List */}
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ export function useTheme() {
|
|||||||
if (savedTheme) {
|
if (savedTheme) {
|
||||||
return savedTheme;
|
return savedTheme;
|
||||||
}
|
}
|
||||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
||||||
return 'dark';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 'light';
|
return 'light';
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -383,6 +383,7 @@ async def merge_documents(
|
|||||||
files: list[UploadFile] = File(...),
|
files: list[UploadFile] = File(...),
|
||||||
manifest: str = Form(default="[]"),
|
manifest: str = Form(default="[]"),
|
||||||
output_filename: str = Form(default="merged.pdf"),
|
output_filename: str = Form(default="merged.pdf"),
|
||||||
|
parent_document_id: str = Form(default=""),
|
||||||
) -> DocumentInfoResponse:
|
) -> DocumentInfoResponse:
|
||||||
if not engine.is_available():
|
if not engine.is_available():
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -443,6 +444,12 @@ async def merge_documents(
|
|||||||
pdfengine = engine.require()
|
pdfengine = engine.require()
|
||||||
doc = pdfengine.PdfDocument.load_from_memory(merged_bytes, "")
|
doc = pdfengine.PdfDocument.load_from_memory(merged_bytes, "")
|
||||||
info = document_store.add_document(output_filename, merged_bytes, doc)
|
info = document_store.add_document(output_filename, merged_bytes, doc)
|
||||||
|
|
||||||
|
if parent_document_id:
|
||||||
|
parent_doc = document_store.get_document(parent_document_id)
|
||||||
|
if parent_doc and "remote_context" in parent_doc:
|
||||||
|
info["remote_context"] = parent_doc["remote_context"]
|
||||||
|
|
||||||
return make_document_response(info)
|
return make_document_response(info)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
Reference in New Issue
Block a user