unictive-angular 1.0.37 → 1.0.40

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 (55) hide show
  1. package/bin/utils/createStructure.js +6 -0
  2. package/bin/utils/resource/.postcssrc.json +1 -2
  3. package/bin/utils/resource/bitbucket-pipelines.yml +31 -0
  4. package/bin/utils/resource/src/app/API/auth/auth.service.ts +160 -0
  5. package/bin/utils/resource/src/app/PAGES/authentication/login/login.component.spec.ts +28 -0
  6. package/bin/utils/resource/src/app/{MODULES → PAGES}/authentication/login/login.component.ts +9 -3
  7. package/bin/utils/resource/src/app/PAGES/authentication/page404/page404.component.spec.ts +28 -0
  8. package/bin/utils/resource/src/app/{MODULES → PAGES}/authentication/page404/page404.component.ts +1 -2
  9. package/bin/utils/resource/src/app/PAGES/home/home.component.html +5 -0
  10. package/bin/utils/resource/src/app/PAGES/home/home.component.spec.ts +28 -0
  11. package/bin/utils/resource/src/app/{MODULES → PAGES}/home/home.component.ts +3 -3
  12. package/bin/utils/resource/src/app/app.config.ts +30 -0
  13. package/bin/utils/resource/src/app/app.routes.ts +4 -7
  14. package/bin/utils/resource/src/app/app.ts +6 -5
  15. package/bin/utils/resource/src/app/core/config/entity-store.ts +44 -15
  16. package/bin/utils/resource/src/app/core/config/single-store.ts +2 -0
  17. package/bin/utils/resource/src/app/core/guards/auth.guard.ts +43 -0
  18. package/bin/utils/resource/src/app/core/guards/login.guard.ts +37 -0
  19. package/bin/utils/resource/src/app/core/models/entities/user.entity.ts +36 -7
  20. package/bin/utils/resource/src/app/core/services/helper.service.ts +41 -28
  21. package/bin/utils/resource/src/app/core/services/listener.service.ts +25 -0
  22. package/bin/utils/resource/src/app/shared/ui/button/button.component.ts +96 -0
  23. package/bin/utils/resource/tsconfig.json +2 -2
  24. package/bin/utils/resource/workers-site/.cargo-ok +0 -0
  25. package/bin/utils/resource/workers-site/.genignore +1 -0
  26. package/bin/utils/resource/workers-site/.github/workflows/semgrep.yml +25 -0
  27. package/bin/utils/resource/workers-site/LICENSE_APACHE +176 -0
  28. package/bin/utils/resource/workers-site/LICENSE_MIT +25 -0
  29. package/bin/utils/resource/workers-site/index.js +66 -0
  30. package/bin/utils/resource/workers-site/package.json +12 -0
  31. package/bin/utils/updatePackageJson.js +78 -54
  32. package/index.js +34 -2
  33. package/package.json +1 -1
  34. package/bin/utils/resource/src/app/MODULES/auth/README.md +0 -10
  35. package/bin/utils/resource/src/app/MODULES/authentication/login/login.module.ts +0 -19
  36. package/bin/utils/resource/src/app/MODULES/authentication/page404/page404.module.ts +0 -17
  37. package/bin/utils/resource/src/app/MODULES/authentication/register/register.component.html +0 -3
  38. package/bin/utils/resource/src/app/MODULES/authentication/register/register.component.ts +0 -16
  39. package/bin/utils/resource/src/app/MODULES/authentication/register/register.module.ts +0 -18
  40. package/bin/utils/resource/src/app/MODULES/dashboard/README.md +0 -10
  41. package/bin/utils/resource/src/app/MODULES/home/README.md +0 -10
  42. package/bin/utils/resource/src/app/MODULES/home/home.component.html +0 -4
  43. package/bin/utils/resource/src/app/MODULES/home/home.module.ts +0 -19
  44. package/bin/utils/resource/src/app/core/models/entities/screen.entity.spec.ts +0 -41
  45. package/bin/utils/resource/src/app/core/models/entities/screen.entity.ts +0 -16
  46. package/bin/utils/resource/src/app/core/respositories/screen_repo/screen.repo.ts +0 -19
  47. package/bin/utils/resource/src/app/core/respository/screen_repo/screen.repo.ts +0 -27
  48. package/bin/utils/resource/src/app/core/respository/user/user_repo.ts +0 -26
  49. package/bin/utils/resource/src/styles.scss +0 -2
  50. /package/bin/utils/resource/src/app/{MODULES/authentication/login/login.component.scss → PAGES/authentication/login/login.component.css} +0 -0
  51. /package/bin/utils/resource/src/app/{MODULES → PAGES}/authentication/login/login.component.html +0 -0
  52. /package/bin/utils/resource/src/app/{MODULES/authentication/page404/page404.component.scss → PAGES/authentication/page404/page404.component.css} +0 -0
  53. /package/bin/utils/resource/src/app/{MODULES → PAGES}/authentication/page404/page404.component.html +0 -0
  54. /package/bin/utils/resource/src/app/{MODULES/authentication/register/register.component.scss → PAGES/home/home.component.css} +0 -0
  55. /package/bin/utils/resource/src/{app/MODULES/home/home.component.scss → styles.css} +0 -0
@@ -7,17 +7,23 @@ const path_template = path.join(__dirname, "resource");
7
7
  // Bisa, Anda dapat menambahkan nama file ke dalam array folders_to_copy.
8
8
  // Misal:
9
9
  const folders_to_copy = [
10
+ "workers-site",
10
11
  "src/app/API",
11
12
  "src/app/core",
12
13
  "src/app/shared",
14
+ "src/app/PAGES",
13
15
  "src/app/app.config.ts",
14
16
  "src/app/app.routes.ts",
15
17
  "src/app/app.ts",
18
+ "src/app/app.html",
16
19
  "src/environments",
20
+
17
21
  // "package.json",
18
22
  // "tailwind.config.js",
23
+ "src/tailwind.css",
19
24
  ".postcssrc.json",
20
25
  "tsconfig.json",
26
+ "bitbucket-pipelines.yml",
21
27
  ];
22
28
 
23
29
  async function copy_template() {
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "plugins": {
3
- "@tailwindcss/postcss": {},
4
- "autoprefixer": {}
3
+ "@tailwindcss/postcss": {}
5
4
  }
6
5
 
7
6
  }
@@ -0,0 +1,31 @@
1
+ image: node:22 # Gunakan image Node.js versi 22 untuk menjalankan pipeline
2
+
3
+ definitions:
4
+ caches:
5
+ sonar: ~/.sonar
6
+ node: ~/.npm # Cache untuk dependencies Node.js
7
+
8
+ steps:
9
+ - step: &build-angular
10
+ name: Build Angular App
11
+ image: node:22
12
+ caches:
13
+ - node
14
+ script:
15
+ - npm install -g wrangler # Install Wrangler CLI versi terbaru
16
+ - npm install --force # Install dependencies dari package.json di root
17
+ - npm run build
18
+ - cd workers-site
19
+ - npm install
20
+ - ls
21
+ - cd ../
22
+ - CLOUDFLARE_API_TOKEN=$CF_API_TOKEN wrangler deploy --config wrangler.toml
23
+
24
+ clone:
25
+ depth: full # Clone seluruh history git (diperlukan SonarQube)
26
+
27
+ pipelines:
28
+ branches:
29
+ staging:
30
+ # - step: *sonar-analysis
31
+ - step: *build-angular
@@ -0,0 +1,160 @@
1
+ import { HttpClient } from '@angular/common/http';
2
+ import { effect, Injectable } from '@angular/core';
3
+ import { singleStoreFactory } from '@core/config/single-store';
4
+ import { AbstractApiService } from '@core/interceptors/jsonrpc';
5
+ import { Entity_User } from '@core/models/entities/user.entity';
6
+
7
+ import { HelperService, Popup_m } from '@core/services/helper.service';
8
+ import { ForceLogoutReason, ListenerService } from '@core/services/listener.service';
9
+
10
+ import { logger } from '@shared/utils/logger';
11
+ import { environment } from 'src/environments/environment';
12
+
13
+
14
+ @Injectable({
15
+ providedIn: 'root'
16
+ })
17
+ export class AuthService extends AbstractApiService<any> {
18
+
19
+ private isLoggingOut = false;
20
+
21
+ constructor(
22
+ http: HttpClient,
23
+ private helper: HelperService,
24
+ private listener: ListenerService
25
+ ) {
26
+ super(http, '');
27
+
28
+
29
+ effect(() => {
30
+ const event = this.listener.forceLogout();
31
+
32
+ if (!event || this.isLoggingOut) return;
33
+
34
+ this.isLoggingOut = true;
35
+ this.handleForceLogout(event);
36
+ });
37
+ }
38
+ mockUser = {
39
+
40
+ key: "ABC123",
41
+ peopleId: 123,
42
+ peopleName: "John Doe",
43
+ peopleUsername: "johndoe",
44
+ nip: "123456789",
45
+ imageProfile: "https://example.com/profile.jpg",
46
+ positionActive: "Manager",
47
+ roleActive: 5,
48
+ positionHistory: {}
49
+
50
+ };
51
+
52
+
53
+ async post_login(
54
+ data: { username: string; password: string },
55
+ param?: any,
56
+ ) {
57
+
58
+ return Entity_User.load(async () => {
59
+
60
+ const params = param ? param : {};
61
+ const res = await this.post(data, '/login', params);
62
+ if (res) {
63
+
64
+
65
+ if (res?.data?.is_internal) {
66
+ this.setLogin(true);
67
+ this.helper.setStorage('users', res?.data?.user);
68
+ this.helper.setStorage(environment.tokenName, { token: res?.data?.token, expired: res?.data?.expiresAt });
69
+ }
70
+ }
71
+ logger.log('Logged', res?.data?.user);
72
+ return res?.data;
73
+ });
74
+ }
75
+
76
+ initFromApi() {
77
+ this.helper.getStorage(environment.tokenName).then((res) => {
78
+ if (res) {
79
+ this.getUserdata();
80
+ }
81
+ })
82
+ }
83
+
84
+ getUserdata(useMock: boolean = false) {
85
+
86
+ this.helper.getStorage('users').then((res: any) => {
87
+ if (res) {
88
+ Entity_User.set(res);
89
+ }
90
+ })
91
+
92
+ return Entity_User.load(async () => {
93
+ let isRole = null;
94
+ await this.helper.getStorage('role').then((res) => {
95
+ if (res) {
96
+ isRole = res;
97
+ }
98
+ });
99
+
100
+ const res = await this.get(isRole ? `/refresh?role=${isRole}` : '/refresh');
101
+ if (res) {
102
+ this.setLogin(true);
103
+ this.helper.setStorage('users', res?.data?.user);
104
+ }
105
+ // logger.log('userdata', res?.data?.user);
106
+ return res?.data?.user;
107
+ });
108
+ }
109
+
110
+
111
+
112
+
113
+ setLogin(state: boolean) {
114
+ const isLogin = singleStoreFactory<{ isLogin: boolean }>();
115
+ isLogin.set({ isLogin: state });
116
+ }
117
+
118
+
119
+
120
+ async logout() {
121
+ this.helper.showLoading('Logging out...');
122
+
123
+ setTimeout(async () => {
124
+
125
+
126
+
127
+ this.helper.removeStorage('users');
128
+ this.helper.removeStorage(environment.tokenName);
129
+ this.helper.removeStorage('isGoogleLogin');
130
+
131
+
132
+ window.location.href = '/';
133
+ window.location.reload();
134
+ }, 2000);
135
+ // await this.logoutNadia();
136
+ }
137
+
138
+
139
+ handleForceLogout(event: { reason: ForceLogoutReason; payload?: any; at: number }) {
140
+
141
+ console.warn('Force logout:', event.reason);
142
+
143
+ const popup: Popup_m = {
144
+ type: 'warning',
145
+ title: 'Logged out',
146
+ message: 'Your account has been logged in from another device. You have been logged out for security reasons.',
147
+ button: 'OK',
148
+ showCancel: false,
149
+ };
150
+
151
+
152
+
153
+ this.helper.confirmationAlert(popup, false).then((ok) => {
154
+ if (!ok) return;
155
+ this.listener.clear();
156
+ this.logout();
157
+ });
158
+ }
159
+
160
+ }
@@ -0,0 +1,28 @@
1
+ /* tslint:disable:no-unused-variable */
2
+ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3
+ import { By } from '@angular/platform-browser';
4
+ import { DebugElement } from '@angular/core';
5
+
6
+ import { LoginComponent } from './login.component';
7
+
8
+ describe('LoginComponent', () => {
9
+ let component: LoginComponent;
10
+ let fixture: ComponentFixture<LoginComponent>;
11
+
12
+ beforeEach(async(() => {
13
+ TestBed.configureTestingModule({
14
+ declarations: [ LoginComponent ]
15
+ })
16
+ .compileComponents();
17
+ }));
18
+
19
+ beforeEach(() => {
20
+ fixture = TestBed.createComponent(LoginComponent);
21
+ component = fixture.componentInstance;
22
+ fixture.detectChanges();
23
+ });
24
+
25
+ it('should create', () => {
26
+ expect(component).toBeTruthy();
27
+ });
28
+ });
@@ -1,12 +1,18 @@
1
1
  import { Component, OnInit } from '@angular/core';
2
- import { FormBuilder, FormGroup, Validators } from '@angular/forms';
2
+ import { FormBuilder, FormGroup, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
3
3
  import { emailPatternValidator } from '@shared/utils/validator_pattern';
4
+ import { CommonModule } from '@angular/common';
5
+ import { InputTextComponent } from '@shared/ui/input-text/input-text.component';
4
6
 
5
7
  @Component({
6
8
  selector: 'app-login',
7
9
  templateUrl: './login.component.html',
8
- styleUrls: ['./login.component.scss'],
9
- standalone: false
10
+ styleUrls: ['./login.component.css'],
11
+ imports: [CommonModule,
12
+ ReactiveFormsModule,
13
+ FormsModule,
14
+ InputTextComponent
15
+ ],
10
16
  })
11
17
  export class LoginComponent implements OnInit {
12
18
  form!: FormGroup;
@@ -0,0 +1,28 @@
1
+ /* tslint:disable:no-unused-variable */
2
+ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3
+ import { By } from '@angular/platform-browser';
4
+ import { DebugElement } from '@angular/core';
5
+
6
+ import { Page404Component } from './page404.component';
7
+
8
+ describe('Page404Component', () => {
9
+ let component: Page404Component;
10
+ let fixture: ComponentFixture<Page404Component>;
11
+
12
+ beforeEach(async(() => {
13
+ TestBed.configureTestingModule({
14
+ declarations: [ Page404Component ]
15
+ })
16
+ .compileComponents();
17
+ }));
18
+
19
+ beforeEach(() => {
20
+ fixture = TestBed.createComponent(Page404Component);
21
+ component = fixture.componentInstance;
22
+ fixture.detectChanges();
23
+ });
24
+
25
+ it('should create', () => {
26
+ expect(component).toBeTruthy();
27
+ });
28
+ });
@@ -3,8 +3,7 @@ import { Component, OnInit } from '@angular/core';
3
3
  @Component({
4
4
  selector: 'app-page404',
5
5
  templateUrl: './page404.component.html',
6
- styleUrls: ['./page404.component.scss'],
7
- standalone: false
6
+ styleUrls: ['./page404.component.css']
8
7
  })
9
8
  export class Page404Component implements OnInit {
10
9
 
@@ -0,0 +1,5 @@
1
+ <p>
2
+ home works!
3
+ </p>
4
+
5
+ <app-widget_game></app-widget_game>
@@ -0,0 +1,28 @@
1
+ /* tslint:disable:no-unused-variable */
2
+ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3
+ import { By } from '@angular/platform-browser';
4
+ import { DebugElement } from '@angular/core';
5
+
6
+ import { HomeComponent } from './home.component';
7
+
8
+ describe('HomeComponent', () => {
9
+ let component: HomeComponent;
10
+ let fixture: ComponentFixture<HomeComponent>;
11
+
12
+ beforeEach(async(() => {
13
+ TestBed.configureTestingModule({
14
+ declarations: [ HomeComponent ]
15
+ })
16
+ .compileComponents();
17
+ }));
18
+
19
+ beforeEach(() => {
20
+ fixture = TestBed.createComponent(HomeComponent);
21
+ component = fixture.componentInstance;
22
+ fixture.detectChanges();
23
+ });
24
+
25
+ it('should create', () => {
26
+ expect(component).toBeTruthy();
27
+ });
28
+ });
@@ -1,10 +1,10 @@
1
1
  import { Component, OnInit } from '@angular/core';
2
-
2
+ import { Widget_gameModule } from '@shared/widgets/widget_game/widget_game.module';
3
3
  @Component({
4
4
  selector: 'app-home',
5
5
  templateUrl: './home.component.html',
6
- styleUrls: ['./home.component.scss'],
7
- standalone: false
6
+ styleUrls: ['./home.component.css'],
7
+ imports: [Widget_gameModule],
8
8
  })
9
9
  export class HomeComponent implements OnInit {
10
10
 
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  ApplicationConfig,
3
+ inject,
4
+ provideAppInitializer,
3
5
  provideBrowserGlobalErrorListeners,
4
6
  provideZonelessChangeDetection,
5
7
  } from '@angular/core';
@@ -9,12 +11,40 @@ import { routes } from './app.routes';
9
11
  import { provideHttpClient, withInterceptors } from '@angular/common/http';
10
12
  import { jwtInterceptor } from '@core/interceptors/jwt.interceptor';
11
13
  import { errorInterceptor } from '@core/interceptors/error.interceptor';
14
+ import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
15
+
16
+ import { AuthService } from '@api/auth/auth.service';
17
+ import { providePrimeNG } from 'primeng/config';
18
+ import Aura from '@primeuix/themes/aura';
19
+
20
+ export function appInitFactoryFromApi(auth: AuthService) {
21
+ return auth.initFromApi();
22
+ }
12
23
 
13
24
  export const appConfig: ApplicationConfig = {
14
25
  providers: [
15
26
  provideBrowserGlobalErrorListeners(),
27
+ provideAnimationsAsync(),
16
28
  provideZonelessChangeDetection(),
17
29
  provideRouter(routes),
18
30
  provideHttpClient(withInterceptors([jwtInterceptor, errorInterceptor])),
31
+ provideAppInitializer(() => appInitFactoryFromApi(inject(AuthService))),
32
+
33
+ providePrimeNG({
34
+ theme: {
35
+ preset: Aura,
36
+ options: {
37
+ darkModeSelector: '[data-theme="dark"]',
38
+ lightModeSelector: '[data-theme="light"]',
39
+ },
40
+ },
41
+
42
+ zIndex: {
43
+ modal: 1100, // dialog, sidebar
44
+ overlay: 1000, // dropdown, overlaypanel
45
+ menu: 1000, // overlay menus
46
+ tooltip: 1100 // tooltip
47
+ }
48
+ }),
19
49
  ],
20
50
  };
@@ -3,20 +3,17 @@ import { Routes } from '@angular/router';
3
3
  export const routes: Routes = [
4
4
  {
5
5
  path: '',
6
- loadChildren: () => import('./MODULES/home/home.module').then(m => m.HomeModule)
6
+ loadComponent: () => import('./PAGES/home/home.component').then(m => m.HomeComponent)
7
7
  },
8
8
 
9
9
  //MARK: AUTHENTICATION
10
10
  {
11
11
  path: 'login',
12
- loadChildren: () => import('./MODULES/authentication/login/login.module').then(m => m.LoginModule)
13
- },
14
- {
15
- path: 'register',
16
- loadChildren: () => import('./MODULES/authentication/register/register.module').then(m => m.RegisterModule)
12
+ loadComponent: () => import('./PAGES/authentication/login/login.component').then(m => m.LoginComponent)
17
13
  },
14
+
18
15
  {
19
16
  path: '**',
20
- loadChildren: () => import('./MODULES/authentication/page404/page404.module').then(m => m.Page404Module)
17
+ loadComponent: () => import('./PAGES/authentication/page404/page404.component').then(m => m.Page404Component)
21
18
  }
22
19
  ];
@@ -1,21 +1,21 @@
1
1
  import { ChangeDetectorRef, Component, effect, OnInit } from '@angular/core';
2
2
  import { RouterOutlet } from '@angular/router';
3
3
  import { Entity_User } from '@core/models/entities/user.entity';
4
- import { Repo_User } from '@core/respository/user/user_repo';
5
-
6
4
  import { logger } from '@shared/utils/logger';
5
+
6
+
7
7
  @Component({
8
8
  selector: 'app-root',
9
9
  imports: [RouterOutlet],
10
10
  templateUrl: './app.html',
11
- styleUrl: './app.scss',
11
+ styleUrl: './app.css',
12
12
  })
13
13
  export class App implements OnInit {
14
14
  protected title = 'OLD PROJECT';
15
15
 
16
16
  user = Entity_User.state();
17
17
 
18
- constructor(private cdr: ChangeDetectorRef, private repo_user: Repo_User) {
18
+ constructor(private cdr: ChangeDetectorRef) {
19
19
  effect(() => {
20
20
  const user = this.user.value;
21
21
  logger.log('User state changed:', user);
@@ -26,7 +26,8 @@ export class App implements OnInit {
26
26
 
27
27
  ngOnInit(): void {
28
28
 
29
- this.repo_user.load_user();
29
+
30
+
30
31
 
31
32
  //MARK:TEST
32
33
  setTimeout(() => {
@@ -1,4 +1,5 @@
1
1
  import { signal, computed } from '@angular/core';
2
+ import { filter } from 'rxjs';
2
3
 
3
4
  export interface EntityState<T> {
4
5
  value: T[];
@@ -6,37 +7,60 @@ export interface EntityState<T> {
6
7
  error?: string;
7
8
  }
8
9
 
9
- export function entityStoreFactory<T extends { id: string | number }>() {
10
+ export function entityStoreFactory<
11
+ T extends Record<any, any>,
12
+ K extends keyof T = 'id'
13
+ >(keyField?: K | any) {
14
+
15
+ // console.log('ListStoreFactory keyField:', keyField);
16
+ const field = (keyField ?? ('id' as K));
17
+
10
18
  const _state = signal<EntityState<T>>({ value: [], loading: false });
11
19
 
20
+ const mapByKey = computed(() => {
21
+ const map = new Map<T[K], T>();
22
+ for (const item of _state().value) {
23
+ map.set(item[field], item);
24
+ }
25
+ return map;
26
+ });
27
+
12
28
  return {
13
29
  state: _state,
30
+ keyField: field,
31
+ mapByKey,
14
32
 
15
- // Mutations
33
+ // --- Mutations ---
16
34
  setAll(value: T[]) {
17
35
  _state.set({ value, loading: false });
18
36
  },
19
37
 
38
+ filtered(predicate?: (item: T) => boolean) {
39
+ return computed(() =>
40
+ predicate ? _state().value.filter(predicate) : _state().value
41
+ );
42
+ },
43
+
20
44
  add(item: T) {
21
45
  _state.update((curr) => ({
22
46
  ...curr,
23
- value: [...curr.value, item]
47
+ value: [...curr.value, item],
24
48
  }));
25
49
  },
26
50
 
27
- update(id: T['id'], partial: Partial<T>) {
51
+ update(key: T[K], partial: Partial<T>) {
28
52
  _state.update((curr) => ({
29
53
  ...curr,
30
54
  value: curr.value.map((i) =>
31
- i.id === id ? { ...i, ...partial } : i
32
- )
55
+ i[field] === key ? { ...i, ...partial } : i
56
+ ),
33
57
  }));
34
58
  },
35
59
 
36
- remove(id: T['id']) {
60
+ remove(key: T[K]) {
37
61
  _state.update((curr) => ({
38
62
  ...curr,
39
- value: curr.value.filter((i) => i.id !== id)
63
+ value: curr.value.filter((i) => i[field] !== key),
40
64
  }));
41
65
  },
42
66
 
@@ -44,26 +68,31 @@ export function entityStoreFactory<T extends { id: string | number }>() {
44
68
  _state.set({ value: [], loading: false });
45
69
  },
46
70
 
47
- // Selectors
48
- getById: (id: T['id']) =>
49
- computed(() => _state().value.find((i) => i.id === id)),
71
+ // --- Selectors ---
72
+ getByKey: (key: T[K]) => computed(() => mapByKey().get(key)),
50
73
 
51
74
  all: computed(() => _state().value),
52
75
  count: computed(() => _state().value.length),
53
76
 
54
- // Async Loader
77
+ // --- Async Loader ---
55
78
  async load(fetchFn: () => Promise<T[]>) {
56
- _state.update((curr) => ({ ...curr, loading: true, error: undefined }));
79
+ _state.update((curr) => ({
80
+ ...curr,
81
+ loading: true,
82
+ error: undefined,
83
+ }));
57
84
  try {
58
85
  const data = await fetchFn();
59
86
  _state.set({ value: data, loading: false });
87
+ return data;
60
88
  } catch (err: any) {
61
89
  _state.update((curr) => ({
62
90
  ...curr,
63
91
  loading: false,
64
- error: err.message ?? 'Unknown error'
92
+ error: err.message ?? 'Unknown error',
65
93
  }));
94
+ return [];
66
95
  }
67
- }
96
+ },
68
97
  };
69
98
  }
@@ -39,12 +39,14 @@ export function singleStoreFactory<T>() {
39
39
  try {
40
40
  const data = await fetchFn();
41
41
  _state.set({ value: data, loading: false });
42
+ return data;
42
43
  } catch (err: any) {
43
44
  _state.update((curr) => ({
44
45
  ...curr,
45
46
  loading: false,
46
47
  error: err.message ?? 'Unknown error'
47
48
  }));
49
+ return Promise.reject(err);
48
50
  }
49
51
  }
50
52
  };
@@ -0,0 +1,43 @@
1
+ import { inject } from '@angular/core';
2
+ import { CanActivateFn, Router } from '@angular/router';
3
+ import { AuthService } from '@api/auth/auth.service';
4
+ import { HelperService } from '@core/services/helper.service';
5
+ import { StorageMap } from '@ngx-pwa/local-storage';
6
+ import { logger } from '@shared/utils/logger';
7
+ import { lastValueFrom, map } from 'rxjs';
8
+ import { environment } from 'src/environments/environment';
9
+
10
+ export const authGuard: CanActivateFn = (route, state) => {
11
+ const router = inject(Router);
12
+ const storage = inject(StorageMap);
13
+ const auth = inject(AuthService);
14
+
15
+ return storage.get(environment.tokenName).pipe(
16
+ map((token: any) => {
17
+ if (token) {
18
+ auth.setLogin(true);
19
+
20
+ // logger.log('Token', token);
21
+
22
+
23
+
24
+ // router.navigateByUrl('/');
25
+ // helper.showPopup(
26
+ // 'error',
27
+ // 'Maaf',
28
+ // 'Mohon untuk logout akun terlebih dahulu di browser ini!'
29
+ // );
30
+ return true;
31
+ } else {
32
+ auth.setLogin(false);
33
+ lastValueFrom(storage.clear());
34
+ router.navigateByUrl('/login');
35
+ setTimeout(() => {
36
+ window.location.reload();
37
+ }, 2000);
38
+ return false;
39
+
40
+ }
41
+ })
42
+ );
43
+ };