@datagouv/components-next 1.0.2-dev.2 → 1.0.2-dev.4

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,4 +1,4 @@
1
- import { b as Ke } from "./main-B2kXxWRG.js";
1
+ import { b as Ke } from "./main-ByqZlhiZ.js";
2
2
  import We from "vue";
3
3
  function Fe(I, K) {
4
4
  for (var V = 0; V < K.length; V++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datagouv/components-next",
3
- "version": "1.0.2-dev.2",
3
+ "version": "1.0.2-dev.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -17,14 +17,15 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "@floating-ui/vue": "^1.1.8",
20
- "@types/leaflet": "^1.9.17",
21
20
  "@headlessui/vue": "^1.7.23",
22
21
  "@remixicon/vue": "^4.5.0",
23
22
  "@types/hast": "^3.0.4",
23
+ "@types/leaflet": "^1.9.17",
24
24
  "@vueuse/core": "^13.1.0",
25
25
  "@vueuse/router": "^13.1.0",
26
26
  "chart.js": "^4.4.8",
27
27
  "dompurify": "^3.2.5",
28
+ "echarts": "^6.0.0",
28
29
  "geopf-extensions-openlayers": "^1.0.0-beta.5",
29
30
  "leaflet": "^1.9.4",
30
31
  "maplibre-gl": "^5.6.2",
@@ -50,6 +51,7 @@
50
51
  "unist-util-visit": "^5.0.0",
51
52
  "vue": "^3.5.13",
52
53
  "vue-content-loader": "^2.0.1",
54
+ "vue-echarts": "^8.0.1",
53
55
  "vue-router": "^4.5.0",
54
56
  "vue-sonner": "^2.0.9",
55
57
  "vue3-json-viewer": "^2.4.1",
@@ -0,0 +1,152 @@
1
+ <template>
2
+ <VChart
3
+ class="w-full min-h-96"
4
+ :option="echartsOption"
5
+ autoresize
6
+ />
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import { format, use, type ComposeOption } from 'echarts/core'
11
+ import { CanvasRenderer } from 'echarts/renderers'
12
+ import { LineChart, BarChart, type BarSeriesOption, type LineSeriesOption } from 'echarts/charts'
13
+ import { TitleComponent, TooltipComponent, LegendComponent, GridComponent, DatasetComponent } from 'echarts/components'
14
+ import VChart from 'vue-echarts'
15
+ import { computed } from 'vue'
16
+ import { summarize } from '../../functions/helpers'
17
+ import type { Chart, DataSeries, XAxis, YAxis, ChartForm, XAxisForm } from '../../types/visualizations'
18
+
19
+ use([CanvasRenderer, LineChart, BarChart, TitleComponent, TooltipComponent, LegendComponent, GridComponent, DatasetComponent])
20
+
21
+ const props = defineProps<{
22
+ chart: Chart | ChartForm
23
+ series: {
24
+ data: Record<string, Array<Record<string, unknown>>>
25
+ columns: Record<string, Array<string>>
26
+ }
27
+ }>()
28
+
29
+ function mapSeriesType(type: DataSeries['type']): 'line' | 'bar' {
30
+ return (type ?? 'line') === 'histogram' ? 'bar' : 'line'
31
+ }
32
+
33
+ function mapXAxisType(xAxis: XAxis | XAxisForm): 'category' | 'value' {
34
+ if (!xAxis) return 'category'
35
+ return (xAxis.type ?? 'discrete') === 'continuous' ? 'value' : 'category'
36
+ }
37
+
38
+ function buildYAxisFormatter(yAxis: YAxis): ((value: number) => string) | undefined {
39
+ return (value: number) => {
40
+ const v = summarize(value)
41
+ if (!yAxis.unit) return v
42
+ if (yAxis.unit_position === 'prefix') return `${yAxis.unit} ${v}`
43
+ return `${v} ${yAxis.unit}`
44
+ }
45
+ }
46
+
47
+ const echartsOption = computed(() => {
48
+ const seriesCount = props.chart.series.length
49
+ if (!props.chart.series || seriesCount === 0) return
50
+
51
+ // Create series configuration with data mapping
52
+ const seriesData = props.chart.series.map((s) => {
53
+ const xColumn = s.column_x_name_override ?? props.chart.x_axis.column_x
54
+ const yColumn = s.aggregate_y ? `${s.column_y}__${s.aggregate_y}` : s.column_y
55
+ const resourceId = s.resource_id
56
+ const seriesType = s.type
57
+
58
+ if (!xColumn || !yColumn || !resourceId || !seriesType || !props.series.data[resourceId] || !props.series.columns[resourceId]) {
59
+ return null
60
+ }
61
+
62
+ // Sort data before passing to ECharts to avoid transform issues
63
+ const sortedData = [...props.series.data[resourceId]]
64
+ const sortBy = props.chart.x_axis.sort_x_by
65
+ const sortDirection = props.chart.x_axis.sort_x_direction ?? 'asc'
66
+
67
+ if (sortBy && sortDirection && props.chart.x_axis.column_x) {
68
+ const sortKey = sortBy === 'axis_x' ? xColumn : yColumn
69
+ sortedData.sort((a, b) => {
70
+ const valA = a[sortKey] as number
71
+ const valB = b[sortKey] as number
72
+ if (valA < valB) return sortDirection === 'asc' ? -1 : 1
73
+ if (valA > valB) return sortDirection === 'asc' ? 1 : -1
74
+ return 0
75
+ })
76
+ }
77
+
78
+ return {
79
+ series: {
80
+ type: mapSeriesType(seriesType),
81
+ dimensions: s.aggregate_y ? [xColumn, yColumn] : props.series.columns[resourceId],
82
+ name: s.column_y,
83
+ encode: {
84
+ x: xColumn,
85
+ y: yColumn,
86
+ },
87
+ } as LineSeriesOption | BarSeriesOption,
88
+ data: {
89
+ source: sortedData,
90
+ dimensions: s.aggregate_y ? [xColumn, yColumn] : props.series.columns[resourceId],
91
+ },
92
+ }
93
+ }).filter(Boolean).reduce((acc: { series: Array<LineSeriesOption | BarSeriesOption>, data: Array<Record<string, unknown>> }, curr) => {
94
+ if (curr) {
95
+ acc.series.push(curr.series)
96
+ acc.data.push(curr.data)
97
+ }
98
+ return acc
99
+ }, {
100
+ series: [],
101
+ data: [],
102
+ })
103
+
104
+ return {
105
+ dataset: [...seriesData.data],
106
+ title: {
107
+ text: props.chart.title,
108
+ left: 'center',
109
+ },
110
+ tooltip: {
111
+ trigger: 'axis' as const,
112
+ formatter: (params: Array<{ value: Record<string, unknown>, axisValueLabel: string, seriesName: string }>) => {
113
+ let tooltip = ''
114
+ for (const param of params) {
115
+ const keys = Object.keys(param.value)
116
+ const col = keys.find(key => key.startsWith(param.seriesName))!
117
+ const formatter = new Intl.NumberFormat('fr-FR')
118
+ tooltip += `${format.encodeHTML(param.axisValueLabel)}: <strong>${formatter.format(Number(param.value[col]))}</strong><br>`
119
+ }
120
+ return tooltip
121
+ },
122
+ },
123
+ legend: {
124
+ bottom: 0,
125
+ },
126
+ grid: {
127
+ top: 60,
128
+ bottom: 40,
129
+ left: 20,
130
+ right: 20,
131
+ containLabel: true,
132
+ },
133
+ xAxis: {
134
+ type: mapXAxisType(props.chart.x_axis),
135
+ name: (props.chart.x_axis as XAxis).column_x,
136
+ },
137
+ yAxis: {
138
+ type: 'value' as const,
139
+ name: props.chart.y_axis.label ?? undefined,
140
+ min: props.chart.y_axis.min ?? undefined,
141
+ max: props.chart.y_axis.max ?? undefined,
142
+ axisLabel: {
143
+ formatter: buildYAxisFormatter(props.chart.y_axis),
144
+ },
145
+ },
146
+ series: seriesData.series,
147
+ } satisfies ComposeOption<
148
+ | BarSeriesOption
149
+ | LineSeriesOption
150
+ >
151
+ })
152
+ </script>
@@ -0,0 +1,194 @@
1
+ <template>
2
+ <LoadingBlock
3
+ :status="status"
4
+ :data="series"
5
+ >
6
+ <template #default="{ data }">
7
+ <ChartViewer
8
+ :chart="chart"
9
+ :series="data"
10
+ />
11
+ </template>
12
+ <template #error>
13
+ <div class="text-center py-8 text-gray-500">
14
+ Une erreur est survenue lors du chargement des données du graphique.
15
+ </div>
16
+ </template>
17
+ </LoadingBlock>
18
+ </template>
19
+
20
+ <script setup lang="ts">
21
+ import { reactive, ref, watch } from 'vue'
22
+ import ChartViewer from './ChartViewer.vue'
23
+ import LoadingBlock from '../../components/LoadingBlock.vue'
24
+ import { useComponentsConfig } from '../../config'
25
+ import { fetchTabularData, useGetProfile, type TabularDataResponse, type TabularProfileResponse } from '../../functions/tabularApi'
26
+ import type { Chart, ChartForm } from '../../types/visualizations'
27
+
28
+ const chart = defineModel<Chart | ChartForm>({ required: true })
29
+
30
+ const emit = defineEmits<{
31
+ columns: [columns: Record<string, Array<string>>]
32
+ }>()
33
+
34
+ const config = useComponentsConfig()
35
+ const getProfile = useGetProfile()
36
+
37
+ // Loading and error states
38
+ const status = ref<'idle' | 'pending' | 'success' | 'error'>('idle')
39
+ const error = ref<Error | null>(null)
40
+
41
+ // Dataset source for the chart
42
+ const series = reactive<{
43
+ data: Record<string, Array<Record<string, unknown>>>
44
+ columns: Record<string, Array<string>>
45
+ }>({
46
+ data: {},
47
+ columns: {},
48
+ })
49
+
50
+ async function fetchSeriesProfile() {
51
+ status.value = 'pending'
52
+ error.value = null
53
+
54
+ try {
55
+ if (chart.value.series.length === 0) {
56
+ status.value = 'success'
57
+ series.data = {}
58
+ series.columns = {}
59
+ return
60
+ }
61
+
62
+ // Fetch data for all series in parallel
63
+ const fetchPromises = chart.value.series.map(async (serie) => {
64
+ if (!serie.resource_id) return
65
+ return {
66
+ id: serie.resource_id,
67
+ profile: await getProfile(serie.resource_id),
68
+ }
69
+ }).filter(Boolean) as Array<Promise<{
70
+ id: string
71
+ profile: TabularProfileResponse
72
+ }>>
73
+
74
+ const results = (await Promise.allSettled(fetchPromises))
75
+ .filter(r => r.status === 'fulfilled')
76
+ .map(r => r.value)
77
+ series.columns = Object.fromEntries(results.map(result => [
78
+ result.id,
79
+ result.profile.profile.header,
80
+ ]))
81
+ if (results.length > 0) {
82
+ const columns = results[0]?.profile.profile.header ?? []
83
+ const firstColumn = columns.filter(c => c !== '__id')[0]
84
+ if (!chart.value.x_axis.column_x && firstColumn) {
85
+ chart.value.x_axis.column_x = firstColumn
86
+ }
87
+ }
88
+ status.value = 'success'
89
+ }
90
+ catch (err) {
91
+ error.value = err instanceof Error ? err : new Error('Failed to fetch series profile')
92
+ status.value = 'error'
93
+ console.log(err)
94
+ series.columns = {}
95
+ }
96
+ }
97
+
98
+ async function fetchSeriesData() {
99
+ status.value = 'pending'
100
+ error.value = null
101
+
102
+ try {
103
+ if (chart.value.series.length === 0 || !chart.value.x_axis.column_x) {
104
+ status.value = 'success'
105
+ series.data = {}
106
+ return
107
+ }
108
+
109
+ // Fetch data for all series in parallel
110
+ const fetchPromises = chart.value.series.map(async (serie) => {
111
+ const xColumn = serie.column_x_name_override ?? chart.value.x_axis.column_x
112
+ if (!xColumn || !serie.resource_id || !serie.column_y) return
113
+ return {
114
+ id: serie.resource_id,
115
+ profile: await getProfile(serie.resource_id),
116
+ data: await fetchTabularData(config, {
117
+ columns: serie.aggregate_y ? undefined : [xColumn, serie.column_y],
118
+ resourceId: serie.resource_id,
119
+ page: 1,
120
+ pageSize: 100,
121
+ groupBy: xColumn,
122
+ aggregation: serie.column_y && serie.aggregate_y
123
+ ? {
124
+ column: serie.column_y,
125
+ type: serie.aggregate_y,
126
+ }
127
+ : undefined,
128
+ }),
129
+ }
130
+ }).filter(Boolean) as Array<Promise<{
131
+ id: string
132
+ data: TabularDataResponse
133
+ }>>
134
+
135
+ const results = (await Promise.allSettled(fetchPromises))
136
+ .filter(r => r.status === 'fulfilled')
137
+ .map(r => r.value)
138
+ // Transform data into echarts format
139
+ series.data = Object.fromEntries(results.map(result => [
140
+ result.id,
141
+ result.data.data,
142
+ ]))
143
+ status.value = 'success'
144
+ }
145
+ catch (err) {
146
+ error.value = err instanceof Error ? err : new Error('Failed to fetch series data')
147
+ status.value = 'error'
148
+ series.data = {}
149
+ }
150
+ }
151
+
152
+ // Watch for changes in the chart or its series
153
+ watch(() => chart.value.series, async () => {
154
+ await fetchSeriesProfile()
155
+ }, { immediate: true, deep: true })
156
+
157
+ // Watch for changes in the chart or its series
158
+ watch([() => chart.value.series, () => chart.value.x_axis.column_x], async () => {
159
+ await fetchSeriesData()
160
+ }, { immediate: true, deep: true })
161
+
162
+ watch(() => series.columns, () => {
163
+ emit('columns', series.columns)
164
+ })
165
+
166
+ /**
167
+ * Combines data from multiple series into a single echarts dataset source
168
+ */
169
+ // function combineSeriesData(seriesData: Array<TabularDataResponse | null>): Array<Array<unknown>> {
170
+ // if (!seriesData.length || seriesData.every(data => !data || !data.data || data.data.length === 0)) {
171
+ // return []
172
+ // }
173
+
174
+ // // Get all unique columns from all series
175
+ // const allColumns = Array.from(new Set(seriesData.flatMap(data =>
176
+ // data?.data?.flatMap(Object.keys) || []
177
+ // )))
178
+
179
+ // // Build the source array with headers
180
+ // const source: Array<Array<unknown>> = [allColumns]
181
+
182
+ // // Add data rows - for simplicity, we'll use data from the first series
183
+ // // In a real implementation, you'd want to merge/combine the data appropriately
184
+ // const firstData = seriesData.find(data => data?.data?.length)?.data
185
+ // if (firstData) {
186
+ // firstData.forEach(row => {
187
+ // const rowValues = allColumns.map(column => row[column])
188
+ // source.push(rowValues)
189
+ // })
190
+ // }
191
+
192
+ // return source
193
+ // }
194
+ </script>
@@ -147,7 +147,7 @@ async function getTableInfos(page: number, sortConfig?: SortConfig) {
147
147
  try {
148
148
  // Check that this function return wanted data
149
149
  const response = await getData(config, props.resource.id, page, sortConfig)
150
- if ('data' in response && response.data && response.data.length > 0) {
150
+ if ('data' in response && response.data && 0 in response.data) {
151
151
  // Update existing rows
152
152
  rows.value = response.data
153
153
  columns.value = Object.keys(response.data[0]).filter(item => item !== '__id')
@@ -6,15 +6,92 @@ export type SortConfig = {
6
6
  type: string
7
7
  } | null
8
8
 
9
+ export type TabularDataResponse = {
10
+ data: Array<Record<string, unknown>>
11
+ meta: { total: number }
12
+ }
13
+
14
+ export type TabularAggregateType = 'avg' | 'sum' | 'count' | 'min' | 'max'
15
+
16
+ export type FetchTabularDataOptions = {
17
+ resourceId: string
18
+ page?: number
19
+ pageSize?: number
20
+ columns?: Array<string> | undefined
21
+ sort?: SortConfig
22
+ groupBy?: string | undefined
23
+ aggregation?: {
24
+ column: string
25
+ type: TabularAggregateType
26
+ } | undefined
27
+ }
28
+
29
+ export type TabularProfileResponse = {
30
+ profile: {
31
+ header: Array<string>
32
+ columns: Record<string, {
33
+ score: number
34
+ format: string
35
+ python_type: string
36
+ }>
37
+ formats: Record<string, Array<string>>
38
+ profile: Record<string, {
39
+ tops: Array<{ count: number, value: string }>
40
+ nb_distinct: number
41
+ nb_missing_values: number
42
+ min?: number
43
+ max?: number
44
+ std?: number
45
+ mean?: number
46
+ }>
47
+ encoding: string
48
+ separator: string
49
+ categorical: Array<string>
50
+ total_lines: number
51
+ nb_duplicates: number
52
+ columns_fields: Record<string, {
53
+ score: number
54
+ format: string
55
+ python_type: string
56
+ }>
57
+ columns_labels: Record<string, {
58
+ score: number
59
+ format: string
60
+ python_type: string
61
+ }>
62
+ header_row_idx: number
63
+ heading_columns: number
64
+ trailing_columns: number
65
+ }
66
+ deleted_at: string | null
67
+ dataset_id: string
68
+ indexes: null
69
+ }
70
+
9
71
  /**
10
- * Call Tabular-api to get table content
72
+ * Call Tabular-api to get table content with options object
11
73
  */
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}`
74
+ export async function fetchTabularData(config: PluginConfig, options: FetchTabularDataOptions): Promise<TabularDataResponse> {
75
+ const page = options.page ?? 1
76
+ const pageSize = options.pageSize ?? config.tabularApiPageSize ?? 15
77
+ let url = `${config.tabularApiUrl}/api/resources/${options.resourceId}/data/?page=${page}&page_size=${pageSize}`
78
+ if (options.columns) {
79
+ url += `&columns=${options.columns.join(',')}`
80
+ }
81
+ if (options.sort) {
82
+ url += `&${options.sort.column}__sort=${options.sort.type}`
16
83
  }
17
- return await ofetch(url)
84
+ if (options.groupBy && options.aggregation?.type) {
85
+ url += `&${options.groupBy}__groupby&${options.aggregation.column}__${options.aggregation.type}`
86
+ }
87
+ return await ofetch<TabularDataResponse>(url)
88
+ }
89
+
90
+ /**
91
+ * Call Tabular-api to get table content
92
+ */
93
+ export function getData(config: PluginConfig, id: string, page: number, sortConfig?: SortConfig) {
94
+ return fetchTabularData(config, { resourceId: id, page, sort: sortConfig })
18
95
  }
19
96
 
20
97
  /**
@@ -22,5 +99,5 @@ export async function getData(config: PluginConfig, id: string, page: number, so
22
99
  */
23
100
  export function useGetProfile() {
24
101
  const config = useComponentsConfig()
25
- return (id: string) => ofetch(`${config.tabularApiUrl}/api/resources/${id}/profile/`)
102
+ return (id: string) => ofetch<TabularProfileResponse>(`${config.tabularApiUrl}/api/resources/${id}/profile/`)
26
103
  }
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, getDefaultGlobalSearchConfig, defaultDatasetSortOptions, defaultDataserviceSortOptions, defaultReuseSortOptions, defaultOrganizationSortOptions } from './types/search'
28
29
 
@@ -100,6 +101,7 @@ import { configKey, useComponentsConfig, type PluginConfig } from './config.js'
100
101
  export { Toaster, toast } from 'vue-sonner'
101
102
 
102
103
  export * from './composables/useActiveDescendant'
104
+ export * from './composables/useDebouncedRef'
103
105
  export * from './composables/useMetrics'
104
106
  export * from './composables/useReuseType'
105
107
  export * from './composables/useTranslation'
@@ -121,6 +123,7 @@ export * from './functions/resources'
121
123
  export * from './functions/reuses'
122
124
  export * from './functions/schemas'
123
125
  export * from './functions/users'
126
+ export * from './functions/tabularApi'
124
127
  export * from './types/access_types'
125
128
 
126
129
  export type {
@@ -218,6 +221,23 @@ export type {
218
221
  ValidataError,
219
222
  Weight,
220
223
  WellType,
224
+ Chart,
225
+ ChartForm,
226
+ ChartForApi,
227
+ FilterCondition,
228
+ Filter,
229
+ AndFilters,
230
+ GenericFilter,
231
+ XAxisType,
232
+ XAxisSortBy,
233
+ SortDirection,
234
+ XAxis,
235
+ XAxisForm,
236
+ UnitPosition,
237
+ YAxis,
238
+ DataSeriesType,
239
+ DataSeries,
240
+ DataSeriesForm,
221
241
  }
222
242
 
223
243
  export {
@@ -0,0 +1,84 @@
1
+ import type { TabularAggregateType } from '../functions/tabularApi'
2
+ import type { Owned, OwnedWithId } from './owned'
3
+
4
+ export type FilterCondition = 'equal' | 'greater'
5
+
6
+ export type Filter = {
7
+ column: string
8
+ condition: FilterCondition
9
+ value?: string
10
+ }
11
+
12
+ export type AndFilters = {
13
+ filters: Array<Filter | AndFilters>
14
+ }
15
+
16
+ export type GenericFilter = Filter | AndFilters
17
+
18
+ export type XAxisType = 'discrete' | 'continuous'
19
+
20
+ export type XAxisSortBy = 'axis_x' | 'axis_y'
21
+
22
+ export type SortDirection = 'asc' | 'desc'
23
+
24
+ export type XAxis = {
25
+ column_x: string
26
+ sort_x_by: XAxisSortBy | null
27
+ sort_x_direction: SortDirection | null
28
+ type: XAxisType
29
+ }
30
+
31
+ export type XAxisForm = Omit<XAxis, 'sort_x_by'> & { sort_x_by: XAxisSortBy | '' | null }
32
+
33
+ export type UnitPosition = 'prefix' | 'suffix'
34
+
35
+ export type YAxis = {
36
+ min: number | null
37
+ max: number | null
38
+ label: string | null
39
+ unit: string | null
40
+ unit_position: UnitPosition | null
41
+ }
42
+
43
+ export type DataSeriesType = 'line' | 'histogram'
44
+
45
+ export type DataSeries = {
46
+ type: DataSeriesType
47
+ column_y: string
48
+ aggregate_y: TabularAggregateType | null
49
+ resource_id: string
50
+ column_x_name_override: string | null
51
+ filters: GenericFilter | null
52
+ }
53
+
54
+ // Type for form where aggregate_y can be empty string for select binding
55
+ export type DataSeriesForm = Omit<DataSeries, 'aggregate_y'> & { aggregate_y: TabularAggregateType | '' | null }
56
+
57
+ export type Chart = Owned & {
58
+ id: string
59
+ title: string
60
+ slug: string
61
+ description: string
62
+ private: boolean
63
+ created_at: string
64
+ last_modified: string
65
+ deleted_at: string | null
66
+ uri: string
67
+ page: string
68
+ x_axis: XAxis
69
+ y_axis: YAxis
70
+ series: Array<DataSeries>
71
+ extras: Record<string, unknown>
72
+ permissions: { delete: boolean, edit: boolean, read: boolean }
73
+ metrics: {
74
+ views: number
75
+ }
76
+ }
77
+
78
+ export type ChartForApi = OwnedWithId & Pick<Chart, 'title' | 'description' | 'private' | 'x_axis' | 'y_axis' | 'series' | 'extras'>
79
+
80
+ export type ChartForm = Omit<ChartForApi, 'x_axis' | 'series' | 'owner' | 'organization'> & {
81
+ owned: OwnedWithId
82
+ x_axis: XAxisForm
83
+ series: Array<DataSeriesForm>
84
+ }
@@ -1,4 +0,0 @@
1
- import { i as f } from "./main-B2kXxWRG.js";
2
- export {
3
- f as default
4
- };