@@ -52,7 +52,7 @@ function groupByCountry(activities: readonly GeocodedActivity[]): Map<string, Ge
5252 const groups = new Map < string , GeocodedActivity [ ] > ( )
5353
5454 for ( const a of activities ) {
55- const country = a . country ?? 'Unknown '
55+ const country = a . country ?? 'General '
5656 const existing = groups . get ( country ) ?? [ ]
5757 existing . push ( a )
5858 groups . set ( country , existing )
@@ -250,6 +250,7 @@ function renderFlatList(
250250 config : PDFConfig ,
251251 renderOptions : RenderOptions
252252) : void {
253+ doc . moveDown ( 2 )
253254 renderActivityList ( doc , activities , config , renderOptions , 'score' )
254255}
255256
@@ -267,9 +268,11 @@ function renderCategorySection(
267268 if ( doc . y > 700 ) doc . addPage ( )
268269
269270 doc . moveDown ( options . moveDown )
271+ doc . x = 50 // Reset to left margin (thumbnails shift X position)
270272 doc
271273 . fontSize ( options . fontSize )
272274 . font ( 'Helvetica-Bold' )
275+ . fillColor ( '#000000' )
273276 . text ( `${ CATEGORY_NAMES [ category ] } (${ items . length } )` )
274277 doc . moveDown ( 0.5 )
275278
@@ -299,49 +302,61 @@ function renderGroupedByCategory(
299302}
300303
301304/**
302- * Render activities grouped by country only .
305+ * Render a horizontal divider line .
303306 */
304- function renderGroupedByCountry (
307+ function renderDivider ( doc : PDFKit . PDFDocument ) : void {
308+ const y = doc . y + 32
309+ doc . strokeColor ( '#cccccc' ) . lineWidth ( 0.5 ) . moveTo ( 50 , y ) . lineTo ( 545 , y ) . stroke ( )
310+ doc . y = y + 8
311+ }
312+
313+ /**
314+ * Render a country section with heading and divider.
315+ */
316+ function renderCountrySection (
305317 doc : PDFKit . PDFDocument ,
306- activities : readonly GeocodedActivity [ ] ,
307- config : PDFConfig ,
308- renderOptions : RenderOptions
318+ country : string ,
319+ items : readonly GeocodedActivity [ ] ,
320+ _config : PDFConfig ,
321+ _renderOptions : RenderOptions ,
322+ options : { fontSize : number ; renderContent : ( ) => void }
309323) : void {
310- const grouped = groupByCountry ( activities )
311- const countries = [ ...grouped . keys ( ) ] . sort ( )
324+ if ( doc . y > 680 ) doc . addPage ( )
312325
313- for ( const country of countries ) {
314- const items = grouped . get ( country )
315- if ( ! items || items . length === 0 ) continue
326+ renderDivider ( doc )
316327
317- if ( doc . y > 700 ) doc . addPage ( )
318-
319- doc . moveDown ( 1.5 )
320- doc . fontSize ( 16 ) . font ( 'Helvetica-Bold' ) . text ( `${ country } (${ items . length } )` )
321- doc . moveDown ( 0.5 )
328+ doc . moveDown ( 0.5 )
329+ doc . x = 50 // Reset to left margin (thumbnails shift X position)
330+ doc
331+ . fontSize ( options . fontSize )
332+ . font ( 'Helvetica-Bold' )
333+ . fillColor ( '#000000' )
334+ . text ( `${ country } (${ items . length } )` )
322335
323- renderActivityList ( doc , items , config , renderOptions , 'score' )
324- }
336+ options . renderContent ( )
325337}
326338
327339/**
328- * Render categories within a country section .
340+ * Render activities grouped by country only .
329341 */
330- function renderCategoriesInCountry (
342+ function renderGroupedByCountry (
331343 doc : PDFKit . PDFDocument ,
332- countryItems : readonly GeocodedActivity [ ] ,
344+ activities : readonly GeocodedActivity [ ] ,
333345 config : PDFConfig ,
334346 renderOptions : RenderOptions
335347) : void {
336- const byCategory = groupByCategory ( countryItems )
348+ const grouped = groupByCountry ( activities )
337349
338- for ( const category of VALID_CATEGORIES ) {
339- const items = byCategory . get ( category )
350+ for ( const country of [ ... grouped . keys ( ) ] . sort ( ) ) {
351+ const items = grouped . get ( country )
340352 if ( ! items || items . length === 0 ) continue
341353
342- renderCategorySection ( doc , category , items , config , renderOptions , {
343- moveDown : 1 ,
344- fontSize : 14
354+ renderCountrySection ( doc , country , items , config , renderOptions , {
355+ fontSize : 16 ,
356+ renderContent : ( ) => {
357+ doc . moveDown ( 0.5 )
358+ renderActivityList ( doc , items , config , renderOptions , 'score' )
359+ }
345360 } )
346361 }
347362}
@@ -356,18 +371,27 @@ function renderGroupedByCountryAndCategory(
356371 renderOptions : RenderOptions
357372) : void {
358373 const byCountry = groupByCountry ( activities )
359- const countries = [ ...byCountry . keys ( ) ] . sort ( )
360374
361- for ( const country of countries ) {
375+ for ( const country of [ ... byCountry . keys ( ) ] . sort ( ) ) {
362376 const countryItems = byCountry . get ( country )
363377 if ( ! countryItems || countryItems . length === 0 ) continue
364378
365- if ( doc . y > 680 ) doc . addPage ( )
379+ const byCategory = groupByCategory ( countryItems )
366380
367- doc . moveDown ( 1.5 )
368- doc . fontSize ( 18 ) . font ( 'Helvetica-Bold' ) . text ( `${ country } (${ countryItems . length } )` )
381+ renderCountrySection ( doc , country , countryItems , config , renderOptions , {
382+ fontSize : 18 ,
383+ renderContent : ( ) => {
384+ for ( const category of VALID_CATEGORIES ) {
385+ const items = byCategory . get ( category )
386+ if ( ! items || items . length === 0 ) continue
369387
370- renderCategoriesInCountry ( doc , countryItems , config , renderOptions )
388+ renderCategorySection ( doc , category , items , config , renderOptions , {
389+ moveDown : 1 ,
390+ fontSize : 14
391+ } )
392+ }
393+ }
394+ } )
371395 }
372396}
373397
0 commit comments