Creazione progetto Blazor 8 wes (server + WASM) x SMART, da completare...

This commit is contained in:
Samuele Locatelli
2025-03-21 18:52:42 +01:00
parent a3e3c14ee7
commit f14f92d73f
364 changed files with 159874 additions and 5 deletions
+3
View File
@@ -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>
-2
View File
@@ -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({
+43
View File
@@ -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++;
}
}
+8
View File
@@ -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();
+9
View File
@@ -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!");
}
}
}
});
})();
+68
View File
@@ -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>&nbsp;&nbsp;ENTRATA</button>
<button class="mancTimb" @onclick="() => doRichiesta(false)">USCITA&nbsp;&nbsp;<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>&nbsp;&nbsp;ENTRATA</button>
<button class="uscitaDisabled" disabled>USCITA&nbsp;&nbsp;<i class="fa-solid fa-right-from-bracket"></i></button>
}
else
{
<button class="entrataDisabled" disabled><i class="fa-solid fa-right-to-bracket"></i>&nbsp;&nbsp;ENTRATA</button>
<button class="uscita" @onclick="() => registraTimbratura(false)">USCITA&nbsp;&nbsp;<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);
}
+1
View File
@@ -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);
}
}
+1
View File
@@ -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&nbsp;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
}
}
+6
View File
@@ -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