@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.
- package/README.md +150 -0
- package/assets/main.css +136 -0
- package/assets/placeholders/author.png +0 -0
- package/assets/placeholders/dataset.png +0 -0
- package/assets/placeholders/news.png +0 -0
- package/assets/placeholders/organization.png +0 -0
- package/assets/placeholders/reuse.png +0 -0
- package/assets/tailwind.config.js +24 -0
- package/dist/components.css +2 -0
- package/dist/locales/de.js +155 -0
- package/dist/locales/en.js +155 -0
- package/dist/locales/es.js +155 -0
- package/dist/locales/fr.js +155 -0
- package/dist/locales/it.js +155 -0
- package/dist/locales/pt.js +155 -0
- package/dist/locales/sr.js +155 -0
- package/package.json +72 -0
- package/src/components/AppLink.vue +51 -0
- package/src/components/Avatar.vue +27 -0
- package/src/components/AvatarWithName.vue +26 -0
- package/src/components/BannerAction.vue +39 -0
- package/src/components/BrandedButton.vue +170 -0
- package/src/components/CopyButton.vue +84 -0
- package/src/components/DataserviceCard.vue +184 -0
- package/src/components/DatasetCard.vue +198 -0
- package/src/components/DatasetInformationPanel.vue +210 -0
- package/src/components/DatasetQuality.vue +68 -0
- package/src/components/DatasetQualityInline.vue +32 -0
- package/src/components/DatasetQualityItem.vue +32 -0
- package/src/components/DatasetQualityItemWarning.vue +21 -0
- package/src/components/DatasetQualityScore.vue +35 -0
- package/src/components/DatasetQualityTooltipContent.vue +79 -0
- package/src/components/DescriptionDetails.vue +23 -0
- package/src/components/DescriptionList/DescriptionDetails.stories.ts +43 -0
- package/src/components/DescriptionList/DescriptionList.stories.ts +47 -0
- package/src/components/DescriptionList/DescriptionTerm.stories.ts +28 -0
- package/src/components/DescriptionList.vue +8 -0
- package/src/components/DescriptionTerm.vue +8 -0
- package/src/components/ExtraAccordion.vue +78 -0
- package/src/components/Icons/Archive.vue +21 -0
- package/src/components/Icons/Code.vue +21 -0
- package/src/components/Icons/Documentation.vue +21 -0
- package/src/components/Icons/File.vue +21 -0
- package/src/components/Icons/Image.vue +7 -0
- package/src/components/Icons/Link.vue +21 -0
- package/src/components/Icons/Table.vue +21 -0
- package/src/components/OrganizationCard.vue +68 -0
- package/src/components/OrganizationNameWithCertificate.vue +45 -0
- package/src/components/OwnerType.vue +43 -0
- package/src/components/OwnerTypeIcon.vue +18 -0
- package/src/components/Pagination.vue +205 -0
- package/src/components/Placeholder.vue +29 -0
- package/src/components/ReadMore.vue +107 -0
- package/src/components/ResourceAccordion/DataStructure.vue +87 -0
- package/src/components/ResourceAccordion/EditButton.vue +34 -0
- package/src/components/ResourceAccordion/Metadata.vue +171 -0
- package/src/components/ResourceAccordion/Preview.vue +229 -0
- package/src/components/ResourceAccordion/PreviewLoader.vue +148 -0
- package/src/components/ResourceAccordion/ResourceAccordion.vue +484 -0
- package/src/components/ResourceAccordion/ResourceIcon.vue +16 -0
- package/src/components/ResourceAccordion/SchemaBadge.vue +148 -0
- package/src/components/ResourceAccordion/SchemaLoader.vue +30 -0
- package/src/components/ResourceAccordion/Swagger.vue +46 -0
- package/src/components/ResourceAccordion/france.svg +1 -0
- package/src/components/ReuseCard.vue +106 -0
- package/src/components/ReuseDetails.vue +45 -0
- package/src/components/SimpleBanner.vue +24 -0
- package/src/components/SmallChart.vue +149 -0
- package/src/components/StatBox.vue +100 -0
- package/src/components/Tabs/Tab.vue +62 -0
- package/src/components/Tabs/TabGroup.vue +20 -0
- package/src/components/Tabs/TabList.vue +15 -0
- package/src/components/Tabs/TabPanel.vue +7 -0
- package/src/components/Tabs/TabPanels.vue +7 -0
- package/src/components/Toggletip.vue +62 -0
- package/src/components/ToggletipButton.vue +14 -0
- package/src/composables/useActiveDescendant.ts +103 -0
- package/src/composables/useReuseType.ts +14 -0
- package/src/config.ts +33 -0
- package/src/functions/api.ts +96 -0
- package/src/functions/api.types.ts +41 -0
- package/src/functions/config.ts +12 -0
- package/src/functions/datasets.ts +24 -0
- package/src/functions/dates.ts +85 -0
- package/src/functions/helpers.ts +38 -0
- package/src/functions/markdown.ts +47 -0
- package/src/functions/matomo.ts +3 -0
- package/src/functions/organizations.ts +85 -0
- package/src/functions/owned.ts +11 -0
- package/src/functions/resources.ts +99 -0
- package/src/functions/reuses.ts +28 -0
- package/src/functions/schemas.ts +96 -0
- package/src/functions/tabularApi.ts +27 -0
- package/src/functions/users.ts +7 -0
- package/src/locales/de.json +154 -0
- package/src/locales/en.json +154 -0
- package/src/locales/es.json +154 -0
- package/src/locales/fr.json +154 -0
- package/src/locales/it.json +154 -0
- package/src/locales/pt.json +154 -0
- package/src/locales/sr.json +154 -0
- package/src/main.ts +147 -0
- package/src/types/badges.ts +5 -0
- package/src/types/contact_point.ts +7 -0
- package/src/types/dataservices.ts +68 -0
- package/src/types/datasets.ts +80 -0
- package/src/types/frequency.ts +6 -0
- package/src/types/granularity.ts +6 -0
- package/src/types/harvest.ts +3 -0
- package/src/types/keyboard.ts +1 -0
- package/src/types/licenses.ts +9 -0
- package/src/types/organizations.ts +41 -0
- package/src/types/owned.ts +9 -0
- package/src/types/resources.ts +37 -0
- package/src/types/reuses.ts +49 -0
- package/src/types/site.ts +23 -0
- package/src/types/topics.ts +20 -0
- package/src/types/ui.ts +3 -0
- 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>
|