@datagouv/components-next 1.0.2-dev.11 → 1.0.2-dev.110

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/assets/main.css +4 -0
  2. package/dist/{Control-DuZJdKV_.js → Control-ZFh5ta_U.js} +1 -1
  3. package/dist/{Datafair.client-8haHXl47.js → Datafair.client-rf4T1IkA.js} +1 -1
  4. package/dist/{Event--kp8kMdJ.js → Event-DSQcW7OF.js} +24 -24
  5. package/dist/{Image-34hvypZI.js → Image-BijNEG0p.js} +6 -6
  6. package/dist/JsonPreview.client-dzar6iuh.js +40 -0
  7. package/dist/{Map-BjUnLyj8.js → Map-BUtPf5GN.js} +756 -756
  8. package/dist/{MapContainer.client-l6HuXTHR.js → MapContainer.client-D-MoRNhG.js} +37 -38
  9. package/dist/{OSM-s40W6sQ2.js → OSM-D4MTdBtk.js} +2 -2
  10. package/dist/{PdfPreview.client-4OueK-2Z.js → PdfPreview.client-DoDYLmJD.js} +822 -850
  11. package/dist/{Pmtiles.client-4j3VTYkz.js → Pmtiles.client-Dzm01Zfm.js} +1 -1
  12. package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-BRNYswg3.js +61 -0
  13. package/dist/{ScaleLine-KW-nXqp3.js → ScaleLine-hJQIqcZm.js} +2 -2
  14. package/dist/{Tile-DbNFNPfU.js → Tile-Dcl7oIVu.js} +35 -35
  15. package/dist/{TileImage-BsXBxMtq.js → TileImage-BJeHipMX.js} +4 -4
  16. package/dist/{View-BR92hTWP.js → View-xp_P_OHw.js} +412 -401
  17. package/dist/XmlPreview.client-cOhwff6P.js +34 -0
  18. package/dist/{common-PJfpC179.js → common-BjQlan3k.js} +36 -36
  19. package/dist/components-next.css +6 -6
  20. package/dist/components-next.js +165 -142
  21. package/dist/components.css +1 -1
  22. package/dist/{index-CVTIoZQ0.js → index-NofRBuyf.js} +32886 -27183
  23. package/dist/main-Iz1ZCL6k.js +73606 -0
  24. package/dist/{proj-DsetBcW7.js → proj-CsNo9yH1.js} +532 -512
  25. package/dist/{tilecoord-Db24Px13.js → tilecoord-A0fLnBZr.js} +28 -28
  26. package/dist/{vue3-xml-viewer.common-CWer_T5-.js → vue3-xml-viewer.common-tVI9uXUz.js} +1 -1
  27. package/package.json +25 -11
  28. package/src/chart.ts +5 -0
  29. package/src/components/ActivityList/ActivityList.vue +3 -2
  30. package/src/components/Chart/ChartViewer.vue +226 -0
  31. package/src/components/Chart/ChartViewerWrapper.vue +170 -0
  32. package/src/components/DataserviceCard.vue +3 -0
  33. package/src/components/DatasetCard.vue +9 -4
  34. package/src/components/Form/Listbox.vue +101 -0
  35. package/src/components/Form/SearchableSelect.vue +2 -1
  36. package/src/components/InfiniteLoader.vue +53 -0
  37. package/src/components/ObjectCardHeader.vue +11 -4
  38. package/src/components/OpenApiViewer/ContentTypeSelect.vue +48 -0
  39. package/src/components/OpenApiViewer/EndpointRequest.vue +164 -0
  40. package/src/components/OpenApiViewer/EndpointResponses.vue +149 -0
  41. package/src/components/OpenApiViewer/OpenApiViewer.vue +308 -0
  42. package/src/components/OpenApiViewer/SchemaPanel.vue +53 -0
  43. package/src/components/OpenApiViewer/SchemaTree.vue +77 -0
  44. package/src/components/OpenApiViewer/openapi.ts +150 -0
  45. package/src/components/OrganizationNameWithCertificate.vue +3 -2
  46. package/src/components/Pagination.vue +8 -5
  47. package/src/components/RadioInput.vue +7 -2
  48. package/src/components/ReadMore.vue +1 -1
  49. package/src/components/ResourceAccordion/DataStructure.vue +11 -33
  50. package/src/components/ResourceAccordion/Downloads.vue +160 -0
  51. package/src/components/ResourceAccordion/JsonPreview.client.vue +23 -104
  52. package/src/components/ResourceAccordion/MapContainer.client.vue +1 -3
  53. package/src/components/ResourceAccordion/Metadata.vue +1 -2
  54. package/src/components/ResourceAccordion/PdfPreview.client.vue +24 -87
  55. package/src/components/ResourceAccordion/Preview.vue +11 -11
  56. package/src/components/ResourceAccordion/PreviewWrapper.vue +82 -0
  57. package/src/components/ResourceAccordion/ResourceAccordion.vue +10 -109
  58. package/src/components/ResourceAccordion/XmlPreview.client.vue +16 -98
  59. package/src/components/ResourceExplorer/ResourceExplorer.vue +14 -10
  60. package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +2 -2
  61. package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +46 -147
  62. package/src/components/ResourceExplorer/ResourceSelector.vue +113 -0
  63. package/src/components/ReuseCard.vue +12 -4
  64. package/src/components/Search/GlobalSearch.vue +201 -113
  65. package/src/components/Search/SearchInput.vue +5 -4
  66. package/src/components/TabularExplorer/TabularCell.vue +51 -0
  67. package/src/components/TabularExplorer/TabularCellPopover.vue +170 -0
  68. package/src/components/TabularExplorer/TabularExplorer.vue +973 -0
  69. package/src/components/TabularExplorer/TabularFilterContent.vue +351 -0
  70. package/src/components/TabularExplorer/TabularFilterPopover.vue +111 -0
  71. package/src/components/TabularExplorer/types.ts +83 -0
  72. package/src/composables/useHasTabularData.ts +13 -0
  73. package/src/composables/useMetrics.ts +1 -1
  74. package/src/composables/useSearchFilter.ts +118 -0
  75. package/src/composables/useStableQueryParams.ts +38 -6
  76. package/src/composables/useTabularProfile.ts +70 -0
  77. package/src/config.ts +20 -3
  78. package/src/functions/activities.ts +3 -3
  79. package/src/functions/api.ts +9 -37
  80. package/src/functions/api.types.ts +1 -0
  81. package/src/functions/charts.ts +68 -0
  82. package/src/functions/datasets.ts +0 -17
  83. package/src/functions/metrics.ts +6 -4
  84. package/src/functions/resources.ts +56 -1
  85. package/src/functions/tabular.ts +60 -0
  86. package/src/functions/tabularApi.ts +138 -11
  87. package/src/main.ts +90 -9
  88. package/src/types/dataservices.ts +2 -0
  89. package/src/types/pages.ts +0 -5
  90. package/src/types/posts.ts +2 -2
  91. package/src/types/reports.ts +5 -1
  92. package/src/types/search.ts +63 -1
  93. package/src/types/site.ts +5 -3
  94. package/src/types/ui.ts +2 -0
  95. package/src/types/users.ts +2 -1
  96. package/src/types/visualizations.ts +89 -0
  97. package/assets/swagger-themes/newspaper.css +0 -1670
  98. package/dist/JsonPreview.client-D53pj9Cw.js +0 -72
  99. package/dist/Swagger.client-DPBmsH9q.js +0 -4
  100. package/dist/XmlPreview.client-XElkoA4F.js +0 -64
  101. package/dist/main-BbT-LUXy.js +0 -105854
  102. package/src/components/ResourceAccordion/Swagger.client.vue +0 -48
  103. package/src/functions/pagination.ts +0 -9
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <div class="text-xs">
3
+ <slot
4
+ v-if="data !== null"
5
+ :data="data"
6
+ />
7
+ <div
8
+ v-else-if="loading"
9
+ class="text-gray-medium"
10
+ >
11
+ {{ t("Chargement de l'aperçu {fileType}...", { fileType }) }}
12
+ </div>
13
+ <PreviewUnavailable v-else-if="!isSizeAllowed">
14
+ {{ fileSizeBytes
15
+ ? t("Le fichier {fileType} est trop volumineux pour être prévisualisé. Téléchargez-le depuis l'onglet Téléchargements.", { fileType })
16
+ : t("La taille du fichier est inconnue, l'aperçu n'est pas disponible. Téléchargez-le depuis l'onglet Téléchargements.")
17
+ }}
18
+ </PreviewUnavailable>
19
+ <PreviewUnavailable v-else-if="corsStatus === 'blocked'">
20
+ {{ t("Ce fichier {fileType} ne peut pas être prévisualisé car il est hébergé sur un site distant qui restreint l'accès (CORS). Téléchargez-le depuis l'onglet Téléchargements.", { fileType }) }}
21
+ </PreviewUnavailable>
22
+ <PreviewUnavailable v-else-if="error === 'network'">
23
+ {{ 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.") }}
24
+ </PreviewUnavailable>
25
+ <PreviewUnavailable v-else-if="error">
26
+ {{ t("L'aperçu de ce fichier n'a pas pu être chargé. Téléchargez-le depuis l'onglet Téléchargements.") }}
27
+ </PreviewUnavailable>
28
+ </div>
29
+ </template>
30
+
31
+ <script setup lang="ts">
32
+ import { computed, nextTick, onMounted, ref } from 'vue'
33
+ import PreviewUnavailable from './PreviewUnavailable.vue'
34
+ import type { Resource } from '../../types/resources'
35
+ import { getResourceFilesize, getResourceCorsStatus } from '../../functions/resources'
36
+ import { useTranslation } from '../../composables/useTranslation'
37
+
38
+ const props = defineProps<{
39
+ fileType: string
40
+ resource: Resource
41
+ maxSize: number | undefined
42
+ load: () => Promise<unknown>
43
+ }>()
44
+
45
+ const emit = defineEmits<{
46
+ loaded: []
47
+ }>()
48
+
49
+ const { t } = useTranslation()
50
+
51
+ const data = ref<unknown>(null)
52
+ const loading = ref(false)
53
+ const error = ref<'network' | 'generic' | null>(null)
54
+
55
+ const fileSizeBytes = computed(() => getResourceFilesize(props.resource))
56
+ const corsStatus = computed(() => getResourceCorsStatus(props.resource))
57
+
58
+ const isSizeAllowed = computed(() => {
59
+ const size = fileSizeBytes.value
60
+ const max = props.maxSize
61
+ if (!size || !max) return false
62
+ return size <= max
63
+ })
64
+
65
+ onMounted(async () => {
66
+ if (!isSizeAllowed.value || corsStatus.value === 'blocked') return
67
+
68
+ loading.value = true
69
+ try {
70
+ data.value = await props.load()
71
+ await nextTick()
72
+ emit('loaded')
73
+ }
74
+ catch (err) {
75
+ console.error('Error loading preview:', err)
76
+ error.value = err instanceof TypeError ? 'network' : 'generic'
77
+ }
78
+ finally {
79
+ loading.value = false
80
+ }
81
+ })
82
+ </script>
@@ -56,7 +56,7 @@
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
  />
@@ -231,7 +231,7 @@
231
231
  :dataset="dataset"
232
232
  />
233
233
  <!-- Show Datafair embedded preview (koumoul) -->
234
- <SwaggerClient
234
+ <OpenApiViewer
235
235
  v-else-if="hasOpenAPIPreview"
236
236
  :url="resource.extras['apidocUrl'] as string"
237
237
  />
@@ -265,88 +265,10 @@
265
265
  <div
266
266
  v-if="tab.key === 'downloads'"
267
267
  >
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>
268
+ <Downloads
269
+ :resource="resource"
270
+ :dataset="dataset"
271
+ />
350
272
  </div>
351
273
  <div
352
274
  v-if="tab.key === 'swagger'"
@@ -357,7 +279,7 @@
357
279
  <p>{{ t("- Si le fichier est supprimé, l'API sera également supprimée.") }}</p>
358
280
  <p>{{ t("Pour des usages pérennes, prévoyez que cette API dépend directement du fichier source.") }}</p>
359
281
  </div>
360
- <Swagger
282
+ <OpenApiViewer
361
283
  v-if="hasTabularData"
362
284
  :url="`${config.tabularApiUrl}/api/resources/${props.resource.id}/swagger/`"
363
285
  />
@@ -387,9 +309,8 @@ import { trackEvent } from '../../functions/matomo'
387
309
  import CopyButton from '../CopyButton.vue'
388
310
  import { useComponentsConfig } from '../../config'
389
311
  import { getOwnerName } from '../../functions/owned'
390
- import { getResourceFormatIcon, getResourceTitleId, detectOgcService } from '../../functions/resources'
312
+ import { getResourceFormatIcon, getResourceTitleId, detectOgcService, getResourceExternalUrl, getResourceFilesize } from '../../functions/resources'
391
313
  import BrandedButton from '../BrandedButton.vue'
392
- import { getResourceExternalUrl, getResourceFilesize } from '../../functions/datasets'
393
314
  import { useTranslation } from '../../composables/useTranslation'
394
315
  import { useHasTabularData } from '../../composables/useHasTabularData'
395
316
  import Metadata from './Metadata.vue'
@@ -397,11 +318,11 @@ import SchemaBadge from './SchemaBadge.vue'
397
318
  import ResourceIcon from './ResourceIcon.vue'
398
319
  import EditButton from './EditButton.vue'
399
320
  import DataStructure from './DataStructure.vue'
321
+ import Downloads from './Downloads.vue'
400
322
  import Preview from './Preview.vue'
401
323
  import { isOrganizationCertified } from '../../functions/organizations'
402
- import SwaggerClient from './Swagger.client.vue'
324
+ import OpenApiViewer from '../OpenApiViewer/OpenApiViewer.vue'
403
325
 
404
- const GENERATED_FORMATS = ['parquet', 'pmtiles', 'geojson']
405
326
  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
327
 
407
328
  const props = withDefaults(defineProps<{
@@ -418,7 +339,6 @@ const props = withDefaults(defineProps<{
418
339
 
419
340
  const config = useComponentsConfig()
420
341
 
421
- const Swagger = defineAsyncComponent(() => import('./Swagger.client.vue'))
422
342
  const MapContainer = defineAsyncComponent(() => import('./MapContainer.client.vue'))
423
343
  const Pmtiles = defineAsyncComponent(() => import('./Pmtiles.client.vue'))
424
344
  const JsonPreview = defineAsyncComponent(() => import('./JsonPreview.client.vue'))
@@ -462,24 +382,6 @@ const ogcService = computed(() => detectOgcService(props.resource))
462
382
 
463
383
  const ogcWms = computed(() => ogcService.value === 'wms')
464
384
 
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
385
  const open = ref(props.expandedOnMount)
484
386
  const toggle = () => {
485
387
  open.value = !open.value
@@ -542,7 +444,6 @@ const communityResource = computed<CommunityResource | null>(() => {
542
444
  const owner = computed(() => communityResource.value ? getOwnerName(communityResource.value) : null)
543
445
 
544
446
  const lastUpdate = props.resource.last_modified
545
- const conversionsLastUpdate = computed(() => formatRelativeIfRecentDate(props.resource.extras['analysis:parsing:finished_at'] as string | undefined))
546
447
  const availabilityChecked = props.resource.extras && 'check:available' in props.resource.extras
547
448
  const resourceFilesize = computed(() => getResourceFilesize(props.resource))
548
449
 
@@ -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>
@@ -7,6 +7,8 @@
7
7
  :key="selectedResource.id"
8
8
  :dataset
9
9
  :resource="selectedResource"
10
+ :resources="flatResources"
11
+ @select="selectResource"
10
12
  />
11
13
  <div
12
14
  v-else-if="search"
@@ -30,16 +32,18 @@
30
32
  </BrandedButton>
31
33
  </div>
32
34
  </div>
33
- <ResourceExplorerSidebar
34
- :resources="allResources"
35
- :selected-resource-id="selectedResource?.id ?? null"
36
- :collapsed="sidebarCollapsed"
37
- :search
38
- @select="selectResource"
39
- @load-more="loadMore"
40
- @update:collapsed="sidebarCollapsed = $event"
41
- @update:search="updateSearch($event)"
42
- />
35
+ <div class="hidden md:block">
36
+ <ResourceExplorerSidebar
37
+ :resources="allResources"
38
+ :selected-resource-id="selectedResource?.id ?? null"
39
+ :collapsed="sidebarCollapsed"
40
+ :search
41
+ @select="selectResource"
42
+ @load-more="loadMore"
43
+ @update:collapsed="sidebarCollapsed = $event"
44
+ @update:search="updateSearch($event)"
45
+ />
46
+ </div>
43
47
  </div>
44
48
  </div>
45
49
  <div
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <aside
3
3
  v-if="!collapsed"
4
- class="w-72 shrink-0 pl-4"
4
+ class="w-full md:w-72 shrink-0 p-4 md:pr-0"
5
5
  >
6
6
  <div class="flex items-center justify-between mb-3">
7
7
  <h3 class="text-sm font-bold uppercase mb-0">
@@ -32,7 +32,7 @@
32
32
  >
33
33
  </div>
34
34
 
35
- <div class="space-y-4 overflow-y-auto">
35
+ <div class="space-y-4 overflow-y-auto md:max-h-[calc(100vh-14rem)]">
36
36
  <div
37
37
  v-for="group in resources"
38
38
  :key="group.type"