@opengis/bi 1.0.13 → 1.0.14

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 (50) hide show
  1. package/README.md +2 -4
  2. package/config.js +5 -5
  3. package/dist/bi.js +1 -1
  4. package/dist/bi.umd.cjs +116 -130
  5. package/dist/{import-file-1T7kpSzt.js → import-file-DUp3rsNI.js} +11132 -10748
  6. package/dist/{map-component-mixin-BLM9iEWA.js → map-component-mixin-CGM0P5ub.js} +1135 -1134
  7. package/dist/style.css +1 -1
  8. package/dist/{vs-calendar-WiK1hcHS.js → vs-calendar-cOoinEwc.js} +33 -30
  9. package/dist/vs-funnel-bar-kLkPoIhJ.js +105 -0
  10. package/dist/vs-heatmap-3XAVGTSo.js +98 -0
  11. package/dist/vs-map-B1tr6V5_.js +74 -0
  12. package/dist/{vs-map-cluster-Dfe9INqE.js → vs-map-cluster-BWJPx7wE.js} +28 -25
  13. package/dist/vs-number-CrU7LmkV.js +48 -0
  14. package/dist/{vs-text-DcrAdQ40.js → vs-text-DRPx3aID.js} +2 -1
  15. package/package.json +37 -12
  16. package/plugin.js +4 -4
  17. package/server/migrations/bi.sql +66 -0
  18. package/server/plugins/docs.js +36 -35
  19. package/server/plugins/hook.js +72 -69
  20. package/server/plugins/vite.js +22 -8
  21. package/server/routes/dashboard/controllers/dashboard.delete.js +5 -3
  22. package/server/routes/dashboard/controllers/dashboard.js +66 -32
  23. package/server/routes/dashboard/controllers/dashboard.list.js +2 -5
  24. package/server/routes/dashboard/controllers/utils/yaml.js +1 -2
  25. package/server/routes/dashboard/index.mjs +5 -4
  26. package/server/routes/data/controllers/data.js +94 -34
  27. package/server/routes/data/controllers/util/chartSQL.js +24 -10
  28. package/server/routes/data/controllers/util/normalizeData.js +51 -29
  29. package/server/routes/data/index.mjs +1 -3
  30. package/server/routes/db/controllers/dbTablePreview.js +63 -0
  31. package/server/routes/db/controllers/dbTables.js +36 -0
  32. package/server/routes/db/index.mjs +17 -0
  33. package/server/routes/edit/controllers/dashboard.add.js +6 -5
  34. package/server/routes/edit/controllers/dashboard.edit.js +16 -9
  35. package/server/routes/edit/controllers/widget.add.js +43 -19
  36. package/server/routes/edit/controllers/widget.del.js +13 -6
  37. package/server/routes/edit/controllers/widget.edit.js +34 -13
  38. package/server/routes/edit/index.mjs +14 -10
  39. package/server/routes/map/controllers/cluster.js +89 -60
  40. package/server/routes/map/controllers/clusterVtile.js +154 -84
  41. package/server/routes/map/controllers/geojson.js +48 -22
  42. package/server/routes/map/controllers/map.js +51 -51
  43. package/server/routes/map/controllers/vtile.js +61 -40
  44. package/server/routes/map/index.mjs +1 -1
  45. package/server/utils/getWidget.js +67 -40
  46. package/utils.js +5 -4
  47. package/dist/vs-funnel-bar-CpPbYZ0_.js +0 -92
  48. package/dist/vs-heatmap-BG4eIROH.js +0 -83
  49. package/dist/vs-map-BRk6Fmks.js +0 -66
  50. package/dist/vs-number-CJq-vi95.js +0 -39
@@ -3,23 +3,33 @@ import Sphericalmercator from '@mapbox/sphericalmercator';
3
3
  import path from 'path';
4
4
  import { createHash } from 'crypto';
5
5
  import { writeFile, mkdir, readFile, stat } from 'fs/promises';
6
- import { existsSync, /*readdirSync, */ readFileSync } from 'fs';
6
+ import { existsSync, /* readdirSync, */ readFileSync } from 'fs';
7
7
 
8
8
  import yaml from '../../dashboard/controllers/utils/yaml.js';
9
9
  import normalizeData from '../../data/controllers/util/normalizeData.js';
10
10
 
11
11
  import { getWidget } from '../../../../utils.js';
12
12
 
13
- const types = { point: 'ST_Point' /* ,ST_MultiPoint */, polygon: 'ST_Polygon,ST_MultiPolygon' };
14
- const hourMs = 3.6e+6;
13
+ const types = {
14
+ point: 'ST_Point' /* ,ST_MultiPoint */,
15
+ polygon: 'ST_Polygon,ST_MultiPolygon',
16
+ };
17
+ const hourMs = 3.6e6;
15
18
 
16
19
  export default async function geojson(req, reply) {
20
+ const { pg, query = {}, funcs = {}, log } = req;
17
21
 
18
22
  const {
19
- pg, query = {}, funcs = {}, log,
20
- } = req;
21
-
22
- const { filter, widget, sql, type, nocache, id, dashboard, geom = 'geom', pointZoom = 0 } = query;
23
+ filter,
24
+ widget,
25
+ sql,
26
+ type,
27
+ nocache,
28
+ id,
29
+ dashboard,
30
+ geom = 'geom',
31
+ pointZoom = 0,
32
+ } = query;
23
33
 
24
34
  if (!widget && !dashboard) {
25
35
  return { message: 'not enough params: widget', status: 400 };
@@ -31,14 +41,17 @@ export default async function geojson(req, reply) {
31
41
  const hash = [pointZoom, filter].filter((el) => el).join();
32
42
 
33
43
  const root = funcs.getFolder(req);
34
- const file = path.join(root, `/map/geojson/${widget}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}.geojson`);
44
+ const file = path.join(
45
+ root,
46
+ `/map/geojson/${widget}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}.geojson`
47
+ );
35
48
 
36
49
  if (existsSync(file)) {
37
50
  const timeNow = Date.now();
38
51
  const stats = await stat(file);
39
52
  const birthTime = new Date(stats.birthtime).getTime();
40
- if (!(birthTime - timeNow > (hourMs * 24)) && !nocache) {
41
- const res = JSON.parse(await readFile(file, 'utf-8') || {});
53
+ if (!(birthTime - timeNow > hourMs * 24) && !nocache) {
54
+ const res = JSON.parse((await readFile(file, 'utf-8')) || {});
42
55
  return res;
43
56
  }
44
57
  }
@@ -46,30 +59,44 @@ export default async function geojson(req, reply) {
46
59
  try {
47
60
  const pkey = pg.pk?.[data?.table];
48
61
  if (!pkey) {
49
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`, status: 400 };
62
+ return {
63
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`,
64
+ status: 400,
65
+ };
50
66
  }
51
67
 
52
- // data param
68
+ // data param
53
69
  const { table, where = '1=1', xName, x } = normalizeData(data, query);
54
70
 
55
71
  if (!xName && !x) {
56
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: x axis column not specified`, status: 400 };
72
+ return {
73
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: x axis column not specified`,
74
+ status: 400,
75
+ };
57
76
  }
58
77
 
59
78
  // get sql
60
- const filterQ = filter ? await funcs.getFilterSQL({ pg, table, filter, query }) : undefined;
79
+ const filterQ = filter
80
+ ? await funcs.getFilterSQL({ pg, table, filter, query })
81
+ : undefined;
61
82
  const q = `select "${pkey}", "${xName || x}", /* st_asgeojson(geom)::json as */ ${geom} as geom from ${filterQ ? `(${filterQ})` : table} q where ${where}`;
62
83
 
63
84
  if (sql === '1') return q;
64
85
 
65
- const { st_geometrytype: geomType = 'point' } = await pg.query(`select st_geometrytype(${geom}), count(*) from ${table}
66
- where ${where} group by st_geometrytype(${geom})`).then((res) => res.rows?.[0] || {});
86
+ const { st_geometrytype: geomType = 'point' } = await pg
87
+ .query(
88
+ `select st_geometrytype(${geom}), count(*) from ${table}
89
+ where ${where} group by st_geometrytype(${geom})`
90
+ )
91
+ .then((res) => res.rows?.[0] || {});
67
92
 
68
93
  const q1 = `SELECT 'FeatureCollection' As type, json_agg(f) As features FROM (
69
94
  SELECT 'Feature' As type, row_number() over() as id,
70
- st_asgeojson(st_force2d(${query.srid
71
- ? `st_transform(${type === 'centroid' ? `st_centroid(${geom})` : geom},${query.srid})`
72
- : `${type === 'centroid' || query.point || query.centroid ? `st_centroid(${geom})` : geom}`}), 6, 0)::json as geometry,
95
+ st_asgeojson(st_force2d(${
96
+ query.srid
97
+ ? `st_transform(${type === 'centroid' ? `st_centroid(${geom})` : geom},${query.srid})`
98
+ : `${type === 'centroid' || query.point || query.centroid ? `st_centroid(${geom})` : geom}`
99
+ }), 6, 0)::json as geometry,
73
100
  (select row_to_json(tc) from (select ${'' ? `${''} as status, ` : ''}
74
101
  ${xName ? `${xName},` : ''}
75
102
  ${data.style?.colorAttr ? `${data.style.colorAttr},` : ''}
@@ -83,7 +110,7 @@ export default async function geojson(req, reply) {
83
110
  limit ${geomType?.toLowerCase()?.includes('point') ? '15000' : '2500'})f`;
84
111
 
85
112
  if (sql === '2') return q1;
86
-
113
+
87
114
  // auto Index
88
115
  funcs.autoIndex({ table, columns: [xName] });
89
116
 
@@ -93,8 +120,7 @@ export default async function geojson(req, reply) {
93
120
  await writeFile(file, JSON.stringify(res));
94
121
 
95
122
  return res;
96
- }
97
- catch (err) {
123
+ } catch (err) {
98
124
  log.error('bi/geojson', { error: err.toString(), query });
99
125
  return { error: err.toString(), status: 500 };
100
126
  }
@@ -1,57 +1,57 @@
1
-
2
-
3
1
  import { getWidget } from '../../../../utils.js';
4
2
 
5
-
6
- export default async function map({
7
- pg, funcs = {}, query = {},
8
- }) {
9
-
10
- const { dashboard, widget } = query;
11
-
12
- const { data, type, layers } = await getWidget({ dashboard, widget });
13
-
14
-
15
- if (!['map'].includes(type)) {
16
- return { message: 'access restricted: invalid widget type', status: 403 };
17
- }
18
- if (!data?.table) {
19
- return { message: 'invalid widget: param table is required', status: 400 };
20
- }
21
- if (!pg.pk[data?.table]) {
22
- return { message: 'invalid widget: table pkey not found', status: 400 };
23
- }
24
-
25
- const { q = '' } = await funcs.getFilterSQL({
26
- pg, table: data?.table, filter: query.filter,
27
- });
28
-
29
- const res = {};
30
- if (data?.color) {
31
- const { rows = [] } = await pg.query(`select count(*), "${data.color}" as val from ${data.table} where ${data.query || '1=1'} group by "${data.color}"`);
32
- Object.assign(res, { colors: rows }); // кольори для легенди
33
- }
34
- if (data?.metrics?.length) {
35
- const metric = data?.metrics[0];
36
- const q1 = `select PERCENTILE_CONT(0) WITHIN GROUP (ORDER BY "${metric}") as "0",
3
+ export default async function map({ pg, funcs = {}, query = {} }) {
4
+ const { dashboard, widget } = query;
5
+
6
+ const { data, type, layers } = await getWidget({ dashboard, widget });
7
+
8
+ if (!['map'].includes(type)) {
9
+ return { message: 'access restricted: invalid widget type', status: 403 };
10
+ }
11
+ if (!data?.table) {
12
+ return { message: 'invalid widget: param table is required', status: 400 };
13
+ }
14
+ if (!pg.pk[data?.table]) {
15
+ return { message: 'invalid widget: table pkey not found', status: 400 };
16
+ }
17
+
18
+ const { q = '' } = await funcs.getFilterSQL({
19
+ pg,
20
+ table: data?.table,
21
+ filter: query.filter,
22
+ });
23
+
24
+ const res = {};
25
+ if (data?.color) {
26
+ const { rows = [] } = await pg.query(
27
+ `select count(*), "${data.color}" as val from ${data.table} where ${data.query || '1=1'} group by "${data.color}"`
28
+ );
29
+ Object.assign(res, { colors: rows }); // кольори для легенди
30
+ }
31
+ if (data?.metrics?.length) {
32
+ const metric = data?.metrics[0];
33
+ const q1 = `select PERCENTILE_CONT(0) WITHIN GROUP (ORDER BY "${metric}") as "0",
37
34
  PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY "${metric}") as "25",
38
35
  PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY "${metric}") as "50",
39
36
  PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY "${metric}") as "75",
40
37
  PERCENTILE_CONT(1) WITHIN GROUP (ORDER BY "${metric}") as "100" from ${data.table} where ${data.query || '1=1'} and ${q || '1=1'}`;
41
- const sizes = await pg.query(q1).then((res1) => Object.values(res1.rows?.[0] || {}));
42
- Object.assign(res, { sizes }); // розміри для легенди
43
- }
44
- const { bbox } = await pg.query(`select st_asgeojson(box2d(geom))::json as bbox from ${data.table} where ${data.query || '1=1'}`).then((res1) => res1.rows?.[0] || {});
45
- Object.assign(res, {
46
-
47
- layers,
48
- columns: data.columns,
49
- bbox, // Map bounds
50
-
51
- top: [], // 10 найкращих
52
- bottom: [], // 10 найгірших
53
- });
54
- return res;
55
-
56
-
57
- }
38
+ const sizes = await pg
39
+ .query(q1)
40
+ .then((res1) => Object.values(res1.rows?.[0] || {}));
41
+ Object.assign(res, { sizes }); // розміри для легенди
42
+ }
43
+ const { bbox } = await pg
44
+ .query(
45
+ `select st_asgeojson(box2d(geom))::json as bbox from ${data.table} where ${data.query || '1=1'}`
46
+ )
47
+ .then((res1) => res1.rows?.[0] || {});
48
+ Object.assign(res, {
49
+ layers,
50
+ columns: data.columns,
51
+ bbox, // Map bounds
52
+
53
+ top: [], // 10 найкращих
54
+ bottom: [], // 10 найгірших
55
+ });
56
+ return res;
57
+ }
@@ -3,7 +3,7 @@ import Sphericalmercator from '@mapbox/sphericalmercator';
3
3
  import path from 'path';
4
4
  import { createHash } from 'crypto';
5
5
  import { writeFile, mkdir } from 'fs/promises';
6
- import { existsSync, /*readdirSync, */ readFileSync } from 'fs';
6
+ import { existsSync, /* readdirSync, */ readFileSync } from 'fs';
7
7
  import { getWidget } from '../../../../utils.js';
8
8
 
9
9
  import yaml from '../../dashboard/controllers/utils/yaml.js';
@@ -11,7 +11,10 @@ import normalizeData from '../../data/controllers/util/normalizeData.js';
11
11
 
12
12
  const mercator = new Sphericalmercator({ size: 256 });
13
13
 
14
- const types = { point: 'ST_Point' /* ,ST_MultiPoint */, polygon: 'ST_Polygon,ST_MultiPolygon' };
14
+ const types = {
15
+ point: 'ST_Point' /* ,ST_MultiPoint */,
16
+ polygon: 'ST_Polygon,ST_MultiPolygon',
17
+ };
15
18
 
16
19
  const area = {
17
20
  1: 1000000,
@@ -30,14 +33,19 @@ const area = {
30
33
  };
31
34
 
32
35
  export default async function vtile(req, reply) {
36
+ const { pg, params = {}, query = {}, funcs = {}, log } = req;
33
37
 
34
38
  const {
35
- pg, params = {}, query = {}, funcs = {}, log,
36
- } = req;
37
-
38
- const { filter, widget, dashboard, sql, cluster, type, nocache, geom = 'geom', pointZoom = 0 } = query;
39
-
40
-
39
+ filter,
40
+ widget,
41
+ dashboard,
42
+ sql,
43
+ cluster,
44
+ type,
45
+ nocache,
46
+ geom = 'geom',
47
+ pointZoom = 0,
48
+ } = query;
41
49
 
42
50
  if (!widget) {
43
51
  return { message: 'not enough params: widget', status: 400 };
@@ -50,35 +58,43 @@ export default async function vtile(req, reply) {
50
58
  return { message: 'not enough params: xyz', status: 400 };
51
59
  }
52
60
 
53
- const { data } = await getWidget({ widget, dashboard })
61
+ const { data } = await getWidget({ widget, dashboard });
54
62
 
55
63
  const headers = {
56
64
  'Content-Type': 'application/x-protobuf',
57
- 'Cache-Control': nocache || sql
58
- ? 'no-cache'
59
- : 'public, max-age=86400',
65
+ 'Cache-Control': nocache || sql ? 'no-cache' : 'public, max-age=86400',
60
66
  };
61
67
 
62
68
  const hash = [pointZoom, filter].filter((el) => el).join();
63
69
 
64
70
  const root = funcs.getFolder(req);
65
- const file = path.join(root, `/map/vtile/${widget}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}${z}/${x}/${y}.mvt`);
71
+ const file = path.join(
72
+ root,
73
+ `/map/vtile/${widget}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}${z}/${x}/${y}.mvt`
74
+ );
66
75
 
67
76
  try {
68
-
69
-
70
77
  const pkey = pg.pk?.[data?.table];
71
78
  if (!pkey) {
72
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`, status: 400 };
79
+ return {
80
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`,
81
+ status: 400,
82
+ };
73
83
  }
74
84
 
75
- // data param
76
- const { table, where = '1=1', xName = {}, metric } = normalizeData(data, query);
77
-
85
+ // data param
86
+ const {
87
+ table,
88
+ where = '1=1',
89
+ xName = {},
90
+ metric,
91
+ } = normalizeData(data, query);
78
92
 
79
93
  // get sql
80
- const columns = data.columns?.map(el => el.name || el)?.join(',') || '1';
81
- const filterQ = filter ? await funcs.getFilterSQL({ pg, table, filter }) : undefined;
94
+ const columns = data.columns?.map((el) => el.name || el)?.join(',') || '1';
95
+ const filterQ = filter
96
+ ? await funcs.getFilterSQL({ pg, table, filter })
97
+ : undefined;
82
98
  const q = `select "${pkey}",
83
99
  ${data?.color ? `"${data?.color}"` : '0'} as x,
84
100
  ${data.metrics?.[0] ? `"${data.metrics[0]}"::float` : '0'} as metric,
@@ -88,22 +104,31 @@ export default async function vtile(req, reply) {
88
104
 
89
105
  if (sql === '1') return q;
90
106
 
91
- const koef = {
92
- 10: 0.1, 11: 0.05, 12: 0.005, 13: 0.0002, 14: 0.00005,
93
- }[z] || 0.000001;
94
-
95
- const geomCol = parseInt(z, 10) < parseInt(pointZoom, 10) || true
96
- ? `ST_Centroid(${geom})`
97
- : geom;
98
-
99
- const bbox = mercator.bbox(+y, +x, +z, false/* , '900913' */);
107
+ const koef =
108
+ {
109
+ 10: 0.1,
110
+ 11: 0.05,
111
+ 12: 0.005,
112
+ 13: 0.0002,
113
+ 14: 0.00005,
114
+ }[z] || 0.000001;
115
+
116
+ const geomCol =
117
+ parseInt(z, 10) < parseInt(pointZoom, 10) || true
118
+ ? `ST_Centroid(${geom})`
119
+ : geom;
120
+
121
+ const bbox = mercator.bbox(+y, +x, +z, false /* , '900913' */);
100
122
  const bbox2d = `'BOX(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d`;
101
123
 
102
- const areaZoom = area[z] && false ? ` and (st_area(st_transform(${geom},3857)))>'${area[z]}'` : '';
124
+ const areaZoom =
125
+ area[z] && false
126
+ ? ` and (st_area(st_transform(${geom},3857)))>'${area[z]}'`
127
+ : '';
103
128
 
104
-
105
- const q1 = cluster > z
106
- ? `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
129
+ const q1 =
130
+ cluster > z
131
+ ? `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
107
132
  FROM (
108
133
  SELECT floor(random() * 100000 + 1)::int + row_number() over() as row, count(*) as point_count,
109
134
  ST_AsMVTGeom(st_transform(st_centroid(ST_Union(geom)),3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,false) as geom
@@ -114,8 +139,7 @@ export default async function vtile(req, reply) {
114
139
  GROUP BY cluster
115
140
  ORDER BY 1 DESC
116
141
  )q`
117
-
118
- : `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
142
+ : `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
119
143
  FROM (
120
144
  SELECT
121
145
  floor(random() * 100000 + 1)::int + row_number() over() as row,
@@ -139,8 +163,6 @@ export default async function vtile(req, reply) {
139
163
 
140
164
  if (sql === '2') return q1;
141
165
 
142
-
143
-
144
166
  const { rows = [] } = await pg.query(q1);
145
167
 
146
168
  if (sql === '3') return rows.map((el) => el.tile);
@@ -153,8 +175,7 @@ export default async function vtile(req, reply) {
153
175
  }
154
176
 
155
177
  return reply.headers(headers).send(buffer);
156
- }
157
- catch (err) {
178
+ } catch (err) {
158
179
  log.error('bi/vtile', { error: err.toString(), query, params });
159
180
  return { error: err.toString(), status: 500 };
160
181
  }
@@ -9,7 +9,7 @@ const biSchema = {
9
9
  querystring: {
10
10
  widget: { type: 'string', pattern: '^([\\d\\w]+)$' },
11
11
  dashboard: { type: 'string', pattern: '^([\\d\\w]+)$' },
12
- sql: { type: 'string', pattern: '^([\\d])$' }
12
+ sql: { type: 'string', pattern: '^([\\d])$' },
13
13
  },
14
14
  params: {
15
15
  id: { type: 'string', pattern: '^([\\d\\w]+)$' },
@@ -1,56 +1,83 @@
1
1
  import { getTemplate } from '@opengis/fastify-table/utils.js';
2
- import pgClients from '@opengis/fastify-table/pg/pgClients.js'
2
+ import pgClients from '@opengis/fastify-table/pg/pgClients.js';
3
+
3
4
  const pg = pgClients.client;
4
5
 
5
6
  async function getWidget({ dashboard, widget }) {
7
+ const dashboardData = dashboard
8
+ ? await getTemplate('dashboard', dashboard)
9
+ : null;
6
10
 
7
- const dashboardData = dashboard ? await getTemplate('dashboard', dashboard) : null;
8
-
9
- const { id, tableName } = !dashboardData && pg.pk['bi.dashboard'] && dashboard ?
10
- await pg.query(`select dashboard_id as id, table_name as "tableName" from bi.dashboard where $1 in (dashboard_id, name)`, [dashboard]).then((res1) => res1.rows?.[0] || {}) : {};
11
-
12
- if (!dashboardData && dashboard && !id) {
13
- return { message: `dashboard not found: ${dashboard}`, status: 404 };
14
- }
11
+ const { id, tableName } =
12
+ !dashboardData && pg.pk['bi.dashboard'] && dashboard
13
+ ? await pg
14
+ .query(
15
+ `select dashboard_id as id, table_name as "tableName" from bi.dashboard where $1 in (dashboard_id, name)`,
16
+ [dashboard]
17
+ )
18
+ .then((res1) => res1.rows?.[0] || {})
19
+ : {};
15
20
 
16
- dashboardData?.forEach(el => {
17
- el[2] = el[0].split('.')[0]
18
- })
19
- const dashboardIndex = dashboardData?.find(el => el[2] == 'index')?.[1];
21
+ if (!dashboardData && dashboard && !id) {
22
+ return { message: `dashboard not found: ${dashboard}`, status: 404 };
23
+ }
20
24
 
21
- const widgetData = dashboard ? dashboardData?.find(el => el[2] === (widget || 'index'))?.[1] : await getTemplate('widget', widget);
22
- if (typeof widgetData === 'string') {
23
- return { source: widgetData, status: 200 }
24
- }
25
- if (!id && !dashboardData && !widgetData) {
26
- return { message: `not found ${widget} ${dashboard}`, status: 404 };
27
- }
25
+ dashboardData?.forEach((el) => {
26
+ el[2] = el[0].split('.')[0];
27
+ });
28
+ const dashboardIndex = dashboardData?.find((el) => el[2] == 'index')?.[1];
28
29
 
29
- const q = `select *, coalesce(data::jsonb, '{}'::jsonb) || jsonb_build_object('table', table_name) as data from bi.widget where dashboard_id=$1 and name=$2`;
30
+ const widgetData = dashboard
31
+ ? dashboardData?.find((el) => el[2] === (widget || 'index'))?.[1]
32
+ : await getTemplate('widget', widget);
33
+ if (typeof widgetData === 'string') {
34
+ return { source: widgetData, status: 200 };
35
+ }
36
+ if (!id && !dashboardData && !widgetData) {
37
+ return { message: `not found ${widget} ${dashboard}`, status: 404 };
38
+ }
30
39
 
31
- const { type, text, data = {}, controls, style, options } = widgetData || await pg.query(q, [id || dashboard, widget]).then((res1) => res1.rows?.[0] || {});
40
+ const q = `select *, coalesce(data::jsonb, '{}'::jsonb) || jsonb_build_object('table', table_name) as data from bi.widget where dashboard_id=$1 and name=$2`;
32
41
 
33
- if (!type) {
34
- return { message: `widget not found: ${widget}`, status: 404 };
35
- }
42
+ const {
43
+ type,
44
+ text,
45
+ data = {},
46
+ controls,
47
+ style,
48
+ options,
49
+ yml
50
+ } = widgetData ||
51
+ (await pg
52
+ .query(q, [id || dashboard, widget])
53
+ .then((res1) => res1.rows?.[0] || {}));
36
54
 
37
- Object.assign(data, {
38
- table: data.table
39
- || tableName
40
- || widgetData?.table_name
41
- || dashboardIndex?.table
42
- || dashboardIndex?.table_name,
43
- db: dashboardIndex?.db || pgClients.client?.options?.database,
44
- });
45
- const main = { ...dashboardIndex || {}, ...widgetData, ...data };
55
+ if (!type) {
56
+ return { message: `widget not found: ${widget}`, status: 404 };
57
+ }
46
58
 
47
- if (!main?.table) {
48
- return { message: /* json.error || */ `invalid ${widget ? 'widget' : 'dashboard'}: 1`, status: 404 };
49
- }
59
+ Object.assign(data, {
60
+ table:
61
+ data.table ||
62
+ tableName ||
63
+ widgetData?.table_name ||
64
+ dashboardIndex?.table ||
65
+ dashboardIndex?.table_name,
66
+ db: dashboardIndex?.db || pgClients.client?.options?.database,
67
+ });
68
+ const main = { ...(dashboardIndex || {}), ...widgetData, ...data };
50
69
 
51
- const tableSQL = main?.sql?.map((el, i) => `left join lateral(${el})t${i + 1} on 1=1`);
70
+ if (!main?.table) {
71
+ return {
72
+ message: /* json.error || */ `invalid ${widget ? 'widget' : 'dashboard'}: 1`,
73
+ status: 404,
74
+ };
75
+ }
52
76
 
53
- return { ...main, tableSQL, data, type, text, controls, style, options };
77
+ const tableSQL = main?.sql?.map(
78
+ (el, i) => `left join lateral(${el})t${i + 1} on 1=1`
79
+ );
54
80
 
81
+ return { ...main, tableSQL, data, type, text, controls, style, options, yml };
55
82
  }
56
- export default getWidget;
83
+ export default getWidget;
package/utils.js CHANGED
@@ -3,9 +3,10 @@
3
3
 
4
4
  // import getTemplatePath from '@opengis/fastify-table/table/controllers/utils/getTemplatePath.js';
5
5
  import getWidget from './server/utils/getWidget.js';
6
+ import yamlSafe from './server/routes/dashboard/controllers/utils/yaml.js';
6
7
 
7
- export default null;
8
8
  export {
9
- // getTemplatePath,
10
- getWidget
11
- };
9
+ // getTemplatePath,
10
+ yamlSafe,
11
+ getWidget,
12
+ };
@@ -1,92 +0,0 @@
1
- import { _ as l, c as h, g as u, a as d, b as p, d as n, i as m } from "./import-file-1T7kpSzt.js";
2
- import { openBlock as f, createElementBlock as y } from "vue";
3
- const D = {
4
- mixins: [h],
5
- data() {
6
- return {
7
- myChartvt: null,
8
- uniqueID: null
9
- };
10
- },
11
- async mounted() {
12
- this.uniqueID = u(), await this.$nextTick(), await this.getData();
13
- const { series: e } = this.prepareData();
14
- e && this.initChart(e);
15
- },
16
- methods: {
17
- buildTooltipForDonut(e, t) {
18
- const { name: r, value: a, percent: s } = e;
19
- return `
20
- <div style="background-color:${t[0]};font-size: 12px; font-family: Helvetica, Arial, sans-serif;color:#ffff; padding:5px; border-radius:5px; ![box-shadow:none]">
21
- ${d(r)}: ${p(a)} (${s}%)
22
- </div>`;
23
- },
24
- prepareData() {
25
- var e;
26
- try {
27
- const t = Object.keys((e = this.sourceData) == null ? void 0 : e[0]), r = Array.from(new Set(this.sourceData.map((o) => o[t[0]]))), a = Array.from(new Set(this.sourceData.map((o) => o[t[1]]))), s = r.map((o, c) => ({
28
- name: o,
29
- value: a[c]
30
- }));
31
- return { series: [
32
- {
33
- name: this.titleCharts ? this.titleCharts : t[0],
34
- type: "funnel",
35
- ...n(this.styleData),
36
- data: s,
37
- height: "100%",
38
- emphasis: {
39
- label: {
40
- show: !1
41
- }
42
- }
43
- }
44
- ] };
45
- } catch (t) {
46
- console.error(t);
47
- }
48
- },
49
- async initChart(e) {
50
- try {
51
- const t = document.getElementById(this.uniqueID), r = m(t), a = {
52
- series: e,
53
- ...n(this.styleData),
54
- tooltip: {
55
- trigger: "item",
56
- formatter: (s) => this.buildTooltipForDonut(s, [s.color]),
57
- borderWidth: 0,
58
- appendToBody: !0,
59
- borderColor: "transparent",
60
- textStyle: {
61
- color: "#000"
62
- },
63
- padding: [10, 15],
64
- shadowColor: "transparent",
65
- backgroundColor: "transparent"
66
- },
67
- itemStyle: {
68
- height: "15px"
69
- },
70
- labelLine: {
71
- show: !1
72
- }
73
- };
74
- r.setOption(a), r.resize(), window.addEventListener("resize", () => {
75
- r.resize();
76
- });
77
- } catch (t) {
78
- console.error(t);
79
- }
80
- }
81
- }
82
- }, b = ["id"];
83
- function x(e, t, r, a, s, i) {
84
- return f(), y("div", {
85
- id: s.uniqueID,
86
- class: "h-[90%] custom-scrollbar min-h-[200px]"
87
- }, null, 8, b);
88
- }
89
- const v = /* @__PURE__ */ l(D, [["render", x]]);
90
- export {
91
- v as default
92
- };