@datagouv/components-next 1.0.2-dev.163 → 1.0.2-dev.165

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/{Datafair.client-DXPU1V24.js → Datafair.client-B-OG5WtK.js} +1 -1
  2. package/dist/ImagePreview.client-D0c-ifUi.js +36 -0
  3. package/dist/{JsonPreview.client-D1SnGfWG.js → JsonPreview.client-Dwh9sW9M.js} +2 -2
  4. package/dist/{MapContainer.client-DlsaEVGx.js → MapContainer.client-C2AZ7sOB.js} +2 -2
  5. package/dist/{PdfPreview.client-C9OrLR0z.js → PdfPreview.client-8NzNfW8b.js} +2 -2
  6. package/dist/{Pmtiles.client-CyylWfTy.js → Pmtiles.client-BFH4rTB7.js} +5842 -5660
  7. package/dist/{PreviewWrapper.vue_vue_type_script_setup_true_lang-CtXZOHXI.js → PreviewWrapper.vue_vue_type_script_setup_true_lang-BOgE441d.js} +7 -10
  8. package/dist/{XmlPreview.client-DoX7RurH.js → XmlPreview.client-DMOUO508.js} +3 -3
  9. package/dist/components-next.css +1 -1
  10. package/dist/components-next.js +179 -171
  11. package/dist/components.css +1 -1
  12. package/dist/{index-Br1sa7wQ.js → index-C9obT9DJ.js} +4392 -4293
  13. package/dist/{leaflet-src-DWG7tmpQ.js → leaflet-src-B4AZU0vr.js} +1 -1
  14. package/dist/{main-BU6kP4dE.js → main-DNoYWfit.js} +16159 -15631
  15. package/dist/{vue3-xml-viewer.common-Cpui-Yi9.js → vue3-xml-viewer.common-Cp7Cu5ME.js} +1 -1
  16. package/package.json +4 -5
  17. package/src/components/Chart/ChartViewer.vue +103 -10
  18. package/src/components/Chart/ChartViewerWrapper.vue +8 -0
  19. package/src/components/DataserviceCard.vue +6 -0
  20. package/src/components/DataserviceQuality.vue +82 -0
  21. package/src/components/DataserviceQualityInline.vue +31 -0
  22. package/src/components/DataserviceQualityTooltipContent.vue +79 -0
  23. package/src/components/DatasetQuality.vue +11 -11
  24. package/src/components/DatasetQualityInline.vue +2 -2
  25. package/src/components/DatasetQualityTooltipContent.vue +1 -1
  26. package/src/components/ResourceAccordion/ImagePreview.client.vue +48 -0
  27. package/src/components/ResourceAccordion/ResourceAccordion.vue +8 -3
  28. package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +15 -1
  29. package/src/components/ReuseCard.vue +2 -2
  30. package/src/components/ReuseDetails.vue +2 -2
  31. package/src/components/ReuseHorizontalCard.vue +2 -2
  32. package/src/components/Search/GlobalSearch.vue +2 -2
  33. package/src/components/TabularExplorer/TabularCell.vue +5 -4
  34. package/src/components/TabularExplorer/TabularCellPopover.vue +1 -1
  35. package/src/components/TabularExplorer/TabularExplorer.vue +168 -49
  36. package/src/components/TabularExplorer/TabularFilterContent.vue +27 -10
  37. package/src/components/TabularExplorer/TabularFilterPopover.vue +1 -0
  38. package/src/components/TabularExplorer/types.ts +3 -3
  39. package/src/composables/useHasTabularData.ts +2 -1
  40. package/src/composables/useResourceCapabilities.ts +27 -14
  41. package/src/config.ts +1 -0
  42. package/src/functions/charts.ts +23 -3
  43. package/src/functions/resources.ts +56 -0
  44. package/src/functions/tabular.ts +44 -1
  45. package/src/main.ts +15 -6
  46. package/src/types/dataservices.ts +15 -0
  47. package/src/types/organizations.ts +10 -0
  48. package/src/types/reuses.ts +12 -1
  49. package/src/types/search.ts +2 -2
  50. package/src/types/visualizations.ts +1 -1
  51. /package/src/components/{DatasetQualityItem.vue → QualityItem.vue} +0 -0
  52. /package/src/components/{DatasetQualityItemWarning.vue → QualityItemWarning.vue} +0 -0
  53. /package/src/components/{DatasetQualityScore.vue → QualityScore.vue} +0 -0
@@ -1,12 +1,12 @@
1
- import { computed, toValue, type MaybeRefOrGetter } from 'vue'
1
+ import { computed, type MaybeRefOrGetter, toValue } from 'vue'
2
2
  import { useComponentsConfig } from '../config'
3
- import { useTranslation } from './useTranslation'
4
- import { useHasTabularData } from './useHasTabularData'
5
- import { detectOgcService } from '../functions/resources'
6
3
  import { isOrganizationCertified } from '../functions/organizations'
7
- import type { Resource, WfsMetadata } from '../types/resources'
8
- import type { Dataset, DatasetV2 } from '../types/datasets'
9
4
  import { getWfsExportFormats } from '../functions/resourceCapabilities'
5
+ import { detectOgcService, getParsingErrorMessage, getParsingErrorStep, isImagePreviewFormat } from '../functions/resources'
6
+ import type { Dataset, DatasetV2 } from '../types/datasets'
7
+ import type { Resource, WfsMetadata } from '../types/resources'
8
+ import { useHasTabularData } from './useHasTabularData'
9
+ import { useTranslation } from './useTranslation'
10
10
 
11
11
  const GENERATED_FORMATS = ['parquet', 'pmtiles', 'geojson']
12
12
  const URL_FORMATS = ['url', 'doi', 'www:link', 'www:link-1.0-http--link', 'www:link-1.0-http--partners', 'www:link-1.0-http--related', 'www:link-1.0-http--samples']
@@ -21,7 +21,7 @@ export function useResourceCapabilities(
21
21
 
22
22
  const hasPreview = computed(() => {
23
23
  const format = toValue(resource).format?.toLowerCase()
24
- return format === 'json' || format === 'pdf' || format === 'xml'
24
+ return format === 'json' || format === 'pdf' || format === 'xml' || isImagePreviewFormat(format)
25
25
  })
26
26
 
27
27
  const hasTabularData = computed(() => {
@@ -49,6 +49,11 @@ export function useResourceCapabilities(
49
49
  const ogcService = computed(() => detectOgcService(toValue(resource)))
50
50
  const ogcWms = computed(() => ogcService.value === 'wms')
51
51
 
52
+ // The map is built from a pmtiles export; when that export fails we still want
53
+ // to show the Carte tab, but with the reason it could not be generated.
54
+ const hasPmtilesError = computed(() => getParsingErrorStep(toValue(resource)) === 'pmtiles_export')
55
+ const pmtilesError = computed(() => hasPmtilesError.value ? getParsingErrorMessage(toValue(resource)) : null)
56
+
52
57
  const generatedFormats = computed(() => {
53
58
  const r = toValue(resource)
54
59
  const formats = GENERATED_FORMATS
@@ -85,21 +90,27 @@ export function useResourceCapabilities(
85
90
  const r = toValue(resource)
86
91
  const options = []
87
92
 
88
- if (hasTabularData.value) {
89
- options.push({ key: 'data', label: t('Données') })
93
+ const dataTab = { key: 'data', label: hasTabularData.value ? t('Données') : t('Aperçu') }
94
+ const mapTab = { key: 'map', label: t('Carte') }
95
+ const hasMap = hasPmtiles.value || ogcWms.value
96
+
97
+ // The map is the most visual preview: when it is available, show it first.
98
+ // When its generation failed, keep the data preview first and surface the
99
+ // Carte tab (with its error) right after.
100
+ if (hasMap) {
101
+ options.push(mapTab, dataTab)
102
+ }
103
+ else if (hasPmtilesError.value) {
104
+ options.push(dataTab, mapTab)
90
105
  }
91
106
  else {
92
- options.push({ key: 'data', label: t('Aperçu') })
107
+ options.push(dataTab)
93
108
  }
94
109
 
95
110
  if (hasTabularData.value) {
96
111
  options.push({ key: 'data-structure', label: t('Structure des données') })
97
112
  }
98
113
 
99
- if (hasPmtiles.value || ogcWms.value) {
100
- options.push({ key: 'map', label: t('Carte') })
101
- }
102
-
103
114
  if (r.description) {
104
115
  options.push({ key: 'description', label: t('Description') })
105
116
  }
@@ -118,6 +129,8 @@ export function useResourceCapabilities(
118
129
  hasPreview,
119
130
  hasTabularData,
120
131
  hasPmtiles,
132
+ hasPmtilesError,
133
+ pmtilesError,
121
134
  hasDatafairPreview,
122
135
  hasOpenAPIPreview,
123
136
  ogcService,
package/src/config.ts CHANGED
@@ -12,6 +12,7 @@ export type PluginConfig = {
12
12
  datasetQualityGuideUrl?: string
13
13
  maxJsonPreviewCharSize?: number // Maximum size of JSON to preview in characters. JSON preview module is partly collapsed by default so we can have a preview for large files.
14
14
  maxPdfPreviewByteSize?: number // Maximum size of PDF to preview in bytes
15
+ maxImagePreviewByteSize?: number // Maximum size of image to preview in bytes
15
16
  maxXmlPreviewCharSize?: number // Maximum size of XML to preview in characters. XML preview module can NOT be collapsed by default so we should not have a preview for large files.
16
17
  pmtilesViewerBaseUrl?: string | null // Base URL of a pmtiles viewer (ex: https://pmtiles.io/#url=)
17
18
  metricsApiUrl?: string
@@ -1,5 +1,5 @@
1
1
  import type { Chart, ChartForm, ChartForApi, Filter, ColumnDefinition } from '../types/visualizations'
2
- import type { TabularProfile } from '../components/TabularExplorer/types'
2
+ import type { ColumnType, TabularProfile } from '../components/TabularExplorer/types'
3
3
  import { resolveColumnType } from './tabular'
4
4
 
5
5
  export function toChartForm(chart: Chart) {
@@ -73,7 +73,27 @@ export function buildColumnsFromProfile(profile: { profile: TabularProfile }): A
73
73
  return profile.profile.header.map((name) => {
74
74
  const colInfo = profile.profile.columns[name]
75
75
  const isCategorical = profile.profile.categorical.includes(name)
76
- const colType = resolveColumnType(colInfo ?? { python_type: 'unknown', format: undefined }, isCategorical)
77
- return { name, type: colType }
76
+ // The chart stack only branches on 'number' and 'date' (continuous axis,
77
+ // aggregatable series, time formatting): it has no notion of a year column,
78
+ // and on an axis a year is a number.
79
+ const resolvedType = resolveColumnType(colInfo ?? { python_type: 'unknown', format: undefined }, isCategorical)
80
+ const colType = resolvedType === 'year' ? 'number' : resolvedType
81
+ const colProfile = profile.profile.profile[name]
82
+ return {
83
+ name,
84
+ type: colType,
85
+ min: normalizeProfileNumber(colProfile?.min, colType),
86
+ max: normalizeProfileNumber(colProfile?.max, colType),
87
+ }
78
88
  })
79
89
  }
90
+
91
+ function normalizeProfileNumber(value: number | string | undefined, type: ColumnType): number | undefined {
92
+ if (value === undefined || value === null) return undefined
93
+ if (type === 'date' && typeof value === 'string') {
94
+ const timestamp = new Date(value).getTime()
95
+ return Number.isNaN(timestamp) ? undefined : timestamp
96
+ }
97
+ const parsed = Number(value)
98
+ return Number.isNaN(parsed) ? undefined : parsed
99
+ }
@@ -105,14 +105,29 @@ export const getResourceLabel = (type: ResourceType, count?: number) => {
105
105
  }
106
106
  return t('Fichiers principaux')
107
107
  case 'documentation':
108
+ if (typeof count === 'number') {
109
+ return t('Aucun fichier de documentation | 1 fichier de documentation | {n} fichiers de documentation', count)
110
+ }
108
111
  return t('Documentation')
109
112
  case 'update':
113
+ if (typeof count === 'number') {
114
+ return t('Aucune mise à jour | 1 mise à jour | {n} mises à jour', count)
115
+ }
110
116
  return t('Mise à jour')
111
117
  case 'api':
118
+ if (typeof count === 'number') {
119
+ return t('Aucune API | 1 API | {n} API', count)
120
+ }
112
121
  return t('API')
113
122
  case 'code':
123
+ if (typeof count === 'number') {
124
+ return t('Aucun fichier de code source | 1 fichier de code source | {n} fichiers de code source', count)
125
+ }
114
126
  return t('Code source')
115
127
  case 'other':
128
+ if (typeof count === 'number') {
129
+ return t('Aucun autre fichier | 1 autre fichier | {n} autres fichiers', count)
130
+ }
116
131
  return t('Autre')
117
132
  }
118
133
  }
@@ -152,6 +167,47 @@ export function getResourceFilesize(resource: Resource): null | number {
152
167
  return null
153
168
  }
154
169
 
170
+ const IMAGE_PREVIEW_FORMATS = ['png', 'jpg', 'jpeg', 'jpe', 'gif', 'webp'] as const
171
+
172
+ export function isImagePreviewFormat(format: string | undefined | null): boolean {
173
+ if (!format) return false
174
+ return IMAGE_PREVIEW_FORMATS.includes(format.toLowerCase() as typeof IMAGE_PREVIEW_FORMATS[number])
175
+ }
176
+
177
+ // Parsing errors are stored as `<step>:<message>`, where `<step>` names the
178
+ // analysis step that failed (e.g. `pmtiles_export`, `geojson_export`).
179
+
180
+ /** The analysis step that failed, or null when there is no parsing error. */
181
+ export function getParsingErrorStep(resource: Resource): string | null {
182
+ const error = resource.extras['analysis:parsing:error']
183
+ if (typeof error !== 'string' || !error) return null
184
+ const [step] = error.split(':', 1)
185
+ return step ?? null
186
+ }
187
+
188
+ /** The parsing error message without its `<step>:` prefix, or null when there is none. */
189
+ export function getParsingErrorMessage(resource: Resource): string | null {
190
+ const error = resource.extras['analysis:parsing:error']
191
+ if (typeof error !== 'string' || !error) return null
192
+ const colon = error.indexOf(':')
193
+ return colon === -1 ? error : error.slice(colon + 1)
194
+ }
195
+
196
+ // Geo/format export steps run *after* the tabular table has been built, so a
197
+ // failure here (e.g. a huge geometry timing out the pmtiles export) leaves the
198
+ // tabular data fully usable and must not disable the tabular explorer.
199
+ const NON_TABULAR_PARSING_ERROR_STEPS = ['pmtiles_export', 'geojson_export']
200
+
201
+ /**
202
+ * Whether the resource's parsing error prevents reading its tabular data.
203
+ * Only errors from the tabular parsing itself are fatal; downstream geo/format
204
+ * export failures are not.
205
+ */
206
+ export function hasTabularParsingError(resource: Resource): boolean {
207
+ const step = getParsingErrorStep(resource)
208
+ return step !== null && !NON_TABULAR_PARSING_ERROR_STEPS.includes(step)
209
+ }
210
+
155
211
  type CorsStatus = 'allowed' | 'blocked' | 'unknown'
156
212
 
157
213
  export const getResourceCorsStatus = (resource: Resource): CorsStatus => {
@@ -42,12 +42,13 @@ export function buildTypeConfig(t: TranslationFunction): Record<ColumnType, Type
42
42
  text: { icon: RiText, label: t('Texte') },
43
43
  date: { icon: RiCalendarLine, label: t('Date') },
44
44
  boolean: { icon: RiCheckboxLine, label: t('Booléen') },
45
+ year: { icon: RiCalendarLine, label: t('Année') },
45
46
  }
46
47
  }
47
48
 
48
49
  export function resolveColumnType(colInfo: { python_type: string, format?: string }, isCategorical: boolean): ColumnType {
50
+ if (colInfo.format === 'year') return 'year'
49
51
  if (['int', 'float'].includes(colInfo.python_type)) return 'number'
50
- if (colInfo.format === 'year') return 'date'
51
52
  if (['date', 'datetime'].includes(colInfo.python_type)) return 'date'
52
53
  if (colInfo.python_type === 'bool') return 'boolean'
53
54
  if (isCategorical) return 'categorical'
@@ -158,6 +159,48 @@ export function useFormatTabular() {
158
159
  const TRUTHY_VALUES = ['true', '1', 'oui', 'yes']
159
160
  const FALSY_VALUES = ['false', '0', 'non', 'no']
160
161
 
162
+ // `encodeURIComponent` leaves `.`, `(` and `)` as-is, but they are the operator
163
+ // separator and the delimiters of the API's `or(...)` grammar: a search value
164
+ // containing one makes the parser reject the whole query with a 400. Wrapping
165
+ // the value in double quotes (as the API README suggests) does not work here —
166
+ // the API keeps them as part of the searched string and returns no result. It
167
+ // does percent-decode the value after parsing, so encoding them is enough.
168
+ function encodeConditionValue(value: string): string {
169
+ return encodeURIComponent(value)
170
+ .replace(/\./g, '%2E')
171
+ .replace(/\(/g, '%28')
172
+ .replace(/\)/g, '%29')
173
+ }
174
+
175
+ // A decimal literal, not `Number()`: the API compares against a number column,
176
+ // and it answers an error for the whole `or(...)` when it is handed something it
177
+ // cannot parse as one — which `Number()` happily accepts (`0x10`, `1e5`, ` `).
178
+ const NUMBER_LITERAL_RE = /^-?\d+(\.\d+)?$/
179
+
180
+ /**
181
+ * Builds the OR conditions for global search across all columns.
182
+ * Text and categorical columns get a `__contains` filter; number columns
183
+ * get a `__exact` filter only when the search value is numeric.
184
+ * Year, date and boolean columns are excluded.
185
+ */
186
+ export function buildGlobalSearchConditions(
187
+ allColumns: string[],
188
+ getColumnType: (col: string) => ColumnType,
189
+ searchValue: string,
190
+ ): string[] {
191
+ const conditions: string[] = []
192
+ for (const col of allColumns) {
193
+ const type = getColumnType(col)
194
+ if (type === 'text' || type === 'categorical') {
195
+ conditions.push(col + '__contains.' + encodeConditionValue(searchValue))
196
+ }
197
+ else if (type === 'number' && NUMBER_LITERAL_RE.test(searchValue)) {
198
+ conditions.push(col + '__exact.' + encodeConditionValue(searchValue))
199
+ }
200
+ }
201
+ return conditions
202
+ }
203
+
161
204
  export function isTruthy(value: unknown): boolean {
162
205
  if (typeof value === 'boolean') return value
163
206
  if (typeof value === 'string') return TRUTHY_VALUES.includes(value.toLowerCase())
package/src/main.ts CHANGED
@@ -15,7 +15,7 @@ import type { Owned, OwnedWithFullObject, OwnedWithId } from './types/owned'
15
15
  import type { Comment, Thread } from './types/discussions'
16
16
  import type { PageBloc, ContentBloc, BlocWithTitle, DatasetsListBloc, DataservicesListBloc, ReusesListBloc, LinkInBloc, LinksListBloc, MarkdownBloc, AccordionItemBloc, AccordionListBloc, HeroBloc } from './types/pages'
17
17
  import type { Post } from './types/posts'
18
- import type { ReuseReference, NewReuse, Reuse, ReuseTopic, ReuseType } from './types/reuses'
18
+ import type { ReuseReference, NewReuse, Reuse, ReuseV2, ReuseTopic, ReuseType } from './types/reuses'
19
19
  import type { RegisteredSchema, Schema, SchemaDetails, SchemaField, SchemaPath, SchemaPublicationMode, SchemaResponseData, SchemaVersion, ValidataError } from './types/schemas'
20
20
  import type { TopicV2, TopicElement, TopicElementClass, TopicElementRel } from './types/topics'
21
21
  import type { CommunityResource, FileResourceFileType, RemoteResourceFileType, ResourceFileType, ResourceGroup, ResourceType, Resource } from './types/resources'
@@ -24,7 +24,7 @@ import type { Weight, WellType } from './types/ui'
24
24
  import type { User, UserReference } from './types/users'
25
25
  import type { Report, ReportSubject, ReportReason } from './types/reports'
26
26
  import type { Chart, ChartForm, ChartForApi, FilterCondition, Filter, AndFilters, GenericFilter, XAxisType, XAxisSortBy, SortDirection, XAxis, XAxisForm, UnitPosition, YAxis, DataSeriesType, DataSeries, DataSeriesForm, CombinedSort, ColumnDefinition, ColumnsDefinition } from './types/visualizations'
27
- import type { ColumnType } from './components/TabularExplorer/types'
27
+ import type { ColumnFilters, ColumnType, TabularRow } from './components/TabularExplorer/types'
28
28
  import type { GlobalSearchConfig, SearchType, SearchTypeConfig, SortOption, HiddenFilter, BuiltInFilterKey, DatasetSearchConfig, DatasetSearchFilters, DataserviceSearchConfig, DataserviceSearchFilters, ReuseSearchConfig, ReuseSearchFilters, OrganizationSearchConfig, OrganizationSearchFilters, TopicSearchConfig, TopicSearchFilters } from './types/search'
29
29
  import { getDefaultDatasetConfig, getDefaultDataserviceConfig, getDefaultReuseConfig, getDefaultOrganizationConfig, getDefaultTopicConfig, getDefaultGlobalSearchConfig, defaultDatasetSortOptions, defaultDataserviceSortOptions, defaultReuseSortOptions, defaultOrganizationSortOptions } from './types/search'
30
30
  import { useSearchFilter } from './composables/useSearchFilter'
@@ -40,6 +40,9 @@ import BannerAction from './components/BannerAction.vue'
40
40
  import BrandedButton from './components/BrandedButton.vue'
41
41
  import CopyButton from './components/CopyButton.vue'
42
42
  import DataserviceCard from './components/DataserviceCard.vue'
43
+ import DataserviceQuality from './components/DataserviceQuality.vue'
44
+ import DataserviceQualityInline from './components/DataserviceQualityInline.vue'
45
+ import DataserviceQualityTooltipContent from './components/DataserviceQualityTooltipContent.vue'
43
46
  import DatasetCard from './components/DatasetCard.vue'
44
47
  import DataStructure from './components/ResourceAccordion/DataStructure.vue'
45
48
  import Downloads from './components/ResourceAccordion/Downloads.vue'
@@ -54,8 +57,8 @@ import LicenseBadge from './components/LicenseBadge.vue'
54
57
  import Tag from './components/Tag.vue'
55
58
  import DatasetQuality from './components/DatasetQuality.vue'
56
59
  import DatasetQualityInline from './components/DatasetQualityInline.vue'
57
- import DatasetQualityItem from './components/DatasetQualityItem.vue'
58
- import DatasetQualityScore from './components/DatasetQualityScore.vue'
60
+ import QualityItem from './components/QualityItem.vue'
61
+ import QualityScore from './components/QualityScore.vue'
59
62
  import ProgressBar from './components/ProgressBar.vue'
60
63
  import DatasetQualityTooltipContent from './components/DatasetQualityTooltipContent.vue'
61
64
  import ExtraAccordion from './components/ExtraAccordion.vue'
@@ -228,6 +231,7 @@ export type {
228
231
  ResourceType,
229
232
  ReuseReference,
230
233
  Reuse,
234
+ ReuseV2,
231
235
  ReuseTopic,
232
236
  ReuseType,
233
237
  Schema,
@@ -254,8 +258,10 @@ export type {
254
258
  ChartForApi,
255
259
  CombinedSort,
256
260
  ColumnDefinition,
261
+ ColumnFilters,
257
262
  ColumnType,
258
263
  ColumnsDefinition,
264
+ TabularRow,
259
265
  FilterCondition,
260
266
  Filter,
261
267
  AndFilters,
@@ -334,6 +340,9 @@ export {
334
340
  BrandedButton,
335
341
  CopyButton,
336
342
  DataserviceCard,
343
+ DataserviceQuality,
344
+ DataserviceQualityInline,
345
+ DataserviceQualityTooltipContent,
337
346
  DatasetCard,
338
347
  DataStructure,
339
348
  Downloads,
@@ -348,8 +357,8 @@ export {
348
357
  DiscussionMessageCard,
349
358
  DatasetQuality,
350
359
  DatasetQualityInline,
351
- DatasetQualityItem,
352
- DatasetQualityScore,
360
+ QualityItem,
361
+ QualityScore,
353
362
  DatasetQualityTooltipContent,
354
363
  DateRangeDetails,
355
364
  ExtraAccordion,
@@ -31,6 +31,20 @@ export type BaseDataservice = Owned & WithAccessType & {
31
31
 
32
32
  export type NewDataservice = Omit<BaseDataservice, keyof OwnedWithId> & OwnedWithId
33
33
 
34
+ export type DataserviceQuality = {
35
+ has_description: boolean
36
+ has_machine_documentation: boolean
37
+ has_technical_documentation: boolean
38
+ has_business_documentation: boolean
39
+ license: boolean
40
+ has_contact_point: boolean
41
+ has_base_api_url: boolean
42
+ availability_documented: boolean
43
+ rate_limiting_documented: boolean
44
+ access_conditions_clear: boolean
45
+ score: number
46
+ }
47
+
34
48
  export type Dataservice = Owned & WithAccessType & {
35
49
  acronym: string
36
50
  archived_at: string | null
@@ -72,4 +86,5 @@ export type Dataservice = Owned & WithAccessType & {
72
86
  slug: string
73
87
  tags: Array<string>
74
88
  title: string
89
+ quality?: DataserviceQuality
75
90
  }
@@ -1,5 +1,6 @@
1
1
  import type { User } from './users'
2
2
  import type { Badges } from './badges'
3
+ import type { PageBloc } from './pages'
3
4
 
4
5
  export type MemberRole = 'admin' | 'editor' | 'partial_editor'
5
6
 
@@ -68,4 +69,13 @@ export type Organization = {
68
69
  reuses_by_months: Record<string, number>
69
70
  views: number
70
71
  }
72
+ // Editorial blocs powering the organization "Présentation" tab. Excluded from
73
+ // the default API read mask, so only present when requested with X-Fields.
74
+ // Read through the API's `public_presentation_blocs`: hidden from the public until
75
+ // published, but always returned to organization administrators.
76
+ presentation_blocs?: Array<PageBloc>
77
+ // Publication date of the blocs. `null` means draft (never public); a set date
78
+ // means published. Like `presentation_blocs`, only present when requested with
79
+ // X-Fields.
80
+ presentation_blocs_published_at?: string | null
71
81
  }
@@ -1,5 +1,5 @@
1
1
  import type { Owned, OwnedWithId } from './owned'
2
- import type { Dataset } from './datasets'
2
+ import type { Dataset, Rel } from './datasets'
3
3
  import type { Badges } from './badges'
4
4
  import type { Dataservice } from './dataservices'
5
5
 
@@ -51,6 +51,17 @@ export type Reuse = BaseReuse & {
51
51
  permissions: { edit: boolean, delete: boolean }
52
52
  }
53
53
 
54
+ /**
55
+ * Lightweight reuse returned by the listing endpoints (`/api/2/reuses/` and
56
+ * `/api/2/reuses/search/`): the `datasets` and `dataservices` reference lists
57
+ * are replaced by links to their respective listing endpoints filtered on this
58
+ * reuse, so that listing reuses does not dereference every linked dataset.
59
+ */
60
+ export type ReuseV2 = Owned & Omit<Reuse, 'datasets' | 'dataservices'> & {
61
+ datasets: Rel
62
+ dataservices: Rel
63
+ }
64
+
54
65
  export type ReuseType = {
55
66
  id: string
56
67
  label: string
@@ -4,7 +4,7 @@ import type { AccessType } from './access_types'
4
4
  import type { Dataset } from './datasets'
5
5
  import type { Dataservice } from './dataservices'
6
6
  import type { Organization } from './organizations'
7
- import type { Reuse } from './reuses'
7
+ import type { ReuseV2 } from './reuses'
8
8
  import type { TopicV2 } from './topics'
9
9
  import type {
10
10
  CERTIFIED,
@@ -372,7 +372,7 @@ export type GlobalSearchConfig = SearchTypeConfig[]
372
372
  export type SearchResponseByClass = {
373
373
  datasets: DatasetSearchResponse<Dataset>
374
374
  dataservices: DataserviceSearchResponse<Dataservice>
375
- reuses: ReuseSearchResponse<Reuse>
375
+ reuses: ReuseSearchResponse<ReuseV2>
376
376
  organizations: OrganizationSearchResponse<Organization>
377
377
  topics: TopicSearchResponse<TopicV2>
378
378
  }
@@ -89,5 +89,5 @@ export type ChartForm = Omit<ChartForApi, 'x_axis' | 'series' | 'owner' | 'organ
89
89
  filter: GenericFilter | null
90
90
  }
91
91
 
92
- export type ColumnDefinition = { name: string, type: ColumnType }
92
+ export type ColumnDefinition = { name: string, type: ColumnType, min?: number, max?: number }
93
93
  export type ColumnsDefinition = Record<string, Array<ColumnDefinition>>