79 Commits

Author SHA1 Message Date
Samuele Locatelli 52a52b3993 Merge tag 'FixUserPwd01' into develop
Fix stringhe connessione con utente dedicato
2024-08-19 10:48:38 +02:00
Samuele Locatelli 6f243eb188 Merge branch 'release/FixUserPwd01' 2024-08-19 10:47:36 +02:00
Samuele Locatelli 28a7c403d9 Merge branch 'net8.0' into develop 2024-08-19 10:44:44 +02:00
Samuele Locatelli f4f2b7c857 Merge branch 'net8.0' of https://gitlab.steamware.net/steamware/gpw_next into net8.0 2024-08-19 10:44:34 +02:00
Samuele Locatelli 3d6e818b8d Update connString user 2024-08-19 10:44:30 +02:00
Samuele E. Locatelli ec48ab08b0 Aggiunto servizio server clock (NON ok) 2024-08-15 15:36:14 +02:00
Samuele Locatelli d5a2406704 update x cache parametri su prerendering 2024-08-09 18:43:57 +02:00
Samuele Locatelli 2044515ce4 Bozza soluzione hybrid dotnet 8.0 2024-08-09 18:10:41 +02:00
Samuele Locatelli 3000a43330 Merge tag 'FixReloadHome01' into develop
Update gestione reset rilettura pagina
2024-08-07 16:23:51 +02:00
Samuele Locatelli 61fafe9236 Merge branch 'release/FixReloadHome01' 2024-08-07 16:23:23 +02:00
Samuele Locatelli 3879bcc6f3 Reorg codice 2024-08-07 16:22:39 +02:00
Samuele Locatelli b678109ec0 Update reload home 2024-08-07 16:16:53 +02:00
Samuele Locatelli 67a3f582bf Merge tag 'FixEntrataStep01' into develop
Fix gestione arrotondamento entrata in worklog
2024-08-05 19:29:43 +02:00
Samuele Locatelli 0d08c57112 Merge branch 'release/FixEntrataStep01' 2024-08-05 19:29:08 +02:00
Samuele Locatelli a62d1688b1 Fix calcolo e display giornata cona rrotondamento ingresso 2024-08-05 19:28:17 +02:00
Samuele Locatelli d3ef9ce45f Merge tag 'AddRobotCacheVeto06' into develop
Fix commenti in RegNewDevice
2024-07-23 12:08:21 +02:00
Samuele Locatelli 46f3146d85 Merge branch 'release/AddRobotCacheVeto06' 2024-07-23 12:08:14 +02:00
Samuele Locatelli 22c9e1186c Fix commenti anche in regNewDevice 2024-07-23 12:08:02 +02:00
Samuele Locatelli e60dc2e172 Merge tag 'AddRobotCacheVeto05' into develop
Fix typo commento reset pagine offline
2024-07-23 11:55:07 +02:00
Samuele Locatelli 14bcf025e0 Merge branch 'release/AddRobotCacheVeto05' 2024-07-23 11:54:57 +02:00
Samuele Locatelli 0713f3d52d Typo commento 2024-07-23 11:54:39 +02:00
Samuele Locatelli 10ce6709dc Merge tag 'AddRobotCacheVeto04' into develop
Aggiunto link istruzioni x fix pagine offline
2024-07-23 11:41:59 +02:00
Samuele Locatelli b415cb6b53 Merge branch 'release/AddRobotCacheVeto04' 2024-07-23 11:41:43 +02:00
Samuele Locatelli 1ccfd55a1c Aggiunta nota x fix gestione pagine offline 2024-07-23 11:41:27 +02:00
Samuele Locatelli bcea43806b Merge tag 'AddRobotCacheVeto03' into develop
Fix typo nocache e levato overlay che non funziona
2024-07-23 11:11:43 +02:00
Samuele Locatelli e1de4d2bde Merge branch 'release/AddRobotCacheVeto03' 2024-07-23 11:11:31 +02:00
Samuele Locatelli 5f7ce2ee5c Refresh meta nocache 2024-07-23 11:11:20 +02:00
Samuele Locatelli 1b1855c8f4 Commentato overlay 2024-07-23 11:10:47 +02:00
Samuele Locatelli 0aafcc6770 Merge tag 'FixSmartPwd01' into develop
Fix smart pwd x invio reset link
2024-07-23 11:06:14 +02:00
Samuele Locatelli f947f98e39 Merge branch 'release/FixSmartPwd01' 2024-07-23 11:06:04 +02:00
Samuele Locatelli 3ba140305e Fix recupero email x reset 2024-07-23 11:05:26 +02:00
Samuele Locatelli dcae718f75 Merge tag 'AddRobotCacheVeto02' into develop
Update x evitare reload con overlay (2 test)
2024-07-23 10:48:41 +02:00
Samuele Locatelli 921cbed53b Merge branch 'release/AddRobotCacheVeto02' 2024-07-23 10:48:31 +02:00
Samuele Locatelli dc43f4fe18 Test num2 gestione offline 2024-07-23 10:45:38 +02:00
Samuele Locatelli 76f7c0d05c Merge tag 'AddRobotCacheVeto01' into develop
Aggiunto veto cache (robot) da VALIDARE
2024-07-23 10:09:42 +02:00
Samuele Locatelli 4adda8deaf Merge branch 'release/AddRobotCacheVeto01' 2024-07-23 10:09:21 +02:00
Samuele Locatelli 236858e4dd COnf x evitare cache (forse) + fix gestione abse URI 2024-07-23 10:08:56 +02:00
Samuele Locatelli 2367b25c3c Merge tag 'FixEmailSendRev02' into develop
Aggiunto log x ogni invio email da API
2024-07-05 08:23:48 +02:00
Samuele Locatelli 840b27e452 Merge branch 'release/FixEmailSendRev02' 2024-07-05 08:23:13 +02:00
Samuele Locatelli 09cb7fd30c Update x log invio email GPW 2024-07-05 08:22:55 +02:00
Samuele Locatelli c53452bf98 Merge tag 'FixEmailSendRev0' into develop
Fix gestione conf email, tolto rev management e test, aggiunto chaimata
realmente async invio
2024-07-04 12:13:14 +02:00
Samuele Locatelli fd99bf78dd Merge branch 'release/FixEmailSendRev0' 2024-07-04 12:12:22 +02:00
Samuele Locatelli 9c0aa41620 FIX gestione conf password in reverse 2024-07-04 12:11:47 +02:00
Samuele Locatelli 01647b74b4 fix conf x email sender 2024-07-03 11:40:28 +02:00
Samuele Locatelli 573c967670 Merge tag 'AddCheckRegAtt02' into develop
Aggiunta Activity History
2024-07-02 17:51:09 +02:00
Samuele Locatelli 0c2b819fb3 Merge branch 'release/AddCheckRegAtt02' 2024-07-02 17:51:03 +02:00
Samuele Locatelli 1e0d2746c6 Update x visualizzazione ActivityHistory 2024-07-02 17:50:47 +02:00
Samuele Locatelli 60ed02f452 Merge tag 'AddCheckRegAtt01' into develop
Aggiunta check inizio/fine e periodi validità insert reg attività
2024-07-02 10:48:01 +02:00
Samuele Locatelli 0cbea6a055 Merge branch 'release/AddCheckRegAtt01' 2024-07-02 10:47:42 +02:00
Samuele Locatelli 0302aee0e8 Aggiunta criteri controllo validità periodo inizio/fine attività 2024-07-02 10:46:55 +02:00
Samuele Locatelli ba96677c80 Cambio colore sidebar x indicare modifiche 2024-07-02 08:34:24 +02:00
Samuele Locatelli fe42629e0e Merge tag 'FixAuthUrlKey02' into develop
Fix if/else url
2024-06-25 08:33:29 +02:00
Samuele Locatelli e2627ffb8d Merge branch 'release/FixAuthUrlKey02' 2024-06-25 08:32:12 +02:00
Samuele Locatelli 955ee08a45 Fix if/else jumper 2024-06-25 08:31:55 +02:00
Samuele Locatelli c0c3bd65e9 Merge tag 'FixAuthUrlKey' into develop
Fix chiave utente in URL x core smart
2024-06-25 08:07:10 +02:00
Samuele Locatelli c5a0d1595b Merge branch 'release/FixAuthUrlKey' 2024-06-25 08:07:00 +02:00
Samuele Locatelli d53a89b95e fix chaive old/new mancante 2024-06-25 08:06:36 +02:00
Samuele Locatelli 212b6abfef Fix ricerca chiave auth formato old/new x CORE SMART 2024-06-25 08:06:20 +02:00
Samuele Locatelli ed249951e2 Merge tag 'AddEmailToResp02' into develop
Fix typo codice idxResp da tab
2024-06-14 18:54:43 +02:00
Samuele Locatelli 9601eaffcd Merge branch 'release/AddEmailToResp02' 2024-06-14 18:54:22 +02:00
Samuele Locatelli d8db7a8075 Typo fix idx resp 2024-06-14 18:54:04 +02:00
Samuele Locatelli 7928ff14e2 Merge tag 'AddEmailToResp01' into develop
Aggiunta invio email al resp se non è approvatore principale
2024-06-14 18:48:59 +02:00
Samuele Locatelli a13bea1813 Merge branch 'release/AddEmailToResp01' 2024-06-14 18:48:48 +02:00
Samuele Locatelli ebd5af2d77 Aggiunta notifica al resp oltre che al l'approvatore 2024-06-14 18:48:25 +02:00
Samuele Locatelli 4fd374a4c3 Merge tag 'AddSearchRA01' into develop
Aggiunta ricerca attività su week + lista
2024-05-20 18:51:50 +02:00
Samuele Locatelli a59bb18c0e Merge branch 'release/AddSearchRA01' 2024-05-20 18:51:39 +02:00
Samuele Locatelli 3e2729f594 Fix search su lista attivita! 2024-05-20 18:51:05 +02:00
Samuele Locatelli 4f33831180 Aggiunto ricerca + highlight attività in RA 2024-05-20 17:00:53 +02:00
Samuele Locatelli 23f334d233 Merge tag 'AddFontDynResize02' into develop
Fine tuning dimensione fonts in caso di multi attività giornaliere
2024-04-16 16:21:25 +02:00
Samuele Locatelli c21532256d Merge branch 'release/AddFontDynResize02' 2024-04-16 16:21:09 +02:00
Samuele Locatelli 0d4fb29b5b Fix display dimensioni x multi-attività WRKLOG 2024-04-16 16:20:43 +02:00
Samuele Locatelli 10a1356a6e Merge tag 'AddFontDynResize01' into develop
Add font dyn resize x tante RA
2024-03-26 17:23:43 +01:00
Samuele Locatelli bf27ae8e19 Merge branch 'release/AddFontDynResize01' 2024-03-26 17:23:32 +01:00
Samuele Locatelli 041eae2260 Dyn resize chars x tanti RA 2024-03-26 17:22:42 +01:00
Samuele Locatelli c3f011ec75 Merge tag 'UseRedisSentinel01' into develop
Aggiunta gestione sentinel + install su IIS01/02/03 + test
2024-03-16 11:56:57 +01:00
Samuele Locatelli 9d05a9f068 Merge branch 'release/UseRedisSentinel01' 2024-03-16 11:56:41 +01:00
Samuele Locatelli 5b459bbd7b Fix passaggio sentinel da localhost 2024-03-16 11:56:29 +01:00
Samuele Locatelli 05ef3a3a4f cleanup cicd 2024-03-16 09:51:34 +01:00
Samuele Locatelli f1b5c99463 Update conf con server redis main/replica e sentinel 2024-03-16 09:48:19 +01:00
85 changed files with 2669 additions and 722 deletions
-4
View File
@@ -182,8 +182,6 @@ CORE.Api:deploy:
- dotnet restore "$env:SOL_NAME.sln"
script:
- dotnet build $env:APP_NAME/$env:APP_NAME.csproj
# # IIS 02
# - dotnet publish -p:PublishProfile=IIS02.pubxml -p:RunCodeAnalysis=false -p:Configuration=Release -p:username=jenkins -p:Password=$IIS_PWD -p:AllowUntrustedCertificate=true -p:verbosity=quiet $env:APP_NAME/$env:APP_NAME.csproj
# IIS PROD
- dotnet publish -p:PublishProfile=IIS-PROD.pubxml -p:RunCodeAnalysis=false -p:Configuration=Release -p:username=jenkins -p:Password=$IIS_PWD -p:AllowUntrustedCertificate=true -p:verbosity=quiet $env:APP_NAME/$env:APP_NAME.csproj
@@ -202,8 +200,6 @@ CORE.WLOG:deploy:
- dotnet restore "$env:SOL_NAME.sln"
script:
- dotnet build $env:APP_NAME/$env:APP_NAME.csproj
# # IIS 02
# - dotnet publish -p:PublishProfile=IIS02.pubxml -p:RunCodeAnalysis=false -p:Configuration=Release -p:username=jenkins -p:Password=$IIS_PWD -p:AllowUntrustedCertificate=true -p:verbosity=quiet $env:APP_NAME/$env:APP_NAME.csproj
# IIS PROD
- dotnet publish -p:PublishProfile=IIS-PROD.pubxml -p:RunCodeAnalysis=false -p:Configuration=Release -p:username=jenkins -p:Password=$IIS_PWD -p:AllowUntrustedCertificate=true -p:verbosity=quiet $env:APP_NAME/$env:APP_NAME.csproj
+97 -96
View File
@@ -10,16 +10,6 @@ namespace GPW.CORE.Api.Controllers
[ApiController]
public class ProjCheckController : ControllerBase
{
#region Private Fields
private static NLog.Logger Log = LogManager.GetCurrentClassLogger();
private readonly IEmailSender _emailSender;
private IConfiguration _configuration;
#endregion Private Fields
#region Public Constructors
/// <summary>
@@ -39,90 +29,6 @@ namespace GPW.CORE.Api.Controllers
#endregion Public Constructors
#region Protected Enums
protected enum logType
{
none,
local,
trace,
info,
warn
}
#endregion Protected Enums
#region Protected Properties
/// <summary>
/// Dataservice x accesso DB
/// </summary>
protected ApiDataService dataService { get; set; }
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Effettua log del tipo richiesto aggiungendo riga anche allo stringbuilder indicato
/// </summary>
/// <param name="message"></param>
/// <param name="tipoLog"></param>
/// <param name="currSB"></param>
protected void doLog(string message, logType tipoLog, ref StringBuilder currSB)
{
bool doLine = true;
switch (tipoLog)
{
case logType.info:
Log.Info(message);
break;
case logType.trace:
Log.Trace(message);
break;
case logType.warn:
Log.Warn(message);
break;
case logType.local:
break;
default:
case logType.none:
doLine = false;
break;
}
if (doLine)
{
currSB.AppendLine(message);
}
else
{
currSB.Append(message);
}
}
protected async Task sendEmail(string subject, string message)
{
string emailRaw = _configuration["MailDest:ProjCheck"] ?? "";
List<string> emailDestList = emailRaw.Split(",").ToList();
foreach (var dest in emailDestList)
{
try
{
await _emailSender.SendEmailAsync(dest, subject, message);
}
catch (Exception exc)
{
Log.Error($"Eccezione durante invio email:{Environment.NewLine}dest: {dest} | subject {subject}{Environment.NewLine}{exc}");
}
}
}
#endregion Protected Methods
#region Public Methods
[HttpGet("ResAlloc")]
@@ -136,7 +42,7 @@ namespace GPW.CORE.Api.Controllers
int numFix = 0;
int numProj = 0;
int numFasi = 0;
string enableChiudiRaw = _configuration["EnableChiudiFasi"]??"false";
string enableChiudiRaw = _configuration["EnableChiudiFasi"] ?? "false";
bool enableChiudi = false;
if (!string.IsNullOrEmpty(enableChiudiRaw))
{
@@ -228,11 +134,106 @@ namespace GPW.CORE.Api.Controllers
// invio email
await sendEmail(msgTopic, msgBody.Replace($"{Environment.NewLine}", "<br/>"));
// ritorno solo LOG azioni
// ritorno solo LOG azioni
answ = numFix == 0 ? $"ALL {numProj} Proj OK" : $"Proj: {numProj} | Fasi: {numFasi} | Fixed/Closed: {numFix}";
return answ;
}
#endregion Public Methods
#region Protected Enums
protected enum logType
{
none,
local,
trace,
info,
warn
}
#endregion Protected Enums
#region Protected Properties
/// <summary>
/// Dataservice x accesso DB
/// </summary>
protected ApiDataService dataService { get; set; }
#endregion Protected Properties
#region Protected Methods
/// <summary>
/// Effettua log del tipo richiesto aggiungendo riga anche allo stringbuilder indicato
/// </summary>
/// <param name="message"></param>
/// <param name="tipoLog"></param>
/// <param name="currSB"></param>
protected void doLog(string message, logType tipoLog, ref StringBuilder currSB)
{
bool doLine = true;
switch (tipoLog)
{
case logType.info:
Log.Info(message);
break;
case logType.trace:
Log.Trace(message);
break;
case logType.warn:
Log.Warn(message);
break;
case logType.local:
break;
default:
case logType.none:
doLine = false;
break;
}
if (doLine)
{
currSB.AppendLine(message);
}
else
{
currSB.Append(message);
}
}
protected async Task sendEmail(string subject, string message)
{
string emailRaw = _configuration["MailDest:ProjCheck"] ?? "";
List<string> emailDestList = emailRaw.Split(",").ToList();
foreach (var dest in emailDestList)
{
try
{
await _emailSender.SendEmailAsync(dest, subject, message);
Log.Info($"Email inviata | dest: {dest} | subject: {subject}");
}
catch (Exception exc)
{
Log.Error($"Eccezione durante invio email:{Environment.NewLine}dest: {dest} | subject: {subject}{Environment.NewLine}{exc}");
}
}
}
#endregion Protected Methods
#region Private Fields
private static NLog.Logger Log = LogManager.GetCurrentClassLogger();
private readonly IEmailSender _emailSender;
private IConfiguration _configuration;
#endregion Private Fields
}
}
-1
View File
@@ -29,7 +29,6 @@ namespace GPW.CORE.Api.Data
/// <param name="emailSender"></param>
/// <param name="redisCacheClient"></param>
public ApiDataService(IConfiguration configuration, ILogger<ApiDataService> logger, IEmailSender emailSender, IConnectionMultiplexer redisConn)
//public ApiDataService(IConfiguration configuration, ILogger<ApiDataService> logger, IEmailSender emailSender, IRedisCacheClient redisCacheClient)
{
_logger = logger;
_configuration = configuration;
+1 -8
View File
@@ -29,19 +29,12 @@ builder.Services.AddSwaggerGen();
// abilitazione x email management con MailKit
//builder.Services.AddTransient<IEmailSender, MailKitEmailSender>();
builder.Services.AddSingleton<IEmailSender, MailKitEmailSender>();
// recupero email reversed
string revPwd = configuration["ExternalProviders:MailKit:SMTP:Password"] ?? "";
string pwd = "";
foreach (char c in revPwd)
{
pwd = c + pwd;
}
builder.Services.Configure<MailKitEmailSenderOptions>(options =>
{
options.Host_Address = configuration["ExternalProviders:MailKit:SMTP:Address"] ?? "";
options.Host_Port = Convert.ToInt32(configuration["ExternalProviders:MailKit:SMTP:Port"] ?? "");
options.Host_Username = configuration["ExternalProviders:MailKit:SMTP:Account"] ?? "";
options.Host_Password = pwd;
options.Host_Password = configuration["ExternalProviders:MailKit:SMTP:Password"] ?? "";
options.Sender_EMail = configuration["ExternalProviders:MailKit:SMTP:SenderEmail"] ?? "";
options.Sender_Name = configuration["ExternalProviders:MailKit:SMTP:SenderName"] ?? "";
});
+18 -5
View File
@@ -7,8 +7,8 @@
},
"AllowedHosts": "*",
"ConnectionStrings": {
"GPW.DB": "Server=W2019-SQL-STEAM;Database=GPW;User ID=sa;Password=keyhammer16;integrated security=False;MultipleActiveResultSets=True;App=LiMan.API;",
"Redis": "localhost:6379,DefaultDatabase=15,connectTimeout=5000,syncTimeout=5000,asyncTimeout=5000"
"GPW.DB": "Server=W2019-SQL-STEAM;Database=GPW;User ID=UserGPW; Password=Us3rGpw!75x93$77;integrated security=False;MultipleActiveResultSets=True;App=LiMan.API;",
"Redis": "localhost:26379,serviceName=prod,DefaultDatabase=15,connectTimeout=5000,syncTimeout=5000,asyncTimeout=5000,password=BtN9Py1wtLfLRvmzWnOPJ7RytDM+CLiVsJ/16zduNTlV8IOPGNrtzJSXPUnImA5PqmUMhKaUqo9NdHIG"
},
"ExternalProviders": {
"MailKit": {
@@ -16,7 +16,7 @@
"Address": "smtp-mail.outlook.com",
"Port": "587",
"Account": "steamwarebot@outlook.it",
"Password": "!39elanoizaNaiVnIomais",
"Password": "siamoInViaNazionale93!",
"SenderEmail": "steamwarebot@outlook.it",
"SenderName": "Steamware Email BOT"
}
@@ -29,17 +29,30 @@
},
"EnableChiudiFasi": "false",
"Redis": {
"Password": "",
"Password": "BtN9Py1wtLfLRvmzWnOPJ7RytDM+CLiVsJ/16zduNTlV8IOPGNrtzJSXPUnImA5PqmUMhKaUqo9NdHIG",
"AllowAdmin": false,
"Ssl": false,
"ConnectTimeout": 6000,
"SyncTimeout": 6000,
"AsyncTimeout": 6000,
"ConnectRetry": 2,
"ServiceName": "prod",
"Hosts": [
{
"Host": "localhost",
"Port": "6379"
"Port": "26379"
},
{
"Host": "redis01.ufficio",
"Port": "26379"
},
{
"Host": "redis03.ufficio",
"Port": "26379"
},
{
"Host": "redis03.ufficio",
"Port": "26379"
}
],
"Database": 15
@@ -366,6 +366,35 @@ namespace GPW.CORE.Data.Controllers
return dbResult;
}
/// <summary>
/// Tabella setup dati orari - elenco completo
/// </summary>
/// <param name="idxProj"></param>
/// <returns></returns>
public List<AnagOrariModel> AnagOrarioGetAll()
{
List<AnagOrariModel> dbResult = new List<AnagOrariModel>();
using (GPWContext localDbCtx = new GPWContext(_configuration))
{
try
{
// recupero intero set....
dbResult = localDbCtx
.DbSetAnagOrari
.ToList();
}
catch (Exception exc)
{
Log.Error($"Errore in AnagOrarioGetAll:{Environment.NewLine}{exc}");
}
}
if (dbResult == null)
{
dbResult = new List<AnagOrariModel>();
}
return dbResult;
}
public List<AnagProgettiModel> AnagProjAll(bool onlyActive)
{
// init dati necessari
@@ -501,6 +530,31 @@ namespace GPW.CORE.Data.Controllers
return dbResult;
}
/// <summary>
/// Recupera l'elenco delle ultime attività inserite da un dipendente
/// </summary>
/// <param name="idxDipendente">Dipendente interessato</param>
/// <param name="numRec">Num record da recuperare</param>
/// <returns></returns>
public List<RegAttivitaModel> UserLastRec(int idxDipendente, int numRec)
{
List<RegAttivitaModel> listRec = new List<RegAttivitaModel>();
// cerco su DB recuperando set di dati....
using (GPWContext localDbCtx = new GPWContext(_configuration))
{
listRec = localDbCtx
.DbSetRegAttivita
.Where(x => x.IdxDipendente == idxDipendente)
.Include(a => a.FasiNav).ThenInclude(p => p.ProgettoNav).ThenInclude(c => c.ClienteNav)
.OrderByDescending(x => x.IdxRa)
.Take(numRec)
.AsNoTracking()
.ToList();
}
return listRec;
}
/// <summary>
/// Recupera l'elenco dei dettagli giornalieri attività di un dipendente, dato periodo riferimento
/// </summary>
@@ -52,6 +52,7 @@ namespace GPW.CORE.Data.DbModels
public DateTime? DataCessazione { get; set; }
public bool? Attivo { get; set; }
public string Sigla { get; set; } = "";
public int idxResp { get; set; } = 0;
public virtual ICollection<RegAttivitaModel> RegAttivitaNav { get; set; }
public virtual ICollection<RilievoTempModel> RilievoTempNav { get; set; }
+5 -3
View File
@@ -30,8 +30,9 @@ namespace GPW.CORE.Data
#region Public Methods
public Task Execute(string to, string subject, string message)
public async Task<bool> Execute(string to, string subject, string message)
{
bool fatto = false;
// create message
var email = new MimeMessage();
email.Sender = MailboxAddress.Parse(Options.Sender_EMail);
@@ -47,11 +48,12 @@ namespace GPW.CORE.Data
{
smtp.Connect(Options.Host_Address, Options.Host_Port, Options.Host_SecureSocketOptions);
smtp.Authenticate(Options.Host_Username, Options.Host_Password);
smtp.Send(email);
await smtp.SendAsync(email);
smtp.Disconnect(true);
fatto = true;
}
return Task.FromResult(true);
return fatto;
}
public Task SendEmailAsync(string email, string subject, string message)
@@ -0,0 +1,25 @@
<div class="progress">
<div class="progress-bar" role="progressbar" aria-label="@CompName" style="width: @CurrPerc;" aria-valuenow="@CurrVal" aria-valuemin="@ValMin" aria-valuemax="@ValMax">@CurrPerc</div>
</div>
@code {
[Parameter]
public double ValMin { get; set; } = 0;
[Parameter]
public double ValMax { get; set; } = 100;
[Parameter]
public double ValCur { get; set; } = 0;
[Parameter]
public string CompName { get; set; } = "";
protected string CurrPerc
{
get => ValMax == 0 ? "0%" : $"{ValCur / ValMax:P0}";
}
protected int CurrVal
{
get => ValMax == 0 ? 0 : (int)(ValCur * 100 / ValMax);
}
}
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
<StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
<!--
<GenerateRuntimeConfigDevFile>true</GenerateRuntimeConfigDevFile>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
-->
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.7" />
</ItemGroup>
</Project>
@@ -0,0 +1,63 @@
@page "/clock-display"
@using GPW.CORE.HYB.Client.Components
@using GPW.CORE.HYB.Client.Services
@implements IDisposable
@rendermode InteractiveWebAssembly
@inject PersistentComponentState ApplicationState
@inject ClockService CServ
<h1>Hello, @handler!</h1>
<h3>Clock test</h3>
<ul class="list-group">
<li class="list-group-item">Realtime: @($"{adesso:HH:mm:ss}")</li>
<li class="list-group-item">Last Sync: @($"{lastSync:HH:mm:ss}")</li>
<li class="list-group-item">Delta: @($"{deltaTs.TotalMilliseconds} ms")</li>
<li class="list-group-item">Click: @($"{adesso:HH:mm:ss}")</li>
</ul>
<p>@adesso</p>
@code {
protected string handler = "";
private DateTime adesso
{
get => DateTime.Now;
}
private DateTime lastSync { get; set; } = DateTime.Today;
private DateTime clockDT { get; set; } = DateTime.Today;
private TimeSpan deltaTs { get; set; } = new TimeSpan();
protected override async void OnInitialized()
{
// persistingSubscription = ApplicationState.RegisterOnPersisting(PersistCount);
// if (!ApplicationState.TryTakeFromJson<int>(nameof(currentCount), out var restoredCount))
// {
// currentCount = Random.Shared.Next(50);
// }
// else
// {
// currentCount = restoredCount!;
// }
if (OperatingSystem.IsBrowser())
{
handler = "WASM";
}
else
{
handler = "Server";
}
lastSync = DateTime.Now;
clockDT = await CServ.GetSrvClockAsync();
deltaTs = clockDT.Subtract(DateTime.Now);
}
private PersistingComponentStateSubscription persistingSubscription;
void IDisposable.Dispose() => persistingSubscription.Dispose();
}
@@ -0,0 +1,109 @@
@page "/counter"
@using GPW.CORE.HYB.Client.Components
@implements IDisposable
@rendermode InteractiveAuto
@inject PersistentComponentState ApplicationState
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<h1>Hello, @handler!</h1>
<ProgressBar ValMin="0" ValMax="100" ValCur="@currVal"></ProgressBar>
<br />
<ul class="list-group">
<li class="list-group-item">Init: @($"{lastInit:HH:mm:ss}")</li>
<li class="list-group-item">Render: @($"{lastRender:HH:mm:ss}")</li>
<li class="list-group-item">ParamSet: @($"{lastParSet:HH:mm:ss}")</li>
<li class="list-group-item">Click: @($"{adesso:HH:mm:ss}")</li>
</ul>
<br />
@lastInit
<hr />
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<hr />
<p>@adesso</p>
@code {
private int currentCount = 0;
private PersistingComponentStateSubscription persistingSubscription;
private double currVal = 0;
private async Task IncrementCount()
{
currentCount++;
await AdvProg();
}
private DateTime adesso
{
get => DateTime.Now;
}
private Task PersistCount()
{
ApplicationState.PersistAsJson(nameof(currentCount), currentCount);
return Task.CompletedTask;
}
void IDisposable.Dispose() => persistingSubscription.Dispose();
protected string handler = "";
protected DateTime lastInit = DateTime.Today;
protected DateTime lastRender = DateTime.Today;
protected DateTime lastParSet = DateTime.Today;
protected override void OnInitialized()
{
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistCount);
if (!ApplicationState.TryTakeFromJson<int>(nameof(currentCount), out var restoredCount))
{
currentCount = Random.Shared.Next(50);
}
else
{
currentCount = restoredCount!;
}
if (OperatingSystem.IsBrowser())
{
handler = "WASM";
}
else
{
handler = "Server";
}
lastInit = DateTime.Now;
}
protected override void OnAfterRender(bool firstRender)
{
lastRender = DateTime.Now;
}
protected async Task AdvProg()
{
currVal = 0;
StateHasChanged();
await Task.Delay(100);
for (int i = 0; i < 10; i++)
{
// Simulate asynchronous loading to demonstrate streaming rendering
await Task.Delay(100);
currVal += 10;
StateHasChanged();
}
currVal = 100;
}
protected override void OnParametersSet()
{
lastParSet = DateTime.Now;
}
}
@@ -0,0 +1,9 @@
using GPW.CORE.HYB.Client.Services;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddScoped<ClockService>();
await builder.Build().RunAsync();
@@ -0,0 +1,19 @@
using System.Net.Http.Json;
namespace GPW.CORE.HYB.Client.Services
{
public class ClockService(HttpClient http) //: IMovieService
{
public async Task<DateTime> GetSrvClockAsync()
{
DateTime answ = DateTime.Today;
var rawData= await http.GetFromJsonAsync<string>("Clock") ?? "";
if (!string.IsNullOrEmpty(rawData))
{
DateTime.TryParse(rawData, out answ);
}
return answ;
}
}
}
@@ -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.HYB.Client
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
+31
View File
@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35122.118
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GPW.CORE.HYB", "GPW.CORE.HYB\GPW.CORE.HYB.csproj", "{7C77D787-A7B1-4A3B-8753-34BF4A56983A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GPW.CORE.HYB.Client", "GPW.CORE.HYB.Client\GPW.CORE.HYB.Client.csproj", "{1FC390C1-D9E5-43CE-923C-78F76A4E7EF8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7C77D787-A7B1-4A3B-8753-34BF4A56983A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C77D787-A7B1-4A3B-8753-34BF4A56983A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C77D787-A7B1-4A3B-8753-34BF4A56983A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C77D787-A7B1-4A3B-8753-34BF4A56983A}.Release|Any CPU.Build.0 = Release|Any CPU
{1FC390C1-D9E5-43CE-923C-78F76A4E7EF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1FC390C1-D9E5-43CE-923C-78F76A4E7EF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1FC390C1-D9E5-43CE-923C-78F76A4E7EF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1FC390C1-D9E5-43CE-923C-78F76A4E7EF8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DFCC3D3A-2A22-41CE-8361-2DAC02CDB9F6}
EndGlobalSection
EndGlobal
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="GPW.CORE.HYB.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
@@ -0,0 +1,23 @@
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>
</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,96 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
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 ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .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.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
@@ -0,0 +1,36 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">GPW.CORE.HYB</a>
</div>
</div>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="clock-display">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Clock Test
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="weather">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
</NavLink>
</div>
</nav>
</div>
@@ -0,0 +1,105 @@
.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
height: 2.5rem;
color: white;
position: absolute;
top: 0.5rem;
right: 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}
.navbar-toggler:checked {
background-color: rgba(255, 255, 255, 0.5);
}
.top-row {
height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}
.bi-house-door-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}
.bi-plus-square-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}
.bi-list-nested-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep .nav-link {
color: #d7d7d7;
background: none;
border: none;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
width: 100%;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}
.nav-item ::deep .nav-link:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.nav-scrollable {
display: none;
}
.navbar-toggler:checked ~ .nav-scrollable {
display: block;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.nav-scrollable {
/* Never collapse the sidebar for wide screens */
display: block;
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}
@@ -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,7 @@
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
@@ -0,0 +1,78 @@
@page "/weather"
@using GPW.CORE.HYB.Client.Components
@attribute [StreamRendering]
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
<ProgressBar ValMin="0" ValMax="100" ValCur="@currVal"></ProgressBar>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
private double currVal = 0;
protected override async Task OnInitializedAsync()
{
currVal = 0;
await Task.Delay(100);
StateHasChanged();
for (int i = 0; i < 10; i++)
{
// Simulate asynchronous loading to demonstrate streaming rendering
await Task.Delay(100);
currVal += 10;
StateHasChanged();
}
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
currVal = 100;
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}
@@ -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>
@@ -0,0 +1,11 @@
@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.HYB
@using GPW.CORE.HYB.Client
@using GPW.CORE.HYB.Components
@@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace GPW.CORE.HYB.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ClockController : ControllerBase
{
//public ClockController() { }
/// <summary>
/// Restituisce l'orario corrente del server
/// GET api/Clock
/// </summary>
/// <returns></returns>
[HttpGet]
public IActionResult Get()
{
return Ok(DateTime.Now);
}
}
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\GPW.CORE.HYB.Client\GPW.CORE.HYB.Client.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.7" />
</ItemGroup>
</Project>
+43
View File
@@ -0,0 +1,43 @@
using GPW.CORE.HYB.Client.Pages;
using GPW.CORE.HYB.Client.Services;
using GPW.CORE.HYB.Components;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
//builder.Services.AddSingleton<ClockService>();
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapControllers();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(GPW.CORE.HYB.Client._Imports).Assembly);
app.Run();
@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:28435",
"sslPort": 44365
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5075",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:7127;http://localhost:5075",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
@@ -0,0 +1,13 @@
namespace GPW.CORE.HYB.Services
{
public class ClockService() //: IMovieService
{
public async Task<DateTime> GetSrvClockAsync()
{
DateTime answ = DateTime.Now;
await Task.Delay(1);
return answ;
}
}
}
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
+51
View File
@@ -0,0 +1,51 @@
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
a, .btn-link {
color: #006bb7;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.content {
padding-top: 1.1rem;
}
h1:focus {
outline: none;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid #e50000;
}
.validation-message {
color: #e50000;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.darker-border-checkbox.form-check-input {
border-color: #929292;
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+56 -45
View File
@@ -1,64 +1,75 @@
<div class="accordion mb-5" 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">
<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="collapseZero" class="accordion-collapse collapse show" aria-labelledby="headingZero" data-bs-parent="#accordionExample">
<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>
@if (!isAuthKeyAdmin && !isApprAdmin)
<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">
<LoadingData DisplayMode="LoadingData.SpinMode.Growl"></LoadingData>
<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>
}
else
@if (isApprAdmin)
{
@if (isAuthKeyAdmin)
{
<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>QRCode direct display</b>
</button>
</h2>
<div id="collapseThree" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
<div class="accordion-body">
<UserQrCode></UserQrCode>
</div>
</div>
<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 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>ADMIN Reset AuthKey via e-mail</b>
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse" aria-labelledby="headingOne" 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="headingTwo">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
<b>Approvazione Timbrature</b>
</button>
</h2>
<div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
<TimbAdmin IdxDipAdmin="@IdxDipendente"></TimbAdmin>
</div>
</div>
}
</div>
}
</div>
+15 -20
View File
@@ -18,19 +18,6 @@ namespace GPW.CORE.Smart.Components
[Parameter]
public int IdxDipendente { get; set; } = -1;
[Parameter]
public List<DailyDataDTO>? ListRecords
{
get => listRecords;
set
{
if (listRecords != value)
{
listRecords = value;
}
}
}
[Parameter]
public List<CalFesteFerieModel>? ListFermateAzienda
{
@@ -44,6 +31,19 @@ namespace GPW.CORE.Smart.Components
}
}
[Parameter]
public List<DailyDataDTO>? ListRecords
{
get => listRecords;
set
{
if (listRecords != value)
{
listRecords = value;
}
}
}
[Parameter]
public List<RegRichiesteModel>? ListRecordsRichieste
{
@@ -62,8 +62,6 @@ namespace GPW.CORE.Smart.Components
#endregion Public Properties
private Dictionary<DateTime, string> DateCFF = new Dictionary<DateTime, string>();
#region Public Methods
public void Dispose()
@@ -80,7 +78,6 @@ namespace GPW.CORE.Smart.Components
#region Protected Fields
protected DailyDataDTO? datiGiorno = null;
protected string emptyTime = "--/--";
#endregion Protected Fields
@@ -105,6 +102,7 @@ namespace GPW.CORE.Smart.Components
#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>();
@@ -126,9 +124,8 @@ namespace GPW.CORE.Smart.Components
private DateTime dtCurr { get; set; } = DateTime.Today;
private List<DailyDataDTO>? listRecords { get; set; } = null;
private List<CalFesteFerieModel>? listCFF { get; set; } = null;
private List<DailyDataDTO>? listRecords { get; set; } = null;
private List<RegRichiesteModel>? listRecordsRichieste { get; set; } = null;
[Inject]
@@ -211,7 +208,6 @@ namespace GPW.CORE.Smart.Components
string cssSpec = "";
foreach (var item in ListFermateAzienda)
{
cssSpec = item.codGiust == "FEST" ? cssFest : cssChius;
if (!DateCFF.ContainsKey(item.data))
{
@@ -244,7 +240,6 @@ namespace GPW.CORE.Smart.Components
}
}
}
}
if (ListRecords != null)
+1 -1
View File
@@ -1,5 +1,5 @@
<div class="container d-flex justify-content-between">
<div class="title @homeCss" @onclick="ReturnHome">
<div class="title @homeCss" @onclick="ForceReset">
<i class="fa-solid fa-home"></i>
<sub>@homeMessage</sub>
</div>
+6 -3
View File
@@ -123,7 +123,7 @@ namespace GPW.CORE.Smart.Components
protected async Task DoVerifyActiv()
{
MService.PayloadOk = false;
await Task.Delay(250);
await Task.Delay(1);
await VerifyActiv();
}
@@ -172,9 +172,12 @@ namespace GPW.CORE.Smart.Components
LocalNet = confNet != null ? confNet.valore : "10";
}
protected void ReturnHome()
protected async Task ReturnHome()
{
navManager.NavigateTo("Home", true);
// svuoto cache
await CDService.FlushRedisCache();
// ritorno in home
navManager.NavigateTo("Home", true);
}
protected async Task VerifyActiv()
+100
View File
@@ -371,6 +371,47 @@ namespace GPW.CORE.Smart.Data
return dbResult;
}
/// <summary>
/// Dati orario globali da setup
/// </summary>
/// <returns></returns>
public async Task<List<AnagOrariModel>> AnagOrarioAll()
{
string source = "DB";
List<AnagOrariModel> dbResult = new List<AnagOrariModel>();
string currKey = $"{rKeyAnOrDip}:ALL";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
if (!string.IsNullOrEmpty(rawData))
{
source = "REDIS";
var tempResult = JsonConvert.DeserializeObject<List<AnagOrariModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<AnagOrariModel>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.AnagOrarioGetAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
}
if (dbResult == null)
{
dbResult = new List<AnagOrariModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"AnagOrarioAll | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Dati orario del dipendente indicato
/// </summary>
@@ -899,8 +940,19 @@ namespace GPW.CORE.Smart.Data
answ = dbController.RegMalattieDelete(currItem);
if (answ)
{
// recupero info dipendente
var listDip = await DipendentiGetAll();
var currDip = listDip.FirstOrDefault(x => x.IdxDipendente == currItem.IdxDipendente);
// gestisco email notifica...
string emailDest = _configuration["MailDest:InfoMal"];
if (currDip.idxResp > 0)
{
string mailResp = await emailResp(currDip.idxResp);
if (!string.IsNullOrEmpty(mailResp) && !emailDest.Contains(mailResp))
{
emailDest += $",{mailResp}";
}
}
string urlRedir = _configuration["AdmApp:LinkMal"];
StringBuilder sbMain = new StringBuilder();
DateTime adesso = DateTime.Now;
@@ -1031,6 +1083,14 @@ namespace GPW.CORE.Smart.Data
var currDip = listDip.FirstOrDefault(x => x.IdxDipendente == currItem.IdxDipendente);
// gestisco email notifica...
string emailDest = _configuration["MailDest:InfoMal"];
if (currDip.idxResp > 0)
{
string mailResp = await emailResp(currDip.idxResp);
if (!string.IsNullOrEmpty(mailResp) && !emailDest.Contains(mailResp))
{
emailDest += $",{mailResp}";
}
}
string urlRedir = _configuration["AdmApp:LinkMal"];
StringBuilder sbMain = new StringBuilder();
DateTime adesso = DateTime.Now;
@@ -1070,8 +1130,19 @@ namespace GPW.CORE.Smart.Data
answ = dbController.RegRichiesteDelete(currItem);
if (answ)
{
// recupero info dipendente
var listDip = await DipendentiGetAll();
var currDip = listDip.FirstOrDefault(x => x.IdxDipendente == currItem.IdxDipendente);
// gestisco email notifica...
string emailDest = _configuration["MailDest:InfoRich"];
if (currDip.idxResp > 0)
{
string mailResp = await emailResp(currDip.idxResp);
if (!string.IsNullOrEmpty(mailResp) && !emailDest.Contains(mailResp))
{
emailDest += $",{mailResp}";
}
}
string urlRedir = _configuration["AdmApp:LinkRich"];
StringBuilder sbMain = new StringBuilder();
DateTime adesso = DateTime.Now;
@@ -1161,6 +1232,14 @@ namespace GPW.CORE.Smart.Data
var currDip = listDip.FirstOrDefault(x => x.IdxDipendente == currItem.IdxDipendente);
// gestisco email notifica...
string emailDest = _configuration["MailDest:InfoRich"];
if (currDip.idxResp > 0)
{
string mailResp = await emailResp(currDip.idxResp);
if (!string.IsNullOrEmpty(mailResp) && !emailDest.Contains(mailResp))
{
emailDest += $",{mailResp}";
}
}
string urlRedir = _configuration["AdmApp:LinkRich"];
StringBuilder sbMain = new StringBuilder();
DateTime adesso = DateTime.Now;
@@ -1438,6 +1517,27 @@ namespace GPW.CORE.Smart.Data
#region Protected Methods
/// <summary>
/// Calcola email del responsabile dato idx
/// </summary>
/// <param name="idxResp"></param>
/// <returns></returns>
protected async Task<string> emailResp(int idxResp)
{
string answ = "";
var listDip = await DipendentiGetAll();
// recupero email resp...
if (idxResp > 0)
{
var recResp = listDip.FirstOrDefault(x => x.IdxDipendente == idxResp);
if (recResp != null)
{
answ = $"{recResp.Email}";
}
}
return answ;
}
/// <summary>
/// Invio email tramite libreria
/// </summary>
+1 -1
View File
@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<Version>3.0.2403.0510</Version>
<Version>3.0.2408.1910</Version>
<ImplicitUsings>enable</ImplicitUsings>
<PackageProjectUrl>www.egalware.com</PackageProjectUrl>
<Description>GPW Smart UI</Description>
+8
View File
@@ -94,6 +94,7 @@ namespace GPW.CORE.Smart.Pages
// resetto valori in localStorage...
string devSec = "";
string devIp = "";
authKey = "";
await MService.setDevSecretAsync(devSec);
await MService.setDevIpv4Async(devIp);
// recupero da URL
@@ -111,6 +112,13 @@ namespace GPW.CORE.Smart.Pages
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");
+20
View File
@@ -27,4 +27,24 @@
</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>
+5 -1
View File
@@ -36,6 +36,10 @@
<a class="dismiss">🗙</a>
</div>
@* <div id="offline-overlay" class="animate-overlay">
<span>OFFLINE</span>
</div> *@
<script type="text/javascript" src="lib/css/bootstrap/js/bootstrap.min.js"></script>
<script src="_framework/blazor.server.js" autostart="false"></script>
@@ -109,7 +113,7 @@
<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/ToastTrigger.js"></script>*@
@* <script type="text/javascript" src="lib/offlineOverlay.js"></script> *@
<script type="text/javascript" src="lib/qrcode.js"></script>
<script type="text/javascript" src="lib/qrHelper.js"></script>
</body>
+5 -8
View File
@@ -22,19 +22,13 @@ var redisMultiplexer = ConnectionMultiplexer.Connect(connStringRedis);
// abilitazione x email management con MailKit
builder.Services.AddTransient<IEmailSender, MailKitEmailSender>();
// recupero email reversed
string revPwd = configuration["ExternalProviders:MailKit:SMTP:Password"] ?? "";
string pwd = "";
foreach (char c in revPwd)
{
pwd = c + pwd;
}
builder.Services.Configure<MailKitEmailSenderOptions>(options =>
{
options.Host_Address = configuration["ExternalProviders:MailKit:SMTP:Address"];
options.Host_Port = Convert.ToInt32(configuration["ExternalProviders:MailKit:SMTP:Port"]);
options.Host_Username = configuration["ExternalProviders:MailKit:SMTP:Account"];
options.Host_Password = pwd;// configuration["ExternalProviders:MailKit:SMTP:Password"];
options.Host_Password = configuration["ExternalProviders:MailKit:SMTP:Password"];
options.Sender_EMail = configuration["ExternalProviders:MailKit:SMTP:SenderEmail"];
options.Sender_Name = configuration["ExternalProviders:MailKit:SMTP:SenderName"];
});
@@ -54,6 +48,9 @@ builder.Services.AddBlazoredSessionStorage();
var app = builder.Build();
// aggiunt base URL x routing corretto
app.UsePathBase(builder.Configuration["OptConf:BaseUrl"]);
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
+6 -3
View File
@@ -8,8 +8,8 @@
"AllowedHosts": "*",
"CodApp": "GPW",
"ConnectionStrings": {
"Redis": "localhost:6379,DefaultDatabase=15,connectTimeout=5000,syncTimeout=5000,asyncTimeout=5000,abortConnect=false,ssl=false",
"GPW.DB": "Server=W2019-SQL-STEAM;Database=GPW; User ID=sa;Password=keyhammer16; integrated security=False; MultipleActiveResultSets=True; App=GPW.CORE.WRKLOG;"
"Redis": "localhost:26379,serviceName=prod,DefaultDatabase=15,connectTimeout=5000,syncTimeout=5000,asyncTimeout=5000,abortConnect=false,ssl=false,password=BtN9Py1wtLfLRvmzWnOPJ7RytDM+CLiVsJ/16zduNTlV8IOPGNrtzJSXPUnImA5PqmUMhKaUqo9NdHIG",
"GPW.DB": "Server=W2019-SQL-STEAM;Database=GPW; User ID=UserGPW; Password=Us3rGpw!75x93$77; integrated security=False; MultipleActiveResultSets=True; App=GPW.CORE.WRKLOG;"
},
"Application": {
"ContactText": "Our contacts and support sites",
@@ -22,12 +22,15 @@
"Address": "smtp-mail.outlook.com",
"Port": "587",
"Account": "steamwarebot@outlook.it",
"Password": "!39elanoizaNaiVnIomais",
"Password": "siamoInViaNazionale93!",
"SenderEmail": "steamwarebot@outlook.it",
"SenderName": "Steamware Email BOT"
}
}
},
"OptConf": {
"BaseUrl": "/GPW/CORE.Smart"
},
"MailDest": {
"Admin": "samuele@steamware.net",
"InfoMal": "samuele.locatelli@egalware.com",
+33
View File
@@ -27,6 +27,39 @@ a,
.btn-link {
color: #0071c1;
}
@keyframes offline {
0% {
opacity: 0;
}
99% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.animate-overlay {
animation-name: offline;
animation-duration: 3s;
}
#offline-overlay {
background-color: rgba(255, 255, 255, 0.9);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
pointer-events: none;
}
#offline-overlay span {
font-size: 300%;
font-family: sans-serif;
letter-spacing: 5px;
color: #888;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
+40
View File
@@ -19,6 +19,46 @@ a, .btn-link {
color: #0071c1;
}
@keyframes offline {
0% {
opacity: 0;
}
99% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.animate-overlay {
animation-name: offline;
animation-duration: 3s;
}
#offline-overlay {
background-color: rgba(255,255,255,.9);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
pointer-events: none;
}
#offline-overlay span {
font-size: 300%;
font-family: sans-serif;
letter-spacing: 5px;
color: #888;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
+1 -1
View File
@@ -1 +1 @@
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');@import url('fonts.min.css');h1,h2,h3,h4,h5,h6,b,display-1,display-2,display-3,display-4{font-family:'Lato',sans-serif;}html,body{font-family:'Roboto',sans-serif;}h1:focus{outline:0;}a,.btn-link{color:#0071c1;}.btn-primary{color:#fff;background-color:#1b6ec2;border-color:#1861ac;}.content{padding-top:1.1rem;}.valid.modified:not([type=checkbox]){outline:1px solid #26b050;}.invalid{outline:1px solid #f00;}.validation-message{color:#f00;}#blazor-error-ui{background:#ffffe0;bottom:0;box-shadow:0 -1px 2px rgba(0,0,0,.2);display:none;left:0;padding:.6rem 1.25rem .7rem 1.25rem;position:fixed;width:100%;z-index:1000;}#blazor-error-ui .dismiss{cursor:pointer;position:absolute;right:.75rem;top:.5rem;}.blazor-error-boundary{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem,#b32121;padding:1rem 1rem 1rem 3.7rem;color:#fff;}.blazor-error-boundary::after{content:"An error has occurred.";}
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');@import url('fonts.min.css');h1,h2,h3,h4,h5,h6,b,display-1,display-2,display-3,display-4{font-family:'Lato',sans-serif;}html,body{font-family:'Roboto',sans-serif;}h1:focus{outline:0;}a,.btn-link{color:#0071c1;}@keyframes offline{0%{opacity:0;}99%{opacity:0;}100%{opacity:1;}}.animate-overlay{animation-name:offline;animation-duration:3s;}#offline-overlay{background-color:rgba(255,255,255,.9);position:absolute;top:0;left:0;right:0;bottom:0;display:flex;justify-content:center;align-items:center;pointer-events:none;}#offline-overlay span{font-size:300%;font-family:sans-serif;letter-spacing:5px;color:#888;}.btn-primary{color:#fff;background-color:#1b6ec2;border-color:#1861ac;}.content{padding-top:1.1rem;}.valid.modified:not([type=checkbox]){outline:1px solid #26b050;}.invalid{outline:1px solid #f00;}.validation-message{color:#f00;}#blazor-error-ui{background:#ffffe0;bottom:0;box-shadow:0 -1px 2px rgba(0,0,0,.2);display:none;left:0;padding:.6rem 1.25rem .7rem 1.25rem;position:fixed;width:100%;z-index:1000;}#blazor-error-ui .dismiss{cursor:pointer;position:absolute;right:.75rem;top:.5rem;}.blazor-error-boundary{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem,#b32121;padding:1rem 1rem 1rem 3.7rem;color:#fff;}.blazor-error-boundary::after{content:"An error has occurred.";}
@@ -1,9 +0,0 @@
var toastTrigger = document.getElementById('ToastBtn')
var currToastObj = document.getElementById('LiveToast')
if (toastTrigger) {
toastTrigger.addEventListener('click', () => {
const toast = new bootstrap.Toast(currToastObj)
toast.show()
})
}
@@ -0,0 +1,15 @@
const overlay = document.getElementById('offline-overlay');
const stopJS = document.getElementById('stopJS');
const heartbeat = () => {
overlay.classList.remove("animate-overlay");
void overlay.offsetWidth;
overlay.classList.add("animate-overlay");
}
const heartbeatInterval = setInterval(heartbeat, 1000);
stopJS.addEventListener("click", function () {
clearInterval(heartbeatInterval);
});
+40 -35
View File
@@ -71,49 +71,54 @@
}
else
{
@*<!-- MPF -->*@
<div class="d-flex justify-content-between text-center small">
@if (hasFest && DayDTO != null && DayDTO.ListFermateAzienda != null)
{
@foreach (var fermata in DayDTO.ListFermateAzienda)
@if (hasFest || hasFerie || hasPerm)
{
<div class="d-flex justify-content-between text-center small">
@*<!-- MPF -->*@
@if (hasFest && DayDTO != null && DayDTO.ListFermateAzienda != null)
{
if (fermata.codGiust == "FEST")
@foreach (var fermata in DayDTO.ListFermateAzienda)
{
<div class="mx-2 bg-danger rounded text-warning w-100">
<div class="text-uppercase"><b>@fermata.codGiust</b> | @fermata.descrizione</div>
</div>
if (fermata.codGiust == "FEST")
{
<div class="mx-2 bg-danger rounded text-warning w-100">
<div class="text-uppercase"><b>@fermata.codGiust</b> | @fermata.descrizione</div>
</div>
}
else
{
<div class="mx-2 bg-warning rounded text-danger w-100">
<div class="text-uppercase"><b>@fermata.codGiust</b> | @fermata.descrizione</div>
</div>
}
}
else
}
@if (hasFerie && DayDTO != null && DayDTO.ListFerieDip != null)
{
@foreach (var richiesta in DayDTO.ListFerieDip)
{
<div class="mx-2 bg-warning rounded text-danger w-100">
<div class="text-uppercase"><b>@fermata.codGiust</b> | @fermata.descrizione</div>
<div class="mx-2 bg-info rounded text-light w-100">
<div>
<b>FERIE</b> | <small class="small">@richiesta.Note</small>
</div>
</div>
}
}
}
@if (hasFerie && DayDTO != null && DayDTO.ListFerieDip != null)
{
@foreach (var richiesta in DayDTO.ListFerieDip)
else if (hasPerm && DayDTO != null && DayDTO.ListRichiesteDip != null)
{
<div class="mx-2 bg-info rounded text-light w-100">
<div>
<b>FERIE</b> | <small class="small">@richiesta.Note</small>
@foreach (var richiesta in DayDTO.ListRichiesteDip)
{
<div class="mx-2 bg-warning rounded text-dark w-100">
<div>
<b>@richiesta.CodGiust</b> | <small class="small">@richiesta.Note, @($"{@richiesta.DtStart:HH.mm} - {@richiesta.DtEnd:HH.mm}")</small>
</div>
</div>
</div>
}
}
}
@if (hasPerm && DayDTO != null && DayDTO.ListRichiesteDip != null)
{
@foreach (var richiesta in DayDTO.ListRichiesteDip)
{
<div class="mx-2 bg-warning rounded text-dark w-100">
<div>
<b>@richiesta.CodGiust</b> | <small class="small">@richiesta.Note, @($"{@richiesta.DtStart:HH.mm} - {@richiesta.DtEnd:HH.mm}")</small>
</div>
</div>
}
}
</div>
</div>
}
@*<!-- Timbrature -->*@
<div class="d-flex justify-content-between text-center">
@if (DayDTO != null && DayDTO.ListTimbr != null && DayDTO.ListTimbr.Count > 0)
@@ -148,14 +153,13 @@
}
else
{
<div class="py-0 mb-1 flex-fill" style="font-size: 1em;">
&nbsp;
</div>
}
</div>
@*<!-- RegAtt -->*@
<div class="d-flex justify-content-between text-center">
<div class="d-flex justify-content-between text-center" style="@cStyle">
@if (@noData)
{
<AddRA NewItemCreated="ReportSelected" InizioPer="@FirstTimb" IdxDip="@IdxDipSel" EnableAction="@EnableActionMenu"></AddRA>
@@ -165,6 +169,7 @@
{
@foreach (var item in ListRegAtt().OrderBy(x => x.Inizio))
{
bool doShow = true;
// se fuori periodo --> nascondo
if (item.Fine < item.Inizio.Date.AddHours(StartHour) || item.Inizio > item.Fine.Date.AddHours(EndHour))
+36 -16
View File
@@ -61,6 +61,32 @@ namespace GPW.CORE.WRKLOG.Components
#endregion Protected Fields
#region Protected Properties
protected string cStyle
{
get
{
int numBlock = ListRegAtt().Count;
string fSize = "1.1rem";
if (numBlock > 9)
{
fSize = "0.85rem";
}
else if (numBlock > 7)
{
fSize = "0.9rem";
}
else if (numBlock > 5)
{
fSize = "1.0rem";
}
return $"font-size: {fSize};";
}
}
#endregion Protected Properties
#region Protected Methods
/// <summary>
@@ -71,9 +97,6 @@ namespace GPW.CORE.WRKLOG.Components
if (DayDTO != null)
{
await PeriodSelected.InvokeAsync(DayDTO);
#if false
//await PeriodSelected.InvokeAsync(DayDTO.ListTimbr);
#endif
}
}
@@ -122,9 +145,6 @@ namespace GPW.CORE.WRKLOG.Components
if (DayDTO != null)
{
await PeriodSelected.InvokeAsync(DayDTO);
#if false
//await PeriodSelected.InvokeAsync(DayDTO.ListTimbr);
#endif
}
}
@@ -252,10 +272,6 @@ namespace GPW.CORE.WRKLOG.Components
}
}
#if false
//public EventCallback<List<TimbratureModel>?> PeriodSelected { get; set; }
#endif
private bool OkTemp
{
get
@@ -348,8 +364,6 @@ namespace GPW.CORE.WRKLOG.Components
answ = DayDTO.OreMinComm;
}
return answ;
//TimeSpan tSpan = TimeSpan.FromHours(oreComm);
//return $"{tSpan.Hours}:{tSpan.Minutes:00}";
}
}
@@ -363,8 +377,6 @@ namespace GPW.CORE.WRKLOG.Components
answ = DayDTO.OreMinLav;
}
return answ;
//TimeSpan tSpan = Utils.TSpanRounded(TimeSpan.FromHours(oreLav), 5, true);
//return $"{tSpan.Hours}:{tSpan.Minutes:00}";
}
}
@@ -469,6 +481,7 @@ namespace GPW.CORE.WRKLOG.Components
result.Add(emptyPeriod);
}
// ...processo cercando uscita relativa oppure NOW
if (timbOUT.Count > idxTimb)
{
@@ -481,12 +494,19 @@ namespace GPW.CORE.WRKLOG.Components
fineLav = DateTime.Today.AddHours(adesso.Hour).AddMinutes(minRound);
isClosed = false;
}
// solo x la prima prendo entrata_1... x rettifica ingresso
DateTime inizioLav = item.DataOra;
if (idxTimb == 0 && DayDTO.TimbrExpl.Entrata1 != null)
{
inizioLav = DayDTO.TimbrExpl.Entrata1 ?? item.DataOra;
}
PeriodoDTO workPeriod = new PeriodoDTO()
{
Inizio = item.DataOra,
Inizio = inizioLav,
Fine = fineLav,
Tipo = CORE.Data.TipoPeriodo.Lavoro,
OreTot = (decimal)fineLav.Subtract(item.DataOra).TotalHours,
OreTot = (decimal)fineLav.Subtract(inizioLav).TotalHours,
IsClosed = isClosed
};
result.Add(workPeriod);
+1 -1
View File
@@ -17,7 +17,7 @@
<button @onclick="() => Clone()" class="px-1 text-center border border-secondary border-top-0 border-bottom-0 border-left-0 btn btn-primary btn-sm py-1" style="font-size: @FontSizeBig; width:3.4em;" title="Copy">
<i class="fas fa-paste"></i>
</button>
<div class="px-2 py-0 text-start textTrim flex-fill" style="font-size: @FontSizeSmall;">
<div class="px-2 py-0 text-start textTrim flex-fill @blockClass" style="font-size: @FontSizeSmall;">
<div class="text-dark textTrim @cssTrim" title="@CurrData.FasiNav?.ProgettoNav?.ClienteNav?.RagSociale - @CurrData.FasiNav?.ProgettoNav?.NomeProj"><b>@(trimLine($"{CurrData.FasiNav?.ProgettoNav?.ClienteNav?.RagSociale}", MaxChar))</b> - @(trimLine($"{CurrData.FasiNav?.ProgettoNav?.NomeProj}", MaxChar))</div>
<div class="text-secondary textTrim @cssTrim" title="@CurrData.FasiNav?.DescrizioneFase">@(trimLine($"{CurrData.FasiNav?.DescrizioneFase}", 100))</div>
<div class="text-trim @cssTrim" title="@CurrData.Descrizione">@(trimLine($"{CurrData.Descrizione}", MaxChar))</div>
+44 -6
View File
@@ -4,13 +4,19 @@ using Microsoft.JSInterop;
namespace GPW.CORE.WRKLOG.Components
{
public partial class DettAtt
public partial class DettAtt : IDisposable
{
#region Public Properties
[Parameter]
public RegAttivitaModel CurrData { get; set; } = null!;
[Parameter]
public string FontSizeBig { get; set; } = "1.4rem";
[Parameter]
public string FontSizeSmall { get; set; } = "1.1rem";
[Parameter]
public int IdxDipSel { get; set; } = 0;
@@ -32,13 +38,17 @@ namespace GPW.CORE.WRKLOG.Components
[Parameter]
public bool ShowDate { get; set; } = false;
[Parameter]
public string FontSizeBig { get; set; } = "1.4rem";
[Parameter]
public string FontSizeSmall { get; set; } = "1.1rem";
#endregion Public Properties
#region Public Methods
public void Dispose()
{
AppMServ.EA_SearchUpdated -= AppMServ_EA_SearchUpdated;
}
#endregion Public Methods
#region Protected Fields
protected bool isSelected = false;
@@ -47,6 +57,24 @@ namespace GPW.CORE.WRKLOG.Components
#region Protected Properties
protected string blockClass
{
get
{
string btnClass = "";
bool highlight = false;
// recupero search
string SearchVal = AppMServ.SearchVal;
// calcolo valore highlight
if (!string.IsNullOrEmpty(SearchVal))
{
highlight = (CurrData.Descrizione.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase));
}
btnClass = highlight ? "bg-success bg-opacity-25" : "bg-light";
return btnClass;
}
}
protected string cssSelected
{
get => isSelected ? "table-info" : "";
@@ -118,6 +146,7 @@ namespace GPW.CORE.WRKLOG.Components
protected override void OnInitialized()
{
AppMServ.EA_SearchUpdated += AppMServ_EA_SearchUpdated;
isSelected = false;
}
@@ -173,5 +202,14 @@ namespace GPW.CORE.WRKLOG.Components
}
#endregion Private Properties
#region Private Methods
private void AppMServ_EA_SearchUpdated()
{
InvokeAsync(StateHasChanged);
}
#endregion Private Methods
}
}
-87
View File
@@ -59,91 +59,4 @@ else
</div>
</div>
@code {
private int _maxSugg = 10;
private int _ggPrec = 60;
private int idxFaseSel = 0;
[Parameter]
public EventCallback<int> ItemSelected { get; set; }
[Parameter]
public int idxFase
{
get
{
return idxFaseSel;
}
set
{
idxFaseSel = value;
}
}
private int maxSugg
{
get
{
return _maxSugg;
}
set
{
_maxSugg = value;
var pUpd = Task.Run(async () => await ReloadData());
pUpd.Wait();
}
}
private int ggPrec
{
get
{
return _ggPrec;
}
set
{
_ggPrec = value;
var pUpd = Task.Run(async () => await ReloadData());
pUpd.Wait();
}
}
protected List<ParetoRegAttModel>? ListRecords { get; set; } = null;
protected string cssRiga(int idxFase)
{
string answ = "";
if (idxFase == idxFaseSel)
{
answ = "bg-info text-light";
}
return answ;
}
protected override async Task OnInitializedAsync()
{
await ReloadData();
AppMServ.EA_SearchUpdated += OnSearchUpdated;
}
protected void OnSearchUpdated()
{
var pUpd = Task.Run(async () => await ReloadData());
pUpd.Wait();
}
protected async Task ReloadData()
{
idxFaseSel = 0;
if (AppMServ.IdxDipendente > 0)
{
ListRecords = await GDataServ.ParetoRegAtt(AppMServ.IdxDipendente, ggPrec, maxSugg);
}
}
protected async Task SelectRecord(int idxFase)
{
idxFaseSel = idxFase;
await ItemSelected.InvokeAsync(idxFase);
}
}
@@ -0,0 +1,124 @@
using GPW.CORE.Data.DbModels;
using Microsoft.AspNetCore.Components;
namespace GPW.CORE.WRKLOG.Components
{
public partial class ParetoRA : IDisposable
{
#region Public Properties
[Parameter]
public int idxFase
{
get
{
return idxFaseSel;
}
set
{
idxFaseSel = value;
}
}
[Parameter]
public EventCallback<int> ItemSelected { get; set; }
#endregion Public Properties
#region Public Methods
public void Dispose()
{
AppMServ.EA_SearchUpdated -= OnSearchUpdated;
}
#endregion Public Methods
#region Protected Properties
protected List<ParetoRegAttModel>? ListRecords { get; set; } = null;
#endregion Protected Properties
#region Protected Methods
protected string cssRiga(int idxFase)
{
string answ = "";
if (idxFase == idxFaseSel)
{
answ = "bg-info text-light";
}
return answ;
}
protected override async Task OnInitializedAsync()
{
await ReloadData();
AppMServ.EA_SearchUpdated += OnSearchUpdated;
}
protected void OnSearchUpdated()
{
var pUpd = Task.Run(async () => await ReloadData());
pUpd.Wait();
}
protected async Task ReloadData()
{
idxFaseSel = 0;
if (AppMServ.IdxDipendente > 0)
{
ListRecords = await GDataServ.ParetoRegAtt(AppMServ.IdxDipendente, ggPrec, maxSugg);
}
}
protected async Task SelectRecord(int idxFase)
{
idxFaseSel = idxFase;
await ItemSelected.InvokeAsync(idxFase);
}
#endregion Protected Methods
#region Private Fields
private int _ggPrec = 60;
private int _maxSugg = 10;
private int idxFaseSel = 0;
#endregion Private Fields
#region Private Properties
private int ggPrec
{
get
{
return _ggPrec;
}
set
{
_ggPrec = value;
var pUpd = Task.Run(async () => await ReloadData());
pUpd.Wait();
}
}
private int maxSugg
{
get
{
return _maxSugg;
}
set
{
_maxSugg = value;
var pUpd = Task.Run(async () => await ReloadData());
pUpd.Wait();
}
}
#endregion Private Properties
}
}
+2 -2
View File
@@ -9,7 +9,7 @@
@if (@CurrData.IdxFase > 0)
{
<div class="dropdown shadow-lg">
<button @onclick="() => Edit()" class="@buttonCss px-1 text-center border border-0 rounded-0 rounded-start btn btn-primary bg-opacity-75 btn-sm py-1 font-monospace2" style="font-size: @FontSize;" disabled="@(IsClipboard)">
<button @onclick="() => Edit()" class="@buttonCss px-1 text-center border border-0 rounded-0 rounded-start @btnClass bg-opacity-75 btn-sm py-1 font-monospace2" style="font-size: @FontSize;" disabled="@(IsClipboard)">
@if (@CurrData.OreTot > 1)
{
<div>@CurrData.Inizio.ToString("HH:mm")</div>
@@ -72,7 +72,7 @@
</div>
}
</div>
<div class="px-1 py-0 text-start textTrim flex-fill shadow">
<div class="px-1 py-0 text-start textTrim flex-fill shadow @blockClass">
<div class="text-dark" title="@CurrData.FasiNav?.ProgettoNav?.ClienteNav?.RagSociale - @CurrData.FasiNav?.ProgettoNav?.NomeProj"><b>@(trimLine($"{CurrData.FasiNav?.ProgettoNav?.ClienteNav?.RagSociale}", 60))</b>&nbsp;@(trimLine($"{CurrData.FasiNav?.ProgettoNav?.NomeProj}", 60))</div>
<div class="text-secondary" title="@CurrData.FasiNav?.DescrizioneFase">@(trimLine($"{CurrData.FasiNav?.DescrizioneFase}", 150))</div>
<div class="small" title="@CurrData.Descrizione">@(trimLine($"{CurrData.Descrizione}", 200))</div>
+52 -5
View File
@@ -4,7 +4,7 @@ using Microsoft.JSInterop;
namespace GPW.CORE.WRKLOG.Components
{
public partial class RegAtt
public partial class RegAtt : IDisposable
{
#region Public Properties
@@ -43,6 +43,15 @@ namespace GPW.CORE.WRKLOG.Components
#endregion Public Properties
#region Public Methods
public void Dispose()
{
AppMServ.EA_SearchUpdated -= AppMServ_EA_SearchUpdated;
}
#endregion Public Methods
#region Protected Fields
protected bool isSelected = false;
@@ -51,6 +60,42 @@ namespace GPW.CORE.WRKLOG.Components
#region Protected Properties
protected string blockClass
{
get
{
string btnClass = "";
bool highlight = false;
// recupero search
string SearchVal = AppMServ.SearchVal;
// calcolo valore highlight
if (!string.IsNullOrEmpty(SearchVal))
{
highlight = (CurrData.Descrizione.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase));
}
btnClass = highlight ? "bg-success bg-opacity-25" : "bg-light";
return btnClass;
}
}
protected string btnClass
{
get
{
string btnClass = "";
bool highlight = false;
// recupero search
string SearchVal = AppMServ.SearchVal;
// calcolo valore highlight
if (!string.IsNullOrEmpty(SearchVal))
{
highlight = (CurrData.Descrizione.Contains(SearchVal, StringComparison.InvariantCultureIgnoreCase));
}
btnClass = highlight ? "btn btn-success" : "btn btn-primary";
return btnClass;
}
}
protected string buttonCss
{
get => !IsClipboard ? "dropbtn" : "";
@@ -136,16 +181,13 @@ namespace GPW.CORE.WRKLOG.Components
/// </summary>
protected async void Edit()
{
// SOLO SE non una copia clipboard...
//if (!IsClipboard)
//{
isSelected = true;
await ItemSelected.InvokeAsync(CurrData);
//}
}
protected override void OnInitialized()
{
AppMServ.EA_SearchUpdated += AppMServ_EA_SearchUpdated;
isSelected = false;
}
@@ -250,6 +292,11 @@ namespace GPW.CORE.WRKLOG.Components
#region Private Methods
private void AppMServ_EA_SearchUpdated()
{
InvokeAsync(StateHasChanged);
}
/// <summary> Verifico coerenza date (inizio < fine) </summary> <param
/// name="modInizio">indica se la data modificata sia l'inizio</param>
private void checkCoerenzaDate(bool modInizio)
+212 -95
View File
@@ -3,52 +3,15 @@ using GPW.CORE.WRKLOG.Data;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using NLog;
using System;
using System.Diagnostics;
namespace GPW.CORE.WRKLOG.Components
{
public partial class RegAttEditor
{
#region Protected Fields
protected string _gruppoSel = "";
protected int _idxProj = 0;
protected bool vetoUpd = false;
#endregion Protected Fields
#region Private Properties
[Inject]
private MessageService AppMServ { get; set; } = null!;
[Inject]
private IJSRuntime JSRuntime { get; set; } = null!;
private bool VetoInsert
{
get => GDataServ.VetoInsert;
}
#endregion Private Properties
#region Protected Properties
protected List<AnagFasiModel> fasiList { get; set; } = new List<AnagFasiModel>();
[Inject]
protected GpwDataService GDataServ { get; set; } = null!;
protected List<AnagGruppiModel> gruppiList { get; set; } = new List<AnagGruppiModel>();
protected List<AnagProgettiModel> projList { get; set; } = new List<AnagProgettiModel>();
#endregion Protected Properties
#region Public Properties
private static Logger Log = LogManager.GetCurrentClassLogger();
[Parameter]
public RegAttivitaModel? currRecord
{
@@ -74,9 +37,8 @@ namespace GPW.CORE.WRKLOG.Components
}
else
{
// effettuo selezione manuale...
var currFase = await GDataServ.AnagFasiByKey(value.IdxFase);
// effettuo selezione manuale...
var currFase = await GDataServ.AnagFasiByKey(value.IdxFase);
if (currFase != null)
{
if (currFase.ProgettoNav != null)
@@ -117,26 +79,6 @@ namespace GPW.CORE.WRKLOG.Components
}
}
protected int idxFase
{
get
{
int answ = 0;
if (currRecord != null)
{
answ = currRecord.IdxFase;
}
return answ;
}
set
{
if (currRecord != null)
{
currRecord.IdxFase = value;
}
}
}
public int idxProj
{
get
@@ -162,47 +104,46 @@ namespace GPW.CORE.WRKLOG.Components
#endregion Public Properties
#region Private Methods
#region Protected Fields
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);
}
}
protected string _gruppoSel = "";
protected int _idxProj = 0;
protected bool vetoUpd = false;
/// <summary>
/// Verifico coerenza date (inizio < fine)
/// </summary>
/// <param name="modInizio">indica se la data modificata sia l'inizio</param>
private void checkCoerenzaDate(bool modInizio)
#endregion Protected Fields
#region Protected Properties
protected List<AnagFasiModel> fasiList { get; set; } = new List<AnagFasiModel>();
[Inject]
protected GpwDataService GDataServ { get; set; } = null!;
protected List<AnagGruppiModel> gruppiList { get; set; } = new List<AnagGruppiModel>();
protected int idxFase
{
bool inError = false;
// verifica presenza errori
if (currRecord != null)
get
{
inError = currRecord.Inizio >= currRecord.Fine;
if (inError)
int answ = 0;
if (currRecord != null)
{
// 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);
}
answ = currRecord.IdxFase;
}
return answ;
}
set
{
if (currRecord != null)
{
currRecord.IdxFase = value;
}
}
}
#endregion Private Methods
protected List<AnagProgettiModel> projList { get; set; } = new List<AnagProgettiModel>();
#endregion Protected Properties
#region Protected Methods
@@ -251,9 +192,18 @@ namespace GPW.CORE.WRKLOG.Components
/// </summary>
protected async void DoUpdate()
{
arrotondaMinuti();
bool checkOk = false;
checkOk = await arrotondaMinuti();
if (checkOk)
{
checkOk = await checkDayDiff();
if (checkOk)
{
checkOk = await checkDurata();
}
}
// aggiorno
if (currRecord != null)
if (currRecord != null && checkOk)
{
// controllo IdxDipendente
if (currRecord.IdxDipendente == 0)
@@ -314,6 +264,11 @@ namespace GPW.CORE.WRKLOG.Components
await ItemUpdated.InvokeAsync(true);
}
protected override async Task OnInitializedAsync()
{
await initConf();
}
protected async Task ReloadData(bool resetProj, bool resetFase)
{
gruppiList = await GDataServ.AnagGruppiUser(AppMServ.IdxDipendente);
@@ -363,5 +318,167 @@ namespace GPW.CORE.WRKLOG.Components
}
#endregion Protected Methods
#region Private Fields
private static Logger Log = LogManager.GetCurrentClassLogger();
private int lapseMinLimit = 1440;
private int lapseMinWarn = 480;
private int maxDayDiff = 60;
#endregion Private Fields
#region Private Properties
[Inject]
private MessageService AppMServ { get; set; } = null!;
[Inject]
private IJSRuntime JSRuntime { get; set; } = null!;
private bool VetoInsert
{
get => GDataServ.VetoInsert;
}
#endregion Private Properties
#region Private Methods
private async Task<bool> arrotondaMinuti()
{
bool answ = false;
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);
answ = await checkCoerenzaDate(true);
}
return answ;
}
/// <summary> Verifico coerenza date (inizio < fine) </summary> <param
/// name="modInizio">indica se la data modificata sia l'inizio</param>
private async Task<bool> checkCoerenzaDate(bool modInizio)
{
bool inError = false;
// verifica presenza errori
if (currRecord != null)
{
inError = currRecord.Inizio >= currRecord.Fine;
if (inError)
{
// chiedo se voglia proseguire...
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"L'inizio dell'attività inserita ({currRecord.Inizio:yyyy-MM-dd HH:mm}) è successivo alla fine dell'attività ({currRecord.Fine:yyyy-MM-dd HH:mm}). Vuoi proseguire correggendo l'inizio attività?"))
return false;
// 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);
}
}
}
return true;
}
/// <summary>
/// Verifica coerenza data inserita/data corrente, altrimenti segnala messaggio e ferma...
/// </summary>
private async Task<bool> checkDayDiff()
{
// verifica presenza errori
if (currRecord != null)
{
DateTime oggi = DateTime.Today;
// controllo coerenza inizio
if (Math.Abs(currRecord.Inizio.Subtract(oggi).TotalDays) > maxDayDiff)
{
// chiedo se voglia proseguire...
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"L'inizio dell'attività inserita ({currRecord.Inizio:yyyy-MM-dd HH:mm}) supera la soglia di coerenza con la data odierna: Sicuro di voler procedere con la modifica?"))
return false;
}
if (Math.Abs(currRecord.Fine.Subtract(oggi).TotalDays) > maxDayDiff)
{
// chiedo se voglia proseguire...
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"La fine dell'attività inserita ({currRecord.Fine:yyyy-MM-dd HH:mm}) supera la soglia di coerenza con la data odierna: Sicuro di voler procedere con la modifica?"))
return false;
}
// verifico non siano date future oltre limite di 1 gg..
if (currRecord.Inizio.Date > oggi)
{
await JSRuntime.InvokeVoidAsync("alert", $"Errore! La data-ora di inizio attività è su una data successiva a quella odierna. Impossibile salvare il record.");
return false;
}
if (currRecord.Fine.Date > oggi.AddDays(1))
{
await JSRuntime.InvokeVoidAsync("alert", $"Errore! La data-ora di fine attività è oltre il limite consentito. Impossibile salvare il record.");
return false;
}
}
return true;
}
/// <summary>
/// Verifica coerenza durata evento: se supera soglia warning segnala messaggio, se supera
/// soglia limite ferma e basta...
/// </summary>
private async Task<bool> checkDurata()
{
// verifica presenza errori
if (currRecord != null)
{
var durata = currRecord.Fine.Subtract(currRecord.Inizio).TotalMinutes;
// controllo coerenza inizio
if (durata > lapseMinLimit)
{
// BLOCCO prosecuzione...
await JSRuntime.InvokeVoidAsync("alert", $"Errore! La durata dell'attività inserita supera il limite consentito di {lapseMinLimit} minuti. Impossibile salvare il record.");
return false;
}
else if (durata > lapseMinWarn)
{
// chiedo se voglia proseguire...
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Attenzione! La durata dell'attività inserita supera il limite standard di {lapseMinWarn} minuti. Sicuro di voler procedere con la modifica?"))
return false;
}
}
return true;
}
/// <summary>
/// init valori da config
/// </summary>
/// <returns></returns>
private async Task initConf()
{
// leggo conf standard controllo RegAtt
var sMaxDayDiff = await GDataServ.ConfigGetKey("maxDayDiffRA");
if (sMaxDayDiff != null)
{
int.TryParse(sMaxDayDiff.valore, out maxDayDiff);
}
var sLapseMinWarn = await GDataServ.ConfigGetKey("lapseMinWarn");
if (sLapseMinWarn != null)
{
int.TryParse(sLapseMinWarn.valore, out lapseMinWarn);
}
var sLapseMinLimit = await GDataServ.ConfigGetKey("lapseMinLimit");
if (sLapseMinLimit != null)
{
int.TryParse(sLapseMinLimit.valore, out lapseMinLimit);
}
}
#endregion Private Methods
}
}
@@ -50,7 +50,7 @@ namespace GPW.CORE.WRKLOG.Components
protected MessageService AppMServ { get; set; } = null!;
[Inject]
protected GpwDataService DataService { get; set; } = null!;
protected GpwDataService GDataServ { get; set; } = null!;
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
@@ -67,7 +67,7 @@ namespace GPW.CORE.WRKLOG.Components
dtFine = dtInizio.AddYears(2);
currRecord.IdxDipendente = AppMServ.IdxDipendente;
CodGiust = "";
var rawGiust = await DataService.AnagGiust();
var rawGiust = await GDataServ.AnagGiust();
if (rawGiust != null)
{
ListGiust = rawGiust.Where(x => x.showReq).ToList();
@@ -185,7 +185,7 @@ namespace GPW.CORE.WRKLOG.Components
await Task.Delay(1);
await InvokeAsync(StateHasChanged);
var updTask = Task.Run(async () =>
await DataService.RegRichiesteDelete(selItem)
await GDataServ.RegRichiesteDelete(selItem)
);
await updTask;
sendDataVal = 100;
@@ -209,12 +209,12 @@ namespace GPW.CORE.WRKLOG.Components
private async Task initConf()
{
// leggo conf standard giorni permessi/ferie
var sNumDayFerieRichAntic = await DataService.ConfigGetKey("NumDayFerieRichAntic");
var sNumDayFerieRichAntic = await GDataServ.ConfigGetKey("NumDayFerieRichAntic");
if (sNumDayFerieRichAntic != null)
{
int.TryParse(sNumDayFerieRichAntic.valore, out NumDayFerieRichAntic);
}
var sNumDayPermMax = await DataService.ConfigGetKey("NumDayPermMax");
var sNumDayPermMax = await GDataServ.ConfigGetKey("NumDayPermMax");
if (sNumDayPermMax != null)
{
int.TryParse(sNumDayPermMax.valore, out NumDayPermMax);
@@ -257,7 +257,7 @@ namespace GPW.CORE.WRKLOG.Components
sendDataNextVal = 90;
await Task.Delay(1);
var updTask = Task.Run(async () =>
await DataService.RegRichiesteUpsert(currRecord)
await GDataServ.RegRichiesteUpsert(currRecord)
);
await updTask;
// effettuo insert...
@@ -311,7 +311,7 @@ namespace GPW.CORE.WRKLOG.Components
ListRecords = null;
await initConf();
await Task.Delay(1);
ListRecords = await DataService.RegRichiesteGetByDip(AppMServ.IdxDipendente, dtInizio, dtFine);
ListRecords = await GDataServ.RegRichiesteGetByDip(AppMServ.IdxDipendente, dtInizio, dtFine);
}
private async Task toggleAddNew()
+177 -44
View File
@@ -727,49 +727,6 @@ namespace GPW.CORE.WRKLOG.Data
return await Task.FromResult(keyResult);
}
/// <summary>
/// Recupera l'elenco dei dettagli giornalieri attività di un dipendente, dato periodo riferimento
/// </summary>
/// <param name="idxDipendente">Dipendente interessato</param>
/// <param name="dtInizio">Data di riferimento (ultima/corrente)</param>
/// <param name="dtFine">NUm settimane precedenti da recuperare</param>
/// <returns></returns>
public async Task<List<DailyDataDTO>> DailyDetails(int idxDipendente, DateTime dtInizio, DateTime dtFine)
{
string source = "DB";
List<DailyDataDTO>? dbResult = new List<DailyDataDTO>();
string currKey = $"{rKeyDailyData}:{idxDipendente}:{dtInizio.ToString("yyyy-MM-dd")}:{dtFine.ToString("yyyy-MM-dd")}";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
if (!string.IsNullOrEmpty(rawData))
{
source = "REDIS";
var tempResult = JsonConvert.DeserializeObject<List<DailyDataDTO>>(rawData);
if (tempResult == null)
{
dbResult = new List<DailyDataDTO>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.DailyDetails(idxDipendente, dtInizio, dtFine);
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, FastCache);
}
if (dbResult == null)
{
dbResult = new List<DailyDataDTO>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"DailyDetails | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Recupera l'elenco dei dettaglio giornaliero attività di tutti i dipendenti, dato giorno riferimento
/// </summary>
@@ -811,7 +768,95 @@ namespace GPW.CORE.WRKLOG.Data
Log.Debug($"DailyDetailAllDip | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Recupera l'elenco delle ultime attività inserite da un dipendente
/// </summary>
/// <param name="idxDipendente">Dipendente interessato</param>
/// <param name="numRec">Num record da recuperare</param>
/// <returns></returns>
public async Task<List<RegAttivitaModel>> UserLastRec(int idxDipendente, int numRec)
{
string source = "DB";
DateTime oggi = DateTime.Today;
List<RegAttivitaModel>? dbResult = new List<RegAttivitaModel>();
string currKey = $"{rKeyLastRegAtt}:{idxDipendente}:{oggi.ToString("yyyy-MM-dd")}:{numRec}";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
if (!string.IsNullOrEmpty(rawData))
{
source = "REDIS";
var tempResult = JsonConvert.DeserializeObject<List<RegAttivitaModel>>(rawData);
if (tempResult == null)
{
dbResult = new List<RegAttivitaModel>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.UserLastRec(idxDipendente, numRec);
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, FastCache);
}
if (dbResult == null)
{
dbResult = new List<RegAttivitaModel>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"UserLastRec | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
/// <summary>
/// Recupera l'elenco dei dettagli giornalieri attività di un dipendente, dato periodo riferimento
/// </summary>
/// <param name="idxDipendente">Dipendente interessato</param>
/// <param name="dtInizio">Data inizio periodo</param>
/// <param name="dtFine">Data fine periodo</param>
/// <returns></returns>
public async Task<List<DailyDataDTO>> DailyDetails(int idxDipendente, DateTime dtInizio, DateTime dtFine)
{
string source = "DB";
List<DailyDataDTO>? dbResult = new List<DailyDataDTO>();
string currKey = $"{rKeyDailyData}:{idxDipendente}:{dtInizio.ToString("yyyy-MM-dd")}:{dtFine.ToString("yyyy-MM-dd")}";
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
string? rawData = await redisDb.StringGetAsync(currKey);
if (!string.IsNullOrEmpty(rawData))
{
source = "REDIS";
var tempResult = JsonConvert.DeserializeObject<List<DailyDataDTO>>(rawData);
if (tempResult == null)
{
dbResult = new List<DailyDataDTO>();
}
else
{
dbResult = tempResult;
}
}
else
{
dbResult = dbController.DailyDetails(idxDipendente, dtInizio, dtFine);
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, FastCache);
}
if (dbResult == null)
{
dbResult = new List<DailyDataDTO>();
}
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Debug($"DailyDetails | {source} in: {ts.TotalMilliseconds} ms");
return dbResult;
}
public string DeriptData(string encData)
{
@@ -1015,6 +1060,7 @@ namespace GPW.CORE.WRKLOG.Data
dbController.RegAttDelete(currItem);
// invalido la cache...
await ExecFlushRedisPattern($"{rKeyDailyData}:*");
await ExecFlushRedisPattern($"{rKeyLastRegAtt}:*");
await ExecFlushRedisPattern($"{rKeyParetoRegAtt}:*");
await ExecFlushRedisPattern($"{rKeyWeekStats}:*");
answ = true;
@@ -1045,6 +1091,7 @@ namespace GPW.CORE.WRKLOG.Data
dbController.RegAttUpdate(currItem);
// invalido la cache...
await ExecFlushRedisPattern($"{rKeyDailyData}:*");
await ExecFlushRedisPattern($"{rKeyLastRegAtt}:*");
await ExecFlushRedisPattern($"{rKeyParetoRegAtt}:*");
await ExecFlushRedisPattern($"{rKeyWeekStats}:*");
answ = true;
@@ -1067,8 +1114,19 @@ namespace GPW.CORE.WRKLOG.Data
answ = dbController.RegMalattieDelete(currItem);
if (answ)
{
// recupero info dipendente
var listDip = await DipendentiGetAll();
var currDip = listDip.FirstOrDefault(x => x.IdxDipendente == currItem.IdxDipendente);
// gestisco email notifica...
string emailDest = _configuration["MailDest:InfoMal"];
if (currDip.idxResp > 0)
{
string mailResp = await emailResp(currDip.idxResp);
if (!string.IsNullOrEmpty(mailResp) && !emailDest.Contains(mailResp))
{
emailDest += $",{mailResp}";
}
}
string urlRedir = _configuration["AdmApp:LinkMal"];
StringBuilder sbMain = new StringBuilder();
DateTime adesso = DateTime.Now;
@@ -1199,6 +1257,14 @@ namespace GPW.CORE.WRKLOG.Data
var currDip = listDip.FirstOrDefault(x => x.IdxDipendente == currItem.IdxDipendente);
// gestisco email notifica...
string emailDest = _configuration["MailDest:InfoMal"];
if (currDip.idxResp > 0)
{
string mailResp = await emailResp(currDip.idxResp);
if (!string.IsNullOrEmpty(mailResp) && !emailDest.Contains(mailResp))
{
emailDest += $",{mailResp}";
}
}
string urlRedir = _configuration["AdmApp:LinkMal"];
StringBuilder sbMain = new StringBuilder();
DateTime adesso = DateTime.Now;
@@ -1238,8 +1304,19 @@ namespace GPW.CORE.WRKLOG.Data
answ = dbController.RegRichiesteDelete(currItem);
if (answ)
{
// recupero info dipendente
var listDip = await DipendentiGetAll();
var currDip = listDip.FirstOrDefault(x => x.IdxDipendente == currItem.IdxDipendente);
// gestisco email notifica...
string emailDest = _configuration["MailDest:InfoRich"];
if (currDip.idxResp > 0)
{
string mailResp = await emailResp(currDip.idxResp);
if (!string.IsNullOrEmpty(mailResp) && !emailDest.Contains(mailResp))
{
emailDest += $",{mailResp}";
}
}
string urlRedir = _configuration["AdmApp:LinkRich"];
StringBuilder sbMain = new StringBuilder();
DateTime adesso = DateTime.Now;
@@ -1261,6 +1338,9 @@ namespace GPW.CORE.WRKLOG.Data
// invalido la cache...
await ExecFlushRedisPattern($"{rKeyDipendenti}:*");
await ExecFlushRedisPattern($"{rKeyDailyData}:*");
await ExecFlushRedisPattern($"{rKeyLastRegAtt}:*");
await ExecFlushRedisPattern($"{rKeyParetoRegAtt}:*");
await ExecFlushRedisPattern($"{rKeyWeekStats}:*");
}
catch
{ }
@@ -1330,6 +1410,14 @@ namespace GPW.CORE.WRKLOG.Data
var currDip = listDip.FirstOrDefault(x => x.IdxDipendente == currItem.IdxDipendente);
// gestisco email notifica...
string emailDest = _configuration["MailDest:InfoRich"];
if (currDip.idxResp > 0)
{
string mailResp = await emailResp(currDip.idxResp);
if (!string.IsNullOrEmpty(mailResp) && !emailDest.Contains(mailResp))
{
emailDest += $",{mailResp}";
}
}
string urlRedir = _configuration["AdmApp:LinkRich"];
StringBuilder sbMain = new StringBuilder();
DateTime adesso = DateTime.Now;
@@ -1351,6 +1439,9 @@ namespace GPW.CORE.WRKLOG.Data
// invalido la cache...
await ExecFlushRedisPattern($"{rKeyDipendenti}:*");
await ExecFlushRedisPattern($"{rKeyDailyData}:*");
await ExecFlushRedisPattern($"{rKeyLastRegAtt}:*");
await ExecFlushRedisPattern($"{rKeyParetoRegAtt}:*");
await ExecFlushRedisPattern($"{rKeyWeekStats}:*");
}
catch
{ }
@@ -1470,6 +1561,7 @@ namespace GPW.CORE.WRKLOG.Data
// invalido la cache...
await ExecFlushRedisPattern($"{rKeyRilTemp}:*");
await ExecFlushRedisPattern($"{rKeyDailyData}:*");
await ExecFlushRedisPattern($"{rKeyLastRegAtt}:*");
await ExecFlushRedisPattern($"{rKeyParetoRegAtt}:*");
await ExecFlushRedisPattern($"{rKeyWeekStats}:*");
answ = true;
@@ -1538,6 +1630,7 @@ namespace GPW.CORE.WRKLOG.Data
#region Protected Fields
protected const string passPhrase = "6E1DA6B4-4647-4578-AB4F-01360E2B7E68";
protected const string redisBaseAddr = "WrkLog";
protected const string rKeyAKV = $"{redisBaseAddr}:Cache:AKV";
@@ -1547,22 +1640,41 @@ namespace GPW.CORE.WRKLOG.Data
protected const string rKeyCalcOreFase = $"{redisBaseAddr}:Cache:CalcOreFase";
protected const string rKeyCalcOreProj = $"{redisBaseAddr}:Cache:CalcOreProj";
protected const string rKeyCalendari = $"{redisBaseAddr}:Cache:Calendari";
protected const string rKeyCliAll = $"{redisBaseAddr}:Cache:CliAll";
protected const string rKeyConfig = $"{redisBaseAddr}:Cache:Config";
protected const string rKeyDailyData = $"{redisBaseAddr}:Cache:DailyData";
protected const string rKeyLastRegAtt = $"{redisBaseAddr}:Cache:LastRegAtt";
protected const string rKeyDipendenti = $"{redisBaseAddr}:Cache:Dipendenti";
protected const string rKeyFasiAll = $"{redisBaseAddr}:Cache:FasiAll";
protected const string rKeyFasiByProj = $"{redisBaseAddr}:Cache:FasiByProj";
protected const string rKeyFasiSearch = $"{redisBaseAddr}:Cache:FasiSearch";
protected const string rKeyGiust = $"{redisBaseAddr}:Cache:Giustif";
protected const string rKeyGrpAll = $"{redisBaseAddr}:Cache:GrpAll";
protected const string rKeyGrpUser = $"{redisBaseAddr}:Cache:GrpUser";
protected const string rKeyParetoRegAtt = $"{redisBaseAddr}:Cache:ParetoRegAtt";
protected const string rKeyProjAll = $"{redisBaseAddr}:Cache:ProjAll";
protected const string rKeyReportData = $"{redisBaseAddr}:Cache:ReportData";
protected const string rKeyRilTemp = $"{redisBaseAddr}:Cache:RilTemp";
protected const string rKeyStatPresDay = $"{redisBaseAddr}:Cache:StatPresDay";
protected const string rKeyVC19 = $"{redisBaseAddr}:Cache:VC19";
protected const string rKeyVetoIns = $"{redisBaseAddr}:Cache:VetoInsert";
@@ -1577,6 +1689,27 @@ namespace GPW.CORE.WRKLOG.Data
#region Protected Methods
/// <summary>
/// Calcola email del responsabile dato idx
/// </summary>
/// <param name="idxResp"></param>
/// <returns></returns>
protected async Task<string> emailResp(int idxResp)
{
string answ = "";
var listDip = await DipendentiGetAll();
// recupero email resp...
if (idxResp > 0)
{
var recResp = listDip.FirstOrDefault(x => x.IdxDipendente == idxResp);
if (recResp != null)
{
answ = $"{recResp.Email}";
}
}
return answ;
}
/// <summary>
/// Invio email tramite libreria
/// </summary>
+146 -131
View File
@@ -3,6 +3,7 @@ using GPW.CORE.Data.DbModels;
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using RestSharp;
using StackExchange.Redis;
using System.Text;
using System.Web;
@@ -13,50 +14,6 @@ namespace GPW.CORE.WRKLOG.Data
/// </summary>
public class LicenseService
{
#region Private Fields
private static IConfiguration? _configuration;
private static ILogger<LicenseService>? _logger;
/// <summary>
/// URL dell'API x chiamate gestione licenze
/// </summary>
private static string apiUrl = "https://liman.egalware.com/ELM.API/";
private readonly IDistributedCache distributedCache;
/// <summary>
/// Elenco obj in cache
/// </summary>
private List<string> cachedDataList = new List<string>();
/// <summary>
/// Fattorte conversione cache sliding --> 1 h
/// </summary>
private int cacheFact = 12;
/// <summary>
/// Durata assoluta massima della cache IN SECONDI
/// </summary>
private int chAbsExp = 60 * 5;
/// <summary>
/// Durata della cache IN SECONDI in modalità inattiva (non acceduta) prima di venire rimossa
/// NON estende oltre il tempo massimo di validità della cache (cacheTtlLong)
/// </summary>
private int chSliExp = 60 * 1;
#endregion Private Fields
#region Protected Fields
/// <summary>
/// Chiave redis x attivazioni della licenza
/// </summary>
protected const string rKeyAttByLic = "LongCache:AttByLic";
#endregion Protected Fields
#region Public Constructors
/// <summary>
@@ -83,9 +40,7 @@ namespace GPW.CORE.WRKLOG.Data
#region Public Properties
public List<LiManObj.AttivazioneDTO> ActivList { get; set; } = new List<LiManObj.AttivazioneDTO>();
public List<AnagKeyValueModel> AKVList { get; set; } = new List<AnagKeyValueModel>();
public string Applicazione { get; set; } = "";
public bool HasActivData
@@ -114,9 +69,7 @@ namespace GPW.CORE.WRKLOG.Data
}
public DateTime infoExpiry { get; set; } = DateTime.Today.AddDays(1);
public string Installazione { get; set; } = "";
public string MasterKey { get; set; } = "";
public bool ValidData
@@ -131,51 +84,102 @@ namespace GPW.CORE.WRKLOG.Data
#endregion Public Properties
#region Private Methods
#region Public Methods
/// <summary>
/// Opzioni cache con moltiplicatore durata risp durata base (1/5 minuti)
/// </summary>
/// <param name="multFact"></param>
/// <returns></returns>
private DistributedCacheEntryOptions cacheOpt(int multFact)
public async Task<List<LiManObj.AttivazioneDTO>> ActivListCache()
{
var numSecAbsExp = chAbsExp * multFact;
var numSecSliExp = chSliExp * multFact;
return new DistributedCacheEntryOptions().SetAbsoluteExpiration(DateTime.Now.AddSeconds(numSecAbsExp)).SetSlidingExpiration(TimeSpan.FromSeconds(numSecSliExp));
}
/// <summary>
/// Elenco attivazioni attuali
/// </summary>
private async Task<List<LiManObj.AttivazioneDTO>?> OnlineActivationList()
{
List<LiManObj.AttivazioneDTO>? answ = new List<LiManObj.AttivazioneDTO>();
// cerco online
RestClient client = new RestClient(apiUrl);
//client.Authenticator = new HttpBasicAuthenticator("username", "password");
string MKeyEnc = HttpUtility.UrlEncode(MasterKey);
var request = new RestRequest($"/api/attivazioni/?chiave={MKeyEnc}", Method.Get);
var response = await client.GetAsync(request);
// controllo risposta
if (response.StatusCode == System.Net.HttpStatusCode.OK)
List<LiManObj.AttivazioneDTO> dbResult = new List<LiManObj.AttivazioneDTO>();
string cacheKey = $"{rKeyAttByLic}:{MasterKey}";
trackCache(cacheKey);
string rawData = await getRSV(cacheKey);
if (!string.IsNullOrEmpty(rawData))
{
// salvo in redis contenuto serializzato
string rawData = $"{response.Content}";
answ = JsonConvert.DeserializeObject<List<LiManObj.AttivazioneDTO>?>(rawData);
var cacheRes = JsonConvert.DeserializeObject<List<LiManObj.AttivazioneDTO>?>(rawData);
if (cacheRes != null)
{
dbResult = cacheRes;
}
}
return await Task.FromResult(answ);
return await Task.FromResult(dbResult);
}
private void ReportUpdated()
/// <summary>
/// Init della classe con variabili di base da Redis/DB
/// </summary>
public bool InitAkv()
{
bool fatto = false;
Installazione = getAVKStr("installazione");
MasterKey = getAVKStr(Installazione);
fatto = !string.IsNullOrEmpty($"{Installazione}{MasterKey}");
return fatto;
}
/// <summary>
/// Init della classe con variabili di base da Redis/DB
/// </summary>
public async Task<bool> RefreshLicense()
{
bool fatto = false;
var onlineAct = await OnlineActivationList();
if (onlineAct != null)
{
if (onlineAct.Count > 0)
{
// scadenza info a 15 gg...
int numDays = 15;
infoExpiry = DateTime.Now.AddDays(numDays);
ActivList = onlineAct;
fatto = await setActivList(onlineAct, numDays);
}
}
await Task.Delay(1);
return fatto;
}
public async Task<bool> resetActivList()
{
await Task.Delay(1);
string cacheKey = $"{rKeyAttByLic}:{MasterKey}";
bool answ = false;
try
{
await distributedCache.RemoveAsync(cacheKey);
if (EA_InfoUpdated != null)
{
EA_InfoUpdated?.Invoke();
}
}
catch
{ }
return answ;
}
public async Task<bool> setActivList(List<LiManObj.AttivazioneDTO> newActList, int numDays)
{
bool fatto = false;
string cacheKey = $"{rKeyAttByLic}:{MasterKey}";
var rawData = JsonConvert.SerializeObject(newActList);
await setRSV(cacheKey, rawData, numDays * cacheFact * 24);
fatto = true;
if (EA_InfoUpdated != null)
{
EA_InfoUpdated?.Invoke();
}
return fatto;
}
#endregion Private Methods
#endregion Public Methods
#region Protected Fields
/// <summary>
/// Chiave redis x attivazioni della licenza
/// </summary>
protected const string rKeyAttByLic = "LongCache:AttByLic";
#endregion Protected Fields
#region Protected Methods
@@ -260,74 +264,85 @@ namespace GPW.CORE.WRKLOG.Data
#endregion Protected Methods
#region Public Methods
#region Private Fields
public async Task<List<LiManObj.AttivazioneDTO>> ActivListCache()
private static IConfiguration? _configuration;
private static ILogger<LicenseService>? _logger;
/// <summary>
/// URL dell'API x chiamate gestione licenze
/// </summary>
private static string apiUrl = "https://liman.egalware.com/ELM.API/";
private readonly IDistributedCache distributedCache;
/// <summary>
/// Elenco obj in cache
/// </summary>
private List<string> cachedDataList = new List<string>();
/// <summary>
/// Fattorte conversione cache sliding --&gt; 1 h
/// </summary>
private int cacheFact = 12;
/// <summary>
/// Durata assoluta massima della cache IN SECONDI
/// </summary>
private int chAbsExp = 60 * 5;
/// <summary>
/// Durata della cache IN SECONDI in modalità inattiva (non acceduta) prima di venire
/// rimossa NON estende oltre il tempo massimo di validità della cache (cacheTtlLong)
/// </summary>
private int chSliExp = 60 * 1;
#endregion Private Fields
#region Private Methods
/// <summary>
/// Opzioni cache con moltiplicatore durata risp durata base (1/5 minuti)
/// </summary>
/// <param name="multFact"></param>
/// <returns></returns>
private DistributedCacheEntryOptions cacheOpt(int multFact)
{
List<LiManObj.AttivazioneDTO> dbResult = new List<LiManObj.AttivazioneDTO>();
string cacheKey = $"{rKeyAttByLic}:{MasterKey}";
trackCache(cacheKey);
string rawData = await getRSV(cacheKey);
if (!string.IsNullOrEmpty(rawData))
{
var cacheRes = JsonConvert.DeserializeObject<List<LiManObj.AttivazioneDTO>?>(rawData);
if (cacheRes != null)
{
dbResult = cacheRes;
}
}
return await Task.FromResult(dbResult);
var numSecAbsExp = chAbsExp * multFact;
var numSecSliExp = chSliExp * multFact;
return new DistributedCacheEntryOptions().SetAbsoluteExpiration(DateTime.Now.AddSeconds(numSecAbsExp)).SetSlidingExpiration(TimeSpan.FromSeconds(numSecSliExp));
}
/// <summary>
/// Init della classe con variabili di base da Redis/DB
/// Elenco attivazioni attuali
/// </summary>
public bool InitAkv()
private async Task<List<LiManObj.AttivazioneDTO>?> OnlineActivationList()
{
bool fatto = false;
Installazione = getAVKStr("installazione");
MasterKey = getAVKStr(Installazione);
fatto = !string.IsNullOrEmpty($"{Installazione}{MasterKey}");
return fatto;
}
/// <summary>
/// Init della classe con variabili di base da Redis/DB
/// </summary>
public async Task<bool> RefreshLicense()
{
bool fatto = false;
var onlineAct = await OnlineActivationList();
if (onlineAct != null)
List<LiManObj.AttivazioneDTO>? answ = new List<LiManObj.AttivazioneDTO>();
// cerco online
RestClient client = new RestClient(apiUrl);
//client.Authenticator = new HttpBasicAuthenticator("username", "password");
string MKeyEnc = HttpUtility.UrlEncode(MasterKey);
var request = new RestRequest($"/api/attivazioni/?chiave={MKeyEnc}", Method.Get);
var response = await client.GetAsync(request);
// controllo risposta
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
if (onlineAct.Count > 0)
{
// scadenza info a 15 gg...
int numDays = 15;
infoExpiry = DateTime.Now.AddDays(numDays);
ActivList = onlineAct;
fatto = await setActivList(onlineAct, numDays);
}
// salvo in redis contenuto serializzato
string rawData = $"{response.Content}";
answ = JsonConvert.DeserializeObject<List<LiManObj.AttivazioneDTO>?>(rawData);
}
await Task.Delay(1);
return fatto;
return await Task.FromResult(answ);
}
public async Task<bool> setActivList(List<LiManObj.AttivazioneDTO> newActList, int numDays)
private void ReportUpdated()
{
bool fatto = false;
string cacheKey = $"{rKeyAttByLic}:{MasterKey}";
var rawData = JsonConvert.SerializeObject(newActList);
await setRSV(cacheKey, rawData, numDays * cacheFact * 24);
fatto = true;
if (EA_InfoUpdated != null)
{
EA_InfoUpdated?.Invoke();
}
return fatto;
}
#endregion Public Methods
#endregion Private Methods
}
}
+3 -1
View File
@@ -189,13 +189,15 @@ namespace GPW.CORE.WRKLOG.Data
}
}
#if false
private void ReportSearch()
{
if (EA_SearchUpdated != null)
{
EA_SearchUpdated?.Invoke();
}
}
}
#endif
#endregion Private Methods
}
+1 -1
View File
@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>3.0.2403.0715</Version>
<Version>3.0.2408.1910</Version>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
+69
View File
@@ -0,0 +1,69 @@
@page "/ActHistory"
@attribute [Authorize]
@using GPW.CORE.WRKLOG.Components
<div class="card">
<div class="card-header table-primary py-1">
<div class="row">
<div class="col-8 col-lg-9 text-start">
<h2><i class="fas fa-home"></i> Last Activity Inserted</h2>
</div>
<div class="col-4 col-lg-3 text-end">
<div class="input-group mb-3">
<span class="input-group-text">Mostra Ultimi</span>
<input type="text" class="form-control" @bind="@numRecLast" />
<span class="input-group-text">record</span>
</div>
</div>
</div>
</div>
<div class="card-body">
@if (ListRecords == null || isLoading)
{
<LoadingData></LoadingData>
}
else
{
<div class="row">
<div class="col-6">
@foreach (var item in ListRecords)
{
<div class="@getItemCss(item)">
<DettAtt CurrData="item" Periodo="@(item.Durata.TotalHours)" ListFasi="@ListFasi" IdxDipSel="@IdxDipendente" ItemSelected="ReportSelected" ItemDeleted="() => ReportDeleted()" MaxChar="@MaxChar" ShowDate="true"></DettAtt>
</div>
}
</div>
</div>
}
</div>
<div class="card-footer py-1">
<EgwCoreLib.Razor.DataPager PageSize="@numRecord" currPage="@currPage" numRecordChanged="SetNumRec" numPageChanged="SetPage" totalCount="@numRecLast" showLoading="@isLoading"></EgwCoreLib.Razor.DataPager>
<div class="d-flex justify-content-between">
<div class="py-1 px-2">
<span>@lastRefresh.ToString("HH:mm:ss")</span> <i class="far fa-clock"></i>
</div>
<div class="py-1 px-2">
@if (clonedRA != null)
{
<div class="d-flex">
<div class="px-2 flex-grow-1">
<RegAtt CurrData="@clonedRA" Periodo="@periodoClonato" ListFasi="@ListFasi" ItemSelected="ReportSelect" IdxDipSel="@IdxDipendente" IsClipboard="true"></RegAtt>
</div>
<div class="px-2">
<button class="btn w-100 btn-warning" @onclick="ResetClone" title="Reset Clipboard">
<i class="fas fa-clipboard"></i><br>
<i class="far fa-times-circle"></i>
</button>
</div>
</div>
}
</div>
</div>
</div>
</div>
<div class="my-4">&nbsp;</div>
+270
View File
@@ -0,0 +1,270 @@
using GPW.CORE.Data;
using GPW.CORE.Data.DbModels;
using GPW.CORE.Data.DTO;
using GPW.CORE.WRKLOG.Data;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace GPW.CORE.WRKLOG.Pages
{
/// <summary>
/// Gestione pagina principale del planner
///
/// modale: vedere qui: https://gist.github.com/conficient/ba98d1662c659e170ec16650acea05c8
/// </summary>
//[Authorize(Roles = "SuperAdmin, Admin")]
public partial class ActHistory : IDisposable
{
#region Public Properties
public int MaxChar { get; set; } = 30;
#endregion Public Properties
#region Public Methods
public void Dispose()
{
UITimer?.Dispose();
}
#endregion Public Methods
#region Protected Fields
protected DateTime lastRefresh = DateTime.Now;
protected int numRecLast = 100;
/// <summary>
/// Timer per refresh dati
///
/// https://stackoverflow.com/questions/63060065/blazor-timer-call-async-api-task-to-update-ui https://www.janduniec.blog/index.php/100/timers-in-blazor/
/// </summary>
protected System.Timers.Timer UITimer = new System.Timers.Timer();
#endregion Protected Fields
#region Protected Properties
protected string actionCss
{
get => isLoading ? "disabled" : "";
}
[Inject]
protected MessageService AppMServ { get; set; } = null!;
protected RegAttivitaModel? clonedRA
{
get
{
return AppMServ.clonedRA;
}
set
{
AppMServ.clonedRA = value;
}
}
[Inject]
protected GpwDataService DataService { get; set; } = null!;
/// <summary>
/// Dipendente corrente
/// </summary>
protected int IdxDipendente
{
get
{
return AppMServ.IdxDipendente;
}
}
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
protected List<AnagFasiModel> ListFasi { get; set; } = null!;
[Inject]
protected NavigationManager NavManager { get; set; } = null!;
#endregion Protected Properties
#region Protected Methods
protected string getItemCss(RegAttivitaModel itemRecord)
{
string answ = "";
if (currRecord != null)
{
if (itemRecord.Equals(currRecord))
{
answ = "bg-info bg-opacity-25";
}
}
return answ;
}
protected override async Task OnInitializedAsync()
{
ListRecords = null;
await InvokeAsync(StateHasChanged);
await Task.Delay(1);
await TryReload();
// avvio un timer infinito x refresh pagina COMUNQUE ogni 60 sec anche non succedesse nulla...
UITimer = new System.Timers.Timer
{
Interval = 1000 * 60,
AutoReset = true,
Enabled = true
};
UITimer.Elapsed += async (s, ea) => await TryReload();
}
/// <summary>
/// Indico item selezionato
/// </summary>
protected async void ReportDeleted()
{
isLoading = true;
currRecord = null;
await InvokeAsync(ReloadData);
await Task.Delay(50);
await InvokeAsync(StateHasChanged);
}
/// <summary>
/// Indico item selezionato
/// </summary>
protected void ReportSelect(RegAttivitaModel selRecord)
{
//selPeriod = false;
// recupero attivita selezionata in curr record
currRecord = selRecord;
}
/// <summary>
/// Indico item selezionato
/// </summary>
protected async void ReportSelected(RegAttivitaModel selRecord)
{
currRecord = selRecord;
await Task.Delay(1);
//await InvokeAsync(ReloadData);
}
protected void ResetClone()
{
clonedRA = null;
}
protected async Task SetNumRec(int newNum)
{
numRecord = newNum;
currPage = 1;
await InvokeAsync(ReloadData);
}
protected async Task SetPage(int newNum)
{
currPage = newNum;
await InvokeAsync(ReloadData);
}
/// <summary>
/// Gestione refresh condizionale
/// </summary>
/// <returns></returns>
protected async Task TryReload()
{
await Task.Delay(1);
await ReloadData();
await InvokeAsync(StateHasChanged);
}
#endregion Protected Methods
#region Private Fields
private RegAttivitaModel? currRecord = null;
private List<RegAttivitaModel>? ListRecords = null;
#endregion Private Fields
#region Private Properties
private int currPage { get; set; } = 1;
private bool isLoading { get; set; } = false;
private int numRecord { get; set; } = 5;
private double periodoClonato
{
get
{
double answ = 8;
// se ho record clonato --> uso quello
if (clonedRA != null)
{
answ = clonedRA.Durata.TotalHours;
}
return answ;
}
}
#endregion Private Properties
#region Private Methods
private string ConvOre(double oreComm)
{
TimeSpan tSpan = TimeSpan.FromHours(oreComm);
return $"{tSpan.Hours}h {tSpan.Minutes}'";
}
private double oreLav(DailyDataDTO DayDTO)
{
double answ = 0;
if (DayDTO != null && DayDTO.TimbrExpl != null && DayDTO.TimbrExpl.HLav != null)
{
answ = (double)DayDTO.TimbrExpl.HLav;
if (DayDTO.DtRif == DateTime.Today)
{
if (DayDTO.ListTimbr != null)
{
// aggiungo ultima timb fino ad adesso...
var lastIn = DayDTO.ListTimbr.Where(x => x.Entrata == true).OrderByDescending(x => x.DataOra).FirstOrDefault();
var lastOut = DayDTO.ListTimbr.Where(x => x.Entrata == false).OrderByDescending(x => x.DataOra).FirstOrDefault();
// se MANCA timb uscita finale...
if (lastIn != null)
{
if (lastOut == null || lastOut.DataOra < lastIn.DataOra)
{
DateTime adesso = DateTime.Now;
answ += adesso.Subtract(lastIn.DataOra).TotalHours;
}
}
}
}
}
return answ;
}
private async Task ReloadData()
{
isLoading = true;
UITimer.Stop();
var searcRecords = await DataService.UserLastRec(IdxDipendente, numRecLast);
ListRecords = searcRecords
.Skip((currPage - 1) * numRecord)
.Take(numRecord)
.ToList();
lastRefresh = DateTime.Now;
UITimer.Start();
isLoading = false;
}
#endregion Private Methods
}
}
@@ -1,4 +1,4 @@
@page "/ListPlan"
@page "/DayList"
@attribute [Authorize]
@using GPW.CORE.WRKLOG.Components
@@ -13,7 +13,7 @@ namespace GPW.CORE.WRKLOG.Pages
/// modale: vedere qui: https://gist.github.com/conficient/ba98d1662c659e170ec16650acea05c8
/// </summary>
//[Authorize(Roles = "SuperAdmin, Admin")]
public partial class ListPlan : IDisposable
public partial class DayList : IDisposable
{
#region Public Methods
@@ -246,13 +246,6 @@ namespace GPW.CORE.WRKLOG.Pages
Enabled = true
};
UITimer.Elapsed += async (s, ea) => await TryReload();
AppMServ.EA_SearchUpdated += OnSearchUpdated;
}
protected void OnSearchUpdated()
{
var pUpd = Task.Run(async () => await TryReload());
pUpd.Wait();
}
/// <summary>
@@ -281,6 +274,19 @@ namespace GPW.CORE.WRKLOG.Pages
await ReloadData();
}
protected async Task ReportDeleted()
{
selPeriod = false;
showDay = false;
showTemp = false;
showTimb = false;
currRecord = null;
ListTimbr = null;
SelDayDTO = null;
await InitData(true);
await ReloadData();
}
/// <summary>
/// Indico item selezionato
/// </summary>
@@ -321,19 +327,6 @@ namespace GPW.CORE.WRKLOG.Pages
await ReloadData();
}
protected async Task ReportDeleted()
{
selPeriod = false;
showDay = false;
showTemp = false;
showTimb = false;
currRecord = null;
ListTimbr = null;
SelDayDTO = null;
await InitData(true);
await ReloadData();
}
protected async Task ResetDayCheck()
{
selPeriod = false;
-20
View File
@@ -35,7 +35,6 @@ namespace GPW.CORE.WRKLOG.Pages
public void Dispose()
{
AppMServ.EA_SearchUpdated -= OnSearchUpdated;
GDataServ.mPipeTimb.EA_NewMessage -= MPipeTimb_EA_NewMessage;
GDataServ.mPipeRich.EA_NewMessage -= MPipeRich_EA_NewMessage;
UITimer?.Dispose();
@@ -166,18 +165,11 @@ namespace GPW.CORE.WRKLOG.Pages
Enabled = true
};
UITimer.Elapsed += async (s, ea) => await TryReload();
AppMServ.EA_SearchUpdated += OnSearchUpdated;
// aggiungo gestione notifica messaggi timbrature...
GDataServ.mPipeTimb.EA_NewMessage += MPipeTimb_EA_NewMessage;
GDataServ.mPipeRich.EA_NewMessage += MPipeRich_EA_NewMessage;
}
protected void OnSearchUpdated()
{
var pUpd = Task.Run(async () => await TryReload());
pUpd.Wait();
}
/// <summary>
/// Indico item selezionato
/// </summary>
@@ -355,27 +347,16 @@ namespace GPW.CORE.WRKLOG.Pages
#region Private Fields
private int _endHour = 18;
private int _startHour = 8;
private RegAttivitaModel? currRecord = null;
private DateTime DtSelected = DateTime.Today;
private List<AnagFasiModel> ListFasi = new List<AnagFasiModel>();
private List<DailyDataDTO>? ListRecords = null;
private List<TimbratureModel>? ListTimbr = null;
private DailyDataDTO? SelDayDTO = null;
private bool selPeriod = false;
private bool showDay = false;
private bool showTemp = false;
private List<WeekStatDTO>? weekStatList = null;
#endregion Private Fields
@@ -576,6 +557,5 @@ namespace GPW.CORE.WRKLOG.Pages
}
#endregion Private Methods
}
}
+1 -8
View File
@@ -31,19 +31,12 @@ var redisMultiplexer = ConnectionMultiplexer.Connect(connStringRedis);
// abilitazione x email management con MailKit
builder.Services.AddTransient<IEmailSender, MailKitEmailSender>();
// recupero email reversed
string revPwd = configuration["ExternalProviders:MailKit:SMTP:Password"] ?? "";
string pwd = "";
foreach (char c in revPwd)
{
pwd = c + pwd;
}
builder.Services.Configure<MailKitEmailSenderOptions>(options =>
{
options.Host_Address = configuration["ExternalProviders:MailKit:SMTP:Address"];
options.Host_Port = Convert.ToInt32(configuration["ExternalProviders:MailKit:SMTP:Port"]);
options.Host_Username = configuration["ExternalProviders:MailKit:SMTP:Account"];
options.Host_Password = pwd;// configuration["ExternalProviders:MailKit:SMTP:Password"];
options.Host_Password = configuration["ExternalProviders:MailKit:SMTP:Password"];
options.Sender_EMail = configuration["ExternalProviders:MailKit:SMTP:SenderEmail"];
options.Sender_Name = configuration["ExternalProviders:MailKit:SMTP:SenderName"];
});
+15 -4
View File
@@ -23,9 +23,18 @@
</div>
</div>
</div>
@if (isAdmin)
{
<div class="px-1">
<div class="px-1">
<div class="d-flex">
<div class="px-1">
<div class="input-group" title="ricerca">
<span class="input-group-text"><i class="fas fa-search"></i></span>
<input type="text" class="form-control" @bind="@searchVal">
<button class="btn btn-outline-secondary" @onclick="resetSearch"><i class="fas fa-ban"></i></button>
</div>
</div>
<div class="px-1">
@if (isAdmin)
{
<NavLink class="btn btn-info btn-sm mb-0 py-1" href="Presenze">
@if (!string.IsNullOrEmpty(lblDisplay))
{
@@ -36,8 +45,10 @@
<span>Presenti: @numPres di @numTot</span>
}
</NavLink>
}
</div>
</div>
}
</div>
</div>
</AuthorizeView>
@@ -33,6 +33,17 @@ namespace GPW.CORE.WRKLOG.Shared
#region Protected Methods
protected void resetSearch()
{
searchVal = "";
}
protected string searchVal
{
get => AppMServ.SearchVal;
set => AppMServ.SearchVal = value;
}
/// <summary>
/// Verifica informazioni condivise applicazione
/// </summary>
@@ -115,6 +126,9 @@ namespace GPW.CORE.WRKLOG.Shared
protected async Task DoVerifyActiv()
{
AppMServ.PayloadOk = false;
// svuoto oggetto activList...
LicServ.ActivList = new List<LiManObj.AttivazioneDTO>();
await LicServ.resetActivList();
await Task.Delay(50);
await VerifyActiv();
}
+5 -1
View File
@@ -8,7 +8,11 @@ main {
}
.sidebar,
.sidebarSmall {
background-image: linear-gradient(180deg, #052767 20%, #3aa6ff 90%);
/*background-image: linear-gradient(180deg, rgb(5, 39, 103) 20%, #3aa6ff 90%);*/
background: -webkit-linear-gradient(to bottom, #052767, #d1afea);
/* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to bottom, #052767, #d1afea);
/* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
}
.top-row {
background-color: #f7f7f7;
+3 -1
View File
@@ -10,7 +10,9 @@ main {
.sidebar,
.sidebarSmall {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 20%, #3aa6ff 90%);
/*background-image: linear-gradient(180deg, rgb(5, 39, 103) 20%, #3aa6ff 90%);*/
background: -webkit-linear-gradient(to bottom, #052767, #d1afea); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(to bottom, #052767, #d1afea); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
}
.top-row {
+1 -1
View File
@@ -1 +1 @@
.page{position:relative;display:flex;flex-direction:column;}main{flex:1;}.sidebar,.sidebarSmall{background-image:linear-gradient(180deg,#052767 20%,#3aa6ff 90%);}.top-row{background-color:#f7f7f7;border-bottom:1px solid #d6d5d5;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;}.bottom-row{color:#dedede;background-color:#000;height:2rem;align-items:center;}@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:13.5rem;height:100vh;position:sticky;top:0;}.sidebarSmall{width:4.5rem;height:100vh;position:sticky;top:0;}.top-row{position:sticky;top:0;z-index:1;}.top-row,article{padding-left:.5rem!important;padding-right:.5rem!important;}}
.page{position:relative;display:flex;flex-direction:column;}main{flex:1;}.sidebar,.sidebarSmall{background:-webkit-linear-gradient(to bottom,#052767,#d1afea);background:linear-gradient(to bottom,#052767,#d1afea);}.top-row{background-color:#f7f7f7;border-bottom:1px solid #d6d5d5;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;}.bottom-row{color:#dedede;background-color:#000;height:2rem;align-items:center;}@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:13.5rem;height:100vh;position:sticky;top:0;}.sidebarSmall{width:4.5rem;height:100vh;position:sticky;top:0;}.top-row{position:sticky;top:0;z-index:1;}.top-row,article{padding-left:.5rem!important;padding-right:.5rem!important;}}
+7 -2
View File
@@ -37,8 +37,13 @@
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="ListPlan">
<span class="fas fa-list px-2" aria-hidden="true"></span> <span class="@hideText">Record List</span>
<NavLink class="nav-link" href="DayList">
<span class="fas fa-calendar-day px-2" aria-hidden="true"></span> <span class="@hideText">Day View</span>
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="ActHistory">
<span class="fas fa-list px-2" aria-hidden="true"></span> <span class="@hideText">Activity History</span>
</NavLink>
</div>
<div class="nav-item px-3">
+4 -20
View File
@@ -8,8 +8,8 @@
"AllowedHosts": "*",
"CodApp": "GPW",
"ConnectionStrings": {
"GPW.DB": "Server=W2019-SQL-STEAM;Database=GPW; User ID=sa;Password=keyhammer16; integrated security=False; MultipleActiveResultSets=True; App=GPW.CORE.WRKLOG;",
"Redis": "localhost:6379,DefaultDatabase=15,connectTimeout=5000,syncTimeout=5000,asyncTimeout=5000,abortConnect=false,ssl=false"
"GPW.DB": "Server=W2019-SQL-STEAM;Database=GPW; User ID=UserGPW; Password=Us3rGpw!75x93$77; integrated security=False; MultipleActiveResultSets=True; App=GPW.CORE.WRKLOG;",
"Redis": "localhost:26379,serviceName=prod,DefaultDatabase=15,connectTimeout=5000,syncTimeout=5000,asyncTimeout=5000,abortConnect=false,ssl=false,password=BtN9Py1wtLfLRvmzWnOPJ7RytDM+CLiVsJ/16zduNTlV8IOPGNrtzJSXPUnImA5PqmUMhKaUqo9NdHIG"
},
"ExternalProviders": {
"MailKit": {
@@ -17,7 +17,7 @@
"Address": "smtp-mail.outlook.com",
"Port": "587",
"Account": "steamwarebot@outlook.it",
"Password": "!39elanoizaNaiVnIomais",
"Password": "siamoInViaNazionale93!",
"SenderEmail": "steamwarebot@outlook.it",
"SenderName": "Steamware Email BOT"
}
@@ -35,21 +35,5 @@
"LinkMal": "https://office.egalware.com/GPW/ADMIN/malattia",
"LinkRich": "https://office.egalware.com/GPW/ADMIN/richiesteDip"
},
"VetoIns": "true",
"Redis": {
"Password": "",
"AllowAdmin": false,
"Ssl": false,
"ConnectTimeout": 6000,
"SyncTimeout": 6000,
"AsyncTimeout": 6000,
"ConnectRetry": 2,
"Hosts": [
{
"Host": "localhost",
"Port": "6379"
}
],
"Database": 15
}
"VetoIns": "true"
}
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>GPW - Gestione Presenze Web</i>
<h4>Versione: 3.0.2403.0715</h4>
<h4>Versione: 3.0.2408.1910</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
3.0.2403.0715
3.0.2408.1910
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>3.0.2403.0715</version>
<version>3.0.2408.1910</version>
<url>http://nexus.steamware.net/repository/SWS/GPW/stable/GPW.UI.zip</url>
<changelog>http://nexus.steamware.net/repository/SWS/GPW/stable/ChangeLog.html</changelog>
<mandatory>false</mandatory>