@@ -540,6 +540,48 @@ function voteTitle(desc) {
540540 return ( desc || "Votazione" ) . replace ( / ^ ( [ A - Z ] + ) \b / , ( m ) => VOTE_KINDS [ m ] || m ) ;
541541}
542542
543+ /** L'atto votato: MOZ/RIS → scheda AIC; altrimenti DDL → scheda atto Camera. */
544+ function voteActRef ( v ) {
545+ const leg = ( ( v . legislature_uri || "" ) . match ( / r e p u b b l i c a _ ( \d + ) / ) || [ ] ) [ 1 ] || "19" ;
546+ const m = ( v . description || "" ) . match ( / ^ ( M O Z | R I S ) \s + ( \d + ) - ( \d + ) \b / ) ;
547+ if ( m ) {
548+ const id = `${ m [ 2 ] } /${ m [ 3 ] . padStart ( 5 , "0" ) } ` ;
549+ return { kind : "aic" , id, url : `https://aic.camera.it/aic/scheda.html?core=aic&numero=${ id } &ramo=CAMERA&leg=${ leg } ` } ;
550+ }
551+ if ( v . bill_number ) {
552+ return { kind : "bill" , id : v . bill_number , url : `https://www.camera.it/leg${ leg } /126?leg=${ leg } &idDocumento=${ v . bill_number } ` } ;
553+ }
554+ return null ;
555+ }
556+
557+ function titleCase ( s ) {
558+ return s . toLowerCase ( ) . replace ( / ( ^ | [ \s ' - ] ) \p{ L} / gu, ( c ) => c . toUpperCase ( ) ) ;
559+ }
560+
561+ /** Una sola query SPARQL risolve tutti gli atti AIC delle card: chi li ha presentati e su cosa. */
562+ async function enrichVoteActs ( leg ) {
563+ const els = [ ...document . querySelectorAll ( "[data-aic]" ) ] ;
564+ const ids = [ ...new Set ( els . map ( ( el ) => el . dataset . aic ) ) ] ;
565+ if ( ! ids . length ) return ;
566+ const query = `PREFIX ocd: <http://dati.camera.it/ocd/> PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> `
567+ + `SELECT DISTINCT ?id ?label (SUBSTR(STR(?d), 1, 700) AS ?excerpt) WHERE { `
568+ + `?s a ocd:aic ; ocd:rif_leg <http://dati.camera.it/ocd/legislatura.rdf/repubblica_${ leg } > ; dc:identifier ?id ; rdfs:label ?label . `
569+ + `OPTIONAL { ?s dc:description ?d } VALUES ?id { ${ ids . map ( ( i ) => `"${ i } "` ) . join ( " " ) } } }` ;
570+ const rows = await callTool ( "sparql" , { query, endpoint : "camera" } , 12 * HOUR ) ;
571+ const byId = new Map ( rows . map ( ( r ) => [ r . id , r ] ) ) ;
572+ for ( const el of els ) {
573+ const r = byId . get ( el . dataset . aic ) ;
574+ if ( ! r ) continue ;
575+ const lm = ( r . label || "" ) . match ( / p r e s e n t a t [ a o ] d a \s + ( [ ^ ( ] + ?) \s * \( ( [ ^ ) ] + ) \) / i) ;
576+ const who = lm ? `${ titleCase ( lm [ 1 ] . trim ( ) . split ( / \s + / ) [ 0 ] ) } (${ groupAcronym ( lm [ 2 ] ) } )` : "" ;
577+ const em = ( r . excerpt || "" ) . match ( / p r e m e s s o c h e : ? \s * (?: \d + \) \s * ) ? ( .{ 40 , 160 } ) / ) ;
578+ const cut = em ? em [ 1 ] . slice ( 0 , em [ 1 ] . lastIndexOf ( " " ) ) . replace ( / [ , ; : ] $ / , "" ) : "" ;
579+ const topic = cut ? `: «${ cut } …»` : "" ;
580+ el . textContent = who ? `di ${ who } ${ topic } ` : "" ;
581+ if ( el . textContent ) gsap . from ( el , { autoAlpha : 0 , duration : 0.6 } ) ;
582+ }
583+ }
584+
543585async function loadVotes ( ) {
544586 const from = new Date ( Date . now ( ) - 60 * 86400e3 ) . toISOString ( ) . slice ( 0 , 10 ) ;
545587 const rows = await callTool ( "votes" , { dateFrom : from , limit : 9 } , 1 * HOUR ) ;
@@ -552,22 +594,31 @@ async function loadVotes() {
552594 for ( const v of rows ) {
553595 const present = Math . max ( 1 , + v . present || ( + v . in_favour + + v . against + + v . abstentions ) ) ;
554596 const ok = v . approved === "true" ;
597+ const ref = voteActRef ( v ) ;
555598 const card = document . createElement ( "article" ) ;
556599 card . className = "vote-card" ;
557600 card . innerHTML = `
558601 <div class="vote-date"><span>${ fmtDate ( v . date ) } · Camera</span><span class="vote-badge ${ ok ? "ok" : "no" } ">${ ok ? "approvata" : "respinta" } </span></div>
559- <h3 class="vote-title"><a href="${ v . url } " target="_blank" rel="noopener">${ voteTitle ( v . description || v . title ) } </a></h3>
602+ <h3 class="vote-title">${ voteTitle ( v . description || v . title ) } </h3>
603+ ${ ref ?. kind === "aic" ? `<div class="vote-atto" data-aic="${ ref . id } "></div>` : "" }
560604 <div class="vote-bars">
561605 ${ [ [ "f" , "sì" , v . in_favour ] , [ "c" , "no" , v . against ] , [ "a" , "ast" , v . abstentions ] ] . map ( ( [ cls , lab , n ] ) => `
562606 <div class="vote-bar ${ cls } ">
563607 <span>${ lab } </span>
564608 <span class="bar-track"><span class="bar-fill" data-w="${ Math . round ( ( + n / present ) * 100 ) } "></span></span>
565609 <span class="bar-n">${ n || 0 } </span>
566610 </div>` ) . join ( "" ) }
611+ </div>
612+ <div class="vote-links mono">
613+ ${ ref ? `<a href="${ ref . url } " target="_blank" rel="noopener">testo dell'atto ↗</a><span class="sep">·</span>` : "" }
614+ <a href="${ v . url } " target="_blank" rel="noopener">scheda votazione ↗</a>
567615 </div>` ;
568616 grid . appendChild ( card ) ;
569617 }
570618
619+ const leg = ( ( rows [ 0 ] ?. legislature_uri || "" ) . match ( / r e p u b b l i c a _ ( \d + ) / ) || [ ] ) [ 1 ] || "19" ;
620+ enrichVoteActs ( leg ) . catch ( ( e ) => console . warn ( "[italianparliament] enrich" , e ) ) ;
621+
571622 gsap . from ( grid . children , {
572623 autoAlpha : 0 , y : 26 , stagger : 0.08 , duration : 0.7 , ease : "power2.out" ,
573624 scrollTrigger : { trigger : grid , start : "top 80%" } ,
0 commit comments