@opengis/cms 0.0.2 → 0.0.3

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 (144) hide show
  1. package/.gitlab-ci.yml +36 -0
  2. package/config.example +21 -0
  3. package/docs/.vitepress/abbr.mjs +26 -0
  4. package/docs/.vitepress/config.mjs +119 -0
  5. package/docs/.vitepress/navigation.mjs +82 -0
  6. package/docs/.vitepress/theme/Layout.vue +17 -0
  7. package/docs/.vitepress/theme/components/NavigationLinks.vue +102 -0
  8. package/docs/.vitepress/theme/components/Panzoom.vue +169 -0
  9. package/docs/.vitepress/theme/index.mjs +15 -0
  10. package/docs/.vitepress/theme/style.scss +136 -0
  11. package/docs/abbr.json +4 -0
  12. package/docs/api/builder/cms.builder.delete.md +65 -0
  13. package/docs/api/builder/cms.builder.get.md +70 -0
  14. package/docs/api/builder/cms.builder.list.md +98 -0
  15. package/docs/api/builder/cms.builder.post.md +72 -0
  16. package/docs/api/builder/cms.builder.put.md +88 -0
  17. package/docs/api/category/cms.category.delete.md +60 -0
  18. package/docs/api/category/cms.category.get.md +61 -0
  19. package/docs/api/category/cms.category.list.md +77 -0
  20. package/docs/api/category/cms.category.post.md +62 -0
  21. package/docs/api/category/cms.category.put.md +78 -0
  22. package/docs/api/index.md +50 -0
  23. package/docs/api/manager/cms.manager.delete.md +64 -0
  24. package/docs/api/manager/cms.manager.get.md +72 -0
  25. package/docs/api/manager/cms.manager.list.md +96 -0
  26. package/docs/api/manager/cms.manager.post.md +70 -0
  27. package/docs/api/manager/cms.manager.put.md +86 -0
  28. package/docs/api/media/del.md +64 -0
  29. package/docs/api/media/edit.md +92 -0
  30. package/docs/api/media/list.md +70 -0
  31. package/docs/api/media/metadata.md +57 -0
  32. package/docs/api/media/preview.md +33 -0
  33. package/docs/api/media/upload.md +84 -0
  34. package/docs/db/erd.md +173 -0
  35. package/docs/db/index.md +7 -0
  36. package/docs/index.md +39 -0
  37. package/docs/public/logo-dark.svg +24 -0
  38. package/docs/public/logo-light.svg +24 -0
  39. package/docs/public/logo-short.svg +15 -0
  40. package/docs/public/logo.svg +19 -0
  41. package/docs/readme/index.md +6 -0
  42. package/docs/src/vs-button.vue +157 -0
  43. package/docs/vue/basic/button.md +144 -0
  44. package/docs/vue/index.md +9 -0
  45. package/index.html +14 -0
  46. package/package.json +2 -5
  47. package/server/app.js +25 -0
  48. package/server/config.js +5 -0
  49. package/server/index.js +23 -0
  50. package/server/migrations/media.sql +30 -0
  51. package/server/plugins/hook.js +91 -0
  52. package/server/plugins/vite.js +80 -0
  53. package/server/routes/builder/controllers/cms.builder.delete.js +21 -0
  54. package/server/routes/builder/controllers/cms.builder.get.js +17 -0
  55. package/server/routes/builder/controllers/cms.builder.list.js +16 -0
  56. package/server/routes/builder/controllers/cms.builder.post.js +21 -0
  57. package/server/routes/builder/controllers/cms.builder.put.js +23 -0
  58. package/server/routes/builder/index.mjs +22 -0
  59. package/server/routes/category/controllers/cms.category.delete.js +21 -0
  60. package/server/routes/category/controllers/cms.category.get.js +17 -0
  61. package/server/routes/category/controllers/cms.category.list.js +16 -0
  62. package/server/routes/category/controllers/cms.category.post.js +21 -0
  63. package/server/routes/category/controllers/cms.category.put.js +23 -0
  64. package/server/routes/category/index.mjs +22 -0
  65. package/server/routes/manager/controllers/cms.manager.delete.js +22 -0
  66. package/server/routes/manager/controllers/cms.manager.get.js +21 -0
  67. package/server/routes/manager/controllers/cms.manager.list.js +31 -0
  68. package/server/routes/manager/controllers/cms.manager.post.js +28 -0
  69. package/server/routes/manager/controllers/cms.manager.put.js +23 -0
  70. package/server/routes/manager/index.mjs +22 -0
  71. package/server/routes/media/controllers/delete.js +59 -0
  72. package/server/routes/media/controllers/edit.js +94 -0
  73. package/server/routes/media/controllers/list.js +74 -0
  74. package/server/routes/media/controllers/metadata.js +51 -0
  75. package/server/routes/media/controllers/preview.js +47 -0
  76. package/server/routes/media/controllers/upload.js +79 -0
  77. package/server/routes/media/index.mjs +16 -0
  78. package/server/routes/root.mjs +15 -0
  79. package/server/templates/cls/cms.category_type.json +10 -0
  80. package/server/templates/cls/cms.content_review_status.json +10 -0
  81. package/server/templates/cls/cms.content_status.json +10 -0
  82. package/server/templates/cls/cms.content_type.json +10 -0
  83. package/server/templates/cls/cms.lang.json +10 -0
  84. package/server/templates/page/login.html +59 -0
  85. package/server/templates/select/cms.category_id.sql +1 -0
  86. package/server/templates/select/cms.type_id.sql +1 -0
  87. package/src/App.vue +4 -0
  88. package/src/assets/tailwind/tailwind.js +62 -0
  89. package/src/assets/vue.svg +1 -0
  90. package/src/components/builder/vs-builder-content.vue +163 -0
  91. package/src/components/builder/vs-builder-menu.vue +142 -0
  92. package/src/components/formats/index.js +8 -0
  93. package/src/components/formats/vs-manager-table-date.vue +29 -0
  94. package/src/components/formats/vs-manager-table-switch.vue +16 -0
  95. package/src/components/icons/icon-actions.vue +24 -0
  96. package/src/components/icons/icon-arrow-left.vue +19 -0
  97. package/src/components/icons/icon-check.vue +23 -0
  98. package/src/components/icons/icon-chewron-right.vue +16 -0
  99. package/src/components/icons/icon-close.vue +22 -0
  100. package/src/components/icons/icon-edit.vue +22 -0
  101. package/src/components/icons/icon-folder.vue +18 -0
  102. package/src/components/icons/icon-folder2.vue +17 -0
  103. package/src/components/icons/icon-home.vue +16 -0
  104. package/src/components/icons/icon-image.vue +18 -0
  105. package/src/components/icons/icon-logo.vue +22 -0
  106. package/src/components/icons/icon-media.vue +22 -0
  107. package/src/components/icons/icon-point.vue +11 -0
  108. package/src/components/icons/icon-search.vue +22 -0
  109. package/src/components/icons/icon-table.vue +22 -0
  110. package/src/components/icons/icon-users.vue +18 -0
  111. package/src/components/icons/icon.plus.vue +18 -0
  112. package/src/components/manager/children/vs-manager-collection-content.vue +55 -0
  113. package/src/components/manager/children/vs-manager-collection-item-content.vue +116 -0
  114. package/src/components/manager/children/vs-manager-single-content.vue +112 -0
  115. package/src/components/manager/manager-table/vs-manager-colection-table-add.vue +84 -0
  116. package/src/components/manager/manager-table/vs-manager-collection-table.vue +59 -0
  117. package/src/components/manager/vs-manager-menu.vue +73 -0
  118. package/src/components/media/Breadcrumb.vue +73 -0
  119. package/src/components/shared-components/vs-not-data.vue +213 -0
  120. package/src/components/vs-main-menu.vue +53 -0
  121. package/src/helpers/debounce.js +10 -0
  122. package/src/helpers/translite.js +19 -0
  123. package/src/main.js +30 -0
  124. package/src/misc/import-file.js +32 -0
  125. package/src/pages/vs-builder.vue +22 -0
  126. package/src/pages/vs-layout.vue +17 -0
  127. package/src/pages/vs-manager.vue +30 -0
  128. package/src/pages/vs-media.vue +398 -0
  129. package/src/router/router.js +9 -0
  130. package/src/router/routes.config.js +40 -0
  131. package/src/style.css +0 -0
  132. package/src/templates/form-columns.js +70 -0
  133. package/src/templates/form-template.js +22 -0
  134. package/test/config.js +17 -0
  135. package/test/files/eye.svg +4 -0
  136. package/test/helper.js +30 -0
  137. package/test/routes/builder.test.js +99 -0
  138. package/test/routes/category.test.js +97 -0
  139. package/test/routes/manager.test.js +103 -0
  140. package/test/routes/media.test.js +252 -0
  141. package/vite.config.js +37 -0
  142. package/editor/dist/cms.js +0 -5900
  143. package/editor/dist/cms.umd.cjs +0 -19
  144. /package/{editor/dist → public}/vite.svg +0 -0
@@ -0,0 +1,19 @@
1
+ <svg width="228" height="30" viewBox="0 0 228 30" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <g clip-path="url(#clip0_200_320)">
3
+ <path d="M27.3044 11.5405C27.8889 11.9223 28.5318 12.2086 29.2086 12.3906V17.5268C26.4523 18.2695 24.4219 20.7272 24.4219 23.6324C24.4219 24.1752 24.4927 24.7121 24.6342 25.2311L20.0536 27.7962C19.5398 27.307 18.9492 26.9044 18.3001 26.6031V20.7063C20.5427 19.6773 22.0132 17.485 22.0132 14.9885C22.0132 14.8304 22.007 14.6723 21.9947 14.5142L27.3044 11.5405ZM27.4828 9.48242L20.0597 13.6403C20.192 14.0668 20.2658 14.5202 20.2658 14.9885C20.2658 17.1927 18.6785 19.039 16.5527 19.5103V27.7962C17.8632 28.0885 18.9676 28.9028 19.6229 30.0004L26.8214 25.9708C26.4092 25.2848 26.1723 24.4884 26.1723 23.6383C26.1723 21.0762 28.3165 18.9972 30.9591 18.9972V10.932C29.587 10.932 28.3565 10.3742 27.4828 9.48242Z" fill="#CE4140"/>
4
+ <path d="M20.1119 2.21018L24.6218 4.73653C24.4895 5.24359 24.4218 5.76556 24.4218 6.29052C24.4218 6.46649 24.4279 6.64247 24.4433 6.81845L19.1736 9.76834C18.0938 9.04951 16.8018 8.6558 15.4759 8.6558C14.1593 8.6558 12.8734 9.04653 11.7967 9.75641L6.51157 6.79757C6.52388 6.63054 6.53311 6.46053 6.53311 6.29052C6.53311 5.76556 6.46543 5.24359 6.33315 4.73653L10.8399 2.21316C12.052 3.39431 13.7132 4.08332 15.4728 4.08332C17.2356 4.08332 18.8998 3.39133 20.1119 2.21018ZM19.6566 0C18.8383 1.42275 17.2725 2.38616 15.4728 2.38616C13.6732 2.38616 12.1074 1.42275 11.2891 0.0029827L4.1582 3.99384C4.55505 4.67091 4.78577 5.45238 4.78577 6.28753C4.78577 6.76775 4.71194 7.23007 4.57043 7.66256L11.9843 11.8115C12.858 10.9077 14.0977 10.344 15.4759 10.344C16.8602 10.344 18.1092 10.9137 18.9829 11.8264L26.3906 7.68045C26.2491 7.23901 26.1691 6.77073 26.1691 6.28455C26.1691 5.44939 26.3968 4.66793 26.7967 3.99085L19.6566 0Z" fill="#CE4140"/>
5
+ <path d="M3.67003 11.5278L8.96434 14.4926C8.95203 14.6567 8.94588 14.8237 8.94588 14.9908C8.94588 17.4843 10.4164 19.6736 12.6528 20.7056V26.6054C12.0068 26.9066 11.4131 27.3093 10.9024 27.7955L6.32487 25.2333C6.46638 24.7144 6.53714 24.1745 6.53714 23.6316C6.53714 20.7235 4.50678 18.2687 1.75041 17.5261V12.3928C2.43028 12.2079 3.08245 11.9156 3.67003 11.5278ZM3.48545 9.4668C2.61178 10.3676 1.37511 10.9283 0 10.9283V18.9906C2.64254 18.9906 4.78672 21.0695 4.78672 23.6316C4.78672 24.4817 4.54985 25.2781 4.13762 25.9641L11.33 29.9908C11.9853 28.8961 13.0866 28.0818 14.3971 27.7895V19.5095C12.2744 19.0353 10.6901 17.192 10.6901 14.9878C10.6901 14.5105 10.764 14.0512 10.9024 13.6187L3.48545 9.4668Z" fill="#CE4140"/>
6
+ <path d="M62.2744 21.4218V17.4638C62.2744 15.4833 60.6132 13.8726 58.5706 13.8726H42.8599C42.7953 13.8726 42.7307 13.8786 42.6661 13.8875H42.1062C41.5002 13.8875 41.008 13.4103 41.008 12.8227V8.86466C41.008 8.27707 41.5002 7.79984 42.1062 7.79984H60.767C61.4869 7.79984 62.0714 7.23312 62.0714 6.53517C62.0714 5.83722 61.4869 5.27051 60.767 5.27051H42.1062C40.0635 5.27051 38.4023 6.88117 38.4023 8.86168V12.8197C38.4023 14.8002 40.0635 16.4109 42.1062 16.4109H57.8169C57.8815 16.4109 57.9461 16.4049 58.0107 16.396H58.5706C59.1766 16.396 59.6688 16.8732 59.6688 17.4608V21.4188C59.6688 22.0064 59.1766 22.4837 58.5706 22.4837H39.9097C39.1899 22.4837 38.6054 23.0504 38.6054 23.7483C38.6054 24.4463 39.1899 25.013 39.9097 25.013H58.5706C60.6132 25.013 62.2744 23.4023 62.2744 21.4218Z" fill="#CE4140"/>
7
+ <path d="M88.8396 21.4011V9.01695C88.8396 7.03644 87.1784 5.42578 85.1357 5.42578H68.739C66.6964 5.42578 65.0352 7.03644 65.0352 9.01695V21.4011C65.0352 23.3816 66.6964 24.9923 68.739 24.9923H85.1357C87.1784 24.9923 88.8396 23.3787 88.8396 21.4011ZM67.6408 9.01695C67.6408 8.42936 68.133 7.95213 68.739 7.95213H85.1357C85.7417 7.95213 86.2339 8.42936 86.2339 9.01695V21.4011C86.2339 21.9887 85.7417 22.4659 85.1357 22.4659H68.739C68.133 22.4659 67.6408 21.9887 67.6408 21.4011V9.01695Z" fill="#CE4140"/>
8
+ <path d="M228.002 21.4011V9.01695C228.002 7.03644 226.34 5.42578 224.298 5.42578H207.901C205.858 5.42578 204.197 7.03644 204.197 9.01695V21.4011C204.197 23.3816 205.858 24.9923 207.901 24.9923H224.298C226.34 24.9923 228.002 23.3787 228.002 21.4011ZM206.806 9.01695C206.806 8.42936 207.298 7.95213 207.904 7.95213H224.301C224.907 7.95213 225.399 8.42936 225.399 9.01695V21.4011C225.399 21.9887 224.907 22.4659 224.301 22.4659H207.904C207.298 22.4659 206.806 21.9887 206.806 21.4011V9.01695Z" fill="#CE4140"/>
9
+ <path d="M94.5998 23.7277V18.329H106.194C106.914 18.329 107.499 17.7623 107.499 17.0643C107.499 16.3664 106.914 15.7997 106.194 15.7997H94.5998V9.01701C94.5998 8.42941 95.092 7.95218 95.698 7.95218H111.682C112.402 7.95218 112.987 7.38547 112.987 6.68752C112.987 5.98956 112.402 5.42285 111.682 5.42285H95.698C93.6553 5.42285 91.9941 7.03351 91.9941 9.01402V23.7247C91.9941 24.4227 92.5786 24.9894 93.2985 24.9894C94.0183 24.9923 94.5998 24.4256 94.5998 23.7277Z" fill="#CE4140"/>
10
+ <path d="M116.758 7.95218H125.849V23.4622C125.849 24.1602 126.433 24.7269 127.153 24.7269C127.873 24.7269 128.457 24.1602 128.457 23.4622V7.95218H137.548C138.268 7.95218 138.852 7.38547 138.852 6.68752C138.852 5.98956 138.268 5.42285 137.548 5.42285H116.761C116.042 5.42285 115.457 5.98956 115.457 6.68752C115.454 7.38547 116.038 7.95218 116.758 7.95218Z" fill="#CE4140"/>
11
+ <path d="M152.364 24.6582C153.084 24.6582 153.668 24.0915 153.668 23.3936V18.3289H171.092C173.135 18.3289 174.796 16.7183 174.796 14.7378V9.01695C174.796 7.03644 173.135 5.42578 171.092 5.42578H154.766C152.724 5.42578 151.063 7.03644 151.063 9.01695V23.3965C151.059 24.0915 151.644 24.6582 152.364 24.6582ZM154.763 7.95213H171.089C171.695 7.95213 172.188 8.42936 172.188 9.01695V14.7378C172.188 15.3254 171.695 15.8026 171.089 15.8026H153.665V9.01695C153.665 8.42936 154.157 7.95213 154.763 7.95213Z" fill="#CE4140"/>
12
+ <path d="M178.913 24.6582C179.632 24.6582 180.217 24.0915 180.217 23.3936V18.3289H190.587C190.664 18.4721 190.772 18.6034 190.907 18.7137L197.835 24.3003C198.078 24.4972 198.373 24.5926 198.669 24.5926C199.041 24.5926 199.413 24.4375 199.671 24.1363C200.133 23.5994 200.056 22.803 199.502 22.3556L194.506 18.326H197.641C199.684 18.326 201.345 16.7153 201.345 14.7348V9.01695C201.345 7.03644 199.684 5.42578 197.641 5.42578H181.315C179.273 5.42578 177.611 7.03644 177.611 9.01695V23.3965C177.608 24.0915 178.193 24.6582 178.913 24.6582ZM181.312 7.95213H197.638C198.244 7.95213 198.736 8.42936 198.736 9.01695V14.7378C198.736 15.3254 198.244 15.8026 197.638 15.8026H180.214V9.01695C180.214 8.42936 180.706 7.95213 181.312 7.95213Z" fill="#CE4140"/>
13
+ </g>
14
+ <defs>
15
+ <clipPath id="clip0_200_320">
16
+ <rect width="228" height="30" fill="white"/>
17
+ </clipPath>
18
+ </defs>
19
+ </svg>
@@ -0,0 +1,6 @@
1
+ ---
2
+ order: 0
3
+ sidebar: false
4
+ ---
5
+
6
+ # Content Management System
@@ -0,0 +1,157 @@
1
+ <template>
2
+ <button
3
+ type="button"
4
+ class="vs-button inline-flex border-solid justify-center items-center gap-2 rounded-md font-semibold focus:outline-none text-sm transition-all"
5
+ :class="{
6
+ ...buttonData.params,
7
+ [size === 'mini'
8
+ ? 'py-1 px-3'
9
+ : size === 'large'
10
+ ? 'py-3 px-7'
11
+ : 'py-1.5 px-5']: true,
12
+ [disabled
13
+ ? 'disabled opacity-50 focus:ring-0 focus:ring-offset-0 cursor-not-allowed'
14
+ : 'focus:ring-2 focus:ring-offset-2']: true,
15
+ ['!px-2 !py-2 !rounded-full']: circle,
16
+ ['!px-2 !py-2 !rounded-md']: square,
17
+ }"
18
+ >
19
+ <slot>{{title}}</slot>
20
+ <i
21
+ v-if="icon"
22
+ :class="'ti ' + icon"
23
+ class="pointer-events-none"
24
+ />
25
+ <div
26
+ v-if="loading"
27
+ role="status"
28
+ >
29
+ <svg
30
+ aria-hidden="true"
31
+ class="inline w-4 h-4 text-gray-200 animate-spin fill-blue-600"
32
+ viewBox="0 0 100 101"
33
+ fill="none"
34
+ xmlns="http://www.w3.org/2000/svg"
35
+ >
36
+ <path
37
+ d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
38
+ fill="currentColor"
39
+ />
40
+ <path
41
+ d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
42
+ fill="currentFill"
43
+ />
44
+ </svg>
45
+ </div>
46
+ </button>
47
+ </template>
48
+ <script lang="ts">
49
+
50
+ interface IButtonClass {
51
+ [key: string]: {
52
+ params: object
53
+ }
54
+ }
55
+
56
+ interface IButtonData {
57
+ buttonClass: IButtonClass
58
+ }
59
+
60
+ export default {
61
+ props: {
62
+ type: { type: String, default: () => '' },
63
+ title: { type: String, default: () => '' },
64
+ color: { type: String, default: 'blue' },
65
+ size: { type: String, default: () => '' },
66
+ disabled: { type: Boolean, default: false },
67
+ loading: { type: Boolean, default: false },
68
+ icon: { type: String, default: () => '' },
69
+ circle: Boolean,
70
+ square: Boolean,
71
+ },
72
+ data() {
73
+ return {
74
+ buttonClass: {},
75
+ } as IButtonData;
76
+ },
77
+ computed: {
78
+ // Return button class by type in props
79
+ buttonData() {
80
+ return this.buttonClass?.[this.type] || this.buttonClass.default;
81
+ },
82
+ },
83
+ watch: {
84
+ color: {
85
+ immediate: true,
86
+ // Here is all aviable types with theirs styles
87
+ handler(newColor) {
88
+ this.buttonClass = {
89
+ primary: {
90
+ params: {
91
+ 'border-1 border-transparent text-white': true,
92
+ [`bg-${newColor}-500`]: true,
93
+ [`hover:bg-${newColor}-600`]: true,
94
+ [`focus:ring-${newColor}-500`]: true,
95
+ },
96
+ },
97
+ text: {
98
+ params: {
99
+ 'border border-transparent': true,
100
+ [`text-${newColor}-500`]: true,
101
+ [`hover:bg-${newColor}-100`]: true,
102
+ [`focus:ring-${newColor}-500`]: true,
103
+ },
104
+ },
105
+ plain: {
106
+ params: {
107
+ 'border border-transparent hover:text-white ring-offset-white': true,
108
+ [`bg-${newColor}-100`]: true,
109
+ [`text-${newColor}-500`]: true,
110
+ [`hover:bg-${newColor}-500`]: true,
111
+ [`focus:ring-${newColor}-500`]: true,
112
+ },
113
+ },
114
+ border: {
115
+ params: {
116
+ 'border font-medium bg-white shadow-sm align-middle hover:bg-gray-50 focus:ring-offset-white': true,
117
+ [`text-${newColor}-700`]: true,
118
+ [`focus:ring-${newColor}-600`]: true,
119
+ },
120
+ },
121
+ link: {
122
+ params: {
123
+ 'border border-transparent ring-offset-white': true,
124
+ [`text-${newColor}-600`]: true,
125
+ [`hover:text-${newColor}-700`]: true,
126
+ [`focus:ring-${newColor}-600`]: true,
127
+ },
128
+ },
129
+ default: {
130
+ params: {
131
+ 'border !border-gray-200 hover:text-white bg-white': true,
132
+ [`text-${newColor}-500`]: true,
133
+ [`hover:border-${newColor}-500`]: true,
134
+ [`hover:bg-${newColor}-500`]: true,
135
+ [`focus:ring-${newColor}-500`]: true,
136
+ },
137
+ },
138
+ };
139
+ },
140
+ },
141
+ },
142
+ };
143
+ </script>
144
+ <style lang="scss" scoped>
145
+ .vs-button {
146
+ width: fit-content;
147
+ &:active {
148
+ opacity: 0.8;
149
+ }
150
+
151
+ &.disabled {
152
+ &:active {
153
+ opacity: 0.5;
154
+ }
155
+ }
156
+ }
157
+ </style>
@@ -0,0 +1,144 @@
1
+ <script setup>
2
+ import VsButton from '../../src/vs-button.vue'
3
+ </script>
4
+
5
+ # VsButton
6
+
7
+ Часто використовувана кнопка.
8
+
9
+ ## Підключення
10
+
11
+ ```html title='vue'
12
+ <VsButton title="Default" />
13
+ ```
14
+
15
+ <div class="flex mt-2">
16
+ <VsButton class="mr-2" title="Default" />
17
+ </div>
18
+
19
+ ## Component API
20
+
21
+ ::: tabs
22
+
23
+ == Style
24
+
25
+ | Name | Description | Type |
26
+ | :-----: | :-------------------------------------------------------------------------------: | :------: |
27
+ | `size` | Розмір кнопки (mini, small, large) | `String` |
28
+ | `color` | Колір кнопки. [Доступні кольори](https://tailwindcss.com/docs/customizing-colors) | `String` |
29
+ | `type` | Тип кнопки (primary, text, plain, border, link, default) | `String` |
30
+
31
+ == State
32
+
33
+ | Name | Description | Type |
34
+ | :--------: | :------------------------------------------: | :-------: |
35
+ | `disabled` | Визначає, чи кнопка вимкнена | `Boolean` |
36
+ | `loading` | Анімація індикатора загрузки поруч з текстом | `Boolean` |
37
+
38
+ == Content
39
+
40
+ | Name | Description | Type |
41
+ | :----: | :---------------------------: | :------: |
42
+ | `icon` | Іконка поруч з текстом кнопки | `String` |
43
+
44
+ == Events
45
+
46
+ | Name | Description |
47
+ | :------: | :-------------------------: |
48
+ | `@onClick` | Реалізує клік по компоненту |
49
+
50
+ == Slots
51
+
52
+ | Name | Description | Default |
53
+ | :-------: | :----------------------------------------------------------: | :------------------------- |
54
+ | `default` | Дефолтний слот, зазвичай у кнопку вставляється текст, іконка | `<template>...</template>` |
55
+
56
+ :::
57
+
58
+ ## Приклади
59
+
60
+ ### Базове використання (тип кнопки)
61
+
62
+ Використовуйте типи `default`, `primary`, `plain` `border`, `text`, `link` щоб визначити стиль кнопки.
63
+
64
+ ```html title='vue'
65
+ <VsButton title="Default" />
66
+ <VsButton title="Primary" type="primary"/>
67
+ <VsButton title="Plain" type="plain"/>
68
+ <VsButton title="Border" type="border"/>
69
+ <VsButton title="Link" type="link"/>
70
+ <VsButton title="Text" type="text"/>
71
+ ```
72
+
73
+ <div class="flex mt-2">
74
+ <VsButton class="mr-2" title="Default" />
75
+ <VsButton class="mr-2" title="Primary" type="primary"/>
76
+ <VsButton class="mr-2" title="Plain" type="plain"/>
77
+ <VsButton class="mr-2" title="Border" type="border"/>
78
+ <VsButton class="mr-2" title="Link" type="link"/>
79
+ <VsButton title="Text" type="text"/>
80
+ </div>
81
+
82
+ ### Розміри
83
+
84
+ Окрім розміру за замовчуванням (`default`), компонент надає два додаткові розміри для вибору з різних сценаріїв.
85
+ Використовуйте розмір атрибута, щоб установити додаткові розміри (`large`, `mini`).
86
+
87
+ <!-- <VueExample title="VsButton" >
88
+ { `
89
+ <VsButton type="primary" size="mini">Primary</VsButton>
90
+ <VsButton type="primary" size="default">Primary</VsButton>
91
+ <VsButton type="primary" size="large">Primary</VsButton>
92
+ ` }
93
+ </VueExample> -->
94
+
95
+ ```html title='vue'
96
+ <VsButton title="Mini" size="mini" type="primary"/>
97
+ <VsButton title="Default" size="default" type="primary"/>
98
+ <VsButton title="Large" size="large" type="primary"/>
99
+ ```
100
+
101
+ <div class="flex mt-2 items-center">
102
+ <VsButton class="mr-2" title="Mini" size="mini" type="primary"/>
103
+ <VsButton class="mr-2" title="Default" size="default" type="primary"/>
104
+ <VsButton class="mr-2" title="Large" size="large" type="primary"/>
105
+ </div>
106
+
107
+ ### Вимкнення кнопки
108
+
109
+ Атрибут `disabled` визначає, чи кнопка вимкнена.
110
+ Використовуйте атрибут `disabled`, щоб визначити, чи кнопка вимкнена. Він приймає логічне значення.
111
+
112
+ ```html title='vue'
113
+ <VsButton title="Mini" size="mini" type="primary"/>
114
+ <VsButton title="Default" size="default" type="primary"/>
115
+ <VsButton title="Large" size="large" type="primary"/>
116
+ ```
117
+
118
+ <div class="flex mt-2 items-center">
119
+ <VsButton class="mr-2" disabled title="Mini" type="primary"/>
120
+ <VsButton class="mr-2" disabled title="Default" type="primary"/>
121
+ <VsButton class="mr-2" disabled title="Large" type="primary"/>
122
+ </div>
123
+
124
+ ### Завантаження
125
+
126
+ Відображає анімацію в кнопці під час завантаження даних.
127
+
128
+ ```html title='vue'
129
+ <VsButton type="default" color="rose" :loading="true">Default</VsButton>
130
+ ```
131
+
132
+ <div class="flex mt-2 items-center">
133
+ <VsButton type="default" color="rose" :loading="true">Default</VsButton>
134
+ </div>
135
+
136
+ ### Іконка
137
+
138
+ Використовуйте іконки, щоб додати більше значення кнопці. Ви можете використовувати лише значок, щоб заощадити місце, або використовувати його з текстом.
139
+ Використовуйте атрибут `icon` у якості слота або пропса, щоб додати іконку. Ви можете знайти список значків на сайті [Tabler]("https://tabler.io/icons").
140
+
141
+ <VsButton
142
+ type="primary"
143
+ icon="ti ti-search"
144
+ />
@@ -0,0 +1,9 @@
1
+ ---
2
+ order: 0
3
+ ---
4
+
5
+ # Список компонентів
6
+
7
+ ## Basic
8
+
9
+ - [Button](basic/button) - Кнопка з різноманітними стилями та можливостями кастомізації.
package/index.html ADDED
@@ -0,0 +1,14 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>User Profile</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <div id="modal"></div>
12
+ <script type="module" src="/src/main.js"></script>
13
+ </body>
14
+ </html>
package/package.json CHANGED
@@ -1,17 +1,14 @@
1
1
  {
2
2
  "name": "@opengis/cms",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "cms",
5
5
  "type": "module",
6
6
  "author": "Softpro",
7
7
  "license": "EULA",
8
- "files": [
9
- "./editor/dist"
10
- ],
11
8
  "scripts": {
12
9
  "test": "node --test test/**/*.test.js",
13
10
  "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
14
- "build": "vite build editor",
11
+ "build": "vite build",
15
12
  "start": "node server",
16
13
  "prod": "cross-env NODE_ENV=production npm run start",
17
14
  "docs:dev": "vitepress dev docs",
package/server/app.js ADDED
@@ -0,0 +1,25 @@
1
+ import path from 'node:path';
2
+ import { config, execMigrations } from '@opengis/fastify-table/utils.js';
3
+
4
+ config.prefix = config.prefix || '/api';
5
+ const { prefix } = config;
6
+
7
+ const cwd = process.cwd();
8
+
9
+ export default async function (fastify) {
10
+ // core
11
+ fastify.register(import('./plugins/hook.js'));
12
+
13
+ fastify.register(import('@opengis/fastify-table'), config);
14
+ fastify.register(import('@opengis/fastify-auth'), config);
15
+ fastify.register(import('@opengis/fastify-file'), config);
16
+
17
+ fastify.register(import('./plugins/vite.js'));
18
+ // API
19
+ fastify.register(import('./routes/root.mjs'));
20
+ fastify.register(import('./routes/builder/index.mjs'), { prefix });
21
+ fastify.register(import('./routes/category/index.mjs'), { prefix });
22
+ fastify.register(import('./routes/manager/index.mjs'), { prefix });
23
+ fastify.register(import('./routes/media/index.mjs'), { prefix });
24
+ execMigrations(path.join(cwd, 'server/migrations')).catch(err => console.log(err));
25
+ }
@@ -0,0 +1,5 @@
1
+ import { readFile } from 'fs/promises';
2
+ import fs from 'fs';
3
+ const config = fs.existsSync('config.json') ? JSON.parse(await readFile('config.json')) : {};
4
+
5
+ export default config;
@@ -0,0 +1,23 @@
1
+ import Fastify from 'fastify';
2
+
3
+
4
+ import appService from './app.js';
5
+ import config from './config.js';
6
+
7
+ const isProduction = process.env.NODE_ENV === 'production';
8
+
9
+ // Instantiate Fastify with some config
10
+ const app = Fastify({ logger: !isProduction });
11
+
12
+ // Register your application as a normal plugin.
13
+
14
+ app.register(appService);
15
+
16
+ process.env.PORT = process.env.PORT || config.port || 3000;
17
+ // Start listening.
18
+ app.listen({ host: '0.0.0.0', port: process.env.PORT }, (err) => {
19
+ if (err) {
20
+ app.log.error(err);
21
+ process.exit(1);
22
+ }
23
+ });
@@ -0,0 +1,30 @@
1
+ create schema if not exists crm;
2
+ create table if not exists crm.media();
3
+
4
+ CREATE TABLE IF NOT EXISTS crm.media();
5
+ ALTER TABLE crm.media DROP CONSTRAINT IF EXISTS crm_media_id_pkey;
6
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS media_id text NOT NULL DEFAULT next_id();
7
+
8
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS uploaded_name text;
9
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS file_path text;
10
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS caption text;
11
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS altname text;
12
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS size numeric;
13
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS ext text;
14
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS tags text[];
15
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS uid text;
16
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS files json;
17
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS cdate timestamp without time zone DEFAULT (now())::timestamp without time zone;
18
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS editor_id text;
19
+ ALTER TABLE crm.media ADD COLUMN IF NOT EXISTS editor_date timestamp without time zone;
20
+ ALTER TABLE crm.media ADD CONSTRAINT crm_media_id_pkey PRIMARY KEY (media_id);
21
+
22
+ comment on table crm.media is 'Медіа файли';
23
+
24
+ comment on column crm.media.uploaded_name is 'Назва файлу';
25
+ comment on column crm.media.file_path is 'Відносний шлях до файлу';
26
+ comment on column crm.media.caption is 'Заголовок';
27
+ comment on column crm.media.altname is 'Альтернативна назва';
28
+ comment on column crm.media.size is 'Розмір файлу';
29
+ comment on column crm.media.ext is 'Розширення файлу';
30
+ comment on column crm.media.tags is 'Теги файлу';
@@ -0,0 +1,91 @@
1
+ import fp from 'fastify-plugin';
2
+ import fs from 'fs';
3
+ import config from '../config.js';
4
+
5
+ // the use of fastify-plugin is required to be able
6
+ // to export the decorators to the outer scope
7
+
8
+ async function plugin(fastify) {
9
+ fastify.decorate('config', config);
10
+
11
+ // pre Request
12
+ fastify.addHook('onRequest', async (req) => {
13
+ req.funcs = fastify;
14
+ const { user } = req.session?.passport || {};
15
+ req.user = user;
16
+ });
17
+
18
+ // preSerialization
19
+ fastify.addHook('preSerialization', async (req, reply, payload) => {
20
+ if (req.url.includes('/suggest/') && !req.query.json) {
21
+ return payload?.data;
22
+ }
23
+ /* if (payload.redirect) {
24
+ return reply.redirect(payload.redirect);
25
+ }*/
26
+ if (reply.sent) {
27
+ return null;
28
+ }
29
+ if (!payload) {
30
+ return payload;
31
+ }
32
+
33
+ if (['200', '400', '500', '403', '404'].includes(payload.status)) {
34
+ reply.status(payload.status);
35
+ }
36
+ /* if (payload.headers) {
37
+ reply.headers(payload.headers);
38
+ } */
39
+ if (payload.buffer) {
40
+ return payload.buffer;
41
+ }
42
+ if (payload.file) {
43
+ // const buffer = await readFile(payload.file);
44
+ // return reply.send(buffer);
45
+ const stream = fs.createReadStream(payload.file);
46
+ return stream;
47
+ // return reply.send(stream);
48
+ }
49
+
50
+ if (payload.message) {
51
+ return payload.message;
52
+ }
53
+ return payload;
54
+ });
55
+
56
+ // preValidation
57
+ fastify.addHook('preValidation', async (req) => {
58
+ const parseRawBody = ['POST', 'PUT'].includes(req.method) && req.body && typeof req.body === 'string'
59
+ && req.body.trim(/\r\n/g).startsWith('{')
60
+ && req.body.trim(/\r\n/g).endsWith('}');
61
+ if (parseRawBody) {
62
+ try {
63
+ req.body = JSON.parse(req.body || '{}');
64
+ }
65
+ catch (err) {
66
+ // throw new Error('invalid body');
67
+ // return { error: 'invalid body', status: 400 };
68
+ }
69
+ }
70
+ });
71
+
72
+ // allow upload file
73
+ const kIsMultipart = Symbol.for('[FastifyMultipart.isMultipart]');
74
+ fastify.addContentTypeParser('multipart', (request, _, done) => {
75
+ request[kIsMultipart] = true;
76
+ done(null);
77
+ });
78
+
79
+ // parse Body
80
+ function contentParser(req, body, done) {
81
+ const parseBody = decodeURIComponent(body.toString()).split('&').reduce((acc, el) => {
82
+ const [key, val] = el.split('=');
83
+ return { ...acc, [key]: val };
84
+ }, {});
85
+ done(null, parseBody);
86
+ }
87
+
88
+ fastify.addContentTypeParser('application/x-www-form-urlencoded', { parseAs: 'buffer' }, contentParser);
89
+ }
90
+
91
+ export default fp(plugin);
@@ -0,0 +1,80 @@
1
+ import fs from 'fs';
2
+ import path, { dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const dir = dirname(fileURLToPath(import.meta.url));
6
+ const root = `${dir}/../..`;
7
+ import config from '../config.js';
8
+
9
+ const isProduction = process.env.NODE_ENV === 'production' || config.production;
10
+
11
+ async function plugin(fastify) {
12
+ // vite server
13
+ if (!isProduction) {
14
+ const vite = await import('vite');
15
+
16
+ const viteServer = await vite.createServer({
17
+ root: `${root}`,
18
+ server: {
19
+ middlewareMode: true,
20
+ },
21
+ });
22
+
23
+ // add root to watch
24
+ viteServer.watcher.add(root);
25
+
26
+ // hot relaod template
27
+ viteServer.watcher.on('all', (d, t) => {
28
+ if (!t.includes('module')) return;
29
+ console.log(d, t);
30
+ viteServer.ws.send({ type: 'full-reload' });
31
+ });
32
+
33
+ // this is middleware for vite's dev servert
34
+ fastify.addHook('onRequest', async (req, reply) => {
35
+ const { user } = req.session?.passport || {};
36
+ const { disableAuth } = req.funcs?.config || {};
37
+ if (!user && !disableAuth) {
38
+ return reply.redirect('/login');
39
+ }
40
+
41
+ const next = () => new Promise((resolve) => {
42
+ viteServer.middlewares(req.raw, reply.raw, () => resolve());
43
+ });
44
+ await next();
45
+ });
46
+ fastify.get('*', async () => { });
47
+ return;
48
+ }
49
+
50
+ // From Build
51
+ fastify.get('*', async (req, reply) => {
52
+ const { user } = req.session?.passport || {};
53
+ const assetsDir = '/dist';
54
+ if (!user) return reply.redirect('/login');
55
+ const stream = fs.createReadStream(`${assetsDir}/index.html`);
56
+ return reply.type('text/html').send(stream);
57
+ });
58
+
59
+ const fileSize = {};
60
+ async function staticFile(req, reply) {
61
+ const assetsDir = '/dist';
62
+ const filePath = path.join(root, assetsDir, req.url);
63
+ const ext = path.extname(filePath);
64
+
65
+ if (!fs.existsSync(filePath)) return { status: 404, message: 'not found' };
66
+ fileSize[filePath] = fileSize[filePath] || fs.statSync(filePath).size;
67
+ const mime = {
68
+ '.js': 'text/javascript', '.css': 'text/css', '.woff2': 'application/font-woff', '.png': 'image/png', '.svg': 'image/svg+xml', '.jpg': 'image/jpg',
69
+ }[ext];
70
+ reply.headers({ 'Cache-control': 'max-age=3600, public', 'Content-length': fileSize[filePath], 'Content-Encoding': 'identity' });
71
+
72
+ const stream = fs.createReadStream(filePath);
73
+ return mime ? reply.type(mime).send(stream) : stream;
74
+ }
75
+
76
+ fastify.get('/assets/*', staticFile);
77
+ fastify.get('/public/*', staticFile);
78
+ }
79
+
80
+ export default plugin;
@@ -0,0 +1,21 @@
1
+ import { dataDelete, getMeta } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function builderDelete({ user = {}, params = {} }) {
4
+
5
+ if (!params.id) {
6
+ return { message: 'id is required', status: 400 };
7
+ }
8
+ const { pk } = await getMeta({ table: 'site.content_types' });
9
+
10
+ if (!pk) {
11
+ return { message: 'table not found', status: 404 };
12
+ }
13
+
14
+ const res = await dataDelete({
15
+ table: 'site.content_types',
16
+ id: params.id,
17
+ uid: user?.uid
18
+ });
19
+
20
+ return { id: res.content_id, rows: [res] };
21
+ }