Breadcrumb e pagina commesse

This commit is contained in:
2026-02-02 15:36:06 +01:00
parent a642ac3ce7
commit e0f15e3e12
17 changed files with 396 additions and 131 deletions

View File

@ -0,0 +1,13 @@
using OAService.Service.Servizi.Implementazioni;
using TecniStamp.Domain;
using TecniStamp.Service.Interfaces;
using TecniStamp.Service.Repository;
namespace TecniStamp.Service;
public class FeatureService : TService<Feature>, IFeatureService
{
public FeatureService(ITecniStampUnitOfWork unitOfWork) : base(unitOfWork)
{
}
}

View File

@ -0,0 +1,8 @@
using OAService.Service.Servizi.Interfacce;
using TecniStamp.Domain;
namespace TecniStamp.Service.Interfaces;
public interface IFeatureService : ITService<Feature>
{
}

View File

@ -2,6 +2,7 @@
public interface IManagerService public interface IManagerService
{ {
IFeatureService FeatureService { get; set; }
IPermissionService PermissionService { get; set; } IPermissionService PermissionService { get; set; }
IRuoloService RuoloService{ get; set; } IRuoloService RuoloService{ get; set; }
ISezioneService SezioneService { get; set; } ISezioneService SezioneService { get; set; }

View File

@ -4,14 +4,16 @@ namespace TecniStamp.Service;
public class ManagerService : IManagerService public class ManagerService : IManagerService
{ {
public ManagerService(IUserService userService, ISezioneService sezioneService, IPermissionService permissionService, IRuoloService ruoloService) public ManagerService(IUserService userService, ISezioneService sezioneService, IPermissionService permissionService, IRuoloService ruoloService, IFeatureService featureService)
{ {
UtenteService = userService; UtenteService = userService;
SezioneService = sezioneService; SezioneService = sezioneService;
PermissionService = permissionService; PermissionService = permissionService;
RuoloService = ruoloService; RuoloService = ruoloService;
FeatureService = featureService;
} }
public IFeatureService FeatureService { get; set; }
public IPermissionService PermissionService { get; set; } public IPermissionService PermissionService { get; set; }
public IRuoloService RuoloService { get; set; } public IRuoloService RuoloService { get; set; }
public ISezioneService SezioneService { get; set; } public ISezioneService SezioneService { get; set; }

View File

@ -7,6 +7,7 @@
<base href="/"/> <base href="/"/>
<link rel="stylesheet" href="bootstrap/bootstrap.min.css"/> <link rel="stylesheet" href="bootstrap/bootstrap.min.css"/>
<link rel="stylesheet" href="app.css"/> <link rel="stylesheet" href="app.css"/>
<link rel="stylesheet" href="css/space.css"/>
<link rel="stylesheet" href="TecniStamp.styles.css"/> <link rel="stylesheet" href="TecniStamp.styles.css"/>
<link rel="icon" type="image/png" href="favicon.png"/> <link rel="icon" type="image/png" href="favicon.png"/>

View File

@ -1,10 +1,10 @@
@page "/anagrafiche" @page "/anagrafiche"
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using TecniStamp.Components.Widget
@using TecniStamp.Model.Common @using TecniStamp.Model.Common
@using TecniStamp.Utils @using TecniStamp.Utils
<PageTitle>Anagrafiche</PageTitle>
<Breadcrumb Items="BreadcrumbList" /> <Breadcrumb Items="BreadcrumbList" />
<main role="main"> <main role="main">

View File

@ -1,23 +1,19 @@
@page "/Anagrafiche/Operatori" @page "/Anagrafiche/Operatori"
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using TecniStamp.Domain
@using TecniStamp.Model @using TecniStamp.Model
@using TecniStamp.Model.Common
@using TecniStamp.Utils @using TecniStamp.Utils
@rendermode InteractiveServer @rendermode InteractiveServer
@inject AuthenticationStateProvider auth @inject AuthenticationStateProvider auth
<PageTitle>Operatori</PageTitle> <PageTitle>Operatori</PageTitle>
<Breadcrumb Items="BreadcrumbList" />
<div class="page-wrapper"> <main role="main">
<!-- BEGIN PAGE BODY --> <div class="container-fluid h-100 mt-5">
<div class="page-body"> <div class="row justify-content-start">
<div class="container-xl">
<div class="row row-cards"> <div class="row row-cards">
<div class="col">
<!-- Page pre-title -->
<h2 class="page-title">Operatori</h2>
</div>
<div class="col-auto ms-auto"> <div class="col-auto ms-auto">
<div class="btn-list"> <div class="btn-list">
<a href="/Anagrafiche/Operatori/Modifica" class="btn btn-primary btn-5 d-none d-sm-inline-block"> <a href="/Anagrafiche/Operatori/Modifica" class="btn btn-primary btn-5 d-none d-sm-inline-block">
@ -51,11 +47,12 @@
</div> </div>
</div> </div>
</div> </div>
</div> </main>
@code { @code {
List<UserViewModel> utenti; List<UserViewModel> utenti;
RadzenDataGrid<UserViewModel> userGrid; RadzenDataGrid<UserViewModel> userGrid;
public List<BreadcrumbViewModel> BreadcrumbList { get; set; } = new();
/// <summary> /// <summary>
/// Carica la lista degli utenti non eliminati, ordinandoli per cognome e nome. /// Carica la lista degli utenti non eliminati, ordinandoli per cognome e nome.
@ -69,6 +66,8 @@
includi: x => x.Include(y => y.Ruolo), includi: x => x.Include(y => y.Ruolo),
ordinamento: x => x.OrderBy(y => y.Cognome).ThenBy(z => z.Nome))) ordinamento: x => x.OrderBy(y => y.Cognome).ThenBy(z => z.Nome)))
.Select(x => (UserViewModel)x).ToList(); .Select(x => (UserViewModel)x).ToList();
BreadcrumbList = await BreadcrumbUtils.BuildBreadcrumbByFeature(_managerService, "Operatori_Insert");
} }
/// <summary> /// <summary>

View File

@ -2,28 +2,26 @@
@page "/Anagrafiche/Operatori/Modifica/{UserId:guid}" @page "/Anagrafiche/Operatori/Modifica/{UserId:guid}"
@using Microsoft.AspNetCore.Identity @using Microsoft.AspNetCore.Identity
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using TecniStamp.Domain @using TecniStamp.Domain
@using TecniStamp.Model @using TecniStamp.Model
@using TecniStamp.Utils
@using TecniStamp.Model.Common
@rendermode InteractiveServer @rendermode InteractiveServer
<PageTitle>@pageTitle</PageTitle> <PageTitle>@pageTitle</PageTitle>
<Breadcrumb Items="BreadcrumbList" />
<div class="page-wrapper"> <main role="main">
<!-- BEGIN PAGE HEADER --> <div class="container-fluid h-100 mt-5">
<div class="page-header d-print-none" aria-label="Page header"> <div class="row justify-content-start">
<div class="container-xl"> <div class="row row-cards">
<div class="row g-2 align-items-center">
<div class="col"> <div class="col">
<h2 class="page-title">@pageTitle</h2> <h2 class="page-title">@pageTitle</h2>
</div> </div>
</div> </div>
</div>
</div>
<!-- END PAGE HEADER -->
<!-- BEGIN PAGE BODY -->
<div class="page-body">
<div class="container-xl">
<div class="row row-cards"> <div class="row row-cards">
<div class="col-lg-12"> <div class="col-lg-12">
<div class="card"> <div class="card">
@ -87,8 +85,7 @@
</div> </div>
</div> </div>
</div> </div>
</div> </main>
@code { @code {
[Parameter] public Guid? UserId { get; set; } [Parameter] public Guid? UserId { get; set; }
@ -98,6 +95,7 @@
private string pageTitle => Model?.Id == Guid.Empty ? "Nuovo operatore" : $"Modifica operatore {Model}"; private string pageTitle => Model?.Id == Guid.Empty ? "Nuovo operatore" : $"Modifica operatore {Model}";
private List<RuoloViewModel> ruoli { get; set; } = new(); private List<RuoloViewModel> ruoli { get; set; } = new();
public List<BreadcrumbViewModel> BreadcrumbList { get; set; } = new();
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
await base.OnInitializedAsync(); await base.OnInitializedAsync();
@ -109,6 +107,8 @@
ruoli = (await _managerService.RuoloService.RicercaQueryable(x => x.Eliminato == false)) ruoli = (await _managerService.RuoloService.RicercaQueryable(x => x.Eliminato == false))
.Select(x => (RuoloViewModel)x).ToList(); .Select(x => (RuoloViewModel)x).ToList();
BreadcrumbList = await BreadcrumbUtils.BuildBreadcrumbByFeature(_managerService, "Operatori_Insert", "Modifica", "/Anagrafiche/Operatori");
} }
/// <summary> /// <summary>

View File

@ -2,6 +2,8 @@
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using TecniStamp.Model @using TecniStamp.Model
@rendermode InteractiveServer
<PageTitle>Ruoli</PageTitle> <PageTitle>Ruoli</PageTitle>
<div class="page-wrapper"> <div class="page-wrapper">

View File

@ -1,7 +1,5 @@
@page "/Anagrafiche/ruoli/Modifica" @page "/Anagrafiche/ruoli/Modifica"
@page "/Anagrafiche/ruoli/Modifica/{RuoloId:guid}" @page "/Anagrafiche/ruoli/Modifica/{RuoloId:guid}"
@using Microsoft.AspNetCore.Identity
@using Microsoft.EntityFrameworkCore
@using TecniStamp.Domain @using TecniStamp.Domain
@using TecniStamp.Model @using TecniStamp.Model

View File

@ -1,130 +1,223 @@
@page "/Carico" @page "/carico"
@using System.Globalization
@using Microsoft.EntityFrameworkCore
@using TecniStamp.Model.Carico @using TecniStamp.Model.Carico
@using TecniStamp.Model.Common
@using TecniStamp.Utils
<RadzenDataGrid Data="@righe" TItem="CalendarioRigaViewModel" <PageTitle>Carico Macchinari</PageTitle>
ColumnWidth="120px" <Breadcrumb Items="BreadcrumbList" />
AllowFiltering="false"
AllowSorting="false">
<Columns> <main role="main">
<!-- Colonna fissa --> <div class="container-fluid h-100 mt-10">
<RadzenDataGridColumn TItem="CalendarioRigaViewModel" <div class="row justify-content-start">
Property="Lavorazione" <div class="calendar-wrapper">
Title="Lavorazione" <div class="calendar-scroll">
Frozen="true" <table class="calendar-table">
Width="100px" /> <thead>
<!-- Riga MESI -->
<tr class="month-row">
<th class="sticky-col month-spacer"></th>
<!-- Colonne dinamiche: settimane --> @foreach (var m in mesiHeader)
@foreach (var settimana in settimane)
{ {
<RadzenDataGridColumn TItem="CalendarioRigaViewModel" <th class="month-cell" colspan="@m.ColSpan">
Title="@($"SETTIMANA {settimana.Numero}")" @m.Nome
Width="175px"> </th>
<Template Context="riga">
@{
riga.Settimane.TryGetValue(settimana.Numero, out var cella);
} }
</tr>
@if (cella != null) <!-- Riga SETTIMANE -->
<tr class="week-row">
<th class="sticky-col week-spacer">Lavorazione</th>
@foreach (var s in settimane)
{
<th class="week-cell">
SETTIMANA @s.Numero
</th>
}
</tr>
</thead>
<tbody>
@foreach (var riga in righe)
{
<tr>
<td class="sticky-col row-title">
@riga.Lavorazione
</td>
@foreach (var s in settimane)
{
riga.Settimane.TryGetValue(s.Numero, out var cella);
<td class="data-cell">
@if (cella is not null)
{ {
<div class="calendar-card"> <div class="calendar-card">
<div class="card-header"> <div class="card-top">
<span class="status">@cella.Stato</span> <span class="status">@cella.Stato</span>
<div class="card-actions">
<RadzenButton Text="Vedi dettaglio"
Size="ButtonSize.Small"
ButtonStyle="ButtonStyle.Light"
Click="@(() => ApriDettaglio(riga, s, cella))"/>
<RadzenButton Icon="more_horiz" <RadzenButton Icon="more_horiz"
Size="ButtonSize.Small" Size="ButtonSize.Small"
ButtonStyle="ButtonStyle.Light"/> ButtonStyle="ButtonStyle.Light"/>
</div> </div>
</div>
<div class="card-body"> <div class="card-body">
<small>Commesse: @cella.Stato</small> <div class="sub">Commesse: @cella.Commesse</div>
<div class="sub small">@cella.Ore/@cella.Capacita h</div>
</div> </div>
<RadzenProgressBar Value="@cella.Percentuale" <RadzenProgressBar Value="@cella.Percentuale"
ShowValue="false" ShowValue="true"
Unit="%" Unit="%"
Style="height:12px" ProgressBarStyle="@GetProgressStyle(cella.Percentuale)"
Color="@GetColor(cella.Percentuale)" /> Style="height:8px"/>
</div> </div>
} }
</Template> </td>
</RadzenDataGridColumn>
} }
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
</Columns> @code
</RadzenDataGrid> {
// ====== DATA ======
@code {
private List<SettimanaViewModel> settimane = new(); private List<SettimanaViewModel> settimane = new();
private List<MeseHeaderViewModel> mesiHeader = new();
private List<CalendarioRigaViewModel> righe = new(); private List<CalendarioRigaViewModel> righe = new();
protected override async Task OnInitializedAsync()
public List<BreadcrumbViewModel> BreadcrumbList { get; set; } = new();
protected override void OnInitialized()
{ {
await base.OnInitializedAsync(); settimane = GeneraSettimaneDaPrimoGennaio(2026);
settimane = GeneraSettimaneAnno(2026);
var settimanePerMese = settimane // Header mesi: raggruppa le settimane per mese (in base alla data di inizio settimana)
.GroupBy(s => new { s.Anno, Mese = s.Inizio.Month }) mesiHeader = settimane
.Select(g => new .GroupBy(s => s.Inizio.Month)
.Select(g => new MeseHeaderViewModel
{ {
Mese = g.Key.Mese, Mese = g.Key,
Nome = new DateTime(2026, g.Key.Mese, 1).ToString("MMMM").ToUpper(), Nome = new DateTime(2026, g.Key, 1).ToString("MMMM", CultureInfo.GetCultureInfo("it-IT")).ToUpperInvariant(),
Count = g.Count() ColSpan = g.Count()
}) })
.ToList(); .ToList();
// Mock righe/celle
righe = new List<CalendarioRigaViewModel> righe = new List<CalendarioRigaViewModel>
{ {
new() new()
{ {
Id = 1, Id = 1,
Lavorazione = "Taglio laser Commessa A", Lavorazione = "TAGLIO",
Settimane = new Dictionary<int, CalendarioCellaViewModel> Settimane =
{ {
[6] = new() { Ore = 16, Stato = "InCorso", Percentuale = 50 }, [3] = new() { Ore = 90, Capacita = 100, Stato = "Estremamente Saturo", Commesse = "24-24, 25-25, ..." },
[7] = new() { Ore = 24, Stato = "InCorso", Percentuale = 75 } [4] = new() { Ore = 100, Capacita = 100, Stato = "Saturo", Commesse = "16-24, 28-24, ..." }
} }
}, },
new() new()
{ {
Id = 2, Id = 2,
Lavorazione = "Saldatura Commessa A", Lavorazione = "TORNITURA",
Settimane = new Dictionary<int, CalendarioCellaViewModel> Settimane =
{ {
[7] = new() { Ore = 32, Stato = "Pianificato", Percentuale = 40}, [1] = new() { Ore = 100, Capacita = 100, Stato = "Saturo", Commesse = "15-24, 25-24, ..." },
[8] = new() { Ore = 16, Stato = "Pianificato", Percentuale = 60} [5] = new() { Ore = 60, Capacita = 100, Stato = "Non Saturo", Commesse = "35-25, 65-25, ..." }
} }
}, },
new() new()
{ {
Id = 3, Id = 3,
Lavorazione = "Verniciatura Commessa B", Lavorazione = "FRESATURA",
Settimane = new Dictionary<int, CalendarioCellaViewModel> Settimane =
{ {
[8] = new() { Ore = 40, Stato = "Pianificato", Percentuale = 90} [2] = new() { Ore = 40, Capacita = 100, Stato = "Non Saturo", Commesse = "12-11, 44-11, ..." },
[3] = new() { Ore = 100, Capacita = 100, Stato = "Saturo", Commesse = "17-22, 19-22, ..." }
} }
} }
}; };
} }
public static List<SettimanaViewModel> GeneraSettimaneAnno(int anno) protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
BreadcrumbList = await BreadcrumbUtils.BuildBreadcrumbByFeature(_managerService, "Carico_Info");
}
private void ApriDettaglio(CalendarioRigaViewModel riga, SettimanaViewModel settimana, CalendarioCellaViewModel cella)
{
// qui ci attacchi un RadzenDialogService se vuoi
// es: DialogService.Open(...)
Console.WriteLine($"Dettaglio: {riga.Lavorazione} - W{settimana.Numero} - {cella.Ore}/{cella.Capacita}");
}
private static ProgressBarStyle GetProgressStyle(int percentuale)
{
// taralo come vuoi
if (percentuale >= 100) return ProgressBarStyle.Success;
if (percentuale >= 80) return ProgressBarStyle.Warning;
return ProgressBarStyle.Info;
}
// ====== ISO WEEKS (corretto) ======
private static List<SettimanaViewModel> GeneraSettimaneISO(int anno)
{
var list = new List<SettimanaViewModel>();
// ISOWeek è disponibile su .NET 6+ (quindi ok per Blazor moderno)
int weeksInYear = ISOWeek.GetWeeksInYear(anno);
for (int w = 1; w <= weeksInYear; w++)
{
// Lunedì della settimana ISO
DateTime start = ISOWeek.ToDateTime(anno, w, DayOfWeek.Monday);
DateTime end = start.AddDays(6);
list.Add(new SettimanaViewModel
{
Anno = anno,
Numero = w,
Inizio = start,
Fine = end
});
}
return list;
}
private static List<SettimanaViewModel> GeneraSettimaneDaPrimoGennaio(int anno)
{ {
var settimane = new List<SettimanaViewModel>(); var settimane = new List<SettimanaViewModel>();
// ISO: settimana 1 = quella che contiene il 4 gennaio var start = new DateTime(anno, 1, 1);
var jan4 = new DateTime(anno, 1, 4); var endOfYear = new DateTime(anno, 12, 31);
// lunedì della settimana 1 int weekNumber = 1;
var startOfWeek1 = jan4.AddDays(-(int)(jan4.DayOfWeek == DayOfWeek.Sunday var currentStart = start;
? 6
: jan4.DayOfWeek - DayOfWeek.Monday));
var currentStart = startOfWeek1; while (currentStart <= endOfYear)
var weekNumber = 1;
while (currentStart.Year <= anno)
{ {
var currentEnd = currentStart.AddDays(6); var currentEnd = currentStart.AddDays(6);
if (currentEnd > endOfYear)
currentEnd = endOfYear;
settimane.Add(new SettimanaViewModel settimane.Add(new SettimanaViewModel
{ {
@ -140,9 +233,4 @@
return settimane; return settimane;
} }
object GetColor(int valuePercentuale)
{
return "Red";
}
} }

View File

@ -1,35 +1,118 @@
.calendar-month-header { .calendar-wrapper {
display: grid; border: 1px solid #ddd;
grid-template-columns: 250px repeat(53, 120px); border-radius: 6px;
border-bottom: 1px solid #ddd; overflow: hidden;
background: #fff;
}
.calendar-scroll {
overflow-x: auto;
overflow-y: auto;
max-height: 70vh;
}
.calendar-table {
border-collapse: collapse;
table-layout: fixed;
width: max-content; /* importantissimo per far lavorare lo scroll orizzontale */
min-width: 100%;
}
/* larghezze: devono combaciare con il tuo mock */
.calendar-table th,
.calendar-table td {
border: 1px solid #e3e3e3;
width: 120px;
min-width: 120px;
padding: 8px;
vertical-align: top;
background: #fff;
}
/* Colonna sticky (lavorazioni) */
.sticky-col {
position: sticky;
left: 0;
z-index: 3;
width: 250px !important;
min-width: 250px !important;
background: #fff;
}
/* header sticky in alto */
thead th {
position: sticky;
top: 0;
z-index: 4;
background: #f5f5f5;
font-weight: 600;
}
/* incrocio sticky (top + left) */
thead .sticky-col {
z-index: 6;
background: #f5f5f5; background: #f5f5f5;
} }
.month-header { .month-row th {
text-align: center; text-align: center;
font-weight: 600; font-size: 12px;
padding: 8px 0; letter-spacing: .5px;
border-left: 1px solid #ddd;
} }
.month-spacer { .week-row th {
border-right: 1px solid #ddd; text-align: center;
font-size: 12px;
font-weight: 500;
color: #666;
}
.row-title {
font-weight: 600;
color: #666;
text-transform: uppercase;
padding-top: 18px;
}
.data-cell {
background: #fff;
} }
.calendar-card { .calendar-card {
background: #f7f7f7; background: #f7f7f7;
border-radius: 6px; border-radius: 6px;
padding: 6px; padding: 8px;
box-shadow: inset 0 0 0 1px #ddd; box-shadow: inset 0 0 0 1px #dedede;
} }
.card-header { .card-top {
display: flex; display: flex;
align-items: start;
justify-content: space-between; justify-content: space-between;
font-size: 12px; gap: 8px;
margin-bottom: 4px; margin-bottom: 8px;
} }
.status { .status {
font-size: 12px;
font-weight: 600; font-weight: 600;
color: #444;
line-height: 1.1;
}
.card-actions {
display: flex;
gap: 6px;
align-items: center;
}
.card-body .sub {
font-size: 11px;
color: #666;
margin-bottom: 6px;
}
.card-body .small {
font-size: 10px;
color: #888;
} }

View File

@ -0,0 +1,18 @@
@page "/commesse"
@using Microsoft.EntityFrameworkCore
@using TecniStamp.Model.Common
@using TecniStamp.Utils
<PageTitle>Commesse</PageTitle>
<Breadcrumb Items="BreadcrumbList" />
@code {
public List<BreadcrumbViewModel> BreadcrumbList { get; set; } = new();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
BreadcrumbList = await BreadcrumbUtils.BuildBreadcrumbByFeature(_managerService, "Commesse_Info");
}
}

View File

@ -3,10 +3,10 @@
@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Authorization
@using Microsoft.EntityFrameworkCore @using Microsoft.EntityFrameworkCore
@using TecniStamp.Components.Widget
@using TecniStamp.Model.Common @using TecniStamp.Model.Common
@using TecniStamp.Utils @using TecniStamp.Utils
<PageTitle>Home</PageTitle>
<Breadcrumb Items="BreadcrumbList" /> <Breadcrumb Items="BreadcrumbList" />
<main role="main"> <main role="main">

View File

@ -9,6 +9,7 @@
@using Microsoft.JSInterop @using Microsoft.JSInterop
@using TecniStamp @using TecniStamp
@using TecniStamp.Components @using TecniStamp.Components
@using TecniStamp.Components.Widget
@using Radzen @using Radzen
@using Radzen.Blazor @using Radzen.Blazor

View File

@ -3,6 +3,22 @@
public class CalendarioCellaViewModel public class CalendarioCellaViewModel
{ {
public int Ore { get; set; } public int Ore { get; set; }
public int Capacita { get; set; } = 100;
public string Stato { get; set; } public string Stato { get; set; }
public string Commesse { get; set; } = "";
public int Percentuale { get; set; } public int Percentuale { get; set; }
} }
public class MeseHeaderViewModel
{
public int Mese { get; set; }
public string Nome { get; set; } = "";
public int ColSpan { get; set; }
}
public class CalendarioHeaderViewModel
{
public int Mese { get; set; }
public string Nome { get; set; }
public int Count { get; set; }
}

View File

@ -1,11 +1,25 @@
using TecniStamp.Domain; using Microsoft.EntityFrameworkCore;
using TecniStamp.Domain;
using TecniStamp.Model.Common; using TecniStamp.Model.Common;
using TecniStamp.Service.Interfaces;
namespace TecniStamp.Utils; namespace TecniStamp.Utils;
public static class BreadcrumbUtils public static class BreadcrumbUtils
{ {
public static List<BreadcrumbViewModel> BuildBreadcrumb(Sezione sezione) public static async Task<List<BreadcrumbViewModel>> BuildBreadcrumbByFeature(IManagerService _managerService, string featureName,
string? extraText = null,
string? parentUrlOverride = null)
{
var section = await _managerService.FeatureService.RicercaPer(x => x.Nome == featureName,
includi:x => x.Include(y => y.Sezione).ThenInclude(x => x.Parent));
return BuildBreadcrumb(section.Sezione, extraText, parentUrlOverride);
}
public static List<BreadcrumbViewModel> BuildBreadcrumb(
Sezione sezione,
string? extraText = null,
string? parentUrlOverride = null)
{ {
var stack = new Stack<Sezione>(); var stack = new Stack<Sezione>();
var current = sezione; var current = sezione;
@ -37,9 +51,30 @@ public static class BreadcrumbUtils
}); });
} }
breadcrumb.Last().IsActive = true; if (!string.IsNullOrWhiteSpace(extraText))
breadcrumb.Last().Url = null; {
// 🔹 rendo cliccabile il penultimo (la sezione)
var parent = breadcrumb.Last();
parent.IsActive = false;
parent.Url = parentUrlOverride;
// 🔹 aggiungo la voce finale custom
breadcrumb.Add(new BreadcrumbViewModel
{
Text = extraText,
Url = null,
IsActive = true
});
}
else
{
// comportamento originale (index)
var last = breadcrumb.Last();
last.IsActive = true;
last.Url = null;
}
return breadcrumb; return breadcrumb;
} }
} }