@datagouv/components-next 0.2.0 → 1.0.0

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 (140) hide show
  1. package/README.md +1 -1
  2. package/assets/main.css +56 -1
  3. package/dist/Control-BNCDn-8E.js +148 -0
  4. package/dist/{Datafair.client-x39O4yfF.js → Datafair.client-Dls5AHTE.js} +1 -1
  5. package/dist/Event-BOgJUhNR.js +738 -0
  6. package/dist/Image-BN-4XkIn.js +247 -0
  7. package/dist/{JsonPreview.client-BMsC5JcY.js → JsonPreview.client-DPDTs433.js} +14 -14
  8. package/dist/Map-BdT3i2C4.js +7609 -0
  9. package/dist/MapContainer.client-BdAzd7bj.js +105 -0
  10. package/dist/OSM-CamriM9b.js +71 -0
  11. package/dist/{PdfPreview.client-COOkEkRA.js → PdfPreview.client-CopqSDyt.js} +3 -3
  12. package/dist/{Pmtiles.client-BaiIo4VZ.js → Pmtiles.client-mF6xaOO_.js} +2 -2
  13. package/dist/ScaleLine-BiesrgOv.js +165 -0
  14. package/dist/Swagger.client-eJ7gpfZA.js +4 -0
  15. package/dist/Tile-DCuqwNOI.js +1206 -0
  16. package/dist/TileImage-CmZf8EdU.js +1067 -0
  17. package/dist/View-DcDc7N2K.js +2858 -0
  18. package/dist/{XmlPreview.client-CAdN0w_Y.js → XmlPreview.client-C0OgBkSq.js} +7 -7
  19. package/dist/common-C4rDcQpp.js +243 -0
  20. package/dist/components-next.css +1 -1
  21. package/dist/components-next.js +153 -117
  22. package/dist/components.css +1 -1
  23. package/dist/{MapContainer.client-DeSo8EvG.js → index-BRGqW8aQ.js} +4975 -21416
  24. package/dist/leaflet-src-7m1mB8LI.js +6338 -0
  25. package/dist/{main-Dgri3TQL.js → main-CNHxAJ8J.js} +56758 -51450
  26. package/dist/proj-CKwYjU38.js +1569 -0
  27. package/dist/tilecoord-YW3qEH_j.js +884 -0
  28. package/dist/{vue3-xml-viewer.common-D6skc_Ai.js → vue3-xml-viewer.common-CmAdQfIy.js} +1 -1
  29. package/package.json +5 -1
  30. package/src/components/ActivityList/ActivityList.vue +6 -2
  31. package/src/components/AppLink.vue +4 -1
  32. package/src/components/Avatar.vue +2 -2
  33. package/src/components/AvatarWithName.vue +8 -4
  34. package/src/components/BouncingDots.vue +21 -0
  35. package/src/components/BrandedButton.vue +2 -0
  36. package/src/components/CopyButton.vue +19 -7
  37. package/src/components/DataserviceCard.vue +83 -118
  38. package/src/components/DatasetCard.vue +110 -171
  39. package/src/components/DatasetInformation/DatasetEmbedSection.vue +43 -0
  40. package/src/components/DatasetInformation/DatasetInformationSection.vue +73 -0
  41. package/src/components/DatasetInformation/DatasetSchemaSection.vue +74 -0
  42. package/src/components/DatasetInformation/DatasetSpatialSection.vue +59 -0
  43. package/src/components/DatasetInformation/DatasetTemporalitySection.vue +45 -0
  44. package/src/components/DatasetInformation/index.ts +5 -0
  45. package/src/components/DatasetQualityTooltipContent.vue +3 -3
  46. package/src/components/DescriptionList.vue +1 -4
  47. package/src/components/DescriptionListDetails.vue +5 -0
  48. package/src/components/DescriptionListTerm.vue +5 -0
  49. package/src/components/DiscussionMessageCard.vue +63 -0
  50. package/src/components/ExtraAccordion.vue +4 -4
  51. package/src/components/Form/BadgeSelect.vue +35 -0
  52. package/src/components/Form/FormatSelect.vue +28 -0
  53. package/src/components/Form/GeozoneSelect.vue +52 -0
  54. package/src/components/Form/GranularitySelect.vue +29 -0
  55. package/src/components/Form/LicenseSelect.vue +30 -0
  56. package/src/components/Form/OrganizationSelect.vue +62 -0
  57. package/src/components/Form/OrganizationTypeSelect.vue +34 -0
  58. package/src/components/Form/ReuseTopicSelect.vue +29 -0
  59. package/src/components/Form/SchemaSelect.vue +30 -0
  60. package/src/components/Form/SearchableSelect.vue +334 -0
  61. package/src/components/Form/SelectGroup.vue +132 -0
  62. package/src/components/Form/TagSelect.vue +38 -0
  63. package/src/components/LeafletMap.vue +31 -0
  64. package/src/components/LicenseBadge.vue +24 -0
  65. package/src/components/LoadingBlock.vue +23 -2
  66. package/src/components/MarkdownViewer.vue +3 -1
  67. package/src/components/ObjectCard.vue +42 -0
  68. package/src/components/ObjectCardBadge.vue +22 -0
  69. package/src/components/ObjectCardHeader.vue +35 -0
  70. package/src/components/ObjectCardOwner.vue +43 -0
  71. package/src/components/ObjectCardShortDescription.vue +28 -0
  72. package/src/components/OrganizationCard.vue +35 -20
  73. package/src/components/OrganizationLogo.vue +1 -1
  74. package/src/components/OrganizationNameWithCertificate.vue +13 -7
  75. package/src/components/OwnerTypeIcon.vue +1 -0
  76. package/src/components/Pagination.vue +1 -1
  77. package/src/components/Placeholder.vue +5 -2
  78. package/src/components/PostCard.vue +62 -0
  79. package/src/components/RadioGroup.vue +32 -0
  80. package/src/components/RadioInput.vue +64 -0
  81. package/src/components/ResourceAccordion/EditButton.vue +2 -3
  82. package/src/components/ResourceAccordion/MapContainer.client.vue +20 -16
  83. package/src/components/ResourceAccordion/Metadata.vue +11 -24
  84. package/src/components/ResourceAccordion/Pmtiles.client.vue +1 -1
  85. package/src/components/ResourceAccordion/Preview.vue +1 -1
  86. package/src/components/ResourceAccordion/ResourceAccordion.vue +30 -20
  87. package/src/components/ResourceAccordion/ResourceIcon.vue +1 -0
  88. package/src/components/ResourceAccordion/SchemaBadge.vue +2 -2
  89. package/src/components/ResourceExplorer/ResourceExplorer.vue +243 -0
  90. package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +116 -0
  91. package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +361 -0
  92. package/src/components/ReuseCard.vue +8 -28
  93. package/src/components/ReuseHorizontalCard.vue +80 -0
  94. package/src/components/Search/BasicAndAdvancedFilters.vue +49 -0
  95. package/src/components/Search/Filter/AccessTypeFilter.vue +37 -0
  96. package/src/components/Search/Filter/DatasetBadgeFilter.vue +40 -0
  97. package/src/components/Search/Filter/FilterButtonGroup.vue +78 -0
  98. package/src/components/Search/Filter/FormatFamilyFilter.vue +39 -0
  99. package/src/components/Search/Filter/LastUpdateRangeFilter.vue +37 -0
  100. package/src/components/Search/Filter/ProducerTypeFilter.vue +39 -0
  101. package/src/components/Search/Filter/ReuseTypeFilter.vue +42 -0
  102. package/src/components/Search/GlobalSearch.vue +611 -0
  103. package/src/components/Search/SearchInput.vue +63 -0
  104. package/src/components/Search/Sidemenu.vue +38 -0
  105. package/src/components/StatBox.vue +5 -5
  106. package/src/components/Tag.vue +30 -0
  107. package/src/components/Toggletip.vue +6 -2
  108. package/src/components/Tooltip.vue +2 -3
  109. package/src/components/TopicCard.vue +134 -0
  110. package/src/components/radioGroupContext.ts +9 -0
  111. package/src/composables/useDebouncedRef.ts +31 -0
  112. package/src/composables/useMetrics.ts +4 -3
  113. package/src/composables/useResourceCapabilities.ts +118 -0
  114. package/src/composables/useRouteQueryBoolean.ts +10 -0
  115. package/src/composables/useSelectModelSync.ts +89 -0
  116. package/src/composables/useStableQueryParams.ts +84 -0
  117. package/src/config.ts +4 -0
  118. package/src/functions/api.ts +17 -6
  119. package/src/functions/api.types.ts +4 -2
  120. package/src/functions/datasets.ts +1 -29
  121. package/src/functions/description.ts +33 -0
  122. package/src/functions/helpers.ts +11 -0
  123. package/src/functions/markdown.ts +60 -16
  124. package/src/functions/metrics.ts +33 -0
  125. package/src/functions/organizations.ts +5 -5
  126. package/src/main.ts +89 -7
  127. package/src/types/dataservices.ts +14 -12
  128. package/src/types/datasets.ts +20 -7
  129. package/src/types/discussions.ts +20 -0
  130. package/src/types/licenses.ts +3 -3
  131. package/src/types/organizations.ts +13 -1
  132. package/src/types/owned.ts +4 -2
  133. package/src/types/pages.ts +70 -0
  134. package/src/types/posts.ts +27 -0
  135. package/src/types/resources.ts +6 -0
  136. package/src/types/reuses.ts +14 -5
  137. package/src/types/search.ts +379 -0
  138. package/src/types/users.ts +12 -3
  139. package/dist/Swagger.client-CpLgaLg6.js +0 -4
  140. package/src/components/DatasetInformationPanel.vue +0 -211
@@ -1,4 +1,4 @@
1
- import { reactive, ref, toValue, watchEffect, type ComputedRef, type Ref } from 'vue'
1
+ import { ref, toValue, watchEffect, onMounted, type ComputedRef, type Ref } from 'vue'
2
2
  import { ofetch } from 'ofetch'
3
3
  import { useComponentsConfig } from '../config'
4
4
  import { useTranslation } from '../composables/useTranslation'
@@ -23,11 +23,13 @@ export async function useFetch<DataT, ErrorT = never>(
23
23
  const execute = async () => {
24
24
  const urlValue = toValue(url)
25
25
  if (!urlValue) return
26
- const fetchOptions = reactive(options ?? {})
26
+ const params = toValue(options?.params)
27
+ const query = toValue(options?.query)
27
28
  status.value = 'pending'
28
29
  try {
29
30
  data.value = await ofetch<DataT | null>(urlValue, {
30
31
  baseURL: config.apiBase,
32
+ params: params ?? query,
31
33
  onRequest(param) {
32
34
  if (config.onRequest) {
33
35
  if (Array.isArray(config.onRequest)) {
@@ -55,7 +57,6 @@ export async function useFetch<DataT, ErrorT = never>(
55
57
  onRequestError: config.onRequestError,
56
58
  onResponse: config.onResponse,
57
59
  onResponseError: config.onResponseError,
58
- ...fetchOptions,
59
60
  })
60
61
  status.value = 'success'
61
62
  }
@@ -65,9 +66,19 @@ export async function useFetch<DataT, ErrorT = never>(
65
66
  }
66
67
  }
67
68
 
68
- watchEffect(async () => {
69
- await execute()
70
- })
69
+ // When server is false, only start watching after mount (client-side only)
70
+ if (options?.server === false) {
71
+ onMounted(() => {
72
+ watchEffect(async () => {
73
+ await execute()
74
+ })
75
+ })
76
+ }
77
+ else {
78
+ watchEffect(async () => {
79
+ await execute()
80
+ })
81
+ }
71
82
 
72
83
  return {
73
84
  data,
@@ -1,10 +1,12 @@
1
1
  import type { ComputedRef, Ref, WatchSource } from 'vue'
2
2
 
3
+ type MaybeRef<T> = T | Ref<T> | ComputedRef<T>
4
+
3
5
  export type UseFetchOptions<DataT> = {
4
6
  key?: string
5
7
  method?: string
6
- query?: Record<string, unknown>
7
- params?: Record<string, unknown>
8
+ query?: MaybeRef<Record<string, unknown>>
9
+ params?: MaybeRef<Record<string, unknown>>
8
10
  body?: RequestInit['body'] | Record<string, unknown>
9
11
  headers?: Record<string, string> | [key: string, value: string][] | Headers
10
12
  baseURL?: string
@@ -1,11 +1,6 @@
1
1
  import { useComponentsConfig } from '../config'
2
2
  import type { Dataset, DatasetV2 } from '../types/datasets'
3
3
  import type { CommunityResource, Resource } from '../types/resources'
4
- import { removeMarkdown } from './markdown'
5
-
6
- // Dataset description constants
7
- export const DESCRIPTION_SHORT_MAX_LENGTH = 200
8
- export const DESCRIPTION_MIN_LENGTH = 200
9
4
 
10
5
  function constructUrl(baseUrl: string, path: string): string {
11
6
  const url = new URL(baseUrl)
@@ -25,30 +20,7 @@ export function isCommunityResource(resource: Resource | CommunityResource): boo
25
20
  }
26
21
 
27
22
  export function getResourceExternalUrl(dataset: Dataset | DatasetV2 | Omit<Dataset, 'resources' | 'community_resources'>, resource: Resource | CommunityResource): string {
28
- return `${dataset.page}#/${isCommunityResource(resource) ? 'community-resources' : 'resources'}/${resource.id}`
29
- }
30
-
31
- /**
32
- * Returns the short description to display.
33
- * If description_short is provided, it is used.
34
- * Otherwise, the first DESCRIPTION_SHORT_MAX_LENGTH characters of description are used.
35
- */
36
- export async function getDescriptionShort(
37
- description: string | null | undefined,
38
- descriptionShort: string | null | undefined,
39
- ): Promise<string> {
40
- if (descriptionShort?.trim()) {
41
- return descriptionShort
42
- }
43
- if (description?.trim()) {
44
- // description field is a markdown field that may contain HTML tags, so we should trim it
45
- const plainText = (await removeMarkdown(description)).trim()
46
- if (plainText.length > DESCRIPTION_SHORT_MAX_LENGTH) {
47
- return `${plainText.substring(0, DESCRIPTION_SHORT_MAX_LENGTH - 1)}…`
48
- }
49
- return plainText
50
- }
51
- return ''
23
+ return `${dataset.page}${isCommunityResource(resource) ? '/community-resources' : ''}?resource_id=${resource.id}`
52
24
  }
53
25
 
54
26
  export function getResourceFilesize(resource: Resource): null | number {
@@ -0,0 +1,33 @@
1
+ import { removeMarkdownSync } from './markdown'
2
+
3
+ // Dataset description constants
4
+
5
+ // Form validation (client-side rules)
6
+ export const DESCRIPTION_SHORT_MAX_LENGTH = 200 // max for `description_short` (+ truncation/output cap)
7
+ export const DESCRIPTION_MIN_LENGTH = 200 // min (recommendation) for `description` (not AI gating)
8
+
9
+ // AI gating (enable AI suggestions; not validation)
10
+ export const AI_SUGGESTION_MIN_DESCRIPTION_LENGTH = 200 // min `description` length to enable suggestions
11
+
12
+ /**
13
+ * Returns the short description to display.
14
+ * If description_short is provided, it is used.
15
+ * Otherwise, the first DESCRIPTION_SHORT_MAX_LENGTH characters of description are used.
16
+ */
17
+ export function getDescriptionShort({ description, descriptionShort }: {
18
+ description: string | null | undefined
19
+ descriptionShort?: string | null | undefined
20
+ }) {
21
+ if (descriptionShort?.trim()) {
22
+ return descriptionShort
23
+ }
24
+ if (description?.trim()) {
25
+ // description field is a markdown field that may contain HTML tags, so we should trim it
26
+ const plainText = removeMarkdownSync(description).trim()
27
+ if (plainText.length > DESCRIPTION_SHORT_MAX_LENGTH) {
28
+ return `${plainText.substring(0, DESCRIPTION_SHORT_MAX_LENGTH - 1)}…`
29
+ }
30
+ return plainText
31
+ }
32
+ return ''
33
+ }
@@ -37,3 +37,14 @@ export const summarize = (val: number, fractionDigits = 0) => {
37
37
  }
38
38
  return `${toFixedIfNotZero(val)}Y`
39
39
  }
40
+
41
+ export const escapeCsvValue = (value: string | number | null | undefined): string => {
42
+ if (value === null || value === undefined || value === '') {
43
+ return ''
44
+ }
45
+ const stringValue = String(value)
46
+ if (stringValue.includes(',') || stringValue.includes('"') || stringValue.includes('\n')) {
47
+ return `"${stringValue.replace(/"/g, '""')}"`
48
+ }
49
+ return stringValue
50
+ }
@@ -16,26 +16,57 @@ import remarkGfm from 'remark-gfm'
16
16
  import strip from 'strip-markdown'
17
17
 
18
18
  // Copied from https://github.com/potato4d/rehype-plugin-image-native-lazy-loading/blob/v1.2.0/src/index.ts
19
- function lazyLoadPlugin(this: Processor): Transformer {
20
- function visitor(el: hast.Element) {
21
- if (el.tagName !== 'img') {
22
- return
23
- }
24
- el.properties = {
25
- ...(el.properties || {}),
26
- loading: 'lazy',
27
- }
19
+ function rehypeLazyLoad(this: Processor): Transformer {
20
+ return function transformer(htmlAST: Node): Node {
21
+ visit(htmlAST, 'element', function visitor(el: hast.Element) {
22
+ if (el.tagName !== 'img') {
23
+ return
24
+ }
25
+ el.properties = {
26
+ ...(el.properties || {}),
27
+ loading: 'lazy',
28
+ }
29
+ })
30
+ return htmlAST
28
31
  }
32
+ }
33
+
34
+ function rehypeNoHeadings(this: Processor): Transformer {
35
+ return function transformer(htmlAST: Node): Node {
36
+ visit(htmlAST, 'element', function visitor(el: hast.Element) {
37
+ if (el.tagName !== 'h1' && el.tagName !== 'h2' && el.tagName !== 'h3' && el.tagName !== 'h4' && el.tagName !== 'h5' && el.tagName !== 'h6') {
38
+ return
39
+ }
40
+
41
+ const classes = {
42
+ h1: 'text-3xl leading-8',
43
+ h2: 'text-2xl leading-7',
44
+ h3: 'text-xl leading-6',
45
+ h4: 'text-base',
46
+ h5: 'text-sm leading-6',
47
+ h6: 'text-sm leading-6',
48
+ }[el.tagName]
29
49
 
30
- function transformer(htmlAST: Node): Node {
31
- visit(htmlAST, 'element', visitor)
50
+ el.properties = {
51
+ ...(el.properties || {}),
52
+ class: `font-extrabold ${classes}`,
53
+ }
54
+ el.tagName = 'div'
55
+ })
32
56
  return htmlAST
33
57
  }
34
-
35
- return transformer
36
58
  }
37
59
 
38
- export function formatMarkdown(md: string, minDepth = 3) {
60
+ export function formatMarkdown(md: string, config: number | { minDepth: number, noHeadings: boolean } = 3) {
61
+ let minDepth: number
62
+ let noHeadings = false
63
+ if (typeof config === 'number') {
64
+ minDepth = config
65
+ }
66
+ else {
67
+ minDepth = config.minDepth
68
+ noHeadings = config.noHeadings
69
+ }
39
70
  const result = unified()
40
71
  .use(behead, { minDepth: minDepth > 1 ? minDepth : undefined } as Options)
41
72
  // Take Markdown as input and turn it into MD syntax tree
@@ -55,14 +86,14 @@ export function formatMarkdown(md: string, minDepth = 3) {
55
86
  .use(rehypeSanitize)
56
87
  // Serialize syntax tree to HTML
57
88
  .use(rehypeStringify)
58
- .use(lazyLoadPlugin)
89
+ .use(noHeadings ? [rehypeLazyLoad, rehypeNoHeadings] : [rehypeLazyLoad])
59
90
  // And finally, process the input
60
91
  .processSync(md)
61
92
 
62
93
  return String(result)
63
94
  }
64
95
 
65
- export async function removeMarkdown(text: string) {
96
+ export async function removeMarkdownAsync(text: string) {
66
97
  const file = await unified()
67
98
  // Take Markdown as input and turn it into MD syntax tree
68
99
  .use(remarkParse, { fragment: true })
@@ -73,6 +104,19 @@ export async function removeMarkdown(text: string) {
73
104
  return String(file)
74
105
  }
75
106
 
107
+ export function removeMarkdownSync(text: string) {
108
+ const file = unified()
109
+ // Take Markdown as input and turn it into MD syntax tree
110
+ .use(remarkParse, { fragment: true })
111
+ .use(remarkGfm)
112
+ .use(strip)
113
+ .use(remarkStringify)
114
+ .processSync(text)
115
+ return String(file)
116
+ }
117
+
118
+ export { removeMarkdownAsync as removeMarkdown }
119
+
76
120
  const prose = 'prose prose-neutral max-w-none prose-strong:text-gray-plain'
77
121
  const proseSm = 'prose-p:text-sm prose-sm'
78
122
  const proseTable = 'prose-table:bg-gray-some prose-table:overflow-visible prose-thead:border-b-2 prose-thead:border-black prose-tr:data-[is-header=true]:border-b-2 prose-tr:data-[is-header=true]:border-black prose-tr:even:bg-gray-lower prose-tr:border-b-0 *:prose-th:m-0 *:prose-td:m-0 prose-th:p-4 prose-td:p-4'
@@ -1,3 +1,8 @@
1
+ import { escapeCsvValue } from './helpers'
2
+ import { ofetch } from 'ofetch'
3
+ import type { DatasetV2 } from '../types/datasets'
4
+ import type { PaginatedArray } from '../types/api'
5
+
1
6
  export type OrganizationMetrics = {
2
7
  downloads: Record<string, number>
3
8
  downloadsTotal: number
@@ -114,6 +119,34 @@ export async function getDatasetMetrics(datasetId: string, metricsApi: string):
114
119
  }
115
120
  }
116
121
 
122
+ export async function createDatasetsForOrganizationMetricsUrl(organizationId: string, metricsApi: string, apiBase: string) {
123
+ let data = 'dataset_title,dataset_id,month,monthly_visit,monthly_download_resource\n'
124
+
125
+ // fetch datasets info from organization datasets
126
+ const datasets: Record<string, Record<string, string>> = {}
127
+ let datasetsUrl: string | null = `/api/2/datasets/?organization=${organizationId}&page_size=200`
128
+ while (datasetsUrl) {
129
+ const body: PaginatedArray<DatasetV2> = await ofetch(datasetsUrl, { baseURL: apiBase, credentials: 'include' })
130
+ datasetsUrl = body.next_page
131
+ for (const row of body.data) {
132
+ datasets[row.id] = { title: row.title }
133
+ }
134
+ }
135
+
136
+ // fetch datasets metrics for the organization
137
+ let metricsUrl: string | null = `${metricsApi}/api/datasets/data/?organization_id__exact=${organizationId}&metric_month__sort=desc&page_size=50`
138
+ while (metricsUrl) {
139
+ const body: { links: { next: string | null }, data: Array<{ dataset_id: string, metric_month: string, monthly_visit: number, monthly_download_resource: number }> } = await ofetch(metricsUrl)
140
+ metricsUrl = body.links.next
141
+ for (const row of body.data) {
142
+ const datasetTitle = datasets[row.dataset_id]?.title || ''
143
+ data += `${escapeCsvValue(datasetTitle)},${escapeCsvValue(row.dataset_id)},${escapeCsvValue(row.metric_month)},${row.monthly_visit},${row.monthly_download_resource}\n`
144
+ }
145
+ }
146
+
147
+ return URL.createObjectURL(new Blob([data], { type: 'text/csv' }))
148
+ }
149
+
117
150
  export async function getDataserviceMetrics(dataserviceId: string, metricsApi: string): Promise<DataserviceMetrics> {
118
151
  // Fetching last 12 months
119
152
  const response = await fetch(`${metricsApi}/api/dataservices/data/?dataservice_id__exact=${dataserviceId}&metric_month__sort=desc&page_size=12`)
@@ -1,7 +1,7 @@
1
1
  import type { Component } from 'vue'
2
2
  import { RiBankLine, RiBuilding2Line, RiCommunityLine, RiGovernmentLine, RiUserLine } from '@remixicon/vue'
3
3
  import { useComponentsConfig } from '../config'
4
- import type { OrganizationReference } from '../types/organizations'
4
+ import type { Organization, OrganizationReference } from '../types/organizations'
5
5
  import { useTranslation } from '../composables/useTranslation'
6
6
 
7
7
  export const CERTIFIED = 'certified'
@@ -22,11 +22,11 @@ function constructUrl(baseUrl: string, path: string): string {
22
22
  return url.toString()
23
23
  }
24
24
 
25
- export function isType(organization: OrganizationReference, type: OrganizationTypes) {
25
+ export function isType(organization: Organization | OrganizationReference, type: OrganizationTypes) {
26
26
  return hasBadge(organization, type)
27
27
  }
28
28
 
29
- export function hasBadge(organization: OrganizationReference, kind: string) {
29
+ export function hasBadge(organization: Organization | OrganizationReference, kind: string) {
30
30
  return organization.badges.some(badge => badge.kind === kind)
31
31
  }
32
32
 
@@ -68,7 +68,7 @@ export function findOrganizationType(searched: OrganizationTypes | UserType) {
68
68
  return getOrganizationTypes().find(type => type.type === searched)!
69
69
  }
70
70
 
71
- export function getOrganizationType(organization: OrganizationReference): OrganizationTypes {
71
+ export function getOrganizationType(organization: Organization | OrganizationReference): OrganizationTypes {
72
72
  if (isType(organization, LOCAL_AUTHORITY)) {
73
73
  return LOCAL_AUTHORITY
74
74
  }
@@ -86,7 +86,7 @@ export function getOrganizationType(organization: OrganizationReference): Organi
86
86
  }
87
87
  }
88
88
 
89
- export function isOrganizationCertified(organization: OrganizationReference | null): boolean {
89
+ export function isOrganizationCertified(organization: Organization | OrganizationReference | null): boolean {
90
90
  if (!organization) return false
91
91
  return hasBadge(organization, CERTIFIED) && (isType(organization, PUBLIC_SERVICE) || isType(organization, LOCAL_AUTHORITY))
92
92
  }
package/src/main.ts CHANGED
@@ -3,23 +3,28 @@ import type { Activity, ActivityKey } from './types/activity.js'
3
3
  import type { PaginatedArray } from './types/api.js'
4
4
  import type { ContactPoint, ContactPointRole } from './types/contact_point.js'
5
5
  import type { Badge, Badges, TranslatedBadge } from './types/badges'
6
- import type { Dataset, DatasetV2, DatasetV2WithFullObject, NewDataset, Quality, Rel } from './types/datasets'
7
- import type { NewDataservice, Dataservice } from './types/dataservices'
6
+ import type { DatasetReference, Dataset, DatasetV2, DatasetV2WithFullObject, NewDataset, Quality, Rel } from './types/datasets'
7
+ import type { DataserviceReference, NewDataservice, Dataservice } from './types/dataservices'
8
8
  import type { AccessType, AccessAudience, AccessAudienceCondition, AccessAudienceType, WithAccessType, AccessTypeForm } from './types/access_types'
9
9
  import type { Frequency, Frequencies } from './types/frequency'
10
10
  import type { Granularity, Granularities, SpatialZone } from './types/granularity'
11
11
  import type { Harvest } from './types/harvest'
12
12
  import type { License } from './types/licenses'
13
13
  import type { Member, MemberRole, NewOrganization, Organization, OrganizationOrSuggest, OrganizationReference, OrganizationSuggest } from './types/organizations'
14
- import type { Owned, OwnedWithId } from './types/owned'
15
- import type { NewReuse, Reuse, ReuseTopic, ReuseType } from './types/reuses'
14
+ import type { Owned, OwnedWithFullObject, OwnedWithId } from './types/owned'
15
+ import type { Comment, Thread } from './types/discussions'
16
+ import type { Page, PageBloc, ContentBloc, BlocWithTitle, DatasetsListBloc, DataservicesListBloc, ReusesListBloc, LinkInBloc, LinksListBloc, MarkdownBloc, AccordionItemBloc, AccordionListBloc, HeroBloc } from './types/pages'
17
+ import type { Post } from './types/posts'
18
+ import type { ReuseReference, NewReuse, Reuse, ReuseTopic, ReuseType } from './types/reuses'
16
19
  import type { RegisteredSchema, Schema, SchemaDetails, SchemaField, SchemaPath, SchemaPublicationMode, SchemaResponseData, SchemaVersion, ValidataError } from './types/schemas'
17
20
  import type { TopicV2, TopicElement, TopicElementClass, TopicElementRel } from './types/topics'
18
- import type { CommunityResource, FileResourceFileType, RemoteResourceFileType, ResourceFileType, ResourceType, Resource } from './types/resources'
21
+ import type { CommunityResource, FileResourceFileType, RemoteResourceFileType, ResourceFileType, ResourceGroup, ResourceType, Resource } from './types/resources'
19
22
  import type { Site } from './types/site'
20
23
  import type { Weight, WellType } from './types/ui'
21
24
  import type { User, UserReference } from './types/users'
22
25
  import type { Report, ReportSubject, ReportReason } from './types/reports'
26
+ import type { GlobalSearchConfig, SearchType, SortOption } from './types/search'
27
+ import { getDefaultDatasetConfig, getDefaultDataserviceConfig, getDefaultReuseConfig, getDefaultGlobalSearchConfig, defaultDatasetSortOptions, defaultDataserviceSortOptions, defaultReuseSortOptions } from './types/search'
23
28
 
24
29
  import ActivityList from './components/ActivityList/ActivityList.vue'
25
30
  import UserActivityList from './components/ActivityList/UserActivityList.vue'
@@ -32,8 +37,14 @@ import BrandedButton from './components/BrandedButton.vue'
32
37
  import CopyButton from './components/CopyButton.vue'
33
38
  import DataserviceCard from './components/DataserviceCard.vue'
34
39
  import DatasetCard from './components/DatasetCard.vue'
40
+ import DescriptionListTerm from './components/DescriptionListTerm.vue'
41
+ import DescriptionListDetails from './components/DescriptionListDetails.vue'
42
+ import DiscussionMessageCard from './components/DiscussionMessageCard.vue'
35
43
  import DateRangeDetails from './components/DateRangeDetails.vue'
36
- import DatasetInformationPanel from './components/DatasetInformationPanel.vue'
44
+ import { DatasetInformationSection, DatasetTemporalitySection, DatasetSpatialSection, DatasetSchemaSection, DatasetEmbedSection } from './components/DatasetInformation'
45
+ import LeafletMap from './components/LeafletMap.vue'
46
+ import LicenseBadge from './components/LicenseBadge.vue'
47
+ import Tag from './components/Tag.vue'
37
48
  import DatasetQuality from './components/DatasetQuality.vue'
38
49
  import DatasetQualityInline from './components/DatasetQualityInline.vue'
39
50
  import DatasetQualityItem from './components/DatasetQualityItem.vue'
@@ -51,11 +62,18 @@ import OwnerTypeIcon from './components/OwnerTypeIcon.vue'
51
62
  import PaddedContainer from './components/PaddedContainer.vue'
52
63
  import Pagination from './components/Pagination.vue'
53
64
  import Placeholder from './components/Placeholder.vue'
65
+ import RadioGroup from './components/RadioGroup.vue'
66
+ import RadioInput from './components/RadioInput.vue'
67
+ import PostCard from './components/PostCard.vue'
54
68
  import ReadMore from './components/ReadMore.vue'
55
69
  import ResourceAccordion from './components/ResourceAccordion/ResourceAccordion.vue'
56
70
  import ResourceIcon from './components/ResourceAccordion/ResourceIcon.vue'
71
+ import ResourceExplorer from './components/ResourceExplorer/ResourceExplorer.vue'
72
+ import ResourceExplorerSidebar from './components/ResourceExplorer/ResourceExplorerSidebar.vue'
73
+ import ResourceExplorerViewer from './components/ResourceExplorer/ResourceExplorerViewer.vue'
57
74
  import Swagger from './components/ResourceAccordion/Swagger.client.vue'
58
75
  import ReuseCard from './components/ReuseCard.vue'
76
+ import ReuseHorizontalCard from './components/ReuseHorizontalCard.vue'
59
77
  import ReuseDetails from './components/ReuseDetails.vue'
60
78
  import SchemaCard from './components/SchemaCard.vue'
61
79
  import SimpleBanner from './components/SimpleBanner.vue'
@@ -68,10 +86,17 @@ import TabPanel from './components/Tabs/TabPanel.vue'
68
86
  import TabPanels from './components/Tabs/TabPanels.vue'
69
87
  import Tooltip from './components/Tooltip.vue'
70
88
  import Toggletip from './components/Toggletip.vue'
89
+ import TopicCard from './components/TopicCard.vue'
71
90
  import TranslationT from './components/TranslationT.vue'
91
+ import GlobalSearch from './components/Search/GlobalSearch.vue'
92
+ import SearchInput from './components/Search/SearchInput.vue'
93
+ import SearchableSelect from './components/Form/SearchableSelect.vue'
94
+ import SelectGroup from './components/Form/SelectGroup.vue'
72
95
  import type { UseFetchFunction } from './functions/api.types'
73
96
  import { configKey, useComponentsConfig, type PluginConfig } from './config.js'
74
97
 
98
+ export { Toaster, toast } from 'vue-sonner'
99
+
75
100
  export * from './composables/useActiveDescendant'
76
101
  export * from './composables/useMetrics'
77
102
  export * from './composables/useReuseType'
@@ -80,6 +105,7 @@ export * from './composables/useTranslation'
80
105
  export * from './functions/activities'
81
106
  export * from './functions/datasets'
82
107
  export * from './functions/dates'
108
+ export * from './functions/description'
83
109
  export * from './functions/helpers'
84
110
  export * from './functions/markdown'
85
111
  export * from './functions/matomo'
@@ -95,6 +121,9 @@ export * from './functions/users'
95
121
  export * from './types/access_types'
96
122
 
97
123
  export type {
124
+ GlobalSearchConfig,
125
+ SearchType,
126
+ SortOption,
98
127
  UseFetchFunction,
99
128
  AccessType,
100
129
  AccessAudience,
@@ -109,10 +138,13 @@ export type {
109
138
  CommunityResource,
110
139
  ContactPoint,
111
140
  ContactPointRole,
141
+ DatasetReference,
112
142
  Dataset,
113
143
  DatasetV2,
114
144
  DatasetV2WithFullObject,
145
+ DataserviceReference,
115
146
  Dataservice,
147
+ Comment,
116
148
  NewDataservice,
117
149
  FileResourceFileType,
118
150
  Frequency,
@@ -131,8 +163,24 @@ export type {
131
163
  OrganizationSuggest,
132
164
  OrganizationOrSuggest,
133
165
  Owned,
166
+ OwnedWithFullObject,
134
167
  OwnedWithId,
168
+ Page,
169
+ PageBloc,
170
+ ContentBloc,
171
+ BlocWithTitle,
172
+ DatasetsListBloc,
173
+ DataservicesListBloc,
174
+ ReusesListBloc,
175
+ LinkInBloc,
176
+ LinksListBloc,
177
+ MarkdownBloc,
178
+ AccordionItemBloc,
179
+ AccordionListBloc,
180
+ HeroBloc,
135
181
  PaginatedArray,
182
+ Post,
183
+ Thread,
136
184
  Quality,
137
185
  RegisteredSchema,
138
186
  Rel,
@@ -142,7 +190,9 @@ export type {
142
190
  ReportReason,
143
191
  Resource,
144
192
  ResourceFileType,
193
+ ResourceGroup,
145
194
  ResourceType,
195
+ ReuseReference,
146
196
  Reuse,
147
197
  ReuseTopic,
148
198
  ReuseType,
@@ -167,6 +217,16 @@ export type {
167
217
  WellType,
168
218
  }
169
219
 
220
+ export {
221
+ getDefaultDatasetConfig,
222
+ getDefaultDataserviceConfig,
223
+ getDefaultReuseConfig,
224
+ getDefaultGlobalSearchConfig,
225
+ defaultDatasetSortOptions,
226
+ defaultDataserviceSortOptions,
227
+ defaultReuseSortOptions,
228
+ }
229
+
170
230
  // Vue Plugin
171
231
  const datagouv: Plugin<PluginConfig> = {
172
232
  async install(app: App, options) {
@@ -191,7 +251,14 @@ export {
191
251
  CopyButton,
192
252
  DataserviceCard,
193
253
  DatasetCard,
194
- DatasetInformationPanel,
254
+ DatasetInformationSection,
255
+ DatasetTemporalitySection,
256
+ DatasetSpatialSection,
257
+ DatasetSchemaSection,
258
+ DatasetEmbedSection,
259
+ DescriptionListTerm,
260
+ DescriptionListDetails,
261
+ DiscussionMessageCard,
195
262
  DatasetQuality,
196
263
  DatasetQualityInline,
197
264
  DatasetQualityItem,
@@ -200,7 +267,10 @@ export {
200
267
  DateRangeDetails,
201
268
  ExtraAccordion,
202
269
  LabelTag,
270
+ LeafletMap,
271
+ LicenseBadge,
203
272
  LoadingBlock,
273
+ Tag,
204
274
  MarkdownViewer,
205
275
  OrganizationCard,
206
276
  OrganizationLogo,
@@ -210,11 +280,18 @@ export {
210
280
  PaddedContainer,
211
281
  Pagination,
212
282
  Placeholder,
283
+ PostCard,
284
+ RadioGroup,
285
+ RadioInput,
213
286
  ReadMore,
214
287
  ResourceAccordion,
288
+ ResourceExplorer,
289
+ ResourceExplorerSidebar,
290
+ ResourceExplorerViewer,
215
291
  ResourceIcon,
216
292
  ReuseCard,
217
293
  ReuseDetails,
294
+ ReuseHorizontalCard,
218
295
  SchemaCard,
219
296
  SimpleBanner,
220
297
  SmallChart,
@@ -227,6 +304,11 @@ export {
227
304
  TabPanels,
228
305
  Tooltip,
229
306
  Toggletip,
307
+ TopicCard,
230
308
  TranslationT,
231
309
  UserActivityList,
310
+ GlobalSearch,
311
+ SearchInput,
312
+ SearchableSelect,
313
+ SelectGroup,
232
314
  }
@@ -2,19 +2,21 @@ import type { Harvest } from './harvest'
2
2
  import type { Owned, OwnedWithId } from './owned'
3
3
  import type { ContactPoint } from './contact_point'
4
4
  import type { WithAccessType } from './access_types'
5
+ import type { DatasetReference } from './datasets'
6
+
7
+ export type DataserviceReference = {
8
+ class: 'Dataservice'
9
+ id: string
10
+ title: string
11
+ self_api_url: string
12
+ self_web_url: string
13
+ }
5
14
 
6
15
  export type BaseDataservice = Owned & WithAccessType & {
7
16
  acronym: string
8
17
  availability: number | null
9
18
  base_api_url: string | null
10
- datasets: Array<{
11
- class: string
12
- id: string
13
- acronym: string
14
- page: string
15
- title: string
16
- uri: string
17
- }>
19
+ datasets: Array<DatasetReference>
18
20
  description: string
19
21
  machine_documentation_url: string | null
20
22
  technical_documentation_url: string | null
@@ -22,7 +24,7 @@ export type BaseDataservice = Owned & WithAccessType & {
22
24
  license: string | null
23
25
  private: boolean
24
26
  rate_limiting: string
25
- title: string
27
+ title: DataserviceReference['title']
26
28
  contact_points: Array<ContactPoint>
27
29
  }
28
30
 
@@ -50,7 +52,7 @@ export type Dataservice = Owned & WithAccessType & {
50
52
  extras: Record<string, unknown>
51
53
  format: string
52
54
  harvest: Harvest
53
- id: string
55
+ id: DataserviceReference['id']
54
56
  license: string | null
55
57
  metadata_modified_at: string
56
58
  metrics: {
@@ -63,8 +65,8 @@ export type Dataservice = Owned & WithAccessType & {
63
65
  permissions: { edit: boolean, delete: boolean }
64
66
  private: boolean
65
67
  rate_limiting: string
66
- self_api_url: string
67
- self_web_url: string
68
+ self_api_url: DataserviceReference['self_api_url']
69
+ self_web_url: DataserviceReference['self_web_url']
68
70
  slug: string
69
71
  tags: Array<string>
70
72
  title: string