This commit is contained in:
2026-02-06 17:57:06 +01:00
parent d885903182
commit 75e567e7cc
9 changed files with 467 additions and 6 deletions

View File

@ -17,4 +17,7 @@ public class CommessaPosizione : EntitaBase
[ForeignKey(nameof(Commessa))] [ForeignKey(nameof(Commessa))]
public Guid? CommessaId { get; set; } public Guid? CommessaId { get; set; }
public Commessa Commessa { get; set; } public Commessa Commessa { get; set; }
[InverseProperty(nameof(CommessaPosizioneLavorazione.CommessaPosizione))]
public List<CommessaPosizioneLavorazione> PosizioneLavorazioni { get; set; }
} }

View File

@ -0,0 +1,117 @@
@page "/commesse/modifica/DettaglioLavorazioni"
@page "/commesse/modifica/DettaglioLavorazioni/{CommessaPosizioneId:guid}/{CommessaId:guid}"
@using Microsoft.EntityFrameworkCore
@using TecniStamp.Model.Commesse
@using TecniStamp.Model.Common
@using TecniStamp.Utils
@rendermode InteractiveServer
<PageTitle>Dettaglio Lavorazioni</PageTitle>
<Breadcrumb Items="BreadcrumbList" />
<style>
.zoom-icon {
cursor: pointer;
transition: transform 0.2s ease;
}
.zoom-icon:hover {
transform: scale(1.2);
}
.zoom-icon:active, .zoom-icon:focus {
outline: none;
}
</style>
<div class="container-fluid p-0 mt-5">
<div class="card border-0 shadow-none" style="width: 100%;">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="m-0 fw-bold" style="font-size: 1rem;">Dettaglio Lavorazioni posizione</h5>
<div class="d-flex align-items-end gap-3">
<RadzenIcon Icon="calendar_month"
class="zoom-icon"
Style="font-size: 2rem; color: #3a4b67; margin-bottom: 2px;"
@onclick="@goToCalendar" />
<div class="d-flex flex-column">
<label class="text-muted mb-1" style="font-size: 12px;">Data ultima modifica:</label>
<RadzenTextBox Disabled="true" Style="width: 130px; height: 30px;" />
</div>
<div class="d-flex flex-column">
<label class="text-muted mb-1" style="font-size: 12px;">Data ultimo Check:</label>
<RadzenTextBox Disabled="true" Style="width: 130px; height: 30px;" />
</div>
</div>
</div>
<div class="mb-3">
<RadzenDataGrid @ref="posizioneLavorazioniGrid"
AllowFiltering="true"
AllowColumnResize="true"
Data="@CommessePosLavList"
TItem="CommessaPosizioneLavorazioneViewModel"
Style="width: 100%;">
<Columns>
<RadzenDataGridColumn TItem="CommessaPosizioneLavorazioneViewModel" Property="DescLavorazione" Title="Lavorazione" />
<RadzenDataGridColumn TItem="CommessaPosizioneLavorazioneViewModel" Property="PrimoTempoPresidiato" Title="1° Tempo Presidiato" />
<RadzenDataGridColumn TItem="CommessaPosizioneLavorazioneViewModel" Property="TempoNonPresidiato" Title="Tempo non Presidiato" />
<RadzenDataGridColumn TItem="CommessaPosizioneLavorazioneViewModel" Property="SecondoTempoPresidiato" Title="2° Tempo Presidiato" />
<RadzenDataGridColumn TItem="CommessaPosizioneLavorazioneViewModel" Property="NomeMacchinario" Title="Macchinario" />
<RadzenDataGridColumn TItem="CommessaPosizioneLavorazioneViewModel" Property="Spostabile" Title="Non Spostabile" />
<RadzenDataGridColumn TItem="CommessaPosizioneLavorazioneViewModel" Property="DataInizioPrevista" Title="Data Inizio Prev." />
<RadzenDataGridColumn TItem="CommessaPosizioneLavorazioneViewModel" Property="DataFinePrevista" Title="Data Fine Prev." />
</Columns>
</RadzenDataGrid>
</div>
<div class="d-flex justify-content-end gap-2">
<div class="col-2 mb-3">
<button type="button" class="btn btn-default w-100" @onclick="backToHome">
Annulla
</button>
</div>
</div>
</div>
</div>
</div>
@code {
[Parameter] public Guid? CommessaPosizioneId { get; set; }
[Parameter] public Guid? CommessaId { get; set; }
public List<CommessaPosizioneLavorazioneViewModel> CommessePosLavList { get; set; } = new();
RadzenDataGrid<CommessaPosizioneLavorazioneViewModel> posizioneLavorazioniGrid;
public List<BreadcrumbViewModel> BreadcrumbList { get; set; } = new();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
CommessePosLavList = (await _managerService.CommessaPosizioneLavorazioneService.RicercaQueryable(
x => x.Eliminato == false && x.CommessaPosizioneId == CommessaPosizioneId,
includi: x => x.Include(x => x.Lavorazione).Include(x => x.Macchinario)))
.Select(x => (CommessaPosizioneLavorazioneViewModel)x).ToList();
BreadcrumbList = await BreadcrumbUtils.BuildBreadcrumbByFeature(_managerService, "Commesse_Info", "Modifica", "/commesse");
}
/// <summary>
/// Torna allelenco commesse senza applicare altre azioni.
/// </summary>
private void backToHome()
{
_navManager.NavigateTo($"/commesse/modifica/{CommessaId}");
}
private void goToCalendar()
{
_navManager.NavigateTo($"/commesse/modifica/Pianificatore");
}
}

View File

@ -29,7 +29,7 @@
<div class="col-3 mb-3"> <div class="col-3 mb-3">
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Data ordine</RadzenText> <RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Data ordine</RadzenText>
<RadzenDatePicker @bind-Value=@Model.DataOrdine ShowCalendarWeek DateFormat="dd/MM/yyyy" /> <RadzenDatePicker Style="width: 100%" @bind-Value=@Model.DataOrdine ShowCalendarWeek DateFormat="dd/MM/yyyy" />
<ValidationMessage For="@(() => Model.DataOrdine)" /> <ValidationMessage For="@(() => Model.DataOrdine)" />
</div> </div>
</div> </div>

View File

@ -4,6 +4,27 @@
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="row g-5"> <div class="row g-5">
<div class="col-3 mb-3">
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Riferimento cliente</RadzenText>
<RadzenTextBox Style="width: 100%" aria-label="Nome" Value="@Model.RiferimentoCliente" Disabled />
</div>
<div class="col-3 mb-3">
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Commessa</RadzenText>
<RadzenTextBox Style="width: 100%" ReadOnly="true" aria-label="Nome" Value="@Model.CodiceCommessa" Disabled />
</div>
<div class="col-3 mb-3">
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Commessa</RadzenText>
<RadzenTextBox Style="width: 100%" ReadOnly="true" aria-label="Nome" Value="@Model.RagioneSocialeCliente" Disabled />
</div>
<div class="col-3 mb-3">
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Ordine N°</RadzenText>
<RadzenTextBox Style="width: 100%" ReadOnly="true" aria-label="Nome" Value="@Model.OrdineNr" Disabled />
</div>
</div>
<div class="row mt-3 g-5">
<div class="table-responsive"> <div class="table-responsive">
<RadzenDataGrid @ref="posizioniGrid" AllowFiltering="true" AllowColumnResize="true" AllowAlternatingRows="false" FilterMode="FilterMode.CheckBoxList" AllowSorting="true" PageSize="25" <RadzenDataGrid @ref="posizioniGrid" AllowFiltering="true" AllowColumnResize="true" AllowAlternatingRows="false" FilterMode="FilterMode.CheckBoxList" AllowSorting="true" PageSize="25"
AllowPaging="true" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true" AllowPaging="true" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true"
@ -49,7 +70,7 @@
private async Task EditRow(CommessaPosizioneViewModel posizione) private async Task EditRow(CommessaPosizioneViewModel posizione)
{ {
/*_navManager.NavigateTo($"/commesse/modifica/{commessa.Id}");*/ _navManager.NavigateTo($"/commesse/modifica/DettaglioLavorazioni/{posizione.Id}/{Model.Id}");
} }
private async Task DeleteRow(CommessaPosizioneViewModel posizione) private async Task DeleteRow(CommessaPosizioneViewModel posizione)

View File

@ -0,0 +1,258 @@
@page "/commesse/modifica/Pianificatore"
@using TecniStamp.Model.Commesse
@using TecniStamp.Model.Common
@using TecniStamp.Utils
@* @page "/commesse/modifica/Pianificatore/{CommessaPosizioneId:guid}/{CommessaId:guid}" *@
<PageTitle>Pianificatore</PageTitle>
<Breadcrumb Items="BreadcrumbList" />
<style>
/* Contenitore che permette lo scroll */
.scheduler-wrapper {
overflow-x: auto;
overflow-y: auto;
max-height: 80vh; /* Altezza massima prima dello scroll verticale */
border: 1px solid #dee2e6;
background: #fff;
}
.scheduler-table {
width: 100%;
border-collapse: separate; /* Necessario per sticky */
border-spacing: 0;
}
.scheduler-table th, .scheduler-table td {
padding: 10px;
border-bottom: 1px solid #dee2e6;
border-right: 1px solid #dee2e6;
min-width: 250px; /* Larghezza minima della colonna Giorno */
vertical-align: top;
}
/* --- LOGICA STICKY (Blocca la prima colonna) --- */
.sticky-col {
position: sticky;
left: 0;
background-color: white; /* Importante per coprire il testo che scorre sotto */
z-index: 10;
border-right: 2px solid #ccc; /* Bordo più marcato per separare */
width: 200px;
min-width: 200px;
}
/* Fix per l'intestazione (deve stare sopra tutto) */
thead tr:first-child th.sticky-col {
z-index: 30;
top: 0;
}
thead tr:first-child th {
position: sticky;
top: 0;
background-color: #f8f9fa;
z-index: 20;
border-bottom: 2px solid #ccc;
}
/* --- STILE CARD (Simile alla tua immagine) --- */
.job-card {
background-color: #f4f4f4;
border-radius: 6px;
padding: 10px;
border: 1px solid #e0e0e0;
margin-bottom: 10px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.job-header {
font-weight: bold;
font-size: 0.85rem;
color: #555;
margin-bottom: 8px;
border-bottom: 1px solid #ddd;
padding-bottom: 4px;
}
.job-stats {
display: flex;
justify-content: space-between;
text-align: center;
}
.stat-box {
display: flex;
flex-direction: column;
}
.stat-label {
font-size: 0.65rem;
color: #888;
text-transform: uppercase;
}
.stat-value {
font-weight: bold;
font-size: 1.1rem;
color: #333;
}
</style>
<div class="container-fluid pt-4 mt-5">
<h2 class="mb-4">Calendario OE</h2>
@* al momento questa row con le 3 col sono solo dimostrative *@
<div class="row mb-4 g-3">
<div class="col-lg-2 col-md-4 col-sm-12">
<RadzenText TextStyle="TextStyle.Caption" class="text-muted mb-1">Reparto</RadzenText>
<RadzenDropDown TValue="string"
Data="@(new List<string>())"
Placeholder="Cerca..."
AllowFiltering="true"
Class="w-100" />
</div>
<div class="col-lg-2 col-md-4 col-sm-12">
<RadzenText TextStyle="TextStyle.Caption" class="text-muted mb-1">Commessa</RadzenText>
<RadzenDropDown TValue="string"
Data="@(new List<string>())"
Placeholder=""
AllowFiltering="true"
Class="w-100" />
</div>
<div class="col-lg-2 col-md-4 col-sm-12">
<RadzenText TextStyle="TextStyle.Caption" class="text-muted mb-1">Posizione</RadzenText>
<RadzenDropDown TValue="string"
Data="@(new List<string>())"
Placeholder="Cerca..."
AllowFiltering="true"
Class="w-100" />
</div>
</div>
<div class="scheduler-wrapper shadow-sm">
<table class="scheduler-table">
<thead>
<tr>
<th class="sticky-col text-center align-middle">
POSIZIONI
</th>
@foreach (var giorno in Giorni)
{
<th class="text-center">
<div class="fw-bold">@giorno.ToString("dddd").ToUpper()</div>
<div class="small text-muted">@giorno.ToString("dd MMM yyyy")</div>
</th>
}
</tr>
</thead>
<tbody>
@foreach (var lavorazione in Lavorazioni)
{
<tr>
<td class="sticky-col align-middle fw-bold text-secondary">
@lavorazione.Nome
</td>
@foreach (var giorno in Giorni)
{
<td>
@{
var task = lavorazione.Tasks.FirstOrDefault(t => t.Data.Date == giorno.Date);
}
@if (task != null)
{
<div class="job-card">
<div class="job-header">
@task.Macchinario
</div>
<div class="job-stats">
<div class="stat-box">
<span class="stat-label">Presidiato</span>
<span class="stat-value">@task.MinutiPresidiati m</span>
</div>
<div class="stat-box">
<span class="stat-label">Non Pres.</span>
<span class="stat-value">@task.OreNonPresidiate h</span>
</div>
<div class="stat-box">
<span class="stat-label">Totale</span>
<span class="stat-value">@task.MinutiTotali m</span>
</div>
</div>
<div class="mt-2 text-end">
<RadzenButton Icon="edit" Size="ButtonSize.ExtraSmall" ButtonStyle="ButtonStyle.Light" />
</div>
</div>
}
</td>
}
</tr>
}
</tbody>
</table>
</div>
</div>
@code{
public List<BreadcrumbViewModel> BreadcrumbList { get; set; } = new();
// --- VARIABILI PAGINA ---
List<DateTime> Giorni = new();
List<LavorazionePianificatoreModel> Lavorazioni = new();
protected override void OnInitialized()
{
// 1. Genero 30 giorni a partire da oggi
var oggi = DateTime.Today;
for (int i = 0; i < 15; i++)
{
Giorni.Add(oggi.AddDays(i));
}
// 2. Creo dati finti simili alla tua immagine
Lavorazioni.Add(new LavorazionePianificatoreModel
{
Nome = "LAVORAZIONE 1",
Tasks = new List<TaskPianificatoreModel>
{
new TaskPianificatoreModel { Data = oggi, Macchinario = "SPINNER VC1650", MinutiPresidiati = 10, OreNonPresidiate = 6, MinutiTotali = 10 },
new TaskPianificatoreModel { Data = oggi.AddDays(2), Macchinario = "SPINNER VC1650", MinutiPresidiati = 20, OreNonPresidiate = 4, MinutiTotali = 30 }
}
});
Lavorazioni.Add(new LavorazionePianificatoreModel
{
Nome = "LAVORAZIONE 2",
Tasks = new List<TaskPianificatoreModel>
{
new TaskPianificatoreModel { Data = oggi, Macchinario = "SPINNER VC1650", MinutiPresidiati = 10, OreNonPresidiate = 6, MinutiTotali = 10 }
}
});
Lavorazioni.Add(new LavorazionePianificatoreModel
{
Nome = "LAVORAZIONE 3",
Tasks = new List<TaskPianificatoreModel>
{
new TaskPianificatoreModel { Data = oggi.AddDays(1), Macchinario = "MAZAK QT200", MinutiPresidiati = 45, OreNonPresidiate = 0, MinutiTotali = 45 }
}
});
}
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
BreadcrumbList = await BreadcrumbUtils.BuildBreadcrumbByFeature(_managerService, "Pianificatore_Info");
}
}

View File

@ -0,0 +1,43 @@
using System.ComponentModel.DataAnnotations.Schema;
using TecniStamp.Domain;
namespace TecniStamp.Model.Commesse;
public class CommessaPosizioneLavorazioneViewModel : BaseViewModel
{
public Guid? CommessaPosizioneId { get; set; }
public CommessaPosizione CommessaPosizione { get; set; }
public Guid? LavorazioneId { get; set; }
public Lavorazione Lavorazione { get; set; }
public Guid? MacchinarioId { get; set; }
public Macchinario Macchinario { get; set; }
public int PrimoTempoPresidiato { get; set; }
public int TempoNonPresidiato { get; set; }
public int SecondoTempoPresidiato { get; set; }
public bool Spostabile { get; set; }
public DateTime? DataInizioPrevista { get; set; }
public DateTime? DataFinePrevista { get; set; }
public string DescLavorazione { get; set; }
public string NomeMacchinario { get; set; }
public override void Validate(){}
public static implicit operator CommessaPosizioneLavorazioneViewModel(CommessaPosizioneLavorazione model)
{
return model == null
? null
: new CommessaPosizioneLavorazioneViewModel()
{
DescLavorazione = model.Lavorazione?.Descrizione ?? "",
NomeMacchinario = model.Macchinario?.Nome ?? "",
PrimoTempoPresidiato = model.PrimoTempoPresidiato,
TempoNonPresidiato = model.TempoNonPresidiato,
SecondoTempoPresidiato = model.SecondoTempoPresidiato,
Spostabile = model.Spostabile,
DataFinePrevista = model.DataFinePrevista,
DataInizioPrevista = model.DataInizioPrevista,
Id = model.Id
};
}
}

View File

@ -0,0 +1,8 @@
namespace TecniStamp.Model.Commesse
{
public class LavorazionePianificatoreModel
{
public string Nome { get; set; }
public List<TaskPianificatoreModel> Tasks { get; set; } = new();
}
}

View File

@ -0,0 +1,11 @@
namespace TecniStamp.Model.Commesse
{
public class TaskPianificatoreModel
{
public DateTime Data { get; set; }
public string Macchinario { get; set; }
public int MinutiPresidiati { get; set; }
public int OreNonPresidiate { get; set; }
public int MinutiTotali { get; set; }
}
}