@@ -80,6 +80,101 @@ function getCategoryColor(category: string): string {
8080 return colors [ category . toUpperCase ( ) ] || '#3b82f6' ; // default blue-500
8181}
8282
83+ // Funzione per generare l'immagine OG della pagina Numeri
84+ export async function generateNumeriOGImage ( outputPath : string , options : OGImageOptions = { } ) : Promise < void > {
85+ const opts = { ...defaultOptions , ...options } ;
86+
87+ const title = 'Numeri' ;
88+ const subtitle = 'Statistiche e analisi delle iniziative referendarie' ;
89+ const titleFontSize = 80 ;
90+ const subtitleFontSize = 36 ;
91+ const titleY = opts . height / 2 - 50 ;
92+ const subtitleY = opts . height / 2 + 20 ;
93+ const brandText = "Un'idea di onData" ;
94+
95+ const svgTemplate = `
96+ <svg width="${ opts . width } " height="${ opts . height } " xmlns="http://www.w3.org/2000/svg">
97+ <!-- Sfondo gradiente -->
98+ <defs>
99+ <linearGradient id="numericBg" x1="0%" y1="0%" x2="100%" y2="100%">
100+ <stop offset="0%" style="stop-color:#1e3a8a;stop-opacity:1" />
101+ <stop offset="100%" style="stop-color:#3730a3;stop-opacity:1" />
102+ </linearGradient>
103+ </defs>
104+
105+ <rect width="100%" height="100%" fill="url(#numericBg)" />
106+
107+ <!-- Elementi grafici decorativi -->
108+ <!-- Barre istogramma stilizzate -->
109+ <rect x="80" y="120" width="200" height="20" rx="10" fill="rgba(16, 185, 129, 0.6)" />
110+ <rect x="80" y="160" width="300" height="20" rx="10" fill="rgba(59, 130, 246, 0.6)" />
111+ <rect x="80" y="200" width="150" height="20" rx="10" fill="rgba(139, 92, 246, 0.6)" />
112+
113+ <!-- Lollipop chart stilizzato -->
114+ <line x1="900" y1="400" x2="1100" y2="400" stroke="rgba(255,255,255,0.3)" stroke-width="2"/>
115+ <circle cx="1100" cy="400" r="12" fill="#10b981" opacity="0.8"/>
116+ <line x1="900" y1="450" x2="1050" y2="450" stroke="rgba(255,255,255,0.3)" stroke-width="2"/>
117+ <circle cx="1050" cy="450" r="12" fill="#3b82f6" opacity="0.8"/>
118+ <line x1="900" y1="500" x2="980" y2="500" stroke="rgba(255,255,255,0.3)" stroke-width="2"/>
119+ <circle cx="980" cy="500" r="12" fill="#8b5cf6" opacity="0.8"/>
120+
121+ <!-- Icone numeriche -->
122+ <text x="900" y="180" font-family="system-ui, -apple-system, sans-serif" font-size="72" font-weight="bold" fill="rgba(255,255,255,0.2)">123</text>
123+ <text x="950" y="250" font-family="system-ui, -apple-system, sans-serif" font-size="48" font-weight="bold" fill="rgba(255,255,255,0.15)">45%</text>
124+
125+ <!-- Titolo principale -->
126+ <text x="60" y="${ titleY } "
127+ font-family="system-ui, -apple-system, sans-serif"
128+ font-size="${ titleFontSize } "
129+ font-weight="900"
130+ fill="${ opts . textColor } ">
131+ ${ title }
132+ </text>
133+
134+ <!-- Sottotitolo -->
135+ <text x="60" y="${ subtitleY } "
136+ font-family="system-ui, -apple-system, sans-serif"
137+ font-size="${ subtitleFontSize } "
138+ font-weight="400"
139+ fill="rgba(255,255,255,0.9)">
140+ ${ subtitle }
141+ </text>
142+
143+ <!-- Logo/Brand text -->
144+ <text x="${ opts . width - 60 } " y="${ opts . height - 40 } "
145+ text-anchor="end"
146+ font-family="system-ui, -apple-system, sans-serif"
147+ font-size="20"
148+ font-weight="500"
149+ fill="rgba(255,255,255,0.7)">
150+ ${ brandText }
151+ </text>
152+
153+ <!-- Elemento decorativo angolare -->
154+ <polygon points="0,0 100,0 0,100" fill="rgba(255,255,255,0.05)" />
155+ <polygon points="${ opts . width } ,${ opts . height } ${ opts . width - 100 } ,${ opts . height } ${ opts . width } ,${ opts . height - 100 } " fill="rgba(255,255,255,0.05)" />
156+ </svg>
157+ ` ;
158+
159+ try {
160+ const pngBuffer = await sharp ( Buffer . from ( svgTemplate ) )
161+ . png ( {
162+ quality : 90 ,
163+ compressionLevel : 6
164+ } )
165+ . toBuffer ( ) ;
166+
167+ const dir = outputPath . substring ( 0 , outputPath . lastIndexOf ( '/' ) ) ;
168+ mkdirSync ( dir , { recursive : true } ) ;
169+ writeFileSync ( outputPath , pngBuffer ) ;
170+
171+ console . log ( `✅ Immagine OG Numeri generata: ${ outputPath } ` ) ;
172+ } catch ( error ) {
173+ console . error ( `❌ Errore nella generazione dell'immagine Numeri:` , error ) ;
174+ throw error ;
175+ }
176+ }
177+
83178export async function generateOGImage (
84179 initiative : Initiative ,
85180 outputPath : string ,
@@ -268,6 +363,9 @@ export async function generateAllOGImages(initiatives: Initiative[], outputDir:
268363 join ( outputDir , 'og-default.png' )
269364 ) ;
270365
366+ // Genero l'immagine dedicata per la pagina Numeri
367+ await generateNumeriOGImage ( join ( outputDir , 'og-numeri.png' ) ) ;
368+
271369 // Genero le immagini per ogni iniziativa
272370 const promises = initiatives . map ( initiative =>
273371 generateOGImage (
0 commit comments