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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/assets/main.css +4 -0
  2. package/dist/{Control-DuZJdKV_.js → Control-ZFh5ta_U.js} +1 -1
  3. package/dist/{Datafair.client-8haHXl47.js → Datafair.client-rf4T1IkA.js} +1 -1
  4. package/dist/{Event--kp8kMdJ.js → Event-DSQcW7OF.js} +24 -24
  5. package/dist/{Image-34hvypZI.js → Image-BijNEG0p.js} +6 -6
  6. package/dist/JsonPreview.client-dzar6iuh.js +40 -0
  7. package/dist/{Map-BjUnLyj8.js → Map-BUtPf5GN.js} +756 -756
  8. package/dist/{MapContainer.client-l6HuXTHR.js → MapContainer.client-D-MoRNhG.js} +37 -38
  9. package/dist/{OSM-s40W6sQ2.js → OSM-D4MTdBtk.js} +2 -2
  10. package/dist/{PdfPreview.client-4OueK-2Z.js → PdfPreview.client-DoDYLmJD.js} +822 -850
  11. package/dist/{Pmtiles.client-4j3VTYkz.js → Pmtiles.client-Dzm01Zfm.js} +1 -1
  12. package/dist/PreviewWrapper.vue_vue_type_script_setup_true_lang-BRNYswg3.js +61 -0
  13. package/dist/{ScaleLine-KW-nXqp3.js → ScaleLine-hJQIqcZm.js} +2 -2
  14. package/dist/{Tile-DbNFNPfU.js → Tile-Dcl7oIVu.js} +35 -35
  15. package/dist/{TileImage-BsXBxMtq.js → TileImage-BJeHipMX.js} +4 -4
  16. package/dist/{View-BR92hTWP.js → View-xp_P_OHw.js} +412 -401
  17. package/dist/XmlPreview.client-cOhwff6P.js +34 -0
  18. package/dist/{common-PJfpC179.js → common-BjQlan3k.js} +36 -36
  19. package/dist/components-next.css +6 -6
  20. package/dist/components-next.js +165 -142
  21. package/dist/components.css +1 -1
  22. package/dist/{index-CVTIoZQ0.js → index-NofRBuyf.js} +32886 -27183
  23. package/dist/main-Iz1ZCL6k.js +73606 -0
  24. package/dist/{proj-DsetBcW7.js → proj-CsNo9yH1.js} +532 -512
  25. package/dist/{tilecoord-Db24Px13.js → tilecoord-A0fLnBZr.js} +28 -28
  26. package/dist/{vue3-xml-viewer.common-CWer_T5-.js → vue3-xml-viewer.common-tVI9uXUz.js} +1 -1
  27. package/package.json +25 -11
  28. package/src/chart.ts +5 -0
  29. package/src/components/ActivityList/ActivityList.vue +3 -2
  30. package/src/components/Chart/ChartViewer.vue +226 -0
  31. package/src/components/Chart/ChartViewerWrapper.vue +170 -0
  32. package/src/components/DataserviceCard.vue +3 -0
  33. package/src/components/DatasetCard.vue +9 -4
  34. package/src/components/Form/Listbox.vue +101 -0
  35. package/src/components/Form/SearchableSelect.vue +2 -1
  36. package/src/components/InfiniteLoader.vue +53 -0
  37. package/src/components/ObjectCardHeader.vue +11 -4
  38. package/src/components/OpenApiViewer/ContentTypeSelect.vue +48 -0
  39. package/src/components/OpenApiViewer/EndpointRequest.vue +164 -0
  40. package/src/components/OpenApiViewer/EndpointResponses.vue +149 -0
  41. package/src/components/OpenApiViewer/OpenApiViewer.vue +308 -0
  42. package/src/components/OpenApiViewer/SchemaPanel.vue +53 -0
  43. package/src/components/OpenApiViewer/SchemaTree.vue +77 -0
  44. package/src/components/OpenApiViewer/openapi.ts +150 -0
  45. package/src/components/OrganizationNameWithCertificate.vue +3 -2
  46. package/src/components/Pagination.vue +8 -5
  47. package/src/components/RadioInput.vue +7 -2
  48. package/src/components/ReadMore.vue +1 -1
  49. package/src/components/ResourceAccordion/DataStructure.vue +11 -33
  50. package/src/components/ResourceAccordion/Downloads.vue +160 -0
  51. package/src/components/ResourceAccordion/JsonPreview.client.vue +23 -104
  52. package/src/components/ResourceAccordion/MapContainer.client.vue +1 -3
  53. package/src/components/ResourceAccordion/Metadata.vue +1 -2
  54. package/src/components/ResourceAccordion/PdfPreview.client.vue +24 -87
  55. package/src/components/ResourceAccordion/Preview.vue +11 -11
  56. package/src/components/ResourceAccordion/PreviewWrapper.vue +82 -0
  57. package/src/components/ResourceAccordion/ResourceAccordion.vue +10 -109
  58. package/src/components/ResourceAccordion/XmlPreview.client.vue +16 -98
  59. package/src/components/ResourceExplorer/ResourceExplorer.vue +14 -10
  60. package/src/components/ResourceExplorer/ResourceExplorerSidebar.vue +2 -2
  61. package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +46 -147
  62. package/src/components/ResourceExplorer/ResourceSelector.vue +113 -0
  63. package/src/components/ReuseCard.vue +12 -4
  64. package/src/components/Search/GlobalSearch.vue +201 -113
  65. package/src/components/Search/SearchInput.vue +5 -4
  66. package/src/components/TabularExplorer/TabularCell.vue +51 -0
  67. package/src/components/TabularExplorer/TabularCellPopover.vue +170 -0
  68. package/src/components/TabularExplorer/TabularExplorer.vue +973 -0
  69. package/src/components/TabularExplorer/TabularFilterContent.vue +351 -0
  70. package/src/components/TabularExplorer/TabularFilterPopover.vue +111 -0
  71. package/src/components/TabularExplorer/types.ts +83 -0
  72. package/src/composables/useHasTabularData.ts +13 -0
  73. package/src/composables/useMetrics.ts +1 -1
  74. package/src/composables/useSearchFilter.ts +118 -0
  75. package/src/composables/useStableQueryParams.ts +38 -6
  76. package/src/composables/useTabularProfile.ts +70 -0
  77. package/src/config.ts +20 -3
  78. package/src/functions/activities.ts +3 -3
  79. package/src/functions/api.ts +9 -37
  80. package/src/functions/api.types.ts +1 -0
  81. package/src/functions/charts.ts +68 -0
  82. package/src/functions/datasets.ts +0 -17
  83. package/src/functions/metrics.ts +6 -4
  84. package/src/functions/resources.ts +56 -1
  85. package/src/functions/tabular.ts +60 -0
  86. package/src/functions/tabularApi.ts +138 -11
  87. package/src/main.ts +90 -9
  88. package/src/types/dataservices.ts +2 -0
  89. package/src/types/pages.ts +0 -5
  90. package/src/types/posts.ts +2 -2
  91. package/src/types/reports.ts +5 -1
  92. package/src/types/search.ts +63 -1
  93. package/src/types/site.ts +5 -3
  94. package/src/types/ui.ts +2 -0
  95. package/src/types/users.ts +2 -1
  96. package/src/types/visualizations.ts +89 -0
  97. package/assets/swagger-themes/newspaper.css +0 -1670
  98. package/dist/JsonPreview.client-D53pj9Cw.js +0 -72
  99. package/dist/Swagger.client-DPBmsH9q.js +0 -4
  100. package/dist/XmlPreview.client-XElkoA4F.js +0 -64
  101. package/dist/main-BbT-LUXy.js +0 -105854
  102. package/src/components/ResourceAccordion/Swagger.client.vue +0 -48
  103. package/src/functions/pagination.ts +0 -9
@@ -0,0 +1,60 @@
1
+ import type { Component } from 'vue'
2
+ import {
3
+ RiHashtag,
4
+ RiPriceTag3Line,
5
+ RiText,
6
+ RiCalendarLine,
7
+ RiCheckboxLine,
8
+ } from '@remixicon/vue'
9
+ import { useTranslation } from '../composables/useTranslation'
10
+ import type { ColumnFilters, ColumnType } from '../components/TabularExplorer/types'
11
+
12
+ export function hasFilterForColumn(filters: Record<string, ColumnFilters>, column: string): boolean {
13
+ const f = filters[column]
14
+ if (!f) return false
15
+ return !!(f.in?.length || f.exact != null || f.contains || f.null || f.min != null || f.max != null)
16
+ }
17
+
18
+ export function buildTypeConfig(t: (s: string) => string): Record<ColumnType, { icon: Component, label: string }> {
19
+ return {
20
+ number: { icon: RiHashtag, label: t('Nombre') },
21
+ categorical: { icon: RiPriceTag3Line, label: t('Catégoriel') },
22
+ text: { icon: RiText, label: t('Texte') },
23
+ date: { icon: RiCalendarLine, label: t('Date') },
24
+ boolean: { icon: RiCheckboxLine, label: t('Booléen') },
25
+ }
26
+ }
27
+
28
+ export function useFormatTabular() {
29
+ const { locale } = useTranslation()
30
+
31
+ function formatNumber(value: unknown): string {
32
+ const num = Number(value)
33
+ if (Number.isNaN(num)) return String(value)
34
+ return num.toLocaleString(locale)
35
+ }
36
+
37
+ function formatCellDate(value: unknown): string {
38
+ if (value == null || value === '') return '–'
39
+ const d = new Date(String(value))
40
+ if (Number.isNaN(d.getTime())) return String(value)
41
+ return new Intl.DateTimeFormat(locale, { day: '2-digit', month: '2-digit', year: 'numeric' }).format(d)
42
+ }
43
+
44
+ return { formatNumber, formatCellDate }
45
+ }
46
+
47
+ const TRUTHY_VALUES = ['true', '1', 'oui', 'yes']
48
+ const FALSY_VALUES = ['false', '0', 'non', 'no']
49
+
50
+ export function isTruthy(value: unknown): boolean {
51
+ if (typeof value === 'boolean') return value
52
+ if (typeof value === 'string') return TRUTHY_VALUES.includes(value.toLowerCase())
53
+ return Boolean(value)
54
+ }
55
+
56
+ export function isFalsy(value: unknown): boolean {
57
+ if (typeof value === 'boolean') return !value
58
+ if (typeof value === 'string') return FALSY_VALUES.includes(value.toLowerCase())
59
+ return !value
60
+ }
@@ -1,20 +1,147 @@
1
1
  import { ofetch } from 'ofetch'
2
2
  import { useComponentsConfig, type PluginConfig } from '../config'
3
+ import type { GenericFilter } from '../types/visualizations'
4
+ import type { SortConfig } from '../components/TabularExplorer/types'
3
5
 
4
- export type SortConfig = {
5
- column: string
6
- type: string
7
- } | null
6
+ export type { SortConfig }
7
+
8
+ export type TabularDataResponse = {
9
+ data: Array<Record<string, unknown>>
10
+ links: {
11
+ profile: string
12
+ swagger: string
13
+ next: string
14
+ }
15
+ meta: { total: number }
16
+ }
17
+
18
+ export type TabularAggregateType = 'avg' | 'sum' | 'count' | 'min' | 'max'
19
+
20
+ export type FetchTabularDataOptions = {
21
+ resourceId: string
22
+ page?: number
23
+ pageSize?: number
24
+ columns?: Array<string> | undefined
25
+ sort?: SortConfig
26
+ groupBy?: string | undefined
27
+ aggregation?: {
28
+ column: string
29
+ type: TabularAggregateType
30
+ } | undefined
31
+ filters?: GenericFilter | undefined
32
+ }
33
+
34
+ export type TabularProfileResponse = {
35
+ profile: {
36
+ header: Array<string>
37
+ columns: Record<string, {
38
+ score: number
39
+ format: string
40
+ python_type: string
41
+ }>
42
+ formats: Record<string, Array<string>>
43
+ profile: Record<string, {
44
+ tops: Array<{ count: number, value: string }>
45
+ nb_distinct: number
46
+ nb_missing_values: number
47
+ min?: number
48
+ max?: number
49
+ std?: number
50
+ mean?: number
51
+ }>
52
+ encoding: string
53
+ separator: string
54
+ categorical: Array<string>
55
+ total_lines: number
56
+ nb_duplicates: number
57
+ columns_fields: Record<string, {
58
+ score: number
59
+ format: string
60
+ python_type: string
61
+ }>
62
+ columns_labels: Record<string, {
63
+ score: number
64
+ format: string
65
+ python_type: string
66
+ }>
67
+ header_row_idx: number
68
+ heading_columns: number
69
+ trailing_columns: number
70
+ }
71
+ deleted_at: string | null
72
+ dataset_id: string
73
+ indexes: null
74
+ }
8
75
 
9
76
  /**
10
- * Call Tabular-api to get table content
77
+ * Call Tabular-api to get table content with options object
11
78
  */
12
- export async function getData(config: PluginConfig, id: string, page: number, sortConfig?: SortConfig) {
13
- let url = `${config.tabularApiUrl}/api/resources/${id}/data/?page=${page}&page_size=${config.tabularApiPageSize || 15}`
14
- if (sortConfig) {
15
- url = url + `&${sortConfig.column}__sort=${sortConfig.type}`
79
+ export async function fetchTabularData(config: PluginConfig, options: FetchTabularDataOptions): Promise<TabularDataResponse> {
80
+ const page = options.page ?? 1
81
+ const pageSize = options.pageSize ?? config.tabularApiPageSize ?? 15
82
+ let url = `${config.tabularApiUrl}/api/resources/${options.resourceId}/data/?page=${page}&page_size=${pageSize}`
83
+ if (options.columns) {
84
+ url += `&columns=${options.columns.map(col => encodeURIComponent(col)).join(',')}`
85
+ }
86
+ if (options.sort) {
87
+ url += `&${encodeURIComponent(options.sort.column)}__sort=${encodeURIComponent(options.sort.direction)}`
16
88
  }
17
- return await ofetch(url)
89
+ if (options.groupBy && options.aggregation?.type) {
90
+ url += `&${encodeURIComponent(options.groupBy)}__groupby&${encodeURIComponent(options.aggregation.column)}__${encodeURIComponent(options.aggregation.type)}`
91
+ }
92
+ if (options.filters) {
93
+ const filterQuery = buildFilterQuery(options.filters)
94
+ if (filterQuery) {
95
+ url += `&${filterQuery}`
96
+ }
97
+ }
98
+ return await ofetch<TabularDataResponse>(url)
99
+ }
100
+
101
+ function buildFilterQuery(filters: GenericFilter): string {
102
+ const params: Array<string> = []
103
+ if ('filters' in filters) {
104
+ for (const filter of filters.filters) {
105
+ if ('filters' in filter) {
106
+ params.push(buildFilterQuery(filter))
107
+ }
108
+ else {
109
+ if (filter.condition === 'is_null') {
110
+ params.push(`${encodeURIComponent(filter.column)}__isnull`)
111
+ }
112
+ else if (filter.condition === 'is_not_null') {
113
+ params.push(`${encodeURIComponent(filter.column)}__isnotnull`)
114
+ }
115
+ else if (filter.value !== null && filter.value !== undefined && filter.value !== '') {
116
+ params.push(`${encodeURIComponent(filter.column)}__${encodeURIComponent(filter.condition)}=${encodeURIComponent(filter.value)}`)
117
+ }
118
+ }
119
+ }
120
+ }
121
+ else {
122
+ const filter = filters
123
+ if (filter.condition === 'is_null') {
124
+ params.push(`${encodeURIComponent(filter.column)}__isnull`)
125
+ }
126
+ else if (filter.condition === 'is_not_null') {
127
+ params.push(`${encodeURIComponent(filter.column)}__isnotnull`)
128
+ }
129
+ else if (filter.value !== null && filter.value !== undefined && filter.value !== '') {
130
+ params.push(`${encodeURIComponent(filter.column)}__${encodeURIComponent(filter.condition)}=${encodeURIComponent(filter.value)}`)
131
+ }
132
+ }
133
+ return params.join('&')
134
+ }
135
+
136
+ /**
137
+ * Call Tabular-api to get table content
138
+ */
139
+ export async function getData(config: PluginConfig, id: string, page: number, sortConfig?: SortConfig | null): Promise<TabularDataResponse> {
140
+ return fetchTabularData(config, {
141
+ resourceId: id,
142
+ page,
143
+ sort: sortConfig ?? undefined,
144
+ })
18
145
  }
19
146
 
20
147
  /**
@@ -22,5 +149,5 @@ export async function getData(config: PluginConfig, id: string, page: number, so
22
149
  */
23
150
  export function useGetProfile() {
24
151
  const config = useComponentsConfig()
25
- return (id: string) => ofetch(`${config.tabularApiUrl}/api/resources/${id}/profile/`)
152
+ return (id: string) => ofetch<TabularProfileResponse>(`${config.tabularApiUrl}/api/resources/${id}/profile/`)
26
153
  }
package/src/main.ts CHANGED
@@ -13,7 +13,7 @@ import type { License } from './types/licenses'
13
13
  import type { Member, MemberRole, NewOrganization, Organization, OrganizationOrSuggest, OrganizationReference, OrganizationSuggest } from './types/organizations'
14
14
  import type { Owned, OwnedWithFullObject, OwnedWithId } from './types/owned'
15
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'
16
+ import type { PageBloc, ContentBloc, BlocWithTitle, DatasetsListBloc, DataservicesListBloc, ReusesListBloc, LinkInBloc, LinksListBloc, MarkdownBloc, AccordionItemBloc, AccordionListBloc, HeroBloc } from './types/pages'
17
17
  import type { Post } from './types/posts'
18
18
  import type { ReuseReference, NewReuse, Reuse, ReuseTopic, ReuseType } from './types/reuses'
19
19
  import type { RegisteredSchema, Schema, SchemaDetails, SchemaField, SchemaPath, SchemaPublicationMode, SchemaResponseData, SchemaVersion, ValidataError } from './types/schemas'
@@ -23,8 +23,11 @@ import type { Site } from './types/site'
23
23
  import type { Weight, WellType } from './types/ui'
24
24
  import type { User, UserReference } from './types/users'
25
25
  import type { Report, ReportSubject, ReportReason } from './types/reports'
26
- import type { GlobalSearchConfig, SearchType, SortOption } from './types/search'
27
- import { getDefaultDatasetConfig, getDefaultDataserviceConfig, getDefaultReuseConfig, getDefaultOrganizationConfig, getDefaultGlobalSearchConfig, defaultDatasetSortOptions, defaultDataserviceSortOptions, defaultReuseSortOptions, defaultOrganizationSortOptions } from './types/search'
26
+ import type { Chart, ChartForm, ChartForApi, FilterCondition, Filter, AndFilters, GenericFilter, XAxisType, XAxisSortBy, SortDirection, XAxis, XAxisForm, UnitPosition, YAxis, DataSeriesType, DataSeries, DataSeriesForm } from './types/visualizations'
27
+ import type { GlobalSearchConfig, SearchType, SearchTypeConfig, SortOption, HiddenFilter, BuiltInFilterKey, DatasetSearchConfig, DatasetSearchFilters, DataserviceSearchConfig, DataserviceSearchFilters, ReuseSearchConfig, ReuseSearchFilters, OrganizationSearchConfig, OrganizationSearchFilters, TopicSearchConfig, TopicSearchFilters } from './types/search'
28
+ import { getDefaultDatasetConfig, getDefaultDataserviceConfig, getDefaultReuseConfig, getDefaultOrganizationConfig, getDefaultTopicConfig, getDefaultGlobalSearchConfig, defaultDatasetSortOptions, defaultDataserviceSortOptions, defaultReuseSortOptions, defaultOrganizationSortOptions } from './types/search'
29
+ import { useSearchFilter } from './composables/useSearchFilter'
30
+ import type { UseSearchFilterOptions } from './composables/useSearchFilter'
28
31
 
29
32
  import ActivityList from './components/ActivityList/ActivityList.vue'
30
33
  import UserActivityList from './components/ActivityList/UserActivityList.vue'
@@ -37,6 +40,9 @@ import BrandedButton from './components/BrandedButton.vue'
37
40
  import CopyButton from './components/CopyButton.vue'
38
41
  import DataserviceCard from './components/DataserviceCard.vue'
39
42
  import DatasetCard from './components/DatasetCard.vue'
43
+ import DataStructure from './components/ResourceAccordion/DataStructure.vue'
44
+ import Downloads from './components/ResourceAccordion/Downloads.vue'
45
+ import Metadata from './components/ResourceAccordion/Metadata.vue'
40
46
  import DescriptionListTerm from './components/DescriptionListTerm.vue'
41
47
  import DescriptionListDetails from './components/DescriptionListDetails.vue'
42
48
  import DiscussionMessageCard from './components/DiscussionMessageCard.vue'
@@ -57,6 +63,7 @@ import LoadingBlock from './components/LoadingBlock.vue'
57
63
  import MarkdownViewer from './components/MarkdownViewer.vue'
58
64
  import OrganizationCard from './components/OrganizationCard.vue'
59
65
  import OrganizationHorizontalCard from './components/OrganizationHorizontalCard.vue'
66
+ import ObjectCardOwner from './components/ObjectCardOwner.vue'
60
67
  import OrganizationLogo from './components/OrganizationLogo.vue'
61
68
  import OrganizationNameWithCertificate from './components/OrganizationNameWithCertificate.vue'
62
69
  import OwnerType from './components/OwnerType.vue'
@@ -70,16 +77,16 @@ import PostCard from './components/PostCard.vue'
70
77
  import ReadMore from './components/ReadMore.vue'
71
78
  import ResourceAccordion from './components/ResourceAccordion/ResourceAccordion.vue'
72
79
  import ResourceIcon from './components/ResourceAccordion/ResourceIcon.vue'
80
+ import ResourceSelector from './components/ResourceExplorer/ResourceSelector.vue'
73
81
  import ResourceExplorer from './components/ResourceExplorer/ResourceExplorer.vue'
74
82
  import ResourceExplorerSidebar from './components/ResourceExplorer/ResourceExplorerSidebar.vue'
75
83
  import ResourceExplorerViewer from './components/ResourceExplorer/ResourceExplorerViewer.vue'
76
- import Swagger from './components/ResourceAccordion/Swagger.client.vue'
84
+ import OpenApiViewer from './components/OpenApiViewer/OpenApiViewer.vue'
77
85
  import ReuseCard from './components/ReuseCard.vue'
78
86
  import ReuseHorizontalCard from './components/ReuseHorizontalCard.vue'
79
87
  import ReuseDetails from './components/ReuseDetails.vue'
80
88
  import SchemaCard from './components/SchemaCard.vue'
81
89
  import SimpleBanner from './components/SimpleBanner.vue'
82
- import SmallChart from './components/SmallChart.vue'
83
90
  import StatBox from './components/StatBox.vue'
84
91
  import Tab from './components/Tabs/Tab.vue'
85
92
  import TabGroup from './components/Tabs/TabGroup.vue'
@@ -94,16 +101,24 @@ import GlobalSearch from './components/Search/GlobalSearch.vue'
94
101
  import SearchInput from './components/Search/SearchInput.vue'
95
102
  import SearchableSelect from './components/Form/SearchableSelect.vue'
96
103
  import SelectGroup from './components/Form/SelectGroup.vue'
104
+ import Listbox from './components/Form/Listbox.vue'
105
+ import InfiniteLoader from './components/InfiniteLoader.vue'
106
+ import TabularExplorer from './components/TabularExplorer/TabularExplorer.vue'
97
107
  import type { UseFetchFunction } from './functions/api.types'
98
108
  import { configKey, useComponentsConfig, type PluginConfig } from './config.js'
109
+ import { ofetch } from 'ofetch'
110
+ import { useTranslation } from './composables/useTranslation'
99
111
 
100
112
  export { Toaster, toast } from 'vue-sonner'
101
113
 
102
114
  export * from './composables/useActiveDescendant'
115
+ export * from './composables/useDebouncedRef'
103
116
  export * from './composables/useMetrics'
104
117
  export * from './composables/useReuseType'
105
118
  export * from './composables/useTranslation'
106
119
  export * from './composables/useHasTabularData'
120
+ export * from './composables/useResourceCapabilities'
121
+ export * from './composables/useTabularProfile'
107
122
 
108
123
  export * from './functions/activities'
109
124
  export * from './functions/datasets'
@@ -116,17 +131,33 @@ export * from './functions/metrics'
116
131
  export * from './functions/never'
117
132
  export * from './functions/organizations'
118
133
  export * from './functions/owned'
119
- export * from './functions/pagination'
120
134
  export * from './functions/resources'
121
135
  export * from './functions/reuses'
122
136
  export * from './functions/schemas'
137
+ export * from './functions/tabular'
123
138
  export * from './functions/users'
139
+ export * from './functions/tabularApi'
140
+ export * from './functions/charts'
124
141
  export * from './types/access_types'
125
142
 
126
143
  export type {
127
144
  GlobalSearchConfig,
128
145
  SearchType,
146
+ SearchTypeConfig,
129
147
  SortOption,
148
+ HiddenFilter,
149
+ BuiltInFilterKey,
150
+ DatasetSearchConfig,
151
+ DatasetSearchFilters,
152
+ DataserviceSearchConfig,
153
+ DataserviceSearchFilters,
154
+ ReuseSearchConfig,
155
+ ReuseSearchFilters,
156
+ OrganizationSearchConfig,
157
+ OrganizationSearchFilters,
158
+ TopicSearchConfig,
159
+ TopicSearchFilters,
160
+ UseSearchFilterOptions,
130
161
  UseFetchFunction,
131
162
  AccessType,
132
163
  AccessAudience,
@@ -168,7 +199,6 @@ export type {
168
199
  Owned,
169
200
  OwnedWithFullObject,
170
201
  OwnedWithId,
171
- Page,
172
202
  PageBloc,
173
203
  ContentBloc,
174
204
  BlocWithTitle,
@@ -218,6 +248,23 @@ export type {
218
248
  ValidataError,
219
249
  Weight,
220
250
  WellType,
251
+ Chart,
252
+ ChartForm,
253
+ ChartForApi,
254
+ FilterCondition,
255
+ Filter,
256
+ AndFilters,
257
+ GenericFilter,
258
+ XAxisType,
259
+ XAxisSortBy,
260
+ SortDirection,
261
+ XAxis,
262
+ XAxisForm,
263
+ UnitPosition,
264
+ YAxis,
265
+ DataSeriesType,
266
+ DataSeries,
267
+ DataSeriesForm,
221
268
  }
222
269
 
223
270
  export {
@@ -225,16 +272,43 @@ export {
225
272
  getDefaultDataserviceConfig,
226
273
  getDefaultReuseConfig,
227
274
  getDefaultOrganizationConfig,
275
+ getDefaultTopicConfig,
228
276
  getDefaultGlobalSearchConfig,
229
277
  defaultDatasetSortOptions,
230
278
  defaultDataserviceSortOptions,
231
279
  defaultReuseSortOptions,
232
280
  defaultOrganizationSortOptions,
281
+ useSearchFilter,
233
282
  }
234
283
 
235
284
  // Vue Plugin
236
285
  const datagouv: Plugin<PluginConfig> = {
237
286
  async install(app: App, options) {
287
+ // Default `$fetch` to an ofetch instance carrying the datagouv API specifics + the consumer's
288
+ // auth hooks, so everything downstream (the default `useFetch`, imperative helpers) can rely on
289
+ // a single configured fetch. A consumer that provides its own `$fetch` keeps full control.
290
+ if (!options.$fetch) {
291
+ options.$fetch = ofetch.create({
292
+ baseURL: options.apiBase,
293
+ onRequest(context) {
294
+ if (options.onRequest) {
295
+ if (Array.isArray(options.onRequest)) options.onRequest.forEach(hook => hook(context))
296
+ else options.onRequest(context)
297
+ }
298
+ context.options.headers.set('Content-Type', 'application/json')
299
+ context.options.headers.set('Accept', 'application/json')
300
+ if (options.devApiKey) context.options.headers.set('X-API-KEY', options.devApiKey)
301
+ const { locale } = useTranslation()
302
+ if (locale) {
303
+ context.options.params ??= {}
304
+ context.options.params['lang'] = locale
305
+ }
306
+ },
307
+ onRequestError: options.onRequestError,
308
+ onResponse: options.onResponse,
309
+ onResponseError: options.onResponseError,
310
+ })
311
+ }
238
312
  app.provide(configKey, options)
239
313
  if (!options.textClamp) {
240
314
  const textClamp = await import('vue3-text-clamp')
@@ -256,6 +330,9 @@ export {
256
330
  CopyButton,
257
331
  DataserviceCard,
258
332
  DatasetCard,
333
+ DataStructure,
334
+ Downloads,
335
+ Metadata,
259
336
  DatasetInformationSection,
260
337
  DatasetTemporalitySection,
261
338
  DatasetSpatialSection,
@@ -295,15 +372,16 @@ export {
295
372
  ResourceExplorer,
296
373
  ResourceExplorerSidebar,
297
374
  ResourceExplorerViewer,
375
+ ObjectCardOwner,
298
376
  ResourceIcon,
377
+ ResourceSelector,
299
378
  ReuseCard,
300
379
  ReuseDetails,
301
380
  ReuseHorizontalCard,
302
381
  SchemaCard,
303
382
  SimpleBanner,
304
- SmallChart,
305
383
  StatBox,
306
- Swagger,
384
+ OpenApiViewer,
307
385
  Tab,
308
386
  TabGroup,
309
387
  TabList,
@@ -318,4 +396,7 @@ export {
318
396
  SearchInput,
319
397
  SearchableSelect,
320
398
  SelectGroup,
399
+ Listbox,
400
+ InfiniteLoader,
401
+ TabularExplorer,
321
402
  }
@@ -24,6 +24,7 @@ export type BaseDataservice = Owned & WithAccessType & {
24
24
  license: string | null
25
25
  private: boolean
26
26
  rate_limiting: string
27
+ rate_limiting_url: string | null
27
28
  title: DataserviceReference['title']
28
29
  contact_points: Array<ContactPoint>
29
30
  }
@@ -65,6 +66,7 @@ export type Dataservice = Owned & WithAccessType & {
65
66
  permissions: { edit: boolean, delete: boolean }
66
67
  private: boolean
67
68
  rate_limiting: string
69
+ rate_limiting_url: string | null
68
70
  self_api_url: DataserviceReference['self_api_url']
69
71
  self_web_url: DataserviceReference['self_web_url']
70
72
  slug: string
@@ -2,11 +2,6 @@ import type { DatasetV2 } from './datasets'
2
2
  import type { Dataservice } from './dataservices'
3
3
  import type { Reuse } from './reuses'
4
4
 
5
- export type Page = {
6
- id: string
7
- blocs: Array<PageBloc>
8
- }
9
-
10
5
  export type BlocWithTitle = {
11
6
  title: string
12
7
  subtitle: string | null
@@ -1,12 +1,12 @@
1
1
  import type { Dataset } from './datasets'
2
- import type { Page } from './pages'
2
+ import type { PageBloc } from './pages'
3
3
  import type { Reuse } from './reuses'
4
4
  import type { User } from './users'
5
5
 
6
6
  export type Post = {
7
7
  body_type: 'markdown' | 'html' | 'blocs'
8
+ blocs: Array<PageBloc>
8
9
  content: string
9
- content_as_page: Page | null
10
10
  created_at: string
11
11
  credit_to: string
12
12
  credit_url: string
@@ -1,7 +1,7 @@
1
1
  import type { User } from './users'
2
2
 
3
3
  export type ReportSubject = {
4
- class: 'Discussion' | 'Dataservice' | 'Dataset' | 'Organization' | 'Reuse'
4
+ class: 'Discussion' | 'Dataservice' | 'Dataset' | 'Organization' | 'Reuse' | 'User'
5
5
  id: string
6
6
  }
7
7
 
@@ -16,11 +16,15 @@ export type Report = {
16
16
  id: string
17
17
  by: User | null
18
18
  subject: ReportSubject | null
19
+ subject_embed_id: string | null
19
20
  reason: ReportReasonValue
20
21
  message: string
21
22
  reported_at: string
22
23
  self_api_url: string
23
24
  subject_deleted_at: string | null
25
+ subject_deleted_by: User | null
26
+ subject_label: string | null
24
27
  dismissed_at: string | null
25
28
  dismissed_by: User | null
29
+ callbacks_count: number
26
30
  }
@@ -1,5 +1,11 @@
1
+ import type { Component } from 'vue'
1
2
  import type { PaginatedArray } from './api'
2
3
  import type { AccessType } from './access_types'
4
+ import type { Dataset } from './datasets'
5
+ import type { Dataservice } from './dataservices'
6
+ import type { Organization } from './organizations'
7
+ import type { Reuse } from './reuses'
8
+ import type { TopicV2 } from './topics'
3
9
  import type {
4
10
  CERTIFIED,
5
11
  PUBLIC_SERVICE,
@@ -291,46 +297,86 @@ export type SortOption<Sort extends string> = {
291
297
 
292
298
  export type DatasetSearchConfig = {
293
299
  class: 'datasets'
300
+ key?: string
294
301
  name?: string
302
+ icon?: Component | string
303
+ placeholder?: string | null
295
304
  hiddenFilters?: HiddenFilter<DatasetSearchFilters>[]
296
305
  basicFilters?: (keyof DatasetSearchFilters)[]
297
306
  advancedFilters?: (keyof DatasetSearchFilters)[]
298
307
  sortOptions?: SortOption<DatasetSearchSort>[]
308
+ defaultSort?: DatasetSearchSort
299
309
  }
300
310
 
301
311
  export type DataserviceSearchConfig = {
302
312
  class: 'dataservices'
313
+ key?: string
303
314
  name?: string
315
+ icon?: Component | string
316
+ placeholder?: string | null
304
317
  hiddenFilters?: HiddenFilter<DataserviceSearchFilters>[]
305
318
  basicFilters?: (keyof DataserviceSearchFilters)[]
306
319
  advancedFilters?: (keyof DataserviceSearchFilters)[]
307
320
  sortOptions?: SortOption<DataserviceSearchSort>[]
321
+ defaultSort?: DataserviceSearchSort
308
322
  }
309
323
 
310
324
  export type ReuseSearchConfig = {
311
325
  class: 'reuses'
326
+ key?: string
312
327
  name?: string
328
+ icon?: Component | string
329
+ placeholder?: string | null
313
330
  hiddenFilters?: HiddenFilter<ReuseSearchFilters>[]
314
331
  basicFilters?: (keyof ReuseSearchFilters)[]
315
332
  advancedFilters?: (keyof ReuseSearchFilters)[]
316
333
  sortOptions?: SortOption<ReuseSearchSort>[]
334
+ defaultSort?: ReuseSearchSort
317
335
  }
318
336
 
319
337
  export type OrganizationSearchConfig = {
320
338
  class: 'organizations'
339
+ key?: string
321
340
  name?: string
341
+ icon?: Component | string
342
+ placeholder?: string | null
322
343
  hiddenFilters?: HiddenFilter<OrganizationSearchFilters>[]
323
344
  basicFilters?: (keyof OrganizationSearchFilters)[]
324
345
  advancedFilters?: (keyof OrganizationSearchFilters)[]
325
346
  sortOptions?: SortOption<OrganizationSearchSort>[]
347
+ defaultSort?: OrganizationSearchSort
326
348
  }
327
349
 
328
- export type SearchTypeConfig = DatasetSearchConfig | DataserviceSearchConfig | ReuseSearchConfig | OrganizationSearchConfig
350
+ export type TopicSearchConfig = {
351
+ class: 'topics'
352
+ key?: string
353
+ name?: string
354
+ icon?: Component | string
355
+ placeholder?: string | null
356
+ hiddenFilters?: HiddenFilter<TopicSearchFilters>[]
357
+ basicFilters?: (keyof TopicSearchFilters)[]
358
+ advancedFilters?: (keyof TopicSearchFilters)[]
359
+ sortOptions?: SortOption<TopicSearchSort>[]
360
+ defaultSort?: TopicSearchSort
361
+ }
362
+
363
+ export type SearchTypeConfig = DatasetSearchConfig | DataserviceSearchConfig | ReuseSearchConfig | OrganizationSearchConfig | TopicSearchConfig
364
+
365
+ export type BuiltInFilterKey = keyof DatasetSearchFilters | keyof DataserviceSearchFilters | keyof ReuseSearchFilters | keyof OrganizationSearchFilters | keyof TopicSearchFilters
329
366
 
330
367
  export type SearchType = SearchTypeConfig['class']
331
368
 
332
369
  export type GlobalSearchConfig = SearchTypeConfig[]
333
370
 
371
+ // Maps each search class to its concrete response shape.
372
+ export type SearchResponseByClass = {
373
+ datasets: DatasetSearchResponse<Dataset>
374
+ dataservices: DataserviceSearchResponse<Dataservice>
375
+ reuses: ReuseSearchResponse<Reuse>
376
+ organizations: OrganizationSearchResponse<Organization>
377
+ topics: TopicSearchResponse<TopicV2>
378
+ }
379
+
334
380
  // Helper functions for default configs
335
381
 
336
382
  export const defaultDatasetSortOptions: SortOption<DatasetSearchSort>[] = [
@@ -397,11 +443,27 @@ export function getDefaultOrganizationConfig(overrides?: Partial<Omit<Organizati
397
443
  }
398
444
  }
399
445
 
446
+ export const defaultTopicSortOptions: SortOption<TopicSearchSort>[] = [
447
+ { value: '-created', label: 'Date de création' },
448
+ { value: '-last_modified', label: 'Dernière mise à jour' },
449
+ ]
450
+
451
+ export function getDefaultTopicConfig(overrides?: Partial<Omit<TopicSearchConfig, 'class'>>): TopicSearchConfig {
452
+ return {
453
+ class: 'topics',
454
+ basicFilters: ['last_update_range', 'producer_type'],
455
+ advancedFilters: ['organization', 'tag'],
456
+ sortOptions: defaultTopicSortOptions,
457
+ ...overrides,
458
+ }
459
+ }
460
+
400
461
  export function getDefaultGlobalSearchConfig(): GlobalSearchConfig {
401
462
  return [
402
463
  getDefaultDatasetConfig(),
403
464
  getDefaultDataserviceConfig(),
404
465
  getDefaultReuseConfig(),
405
466
  getDefaultOrganizationConfig(),
467
+ getDefaultTopicConfig(),
406
468
  ]
407
469
  }
package/src/types/site.ts CHANGED
@@ -1,9 +1,11 @@
1
+ import type { PageBloc } from './pages'
2
+
1
3
  export type Site = {
2
4
  id: string
3
5
  title: string
4
- datasets_page: string | null
5
- reuses_page: string | null
6
- dataservices_page: string | null
6
+ datasets_blocs: Array<PageBloc>
7
+ reuses_blocs: Array<PageBloc>
8
+ dataservices_blocs: Array<PageBloc>
7
9
  version: string
8
10
  metrics: {
9
11
  'dataservices': number
package/src/types/ui.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export type WellType = 'primary' | 'secondary'
2
2
 
3
3
  export type Weight = 'light' | 'regular' | 'semi-bold' | 'bold' | 'heavy'
4
+
5
+ export type TitleTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p'