@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
@@ -1,5 +1,5 @@
1
1
  import { openBlock as Nt, createElementBlock as jt, createStaticVNode as Gp, resolveComponent as Uo, createElementVNode as ct, createVNode as $o, toDisplayString as Ln, createCommentVNode as jo, Fragment as oh, renderList as lh, normalizeStyle as Hu, normalizeClass as ch, withDirectives as Hp, vShow as Xp } from "vue";
2
- import { _ as en, j as Wp, V as Kp } from "./import-file-C8BY90-b.js";
2
+ import { _ as en, j as Wp, V as Kp } from "./import-file-r0dN1aVl.js";
3
3
  function Jp(te) {
4
4
  return [
5
5
  {
package/dist/style.css CHANGED
@@ -1 +1 @@
1
- .custom-x-scrollbar{overflow-x:auto;overflow-y:clip}.custom-x-scrollbar::-webkit-scrollbar{height:6px}.custom-x-scrollbar::-webkit-scrollbar-thumb{background-color:#e0e0e0}.custom-x-scrollbar::-webkit-scrollbar-track{background-color:#f1f1f1}.ui-dialog__wrapper[data-v-e51369e3]{position:relative}.ui-dialog__modal[data-v-e51369e3]{margin:10px;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);min-height:0;height:fit-content;max-height:80vh;overflow:hidden}.ui-dialog__content[data-v-e51369e3]{min-height:0;height:100%;overflow:auto}.ui-dialog__content[data-v-e51369e3]::-webkit-scrollbar{width:6px;height:6px;background-color:#f5f5f5}.ui-dialog__content[data-v-e51369e3]::-webkit-scrollbar-thumb{border-radius:10px;background-color:#d9d9d9bf}.ui-dialog__content[data-v-e51369e3]::-webkit-scrollbar-track{background-color:#f5f5f5}@media (max-width: 650px){.ui-dialog__modal[data-v-e51369e3]{top:50%}}.fade-enter-active[data-v-e51369e3],.fade-leave-active[data-v-e51369e3]{transition:opacity .2s}.fade-enter-from[data-v-e51369e3],.fade-leave-to[data-v-e51369e3]{opacity:0}.content-enter-active[data-v-e51369e3],.content-leave-active[data-v-e51369e3]{transition:transform .4s}.content-enter-from[data-v-e51369e3],.content-leave-to[data-v-e51369e3]{transform:translate(-50%,-50%) scale(.95)}.custom-scrollbar{overflow-y:auto;overflow-x:clip}.custom-scrollbar::-webkit-scrollbar{width:6px;margin-right:0}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#e0e0e0;border-radius:10px}.custom-scrollbar::-webkit-scrollbar-track{background-color:#f1f1f1}.vs-chart{width:100%;height:100%}.vs-chart-tooltip{border-radius:7px;max-height:100px;max-width:340px;min-width:140px;background-color:#fff;box-shadow:0 0 8px #0000002e}.vs-chart-tooltip__head{padding:5px;border-bottom:1px solid #eee;margin-bottom:5px}.vs-chart-tooltip__head-title{font-size:14px;font-weight:700;display:flex;align-items:center}.vs-chart-tooltip__head-value{margin-right:5px}.vs-chart-tooltip__head-series{font-size:14px;display:flex;align-items:center}.vs-chart-tooltip__color{width:12px;height:12px;display:block;border-radius:50%;margin-right:5px}.vs-chart-tooltip__body{padding:5px;font-size:14px;display:flex;align-items:start;flex-direction:column}.vs-chart-tooltip__body .vs-chart-tooltip__color{border-radius:3px}.vs-chart-tooltip__item{width:100%;display:flex;align-items:center;justify-content:space-between}.vs-chart-tooltip__text{margin-right:auto}.vs-chart-tooltip__body svg{margin-right:8px}body{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.vs-map-portal__legend[data-v-16c83b8b]{position:absolute;bottom:10px;right:45px}.vs-map-portal__legend-body[data-v-16c83b8b]{background-color:#fff;border-radius:12px;border:1px solid #dcdfe5;width:250px;padding:16px;max-height:250px}.map__settings{display:flex;padding:3px 1px;background:#fff;position:absolute;right:0;bottom:15px;height:30px;align-items:center;font-weight:100;text-align:center}.map__settings-item{display:flex;white-space:nowrap;padding:0 2px;font-size:10px;font-weight:400}.map__settings-attribution{padding:0}.map__settings-menu{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:15px!important;position:absolute;background-color:#fff;bottom:60px;right:90px;width:260px}.map__settings-menu-item{display:flex;align-items:center;color:#2c2c2c;margin-bottom:5px;font-size:12px;justify-content:space-between;height:28px}.map__settings-menu-item-text{height:28px;line-height:28px;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #dcdfe6;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;display:inline-block;font-size:12px;height:40px;line-height:40px;outline:0;padding:0 10px}.map__settings-menu-item--checktext{color:#000!important;font-size:12px;display:inline-block;padding-left:10px;line-height:19px;cursor:pointer}.checked{color:#409eff!important}.vs-map-slot-layers[data-v-aebbde6b]{position:absolute;display:flex;bottom:10px;left:15px}.vs-map-slot-layers__layer[data-v-aebbde6b]{width:50px;height:50px;background-color:#fff;box-shadow:0 0 7px #434c5626;padding:5px;border-radius:5px;margin-top:10px;margin-right:10px;cursor:pointer;transition:all .3s}.vs-map-slot-layers__layer[data-v-aebbde6b]:hover{background-color:#eee}.vs-map-slot-layers__image[data-v-aebbde6b]{width:100%;border-radius:5px;object-fit:contain}.focused[data-v-aebbde6b]{background-color:#1989fa!important}.vs-map-portal__list{position:absolute}.vs-map-portal__legend-title{padding-bottom:5px;border-bottom:1px solid #ebebeb}.vs-map-portal__legend-button{cursor:pointer;display:flex;align-items:center;justify-content:center;flex-direction:column;height:35px;background-color:#fff;border-radius:5px;border:1px solid #dcdfe5;padding:5px}.vs-map-portal__legend-body{background-color:#fff;border-radius:5px;border:1px solid #dcdfe5;padding:5px;width:250px;max-height:250px}.vs-map-portal__legend-body__content{padding-top:10px}.vs-map-portal__legend-category{font-weight:700;margin-bottom:5px}.vs-map-portal__legend-item{margin-right:10px;display:flex;flex-shrink:0}.vs-map-portal__legend-item__color{width:16px;height:16px;border-radius:3px;margin-right:10px;display:flex;flex-shrink:0}.maplibregl-popup-content{padding:0}
1
+ @font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2) format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2) format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2) format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2JL7SUc.woff2) format("woff2");unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa0ZL7SUc.woff2) format("woff2");unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2) format("woff2");unicode-range:U+1F00-1FFF}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2) format("woff2");unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2) format("woff2");unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2) format("woff2");unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter;font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}body{font-family:Inter,Segoe UI,Helvetica Neue,Roboto,sans-serif;font-weight:400}h1{font-family:Inter,Segoe UI,Helvetica Neue,Roboto,sans-serif;font-weight:700}em{font-family:Inter,Segoe UI,Helvetica Neue,Roboto,sans-serif;font-style:italic}.custom-x-scrollbar{overflow-x:auto;overflow-y:clip}.custom-x-scrollbar::-webkit-scrollbar{height:6px}.custom-x-scrollbar::-webkit-scrollbar-thumb{background-color:#e0e0e0}.custom-x-scrollbar::-webkit-scrollbar-track{background-color:#f1f1f1}.ui-dialog__wrapper[data-v-e51369e3]{position:relative}.ui-dialog__modal[data-v-e51369e3]{margin:10px;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);min-height:0;height:fit-content;max-height:80vh;overflow:hidden}.ui-dialog__content[data-v-e51369e3]{min-height:0;height:100%;overflow:auto}.ui-dialog__content[data-v-e51369e3]::-webkit-scrollbar{width:6px;height:6px;background-color:#f5f5f5}.ui-dialog__content[data-v-e51369e3]::-webkit-scrollbar-thumb{border-radius:10px;background-color:#d9d9d9bf}.ui-dialog__content[data-v-e51369e3]::-webkit-scrollbar-track{background-color:#f5f5f5}@media (max-width: 650px){.ui-dialog__modal[data-v-e51369e3]{top:50%}}.fade-enter-active[data-v-e51369e3],.fade-leave-active[data-v-e51369e3]{transition:opacity .2s}.fade-enter-from[data-v-e51369e3],.fade-leave-to[data-v-e51369e3]{opacity:0}.content-enter-active[data-v-e51369e3],.content-leave-active[data-v-e51369e3]{transition:transform .4s}.content-enter-from[data-v-e51369e3],.content-leave-to[data-v-e51369e3]{transform:translate(-50%,-50%) scale(.95)}.custom-scrollbar{overflow-y:auto;overflow-x:clip}.custom-scrollbar::-webkit-scrollbar{width:6px;margin-right:0}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#e0e0e0;border-radius:10px}.custom-scrollbar::-webkit-scrollbar-track{background-color:#f1f1f1}.vs-chart{width:100%;height:100%}.vs-chart-tooltip{border-radius:7px;max-height:100px;max-width:340px;min-width:140px;background-color:#fff;box-shadow:0 0 8px #0000002e}.vs-chart-tooltip__head{padding:5px;border-bottom:1px solid #eee;margin-bottom:5px}.vs-chart-tooltip__head-title{font-size:14px;font-weight:700;display:flex;align-items:center}.vs-chart-tooltip__head-value{margin-right:5px}.vs-chart-tooltip__head-series{font-size:14px;display:flex;align-items:center}.vs-chart-tooltip__color{width:12px;height:12px;display:block;border-radius:50%;margin-right:5px}.vs-chart-tooltip__body{padding:5px;font-size:14px;display:flex;align-items:start;flex-direction:column}.vs-chart-tooltip__body .vs-chart-tooltip__color{border-radius:3px}.vs-chart-tooltip__item{width:100%;display:flex;align-items:center;justify-content:space-between}.vs-chart-tooltip__text{margin-right:auto}.vs-chart-tooltip__body svg{margin-right:8px}body{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.label[data-v-1a2408da]{margin-left:4px;font-size:16px}.vs-map-portal__legend[data-v-16c83b8b]{position:absolute;bottom:10px;right:45px}.vs-map-portal__legend-body[data-v-16c83b8b]{background-color:#fff;border-radius:12px;border:1px solid #dcdfe5;width:250px;padding:16px;max-height:250px}.map__settings{display:flex;padding:3px 1px;background:#fff;position:absolute;right:0;bottom:15px;height:30px;align-items:center;font-weight:100;text-align:center}.map__settings-item{display:flex;white-space:nowrap;padding:0 2px;font-size:10px;font-weight:400}.map__settings-attribution{padding:0}.map__settings-menu{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:15px!important;position:absolute;background-color:#fff;bottom:60px;right:90px;width:260px}.map__settings-menu-item{display:flex;align-items:center;color:#2c2c2c;margin-bottom:5px;font-size:12px;justify-content:space-between;height:28px}.map__settings-menu-item-text{height:28px;line-height:28px;background-color:#fff;background-image:none;border-radius:4px;border:1px solid #dcdfe6;-webkit-box-sizing:border-box;box-sizing:border-box;color:#606266;display:inline-block;font-size:12px;height:40px;line-height:40px;outline:0;padding:0 10px}.map__settings-menu-item--checktext{color:#000!important;font-size:12px;display:inline-block;padding-left:10px;line-height:19px;cursor:pointer}.checked{color:#409eff!important}.vs-map-slot-layers[data-v-aebbde6b]{position:absolute;display:flex;bottom:10px;left:15px}.vs-map-slot-layers__layer[data-v-aebbde6b]{width:50px;height:50px;background-color:#fff;box-shadow:0 0 7px #434c5626;padding:5px;border-radius:5px;margin-top:10px;margin-right:10px;cursor:pointer;transition:all .3s}.vs-map-slot-layers__layer[data-v-aebbde6b]:hover{background-color:#eee}.vs-map-slot-layers__image[data-v-aebbde6b]{width:100%;border-radius:5px;object-fit:contain}.focused[data-v-aebbde6b]{background-color:#1989fa!important}.vs-map-portal__list{position:absolute}.vs-map-portal__legend-title{padding-bottom:5px;border-bottom:1px solid #ebebeb}.vs-map-portal__legend-button{cursor:pointer;display:flex;align-items:center;justify-content:center;flex-direction:column;height:35px;background-color:#fff;border-radius:5px;border:1px solid #dcdfe5;padding:5px}.vs-map-portal__legend-body{background-color:#fff;border-radius:5px;border:1px solid #dcdfe5;padding:5px;width:250px;max-height:250px}.vs-map-portal__legend-body__content{padding-top:10px}.vs-map-portal__legend-category{font-weight:700;margin-bottom:5px}.vs-map-portal__legend-item{margin-right:10px;display:flex;flex-shrink:0}.vs-map-portal__legend-item__color{width:16px;height:16px;border-radius:3px;margin-right:10px;display:flex;flex-shrink:0}.maplibregl-popup-content{padding:0}
@@ -1,4 +1,4 @@
1
- import { _ as l, c, i as h, t as p, h as d } from "./import-file-C8BY90-b.js";
1
+ import { _ as l, c, i as h, t as p, h as d } from "./import-file-r0dN1aVl.js";
2
2
  import { openBlock as u, createElementBlock as m } from "vue";
3
3
  const D = {
4
4
  name: "VsCalendar",
@@ -1,4 +1,4 @@
1
- import { _ as o, c, a as h, b as l, d as n, i as d } from "./import-file-C8BY90-b.js";
1
+ import { _ as o, c, a as h, b as l, d as n, i as d } from "./import-file-r0dN1aVl.js";
2
2
  import { openBlock as p, createElementBlock as u } from "vue";
3
3
  const m = {
4
4
  name: "VsFunnelBar",
@@ -1,4 +1,4 @@
1
- import { _ as u, c as m, a as d, i as f, t as x, e as y, g as c, d as D } from "./import-file-C8BY90-b.js";
1
+ import { _ as u, c as m, a as d, i as f, t as x, e as y, g as c, d as D } from "./import-file-r0dN1aVl.js";
2
2
  import { openBlock as _, createElementBlock as g } from "vue";
3
3
  const w = {
4
4
  name: "VsHeatmap",
@@ -1,5 +1,5 @@
1
- import { l as g, m as M } from "./map-component-mixin-CFtShPun.js";
2
- import { _ as w, c as y } from "./import-file-C8BY90-b.js";
1
+ import { l as g, m as M } from "./map-component-mixin-DU9YFNY4.js";
2
+ import { _ as w, c as y } from "./import-file-r0dN1aVl.js";
3
3
  import { resolveComponent as i, openBlock as u, createElementBlock as _, createElementVNode as r, createBlock as V, createCommentVNode as v, createVNode as d } from "vue";
4
4
  const x = {
5
5
  mixins: [y, g, M],
@@ -1,5 +1,5 @@
1
- import { l as w, m as V, p as _ } from "./map-component-mixin-CFtShPun.js";
2
- import { _ as v, c as C } from "./import-file-C8BY90-b.js";
1
+ import { l as w, m as V, p as _ } from "./map-component-mixin-DU9YFNY4.js";
2
+ import { _ as v, c as C } from "./import-file-r0dN1aVl.js";
3
3
  import { resolveComponent as a, openBlock as h, createElementBlock as $, createVNode as s, createElementVNode as u, createBlock as x, createCommentVNode as L } from "vue";
4
4
  const k = {
5
5
  mixins: [C, w, V],
@@ -1,4 +1,4 @@
1
- import { _ as c, c as o, f as n } from "./import-file-C8BY90-b.js";
1
+ import { _ as c, c as o, f as n } from "./import-file-r0dN1aVl.js";
2
2
  import { openBlock as i, createElementBlock as m, toDisplayString as s } from "vue";
3
3
  const u = {
4
4
  name: "VsNumber",
@@ -0,0 +1,68 @@
1
+ import { _ as h, c as m, a as p, b as f } from "./import-file-r0dN1aVl.js";
2
+ import { openBlock as a, createElementBlock as r, createElementVNode as s, Fragment as n, renderList as c, toDisplayString as d } from "vue";
3
+ const _ = {
4
+ name: "VsTable",
5
+ mixins: [m],
6
+ data() {
7
+ return {
8
+ values: null,
9
+ xs: null,
10
+ products: null
11
+ };
12
+ },
13
+ async mounted() {
14
+ await this.getData(), this.prepareData();
15
+ },
16
+ methods: {
17
+ onChangedData() {
18
+ try {
19
+ this.sourceData && this.prepareData();
20
+ } catch {
21
+ }
22
+ },
23
+ prepareData() {
24
+ try {
25
+ const t = Array.from(
26
+ new Set(this.sourceData.map((e) => e[this.dimensions[0]]))
27
+ );
28
+ this.dimensions[0].includes("date") ? this.xs = t.map((e) => p(e)) : this.xs = [...t], this.products = this.dimensions.filter((e, o) => o != 0), this.values = this.products.map((e, o) => ({
29
+ name: e,
30
+ data: this.sourceData.filter((l) => parseFloat(l[e])).map((l) => f(parseFloat(l[e])))
31
+ }));
32
+ } catch (t) {
33
+ console.error(t);
34
+ }
35
+ }
36
+ }
37
+ }, b = { class: "flex flex-col h-full p-5 space-y-4 rounded-xl custom-scrollbar" }, x = { class: "overflow-x-auto [&::-webkit-scrollbar]:h-2 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-track]:bg-gray-100 [&::-webkit-scrollbar-thumb]:bg-gray-300 custom-scrollbar" }, y = { class: "inline-block min-w-full align-middle" }, g = { class: "relative min-w-full divide-y divide-gray-200" }, v = { class: "sticky top-0 bg-white w-full after:absolute after:content-[''] after:block after:w-full after:h-px after:bg-stone-200" }, w = { class: "space-x-2" }, k = {
38
+ scope: "col",
39
+ class: "xl:min-w-[120px] min-w-48"
40
+ }, D = { class: "flex items-center px-1 py-3 font-medium text-gray-800 text-start gap-x-1 dark:text-neutral-200" }, $ = { class: "divide-y divide-gray-200" }, F = { class: "py-3 size-px whitespace-nowrap" }, T = { class: "text-sm text-gray-600" };
41
+ function B(t, e, o, l, E, N) {
42
+ return a(), r("div", b, [
43
+ s("div", x, [
44
+ s("div", y, [
45
+ s("table", g, [
46
+ s("thead", v, [
47
+ s("tr", w, [
48
+ (a(!0), r(n, null, c(t.dimensions, (i) => (a(), r("th", k, [
49
+ s("div", D, d(i), 1)
50
+ ]))), 256))
51
+ ])
52
+ ]),
53
+ (a(!0), r(n, null, c(t.sourceData, (i) => (a(), r("tbody", $, [
54
+ s("tr", null, [
55
+ (a(!0), r(n, null, c(t.dimensions, (u, S) => (a(), r("td", F, [
56
+ s("span", T, d(i == null ? void 0 : i[u]), 1)
57
+ ]))), 256))
58
+ ])
59
+ ]))), 256))
60
+ ])
61
+ ])
62
+ ])
63
+ ]);
64
+ }
65
+ const A = /* @__PURE__ */ h(_, [["render", B]]);
66
+ export {
67
+ A as default
68
+ };
@@ -1,7 +1,7 @@
1
1
  var ge = Object.defineProperty;
2
2
  var ke = (c, e, t) => e in c ? ge(c, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : c[e] = t;
3
3
  var k = (c, e, t) => ke(c, typeof e != "symbol" ? e + "" : e, t);
4
- import { _ as de, c as xe } from "./import-file-C8BY90-b.js";
4
+ import { _ as de, c as xe } from "./import-file-r0dN1aVl.js";
5
5
  import { openBlock as V, createElementBlock as J, createCommentVNode as be } from "vue";
6
6
  function Q() {
7
7
  return {
@@ -1508,7 +1508,7 @@ const Ke = {
1508
1508
  return null;
1509
1509
  }
1510
1510
  }
1511
- }, Ye = { class: "relative select-auto h-full py-4 rounded-xl p-4 box-border bg-white custom-scrollbar" }, et = ["innerHTML"];
1511
+ }, Ye = { class: "relative select-auto h-[calc(100%-40px)] py-4 rounded-xl p-4 box-border bg-white custom-scrollbar" }, et = ["innerHTML"];
1512
1512
  function tt(c, e, t, n, s, i) {
1513
1513
  return V(), J("div", Ye, [
1514
1514
  s.markedText ? (V(), J("div", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/bi",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "BI data visualization module",
5
5
  "main": "dist/bi.js",
6
6
  "browser": "dist/bi.umd.cjs",
@@ -47,10 +47,10 @@
47
47
  "dependencies": {
48
48
  "@highlightjs/vue-plugin": "github:highlightjs/vue-plugin",
49
49
  "@mapbox/sphericalmercator": "^1.2.0",
50
- "@opengis/fastify-auth": "^1.0.50",
50
+ "@opengis/fastify-auth": "^1.0.60",
51
51
  "@opengis/fastify-file": "^1.0.32",
52
- "@opengis/fastify-table": "^1.1.93",
53
- "@opengis/v3-core": "^0.3.8",
52
+ "@opengis/fastify-table": "^1.1.145",
53
+ "@opengis/v3-core": "^0.3.31",
54
54
  "@opengis/v3-filter": "^0.0.39",
55
55
  "@turf/turf": "^7.1.0",
56
56
  "@vueuse/core": "^11.1.0",
@@ -63,6 +63,7 @@
63
63
  "highlight.js": "^11.10.0",
64
64
  "js-yaml": "^4.1.0",
65
65
  "maplibre-gl": "^4.7.1",
66
+ "markdown-it": "^14.1.0",
66
67
  "marked": "^14.1.2",
67
68
  "pinia": "^2.2.4",
68
69
  "vite": "^5.1.5",
package/plugin.js CHANGED
@@ -6,7 +6,7 @@ async function plugin(fastify, opts = config) {
6
6
  // API
7
7
  fastify.register(import('./server/routes/dashboard/index.mjs'), config);
8
8
  fastify.register(import('./server/routes/dataset/index.mjs'), config);
9
- fastify.register(import('./server/routes/db/index.mjs'), config);
9
+
10
10
  fastify.register(import('./server/routes/data/index.mjs'), config);
11
11
  fastify.register(import('./server/routes/edit/index.mjs'), config);
12
12
  }
@@ -0,0 +1,17 @@
1
+ import md from 'markdown-it';
2
+
3
+ const md1 = md({ html: true });
4
+
5
+ /**
6
+ * Перетворення з файла readme.md до формату HTML.
7
+ * Потрабно вставити в хелпер шлях до файла або текст readme.md і за допомогою бібліотеки markdown-it перетвориться в HTML.
8
+
9
+ * @returns {String} Returns HTML
10
+ */
11
+ export default function mdToHTML(data, options) {
12
+ // auto detect HTML or MD
13
+ // const result = md().render(data);
14
+ if (!data) return 'empty data';
15
+ const result = md1.render(data);
16
+ return result;
17
+ };
@@ -21,6 +21,18 @@ alter table bi.dataset add column if not exists editor_id text;
21
21
  alter table bi.dataset add column if not exists editor_date timestamp without time zone;
22
22
  alter table bi.dataset add column if not exists cdate timestamp without time zone;
23
23
  alter table bi.dataset alter column cdate set DEFAULT (now())::timestamp without time zone;
24
+ ALTER TABLE bi.dataset add column if not exists filter_list json;
25
+ ALTER TABLE bi.dataset add column if not exists dataset_file_path text;
26
+ ALTER TABLE bi.dataset add column if not exists files json;
27
+ ALTER TABLE bi.dataset add column if not exists style json;
28
+ ALTER TABLE bi.dataset add column if not exists data_source json;
29
+ ALTER TABLE bi.dataset add column if not exists setting json;
30
+ ALTER TABLE bi.dataset add column if not exists geom geometry;
31
+ ALTER TABLE bi.dataset add column if not exists pk text;
32
+ ALTER TABLE bi.dataset add column if not exists query text;
33
+ ALTER TABLE bi.dataset add column if not exists form_setting json;
34
+ ALTER TABLE bi.dataset add column if not exists access_level text;
35
+ ALTER TABLE bi.dataset add column if not exists export_columns text[];
24
36
 
25
37
  alter table bi.dataset add CONSTRAINT bi_dataset_id_pkey PRIMARY KEY (dataset_id);
26
38
 
@@ -30,3 +42,4 @@ COMMENT ON COLUMN bi.dataset.dataset_id IS 'PK';
30
42
  COMMENT ON COLUMN bi.dataset.name IS 'Назва';
31
43
  COMMENT ON COLUMN bi.dataset.file_path IS 'Шлях до імпортованого файлу';
32
44
  COMMENT ON COLUMN bi.dataset.table_name IS 'Таблиця';
45
+
@@ -86,7 +86,7 @@ export default async function dashboard({
86
86
  const options = await getTemplate('cls', el.data);
87
87
  Object.assign(el, { options });
88
88
  } else if (index?.table_name || index?.table) {
89
- const { rows = [] } = await pg.query(`select "${el.id}" as id, count(*) from ${index?.table_name || index?.table} group by "${el.id}"`);
89
+ const { rows = [] } = await pg1.query(`select "${el.id}" as id, count(*) from ${index?.table_name || index?.table} group by "${el.id}"`);
90
90
  Object.assign(el, { options: rows });
91
91
  }
92
92
  }));
@@ -94,7 +94,7 @@ export default async function dashboard({
94
94
  const ranges = index?.filters?.filter((el) => el?.id && columns.map((el) => el?.name).includes(el?.id) && el?.type === 'Range');
95
95
  if (ranges?.length) {
96
96
  await Promise.all(ranges.map(async (el) => {
97
- const rows = await pg.query(`select array[
97
+ const rows = await pg1.query(`select array[
98
98
  percentile_cont(0) within group (order by ${el.id}), percentile_cont(0.25) within group (order by ${el.id}),
99
99
  percentile_cont(0.5) within group (order by ${el.id}), percentile_cont(0.75) within group (order by ${el.id}),
100
100
  percentile_cont(1.0) within group (order by ${el.id})
@@ -118,7 +118,7 @@ export default async function dashboard({
118
118
  }
119
119
  : { name: el[0].split('.')[0], title: el[1] }
120
120
  );
121
-
121
+
122
122
  return {
123
123
  ...data,
124
124
  table_name: table,
@@ -62,7 +62,7 @@ export default async function dataAPI({ query = {} }) {
62
62
 
63
63
  const columnList = columns.map(col => col.name);
64
64
  const groupbyColumnNotExists = groupby?.split?.(',')?.filter?.(el => !columnList.includes(el.trim()));
65
-
65
+
66
66
  if (groupby && groupbyColumnNotExists?.length) {
67
67
  return { message: `groupby column not found: ${groupbyColumnNotExists} (${data.table}/${pg.options?.database})`, status: 404 };
68
68
  }
@@ -97,6 +97,7 @@ export default async function dataAPI({ query = {} }) {
97
97
  const sql = (chartSQL[type] || chartSQL.chart)({
98
98
  where,
99
99
  metric,
100
+ columns: widgetData.columns,
100
101
  table: `(${optimizedSQL} ${samples ? 'limit 10' : ''})q`,
101
102
  x,
102
103
  groupData,
@@ -139,6 +140,12 @@ export default async function dataAPI({ query = {} }) {
139
140
  });
140
141
  }
141
142
 
143
+ const titles = Array.isArray(widgetData?.columns)
144
+ ? widgetData.columns.reduce((acc, curr) => Object.assign(acc, { [curr.name]: curr.title || curr.ua }), {})
145
+ : Object.keys(widgetData?.columns || {}).reduce((acc, curr) => Object.assign(acc, { [curr]: widgetData?.columns?.[curr] }), {});
146
+
147
+ const rows1 = type === 'table' ? rows.map(row => Object.keys(row || {}).reduce((acc, curr) => Object.assign(acc, { [titles?.[curr] || curr]: row?.[curr] }), {})) : rows;
148
+
142
149
  const yml = widgetData.yml || yaml.dump(extractYml(widgetData));
143
150
  const dimensions = fields.map((el) => el.name);
144
151
 
@@ -155,22 +162,14 @@ export default async function dataAPI({ query = {} }) {
155
162
  // data: query.format === 'data' ? dimensions.map(el => rows.map(r => r[el])) : undefined,
156
163
  source:
157
164
  query.format === 'array'
158
- ? dimensions.map((el) => rows.map((r) => r[el]))
159
- : rows,
165
+ ? dimensions.map((el) => rows1.map((r) => r[el]))
166
+ : rows1,
160
167
  style,
161
168
  options,
162
169
  controls,
163
170
  yml,
164
- params: config?.local ? {
165
- x,
166
- cls,
167
- metric,
168
- table,
169
- tableSQL,
170
- where,
171
- groupby,
172
- sql,
173
- } : undefined,
171
+ data: widgetData.data,
172
+ id: query.widget,
174
173
  columnTypes,
175
174
  };
176
175
  return res;
@@ -2,8 +2,11 @@ function number({ metric, where, table, samples }) {
2
2
  const sql = `select ${metric} from ${table} where ${where} ${samples ? 'limit 10' : ''}`;
3
3
  return sql;
4
4
  }
5
- function table({ columns, table, where, samples }) {
6
- return `select ${columns.map((el) => el.name || el)}::text from ${table} where ${where} ${samples ? 'limit 10' : 'limit 20'} `;
5
+ function table({ columns = [], table, where, samples }) {
6
+ const cols = Array.isArray(columns)
7
+ ? columns.map((el) => `"${(el.name || el).replace(/'/g, "''")}"`).join(',')
8
+ : Object.keys(columns).map(key => `"${key.replace(/'/g, "''")}"`).join(',');
9
+ return `select ${cols || '*'} from ${table} where ${where} ${samples ? 'limit 10' : 'limit 20'} `;
7
10
  }
8
11
 
9
12
  function chart({
@@ -39,4 +42,4 @@ function text() {
39
42
  return undefined;
40
43
  }
41
44
 
42
- export default { number, chart };
45
+ export default { number, chart, table };
@@ -17,7 +17,9 @@ const maxLimit = 100;
17
17
  * @returns {Object} rows Масив з колонками таблиці
18
18
  */
19
19
 
20
- export default async function biDatasetList({ pg = pgClients.client, query = {} }) {
20
+ const pg = pgClients.client;
21
+
22
+ export default async function biDatasetList({ query = {} }) {
21
23
 
22
24
  const limit = Math.min(maxLimit, +(query.limit || 20));
23
25
  const offset = query.page && query.page > 0 ? (query.page - 1) * limit : 0;
@@ -0,0 +1,55 @@
1
+ import { pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function datasetEditComment({
4
+ pg = pgClients.client,
5
+ body = {},
6
+ params = {},
7
+ }) {
8
+ if (!params?.id) {
9
+ return { message: 'not enough params: id', status: 400 };
10
+ }
11
+
12
+ if (!Array.isArray(body.data) || !body.data?.length) {
13
+ return { message: 'invalid param: body.data not an array / empty', status: 400 };
14
+ }
15
+
16
+ const dataset = await pg.query('select dataset_id as id, table_name as table from bi.dataset where dataset_id=$1', [params.id])
17
+ .then(el => el.rows?.[0] || {});
18
+
19
+ if (!dataset?.table) {
20
+ return { message: dataset?.id ? 'dataset table not set' : 'dataset not found', status: 404 };
21
+ }
22
+
23
+ const { fields = [] } = pg.pk?.[dataset.table] ? await pg.query(`select * from ${dataset.table} limit 0`) : {};
24
+
25
+ if (!fields.length) {
26
+ return { message: `table not found: ${dataset.table}`, status: 404 };
27
+ }
28
+
29
+ const columnList = fields.map(el => el.name);
30
+ const validData = body.data?.filter?.(el => el?.name && el?.comment && columnList.includes(el.name));
31
+
32
+ const q1 = validData.map(el => `COMMENT ON COLUMN ${dataset.table}."${el.name.replace(/'/g, "''")}" IS '${el.comment.replace(/'/g, "''")}'`).join(';');
33
+ if (q1) await pg.query(q1);
34
+
35
+ const q2 = `select
36
+ attrelid::regclass,
37
+ attname,
38
+ pg_catalog.col_description(attrelid,attnum) as title
39
+ from pg_catalog.pg_attribute a
40
+ where attnum > 0
41
+ and attname not in ('editor_id','editor_date','cdate','geom','id','uid','cdate')
42
+ and atttypid::regtype not in ('json','geometry')
43
+ and pg_catalog.col_description(attrelid,attnum) is not null`;
44
+
45
+ const { rows = [] } = await pg.query(q2);
46
+
47
+ const title = rows
48
+ .filter(el => el.attrelid === dataset.table)
49
+ .reduce((p, el) => ({ ...p, [el.attname]: el.title }), {});
50
+
51
+ const res = await pg.query(`update bi.dataset set setting=coalesce(setting::jsonb, '{}'::jsonb)||$1::jsonb
52
+ where dataset_id=$2 returning dataset_id as id, setting`, [{ title }, dataset.id]);
53
+
54
+ return res.rows?.[0];
55
+ }
@@ -0,0 +1,132 @@
1
+ import path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+
4
+ import { config, pgClients, getFolder } from '@opengis/fastify-table/utils.js';
5
+ import { file2json } from '@opengis/fastify-file/utils.js';
6
+
7
+ import createTableQuery from '../utils/createTableQuery.js';
8
+ import executeQuery from '../utils/executeQuery.js';
9
+ import downloadRemoteFile from '../utils/downloadRemoteFile.js';
10
+
11
+ const insertDataset = `insert into bi.dataset
12
+ (name, table_name, dataset_file_path, column_list, pk, data_source, uid)
13
+ values($1,$2,$3,$4,$5,$6,$7) returning dataset_id`;
14
+
15
+ export default async function createDatasetPost({
16
+ pg = pgClients.client, body = {}, user = {},
17
+ }) {
18
+ if (!user?.uid) {
19
+ return { message: 'access restricted', status: 403 };
20
+ }
21
+
22
+ if (!body?.name) {
23
+ return { message: 'not enough query params: name', status: 400 };
24
+ }
25
+
26
+ if (!body?.table_name && !body?.file && !body?.column_list?.length && !body?.dataset_url) {
27
+ return { message: 'not enough query params: table / file / column_list/ url', status: 400 };
28
+ }
29
+
30
+ const {
31
+ name,
32
+ table_name: existingTable,
33
+ column_list: columns = [],
34
+ dataset_url: datasetUrl,
35
+ } = body;
36
+
37
+ const rootDir = getFolder(config, 'local');
38
+
39
+ if (datasetUrl) {
40
+ const { filePath, error } = await downloadRemoteFile({
41
+ rootDir, url: datasetUrl, table: name || datasetUrl,
42
+ });
43
+ if (error || !filePath) {
44
+ return { message: error || 'file request URL error', status: 500 };
45
+ }
46
+ Object.assign(body, { file: filePath });
47
+ }
48
+
49
+ const { file: relPath } = body;
50
+
51
+ if (relPath) {
52
+ const filepath = path.join(rootDir, relPath);
53
+ const exists = existsSync(filepath);
54
+
55
+ if (!exists) {
56
+ return { message: 'Файл з вихідними даними не знайдено', status: 404 };
57
+ }
58
+
59
+ const json = await file2json({ filepath });
60
+
61
+ // excel sheets fix?
62
+ const data1 = (['.xls', '.xlsx'].includes(path.extname(filepath)) && !Array.isArray(json)) ? json[Object.keys(json)[0]] : json;
63
+ const data = path.extname(filepath) === '.json' && body?.key ? data1?.[body.key] : data1;
64
+
65
+ const features = ['.csv', '.xlsx', '.xls'].includes(path.extname(filepath))
66
+ ? data?.map?.((el) => ({ type: 'Feature', properties: Object.keys(el).reduce((acc, curr) => Object.assign(acc, { [curr]: el[curr] }), {}) }))
67
+ : data?.features || data?.map?.((el) => ({ type: 'Feature', geometry: el.geom, properties: Object.keys(el).filter((key) => key !== 'geom').reduce((acc, curr) => Object.assign(acc, { [curr]: el[curr] }), {}) }));
68
+
69
+ if (!Array.isArray(features) || !features?.length) {
70
+ return { message: 'Файл з вихідними даними порожній', status: 400 };
71
+ }
72
+
73
+ Object.assign(data, { features });
74
+
75
+ const fileColumns = Object.keys(data?.features[0]?.properties)
76
+ ?.filter((el) => !['editor_date', 'cdate', 'uid', 'editor_id', 'files'].includes(el.toLowerCase()))
77
+ ?.map((el) => ({ title: el, format: 'text' }));
78
+
79
+ const { sql, pkey, table } = createTableQuery(fileColumns, name);
80
+
81
+ const { datasetId, error } = await executeQuery({
82
+ sql,
83
+ data,
84
+ name,
85
+ table,
86
+ relPath,
87
+ columns: fileColumns,
88
+ pkey,
89
+ source: datasetUrl ? 'url' : 'file',
90
+ url: datasetUrl,
91
+ user,
92
+ dataKey: body?.key,
93
+ });
94
+
95
+ if (error) return { error, status: 500 };
96
+
97
+ pgClients.client.pk[table] = pkey;
98
+ pgClients.client.tlist.push(table);
99
+ return { message: { id: datasetId, table, source: datasetUrl ? 'url' : 'file' }, status: 200 };
100
+ }
101
+
102
+ if (existingTable) {
103
+ await pg.query(`alter table ${existingTable} add column if not exists geom public.geometry;
104
+ alter table ${existingTable} add column if not exists files json`);
105
+ const args = [name, existingTable, null, null, pg.pk?.[existingTable], JSON.stringify({ type: 'table' }), user?.uid];
106
+ const datasetId = await pg.query(insertDataset, args).then(el => el.rows?.[0]?.dataset_id);
107
+ return { message: { id: datasetId, table: existingTable, source: 'table' }, status: 200 };
108
+ }
109
+
110
+ if (!columns?.length) {
111
+ return { message: 'У даній заяві відсутні налаштування структури набору даних', status: 400 };
112
+ }
113
+
114
+ const { sql, pkey, table } = createTableQuery(columns, name);
115
+
116
+ const { datasetId, error } = await executeQuery({
117
+ sql,
118
+ name,
119
+ table,
120
+ relPath,
121
+ columns,
122
+ pkey,
123
+ source: 'newtable',
124
+ user,
125
+ });
126
+
127
+ if (error) return { error, status: 500 };
128
+
129
+ pgClients.client.pk[table] = pkey;
130
+ pgClients.client.tlist.push(table);
131
+ return { message: { id: datasetId, table, source: 'newtable' }, status: 200 };
132
+ }