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

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 (52) hide show
  1. package/dist/Datafair.client-CYO9vwx6.js +30 -0
  2. package/dist/JsonPreview.client-B6aU3vl4.js +78 -0
  3. package/dist/{MapContainer.client-DjjvdKBp.js → MapContainer.client-BZsKgRUh.js} +35 -38
  4. package/dist/{PdfPreview.client-CsvKU0Aq.js → PdfPreview.client-ClkseuKU.js} +694 -700
  5. package/dist/{Pmtiles.client-uqg1fwOl.js → Pmtiles.client-CUaeaV-O.js} +574 -579
  6. package/dist/Swagger.client-FpYXdDuX.js +4 -0
  7. package/dist/XmlPreview.client-BNGHvVnU.js +70 -0
  8. package/dist/components-next.css +3 -3
  9. package/dist/components-next.js +83 -86
  10. package/dist/components.css +1 -1
  11. package/dist/{index-PMeuFwWj.js → index-B0fPq7-b.js} +1 -1
  12. package/dist/{main-ByqZlhiZ.js → main-ifX24DGW.js} +31224 -30474
  13. package/dist/{vue3-xml-viewer.common-DFrGHXJC.js → vue3-xml-viewer.common-Bkgr-tAS.js} +1 -1
  14. package/package.json +10 -8
  15. package/src/components/ActivityList/ActivityList.vue +0 -2
  16. package/src/components/Form/SearchableSelect.vue +2 -1
  17. package/src/components/Pagination.vue +8 -5
  18. package/src/components/ReadMore.vue +1 -1
  19. package/src/components/ResourceAccordion/Datafair.client.vue +4 -10
  20. package/src/components/ResourceAccordion/JsonPreview.client.vue +34 -47
  21. package/src/components/ResourceAccordion/MapContainer.client.vue +7 -11
  22. package/src/components/ResourceAccordion/Metadata.vue +1 -2
  23. package/src/components/ResourceAccordion/PdfPreview.client.vue +28 -32
  24. package/src/components/ResourceAccordion/Pmtiles.client.vue +5 -10
  25. package/src/components/ResourceAccordion/Preview.vue +6 -11
  26. package/src/components/ResourceAccordion/PreviewLoader.vue +1 -2
  27. package/src/components/ResourceAccordion/PreviewUnavailable.vue +22 -0
  28. package/src/components/ResourceAccordion/ResourceAccordion.vue +1 -2
  29. package/src/components/ResourceAccordion/XmlPreview.client.vue +34 -47
  30. package/src/components/ResourceExplorer/ResourceExplorer.vue +21 -10
  31. package/src/components/ResourceExplorer/ResourceExplorerViewer.vue +24 -3
  32. package/src/components/Search/GlobalSearch.vue +29 -4
  33. package/src/composables/useResourceCapabilities.ts +1 -1
  34. package/src/config.ts +2 -0
  35. package/src/functions/datasets.ts +0 -17
  36. package/src/functions/resources.ts +56 -1
  37. package/src/functions/tabularApi.ts +7 -84
  38. package/src/main.ts +2 -22
  39. package/src/types/dataservices.ts +2 -0
  40. package/src/types/organizations.ts +1 -1
  41. package/src/types/reports.ts +3 -0
  42. package/src/types/search.ts +26 -1
  43. package/src/types/users.ts +0 -1
  44. package/dist/Datafair.client-c1cUKkQR.js +0 -35
  45. package/dist/JsonPreview.client-CAs9XTCX.js +0 -87
  46. package/dist/Swagger.client-BGrkka3l.js +0 -4
  47. package/dist/XmlPreview.client-BWbKzLte.js +0 -79
  48. package/src/components/Chart/ChartViewer.vue +0 -152
  49. package/src/components/Chart/ChartViewerWrapper.vue +0 -194
  50. package/src/functions/pagination.ts +0 -9
  51. package/src/types/visualizations.ts +0 -84
  52. /package/assets/illustrations/{_microscope.svg → microscope.svg} +0 -0
@@ -325,7 +325,16 @@ export type OrganizationSearchConfig = {
325
325
  sortOptions?: SortOption<OrganizationSearchSort>[]
326
326
  }
327
327
 
328
- export type SearchTypeConfig = DatasetSearchConfig | DataserviceSearchConfig | ReuseSearchConfig | OrganizationSearchConfig
328
+ export type TopicSearchConfig = {
329
+ class: 'topics'
330
+ name?: string
331
+ hiddenFilters?: HiddenFilter<TopicSearchFilters>[]
332
+ basicFilters?: (keyof TopicSearchFilters)[]
333
+ advancedFilters?: (keyof TopicSearchFilters)[]
334
+ sortOptions?: SortOption<TopicSearchSort>[]
335
+ }
336
+
337
+ export type SearchTypeConfig = DatasetSearchConfig | DataserviceSearchConfig | ReuseSearchConfig | OrganizationSearchConfig | TopicSearchConfig
329
338
 
330
339
  export type SearchType = SearchTypeConfig['class']
331
340
 
@@ -397,11 +406,27 @@ export function getDefaultOrganizationConfig(overrides?: Partial<Omit<Organizati
397
406
  }
398
407
  }
399
408
 
409
+ export const defaultTopicSortOptions: SortOption<TopicSearchSort>[] = [
410
+ { value: '-created', label: 'Date de création' },
411
+ { value: '-last_modified', label: 'Dernière mise à jour' },
412
+ ]
413
+
414
+ export function getDefaultTopicConfig(overrides?: Partial<Omit<TopicSearchConfig, 'class'>>): TopicSearchConfig {
415
+ return {
416
+ class: 'topics',
417
+ basicFilters: ['last_update_range', 'producer_type'],
418
+ advancedFilters: ['organization', 'tag'],
419
+ sortOptions: defaultTopicSortOptions,
420
+ ...overrides,
421
+ }
422
+ }
423
+
400
424
  export function getDefaultGlobalSearchConfig(): GlobalSearchConfig {
401
425
  return [
402
426
  getDefaultDatasetConfig(),
403
427
  getDefaultDataserviceConfig(),
404
428
  getDefaultReuseConfig(),
405
429
  getDefaultOrganizationConfig(),
430
+ getDefaultTopicConfig(),
406
431
  ]
407
432
  }
@@ -21,7 +21,6 @@ export type User = {
21
21
  page: UserReference['page']
22
22
  avatar: UserReference['avatar']
23
23
  avatar_thumbnail: UserReference['avatar_thumbnail']
24
- apikey?: string
25
24
  email?: string
26
25
  about: string
27
26
  website?: string
@@ -1,35 +0,0 @@
1
- import { defineComponent as f, computed as a, createElementBlock as o, openBlock as t, createBlock as p, createElementVNode as i, withCtx as m, createVNode as _, unref as c, toDisplayString as x } from "vue";
2
- import { a as h, _ as v, F as g } from "./main-ByqZlhiZ.js";
3
- const k = { class: "fr-text--xs" }, b = { key: 0 }, y = ["src"], B = /* @__PURE__ */ f({
4
- __name: "Datafair.client",
5
- props: {
6
- resource: {},
7
- dataset: {}
8
- },
9
- setup(d) {
10
- const e = d, { t: l } = h(), r = a(() => e.resource.extras.datafairOrigin || e.dataset.extras.datafairOrigin), s = a(() => e.resource.extras.datafairDatasetId || e.dataset.extras.datafairDatasetId), u = a(() => e.resource.extras.datafairEmbed), n = a(() => !r.value || !s.value ? null : `${r.value}/embed/dataset/${s.value}/${u.value}`);
11
- return (D, E) => (t(), o("div", k, [
12
- n.value ? (t(), o("div", b, [
13
- i("iframe", {
14
- src: n.value,
15
- width: "100%",
16
- height: "500px",
17
- style: { "background-color": "transparent", border: "none" }
18
- }, null, 8, y)
19
- ])) : (t(), p(v, {
20
- key: 1,
21
- type: "warning",
22
- class: "flex items-center space-x-2"
23
- }, {
24
- default: m(() => [
25
- _(c(g), { class: "shrink-0 size-6" }),
26
- i("span", null, x(c(l)("Erreur lors de l'affichage de l'aperçu.")), 1)
27
- ]),
28
- _: 1
29
- }))
30
- ]));
31
- }
32
- });
33
- export {
34
- B as default
35
- };
@@ -1,87 +0,0 @@
1
- import { defineComponent as S, defineAsyncComponent as b, ref as o, computed as w, onMounted as N, createElementBlock as p, openBlock as r, createBlock as f, createCommentVNode as T, createVNode as u, unref as t, toDisplayString as i, withCtx as d, createElementVNode as h } from "vue";
2
- import { u as P, a as E, g as q, _ as m, F as v } from "./main-ByqZlhiZ.js";
3
- const B = { class: "fr-text--xs" }, O = { key: 0 }, V = {
4
- key: 1,
5
- class: "text-gray-medium"
6
- }, L = /* @__PURE__ */ S({
7
- __name: "JsonPreview.client",
8
- props: {
9
- resource: {}
10
- },
11
- setup(k) {
12
- const z = b(
13
- () => import("./vue3-json-viewer-BXwup7nO.js").then((e) => (Promise.resolve({ }), e.JsonViewer))
14
- ), g = k, x = P(), { t: s } = E(), l = o(null), c = o(!1), n = o(null), _ = o(!1), y = w(() => q(g.resource)), J = w(() => {
15
- const e = y.value;
16
- if (!e || !x.maxJsonPreviewCharSize)
17
- return !1;
18
- const a = x.maxJsonPreviewCharSize;
19
- return e <= a;
20
- }), C = async () => {
21
- if (!J.value) {
22
- _.value = !0;
23
- return;
24
- }
25
- c.value = !0, n.value = null;
26
- try {
27
- const e = await fetch(g.resource.url);
28
- if (!e.ok)
29
- throw new Error(`HTTP error! status: ${e.status}`);
30
- const a = await e.json();
31
- l.value = a;
32
- } catch (e) {
33
- console.error("Error loading JSON:", e), e instanceof TypeError ? n.value = "network" : n.value = "generic", l.value = null;
34
- } finally {
35
- c.value = !1;
36
- }
37
- };
38
- return N(() => {
39
- C();
40
- }), (e, a) => (r(), p("div", B, [
41
- l.value ? (r(), p("div", O, [
42
- u(t(z), {
43
- value: l.value,
44
- boxed: "",
45
- sort: "",
46
- theme: "light",
47
- "max-depth": 3,
48
- "expand-depth": 2,
49
- "indent-width": 2
50
- }, null, 8, ["value"])
51
- ])) : c.value ? (r(), p("div", V, i(t(s)("Chargement de l'aperçu JSON...")), 1)) : _.value ? (r(), f(m, {
52
- key: 2,
53
- type: "warning",
54
- class: "flex items-center space-x-2"
55
- }, {
56
- default: d(() => [
57
- u(t(v), { class: "shrink-0 size-6" }),
58
- h("span", null, i(y.value ? t(s)("Fichier JSON trop volumineux pour l'aperçu. Pour consulter le fichier complet, téléchargez-le en cliquant sur le bouton bleu ou depuis l'onglet Téléchargements.") : t(s)("L'aperçu n'est pas disponible car la taille du fichier est inconnue. Pour consulter le fichier complet, téléchargez-le en cliquant sur le bouton bleu ou depuis l'onglet Téléchargements.")), 1)
59
- ]),
60
- _: 1
61
- })) : n.value === "network" ? (r(), f(m, {
62
- key: 3,
63
- type: "warning",
64
- class: "flex items-center space-x-2"
65
- }, {
66
- default: d(() => [
67
- u(t(v), { class: "shrink-0 size-6" }),
68
- h("span", null, i(t(s)("Ce fichier JSON ne peut pas être prévisualisé, peut-être parce qu'il est hébergé sur un autre site qui ne l'autorise pas. Pour le consulter, téléchargez-le en cliquant sur le bouton bleu ou depuis l'onglet Téléchargements.")), 1)
69
- ]),
70
- _: 1
71
- })) : n.value ? (r(), f(m, {
72
- key: 4,
73
- type: "warning",
74
- class: "flex items-center space-x-2"
75
- }, {
76
- default: d(() => [
77
- u(t(v), { class: "shrink-0 size-6" }),
78
- h("span", null, i(t(s)("Erreur lors du chargement de l'aperçu JSON.")), 1)
79
- ]),
80
- _: 1
81
- })) : T("", !0)
82
- ]));
83
- }
84
- });
85
- export {
86
- L as default
87
- };
@@ -1,4 +0,0 @@
1
- import { i as f } from "./main-ByqZlhiZ.js";
2
- export {
3
- f as default
4
- };
@@ -1,79 +0,0 @@
1
- import { defineComponent as b, defineAsyncComponent as L, ref as o, computed as w, onMounted as T, createElementBlock as p, openBlock as r, createBlock as m, createCommentVNode as P, createVNode as u, unref as t, toDisplayString as i, withCtx as f, createElementVNode as d } from "vue";
2
- import { u as E, a as M, g as q, _ as h, F as v } from "./main-ByqZlhiZ.js";
3
- const B = { class: "fr-text--xs" }, S = { key: 0 }, V = {
4
- key: 1,
5
- class: "text-gray-medium"
6
- }, N = /* @__PURE__ */ b({
7
- __name: "XmlPreview.client",
8
- props: {
9
- resource: {}
10
- },
11
- setup(k) {
12
- const z = L(
13
- () => import("./vue3-xml-viewer.common-DFrGHXJC.js").then((e) => e.v).then((e) => e.default || e.XmlViewer)
14
- ), g = k, x = E(), { t: l } = M(), n = o(null), c = o(!1), s = o(null), _ = o(!1), y = w(() => q(g.resource)), X = w(() => {
15
- const e = y.value;
16
- if (!e || !x.maxXmlPreviewCharSize)
17
- return !1;
18
- const a = x.maxXmlPreviewCharSize;
19
- return e <= a;
20
- }), C = async () => {
21
- if (!X.value) {
22
- _.value = !0;
23
- return;
24
- }
25
- c.value = !0, s.value = null;
26
- try {
27
- const e = await fetch(g.resource.url);
28
- if (!e.ok)
29
- throw new Error(`HTTP error! status: ${e.status}`);
30
- const a = await e.text();
31
- n.value = a;
32
- } catch (e) {
33
- console.error("Error loading XML:", e), e instanceof TypeError ? s.value = "network" : s.value = "generic", n.value = null;
34
- } finally {
35
- c.value = !1;
36
- }
37
- };
38
- return T(() => {
39
- C();
40
- }), (e, a) => (r(), p("div", B, [
41
- n.value ? (r(), p("div", S, [
42
- u(t(z), { xml: n.value }, null, 8, ["xml"])
43
- ])) : c.value ? (r(), p("div", V, i(t(l)("Chargement de l'aperçu XML...")), 1)) : _.value ? (r(), m(h, {
44
- key: 2,
45
- type: "warning",
46
- class: "flex items-center space-x-2"
47
- }, {
48
- default: f(() => [
49
- u(t(v), { class: "shrink-0 size-6" }),
50
- d("span", null, i(y.value ? t(l)("Fichier XML trop volumineux pour l'aperçu. Pour consulter le fichier complet, téléchargez-le en cliquant sur le bouton bleu ou depuis l'onglet Téléchargements.") : t(l)("L'aperçu n'est pas disponible car la taille du fichier est inconnue. Pour consulter le fichier complet, téléchargez-le en cliquant sur le bouton bleu ou depuis l'onglet Téléchargements.")), 1)
51
- ]),
52
- _: 1
53
- })) : s.value === "network" ? (r(), m(h, {
54
- key: 3,
55
- type: "warning",
56
- class: "flex items-center space-x-2"
57
- }, {
58
- default: f(() => [
59
- u(t(v), { class: "shrink-0 size-6" }),
60
- d("span", null, i(t(l)("Ce fichier XML ne peut pas être prévisualisé, peut-être parce qu'il est hébergé sur un autre site qui ne l'autorise pas. Pour le consulter, téléchargez-le en cliquant sur le bouton bleu ou depuis l'onglet Téléchargements.")), 1)
61
- ]),
62
- _: 1
63
- })) : s.value ? (r(), m(h, {
64
- key: 4,
65
- type: "warning",
66
- class: "flex items-center space-x-2"
67
- }, {
68
- default: f(() => [
69
- u(t(v), { class: "shrink-0 size-6" }),
70
- d("span", null, i(t(l)("Erreur lors du chargement de l'aperçu XML.")), 1)
71
- ]),
72
- _: 1
73
- })) : P("", !0)
74
- ]));
75
- }
76
- });
77
- export {
78
- N as default
79
- };
@@ -1,152 +0,0 @@
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>
@@ -1,194 +0,0 @@
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>
@@ -1,9 +0,0 @@
1
- import { useRoute } from 'vue-router'
2
-
3
- export function getLink(page: number): string {
4
- const route = useRoute()
5
- const routePath = route.path
6
- const search = new URLSearchParams(route.query as Record<string, string>)
7
- search.set('page', page.toFixed(0))
8
- return `${routePath}?${search.toString()}`
9
- }