import { Component, signal, effect, computed } from '@angular/core'; import { DecimalPipe} from '@angular/common'; import { Asset, AssetPlanPosition, TypeDepreciation, YearMonth, AssetLifeChange, AssetDepreciationMethod } from '../assets/asset'; import { AssetService } from '../assets/service/asset.service'; import { TranslateModule, TranslateService } from "@ngx-translate/core"; import { FormsModule } from '@angular/forms'; import { MatDatepicker, MatDatepickerModule } from '@angular/material/datepicker'; import { MatNativeDateModule } from '@angular/material/core'; import { MatInputModule } from '@angular/material/input'; import { format, parse } from 'date-fns'; import { createHash } from 'crypto'; class AssetLifeChangeWrapper { when = signal(YearMonth.todayTxt()); initial = signal(0); constructor(assetLifeChange: AssetLifeChange) { this.set(assetLifeChange); } get(): AssetLifeChange { return new AssetLifeChange(YearMonth.from(this.when()), this.initial(), 0, 0); } set(assetLifeChange: AssetLifeChange) { this.when.set(YearMonth.toTxt(assetLifeChange.when)); this.initial.set(assetLifeChange.initial); } } const STORAGE_KEY = 'assetForCalculator'; const CACHE_KEY = 'cachedResults'; const HASH_KEY = 'assetHash'; @Component({ selector: 'app-asset-calculator', standalone: true, imports: [DecimalPipe, TranslateModule, FormsModule, MatDatepickerModule, MatNativeDateModule, MatInputModule], providers: [MatDatepickerModule, MatNativeDateModule], templateUrl: "asset-calculator.component.html", styleUrls: ['asset-calculator.component.css'] }) export class AssetCalculatorComponent { TypeDepreciation = TypeDepreciation; initialValueAsset = signal(6000); rate = signal(20); startDepreciation = signal(new Date()); typeDepreciation = signal(TypeDepreciation.linear); factor = signal(2); lifeChangesSignal = signal([]); amortizationsSignal = signal([]); constructor(private assetService: AssetService, private translate: TranslateService) {} ngOnInit(): void { this.restoreState(); } ngOnDestroy(): void { this.saveState(this.controlsToAsset()); } private restoreState() { const savedAsset = localStorage.getItem(STORAGE_KEY); const savedResults = localStorage.getItem(CACHE_KEY); const savedHash = localStorage.getItem(HASH_KEY); if (savedAsset) { const asset: Asset = JSON.parse(savedAsset); this.assetToControls(asset); // Generujemy nowy hash const newHash = this.generateHash(asset); // Jeśli hash jest taki sam, ładujemy wyniki z cache if (savedHash === newHash && savedResults) { this.amortizationsSignal.set(JSON.parse(savedResults)); console.log("✅ Załadowano wyniki z cache – brak przeliczeń."); return; } console.log("🔄 Hash zmieniony – wykonuję przeliczenie."); this.reCalculate(); } } private saveState(asset: Asset) { localStorage.setItem(STORAGE_KEY, JSON.stringify(asset)); localStorage.setItem(HASH_KEY, this.generateHash(asset)); // Zapisujemy nowy hash } private assetToControls(asset: Asset) { if (!asset.life.length) return; const [firstLife, ...restLives] = asset.life; const method = asset.depreciationMethods[0]; this.initialValueAsset.set(firstLife.initial); this.startDepreciation.set(YearMonth.toDate(firstLife.when)); this.rate.set(method.rate); this.typeDepreciation.set(method.type); this.factor.set(method.factor); this.lifeChangesSignal.set(restLives.map(life => new AssetLifeChangeWrapper(life))); } private controlsToAsset(): Asset { const when = YearMonth.fromDate(this.startDepreciation()); const asset = new Asset(when); const method = new AssetDepreciationMethod(when.year, this.rate(), this.typeDepreciation(), this.factor()); asset.addMethod(method); asset.addChange(new AssetLifeChange(when, this.initialValueAsset(), 0, 0)); this.lifeChangesSignal().forEach(lc => asset.addChange(lc.get())); return asset; } private calculateToValues(positions: AssetPlanPosition[]) { let sum = 0, sumThisYear = 0; positions.forEach(pos => { pos.calculatedDepreciation *= 0.01; sum += pos.calculatedDepreciation; pos.sum = sum; sumThisYear = pos.when.month === 1 ? pos.calculatedDepreciation : sumThisYear + pos.calculatedDepreciation; pos.sumThisYear = sumThisYear; }); // Cache wyników localStorage.setItem(CACHE_KEY, JSON.stringify(positions)); } private reCalculate() { this.calculateAmortizationsForAsset(this.controlsToAsset()); } private calculateAmortizationsForAsset(asset: Asset) { this.assetService.calculate(asset).subscribe(positions => { this.calculateToValues(positions); this.amortizationsSignal.set(positions); }); } clazz(pos: AssetPlanPosition) { return pos.when.year % 2 === 0 ? "table-light" : "table-dark"; } addLifeChange(amount = 1000) { const change = new AssetLifeChange(YearMonth.fromDate(this.startDepreciation()), amount, 0, 0); this.lifeChangesSignal.update(changes => [...changes, new AssetLifeChangeWrapper(change)]); } removeLifeChange(index: number) { this.lifeChangesSignal.update(changes => changes.filter((_, i) => i !== index)); } updateDateFromInput(inputValue: string) { this.startDepreciation.set(parse(inputValue, 'yyyy-MM', new Date())); } onMonthSelected(event: Date, datepicker: any) { this.startDepreciation.set(event); datepicker.close(); } private generateHash(asset: Asset): string { const data = JSON.stringify({ initialValue: asset.life[0]?.initial, rate: asset.depreciationMethods[0]?.rate, type: asset.depreciationMethods[0]?.type, factor: asset.depreciationMethods[0]?.factor, lifeChanges: asset.life.map(lc => ({ when: lc.when, initial: lc.initial })) }); return createHash('sha256').update(data).digest('hex'); } }