pdf flow
This commit is contained in:
parent
a162db1388
commit
8255b58552
56
add_modal_to_template.js
Normal file
56
add_modal_to_template.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Add modal section to the end of the template, before closing </template>
|
||||||
|
const modalHTML = `
|
||||||
|
<!-- PDF Download Modal -->
|
||||||
|
<template if:true={showDownloadModal}>
|
||||||
|
<div class="pdf-download-modal-overlay" onclick={closeDownloadModal}>
|
||||||
|
<div class="pdf-download-modal" onclick={stopPropagation}>
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>🎉 PDF Ready for Download!</h2>
|
||||||
|
<button class="close-btn" onclick={closeDownloadModal}>×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="download-info">
|
||||||
|
<h3>📄 Download Information</h3>
|
||||||
|
|
||||||
|
<div class="file-details">
|
||||||
|
<div class="file-detail">
|
||||||
|
<div class="label">File Name</div>
|
||||||
|
<div class="value">{downloadInfo.filename}</div>
|
||||||
|
</div>
|
||||||
|
<div class="file-detail">
|
||||||
|
<div class="label">File Size</div>
|
||||||
|
<div class="value">{downloadInfo.fileSize}</div>
|
||||||
|
</div>
|
||||||
|
<div class="file-detail">
|
||||||
|
<div class="label">Generated</div>
|
||||||
|
<div class="value">{downloadInfo.generatedAt}</div>
|
||||||
|
</div>
|
||||||
|
<div class="file-detail">
|
||||||
|
<div class="label">Expires</div>
|
||||||
|
<div class="value">{downloadInfo.expiresAt}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="download-link-section">
|
||||||
|
<label>🔗 Download Link:</label>
|
||||||
|
<div class="download-link">{downloadInfo.downloadUrl}</div>
|
||||||
|
<button class="copy-btn" onclick={copyDownloadLink}>📋 Copy Link</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-actions">
|
||||||
|
<a href={downloadInfo.downloadUrl} target="_blank" class="download-btn">
|
||||||
|
📥 Download PDF
|
||||||
|
</a>
|
||||||
|
<button class="open-tab-btn" onclick={openInNewTab}>
|
||||||
|
🔗 Open in New Tab
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
`;
|
||||||
|
|
||||||
|
console.log("Modal HTML to add:", modalHTML);
|
||||||
41
deploy-manual.sh
Executable file
41
deploy-manual.sh
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "🚀 Manual Salesforce Deployment Instructions"
|
||||||
|
echo "============================================="
|
||||||
|
echo ""
|
||||||
|
echo "Since we don't have Salesforce CLI authentication set up, here are the manual steps:"
|
||||||
|
echo ""
|
||||||
|
echo "1. 📋 FILES TO DEPLOY:"
|
||||||
|
echo " - force-app/main/default/classes/PDFGenerationProxy.cls"
|
||||||
|
echo " - force-app/main/default/classes/PDFGenerationProxy.cls-meta.xml"
|
||||||
|
echo " - force-app/main/default/lwc/propertyTemplateSelector/"
|
||||||
|
echo ""
|
||||||
|
echo "2. 🔐 AUTHENTICATION OPTIONS:"
|
||||||
|
echo ""
|
||||||
|
echo " OPTION A - Web Login (Recommended):"
|
||||||
|
echo " sf org login web --alias myorg"
|
||||||
|
echo ""
|
||||||
|
echo " OPTION B - JWT Authentication:"
|
||||||
|
echo " sf org login jwt --client-id YOUR_CLIENT_ID --jwt-key-file server.key --username YOUR_USERNAME --alias myorg"
|
||||||
|
echo ""
|
||||||
|
echo "3. 🚀 DEPLOYMENT COMMANDS:"
|
||||||
|
echo " sf project deploy start --source-dir force-app/main/default/classes --target-org myorg"
|
||||||
|
echo " sf project deploy start --source-dir force-app/main/default/lwc --target-org myorg"
|
||||||
|
echo ""
|
||||||
|
echo "4. ✅ VERIFICATION:"
|
||||||
|
echo " sf org display --target-org myorg"
|
||||||
|
echo ""
|
||||||
|
echo "📁 Current files ready for deployment:"
|
||||||
|
echo "======================================"
|
||||||
|
find force-app -name "*.cls" -o -name "*.js" -o -name "*.xml" | sort
|
||||||
|
echo ""
|
||||||
|
echo "🎯 The PDFGenerationProxy.cls is already updated with:"
|
||||||
|
echo " ✅ return_download_link: true support"
|
||||||
|
echo " ✅ Proper error handling"
|
||||||
|
echo " ✅ Node.js API integration"
|
||||||
|
echo " ✅ 6MB size limit handling"
|
||||||
|
echo ""
|
||||||
|
echo "💡 After deployment, test with:"
|
||||||
|
echo " - Generate a PDF with return_download_link: true"
|
||||||
|
echo " - Verify the download link is returned"
|
||||||
|
echo " - Test the download endpoint"
|
||||||
@ -8,40 +8,63 @@ public with sharing class PDFGenerationProxy {
|
|||||||
throw new AuraHandledException('HTML content cannot be empty. Please provide valid HTML content.');
|
throw new AuraHandledException('HTML content cannot be empty. Please provide valid HTML content.');
|
||||||
}
|
}
|
||||||
|
|
||||||
System.debug('=== PDF GENERATION DEBUG (TEMPORARY FIX) ===');
|
System.debug('=== PDF GENERATION DEBUG ===');
|
||||||
System.debug('HTML Content Length: ' + htmlContent.length());
|
System.debug('HTML Content Length: ' + htmlContent.length());
|
||||||
System.debug('Page Size: ' + pageSize);
|
System.debug('Page Size: ' + pageSize);
|
||||||
|
|
||||||
// Generate unique PDF ID for tracking
|
// Call the Node.js API with return_download_link: true
|
||||||
String pdfId = 'PDF_' + DateTime.now().getTime() + '_' + Math.random();
|
Http http = new Http();
|
||||||
|
HttpRequest request = new HttpRequest();
|
||||||
|
|
||||||
// TEMPORARY FIX: Return download link without calling Python server
|
// Set the endpoint URL
|
||||||
// This eliminates the 6MB limit issue immediately
|
request.setEndpoint('https://salesforce.tech4biz.io/generate-pdf');
|
||||||
Map<String, Object> result = new Map<String, Object>();
|
request.setMethod('POST');
|
||||||
result.put('success', true);
|
request.setHeader('Content-Type', 'application/json');
|
||||||
result.put('pdf_id', pdfId);
|
request.setTimeout(120000); // 2 minutes timeout
|
||||||
result.put('status', 'download_ready');
|
|
||||||
result.put('message', 'PDF generation request received. Download link will be available shortly.');
|
|
||||||
|
|
||||||
// Create a temporary download URL (replace with actual Python server URL when ready)
|
// Prepare the request body
|
||||||
result.put('download_url', 'https://salesforce.tech4biz.io/download-pdf/' + pdfId);
|
Map<String, Object> requestBody = new Map<String, Object>();
|
||||||
|
requestBody.put('input', htmlContent);
|
||||||
|
requestBody.put('return_download_link', true);
|
||||||
|
if (String.isNotBlank(pageSize)) {
|
||||||
|
requestBody.put('page_size', pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
// Add additional information
|
String jsonBody = JSON.serialize(requestBody);
|
||||||
result.put('page_size', pageSize != null ? pageSize : 'A4');
|
request.setBody(jsonBody);
|
||||||
result.put('generated_at', Datetime.now().format('yyyy-MM-dd HH:mm:ss'));
|
|
||||||
result.put('compression_applied', true);
|
|
||||||
result.put('file_size_mb', 'TBD');
|
|
||||||
result.put('expires_at', Datetime.now().addDays(7).format('yyyy-MM-dd HH:mm:ss'));
|
|
||||||
result.put('pdf_stored', true);
|
|
||||||
result.put('temp_folder_path', '/temp/pdfs/' + pdfId);
|
|
||||||
result.put('backend_status', 'TEMPORARY_FIX_ACTIVE');
|
|
||||||
result.put('note', 'This is a temporary fix to eliminate 6MB limit. Python server integration pending.');
|
|
||||||
|
|
||||||
System.debug('Temporary fix applied - returning download link without API call');
|
System.debug('Request body: ' + jsonBody);
|
||||||
return result;
|
|
||||||
|
// Make the HTTP call
|
||||||
|
HttpResponse response = http.send(request);
|
||||||
|
|
||||||
|
System.debug('Response status: ' + response.getStatusCode());
|
||||||
|
System.debug('Response body: ' + response.getBody());
|
||||||
|
|
||||||
|
if (response.getStatusCode() == 200) {
|
||||||
|
// Parse the response
|
||||||
|
Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
|
||||||
|
|
||||||
|
// Add additional metadata
|
||||||
|
result.put('salesforce_generated_at', Datetime.now().format('yyyy-MM-dd HH:mm:ss'));
|
||||||
|
result.put('api_version', '7.0.0');
|
||||||
|
result.put('backend_status', 'NODE_JS_API_ACTIVE');
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
// Handle error response
|
||||||
|
Map<String, Object> errorResult = new Map<String, Object>();
|
||||||
|
errorResult.put('success', false);
|
||||||
|
errorResult.put('error', 'PDF generation failed: HTTP ' + response.getStatusCode());
|
||||||
|
errorResult.put('message', response.getBody());
|
||||||
|
errorResult.put('pdf_id', 'ERROR_' + DateTime.now().getTime());
|
||||||
|
|
||||||
|
return errorResult;
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.debug('Exception in generatePDFFromHTML: ' + e.getMessage());
|
System.debug('Exception in generatePDFFromHTML: ' + e.getMessage());
|
||||||
|
System.debug('Exception stack trace: ' + e.getStackTraceString());
|
||||||
|
|
||||||
Map<String, Object> errorResult = new Map<String, Object>();
|
Map<String, Object> errorResult = new Map<String, Object>();
|
||||||
errorResult.put('success', false);
|
errorResult.put('success', false);
|
||||||
@ -61,37 +84,66 @@ public with sharing class PDFGenerationProxy {
|
|||||||
throw new AuraHandledException('HTML content cannot be empty. Please provide valid HTML content.');
|
throw new AuraHandledException('HTML content cannot be empty. Please provide valid HTML content.');
|
||||||
}
|
}
|
||||||
|
|
||||||
System.debug('=== COMPRESSED PDF GENERATION DEBUG (TEMPORARY FIX) ===');
|
System.debug('=== COMPRESSED PDF GENERATION DEBUG ===');
|
||||||
System.debug('HTML Content Length: ' + htmlContent.length());
|
System.debug('HTML Content Length: ' + htmlContent.length());
|
||||||
System.debug('Page Size: ' + pageSize);
|
System.debug('Page Size: ' + pageSize);
|
||||||
|
|
||||||
// Generate unique PDF ID for tracking
|
// Call the Node.js API with return_download_link: true
|
||||||
String pdfId = 'COMPRESSED_PDF_' + DateTime.now().getTime() + '_' + Math.random();
|
Http http = new Http();
|
||||||
|
HttpRequest request = new HttpRequest();
|
||||||
|
|
||||||
// TEMPORARY FIX: Return download link without calling Python server
|
// Set the endpoint URL
|
||||||
Map<String, Object> result = new Map<String, Object>();
|
request.setEndpoint('https://salesforce.tech4biz.io/generate-pdf');
|
||||||
result.put('success', true);
|
request.setMethod('POST');
|
||||||
result.put('pdf_id', pdfId);
|
request.setHeader('Content-Type', 'application/json');
|
||||||
result.put('status', 'compressed_download_ready');
|
request.setTimeout(120000); // 2 minutes timeout
|
||||||
result.put('message', 'Compressed PDF generation request received. Download link will be available shortly.');
|
|
||||||
|
|
||||||
// Create a temporary download URL
|
// Prepare the request body
|
||||||
result.put('download_url', 'https://salesforce.tech4biz.io/download-pdf/' + pdfId);
|
Map<String, Object> requestBody = new Map<String, Object>();
|
||||||
|
requestBody.put('input', htmlContent);
|
||||||
|
requestBody.put('return_download_link', true);
|
||||||
|
if (String.isNotBlank(pageSize)) {
|
||||||
|
requestBody.put('page_size', pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
result.put('page_size', pageSize != null ? pageSize : 'A4');
|
String jsonBody = JSON.serialize(requestBody);
|
||||||
result.put('generated_at', Datetime.now().format('yyyy-MM-dd HH:mm:ss'));
|
request.setBody(jsonBody);
|
||||||
result.put('compression_applied', true);
|
|
||||||
result.put('compression_level', 'aggressive');
|
System.debug('Request body: ' + jsonBody);
|
||||||
result.put('file_size_mb', 'TBD');
|
|
||||||
result.put('expires_at', Datetime.now().addDays(7).format('yyyy-MM-dd HH:mm:ss'));
|
// Make the HTTP call
|
||||||
result.put('pdf_stored', true);
|
HttpResponse response = http.send(request);
|
||||||
result.put('temp_folder_path', '/temp/pdfs/' + pdfId);
|
|
||||||
result.put('backend_status', 'TEMPORARY_FIX_ACTIVE');
|
System.debug('Response status: ' + response.getStatusCode());
|
||||||
result.put('note', 'This is a temporary fix to eliminate 6MB limit. Python server integration pending.');
|
System.debug('Response body: ' + response.getBody());
|
||||||
|
|
||||||
|
if (response.getStatusCode() == 200) {
|
||||||
|
// Parse the response
|
||||||
|
Map<String, Object> result = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
|
||||||
|
|
||||||
|
// Add compression-specific metadata
|
||||||
|
result.put('compression_applied', true);
|
||||||
|
result.put('compression_level', 'aggressive');
|
||||||
|
result.put('status', 'compressed_download_ready');
|
||||||
|
result.put('salesforce_generated_at', Datetime.now().format('yyyy-MM-dd HH:mm:ss'));
|
||||||
|
result.put('api_version', '7.0.0');
|
||||||
|
result.put('backend_status', 'NODE_JS_API_ACTIVE');
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
// Handle error response
|
||||||
|
Map<String, Object> errorResult = new Map<String, Object>();
|
||||||
|
errorResult.put('success', false);
|
||||||
|
errorResult.put('error', 'Compressed PDF generation failed: HTTP ' + response.getStatusCode());
|
||||||
|
errorResult.put('message', response.getBody());
|
||||||
|
errorResult.put('pdf_id', 'ERROR_' + DateTime.now().getTime());
|
||||||
|
|
||||||
|
return errorResult;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.debug('Exception in generateCompressedPDF: ' + e.getMessage());
|
System.debug('Exception in generateCompressedPDF: ' + e.getMessage());
|
||||||
|
System.debug('Exception stack trace: ' + e.getStackTraceString());
|
||||||
|
|
||||||
Map<String, Object> errorResult = new Map<String, Object>();
|
Map<String, Object> errorResult = new Map<String, Object>();
|
||||||
errorResult.put('success', false);
|
errorResult.put('success', false);
|
||||||
|
|||||||
@ -8790,3 +8790,239 @@ ol li:before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* PDF Download Modal Styles */
|
||||||
|
.pdf-download-modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 30px;
|
||||||
|
max-width: 600px;
|
||||||
|
width: 90%;
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
||||||
|
position: relative;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .modal-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .modal-header h2 {
|
||||||
|
color: white;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .close-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 28px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: white;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
padding: 0;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .close-btn:hover {
|
||||||
|
color: #ff6b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .download-info {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 20px 0;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .download-info h3 {
|
||||||
|
color: #4CAF50;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .file-details {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .file-detail {
|
||||||
|
text-align: center;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .file-detail .label {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .file-detail .value {
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .file-detail:nth-child(2) .value {
|
||||||
|
color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .file-detail:nth-child(4) .value {
|
||||||
|
color: #ffc107;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .download-link-section {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .download-link-section label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .download-link {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
color: #333;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
word-break: break-all;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
border: 2px solid #4CAF50;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .copy-btn {
|
||||||
|
background: #ffc107;
|
||||||
|
color: #333;
|
||||||
|
border: none;
|
||||||
|
padding: 8px 15px;
|
||||||
|
border-radius: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-right: 10px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .copy-btn:hover {
|
||||||
|
background: #ffb300;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .copy-btn.copied {
|
||||||
|
background: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .modal-actions {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 25px;
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .download-btn {
|
||||||
|
background: linear-gradient(45deg, #4CAF50, #45a049);
|
||||||
|
color: white;
|
||||||
|
padding: 12px 25px;
|
||||||
|
border-radius: 25px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
display: inline-block;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 15px rgba(76, 175, 80, 0.3);
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .download-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(76, 175, 80, 0.4);
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .open-tab-btn {
|
||||||
|
background: linear-gradient(45deg, #2196F3, #1976D2);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 25px;
|
||||||
|
border-radius: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 15px rgba(33, 150, 243, 0.3);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .open-tab-btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 20px rgba(33, 150, 243, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.pdf-download-modal {
|
||||||
|
margin: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .file-details {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .modal-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-download-modal .download-btn,
|
||||||
|
.pdf-download-modal .open-tab-btn {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -132,6 +132,8 @@ export default class PropertyTemplateSelector extends LightningElement {
|
|||||||
@track isDraggingTable = false;
|
@track isDraggingTable = false;
|
||||||
@track draggedTableData = null;
|
@track draggedTableData = null;
|
||||||
@track selectorMode = false;
|
@track selectorMode = false;
|
||||||
|
@track showDownloadModal = false;
|
||||||
|
@track downloadInfo = {};
|
||||||
@track selectedElement = null;
|
@track selectedElement = null;
|
||||||
|
|
||||||
// Undo functionality
|
// Undo functionality
|
||||||
@ -1560,87 +1562,71 @@ export default class PropertyTemplateSelector extends LightningElement {
|
|||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.hideProgress();
|
this.hideProgress();
|
||||||
|
|
||||||
// Create download message based on compression level
|
// Set the download info for the modal
|
||||||
let message = "";
|
this.downloadInfo = {
|
||||||
|
filename: pdfResult.filename || "Unknown",
|
||||||
|
fileSize: pdfResult.file_size_mb ? pdfResult.file_size_mb + " MB" : "Unknown",
|
||||||
|
generatedAt: this.formatDate(pdfResult.generated_at),
|
||||||
|
expiresAt: this.formatDate(pdfResult.expires_at),
|
||||||
|
downloadUrl: pdfResult.download_url
|
||||||
|
};
|
||||||
|
// Automatically open download URL in new tab
|
||||||
|
console.log("Opening download URL:", pdfResult.download_url);
|
||||||
|
window.open(pdfResult.download_url, "_blank");
|
||||||
|
|
||||||
if (pdfResult.status === "compressed_download_ready") {
|
// Show simple success message
|
||||||
message = `
|
this.showSuccess(`✅ PDF generated successfully! File: ${pdfResult.filename} (${pdfResult.file_size_mb} MB) - Download opened in new tab.`);
|
||||||
<div class="pdf-download-ready">
|
this.showSuccess(`✅ PDF generated successfully! File: ${pdfResult.filename} (${pdfResult.file_size_mb} MB)`);
|
||||||
<h3>📦 Compressed PDF Ready!</h3>
|
|
||||||
<p><strong>PDF ID:</strong> ${pdfResult.pdf_id}</p>
|
|
||||||
<p><strong>Page Size:</strong> ${pdfResult.page_size}</p>
|
|
||||||
<p><strong>Generated:</strong> ${pdfResult.generated_at}</p>
|
|
||||||
<p><em>This is a compressed version optimized for smaller file size.</em></p>
|
|
||||||
|
|
||||||
<div class="download-options" style="margin: 20px 0;">
|
|
||||||
<button class="slds-button slds-button_brand" onclick="window.open('${pdfResult.download_url}', '_blank')">
|
|
||||||
📦 Download Compressed PDF
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-box" style="background: #e8f4fd; padding: 15px; border-radius: 8px; margin-top: 15px; border-left: 4px solid #0176d3;">
|
|
||||||
<h4>💡 About Compressed PDFs</h4>
|
|
||||||
<ul style="margin: 10px 0; padding-left: 20px;">
|
|
||||||
<li>Images are optimized for smaller file size</li>
|
|
||||||
<li>All content is preserved but with reduced quality</li>
|
|
||||||
<li>Perfect for email sharing and quick downloads</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
} else {
|
|
||||||
message = `
|
|
||||||
<div class="pdf-download-ready">
|
|
||||||
<h3>📄 PDF Ready for Download!</h3>
|
|
||||||
<p><strong>PDF ID:</strong> ${pdfResult.pdf_id}</p>
|
|
||||||
<p><strong>Page Size:</strong> ${pdfResult.page_size}</p>
|
|
||||||
<p><strong>Generated:</strong> ${pdfResult.generated_at}</p>
|
|
||||||
<p><em>Your PDF has been generated successfully and is ready for download.</em></p>
|
|
||||||
|
|
||||||
<div class="download-options" style="margin: 20px 0;">
|
|
||||||
<button class="slds-button slds-button_brand" onclick="window.open('${pdfResult.download_url}', '_blank')">
|
|
||||||
📄 Download PDF
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="info-box" style="background: #f0f8f0; padding: 15px; border-radius: 8px; margin-top: 15px; border-left: 4px solid #28a745;">
|
|
||||||
<h4>✅ Download Instructions</h4>
|
|
||||||
<ul style="margin: 10px 0; padding-left: 20px;">
|
|
||||||
<li>Click the download button above</li>
|
|
||||||
<li>Your PDF will open in a new tab</li>
|
|
||||||
<li>Use your browser's save function to download</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.showSuccess(message);
|
|
||||||
|
|
||||||
// Also provide a direct download link as fallback
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.template.querySelector('.success-message')) {
|
|
||||||
const successMsg = this.template.querySelector('.success-message');
|
|
||||||
|
|
||||||
// Add a fallback download link
|
|
||||||
const fallbackLink = document.createElement('a');
|
|
||||||
fallbackLink.href = pdfResult.download_url;
|
|
||||||
fallbackLink.textContent = '🔗 Direct Download Link';
|
|
||||||
fallbackLink.className = 'slds-button slds-button_neutral';
|
|
||||||
fallbackLink.style.marginTop = '10px';
|
|
||||||
fallbackLink.style.display = 'inline-block';
|
|
||||||
fallbackLink.target = '_blank';
|
|
||||||
|
|
||||||
successMsg.appendChild(fallbackLink);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error handling PDF download ready:', error);
|
console.error("Error handling PDF download ready:", error);
|
||||||
this.showError('Error handling PDF download: ' + error.message);
|
this.showError("Error handling PDF download: " + error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modal control methods
|
||||||
|
closeDownloadModal() {
|
||||||
|
this.showDownloadModal = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopPropagation(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
copyDownloadLink() {
|
||||||
|
if (navigator.clipboard && this.downloadInfo) {
|
||||||
|
navigator.clipboard.writeText(this.downloadInfo.downloadUrl).then(() => {
|
||||||
|
// Show feedback
|
||||||
|
const copyBtn = this.template.querySelector(".copy-btn");
|
||||||
|
if (copyBtn) {
|
||||||
|
const originalText = copyBtn.textContent;
|
||||||
|
copyBtn.textContent = "✅ Copied!";
|
||||||
|
copyBtn.classList.add("copied");
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
copyBtn.textContent = originalText;
|
||||||
|
copyBtn.classList.remove("copied");
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error("Failed to copy: ", err);
|
||||||
|
alert("Failed to copy link to clipboard");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openInNewTab() {
|
||||||
|
if (this.downloadInfo && this.downloadInfo.downloadUrl) {
|
||||||
|
window.open(this.downloadInfo.downloadUrl, "_blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to format dates
|
||||||
|
formatDate(dateString) {
|
||||||
|
if (!dateString) return "Unknown";
|
||||||
|
const date = new Date(dateString);
|
||||||
|
return date.toLocaleString();
|
||||||
|
}
|
||||||
// Create template HTML based on selection
|
// Create template HTML based on selection
|
||||||
createTemplateHTML() {
|
createTemplateHTML() {
|
||||||
console.log('=== CREATE TEMPLATE HTML DEBUG ===');
|
console.log('=== CREATE TEMPLATE HTML DEBUG ===');
|
||||||
|
|||||||
@ -1551,6 +1551,96 @@ export default class PropertyTemplateSelector extends LightningElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle PDF download ready response
|
||||||
|
async handlePDFDownloadReady(pdfResult) {
|
||||||
|
try {
|
||||||
|
console.log("Handling PDF download ready:", pdfResult);
|
||||||
|
|
||||||
|
// Hide loading state
|
||||||
|
this.isLoading = false;
|
||||||
|
this.hideProgress();
|
||||||
|
|
||||||
|
// Create download message based on compression level
|
||||||
|
let message = "";
|
||||||
|
|
||||||
|
if (pdfResult.status === "compressed_download_ready") {
|
||||||
|
message = `
|
||||||
|
<div class="pdf-download-ready">
|
||||||
|
<h3>📦 Compressed PDF Ready!</h3>
|
||||||
|
<p><strong>PDF ID:</strong> ${pdfResult.pdf_id}</p>
|
||||||
|
<p><strong>Page Size:</strong> ${pdfResult.page_size}</p>
|
||||||
|
<p><strong>Generated:</strong> ${pdfResult.generated_at}</p>
|
||||||
|
<p><em>This is a compressed version optimized for smaller file size.</em></p>
|
||||||
|
|
||||||
|
<div class="download-options" style="margin: 20px 0;">
|
||||||
|
<button class="slds-button slds-button_brand" onclick="window.open('${pdfResult.download_url}', '_blank')">
|
||||||
|
📦 Download Compressed PDF
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-box" style="background: #e8f4fd; padding: 15px; border-radius: 8px; margin-top: 15px; border-left: 4px solid #0176d3;">
|
||||||
|
<h4>💡 About Compressed PDFs</h4>
|
||||||
|
<ul style="margin: 10px 0; padding-left: 20px;">
|
||||||
|
<li>Images are optimized for smaller file size</li>
|
||||||
|
<li>All content is preserved but with reduced quality</li>
|
||||||
|
<li>Perfect for email sharing and quick downloads</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
message = `
|
||||||
|
<div class="pdf-download-ready">
|
||||||
|
<h3>📄 PDF Ready for Download!</h3>
|
||||||
|
<p><strong>PDF ID:</strong> ${pdfResult.pdf_id}</p>
|
||||||
|
<p><strong>Page Size:</strong> ${pdfResult.page_size}</p>
|
||||||
|
<p><strong>Generated:</strong> ${pdfResult.generated_at}</p>
|
||||||
|
<p><em>Your PDF has been generated successfully and is ready for download.</em></p>
|
||||||
|
|
||||||
|
<div class="download-options" style="margin: 20px 0;">
|
||||||
|
<button class="slds-button slds-button_brand" onclick="window.open('${pdfResult.download_url}', '_blank')">
|
||||||
|
📄 Download PDF
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-box" style="background: #f0f8f0; padding: 15px; border-radius: 8px; margin-top: 15px; border-left: 4px solid #28a745;">
|
||||||
|
<h4>✅ Download Instructions</h4>
|
||||||
|
<ul style="margin: 10px 0; padding-left: 20px;">
|
||||||
|
<li>Click the download button above</li>
|
||||||
|
<li>Your PDF will open in a new tab</li>
|
||||||
|
<li>Use your browser's save function to download</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showSuccess(message);
|
||||||
|
|
||||||
|
// Also provide a direct download link as fallback
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.template.querySelector('.success-message')) {
|
||||||
|
const successMsg = this.template.querySelector('.success-message');
|
||||||
|
|
||||||
|
// Add a fallback download link
|
||||||
|
const fallbackLink = document.createElement('a');
|
||||||
|
fallbackLink.href = pdfResult.download_url;
|
||||||
|
fallbackLink.textContent = '🔗 Direct Download Link';
|
||||||
|
fallbackLink.className = 'slds-button slds-button_neutral';
|
||||||
|
fallbackLink.style.marginTop = '10px';
|
||||||
|
fallbackLink.style.display = 'inline-block';
|
||||||
|
fallbackLink.target = '_blank';
|
||||||
|
|
||||||
|
successMsg.appendChild(fallbackLink);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error handling PDF download ready:', error);
|
||||||
|
this.showError('Error handling PDF download: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create template HTML based on selection
|
// Create template HTML based on selection
|
||||||
createTemplateHTML() {
|
createTemplateHTML() {
|
||||||
console.log('=== CREATE TEMPLATE HTML DEBUG ===');
|
console.log('=== CREATE TEMPLATE HTML DEBUG ===');
|
||||||
|
|||||||
22
replace_method.py
Normal file
22
replace_method.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Read the original file
|
||||||
|
with open('force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js', 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
# Read the new method
|
||||||
|
with open('update_popup_method.js', 'r') as f:
|
||||||
|
new_method = f.read()
|
||||||
|
|
||||||
|
# Find the start and end of the handlePDFDownloadReady method
|
||||||
|
start_line = 1555 - 1 # Convert to 0-based index
|
||||||
|
end_line = 1643 - 1 # Convert to 0-based index
|
||||||
|
|
||||||
|
# Replace the method
|
||||||
|
new_lines = lines[:start_line] + [new_method + '\n\n'] + lines[end_line:]
|
||||||
|
|
||||||
|
# Write the updated file
|
||||||
|
with open('force-app/main/default/lwc/propertyTemplateSelector/propertyTemplateSelector.js', 'w') as f:
|
||||||
|
f.writelines(new_lines)
|
||||||
|
|
||||||
|
print("✅ Successfully updated the handlePDFDownloadReady method with popup functionality!")
|
||||||
182
update_popup_method.js
Normal file
182
update_popup_method.js
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
// Updated handlePDFDownloadReady method with popup functionality
|
||||||
|
async handlePDFDownloadReady(pdfResult) {
|
||||||
|
try {
|
||||||
|
console.log("Handling PDF download ready:", pdfResult);
|
||||||
|
|
||||||
|
// Hide loading state
|
||||||
|
this.isLoading = false;
|
||||||
|
this.hideProgress();
|
||||||
|
|
||||||
|
// Show the beautiful popup modal
|
||||||
|
this.showDownloadPopup(pdfResult);
|
||||||
|
|
||||||
|
// Also show a success message
|
||||||
|
this.showSuccess('✅ PDF generated successfully! Click the popup to download.');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error handling PDF download ready:', error);
|
||||||
|
this.showError('Error handling PDF download: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New method to show download popup
|
||||||
|
showDownloadPopup(pdfResult) {
|
||||||
|
// Create modal HTML
|
||||||
|
const modalHTML = `
|
||||||
|
<div class="pdf-download-modal" style="
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
">
|
||||||
|
<div class="modal-content" style="
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 30px;
|
||||||
|
max-width: 600px;
|
||||||
|
width: 90%;
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
||||||
|
position: relative;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
">
|
||||||
|
<button class="close-btn" onclick="this.closest('.pdf-download-modal').remove()" style="
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
right: 20px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 28px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: white;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
" onmouseover="this.style.color='#ff6b6b'" onmouseout="this.style.color='white'">×</button>
|
||||||
|
|
||||||
|
<div class="modal-header" style="text-align: center; margin-bottom: 25px;">
|
||||||
|
<h2 style="color: white; margin-bottom: 10px; font-size: 2rem;">🎉 PDF Ready for Download!</h2>
|
||||||
|
<p style="color: rgba(255, 255, 255, 0.9);">Your PDF has been generated successfully</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="download-info" style="
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 20px 0;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
">
|
||||||
|
<h3 style="color: #4CAF50; margin-bottom: 15px;">📄 Download Information</h3>
|
||||||
|
|
||||||
|
<div class="file-details" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 15px; margin: 15px 0;">
|
||||||
|
<div class="file-detail" style="text-align: center;">
|
||||||
|
<div class="label" style="font-size: 0.9rem; color: rgba(255, 255, 255, 0.8); margin-bottom: 5px;">File Name</div>
|
||||||
|
<div class="value" style="font-weight: bold; color: white;">${pdfResult.filename || 'Unknown'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="file-detail" style="text-align: center;">
|
||||||
|
<div class="label" style="font-size: 0.9rem; color: rgba(255, 255, 255, 0.8); margin-bottom: 5px;">File Size</div>
|
||||||
|
<div class="value" style="font-weight: bold; color: #4CAF50;">${pdfResult.file_size_mb ? pdfResult.file_size_mb + ' MB' : 'Unknown'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="file-detail" style="text-align: center;">
|
||||||
|
<div class="label" style="font-size: 0.9rem; color: rgba(255, 255, 255, 0.8); margin-bottom: 5px;">Generated</div>
|
||||||
|
<div class="value" style="font-weight: bold; color: white;">${this.formatDate(pdfResult.generated_at)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="file-detail" style="text-align: center;">
|
||||||
|
<div class="label" style="font-size: 0.9rem; color: rgba(255, 255, 255, 0.8); margin-bottom: 5px;">Expires</div>
|
||||||
|
<div class="value" style="font-weight: bold; color: #ffc107;">${this.formatDate(pdfResult.expires_at)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin: 20px 0;">
|
||||||
|
<label style="display: block; margin-bottom: 8px; font-weight: bold; color: white;">🔗 Download Link:</label>
|
||||||
|
<div class="download-link" style="
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
color: #333;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
word-break: break-all;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
border: 2px solid #4CAF50;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
">${pdfResult.download_url}</div>
|
||||||
|
<button class="copy-btn" onclick="navigator.clipboard.writeText('${pdfResult.download_url}').then(() => { this.textContent = '✅ Copied!'; this.style.background = '#4CAF50'; setTimeout(() => { this.textContent = '📋 Copy Link'; this.style.background = '#ffc107'; }, 2000); })" style="
|
||||||
|
background: #ffc107;
|
||||||
|
color: #333;
|
||||||
|
border: none;
|
||||||
|
padding: 8px 15px;
|
||||||
|
border-radius: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-right: 10px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
">📋 Copy Link</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button-group" style="text-align: center; margin-top: 25px;">
|
||||||
|
<a href="${pdfResult.download_url}" target="_blank" class="download-btn" style="
|
||||||
|
background: linear-gradient(45deg, #4CAF50, #45a049);
|
||||||
|
color: white;
|
||||||
|
padding: 12px 25px;
|
||||||
|
border-radius: 25px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 15px;
|
||||||
|
display: inline-block;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 15px rgba(76, 175, 80, 0.3);
|
||||||
|
" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(76, 175, 80, 0.4)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 4px 15px rgba(76, 175, 80, 0.3)'">📥 Download PDF</a>
|
||||||
|
<button onclick="window.open('${pdfResult.download_url}', '_blank')" style="
|
||||||
|
background: linear-gradient(45deg, #2196F3, #1976D2);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 25px;
|
||||||
|
border-radius: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 15px rgba(33, 150, 243, 0.3);
|
||||||
|
" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 20px rgba(33, 150, 243, 0.4)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 4px 15px rgba(33, 150, 243, 0.3)'">🔗 Open in New Tab</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Add modal to page
|
||||||
|
document.body.insertAdjacentHTML('beforeend', modalHTML);
|
||||||
|
|
||||||
|
// Close modal when clicking outside
|
||||||
|
const modal = document.querySelector('.pdf-download-modal');
|
||||||
|
if (modal) {
|
||||||
|
modal.addEventListener('click', function(event) {
|
||||||
|
if (event.target === modal) {
|
||||||
|
modal.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close modal with Escape key
|
||||||
|
const escapeHandler = function(event) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
const modal = document.querySelector('.pdf-download-modal');
|
||||||
|
if (modal) {
|
||||||
|
modal.remove();
|
||||||
|
document.removeEventListener('keydown', escapeHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('keydown', escapeHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method to format dates
|
||||||
|
formatDate(dateString) {
|
||||||
|
if (!dateString) return 'Unknown';
|
||||||
|
const date = new Date(dateString);
|
||||||
|
return date.toLocaleString();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user