@@ -29,10 +29,13 @@ export type AidCounts = {
2929 years : { first : number ; last : number } | null ;
3030} ;
3131
32- /** What DFO holds as a whole. */
32+ /** What DFO holds as a whole. `indicators` counts the StatVars search can
33+ * serve and `datasets` the source datasets behind them. */
3334export type CatalogueCounts = {
3435 publishers : number ;
3536 countries : number ;
37+ indicators : number ;
38+ datasets : number ;
3639 aid : AidCounts ;
3740} ;
3841
@@ -59,8 +62,9 @@ export type PlaceCatalogue = {
5962 examples : CatalogueExample [ ] ;
6063} ;
6164
62- /** The first idle chip. It shows the starting point from the catalogue the
63- * page already holds, with no search call. */
65+ /** The starting point's own question. Asked as a search, it shows the
66+ * starting point from the catalogue the page already holds, with no search
67+ * call. */
6468export const STARTING_POINT_QUERY = 'What data do you have?' ;
6569
6670/** How many examples a declined out_of_scope answer offers. */
@@ -83,7 +87,14 @@ const isExample = (value: unknown): value is CatalogueExample => {
8387
8488type CatalogueBody = {
8589 catalogue_version ?: unknown ;
86- counts ?: { publishers ?: unknown ; countries ?: unknown ; variables ?: unknown ; aid ?: unknown } ;
90+ counts ?: {
91+ publishers ?: unknown ;
92+ countries ?: unknown ;
93+ indicators ?: unknown ;
94+ datasets ?: unknown ;
95+ variables ?: unknown ;
96+ aid ?: unknown ;
97+ } ;
8798 examples ?: unknown ;
8899} ;
89100
@@ -141,14 +152,23 @@ const parseBody = (body: unknown, scoped: 'countries' | 'variables') => {
141152/** The whole catalogue read off the wire, or null when the body is not one. */
142153export function parseCatalogue ( body : unknown ) : SearchCatalogue | null {
143154 const read = parseBody ( body , 'countries' ) ;
155+ const counts = ( body as CatalogueBody | null ) ?. counts ;
144156
145- return (
146- read && {
147- catalogue_version : read . catalogue_version ,
148- counts : { publishers : read . publishers , countries : read . scoped , aid : read . aid } ,
149- examples : read . examples ,
150- }
151- ) ;
157+ if ( ! read || ! isCount ( counts ?. indicators ) || ! isCount ( counts . datasets ) ) {
158+ return null ;
159+ }
160+
161+ return {
162+ catalogue_version : read . catalogue_version ,
163+ counts : {
164+ publishers : read . publishers ,
165+ countries : read . scoped ,
166+ indicators : counts . indicators ,
167+ datasets : counts . datasets ,
168+ aid : read . aid ,
169+ } ,
170+ examples : read . examples ,
171+ } ;
152172}
153173
154174/** One country's catalogue read off the wire, or null when the body is not
@@ -165,13 +185,30 @@ export function parsePlaceCatalogue(body: unknown): PlaceCatalogue | null {
165185 ) ;
166186}
167187
168- /** The idle prompt's chips: the starting point, then every example. */
188+ /** The idle prompt's chips: every example. */
169189export function catalogueChips ( catalogue : SearchCatalogue | null ) : string [ ] {
170- return [ STARTING_POINT_QUERY , ... ( catalogue ?. examples ?? [ ] ) . map ( ( example ) => example . text ) ] ;
190+ return ( catalogue ?. examples ?? [ ] ) . map ( ( example ) => example . text ) ;
171191}
172192
173193const formatCount = ( value : number ) : string => value . toLocaleString ( 'en-GB' ) ;
174194
195+ // `en` writes millions as `1M`, where `en-GB` writes `1m`.
196+ const compactFormat = new Intl . NumberFormat ( 'en' , { notation : 'compact' } ) ;
197+
198+ /** A count of 1,000 or more floored to one significant figure in compact
199+ * form, with a `+` when the floor drops part of it: 112,866 reads `100K+`,
200+ * 100,000 reads `100K`. A smaller count reads exactly. */
201+ export function roundedCount ( count : number ) : string {
202+ if ( count < 1000 ) {
203+ return formatCount ( count ) ;
204+ }
205+
206+ const magnitude = 10 ** Math . floor ( Math . log10 ( count ) ) ;
207+ const floored = Math . floor ( count / magnitude ) * magnitude ;
208+
209+ return `${ compactFormat . format ( floored ) } ${ floored < count ? '+' : '' } ` ;
210+ }
211+
175212const publisherCount = ( publishers : number ) : string =>
176213 `${ formatCount ( publishers ) } ${ publishers === 1 ? 'publisher' : 'publishers' } ` ;
177214
@@ -213,7 +250,7 @@ export function declinedExamples(
213250 return ( placed . length > 0 ? placed : ( catalogue ?. examples ?? [ ] ) ) . slice ( 0 , count ) ;
214251}
215252
216- /** The stat tiles on /data and the homepage: publishers and countries , or
253+ /** The stat tiles on /data and the homepage: indicators and datasets , or
217254 * none without a catalogue. */
218255export function catalogueTiles (
219256 catalogue : SearchCatalogue | null ,
@@ -223,7 +260,7 @@ export function catalogueTiles(
223260 }
224261
225262 return [
226- { value : formatCount ( catalogue . counts . publishers ) , label : 'Publishers ' } ,
227- { value : formatCount ( catalogue . counts . countries ) , label : 'Countries ' } ,
263+ { value : roundedCount ( catalogue . counts . indicators ) , label : 'Indicators ' } ,
264+ { value : formatCount ( catalogue . counts . datasets ) , label : 'Datasets ' } ,
228265 ] ;
229266}
0 commit comments