unictive-angular 1.0.13 → 1.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 (95) hide show
  1. package/README.md +25 -0
  2. package/bin/index.js +11 -7
  3. package/bin/utils/auth.js +0 -3
  4. package/bin/utils/createStructure.js +36 -7
  5. package/bin/utils/{setupAssets.js → refactorAngularJson.js} +63 -3
  6. package/bin/utils/resource/.postcssrc.json +12 -0
  7. package/bin/utils/resource/package.json +48 -0
  8. package/bin/utils/resource/src/app/MODULES/auth/README.md +10 -0
  9. package/bin/utils/resource/src/app/MODULES/dashboard/README.md +10 -0
  10. package/bin/utils/resource/src/app/MODULES/home/README.md +10 -0
  11. package/bin/utils/resource/src/app/app.config.ts +20 -0
  12. package/bin/utils/resource/src/app/app.html +263 -0
  13. package/bin/utils/resource/src/app/app.routes.ts +3 -0
  14. package/bin/utils/resource/src/app/app.scss +6 -0
  15. package/bin/utils/resource/src/app/app.spec.ts +25 -0
  16. package/bin/utils/resource/src/app/app.ts +32 -0
  17. package/bin/utils/resource/src/app/core/README.md +10 -0
  18. package/bin/utils/resource/src/app/core/config/README.md +10 -0
  19. package/bin/utils/resource/src/app/core/guards/README.md +10 -0
  20. package/bin/utils/resource/src/app/core/interceptors/README.md +10 -0
  21. package/bin/utils/resource/src/app/core/interceptors/error.interceptor.ts +47 -0
  22. package/bin/utils/resource/src/app/core/interceptors/jsonrpc.ts +440 -0
  23. package/bin/utils/resource/src/app/core/interceptors/jwt.interceptor.ts +36 -0
  24. package/bin/utils/resource/src/app/core/models/README.md +10 -0
  25. package/bin/utils/resource/src/app/core/services/README.md +10 -0
  26. package/bin/utils/resource/src/app/shared/README.md +10 -0
  27. package/bin/utils/resource/src/app/shared/directives/README.md +10 -0
  28. package/bin/utils/resource/src/app/shared/pipes/README.md +10 -0
  29. package/bin/utils/resource/src/app/shared/ui/README.md +10 -0
  30. package/bin/utils/resource/src/app/shared/utils/README.md +10 -0
  31. package/bin/utils/resource/src/app/shared/utils/logger.ts +40 -0
  32. package/bin/utils/resource/src/app/shared/widgets/test/test.component.css +0 -0
  33. package/bin/utils/resource/src/app/shared/widgets/test/test.component.html +20 -0
  34. package/bin/utils/resource/src/app/shared/widgets/test/test.component.ts +56 -0
  35. package/bin/utils/resource/src/app/shared/widgets/test/test.module.ts +10 -0
  36. package/bin/utils/resource/src/environments/environment.prod.ts +6 -0
  37. package/bin/utils/resource/src/environments/environment.staging.ts +6 -0
  38. package/bin/utils/resource/src/environments/environment.ts +6 -0
  39. package/bin/utils/resource/src/index.html +16 -0
  40. package/bin/utils/resource/src/main.ts +6 -0
  41. package/bin/utils/resource/src/styles.scss +2 -0
  42. package/bin/utils/resource/src/tailwind.css +11 -0
  43. package/bin/utils/resource/tailwind.config.js +19 -0
  44. package/bin/utils/resource/tsconfig.json +46 -0
  45. package/coba/.editorconfig +17 -0
  46. package/coba/.postcssrc.json +12 -0
  47. package/coba/.vscode/extensions.json +4 -0
  48. package/coba/.vscode/launch.json +20 -0
  49. package/coba/.vscode/tasks.json +42 -0
  50. package/coba/README.md +59 -0
  51. package/coba/angular.json +102 -0
  52. package/coba/package-lock.json +10696 -0
  53. package/coba/package.json +48 -0
  54. package/coba/public/favicon.ico +0 -0
  55. package/coba/src/app/MODULES/auth/README.md +10 -0
  56. package/coba/src/app/MODULES/dashboard/README.md +10 -0
  57. package/coba/src/app/MODULES/home/README.md +10 -0
  58. package/coba/src/app/app.config.ts +20 -0
  59. package/coba/src/app/app.html +263 -0
  60. package/coba/src/app/app.routes.ts +3 -0
  61. package/coba/src/app/app.scss +6 -0
  62. package/coba/src/app/app.spec.ts +25 -0
  63. package/coba/src/app/app.ts +32 -0
  64. package/coba/src/app/core/README.md +10 -0
  65. package/coba/src/app/core/config/README.md +10 -0
  66. package/coba/src/app/core/guards/README.md +10 -0
  67. package/coba/src/app/core/interceptors/README.md +10 -0
  68. package/coba/src/app/core/interceptors/error.interceptor.ts +47 -0
  69. package/coba/src/app/core/interceptors/jsonrpc.ts +440 -0
  70. package/coba/src/app/core/interceptors/jwt.interceptor.ts +36 -0
  71. package/coba/src/app/core/models/README.md +10 -0
  72. package/coba/src/app/core/services/README.md +10 -0
  73. package/coba/src/app/shared/README.md +10 -0
  74. package/coba/src/app/shared/directives/README.md +10 -0
  75. package/coba/src/app/shared/pipes/README.md +10 -0
  76. package/coba/src/app/shared/ui/README.md +10 -0
  77. package/coba/src/app/shared/utils/README.md +10 -0
  78. package/coba/src/app/shared/utils/logger.ts +40 -0
  79. package/coba/src/app/shared/widgets/test/test.component.css +0 -0
  80. package/coba/src/app/shared/widgets/test/test.component.html +20 -0
  81. package/coba/src/app/shared/widgets/test/test.component.ts +56 -0
  82. package/coba/src/app/shared/widgets/test/test.module.ts +10 -0
  83. package/coba/src/environments/environment.prod.ts +6 -0
  84. package/coba/src/environments/environment.staging.ts +6 -0
  85. package/coba/src/environments/environment.ts +6 -0
  86. package/coba/src/index.html +16 -0
  87. package/coba/src/main.ts +6 -0
  88. package/coba/src/styles.scss +2 -0
  89. package/coba/src/tailwind.css +11 -0
  90. package/coba/tailwind.config.js +19 -0
  91. package/coba/tsconfig.app.json +15 -0
  92. package/coba/tsconfig.json +46 -0
  93. package/coba/tsconfig.spec.json +14 -0
  94. package/package.json +1 -1
  95. package/test/node_modules/@tailwindcss/oxide-win32-x64-msvc/tailwindcss-oxide.win32-x64-msvc.node +0 -0
@@ -0,0 +1,25 @@
1
+ import { provideZonelessChangeDetection } from '@angular/core';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { App } from './app';
4
+
5
+ describe('App', () => {
6
+ beforeEach(async () => {
7
+ await TestBed.configureTestingModule({
8
+ imports: [App],
9
+ providers: [provideZonelessChangeDetection()]
10
+ }).compileComponents();
11
+ });
12
+
13
+ it('should create the app', () => {
14
+ const fixture = TestBed.createComponent(App);
15
+ const app = fixture.componentInstance;
16
+ expect(app).toBeTruthy();
17
+ });
18
+
19
+ it('should render title', () => {
20
+ const fixture = TestBed.createComponent(App);
21
+ fixture.detectChanges();
22
+ const compiled = fixture.nativeElement as HTMLElement;
23
+ expect(compiled.querySelector('h1')?.textContent).toContain('Hello, cunicti');
24
+ });
25
+ });
@@ -0,0 +1,32 @@
1
+ import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
2
+ import { RouterOutlet } from '@angular/router';
3
+ import { logger } from '@shared/utils/logger';
4
+ @Component({
5
+ selector: 'app-root',
6
+ imports: [RouterOutlet],
7
+ templateUrl: './app.html',
8
+ styleUrl: './app.scss',
9
+ })
10
+ export class App implements OnInit {
11
+ protected title = 'OLD PROJECT';
12
+
13
+ constructor(private cdr: ChangeDetectorRef) { }
14
+ ngOnInit(): void {
15
+ //MARK:TEST
16
+ setTimeout(() => {
17
+ this.ubah();
18
+ logger.log('Title changed to:', this.title);
19
+ this.cdr.detectChanges();
20
+
21
+ // Harap selalu tambahkan this.cdr.detectChanges(); untuk memuat data dari sisi UI secara dinamis, karna project ini menggunakan zoneless;
22
+ // Terkecuali operasi dari angular context seperti event click, dll...
23
+ }, 3000);
24
+
25
+ // this.cdr.detectChanges();
26
+ // Initialization logic here
27
+ }
28
+
29
+ ubah() {
30
+ this.title = 'CHANGE TO NEW PROJECT';
31
+ }
32
+ }
@@ -0,0 +1,10 @@
1
+ # CORE
2
+
3
+ Folder ini untuk layer **core**.
4
+
5
+ - **domain** → berisi entity, interface, dan aturan bisnis murni
6
+ - **application** → berisi use case, service koordinasi domain
7
+ - **infrastructure** → berisi API call, repository, integrasi eksternal
8
+ - **presentation** → berisi komponen Angular, routing, dan UI
9
+
10
+ > Bagian ini adalah bagian dari arsitektur **Clean Architecture** untuk fitur ini.
@@ -0,0 +1,10 @@
1
+ # CONFIG
2
+
3
+ Folder ini untuk layer **config**.
4
+
5
+ - **domain** → berisi entity, interface, dan aturan bisnis murni
6
+ - **application** → berisi use case, service koordinasi domain
7
+ - **infrastructure** → berisi API call, repository, integrasi eksternal
8
+ - **presentation** → berisi komponen Angular, routing, dan UI
9
+
10
+ > Bagian ini adalah bagian dari arsitektur **Clean Architecture** untuk fitur ini.
@@ -0,0 +1,10 @@
1
+ # GUARDS
2
+
3
+ Folder ini untuk layer **guards**.
4
+
5
+ - **domain** → berisi entity, interface, dan aturan bisnis murni
6
+ - **application** → berisi use case, service koordinasi domain
7
+ - **infrastructure** → berisi API call, repository, integrasi eksternal
8
+ - **presentation** → berisi komponen Angular, routing, dan UI
9
+
10
+ > Bagian ini adalah bagian dari arsitektur **Clean Architecture** untuk fitur ini.
@@ -0,0 +1,10 @@
1
+ # INTERCEPTORS
2
+
3
+ Folder ini untuk layer **interceptors**.
4
+
5
+ - **domain** → berisi entity, interface, dan aturan bisnis murni
6
+ - **application** → berisi use case, service koordinasi domain
7
+ - **infrastructure** → berisi API call, repository, integrasi eksternal
8
+ - **presentation** → berisi komponen Angular, routing, dan UI
9
+
10
+ > Bagian ini adalah bagian dari arsitektur **Clean Architecture** untuk fitur ini.
@@ -0,0 +1,47 @@
1
+ import { throwError } from 'rxjs';
2
+ import { catchError } from 'rxjs/operators';
3
+
4
+ import { HttpInterceptorFn } from '@angular/common/http';
5
+ import { logger } from '@shared/utils/logger';
6
+
7
+ const excludedUrls: string[] = [
8
+ '/your-excluded-url',
9
+ '/another-excluded-url',
10
+ '/api/health',
11
+ // Tambahkan URL lain yang ingin dikecualikan di sini
12
+ ];
13
+
14
+ export const errorInterceptor: HttpInterceptorFn = (req, next) => {
15
+ return next(req).pipe(
16
+ catchError((err) => {
17
+ if (excludedUrls.some((url) => req.url.includes(url))) {
18
+ return throwError(() => err);
19
+ }
20
+
21
+ // You need to inject services manually since HttpInterceptorFn is a function
22
+ // const helper: HelperService = injector.get(HelperService);
23
+ // const auth: AuthService = injector.get(AuthService);
24
+
25
+ switch (err.status) {
26
+ case 401:
27
+ setTimeout(() => {
28
+ // helper.errorAlertHandler(err);
29
+ // helper.dismissLoading();
30
+ // auth.onLogout();
31
+ }, 1000);
32
+ break;
33
+ default:
34
+ setTimeout(() => {
35
+ // helper.dismissLoading();
36
+ // helper.errorAlertHandler(err);
37
+ }, 1000);
38
+ break;
39
+ }
40
+
41
+ const error = err?.error?.message || err?.statusText;
42
+
43
+ logger.error('Error Interceptor:', error);
44
+ return throwError(() => error);
45
+ })
46
+ );
47
+ };
@@ -0,0 +1,440 @@
1
+ import {
2
+ HttpClient,
3
+ HttpEvent,
4
+ HttpEventType,
5
+ HttpHeaders,
6
+ HttpParams,
7
+ HttpRequest,
8
+ } from '@angular/common/http';
9
+ import { logger } from '@shared/utils/logger';
10
+ import { delay, lastValueFrom, Observable } from 'rxjs';
11
+ import { environment } from 'src/environments/environment';
12
+
13
+ export abstract class AbstractApiService<T> {
14
+ constructor(protected http: HttpClient, protected path: string) {}
15
+
16
+ protected createCustomHeaders(headers: {
17
+ [key: string]: string;
18
+ }): HttpHeaders {
19
+ let customHeaders = new HttpHeaders();
20
+ for (const key in headers) {
21
+ if (headers.hasOwnProperty(key)) {
22
+ customHeaders = customHeaders.set(key, headers[key]);
23
+ }
24
+ }
25
+ return customHeaders;
26
+ }
27
+
28
+ async export2(endpointURL?: string): Promise<any> {
29
+ return lastValueFrom(
30
+ this.http.get<any>(
31
+ `${environment.apiUrl}` + (endpointURL ? endpointURL : this.path),
32
+ { responseType: 'blob' as 'json' }
33
+ )
34
+ );
35
+ }
36
+
37
+ async exportPost(data: any, endpointURL?: string, param?: any): Promise<any> {
38
+ return lastValueFrom(
39
+ this.http.post<any>(
40
+ `${environment.apiUrl}` +
41
+ (endpointURL ? endpointURL : this.path) +
42
+ (param ? '?' + new HttpParams({ fromObject: param }).toString() : ''),
43
+ data,
44
+ { responseType: 'blob' as 'json' }
45
+ )
46
+ );
47
+ }
48
+
49
+ //MARK:GET
50
+ async getAll(params?: any, endpointURL?: string): Promise<any> {
51
+ let param = new HttpParams({ fromObject: params }).toString();
52
+ return lastValueFrom(
53
+ this.http.get<any>(
54
+ `${environment.apiUrl}` +
55
+ (endpointURL ? endpointURL : this.path) +
56
+ (param ? '?' + param : '')
57
+ )
58
+ );
59
+ }
60
+
61
+ async getById(id?: any, endpointURL?: string): Promise<any> {
62
+ return lastValueFrom(
63
+ this.http.get<any>(
64
+ `${environment.apiUrl}` +
65
+ (endpointURL ? endpointURL : this.path) +
66
+ '/' +
67
+ id
68
+ )
69
+ );
70
+ }
71
+
72
+ async get(endpointURL?: string): Promise<any> {
73
+ return lastValueFrom(
74
+ this.http.get<any>(
75
+ `${environment.apiUrl}` + (endpointURL ? endpointURL : this.path)
76
+ )
77
+ );
78
+ }
79
+
80
+ async getURL(url: string): Promise<any> {
81
+ return lastValueFrom(this.http.get<any>(url));
82
+ }
83
+
84
+ //MARK:POST
85
+
86
+ async post(data: any, endpointURL?: string, param?: any): Promise<any> {
87
+ let params;
88
+ if (param) {
89
+ params = new HttpParams({ fromObject: param }).toString();
90
+ }
91
+ return lastValueFrom(
92
+ this.http.post<any>(
93
+ `${environment.apiUrl}` +
94
+ (endpointURL ? endpointURL : this.path) +
95
+ (params ? '?' + params : ''),
96
+ data
97
+ )
98
+ );
99
+ }
100
+
101
+ //POST with form data
102
+
103
+ async post_formdata_edited_array(
104
+ data: any,
105
+ endpointURL?: string,
106
+ params?: any
107
+ ): Promise<any> {
108
+ let param = new HttpParams({ fromObject: params }).toString();
109
+ let body = new FormData();
110
+
111
+ for (var key in data) {
112
+ // body.append(key, data[key]);
113
+
114
+ if (data[key] == null) {
115
+ body.append(key, '');
116
+ continue;
117
+ }
118
+
119
+ if (Array.isArray(data[key])) {
120
+ data[key].forEach((item: any) => {
121
+ body.append(`${key}[]`, item);
122
+ });
123
+ } else {
124
+ body.append(key, data[key]);
125
+ }
126
+ }
127
+
128
+ return lastValueFrom(
129
+ this.http.post<any>(
130
+ `${environment.apiUrl}` +
131
+ (endpointURL ? endpointURL : this.path) +
132
+ (param ? '?' + param : ''),
133
+ body,
134
+ {
135
+ headers: this.createCustomHeaders({
136
+ Accept: 'application/json',
137
+ // Add any other headers you need here
138
+ }),
139
+ }
140
+ )
141
+ );
142
+ }
143
+
144
+ async acces_custom(endpointURL?: string, token?: any) {
145
+ return lastValueFrom(
146
+ this.http.get<any>(
147
+ `${environment.apiUrl}` + (endpointURL ? endpointURL : this.path),
148
+ {
149
+ headers: this.createCustomHeaders({
150
+ // Accept: 'application/json',
151
+ Authorization: `Bearer ${token}`, // Assuming token is a string
152
+ 'Content-Type': 'application/json', // Set content type if needed
153
+ // Add any other headers you need here
154
+ }),
155
+ }
156
+ )
157
+ );
158
+ }
159
+
160
+ async post_formdata(
161
+ data: any,
162
+ endpointURL?: string,
163
+ params?: any
164
+ ): Promise<any> {
165
+ let param = new HttpParams({ fromObject: params }).toString();
166
+ let body = new FormData();
167
+
168
+ for (var key in data) {
169
+ // body.append(key, data[key]);
170
+
171
+ if (Array.isArray(data[key])) {
172
+ data[key].forEach((item: any) => {
173
+ body.append(key, item == null ? '' : item);
174
+ });
175
+ } else {
176
+ body.append(key, data[key] == null ? '' : data[key]);
177
+ }
178
+ }
179
+
180
+ return lastValueFrom(
181
+ this.http.post<any>(
182
+ `${environment.apiUrl}` +
183
+ (endpointURL ? endpointURL : this.path) +
184
+ (param ? '?' + param : ''),
185
+ body,
186
+ {
187
+ headers: this.createCustomHeaders({
188
+ Accept: 'application/json',
189
+ // Add any other headers you need here
190
+ }),
191
+ }
192
+ )
193
+ );
194
+ }
195
+
196
+ //MARK:PUT
197
+ async putId(id: number | string, data: T, endpointURL?: string): Promise<T> {
198
+ return lastValueFrom(
199
+ this.http.put<T>(
200
+ `${environment.apiUrl}` +
201
+ (endpointURL ? endpointURL : this.path) +
202
+ '/' +
203
+ id,
204
+ data
205
+ )
206
+ );
207
+ }
208
+
209
+ async put_custom(data: any, endpointURL?: string, params?: any): Promise<T> {
210
+ let param = new HttpParams({ fromObject: params }).toString();
211
+ return lastValueFrom(
212
+ this.http.put<T>(
213
+ `${environment.apiUrl}` +
214
+ (endpointURL ? endpointURL : this.path) +
215
+ (param ? '?' + param : ''),
216
+ data
217
+ )
218
+ );
219
+ }
220
+
221
+ //put with form data
222
+ async put_formdata(
223
+ data: any,
224
+ endpointURL?: string,
225
+ params?: any
226
+ ): Promise<Response> {
227
+ let param = new HttpParams({ fromObject: params }).toString();
228
+ let body = new FormData();
229
+ for (var key in data) {
230
+ body.append(key, data[key]);
231
+ }
232
+
233
+ return lastValueFrom(
234
+ this.http.put<Response>(
235
+ `${environment.apiUrl}` +
236
+ (endpointURL ? endpointURL : this.path) +
237
+ (param ? '?' + param : ''),
238
+ body,
239
+ {
240
+ headers: this.createCustomHeaders({
241
+ Accept: 'application/json',
242
+ // Add any other headers you need here
243
+ }),
244
+ }
245
+ )
246
+ );
247
+ }
248
+
249
+ //MARK:DELETE
250
+ async delete(id: number | string, endpointURL?: string): Promise<Response> {
251
+ return lastValueFrom(
252
+ this.http.delete<Response>(
253
+ `${environment.apiUrl}` +
254
+ (endpointURL ? endpointURL : this.path) +
255
+ '/' +
256
+ id
257
+ )
258
+ );
259
+ }
260
+ async delete_param(params?: any, endpointURL?: string): Promise<any> {
261
+ let param = new HttpParams({ fromObject: params }).toString();
262
+ return lastValueFrom(
263
+ this.http.delete<any>(
264
+ `${environment.apiUrl}` +
265
+ (endpointURL ? endpointURL : this.path) +
266
+ (param ? '?' + param : '')
267
+ )
268
+ );
269
+ }
270
+
271
+ //MARK:PATCH
272
+ async patch(
273
+ id: number | string,
274
+ partialdata: Partial<T>,
275
+ endpointURL?: string
276
+ ): Promise<T> {
277
+ return lastValueFrom(
278
+ this.http.patch<T>(
279
+ `${environment.apiUrl}` +
280
+ (endpointURL ? endpointURL : this.path) +
281
+ '/' +
282
+ id,
283
+ partialdata
284
+ )
285
+ );
286
+ }
287
+
288
+ async post_formdata_custom(
289
+ data: any,
290
+ endpointURL?: string,
291
+ params?: any
292
+ ): Promise<any> {
293
+ let param = new HttpParams({ fromObject: params }).toString();
294
+ let body = this.jsonToFormData(data);
295
+
296
+ return lastValueFrom(
297
+ this.http.post<any>(
298
+ `${environment.apiUrl}` +
299
+ (endpointURL ? endpointURL : this.path) +
300
+ (param ? '?' + param : ''),
301
+ body,
302
+ {
303
+ headers: this.createCustomHeaders({
304
+ Accept: 'application/json',
305
+ // Add any other headers you need here
306
+ }),
307
+ }
308
+ )
309
+ );
310
+ }
311
+
312
+ async put_formdata_custom(
313
+ data: any,
314
+ endpointURL?: string,
315
+ params?: any
316
+ ): Promise<any> {
317
+ let param = new HttpParams({ fromObject: params }).toString();
318
+ let body = this.jsonToFormData(data);
319
+
320
+ return lastValueFrom(
321
+ this.http.post<any>(
322
+ `${environment.apiUrl}` +
323
+ (endpointURL ? endpointURL : this.path) +
324
+ (param ? '?' + param : ''),
325
+ body,
326
+ {
327
+ headers: this.createCustomHeaders({
328
+ Accept: 'application/json',
329
+ // Add any other headers you need here
330
+ }),
331
+ }
332
+ )
333
+ );
334
+ }
335
+
336
+ uploadFile(file: File): Observable<HttpEvent<any>> {
337
+ const formData: FormData = new FormData();
338
+ formData.append('file', file);
339
+
340
+ const req = this.http.post('/api/upload', formData, {
341
+ reportProgress: true,
342
+ observe: 'events',
343
+ });
344
+
345
+ return req;
346
+ }
347
+
348
+ consoleFormData(payload: any) {
349
+ const form = this.jsonToFormData(payload);
350
+ for (const [key, value] of form.entries()) {
351
+ console.log(`${key}: ${value}`);
352
+ }
353
+
354
+ logger.log('Form Data:', form);
355
+ }
356
+
357
+ jsonToFormData(
358
+ json: any,
359
+ formData: FormData = new FormData(),
360
+ parentKey: string = ''
361
+ ): FormData {
362
+ for (const key in json) {
363
+ if (json.hasOwnProperty(key)) {
364
+ const value = json[key];
365
+
366
+ //console.log(value);
367
+ const newKey = parentKey ? `${parentKey}[${key}]` : key;
368
+
369
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
370
+ // Rekursif untuk nested object
371
+ if (value instanceof File) {
372
+ formData.append(newKey, value);
373
+ } else {
374
+ this.jsonToFormData(value, formData, newKey);
375
+ }
376
+ } else if (Array.isArray(value)) {
377
+ // Tangani array
378
+ value.forEach((item, index) => {
379
+ if (item && typeof item === 'object' && item.id !== undefined) {
380
+ // Array berisi objek dengan id
381
+
382
+ if (item?.isObject) {
383
+ for (const subKey in item) {
384
+ if (item.hasOwnProperty(subKey) && subKey != 'isObject') {
385
+ const subValue = item[subKey];
386
+ const newSubKey = `${newKey}[${item.id}][${subKey}]`;
387
+ if (subValue && typeof subValue === 'object') {
388
+ // this.jsonToFormData(subValue, formData, newSubKey);
389
+ formData.append(newSubKey, !subValue ? '' : subValue);
390
+ } else {
391
+ formData.append(newSubKey, !subValue ? '' : subValue);
392
+ }
393
+ }
394
+ }
395
+ } else {
396
+ formData.append(
397
+ `${newKey}[${item.id}]`,
398
+ item?.value ? item.value : ''
399
+ );
400
+ }
401
+ } else {
402
+ // Array berisi objek sederhana
403
+ if (item?.isObject) {
404
+ for (const subKey in item) {
405
+ if (item.hasOwnProperty(subKey) && subKey != 'isObject') {
406
+ const subValue = item[subKey];
407
+ const newSubKey = `${newKey}[${index}][${subKey}]`;
408
+ if (subValue && typeof subValue === 'object') {
409
+ // this.jsonToFormData(subValue, formData, newSubKey);
410
+ formData.append(newSubKey, !subValue ? '' : subValue);
411
+ } else {
412
+ formData.append(newSubKey, !subValue ? '' : subValue);
413
+ }
414
+ }
415
+ }
416
+ } else {
417
+ // Array berisi nilai sederhana
418
+ formData.append(
419
+ `${newKey}[${index}]`,
420
+ item === null || item === undefined ? '' : item
421
+ );
422
+ }
423
+ }
424
+ });
425
+
426
+ if (value.length === 0) {
427
+ formData.append(`${newKey}[]`, '');
428
+ }
429
+ } else {
430
+ // Nilai sederhana langsung append ke FormData
431
+ formData.append(
432
+ newKey,
433
+ value === null || value === undefined ? '' : value
434
+ );
435
+ }
436
+ }
437
+ }
438
+ return formData;
439
+ }
440
+ }
@@ -0,0 +1,36 @@
1
+ import { inject, Injectable } from '@angular/core';
2
+ import {
3
+ HttpRequest,
4
+ HttpHandler,
5
+ HttpEvent,
6
+ HttpInterceptor,
7
+ HttpInterceptorFn,
8
+ } from '@angular/common/http';
9
+ import { Observable, finalize, from, switchMap } from 'rxjs';
10
+
11
+ import { StorageMap } from '@ngx-pwa/local-storage';
12
+ import { environment } from 'src/environments/environment';
13
+
14
+ export const jwtInterceptor: HttpInterceptorFn = (request, next) => {
15
+ // Assume you have access to StorageMap and environment
16
+ // You may need to inject StorageMap via a provider in your app module
17
+ const storage = inject(StorageMap);
18
+
19
+ return from(storage.get(environment.tokenName)).pipe(
20
+ switchMap((token: any) => {
21
+ if (token) {
22
+ request = request.clone({
23
+ setHeaders: {
24
+ Authorization: `Bearer ${token.token}`,
25
+ },
26
+ });
27
+ }
28
+
29
+ return next(request).pipe(
30
+ finalize(() => {
31
+ // end loading
32
+ })
33
+ );
34
+ })
35
+ );
36
+ };
@@ -0,0 +1,10 @@
1
+ # MODELS
2
+
3
+ Folder ini untuk layer **models**.
4
+
5
+ - **domain** → berisi entity, interface, dan aturan bisnis murni
6
+ - **application** → berisi use case, service koordinasi domain
7
+ - **infrastructure** → berisi API call, repository, integrasi eksternal
8
+ - **presentation** → berisi komponen Angular, routing, dan UI
9
+
10
+ > Bagian ini adalah bagian dari arsitektur **Clean Architecture** untuk fitur ini.
@@ -0,0 +1,10 @@
1
+ # SERVICES
2
+
3
+ Folder ini untuk layer **services**.
4
+
5
+ - **domain** → berisi entity, interface, dan aturan bisnis murni
6
+ - **application** → berisi use case, service koordinasi domain
7
+ - **infrastructure** → berisi API call, repository, integrasi eksternal
8
+ - **presentation** → berisi komponen Angular, routing, dan UI
9
+
10
+ > Bagian ini adalah bagian dari arsitektur **Clean Architecture** untuk fitur ini.
@@ -0,0 +1,10 @@
1
+ # SHARED
2
+
3
+ Folder ini untuk layer **shared**.
4
+
5
+ - **domain** → berisi entity, interface, dan aturan bisnis murni
6
+ - **application** → berisi use case, service koordinasi domain
7
+ - **infrastructure** → berisi API call, repository, integrasi eksternal
8
+ - **presentation** → berisi komponen Angular, routing, dan UI
9
+
10
+ > Bagian ini adalah bagian dari arsitektur **Clean Architecture** untuk fitur ini.
@@ -0,0 +1,10 @@
1
+ # DIRECTIVES
2
+
3
+ Folder ini untuk layer **directives**.
4
+
5
+ - **domain** → berisi entity, interface, dan aturan bisnis murni
6
+ - **application** → berisi use case, service koordinasi domain
7
+ - **infrastructure** → berisi API call, repository, integrasi eksternal
8
+ - **presentation** → berisi komponen Angular, routing, dan UI
9
+
10
+ > Bagian ini adalah bagian dari arsitektur **Clean Architecture** untuk fitur ini.
@@ -0,0 +1,10 @@
1
+ # PIPES
2
+
3
+ Folder ini untuk layer **pipes**.
4
+
5
+ - **domain** → berisi entity, interface, dan aturan bisnis murni
6
+ - **application** → berisi use case, service koordinasi domain
7
+ - **infrastructure** → berisi API call, repository, integrasi eksternal
8
+ - **presentation** → berisi komponen Angular, routing, dan UI
9
+
10
+ > Bagian ini adalah bagian dari arsitektur **Clean Architecture** untuk fitur ini.
@@ -0,0 +1,10 @@
1
+ # UI
2
+
3
+ Folder ini untuk layer **ui**.
4
+
5
+ - **domain** → berisi entity, interface, dan aturan bisnis murni
6
+ - **application** → berisi use case, service koordinasi domain
7
+ - **infrastructure** → berisi API call, repository, integrasi eksternal
8
+ - **presentation** → berisi komponen Angular, routing, dan UI
9
+
10
+ > Bagian ini adalah bagian dari arsitektur **Clean Architecture** untuk fitur ini.