new-project / static /index.html
amit01Xindus's picture
Upload 8 files
96c003e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PDF Converter Tool</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
padding: 40px;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
max-width: 600px;
width: 100%;
animation: slideIn 0.6s ease-out;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.header {
text-align: center;
margin-bottom: 40px;
}
.header h1 {
color: #333;
font-size: 2.5em;
margin-bottom: 10px;
background: linear-gradient(45deg, #667eea, #764ba2);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.header p {
color: #666;
font-size: 1.1em;
}
.status-indicator {
position: absolute;
top: 20px;
right: 20px;
padding: 8px 16px;
border-radius: 20px;
font-size: 0.8em;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.status-online {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-offline {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.conversion-options {
display: grid;
gap: 20px;
margin-bottom: 30px;
}
.option-card {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border: none;
border-radius: 15px;
padding: 25px;
cursor: pointer;
transition: all 0.3s ease;
color: white;
text-align: left;
position: relative;
overflow: hidden;
}
.option-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2);
}
.option-card.html {
background: linear-gradient(135deg, #fa709a 0%, #fee140 100%);
}
.option-card.word {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.option-card.json {
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
/* New style for Excel option card */
.option-card.excel {
background: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%); /* Green shades for Excel */
}
.option-card::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.2),
transparent
);
transition: left 0.5s;
}
.option-card:hover::before {
left: 100%;
}
.option-icon {
font-size: 2em;
margin-bottom: 10px;
}
.option-title {
font-size: 1.3em;
font-weight: bold;
margin-bottom: 5px;
}
.option-desc {
font-size: 0.9em;
opacity: 0.9;
}
.upload-section {
display: none;
background: #f8f9fa;
border-radius: 15px;
padding: 30px;
margin-top: 20px;
border: 2px dashed #ddd;
transition: all 0.3s ease;
}
.upload-section.active {
display: block;
animation: fadeIn 0.5s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.file-input-wrapper {
position: relative;
display: inline-block;
width: 100%;
margin-bottom: 20px;
}
.file-input {
display: none;
}
.file-input-label {
display: block;
padding: 15px 25px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 10px;
cursor: pointer;
text-align: center;
transition: all 0.3s ease;
font-weight: 500;
}
.file-input-label:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
}
.file-name {
margin-top: 10px;
padding: 10px;
background: #e9ecef;
border-radius: 8px;
font-size: 0.9em;
color: #495057;
display: none;
}
.output-name {
width: 100%;
padding: 15px;
border: 2px solid #e9ecef;
border-radius: 10px;
font-size: 1em;
margin-bottom: 20px;
transition: border-color 0.3s ease;
}
.output-name:focus {
outline: none;
border-color: #667eea;
}
.convert-btn {
width: 100%;
padding: 15px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 10px;
font-size: 1.1em;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.convert-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
}
.convert-btn:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
.back-btn {
background: #6c757d;
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
margin-bottom: 20px;
transition: all 0.3s ease;
}
.back-btn:hover {
background: #5a6268;
transform: translateY(-1px);
}
.progress-bar {
width: 100%;
height: 6px;
background: #e9ecef;
border-radius: 3px;
margin: 20px 0;
overflow: hidden;
display: none;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
width: 0%;
transition: width 0.3s ease;
border-radius: 3px;
}
.result-section {
margin-top: 20px;
padding: 20px;
border-radius: 12px;
display: none;
}
.result-success {
background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%);
border: 1px solid #c3e6cb;
color: #155724;
}
.result-error {
background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
border: 1px solid #f5c6cb;
color: #721c24;
}
.loading {
display: none;
text-align: center;
margin: 20px 0;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.drag-over {
border-color: #667eea !important;
background: rgba(102, 126, 234, 0.1) !important;
}
.debug-info {
margin-top: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
font-size: 0.9em;
color: #6c757d;
border-left: 4px solid #007bff;
}
.error-details {
margin-top: 10px;
padding: 10px;
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 6px;
font-size: 0.85em;
color: #856404;
max-height: 200px;
overflow-y: auto;
}
@media (max-width: 768px) {
.container {
padding: 25px;
margin: 10px;
}
.header h1 {
font-size: 2em;
}
.option-card {
padding: 20px;
}
.status-indicator {
position: relative;
top: auto;
right: auto;
margin-bottom: 20px;
display: inline-block;
}
}
</style>
</head>
<body>
<div class="container">
<div id="status-indicator" class="status-indicator status-offline">
Server Offline
</div>
<div class="header">
<h1>🧠 PDF Converter</h1>
<p>Convert your PDF files to HTML, Word, JSON, or Excel format</p>
</div>
<div id="main-menu">
<div class="conversion-options">
<button class="option-card html" onclick="showUploadSection('html')">
<div class="option-icon">🌐</div>
<div class="option-title">Convert to HTML</div>
<div class="option-desc">
Transform PDF into web-ready HTML format
</div>
</button>
<button class="option-card word" onclick="showUploadSection('word')">
<div class="option-icon">πŸ“„</div>
<div class="option-title">Convert to Word</div>
<div class="option-desc">
Create editable Word documents from PDF
</div>
</button>
<button class="option-card json" onclick="showUploadSection('json')">
<div class="option-icon">πŸ“Š</div>
<div class="option-title">Convert to JSON</div>
<div class="option-desc">
Extract structured data in JSON format
</div>
</button>
<button class="option-card excel" onclick="showUploadSection('excel')">
<div class="option-icon">πŸ“ˆ</div>
<div class="option-title">Convert to Excel</div>
<div class="option-desc">
Organize PDF tables into an Excel spreadsheet
</div>
</button>
</div>
</div>
<div id="upload-section" class="upload-section">
<button class="back-btn" onclick="showMainMenu()">
← Back to Menu
</button>
<div class="file-input-wrapper">
<input
type="file"
id="pdf-file"
class="file-input"
accept=".pdf"
onchange="handleFileSelect(event)"
/>
<label for="pdf-file" class="file-input-label" id="file-label">
πŸ“„ Choose PDF File or Drag & Drop Here
</label>
<div id="file-name" class="file-name"></div>
</div>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>Converting your PDF file...</p>
</div>
<div class="progress-bar" id="progress-bar">
<div class="progress-fill" id="progress-fill"></div>
</div>
<button
class="convert-btn"
id="convert-btn"
onclick="convertFile()"
disabled
>
πŸš€ Start Conversion
</button>
<div id="result-section" class="result-section">
<div id="result-message"></div>
</div>
<div id="debug-info" class="debug-info" style="display: none">
<strong>Debug Information:</strong>
<div id="debug-content"></div>
</div>
</div>
</div>
<script>
let currentFormat = "";
let selectedFile = null;
let serverOnline = false;
// Check server status on page load
document.addEventListener("DOMContentLoaded", function () {
checkServerStatus();
// Check server status every 30 seconds
setInterval(checkServerStatus, 30000);
});
async function checkServerStatus() {
try {
const response = await fetch("/health", {
method: "GET",
mode: "cors",
headers: {
Accept: "application/json",
},
signal: AbortSignal.timeout(5000), // 5 second timeout
});
if (response.ok) {
const data = await response.json();
updateServerStatus(true, data.message || "Server is online");
} else {
updateServerStatus(false, `Server returned ${response.status}`);
}
} catch (error) {
updateServerStatus(false, error.message);
}
}
function updateServerStatus(online, message) {
serverOnline = online;
const indicator = document.getElementById("status-indicator");
if (online) {
indicator.className = "status-indicator status-online";
indicator.textContent = "Server Online";
indicator.title = message;
} else {
indicator.className = "status-indicator status-offline";
indicator.textContent = "Server Offline";
indicator.title = `Error: ${message}`;
}
}
function showUploadSection(format) {
if (!serverOnline) {
alert("Server is offline. Please start the Flask server first.");
return;
}
currentFormat = format;
document.getElementById("main-menu").style.display = "none";
document.getElementById("upload-section").classList.add("active");
resetForm(); // βœ… Always reset when entering upload
const outputInput = document.getElementById("output-name");
const extensions = { html: ".html", word: ".docx", json: ".json", excel: ".xlsx" };
outputInput.placeholder = `Enter output filename (e.g., converted_file${extensions[format]})`;
}
function showMainMenu() {
window.location.reload();
document.getElementById("main-menu").style.display = "block";
document.getElementById("upload-section").classList.remove("active");
resetForm();
selectedFile = null;
}
function resetForm() {
selectedFile = null;
const pdfInput = document.getElementById("pdf-file");
const outputInput = document.getElementById("output-name");
const fileName = document.getElementById("file-name");
const fileLabel = document.getElementById("file-label");
// Clear inputs
pdfInput.value = "";
// Hide filename display
fileName.style.display = "none";
fileName.textContent = "";
// Reset label text
fileLabel.textContent = "πŸ“„ Choose PDF File or Drag & Drop Here";
// Reset buttons and sections
document.getElementById("convert-btn").disabled = true;
document.getElementById("result-section").style.display = "none";
document.getElementById("loading").style.display = "none";
document.getElementById("progress-bar").style.display = "none";
document.getElementById("debug-info").style.display = "none";
// Also reset drag-over styling if stuck
document.getElementById("upload-section").classList.remove("drag-over");
}
function handleFileSelect(event) {
const file = event.target.files[0];
if (file && file.type === "application/pdf") {
selectedFile = file;
document.getElementById("file-name").textContent = `Selected: ${
file.name
} (${(file.size / 1024 / 1024).toFixed(2)} MB)`;
document.getElementById("file-name").style.display = "block";
document.getElementById(
"file-label"
).textContent = `βœ… ${file.name} selected`;
checkFormValidity();
} else {
alert("Please select a valid PDF file.");
resetFileInput();
}
}
function resetFileInput() {
selectedFile = null;
document.getElementById("pdf-file").value = "";
document.getElementById("file-name").style.display = "none";
document.getElementById("file-label").textContent =
"πŸ“„ Choose PDF File or Drag & Drop Here";
checkFormValidity();
}
function checkFormValidity() {
const outputName = document.getElementById("output-name").value.trim();
const convertBtn = document.getElementById("convert-btn");
if (selectedFile && outputName && serverOnline) {
convertBtn.disabled = false;
convertBtn.textContent = "πŸš€ Start Conversion";
} else {
convertBtn.disabled = true;
convertBtn.textContent = serverOnline
? "πŸš€ Start Conversion"
: "❌ Server Offline";
}
}
// Add event listener for output name input
document
.getElementById("output-name")
.addEventListener("input", checkFormValidity);
// Drag and drop functionality
const uploadSection = document.getElementById("upload-section");
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
uploadSection.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
["dragenter", "dragover"].forEach((eventName) => {
uploadSection.addEventListener(eventName, highlight, false);
});
["dragleave", "drop"].forEach((eventName) => {
uploadSection.addEventListener(eventName, unhighlight, false);
});
function highlight() {
uploadSection.classList.add("drag-over");
}
function unhighlight() {
uploadSection.classList.remove("drag-over");
}
uploadSection.addEventListener("drop", handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
if (files.length > 0) {
const file = files[0];
if (file.type === "application/pdf") {
selectedFile = file;
document.getElementById("file-name").textContent = `Selected: ${
file.name
} (${(file.size / 1024 / 1024).toFixed(2)} MB)`;
document.getElementById("file-name").style.display = "block";
document.getElementById(
"file-label"
).textContent = `βœ… ${file.name} selected`;
checkFormValidity();
} else {
alert("Please drop a valid PDF file.");
}
}
}
function checkFormValidity() {
const convertBtn = document.getElementById("convert-btn");
if (selectedFile && serverOnline) {
convertBtn.disabled = false;
convertBtn.textContent = "πŸš€ Start Conversion";
} else {
convertBtn.disabled = true;
convertBtn.textContent = serverOnline
? "πŸš€ Start Conversion"
: "❌ Server Offline";
}
}
async function convertFile() {
if (!selectedFile || !currentFormat) {
alert("Please select a file and format.");
return;
}
if (!serverOnline) {
alert("Server is offline. Please start the Flask server first.");
return;
}
const outputName = selectedFile.name.replace(/\.[^/.]+$/, "");
document.getElementById("loading").style.display = "block";
document.getElementById("progress-bar").style.display = "block";
document.getElementById("convert-btn").disabled = true;
document.getElementById("result-section").style.display = "none";
document.getElementById("debug-info").style.display = "none";
simulateProgress();
const formData = new FormData();
formData.append("file", selectedFile);
formData.append("format", currentFormat);
formData.append("output_name", outputName);
const debugInfo = {
fileName: selectedFile.name,
fileSize: selectedFile.size,
format: currentFormat,
outputName: outputName,
timestamp: new Date().toISOString(),
};
try {
console.log("πŸ”„ Starting conversion...", debugInfo);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 420000); // 60 second timeout
const response = await fetch("/convert", {
method: "POST",
body: formData,
headers: {
Accept: "application/json",
},
mode: "cors",
signal: controller.signal,
});
clearTimeout(timeoutId);
console.log("πŸ“‘ Response status:", response.status);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Server returned ${response.status}: ${errorText}`);
}
const result = await response.json();
console.log("βœ… Conversion result:", result);
// Hide loading
document.getElementById("loading").style.display = "none";
document.getElementById("progress-bar").style.display = "none";
// Show result
const resultSection = document.getElementById("result-section");
const resultMessage = document.getElementById("result-message");
if (result.success) {
resultSection.className = "result-section result-success";
resultMessage.innerHTML = `<h3>βœ… Conversion Successful!</h3>
<p>Your PDF has been converted to ${currentFormat.toUpperCase()} format.</p>
<p><strong>Output file:</strong> ${
result.output_path || "Generated successfully"
}</p>`;
if (result.download_url) {
const downloadUrl = `${window.location.origin}${result.download_url}`;
// Add link for user
resultMessage.innerHTML += `<p><a href="${downloadUrl}" target="_blank" style="color: #155724; text-decoration: none; font-weight: bold;">πŸ“₯ Download File</a></p>`;
// ⬇️ Auto-download
const a = document.createElement("a");
a.href = downloadUrl;
a.download = result.output_path || "converted_file";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
} else {
resultSection.className = "result-section result-error";
resultMessage.innerHTML = `
<h3>❌ Conversion Failed</h3>
<p>${
result.error || "An unexpected error occurred."
}</p>
`;
}
resultSection.style.display = "block";
} catch (error) {
console.error("❌ Error during conversion:", error);
// Hide loading
document.getElementById("loading").style.display = "none";
document.getElementById("progress-bar").style.display = "none";
// Show error
const resultSection = document.getElementById("result-section");
const resultMessage = document.getElementById("result-message");
resultSection.className = "result-section result-error";
let errorMessage = "An unexpected error occurred.";
if (error.name === "AbortError") {
errorMessage =
"Request timed out. The file might be too large or the server is taking too long to respond.";
} else if (error.message.includes("Failed to fetch")) {
errorMessage =
"Cannot connect to server. Please ensure the Flask server is running on http://localhost:5000";
} else {
errorMessage = error.message;
}
resultMessage.innerHTML = `
<h3>❌ Conversion Error</h3>
<p>${errorMessage}</p>
`;
resultSection.style.display = "block";
// Show debug information
const debugElement = document.getElementById("debug-info");
const debugContent = document.getElementById("debug-content");
debugContent.innerHTML = `
<div class="error-details">
<strong>Error Details:</strong><br>
Type: ${error.name}<br>
Message: ${error.message}<br>
<br>
<strong>Request Details:</strong><br>
${JSON.stringify(debugInfo, null, 2)}
<br>
<strong>Troubleshooting:</strong><br>
1. Ensure Flask server is running: python app.py<br>
2. Check server logs for errors<br>
3. Verify file size is under 100MB<br>
4. Check browser console for additional errors
</div>
`;
debugElement.style.display = "block";
}
document.getElementById("convert-btn").disabled = false;
checkFormValidity(); // Update button state
}
function simulateProgress() {
const progressFill = document.getElementById("progress-fill");
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 15;
if (progress > 90) progress = 90;
progressFill.style.width = progress + "%";
if (progress >= 90) {
clearInterval(interval);
}
}, 200);
// Reset progress after animation
setTimeout(() => {
progressFill.style.width = "0%";
}, 5000);
}
</script>
</body>
</html>