Convert-it/Areas/ImageConverters/Views/HeicToJpg/Index.cshtml
Ricardo Carneiro 84b058904f
All checks were successful
Deploy ASP.NET MVC to OCI / build-and-deploy (push) Successful in 9m32s
fix: adiversos ajustes e mais 1 conversor
2025-09-14 21:26:16 -03:00

451 lines
20 KiB
Plaintext

@{
ViewData["Title"] = ViewBag.PageTitle;
}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="text-center mb-4">
<h1 class="display-5">@ViewBag.PageTitle</h1>
<p class="lead text-muted">@ViewBag.PageDescription</p>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-lg-10 col-xl-8">
<div class="card shadow-sm">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">
<i class="bi bi-file-earmark-image me-2"></i>Conversor de Imagens
</h5>
</div>
<div class="card-body">
@if (ViewBag.ConversionError != null)
{
<div class="alert alert-danger" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
@ViewBag.ConversionError
</div>
}
<!-- Navigation Tabs -->
<ul class="nav nav-tabs mb-4" id="converterTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="heic-to-jpg-tab" data-bs-toggle="tab" data-bs-target="#heic-to-jpg" type="button" role="tab" aria-controls="heic-to-jpg" aria-selected="true">
<i class="bi bi-arrow-right me-2"></i>@ViewBag.HeicToJpgTabTitle
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="jpg-to-heic-tab" data-bs-toggle="tab" data-bs-target="#jpg-to-heic" type="button" role="tab" aria-controls="jpg-to-heic" aria-selected="false">
<i class="bi bi-arrow-left me-2"></i>@ViewBag.JpgToHeicTabTitle
</button>
</li>
</ul>
<!-- Tab Content -->
<div class="tab-content" id="converterTabContent">
<!-- HEIC to JPG Tab -->
<div class="tab-pane fade show active" id="heic-to-jpg" role="tabpanel" aria-labelledby="heic-to-jpg-tab">
<form id="heicToJpgForm" asp-action="ConvertHeicToJpg" method="post" enctype="multipart/form-data" class="needs-validation">
<div asp-validation-summary="ModelOnly" class="alert alert-danger" role="alert"></div>
<div class="mb-4">
<label for="heicFile" class="form-label fw-semibold">@ViewBag.HeicFileInputLabel</label>
<input class="form-control" type="file" id="heicFile" name="heicFile" accept=".heic,.HEIC,.heif,.HEIF" required>
<div class="form-text">
<i class="bi bi-info-circle me-1"></i>Formatos aceitos: .heic, .HEIC, .heif, .HEIF (máx. 10MB para preview)
</div>
<div class="invalid-feedback">
Por favor, selecione um arquivo HEIC válido.
</div>
</div>
<div class="d-grid gap-2">
<button type="button" id="convertPreviewBtn" class="btn btn-primary">
<i class="bi bi-eye me-2"></i>Converter e Visualizar
</button>
<button type="submit" class="btn btn-outline-secondary">
<i class="bi bi-download me-2"></i>Converter e Baixar Diretamente
</button>
</div>
</form>
</div>
<!-- JPG to HEIC Tab -->
<div class="tab-pane fade" id="jpg-to-heic" role="tabpanel" aria-labelledby="jpg-to-heic-tab">
<form id="jpgToHeicForm" asp-action="ConvertJpgToHeic" method="post" enctype="multipart/form-data" class="needs-validation">
<div asp-validation-summary="ModelOnly" class="alert alert-danger" role="alert"></div>
<div class="mb-4">
<label for="jpgFile" class="form-label fw-semibold">@ViewBag.JpgFileInputLabel</label>
<input class="form-control" type="file" id="jpgFile" name="jpgFile" accept=".jpg,.jpeg,.JPG,.JPEG" required>
<div class="form-text">
<i class="bi bi-info-circle me-1"></i>Formatos aceitos: .jpg, .jpeg (máx. 10MB para preview)
</div>
<div class="invalid-feedback">
Por favor, selecione um arquivo JPG válido.
</div>
</div>
<div class="d-grid gap-2">
<button type="button" id="convertJpgPreviewBtn" class="btn btn-primary">
<i class="bi bi-eye me-2"></i>Converter e Visualizar
</button>
<button type="submit" class="btn btn-outline-secondary">
<i class="bi bi-download me-2"></i>Converter e Baixar Diretamente
</button>
</div>
</form>
</div>
</div>
<!-- Loading Spinner -->
<div id="loadingSpinner" class="text-center mt-4" style="display: none;">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Convertendo...</span>
</div>
<p class="mt-2 text-muted">Convertendo sua imagem...</p>
</div>
<!-- Preview Area -->
<div id="previewArea" class="mt-4" style="display: none;">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">
<i class="bi bi-image me-2"></i>Imagem Convertida
</h6>
<button id="downloadBtn" class="btn btn-success btn-sm">
<i class="bi bi-download me-1"></i>Baixar JPG
</button>
</div>
<div class="card-body text-center">
<img id="previewImage" src="" alt="Imagem convertida" class="img-fluid rounded" style="max-height: 400px; max-width: 100%;">
<div class="mt-2">
<small id="imageInfo" class="text-muted"></small>
</div>
</div>
</div>
</div>
<!-- Error Area -->
<div id="errorArea" class="mt-4" style="display: none;">
<div class="alert alert-warning" role="alert">
<i class="bi bi-exclamation-triangle me-2"></i>
<span id="errorMessage"></span>
<button id="fallbackDownloadBtn" class="btn btn-outline-primary btn-sm ms-2">
Tentar Download Direto
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- FAQ Accordion -->
<div class="row mt-5">
<div class="col-lg-10 mx-auto">
<div class="converter-faq">
<h3 class="h4 mb-3 text-center">Perguntas Frequentes</h3>
<div class="accordion" id="heicConverterFaqAccordion">
<div class="accordion-item">
<h2 class="accordion-header" id="headingWhat">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWhat" aria-expanded="false" aria-controls="collapseWhat">
<i class="bi bi-question-circle me-2"></i>@ViewBag.FaqWhatTitle
</button>
</h2>
<div id="collapseWhat" class="accordion-collapse collapse" aria-labelledby="headingWhat" data-bs-parent="#heicConverterFaqAccordion">
<div class="accordion-body">
@ViewBag.FaqWhatContent
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingHow">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseHow" aria-expanded="false" aria-controls="collapseHow">
<i class="bi bi-gear me-2"></i>@ViewBag.FaqHowTitle
</button>
</h2>
<div id="collapseHow" class="accordion-collapse collapse" aria-labelledby="headingHow" data-bs-parent="#heicConverterFaqAccordion">
<div class="accordion-body">
@ViewBag.FaqHowContent
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingWhy">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseWhy" aria-expanded="false" aria-controls="collapseWhy">
<i class="bi bi-lightbulb me-2"></i>@ViewBag.FaqWhyTitle
</button>
</h2>
<div id="collapseWhy" class="accordion-collapse collapse" aria-labelledby="headingWhy" data-bs-parent="#heicConverterFaqAccordion">
<div class="accordion-body">
@ViewBag.FaqWhyContent
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingSecurity">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSecurity" aria-expanded="false" aria-controls="collapseSecurity">
<i class="bi bi-shield-check me-2"></i>@ViewBag.FaqSecurityTitle
</button>
</h2>
<div id="collapseSecurity" class="accordion-collapse collapse" aria-labelledby="headingSecurity" data-bs-parent="#heicConverterFaqAccordion">
<div class="accordion-body">
@ViewBag.FaqSecurityContent
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header" id="headingLimits">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseLimits" aria-expanded="false" aria-controls="collapseLimits">
<i class="bi bi-exclamation-triangle me-2"></i>@ViewBag.FaqLimitsTitle
</button>
</h2>
<div id="collapseLimits" class="accordion-collapse collapse" aria-labelledby="headingLimits" data-bs-parent="#heicConverterFaqAccordion">
<div class="accordion-body">
@ViewBag.FaqLimitsContent
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.nav-tabs .nav-link.disabled {
background-color: #f8f9fa;
border-color: #dee2e6;
color: #6c757d;
cursor: not-allowed;
}
.nav-tabs .nav-link.disabled:hover {
background-color: #f8f9fa;
border-color: #dee2e6;
color: #6c757d;
}
#previewImage {
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
border-radius: 8px;
}
.spinner-border {
width: 3rem;
height: 3rem;
}
@@media (max-width: 576px) {
#previewImage {
max-height: 250px;
}
.card-header .btn {
font-size: 0.875rem;
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const convertPreviewBtn = document.getElementById('convertPreviewBtn');
const convertJpgPreviewBtn = document.getElementById('convertJpgPreviewBtn');
const downloadBtn = document.getElementById('downloadBtn');
const fallbackDownloadBtn = document.getElementById('fallbackDownloadBtn');
const heicForm = document.getElementById('heicToJpgForm');
const jpgForm = document.getElementById('jpgToHeicForm');
const heicFileInput = document.getElementById('heicFile');
const jpgFileInput = document.getElementById('jpgFile');
const loadingSpinner = document.getElementById('loadingSpinner');
const previewArea = document.getElementById('previewArea');
const errorArea = document.getElementById('errorArea');
const previewImage = document.getElementById('previewImage');
const imageInfo = document.getElementById('imageInfo');
const errorMessage = document.getElementById('errorMessage');
let convertedImageData = null;
let convertedFileName = null;
// Bootstrap form validation
const forms = document.querySelectorAll('.needs-validation');
Array.from(forms).forEach(function(form) {
form.addEventListener('submit', function(event) {
if (!form.checkValidity()) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
});
});
// File input validation
heicFileInput.addEventListener('change', function() {
hideAllAreas();
if (this.files.length > 0) {
const file = this.files[0];
const maxSize = 10 * 1024 * 1024; // 10MB
if (file.size > maxSize) {
showError('Arquivo muito grande para preview (máx. 10MB). Use "Converter e Baixar Diretamente".');
convertPreviewBtn.disabled = true;
} else {
convertPreviewBtn.disabled = false;
}
}
});
jpgFileInput.addEventListener('change', function() {
hideAllAreas();
if (this.files.length > 0) {
const file = this.files[0];
const maxSize = 10 * 1024 * 1024; // 10MB
if (file.size > maxSize) {
showError('Arquivo muito grande para preview (máx. 10MB). Use "Converter e Baixar Diretamente".');
convertJpgPreviewBtn.disabled = true;
} else {
convertJpgPreviewBtn.disabled = false;
}
}
});
// Convert and Preview button
convertPreviewBtn.addEventListener('click', async function() {
if (!heicFileInput.files[0]) {
showError('Por favor, selecione um arquivo HEIC primeiro.');
return;
}
await convertToPreview(heicForm);
});
// Convert JPG to HEIC Preview button
convertJpgPreviewBtn.addEventListener('click', async function() {
if (!jpgFileInput.files[0]) {
showError('Por favor, selecione um arquivo JPG primeiro.');
return;
}
await convertToPreview(jpgForm);
});
// Download button (after preview)
downloadBtn.addEventListener('click', function() {
if (convertedImageData && convertedFileName) {
downloadBase64Image(convertedImageData, convertedFileName);
}
});
// Fallback download button
fallbackDownloadBtn.addEventListener('click', function() {
const activeTab = document.querySelector('.nav-link.active');
const currentForm = activeTab.id === 'heic-to-jpg-tab' ? heicForm : jpgForm;
submitFormForDownload(currentForm);
});
async function convertToPreview(form) {
showLoading();
try {
const formData = new FormData(form);
const response = await fetch(form.action + '?preview=true', {
method: 'POST',
body: formData
});
if (response.ok) {
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
// JSON response with base64
const result = await response.json();
if (result.success) {
showPreview(result.base64, result.filename, result.originalSize, result.convertedSize);
} else {
showError(result.message || 'Erro na conversão. Tentando download direto...');
setTimeout(() => submitFormForDownload(form), 2000);
}
} else {
// Binary response - fallback to download
showError('Arquivo muito grande para preview. Iniciando download direto...');
setTimeout(() => submitFormForDownload(form), 1000);
}
} else {
const errorText = await response.text();
showError('Erro na conversão: ' + (errorText || response.statusText));
}
} catch (error) {
console.error('Conversion error:', error);
showError('Erro de conexão. Tentando download direto...');
setTimeout(() => submitFormForDownload(form), 2000);
}
}
function showPreview(base64Data, filename, originalSize, convertedSize) {
hideAllAreas();
previewImage.src = base64Data;
convertedImageData = base64Data;
convertedFileName = filename;
const originalSizeMB = (originalSize / (1024 * 1024)).toFixed(2);
const convertedSizeMB = (convertedSize / (1024 * 1024)).toFixed(2);
const reduction = (((originalSize - convertedSize) / originalSize) * 100).toFixed(1);
imageInfo.textContent = `Original: ${originalSizeMB}MB → Convertido: ${convertedSizeMB}MB (${reduction}% menor)`;
previewArea.style.display = 'block';
}
function showLoading() {
hideAllAreas();
loadingSpinner.style.display = 'block';
}
function showError(message) {
hideAllAreas();
errorMessage.textContent = message;
errorArea.style.display = 'block';
}
function hideAllAreas() {
loadingSpinner.style.display = 'none';
previewArea.style.display = 'none';
errorArea.style.display = 'none';
}
function submitFormForDownload(form) {
// Submit form normally for direct download
if (form) {
form.submit();
}
}
function downloadBase64Image(base64Data, filename) {
// Convert base64 to blob and download
const base64Response = fetch(base64Data);
base64Response.then(res => res.blob()).then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
});
}
});
</script>