@lenne.tech/ng-base 18.1.2 → 22.0.0

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 (416) hide show
  1. package/CLAUDE.md +55 -0
  2. package/LICENSE +2 -2
  3. package/README.md +237 -44
  4. package/fesm2022/lenne.tech-ng-base-ai.mjs +767 -0
  5. package/fesm2022/lenne.tech-ng-base-ai.mjs.map +1 -0
  6. package/fesm2022/lenne.tech-ng-base-auth-provide.mjs +939 -0
  7. package/fesm2022/lenne.tech-ng-base-auth-provide.mjs.map +1 -0
  8. package/fesm2022/lenne.tech-ng-base-auth.mjs +454 -0
  9. package/fesm2022/lenne.tech-ng-base-auth.mjs.map +1 -0
  10. package/fesm2022/lenne.tech-ng-base-bug-bug-report-dialog-BRzVmKJr.mjs +825 -0
  11. package/fesm2022/lenne.tech-ng-base-bug-bug-report-dialog-BRzVmKJr.mjs.map +1 -0
  12. package/fesm2022/lenne.tech-ng-base-bug-bug-report-service-DWoTb_iw.mjs +351 -0
  13. package/fesm2022/lenne.tech-ng-base-bug-bug-report-service-DWoTb_iw.mjs.map +1 -0
  14. package/fesm2022/lenne.tech-ng-base-bug-capture-engine-TkjMDdqj.mjs +1046 -0
  15. package/fesm2022/lenne.tech-ng-base-bug-capture-engine-TkjMDdqj.mjs.map +1 -0
  16. package/fesm2022/lenne.tech-ng-base-bug-constants-Brm2KGHi.mjs +4 -0
  17. package/fesm2022/lenne.tech-ng-base-bug-constants-Brm2KGHi.mjs.map +1 -0
  18. package/fesm2022/lenne.tech-ng-base-bug-messages-BsP4fRQU.mjs +179 -0
  19. package/fesm2022/lenne.tech-ng-base-bug-messages-BsP4fRQU.mjs.map +1 -0
  20. package/fesm2022/lenne.tech-ng-base-bug-provide.mjs +26 -0
  21. package/fesm2022/lenne.tech-ng-base-bug-provide.mjs.map +1 -0
  22. package/fesm2022/lenne.tech-ng-base-bug-report-Tzlhe1Cm.mjs +62 -0
  23. package/fesm2022/lenne.tech-ng-base-bug-report-Tzlhe1Cm.mjs.map +1 -0
  24. package/fesm2022/lenne.tech-ng-base-bug-server.mjs +818 -0
  25. package/fesm2022/lenne.tech-ng-base-bug-server.mjs.map +1 -0
  26. package/fesm2022/lenne.tech-ng-base-bug.mjs +196 -0
  27. package/fesm2022/lenne.tech-ng-base-bug.mjs.map +1 -0
  28. package/fesm2022/lenne.tech-ng-base-files-ui.mjs +445 -0
  29. package/fesm2022/lenne.tech-ng-base-files-ui.mjs.map +1 -0
  30. package/fesm2022/lenne.tech-ng-base-files.mjs +474 -0
  31. package/fesm2022/lenne.tech-ng-base-files.mjs.map +1 -0
  32. package/fesm2022/lenne.tech-ng-base-server.mjs +408 -0
  33. package/fesm2022/lenne.tech-ng-base-server.mjs.map +1 -0
  34. package/fesm2022/lenne.tech-ng-base-share.mjs +76 -0
  35. package/fesm2022/lenne.tech-ng-base-share.mjs.map +1 -0
  36. package/fesm2022/lenne.tech-ng-base-sync.mjs +1096 -0
  37. package/fesm2022/lenne.tech-ng-base-sync.mjs.map +1 -0
  38. package/fesm2022/lenne.tech-ng-base-testing.mjs +171 -0
  39. package/fesm2022/lenne.tech-ng-base-testing.mjs.map +1 -0
  40. package/fesm2022/lenne.tech-ng-base-ui-provide.mjs +124 -0
  41. package/fesm2022/lenne.tech-ng-base-ui-provide.mjs.map +1 -0
  42. package/fesm2022/lenne.tech-ng-base-ui.mjs +34611 -0
  43. package/fesm2022/lenne.tech-ng-base-ui.mjs.map +1 -0
  44. package/fesm2022/lenne.tech-ng-base-utils.mjs +287 -0
  45. package/fesm2022/lenne.tech-ng-base-utils.mjs.map +1 -0
  46. package/fesm2022/lenne.tech-ng-base.mjs +615 -902
  47. package/fesm2022/lenne.tech-ng-base.mjs.map +1 -1
  48. package/i18n/auth/de.json +17 -0
  49. package/i18n/auth/en.json +17 -0
  50. package/i18n/bug/de.json +85 -0
  51. package/i18n/bug/en.json +85 -0
  52. package/i18n/de.json +8 -0
  53. package/i18n/en.json +8 -0
  54. package/i18n/files/de.json +27 -0
  55. package/i18n/files/en.json +27 -0
  56. package/i18n/share/de.json +8 -0
  57. package/i18n/share/en.json +8 -0
  58. package/i18n/ui/de.json +38 -0
  59. package/i18n/ui/en.json +38 -0
  60. package/package.json +127 -58
  61. package/types/lenne.tech-ng-base-ai.d.ts +830 -0
  62. package/types/lenne.tech-ng-base-auth-provide.d.ts +603 -0
  63. package/types/lenne.tech-ng-base-auth.d.ts +320 -0
  64. package/types/lenne.tech-ng-base-bug-provide.d.ts +272 -0
  65. package/types/lenne.tech-ng-base-bug-rolldown-runtime-C25Le4-w.d.ts +1 -0
  66. package/types/lenne.tech-ng-base-bug-server.d.ts +316 -0
  67. package/types/lenne.tech-ng-base-bug.d.ts +202 -0
  68. package/types/lenne.tech-ng-base-files-ui.d.ts +116 -0
  69. package/types/lenne.tech-ng-base-files.d.ts +288 -0
  70. package/types/lenne.tech-ng-base-server.d.ts +262 -0
  71. package/types/lenne.tech-ng-base-share.d.ts +21 -0
  72. package/types/lenne.tech-ng-base-sync.d.ts +380 -0
  73. package/types/lenne.tech-ng-base-testing.d.ts +327 -0
  74. package/types/lenne.tech-ng-base-ui-provide.d.ts +65 -0
  75. package/types/lenne.tech-ng-base-ui.d.ts +7732 -0
  76. package/types/lenne.tech-ng-base-utils.d.ts +150 -0
  77. package/types/lenne.tech-ng-base.d.ts +503 -0
  78. package/ui/styles/components/accordion.css +3 -0
  79. package/ui/styles/components/alert-dialog.css +4 -0
  80. package/ui/styles/components/alert.css +3 -0
  81. package/ui/styles/components/aspect-ratio.css +3 -0
  82. package/ui/styles/components/attachment.css +4 -0
  83. package/ui/styles/components/autocomplete.css +4 -0
  84. package/ui/styles/components/avatar.css +3 -0
  85. package/ui/styles/components/badge.css +3 -0
  86. package/ui/styles/components/breadcrumb.css +3 -0
  87. package/ui/styles/components/bubble.css +3 -0
  88. package/ui/styles/components/button-group.css +3 -0
  89. package/ui/styles/components/button.css +3 -0
  90. package/ui/styles/components/calendar.css +5 -0
  91. package/ui/styles/components/card.css +3 -0
  92. package/ui/styles/components/checkbox.css +3 -0
  93. package/ui/styles/components/code-block.css +4 -0
  94. package/ui/styles/components/collapsible.css +3 -0
  95. package/ui/styles/components/combobox.css +5 -0
  96. package/ui/styles/components/command.css +5 -0
  97. package/ui/styles/components/context-menu.css +3 -0
  98. package/ui/styles/components/date-picker.css +7 -0
  99. package/ui/styles/components/dialog.css +4 -0
  100. package/ui/styles/components/drawer.css +3 -0
  101. package/ui/styles/components/dropdown-menu.css +3 -0
  102. package/ui/styles/components/empty.css +3 -0
  103. package/ui/styles/components/field.css +5 -0
  104. package/ui/styles/components/hover-card.css +3 -0
  105. package/ui/styles/components/index.css +91 -0
  106. package/ui/styles/components/input-group.css +6 -0
  107. package/ui/styles/components/input-otp.css +3 -0
  108. package/ui/styles/components/input.css +3 -0
  109. package/ui/styles/components/item.css +4 -0
  110. package/ui/styles/components/kbd.css +3 -0
  111. package/ui/styles/components/label.css +3 -0
  112. package/ui/styles/components/lt-breadcrumb.css +3 -0
  113. package/ui/styles/components/lt-bug-attachments.css +5 -0
  114. package/ui/styles/components/lt-bug-journey-timeline.css +5 -0
  115. package/ui/styles/components/lt-bug-report-dialog.css +15 -0
  116. package/ui/styles/components/lt-bug-report.css +4 -0
  117. package/ui/styles/components/lt-color-mode-button.css +3 -0
  118. package/ui/styles/components/lt-confirm-dialog.css +4 -0
  119. package/ui/styles/components/lt-data-view.css +5 -0
  120. package/ui/styles/components/lt-date-picker.css +3 -0
  121. package/ui/styles/components/lt-date-time-picker.css +5 -0
  122. package/ui/styles/components/lt-enter-action.css +2 -0
  123. package/ui/styles/components/lt-footer.css +3 -0
  124. package/ui/styles/components/lt-form-field.css +4 -0
  125. package/ui/styles/components/lt-header.css +5 -0
  126. package/ui/styles/components/lt-imports.css +21 -0
  127. package/ui/styles/components/lt-navigation-menu.css +3 -0
  128. package/ui/styles/components/lt-notice.css +3 -0
  129. package/ui/styles/components/lt-overlay-defaults.css +3 -0
  130. package/ui/styles/components/lt-page-card.css +3 -0
  131. package/ui/styles/components/lt-page-title.css +2 -0
  132. package/ui/styles/components/lt-password-input.css +4 -0
  133. package/ui/styles/components/lt-readonly-field.css +2 -0
  134. package/ui/styles/components/lt-select.css +4 -0
  135. package/ui/styles/components/lt-text-field.css +5 -0
  136. package/ui/styles/components/lt-toolbar-item.css +2 -0
  137. package/ui/styles/components/lt-toolbar.css +3 -0
  138. package/ui/styles/components/lt-tus-upload-component.css +5 -0
  139. package/ui/styles/components/marker.css +3 -0
  140. package/ui/styles/components/menubar.css +3 -0
  141. package/ui/styles/components/message-scroller.css +4 -0
  142. package/ui/styles/components/message.css +3 -0
  143. package/ui/styles/components/native-select.css +3 -0
  144. package/ui/styles/components/navigation-menu.css +3 -0
  145. package/ui/styles/components/pagination.css +4 -0
  146. package/ui/styles/components/popover.css +3 -0
  147. package/ui/styles/components/progress.css +3 -0
  148. package/ui/styles/components/questionnaire.css +4 -0
  149. package/ui/styles/components/radio-group.css +3 -0
  150. package/ui/styles/components/resizable.css +3 -0
  151. package/ui/styles/components/select.css +3 -0
  152. package/ui/styles/components/separator.css +3 -0
  153. package/ui/styles/components/sheet.css +4 -0
  154. package/ui/styles/components/sidebar.css +9 -0
  155. package/ui/styles/components/skeleton.css +3 -0
  156. package/ui/styles/components/slider.css +3 -0
  157. package/ui/styles/components/sonner.css +4 -0
  158. package/ui/styles/components/spinner.css +3 -0
  159. package/ui/styles/components/switch.css +3 -0
  160. package/ui/styles/components/table.css +3 -0
  161. package/ui/styles/components/tabs.css +4 -0
  162. package/ui/styles/components/textarea.css +3 -0
  163. package/ui/styles/components/toggle-group.css +4 -0
  164. package/ui/styles/components/toggle.css +3 -0
  165. package/ui/styles/components/tooltip.css +3 -0
  166. package/ui/styles/components/trace.css +3 -0
  167. package/ui/styles/components/typography.css +3 -0
  168. package/ui/styles/components/utils.css +2 -0
  169. package/ui/styles/core.css +137 -0
  170. package/ui/styles/palette.css +94 -0
  171. package/ui/styles/theme.css +14 -0
  172. package/ui/styles/transitions.css +131 -0
  173. package/base-cms/index.d.ts +0 -5
  174. package/base-cms/interfaces/base-cms-module-config.interface.d.ts +0 -16
  175. package/base-cms/pages/base-cms/base-cms.component.d.ts +0 -92
  176. package/base-cms/public-api.d.ts +0 -5
  177. package/base-components/components/breadcrumb/breadcrumb.component.d.ts +0 -10
  178. package/base-components/components/change-password/change-password.component.d.ts +0 -24
  179. package/base-components/components/checkbox/checkbox.component.d.ts +0 -12
  180. package/base-components/components/delete-account/delete-account.component.d.ts +0 -15
  181. package/base-components/components/fab-button/fab-button.component.d.ts +0 -16
  182. package/base-components/components/input/input.component.d.ts +0 -27
  183. package/base-components/components/loading-bar/loading-bar.component.d.ts +0 -17
  184. package/base-components/components/model-form/model-form-base.component.d.ts +0 -64
  185. package/base-components/components/model-form/model-form.component.d.ts +0 -139
  186. package/base-components/components/model-form-sub/model-form-sub.component.d.ts +0 -56
  187. package/base-components/components/model-table/model-table.component.d.ts +0 -115
  188. package/base-components/components/refence-input/refence-input.component.d.ts +0 -84
  189. package/base-components/components/select/select.component.d.ts +0 -25
  190. package/base-components/components/skeleton-loader/skeleton-loader.component.d.ts +0 -10
  191. package/base-components/components/tags/tags.component.d.ts +0 -60
  192. package/base-components/components/textarea/textarea.component.d.ts +0 -21
  193. package/base-components/components/toast/toast.component.d.ts +0 -19
  194. package/base-components/components/upload-image/upload-image.component.d.ts +0 -111
  195. package/base-components/directives/back-button.directive.d.ts +0 -9
  196. package/base-components/directives/context-menu.directive.d.ts +0 -22
  197. package/base-components/directives/sort.directive.d.ts +0 -15
  198. package/base-components/directives/stop-propagation.directive.d.ts +0 -10
  199. package/base-components/index.d.ts +0 -5
  200. package/base-components/pipes/ellipses.pipe.d.ts +0 -10
  201. package/base-components/public-api.d.ts +0 -26
  202. package/base-prototype/index.d.ts +0 -5
  203. package/base-prototype/pages/email-verification/email-verification.component.d.ts +0 -18
  204. package/base-prototype/pages/forgot-password/forgot-password.component.d.ts +0 -26
  205. package/base-prototype/pages/login/login.component.d.ts +0 -33
  206. package/base-prototype/pages/profile/profile.component.d.ts +0 -24
  207. package/base-prototype/pages/register/register.component.d.ts +0 -31
  208. package/base-prototype/pages/set-password/set-password.component.d.ts +0 -29
  209. package/base-prototype/public-api.d.ts +0 -9
  210. package/esm2022/base-cms/interfaces/base-cms-module-config.interface.mjs +0 -3
  211. package/esm2022/base-cms/lenne.tech-ng-base-base-cms.mjs +0 -5
  212. package/esm2022/base-cms/pages/base-cms/base-cms.component.mjs +0 -238
  213. package/esm2022/base-cms/public-api.mjs +0 -8
  214. package/esm2022/base-components/components/breadcrumb/breadcrumb.component.mjs +0 -23
  215. package/esm2022/base-components/components/change-password/change-password.component.mjs +0 -97
  216. package/esm2022/base-components/components/checkbox/checkbox.component.mjs +0 -38
  217. package/esm2022/base-components/components/delete-account/delete-account.component.mjs +0 -59
  218. package/esm2022/base-components/components/fab-button/fab-button.component.mjs +0 -24
  219. package/esm2022/base-components/components/input/input.component.mjs +0 -75
  220. package/esm2022/base-components/components/loading-bar/loading-bar.component.mjs +0 -37
  221. package/esm2022/base-components/components/model-form/model-form-base.component.mjs +0 -145
  222. package/esm2022/base-components/components/model-form/model-form.component.mjs +0 -591
  223. package/esm2022/base-components/components/model-form-sub/model-form-sub.component.mjs +0 -117
  224. package/esm2022/base-components/components/model-table/model-table.component.mjs +0 -478
  225. package/esm2022/base-components/components/refence-input/refence-input.component.mjs +0 -278
  226. package/esm2022/base-components/components/select/select.component.mjs +0 -62
  227. package/esm2022/base-components/components/skeleton-loader/skeleton-loader.component.mjs +0 -33
  228. package/esm2022/base-components/components/tags/tags.component.mjs +0 -227
  229. package/esm2022/base-components/components/textarea/textarea.component.mjs +0 -79
  230. package/esm2022/base-components/components/toast/toast.component.mjs +0 -69
  231. package/esm2022/base-components/components/upload-image/upload-image.component.mjs +0 -346
  232. package/esm2022/base-components/directives/back-button.directive.mjs +0 -25
  233. package/esm2022/base-components/directives/context-menu.directive.mjs +0 -112
  234. package/esm2022/base-components/directives/sort.directive.mjs +0 -61
  235. package/esm2022/base-components/directives/stop-propagation.directive.mjs +0 -24
  236. package/esm2022/base-components/lenne.tech-ng-base-base-components.mjs +0 -5
  237. package/esm2022/base-components/pipes/ellipses.pipe.mjs +0 -23
  238. package/esm2022/base-components/public-api.mjs +0 -30
  239. package/esm2022/base-prototype/lenne.tech-ng-base-base-prototype.mjs +0 -5
  240. package/esm2022/base-prototype/pages/email-verification/email-verification.component.mjs +0 -60
  241. package/esm2022/base-prototype/pages/forgot-password/forgot-password.component.mjs +0 -78
  242. package/esm2022/base-prototype/pages/login/login.component.mjs +0 -114
  243. package/esm2022/base-prototype/pages/profile/profile.component.mjs +0 -87
  244. package/esm2022/base-prototype/pages/register/register.component.mjs +0 -100
  245. package/esm2022/base-prototype/pages/set-password/set-password.component.mjs +0 -91
  246. package/esm2022/base-prototype/public-api.mjs +0 -11
  247. package/esm2022/lenne.tech-ng-base.mjs +0 -5
  248. package/esm2022/public-api.mjs +0 -25
  249. package/esm2022/shared/classes/auth.class.mjs +0 -14
  250. package/esm2022/shared/classes/basic-user.class.mjs +0 -49
  251. package/esm2022/shared/classes/graphql-enum.class.mjs +0 -10
  252. package/esm2022/shared/classes/graphql-meta.class.mjs +0 -345
  253. package/esm2022/shared/classes/graphql-type.class.mjs +0 -14
  254. package/esm2022/shared/classes/helper.class.mjs +0 -307
  255. package/esm2022/shared/classes/sort.class.mjs +0 -38
  256. package/esm2022/shared/classes/standard.class.mjs +0 -40
  257. package/esm2022/shared/decorators/debounce.decorator.mjs +0 -12
  258. package/esm2022/shared/enums/comparison-operator.enum.mjs +0 -16
  259. package/esm2022/shared/enums/graphql-request-type.enum.mjs +0 -10
  260. package/esm2022/shared/enums/logical-operator.enum.mjs +0 -10
  261. package/esm2022/shared/enums/skeleton-type.enum.mjs +0 -7
  262. package/esm2022/shared/enums/sort-order.enum.mjs +0 -6
  263. package/esm2022/shared/enums/toast-type.enum.mjs +0 -8
  264. package/esm2022/shared/functions/ws-client.function.mjs +0 -39
  265. package/esm2022/shared/interfaces/base-module-config.interface.mjs +0 -3
  266. package/esm2022/shared/interfaces/breadcrumb.interface.mjs +0 -2
  267. package/esm2022/shared/interfaces/can-component-deactivate.interface.mjs +0 -2
  268. package/esm2022/shared/interfaces/cms-enum-values.interface.mjs +0 -2
  269. package/esm2022/shared/interfaces/cms-field-config.interface.mjs +0 -2
  270. package/esm2022/shared/interfaces/cms-model-config.interface.mjs +0 -2
  271. package/esm2022/shared/interfaces/cms-table-field.interface.mjs +0 -2
  272. package/esm2022/shared/interfaces/combined-filter-input.interface.mjs +0 -2
  273. package/esm2022/shared/interfaces/compress-options.interface.mjs +0 -2
  274. package/esm2022/shared/interfaces/cropping-options.interface.mjs +0 -2
  275. package/esm2022/shared/interfaces/field-object.interface.mjs +0 -2
  276. package/esm2022/shared/interfaces/field.interface.mjs +0 -2
  277. package/esm2022/shared/interfaces/filter-input.interface.mjs +0 -2
  278. package/esm2022/shared/interfaces/find-args.interface.mjs +0 -2
  279. package/esm2022/shared/interfaces/graphql-crud-type.interface.mjs +0 -2
  280. package/esm2022/shared/interfaces/graphql-fields-object.interface.mjs +0 -2
  281. package/esm2022/shared/interfaces/graphql-options.interface.mjs +0 -2
  282. package/esm2022/shared/interfaces/graphql-plus-options.interfacen.mjs +0 -2
  283. package/esm2022/shared/interfaces/graphql-type-collection.interface.mjs +0 -2
  284. package/esm2022/shared/interfaces/login-config.interface.mjs +0 -2
  285. package/esm2022/shared/interfaces/register-config.interface.mjs +0 -2
  286. package/esm2022/shared/interfaces/single-filter-input.interface.mjs +0 -2
  287. package/esm2022/shared/interfaces/sort-input.interface.mjs +0 -2
  288. package/esm2022/shared/interfaces/time-components.interface.mjs +0 -2
  289. package/esm2022/shared/interfaces/toast.interface.mjs +0 -2
  290. package/esm2022/shared/lenne.tech-ng-base-shared.mjs +0 -5
  291. package/esm2022/shared/public-api.mjs +0 -74
  292. package/esm2022/shared/services/auth.service.mjs +0 -152
  293. package/esm2022/shared/services/breadcrumb.service.mjs +0 -69
  294. package/esm2022/shared/services/cms.service.mjs +0 -82
  295. package/esm2022/shared/services/file.service.mjs +0 -86
  296. package/esm2022/shared/services/forms.service.mjs +0 -61
  297. package/esm2022/shared/services/graphql-meta.service.mjs +0 -132
  298. package/esm2022/shared/services/graphql-plus.service.mjs +0 -93
  299. package/esm2022/shared/services/graphql.service.mjs +0 -510
  300. package/esm2022/shared/services/image.service.mjs +0 -102
  301. package/esm2022/shared/services/loader.service.mjs +0 -78
  302. package/esm2022/shared/services/navigation.service.mjs +0 -38
  303. package/esm2022/shared/services/scroll.service.mjs +0 -207
  304. package/esm2022/shared/services/storage.service.mjs +0 -149
  305. package/esm2022/shared/services/toast.service.mjs +0 -39
  306. package/esm2022/shared/services/user.service.mjs +0 -160
  307. package/esm2022/shared/services/ws.service.mjs +0 -17
  308. package/esm2022/shared/types/graphql-fields.type.mjs +0 -2
  309. package/esm2022/shared/types/storage.type.mjs +0 -2
  310. package/esm2022/shared/validators/email.validator.mjs +0 -7
  311. package/esm2022/shared/validators/match.validator.mjs +0 -22
  312. package/esm2022/shared/validators/secure-password.validator.mjs +0 -7
  313. package/esm2022/src/base.module.mjs +0 -64
  314. package/esm2022/src/decorators/auto-unsubscribe.decorator.mjs +0 -23
  315. package/esm2022/src/directives/lazy-load.directive.mjs +0 -48
  316. package/esm2022/src/directives/match-height.directive.mjs +0 -92
  317. package/esm2022/src/directives/resizable.directive.mjs +0 -92
  318. package/esm2022/src/factories/apollo-options.factory.mjs +0 -108
  319. package/esm2022/src/functions/extract-files.function.mjs +0 -87
  320. package/esm2022/src/guards/auth.guard.mjs +0 -91
  321. package/esm2022/src/guards/can-deactivate.guard.mjs +0 -16
  322. package/esm2022/src/guards/logout.guard.mjs +0 -34
  323. package/esm2022/src/pipes/date-ago.pipe.mjs +0 -64
  324. package/esm2022/src/pipes/safe-html.pipe.mjs +0 -22
  325. package/esm2022/src/services/fullscreen.service.mjs +0 -51
  326. package/esm2022/src/services/seo.service.mjs +0 -109
  327. package/esm2022/src/services/theme.service.mjs +0 -44
  328. package/fesm2022/lenne.tech-ng-base-base-cms.mjs +0 -251
  329. package/fesm2022/lenne.tech-ng-base-base-cms.mjs.map +0 -1
  330. package/fesm2022/lenne.tech-ng-base-base-components.mjs +0 -2917
  331. package/fesm2022/lenne.tech-ng-base-base-components.mjs.map +0 -1
  332. package/fesm2022/lenne.tech-ng-base-base-prototype.mjs +0 -505
  333. package/fesm2022/lenne.tech-ng-base-base-prototype.mjs.map +0 -1
  334. package/fesm2022/lenne.tech-ng-base-shared.mjs +0 -2863
  335. package/fesm2022/lenne.tech-ng-base-shared.mjs.map +0 -1
  336. package/index.d.ts +0 -5
  337. package/public-api.d.ts +0 -17
  338. package/shared/classes/auth.class.d.ts +0 -10
  339. package/shared/classes/basic-user.class.d.ts +0 -27
  340. package/shared/classes/graphql-enum.class.d.ts +0 -7
  341. package/shared/classes/graphql-meta.class.d.ts +0 -66
  342. package/shared/classes/graphql-type.class.d.ts +0 -13
  343. package/shared/classes/helper.class.d.ts +0 -99
  344. package/shared/classes/sort.class.d.ts +0 -12
  345. package/shared/classes/standard.class.d.ts +0 -32
  346. package/shared/decorators/debounce.decorator.d.ts +0 -1
  347. package/shared/enums/comparison-operator.enum.d.ts +0 -14
  348. package/shared/enums/graphql-request-type.enum.d.ts +0 -8
  349. package/shared/enums/logical-operator.enum.d.ts +0 -8
  350. package/shared/enums/skeleton-type.enum.d.ts +0 -5
  351. package/shared/enums/sort-order.enum.d.ts +0 -4
  352. package/shared/enums/toast-type.enum.d.ts +0 -6
  353. package/shared/functions/ws-client.function.d.ts +0 -5
  354. package/shared/index.d.ts +0 -5
  355. package/shared/interfaces/base-module-config.interface.d.ts +0 -61
  356. package/shared/interfaces/breadcrumb.interface.d.ts +0 -4
  357. package/shared/interfaces/can-component-deactivate.interface.d.ts +0 -7
  358. package/shared/interfaces/cms-enum-values.interface.d.ts +0 -4
  359. package/shared/interfaces/cms-field-config.interface.d.ts +0 -45
  360. package/shared/interfaces/cms-model-config.interface.d.ts +0 -14
  361. package/shared/interfaces/cms-table-field.interface.d.ts +0 -10
  362. package/shared/interfaces/combined-filter-input.interface.d.ts +0 -9
  363. package/shared/interfaces/compress-options.interface.d.ts +0 -15
  364. package/shared/interfaces/cropping-options.interface.d.ts +0 -12
  365. package/shared/interfaces/field-object.interface.d.ts +0 -6
  366. package/shared/interfaces/field.interface.d.ts +0 -5
  367. package/shared/interfaces/filter-input.interface.d.ts +0 -9
  368. package/shared/interfaces/find-args.interface.d.ts +0 -11
  369. package/shared/interfaces/graphql-crud-type.interface.d.ts +0 -7
  370. package/shared/interfaces/graphql-fields-object.interface.d.ts +0 -6
  371. package/shared/interfaces/graphql-options.interface.d.ts +0 -11
  372. package/shared/interfaces/graphql-plus-options.interfacen.d.ts +0 -9
  373. package/shared/interfaces/graphql-type-collection.interface.d.ts +0 -7
  374. package/shared/interfaces/login-config.interface.d.ts +0 -6
  375. package/shared/interfaces/register-config.interface.d.ts +0 -4
  376. package/shared/interfaces/single-filter-input.interface.d.ts +0 -11
  377. package/shared/interfaces/sort-input.interface.d.ts +0 -5
  378. package/shared/interfaces/time-components.interface.d.ts +0 -6
  379. package/shared/interfaces/toast.interface.d.ts +0 -8
  380. package/shared/public-api.d.ts +0 -65
  381. package/shared/services/auth.service.d.ts +0 -44
  382. package/shared/services/breadcrumb.service.d.ts +0 -32
  383. package/shared/services/cms.service.d.ts +0 -27
  384. package/shared/services/file.service.d.ts +0 -22
  385. package/shared/services/forms.service.d.ts +0 -14
  386. package/shared/services/graphql-meta.service.d.ts +0 -45
  387. package/shared/services/graphql-plus.service.d.ts +0 -27
  388. package/shared/services/graphql.service.d.ts +0 -71
  389. package/shared/services/image.service.d.ts +0 -27
  390. package/shared/services/loader.service.d.ts +0 -32
  391. package/shared/services/navigation.service.d.ts +0 -15
  392. package/shared/services/scroll.service.d.ts +0 -85
  393. package/shared/services/storage.service.d.ts +0 -49
  394. package/shared/services/toast.service.d.ts +0 -15
  395. package/shared/services/user.service.d.ts +0 -77
  396. package/shared/services/ws.service.d.ts +0 -8
  397. package/shared/types/graphql-fields.type.d.ts +0 -5
  398. package/shared/types/storage.type.d.ts +0 -4
  399. package/shared/validators/email.validator.d.ts +0 -1
  400. package/shared/validators/match.validator.d.ts +0 -4
  401. package/shared/validators/secure-password.validator.d.ts +0 -1
  402. package/src/base.module.d.ts +0 -16
  403. package/src/decorators/auto-unsubscribe.decorator.d.ts +0 -5
  404. package/src/directives/lazy-load.directive.d.ts +0 -17
  405. package/src/directives/match-height.directive.d.ts +0 -20
  406. package/src/directives/resizable.directive.d.ts +0 -26
  407. package/src/factories/apollo-options.factory.d.ts +0 -16
  408. package/src/functions/extract-files.function.d.ts +0 -15
  409. package/src/guards/auth.guard.d.ts +0 -27
  410. package/src/guards/can-deactivate.guard.d.ts +0 -10
  411. package/src/guards/logout.guard.d.ts +0 -16
  412. package/src/pipes/date-ago.pipe.d.ts +0 -34
  413. package/src/pipes/safe-html.pipe.d.ts +0 -13
  414. package/src/services/fullscreen.service.d.ts +0 -18
  415. package/src/services/seo.service.d.ts +0 -50
  416. package/src/services/theme.service.d.ts +0 -17
@@ -1,2917 +0,0 @@
1
- import * as i0 from '@angular/core';
2
- import { EventEmitter, Directive, Inject, Input, Output, HostListener, Pipe, Component, ViewChild, ViewContainerRef } from '@angular/core';
3
- import { DOCUMENT, NgClass, NgStyle, AsyncPipe, DatePipe } from '@angular/common';
4
- import * as i1 from '@lenne.tech/ng-base/shared';
5
- import { Sort, ToastType, SortOrderEnum, GraphQLRequestType, BasicUser, fullEmail, securePasswordValidator, Validation } from '@lenne.tech/ng-base/shared';
6
- import * as i1$1 from '@angular/forms';
7
- import { ReactiveFormsModule, FormControl, FormsModule, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
8
- import { trigger, transition, query, style, animate } from '@angular/animations';
9
- import { Subscription } from 'rxjs';
10
- import * as i2 from '@angular/router';
11
- import { RouterLink } from '@angular/router';
12
- import { ImageCropperComponent } from 'ngx-image-cropper';
13
- import * as i1$2 from '@angular/common/http';
14
-
15
- class SortDirective {
16
- renderer;
17
- targetElem;
18
- baseSort;
19
- baseSorted = new EventEmitter();
20
- constructor(renderer, targetElem, document) {
21
- this.renderer = renderer;
22
- this.targetElem = targetElem;
23
- }
24
- /**
25
- * Sorts columns on click
26
- */
27
- sortColumn() {
28
- const items = document.getElementsByTagName('th');
29
- for (let i = 0; i < items.length; i++) {
30
- items[i].classList.remove('asc');
31
- items[i].classList.remove('desc');
32
- }
33
- const sort = new Sort();
34
- const elem = this.targetElem.nativeElement;
35
- const order = elem.getAttribute('data-order');
36
- const type = elem.getAttribute('data-type');
37
- const property = elem.getAttribute('data-name');
38
- if (order === 'desc') {
39
- this.baseSorted.emit([...this.baseSort].sort(sort.startSort(property, order, type)));
40
- elem.setAttribute('data-order', 'asc');
41
- elem.classList.remove('desc');
42
- elem.classList.add('asc');
43
- }
44
- else {
45
- this.baseSorted.emit([...this.baseSort].sort(sort.startSort(property, order, type)));
46
- elem.setAttribute('data-order', 'desc');
47
- elem.classList.remove('asc');
48
- elem.classList.add('desc');
49
- }
50
- }
51
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SortDirective, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive });
52
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.2", type: SortDirective, isStandalone: true, selector: "[baseSort]", inputs: { baseSort: "baseSort" }, outputs: { baseSorted: "baseSorted" }, host: { listeners: { "click": "sortColumn()" } }, ngImport: i0 });
53
- }
54
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SortDirective, decorators: [{
55
- type: Directive,
56
- args: [{
57
- selector: '[baseSort]',
58
- standalone: true,
59
- }]
60
- }], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ElementRef }, { type: Document, decorators: [{
61
- type: Inject,
62
- args: [DOCUMENT]
63
- }] }], propDecorators: { baseSort: [{
64
- type: Input
65
- }], baseSorted: [{
66
- type: Output
67
- }], sortColumn: [{
68
- type: HostListener,
69
- args: ['click']
70
- }] } });
71
-
72
- class BackButtonDirective {
73
- navigation;
74
- constructor(navigation) {
75
- this.navigation = navigation;
76
- }
77
- onClick() {
78
- this.navigation.back();
79
- }
80
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: BackButtonDirective, deps: [{ token: i1.NavigationService }], target: i0.ɵɵFactoryTarget.Directive });
81
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.2", type: BackButtonDirective, isStandalone: true, selector: "[baseBackButton]", host: { listeners: { "click": "onClick()" } }, ngImport: i0 });
82
- }
83
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: BackButtonDirective, decorators: [{
84
- type: Directive,
85
- args: [{
86
- selector: '[baseBackButton]',
87
- standalone: true,
88
- }]
89
- }], ctorParameters: () => [{ type: i1.NavigationService }], propDecorators: { onClick: [{
90
- type: HostListener,
91
- args: ['click']
92
- }] } });
93
-
94
- /**
95
- * Directive vor context menu
96
- * See https://stackblitz.com/edit/angular-custom-context-menu
97
- */
98
- class ContextMenuDirective {
99
- el;
100
- contextMenuData;
101
- ltContextMenu;
102
- xGap = 0;
103
- yGap = 0;
104
- overflowSpace = 10;
105
- selectors = '*';
106
- clickedElement = null;
107
- previousDisplayStatus = null;
108
- constructor(el) {
109
- this.el = el;
110
- }
111
- ngOnInit() {
112
- if (this.ltContextMenu) {
113
- this.previousDisplayStatus = this.ltContextMenu.style.display;
114
- }
115
- // Add a click listener to the window,
116
- // so that we can close the context menu on left click
117
- window.addEventListener('click', () => {
118
- this.showContextMenu(null, false);
119
- });
120
- // Close context menu when escape is pressed
121
- window.addEventListener('keydown', (e) => {
122
- if (e.code === 'Escape') {
123
- this.showContextMenu(null, false);
124
- }
125
- });
126
- // Add an event listener to listen to the 'contextmenu' event
127
- this.el.nativeElement.addEventListener('contextmenu', (e) => {
128
- // Prevent the default context menu to popup
129
- if (e.preventDefault) {
130
- e.preventDefault();
131
- }
132
- // Shows our own context menu
133
- this.showContextMenu(e, true);
134
- });
135
- }
136
- // Shows or hides our custom context menu, which is specified as a input
137
- showContextMenu(event, show) {
138
- const cm = this.ltContextMenu;
139
- if (cm) {
140
- // Try to open context menu
141
- if (show && event && event instanceof MouseEvent) {
142
- // Get valid clicked structureElement
143
- const clickedElement = event?.target?.closest(this.selectors);
144
- if (clickedElement) {
145
- this.clickedElement = clickedElement;
146
- setTimeout(() => {
147
- // Set display status
148
- cm.style.display = 'block';
149
- // Calculate top position
150
- let top = event.y + this.yGap;
151
- if (top + cm.offsetHeight + 10 > document.documentElement.clientHeight) {
152
- top = document.documentElement.clientHeight - cm.offsetHeight - this.overflowSpace;
153
- }
154
- // Calculate left position
155
- let left = event.x + this.xGap;
156
- if (left + cm.offsetWidth + 10 > document.documentElement.clientWidth) {
157
- left = document.documentElement.clientWidth - cm.offsetWidth - this.overflowSpace;
158
- }
159
- // Set position
160
- cm.style.top = top + 'px';
161
- cm.style.left = left + 'px';
162
- // Show
163
- cm.style.opacity = '1.0';
164
- cm.style['pointer-events'] = 'auto';
165
- }, 1);
166
- return;
167
- }
168
- }
169
- // Close context menu
170
- this.clickedElement = null;
171
- if (this.previousDisplayStatus) {
172
- cm.style.display = this.previousDisplayStatus;
173
- }
174
- cm.style.opacity = '0.01';
175
- cm.style['pointer-events'] = 'none';
176
- }
177
- }
178
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ContextMenuDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
179
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.2", type: ContextMenuDirective, isStandalone: true, selector: "[ltContextMenu]", inputs: { contextMenuData: "contextMenuData", ltContextMenu: "ltContextMenu", xGap: "xGap", yGap: "yGap", overflowSpace: "overflowSpace", selectors: "selectors" }, outputs: { clickedElement: "clickedElement" }, exportAs: ["contextMenuData"], ngImport: i0 });
180
- }
181
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ContextMenuDirective, decorators: [{
182
- type: Directive,
183
- args: [{
184
- selector: '[ltContextMenu]',
185
- exportAs: 'contextMenuData',
186
- standalone: true,
187
- }]
188
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { contextMenuData: [{
189
- type: Input
190
- }], ltContextMenu: [{
191
- type: Input
192
- }], xGap: [{
193
- type: Input
194
- }], yGap: [{
195
- type: Input
196
- }], overflowSpace: [{
197
- type: Input
198
- }], selectors: [{
199
- type: Input
200
- }], clickedElement: [{
201
- type: Output
202
- }] } });
203
-
204
- /**
205
- * Stops propagation of click events
206
- * Inspired by https://stackoverflow.com/a/41001184
207
- */
208
- class StopPropagationDirective {
209
- onClick(event) {
210
- event.stopPropagation();
211
- }
212
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: StopPropagationDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
213
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.2", type: StopPropagationDirective, isStandalone: true, selector: "[baseStopPropagation]", host: { listeners: { "click": "onClick($event)" } }, ngImport: i0 });
214
- }
215
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: StopPropagationDirective, decorators: [{
216
- type: Directive,
217
- args: [{
218
- selector: '[baseStopPropagation]',
219
- standalone: true,
220
- }]
221
- }], propDecorators: { onClick: [{
222
- type: HostListener,
223
- args: ['click', ['$event']]
224
- }] } });
225
-
226
- /**
227
- * Truncate string with ellipses
228
- */
229
- class EllipsesPipe {
230
- transform(value, limit) {
231
- if (!value) {
232
- return null;
233
- }
234
- return value.length > limit ? value.substring(0, limit) + '...' : value;
235
- }
236
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: EllipsesPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
237
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.0.2", ngImport: i0, type: EllipsesPipe, isStandalone: true, name: "baseEllipses" });
238
- }
239
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: EllipsesPipe, decorators: [{
240
- type: Pipe,
241
- args: [{
242
- name: 'baseEllipses',
243
- standalone: true,
244
- }]
245
- }] });
246
-
247
- class InputComponent {
248
- id;
249
- name;
250
- infoText;
251
- label;
252
- placeholder = '';
253
- autocomplete;
254
- tabIndex;
255
- control;
256
- type = 'text';
257
- required = false;
258
- eyeButton = false;
259
- objectPath = '';
260
- enter = new EventEmitter();
261
- /**
262
- * If the control has a value, emit the enter event
263
- */
264
- enterEvent() {
265
- if (this.control?.value) {
266
- this.enter.emit();
267
- }
268
- }
269
- /**
270
- * Switch input type
271
- */
272
- togglePasswordType() {
273
- if (this.type === 'password') {
274
- this.type = 'text';
275
- }
276
- else {
277
- this.type = 'password';
278
- }
279
- }
280
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: InputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
281
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: InputComponent, isStandalone: true, selector: "base-input", inputs: { id: "id", name: "name", infoText: "infoText", label: "label", placeholder: "placeholder", autocomplete: "autocomplete", tabIndex: "tabIndex", control: "control", type: "type", required: "required", eyeButton: "eyeButton", objectPath: "objectPath" }, outputs: { enter: "enter" }, ngImport: i0, template: "<div class=\"form-group mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n <div class=\"input-group\">\n <input\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n class=\"form-control\"\n [type]=\"type\"\n [formControl]=\"control\"\n [required]=\"required\"\n [placeholder]=\"placeholder\"\n [autocomplete]=\"autocomplete ?? null\"\n [tabIndex]=\"tabIndex ?? null\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n (keyup.enter)=\"enterEvent()\"\n [attr.aria-describedby]=\"id + 'eye-button'\"\n [attr.data-cy]=\"name\"\n />\n @if (eyeButton) {\n <button\n class=\"btn btn-outline-primary\"\n type=\"button\"\n [id]=\"id + 'eye-button'\"\n (click)=\"togglePasswordType()\"\n >\n @if (type === 'password') {\n <i class=\"bi bi-eye\"></i>\n }\n @if (type === 'text') {\n <i class=\"bi bi-eye-slash\"></i>\n }\n </button>\n }\n\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n @if (control?.errors?.['minlength']) {\n <div>\n Die Mindestl\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control.errors?.['minlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['maxlength']) {\n <div>\n Die maximale L\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control.errors?.['maxlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['email']) {\n <div>Bitte eine g\u00FCltige E-Mail Adresse angeben.</div>\n }\n @if (control?.errors?.['pattern'] && type === 'tel') {\n <div>Bitte eine g\u00FCltige Telefonnummer angeben.</div>\n }\n @if (control?.errors?.['invalidEmail']) {\n <div>Bitte eine g\u00FCltige E-Mail Adresse angeben.</div>\n }\n @if (control?.errors?.['matching']) {\n <div>Die Passw\u00F6rter m\u00FCssen \u00FCberein stimmen.</div>\n }\n @if (control?.errors?.['invalidSecurePassword']) {\n <div>\n Das Passwort ist zu unsicher. Es muss mind. 8 Zeichen lang sein, ein Gro\u00DFbustabe, ein Kleinbuchstabe, eine Zahl\n und ein Sonderzeichen enthalten.\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".required:after{content:\" *\"}.input-group>.btn{min-width:100px}button{height:100%}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
282
- }
283
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: InputComponent, decorators: [{
284
- type: Component,
285
- args: [{ selector: 'base-input', standalone: true, imports: [
286
- ReactiveFormsModule,
287
- NgClass
288
- ], template: "<div class=\"form-group mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n <div class=\"input-group\">\n <input\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n class=\"form-control\"\n [type]=\"type\"\n [formControl]=\"control\"\n [required]=\"required\"\n [placeholder]=\"placeholder\"\n [autocomplete]=\"autocomplete ?? null\"\n [tabIndex]=\"tabIndex ?? null\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n (keyup.enter)=\"enterEvent()\"\n [attr.aria-describedby]=\"id + 'eye-button'\"\n [attr.data-cy]=\"name\"\n />\n @if (eyeButton) {\n <button\n class=\"btn btn-outline-primary\"\n type=\"button\"\n [id]=\"id + 'eye-button'\"\n (click)=\"togglePasswordType()\"\n >\n @if (type === 'password') {\n <i class=\"bi bi-eye\"></i>\n }\n @if (type === 'text') {\n <i class=\"bi bi-eye-slash\"></i>\n }\n </button>\n }\n\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n @if (control?.errors?.['minlength']) {\n <div>\n Die Mindestl\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control.errors?.['minlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['maxlength']) {\n <div>\n Die maximale L\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control.errors?.['maxlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['email']) {\n <div>Bitte eine g\u00FCltige E-Mail Adresse angeben.</div>\n }\n @if (control?.errors?.['pattern'] && type === 'tel') {\n <div>Bitte eine g\u00FCltige Telefonnummer angeben.</div>\n }\n @if (control?.errors?.['invalidEmail']) {\n <div>Bitte eine g\u00FCltige E-Mail Adresse angeben.</div>\n }\n @if (control?.errors?.['matching']) {\n <div>Die Passw\u00F6rter m\u00FCssen \u00FCberein stimmen.</div>\n }\n @if (control?.errors?.['invalidSecurePassword']) {\n <div>\n Das Passwort ist zu unsicher. Es muss mind. 8 Zeichen lang sein, ein Gro\u00DFbustabe, ein Kleinbuchstabe, eine Zahl\n und ein Sonderzeichen enthalten.\n </div>\n }\n </div>\n }\n </div>\n</div>\n", styles: [".required:after{content:\" *\"}.input-group>.btn{min-width:100px}button{height:100%}\n"] }]
289
- }], propDecorators: { id: [{
290
- type: Input
291
- }], name: [{
292
- type: Input
293
- }], infoText: [{
294
- type: Input
295
- }], label: [{
296
- type: Input
297
- }], placeholder: [{
298
- type: Input
299
- }], autocomplete: [{
300
- type: Input
301
- }], tabIndex: [{
302
- type: Input
303
- }], control: [{
304
- type: Input
305
- }], type: [{
306
- type: Input
307
- }], required: [{
308
- type: Input
309
- }], eyeButton: [{
310
- type: Input
311
- }], objectPath: [{
312
- type: Input
313
- }], enter: [{
314
- type: Output
315
- }] } });
316
-
317
- class SelectComponent {
318
- select;
319
- id;
320
- name;
321
- infoText;
322
- label;
323
- placeholder = '';
324
- objectPath = '';
325
- control;
326
- tabIndex;
327
- required = false;
328
- multiple = false;
329
- search = false;
330
- options = [];
331
- ngOnInit() {
332
- if (this.multiple) {
333
- this.select.nativeElement.setAttribute('multiple', true);
334
- }
335
- }
336
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
337
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: SelectComponent, isStandalone: true, selector: "base-select", inputs: { id: "id", name: "name", infoText: "infoText", label: "label", placeholder: "placeholder", objectPath: "objectPath", control: "control", tabIndex: "tabIndex", required: "required", multiple: "multiple", search: "search", options: "options" }, viewQueries: [{ propertyName: "select", first: true, predicate: ["select"], descendants: true }], ngImport: i0, template: "<div class=\"form-group mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n <select\n #select\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n class=\"form-select\"\n [attr.aria-label]=\"placeholder\"\n [attr.data-live-search]=\"search\"\n [formControl]=\"control\"\n [required]=\"required\"\n [tabIndex]=\"tabIndex ?? null\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n >\n <option [ngValue]=\"null\" disabled selected>{{ placeholder }}</option>\n @for (option of options; track option) {\n <option [ngValue]=\"option.value\">\n {{ option.text }}\n </option>\n }\n </select>\n\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n @if (control?.errors?.['minlength']) {\n <div>\n Die Mindestl\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control?.errors?.['minlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['maxlength']) {\n <div>\n Die maximale L\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control?.errors?.['maxlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['email']) {\n <div>Bitte eine g\u00FCltige E-Mail Adresse angeben.</div>\n }\n </div>\n }\n</div>\n", styles: [".required:after{content:\" *\"}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
338
- }
339
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SelectComponent, decorators: [{
340
- type: Component,
341
- args: [{ selector: 'base-select', standalone: true, imports: [
342
- NgClass,
343
- ReactiveFormsModule,
344
- ], template: "<div class=\"form-group mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n <select\n #select\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n class=\"form-select\"\n [attr.aria-label]=\"placeholder\"\n [attr.data-live-search]=\"search\"\n [formControl]=\"control\"\n [required]=\"required\"\n [tabIndex]=\"tabIndex ?? null\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n >\n <option [ngValue]=\"null\" disabled selected>{{ placeholder }}</option>\n @for (option of options; track option) {\n <option [ngValue]=\"option.value\">\n {{ option.text }}\n </option>\n }\n </select>\n\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n @if (control?.errors?.['minlength']) {\n <div>\n Die Mindestl\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control?.errors?.['minlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['maxlength']) {\n <div>\n Die maximale L\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control?.errors?.['maxlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['email']) {\n <div>Bitte eine g\u00FCltige E-Mail Adresse angeben.</div>\n }\n </div>\n }\n</div>\n", styles: [".required:after{content:\" *\"}\n"] }]
345
- }], propDecorators: { select: [{
346
- type: ViewChild,
347
- args: ['select']
348
- }], id: [{
349
- type: Input
350
- }], name: [{
351
- type: Input
352
- }], infoText: [{
353
- type: Input
354
- }], label: [{
355
- type: Input
356
- }], placeholder: [{
357
- type: Input
358
- }], objectPath: [{
359
- type: Input
360
- }], control: [{
361
- type: Input
362
- }], tabIndex: [{
363
- type: Input
364
- }], required: [{
365
- type: Input
366
- }], multiple: [{
367
- type: Input
368
- }], search: [{
369
- type: Input
370
- }], options: [{
371
- type: Input
372
- }] } });
373
-
374
- class CheckboxComponent {
375
- id;
376
- name;
377
- label;
378
- value;
379
- control;
380
- required = false;
381
- objectPath = '';
382
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: CheckboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
383
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: CheckboxComponent, isStandalone: true, selector: "base-checkbox", inputs: { id: "id", name: "name", label: "label", value: "value", control: "control", required: "required", objectPath: "objectPath" }, ngImport: i0, template: "<div class=\"form-check\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n [formControl]=\"control\"\n [required]=\"required\"\n [value]=\"value\"\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n />\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">\n @if (label) {\n {{ label }}\n }\n <ng-content></ng-content>\n </label>\n\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback ms-0 m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n </div>\n }\n</div>\n", styles: [".required:after{content:\" *\"}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.CheckboxRequiredValidator, selector: "input[type=checkbox][required][formControlName],input[type=checkbox][required][formControl],input[type=checkbox][required][ngModel]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
384
- }
385
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: CheckboxComponent, decorators: [{
386
- type: Component,
387
- args: [{ selector: 'base-checkbox', standalone: true, imports: [
388
- ReactiveFormsModule,
389
- NgClass
390
- ], template: "<div class=\"form-check\">\n <input\n class=\"form-check-input\"\n type=\"checkbox\"\n [formControl]=\"control\"\n [required]=\"required\"\n [value]=\"value\"\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n />\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">\n @if (label) {\n {{ label }}\n }\n <ng-content></ng-content>\n </label>\n\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback ms-0 m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n </div>\n }\n</div>\n", styles: [".required:after{content:\" *\"}\n"] }]
391
- }], propDecorators: { id: [{
392
- type: Input
393
- }], name: [{
394
- type: Input
395
- }], label: [{
396
- type: Input
397
- }], value: [{
398
- type: Input
399
- }], control: [{
400
- type: Input
401
- }], required: [{
402
- type: Input
403
- }], objectPath: [{
404
- type: Input
405
- }] } });
406
-
407
- class TextareaComponent {
408
- textarea;
409
- id;
410
- name;
411
- infoText;
412
- label = '';
413
- placeholder = '';
414
- control;
415
- rows = 5;
416
- required = false;
417
- autoHeight = true;
418
- tabIndex;
419
- objectPath = '';
420
- ngOnChanges(changes) {
421
- if (changes['control'] && this.autoHeight) {
422
- setTimeout(() => {
423
- this.adjustInitHeight();
424
- }, 350);
425
- }
426
- }
427
- adjustInitHeight() {
428
- if (!this.textarea || !this.autoHeight) {
429
- return;
430
- }
431
- this.textarea.nativeElement.style.cssText = 'height:auto;';
432
- this.textarea.nativeElement.style.cssText = 'height:' + (this.textarea.nativeElement.scrollHeight + 5) + 'px';
433
- }
434
- adjustHeight(event) {
435
- if (!this.autoHeight) {
436
- return;
437
- }
438
- setTimeout(() => {
439
- event.target.style.cssText = 'height:auto;';
440
- // for box-sizing other than "content-box" use:
441
- // el.style.cssText = '-moz-box-sizing:content-box';
442
- event.target.style.cssText = 'height:' + (event.target.scrollHeight + 5) + 'px';
443
- }, 0);
444
- }
445
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: TextareaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
446
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: TextareaComponent, isStandalone: true, selector: "base-textarea", inputs: { id: "id", name: "name", infoText: "infoText", label: "label", placeholder: "placeholder", control: "control", rows: "rows", required: "required", autoHeight: "autoHeight", tabIndex: "tabIndex", objectPath: "objectPath" }, viewQueries: [{ propertyName: "textarea", first: true, predicate: ["textarea"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"form-group mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n\n <textarea\n #textarea\n class=\"form-control\"\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n [rows]=\"rows\"\n (input)=\"adjustHeight($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [tabIndex]=\"tabIndex ?? null\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n ></textarea>\n\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n @if (control?.errors?.['minlength']) {\n <div>\n Die Mindestl\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control?.errors?.['minlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['maxlength']) {\n <div>\n Die maximale L\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control?.errors?.['maxlength']?.requiredLength}} Zeichen.\n </div>\n }\n </div>\n }\n</div>\n", styles: [".required:after{content:\" *\"}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
447
- }
448
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: TextareaComponent, decorators: [{
449
- type: Component,
450
- args: [{ selector: 'base-textarea', standalone: true, imports: [
451
- NgClass,
452
- ReactiveFormsModule
453
- ], template: "<div class=\"form-group mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n\n <textarea\n #textarea\n class=\"form-control\"\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n [rows]=\"rows\"\n (input)=\"adjustHeight($event)\"\n [placeholder]=\"placeholder\"\n [formControl]=\"control\"\n [tabIndex]=\"tabIndex ?? null\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n ></textarea>\n\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n @if (control?.errors?.['minlength']) {\n <div>\n Die Mindestl\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control?.errors?.['minlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['maxlength']) {\n <div>\n Die maximale L\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control?.errors?.['maxlength']?.requiredLength}} Zeichen.\n </div>\n }\n </div>\n }\n</div>\n", styles: [".required:after{content:\" *\"}\n"] }]
454
- }], propDecorators: { textarea: [{
455
- type: ViewChild,
456
- args: ['textarea']
457
- }], id: [{
458
- type: Input
459
- }], name: [{
460
- type: Input
461
- }], infoText: [{
462
- type: Input
463
- }], label: [{
464
- type: Input
465
- }], placeholder: [{
466
- type: Input
467
- }], control: [{
468
- type: Input
469
- }], rows: [{
470
- type: Input
471
- }], required: [{
472
- type: Input
473
- }], autoHeight: [{
474
- type: Input
475
- }], tabIndex: [{
476
- type: Input
477
- }], objectPath: [{
478
- type: Input
479
- }] } });
480
-
481
- class TagsComponent {
482
- id;
483
- name;
484
- infoText;
485
- label = '';
486
- placeholder = '';
487
- autocomplete;
488
- tabIndex;
489
- control;
490
- options;
491
- custom = true;
492
- removeByKey = true;
493
- required = false;
494
- objectPath = '';
495
- validators = [];
496
- inputValue = '';
497
- selectedElement;
498
- /**
499
- * Convert id into text field
500
- */
501
- getReadableTag(tag) {
502
- if (this.control?.value && this.options) {
503
- const foundElement = this.options.find((e) => e.value === tag);
504
- if (foundElement) {
505
- return foundElement.text;
506
- }
507
- else {
508
- return tag;
509
- }
510
- }
511
- else {
512
- return tag;
513
- }
514
- }
515
- /**
516
- * Listen to enter keys
517
- *
518
- * @param event
519
- */
520
- enterKey(event) {
521
- this.selectedElement = event.target;
522
- if (this.removeByKey && event.code === 'Backspace' && !this.inputValue) {
523
- this.removeTag();
524
- return;
525
- }
526
- else {
527
- if (event.code === 'Comma' || event.code === 'Space' || event.code === 'Enter') {
528
- this.addTag(this.inputValue);
529
- }
530
- }
531
- }
532
- /**
533
- * Add new tag
534
- *
535
- * @param tag
536
- * @param reset
537
- * @param isOption
538
- */
539
- addTag(tag, reset = true, isOption = false) {
540
- if (tag.endsWith(',') || tag.endsWith(' ')) {
541
- tag = tag.slice(0, -1);
542
- }
543
- const temp = new FormControl(tag, this.validators);
544
- this.control.setErrors({ ...this.control.errors, ...temp.errors });
545
- if (temp.invalid) {
546
- return;
547
- }
548
- // Check if user copy string into input
549
- if (tag.split(' ').length > 1 && !isOption) {
550
- const tags = tag.split(' ');
551
- for (let i = 0; i < tags.length; i++) {
552
- this.addTag(tags[i], i + 1 === tags.length);
553
- }
554
- return;
555
- }
556
- // Check if user copy string into input
557
- if (tag.split(',').length > 1 && !isOption) {
558
- const tags = tag.split(',');
559
- for (let i = 0; i < tags.length; i++) {
560
- this.addTag(tags[i], i + 1 === tags.length);
561
- }
562
- return;
563
- }
564
- let foundOption;
565
- if (!this.options) {
566
- foundOption = { value: tag, text: tag };
567
- }
568
- else {
569
- foundOption = this.options.find((e) => e.text === tag);
570
- }
571
- if (!this.custom && !foundOption) {
572
- return;
573
- }
574
- this.selectedElement = null;
575
- if (!this.control.value) {
576
- this.control.setValue([]);
577
- }
578
- if (this.control.value?.length === 0) {
579
- this.control.setValue([foundOption.value]);
580
- if (reset) {
581
- this.inputValue = '';
582
- }
583
- this.control.setErrors(null);
584
- return;
585
- }
586
- if (tag.length > 0 &&
587
- this.control.value &&
588
- !this.control.value.some((item) => item === foundOption.value)) {
589
- this.control.setValue([...this.control.value, foundOption.value]);
590
- if (reset) {
591
- this.inputValue = '';
592
- }
593
- this.control.setErrors(null);
594
- }
595
- if (this.control.value.some((item) => item === foundOption.value)) {
596
- if (reset) {
597
- this.inputValue = '';
598
- }
599
- }
600
- }
601
- /**
602
- * Remove tag
603
- *
604
- * @param tag
605
- */
606
- removeTag(tag) {
607
- let foundOption;
608
- if (!this.options) {
609
- foundOption = { value: tag, text: tag };
610
- }
611
- else {
612
- foundOption = this.options.find((e) => e.value === tag);
613
- }
614
- this.control.markAsTouched();
615
- if (foundOption) {
616
- this.control.setValue(this.control.value?.filter((item) => item !== foundOption.value));
617
- }
618
- else if (this.control.value) {
619
- this.control.value.splice(-1);
620
- }
621
- if (this.control.value?.length === 0) {
622
- this.control.setErrors({ ...this.control.errors, required: true });
623
- }
624
- }
625
- /**
626
- * Filter options for drop down
627
- */
628
- filterOptions() {
629
- return this.options
630
- ? this.options.filter((e) => (!this.inputValue || e?.text?.includes(this.inputValue)) && !this.control.value?.includes(e.value))
631
- : [];
632
- }
633
- /**
634
- * Show dropdown focus
635
- */
636
- onFocus(event) {
637
- if (event) {
638
- this.control.setErrors(null);
639
- this.selectedElement = event.target;
640
- }
641
- else {
642
- if (this.inputValue) {
643
- this.addTag(this.inputValue);
644
- }
645
- this.control.markAsTouched();
646
- setTimeout(() => {
647
- if (this.options && this.inputValue) {
648
- if (this.options.find((e) => e.text === this.inputValue)) {
649
- this.addTag(this.inputValue);
650
- this.control.setErrors(null);
651
- }
652
- else {
653
- this.inputValue = '';
654
- }
655
- }
656
- if (!this.control.value || this.control.value?.length === 0) {
657
- this.control.setErrors({ ...this.control.errors, required: true });
658
- }
659
- this.selectedElement = null;
660
- }, 250);
661
- }
662
- }
663
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: TagsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
664
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: TagsComponent, isStandalone: true, selector: "base-tags", inputs: { id: "id", name: "name", infoText: "infoText", label: "label", placeholder: "placeholder", autocomplete: "autocomplete", tabIndex: "tabIndex", control: "control", options: "options", custom: "custom", removeByKey: "removeByKey", required: "required", objectPath: "objectPath", validators: "validators" }, ngImport: i0, template: "@if (control) {\n <div class=\"form-group position-relative\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n @if (control?.value) {\n <div class=\"tag-wrapper\">\n @for (tag of control?.value; track tag) {\n <div class=\"tag\">\n {{ getReadableTag(tag) }}\n <span (click)=\"removeTag(tag)\" data-cy=\"input-tags-item-remove\"><i class=\"bi bi-x-circle\"></i></span>\n </div>\n }\n </div>\n }\n <input [formControl]=\"control\" hidden />\n <input\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n [placeholder]=\"placeholder\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n [(ngModel)]=\"inputValue\"\n [autocomplete]=\"autocomplete ?? null\"\n [tabIndex]=\"tabIndex ?? null\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onFocus()\"\n type=\"text\"\n class=\"form-control\"\n (keyup)=\"enterKey($event)\"\n [attr.data-cy]=\"'input-tags-field'\"\n />\n @if (options && selectedElement) {\n <div class=\"tag-options\">\n @for (option of filterOptions(); track option) {\n <div class=\"tag-option-item\" (click)=\"addTag(option?.text, true, true)\">\n {{ option?.text }}\n </div>\n }\n </div>\n }\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n @if (control?.errors?.['invalidReference'] && inputValue) {\n <div>\n {{ inputValue }} ist kein g\u00FCltiger Wert f\u00FCr {{ label }}.\n </div>\n }\n @if (control?.errors?.['email']) {\n <div>Bitte gib eine g\u00FCltige E-Mail Adresse ein.</div>\n }\n </div>\n }\n </div>\n}\n", styles: [".required:after{content:\" *\"}.tag-options{position:absolute;display:block;top:100%;left:0;right:0;border-top:0;background-color:#fff;box-shadow:0 2px 5px #00000029,0 2px 10px #0000001f;transition:all .5s;z-index:999;max-height:200px;overflow-y:scroll}.tag-options .tag-option-item{padding:10px;cursor:pointer}.tag-options .tag-option-item:hover{color:var(--bs-primary)}.tag-wrapper{display:flex;flex-direction:row;justify-content:flex-start;column-gap:10px;row-gap:10px;flex-wrap:wrap;margin-bottom:10px;position:relative}.tag-wrapper .tag{background-color:var(--bs-light);color:var(--bs-dark);padding:10px 15px;font-style:normal;font-weight:300;font-size:14px;line-height:140%}.tag-wrapper .tag span{margin-left:20px;cursor:pointer}.tag-wrapper .tag span .bi{color:var(--bs-black)}.tag-wrapper .tag span .bi:before{vertical-align:-.175em}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
665
- }
666
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: TagsComponent, decorators: [{
667
- type: Component,
668
- args: [{ selector: 'base-tags', standalone: true, imports: [
669
- NgClass,
670
- ReactiveFormsModule,
671
- FormsModule
672
- ], template: "@if (control) {\n <div class=\"form-group position-relative\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n @if (control?.value) {\n <div class=\"tag-wrapper\">\n @for (tag of control?.value; track tag) {\n <div class=\"tag\">\n {{ getReadableTag(tag) }}\n <span (click)=\"removeTag(tag)\" data-cy=\"input-tags-item-remove\"><i class=\"bi bi-x-circle\"></i></span>\n </div>\n }\n </div>\n }\n <input [formControl]=\"control\" hidden />\n <input\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n [placeholder]=\"placeholder\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n [(ngModel)]=\"inputValue\"\n [autocomplete]=\"autocomplete ?? null\"\n [tabIndex]=\"tabIndex ?? null\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onFocus()\"\n type=\"text\"\n class=\"form-control\"\n (keyup)=\"enterKey($event)\"\n [attr.data-cy]=\"'input-tags-field'\"\n />\n @if (options && selectedElement) {\n <div class=\"tag-options\">\n @for (option of filterOptions(); track option) {\n <div class=\"tag-option-item\" (click)=\"addTag(option?.text, true, true)\">\n {{ option?.text }}\n </div>\n }\n </div>\n }\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n @if (control?.errors?.['invalidReference'] && inputValue) {\n <div>\n {{ inputValue }} ist kein g\u00FCltiger Wert f\u00FCr {{ label }}.\n </div>\n }\n @if (control?.errors?.['email']) {\n <div>Bitte gib eine g\u00FCltige E-Mail Adresse ein.</div>\n }\n </div>\n }\n </div>\n}\n", styles: [".required:after{content:\" *\"}.tag-options{position:absolute;display:block;top:100%;left:0;right:0;border-top:0;background-color:#fff;box-shadow:0 2px 5px #00000029,0 2px 10px #0000001f;transition:all .5s;z-index:999;max-height:200px;overflow-y:scroll}.tag-options .tag-option-item{padding:10px;cursor:pointer}.tag-options .tag-option-item:hover{color:var(--bs-primary)}.tag-wrapper{display:flex;flex-direction:row;justify-content:flex-start;column-gap:10px;row-gap:10px;flex-wrap:wrap;margin-bottom:10px;position:relative}.tag-wrapper .tag{background-color:var(--bs-light);color:var(--bs-dark);padding:10px 15px;font-style:normal;font-weight:300;font-size:14px;line-height:140%}.tag-wrapper .tag span{margin-left:20px;cursor:pointer}.tag-wrapper .tag span .bi{color:var(--bs-black)}.tag-wrapper .tag span .bi:before{vertical-align:-.175em}\n"] }]
673
- }], propDecorators: { id: [{
674
- type: Input
675
- }], name: [{
676
- type: Input
677
- }], infoText: [{
678
- type: Input
679
- }], label: [{
680
- type: Input
681
- }], placeholder: [{
682
- type: Input
683
- }], autocomplete: [{
684
- type: Input
685
- }], tabIndex: [{
686
- type: Input
687
- }], control: [{
688
- type: Input
689
- }], options: [{
690
- type: Input
691
- }], custom: [{
692
- type: Input
693
- }], removeByKey: [{
694
- type: Input
695
- }], required: [{
696
- type: Input
697
- }], objectPath: [{
698
- type: Input
699
- }], validators: [{
700
- type: Input
701
- }] } });
702
-
703
- const toastAnimation = trigger('toastAnimation', [
704
- transition('* <=> *', [
705
- query(':enter', [style({ transform: 'translateX({{ x }})' }), animate('200ms ease-in', style({ transform: 'translateX(0%)' }))], { optional: true }),
706
- query(':leave', animate('200ms ease-in', style({ transform: 'translateX({{ x }})' })), { optional: true }),
707
- ]),
708
- ]);
709
- class ToastComponent {
710
- toastService;
711
- toasts = [];
712
- ToastType = ToastType;
713
- subscriptions = new Subscription();
714
- position = 'bottom-right';
715
- constructor(toastService) {
716
- this.toastService = toastService;
717
- }
718
- ngOnInit() {
719
- this.subscriptions.add(this.toastService.toastsAsObservable.subscribe({
720
- next: (value) => {
721
- this.toasts = value;
722
- },
723
- }));
724
- }
725
- ngOnDestroy() {
726
- this.subscriptions.unsubscribe();
727
- }
728
- getDefaultTitle(toast) {
729
- switch (toast.type) {
730
- case ToastType.ERROR:
731
- return 'Irgendetwas ist schiefgelaufen';
732
- case ToastType.INFO:
733
- return 'Info';
734
- case ToastType.WARNING:
735
- return 'Achtung';
736
- case ToastType.SUCCESS:
737
- return 'Erfolgreich';
738
- }
739
- }
740
- getDefaultDescription(toast) {
741
- switch (toast.type) {
742
- case ToastType.ERROR:
743
- return 'Sollte das Problem länger bestehen bleiben, dann wenden Sie sich bitte\n' + ' an den Support.';
744
- case ToastType.INFO:
745
- return 'Info';
746
- case ToastType.WARNING:
747
- return 'Warning';
748
- case ToastType.SUCCESS:
749
- return 'Die Änderungen wurden erfolgreich gespeichert.';
750
- }
751
- }
752
- reload() {
753
- location.reload();
754
- }
755
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ToastComponent, deps: [{ token: i1.ToastService }], target: i0.ɵɵFactoryTarget.Component });
756
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: ToastComponent, isStandalone: true, selector: "base-toast", inputs: { position: "position" }, ngImport: i0, template: "<div\n class=\"toast-container position-fixed\"\n [ngClass]=\"position\"\n [@toastAnimation]=\"{ value: toasts.length, params: { x: position.includes('left') ? '-100%' : '100%' } }\"\n >\n @for (toast of toasts; track toast) {\n <div\n class=\"toast show\"\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n [ngClass]=\"toast.type\"\n >\n <div class=\"toast-header\">\n <strong class=\"me-auto\">{{ toast?.title ?? getDefaultTitle(toast) }}</strong>\n <button type=\"button\" class=\"btn-close btn-close-white\" data-bs-dismiss=\"toast\" aria-label=\"Close\"></button>\n </div>\n <div class=\"toast-body\">\n {{ toast?.description ?? getDefaultDescription(toast) }}\n @if (toast?.errorCode) {\n <div class=\"mt-2\">\n <code>ERROR: {{ toast.errorCode }}</code>\n </div>\n }\n @if (toast.type === ToastType.ERROR) {\n <div class=\"mt-2\">\n <a class=\"color-secondary\" href=\"#\" (click)=\"reload()\">Seite neu laden</a>\n </div>\n }\n </div>\n </div>\n }\n</div>\n", styles: [":host{--base-toast-z-index: 999}.toast-container{padding:10px;z-index:var(--base-toast-z-index)}.bottom-right{bottom:0;right:0}.top-right{top:0;right:0}.bottom-left{bottom:0;left:0}.top-left{top:0;left:0}.toast{border-radius:0;border:0}.toast a{text-decoration:none;font-weight:600}.toast .toast-header{border-radius:0}.error .toast-header{background-color:var(--bs-danger);color:var(--bs-white)}.warning .toast-header{background-color:var(--bs-warning);color:var(--bs-white)}.info .toast-header{background-color:var(--bs-info);color:var(--bs-white)}.success .toast-header{background-color:var(--bs-success);color:var(--bs-white)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], animations: [toastAnimation] });
757
- }
758
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ToastComponent, decorators: [{
759
- type: Component,
760
- args: [{ selector: 'base-toast', animations: [toastAnimation], standalone: true, imports: [NgClass], template: "<div\n class=\"toast-container position-fixed\"\n [ngClass]=\"position\"\n [@toastAnimation]=\"{ value: toasts.length, params: { x: position.includes('left') ? '-100%' : '100%' } }\"\n >\n @for (toast of toasts; track toast) {\n <div\n class=\"toast show\"\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n [ngClass]=\"toast.type\"\n >\n <div class=\"toast-header\">\n <strong class=\"me-auto\">{{ toast?.title ?? getDefaultTitle(toast) }}</strong>\n <button type=\"button\" class=\"btn-close btn-close-white\" data-bs-dismiss=\"toast\" aria-label=\"Close\"></button>\n </div>\n <div class=\"toast-body\">\n {{ toast?.description ?? getDefaultDescription(toast) }}\n @if (toast?.errorCode) {\n <div class=\"mt-2\">\n <code>ERROR: {{ toast.errorCode }}</code>\n </div>\n }\n @if (toast.type === ToastType.ERROR) {\n <div class=\"mt-2\">\n <a class=\"color-secondary\" href=\"#\" (click)=\"reload()\">Seite neu laden</a>\n </div>\n }\n </div>\n </div>\n }\n</div>\n", styles: [":host{--base-toast-z-index: 999}.toast-container{padding:10px;z-index:var(--base-toast-z-index)}.bottom-right{bottom:0;right:0}.top-right{top:0;right:0}.bottom-left{bottom:0;left:0}.top-left{top:0;left:0}.toast{border-radius:0;border:0}.toast a{text-decoration:none;font-weight:600}.toast .toast-header{border-radius:0}.error .toast-header{background-color:var(--bs-danger);color:var(--bs-white)}.warning .toast-header{background-color:var(--bs-warning);color:var(--bs-white)}.info .toast-header{background-color:var(--bs-info);color:var(--bs-white)}.success .toast-header{background-color:var(--bs-success);color:var(--bs-white)}\n"] }]
761
- }], ctorParameters: () => [{ type: i1.ToastService }], propDecorators: { position: [{
762
- type: Input
763
- }] } });
764
-
765
- class LoadingBarComponent {
766
- ngZone;
767
- changeDetectorRef;
768
- loaderService;
769
- manuallyLoading = false;
770
- loading = false;
771
- subscription = new Subscription();
772
- constructor(ngZone, changeDetectorRef, loaderService) {
773
- this.ngZone = ngZone;
774
- this.changeDetectorRef = changeDetectorRef;
775
- this.loaderService = loaderService;
776
- }
777
- ngOnInit() {
778
- this.ngZone.runOutsideAngular(() => {
779
- this.subscription.add(this.loaderService.isLoading.subscribe((value) => {
780
- this.loading = value;
781
- this.changeDetectorRef.detectChanges();
782
- }));
783
- });
784
- }
785
- ngOnDestroy() {
786
- this.subscription.unsubscribe();
787
- }
788
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: LoadingBarComponent, deps: [{ token: i0.NgZone }, { token: i0.ChangeDetectorRef }, { token: i1.LoaderService }], target: i0.ɵɵFactoryTarget.Component });
789
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: LoadingBarComponent, selector: "base-loading-bar", inputs: { manuallyLoading: "manuallyLoading" }, ngImport: i0, template: "@if (loading || manuallyLoading) {\n <div class=\"slider\">\n <div class=\"line\"></div>\n <div class=\"subline inc\"></div>\n <div class=\"subline dec\"></div>\n </div>\n}\n", styles: [":host{--base-loading-bar-color: #f28c18;--base-loading-bar-height: 5px;--base-loading-bar-z-index: 999999999}.slider{position:fixed;left:0;right:0;height:var(--base-loading-bar-height);overflow-x:hidden;z-index:var(--base-loading-bar-z-index);background-color:#fff}.line{position:absolute;opacity:.6;background:var(--base-loading-bar-color);width:150%;height:var(--base-loading-bar-height)}.subline{position:absolute;background:var(--base-loading-bar-color);height:var(--base-loading-bar-height)}.inc{animation:increase 2s infinite}.dec{animation:decrease 2s .5s infinite}@keyframes increase{0%{left:-5%;width:5%}to{left:130%;width:100%}}@keyframes decrease{0%{left:-80%;width:80%}to{left:110%;width:10%}}\n"] });
790
- }
791
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: LoadingBarComponent, decorators: [{
792
- type: Component,
793
- args: [{ selector: 'base-loading-bar', template: "@if (loading || manuallyLoading) {\n <div class=\"slider\">\n <div class=\"line\"></div>\n <div class=\"subline inc\"></div>\n <div class=\"subline dec\"></div>\n </div>\n}\n", styles: [":host{--base-loading-bar-color: #f28c18;--base-loading-bar-height: 5px;--base-loading-bar-z-index: 999999999}.slider{position:fixed;left:0;right:0;height:var(--base-loading-bar-height);overflow-x:hidden;z-index:var(--base-loading-bar-z-index);background-color:#fff}.line{position:absolute;opacity:.6;background:var(--base-loading-bar-color);width:150%;height:var(--base-loading-bar-height)}.subline{position:absolute;background:var(--base-loading-bar-color);height:var(--base-loading-bar-height)}.inc{animation:increase 2s infinite}.dec{animation:decrease 2s .5s infinite}@keyframes increase{0%{left:-5%;width:5%}to{left:130%;width:100%}}@keyframes decrease{0%{left:-80%;width:80%}to{left:110%;width:10%}}\n"] }]
794
- }], ctorParameters: () => [{ type: i0.NgZone }, { type: i0.ChangeDetectorRef }, { type: i1.LoaderService }], propDecorators: { manuallyLoading: [{
795
- type: Input
796
- }] } });
797
-
798
- class SkeletonLoaderComponent {
799
- type;
800
- count = 1;
801
- size = 18;
802
- convertToArray(value) {
803
- if (!value) {
804
- return [];
805
- }
806
- const res = [];
807
- for (let i = 0; i < value; i++) {
808
- res.push(i);
809
- }
810
- return res;
811
- }
812
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SkeletonLoaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
813
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: SkeletonLoaderComponent, isStandalone: true, selector: "base-skeleton-loader", inputs: { type: "type", count: "count", size: "size" }, ngImport: i0, template: "@if (type) {\n @switch (type) {\n @case ('LINE') {\n <div>\n <div class=\"line-loader-wrapper\">\n @for (key of convertToArray(count); track key) {\n <div class=\"line-loader\"></div>\n }\n </div>\n </div>\n }\n @case ('RECTANGLE') {\n <div>\n <div class=\"line-rectangle\" [ngStyle]=\"{ height: size + 'px' }\"></div>\n </div>\n }\n @case ('AVATAR') {\n <div>\n <div class=\"line-avatar\" [ngStyle]=\"{ height: size + 'px', width: size + 'px' }\"></div>\n </div>\n }\n }\n}\n", styles: ["@keyframes shine{to{background-position-x:-200%}}.line-loader-wrapper>:not(:first-of-type){margin-top:10px}.line-loader-wrapper .line-loader{background:linear-gradient(110deg,#ececec 8%,#f5f5f5 18%,#ececec 33%);background-size:200% 100%;animation:1.5s shine linear infinite;height:18px;border-radius:5px}.line-rectangle{background:linear-gradient(110deg,#ececec 8%,#f5f5f5 18%,#ececec 33%);background-size:200% 100%;animation:1.5s shine linear infinite;border-radius:5px}.line-avatar{background:linear-gradient(110deg,#ececec 8%,#f5f5f5 18%,#ececec 33%);background-size:200% 100%;animation:1.5s shine linear infinite;border-radius:50%}\n"], dependencies: [{ kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
814
- }
815
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: SkeletonLoaderComponent, decorators: [{
816
- type: Component,
817
- args: [{ selector: 'base-skeleton-loader', standalone: true, imports: [
818
- NgStyle
819
- ], template: "@if (type) {\n @switch (type) {\n @case ('LINE') {\n <div>\n <div class=\"line-loader-wrapper\">\n @for (key of convertToArray(count); track key) {\n <div class=\"line-loader\"></div>\n }\n </div>\n </div>\n }\n @case ('RECTANGLE') {\n <div>\n <div class=\"line-rectangle\" [ngStyle]=\"{ height: size + 'px' }\"></div>\n </div>\n }\n @case ('AVATAR') {\n <div>\n <div class=\"line-avatar\" [ngStyle]=\"{ height: size + 'px', width: size + 'px' }\"></div>\n </div>\n }\n }\n}\n", styles: ["@keyframes shine{to{background-position-x:-200%}}.line-loader-wrapper>:not(:first-of-type){margin-top:10px}.line-loader-wrapper .line-loader{background:linear-gradient(110deg,#ececec 8%,#f5f5f5 18%,#ececec 33%);background-size:200% 100%;animation:1.5s shine linear infinite;height:18px;border-radius:5px}.line-rectangle{background:linear-gradient(110deg,#ececec 8%,#f5f5f5 18%,#ececec 33%);background-size:200% 100%;animation:1.5s shine linear infinite;border-radius:5px}.line-avatar{background:linear-gradient(110deg,#ececec 8%,#f5f5f5 18%,#ececec 33%);background-size:200% 100%;animation:1.5s shine linear infinite;border-radius:50%}\n"] }]
820
- }], propDecorators: { type: [{
821
- type: Input
822
- }], count: [{
823
- type: Input
824
- }], size: [{
825
- type: Input
826
- }] } });
827
-
828
- class BreadcrumbComponent {
829
- breadcrumbService;
830
- breadcrumbs$;
831
- constructor(breadcrumbService) {
832
- this.breadcrumbService = breadcrumbService;
833
- this.breadcrumbs$ = breadcrumbService.breadcrumbsObservable;
834
- }
835
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: BreadcrumbComponent, deps: [{ token: i1.BreadcrumbService }], target: i0.ɵɵFactoryTarget.Component });
836
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: BreadcrumbComponent, isStandalone: true, selector: "base-breadcrumb", ngImport: i0, template: "<ul class=\"pt-3\">\n @for (breadcrumb of breadcrumbs$ | async; track breadcrumb) {\n <li>\n <a [routerLink]=\"breadcrumb.url\">{{ breadcrumb.label }}</a>\n </li>\n }\n</ul>\n", styles: ["ul{list-style:none;margin:0;padding:0}ul li{display:inline}ul li:before{content:\"|\";margin:0 6px}ul li:first-child:before{content:\"\";margin:0}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] });
837
- }
838
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: BreadcrumbComponent, decorators: [{
839
- type: Component,
840
- args: [{ selector: 'base-breadcrumb', standalone: true, imports: [
841
- RouterLink,
842
- AsyncPipe
843
- ], template: "<ul class=\"pt-3\">\n @for (breadcrumb of breadcrumbs$ | async; track breadcrumb) {\n <li>\n <a [routerLink]=\"breadcrumb.url\">{{ breadcrumb.label }}</a>\n </li>\n }\n</ul>\n", styles: ["ul{list-style:none;margin:0;padding:0}ul li{display:inline}ul li:before{content:\"|\";margin:0 6px}ul li:first-child:before{content:\"\";margin:0}\n"] }]
844
- }], ctorParameters: () => [{ type: i1.BreadcrumbService }] });
845
-
846
- class FabButtonComponent {
847
- fabIcon = 'bi-list';
848
- buttons;
849
- toggle = false;
850
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: FabButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
851
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: FabButtonComponent, isStandalone: true, selector: "base-fab-button", inputs: { fabIcon: "fabIcon", buttons: "buttons" }, ngImport: i0, template: "<div class=\"fab-container\" [ngClass]=\"{ 'fab-container-active': toggle }\">\n <div class=\"fab shadow\">\n <div class=\"fab-content\" (click)=\"toggle = !toggle\">\n @if (toggle) {\n <i class=\"bi bi-x-lg\"></i>\n }\n @if (!toggle) {\n <i class=\"bi\" [ngClass]=\"fabIcon\"></i>\n }\n </div>\n </div>\n @for (button of buttons; track button) {\n <div\n class=\"sub-button shadow\"\n (click)=\"button.event.emit(true); toggle = !toggle\"\n [ngStyle]=\"{ backgroundColor: button.color }\"\n >\n <a [routerLink]=\"button.routerLink\">\n <i class=\"bi\" [ngClass]=\"button.icon\"></i>\n </a>\n </div>\n }\n</div>\n", styles: [".fab-container{display:flex;flex-direction:column;justify-content:flex-end;align-items:center;-webkit-user-select:none;user-select:none;position:absolute;bottom:10px;right:10px}.fab-container-active{height:100%}.fab-container-active .sub-button:nth-child(2){transform:translateY(-80px)}.fab-container-active .sub-button:nth-child(3){transform:translateY(-140px)}.fab-container-active .sub-button:nth-child(4){transform:translateY(-200px)}.fab-container-active .sub-button:nth-child(5){transform:translateY(-260px)}.fab-container-active .sub-button:nth-child(6){transform:translateY(-320px)}.fab-container .fab{position:relative;height:60px;width:60px;background-color:var(--bs-primary);border-radius:50%;z-index:2}.fab-container .fab:before{content:\" \";position:absolute;bottom:0;right:0;height:30px;width:30px;background-color:inherit;border-radius:0 0 10px;z-index:-1}.fab-container .fab .fab-content{display:flex;align-items:center;justify-content:center;height:inherit;width:inherit;border-radius:50%}.fab-container .fab .fab-content .bi{color:#fff;font-size:32px}.fab-container .sub-button{position:absolute;display:flex;align-items:center;justify-content:center;bottom:10px;right:10px;height:40px;width:40px;background-color:var(--bs-primary);border-radius:50%;transition:all .3s ease}.fab-container .sub-button:hover{cursor:pointer}.fab-container .sub-button .bi{color:#fff;padding-top:6px}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] });
852
- }
853
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: FabButtonComponent, decorators: [{
854
- type: Component,
855
- args: [{ selector: 'base-fab-button', standalone: true, imports: [
856
- NgClass,
857
- NgStyle,
858
- RouterLink
859
- ], template: "<div class=\"fab-container\" [ngClass]=\"{ 'fab-container-active': toggle }\">\n <div class=\"fab shadow\">\n <div class=\"fab-content\" (click)=\"toggle = !toggle\">\n @if (toggle) {\n <i class=\"bi bi-x-lg\"></i>\n }\n @if (!toggle) {\n <i class=\"bi\" [ngClass]=\"fabIcon\"></i>\n }\n </div>\n </div>\n @for (button of buttons; track button) {\n <div\n class=\"sub-button shadow\"\n (click)=\"button.event.emit(true); toggle = !toggle\"\n [ngStyle]=\"{ backgroundColor: button.color }\"\n >\n <a [routerLink]=\"button.routerLink\">\n <i class=\"bi\" [ngClass]=\"button.icon\"></i>\n </a>\n </div>\n }\n</div>\n", styles: [".fab-container{display:flex;flex-direction:column;justify-content:flex-end;align-items:center;-webkit-user-select:none;user-select:none;position:absolute;bottom:10px;right:10px}.fab-container-active{height:100%}.fab-container-active .sub-button:nth-child(2){transform:translateY(-80px)}.fab-container-active .sub-button:nth-child(3){transform:translateY(-140px)}.fab-container-active .sub-button:nth-child(4){transform:translateY(-200px)}.fab-container-active .sub-button:nth-child(5){transform:translateY(-260px)}.fab-container-active .sub-button:nth-child(6){transform:translateY(-320px)}.fab-container .fab{position:relative;height:60px;width:60px;background-color:var(--bs-primary);border-radius:50%;z-index:2}.fab-container .fab:before{content:\" \";position:absolute;bottom:0;right:0;height:30px;width:30px;background-color:inherit;border-radius:0 0 10px;z-index:-1}.fab-container .fab .fab-content{display:flex;align-items:center;justify-content:center;height:inherit;width:inherit;border-radius:50%}.fab-container .fab .fab-content .bi{color:#fff;font-size:32px}.fab-container .sub-button{position:absolute;display:flex;align-items:center;justify-content:center;bottom:10px;right:10px;height:40px;width:40px;background-color:var(--bs-primary);border-radius:50%;transition:all .3s ease}.fab-container .sub-button:hover{cursor:pointer}.fab-container .sub-button .bi{color:#fff;padding-top:6px}\n"] }]
860
- }], propDecorators: { fabIcon: [{
861
- type: Input
862
- }], buttons: [{
863
- type: Input
864
- }] } });
865
-
866
- class UploadImageComponent {
867
- httpClient;
868
- authService;
869
- fileService;
870
- toastService;
871
- fileInput;
872
- id;
873
- name;
874
- infoText;
875
- label;
876
- supportText = 'Supports: JPEG, JPG, PNG';
877
- required = false;
878
- defaultDragText = 'Drag & Drop';
879
- defaultReleaseText = 'Release to Upload';
880
- preButtonText = 'or';
881
- buttonText = 'browse';
882
- validExtensions = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'];
883
- control;
884
- url = 'http://localhost:3000';
885
- uploadPath = '/files/upload';
886
- deletePath = '/files/';
887
- path = '/files/';
888
- compressOptions;
889
- maxSize;
890
- uploadDirectly = false;
891
- croppingImage = false;
892
- croppingOptions;
893
- objectPath;
894
- mode = 'file';
895
- imageUploaded = new EventEmitter();
896
- imageDeleted = new EventEmitter();
897
- fileChanged = new EventEmitter();
898
- dragActive = false;
899
- dragText = 'Drag & Drop';
900
- selectedFile;
901
- croppedFile;
902
- fileUrl;
903
- loading = false;
904
- croppedInProgress = false;
905
- fileEvent;
906
- cOptions;
907
- constructor(httpClient, authService, fileService, toastService) {
908
- this.httpClient = httpClient;
909
- this.authService = authService;
910
- this.fileService = fileService;
911
- this.toastService = toastService;
912
- }
913
- ngOnInit() {
914
- // Set default options for cropping
915
- this.cOptions = {
916
- maintainAspectRatio: true,
917
- aspectRatio: 4 / 3,
918
- onlyScaleDown: false,
919
- imageQuality: 92,
920
- autoCrop: true,
921
- alignImage: 'center',
922
- format: 'jpeg',
923
- ...this.croppingOptions,
924
- };
925
- }
926
- /**
927
- * The dragOver function is called when the user drags a file over the dropzone. It prevents the default action of the
928
- * event, and sets the dragText to 'Release to Upload' and the dragActive to true
929
- *
930
- * @param event - any - This is the event that is triggered when the user drags a file over the dropzone.
931
- */
932
- dragOver(event) {
933
- event.preventDefault();
934
- this.dragText = this.defaultReleaseText;
935
- this.dragActive = true;
936
- }
937
- /**
938
- * When the user drags a file over the drop zone, the dragActive variable is set to true, which will change the color of
939
- * the drop zone to green
940
- *
941
- * @param event - The event object that is passed to the dragLeave event.
942
- */
943
- dragLeave(event) {
944
- this.dragText = this.defaultDragText;
945
- this.dragActive = false;
946
- }
947
- /**
948
- * The function takes an event as a parameter, prevents the default action of the event, sets the URL of the image to the
949
- * file that was dragged and dropped, sets the text of the drag and drop area to the default text, and sets the
950
- * dragActive variable to false
951
- *
952
- * @param event - any - This is the event that is triggered when the user drags and drops a file.
953
- */
954
- drop(event) {
955
- event.preventDefault();
956
- this.fileInput.nativeElement.files = event.dataTransfer.files;
957
- const e = new Event('change');
958
- this.fileInput.nativeElement.dispatchEvent(e);
959
- this.dragText = 'Drag & Drop';
960
- this.dragActive = false;
961
- }
962
- /**
963
- * It takes a file as an argument, checks if the file type is valid, and if it is, it creates a new FileReader object,
964
- * sets the onload function to set the fileUrl to the result of the fileReader, sets the selectedFile to the file, and
965
- * calls the upload function
966
- *
967
- * @param file - any - this is the file that is selected by the user.
968
- */
969
- setUrl(file) {
970
- const fileReader = new FileReader();
971
- fileReader.onload = (result) => {
972
- this.loading = true;
973
- this.fileUrl = fileReader.result;
974
- this.selectedFile = file;
975
- this.upload();
976
- };
977
- fileReader.readAsDataURL(file);
978
- }
979
- checkFile(file) {
980
- if (this.maxSize && file.size > this.maxSize) {
981
- this.toastService.show({
982
- id: 'image-invalid-size',
983
- type: ToastType.ERROR,
984
- title: 'Fehlgeschlagen',
985
- description: 'Die Datei ist zu groß. Die Datei darf nicht größer als ' +
986
- (this.maxSize / (1024 * 1024)).toFixed(2) +
987
- 'MB sein.',
988
- }, 3500);
989
- return false;
990
- }
991
- else {
992
- if (this.validExtensions.includes(file?.type)) {
993
- return true;
994
- }
995
- else {
996
- this.toastService.show({
997
- id: 'image-invalid-format',
998
- type: ToastType.ERROR,
999
- title: 'Fehlgeschlagen',
1000
- description: 'Das Format der Datei wird nicht unterstützt.',
1001
- }, 3500);
1002
- return false;
1003
- }
1004
- }
1005
- }
1006
- /**
1007
- * The function takes an event as an argument, and then sets the url of the file to the first file in the event's
1008
- * target's files array
1009
- *
1010
- * @param event - any - The event that is triggered when the user selects a file.
1011
- */
1012
- selectFile(event) {
1013
- if (!this.checkFile(event.target.files[0])) {
1014
- return;
1015
- }
1016
- if (this.croppingImage) {
1017
- this.croppedInProgress = true;
1018
- this.fileEvent = event;
1019
- }
1020
- else {
1021
- this.setUrl(event.target.files[0]);
1022
- }
1023
- }
1024
- /**
1025
- * We create a new FormData object, append the selected file to it, and then send it to the server
1026
- */
1027
- async upload() {
1028
- // Prepare file data
1029
- let file = this.selectedFile;
1030
- if (this.compressOptions) {
1031
- file = await this.fileService.compress(file, this.compressOptions);
1032
- }
1033
- const fileData = {
1034
- field: this.objectPath ? this.objectPath + '.' + this.id : this.id,
1035
- file,
1036
- base64: await this.getBase64(file),
1037
- };
1038
- // Emit file data
1039
- this.fileChanged.emit(fileData);
1040
- // Set control value and upload directly if required
1041
- if (this.uploadDirectly) {
1042
- const result = await this.fileService.upload(this.url, this.uploadPath, this.selectedFile);
1043
- if (result?.id) {
1044
- this.control.setValue(result.id);
1045
- this.imageUploaded.emit(result.id);
1046
- }
1047
- }
1048
- else {
1049
- this.control.setValue(this.mode === 'base64' ? fileData.base64 : this.selectedFile);
1050
- }
1051
- // Final processes
1052
- this.control.markAsTouched();
1053
- this.loading = false;
1054
- }
1055
- /**
1056
- * It deletes the file from the server and sets the selectedFile and fileUrl to null
1057
- *
1058
- * @param [id] - The id of the file to delete.
1059
- */
1060
- async deleteFile(id) {
1061
- if (!this.uploadDirectly) {
1062
- this.selectedFile = null;
1063
- this.fileUrl = null;
1064
- this.control.setValue('');
1065
- this.control.markAsTouched();
1066
- this.fileChanged.emit({
1067
- field: this.objectPath ? this.objectPath + '.' + this.id : this.id,
1068
- file: null,
1069
- base64: null,
1070
- });
1071
- return;
1072
- }
1073
- this.loading = true;
1074
- this.selectedFile = null;
1075
- this.fileUrl = null;
1076
- if (id) {
1077
- const result = await this.fileService.delete(this.url, this.deletePath, id);
1078
- if (result) {
1079
- this.selectedFile = null;
1080
- this.fileUrl = null;
1081
- this.control.setValue('');
1082
- this.imageDeleted.emit();
1083
- this.loading = false;
1084
- }
1085
- }
1086
- }
1087
- /**
1088
- * Convert a file into a BASE64 encoded string
1089
- */
1090
- getBase64(file) {
1091
- return new Promise((resolve, reject) => {
1092
- if (!file) {
1093
- return resolve(null);
1094
- }
1095
- const reader = new FileReader();
1096
- reader.readAsDataURL(file);
1097
- reader.onload = () => {
1098
- if (!reader.result) {
1099
- return reject(new Error('Conversion failed'));
1100
- }
1101
- resolve(reader.result.toString());
1102
- };
1103
- reader.onerror = (error) => {
1104
- reject(error);
1105
- };
1106
- });
1107
- }
1108
- base64ToFile(base64, fileName, fileType) {
1109
- return new Promise((resolve, reject) => {
1110
- fetch(base64)
1111
- .then((res) => res.blob())
1112
- .then((blob) => {
1113
- resolve(new File([blob], fileName, { type: fileType }));
1114
- }, (err) => {
1115
- reject(err);
1116
- });
1117
- });
1118
- }
1119
- async imageCropped($event, fileEvent) {
1120
- const file = fileEvent.target.files[0];
1121
- this.croppedFile = await this.base64ToFile($event.base64, file.name, file.type);
1122
- }
1123
- finishedCropping() {
1124
- this.fileEvent = null;
1125
- this.setUrl(this.croppedFile);
1126
- this.croppedInProgress = false;
1127
- this.croppedFile = null;
1128
- }
1129
- loadImageFailed() {
1130
- this.toastService.show({
1131
- id: 'image-invalid-cropped',
1132
- type: ToastType.ERROR,
1133
- title: 'Fehlgeschlagen',
1134
- description: 'Es ist ein Fehler aufgetreten. Bitte probieren Sie es später erneut oder wenden Sie sich an den Support.',
1135
- }, 3500);
1136
- }
1137
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: UploadImageComponent, deps: [{ token: i1$2.HttpClient }, { token: i1.AuthService }, { token: i1.FileService }, { token: i1.ToastService }], target: i0.ɵɵFactoryTarget.Component });
1138
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: UploadImageComponent, isStandalone: true, selector: "base-upload-image", inputs: { id: "id", name: "name", infoText: "infoText", label: "label", supportText: "supportText", required: "required", defaultDragText: "defaultDragText", defaultReleaseText: "defaultReleaseText", preButtonText: "preButtonText", buttonText: "buttonText", validExtensions: "validExtensions", control: "control", url: "url", uploadPath: "uploadPath", deletePath: "deletePath", path: "path", compressOptions: "compressOptions", maxSize: "maxSize", uploadDirectly: "uploadDirectly", croppingImage: "croppingImage", croppingOptions: "croppingOptions", objectPath: "objectPath", mode: "mode" }, outputs: { imageUploaded: "imageUploaded", imageDeleted: "imageDeleted", fileChanged: "fileChanged" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }], ngImport: i0, template: "<div class=\"form-group mt-3\" [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n\n <div\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n class=\"drag-area\"\n (dragover)=\"dragOver($event)\"\n (dragleave)=\"dragLeave($event)\"\n (drop)=\"drop($event)\"\n [ngClass]=\"{ active: dragActive, pointer: !loading, 'is-invalid': control?.invalid && control?.touched }\"\n (click)=\"!croppedInProgress && !loading && fileInput.click()\"\n >\n @if (loading) {\n <div class=\"d-flex justify-content-center\">\n <div class=\"spinner-border text-primary\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n }\n\n @if (selectedFile && !fileEvent && !croppedInProgress && !loading) {\n @if (selectedFile) {\n <img [src]=\"fileUrl\" />\n }\n <button class=\"btn btn-danger\" type=\"button\" (click)=\"deleteFile()\" baseStopPropagation>L\u00F6schen</button>\n }\n\n @if (control?.value && !fileEvent && !selectedFile && !loading && !croppedInProgress) {\n <img [src]=\"control?.value.startsWith('data:') ? control?.value : url + path + control?.value\" />\n <button class=\"btn btn-danger\" type=\"button\" (click)=\"deleteFile(control?.value)\" baseStopPropagation>\n L\u00F6schen\n </button>\n }\n\n @if (!selectedFile && !control?.value && !loading && !fileEvent) {\n <div class=\"icon\">\n <i class=\"bi bi-images\"></i>\n </div>\n <span class=\"header\">{{ dragText }}</span>\n <span class=\"header\"\n >{{ preButtonText }}\n <span class=\"button\" (click)=\"fileInput.click()\" baseStopPropagation>{{ buttonText }}</span></span\n >\n <span class=\"support\" [innerText]=\"supportText\"></span>\n }\n\n <input type=\"file\" (change)=\"selectFile($event)\" [attr.accept]=\"validExtensions?.join(',')\" hidden #fileInput />\n\n @if (croppedInProgress && fileEvent) {\n <image-cropper\n [imageChangedEvent]=\"fileEvent\"\n [maintainAspectRatio]=\"cOptions.maintainAspectRatio\"\n [aspectRatio]=\"cOptions.aspectRatio\"\n [alignImage]=\"cOptions.alignImage\"\n [resizeToWidth]=\"cOptions.resizeToWidth\"\n [resizeToHeight]=\"cOptions.resizeToHeight\"\n [autoCrop]=\"cOptions.autoCrop\"\n [imageQuality]=\"cOptions.imageQuality\"\n [onlyScaleDown]=\"cOptions.onlyScaleDown\"\n [format]=\"cOptions.format\"\n (imageCropped)=\"imageCropped($event, fileEvent)\"\n (loadImageFailed)=\"loadImageFailed()\"\n ></image-cropper>\n <button class=\"btn btn-primary mt-2\" type=\"button\" (click)=\"finishedCropping()\" baseStopPropagation>\n Zuschneiden\n </button>\n }\n </div>\n\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n </div>\n }\n </div>\n", styles: [":host{--base-upload-image-area-bg: #ffffff;--base-upload-image-area-border-color: #e1e1e1;--base-upload-image-area-height: 400px}.is-invalid .drag-area{border-color:var(--bs-danger)}.is-invalid .invalid-feedback{display:block}.required:after{content:\" *\"}.drag-area{height:var(--base-upload-image-area-height);border:2px dashed var(--base-upload-image-area-border-color);background-color:var(--base-upload-image-area-bg);display:flex;flex-direction:column;justify-content:center;align-items:center;margin:10px auto;padding:20px}.drag-area img{height:100%;max-height:280px;max-width:100%;object-fit:scale-down}.drag-area .btn-danger{margin-top:auto}.drag-area .icon{font-size:50px;color:var(--bs-primary)}.drag-area .header{font-size:20px;font-weight:500}.drag-area .support{font-size:12px;color:#e1e1e1;margin:10px 0 15px}.drag-area .button{font-size:20px;font-weight:500;color:var(--bs-primary);cursor:pointer}.active{border:2px solid var(--bs-primary)}.pointer{cursor:pointer}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: ImageCropperComponent, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "cropperFrameAriaLabel", "output", "format", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "autoCrop", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "cropper", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange"] }, { kind: "directive", type: StopPropagationDirective, selector: "[baseStopPropagation]" }] });
1139
- }
1140
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: UploadImageComponent, decorators: [{
1141
- type: Component,
1142
- args: [{ selector: 'base-upload-image', standalone: true, imports: [
1143
- NgClass,
1144
- ImageCropperComponent,
1145
- StopPropagationDirective
1146
- ], template: "<div class=\"form-group mt-3\" [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n\n <div\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n class=\"drag-area\"\n (dragover)=\"dragOver($event)\"\n (dragleave)=\"dragLeave($event)\"\n (drop)=\"drop($event)\"\n [ngClass]=\"{ active: dragActive, pointer: !loading, 'is-invalid': control?.invalid && control?.touched }\"\n (click)=\"!croppedInProgress && !loading && fileInput.click()\"\n >\n @if (loading) {\n <div class=\"d-flex justify-content-center\">\n <div class=\"spinner-border text-primary\" role=\"status\">\n <span class=\"visually-hidden\">Loading...</span>\n </div>\n </div>\n }\n\n @if (selectedFile && !fileEvent && !croppedInProgress && !loading) {\n @if (selectedFile) {\n <img [src]=\"fileUrl\" />\n }\n <button class=\"btn btn-danger\" type=\"button\" (click)=\"deleteFile()\" baseStopPropagation>L\u00F6schen</button>\n }\n\n @if (control?.value && !fileEvent && !selectedFile && !loading && !croppedInProgress) {\n <img [src]=\"control?.value.startsWith('data:') ? control?.value : url + path + control?.value\" />\n <button class=\"btn btn-danger\" type=\"button\" (click)=\"deleteFile(control?.value)\" baseStopPropagation>\n L\u00F6schen\n </button>\n }\n\n @if (!selectedFile && !control?.value && !loading && !fileEvent) {\n <div class=\"icon\">\n <i class=\"bi bi-images\"></i>\n </div>\n <span class=\"header\">{{ dragText }}</span>\n <span class=\"header\"\n >{{ preButtonText }}\n <span class=\"button\" (click)=\"fileInput.click()\" baseStopPropagation>{{ buttonText }}</span></span\n >\n <span class=\"support\" [innerText]=\"supportText\"></span>\n }\n\n <input type=\"file\" (change)=\"selectFile($event)\" [attr.accept]=\"validExtensions?.join(',')\" hidden #fileInput />\n\n @if (croppedInProgress && fileEvent) {\n <image-cropper\n [imageChangedEvent]=\"fileEvent\"\n [maintainAspectRatio]=\"cOptions.maintainAspectRatio\"\n [aspectRatio]=\"cOptions.aspectRatio\"\n [alignImage]=\"cOptions.alignImage\"\n [resizeToWidth]=\"cOptions.resizeToWidth\"\n [resizeToHeight]=\"cOptions.resizeToHeight\"\n [autoCrop]=\"cOptions.autoCrop\"\n [imageQuality]=\"cOptions.imageQuality\"\n [onlyScaleDown]=\"cOptions.onlyScaleDown\"\n [format]=\"cOptions.format\"\n (imageCropped)=\"imageCropped($event, fileEvent)\"\n (loadImageFailed)=\"loadImageFailed()\"\n ></image-cropper>\n <button class=\"btn btn-primary mt-2\" type=\"button\" (click)=\"finishedCropping()\" baseStopPropagation>\n Zuschneiden\n </button>\n }\n </div>\n\n @if (control?.invalid && (control?.dirty || control?.touched)) {\n <div class=\"invalid-feedback m-2\">\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n </div>\n }\n </div>\n", styles: [":host{--base-upload-image-area-bg: #ffffff;--base-upload-image-area-border-color: #e1e1e1;--base-upload-image-area-height: 400px}.is-invalid .drag-area{border-color:var(--bs-danger)}.is-invalid .invalid-feedback{display:block}.required:after{content:\" *\"}.drag-area{height:var(--base-upload-image-area-height);border:2px dashed var(--base-upload-image-area-border-color);background-color:var(--base-upload-image-area-bg);display:flex;flex-direction:column;justify-content:center;align-items:center;margin:10px auto;padding:20px}.drag-area img{height:100%;max-height:280px;max-width:100%;object-fit:scale-down}.drag-area .btn-danger{margin-top:auto}.drag-area .icon{font-size:50px;color:var(--bs-primary)}.drag-area .header{font-size:20px;font-weight:500}.drag-area .support{font-size:12px;color:#e1e1e1;margin:10px 0 15px}.drag-area .button{font-size:20px;font-weight:500;color:var(--bs-primary);cursor:pointer}.active{border:2px solid var(--bs-primary)}.pointer{cursor:pointer}\n"] }]
1147
- }], ctorParameters: () => [{ type: i1$2.HttpClient }, { type: i1.AuthService }, { type: i1.FileService }, { type: i1.ToastService }], propDecorators: { fileInput: [{
1148
- type: ViewChild,
1149
- args: ['fileInput']
1150
- }], id: [{
1151
- type: Input
1152
- }], name: [{
1153
- type: Input
1154
- }], infoText: [{
1155
- type: Input
1156
- }], label: [{
1157
- type: Input
1158
- }], supportText: [{
1159
- type: Input
1160
- }], required: [{
1161
- type: Input
1162
- }], defaultDragText: [{
1163
- type: Input
1164
- }], defaultReleaseText: [{
1165
- type: Input
1166
- }], preButtonText: [{
1167
- type: Input
1168
- }], buttonText: [{
1169
- type: Input
1170
- }], validExtensions: [{
1171
- type: Input
1172
- }], control: [{
1173
- type: Input
1174
- }], url: [{
1175
- type: Input
1176
- }], uploadPath: [{
1177
- type: Input
1178
- }], deletePath: [{
1179
- type: Input
1180
- }], path: [{
1181
- type: Input
1182
- }], compressOptions: [{
1183
- type: Input
1184
- }], maxSize: [{
1185
- type: Input
1186
- }], uploadDirectly: [{
1187
- type: Input
1188
- }], croppingImage: [{
1189
- type: Input
1190
- }], croppingOptions: [{
1191
- type: Input
1192
- }], objectPath: [{
1193
- type: Input
1194
- }], mode: [{
1195
- type: Input
1196
- }], imageUploaded: [{
1197
- type: Output
1198
- }], imageDeleted: [{
1199
- type: Output
1200
- }], fileChanged: [{
1201
- type: Output
1202
- }] } });
1203
-
1204
- class RefenceInputComponent {
1205
- graphQLService;
1206
- router;
1207
- id;
1208
- name;
1209
- infoText;
1210
- label;
1211
- isList = false;
1212
- search = true;
1213
- useParamAsArg = false;
1214
- param;
1215
- placeholder = '';
1216
- autocomplete;
1217
- tabIndex;
1218
- control;
1219
- required = false;
1220
- method = 'find';
1221
- fields = ['id', 'name'];
1222
- valueField = 'id';
1223
- objectPath = '';
1224
- nameField = 'name';
1225
- creationLink = '';
1226
- objects = [];
1227
- currentValue;
1228
- optionsForTagInput = [];
1229
- selectedElement;
1230
- subscription = new Subscription();
1231
- constructor(graphQLService, router) {
1232
- this.graphQLService = graphQLService;
1233
- this.router = router;
1234
- }
1235
- async ngOnInit() {
1236
- this.getReferences();
1237
- this.subscription.add(this.control.valueChanges.subscribe(() => {
1238
- if (!this.currentValue) {
1239
- this.setValue();
1240
- }
1241
- }));
1242
- }
1243
- ngOnDestroy() {
1244
- this.subscription.unsubscribe();
1245
- }
1246
- /**
1247
- * Open creation link for new reference if enabled
1248
- */
1249
- openLink() {
1250
- this.router.navigate([this.creationLink]);
1251
- }
1252
- /**
1253
- * It gets the references from the GraphQL server and sets the value of the select
1254
- */
1255
- getReferences() {
1256
- const fields = [...this.fields];
1257
- if (Array.isArray(this.nameField)) {
1258
- for (const field of this.nameField) {
1259
- if (!fields.includes(field)) {
1260
- fields.push(field);
1261
- }
1262
- }
1263
- }
1264
- else {
1265
- if (!fields.includes(this.nameField)) {
1266
- fields.push(this.nameField);
1267
- }
1268
- }
1269
- let args;
1270
- if (!this.useParamAsArg) {
1271
- args = { sort: [{ field: fields.includes('name') ? 'name' : fields[0], order: SortOrderEnum.ASC }] };
1272
- }
1273
- else {
1274
- args = {
1275
- id: this.param,
1276
- sort: [{ field: fields.includes('name') ? 'name' : fields[0], order: SortOrderEnum.ASC }],
1277
- };
1278
- }
1279
- this.graphQLService
1280
- .graphQl(this.method, {
1281
- fields,
1282
- arguments: args,
1283
- type: GraphQLRequestType.QUERY,
1284
- })
1285
- .subscribe(async (result) => {
1286
- if (result) {
1287
- this.objects = result;
1288
- this.setObjectsAsOptions();
1289
- if (!this.currentValue) {
1290
- this.setValue();
1291
- }
1292
- }
1293
- });
1294
- }
1295
- /**
1296
- * If the current value is not valid, set the control to an invalid state
1297
- */
1298
- checkValidReference() {
1299
- if (!this.currentValue) {
1300
- this.control.markAsTouched();
1301
- this.control.markAsDirty();
1302
- this.control.setValue('');
1303
- this.control.setErrors(null);
1304
- return;
1305
- }
1306
- const foundElement = this.objects.find((e) => this.checkForNameFields(e));
1307
- if (!foundElement) {
1308
- this.control.setValue(null);
1309
- this.control.markAsTouched();
1310
- this.control.markAsDirty();
1311
- this.control.setErrors({ invalidReference: true });
1312
- }
1313
- else {
1314
- this.control.setErrors(null);
1315
- }
1316
- }
1317
- /**
1318
- * If the user presses the enter key, mark the control as touched
1319
- */
1320
- enterKey(event) {
1321
- this.selectedElement = event.target;
1322
- this.control.markAsTouched();
1323
- }
1324
- /**
1325
- * If the control has a value and the objects array is not empty, then find the object in the array that has the same
1326
- * value as the control's value and set the currentValue to the textField of that object. If the control has a value but
1327
- * the objects array is empty, then set the currentValue to the control's value
1328
- */
1329
- setValue() {
1330
- if (this.control?.value && this.objects?.length > 0) {
1331
- if (this.control?.value?.id) {
1332
- this.control.setValue(this.control.value.id);
1333
- }
1334
- const foundElement = this.objects.find((e) => e[this.valueField] === this.control.value);
1335
- if (foundElement) {
1336
- this.currentValue = this.prepareNameField(foundElement);
1337
- }
1338
- }
1339
- }
1340
- /**
1341
- * It sets the value of the control to the id of the selected option
1342
- */
1343
- set(id) {
1344
- this.control.markAsTouched();
1345
- this.control.markAsDirty();
1346
- this.control.setValue(id);
1347
- this.setValue();
1348
- this.selectedElement = null;
1349
- }
1350
- /**
1351
- * Filter options for drop down
1352
- */
1353
- filterOptions() {
1354
- return this.objects
1355
- ? this.objects.filter((e) => (!this.currentValue || this.checkForNameFields(e)) && !this.control?.value?.includes(e?.value))
1356
- : [];
1357
- }
1358
- /**
1359
- * It takes an array of objects and returns an array of objects with the text and value fields specified
1360
- */
1361
- setObjectsAsOptions() {
1362
- this.optionsForTagInput = this.objects.map((e) => {
1363
- return {
1364
- text: this.prepareNameField(e),
1365
- value: e[this.valueField],
1366
- };
1367
- });
1368
- }
1369
- /**
1370
- * Show dropdown focus
1371
- */
1372
- onFocus(event) {
1373
- if (event) {
1374
- this.selectedElement = event.target;
1375
- }
1376
- else {
1377
- this.checkValidReference();
1378
- setTimeout(() => {
1379
- this.selectedElement = null;
1380
- }, 250);
1381
- }
1382
- }
1383
- /**
1384
- * Prepare string function if name fields is an array
1385
- */
1386
- prepareNameField(object) {
1387
- let result;
1388
- if (Array.isArray(this.nameField)) {
1389
- for (const field of this.nameField) {
1390
- if (!result) {
1391
- result = object[field];
1392
- }
1393
- else if (object[field]) {
1394
- result = result + ' ' + object[field];
1395
- }
1396
- }
1397
- }
1398
- else {
1399
- result = object[this.nameField];
1400
- }
1401
- return result;
1402
- }
1403
- /**
1404
- * Check function if name fields is an array
1405
- */
1406
- checkForNameFields(object) {
1407
- if (Array.isArray(this.nameField)) {
1408
- let result = false;
1409
- for (const field of this.nameField) {
1410
- const check = object[field].includes(this.currentValue);
1411
- if (check) {
1412
- result = check;
1413
- }
1414
- }
1415
- return result;
1416
- }
1417
- else {
1418
- return object[this.nameField]?.includes(this.currentValue);
1419
- }
1420
- }
1421
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: RefenceInputComponent, deps: [{ token: i1.GraphQLService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Component });
1422
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: RefenceInputComponent, isStandalone: true, selector: "base-refence-input", inputs: { id: "id", name: "name", infoText: "infoText", label: "label", isList: "isList", search: "search", useParamAsArg: "useParamAsArg", param: "param", placeholder: "placeholder", autocomplete: "autocomplete", tabIndex: "tabIndex", control: "control", required: "required", method: "method", fields: "fields", valueField: "valueField", objectPath: "objectPath", nameField: "nameField", creationLink: "creationLink" }, ngImport: i0, template: "@if (!isList) {\n <div class=\"form-group mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n @if (search) {\n <div class=\"position-relative\">\n <div class=\"d-flex flex-row\">\n <input [formControl]=\"control\" hidden />\n <input\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n class=\"form-control\"\n [(ngModel)]=\"currentValue\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onFocus()\"\n [required]=\"required\"\n [placeholder]=\"placeholder\"\n [autocomplete]=\"autocomplete ?? null\"\n [tabIndex]=\"tabIndex ?? null\"\n (keyup)=\"enterKey($event)\"\n />\n @if (creationLink) {\n <button class=\"btn btn-primary\" (click)=\"openLink()\">Erstellen</button>\n }\n </div>\n @if (objects && selectedElement) {\n <div class=\"options\">\n @for (object of filterOptions(); track object) {\n <div class=\"option-item\" (click)=\"set(object[valueField])\">\n {{ prepareNameField(object) }}\n </div>\n }\n </div>\n }\n </div>\n }\n @if (!search) {\n <div>\n <div class=\"d-flex flex-row\">\n <select\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n class=\"form-select\"\n [attr.aria-label]=\"placeholder\"\n [formControl]=\"control\"\n [required]=\"required\"\n [tabIndex]=\"tabIndex ?? null\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n >\n <option [ngValue]=\"null\" selected>{{ placeholder }}</option>\n @for (object of objects; track object) {\n <option [ngValue]=\"object[valueField]\">\n {{ prepareNameField(object) }}\n </option>\n }\n </select>\n @if (creationLink) {\n <button class=\"btn btn-primary\" (click)=\"openLink()\">Erstellen</button>\n }\n </div>\n </div>\n }\n @if (control?.invalid && (control?.dirty || control?.touched) && !selectedElement) {\n <div\n class=\"d-block invalid-feedback m-2\"\n >\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n @if (control?.errors?.['invalidReference'] && currentValue) {\n <div>\n {{ currentValue }} ist kein g\u00FCltiger Wert f\u00FCr {{ label }}.\n </div>\n }\n @if (control?.errors?.['minlength']) {\n <div>\n Die Mindestl\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control.errors?.['minlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['maxlength']) {\n <div>\n Die maximale L\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control.errors?.['maxlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['email']) {\n <div>Bitte eine g\u00FCltige E-Mail Adresse angeben.</div>\n }\n @if (control?.errors?.['matching']) {\n <div>Die Passw\u00F6rter m\u00FCssen \u00FCberein stimmen.</div>\n }\n </div>\n }\n </div>\n}\n\n@if (isList) {\n <base-tags\n [id]=\"id\"\n [label]=\"label\"\n [name]=\"name\"\n [control]=\"control\"\n [required]=\"required\"\n [placeholder]=\"placeholder\"\n [custom]=\"false\"\n [options]=\"optionsForTagInput\"\n ></base-tags>\n}\n", styles: [".options{position:absolute;display:block;top:100%;left:0;right:0;border-top:0;background-color:#fff;box-shadow:0 2px 5px #00000029,0 2px 10px #0000001f;transition:all .5s;z-index:999;max-height:200px;overflow-y:scroll}.options .option-item{padding:10px;cursor:pointer}.options .option-item:hover{color:var(--bs-primary)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: TagsComponent, selector: "base-tags", inputs: ["id", "name", "infoText", "label", "placeholder", "autocomplete", "tabIndex", "control", "options", "custom", "removeByKey", "required", "objectPath", "validators"] }] });
1423
- }
1424
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: RefenceInputComponent, decorators: [{
1425
- type: Component,
1426
- args: [{ selector: 'base-refence-input', standalone: true, imports: [
1427
- NgClass,
1428
- ReactiveFormsModule,
1429
- FormsModule,
1430
- TagsComponent
1431
- ], template: "@if (!isList) {\n <div class=\"form-group mb-3\">\n <div class=\"d-flex justify-content-between align-items-center\">\n @if (label) {\n <label [for]=\"id\" class=\"form-label\" [ngClass]=\"{ required: required }\">{{ label }}</label>\n }\n @if (infoText) {\n <i class=\"bi bi-info-circle\" [ngbTooltip]=\"infoText\" placement=\"start\"></i>\n }\n </div>\n @if (search) {\n <div class=\"position-relative\">\n <div class=\"d-flex flex-row\">\n <input [formControl]=\"control\" hidden />\n <input\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n class=\"form-control\"\n [(ngModel)]=\"currentValue\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onFocus()\"\n [required]=\"required\"\n [placeholder]=\"placeholder\"\n [autocomplete]=\"autocomplete ?? null\"\n [tabIndex]=\"tabIndex ?? null\"\n (keyup)=\"enterKey($event)\"\n />\n @if (creationLink) {\n <button class=\"btn btn-primary\" (click)=\"openLink()\">Erstellen</button>\n }\n </div>\n @if (objects && selectedElement) {\n <div class=\"options\">\n @for (object of filterOptions(); track object) {\n <div class=\"option-item\" (click)=\"set(object[valueField])\">\n {{ prepareNameField(object) }}\n </div>\n }\n </div>\n }\n </div>\n }\n @if (!search) {\n <div>\n <div class=\"d-flex flex-row\">\n <select\n [id]=\"objectPath ? objectPath + '.' + id : id\"\n [name]=\"name\"\n class=\"form-select\"\n [attr.aria-label]=\"placeholder\"\n [formControl]=\"control\"\n [required]=\"required\"\n [tabIndex]=\"tabIndex ?? null\"\n [ngClass]=\"{ 'is-invalid': control?.invalid && control?.touched }\"\n >\n <option [ngValue]=\"null\" selected>{{ placeholder }}</option>\n @for (object of objects; track object) {\n <option [ngValue]=\"object[valueField]\">\n {{ prepareNameField(object) }}\n </option>\n }\n </select>\n @if (creationLink) {\n <button class=\"btn btn-primary\" (click)=\"openLink()\">Erstellen</button>\n }\n </div>\n </div>\n }\n @if (control?.invalid && (control?.dirty || control?.touched) && !selectedElement) {\n <div\n class=\"d-block invalid-feedback m-2\"\n >\n @if (control?.errors?.['required']) {\n <div>Dieses Feld ist ein Pflichtfeld.</div>\n }\n @if (control?.errors?.['invalidReference'] && currentValue) {\n <div>\n {{ currentValue }} ist kein g\u00FCltiger Wert f\u00FCr {{ label }}.\n </div>\n }\n @if (control?.errors?.['minlength']) {\n <div>\n Die Mindestl\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control.errors?.['minlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['maxlength']) {\n <div>\n Die maximale L\u00E4nge f\u00FCr dieses Feld betr\u00E4gt\n {{control.errors?.['maxlength']?.requiredLength}} Zeichen.\n </div>\n }\n @if (control?.errors?.['email']) {\n <div>Bitte eine g\u00FCltige E-Mail Adresse angeben.</div>\n }\n @if (control?.errors?.['matching']) {\n <div>Die Passw\u00F6rter m\u00FCssen \u00FCberein stimmen.</div>\n }\n </div>\n }\n </div>\n}\n\n@if (isList) {\n <base-tags\n [id]=\"id\"\n [label]=\"label\"\n [name]=\"name\"\n [control]=\"control\"\n [required]=\"required\"\n [placeholder]=\"placeholder\"\n [custom]=\"false\"\n [options]=\"optionsForTagInput\"\n ></base-tags>\n}\n", styles: [".options{position:absolute;display:block;top:100%;left:0;right:0;border-top:0;background-color:#fff;box-shadow:0 2px 5px #00000029,0 2px 10px #0000001f;transition:all .5s;z-index:999;max-height:200px;overflow-y:scroll}.options .option-item{padding:10px;cursor:pointer}.options .option-item:hover{color:var(--bs-primary)}\n"] }]
1432
- }], ctorParameters: () => [{ type: i1.GraphQLService }, { type: i2.Router }], propDecorators: { id: [{
1433
- type: Input
1434
- }], name: [{
1435
- type: Input
1436
- }], infoText: [{
1437
- type: Input
1438
- }], label: [{
1439
- type: Input
1440
- }], isList: [{
1441
- type: Input
1442
- }], search: [{
1443
- type: Input
1444
- }], useParamAsArg: [{
1445
- type: Input
1446
- }], param: [{
1447
- type: Input
1448
- }], placeholder: [{
1449
- type: Input
1450
- }], autocomplete: [{
1451
- type: Input
1452
- }], tabIndex: [{
1453
- type: Input
1454
- }], control: [{
1455
- type: Input
1456
- }], required: [{
1457
- type: Input
1458
- }], method: [{
1459
- type: Input
1460
- }], fields: [{
1461
- type: Input
1462
- }], valueField: [{
1463
- type: Input
1464
- }], objectPath: [{
1465
- type: Input
1466
- }], nameField: [{
1467
- type: Input
1468
- }], creationLink: [{
1469
- type: Input
1470
- }] } });
1471
-
1472
- class ModelFormSubComponent {
1473
- authService;
1474
- id;
1475
- parentKey;
1476
- fields;
1477
- form;
1478
- config;
1479
- imageChanged = new EventEmitter();
1480
- fileChanged = new EventEmitter();
1481
- keys = [];
1482
- user;
1483
- objectPath;
1484
- constructor(authService) {
1485
- this.authService = authService;
1486
- }
1487
- ngOnInit() {
1488
- // Get keys
1489
- const unsortedKeys = Object.keys(this.fields);
1490
- // Sort keys by config order
1491
- this.keys = unsortedKeys.sort((a, b) => {
1492
- return ((this.config[a]?.order ? this.config[a]?.order : 99) - (this.config[b]?.order ? this.config[b]?.order : 99));
1493
- });
1494
- // Set user for roles check
1495
- this.user = BasicUser.map(this.authService.currentUser);
1496
- if (this.parentKey) {
1497
- if (!this.objectPath) {
1498
- this.objectPath = this.parentKey;
1499
- }
1500
- else {
1501
- this.objectPath = this.objectPath + '.' + this.parentKey;
1502
- }
1503
- }
1504
- }
1505
- /**
1506
- * If the field has fields, return true, otherwise return false.
1507
- */
1508
- checkFieldHasFields(field) {
1509
- return Object.keys(field.fields)?.length !== 0;
1510
- }
1511
- /**
1512
- * It takes a string, capitalizes the first letter, and returns the new string
1513
- *
1514
- * @param value - The string that you want to capitalize the first letter of.
1515
- * @returns The first letter of the string is being capitalized and the rest of the string is being returned.
1516
- */
1517
- capitalizeFirstLetter(value) {
1518
- return value.charAt(0).toUpperCase() + value.slice(1);
1519
- }
1520
- /**
1521
- * It takes an array of values and returns an array of objects with a value and text property
1522
- *
1523
- * @param values - any[] - the array of values that you want to convert to a selectable list
1524
- * @param config
1525
- * @returns An array of objects with a value and text property.
1526
- */
1527
- prepareEnumForSelect(values, config = {}) {
1528
- return values
1529
- .filter((i) => {
1530
- return !(config && config[i]?.roles && !this.user.hasAllRoles(config[i]?.roles));
1531
- })
1532
- .map((e) => {
1533
- return {
1534
- value: e,
1535
- text: config && config[e]?.text ? config[e].text : e,
1536
- };
1537
- });
1538
- }
1539
- /**
1540
- * It takes an AbstractControl and returns a FormControl
1541
- *
1542
- * @param control - AbstractControl | null | undefined
1543
- * @returns A FormControl
1544
- */
1545
- transformToControl(control) {
1546
- return control;
1547
- }
1548
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ModelFormSubComponent, deps: [{ token: i1.AuthService }], target: i0.ɵɵFactoryTarget.Component });
1549
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: ModelFormSubComponent, isStandalone: true, selector: "base-model-form-sub", inputs: { id: "id", parentKey: "parentKey", fields: "fields", form: "form", config: "config" }, outputs: { imageChanged: "imageChanged", fileChanged: "fileChanged" }, ngImport: i0, template: "@for (key of keys; track key) {\n <div class=\"mt-3\">\n @if (checkFieldHasFields(fields[key]) && (config[key]?.roles ? user?.hasAllRoles(config[key]?.roles) : true)) {\n <fieldset class=\"border rounded-3 p-3 mt-3\">\n <legend class=\"float-none w-auto px-3\">\n {{ config[key]?.label ? config[key].label : capitalizeFirstLetter(key) }}\n </legend>\n <base-model-form-sub\n [parentKey]=\"key\"\n [fields]=\"fields[key].fields\"\n [config]=\"config[key]?.fields || {}\"\n [form]=\"form.get(key)\"\n (imageChanged)=\"imageChanged.emit($event)\"\n (fileChanged)=\"fileChanged.emit($event)\"\n ></base-model-form-sub>\n </fieldset>\n }\n @if (!checkFieldHasFields(fields[key]) && (config[key]?.roles ? user?.hasAllRoles(config[key]?.roles) : true)) {\n @if (fields[key]?.isEnum && !config[key]?.exclude) {\n <base-select\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || 'Bitte einen Wert ausw\u00E4hlen'\"\n [options]=\"prepareEnumForSelect(fields[key]?.validEnums, config[key]?.enumValues)\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\"\n [objectPath]=\"objectPath\"\n ></base-select>\n }\n @if (!fields[key]?.isEnum && !config[key]?.exclude) {\n @switch (config[key]?.type ? config[key]?.type : fields[key]?.type) {\n @case ('String') {\n @if (fields[key]?.isList && !config[key]?.exclude) {\n <base-tags\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [validators]=\"config[key]?.validators\"\n [objectPath]=\"objectPath\"\n ></base-tags>\n }\n @if (!fields[key]?.isList && !config[key]?.exclude) {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [type]=\"'text'\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n }\n @case ('Image') {\n <base-upload-image\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [control]=\"transformToControl(form?.get(key))\"\n [url]=\"config[key]?.url\"\n [path]=\"config[key]?.path || '/files/'\"\n [uploadPath]=\"config[key]?.uploadPath || '/files/upload'\"\n [deletePath]=\"config[key]?.deletePath || '/files/'\"\n [supportText]=\"config[key]?.supportText || 'Unterst\u00FCtzte Formate: JPEG, JPG, PNG'\"\n [defaultDragText]=\"config[key]?.dragText || 'Drag & Drop'\"\n [defaultReleaseText]=\"config[key]?.releaseText || 'Loslassen zum hochladen'\"\n [preButtonText]=\"config[key]?.preButtonText || 'or'\"\n [buttonText]=\"config[key]?.buttonText || 'browse'\"\n [validExtensions]=\"config[key]?.validExtensions || ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [compressOptions]=\"config[key]?.compressOptions\"\n [croppingOptions]=\"config[key]?.croppingOptions\"\n [croppingImage]=\"config[key]?.croppingImage\"\n [maxSize]=\"config[key]?.maxSize\"\n [uploadDirectly]=\"false\"\n [objectPath]=\"objectPath\"\n [mode]=\"config[key]?.imageMode || 'base64'\"\n (fileChanged)=\"fileChanged.emit($event)\"\n (imageDeleted)=\"imageChanged.emit(key)\"\n (imageUploaded)=\"imageChanged.emit(key)\"\n ></base-upload-image>\n }\n @case ('Upload') {\n <base-upload-image\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [control]=\"transformToControl(form?.get(key))\"\n [supportText]=\"config[key]?.supportText || 'Unterst\u00FCtzte Formate: JPEG, JPG, PNG'\"\n [defaultDragText]=\"config[key]?.dragText || 'Drag & Drop'\"\n [defaultReleaseText]=\"config[key]?.releaseText || 'Loslassen zum hochladen'\"\n [preButtonText]=\"config[key]?.preButtonText || 'or'\"\n [buttonText]=\"config[key]?.buttonText || 'browse'\"\n [validExtensions]=\"config[key]?.validExtensions || ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [compressOptions]=\"config[key]?.compressOptions\"\n [maxSize]=\"config[key]?.maxSize\"\n [objectPath]=\"objectPath\"\n [mode]=\"'file'\"\n ></base-upload-image>\n }\n @case ('Reference') {\n <base-refence-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [isList]=\"fields[key]?.isList\"\n [useParamAsArg]=\"config[key]?.useParamAsArg ?? false\"\n [param]=\"id\"\n [search]=\"config[key]?.searchable ?? true\"\n [placeholder]=\"config[key]?.placeholder || 'Bitte einen Wert ausw\u00E4hlen'\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [control]=\"transformToControl(form?.get(key))\"\n [method]=\"config[key]?.method || 'find'\"\n [fields]=\"config[key]?.requestFields || ['id', 'name']\"\n [valueField]=\"config[key]?.valueField || 'id'\"\n [nameField]=\"config[key]?.nameField || 'name'\"\n [creationLink]=\"config[key]?.creationLink || ''\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-refence-input>\n }\n @case ('Boolean') {\n <base-checkbox\n [id]=\"key\"\n [name]=\"key\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-checkbox>\n }\n @case ('LongText') {\n <base-textarea\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-textarea>\n }\n @case ('Float') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [type]=\"'number'\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n @case ('Password') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [type]=\"'password'\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n @case ('Color') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [type]=\"'color'\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n @case ('URL') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [type]=\"'url'\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n @case ('Email') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [type]=\"'email'\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n @case ('DateTime') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [type]=\"'datetime-local'\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n }\n }\n }\n </div>\n}\n", styles: [""], dependencies: [{ kind: "component", type: ModelFormSubComponent, selector: "base-model-form-sub", inputs: ["id", "parentKey", "fields", "form", "config"], outputs: ["imageChanged", "fileChanged"] }, { kind: "component", type: InputComponent, selector: "base-input", inputs: ["id", "name", "infoText", "label", "placeholder", "autocomplete", "tabIndex", "control", "type", "required", "eyeButton", "objectPath"], outputs: ["enter"] }, { kind: "component", type: SelectComponent, selector: "base-select", inputs: ["id", "name", "infoText", "label", "placeholder", "objectPath", "control", "tabIndex", "required", "multiple", "search", "options"] }, { kind: "component", type: TagsComponent, selector: "base-tags", inputs: ["id", "name", "infoText", "label", "placeholder", "autocomplete", "tabIndex", "control", "options", "custom", "removeByKey", "required", "objectPath", "validators"] }, { kind: "component", type: UploadImageComponent, selector: "base-upload-image", inputs: ["id", "name", "infoText", "label", "supportText", "required", "defaultDragText", "defaultReleaseText", "preButtonText", "buttonText", "validExtensions", "control", "url", "uploadPath", "deletePath", "path", "compressOptions", "maxSize", "uploadDirectly", "croppingImage", "croppingOptions", "objectPath", "mode"], outputs: ["imageUploaded", "imageDeleted", "fileChanged"] }, { kind: "component", type: RefenceInputComponent, selector: "base-refence-input", inputs: ["id", "name", "infoText", "label", "isList", "search", "useParamAsArg", "param", "placeholder", "autocomplete", "tabIndex", "control", "required", "method", "fields", "valueField", "objectPath", "nameField", "creationLink"] }, { kind: "component", type: CheckboxComponent, selector: "base-checkbox", inputs: ["id", "name", "label", "value", "control", "required", "objectPath"] }, { kind: "component", type: TextareaComponent, selector: "base-textarea", inputs: ["id", "name", "infoText", "label", "placeholder", "control", "rows", "required", "autoHeight", "tabIndex", "objectPath"] }] });
1550
- }
1551
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ModelFormSubComponent, decorators: [{
1552
- type: Component,
1553
- args: [{ selector: 'base-model-form-sub', standalone: true, imports: [
1554
- InputComponent,
1555
- SelectComponent,
1556
- TagsComponent,
1557
- UploadImageComponent,
1558
- RefenceInputComponent,
1559
- CheckboxComponent,
1560
- TextareaComponent
1561
- ], template: "@for (key of keys; track key) {\n <div class=\"mt-3\">\n @if (checkFieldHasFields(fields[key]) && (config[key]?.roles ? user?.hasAllRoles(config[key]?.roles) : true)) {\n <fieldset class=\"border rounded-3 p-3 mt-3\">\n <legend class=\"float-none w-auto px-3\">\n {{ config[key]?.label ? config[key].label : capitalizeFirstLetter(key) }}\n </legend>\n <base-model-form-sub\n [parentKey]=\"key\"\n [fields]=\"fields[key].fields\"\n [config]=\"config[key]?.fields || {}\"\n [form]=\"form.get(key)\"\n (imageChanged)=\"imageChanged.emit($event)\"\n (fileChanged)=\"fileChanged.emit($event)\"\n ></base-model-form-sub>\n </fieldset>\n }\n @if (!checkFieldHasFields(fields[key]) && (config[key]?.roles ? user?.hasAllRoles(config[key]?.roles) : true)) {\n @if (fields[key]?.isEnum && !config[key]?.exclude) {\n <base-select\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || 'Bitte einen Wert ausw\u00E4hlen'\"\n [options]=\"prepareEnumForSelect(fields[key]?.validEnums, config[key]?.enumValues)\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\"\n [objectPath]=\"objectPath\"\n ></base-select>\n }\n @if (!fields[key]?.isEnum && !config[key]?.exclude) {\n @switch (config[key]?.type ? config[key]?.type : fields[key]?.type) {\n @case ('String') {\n @if (fields[key]?.isList && !config[key]?.exclude) {\n <base-tags\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [validators]=\"config[key]?.validators\"\n [objectPath]=\"objectPath\"\n ></base-tags>\n }\n @if (!fields[key]?.isList && !config[key]?.exclude) {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [type]=\"'text'\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n }\n @case ('Image') {\n <base-upload-image\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [control]=\"transformToControl(form?.get(key))\"\n [url]=\"config[key]?.url\"\n [path]=\"config[key]?.path || '/files/'\"\n [uploadPath]=\"config[key]?.uploadPath || '/files/upload'\"\n [deletePath]=\"config[key]?.deletePath || '/files/'\"\n [supportText]=\"config[key]?.supportText || 'Unterst\u00FCtzte Formate: JPEG, JPG, PNG'\"\n [defaultDragText]=\"config[key]?.dragText || 'Drag & Drop'\"\n [defaultReleaseText]=\"config[key]?.releaseText || 'Loslassen zum hochladen'\"\n [preButtonText]=\"config[key]?.preButtonText || 'or'\"\n [buttonText]=\"config[key]?.buttonText || 'browse'\"\n [validExtensions]=\"config[key]?.validExtensions || ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [compressOptions]=\"config[key]?.compressOptions\"\n [croppingOptions]=\"config[key]?.croppingOptions\"\n [croppingImage]=\"config[key]?.croppingImage\"\n [maxSize]=\"config[key]?.maxSize\"\n [uploadDirectly]=\"false\"\n [objectPath]=\"objectPath\"\n [mode]=\"config[key]?.imageMode || 'base64'\"\n (fileChanged)=\"fileChanged.emit($event)\"\n (imageDeleted)=\"imageChanged.emit(key)\"\n (imageUploaded)=\"imageChanged.emit(key)\"\n ></base-upload-image>\n }\n @case ('Upload') {\n <base-upload-image\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [control]=\"transformToControl(form?.get(key))\"\n [supportText]=\"config[key]?.supportText || 'Unterst\u00FCtzte Formate: JPEG, JPG, PNG'\"\n [defaultDragText]=\"config[key]?.dragText || 'Drag & Drop'\"\n [defaultReleaseText]=\"config[key]?.releaseText || 'Loslassen zum hochladen'\"\n [preButtonText]=\"config[key]?.preButtonText || 'or'\"\n [buttonText]=\"config[key]?.buttonText || 'browse'\"\n [validExtensions]=\"config[key]?.validExtensions || ['image/jpeg', 'image/jpg', 'image/png', 'image/webp']\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [compressOptions]=\"config[key]?.compressOptions\"\n [maxSize]=\"config[key]?.maxSize\"\n [objectPath]=\"objectPath\"\n [mode]=\"'file'\"\n ></base-upload-image>\n }\n @case ('Reference') {\n <base-refence-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [isList]=\"fields[key]?.isList\"\n [useParamAsArg]=\"config[key]?.useParamAsArg ?? false\"\n [param]=\"id\"\n [search]=\"config[key]?.searchable ?? true\"\n [placeholder]=\"config[key]?.placeholder || 'Bitte einen Wert ausw\u00E4hlen'\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [control]=\"transformToControl(form?.get(key))\"\n [method]=\"config[key]?.method || 'find'\"\n [fields]=\"config[key]?.requestFields || ['id', 'name']\"\n [valueField]=\"config[key]?.valueField || 'id'\"\n [nameField]=\"config[key]?.nameField || 'name'\"\n [creationLink]=\"config[key]?.creationLink || ''\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-refence-input>\n }\n @case ('Boolean') {\n <base-checkbox\n [id]=\"key\"\n [name]=\"key\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-checkbox>\n }\n @case ('LongText') {\n <base-textarea\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-textarea>\n }\n @case ('Float') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [type]=\"'number'\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n @case ('Password') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [type]=\"'password'\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n @case ('Color') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [type]=\"'color'\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n @case ('URL') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [type]=\"'url'\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n @case ('Email') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [type]=\"'email'\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n @case ('DateTime') {\n <base-input\n [id]=\"key\"\n [name]=\"key\"\n [infoText]=\"config[key]?.infoText ? config[key].infoText : null\"\n [label]=\"config[key]?.label ? config[key].label : capitalizeFirstLetter(key)\"\n [placeholder]=\"config[key]?.placeholder || ''\"\n [type]=\"'datetime-local'\"\n [control]=\"transformToControl(form?.get(key))\"\n [required]=\"\n config[key] && config[key]?.required !== undefined ? config[key]?.required : fields[key].isRequired\n \"\n [objectPath]=\"objectPath\"\n ></base-input>\n }\n }\n }\n }\n </div>\n}\n" }]
1562
- }], ctorParameters: () => [{ type: i1.AuthService }], propDecorators: { id: [{
1563
- type: Input
1564
- }], parentKey: [{
1565
- type: Input
1566
- }], fields: [{
1567
- type: Input
1568
- }], form: [{
1569
- type: Input
1570
- }], config: [{
1571
- type: Input
1572
- }], imageChanged: [{
1573
- type: Output
1574
- }], fileChanged: [{
1575
- type: Output
1576
- }] } });
1577
-
1578
- /**
1579
- * ModelFormComponent
1580
- *
1581
- * Hint for handling extensions of this component in project:
1582
- * If you want to use complete custom services and methods, extend ModelFormBaseComponent instead of ModelFormComponent
1583
- */
1584
- class ModelFormComponent {
1585
- graphQLMetaService;
1586
- graphQLService;
1587
- formsService;
1588
- authService;
1589
- router;
1590
- route;
1591
- cmsService;
1592
- toastService;
1593
- fileService;
1594
- modelName;
1595
- label;
1596
- id;
1597
- delete = true;
1598
- onlyUpdateMode = false;
1599
- duplicate = false;
1600
- logging = false;
1601
- config;
1602
- showFavButton = true;
1603
- finished = new EventEmitter();
1604
- meta;
1605
- form;
1606
- _loading = false;
1607
- fields;
1608
- operation;
1609
- keys = [];
1610
- user;
1611
- object;
1612
- fabButtons = [];
1613
- fileChanges;
1614
- set loading(value) {
1615
- if (!value) {
1616
- setTimeout(() => {
1617
- this._loading = value;
1618
- }, 400);
1619
- }
1620
- else {
1621
- this._loading = value;
1622
- }
1623
- }
1624
- constructor(graphQLMetaService, graphQLService, formsService, authService, router, route, cmsService, toastService, fileService) {
1625
- this.graphQLMetaService = graphQLMetaService;
1626
- this.graphQLService = graphQLService;
1627
- this.formsService = formsService;
1628
- this.authService = authService;
1629
- this.router = router;
1630
- this.route = route;
1631
- this.cmsService = cmsService;
1632
- this.toastService = toastService;
1633
- this.fileService = fileService;
1634
- }
1635
- ngOnInit() {
1636
- // Set user for roles check
1637
- this.user = BasicUser.map(this.authService.currentUser);
1638
- this.graphQLMetaService.getMeta().subscribe((meta) => {
1639
- this.meta = meta;
1640
- if (this.modelName) {
1641
- this.init();
1642
- }
1643
- });
1644
- this.initFabActions();
1645
- }
1646
- ngOnChanges(changes) {
1647
- // Reinit form for new model or load and patch form with new id
1648
- if (changes['modelName'] || changes['id']) {
1649
- this.init();
1650
- }
1651
- }
1652
- /**
1653
- * > If the model name and metadata are present, set the operation to update or create, get the fields from the
1654
- * metadata, create the form, and if the value is present, patch the form with the value
1655
- */
1656
- init(id) {
1657
- if (id) {
1658
- this.id = id;
1659
- }
1660
- if (!this.id && this.onlyUpdateMode) {
1661
- return;
1662
- }
1663
- if (this.modelName && this.meta) {
1664
- this.operation = this.id ? 'update' : 'create';
1665
- if (this.logging) {
1666
- console.log('ModelFormComponent::init->operation', this.operation);
1667
- console.log('ModelFormComponent::init->meta.getArgs', this.operation + this.capitalizeFirstLetter(this.modelName));
1668
- }
1669
- this.fields = this.meta.getArgs(this.operation + this.capitalizeFirstLetter(this.modelName), {
1670
- type: GraphQLRequestType.MUTATION,
1671
- }).fields;
1672
- this.fields = this.fields['input'].fields;
1673
- this.keys = Object.keys(this.fields);
1674
- if (this.logging) {
1675
- console.log('ModelFormComponent::init->id', this.id);
1676
- console.log('ModelFormComponent::init->fields', this.fields);
1677
- console.log('ModelFormComponent::init->keys', this.keys);
1678
- }
1679
- this.form = this.createForm(this.fields);
1680
- if (this.id) {
1681
- this.getObjectById(this.id);
1682
- }
1683
- }
1684
- }
1685
- /**
1686
- * It takes a key as an argument, and returns an array of keys from the sub-object
1687
- */
1688
- getKeysFromSubObject(key) {
1689
- return Object.keys(this.fields[key]);
1690
- }
1691
- /**
1692
- * It takes an id, and then calls the graphQLService to get the object from the database
1693
- *
1694
- * @param id - The id of the object to be loaded
1695
- */
1696
- getObjectById(id) {
1697
- if (!id) {
1698
- return;
1699
- }
1700
- if (this.logging) {
1701
- console.log('ModelFormComponent::getObjectById->id', id);
1702
- console.log('ModelFormComponent::getObjectById->requestFields', this.createRequestObject(this.fields));
1703
- }
1704
- this.graphQLService
1705
- .graphQl('get' + this.capitalizeFirstLetter(this.modelName), {
1706
- arguments: { id },
1707
- fields: this.createRequestObject(this.fields),
1708
- type: GraphQLRequestType.QUERY,
1709
- })
1710
- .subscribe({
1711
- next: (response) => {
1712
- if (this.logging) {
1713
- console.log('ModelFormComponent::get->value', response);
1714
- }
1715
- this.object = response;
1716
- this.form.patchValue({ ...{}, ...response });
1717
- // Process patchFields for reference input
1718
- for (const [key, value] of Object.entries(this.fields)) {
1719
- if (this.nestedObject(key, this.fields)?.type === 'DateTime' ||
1720
- this.nestedObject(key, this.config)?.type === 'DateTime') {
1721
- this.form.get(key).patchValue(this.formatDate(this.nestedObject(key, response)));
1722
- }
1723
- // If it not an array
1724
- if (this.nestedObject(key, this.config)?.type === 'Reference' &&
1725
- !this.nestedObject(key, this.fields)?.isList) {
1726
- if (this.form.get(key)) {
1727
- this.form
1728
- .get(key)
1729
- .patchValue(response[this.nestedObject(key, this.config)?.patchField
1730
- ? this.nestedObject(key, this.config)?.patchField
1731
- : key]?.id);
1732
- }
1733
- }
1734
- // If value is an array
1735
- if (this.nestedObject(key, this.config)?.type === 'Reference' &&
1736
- this.nestedObject(key, this.fields)?.isList) {
1737
- if (this.form.get(key)) {
1738
- this.form
1739
- .get(key)
1740
- .patchValue(response[this.nestedObject(key, this.config)?.patchField
1741
- ? this.nestedObject(key, this.config)?.patchField
1742
- : key]?.map((e) => e?.id));
1743
- }
1744
- }
1745
- }
1746
- if (this.logging) {
1747
- console.log('ModelFormComponent::getObjectById->patchedForm', this.form);
1748
- }
1749
- },
1750
- error: (err) => {
1751
- console.error('Error on load object', err);
1752
- this.finished.emit(null);
1753
- },
1754
- });
1755
- }
1756
- /**
1757
- * It takes a GraphQL type collection and returns an array of strings and objects
1758
- *
1759
- * @param fields - IGraphQLTypeCollection - This is the object that is returned from the
1760
- * introspection query.
1761
- * @returns An array of strings and objects.
1762
- */
1763
- createRequestObject(fields) {
1764
- const requestFields = [];
1765
- for (const [key, value] of Object.entries(fields)) {
1766
- if (this.config[key]?.exclude !== undefined && this.config[key]?.exclude) {
1767
- continue;
1768
- }
1769
- if (Object.keys(fields[key]?.fields)?.length === 0) {
1770
- if (this.config[key]?.type !== 'Reference') {
1771
- requestFields.push(key);
1772
- }
1773
- else {
1774
- requestFields.push({ [this.config[key]?.patchField ? this.config[key]?.patchField : key]: ['id'] });
1775
- }
1776
- }
1777
- else {
1778
- const subkeys = this.createRequestObject(fields[key].fields);
1779
- const resultObject = {};
1780
- resultObject[key] = subkeys;
1781
- requestFields.push(resultObject);
1782
- }
1783
- }
1784
- if (this.logging) {
1785
- console.log('ModelFormComponent::createRequestObject->requestFields', requestFields);
1786
- }
1787
- return requestFields;
1788
- }
1789
- /**
1790
- * It takes a collection of GraphQL types and creates a form group with a form control for each type
1791
- *
1792
- * @param fields - IGraphQLTypeCollection
1793
- * @param parents
1794
- */
1795
- createForm(fields, parents = []) {
1796
- const group = {};
1797
- let internParents = [...[], ...parents];
1798
- for (const [key, value] of Object.entries(fields)) {
1799
- if (Object.keys(value?.fields)?.length === 0) {
1800
- group[key] = new UntypedFormControl(null, this.processValidators(this.nestedObject(internParents.length ? internParents.join('.') + '.' + key : key, this.config), value));
1801
- }
1802
- else {
1803
- internParents.push(key);
1804
- group[key] = this.createForm(fields[key].fields, internParents);
1805
- internParents = [];
1806
- }
1807
- }
1808
- if (this.logging) {
1809
- console.log('ModelFormComponent::createForm->group', group);
1810
- }
1811
- return new UntypedFormGroup(group);
1812
- }
1813
- /**
1814
- * Process form validators for control
1815
- */
1816
- processValidators(config, value) {
1817
- const validators = [];
1818
- if (config) {
1819
- if (config?.required !== undefined ? config?.required : value.isRequired) {
1820
- validators.push(Validators.required);
1821
- }
1822
- if (config?.type === 'Email') {
1823
- validators.push(fullEmail());
1824
- }
1825
- if (config?.type === 'Password') {
1826
- validators.push(Validators.minLength(6));
1827
- }
1828
- if (typeof config?.min === 'number') {
1829
- validators.push(Validators.min(config.min));
1830
- }
1831
- if (typeof config?.max === 'number') {
1832
- validators.push(Validators.max(config.max));
1833
- }
1834
- if (Array.isArray(config?.validators)) {
1835
- validators.push(...config?.validators);
1836
- }
1837
- }
1838
- return validators;
1839
- }
1840
- /**
1841
- * It calls the graphQLService to delete the object with the id of the object that is currently being edited
1842
- */
1843
- deleteObject() {
1844
- this.cmsService.deleteObject(this.id, this.modelName).then(() => {
1845
- this.toastService.show({
1846
- id: this.modelName + '-success',
1847
- type: ToastType.SUCCESS,
1848
- title: 'Erfolgreich',
1849
- description: this.label + ' wurde erfolgreich gelöscht.',
1850
- });
1851
- this.finished.emit(null);
1852
- });
1853
- }
1854
- /**
1855
- * It sends a GraphQL mutation to the server to duplicate the object
1856
- */
1857
- async duplicateObject() {
1858
- this.cmsService.duplicateObject(this.id, this.modelName).then(async (value) => {
1859
- if (value?.id) {
1860
- await this.router.navigate(['../'], { relativeTo: this.route });
1861
- this.toastService.show({
1862
- id: this.modelName + '-success',
1863
- type: ToastType.SUCCESS,
1864
- title: 'Erfolgreich',
1865
- description: this.label + ' wurde erfolgreich dupliziert.',
1866
- });
1867
- }
1868
- else {
1869
- this.finished.emit(null);
1870
- }
1871
- });
1872
- }
1873
- /**
1874
- * It takes a string, capitalizes the first letter, and returns the modified string
1875
- *
1876
- * @param value - The string to capitalize.
1877
- * @returns The first letter of the string is being capitalized and the rest of the string is being returned.
1878
- */
1879
- capitalizeFirstLetter(value) {
1880
- return this.cmsService.capitalizeFirstLetter(value);
1881
- }
1882
- /**
1883
- * It takes an AbstractControl and returns a FormControl
1884
- *
1885
- * @param control - AbstractControl | null | undefined
1886
- * @returns A FormControl
1887
- */
1888
- transformToControl(control) {
1889
- return control;
1890
- }
1891
- /**
1892
- * It takes the key of the form control that has changed, gets the value of that form control, creates an object with the
1893
- * key and value, and then sends that object to the GraphQL server to update the database
1894
- */
1895
- imageChanged(key) {
1896
- const data = {};
1897
- data[key] = this.form.get(key).value;
1898
- if (this.logging) {
1899
- console.log('ModelFormComponent::imageChanged->body', data);
1900
- }
1901
- if (this.id) {
1902
- try {
1903
- this.graphQLService
1904
- .graphQl('update' + this.capitalizeFirstLetter(this.modelName), {
1905
- arguments: { id: this.id, input: data },
1906
- fields: ['id'],
1907
- type: GraphQLRequestType.MUTATION,
1908
- })
1909
- .subscribe({
1910
- next: (value) => {
1911
- if (this.logging) {
1912
- console.log('ModelFormComponent::imageChanged->success', value);
1913
- }
1914
- },
1915
- });
1916
- }
1917
- catch (e) {
1918
- if (this.logging) {
1919
- console.log('ModelFormComponent::imageChanged->error', e);
1920
- }
1921
- }
1922
- }
1923
- }
1924
- /**
1925
- * It takes the form, validates it, and then sends it to the GraphQL server
1926
- *
1927
- * @returns The id of the newly created or updated object.
1928
- */
1929
- submit(secret = false) {
1930
- return new Promise(async (resolve, reject) => {
1931
- if (!secret) {
1932
- this.loading = true;
1933
- }
1934
- if (this.form.invalid && !secret) {
1935
- this.formsService.validateAllFormFields(this.form);
1936
- this.formsService.scrollToInvalidControl(this.form);
1937
- this.loading = false;
1938
- resolve(null);
1939
- return;
1940
- }
1941
- if (this.operation === 'update' && !this.id) {
1942
- console.error('ID is missing for update!');
1943
- this.loading = false;
1944
- resolve(null);
1945
- return;
1946
- }
1947
- if (this.logging) {
1948
- console.log('ModelFormComponent::submit->formValue', this.form.value);
1949
- }
1950
- // Prepare data for server request
1951
- let data = Object.assign({}, this.form.value);
1952
- data = this.prepareDataForRequest(this.config, this.fields, data);
1953
- if (this.logging) {
1954
- console.log('ModelFormComponent::submit->valueToGraphql', data);
1955
- console.log('ModelFormComponent::submit->fileChanges', this.fileChanges);
1956
- }
1957
- if (this.fileChanges?.length) {
1958
- for (const fileChange of this.fileChanges) {
1959
- const key = fileChange.field;
1960
- const config = this.nestedObject(key, this.config);
1961
- if (fileChange.file) {
1962
- // Upload
1963
- const value = this.form.get(key).value;
1964
- // Check if file is an ID or a base64 string
1965
- if (typeof value === 'string') {
1966
- // Delete if ID
1967
- if (!this.form.get(key).value?.startsWith('data:')) {
1968
- const deleteResult = await this.fileService.delete(this.nestedObject(key, this.config)?.url, this.nestedObject(key, this.config)?.deletePath || '/files/', this.nestedObject(key, this.object));
1969
- if (deleteResult) {
1970
- this.nestedObject(key, data, '');
1971
- }
1972
- }
1973
- const uploadResult = await this.fileService.upload(this.nestedObject(key, this.config)?.url, this.nestedObject(key, this.config)?.uploadPath || '/files/upload', fileChange.file);
1974
- if (uploadResult?.id) {
1975
- this.nestedObject(key, data, uploadResult.id);
1976
- }
1977
- }
1978
- else if (config?.imageMode && config?.imageMode !== 'file') {
1979
- // Delete
1980
- const result = await this.fileService.delete(config?.url, this.nestedObject(key, this.config)?.deletePath || '/files/', this.nestedObject(key, this.object));
1981
- if (result) {
1982
- this.nestedObject(key, data, '');
1983
- }
1984
- }
1985
- }
1986
- }
1987
- }
1988
- this.graphQLService
1989
- .graphQl(this.operation + this.capitalizeFirstLetter(this.modelName), {
1990
- arguments: this.operation === 'update' ? { id: this.id, input: data } : { input: data },
1991
- fields: ['id'],
1992
- type: GraphQLRequestType.MUTATION,
1993
- })
1994
- .subscribe({
1995
- next: (value) => {
1996
- if (!secret) {
1997
- this.loading = false;
1998
- this.finished.emit(null);
1999
- this.toastService.show({
2000
- id: this.modelName + '-success',
2001
- type: ToastType.SUCCESS,
2002
- title: 'Erfolgreich',
2003
- description: this.label + ' wurde erfolgreich ' + (this.operation === 'create' ? 'erstellt.' : 'aktualisiert.'),
2004
- });
2005
- }
2006
- resolve(value);
2007
- },
2008
- error: (err) => {
2009
- if (!secret) {
2010
- console.error('Failed on ' + this.operation, err);
2011
- this.loading = false;
2012
- this.toastService.show({
2013
- id: this.modelName + '-success',
2014
- type: ToastType.ERROR,
2015
- description: 'Beim ' +
2016
- (this.operation === 'create' ? 'erstellen' : 'aktualisieren') +
2017
- ' von ' +
2018
- this.label +
2019
- ' ist etwas schiefgelaufen.',
2020
- });
2021
- }
2022
- reject();
2023
- },
2024
- });
2025
- });
2026
- }
2027
- /**
2028
- * Prepare data for server
2029
- */
2030
- prepareDataForRequest(config, fieldsConfig, data) {
2031
- if (!data) {
2032
- return data;
2033
- }
2034
- for (const [key, value] of Object.entries(data)) {
2035
- if ((this.fields[key]?.type === 'DateTime' || this.config[key]?.type === 'DateTime') && value === '') {
2036
- data[key] = null;
2037
- continue;
2038
- }
2039
- if ((config[key]?.roles ? !this.user.hasAllRoles(config[key]?.roles) : false) || config[key]?.exclude) {
2040
- delete data[key];
2041
- continue;
2042
- }
2043
- if (value && typeof value === 'object' && !Array.isArray(value) && this.fields[key]?.type !== 'Upload') {
2044
- data[key] = Object.keys(data[key])
2045
- .filter((k) => data[key][k] != null)
2046
- .reduce((a, k) => ({ ...a, [k]: data[key][k] }), {});
2047
- if (Object.keys(data[key])?.length === 0) {
2048
- if (this.logging) {
2049
- console.log('Object is empty from result');
2050
- }
2051
- data[key] = null;
2052
- }
2053
- }
2054
- // Set type for graphql method
2055
- if (fieldsConfig[key]?.type === 'Float') {
2056
- if (value) {
2057
- data[key] = Number(value);
2058
- }
2059
- else {
2060
- data[key] = null;
2061
- }
2062
- }
2063
- if (fieldsConfig[key]?.fields && Object.keys(fieldsConfig[key]?.fields).length > 0) {
2064
- data[key] = this.prepareDataForRequest(config[key]?.fields, fieldsConfig[key]?.fields, data[key]);
2065
- }
2066
- }
2067
- return data;
2068
- }
2069
- /**
2070
- * Init fab buttons for mobile devices
2071
- */
2072
- initFabActions() {
2073
- const saveEvent = new EventEmitter();
2074
- this.fabButtons.push({ icon: 'bi-check-lg', color: 'var(--bs-success)', event: saveEvent });
2075
- saveEvent.subscribe(() => this.submit(false));
2076
- if (this.duplicate && this.operation === 'update') {
2077
- const event = new EventEmitter();
2078
- this.fabButtons.push({ icon: 'bi-back', color: 'var(--bs-info)', event });
2079
- event.subscribe(() => this.duplicateObject());
2080
- }
2081
- if (this.operation === 'update' && this.delete) {
2082
- const event = new EventEmitter();
2083
- this.fabButtons.push({ icon: 'bi-trash3', color: 'var(--bs-danger)', event });
2084
- event.subscribe(() => this.deleteObject());
2085
- }
2086
- }
2087
- /**
2088
- * Set file changes for process
2089
- */
2090
- processFileChanges(event) {
2091
- const index = this.fileChanges?.findIndex((e) => e.field === event.field);
2092
- if (index !== undefined && index !== -1) {
2093
- this.fileChanges[index] = event;
2094
- }
2095
- else if (this.fileChanges) {
2096
- this.fileChanges.push(event);
2097
- }
2098
- else {
2099
- this.fileChanges = [event];
2100
- }
2101
- }
2102
- formatDate(date) {
2103
- if (!date) {
2104
- return null;
2105
- }
2106
- const newDate = new Date(date);
2107
- return newDate.toISOString().substring(0, 16);
2108
- }
2109
- nestedObject(key, nestedObject, value) {
2110
- const props = key.split('.');
2111
- let result = nestedObject;
2112
- for (let i = 0; i < props.length; i++) {
2113
- if (value && i === props.length - 1) {
2114
- result[props[i]] = value;
2115
- return result[props[i]];
2116
- }
2117
- result =
2118
- result[props[i]]?.fields && Object.keys(result[props[i]]?.fields)?.length > 0
2119
- ? result[props[i]]?.fields
2120
- : result[props[i]];
2121
- }
2122
- return result;
2123
- }
2124
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ModelFormComponent, deps: [{ token: i1.GraphQLMetaService }, { token: i1.GraphQLService }, { token: i1.FormsService }, { token: i1.AuthService }, { token: i2.Router }, { token: i2.ActivatedRoute }, { token: i1.CmsService }, { token: i1.ToastService }, { token: i1.FileService }], target: i0.ɵɵFactoryTarget.Component });
2125
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: ModelFormComponent, isStandalone: true, selector: "base-model-form", inputs: { modelName: "modelName", label: "label", id: "id", delete: "delete", onlyUpdateMode: "onlyUpdateMode", duplicate: "duplicate", logging: "logging", config: "config", showFavButton: "showFavButton" }, outputs: { finished: "finished" }, usesOnChanges: true, ngImport: i0, template: "@if (form) {\n <div class=\"w-100\">\n <div class=\"d-flex flex-row justify-content-between mt-3\">\n <div class=\"responsive-back d-flex d-lg-none\">\n <i class=\"bi bi-arrow-left\" routerLink=\"../\"></i>\n </div>\n <h2 class=\"mb-0\">\n {{ label ? label : capitalizeFirstLetter(modelName) }} {{ operation === 'update' ? 'bearbeiten' : 'erstellen' }}\n </h2>\n <div class=\"d-none d-lg-flex flex-row align-items-center justify-content-end\">\n @if (id && duplicate && !_loading) {\n <button class=\"btn btn-outline-primary me-2\" (click)=\"duplicateObject()\">\n Duplizieren\n </button>\n }\n @if (!_loading) {\n <button\n class=\"btn btn-primary\"\n type=\"button\"\n [disabled]=\"!form.touched && !form.dirty\"\n (click)=\"submit(false)\"\n >\n {{ operation === 'create' ? 'Erstellen' : 'Speichern' }}\n </button>\n }\n @if (_loading) {\n <button class=\"btn btn-primary\" type=\"button\" disabled>\n <span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span>\n {{ operation === 'create' ? 'Erstellen' : 'Speichern' }}...\n </button>\n }\n </div>\n </div>\n <form [formGroup]=\"form\">\n <base-model-form-sub\n [id]=\"id\"\n [fields]=\"fields\"\n [config]=\"config\"\n [form]=\"form\"\n (imageChanged)=\"imageChanged($event)\"\n (fileChanged)=\"processFileChanges($event)\"\n ></base-model-form-sub>\n <div class=\"d-none d-lg-flex mt-4\" [ngClass]=\"id && delete ? 'justify-content-between' : 'justify-content-end'\">\n @if (id && delete) {\n <div>\n <button class=\"btn btn-danger\" type=\"button\" (click)=\"deleteObject()\">L\u00F6schen</button>\n </div>\n }\n <div>\n @if (!_loading) {\n <button\n class=\"btn btn-primary\"\n type=\"button\"\n [disabled]=\"!form.touched && !form.dirty\"\n (click)=\"submit(false)\"\n >\n {{ operation === 'create' ? 'Erstellen' : 'Speichern' }}\n </button>\n }\n @if (_loading) {\n <button class=\"btn btn-primary\" type=\"button\" disabled>\n <span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span>\n {{ operation === 'create' ? 'Erstellen' : 'Speichern' }}...\n </button>\n }\n </div>\n </div>\n </form>\n</div>\n}\n\n@if (showFavButton) {\n <div class=\"d-block d-lg-none responsive-action-button\">\n <base-fab-button [buttons]=\"fabButtons\"></base-fab-button>\n </div>\n}\n", styles: [".responsive-back{display:flex;align-items:center;margin-right:6px}.responsive-back .bi-arrow-left{font-size:28px}h2{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.responsive-action-button{position:fixed;bottom:15px;right:15px;z-index:100}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: ModelFormSubComponent, selector: "base-model-form-sub", inputs: ["id", "parentKey", "fields", "form", "config"], outputs: ["imageChanged", "fileChanged"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: FabButtonComponent, selector: "base-fab-button", inputs: ["fabIcon", "buttons"] }] });
2126
- }
2127
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ModelFormComponent, decorators: [{
2128
- type: Component,
2129
- args: [{ selector: 'base-model-form', standalone: true, imports: [
2130
- RouterLink,
2131
- ReactiveFormsModule,
2132
- ModelFormSubComponent,
2133
- NgClass,
2134
- FabButtonComponent
2135
- ], template: "@if (form) {\n <div class=\"w-100\">\n <div class=\"d-flex flex-row justify-content-between mt-3\">\n <div class=\"responsive-back d-flex d-lg-none\">\n <i class=\"bi bi-arrow-left\" routerLink=\"../\"></i>\n </div>\n <h2 class=\"mb-0\">\n {{ label ? label : capitalizeFirstLetter(modelName) }} {{ operation === 'update' ? 'bearbeiten' : 'erstellen' }}\n </h2>\n <div class=\"d-none d-lg-flex flex-row align-items-center justify-content-end\">\n @if (id && duplicate && !_loading) {\n <button class=\"btn btn-outline-primary me-2\" (click)=\"duplicateObject()\">\n Duplizieren\n </button>\n }\n @if (!_loading) {\n <button\n class=\"btn btn-primary\"\n type=\"button\"\n [disabled]=\"!form.touched && !form.dirty\"\n (click)=\"submit(false)\"\n >\n {{ operation === 'create' ? 'Erstellen' : 'Speichern' }}\n </button>\n }\n @if (_loading) {\n <button class=\"btn btn-primary\" type=\"button\" disabled>\n <span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span>\n {{ operation === 'create' ? 'Erstellen' : 'Speichern' }}...\n </button>\n }\n </div>\n </div>\n <form [formGroup]=\"form\">\n <base-model-form-sub\n [id]=\"id\"\n [fields]=\"fields\"\n [config]=\"config\"\n [form]=\"form\"\n (imageChanged)=\"imageChanged($event)\"\n (fileChanged)=\"processFileChanges($event)\"\n ></base-model-form-sub>\n <div class=\"d-none d-lg-flex mt-4\" [ngClass]=\"id && delete ? 'justify-content-between' : 'justify-content-end'\">\n @if (id && delete) {\n <div>\n <button class=\"btn btn-danger\" type=\"button\" (click)=\"deleteObject()\">L\u00F6schen</button>\n </div>\n }\n <div>\n @if (!_loading) {\n <button\n class=\"btn btn-primary\"\n type=\"button\"\n [disabled]=\"!form.touched && !form.dirty\"\n (click)=\"submit(false)\"\n >\n {{ operation === 'create' ? 'Erstellen' : 'Speichern' }}\n </button>\n }\n @if (_loading) {\n <button class=\"btn btn-primary\" type=\"button\" disabled>\n <span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span>\n {{ operation === 'create' ? 'Erstellen' : 'Speichern' }}...\n </button>\n }\n </div>\n </div>\n </form>\n</div>\n}\n\n@if (showFavButton) {\n <div class=\"d-block d-lg-none responsive-action-button\">\n <base-fab-button [buttons]=\"fabButtons\"></base-fab-button>\n </div>\n}\n", styles: [".responsive-back{display:flex;align-items:center;margin-right:6px}.responsive-back .bi-arrow-left{font-size:28px}h2{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.responsive-action-button{position:fixed;bottom:15px;right:15px;z-index:100}\n"] }]
2136
- }], ctorParameters: () => [{ type: i1.GraphQLMetaService }, { type: i1.GraphQLService }, { type: i1.FormsService }, { type: i1.AuthService }, { type: i2.Router }, { type: i2.ActivatedRoute }, { type: i1.CmsService }, { type: i1.ToastService }, { type: i1.FileService }], propDecorators: { modelName: [{
2137
- type: Input
2138
- }], label: [{
2139
- type: Input
2140
- }], id: [{
2141
- type: Input
2142
- }], delete: [{
2143
- type: Input
2144
- }], onlyUpdateMode: [{
2145
- type: Input
2146
- }], duplicate: [{
2147
- type: Input
2148
- }], logging: [{
2149
- type: Input
2150
- }], config: [{
2151
- type: Input
2152
- }], showFavButton: [{
2153
- type: Input
2154
- }], finished: [{
2155
- type: Output
2156
- }] } });
2157
-
2158
- /**
2159
- * Abstract Component for inputs and outputs only
2160
- *
2161
- * Hint for handling extensions of this component in project:
2162
- * If you want to use complete custom services and methods, extend ModelFormComponent instead of ModelFormBaseComponent
2163
- */
2164
- class ModelFormBaseComponent {
2165
- cmsService;
2166
- toastService;
2167
- router;
2168
- route;
2169
- modelName;
2170
- label;
2171
- id;
2172
- delete = true;
2173
- onlyUpdateMode = false;
2174
- duplicate = false;
2175
- logging = false;
2176
- config = null;
2177
- showFavButton = true;
2178
- finished = new EventEmitter();
2179
- fabButtons = [];
2180
- operation;
2181
- constructor(cmsService, toastService, router, route) {
2182
- this.cmsService = cmsService;
2183
- this.toastService = toastService;
2184
- this.router = router;
2185
- this.route = route;
2186
- }
2187
- ngOnInit() {
2188
- this.initFabActions();
2189
- }
2190
- /**
2191
- * Init fab buttons for mobile devices
2192
- */
2193
- initFabActions() {
2194
- const saveEvent = new EventEmitter();
2195
- this.fabButtons.push({ icon: 'bi-check-lg', color: 'var(--bs-success)', event: saveEvent });
2196
- saveEvent.subscribe(() => this.submit());
2197
- if (this.duplicate && this.operation === 'update') {
2198
- const event = new EventEmitter();
2199
- this.fabButtons.push({ icon: 'bi-back', color: 'var(--bs-info)', event });
2200
- event.subscribe(() => this.duplicateObject());
2201
- }
2202
- if (this.operation === 'update' && this.delete) {
2203
- const event = new EventEmitter();
2204
- this.fabButtons.push({ icon: 'bi-trash3', color: 'var(--bs-danger)', event });
2205
- event.subscribe(() => this.deleteObject());
2206
- }
2207
- }
2208
- submit() { }
2209
- /**
2210
- * It calls the graphQLService to delete the object with the id of the object that is currently being edited
2211
- */
2212
- deleteObject() {
2213
- this.cmsService.deleteObject(this.id, this.modelName).then(() => {
2214
- this.toastService.show({
2215
- id: this.modelName + '-success',
2216
- type: ToastType.SUCCESS,
2217
- title: 'Erfolgreich',
2218
- description: this.label + ' wurde erfolgreich gelöscht.',
2219
- });
2220
- this.finished.emit(null);
2221
- });
2222
- }
2223
- /**
2224
- * It sends a GraphQL mutation to the server to duplicate the object
2225
- */
2226
- async duplicateObject() {
2227
- this.cmsService.duplicateObject(this.id, this.modelName).then(async (value) => {
2228
- if (value?.id) {
2229
- await this.router.navigate(['../'], { relativeTo: this.route });
2230
- this.toastService.show({
2231
- id: this.modelName + '-success',
2232
- type: ToastType.SUCCESS,
2233
- title: 'Erfolgreich',
2234
- description: this.label + ' wurde erfolgreich dupliziert.',
2235
- });
2236
- }
2237
- else {
2238
- this.finished.emit(null);
2239
- }
2240
- });
2241
- }
2242
- /**
2243
- * It takes a string, capitalizes the first letter, and returns the modified string
2244
- *
2245
- * @param value - The string to capitalize.
2246
- * @returns The first letter of the string is being capitalized and the rest of the string is being returned.
2247
- */
2248
- capitalizeFirstLetter(value) {
2249
- return this.cmsService.capitalizeFirstLetter(value);
2250
- }
2251
- /**
2252
- * It takes an AbstractControl and returns a FormControl
2253
- *
2254
- * @param control - AbstractControl | null | undefined
2255
- * @returns A FormControl
2256
- */
2257
- transformToControl(control) {
2258
- return control;
2259
- }
2260
- formatDate(date) {
2261
- if (!date) {
2262
- return null;
2263
- }
2264
- const newDate = new Date(date);
2265
- return newDate.toISOString().substring(0, 16);
2266
- }
2267
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ModelFormBaseComponent, deps: [{ token: i1.CmsService }, { token: i1.ToastService }, { token: i2.Router }, { token: i2.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Component });
2268
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.2", type: ModelFormBaseComponent, isStandalone: true, selector: "ng-component", inputs: { modelName: "modelName", label: "label", id: "id", delete: "delete", onlyUpdateMode: "onlyUpdateMode", duplicate: "duplicate", logging: "logging", config: "config", showFavButton: "showFavButton" }, outputs: { finished: "finished" }, ngImport: i0, template: '', isInline: true });
2269
- }
2270
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ModelFormBaseComponent, decorators: [{
2271
- type: Component,
2272
- args: [{
2273
- template: '',
2274
- standalone: true,
2275
- }]
2276
- }], ctorParameters: () => [{ type: i1.CmsService }, { type: i1.ToastService }, { type: i2.Router }, { type: i2.ActivatedRoute }], propDecorators: { modelName: [{
2277
- type: Input
2278
- }], label: [{
2279
- type: Input
2280
- }], id: [{
2281
- type: Input
2282
- }], delete: [{
2283
- type: Input
2284
- }], onlyUpdateMode: [{
2285
- type: Input
2286
- }], duplicate: [{
2287
- type: Input
2288
- }], logging: [{
2289
- type: Input
2290
- }], config: [{
2291
- type: Input
2292
- }], showFavButton: [{
2293
- type: Input
2294
- }], finished: [{
2295
- type: Output
2296
- }] } });
2297
-
2298
- class ModelTableComponent {
2299
- graphQLMetaService;
2300
- graphQLService;
2301
- cmsService;
2302
- toastService;
2303
- scrollService;
2304
- storageService;
2305
- modelName;
2306
- objectId;
2307
- createMode = false;
2308
- logging = false;
2309
- create = true;
2310
- update = true;
2311
- delete = true;
2312
- import = true;
2313
- export = true;
2314
- duplicate = true;
2315
- modelConfig = null;
2316
- config;
2317
- showFavButton = false;
2318
- idSelected = new EventEmitter();
2319
- createModeChanged = new EventEmitter();
2320
- customFormTemplate;
2321
- uniqueField = 'id';
2322
- tableFields = {
2323
- id: { label: 'ID' },
2324
- name: { label: 'Name' },
2325
- title: { label: 'Titel' },
2326
- description: { label: 'Beschreibung' },
2327
- email: { label: 'E-Mail' },
2328
- createdAt: { label: 'Erstellt am', dateFormat: 'dd.MM.YYYY HH:mm' },
2329
- updatedAt: { label: 'Bearbeitet am', dateFormat: 'dd.MM.YYYY HH:mm' },
2330
- };
2331
- meta;
2332
- objects = [];
2333
- availableFields = [];
2334
- id = '';
2335
- queryName = null;
2336
- camelModelName;
2337
- possibleFields = {};
2338
- componentRef = undefined;
2339
- totalCount;
2340
- selectedPageIndex = 0;
2341
- pages = [];
2342
- pageItemsLimitControl = new FormControl(25);
2343
- pageOptions = [
2344
- {
2345
- text: '25',
2346
- value: 25,
2347
- },
2348
- {
2349
- text: '50',
2350
- value: 50,
2351
- },
2352
- {
2353
- text: '75',
2354
- value: 75,
2355
- },
2356
- {
2357
- text: '100',
2358
- value: 100,
2359
- },
2360
- {
2361
- text: '200',
2362
- value: 200,
2363
- },
2364
- ];
2365
- subscription = new Subscription();
2366
- constructor(graphQLMetaService, graphQLService, cmsService, toastService, scrollService, storageService) {
2367
- this.graphQLMetaService = graphQLMetaService;
2368
- this.graphQLService = graphQLService;
2369
- this.cmsService = cmsService;
2370
- this.toastService = toastService;
2371
- this.scrollService = scrollService;
2372
- this.storageService = storageService;
2373
- }
2374
- ngAfterViewInit() {
2375
- // Needed time out to prevent ExpressionChangedAfterItHasBeenCheckedError
2376
- setTimeout(() => {
2377
- this.integrateCustomFormComponent();
2378
- }, 10);
2379
- }
2380
- ngOnInit() {
2381
- const pageItemsLimit = this.storageService.load('pageItemsLimit');
2382
- this.pageItemsLimitControl.setValue(pageItemsLimit ? pageItemsLimit : 25, { onlySelf: true, emitEvent: false });
2383
- this.graphQLMetaService.getMeta().subscribe((meta) => {
2384
- this.meta = meta;
2385
- if (this.modelName) {
2386
- this.init();
2387
- }
2388
- if (this.objectId) {
2389
- this.id = this.objectId;
2390
- // Needed time out for rerender
2391
- setTimeout(() => {
2392
- this.integrateCustomFormComponent();
2393
- }, 10);
2394
- }
2395
- });
2396
- }
2397
- ngOnChanges(changes) {
2398
- if (changes['modelName'] && this.meta) {
2399
- this.init();
2400
- }
2401
- if (changes['objectId'] && this.meta) {
2402
- if (this.id !== this.objectId) {
2403
- if (this.logging) {
2404
- console.log('ModelTableComponent::ngOnChanges->selectedId', this.id);
2405
- }
2406
- this.id = this.objectId;
2407
- }
2408
- }
2409
- this.setChangesInComponentRef();
2410
- }
2411
- ngOnDestroy() {
2412
- this.subscription.unsubscribe();
2413
- }
2414
- /**
2415
- * We're using the `meta` service to get the fields that are available for the `find` query for the model we're working
2416
- * with. We're then using that information to populate the `availableFields` array
2417
- */
2418
- async init() {
2419
- this.availableFields = [];
2420
- this.pages = [];
2421
- this.totalCount = null;
2422
- this.camelModelName = this.kebabToCamelCase(this.modelName);
2423
- // Set query name
2424
- if (!this.modelConfig?.queryName) {
2425
- this.queryName = 'findAndCount' + this.capitalizeFirstLetter(this.camelModelName) + 's';
2426
- }
2427
- else {
2428
- this.queryName = this.modelConfig?.queryName;
2429
- }
2430
- if (this.modelConfig?.tableFields) {
2431
- this.tableFields = this.modelConfig.tableFields;
2432
- }
2433
- if (this.logging) {
2434
- console.log('ModelTableComponent::init->modelName', this.modelName);
2435
- console.log('ModelTableComponent::init->camelModelName', this.camelModelName);
2436
- console.log('ModelTableComponent::init->queryName', this.queryName);
2437
- console.log('ModelTableComponent::init->tableFields', this.tableFields);
2438
- }
2439
- this.possibleFields = this.meta.getFields(this.queryName).fields;
2440
- let keys;
2441
- if (this.possibleFields?.items && Object.keys(this.possibleFields?.items?.fields).length > 0) {
2442
- keys = Object.keys(this.possibleFields.items.fields);
2443
- }
2444
- else {
2445
- keys = Object.keys(this.possibleFields);
2446
- }
2447
- const tableFieldKeys = Object.keys(this.tableFields);
2448
- tableFieldKeys?.forEach((field) => {
2449
- if (keys.includes(field)) {
2450
- this.availableFields.push(field);
2451
- }
2452
- });
2453
- if (this.logging) {
2454
- console.log('ModelTableComponent::init->possibleFields', this.possibleFields);
2455
- console.log('ModelTableComponent::init->keys', keys);
2456
- console.log('ModelTableComponent::init->availableFields', this.availableFields);
2457
- }
2458
- this.selectedPageIndex = this.storageService.load('pageIndex') ? this.storageService.load('pageIndex') : 0;
2459
- this.objects = await this.loadObjects(this.availableFields, 0, this.pageItemsLimitControl.value);
2460
- this.subscribeToPageItems();
2461
- }
2462
- subscribeToPageItems() {
2463
- this.subscription.add(this.pageItemsLimitControl.valueChanges.subscribe({
2464
- next: async (value) => {
2465
- this.selectedPageIndex = 0;
2466
- this.storageService.save('pageIndex', this.selectedPageIndex);
2467
- this.objects = await this.loadObjects(this.availableFields, 0, value);
2468
- this.storageService.save('pageItemsLimit', value);
2469
- },
2470
- }));
2471
- }
2472
- integrateCustomFormComponent() {
2473
- if (!this.customFormTemplate || this.componentRef) {
2474
- return;
2475
- }
2476
- const customComponent = this.modelConfig?.customTemplateComponent;
2477
- if (customComponent) {
2478
- this.componentRef = this.customFormTemplate?.createComponent(customComponent);
2479
- this.setChangesInComponentRef();
2480
- this.componentRef.instance.finished.subscribe(() => {
2481
- this.selectId('');
2482
- });
2483
- }
2484
- }
2485
- setChangesInComponentRef() {
2486
- if (this.componentRef) {
2487
- const data = {
2488
- config: this.config,
2489
- delete: this.delete,
2490
- duplicate: this.duplicate,
2491
- id: this.id,
2492
- label: this.modelConfig?.label,
2493
- logging: this.logging,
2494
- modelName: this.camelModelName,
2495
- showFavButton: this.showFavButton,
2496
- };
2497
- for (const [key, value] of Object.entries(data)) {
2498
- this.componentRef.setInput(key, value);
2499
- }
2500
- }
2501
- }
2502
- /**
2503
- * When the user clicks the "Create New" button, the createModeChanged event is emitted, which is then handled by the
2504
- * parent component
2505
- */
2506
- createNewObject() {
2507
- this.createMode = true;
2508
- this.createModeChanged.emit(true);
2509
- }
2510
- /**
2511
- * It loads the objects from the database and stores them in the objects variable
2512
- *
2513
- * @param fields - string[] - The fields you want to load from the database.
2514
- * @param skip
2515
- * @param limit
2516
- */
2517
- loadObjects(fields, skip = 0, limit = 25) {
2518
- return new Promise((resolve, reject) => {
2519
- const requestFields = [...fields];
2520
- const idInFields = requestFields.find((e) => e === 'id');
2521
- if (!idInFields) {
2522
- requestFields.push('id');
2523
- }
2524
- const isFindAndCount = this.queryName.includes('findAndCount');
2525
- this.graphQLService
2526
- .graphQl(this.queryName, {
2527
- arguments: {
2528
- limit: isFindAndCount ? limit : null,
2529
- skip: isFindAndCount ? skip : null,
2530
- },
2531
- fields: isFindAndCount ? ['totalCount', { items: requestFields }] : requestFields,
2532
- type: GraphQLRequestType.QUERY,
2533
- })
2534
- .subscribe({
2535
- next: (value) => {
2536
- let items;
2537
- if (this.logging) {
2538
- console.log('ModelTableComponent::loadObjects->value', value);
2539
- }
2540
- if (value?.totalCount >= 0) {
2541
- items = value.items;
2542
- this.totalCount = value.totalCount;
2543
- this.pages = [];
2544
- let numbers = 0;
2545
- while (numbers < this.totalCount) {
2546
- this.pages.push({
2547
- skip: numbers,
2548
- limit: this.pageItemsLimitControl.value,
2549
- });
2550
- numbers = numbers + this.pageItemsLimitControl.value;
2551
- }
2552
- const loadedIndex = this.storageService.load('pageIndex');
2553
- if (loadedIndex) {
2554
- if (loadedIndex > this.pages.length) {
2555
- this.selectedPageIndex = 0;
2556
- this.storageService.save('pageIndex');
2557
- }
2558
- }
2559
- }
2560
- else {
2561
- items = value;
2562
- }
2563
- resolve(items);
2564
- },
2565
- error: (err) => {
2566
- console.log('Error on loading objects', err);
2567
- reject(err);
2568
- },
2569
- });
2570
- });
2571
- }
2572
- /**
2573
- * If the update property is true, then set the selectedId property to the id of the object passed in, and emit the
2574
- * selectedId property
2575
- */
2576
- selectId(object) {
2577
- if (!object) {
2578
- this.createMode = false;
2579
- this.createModeChanged.emit(false);
2580
- }
2581
- if (this.update) {
2582
- this.id = object['id'];
2583
- this.idSelected.emit(this.id);
2584
- }
2585
- }
2586
- openNewTab(object) {
2587
- window.open(location.href + '/' + object['id'], '_blank');
2588
- }
2589
- /**
2590
- * It calls the duplicateObject function in the cmsService, passing in the id of the object to be duplicated and the
2591
- * modelName of the object
2592
- */
2593
- async duplicateObject(id) {
2594
- await this.cmsService.duplicateObject(id, this.camelModelName);
2595
- this.selectedPageIndex = this.storageService.load('pageIndex') ? this.storageService.load('pageIndex') : 0;
2596
- this.objects = await this.loadObjects(this.availableFields, this.pages[this.selectedPageIndex].skip, this.pages[this.selectedPageIndex].limit);
2597
- }
2598
- /**
2599
- * It calls the deleteObject function in the cmsService, passing in the id of the object to delete and the name of the
2600
- * model
2601
- */
2602
- async deleteObject(id) {
2603
- await this.cmsService.deleteObject(id, this.camelModelName);
2604
- this.selectedPageIndex = this.storageService.load('pageIndex') ? this.storageService.load('pageIndex') : 0;
2605
- this.objects = await this.loadObjects(this.availableFields, this.pages[this.selectedPageIndex].skip, this.pages[this.selectedPageIndex].limit);
2606
- }
2607
- /**
2608
- * It takes a string, capitalizes the first letter, and returns the modified string
2609
- */
2610
- capitalizeFirstLetter(value) {
2611
- return this.cmsService.capitalizeFirstLetter(value);
2612
- }
2613
- /**
2614
- * Kebab to camel case string
2615
- */
2616
- kebabToCamelCase(str) {
2617
- return str.replace(/-./g, (match) => match[1].toUpperCase());
2618
- }
2619
- /**
2620
- * Import objects from json file
2621
- */
2622
- processImport(event) {
2623
- // Create a new FileReader() object
2624
- const reader = new FileReader();
2625
- // Setup the callback event to run when the file is read
2626
- reader.onload = async (readerEvent) => {
2627
- let jsonResult = JSON.parse(readerEvent.target.result);
2628
- if (!Array.isArray(jsonResult)) {
2629
- jsonResult = [jsonResult];
2630
- }
2631
- for (let i = 0; i < jsonResult.length; i++) {
2632
- try {
2633
- this.graphQLService
2634
- .graphQl('create' + this.capitalizeFirstLetter(this.modelName), {
2635
- arguments: { input: jsonResult[i] },
2636
- fields: ['id'],
2637
- type: GraphQLRequestType.MUTATION,
2638
- })
2639
- .subscribe({
2640
- next: async () => {
2641
- this.toastService.show({
2642
- id: this.modelName + '-' + i + '-success',
2643
- type: ToastType.SUCCESS,
2644
- title: 'Erfolgreich',
2645
- description: 'Element ' + (i + 1) + ' von ' + jsonResult.length + ' erfolgreich importiert.',
2646
- });
2647
- if (i === jsonResult.length - 1) {
2648
- this.selectedPageIndex = this.storageService.load('pageIndex')
2649
- ? this.storageService.load('pageIndex')
2650
- : 0;
2651
- this.objects = await this.loadObjects(this.availableFields, this.pages[this.selectedPageIndex].skip, this.pages[this.selectedPageIndex].limit);
2652
- }
2653
- },
2654
- error: () => {
2655
- this.toastService.show({
2656
- id: this.modelName + '-' + i + '-error',
2657
- type: ToastType.ERROR,
2658
- title: 'Error',
2659
- description: 'Element ' + (i + 1) + ' von ' + jsonResult.length + ' konnte nicht importiert werden.',
2660
- });
2661
- },
2662
- });
2663
- }
2664
- catch (e) {
2665
- this.toastService.show({
2666
- id: this.modelName + '-' + i + '-error',
2667
- type: ToastType.ERROR,
2668
- title: 'Error',
2669
- description: 'Element ' + (i + 1) + ' von ' + jsonResult.length + ' konnte nicht importiert werden.',
2670
- });
2671
- }
2672
- }
2673
- };
2674
- // Read the file
2675
- reader.readAsText(event.target.files[0]);
2676
- }
2677
- /**
2678
- * Export objects as json
2679
- */
2680
- async processExport() {
2681
- const keys = this.getAllKeys();
2682
- const items = await this.loadObjects(keys, this.pages[this.selectedPageIndex].skip, this.pages[this.selectedPageIndex].limit);
2683
- const data = JSON.stringify(items);
2684
- const blob = new Blob([data], { type: 'text/json;charset=utf-8;' });
2685
- const url = URL.createObjectURL(blob);
2686
- const link = document.createElement('a');
2687
- link.setAttribute('href', url);
2688
- link.setAttribute('download', this.modelName + 's.json');
2689
- document.body.appendChild(link);
2690
- link.click();
2691
- }
2692
- /**
2693
- * Get all keys for request to export objects as json
2694
- */
2695
- getAllKeys() {
2696
- const items = [];
2697
- for (const [key, value] of Object.entries(this.possibleFields)) {
2698
- if (!this.possibleFields[key].fields || Object.keys(this.possibleFields[key].fields).length === 0) {
2699
- items.push(key);
2700
- }
2701
- }
2702
- return items;
2703
- }
2704
- async selectPage(i) {
2705
- if (!this.pages[i]) {
2706
- return;
2707
- }
2708
- this.selectedPageIndex = i;
2709
- await this.scrollService.scrollTo('model-table-top');
2710
- this.storageService.save('pageIndex', this.selectedPageIndex);
2711
- this.objects = await this.loadObjects(this.availableFields, this.pages[this.selectedPageIndex].skip, this.pages[this.selectedPageIndex].limit);
2712
- }
2713
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ModelTableComponent, deps: [{ token: i1.GraphQLMetaService }, { token: i1.GraphQLService }, { token: i1.CmsService }, { token: i1.ToastService }, { token: i1.ScrollService }, { token: i1.StorageService }], target: i0.ɵɵFactoryTarget.Component });
2714
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: ModelTableComponent, isStandalone: true, selector: "base-model-table", inputs: { modelName: "modelName", objectId: "objectId", createMode: "createMode", logging: "logging", create: "create", update: "update", delete: "delete", import: "import", export: "export", duplicate: "duplicate", modelConfig: "modelConfig", config: "config", showFavButton: "showFavButton" }, outputs: { idSelected: "idSelected", createModeChanged: "createModeChanged" }, viewQueries: [{ propertyName: "customFormTemplate", first: true, predicate: ["customFormTemplate"], descendants: true, read: ViewContainerRef }], usesOnChanges: true, ngImport: i0, template: "@if (!id && !createMode) {\n <div>\n <div class=\"w-100 d-flex flex-row justify-content-between align-items-center mb-2 mb-lg-4\">\n @if (modelName) {\n <h2 id=\"model-table-top\" class=\"mb-0\">\n {{\n modelConfig?.plural\n ? modelConfig?.plural\n : modelConfig?.label\n ? modelConfig?.label\n : capitalizeFirstLetter(camelModelName) + 's'\n }}\n </h2>\n }\n <div class=\"d-flex align-items-center d-none d-lg-block\">\n <input #importFile type=\"file\" accept=\".json\" class=\"d-none\" (change)=\"processImport($event)\" />\n @if (import || export) {\n <div class=\"btn-group me-4\">\n <i\n class=\"bi bi-three-dots-vertical\"\n id=\"settingsDropdown\"\n type=\"button\"\n data-bs-toggle=\"dropdown\"\n data-bs-auto-close=\"true\"\n aria-expanded=\"false\"\n ></i>\n <ul class=\"dropdown-menu dropdown-menu-end\" aria-labelledby=\"settingsDropdown\">\n <li>@if (export) {\n <a class=\"dropdown-item cursor-pointer\" (click)=\"processExport()\">Export</a>\n }</li>\n <li>@if (import) {\n <a class=\"dropdown-item cursor-pointer\" (click)=\"importFile.click()\"> Import </a>\n }</li>\n </ul>\n </div>\n }\n @if (create) {\n <button class=\"btn btn-primary\" (click)=\"createNewObject()\">Erstellen</button>\n }\n </div>\n </div>\n @if (totalCount >= 0) {\n <div class=\"container-fluid\">\n <nav class=\"d-flex row\">\n <div class=\"col-12 col-md-2\"></div>\n <ul class=\"pagination justify-content-center col-12 col-md-8\">\n <li class=\"page-item\">\n <a\n class=\"page-link\"\n (click)=\"selectPage(selectedPageIndex - 1)\"\n aria-label=\"Previous\"\n [ngClass]=\"{ disabled: selectedPageIndex - 1 > 0 }\"\n >\n <span aria-hidden=\"true\">&laquo;</span>\n </a>\n </li>\n @for (page of pages; track page; let i = $index) {\n <li class=\"page-item\">\n <a class=\"page-link\" [ngClass]=\"{ active: selectedPageIndex === i }\" (click)=\"selectPage(i)\">{{ i + 1 }}</a>\n </li>\n }\n <li class=\"page-item\">\n <a\n class=\"page-link\"\n (click)=\"selectPage(selectedPageIndex + 1)\"\n aria-label=\"Next\"\n [ngClass]=\"{ disabled: pages[selectedPageIndex + 1] }\"\n >\n <span aria-hidden=\"true\">&raquo;</span>\n </a>\n </li>\n </ul>\n <div class=\"col-12 col-md-2\">\n <base-select [options]=\"pageOptions\" [control]=\"pageItemsLimitControl\"></base-select>\n </div>\n </nav>\n </div>\n }\n <div\n class=\"table-responsive\"\n [ltContextMenu]=\"contextMenu\"\n [selectors]=\"'.object-col'\"\n #contextMenuData=\"contextMenuData\"\n >\n <table class=\"table table-hover bg-white d-none d-lg-table\">\n <thead>\n <tr>\n @for (field of availableFields; track field) {\n <th\n scope=\"col\"\n [baseSort]=\"objects\"\n (baseSorted)=\"objects = $event\"\n data-order=\"desc\"\n [attr.data-name]=\"field\"\n >\n {{ tableFields[field]?.label ? tableFields[field]?.label : capitalizeFirstLetter(field) }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (object of objects; track object) {\n <tr class=\"object-col\" [id]=\"object[uniqueField]\">\n @for (field of availableFields; track field) {\n <td class=\"cursor-pointer\" (click)=\"selectId(object)\">\n @if (tableFields[field]?.dateFormat) {\n {{ object[field] | date : tableFields[field]?.dateFormat }} Uhr\n }\n @if (tableFields[field]?.enumValues) {\n {{ tableFields[field]?.enumValues[object[field]]?.text }}\n }\n @if (!tableFields[field]?.dateFormat && !tableFields[field]?.enumValues) {\n {{ object[field] | baseEllipses : 60 }}\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n <div class=\"d-flex d-lg-none flex-column flex-grow-1\">\n @for (object of objects; track object) {\n <div class=\"table-card\" [id]=\"object[uniqueField]\">\n @for (field of availableFields; track field; let i = $index) {\n <div class=\"table-card-entry\" (click)=\"selectId(object)\">\n @if (i > 0) {\n <b\n >{{ tableFields[field]?.label ? tableFields[field]?.label : capitalizeFirstLetter(field) }}:</b\n >\n }\n <p [ngClass]=\"{ 'table-card-entry-first': i === 0 }\">\n @if (tableFields[field]?.dateFormat) {\n {{ object[field] | date : tableFields[field]?.dateFormat }} Uhr\n }\n @if (tableFields[field]?.enumValues) {\n {{ tableFields[field]?.enumValues[object[field]]?.text }}\n }\n @if (!tableFields[field]?.dateFormat && !tableFields[field]?.enumValues) {\n {{ object[field] | baseEllipses : 60 }}\n }\n </p>\n </div>\n }\n </div>\n }\n </div>\n @if (totalCount >= 0) {\n <div class=\"container-fluid\">\n <nav class=\"d-flex row\">\n <div class=\"col-12 col-md-2\"></div>\n <ul class=\"pagination justify-content-center col-12 col-md-8\">\n <li class=\"page-item\">\n <a\n class=\"page-link\"\n (click)=\"selectPage(selectedPageIndex - 1)\"\n aria-label=\"Previous\"\n [ngClass]=\"{ disabled: selectedPageIndex - 1 > 0 }\"\n >\n <span aria-hidden=\"true\">&laquo;</span>\n </a>\n </li>\n @for (page of pages; track page; let i = $index) {\n <li class=\"page-item\">\n <a class=\"page-link\" [ngClass]=\"{ active: selectedPageIndex === i }\" (click)=\"selectPage(i)\">{{ i + 1 }}</a>\n </li>\n }\n <li class=\"page-item\">\n <a\n class=\"page-link\"\n (click)=\"selectPage(selectedPageIndex + 1)\"\n aria-label=\"Next\"\n [ngClass]=\"{ disabled: pages[selectedPageIndex + 1] }\"\n >\n <span aria-hidden=\"true\">&raquo;</span>\n </a>\n </li>\n </ul>\n <div class=\"col-12 col-md-2\">\n <base-select [options]=\"pageOptions\" [control]=\"pageItemsLimitControl\"></base-select>\n </div>\n </nav>\n </div>\n }\n <div class=\"context-menu\" #contextMenu>\n <div class=\"context-menu-item\" (click)=\"selectId({ id: contextMenuData.clickedElement?.id })\">Bearbeiten</div>\n <div class=\"context-menu-item\" (click)=\"openNewTab({ id: contextMenuData.clickedElement?.id })\">\n In neuen Tab \u00F6ffnen\n </div>\n @if (duplicate) {\n <div class=\"context-menu-item\" (click)=\"duplicateObject(contextMenuData.clickedElement?.id)\">\n Duplizieren\n </div>\n }\n @if (delete) {\n <div class=\"context-menu-item\" (click)=\"deleteObject(contextMenuData.clickedElement?.id)\">\n L\u00F6schen\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n @if (id || createMode) {\n <div class=\"w-100\">\n <button class=\"d-none d-lg-block btn btn-outline-primary mb-3\" (click)=\"selectId('')\">Zur\u00FCck</button>\n @if (!modelConfig?.customTemplateComponent) {\n <base-model-form\n [modelName]=\"camelModelName\"\n [label]=\"modelConfig?.label\"\n [config]=\"config\"\n [logging]=\"logging\"\n [id]=\"id\"\n (finished)=\"selectId('')\"\n [delete]=\"delete\"\n [duplicate]=\"duplicate\"\n [showFavButton]=\"showFavButton\"\n ></base-model-form>\n }\n @if (modelConfig?.customTemplateComponent) {\n <ng-template #customFormTemplate></ng-template>\n }\n </div>\n }\n\n <div class=\"responsive-action-button d-block d-lg-none\">\n @if (!(id || createMode) && showFavButton) {\n <base-fab-button\n [fabIcon]=\"'bi-plus'\"\n (click)=\"createNewObject()\"\n ></base-fab-button>\n }\n </div>\n", styles: [".cursor-pointer{cursor:pointer}.asc:after,.desc:after{border-style:solid;border-width:.2em .2em 0 0;content:\"\";display:inline-block;height:.45em;left:5px;position:relative;transform:rotate(-45deg);vertical-align:middle;width:.45em}.asc:after{transform:rotate(135deg)}td{vertical-align:middle}.bi,th{cursor:pointer;-webkit-user-select:none;user-select:none}.table>:not(:first-child){border-top:0}table{border-radius:.25rem;border-color:#e2e8f0!important}table thead{text-transform:uppercase;font-size:12px}table thead th{padding:12px 24px}table tbody tr td{font-weight:400!important;font-size:16px;padding:16px 24px}.context-menu{opacity:.01%;pointer-events:none;background-color:#fff;border:1px solid #ccc;box-shadow:1px 1px 10px #0000001a;padding:10px 0;position:fixed;top:0;left:0;width:200px;z-index:999}.context-menu-item{cursor:pointer;padding:8px 15px}.context-menu-item:hover{color:var(--bs-primary)}.table-card{background:var(--bs-white);padding:10px;margin-bottom:10px}.table-card-entry{margin-top:4px}.table-card-entry-first{font-size:18px;font-weight:800;color:var(--bs-primary)}.responsive-action-button{position:fixed;bottom:15px;right:15px}.bi-three-dots-vertical{font-size:18px;line-height:18px}.page-item{cursor:pointer}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: SelectComponent, selector: "base-select", inputs: ["id", "name", "infoText", "label", "placeholder", "objectPath", "control", "tabIndex", "required", "multiple", "search", "options"] }, { kind: "directive", type: ContextMenuDirective, selector: "[ltContextMenu]", inputs: ["contextMenuData", "ltContextMenu", "xGap", "yGap", "overflowSpace", "selectors"], outputs: ["clickedElement"], exportAs: ["contextMenuData"] }, { kind: "directive", type: SortDirective, selector: "[baseSort]", inputs: ["baseSort"], outputs: ["baseSorted"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: EllipsesPipe, name: "baseEllipses" }, { kind: "component", type: ModelFormComponent, selector: "base-model-form", inputs: ["modelName", "label", "id", "delete", "onlyUpdateMode", "duplicate", "logging", "config", "showFavButton"], outputs: ["finished"] }, { kind: "component", type: FabButtonComponent, selector: "base-fab-button", inputs: ["fabIcon", "buttons"] }] });
2715
- }
2716
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ModelTableComponent, decorators: [{
2717
- type: Component,
2718
- args: [{ selector: 'base-model-table', standalone: true, imports: [
2719
- NgClass,
2720
- SelectComponent,
2721
- ContextMenuDirective,
2722
- SortDirective,
2723
- DatePipe,
2724
- EllipsesPipe,
2725
- ModelFormComponent,
2726
- FabButtonComponent,
2727
- ], template: "@if (!id && !createMode) {\n <div>\n <div class=\"w-100 d-flex flex-row justify-content-between align-items-center mb-2 mb-lg-4\">\n @if (modelName) {\n <h2 id=\"model-table-top\" class=\"mb-0\">\n {{\n modelConfig?.plural\n ? modelConfig?.plural\n : modelConfig?.label\n ? modelConfig?.label\n : capitalizeFirstLetter(camelModelName) + 's'\n }}\n </h2>\n }\n <div class=\"d-flex align-items-center d-none d-lg-block\">\n <input #importFile type=\"file\" accept=\".json\" class=\"d-none\" (change)=\"processImport($event)\" />\n @if (import || export) {\n <div class=\"btn-group me-4\">\n <i\n class=\"bi bi-three-dots-vertical\"\n id=\"settingsDropdown\"\n type=\"button\"\n data-bs-toggle=\"dropdown\"\n data-bs-auto-close=\"true\"\n aria-expanded=\"false\"\n ></i>\n <ul class=\"dropdown-menu dropdown-menu-end\" aria-labelledby=\"settingsDropdown\">\n <li>@if (export) {\n <a class=\"dropdown-item cursor-pointer\" (click)=\"processExport()\">Export</a>\n }</li>\n <li>@if (import) {\n <a class=\"dropdown-item cursor-pointer\" (click)=\"importFile.click()\"> Import </a>\n }</li>\n </ul>\n </div>\n }\n @if (create) {\n <button class=\"btn btn-primary\" (click)=\"createNewObject()\">Erstellen</button>\n }\n </div>\n </div>\n @if (totalCount >= 0) {\n <div class=\"container-fluid\">\n <nav class=\"d-flex row\">\n <div class=\"col-12 col-md-2\"></div>\n <ul class=\"pagination justify-content-center col-12 col-md-8\">\n <li class=\"page-item\">\n <a\n class=\"page-link\"\n (click)=\"selectPage(selectedPageIndex - 1)\"\n aria-label=\"Previous\"\n [ngClass]=\"{ disabled: selectedPageIndex - 1 > 0 }\"\n >\n <span aria-hidden=\"true\">&laquo;</span>\n </a>\n </li>\n @for (page of pages; track page; let i = $index) {\n <li class=\"page-item\">\n <a class=\"page-link\" [ngClass]=\"{ active: selectedPageIndex === i }\" (click)=\"selectPage(i)\">{{ i + 1 }}</a>\n </li>\n }\n <li class=\"page-item\">\n <a\n class=\"page-link\"\n (click)=\"selectPage(selectedPageIndex + 1)\"\n aria-label=\"Next\"\n [ngClass]=\"{ disabled: pages[selectedPageIndex + 1] }\"\n >\n <span aria-hidden=\"true\">&raquo;</span>\n </a>\n </li>\n </ul>\n <div class=\"col-12 col-md-2\">\n <base-select [options]=\"pageOptions\" [control]=\"pageItemsLimitControl\"></base-select>\n </div>\n </nav>\n </div>\n }\n <div\n class=\"table-responsive\"\n [ltContextMenu]=\"contextMenu\"\n [selectors]=\"'.object-col'\"\n #contextMenuData=\"contextMenuData\"\n >\n <table class=\"table table-hover bg-white d-none d-lg-table\">\n <thead>\n <tr>\n @for (field of availableFields; track field) {\n <th\n scope=\"col\"\n [baseSort]=\"objects\"\n (baseSorted)=\"objects = $event\"\n data-order=\"desc\"\n [attr.data-name]=\"field\"\n >\n {{ tableFields[field]?.label ? tableFields[field]?.label : capitalizeFirstLetter(field) }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (object of objects; track object) {\n <tr class=\"object-col\" [id]=\"object[uniqueField]\">\n @for (field of availableFields; track field) {\n <td class=\"cursor-pointer\" (click)=\"selectId(object)\">\n @if (tableFields[field]?.dateFormat) {\n {{ object[field] | date : tableFields[field]?.dateFormat }} Uhr\n }\n @if (tableFields[field]?.enumValues) {\n {{ tableFields[field]?.enumValues[object[field]]?.text }}\n }\n @if (!tableFields[field]?.dateFormat && !tableFields[field]?.enumValues) {\n {{ object[field] | baseEllipses : 60 }}\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n <div class=\"d-flex d-lg-none flex-column flex-grow-1\">\n @for (object of objects; track object) {\n <div class=\"table-card\" [id]=\"object[uniqueField]\">\n @for (field of availableFields; track field; let i = $index) {\n <div class=\"table-card-entry\" (click)=\"selectId(object)\">\n @if (i > 0) {\n <b\n >{{ tableFields[field]?.label ? tableFields[field]?.label : capitalizeFirstLetter(field) }}:</b\n >\n }\n <p [ngClass]=\"{ 'table-card-entry-first': i === 0 }\">\n @if (tableFields[field]?.dateFormat) {\n {{ object[field] | date : tableFields[field]?.dateFormat }} Uhr\n }\n @if (tableFields[field]?.enumValues) {\n {{ tableFields[field]?.enumValues[object[field]]?.text }}\n }\n @if (!tableFields[field]?.dateFormat && !tableFields[field]?.enumValues) {\n {{ object[field] | baseEllipses : 60 }}\n }\n </p>\n </div>\n }\n </div>\n }\n </div>\n @if (totalCount >= 0) {\n <div class=\"container-fluid\">\n <nav class=\"d-flex row\">\n <div class=\"col-12 col-md-2\"></div>\n <ul class=\"pagination justify-content-center col-12 col-md-8\">\n <li class=\"page-item\">\n <a\n class=\"page-link\"\n (click)=\"selectPage(selectedPageIndex - 1)\"\n aria-label=\"Previous\"\n [ngClass]=\"{ disabled: selectedPageIndex - 1 > 0 }\"\n >\n <span aria-hidden=\"true\">&laquo;</span>\n </a>\n </li>\n @for (page of pages; track page; let i = $index) {\n <li class=\"page-item\">\n <a class=\"page-link\" [ngClass]=\"{ active: selectedPageIndex === i }\" (click)=\"selectPage(i)\">{{ i + 1 }}</a>\n </li>\n }\n <li class=\"page-item\">\n <a\n class=\"page-link\"\n (click)=\"selectPage(selectedPageIndex + 1)\"\n aria-label=\"Next\"\n [ngClass]=\"{ disabled: pages[selectedPageIndex + 1] }\"\n >\n <span aria-hidden=\"true\">&raquo;</span>\n </a>\n </li>\n </ul>\n <div class=\"col-12 col-md-2\">\n <base-select [options]=\"pageOptions\" [control]=\"pageItemsLimitControl\"></base-select>\n </div>\n </nav>\n </div>\n }\n <div class=\"context-menu\" #contextMenu>\n <div class=\"context-menu-item\" (click)=\"selectId({ id: contextMenuData.clickedElement?.id })\">Bearbeiten</div>\n <div class=\"context-menu-item\" (click)=\"openNewTab({ id: contextMenuData.clickedElement?.id })\">\n In neuen Tab \u00F6ffnen\n </div>\n @if (duplicate) {\n <div class=\"context-menu-item\" (click)=\"duplicateObject(contextMenuData.clickedElement?.id)\">\n Duplizieren\n </div>\n }\n @if (delete) {\n <div class=\"context-menu-item\" (click)=\"deleteObject(contextMenuData.clickedElement?.id)\">\n L\u00F6schen\n </div>\n }\n </div>\n </div>\n </div>\n }\n\n @if (id || createMode) {\n <div class=\"w-100\">\n <button class=\"d-none d-lg-block btn btn-outline-primary mb-3\" (click)=\"selectId('')\">Zur\u00FCck</button>\n @if (!modelConfig?.customTemplateComponent) {\n <base-model-form\n [modelName]=\"camelModelName\"\n [label]=\"modelConfig?.label\"\n [config]=\"config\"\n [logging]=\"logging\"\n [id]=\"id\"\n (finished)=\"selectId('')\"\n [delete]=\"delete\"\n [duplicate]=\"duplicate\"\n [showFavButton]=\"showFavButton\"\n ></base-model-form>\n }\n @if (modelConfig?.customTemplateComponent) {\n <ng-template #customFormTemplate></ng-template>\n }\n </div>\n }\n\n <div class=\"responsive-action-button d-block d-lg-none\">\n @if (!(id || createMode) && showFavButton) {\n <base-fab-button\n [fabIcon]=\"'bi-plus'\"\n (click)=\"createNewObject()\"\n ></base-fab-button>\n }\n </div>\n", styles: [".cursor-pointer{cursor:pointer}.asc:after,.desc:after{border-style:solid;border-width:.2em .2em 0 0;content:\"\";display:inline-block;height:.45em;left:5px;position:relative;transform:rotate(-45deg);vertical-align:middle;width:.45em}.asc:after{transform:rotate(135deg)}td{vertical-align:middle}.bi,th{cursor:pointer;-webkit-user-select:none;user-select:none}.table>:not(:first-child){border-top:0}table{border-radius:.25rem;border-color:#e2e8f0!important}table thead{text-transform:uppercase;font-size:12px}table thead th{padding:12px 24px}table tbody tr td{font-weight:400!important;font-size:16px;padding:16px 24px}.context-menu{opacity:.01%;pointer-events:none;background-color:#fff;border:1px solid #ccc;box-shadow:1px 1px 10px #0000001a;padding:10px 0;position:fixed;top:0;left:0;width:200px;z-index:999}.context-menu-item{cursor:pointer;padding:8px 15px}.context-menu-item:hover{color:var(--bs-primary)}.table-card{background:var(--bs-white);padding:10px;margin-bottom:10px}.table-card-entry{margin-top:4px}.table-card-entry-first{font-size:18px;font-weight:800;color:var(--bs-primary)}.responsive-action-button{position:fixed;bottom:15px;right:15px}.bi-three-dots-vertical{font-size:18px;line-height:18px}.page-item{cursor:pointer}\n"] }]
2728
- }], ctorParameters: () => [{ type: i1.GraphQLMetaService }, { type: i1.GraphQLService }, { type: i1.CmsService }, { type: i1.ToastService }, { type: i1.ScrollService }, { type: i1.StorageService }], propDecorators: { modelName: [{
2729
- type: Input
2730
- }], objectId: [{
2731
- type: Input
2732
- }], createMode: [{
2733
- type: Input
2734
- }], logging: [{
2735
- type: Input
2736
- }], create: [{
2737
- type: Input
2738
- }], update: [{
2739
- type: Input
2740
- }], delete: [{
2741
- type: Input
2742
- }], import: [{
2743
- type: Input
2744
- }], export: [{
2745
- type: Input
2746
- }], duplicate: [{
2747
- type: Input
2748
- }], modelConfig: [{
2749
- type: Input
2750
- }], config: [{
2751
- type: Input
2752
- }], showFavButton: [{
2753
- type: Input
2754
- }], idSelected: [{
2755
- type: Output
2756
- }], createModeChanged: [{
2757
- type: Output
2758
- }], customFormTemplate: [{
2759
- type: ViewChild,
2760
- args: ['customFormTemplate', { read: ViewContainerRef }]
2761
- }] } });
2762
-
2763
- class ChangePasswordComponent {
2764
- formsService;
2765
- userService;
2766
- authService;
2767
- toastService;
2768
- form;
2769
- loading = false;
2770
- constructor(formsService, userService, authService, toastService) {
2771
- this.formsService = formsService;
2772
- this.userService = userService;
2773
- this.authService = authService;
2774
- this.toastService = toastService;
2775
- }
2776
- ngOnInit() {
2777
- this.createForm();
2778
- }
2779
- /**
2780
- * Create password change form
2781
- */
2782
- createForm() {
2783
- this.form = new UntypedFormGroup({
2784
- current: new FormControl('', [Validators.required]),
2785
- password: new FormControl('', [Validators.required, securePasswordValidator()]),
2786
- passwordConfirm: new FormControl('', [Validators.required, securePasswordValidator()]),
2787
- }, Validation.match('password', 'passwordConfirm'));
2788
- }
2789
- /**
2790
- * Submit form and change password of user
2791
- */
2792
- submit() {
2793
- this.loading = true;
2794
- this.userService
2795
- .login({
2796
- email: this.authService.currentUser.email,
2797
- password: this.form.get('current').value,
2798
- })
2799
- .subscribe({
2800
- next: () => {
2801
- if (this.form.invalid) {
2802
- this.formsService.validateAllFormFields(this.form);
2803
- this.loading = false;
2804
- return;
2805
- }
2806
- this.userService.changePassword(this.authService.currentUser.id, this.form.get('password').value).subscribe({
2807
- next: (response) => {
2808
- if (response) {
2809
- this.form.reset();
2810
- this.toastService.show({
2811
- id: 'password-change-success',
2812
- title: 'Erfolgreich',
2813
- description: 'Passwort wurde erfolgreich geändert.',
2814
- type: ToastType.SUCCESS,
2815
- });
2816
- }
2817
- this.loading = false;
2818
- },
2819
- error: () => {
2820
- this.loading = false;
2821
- this.toastService.show({
2822
- id: 'password-change-error',
2823
- title: 'Fehlgeschlagen',
2824
- description: 'Passwort ändern ist fehlgeschlagen, bitte versuche es später erneut.',
2825
- type: ToastType.ERROR,
2826
- });
2827
- },
2828
- });
2829
- },
2830
- error: () => {
2831
- this.loading = false;
2832
- this.toastService.show({
2833
- id: 'password-change-error',
2834
- title: 'Fehlgeschlagen',
2835
- description: 'Passwort ändern ist fehlgeschlagen. Falsches Passwort.',
2836
- type: ToastType.ERROR,
2837
- errorCode: '401',
2838
- });
2839
- },
2840
- });
2841
- }
2842
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ChangePasswordComponent, deps: [{ token: i1.FormsService }, { token: i1.UserService }, { token: i1.AuthService }, { token: i1.ToastService }], target: i0.ɵɵFactoryTarget.Component });
2843
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: ChangePasswordComponent, isStandalone: true, selector: "base-change-password", ngImport: i0, template: "<h3>Passwort \u00E4ndern</h3>\n\n<form [formGroup]=\"form\">\n <base-input\n [id]=\"'Aktuelles Passwort'\"\n [label]=\"'Passwort'\"\n [required]=\"true\"\n [type]=\"'password'\"\n [control]=\"form.get('current')\"\n [tabIndex]=\"1\"\n [eyeButton]=\"true\"\n ></base-input>\n <base-input\n [id]=\"'password'\"\n [label]=\"'Neues Passwort'\"\n [required]=\"true\"\n [type]=\"'password'\"\n [control]=\"form.get('password')\"\n [tabIndex]=\"2\"\n [eyeButton]=\"true\"\n ></base-input>\n <base-input\n [id]=\"'password-confirm'\"\n [label]=\"'Neues Passwort best\u00E4tigen'\"\n [required]=\"true\"\n [type]=\"'password'\"\n [control]=\"form.get('passwordConfirm')\"\n [tabIndex]=\"3\"\n [eyeButton]=\"true\"\n ></base-input>\n\n <div class=\"d-flex justify-content-end\">\n <div>\n @if (!loading) {\n <button class=\"btn btn-primary\" (click)=\"submit()\">Passwort \u00E4ndern</button>\n }\n @if (loading) {\n <button class=\"btn btn-primary\" type=\"button\" disabled>\n <span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span>\n Loading...\n </button>\n }\n </div>\n </div>\n</form>\n", styles: [""], dependencies: [{ kind: "component", type: InputComponent, selector: "base-input", inputs: ["id", "name", "infoText", "label", "placeholder", "autocomplete", "tabIndex", "control", "type", "required", "eyeButton", "objectPath"], outputs: ["enter"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
2844
- }
2845
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: ChangePasswordComponent, decorators: [{
2846
- type: Component,
2847
- args: [{ selector: 'base-change-password', standalone: true, imports: [
2848
- InputComponent,
2849
- ReactiveFormsModule
2850
- ], template: "<h3>Passwort \u00E4ndern</h3>\n\n<form [formGroup]=\"form\">\n <base-input\n [id]=\"'Aktuelles Passwort'\"\n [label]=\"'Passwort'\"\n [required]=\"true\"\n [type]=\"'password'\"\n [control]=\"form.get('current')\"\n [tabIndex]=\"1\"\n [eyeButton]=\"true\"\n ></base-input>\n <base-input\n [id]=\"'password'\"\n [label]=\"'Neues Passwort'\"\n [required]=\"true\"\n [type]=\"'password'\"\n [control]=\"form.get('password')\"\n [tabIndex]=\"2\"\n [eyeButton]=\"true\"\n ></base-input>\n <base-input\n [id]=\"'password-confirm'\"\n [label]=\"'Neues Passwort best\u00E4tigen'\"\n [required]=\"true\"\n [type]=\"'password'\"\n [control]=\"form.get('passwordConfirm')\"\n [tabIndex]=\"3\"\n [eyeButton]=\"true\"\n ></base-input>\n\n <div class=\"d-flex justify-content-end\">\n <div>\n @if (!loading) {\n <button class=\"btn btn-primary\" (click)=\"submit()\">Passwort \u00E4ndern</button>\n }\n @if (loading) {\n <button class=\"btn btn-primary\" type=\"button\" disabled>\n <span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span>\n Loading...\n </button>\n }\n </div>\n </div>\n</form>\n" }]
2851
- }], ctorParameters: () => [{ type: i1.FormsService }, { type: i1.UserService }, { type: i1.AuthService }, { type: i1.ToastService }] });
2852
-
2853
- class DeleteAccountComponent {
2854
- userService;
2855
- authService;
2856
- toastService;
2857
- router;
2858
- description = 'Mit dem Löschen Deines Kontos werden alle persönlichen Daten sofort gelöscht und sind nicht wiederherstellbar.';
2859
- loading = false;
2860
- constructor(userService, authService, toastService, router) {
2861
- this.userService = userService;
2862
- this.authService = authService;
2863
- this.toastService = toastService;
2864
- this.router = router;
2865
- }
2866
- deleteAccount() {
2867
- this.loading = true;
2868
- if (!confirm('Willst Du wirklich ein Konto unwiderruflich löschen?')) {
2869
- this.loading = false;
2870
- return;
2871
- }
2872
- this.userService.delete(this.authService.currentUser.id).subscribe({
2873
- next: (response) => {
2874
- if (response) {
2875
- this.toastService.show({
2876
- id: 'delete-user-success',
2877
- title: 'Erfolgreich',
2878
- description: 'Dein Konto wurde erfolgreich gelöscht.',
2879
- type: ToastType.SUCCESS,
2880
- });
2881
- }
2882
- this.authService.logout();
2883
- this.router.navigate(['/']);
2884
- this.loading = false;
2885
- },
2886
- error: (error) => {
2887
- this.loading = false;
2888
- this.toastService.show({
2889
- id: 'delete-user-error',
2890
- title: 'Fehlgeschlagen',
2891
- description: 'Konto löschen ist fehlgeschlagen, bitte versuche es später erneut.',
2892
- type: ToastType.ERROR,
2893
- });
2894
- },
2895
- });
2896
- }
2897
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: DeleteAccountComponent, deps: [{ token: i1.UserService }, { token: i1.AuthService }, { token: i1.ToastService }, { token: i2.Router }], target: i0.ɵɵFactoryTarget.Component });
2898
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.2", type: DeleteAccountComponent, isStandalone: true, selector: "base-delete-account", inputs: { description: "description" }, ngImport: i0, template: "<div>\n <h3>Konto l\u00F6schen</h3>\n\n <p>{{ description }}</p>\n\n <div class=\"d-flex justify-content-end mt-3\">\n <div>\n @if (!loading) {\n <button class=\"btn btn-primary\" (click)=\"deleteAccount()\">Konto l\u00F6schen</button>\n }\n @if (loading) {\n <button class=\"btn btn-primary\" type=\"button\" disabled>\n <span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span>\n Loading...\n </button>\n }\n </div>\n </div>\n</div>\n", styles: [""] });
2899
- }
2900
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.2", ngImport: i0, type: DeleteAccountComponent, decorators: [{
2901
- type: Component,
2902
- args: [{ selector: 'base-delete-account', standalone: true, template: "<div>\n <h3>Konto l\u00F6schen</h3>\n\n <p>{{ description }}</p>\n\n <div class=\"d-flex justify-content-end mt-3\">\n <div>\n @if (!loading) {\n <button class=\"btn btn-primary\" (click)=\"deleteAccount()\">Konto l\u00F6schen</button>\n }\n @if (loading) {\n <button class=\"btn btn-primary\" type=\"button\" disabled>\n <span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span>\n Loading...\n </button>\n }\n </div>\n </div>\n</div>\n" }]
2903
- }], ctorParameters: () => [{ type: i1.UserService }, { type: i1.AuthService }, { type: i1.ToastService }, { type: i2.Router }], propDecorators: { description: [{
2904
- type: Input
2905
- }] } });
2906
-
2907
- /**
2908
- * Exports for public API
2909
- */
2910
- // Directives
2911
-
2912
- /**
2913
- * Generated bundle index. Do not edit.
2914
- */
2915
-
2916
- export { BackButtonDirective, BreadcrumbComponent, ChangePasswordComponent, CheckboxComponent, ContextMenuDirective, DeleteAccountComponent, EllipsesPipe, FabButtonComponent, InputComponent, LoadingBarComponent, ModelFormBaseComponent, ModelFormComponent, ModelFormSubComponent, ModelTableComponent, RefenceInputComponent, SelectComponent, SkeletonLoaderComponent, SortDirective, StopPropagationDirective, TagsComponent, TextareaComponent, ToastComponent, UploadImageComponent };
2917
- //# sourceMappingURL=lenne.tech-ng-base-base-components.mjs.map