1// pages/WhyCVPage.js 2 3import { contentService } from "../services/ContentService.js"; 4import { filmographyService } from "../services/FilmographyService.js"; 5import { i18n } from "../services/I18nService.js"; 6import { seoService } from "../services/SeoService.js"; 7import { incentivesService } from "../services/IncentivesService.js"; 8import { mediaService } from "../services/MediaService.js"; 9import { leadService } from "../services/LeadService.js"; 10import { pressService } from "../services/PressService.js"; 11import { AppConfig } from "../config/AppConfig.js"; 12import { initCarousel, renderCarousel, renderPopup } from '../components/Carousel.js'; 13 14//VARIABLES PARA ABREVIAR I18n Y LLAMADA GLOBAL 15const t = (key) => i18n.t(key); 16const overview = 'why-cv.overview'; 17const data = 'why-cv.incentives'; 18const filmography = 'why-cv.filmography'; 19const guide = 'why-cv.production_guide'; 20const connectivity = 'why-cv.connectivity'; 21const investors = 'why-cv.investors'; 22 23// --- 1. VISTA GENERAL DE LA SECCIÃN --- 24function renderWhyCVOverview() { 25 26 setTimeout(async () => { 27 const response = await contentService.getPageContent('why-cv'); 28 const videoUrl = mediaService.getImageUrl(response?.conectivity_video?.url || ''); 29 const videoPoster = mediaService.getImageUrl(response?.conectivity_video?.poster || ''); 30 31 const titleContainer = document.getElementById('title-container'); 32 if(titleContainer) { 33 titleContainer.innerHTML = `<h1 class="text-5xl md:text-7xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none"> 34 ${response.title_1 || t(`${overview}.title_1`)} <br><span class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold to-yellow-600">${response.title_2 || t(`${overview}.title_2`)}</span> 35 </h1> 36 <p class="text-lg text-slate-500 max-w-3xl mx-auto leading-relaxed"> 37 ${response.subtitle || t(`${overview}.subtitle`)} 38 </p>` 39 } 40 41 const content = document.getElementById('content'); 42 if(content && response) { 43 content.innerHTML = `<div class="relative h-[500px] rounded-[2.5rem] overflow-hidden shadow-2xl mb-16 border-4 border-slate-100 group"> 44 <video controls class="absolute inset-0 w-full h-full object-cover" poster="${videoPoster}"> 45 <source src="${videoUrl}" type="video/mp4"> 46 ${t('why-cv.connectivity.video_error')} 47 </video> 48 49 <div class="absolute inset-0 bg-gradient-to-br from-slate-800 to-black opacity-30 group-hover:opacity-10 transition-opacity duration-500 pointer-events-none"></div> 50 </div> 51 52 <div class="grid grid-cols-1 md:grid-cols-4 gap-8 mt-8 text-center"> 53 54 <div class="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-lg hover:shadow-xl hover:-translate-y-1 transition duration-300 group"> 55 <div class="relative z-10 flex flex-col items-center h-full"> 56 <div class="w-16 h-16 bg-slate-50 rounded-2xl flex items-center justify-center mb-8 border border-slate-100 group-hover:scale-110 transition-transform duration-500 shadow-sm"> 57 <i data-lucide="${response.stats_card.icon || 'sun'}" class="w-8 h-8 text-amber-500"></i> 58 </div> 59 60 <div class="flex items-baseline justify-center gap-1 mb-4"> 61 <span class="text-6xl md:text-7xl font-black text-cdl-blue tracking-tighter">${response.stats_card.value || '300'}</span> 62 <span class="text-4xl font-bold text-cdl-gold">${response.stats_card.suffix || '+'}</span> 63 </div> 64 65 <div class="w-8 h-1 bg-slate-200 rounded-full mb-6 group-hover:w-16 group-hover:bg-cdl-gold transition-all duration-300"></div> 66 67 <h3 class="text-slate-600 font-bold uppercase tracking-widest text-xs leading-relaxed">${response.stats_card.label || 'DÃas de sol al año'}</h3> 68 </div> 69 </div> 70 71 ${response.conectivity_cards.map(c => `<div class="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-lg hover:shadow-xl hover:-translate-y-1 transition duration-300 group"> 72 <div class="w-16 h-16 bg-blue-50 rounded-2xl flex items-center justify-center mx-auto mb-6 group-hover:bg-cdl-blue transition-colors"> 73 <i data-lucide="${c.lucide}" class="w-8 h-8 text-cdl-blue group-hover:text-white transition-colors"></i> 74 </div> 75 <h3 class="font-black text-xl text-slate-900 mb-2 uppercase tracking-tight">${c.title}</h3> 76 <p class="text-slate-500 text-sm leading-relaxed">${c.desc}</p> 77 </div>`).join('')} 78 </div>` 79 } 80 81 lucide.createIcons(); 82 }, 0) 83 84 return ` 85 <div class="w-full bg-white"> 86 87 <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto text-center"> 88 <nav class="flex justify-center text-xs text-slate-400 mb-6 uppercase tracking-widest font-bold" aria-label="Breadcrumb"> 89 <ol class="list-none p-0 inline-flex items-center gap-2"> 90 <li><a href="/" class="hover:text-cdl-blue transition">${t('actions.home')}</a></li> 91 <li><span class="text-cdl-gold">/</span></li> 92 <li><span class="text-cdl-blue">${t('why-cv.nav')}</span></li> 93 </ol> 94 </nav> 95 <div id="title-container"> 96 97 </div> 98 </div> 99 100 <div class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-24"> 101 102 <div id="content"> 103 104 </div> 105 106 <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mt-8"> 107 <a href="/why-cv/production-guide" class="group !relative bg-blue-900 p-8 rounded-[2.5rem] shadow-xl hover:shadow-2xl hover:shadow-blue-900/30 transition-all duration-300 hover:-translate-y-2 flex flex-col overflow-hidden"> 108 <div class="absolute top-0 right-0 w-32 h-32 bg-white/5 rounded-full blur-3xl -translate-y-1/2 translate-x-1/2"></div> 109 <div class="w-16 h-16 bg-white/10 backdrop-blur-sm rounded-2xl flex items-center justify-center mb-6 border border-white/20"> 110 <i data-lucide="book-open" class="w-8 h-8 text-white"></i> 111 </div> 112 <h3 class="text-2xl font-black text-white mb-2 uppercase tracking-tight">${t(`${guide}.title_1`)} ${t(`${guide}.title_2`)}</h3> 113 <p class="text-blue-200 text-sm leading-relaxed mb-6 flex-grow">${t(`${guide}.subtitle`)}</p>
114 <span class="text-xs font-bold text-white uppercase tracking-widest flex items-center gap-2">${t(`${guide}.action`)} <i data-lucide="arrow-right" class="w-4 h-4"></i></span> 115 </a> 116 117 <a href="/why-cv/incentives" class="group bg-white p-8 rounded-[2.5rem] border border-slate-100 shadow-lg hover:shadow-2xl transition-all duration-300 hover:-translate-y-2 flex flex-col"> 118 <div class="w-16 h-16 bg-blue-50 rounded-2xl flex items-center justify-center mb-6 group-hover:bg-cdl-blue transition-colors"> 119 <i data-lucide="bar-chart-2" class="w-8 h-8 text-cdl-blue group-hover:text-white transition-colors"></i> 120 </div> 121 <h3 class="text-2xl font-black text-slate-800 mb-2 group-hover:text-cdl-blue uppercase tracking-tight">${t(`${data}.title_1`)} ${t(`${data}.title_2`)}</h3> 122 <p class="text-slate-500 text-sm leading-relaxed mb-6 flex-grow">${t(`${data}.subtitle`)}</p> 123 <span class="text-xs font-bold text-cdl-gold uppercase tracking-widest flex items-center gap-2">${t(`${data}.action`)} <i data-lucide="arrow-right" class="w-4 h-4"></i></span> 124 </a> 125 126 127 <a href="/why-cv/investors" class="group bg-slate-50 p-8 rounded-[2.5rem] border border-slate-200 shadow-lg hover:shadow-2xl transition-all duration-300 hover:-translate-y-2 flex flex-col"> 128 <div class="w-16 h-16 bg-white rounded-2xl flex items-center justify-center mb-6 shadow-sm"> 129 <i data-lucide="trending-up" class="w-8 h-8 text-slate-800"></i> 130 </div> 131 <h3 class="text-2xl font-black text-slate-800 mb-2 uppercase tracking-tight">${t(`${investors}.title`)}</h3> 132 <p class="text-slate-500 text-sm leading-relaxed mb-6 flex-grow">${t(`${investors}.subtitle`)}</p> 133 <span class="text-xs font-bold text-slate-800 uppercase tracking-widest flex items-center gap-2">${t(`${investors}.action`)} <i data-lucide="arrow-right" class="w-4 h-4"></i></span> 134 </a> 135 </div> 136 137 </div> 138 </div>`; 139} 140 141// --- 2. SUBPÃGINA 4.1: DATOS Y CIFRAS --- 142// --- 2. SUBPÃGINA 4.2: DATOS Y CIFRAS (PREMIUM DASHBOARD) --- 143function renderDataCard(data, index){ 144 // Asignación de iconos dinámicos por si no vienen del backend 145 const icons = ['film', 'sun', 'trending-up', 'landmark', 'pie-chart', 'award']; 146 const icon = data.icon || icons[index % icons.length]; 147 148 return ` 149 <div class="relative bg-white p-10 rounded-[3rem] text-center border border-slate-100 shadow-xl hover:shadow-2xl hover:-translate-y-2 transition-all duration-500 overflow-hidden group"> 150 <div class="absolute -top-10 -right-10 w-32 h-32 bg-blue-50 rounded-full blur-3xl group-hover:bg-blue-100 transition-colors duration-500 pointer-events-none"></div> 151 152 <div class="relative z-10 flex flex-col items-center h-full"> 153 <div class="w-16 h-16 bg-slate-50 rounded-2xl flex items-center justify-center mb-8 border border-slate-100 group-hover:scale-110 transition-transform duration-500 shadow-sm"> 154 <i data-lucide="${icon}" class="w-8 h-8 text-cdl-gold"></i> 155 </div> 156 157 <div class="flex items-baseline justify-center gap-1 mb-4"> 158 <span class="text-6xl md:text-7xl font-black text-cdl-blue tracking-tighter">${data.value}</span> 159 <span class="text-4xl font-bold text-cdl-gold">${data.suffix || ''}</span> 160 </div> 161 162 <div class="w-8 h-1 bg-slate-200 rounded-full mb-6 group-hover:w-16 group-hover:bg-cdl-gold transition-all duration-300"></div> 163 164 <h3 class="text-slate-600 font-bold uppercase tracking-widest text-xs leading-relaxed">${data.label}</h3> 165 </div> 166 </div>`; 167} 168 169function renderDataPage() { 170 const t = (key) => i18n.t(key); 171 const dataKey = 'why-cv.data_figures'; 172 173 setTimeout(async () => { 174 const dataSection = document.getElementById('data-section'); 175 try{ 176 const pageData = await contentService.getPageContent('why-cv'); 177 const incentives = await incentivesService.getCurrentIncentives(); 178 const greenBook = await pressService.getGreenBook(); 179 180 // Se mapea un nuevo objeto data buscando los objetos que comparten datos con incentives. 181 const newData = pageData.stats.map((d, index) => { 182 if (d.incentives) { 183 d.value = incentives[d.incentives]?.percentage || d.value; 184 } 185 return renderDataCard(d, index); 186 }); 187 188 if (dataSection) dataSection.innerHTML = newData.join(''); 189 190 const greenBookBtn = document.getElementById('green-book-btn'); 191 if (greenBookBtn) { 192 if (greenBook && greenBook.url) { 193 greenBookBtn.href = greenBook.url; 194 greenBookBtn.innerHTML = `<i data-lucide="${greenBook.icon}" class="w-5 h-5"></i> ${t('home.green_book_cta')}`; 195 greenBookBtn.classList.remove('hidden'); 196 } 197 } 198 199 if (window.lucide) lucide.createIcons(); 200 201 } catch(error) { 202 console.error("Error al devolver datos: ", error); 203 if (dataSection) { 204 dataSection.innerHTML = `<div class="col-span-full text-center text-red-500 font-bold py-10">${t('actions.error_loading') || 'Error al cargar las estadÃsticas.'}</div>`; 205 } 206 } 207 }, 0); 208 209 return ` 210 <div class="w-full bg-slate-50/30 min-h-screen"> 211 212 <div class="relative pt-32 pb-20 px-6 max-w-7xl mx-auto text-center overflow-hidden bg-white border border-slate-100 shadow-sm rounded-[3rem] md:rounded-[4rem] mb-16 mt-6"> 213 <div class="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] bg-cdl-blue/5 rounded-full blur-[100px] pointer-events-none"></div> 214 215 <div class="relative z-10"> 216 <a href="/why-cv" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-8 text-xs font-bold uppercase tracking-widest transition"> 217 <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')} 218 </a> 219 <br> 220 <div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-blue-50 text-cdl-blue text-[10px] font-black uppercase tracking-widest mb-6 border border-blue-100 shadow-sm"> 221 <i data-lucide="bar-chart-2" class="w-4 h-4"></i> ${t(`${dataKey}.title_1`)} ${t(`${dataKey}.title_2`)} 222 </div> 223 224 <h1 class="text-5xl md:text-7xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none"> 225 ${t(`${dataKey}.title_1`)} <span class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold to-yellow-600">${t(`${dataKey}.title_2`)}</span> 226 </h1> 227 <p class="text-lg text-slate-500 max-w-2xl mx-auto leading-relaxed font-medium"> 228 ${t(`${dataKey}.desc`)} 229 </p> 230 </div> 231 </div> 232 233 <div class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-32"> 234 <div id="data-section" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8"> 235 <div class="col-span-full flex flex-col items-center justify-center py-24 space-y-4"> 236 <div class="animate-spin rounded-full h-12 w-12 border-4 border-slate-200 border-t-cdl-gold"></div> 237 <p class="text-slate-400 font-medium animate-pulse">${t('actions.loader')}</p> 238 </div> 239 </div> 240 241 <div class="mt-24 bg-cdl-blue rounded-[3rem] p-12 md:p-16 text-center relative overflow-hidden shadow-2xl"> 242 <div class="absolute top-0 right-0 w-64 h-64 bg-cdl-gold/20 rounded-full blur-3xl -translate-y-32 translate-x-32 pointer-events-none"></div> 243 <div class="absolute bottom-0 left-0 w-64 h-64 bg-cyan-500/20 rounded-full blur-3xl translate-y-32 -translate-x-32 pointer-events-none"></div> 244 245 <div class="relative z-10 max-w-3xl mx-auto"> 246 <h2 class="text-3xl md:text-5xl font-black text-white mb-6 uppercase italic tracking-tight">${t(`${dataKey}.info_title`)}</h2> 247 <p class="text-blue-100 text-lg mb-10 leading-relaxed">${t(`${dataKey}.info_subtitle`)}</p> 248 <div class="flex flex-wrap justify-center gap-4"> 249 <a href="/contact" class="inline-flex items-center gap-3 bg-cdl-gold hover:bg-yellow-500 text-white font-black py-4 px-10 rounded-full shadow-xl shadow-amber-900/20 transition-all transform hover:scale-105 uppercase text-xs tracking-widest"> 250 <i data-lucide="download" class="w-5 h-5"></i> ${t(`${dataKey}.info_btn`)} 251 </a> 252 <a id="green-book-btn" href="#" target="_blank" rel="noopener noreferrer" download 253 class="hidden inline-flex items-center gap-3 bg-white text-cdl-blue font-black py-4 px-10 rounded-full transition-all transform hover:scale-105 hover:bg-cdl-gold hover:text-white uppercase text-xs tracking-widest shadow-xl"> 254 255 </a> 256 </div> 257 </div> 258 </div> 259 </div> 260 </div>`; 261} 262 263// --- 3. SUBPÃGINA 4.2: FILMOGRAFÃA ---
264function renderSuccesStoriesCard(data){ 265 const t = (key) => i18n.t(key); 266 267 // Pasamos la imagen por el mediaService. 268 // Si tu servicio acepta un parámetro de tamaño, podrÃas usar: mediaService.getImageUrl(data.image, 'small') 269 const imageUrl = mediaService.getImageUrl(data.image); 270 271 return `<div onclick="window.navigateTo('/filmography/${data.slug || data.id}')" class="group relative aspect-[2/3] rounded-xl overflow-hidden bg-slate-200 shadow-md hover:shadow-2xl transition-all duration-500 hover:-translate-y-2 cursor-pointer"> 272 <div class="absolute inset-0 flex items-center justify-center bg-slate-300 text-slate-400 font-bold text-center p-2 text-xs uppercase tracking-widest"> 273 <img class="w-full h-full object-cover object-center" src="${imageUrl}" alt="${data.title || 'Producción'}"> 274 <div class="absolute inset-0 bg-cdl-blue/90 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-col items-center justify-center p-4 text-center"> 275 <h3 class="text-white font-bold text-sm mb-2">${data.title}</h3> 276 <span class="text-cdl-gold text-[10px] font-bold uppercase tracking-wider border border-cdl-gold px-2 py-1 rounded">${t('why-cv.filmography.card_btn') || 'Ver Ficha'}</span> 277 </div> 278 </div> 279 </div>` 280} 281 282function renderFilmographyDetailPage(id) { 283 const t = (key) => i18n.t(key); 284 285 setTimeout(async () => { 286 const container = document.getElementById('filmography-detail-content'); 287 try { 288 const prod = await filmographyService.getFilmographyById(id); 289 290 seoService.update({ 291 seo: prod?.seo, 292 fallback: { title: prod?.title, description: prod?.description, image: prod?.image?.url }, 293 path: `/filmography/${prod?.slug || id}`, 294 }); 295 296 // --- CONFIGURACIÃN DEL CARRUSEL --- 297 if(prod.gallery.length > 0) { 298 const carousel = document.getElementById('carousel'); 299 const popupCarousel = document.getElementById('carousel-popup'); 300 if(carousel) carousel.innerHTML = renderCarousel(prod.gallery) // Se renderiza primero el carousel 301 if(popupCarousel) popupCarousel.innerHTML = renderPopup(); // Luego se renderiza el popup de las imágenes 302 initCarousel(prod.gallery); // Por último se inicia la funcionalidad 303 } 304 305 if (!prod) throw new Error("Not found"); 306 307 //RENDER DATOS FILMOGRAFÃA 308 const poster = document.getElementById('poster-container'); 309 poster.innerHTML = `<img src="${prod.image.url}" class="w-full h-full object-cover object-center" alt="${prod.image.alt}" onerror="this.onerror=null; this.src='${mediaService.fallbackImage}'">`; 310 311 const title = document.getElementById('title-container'); 312 title.innerHTML += `<h1 class="text-5xl md:text-7xl font-black text-cdl-blue uppercase italic tracking-tighter leading-none">${prod.title}</h1> 313 <p class="text-2xl text-slate-400 font-light mt-2 italic">${prod.subtitle}</p>`; 314 315 const synopsis = document.getElementById('synopsis-container'); 316 synopsis.innerHTML += `<p>${prod.description || t('why-cv.filmography.detail.desc_placeholder')}</p>`; 317 318 const locations = document.getElementById('locations-container'); 319 locations.innerHTML = prod?.locations_used.length > 0 ? `<div class="p-6 bg-slate-50 rounded-3xl border border-slate-100"> 320 <span class="block text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2">${t('why-cv.filmography.detail.locations_used') || 'Instalaciones utilizadas'}</span> 321 <ul class="space-y-2"> 322 ${prod.locations_used.map(l => `<li class="flex items-center gap-2 text-cdl-blue font-bold text-sm"> 323 <i data-lucide="check-circle" class="w-4 h-4 text-cdl-gold"></i> ${l} 324 </li>`).join('')} 325 </ul> 326 </div>` : ''; 327 328 const trailer = document.getElementById('film-trailer-grid'); 329 const trailerPanel = document.getElementById('panel-trailer'); 330 const trailerBtn = document.getElementById('tab-trailer'); 331 if(!prod.trailer && trailerPanel && trailerBtn) { 332 trailerPanel.remove(); 333 trailerBtn.remove(); 334 } 335 const videoUrl = mediaService.getImageUrl(prod.trailer.video || ''); 336 const posterUrl = mediaService.getImageUrl(prod.trailer.poster || ''); 337 const embedUrl = mediaService.getEmbedUrl(prod.trailer.url || ''); 338 trailer.innerHTML = prod.trailer.is_external ? `<div class="flex flex-col items-center space-y-4"> 339 <div class="aspect-video rounded-2xl overflow-hidden border border-slate-100 shadow-sm hover:shadow-md hover:border-cdl-blue transition cursor-pointer group bg-slate-200 w-full h-full"> 340 <iframe class="w-full h-full" src="${embedUrl}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture;
340 web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> 341 </div> 342 <div class="w-full text-slate-900 mt-3 px-1 flex items-center gap-3 justify-center"> 343 <p class="font-bold uppercase text-lg tracking-widest"> 344 ${prod?.trailer?.caption || ''} 345 </p> 346 </div> 347 </div>` : 348 `<div class="flex flex-col items-center space-y-4"> 349 <div class="aspect-video w-full h-full rounded-2xl overflow-hidden border border-slate-100 shadow-sm hover:shadow-md hover:border-cdl-blue transition cursor-pointer group bg-slate-200"> 350 <video controls poster="${posterUrl}" class="w-full h-full"> 351 <source src="${videoUrl}" type="video/mp4"> 352 </video> 353 </div> 354 <div class="w-full text-slate-900 mt-3 px-1 flex items-center gap-3 justify-center"> 355 <p class="font-bold text-lg"> 356 ${prod?.trailer?.video?.caption || ''} 357 </p> 358 </div> 359 </div>` 360 361 const galleryPanel = document.getElementById('panel-gallery'); 362 const galleryBtn = document.getElementById('tab-gallery'); 363 if(!prod.gallery || prod.gallery.length == 0 && galleryPanel && galleryBtn) { 364 galleryPanel.remove(); 365 galleryBtn.remove(); 366 } 367 368 const videos = document.getElementById('film-videos-list'); 369 const videosPanel = document.getElementById('panel-videos'); 370 const videosBtn = document.getElementById('tab-videos'); 371 if(!prod.videos || prod.videos.length == 0 && videosPanel && videosBtn) { 372 videosPanel.remove(); 373 videosBtn.remove(); 374 } 375 prod.videos.forEach(video => { 376 const videoUrl = mediaService.getImageUrl(video.video || ''); 377 const posterUrl = mediaService.getImageUrl(video.poster || ''); 378 const embedUrl = mediaService.getEmbedUrl(video.url || ''); 379 if(!embedUrl.includes('youtube') && video.is_external) return; 380 381 videos.innerHTML += video.is_external ? `<div class="flex flex-col items-center space-y-4"> 382 <div class="aspect-video w-full h-full rounded-2xl overflow-hidden border border-slate-100 shadow-sm hover:shadow-md hover:border-cdl-blue transition cursor-pointer group bg-slate-200"> 383 <iframe class="w-full h-full" src="${embedUrl}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> 384 </div> 385 <div class="w-full text-slate-900 mt-3 px-1 flex items-center gap-3"> 386 <p class="font-bold text-sm"> 387 ${video?.caption || ''} 388 </p> 389 </div> 390 </div>` : 391 `<div class="flex flex-col items-center space-y-4"> 392 <div class="aspect-video rounded-2xl overflow-hidden border border-slate-100 shadow-sm hover:shadow-md hover:border-cdl-blue transition cursor-pointer group bg-slate-200 w-full h-full"> 393 <video controls poster="${posterUrl}" class="w-full h-full"> 394 <source src="${videoUrl}" type="video/mp4"> 395 </video> 396 </div> 397 <div class="w-full text-slate-900 mt-3 px-1 flex items-center gap-3 justify-start"> 398 <p class="font-bold text-sm"> 399 ${video?.video?.caption || ''} 400 </p> 401 </div> 402 </div>` 403 }) 404 405 //PRIMER ELEMENTO ACTIVO DEL TAB-PANEL 406 const tabList = document.querySelectorAll('button[role="tab"]'); 407 const panelList = document.querySelectorAll('div[role="tabpanel"]'); 408 if(tabList.length > 0 && panelList.length > 0) { 409 tabList[0].classList.add('active'); 410 tabList[0].classList.remove('hidden'); 411 panelList[0].classList.remove('hidden'); 412 } 413 414 //TRAS ESPERAR A QUE SE PINTEN SOLO LOS TABS QUE TENGAN CONTENIDO SE ELIMINA LA CLASE HIDDEN DEL CONTENEDOR 415 const tabsContainer = document.querySelector('div[data-tabs-container]'); 416 tabsContainer.classList.remove('hidden') 417 418 if (window.lucide) lucide.createIcons(); 419 } catch (error) { 420 container.innerHTML = `<div class="text-center py-24"><h2 class="text-2xl font-bold text-slate-400">${t('actions.not_found')}</h2></div>`; 421 } 422 }, 0); 423 424 return ` 425 <div class="w-full bg-white"> 426 <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto"> 427 <a href="/filmography" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-12 text-xs font-bold uppercase tracking-widest transition"> 428 <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')} 429 </a> 430 431 <div id="filmography-detail-content">
432 <div class="grid col-span-12 grid-cols-1 lg:grid-cols-12 gap-12 items-start"> 433 <div class="lg:col-span-4"> 434 <div id="poster-container" class="aspect-[2/3] rounded-[2rem] overflow-hidden shadow-2xl border-4 border-white"> 435 436 </div> 437 </div> 438 439 <div class="lg:col-span-8 space-y-8"> 440 <div id="title-container"> 441 <span class="inline-block bg-cdl-gold/10 text-cdl-gold text-[10px] font-black uppercase tracking-[0.2em] px-3 py-1 rounded-md mb-4 border border-cdl-gold/20">${t('why-cv.filmography.detail.production_label') || 'Producción Destacada'}</span> 442 443 </div> 444 445 <div id="synopsis-container" class="prose prose-lg text-slate-600 max-w-none"> 446 <h3 class="text-slate-900 font-bold uppercase text-sm tracking-widest border-b border-slate-100 pb-2 mb-4">${t('why-cv.filmography.detail.synopsis') || 'Sobre la producción'}</h3> 447 448 </div> 449 450 <div class="grid grid-cols-1 md:grid-cols-2 gap-6 pt-6"> 451 <div id="locations-container"> 452 453 </div> 454 455 <div class="p-6 bg-slate-50 rounded-3xl border border-slate-100 flex flex-col justify-center"> 456 <span class="block text-xs font-bold text-slate-500 mb-4">${t('why-cv.filmography.detail.cta_text') || '¿Quieres rodar tu próximo proyecto con nosotros?'}</span> 457 <a href="/contact" class="bg-cdl-blue text-white text-center py-3 rounded-xl font-bold uppercase text-xs tracking-widest hover:bg-slate-900 transition shadow-lg">${t('actions.contact')}</a> 458 </div> 459 </div> 460 461 </div> 462 </div> 463 <div> 464 <div data-tabs-container class="hidden bg-white py-8 md:py-12 rounded-[3rem] border border-slate-100"> 465 <div class="flex border-b border-slate-100 mb-12 overflow-x-auto"> 466 <nav role="tablist" class="flex space-x-12 min-w-max px-4"> 467 <button role="tab" id="tab-trailer" aria-controls="panel-trailer" aria-selected="true" class="tab-button active py-6 font-black uppercase tracking-widest text-xs">${'Trailer'}</button> 468 <button role="tab" id="tab-videos" aria-controls="panel-videos" aria-selected="false" class="tab-button py-6 font-black uppercase tracking-widest text-xs">${'Videos de Producción'}</button> 469 <button role="tab" id="tab-gallery" aria-controls="panel-gallery" aria-selected="false" class="tab-button py-6 font-black uppercase tracking-widest text-xs">${t('plato_detail.tab_gallery') || 'GalerÃa'}</button> 470 </nav> 471 </div> 472 <div> 473 <div role="tabpanel" id="panel-trailer" class="animate-fade-in-up"> 474 <div id="film-trailer-grid" class="grid grid-cols-1 gap-4"> 475 476 </div> 477 </div> 478 479 480 <div role="tabpanel" id="panel-videos" class="hidden animate-fade-in-up"> 481 <div id="film-videos-list" class="grid grid-cols-1 md:grid-cols-3 gap-4"> 482 483 </div> 484 </div> 485 486 <div role="tabpanel" id="panel-gallery" class="hidden animate-fade-in-up space-y-12"> 487 <div class="space-y-6"> 488 <div id="carousel"></div> 489 <div id="film-gallery-grid" class="grid grid-cols-2 md:grid-cols-4 gap-4"> 490 491 </div> 492 </div> 493 </div> 494 </div> 495 </div> 496 </div> 497 </div> 498 </div> 499 <div id="carousel-popup"></div> 500 </div>`; 501} 502
503// pages/WhyCVPage.js 504 505// ... (imports y variables existentes) 506 507/** 508 * Nueva función auxiliar para renderizar los controles de paginación 509 */ 510function renderPagination(pagination, baseUrl) { 511 if (!pagination || pagination.pageCount <= 1) return ''; 512 513 const { page, pageCount } = pagination; 514 let buttons = ''; 515 516 // Botón Anterior 517 buttons += ` 518 <button ${page > 1 ? `onclick="window.navigateTo('${baseUrl}/${page - 1}')"` : 'disabled'} 519 class="w-10 h-10 rounded-full border border-slate-200 flex items-center justify-center text-slate-400 hover:bg-slate-50 transition disabled:opacity-30"> 520 <i data-lucide="chevron-left" class="w-4 h-4"></i> 521 </button> 522 `; 523 524 // Números de página 525 for (let i = 1; i <= pageCount; i++) { 526 buttons += ` 527 <button onclick="window.navigateTo('${baseUrl}/${i}')" 528 class="w-10 h-10 rounded-full ${i === page ? 'bg-cdl-blue text-white font-bold shadow-md' : 'border border-slate-200 text-slate-600 hover:bg-slate-50'} transition"> 529 ${i} 530 </button> 531 `; 532 } 533 534 // Botón Siguiente 535 buttons += `
536 <button ${page < pageCount ? `onclick="window.navigateTo('${baseUrl}/${page + 1}')"` : 'disabled'} 537 class="w-10 h-10 rounded-full border border-slate-200 flex items-center justify-center text-slate-400 hover:bg-slate-50 transition disabled:opacity-30"> 538 <i data-lucide="chevron-right" class="w-4 h-4"></i> 539 </button> 540 `; 541 542 return ` 543 <div class="flex justify-center items-center space-x-2 mt-12 pt-8 border-t border-slate-100"> 544 ${buttons} 545 </div> 546 `; 547} 548 549/** 550 * Versión actualizada con soporte para paginación 551 */ 552function renderSuccessStoriesPage() { 553 const t = (key) => i18n.t(key); 554 555 setTimeout(async () => { 556 const pageData = await contentService.getContent('filmography'); 557 if (pageData?.title_1) document.getElementById('page-title')?.replaceChildren(pageData.title_1); 558 if (pageData?.title_2) document.getElementById('page-title-highlight')?.replaceChildren(pageData.title_2); 559 if (pageData?.desc) document.getElementById('page-desc')?.replaceChildren(pageData.desc); 560 }, 0); 561 562 setTimeout(async () => { 563 const container = document.getElementById('view-list-container'); 564 const paginationContainer = document.getElementById('pagination-container'); 565 566 try { 567 // 1. Pedimos más resultados y los ordenamos por año descendente. 568 // Ajusta 'year:desc' si tu campo en la base de datos se llama distinto (ej. releaseDate:desc) 569 const query = `pagination[pageSize]=100&sort[0]=year:desc`; 570 const response = await filmographyService.getAllProductions(query); 571 572 const productions = response.data || response; 573 574 // 2. Agrupar las producciones por año 575 const groupedByYear = productions.reduce((acc, prod) => { 576 const year = prod.year || 'N/A'; // Cambia prod.year por el nombre de tu variable de año 577 if (!acc[year]) acc[year] = []; 578 acc[year].push(prod); 579 return acc; 580 }, {}); 581 582 // 3. Obtener el año actual para saber qué pestaña abrir 583 const currentYear = new Date().getFullYear().toString(); 584 585 // 4. Ordenar las claves (años) de mayor a menor 586 const sortedYears = Object.keys(groupedByYear).sort((a, b) => { 587 if (a === 'N/A') return 1; 588 if (b === 'N/A') return -1; 589 return parseInt(b) - parseInt(a); 590 }); 591 592 // 5. Generar el HTML de los desplegables (Acordeón nativo de HTML5) 593 let html = ''; 594 sortedYears.forEach(year => { 595 // Si el año coincide con el actual, le añadimos el atributo 'open' 596 const isOpen = year === currentYear ? 'open' : ''; 597 598 html += ` 599 <details class="group mb-4 bg-white rounded-[2rem] border border-slate-100 shadow-sm overflow-hidden" ${isOpen}> 600 <summary class="cursor-pointer p-6 px-8 bg-slate-50 hover:bg-slate-100 transition-colors font-black text-2xl text-cdl-blue uppercase tracking-widest flex justify-between items-center list-none outline-none [&::-webkit-details-marker]:hidden select-none"> 601 <span class="flex items-center gap-3"> 602 ${year} 603 <span class="text-xs font-bold text-slate-400 bg-white px-3 py-1 rounded-full border border-slate-200 shadow-sm">${groupedByYear[year].length}</span> 604 </span> 605 <span class="text-slate-400 group-open:rotate-180 transition-transform duration-300"> 606 <i data-lucide="chevron-down" class="w-6 h-6"></i> 607 </span> 608 </summary> 609 <div class="p-8 grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-6 bg-white border-t border-slate-100"> 610 ${groupedByYear[year].map(p => renderSuccesStoriesCard(p)).join('')} 611 </div> 612 </details>`; 613 }); 614 615 container.innerHTML = html; 616 617 // Ocultamos el contenedor de paginación ya que ahora mostramos todo en acordeones 618 if (paginationContainer) { 619 paginationContainer.innerHTML = ''; 620 } 621 622 if (window.lucide) lucide.createIcons(); 623 } catch (error) { 624 console.error("Error cargando filmografÃa agrupada:", error); 625 container.innerHTML = `<p class="col-span-full text-center text-slate-400">${t('actions.error_loading') || 'Error al cargar datos'}</p>`; 626 } 627 }, 0); 628 629 return ` 630 <div class="w-full bg-white"> 631 <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto text-center"> 632 <a href="/" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-8 text-xs font-bold uppercase tracking-widest transition"> 633 <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')} 634 </a> 635 <h1 class="text-5xl md:text-6xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none">
636 <span id="page-title">${t('why-cv.filmography.title')}</span><br><span id="page-title-highlight" class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold to-yellow-600"></span> 637 </h1> 638 <p id="page-desc" class="text-lg text-slate-500 max-w-3xl mx-auto leading-relaxed"> 639 ${t('why-cv.filmography.desc')} 640 </p> 641 </div> 642 643 <div class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-24"> 644 <div id="view-list-container" class="flex flex-col"> 645 <div class="flex justify-center py-24"><div class="animate-spin rounded-full h-12 w-12 border-4 border-slate-200 border-t-cdl-gold"></div></div> 646 </div> 647 648 <div id="pagination-container"></div> 649 </div> 650 </div> 651 `; 652} 653// --- 4. SUBPÃGINA 4.3: GUÃA DE PRODUCCIÃN --- 654function renderProductionGuidePage() { 655 setTimeout(async() => { 656 const response = await contentService.getPageContent('why-cv'); 657 const content = document.getElementById('content'); 658 if(response && content){ 659 const stepCtas = [ 660 { 661 href: '/contact', 662 label: t(`${guide}.cta_contact`), 663 icon: 'message-circle' 664 }, 665 { 666 href: '/locations', 667 label: t(`${guide}.cta_locations`), 668 icon: 'search' 669 }, 670 { 671 href: '/hub/film-offices', 672 label: t(`${guide}.cta_film_offices`), 673 icon: 'building-2' 674 }, 675 { 676 href: '/hub', 677 label: t(`${guide}.cta_talent_companies`), 678 icon: 'users' 679 } 680 ]; 681 682 const stepCards = response.production_guide_4steps.map((step, index) => { 683 const card = { 684 ...step, 685 ...(index === 2 ? { 686 title: t(`${guide}.film_offices_title`), 687 desc: t(`${guide}.film_offices_desc`), 688 lucide: 'building-2' 689 } : {}) 690 }; 691 const cta = stepCtas[index]; 692 693 return `<div class="relative p-6 rounded-2xl bg-white border border-slate-100 shadow-sm text-center group hover:shadow-lg transition flex flex-col"> 694 <div class="text-6xl font-black text-slate-100 absolute top-2 right-4 group-hover:text-blue-50 transition-colors">${card.step}</div> 695 <div class="relative z-10 flex h-full flex-col"> 696 <div class="w-12 h-12 bg-blue-50 rounded-full flex items-center justify-center mx-auto mb-4 text-cdl-blue font-bold"><i data-lucide="${card.lucide}" class="w-6 h-6"></i></div> 697 <h3 class="text-xl font-bold text-slate-900 mb-2">${card.title}</h3> 698 <p class="text-sm text-slate-500 mb-6 flex-1">${card.desc}</p> 699 <a href="${cta.href}" class="mt-auto inline-flex items-center justify-center gap-2 bg-cdl-blue hover:bg-slate-900 text-white font-bold py-3 px-5 rounded-xl transition shadow-lg text-xs uppercase tracking-widest"> 700 <i data-lucide="${cta.icon}" class="w-4 h-4"></i>${cta.label} 701 </a> 702 </div> 703 </div>`; 704 }).join(''); 705 706 content.innerHTML = `<section class="mb-16 bg-blue-50 px-8 py-10 rounded-[2.5rem] border border-blue-100 text-center"> 707 <p class="text-lg text-blue-800 max-w-3xl mx-auto leading-relaxed">${response.production_guide_desc}</p> 708 </section> 709 710 <section class="mb-20"> 711 <div class="grid grid-cols-1 md:grid-cols-4 gap-8"> 712 ${stepCards} 713 714 </div> 715 </section> 716 717 ${ 718 response.downloadable_resources.filter(r => r.url).length != 0 ? `<section> 719 <h2 class="text-2xl font-black text-slate-900 mb-8 uppercase tracking-tight">${t(`${guide}.downloadable_resources`)}</h2> 720 <div class="grid grid-cols-1 md:grid-cols-3 gap-6"> 721 ${response.downloadable_resources.filter(r => r.url).map(r => `<a href="${AppConfig.API_BASE_URL}${r.url}" target="_blank" class="flex items-center p-6 bg-slate-50 rounded-2xl border border-slate-200 hover:bg-white hover:shadow-md transition group"> 722 <div class="mr-4 text-${r.icon_color.includes('cdl') ? r.icon_color : r.icon_color.concat("","-500")}"><i data-lucide="${r.icon}" class="w-8 h-8"></i></div> 723 <div> 724 <h3 class="font-bold text-slate-900 group-hover:text-cdl-blue transition">${r.filename}</h3>
725 <span class="text-xs font-bold text-slate-400 uppercase tracking-wider">${r.extension} ⢠${r.size}</span> 726 </div> 727 <i data-lucide="download" class="w-5 h-5 ml-auto text-slate-300 group-hover:text-cdl-blue transition"></i> 728 </a>`).join('')} 729 </div> 730 </section>` 731 : 732 ''}` 733 } 734 735 lucide.createIcons(); 736 }, 0) 737 738 return ` 739 <div class="w-full bg-white"> 740 741 <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto text-center"> 742 <a href="/why-cv" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-8 text-xs font-bold uppercase tracking-widest transition"> 743 <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')} 744 </a> 745 <h1 class="text-5xl md:text-6xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none"> 746 ${t(`${guide}.title_1`)} <span class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold">${t(`${guide}.title_2`)}</span> 747 </h1> 748 <p class="text-lg text-slate-500 max-w-3xl mx-auto leading-relaxed"> 749 ${t(`${guide}.subtitle`)} 750 </p> 751 </div> 752 753 <div id="content" class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-24"> 754 755 </div> 756 </div> 757 `; 758} 759 760// --- 5. SUBPÃGINA 4.4: CONECTIVIDAD --- 761function renderConnectivityPage() { 762 const t = (key) => i18n.t(key); 763 setTimeout(async () => { 764 const response = await contentService.getPageContent('why-cv'); 765 const videoUrl = mediaService.getImageUrl(response?.conectivity_video?.url || ''); 766 const videoPoster = mediaService.getImageUrl(response?.conectivity_video?.poster || ''); 767 768 const content = document.getElementById('content'); 769 if(content && response) { 770 content.innerHTML = `<div class="relative h-[500px] rounded-[2.5rem] overflow-hidden shadow-2xl mb-16 border-4 border-slate-100 group"> 771 <video controls class="absolute inset-0 w-full h-full object-cover" poster="${videoPoster}"> 772 <source src="${videoUrl}" type="video/mp4"> 773 ${t('why-cv.connectivity.video_error')} 774 </video> 775 776 <div class="absolute inset-0 bg-gradient-to-br from-slate-800 to-black opacity-30 group-hover:opacity-10 transition-opacity duration-500 pointer-events-none"></div> 777 </div> 778 779 <div class="grid grid-cols-1 md:grid-cols-3 gap-8 mt-8 text-center"> 780 781 ${response.conectivity_cards.map(c => `<div class="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-lg hover:shadow-xl hover:-translate-y-1 transition duration-300 group"> 782 <div class="w-16 h-16 bg-blue-50 rounded-2xl flex items-center justify-center mx-auto mb-6 group-hover:bg-cdl-blue transition-colors"> 783 <i data-lucide="${c.lucide}" class="w-8 h-8 text-cdl-blue group-hover:text-white transition-colors"></i> 784 </div> 785 <h3 class="font-black text-xl text-slate-900 mb-2 uppercase tracking-tight">${c.title}</h3> 786 <p class="text-slate-500 text-sm leading-relaxed">${c.desc}</p> 787 </div>`).join('')} 788 </div>` 789 } 790 791 lucide.createIcons(); 792 }, 0) 793 return ` 794 <div class="w-full bg-white"> 795 796 <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto text-center"> 797 <a href="/why-cv" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-8 text-xs font-bold uppercase tracking-widest transition"> 798 <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')} 799 </a> 800 <h1 class="text-5xl md:text-6xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none"> 801 ${t(`${connectivity}.title_1`)} <span class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold to-yellow-600">${t(`${connectivity}.title_2`)}</span> 802 </h1> 803 <p class="text-lg text-slate-500 max-w-3xl mx-auto leading-relaxed"> 804 ${t(`${connectivity}.subtitle_1`)} 805 </p> 806 </div> 807
808 <div id="content" class="container mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-24"> 809 810 </div> 811 </div>`; 812} 813 814// --- 6. SUBPÃGINA 4.5: CONTACTO INVERSORES --- 815function renderInvestorsContactPage() { 816 817 // Inicializamos los formularios una vez inyectado el HTML 818 setTimeout(() => { 819 leadService.initLeadForm('investors-form', 'investors', 'success-investors'); 820 if (window.lucide) lucide.createIcons(); 821 }, 0); 822 823 return ` 824 <div class="w-full bg-white"> 825 826 <div class="pt-32 pb-12 px-6 max-w-3xl mx-auto text-center"> 827 <a href="/why-cv" class="inline-flex items-center text-slate-400 hover:text-cdl-blue mb-8 text-xs font-bold uppercase tracking-widest transition"> 828 <i data-lucide="arrow-left" class="w-4 h-4 mr-2"></i> ${t('actions.nav_back')} 829 </a> 830 <h1 class="text-4xl md:text-5xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none"> 831 ${t(`${investors}.title`)} 832 </h1> 833 </div> 834 835 <div class="container mx-auto max-w-3xl px-4 sm:px-6 lg:px-8 pb-24"> 836 <div class="bg-white p-10 rounded-[2.5rem] shadow-2xl border border-slate-100"> 837 <form id="investors-form" class="space-y-6"> 838 <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> 839 <div> 840 <label class="block text-xs font-bold text-slate-500 uppercase mb-2 ml-1">${t('forms.name_label')} <span class="text-red-500">*</span></label> 841 <input name="name" type="text" required class="w-full p-4 bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:border-cdl-blue transition"> 842 </div> 843 <div> 844 <label class="block text-xs font-bold text-slate-500 uppercase mb-2 ml-1">${t(`${investors}.form_company_label`)}</label> 845 <input name="company" type="text" class="w-full p-4 bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:border-cdl-blue transition"> 846 </div> 847 </div> 848 849 <div> 850 <label class="block text-xs font-bold text-slate-500 uppercase mb-2 ml-1">${t(`${investors}.form_email_label`)} <span class="text-red-500">*</span></label> 851 <input name="email" type="email" required class="w-full p-4 bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:border-cdl-blue transition"> 852 </div> 853 854 <div> 855 <label class="block text-xs font-bold text-slate-500 uppercase mb-2 ml-1">${t(`${investors}.form_interest_label`)} <span class="text-red-500">*</span></label> 856 <textarea name="message" required rows="4" class="w-full p-4 bg-slate-50 border border-slate-200 rounded-xl focus:outline-none focus:border-cdl-blue transition"></textarea> 857 </div> 858 859 <div class="pt-4"> 860 <button type="submit" class="w-full bg-slate-900 hover:bg-cdl-blue text-white font-bold py-4 px-8 rounded-xl shadow-lg transition duration-300 flex items-center justify-center gap-2"> 861 <i data-lucide="send" class="w-4 h-4"></i> ${t(`${investors}.submit_btn`)} 862 </button> 863 </div> 864 </form> 865 866 <div id="success-investors" class="hidden text-center py-8"> 867 <div class="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4"> 868 <i data-lucide="check" class="w-8 h-8 text-green-600"></i> 869 </div> 870 <h3 class="text-xl font-bold text-slate-800 mb-2">${t(`${investors}.success_title`)}</h3> 871 <p class="text-slate-500">${t(`${investors}.success_msg`)}</p> 872 </div> 873 </div> 874 </div> 875 </div>`; 876} 877 878// --- 7. SUBPÃGINA 4.6: ATRATIVOS INCENTIVOS --- 879export function renderIncentivesPage() { 880 const t = (key) => i18n.t(key); 881 882 setTimeout(async () => { 883 const container = document.getElementById('incentives-container'); 884 try { 885 const data = await incentivesService.getCurrentIncentives(); 886 887 if (container && data) {
888 const defaultDesc = `<p>${t('why-cv.incentives.default_text') || 'La Comunitat Valenciana ofrece uno de los marcos fiscales más atractivos de Europa.'}</p>`; 889 const descriptionHtml = data.description ? data.description : defaultDesc; 890 891 container.innerHTML = ` 892 <div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-16"> 893 894 <div class="bg-gradient-to-br from-slate-900 to-cdl-blue p-10 md:p-12 rounded-[3rem] shadow-2xl text-white relative overflow-hidden group hover:scale-[1.02] transition-transform duration-500"> 895 <div class="absolute top-0 right-0 w-48 h-48 bg-cdl-gold/20 rounded-full blur-3xl -translate-y-16 translate-x-16"></div> 896 <div class="relative z-10 flex flex-col h-full"> 897 <div class="flex justify-between items-start mb-8"> 898 <div class="w-14 h-14 bg-white/10 backdrop-blur-md rounded-2xl flex items-center justify-center border border-white/20 shadow-sm"> 899 <i data-lucide="globe" class="w-7 h-7 text-cdl-gold"></i> 900 </div> 901 <span class="bg-cdl-gold/20 text-cdl-gold text-[10px] font-black uppercase tracking-[0.2em] px-4 py-2 rounded-full border border-cdl-gold/30"> 902 ${data.national?.title || t('why-cv.incentives.national')} 903 </span> 904 </div> 905 <div class="mt-auto"> 906 <div class="text-8xl md:text-9xl font-black text-white mb-2 tracking-tighter leading-none text-shadow-sm"> 907 ${data.national?.percentage || '0'}% 908 </div> 909 <h3 class="text-xl font-bold text-slate-500 uppercase tracking-widest"> 910 ${data.national?.label || t('why-cv.incentives.tax_rebate')} 911 </h3> 912 </div> 913 </div> 914 </div> 915 916 <div class="bg-white p-10 md:p-12 rounded-[3rem] shadow-xl border border-slate-100 relative overflow-hidden group hover:scale-[1.02] transition-transform duration-500"> 917 <div class="absolute top-0 right-0 w-48 h-48 bg-cyan-500/10 rounded-full blur-3xl -translate-y-16 translate-x-16"></div> 918 <div class="relative z-10 flex flex-col h-full"> 919 <div class="flex justify-between items-start mb-8"> 920 <div class="w-14 h-14 bg-blue-50 rounded-2xl flex items-center justify-center border border-blue-100 shadow-sm"> 921 <i data-lucide="map" class="w-7 h-7 text-cdl-blue"></i> 922 </div> 923 <span class="bg-blue-50 text-cdl-blue text-[10px] font-black uppercase tracking-[0.2em] px-4 py-2 rounded-full border border-blue-100"> 924 ${data.regional?.title || t('why-cv.incentives.regional')} 925 </span> 926 </div> 927 <div class="mt-auto"> 928 <div class="text-8xl md:text-9xl font-black text-cdl-blue mb-2 tracking-tighter leading-none"> 929 ${data.regional?.percentage || '0'}% 930 </div> 931 <h3 class="text-xl font-bold text-slate-500 uppercase tracking-widest"> 932 ${data.regional?.label || t('why-cv.incentives.grant')} 933 </h3> 934 </div> 935 </div> 936 </div> 937 938 </div> 939 940 <div class="bg-slate-50 p-10 md:p-16 rounded-[3rem] border border-slate-100 shadow-inner"> 941 <div class="flex items-center gap-4 mb-8 border-b border-slate-200 pb-6"> 942 <div class="w-10 h-10 bg-white rounded-full flex items-center justify-center shadow-sm"> 943 <i data-lucide="info" class="w-5 h-5 text-cdl-gold"></i> 944 </div> 945 <h2 class="text-2xl font-black text-slate-800 uppercase tracking-tight"> 946 ${t('why-cv.incentives.details_title') || 'Condiciones y Detalles'} 947 </h2> 948 </div> 949 <div class="prose prose-lg prose-slate text-slate-600 max-w-none"> 950 ${descriptionHtml} 951 </div> 952 </div> 953 954 <div class="mt-16 text-center"> 955 <a href="/contact" class="inline-flex items-center gap-3 bg-cdl-gold hover:bg-yellow-500 text-white font-black py-5 px-10 rounded-full shadow-xl shadow-amber-900/20 transition-all transform hover:scale-105 uppercase text-sm tracking-widest"> 956 <i data-lucide="calculator" class="w-5 h-5"></i> ${t('why-cv.incentives.cta') || 'Consultar mi Proyecto'} 957 </a> 958 ${data.lastUpdated ? ` 959 <p class="text-[10px] text-slate-400 font-bold uppercase tracking-[0.2em] mt-6"> 960 ${t('why-cv.incentives.last_update') || 'Ãltima actualización'}: ${data.lastUpdated} 961 </p>` : ''} 962 </div> 963 `; 964 } 965 if (window.lucide) lucide.createIcons(); 966 } catch (e) { 967 console.error("Error al renderizar incentivos:", e); 968 if (container) container.innerHTML = `<p class="text-center text-red-500 font-bold">${t('why-cv.incentives.not_found')}</p>`; 969 } 970 }, 0); 971 972 // ... [El HTML de retorno de Incentivos se mantiene igual] 973 return ` 974 <div class="w-full bg-white"> 975 <div class="pt-32 pb-12 px-6 max-w-7xl mx-auto text-center"> 976 <nav class="flex justify-center text-xs text-slate-400 mb-6 uppercase tracking-widest font-bold" aria-label="Breadcrumb"> 977 <ol class="list-none p-0 inline-flex items-center gap-2"> 978 <li><a href="/" class="hover:text-cdl-blue transition">${t('actions.home')}</a></li> 979 <li><span class="text-cdl-gold">/</span></li> 980 <li><a href="/why-cv" class="hover:text-cdl-blue transition">${t('nav.why')}</a></li> 981 <li><span class="text-cdl-gold">/</span></li> 982 <li><span class="text-cdl-blue">${t('why-cv.incentives.title')}</span></li> 983 </ol> 984 </nav> 985 <h1 class="text-5xl md:text-7xl font-black text-cdl-blue mb-6 uppercase tracking-tighter italic leading-none"> 986 ${t('why-cv.incentives.title_1') || 'Incentivos'} <span class="text-cdl-gold bg-clip-text bg-gradient-to-r from-cdl-gold to-yellow-600">${t('why-cv.incentives.title_2') || 'Fiscales'}</span> 987 </h1> 988 <p class="text-lg text-slate-500 max-w-3xl mx-auto leading-relaxed"> 989 ${t('why-cv.incentives.desc')} 990 </p> 991 </div> 992 993 <div id="incentives-container" class="container mx-auto max-w-5xl px-4 sm:px-6 lg:px-8 pb-32"> 994 <div class="flex flex-col items-center justify-center py-24 space-y-4"> 995 <div class="animate-spin rounded-full h-12 w-12 border-4 border-slate-200 border-t-cdl-gold"></div> 996 <p class="text-slate-400 font-medium animate-pulse">${t('actions.loader')}</p> 997 </div> 998 </div> 999 </div>`; 1000} 1001 1002export { 1003 renderWhyCVOverview, 1004 renderDataPage, 1005 renderSuccessStoriesPage, 1006 renderConnectivityPage, 1007 renderInvestorsContactPage, 1008 renderProductionGuidePage, 1009 renderFilmographyDetailPage 1010};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.