Files
QRPayWeb/QRPay/Pages/Home.razor
T
Michal Vrba d8e94fd985 Add /qr route and refactor payment QR code generation
Introduce a dedicated /qr route for QR code preview via query params.
Add PaymentRequest model for payment data and validation, replacing
the old inline model. Extract QR code generation to PaymentQrCodeService
for reuse. Refactor Home.razor to use new model/service and improve
validation/UI. Add EmptyLayout for minimal layout needs. Update
.gitignore and _Imports.razor for new structure.
2026-03-16 07:36:13 +01:00

204 lines
6.9 KiB
Plaintext

@page "/"
@inject IJSRuntime JSRuntime
@using QRPay.Models
@using QRPay.Services
@implements IDisposable
<PageTitle>QRPay - QR Payment Generator</PageTitle>
<div class="qrpay-container">
@if (!_showQRCode)
{
<div class="qrpay-card">
<div class="qrpay-header">
<div class="qrpay-logo">
<img src="logo.svg" alt="QRPay Logo" />
</div>
<h1 class="qrpay-title">QRPay</h1>
<p class="qrpay-subtitle">Generate QR payment codes instantly</p>
</div>
<EditForm Model="@paymentModel" OnValidSubmit="@GenerateQRCode">
<DataAnnotationsValidator />
<div class="qrpay-form">
<div class="form-group">
<label for="name" class="form-label">Beneficiary Name</label>
<InputText @bind-Value="paymentModel.Name" class="form-control" id="name" placeholder="Enter beneficiary name" />
<ValidationMessage For="@(() => paymentModel.Name)" class="validation-message" />
</div>
<div class="form-group">
<label for="iban" class="form-label">IBAN</label>
<InputText @bind-Value="paymentModel.IBAN" class="form-control" id="iban" placeholder="Enter IBAN (e.g., SK89 7500 0000 0000 1234 5671)" />
<ValidationMessage For="@(() => paymentModel.IBAN)" class="validation-message" />
</div>
<div class="form-group">
<label for="amount" class="form-label">Amount (EUR)</label>
<InputNumber @bind-Value="paymentModel.Amount" class="form-control" id="amount" placeholder="0.00" step="0.01" />
<ValidationMessage For="@(() => paymentModel.Amount)" class="validation-message" />
</div>
<div class="form-group">
<label for="remark" class="form-label">Payment Reference (Optional)</label>
<InputText @bind-Value="paymentModel.Remark" class="form-control" id="remark" placeholder="Enter payment reference or note" />
<ValidationMessage For="@(() => paymentModel.Remark)" class="validation-message" />
</div>
<button type="submit" class="btn-base btn-generate" disabled="@_isGenerating">
@if (_isGenerating)
{
<span class="spinner"></span>
<span>Generating...</span>
}
else
{
<span>Generate QR Code</span>
}
</button>
</div>
</EditForm>
</div>
}
else
{
<div class="qrcode-container">
<div class="qrcode-header">
<h2>Payment QR Code</h2>
<p>Scan this code with your banking app</p>
</div>
<div class="qrcode-display">
<img id="qrcode-image" src="@QRCodeStr" alt="Payment QR Code" />
<button class="btn-copy @(_isCopying ? "copying" : "")" @onclick="@CopyQRCodeToClipboard" disabled="@_isCopying">
@if (_isCopying)
{
<span class="copy-spinner"></span>
<span>Copying...</span>
}
else if (_copySuccess)
{
<span>✓ Copied!</span>
}
else
{
<span>📋 Copy QR Code</span>
}
</button>
</div>
<div class="payment-details">
<div class="detail-row">
<span class="detail-label">To:</span>
<span class="detail-value">@(string.IsNullOrWhiteSpace(paymentModel.Name) ? "Not provided" : paymentModel.Name)</span>
</div>
<div class="detail-row">
<span class="detail-label">Amount:</span>
<span class="detail-value">€@paymentModel.Amount?.ToString("F2")</span>
</div>
@if (!string.IsNullOrWhiteSpace(paymentModel.Remark))
{
<div class="detail-row">
<span class="detail-label">Reference:</span>
<span class="detail-value">@paymentModel.Remark</span>
</div>
}
</div>
<button class="btn-base btn-back" @onclick="@GoBack">
<span>← Create New Payment</span>
</button>
</div>
}
</div>
@code {
private PaymentRequest paymentModel = new();
private string QRCodeStr = string.Empty;
private bool _showQRCode;
private bool _isGenerating;
private bool _isCopying;
private bool _copySuccess;
private System.Threading.Timer? _copySuccessTimer;
public async Task GenerateQRCode()
{
_isGenerating = true;
StateHasChanged();
await Task.Delay(500);
try
{
QRCodeStr = string.Empty;
if (!string.IsNullOrWhiteSpace(paymentModel.IBAN))
{
QRCodeStr = PaymentQrCodeService.GenerateDataUri(paymentModel);
_showQRCode = true;
}
}
finally
{
_isGenerating = false;
StateHasChanged();
}
}
public async Task CopyQRCodeToClipboard()
{
try
{
_isCopying = true;
_copySuccess = false;
StateHasChanged();
var success = await JSRuntime.InvokeAsync<bool>("copyImageToClipboardSmall", "qrcode-image");
_isCopying = false;
if (success)
{
_copySuccess = true;
StateHasChanged();
_copySuccessTimer?.Dispose();
_copySuccessTimer = new System.Threading.Timer(_ =>
{
InvokeAsync(() =>
{
_copySuccess = false;
StateHasChanged();
});
}, null, TimeSpan.FromSeconds(3), Timeout.InfiniteTimeSpan);
}
else
{
StateHasChanged();
}
}
catch (Exception)
{
_isCopying = false;
StateHasChanged();
}
}
public void GoBack()
{
_showQRCode = false;
QRCodeStr = string.Empty;
paymentModel = new PaymentRequest();
_copySuccess = false;
_copySuccessTimer?.Dispose();
StateHasChanged();
}
public void Dispose()
{
_copySuccessTimer?.Dispose();
}
}