@cccteam/ccc-lib 0.0.13 → 0.0.15

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 (226) hide show
  1. package/eslint.config.js +32 -0
  2. package/ng-package.json +11 -0
  3. package/package.json +7 -68
  4. package/src/auth-authentication-guard/authentication.guard.ts +40 -0
  5. package/src/auth-authentication-guard/ng-package.json +6 -0
  6. package/src/auth-authorization-guard/authorization.guard.ts +17 -0
  7. package/src/auth-authorization-guard/ng-package.json +6 -0
  8. package/src/auth-forms/ccc-field/ccc-field.component.html +1 -0
  9. package/src/auth-forms/ccc-field/ccc-field.component.scss +0 -0
  10. package/src/auth-forms/ccc-field/ccc-field.component.spec.ts +22 -0
  11. package/src/auth-forms/ccc-field/ccc-field.component.ts +74 -0
  12. package/src/auth-forms/form-helpers.ts +39 -0
  13. package/src/auth-forms/{public-api.d.ts → index.ts} +1 -0
  14. package/src/auth-forms/ng-package.json +6 -0
  15. package/src/auth-has-permission/has-permission.directive.ts +34 -0
  16. package/src/auth-has-permission/ng-package.json +6 -0
  17. package/src/auth-service/auth.service.ts +92 -0
  18. package/src/auth-service/ng-package.json +6 -0
  19. package/src/ccc-camel-case-to-title/camel-case-to-title.pipe.ts +23 -0
  20. package/src/ccc-camel-case-to-title/index.ts +1 -0
  21. package/src/ccc-camel-case-to-title/ng-package.json +6 -0
  22. package/src/ccc-grid/ccc-grid.component.ts +155 -0
  23. package/src/ccc-grid/index.ts +3 -0
  24. package/src/ccc-grid/ng-package.json +6 -0
  25. package/src/ccc-grid/table-button/table-button.component.html +16 -0
  26. package/src/ccc-grid/table-button/table-button.component.scss +5 -0
  27. package/src/ccc-grid/table-button/table-button.component.spec.ts +22 -0
  28. package/src/ccc-grid/table-button/table-button.component.ts +49 -0
  29. package/src/ccc-resource/can-deactivate.guard.ts +41 -0
  30. package/src/ccc-resource/compound-resource/compound-resource.component.html +57 -0
  31. package/src/ccc-resource/compound-resource/compound-resource.component.scss +86 -0
  32. package/src/ccc-resource/compound-resource/compound-resource.component.spec.ts +22 -0
  33. package/src/ccc-resource/compound-resource/compound-resource.component.ts +158 -0
  34. package/src/ccc-resource/concat-fns.ts +162 -0
  35. package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.html +12 -0
  36. package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.scss +0 -0
  37. package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.spec.ts +23 -0
  38. package/src/ccc-resource/empty-readonly-field/empty-readonly-field.component.ts +17 -0
  39. package/src/ccc-resource/form-state.service.ts +24 -0
  40. package/src/ccc-resource/format-fns.ts +49 -0
  41. package/src/ccc-resource/gui-constants.ts +88 -0
  42. package/src/ccc-resource/index.ts +23 -0
  43. package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.html +8 -0
  44. package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.scss +0 -0
  45. package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.spec.ts +22 -0
  46. package/src/ccc-resource/leave-page-confirmation-modal/leave-page-confirmation-modal.component.ts +12 -0
  47. package/src/ccc-resource/ng-package.json +6 -0
  48. package/src/ccc-resource/operation-types.ts +19 -0
  49. package/src/ccc-resource/padding-element/padding-element.component.html +1 -0
  50. package/src/ccc-resource/padding-element/padding-element.component.scss +3 -0
  51. package/src/ccc-resource/padding-element/padding-element.component.spec.ts +22 -0
  52. package/src/ccc-resource/padding-element/padding-element.component.ts +20 -0
  53. package/src/ccc-resource/resource-array-view/resource-array-view.component.html +81 -0
  54. package/src/ccc-resource/resource-array-view/resource-array-view.component.scss +21 -0
  55. package/src/ccc-resource/resource-array-view/resource-array-view.component.spec.ts +22 -0
  56. package/src/ccc-resource/resource-array-view/resource-array-view.component.ts +143 -0
  57. package/src/ccc-resource/resource-base/resource-base.component.spec.ts +22 -0
  58. package/src/ccc-resource/resource-base/resource-base.component.ts +11 -0
  59. package/src/ccc-resource/resource-cache.service.ts +232 -0
  60. package/src/ccc-resource/resource-create/resource-create.component.html +31 -0
  61. package/src/ccc-resource/resource-create/resource-create.component.scss +130 -0
  62. package/src/ccc-resource/resource-create/resource-create.component.spec.ts +22 -0
  63. package/src/ccc-resource/resource-create/resource-create.component.ts +303 -0
  64. package/src/ccc-resource/resource-field/base-field.directive.ts +102 -0
  65. package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.html +16 -0
  66. package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.scss +0 -0
  67. package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.spec.ts +22 -0
  68. package/src/ccc-resource/resource-field/fields/boolean-field/boolean-field.component.ts +15 -0
  69. package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.html +13 -0
  70. package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.scss +0 -0
  71. package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.spec.ts +23 -0
  72. package/src/ccc-resource/resource-field/fields/computed-field/computed-field.component.ts +50 -0
  73. package/src/ccc-resource/resource-field/fields/date-field/date-field.component.html +22 -0
  74. package/src/ccc-resource/resource-field/fields/date-field/date-field.component.scss +0 -0
  75. package/src/ccc-resource/resource-field/fields/date-field/date-field.component.spec.ts +22 -0
  76. package/src/ccc-resource/resource-field/fields/date-field/date-field.component.ts +14 -0
  77. package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.html +71 -0
  78. package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.scss +9 -0
  79. package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.spec.ts +22 -0
  80. package/src/ccc-resource/resource-field/fields/enumerated-field/enumerated-field.component.ts +207 -0
  81. package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.html +38 -0
  82. package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.scss +3 -0
  83. package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.spec.ts +22 -0
  84. package/src/ccc-resource/resource-field/fields/nullboolean-field/nullboolean-field.component.ts +87 -0
  85. package/src/ccc-resource/resource-field/fields/number-field/number-field.component.html +23 -0
  86. package/src/ccc-resource/resource-field/fields/number-field/number-field.component.scss +6 -0
  87. package/src/ccc-resource/resource-field/fields/number-field/number-field.component.spec.ts +22 -0
  88. package/src/ccc-resource/resource-field/fields/number-field/number-field.component.ts +14 -0
  89. package/src/ccc-resource/resource-field/fields/text-field/text-field.component.html +29 -0
  90. package/src/ccc-resource/resource-field/fields/text-field/text-field.component.scss +6 -0
  91. package/src/ccc-resource/resource-field/fields/text-field/text-field.component.spec.ts +22 -0
  92. package/src/ccc-resource/resource-field/fields/text-field/text-field.component.ts +23 -0
  93. package/src/ccc-resource/resource-field/resource-field.component.html +112 -0
  94. package/src/ccc-resource/resource-field/resource-field.component.scss +7 -0
  95. package/src/ccc-resource/resource-field/resource-field.component.spec.ts +22 -0
  96. package/src/ccc-resource/resource-field/resource-field.component.ts +214 -0
  97. package/src/ccc-resource/resource-layout/resource-layout.component.html +73 -0
  98. package/src/ccc-resource/resource-layout/resource-layout.component.scss +26 -0
  99. package/src/ccc-resource/resource-layout/resource-layout.component.spec.ts +22 -0
  100. package/src/ccc-resource/resource-layout/resource-layout.component.ts +176 -0
  101. package/src/ccc-resource/resource-list/ resource-list.component.spec.ts +22 -0
  102. package/src/ccc-resource/resource-list/resource-list.component.html +27 -0
  103. package/src/ccc-resource/resource-list/resource-list.component.scss +67 -0
  104. package/src/ccc-resource/resource-list/resource-list.component.ts +376 -0
  105. package/src/ccc-resource/resource-list-create/resource-list-create.component.html +71 -0
  106. package/src/ccc-resource/resource-list-create/resource-list-create.component.scss +9 -0
  107. package/src/ccc-resource/resource-list-create/resource-list-create.component.spec.ts +22 -0
  108. package/src/ccc-resource/resource-list-create/resource-list-create.component.ts +103 -0
  109. package/src/ccc-resource/resource-resolver/resource-resolver.component.html +1 -0
  110. package/src/ccc-resource/resource-resolver/resource-resolver.component.scss +0 -0
  111. package/src/ccc-resource/resource-resolver/resource-resolver.component.spec.ts +22 -0
  112. package/src/ccc-resource/resource-resolver/resource-resolver.component.ts +69 -0
  113. package/src/ccc-resource/resource-store.service.ts +93 -0
  114. package/src/ccc-resource/resource-view/resource-view.component.html +133 -0
  115. package/src/ccc-resource/resource-view/resource-view.component.scss +150 -0
  116. package/src/ccc-resource/resource-view/resource-view.component.spec.ts +22 -0
  117. package/src/ccc-resource/resource-view/resource-view.component.ts +354 -0
  118. package/src/ccc-resource/resources-helpers.ts +262 -0
  119. package/src/ccc-resource/utils/validator-utils.ts +6 -0
  120. package/{fesm2022/cccteam-ccc-lib.mjs → src/index.ts} +32 -11
  121. package/src/internal-types/index.ts +1 -0
  122. package/src/internal-types/ng-package.json +6 -0
  123. package/src/types/auth.actions.ts +46 -0
  124. package/src/types/configs.ts +952 -0
  125. package/src/types/constants.ts +1 -0
  126. package/src/types/core.actions.ts +33 -0
  127. package/src/types/{public-api.d.ts → index.ts} +3 -0
  128. package/src/types/ng-package.json +6 -0
  129. package/src/types/notification-message.ts +20 -0
  130. package/src/types/{permissions.d.ts → permissions.ts} +9 -9
  131. package/src/types/{session-info.d.ts → session-info.ts} +4 -3
  132. package/src/types/tokens.ts +20 -0
  133. package/src/ui-alert/alert.component.html +13 -0
  134. package/src/ui-alert/alert.component.scss +48 -0
  135. package/src/ui-alert/alert.component.spec.ts +22 -0
  136. package/src/ui-alert/alert.component.ts +35 -0
  137. package/src/ui-alert/ng-package.json +6 -0
  138. package/src/ui-core-service/index.ts +1 -0
  139. package/src/ui-core-service/ng-package.json +6 -0
  140. package/src/ui-core-service/ui-core.service.ts +34 -0
  141. package/src/ui-interceptor/api.interceptor.spec.ts +16 -0
  142. package/src/ui-interceptor/api.interceptor.ts +45 -0
  143. package/src/ui-interceptor/ng-package.json +6 -0
  144. package/src/ui-notification-service/ng-package.json +6 -0
  145. package/src/ui-notification-service/notification.service.ts +59 -0
  146. package/src/ui-sidenav/ng-package.json +6 -0
  147. package/src/ui-sidenav/sidenav.component.html +60 -0
  148. package/src/ui-sidenav/sidenav.component.scss +99 -0
  149. package/src/ui-sidenav/sidenav.component.spec.ts +22 -0
  150. package/src/ui-sidenav/sidenav.component.ts +64 -0
  151. package/src/util-request-options/ng-package.json +6 -0
  152. package/src/util-request-options/request-options.ts +17 -0
  153. package/tsconfig.lib.json +13 -0
  154. package/tsconfig.lib.prod.json +11 -0
  155. package/tsconfig.spec.json +15 -0
  156. package/cccteam-ccc-lib-0.0.13.tgz +0 -0
  157. package/fesm2022/cccteam-ccc-lib-src-auth-authentication-guard.mjs +0 -44
  158. package/fesm2022/cccteam-ccc-lib-src-auth-authentication-guard.mjs.map +0 -1
  159. package/fesm2022/cccteam-ccc-lib-src-auth-authorization-guard.mjs +0 -24
  160. package/fesm2022/cccteam-ccc-lib-src-auth-authorization-guard.mjs.map +0 -1
  161. package/fesm2022/cccteam-ccc-lib-src-auth-forms.mjs +0 -118
  162. package/fesm2022/cccteam-ccc-lib-src-auth-forms.mjs.map +0 -1
  163. package/fesm2022/cccteam-ccc-lib-src-auth-has-permission.mjs +0 -51
  164. package/fesm2022/cccteam-ccc-lib-src-auth-has-permission.mjs.map +0 -1
  165. package/fesm2022/cccteam-ccc-lib-src-auth-service.mjs +0 -56
  166. package/fesm2022/cccteam-ccc-lib-src-auth-service.mjs.map +0 -1
  167. package/fesm2022/cccteam-ccc-lib-src-auth-state.mjs +0 -109
  168. package/fesm2022/cccteam-ccc-lib-src-auth-state.mjs.map +0 -1
  169. package/fesm2022/cccteam-ccc-lib-src-types.mjs +0 -137
  170. package/fesm2022/cccteam-ccc-lib-src-types.mjs.map +0 -1
  171. package/fesm2022/cccteam-ccc-lib-src-ui-alert.mjs +0 -48
  172. package/fesm2022/cccteam-ccc-lib-src-ui-alert.mjs.map +0 -1
  173. package/fesm2022/cccteam-ccc-lib-src-ui-core-state.mjs +0 -100
  174. package/fesm2022/cccteam-ccc-lib-src-ui-core-state.mjs.map +0 -1
  175. package/fesm2022/cccteam-ccc-lib-src-ui-interceptor.mjs +0 -48
  176. package/fesm2022/cccteam-ccc-lib-src-ui-interceptor.mjs.map +0 -1
  177. package/fesm2022/cccteam-ccc-lib-src-ui-notification-service.mjs +0 -57
  178. package/fesm2022/cccteam-ccc-lib-src-ui-notification-service.mjs.map +0 -1
  179. package/fesm2022/cccteam-ccc-lib-src-ui-sidenav.mjs +0 -70
  180. package/fesm2022/cccteam-ccc-lib-src-ui-sidenav.mjs.map +0 -1
  181. package/fesm2022/cccteam-ccc-lib-src-util-request-options.mjs +0 -19
  182. package/fesm2022/cccteam-ccc-lib-src-util-request-options.mjs.map +0 -1
  183. package/fesm2022/cccteam-ccc-lib.mjs.map +0 -1
  184. package/index.d.ts +0 -5
  185. package/public-api.d.ts +0 -13
  186. package/src/auth-authentication-guard/authentication.guard.d.ts +0 -3
  187. package/src/auth-authentication-guard/index.d.ts +0 -5
  188. package/src/auth-authorization-guard/authorization.guard.d.ts +0 -3
  189. package/src/auth-authorization-guard/index.d.ts +0 -5
  190. package/src/auth-forms/ccc-field/ccc-field.component.d.ts +0 -25
  191. package/src/auth-forms/form-helpers.d.ts +0 -16
  192. package/src/auth-forms/index.d.ts +0 -5
  193. package/src/auth-has-permission/has-permission.directive.d.ts +0 -12
  194. package/src/auth-has-permission/index.d.ts +0 -5
  195. package/src/auth-service/auth.service.d.ts +0 -24
  196. package/src/auth-service/index.d.ts +0 -5
  197. package/src/auth-state/auth.state.d.ts +0 -27
  198. package/src/auth-state/index.d.ts +0 -5
  199. package/src/auth-state/public-api.d.ts +0 -1
  200. package/src/types/auth.actions.d.ts +0 -41
  201. package/src/types/core.actions.d.ts +0 -31
  202. package/src/types/index.d.ts +0 -5
  203. package/src/types/notification-message.d.ts +0 -18
  204. package/src/types/tokens.d.ts +0 -13
  205. package/src/ui-alert/alert.component.d.ts +0 -13
  206. package/src/ui-alert/index.d.ts +0 -5
  207. package/src/ui-core-state/core.state.d.ts +0 -28
  208. package/src/ui-core-state/index.d.ts +0 -5
  209. package/src/ui-core-state/public-api.d.ts +0 -1
  210. package/src/ui-interceptor/api.interceptor.d.ts +0 -12
  211. package/src/ui-interceptor/index.d.ts +0 -5
  212. package/src/ui-notification-service/index.d.ts +0 -5
  213. package/src/ui-notification-service/notification.service.d.ts +0 -30
  214. package/src/ui-sidenav/index.d.ts +0 -5
  215. package/src/ui-sidenav/sidenav.component.d.ts +0 -31
  216. package/src/util-request-options/index.d.ts +0 -5
  217. package/src/util-request-options/request-options.d.ts +0 -8
  218. /package/src/auth-authentication-guard/{public-api.d.ts → index.ts} +0 -0
  219. /package/src/auth-authorization-guard/{public-api.d.ts → index.ts} +0 -0
  220. /package/src/auth-has-permission/{public-api.d.ts → index.ts} +0 -0
  221. /package/src/auth-service/{public-api.d.ts → index.ts} +0 -0
  222. /package/src/ui-alert/{public-api.d.ts → index.ts} +0 -0
  223. /package/src/ui-interceptor/{public-api.d.ts → index.ts} +0 -0
  224. /package/src/ui-notification-service/{public-api.d.ts → index.ts} +0 -0
  225. /package/src/ui-sidenav/{public-api.d.ts → index.ts} +0 -0
  226. /package/src/util-request-options/{public-api.d.ts → index.ts} +0 -0
@@ -0,0 +1,143 @@
1
+ import { NgComponentOutlet } from '@angular/common';
2
+ import {
3
+ Component,
4
+ ComponentRef,
5
+ computed,
6
+ effect,
7
+ inject,
8
+ Injector,
9
+ input,
10
+ OnInit,
11
+ output,
12
+ signal,
13
+ viewChild
14
+ } from '@angular/core';
15
+ import { MatButtonModule } from '@angular/material/button';
16
+ import { MatExpansionModule, MatExpansionPanel } from '@angular/material/expansion';
17
+ import { MatIconModule } from '@angular/material/icon';
18
+ import { MatTooltipModule } from '@angular/material/tooltip';
19
+ import { ArrayConfig, ColumnConfig, RecordData, RESOURCE_META, ViewConfig } from '@cccteam/ccc-lib/src/types';
20
+ import { ResourceCacheService } from '../resource-cache.service';
21
+ import { ResourceCreateComponent } from '../resource-create/resource-create.component';
22
+ import { ResourceStore } from '../resource-store.service';
23
+
24
+ @Component({
25
+ selector: 'ccc-resource-array-view',
26
+ templateUrl: './resource-array-view.component.html',
27
+ styleUrl: './resource-array-view.component.scss',
28
+ imports: [
29
+ MatButtonModule,
30
+ MatExpansionModule,
31
+ MatIconModule,
32
+ MatTooltipModule,
33
+ ResourceCreateComponent,
34
+ NgComponentOutlet,
35
+ ],
36
+ providers: [ResourceStore],
37
+ })
38
+ export class ResourceArrayViewComponent implements OnInit {
39
+ store = inject(ResourceStore);
40
+ cache = inject(ResourceCacheService);
41
+ injector = inject(Injector);
42
+ resourceMeta = inject(RESOURCE_META);
43
+
44
+ resourceConfig = input.required<ArrayConfig>();
45
+ parentData = input<RecordData>({});
46
+ expPanel = viewChild<MatExpansionPanel, MatExpansionPanel>('expPanel', { read: MatExpansionPanel });
47
+
48
+ emptyOneToOne = output<boolean>();
49
+
50
+ createMode = signal(false);
51
+ compoundResourceComponent = input.required<ComponentRef<any>>();
52
+
53
+ showCreateButton = computed(() => {
54
+ const list = this.store.listData();
55
+ const resourceConfig = this.resourceConfig();
56
+ const iteratedConfig = resourceConfig.iteratedConfig;
57
+ if (list && resourceConfig?.viewType === 'OneToOne') {
58
+ this.emptyOneToOne.emit(list.length === 0);
59
+ return list.length === 0;
60
+ }
61
+ if ('createTitle' in iteratedConfig && iteratedConfig.createTitle !== '') {
62
+ return true;
63
+ }
64
+ return false;
65
+ });
66
+
67
+ createConfig = computed(() => {
68
+ const config = this.resourceConfig();
69
+ if (config.createConfig && Object.keys(config.createConfig).length !== 0) {
70
+ return config.createConfig as ViewConfig;
71
+ }
72
+ if (config.iteratedConfig && Object.keys(config.iteratedConfig).length !== 0) {
73
+ return config.iteratedConfig as ViewConfig;
74
+ }
75
+ return config;
76
+ });
77
+
78
+ resourceListRoute = computed(() => {
79
+ const config = this.resourceConfig();
80
+ const meta = this.resourceMeta(config.connectorResource || config.primaryResource);
81
+ return meta.route;
82
+ });
83
+
84
+ setCreateMode(value: boolean): void {
85
+ this.createMode.set(value);
86
+ if (value && this.expPanel && this.expPanel()?.closed) {
87
+ this.expPanel()?.open();
88
+ }
89
+ }
90
+
91
+ ngOnInit(): void {
92
+ this.store.resourceName.set(this.resourceConfig().primaryResource);
93
+ const meta = this.resourceMeta(this.resourceConfig().primaryResource);
94
+ this.store.resourceMeta.set(meta);
95
+ this.store.overrideRoute.set(this.resourceListRoute());
96
+ const columnArray = [];
97
+ if (this.resourceConfig().connectorField !== '') {
98
+ columnArray.push({ id: this.resourceConfig().connectorField });
99
+ } else {
100
+ columnArray.push({ id: 'id' } as ColumnConfig);
101
+ }
102
+ this.store.listColumns.set(columnArray);
103
+ this.store.sorts.set(this.resourceConfig().sorts);
104
+ }
105
+
106
+ constructor() {
107
+ effect(() => {
108
+ const parentData = this.parentData();
109
+ const resourceConfig = this.resourceConfig();
110
+
111
+ if (resourceConfig && 'listFilter' in resourceConfig && parentData) {
112
+ const filter = resourceConfig.listFilter(parentData);
113
+
114
+ this.store.filter.set(filter);
115
+ this.store.resetResourceList();
116
+ }
117
+ });
118
+ }
119
+
120
+ createResource(event: MouseEvent): void {
121
+ event.stopPropagation();
122
+ const expPanel = this.expPanel();
123
+
124
+ if (!expPanel) {
125
+ this.setCreateMode(true);
126
+ return;
127
+ }
128
+ this.setCreateMode(true);
129
+ if (expPanel.closed) {
130
+ expPanel.open();
131
+ }
132
+ }
133
+
134
+ onResourceDeleted(): void {
135
+ this.setCreateMode(false);
136
+ }
137
+
138
+ onCreateCompleted(): void {
139
+ this.cache.updateResourceInCache(this.store.resourceName(), 'list');
140
+
141
+ this.setCreateMode(false);
142
+ }
143
+ }
@@ -0,0 +1,22 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { ResourceBaseComponent } from './resource-base.component';
4
+
5
+ xdescribe('ResourceBaseComponent', () => {
6
+ let component: ResourceBaseComponent;
7
+ let fixture: ComponentFixture<ResourceBaseComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [ResourceBaseComponent],
12
+ }).compileComponents();
13
+
14
+ fixture = TestBed.createComponent(ResourceBaseComponent);
15
+ component = fixture.componentInstance;
16
+ fixture.detectChanges();
17
+ });
18
+
19
+ it('should create', () => {
20
+ expect(component).toBeTruthy();
21
+ });
22
+ });
@@ -0,0 +1,11 @@
1
+ import { Component } from '@angular/core';
2
+ import { RouterModule } from '@angular/router';
3
+ import { ResourceCacheService } from '../resource-cache.service';
4
+
5
+ @Component({
6
+ selector: 'ccc-resource-base',
7
+ imports: [RouterModule],
8
+ providers: [ResourceCacheService],
9
+ template: '<router-outlet></router-outlet>',
10
+ })
11
+ export class ResourceBaseComponent {}
@@ -0,0 +1,232 @@
1
+ import { HttpClient, HttpParams } from '@angular/common/http';
2
+ import { inject, Injectable, Injector, ResourceRef, signal, Signal, untracked } from '@angular/core';
3
+ import { rxResource } from '@angular/core/rxjs-interop';
4
+ import { Router } from '@angular/router';
5
+ import type { FieldSort, RecordData, Resource } from '@cccteam/ccc-lib/src/internal-types';
6
+ import { AlertLevel, API_URL } from '@cccteam/ccc-lib/src/internal-types';
7
+ import { CreateNotificationMessage } from '@cccteam/ccc-lib/src/types';
8
+ import { NotificationService } from '@cccteam/ccc-lib/src/ui-notification-service';
9
+ import { catchError, Observable, of, tap } from 'rxjs';
10
+ import { Operation } from './operation-types';
11
+
12
+ @Injectable()
13
+ export class ResourceCacheService {
14
+ cache = new Map<string, ResourceRef<RecordData[] | RecordData>>();
15
+ http = inject(HttpClient);
16
+ notifications = inject(NotificationService);
17
+ injector = inject(Injector);
18
+ router = inject(Router);
19
+ apiUrl = inject(API_URL);
20
+
21
+ registerView(route: Signal<string>, resource: Signal<Resource>, uuid: Signal<string>): ResourceRef<RecordData> {
22
+ const cacheKey = this.createCacheKey(resource(), 'view', uuid(), []);
23
+ if (this.cache.has(cacheKey)) {
24
+ return this.cache.get(cacheKey) as ResourceRef<RecordData>;
25
+ }
26
+
27
+ return untracked(() => {
28
+ const resourceRef = rxResource({
29
+ injector: this.injector,
30
+ params: () => ({
31
+ route: route(),
32
+ uuid: uuid(),
33
+ }),
34
+ stream: ({ params }) => {
35
+ if (!params.route || !params.uuid || uuid() === 'undefined') return of({} as RecordData);
36
+ return this.http.get<RecordData>(this.routes.resource(this.apiUrl, String(params.route), uuid() || ''));
37
+ },
38
+ }) as ResourceRef<RecordData>;
39
+
40
+ this.cache.set(cacheKey, resourceRef);
41
+ return resourceRef;
42
+ });
43
+ }
44
+
45
+ registerList(
46
+ route: Signal<string>,
47
+ resource: Signal<Resource>,
48
+ filter: Signal<string> = signal(''),
49
+ columns: Signal<string[]> = signal([]),
50
+ searchTokens: Signal<string> = signal(''),
51
+ sorts: Signal<FieldSort[]> = signal([]),
52
+ defaultEmpty = false,
53
+ ): ResourceRef<RecordData[]> {
54
+ const cacheKey = this.createCacheKey(resource(), 'list', filter(), columns());
55
+ if (this.cache.has(cacheKey)) {
56
+ const resourceRef = this.cache.get(cacheKey) as ResourceRef<RecordData[]>;
57
+ return resourceRef as ResourceRef<RecordData[]>;
58
+ }
59
+
60
+ return untracked(() => {
61
+ const resourceRef = rxResource({
62
+ defaultValue: [] as RecordData[],
63
+ injector: this.injector,
64
+ params: () => ({
65
+ route: route(),
66
+ filter: filter(),
67
+ columns: columns(),
68
+ searchTokens: searchTokens(),
69
+ sorts: sorts(),
70
+ }),
71
+ stream: ({ params }) => {
72
+ if (!params.route) return of([] as RecordData[]);
73
+ if (defaultEmpty && (!params.searchTokens || params.searchTokens.trim() === '')) {
74
+ return of([] as RecordData[]);
75
+ }
76
+ return this.list<RecordData>(
77
+ String(params.route),
78
+ params.filter,
79
+ params.columns,
80
+ params.searchTokens,
81
+ params.sorts,
82
+ );
83
+ },
84
+ }) as ResourceRef<RecordData[]>;
85
+ this.cache.set(cacheKey, resourceRef);
86
+ return resourceRef;
87
+ });
88
+ }
89
+
90
+ createCacheKey(resource: Resource, type: 'view' | 'list', params: string, columns: string[] = []): string {
91
+ const normalizedColumns = columns.map((col) => col || '').sort((a, b) => a.localeCompare(b));
92
+ return `${resource}:${type}:${JSON.stringify(params)}:${JSON.stringify(normalizedColumns)}`;
93
+ }
94
+
95
+ /**
96
+ *
97
+ * @param resource
98
+ * @returns void
99
+ * @description This method tries to update the resource by exact name in the cache by calling the reload method on it.
100
+ */
101
+ updateResourceInCache(resource: Resource, type: 'view' | 'list'): void {
102
+ this.cache.forEach((resourceRef, key) => {
103
+ const [cachedResource, cachedType] = key.split(':');
104
+ if (cachedResource === resource && cachedType === type) {
105
+ resourceRef.reload();
106
+ }
107
+ });
108
+ }
109
+
110
+ /**
111
+ *
112
+ * @param resource
113
+ * @returns void
114
+ * @description This method tries to update any resource that includes the resource name in the cache by calling the reload method on it.
115
+ * For example, updating ResourceTest will update both ResourceTest and ResourceTestRelatedResourceFoobar.
116
+ */
117
+ updateResourceGroupInCache(resource: Resource): void {
118
+ this.cache.forEach((resourceRef, key) => {
119
+ const [cachedResource] = key.split(':');
120
+ if (cachedResource?.includes(resource)) {
121
+ resourceRef.reload();
122
+ }
123
+ });
124
+ }
125
+
126
+ routes = {
127
+ resources: (rootUrl: string, resources: string): string => `${rootUrl}/${resources}`,
128
+ resource: (rootUrl: string, resources: string, uuid: string): string => `${rootUrl}/${resources}/${uuid}`,
129
+ method: (rootUrl: string, method: string): string => `${rootUrl}/${method}`,
130
+ };
131
+
132
+ makePatches(operations: Operation[], route: string, resource: Resource): Observable<Record<string, unknown>> {
133
+ return this.patchMultiple(String(route), operations).pipe(
134
+ tap(() => {
135
+ this.notifications.addGlobalNotification({
136
+ message: `${resource} updated successfully`,
137
+ level: AlertLevel.SUCCESS,
138
+ duration: 5000,
139
+ link: '',
140
+ } satisfies CreateNotificationMessage);
141
+ }),
142
+ catchError((error) => {
143
+ this.notifications.addGlobalNotification({
144
+ message: `${resource} failed to update: ${error}`,
145
+ level: AlertLevel.ERROR,
146
+ duration: 5000,
147
+ link: '',
148
+ } satisfies CreateNotificationMessage);
149
+ return of({});
150
+ }),
151
+ );
152
+ }
153
+
154
+ createPatch(operation: Operation, route: string, resource: Resource): Observable<Record<string, unknown>> {
155
+ return this.patchMultiple(String(route), [operation]).pipe(
156
+ tap(() => {
157
+ this.notifications.addGlobalNotification({
158
+ message: `${resource} created successfully`,
159
+ level: AlertLevel.SUCCESS,
160
+ duration: 5000,
161
+ link: '',
162
+ } satisfies CreateNotificationMessage);
163
+ }),
164
+ catchError((error) => {
165
+ this.notifications.addGlobalNotification({
166
+ message: `${resource} failed to create: ${error}`,
167
+ level: AlertLevel.ERROR,
168
+ duration: 5000,
169
+ link: '',
170
+ } satisfies CreateNotificationMessage);
171
+ return of({});
172
+ }),
173
+ );
174
+ }
175
+
176
+ private patchMultiple(resourceRoute: string, data: Operation[]): Observable<Record<string, unknown>> {
177
+ return this.http.patch<Record<string, unknown>>(this.routes.resources(this.apiUrl, resourceRoute), data);
178
+ }
179
+
180
+ private list<T>(
181
+ resourceRoute: string,
182
+ filter?: string,
183
+ columns?: string[],
184
+ searchTokens?: string,
185
+ sort?: FieldSort[],
186
+ ): Observable<T[]> {
187
+ const paramsObj: Record<string, string> = {};
188
+ if (filter && filter.trim() !== '') paramsObj['filter'] = filter;
189
+ if (columns && columns.length > 0) paramsObj['columns'] = columns.join(',');
190
+ if (searchTokens && searchTokens.trim() !== '') paramsObj['SearchTokens'] = searchTokens;
191
+ if (sort && sort.length > 0) {
192
+ paramsObj['sort'] = sort.map((s) => `${s.field}:${s.direction}`).join(',');
193
+ }
194
+ const params = new HttpParams({ fromObject: paramsObj });
195
+ return this.http.get<T[]>(this.routes.resources(this.apiUrl, resourceRoute), { params });
196
+ }
197
+
198
+ // rpcCall<T>(rpcConfig: RPCConfig, body: T): Observable<T> {
199
+ // const methodData = methodMeta(rpcConfig.method);
200
+ // if (!methodData) {
201
+ // console.error('Method not found in methodMap:', rpcConfig.method);
202
+ // return of({} as T);
203
+ // }
204
+
205
+ // return this.http.post<T>(this.routes.method(this.apiUrl, methodData.route), body).pipe(
206
+ // tap(() => {
207
+ // this.notifications.addGlobalNotification({
208
+ // message: rpcConfig.successMessage ? rpcConfig.successMessage : `${rpcConfig.method} called successfully`,
209
+ // level: AlertLevel.SUCCESS,
210
+ // duration: 5000,
211
+ // link: '',
212
+ // } satisfies CreateNotificationMessage);
213
+ // if (rpcConfig.afterMethodRedirect) {
214
+ // if (typeof rpcConfig.afterMethodRedirect === 'string') {
215
+ // this.router.navigate([rpcConfig.afterMethodRedirect]);
216
+ // } else {
217
+ // this.router.navigate(rpcConfig.afterMethodRedirect);
218
+ // }
219
+ // }
220
+ // }),
221
+ // catchError((error) => {
222
+ // this.notifications.addGlobalNotification({
223
+ // message: error,
224
+ // level: AlertLevel.ERROR,
225
+ // duration: 5000,
226
+ // link: '',
227
+ // } satisfies CreateNotificationMessage);
228
+ // return of();
229
+ // }),
230
+ // );
231
+ // }
232
+ }
@@ -0,0 +1,31 @@
1
+ <div class="page-container">
2
+ <div class="header-container">
3
+ <div class="title" [class.indent]="indentTitle()">
4
+ <h1>{{ config().title }}</h1>
5
+ </div>
6
+
7
+ <div class="state-buttons">
8
+ @if (!form().valid && submitted()) {
9
+ <div class="invalid message">Please complete or fix required fields.</div>
10
+ }
11
+ <button mat-raised-button color="primary" (click)="cancelForm()">Cancel</button>
12
+ <button mat-raised-button color="accent" (click)="saveForm()" [disabled]="form().pristine">Save</button>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="resource-container">
17
+ <form [formGroup]="form()">
18
+ <div class="resource row">
19
+ @for (element of config().elements; track element) {
20
+ <ccc-resource-layout-template
21
+ [element]="element"
22
+ [meta]="store.resourceMeta()"
23
+ [fieldClass]="config().fieldClass"
24
+ [relatedData]="parentData()"
25
+ editMode="edit"
26
+ [form]="form()"></ccc-resource-layout-template>
27
+ }
28
+ </div>
29
+ </form>
30
+ </div>
31
+ </div>
@@ -0,0 +1,130 @@
1
+ .container {
2
+ border: 1px solid #8e8e8e;
3
+ border-radius: 5px;
4
+ position: relative;
5
+ margin-bottom: 20px;
6
+ padding-top: 10px;
7
+ }
8
+
9
+ .label {
10
+ position: absolute;
11
+ top: -15px;
12
+ left: 20px;
13
+ height: 20px;
14
+ background-color: #ffffff;
15
+ margin: 0px 10px 0px 10px;
16
+ padding: 0px 10px 0px 10px;
17
+ text-align: center;
18
+ z-index: 10;
19
+ font-weight: 500;
20
+ font:
21
+ 18px 'Roboto',
22
+ sans-serif;
23
+ }
24
+
25
+ .page-container {
26
+ position: relative;
27
+ }
28
+
29
+ .header-container {
30
+ margin: 20px 20px 0px 20px;
31
+ display: flex;
32
+ flex-direction: row;
33
+ top: 0;
34
+ z-index: 11;
35
+ background: transparent;
36
+ }
37
+
38
+ .title {
39
+ display: flex;
40
+ align-items: center;
41
+ flex-shrink: 0;
42
+ }
43
+
44
+ .indent {
45
+ padding-left: 20px;
46
+ }
47
+
48
+ .message {
49
+ margin-top: auto;
50
+ margin-bottom: auto;
51
+ }
52
+
53
+ .state-buttons {
54
+ display: flex;
55
+ flex-direction: row;
56
+ margin-top: auto;
57
+ margin-bottom: auto;
58
+ justify-content: right;
59
+ flex-grow: 1;
60
+ gap: 20px;
61
+ }
62
+
63
+ .mode-text {
64
+ margin-top: auto;
65
+ margin-bottom: auto;
66
+ }
67
+
68
+ .resource {
69
+ margin: 10px;
70
+ }
71
+ .section h2 {
72
+ font-size: 1.5em;
73
+ border-bottom: 2px solid #ccc;
74
+ padding-bottom: 8px;
75
+
76
+ margin-bottom: 100px;
77
+ }
78
+ .section {
79
+ margin-top: 15px;
80
+ }
81
+
82
+ .divider {
83
+ margin: 16px 0;
84
+ }
85
+
86
+ .section-elements {
87
+ display: flex;
88
+ flex-wrap: wrap;
89
+ }
90
+
91
+ .mat-input-element:disabled[readonly] {
92
+ color: currentColor;
93
+ }
94
+
95
+ .unsaved {
96
+ color: #f44336;
97
+ }
98
+
99
+ .edit-button {
100
+ margin-top: auto;
101
+ margin-bottom: auto;
102
+ padding-left: 10px;
103
+ }
104
+
105
+ .button-text {
106
+ display: flex;
107
+ flex-direction: row;
108
+ align-items: center;
109
+
110
+ .edit {
111
+ color: #003b49;
112
+ }
113
+
114
+ .mat-icon {
115
+ margin-right: 8px;
116
+ }
117
+ }
118
+
119
+ .shaded {
120
+ background-color: #e9e9e9;
121
+ }
122
+
123
+ .rounded {
124
+ border-radius: 8px;
125
+ }
126
+
127
+ .dotted-outline {
128
+ border: 1px dotted #ccc;
129
+ padding: 8px;
130
+ }
@@ -0,0 +1,22 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { ResourceCreateComponent } from './resource-create.component';
4
+
5
+ xdescribe('ResourceCreateComponent', () => {
6
+ let component: ResourceCreateComponent;
7
+ let fixture: ComponentFixture<ResourceCreateComponent>;
8
+
9
+ beforeEach(async () => {
10
+ await TestBed.configureTestingModule({
11
+ imports: [ResourceCreateComponent],
12
+ }).compileComponents();
13
+
14
+ fixture = TestBed.createComponent(ResourceCreateComponent);
15
+ component = fixture.componentInstance;
16
+ fixture.detectChanges();
17
+ });
18
+
19
+ it('should create', () => {
20
+ expect(component).toBeTruthy();
21
+ });
22
+ });