unictive-angular 1.0.27 → 1.0.29

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 (38) hide show
  1. package/bin/utils/resource/src/app/API/README.md +10 -0
  2. package/bin/utils/resource/src/app/MODULES/authentication/login/login.component.html +20 -0
  3. package/bin/utils/resource/src/app/MODULES/authentication/login/login.component.scss +0 -0
  4. package/bin/utils/resource/src/app/MODULES/authentication/login/login.component.ts +35 -0
  5. package/bin/utils/resource/src/app/MODULES/authentication/login/login.module.ts +19 -0
  6. package/bin/utils/resource/src/app/MODULES/authentication/page404/page404.component.html +3 -0
  7. package/bin/utils/resource/src/app/MODULES/authentication/page404/page404.component.scss +0 -0
  8. package/bin/utils/resource/src/app/MODULES/authentication/page404/page404.component.ts +16 -0
  9. package/bin/utils/resource/src/app/MODULES/authentication/page404/page404.module.ts +17 -0
  10. package/bin/utils/resource/src/app/MODULES/authentication/register/register.component.html +3 -0
  11. package/bin/utils/resource/src/app/MODULES/authentication/register/register.component.scss +0 -0
  12. package/bin/utils/resource/src/app/MODULES/authentication/register/register.component.ts +16 -0
  13. package/bin/utils/resource/src/app/MODULES/authentication/register/register.module.ts +18 -0
  14. package/bin/utils/resource/src/app/MODULES/home/home.component.html +4 -0
  15. package/bin/utils/resource/src/app/MODULES/home/home.component.scss +0 -0
  16. package/bin/utils/resource/src/app/MODULES/home/home.component.ts +16 -0
  17. package/bin/utils/resource/src/app/MODULES/home/home.module.ts +19 -0
  18. package/bin/utils/resource/src/app/app.html +0 -262
  19. package/bin/utils/resource/src/app/app.routes.ts +20 -1
  20. package/bin/utils/resource/src/app/core/config/entity-store.ts +69 -0
  21. package/bin/utils/resource/src/app/core/config/single-store.ts +51 -0
  22. package/bin/utils/resource/src/app/core/models/entities/screen.entity.spec.ts +41 -0
  23. package/bin/utils/resource/src/app/core/models/entities/screen.entity.ts +16 -0
  24. package/bin/utils/resource/src/app/core/respositories/screen_repo/screen.repo.ts +19 -0
  25. package/bin/utils/resource/src/app/core/services/helper.service.ts +238 -0
  26. package/bin/utils/resource/src/app/core/services/listener.service.ts +15 -0
  27. package/bin/utils/resource/src/app/shared/libs/library.module.ts +14 -0
  28. package/bin/utils/resource/src/app/shared/ui/input-text/input-text.component.scss +0 -0
  29. package/bin/utils/resource/src/app/shared/ui/input-text/input-text.component.ts +64 -0
  30. package/bin/utils/resource/src/app/shared/utils/validator_pattern.ts +25 -0
  31. package/bin/utils/resource/src/app/shared/widgets/alert-error-input/alert-error-input.component.html +94 -0
  32. package/bin/utils/resource/src/app/shared/widgets/alert-error-input/alert-error-input.component.ts +24 -0
  33. package/bin/utils/resource/src/app/shared/widgets/widget_game/widget_game.component.html +20 -0
  34. package/bin/utils/resource/src/app/shared/widgets/widget_game/widget_game.component.scss +0 -0
  35. package/bin/utils/resource/src/app/shared/widgets/widget_game/widget_game.component.ts +65 -0
  36. package/bin/utils/resource/src/app/shared/widgets/widget_game/widget_game.module.ts +12 -0
  37. package/index.js +1 -1
  38. package/package.json +1 -1
@@ -0,0 +1,41 @@
1
+ import { RepoScreen } from '@core/respository/screen_repo/screen.repo';
2
+ import { ScreenEntity, Screen_m } from './screen.entity';
3
+ import { provideZonelessChangeDetection } from '@angular/core';
4
+ import { TestBed } from '@angular/core/testing';
5
+ import { HttpClientTestingModule } from '@angular/common/http/testing';
6
+
7
+
8
+ describe('ScreenEntity', () => {
9
+ let repo: RepoScreen;
10
+
11
+ const mockScreen: Screen_m = {
12
+ timestamp: Date.now(),
13
+ type: 'test',
14
+ id: 123,
15
+ name: 'Mock Screen',
16
+ mode: 'demo',
17
+ time: 60000
18
+
19
+ };
20
+
21
+ beforeEach(async () => {
22
+ await TestBed.configureTestingModule({
23
+ imports: [HttpClientTestingModule],
24
+ providers: [provideZonelessChangeDetection()]
25
+ }).compileComponents();
26
+
27
+ repo = TestBed.inject(RepoScreen);
28
+ });
29
+
30
+ it('should set and get screen', () => {
31
+ repo.setData(mockScreen);
32
+ expect(repo.getData.value).toEqual(mockScreen);
33
+ });
34
+
35
+ it('should update state when entity is set again', () => {
36
+ const newMock: Screen_m = { ...mockScreen, id: 456, name: 'New Mock' };
37
+ repo.setData(newMock);
38
+ expect(repo.getData.value?.id).toBe(456);
39
+ expect(repo.getData.value?.name).toBe('New Mock');
40
+ });
41
+ });
@@ -0,0 +1,16 @@
1
+ // import { entityStoreFactory } from "@core/config/entity-store";
2
+ import { singleStoreFactory } from "@core/config/single-store";
3
+
4
+
5
+
6
+ export interface Screen_m {
7
+ timestamp: number;
8
+ type: string;
9
+ id: number;
10
+ name: string;
11
+ mode: string;
12
+ time: number;
13
+ }
14
+
15
+
16
+ export const ScreenEntity = singleStoreFactory<Screen_m>();
@@ -0,0 +1,19 @@
1
+ import { Injectable } from '@angular/core';
2
+ import { HttpClient } from '@angular/common/http';
3
+ import { ScreenEntity } from '@core/models/entities/screen.entity';
4
+ import { logger } from '@shared/utils/logger';
5
+
6
+ @Injectable({ providedIn: 'root' })
7
+ export class RepoScreen {
8
+
9
+
10
+ constructor() { }
11
+
12
+ setData(screen: any) {
13
+ ScreenEntity.set(screen);
14
+ }
15
+
16
+ get getData() {
17
+ return ScreenEntity.state();
18
+ }
19
+ }
@@ -0,0 +1,238 @@
1
+ import { computed, Injectable, signal } from '@angular/core';
2
+ import { StorageMap } from '@ngx-pwa/local-storage';
3
+ import { ListenerService } from './listener.service';
4
+ import { lastValueFrom } from 'rxjs';
5
+ import { Router } from '@angular/router';
6
+ import Swal from 'sweetalert2';
7
+ import { ValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms';
8
+
9
+ export interface Popup_m {
10
+ type:
11
+ | 'delete'
12
+ | 'success'
13
+ | 'error'
14
+ | 'warning'
15
+ | 'info'
16
+ | 'confirm'
17
+ | 'suspend'
18
+ | 'change'
19
+ | 'send'
20
+ | 'restore';
21
+ icon?: string;
22
+ title: string;
23
+ message: string;
24
+ button: string;
25
+ button_cancel?: string;
26
+ showCancel?: boolean;
27
+ }
28
+
29
+
30
+ @Injectable({
31
+ providedIn: 'root'
32
+ })
33
+ export class HelperService {
34
+
35
+ private width = signal(window.innerWidth);
36
+ screenWidth = this.width.asReadonly();
37
+
38
+ constructor(
39
+ private storage: StorageMap,
40
+ private listener: ListenerService,
41
+ private router: Router
42
+ ) {
43
+ window.addEventListener('resize', () => {
44
+ this.width.set(window.innerWidth);
45
+ });
46
+ }
47
+
48
+ screenSize = computed<'mobile' | 'tablet' | 'desktop'>(() => {
49
+ const w = this.width();
50
+ if (w < 768) return 'mobile';
51
+ if (w < 1024) return 'tablet';
52
+ return 'desktop';
53
+ });
54
+
55
+
56
+ getTimeInZone(timeZone: any) {
57
+ const options: any = {
58
+ timeZone: timeZone,
59
+ year: 'numeric',
60
+ month: '2-digit',
61
+ day: '2-digit',
62
+ hour: '2-digit',
63
+ minute: '2-digit',
64
+ second: '2-digit',
65
+ hour12: false,
66
+ };
67
+ const date = new Date();
68
+ const formatter = new Intl.DateTimeFormat('en-US', options);
69
+ const parts = formatter.formatToParts(date);
70
+ const dateParts: any = {};
71
+ parts.forEach((part) => {
72
+ if (part.type !== 'literal') {
73
+ dateParts[part.type] = part.value;
74
+ }
75
+ });
76
+
77
+ return `${dateParts.year}-${dateParts.month}-${dateParts.day} ${dateParts.hour}:${dateParts.minute}:${dateParts.second}`;
78
+ }
79
+
80
+ //Fix time global
81
+ getTimeNow() {
82
+ // Waktu di zona waktu Indonesia Barat (WIB)
83
+ const waktuWIB = this.getTimeInZone('Asia/Jakarta');
84
+
85
+ //console.log(waktuWIB);
86
+
87
+ // Waktu di zona waktu Indonesia Tengah (WITA)
88
+ const waktuWITA = this.getTimeInZone('Asia/Makassar');
89
+ //console.log('Waktu WITA (Asia/Makassar):', waktuWITA);
90
+
91
+ // Waktu di zona waktu Indonesia Timur (WIT)
92
+ const waktuWIT = this.getTimeInZone('Asia/Jayapura');
93
+ //console.log('Waktu WIT (Asia/Jayapura):', waktuWIT);
94
+
95
+ return new Date(waktuWIB);
96
+ }
97
+
98
+ /**
99
+ * Realtime screen size detection
100
+ * cara menggunakan isMobile(), isTablet(), isDesktop()
101
+ */
102
+ isMobile = computed(() => this.screenSize() === 'mobile');
103
+ isTablet = computed(() => this.screenSize() === 'tablet');
104
+ isDesktop = computed(() => this.screenSize() === 'desktop');
105
+
106
+ /**
107
+ * Download file from Blob data.
108
+ * @param data - Blob data of the file.
109
+ * @param filename - Name of the file to be downloaded.
110
+ */
111
+ //MARK: DOWNLOAD FILE
112
+ downloadFile(data: any, filename: string) {
113
+ const url = window.URL.createObjectURL(data);
114
+ const a = document.createElement('a');
115
+ a.href = url;
116
+ a.download = filename;
117
+ a.click();
118
+ window.URL.revokeObjectURL(url);
119
+ }
120
+
121
+ //MARK: STORAGE
122
+ setStorage(key: string, value: any) {
123
+ return lastValueFrom(this.storage.set(key, value));
124
+ }
125
+
126
+ getStorage(key: string) {
127
+ return lastValueFrom(this.storage.get(key));
128
+ }
129
+
130
+ removeStorage(key: string) {
131
+ return lastValueFrom(this.storage.delete(key));
132
+ }
133
+
134
+ /**
135
+ * Menampilkan file dalam tab baru atau sebagai iframe.
136
+ * @param file - File atau URL string yang akan ditampilkan.
137
+ */
138
+ //MARK: SHOW FILE
139
+ showFile(file: File | string) {
140
+ if (typeof file === 'string') {
141
+ const newTab = window.open(file, '_blank');
142
+ if (!newTab) {
143
+ this.showPopup('error', 'Maaf', 'Unable to open file.');
144
+ }
145
+ } else {
146
+ let reader = new FileReader();
147
+ reader.readAsDataURL(file);
148
+ reader.onload = (e) => {
149
+ if (e.target?.result) {
150
+ const newTab = window.open();
151
+ if (newTab) {
152
+ newTab.document.body.innerHTML = `<iframe src="${e.target.result}" style="width:100%; height:100%;" frameborder="0"></iframe>`;
153
+ }
154
+ }
155
+ };
156
+ }
157
+ }
158
+
159
+ async showLoading(message?: any) {
160
+ return await this.listener.$loading.set({ load: true, message: message });
161
+ }
162
+
163
+ dismissLoading() {
164
+ this.listener.$loading.set({ load: false, message: null });
165
+ }
166
+
167
+ routerLink(link: string) {
168
+ this.router.navigateByUrl(link);
169
+ }
170
+
171
+ //--------------------------- POP UP-------------------------//
172
+ showPopup(icon: any, title: any, msg: any, timer: boolean = false) {
173
+ Swal.fire({
174
+ title: title,
175
+ html: msg,
176
+ timer: timer ? 3000 : 0,
177
+ icon: icon,
178
+ allowOutsideClick: false,
179
+ heightAuto: false,
180
+ allowEscapeKey: false,
181
+ });
182
+ }
183
+
184
+ dismissPopup() {
185
+ Swal.close();
186
+ }
187
+
188
+ async confirmationAlert(data: Popup_m): Promise<boolean> {
189
+ switch (data.type) {
190
+ case 'delete':
191
+ data.icon = 'icon-delete.svg';
192
+ break;
193
+ case 'success':
194
+ data.icon = 'icon-success.svg';
195
+ break;
196
+ case 'confirm':
197
+ data.icon = 'icon-confirm.svg';
198
+ break;
199
+ case 'suspend':
200
+ data.icon = 'icon-suspend.svg';
201
+ break;
202
+ case 'change':
203
+ data.icon = 'icon-edit.svg';
204
+ break;
205
+ case 'send':
206
+ data.icon = 'icon-send.png';
207
+ break;
208
+ case 'restore':
209
+ data.icon = 'icon-restore.png';
210
+ break;
211
+ default:
212
+ break;
213
+ }
214
+
215
+ return Swal.fire({
216
+ html: `
217
+ <h4 class="text-[18px] lg:text-[24px] mb-[16px] text-[#272727]">${data.title}</h4>
218
+ <div class="!text-[#272727] text-[16px] leading-[1.2]">${data.message}</div>
219
+ `,
220
+
221
+ showCancelButton: data?.showCancel ?? true,
222
+ confirmButtonText: data.button,
223
+ cancelButtonText: data?.button_cancel ? data.button_cancel : 'Batal',
224
+ reverseButtons: true,
225
+ confirmButtonColor: data.type == 'delete' ? '#D12114' : '#0072B9',
226
+ customClass: {
227
+ cancelButton: 'custom-cancel-button',
228
+ },
229
+ }).then((result) => result.isConfirmed);
230
+ }
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+ }
@@ -0,0 +1,15 @@
1
+ import { Injectable, signal } from '@angular/core';
2
+
3
+ @Injectable({
4
+ providedIn: 'root'
5
+ })
6
+ export class ListenerService {
7
+
8
+ $loading: any = signal<{ load: boolean; message?: any }>({
9
+ load: false,
10
+ message: null,
11
+ });
12
+
13
+ constructor() { }
14
+
15
+ }
@@ -0,0 +1,14 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
4
+ import { InputTextComponent } from '@shared/ui/input-text/input-text.component';
5
+
6
+ @NgModule({
7
+ imports: [
8
+ ReactiveFormsModule,
9
+ FormsModule,
10
+ InputTextComponent
11
+ ],
12
+ exports: [ReactiveFormsModule, FormsModule, InputTextComponent],
13
+ })
14
+ export class LibraryModule { }
@@ -0,0 +1,64 @@
1
+ import { Component, forwardRef, input, Input, OnInit } from '@angular/core';
2
+ import { ControlValueAccessor, FormControl, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
3
+ import { AlertErrorInputComponent } from '@shared/widgets/alert-error-input/alert-error-input.component';
4
+
5
+ @Component({
6
+ selector: 'input-text',
7
+ styleUrls: ['./input-text.component.scss'],
8
+ standalone: true,
9
+ imports: [ReactiveFormsModule, FormsModule, AlertErrorInputComponent],
10
+ providers: [
11
+ {
12
+ provide: NG_VALUE_ACCESSOR,
13
+ useExisting: forwardRef(() => InputTextComponent),
14
+ multi: true,
15
+ },
16
+ ],
17
+ template: `
18
+ <input (ngModelChange)="onValueChange($event)" [(ngModel)]="value" type="email"
19
+ class="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
20
+ [placeholder]="placeholder()" />
21
+ <alert-error [control]="control" [label]="label()"></alert-error>
22
+ `
23
+ })
24
+ export class InputTextComponent implements ControlValueAccessor, OnInit {
25
+
26
+ @Input() control!: FormControl | any;
27
+ placeholder = input('Enter your email'); //default
28
+ label = input('label'); //default
29
+
30
+ value: any;
31
+
32
+ constructor() { }
33
+
34
+ ngOnInit() {
35
+ }
36
+
37
+ writeValue(value: any): void {
38
+ }
39
+
40
+ registerOnChange(fn: any): void {
41
+ this.onChange = fn;
42
+ }
43
+
44
+ registerOnTouched(fn: any): void {
45
+ this.onTouched = fn;
46
+ }
47
+
48
+ hasError(error: string): boolean | null {
49
+ // console.log(this.control.errors);
50
+ return (
51
+ this.control?.hasError(error) &&
52
+ (this.control?.touched || this.control?.dirty)
53
+ );
54
+ }
55
+
56
+ onValueChange(value: any) {
57
+ this.onChange(value);
58
+ }
59
+
60
+ // Fungsi yang akan dipanggil ketika nilai diubah
61
+ onChange: (date: any) => void = () => { };
62
+ onTouched: () => void = () => { };
63
+
64
+ }
@@ -0,0 +1,25 @@
1
+ import { ValidatorFn, AbstractControl, ValidationErrors } from "@angular/forms";
2
+
3
+ function emailPatternValidator(): ValidatorFn {
4
+ return (control: AbstractControl): ValidationErrors | null => {
5
+ const value = control.value;
6
+ if (!value) return null;
7
+
8
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
9
+ return emailRegex.test(value) ? null : { invalidEmailPattern: true };
10
+ };
11
+ }
12
+
13
+ function linkPatternValidator(): ValidatorFn {
14
+ return (control: AbstractControl): ValidationErrors | null => {
15
+ const value = control.value;
16
+ if (!value) return null;
17
+
18
+ const regex =
19
+ /^https:\/\/([\da-z.-]+)\.([a-z.]{2,6})([\/\w .-]*)*\/?(\?.*)?$/;
20
+ return regex.test(value) ? null : { invalidLinkPattern: true };
21
+ };
22
+ }
23
+
24
+ export { emailPatternValidator, linkPatternValidator };
25
+
@@ -0,0 +1,94 @@
1
+ @if (hasError('required')) {
2
+ <small class="text-[#EB5757]">
3
+ <div class="w-full my-2 bg-red-100 rounded-[4px] px-[12px] py-[4px]">
4
+ {{label}} diperlukan
5
+ </div>
6
+ </small>
7
+ }
8
+
9
+ @if (hasError('invalidLinkPattern')) {
10
+ <small class="text-[#eb5757]">
11
+ Format {{label}} salah!, Contoh: https://gmeet.google.com/
12
+ </small>
13
+ }
14
+
15
+ @if (hasError('invalidEmailPattern')) {
16
+ <small class="text-[#eb5757]">
17
+ Format {{label}} salah!
18
+ </small>
19
+ }
20
+
21
+
22
+ @if (hasError('dateRange')) {
23
+ <small class="text-[#eb5757]">
24
+ {{control?.errors['dateRange']}}
25
+ </small>
26
+ }
27
+
28
+ @if (hasError('totalPriceExceed')) {
29
+ <small class="text-[#eb5757]">
30
+ Total tidak boleh lebih besar dari Total Negosiasi
31
+ </small>
32
+ }
33
+
34
+ @if (hasError('minlength')) {
35
+ <small class="text-[#eb5757]">
36
+ {{label}} Minimal {{ control?.errors?.minlength?.requiredLength }}
37
+ Error
38
+ </small>
39
+ }
40
+
41
+ @if (hasError('maxlength')) {
42
+ <small class="text-[#eb5757]">
43
+ <div class="w-full my-2 bg-red-100 rounded-[4px] px-[12px] py-[4px]">
44
+ {{label}} Maksimal {{ control?.errors?.maxlength?.requiredLength }}
45
+ Karakter
46
+ </div>
47
+ </small>
48
+ }
49
+
50
+ @if (hasError('min')) {
51
+ <small class="text-[#eb5757]">
52
+ <div class="w-full my-2 bg-red-100 rounded-[4px] px-[12px] py-[4px]">
53
+ {{label}} Minimal {{ control?.errors?.min?.min }}
54
+ </div>
55
+ </small>
56
+ }
57
+
58
+ @if (hasError('max')) {
59
+ <small class="text-[#eb5757]">
60
+ <div class="w-full my-2 bg-red-100 rounded-[4px] px-[12px] py-[4px]">
61
+ {{label}} Maksimal {{ control?.errors?.max?.max }}
62
+ </div>
63
+ </small>
64
+ }
65
+
66
+ @if (hasError('mismatch')) {
67
+ <small class="text-[#eb5757]">
68
+ tidak sama
69
+ </small>
70
+ }
71
+
72
+ @if (hasError('passwordSameAsCurrent')) {
73
+ <small class="text-[#eb5757]">
74
+ <div class="w-full my-2 bg-red-100 rounded-[4px] px-[12px] py-[4px]">
75
+ Password baru tidak boleh sama dengan password lama
76
+ </div>
77
+ </small>
78
+ }
79
+
80
+ @if (hasError('passwordMismatch')) {
81
+ <small class="text-[#eb5757]">
82
+ <div class="w-full my-2 bg-red-100 rounded-[4px] px-[12px] py-[4px]">
83
+ Password baru dan konfirmasi password baru tidak sama
84
+ </div>
85
+ </small>
86
+ }
87
+
88
+ @if (hasError('strongPassword')) {
89
+ <small class="text-[#eb5757]">
90
+ <div class="w-full my-2 bg-red-100 rounded-[4px] px-[12px] py-[4px]">
91
+ Password harus mengandung minimal 8 karakter, huruf besar, huruf kecil, angka, dan karakter spesial.
92
+ </div>
93
+ </small>
94
+ }
@@ -0,0 +1,24 @@
1
+ import { Component, Input, OnInit } from '@angular/core';
2
+ import { FormControl } from '@angular/forms';
3
+
4
+ @Component({
5
+ selector: 'alert-error',
6
+ templateUrl: './alert-error-input.component.html',
7
+ standalone: true,
8
+ })
9
+ export class AlertErrorInputComponent implements OnInit {
10
+ @Input() control!: FormControl | any;
11
+ @Input() label!: string;
12
+ @Input() secondLabel!: string;
13
+ constructor() { }
14
+
15
+ ngOnInit() { }
16
+
17
+ hasError(error: string): boolean {
18
+ //console.log(error, this.control.errors);
19
+ return (
20
+ this.control?.hasError(error) &&
21
+ (this.control?.touched || this.control?.dirty)
22
+ );
23
+ }
24
+ }
@@ -0,0 +1,20 @@
1
+ <div class="fixed inset-0 bgs z-50 flex items-center justify-center">
2
+ <div class="absolute transition-all duration-100 ease-in-out transform hover:scale-110"
3
+ style="padding: 50px; box-sizing: content-box;"
4
+ [ngStyle]="{ top: boxPosition.top, left: boxPosition.left, position: 'absolute' }" (mouseenter)="onMouseEnterBox()">
5
+
6
+ <div (click)="onLogout()"
7
+ class=" w-40 h-20 bg-black text-white flex items-center text-center justify-center cursor-pointer rounded-xl shadow-lg">
8
+ Tangkap
9
+ </div>
10
+ </div>
11
+
12
+ <div class="absolute top-5 right-5 text-white text-xl space-y-2 text-right bg-amber-900 p-2 rounded-2xl">
13
+ <div>Waktu: {{ timer }} detik</div>
14
+ <div>Skor: {{ score }}</div>
15
+ </div>
16
+
17
+ <div *ngIf="gameEnded" class="absolute bottom-10 text-white text-2xl font-bold">
18
+ Permainan Selesai! Skor akhir: {{ score }}
19
+ </div>
20
+ </div>
@@ -0,0 +1,65 @@
1
+ import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'app-widget_game',
5
+ templateUrl: './widget_game.component.html',
6
+ styleUrls: ['./widget_game.component.scss'],
7
+ standalone: false
8
+ })
9
+ export class Widget_gameComponent implements OnInit {
10
+
11
+ boxPosition = { top: '50%', left: '50%' };
12
+ gameStarted = false;
13
+ gameEnded = false;
14
+ timer = 60;
15
+ score = 0;
16
+ intervalId: any;
17
+ constructor(
18
+ private cdr: ChangeDetectorRef
19
+ ) { }
20
+
21
+ ngOnInit() {
22
+ this.startGame();
23
+ }
24
+ startGame() {
25
+ this.gameStarted = true;
26
+ this.timer = 60;
27
+ this.score = 0;
28
+ this.gameEnded = false;
29
+ this.moveBoxRandomly();
30
+
31
+ this.intervalId = setInterval(() => {
32
+ this.timer--;
33
+ if (this.timer <= 0) {
34
+ clearInterval(this.intervalId);
35
+ this.gameEnded = true;
36
+ }
37
+ this.cdr.detectChanges();
38
+ }, 1000);
39
+ }
40
+
41
+ onMouseEnterBox() {
42
+ if (!this.gameEnded) {
43
+ this.score++;
44
+ this.moveBoxRandomly();
45
+ // this.cdr.detectChanges();
46
+
47
+ }
48
+ }
49
+
50
+ moveBoxRandomly() {
51
+ const top = Math.random() * 90;
52
+ const left = Math.random() * 90;
53
+ this.boxPosition = {
54
+ top: `${top}%`,
55
+ left: `${left}%`,
56
+ };
57
+ this.cdr.detectChanges();
58
+
59
+ }
60
+
61
+ onLogout() {
62
+ // this.auth.onLogout();
63
+ }
64
+
65
+ }
@@ -0,0 +1,12 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { Widget_gameComponent } from './widget_game.component';
4
+
5
+ @NgModule({
6
+ imports: [
7
+ CommonModule
8
+ ],
9
+ exports: [Widget_gameComponent],
10
+ declarations: [Widget_gameComponent]
11
+ })
12
+ export class Widget_gameModule { }
package/index.js CHANGED
@@ -22,7 +22,7 @@ const updateNotifier = require('update-notifier');
22
22
  const chalk = require('chalk');
23
23
 
24
24
 
25
- require('@dotenvx/dotenvx').config({ path: path.resolve(__dirname, ".env") })
25
+ require('@dotenvx/dotenvx').config({ path: path.resolve(__dirname, ".env"), silent: true });
26
26
 
27
27
  const { login, logout } = require("./bin/utils/auth");
28
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unictive-angular",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
4
4
  "description": "CLI untuk membuat project Angular dengan struktur kustom (Modules, Core, API, Shared).",
5
5
  "scripts": {
6
6
  "build": "npm version patch && npm publish --access public"