@opengis/bi 1.0.21 → 1.0.22

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 (60) hide show
  1. package/dist/bi.js +1 -1
  2. package/dist/bi.umd.cjs +101 -101
  3. package/dist/{import-file-C8BY90-b.js → import-file-r0dN1aVl.js} +19761 -18120
  4. package/dist/{map-component-mixin-CFtShPun.js → map-component-mixin-DU9YFNY4.js} +1 -1
  5. package/dist/style.css +1 -1
  6. package/dist/{vs-calendar-B9vXdsaG.js → vs-calendar-B64GoLWu.js} +1 -1
  7. package/dist/{vs-funnel-bar-Cj0O8tIf.js → vs-funnel-bar-DV5vXI3k.js} +1 -1
  8. package/dist/{vs-heatmap-C9oFph_f.js → vs-heatmap-Ox5uspv9.js} +1 -1
  9. package/dist/{vs-map-WOn0RAU7.js → vs-map-9t4WlfUa.js} +2 -2
  10. package/dist/{vs-map-cluster-RJa6sNfI.js → vs-map-cluster-DSZGPUt8.js} +2 -2
  11. package/dist/{vs-number-BG0szZL-.js → vs-number-O3_Cvwaw.js} +1 -1
  12. package/dist/vs-table-C_CsDsZN.js +68 -0
  13. package/dist/{vs-text-Kwl3-0yy.js → vs-text-I3gRlw-X.js} +2 -2
  14. package/package.json +5 -4
  15. package/plugin.js +1 -1
  16. package/server/helpers/mdToHTML.js +17 -0
  17. package/server/migrations/bi.dataset.sql +13 -0
  18. package/server/routes/dashboard/controllers/dashboard.js +3 -3
  19. package/server/routes/data/controllers/data.js +12 -13
  20. package/server/routes/data/controllers/util/chartSQL.js +6 -3
  21. package/server/routes/dataset/controllers/bi.dataset.list.js +3 -1
  22. package/server/routes/dataset/controllers/comment.js +55 -0
  23. package/server/routes/dataset/controllers/createDatasetPost.js +132 -0
  24. package/server/routes/dataset/controllers/data.js +145 -0
  25. package/server/routes/{db → dataset}/controllers/dbTablePreview.js +17 -24
  26. package/server/routes/{db → dataset}/controllers/dbTables.js +7 -11
  27. package/server/routes/dataset/controllers/delete.js +39 -0
  28. package/server/routes/dataset/controllers/{bi.dataset.edit.js → editDataset.js} +32 -26
  29. package/server/routes/dataset/controllers/export.js +213 -0
  30. package/server/routes/dataset/controllers/form.js +99 -0
  31. package/server/routes/dataset/controllers/format.js +44 -0
  32. package/server/routes/dataset/controllers/insert.js +46 -0
  33. package/server/routes/dataset/controllers/table.js +69 -0
  34. package/server/routes/dataset/controllers/update.js +42 -0
  35. package/server/routes/dataset/index.mjs +88 -43
  36. package/server/routes/dataset/utils/convertJSONToCSV.js +17 -0
  37. package/server/routes/dataset/utils/convertJSONToXls.js +49 -0
  38. package/server/routes/dataset/utils/createTableQuery.js +59 -0
  39. package/server/routes/dataset/utils/datasetForms.js +1 -0
  40. package/server/routes/dataset/utils/descriptionList.js +46 -0
  41. package/server/routes/dataset/utils/downloadRemoteFile.js +58 -0
  42. package/server/routes/dataset/utils/executeQuery.js +46 -0
  43. package/server/routes/dataset/utils/getLayersData.js +107 -0
  44. package/server/routes/dataset/utils/getTableData.js +47 -0
  45. package/server/routes/dataset/utils/insertDataQuery.js +12 -0
  46. package/server/routes/dataset/utils/metaFormat.js +24 -0
  47. package/server/routes/edit/controllers/widget.edit.js +23 -5
  48. package/server/routes/map/controllers/heatmap.js +118 -0
  49. package/server/routes/map/index.mjs +2 -0
  50. package/server/utils/getWidget.js +5 -2
  51. package/server/routes/dataset/controllers/bi.dataset.add.js +0 -86
  52. package/server/routes/dataset/controllers/bi.dataset.data.add.js +0 -49
  53. package/server/routes/dataset/controllers/bi.dataset.data.del.js +0 -54
  54. package/server/routes/dataset/controllers/bi.dataset.data.edit.js +0 -55
  55. package/server/routes/dataset/controllers/bi.dataset.data.list.js +0 -71
  56. package/server/routes/dataset/controllers/bi.dataset.del.js +0 -48
  57. package/server/routes/dataset/controllers/bi.dataset.demo.add.js +0 -97
  58. package/server/routes/dataset/controllers/util/create.table.js +0 -21
  59. package/server/routes/dataset/controllers/util/prepare.data.js +0 -49
  60. package/server/routes/db/index.mjs +0 -17
@@ -0,0 +1,99 @@
1
+ import {
2
+ config, pgClients, getMeta, getToken,
3
+ } from '@opengis/fastify-table/utils.js';
4
+
5
+ import datasetForms from '../utils/datasetForms.js';
6
+
7
+ const inputType = {
8
+ text: 'Text',
9
+ autocomplete: 'Autocomplete',
10
+ select: 'Autocomplete',
11
+ date: 'DatePicker',
12
+ 'yes/no': 'Switcher',
13
+ badge: 'Select',
14
+ number: 'Number',
15
+ tags: 'Tags',
16
+ file: 'File',
17
+ };
18
+
19
+ const systemColumns = [
20
+ 'uid',
21
+ 'cdate',
22
+ 'editor_id',
23
+ 'editor_date',
24
+ 'files',
25
+ ];
26
+
27
+ export default async function datasetForm({
28
+ pg = pgClients.client, params = {}, query = {}, user = {},
29
+ }) {
30
+ if (!user?.uid) {
31
+ return { message: 'access restricted', status: 403 };
32
+ }
33
+
34
+ if (!params?.id) {
35
+ return { message: 'not enough params: id', status: 404 };
36
+ }
37
+
38
+ const tokenData = await getToken({
39
+ uid: user?.uid,
40
+ token: params.id,
41
+ mode: 'a',
42
+ json: 1,
43
+ });
44
+
45
+ if (!tokenData && !config.local && !config.debug) {
46
+ return { message: 'token not allow', status: 403 };
47
+ }
48
+
49
+ const {
50
+ id, columns, formSetting, table, style,
51
+ } = await pg.query(`select dataset_id as id, table_name as table, column_list as columns, style,
52
+ form_setting as "formSetting" from bi.dataset where dataset_id=$1`, [tokenData?.dataset || params.id])
53
+ .then(el => el.rows?.[0] || {});
54
+
55
+ if (!id) {
56
+ return { message: 'dataset not found', status: 404 };
57
+ }
58
+
59
+ if (datasetForms[id] && !query.nocache) {
60
+ return datasetForms[id];
61
+ }
62
+
63
+ const { columns: dbColumns = [] } = await getMeta(table) || {};
64
+ const isFilesColumn = dbColumns.find((el) => el.name === 'files' && pg.pgType[el.dataTypeID] === 'text');
65
+
66
+ const formSchema = formSetting
67
+ || ((columns || dbColumns).filter((col) => !systemColumns.includes(col.name))).reduce((acc, curr) => Object.assign(acc, {
68
+ [curr.name]: {
69
+ type: inputType[curr?.format || curr?.type || 'text'] || 'Text',
70
+ ua: curr?.description || curr?.title || curr?.name,
71
+ validators: curr?.is_required ? ['required'] : null,
72
+ options: curr?.values?.length ? curr?.values : null,
73
+ data: curr?.data,
74
+ min: curr?.type || curr?.format === 'number' ? 0 : null,
75
+ },
76
+ }), {});
77
+
78
+ Object.assign(formSchema, {
79
+ geom: {
80
+ type: 'Geom',
81
+ ua: 'Геометрія',
82
+ height: '500',
83
+ geom_edit_controol: [style?.type ? [style.type] : null],
84
+ },
85
+ });
86
+
87
+ if (isFilesColumn) {
88
+ Object.assign(formSchema, {
89
+ files: {
90
+ type: 'FileList',
91
+ ua: 'Файли',
92
+ },
93
+ });
94
+ }
95
+
96
+ datasetForms[id] = formSchema;
97
+
98
+ return formSchema;
99
+ }
@@ -0,0 +1,44 @@
1
+ import getTableData from "../utils/getTableData.js";
2
+ import getLayersData from "../utils/getLayersData.js";
3
+
4
+ export default async function gisFormat({ query = {}, pg }) {
5
+ const time = [Date.now()];
6
+ const { layer, table, id, lat, lng } = query;
7
+
8
+ if (!table && !layer) {
9
+ return { message: 'not enough params', status: 400 };
10
+ }
11
+
12
+ if (layer && !(lat && lng)) {
13
+ return { message: 'not enough params: lat/lng', status: 400 };
14
+ }
15
+
16
+ if (table && !(id || layer)) {
17
+ return { message: 'not enough params: id', status: 400 };
18
+ }
19
+
20
+ const { message, template, rows, layers, html, body } = layer
21
+ ? await getLayersData({ pg, layer, table, id, lat, lng, time })
22
+ : await getTableData({ pg, table, id, time });
23
+
24
+ if (message) return { message, status: 400 };
25
+
26
+ return {
27
+ time: {
28
+ layer: time[1] - time[0],
29
+ data: time[2] - time[1],
30
+ html: time[3] - time[2],
31
+ total: time[3] - time[0],
32
+ },
33
+
34
+ id: rows[0]?.id,
35
+ table,
36
+ layers,
37
+
38
+ data: { geom: rows[0]?.geom },
39
+ rows,
40
+ html,
41
+ template,
42
+ body,
43
+ };
44
+ }
@@ -0,0 +1,46 @@
1
+ import {
2
+ config, getToken, dataInsert, pgClients,
3
+ } from '@opengis/fastify-table/utils.js';
4
+
5
+ import datasetForms from '../utils/datasetForms.js';
6
+
7
+ export default async function datasetDataInsert({
8
+ pg = pgClients.client, params = {}, body = {}, user = {},
9
+ }) {
10
+ if (!user?.uid) {
11
+ return { message: 'access restricted', status: 403 };
12
+ }
13
+
14
+ const tokenData = await getToken({
15
+ uid: user?.uid,
16
+ token: params.id,
17
+ mode: 'a',
18
+ json: 1,
19
+ });
20
+
21
+ if (!tokenData && !config.local && !config.debug) {
22
+ return { message: 'token not allow', status: 403 };
23
+ }
24
+
25
+ const dataset = await pg.query('select dataset_id as id, table_name as table from bi.dataset where dataset_id=$1', [tokenData?.dataset || params.id])
26
+ .then(el => el.rows[0] || {});
27
+
28
+ const { form, table: add } = tokenData
29
+ || { form: `${dataset.id}.form`, table: dataset.table };
30
+
31
+ if (!form || !dataset.id || !datasetForms[dataset.id]) {
32
+ return { message: 'dataset not found', status: 404 };
33
+ }
34
+
35
+ const res = await dataInsert({
36
+ table: add,
37
+ data: body,
38
+ uid: user?.uid,
39
+ });
40
+
41
+ if (!res) {
42
+ return { message: 'nothing added' };
43
+ }
44
+
45
+ return { rows: res.rows };
46
+ }
@@ -0,0 +1,69 @@
1
+ import {
2
+ config, pgClients, getMeta, setToken, getToken,
3
+ } from '@opengis/fastify-table/utils.js';
4
+
5
+ export default async function datasetFormData({
6
+ pg = pgClients.client, params = {}, query = {}, user = {},
7
+ }) {
8
+ if (!params?.id) {
9
+ return { message: 'not enough params: id', status: 400 };
10
+ }
11
+
12
+ if (!user?.uid) {
13
+ return { message: 'access restricted', status: 403 };
14
+ }
15
+
16
+ const { user_type: userType = 'regular' } = user;
17
+
18
+ const tokenData = await getToken({
19
+ uid: user?.uid,
20
+ token: params.id,
21
+ mode: 'w',
22
+ json: 1,
23
+ });
24
+
25
+ if (!tokenData && !params?.object_id) {
26
+ return { message: 'not enough params: object_id', status: 400 };
27
+ }
28
+
29
+ const dataset = await pg.query(`select dataset_id as id, table_name as table, column_list as columns, filter_list as filters,
30
+ sql_list as sql, style, form_setting, pk, access_level from bi.dataset where dataset_id=$1`, [tokenData?.dataset || params.id]).then(el => el.rows[0] || {});
31
+
32
+ if (!dataset?.id) {
33
+ return { message: 'dataset not found', status: 404 };
34
+ }
35
+
36
+ const tlist = await pg.query(`select array_agg((select nspname from pg_namespace where oid=relnamespace)||'.'||relname) tlist
37
+ from pg_class where relkind in ('r','v')`).then(el => el.rows[0]?.tlist || []);
38
+
39
+ if (!dataset.table || !tlist.includes(dataset.table.replace(/"/g, '')) || (!pg.pk?.[dataset.table.replace(/"/g, '')] && !dataset.pk)) {
40
+ return { message: `table not found: ${dataset.table}`, status: 404 };
41
+ }
42
+
43
+ const { pk = dataset.pk, columns: dbColumns = [] } = await getMeta(dataset.table) || {};
44
+ const cols = dataset.columns?.filter((el) => el.name !== 'geom')?.map((el) => el.name || el)?.join(',');
45
+
46
+ const where = [`"${pk}" = $1`, dataset.query].filter((el) => el);
47
+
48
+ const geom = dbColumns.find((el) => el.name === 'geom' && pg.pgType[el.dataTypeID] === 'geometry') ? ',st_asgeojson(geom)::json as geom' : '';
49
+ const files = dbColumns.find((el) => el.name === 'files' && pg.pgType[el.dataTypeID] === 'text') ? ',files' : '';
50
+ const q = `select "${pk}" as id, ${cols || '*'} ${files} ${geom} from ${dataset.table} t where ${where.join(' and ') || 'true'} limit 1`;
51
+
52
+ if (query.sql === '1' && (config.debug || userType.includes('admin'))) { return q; }
53
+
54
+ const data = await pg.query(q, [tokenData?.id || params.object_id]).then(el => el.rows?.[0] || {});
55
+
56
+ data.token = setToken({
57
+ ids: [JSON.stringify({
58
+ id: data.id,
59
+ table: dataset.table,
60
+ form: `${dataset.id}.form`,
61
+ dataset: dataset.id,
62
+ })],
63
+ uid: user?.uid,
64
+ array: 1,
65
+ })[0];
66
+
67
+ return data;
68
+ }
69
+
@@ -0,0 +1,42 @@
1
+ import {
2
+ config, getToken, dataUpdate, pgClients,
3
+ } from '@opengis/fastify-table/utils.js';
4
+
5
+ import datasetForms from '../utils/datasetForms.js';
6
+
7
+ export default async function datasetDataUpdate({
8
+ pg = pgClients.client, params = {}, body = {}, user = {},
9
+ }) {
10
+ if (!user?.uid) {
11
+ return { message: 'access restricted', status: 403 };
12
+ }
13
+
14
+ const tokenData = await getToken({
15
+ uid: user?.uid,
16
+ token: params.id,
17
+ mode: 'a',
18
+ json: 1,
19
+ });
20
+
21
+ if (!tokenData && !config.local && !config.debug) {
22
+ return { message: 'token not allow', status: 403 };
23
+ }
24
+
25
+ const dataset = await pg.query('select dataset_id as id, table_name as table from bi.dataset where dataset_id=$1', [tokenData?.dataset || params.id])
26
+ .then(el => el.rows[0] || {});
27
+
28
+ const table = tokenData?.table || dataset.table;
29
+
30
+ if (!dataset.id || !datasetForms[dataset.id]) {
31
+ return { message: 'dataset not found', status: 404 };
32
+ }
33
+
34
+ const res = await dataUpdate({
35
+ id: tokenData?.id || params?.object_id,
36
+ table,
37
+ data: body,
38
+ uid: user?.uid,
39
+ });
40
+
41
+ return res;
42
+ }
@@ -1,75 +1,120 @@
1
- import biDatasetDataList from './controllers/bi.dataset.data.list.js'; // get dataset structure + data
2
- import biDatasetDemoAdd from './controllers/bi.dataset.demo.add.js'; // import demo datasets
3
- import biDatasetList from './controllers/bi.dataset.list.js'; // get dataset list
4
- import biDatasetAdd from './controllers/bi.dataset.add.js'; // assign table / import data from body (vue sheet) or file (relative path)
5
- import biDatasetDel from './controllers/bi.dataset.del.js'; // delete dataset + data
6
- import biDatasetEdit from './controllers/bi.dataset.edit.js'; // edit dataset structure
7
- import biDbList from './controllers/bi.db.list.js'; // list db
8
- import biDatasetDataAdd from './controllers/bi.dataset.data.add.js';
9
- import biDatasetDataEdit from './controllers/bi.dataset.data.edit.js';
10
- import biDatasetDataDel from './controllers/bi.dataset.data.del.js';
1
+ import dbTables from './controllers/dbTables.js';
2
+ import dbTablePreview from './controllers/dbTablePreview.js';
11
3
 
12
- const biSchema = {};
4
+ import createDatasetPost from './controllers/createDatasetPost.js';
5
+ import editDataset from './controllers/editDataset.js';
6
+ import datasetData from './controllers/data.js';
7
+ import datasetFormData from './controllers/table.js';
8
+ import datasetDataInsert from './controllers/insert.js';
9
+ import datasetDataUpdate from './controllers/update.js';
10
+ import datasetDataDelete from './controllers/delete.js';
11
+ import datasetDataExport from './controllers/export.js';
12
+ import datasetForm from './controllers/form.js';
13
+ import format from './controllers/format.js';
14
+ import datasetEditComment from './controllers/comment.js';
15
+ import biDbList from './controllers/bi.db.list.js'; // list db
16
+ import biDatasetList from './controllers/bi.dataset.list.js'; // list datasets
13
17
 
14
18
  export default async function route(fastify, opts) {
15
19
  fastify.route({
16
- method: 'POST',
17
- url: '/bi-datasets',
18
- schema: biSchema,
19
- handler: biDatasetAdd,
20
+ method: 'GET',
21
+ url: '/bi-db-list',
22
+ schema: {},
23
+ handler: biDbList,
20
24
  });
21
25
  fastify.route({
22
26
  method: 'GET',
23
- url: '/bi-dataset-demo',
24
- schema: biSchema,
25
- handler: biDatasetDemoAdd,
27
+ url: '/bi-datasets',
28
+ schema: {},
29
+ handler: biDatasetList,
26
30
  });
27
31
  fastify.route({
28
32
  method: 'GET',
29
- url: '/bi-db-list',
30
- schema: biSchema,
31
- handler: biDbList,
33
+ url: '/gis-format',
34
+ config: { policy: ['site'] },
35
+ handler: format,
32
36
  });
33
37
  fastify.route({
34
38
  method: 'GET',
35
- url: '/bi-datasets',
36
- schema: biSchema,
37
- handler: biDatasetList,
39
+ url: '/db-tables',
40
+ schema: {},
41
+ config: { policy: ['site'] },
42
+ handler: dbTables,
38
43
  });
39
44
  fastify.route({
40
45
  method: 'GET',
41
- url: '/bi-datasets/:id',
42
- schema: biSchema,
43
- handler: biDatasetDataList,
46
+ url: '/db-tables/:name',
47
+ schema: {},
48
+ config: { policy: ['site'] },
49
+ handler: dbTablePreview,
44
50
  });
45
51
  fastify.route({
46
- method: 'DELETE',
47
- url: '/bi-datasets/:id',
48
- schema: biSchema,
49
- handler: biDatasetDel,
52
+ method: 'POST',
53
+ url: '/dataset',
54
+ schema: {},
55
+ config: { policy: ['site'] },
56
+ handler: createDatasetPost,
50
57
  });
51
58
  fastify.route({
52
59
  method: 'PUT',
53
- url: '/bi-datasets/:id',
54
- schema: biSchema,
55
- handler: biDatasetEdit,
60
+ url: '/dataset',
61
+ schema: {},
62
+ config: { policy: ['site'] },
63
+ handler: editDataset,
64
+ });
65
+ fastify.route({
66
+ method: 'GET',
67
+ url: '/dataset-data/:id',
68
+ schema: {},
69
+ config: { policy: ['site'] },
70
+ handler: datasetData,
71
+ });
72
+ fastify.route({
73
+ method: 'GET',
74
+ url: '/dataset-data/:id/:object_id',
75
+ schema: {},
76
+ config: { policy: ['site'] },
77
+ handler: datasetFormData,
56
78
  });
57
79
  fastify.route({
58
80
  method: 'POST',
59
- url: '/bi-datasets/:id',
60
- schema: biSchema,
61
- handler: biDatasetDataAdd,
81
+ url: '/dataset-data/:id',
82
+ schema: {},
83
+ config: { policy: ['site'] },
84
+ handler: datasetDataInsert,
62
85
  });
63
86
  fastify.route({
64
87
  method: 'PUT',
65
- url: '/bi-datasets/:id/:objectId',
66
- schema: biSchema,
67
- handler: biDatasetDataEdit,
88
+ url: '/dataset-data/:id/:object_id?',
89
+ schema: {},
90
+ config: { policy: ['site'] },
91
+ handler: datasetDataUpdate,
68
92
  });
69
93
  fastify.route({
70
94
  method: 'DELETE',
71
- url: '/bi-datasets/:id/:objectId',
72
- schema: biSchema,
73
- handler: biDatasetDataDel,
95
+ url: '/dataset-data/:id/:object_id?',
96
+ schema: {},
97
+ config: { policy: ['site'] },
98
+ handler: datasetDataDelete,
99
+ });
100
+ fastify.route({
101
+ method: 'GET',
102
+ url: '/dataset-form/:id',
103
+ schema: {},
104
+ config: { policy: ['site'] },
105
+ handler: datasetForm,
106
+ });
107
+ fastify.route({
108
+ method: 'GET',
109
+ url: '/dataset-export/:id',
110
+ schema: {},
111
+ config: { policy: ['site'] },
112
+ handler: datasetDataExport,
113
+ });
114
+ fastify.route({
115
+ method: 'POST',
116
+ url: '/dataset-comment/:id',
117
+ config: { policy: ['site'] },
118
+ handler: datasetEditComment,
74
119
  });
75
120
  }
@@ -0,0 +1,17 @@
1
+ import path from 'node:path';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+
4
+ export default async function convertJSONToCSV({
5
+ filepathJSON, send = () => { }, delimiter = ';',
6
+ }) {
7
+ if (!filepathJSON) return null;
8
+ const rows = JSON.parse(await readFile(filepathJSON) || '{}');
9
+ const values = rows.map((row) => Object.values(row).map((el) => `${el};`));
10
+ values.unshift(Object.keys(rows[0]).map((el) => `${el};`));
11
+
12
+ send('Конвертація успішно завершена. Формування файлу...');
13
+ await mkdir(path.dirname(filepathJSON), { recursive: true });
14
+ await writeFile(filepathJSON.replace('.json', '.csv'), `\ufeff${values.join('\n').replace(/;,/g, delimiter)}`, 'utf8');
15
+ send('Файл успішно сформовано. Оновіть сторінку або натисніть кнопку ще раз для завантаження. ', 1);
16
+ return { message: 'Файл успішно сформовано. Натистіть кнопку ще раз для завантаження даних', status: 200 };
17
+ }
@@ -0,0 +1,49 @@
1
+ import path from 'path';
2
+ import {
3
+ mkdir, readFile, writeFile,
4
+ } from 'fs/promises';
5
+
6
+ import { logger } from '@opengis/fastify-table/utils.js';
7
+
8
+ import { grpc } from '@opengis/fastify-file/utils.js';
9
+
10
+ const { jsonToXls } = grpc();
11
+
12
+ export default async function convertJSONToXls({
13
+ filepathJSON, colmodel, domain, source, send = () => { },
14
+ }) {
15
+ if (!filepathJSON) return null;
16
+
17
+ const rows = JSON.parse(await readFile(filepathJSON) || '{}');
18
+
19
+ if (!rows?.length) {
20
+ return { message: 'empty rows', status: 400 };
21
+ }
22
+
23
+ send('Конвертація файлу...');
24
+ try {
25
+ const { result } = await jsonToXls({
26
+ json: JSON.stringify(rows),
27
+ header: source,
28
+ subheader: `Завантажено з порталу ${domain} - ${new Date().toLocaleDateString()}. Всього: ${rows.length}`,
29
+ colmodel: JSON.stringify(colmodel),
30
+ });
31
+ send('Конвертація успішно завершена. Формування файлу...');
32
+ await mkdir(path.dirname(filepathJSON), { recursive: true });
33
+ await writeFile(filepathJSON.replace('.json', '.xlsx'), result, 'base64');
34
+ send('Файл успішно сформовано. Оновіть сторінку або натисніть кнопку ще раз для завантаження. ', 1);
35
+ return { message: 'Файл успішно сформовано. Натистіть кнопку ще раз для завантаження даних', status: 200 };
36
+ }
37
+ catch (err) {
38
+ send(`jsonToXLS err: ${err.toString()}`, 1);
39
+ logger.error({
40
+ name: 'export/table',
41
+ filepathJSON,
42
+ colmodel,
43
+ domain,
44
+ rows: rows.length,
45
+ error: err.toString(),
46
+ });
47
+ return { error: err.toString(), status: 500 };
48
+ }
49
+ }
@@ -0,0 +1,59 @@
1
+ import { randomBytes, createHash } from 'node:crypto';
2
+
3
+ const schema = 'data_user';
4
+
5
+ const columnType = {
6
+ text: 'text',
7
+ select: 'text',
8
+ date: 'date',
9
+ 'yes/no': 'boolean',
10
+ badge: 'text',
11
+ number: 'numeric',
12
+ tags: 'text[]',
13
+ geom: 'geom',
14
+ };
15
+
16
+ export default function createTable(columns, name) {
17
+ const tableName = randomBytes(64).toString('hex').substring(0, 24).replace(/^\d+/, '');
18
+ const table = `${schema}.${tableName}`;
19
+ const pkey = tableName.concat('_id');
20
+
21
+ columns?.forEach?.((el, i) => Object.assign(el, {
22
+ name: `col_${i}`, // el.name
23
+ type: columnType[el.format || 'text'] || 'text',
24
+ }));
25
+
26
+ const clsQuery = columns
27
+ ?.filter?.(col => col?.format === 'select' && col.data?.length)
28
+ ?.map(col => {
29
+ const data = JSON.stringify(col.data).replace(/'/g, "''");
30
+ Object.assign(col, { data: createHash('md5').update([tableName, col.name].join()).digest('hex') });
31
+ return `insert into admin.cls(clsid,name,type,module) values('${col.data}','${col.data}','json', '${table}');
32
+ insert into admin.cls(code,name,parent,icon,data,module)
33
+ select value->>'id',value->>'text','${col.data}',value->>'icon',value->>'data', '${table}'
34
+ from json_array_elements('${data}'::json)`;
35
+ })
36
+ ?.join(';');
37
+
38
+ const createQuery = `create table if not exists ${table} (
39
+ ${pkey} text not null default public.uuid_generate_v4(),
40
+ geom public.geometry, ${columns ? `
41
+ ${columns?.map?.((el) => `${el.name} ${el.type}`).join(', ')},` : ''}
42
+ cdate timestamp without time zone not null default now(),
43
+ editor_date timestamp without time zone,
44
+ uid text,
45
+ editor_id text,
46
+ files json,
47
+ constraint ${table.replace(/\./g, '_')}_constraint_pkey PRIMARY KEY (${pkey}) )`;
48
+
49
+ const commentQuery = columns
50
+ ?.filter?.((el) => el.title)
51
+ ?.map((el) => `comment on column ${table}.${el.name} is '${el.title}'`)
52
+ ?.join(';');
53
+
54
+ return {
55
+ table,
56
+ pkey,
57
+ sql: [createQuery, commentQuery, (name ? `comment on table ${table} is '${name}'` : undefined), clsQuery].filter((el) => el).join(';'),
58
+ };
59
+ }
@@ -0,0 +1 @@
1
+ export default {};
@@ -0,0 +1,46 @@
1
+ import { handlebarsSync, handlebars } from '@opengis/fastify-table/utils.js'
2
+
3
+ function format(d, key, data) {
4
+ if (!key?.includes) return '';
5
+ if (d === true) return 'так';
6
+ if (d === false) return 'ні'
7
+
8
+ if (key.includes('{{')) {
9
+ return handlebarsSync.compile(key)(data);
10
+ }
11
+ if (!d) return '-';
12
+ return d;
13
+ }
14
+ export default async function descriptionList(data, opt) {
15
+ const { hash } = opt;
16
+
17
+ // no data
18
+ if (hash.nodata && !data) {
19
+ const noDataText = typeof hash.nodata == 'string' ? hash.nodata : 'no data';
20
+ return noDataText
21
+ }
22
+ if (!data) return 'empty data'
23
+ if (!hash.columns.split) return 'columns empty' + JSON.stringify(hash.columns)
24
+ const keys = hash.columns.split(hash.divider || '|').map(el => hash.comma ? el.trim().replace(new RegExp(hash.comma || '#', 'g'), ',') : el.trim());
25
+
26
+ const result = [];
27
+
28
+ for (let i = 0; i < keys.length; i += 2) {
29
+ const name = keys[i];
30
+ const nameHBS = name.includes('{{') ? handlebarsSync.compile(name)({ ...data, hash }) : false
31
+
32
+ if (!nameHBS && name.includes('{{')) continue;
33
+
34
+ const key = keys[i + 1];
35
+ const d1 = ['{{format', '{{select', '{{badge', '{{ifCond'].filter(el => key?.includes?.(el)).length ? await handlebars.compile(key)(data) || '-' : null
36
+ if (d1 || data[key]) {
37
+ result.push(`<div class="flex py-[16px] text-[14px] border-b border-[#E5E7EB">
38
+ <div class="text-[#6B7280] dark:text-white font-[600] w-[50%]">${nameHBS || name}</div>
39
+ <div class="font-[400] text-black dark:text-[#bdbdbd] w-[50%]">${d1 || format(data[key], key, data)}</div>
40
+ </div>
41
+ `);
42
+ }
43
+
44
+ }
45
+ return '<div class="w-full">' + result.join('') + '</div>';
46
+ }