added some stuffs

This commit is contained in:
taywon18 2025-03-21 07:19:48 +01:00
parent 07f8b19394
commit 204e75ad03
41 changed files with 905 additions and 355 deletions

View File

@ -0,0 +1,59 @@
@if (!Uploading)
{
<InputFile OnChange="OnFileChanged" />
}
@Status
@code {
[Parameter]
public InspectionRegister Register { get; set; }
[Parameter]
public string ItemId { get; set; }
public bool Uploading { get; set; } = false;
public string Status { get; set; } = "";
async Task OnFileChanged(InputFileChangeEventArgs e)
{
Console.WriteLine("Début de l'envoi");
Status = "Envoi en cours...";
Uploading = true;
_ = InvokeAsync(StateHasChanged);
try
{
foreach (var file in e.GetMultipleFiles())
{
Console.WriteLine($"DEbut envoi {file.Name}");
Status = $"Envoi : {file.Name}.";
_ = InvokeAsync(StateHasChanged);
using var memstr = new MemoryStream();
await file.OpenReadStream(maxAllowedSize: long.MaxValue).CopyToAsync(memstr); ;
Register.Add(new()
{
Identifier = Guid.NewGuid().ToString(),
Author = "(null)",
Type = InspectionEntry.EntryType.Attachement,
Target = ItemId,
Attachment = memstr.ToArray(),
AttachmentName = file.Name
});
Console.WriteLine($"Added attachment {file.Name}, size: {memstr.Length}.");
}
}
catch (Exception ex)
{
Status = $"Échec de l'envoi : {ex}.";
_ = InvokeAsync(StateHasChanged);
Console.WriteLine($"Echec de l'envoi: {ex}");
}
Uploading = false;
Status = "";
_ = InvokeAsync(StateHasChanged);
Console.WriteLine("Envoi réussi");
}
}

View File

@ -0,0 +1,31 @@
<div>
@foreach(var a in Attachments)
{
<p>@a.Name</p>
}
</div>
<AttachmentSend Register=@Register ItemId=@ItemId/>
@code {
[Parameter]
public InspectionRegister Register { get; set; }
[Parameter]
public string ItemId { get; set; }
List<Attachment> Attachments { get; set; } = new();
public async Task Refresh()
{
Attachments.Clear();
Attachments = Register.GetAttachments(ItemId).ToList();
StateHasChanged();
}
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await Refresh();
}
}

View File

@ -1,5 +1,42 @@
<h3>CommentsView</h3>
<style>
.comment {
box-shadow: 5px 6px 6px 2px #e9ecef;
border-radius: 4px;
margin-top: 5px;
margin-bottom: 5px;
}
.author{
/*font-weight: bold;*/
font-style: italic;
}
</style>
<div >
@foreach (var c in Comments)
{
<div class="comment">
<span class="author">@(c.Author ?? "Inconnu")</span>:
<span>@c.Content</span>
</div>
}
<InputTextArea style="width:100%;" @bind-Value=@NewCommentText />
</div>
@code {
[Parameter]
public InspectionRegister Register { get; set; }
List<Comment> Comments { get; } = new();
string NewCommentText { get; set; } = "";
protected async override Task OnInitializedAsync()
{
await base.OnInitializedAsync();
Comments.Add(new()
{
Content = "test comment"
});
}
}

View File

@ -0,0 +1,92 @@
@using System.Timers
<Grid
TItem="InspectionEntry"
@ref="Grid"
DataProvider="EmployeesDataProvider"
Responsive="true"
>
<GridColumns>
<GridColumn TItem="InspectionEntry"
PropertyName="Author"
HeaderText="Auteur">
@context.Author
</GridColumn>
<GridColumn TItem="InspectionEntry"
PropertyName="When"
HeaderText="Horodatage">
@FormatDate(context.When)
</GridColumn>
<GridColumn TItem="InspectionEntry"
HeaderText="Description">
@FormatEntry(context)
</GridColumn>
</GridColumns>
</Grid>
@code {
[Parameter]
public InspectionRegister Register { get; set; }
[Parameter]
public string ItemId { get; set; }
private System.Timers.Timer Timer = new(1000);
Grid<InspectionEntry> Grid = default!;
private async Task<GridDataProviderResult<InspectionEntry>> EmployeesDataProvider(GridDataProviderRequest<InspectionEntry> request)
{
var entries = Register.GetEntriesOrdered(ItemId);
return await Task.FromResult(request.ApplyTo(entries));
}
protected override void OnInitialized()
{
base.OnInitialized();
Timer.Elapsed += async (object? sender, ElapsedEventArgs e) =>
{
await InvokeAsync(async() => {
await Grid.RefreshDataAsync();
StateHasChanged();
});
};
Timer.Enabled = true;
}
public string FormatDate(DateTime dt)
{
return dt.ToString("M MMM hh:mm");
}
public string FormatEntry(InspectionEntry e)
{
if (e.Type == InspectionEntry.EntryType.Attachement)
return "Ajout d'une pièce jointe.";
else if(e.Type == InspectionEntry.EntryType.Set)
return $"Changement de l'état vers {FormatState(e.State)}.";
else
return e.Type.ToString();
}
public string FormatState(ItemAnswer.State? s)
{
if (s is null)
return "(null)";
else if (s == ItemAnswer.State.NotAnswed)
return "non répondu";
else if (s == ItemAnswer.State.Compliant)
return "conforme";
else if (s == ItemAnswer.State.PartiallyCompliant)
return "partiellement conforme";
else if (s == ItemAnswer.State.Improper)
return "non conforme";
else if (s == ItemAnswer.State.Invalid)
return "sans objet";
else if (s == ItemAnswer.State.Ignored)
return "non instruit";
return s.ToString();
}
}

View File

@ -0,0 +1,42 @@
<h1>@Inspection.Name</h1>
<div style="position:sticky; top:7vh;">
<input class="form-control" placeholder="Filtrer" @oninput="OnInput" />
</div>
@foreach (var item in DisplayedItems)
{
<InspectionItemFiller Item=@item Register=@Inspection.Register/>
}
@code
{
[Parameter]
public Inspection Inspection { get; set; }
string? _filter { get; set; } = null;
public List<Item> DisplayedItems { get; set; } = new();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
Refresh();
}
public void Refresh()
{
DisplayedItems = Inspection.Grid.GetItems().ToList();
if(!String.IsNullOrEmpty(_filter))
DisplayedItems = DisplayedItems.Where(x => x.Label.Contains(_filter) || x.Identifier.Contains(_filter)).ToList();
StateHasChanged();
}
private async Task OnInput(ChangeEventArgs e)
{
_filter = e.Value?.ToString();
Refresh();
}
}

View File

@ -93,7 +93,7 @@
public Item Item { get; set; }
[Parameter]
public Register? Register { get; set; }
public InspectionRegister? Register { get; set; }
private Modal Details = default!;

View File

@ -1,4 +1,11 @@
@if (Item.Force == ItemVerification.VerificationForce.Optionnal)
<style>
h6 {
font-weight: 500;
font-size: 1.125rem;
}
</style>
@if (Item.Force == ItemVerification.VerificationForce.Optionnal)
{
<Alert Color="AlertColor.Info">
<Icon Name="IconName.InfoCircleFill" class="me-2" />
@ -22,28 +29,44 @@ else if (Item.Force == ItemVerification.VerificationForce.Mandatory)
<p>@Item.Label</p>
<h6>Statut</h6>
<InspectionItemQuickAnswerSelector Item=@Item Register=@Register />
<div class="">
<h6>Statut</h6>
<InspectionItemQuickAnswerSelector Item=@Item Register=@Register />
</div>
@if (String.IsNullOrEmpty(Item.Description))
{
<h6>Description</h6>
<p>@Item.Description</p>
}
<div class="">
@if (String.IsNullOrEmpty(Item.Description))
{
<h6>Description</h6>
<p>@Item.Description</p>
}
</div>
<h6>Références</h6>
<p>@Item.References</p>
<h6>Commentaires</h6>
<p></p>
<div class="">
<h6>Références</h6>
<p>@Item.References</p>
</div>
<h6>Historique des modifications</h6>
<p></p>
<div class="">
<h6>Pièces jointes</h6>
<AttachmentsView Register=@Register ItemId=@Item.Identifier/>
</div>
<div class="">
<h6>Commentaires</h6>
<CommentsView Register=@Register/>
</div>
<div class="">
<h6>Historique des modifications</h6>
<HistoryView Register=@Register ItemId=@Item.Identifier />
</div>
@code {
[Parameter]
public ItemVerification Item { get; set; }
[Parameter]
public Register? Register { get; set; }
public InspectionRegister? Register { get; set; }
}

View File

@ -13,7 +13,7 @@
public ItemVerification Item { get; set; }
[Parameter]
public Register Register { get; set; }
public InspectionRegister Register { get; set; }
ItemAnswer.State Value
{
@ -27,7 +27,7 @@
{
Identifier = Guid.NewGuid().ToString(),
Author = "(null)",
Type = Entry.EntryType.Set,
Type = InspectionEntry.EntryType.Set,
Target = Item.Identifier,
State = value
});

View File

@ -1,160 +0,0 @@
<style>
.row{
}
.title{
margin-top: 1em;
}
.order-0{
font-size: xx-large;
}
.order-1 {
font-size: x-large;
text-indent: 2em;
}
.order-2 {
font-size: large;
text-indent: 4em;
}
.order-3 {
font-size: medium;
text-indent: 6em;
}
</style>
<div class="row align-items-center">
@if(Row is not null)
{
@if(Row.IsTitle)
{
<span class="@Class">
@Row.Item.OrderId - @Row.Item.Label
</span>
}
else if(Row.Item is ItemVerification verification)
{
<div class="col-1">
<Tooltip Class="d-inline-block" Title="Détails" role="button">
<Icon Name="IconName.Eye" Color="IconColor.Success" Size="IconSize.x5" @onclick="async () => {
await ShowDetails();
}" Style="cursor: pointer;"/>
</Tooltip>
@if (verification.Force == ItemVerification.VerificationForce.Optionnal)
{
<Tooltip Class="d-inline-block" Title="Indication" role="button">
<Icon Name="IconName.InfoCircleFill" Color="IconColor.Info" Size="IconSize.x5" />
</Tooltip>
}
else if(verification.Force == ItemVerification.VerificationForce.Recommandation)
{
<Tooltip Class="d-inline-block" Title="Recommandation" role="button">
<Icon Name="IconName.ExclamationDiamondFill" Color="IconColor.Warning" Size="IconSize.x5" />
</Tooltip>
}
else if(verification.Force == ItemVerification.VerificationForce.Mandatory)
{
<Tooltip Class="d-inline-block" Title="Obligation" role="button">
<Icon Name="IconName.ExclamationTriangleFill" Color="IconColor.Danger" Size="IconSize.x5" />
</Tooltip>
}
</div>
<div class="col-2">
@Row.Item.OrderId
</div>
<div class="col-5">
@Row.Item.Label
</div>
<div class="col-2">
@Row.Item.References
</div>
<div class="col-2">
<AnswerStateSelect
Value=@Row.Answer.CurrentState
ValueChanged="(v) => {
Row.Answer.CurrentState = v;
}"/>
<p>Out: @Row.Answer.CurrentState</p>
</div>
}
}
else
{
<Spinner />
}
</div>
<Modal @ref="detailsModal" title="Détails" Size="ModalSize.ExtraLarge" Fullscreen="ModalFullscreen.LargeDown"/>
@code {
public Shared.InspectionRow? _row;
[Parameter]
public Shared.InspectionRow? Row
{
get
{
return _row;
}
set
{
if (_row != value)
{
_row = value;
if (RowChanged.HasDelegate)
RowChanged.InvokeAsync(_row);
}
}
}
[Parameter]
public EventCallback<Shared.InspectionRow?> RowChanged { get; set; }
private Modal detailsModal = default!;
protected override Task OnInitializedAsync()
{
return base.OnInitializedAsync();
}
public string Class
{
get
{
if (Row is null)
return "";
if (!Row.IsTitle)
return "verification";
return "title order-" + Row.Order;
}
}
private async Task ShowDetails()
{
if (Row is null)
return; //TODO: add error message
Action<Shared.InspectionRow> callback = (Shared.InspectionRow row) =>
{
StateHasChanged();
};
var parameters = new Dictionary<string, object>();
parameters.Add("Row", Row);
parameters.Add("RowChanged", callback);
await detailsModal.ShowAsync<InspectionRowDetails>(title: "Détails", parameters: parameters);
}
}

View File

@ -1,71 +0,0 @@
@if (Row is not null && Verification is not null)
{
@if (Verification.Force == ItemVerification.VerificationForce.Optionnal)
{
<Alert Color="AlertColor.Info">
<Icon Name="IconName.InfoCircleFill" class="me-2" />
Cet item est une indication, il ne relève ni d'une recommandation, ni d'une obligation.
</Alert>
}
else if (Verification.Force == ItemVerification.VerificationForce.Recommandation)
{
<Alert Color="AlertColor.Warning">
<Icon Name="IconName.ExclamationDiamondFill" class="me-2" />
Cet item relève de la recommandation, il relève des recommandations opposables.
</Alert>
}
else if (Verification.Force == ItemVerification.VerificationForce.Mandatory)
{
<Alert Color="AlertColor.Danger">
<Icon Name="IconName.ExclamationTriangleFill" class="me-2" />
Cet item doit obligatoirement être suivi, car il fait l'objet d'un référentiel opposable.
</Alert>
}
<p>@Row.Item.Label</p>
<h6>Statut</h6>
<AnswerStateSelect
Value=@Row.Answer.CurrentState
ValueChanged="(v) => {
if(Row.Answer.CurrentState != v)
{
Row.Answer.CurrentState = v;
RowChanged(Row);
}
}" />
@if(String.IsNullOrEmpty(Row.Item.Description))
{
<h6>Description</h6>
<p>@Row.Item.Description</p>
}
<h6>Références</h6>
<p>@Row.Item.References</p>
<h6>Commentaires</h6>
<p></p>
<h6>Historique des modifications</h6>
<p></p>
}
else
{
<Spinner />
}
@code {
[Parameter]
public Shared.InspectionRow? Row { get; set; }
public Shared.ItemVerification? Verification { get {
if (Row == null)
return null;
return (Shared.ItemVerification)Row.Item ?? null;
} }
[Parameter]
public Action<Shared.InspectionRow> RowChanged { get; set; } = (Shared.InspectionRow r) => { };
}

View File

@ -0,0 +1,48 @@
<h3>@Grid.Name <Button @onclick=@(async () => await AddChildTitle())></Button></h3>
@foreach (var i in Grid.Register.Forge())
{
<TemplateFillerItem Grid=@Grid Item=@i OnAddChild=@(async () => await AddChildTitle(i.Identifier))/>
}
@code {
[Parameter]
public TemplateGrid Grid { get; set; } = default!;
protected async Task AddChildTitle(string? parent = null)
{
var id = Guid.NewGuid().ToString();
Grid.Register.Add(new()
{
Identifier = Guid.NewGuid().ToString(),
Target = id,
Author = "",
Type = TemplateEntry.EntryType.Set,
Field = TemplateEntry.FieldType.Type,
Content = TemplateEntry.TYPE_TITLE,
});
Grid.Register.Add(new()
{
Identifier = Guid.NewGuid().ToString(),
Target = id,
Author = "",
Type = TemplateEntry.EntryType.Set,
Field = TemplateEntry.FieldType.Parent,
Content = parent
});
Grid.Register.Add(new()
{
Identifier = Guid.NewGuid().ToString(),
Target = id,
Author = "",
Type = TemplateEntry.EntryType.Set,
Field = TemplateEntry.FieldType.Label,
Content = "TEST "
});
StateHasChanged();
}
}

View File

@ -0,0 +1,9 @@
<h3>TemplateFillerDetail</h3>
@code {
[Parameter]
public TemplateGrid Grid { get; set; } = default!;
[Parameter]
public Item Item { get; set; } = default!;
}

View File

@ -0,0 +1,113 @@
<style>
.row {
}
.title {
margin-top: 1em;
}
.order-0 {
font-size: xx-large;
}
.order-1 {
font-size: x-large;
text-indent: 2em;
}
.order-2 {
font-size: large;
text-indent: 4em;
}
.order-3 {
font-size: medium;
text-indent: 6em;
}
</style>
<div class="row align-items-center">
<div class="col-1">
<Tooltip Class="d-inline-block" Title="Détails" role="button">
<Icon Name="IconName.Eye" Color="IconColor.Success" Size="IconSize.x5" @onclick="async () => {
await ShowDetails();
}" Style="cursor: pointer;" />
</Tooltip>
<Tooltip Class="d-inline-block" Title="Ajouter un enfant" role="button">
<Icon Name="IconName.Plus" Color="IconColor.Success" Size="IconSize.x5" @onclick="async () => {
await ShowDetails();
}" Style="cursor: pointer;" />
</Tooltip>
@if (Item is ItemTitle t)
{
<Tooltip Class="d-inline-block" Title="Ajouter un sous-titre" role="button">
<Icon Name="IconName.Plus" Color="IconColor.Success" Size="IconSize.x5" @onclick="async () => {
await ShowDetails();
}" Style="cursor: pointer;" />
</Tooltip>
}
</div>
<div class="col-6">
@if (Item is ItemTitle title)
{
<span class="@Class">
@title.OrderId - @title.Label
</span>
}
else if (Item is ItemVerification verif)
{
@verif.Label
}
</div>
</div>
<Modal @ref="Details"
title="Détails"
Size="ModalSize.ExtraLarge"
Fullscreen="ModalFullscreen.LargeDown"
OnHidden="OnModalHidden" />
@code {
[Parameter]
public TemplateGrid Grid { get; set; } = default!;
[Parameter]
public Item Item { get; set; } = default!;
[Parameter]
public EventCallback OnAddChild { get; set; } = default!;
private Modal Details = default!;
public string Class
{
get
{
if (Item is not ItemTitle)
return "verification";
return "title order-" + Item.Order;
}
}
private async Task ShowDetails()
{
var parameters = new Dictionary<string, object>();
parameters.Add(nameof(TemplateFillerDetail.Grid), Grid);
parameters.Add(nameof(TemplateFillerDetail.Item), Item);
await Details.ShowAsync<InspectionItemFillerDetail>(title: "Détails", parameters: parameters);
}
private void OnModalHidden()
{
StateHasChanged();
}
}

View File

@ -1,4 +1,61 @@
<h1>Liste des modèles</h1>
@inject TemplateService Templates;
<button @onclick="ShowNewModal">Créer une grille</button>
@foreach(var i in Grids)
{
<div class="row align-items-center">
<a href=@("/model/"+i.Identifier)>@i.Name</a>
</div>
}
<Modal @ref="modal" Title="Modal title">
<BodyTemplate>
<label>
Nom de la grille
<InputText @bind-Value=@Name />
</label>
</BodyTemplate>
<FooterTemplate>
<Button Color="ButtonColor.Primary" @onclick="CreateNewModal">✔️ Confirmer la création de la grille</Button>
</FooterTemplate>
</Modal>
@code {
private Modal modal = default!;
[Parameter]
public string Name { get; set; } = String.Empty;
public List<TemplateGrid> Grids { get; set; } = [];
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
await Refresh();
}
public async Task ShowNewModal()
{
await modal.ShowAsync();
}
async Task CreateNewModal()
{
await modal.HideAsync();
var n = await Templates.Create(Name);
//todo: switch to correct page
await Refresh();
}
public async Task Refresh()
{
Grids.Clear();
await foreach (var i in Templates.Get())
Grids.Add(i);
StateHasChanged();
}
}

View File

@ -7,7 +7,7 @@
</div>
</div>
<div class="@NavMenuCssClass nav-scrollable" <!-- @onclick="ToggleNavMenu" --> />
<div class="@NavMenuCssClass nav-scrollable" >
<nav class="flex-column">
@ -18,7 +18,7 @@
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="models" Match="NavLinkMatch.All">
<NavLink class="nav-link" href="model" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Modèles
</NavLink>
</div>
@ -26,7 +26,7 @@
<div class="nav-item px-3">
<NavLink class="nav-link" href="inspection">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Inspection
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Inspection en cours
</NavLink>
</div>
</nav>

View File

@ -1,40 +0,0 @@
@page "/inspection"
@inject Trip TripManager;
@if(Inspection is not null)
{
<h1>@Inspection.Name</h1>
<div style="position:sticky; top:7vh;">
<InputText class="form-control" placeholder="Filtrer" @bind-Value="Filter" />
</div>
@foreach(var item in Inspection.Grid.GetItems())
{
<InspectionItemFiller Item=@item Register=@Inspection.Register/>
}
}
else
{
<Spinner />
}
@code
{
public Inspection? Inspection = null;
public string? Filter {get; set;} = "";
class ViewModel
{
public bool Mandatory { get; set; } = true;
public string Reference { get; set; } = "v0a:PECM.1.1.1.0c";
public string Label { get; set; } = "La structure DEVRAIT avoir défini un plan d'action pour améliorer la PCEM";
}
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
Inspection = TripManager.Current;
}
}

View File

@ -0,0 +1,24 @@
@page "/inspection"
@inject Trip TripManager;
@if(Inspection is not null)
{
<InspectionFiller Inspection=@Inspection/>
}
else
{
<Spinner />
}
@code
{
public Inspection? Inspection = null;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
Inspection = TripManager.Current;
}
}

View File

@ -0,0 +1,23 @@
@page "/model/{id}"
@inject TemplateService Templates;
<TemplateFiller Grid=@Grid/>
@code {
[Parameter]
public string Id { get; set; }
public TemplateGrid Grid { get; private set; } = default!;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
Grid = await Templates.Get(Id) ?? throw new ArgumentException();
}
}

View File

@ -1,8 +1,8 @@
@page "/models"
@page "/model"
<h1>Liste des modèles</h1>
<TemplateTable />
@code {

View File

@ -9,6 +9,7 @@ builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddBlazorBootstrap();
builder.Services.AddSingleton<Trip>();
builder.Services.AddSingleton<TemplateService>();
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();

View File

@ -0,0 +1,49 @@
using Thor.Shared.Model;
namespace Thor.BlazorWAsm.Services;
public class TemplateService
{
private List<TemplateGrid> All = [];
public TemplateService()
{
All.Add(new()
{
Name = "Inspection test"
});
}
public async Task<TemplateGrid> Create(string name)
{
TemplateGrid grid = new(name);
lock(All)
All.Add(grid);
return grid;
}
public async IAsyncEnumerable<TemplateGrid> Get()
{
lock (All)
foreach (var i in All)
yield return i;
}
public Task<TemplateGrid?> Get(string id)
{
lock (All)
foreach (var i in All)
if(i.Identifier == id)
return Task.FromResult<TemplateGrid?>(i);
return Task.FromResult<TemplateGrid?>(null);
}
public async Task Remove(string identifier)
{
lock(All)
All.RemoveAll(x => x.Identifier == identifier);
}
}

View File

@ -1,4 +1,5 @@
using Thor.Shared;
using Thor.Shared.Inspect;
namespace Thor.BlazorWAsm.Services;

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>

View File

@ -7,6 +7,8 @@
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using Thor.Shared;
@using Thor.Shared.Inspect;
@using Thor.Shared.Model;
@using Thor.BlazorWAsm
@using Thor.BlazorWAsm.Layout
@using Thor.BlazorWAsm.Components;

View File

@ -100,4 +100,4 @@ a, .btn-link {
code {
color: #c02d76;
}
}

View File

@ -1,5 +0,0 @@
namespace Thor.Shared;
public class Comments
{
}

14
Thor.Shared/EntryBase.cs Normal file
View File

@ -0,0 +1,14 @@
namespace Thor.Shared;
public class EntryBase
{
public string Identifier { get; set; }
public string Author { get; set; }
public DateTime When { get; set; } = DateTime.Now;
public string Target { get; set; }
public EntryBase()
{
Identifier = Guid.NewGuid().ToString();
}
}

View File

@ -0,0 +1,10 @@
namespace Thor.Shared.Inspect;
public class Attachment
{
public string Id { get; set; }
public DateTime When { get; set; } = DateTime.Now;
public string Author { get; set; } = "";
public string Name { get; set; } = "";
public byte[] Content { get; set; } = Array.Empty<byte>();
}

View File

@ -0,0 +1,8 @@
namespace Thor.Shared.Inspect;
public class Comment
{
public DateTime When { get; set; } = DateTime.Now;
public string Author { get; set; } = "";
public string Content { get; set; } = "";
}

View File

@ -1,6 +1,6 @@
using System.Collections;
namespace Thor.Shared;
namespace Thor.Shared.Inspect;
public class Grid
{
@ -17,7 +17,7 @@ public class Grid
return title;
var verification = Verifications.Where(v => v.Identifier == id).FirstOrDefault();
if(verification is not null)
if (verification is not null)
return verification;
return null;
@ -37,14 +37,14 @@ public class Grid
public IEnumerable<Item> GetItems()
{
return Enumerable.Union<Item>(Titles, Verifications).OrderBy(x => x.OrderId);
return Titles.Union<Item>(Verifications).OrderBy(x => x.OrderId);
}
public IEnumerable<Item> GetChildren(IEnumerable<string> parentsid)
{
var lst = parentsid.ToHashSet();
foreach (var i
in Titles
in Titles
.Where(
i => lst.Contains(i.ParentId)))
{
@ -69,11 +69,11 @@ public class Grid
continue;
toReturn.Add(item);
if(item is ItemTitle title)
if (item is ItemTitle title)
{
var children = GetChildren(title.Identifier);
foreach (var child in children)
if(!toReturn.Contains(child))
if (!toReturn.Contains(child))
toExplore.Add(child.Identifier);
}
}

View File

@ -1,21 +1,21 @@
namespace Thor.Shared;
namespace Thor.Shared.Inspect;
public class Inspection
{
public string Identifier { get; set; }
public string Name { get; set; } = string.Empty;
public Grid Grid { get; } = new();
public Register Register { get; set; } = new();
public InspectionRegister Register { get; set; } = new();
public IEnumerable<InspectionRow> GetRows()
{
var items = Grid.GetItems().OrderBy(r => r.OrderId);
foreach(var item in items)
foreach (var item in items)
{
if (item is ItemTitle)
yield return new(item);
else if (item is ItemVerification)
yield return new(item, Register.ForgeItemAnswer(item.Identifier));
yield return new(item, Register.Forge(item.Identifier));
else throw new NotImplementedException();
}
}

View File

@ -1,10 +1,10 @@
namespace Thor.Shared;
namespace Thor.Shared.Inspect;
public class Entry
public class InspectionEntry
: EntryBase
{
public enum EntryType
{
Metadata,
Set,
Comment,
Observation,
@ -12,16 +12,12 @@ public class Entry
Show
}
public string Identifier { get; set; }
public string Author { get; set; }
public EntryType Type { get; set; }
public string Target { get; set; }
public DateTime When { get; set; } = DateTime.Now;
public ItemAnswer.State? State { get; set; } = null;
public string? Content { get; set; } = null;
public string? Observation { get; set; } = null;
public string? Attachment { get; set; } = null;
public byte[]? Attachment { get; set; } = null;
public string? AttachmentName { get; set; } = null;
public bool? Display { get; set; } = null;
}

View File

@ -1,30 +1,10 @@
namespace Thor.Shared;
namespace Thor.Shared.Inspect;
public class Register
public class InspectionRegister
: RegisterBase<InspectionEntry, ItemAnswer>
{
List<Entry> Entries = new();
public void Add(Entry entry)
{
Entries.Add(entry);
}
public void Clear()
{
Entries.Clear();
}
public List<Entry> GetEntriesOrdered(string itemid)
{
var entries = Entries
.Where(e => e.Target == itemid)
.ToList();
entries.Sort((a, b) => a.When.CompareTo(b.When));
return entries;
}
public ItemAnswer ForgeItemAnswer(string itemid)
public ItemAnswer Forge(string itemid)
{
var entries = GetEntriesOrdered(itemid);
@ -37,14 +17,14 @@ public class Register
foreach (var entry in entries)
{
if (entry.Type == Entry.EntryType.Set)
if (entry.Type == InspectionEntry.EntryType.Set)
{
if (!entry.State.HasValue)
throw new Exception("Entry typed Set send without any State.");
ret.CurrentState = entry.State.Value;
}
else
else
throw new NotImplementedException();
Entries.Add(entry);
@ -58,15 +38,35 @@ public class Register
var entries = Entries
.Where(e
=> e.Target == itemid
&& e.Type == Entry.EntryType.Set)
&& e.Type == InspectionEntry.EntryType.Set)
.OrderByDescending(e => e.When)
.ToList();
if(entries.Count == 0)
return default(ItemAnswer.State);
if (entries.Count == 0)
return default;
var firstEntryState = entries.First().State;
return firstEntryState ?? default(ItemAnswer.State);
return firstEntryState ?? default;
}
public IEnumerable<Attachment> GetAttachments(string itemid)
{
var entries = Entries
.Where(e
=> e.Target == itemid
&& e.Type == InspectionEntry.EntryType.Attachement)
.OrderByDescending(e => e.When)
.ToList();
foreach (var entry in entries)
yield return new()
{
Id = entry.Identifier,
Author = entry.Author,
When = entry.When,
Name = entry.AttachmentName,
Content = entry.Attachment
};
}
}

View File

@ -1,4 +1,4 @@
namespace Thor.Shared;
namespace Thor.Shared.Inspect;
public class InspectionRow
{

View File

@ -1,4 +1,4 @@
namespace Thor.Shared;
namespace Thor.Shared.Inspect;
public class ItemAnswer
@ -16,5 +16,5 @@ public class ItemAnswer
public string TargetId { get; set; }
public State CurrentState { get; set; } = State.NotAnswed;
List<Entry> Entries { get; } = new();
List<InspectionEntry> Entries { get; } = new();
}

View File

@ -10,6 +10,8 @@ public abstract class Item
public string References { get; set; } = string.Empty;
public int Order => OrderId.Count(x => x == '.');
public bool Hidden { get; set; }
}
public class ItemTitle

View File

@ -0,0 +1,30 @@
namespace Thor.Shared.Model;
public class TemplateEntry
: EntryBase
{
public const string TYPE_TITLE = "t";
public const string TYPE_ITEM = "i";
public enum EntryType
{
Set,
Comment,
Delete,
Restore
}
public enum FieldType
{
Type,
Parent,
Order,
Label,
Description,
Reference
}
public EntryType Type { get; set; }
public FieldType? Field { get; set; } = null;
public string? Content { get; set; }
}

View File

@ -0,0 +1,27 @@
namespace Thor.Shared.Model;
public class TemplateGrid
{
public string Identifier { get; set; }
public string Name { get; set; } = string.Empty;
public TemplateRegister Register { get; set; } = new();
public TemplateGrid()
{
Identifier = Guid.NewGuid().ToString();
}
public TemplateGrid(string name)
: this()
{
Name = name;
}
public TemplateGrid(string identifier, string name, TemplateRegister register)
{
Identifier = identifier;
Name = name;
Register = register;
}
}

View File

@ -0,0 +1,85 @@
namespace Thor.Shared.Model;
public class TemplateRegister
: RegisterBase<TemplateEntry, Item>
{
public List<Item> Forge()
{
Dictionary<string, List<TemplateEntry>> entriesByTarget = [];
foreach (var i in GetEntriesOrdered())
{
var target = i.Target;
if (entriesByTarget.TryGetValue(target, out var list))
list.Add(i);
else
entriesByTarget.Add(target, [i]);
}
List<Item> items = [];
foreach(var kv in entriesByTarget)
{
var item = Forge(kv.Key, kv.Value);
if(item is not null)
items.Add(item);
}
return items.OrderBy(x => x.OrderId).ToList();
}
private Item? Forge(string id, IEnumerable<TemplateEntry> entries)
{
bool? IsTitle = null;
string Identifier = id;
string? ParentId = null;
string? OrderId = null;
string? Label = null;
string? Description = null;
string? References = null;
int restauration = 1;
foreach(var entry in entries)
{
if(entry.Type == TemplateEntry.EntryType.Delete)
{
restauration--;
continue;
}
if (entry.Type == TemplateEntry.EntryType.Restore)
{
restauration++;
continue;
}
//ignore comments
if (entry.Type != TemplateEntry.EntryType.Set)
continue;
if (entry.Field == TemplateEntry.FieldType.Type && IsTitle is null)
IsTitle = entry.Content == TemplateEntry.TYPE_TITLE;
else if(entry.Field == TemplateEntry.FieldType.Reference && References is null)
References = entry.Content;
else if(entry.Field == TemplateEntry.FieldType.Label && Label is null)
Label = entry.Content;
else if(entry.Field == TemplateEntry.FieldType.Parent && ParentId is null)
ParentId = entry.Content;
else if(entry.Field == TemplateEntry.FieldType.Description && Description is null)
Description = entry.Content;
else if(entry.Field == TemplateEntry.FieldType.Order && OrderId is null)
OrderId = entry.Content;
}
if (IsTitle is null)
return null;
Item ret = IsTitle == true ? new ItemTitle() : new ItemVerification();
ret.Identifier = Identifier;
ret.ParentId = ParentId ?? String.Empty;
ret.OrderId = OrderId ?? String.Empty;
ret.Label = Label ?? String.Empty;
ret.Description = Description ?? String.Empty;
ret.References = References ?? String.Empty;
return ret;
}
}

View File

@ -0,0 +1,37 @@
namespace Thor.Shared;
public abstract class RegisterBase<EntryType, ResultType>
where EntryType : EntryBase
where ResultType : class
{
protected List<EntryType> Entries { get; } = [];
public void Add(EntryType entry)
{
Entries.Add(entry);
}
public void Clear()
{
Entries.Clear();
}
public List<EntryType> GetEntriesOrdered(string itemid)
{
var entries = Entries
.Where(e => e.Target == itemid)
.ToList();
entries.Sort((a, b) => a.When.CompareTo(b.When));
return entries;
}
public List<EntryType> GetEntriesOrdered()
{
var entries = Entries
.ToList();
entries.Sort((a, b) => a.When.CompareTo(b.When));
return entries;
}
}

View File

@ -6,6 +6,12 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Remove="NewFolder\**" />
<EmbeddedResource Remove="NewFolder\**" />
<None Remove="NewFolder\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="LiteDB" Version="5.0.20" />
</ItemGroup>