Creazione progetto Blazor 8 wes (server + WASM) x SMART, da completare...
This commit is contained in:
@@ -24,4 +24,7 @@
|
||||
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="8.0.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GPW.CORE\GPW.CORE.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -27,8 +27,6 @@
|
||||
<Routes @rendermode=@(new InteractiveServerRenderMode(prerender: false)) />
|
||||
|
||||
@* <script src="_framework/blazor.web.js"></script> *@
|
||||
|
||||
@* Gestione Blazor 6 x autoreconnect, da valutare *@
|
||||
<script src="_framework/blazor.server.js" autostart="false"></script>
|
||||
<script>
|
||||
Blazor.start({
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.4.33205.214
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GPW.CORE.Data", "GPW.CORE.Data\GPW.CORE.Data.csproj", "{32DE3E46-CCED-4F7E-8EC1-A854DA4F51C1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GPW.CORE.Smart8", "GPW.CORE.Smart8\GPW.CORE.Smart8.csproj", "{FD29ECAA-E9B6-4AA5-8D89-715D58C52891}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GPW.CORE.Smart8.Client", "GPW.CORE.Smart8.CLient\GPW.CORE.Smart8.Client.csproj", "{02822ACA-2780-4A7E-BA8B-0FE59457072A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GPW.CORE", "GPW.CORE\GPW.CORE.csproj", "{98EF4DDB-F268-40C1-BACF-13BCFF41F19D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{32DE3E46-CCED-4F7E-8EC1-A854DA4F51C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{32DE3E46-CCED-4F7E-8EC1-A854DA4F51C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{32DE3E46-CCED-4F7E-8EC1-A854DA4F51C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{32DE3E46-CCED-4F7E-8EC1-A854DA4F51C1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FD29ECAA-E9B6-4AA5-8D89-715D58C52891}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FD29ECAA-E9B6-4AA5-8D89-715D58C52891}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FD29ECAA-E9B6-4AA5-8D89-715D58C52891}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FD29ECAA-E9B6-4AA5-8D89-715D58C52891}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{02822ACA-2780-4A7E-BA8B-0FE59457072A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{02822ACA-2780-4A7E-BA8B-0FE59457072A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{02822ACA-2780-4A7E-BA8B-0FE59457072A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{02822ACA-2780-4A7E-BA8B-0FE59457072A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{98EF4DDB-F268-40C1-BACF-13BCFF41F19D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{98EF4DDB-F268-40C1-BACF-13BCFF41F19D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{98EF4DDB-F268-40C1-BACF-13BCFF41F19D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{98EF4DDB-F268-40C1-BACF-13BCFF41F19D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B0B304CA-6B40-44FF-A9C3-F90A3C56B21A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,56 @@
|
||||
@if (!isVisible)
|
||||
{
|
||||
<div class="tagOnly">
|
||||
<div class="d-flex justify-content-around">
|
||||
<button class="btn-show" @onclick="()=>showMenu()">
|
||||
<div class="d-flex justify-content-around">
|
||||
<div class="px-2"><i class="fa-solid fa-bars"></i></div>
|
||||
<div class="px-2">Accesso Rapido</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="menuArea w-100">
|
||||
<div class="d-flex justify-content-around">
|
||||
<button class="btn-hide" @onclick="()=>hideMenu()">
|
||||
<div class="d-flex justify-content-around">
|
||||
<div class="px-2"><i class="fa-solid fa-bars"></i></div>
|
||||
<div class="px-2">Accesso Rapido</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="choiceArea text-center pt-2">
|
||||
<div class="rapid-buttons">
|
||||
<div class="btn-group w-100">
|
||||
<button type="button" class="btn btn-light" @onclick="()=>goToIndex()">
|
||||
<i class="fa-solid fa-person-booth fa-2x"></i>
|
||||
<div>Timbra</div>
|
||||
</button>
|
||||
<button type="button" class="btn btn-light mx-1" @onclick="()=>goToAdmin()">
|
||||
<i class="fa-solid fa-user fa-2x"></i>
|
||||
<div>Admin</div>
|
||||
</button>
|
||||
<button type="button" class="btn btn-light" @onclick="()=>goToMPF()">
|
||||
<i class="fa-solid fa-ticket fa-2x"></i>
|
||||
<div>MFP</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex justify-content-between px-3 text-light my-2">
|
||||
<div>
|
||||
GPW <span class="small">v.@CurrConf.Versione</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<a type="button" class="text-light" @onclick="() => goToAbout()">
|
||||
<img class="img-fluid" width="16" src="images/LogoEgw.png" /> Egalware
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Client.Components
|
||||
{
|
||||
public partial class NavBottom
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public ConfigDTO CurrConf { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipendente { get; set; } = 0;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected bool isActive { get; set; } = false;
|
||||
|
||||
[Inject]
|
||||
protected NavigationManager NavManager { get; set; } = null!;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected void goTo(string target)
|
||||
{
|
||||
NavManager.NavigateTo($"{CurrConf.BaseUrl}{target}", true);
|
||||
Console.WriteLine($"NavBottom | NavTo: {target}");
|
||||
}
|
||||
|
||||
protected void goToAbout()
|
||||
{
|
||||
goTo("About");
|
||||
}
|
||||
|
||||
protected void goToAdmin()
|
||||
{
|
||||
goTo("Admin");
|
||||
}
|
||||
|
||||
protected void goToIndex()
|
||||
{
|
||||
goTo("Index");
|
||||
}
|
||||
|
||||
protected void goToMPF()
|
||||
{
|
||||
goTo("DayOff");
|
||||
}
|
||||
|
||||
protected void hideMenu()
|
||||
{
|
||||
isVisible = false;
|
||||
hideMenuCSS = "hidden; height: 0%;";
|
||||
hideButtonCSS = "visible";
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
Console.WriteLine($"NavBottom | Initialized");
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
base.OnParametersSet();
|
||||
Console.WriteLine($"NavBottom | Params | baseUrl: {CurrConf.BaseUrl} | ver: {CurrConf.Versione} | idxDip: {IdxDipendente}");
|
||||
}
|
||||
|
||||
protected void showMenu()
|
||||
{
|
||||
if (IdxDipendente > 0)
|
||||
{
|
||||
isVisible = true;
|
||||
hideMenuCSS = "visible; height: 22%; display:block;";
|
||||
hideButtonCSS = "hidden";
|
||||
}
|
||||
}
|
||||
|
||||
protected void slideIn()
|
||||
{
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
protected void slideOut()
|
||||
{
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private string hideButtonCSS = "visible";
|
||||
private string hideMenuCSS = "hidden";
|
||||
private bool isVisible = false;
|
||||
|
||||
#endregion Private Fields
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
.btn-toggle {
|
||||
--y: 0;
|
||||
--z: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(to top, #3f4c6b, #606c88);
|
||||
padding: 0.75rem 3rem;
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
border: none;
|
||||
box-shadow: 0 2px 40px -10px #000;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
left: 50%;
|
||||
transform: translate3d(var(--x), var(--y), var(--z));
|
||||
cursor: pointer;
|
||||
z-index: 105;
|
||||
}
|
||||
.btn-show {
|
||||
--x: -50%;
|
||||
--y: 0;
|
||||
--z: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(to top, #3f4c6b, #606c88);
|
||||
padding: 0.75rem 3rem;
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
border: none;
|
||||
box-shadow: 0 2px 40px -10px #000;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
left: 50%;
|
||||
transform: translate3d(var(--x), var(--y), var(--z));
|
||||
cursor: pointer;
|
||||
z-index: 105;
|
||||
position: fixed;
|
||||
bottom: 0rem;
|
||||
}
|
||||
.btn-hide {
|
||||
--y: 0;
|
||||
--z: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(to top, #3f4c6b, #606c88);
|
||||
padding: 0.75rem 3rem;
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
border: none;
|
||||
box-shadow: 0 2px 40px -10px #000;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
left: 50%;
|
||||
transform: translate3d(var(--x), var(--y), var(--z));
|
||||
cursor: pointer;
|
||||
z-index: 105;
|
||||
}
|
||||
.menuArea {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
animation-name: menuShow;
|
||||
animation-duration: 0.8s;
|
||||
}
|
||||
.tagOnly {
|
||||
animation-name: menuHide;
|
||||
animation-duration: 0.2s;
|
||||
}
|
||||
.choiceArea {
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
padding: 0.2rem 0.4rem 0.2rem 0.4rem;
|
||||
box-shadow: 0px -9px 50px -30px black;
|
||||
transition: height 200ms ease-in-out 400ms, opacity 700ms;
|
||||
background: linear-gradient(to bottom, #606c88, #3f4c6b);
|
||||
}
|
||||
.rapid-buttons {
|
||||
display: flex;
|
||||
padding: 0.4rem 0.2rem;
|
||||
justify-content: center;
|
||||
}
|
||||
.rapid-buttons a {
|
||||
flex: 1 1 30%;
|
||||
padding: 1.5rem;
|
||||
margin: 5px;
|
||||
border-radius: 8px;
|
||||
background: #d1d1d1;
|
||||
color: var(--color);
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
@keyframes menuShow {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
transform: translateY(8rem);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes menuHide {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
/*transform: rotateY(180deg);*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
.btn-toggle {
|
||||
--y: 0;
|
||||
--z: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(to top, #3f4c6b, #606c88);
|
||||
padding: .75rem 3rem;
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
border: none;
|
||||
box-shadow: 0 2px 40px -10px #000;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
left: 50%;
|
||||
transform: translate3d(var(--x), var(--y), var(--z));
|
||||
cursor: pointer;
|
||||
z-index: 105;
|
||||
}
|
||||
|
||||
.btn-show {
|
||||
--x: -50%;
|
||||
.btn-toggle;
|
||||
position: fixed;
|
||||
bottom: 0rem;
|
||||
}
|
||||
|
||||
.btn-hide {
|
||||
.btn-toggle;
|
||||
}
|
||||
|
||||
.menuArea {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
animation-name: menuShow;
|
||||
animation-duration: 0.8s;
|
||||
}
|
||||
|
||||
.tagOnly {
|
||||
animation-name: menuHide;
|
||||
animation-duration: 0.2s;
|
||||
}
|
||||
|
||||
.choiceArea {
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
padding: 0.2rem .4rem 0.2rem .4rem;
|
||||
box-shadow: 0px -9px 50px -30px black;
|
||||
transition: height 200ms ease-in-out 400ms, opacity 700ms;
|
||||
background: linear-gradient(to bottom, #606c88, #3f4c6b);
|
||||
}
|
||||
|
||||
.rapid-buttons {
|
||||
display: flex;
|
||||
padding: 0.4rem 0.2rem;
|
||||
justify-content: center;
|
||||
|
||||
a {
|
||||
flex: 1 1 30%;
|
||||
padding: 1.5rem;
|
||||
margin: 5px;
|
||||
border-radius: 8px;
|
||||
background: #d1d1d1;
|
||||
color: var(--color);
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes menuShow {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
transform: translateY(8rem);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes menuHide {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
/*transform: rotateY(180deg);*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.btn-toggle{--y:0;--z:0;display:flex;align-items:center;justify-content:space-between;background:linear-gradient(to top,#3f4c6b,#606c88);padding:.75rem 3rem;border-radius:1.5rem 1.5rem 0 0;border:0;box-shadow:0 2px 40px -10px #000;color:#fff;white-space:nowrap;left:50%;transform:translate3d(var(--x),var(--y),var(--z));cursor:pointer;z-index:105;}.btn-show{--x:-50%;--y:0;--z:0;display:flex;align-items:center;justify-content:space-between;background:linear-gradient(to top,#3f4c6b,#606c88);padding:.75rem 3rem;border-radius:1.5rem 1.5rem 0 0;border:0;box-shadow:0 2px 40px -10px #000;color:#fff;white-space:nowrap;left:50%;transform:translate3d(var(--x),var(--y),var(--z));cursor:pointer;z-index:105;position:fixed;bottom:0;}.btn-hide{--y:0;--z:0;display:flex;align-items:center;justify-content:space-between;background:linear-gradient(to top,#3f4c6b,#606c88);padding:.75rem 3rem;border-radius:1.5rem 1.5rem 0 0;border:0;box-shadow:0 2px 40px -10px #000;color:#fff;white-space:nowrap;left:50%;transform:translate3d(var(--x),var(--y),var(--z));cursor:pointer;z-index:105;}.menuArea{position:fixed;bottom:0;animation-name:menuShow;animation-duration:.8s;}.tagOnly{animation-name:menuHide;animation-duration:.2s;}.choiceArea{border-radius:1.5rem 1.5rem 0 0;padding:.2rem .4rem .2rem .4rem;box-shadow:0 -9px 50px -30px black;transition:height 200ms ease-in-out 400ms,opacity 700ms;background:linear-gradient(to bottom,#606c88,#3f4c6b);}.rapid-buttons{display:flex;padding:.4rem .2rem;justify-content:center;}.rapid-buttons a{flex:1 1 30%;padding:1.5rem;margin:5px;border-radius:8px;background:#d1d1d1;color:var(--color);text-align:center;font-size:14px;font-weight:bold;text-decoration:none;}@keyframes menuShow{0%{opacity:.6;transform:translateY(8rem);}100%{opacity:1;transform:translateY(0);}}@keyframes menuHide{0%{opacity:.6;}100%{opacity:1;}}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
|
||||
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.14" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GPW.CORE\GPW.CORE.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
@page "/counter"
|
||||
@rendermode InteractiveAuto
|
||||
|
||||
<PageTitle>Counter</PageTitle>
|
||||
|
||||
<h1>Counter</h1>
|
||||
|
||||
<p role="status">Current count: @currentCount</p>
|
||||
|
||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
||||
|
||||
@code {
|
||||
private int currentCount = 0;
|
||||
|
||||
private void IncrementCount()
|
||||
{
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
|
||||
|
||||
var builder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||
|
||||
//CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("it-IT");
|
||||
//CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("it-IT");
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
@@ -0,0 +1,9 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.JSInterop
|
||||
@using GPW.CORE.Smart8.Client
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
window.getWindowDimensions = function () {
|
||||
return {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
(() => {
|
||||
const maximumRetryCount = 7200;
|
||||
const retryIntervalMilliseconds = 1000;
|
||||
const rectModalBox = document.getElementById('reconnect-modal');
|
||||
const recModal = document.getElementById('reconnect-modal-text');
|
||||
|
||||
const startReconnectionProcess = () => {
|
||||
rectModalBox.style.display = 'block';
|
||||
|
||||
let isCanceled = false;
|
||||
|
||||
(async () => {
|
||||
for (let i = 0; i < maximumRetryCount; i++) {
|
||||
recModal.innerText = `Tentativo ${i + 1}...`;
|
||||
//recModal.innerText = `Tentativo riconnessione: ${i + 1} / ${maximumRetryCount}`;
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));
|
||||
|
||||
if (isCanceled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await Blazor.reconnect();
|
||||
if (!result) {
|
||||
// The server was reached, but the connection was rejected; reload the page.
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
// Successfully reconnected to the server.
|
||||
return;
|
||||
} catch {
|
||||
// Didn't reach the server; try again.
|
||||
}
|
||||
}
|
||||
|
||||
// Retried too many times; reload the page.
|
||||
location.reload();
|
||||
})();
|
||||
|
||||
return {
|
||||
cancel: () => {
|
||||
isCanceled = true;
|
||||
rectModalBox.style.display = 'none';
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
let currentReconnectionProcess = null;
|
||||
|
||||
Blazor.start({
|
||||
circuit: {
|
||||
reconnectionHandler: {
|
||||
onConnectionDown: () => {
|
||||
currentReconnectionProcess ??= startReconnectionProcess();
|
||||
console.log("Connection Lost!");
|
||||
},
|
||||
onConnectionUp: () => {
|
||||
currentReconnectionProcess?.cancel();
|
||||
currentReconnectionProcess = null;
|
||||
console.log("Connection restored!");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||
<meta name="description" content="GPW CORE SMART" />
|
||||
<meta name="author" content="EgalWare" />
|
||||
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
|
||||
<link rel="icon" href="images/favicon.png" type="image/png" />
|
||||
@* <base href="" /> *@
|
||||
<link rel="stylesheet" href="lib/css/bootstrap/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="lib/font-awesome/css/all.min.css" />
|
||||
<link rel="stylesheet" href="css/site.min.css" />
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="GPW.CORE.Smart8.styles.css" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<HeadOutlet />
|
||||
@* <HeadOutlet @rendermode=@(new InteractiveServerRenderMode(prerender: false)) /> *@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@* <Routes /> *@
|
||||
<Routes @rendermode=@(new InteractiveServerRenderMode(prerender: false)) />
|
||||
<div id="reconnect-modal" class="text-center">
|
||||
<div id="reconnect-modal-box" class="card px-2 bg-dark bg-gradient border border-info border-5 shadow rounded rounded-4 opacity-90">
|
||||
<div class="card-header fs-1 text-light row">
|
||||
<div class="col-2">
|
||||
<img class="img-fluid" src="images/favicon.png" />
|
||||
</div>
|
||||
<div class="col-8 text-center">GPW Smart</div>
|
||||
<div class="col-2">
|
||||
<img class="img-fluid" src="images/LogoEgwWhite.png" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body fs-2">
|
||||
<div class="d-flex justify-content-around py-3 my-5">
|
||||
<i class="fa-solid fa-network-wired fa-beat fa-2xl text-warning"></i>
|
||||
</div>
|
||||
<div id="reconnect-modal-text" class="d-flex justify-content-around my-5 text-white">Tentativo 999...</div>
|
||||
</div>
|
||||
<div class=" card-footer d-flex justify-content-around small text-secondary text-center">
|
||||
mancanza di comunicazione con il server: riconnessione in corso
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="lib/css/bootstrap/js/bootstrap.min.js"></script>
|
||||
@* <script type="text/javascript" src="lib/font-awesome/js/all.min.js"></script> *@
|
||||
<script type="text/javascript" src="lib/Chart.js/chart.js"></script>
|
||||
<script type="text/javascript" src="lib/luxon/luxon.js"></script>
|
||||
<script type="text/javascript" src="lib/chartjs-adapter-luxon/chartjs-adapter-luxon.js"></script>
|
||||
<script type="text/javascript" src="lib/chartBoot.js"></script>
|
||||
<script type="text/javascript" src="lib/WindowSize.js"></script>
|
||||
<script type="text/javascript" src="lib/UserAgent.js"></script>
|
||||
<script type="text/javascript" src="lib/ModalPopper.js"></script>
|
||||
<script type="text/javascript" src="lib/qrcode.js"></script>
|
||||
<script type="text/javascript" src="lib/qrHelper.js"></script>
|
||||
|
||||
@* <script src="_framework/blazor.web.js"></script> *@
|
||||
<script src="_framework/blazor.web.js" autostart="false"></script>
|
||||
<script src="lib/boot.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,76 @@
|
||||
|
||||
<div class="accordion mb-5" id="accordionExample">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading01">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse01" aria-expanded="false" aria-controls="collapse01">
|
||||
<b>Gestione Admin</b>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse01" class="accordion-collapse collapse show" aria-labelledby="heading01" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body small">
|
||||
Di seguito le procedure per resettare e reinviare via email l'authKey al dipendente, approvare le timbrature, mostrare il QRCode di login
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading02">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse02" aria-expanded="false" aria-controls="collapse02">
|
||||
<b>Fix pagine offline</b>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse02" class="accordion-collapse collapse" aria-labelledby="heading02" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body small">
|
||||
Nel caso il browser stia presentando pagine offline/archiviate come valide, traendo in inganno sulle effettive timbrature, si deve procedere con la pulizia della cache delle pagine offline.
|
||||
<br />
|
||||
Per farlo si deve seguire la procedura manuale di seguito riportata:
|
||||
<ul>
|
||||
<li>Aprire una nuova pagina dal browser chrome sul device</li>
|
||||
<li>inserire l'indirizzo seguente:<br /><b>chrome://offline-internals/</b></li>
|
||||
<li>eliminare almeno le pagine di<br /> <b>office.egalware.com</b></li>
|
||||
</ul>
|
||||
Purtroppo non è possibile automatizzare questa procedura ne aprire direttamente il link con un pulsante o una procedura poiché questo è vietato per motivi di sicurezza interna dai browser.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (isAuthKeyAdmin)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="headingOne">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
|
||||
<b>QRCode direct display</b>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<UserQrCode></UserQrCode>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="headingTwo">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
|
||||
<b>ADMIN Reset AuthKey via e-mail</b>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body p-1">
|
||||
<InvioLinkEmail IdxDipAdmin="@IdxDipendente"></InvioLinkEmail>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (isApprAdmin)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="headingThree">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
|
||||
<b>Approvazione Timbrature</b>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseThree" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
|
||||
<TimbAdmin IdxDipAdmin="@IdxDipendente"></TimbAdmin>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class AdminTask
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipendente { get; set; } = 0;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (IdxDipendente > 0)
|
||||
{
|
||||
await ReloadData();
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private bool isApprAdmin = false;
|
||||
|
||||
private bool isAuthKeyAdmin = false;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
private List<Dipendenti2RuoliModel>? listaRuoli { get; set; } = new List<Dipendenti2RuoliModel>();
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
// verifico se il dip sia admin...
|
||||
listaRuoli = await CDService.Dip2RuoliGetAll();
|
||||
if (listaRuoli != null)
|
||||
{
|
||||
isAuthKeyAdmin = listaRuoli.FirstOrDefault(x => x.IdxDipendente == IdxDipendente && x.CodRuolo == "ResetAuthKey") != null;
|
||||
isApprAdmin = listaRuoli.FirstOrDefault(x => x.IdxDipendente == IdxDipendente && x.CodRuolo == "AppMancTimb") != null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
|
||||
//private async void MService_EA_DipUpdated()
|
||||
//{
|
||||
// //idxDipendente = MService.IdxDipendente;
|
||||
// await Task.Delay(1);
|
||||
// await ReloadData();
|
||||
// await Task.Delay(1);
|
||||
// await InvokeAsync(StateHasChanged);
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<div class="buttons">
|
||||
@if (IsRicTimb)
|
||||
{
|
||||
<button class="mancTimb" @onclick="() => doRichiesta(true)"><i class="fa-solid fa-right-to-bracket"></i> ENTRATA</button>
|
||||
<button class="mancTimb" @onclick="() => doRichiesta(false)">USCITA <i class="fa-solid fa-right-from-bracket"></i></button>
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@if (nextIsEntrata)
|
||||
{
|
||||
<button class="entrata" @onclick="() => registraTimbratura(true)"><i class="fa-solid fa-right-to-bracket"></i> ENTRATA</button>
|
||||
<button class="uscitaDisabled" disabled>USCITA <i class="fa-solid fa-right-from-bracket"></i></button>
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="entrataDisabled" disabled><i class="fa-solid fa-right-to-bracket"></i> ENTRATA</button>
|
||||
<button class="uscita" @onclick="() => registraTimbratura(false)">USCITA <i class="fa-solid fa-right-from-bracket"></i></button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
@if (isRecording && false)
|
||||
{
|
||||
<dialog class="modal fade show" tabindex="-1" style="display:block; background-color: rgba(10,10,10,.6);" aria-modal="true" role="dialog">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="col-12 text-center my-0 py-3 alert alert-primary">
|
||||
<div class="d-flex justify-content-between py-4">
|
||||
<div class="text-start">
|
||||
<h1><i class="fa-solid fa-database"></i> working</h1>
|
||||
<b>@lastAction</b>
|
||||
</div>
|
||||
<div>
|
||||
<div class="spinner-grow" role="status" style="width: 3rem; height: 3rem;">
|
||||
<span class="visually-hidden">Sending...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
}
|
||||
|
||||
<div class="toast-container p-3 bottom-0 start-50 translate-middle-x">
|
||||
<div class="toast bg-light fade @toastCss" id="LiveToast2" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="toast-header">
|
||||
<span class="bg-primary px-2 text-light rounded me-2"><i class="fa-solid fa-clock"></i></span>
|
||||
<strong class="me-auto">Timbratura</strong>
|
||||
<small>@textDtAct</small>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
@lastAction
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,277 @@
|
||||
using GPW.CORE.Data;
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Data.DTO;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using System.Data;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class BottoniEntrEsc : IDisposable
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipendente { get; set; } = -1;
|
||||
|
||||
public bool isRecording { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public bool IsRicTimb { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public List<DailyDataDTO>? ListRecords
|
||||
{
|
||||
get => listRecords;
|
||||
set { listRecords = value; }
|
||||
}
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
listRecords = null;
|
||||
ListRecords = null;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Inserimento record timbratura
|
||||
/// </summary>
|
||||
/// <param name="IsUscita"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task<bool> DoAdd(bool IsUscita)
|
||||
{
|
||||
bool done = false;
|
||||
// var accessorie / base
|
||||
TimbratureModel currRecord = new TimbratureModel();
|
||||
string ipv4 = await MService.getDevIpAsync();
|
||||
int idxDip = MService.IdxDipendente;
|
||||
|
||||
// verifico se si trattasse di timbratura uscita da attivita' del giorno precedente...
|
||||
if (IsUscita)
|
||||
{
|
||||
// se il giorno precedente NON fosse stato chiuso...
|
||||
DateTime ieri = DataRif.Date.AddDays(-1);
|
||||
List<TimbratureModel> ListTimbIeri = await CDService.TimbratureDay(ieri, idxDip);
|
||||
// cerco ultima, se fosse entrata...
|
||||
if (ListTimbIeri != null && ListTimbIeri.Count > 0)
|
||||
{
|
||||
var isEntrata = false;
|
||||
var ultima = ListTimbIeri.OrderByDescending(x => x.DataOra).FirstOrDefault();
|
||||
if (ultima != null)
|
||||
{
|
||||
bool.TryParse($"{ultima.Entrata}", out isEntrata);
|
||||
if (isEntrata)
|
||||
{
|
||||
// chiedo verifica
|
||||
bool doSplit = await JSRuntime.InvokeAsync<bool>("confirm", "Sembra tu stia chiudendo un periodo iniziato in un giorno precedente, vuoi suddividere in set di timbrature per ogni giornata?");
|
||||
|
||||
// aggiungo chiusura alla timbratura di ieri alle 23.59
|
||||
currRecord = new TimbratureModel()
|
||||
{
|
||||
Entrata = false,
|
||||
Approv = false,
|
||||
CodTipoTimb = "Web",
|
||||
DataOra = ieri.AddDays(1).AddSeconds(-1),
|
||||
IdxDipendente = idxDip,
|
||||
Ipv4 = $"{ipv4}"
|
||||
};
|
||||
done = await CDService.TimbratureUpdate(currRecord);
|
||||
|
||||
if (done)
|
||||
{
|
||||
// aggiungo apertura ad oggi
|
||||
currRecord = new TimbratureModel()
|
||||
{
|
||||
Entrata = true,
|
||||
Approv = false,
|
||||
CodTipoTimb = "Web",
|
||||
DataOra = ieri.AddDays(1),
|
||||
IdxDipendente = idxDip,
|
||||
Ipv4 = $"{ipv4}"
|
||||
};
|
||||
done = await CDService.TimbratureUpdate(currRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// seleziono data-ora timbratura
|
||||
DateTime dtTimb = DateTime.Now;
|
||||
bool preApprov = true;
|
||||
// effettuo controllo: se IP non fosse interno --> arrotondo 5 minuti!
|
||||
if (!ipv4.Contains(LocalNet))
|
||||
{
|
||||
// arrotondo ingresso/uscita ai 5 minuti secondo sia entrata o uscita...
|
||||
dtTimb = Utils.DateRounded(DataRif, 5, IsUscita);
|
||||
preApprov = false;
|
||||
}
|
||||
// altrimenti
|
||||
currRecord = new TimbratureModel()
|
||||
{
|
||||
Entrata = !IsUscita,
|
||||
Approv = preApprov,
|
||||
CodTipoTimb = "Web",
|
||||
DataOra = dtTimb,
|
||||
IdxDipendente = idxDip,
|
||||
Ipv4 = $"{ipv4}"
|
||||
};
|
||||
|
||||
// aggiorno
|
||||
done = await CDService.TimbratureUpdate(currRecord);
|
||||
await Task.Delay(1);
|
||||
await ReloadData();
|
||||
return done;
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await ReloadData();
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private DateTime DataRif = DateTime.Now;
|
||||
private string LocalNet = "10.74";
|
||||
private string textDtAct = "";
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IHttpContextAccessor httpContextAccessor { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gestione timbrature interne/esterne (DA FARE!!!)
|
||||
/// </summary>
|
||||
private bool isInternal { get; set; } = false;
|
||||
|
||||
[Inject]
|
||||
private IJSRuntime JSRuntime { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Determina il tipo di chiamata ammessa successiva (entrata/uscita)
|
||||
/// </summary>
|
||||
private string lastAction
|
||||
{
|
||||
get => MService.LastAction;
|
||||
set => MService.LastAction = value;
|
||||
}
|
||||
|
||||
private List<DailyDataDTO>? listRecords { get; set; } = null;
|
||||
|
||||
[Inject]
|
||||
private MessageService MService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private NavigationManager NavManager { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Determina il tipo di chiamata ammessa successiva (entrata/uscita)
|
||||
/// </summary>
|
||||
private bool nextIsEntrata
|
||||
{
|
||||
get => MService.NextIsEntrata;
|
||||
set => MService.NextIsEntrata = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Orario in cui si fa richiesta
|
||||
/// </summary>
|
||||
private DateTime oraRichiestaTimb
|
||||
{
|
||||
get => MService.targetDateMancTimb;
|
||||
}
|
||||
|
||||
private string toastCss
|
||||
{
|
||||
get => isRecording ? "show" : "";
|
||||
}
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task doRichiesta(bool isEntrata)
|
||||
{
|
||||
TimbratureModel currRecord = new TimbratureModel();
|
||||
// recupero ip...
|
||||
string ipv4 = await MService.getDevIpAsync();
|
||||
if (string.IsNullOrEmpty(ipv4))
|
||||
{
|
||||
if (httpContextAccessor.HttpContext != null)
|
||||
{
|
||||
var remoteIp = $"{httpContextAccessor.HttpContext.Connection?.RemoteIpAddress}";
|
||||
// provo a recuperare ipV4...
|
||||
ipv4 = EgwCoreLib.Razor.Data.IpUtils.getLocalIpv4(remoteIp);
|
||||
}
|
||||
}
|
||||
// arrotondo ingresso/uscita ai 5 minuti secondo sia entrata o uscita...
|
||||
var dtTimb = Utils.DateRounded(oraRichiestaTimb, 5, !isEntrata);
|
||||
// genero record
|
||||
currRecord = new TimbratureModel()
|
||||
{
|
||||
Entrata = isEntrata,
|
||||
Approv = false,
|
||||
CodTipoTimb = "NoTim",
|
||||
DataOra = dtTimb,
|
||||
IdxDipendente = MService.IdxDipendente,
|
||||
Ipv4 = $"{ipv4}"
|
||||
};
|
||||
|
||||
await CDService.TimbratureInsRichiesta(currRecord);
|
||||
|
||||
NavManager.NavigateTo(NavManager.Uri, true);
|
||||
}
|
||||
|
||||
private async Task registraTimbratura(bool isEntrata)
|
||||
{
|
||||
DateTime adesso = DateTime.Now;
|
||||
textDtAct = $"{adesso:dddd dd.MM.yyyy}";
|
||||
string lastAct = isEntrata ? "INGRESSO" : "USCITA";
|
||||
lastAction = $"Registrato evento {lastAct} alle {adesso:HH:mm}";
|
||||
// inidico inizio update
|
||||
isRecording = true;
|
||||
bool done = await DoAdd(!isEntrata);
|
||||
// solo se ha fatto mostro udpate
|
||||
if (done)
|
||||
{
|
||||
nextIsEntrata = !isEntrata;
|
||||
MService.ReportTimbratura();
|
||||
// chiudo visualizzazione stato update
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
isRecording = false;
|
||||
}
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
DataRif = DateTime.Now;
|
||||
var confNet = await CDService.ConfigGetKey("LocalNet");
|
||||
LocalNet = confNet != null ? confNet.valore : "10";
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.buttonsAll {
|
||||
border-radius: 0.75rem;
|
||||
border: none;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
font-size: 1rem;
|
||||
box-shadow: 0 0.3rem 0.45rem rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
.entrata {
|
||||
border-radius: 0.75rem;
|
||||
border: none;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
font-size: 1rem;
|
||||
box-shadow: 0 0.3rem 0.45rem rgba(0, 0, 0, 0.8);
|
||||
background-color: #9B59B6;
|
||||
}
|
||||
.mancTimb {
|
||||
border-radius: 0.75rem;
|
||||
border: none;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
font-size: 1rem;
|
||||
box-shadow: 0 0.3rem 0.45rem rgba(0, 0, 0, 0.8);
|
||||
background-color: #2980B9;
|
||||
}
|
||||
.uscita {
|
||||
border-radius: 0.75rem;
|
||||
border: none;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
font-size: 1rem;
|
||||
box-shadow: 0 0.3rem 0.45rem rgba(0, 0, 0, 0.8);
|
||||
background-color: #4CAF50;
|
||||
}
|
||||
.entrataDisabled {
|
||||
border-radius: 0.75rem;
|
||||
border: none;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
font-size: 1rem;
|
||||
box-shadow: 0 0.3rem 0.45rem rgba(0, 0, 0, 0.8);
|
||||
border-color: #9B59B6;
|
||||
color: #000;
|
||||
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.uscitaDisabled {
|
||||
border-radius: 0.75rem;
|
||||
border: none;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
font-size: 1rem;
|
||||
box-shadow: 0 0.3rem 0.45rem rgba(0, 0, 0, 0.8);
|
||||
border-color: #4CAF50;
|
||||
color: #000;
|
||||
box-shadow: 0 0.1rem 0.2rem rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.buttonsAll {
|
||||
border-radius: 0.75rem;
|
||||
border: none;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 2.5rem;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
font-size: 1rem;
|
||||
box-shadow: 0 0.3rem 0.45rem rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.entrata {
|
||||
.buttonsAll;
|
||||
background-color: #9B59B6;
|
||||
}
|
||||
.mancTimb {
|
||||
.buttonsAll;
|
||||
background-color: #2980B9;
|
||||
}
|
||||
.uscita {
|
||||
.buttonsAll;
|
||||
background-color: #4CAF50;
|
||||
}
|
||||
|
||||
|
||||
.entrataDisabled {
|
||||
.buttonsAll;
|
||||
border-color: #9B59B6;
|
||||
color: #000;
|
||||
box-shadow: 0 0.1rem 0.2rem rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.uscitaDisabled {
|
||||
.buttonsAll;
|
||||
border-color: #4CAF50;
|
||||
color: #000;
|
||||
box-shadow: 0 0.1rem 0.2rem rgba(0,0,0,0.3);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.buttons{display:flex;justify-content:space-between;gap:1rem;width:100%;height:100%;}.buttonsAll{border-radius:.75rem;border:0;color:#fff;width:100%;height:2.5rem;text-align:center;display:inline-block;font-size:1rem;box-shadow:0 .3rem .45rem rgba(0,0,0,.8);}.entrata{border-radius:.75rem;border:0;color:#fff;width:100%;height:2.5rem;text-align:center;display:inline-block;font-size:1rem;box-shadow:0 .3rem .45rem rgba(0,0,0,.8);background-color:#9b59b6;}.mancTimb{border-radius:.75rem;border:0;color:#fff;width:100%;height:2.5rem;text-align:center;display:inline-block;font-size:1rem;box-shadow:0 .3rem .45rem rgba(0,0,0,.8);background-color:#2980b9;}.uscita{border-radius:.75rem;border:0;color:#fff;width:100%;height:2.5rem;text-align:center;display:inline-block;font-size:1rem;box-shadow:0 .3rem .45rem rgba(0,0,0,.8);background-color:#4caf50;}.entrataDisabled{border-radius:.75rem;border:0;color:#fff;width:100%;height:2.5rem;text-align:center;display:inline-block;font-size:1rem;box-shadow:0 .3rem .45rem rgba(0,0,0,.8);border-color:#9b59b6;color:#000;box-shadow:0 .1rem .2rem rgba(0,0,0,.3);}.uscitaDisabled{border-radius:.75rem;border:0;color:#fff;width:100%;height:2.5rem;text-align:center;display:inline-block;font-size:1rem;box-shadow:0 .3rem .45rem rgba(0,0,0,.8);border-color:#4caf50;color:#000;box-shadow:0 .1rem .2rem rgba(0,0,0,.3);}
|
||||
@@ -0,0 +1,214 @@
|
||||
|
||||
@if (ListRecords == null)
|
||||
{
|
||||
<div class="row m-2 placeholder-glow">
|
||||
<span class="placeholder col-4 my-1"></span>
|
||||
<span class="placeholder col-7 my-1"></span>
|
||||
<span class="placeholder col-8 my-1"></span>
|
||||
<span class="placeholder col-3 my-1"></span>
|
||||
</div>
|
||||
<LoadingData DisplaySize="LoadingData.CtrlSize.Large" DisplayMode="LoadingData.SpinMode.Growl"></LoadingData>
|
||||
<div class="row m-2 placeholder-glow">
|
||||
<span class="placeholder col-4 my-1"></span>
|
||||
<span class="placeholder col-7 my-1"></span>
|
||||
<span class="placeholder col-8 my-1"></span>
|
||||
<span class="placeholder col-3 my-1"></span>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div style="background-color: #34495E; border-radius: 12px; width:100%" class="pb-1 mb-5">
|
||||
<CalendarMonth DtRif="@dtCurr" MainCss="table table-dark table-borderless" DateSelected="DisplayDate" MonthChanged="ReloadMonth" DateCheck="@DateCheck" DateCheckBlock="@DateCFF" SingleWeek="@showDetail" SelDayCss="selDate rounded-top p-2" HeadStyle="color: #E67E22"></CalendarMonth>
|
||||
@if (showDetail)
|
||||
{
|
||||
<div class="statsContainer m-2 mt-0">
|
||||
<div class="d-flex justify-content-between px-1 text-light small">
|
||||
@if (datiGiorno != null)
|
||||
{
|
||||
@if (datiGiorno.TimbrExpl != null)
|
||||
{
|
||||
@if (currMode == CalendarModeEnum.modoControllo.showGauge)
|
||||
{
|
||||
<div>
|
||||
<div type="button" class="d-flex justify-content-between text-center bg-transparent text-light border-0">
|
||||
<div class="pe-1 text-decoration-none">
|
||||
Entrata
|
||||
@if (datiGiorno.TimbrExpl.Entrata1 != null)
|
||||
{
|
||||
<div class="@cssChooseEntr(datiGiorno.TimbrExpl.Entrata1)">
|
||||
|
||||
@($"{datiGiorno.TimbrExpl.Entrata1:HH:mm}")
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-success">
|
||||
@emptyTime
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
Uscita
|
||||
@if (datiGiorno.TimbrExpl.Uscita1 != null)
|
||||
{
|
||||
<div class="@cssChooseEsc(datiGiorno.TimbrExpl.Uscita1)">
|
||||
|
||||
@($"{datiGiorno.TimbrExpl.Uscita1:HH:mm}")
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-primary">
|
||||
@emptyTime
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="d-flex justify-content-between text-center bg-transparent text-light border-0">
|
||||
<div class="pe-1">
|
||||
Entrata
|
||||
@if (datiGiorno.TimbrExpl.Entrata2 != null)
|
||||
{
|
||||
<div class="@cssChooseEntr(datiGiorno.TimbrExpl.Entrata2)">
|
||||
|
||||
@($"{datiGiorno.TimbrExpl.Entrata2:HH:mm}")
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-success">
|
||||
@emptyTime
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
Uscita
|
||||
@if (datiGiorno.TimbrExpl.Uscita2 != null)
|
||||
{
|
||||
<div class="@cssChooseEsc(datiGiorno.TimbrExpl.Uscita2)">
|
||||
|
||||
@($"{datiGiorno.TimbrExpl.Uscita2:HH:mm}")
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-primary">
|
||||
@emptyTime
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button class="d-flex justify-content-between text-center bg-transparent text-light border-0">
|
||||
<div class="pe-1">
|
||||
Entrata
|
||||
@if (datiGiorno.TimbrExpl.Entrata3 != null)
|
||||
{
|
||||
<div class="@cssChooseEntr(datiGiorno.TimbrExpl.Entrata3)">
|
||||
|
||||
@($"{datiGiorno.TimbrExpl.Entrata3:HH:mm}")
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-success">
|
||||
@emptyTime
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
Uscita
|
||||
@if (datiGiorno.TimbrExpl.Uscita3 != null)
|
||||
{
|
||||
<div class="@cssChooseEsc(datiGiorno.TimbrExpl.Uscita3)">
|
||||
|
||||
@($"{datiGiorno.TimbrExpl.Uscita3:HH:mm}")
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="text-primary">
|
||||
@emptyTime
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert bg-warning w-100 text-center mt-1 mb-2 p-1" @onclick="()=>setModeRichTimb()">
|
||||
NESSUN DATO DISPONIBILE
|
||||
</div>
|
||||
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@if (datiGiorno != null)
|
||||
{
|
||||
@if (currMode == CalendarModeEnum.modoControllo.showGauge)
|
||||
{
|
||||
<div class="d-flex justify-content-between">
|
||||
<div @onclick="()=>setModeProgetti()">
|
||||
<CircleGauge Titolo="@datiGiorno.OreMinComm" Testo="Caricate" maxVal="480" currVal="@datiGiorno.MinComm" strokeColorVal="#F1C40F" StyleTitolo="font-size: 2.7rem; font-weight:bold; fill: white;" StyleTesto="font-size: 1rem; fill: #ACACAC;" ShowCircleBtn="true"></CircleGauge>
|
||||
</div>
|
||||
<div @onclick="()=>setModeRichTimb()">
|
||||
<CircleGauge Titolo="@datiGiorno.OreMinLav" Testo="Lavorate" maxVal="480" currVal="@datiGiorno.MinLav" strokeColorVal="#00FF00" StyleTitolo="font-size: 2.7rem; font-weight:bold; fill: white;" StyleTesto="font-size: 1rem; fill: #ACACAC;" ShowCircleBtn="true"></CircleGauge>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (currMode == CalendarModeEnum.modoControllo.showTimbr)
|
||||
{
|
||||
<div class="p-2 text-center text-light">
|
||||
<TimbUser IdxDipCurr="IdxDipendente" ListTimbRich="listTimb" ReqReload="ReloadTimb"></TimbUser>
|
||||
</div>
|
||||
}
|
||||
else if (currMode == CalendarModeEnum.modoControllo.showProj)
|
||||
{
|
||||
<div class="p-1 text-center text-light">
|
||||
<div>
|
||||
<CompProj ListProgetti="listaP" ReqReload="ForceReload" DtRif="@datiGiorno.DtRif"></CompProj>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (currMode == CalendarModeEnum.modoControllo.showTempRil)
|
||||
{
|
||||
<CheckTemp TargetDate="@datiGiorno.DtRif" IdxDipendente="@IdxDipendente"></CheckTemp>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="p-2 text-center text-light">
|
||||
N.A.
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<div class="row p-2 pt-3">
|
||||
<div class="col-6 d-grid gap-2">
|
||||
<button class="btnReset" @onclick="()=>resetCal()"><i class="fa-solid fa-circle-chevron-left"></i> Mese</button>
|
||||
</div>
|
||||
<div class="col-6 d-grid gap-2">
|
||||
@if (currMode == CalendarModeEnum.modoControllo.showGauge)
|
||||
{
|
||||
<button class="btnTemp" @onclick="() => showTempRil()"><i class="fa-solid fa-temperature-low"></i> Temperatura</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btnGiorno" @onclick="()=> backToGauge()"><i class="fa-solid fa-circle-chevron-left"></i> Giorno</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
using EgwCoreLib.Razor.Data;
|
||||
using GPW.CORE.Data;
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Data.DTO;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class Calendario : IDisposable
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public CalendarModeEnum.modoControllo currMode { get; set; } = CalendarModeEnum.modoControllo.showCalendar;
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipendente { get; set; } = -1;
|
||||
|
||||
[Parameter]
|
||||
public List<CalFesteFerieModel>? ListFermateAzienda { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<DailyDataDTO>? ListRecords { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<RegRichiesteModel>? ListRecordsRichieste
|
||||
{
|
||||
get => listRecordsRichieste;
|
||||
set => listRecordsRichieste = value;
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> ReqReload { get; set; }
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DateCheck = new Dictionary<DateTime, string>();
|
||||
DateCFF = new Dictionary<DateTime, string>();
|
||||
ListRecords = null;
|
||||
listRecordsRichieste = null;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
protected DailyDataDTO? datiGiorno = null;
|
||||
protected string emptyTime = "--/--";
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected string baseUrl
|
||||
{
|
||||
get => configuration.GetValue<string>("OptConf:BaseUrl") ?? "~/";
|
||||
}
|
||||
|
||||
[Inject]
|
||||
protected IConfiguration configuration { get; set; } = null!;
|
||||
|
||||
protected DateTime dtMancTimb { get; set; } = Utils.InitDatetime(DateTime.Now, 5);
|
||||
protected bool isApprovedEntr { get; set; } = true;
|
||||
protected bool isApprovedEsc { get; set; } = true;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
ReloadData();
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private Dictionary<DateTime, string> DateCFF = new Dictionary<DateTime, string>();
|
||||
private Dictionary<DateTime, string> DateCheck = new Dictionary<DateTime, string>();
|
||||
|
||||
private Dictionary<DateTime, string> DateFerFestCheck = new Dictionary<DateTime, string>();
|
||||
|
||||
private Dictionary<DateTime, string> DatePermCheck = new Dictionary<DateTime, string>();
|
||||
|
||||
private List<RegAttivitaModel>? listaP = new List<RegAttivitaModel>();
|
||||
|
||||
private List<List<RegAttivitaModel>?> listRA = null!;
|
||||
|
||||
private List<TimbratureModel>? listTimb = null;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
private DateTime dtCurr { get; set; } = DateTime.Today;
|
||||
|
||||
private List<CalFesteFerieModel>? listCFF { get; set; } = null;
|
||||
private List<DailyDataDTO>? listRecords { get; set; } = null;
|
||||
private List<RegRichiesteModel>? listRecordsRichieste { get; set; } = null;
|
||||
|
||||
[Inject]
|
||||
private MessageService MService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private NavigationManager navManager { get; set; } = null!;
|
||||
|
||||
private bool showDetail { get; set; } = false;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task backToGauge()
|
||||
{
|
||||
setModeDetail();
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
private string cssChooseEntr(DateTime? dtRif)
|
||||
{
|
||||
string answ = "";
|
||||
try
|
||||
{
|
||||
answ = (isApprovedEntr) ? "text-entrata" : "text-danger";
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
private string cssChooseEsc(DateTime? dtRif)
|
||||
{
|
||||
string answ = "";
|
||||
try
|
||||
{
|
||||
answ = (isApprovedEsc) ? "text-uscita" : "text-danger";
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
private void DisplayDate(DateTime dtSel)
|
||||
{
|
||||
dtCurr = dtSel;
|
||||
MService.targetDate = dtSel;
|
||||
MService.targetDateMancTimb = dtCurr;
|
||||
showDetail = true;
|
||||
// se sono in calendario --> mostro dettagli...
|
||||
if (currMode == CalendarModeEnum.modoControllo.showCalendar)
|
||||
{
|
||||
setModeDetail();
|
||||
}
|
||||
updateDetail();
|
||||
MService.ReportDateChange();
|
||||
}
|
||||
|
||||
/// <summary> Richiesta evento reload --> lo passa al componente superiore <param name="doForce"></param>
|
||||
private async Task ForceReload(bool doForce)
|
||||
{
|
||||
await ReqReload.InvokeAsync(doForce);
|
||||
}
|
||||
|
||||
private void ReloadData()
|
||||
{
|
||||
DateCheck = new Dictionary<DateTime, string>();
|
||||
DateCFF = new Dictionary<DateTime, string>();
|
||||
string cssOkComm = "bg-success text-light rounded-circle p-2";
|
||||
string cssOkLav = "bg-warning text-light rounded-circle p-2";
|
||||
string cssToday = "bg-info text-dark rounded-circle p-2";
|
||||
string cssFest = "bg-danger text-warning rounded-pill2 px-0 opacity-50";
|
||||
string cssChius = "bg-warning text-dark rounded-pill2 px-0 opacity-50";
|
||||
string cssFerie = "bg-success text-light px-0 opacity-50";
|
||||
|
||||
// recupero le fermate aziendali
|
||||
if (ListFermateAzienda != null && ListFermateAzienda.Count > 0)
|
||||
{
|
||||
string cssSpec = "";
|
||||
foreach (var item in ListFermateAzienda)
|
||||
{
|
||||
cssSpec = item.codGiust == "FEST" ? cssFest : cssChius;
|
||||
if (!DateCFF.ContainsKey(item.data))
|
||||
{
|
||||
if (item.data.DayOfWeek != DayOfWeek.Saturday && item.data.DayOfWeek != DayOfWeek.Sunday)
|
||||
{
|
||||
DateCFF.Add(item.data, cssSpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// aggiungo ferie dipendente
|
||||
if (ListRecordsRichieste != null && ListRecordsRichieste.Count > 0)
|
||||
{
|
||||
string cssSpec = "";
|
||||
foreach (var item in ListRecordsRichieste)
|
||||
{
|
||||
cssSpec = item.CodGiust == "FER" ? cssFerie : "";
|
||||
// creo il set di date
|
||||
int numDay = item.DtEnd.Subtract(item.DtStart).Days;
|
||||
for (int i = 0; i <= numDay; i++)
|
||||
{
|
||||
DateTime currDay = item.DtStart.AddDays(i);
|
||||
if (!DateCFF.ContainsKey(currDay))
|
||||
{
|
||||
if (currDay.DayOfWeek != DayOfWeek.Saturday && currDay.DayOfWeek != DayOfWeek.Sunday)
|
||||
{
|
||||
DateCFF.Add(currDay, cssSpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ListRecords != null)
|
||||
{
|
||||
string currCss = "";
|
||||
foreach (var giorno in ListRecords)
|
||||
{
|
||||
currCss = giorno.OreLav == 0 ? "" : cssOkLav;
|
||||
if (giorno.DtRif == DateTime.Today)
|
||||
{
|
||||
currCss = cssToday;
|
||||
}
|
||||
else if (Math.Abs((giorno.OreLav - giorno.OreComm)) < 0.5 && giorno.OreLav > 0)
|
||||
{
|
||||
currCss = cssOkComm;
|
||||
}
|
||||
DateCheck.Add(giorno.DtRif, currCss);
|
||||
}
|
||||
// aggiorno dettagli
|
||||
updateDetail();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReloadMonth(DateTime dtSel)
|
||||
{
|
||||
dtCurr = dtSel;
|
||||
MService.targetDate = dtSel;
|
||||
showDetail = false;
|
||||
await Task.Delay(1);
|
||||
MService.ReportDateChange();
|
||||
}
|
||||
|
||||
// richiesta update + ritorno a gauge...
|
||||
private async Task ReloadTimb(bool doForce)
|
||||
{
|
||||
await ForceReload(doForce);
|
||||
await backToGauge();
|
||||
}
|
||||
|
||||
private async Task resetCal()
|
||||
{
|
||||
showDetail = false;
|
||||
MService.isRicTimb = false;
|
||||
MService.setModeCalendar();
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imposta modalit� dettaglio (mostra i gauge)
|
||||
/// </summary>
|
||||
private void setModeDetail()
|
||||
{
|
||||
MService.isRicTimb = false;
|
||||
MService.setModeGauge();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imposta modalit� progetti (x ora rimanda a pagina progetti)
|
||||
/// </summary>
|
||||
private void setModeProgetti()
|
||||
{
|
||||
MService.isRicTimb = false;
|
||||
MService.setModeProj();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imposta modalit� mancate timbrature
|
||||
/// </summary>
|
||||
private void setModeRichTimb()
|
||||
{
|
||||
MService.isRicTimb = true;
|
||||
MService.setModeRichTimb();
|
||||
}
|
||||
|
||||
private void ShowTemp()
|
||||
{
|
||||
MService.targetDate = dtCurr;
|
||||
navManager.NavigateTo($"{baseUrl}TempRil");
|
||||
}
|
||||
|
||||
private void showTempRil()
|
||||
{
|
||||
MService.isRicTimb = false;
|
||||
MService.setModeTempRil();
|
||||
}
|
||||
|
||||
private async void updateDetail()
|
||||
{
|
||||
isApprovedEntr = true;
|
||||
isApprovedEsc = true;
|
||||
listaP.Clear();
|
||||
if (ListRecords != null)
|
||||
{
|
||||
datiGiorno = ListRecords.Where(x => x.DtRif == dtCurr).FirstOrDefault();
|
||||
listRA = ListRecords.Select(x => x.ListRA).ToList();
|
||||
|
||||
foreach (var item in listRA)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
foreach (var k in item)
|
||||
{
|
||||
if (k != null)
|
||||
{
|
||||
if (k.Inizio.Date == dtCurr.Date)
|
||||
{
|
||||
listaP.Add(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
listTimb = await CDService.TimbratureDay(dtCurr, IdxDipendente);
|
||||
if (listTimb != null)
|
||||
{
|
||||
if (listTimb.Count != 0)
|
||||
{
|
||||
foreach (var item in listTimb)
|
||||
{
|
||||
if (item.Approv == false && item.Entrata == true)
|
||||
{
|
||||
isApprovedEntr = false;
|
||||
}
|
||||
else if (item.Approv == false && item.Entrata == false)
|
||||
{
|
||||
isApprovedEsc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
.statsContainer {
|
||||
border: 0.3rem solid #E67E22;
|
||||
border-radius: 0.8rem;
|
||||
/*border-radius: 0rem 0rem 0.8rem 0.8rem;*/
|
||||
/*margin: 0px 10px 20px 10px;*/
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.baseBtn {
|
||||
height: 2.5rem;
|
||||
border: none;
|
||||
border-radius: 0.8rem;
|
||||
color: #fff;
|
||||
}
|
||||
.btnReset {
|
||||
height: 2.5rem;
|
||||
border: none;
|
||||
border-radius: 0.8rem;
|
||||
color: #fff;
|
||||
background-color: #1AB7EA;
|
||||
}
|
||||
.btnGiorno {
|
||||
height: 2.5rem;
|
||||
border: none;
|
||||
border-radius: 0.8rem;
|
||||
color: #fff;
|
||||
background-color: #E67E22;
|
||||
}
|
||||
.btnTemp {
|
||||
height: 2.5rem;
|
||||
border: none;
|
||||
border-radius: 0.8rem;
|
||||
color: #fff;
|
||||
background-color: #1ABC9C;
|
||||
}
|
||||
.timeContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.timeContainer input {
|
||||
border: none;
|
||||
border-radius: 0.8rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
margin: 2rem;
|
||||
background-color: #d9d9d9;
|
||||
font-size: 3rem;
|
||||
}
|
||||
input[type="time"]::-webkit-calendar-picker-indicator {
|
||||
color: #fff;
|
||||
}
|
||||
.text-entrata {
|
||||
color: #38CF60;
|
||||
}
|
||||
.text-uscita {
|
||||
color: #40C4FF;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
.statsContainer {
|
||||
border: 0.3rem solid #E67E22;
|
||||
border-radius: 0.8rem;
|
||||
/*border-radius: 0rem 0rem 0.8rem 0.8rem;*/
|
||||
/*margin: 0px 10px 20px 10px;*/
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.baseBtn{
|
||||
height: 2.5rem;
|
||||
border: none;
|
||||
border-radius: 0.8rem;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
.btnReset {
|
||||
.baseBtn;
|
||||
background-color: #1AB7EA;
|
||||
}
|
||||
.btnGiorno {
|
||||
.baseBtn;
|
||||
background-color: #E67E22;
|
||||
}
|
||||
.btnTemp {
|
||||
.baseBtn;
|
||||
background-color: #1ABC9C;
|
||||
}
|
||||
|
||||
|
||||
.timeContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.timeContainer input {
|
||||
border: none;
|
||||
border-radius: 0.8rem;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
margin: 2rem;
|
||||
background-color: #d9d9d9;
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
input[type="time"]::-webkit-calendar-picker-indicator {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.text-entrata{
|
||||
color: #38CF60;
|
||||
}
|
||||
|
||||
.text-uscita {
|
||||
color: #40C4FF;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.statsContainer{border:.3rem solid #e67e22;border-radius:.8rem;padding-bottom:10px;}.baseBtn{height:2.5rem;border:0;border-radius:.8rem;color:#fff;}.btnReset{height:2.5rem;border:0;border-radius:.8rem;color:#fff;background-color:#1ab7ea;}.btnGiorno{height:2.5rem;border:0;border-radius:.8rem;color:#fff;background-color:#e67e22;}.btnTemp{height:2.5rem;border:0;border-radius:.8rem;color:#fff;background-color:#1abc9c;}.timeContainer{width:100%;height:100%;display:flex;justify-content:center;}.timeContainer input{border:0;border-radius:.8rem;padding-left:.5rem;padding-right:.5rem;margin:2rem;background-color:#d9d9d9;font-size:3rem;}input[type="time"]::-webkit-calendar-picker-indicator{color:#fff;}.text-entrata{color:#38cf60;}.text-uscita{color:#40c4ff;}
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
<div class="row px-1">
|
||||
<div class="col-12">
|
||||
<div class="row my-1">
|
||||
<div class="col-6">
|
||||
@if (currRecord != null)
|
||||
{
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text" style="width: 2.5em;">
|
||||
<i class="fas fa-thermometer-half"></i>
|
||||
<small>°C</small>
|
||||
</span>
|
||||
<input type="number" step="0.1" min="35" max="42" class="form-control text-end" @bind="@currRecord.TempRil"></input>
|
||||
<button class="btn btn-success" title="Salva temperatura" @onclick="() => DoSave()"><i class="far fa-save"></i></button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<LoadingData DisplaySize="LoadingData.CtrlSize.Small"></LoadingData>
|
||||
}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="input-group input-group-sm">
|
||||
<select @bind="@numDays" class="form-select text-end" title="finestra analisi">
|
||||
<option value="30">1 Mese</option>
|
||||
<option value="60">2 Mesi</option>
|
||||
<option value="90">3 Mesi</option>
|
||||
<option value="180">6 Mesi</option>
|
||||
<option value="365">1 Anno</option>
|
||||
<option value="730">2 Anno</option>
|
||||
<option value="1825">5 Anni</option>
|
||||
</select>
|
||||
<span class="input-group-text">
|
||||
<i class="far fa-calendar-alt"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row mx-0">
|
||||
<div class="col-12">
|
||||
@if (listRilTemp != null)
|
||||
{
|
||||
<ChartTS Id="TempRil" DataTS="@listRilTemp" LineColor="rgb(7, 173, 236)" BackColor="rgba(107, 223, 255, 0.5)" ChartLabel="Temperatura Rilevata" AnimationTime="0" TextColor="rgba(255,255,255,.8)" GridColor="rgba(160,160,160,.4)"></ChartTS>
|
||||
<ChartHist Id="FreqTemp1" Data="@histData" Labels="@histLabel" LineColor="rgb(7, 173, 236)" BackColor="rgba(107, 213, 255, 0.5)" ChartLabel="Freq. Osservate" AnimationTime="0" TextColor="rgba(255,255,255,.8)" GridColor="rgba(200,200,255,.4)"></ChartHist>
|
||||
}
|
||||
else
|
||||
{
|
||||
<LoadingData DisplaySize="LoadingData.CtrlSize.Small"></LoadingData>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,152 @@
|
||||
using EgwCoreLib.Razor.Data;
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class CheckTemp
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> CloseReq { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipendente { get; set; } = -1;
|
||||
|
||||
[Parameter]
|
||||
public DateTime TargetDate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _targetDate;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_targetDate = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
protected int _numDays = 30;
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected DateTime _targetDate { get; set; } = DateTime.Today;
|
||||
|
||||
protected RilievoTempModel? currRecord { get; set; } = null;
|
||||
|
||||
protected string[]? histData { get; set; } = null;
|
||||
|
||||
protected string[]? histLabel { get; set; } = null;
|
||||
|
||||
protected List<chartJsData.chartJsTSerie>? listRilTemp { get; set; } = null;
|
||||
|
||||
protected List<CheckVc19Model>? listVC19 { get; set; } = null;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected async Task addDays(int numDay)
|
||||
{
|
||||
TargetDate = TargetDate.AddDays(numDay);
|
||||
await ReloadData();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indico CurrItem selezionato
|
||||
/// </summary>
|
||||
protected async void DoClose()
|
||||
{
|
||||
await CloseReq.InvokeAsync(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Effettua salvataggio misurazione temperatura
|
||||
/// </summary>
|
||||
protected async void DoSave()
|
||||
{
|
||||
// chiamo classe gestione che salva e resetta cache dati...
|
||||
if (currRecord != null)
|
||||
{
|
||||
await CDService.RilTempUpdate(currRecord);
|
||||
}
|
||||
|
||||
// chiamo chiusura!
|
||||
await CloseReq.InvokeAsync(true);
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await ReloadData();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
protected async Task ReloadData()
|
||||
{
|
||||
DateTime inizio = TargetDate.AddDays(1 - numDays);
|
||||
DateTime fine = TargetDate.AddDays(1);
|
||||
// recupero dati completi...
|
||||
var rawVC19 = await CDService.CheckVC19List(IdxDipendente, fine.AddDays(-15), fine);
|
||||
var rawData = await CDService.RilTempList(IdxDipendente, inizio, fine);
|
||||
|
||||
// calcolo dati derivati
|
||||
listVC19 = rawVC19.OrderByDescending(x => x.DtCheck).ToList();
|
||||
listRilTemp = rawData.Select(r => new chartJsData.chartJsTSerie()
|
||||
{ x = r.DtRilievo, y = r.TempRil }).ToList();
|
||||
|
||||
// calcolo hist frequenza con EFCore: https://entityframeworkcore.com/knowledge-base/60871048/group-by-and-to-dictionary-in-ef-core-3-1
|
||||
var histDict = rawData.GroupBy(r => r.TempRil.ToString("N1")).Select(g => new
|
||||
{
|
||||
g.Key,
|
||||
Count = g.Count()
|
||||
}).OrderBy(d => d.Key).ToDictionary(x => x.Key, x => x.Count.ToString());
|
||||
histData = histDict.Values.ToArray();
|
||||
histLabel = histDict.Keys.ToArray();
|
||||
// cerco se c'� dato odierno della temperatura...
|
||||
currRecord = rawData.Where(x => x.DtRilievo == TargetDate).FirstOrDefault();
|
||||
if (currRecord == null)
|
||||
{
|
||||
currRecord = new RilievoTempModel()
|
||||
{
|
||||
IdxDipendente = IdxDipendente,
|
||||
DtRilievo = TargetDate,
|
||||
TempRil = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
private int numDays
|
||||
{
|
||||
get
|
||||
{
|
||||
return _numDays;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_numDays = value;
|
||||
var pUpd = Task.Run(async () => await ReloadData());
|
||||
pUpd.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Properties
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
<div class="container d-flex justify-content-between">
|
||||
<div class="title @homeCss" @onclick="ReturnHome">
|
||||
<i class="fa-solid fa-home"></i>
|
||||
<sub>@homeMessage</sub>
|
||||
</div>
|
||||
<div class="username" @onclick="() => ForceReset()">@currDip</div>
|
||||
</div>
|
||||
@@ -0,0 +1,83 @@
|
||||
using EgwCoreLib.Razor.Data;
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class CmpTop : IDisposable
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public ConfigDTO CurrConf { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public UserDTO? CurrUser { get; set; } = null;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> EC_ForceReset { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> EC_ReturnHome { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool IpIsLocal { get; set; } = false;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected string currDip
|
||||
{
|
||||
get => CurrUser != null ? CurrUser.DisplayName : "N.A. [0]";
|
||||
}
|
||||
|
||||
protected string homeCss
|
||||
{
|
||||
get => IpIsLocal ? "text-light" : "text-danger";
|
||||
}
|
||||
|
||||
protected string homeMessage
|
||||
{
|
||||
get => IpIsLocal ? "INT" : "EXT";
|
||||
}
|
||||
|
||||
[Inject]
|
||||
protected IJSRuntime JSRuntime { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected MessageService MService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected NavigationManager navManager { get; set; } = null!;
|
||||
|
||||
protected string Nome { get; set; } = "";
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected async Task ForceReset()
|
||||
{
|
||||
await EC_ForceReset.InvokeAsync(true);
|
||||
}
|
||||
|
||||
protected async Task ReturnHome()
|
||||
{
|
||||
await EC_ReturnHome.InvokeAsync(true);
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
.container {
|
||||
background-color: #34495E;
|
||||
height: 3.5rem;
|
||||
width: 100%;
|
||||
border-radius: 0px 0px 12px 12px;
|
||||
border: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
.username {
|
||||
color: #ffffff;
|
||||
padding-top: 10px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.title {
|
||||
color: #ffffff;
|
||||
padding-top: 10px;
|
||||
font-size: 1.25rem;
|
||||
text-shadow: 0px 3px 4px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
body {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #34495E;
|
||||
height: 3.5rem;
|
||||
width: 100%;
|
||||
border-radius: 0px 0px 12px 12px;
|
||||
border: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.username {
|
||||
color: #ffffff;
|
||||
padding-top: 10px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #ffffff;
|
||||
padding-top: 10px;
|
||||
font-size: 1.25rem;
|
||||
text-shadow: 0px 3px 4px rgba(0,0,0,0.7);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
body{margin:0;padding:0;width:100%;}.container{background-color:#34495e;height:3.5rem;width:100%;border-radius:0 0 12px 12px;border:2px;text-align:center;}.username{color:#fff;padding-top:10px;font-size:1.1rem;}.title{color:#fff;padding-top:10px;font-size:1.25rem;text-shadow:0 3px 4px rgba(0,0,0,.7);}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
<div class="toast-container py-2 top-0 start-50 translate-middle-x" style="position: fixed; top: 0;">
|
||||
<div class="toast bg-warning fade @toastCss" id="LiveToast2" role="alert">
|
||||
<div class="toast-header">
|
||||
<span class="bg-warning py-1 px-2 text-light rounded me-2"><i class="fa-regular fa-copy"></i></span>
|
||||
<strong class="me-auto">@lastAction</strong>
|
||||
<small>@textDtAct</small>
|
||||
<button type="button" class="btn-close" aria-label="Close" @onclick="() => resetClone()"></button>
|
||||
</div>
|
||||
<div class="toast-body text-dark small">
|
||||
@if (ClonedRA != null)
|
||||
{
|
||||
<SingleRaDisplay CurrItem="@ClonedRA" EnableAction="false"></SingleRaDisplay>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (CurrRA != null)
|
||||
{
|
||||
<dialog class="modal fade show p-0" tabindex="-1" style="display:block; background-color: rgba(10,10,10,.6);" aria-modal="true" role="dialog">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<RegAttEditor CurrRecord="@CurrRA" ItemReset="UpdRegAttData" ItemUpdated="UpdRegAttData"></RegAttEditor>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
}
|
||||
|
||||
<ProjectsList ListRA="ListProgetti" EnableAdmin="false" EnableEdit="true" ReqUpdate="DoUpdate" ItemCloned="SaveCloned" ItemSelected="EditItem" ActionPaste="ExecPaste"></ProjectsList>
|
||||
@@ -0,0 +1,243 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class CompProj
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public DateTime DtRif { get; set; } = DateTime.Today;
|
||||
|
||||
public int EndHour { get; set; } = 16;
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipCurr { get; set; } = 0;
|
||||
|
||||
[Parameter]
|
||||
public List<RegAttivitaModel>? ListProgetti
|
||||
{
|
||||
get => listRA;
|
||||
set => listRA = value;
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> ReqReload { get; set; }
|
||||
|
||||
public int StartHour { get; set; } = 8;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper valori con calcolo la lista comprensiva di spazi vuoti da quella ricevuta...
|
||||
/// </summary>
|
||||
protected List<RegAttivitaModel>? listRA
|
||||
{
|
||||
get => _listRA;
|
||||
set => _listRA = value != null ? ListRegAtt(value) : new List<RegAttivitaModel>();
|
||||
}
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Aggiorno e riporto update
|
||||
/// </summary>
|
||||
protected async void DoUpdate(bool forceReload)
|
||||
{
|
||||
if (forceReload)
|
||||
{
|
||||
await ReloadData();
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task EditItem(RegAttivitaModel selRec)
|
||||
{
|
||||
ClonedRA = null;
|
||||
CurrRA = selRec;
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Esegue operazione Paste(incolla) del record attualmente in memoria
|
||||
/// </summary>
|
||||
/// <param name="pasteRec"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task ExecPaste(RegAttivitaModel pasteRec)
|
||||
{
|
||||
// usa i dati del record ricevuto x indicare inizio...
|
||||
ClonedRA.Inizio = pasteRec.Inizio;
|
||||
// fine e durata calcolate...
|
||||
if (pasteRec.OreTot < ClonedRA.OreTot)
|
||||
{
|
||||
ClonedRA.Fine = pasteRec.Fine;
|
||||
ClonedRA.OreTot = pasteRec.OreTot;
|
||||
}
|
||||
else
|
||||
{
|
||||
ClonedRA.Fine = pasteRec.Inizio.Add(ClonedRA.Durata);
|
||||
}
|
||||
// chiama insert ed invalida cache...
|
||||
await CDService.RegAttUpdate(ClonedRA);
|
||||
// svuota cache x copy/paste
|
||||
ClonedRA = null;
|
||||
await Task.Delay(1);
|
||||
// richiesta rilettura dati...
|
||||
await ReqReload.InvokeAsync(true);
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
protected async Task SaveCloned(RegAttivitaModel clonedRec)
|
||||
{
|
||||
ClonedRA = clonedRec;
|
||||
textDtAct = $"{DateTime.Now:ddd dd.MM.yyyy}";
|
||||
lastAction = $"Clonato Attivita";
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
protected async Task UpdRegAttData()
|
||||
{
|
||||
isLoading = true;
|
||||
CurrRA = null;
|
||||
// richiesta rilettura dati...
|
||||
await ReqReload.InvokeAsync(true);
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private string lastAction = "";
|
||||
private string textDtAct = "";
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
private List<RegAttivitaModel>? _listRA { get; set; } = null;
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gestione ricezione evento + record RA clonato
|
||||
/// </summary>
|
||||
/// <param name="clonedRec"></param>
|
||||
/// <returns></returns>
|
||||
private RegAttivitaModel? ClonedRA
|
||||
{
|
||||
get => MService.clonedRA;
|
||||
set => MService.clonedRA = value;
|
||||
}
|
||||
|
||||
private RegAttivitaModel? CurrRA { get; set; } = null;
|
||||
private bool isLoading { get; set; } = false;
|
||||
|
||||
[Inject]
|
||||
private MessageService MService { get; set; } = null!;
|
||||
|
||||
private bool showToast
|
||||
{
|
||||
get => MService.clonedRA != null;
|
||||
}
|
||||
|
||||
private string toastCss
|
||||
{
|
||||
get => showToast ? "show" : "";
|
||||
}
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private List<RegAttivitaModel> ListRegAtt(List<RegAttivitaModel> ListOnlyRA)
|
||||
{
|
||||
// init
|
||||
List<RegAttivitaModel> result = new List<RegAttivitaModel>();
|
||||
if (ListOnlyRA != null && ListOnlyRA.Count > 0)
|
||||
{
|
||||
// prendo prima attivita...
|
||||
var firstRA = ListOnlyRA[0];
|
||||
DateTime currHour = firstRA.Inizio.Date.AddHours(StartHour);
|
||||
DateTime lastHour = firstRA.Inizio.Date.AddHours(EndHour);
|
||||
DateTime domani = firstRA.Inizio.Date.AddDays(1);
|
||||
// ciclo partendo dal primo evento, aggiungendo eventuali periodi in testa / coda intermedi
|
||||
foreach (var item in ListOnlyRA)
|
||||
{
|
||||
// se evento > ora corrente --> aggiungo vuoto...
|
||||
if (item.Inizio > currHour)
|
||||
{
|
||||
RegAttivitaModel? newItem = new RegAttivitaModel()
|
||||
{
|
||||
Inizio = currHour,
|
||||
IdxFase = 0,
|
||||
Descrizione = "-",
|
||||
Fine = item.Inizio,
|
||||
OreTot = (decimal)item.Inizio.Subtract(currHour).TotalHours
|
||||
};
|
||||
result.Add(newItem);
|
||||
}
|
||||
|
||||
// ...altrimenti parto con lui...
|
||||
result.Add(item);
|
||||
currHour = item.Fine;
|
||||
}
|
||||
// se non sono in fondo alla giornata --> aggiungo!
|
||||
if (currHour < domani)
|
||||
{
|
||||
var oreTot = currHour < lastHour ? (decimal)lastHour.Subtract(currHour).TotalHours : 1;
|
||||
var dtFine = currHour < lastHour ? lastHour : currHour.AddHours(1);
|
||||
RegAttivitaModel? newItem = new RegAttivitaModel()
|
||||
{
|
||||
Inizio = currHour,
|
||||
IdxFase = 0,
|
||||
Descrizione = "-",
|
||||
Fine = dtFine,
|
||||
OreTot = oreTot
|
||||
};
|
||||
result.Add(newItem);
|
||||
}
|
||||
}
|
||||
// altrimenti aggiunge comunque un attivita...
|
||||
else
|
||||
{
|
||||
RegAttivitaModel? newItem = new RegAttivitaModel()
|
||||
{
|
||||
Inizio = DtRif.AddHours(StartHour),
|
||||
IdxFase = 0,
|
||||
Descrizione = "-",
|
||||
Fine = DtRif.AddHours(StartHour + 1),
|
||||
OreTot = 1
|
||||
};
|
||||
result.Add(newItem);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
isLoading = true;
|
||||
await Task.Delay(1);
|
||||
isLoading = true;
|
||||
}
|
||||
|
||||
private async Task resetClone()
|
||||
{
|
||||
ClonedRA = null;
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
@if (isLoading)
|
||||
{
|
||||
<LoadingData></LoadingData>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div>
|
||||
@if (modoCal == CalendarModeEnum.modoControllo.showProj || modoCal == CalendarModeEnum.modoControllo.showTempRil)
|
||||
{
|
||||
<DtCard NextIsExtrata="@nextIsEntrata" IsLoading=@isLoading IsRicTimb="isRicTimb" isCompactMode="true"></DtCard>
|
||||
}
|
||||
else
|
||||
{
|
||||
<DtCard NextIsExtrata="@nextIsEntrata" IsLoading=@isLoading IsRicTimb="isRicTimb"></DtCard>
|
||||
<div class="row m-3">
|
||||
<BottoniEntrEsc IsRicTimb="@isRicTimb" IdxDipendente="@IdxDipCurr" ListRecords="@listRecords"></BottoniEntrEsc>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col">
|
||||
<Calendario IdxDipendente="@IdxDipCurr" ListRecords="@listRecords" currMode="@modoCal" ReqReload="ForceReload" ListRecordsRichieste="@listRecordsRichieste" ListFermateAzienda="@listCFF"></Calendario>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Data.DTO;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class CompTimbra : IDisposable
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipCurr { get; set; } = 0;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
MService.EA_TimbUpd -= MService_EA_TimbUpd;
|
||||
MService.EA_DateChanged -= MService_EA_DateChanged;
|
||||
MService.EA_CalModeChanged -= MService_EA_CalModeChanged;
|
||||
listRecords = null;
|
||||
listRecordsRichieste = null;
|
||||
listCFF = null;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
[Inject]
|
||||
protected MessageService MService { get; set; } = null!;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
//private int idxDipCurr { get; set; } = 0;
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
MService.EA_TimbUpd += MService_EA_TimbUpd;
|
||||
MService.EA_DateChanged += MService_EA_DateChanged;
|
||||
MService.EA_CalModeChanged += MService_EA_CalModeChanged;
|
||||
isRicTimb = MService.isRicTimb;
|
||||
nextIsEntrata = MService.NextIsEntrata;
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await Task.Delay(1);
|
||||
if (IdxDipCurr > 0)
|
||||
{
|
||||
await reloadData();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// elenco principale record utente
|
||||
/// </summary>
|
||||
private List<DailyDataDTO>? listRecords = null;
|
||||
|
||||
private List<RegRichiesteModel>? listRecordsRichieste = null;
|
||||
private List<CalFesteFerieModel>? listCFF { get; set; } = null;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
private DateTime dtCurr { get; set; } = DateTime.Today;
|
||||
|
||||
private bool isLoading
|
||||
{
|
||||
get => IdxDipCurr == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indica se si � in modalit� mancata timbratura
|
||||
/// </summary>
|
||||
private bool isRicTimb { get; set; } = false;
|
||||
|
||||
private CalendarModeEnum.modoControllo modoCal { get; set; } = CalendarModeEnum.modoControllo.showCalendar;
|
||||
|
||||
/// <summary>
|
||||
/// Indica quale deve essere prossima timbratura
|
||||
/// </summary>
|
||||
private bool nextIsEntrata { get; set; } = true;
|
||||
|
||||
private DailyDataDTO? todayDTO { get; set; } = null;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary> Richiesta evento reload --> lo passa al componente superiore <param name="doForce"></param>
|
||||
private async Task ForceReload(bool doForce)
|
||||
{
|
||||
if (doForce)
|
||||
{
|
||||
await reloadData();
|
||||
}
|
||||
}
|
||||
|
||||
private async void MService_EA_CalModeChanged()
|
||||
{
|
||||
modoCal = MService.modoCal;
|
||||
//isRicTimb = (modoCal == Calendario.modoControllo.showTimbr);
|
||||
isRicTimb = MService.isRicTimb;
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async void MService_EA_DateChanged()
|
||||
{
|
||||
dtCurr = MService.targetDate;
|
||||
await Task.Delay(1);
|
||||
await reloadData();
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async void MService_EA_TimbUpd()
|
||||
{
|
||||
nextIsEntrata = MService.NextIsEntrata;
|
||||
await Task.Delay(1);
|
||||
await reloadData();
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task reloadData()
|
||||
{
|
||||
if (IdxDipCurr > 0)
|
||||
{
|
||||
var lastMode = modoCal;
|
||||
modoCal = CalendarModeEnum.modoControllo.showCalendar;
|
||||
listRecords = null;
|
||||
await Task.Delay(25);
|
||||
// calcolo mese corrente e recupero dati
|
||||
DateTime MeseStart = new DateTime(dtCurr.Year, dtCurr.Month, 1);
|
||||
DateTime MeseEnd = MeseStart.AddMonths(1);
|
||||
listRecords = await CDService.DailyDetails(IdxDipCurr, MeseStart, MeseEnd);
|
||||
listRecordsRichieste = await CDService.RegRichiesteGetByDip(IdxDipCurr, MeseStart, MeseEnd);
|
||||
listCFF = await CDService.CalFestFeriePeriodo(MeseStart, MeseEnd);
|
||||
// recupero dati che mi servono...
|
||||
await updateNextTimb();
|
||||
await Task.Delay(25);
|
||||
modoCal = lastMode;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task updateNextTimb()
|
||||
{
|
||||
// verifico valore entrata/uscita x timbrature dipendente...
|
||||
DateTime oggi = DateTime.Today.Date;
|
||||
// recupero da dati correnti...
|
||||
if (listRecords != null)
|
||||
{
|
||||
todayDTO = listRecords.FirstOrDefault(x => x.DtRif == oggi);
|
||||
if (todayDTO != null)
|
||||
{
|
||||
nextIsEntrata = todayDTO.ListTimbr.Count % 2 == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
List<TimbratureModel> ListTimbOggi = await CDService.TimbratureDay(oggi, IdxDipCurr);
|
||||
if (ListTimbOggi != null)
|
||||
{
|
||||
nextIsEntrata = ListTimbOggi.Count % 2 == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextIsEntrata = true;
|
||||
}
|
||||
}
|
||||
MService.NextIsEntrata = nextIsEntrata;
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
@if (IsRicTimb)
|
||||
{
|
||||
<div class="cardContainerMancTimb py-2 px-3">
|
||||
<div class="data d-flex justify-content-between">
|
||||
<div class="px-0">
|
||||
<span>@($"{oraRichiesta:dddd}")</span>
|
||||
</div>
|
||||
<div class="px-0">
|
||||
<span>@($"{oraRichiesta:dd MMMM}")</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oraRichiesta">
|
||||
<input @bind="oraRichiesta" type="time" step="300" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (!isCompactMode)
|
||||
{
|
||||
<div class="cardContainer @cardCss py-2 px-3">
|
||||
<div class="data d-flex justify-content-between">
|
||||
<div class="px-0">
|
||||
<span>@($"{dtCurr:dddd}")</span>
|
||||
</div>
|
||||
<div class="px-0">
|
||||
<span>@($"{dtCurr:dd MMMM}")</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ora">
|
||||
<div class="d-flex justify-content-center">
|
||||
<div>
|
||||
@($"{dtCurr:HH}")
|
||||
</div>
|
||||
<div class="@cssSec">:</div>
|
||||
<div>
|
||||
@($"{dtCurr:mm}")
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="cardContainerCompact @cardCss py-1 px-3">
|
||||
<div class="dataComp">
|
||||
<div class="data d-flex justify-content-between">
|
||||
<div class="px-0">
|
||||
<span>@($"{dtCurr:dddd}")</span>
|
||||
</div>
|
||||
<div class="px-0">
|
||||
<span>@($"{dtCurr:dd MMMM}")</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ora">
|
||||
<div class="d-flex justify-content-center">
|
||||
<div>
|
||||
@($"{dtCurr:HH}")
|
||||
</div>
|
||||
<div class="@cssSec">:</div>
|
||||
<div>
|
||||
@($"{dtCurr:mm}")
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class DtCard : IDisposable
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public bool IsLoading { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public bool IsRicTimb { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public bool NextIsExtrata { get; set; } = true;
|
||||
[Parameter]
|
||||
public bool isCompactMode { get; set; } = false;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (uiTimer != null)
|
||||
{
|
||||
uiTimer.Elapsed -= ElapsedTimerUi;
|
||||
uiTimer.Stop();
|
||||
uiTimer.Dispose();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
|
||||
public void ElapsedTimerUi(object? source, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
var pUpd = Task.Run(async () =>
|
||||
{
|
||||
if (!IsRicTimb)
|
||||
{
|
||||
dtCurr = DateTime.Now;
|
||||
cssSec = (dtCurr.Second % 2 == 0) ? "text-light" : "text-light opacity-50";
|
||||
await Task.Delay(1);
|
||||
|
||||
await InvokeAsync(() => StateHasChanged());
|
||||
}
|
||||
});
|
||||
pUpd.Wait();
|
||||
}
|
||||
|
||||
public void StartTimerUI()
|
||||
{
|
||||
// timer che scatta dopo 3sec
|
||||
uiTimer = new System.Timers.Timer(1000);
|
||||
uiTimer.Elapsed += ElapsedTimerUi;
|
||||
uiTimer.Enabled = true;
|
||||
uiTimer.AutoReset = true;
|
||||
uiTimer.Start();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected string cssSec { get; set; } = "text-light";
|
||||
|
||||
[Inject]
|
||||
protected MessageService MService { get; set; } = null!;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
base.OnInitialized();
|
||||
MService.EA_DateChanged += MService_EA_DateChanged;
|
||||
if (!IsRicTimb)
|
||||
{
|
||||
StartTimerUI();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private DateTime dtCurr = DateTime.Now;
|
||||
|
||||
private System.Timers.Timer uiTimer = null!;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
/// <summary>
|
||||
/// Card da collegare al tipo di stato/colore
|
||||
/// </summary>
|
||||
private string cardCss
|
||||
{
|
||||
get
|
||||
{
|
||||
string answ = "defaultCard";
|
||||
if (!IsLoading)
|
||||
{
|
||||
answ = NextIsExtrata ? "cardEntrata" : "cardUscita";
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime oraRichiesta
|
||||
{
|
||||
get => MService.targetDateMancTimb;
|
||||
set => MService.targetDateMancTimb = new DateTime(MService.targetDateMancTimb.Year, MService.targetDateMancTimb.Month, MService.targetDateMancTimb.Day, value.Hour, value.Minute, 0);
|
||||
}
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async void MService_EA_DateChanged()
|
||||
{
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
.cardContainerAll {
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
}
|
||||
.cardContainer {
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
min-height: 8.5rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
.cardContainerCompact {
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
min-height: 4rem;
|
||||
}
|
||||
.cardContainerCompact .dataComp {
|
||||
font-weight: bold;
|
||||
/*padding-top: 1.5rem;*/
|
||||
/*padding-left: 1.5rem;*/
|
||||
position: relative;
|
||||
text-transform: uppercase;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.cardContainerCompact .ora {
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
font-size: 3.5rem;
|
||||
text-align: center;
|
||||
font-size: 2.8rem;
|
||||
}
|
||||
.cardContainerMancTimb {
|
||||
padding-bottom: 1rem;
|
||||
min-height: 8.5rem;
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
background-color: #2980B9;
|
||||
}
|
||||
.titolo {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.data {
|
||||
font-weight: bold;
|
||||
/*padding-top: 1.5rem;*/
|
||||
/*padding-left: 1.5rem;*/
|
||||
position: relative;
|
||||
font-size: 1.25rem;
|
||||
text-transform: uppercase;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.ora {
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
font-size: 3.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
.oraRichiesta {
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
.oraRichiesta input {
|
||||
border-radius: 0.5rem;
|
||||
border: none;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
.defaultCard {
|
||||
background-color: rgba(0, 128, 255, 0.8);
|
||||
}
|
||||
.cardEntrata {
|
||||
background-color: #9B59B6;
|
||||
}
|
||||
.cardUscita {
|
||||
background-color: #4CAF50;
|
||||
}
|
||||
.txtSec {
|
||||
font-weight: normal;
|
||||
font-size: 2rem;
|
||||
}
|
||||
.pulse-warning {
|
||||
/*color: rgba(255, 255, 255, 0.8);*/
|
||||
box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0.8);
|
||||
animation: pulse-warning 2.5s infinite;
|
||||
}
|
||||
@-webkit-keyframes pulse-warning {
|
||||
0% {
|
||||
-webkit-box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0.8);
|
||||
color: #ffffff;
|
||||
}
|
||||
70% {
|
||||
-webkit-box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0);
|
||||
}
|
||||
100% {
|
||||
-webkit-box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0.8);
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
@keyframes pulse-warning {
|
||||
0% {
|
||||
-moz-box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0.8);
|
||||
box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0.8);
|
||||
color: #ffffff;
|
||||
}
|
||||
70% {
|
||||
-moz-box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0);
|
||||
box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0);
|
||||
}
|
||||
100% {
|
||||
-moz-box-shadow: 0px 0px 20px 5px rgba(255, 170, 0, 0.8);
|
||||
box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0.8);
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
|
||||
.cardContainerAll {
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
box-shadow: 0px 3px 4px rgba(0,0,0,0.7);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.cardContainer {
|
||||
.cardContainerAll;
|
||||
min-height: 8.5rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.cardContainerCompact {
|
||||
.cardContainerAll;
|
||||
min-height: 4rem;
|
||||
}
|
||||
|
||||
.cardContainerCompact .dataComp {
|
||||
font-weight: bold;
|
||||
/*padding-top: 1.5rem;*/
|
||||
/*padding-left: 1.5rem;*/
|
||||
position: relative;
|
||||
text-transform: uppercase;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.cardContainerCompact .ora {
|
||||
.ora;
|
||||
font-size: 2.8rem;
|
||||
}
|
||||
|
||||
.cardContainerMancTimb {
|
||||
padding-bottom: 1rem;
|
||||
min-height: 8.5rem;
|
||||
border-radius: 12px;
|
||||
width: 100%;
|
||||
box-shadow: 0px 3px 4px rgba(0,0,0,0.7);
|
||||
color: #fff;
|
||||
background-color: #2980B9;
|
||||
}
|
||||
|
||||
.titolo {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.data {
|
||||
font-weight: bold;
|
||||
/*padding-top: 1.5rem;*/
|
||||
/*padding-left: 1.5rem;*/
|
||||
position: relative;
|
||||
font-size: 1.25rem;
|
||||
text-transform: uppercase;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.ora {
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
font-size: 3.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.oraRichiesta {
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.oraRichiesta input {
|
||||
border-radius: 0.5rem;
|
||||
border: none;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
.defaultCard {
|
||||
background-color: rgba(0, 128, 255, .8);
|
||||
}
|
||||
|
||||
.cardEntrata {
|
||||
background-color: #9B59B6;
|
||||
}
|
||||
|
||||
.cardUscita {
|
||||
background-color: #4CAF50;
|
||||
}
|
||||
|
||||
.txtSec {
|
||||
font-weight: normal;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.pulse-warning {
|
||||
/*color: rgba(255, 255, 255, 0.8);*/
|
||||
box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, .8);
|
||||
animation: pulse-warning 2.5s infinite;
|
||||
}
|
||||
|
||||
|
||||
@-webkit-keyframes pulse-warning {
|
||||
0% {
|
||||
-webkit-box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, .8);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
70% {
|
||||
-webkit-box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, .8);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse-warning {
|
||||
0% {
|
||||
-moz-box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, .8);
|
||||
box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, .8);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
70% {
|
||||
-moz-box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0);
|
||||
box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
-moz-box-shadow: 0px 0px 20px 5px rgba(255, 170, 0, .8);
|
||||
box-shadow: 0px 0px 20px 5px rgba(0, 128, 255, .8);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.cardContainerAll{border-radius:12px;width:100%;box-shadow:0 3px 4px rgba(0,0,0,.7);color:#fff;}.cardContainer{border-radius:12px;width:100%;box-shadow:0 3px 4px rgba(0,0,0,.7);color:#fff;min-height:8.5rem;padding-bottom:1rem;}.cardContainerCompact{border-radius:12px;width:100%;box-shadow:0 3px 4px rgba(0,0,0,.7);color:#fff;min-height:4rem;}.cardContainerCompact .dataComp{font-weight:bold;position:relative;text-transform:uppercase;font-size:1rem;}.cardContainerCompact .ora{width:100%;font-weight:bold;position:relative;font-size:3.5rem;text-align:center;font-size:2.8rem;}.cardContainerMancTimb{padding-bottom:1rem;min-height:8.5rem;border-radius:12px;width:100%;box-shadow:0 3px 4px rgba(0,0,0,.7);color:#fff;background-color:#2980b9;}.titolo{font-size:1rem;}.data{font-weight:bold;position:relative;font-size:1.25rem;text-transform:uppercase;display:flex;justify-content:space-between;}.ora{width:100%;font-weight:bold;position:relative;font-size:3.5rem;text-align:center;}.oraRichiesta{width:100%;font-weight:bold;position:relative;font-size:2rem;text-align:center;padding-top:1.5rem;}.oraRichiesta input{border-radius:.5rem;border:0;padding:.25rem;}.defaultCard{background-color:rgba(0,128,255,.8);}.cardEntrata{background-color:#9b59b6;}.cardUscita{background-color:#4caf50;}.txtSec{font-weight:normal;font-size:2rem;}.pulse-warning{box-shadow:0 0 20px 5px rgba(0,128,255,.8);animation:pulse-warning 2.5s infinite;}@-webkit-keyframes pulse-warning{0%{-webkit-box-shadow:0 0 20px 5px rgba(0,128,255,.8);color:#fff;}70%{-webkit-box-shadow:0 0 20px 5px rgba(0,128,255,0);}100%{-webkit-box-shadow:0 0 20px 5px rgba(0,128,255,.8);color:#fff;}}@keyframes pulse-warning{0%{-moz-box-shadow:0 0 20px 5px rgba(0,128,255,.8);box-shadow:0 0 20px 5px rgba(0,128,255,.8);color:#fff;}70%{-moz-box-shadow:0 0 20px 5px rgba(0,128,255,0);box-shadow:0 0 20px 5px rgba(0,128,255,0);}100%{-moz-box-shadow:0 0 20px 5px rgba(255,170,0,.8);box-shadow:0 0 20px 5px rgba(0,128,255,.8);color:#fff;}}
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
Prego selezionare dipendente:
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="input-group input-group-sm">
|
||||
<select @bind="@idxDipendente" class="form-select">
|
||||
<option value="0">--- Selezionare Nominativo ---</option>
|
||||
@foreach (var dip in ListaDip)
|
||||
{
|
||||
<option value="@dip.IdxDipendente">@dip.Cognome @dip.Nome</option>
|
||||
}
|
||||
</select>
|
||||
@if (idxDipendente == 0)
|
||||
{
|
||||
<button class="btn btn-secondary" disabled>Invia</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btn btn-success" @onclick="() => doSendEmailAccessLink()">Invia</button>
|
||||
}
|
||||
</div>
|
||||
@if (userIsAdmin)
|
||||
{
|
||||
<Toggler SelFilter="@ToggleData" FilterChanged="evToggled"></Toggler>
|
||||
}
|
||||
</div>
|
||||
@if (isSendingData)
|
||||
{
|
||||
<dialog class="modal fade show" tabindex="-1" style="display:block; background-color: rgba(10,10,10,.6);" aria-modal="true" role="dialog">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="col-12 text-center my-0 py-3 alert alert-primary">
|
||||
<div class="d-flex justify-content-between py-4">
|
||||
@*<div><i class="fas fa-paper-plane fa-2x"></i></div>*@
|
||||
<div class="text-start">
|
||||
<h1>Invio <i class="fas fa-paper-plane"></i></h1>
|
||||
<i>...un attimo di pazienza...</i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="spinner-grow" role="status" style="width: 3rem; height: 3rem;">
|
||||
<span class="visually-hidden">Sending...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
using EgwCoreLib.Razor;
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System.Text;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class InvioLinkEmail
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipAdmin { get; set; } = 0;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
initToggler();
|
||||
await initConf();
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// verifico se user sia admin...
|
||||
var listaRuoli = await CDService.Dip2RuoliGetAll();
|
||||
var rigaDip = listaRuoli.FirstOrDefault(x => x.IdxDipendente == IdxDipAdmin && x.CodRuolo == "ResetAuthKey");
|
||||
userIsAdmin = rigaDip != null;
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private bool isSendingData = false;
|
||||
|
||||
private List<DipendentiModel> ListaDip = new List<DipendentiModel>();
|
||||
|
||||
private bool userIsAdmin = false;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IConfiguration Configuration { get; set; } = null!;
|
||||
|
||||
private bool forceResetAuthKey { get; set; } = false;
|
||||
|
||||
private int idxDipendente { get; set; } = 0;
|
||||
|
||||
[Inject]
|
||||
private MessageService MService { get; set; } = null!;
|
||||
|
||||
private Toggler.SelectGlobalToggle ToggleData { get; set; } = new Toggler.SelectGlobalToggle();
|
||||
|
||||
private string UrlLinkExt { get; set; } = "";
|
||||
|
||||
private string UrlLinkInt { get; set; } = "";
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Effettua vero reset authKey dipendente...
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task doResetAuthKey()
|
||||
{
|
||||
var rigaDip = ListaDip.FirstOrDefault(x => x.IdxDipendente == idxDipendente);
|
||||
if (rigaDip != null)
|
||||
{
|
||||
Guid newGuid = Guid.NewGuid();
|
||||
string newAuthKey = $"{newGuid}";
|
||||
rigaDip.AuthKey = newAuthKey.ToUpper();
|
||||
// update!
|
||||
await CDService.DipendentiUpdate(rigaDip);
|
||||
}
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invia email con link x accesso
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task doSendEmailAccessLink()
|
||||
{
|
||||
isSendingData = true;
|
||||
await Task.Delay(1);
|
||||
// recupero record dip da selezioanta...
|
||||
if (idxDipendente > 0)
|
||||
{
|
||||
string authKey = "";
|
||||
string destinatario = "";
|
||||
// se riechiesto reset --> ricalcolo authKey x dip
|
||||
if (forceResetAuthKey)
|
||||
{
|
||||
await doResetAuthKey();
|
||||
await ReloadData();
|
||||
}
|
||||
var rigaDip = ListaDip.FirstOrDefault(x => x.IdxDipendente == idxDipendente);
|
||||
if (rigaDip != null)
|
||||
{
|
||||
authKey = System.Web.HttpUtility.UrlEncode(rigaDip.AuthKey);
|
||||
destinatario = rigaDip.Email;
|
||||
|
||||
// compongo testo messaggio
|
||||
string oggetto = "Link autorizzazione device per GPW Core.Smart";
|
||||
string userUrl = $"{UrlLinkInt}jumper?idxDipendente={idxDipendente}&authKey={authKey}";
|
||||
string userWebUrl = $"{UrlLinkExt}jumper?idxDipendente={idxDipendente}&authKey={authKey}";
|
||||
// corpo
|
||||
StringBuilder sbBody = new StringBuilder();
|
||||
sbBody.AppendLine("Hai ricevuto questa email su richiesta tua o dell'Admin per poter procedere a registrare un (nuovo) devices con GPW");
|
||||
sbBody.AppendLine("");
|
||||
sbBody.AppendLine("<hr/>");
|
||||
sbBody.AppendLine("<b>Rete Interna</b>");
|
||||
sbBody.AppendLine($"Per proseguire clicca sul <a href=\"{userUrl}\" target=\"_blank\">link seguente</a> (rete interna)");
|
||||
//sbBody.AppendLine(userUrl);
|
||||
sbBody.AppendLine("<hr/>");
|
||||
sbBody.AppendLine("<b>Internet</b>");
|
||||
sbBody.AppendLine($"Oppure sul <a href=\"{userWebUrl}\" target=\"_blank\">link seguente</a> (internet)");
|
||||
//sbBody.AppendLine(userWebUrl);
|
||||
sbBody.AppendLine("<hr/>");
|
||||
sbBody.AppendLine("");
|
||||
sbBody.AppendLine("");
|
||||
sbBody.AppendLine("Team GPW EgalWare");
|
||||
string msgBody = sbBody.ToString().Replace($"{Environment.NewLine}", "<br/>");
|
||||
// invio
|
||||
bool fatto = await CDService.sendEmailAsync(destinatario, oggetto, msgBody);
|
||||
}
|
||||
}
|
||||
isSendingData = false;
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
private async Task evToggled(Toggler.SelectGlobalToggle newTogData)
|
||||
{
|
||||
ToggleData = newTogData;
|
||||
forceResetAuthKey = !ToggleData.isActive;
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// init valori da config
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task initConf()
|
||||
{
|
||||
UrlLinkInt = Configuration.GetValue<string>("OptPar:UrlLinkInt") ?? "http://missing.internal.link";
|
||||
UrlLinkExt = Configuration.GetValue<string>("OptPar:UrlLinkExt") ?? "http://missing.external.link";
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
private void initToggler()
|
||||
{
|
||||
ToggleData = new Toggler.SelectGlobalToggle()
|
||||
{
|
||||
leftString = "Reset + Invio",
|
||||
rightString = "Solo Invio",
|
||||
placardCss = "bg-light border-light text-dark",
|
||||
};
|
||||
}
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
var rawList = await CDService.DipendentiGetAll();
|
||||
ListaDip = rawList
|
||||
.Where(x => x.Attivo == true)
|
||||
.OrderBy(x => x.Cognome)
|
||||
.ThenBy(x => x.Nome)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
@if (!isVisible)
|
||||
{
|
||||
<div class="tagOnly">
|
||||
<div class="d-flex justify-content-around">
|
||||
<button class="btn-show" @onclick="()=>showMenu()">
|
||||
<div class="d-flex justify-content-around">
|
||||
<div class="px-2"><i class="fa-solid fa-bars"></i></div>
|
||||
<div class="px-2">Accesso Rapido</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="menuArea w-100">
|
||||
<div class="d-flex justify-content-around">
|
||||
<button class="btn-hide" @onclick="()=>hideMenu()">
|
||||
<div class="d-flex justify-content-around">
|
||||
<div class="px-2"><i class="fa-solid fa-bars"></i></div>
|
||||
<div class="px-2">Accesso Rapido</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div class="choiceArea text-center pt-2">
|
||||
<div class="rapid-buttons">
|
||||
<div class="btn-group w-100">
|
||||
<button type="button" class="btn btn-light" @onclick="()=>goToIndex()">
|
||||
<i class="fa-solid fa-person-booth fa-2x"></i>
|
||||
<div>Timbra</div>
|
||||
</button>
|
||||
<button type="button" class="btn btn-light mx-1" @onclick="()=>goToAdmin()">
|
||||
<i class="fa-solid fa-user fa-2x"></i>
|
||||
<div>Admin</div>
|
||||
</button>
|
||||
<button type="button" class="btn btn-light" @onclick="()=>goToMPF()">
|
||||
<i class="fa-solid fa-ticket fa-2x"></i>
|
||||
<div>MFP</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex justify-content-between px-3 text-light my-2">
|
||||
<div>
|
||||
GPW <span class="small">v.@version</span>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<a type="button" class="text-light" @onclick="() => goToAbout()">
|
||||
<img class="img-fluid" width="16" src="images/LogoEgw.png" /> Egalware
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class NavBottom
|
||||
{
|
||||
#region Protected Fields
|
||||
|
||||
protected string hideButtonCSS = "visible";
|
||||
protected string hideMenuCSS = "hidden";
|
||||
protected bool isVisible = false;
|
||||
protected Version? version = typeof(Program).Assembly.GetName().Version;
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected string baseUrl
|
||||
{
|
||||
get => configuration.GetValue<string>("OptConf:BaseUrl") ?? "~/";
|
||||
}
|
||||
|
||||
[Inject]
|
||||
protected IConfiguration configuration { get; set; } = null!;
|
||||
|
||||
protected bool isActive { get; set; } = false;
|
||||
|
||||
[Inject]
|
||||
protected MessageService MService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected NavigationManager NavManager { get; set; } = null!;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected void goTo(string target)
|
||||
{
|
||||
NavManager.NavigateTo($"{baseUrl}{target}", true);
|
||||
}
|
||||
|
||||
protected void goToAbout()
|
||||
{
|
||||
goTo("About");
|
||||
}
|
||||
|
||||
protected void goToAdmin()
|
||||
{
|
||||
goTo("Admin");
|
||||
}
|
||||
|
||||
protected void goToIndex()
|
||||
{
|
||||
goTo("Index");
|
||||
}
|
||||
|
||||
protected void goToMPF()
|
||||
{
|
||||
goTo("DayOff");
|
||||
}
|
||||
|
||||
protected void hideMenu()
|
||||
{
|
||||
isVisible = false;
|
||||
hideMenuCSS = "hidden; height: 0%;";
|
||||
hideButtonCSS = "visible";
|
||||
}
|
||||
|
||||
protected void showMenu()
|
||||
{
|
||||
if (MService.IdxDipendente > 0)
|
||||
{
|
||||
isVisible = true;
|
||||
hideMenuCSS = "visible; height: 22%; display:block;";
|
||||
hideButtonCSS = "hidden";
|
||||
}
|
||||
}
|
||||
|
||||
protected void slideIn()
|
||||
{
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
protected void slideOut()
|
||||
{
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
.btn-toggle {
|
||||
--y: 0;
|
||||
--z: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(to top, #3f4c6b, #606c88);
|
||||
padding: 0.75rem 3rem;
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
border: none;
|
||||
box-shadow: 0 2px 40px -10px #000;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
left: 50%;
|
||||
transform: translate3d(var(--x), var(--y), var(--z));
|
||||
cursor: pointer;
|
||||
z-index: 105;
|
||||
}
|
||||
.btn-show {
|
||||
--x: -50%;
|
||||
--y: 0;
|
||||
--z: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(to top, #3f4c6b, #606c88);
|
||||
padding: 0.75rem 3rem;
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
border: none;
|
||||
box-shadow: 0 2px 40px -10px #000;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
left: 50%;
|
||||
transform: translate3d(var(--x), var(--y), var(--z));
|
||||
cursor: pointer;
|
||||
z-index: 105;
|
||||
position: fixed;
|
||||
bottom: 0rem;
|
||||
}
|
||||
.btn-hide {
|
||||
--y: 0;
|
||||
--z: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(to top, #3f4c6b, #606c88);
|
||||
padding: 0.75rem 3rem;
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
border: none;
|
||||
box-shadow: 0 2px 40px -10px #000;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
left: 50%;
|
||||
transform: translate3d(var(--x), var(--y), var(--z));
|
||||
cursor: pointer;
|
||||
z-index: 105;
|
||||
}
|
||||
.menuArea {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
animation-name: menuShow;
|
||||
animation-duration: 0.8s;
|
||||
}
|
||||
.tagOnly {
|
||||
animation-name: menuHide;
|
||||
animation-duration: 0.2s;
|
||||
}
|
||||
.choiceArea {
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
padding: 0.2rem 0.4rem 0.2rem 0.4rem;
|
||||
box-shadow: 0px -9px 50px -30px black;
|
||||
transition: height 200ms ease-in-out 400ms, opacity 700ms;
|
||||
background: linear-gradient(to bottom, #606c88, #3f4c6b);
|
||||
}
|
||||
.rapid-buttons {
|
||||
display: flex;
|
||||
padding: 0.4rem 0.2rem;
|
||||
justify-content: center;
|
||||
}
|
||||
.rapid-buttons a {
|
||||
flex: 1 1 30%;
|
||||
padding: 1.5rem;
|
||||
margin: 5px;
|
||||
border-radius: 8px;
|
||||
background: #d1d1d1;
|
||||
color: var(--color);
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
@keyframes menuShow {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
transform: translateY(8rem);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes menuHide {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
/*transform: rotateY(180deg);*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
.btn-toggle {
|
||||
--y: 0;
|
||||
--z: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(to top, #3f4c6b, #606c88);
|
||||
padding: .75rem 3rem;
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
border: none;
|
||||
box-shadow: 0 2px 40px -10px #000;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
left: 50%;
|
||||
transform: translate3d(var(--x), var(--y), var(--z));
|
||||
cursor: pointer;
|
||||
z-index: 105;
|
||||
}
|
||||
|
||||
.btn-show {
|
||||
--x: -50%;
|
||||
.btn-toggle;
|
||||
position: fixed;
|
||||
bottom: 0rem;
|
||||
}
|
||||
|
||||
.btn-hide {
|
||||
.btn-toggle;
|
||||
}
|
||||
|
||||
.menuArea {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
animation-name: menuShow;
|
||||
animation-duration: 0.8s;
|
||||
}
|
||||
|
||||
.tagOnly {
|
||||
animation-name: menuHide;
|
||||
animation-duration: 0.2s;
|
||||
}
|
||||
|
||||
.choiceArea {
|
||||
border-radius: 1.5rem 1.5rem 0 0;
|
||||
padding: 0.2rem .4rem 0.2rem .4rem;
|
||||
box-shadow: 0px -9px 50px -30px black;
|
||||
transition: height 200ms ease-in-out 400ms, opacity 700ms;
|
||||
background: linear-gradient(to bottom, #606c88, #3f4c6b);
|
||||
}
|
||||
|
||||
.rapid-buttons {
|
||||
display: flex;
|
||||
padding: 0.4rem 0.2rem;
|
||||
justify-content: center;
|
||||
|
||||
a {
|
||||
flex: 1 1 30%;
|
||||
padding: 1.5rem;
|
||||
margin: 5px;
|
||||
border-radius: 8px;
|
||||
background: #d1d1d1;
|
||||
color: var(--color);
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes menuShow {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
transform: translateY(8rem);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes menuHide {
|
||||
0% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
/*transform: rotateY(180deg);*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.btn-toggle{--y:0;--z:0;display:flex;align-items:center;justify-content:space-between;background:linear-gradient(to top,#3f4c6b,#606c88);padding:.75rem 3rem;border-radius:1.5rem 1.5rem 0 0;border:0;box-shadow:0 2px 40px -10px #000;color:#fff;white-space:nowrap;left:50%;transform:translate3d(var(--x),var(--y),var(--z));cursor:pointer;z-index:105;}.btn-show{--x:-50%;--y:0;--z:0;display:flex;align-items:center;justify-content:space-between;background:linear-gradient(to top,#3f4c6b,#606c88);padding:.75rem 3rem;border-radius:1.5rem 1.5rem 0 0;border:0;box-shadow:0 2px 40px -10px #000;color:#fff;white-space:nowrap;left:50%;transform:translate3d(var(--x),var(--y),var(--z));cursor:pointer;z-index:105;position:fixed;bottom:0;}.btn-hide{--y:0;--z:0;display:flex;align-items:center;justify-content:space-between;background:linear-gradient(to top,#3f4c6b,#606c88);padding:.75rem 3rem;border-radius:1.5rem 1.5rem 0 0;border:0;box-shadow:0 2px 40px -10px #000;color:#fff;white-space:nowrap;left:50%;transform:translate3d(var(--x),var(--y),var(--z));cursor:pointer;z-index:105;}.menuArea{position:fixed;bottom:0;animation-name:menuShow;animation-duration:.8s;}.tagOnly{animation-name:menuHide;animation-duration:.2s;}.choiceArea{border-radius:1.5rem 1.5rem 0 0;padding:.2rem .4rem .2rem .4rem;box-shadow:0 -9px 50px -30px black;transition:height 200ms ease-in-out 400ms,opacity 700ms;background:linear-gradient(to bottom,#606c88,#3f4c6b);}.rapid-buttons{display:flex;padding:.4rem .2rem;justify-content:center;}.rapid-buttons a{flex:1 1 30%;padding:1.5rem;margin:5px;border-radius:8px;background:#d1d1d1;color:var(--color);text-align:center;font-size:14px;font-weight:bold;text-decoration:none;}@keyframes menuShow{0%{opacity:.6;transform:translateY(8rem);}100%{opacity:1;transform:translateY(0);}}@keyframes menuHide{0%{opacity:.6;}100%{opacity:1;}}
|
||||
@@ -0,0 +1,33 @@
|
||||
@using GPW.CORE.Data.DbModels
|
||||
|
||||
@if (ListRA != null)
|
||||
{
|
||||
@*int lastIdxRa = 0;*@
|
||||
@foreach (var item in ListRA)
|
||||
{
|
||||
@*lastIdxRa = item.IdxRa;*@
|
||||
if (item.IdxRa == 0)
|
||||
{
|
||||
@if (hasClonedRa)
|
||||
{
|
||||
<button class="btn btn-warning btn-sm w-75 mt-2" @onclick="() => DoPaste(item)">
|
||||
<i class="fa-regular fa-clipboard"></i>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btn btn-success btn-sm w-50 mt-2" @onclick="() => DoSel(item)">
|
||||
<i class="fa-solid fa-calendar-plus"></i>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="pt-2 px-0">
|
||||
<SingleRaDisplay EnableAction="true" CurrItem="@item" ItemCloned="DoClone" ItemSelected="DoSel"></SingleRaDisplay>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class ProjectsList : IDisposable
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<RegAttivitaModel> ActionPaste { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool EnableAdmin { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public bool EnableEdit { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<RegAttivitaModel> ItemCloned { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<RegAttivitaModel> ItemDeleted { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<RegAttivitaModel> ItemSelected { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public List<RegAttivitaModel>? ListRA { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> ReqUpdate { get; set; }
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ListRA = null;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected bool hasClonedRa
|
||||
{
|
||||
get => MService.clonedRA != null;
|
||||
}
|
||||
|
||||
[Inject]
|
||||
protected MessageService MService { get; set; } = null!;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gestione evento cloning
|
||||
/// </summary>
|
||||
/// <param name="currRecord"></param>
|
||||
protected async void DoClone(RegAttivitaModel currRecord)
|
||||
{
|
||||
if (currRecord != null)
|
||||
{
|
||||
await ItemCloned.InvokeAsync(currRecord);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gestione evento incolla
|
||||
/// </summary>
|
||||
/// <param name="currRecord"></param>
|
||||
protected async void DoPaste(RegAttivitaModel currRecord)
|
||||
{
|
||||
if (currRecord != null)
|
||||
{
|
||||
await ActionPaste.InvokeAsync(currRecord);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gestione evento selezione x editing
|
||||
/// </summary>
|
||||
protected async void DoSel(RegAttivitaModel currRecord)
|
||||
{
|
||||
if (currRecord != null)
|
||||
{
|
||||
await ItemSelected.InvokeAsync(currRecord);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<EditForm Model="@CurrRecord">
|
||||
<div class="card">
|
||||
<div class="card-header bg-info">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h4>
|
||||
Modifica #
|
||||
@if (CurrRecord != null)
|
||||
{
|
||||
<b>@CurrRecord.IdxRa</b>
|
||||
}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="col-12 text-end">
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">
|
||||
<i class="far fa-object-group"></i>
|
||||
</span>
|
||||
<InputSelect @bind-Value="@gruppoSel" class="form-select" title="Gruppo">
|
||||
@foreach (var item in gruppiList)
|
||||
{
|
||||
<option value="@item.Gruppo">@item.DescrGruppo</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<DataAnnotationsValidator />
|
||||
<ValidationSummary />
|
||||
<div class="row">
|
||||
<div class="col-12 col-xl-6 text-end">
|
||||
<label class="small">Progetto</label>
|
||||
<div class="input-group input-group-sm" title="Progetto">
|
||||
<span class="input-group-text" style="width:3em;">
|
||||
<i class="fas fa-project-diagram"></i>
|
||||
</span>
|
||||
<InputSelect @bind-Value="@idxProj" class="form-select bg-light text-start" title="Progetto">
|
||||
@foreach (var item in projList)
|
||||
{
|
||||
<option value="@item.IdxProgetto">@item.ClienteNav.RagSociale | @item.NomeProj</option>
|
||||
}
|
||||
</InputSelect>
|
||||
</div>
|
||||
<label class="small">Fase</label>
|
||||
<div class="input-group input-group-sm" title="Fase">
|
||||
<span class="input-group-text" style="width:3em;">
|
||||
<i class="fas fa-tasks"></i>
|
||||
</span>
|
||||
<InputSelect @bind-Value="@idxFase" class="form-select bg-light text-start" title="Fase">
|
||||
@foreach (var item in fasiList)
|
||||
{
|
||||
if (@item.EnableTime)
|
||||
{
|
||||
if (item.Attivo)
|
||||
{
|
||||
<option value="@item.IdxFase">@item.NomeFase</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@item.IdxFase" disabled="@VetoInsert" title="Fase Disattivata, non selezionabile">@item.NomeFase / DISATTIVA</option>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.Attivo)
|
||||
{
|
||||
<option value="@item.IdxFase" disabled class="bg-dark text-warning">[@item.NomeFase]</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@item.IdxFase" disabled class="bg-dark text-warning" title="Fase Disattivata, non selezionabile">[@item.NomeFase / DISATTIVA]</option>
|
||||
}
|
||||
}
|
||||
}
|
||||
</InputSelect>
|
||||
</div>
|
||||
</div>
|
||||
@if (CurrRecord != null)
|
||||
{
|
||||
<div class="col-12 col-xl-6 text-end">
|
||||
<label class="small">Periodo (arr 5 min)</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-play fa-xs"></i>
|
||||
</span>
|
||||
<input type="datetime-local" class="form-control text-end" @bind="@CurrRecord.Inizio"></input>
|
||||
<span class="input-group-text" style="width: 4em;">
|
||||
inizio
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-stop fa-xs"></i>
|
||||
</span>
|
||||
<input type="datetime-local" class="form-control text-end" @bind="@CurrRecord.Fine"></input>
|
||||
<span class="input-group-text" style="width: 4em;">
|
||||
fine
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 text-start">
|
||||
<label class="small">Descrizione</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-text">
|
||||
<i class="far fa-comment-alt"></i>
|
||||
</span>
|
||||
<InputTextArea id="idxRa" @bind-Value="@CurrRecord.Descrizione" class="form-control" title="Descrizione attività"></InputTextArea>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
@if (AppMServ.PayloadOk)
|
||||
{
|
||||
<button type="button" class="btn w-100 btn-success" @onclick="DoUpdate"><i class="fas fa-check-circle"></i> Save</button>
|
||||
}
|
||||
</div>
|
||||
<div class="col px-0 mx-0">
|
||||
<button type="button" class="btn w-100 btn-warning" @onclick="DoReset"><i class="fas fa-ban"></i> Cancel</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
@if (AppMServ.PayloadOk)
|
||||
{
|
||||
<button type="button" class="btn w-100 btn-danger" @onclick="DoDelete"><i class="fa-solid fa-trash"></i> Delete</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</EditForm>
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using NLog;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class RegAttEditor
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public RegAttivitaModel? CurrRecord
|
||||
{
|
||||
get => currRecord;
|
||||
set => currRecord = value;
|
||||
}
|
||||
|
||||
public string gruppoSel
|
||||
{
|
||||
get
|
||||
{
|
||||
return _gruppoSel;
|
||||
}
|
||||
set
|
||||
{
|
||||
_gruppoSel = value;
|
||||
if (!vetoUpd)
|
||||
{
|
||||
var pUpd = Task.Run(async () => await ReloadData(true, true));
|
||||
pUpd.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int idxProj
|
||||
{
|
||||
get
|
||||
{
|
||||
return _idxProj;
|
||||
}
|
||||
set
|
||||
{
|
||||
_idxProj = value;
|
||||
if (!vetoUpd)
|
||||
{
|
||||
var pUpd = Task.Run(async () => await ReloadData(false, true));
|
||||
pUpd.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> ItemReset { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> ItemUpdated { get; set; }
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
protected string _gruppoSel = "";
|
||||
|
||||
protected int _idxProj = 0;
|
||||
|
||||
protected bool vetoUpd = false;
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
[Inject]
|
||||
protected CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper salvataggio in MServ
|
||||
/// </summary>
|
||||
protected RegAttivitaModel? currRecord
|
||||
{
|
||||
get => AppMServ.recordRA;
|
||||
set => AppMServ.recordRA = value;
|
||||
}
|
||||
|
||||
protected List<AnagFasiModel> fasiList { get; set; } = new List<AnagFasiModel>();
|
||||
|
||||
protected List<AnagGruppiModel> gruppiList { get; set; } = new List<AnagGruppiModel>();
|
||||
|
||||
protected int idxFase
|
||||
{
|
||||
get
|
||||
{
|
||||
int answ = 0;
|
||||
if (CurrRecord != null)
|
||||
{
|
||||
answ = CurrRecord.IdxFase;
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (CurrRecord != null)
|
||||
{
|
||||
CurrRecord.IdxFase = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected List<AnagProgettiModel> projList { get; set; } = new List<AnagProgettiModel>();
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Salvo in MService record clonato
|
||||
/// </summary>
|
||||
protected async void DoClone()
|
||||
{
|
||||
if (CurrRecord != null)
|
||||
{
|
||||
var newData = CurrRecord.Clone();
|
||||
newData.IdxRa = 0;
|
||||
AppMServ.clonedRA = newData;
|
||||
await ItemReset.InvokeAsync(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Elimino record
|
||||
/// </summary>
|
||||
protected async void DoDelete()
|
||||
{
|
||||
// chiedo verifica
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", "Sicuro di voler eliminare il record selezionato??"))
|
||||
return;
|
||||
|
||||
if (CurrRecord != null)
|
||||
{
|
||||
// aggiorno
|
||||
await CDService.RegAttDelete(CurrRecord);
|
||||
// registro fatto
|
||||
await ItemUpdated.InvokeAsync(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indico item selezionato
|
||||
/// </summary>
|
||||
protected async void DoReset()
|
||||
{
|
||||
await ItemReset.InvokeAsync(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggiorno e riporto update
|
||||
/// </summary>
|
||||
protected async void DoUpdate()
|
||||
{
|
||||
arrotondaMinuti();
|
||||
// aggiorno
|
||||
if (CurrRecord != null)
|
||||
{
|
||||
// controllo IdxDipendente
|
||||
if (CurrRecord.IdxDipendente == 0)
|
||||
{
|
||||
CurrRecord.IdxDipendente = AppMServ.IdxDipendente;
|
||||
}
|
||||
|
||||
// prearo lista inserimento
|
||||
List<RegAttivitaModel> data2ins = new List<RegAttivitaModel>();
|
||||
|
||||
// se inizio/fine stesso giorno processo
|
||||
if (CurrRecord.Fine.Date.Equals(CurrRecord.Inizio.Date))
|
||||
{
|
||||
data2ins.Add(CurrRecord);
|
||||
}
|
||||
// in caso di eventuale supero della mezzanotte --> chiedo se si vuole split su 2 attivit�...
|
||||
else
|
||||
{
|
||||
bool doSplit = await JSRuntime.InvokeAsync<bool>("confirm", "L'attivit� si protrae su pi� giorni, vuoi suddividere in record per ogni giornata?");
|
||||
// chiedo verifica x splittare in N+1 periodi (N = giorni)
|
||||
if (doSplit)
|
||||
{
|
||||
RegAttivitaModel currRA = CurrRecord.Clone() as RegAttivitaModel;
|
||||
// primo record � inizio --> mezzanote
|
||||
DateTime lastInizio = currRA.Inizio;
|
||||
DateTime lastFine = currRA.Inizio.Date.AddDays(1);
|
||||
currRA.Fine = lastFine;
|
||||
data2ins.Add(currRA);
|
||||
// calcolo i gg
|
||||
int numGG = CurrRecord.Fine.Date.Subtract(CurrRecord.Inizio.Date).Days;
|
||||
// ciclo
|
||||
for (int i = 0; i < numGG; i++)
|
||||
{
|
||||
// splitto... inizio = fine + 1 sec...
|
||||
currRA = CurrRecord.Clone() as RegAttivitaModel;
|
||||
currRA.Inizio = lastFine;
|
||||
// porto avanti ultima fine...
|
||||
lastFine = lastFine.AddDays(1);
|
||||
currRA.Fine = currRA.Fine > lastFine ? lastFine : currRA.Fine;
|
||||
data2ins.Add(currRA);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data2ins.Add(CurrRecord);
|
||||
}
|
||||
}
|
||||
// ciclo x inserire
|
||||
foreach (var item in data2ins)
|
||||
{
|
||||
await CDService.RegAttUpdate(item);
|
||||
}
|
||||
}
|
||||
// resetto clone e record corrente...
|
||||
AppMServ.clonedRA = null;
|
||||
CurrRecord = null;
|
||||
// registro fatto
|
||||
await ItemUpdated.InvokeAsync(true);
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
vetoUpd = true;
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var value = currRecord;
|
||||
if (value != null)
|
||||
{
|
||||
if (value.FasiNav != null && value.FasiNav.ProgettoNav != null)
|
||||
{
|
||||
gruppoSel = value.FasiNav.ProgettoNav.Gruppo;
|
||||
idxProj = value.FasiNav.IdxProgetto != null ? (int)value.FasiNav.IdxProgetto : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// effettuo selezione manuale...
|
||||
var currFase = await CDService.AnagFasiByKey(value.IdxFase);
|
||||
if (currFase != null)
|
||||
{
|
||||
if (currFase.ProgettoNav != null)
|
||||
{
|
||||
gruppoSel = currFase.ProgettoNav.Gruppo;
|
||||
}
|
||||
idxProj = currFase.IdxProgetto != null ? (int)currFase.IdxProgetto : 0;
|
||||
}
|
||||
value.IdxFase = 0;
|
||||
}
|
||||
}
|
||||
await ReloadData(false, false);
|
||||
sw.Stop();
|
||||
TimeSpan ts = sw.Elapsed;
|
||||
|
||||
Log.Trace($"Test esecuzione sel cascata: {ts.TotalMilliseconds} ms");
|
||||
vetoUpd = false;
|
||||
}
|
||||
|
||||
protected async Task ReloadData(bool resetProj, bool resetFase)
|
||||
{
|
||||
gruppiList = await CDService.AnagGruppiUser(AppMServ.IdxDipendente);
|
||||
var allProj = await CDService.AnagProjAll();
|
||||
projList = allProj
|
||||
.Where(x => x.Attivo == true && x.Gruppo == gruppoSel)
|
||||
.OrderBy(x => x.ClienteNav.RagSociale)
|
||||
.ThenBy(x => x.NomeProj)
|
||||
.ToList();
|
||||
if (resetProj)
|
||||
{
|
||||
if (projList.Count > 0)
|
||||
{
|
||||
idxProj = projList[0].IdxProgetto;
|
||||
}
|
||||
}
|
||||
var fasiProj = await CDService.AnagFasiByProj(idxProj);
|
||||
if (fasiProj != null)
|
||||
{
|
||||
fasiList = fasiProj;
|
||||
}
|
||||
else
|
||||
{
|
||||
fasiList = new List<AnagFasiModel>();
|
||||
}
|
||||
if (resetFase)
|
||||
{
|
||||
// se ho 2+ risultati --> il primo � titolo faccio il secondo
|
||||
if (fasiList.Count > 1)
|
||||
{
|
||||
var currData = fasiList.Skip(1).FirstOrDefault();
|
||||
if (currData != null)
|
||||
{
|
||||
idxFase = currData.IdxFase;
|
||||
}
|
||||
}
|
||||
// altrimenti primo...
|
||||
else
|
||||
{
|
||||
var currData = fasiList.FirstOrDefault();
|
||||
if (currData != null)
|
||||
{
|
||||
idxFase = currData.IdxFase;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private MessageService AppMServ { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IJSRuntime JSRuntime { get; set; } = null!;
|
||||
|
||||
private bool VetoInsert
|
||||
{
|
||||
get => CDService.VetoInsert;
|
||||
}
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void arrotondaMinuti()
|
||||
{
|
||||
if (CurrRecord != null)
|
||||
{
|
||||
// arrotondo ai 5 min...
|
||||
CurrRecord.Inizio = CORE.Data.Utils.DateRounded(CurrRecord.Inizio.AddMinutes(2), 5, true);
|
||||
// arrotondo ai 5 min...
|
||||
CurrRecord.Fine = CORE.Data.Utils.DateRounded(CurrRecord.Fine.AddMinutes(2), 5, true);
|
||||
checkCoerenzaDate(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Verifico coerenza date (inizio < fine) </summary> <param
|
||||
/// name="modInizio">indica se la data modificata sia l'inizio</param>
|
||||
private void checkCoerenzaDate(bool modInizio)
|
||||
{
|
||||
bool inError = false;
|
||||
// verifica presenza errori
|
||||
if (CurrRecord != null)
|
||||
{
|
||||
inError = CurrRecord.Inizio >= CurrRecord.Fine;
|
||||
if (inError)
|
||||
{
|
||||
// se ho mod inizio --> tengo ferma fine, inizio 1/2 h prima
|
||||
if (modInizio)
|
||||
{
|
||||
CurrRecord.Inizio = CurrRecord.Fine.AddMinutes(-30);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrRecord.Fine = CurrRecord.Inizio.AddMinutes(30);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
@if (isLoading)
|
||||
{
|
||||
<LoadingData></LoadingData>
|
||||
}
|
||||
else
|
||||
{ @if (isSendingData)
|
||||
{
|
||||
<ProgressDisplay Title="Salvataggio ed invio richiesta" CurrVal="@sendDataVal" MaxVal="@sendDataMaxVal" ExpTimeMSec="1000"></ProgressDisplay>
|
||||
}
|
||||
@if (ListRecords == null)
|
||||
{
|
||||
<LoadingData DisplaySize="LoadingData.CtrlSize.Small"></LoadingData>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (showAdd)
|
||||
{
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<b>Inizio Malattia</b>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<input type="date" class="form-control text-end" @bind="@currRecord.DtInizio">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<b>Giorni Malattia</b>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<input type="number" class="form-control text-end" @bind="@currRecord.NumGG">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<b>Codice Certificato (INPS)</b>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<input type="text" class="form-control text-end" @bind="@currRecord.CodCert">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<button class="btn btn-warning w-100" @onclick="() => toggleAddNew()"><i class="fas fa-window-close"></i> Cancel</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
@if (currRecord.CodCert != "")
|
||||
{
|
||||
<button class="btn btn-success w-100" @onclick="() => insertRecord()"><i class="far fa-save"></i> Update</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btn btn-secondary w-100" disabled><i class="far fa-save"></i> Update</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
@if (ListRecords.Count == 0)
|
||||
{
|
||||
<div class="alert alert-warning">
|
||||
Nessu record registrato
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-success btn-sm" @onclick="() => toggleAddNew()">Add New</button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-sm table-striped table-responsive-md border border-dark">
|
||||
<thead>
|
||||
<tr class="bg-dark text-light">
|
||||
<th>
|
||||
<button class="btn btn-success btn-sm" @onclick="() => toggleAddNew()"><i class="fas fa-plus"></i></button>
|
||||
</th>
|
||||
<th>Data</th>
|
||||
<th class="text-center">Giorni</th>
|
||||
<th class="text-end">Codice</th>
|
||||
<th class="text-end"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in ListRecords.OrderByDescending(x => x.DtInizio))
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@if (!item.Conf)
|
||||
{
|
||||
<button class="btn btn-primary btn-sm" @onclick="() => editRec(item)"><i class="fas fa-pen"></i></button>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@item.DtInizio.ToString("dd MMM yyyy"), <b>@item.DtInizio.ToString("dddd")</b>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
@item.NumGG
|
||||
</td>
|
||||
<td class="text-end">
|
||||
@item.CodCert
|
||||
</td>
|
||||
<td class="text-end">
|
||||
@if (!item.Conf)
|
||||
{
|
||||
<button class="btn btn-danger btn-sm" @onclick="() => DeleteRec(item)"><i class="fas fa-trash-alt"></i></button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class RegMalattia
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipendente { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool isLoading
|
||||
{
|
||||
get => loading;
|
||||
set => loading = value;
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> ReportUpdate { get; set; }
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
[Inject]
|
||||
protected MessageService AppMServ { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected IJSRuntime JSRuntime { get; set; } = null!;
|
||||
|
||||
protected bool loading { get; set; } = false;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private bool isSendingData = false;
|
||||
private List<RegMalattieModel>? ListRecords = null;
|
||||
private int sendDataMaxVal = 100;
|
||||
private int sendDataVal = 0;
|
||||
private bool showAdd = false;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
private RegMalattieModel currRecord { get; set; } = new RegMalattieModel();
|
||||
|
||||
private string NomeDip
|
||||
{
|
||||
get
|
||||
{
|
||||
string answ = "per cortesia";
|
||||
if (AppMServ != null && AppMServ.RigaDip != null)
|
||||
{
|
||||
answ = $"{AppMServ.RigaDip.Nome}";
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task DeleteRec(RegMalattieModel selItem)
|
||||
{
|
||||
// chiedo verifica
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", "Sicuro di voler eliminare il record selezionato??"))
|
||||
return;
|
||||
|
||||
isSendingData = true;
|
||||
sendDataVal = 0;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await Task.Delay(1);
|
||||
var updTask = Task.Run(async () =>
|
||||
await CDService.RegMalattieDelete(selItem)
|
||||
);
|
||||
await updTask;
|
||||
sendDataVal = 100;
|
||||
await Task.Delay(1);
|
||||
await ReloadData();
|
||||
await ReportUpdate.InvokeAsync(true);
|
||||
isSendingData = false;
|
||||
}
|
||||
|
||||
private void editRec(RegMalattieModel selItem)
|
||||
{
|
||||
currRecord = selItem;
|
||||
showAdd = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registra il record
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task insertRecord()
|
||||
{
|
||||
isSendingData = true;
|
||||
sendDataVal = 0;
|
||||
await Task.Delay(1);
|
||||
// forzo dipendente
|
||||
currRecord.IdxDipendente = AppMServ.IdxDipendente;
|
||||
await Task.Delay(1);
|
||||
sendDataVal = 5;
|
||||
await Task.Delay(1);
|
||||
var updTask = Task.Run(async () =>
|
||||
await CDService.RegMalattieUpsert(currRecord)
|
||||
);
|
||||
await updTask;
|
||||
// effettuo insert...
|
||||
sendDataVal = 100;
|
||||
await Task.Delay(1);
|
||||
// aggiorno display...
|
||||
showAdd = false;
|
||||
await ReloadData();
|
||||
await ReportUpdate.InvokeAsync(true);
|
||||
isSendingData = false;
|
||||
}
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
ListRecords = null;
|
||||
await Task.Delay(1);
|
||||
ListRecords = await CDService.RegMalattieGetByDip(IdxDipendente, 10);
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task toggleAddNew()
|
||||
{
|
||||
showAdd = !showAdd;
|
||||
await Task.Delay(1);
|
||||
if (showAdd)
|
||||
{
|
||||
currRecord = new RegMalattieModel() { IdxDipendente = AppMServ.IdxDipendente };
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
@if (isLoading)
|
||||
{
|
||||
<LoadingData></LoadingData>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (isSendingData)
|
||||
{
|
||||
<ProgressDisplay Title="Salvataggio ed invio richiesta" CurrVal="@sendDataVal" MaxVal="@sendDataMaxVal" ExpTimeMSec="1000"></ProgressDisplay>
|
||||
}
|
||||
@if (ListRecords == null)
|
||||
{
|
||||
<LoadingData DisplaySize="LoadingData.CtrlSize.Small"></LoadingData>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (showAdd)
|
||||
{
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-5">
|
||||
<b>Causale</b>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
<div class="input-group input-group-sm">
|
||||
<select @bind="@CodGiust" class="form-select">
|
||||
<option value="">--- Selezionare Causale ---</option>
|
||||
@if (ListGiust != null)
|
||||
{
|
||||
@foreach (var giust in ListGiust)
|
||||
{
|
||||
<option value="@giust.codGiust">@giust.descrizione</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-5">
|
||||
<b>Inizio Periodo</b>
|
||||
<div class="small">min: @($"{minDate(currRecord.CodGiust):dd.MM.yyyy}")</div>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
@if (@CodGiust == "PERM" || @CodGiust == "104")
|
||||
{
|
||||
<input type="datetime-local" aria-label="Last name" class="form-control text-end" @bind="@dtStart" min="@($"{minDate(CodGiust):yyyy-MM-dd HH:mm}")" max="@($"{maxDate(CodGiust):yyyy-MM-dd HH:mm}")">
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="date" aria-label="Last name" class="form-control text-end" @bind="@dtStart" min="@($"{minDate(CodGiust):yyyy-MM-dd}")" max="@($"{maxDate(CodGiust):yyyy-MM-dd}")">
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-5">
|
||||
<b>Fine Periodo</b>
|
||||
<div class="small">max: @($"{maxDate(currRecord.CodGiust):dd.MM.yyyy}")</div>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
@if (@CodGiust == "PERM" || @CodGiust == "104")
|
||||
{
|
||||
<input type="datetime-local" aria-label="Last name" class="form-control text-end" @bind="@dtEnd" min="@($"{minDate(CodGiust):yyyy-MM-dd HH:mm}")" max="@($"{maxDate(CodGiust):yyyy-MM-dd HH:mm}")">
|
||||
}
|
||||
else
|
||||
{
|
||||
<input type="date" aria-label="Last name" class="form-control text-end" @bind="@dtEnd" min="@($"{minDate(CodGiust):yyyy-MM-dd}")" max="@($"{maxDate(CodGiust):yyyy-MM-dd}")">
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<b>Note</b>
|
||||
</div>
|
||||
<div class="small">
|
||||
(opzionali)
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="col-12">
|
||||
<input type="text" aria-label="Last name" class="form-control text-end" @bind="@currRecord.Note">
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<button class="btn btn-warning w-100" @onclick="() => toggleAddNew()"><i class="fas fa-window-close"></i> Cancel</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
@if (CodGiust != "")
|
||||
{
|
||||
<button class="btn btn-success w-100" @onclick="() => insertRecord()"><i class="far fa-save"></i> Update</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btn btn-secondary w-100" disabled><i class="far fa-save"></i> Update</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
@if (ListRecords.Count == 0)
|
||||
{
|
||||
<div class="alert alert-warning d-flex justify-content-between">
|
||||
<div>
|
||||
Nessu record registrato
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-success btn-sm" @onclick="() => toggleAddNew()">Add New</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-sm table-striped table-responsive-md border border-dark">
|
||||
<thead>
|
||||
<tr class="bg-dark text-light">
|
||||
<th>
|
||||
<button class="btn btn-success btn-sm" @onclick="() => toggleAddNew()"><i class="fas fa-plus"></i></button>
|
||||
</th>
|
||||
<th class="">
|
||||
Richiesta
|
||||
@*<div class="d-flex justify-content-between">
|
||||
<div>Periodo</div>
|
||||
<div>Causale</div>
|
||||
</div>*@
|
||||
</th>
|
||||
<th class="text-end"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in ListRecords)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@if (!item.Conf)
|
||||
{
|
||||
<button class="btn btn-primary btn-sm" @onclick="() => editRec(item)">
|
||||
<b>@item.CodGiustTrim</b>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="btn btn-sm btn-secondary disabled">
|
||||
<b>@item.CodGiustTrim</b>
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="w-100">
|
||||
@if (item.CodGiust == "PERM")
|
||||
{
|
||||
<div class="d-flex justify-content-between pe-2">
|
||||
<div>
|
||||
@item.DtStart.ToString("dd MMM yyyy"), <b>@item.DtStart.ToString("HH:mm")</b>
|
||||
<i class="fa-solid fa-caret-right"></i>
|
||||
<b>@item.DtEnd.ToString("HH:mm")</b>
|
||||
</div>
|
||||
<div>
|
||||
<b>@item.Durata</b>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="d-flex justify-content-between pe-2">
|
||||
<div>
|
||||
<b>@item.DtStart.ToString("dd MMM")</b> @item.DtStart.ToString("yyyy")
|
||||
<i class="fa-solid fa-caret-right"></i>
|
||||
<b>@item.DtEnd.ToString("dd MMM")</b> @item.DtEnd.ToString("yyyy")
|
||||
</div>
|
||||
<div>
|
||||
<b>@item.Durata</b>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<small>
|
||||
@item.Note
|
||||
</small>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
@if (!item.Conf)
|
||||
{
|
||||
<button class="btn btn-danger btn-sm" @onclick="() => DeleteRec(item)"><i class="fas fa-trash-alt"></i></button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,317 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class RegRichieste
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipendente { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool isLoading
|
||||
{
|
||||
get => loading;
|
||||
set => loading = value;
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public int months { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> ReportUpdate { get; set; }
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
[Inject]
|
||||
protected MessageService AppMServ { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected IJSRuntime JSRuntime { get; set; } = null!;
|
||||
|
||||
protected bool loading { get; set; } = false;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
dtInizio = new DateTime(DateTime.Today.Year, 1, 1);
|
||||
dtFine = dtInizio.AddYears(1);
|
||||
currRecord.IdxDipendente = AppMServ.IdxDipendente;
|
||||
CodGiust = "";
|
||||
var rawGiust = await CDService.AnagGiust();
|
||||
if (rawGiust != null)
|
||||
{
|
||||
ListGiust = rawGiust.Where(x => x.showReq).ToList();
|
||||
}
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private bool isSendingData = false;
|
||||
private List<AnagGiustModel>? ListGiust = null;
|
||||
private List<RegRichiesteModel>? ListRecords = null;
|
||||
|
||||
/// <summary>
|
||||
/// Numero giorni minimo per richiesta ferie
|
||||
/// </summary>
|
||||
private int NumDayFerieRichAntic = 14;
|
||||
|
||||
/// <summary>
|
||||
/// NUmero giorni massimo per richiesta permessi
|
||||
/// </summary>
|
||||
private int NumDayPermMax = 21;
|
||||
|
||||
private int sendDataMaxVal = 100;
|
||||
private int sendDataVal = 0;
|
||||
private bool showAdd = false;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
private string CodGiust
|
||||
{
|
||||
get => currRecord.CodGiust;
|
||||
set
|
||||
{
|
||||
if (currRecord.CodGiust != value)
|
||||
{
|
||||
currRecord.CodGiust = value;
|
||||
// calcolo limite date
|
||||
DateTime mDtStart = minDate(value);
|
||||
DateTime mDtEnd = maxDate(value);
|
||||
// imposto dataOra min/max da tipo cod Giust
|
||||
if (value != "FER")
|
||||
{
|
||||
int ora = DateTime.Now.Hour;
|
||||
mDtStart = minDate(value).AddHours(ora);
|
||||
mDtEnd = minDate(value).AddHours(ora + 4);
|
||||
}
|
||||
// modifico SE fossero extra periodo
|
||||
if (mDtStart > dtStart)
|
||||
{
|
||||
dtStart = mDtStart;
|
||||
}
|
||||
if (mDtEnd < dtEnd)
|
||||
{
|
||||
dtStart = mDtStart;
|
||||
dtEnd = mDtEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RegRichiesteModel currRecord { get; set; } = new RegRichiesteModel();
|
||||
|
||||
private DateTime dtEnd
|
||||
{
|
||||
get => currRecord.DtEnd;
|
||||
set
|
||||
{
|
||||
currRecord.DtEnd = value;
|
||||
// verifico coerenza date...
|
||||
if (dtStart > dtEnd)
|
||||
{
|
||||
dtStart = dtEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime dtFine { get; set; } = DateTime.Today;
|
||||
|
||||
private DateTime dtInizio { get; set; } = DateTime.Today;
|
||||
|
||||
private DateTime dtStart
|
||||
{
|
||||
get => currRecord.DtStart;
|
||||
set
|
||||
{
|
||||
currRecord.DtStart = value;
|
||||
// verifico coerenza date...
|
||||
if (dtEnd < dtStart)
|
||||
{
|
||||
dtEnd = dtStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string NomeDip
|
||||
{
|
||||
get
|
||||
{
|
||||
string answ = "per cortesia";
|
||||
if (AppMServ != null && AppMServ.RigaDip != null)
|
||||
{
|
||||
answ = $"{AppMServ.RigaDip.Nome}";
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task DeleteRec(RegRichiesteModel selItem)
|
||||
{
|
||||
// chiedo verifica
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", "Sicuro di voler eliminare il record selezionato??"))
|
||||
return;
|
||||
|
||||
isSendingData = true;
|
||||
sendDataVal = 0;
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
var updTask = Task.Run(async () =>
|
||||
await CDService.RegRichiesteDelete(selItem)
|
||||
);
|
||||
await updTask;
|
||||
sendDataVal = 100;
|
||||
await Task.Delay(1);
|
||||
await ReloadData();
|
||||
await ReportUpdate.InvokeAsync(true);
|
||||
isSendingData = false;
|
||||
}
|
||||
|
||||
private void editRec(RegRichiesteModel selItem)
|
||||
{
|
||||
currRecord = selItem;
|
||||
showAdd = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// init valori da config
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task initConf()
|
||||
{
|
||||
// leggo conf standard giorni permessi/ferie
|
||||
var sNumDayFerieRichAntic = await CDService.ConfigGetKey("NumDayFerieRichAntic");
|
||||
if (sNumDayFerieRichAntic != null)
|
||||
{
|
||||
int.TryParse(sNumDayFerieRichAntic.valore, out NumDayFerieRichAntic);
|
||||
}
|
||||
var sNumDayPermMax = await CDService.ConfigGetKey("NumDayPermMax");
|
||||
if (sNumDayPermMax != null)
|
||||
{
|
||||
int.TryParse(sNumDayPermMax.valore, out NumDayPermMax);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registra il record
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task insertRecord()
|
||||
{
|
||||
isSendingData = true;
|
||||
sendDataVal = 0;
|
||||
await Task.Delay(1);
|
||||
// verifica preliminare delle date compatibili...
|
||||
var dtCheckMin = minDate(currRecord.CodGiust);
|
||||
var dtCheckMax = maxDate(currRecord.CodGiust);
|
||||
if (currRecord.DtStart < dtCheckMin)
|
||||
{
|
||||
currRecord.DtStart = dtCheckMin;
|
||||
}
|
||||
else if (currRecord.DtStart > dtCheckMax)
|
||||
{
|
||||
currRecord.DtStart = dtCheckMax;
|
||||
currRecord.DtEnd = dtCheckMax;
|
||||
}
|
||||
if (currRecord.DtEnd > dtCheckMax)
|
||||
{
|
||||
currRecord.DtEnd = dtCheckMax;
|
||||
}
|
||||
else if (currRecord.DtEnd < dtCheckMin)
|
||||
{
|
||||
currRecord.DtStart = dtCheckMin;
|
||||
currRecord.DtEnd = dtCheckMin;
|
||||
}
|
||||
sendDataVal = 5;
|
||||
await Task.Delay(1);
|
||||
var updTask = Task.Run(async () =>
|
||||
await CDService.RegRichiesteUpsert(currRecord)
|
||||
);
|
||||
await updTask;
|
||||
// effettuo insert...
|
||||
sendDataVal = 100;
|
||||
await Task.Delay(1);
|
||||
showAdd = false;
|
||||
await ReloadData();
|
||||
await ReportUpdate.InvokeAsync(true);
|
||||
isSendingData = false;
|
||||
}
|
||||
|
||||
private DateTime maxDate(string codGiust)
|
||||
{
|
||||
DateTime answ = DateTime.Today.AddDays(NumDayPermMax);
|
||||
switch (codGiust)
|
||||
{
|
||||
case "FER":
|
||||
answ = new DateTime(answ.Year, answ.Month, 1).AddMonths(months);
|
||||
break;
|
||||
|
||||
case "104":
|
||||
case "PERM":
|
||||
default:
|
||||
DateTime.Today.AddDays(NumDayPermMax);
|
||||
break;
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
private DateTime minDate(string codGiust)
|
||||
{
|
||||
DateTime answ = DateTime.Today;
|
||||
switch (codGiust)
|
||||
{
|
||||
case "FER":
|
||||
answ = DateTime.Today.AddDays(NumDayFerieRichAntic);
|
||||
break;
|
||||
|
||||
case "104":
|
||||
case "PERM":
|
||||
default:
|
||||
answ = DateTime.Today;
|
||||
break;
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
ListRecords = null;
|
||||
await initConf();
|
||||
await Task.Delay(1);
|
||||
ListRecords = await CDService.RegRichiesteGetByDip(AppMServ.IdxDipendente, dtInizio, dtFine);
|
||||
}
|
||||
|
||||
private async Task toggleAddNew()
|
||||
{
|
||||
showAdd = !showAdd;
|
||||
await Task.Delay(1);
|
||||
if (showAdd)
|
||||
{
|
||||
currRecord = new RegRichiesteModel() { IdxDipendente = AppMServ.IdxDipendente };
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<div class="prjCard">
|
||||
<div class="up">
|
||||
<div class="prjTime p-1" @onclick="() => DoClone(CurrItem)">
|
||||
<div>@($"{CurrItem.Inizio:HH:mm}")</div>
|
||||
<div>@($"{CurrItem.Fine:HH:mm}")</div>
|
||||
<div class="badge bg-dark font-monospace">
|
||||
@($"{CurrItem.Durata.Hours:00}:{CurrItem.Durata.Minutes:00}")
|
||||
</div>
|
||||
</div>
|
||||
<div class="prjContent p-1 small" @onclick="() => DoSel(CurrItem)">
|
||||
<div class="group"><b>@CurrItem.FasiNav?.ProgettoNav?.ClienteNav?.RagSociale</b> <span>@CurrItem.FasiNav?.ProgettoNav?.NomeProj</span></div>
|
||||
<div class="fase">@CurrItem.FasiNav?.DescrizioneFase</div>
|
||||
<div class="desc small">@CurrItem.Descrizione</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class SingleRaDisplay
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public RegAttivitaModel CurrItem { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public bool EnableAction { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<RegAttivitaModel> ItemCloned { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<RegAttivitaModel> ItemSelected { get; set; }
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gestione evento cloning
|
||||
/// </summary>
|
||||
/// <param name="currRecord"></param>
|
||||
protected async void DoClone(RegAttivitaModel currRecord)
|
||||
{
|
||||
if (EnableAction)
|
||||
{
|
||||
if (currRecord != null)
|
||||
{
|
||||
var newData = currRecord.Clone();
|
||||
newData.IdxRa = 0;
|
||||
await ItemCloned.InvokeAsync(newData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gestione evento selezione x editing
|
||||
/// </summary>
|
||||
protected async void DoSel(RegAttivitaModel currRecord)
|
||||
{
|
||||
if (EnableAction)
|
||||
{
|
||||
if (currRecord != null)
|
||||
{
|
||||
await ItemSelected.InvokeAsync(currRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
.prjCard {
|
||||
/*background-color: #fff;*/
|
||||
background-image: linear-gradient(90deg, #fff 0%, #DEDEDE 95%, rgba(255, 255, 255, 0) 100%);
|
||||
width: 100%;
|
||||
border-radius: 1.5rem 0rem 0rem 1.5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-content: start;
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
.extBase {
|
||||
padding: 0.5rem;
|
||||
font-size: 1rem;
|
||||
background-color: #304FFE;
|
||||
height: 100%;
|
||||
}
|
||||
.prjTime {
|
||||
padding: 0.5rem;
|
||||
font-size: 1rem;
|
||||
background-color: #304FFE;
|
||||
height: 100%;
|
||||
border-radius: 0.8rem 0 0 0.8rem;
|
||||
color: #fff;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.up {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
.prjContent {
|
||||
font-size: 1rem;
|
||||
color: #000;
|
||||
text-align: start;
|
||||
padding: 0.5rem;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.prjContent .group {
|
||||
font-size: 0.95rem;
|
||||
width: 95%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.prjContent .fase {
|
||||
font-size: 0.85rem;
|
||||
color: rgba(128, 128, 128, 0.8);
|
||||
text-transform: capitalize;
|
||||
width: 95%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.prjContent .desc {
|
||||
font-size: 0.75rem;
|
||||
width: 90%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
.prjCard {
|
||||
/*background-color: #fff;*/
|
||||
background-image: linear-gradient(90deg, #fff 0%, #DEDEDE 95%, rgba(255,255,255,0) 100%);
|
||||
width: 100%;
|
||||
border-radius: 1.5rem 0rem 0rem 1.5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-content: start;
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
|
||||
.extBase {
|
||||
padding: 0.5rem;
|
||||
font-size: 1rem;
|
||||
background-color: #304FFE;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.prjTime {
|
||||
.extBase;
|
||||
border-radius: 0.8rem 0 0 0.8rem;
|
||||
color: #fff;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.up {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.prjContent {
|
||||
font-size: 1rem;
|
||||
color: #000;
|
||||
text-align: start;
|
||||
padding: 0.5rem;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.prjContent .group {
|
||||
font-size: 0.95rem;
|
||||
width: 95%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.prjContent .fase {
|
||||
font-size: 0.85rem;
|
||||
color: rgba(128, 128, 128, 0.8);
|
||||
text-transform: capitalize;
|
||||
width: 95%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.prjContent .desc {
|
||||
font-size: 0.75rem;
|
||||
width: 90%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.prjCard{background-image:linear-gradient(90deg,#fff 0%,#dedede 95%,rgba(255,255,255,0) 100%);width:100%;border-radius:1.5rem 0 0 1.5rem;display:flex;justify-content:space-between;align-content:start;line-height:1.2rem;}.extBase{padding:.5rem;font-size:1rem;background-color:#304ffe;height:100%;}.prjTime{padding:.5rem;font-size:1rem;background-color:#304ffe;height:100%;border-radius:.8rem 0 0 .8rem;color:#fff;font-size:.9rem;}.up{display:flex;justify-content:space-between;width:100%;}.prjContent{font-size:1rem;color:#000;text-align:start;padding:.5rem;width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}.prjContent .group{font-size:.95rem;width:95%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}.prjContent .fase{font-size:.85rem;color:rgba(128,128,128,.8);text-transform:capitalize;width:95%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}.prjContent .desc{font-size:.75rem;width:90%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
<CalendarWeek DateList="@ListDate" WeekChanged="ChangePeriod" EventList="@eventListPerWeek"></CalendarWeek>
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
using EgwCoreLib.Razor.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class TestWeekCal
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public DateTime DtReq { get; set; } = DateTime.Today;
|
||||
|
||||
[Parameter]
|
||||
public int NumDays { get; set; } = 1;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
protected List<DateTime> ListDate = new List<DateTime>();
|
||||
|
||||
[Parameter]
|
||||
public List<CalendarEvent> eventList { get; set; } = new List<CalendarEvent>();
|
||||
|
||||
protected List<CalendarEvent> eventListPerWeek { get; set; } = new List<CalendarEvent>();
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected async Task ChangePeriod(int numStep)
|
||||
{
|
||||
ListDate = new List<DateTime>();
|
||||
await Task.Delay(1);
|
||||
DtReq = DtReq.AddDays(NumDays * numStep);
|
||||
await reloadData();
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await reloadData();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await reloadData();
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Methods
|
||||
|
||||
protected Random rnd = new Random();
|
||||
|
||||
private async Task reloadData()
|
||||
{
|
||||
ListDate = new List<DateTime>();
|
||||
int nWeek = DtReq.DayOfWeek - DayOfWeek.Monday;
|
||||
if (NumDays < 5 || NumDays > 7)
|
||||
{
|
||||
nWeek = 0;
|
||||
}
|
||||
for (int i = 0; i < NumDays; i++)
|
||||
{
|
||||
ListDate.Add(DtReq.AddDays(i - nWeek));
|
||||
}
|
||||
eventListPerWeek = eventList.Where(x => ListDate.Contains(x.Inizio)).ToList();
|
||||
|
||||
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
@if (!userIsAdmin)
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
<h3>Attenzione</h3>
|
||||
Autorizzazione richiesta per accedere alla funzionalità
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (ListTimbRich == null)
|
||||
{
|
||||
<LoadingData></LoadingData>
|
||||
}
|
||||
else if (ListTimbRich.Count == 0)
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
<h5>Richieste</h5>
|
||||
Nessun record trovato...
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<TimbList ListTimb="@ListTimbRich" EnableAdmin="true" EnableEdit="true" ReqUpdate="DoUpdate" ShowReload="@isLoading"></TimbList>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class TimbAdmin
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipAdmin { get; set; } = 0;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Aggiorno e riporto update
|
||||
/// </summary>
|
||||
protected async void DoUpdate(bool forceReload)
|
||||
{
|
||||
if (forceReload)
|
||||
{
|
||||
isLoading= true;
|
||||
await Task.Delay(1);
|
||||
await ReloadData();
|
||||
await Task.Delay(1);
|
||||
isLoading = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
// verifico se il dip sia admin...
|
||||
var listaRuoli = await CDService.Dip2RuoliGetAll();
|
||||
var rigaDip = listaRuoli.FirstOrDefault(x => x.IdxDipendente == IdxDipAdmin && x.CodRuolo == "AppMancTimb");
|
||||
userIsAdmin = rigaDip != null;
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private bool userIsAdmin = false;
|
||||
|
||||
private bool isLoading = false;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
private List<TimbratureModel>? ListTimbRich { get; set; } = null;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
ListTimbRich = null;
|
||||
await Task.Delay(1);
|
||||
if (userIsAdmin)
|
||||
{
|
||||
var rawData = await CDService.TimbratureRichieste();
|
||||
ListTimbRich = rawData
|
||||
.OrderByDescending(x => x.DataOra)
|
||||
.ToList();
|
||||
}
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
|
||||
@if (ShowReload)
|
||||
{
|
||||
<LoadingData DisplaySize="LoadingData.CtrlSize.Small"></LoadingData>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table table-sm table-responsive-md @TextCss">
|
||||
<thead>
|
||||
<tr>
|
||||
@if (EnableAdmin)
|
||||
{
|
||||
<th>
|
||||
<i class="fa-solid fa-user-shield"></i>
|
||||
</th>
|
||||
}
|
||||
<th><i class="far fa-clock"></i></th>
|
||||
<th title="Entrata"><i class="fas fa-sign-in-alt"></i></th>
|
||||
<th class="text-center">Scambio</th>
|
||||
<th title="Uscita"><i class="fas fa-sign-out-alt"></i></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (@isLoading)
|
||||
{
|
||||
<tr>
|
||||
@if (EnableAdmin)
|
||||
{
|
||||
<td colspan="6">
|
||||
<LoadingData DisplaySize="LoadingData.CtrlSize.Small"></LoadingData>
|
||||
</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td colspan="5">
|
||||
<LoadingData DisplaySize="LoadingData.CtrlSize.Small"></LoadingData>
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var item in _ListTimb)
|
||||
{
|
||||
<tr>
|
||||
@if (EnableAdmin)
|
||||
{
|
||||
<td>
|
||||
<button class="btn btn-success btn-sm" @onclick="() => ApprovaTimb(item)">
|
||||
<i class="fa-solid fa-thumbs-up"></i>
|
||||
</button>
|
||||
</td>
|
||||
}
|
||||
<td class="small">
|
||||
@if (EnableAdmin)
|
||||
{
|
||||
<div>
|
||||
<small><b>@item.DipNav.Cognome</b> @item.DipNav.Nome</small>
|
||||
</div>
|
||||
@item.DataOra.ToString("dd/MM - HH:mm")
|
||||
}
|
||||
else
|
||||
{
|
||||
@item.DataOra.ToString("HH:mm")
|
||||
}
|
||||
</td>
|
||||
<td title="Entrata">
|
||||
@if (@item.Entrata == true)
|
||||
{
|
||||
<i class="fas fa-times"></i>
|
||||
}
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-sm btn-primary" title="Scambio IN/OUT" @onclick="() => ScambioInOut(item)"><i class="fas fa-exchange-alt"></i></button>
|
||||
</td>
|
||||
<td title="Uscita">
|
||||
@if (@item.Entrata == false)
|
||||
{
|
||||
<i class="fas fa-times"></i>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (EnableEdit && canDelete(@item))
|
||||
{
|
||||
<button class="btn btn-sm btn-danger" @onclick="() => Delete(item)"><i class="fas fa-trash-alt"></i></button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btn btn-sm btn-secondary" disabled><i class="fas fa-trash-alt"></i></button>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class TimbList
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public bool EnableAdmin { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public bool EnableEdit { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public List<TimbratureModel> ListTimb
|
||||
{
|
||||
get => _ListTimb;
|
||||
set => _ListTimb = value;
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> ReqUpdate { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool ShowReload { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public string TextCss { get; set; } = "text-dark";
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
[Inject]
|
||||
protected CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected IJSRuntime JSRuntime { get; set; } = null!;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected async void ApprovaTimb(TimbratureModel currRecord)
|
||||
{
|
||||
string message = "Sicuro di voler approvare la richiesta selezionata?";
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", message))
|
||||
return;
|
||||
|
||||
// aggiorno
|
||||
currRecord.Approv = true;
|
||||
await CDService.TimbratureUpdate(currRecord);
|
||||
await Task.Delay(1);
|
||||
await ReqUpdate.InvokeAsync(true);
|
||||
}
|
||||
|
||||
protected async void Delete(TimbratureModel currRecord)
|
||||
{
|
||||
string message = "Sicuro di voler eliminare/rifiutare la richiesta selezionata?";
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", message))
|
||||
return;
|
||||
|
||||
// elimino
|
||||
await CDService.TimbratureDelete(currRecord);
|
||||
await ReqUpdate.InvokeAsync(true);
|
||||
}
|
||||
|
||||
protected async void ScambioInOut(TimbratureModel currRecord)
|
||||
{
|
||||
isLoading = true;
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
// aggiorno
|
||||
currRecord.Entrata = !currRecord.Entrata;
|
||||
bool done = await CDService.TimbratureUpdate(currRecord);
|
||||
await Task.Delay(1);
|
||||
isLoading = false;
|
||||
if (done)
|
||||
{
|
||||
await ReqUpdate.InvokeAsync(false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Properties
|
||||
|
||||
private List<TimbratureModel> _ListTimb { get; set; } = new List<TimbratureModel>();
|
||||
private bool isLoading { get; set; } = false;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private bool canDelete(TimbratureModel currRec)
|
||||
{
|
||||
bool answ = false;
|
||||
if (currRec.Approv != null)
|
||||
{
|
||||
answ = !(bool)currRec.Approv;
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
@if (ListTimbRich == null)
|
||||
{
|
||||
<LoadingData></LoadingData>
|
||||
}
|
||||
else if (ListTimbRich.Count == 0)
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
<h3>Timbrature</h3>
|
||||
Nessun record trovato...
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<TimbList ListTimb="@ListTimbRich" EnableAdmin="false" EnableEdit="true" ReqUpdate="DoUpdate" TextCss="text-light" ShowReload="@isLoading"></TimbList>
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class TimbUser
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public int IdxDipCurr { get; set; } = 0;
|
||||
|
||||
[Parameter]
|
||||
public List<TimbratureModel>? ListTimbRich { get; set; } = null;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Aggiorno e riporto update
|
||||
/// </summary>
|
||||
protected async void DoUpdate(bool forceReload)
|
||||
{
|
||||
if (forceReload)
|
||||
{
|
||||
isLoading = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
// richiesta rilettura dati...
|
||||
ListTimbRich = null;
|
||||
await Task.Delay(1);
|
||||
await ReloadData();
|
||||
await Task.Delay(1);
|
||||
isLoading = false;
|
||||
await ReqReload.InvokeAsync(true);
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> ReqReload { get; set; }
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
private bool isLoading { get; set; } = false;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
isLoading = true;
|
||||
await Task.Delay(1);
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="card w-100">
|
||||
<div class="card-header list-group-item-info2 text-center">
|
||||
<div class="input-group input-group-sm">
|
||||
<select @bind="@idxDipendente" class="form-select">
|
||||
<option value="0">--- Selezionare Nominativo ---</option>
|
||||
@foreach (var dip in ListaDip)
|
||||
{
|
||||
<option value="@dip.IdxDipendente">@dip.Cognome @dip.Nome</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (@idxDipendente > 0 && recordDip != null)
|
||||
{
|
||||
<div class="card-body text-center">
|
||||
<div class="img-fluid" id="qrCodeImg_@idxDipendente"></div>
|
||||
</div>
|
||||
<div class="card-footer text-center py-0 small">
|
||||
<Toggler SelFilter="@ToggleData" FilterChanged="evToggled"></Toggler>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
using EgwCoreLib.Razor;
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Compo
|
||||
{
|
||||
public partial class UserQrCode
|
||||
{
|
||||
#region Protected Methods
|
||||
|
||||
protected string getUrlCode(string baseUrl, string authKey)
|
||||
{
|
||||
return $"{baseUrl}jumper?idxDipendente={idxDipendente}&authKey={authKey}";
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (recordDip != null)
|
||||
{
|
||||
string baseUrl = ToggleData.isActive ? UrlLinkInt : UrlLinkExt;
|
||||
//calcolo authKey valida x url --> escaped!
|
||||
string authKeyFix = System.Web.HttpUtility.UrlEncode(recordDip.AuthKey);
|
||||
string newCode = getUrlCode(baseUrl, authKeyFix);
|
||||
if (rawCode != newCode)
|
||||
{
|
||||
rawCode = newCode;
|
||||
await JSRuntime.InvokeVoidAsync("clearContent", $"qrCodeImg_{idxDipendente}");
|
||||
await JSRuntime.InvokeVoidAsync("displayQr", $"qrCodeImg_{idxDipendente}", rawCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
initToggler();
|
||||
await initConf();
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
protected string rawCode = "";
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private List<DipendentiModel> ListaDip = new List<DipendentiModel>();
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
private int _idxDip { get; set; } = 0;
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IConfiguration Configuration { get; set; } = null!;
|
||||
|
||||
private int idxDipendente
|
||||
{
|
||||
get => _idxDip;
|
||||
set
|
||||
{
|
||||
_idxDip = value;
|
||||
if (value > 0)
|
||||
{
|
||||
recordDip = ListaDip.FirstOrDefault(x => x.IdxDipendente == idxDipendente);
|
||||
}
|
||||
else
|
||||
{
|
||||
recordDip = null;
|
||||
}
|
||||
//StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[Inject]
|
||||
private IJSRuntime JSRuntime { get; set; } = null!;
|
||||
|
||||
private DipendentiModel? recordDip { get; set; } = null;
|
||||
|
||||
private Toggler.SelectGlobalToggle ToggleData { get; set; } = new Toggler.SelectGlobalToggle();
|
||||
|
||||
private string UrlLinkExt { get; set; } = "";
|
||||
|
||||
private string UrlLinkInt { get; set; } = "";
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task evToggled(Toggler.SelectGlobalToggle newTogData)
|
||||
{
|
||||
ToggleData = newTogData;
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// init valori da config
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task initConf()
|
||||
{
|
||||
UrlLinkInt = Configuration.GetValue<string>("OptPar:UrlLinkInt") ?? "";
|
||||
UrlLinkExt = Configuration.GetValue<string>("OptPar:UrlLinkExt") ?? "";
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
private void initToggler()
|
||||
{
|
||||
ToggleData = new Toggler.SelectGlobalToggle()
|
||||
{
|
||||
leftString = "Link Esterno",
|
||||
rightString = "Link Interno",
|
||||
placardCss = "bg-light border-light text-dark",
|
||||
};
|
||||
}
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
var rawList = await CDService.DipendentiGetAll();
|
||||
ListaDip = rawList
|
||||
.Where(x => x.Attivo == true)
|
||||
.OrderBy(x => x.Cognome)
|
||||
.ThenBy(x => x.Nome)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
@using NLog
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<PageTitle>GPW.CORE.Smart</PageTitle>
|
||||
|
||||
<div class="page vh-100">
|
||||
<main class="vw-100">
|
||||
<CmpTop @rendermode="InteractiveServer" IpIsLocal=@isIpLocal CurrUser=@currUser EC_ReturnHome=@ReturnHome EC_ForceReset=@ForceReset></CmpTop>
|
||||
<article class="content p-2">
|
||||
@Body
|
||||
</article>
|
||||
@if (dataLoaded)
|
||||
{
|
||||
<NavBottom @rendermode="InteractiveServer" CurrConf=@currConfig IdxDipendente=@currUser.IdxDip></NavBottom>
|
||||
}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,389 @@
|
||||
using EgwCoreLib.Razor.Data;
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Components.Pages;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using NLog;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Layout
|
||||
{
|
||||
public partial class MainLayout : IDisposable
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
#if false
|
||||
MService.EA_DipUpdated -= MService_EA_DipUpdated;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
protected string currIpv4 = "";
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected string baseUrl
|
||||
{
|
||||
get => configuration.GetValue<string>("OptConf:BaseUrl") ?? "~/";
|
||||
}
|
||||
|
||||
[Inject]
|
||||
protected CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected IConfiguration configuration { get; set; } = null!;
|
||||
|
||||
protected bool DipOk
|
||||
{
|
||||
get => (currUser != null && currUser.IdxDip > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica se IP sia locale
|
||||
/// </summary>
|
||||
protected bool isIpLocal
|
||||
{
|
||||
get
|
||||
{
|
||||
bool answ = false;
|
||||
if (!string.IsNullOrEmpty(currIpv4))
|
||||
{
|
||||
answ = currIpv4.Contains(LocalNet);
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
[Inject]
|
||||
protected LicenseService LicServ { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected MessageService MService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected NavigationManager navManager { get; set; } = null!;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Verifica informazioni condivise applicazione
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected async Task<bool> checkActivations()
|
||||
{
|
||||
bool allOk = false;
|
||||
if (LicServ.ValidData)
|
||||
{
|
||||
// se non fosse tutto ok
|
||||
if (!LicServ.HasActivData)
|
||||
{
|
||||
// chiamo refresh licenze da remoto
|
||||
allOk = await LicServ.RefreshLicense();
|
||||
}
|
||||
// se non avesse info applicativo...
|
||||
if (!LicServ.HasAppData)
|
||||
{
|
||||
await LicServ.RefreshApplic();
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(1);
|
||||
return allOk;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica informazioni condivise applicazione
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected async Task<bool> checkSharedInfo()
|
||||
{
|
||||
bool allOk = false;
|
||||
if (!LicServ.ValidData)
|
||||
{
|
||||
// salvo cod app da Conf...
|
||||
LicServ.Applicazione = CDService.CodApp;
|
||||
// effettuo lettura dati preliminari da AKV (eventualmente in cache...)
|
||||
List<AnagKeyValueModel>? rawAkvList = await CDService.AKVList();
|
||||
if (rawAkvList != null)
|
||||
{
|
||||
LicServ.AKVList = rawAkvList;
|
||||
allOk = LicServ.InitAkv();
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(1);
|
||||
return allOk;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica info specifiche utente
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected async Task<bool> checkUserLicense()
|
||||
{
|
||||
bool answ = false;
|
||||
// verifico utente attivo
|
||||
answ = MService.IsActive;
|
||||
if (answ)
|
||||
{
|
||||
var rawActList = LicServ.ActivList;
|
||||
// recupero hash utente
|
||||
string hashImpiego = MService.HashDip;
|
||||
// confronto con elenco attivazioni dello shared service...
|
||||
var foundRec = rawActList.Where(x => x.CodImpiego == hashImpiego);
|
||||
answ = foundRec.Count() > 0;
|
||||
// salvo status payloadOk
|
||||
MService.PayloadOk = answ;
|
||||
}
|
||||
|
||||
await Task.Delay(1);
|
||||
return answ;
|
||||
}
|
||||
|
||||
protected async Task DoVerifyActiv()
|
||||
{
|
||||
MService.PayloadOk = false;
|
||||
await Task.Delay(1);
|
||||
await VerifyActiv();
|
||||
}
|
||||
|
||||
protected async Task ForceReset()
|
||||
{
|
||||
string nextPage = navManager.ToBaseRelativePath(navManager.Uri);
|
||||
// tengo solo parte finale...
|
||||
if (nextPage.Contains("/"))
|
||||
{
|
||||
nextPage = nextPage.Substring(nextPage.LastIndexOf("/") + 1);
|
||||
}
|
||||
await Task.Delay(1);
|
||||
await DoVerifyActiv();
|
||||
navManager.NavigateTo($"{baseUrl}ForceReset?nextPage={nextPage}", true);
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstrender)
|
||||
{
|
||||
await Task.Delay(1);
|
||||
if (firstrender)
|
||||
{
|
||||
if (!navManager.Uri.Contains("jumper"))
|
||||
{
|
||||
await checkUser();
|
||||
// verifico attivazione dipendente...
|
||||
await checkUserLicense();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
string baseUrl = configuration.GetValue<string>("OptConf:BaseUrl") ?? "~/";
|
||||
var versione = typeof(Program).Assembly.GetName().Version;
|
||||
currConfig = new ConfigDTO()
|
||||
{
|
||||
BaseUrl = baseUrl,
|
||||
Versione = $"{versione}"
|
||||
};
|
||||
|
||||
await ReloadData();
|
||||
|
||||
#if false
|
||||
idxDip = MService.IdxDipendente;
|
||||
MService.EA_DipUpdated += MService_EA_DipUpdated;
|
||||
#endif
|
||||
|
||||
base.OnInitialized();
|
||||
Log.Info($"MainLayout.OnInitialized | url: {baseUrl} | idxDip: {idxDip} | v:{versione}");
|
||||
}
|
||||
|
||||
protected async Task ReturnHome()
|
||||
{
|
||||
// svuoto cache
|
||||
await CDService.FlushRedisCache();
|
||||
// ritorno in home
|
||||
navManager.NavigateTo($"{currConfig.BaseUrl}Home", true);
|
||||
}
|
||||
|
||||
protected async Task VerifyActiv()
|
||||
{
|
||||
// preliminarmente verifica shared info
|
||||
await checkSharedInfo();
|
||||
// refresh attivazioni se necessario
|
||||
await checkActivations();
|
||||
// verifico e salvo dati attivazione dipendente...
|
||||
userOk = await checkUserLicense();
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private ConfigDTO currConfig = new ConfigDTO();
|
||||
|
||||
/// <summary>
|
||||
/// Info utente corrente x display
|
||||
/// </summary>
|
||||
private UserDTO? currUser = null;
|
||||
|
||||
private int idxDip = 0;
|
||||
|
||||
private string LocalNet = "10.74";
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private IHttpContextAccessor httpContextAccessor { get; set; } = null!;
|
||||
|
||||
private bool userOk { get; set; } = false;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task checkOtherData()
|
||||
{
|
||||
currIpv4 = await MService.getDevIpAsync();
|
||||
if (string.IsNullOrEmpty(currIpv4))
|
||||
{
|
||||
// ricalcolo e salvo...
|
||||
if (httpContextAccessor.HttpContext != null)
|
||||
{
|
||||
var remoteIp = $"{httpContextAccessor.HttpContext.Connection?.RemoteIpAddress}";
|
||||
// provo a recuperare ipV4...
|
||||
currIpv4 = IpUtils.getLocalIpv4(remoteIp);
|
||||
await MService.setDevIpv4Async(currIpv4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica dati utente e pagina
|
||||
/// </summary>
|
||||
private async Task checkUser()
|
||||
{
|
||||
dataLoaded = false;
|
||||
// se idxDip NON valido
|
||||
if (!DipOk || MService.IdxDipendente <= 0)
|
||||
{
|
||||
await tryDeviceLogin();
|
||||
// verifico se ho dipendente...
|
||||
if (currUser.IdxDip > 0 || MService.IdxDipendente > 0)
|
||||
{
|
||||
// verifico o calcolo IP...
|
||||
await checkOtherData();
|
||||
dataLoaded = true;
|
||||
// segnalo update...
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (idxDipendente <= 0)
|
||||
if (!DipOk)
|
||||
{
|
||||
await setupUserData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
var confNet = await CDService.ConfigGetKey("LocalNet");
|
||||
LocalNet = confNet != null ? confNet.valore : "10";
|
||||
}
|
||||
|
||||
private async Task setupUserData()
|
||||
{
|
||||
//if (MService.RigaDip != null && MService.IdxDipendente > 0)
|
||||
if (DipOk)
|
||||
{
|
||||
#if false
|
||||
idxDipendente = MService.IdxDipendente;
|
||||
Cognome = MService.RigaDip?.Cognome ?? "ND";
|
||||
Nome = MService.RigaDip?.Nome ?? "-";
|
||||
#endif
|
||||
|
||||
// verifico o calcolo IP...
|
||||
await checkOtherData();
|
||||
|
||||
// segnalo update...
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prova login da dati Device (in LocalStorage)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task tryDeviceLogin()
|
||||
{
|
||||
// cerco in localstorage il devicesecret
|
||||
string devSecret = await MService.getDevSecretAsync();
|
||||
await Task.Delay(50);
|
||||
if (!string.IsNullOrEmpty(devSecret))
|
||||
{
|
||||
// cerco sul DB...
|
||||
var rigaDev = await CDService.DeviceBySecret(devSecret);
|
||||
// se trovato
|
||||
if (rigaDev != null)
|
||||
{
|
||||
// recupero dati dip e inizializzo message service
|
||||
var elencoDip = await CDService.DipendentiGetAll();
|
||||
if (elencoDip != null)
|
||||
{
|
||||
var rigaDip = elencoDip.FirstOrDefault(x => x.IdxDipendente == rigaDev.IdxDipendente);
|
||||
if (rigaDip != null)
|
||||
{
|
||||
MService.RigaDip = rigaDip;
|
||||
currUser = new UserDTO()
|
||||
{
|
||||
IdxDip = rigaDip.IdxDipendente,
|
||||
Cognome = rigaDip.Cognome ?? "-",
|
||||
Nome = rigaDip.Nome ?? "...",
|
||||
Attivo = rigaDip.Attivo,
|
||||
AuthKey = rigaDip.AuthKey,
|
||||
Cf = rigaDip.Cf,
|
||||
DataNascita = rigaDip.DataNascita,
|
||||
Email = rigaDip.Email,
|
||||
Gruppo = rigaDip.Gruppo,
|
||||
Matricola = rigaDip.Matricola,
|
||||
Dominio = rigaDip.Dominio,
|
||||
Utente = rigaDip.Utente
|
||||
};
|
||||
await setupUserData();
|
||||
}
|
||||
#if false
|
||||
MService.RigaDip = rigaDip;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
navManager.NavigateTo($"{baseUrl}RegNewDevice");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
navManager.NavigateTo($"{baseUrl}RegNewDevice");
|
||||
}
|
||||
}
|
||||
|
||||
private bool dataLoaded = false;
|
||||
|
||||
|
||||
#endregion Private Methods
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
.page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: -webkit-linear-gradient(to bottom, #554e93, #1aafc8);
|
||||
/* Chrome 10-25, Safari 5.1-6 */
|
||||
background: linear-gradient(to bottom, #554e93, #1aafc8);
|
||||
/* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
|
||||
}
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
.sidebar {
|
||||
background-image: linear-gradient(180deg, #052767 0%, #3a0647 70%);
|
||||
}
|
||||
.top-row {
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #d6d5d5;
|
||||
justify-content: flex-end;
|
||||
height: 3.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.top-row ::deep a,
|
||||
.top-row .btn-link {
|
||||
white-space: nowrap;
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
.top-row a:first-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@media (max-width: 640.98px) {
|
||||
.top-row:not(.auth) {
|
||||
display: none;
|
||||
}
|
||||
.top-row.auth {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.top-row a,
|
||||
.top-row .btn-link {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
@media (min-width: 641px) {
|
||||
.page {
|
||||
flex-direction: row;
|
||||
}
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
height: 100vh;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
.top-row {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
.top-row,
|
||||
article {
|
||||
padding-left: 2rem !important;
|
||||
padding-right: 1.5rem !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
.page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: -webkit-linear-gradient(to bottom, #554e93, #1aafc8); /* Chrome 10-25, Safari 5.1-6 */
|
||||
background: linear-gradient(to bottom, #554e93, #1aafc8); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
||||
}
|
||||
|
||||
.top-row {
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #d6d5d5;
|
||||
justify-content: flex-end;
|
||||
height: 3.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row .btn-link {
|
||||
white-space: nowrap;
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.top-row a:first-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 640.98px) {
|
||||
.top-row:not(.auth) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.top-row.auth {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.top-row a, .top-row .btn-link {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.page {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
height: 100vh;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.top-row, article {
|
||||
padding-left: 2rem !important;
|
||||
padding-right: 1.5rem !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.page{position:relative;display:flex;flex-direction:column;background:-webkit-linear-gradient(to bottom,#554e93,#1aafc8);background:linear-gradient(to bottom,#554e93,#1aafc8);}main{flex:1;}.sidebar{background-image:linear-gradient(180deg,#052767 0%,#3a0647 70%);}.top-row{background-color:#f7f7f7;border-bottom:1px solid #d6d5d5;justify-content:flex-end;height:3.5rem;display:flex;align-items:center;}.top-row ::deep a,.top-row .btn-link{white-space:nowrap;margin-left:1.5rem;}.top-row a:first-child{overflow:hidden;text-overflow:ellipsis;}@media(max-width:640.98px){.top-row:not(.auth){display:none;}.top-row.auth{justify-content:space-between;}.top-row a,.top-row .btn-link{margin-left:0;}}@media(min-width:641px){.page{flex-direction:row;}.sidebar{width:250px;height:100vh;position:sticky;top:0;}.top-row{position:sticky;top:0;z-index:1;}.top-row,article{padding-left:2rem!important;padding-right:1.5rem!important;}}
|
||||
@@ -0,0 +1,77 @@
|
||||
@page "/About"
|
||||
|
||||
<div class="row mx-2">
|
||||
<div class="col-12 col-lg-10 offset-lg-1">
|
||||
<div class="card">
|
||||
<div class="card-header text-center">
|
||||
<h1>@Titolo</h1>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<h4>Headquarters</h4>
|
||||
<address>
|
||||
<strong>EgalWare s.r.l.</strong><br />
|
||||
via Nazionale, 93<br />
|
||||
24068 Seriate - BG<br />
|
||||
<abbr title="Phone"><i class="fa-solid fa-square-phone"></i></abbr>
|
||||
+39.035.290178<br />
|
||||
<abbr title="Phone"><i class="fa-solid fa-square-phone"></i></abbr>
|
||||
+39.035.460560
|
||||
</address>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<img src="images/LogoEgw.png" wi class="img-fluid" />
|
||||
</div>
|
||||
<hr />
|
||||
<div class="col-12">
|
||||
<h4 class="card-title">Version and license</h4>
|
||||
</div>
|
||||
<div class="col-6 small">
|
||||
<strong>GPW</strong><br />
|
||||
<div class="small">v.<b>@version</b></div>
|
||||
<div class="small">@framework</div>
|
||||
</div>
|
||||
<div class="col-6 small">
|
||||
<strong>License:</strong><br />
|
||||
<ul>
|
||||
<li>
|
||||
Scad: @licExpiry
|
||||
</li>
|
||||
<li>
|
||||
Num: @licStatus
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="col-12">
|
||||
<h4 class="card-title">@Messaggio</h4>
|
||||
</div>
|
||||
<div class="col-6 small">
|
||||
<address>
|
||||
<strong>Web:</strong><br />
|
||||
Access to support:<br />
|
||||
<a href="https://support.egalware.com/" target="_blank">our website</a><br />
|
||||
</address>
|
||||
|
||||
</div>
|
||||
<div class="col-6 small">
|
||||
<address>
|
||||
<strong>Email:</strong><br />
|
||||
Write an email:<br />
|
||||
<a href="mailto:info@egalware.com">info at egalware.com</a>
|
||||
</address>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-around">
|
||||
<div class="badge rounded-pill bg-dark px-1 py-0">
|
||||
<div class="px-1">
|
||||
<a class="btn text-light text-decoration-none" href="https://www.egalware.com/" target="_blank">powered by EgalWare <img width="24" class="img-fluid" src="images/LogoEgw.png" /></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using MailKit.Net.Imap;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Pages
|
||||
{
|
||||
public partial class About : IDisposable
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
protected string Messaggio = "";
|
||||
protected string Titolo = "";
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Methods
|
||||
protected string framework = "???";
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LicServ.RefreshApplic();
|
||||
Titolo = Configuration.GetValue<string>("Application:Name")!;
|
||||
Messaggio = Configuration.GetValue<string>("Application:ContactText")!;
|
||||
var tgtFrmwrkAtt = Assembly.GetExecutingAssembly()
|
||||
.GetCustomAttributes(typeof(TargetFrameworkAttribute), false)
|
||||
.FirstOrDefault();
|
||||
if (tgtFrmwrkAtt != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
framework = ((TargetFrameworkAttribute)tgtFrmwrkAtt).FrameworkName;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
private int numLicAvail
|
||||
{
|
||||
get
|
||||
{
|
||||
int answ = 0;
|
||||
if (LicServ.HasActivData)
|
||||
{
|
||||
var licRec = LicServ.AKVList.Where(x => x.NomeVar == LicServ.Installazione).FirstOrDefault();
|
||||
if (licRec != null)
|
||||
{
|
||||
answ = licRec.ValInt ?? 0;
|
||||
}
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
private string licStatus
|
||||
{
|
||||
get => LicServ.HasActivData ? $"{LicServ.ActivList.Count}/{numLicAvail}" : "N.A.";
|
||||
}
|
||||
private string licExpiry
|
||||
{
|
||||
get => LicServ.HasActivData ? $"{LicServ.AppLicense.Scadenza:yyyy-MM-dd}" : "N.A.";
|
||||
}
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private IConfiguration Configuration { get; set; } = null!;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
[Inject]
|
||||
protected LicenseService LicServ { get; set; } = null!;
|
||||
|
||||
protected Version? version = typeof(Program).Assembly.GetName().Version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@page "/Admin"
|
||||
|
||||
<AdminTask IdxDipendente="@idxDipendente"></AdminTask>
|
||||
@@ -0,0 +1,52 @@
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Pages
|
||||
{
|
||||
public partial class Admin : IDisposable
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
MService.EA_DipUpdated -= MService_EA_DipUpdated;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
MService.EA_DipUpdated += MService_EA_DipUpdated;
|
||||
idxDipendente = MService.IdxDipendente;
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Properties
|
||||
|
||||
/// <summary>
|
||||
/// IdxDipendente corrente
|
||||
/// </summary>
|
||||
private int idxDipendente { get; set; } = 0;
|
||||
|
||||
[Inject]
|
||||
private MessageService MService { get; set; } = null!;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async void MService_EA_DipUpdated()
|
||||
{
|
||||
await Task.Delay(1);
|
||||
idxDipendente = MService.IdxDipendente;
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
@page "/DayOff"
|
||||
@using EgwCoreLib.Razor
|
||||
@using GPW.CORE.Smart8.Components.Compo
|
||||
|
||||
<div class="card mb-5">
|
||||
<div class="card-header table-primary">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-between">
|
||||
@if (showFiltEv)
|
||||
{
|
||||
<div class="px-2">
|
||||
Mostra:
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-sm @btnSel("PER")" @onclick="toggPer">Permessi</button>
|
||||
<button type="button" class="btn btn-sm @btnSel("FER")" @onclick="toggFer">Ferie</button>
|
||||
<button type="button" class="btn btn-sm @btnSel("MAL")" @onclick="toggMal">Malattie</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-2">
|
||||
<button class="btn btn-sm btn-warning" @onclick="toggleFiltEv"><i class="fa-solid fa-gear"></i></button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="px-2">
|
||||
<b>Gestione Permessi, Ferie e Malattie</b>
|
||||
</div>
|
||||
<div class="px-2">
|
||||
<button class="btn btn-sm btn-primary" @onclick="toggleFiltEv"><i class="fa-solid fa-gear"></i></button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<Toggler SelFilter="@ToggleShow" FilterChanged="evTogShow"></Toggler>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-1">
|
||||
@if (isLoading)
|
||||
{
|
||||
<LoadingData></LoadingData>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@if (!showDetail)
|
||||
{
|
||||
<CalendarMonth DtRif="@DtReq" MainCss="table table-dark table-borderless" DateSelected="DisplayDate" MonthChanged="ReloadMonth" DateCheckBlock="@DateCFF" EventList="@eventList" SingleWeek="@showDetail" SelDayCss="selDate rounded-top p-2" HeadStyle="color: #E67E22"></CalendarMonth>
|
||||
}
|
||||
else
|
||||
{
|
||||
<CalendarWeek DateList="@ListDate" WeekChanged="ChangePeriod" backToCalendar="backToCal" EC_HeadDateSel="SetDayDetail" EventList="@EventListWeek" PercY="0" PercX="150"></CalendarWeek>
|
||||
}
|
||||
</div>
|
||||
@if (showRichieste)
|
||||
{
|
||||
<div class="col-12 small mt-2 mb-0">
|
||||
<Toggler SelFilter="@ToggleTipo" FilterChanged="evTogTipo"></Toggler>
|
||||
@if (showMalattie)
|
||||
{
|
||||
<RegMalattia IdxDipendente="@idxDipendente" isLoading="@isLoading" ReportUpdate="()=>ForceReloadCal()"></RegMalattia>
|
||||
}
|
||||
else
|
||||
{
|
||||
<RegRichieste IdxDipendente="@idxDipendente" isLoading="@isLoading" ReportUpdate="()=>ForceReloadCal()" months="@numMesi"></RegRichieste>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
using EgwCoreLib.Razor;
|
||||
using EgwCoreLib.Razor.Data;
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.CodeAnalysis.CodeFixes;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Pages
|
||||
{
|
||||
public partial class DayOff : IDisposable
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
public CalendarModeEnum.modoControllo currMode { get; set; } = CalendarModeEnum.modoControllo.showCalendar;
|
||||
public int MoveBlock { get; set; } = 7;
|
||||
public int NumDays { get; set; } = 5;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ListDate = new List<DateTime>();
|
||||
DateCFF = new Dictionary<DateTime, string>();
|
||||
eventList = new List<CalendarEvent>();
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
protected int anno = DateTime.Today.Year;
|
||||
protected DateTime dtMax = DateTime.Today.AddMonths(3);
|
||||
protected DateTime dtMin = DateTime.Today;
|
||||
protected List<DateTime> ListDate = new List<DateTime>();
|
||||
protected int numDays = 5;
|
||||
protected int numMesi = 3;
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
[Inject]
|
||||
protected MessageService AppMServ { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
protected DateTime DtReq { get; set; } = DateTime.Today;
|
||||
protected List<CalendarEvent> EventListWeek { get; set; } = new List<CalendarEvent>();
|
||||
|
||||
protected int idxDipendente
|
||||
{
|
||||
get => AppMServ.IdxDipendente;
|
||||
}
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected async Task backToCal(bool isMode)
|
||||
{
|
||||
showDetail = false;
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
protected string btnSel(string bName)
|
||||
{
|
||||
bool answ = false;
|
||||
switch (bName)
|
||||
{
|
||||
case "FER":
|
||||
answ = showFer;
|
||||
break;
|
||||
|
||||
case "MAL":
|
||||
answ = showMal;
|
||||
break;
|
||||
|
||||
case "PER":
|
||||
answ = showPer;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return answ ? "btn-primary" : "btn-secondary";
|
||||
}
|
||||
|
||||
protected async Task ChangePeriod(int numStep)
|
||||
{
|
||||
ListDate = new List<DateTime>();
|
||||
await Task.Delay(1);
|
||||
DtReq = DtReq.AddDays(MoveBlock * numStep);
|
||||
ReloadWeekData();
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
initToggler();
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
protected void SetDayDetail(DateTime selDate)
|
||||
{
|
||||
var tmpDays = ListDate.Where(x => x.Equals(selDate)).ToList();
|
||||
var tmpEv = EventListWeek.Where(x => x.Inizio.Date.Equals(selDate)).ToList();
|
||||
// salvo valori simulati
|
||||
ListDate = tmpDays;
|
||||
EventListWeek = tmpEv;
|
||||
}
|
||||
|
||||
protected async Task toggFer()
|
||||
{
|
||||
showFer = !showFer;
|
||||
await ReloadData();
|
||||
ReloadWeekData();
|
||||
}
|
||||
|
||||
protected void toggleFiltEv()
|
||||
{
|
||||
showFiltEv = !showFiltEv;
|
||||
}
|
||||
|
||||
protected async Task toggMal()
|
||||
{
|
||||
showMal = !showMal;
|
||||
await ReloadData();
|
||||
ReloadWeekData();
|
||||
}
|
||||
|
||||
protected async Task toggPer()
|
||||
{
|
||||
showPer = !showPer;
|
||||
await ReloadData();
|
||||
ReloadWeekData();
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private Dictionary<DateTime, string> DateCFF = new Dictionary<DateTime, string>();
|
||||
private Dictionary<DateTime, string> DateRDip = new Dictionary<DateTime, string>();
|
||||
private List<CalendarEvent> eventList = new List<CalendarEvent>();
|
||||
private bool isLoading = false;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
private List<CalFesteFerieModel> ListFermateAzienda { get; set; } = new List<CalFesteFerieModel>();
|
||||
|
||||
private List<RegMalattieModel> ListMalattie { get; set; } = new List<RegMalattieModel>();
|
||||
|
||||
private List<RegRichiesteModel> ListRichiesteDip { get; set; } = new List<RegRichiesteModel>();
|
||||
|
||||
[Inject]
|
||||
private MessageService MService { get; set; } = null!;
|
||||
|
||||
private bool showDetail { get; set; } = false;
|
||||
|
||||
private bool showFer { get; set; } = true;
|
||||
private bool showFiltEv { get; set; } = false;
|
||||
private bool showMal { get; set; } = true;
|
||||
private bool showMalattie { get; set; } = false;
|
||||
|
||||
private bool showPer { get; set; } = true;
|
||||
private bool showRichieste { get; set; } = false;
|
||||
|
||||
private Toggler.SelectGlobalToggle ToggleShow { get; set; } = new Toggler.SelectGlobalToggle();
|
||||
|
||||
private Toggler.SelectGlobalToggle ToggleTipo { get; set; } = new Toggler.SelectGlobalToggle();
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private string colorByGiustStato(string codGiust, bool conf)
|
||||
{
|
||||
string answ = "#EDEDED";
|
||||
switch (codGiust)
|
||||
{
|
||||
case "PERM":
|
||||
answ = conf ? "#9966DE" : "#CDAAFF";
|
||||
break;
|
||||
|
||||
case "FER":
|
||||
answ = conf ? "#11CD44" : "#AAFFCD";
|
||||
break;
|
||||
|
||||
case "104":
|
||||
answ = conf ? "#DE00AB" : "#FFAACD";
|
||||
break;
|
||||
|
||||
case "MAL":
|
||||
answ = conf ? "#000" : "#696969";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
private string colorTextByGiustStato(string codGiust, bool conf)
|
||||
{
|
||||
string answ = "#EDEDED";
|
||||
switch (codGiust)
|
||||
{
|
||||
case "PERM":
|
||||
answ = conf ? "#DEDEDE" : "#000000";
|
||||
break;
|
||||
|
||||
case "FER":
|
||||
answ = conf ? "#FFFFFF" : "#000000";
|
||||
break;
|
||||
|
||||
case "104":
|
||||
answ = conf ? "#DEDEDE" : "#000000";
|
||||
break;
|
||||
|
||||
case "MAL":
|
||||
answ = conf ? "#DEDEDE" : "#ABABAB";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
private void DisplayDate(DateTime dtSel)
|
||||
{
|
||||
DtReq = dtSel;
|
||||
ReloadWeekData();
|
||||
showDetail = true;
|
||||
MService.ReportDateChange();
|
||||
}
|
||||
|
||||
private async Task evTogShow(Toggler.SelectGlobalToggle newTogData)
|
||||
{
|
||||
ToggleShow = newTogData;
|
||||
showRichieste = !ToggleShow.isActive;
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
private async Task evTogTipo(Toggler.SelectGlobalToggle newTogData)
|
||||
{
|
||||
ToggleTipo = newTogData;
|
||||
showMalattie = !ToggleTipo.isActive;
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
private async Task ForceReloadCal()
|
||||
{
|
||||
await Task.Delay(1);
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
private void initToggler()
|
||||
{
|
||||
ToggleTipo = new Toggler.SelectGlobalToggle()
|
||||
{
|
||||
leftString = "Malattie",
|
||||
rightString = "Ferie e Permessi",
|
||||
placardCss = "bg-light border-light text-dark",
|
||||
};
|
||||
ToggleShow = new Toggler.SelectGlobalToggle()
|
||||
{
|
||||
leftString = "Mostra Richieste",
|
||||
rightString = "Solo Calendario",
|
||||
placardCss = "bg-light border-light text-dark",
|
||||
};
|
||||
}
|
||||
|
||||
private async void MService_EA_DipUpdated()
|
||||
{
|
||||
if (idxDipendente > 0)
|
||||
{
|
||||
await ReloadData();
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
isLoading = true;
|
||||
int iCnt = 0;
|
||||
DateCFF = new Dictionary<DateTime, string>();
|
||||
eventList = new List<CalendarEvent>();
|
||||
DateTime oggi = DateTime.Today;
|
||||
// prendo mese prec x partenza
|
||||
dtMin = new DateTime(DtReq.Year, DtReq.Month, 1).AddMonths(-1);
|
||||
// prendo sempre 3 mesi (prec, corrente, successivo)
|
||||
dtMax = dtMin.AddMonths(numMesi);
|
||||
|
||||
// dichiarazione codici CSS di base
|
||||
string cssFest = "bg-danger text-warning rounded-pill ";
|
||||
string cssChius = "bg-warning text-dark rounded-pill ";
|
||||
string cssRichFer = "bg-warning text-dark rounded-circle p-2";
|
||||
string cssRichPerm = "bg-info text-light rounded-circle p-2";
|
||||
string cssMal = "bg-dark text-light rounded-circle p-2";
|
||||
string cssTd = "bg-info text-dark rounded-pill";
|
||||
|
||||
DateCFF.Add(DateTime.Today, cssTd);
|
||||
// recupero le fermate aziendali
|
||||
ListFermateAzienda = await CDService.CalFestFeriePeriodo(dtMin, dtMax);
|
||||
if (ListFermateAzienda.Count > 0)
|
||||
{
|
||||
string cssSpec = "";
|
||||
foreach (var item in ListFermateAzienda)
|
||||
{
|
||||
cssSpec = item.codGiust == "FEST" ? cssFest : cssChius;
|
||||
if (!DateCFF.ContainsKey(item.data))
|
||||
{
|
||||
if (item.data.DayOfWeek != DayOfWeek.Saturday && item.data.DayOfWeek != DayOfWeek.Sunday)
|
||||
{
|
||||
DateCFF.Add(item.data, cssSpec);
|
||||
}
|
||||
}
|
||||
|
||||
eventList.Add(new CalendarEvent()
|
||||
{
|
||||
id = iCnt++,
|
||||
Inizio = item.data.Date.AddHours(9),
|
||||
Fine = item.data.Date.AddHours(19),
|
||||
Type = item.codGiust,
|
||||
Description = item.descrizione,
|
||||
CssClass = item.codGiust == "FEST" ? "blockFest" : "blockChius"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// recupero e converto le richieste dipendente... RAGGUPPANDOLE
|
||||
ListRichiesteDip = await CDService.RegRichiesteGetByDip(0, dtMin, dtMax);
|
||||
if (ListRichiesteDip.Count > 0)
|
||||
{
|
||||
string cssSpec = "";
|
||||
DateTime dtIns = DateTime.Today;
|
||||
foreach (var item in ListRichiesteDip)
|
||||
{
|
||||
// verifico se abilitato per tipo
|
||||
if ((item.CodGiust == "FER" && showFer) || (showPer && item.CodGiust != "FER"))
|
||||
{
|
||||
switch (item.CodGiust)
|
||||
{
|
||||
case "PERM":
|
||||
case "104":
|
||||
cssSpec = cssRichPerm;
|
||||
break;
|
||||
|
||||
case "FER":
|
||||
cssSpec = cssRichFer;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// aggiungo x tutte le date richieste...
|
||||
int numGG = item.DtEnd.Subtract(item.DtStart).Days + 1;
|
||||
for (int i = 0; i < numGG; i++)
|
||||
{
|
||||
dtIns = item.DtStart.Date.AddDays(i);
|
||||
if (!DateRDip.ContainsKey(dtIns))
|
||||
{
|
||||
DateRDip.Add(dtIns, cssSpec);
|
||||
}
|
||||
}
|
||||
string css = "";
|
||||
DateTime dtFine = item.DtEnd;
|
||||
if (item.CodGiust == "PERM")
|
||||
{
|
||||
css = "blockPerm";
|
||||
}
|
||||
else if (item.CodGiust == "104")
|
||||
{
|
||||
css = "block104";
|
||||
}
|
||||
else if (item.CodGiust == "FER")
|
||||
{
|
||||
css = "blockFerie";
|
||||
}
|
||||
|
||||
if (item.CodGiust == "FER")
|
||||
{
|
||||
for (int i = 0; i < numGG; i++)
|
||||
{
|
||||
var dtEvInizio = item.DtStart.AddDays(i).AddHours(9);
|
||||
var dtEvFine = item.DtStart.AddDays(i).AddHours(19);
|
||||
var evDescr = item.DipNav.Abbrev;// $"{item.DipNav.Nome.Substring(0, 1)}. {item.DipNav.Cognome}";
|
||||
// 2024.08.27 cerco se esistesse già...
|
||||
var prevRec = eventList
|
||||
.Where(x => x.Inizio == dtEvInizio && x.Fine == dtEvFine && x.Type == item.CodGiust)
|
||||
.FirstOrDefault();
|
||||
// se manca aggiungo
|
||||
if (prevRec == null)
|
||||
{
|
||||
eventList.Add(new CalendarEvent()
|
||||
{
|
||||
id = iCnt++,
|
||||
Inizio = item.DtStart.AddDays(i).AddHours(9),
|
||||
Fine = item.DtStart.AddDays(i).AddHours(19),
|
||||
Description = evDescr,
|
||||
Type = item.CodGiust,
|
||||
CssClass = css
|
||||
});
|
||||
}
|
||||
// altrimenti accodo....
|
||||
else
|
||||
{
|
||||
prevRec.Description += $", {evDescr}";
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var evDescr = item.DipNav.Abbrev;
|
||||
eventList.Add(new CalendarEvent()
|
||||
{
|
||||
id = iCnt++,
|
||||
Inizio = item.DtStart.Hour > 0 ? item.DtStart : item.DtStart.AddHours(9),
|
||||
Fine = item.DtEnd.Hour > 0 ? item.DtEnd : item.DtStart.AddHours(19),
|
||||
Type = item.CodGiust,
|
||||
Description = item.DipNav.Abbrev,
|
||||
CssClass = css
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (showMal)
|
||||
{
|
||||
// recupero e converto le malattie...
|
||||
ListMalattie = await CDService.RegMalattieGetAll(100);
|
||||
if (ListMalattie.Count > 0)
|
||||
{
|
||||
string cssSpec = cssMal;
|
||||
DateTime dtIns = DateTime.Today;
|
||||
foreach (var item in ListMalattie)
|
||||
{
|
||||
// aggiungo x tutte le date richieste...
|
||||
for (int i = 0; i < item.NumGG; i++)
|
||||
{
|
||||
dtIns = item.DtInizio.Date.AddDays(i - 1);
|
||||
if (!DateRDip.ContainsKey(dtIns))
|
||||
{
|
||||
DateRDip.Add(dtIns, cssSpec);
|
||||
}
|
||||
}
|
||||
// esplosione malattie...
|
||||
for (int i = 0; i < item.NumGG; i++)
|
||||
{
|
||||
eventList.Add(new CalendarEvent()
|
||||
{
|
||||
id = iCnt++,
|
||||
Inizio = item.DtInizio.AddDays(i).AddHours(9),
|
||||
Fine = item.DtInizio.AddDays(i).AddHours(19),
|
||||
Title = "MAL",
|
||||
Description = $"{item.DipNav.Nome.Substring(0, 1)}. {item.DipNav.Cognome}",
|
||||
Type = "MAL",
|
||||
ToolTip = "MAL",
|
||||
CssClass = "blockMal"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
isLoading = false;
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
private async Task ReloadMonth(DateTime dtSel)
|
||||
{
|
||||
DtReq = dtSel;
|
||||
MService.targetDate = dtSel;
|
||||
showDetail = false;
|
||||
await ReloadData();
|
||||
await Task.Delay(1);
|
||||
MService.ReportDateChange();
|
||||
}
|
||||
|
||||
private void ReloadWeekData()
|
||||
{
|
||||
ListDate = new List<DateTime>();
|
||||
EventListWeek = new List<CalendarEvent>();
|
||||
int nWeek = DtReq.DayOfWeek - DayOfWeek.Monday;
|
||||
if (NumDays < 5 || NumDays > 7)
|
||||
{
|
||||
nWeek = 0;
|
||||
}
|
||||
for (int i = 0; i < NumDays; i++)
|
||||
{
|
||||
ListDate.Add(DtReq.AddDays(i - nWeek));
|
||||
}
|
||||
// ciclo giorno x giorno e controllo se devo "ritardare" x visualizzare...
|
||||
EventListWeek = eventList.Where(x => ListDate.Contains(x.Inizio.Date)).ToList();
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@RequestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
|
||||
@code{
|
||||
[CascadingParameter]
|
||||
private HttpContext? HttpContext { get; set; }
|
||||
|
||||
private string? RequestId { get; set; }
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
|
||||
protected override void OnInitialized() =>
|
||||
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
@page "/ForceReset"
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Data Reset
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h1>@message</h1>
|
||||
<LoadingData></LoadingData>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,78 @@
|
||||
using EgwCoreLib.Razor.Data;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Pages
|
||||
{
|
||||
public partial class ForceReset : IDisposable
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
protected string message = "Resetting Cache!";
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected string baseUrl
|
||||
{
|
||||
get => configuration.GetValue<string>("OptConf:BaseUrl") ?? "~/";
|
||||
}
|
||||
|
||||
[Inject]
|
||||
protected CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected IConfiguration configuration { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected MessageService MService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected NavigationManager NavManager { get; set; } = null!;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await doReset();
|
||||
}
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await doReset();
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async Task doReset()
|
||||
{
|
||||
await CDService.FlushRedisCache();
|
||||
MService.clonedRA = null;
|
||||
MService.recordRA = null;
|
||||
message = "Reset done!";
|
||||
// attendo 5 msec
|
||||
await Task.Delay(5);
|
||||
string nextPage = QHelper.GetQueryParm(NavManager.Uri, "nextPage");
|
||||
// se ho una next page rimando li...
|
||||
string tgtUrl = nextPage ?? "Home";
|
||||
// passo a pagina home
|
||||
NavManager.NavigateTo($"{baseUrl}{tgtUrl}");
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
@page "/"
|
||||
@page "/Home"
|
||||
@page "/Index"
|
||||
|
||||
<CompTimbra IdxDipCurr="@idxDipendente" @rendermode="InteractiveServer"></CompTimbra>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Pages
|
||||
{
|
||||
public partial class Home : IDisposable
|
||||
{
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
MService.EA_DipUpdated -= MService_EA_DipUpdated;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
MService.EA_DipUpdated += MService_EA_DipUpdated;
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Properties
|
||||
|
||||
/// <summary>
|
||||
/// IdxDipCurr
|
||||
/// </summary>
|
||||
private int idxDipendente { get; set; } = 0;
|
||||
|
||||
[Inject]
|
||||
private Data.MessageService MService { get; set; } = null!;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private async void MService_EA_DipUpdated()
|
||||
{
|
||||
await ReloadData();
|
||||
}
|
||||
|
||||
private async Task ReloadData()
|
||||
{
|
||||
idxDipendente = MService.IdxDipendente;
|
||||
await Task.Delay(1);
|
||||
if (idxDipendente > 0)
|
||||
{
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
@page "/Jumper"
|
||||
|
||||
<h3>Logging in...</h3>
|
||||
|
||||
|
||||
<LoadingData></LoadingData>
|
||||
@@ -0,0 +1,184 @@
|
||||
using EgwCoreLib.Razor.Data;
|
||||
using EgwCoreLib.Utils;
|
||||
using GPW.CORE.Data.DbModels;
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.JSInterop;
|
||||
using NLog;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Pages
|
||||
{
|
||||
public partial class Jumper
|
||||
{
|
||||
#region Public Classes
|
||||
|
||||
public class WindowDimension
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
public int Height { get; set; }
|
||||
public int Width { get; set; }
|
||||
|
||||
#endregion Public Properties
|
||||
}
|
||||
|
||||
#endregion Public Classes
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
protected DipendentiModel? currDip;
|
||||
|
||||
protected List<DipendentiModel>? currDipList;
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected string authKey { get; set; } = "";
|
||||
|
||||
protected int idxDipendente = 0;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
await Task.Delay(50);
|
||||
if (firstRender)
|
||||
{
|
||||
await tryUrlLogin();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private Random rnd = new Random();
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Properties
|
||||
|
||||
[Inject]
|
||||
private CoreSmartDataService CDService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IHttpContextAccessor httpContextAccessor { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IJSRuntime JSRuntime { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private MessageService MService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private NavigationManager navManager { get; set; } = null!;
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Prova login da dati URL
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task tryUrlLogin()
|
||||
{
|
||||
var uri = navManager.ToAbsoluteUri(navManager.Uri);
|
||||
await Task.Delay(1);
|
||||
// resetto valori in localStorage...
|
||||
string devSec = "";
|
||||
string devIp = "";
|
||||
authKey = "";
|
||||
await MService.setDevSecretAsync(devSec);
|
||||
await MService.setDevIpv4Async(devIp);
|
||||
// recupero da URL
|
||||
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("idxDipendente", out var _idxDipendente))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_idxDipendente))
|
||||
{
|
||||
idxDipendente = int.Parse($"{_idxDipendente}");
|
||||
}
|
||||
}
|
||||
if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("authKey", out var _authKey))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_authKey))
|
||||
{
|
||||
authKey = $"{_authKey}";
|
||||
}
|
||||
}
|
||||
else if (QueryHelpers.ParseQuery(uri.Query).TryGetValue("UserAuthKey", out var _userAuthKey))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_userAuthKey))
|
||||
{
|
||||
authKey = $"{_userAuthKey}";
|
||||
}
|
||||
}
|
||||
DateTime adesso = DateTime.Now;
|
||||
string dtData = $"{DateTime.UtcNow:yyyy-MM-dd_ZHH:mm:ss}";
|
||||
string devAgent = await JSRuntime.InvokeAsync<string>("getUserAgent");
|
||||
var dimension = await JSRuntime.InvokeAsync<WindowDimension>("getWindowDimensions");
|
||||
string screenSize = $"{dimension.Width}x{dimension.Height}";
|
||||
if (httpContextAccessor.HttpContext != null)
|
||||
{
|
||||
var remoteIp = $"{httpContextAccessor.HttpContext.Connection?.RemoteIpAddress}";
|
||||
// provo a recuperare ipV4...
|
||||
devIp = IpUtils.getLocalIpv4(remoteIp);
|
||||
}
|
||||
string rndTxt01 = SteamCrypto.RandomString(16, true);
|
||||
string rndTxt02 = SteamCrypto.RandomString(16, true);
|
||||
currDipList = await CDService.DipendentiGetAll();
|
||||
currDip = currDipList.Where(x => x.IdxDipendente == idxDipendente && x.AuthKey == authKey).FirstOrDefault();
|
||||
if (currDip != null)
|
||||
{
|
||||
// verifico authKey...
|
||||
if (currDip.AuthKey == authKey)
|
||||
{
|
||||
// creo la DeviceSecret
|
||||
devSec = CDService.EncriptData($"{rndTxt01}|{dtData}|{idxDipendente}|{devIp}|{rndTxt02}");
|
||||
// ...e la salvo sul DB
|
||||
AnagDeviceModel newDev = new AnagDeviceModel()
|
||||
{
|
||||
IdxDipendente = idxDipendente,
|
||||
DeviceSecret = devSec,
|
||||
DataOraEnabled = adesso,
|
||||
DataOraLastSeen = adesso,
|
||||
ScreenSize = screenSize,
|
||||
DeviceName = devIp,
|
||||
Description = devAgent,
|
||||
LastIpv4 = devIp
|
||||
};
|
||||
bool fatto = await CDService.DeviceInsert(newDev);
|
||||
if (fatto)
|
||||
{
|
||||
// ...e nel browser tramite message service
|
||||
await MService.setDevSecretAsync(devSec);
|
||||
await MService.setDevIpv4Async(devIp);
|
||||
// infine salvo dati dipendente e rimando a login
|
||||
MService.RigaDip = currDip;
|
||||
navManager.NavigateTo($"{baseUrl}Home", true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
navManager.NavigateTo($"{baseUrl}RegNewDevice");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected string baseUrl
|
||||
{
|
||||
get => configuration.GetValue<string>("OptConf:BaseUrl") ?? "~/";
|
||||
}
|
||||
|
||||
[Inject]
|
||||
protected IConfiguration configuration { get; set; } = null!;
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
@page "/LogOut"
|
||||
|
||||
<h3>LogOut Page</h3>
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using GPW.CORE.Smart8.Data;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace GPW.CORE.Smart8.Components.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper page x eseguire logout utente...
|
||||
/// </summary>
|
||||
public partial class LogOut
|
||||
{
|
||||
#region Protected Properties
|
||||
|
||||
[Inject]
|
||||
protected MessageService MService { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
protected NavigationManager navManager { get; set; } = null!;
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
// salvo in messageservice valori nulli...
|
||||
await MService.setDevSecretAsync("");
|
||||
MService.RigaDip = new CORE.Data.DbModels.DipendentiModel();
|
||||
// aspetto
|
||||
await Task.Delay(200);
|
||||
// rimando a home
|
||||
navManager.NavigateTo("RegNewDevice");
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
@page "/RegNewDevice"
|
||||
|
||||
@attribute [StreamRendering(false)]
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<div class="accordion" id="accordionExample">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="headingThree">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseZero" aria-expanded="false" aria-controls="collapseZero">
|
||||
<b>Registrazione Dispositivo</b>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseZero" class="accordion-collapse collapse show" aria-labelledby="headingZero" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body small">
|
||||
Per registrare un nuovo dispositivo si deve partire dal <b>link ricevuto via email</b> o dal <b>codice QR</b> fornito che comprende le chaivi di autorizzazione ed esegue gli step in modo automatico.
|
||||
<br />
|
||||
In alternativa vedere le procedure seguenti per resettare o rieseguire la procedura.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="headingOne">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
|
||||
<b>Recupero tramite e-mail</b>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<InvioLinkEmail></InvioLinkEmail>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading02">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse02" aria-expanded="false" aria-controls="collapse02">
|
||||
<b>Fix pagine offline</b>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse02" class="accordion-collapse collapse" aria-labelledby="heading02" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body small">
|
||||
Nel caso il browser stia presentando pagine offline/archiviate come valide, traendo in inganno sulle effettive timbrature, si deve procedere con la pulizia della cache delle pagine offline.
|
||||
<br />
|
||||
Per farlo si deve seguire la procedura manuale di seguito riportata:
|
||||
<ul>
|
||||
<li>Aprire una nuova pagina dal browser chrome sul device</li>
|
||||
<li>inserire l'indirizzo seguente:<br /><b>chrome://offline-internals/</b></li>
|
||||
<li>eliminare almeno le pagine di<br /> <b>office.egalware.com</b></li>
|
||||
</ul>
|
||||
Purtroppo non è possibile automatizzare questa procedura ne aprire direttamente il link con un pulsante o una procedura poiché questo è vietato per motivi di sicurezza interna dai browser.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace GPW.CORE.Smart8.Components.Pages
|
||||
{
|
||||
public partial class RegNewDevice
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
@page "/Scratch"
|
||||
|
||||
<TestWeekCal NumDays="@numDays" DtReq="@DtReq"></TestWeekCal>
|
||||
|
||||
<div class="input-group text-end">
|
||||
<input type="number" class="form-control" placeholder="numero giorni da mostrare" @bind="@numDays">
|
||||
<span class="input-group-text">giorni</span>
|
||||
</div>
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace GPW.CORE.Smart8.Components.Pages
|
||||
{
|
||||
public partial class Scratch
|
||||
{
|
||||
#region Protected Fields
|
||||
|
||||
protected int numDays = 5;
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
protected DateTime DtReq { get; set; } = DateTime.Today;
|
||||
|
||||
#endregion Protected Properties
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Client._Imports).Assembly }">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
||||
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
||||
</Found>
|
||||
</Router>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user