@datagouv/components-next 1.0.2-dev.50 → 1.0.2-dev.52

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.
@@ -1,20 +1,146 @@
1
1
  import { ofetch } from 'ofetch'
2
2
  import { useComponentsConfig, type PluginConfig } from '../config'
3
+ import type { GenericFilter } from '../types/visualizations'
3
4
 
4
5
  export type SortConfig = {
5
6
  column: string
6
7
  type: string
7
8
  } | null
8
9
 
10
+ export type TabularDataResponse = {
11
+ data: Array<Record<string, unknown>>
12
+ links: {
13
+ profile: string
14
+ swagger: string
15
+ next: string
16
+ }
17
+ meta: { total: number }
18
+ }
19
+
20
+ export type TabularAggregateType = 'avg' | 'sum' | 'count' | 'min' | 'max'
21
+
22
+ export type FetchTabularDataOptions = {
23
+ resourceId: string
24
+ page?: number
25
+ pageSize?: number
26
+ columns?: Array<string> | undefined
27
+ sort?: SortConfig
28
+ groupBy?: string | undefined
29
+ aggregation?: {
30
+ column: string
31
+ type: TabularAggregateType
32
+ } | undefined
33
+ filters?: GenericFilter | undefined
34
+ }
35
+
36
+ export type TabularProfileResponse = {
37
+ profile: {
38
+ header: Array<string>
39
+ columns: Record<string, {
40
+ score: number
41
+ format: string
42
+ python_type: string
43
+ }>
44
+ formats: Record<string, Array<string>>
45
+ profile: Record<string, {
46
+ tops: Array<{ count: number, value: string }>
47
+ nb_distinct: number
48
+ nb_missing_values: number
49
+ min?: number
50
+ max?: number
51
+ std?: number
52
+ mean?: number
53
+ }>
54
+ encoding: string
55
+ separator: string
56
+ categorical: Array<string>
57
+ total_lines: number
58
+ nb_duplicates: number
59
+ columns_fields: Record<string, {
60
+ score: number
61
+ format: string
62
+ python_type: string
63
+ }>
64
+ columns_labels: Record<string, {
65
+ score: number
66
+ format: string
67
+ python_type: string
68
+ }>
69
+ header_row_idx: number
70
+ heading_columns: number
71
+ trailing_columns: number
72
+ }
73
+ deleted_at: string | null
74
+ dataset_id: string
75
+ indexes: null
76
+ }
77
+
9
78
  /**
10
- * Call Tabular-api to get table content
79
+ * Call Tabular-api to get table content with options object
11
80
  */
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}`
81
+ export async function fetchTabularData(config: PluginConfig, options: FetchTabularDataOptions): Promise<TabularDataResponse> {
82
+ const page = options.page ?? 1
83
+ const pageSize = options.pageSize ?? config.tabularApiPageSize ?? 15
84
+ console.log(options)
85
+ let url = `${config.tabularApiUrl}/api/resources/${options.resourceId}/data/?page=${page}&page_size=${pageSize}`
86
+ if (options.columns) {
87
+ url += `&columns=${options.columns.join(',')}`
88
+ }
89
+ if (options.sort) {
90
+ url += `&${options.sort.column}__sort=${options.sort.type}`
16
91
  }
17
- return await ofetch(url)
92
+ if (options.groupBy && options.aggregation?.type) {
93
+ url += `&${options.groupBy}__groupby&${options.aggregation.column}__${options.aggregation.type}`
94
+ }
95
+ if (options.filters) {
96
+ const filterQuery = buildFilterQuery(options.filters)
97
+ if (filterQuery) {
98
+ url += `&${filterQuery}`
99
+ }
100
+ }
101
+ return await ofetch<TabularDataResponse>(url)
102
+ }
103
+
104
+ function buildFilterQuery(filters: GenericFilter): string {
105
+ const params: Array<string> = []
106
+ if ('filters' in filters) {
107
+ for (const filter of filters.filters) {
108
+ if ('filters' in filter) {
109
+ params.push(buildFilterQuery(filter))
110
+ }
111
+ else {
112
+ if (filter.condition === 'is_null') {
113
+ params.push(`${filter.column}__isnull`)
114
+ }
115
+ else if (filter.condition === 'is_not_null') {
116
+ params.push(`${filter.column}__isnotnull`)
117
+ }
118
+ else if (filter.value !== null && filter.value !== undefined && filter.value !== '') {
119
+ params.push(`${filter.column}__${filter.condition}=${encodeURIComponent(filter.value)}`)
120
+ }
121
+ }
122
+ }
123
+ }
124
+ else {
125
+ const filter = filters
126
+ if (filter.condition === 'is_null') {
127
+ params.push(`${filter.column}__isnull`)
128
+ }
129
+ else if (filter.condition === 'is_not_null') {
130
+ params.push(`${filter.column}__isnotnull`)
131
+ }
132
+ else if (filter.value !== null && filter.value !== undefined && filter.value !== '') {
133
+ params.push(`${filter.column}__${filter.condition}=${encodeURIComponent(filter.value)}`)
134
+ }
135
+ }
136
+ return params.join('&')
137
+ }
138
+
139
+ /**
140
+ * Call Tabular-api to get table content
141
+ */
142
+ export function getData(config: PluginConfig, id: string, page: number, sortConfig?: SortConfig) {
143
+ return fetchTabularData(config, { resourceId: id, page, sort: sortConfig })
18
144
  }
19
145
 
20
146
  /**
@@ -22,5 +148,5 @@ export async function getData(config: PluginConfig, id: string, page: number, so
22
148
  */
23
149
  export function useGetProfile() {
24
150
  const config = useComponentsConfig()
25
- return (id: string) => ofetch(`${config.tabularApiUrl}/api/resources/${id}/profile/`)
151
+ return (id: string) => ofetch<TabularProfileResponse>(`${config.tabularApiUrl}/api/resources/${id}/profile/`)
26
152
  }
package/src/main.ts CHANGED
@@ -23,6 +23,7 @@ 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 { Chart, ChartForm, ChartForApi, FilterCondition, Filter, AndFilters, GenericFilter, XAxisType, XAxisSortBy, SortDirection, XAxis, XAxisForm, UnitPosition, YAxis, DataSeriesType, DataSeries, DataSeriesForm } from './types/visualizations'
26
27
  import type { GlobalSearchConfig, SearchType, SortOption } from './types/search'
27
28
  import { getDefaultDatasetConfig, getDefaultDataserviceConfig, getDefaultReuseConfig, getDefaultOrganizationConfig, getDefaultTopicConfig, getDefaultGlobalSearchConfig, defaultDatasetSortOptions, defaultDataserviceSortOptions, defaultReuseSortOptions, defaultOrganizationSortOptions } from './types/search'
28
29
 
@@ -80,6 +81,8 @@ import ReuseDetails from './components/ReuseDetails.vue'
80
81
  import SchemaCard from './components/SchemaCard.vue'
81
82
  import SimpleBanner from './components/SimpleBanner.vue'
82
83
  import SmallChart from './components/SmallChart.vue'
84
+ import ChartViewer from './components/Chart/ChartViewer.vue'
85
+ import ChartViewerWrapper from './components/Chart/ChartViewerWrapper.vue'
83
86
  import StatBox from './components/StatBox.vue'
84
87
  import Tab from './components/Tabs/Tab.vue'
85
88
  import TabGroup from './components/Tabs/TabGroup.vue'
@@ -94,12 +97,14 @@ import GlobalSearch from './components/Search/GlobalSearch.vue'
94
97
  import SearchInput from './components/Search/SearchInput.vue'
95
98
  import SearchableSelect from './components/Form/SearchableSelect.vue'
96
99
  import SelectGroup from './components/Form/SelectGroup.vue'
100
+ import Listbox from './components/Form/Listbox.vue'
97
101
  import type { UseFetchFunction } from './functions/api.types'
98
102
  import { configKey, useComponentsConfig, type PluginConfig } from './config.js'
99
103
 
100
104
  export { Toaster, toast } from 'vue-sonner'
101
105
 
102
106
  export * from './composables/useActiveDescendant'
107
+ export * from './composables/useDebouncedRef'
103
108
  export * from './composables/useMetrics'
104
109
  export * from './composables/useReuseType'
105
110
  export * from './composables/useTranslation'
@@ -120,6 +125,8 @@ export * from './functions/resources'
120
125
  export * from './functions/reuses'
121
126
  export * from './functions/schemas'
122
127
  export * from './functions/users'
128
+ export * from './functions/tabularApi'
129
+ export * from './functions/charts'
123
130
  export * from './types/access_types'
124
131
 
125
132
  export type {
@@ -216,6 +223,23 @@ export type {
216
223
  ValidataError,
217
224
  Weight,
218
225
  WellType,
226
+ Chart,
227
+ ChartForm,
228
+ ChartForApi,
229
+ FilterCondition,
230
+ Filter,
231
+ AndFilters,
232
+ GenericFilter,
233
+ XAxisType,
234
+ XAxisSortBy,
235
+ SortDirection,
236
+ XAxis,
237
+ XAxisForm,
238
+ UnitPosition,
239
+ YAxis,
240
+ DataSeriesType,
241
+ DataSeries,
242
+ DataSeriesForm,
219
243
  }
220
244
 
221
245
  export {
@@ -301,6 +325,8 @@ export {
301
325
  SchemaCard,
302
326
  SimpleBanner,
303
327
  SmallChart,
328
+ ChartViewer,
329
+ ChartViewerWrapper,
304
330
  StatBox,
305
331
  OpenApiViewer,
306
332
  Tab,
@@ -317,4 +343,5 @@ export {
317
343
  SearchInput,
318
344
  SearchableSelect,
319
345
  SelectGroup,
346
+ Listbox,
320
347
  }
@@ -0,0 +1,89 @@
1
+ import type { TabularAggregateType } from '../functions/tabularApi'
2
+ import type { Owned, OwnedWithId } from './owned'
3
+
4
+ export type FilterCondition = 'exact' | 'differs' | 'is_null' | 'is_not_null' | 'greater' | 'less' | 'strictly_greater' | 'strictly_less'
5
+ export type Filter = {
6
+ _cls: 'Filter'
7
+ column: string
8
+ condition: FilterCondition
9
+ value: string | null
10
+ }
11
+
12
+ export type AndFilters = {
13
+ _cls: 'AndFilters'
14
+ filters: Array<Filter | AndFilters>
15
+ }
16
+
17
+ export type GenericFilter = Filter | AndFilters
18
+
19
+ export type XAxisType = 'discrete' | 'continuous'
20
+
21
+ export type XAxisSortBy = 'axis_x' | 'axis_y'
22
+
23
+ export type SortDirection = 'asc' | 'desc'
24
+
25
+ export type XAxis = {
26
+ column_x: string
27
+ sort_x_by: XAxisSortBy | null
28
+ sort_x_direction: SortDirection | null
29
+ type: XAxisType
30
+ }
31
+
32
+ export type CombinedSort = '' | 'axis_x-asc' | 'axis_x-desc' | 'axis_y-asc' | 'axis_y-desc'
33
+
34
+ export type XAxisForm = Omit<XAxis, 'sort_x_by' | 'sort_x_direction'> & { sort_combined: CombinedSort }
35
+
36
+ export type UnitPosition = 'prefix' | 'suffix'
37
+
38
+ export type YAxis = {
39
+ min: number | null
40
+ max: number | null
41
+ label: string | null
42
+ unit: string | null
43
+ unit_position: UnitPosition | null
44
+ }
45
+
46
+ export type DataSeriesType = 'line' | 'histogram'
47
+
48
+ export type DataSeries = {
49
+ type: DataSeriesType
50
+ column_y: string
51
+ aggregate_y: TabularAggregateType | null
52
+ resource_id: string
53
+ column_x_name_override: string | null
54
+ filters: GenericFilter | null
55
+ }
56
+
57
+ // Type for form where aggregate_y can be empty string for select binding
58
+ export type DataSeriesForm = Omit<DataSeries, 'aggregate_y'> & { aggregate_y: TabularAggregateType | '' | null }
59
+
60
+ export type Chart = Owned & {
61
+ id: string
62
+ title: string
63
+ slug: string
64
+ description: string
65
+ private: boolean
66
+ created_at: string
67
+ last_modified: string
68
+ deleted_at: string | null
69
+ uri: string
70
+ page: string
71
+ x_axis: XAxis
72
+ y_axis: YAxis
73
+ series: Array<DataSeries>
74
+ extras: Record<string, unknown>
75
+ permissions: { delete: boolean, edit: boolean, read: boolean }
76
+ metrics: {
77
+ views: number
78
+ }
79
+ }
80
+
81
+ export type ChartForApi = OwnedWithId & Pick<Chart, 'title' | 'description' | 'private' | 'x_axis' | 'y_axis' | 'series' | 'extras'>
82
+
83
+ export type ChartForm = Omit<ChartForApi, 'x_axis' | 'series' | 'owner' | 'organization'> & {
84
+ owned: OwnedWithId
85
+ x_axis: XAxisForm
86
+ series: Array<DataSeriesForm>
87
+ chart_type?: DataSeriesType | null
88
+ filter: GenericFilter | null
89
+ }