@datagouv/components-next 0.0.1

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 (119) hide show
  1. package/README.md +150 -0
  2. package/assets/main.css +136 -0
  3. package/assets/placeholders/author.png +0 -0
  4. package/assets/placeholders/dataset.png +0 -0
  5. package/assets/placeholders/news.png +0 -0
  6. package/assets/placeholders/organization.png +0 -0
  7. package/assets/placeholders/reuse.png +0 -0
  8. package/assets/tailwind.config.js +24 -0
  9. package/dist/components.css +2 -0
  10. package/dist/locales/de.js +155 -0
  11. package/dist/locales/en.js +155 -0
  12. package/dist/locales/es.js +155 -0
  13. package/dist/locales/fr.js +155 -0
  14. package/dist/locales/it.js +155 -0
  15. package/dist/locales/pt.js +155 -0
  16. package/dist/locales/sr.js +155 -0
  17. package/package.json +72 -0
  18. package/src/components/AppLink.vue +51 -0
  19. package/src/components/Avatar.vue +27 -0
  20. package/src/components/AvatarWithName.vue +26 -0
  21. package/src/components/BannerAction.vue +39 -0
  22. package/src/components/BrandedButton.vue +170 -0
  23. package/src/components/CopyButton.vue +84 -0
  24. package/src/components/DataserviceCard.vue +184 -0
  25. package/src/components/DatasetCard.vue +198 -0
  26. package/src/components/DatasetInformationPanel.vue +210 -0
  27. package/src/components/DatasetQuality.vue +68 -0
  28. package/src/components/DatasetQualityInline.vue +32 -0
  29. package/src/components/DatasetQualityItem.vue +32 -0
  30. package/src/components/DatasetQualityItemWarning.vue +21 -0
  31. package/src/components/DatasetQualityScore.vue +35 -0
  32. package/src/components/DatasetQualityTooltipContent.vue +79 -0
  33. package/src/components/DescriptionDetails.vue +23 -0
  34. package/src/components/DescriptionList/DescriptionDetails.stories.ts +43 -0
  35. package/src/components/DescriptionList/DescriptionList.stories.ts +47 -0
  36. package/src/components/DescriptionList/DescriptionTerm.stories.ts +28 -0
  37. package/src/components/DescriptionList.vue +8 -0
  38. package/src/components/DescriptionTerm.vue +8 -0
  39. package/src/components/ExtraAccordion.vue +78 -0
  40. package/src/components/Icons/Archive.vue +21 -0
  41. package/src/components/Icons/Code.vue +21 -0
  42. package/src/components/Icons/Documentation.vue +21 -0
  43. package/src/components/Icons/File.vue +21 -0
  44. package/src/components/Icons/Image.vue +7 -0
  45. package/src/components/Icons/Link.vue +21 -0
  46. package/src/components/Icons/Table.vue +21 -0
  47. package/src/components/OrganizationCard.vue +68 -0
  48. package/src/components/OrganizationNameWithCertificate.vue +45 -0
  49. package/src/components/OwnerType.vue +43 -0
  50. package/src/components/OwnerTypeIcon.vue +18 -0
  51. package/src/components/Pagination.vue +205 -0
  52. package/src/components/Placeholder.vue +29 -0
  53. package/src/components/ReadMore.vue +107 -0
  54. package/src/components/ResourceAccordion/DataStructure.vue +87 -0
  55. package/src/components/ResourceAccordion/EditButton.vue +34 -0
  56. package/src/components/ResourceAccordion/Metadata.vue +171 -0
  57. package/src/components/ResourceAccordion/Preview.vue +229 -0
  58. package/src/components/ResourceAccordion/PreviewLoader.vue +148 -0
  59. package/src/components/ResourceAccordion/ResourceAccordion.vue +484 -0
  60. package/src/components/ResourceAccordion/ResourceIcon.vue +16 -0
  61. package/src/components/ResourceAccordion/SchemaBadge.vue +148 -0
  62. package/src/components/ResourceAccordion/SchemaLoader.vue +30 -0
  63. package/src/components/ResourceAccordion/Swagger.vue +46 -0
  64. package/src/components/ResourceAccordion/france.svg +1 -0
  65. package/src/components/ReuseCard.vue +106 -0
  66. package/src/components/ReuseDetails.vue +45 -0
  67. package/src/components/SimpleBanner.vue +24 -0
  68. package/src/components/SmallChart.vue +149 -0
  69. package/src/components/StatBox.vue +100 -0
  70. package/src/components/Tabs/Tab.vue +62 -0
  71. package/src/components/Tabs/TabGroup.vue +20 -0
  72. package/src/components/Tabs/TabList.vue +15 -0
  73. package/src/components/Tabs/TabPanel.vue +7 -0
  74. package/src/components/Tabs/TabPanels.vue +7 -0
  75. package/src/components/Toggletip.vue +62 -0
  76. package/src/components/ToggletipButton.vue +14 -0
  77. package/src/composables/useActiveDescendant.ts +103 -0
  78. package/src/composables/useReuseType.ts +14 -0
  79. package/src/config.ts +33 -0
  80. package/src/functions/api.ts +96 -0
  81. package/src/functions/api.types.ts +41 -0
  82. package/src/functions/config.ts +12 -0
  83. package/src/functions/datasets.ts +24 -0
  84. package/src/functions/dates.ts +85 -0
  85. package/src/functions/helpers.ts +38 -0
  86. package/src/functions/markdown.ts +47 -0
  87. package/src/functions/matomo.ts +3 -0
  88. package/src/functions/organizations.ts +85 -0
  89. package/src/functions/owned.ts +11 -0
  90. package/src/functions/resources.ts +99 -0
  91. package/src/functions/reuses.ts +28 -0
  92. package/src/functions/schemas.ts +96 -0
  93. package/src/functions/tabularApi.ts +27 -0
  94. package/src/functions/users.ts +7 -0
  95. package/src/locales/de.json +154 -0
  96. package/src/locales/en.json +154 -0
  97. package/src/locales/es.json +154 -0
  98. package/src/locales/fr.json +154 -0
  99. package/src/locales/it.json +154 -0
  100. package/src/locales/pt.json +154 -0
  101. package/src/locales/sr.json +154 -0
  102. package/src/main.ts +147 -0
  103. package/src/types/badges.ts +5 -0
  104. package/src/types/contact_point.ts +7 -0
  105. package/src/types/dataservices.ts +68 -0
  106. package/src/types/datasets.ts +80 -0
  107. package/src/types/frequency.ts +6 -0
  108. package/src/types/granularity.ts +6 -0
  109. package/src/types/harvest.ts +3 -0
  110. package/src/types/keyboard.ts +1 -0
  111. package/src/types/licenses.ts +9 -0
  112. package/src/types/organizations.ts +41 -0
  113. package/src/types/owned.ts +9 -0
  114. package/src/types/resources.ts +37 -0
  115. package/src/types/reuses.ts +49 -0
  116. package/src/types/site.ts +23 -0
  117. package/src/types/topics.ts +20 -0
  118. package/src/types/ui.ts +3 -0
  119. package/src/types/users.ts +10 -0
@@ -0,0 +1,198 @@
1
+ <template>
2
+ <div class="p-4 border border-gray-default relative hover:bg-gray-some">
3
+ <div :id />
4
+ <div
5
+ v-if="dataset.private || dataset.archived"
6
+ class="absolute top-0 fr-grid-row fr-grid-row--middle fr-mt-n3v fr-ml-n1v"
7
+ >
8
+ <p
9
+ v-if="dataset.private"
10
+ class="fr-badge fr-badge--sm fr-badge--mention-grey text-gray-medium mr-2"
11
+ >
12
+ <span
13
+ class="fr-icon-lock-line fr-icon--sm"
14
+ aria-hidden="true"
15
+ />
16
+ {{ t('Draft') }}
17
+ </p>
18
+ <p
19
+ v-if="dataset.archived"
20
+ class="fr-badge fr-badge--sm fr-badge--mention-grey text-gray-medium mr-2"
21
+ >
22
+ <span
23
+ class="fr-icon-archive-line fr-icon--sm"
24
+ aria-hidden="true"
25
+ />
26
+ {{ t('Archived') }}
27
+ </p>
28
+ </div>
29
+ <div class="flex flex-wrap md:flex-nowrap gap-4 items-start">
30
+ <div class="flex-none">
31
+ <div class="flex justify-center items-center p-3 border border-gray-lower bg-[#fff]">
32
+ <Placeholder
33
+ v-if="dataset.organization"
34
+ type="dataset"
35
+ :src="dataset.organization.logo_thumbnail"
36
+ alt=""
37
+ :size="40"
38
+ />
39
+ <Avatar
40
+ v-else-if="dataset.owner"
41
+ :user="dataset.owner"
42
+ :size="40"
43
+ />
44
+ <Placeholder
45
+ v-else
46
+ type="dataset"
47
+ :size="40"
48
+ />
49
+ </div>
50
+ </div>
51
+ <div class="flex-1 overflow-hidden">
52
+ <h4 class="w-full text-base mb-0 flex">
53
+ <slot
54
+ name="datasetUrl"
55
+ :dataset="dataset"
56
+ :dataset-url="datasetUrl"
57
+ >
58
+ <AppLink
59
+ :to="datasetUrl"
60
+ class="text-gray-title text-base bg-none flex truncate"
61
+ >
62
+ <span class="block flex-initial truncate">{{ dataset.title }}</span>
63
+ <small
64
+ v-if="dataset.acronym"
65
+ class="flex-none ml-2"
66
+ >{{ dataset.acronym }}</small>
67
+ <span class="absolute inset-0" />
68
+ </AppLink>
69
+ </slot>
70
+ </h4>
71
+ <div
72
+ v-if="dataset.organization || dataset.owner"
73
+ class="text-sm m-0 flex truncate"
74
+ >
75
+ <template v-if="dataset.organization">
76
+ <div class="-mr-0.5 flex-initial truncate">
77
+ <AppLink
78
+ v-if="organizationUrl"
79
+ class="link text-sm flex items-center relative z-[2] truncate"
80
+ :to="organizationUrl"
81
+ >
82
+ <OrganizationNameWithCertificate :organization="dataset.organization" />
83
+ </AppLink>
84
+ <OrganizationNameWithCertificate
85
+ v-else
86
+ :organization="dataset.organization"
87
+ />
88
+ </div>
89
+ </template>
90
+ <div
91
+ v-else
92
+ class="mr-1 truncate"
93
+ >
94
+ {{ ownerName }}
95
+ </div>
96
+ <div class="text-gray-medium dash-before-sm whitespace-nowrap">
97
+ {{ $t('Updated {date}', { date: formatRelativeIfRecentDate(dataset.last_update, { dateStyle: 'medium' }) }) }}
98
+ </div>
99
+ </div>
100
+ <div class="mx-0 -mb-1 flex flex-wrap items-center text-sm text-gray-medium">
101
+ <div class="fr-hidden flex-sm dash-after-sm text-gray-500 -ml-2.5">
102
+ <DatasetQualityInline
103
+ :quality="dataset.quality"
104
+ :teleport-id="id"
105
+ />
106
+ </div>
107
+ <div class="fr-grid-row fr-grid-row--middle fr-mr-1v">
108
+ <p
109
+ class="fr-text--sm fr-my-0"
110
+ :aria-label="t('{n} resources downloads', dataset.metrics.resources_downloads)"
111
+ >
112
+ <span
113
+ class="fr-icon-download-line fr-icon--sm fr-px-1v"
114
+ aria-hidden="true"
115
+ />{{ summarize(dataset.metrics.resources_downloads) }}
116
+ </p>
117
+ <p
118
+ class="fr-text--sm fr-my-0"
119
+ :aria-label="t('{n} followers', dataset.metrics.followers)"
120
+ >
121
+ <span
122
+ class="fr-icon-star-line fr-icon--sm fr-px-1v"
123
+ aria-hidden="true"
124
+ />{{ summarize(dataset.metrics.followers) }}
125
+ </p>
126
+ <p
127
+ class="fr-text--sm fr-my-0"
128
+ :aria-label="t('{n} reuses', dataset.metrics.reuses)"
129
+ >
130
+ <span
131
+ class="fr-icon-line-chart-line fr-icon--sm fr-px-1v"
132
+ aria-hidden="true"
133
+ />{{ summarize(dataset.metrics.reuses) }}
134
+ </p>
135
+ </div>
136
+ </div>
137
+ <component
138
+ :is="config.textClamp"
139
+ v-if="showDescription && config && config.textClamp && description"
140
+ class="fr-text--sm fr-mt-1w fr-mb-0 overflow-wrap-anywhere"
141
+ :auto-resize="true"
142
+ :text="description"
143
+ :max-lines="2"
144
+ />
145
+ </div>
146
+ </div>
147
+ </div>
148
+ </template>
149
+
150
+ <script setup lang="ts">
151
+ import { useI18n } from 'vue-i18n'
152
+ import type { RouteLocationRaw } from 'vue-router'
153
+ import { computed, ref, useId, watchEffect } from 'vue'
154
+ import type { Dataset, DatasetV2 } from '../types/datasets'
155
+ import { summarize } from '../functions/helpers'
156
+ import { formatRelativeIfRecentDate } from '../functions/dates'
157
+ import { getOwnerName } from '../functions/owned'
158
+ import { removeMarkdown } from '../functions/markdown'
159
+ import { useComponentsConfig } from '../config'
160
+ import DatasetQualityInline from './DatasetQualityInline.vue'
161
+ import Avatar from './Avatar.vue'
162
+ import Placeholder from './Placeholder.vue'
163
+ import OrganizationNameWithCertificate from './OrganizationNameWithCertificate.vue'
164
+ import AppLink from './AppLink.vue'
165
+
166
+ type Props = {
167
+ dataset: Dataset | DatasetV2
168
+
169
+ /**
170
+ * The datasetUrl is a route location object to allow Vue Router to navigate to the details of a dataset.
171
+ * It is used as a separate prop to allow other sites using the package to define their own dataset pages.
172
+ */
173
+ datasetUrl: RouteLocationRaw
174
+
175
+ /**
176
+ * The organizationUrl is an optional route location object to allow Vue Router to navigate to the details of the organization linked to tha dataset.
177
+ * It is used as a separate prop to allow other sites using the package to define their own organization pages.
178
+ */
179
+ organizationUrl?: RouteLocationRaw
180
+ showDescription?: boolean
181
+ }
182
+
183
+ const props = withDefaults(defineProps<Props>(), {
184
+ style: () => ({}),
185
+ showDescription: true,
186
+ })
187
+
188
+ const { t } = useI18n()
189
+ const id = useId()
190
+ const ownerName = computed(() => getOwnerName(props.dataset))
191
+ const config = useComponentsConfig()
192
+
193
+ const description = ref('')
194
+ watchEffect(async () => {
195
+ if (!props.showDescription) return
196
+ description.value = await removeMarkdown(props.dataset.description)
197
+ })
198
+ </script>
@@ -0,0 +1,210 @@
1
+ <template>
2
+ <div class="py-6 mb-6 border-bottom border-gray-default">
3
+ <h2 class="subtitle subtitle--uppercase">
4
+ {{ $t('Informations') }}
5
+ </h2>
6
+ <div class="fr-text--sm fr-m-0">
7
+ <dl class="fr-grid-row fr-grid-row--gutters">
8
+ <div
9
+ v-if="license"
10
+ class="fr-col-12 fr-col-sm-6 fr-col-md-4"
11
+ >
12
+ <dt class="subtitle fr-mb-0">
13
+ {{ $t('License') }}
14
+ </dt>
15
+ <dd class="text-sm m-0 text-gray-medium p-0">
16
+ <code class="bg-grey-some px-1 text-gray-medium">
17
+ <a :href="license.url">
18
+ {{ license.title }}
19
+ </a>
20
+ </code>
21
+ </dd>
22
+ </div>
23
+ <div class="fr-col-12 fr-col-sm-6 fr-col-md-4">
24
+ <dt class="subtitle fr-mb-0">
25
+ ID
26
+ </dt>
27
+ <dd class="text-sm m-0 text-gray-medium p-0">
28
+ {{ dataset.id }}
29
+ </dd>
30
+ </div>
31
+ </dl>
32
+ </div>
33
+ </div>
34
+ <div class="pb-6 mb-6 border-bottom border-gray-default">
35
+ <h2 class="subtitle subtitle--uppercase">
36
+ {{ $t('Temporality') }}
37
+ </h2>
38
+ <div class="fr-text--sm fr-m-0">
39
+ <dl class="fr-grid-row fr-grid-row--gutters">
40
+ <div class="fr-col-12 fr-col-sm-6 fr-col-md-4">
41
+ <dt class="subtitle fr-mb-0">
42
+ {{ $t('Creation') }}
43
+ </dt>
44
+ <dd class="text-sm m-0 text-gray-medium p-0">
45
+ {{ formatDate(dataset.created_at) }}
46
+ </dd>
47
+ </div>
48
+ <div
49
+ v-if="frequency"
50
+ class="fr-col-12 fr-col-sm-6 fr-col-md-4"
51
+ >
52
+ <dt class="subtitle fr-mb-0">
53
+ {{ $t('Frequency') }}
54
+ </dt>
55
+ <dd class="text-sm m-0 text-gray-medium p-0">
56
+ {{ frequency.label }}
57
+ </dd>
58
+ </div>
59
+ </dl>
60
+ <dl class="fr-grid-row fr-grid-row--gutters">
61
+ <div class="fr-col-12 fr-col-sm-6 fr-col-md-4">
62
+ <dt class="subtitle fr-mb-0">
63
+ {{ $t('Last update') }}
64
+ </dt>
65
+ <dd class="text-sm m-0 text-gray-medium p-0">
66
+ {{ formatDate(props.dataset.last_update) }}
67
+ </dd>
68
+ </div>
69
+ </dl>
70
+ </div>
71
+ </div>
72
+ <div class="pb-6 mb-6 border-bottom border-gray-default">
73
+ <h2 class="subtitle subtitle--uppercase">
74
+ {{ $t('Spatial coverage') }}
75
+ </h2>
76
+ <div class="fr-text--sm fr-m-0">
77
+ <dl class="fr-grid-row fr-grid-row--gutters">
78
+ <div
79
+ v-if="zonesLabels.length"
80
+ class="fr-col-12 fr-col-sm-6 fr-col-md-4"
81
+ >
82
+ <dt class="subtitle fr-mb-0">
83
+ {{ $t('Territorial coverage') }}
84
+ </dt>
85
+ <dd class="text-sm m-0 text-gray-medium p-0">
86
+ {{ zonesLabels.join(', ') }}
87
+ </dd>
88
+ </div>
89
+ <div
90
+ v-if="granularity"
91
+ class="fr-col-12 fr-col-sm-6 fr-col-md-4"
92
+ >
93
+ <dt class="subtitle fr-mb-0">
94
+ {{ $t('Granularity of territorial coverage') }}
95
+ </dt>
96
+ <dd class="text-sm m-0 text-gray-medium p-0">
97
+ {{ granularity.name }}
98
+ </dd>
99
+ </div>
100
+ </dl>
101
+ </div>
102
+ </div>
103
+ <div class="pb-6 mb-6 border-bottom border-gray-default">
104
+ <h2 class="subtitle subtitle--uppercase">
105
+ Actions
106
+ </h2>
107
+ <div class="fr-text--sm fr-m-0">
108
+ <h3 class="subtitle fr-mb-1v">
109
+ {{ $t('Integrate on your website') }}
110
+ <CopyButton
111
+ :hide-label="true"
112
+ :label="$t('Copy embed')"
113
+ :copied-label="$t('Embed copied')"
114
+ class="fr-my-1w fr-mr-1w"
115
+ :text="getDatasetOEmbedHtml('dataset', dataset.id)"
116
+ />
117
+ </h3>
118
+ <div class="embed-wrapper">
119
+ <textarea
120
+ ref="textAreaRef"
121
+ :value="getDatasetOEmbedHtml('dataset', dataset.id)"
122
+ readonly="true"
123
+ rows="1"
124
+ @click="selectContent"
125
+ />
126
+ </div>
127
+ </div>
128
+ </div>
129
+ <div v-if="hasExtras">
130
+ <ExtraAccordion
131
+ :button-text="t('See extras')"
132
+ :title-text="t('Extras')"
133
+ :extra="props.dataset.extras"
134
+ title-level="h2"
135
+ />
136
+ </div>
137
+ <div v-if="props.dataset?.harvest">
138
+ <ExtraAccordion
139
+ :button-text="t('See harvest')"
140
+ :title-text="t('Harvest')"
141
+ :extra="props.dataset.harvest"
142
+ title-level="h2"
143
+ />
144
+ </div>
145
+ </template>
146
+
147
+ <script setup lang="ts">
148
+ import { ref, computed } from 'vue'
149
+ import { useI18n } from 'vue-i18n'
150
+ import { formatDate } from '../functions/dates'
151
+ // import useOEmbed from '../../composables/useOEmbed'
152
+ import type { Dataset, DatasetV2 } from '../types/datasets'
153
+ import type { Granularity } from '../types/granularity'
154
+ import type { Frequency } from '../types/frequency'
155
+ import type { License } from '../types/licenses'
156
+ import { useFetch } from '../functions/api'
157
+ import getDatasetOEmbedHtml from '../functions/datasets'
158
+ import ExtraAccordion from './ExtraAccordion.vue'
159
+ import CopyButton from './CopyButton.vue'
160
+
161
+ const props = defineProps<{
162
+ dataset: DatasetV2 | Dataset
163
+ }>()
164
+ const { t } = useI18n()
165
+ // const embedText = useOEmbed('dataset', props.dataset.id)
166
+ const textAreaRef = ref<HTMLTextAreaElement | null>(null)
167
+
168
+ const hasExtras = computed(() => Object.keys(props.dataset.extras).length > 0)
169
+
170
+ function selectContent() {
171
+ if (textAreaRef.value) {
172
+ textAreaRef.value.select()
173
+ }
174
+ };
175
+
176
+ const { data: allLicenses } = await useFetch<Array<License>>('/api/1/datasets/licenses/')
177
+ const license = computed(() => {
178
+ if (!props.dataset.license) return null
179
+ if (!allLicenses.value) return null
180
+
181
+ return allLicenses.value.find(license => license.id === props.dataset.license)
182
+ })
183
+
184
+ const { data: frequencies } = await useFetch<Array<Frequency>>('/api/1/datasets/frequencies/')
185
+ const frequency = computed(() => {
186
+ if (!props.dataset.frequency) return null
187
+ if (!frequencies.value) return null
188
+
189
+ return frequencies.value.find(frequency => frequency.id === props.dataset.frequency)
190
+ })
191
+
192
+ const zonesUrl = computed(() => {
193
+ if (!props.dataset.spatial?.zones || !props.dataset.spatial.zones.length) return null
194
+
195
+ return `/api/1/spatial/zones/${props.dataset.spatial.zones.join(',')}/`
196
+ })
197
+ const { data: zones } = await useFetch<{ features: Array<{ properties: { name: string } }> }>(zonesUrl)
198
+ const zonesLabels = computed(() => {
199
+ if (!zones.value) return []
200
+ return zones.value.features.map(feature => feature.properties.name)
201
+ })
202
+
203
+ const { data: granularities } = await useFetch<Array<Granularity>>('/api/1/spatial/granularities/')
204
+ const granularity = computed(() => {
205
+ if (!props.dataset.spatial?.granularity) return null
206
+ if (!granularities.value) return null
207
+
208
+ return granularities.value.find(granularity => granularity.id === props.dataset.spatial?.granularity)
209
+ })
210
+ </script>
@@ -0,0 +1,68 @@
1
+ <template>
2
+ <div class="fr-grid-row fr-grid-row--middle fr-ml-n1v">
3
+ <Toggletip>
4
+ {{ $t('Metadata quality:') }}
5
+ <template #toggletip>
6
+ <DatasetQualityTooltipContent :quality />
7
+ </template>
8
+ </Toggletip>
9
+ <p class="fr-m-0 fr-mr-1v">
10
+ {{ $t('Metadata quality:') }}
11
+ </p>
12
+ </div>
13
+ <DatasetQualityScore
14
+ :score="quality.score"
15
+ class="w-100"
16
+ />
17
+ <template v-if="showItemWarnings">
18
+ <ul class="list-none pl-0">
19
+ <DatasetQualityItemWarning
20
+ :quality-item="quality.dataset_description_quality"
21
+ :message="$t('Data description empty')"
22
+ />
23
+ <DatasetQualityItemWarning
24
+ :quality-item="quality.resources_documentation"
25
+ :message="$t('Files documentation missing')"
26
+ />
27
+ <DatasetQualityItemWarning
28
+ :quality-item="quality.license"
29
+ :message="$t('No license set')"
30
+ />
31
+ <DatasetQualityItemWarning
32
+ :quality-item="quality.update_frequency && quality.update_fulfilled_in_time"
33
+ :message="quality.update_frequency ? $t('Update frequency not followed') : $t('Update frequency not set')"
34
+ />
35
+ <DatasetQualityItemWarning
36
+ :quality-item="quality.has_open_format"
37
+ :message="$t('File formats are closed')"
38
+ />
39
+ <DatasetQualityItemWarning
40
+ :quality-item="quality.temporal_coverage"
41
+ :message="$t('Temporal coverage not set')"
42
+ />
43
+ <DatasetQualityItemWarning
44
+ :quality-item="quality.spatial"
45
+ :message="$t('Spatial coverage not set')"
46
+ />
47
+ <DatasetQualityItemWarning
48
+ :quality-item="quality.all_resources_available"
49
+ :message="$t('Some files are unavailable')"
50
+ />
51
+ </ul>
52
+ </template>
53
+ </template>
54
+
55
+ <script setup lang="ts">
56
+ import type { Quality } from '../types/datasets'
57
+ import DatasetQualityItemWarning from './DatasetQualityItemWarning.vue'
58
+ import DatasetQualityScore from './DatasetQualityScore.vue'
59
+ import Toggletip from './Toggletip.vue'
60
+ import DatasetQualityTooltipContent from './DatasetQualityTooltipContent.vue'
61
+
62
+ withDefaults(defineProps<{
63
+ quality: Quality
64
+ showItemWarnings?: boolean
65
+ }>(), {
66
+ showItemWarnings: true,
67
+ })
68
+ </script>
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <div class="m-0 flex flex-wrap items-center text-sm text-gray-medium">
3
+ <div class="fr-grid-row fr-grid-row--middle">
4
+ <Toggletip
5
+ class="relative z-2"
6
+ :teleport-id
7
+ >
8
+ <template #toggletip>
9
+ <DatasetQualityTooltipContent :quality />
10
+ </template>
11
+ </Toggletip>
12
+ <p class="my-0 mr-1 text-gray-medium text-sm">
13
+ {{ $t('Metadata :') }}
14
+ </p>
15
+ <div class="fr-grid-row fr-grid-row--middle fr-mr-1v">
16
+ <DatasetQualityScore :score="quality.score" />
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ import type { Quality } from '../types/datasets'
24
+ import DatasetQualityScore from './DatasetQualityScore.vue'
25
+ import DatasetQualityTooltipContent from './DatasetQualityTooltipContent.vue'
26
+ import Toggletip from './Toggletip.vue'
27
+
28
+ defineProps<{
29
+ quality: Quality
30
+ teleportId?: string
31
+ }>()
32
+ </script>
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <li>
3
+ <p
4
+ v-if="passed"
5
+ class="fr-my-0"
6
+ >
7
+ <span
8
+ class="fr-icon-check-line"
9
+ aria-hidden="true"
10
+ />
11
+ {{ messagePassed }}
12
+ </p>
13
+ <p
14
+ v-else
15
+ class="my-0 text-gray-medium"
16
+ >
17
+ <span
18
+ class="fr-icon-warning-line"
19
+ aria-hidden="true"
20
+ />
21
+ {{ messageFailed }}
22
+ </p>
23
+ </li>
24
+ </template>
25
+
26
+ <script setup lang="ts">
27
+ defineProps<{
28
+ passed: boolean | null
29
+ messagePassed: string
30
+ messageFailed: string
31
+ }>()
32
+ </script>
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <li
3
+ v-if="!qualityItem"
4
+ class="text-warning-dark flex flex-wrap items-center m-1"
5
+ >
6
+ <p class="fr-my-0 fr-text--sm">
7
+ <span
8
+ class="fr-icon-warning-line fr-icon--sm"
9
+ aria-hidden="true"
10
+ />
11
+ {{ message }}
12
+ </p>
13
+ </li>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ defineProps<{
18
+ qualityItem: boolean
19
+ message: string
20
+ }>()
21
+ </script>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <meter
3
+ class="quality-score"
4
+ :class="props.class"
5
+ min="0"
6
+ low="0"
7
+ :high="high"
8
+ :max="quality_max_score"
9
+ :optimum="quality_max_score"
10
+ :value="score"
11
+ >
12
+ <template v-if="score >= high">
13
+ {{ t('Good') }}
14
+ </template>
15
+ <template v-else>
16
+ {{ t('To improve') }}
17
+ </template>({{ calculatedScore }})
18
+ </meter>
19
+ </template>
20
+
21
+ <script setup lang="ts">
22
+ import { computed } from 'vue'
23
+ import { useI18n } from 'vue-i18n'
24
+
25
+ const props = withDefaults(defineProps<{
26
+ score: number
27
+ class?: string
28
+ }>(), {
29
+ class: '',
30
+ })
31
+ const quality_max_score = 1
32
+ const high = quality_max_score * 2 / 3
33
+ const { t, locale } = useI18n()
34
+ const calculatedScore = computed(() => new Intl.NumberFormat(locale.value, { style: 'percent' }).format(props.score))
35
+ </script>
@@ -0,0 +1,79 @@
1
+ <template>
2
+ <h5 class="fr-text--sm fr-my-0">
3
+ {{ t("Metadata quality:") }}
4
+ </h5>
5
+ <ul class="list-none pl-0">
6
+ <DatasetQualityItem
7
+ :passed="quality.dataset_description_quality"
8
+ :message-passed="t('Data description filled')"
9
+ :message-failed="t('Data description empty')"
10
+ class="fr-my-1w"
11
+ />
12
+ <DatasetQualityItem
13
+ :passed="quality.resources_documentation"
14
+ :message-passed="t('Files documented')"
15
+ :message-failed="t('Files documentation missing')"
16
+ class="fr-my-1w"
17
+ />
18
+ <DatasetQualityItem
19
+ :passed="quality.license"
20
+ :message-passed="t('License filled')"
21
+ :message-failed="t('No license set')"
22
+ class="fr-my-1w"
23
+ />
24
+ <DatasetQualityItem
25
+ :passed="quality.update_frequency && !!quality.update_fulfilled_in_time"
26
+ :message-passed="t('Update frequency followed')"
27
+ :message-failed="quality.update_frequency ? t('Update frequency not followed') : t('Update frequency not set')"
28
+ class="fr-my-1w"
29
+ />
30
+ <DatasetQualityItem
31
+ :passed="quality.has_open_format"
32
+ :message-passed="t('File formats are open')"
33
+ :message-failed="t('File formats are closed')"
34
+ class="fr-my-1w"
35
+ />
36
+ <DatasetQualityItem
37
+ :passed="quality.temporal_coverage"
38
+ :message-passed="t('Temporal coverage filled')"
39
+ :message-failed="t('Temporal coverage not set')"
40
+ class="fr-my-1w"
41
+ />
42
+ <DatasetQualityItem
43
+ :passed="quality.spatial"
44
+ :message-passed="t('Spatial coverage filled')"
45
+ :message-failed="t('Spatial coverage not set')"
46
+ class="fr-my-1w"
47
+ />
48
+ <DatasetQualityItem
49
+ :passed="quality.all_resources_available"
50
+ :message-passed="t('All files are available')"
51
+ :message-failed="t('Some files are unavailable')"
52
+ class="fr-my-1w"
53
+ />
54
+ </ul>
55
+ <div class="fr-grid-row fr-grid-row--right not-enlarged">
56
+ <a
57
+ :href="config.datasetQualityGuideUrl"
58
+ target="_blank"
59
+ rel="noopener"
60
+ :title="t('Learn more about this indicator - opens a new window')"
61
+ >
62
+ {{ t("Learn more about this indicator") }}
63
+ </a>
64
+ </div>
65
+ </template>
66
+
67
+ <script setup lang="ts">
68
+ import { useI18n } from 'vue-i18n'
69
+ import type { Quality } from '../types/datasets'
70
+ import { useComponentsConfig } from '../config'
71
+ import DatasetQualityItem from './DatasetQualityItem.vue'
72
+
73
+ defineProps<{
74
+ quality: Quality
75
+ }>()
76
+
77
+ const { t } = useI18n()
78
+ const config = useComponentsConfig()
79
+ </script>