@@ -201,81 +201,131 @@ function renderXP() {
201201 ========================================================= */
202202let PROJECT_INDEX = 0 ;
203203
204- function getProjectItems ( ) {
205- const proj = STATE . dict ?. projects ;
206- if ( ! proj ) return [ ] ;
207-
204+ function getProjectItems ( proj ) {
208205 return [
209206 {
210207 title : proj . p1_title ,
211208 desc : proj . p1_desc ,
212- why : proj . p1_why ,
209+ why : proj . p1_why ,
210+ problem : proj . p1_problem ,
211+ solution : proj . p1_solution ,
212+ result : proj . p1_result ,
213213 link : "https://github.com/SebiGitHub/WEB-DE-PROTOCOLOS-HSJD" ,
214- tech : [ "PowerApps" , "SharePoint" , "VBScript" , "Access" , "Excel" ] ,
215- icon : "fa-solid fa-diagram-project" ,
216- thumbClass : "thumb-powerapps"
214+ iconClass : "fa-solid fa-diagram-project" ,
215+ thumbClass : "thumb-powerapps" ,
216+ tech : [
217+ { label :"PowerApps" , cls :"t-powerapps" } ,
218+ { label :"SharePoint" , cls :"t-powerapps" } ,
219+ { label :"Access/SQL" , cls :"t-data" } ,
220+ { label :"Excel" , cls :"t-excel" }
221+ ]
217222 } ,
218223 {
219224 title : proj . p2_title ,
220225 desc : proj . p2_desc ,
221- why : proj . p2_why ,
226+ why : proj . p2_why ,
227+ problem : proj . p2_problem ,
228+ solution : proj . p2_solution ,
229+ result : proj . p2_result ,
222230 link : "https://github.com/SebiGitHub/AvaloniaCatalogoWinForms" ,
223- tech : [ "Avalonia" , "WinForms" , "Visual Studio" ] ,
224- icon : "fa-solid fa-cubes-stacked" ,
225- thumbClass : "thumb-desktop"
231+ iconClass : "fa-solid fa-desktop" ,
232+ thumbClass : "thumb-desktop" ,
233+ tech : [
234+ { label :"C# / .NET" , cls :"t-dotnet" }
235+ ]
226236 } ,
227237 {
228238 title : proj . p3_title ,
229239 desc : proj . p3_desc ,
230- why : proj . p3_why ,
240+ why : proj . p3_why ,
241+ problem : proj . p3_problem ,
242+ solution : proj . p3_solution ,
243+ result : proj . p3_result ,
231244 link : "https://github.com/SebiGitHub/Agenda" ,
232- tech : [ "Android" , "Kotlin" , "SQLite/Room" ] ,
233- icon : "fa-solid fa-calendar-days" ,
234- thumbClass : "thumb-android"
245+ iconClass : "fa-solid fa-calendar-days" ,
246+ thumbClass : "thumb-android" ,
247+ tech : [
248+ { label :"Kotlin" , cls :"t-kotlin" } ,
249+ { label :"Firebase" , cls :"t-data" }
250+ ]
235251 } ,
236252 {
237253 title : proj . p4_title ,
238254 desc : proj . p4_desc ,
239- why : proj . p4_why ,
255+ why : proj . p4_why ,
256+ problem : proj . p4_problem ,
257+ solution : proj . p4_solution ,
258+ result : proj . p4_result ,
240259 link : "https://github.com/SebiGitHub/Realtime-Collaborative-Bingo-Web-App" ,
241- tech : [ "Web" , "Realtime" , "Rooms" ] ,
242- icon : "fa-solid fa-users" ,
243- thumbClass : "thumb-realtime"
260+ iconClass : "fa-solid fa-users" ,
261+ thumbClass : "thumb-realtime" ,
262+ tech : [
263+ { label :"Realtime" , cls :"t-soft-comm" }
264+ ]
244265 } ,
245266 {
246267 title : proj . p5_title ,
247268 desc : proj . p5_desc ,
248- why : proj . p5_why ,
269+ why : proj . p5_why ,
270+ problem : proj . p5_problem ,
271+ solution : proj . p5_solution ,
272+ result : proj . p5_result ,
249273 link : "https://github.com/SebiGitHub/Selenium-end-to-end" ,
250- tech : [ "Selenium" , "E2E" , "Testing" ] ,
251- icon : "fa-solid fa-vial-circle-check" ,
252- thumbClass : "thumb-testing"
274+ iconClass : "fa-solid fa-vial" ,
275+ thumbClass : "thumb-testing" ,
276+ tech : [
277+ { label :"Selenium" , cls :"t-soft-org" } ,
278+ { label :"JUnit5" , cls :"t-java" } ,
279+ { label :"CI" , cls :"t-github" }
280+ ]
253281 } ,
254282 {
255283 title : proj . p6_title ,
256284 desc : proj . p6_desc ,
257- why : proj . p6_why ,
258- link : "https://github.com/SebiGitHub/Corrutinas" ,
259- tech : [ "Kotlin" , "Labs" , "Android" ] ,
260- icon : "fa-solid fa-flask" ,
285+ why : proj . p6_why ,
286+ problem : proj . p6_problem ,
287+ solution : proj . p6_solution ,
288+ result : proj . p6_result ,
289+ link : "#https://github.com/SebiGitHub/Corrutinas" , // Kotlin Labs no es un repo único
290+ iconClass : "fa-solid fa-flask" ,
261291 thumbClass : "thumb-labs" ,
292+ tech : [
293+ { label :"Kotlin" , cls :"t-kotlin" } ,
294+ { label :"Coroutines" , cls :"t-kotlin" } ,
295+ { label :"Persistence" , cls :"t-data" }
296+ ] ,
262297 cases : proj . p6_cases || [ ]
263298 }
264299 ] ;
265300}
266301
302+
267303function clampIndex ( i , len ) {
268304 if ( len <= 0 ) return 0 ;
269305 return ( i + len ) % len ;
270306}
271307
272- function renderProjects ( ) {
308+ function renderProjects ( ) {
273309 const stage = document . getElementById ( "project-stage" ) ;
274- const dots = document . getElementById ( "project-dots" ) ;
310+ const dots = document . getElementById ( "project-dots" ) ;
275311 if ( ! stage || ! dots ) return ;
276312
277- const items = getProjectItems ( ) ;
278- if ( items . length === 0 ) {
313+ const proj = STATE . dict ?. projects ;
314+ if ( ! proj ) {
315+ stage . innerHTML = "" ;
316+ dots . innerHTML = "" ;
317+ return ;
318+ }
319+
320+ // ✅ Labels seguros (si faltan en el JSON no rompe)
321+ const L = proj . psr_labels || proj . case_labels || {
322+ problem : "Problem" ,
323+ solution : "Solution" ,
324+ result : "Result"
325+ } ;
326+
327+ const items = getProjectItems ( proj ) ; // 👈 importante, ver B)
328+ if ( ! items . length ) {
279329 stage . innerHTML = "" ;
280330 dots . innerHTML = "" ;
281331 return ;
@@ -284,64 +334,42 @@ function renderProjects() {
284334 PROJECT_INDEX = clampIndex ( PROJECT_INDEX , items . length ) ;
285335 const p = items [ PROJECT_INDEX ] ;
286336
287- const labels = STATE . dict ?. projects ?. case_labels || { } ;
288- const casesHTML = ( p . cases && p . cases . length )
289- ? `
290- <div class="mini-grid" aria-label="Mini casos">
291- ${ p . cases . map ( c => `
292- <article class="mini-card">
293- <div class="mini-title">${ c . title } </div>
294- <div class="mini-row"><span class="mini-k">${ labels . problem || "Problema" } :</span> ${ c . problem } </div>
295- <div class="mini-row"><span class="mini-k">${ labels . solution || "Solución" } :</span> ${ c . solution } </div>
296- <div class="mini-row"><span class="mini-k">${ labels . signal || "Señal" } :</span> ${ c . signal } </div>
297- ${ c . link ? `<a class="mini-link" href="${ c . link } " target="_blank" rel="noopener noreferrer">Ver repo</a>` : `` }
298- </article>
299- ` ) . join ( "" ) }
300- </div>
301- `
302- : "" ;
303-
304337 stage . innerHTML = `
305338 <div class="card project-card">
306339 <div class="project-thumb ${ p . thumbClass || "" } ">
307- <i class="project-icon ${ p . icon } " aria-hidden="true"></i>
340+ <i class="${ p . iconClass } project-icon " aria-hidden="true"></i>
308341 </div>
309342
310343 <h3>${ p . title } </h3>
311344 <p>${ p . desc } </p>
312345
313- <div class="project-psr">
314- <div><strong>${ STATE . dict . projects . labels . problem } :</strong> ${ p . problem } </div>
315- <div><strong>${ STATE . dict . projects . labels . solution } :</strong> ${ p . solution } </div>
316- <div><strong>${ STATE . dict . projects . labels . result } :</strong> ${ p . result } </div>
317- </div>
318-
319346 <div class="tech">
320- ${ p . tech . map ( t => `<span>${ t } </span>` ) . join ( "" ) }
347+ ${ ( p . tech || [ ] ) . map ( t => `<span class=" ${ t . cls || "" } " >${ t . label } </span>` ) . join ( "" ) }
321348 </div>
322349
323- ${ casesHTML }
350+ <div class="project-psr">
351+ <div><strong>${ L . problem } :</strong> ${ p . problem || "—" } </div>
352+ <div><strong>${ L . solution } :</strong> ${ p . solution || "—" } </div>
353+ <div><strong>${ L . result } :</strong> ${ p . result || "—" } </div>
354+ </div>
324355
325356 <details>
326357 <summary>+ info</summary>
327- <p>${ p . why } </p>
358+ <p>${ p . why || "" } </p>
328359 </details>
329360
330361 <a href="${ p . link } " class="btn" target="_blank" rel="noopener noreferrer">GitHub</a>
331362 </div>
332363 ` ;
333364
334- dots . innerHTML = items
335- . map (
336- ( _ , idx ) => `
365+ dots . innerHTML = items . map ( ( _ , idx ) => `
337366 <button class="carousel-dot ${ idx === PROJECT_INDEX ? "active" : "" } "
338- aria-label="Ir al proyecto ${ idx + 1 } " data-idx="${ idx } "></button>
339- `
340- )
341- . join ( "" ) ;
367+ aria-label="Ir al proyecto ${ idx + 1 } "
368+ data-idx="${ idx } "></button>
369+ ` ) . join ( "" ) ;
342370
343- dots . querySelectorAll ( ".carousel-dot" ) . forEach ( ( b ) => {
344- b . addEventListener ( "click" , ( ) => {
371+ dots . querySelectorAll ( ".carousel-dot" ) . forEach ( b => {
372+ b . addEventListener ( "click" , ( ) => {
345373 PROJECT_INDEX = Number ( b . getAttribute ( "data-idx" ) ) ;
346374 renderProjects ( ) ;
347375 } ) ;
0 commit comments