@datagouv/components-next 0.0.7 → 0.0.9

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 (103) hide show
  1. package/README.md +34 -14
  2. package/assets/json/vector.json +2377 -0
  3. package/assets/main.css +3 -0
  4. package/assets/swagger-themes/newspaper.css +1669 -0
  5. package/assets/tailwind.config.js +1 -1
  6. package/dist/JsonPreview.client-BIz1_EiB.js +92 -0
  7. package/dist/MapContainer.client-ZDwr4Q_I.js +78276 -0
  8. package/dist/PdfPreview.client-BTTMM27i.js +112 -0
  9. package/dist/Pmtiles.client-4kOoUQcR.js +22377 -0
  10. package/dist/Swagger.client-Q7a5wb51.js +4 -0
  11. package/dist/XmlPreview.client-BYIIkDqf.js +84 -0
  12. package/dist/components-next.css +52 -1
  13. package/dist/components-next.js +42 -41
  14. package/dist/components.css +1 -1
  15. package/dist/main-CLUk9Jj7.js +105843 -0
  16. package/dist/pdf-vue3-BZh6kzke.js +273 -0
  17. package/dist/pdf.min-f72cfa08-DAetWL3M.js +9501 -0
  18. package/dist/{text-clamp.esm-DurZFOvT.js → text-clamp.esm-DP59tec5.js} +1 -1
  19. package/dist/vue3-json-viewer-DIQzFF6K.js +1089 -0
  20. package/dist/vue3-xml-viewer.common-BmKw6vER.js +5437 -0
  21. package/package.json +7 -5
  22. package/src/components/AvatarWithName.vue +6 -2
  23. package/src/components/BannerAction.vue +1 -1
  24. package/src/components/BrandedButton.vue +13 -8
  25. package/src/components/CopyButton.vue +7 -7
  26. package/src/components/DataserviceCard.vue +54 -23
  27. package/src/components/DatasetCard.vue +36 -24
  28. package/src/components/DatasetInformationPanel.vue +19 -18
  29. package/src/components/DatasetQuality.vue +21 -18
  30. package/src/components/DatasetQualityInline.vue +1 -1
  31. package/src/components/DatasetQualityItem.vue +3 -3
  32. package/src/components/DatasetQualityItemWarning.vue +2 -2
  33. package/src/components/DatasetQualityScore.vue +2 -2
  34. package/src/components/DatasetQualityTooltipContent.vue +29 -29
  35. package/src/components/DescriptionDetails.vue +2 -2
  36. package/src/components/ExtraAccordion.vue +10 -7
  37. package/src/components/OrganizationCard.vue +9 -4
  38. package/src/components/OrganizationNameWithCertificate.vue +25 -11
  39. package/src/components/Pagination.vue +26 -15
  40. package/src/components/ReadMore.vue +2 -2
  41. package/src/components/ResourceAccordion/DataStructure.vue +2 -2
  42. package/src/components/ResourceAccordion/EditButton.vue +10 -6
  43. package/src/components/ResourceAccordion/JsonPreview.client.vue +153 -0
  44. package/src/components/ResourceAccordion/MapContainer.client.vue +137 -0
  45. package/src/components/ResourceAccordion/Metadata.vue +33 -54
  46. package/src/components/ResourceAccordion/PdfPreview.client.vue +189 -0
  47. package/src/components/ResourceAccordion/Pmtiles.client.vue +166 -0
  48. package/src/components/ResourceAccordion/Preview.vue +39 -37
  49. package/src/components/ResourceAccordion/ResourceAccordion.vue +141 -63
  50. package/src/components/ResourceAccordion/ResourceIcon.vue +7 -1
  51. package/src/components/ResourceAccordion/SchemaBadge.vue +26 -26
  52. package/src/components/ResourceAccordion/{Swagger.vue → Swagger.client.vue} +1 -1
  53. package/src/components/ResourceAccordion/XmlPreview.client.vue +143 -0
  54. package/src/components/ReuseCard.vue +10 -7
  55. package/src/components/ReuseDetails.vue +3 -3
  56. package/src/components/SimpleBanner.vue +7 -4
  57. package/src/components/SmallChart.vue +23 -9
  58. package/src/components/StatBox.vue +92 -10
  59. package/src/config.ts +6 -2
  60. package/src/functions/api.ts +18 -18
  61. package/src/functions/dates.ts +81 -74
  62. package/src/functions/helpers.ts +5 -4
  63. package/src/functions/organizations.ts +5 -5
  64. package/src/functions/resources.ts +34 -5
  65. package/src/functions/schemas.ts +4 -3
  66. package/src/functions/tabularApi.ts +1 -1
  67. package/src/main.ts +10 -11
  68. package/src/types/badges.ts +3 -3
  69. package/src/types/contact_point.ts +5 -5
  70. package/src/types/dataservices.ts +16 -2
  71. package/src/types/datasets.ts +20 -2
  72. package/src/types/frequency.ts +5 -5
  73. package/src/types/granularity.ts +12 -4
  74. package/src/types/harvest.ts +2 -2
  75. package/src/types/licenses.ts +8 -8
  76. package/src/types/organizations.ts +6 -0
  77. package/src/types/resources.ts +3 -3
  78. package/src/types/reuses.ts +3 -1
  79. package/src/types/site.ts +8 -0
  80. package/src/types/ui.ts +2 -2
  81. package/src/types/users.ts +24 -8
  82. package/src/types/vue3-xml-viewer.d.ts +10 -0
  83. package/dist/Swagger-DjysB-OI.js +0 -67851
  84. package/dist/en-DCRve7vN.js +0 -613
  85. package/dist/fr-DCOnbL-p.js +0 -613
  86. package/dist/locales/de.js +0 -155
  87. package/dist/locales/en.js +0 -155
  88. package/dist/locales/es.js +0 -155
  89. package/dist/locales/fr.js +0 -155
  90. package/dist/locales/it.js +0 -155
  91. package/dist/locales/pt.js +0 -155
  92. package/dist/locales/sr.js +0 -155
  93. package/dist/main-CPW2vNLE.js +0 -32008
  94. package/src/components/DescriptionList/DescriptionDetails.stories.ts +0 -43
  95. package/src/components/DescriptionList/DescriptionList.stories.ts +0 -47
  96. package/src/components/DescriptionList/DescriptionTerm.stories.ts +0 -28
  97. package/src/locales/de.json +0 -154
  98. package/src/locales/en.json +0 -154
  99. package/src/locales/es.json +0 -154
  100. package/src/locales/fr.json +0 -154
  101. package/src/locales/it.json +0 -154
  102. package/src/locales/pt.json +0 -154
  103. package/src/locales/sr.json +0 -154
@@ -1,11 +1,11 @@
1
1
  <template>
2
2
  <div
3
- class="border border-gray-default"
3
+ class="border border-gray-default overflow-auto"
4
4
  :class="{ 'fr-pb-4v': open }"
5
5
  >
6
6
  <header
7
7
  :id="resourceHeaderId"
8
- class="fr-p-4v flex items-center justify-between relative"
8
+ class="fr-p-4v flex flex-wrap gap-4 items-center justify-between relative"
9
9
  >
10
10
  <div>
11
11
  <div class="flex items-center fr-mb-1v">
@@ -25,22 +25,28 @@
25
25
  class="size-3.5 mr-1"
26
26
  />
27
27
  <span
28
- :class="{
29
- 'font-bold': open,
30
- }"
28
+ v-if="open"
29
+ class="font-bold text-left"
30
+ ><component
31
+ :is="config.textClamp"
32
+ v-if="config && config.textClamp"
33
+ :max-lines="2"
34
+ :text="resource.title || t('Fichier sans nom')"
35
+ /></span>
36
+ <span
37
+ v-else
31
38
  ><component
32
39
  :is="config.textClamp"
33
40
  v-if="config && config.textClamp"
34
41
  :max-lines="1"
35
- :text="resource.title || t('Nameless file')"
42
+ :text="resource.title || t('Fichier sans nom')"
36
43
  /></span>
37
-
38
44
  <span class="absolute inset-0 z-1" />
39
45
  </button>
40
46
  </h4>
41
47
  <CopyButton
42
- :label="$t('Copy link')"
43
- :copied-label="$t('Link copied!')"
48
+ :label="$t('Copier le lien')"
49
+ :copied-label="$t('Lien copié !')"
44
50
  :text="resourceExternalUrl"
45
51
  class="z-2"
46
52
  />
@@ -50,7 +56,7 @@
50
56
  :resource
51
57
  class="dash-after"
52
58
  />
53
- <span class="fr-text--xs fr-mb-0 dash-after">{{ t('Updated {date}', { date: formatRelativeIfRecentDate(lastUpdate) }) }}</span>
59
+ <span class="fr-text--xs fr-mb-0 dash-after">{{ t('Mis à jour {date}', { date: formatRelativeIfRecentDate(lastUpdate) }) }}</span>
54
60
  <span
55
61
  v-if="resource.format"
56
62
  class="fr-text--xs fr-mb-0 dash-after"
@@ -61,17 +67,17 @@
61
67
  </span>
62
68
  <span
63
69
  class="inline-flex items-center fr-text--xs fr-mb-0"
64
- :aria-label="t('{n} downloads', resource.metrics.views)"
70
+ :aria-label="t('{n} téléchargements', resource.metrics.views)"
65
71
  >
66
72
  <span class="fr-icon-download-line fr-icon--xs fr-mr-1v" />
67
- <span>{{ summarize(resource.metrics.views) }} <span class="hidden show-on-small">{{ t("downloads") }}</span></span>
73
+ <span>{{ summarize(resource.metrics.views) }} <span class="hidden show-on-small">{{ t("téléchargements") }}</span></span>
68
74
  </span>
69
75
  </div>
70
76
  <p
71
77
  v-if="communityResource"
72
78
  class="fr-mb-0 fr-mt-1v fr-text--xs text-gray-medium"
73
79
  >
74
- {{ t('From') }}
80
+ {{ t('Par') }}
75
81
  <a
76
82
  v-if="communityResource.organization"
77
83
  class="fr-link fr-text--xs"
@@ -84,26 +90,21 @@
84
90
  </template>
85
91
  </p>
86
92
  </div>
87
- <div class="flex items-center fr-ml-4v buttons">
88
- <p
89
- v-if="unavailable"
90
- class="text-default-warning fr-m-0 fr-mr-2v"
91
- >
92
- {{ t('Unavailable') }}
93
- </p>
93
+ <div class="flex items-center buttons">
94
94
  <p
95
95
  v-if="resource.format === 'url'"
96
96
  class="fr-col-auto fr-ml-3v fr-m-0 z-2"
97
97
  >
98
98
  <BrandedButton
99
99
  :href="resource.latest"
100
- :title="t('File link - opens a new window')"
100
+ :title="t('Lien du fichier - ouvre une nouvelle fenêtre')"
101
101
  :aria-describedby="resourceTitleId"
102
102
  rel="ugc nofollow noopener"
103
103
  new-tab
104
104
  size="xs"
105
+ external
105
106
  >
106
- {{ $t('Visit') }}
107
+ {{ $t('Visiter') }}
107
108
  </BrandedButton>
108
109
  </p>
109
110
  <p
@@ -118,7 +119,7 @@
118
119
  size="xs"
119
120
  :icon="RiFileCopyLine"
120
121
  >
121
- {{ t('Copy link') }}
122
+ {{ t('Copier le lien') }}
122
123
  </BrandedButton>
123
124
  </p>
124
125
  <p
@@ -128,14 +129,15 @@
128
129
  <BrandedButton
129
130
  :href="resource.latest"
130
131
  rel="ugc nofollow noopener"
131
- :title="t('Download file')"
132
+ :title="downloadButtonTitle"
132
133
  download
133
134
  class="relative text-transform-uppercase matomo_download z-2"
134
- :icon="RiDownloadLine"
135
+ :icon="unavailable ? RiFileWarningLine : RiDownloadLine"
135
136
  size="xs"
136
137
  :aria-describedby="resourceTitleId"
138
+ external
137
139
  >
138
- <span class="sr-only">{{ t('Download file as ') }}</span>{{ format }}
140
+ <span class="sr-only">{{ t('Télécharger la liste au format ') }}</span>{{ format }}
139
141
  </BrandedButton>
140
142
  </p>
141
143
  <p
@@ -146,6 +148,7 @@
146
148
  :dataset-id="dataset.id"
147
149
  :resource-id="resource.id"
148
150
  :is-community-resource="isCommunityResource"
151
+ size="xs"
149
152
  />
150
153
  </p>
151
154
  <div
@@ -177,13 +180,42 @@
177
180
  <TabPanel
178
181
  v-for="tab in tabsOptions"
179
182
  :key="tab.key"
183
+ class="px-4"
180
184
  >
185
+ <div v-if="tab.key === 'map'">
186
+ <Pmtiles
187
+ v-if="hasPmtiles"
188
+ :resource="resource"
189
+ />
190
+ <MapContainer
191
+ v-if="ogcWms"
192
+ :resource="resource"
193
+ />
194
+ </div>
181
195
  <div v-if="tab.key === 'data'">
182
- <Preview :resource="resource" />
196
+ <!-- Show JSON viewer for JSON files -->
197
+ <JsonPreview
198
+ v-if="resource.format && resource.format.toLowerCase() === 'json'"
199
+ :resource="resource"
200
+ />
201
+ <!-- Show PDF viewer for PDF files -->
202
+ <PdfPreview
203
+ v-else-if="resource.format && resource.format.toLowerCase() === 'pdf'"
204
+ :resource="resource"
205
+ />
206
+ <!-- Show XML viewer for XML files -->
207
+ <XmlPreview
208
+ v-else-if="resource.format && resource.format.toLowerCase() === 'xml'"
209
+ :resource="resource"
210
+ />
211
+ <!-- Show regular preview for other file types -->
212
+ <Preview
213
+ v-else
214
+ :resource="resource"
215
+ />
183
216
  </div>
184
217
  <div
185
218
  v-if="tab.key === 'description'"
186
- class="fr-pl-4v fr-pr-4v"
187
219
  >
188
220
  <div
189
221
  class="fr-mt-0 markdown fr-text--sm text-mention-grey"
@@ -192,37 +224,34 @@
192
224
  </div>
193
225
  <div
194
226
  v-if="tab.key === 'data-structure'"
195
- class="fr-pl-4v fr-pr-4v"
196
227
  >
197
228
  <DataStructure
198
- v-if="hasPreview"
229
+ v-if="hasTabularData"
199
230
  :resource="resource"
200
231
  />
201
232
  </div>
202
233
  <div
203
234
  v-if="tab.key === 'metadata'"
204
- class="fr-pl-4v fr-pr-4v"
205
235
  >
206
236
  <Metadata :resource />
207
237
  </div>
208
238
  <div
209
239
  v-if="tab.key === 'downloads'"
210
- class="fr-pl-4v fr-pr-4v"
211
240
  >
212
241
  <dl class="fr-pl-0">
213
242
  <dt
214
243
  v-if="resource.format === 'url'"
215
244
  class="font-bold fr-text--sm fr-mb-0"
216
245
  >
217
- {{ $t('Original URL') }}
246
+ {{ $t("URL d'origine") }}
218
247
  </dt>
219
248
  <dt
220
249
  v-else
221
250
  class="font-bold fr-text--sm fr-mb-0"
222
251
  >
223
- {{ $t('Original format') }}
252
+ {{ $t('Format original') }}
224
253
  </dt>
225
- <dd class="text-sm ml-0 mt-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center">
254
+ <dd class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center">
226
255
  <span v-if="resource.format === 'url'">
227
256
  <a
228
257
  :href="resource.latest"
@@ -254,31 +283,35 @@
254
283
  </a>
255
284
  </span>
256
285
  <CopyButton
257
- :label="$t('Copy link')"
258
- :copied-label="$t('Link copied!')"
286
+ :label="$t('Copier le lien')"
287
+ :copied-label="$t('Lien copié !')"
259
288
  :text="resource.latest"
260
289
  class="relative"
261
290
  />
262
291
  </dd>
263
- <template v-if="resource.extras['analysis:parsing:parquet_url']">
292
+ <template v-if="generatedFormats.length">
264
293
  <dt class="font-bold fr-text--sm fr-mb-0">
265
- {{ $t('Auto-generated formats from {platform}', { platform: config.name }) }}
294
+ {{ $t('Formats générés automatiquement par {platform} (dernière mise à jour {date})', { platform: config.name, date: conversionsLastUpdate }) }}
266
295
  </dt>
267
- <dd class="text-sm ml-0 mt-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center">
296
+ <dd
297
+ v-for="generatedFormat in generatedFormats"
298
+ :key="generatedFormat.format"
299
+ class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center"
300
+ >
268
301
  <span>
269
302
  <span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
270
303
  <a
271
- :href="resource.extras['analysis:parsing:parquet_url']"
304
+ :href="generatedFormat.url"
272
305
  class="fr-link"
273
306
  rel="ugc nofollow noopener"
274
307
  >
275
- <span>{{ $t('Format {format}', { format: 'parquet' }) }}<span v-if="resource.extras['analysis:parsing:parquet_size']"> - {{ filesize(resource.extras['analysis:parsing:parquet_size']) }}</span></span>
308
+ <span>{{ $t('Format {format}', { format: generatedFormat.format }) }}<span v-if="generatedFormat.size"> - {{ filesize(generatedFormat.size) }}</span></span>
276
309
  </a>
277
310
  </span>
278
311
  <CopyButton
279
- :label="$t('Copy link')"
280
- :copied-label="$t('Link copied!')"
281
- :text="resource.extras['analysis:parsing:parquet_url']"
312
+ :label="$t('Copier le lien')"
313
+ :copied-label="$t('Lien copié !')"
314
+ :text="generatedFormat.url"
282
315
  class="relative"
283
316
  />
284
317
  </dd>
@@ -287,11 +320,10 @@
287
320
  </div>
288
321
  <div
289
322
  v-if="tab.key === 'swagger'"
290
- class="fr-pl-4v fr-pr-4v"
291
323
  >
292
- <div>{{ t('Swagger automatically generated by data.gouv.fr. This swagger allows you to query data by API by filtering it by column value.') }}</div>
324
+ <div>{{ t("Swagger généré automatiquement par {platform}. Ce swagger vous permet d'interroger les données par API en les filtrant par valeur de colonne.", { platform: config.name }) }}</div>
293
325
  <Swagger
294
- v-if="hasPreview"
326
+ v-if="hasTabularData"
295
327
  :url="`${config.tabularApiUrl}/api/resources/${props.resource.id}/swagger/`"
296
328
  />
297
329
  </div>
@@ -305,11 +337,11 @@
305
337
  <script setup lang="ts">
306
338
  import { ref, computed, defineAsyncComponent } from 'vue'
307
339
  import { useI18n } from 'vue-i18n'
308
- import { RiDownloadLine, RiFileCopyLine } from '@remixicon/vue'
340
+ import { RiDownloadLine, RiFileCopyLine, RiFileWarningLine } from '@remixicon/vue'
309
341
  import OrganizationNameWithCertificate from '../OrganizationNameWithCertificate.vue'
310
342
  import { filesize, summarize } from '../../functions/helpers'
311
343
  import { markdown } from '../../functions/markdown'
312
- import { formatRelativeIfRecentDate } from '../../functions/dates'
344
+ import { useFormatDate } from '../../functions/dates'
313
345
  import type { CommunityResource, Resource } from '../../types/resources'
314
346
  import type { Dataset, DatasetV2 } from '../../types/datasets'
315
347
  import TabGroup from '../Tabs/TabGroup.vue'
@@ -321,7 +353,7 @@ import { trackEvent } from '../../functions/matomo'
321
353
  import CopyButton from '../CopyButton.vue'
322
354
  import { useComponentsConfig } from '../../config'
323
355
  import { getOwnerName } from '../../functions/owned'
324
- import { getResourceFormatIcon, getResourceTitleId } from '../../functions/resources'
356
+ import { getResourceFormatIcon, getResourceTitleId, detectOgcService } from '../../functions/resources'
325
357
  import BrandedButton from '../BrandedButton.vue'
326
358
  import { getResourceExternalUrl } from '../../functions/datasets'
327
359
  import Metadata from './Metadata.vue'
@@ -331,7 +363,7 @@ import EditButton from './EditButton.vue'
331
363
  import DataStructure from './DataStructure.vue'
332
364
  import Preview from './Preview.vue'
333
365
 
334
- const OGC_SERVICES_FORMATS = ['ogc:wfs', 'ogc:wms', 'wfs', 'wms']
366
+ const GENERATED_FORMATS = ['parquet', 'pmtiles', 'geojson']
335
367
 
336
368
  const props = withDefaults(defineProps<{
337
369
  dataset: Dataset | DatasetV2
@@ -347,20 +379,59 @@ const props = withDefaults(defineProps<{
347
379
 
348
380
  const config = useComponentsConfig()
349
381
 
350
- const Swagger = defineAsyncComponent(() => import('./Swagger.vue'))
382
+ const Swagger = defineAsyncComponent(() => import('./Swagger.client.vue'))
383
+ const MapContainer = defineAsyncComponent(() => import('./MapContainer.client.vue'))
384
+ const Pmtiles = defineAsyncComponent(() => import('./Pmtiles.client.vue'))
385
+ const JsonPreview = defineAsyncComponent(() => import('./JsonPreview.client.vue'))
386
+ const PdfPreview = defineAsyncComponent(() => import('./PdfPreview.client.vue'))
387
+ const XmlPreview = defineAsyncComponent(() => import('./XmlPreview.client.vue'))
351
388
 
352
389
  const { t } = useI18n()
390
+ const { formatRelativeIfRecentDate } = useFormatDate()
353
391
 
354
392
  const hasPreview = computed(() => {
393
+ // For JSON, PDF, and XML files, show preview.
394
+ // We cannot check for CORS issues here because we cannot use an async component here.
395
+ // If there is a CORS issue when fetching the file for preview, it will be managed and displayed as an error banner by the preview component.
396
+ const format = props.resource.format?.toLowerCase()
397
+ return format === 'json' || format === 'pdf' || format === 'xml'
398
+ })
399
+
400
+ const hasTabularData = computed(() => {
401
+ // Determines if we should show the "Données" tab for tabular files AND the "Structure des données" tab (for tabular data structure)
355
402
  return config.tabularApiUrl
356
- && props.resource.extras['analysis:parsing:finished_at']
403
+ && props.resource.extras['analysis:parsing:parsing_table']
357
404
  && !props.resource.extras['analysis:parsing:error']
358
405
  && (config.tabularAllowRemote || props.resource.filetype === 'file')
359
406
  })
360
407
 
361
- const format = computed(() => getResourceFormatIcon(props.resource.format) ? props.resource.format : t('File'))
408
+ const hasPmtiles = computed(() => {
409
+ return props.resource.extras['analysis:parsing:pmtiles_url']
410
+ })
362
411
 
363
- const ogcService = computed(() => OGC_SERVICES_FORMATS.includes(props.resource.format))
412
+ const format = computed(() => getResourceFormatIcon(props.resource.format) ? props.resource.format : t('Fichier'))
413
+
414
+ const ogcService = computed(() => detectOgcService(props.resource))
415
+
416
+ const ogcWms = computed(() => ogcService.value === 'wms')
417
+
418
+ const generatedFormats = computed(() => {
419
+ const formats = GENERATED_FORMATS
420
+ .filter(format => `analysis:parsing:${format}_url` in props.resource.extras)
421
+ .map(format => ({
422
+ url: props.resource.extras[`analysis:parsing:${format}_url`] as string,
423
+ size: props.resource.extras[`analysis:parsing:${format}_size`] as number | undefined,
424
+ format: format,
425
+ }))
426
+ if ('analysis:parsing:parsing_table' in props.resource.extras) {
427
+ formats.push({
428
+ url: `${config.tabularApiUrl}/api/resources/${props.resource.id}/data/json/`,
429
+ size: undefined,
430
+ format: 'json',
431
+ })
432
+ }
433
+ return formats
434
+ })
364
435
 
365
436
  const open = ref(props.expandedOnMount)
366
437
  const toggle = () => {
@@ -377,22 +448,26 @@ const toggle = () => {
377
448
  const tabsOptions = computed(() => {
378
449
  const options = []
379
450
 
380
- if (hasPreview.value) {
381
- options.push({ key: 'data', label: t('Data') })
451
+ if (hasPmtiles.value || ogcWms.value) {
452
+ options.push({ key: 'map', label: t('Carte') })
453
+ }
454
+
455
+ if (hasTabularData.value || hasPreview.value) {
456
+ options.push({ key: 'data', label: t('Aperçu') })
382
457
  }
383
458
 
384
459
  if (props.resource.description) {
385
460
  options.push({ key: 'description', label: t('Description') })
386
461
  }
387
462
 
388
- if (hasPreview.value) {
389
- options.push({ key: 'data-structure', label: t('Data structure') })
463
+ if (hasTabularData.value) {
464
+ options.push({ key: 'data-structure', label: t('Structure des données') })
390
465
  }
391
466
 
392
- options.push({ key: 'metadata', label: t('Metadata') })
393
- options.push({ key: 'downloads', label: t('Downloads') })
467
+ options.push({ key: 'metadata', label: t('Métadonnées') })
468
+ options.push({ key: 'downloads', label: t('Téléchargements') })
394
469
 
395
- if (hasPreview.value) {
470
+ if (hasTabularData.value) {
396
471
  options.push({ key: 'swagger', label: t('Swagger') })
397
472
  }
398
473
 
@@ -417,8 +492,11 @@ const communityResource = computed<CommunityResource | null>(() => {
417
492
  const owner = computed(() => communityResource.value ? getOwnerName(communityResource.value) : null)
418
493
 
419
494
  const lastUpdate = props.resource.last_modified
495
+ const conversionsLastUpdate = computed(() => formatRelativeIfRecentDate(props.resource.extras['analysis:parsing:finished_at']))
420
496
  const availabilityChecked = props.resource.extras && 'check:available' in props.resource.extras
497
+
421
498
  const unavailable = availabilityChecked && props.resource.extras['check:available'] === false
499
+ const downloadButtonTitle = unavailable ? t(`Le robot de {certifier} n'a pas pu accéder à ce fichier - Télécharger le fichier en {format}`, { certifier: config.name, format: format.value }) : t(`Télécharger le fichier en {format}`, { format: format.value })
422
500
 
423
501
  const resourceExternalUrl = computed(() => getResourceExternalUrl(props.dataset, props.resource))
424
502
 
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <component
3
- :is="(resource.filetype === 'remote' && resource.format ? getResourceFormatIcon(resource.format) : null) || File"
3
+ :is="(resource.format ? getResourceFormatIcon(resource.format) : null) || File"
4
4
  class="text-gray-800 shrink-0"
5
5
  />
6
6
  </template>
@@ -11,6 +11,12 @@ import File from '../Icons/File.vue'
11
11
  import { getResourceFormatIcon } from '../../functions/resources'
12
12
 
13
13
  defineProps<{
14
+ // We would like to use Resource | ResourceForm but ResourceForm is not in
15
+ // datagouv/components yet. We don't use `filetype` but we need otherwise
16
+ // TS don't like passing some object without `format` because it would have
17
+ // nothing in common with `{}`. We could do `{ format: … } | object` but it's
18
+ // really generic and I prefer to have something like `filetype` which is specific
19
+ // to resources' objects.
14
20
  resource: { format?: string | null, filetype: ResourceFileType | null }
15
21
  }>()
16
22
  </script>
@@ -1,94 +1,94 @@
1
1
  <template>
2
2
  <span
3
3
  v-if="title"
4
- class="inline-flex fr-mb-0 align-items-baseline fr-text--xs"
4
+ class="inline-flex mb-0 items-baseline text-xs"
5
5
  >
6
6
  <Toggletip
7
7
  position="right"
8
8
  no-margin
9
- class="relative z-2"
9
+ class="relative z-2 -ml-3 top-1 -my-3"
10
10
  >
11
11
  <template #toggletip="{ close }">
12
12
  <div class="flex justify-between border-bottom">
13
- <h5 class="fr-text--sm fr-my-0 fr-p-2v">{{ $t("Data schema") }}</h5>
13
+ <h5 class="fr-text--sm fr-my-0 fr-p-2v">{{ $t("Schéma de données") }}</h5>
14
14
  <button
15
15
  type="button"
16
- :title="t('Close')"
16
+ :title="t('Fermer')"
17
17
  class="border-left close-button flex items-center justify-center"
18
18
  @click="close"
19
19
  >&times;</button>
20
20
  </div>
21
- <div class="fr-p-3v">
21
+ <div class="p-3">
22
22
  <div v-if="validataStatus === 'ok'">
23
- {{ t("This file is valid for the shema:") }} <component
23
+ {{ t("Ce fichier est valide pour le schéma :") }} <component
24
24
  :is="documentationUrl ? 'a' : 'span'"
25
25
  :href="documentationUrl"
26
26
  class="fr-link fr-text--sm"
27
27
  >{{ title }}</component>.
28
28
  </div>
29
29
  <div v-if="validataStatus === 'warnings'">
30
- {{ t("This file is valid for the shema:") }} <component
30
+ {{ t("Ce fichier est valide pour le schéma :") }} <component
31
31
  :is="documentationUrl ? 'a' : 'span'"
32
32
  :href="documentationUrl"
33
33
  class="fr-link fr-text--sm"
34
- >{{ title }}</component>. {{ t("But its compliance could be improved.") }}
34
+ >{{ title }}</component>. {{ t("Mais sa conformité peut être améliorée.") }}
35
35
  </div>
36
36
  <div v-if="validataStatus === 'ko'">
37
- {{ t("This file indicates to follow the schema:") }} <component
37
+ {{ t("Ce fichier indique suivre le schéma :") }} <component
38
38
  :is="documentationUrl ? 'a' : 'span'"
39
39
  :href="documentationUrl"
40
40
  class="fr-link fr-text--sm"
41
- >{{ title }}</component>. {{ t("But is not compliant.") }}
41
+ >{{ title }}</component>. {{ t("Mais n'est pas conforme.") }}
42
42
  </div>
43
43
 
44
44
  <div
45
45
  v-if="validataWarnings.length"
46
- class="text-default-warning flex items-center fr-mt-4v"
46
+ class="text-default-warning flex items-center mt-4"
47
47
  >
48
- <span class="fr-icon-alert-line fr-icon--sm fr-mr-1v" />
49
- <span>{{ validataWarnings.length }} {{ t('advices') }}</span>
48
+ <span class="fr-icon-alert-line fr-icon--sm mr-1" />
49
+ <span>{{ validataWarnings.length }} {{ t('recommandations') }}</span>
50
50
  </div>
51
51
  <div
52
52
  v-if="validataStructureErrors.length"
53
- class="text-default-warning flex items-center fr-mt-4v"
53
+ class="text-default-warning flex items-center mt-4"
54
54
  >
55
- <span class="fr-icon-alert-line fr-icon--sm fr-mr-1v" />
56
- <span>{{ validataStructureErrors.length }} {{ t('structure errors') }}</span>
55
+ <span class="fr-icon-alert-line fr-icon--sm mr-1" />
56
+ <span>{{ validataStructureErrors.length }} {{ t('erreurs de structures') }}</span>
57
57
  </div>
58
58
  <div
59
59
  v-if="validataBodyErrors.length"
60
- class="text-default-warning flex items-center fr-mt-4v"
60
+ class="text-default-warning flex items-center mt-4"
61
61
  >
62
- <span class="fr-icon-alert-line fr-icon--sm fr-mr-1v" />
63
- <span>{{ validataBodyErrors.length }} {{ t('body errors') }}</span>
62
+ <span class="fr-icon-alert-line fr-icon--sm mr-1" />
63
+ <span>{{ validataBodyErrors.length }} {{ t('erreurs de contenus') }}</span>
64
64
  </div>
65
65
 
66
66
  <div
67
67
  v-if="validationUrl"
68
- class="w-100 text-align-right fr-mt-5v"
68
+ class="w-full text-right mt-5"
69
69
  target="_blank"
70
70
  >
71
- <a :href="validationUrl">{{ t('See validation report') }}</a>
71
+ <a :href="validationUrl">{{ t('Voir le rapport de validation') }}</a>
72
72
  </div>
73
73
  </div>
74
74
  </template>
75
75
  </Toggletip>
76
- <span class="fr-mr-1v text-gray-medium">{{ t("Schema:") }}</span>
76
+ <span class="mr-1 text-gray-medium">{{ t("Schéma:") }}</span>
77
77
  <span class="flex items-center bg-danger-lightest rounded-sm">
78
78
  <span class="fr-tag fr-tag--sm">{{ title }}</span>
79
79
  <span
80
80
  v-if="validataStatus === 'warnings'"
81
81
  class="flex items-center padding-sm"
82
82
  >
83
- <span class="fr-icon-alert-line fr-icon--sm fr-mr-1v" />
84
- <span>{{ t("Invalid") }}</span>
83
+ <span class="fr-icon-alert-line fr-icon--sm mr-1" />
84
+ <span>{{ t("Invalide") }}</span>
85
85
  </span>
86
86
  <span
87
87
  v-if="validataStatus === 'ko'"
88
88
  class="flex items-center text-warning-dark padding-sm"
89
89
  >
90
- <span class="fr-icon-error-line fr-icon--sm fr-mr-1v" />
91
- <span>{{ t("Invalid") }}</span>
90
+ <span class="fr-icon-error-line fr-icon--sm mr-1" />
91
+ <span>{{ t("Invalide") }}</span>
92
92
  </span>
93
93
  </span>
94
94
  </span>
@@ -6,7 +6,7 @@
6
6
  import { onMounted } from 'vue'
7
7
  import SwaggerUI from 'swagger-ui'
8
8
  import 'swagger-ui/dist/swagger-ui.css'
9
- import 'swagger-themes/themes/newspaper.css'
9
+ import '../../../assets/swagger-themes/newspaper.css'
10
10
 
11
11
  const props = defineProps<{
12
12
  url: string