api settings

This commit is contained in:
sdarbinyan
2026-05-13 10:45:18 +04:00
parent 985ec5db7f
commit e9b50078fe
6 changed files with 116 additions and 4 deletions

View File

@@ -11,6 +11,10 @@
<div class="card__body">
<!-- @if (settingsError()) {
<p class="field__hint" role="status">{{ settingsError() }}</p>
} -->
<!-- Fastcheck number + new -->
<div class="field">
<label class="field__label" for="fcNumber">

View File

@@ -2,7 +2,7 @@
import { FormsModule } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { FastcheckService } from '../../fastcheck.service';
import { FASTCHECK_API } from '../../api';
import { FASTCHECK_API, QR_VITANOVA_API } from '../../api';
import { TranslatePipe } from '../../translate/translate.pipe';
import { TranslationService } from '../../translate/translation.service';
@@ -26,6 +26,27 @@ interface CheckFastcheckResponse {
firetransactionID: string;
}
/**
* Response of GET /api/settings?id=<partnerId>.
* Shape is defensive — backend may include min/max limits and/or an
* already-active fastcheck (or QR) for this partner so the page can autofill.
*/
interface SettingsResponse {
minAmount?: number;
maxAmount?: number;
// Possible active fastcheck data — backend may use different casing.
fastcheck?: string;
fastcheckNumber?: string;
code?: string;
amount?: number;
note?: string;
status?: string;
// QR-side info (not rendered on this page yet, but accepted for future use).
qrId?: string;
qrUrl?: string;
payload?: string;
}
@Component({
selector: 'app-fastcheck-page',
imports: [FormsModule, TranslatePipe],
@@ -58,6 +79,10 @@ export class FastcheckPage {
return `https://qr.vitanova.network/?id=${encodeURIComponent(id)}`;
});
// Non-blocking settings hint shown above the form when /settings fails.
settingsError = signal<string>('');
settingsLoaded = signal<boolean>(false);
popupOpen = signal<boolean>(false);
popupLoading = signal<boolean>(false);
popupError = signal<string>('');
@@ -120,6 +145,75 @@ export class FastcheckPage {
this.fastcheckNumber.set(masked);
if (digits.length === 18) this.lookupFastcheck(masked);
}
// Always call settings on each load — may return active check data to autofill.
this.loadSettings(!!created || !!iidParam);
}
/**
* GET /api/settings?id=<partnerId>. Idempotent — applies response to the
* current UI without creating any new check. Errors are non-blocking.
*
* @param alreadyAutofilled - true when constructor already populated the form
* from nav state / iid; in that case settings must not overwrite it.
*/
private loadSettings(alreadyAutofilled: boolean): void {
const id = this.partnerId();
if (!id) {
this.settingsError.set(this.t('errors.settings_missing_id'));
this.settingsLoaded.set(true);
return;
}
const url = `${QR_VITANOVA_API}/settings?id=${encodeURIComponent(id)}`;
this.http.get<SettingsResponse>(url).subscribe({
next: (res) => {
this.settingsLoaded.set(true);
this.settingsError.set('');
this.applySettings(res ?? {}, alreadyAutofilled);
},
error: () => {
// Non-blocking: keep manual mode available.
this.settingsLoaded.set(true);
this.settingsError.set(this.t('errors.settings_failed'));
}
});
}
/** Apply settings response to UI state without creating any new check. */
private applySettings(res: SettingsResponse, alreadyAutofilled: boolean): void {
// Active check autofill — only if user hasn't already got data on screen.
if (alreadyAutofilled) return;
const rawNumber = res.fastcheck ?? res.fastcheckNumber ?? '';
if (rawNumber) {
const digits = String(rawNumber).replace(/\D/g, '').slice(0, 18);
if (digits.length > 0) {
const groups: string[] = [];
for (let i = 0; i < digits.length; i += 6) groups.push(digits.slice(i, i + 6));
this.fastcheckNumber.set(groups.join('-'));
if (digits.length === 18) {
// Trigger the regular lookup so amount/code-enabled stay consistent.
this.lookupFastcheck(groups.join('-'));
}
}
}
if (typeof res.amount === 'number') {
this.fastcheckAmount.set(res.amount);
}
if (typeof res.code === 'string' && res.code) {
const codeDigits = res.code.replace(/\D/g, '').slice(0, 6);
this.fastcheckCode.set(codeDigits);
this.codeEnabled.set(true);
}
// Final statuses end the flow — no polling, no new requests.
const status = (res.status ?? '').toUpperCase();
if (status === 'COMPLETED' || status === 'APPROVED') {
this.paid.set(true);
}
}
pay(): void {