Allineamento da prod
This commit is contained in:
18
StandManager.Domain/Entita/InvitoEvento.cs
Normal file
18
StandManager.Domain/Entita/InvitoEvento.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using StandManager.Domain.Entita.Base;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace StandManager.Domain.Entita;
|
||||||
|
|
||||||
|
public class InvitoEvento : EntitaBase
|
||||||
|
{
|
||||||
|
[ForeignKey(nameof(Evento))]
|
||||||
|
public Guid? EventoId { get; set; }
|
||||||
|
public Evento Evento{ get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(Cliente))]
|
||||||
|
public Guid? ClienteId { get; set; }
|
||||||
|
public Cliente Cliente { get; set; }
|
||||||
|
|
||||||
|
[InverseProperty(nameof(IscrizioneEvento.InvitoEvento))]
|
||||||
|
public List<IscrizioneEvento> IscrizioniEvento { get; set; }
|
||||||
|
}
|
||||||
@ -3,20 +3,6 @@ using System.ComponentModel.DataAnnotations.Schema;
|
|||||||
|
|
||||||
namespace StandManager.Domain.Entita;
|
namespace StandManager.Domain.Entita;
|
||||||
|
|
||||||
public class InvitoEvento : EntitaBase
|
|
||||||
{
|
|
||||||
[ForeignKey(nameof(Evento))]
|
|
||||||
public Guid? EventoId { get; set; }
|
|
||||||
public Evento Evento{ get; set; }
|
|
||||||
|
|
||||||
[ForeignKey(nameof(Cliente))]
|
|
||||||
public Guid? ClienteId { get; set; }
|
|
||||||
public Cliente Cliente { get; set; }
|
|
||||||
|
|
||||||
[InverseProperty(nameof(IscrizioneEvento.InvitoEvento))]
|
|
||||||
public List<IscrizioneEvento> IscrizioniEvento { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class IscrizioneEvento : EntitaBase
|
public class IscrizioneEvento : EntitaBase
|
||||||
{
|
{
|
||||||
[ForeignKey(nameof(Evento))]
|
[ForeignKey(nameof(Evento))]
|
||||||
|
|||||||
@ -3,7 +3,6 @@ using StandManager.Domain.Entita;
|
|||||||
|
|
||||||
namespace StandManager.Service.Interfaces;
|
namespace StandManager.Service.Interfaces;
|
||||||
|
|
||||||
public interface IIscrizioneEventoService : ITService<IscrizioneEvento>
|
public interface IIscrizioneEventoService : ITService<IscrizioneEvento>{
|
||||||
{
|
|
||||||
Task<IscrizioneEvento> Salva(IscrizioneEvento model);
|
Task<IscrizioneEvento> Salva(IscrizioneEvento model);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,14 +6,14 @@
|
|||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<!-- NAV MENU MANAGEMENT -->
|
<!-- NAV MENU MANAGEMENT -->
|
||||||
<header class="navbar navbar-expand-md d-print-none" >
|
<header class="navbar navbar-expand-md d-print-none">
|
||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-menu" aria-controls="navbar-menu" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-menu" aria-controls="navbar-menu" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
|
<h1 class="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
|
||||||
<a href="/">
|
<a href="/">
|
||||||
<img src="/Logo_dac.png" width="80" class="">
|
<img src="/Logo_dac.png" width="80" class="">
|
||||||
</a>
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
<AuthorizeView>
|
<AuthorizeView>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
@using StandManager.Components.Pages.Management
|
@using StandManager.Components.Pages.Management
|
||||||
|
|
||||||
<PublicHeader />
|
|
||||||
|
|
||||||
@Body
|
@Body
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.js"></script>
|
||||||
|
<script src="/js/qrScanner.js"></script>
|
||||||
@ -1,4 +1,5 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
|
@page "/Home"
|
||||||
@page "/{invitationId:guid}"
|
@page "/{invitationId:guid}"
|
||||||
@layout PublicLayout
|
@layout PublicLayout
|
||||||
|
|
||||||
@ -12,6 +13,40 @@
|
|||||||
|
|
||||||
<PageTitle>Iscrizioni Evento</PageTitle>
|
<PageTitle>Iscrizioni Evento</PageTitle>
|
||||||
|
|
||||||
|
<AuthorizeView>
|
||||||
|
<Authorized>
|
||||||
|
<header class="navbar navbar-expand-lg navbar-transparent py-3">
|
||||||
|
<div class="container">
|
||||||
|
<div class="collapse navbar-collapse">
|
||||||
|
<nav class="navbar-nav ms-auto">
|
||||||
|
<div class="nav-item">
|
||||||
|
<a class="nav-link" href="/management"><span class="nav-link-title">Management</span></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="nav-item">
|
||||||
|
<a class="nav-link" href="/scan"><span class="nav-link-title">Scansione</span></a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</Authorized>
|
||||||
|
|
||||||
|
<NotAuthorized>
|
||||||
|
<header class="navbar navbar-expand-lg navbar-transparent py-3">
|
||||||
|
<div class="container">
|
||||||
|
<div class="collapse navbar-collapse">
|
||||||
|
<nav class="navbar-nav ms-auto">
|
||||||
|
<div class="nav-item">
|
||||||
|
<a class="nav-link" href="/management"><span class="nav-link-title">Management</span></a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</NotAuthorized>
|
||||||
|
</AuthorizeView>
|
||||||
|
|
||||||
<header class="hero pb-0">
|
<header class="hero pb-0">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@if (invitationId.HasValue && invito != null)
|
@if (invitationId.HasValue && invito != null)
|
||||||
@ -128,13 +163,12 @@
|
|||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
base.OnInitializedAsync();
|
base.OnInitializedAsync();
|
||||||
|
|
||||||
invito = invitationId.GetValueOrDefault() != Guid.Empty
|
invito = invitationId.GetValueOrDefault() != Guid.Empty
|
||||||
? await _managerService.InvitoEventoService.RicercaPer(x => x.Id == invitationId && x.Eliminato == false,
|
? await _managerService.InvitoEventoService.RicercaPer(x => x.Id == invitationId && x.Eliminato == false,
|
||||||
includi: x => x.Include(y => y.Evento).Include(y => y.Cliente).ThenInclude(y => y.Destinazioni).Include(y => y.IscrizioniEvento))
|
includi: x => x.Include(y => y.Evento).Include(y => y.Cliente).ThenInclude(y => y.Destinazioni).Include(y => y.IscrizioniEvento))
|
||||||
: new();
|
: new();
|
||||||
|
|
||||||
if(invito == null)
|
if (invito == null)
|
||||||
invalidCode = "Il codice inserito non risulta corretto";
|
invalidCode = "Il codice inserito non risulta corretto";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -177,7 +211,6 @@
|
|||||||
await _managerService.IscrizioneEventoService.Salva(model);
|
await _managerService.IscrizioneEventoService.Salva(model);
|
||||||
|
|
||||||
_navManager.NavigateTo($"/");
|
_navManager.NavigateTo($"/");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@
|
|||||||
model = destinazione.Map(model);
|
model = destinazione.Map(model);
|
||||||
|
|
||||||
if (destinazione.AgenteId.GetValueOrDefault() != Guid.Empty)
|
if (destinazione.AgenteId.GetValueOrDefault() != Guid.Empty)
|
||||||
model.AgenteId = destinazione.AgenteId;
|
model.Agente = await _managerService.UtenteService.RicercaPer(x => x.Id == destinazione.AgenteId);
|
||||||
|
|
||||||
await _managerService.DestinazioneService.Salva(model, idClaim);
|
await _managerService.DestinazioneService.Salva(model, idClaim);
|
||||||
_dialogService.Close(true);
|
_dialogService.Close(true);
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
@attribute [Authorize]
|
@attribute [Authorize]
|
||||||
@page "/management/Eventi"
|
@page "/management/Eventi"
|
||||||
|
@using Microsoft.EntityFrameworkCore
|
||||||
@using StandManager.Model
|
@using StandManager.Model
|
||||||
|
|
||||||
@rendermode InteractiveServer
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
@inject TooltipService tooltipService
|
||||||
|
|
||||||
<PageTitle>Eventi</PageTitle>
|
<PageTitle>Eventi</PageTitle>
|
||||||
|
|
||||||
<div class="page-wrapper">
|
<div class="page-wrapper">
|
||||||
@ -37,8 +40,9 @@
|
|||||||
|
|
||||||
<RadzenDataGridColumn Context="evento" Filterable="false" Sortable="false" TextAlign="TextAlign.Right" Width="250px">
|
<RadzenDataGridColumn Context="evento" Filterable="false" Sortable="false" TextAlign="TextAlign.Right" Width="250px">
|
||||||
<Template Context="evento">
|
<Template Context="evento">
|
||||||
<RadzenButton Icon="edit" ButtonStyle="ButtonStyle.Light" Variant="Variant.Flat" Size="ButtonSize.Medium" class="rz-my-1 rz-ms-1" Click="@(args => EditRow(evento))" @onclick:stopPropagation="true" />
|
<RadzenButton Icon="calendar_clock" ButtonStyle="ButtonStyle.Light" Variant="Variant.Flat" Size="ButtonSize.Medium" class="rz-my-1 rz-ms-1" Click="@(args => SendInvitation(evento))" @onclick:stopPropagation="true" MouseEnter="@(args => ShowTooltip(args, new TooltipOptions() { Text = "Invia invito" }))" />
|
||||||
<RadzenButton Icon="delete" ButtonStyle="ButtonStyle.Danger" Variant="Variant.Flat" Size="ButtonSize.Medium" Shade="Shade.Lighter" class="rz-my-1 rz-ms-1" Click="@(args => DeleteRow(evento))" @onclick:stopPropagation="true" />
|
<RadzenButton Icon="edit" ButtonStyle="ButtonStyle.Light" Variant="Variant.Flat" Size="ButtonSize.Medium" class="rz-my-1 rz-ms-1" Click="@(args => EditRow(evento))" @onclick:stopPropagation="true" MouseEnter="@(args => ShowTooltip(args, new TooltipOptions() { Text = "Modifica evento" }))" />
|
||||||
|
<RadzenButton Icon="delete" ButtonStyle="ButtonStyle.Danger" Variant="Variant.Flat" Size="ButtonSize.Medium" Shade="Shade.Lighter" class="rz-my-1 rz-ms-1" Click="@(args => DeleteRow(evento))" @onclick:stopPropagation="true" MouseEnter="@(args => ShowTooltip(args, new TooltipOptions() { Text = "Rimuovi evento" }))" />
|
||||||
</Template>
|
</Template>
|
||||||
</RadzenDataGridColumn>
|
</RadzenDataGridColumn>
|
||||||
</Columns>
|
</Columns>
|
||||||
@ -77,4 +81,18 @@
|
|||||||
eventi = (await _managerService.EventoService.RicercaQueryable(x => x.Eliminato == false)).Select(x => (EventoViewModel)x).ToList();
|
eventi = (await _managerService.EventoService.RicercaQueryable(x => x.Eliminato == false)).Select(x => (EventoViewModel)x).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SendInvitation(EventoViewModel evento)
|
||||||
|
{
|
||||||
|
var ok = await _dialogService.Confirm($"Vuoi davvero invitare i clienti all'evento {evento.Titolo}?", "Conferma invito", new ConfirmOptions { OkButtonText = "Sì", CancelButtonText = "No", Width = "400px" });
|
||||||
|
|
||||||
|
if (ok == true)
|
||||||
|
{
|
||||||
|
_dialogService.Close();
|
||||||
|
//Invito
|
||||||
|
await _dialogService.OpenAsync<Eventi_Inviti>("Inviti", new Dictionary<string, object>() { { "eventoId", evento.Id } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowTooltip(ElementReference elementReference, TooltipOptions options = null) => tooltipService.Open(elementReference, options.Text, options);
|
||||||
}
|
}
|
||||||
46
StandManager/Components/Pages/Management/Eventi_Inviti.razor
Normal file
46
StandManager/Components/Pages/Management/Eventi_Inviti.razor
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
@using Microsoft.EntityFrameworkCore
|
||||||
|
|
||||||
|
<RadzenStack Gap="1rem" class="rz-m-12">
|
||||||
|
<RadzenProgressBar Value="@counter" Max="@clientiTotali" Unit="@counterLabel" AriaLabel="" />
|
||||||
|
</RadzenStack>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter] private Dialog _dialog { get; set; }
|
||||||
|
[Parameter] public Guid eventoId { get; set; } = Guid.Empty;
|
||||||
|
|
||||||
|
private int clientiTotali { get; set; } = 0;
|
||||||
|
private int counter { get; set; } = 0;
|
||||||
|
private string counterLabel{ get; set; } = string.Empty;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
base.OnInitializedAsync();
|
||||||
|
|
||||||
|
var evento = await _managerService.EventoService.RicercaPer(x => x.Eliminato == false && x.Id == eventoId);
|
||||||
|
var clienti = await _managerService.ClienteService.RicercaQueryable(x => x.Eliminato == false);
|
||||||
|
var invitati = await _managerService.InvitoEventoService.RicercaQueryable(x => x.Eliminato == false && x.EventoId == eventoId, includi: y => y.Include(z => z.Cliente));
|
||||||
|
|
||||||
|
var clientiInvitatiIds = invitati.Select(x => x.ClienteId).ToList();
|
||||||
|
var userId = await MembershipUtils.GetUserId(_auth);
|
||||||
|
|
||||||
|
counter = 0;
|
||||||
|
clientiTotali = clienti.Count(c => !clientiInvitatiIds.Contains(c.Id));
|
||||||
|
|
||||||
|
foreach (var c in clienti.Where(c => !clientiInvitatiIds.Contains(c.Id)))
|
||||||
|
{
|
||||||
|
var invito = new InvitoEvento
|
||||||
|
{
|
||||||
|
ClienteId = c.Id,
|
||||||
|
EventoId = evento.Id,
|
||||||
|
Eliminato = false
|
||||||
|
};
|
||||||
|
await _managerService.InvitoEventoService.Salva(invito, userId);
|
||||||
|
counter += 1;
|
||||||
|
counterLabel = " di " + clientiTotali;
|
||||||
|
StateHasChanged();
|
||||||
|
await Task.Delay(10); // Simula un ritardo per l'invio dell'email
|
||||||
|
}
|
||||||
|
|
||||||
|
_dialogService.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,13 +27,13 @@
|
|||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<RadzenDataGrid @ref="userGrid" AllowFiltering="true" AllowColumnResize="true" AllowAlternatingRows="false" FilterMode="FilterMode.CheckBoxList" AllowSorting="true" PageSize="5"
|
<RadzenDataGrid @ref="userGrid" AllowFiltering="true" AllowColumnResize="true" AllowAlternatingRows="false" FilterMode="FilterMode.CheckBoxList" AllowSorting="true" PageSize="15"
|
||||||
AllowPaging="true" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true"
|
AllowPaging="true" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true"
|
||||||
Data="@utenti" ColumnWidth="300px" LogicalFilterOperator="LogicalFilterOperator.Or" SelectionMode="DataGridSelectionMode.Single">
|
Data="@utenti" ColumnWidth="300px" LogicalFilterOperator="LogicalFilterOperator.Or" SelectionMode="DataGridSelectionMode.Single">
|
||||||
<Columns>
|
<Columns>
|
||||||
<RadzenDataGridColumn Property="@nameof(Utente.Id)" Filterable="false" Title="ID" Width="80px" TextAlign="TextAlign.Center" />
|
<RadzenDataGridColumn Property="@nameof(Utente.Id)" Filterable="false" Title="ID" Width="80px" TextAlign="TextAlign.Center" />
|
||||||
<RadzenDataGridColumn Property="@nameof(Utente.Nome)" Title="First Name" Width="160px" />
|
<RadzenDataGridColumn Property="@nameof(Utente.Nome)" Title="Nome" Width="160px" />
|
||||||
<RadzenDataGridColumn Property="@nameof(Utente.Cognome)" Title="Last Name" Width="160px" />
|
<RadzenDataGridColumn Property="@nameof(Utente.Cognome)" Title="Cognome" Width="160px" />
|
||||||
<RadzenDataGridColumn Property="@nameof(Utente.Email)" Title="Mail" Width="200px" />
|
<RadzenDataGridColumn Property="@nameof(Utente.Email)" Title="Mail" Width="200px" />
|
||||||
|
|
||||||
<RadzenDataGridColumn Context="order" Filterable="false" Sortable="false" TextAlign="TextAlign.Right">
|
<RadzenDataGridColumn Context="order" Filterable="false" Sortable="false" TextAlign="TextAlign.Right">
|
||||||
|
|||||||
110
StandManager/Components/Pages/Scan.razor
Normal file
110
StandManager/Components/Pages/Scan.razor
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
@page "/scan"
|
||||||
|
@using StandManager.Components.Layout
|
||||||
|
@layout PublicLayout
|
||||||
|
|
||||||
|
@inject IJSRuntime JS
|
||||||
|
@inject BodyClassService BodyClass
|
||||||
|
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
|
||||||
|
<PageTitle>Scan</PageTitle>
|
||||||
|
|
||||||
|
<header class="hero pb-0">
|
||||||
|
<div class="container">
|
||||||
|
<div class="mb-3">
|
||||||
|
<video @ref="videoRef" autoplay playsinline style="width:100%;max-width:400px;border:1px solid #ccc;border-radius:8px;"></video>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary" @onclick="StartScan" disabled="@isScanning">
|
||||||
|
Avvia scansione
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn btn-secondary" @onclick="StopScan" disabled="@(!isScanning)">
|
||||||
|
Ferma
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(lastResult))
|
||||||
|
{
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<strong>QR letto:</strong> @lastResult
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrWhiteSpace(errorMessage))
|
||||||
|
{
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<strong>Errore:</strong> @errorMessage
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private ElementReference videoRef;
|
||||||
|
private DotNetObjectReference<Scan>? objRef;
|
||||||
|
private bool isScanning;
|
||||||
|
private string? lastResult;
|
||||||
|
private string? errorMessage;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
objRef = DotNetObjectReference.Create(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartScan()
|
||||||
|
{
|
||||||
|
errorMessage = null;
|
||||||
|
lastResult = null;
|
||||||
|
|
||||||
|
if (isScanning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
isScanning = true;
|
||||||
|
await JS.InvokeVoidAsync("qrScanner.start", videoRef, objRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StopScan()
|
||||||
|
{
|
||||||
|
if (!isScanning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
isScanning = false;
|
||||||
|
await JS.InvokeVoidAsync("qrScanner.stop", videoRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
[JSInvokable]
|
||||||
|
public Task OnQrDecoded(string text)
|
||||||
|
{
|
||||||
|
lastResult = text;
|
||||||
|
isScanning = false;
|
||||||
|
StateHasChanged();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
[JSInvokable]
|
||||||
|
public Task OnQrError(string message)
|
||||||
|
{
|
||||||
|
errorMessage = message;
|
||||||
|
isScanning = false;
|
||||||
|
StateHasChanged();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
if (isScanning)
|
||||||
|
{
|
||||||
|
await StopScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
objRef?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
await BodyClass.SetBodyClass("body-marketing body-gradient");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,21 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Authorization
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
|
|
||||||
<Router AppAssembly="@typeof(Program).Assembly">
|
<CascadingAuthenticationState>
|
||||||
<Found Context="routeData">
|
<Router AppAssembly="@typeof(Program).Assembly">
|
||||||
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
|
<Found Context="routeData">
|
||||||
<NotAuthorized>
|
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)">
|
||||||
<RedirectToLogin />
|
<NotAuthorized>
|
||||||
</NotAuthorized>
|
<RedirectToLogin />
|
||||||
</AuthorizeRouteView>
|
</NotAuthorized>
|
||||||
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
</AuthorizeRouteView>
|
||||||
</Found>
|
|
||||||
</Router>
|
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||||
|
</Found>
|
||||||
|
|
||||||
|
<NotFound>
|
||||||
|
<LayoutView Layout="@typeof(Layout.MainLayout)">
|
||||||
|
<p>Pagina non trovata.</p>
|
||||||
|
</LayoutView>
|
||||||
|
</NotFound>
|
||||||
|
</Router>
|
||||||
|
</CascadingAuthenticationState>
|
||||||
78
StandManager/wwwroot/js/qrScanner.js
Normal file
78
StandManager/wwwroot/js/qrScanner.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
window.qrScanner = (function () {
|
||||||
|
let animationFrameId = null;
|
||||||
|
let currentStream = null;
|
||||||
|
|
||||||
|
async function start(videoElement, dotNetRef) {
|
||||||
|
try {
|
||||||
|
// chiede accesso alla camera (di solito quella posteriore su mobile)
|
||||||
|
const constraints = {
|
||||||
|
video: {
|
||||||
|
facingMode: "environment"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
currentStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
|
videoElement.srcObject = currentStream;
|
||||||
|
await videoElement.play();
|
||||||
|
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
const tick = async () => {
|
||||||
|
if (!videoElement || videoElement.readyState !== videoElement.HAVE_ENOUGH_DATA) {
|
||||||
|
animationFrameId = requestAnimationFrame(tick);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.width = videoElement.videoWidth;
|
||||||
|
canvas.height = videoElement.videoHeight;
|
||||||
|
|
||||||
|
ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
|
||||||
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
const code = jsQR(imageData.data, canvas.width, canvas.height, {
|
||||||
|
inversionAttempts: "dontInvert"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (code) {
|
||||||
|
console.log("QR trovato:", code.data);
|
||||||
|
// Chiama il metodo C# marcato [JSInvokable]
|
||||||
|
await dotNetRef.invokeMethodAsync("OnQrDecoded", code.data);
|
||||||
|
|
||||||
|
stop(videoElement); // ferma appena trovato
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
animationFrameId = requestAnimationFrame(tick);
|
||||||
|
};
|
||||||
|
|
||||||
|
tick();
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Errore avvio camera:", err);
|
||||||
|
if (dotNetRef) {
|
||||||
|
await dotNetRef.invokeMethodAsync("OnQrError", err.message ?? "Errore sconosciuto");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop(videoElement) {
|
||||||
|
if (animationFrameId) {
|
||||||
|
cancelAnimationFrame(animationFrameId);
|
||||||
|
animationFrameId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentStream) {
|
||||||
|
currentStream.getTracks().forEach(t => t.stop());
|
||||||
|
currentStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoElement) {
|
||||||
|
videoElement.srcObject = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: start,
|
||||||
|
stop: stop
|
||||||
|
};
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user