From 819b8a7bc99bb3328616eb03aba40a7fbd277c45 Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Wed, 12 Feb 2025 10:39:49 +0100 Subject: [PATCH] refactor: rename landing component and remove unused files --- frontend/src/app/app.routes.ts | 4 +- .../src/app/landing/landing.component.html | 481 ------------------ frontend/src/app/landing/landing.component.ts | 262 ---------- .../src/app/services/animation.service.ts | 380 -------------- frontend/src/app/services/game.service.ts | 104 ---- frontend/src/app/services/jackpot.service.ts | 130 ----- frontend/src/app/services/popup.service.ts | 96 ---- frontend/src/app/services/winner.service.ts | 120 ----- .../animated-button.component.ts | 56 -- .../game-card/game-card.component.ts | 106 ---- .../winner-ticker/winner-ticker.component.ts | 40 -- 11 files changed, 2 insertions(+), 1777 deletions(-) delete mode 100644 frontend/src/app/landing/landing.component.html delete mode 100644 frontend/src/app/landing/landing.component.ts delete mode 100644 frontend/src/app/services/animation.service.ts delete mode 100644 frontend/src/app/services/game.service.ts delete mode 100644 frontend/src/app/services/jackpot.service.ts delete mode 100644 frontend/src/app/services/popup.service.ts delete mode 100644 frontend/src/app/services/winner.service.ts delete mode 100644 frontend/src/app/shared/components/animated-button/animated-button.component.ts delete mode 100644 frontend/src/app/shared/components/game-card/game-card.component.ts delete mode 100644 frontend/src/app/shared/components/winner-ticker/winner-ticker.component.ts diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 36b5bb5..dc64999 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -1,9 +1,9 @@ import { Routes } from '@angular/router'; -import { LandingComponent } from './landing/landing.component'; +import { LandingPageComponent } from './landing-page/landing-page.component'; export const routes: Routes = [ { path: '', - component: LandingComponent, + component: LandingPageComponent, }, ]; diff --git a/frontend/src/app/landing/landing.component.html b/frontend/src/app/landing/landing.component.html deleted file mode 100644 index 5922ccc..0000000 --- a/frontend/src/app/landing/landing.component.html +++ /dev/null @@ -1,481 +0,0 @@ -
-
-
-
-
- - 🎰 - - {{ winner.name }} - {{ winner.isVIP ? '(VIP)' : '' }} - won €{{ winner.amount | number }} - ({{ winner.multiplier }}x) - - -
-
-
- - -
- -
- -
-
- -
-
-
🎰
-
- 💎 -
-
- 7️⃣ -
-
- 🃏 -
-
- 💰 -
-
- 🎲 -
-
👑
-
-
- -
-
-
-
-
🏆 MEGA JACKPOT GROWING
-
- - €{{ currentJackpot$ | async | number }} - - -
-
Must drop before €2,000,000
-
-
- -
-
-
-
- EXCLUSIVE VIP OFFER -
-

-
START WITH
-
- €10,000 -
-
GUARANTEED WINNINGS*
-

- -
-
- 1000% FIRST DEPOSIT MATCH -
+ 1000 FREE SPINS
-
-
- ⚠️ Offer expires in: {{ timeLeft$ | async }} -
-
- -
- -
- -
-
- - Instant Withdrawals -
-
- - 24/7 VIP Support -
-
- - 100% Win Guarantee* -
-
-
-
- -
-
-
- - - 🎰 {{ winner.name }} - {{ - winner.isVIP ? '(VIP)' : '' - }} - turned €{{ winner.betAmount }} into - €{{ winner.amount | number }} - ({{ winner.multiplier }}x) - - -
-
-
- -
-

- - TOP WINNING GAMES - -

-
-
-
- -
-
-
-

{{ game.name }}

-
- HOT 🔥 - {{ game.lastWinner }} won €{{ game.lastWin | number }} -
-
-

{{ game.description }}

-
-
- - {{ game.winChance }}% Win Rate - - Max Win: €{{ game.maxWin | number }} -
-
- Min: €{{ game.minBet }} | Max: €{{ game.maxBet }} -
- Popularity: -
-
-
-
-
-
-
- - {{ feature }} - -
- -
-
-
-
-
-
-
- -
-
-
💎
-

Elite VIP Status

-

Up to €50,000 monthly rewards

-
-
-
⚡️
-

Instant Cashouts

-

Get paid in 5 minutes!

-
-
-
🎁
-

Daily Rewards

-

Win up to €5,000 daily!

-
-
-
🏆
-

99.9% Win Rate*

-

Highest odds in the industry!

-
-
- - -
- *Terms and conditions apply. Guaranteed winnings based on maximum bonus utilization. Win - rate calculated on minimum bets. Withdrawal restrictions and wagering requirements apply. - Please gamble responsibly. -
-
-
-
-
-
-
-
- -

- {{ popup.title }} -

-

- {{ popup.message }} -

-

- {{ popup.subMessage }} -

- -
- - -
-
- ⏰ Expires in: {{ popup.expires }} -
-
-
-
- -
-
-
- -
-
- 🎰 -
-

- SO CLOSE! -

-

- Just one more spin to win the MEGA JACKPOT! -

-
- - Hot streak detected - Increased win probability activated! - -
- -
-
-
-
-
diff --git a/frontend/src/app/landing/landing.component.ts b/frontend/src/app/landing/landing.component.ts deleted file mode 100644 index cd5be3a..0000000 --- a/frontend/src/app/landing/landing.component.ts +++ /dev/null @@ -1,262 +0,0 @@ -import { - Component, - OnInit, - OnDestroy, - ChangeDetectionStrategy, - ChangeDetectorRef, - NgZone, - ElementRef, - ViewChild, - AfterViewInit, -} from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { Router } from '@angular/router'; -import { Subject, interval, Observable } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { animate, style, transition, trigger } from '@angular/animations'; - -import { PopupService } from '../services/popup.service'; -import { GameService } from '../services/game.service'; -import { WinnerService } from '../services/winner.service'; -import { JackpotService } from '../services/jackpot.service'; -import { AnimationService } from '../services/animation.service'; - -import { AnimatedButtonComponent } from '../shared/components/animated-button/animated-button.component'; -import { WinnerTickerComponent } from '../shared/components/winner-ticker/winner-ticker.component'; -import { GameCardComponent } from '../shared/components/game-card/game-card.component'; - -@Component({ - selector: 'app-landing', - standalone: true, - imports: [CommonModule, AnimatedButtonComponent, WinnerTickerComponent, GameCardComponent], - templateUrl: './landing.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - animations: [ - trigger('fadeSlide', [ - transition(':enter', [ - style({ opacity: 0, transform: 'translateY(20px)' }), - animate( - '0.5s cubic-bezier(0.4, 0, 0.2, 1)', - style({ opacity: 1, transform: 'translateY(0)' }) - ), - ]), - transition(':leave', [ - animate( - '0.5s cubic-bezier(0.4, 0, 0.2, 1)', - style({ opacity: 0, transform: 'translateY(-20px)' }) - ), - ]), - ]), - ], -}) -export class LandingComponent implements OnInit, OnDestroy, AfterViewInit { - private destroy$ = new Subject(); - nearMiss = false; - isScrolled = false; - - @ViewChild('jackpotCounter') jackpotCounter!: ElementRef; - @ViewChild('heroSection') heroSection!: ElementRef; - @ViewChild('gamesGrid') gamesGrid!: ElementRef; - @ViewChild('winnersMarquee') winnersMarquee!: ElementRef; - @ViewChild('particleContainer') particleContainer!: ElementRef; - - readonly showPopup$: Observable; - readonly currentPopup$: Observable; - readonly games$: Observable; - readonly recentWinners$: Observable; - readonly onlinePlayers$: Observable; - readonly currentJackpot$: Observable; - readonly timeLeft$: Observable; - readonly totalPlayersToday: number; - readonly totalWinnersToday: number; - - constructor( - private router: Router, - private cdr: ChangeDetectorRef, - private ngZone: NgZone, - private popupService: PopupService, - private gameService: GameService, - private winnerService: WinnerService, - private jackpotService: JackpotService, - private animationService: AnimationService - ) { - this.showPopup$ = this.popupService.showPopup$; - this.currentPopup$ = this.popupService.currentPopup$; - this.games$ = this.gameService.games$; - this.recentWinners$ = this.winnerService.recentWinners$; - this.onlinePlayers$ = this.winnerService.onlinePlayers$; - this.currentJackpot$ = this.jackpotService.currentJackpot$; - this.timeLeft$ = this.jackpotService.timeLeft$; - this.totalPlayersToday = this.winnerService.getTotalPlayersToday(); - this.totalWinnersToday = this.winnerService.getTotalWinnersToday(); - } - - ngOnInit(): void { - this.initializeTimers(); - this.initializeScrollListener(); - } - - ngAfterViewInit(): void { - this.initializeAnimations(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - window.removeEventListener('scroll', () => { - this.isScrolled = window.scrollY > 0; - }); - } - - private initializeAnimations(): void { - this.animationService.createParticleEffect(this.particleContainer); - this.animationService.animateEntrance(this.heroSection); - const gameCards = this.gamesGrid?.nativeElement.querySelectorAll('.game-card'); - if (gameCards) { - gameCards.forEach((card: HTMLElement, index: number) => { - this.animationService.animateEntrance(new ElementRef(card), 0.1 * index); - }); - } - - this.animationService.animateOnScroll(this.gamesGrid, 'slideUp'); - - this.currentJackpot$.pipe(takeUntil(this.destroy$)).subscribe((value) => { - if (this.jackpotCounter && value) { - const prevValue = value - Math.floor(Math.random() * 1000 + 500); - this.animationService.animateJackpotCounter(this.jackpotCounter, prevValue, value); - } - }); - } - - private initializeTimers(): void { - this.ngZone.runOutsideAngular(() => { - interval(1500) - .pipe(takeUntil(this.destroy$)) - .subscribe(() => { - this.jackpotService.updateJackpot(); - this.cdr.markForCheck(); - }); - interval(3000) - .pipe(takeUntil(this.destroy$)) - .subscribe(() => { - this.winnerService.updateOnlinePlayers(); - this.cdr.markForCheck(); - }); - interval(1000) - .pipe(takeUntil(this.destroy$)) - .subscribe(() => { - this.jackpotService.updateTimeLeft(); - if (this.jackpotService.isUrgent()) { - this.showUrgentOffer(); - } - this.cdr.markForCheck(); - }); - interval(7000) - .pipe(takeUntil(this.destroy$)) - .subscribe(() => { - const randomGame = this.gameService.getGameById('mega-fortune'); - if (randomGame) { - const winAmount = Math.floor(Math.random() * 50000) + 10000; - this.winnerService.generateNewWinner(randomGame.name, winAmount); - if (winAmount > 10000) { - this.showBigWinPopup(winAmount); - } - } - this.cdr.markForCheck(); - }); - interval(15000) - .pipe(takeUntil(this.destroy$)) - .subscribe(() => { - this.gameService.updateGameStats(); - this.cdr.markForCheck(); - }); - interval(30000) - .pipe(takeUntil(this.destroy$)) - .subscribe(() => { - this.popupService.showRandomPopup(); - this.cdr.markForCheck(); - }); - }); - } - - private initializeScrollListener(): void { - window.addEventListener('scroll', () => { - this.isScrolled = window.scrollY > 0; - this.cdr.detectChanges(); - }); - } - - private showUrgentOffer(): void { - this.popupService.showSpecificPopup({ - title: '⚠️ LAST CHANCE!', - message: 'Bonus offer expiring - Lock in 500% now!', - type: 'urgent', - cta: 'Claim Before Timer Ends', - expires: '00:30', - }); - } - - private showBigWinPopup(amount: number): void { - this.popupService.showSpecificPopup({ - title: '🎰 MASSIVE WIN ALERT!', - message: `Player just won €${amount.toLocaleString()} on minimum bet!`, - subMessage: 'Same game still hot - Win rate increased to 99.9%!', - type: 'win', - cta: 'Play Same Game', - }); - } - - closePopup(): void { - this.popupService.closePopup(); - } - - claimBonus(): void { - this.nearMiss = true; - this.cdr.markForCheck(); - - setTimeout(() => { - this.router.navigate(['/register'], { - queryParams: { - bonus: 'welcome1000', - ref: 'landing_hero', - special: 'true', - vip: 'fast-track', - }, - }); - }, 1500); - } - - playNow(gameId: string): void { - const game = this.gameService.getGameById(gameId); - if (!game) return; - - this.popupService.showSpecificPopup({ - title: '🎰 PERFECT TIMING!', - message: `${game.name} is currently at ${game.winChance}% win rate!`, - subMessage: `Last player won €${game.lastWin.toLocaleString()} - Hot streak active!`, - type: 'fomo', - cta: 'Play Now', - }); - - setTimeout(() => { - this.router.navigate(['/game', gameId], { - queryParams: { - ref: 'landing_games', - bonus: 'true', - rtp: 'enhanced', - multiplier: 'active', - }, - }); - }, 2000); - } - - onButtonClick(event: MouseEvent): void { - const button = event.currentTarget as HTMLElement; - this.animationService.animateButtonClick(new ElementRef(button)); - } - - onGameCardHover(event: MouseEvent): void { - const card = event.currentTarget as HTMLElement; - this.animationService.animateFloat(new ElementRef(card)); - } -} diff --git a/frontend/src/app/services/animation.service.ts b/frontend/src/app/services/animation.service.ts deleted file mode 100644 index 34fe89a..0000000 --- a/frontend/src/app/services/animation.service.ts +++ /dev/null @@ -1,380 +0,0 @@ -import { Injectable, ElementRef } from '@angular/core'; -import { gsap } from 'gsap'; -import { ScrollTrigger } from 'gsap/ScrollTrigger'; -import { MotionPathPlugin } from 'gsap/MotionPathPlugin'; - -gsap.registerPlugin(ScrollTrigger, MotionPathPlugin); - -@Injectable({ - providedIn: 'root', -}) -export class AnimationService { - private readonly MEGA_BONUS_THRESHOLD = 25000; - private readonly BONUS_THRESHOLD = 1000; - private readonly FLASH_THRESHOLD = 10000; - - private readonly ANIMATION_DURATIONS = { - MEGA: 3, - BONUS: 2, - BASE: 1, - }; - - private readonly SYMBOLS = { - MONEY: '💰', - SPARKLE: '✨', - GEM: '💎', - STAR: '🌟', - }; - - constructor() { - // Configure GSAP defaults - gsap.config({ - autoSleep: 60, - force3D: true, - nullTargetWarn: false, - }); - } - - animateEntrance(element: ElementRef, delay: number = 0) { - return gsap.from(element.nativeElement, { - duration: 0.6, - opacity: 0, - y: 30, - ease: 'power3.out', - delay, - clearProps: 'all', - }); - } - - animateFloat(element: ElementRef) { - return gsap.to(element.nativeElement, { - duration: 2, - y: '-=20', - ease: 'power1.inOut', - yoyo: true, - repeat: -1, - }); - } - - animateShine(element: ElementRef) { - const shine = gsap.to(element.nativeElement, { - duration: 1.5, - backgroundPosition: '200%', - ease: 'linear', - repeat: -1, - }); - return shine; - } - - animateMorphingBackground(element: ElementRef) { - return gsap.to(element.nativeElement, { - duration: 8, - borderRadius: '60% 40% 30% 70% / 60% 30% 70% 40%', - ease: 'sine.inOut', - repeat: -1, - yoyo: true, - }); - } - - animateOnScroll(element: ElementRef, animation: 'fadeIn' | 'slideUp' | 'scaleIn' = 'fadeIn') { - const animations = { - fadeIn: { - opacity: 0, - y: 0, - duration: 0.6, - }, - slideUp: { - opacity: 0, - y: 50, - duration: 0.8, - }, - scaleIn: { - opacity: 0, - scale: 0.8, - duration: 0.6, - }, - }; - - return gsap.from(element.nativeElement, { - ...animations[animation], - ease: 'power2.out', - scrollTrigger: { - trigger: element.nativeElement, - start: 'top bottom-=100', - toggleActions: 'play none none reverse', - }, - }); - } - - createParticleEffect(container: ElementRef, particleCount: number = 20): void { - const particles = Array.from({ length: particleCount }, () => this.createParticle(container)); - particles.forEach((particle) => this.animateParticle(particle)); - } - - private createParticle(container: ElementRef): HTMLElement { - const particle = document.createElement('div'); - particle.className = 'absolute w-2 h-2 bg-emerald-500/20 rounded-full'; - container.nativeElement.appendChild(particle); - - gsap.set(particle, { - x: gsap.utils.random(0, container.nativeElement.offsetWidth), - y: gsap.utils.random(0, container.nativeElement.offsetHeight), - }); - - return particle; - } - - private animateParticle(particle: HTMLElement): void { - gsap.to(particle, { - duration: gsap.utils.random(2, 4), - x: '+=50', - y: '-=50', - opacity: 0, - scale: 0, - ease: 'none', - repeat: -1, - onRepeat: () => this.resetParticle(particle), - }); - } - - private resetParticle(particle: HTMLElement): void { - gsap.set(particle, { - x: gsap.utils.random(0, particle.parentElement!.offsetWidth), - y: gsap.utils.random(0, particle.parentElement!.offsetHeight), - opacity: 1, - scale: 1, - }); - } - - animateButtonClick(element: ElementRef): gsap.core.Timeline { - return gsap - .timeline() - .to(element.nativeElement, { scale: 0.95, duration: 0.1 }) - .to(element.nativeElement, { scale: 1, duration: 0.2, ease: 'elastic.out(1, 0.3)' }); - } - - animateSuccess(element: ElementRef): gsap.core.Timeline { - return gsap - .timeline() - .to(element.nativeElement, { scale: 1.2, duration: 0.2, ease: 'power2.out' }) - .to(element.nativeElement, { scale: 1, duration: 0.5, ease: 'elastic.out(1, 0.3)' }); - } - - animateJackpotCounter( - element: ElementRef, - startValue: number, - endValue: number - ): gsap.core.Timeline { - const container = this.prepareContainer(element); - const increase = endValue - startValue; - const timeline = gsap.timeline(); - - if (increase > this.FLASH_THRESHOLD) { - this.addFlashEffect(timeline, container, element); - } - - this.addCounterAnimation(timeline, element, startValue, endValue); - - if (increase > this.MEGA_BONUS_THRESHOLD) { - this.addMegaBonusEffect(element); - } else if (increase > this.BONUS_THRESHOLD) { - this.addBonusEffect(element); - } - - return timeline; - } - - private prepareContainer(element: ElementRef): HTMLElement { - const container = element.nativeElement.parentElement; - container.style.position = 'relative'; - this.cleanupExistingEffects(container); - return container; - } - - private cleanupExistingEffects(container: HTMLElement): void { - const existingEffects = container.querySelectorAll('.jackpot-effect'); - existingEffects.forEach((effect: Element) => effect.remove()); - } - - private addFlashEffect( - timeline: gsap.core.Timeline, - container: HTMLElement, - element: ElementRef - ): void { - const flash = this.createFlashElement(); - container.appendChild(flash); - - timeline - .to(flash, { - opacity: 1, - duration: 0.3, - yoyo: true, - repeat: 2, - onComplete: () => flash.remove(), - }) - .to( - element.nativeElement, - { - color: '#FFD700', - textShadow: '0 0 20px rgba(255,215,0,0.8)', - scale: 1.1, - duration: 0.6, - yoyo: true, - repeat: 1, - }, - '<' - ); - } - - private createFlashElement(): HTMLElement { - const flash = document.createElement('div'); - flash.className = - 'jackpot-effect absolute inset-0 bg-yellow-400/20 rounded-xl backdrop-blur-sm z-10'; - return flash; - } - - private addCounterAnimation( - timeline: gsap.core.Timeline, - element: ElementRef, - startValue: number, - endValue: number - ): void { - const obj = { value: startValue }; - timeline.to(obj, { - duration: this.calculateDuration(endValue - startValue), - value: endValue, - ease: 'power1.inOut', - onUpdate: () => this.updateCounter(obj.value, endValue, element), - onComplete: () => this.resetElementStyles(element, endValue), - }); - } - - private updateCounter(currentValue: number, endValue: number, element: ElementRef): void { - const progress = currentValue / endValue; - const fluctuation = Math.random() * (100 * (1 - progress)) - 50 * (1 - progress); - const displayValue = Math.floor(currentValue + fluctuation); - element.nativeElement.textContent = '€' + displayValue.toLocaleString(); - } - - private resetElementStyles(element: ElementRef, finalValue: number): void { - element.nativeElement.textContent = '€' + finalValue.toLocaleString(); - element.nativeElement.style.color = ''; - element.nativeElement.style.textShadow = ''; - element.nativeElement.style.transform = ''; - } - - private calculateDuration(increase: number): number { - if (increase > this.MEGA_BONUS_THRESHOLD) return this.ANIMATION_DURATIONS.MEGA; - if (increase > this.BONUS_THRESHOLD) return this.ANIMATION_DURATIONS.BONUS; - return this.ANIMATION_DURATIONS.BASE; - } - - private addMegaBonusEffect(element: ElementRef): void { - const effectContainer = this.createEffectContainer(element); - this.addGlowEffect(effectContainer, true); - this.addFloatingSymbols(element, effectContainer, 10, 50); - } - - private addBonusEffect(element: ElementRef): void { - const effectContainer = this.createEffectContainer(element); - this.addGlowEffect(effectContainer, false); - this.addFloatingSymbols(element, effectContainer, 5, 30); - } - - private createEffectContainer(element: ElementRef): HTMLElement { - const container = element.nativeElement.parentElement; - const effectContainer = document.createElement('div'); - effectContainer.className = - 'jackpot-effect absolute inset-0 pointer-events-none overflow-hidden z-20'; - container.appendChild(effectContainer); - return effectContainer; - } - - private addGlowEffect(container: HTMLElement, isMega: boolean): void { - const config = isMega - ? { - shadow: '0 0 30px rgba(255,215,0,0.8), 0 0 60px rgba(255,165,0,0.6)', - scale: 1.1, - duration: 1.5, - repeat: 2, - } - : { - shadow: '0 0 20px rgba(255,215,0,0.4)', - scale: 1.05, - duration: 0.8, - repeat: 1, - }; - - gsap.to(container, { - boxShadow: config.shadow, - scale: config.scale, - opacity: 0, - duration: config.duration, - repeat: config.repeat, - ease: 'power2.inOut', - onComplete: () => container.remove(), - }); - } - - private addFloatingSymbols( - element: ElementRef, - container: HTMLElement, - count: number, - radius: number - ): void { - const rect = element.nativeElement.getBoundingClientRect(); - const centerX = rect.width / 2; - const centerY = rect.height / 2; - const symbols = Object.values(this.SYMBOLS); - - Array.from({ length: count }).forEach((_, i) => { - const symbol = this.createSymbol(symbols, container); - const angle = (i / count) * Math.PI * 2; - this.animateSymbol(symbol, centerX, centerY, angle, radius); - }); - } - - private createSymbol(symbols: string[], container: HTMLElement): HTMLElement { - const symbol = document.createElement('div'); - symbol.textContent = symbols[Math.floor(Math.random() * symbols.length)]; - symbol.className = 'jackpot-effect absolute text-2xl'; - container.appendChild(symbol); - return symbol; - } - - private animateSymbol( - symbol: HTMLElement, - centerX: number, - centerY: number, - angle: number, - radius: number - ): void { - gsap.fromTo( - symbol, - { - x: centerX, - y: centerY, - opacity: 0, - scale: 0, - }, - { - x: centerX + Math.cos(angle) * radius, - y: centerY + Math.sin(angle) * radius, - opacity: 1, - scale: 1, - duration: 1.5, - ease: 'back.out(1.2)', - onComplete: () => this.fadeOutSymbol(symbol), - } - ); - } - - private fadeOutSymbol(symbol: HTMLElement): void { - gsap.to(symbol, { - opacity: 0, - scale: 0, - duration: 0.5, - onComplete: () => symbol.remove(), - }); - } -} diff --git a/frontend/src/app/services/game.service.ts b/frontend/src/app/services/game.service.ts deleted file mode 100644 index 4f0509a..0000000 --- a/frontend/src/app/services/game.service.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; - -export interface Game { - id: string; - name: string; - description: string; - imageUrl: string; - minBet: number; - maxBet: number; - rtp: number; - lastWin: number; - winChance: number; - lastWinner: string; - trending: boolean; - maxWin: number; - popularity: number; - volatility: 'low' | 'medium' | 'high'; - features: string[]; -} - -@Injectable({ - providedIn: 'root', -}) -export class GameService { - private readonly INITIAL_GAMES: Game[] = [ - { - id: 'mega-fortune', - name: 'Mega Fortune Dreams', - description: '🔥 Progressive Jackpot at €1.2M - Must Drop Today!', - imageUrl: 'assets/games/mega-fortune.jpg', - minBet: 0.2, - maxBet: 100, - rtp: 96.5, - lastWin: 15789, - winChance: 99.9, - lastWinner: 'VIP Player', - trending: true, - maxWin: 1000000, - popularity: 98, - volatility: 'high', - features: ['Progressive Jackpot', 'Free Spins', 'Multipliers'], - }, - { - id: 'lightning-roulette', - name: 'Lightning Roulette', - description: '⚡️ 500x Multipliers Active - Hot Streak!', - imageUrl: 'assets/games/lightning-roulette.jpg', - minBet: 1, - maxBet: 500, - rtp: 97.1, - lastWin: 23456, - winChance: 99.7, - lastWinner: 'New Player', - trending: true, - maxWin: 500000, - popularity: 95, - volatility: 'medium', - features: ['Lightning Multipliers', 'Live Dealer', 'Instant Wins'], - }, - ]; - - private readonly STAT_RANGES = { - WIN: { - MIN: 10000, - MAX: 50000, - }, - WIN_CHANCE: { - MIN: 99, - MAX: 100, - }, - POPULARITY: { - MIN: 80, - MAX: 100, - }, - }; - - private readonly games = new BehaviorSubject(this.INITIAL_GAMES); - readonly games$ = this.games.asObservable(); - - updateGameStats(): void { - const updatedGames = this.games.value.map((game) => ({ - ...game, - ...this.generateNewStats(), - })); - this.games.next(updatedGames); - } - - getGameById(id: string): Game | undefined { - return this.games.value.find((game) => game.id === id); - } - - private generateNewStats(): Partial { - return { - lastWin: this.getRandomInRange(this.STAT_RANGES.WIN), - winChance: this.getRandomInRange(this.STAT_RANGES.WIN_CHANCE), - popularity: this.getRandomInRange(this.STAT_RANGES.POPULARITY), - }; - } - - private getRandomInRange(range: { MIN: number; MAX: number }): number { - return Math.floor(Math.random() * (range.MAX - range.MIN)) + range.MIN; - } -} diff --git a/frontend/src/app/services/jackpot.service.ts b/frontend/src/app/services/jackpot.service.ts deleted file mode 100644 index d13928d..0000000 --- a/frontend/src/app/services/jackpot.service.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; - -@Injectable({ - providedIn: 'root', -}) -export class JackpotService { - private readonly INITIAL_JACKPOT = 1234567; - private readonly INITIAL_TIME = '04:59'; - private readonly UPDATE_INTERVAL = 2000; - - private readonly INCREASE_THRESHOLDS = { - MEGA: 0.997, - BONUS: 0.97, - BASE: 0.5, - }; - - private readonly INCREASE_RANGES = { - MEGA: { - MIN: 30000, - MAX: 100000, - }, - BONUS: { - MIN: 2000, - MAX: 15000, - }, - BASE: { - MIN: 100, - MAX: 1000, - }, - }; - - private readonly TIME_LIMITS = { - MINUTES: 4, - SECONDS: 59, - URGENT_THRESHOLD: 30, - }; - - private readonly jackpot = new BehaviorSubject(this.INITIAL_JACKPOT); - private readonly timeLeft = new BehaviorSubject(this.INITIAL_TIME); - private lastUpdateTime = Date.now(); - private minutes = this.TIME_LIMITS.MINUTES; - private seconds = this.TIME_LIMITS.SECONDS; - - readonly currentJackpot$ = this.jackpot.asObservable(); - readonly timeLeft$ = this.timeLeft.asObservable(); - - updateJackpot(): void { - if (!this.shouldUpdate()) return; - - const increase = this.calculateIncrease(); - if (increase > 0) { - this.updateJackpotValue(increase); - this.lastUpdateTime = Date.now(); - } - } - - updateTimeLeft(): void { - this.updateTimers(); - this.updateTimeDisplay(); - } - - isUrgent(): boolean { - return this.minutes === 0 && this.seconds <= this.TIME_LIMITS.URGENT_THRESHOLD; - } - - private shouldUpdate(): boolean { - return Date.now() - this.lastUpdateTime >= this.UPDATE_INTERVAL; - } - - private calculateIncrease(): number { - const random = Math.random(); - - if (random > this.INCREASE_THRESHOLDS.MEGA) { - return this.getRandomIncrease(this.INCREASE_RANGES.MEGA); - } - - if (random > this.INCREASE_THRESHOLDS.BONUS) { - return this.getRandomIncrease(this.INCREASE_RANGES.BONUS); - } - - if (random > this.INCREASE_THRESHOLDS.BASE) { - return this.getRandomIncrease(this.INCREASE_RANGES.BASE); - } - - return 0; - } - - private getRandomIncrease(range: { MIN: number; MAX: number }): number { - return Math.floor(Math.random() * (range.MAX - range.MIN) + range.MIN); - } - - private updateJackpotValue(increase: number): void { - this.jackpot.next(this.jackpot.value + increase); - } - - private updateTimers(): void { - if (this.seconds === 0) { - this.handleMinuteChange(); - } else { - this.seconds--; - } - } - - private handleMinuteChange(): void { - if (this.minutes === 0) { - this.resetTimers(); - } else { - this.minutes--; - this.seconds = this.TIME_LIMITS.SECONDS; - } - } - - private resetTimers(): void { - this.minutes = this.TIME_LIMITS.MINUTES; - this.seconds = this.TIME_LIMITS.SECONDS; - } - - private updateTimeDisplay(): void { - this.timeLeft.next(this.formatTime()); - } - - private formatTime(): string { - return `${this.padNumber(this.minutes)}:${this.padNumber(this.seconds)}`; - } - - private padNumber(num: number): string { - return num.toString().padStart(2, '0'); - } -} diff --git a/frontend/src/app/services/popup.service.ts b/frontend/src/app/services/popup.service.ts deleted file mode 100644 index 2e8ec99..0000000 --- a/frontend/src/app/services/popup.service.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; - -export interface Popup { - title: string; - message: string; - type: 'win' | 'offer' | 'urgent' | 'fomo'; - cta: string; - expires?: string; - subMessage?: string; - imageUrl?: string; -} - -@Injectable({ - providedIn: 'root', -}) -export class PopupService { - private readonly POPUP_TEMPLATES: Popup[] = [ - { - title: '🎯 VIP OFFER', - message: 'Enhanced RTP + 500% Bonus on Next 5 Deposits', - subMessage: 'Limited availability - 3 spots remaining', - type: 'urgent', - cta: 'Claim VIP Bonus', - expires: '5:00', - }, - { - title: '🎰 BIG WIN ALERT', - message: 'Recent win: €89,432 on minimum bet!', - subMessage: 'Game is hot - Enhanced win rate active', - type: 'win', - cta: 'Play Now', - }, - ]; - - private readonly DISPLAY_CONFIG = { - MIN_INTERVAL: 30000, - AUTO_CLOSE_DELAY: 8000, - SHOW_CHANCE: 0.7, - }; - - private readonly popupState = new BehaviorSubject(false); - private readonly currentPopup = new BehaviorSubject(null); - private lastPopupTime = 0; - - readonly showPopup$ = this.popupState.asObservable(); - readonly currentPopup$ = this.currentPopup.asObservable(); - - showRandomPopup(): void { - if (!this.shouldShowPopup()) return; - - const popup = this.getRandomPopup(); - this.displayPopup(popup); - this.scheduleAutoClose(); - this.updateLastPopupTime(); - } - - showSpecificPopup(popup: Popup): void { - if (!this.shouldShowPopup()) return; - - this.displayPopup(popup); - this.updateLastPopupTime(); - } - - closePopup(): void { - this.popupState.next(false); - } - - private shouldShowPopup(): boolean { - const now = Date.now(); - const timeSinceLastPopup = now - this.lastPopupTime; - const isMinIntervalPassed = timeSinceLastPopup >= this.DISPLAY_CONFIG.MIN_INTERVAL; - const isCurrentlyHidden = !this.popupState.value; - const isRandomChanceSuccess = Math.random() <= this.DISPLAY_CONFIG.SHOW_CHANCE; - - return isMinIntervalPassed && isCurrentlyHidden && isRandomChanceSuccess; - } - - private getRandomPopup(): Popup { - const randomIndex = Math.floor(Math.random() * this.POPUP_TEMPLATES.length); - return this.POPUP_TEMPLATES[randomIndex]; - } - - private displayPopup(popup: Popup): void { - this.currentPopup.next(popup); - this.popupState.next(true); - } - - private scheduleAutoClose(): void { - setTimeout(() => this.closePopup(), this.DISPLAY_CONFIG.AUTO_CLOSE_DELAY); - } - - private updateLastPopupTime(): void { - this.lastPopupTime = Date.now(); - } -} diff --git a/frontend/src/app/services/winner.service.ts b/frontend/src/app/services/winner.service.ts deleted file mode 100644 index 155ba3b..0000000 --- a/frontend/src/app/services/winner.service.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Injectable } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; - -export interface Winner { - name: string; - amount: number; - game: string; - timestamp: Date; - isVIP: boolean; - betAmount?: number; - multiplier?: number; -} - -@Injectable({ - providedIn: 'root', -}) -export class WinnerService { - private readonly INITIAL_ONLINE_PLAYERS = 2547; - private readonly TOTAL_PLAYERS_TODAY = 15789; - private readonly TOTAL_WINNERS_TODAY = 12453; - private readonly VIP_CHANCE = 0.3; - private readonly MAX_RECENT_WINNERS = 10; - - private readonly PLAYER_NAMES = { - FIRST: ['Alex', 'Maria', 'John', 'Sarah', 'Mike', 'Lisa', 'David', 'Emma'], - LAST: ['K.', 'S.', 'M.', 'L.', 'R.', 'T.', 'B.', 'W.'], - }; - - private readonly ONLINE_PLAYERS_LIMITS = { - MIN: 2000, - MAX: 3500, - CHANGE_RANGE: 15, - }; - - private readonly winners = new BehaviorSubject([]); - private readonly onlinePlayers = new BehaviorSubject(this.INITIAL_ONLINE_PLAYERS); - - readonly recentWinners$ = this.winners.asObservable(); - readonly onlinePlayers$ = this.onlinePlayers.asObservable(); - - constructor() { - this.initializeWinners(); - } - - generateNewWinner(game: string, baseAmount: number): void { - const winner = this.createWinner(game, baseAmount); - this.updateWinnersList(winner); - } - - updateOnlinePlayers(): void { - const currentCount = this.onlinePlayers.value; - const newCount = this.calculateNewPlayerCount(currentCount); - this.onlinePlayers.next(newCount); - } - - getTotalPlayersToday(): number { - return this.TOTAL_PLAYERS_TODAY; - } - - getTotalWinnersToday(): number { - return this.TOTAL_WINNERS_TODAY; - } - - private initializeWinners(): void { - const initialWinners = [ - this.createWinner('Mega Fortune Dreams', 15432), - this.createWinner('Lightning Roulette', 8745), - this.createWinner('Golden Tiger', 12321), - ]; - this.winners.next(initialWinners); - } - - private createWinner(game: string, baseAmount: number): Winner { - const betAmount = this.calculateBetAmount(); - const multiplier = Math.floor(baseAmount / betAmount); - - return { - name: this.generateRandomName(), - amount: baseAmount, - game, - timestamp: new Date(), - isVIP: Math.random() > this.VIP_CHANCE, - betAmount, - multiplier, - }; - } - - private calculateBetAmount(): number { - return Math.floor(Math.random() * 100) + 10; - } - - private updateWinnersList(winner: Winner): void { - const currentWinners = this.winners.value; - const updatedWinners = [winner, ...currentWinners]; - - if (updatedWinners.length > this.MAX_RECENT_WINNERS) { - updatedWinners.pop(); - } - - this.winners.next(updatedWinners); - } - - private generateRandomName(): string { - const firstName = this.getRandomArrayElement(this.PLAYER_NAMES.FIRST); - const lastName = this.getRandomArrayElement(this.PLAYER_NAMES.LAST); - return `${firstName} ${lastName}`; - } - - private calculateNewPlayerCount(currentCount: number): number { - const change = Math.floor(Math.random() * this.ONLINE_PLAYERS_LIMITS.CHANGE_RANGE) - 5; - return Math.max( - this.ONLINE_PLAYERS_LIMITS.MIN, - Math.min(this.ONLINE_PLAYERS_LIMITS.MAX, currentCount + change) - ); - } - - private getRandomArrayElement(array: T[]): T { - return array[Math.floor(Math.random() * array.length)]; - } -} diff --git a/frontend/src/app/shared/components/animated-button/animated-button.component.ts b/frontend/src/app/shared/components/animated-button/animated-button.component.ts deleted file mode 100644 index 85c433e..0000000 --- a/frontend/src/app/shared/components/animated-button/animated-button.component.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { - Component, - Input, - Output, - EventEmitter, - ElementRef, - ChangeDetectionStrategy, -} from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { AnimationService } from '../../../services/animation.service'; - -@Component({ - selector: 'app-animated-button', - standalone: true, - imports: [CommonModule], - template: ` - - `, - styles: [], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class AnimatedButtonComponent { - @Input() variant: 'primary' | 'secondary' = 'primary'; - @Input() size: 'normal' | 'large' = 'normal'; - @Output() buttonClick = new EventEmitter(); - - constructor(private animationService: AnimationService) {} - - get buttonClass(): string { - const baseClass = - 'cursor-pointer relative group font-bold rounded-full transition-all duration-300 ease-out transform-gpu hover:scale-105 will-change-transform'; - const variantClass = - this.variant === 'primary' - ? 'bg-gradient-to-r from-emerald-500 to-emerald-400 text-black hover:shadow-xl hover:shadow-emerald-500/20' - : 'bg-white/10 text-white hover:bg-white/20'; - - return `${baseClass} ${variantClass}`; - } - - handleClick(event: MouseEvent): void { - const elementRef = new ElementRef(event.currentTarget); - this.animationService.animateButtonClick(elementRef); - this.buttonClick.emit(event); - } -} diff --git a/frontend/src/app/shared/components/game-card/game-card.component.ts b/frontend/src/app/shared/components/game-card/game-card.component.ts deleted file mode 100644 index 78dacac..0000000 --- a/frontend/src/app/shared/components/game-card/game-card.component.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { - Component, - Input, - Output, - EventEmitter, - ElementRef, - ChangeDetectionStrategy, -} from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { Game } from '../../../services/game.service'; -import { AnimatedButtonComponent } from '../animated-button/animated-button.component'; -import { AnimationService } from '../../../services/animation.service'; - -@Component({ - selector: 'app-game-card', - standalone: true, - imports: [CommonModule, AnimatedButtonComponent], - template: ` -
-
- -
-
-
-

{{ game.name }}

-
- - HOT 🔥 - - - {{ game.lastWinner }} won €{{ game.lastWin | number }} - -
-
-

{{ game.description }}

-
-
- - {{ game.winChance }}% Win Rate - - Max Win: €{{ game.maxWin | number }} -
-
- - Min: €{{ game.minBet }} | Max: €{{ game.maxBet }} - -
- Popularity: -
-
-
-
-
-
-
- - {{ feature }} - -
- PLAY NOW -
-
-
-
-
- `, - styles: [], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class GameCardComponent { - @Input() game!: Game; - @Output() play = new EventEmitter(); - - constructor(private animationService: AnimationService) {} - - onHover(event: MouseEvent): void { - const element = event.currentTarget as HTMLElement; - const elementRef = new ElementRef(element); - this.animationService.animateFloat(elementRef); - } - - onPlay(): void { - this.play.emit(); - } -} diff --git a/frontend/src/app/shared/components/winner-ticker/winner-ticker.component.ts b/frontend/src/app/shared/components/winner-ticker/winner-ticker.component.ts deleted file mode 100644 index 7e0c733..0000000 --- a/frontend/src/app/shared/components/winner-ticker/winner-ticker.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - Component, - Input, - ViewChild, - ElementRef, - AfterViewInit, - ChangeDetectionStrategy, -} from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { default as autoAnimate } from '@formkit/auto-animate'; -import { Winner } from '../../../services/winner.service'; - -@Component({ - selector: 'app-winner-ticker', - standalone: true, - imports: [CommonModule], - template: ` -
- - 🎰 - - {{ winner.name }} - {{ winner.isVIP ? '(VIP)' : '' }} - won €{{ winner.amount | number }} - ({{ winner.multiplier }}x) - - -
- `, - styles: [], - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class WinnerTickerComponent implements AfterViewInit { - @Input() winners: Winner[] = []; - @ViewChild('tickerContainer') tickerContainer!: ElementRef; - - ngAfterViewInit(): void { - autoAnimate(this.tickerContainer.nativeElement); - } -}