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.
- package/README.md +25 -0
- package/bin/index.js +11 -7
- package/bin/utils/auth.js +0 -3
- package/bin/utils/createStructure.js +36 -7
- package/bin/utils/{setupAssets.js → refactorAngularJson.js} +63 -3
- package/bin/utils/resource/.postcssrc.json +12 -0
- package/bin/utils/resource/package.json +48 -0
- package/bin/utils/resource/src/app/MODULES/auth/README.md +10 -0
- package/bin/utils/resource/src/app/MODULES/dashboard/README.md +10 -0
- package/bin/utils/resource/src/app/MODULES/home/README.md +10 -0
- package/bin/utils/resource/src/app/app.config.ts +20 -0
- package/bin/utils/resource/src/app/app.html +263 -0
- package/bin/utils/resource/src/app/app.routes.ts +3 -0
- package/bin/utils/resource/src/app/app.scss +6 -0
- package/bin/utils/resource/src/app/app.spec.ts +25 -0
- package/bin/utils/resource/src/app/app.ts +32 -0
- package/bin/utils/resource/src/app/core/README.md +10 -0
- package/bin/utils/resource/src/app/core/config/README.md +10 -0
- package/bin/utils/resource/src/app/core/guards/README.md +10 -0
- package/bin/utils/resource/src/app/core/interceptors/README.md +10 -0
- package/bin/utils/resource/src/app/core/interceptors/error.interceptor.ts +47 -0
- package/bin/utils/resource/src/app/core/interceptors/jsonrpc.ts +440 -0
- package/bin/utils/resource/src/app/core/interceptors/jwt.interceptor.ts +36 -0
- package/bin/utils/resource/src/app/core/models/README.md +10 -0
- package/bin/utils/resource/src/app/core/services/README.md +10 -0
- package/bin/utils/resource/src/app/shared/README.md +10 -0
- package/bin/utils/resource/src/app/shared/directives/README.md +10 -0
- package/bin/utils/resource/src/app/shared/pipes/README.md +10 -0
- package/bin/utils/resource/src/app/shared/ui/README.md +10 -0
- package/bin/utils/resource/src/app/shared/utils/README.md +10 -0
- package/bin/utils/resource/src/app/shared/utils/logger.ts +40 -0
- package/bin/utils/resource/src/app/shared/widgets/test/test.component.css +0 -0
- package/bin/utils/resource/src/app/shared/widgets/test/test.component.html +20 -0
- package/bin/utils/resource/src/app/shared/widgets/test/test.component.ts +56 -0
- package/bin/utils/resource/src/app/shared/widgets/test/test.module.ts +10 -0
- package/bin/utils/resource/src/environments/environment.prod.ts +6 -0
- package/bin/utils/resource/src/environments/environment.staging.ts +6 -0
- package/bin/utils/resource/src/environments/environment.ts +6 -0
- package/bin/utils/resource/src/index.html +16 -0
- package/bin/utils/resource/src/main.ts +6 -0
- package/bin/utils/resource/src/styles.scss +2 -0
- package/bin/utils/resource/src/tailwind.css +11 -0
- package/bin/utils/resource/tailwind.config.js +19 -0
- package/bin/utils/resource/tsconfig.json +46 -0
- package/coba/.editorconfig +17 -0
- package/coba/.postcssrc.json +12 -0
- package/coba/.vscode/extensions.json +4 -0
- package/coba/.vscode/launch.json +20 -0
- package/coba/.vscode/tasks.json +42 -0
- package/coba/README.md +59 -0
- package/coba/angular.json +102 -0
- package/coba/package-lock.json +10696 -0
- package/coba/package.json +48 -0
- package/coba/public/favicon.ico +0 -0
- package/coba/src/app/MODULES/auth/README.md +10 -0
- package/coba/src/app/MODULES/dashboard/README.md +10 -0
- package/coba/src/app/MODULES/home/README.md +10 -0
- package/coba/src/app/app.config.ts +20 -0
- package/coba/src/app/app.html +263 -0
- package/coba/src/app/app.routes.ts +3 -0
- package/coba/src/app/app.scss +6 -0
- package/coba/src/app/app.spec.ts +25 -0
- package/coba/src/app/app.ts +32 -0
- package/coba/src/app/core/README.md +10 -0
- package/coba/src/app/core/config/README.md +10 -0
- package/coba/src/app/core/guards/README.md +10 -0
- package/coba/src/app/core/interceptors/README.md +10 -0
- package/coba/src/app/core/interceptors/error.interceptor.ts +47 -0
- package/coba/src/app/core/interceptors/jsonrpc.ts +440 -0
- package/coba/src/app/core/interceptors/jwt.interceptor.ts +36 -0
- package/coba/src/app/core/models/README.md +10 -0
- package/coba/src/app/core/services/README.md +10 -0
- package/coba/src/app/shared/README.md +10 -0
- package/coba/src/app/shared/directives/README.md +10 -0
- package/coba/src/app/shared/pipes/README.md +10 -0
- package/coba/src/app/shared/ui/README.md +10 -0
- package/coba/src/app/shared/utils/README.md +10 -0
- package/coba/src/app/shared/utils/logger.ts +40 -0
- package/coba/src/app/shared/widgets/test/test.component.css +0 -0
- package/coba/src/app/shared/widgets/test/test.component.html +20 -0
- package/coba/src/app/shared/widgets/test/test.component.ts +56 -0
- package/coba/src/app/shared/widgets/test/test.module.ts +10 -0
- package/coba/src/environments/environment.prod.ts +6 -0
- package/coba/src/environments/environment.staging.ts +6 -0
- package/coba/src/environments/environment.ts +6 -0
- package/coba/src/index.html +16 -0
- package/coba/src/main.ts +6 -0
- package/coba/src/styles.scss +2 -0
- package/coba/src/tailwind.css +11 -0
- package/coba/tailwind.config.js +19 -0
- package/coba/tsconfig.app.json +15 -0
- package/coba/tsconfig.json +46 -0
- package/coba/tsconfig.spec.json +14 -0
- package/package.json +1 -1
- 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.
|