@datagouv/components-next 1.0.2-dev.20 → 1.0.2-dev.200

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 (165) hide show
  1. package/README.md +7 -6
  2. package/assets/main.css +4 -0
  3. package/dist/{Control-DuZJdKV_.js → Control-B9WQxlpJ.js} +1 -1
  4. package/dist/{Datafair.client-Ds3VQrRi.js → Datafair.client-CFTmBFoY.js} +9 -9
  5. package/dist/{Event--kp8kMdJ.js → Event-iXDrp-fy.js} +31 -31
  6. package/dist/{Image-34hvypZI.js → Image-mNMBOofs.js} +9 -9
  7. package/dist/ImagePreview.client-BvDM3qjE.js +36 -0
  8. package/dist/JsonPreview.client-BP6gPRxf.js +40 -0
  9. package/dist/{Map-BjUnLyj8.js → Map-BfqKMw0S.js} +1739 -1599
  10. package/dist/{MapContainer.client-B2BijMx5.js → MapContainer.client-D9N3664r.js} +41 -42
  11. package/dist/{OSM-s40W6sQ2.js → OSM-CcrUhItr.js} +15 -23
  12. package/dist/{PdfPreview.client-DJBMzrlC.js → PdfPreview.client-C6A6ZiKl.js} +835 -856
  13. package/dist/Pmtiles.client-B_sDZoqu.js +26333 -0
  14. package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-DGLXBys_.js +60 -0
  15. package/dist/{ScaleLine-KW-nXqp3.js → ScaleLine-VGQ7nkMS.js} +2 -2
  16. package/dist/{Tile-DbNFNPfU.js → Tile-CSpn0Ca8.js} +160 -163
  17. package/dist/{TileImage-BsXBxMtq.js → TileImage-CpbaEFwd.js} +143 -131
  18. package/dist/{View-BR92hTWP.js → View-BYBpt-AU.js} +483 -422
  19. package/dist/XmlPreview.client-DFQ1T-LN.js +34 -0
  20. package/dist/{common-PJfpC179.js → common-BoeTUnDZ.js} +52 -52
  21. package/dist/components-next.css +1 -52
  22. package/dist/components-next.js +196 -150
  23. package/dist/components.css +2 -2
  24. package/dist/{index-BFQX7Ccz.js → index-B_tPYi5Z.js} +34094 -27864
  25. package/dist/{leaflet-src-7m1mB8LI.js → leaflet-src-PeylHk0o.js} +162 -164
  26. package/dist/{main-vqhkVhVS.js → main-CVOZp_Nx.js} +41939 -73132
  27. package/dist/{proj-DsetBcW7.js → proj-DxGbobp7.js} +662 -626
  28. package/dist/{text-clamp.esm-B5kW_XMt.js → text-clamp.esm-DVseBqZZ.js} +4 -4
  29. package/dist/{tilecoord-Db24Px13.js → tilecoord-DvX7WWiA.js} +273 -264
  30. package/dist/{vue3-json-viewer-BXwup7nO.js → vue3-json-viewer-CJLQFzXZ.js} +10 -16
  31. package/dist/{vue3-xml-viewer.common-BcNJo35o.js → vue3-xml-viewer.common-CcF3Xd23.js} +1 -1
  32. package/package.json +47 -38
  33. package/src/chart.ts +5 -0
  34. package/src/components/ActivityList/ActivityList.vue +24 -14
  35. package/src/components/AppLink.vue +3 -0
  36. package/src/components/BannerAction.vue +6 -2
  37. package/src/components/Chart/ChartViewer.vue +320 -0
  38. package/src/components/Chart/ChartViewerWrapper.vue +182 -0
  39. package/src/components/CopyButton.vue +6 -2
  40. package/src/components/DataserviceCard.vue +24 -5
  41. package/src/components/DataserviceQuality.vue +82 -0
  42. package/src/components/DataserviceQualityInline.vue +31 -0
  43. package/src/components/DataserviceQualityTooltipContent.vue +79 -0
  44. package/src/components/DatasetCard.vue +24 -9
  45. package/src/components/DatasetInformation/DatasetTemporalitySection.vue +7 -4
  46. package/src/components/DatasetQuality.vue +11 -11
  47. package/src/components/DatasetQualityInline.vue +2 -2
  48. package/src/components/DatasetQualityTooltipContent.vue +1 -1
  49. package/src/components/DiscussionMessageCard.vue +14 -15
  50. package/src/components/Form/Listbox.vue +101 -0
  51. package/src/components/Form/SchemaSelect.vue +2 -0
  52. package/src/components/FormattedDate.vue +81 -0
  53. package/src/components/InfiniteLoader.vue +53 -0
  54. package/src/components/LoadingBlock.vue +4 -1
  55. package/src/components/ObjectCardHeader.vue +11 -4
  56. package/src/components/OpenApiViewer/ContentTypeSelect.vue +48 -0
  57. package/src/components/OpenApiViewer/EndpointRequest.vue +164 -0
  58. package/src/components/OpenApiViewer/EndpointResponses.vue +149 -0
  59. package/src/components/OpenApiViewer/OpenApiViewer.vue +308 -0
  60. package/src/components/OpenApiViewer/SchemaPanel.vue +53 -0
  61. package/src/components/OpenApiViewer/SchemaTree.vue +77 -0
  62. package/src/components/OpenApiViewer/openapi.ts +150 -0
  63. package/src/components/OrganizationNameWithCertificate.vue +3 -2
  64. package/src/components/Pagination.vue +25 -62
  65. package/src/components/PostCard.vue +13 -15
  66. package/src/components/RadioInput.vue +7 -2
  67. package/src/components/ResourceAccordion/DataStructure.vue +11 -33
  68. package/src/components/ResourceAccordion/Datafair.client.vue +1 -1
  69. package/src/components/ResourceAccordion/Downloads.vue +172 -0
  70. package/src/components/ResourceAccordion/ImagePreview.client.vue +48 -0
  71. package/src/components/ResourceAccordion/JsonPreview.client.vue +23 -104
  72. package/src/components/ResourceAccordion/MapContainer.client.vue +2 -11
  73. package/src/components/ResourceAccordion/Metadata.vue +4 -6
  74. package/src/components/ResourceAccordion/PdfPreview.client.vue +58 -97
  75. package/src/components/ResourceAccordion/PdfSkeleton.vue +44 -0
  76. package/src/components/ResourceAccordion/Pmtiles.client.vue +28 -13
  77. package/src/components/ResourceAccordion/Preview.vue +19 -15
  78. package/src/components/ResourceAccordion/PreviewWrapper.vue +89 -0
  79. package/src/components/ResourceAccordion/ResourceAccordion.vue +36 -117
  80. package/src/components/ResourceAccordion/XmlPreview.client.vue +16 -98
  81. package/src/components/ResourceExplorer/ResourceExplorer.vue +105 -210
  82. package/src/components/ResourceExplorer/ResourceExplorerHeader.vue +79 -0
  83. package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +104 -71
  84. package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +143 -291
  85. package/src/components/ResourceExplorer/ResourceMainAction.vue +240 -0
  86. package/src/components/ResourceExplorer/ResourceSelector.vue +51 -0
  87. package/src/components/ResourceExplorer/ResourceViewerHeader.vue +122 -0
  88. package/src/components/ResourceExplorer/ResourceViewerSkeleton.vue +94 -0
  89. package/src/components/ResourceListItem.vue +168 -0
  90. package/src/components/ReuseCard.vue +28 -9
  91. package/src/components/ReuseDetails.vue +2 -2
  92. package/src/components/ReuseHorizontalCard.vue +2 -2
  93. package/src/components/SchemaCard.vue +10 -1
  94. package/src/components/Search/Filter/AccessTypeFilter.vue +2 -2
  95. package/src/components/Search/GlobalSearch.vue +229 -123
  96. package/src/components/Search/SearchInput.vue +5 -4
  97. package/src/components/Search/Sidemenu.vue +4 -3
  98. package/src/components/SmallChart.vue +11 -10
  99. package/src/components/StatBox.vue +13 -5
  100. package/src/components/TabularExplorer/TabularActiveFilters.vue +79 -0
  101. package/src/components/TabularExplorer/TabularCell.vue +50 -0
  102. package/src/components/TabularExplorer/TabularCellPopover.vue +170 -0
  103. package/src/components/TabularExplorer/TabularColumnsMenu.vue +112 -0
  104. package/src/components/TabularExplorer/TabularExplorer.vue +299 -0
  105. package/src/components/TabularExplorer/TabularFilterContent.vue +362 -0
  106. package/src/components/TabularExplorer/TabularFilterPopover.vue +106 -0
  107. package/src/components/TabularExplorer/TabularMobileFilterButton.vue +21 -0
  108. package/src/components/TabularExplorer/TabularMobileFilters.vue +150 -0
  109. package/src/components/TabularExplorer/TabularRowsInfo.vue +31 -0
  110. package/src/components/TabularExplorer/TabularSkeleton.vue +92 -0
  111. package/src/components/TabularExplorer/TabularTable.vue +481 -0
  112. package/src/components/TabularExplorer/TabularToolbar.vue +25 -0
  113. package/src/components/TabularExplorer/types.ts +84 -0
  114. package/src/components/TabularExplorer/useColumnMetadata.ts +98 -0
  115. package/src/components/TabularExplorer/useTabularContext.ts +82 -0
  116. package/src/components/TopicCard.vue +13 -14
  117. package/src/composables/useActiveDescendant.ts +5 -0
  118. package/src/composables/useDatasetResources.ts +164 -0
  119. package/src/composables/useHasTabularData.ts +15 -1
  120. package/src/composables/useMetrics.ts +1 -1
  121. package/src/composables/useResizable.ts +46 -0
  122. package/src/composables/useResourceCapabilities.ts +48 -40
  123. package/src/composables/useSearchFilter.ts +120 -0
  124. package/src/composables/useStableQueryParams.ts +38 -6
  125. package/src/composables/useTabularProfile.ts +70 -0
  126. package/src/composables/useTranslation.ts +24 -9
  127. package/src/config.ts +21 -3
  128. package/src/functions/activities.ts +3 -3
  129. package/src/functions/api.ts +20 -42
  130. package/src/functions/api.types.ts +1 -0
  131. package/src/functions/charts.ts +99 -0
  132. package/src/functions/datasets.ts +0 -17
  133. package/src/functions/dates.ts +6 -7
  134. package/src/functions/helpers.ts +15 -0
  135. package/src/functions/metrics.ts +63 -60
  136. package/src/functions/paginate.ts +46 -0
  137. package/src/functions/resourceCapabilities.ts +34 -0
  138. package/src/functions/resources.ts +136 -1
  139. package/src/functions/schemas.ts +27 -0
  140. package/src/functions/scroll.ts +20 -0
  141. package/src/functions/tabular.ts +214 -0
  142. package/src/functions/tabularApi.ts +96 -11
  143. package/src/main.ts +119 -14
  144. package/src/types/dataservices.ts +17 -0
  145. package/src/types/organizations.ts +10 -0
  146. package/src/types/pages.ts +0 -5
  147. package/src/types/posts.ts +2 -2
  148. package/src/types/reports.ts +5 -1
  149. package/src/types/resources.ts +0 -1
  150. package/src/types/reuses.ts +12 -1
  151. package/src/types/search.ts +63 -1
  152. package/src/types/site.ts +5 -3
  153. package/src/types/ui.ts +2 -0
  154. package/src/types/users.ts +2 -1
  155. package/src/types/visualizations.ts +93 -0
  156. package/assets/swagger-themes/newspaper.css +0 -1670
  157. package/dist/JsonPreview.client-CYxmz0yV.js +0 -72
  158. package/dist/Pmtiles.client-C_hvRDuy.js +0 -25185
  159. package/dist/Swagger.client-B4y0hXME.js +0 -4
  160. package/dist/XmlPreview.client-LETEfNBH.js +0 -64
  161. package/src/components/ResourceAccordion/Swagger.client.vue +0 -48
  162. package/src/functions/pagination.ts +0 -9
  163. /package/src/components/{DatasetQualityItem.vue → QualityItem.vue} +0 -0
  164. /package/src/components/{DatasetQualityItemWarning.vue → QualityItemWarning.vue} +0 -0
  165. /package/src/components/{DatasetQualityScore.vue → QualityScore.vue} +0 -0
@@ -56,11 +56,21 @@
56
56
  :resource
57
57
  />
58
58
  <RiSubtractLine
59
- v-if="resource.schema"
59
+ v-if="resource.schema?.name || resource.schema?.url"
60
60
  aria-hidden="true"
61
61
  class="size-3 fill-gray-medium"
62
62
  />
63
- <span class="text-xs mb-0">{{ t('Mis à jour {date}', { date: formatRelativeIfRecentDate(lastUpdate) }) }}</span>
63
+ <TranslationT
64
+ class="text-xs mb-0"
65
+ keypath="Mis à jour {date}"
66
+ >
67
+ <template #date>
68
+ <FormattedDate
69
+ :date="lastUpdate"
70
+ format="relative"
71
+ />
72
+ </template>
73
+ </TranslationT>
64
74
  <RiSubtractLine
65
75
  aria-hidden="true"
66
76
  class="size-3 fill-gray-medium"
@@ -197,7 +207,10 @@
197
207
  :key="tab.key"
198
208
  class="px-4"
199
209
  >
200
- <div v-if="tab.key === 'map'">
210
+ <div
211
+ v-if="tab.key === 'map'"
212
+ class="-mx-4 h-[600px]"
213
+ >
201
214
  <Pmtiles
202
215
  v-if="hasPmtiles"
203
216
  :resource="resource"
@@ -224,6 +237,10 @@
224
237
  v-else-if="resource.format && resource.format.toLowerCase() === 'xml'"
225
238
  :resource="resource"
226
239
  />
240
+ <ImagePreview
241
+ v-else-if="isImagePreviewFormat(resource.format)"
242
+ :resource="resource"
243
+ />
227
244
  <!-- Show Datafair embedded preview (koumoul) -->
228
245
  <DatafairPreview
229
246
  v-else-if="hasDatafairPreview"
@@ -231,7 +248,7 @@
231
248
  :dataset="dataset"
232
249
  />
233
250
  <!-- Show Datafair embedded preview (koumoul) -->
234
- <SwaggerClient
251
+ <OpenApiViewer
235
252
  v-else-if="hasOpenAPIPreview"
236
253
  :url="resource.extras['apidocUrl'] as string"
237
254
  />
@@ -265,91 +282,13 @@
265
282
  <div
266
283
  v-if="tab.key === 'downloads'"
267
284
  >
268
- <dl class="fr-pl-0">
269
- <dt
270
- v-if="resource.format === 'url'"
271
- class="font-bold fr-text--sm fr-mb-0"
272
- >
273
- {{ t("URL d'origine") }}
274
- </dt>
275
- <dt
276
- v-else
277
- class="font-bold fr-text--sm fr-mb-0"
278
- >
279
- {{ t('Format original') }}
280
- </dt>
281
- <dd class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center">
282
- <span v-if="resource.format === 'url'">
283
- <a
284
- :href="resource.latest"
285
- class="fr-link no-icon-after"
286
- rel="ugc nofollow noopener"
287
- target="_blank"
288
- @click="trackEvent('Jeux de données', 'Télécharger un fichier', 'Bouton : télécharger un fichier')"
289
- >
290
- <component
291
- :is="config.textClamp"
292
- v-if="config && config.textClamp"
293
- :auto-resize="true"
294
- :max-lines="1"
295
- :text="resource.url"
296
- >
297
- <template #after>
298
- <span class="fr-ml-1v fr-icon-external-link-line fr-icon--sm" />
299
- </template>
300
- </component>
301
- </a>
302
- </span>
303
- <span v-else>
304
- <span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
305
- <a
306
- :href="resource.latest"
307
- class="fr-link"
308
- rel="ugc nofollow noopener"
309
- @click="trackEvent('Jeux de données', 'Télécharger un fichier', `Bouton : format ${resource.format}`)"
310
- >
311
- <span>{{ t('Format {format}', { format: resource.format }) }}<span v-if="resourceFilesize"> - {{ filesize(resourceFilesize) }}</span></span>
312
- </a>
313
- </span>
314
- <CopyButton
315
- :label="t('Copier le lien')"
316
- :copied-label="t('Lien copié !')"
317
- :text="resource.latest"
318
- class="relative"
319
- />
320
- </dd>
321
- <template v-if="generatedFormats.length">
322
- <dt class="font-bold fr-text--sm fr-mb-0">
323
- {{ t('Formats générés automatiquement par {platform} (dernière mise à jour {date})', { platform: config.name, date: conversionsLastUpdate }) }}
324
- </dt>
325
- <dd
326
- v-for="generatedFormat in generatedFormats"
327
- :key="generatedFormat.format"
328
- class="text-sm pl-0 mb-4 text-gray-medium h-8 flex flex-wrap items-center"
329
- >
330
- <span>
331
- <span class="text-datagouv fr-icon-download-line fr-icon--sm fr-mr-1v fr-mt-1v" />
332
- <a
333
- :href="generatedFormat.url"
334
- class="fr-link"
335
- rel="ugc nofollow noopener"
336
- @click="trackEvent('Jeux de données', 'Télécharger un fichier', `Bouton : format ${generatedFormat.format}`)"
337
- >
338
- <span>{{ t('Format {format}', { format: generatedFormat.format }) }}<span v-if="generatedFormat.size"> - {{ filesize(generatedFormat.size) }}</span></span>
339
- </a>
340
- </span>
341
- <CopyButton
342
- :label="t('Copier le lien')"
343
- :copied-label="t('Lien copié !')"
344
- :text="generatedFormat.url"
345
- class="relative"
346
- />
347
- </dd>
348
- </template>
349
- </dl>
285
+ <Downloads
286
+ :resource="resource"
287
+ :dataset="dataset"
288
+ />
350
289
  </div>
351
290
  <div
352
- v-if="tab.key === 'swagger'"
291
+ v-if="tab.key === 'api'"
353
292
  >
354
293
  <div class="fr-mb-4w">
355
294
  <p>{{ t("Cette API est générée automatiquement par {platform} à partir du fichier.", { platform: config.name }) }}</p>
@@ -357,7 +296,7 @@
357
296
  <p>{{ t("- Si le fichier est supprimé, l'API sera également supprimée.") }}</p>
358
297
  <p>{{ t("Pour des usages pérennes, prévoyez que cette API dépend directement du fichier source.") }}</p>
359
298
  </div>
360
- <Swagger
299
+ <OpenApiViewer
361
300
  v-if="hasTabularData"
362
301
  :url="`${config.tabularApiUrl}/api/resources/${props.resource.id}/swagger/`"
363
302
  />
@@ -374,7 +313,8 @@ import { ref, computed, defineAsyncComponent } from 'vue'
374
313
  import { RiDownloadLine, RiFileCopyLine, RiFileWarningLine, RiSubtractLine } from '@remixicon/vue'
375
314
  import OrganizationNameWithCertificate from '../OrganizationNameWithCertificate.vue'
376
315
  import { filesize, summarize } from '../../functions/helpers'
377
- import { useFormatDate } from '../../functions/dates'
316
+ import FormattedDate from '../FormattedDate.vue'
317
+ import TranslationT from '../TranslationT.vue'
378
318
  import MarkdownViewer from '../MarkdownViewer.vue'
379
319
  import type { CommunityResource, Resource } from '../../types/resources'
380
320
  import type { Dataset, DatasetV2 } from '../../types/datasets'
@@ -387,9 +327,8 @@ import { trackEvent } from '../../functions/matomo'
387
327
  import CopyButton from '../CopyButton.vue'
388
328
  import { useComponentsConfig } from '../../config'
389
329
  import { getOwnerName } from '../../functions/owned'
390
- import { getResourceFormatIcon, getResourceTitleId, detectOgcService } from '../../functions/resources'
330
+ import { getResourceFormatIcon, getResourceTitleId, detectOgcService, getResourceExternalUrl, getResourceFilesize, isImagePreviewFormat } from '../../functions/resources'
391
331
  import BrandedButton from '../BrandedButton.vue'
392
- import { getResourceExternalUrl, getResourceFilesize } from '../../functions/datasets'
393
332
  import { useTranslation } from '../../composables/useTranslation'
394
333
  import { useHasTabularData } from '../../composables/useHasTabularData'
395
334
  import Metadata from './Metadata.vue'
@@ -397,11 +336,11 @@ import SchemaBadge from './SchemaBadge.vue'
397
336
  import ResourceIcon from './ResourceIcon.vue'
398
337
  import EditButton from './EditButton.vue'
399
338
  import DataStructure from './DataStructure.vue'
339
+ import Downloads from './Downloads.vue'
400
340
  import Preview from './Preview.vue'
401
341
  import { isOrganizationCertified } from '../../functions/organizations'
402
- import SwaggerClient from './Swagger.client.vue'
342
+ import OpenApiViewer from '../OpenApiViewer/OpenApiViewer.vue'
403
343
 
404
- const GENERATED_FORMATS = ['parquet', 'pmtiles', 'geojson']
405
344
  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']
406
345
 
407
346
  const props = withDefaults(defineProps<{
@@ -418,24 +357,23 @@ const props = withDefaults(defineProps<{
418
357
 
419
358
  const config = useComponentsConfig()
420
359
 
421
- const Swagger = defineAsyncComponent(() => import('./Swagger.client.vue'))
422
360
  const MapContainer = defineAsyncComponent(() => import('./MapContainer.client.vue'))
423
361
  const Pmtiles = defineAsyncComponent(() => import('./Pmtiles.client.vue'))
424
362
  const JsonPreview = defineAsyncComponent(() => import('./JsonPreview.client.vue'))
425
363
  const PdfPreview = defineAsyncComponent(() => import('./PdfPreview.client.vue'))
426
364
  const XmlPreview = defineAsyncComponent(() => import('./XmlPreview.client.vue'))
365
+ const ImagePreview = defineAsyncComponent(() => import('./ImagePreview.client.vue'))
427
366
  const DatafairPreview = defineAsyncComponent(() => import('./Datafair.client.vue'))
428
367
 
429
368
  const { t } = useTranslation()
430
- const { formatRelativeIfRecentDate } = useFormatDate()
431
369
  const checkTabularData = useHasTabularData()
432
370
 
433
371
  const hasPreview = computed(() => {
434
- // For JSON, PDF, and XML files, show preview.
372
+ // For JSON, PDF, XML, and image files, show preview.
435
373
  // We cannot check for CORS issues here because we cannot use an async component here.
436
374
  // 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.
437
375
  const format = props.resource.format?.toLowerCase()
438
- return format === 'json' || format === 'pdf' || format === 'xml'
376
+ return format === 'json' || format === 'pdf' || format === 'xml' || isImagePreviewFormat(format)
439
377
  })
440
378
 
441
379
  const hasTabularData = computed(() => checkTabularData(props.resource))
@@ -462,24 +400,6 @@ const ogcService = computed(() => detectOgcService(props.resource))
462
400
 
463
401
  const ogcWms = computed(() => ogcService.value === 'wms')
464
402
 
465
- const generatedFormats = computed(() => {
466
- const formats = GENERATED_FORMATS
467
- .filter(format => `analysis:parsing:${format}_url` in props.resource.extras)
468
- .map(format => ({
469
- url: props.resource.extras[`analysis:parsing:${format}_url`] as string,
470
- size: props.resource.extras[`analysis:parsing:${format}_size`] as number | undefined,
471
- format: format,
472
- }))
473
- if ('analysis:parsing:parsing_table' in props.resource.extras) {
474
- formats.push({
475
- url: `${config.tabularApiUrl}/api/resources/${props.resource.id}/data/json/`,
476
- size: undefined,
477
- format: 'json',
478
- })
479
- }
480
- return formats
481
- })
482
-
483
403
  const open = ref(props.expandedOnMount)
484
404
  const toggle = () => {
485
405
  open.value = !open.value
@@ -515,7 +435,7 @@ const tabsOptions = computed(() => {
515
435
  options.push({ key: 'downloads', label: t('Téléchargements') })
516
436
 
517
437
  if (hasTabularData.value) {
518
- options.push({ key: 'swagger', label: t('Swagger') })
438
+ options.push({ key: 'api', label: t('API') })
519
439
  }
520
440
 
521
441
  return options
@@ -542,7 +462,6 @@ const communityResource = computed<CommunityResource | null>(() => {
542
462
  const owner = computed(() => communityResource.value ? getOwnerName(communityResource.value) : null)
543
463
 
544
464
  const lastUpdate = props.resource.last_modified
545
- const conversionsLastUpdate = computed(() => formatRelativeIfRecentDate(props.resource.extras['analysis:parsing:finished_at'] as string | undefined))
546
465
  const availabilityChecked = props.resource.extras && 'check:available' in props.resource.extras
547
466
  const resourceFilesize = computed(() => getResourceFilesize(props.resource))
548
467
 
@@ -1,42 +1,24 @@
1
1
  <template>
2
- <div class="fr-text--xs">
3
- <div v-if="xmlData">
4
- <XmlViewer :xml="xmlData" />
5
- </div>
6
- <div
7
- v-else-if="loading"
8
- class="text-gray-medium"
9
- >
10
- {{ t("Chargement de l'aperçu XML...") }}
11
- </div>
12
- <PreviewUnavailable v-else-if="fileTooLarge">
13
- {{ fileSizeBytes
14
- ? t("Le fichier XML est trop volumineux pour être prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.")
15
- : t("La taille du fichier est inconnue, l'aperçu n'est pas disponible. Téléchargez-le depuis l'onglet Téléchargements.")
16
- }}
17
- </PreviewUnavailable>
18
- <PreviewUnavailable v-else-if="error === 'network'">
19
- {{ t("Ce fichier est hébergé sur un site externe qui ne permet pas la prévisualisation. Téléchargez-le depuis l'onglet Téléchargements.") }}
20
- </PreviewUnavailable>
21
- <PreviewUnavailable v-else-if="error">
22
- {{ t("L'aperçu de ce fichier n'a pas pu être chargé. Téléchargez-le depuis l'onglet Téléchargements.") }}
23
- </PreviewUnavailable>
24
- </div>
2
+ <PreviewWrapper
3
+ v-slot="{ data }"
4
+ file-type="XML"
5
+ :resource="resource"
6
+ :max-size="config.maxXmlPreviewCharSize"
7
+ :load="load"
8
+ >
9
+ <XmlViewer :xml="(data as string)" />
10
+ </PreviewWrapper>
25
11
  </template>
26
12
 
27
13
  <script setup lang="ts">
28
- import { computed, defineAsyncComponent, onMounted, ref } from 'vue'
14
+ import { defineAsyncComponent } from 'vue'
29
15
  import { useComponentsConfig } from '../../config'
30
- import PreviewUnavailable from './PreviewUnavailable.vue'
16
+ import PreviewWrapper from './PreviewWrapper.vue'
31
17
  import type { Resource } from '../../types/resources'
32
- import { useTranslation } from '../../composables/useTranslation'
33
18
  import '../../types/vue3-xml-viewer.d'
34
- import { getResourceFilesize } from '../../main'
35
19
 
36
20
  const XmlViewer = defineAsyncComponent(() =>
37
- import('vue3-xml-viewer').then((module) => {
38
- return module.default || module.XmlViewer
39
- }),
21
+ import('vue3-xml-viewer').then(module => module.default || module.XmlViewer),
40
22
  )
41
23
 
42
24
  const props = defineProps<{
@@ -44,74 +26,10 @@ const props = defineProps<{
44
26
  }>()
45
27
 
46
28
  const config = useComponentsConfig()
47
- const { t } = useTranslation()
48
29
 
49
- const xmlData = ref<string | null>(null)
50
- const loading = ref(false)
51
- const error = ref<string | null>(null)
52
- const fileTooLarge = ref(false)
53
-
54
- const fileSizeBytes = computed(() => getResourceFilesize(props.resource))
55
-
56
- const shouldLoadXml = computed(() => {
57
- const size = fileSizeBytes.value
58
- if (!size) {
59
- // If we don't know the size, don't risk loading a potentially huge file
60
- return false
61
- }
62
-
63
- // Check if maxXmlPreviewCharSize is configured
64
- if (!config.maxXmlPreviewCharSize) {
65
- // If no limit is set, don't load unknown files
66
- return false
67
- }
68
-
69
- // Convert maxXmlPreviewCharSize from characters to bytes (rough estimate)
70
- // Assuming average 1 byte per character for XML
71
- const maxByteSize = config.maxXmlPreviewCharSize
72
-
73
- return size <= maxByteSize
74
- })
75
-
76
- const fetchXmlData = async () => {
77
- // Check if file is too large or size is unknown before making the request
78
- if (!shouldLoadXml.value) {
79
- fileTooLarge.value = true
80
- return
81
- }
82
-
83
- loading.value = true
84
- error.value = null
85
-
86
- try {
87
- const response = await fetch(props.resource.url)
88
- // const response = await fetch('/test-data.xml') // For testing locally without CORS issues
89
- if (!response.ok) {
90
- throw new Error(`HTTP error! status: ${response.status}`)
91
- }
92
- const data = await response.text()
93
-
94
- // Use the XML data as string - let the XML viewer handle large files
95
- xmlData.value = data
96
- }
97
- catch (err) {
98
- console.error('Error loading XML:', err)
99
-
100
- if (err instanceof TypeError) {
101
- error.value = 'network'
102
- }
103
- else {
104
- error.value = 'generic'
105
- }
106
-
107
- xmlData.value = null
108
- }
109
- finally {
110
- loading.value = false
111
- }
30
+ const load = async () => {
31
+ const response = await fetch(props.resource.url)
32
+ if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)
33
+ return response.text()
112
34
  }
113
-
114
- onMounted(() => {
115
- fetchXmlData()
116
- })
117
35
  </script>