style: format code
This commit is contained in:
		
					parent
					
						
							
								b2053acdfe
							
						
					
				
			
			
				commit
				
					
						f2aa81b6d2
					
				
			
		
					 5 changed files with 274 additions and 221 deletions
				
			
		|  | @ -63,7 +63,7 @@ export const routes: Routes = [ | |||
|   }, | ||||
|   { | ||||
|     path: 'game/dice', | ||||
|     loadComponent: () => import('./feature/game/dice/dice.component').then(m => m.DiceComponent), | ||||
|     loadComponent: () => import('./feature/game/dice/dice.component').then((m) => m.DiceComponent), | ||||
|     canActivate: [authGuard], | ||||
|   }, | ||||
| ]; | ||||
|  |  | |||
|  | @ -1,74 +1,121 @@ | |||
| <div class="container mx-auto px-4 py-8 space-y-8"> | ||||
|   <h1 class="text-3xl font-bold text-white mb-6">Dice Game</h1> | ||||
| 
 | ||||
|   <div class="grid grid-cols-1 lg:grid-cols-4 gap-8"> | ||||
|     <div class="lg:col-span-1 card p-8 space-y-6"> | ||||
|       <form [formGroup]="diceForm" (ngSubmit)="roll()" class="space-y-6"> | ||||
|         <div class="controls space-y-4"> | ||||
|           <div> | ||||
|             <label for="betAmount" class="block text-text-secondary mb-2">Bet Amount:</label> | ||||
|             <input id="betAmount" type="number" formControlName="betAmount" min="0.01" step="0.01" | ||||
|                    class="w-full bg-deep-blue-light text-white rounded-lg p-2 focus:outline-none focus:ring-1 focus:ring-emerald"> | ||||
|             @if (hasError('betAmount', 'required')) { | ||||
|               <span class="text-accent-red text-sm mt-1 block">Bet Amount is required</span> | ||||
|             } | ||||
|             @if (hasError('betAmount', 'min')) { | ||||
|               <span class="text-accent-red text-sm mt-1 block">Bet Amount must be at least 0.01</span> | ||||
|             } | ||||
|           </div> | ||||
| 
 | ||||
|           <div> | ||||
|             <label class="block text-text-secondary mb-2">Roll Mode:</label> | ||||
|             <div class="roll-mode flex rounded-lg overflow-hidden"> | ||||
|               <button type="button" (click)="toggleRollMode()" [ngClass]="{'bg-emerald text-white': diceForm.get('rollOver')?.value, 'bg-deep-blue-light text-text-secondary': !diceForm.get('rollOver')?.value}" | ||||
|                       class="flex-1 py-2 text-center font-semibold transition-colors duration-200">Roll Over</button> | ||||
|               <button type="button" (click)="toggleRollMode()" [ngClass]="{'bg-emerald text-white': !diceForm.get('rollOver')?.value, 'bg-deep-blue-light text-text-secondary': diceForm.get('rollOver')?.value}" | ||||
|                       class="flex-1 py-2 text-center font-semibold transition-colors duration-200">Roll Under</button> | ||||
|             </div> | ||||
|           </div> | ||||
| 
 | ||||
|           <div> | ||||
|             <label for="targetValue" class="block text-text-secondary mb-2">Target Value: {{ diceForm.get('targetValue')?.value | number:'1.0-2' }}</label> | ||||
|             <input id="targetValue" type="range" formControlName="targetValue" min="1" max="100" step="0.01" | ||||
|                    class="w-full h-2 bg-deep-blue-light rounded-lg appearance-none cursor-pointer range-lg accent-emerald"> | ||||
|              @if (hasError('targetValue', 'required')) { | ||||
|               <span class="text-accent-red text-sm mt-1 block">Target Value is required</span> | ||||
|             } | ||||
|             @if (hasError('targetValue', 'min')) { | ||||
|               <span class="text-accent-red text-sm mt-1 block">Target Value must be at least 1</span> | ||||
|             } | ||||
|              @if (hasError('targetValue', 'max')) { | ||||
|               <span class="text-accent-red text-sm mt-1 block">Target Value must be at most 100</span> | ||||
|             } | ||||
|           </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="info space-y-2 text-text-secondary"> | ||||
|           <p>Win Chance: <span class="text-white">{{ winChance() | number:'1.0-2' }}%</span></p> | ||||
|           <p>Potential Win: <span class="text-white">{{ potentialWin() | currency:'EUR':'symbol':'1.2-2' }}</span></p> | ||||
|         </div> | ||||
| 
 | ||||
|         <button type="submit" class="button-primary w-full py-2 font-bold">Roll Dice</button> | ||||
|       </form> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="lg:col-span-3 card p-8 flex items-center justify-center"> | ||||
|       @if (rolledValue() !== null) { | ||||
|         <div class="text-white text-center text-8xl font-bold"> | ||||
|           {{ rolledValue() }} | ||||
|         </div> | ||||
|       } | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
| 
 | ||||
|   @if (rolledValue() !== null) { | ||||
|     <div class="result max-w-sm mx-auto card p-6 mt-8 text-center"> | ||||
|       @if (win()) { | ||||
|         <p class="text-emerald text-base font-semibold">You Won! Payout: {{ payout() | currency:'EUR':'symbol':'1.2-2' }}</p> | ||||
|       } @else { | ||||
|         <p class="text-accent-red text-base font-semibold">You Lost.</p> | ||||
|       } | ||||
|     </div> | ||||
|   } | ||||
| </div> | ||||
| <div class="container mx-auto px-4 py-8 space-y-8"> | ||||
|   <h1 class="text-3xl font-bold text-white mb-6">Dice Game</h1> | ||||
| 
 | ||||
|   <div class="grid grid-cols-1 lg:grid-cols-4 gap-8"> | ||||
|     <div class="lg:col-span-1 card p-8 space-y-6"> | ||||
|       <form [formGroup]="diceForm" (ngSubmit)="roll()" class="space-y-6"> | ||||
|         <div class="controls space-y-4"> | ||||
|           <div> | ||||
|             <label for="betAmount" class="block text-text-secondary mb-2">Bet Amount:</label> | ||||
|             <input | ||||
|               id="betAmount" | ||||
|               type="number" | ||||
|               formControlName="betAmount" | ||||
|               min="0.01" | ||||
|               step="0.01" | ||||
|               class="w-full bg-deep-blue-light text-white rounded-lg p-2 focus:outline-none focus:ring-1 focus:ring-emerald" | ||||
|             /> | ||||
|             @if (hasError('betAmount', 'required')) { | ||||
|               <span class="text-accent-red text-sm mt-1 block">Bet Amount is required</span> | ||||
|             } | ||||
|             @if (hasError('betAmount', 'min')) { | ||||
|               <span class="text-accent-red text-sm mt-1 block" | ||||
|                 >Bet Amount must be at least 0.01</span | ||||
|               > | ||||
|             } | ||||
|           </div> | ||||
| 
 | ||||
|           <div> | ||||
|             <label class="block text-text-secondary mb-2">Roll Mode:</label> | ||||
|             <div class="roll-mode flex rounded-lg overflow-hidden"> | ||||
|               <button | ||||
|                 type="button" | ||||
|                 (click)="toggleRollMode()" | ||||
|                 [ngClass]="{ | ||||
|                   'bg-emerald text-white': diceForm.get('rollOver')?.value, | ||||
|                   'bg-deep-blue-light text-text-secondary': !diceForm.get('rollOver')?.value, | ||||
|                 }" | ||||
|                 class="flex-1 py-2 text-center font-semibold transition-colors duration-200" | ||||
|               > | ||||
|                 Roll Over | ||||
|               </button> | ||||
|               <button | ||||
|                 type="button" | ||||
|                 (click)="toggleRollMode()" | ||||
|                 [ngClass]="{ | ||||
|                   'bg-emerald text-white': !diceForm.get('rollOver')?.value, | ||||
|                   'bg-deep-blue-light text-text-secondary': diceForm.get('rollOver')?.value, | ||||
|                 }" | ||||
|                 class="flex-1 py-2 text-center font-semibold transition-colors duration-200" | ||||
|               > | ||||
|                 Roll Under | ||||
|               </button> | ||||
|             </div> | ||||
|           </div> | ||||
| 
 | ||||
|           <div> | ||||
|             <label for="targetValue" class="block text-text-secondary mb-2" | ||||
|               >Target Value: {{ diceForm.get('targetValue')?.value | number: '1.0-2' }}</label | ||||
|             > | ||||
|             <input | ||||
|               id="targetValue" | ||||
|               type="range" | ||||
|               formControlName="targetValue" | ||||
|               min="1" | ||||
|               max="100" | ||||
|               step="0.01" | ||||
|               class="w-full h-2 bg-deep-blue-light rounded-lg appearance-none cursor-pointer range-lg accent-emerald" | ||||
|             /> | ||||
|             @if (hasError('targetValue', 'required')) { | ||||
|               <span class="text-accent-red text-sm mt-1 block">Target Value is required</span> | ||||
|             } | ||||
|             @if (hasError('targetValue', 'min')) { | ||||
|               <span class="text-accent-red text-sm mt-1 block" | ||||
|                 >Target Value must be at least 1</span | ||||
|               > | ||||
|             } | ||||
|             @if (hasError('targetValue', 'max')) { | ||||
|               <span class="text-accent-red text-sm mt-1 block" | ||||
|                 >Target Value must be at most 100</span | ||||
|               > | ||||
|             } | ||||
|           </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="info space-y-2 text-text-secondary"> | ||||
|           <p> | ||||
|             Win Chance: <span class="text-white">{{ winChance() | number: '1.0-2' }}%</span> | ||||
|           </p> | ||||
|           <p> | ||||
|             Potential Win: | ||||
|             <span class="text-white">{{ | ||||
|               potentialWin() | currency: 'EUR' : 'symbol' : '1.2-2' | ||||
|             }}</span> | ||||
|           </p> | ||||
|         </div> | ||||
| 
 | ||||
|         <button type="submit" class="button-primary w-full py-2 font-bold">Roll Dice</button> | ||||
|       </form> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="lg:col-span-3 card p-8 flex items-center justify-center"> | ||||
|       @if (rolledValue() !== null) { | ||||
|         <div class="text-white text-center text-8xl font-bold"> | ||||
|           {{ rolledValue() }} | ||||
|         </div> | ||||
|       } | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
|   @if (rolledValue() !== null) { | ||||
|     <div class="result max-w-sm mx-auto card p-6 mt-8 text-center"> | ||||
|       @if (win()) { | ||||
|         <p class="text-emerald text-base font-semibold"> | ||||
|           You Won! Payout: {{ payout() | currency: 'EUR' : 'symbol' : '1.2-2' }} | ||||
|         </p> | ||||
|       } @else { | ||||
|         <p class="text-accent-red text-base font-semibold">You Lost.</p> | ||||
|       } | ||||
|     </div> | ||||
|   } | ||||
| </div> | ||||
|  |  | |||
|  | @ -1,118 +1,124 @@ | |||
| import { Component, signal, inject, OnInit } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; | ||||
| import { DiceService } from './dice.service'; | ||||
| import { DiceDto, DiceResult } from './dice.model'; | ||||
| import { debounceTime, tap } from 'rxjs/operators'; | ||||
| import {UserService} from "@service/user.service"; | ||||
| 
 | ||||
| type DiceFormGroup = FormGroup<{ | ||||
|   betAmount: FormControl<number | null>; | ||||
|   rollOver: FormControl<boolean>; | ||||
|   targetValue: FormControl<number | null>; | ||||
| }>; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-dice', | ||||
|   standalone: true, | ||||
|   imports: [CommonModule, ReactiveFormsModule], | ||||
|   templateUrl: './dice.component.html', | ||||
| }) | ||||
| export class DiceComponent implements OnInit { | ||||
|   private readonly formBuilder = inject(FormBuilder); | ||||
|   private readonly diceService = inject(DiceService); | ||||
|   private readonly userService = inject(UserService); | ||||
| 
 | ||||
| 
 | ||||
|   rolledValue = signal<number | null>(null); | ||||
|   win = signal<boolean | null>(null); | ||||
|   payout = signal<number | null>(null); | ||||
|   winChance = signal(0); | ||||
|   potentialWin = signal(0); | ||||
| 
 | ||||
|   readonly diceForm: DiceFormGroup = this.createDiceForm(); | ||||
| 
 | ||||
|   private readonly MAX_DICE_VALUE = 100; | ||||
| 
 | ||||
|   constructor() { | ||||
|   } | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     this.diceForm.valueChanges.pipe( | ||||
|       debounceTime(100), | ||||
|       tap(() => this.calculateWinChanceAndPotentialWin()) | ||||
|     ).subscribe(); | ||||
| 
 | ||||
|     this.calculateWinChanceAndPotentialWin(); | ||||
|   } | ||||
| 
 | ||||
|   createDiceForm(): DiceFormGroup { | ||||
|     return this.formBuilder.group({ | ||||
|       betAmount: new FormControl<number | null>(1.00, { | ||||
|         validators: [Validators.required, Validators.min(0.01)], | ||||
|         nonNullable: true, | ||||
|       }), | ||||
|       rollOver: new FormControl<boolean>(true, { | ||||
|         validators: [Validators.required], | ||||
|         nonNullable: true, | ||||
|       }), | ||||
|       targetValue: new FormControl<number | null>(50.50, { | ||||
|         validators: [Validators.required, Validators.min(1), Validators.max(100)], | ||||
|         nonNullable: true, | ||||
|       }), | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   toggleRollMode(): void { | ||||
|     const currentMode = this.diceForm.get('rollOver')?.value; | ||||
|     this.diceForm.get('rollOver')?.setValue(!currentMode); | ||||
|   } | ||||
| 
 | ||||
|   calculateWinChanceAndPotentialWin(): void { | ||||
|     const formValues = this.diceForm.value; | ||||
|     const target = formValues.targetValue ?? 0; | ||||
|     const bet = formValues.betAmount ?? 0; | ||||
|     const isOver = formValues.rollOver ?? true; | ||||
| 
 | ||||
|     const calculatedWinChance = isOver ? this.MAX_DICE_VALUE - target : target - 1; | ||||
| 
 | ||||
|     this.winChance.set(Math.max(0, calculatedWinChance)); | ||||
| 
 | ||||
|     let multiplier = 0; | ||||
|     if (calculatedWinChance > 0) { | ||||
|       multiplier = (this.MAX_DICE_VALUE - 1) / calculatedWinChance; | ||||
|     } | ||||
| 
 | ||||
|     this.potentialWin.set(bet * multiplier); | ||||
|   } | ||||
| 
 | ||||
|   roll(): void { | ||||
|     if (this.diceForm.invalid) { | ||||
|       this.diceForm.markAllAsTouched(); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const diceDto: DiceDto = this.diceForm.getRawValue() as DiceDto; | ||||
| 
 | ||||
|     this.rolledValue.set(null); | ||||
|     this.win.set(null); | ||||
|     this.payout.set(null); | ||||
| 
 | ||||
|     this.diceService.rollDice(diceDto).subscribe({ | ||||
|       next: (result: DiceResult) => { | ||||
|         this.rolledValue.set(result.rolledValue); | ||||
|         this.win.set(result.win); | ||||
|         this.payout.set(result.payout); | ||||
|         this.userService.refreshCurrentUser(); | ||||
|       }, | ||||
|       error: (error) => { | ||||
|         console.error('Dice roll failed:', error); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   hasError(controlName: string, errorName: string): boolean { | ||||
|     const control = this.diceForm.get(controlName); | ||||
|     return control !== null && control.touched && control.hasError(errorName); | ||||
|   } | ||||
| } | ||||
| import { Component, signal, inject, OnInit } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| import { | ||||
|   FormBuilder, | ||||
|   FormControl, | ||||
|   FormGroup, | ||||
|   ReactiveFormsModule, | ||||
|   Validators, | ||||
| } from '@angular/forms'; | ||||
| import { DiceService } from './dice.service'; | ||||
| import { DiceDto, DiceResult } from './dice.model'; | ||||
| import { debounceTime, tap } from 'rxjs/operators'; | ||||
| import { UserService } from '@service/user.service'; | ||||
| 
 | ||||
| type DiceFormGroup = FormGroup<{ | ||||
|   betAmount: FormControl<number | null>; | ||||
|   rollOver: FormControl<boolean>; | ||||
|   targetValue: FormControl<number | null>; | ||||
| }>; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'app-dice', | ||||
|   standalone: true, | ||||
|   imports: [CommonModule, ReactiveFormsModule], | ||||
|   templateUrl: './dice.component.html', | ||||
| }) | ||||
| export class DiceComponent implements OnInit { | ||||
|   private readonly formBuilder = inject(FormBuilder); | ||||
|   private readonly diceService = inject(DiceService); | ||||
|   private readonly userService = inject(UserService); | ||||
| 
 | ||||
|   rolledValue = signal<number | null>(null); | ||||
|   win = signal<boolean | null>(null); | ||||
|   payout = signal<number | null>(null); | ||||
|   winChance = signal(0); | ||||
|   potentialWin = signal(0); | ||||
| 
 | ||||
|   readonly diceForm: DiceFormGroup = this.createDiceForm(); | ||||
| 
 | ||||
|   private readonly MAX_DICE_VALUE = 100; | ||||
| 
 | ||||
|   constructor() {} | ||||
| 
 | ||||
|   ngOnInit(): void { | ||||
|     this.diceForm.valueChanges | ||||
|       .pipe( | ||||
|         debounceTime(100), | ||||
|         tap(() => this.calculateWinChanceAndPotentialWin()) | ||||
|       ) | ||||
|       .subscribe(); | ||||
| 
 | ||||
|     this.calculateWinChanceAndPotentialWin(); | ||||
|   } | ||||
| 
 | ||||
|   createDiceForm(): DiceFormGroup { | ||||
|     return this.formBuilder.group({ | ||||
|       betAmount: new FormControl<number | null>(1.0, { | ||||
|         validators: [Validators.required, Validators.min(0.01)], | ||||
|         nonNullable: true, | ||||
|       }), | ||||
|       rollOver: new FormControl<boolean>(true, { | ||||
|         validators: [Validators.required], | ||||
|         nonNullable: true, | ||||
|       }), | ||||
|       targetValue: new FormControl<number | null>(50.5, { | ||||
|         validators: [Validators.required, Validators.min(1), Validators.max(100)], | ||||
|         nonNullable: true, | ||||
|       }), | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   toggleRollMode(): void { | ||||
|     const currentMode = this.diceForm.get('rollOver')?.value; | ||||
|     this.diceForm.get('rollOver')?.setValue(!currentMode); | ||||
|   } | ||||
| 
 | ||||
|   calculateWinChanceAndPotentialWin(): void { | ||||
|     const formValues = this.diceForm.value; | ||||
|     const target = formValues.targetValue ?? 0; | ||||
|     const bet = formValues.betAmount ?? 0; | ||||
|     const isOver = formValues.rollOver ?? true; | ||||
| 
 | ||||
|     const calculatedWinChance = isOver ? this.MAX_DICE_VALUE - target : target - 1; | ||||
| 
 | ||||
|     this.winChance.set(Math.max(0, calculatedWinChance)); | ||||
| 
 | ||||
|     let multiplier = 0; | ||||
|     if (calculatedWinChance > 0) { | ||||
|       multiplier = (this.MAX_DICE_VALUE - 1) / calculatedWinChance; | ||||
|     } | ||||
| 
 | ||||
|     this.potentialWin.set(bet * multiplier); | ||||
|   } | ||||
| 
 | ||||
|   roll(): void { | ||||
|     if (this.diceForm.invalid) { | ||||
|       this.diceForm.markAllAsTouched(); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     const diceDto: DiceDto = this.diceForm.getRawValue() as DiceDto; | ||||
| 
 | ||||
|     this.rolledValue.set(null); | ||||
|     this.win.set(null); | ||||
|     this.payout.set(null); | ||||
| 
 | ||||
|     this.diceService.rollDice(diceDto).subscribe({ | ||||
|       next: (result: DiceResult) => { | ||||
|         this.rolledValue.set(result.rolledValue); | ||||
|         this.win.set(result.win); | ||||
|         this.payout.set(result.payout); | ||||
|         this.userService.refreshCurrentUser(); | ||||
|       }, | ||||
|       error: (error) => { | ||||
|         console.error('Dice roll failed:', error); | ||||
|       }, | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   hasError(controlName: string, errorName: string): boolean { | ||||
|     const control = this.diceForm.get(controlName); | ||||
|     return control !== null && control.touched && control.hasError(errorName); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,11 +1,11 @@ | |||
| export interface DiceDto { | ||||
|   betAmount: number; | ||||
|   rollOver: boolean; | ||||
|   targetValue: number; | ||||
| } | ||||
| 
 | ||||
| export interface DiceResult { | ||||
|   win: boolean; | ||||
|   payout: number; | ||||
|   rolledValue: number; | ||||
| } | ||||
| export interface DiceDto { | ||||
|   betAmount: number; | ||||
|   rollOver: boolean; | ||||
|   targetValue: number; | ||||
| } | ||||
| 
 | ||||
| export interface DiceResult { | ||||
|   win: boolean; | ||||
|   payout: number; | ||||
|   rolledValue: number; | ||||
| } | ||||
|  |  | |||
|  | @ -1,17 +1,17 @@ | |||
| import { Injectable } from '@angular/core'; | ||||
| import { HttpClient } from '@angular/common/http'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { DiceDto, DiceResult } from './dice.model'; | ||||
| 
 | ||||
| @Injectable({ | ||||
|   providedIn: 'root' | ||||
| }) | ||||
| export class DiceService { | ||||
|   private apiUrl = '/backend/dice'; | ||||
| 
 | ||||
|   constructor(private http: HttpClient) { } | ||||
| 
 | ||||
|   rollDice(diceDto: DiceDto): Observable<DiceResult> { | ||||
|     return this.http.post<DiceResult>(this.apiUrl, diceDto); | ||||
|   } | ||||
| } | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { HttpClient } from '@angular/common/http'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { DiceDto, DiceResult } from './dice.model'; | ||||
| 
 | ||||
| @Injectable({ | ||||
|   providedIn: 'root', | ||||
| }) | ||||
| export class DiceService { | ||||
|   private apiUrl = '/backend/dice'; | ||||
| 
 | ||||
|   constructor(private http: HttpClient) {} | ||||
| 
 | ||||
|   rollDice(diceDto: DiceDto): Observable<DiceResult> { | ||||
|     return this.http.post<DiceResult>(this.apiUrl, diceDto); | ||||
|   } | ||||
| } | ||||
|  |  | |||
		Reference in a new issue