531433f54c
Simplified PDF download UX to use only the modal loading overlay, removing the redundant toast notification that appeared when the modal was closed during download. Updated tests to reflect the new behavior. Changes: - Removed toast trigger logic from PDF modal download function - Removed modal close event listener for toast display - Updated toast notification test expectations - Fixed recommended card outline styling
368 lines
17 KiB
HTML
368 lines
17 KiB
HTML
{{define "pdf-modal"}}
|
|
<!-- PDF Download Modal - Interactive Thumbnails -->
|
|
<dialog id="pdf-modal" class="info-modal pdf-download-modal no-print"
|
|
_="on click
|
|
if event.target is me
|
|
call me.close()
|
|
end">
|
|
<div class="info-modal-content" id="pdf-modal-content">
|
|
<!-- Loading Overlay -->
|
|
<div class="pdf-loading-overlay" id="pdf-loading-overlay">
|
|
<div class="pdf-loading-content">
|
|
<div class="pdf-loading-spinner"></div>
|
|
<h3 class="pdf-loading-title" id="pdf-loading-title">
|
|
{{if eq .Lang "es"}}Preparando PDF...{{else}}Preparing PDF...{{end}}
|
|
</h3>
|
|
<p class="pdf-loading-message" id="pdf-loading-message">
|
|
{{if eq .Lang "es"}}Por favor espera mientras generamos tu CV{{else}}Please wait while we generate your CV{{end}}
|
|
</p>
|
|
<p class="pdf-loading-estimate" id="pdf-loading-estimate"></p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Close Button -->
|
|
<button class="info-modal-close"
|
|
onclick="document.getElementById('pdf-modal').close()"
|
|
aria-label="{{if eq .Lang "es"}}Cerrar{{else}}Close{{end}}">
|
|
<iconify-icon icon="mdi:close" width="24" height="24"></iconify-icon>
|
|
</button>
|
|
|
|
<!-- Header -->
|
|
<div class="info-modal-header">
|
|
<iconify-icon icon="catppuccin:pdf" width="40" height="40" style="margin-bottom: 0.5rem;"></iconify-icon>
|
|
<h2>{{if eq .Lang "es"}}Descargar PDF{{else}}Download PDF{{end}}</h2>
|
|
<p class="pdf-modal-subtitle">
|
|
{{if eq .Lang "es"}}Elige tu formato preferido{{else}}Choose your preferred format{{end}}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Body: Three Thumbnail Cards -->
|
|
<div class="pdf-options-grid">
|
|
|
|
<!-- Short CV Card -->
|
|
<div class="pdf-option-card"
|
|
data-cv-format="short"
|
|
role="radio"
|
|
aria-checked="false"
|
|
aria-label="{{if eq .Lang "es"}}CV Corto - 4 páginas, información esencial{{else}}Short CV - 4 pages, essential information{{end}}"
|
|
tabindex="0"
|
|
_="on click
|
|
-- Remove selected from all cards
|
|
set cards to .pdf-option-card in #pdf-modal
|
|
for card in cards
|
|
remove .selected from card
|
|
set card's @aria-checked to 'false'
|
|
end
|
|
|
|
-- Add selected to this card
|
|
add .selected to me
|
|
set my @aria-checked to 'true'
|
|
|
|
-- Enable download button
|
|
set downloadBtn to .pdf-download-btn in #pdf-modal
|
|
remove @disabled from downloadBtn
|
|
|
|
-- Store selected format
|
|
set :selectedFormat to my @data-cv-format
|
|
|
|
-- Announce to screen readers
|
|
set announcement to #pdf-selection-announcement
|
|
if :selectedFormat is 'short'
|
|
set announcement.textContent to '{{if eq .Lang "es"}}Seleccionado: CV Corto - Una página{{else}}Selected: Short CV - One page{{end}}'
|
|
end
|
|
end
|
|
|
|
on keydown
|
|
if event.key is 'Enter' or event.key is ' '
|
|
halt the event
|
|
trigger click on me
|
|
end
|
|
end">
|
|
|
|
<div class="pdf-thumbnail thumbnail-short">
|
|
<!-- Header representation -->
|
|
<div class="skeleton-block" style="height: 48px;"></div>
|
|
|
|
<!-- Content sections (compact) -->
|
|
<div class="skeleton-block" style="height: 60px;"></div>
|
|
<div class="skeleton-block" style="height: 60px;"></div>
|
|
<div class="skeleton-block" style="height: 60px;"></div>
|
|
|
|
<!-- Page count badge -->
|
|
<div class="thumbnail-badge">
|
|
{{if eq .Lang "es"}}4 Páginas{{else}}4 Pages{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pdf-option-info">
|
|
<h3>{{if eq .Lang "es"}}CV Corto (4 páginas){{else}}Short CV (4 pages){{end}}</h3>
|
|
<p>{{if eq .Lang "es"}}Información esencial{{else}}Essential info{{end}}</p>
|
|
</div>
|
|
|
|
<div class="pdf-option-badge">
|
|
<iconify-icon icon="mdi:check-circle" width="32" height="32"></iconify-icon>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Default CV Card (Recommended) -->
|
|
<div class="pdf-option-card pdf-option-recommended selected"
|
|
data-cv-format="default"
|
|
role="radio"
|
|
aria-checked="true"
|
|
aria-label="{{if eq .Lang "es"}}CV Por Defecto - 5 páginas con habilidades (Recomendado){{else}}Default CV - 5 pages with skills (Recommended){{end}}"
|
|
tabindex="0"
|
|
_="on click
|
|
-- Remove selected from all cards
|
|
set cards to .pdf-option-card in #pdf-modal
|
|
for card in cards
|
|
remove .selected from card
|
|
set card's @aria-checked to 'false'
|
|
end
|
|
|
|
-- Add selected to this card
|
|
add .selected to me
|
|
set my @aria-checked to 'true'
|
|
|
|
-- Enable download button
|
|
set downloadBtn to .pdf-download-btn in #pdf-modal
|
|
remove @disabled from downloadBtn
|
|
|
|
-- Store selected format
|
|
set :selectedFormat to my @data-cv-format
|
|
|
|
-- Announce to screen readers
|
|
set announcement to #pdf-selection-announcement
|
|
if :selectedFormat is 'default'
|
|
set announcement.textContent to '{{if eq .Lang "es"}}Seleccionado: CV Por Defecto (Recomendado){{else}}Selected: Default CV (Recommended){{end}}'
|
|
end
|
|
end
|
|
|
|
on keydown
|
|
if event.key is 'Enter' or event.key is ' '
|
|
halt the event
|
|
trigger click on me
|
|
end
|
|
end">
|
|
|
|
<div class="pdf-thumbnail thumbnail-default">
|
|
<!-- Two-column layout with sidebar -->
|
|
<div class="skeleton-block" style="height: 36px; margin-bottom: 6px;"></div>
|
|
<div style="display: flex; gap: 4px;">
|
|
<div style="width: 25%; display: flex; flex-direction: column; gap: 3px;">
|
|
<div class="skeleton-block" style="height: 20px;"></div>
|
|
<div class="skeleton-block" style="height: 16px;"></div>
|
|
</div>
|
|
<div style="width: 75%; display: flex; flex-direction: column; gap: 3px;">
|
|
<div class="skeleton-block" style="height: 32px;"></div>
|
|
<div class="skeleton-block" style="height: 32px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Page count badge with star -->
|
|
<div class="thumbnail-badge" style="font-weight: 600;">
|
|
⭐ {{if eq .Lang "es"}}5 Páginas{{else}}5 Pages{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pdf-option-info">
|
|
<h3>
|
|
{{if eq .Lang "es"}}CV Por Defecto (5 páginas){{else}}Default CV (5 pages){{end}}
|
|
<span style="color: #667eea; font-size: 0.9em;">⭐</span>
|
|
</h3>
|
|
<p style="font-weight: 500;">{{if eq .Lang "es"}}Corto con habilidades - Recomendado{{else}}Short with skills - Recommended{{end}}</p>
|
|
</div>
|
|
|
|
<div class="pdf-option-badge">
|
|
<iconify-icon icon="mdi:check-circle" width="32" height="32"></iconify-icon>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Extended CV Card -->
|
|
<div class="pdf-option-card"
|
|
data-cv-format="long"
|
|
role="radio"
|
|
aria-checked="false"
|
|
aria-label="{{if eq .Lang "es"}}CV Extendido - 9 páginas, versión completa{{else}}Extended CV - 9 pages, full version{{end}}"
|
|
tabindex="0"
|
|
_="on click
|
|
-- Remove selected from all cards
|
|
set cards to .pdf-option-card in #pdf-modal
|
|
for card in cards
|
|
remove .selected from card
|
|
set card's @aria-checked to 'false'
|
|
end
|
|
|
|
-- Add selected to this card
|
|
add .selected to me
|
|
set my @aria-checked to 'true'
|
|
|
|
-- Enable download button
|
|
set downloadBtn to .pdf-download-btn in #pdf-modal
|
|
remove @disabled from downloadBtn
|
|
|
|
-- Store selected format
|
|
set :selectedFormat to my @data-cv-format
|
|
|
|
-- Announce to screen readers
|
|
set announcement to #pdf-selection-announcement
|
|
if :selectedFormat is 'long'
|
|
set announcement.textContent to '{{if eq .Lang "es"}}Seleccionado: CV Completo - Versión completa{{else}}Selected: Long CV - Full version{{end}}'
|
|
end
|
|
end
|
|
|
|
on keydown
|
|
if event.key is 'Enter' or event.key is ' '
|
|
halt the event
|
|
trigger click on me
|
|
end
|
|
end">
|
|
|
|
<div class="pdf-thumbnail thumbnail-long">
|
|
<!-- Header representation -->
|
|
<div class="skeleton-block" style="height: 48px;"></div>
|
|
|
|
<!-- More content sections (detailed) -->
|
|
<div class="skeleton-block" style="height: 40px;"></div>
|
|
<div class="skeleton-block" style="height: 40px;"></div>
|
|
<div class="skeleton-block" style="height: 40px;"></div>
|
|
<div class="skeleton-block" style="height: 40px;"></div>
|
|
<div class="skeleton-block" style="height: 40px;"></div>
|
|
|
|
<!-- Page count badge -->
|
|
<div class="thumbnail-badge">
|
|
{{if eq .Lang "es"}}9 Páginas{{else}}9 Pages{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pdf-option-info">
|
|
<h3>{{if eq .Lang "es"}}CV Extendido (9 páginas){{else}}Extended CV (9 pages){{end}}</h3>
|
|
<p>{{if eq .Lang "es"}}Todos los detalles{{else}}All details{{end}}</p>
|
|
</div>
|
|
|
|
<div class="pdf-option-badge">
|
|
<iconify-icon icon="mdi:check-circle" width="32" height="32"></iconify-icon>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer: Download Button -->
|
|
<div class="pdf-modal-footer">
|
|
<button class="pdf-download-btn"
|
|
id="pdf-download-btn"
|
|
onclick="downloadPDF()">
|
|
<iconify-icon icon="mdi:download" width="20" height="20"></iconify-icon>
|
|
{{if eq .Lang "es"}}Descargar PDF{{else}}Download PDF{{end}}
|
|
</button>
|
|
</div>
|
|
|
|
<script>
|
|
// Reset loading state when modal opens to prevent stuck blur effect
|
|
(function() {
|
|
const modal = document.getElementById('pdf-modal');
|
|
const modalContent = document.getElementById('pdf-modal-content');
|
|
const overlay = document.getElementById('pdf-loading-overlay');
|
|
|
|
// Clean up loading state when modal closes
|
|
modal.addEventListener('close', function() {
|
|
overlay.classList.remove('active');
|
|
modalContent.classList.remove('loading-active');
|
|
});
|
|
|
|
// Also clean up when modal opens (in case of stuck state)
|
|
// Use MutationObserver to detect when modal becomes visible
|
|
const observer = new MutationObserver(function(mutations) {
|
|
mutations.forEach(function(mutation) {
|
|
if (mutation.attributeName === 'open') {
|
|
if (modal.hasAttribute('open')) {
|
|
// Modal just opened - ensure clean state
|
|
overlay.classList.remove('active');
|
|
modalContent.classList.remove('loading-active');
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
observer.observe(modal, { attributes: true });
|
|
})();
|
|
|
|
function downloadPDF() {
|
|
const selectedCard = document.querySelector('#pdf-modal .pdf-option-card.selected');
|
|
if (!selectedCard) {
|
|
console.error('No card selected');
|
|
return;
|
|
}
|
|
|
|
const selectedFormat = selectedCard.getAttribute('data-cv-format');
|
|
const lang = '{{.Lang}}';
|
|
const isSpanish = lang === 'es';
|
|
let url;
|
|
let estimatedTime = 4; // Default: 4 seconds
|
|
let formatName = '';
|
|
|
|
console.log('Download requested for format:', selectedFormat);
|
|
|
|
// Build URL and set estimated time based on format
|
|
if (selectedFormat === 'short') {
|
|
// Short CV: clean version (no skills), short length
|
|
url = `/export/pdf?lang=${lang}&length=short&icons=show&version=clean`;
|
|
estimatedTime = 3;
|
|
formatName = isSpanish ? 'CV Corto (4 páginas)' : 'Short CV (4 pages)';
|
|
} else if (selectedFormat === 'default') {
|
|
// Default CV: use shortcut URL (short with skills, 5 pages)
|
|
const currentYear = new Date().getFullYear();
|
|
url = `/cv-jamr-${currentYear}-${lang}.pdf`;
|
|
estimatedTime = 4;
|
|
formatName = isSpanish ? 'CV Por Defecto (5 páginas)' : 'Default CV (5 pages)';
|
|
} else if (selectedFormat === 'long') {
|
|
// Long CV: with skills sidebar, long length
|
|
url = `/export/pdf?lang=${lang}&length=long&icons=show&version=with_skills`;
|
|
estimatedTime = 8;
|
|
formatName = isSpanish ? 'CV Extendido (9 páginas)' : 'Extended CV (9 pages)';
|
|
} else if (selectedFormat === 'current') {
|
|
// Current view: use localStorage settings
|
|
let currentLength = localStorage.getItem('cv-length') || 'short';
|
|
const currentIcons = localStorage.getItem('cv-icons') || 'show';
|
|
const currentTheme = localStorage.getItem('cv-theme') || 'default';
|
|
const version = currentTheme === 'clean' ? 'clean' : 'with_skills';
|
|
url = `/export/pdf?lang=${lang}&length=${currentLength}&icons=${currentIcons}&version=${version}`;
|
|
estimatedTime = currentLength === 'long' ? 8 : 4;
|
|
formatName = isSpanish ? 'CV Actual' : 'Current CV';
|
|
}
|
|
|
|
// Show loading overlay in modal
|
|
const overlay = document.getElementById('pdf-loading-overlay');
|
|
const modalContent = document.getElementById('pdf-modal-content');
|
|
const estimateEl = document.getElementById('pdf-loading-estimate');
|
|
const modal = document.getElementById('pdf-modal');
|
|
|
|
overlay.classList.add('active');
|
|
modalContent.classList.add('loading-active');
|
|
|
|
// Update estimate message
|
|
const estimateMsg = isSpanish
|
|
? `Generando ${formatName}... Esto puede tardar ~${estimatedTime} segundos`
|
|
: `Generating ${formatName}... This may take ~${estimatedTime} seconds`;
|
|
estimateEl.textContent = estimateMsg;
|
|
|
|
console.log('Navigating to:', url);
|
|
|
|
// Trigger download
|
|
window.location.href = url;
|
|
|
|
// After estimated time: remove loading state and close modal
|
|
setTimeout(() => {
|
|
overlay.classList.remove('active');
|
|
modalContent.classList.remove('loading-active');
|
|
|
|
// Close modal if still open
|
|
modal.close();
|
|
}, estimatedTime * 1000);
|
|
}
|
|
</script>
|
|
|
|
<!-- Screen Reader Announcement Area -->
|
|
<div id="pdf-selection-announcement" class="sr-only" aria-live="polite" aria-atomic="true"></div>
|
|
</div>
|
|
</dialog>
|
|
{{end}}
|