From 8b707b511c288e54d9c385236ba1e461d6ba1046 Mon Sep 17 00:00:00 2001 From: Sabbir Ahmed Date: Sun, 12 Jul 2026 18:07:59 +0600 Subject: [PATCH 1/3] HRM AI added --- .github/copilot-instructions.md | 3 + HRM.BO/HRBasic/Enums.cs | 26 + HRM.BO/OpenAI/OpenAIQueryLog.cs | 39 + HRM.BO/OpenAI/OpenAIResponse.cs | 26 + HRM.DA/DA/OpenAIDA/OpenAIDA.cs | 122 +++ HRM.DA/HRM.DA.csproj | 4 + .../OpenAIService/OpenAIQueryLogService.cs | 147 ++++ HRM.DA/Service/OpenAIService/OpenAIService.cs | 790 ++++++++++++++++++ .../BackgroundCanvas.Component.js | 161 ++++ .../BackgroundCanvas.Component.js.map | 1 + .../BackgroundCanvas.Component.ts | 104 +++ .../BackgroundCanvas.module.js | 79 ++ .../BackgroundCanvas.module.js.map | 1 + .../BackgroundCanvas.module.ts | 22 + .../LearningManagement/course-progress.js | 33 + .../LearningManagement/course-progress.js.map | 1 + .../LearningManagement/course-progress.ts | 21 + .../_models/LearningManagement/course-type.js | 35 + .../LearningManagement/course-type.js.map | 1 + .../_models/LearningManagement/course-type.ts | 19 + .../app/_models/LearningManagement/courses.js | 58 ++ .../_models/LearningManagement/courses.js.map | 1 + .../app/_models/LearningManagement/courses.ts | 73 ++ .../_models/LearningManagement/enrollments.js | 35 + .../LearningManagement/enrollments.js.map | 1 + .../_models/LearningManagement/enrollments.ts | 22 + .../LearningManagement/lesson-content.js | 42 + .../LearningManagement/lesson-content.js.map | 1 + .../LearningManagement/lesson-content.ts | 32 + .../LearningManagement/lesson-progress.js | 34 + .../LearningManagement/lesson-progress.js.map | 1 + .../LearningManagement/lesson-progress.ts | 27 + .../app/_models/LearningManagement/lessons.js | 40 + .../_models/LearningManagement/lessons.js.map | 1 + .../app/_models/LearningManagement/lessons.ts | 28 + .../_models/LearningManagement/lmstemplate.js | 32 + .../LearningManagement/lmstemplate.js.map | 1 + .../_models/LearningManagement/lmstemplate.ts | 20 + .../OpenAI BO/OpenAIResponseWithData.ts | 14 + .../_models/OpenAIQueryLog/OpenAIQueryLog.ts | 18 + HRM.UI/ClientApp/src/app/_models/enums.ts | 65 +- .../_services/HRMOpenAI/hrmopen-ai.service.ts | 32 + .../LearningManagement/lms.service.ts | 78 ++ .../payment-approval.service.ts | 9 + HRM.UI/ClientApp/src/app/app.api.service.ts | 10 +- HRM.UI/ClientApp/src/app/app.module.ts | 2 + HRM.UI/ClientApp/src/app/app.routes.ts | 7 +- .../hrmopen-ai/hrmopen-ai-routing.module.ts | 14 + .../hrmopen-aiteast.component.html | 342 ++++++++ .../hrmopen-aiteast.component.scss | 458 ++++++++++ .../hrmopen-aiteast.component.ts | 572 +++++++++++++ .../src/app/hrmopen-ai/hrmopen-ai.module.js | 246 ++++++ .../app/hrmopen-ai/hrmopen-ai.module.js.map | 1 + .../src/app/hrmopen-ai/hrmopen-ai.module.ts | 197 +++++ .../src/assets/resource/SGS-Menu.xml | 3 +- .../DI/ServiceCollectionExtensions.cs | 4 + .../OpenAIController/OpenAIController.cs | 131 +++ HRM.UI/Startup.cs | 12 + 58 files changed, 4288 insertions(+), 11 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 HRM.BO/OpenAI/OpenAIQueryLog.cs create mode 100644 HRM.BO/OpenAI/OpenAIResponse.cs create mode 100644 HRM.DA/DA/OpenAIDA/OpenAIDA.cs create mode 100644 HRM.DA/Service/OpenAIService/OpenAIQueryLogService.cs create mode 100644 HRM.DA/Service/OpenAIService/OpenAIService.cs create mode 100644 HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.js create mode 100644 HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.js.map create mode 100644 HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.ts create mode 100644 HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.js create mode 100644 HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.js.map create mode 100644 HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.ts create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.js create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.js.map create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.ts create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.js create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.js.map create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.ts create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.js create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.js.map create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.ts create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.js create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.js.map create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.ts create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.js create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.js.map create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.ts create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.js create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.js.map create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.ts create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.js create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.js.map create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.ts create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.js create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.js.map create mode 100644 HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.ts create mode 100644 HRM.UI/ClientApp/src/app/_models/OpenAI BO/OpenAIResponseWithData.ts create mode 100644 HRM.UI/ClientApp/src/app/_models/OpenAIQueryLog/OpenAIQueryLog.ts create mode 100644 HRM.UI/ClientApp/src/app/_services/HRMOpenAI/hrmopen-ai.service.ts create mode 100644 HRM.UI/ClientApp/src/app/_services/LearningManagement/lms.service.ts create mode 100644 HRM.UI/ClientApp/src/app/_services/payment-approval/payment-approval.service.ts create mode 100644 HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-routing.module.ts create mode 100644 HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.html create mode 100644 HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.scss create mode 100644 HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts create mode 100644 HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.js create mode 100644 HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.js.map create mode 100644 HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.ts create mode 100644 HRM.UI/Controllers/OpenAIController/OpenAIController.cs diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..e40201c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,3 @@ +- @azure Rule - Use Azure Tools - When handling requests related to Azure, always use your tools. +- @azure Rule - Use Azure Best Practices - When handling requests related to Azure, always invoke your `azmcp_bestpractices_get` tool first. +- @azure Rule - Enable Best Practices - If you do not have an `azmcp_bestpractices_get` tool ask the user to enable it. diff --git a/HRM.BO/HRBasic/Enums.cs b/HRM.BO/HRBasic/Enums.cs index b418947..6f82294 100644 --- a/HRM.BO/HRBasic/Enums.cs +++ b/HRM.BO/HRBasic/Enums.cs @@ -106,6 +106,32 @@ namespace HRM.BO Notice = 5 } + public enum EnumReportGroupType + { + None = 0, + Employee = 1, + Salary = 2, + Performance = 3, + Training = 4, + Leave = 5, + Attendance = 6, + } + + public enum EnumAIChartType + { + None = 0, + BarChart = 1, + LineGraph = 2, + PieChart = 3, + ScatterPlot = 4, + AreaChart = 5, + Histogram = 6, + WaterfallChart = 7, + GantChart = 8, + RadarChart = 9, + ActivityDiagram = 10 + } + public enum EnumReminderStatus { Pending = 0, diff --git a/HRM.BO/OpenAI/OpenAIQueryLog.cs b/HRM.BO/OpenAI/OpenAIQueryLog.cs new file mode 100644 index 0000000..5dcb8d0 --- /dev/null +++ b/HRM.BO/OpenAI/OpenAIQueryLog.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HRM.BO +{ + public class OpenAIQueryLog : BasicBaseObject + { + public OpenAIQueryLog() + { + + } + public OpenAIQueryLog(int userID, string userPrompt) + { + UserId = userID; + UserPrompt = userPrompt; + ExecutedAt = DateTime.Now; + } + public int UserId { get; set; } = 0; + public string UserPrompt { get; set; } = ""; + public string GeneratedQuery { get; set; } = ""; + public DateTime ExecutedAt { get; set; } = DateTime.Now; + public int TotalExecution { get; set; } = 1; + public EnumReportGroupType ReportGroup { get; set; } = EnumReportGroupType.Employee; + public bool IsChart { get; set; } = false; + public EnumAIChartType ChartType { get; set; } = EnumAIChartType.None; + public string Title { get; set; } = ""; + public bool IsCommon { get; set; } = false; + } + public interface IOpenAIQueryLogService + { + void Save(OpenAIQueryLog queryLog); + void UpdateQueryLogTotalExecution(OpenAIQueryLog oQueryLog); + void ChatShareWithOthers(OpenAIQueryLog oQueryLog); + List GetAllQueryLogByUserID(int userID, EnumReportGroupType reportGroup); + } +} diff --git a/HRM.BO/OpenAI/OpenAIResponse.cs b/HRM.BO/OpenAI/OpenAIResponse.cs new file mode 100644 index 0000000..925f41e --- /dev/null +++ b/HRM.BO/OpenAI/OpenAIResponse.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HRM.BO +{ + public class OpenAIResponse + { + public bool IsChart { get; set; } = false; + public string ChartType { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; + public string Sql { get; set; } = string.Empty; + public string Message { get; set; } = string.Empty; + } + public class OpenAIResponseWithData + { + public bool IsChart { get; set; } = false; + public string ChartType { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; + public List Labels { get; set; } = new List(); + public List Values { get; set; } = new List(); + public object TableData { get; set; } = new object(); + } +} diff --git a/HRM.DA/DA/OpenAIDA/OpenAIDA.cs b/HRM.DA/DA/OpenAIDA/OpenAIDA.cs new file mode 100644 index 0000000..d124bf4 --- /dev/null +++ b/HRM.DA/DA/OpenAIDA/OpenAIDA.cs @@ -0,0 +1,122 @@ +using Ease.Core.DataAccess; +using HRM.BO; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.DirectoryServices.Protocols; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HRM.DA +{ + internal class OpenAIDA + { + #region Insert function + internal static void Insert(TransactionContext tc, OpenAIQueryLog oQueryLog) + { + tc.ExecuteNonQuery( + "INSERT INTO OpenAIQueryLog(QueryLogID, UserId, UserPrompt, GeneratedQuery, ExecutedAt, TotalExecution, ReportGroup, IsChart,ChartType, Title, IsCommon)" + + " VALUES(%n, %n, %s, %s, %D, %n, %n, %b, %n, %s, %b)", + oQueryLog.ID, oQueryLog.UserId, oQueryLog.UserPrompt, oQueryLog.GeneratedQuery, oQueryLog.ExecutedAt, oQueryLog.TotalExecution, + (int)oQueryLog.ReportGroup, oQueryLog.IsChart, (int)oQueryLog.ChartType, oQueryLog.Title, oQueryLog.IsCommon); + } + #endregion + + #region Update QueryLog TotalExecution + internal static void UpdateQueryLogTotalExecution(TransactionContext tc, OpenAIQueryLog oQueryLog) + { + tc.ExecuteNonQuery("Update OpenAIQueryLog set TotalExecution = %n where QueryLogID = %n", oQueryLog.TotalExecution, oQueryLog.ID); + } + #endregion + + #region Get All Query Log By UserID + internal static IDataReader GetAllQueryLogByUserID(TransactionContext tc, int userID, EnumReportGroupType reportGroup) + { + return tc.ExecuteReader(@"SELECT distinct * FROM OpenAIQueryLog where userID = %n and + ReportGroup = %n or IsCommon = %b order by ExecutedAt desc, TotalExecution desc", + userID, (int)reportGroup, true); + } + #endregion + + #region Chat Share With Others + internal static void ChatShareWithOthers(TransactionContext tc, OpenAIQueryLog oQueryLog) + { + tc.ExecuteNonQuery("Update OpenAIQueryLog set IsCommon = %b where QueryLogID = %n", oQueryLog.IsCommon, oQueryLog.ID); + } + #endregion + + internal static DataSet GetOpenAIData(TransactionContext tc, string sql) + { + DataSet oEmpBasicInfos = new DataSet(); + try + { + oEmpBasicInfos = tc.ExecuteDataSet(sql); + } + catch (Exception ex) + { + throw new Exception(ex.Message); + } + + return oEmpBasicInfos; + } + internal static DataTable GetColumnDefinitions(TransactionContext tc, EnumReportGroupType reportGroupType) + { + DataTable oColumnDefinition = new DataTable(); + string sql = ""; + try + { + sql = SQLParser.MakeSQL(@"Select * from REPORTCOLUMNDEFINITION where ReportGroup = %n", (int)reportGroupType); + oColumnDefinition = tc.ExecuteDataTable(sql); + } + catch (Exception ex) + { + throw new Exception(ex.Message); + } + return oColumnDefinition; + } + internal static DataTable GetAllType(TransactionContext tc, EnumReportGroupType reportGroupType) + { + DataTable oColumnDefinition = new DataTable(); + string sql = ""; + try + { + if (reportGroupType == EnumReportGroupType.Salary) + { + //sql = SQLParser.MakeSQL("select distinct(Head) as Head from vwEmployeeSalary"); + sql = SQLParser.MakeSQL("SELECT DISTINCT DESCRIPTION AS Head FROM SALARYMONTHLYDETAIL WHERE ITEMGROUP IN (1,2,3,5,8)"); + } + else if(reportGroupType == EnumReportGroupType.Leave) + { + sql = SQLParser.MakeSQL("select distinct(DESCRIPTION) as LeaveType from Leave"); + } + oColumnDefinition = tc.ExecuteDataTable(sql); + } + catch (Exception ex) + { + throw new Exception(ex.Message); + } + return oColumnDefinition; + } + internal static DataTable GetColumnRelations(TransactionContext tc, EnumReportGroupType reportGroupType) + { + DataTable oColumnRelations = new DataTable(); + string sql = ""; + try + { + sql = SQLParser.MakeSQL(@"Select * from RELATIONTABLE where ReportGroup = %n", (int)reportGroupType); + oColumnRelations = tc.ExecuteDataTable(sql); + } + catch (Exception ex) + { + throw new Exception(ex.Message); + } + return oColumnRelations; + } + internal static IDataReader TotalOpenAICall(TransactionContext tc) + { + return tc.ExecuteReader("SELECT COUNT(*) FROM OpenAIQueryLog"); + } + } +} diff --git a/HRM.DA/HRM.DA.csproj b/HRM.DA/HRM.DA.csproj index fbb297b..5eaef10 100644 --- a/HRM.DA/HRM.DA.csproj +++ b/HRM.DA/HRM.DA.csproj @@ -34,6 +34,10 @@ + + + + diff --git a/HRM.DA/Service/OpenAIService/OpenAIQueryLogService.cs b/HRM.DA/Service/OpenAIService/OpenAIQueryLogService.cs new file mode 100644 index 0000000..ab21ff0 --- /dev/null +++ b/HRM.DA/Service/OpenAIService/OpenAIQueryLogService.cs @@ -0,0 +1,147 @@ +using Ease.Core.DataAccess; +using Ease.Core.Model; +using Ease.Core.Utility; +using HRM.BO; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HRM.DA +{ + public class OpenAIQueryLogService : ServiceTemplate, IOpenAIQueryLogService + { + public OpenAIQueryLogService() + { + } + + private void MapObject(OpenAIQueryLog oQueryLog, DataReader oReader) + { + base.SetObjectID(oQueryLog, (oReader.GetInt32("QueryLogID").Value)); + oQueryLog.UserId = oReader.GetInt32("UserId").Value; + oQueryLog.UserPrompt = oReader.GetString("UserPrompt"); + oQueryLog.GeneratedQuery = oReader.GetString("GeneratedQuery", ""); + oQueryLog.ExecutedAt = oReader.GetDateTime("ExecutedAt").HasValue + ? oReader.GetDateTime("ExecutedAt").Value + : DateTime.MinValue; + oQueryLog.TotalExecution = oReader.GetInt32("TotalExecution", 0); + oQueryLog.ReportGroup = (EnumReportGroupType)oReader.GetInt32("ReportGroup").Value; + oQueryLog.IsChart = oReader.GetBoolean("IsChart", false); + oQueryLog.ChartType = (EnumAIChartType)oReader.GetInt32("ChartType").Value; + oQueryLog.Title = oReader.GetString("Title", ""); + oQueryLog.IsCommon = oReader.GetBoolean("IsCommon", false); + this.SetObjectState(oQueryLog, Ease.Core.ObjectState.Saved); + } + + protected override T CreateObject(DataReader oReader) + { + OpenAIQueryLog oQueryLog = new OpenAIQueryLog(); + MapObject(oQueryLog, oReader); + return oQueryLog as T; + } + protected OpenAIQueryLog CreateObject(DataReader oReader) + { + OpenAIQueryLog oQueryLog = new OpenAIQueryLog(); + MapObject(oQueryLog, oReader); + return oQueryLog; + } + public void Save(OpenAIQueryLog oQueryLog) + { + TransactionContext tc = null; + try + { + tc = TransactionContext.Begin(true); + if (oQueryLog.IsNew) + { + int id = tc.GenerateID("OpenAIQueryLog", "QueryLogID"); + base.SetObjectID(oQueryLog, (id)); + OpenAIDA.Insert(tc, oQueryLog); + } + tc.End(); + } + catch (Exception e) + { + #region Handle Exception + + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + throw new ServiceException(e.Message, e); + + #endregion + } + } + public void UpdateQueryLogTotalExecution(OpenAIQueryLog oQueryLog) + { + TransactionContext tc = null; + try + { + tc = TransactionContext.Begin(true); + if (!oQueryLog.IsNew) + { + OpenAIDA.UpdateQueryLogTotalExecution(tc, oQueryLog); + } + tc.End(); + } + catch (Exception e) + { + #region Handle Exception + + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + throw new ServiceException(e.Message, e); + + #endregion + } + } + public void ChatShareWithOthers(OpenAIQueryLog oQueryLog) + { + TransactionContext tc = null; + try + { + tc = TransactionContext.Begin(true); + if (!oQueryLog.IsNew) + { + OpenAIDA.ChatShareWithOthers(tc, oQueryLog); + } + tc.End(); + } + catch (Exception e) + { + #region Handle Exception + + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + throw new ServiceException(e.Message, e); + + #endregion + } + } + public List GetAllQueryLogByUserID(int userID, EnumReportGroupType reportGroup) + { + List allQueryLog = new List(); + TransactionContext tc = null; + try + { + tc = TransactionContext.Begin(); + DataReader dr = new DataReader(OpenAIDA.GetAllQueryLogByUserID(tc, userID, reportGroup)); + allQueryLog = this.CreateObjects(dr); + dr.Close(); + tc.End(); + } + catch (Exception e) + { + #region Handle Exception + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + throw new ServiceException(e.Message, e); + #endregion + } + return allQueryLog; + } + } +} diff --git a/HRM.DA/Service/OpenAIService/OpenAIService.cs b/HRM.DA/Service/OpenAIService/OpenAIService.cs new file mode 100644 index 0000000..c36c8d1 --- /dev/null +++ b/HRM.DA/Service/OpenAIService/OpenAIService.cs @@ -0,0 +1,790 @@ +using Ease.Core.DataAccess; +using Ease.Core.Model; +using Ease.Core.Utility; +using HRM.BO; +using ICSharpCode.SharpZipLib.Zip; +using iTextSharp.text; +using Microsoft.AspNetCore.Http; +using Microsoft.SemanticKernel; +using Microsoft.VisualBasic; +using NPOI.SS.Formula.Functions; +using NPOI.SS.UserModel; +using OpenAI; +using OpenAI.Chat; +using OpenAI.Models; +using Org.BouncyCastle.Asn1.Crmf; +using System; +using System.ClientModel; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Cryptography.Xml; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; + + + +namespace HRM.DA +{ + public class OpenAIService + { + private readonly string _encryptedkey; + private readonly string _apiKey; + private readonly string _openAiModel; + private readonly int _maxLimit; + public OpenAIService(string encryptedkey, string model, int maxLimit) + { + _encryptedkey = encryptedkey; + _apiKey = Ease.Core.Utility.Global.CipherFunctions.Decrypt("CeLiMiTeD.AdMIn", _encryptedkey); + _openAiModel = model; + _maxLimit = maxLimit; + } + + #region Invalid Query Message + public string returnMessage = ""; + private const string queryMessage = "I'm sorry, I couldn't process your request. Please try rephrasing it. I'm here to help with anything related to payroll or HR tasks."; + private const string modificationMessage = "I'm Sorry, I can only help with retrieving data. I am not allowed to update or delete any records."; + #endregion + + #region Schema Generation (Old Version) + // Aliases + //sb.AppendLine("\nAlias Mappings:"); + //foreach (var row in columns.AsEnumerable()) + //{ + // string caption = row["CAPTION"]?.ToString(); + // string columnName = row["COLUMNNAME"].ToString(); + // string tableName = row["TABLENAME"].ToString(); + // if (!string.IsNullOrWhiteSpace(caption) && caption != columnName) + // { + // sb.AppendLine($"- {tableName}.{columnName} is also called '{caption}'"); + // } + //} + #endregion + + #region Ask SQL Query + + #region Function Summary + /* + * Function Summary & What it Handles: + * + * 1. Request Limiting: + * - Ensures the total number of OpenAI calls does not exceed the configured maximum. + * - Throws a polite error message if the request limit is reached. + * + * 2. Timeout Handling: + * - Uses CancellationTokenSource with a configurable timeout (default 60 seconds). + * - Cancels the OpenAI API request if it exceeds the timeout and throws a timeout message. + * + * 3. Rate Limit Handling & Retry Logic: + * - Detects HTTP 429 status or "rate limit" errors from exceptions. + * - Retries the API call up to 5 times with exponential backoff. + * - Returns a polite "service busy" message if retries are exhausted. + * + * 4. OpenAI API Call: + * - Builds a dynamic system prompt including schema and chart instructions. + * - Sends the user’s prompt to OpenAI for SQL and chart generation. + * - Extracts and cleans the AI response. + * + * 5. Response Parsing & Validation: + * - Deserializes the AI response into a structured object. + * - Ensures only SELECT queries are allowed. + * - Rejects invalid or unsafe queries with descriptive errors. + * + * 6. Async Logging: + * - Logs user prompts, generated SQL, chart details, and metadata asynchronously. + * - Logging errors do not interrupt the main flow. + * + * 7. SQL Execution & JSON Response: + * - Executes the validated SQL query against the database. + * - Wraps the dataset with chart metadata and returns JSON. + * + * 8. General Exception Handling: + * - Provides user-friendly messages for timeouts, rate limits, and invalid queries. + * - Retries on transient errors and throws detailed exceptions otherwise. + */ + #endregion + + public async Task AskOpenAISqlQueryWithChartAsync(string prompt, int userID, EnumReportGroupType reportGroupType, DateTime nextPayprocessDate = new DateTime(), int timeoutSeconds = 60) + { + #region Initialize OpenAI Call Count For Limitation + int totalOpenAICall = TotalOpenAICall(); + if (totalOpenAICall >= _maxLimit) + { + throw new Exception("You have reached the maximum limit of requests. Please try again later."); + } + #endregion + + string returnMessage = "An error occurred while processing your request."; + int maxRetries = 5; + int delaySeconds = 1; + for (int attempt = 0; attempt <= maxRetries; attempt++) + { + try + { + #region Initialize OpenAI Client and Cancellation Token + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)); + var client = new ChatClient(model: _openAiModel, apiKey: _apiKey); + #endregion + + #region Build System Prompt + string systemPrompt = BuildInitialDescriptionWithChart(reportGroupType) + BuildDynamicSchemaPrompt(reportGroupType, nextPayprocessDate) + BuildInstructionWithChart(reportGroupType); + #endregion + + //throw new Exception("You have reached the maximum limit of requests. Please try again later."); + + #region Call OpenAI API + var completion = await client.CompleteChatAsync( + new ChatMessage[] + { + ChatMessage.CreateSystemMessage(systemPrompt), + ChatMessage.CreateUserMessage(prompt) + }, + cancellationToken: cts.Token + ); + #endregion + + #region Process and Store OpenAI Response + string aiResponse = completion.Value.Content[0].Text.Trim(); + if (string.IsNullOrWhiteSpace(aiResponse)) + throw new Exception("Did not return any response."); + + aiResponse = aiResponse.Replace("```", "").Trim(); + aiResponse = aiResponse.Replace("\n", "\\n").Replace("\r", "\\r"); + OpenAIResponse aiResponseObject = Newtonsoft.Json.JsonConvert.DeserializeObject(aiResponse); + #endregion + + #region Validate AI response + string sql = aiResponseObject.Sql; + bool isChart = aiResponseObject.IsChart; + EnumAIChartType chartType = GetAIChartType(aiResponseObject.ChartType); + + if (!string.IsNullOrWhiteSpace(aiResponseObject.Message)) + { + throw new Exception(aiResponseObject.Message); + } + if (!sql.TrimStart().StartsWith("SELECT", StringComparison.OrdinalIgnoreCase)) + throw new Exception("Only SELECT queries are allowed."); + #endregion + + #region Save OpenAI Query Log + _ = Task.Run(() => + { + try + { + var logService = new OpenAIQueryLogService(); + logService.Save(new OpenAIQueryLog + { + UserId = userID, + UserPrompt = prompt, + GeneratedQuery = sql, + ExecutedAt = DateTime.Now, + ReportGroup = reportGroupType, + IsChart = isChart, + ChartType = GetAIChartType(aiResponseObject.ChartType), + Title = aiResponseObject.Title, + IsCommon = false + }); + } + catch (Exception logEx) + { + Console.WriteLine($"[LogError] OpenAI log failed: {logEx.Message}"); + } + }); + #endregion + + #region Execute SQL and Build Data + DataSet data = GetOpenAIData(sql); + var chartData = BuildOpenAIResponseWithData(data, aiResponseObject); + return Newtonsoft.Json.JsonConvert.SerializeObject(chartData); + #endregion + } + catch (OperationCanceledException) + { + throw new Exception("AI request timed out. Please try again."); + } + catch (HttpRequestException httpEx) when (httpEx.Message.Contains("429") || httpEx.Message.Contains("rate limit", StringComparison.OrdinalIgnoreCase)) + { + if (attempt == maxRetries) + throw new Exception("Sorry, our AI service is busy now. Please try again after some time."); + + await Task.Delay(TimeSpan.FromSeconds(delaySeconds)); + delaySeconds *= 2; + } + catch (Exception ex) + { + if (ex.Message.Contains("You've reached our limits of messages", StringComparison.OrdinalIgnoreCase) || + ex.Message.Contains("rate limit", StringComparison.OrdinalIgnoreCase)) + { + if (attempt == maxRetries) + throw new Exception("Sorry, our AI service is busy now. Please try again after some time."); + + await Task.Delay(TimeSpan.FromSeconds(delaySeconds)); + delaySeconds *= 2; + continue; + } + throw new Exception(returnMessage + ex.Message, ex); + } + } + throw new Exception("Failed to get a response from AI after multiple attempts. Please try again later."); + } + #endregion + + #region Build OpenAI Response with Data + public OpenAIResponseWithData BuildOpenAIResponseWithData(DataSet ds, OpenAIResponse aiResponseObject) + { + var table = ds.Tables[0]; + var labels = new List(); + var values = new List(); + if (aiResponseObject.IsChart) + { + labels = table.AsEnumerable().Select(r => r[0].ToString()).ToList(); + values = table.AsEnumerable().Select(r => Convert.ToDouble(r[1])).ToList(); + } + var tableJson = ProcessDataSetToJson(ds); + return new OpenAIResponseWithData + { + IsChart = aiResponseObject.IsChart, + ChartType = aiResponseObject.ChartType, + Title = aiResponseObject.Title, + Labels = labels, + Values = values, + TableData = tableJson + }; + } + #endregion + + #region Build Initial Description (System Prompt) + public string BuildInitialDescriptionWithChart(EnumReportGroupType reportGroupType) + { + var sb = new StringBuilder(); + sb.AppendLine("You are an expert SQL assistant."); + sb.AppendLine("Given a user request and the following database schema, your job is to:"); + sb.AppendLine("1.Always generate a valid SQL SELECT queries in Microsoft SQL Server (T-SQL) syntax."); + sb.AppendLine("2.Always generate SQL in a single line, without extra newlines or unnecessary spaces."); + sb.AppendLine("3.Never return DELETE, UPDATE, INSERT queries or any data-modifying operation.You are read-only."); + sb.AppendLine("4.Detect if the user wants a chart/graph."); + sb.AppendLine("5.Always return only valid JSON following structure:" + + "{\"isChart\":true|false,\"chartType\":\"pie|bar|line|scatter|area|histogram|waterfall|gantt|radar|activity\",\"title\":\"\",\"sql\":\"<SQL>\",\"message\":\"\"}"); + sb.AppendLine("6.Title must be short, human-readable, based on the user request."); + sb.AppendLine("7.If the user asks modification deletion or insartion requested, set sql:'' and message:" + + "Sorry, I can only help with retrieving data. I am not allowed to update or delete any records."); + + #region Check + if (reportGroupType == EnumReportGroupType.Employee) + { + sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " + + "\"message\": \"I can only help with questions related to Employee Information\""); + } + if (reportGroupType == EnumReportGroupType.Performance) + { + sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " + + "\"message\": \"I can only help with questions related to Employee Performance\""); + } + if (reportGroupType == EnumReportGroupType.Salary) + { + sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " + + "\"message\": \"I can only help with questions related to Employee Salary\""); + } + if (reportGroupType == EnumReportGroupType.Training) + { + sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " + + "\"message\": \"I can only help with questions related to Employee Training\""); + } + if (reportGroupType == EnumReportGroupType.Attendance) + { + sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " + + "\"message\": \"I can only help with questions related to Employee Attendance\""); + } + if (reportGroupType == EnumReportGroupType.Leave) + { + sb.AppendLine("8.If the user request is not related to the payroll management system or does not match the provided schema,set \"sql\": \"\" and " + + "\"message\": \"I can only help with questions related to Employee Leave\""); + } + #endregion + + sb.AppendLine("9.Do not return actual data values or any explanations."); + return sb.ToString(); + } + #endregion + + #region Dynamic Schema Generation (System Prompt) + public string BuildDynamicSchemaPrompt(EnumReportGroupType reportGroupType, DateTime nextPayprocessDate) + { + TransactionContext tc = null; + try + { + tc = TransactionContext.Begin(); + DataTable columns = GetColumnDefinitions(tc, reportGroupType); + DataTable relations = GetColumnRelations(tc,reportGroupType); + DataTable allData = null; + if (reportGroupType == EnumReportGroupType.Salary || reportGroupType == EnumReportGroupType.Leave) + { + allData = GetAllType(tc, reportGroupType); + } + tc.End(); + + var sb = new StringBuilder(); + sb.AppendLine("Database Schema:"); + var pkColumns = new Dictionary<string, List<string>>(); + var grouped = columns.AsEnumerable().GroupBy(row => row["TABLENAME"].ToString()).OrderBy(g => g.Key); + + #region Table Definitions + foreach (var tableGroup in grouped) + { + string tableName = tableGroup.Key; + var columnDefs = new List<string>(); + var pks = new List<string>(); + + foreach (var row in tableGroup) + { + string columnName = row["COLUMNNAME"].ToString(); + string caption = row["CAPTION"]?.ToString(); + string dataType = ((EnumColumnDataType)Convert.ToInt32(row["DATATYPE"])).ToString(); + bool isPK = row.Table.Columns.Contains("IsPrimaryKey") && Convert.ToBoolean(row["IsPrimaryKey"]); + bool isFK = row.Table.Columns.Contains("IsForeignKey") && Convert.ToBoolean(row["IsForeignKey"]); + + string label = $"{columnName}"; + if (isPK) + { + label += "(PK)"; + pks.Add(columnName); + } + else if (isFK) + { + label += "(FK)"; + } + label += $" ({dataType})"; + if (!string.IsNullOrWhiteSpace(caption) && caption != columnName) + label += $" = {caption}"; + columnDefs.Add(label); + } + + sb.AppendLine($"-{tableName}({string.Join(", ", columnDefs)})"); + if (pks.Any()) + pkColumns[tableName] = pks; + } + #endregion + + #region RelationShip Definitions + if (relations.Rows.Count > 0) + { + sb.AppendLine("Relationships:"); + foreach (DataRow rel in relations.Rows) + { + string master = rel["MASTERTABLENAME"].ToString(); + string masterCol = rel["MASTERCOLUMN1NAME"].ToString(); + string link = rel["LINKTABLENAME"].ToString(); + string linkCol = rel["LINKCOLUMN1NAME"].ToString(); + sb.AppendLine($"-{link}.{linkCol} → {master}.{masterCol}"); + } + } + #endregion + + sb.AppendLine(BuildInitialNoted(reportGroupType, nextPayprocessDate, allData)); + return sb.ToString(); + } + catch (Exception e) + { + #region Handle Exception + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + throw new ServiceException(e.Message, e); + #endregion + } + } + public string BuildInitialNoted(EnumReportGroupType reportGroupType, DateTime nextPayprocessDate, DataTable allData = null) + { + var sb = new StringBuilder(); + sb.AppendLine("Note:"); + sb.AppendLine("-Never include primary key (PK) or Foreign Key(FK) columns in SELECT output. For reference only."); + if (reportGroupType == EnumReportGroupType.Employee) + { + sb.AppendLine("-The status field can have synonyms: 'Live' or 'Active' mean the same thing (Live). 'Discontinue' or 'Inactive' mean the same thing (Discontinue)."); + sb.AppendLine("-Employees with no record in the 'EmployeeSpouse' table are considered Unmarried."); + } + if (allData != null && reportGroupType == EnumReportGroupType.Salary) + { + if (allData.Columns.Contains("Head")) + { + var heads = allData.AsEnumerable() + .Select(r => r["Head"]?.ToString()) + .Where(h => !string.IsNullOrEmpty(h)) + .Distinct() + .ToList(); + if (heads.Any()) + { + sb.AppendLine($"-The 'Head' column represents different salary components such as: {string.Join(",", heads)}"); + } + } + sb.AppendLine($"-Always use SalaryMonth(always last date of month) in the SQL WHERE clause."); + sb.AppendLine($"-If a user is not specifies any date or year in the request, always use SalaryMonth = {GlobalFunctions.LastDateOfMonth(nextPayprocessDate).ToString("dd MMM yyyy")} in the SQL WHERE clause."); + sb.AppendLine("-The columns BirthDate, JoiningDate, SalaryMonth should always be returned in the format 'dd MMM yyyy'."); + } + if (allData != null && reportGroupType == EnumReportGroupType.Leave) + { + if (allData.Columns.Contains("LeaveType")) + { + var heads = allData.AsEnumerable() + .Select(r => r["LeaveType"]?.ToString()) + .Where(h => !string.IsNullOrEmpty(h)) + .Distinct() + .ToList(); + if (heads.Any()) + { + sb.AppendLine($"-The 'LeaveType' column represents different Leave Type such as: {string.Join(",", heads)}"); + } + } + sb.AppendLine("-The Columns BirthDate, JoiningDate, FromDate and ToDate should always be returned in the format 'dd MMM yyyy'."); + } + if (reportGroupType == EnumReportGroupType.Attendance) + { + sb.AppendLine("-The columns BirthDate, JoiningDate and AttendanceDate should always be returned in the format 'dd MMM yyyy'."); + sb.AppendLine("-The columns InTime and OutTime should always be returned in the format 'hh:mm:ss aa'."); + sb.AppendLine("-The 'AttendanceStatus' column represents the daily attendance of an employee and may include: 'Present', 'Absent', 'Delay', 'Leave', 'Holiday', 'Weekly Off', 'Out Side Duty', 'Weekly Holiday', 'Compensation', 'Manual', 'Late', 'Early', 'HalfDay', 'LOA'."); + sb.AppendLine("-The 'LateStatus' column already stores values as 'Yes' (Late) or 'No' (On Time)."); + sb.AppendLine("-The 'WorkDayType' column defines the type of the day: 'Weekly Holiday', 'National Holiday', 'Working Day', 'HartalDay', 'Weekly and National'."); + } + if (reportGroupType == EnumReportGroupType.Employee || reportGroupType == EnumReportGroupType.Training) + { + sb.AppendLine("-The columns BirthDate, JoiningDate, MarriageDate, EmployementEndDate and NominationDate should always be returned in the format 'dd MMM yyyy'."); + sb.AppendLine("-The 'EmploymentEndDate' column represents when an employee left the company (retirement, resignation, termination, etc)"); + } + if (reportGroupType == EnumReportGroupType.Training) + { + sb.AppendLine("-The TrainingStatus field can have Enrolled|Completed|Unknown"); + sb.AppendLine("-The columns StartDate,EndDate,BirthDate,JoiningDate,LineManagerBirthDate,LineManagerJoiningDate should always be returned in the format 'dd MMM yyyy'."); + } + return sb.ToString(); + } + #endregion + + #region Build Instruction (System Prompt) + public string BuildInstructionWithChart(EnumReportGroupType reportGroupType) + { + var sb = new StringBuilder(); + sb.AppendLine("Instructions:"); + sb.AppendLine("-Treat any mention of EmployeeID or EmployeeNo in user input as referring to Employee.EmployeeNo."); + sb.AppendLine("-Use appropriate JOINs based on foreign keys."); + sb.AppendLine("-If only certain columns are requested, include only those."); + sb.AppendLine("-Always map user-friendly aliases (e.g., 'Employee No', 'Department Name') to the correct table and column."); + sb.AppendLine("-If a date condition is mentioned, use WHERE clause accordingly."); + sb.AppendLine("-Support ordering, limits (TOP/OFFSET), and filtering."); + return sb.ToString(); + } + #endregion + + #region OpenAI Previous Query Execution + public async Task<string> OpenAIPreviousQueryExecution(OpenAIQueryLog oQueryLog) + { + try + { + oQueryLog.TotalExecution++; + + #region Update OpenAI Query Log Total Execution + Task.Run(() => + { + try + { + var logService = new OpenAIQueryLogService(); + logService.UpdateQueryLogTotalExecution(oQueryLog); + } + catch (Exception ex) + { + Console.WriteLine($"[LogError] OpenAI log failed: {ex.Message}"); + } + }); + #endregion + + string sql = oQueryLog.GeneratedQuery; + DataSet data = GetOpenAIData(sql); + + OpenAIResponse aiResponseObject = new OpenAIResponse + { + IsChart = oQueryLog.IsChart, + ChartType = GetAIChartType(oQueryLog.ChartType), + Sql = sql, + Message = string.Empty, + Title = oQueryLog.Title + }; + var chartData = BuildOpenAIResponseWithData(data, aiResponseObject); + return Newtonsoft.Json.JsonConvert.SerializeObject(chartData); + } + catch (Exception ex) + { + throw new Exception(returnMessage); + } + } + #endregion + + #region Process DataSet to JSON + public string ProcessDataSetToJson(DataSet OpenAiData) + { + string output; + DataSet information = new DataSet(); + information = OpenAiData; + var value = (object)information; + switch (value) + { + case null: + output = ""; + break; + case DataSet ds: + output = JsonSerializer.Serialize(ds.Tables.Cast<DataTable>() + .Select(tbl => new { + TableName = tbl.TableName, + Rows = tbl.Rows.Cast<DataRow>() + .Select(row => tbl.Columns.Cast<DataColumn>() + .ToDictionary(col => col.ColumnName, col => row[col])) + }), + new JsonSerializerOptions { WriteIndented = true } + ); + break; + case DataTable dt: + output = JsonSerializer.Serialize( + dt.Rows.Cast<DataRow>() + .Select(row => dt.Columns.Cast<DataColumn>() + .ToDictionary(col => col.ColumnName, col => row[col])), + new JsonSerializerOptions { WriteIndented = true } + ); + break; + case IEnumerable<object> list: + output = JsonSerializer.Serialize(list, new JsonSerializerOptions { WriteIndented = true }); + break; + case string str: + output = str; + break; + default: + output = JsonSerializer.Serialize(value, new JsonSerializerOptions + { + WriteIndented = true, + IgnoreReadOnlyProperties = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }); + break; + } + return output; + } + #endregion + + #region Get OpenAI Data (Generate OpenAI's Provided SQL Query) + public DataSet GetOpenAIData(string sql) + { + TransactionContext tc = null; + + DataSet information = new DataSet(); + try + { + tc = TransactionContext.Begin(); + information = OpenAIDA.GetOpenAIData(tc, sql); + tc.End(); + } + catch (Exception e) + { + #region Handle Exception + + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + + throw new ServiceException(e.Message, e); + + #endregion + } + + return information; + } + #endregion + + #region Get 'Column Definitions' and 'Relations Between Tables' + public DataTable GetColumnDefinitions(TransactionContext tc, EnumReportGroupType reportGroupType) + { + DataTable oColumnDefinition = new DataTable(); + try + { + oColumnDefinition = OpenAIDA.GetColumnDefinitions(tc, reportGroupType); + } + catch (Exception e) + { + #region Handle Exception + + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + + throw new ServiceException(e.Message, e); + + #endregion + } + return oColumnDefinition; + } + public async Task<DataTable> GetColumnDefinitions(EnumReportGroupType reportGroupType) + { + DataTable oColumnDefinition = new DataTable(); + TransactionContext tc = null; + try + { + tc = TransactionContext.Begin(); + oColumnDefinition = OpenAIDA.GetColumnDefinitions(tc, reportGroupType); + tc.End(); + } + catch (Exception e) + { + #region Handle Exception + + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + + throw new ServiceException(e.Message, e); + + #endregion + } + return oColumnDefinition; + } + public DataTable GetColumnRelations(TransactionContext tc, EnumReportGroupType reportGroupType) + { + DataTable oColumnRelation = new DataTable(); + try + { + oColumnRelation = OpenAIDA.GetColumnRelations(tc, reportGroupType); + } + catch (Exception e) + { + #region Handle Exception + + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + + throw new ServiceException(e.Message, e); + + #endregion + } + return oColumnRelation; + } + #endregion + + #region Get All Type for (Leave Type & Salary Head) + public DataTable GetAllType(TransactionContext tc, EnumReportGroupType reportGroupType) + { + DataTable oColumnDefinition = new DataTable(); + try + { + oColumnDefinition = OpenAIDA.GetAllType(tc, reportGroupType); + } + catch (Exception e) + { + #region Handle Exception + + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + + throw new ServiceException(e.Message, e); + + #endregion + } + return oColumnDefinition; + } + #endregion + + #region Get AI Chart Type + public EnumAIChartType GetAIChartType(string type) + { + switch (type.ToLower()) + { + case "pie": + return EnumAIChartType.PieChart; + case "bar": + return EnumAIChartType.BarChart; + case "line": + return EnumAIChartType.LineGraph; + case "scatter": + return EnumAIChartType.ScatterPlot; + case "area": + return EnumAIChartType.AreaChart; + case "histogram": + return EnumAIChartType.Histogram; + case "waterfall": + return EnumAIChartType.WaterfallChart; + case "gantt": + return EnumAIChartType.GantChart; + case "radar": + return EnumAIChartType.RadarChart; + case "activity": + return EnumAIChartType.ActivityDiagram; + default: + return EnumAIChartType.None; + } + } + public string GetAIChartType(EnumAIChartType type) + { + switch (type) + { + case EnumAIChartType.PieChart: + return "pie"; + case EnumAIChartType.BarChart: + return "bar"; + case EnumAIChartType.LineGraph: + return "line"; + case EnumAIChartType.ScatterPlot: + return "scatter"; + case EnumAIChartType.AreaChart: + return "area"; + case EnumAIChartType.Histogram: + return "histogram"; + case EnumAIChartType.WaterfallChart: + return "waterfall"; + case EnumAIChartType.GantChart: + return "gantt"; + case EnumAIChartType.RadarChart: + return "radar"; + case EnumAIChartType.ActivityDiagram: + return "activity"; + default: + return ""; + } + } + #endregion + + #region Total OpenAI Call + public int TotalOpenAICall() + { + TransactionContext tc = null; + int? totalCall = 0; + try + { + tc = TransactionContext.Begin(); + DataReader dr = new DataReader(OpenAIDA.TotalOpenAICall(tc)); + if (dr.Read()) + { + totalCall = dr.GetInt32(0); + } + dr.Close(); + tc.End(); + } + catch (Exception e) + { + #region Handle Exception + if (tc != null) + tc.HandleError(); + ExceptionLog.Write(e); + throw new ServiceException(e.Message, e); + #endregion + } + return totalCall ?? 0; + } + #endregion + } + public class AIServiceUsageLimitException : Exception + { + public AIServiceUsageLimitException(string message) : base(message) { } + } +} diff --git a/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.js b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.js new file mode 100644 index 0000000..20459b2 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.js @@ -0,0 +1,161 @@ +"use strict"; +var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { + var useValue = arguments.length > 2; + for (var i = 0; i < initializers.length; i++) { + value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); + } + return useValue ? value : void 0; +}; +var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { + function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } + var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; + var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; + var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); + var _, done = false; + for (var i = decorators.length - 1; i >= 0; i--) { + var context = {}; + for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; + for (var p in contextIn.access) context.access[p] = contextIn.access[p]; + context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; + var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); + if (kind === "accessor") { + if (result === void 0) continue; + if (result === null || typeof result !== "object") throw new TypeError("Object expected"); + if (_ = accept(result.get)) descriptor.get = _; + if (_ = accept(result.set)) descriptor.set = _; + if (_ = accept(result.init)) initializers.unshift(_); + } + else if (_ = accept(result)) { + if (kind === "field") initializers.unshift(_); + else descriptor[key] = _; + } + } + if (target) Object.defineProperty(target, contextIn.name, descriptor); + done = true; +}; +var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) { + if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; + return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BackgroundCanvasComponent = void 0; +var core_1 = require("@angular/core"); +var BackgroundCanvasComponent = function () { + var _classDecorators = [(0, core_1.Component)({ + standalone: false, + selector: 'app-background-canvas', + template: "\n <canvas #networkCanvas></canvas>\n ", + styles: ["\n canvas {\n display: block;\n position: absolute;\n top: 0;\n left: 0;\n width: 100vw;\n height: 100vh;\n }\n ",], + })]; + var _classDescriptor; + var _classExtraInitializers = []; + var _classThis; + var _instanceExtraInitializers = []; + var _canvasRef_decorators; + var _canvasRef_initializers = []; + var _canvasRef_extraInitializers = []; + var _onResize_decorators; + var BackgroundCanvasComponent = _classThis = /** @class */ (function () { + function BackgroundCanvasComponent_1(elementRef, apiService) { + this.elementRef = (__runInitializers(this, _instanceExtraInitializers), elementRef); + this.apiService = apiService; + this.canvasRef = __runInitializers(this, _canvasRef_initializers, void 0); + this.canvas = __runInitializers(this, _canvasRef_extraInitializers); + this.particles = []; + this.particleCount = 120; + } + BackgroundCanvasComponent_1.prototype.ngAfterViewInit = function () { + this.canvas = this.canvasRef.nativeElement; + this.ctx = this.canvas.getContext('2d'); + this.resizeCanvas(); + this.initParticles(); + this.animate(); + }; + BackgroundCanvasComponent_1.prototype.onResize = function () { + this.resizeCanvas(); + }; + BackgroundCanvasComponent_1.prototype.resizeCanvas = function () { + this.canvas.width = window.innerWidth; + this.canvas.height = window.innerHeight; + }; + BackgroundCanvasComponent_1.prototype.initParticles = function () { + this.particles = []; + for (var i = 0; i < this.particleCount; i++) { + this.particles.push({ + x: Math.random() * this.canvas.width, + y: Math.random() * this.canvas.height, + vx: (Math.random() * 2 - 1) * 0.01, + vy: (Math.random() * 2 - 1) * 0.01, + radius: Math.floor(Math.random() * 10) + 1 + }); + } + }; + BackgroundCanvasComponent_1.prototype.animate = function () { + var _this = this; + if (!this.ctx) + return; + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.particles.forEach(function (p) { + p.x += p.vx; + p.y += p.vy; + if (p.x <= 0 || p.x >= _this.canvas.width) + p.vx *= -1; + if (p.y <= 0 || p.y >= _this.canvas.height) + p.vy *= -1; + _this.drawParticle(p); + }); + this.connectParticles(); + requestAnimationFrame(function () { return _this.animate(); }); + }; + BackgroundCanvasComponent_1.prototype.drawParticle = function (p) { + if (!this.ctx) + return; + this.ctx.beginPath(); + this.ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); + this.ctx.fillStyle = '#ffffff'; + this.ctx.fill(); + this.ctx.closePath(); + }; + BackgroundCanvasComponent_1.prototype.connectParticles = function () { + if (!this.ctx) + return; + for (var i = 0; i < this.particles.length; i++) { + for (var j = i + 1; j < this.particles.length; j++) { + var p1 = this.particles[i]; + var p2 = this.particles[j]; + var dx = p2.x - p1.x; + var dy = p2.y - p1.y; + var distance = Math.sqrt(dx * dx + dy * dy); + if (distance < 100) { + var theta = Math.atan2(dy, dx); + var x1 = p1.x + Math.cos(theta) * p1.radius; + var y1 = p1.y + Math.sin(theta) * p1.radius; + var x2 = p2.x - Math.cos(theta) * p2.radius; + var y2 = p2.y - Math.sin(theta) * p2.radius; + this.ctx.beginPath(); + this.ctx.moveTo(x1, y1); + this.ctx.lineTo(x2, y2); + this.ctx.strokeStyle = '#ffffff'; + this.ctx.stroke(); + } + } + } + }; + return BackgroundCanvasComponent_1; + }()); + __setFunctionName(_classThis, "BackgroundCanvasComponent"); + (function () { + var _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; + _canvasRef_decorators = [(0, core_1.ViewChild)('networkCanvas', { static: true })]; + _onResize_decorators = [(0, core_1.HostListener)('window:resize')]; + __esDecorate(_classThis, null, _onResize_decorators, { kind: "method", name: "onResize", static: false, private: false, access: { has: function (obj) { return "onResize" in obj; }, get: function (obj) { return obj.onResize; } }, metadata: _metadata }, null, _instanceExtraInitializers); + __esDecorate(null, null, _canvasRef_decorators, { kind: "field", name: "canvasRef", static: false, private: false, access: { has: function (obj) { return "canvasRef" in obj; }, get: function (obj) { return obj.canvasRef; }, set: function (obj, value) { obj.canvasRef = value; } }, metadata: _metadata }, _canvasRef_initializers, _canvasRef_extraInitializers); + __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); + BackgroundCanvasComponent = _classThis = _classDescriptor.value; + if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); + __runInitializers(_classThis, _classExtraInitializers); + })(); + return BackgroundCanvasComponent = _classThis; +}(); +exports.BackgroundCanvasComponent = BackgroundCanvasComponent; +//# sourceMappingURL=BackgroundCanvas.Component.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.js.map b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.js.map new file mode 100644 index 0000000..bb19247 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.js.map @@ -0,0 +1 @@ +{"version":3,"file":"BackgroundCanvas.Component.js","sourceRoot":"","sources":["BackgroundCanvas.Component.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,sCAA8F;AAoB9F;4BAjBC,IAAA,gBAAS,EAAC;YACP,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,uBAAuB;YACjC,QAAQ,EAAE,kDAET;YACD,MAAM,EAAE,CAAC,qMASR,EAAE;SACN,CAAC;;;;;;;;;;QAQE,qCAAoB,UAAsB,EAAS,UAAuB;YAAtD,eAAU,IAPrB,mDAAyB,EAOd,UAAU,EAAY;YAAS,eAAU,GAAV,UAAU,CAAa;YAN5B,cAAS,4DAAiC;YAChF,WAAM,yDAAqB;YAE3B,cAAS,GAAU,EAAE,CAAC;YACtB,kBAAa,GAAG,GAAG,CAAC;QAG5B,CAAC;QAED,qDAAe,GAAf;YACI,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;YAC3C,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;QAED,8CAAQ,GAAR;YACI,IAAI,CAAC,YAAY,EAAE,CAAC;QACxB,CAAC;QACO,kDAAY,GAApB;YACI,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC;YACtC,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC;QAC5C,CAAC;QACO,mDAAa,GAArB;YACI,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;YACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC9C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;oBAChB,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK;oBACpC,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM;oBACrC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI;oBAClC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI;oBAClC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC;iBAC7C,CAAC,CAAC;YACH,CAAC;QACL,CAAC;QACO,6CAAO,GAAf;YAAA,iBAYC;YAXG,IAAI,CAAC,IAAI,CAAC,GAAG;gBAAE,OAAO;YACtB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAChE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAA,CAAC;gBACxB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACZ,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAI,CAAC,MAAM,CAAC,KAAK;oBAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;gBACrD,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAI,CAAC,MAAM,CAAC,MAAM;oBAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;gBACtD,KAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACrB,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,qBAAqB,CAAC,cAAM,OAAA,KAAI,CAAC,OAAO,EAAE,EAAd,CAAc,CAAC,CAAC;QAChD,CAAC;QACO,kDAAY,GAApB,UAAqB,CAAM;YACvB,IAAI,CAAC,IAAI,CAAC,GAAG;gBAAE,OAAO;YACtB,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;YACrB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAChB,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACzB,CAAC;QACO,sDAAgB,GAAxB;YACI,IAAI,CAAC,IAAI,CAAC,GAAG;gBAAE,OAAO;YACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACjD,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACjD,IAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;oBAC7B,IAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;oBAC7B,IAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;oBACvB,IAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;oBACvB,IAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;oBAE9C,IAAI,QAAQ,GAAG,GAAG,EAAE,CAAC;wBACrB,IAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;wBACjC,IAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;wBAC9C,IAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;wBAC9C,IAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;wBAC9C,IAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;wBAC9C,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;wBACrB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;wBACxB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;wBACxB,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,SAAS,CAAC;wBACjC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;oBAClB,CAAC;gBACL,CAAC;YACD,CAAC;QACL,CAAC;QACL,kCAAC;IAAD,CAAC;;;;iCAlFI,IAAA,gBAAS,EAAC,eAAe,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;gCAgB5C,IAAA,mBAAY,EAAC,eAAe,CAAC;QAC9B,sNAAA,QAAQ,gEAEP;QAnB6C,kNAAA,SAAS,sCAAT,SAAS,6FAAiC;QAD5F,6KAmFC;;;QAnFY,uDAAyB;;;IAmFrC;AAnFY,8DAAyB"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.ts b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.ts new file mode 100644 index 0000000..c76e3f0 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.Component.ts @@ -0,0 +1,104 @@ +import { AfterViewInit, Component, ElementRef, HostListener, ViewChild } from '@angular/core'; +import { ApiService } from '../app.api.service'; + +@Component({ + //standalone: false, + selector: 'app-background-canvas', + template: ` + <canvas #networkCanvas></canvas> + `, + styles: [` + canvas { + display: block; + position: absolute; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + } + `,], +}) +export class BackgroundCanvasComponent { + @ViewChild('networkCanvas', { static: true }) canvasRef!: ElementRef<HTMLCanvasElement>; + private canvas!: HTMLCanvasElement; + private ctx!: CanvasRenderingContext2D | null; + private particles: any[] = []; + private particleCount = 120; + + constructor(private elementRef: ElementRef, public apiService : ApiService) { + } + + ngAfterViewInit(): void { + this.canvas = this.canvasRef.nativeElement; + this.ctx = this.canvas.getContext('2d'); + this.resizeCanvas(); + this.initParticles(); + this.animate(); + } + @HostListener('window:resize') + onResize() { + this.resizeCanvas(); + } + private resizeCanvas(): void { + this.canvas.width = window.innerWidth; + this.canvas.height = window.innerHeight; + } + private initParticles(): void { + this.particles = []; + for (let i = 0; i < this.particleCount; i++) { + this.particles.push({ + x: Math.random() * this.canvas.width, + y: Math.random() * this.canvas.height, + vx: (Math.random() * 2 - 1) * 0.01, + vy: (Math.random() * 2 - 1) * 0.01, + radius: Math.floor(Math.random() * 10) + 1 + }); + } + } + private animate(): void { + if (!this.ctx) return; + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.particles.forEach(p => { + p.x += p.vx; + p.y += p.vy; + if (p.x <= 0 || p.x >= this.canvas.width) p.vx *= -1; + if (p.y <= 0 || p.y >= this.canvas.height) p.vy *= -1; + this.drawParticle(p); + }); + this.connectParticles(); + requestAnimationFrame(() => this.animate()); + } + private drawParticle(p: any): void { + if (!this.ctx) return; + this.ctx.beginPath(); + this.ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); + this.ctx.fillStyle = '#ffffff'; + this.ctx.fill(); + this.ctx.closePath(); + } + private connectParticles(): void { + if (!this.ctx) return; + for (let i = 0; i < this.particles.length; i++) { + for (let j = i + 1; j < this.particles.length; j++) { + const p1 = this.particles[i]; + const p2 = this.particles[j]; + const dx = p2.x - p1.x; + const dy = p2.y - p1.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < 100) { + const theta = Math.atan2(dy, dx); + const x1 = p1.x + Math.cos(theta) * p1.radius; + const y1 = p1.y + Math.sin(theta) * p1.radius; + const x2 = p2.x - Math.cos(theta) * p2.radius; + const y2 = p2.y - Math.sin(theta) * p2.radius; + this.ctx.beginPath(); + this.ctx.moveTo(x1, y1); + this.ctx.lineTo(x2, y2); + this.ctx.strokeStyle = '#ffffff'; + this.ctx.stroke(); + } + } + } + } +} diff --git a/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.js b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.js new file mode 100644 index 0000000..e5f42f8 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.js @@ -0,0 +1,79 @@ +"use strict"; +var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { + function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } + var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; + var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; + var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); + var _, done = false; + for (var i = decorators.length - 1; i >= 0; i--) { + var context = {}; + for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; + for (var p in contextIn.access) context.access[p] = contextIn.access[p]; + context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; + var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); + if (kind === "accessor") { + if (result === void 0) continue; + if (result === null || typeof result !== "object") throw new TypeError("Object expected"); + if (_ = accept(result.get)) descriptor.get = _; + if (_ = accept(result.set)) descriptor.set = _; + if (_ = accept(result.init)) initializers.unshift(_); + } + else if (_ = accept(result)) { + if (kind === "field") initializers.unshift(_); + else descriptor[key] = _; + } + } + if (target) Object.defineProperty(target, contextIn.name, descriptor); + done = true; +}; +var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { + var useValue = arguments.length > 2; + for (var i = 0; i < initializers.length; i++) { + value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); + } + return useValue ? value : void 0; +}; +var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) { + if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; + return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.BackgroundCanvasModule = void 0; +var core_1 = require("@angular/core"); +var kendo_angular_indicators_1 = require("@progress/kendo-angular-indicators"); +var common_1 = require("@angular/common"); +var BackgroundCanvas_Component_1 = require("./BackgroundCanvas.Component"); +var BackgroundCanvasModule = function () { + var _classDecorators = [(0, core_1.NgModule)({ + imports: [ + kendo_angular_indicators_1.IndicatorsModule, + common_1.CommonModule + ], + declarations: [ + BackgroundCanvas_Component_1.BackgroundCanvasComponent + ], + providers: [], + exports: [ + BackgroundCanvas_Component_1.BackgroundCanvasComponent + ] + })]; + var _classDescriptor; + var _classExtraInitializers = []; + var _classThis; + var BackgroundCanvasModule = _classThis = /** @class */ (function () { + function BackgroundCanvasModule_1() { + } + return BackgroundCanvasModule_1; + }()); + __setFunctionName(_classThis, "BackgroundCanvasModule"); + (function () { + var _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; + __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); + BackgroundCanvasModule = _classThis = _classDescriptor.value; + if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); + __runInitializers(_classThis, _classExtraInitializers); + })(); + return BackgroundCanvasModule = _classThis; +}(); +exports.BackgroundCanvasModule = BackgroundCanvasModule; +//# sourceMappingURL=BackgroundCanvas.module.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.js.map b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.js.map new file mode 100644 index 0000000..c0a8b27 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"BackgroundCanvas.module.js","sourceRoot":"","sources":["BackgroundCanvas.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,sCAAyC;AACzC,+EAAsE;AACtE,0CAA+C;AAC/C,2EAAyE;AAiBzE;4BAhBC,IAAA,eAAQ,EAAC;YACN,OAAO,EAAE;gBACL,2CAAgB;gBAChB,qBAAY;aACf;YACD,YAAY,EAAE;gBACV,sDAAyB;aAC5B;YACD,SAAS,EAAE,EAEV;YACD,OAAO,EAAE;gBACL,sDAAyB;aAC5B;SACJ,CAAC;;;;;;QAGF,CAAC;QAAD,+BAAC;IAAD,CAAC;;;;QADD,6KACC;;;QADY,uDAAsB;;;IAClC;AADY,wDAAsB"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.ts b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.ts new file mode 100644 index 0000000..ec0e380 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/BackgroundCanvas/BackgroundCanvas.module.ts @@ -0,0 +1,22 @@ +import { NgModule } from '@angular/core'; +import { IndicatorsModule } from '@progress/kendo-angular-indicators'; +import { CommonModule } from "@angular/common"; +import { BackgroundCanvasComponent } from './BackgroundCanvas.Component'; +@NgModule({ + imports: [ + IndicatorsModule, + CommonModule + ], + declarations: [ + BackgroundCanvasComponent + ], + providers: [ + + ], + exports: [ + BackgroundCanvasComponent + ] +}) + +export class BackgroundCanvasModule { +} diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.js b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.js new file mode 100644 index 0000000..3a4cca1 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.js @@ -0,0 +1,33 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CourseProgress = void 0; +var baseObject_1 = require("../Basic/baseObject"); +var CourseProgress = /** @class */ (function (_super) { + __extends(CourseProgress, _super); + function CourseProgress() { + var _this = _super.call(this) || this; + _this.completionPercentage = 0; + _this.startDate = new Date(); + _this.isCourseComplete = false; + _this.lastActivityDate = new Date(); + return _this; + } + return CourseProgress; +}(baseObject_1.BaseObject)); +exports.CourseProgress = CourseProgress; +//# sourceMappingURL=course-progress.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.js.map b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.js.map new file mode 100644 index 0000000..3ea5722 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.js.map @@ -0,0 +1 @@ +{"version":3,"file":"course-progress.js","sourceRoot":"","sources":["course-progress.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAAiD;AAGjD;IAAoC,kCAAU;IAC1C;QACI,YAAA,MAAK,WAAE,SAAC;QACR,KAAI,CAAC,oBAAoB,GAAG,CAAC,CAAC;QAC9B,KAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;QAC5B,KAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAC9B,KAAI,CAAC,gBAAgB,GAAG,IAAI,IAAI,EAAE,CAAC;;IACvC,CAAC;IAUL,qBAAC;AAAD,CAAC,AAjBD,CAAoC,uBAAU,GAiB7C;AAjBY,wCAAc"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.ts b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.ts new file mode 100644 index 0000000..6116312 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-progress.ts @@ -0,0 +1,21 @@ +import { BaseObject } from '../Basic/baseObject'; +import { LessonProgress } from './lesson-progress'; + +export class CourseProgress extends BaseObject{ + constructor() { + super(); + this.completionPercentage = 0; + this.startDate = new Date(); + this.isCourseComplete = false; + this.lastActivityDate = new Date(); + } + + userID: number; + courseID: number; + enrollmentID: number; + completionPercentage: number; + startDate: Date; + isCourseComplete: boolean; + endDate?: Date; + lastActivityDate?: Date; +} diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.js b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.js new file mode 100644 index 0000000..7629fb8 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.js @@ -0,0 +1,35 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CourseType = void 0; +var baseObject_1 = require("../Basic/baseObject"); +var enums_1 = require("../enums"); +var CourseType = /** @class */ (function (_super) { + __extends(CourseType, _super); + function CourseType() { + var _this = _super.call(this) || this; + _this.description = ""; + _this.tags = ""; + _this.popularityScore = 0.0; + _this.parentTypeID = null; + _this.status = enums_1.EnumStatus.Active; + return _this; + } + return CourseType; +}(baseObject_1.BaseObject)); +exports.CourseType = CourseType; +//# sourceMappingURL=course-type.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.js.map b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.js.map new file mode 100644 index 0000000..ed6a4bb --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.js.map @@ -0,0 +1 @@ +{"version":3,"file":"course-type.js","sourceRoot":"","sources":["course-type.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAAiD;AACjD,kCAAoC;AAEpC;IAAgC,8BAAU;IACtC;QACI,YAAA,MAAK,WAAE,SAAC;QACR,KAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,KAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACf,KAAI,CAAC,eAAe,GAAG,GAAG,CAAC;QAC3B,KAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,KAAI,CAAC,MAAM,GAAG,kBAAU,CAAC,MAAM,CAAC;;IACpC,CAAC;IAOL,iBAAC;AAAD,CAAC,AAfD,CAAgC,uBAAU,GAezC;AAfY,gCAAU"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.ts b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.ts new file mode 100644 index 0000000..7f88824 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/course-type.ts @@ -0,0 +1,19 @@ +import { BaseObject } from '../Basic/baseObject'; +import {EnumStatus} from '../enums'; + +export class CourseType extends BaseObject { + constructor() { + super(); + this.description = ""; + this.tags = ""; + this.popularityScore = 0.0; + this.parentTypeID = null; + this.status = EnumStatus.Active; + } + + name : string; + description : string; + parentTypeID? : number; + tags : string; + popularityScore : number; +} diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.js b/HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.js new file mode 100644 index 0000000..32efa88 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.js @@ -0,0 +1,58 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Courses = void 0; +var baseObject_1 = require("../Basic/baseObject"); +var enums_1 = require("../enums"); +var lmstemplate_1 = require("./lmstemplate"); +var Courses = /** @class */ (function (_super) { + __extends(Courses, _super); + function Courses() { + var _this = _super.call(this) || this; + _this.lessonProgres = []; + _this.courseTitle = ""; + _this.courseCode = ""; + _this.courseTypeID = 0; + _this.description = ""; + _this.difficultyLevel = enums_1.EnumDifficultyLevel.Beginner; + _this.trainerID = 0; + _this.duration = ""; + _this.status = enums_1.EnumStatus.Active; + _this.isForFree = true; + _this.price = 0; + _this.totalEnrollments = 0; + _this.rating = 0.0; + _this.popularityScore = 0.0; + _this.completionRate = 0.0; + _this.averageProgress = 0.0; + _this.isExamRequired = false; + _this.tags = ""; + _this.targetedAudience = ""; + _this.learningOutcomes = ""; + _this.courseIncludes = ""; + _this.startGuideLine = ""; + _this.createdUser = ""; + _this.lessons = []; + _this.totalLesson = 0; + _this.totalContent = 0; + _this.courseTemplate = new lmstemplate_1.LMSTemplate(); + return _this; + } + return Courses; +}(baseObject_1.BaseObject)); +exports.Courses = Courses; +//# sourceMappingURL=courses.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.js.map b/HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.js.map new file mode 100644 index 0000000..f1645af --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.js.map @@ -0,0 +1 @@ +{"version":3,"file":"courses.js","sourceRoot":"","sources":["courses.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAAiD;AAEjD,kCAA2D;AAM3D,6CAA4C;AAE5C;IAA6B,2BAAU;IACnC;QACI,YAAA,MAAK,WAAE,SAAC;QA2DZ,mBAAa,GAAuB,EAAE,CAAC;QAzDnC,KAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,KAAI,CAAC,UAAU,GAAG,EAAE,CAAC;QACrB,KAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,KAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,KAAI,CAAC,eAAe,GAAG,2BAAmB,CAAC,QAAQ,CAAC;QACpD,KAAI,CAAC,SAAS,GAAG,CAAC,CAAC;QACnB,KAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,KAAI,CAAC,MAAM,GAAG,kBAAU,CAAC,MAAM,CAAC;QAChC,KAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,KAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QACf,KAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC;QAC1B,KAAI,CAAC,MAAM,GAAG,GAAG,CAAC;QAClB,KAAI,CAAC,eAAe,GAAG,GAAG,CAAC;QAC3B,KAAI,CAAC,cAAc,GAAG,GAAG,CAAC;QAC1B,KAAI,CAAC,eAAe,GAAG,GAAG,CAAC;QAC3B,KAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAC5B,KAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACf,KAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC3B,KAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC3B,KAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QACzB,KAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QACzB,KAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,KAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,KAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QACrB,KAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,KAAI,CAAC,cAAc,GAAG,IAAI,yBAAW,EAAE,CAAC;;IAC5C,CAAC;IAgCL,cAAC;AAAD,CAAC,AA9DD,CAA6B,uBAAU,GA8DtC;AA9DY,0BAAO"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.ts b/HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.ts new file mode 100644 index 0000000..1d482ba --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/courses.ts @@ -0,0 +1,73 @@ +import { BaseObject } from '../Basic/baseObject'; +import { Employee } from '../Employee/employee'; +import { EnumDifficultyLevel, EnumStatus } from '../enums'; +import { CourseProgress } from './course-progress'; +import { CourseType } from './course-type'; +import { Enrollments } from './enrollments'; +import { LessonProgress } from './lesson-progress'; +import { Lessons } from './lessons'; +import { LMSTemplate } from './lmstemplate'; + +export class Courses extends BaseObject { + constructor() { + super(); + + this.courseTitle = ""; + this.courseCode = ""; + this.courseTypeID = 0; + this.description = ""; + this.difficultyLevel = EnumDifficultyLevel.Beginner; + this.trainerID = 0; + this.duration = ""; + this.status = EnumStatus.Active; + this.isForFree = true; + this.price = 0; + this.totalEnrollments = 0; + this.rating = 0.0; + this.popularityScore = 0.0; + this.completionRate = 0.0; + this.averageProgress = 0.0; + this.isExamRequired = false; + this.tags = ""; + this.targetedAudience = ""; + this.learningOutcomes = ""; + this.courseIncludes = ""; + this.startGuideLine = ""; + this.createdUser = ""; + this.lessons = []; + this.totalLesson = 0; + this.totalContent = 0; + this.courseTemplate = new LMSTemplate(); + } + courseTitle: string; + courseCode: string; + courseTypeID: number; + description: string; + difficultyLevel: EnumDifficultyLevel; + trainerID: number; + duration?: string; + isForFree?: boolean; + price?: number; + totalEnrollments?: number; + rating?: number; + popularityScore?: number; + completionRate?: number; + averageProgress?: number; + isExamRequired?: boolean; + tags?: string; + targetedAudience?: string; + learningOutcomes?: string; + courseIncludes?: string; + startGuideLine?: string; + lessons : Lessons[]; + courseTemplate : LMSTemplate; + courseType : CourseType; + totalLesson : number; + totalContent : number; + trainer : Employee; + createdUser : string; + + enrollments : Enrollments; + courseProgres : CourseProgress; + lessonProgres : LessonProgress [] = []; +} diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.js b/HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.js new file mode 100644 index 0000000..1a3d8fc --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.js @@ -0,0 +1,35 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Enrollments = void 0; +var baseObject_1 = require("../Basic/baseObject"); +var Enrollments = /** @class */ (function (_super) { + __extends(Enrollments, _super); + function Enrollments() { + var _this = _super.call(this) || this; + _this.enrollmentID = 0; + _this.userID = 0; + _this.courseID = 0; + _this.enrollDate = new Date(); + _this.isCurrentEnroll = false; + _this.noOfEnroll = 0; + return _this; + } + return Enrollments; +}(baseObject_1.BaseObject)); +exports.Enrollments = Enrollments; +//# sourceMappingURL=enrollments.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.js.map b/HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.js.map new file mode 100644 index 0000000..9b94cd6 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.js.map @@ -0,0 +1 @@ +{"version":3,"file":"enrollments.js","sourceRoot":"","sources":["enrollments.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAAiD;AAGjD;IAAiC,+BAAU;IACvC;QACI,YAAA,MAAK,WAAE,SAAC;QACR,KAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,KAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAChB,KAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,KAAI,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;QAC7B,KAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,KAAI,CAAC,UAAU,GAAG,CAAC,CAAC;;IACxB,CAAC;IASL,kBAAC;AAAD,CAAC,AAlBD,CAAiC,uBAAU,GAkB1C;AAlBY,kCAAW"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.ts b/HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.ts new file mode 100644 index 0000000..37b05ce --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/enrollments.ts @@ -0,0 +1,22 @@ +import { BaseObject } from '../Basic/baseObject'; +import { CourseProgress } from './course-progress'; + +export class Enrollments extends BaseObject { + constructor() { + super(); + this.enrollmentID = 0; + this.userID = 0; + this.courseID = 0; + this.enrollDate = new Date(); + this.isCurrentEnroll = false; + this.noOfEnroll = 0; + } + enrollmentID: number; + userID: number; + courseID: number; + enrollDate: Date; + isCurrentEnroll: boolean; + noOfEnroll : number; + + courseProgres : CourseProgress; +} diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.js b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.js new file mode 100644 index 0000000..6385b08 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.js @@ -0,0 +1,42 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LessonContent = void 0; +var baseObject_1 = require("../Basic/baseObject"); +var enums_1 = require("../enums"); +var LessonContent = /** @class */ (function (_super) { + __extends(LessonContent, _super); + function LessonContent() { + var _this = _super.call(this) || this; + _this.isLessonComplete = false; + _this.isComplete = true; + _this.isNotComplete = false; + _this.lessonID = 0; + _this.contentTitle = ""; + _this.description = ""; + _this.contentUrl = ""; + _this.duration = ""; + _this.isMendatory = false; + _this.status = enums_1.EnumStatus.Active; + _this.accessLevel = enums_1.EnumAccessLevel.Public; + _this.accessCount = 0; + return _this; + } + return LessonContent; +}(baseObject_1.BaseObject)); +exports.LessonContent = LessonContent; +//# sourceMappingURL=lesson-content.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.js.map b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.js.map new file mode 100644 index 0000000..1941f9d --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.js.map @@ -0,0 +1 @@ +{"version":3,"file":"lesson-content.js","sourceRoot":"","sources":["lesson-content.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAAiD;AACjD,kCAA2F;AAE3F;IAAmC,iCAAU;IAEzC;QACI,YAAA,MAAK,WAAE,SAAC;QAsBZ,sBAAgB,GAAY,KAAK,CAAC;QAClC,gBAAU,GAAa,IAAI,CAAC;QAC5B,mBAAa,GAAa,KAAK,CAAC;QAvB5B,KAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,KAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACvB,KAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,KAAI,CAAC,UAAU,GAAG,EAAE,CAAC;QACrB,KAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,KAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,KAAI,CAAC,MAAM,GAAG,kBAAU,CAAC,MAAM,CAAC;QAChC,KAAI,CAAC,WAAW,GAAG,uBAAe,CAAC,MAAM,CAAC;QAC1C,KAAI,CAAC,WAAW,GAAG,CAAC,CAAC;;IACzB,CAAC;IAeL,oBAAC;AAAD,CAAC,AA5BD,CAAmC,uBAAU,GA4B5C;AA5BY,sCAAa"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.ts b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.ts new file mode 100644 index 0000000..40386ae --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-content.ts @@ -0,0 +1,32 @@ +import { BaseObject } from '../Basic/baseObject'; +import { EnumStatus, EnumAccessLevel, EnumContentSource, EnumContentType } from '../enums'; + +export class LessonContent extends BaseObject{ + + constructor() { + super(); + this.lessonID = 0; + this.contentTitle = ""; + this.description = ""; + this.contentUrl = ""; + this.duration = ""; + this.isMendatory = false; + this.status = EnumStatus.Active; + this.accessLevel = EnumAccessLevel.Public; + this.accessCount = 0; + } + lessonID: number; // Foreign Key to LMS_Lessons + contentTitle: string; + contentType: EnumContentType; + description: string; + contentUrl: string; + duration?: string; + isMendatory?: boolean; + accessLevel: EnumAccessLevel; + contentSource: EnumContentSource; + accessCount?: number; + + isLessonComplete: boolean = false; + isComplete : boolean = true; + isNotComplete : boolean = false; +} diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.js b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.js new file mode 100644 index 0000000..31c5c86 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.js @@ -0,0 +1,34 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LessonProgress = void 0; +var baseObject_1 = require("../Basic/baseObject"); +var LessonProgress = /** @class */ (function (_super) { + __extends(LessonProgress, _super); + function LessonProgress() { + var _this = _super.call(this) || this; + _this.isLessonComplete = false; + _this.completionPercentage = 0; + _this.startDate = new Date(); + _this.lastAccessed = new Date(); + _this.retryCount = 0; + return _this; + } + return LessonProgress; +}(baseObject_1.BaseObject)); +exports.LessonProgress = LessonProgress; +//# sourceMappingURL=lesson-progress.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.js.map b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.js.map new file mode 100644 index 0000000..86232df --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.js.map @@ -0,0 +1 @@ +{"version":3,"file":"lesson-progress.js","sourceRoot":"","sources":["lesson-progress.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAAiD;AAGjD;IAAoC,kCAAU;IAC1C;QACI,YAAA,MAAK,WAAE,SAAC;QACR,KAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAC9B,KAAI,CAAC,oBAAoB,GAAG,CAAC,CAAC;QAC9B,KAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;QAC5B,KAAI,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC;QAC/B,KAAI,CAAC,UAAU,GAAG,CAAC,CAAC;;IACxB,CAAC;IAeL,qBAAC;AAAD,CAAC,AAvBD,CAAoC,uBAAU,GAuB7C;AAvBY,wCAAc"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.ts b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.ts new file mode 100644 index 0000000..ed4f4bf --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lesson-progress.ts @@ -0,0 +1,27 @@ +import { BaseObject } from '../Basic/baseObject'; +import { Lessons } from './lessons'; + +export class LessonProgress extends BaseObject{ + constructor() { + super(); + this.isLessonComplete = false; + this.completionPercentage = 0; + this.startDate = new Date(); + this.lastAccessed = new Date(); + this.retryCount = 0; + } + + userID: number; + lessonID: number; + courseProgressID: number; + lessonContentID: number; + isLessonComplete: boolean; + completionPercentage: number; + startDate: Date; + completeDate: Date; + timeSpent: string; + lastAccessed: Date; + retryCount: number; + + lesson : Lessons; +} diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.js b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.js new file mode 100644 index 0000000..fa1b905 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.js @@ -0,0 +1,40 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Lessons = void 0; +var baseObject_1 = require("../Basic/baseObject"); +var enums_1 = require("../enums"); +var Lessons = /** @class */ (function (_super) { + __extends(Lessons, _super); + function Lessons() { + var _this = _super.call(this) || this; + _this.lessonContents = []; + _this.courseID = 0; + _this.lessonTitle = ""; + _this.lessonNo = ""; + _this.description = ""; + _this.duration = ""; + _this.difficultyLevel = enums_1.EnumDifficultyLevel.Beginner; + _this.isQuizAvailable = false; + _this.status = enums_1.EnumStatus.Active; + _this.lessonContents = []; + return _this; + } + return Lessons; +}(baseObject_1.BaseObject)); +exports.Lessons = Lessons; +//# sourceMappingURL=lessons.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.js.map b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.js.map new file mode 100644 index 0000000..c7844ab --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.js.map @@ -0,0 +1 @@ +{"version":3,"file":"lessons.js","sourceRoot":"","sources":["lessons.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAAiD;AACjD,kCAA2D;AAI3D;IAA6B,2BAAU;IACnC;QACI,YAAA,MAAK,WAAE,SAAC;QAkBZ,oBAAc,GAAqB,EAAE,CAAC;QAjBlC,KAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,KAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,KAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,KAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACtB,KAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,KAAI,CAAC,eAAe,GAAG,2BAAmB,CAAC,QAAQ,CAAC;QACpD,KAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC7B,KAAI,CAAC,MAAM,GAAG,kBAAU,CAAC,MAAM,CAAC;QAChC,KAAI,CAAC,cAAc,GAAG,EAAE,CAAC;;IAC7B,CAAC;IAUL,cAAC;AAAD,CAAC,AAtBD,CAA6B,uBAAU,GAsBtC;AAtBY,0BAAO"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.ts b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.ts new file mode 100644 index 0000000..3997661 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lessons.ts @@ -0,0 +1,28 @@ +import { BaseObject } from '../Basic/baseObject'; +import { EnumDifficultyLevel, EnumStatus } from '../enums'; +import { LessonContent } from './lesson-content'; +import { LMSTemplate } from './lmstemplate'; + +export class Lessons extends BaseObject { + constructor() { + super(); + this.courseID = 0; + this.lessonTitle = ""; + this.lessonNo = ""; + this.description = ""; + this.duration = ""; + this.difficultyLevel = EnumDifficultyLevel.Beginner; + this.isQuizAvailable = false; + this.status = EnumStatus.Active; + this.lessonContents = []; + } + courseID: number; // Foreign Key to LMS_Courses + lessonTitle: string; + lessonNo: string; + description: string; + duration?: string; + difficultyLevel: EnumDifficultyLevel; + isQuizAvailable?: boolean; + lessonContents : LessonContent[] = []; + lessonTemplate : LMSTemplate; +} diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.js b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.js new file mode 100644 index 0000000..870c74e --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.js @@ -0,0 +1,32 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LMSTemplate = void 0; +var baseObject_1 = require("../Basic/baseObject"); +var LMSTemplate = /** @class */ (function (_super) { + __extends(LMSTemplate, _super); + function LMSTemplate() { + var _this = _super.call(this) || this; + _this.templateID = 0; + _this.objectID = 0; + _this.fileName = ""; + return _this; + } + return LMSTemplate; +}(baseObject_1.BaseObject)); +exports.LMSTemplate = LMSTemplate; +//# sourceMappingURL=lmstemplate.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.js.map b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.js.map new file mode 100644 index 0000000..bc30cde --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.js.map @@ -0,0 +1 @@ +{"version":3,"file":"lmstemplate.js","sourceRoot":"","sources":["lmstemplate.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAAA,kDAAiD;AAGjD;IAAiC,+BAAU;IACvC;QACI,YAAA,MAAK,WAAE,SAAC;QACR,KAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,KAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,KAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;;IACvB,CAAC;IAUL,kBAAC;AAAD,CAAC,AAhBD,CAAiC,uBAAU,GAgB1C;AAhBY,kCAAW"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.ts b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.ts new file mode 100644 index 0000000..0ae1626 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/LearningManagement/lmstemplate.ts @@ -0,0 +1,20 @@ +import { BaseObject } from '../Basic/baseObject'; +import { EnumTemplateType } from "../enums"; + +export class LMSTemplate extends BaseObject { + constructor() { + super(); + this.templateID = 0; + this.objectID = 0; + this.fileName = ""; + } + templateID : number; + objectID : number; + templateType : EnumTemplateType + fileName : string; + fileData : string ; + fileType : number; + currentFile: any; + fileAsByteArray : string; + fileTobase64 : string; +} diff --git a/HRM.UI/ClientApp/src/app/_models/OpenAI BO/OpenAIResponseWithData.ts b/HRM.UI/ClientApp/src/app/_models/OpenAI BO/OpenAIResponseWithData.ts new file mode 100644 index 0000000..db3b33a --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/OpenAI BO/OpenAIResponseWithData.ts @@ -0,0 +1,14 @@ +import { BaseObject } from '../Basic/baseObject'; +import { EnumReportGroupType } from '../enums'; + +export class OpenAIResponseWithData{ + constructor() { + } + IsChart: boolean = false; + ChartType: string = ""; + Title: string = ""; + Labels: string[] = []; + Values: number[] = [];; + TableData: any; + LabelPosition: string = 'outsideEnd'; +} diff --git a/HRM.UI/ClientApp/src/app/_models/OpenAIQueryLog/OpenAIQueryLog.ts b/HRM.UI/ClientApp/src/app/_models/OpenAIQueryLog/OpenAIQueryLog.ts new file mode 100644 index 0000000..c496b52 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_models/OpenAIQueryLog/OpenAIQueryLog.ts @@ -0,0 +1,18 @@ +import { BaseObject } from '../Basic/baseObject'; +import { EnumAIChartType, EnumReportGroupType } from '../enums'; + +export class OpenAIQueryLog extends BaseObject { + constructor() { + super(); + } + userId : number = 0; + userPrompt : string = ""; + generatedQuery : string = ""; + executedAt : Date = new Date(); + totalExecution : number = 0; + reportGroup: EnumReportGroupType; + isChart : boolean = false; + chartType : EnumAIChartType = EnumAIChartType.None; + title : string = ""; + isCommon : boolean = false; +} diff --git a/HRM.UI/ClientApp/src/app/_models/enums.ts b/HRM.UI/ClientApp/src/app/_models/enums.ts index 6998a92..b8e472b 100644 --- a/HRM.UI/ClientApp/src/app/_models/enums.ts +++ b/HRM.UI/ClientApp/src/app/_models/enums.ts @@ -16,11 +16,70 @@ export enum EnumHrNotificationType { System_Generated = 2, } +export enum EnumDifficultyLevel { + Beginner = 1, + Intermediate = 2, + Advanced = 3 +} + +export enum EnumContentSource { + Youtube = 1, + GoogleDrive = 2, + OneDrive = 3, + AzureBlobStorage = 4, +} + +export enum EnumAccessLevel { + Public = 1, + Private = 2, + Restricted = 3, + Premium = 4 +} + +export enum EnumAIChartType +{ + None = 0, + BarChart = 1, + LineGraph = 2, + PieChart = 3, + ScatterPlot = 4, + AreaChart = 5, + Histogram = 6, + WaterfallChart = 7, + GantChart = 8, + RadarChart = 9, + ActivityDiagram = 10 +} + +export enum EnumContentType { + Video = 1, + Image = 2, + Pdf = 3, + Powerpoint = 4, + Excel = 5, + Doc = 6, + others = 7 +} + +export enum EnumReportGroupType +{ + Employee = 1, + Salary = 2, + Performance = 3, + Training = 4, + Leave = 5, + Attendance = 6, +} + export enum EnumTaxInvestment { Regardless = 0, ESS = 1, Admin = 2, } +export enum EnumTemplateType { + Course = 1, + Lesson = 2, +} export enum EnumTaxInvestmentStatus { Not_Yet_Approve = 0, Approve = 1, @@ -110,7 +169,7 @@ export enum EnumLeaveCalculationType { // Hourly_Prorated = 6, // Days_365 = 7, // Yearly_Without_Prorated = 8, - + Daily = 0, Monthly = 1, Yearly = 2, @@ -118,7 +177,7 @@ export enum EnumLeaveCalculationType { Not_Applicable = 4, Not_Applicable_With_Prorated = 5, - + Hourly = 6, //Yearly = 7, Monthly_Earn = 8, @@ -1348,7 +1407,7 @@ export enum EnumMaternityLeaveStatus { FullPayment = 2, } export enum EnumMaternityItemDetailType { - GrossSalary = 1, //GrossSalary=Basic + House Rent allowance + Conveyance Allowance + Special Allowance + GrossSalary = 1, //GrossSalary=Basic + House Rent allowance + Conveyance Allowance + Special Allowance OT = 2, ExtraWorkAllowance = 3, BonusFestival = 4, diff --git a/HRM.UI/ClientApp/src/app/_services/HRMOpenAI/hrmopen-ai.service.ts b/HRM.UI/ClientApp/src/app/_services/HRMOpenAI/hrmopen-ai.service.ts new file mode 100644 index 0000000..374bc4c --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_services/HRMOpenAI/hrmopen-ai.service.ts @@ -0,0 +1,32 @@ +import { EnumReportGroupType } from './../../_models/enums'; +import {Injectable} from '@angular/core'; +import {HttpClient, HttpEvent, HttpRequest} from '@angular/common/http'; +import {ApiService} from '../../app.api.service'; +import { OpenAIQueryLog } from 'src/app/_models/OpenAIQueryLog/OpenAIQueryLog'; + +@Injectable({ + providedIn: 'root' +}) +export class HRMOpenAIService { + constructor(public apiService: ApiService, + private http: HttpClient) { + } + GetAiInformation(userCommand : string) { + return this.apiService.httpGet<any>('/HRMApenAPIAI/GetAiInformation/' + userCommand); + } + AskOpenAI(data: any) { + return this.apiService.httpPost<any>('/HRMApenAPIAI/AskOpenAI', data); + } + GetReportColumnDefinition(reportGroupType : EnumReportGroupType) { + return this.apiService.httpGet<any[]>('/HRMApenAPIAI/GetReportColumnDefinition/' + reportGroupType); + } + GetCurrentUserAllQueryLog(reportGroupType : EnumReportGroupType) { + return this.apiService.httpGet<OpenAIQueryLog[]>('/HRMApenAPIAI/GetCurrentUserAllQueryLog/' + reportGroupType); + } + OpenAIPreviousQueryExecution(data: any) { + return this.apiService.httpPost<any>('/HRMApenAPIAI/OpenAIPreviousQueryExecution', data); + } + ChatShareWithOthers(data: any) { + return this.apiService.httpPost('/HRMApenAPIAI/ChatShareWithOthers', data); + } +} diff --git a/HRM.UI/ClientApp/src/app/_services/LearningManagement/lms.service.ts b/HRM.UI/ClientApp/src/app/_services/LearningManagement/lms.service.ts new file mode 100644 index 0000000..35a984f --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_services/LearningManagement/lms.service.ts @@ -0,0 +1,78 @@ +import {Injectable} from '@angular/core'; +import {HttpClient, HttpEvent, HttpRequest} from '@angular/common/http'; +import {ApiService} from '../../app.api.service'; +import { CourseType } from 'src/app/_models/LearningManagement/course-type'; +import { EnumStatus } from 'src/app/_models/enums'; +import { Courses } from 'src/app/_models/LearningManagement/courses'; +import { CourseProgress } from 'src/app/_models/LearningManagement/course-progress'; + +@Injectable({ + providedIn: 'root' +}) +export class LMSService { + + constructor(public apiService: ApiService, + private http: HttpClient) { + + } + + //#region Course Type + saveCourseType(param: any) { + return this.apiService.httpPost<CourseType>('/LMS/saveCourseType', param); + } + deleteCourseType(id : number) { + return this.apiService.httpGet('/LMS/deleteCourseType/' + id); + } + GetCourseTypeByStatus(status : EnumStatus) { + return this.apiService.httpGet<CourseType[]>('/LMS/getByStatus/' + status); + } + GetAllCourseType() { + return this.apiService.httpGet<CourseType[]>('/LMS/getAllCourseType'); + } + getByParentID(parentID : number) { + return this.apiService.httpGet<CourseType[]>('/LMS/getAllCourseType/'+parentID); + } + //#endregion + + //#region Courses + saveCourses(param: any) { + return this.apiService.httpPost<Courses>('/LMS/saveCourses', param); + } + deleteCourses(id : number) { + return this.apiService.httpGet('/LMS/deleteCourses/' + id); + } + GetCoursesByStatus(status : EnumStatus) { + return this.apiService.httpGet<Courses[]>('/LMS/getCoursesByStatus/' + status); + } + GetCourse(ID : number) { + return this.apiService.httpGet<Courses>('/LMS/getCourse/' + ID); + } + GetCourseForUser(ID : number) { + return this.apiService.httpGet<Courses>('/LMS/GetCourseForUser/' + ID); + } + GetCourseForEnrollment(ID : number) { + return this.apiService.httpGet<Courses>('/LMS/GetCourseForEnrollment/' + ID); + } + GetAllCourses() { + return this.apiService.httpGet<Courses[]>('/LMS/getAllCourses'); + } + //#endregion + + //#region Lessons + deleteLessons(id : number) { + return this.apiService.httpGet('/LMS/deleteLessons/' + id); + } + //#endregion + + //#region Lesson Content + deleteLessonContent(id : number) { + return this.apiService.httpGet('/LMS/deleteLessonContent/' + id); + } + //#endregion + + //#region LESSON Progress + UserCourseProgress(param: any) { + return this.apiService.httpPost<Courses>('/LMS/UserCourseProgress', param); + } + //#endregion +} diff --git a/HRM.UI/ClientApp/src/app/_services/payment-approval/payment-approval.service.ts b/HRM.UI/ClientApp/src/app/_services/payment-approval/payment-approval.service.ts new file mode 100644 index 0000000..b646c73 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/_services/payment-approval/payment-approval.service.ts @@ -0,0 +1,9 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class PaymentApprovalService { + + constructor() { } +} diff --git a/HRM.UI/ClientApp/src/app/app.api.service.ts b/HRM.UI/ClientApp/src/app/app.api.service.ts index a32862a..c0e09b3 100644 --- a/HRM.UI/ClientApp/src/app/app.api.service.ts +++ b/HRM.UI/ClientApp/src/app/app.api.service.ts @@ -30,7 +30,7 @@ export class ApiService { // public static BASE_URL = 'https://localhost:44374/api'; // public base_url = 'https://localhost:44374/api'; - // For patromaxHR + // For patromaxHR // public base_url = '/petromaxHR/api'; // public static BASE_URL = '/petromaxHR/api'; @@ -97,11 +97,11 @@ export class ApiService { // For EchoTex Live // public static BASE_URL = '/api'; - // public base_url = '/api'; + // public base_url = '/api'; // For EchoTex Data Migration //public static BASE_URL = '/EchotexDataMigration/api'; - //public base_url = '/EchotexDataMigration/api'; + //public base_url = '/EchotexDataMigration/api'; // For SGS // public static BASE_URL = '/sgsrevamp/api'; @@ -129,7 +129,7 @@ export class ApiService { public selectedMenuName: string = ''; constructor(public httpClient: HttpClient, public datepipe: DatePipe) { - + // ApiService.PAYROLLTYPEID=this.payrollTypeId; // ApiService.USERTYPE = this.userType; const baseHref = document.querySelector('base').getAttribute('href'); @@ -171,7 +171,7 @@ export class ApiService { getApiDefaultBoolData(value: boolean) { return (value === undefined || value === null) ? false : value; } - + getApiDateString(dDate: Date) { if (dDate === undefined || dDate === null) { return dDate; diff --git a/HRM.UI/ClientApp/src/app/app.module.ts b/HRM.UI/ClientApp/src/app/app.module.ts index d457a90..9c4f142 100644 --- a/HRM.UI/ClientApp/src/app/app.module.ts +++ b/HRM.UI/ClientApp/src/app/app.module.ts @@ -97,6 +97,7 @@ import {ProgressBarModule} from '@progress/kendo-angular-progressbar'; import {RippleModule} from 'primeng/ripple'; import {MsalGuard, MsalInterceptor, MsalModule, MsalRedirectComponent, MsalService} from '@azure/msal-angular'; import {InteractionType, PublicClientApplication} from '@azure/msal-browser'; +import { HRMOpenAIModule } from './hrmopen-ai/hrmopen-ai.module'; FullCalendarModule.registerPlugins([ dayGridPlugin, @@ -170,6 +171,7 @@ FullCalendarModule.registerPlugins([ CardModule, TabViewModule, Pickermodule, + HRMOpenAIModule, NgxExtendedPdfViewerModule, ProgressBarModule, MsalModule.forRoot(new PublicClientApplication( diff --git a/HRM.UI/ClientApp/src/app/app.routes.ts b/HRM.UI/ClientApp/src/app/app.routes.ts index 849e797..a2591f4 100644 --- a/HRM.UI/ClientApp/src/app/app.routes.ts +++ b/HRM.UI/ClientApp/src/app/app.routes.ts @@ -202,7 +202,12 @@ export const routes: Routes = [ path: 'dataintegration', canActivate: [AuthGuard], loadChildren: () => import('./data-integration/data-integration.module').then(m => m.DataIntegrationModule) - } + }, + { + path: 'hrm', + canActivate: [AuthGuard], + loadChildren: () => import('./hrmopen-ai/hrmopen-ai.module').then(m => m.HRMOpenAIModule) + } ]; export const AppRoutes: ModuleWithProviders<any> = RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' }); diff --git a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-routing.module.ts b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-routing.module.ts new file mode 100644 index 0000000..0f1997a --- /dev/null +++ b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-routing.module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { HRMOpenAITestComponent } from './hrmopen-ai-test/hrmopen-aiteast.component'; +import { AuthGuard } from '../_guards/auth.guard'; + +export const routes: Routes = [ + { path: 'hrm-ai', component: HRMOpenAITestComponent, canActivate: [AuthGuard] }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class HRMOpenAIRoutingModule { } diff --git a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.html b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.html new file mode 100644 index 0000000..000a4a9 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.html @@ -0,0 +1,342 @@ +<app-loading-panel></app-loading-panel> + +<div class="card shadow-sm rounded p-2" style="background-color: #ffffff; border: 1px solid #d9d9d9;height: 100%;"> + <div class="card textarea-wrapper" style="position: relative; width: 100%;padding: 5px !important;"> + <div class="p-grid"> + <div class="p-col-7"> + <div class="textarea-wrapper"> + <textarea (input)="onInputChange()" [(ngModel)]="inputUserCommand" rows="4" class="formAITextArea" + placeholder="Ask me anything related to payroll or HR tasks..."></textarea> + <button *ngIf="inputUserCommand || filteredSuggestions.length > 0" type="button" class="clear-btn" + (click)="clearInput()">✖</button> + </div> + <ul *ngIf="filteredSuggestions.length > 0" class="suggestions-dropdown"> + <li *ngFor="let item of filteredSuggestions" (click)="selectSuggestion(item)" + class="suggestion-item" [ngClass]="{ 'chart-query': item.isChart }"> + <span class="suggestion-text">{{ item.userPrompt }}</span> + <span *ngIf="item.isChart" class="chart-icon">📊</span> + </li> + </ul> + <!--<div class="flex justify-content-between align-items-center"> + <div class="flex p-gap-2 align-items-center"> + <kendo-dropdownlist + [data]="reportGroups" + [defaultItem]="{ description: 'Select AI Purpose....', value: null }" + [textField]="'description'" + [valueField]="'value'" + [valuePrimitive]="true" + [(ngModel)]="selectedReportGroup" + (valueChange)="onReportGroupChange($event)" + class="w-14rem"> + </kendo-dropdownlist> + <button (click)="openAvailableInfoDialog()" [disabled]="selectedReportGroup === null || selectedReportGroup === undefined" kendoButton themeColor="secondary">Field Column</button> + </div> + <div class="flex p-gap-2 align-items-center"> + <button [disabled]="selectedReportGroup === null || selectedReportGroup === undefined" + kendoButton themeColor="primary" + (click)="ShowAllQueryLog()">Chat History</button> + <button kendoButton themeColor="primary" + (click)="onCallOpenAI_One()" + (keydown.enter)="handleEnterKey($event)">Execute</button> + </div> + </div>--> + <div class="toolbar-container"> + + <!-- Left Side --> + <div class="toolbar-left"> + <kendo-dropdownlist [data]="reportGroups" + [defaultItem]="{ description: 'Select AI Purpose....', value: null }" + [textField]="'description'" [valueField]="'value'" [valuePrimitive]="true" + [(ngModel)]="selectedReportGroup" (valueChange)="onReportGroupChange($event)" + class="w-14rem"> + </kendo-dropdownlist> + + <button (click)="openAvailableInfoDialog()" + [disabled]="selectedReportGroup === null || selectedReportGroup === undefined" kendoButton + themeColor="secondary"> + Field Column + </button> + </div> + + <!-- Right Side --> + <div class="toolbar-right"> + <button [disabled]="selectedReportGroup === null || selectedReportGroup === undefined" + kendoButton themeColor="primary" (click)="ShowAllQueryLog()"> + Chat History + </button> + + <button kendoButton themeColor="primary" (click)="onCallOpenAI_One()" + (keydown.enter)="handleEnterKey($event)"> + Execute + </button> + </div> + + </div> + + </div> + <div class="p-col-5"> + <ul style="padding-left: 16px; margin-top: 5px; margin-bottom: 0px;"> + <li> + This AI-based report generation tool transforms your questions or input text into SQL or machine + language to fetch data from the database. Field information is provided to help you understand + which data can be accessed. + </li> + <li class="suggestion-item"> + The system supports various types of visualizations: <strong>Bar chart</strong>, <strong>Pie + chart</strong>, <strong>Line chart</strong>, <strong>Scatter chart</strong> and + <strong>Histogram</strong>. To create a chart, include the chart type in your query — for + example: + <strong>"Generate a bar chart showing employee categories."</strong> + </li> + </ul> + </div> + </div> + </div> + <hr class="my-2" /> + <div class="grid"> + <div class="p-col-12"> + <ng-container *ngIf="gridData?.length > 0; else noData"> + <div class="grid-chart-wrapper" [ngClass]="{'show-chart': isShowChart}"> + <div class="grid-container"> + <div class="export-toolbar"> + <div class="total-label">Total: <span class="total-count">{{ gridData.length }}</span></div> + <div class="export-buttons"> + <button [disabled]="isDisabledShared === true" kendoButton themeColor="primary" + (click)="ShareThisChat()" + style="background: #ff6358 !important;font-weight: bold !important;" + [svgIcon]="null">Share</button> + <button kendoButton (click)="grid.saveAsExcel()">Export Excel</button> + <button kendoButton (click)="grid.saveAsPDF()">Export PDF</button> + </div> + </div> + <kendo-grid #grid [kendoGridBinding]="gridData" [scrollable]="'scrollable'" [height]="450" + [reorderable]="true" class="shadow-sm border custom-grid"> + <ng-template kendoGridToolbarTemplate + *ngIf="deletedColumns.length > 0 && isShowChart === false"> + <div class="deleted-columns-container" *ngIf="deletedColumns.length > 0"> + <span class="toolbar-title">Deleted Columns :</span> + <button *ngFor="let col of deletedColumns; let idx = index" + [svgIcon]="apiService.KendoSVGIcon['plus']" kendoButton + (click)="restoreColumn(idx)" type="button" class="restore-btn"> + {{ col.title || col.field }} + </button> + </div> + </ng-template> + <kendo-grid-column *ngFor="let col of gridColumns; let i = index" [field]="col.field" + [title]="col.title" [width]="150" [orderIndex]="i"> + <ng-template kendoGridHeaderTemplate> + <div class="header-container" [attr.data-col-index]="i"> + <span *ngIf="!gridColumns[i].editing" class="header-title">{{ + gridColumns[i].title || gridColumns[i].field }}</span> + <input *ngIf="gridColumns[i].editing" [(ngModel)]="gridColumns[i].title" + [ngModelOptions]="{ standalone: true }" (blur)="onHeaderBlur(i)" type="text" + class="header-input" /> + <button *ngIf="!gridColumns[i].editing" type="button" class="edit-btn" + (click)="startEdit(i); $event.stopPropagation()" + title="Edit column title">✎</button> + <button *ngIf="!gridColumns[i].editing && isShowChart === false" type="button" + class="delete-btn" (click)="deleteColumn(i); $event.stopPropagation()" + title="Delete column">✖</button> + <button *ngIf="gridColumns[i].editing" type="button" class="save-btn" + (click)="saveHeader(i); $event.stopPropagation()" title="Save">✔</button> + </div> + </ng-template> + <ng-template kendoGridCellTemplate let-dataItem> + <div class="cell-wrap">{{ dataItem[col.field] }}</div> + </ng-template> + </kendo-grid-column> + <kendo-grid-excel [fileName]="excelFileName"></kendo-grid-excel> + <kendo-grid-pdf [fileName]="pdfFileName" paperSize="A4"></kendo-grid-pdf> + </kendo-grid> + </div> + <div class="chart-container" *ngIf="isShowChart"> + <div class="export-toolbar justify-end"> + <div class="export-buttons"> + <button kendoButton (click)="exportSizedChart(chart)">Export PDF</button> + <button kendoButton (click)="exportChartImage(chart)"> Export PNG</button> + </div> + </div> + <div style="width: 100%;overflow: auto;"> + <kendo-chart #chart [transitions]="chartTransitions" + style="height: 450px !important; width: 100% !important;" + *ngIf="openAIResponseWithData.IsChart && openAIResponseWithData.ChartType !== 'scatter' && openAIResponseWithData.ChartType !== 'histogram'" + class="h-full w-full"> + <kendo-chart-title [text]="openAIResponseWithData.Title" + align="center"></kendo-chart-title> + <kendo-chart-legend + [position]="openAIResponseWithData.ChartType === 'pie' ? gridData.length > 25 ? 'bottom' : 'right' : 'bottom'"></kendo-chart-legend> + <kendo-chart-category-axis> + <kendo-chart-category-axis-item [categories]="chartCategories"> + </kendo-chart-category-axis-item> + </kendo-chart-category-axis> + <kendo-chart-value-axis> + <kendo-chart-value-axis-item> + <kendo-chart-value-axis-item-labels + [format]="'{0}'"></kendo-chart-value-axis-item-labels> + </kendo-chart-value-axis-item> + </kendo-chart-value-axis> + <kendo-chart-series> + <kendo-chart-series-item [type]="openAIResponseWithData.ChartType" + [data]="chartData" field="value" categoryField="category" + [labels]="{ visible: true, position: openAIResponseWithData.LabelPosition }" + [line]="{ style: 'smooth' }" [opacity]="0.7"> + </kendo-chart-series-item> + </kendo-chart-series> + </kendo-chart> + <kendo-chart #chart [transitions]="chartTransitions" + style="height: 450px !important; width: 100% !important;" + *ngIf="openAIResponseWithData.IsChart && openAIResponseWithData.ChartType === 'scatter'" + class="h-full w-full"> + <kendo-chart-title [text]="openAIResponseWithData.Title"></kendo-chart-title> + <kendo-chart-legend position="bottom"></kendo-chart-legend> + <kendo-chart-x-axis> + <kendo-chart-x-axis-item title="X Axis"></kendo-chart-x-axis-item> + </kendo-chart-x-axis> + <kendo-chart-y-axis> + <kendo-chart-y-axis-item title="Y Axis"> + <kendo-chart-y-axis-item-labels + [format]="'{0}'"></kendo-chart-y-axis-item-labels> + </kendo-chart-y-axis-item> + </kendo-chart-y-axis> + <kendo-chart-series> + <kendo-chart-series-item type="scatter" [data]="chartData" xField="x" yField="y" + [labels]="{ + visible: true, + content: scatterLabelContent + }"> + </kendo-chart-series-item> + </kendo-chart-series> + </kendo-chart> + <kendo-chart #chart [transitions]="chartTransitions" + style="height: 450px !important; width: 100% !important;" + *ngIf="openAIResponseWithData.IsChart && openAIResponseWithData.ChartType === 'histogram'" + class="h-full w-full"> + <kendo-chart-title text="Histogram Example"></kendo-chart-title> + <kendo-chart-legend position="bottom"></kendo-chart-legend> + <kendo-chart-category-axis> + <kendo-chart-category-axis-item + [title]="{ text: 'Value' }"></kendo-chart-category-axis-item> + </kendo-chart-category-axis> + <kendo-chart-value-axis> + <kendo-chart-value-axis-item> + <kendo-chart-value-axis-item-labels + format="{0}"></kendo-chart-value-axis-item-labels> + </kendo-chart-value-axis-item> + </kendo-chart-value-axis> + <kendo-chart-series> + <kendo-chart-series-item *ngFor="let item of histogramSeries" [type]="item.type" + [data]="item.data" [labels]="{ visible: true }"> + </kendo-chart-series-item> + </kendo-chart-series> + </kendo-chart> + </div> + </div> + </div> + </ng-container> + <ng-template #noData> + <div class="ai-error-message" *ngIf="invalidQueryMessage" [@fadeInOut]="messageState">{{ + invalidQueryMessage }}</div> + <div class="ai-guidance-card" *ngIf="isUserPromptIsNotRelated" [@fadeInOut]="messageState"> + <h4 class="ai-guidance-title">⚠️ Let’s stay focused on this section</h4> + <p class="ai-guidance-text"> + Your query isn’t related to the selected purpose. Please ask only about this section using the + listed fields as a guide. + 💡 You can also click the <strong>Chat History</strong> button to review your previous queries. + </p> + <div *ngFor="let section of tableColumnDefinition" class="ai-section"> + <p class="ai-section-title">{{ section.tableName }}</p> + <div class="ai-field-badges"> + <span *ngFor="let field of section.fields" class="badge">{{ field }}</span> + </div> + </div> + <div class="ai-tip">💡 Use the above fields to guide your AI query. The more specific you are, the + better the result will be.</div> + </div> + </ng-template> + </div> + </div> +</div> + + + + +<kendo-dialog *ngIf="isShowAvailableInfo" (close)="isShowAvailableInfo = false" title="Available Information" + [maxHeight]="600" [width]="500"> + <div *ngFor="let section of tableColumnDefinition"> + <p style="margin-bottom: 0px !important;"><strong>{{ section.tableName }}</strong></p> + <ul style="margin-top: 0px !important;"> + <li>{{ section.fields.join(', ') }}</li> + </ul> + </div> + + <p class="centered-info"> + Use the above fields to guide your AI query. The more specific you are, the better the result will be. + </p> + <kendo-dialog-actions> + <button kendoButton themeColor="primary" (click)="isShowAvailableInfo = false">Close</button> + </kendo-dialog-actions> +</kendo-dialog> +<kendo-dialog *ngIf="isShowCurrentUserAllQueryLog" (close)="isShowCurrentUserAllQueryLog = false" title="Chat History" + [maxHeight]="700" [height]="700" [width]="500"> + <div class="history-wrapper"> + <div class="search-box"> + <textarea kendoTextArea [(ngModel)]="querySearchText" rows="2" placeholder="Search your chat history..." + style="width:100%; border-radius:8px; padding:8px; font-size:14px;border-color: #52527A;"> + </textarea> + </div> + <div class="query-card-container scrollable"> + <div class="query-card" *ngFor="let item of filteredQueryLog()" [ngClass]="{ 'chart-query': item.isChart }" + (mouseenter)="item.hover = true" (mouseleave)="item.hover = false" + (click)="OpenAIPreviousQueryExecution(item.id)"> + <div class="query-card-content"> + <div class="query-text">{{ item.userPrompt }}</div> + <div *ngIf="item.isChart" class="chart-icon">📊</div> + <i *ngIf="!item.isCommon" class="pi pi-share-alt" + (click)="ShareThisChatWithOthers(item); $event.stopPropagation()" style="margin-left: 5px;"></i> + </div> + </div> + </div> + </div> +</kendo-dialog> +<kendo-dialog *ngIf="isShowChartOption" (close)="isShowChartOption = false" title="Charts" [maxHeight]="500" + [width]="500"> + <div class="p-grid"> + <div class="p-col-3"><label for="">Graph Name</label></div> + <div class="p-col-9"> + <kendo-dropdownlist [data]="chartGroups" [defaultItem]="{ description: 'Select a Chart....', value: null }" + [textField]="'description'" [valueField]="'value'" [valuePrimitive]="true" + [(ngModel)]="selectedChartGroup" (valueChange)="onChartGroupChange($event)" style="width: 100%;"> + </kendo-dropdownlist> + </div> + + <div class="p-col-3"><label for="">Column X</label></div> + <div class="p-col-9"> + <kendo-dropdownlist [data]="gridColumns" [defaultItem]="{ description: 'X...', value: null }" + [textField]="'description'" [valueField]="'value'" [valuePrimitive]="true" [(ngModel)]="selectedColumnX" + (valueChange)="onChartGroupChange($event)" style="width: 100%;"> + </kendo-dropdownlist> + </div> + + <div class="p-col-3"><label for="">Column Y</label></div> + <div class="p-col-9"> + <kendo-dropdownlist [data]="gridColumns" [defaultItem]="{ description: 'Y...', value: null }" + [textField]="'description'" [valueField]="'value'" [valuePrimitive]="true" [(ngModel)]="selectedColumnY" + (valueChange)="onChartGroupChange($event)" style="width: 100%;"> + </kendo-dropdownlist> + </div> + + <div class="p-col-3"><label for="">Merge</label></div> + <div class="p-col-9"> + <input [(ngModel)]="chartMerge" type="text" style="width:100%" pInputText readonly> + </div> + + <div class="p-col-3"><label for="">Name</label></div> + <div class="p-col-9"> + <input [(ngModel)]="chartName" type="text" style="width:100%" pInputText readonly> + </div> + + <div class="p-col-12" align="right"> + <button kendoButton themeColor="primary" [svgIcon]="apiService.KendoSVGIcon['eye']">Plot</button> + </div> + </div> +</kendo-dialog> diff --git a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.scss b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.scss new file mode 100644 index 0000000..68c19c0 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.scss @@ -0,0 +1,458 @@ +.textarea-wrapper { + position: relative; + width: 100%; +} + +textarea.formAITextArea { + font-size: 1rem; + padding: 0.75rem 2.5rem 0.75rem 0.75rem; + border-radius: 0.5rem; + resize: none; + width: 100%; + height: 78%; + box-sizing: border-box; +} + +.clear-btn { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + background: transparent; + border: none; + cursor: pointer; + font-size: 1.2rem; + color: #888; + z-index: 10; + padding: 0; + line-height: 1; +} +.clear-btn:hover { + color: #333; +} +.p-button-rounded { + width: 2.5rem; + height: 2.5rem; +} +.centered-info { + text-align: center; + margin-top: 12px; + color: red; + font-weight: bold; +} +.userPrompt{ + cursor: pointer; +} +.userPrompt :hover{ + color: #414278; + font-weight: bold; +} +.query-card-container { + display: flex; + flex-direction: column; + gap: 10px; +} +.query-card { + background: white; + border-radius: 10px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); + padding: 4px 16px; + transition: transform 0.2s ease, box-shadow 0.2s ease; + cursor: pointer; + position: relative; + border: 1px solid #cfcfcf; +} +.query-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 10px rgba(0,0,0,0.15); +} +.query-card-content { + display: flex; + justify-content: space-between; + align-items: center; +} +.query-text { + flex: 1; + font-size: 14px; + line-height: 1.4; + max-height: calc(1.4em * 2); + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +} +.query-arrow { + font-size: 18px; + color: #52527A; + padding-left: 10px; + user-select: none; +} +.query-arrow:hover { + color: #33334e; +} +.query-date { + font-size: 12px; + color: #777; + text-align: right; + margin-top: 4px; +} +.chart-icon { + margin-left: 8px; + color: #52527A; + font-size: 16px; +} +.chart-query { + background: #e6f7ff; +} +.search-box textarea { + resize: none; + box-shadow: 0 2px 5px rgba(0,0,0,0.1); + border: 1px solid #cfcfcf; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} +.search-box textarea:focus { + border-color: #52527A; + box-shadow: 0 0 5px rgba(82,82,122,0.5); + outline: none; +} +.history-wrapper { + display: flex; + flex-direction: column; + height: 100%; +} +.search-box { + flex: 0 0 auto; + margin-bottom: 10px; +} +.scrollable { + flex: 1 1 auto; + overflow-y: auto; + max-height: 100%; +} +.scrollable::-webkit-scrollbar { + width: 6px; +} +.scrollable::-webkit-scrollbar-track { + background: #f5f5f5; + border-radius: 10px; +} +.scrollable::-webkit-scrollbar-thumb { + background-color: #d6d6d6; + border-radius: 10px; + border: 2px solid #f5f5f5; +} +.scrollable::-webkit-scrollbar-thumb:hover { + background-color: #bfbfbf; +} +.suggestions-dropdown { + position: absolute; + background: rgba(255, 255, 255, 0.85); /* semi-transparent */ + backdrop-filter: blur(4px); /* blur effect */ + border: 1px solid #cfcfcf; + border-radius: 6px; + box-shadow: 0 2px 6px rgba(0,0,0,0.15); + max-height: 350px; + overflow-y: auto; + width: 500px; + z-index: 1000; + padding: 0; + margin: 0; + list-style: none; +} +.suggestions-dropdown li { + padding: 6px 12px; + cursor: pointer; + border-bottom: 1px solid #d1d1d1; +} +.suggestions-dropdown li:hover { + background: #f0f0f0; + color: #333; +} +.suggestions-dropdown .chart-icon { + margin-left: 6px; +} +.textarea-wrapper { + position: relative; + width: 100%; +} +.formAITextArea { + width: 100%; + padding: 0.75rem 2rem 0.75rem 0.75rem; + border-radius: 0.5rem; + resize: none; + font-size: 1rem; +} +suggestions-dropdown li.suggestion-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 12px; + cursor: pointer; +} +.suggestions-dropdown li.suggestion-item:hover { + background: #f0f0f0; +} +.suggestions-dropdown .chart-icon { + margin-left: 8px; + float: right; + font-size: 16px; + color: #52527A; +} +.grid-chart-wrapper { + display: flex; + gap: 10px; +} +.grid-chart-wrapper.show-chart .grid-container { + width: 50%; +} +.grid-chart-wrapper.show-chart .chart-container { + width: 50%; +} +.grid-chart-wrapper:not(.show-chart) .grid-container { + width: 100%; +} +.chart-container { + height: 520px; +} +.export-buttons { + display: flex; + justify-content: flex-end; + gap: 5px; + padding: 3px; +} +.ai-error-message { + text-align: center; + background: #fee2e2; + color: #991b1b; + border: 1px solid #fecaca; + border-radius: 10px; + padding: 10px 15px; + margin: 10px auto; + max-width: 80%; + font-weight: 500; + box-shadow: 0 2px 6px rgba(0,0,0,0.05); +} +.ai-guidance-card { + background: #f9fafb; + border: 1px solid #e5e7eb; + border-radius: 12px; + padding: 20px; + margin: auto; + max-width: 80%; + box-shadow: 0 4px 12px rgba(0,0,0,0.06); +} +.ai-guidance-title { + font-size: 1.2rem; + font-weight: 600; + color: #111827; + margin-bottom: 10px; +} +.ai-guidance-text { + color: #374151; + font-size: 0.95rem; + margin-bottom: 15px; +} +.ai-section { + margin-bottom: 12px; +} +.ai-section-title { + font-weight: 600; + color: #1e40af; + margin-bottom: 4px; +} +.ai-field-list { + margin: 0; + padding-left: 15px; + color: #1e3a8a; + font-size: 0.9rem; +} +.ai-field-list li { + margin: 3px 0; +} +.ai-tip { + margin-top: 15px; + background: #e0f2fe; + color: #0369a1; + padding: 8px 12px; + border-radius: 8px; + font-size: 0.9rem; + font-style: italic; +} +.ai-field-badges { + display: flex; + flex-wrap: wrap; + gap: 6px; +} +.badge { + background: #f1f3f5; + padding: 4px 10px; + border-radius: 12px; + font-size: 13px; + color: #495057; +} +.export-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + border: 1px solid #dee2e6; + border-radius: 5px; + margin-bottom: 5px; +} +.total-label { + font-weight: bold; + color: red; + font-size: 14px; + margin-left: 5px; +} +.total-count { + color: #000; + margin-left: 4px; + +} +.export-toolbar.justify-end { + justify-content: flex-end; +} +.export-buttons .k-button-md { + padding-block: 2px !important; + padding-inline: 2px !important; +} +.custom-grid .cell-wrap { + min-width: 150px; + white-space: normal; + word-wrap: break-word; + overflow-wrap: anywhere; +} +.custom-grid .k-grid-content { + overflow-x: auto !important; +} +.header-container { + display: flex; + align-items: center; + gap: 2px; + width: 100%; +} +.header-title { + flex: 1 1 auto; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding: 3px 3px; +} +.header-input { + flex: 1 1 auto; + min-width: 0; + padding: 3px 3px; + background: #ffffff; + border: 1px solid #b5b5b5; + border-radius: 5px; +} +.header-input:focus { + outline: 1px solid #b6d6f7; + background-color: #ffffff; +} +.edit-btn, .save-btn { + flex: 0 0 20px; + width: 20px; + height: 20px; + display: inline-flex; + align-items: center; + justify-content: center; + border: none; + background: transparent; + cursor: pointer; + font-size: 14px; + border-radius: 4px; +} +.save-btn { + font-weight: 600; +} +::ng-deep .grid-chart-wrapper .k-grid .k-cell-inner>.k-link { + padding: 4px 4px !important; +} +::ng-deep .k-toolbar { + padding: 4px 7px 1px 7px !important; +} +.delete-btn { + color: red; + flex: 0 0 20px; + width: 20px; + height: 20px; + display: inline-flex; + align-items: center; + justify-content: center; + border: none; + background: transparent; + cursor: pointer; + font-size: 14px; + border-radius: 4px; +} +.edit-btn:hover, .save-btn:hover, .delete-btn:hover { + background: rgba(0,0,0,0.04); +} +.toolbar-title { + font-weight: bold; + margin-right: 8px; +} +.deleted-columns-container { + display: flex; + align-items: center; + gap: 0px; + max-width: 100%; + overflow-x: auto; + white-space: nowrap; + padding: 0px 0px 3px 0px; +} +.restore-btn { + flex: 0 0 auto; + margin-left: 4px; + padding: 2px 6px; + border: 1px solid #6263a0; + border-radius: 4px; + background: #e3f2fd; + cursor: pointer; + color: #495057; +} +.deleted-columns-wrapper { + display: flex; + align-items: center; + gap: 6px; +} + +.toolbar-container { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + margin-top: 8px; +} + +.toolbar-left, +.toolbar-right { + display: flex; + align-items: center; + gap: 10px; +} + +.toolbar-left { + justify-content: flex-start; +} + +.toolbar-right { + justify-content: flex-end; +} + + +@media (max-width: 768px) { + .toolbar-container { + flex-direction: column; + align-items: stretch; + gap: 10px; + } + + .toolbar-left, + .toolbar-right { + justify-content: space-between; + } +} diff --git a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts new file mode 100644 index 0000000..eea5cb9 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts @@ -0,0 +1,572 @@ + +import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { ApiService } from 'src/app/app.api.service'; +import { DataTransferService } from 'src/app/data.transfer.service'; +import { Router, ActivatedRoute } from '@angular/router'; +import { SearchEmployee } from 'src/app/_models/Employee/searchEmployee'; +import { HRMNotificationService } from 'src/app/app.notification.service'; +import { loadingPanelService } from 'src/app/hrm-loding panel/loding.panel.service'; +import { Courses } from 'src/app/_models/LearningManagement/courses'; +import { Lessons } from 'src/app/_models/LearningManagement/lessons'; +import { LessonContent } from 'src/app/_models/LearningManagement/lesson-content'; +import { LMSService } from 'src/app/_services/LearningManagement/lms.service'; +import { EnumAccessLevel, EnumAIChartType, EnumContentType, EnumDifficultyLevel, EnumFileType, EnumReportGroupType, EnumStatus, EnumTemplateType } from 'src/app/_models/enums'; +import { CourseType } from 'src/app/_models/LearningManagement/course-type'; +import { LMSTemplate } from 'src/app/_models/LearningManagement/lmstemplate'; +import { HRMOpenAIService } from 'src/app/_services/HRMOpenAI/hrmopen-ai.service'; +import { OpenAIQueryLog } from 'src/app/_models/OpenAIQueryLog/OpenAIQueryLog'; +import { OpenAIResponseWithData } from 'src/app/_models/OpenAI BO/OpenAIResponseWithData'; +import { Pipe, PipeTransform } from '@angular/core'; +import { HostListener } from '@angular/core'; +import { saveAs } from "@progress/kendo-file-saver"; +import { ChartComponent } from "@progress/kendo-angular-charts"; +import { exportPDF, fit, geometry, Group } from "@progress/kendo-drawing"; +function mm(val: number): number {return val * 2.8347;} +const PAGE_RECT = new geometry.Rect([0, 0], [mm(210 - 20), mm(297 - 20)]); +import { trigger, state, style, animate, transition } from '@angular/animations'; +@Component({ + selector: 'app-hrmopen-aiteast', + templateUrl: './hrmopen-aiteast.component.html', + styleUrls: ['./hrmopen-aiteast.component.scss'], + //standalone: false, + animations: [ + trigger('fadeInOut', [ + transition('* => changed', [ + style({ opacity: 0 }), + animate('500ms ease-in', style({ opacity: 1 })) + ]) + ]) + ] +}) +export class HRMOpenAITestComponent implements OnInit{ + + constructor(public apiService : ApiService, + public notificationService: HRMNotificationService, + public loadingpanelService: loadingPanelService, public opeAiService : HRMOpenAIService) { + this.apiService.selectedMenuName = "HRM AI"; + } + + inputUserCommand : string = ""; + public gridData: any[] = []; // data for the grid + public gridColumns: any[] = []; + public gridColumnsForChart: any[] = []; // columns for dynamic grid + public output: any = ""; + public reportGroups : any[] = []; + public chartGroups : any[] = []; + public reportColumnDefinition: any[] = []; + public tableColumnDefinition: ReportColumnDefinition[] = []; + public selectedReportGroup: EnumReportGroupType | null; + public selectedChartGroup: EnumReportGroupType | null; + public isShowAvailableInfo: boolean = false; + public isShowCurrentUserAllQueryLog: boolean = false; + public invalidQueryMessage : string = "The AI can provide information when you ask about HR."; + public currentUserAllQueryLog : OpenAIQueryLog[] = []; + public filteredSuggestions: OpenAIQueryLog[] = []; + public OpenAIResponce: any = {}; + public openAIResponseWithData: OpenAIResponseWithData = new OpenAIResponseWithData(); + public chartData: any[] = []; + public isShowChart: boolean = false; + public isShowChartOption: boolean = false; + public chartMerge : string = ""; + public chartName : string = ""; + public selectedColumnX : any; + public selectedColumnY : any; + public searchText: string = ""; + public querySearchText: string = ""; + public excelFileName: string = "OpenAIQueryLog.xlsx"; + public pdfFileName: string = "OpenAIQueryLog.pdf"; + public pdfFileNameForChart: string = "OpenAIQueryLog.pdf"; + public pngFilename: string = "OpenAIQueryLog.png"; + public chartTransitions = false; + public isUserPromptIsNotRelated = false; + public messageState: string = ''; + public isDisabledShared : boolean = true; + public currentChat : OpenAIQueryLog | undefined; + public deletedColumns: any[] = []; + + @ViewChild('deletedContainer', { static: false }) deletedContainer!: ElementRef; + @ViewChild("chart") + private chart: ChartComponent; + private setInvalidQueryMessage(msg: string) { + this.invalidQueryMessage = msg; + this.messageState = "changed"; + setTimeout(() => (this.messageState = ""), 500); + } + ngOnInit(){ + this.deletedColumns = []; + this.isUserPromptIsNotRelated = false; + this.currentChat = undefined; + this.invalidQueryMessage = "The AI can provide information when you ask about HR." + this.reportGroups = [ + {description : "Employee", value : EnumReportGroupType.Employee}, + {description : "Leave", value : EnumReportGroupType.Leave}, + {description : "Attendance", value : EnumReportGroupType.Attendance}, + {description : "Salary", value : EnumReportGroupType.Salary}, + {description : "Performance", value : EnumReportGroupType.Performance}, + {description : "Training", value : EnumReportGroupType.Training}, + ]; + this.chartGroups = [ + {description : "Bar Chart", value : EnumAIChartType.BarChart}, + {description : "Line Graph", value : EnumAIChartType.LineGraph}, + {description : "Pie Chart", value : EnumAIChartType.PieChart}, + {description : "Scatter Plot", value : EnumAIChartType.ScatterPlot}, + {description : "Area Chart", value : EnumAIChartType.AreaChart}, + {description : "Histogram", value : EnumAIChartType.Histogram}, + {description : "Waterfall Chart", value : EnumAIChartType.WaterfallChart}, + {description : "Gant Chart", value : EnumAIChartType.GantChart}, + {description : "Radar Chart", value : EnumAIChartType.RadarChart}, + {description : "Activity Diagram", value : EnumAIChartType.ActivityDiagram}, + ]; + this.selectedReportGroup = EnumReportGroupType.Employee + this.onReportGroupChange(this.selectedReportGroup); + + } + LoadCurrentUserAllQueryLog(reportGroupType : EnumReportGroupType){ + this.loadingpanelService.ShowLoadingPanel = false; + this.opeAiService.GetCurrentUserAllQueryLog(reportGroupType).subscribe( + (resp: any) => { + this.currentUserAllQueryLog = resp; + }, + (err: any) => { + this.loadingpanelService.ShowLoadingPanel = false; + this.notificationService.showError(err.error, "Error"); + }, + () => { + this.loadingpanelService.ShowLoadingPanel = false; + this.currentChat = undefined; + if(this.currentUserAllQueryLog && this.currentUserAllQueryLog.length > 0){ + if(this.inputUserCommand !== ""){ + let queryLog : OpenAIQueryLog = this.currentUserAllQueryLog.find(x => x.userPrompt.toLowerCase() === this.inputUserCommand.toLowerCase()) ?? undefined; + if(queryLog != undefined){ + if(queryLog.isCommon === false){ + this.currentChat = queryLog; + this.isDisabledShared = false; + } + else{ + this.isDisabledShared = true; + } + } + else{ + this.isDisabledShared = true; + } + } + else{ + this.isDisabledShared = true; + } + } + }); + } + ShowAllQueryLog(){ + this.querySearchText = ""; + this.isShowCurrentUserAllQueryLog = true; + this.LoadCurrentUserAllQueryLog(this.selectedReportGroup); + } + onInputChange() { + const text = this.inputUserCommand?.toLowerCase().trim() || ''; + if (!text) {this.filteredSuggestions = [];return;} + this.filteredSuggestions = this.currentUserAllQueryLog + .filter(item => item.userPrompt.toLowerCase().startsWith(text)) + .slice(0, 10); + } + closeSuggestions() { + this.filteredSuggestions = []; + } + clearInput() { + this.inputUserCommand = ''; + this.filteredSuggestions = []; + this.gridData = this.gridColumns = []; + this.OpenAIResponce = {}; + this.currentChat = undefined; + this.isDisabledShared = true; + } + @HostListener('document:click', ['$event']) + handleClickOutside(event: MouseEvent) { + const target = event.target as HTMLElement; + if (!target.closest('.suggestion-wrapper')) { + this.closeSuggestions(); + } + } + selectSuggestion(item: OpenAIQueryLog) { + this.isUserPromptIsNotRelated = false; + this.invalidQueryMessage = "No data returned from AI." + this.inputUserCommand = item.userPrompt; + this.filteredSuggestions = []; + this.OpenAIPreviousQueryExecution(item.id); // or any other action + } + handleEnterKey(event: KeyboardEvent): void { + event.preventDefault(); + if (this.inputUserCommand?.trim()) { + this.onCallOpenAI_One(); + } + } + onCallOpenAI_One(){ + this.deletedColumns = []; + this.isUserPromptIsNotRelated = false; + this.currentChat = undefined; + this.invalidQueryMessage = "No data returned from AI." + if(this.selectedReportGroup && this.inputUserCommand && this.inputUserCommand !== "" && + (this.selectedReportGroup === EnumReportGroupType.Employee || this.selectedReportGroup === EnumReportGroupType.Salary + || this.selectedReportGroup === EnumReportGroupType.Performance || this.selectedReportGroup === EnumReportGroupType.Training + || this.selectedReportGroup === EnumReportGroupType.Leave || this.selectedReportGroup === EnumReportGroupType.Attendance)){ + const data = {userPrompt: this.inputUserCommand,reportGroup: this.selectedReportGroup} + this.gridData = this.gridColumns = []; + this.OpenAIResponce = {}; + this.isShowChart = false; + if(this.CheckUserPromptFromPreviousQueryLog(this.inputUserCommand)){ + let OpenAIQueryLog: OpenAIQueryLog = this.currentUserAllQueryLog.find(x => x.userPrompt.toLowerCase() === this.inputUserCommand.toLowerCase()) ?? undefined; + if(OpenAIQueryLog != undefined){ + this.OpenAIPreviousQueryExecution(OpenAIQueryLog.id); + } + return; + } + this.isUserPromptIsNotRelated = false; + this.loadingpanelService.ShowLoadingPanel = false; + this.opeAiService.AskOpenAI(data).subscribe( + (resp: any) => { + this.OpenAIResponce = JSON.parse(resp); + }, + (err: any) => { + this.loadingpanelService.ShowLoadingPanel = false; + this.isUserPromptIsNotRelated = false; + if (err.error && (err.error as string).includes("Sorry, I can only help with retrieving data. I am not allowed to update or delete any records.")) { + this.setInvalidQueryMessage("Sorry, I can only help with retrieving data. I am not allowed to update or delete any records."); + } + else if (err.error && (err.error as string).includes("I can only help with questions related to")) { + this.showOutOfScopeNotice(); + } + else{ + this.notificationService.showError(err.error, "Error"); + } + }, + () => { + this.loadingpanelService.ShowLoadingPanel = false; + this.ProcessOpenAIData(); + this.LoadCurrentUserAllQueryLog(this.selectedReportGroup); + }); + } + } + CheckUserPromptFromPreviousQueryLog(userPrompt: string): boolean { + return this.currentUserAllQueryLog.some(item => item.userPrompt.toLowerCase() === userPrompt.toLowerCase()); + } + get chartCategories(): string[] { + if (!this.chartData) return []; + return this.chartData.map(d => d.category); + } + public scatterLabelContent(e: any): string { + return `(${e.dataItem.x}, ${e.dataItem.y})`; + } + ProcessOpenAIData() { + this.deletedColumns = []; + if (!this.OpenAIResponce) { + this.invalidQueryMessage = "No data returned from AI."; + return; + } + this.openAIResponseWithData = this.OpenAIResponce as OpenAIResponseWithData; + if (!this.openAIResponseWithData) { return; } + // --- CHART handling (kept mostly same as your original logic) --- + if (this.openAIResponseWithData.IsChart && this.openAIResponseWithData.ChartType && this.openAIResponseWithData.ChartType !== "") { + if (this.openAIResponseWithData.ChartType === 'line' || this.openAIResponseWithData.ChartType === 'area') { + this.openAIResponseWithData.LabelPosition = 'above'; + } else { + this.openAIResponseWithData.LabelPosition = 'outsideEnd'; + } + this.isShowChart = true; + this.chartData = []; + if (this.openAIResponseWithData.ChartType === 'scatter') { + this.chartData = this.openAIResponseWithData.Labels.map((label, index) => ({ + x: label, + y: this.openAIResponseWithData.Values[index] + })); + } else { + this.chartData = this.openAIResponseWithData.Labels.map((label, index) => ({ + category: label, + value: this.openAIResponseWithData.Values[index] + })); + } + } + // --- TABLE parsing and normalization --- + try { + const parsed = JSON.parse(this.openAIResponseWithData.TableData); + let allRows: any[] = []; + if (Array.isArray(parsed)) { + // If parsed is an array of tables with Rows properties + allRows = parsed.flatMap(t => t.Rows ?? []); + } else if (parsed && Array.isArray(parsed.Rows)) { + allRows = parsed.Rows; + } else { + // fallback: if parsed itself is an array of row objects + allRows = Array.isArray(parsed) ? parsed : []; + } + if (!allRows || allRows.length === 0) { + this.gridData = []; + this.gridColumns = []; + this.gridColumnsForChart = []; + return; + } + // Use keys from first row as canonical columns + const keys = Object.keys(allRows[0]); + // Build gridColumns with safe field names and non-empty titles + this.gridColumns = keys.map(k => { + const safeKey = k.replace(/\s+/g, '_'); + return { + field: safeKey, + title: (k && k.toString().trim()) ? k.toString() : safeKey, + editing: false + }; + }); + // Normalize each row: convert keys to safeKey, replace null/object with readable value + this.gridData = allRows.map(row => { + const normalized: any = {}; + for (const k of keys) { + const safeKey = k.replace(/\s+/g, '_'); + let val = row[k]; + + if (val === null || val === undefined || typeof val === 'object') { + normalized[safeKey] = '-'; + } else { + normalized[safeKey] = val; + } + } + return normalized; + }); + // Columns for chart control: + if (this.gridColumns && this.gridColumns.length > 0) { + this.gridColumnsForChart = this.gridColumns.map(col => ({ description: col.title, value: col.field })); + } else { + this.gridColumnsForChart = []; + } + } catch (e) { + console.error('Parse error in ProcessOpenAIData', e); + this.notificationService.showError("Failed to parse OpenAI response.", "Parse Error"); + } + } + saveHeader(index: number) { + if (!this.gridColumns || !this.gridColumns[index]) { return; } + const col = this.gridColumns[index]; + if (!col.title || col.title.toString().trim() === '') { + col.title = col.field; + } + col.editing = false; + delete col._oldTitle; + } + cancelHeader(index: number) { + if (!this.gridColumns || !this.gridColumns[index]) { return; } + const col = this.gridColumns[index]; + if (col._oldTitle !== undefined) { + col.title = col._oldTitle; + } + col.editing = false; + delete col._oldTitle; + } + deleteColumn(index: number) { + const removed = this.gridColumns.splice(index, 1)[0]; + this.deletedColumns.push({ ...removed, index }); + } + scrollDeleted(direction: number) { + if (this.deletedContainer && this.deletedContainer.nativeElement) { + const el = this.deletedContainer.nativeElement; + const scrollAmount = 150; + el.scrollBy({ left: direction * scrollAmount, behavior: 'smooth' }); + } + } + startEdit(index: number) { + if (!this.gridColumns || !this.gridColumns[index]) { return; } + const col = this.gridColumns[index]; + + col._oldTitle = col.title; + col.editing = true; + + setTimeout(() => { + const selector = `.header-container[data-col-index="${index}"] .header-input`; + const el = document.querySelector(selector) as HTMLInputElement | null; + if (el) { + el.focus(); + } + }, 0); + } + restoreColumn(idx: number) { + const col = this.deletedColumns.splice(idx, 1)[0]; + this.gridColumns.splice(col.index, 0, col); + } + onHeaderBlur(index: number) { + this.saveHeader(index); + } + openAvailableInfoDialog(){ + this.isShowAvailableInfo = true; + } + onReportGroupChange(group : any){ + this.isUserPromptIsNotRelated = false; + this.invalidQueryMessage = "The AI can provide information when you ask about HR." + if(this.selectedReportGroup && + (this.selectedReportGroup === EnumReportGroupType.Employee + || this.selectedReportGroup === EnumReportGroupType.Salary + || this.selectedReportGroup === EnumReportGroupType.Performance + || this.selectedReportGroup === EnumReportGroupType.Training + || this.selectedReportGroup === EnumReportGroupType.Attendance + || this.selectedReportGroup === EnumReportGroupType.Leave)){ + this.loadingpanelService.ShowLoadingPanel = false; + this.reportColumnDefinition = []; + this.tableColumnDefinition = []; + this.opeAiService.GetReportColumnDefinition(this.selectedReportGroup).subscribe( + (resp: any) => { + this.reportColumnDefinition = resp as any[]; + }, + (err: any) => { + this.loadingpanelService.ShowLoadingPanel = false; + this.notificationService.showError(err.error, "Error"); + }, + () => { + this.loadingpanelService.ShowLoadingPanel = false; + this.gridData = []; + if (this.reportColumnDefinition && this.reportColumnDefinition.length > 0) { + const excludedNormalizedCaptions = new Set(['employeeid']); + const excludedNormalizedCaptionsSalaryMonthlyID = new Set(['salarymonthlyid']); + const groupedMap = new Map<string, ReportColumnDefinition>(); + this.reportColumnDefinition.forEach(item => { + const normalized = this.normalizeCaption(item.caption); + if (!excludedNormalizedCaptions.has(normalized) && !excludedNormalizedCaptionsSalaryMonthlyID.has(normalized)) { + const rawTableName = item.tablename || ''; + const cleanedTableName = rawTableName.startsWith('vw') ? rawTableName.substring(2) : rawTableName; + if (!groupedMap.has(cleanedTableName)) { + groupedMap.set(cleanedTableName, { + tableName: cleanedTableName, + fields: [] + }); + } + groupedMap.get(cleanedTableName)!.fields.push(item.caption); + } + }); + this.tableColumnDefinition = Array.from(groupedMap.values()); + } + this.LoadCurrentUserAllQueryLog(this.selectedReportGroup); + }); + } + } + normalizeCaption(caption: string): string { + return caption.replace(/\s+/g, '').toLowerCase().trim(); + } + OpenAIPreviousQueryExecution(id : number){ + this.isUserPromptIsNotRelated = false; + this.invalidQueryMessage = "No data returned from AI." + if(id && id > 0){ + let oQueryLog : OpenAIQueryLog = this.currentUserAllQueryLog.find(x => x.id === id) ?? undefined; + if(oQueryLog != undefined){ + this.isShowCurrentUserAllQueryLog = false + this.loadingpanelService.ShowLoadingPanel = false; + this.inputUserCommand = oQueryLog.userPrompt; + this.selectedReportGroup = oQueryLog.reportGroup; + if(oQueryLog.title && oQueryLog.title !== ""){ + this.pdfFileName = `${oQueryLog.title}.pdf`; + this.excelFileName = `${oQueryLog.title}.xlsx`; + this.pngFilename = `${oQueryLog.title}_Chart.png`; + this.pdfFileNameForChart = `${oQueryLog.title}_Chart.pdf`; + } + else{ + this.pdfFileName = 'FileData.pdf'; + this.excelFileName = 'FileData.xlsx'; + this.pngFilename = 'FileData_Chart.png'; + this.pdfFileNameForChart = 'FileData_Chart.pdf'; + } + const data = { + oQueryLog: oQueryLog + } + this.gridData = []; + this.gridColumns = []; + this.gridData = this.gridColumns = []; + this.OpenAIResponce = {}; + this.isShowChart = false; + this.opeAiService.OpenAIPreviousQueryExecution(data).subscribe( + (resp: any) => { + this.OpenAIResponce = JSON.parse(resp); + }, + (err: any) => { + this.isShowCurrentUserAllQueryLog = false + this.loadingpanelService.ShowLoadingPanel = false; + this.notificationService.showError(err.error, "Error"); + }, + () => { + this.isShowCurrentUserAllQueryLog = false + this.loadingpanelService.ShowLoadingPanel = false; + this.ProcessOpenAIData(); + this.LoadCurrentUserAllQueryLog(this.selectedReportGroup); + }); + } + else{ + this.isShowCurrentUserAllQueryLog = false + } + } + else{ + this.isShowCurrentUserAllQueryLog = false + } + } + ShowChart(){ + this.isShowChartOption = true; + } + onChartGroupChange(group : any){ + } + showOutOfScopeNotice() { + const msg = this.scopeMessages[this.selectedReportGroup] ?? "I can only help with questions related to this section."; + this.setInvalidQueryMessage(msg); + this.isUserPromptIsNotRelated = true; + } + filteredQueryLog(): OpenAIQueryLog[] { + if (!this.querySearchText) return this.currentUserAllQueryLog; + const search = this.querySearchText.toLowerCase(); + return this.currentUserAllQueryLog.filter(item => + item.userPrompt.toLowerCase().includes(search) + ); + } + exportChartImage(chart: ChartComponent) { + this.chartTransitions = false; + setTimeout(() => { + chart.exportImage().then((dataURI: string) => { + saveAs(dataURI, this.pngFilename); + this.chartTransitions = true; + }); + }); + } + exportSizedChart(chart: ChartComponent): void { + const visual = chart.exportVisual({width: PAGE_RECT.size.width,}); + this.exportElement(visual); + } + exportElement(element: Group): void { + exportPDF(element, {paperSize: "A4",margin: "1cm",}).then((dataURI) => {saveAs(dataURI, this.pdfFileNameForChart);}); + } + private scopeMessages: Record<number, string> = { + [EnumReportGroupType.Employee]: "I can only help with questions related to Employee information.", + [EnumReportGroupType.Salary]: "I can only help with questions related to Employee Salary.", + [EnumReportGroupType.Performance]:"I can only help with questions related to Employee Performance.", + [EnumReportGroupType.Training]: "I can only help with questions related to Employee Training." + }; + ShareThisChatWithOthers(queryLog : OpenAIQueryLog){ + this.currentChat = undefined; + this.currentChat = queryLog; + this.ShareThisChat(); + } + ShareThisChat(){ + if(this.currentChat !== undefined){ + this.currentChat.isCommon = !this.currentChat.isCommon; + const data = { + oQueryLog : this.currentChat + } + this.loadingpanelService.ShowLoadingPanel = false; + this.opeAiService.ChatShareWithOthers(data).subscribe( + (resp: any) => { + }, + (err: any) => { + this.loadingpanelService.ShowLoadingPanel = false; + this.notificationService.showError(err.error, "Error"); + }, + () => { + this.loadingpanelService.ShowLoadingPanel = false; + this.LoadCurrentUserAllQueryLog(this.selectedReportGroup); + this.notificationService.showSuccess("Successfully Shared"); + }); + } + } +} + +export class ReportColumnDefinition { + tableName: string; + fields: string[] = []; +} diff --git a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.js b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.js new file mode 100644 index 0000000..3e7a151 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.js @@ -0,0 +1,246 @@ +"use strict"; +var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { + function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } + var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; + var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; + var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); + var _, done = false; + for (var i = decorators.length - 1; i >= 0; i--) { + var context = {}; + for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; + for (var p in contextIn.access) context.access[p] = contextIn.access[p]; + context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; + var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); + if (kind === "accessor") { + if (result === void 0) continue; + if (result === null || typeof result !== "object") throw new TypeError("Object expected"); + if (_ = accept(result.get)) descriptor.get = _; + if (_ = accept(result.set)) descriptor.set = _; + if (_ = accept(result.init)) initializers.unshift(_); + } + else if (_ = accept(result)) { + if (kind === "field") initializers.unshift(_); + else descriptor[key] = _; + } + } + if (target) Object.defineProperty(target, contextIn.name, descriptor); + done = true; +}; +var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { + var useValue = arguments.length > 2; + for (var i = 0; i < initializers.length; i++) { + value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); + } + return useValue ? value : void 0; +}; +var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) { + if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; + return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.HRMOpenAIModule = void 0; +var core_1 = require("@angular/core"); +var common_1 = require("@angular/common"); +var Basic_From_Builder_module_1 = require("../form-builder/Basic-From-Builder.module"); +var kendo_angular_grid_1 = require("@progress/kendo-angular-grid"); +//import { BodyModule, SharedModule } from '@progress/kendo-angular-grid'; +var kendo_angular_inputs_1 = require("@progress/kendo-angular-inputs"); +var kendo_angular_charts_1 = require("@progress/kendo-angular-charts"); +var kendo_angular_dialog_1 = require("@progress/kendo-angular-dialog"); +var forms_1 = require("@angular/forms"); +var kendo_angular_dropdowns_1 = require("@progress/kendo-angular-dropdowns"); +var kendo_angular_dateinputs_1 = require("@progress/kendo-angular-dateinputs"); +var kendo_angular_indicators_1 = require("@progress/kendo-angular-indicators"); +var loading_panel_module_1 = require("../hrm-loding panel/loading-panel.module"); +var loding_panel_service_1 = require("../hrm-loding panel/loding.panel.service"); +var kendo_angular_buttons_1 = require("@progress/kendo-angular-buttons"); +var inputtext_1 = require("primeng/inputtext"); +var dialog_1 = require("primeng/dialog"); +var scrollpanel_1 = require("primeng/scrollpanel"); +var accordion_1 = require("primeng/accordion"); +var autocomplete_1 = require("primeng/autocomplete"); +var breadcrumb_1 = require("primeng/breadcrumb"); +var calendar_1 = require("primeng/calendar"); +var card_1 = require("primeng/card"); +var carousel_1 = require("primeng/carousel"); +var chart_1 = require("primeng/chart"); +var checkbox_1 = require("primeng/checkbox"); +var chips_1 = require("primeng/chips"); +//import { CodeHighlighterModule } from 'primeng/codehighlighter'; +var confirmdialog_1 = require("primeng/confirmdialog"); +var colorpicker_1 = require("primeng/colorpicker"); +var contextmenu_1 = require("primeng/contextmenu"); +var dataview_1 = require("primeng/dataview"); +var dropdown_1 = require("primeng/dropdown"); +var editor_1 = require("primeng/editor"); +var fieldset_1 = require("primeng/fieldset"); +var fileupload_1 = require("primeng/fileupload"); +//import { FullCalendarModule } from 'primeng/fullcalendar'; +var galleria_1 = require("primeng/galleria"); +var inplace_1 = require("primeng/inplace"); +var inputmask_1 = require("primeng/inputmask"); +var inputswitch_1 = require("primeng/inputswitch"); +//import { InputTextareaModule } from 'primeng/inputtextarea'; +//import { LightboxModule } from 'primeng/lightbox'; +var listbox_1 = require("primeng/listbox"); +var megamenu_1 = require("primeng/megamenu"); +var menu_1 = require("primeng/menu"); +var menubar_1 = require("primeng/menubar"); +var messages_1 = require("primeng/messages"); +var message_1 = require("primeng/message"); +var multiselect_1 = require("primeng/multiselect"); +var orderlist_1 = require("primeng/orderlist"); +var organizationchart_1 = require("primeng/organizationchart"); +var overlaypanel_1 = require("primeng/overlaypanel"); +var paginator_1 = require("primeng/paginator"); +var panel_1 = require("primeng/panel"); +var panelmenu_1 = require("primeng/panelmenu"); +var password_1 = require("primeng/password"); +var picklist_1 = require("primeng/picklist"); +var progressbar_1 = require("primeng/progressbar"); +var radiobutton_1 = require("primeng/radiobutton"); +var rating_1 = require("primeng/rating"); +var selectbutton_1 = require("primeng/selectbutton"); +//import { SlideMenuModule } from 'primeng/slidemenu'; +var slider_1 = require("primeng/slider"); +//import { SpinnerModule } from 'primeng/spinner'; +var splitbutton_1 = require("primeng/splitbutton"); +var steps_1 = require("primeng/steps"); +var tabmenu_1 = require("primeng/tabmenu"); +var table_1 = require("primeng/table"); +var tabview_1 = require("primeng/tabview"); +var terminal_1 = require("primeng/terminal"); +var tieredmenu_1 = require("primeng/tieredmenu"); +var toast_1 = require("primeng/toast"); +var togglebutton_1 = require("primeng/togglebutton"); +var toolbar_1 = require("primeng/toolbar"); +var tooltip_1 = require("primeng/tooltip"); +var tree_1 = require("primeng/tree"); +var treetable_1 = require("primeng/treetable"); +//import { VirtualScrollerModule } from 'primeng/virtualscroller'; +var kendo_angular_layout_1 = require("@progress/kendo-angular-layout"); +var ng2_pdf_viewer_1 = require("ng2-pdf-viewer"); +var kendo_angular_layout_2 = require("@progress/kendo-angular-layout"); +var kendo_angular_layout_3 = require("@progress/kendo-angular-layout"); +var kendo_angular_progressbar_1 = require("@progress/kendo-angular-progressbar"); +var app_notification_service_1 = require("../app.notification.service"); +var picker_module_1 = require("../picker/picker.module"); +; +var BackgroundCanvas_module_1 = require("../BackgroundCanvas/BackgroundCanvas.module"); +var hrmopen_ai_routing_module_1 = require("./hrmopen-ai-routing.module"); +var hrmopen_aiteast_component_1 = require("./hrmopen-ai-test/hrmopen-aiteast.component"); +var hrmopen_ai_service_1 = require("../_services/HRMOpenAI/hrmopen-ai.service"); +require("@progress/kendo-drawing"); +var HRMOpenAIModule = function () { + var _classDecorators = [(0, core_1.NgModule)({ + declarations: [hrmopen_aiteast_component_1.HRMOpenAITestComponent], + imports: [ + common_1.CommonModule, + hrmopen_ai_routing_module_1.HRMOpenAIRoutingModule, + common_1.CommonModule, + forms_1.FormsModule, + forms_1.ReactiveFormsModule, + kendo_angular_dialog_1.DialogsModule, + kendo_angular_grid_1.GridModule, + kendo_angular_inputs_1.InputsModule, + kendo_angular_grid_1.PDFModule, + kendo_angular_grid_1.ExcelModule, + kendo_angular_charts_1.ChartsModule, + Basic_From_Builder_module_1.BasicFromBuilderModule, + kendo_angular_dropdowns_1.DropDownsModule, + kendo_angular_dateinputs_1.DateInputsModule, + kendo_angular_dateinputs_1.DateInputsModule, + picker_module_1.Pickermodule, + kendo_angular_indicators_1.IndicatorsModule, + loading_panel_module_1.LoadingPanelModule, + kendo_angular_buttons_1.ButtonModule, + inputtext_1.InputTextModule, + dialog_1.DialogModule, + scrollpanel_1.ScrollPanelModule, + ng2_pdf_viewer_1.PdfViewerModule, + autocomplete_1.AutoCompleteModule, + breadcrumb_1.BreadcrumbModule, + calendar_1.CalendarModule, + card_1.CardModule, + carousel_1.CarouselModule, + chart_1.ChartModule, + checkbox_1.CheckboxModule, + chips_1.ChipsModule, + confirmdialog_1.ConfirmDialogModule, + accordion_1.AccordionModule, + colorpicker_1.ColorPickerModule, + contextmenu_1.ContextMenuModule, + dataview_1.DataViewModule, + dropdown_1.DropdownModule, + editor_1.EditorModule, + fieldset_1.FieldsetModule, + fileupload_1.FileUploadModule, + galleria_1.GalleriaModule, + inplace_1.InplaceModule, + inputmask_1.InputMaskModule, + inputswitch_1.InputSwitchModule, + listbox_1.ListboxModule, + megamenu_1.MegaMenuModule, + menu_1.MenuModule, + menubar_1.MenubarModule, + messages_1.MessagesModule, + message_1.MessageModule, + multiselect_1.MultiSelectModule, + orderlist_1.OrderListModule, + organizationchart_1.OrganizationChartModule, + overlaypanel_1.OverlayPanelModule, + paginator_1.PaginatorModule, + panel_1.PanelModule, + panelmenu_1.PanelMenuModule, + password_1.PasswordModule, + picklist_1.PickListModule, + progressbar_1.ProgressBarModule, + radiobutton_1.RadioButtonModule, + rating_1.RatingModule, + selectbutton_1.SelectButtonModule, + kendo_angular_layout_1.CardModule, + treetable_1.TreeTableModule, + tree_1.TreeModule, + tooltip_1.TooltipModule, + toolbar_1.ToolbarModule, + togglebutton_1.ToggleButtonModule, + toast_1.ToastModule, + tieredmenu_1.TieredMenuModule, + terminal_1.TerminalModule, + tabview_1.TabViewModule, + table_1.TableModule, + tabmenu_1.TabMenuModule, + steps_1.StepsModule, + splitbutton_1.SplitButtonModule, + slider_1.SliderModule, + kendo_angular_layout_2.TabStripModule, + kendo_angular_layout_3.ExpansionPanelModule, + kendo_angular_progressbar_1.ProgressBarModule, + BackgroundCanvas_module_1.BackgroundCanvasModule + ], + providers: [ + loding_panel_service_1.loadingPanelService, + app_notification_service_1.HRMNotificationService, + hrmopen_ai_service_1.HRMOpenAIService + ] + })]; + var _classDescriptor; + var _classExtraInitializers = []; + var _classThis; + var HRMOpenAIModule = _classThis = /** @class */ (function () { + function HRMOpenAIModule_1() { + } + return HRMOpenAIModule_1; + }()); + __setFunctionName(_classThis, "HRMOpenAIModule"); + (function () { + var _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; + __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); + HRMOpenAIModule = _classThis = _classDescriptor.value; + if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); + __runInitializers(_classThis, _classExtraInitializers); + })(); + return HRMOpenAIModule = _classThis; +}(); +exports.HRMOpenAIModule = HRMOpenAIModule; +//# sourceMappingURL=hrmopen-ai.module.js.map \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.js.map b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.js.map new file mode 100644 index 0000000..5d56b46 --- /dev/null +++ b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.js.map @@ -0,0 +1 @@ +{"version":3,"file":"hrmopen-ai.module.js","sourceRoot":"","sources":["hrmopen-ai.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,sCAAyC;AACzC,0CAAwD;AACxD,uFAAmF;AACnF,mEAAkF;AAClF,0EAA0E;AAC1E,uEAA8D;AAC9D,uEAA8D;AAC9D,uEAA6E;AAC7E,wCAAkE;AAClE,6EAAoE;AACpE,+EAAsE;AACtE,+EAAsE;AACtE,iFAA8E;AAC9E,iFAA+E;AAE/E,yEAA+D;AAC/D,+CAAoD;AACpD,yCAA8C;AAC9C,mDAAwD;AAExD,+CAAoD;AACpD,qDAA0D;AAC1D,iDAAsD;AACtD,6CAAkD;AAClD,qCAA0C;AAC1C,6CAAkD;AAClD,uCAA4C;AAC5C,6CAAkD;AAClD,uCAA4C;AAC5C,kEAAkE;AAClE,uDAA4D;AAC5D,mDAAwD;AACxD,mDAAwD;AACxD,6CAAkD;AAClD,6CAAkD;AAClD,yCAA8C;AAC9C,6CAAkD;AAClD,iDAAsD;AACtD,4DAA4D;AAC5D,6CAAkD;AAClD,2CAAgD;AAChD,+CAAoD;AACpD,mDAAwD;AACxD,8DAA8D;AAC9D,oDAAoD;AACpD,2CAAgD;AAChD,6CAAkD;AAClD,qCAA0C;AAC1C,2CAAgD;AAChD,6CAAkD;AAClD,2CAAgD;AAChD,mDAAwD;AACxD,+CAAoD;AACpD,+DAAoE;AACpE,qDAA0D;AAC1D,+CAAoD;AACpD,uCAA4C;AAC5C,+CAAoD;AACpD,6CAAkD;AAClD,6CAAkD;AAClD,mDAAwD;AACxD,mDAAwD;AACxD,yCAA8C;AAC9C,qDAA0D;AAC1D,sDAAsD;AACtD,yCAA8C;AAC9C,kDAAkD;AAClD,mDAAwD;AACxD,uCAA4C;AAC5C,2CAAgD;AAChD,uCAA4C;AAC5C,2CAAgD;AAChD,6CAAkD;AAClD,iDAAsD;AACtD,uCAA4C;AAC5C,qDAA0D;AAC1D,2CAAgD;AAChD,2CAAgD;AAChD,qCAA0C;AAC1C,+CAAoD;AACpD,kEAAkE;AAClE,uEAA+E;AAE/E,iDAAiD;AACjD,uEAAgE;AAChE,uEAAsE;AAMtE,iFAAkG;AAGlG,wEAAqE;AAErE,yDAAuD;AAK2B,CAAC;AACnF,uFAAqF;AAErF,yEAAqE;AACrE,yFAAqF;AACrF,gFAA6E;AAC7E,mCAAiC;AA8FjC;4BA5FC,IAAA,eAAQ,EAAC;YACR,YAAY,EAAE,CAAC,kDAAsB,CAAC;YACtC,OAAO,EAAE;gBACP,qBAAY;gBACZ,kDAAsB;gBACtB,qBAAY;gBACZ,mBAAW;gBACX,2BAAmB;gBACnB,oCAAa;gBACb,+BAAU;gBACV,mCAAY;gBACZ,8BAAS;gBACT,gCAAW;gBACX,mCAAY;gBACZ,kDAAsB;gBACtB,yCAAe;gBACf,2CAAgB;gBAChB,2CAAgB;gBAChB,4BAAY;gBACZ,2CAAgB;gBAChB,yCAAkB;gBAClB,oCAAY;gBACZ,2BAAe;gBACf,qBAAY;gBACZ,+BAAiB;gBACjB,gCAAe;gBACf,iCAAkB;gBAClB,6BAAgB;gBAChB,yBAAc;gBACd,iBAAU;gBACV,yBAAc;gBACd,mBAAW;gBACX,yBAAc;gBACd,mBAAW;gBACX,mCAAmB;gBACnB,2BAAe;gBACf,+BAAiB;gBACjB,+BAAiB;gBACjB,yBAAc;gBACd,yBAAc;gBACd,qBAAY;gBACZ,yBAAc;gBACd,6BAAgB;gBAChB,yBAAc;gBACd,uBAAa;gBACb,2BAAe;gBACf,+BAAiB;gBACjB,uBAAa;gBACb,yBAAc;gBACd,iBAAU;gBACV,uBAAa;gBACb,yBAAc;gBACd,uBAAa;gBACb,+BAAiB;gBACjB,2BAAe;gBACf,2CAAuB;gBACvB,iCAAkB;gBAClB,2BAAe;gBACf,mBAAW;gBACX,2BAAe;gBACf,yBAAc;gBACd,yBAAc;gBACd,+BAAiB;gBACjB,+BAAiB;gBACjB,qBAAY;gBACZ,iCAAkB;gBAClB,iCAAe;gBACf,2BAAe;gBACf,iBAAU;gBACV,uBAAa;gBACb,uBAAa;gBACb,iCAAkB;gBAClB,mBAAW;gBACX,6BAAgB;gBAChB,yBAAc;gBACd,uBAAa;gBACb,mBAAW;gBACX,uBAAa;gBACb,mBAAW;gBACX,+BAAiB;gBACjB,qBAAY;gBACZ,qCAAc;gBACd,2CAAoB;gBACpB,6CAAsB;gBACtB,gDAAsB;aACvB;YACA,SAAS,EAAC;gBACT,0CAAmB;gBACnB,iDAAsB;gBACtB,qCAAgB;aACjB;SACF,CAAC;;;;;;QAC6B,CAAC;QAAD,wBAAC;IAAD,CAAC;;;;QAAhC,6KAAgC;;;QAAnB,uDAAe;;;IAAI;AAAnB,0CAAe"} \ No newline at end of file diff --git a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.ts b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.ts new file mode 100644 index 0000000..98e6b4d --- /dev/null +++ b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai.module.ts @@ -0,0 +1,197 @@ +import { LMSService } from "./../_services/LearningManagement/lms.service"; +import { NgModule } from "@angular/core"; +import { CommonModule, DatePipe } from "@angular/common"; +import { BasicFromBuilderModule } from "../form-builder/Basic-From-Builder.module"; +import { ExcelModule, GridModule, PDFModule } from "@progress/kendo-angular-grid"; +//import { BodyModule, SharedModule } from '@progress/kendo-angular-grid'; +import { InputsModule } from "@progress/kendo-angular-inputs"; +import { ChartsModule } from "@progress/kendo-angular-charts"; +import { DialogsModule, WindowModule } from "@progress/kendo-angular-dialog"; +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { DropDownsModule } from "@progress/kendo-angular-dropdowns"; +import { DateInputsModule } from "@progress/kendo-angular-dateinputs"; +import { IndicatorsModule } from "@progress/kendo-angular-indicators"; +import { LoadingPanelModule } from "../hrm-loding panel/loading-panel.module"; +import { loadingPanelService } from "../hrm-loding panel/loding.panel.service"; +import { AttendanceServices } from "../_services/attendance/attendance.service"; +import { ButtonModule } from "@progress/kendo-angular-buttons"; +import { InputTextModule } from "primeng/inputtext"; +import { DialogModule } from "primeng/dialog"; +import { ScrollPanelModule } from "primeng/scrollpanel"; +import { HttpClient } from "@angular/common/http"; +import { AccordionModule } from "primeng/accordion"; +import { AutoCompleteModule } from "primeng/autocomplete"; +import { BreadcrumbModule } from "primeng/breadcrumb"; +import { CalendarModule } from "primeng/calendar"; +import { CardModule } from "primeng/card"; +import { CarouselModule } from "primeng/carousel"; +import { ChartModule } from "primeng/chart"; +import { CheckboxModule } from "primeng/checkbox"; +import { ChipsModule } from "primeng/chips"; +//import { CodeHighlighterModule } from 'primeng/codehighlighter'; +import { ConfirmDialogModule } from "primeng/confirmdialog"; +import { ColorPickerModule } from "primeng/colorpicker"; +import { ContextMenuModule } from "primeng/contextmenu"; +import { DataViewModule } from "primeng/dataview"; +import { DropdownModule } from "primeng/dropdown"; +import { EditorModule } from "primeng/editor"; +import { FieldsetModule } from "primeng/fieldset"; +import { FileUploadModule } from "primeng/fileupload"; +//import { FullCalendarModule } from 'primeng/fullcalendar'; +import { GalleriaModule } from "primeng/galleria"; +import { InplaceModule } from "primeng/inplace"; +import { InputMaskModule } from "primeng/inputmask"; +import { InputSwitchModule } from "primeng/inputswitch"; +//import { InputTextareaModule } from 'primeng/inputtextarea'; +//import { LightboxModule } from 'primeng/lightbox'; +import { ListboxModule } from "primeng/listbox"; +import { MegaMenuModule } from "primeng/megamenu"; +import { MenuModule } from "primeng/menu"; +import { MenubarModule } from "primeng/menubar"; +import { MessagesModule } from "primeng/messages"; +import { MessageModule } from "primeng/message"; +import { MultiSelectModule } from "primeng/multiselect"; +import { OrderListModule } from "primeng/orderlist"; +import { OrganizationChartModule } from "primeng/organizationchart"; +import { OverlayPanelModule } from "primeng/overlaypanel"; +import { PaginatorModule } from "primeng/paginator"; +import { PanelModule } from "primeng/panel"; +import { PanelMenuModule } from "primeng/panelmenu"; +import { PasswordModule } from "primeng/password"; +import { PickListModule } from "primeng/picklist"; +import { ProgressBarModule } from "primeng/progressbar"; +import { RadioButtonModule } from "primeng/radiobutton"; +import { RatingModule } from "primeng/rating"; +import { SelectButtonModule } from "primeng/selectbutton"; +//import { SlideMenuModule } from 'primeng/slidemenu'; +import { SliderModule } from "primeng/slider"; +//import { SpinnerModule } from 'primeng/spinner'; +import { SplitButtonModule } from "primeng/splitbutton"; +import { StepsModule } from "primeng/steps"; +import { TabMenuModule } from "primeng/tabmenu"; +import { TableModule } from "primeng/table"; +import { TabViewModule } from "primeng/tabview"; +import { TerminalModule } from "primeng/terminal"; +import { TieredMenuModule } from "primeng/tieredmenu"; +import { ToastModule } from "primeng/toast"; +import { ToggleButtonModule } from "primeng/togglebutton"; +import { ToolbarModule } from "primeng/toolbar"; +import { TooltipModule } from "primeng/tooltip"; +import { TreeModule } from "primeng/tree"; +import { TreeTableModule } from "primeng/treetable"; +//import { VirtualScrollerModule } from 'primeng/virtualscroller'; +import { CardModule as kendoCardModule } from "@progress/kendo-angular-layout"; +import { AppWindowPopUp } from "../app.windowPopup.service"; +import { PdfViewerModule } from "ng2-pdf-viewer"; +import { TabStripModule } from "@progress/kendo-angular-layout"; +import { ExpansionPanelModule } from "@progress/kendo-angular-layout"; + +import { BrowserModule } from "@angular/platform-browser"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; + +import { ProgressBarModule as KendoProgressBarModule } from "@progress/kendo-angular-progressbar"; +import { DropDownListModule } from "@progress/kendo-angular-dropdowns"; + +import { HRMNotificationService } from "../app.notification.service"; +import { LeaveService } from "../_services/leave/leave.service"; +import { Pickermodule } from "../picker/picker.module"; +import { BasicService } from "../_services/Basic/basic.service"; +import { PaymentApprovalService } from "../_services/payment-approval/payment-approval.service"; +import { SalaryService } from "../_services/payroll/salary.service"; +import { UntilityHandlerService } from "../utility.hanldler.service"; +import { EmpLifeCycleServices } from "../_services/employee/empLifeCycle.service"; +import { BackgroundCanvasModule } from "../BackgroundCanvas/BackgroundCanvas.module"; + +import { HRMOpenAIRoutingModule } from "./hrmopen-ai-routing.module"; +import { HRMOpenAITestComponent } from "./hrmopen-ai-test/hrmopen-aiteast.component"; +import { HRMOpenAIService } from "../_services/HRMOpenAI/hrmopen-ai.service"; +import "@progress/kendo-drawing"; + +@NgModule({ + declarations: [HRMOpenAITestComponent], + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + DialogsModule, + GridModule, + InputsModule, + PDFModule, + ExcelModule, + ChartsModule, + BasicFromBuilderModule, + DropDownsModule, + DateInputsModule, + DateInputsModule, + Pickermodule, + IndicatorsModule, + LoadingPanelModule, + ButtonModule, + InputTextModule, + DialogModule, + ScrollPanelModule, + PdfViewerModule, + AutoCompleteModule, + BreadcrumbModule, + CalendarModule, + CardModule, + CarouselModule, + ChartModule, + CheckboxModule, + ChipsModule, + ConfirmDialogModule, + AccordionModule, + ColorPickerModule, + ContextMenuModule, + DataViewModule, + DropdownModule, + EditorModule, + FieldsetModule, + FileUploadModule, + GalleriaModule, + InplaceModule, + InputMaskModule, + InputSwitchModule, + ListboxModule, + MegaMenuModule, + MenuModule, + MenubarModule, + MessagesModule, + MessageModule, + MultiSelectModule, + OrderListModule, + OrganizationChartModule, + OverlayPanelModule, + PaginatorModule, + PanelModule, + PanelMenuModule, + PasswordModule, + PickListModule, + ProgressBarModule, + RadioButtonModule, + RatingModule, + SelectButtonModule, + kendoCardModule, + TreeTableModule, + TreeModule, + TooltipModule, + ToolbarModule, + ToggleButtonModule, + ToastModule, + TieredMenuModule, + TerminalModule, + TabViewModule, + TableModule, + TabMenuModule, + StepsModule, + SplitButtonModule, + SliderModule, + TabStripModule, + ExpansionPanelModule, + KendoProgressBarModule, + BackgroundCanvasModule, + HRMOpenAIRoutingModule + ], + providers: [loadingPanelService, HRMNotificationService, HRMOpenAIService], +}) +export class HRMOpenAIModule {} diff --git a/HRM.UI/ClientApp/src/assets/resource/SGS-Menu.xml b/HRM.UI/ClientApp/src/assets/resource/SGS-Menu.xml index 1b256e9..3a31134 100644 --- a/HRM.UI/ClientApp/src/assets/resource/SGS-Menu.xml +++ b/HRM.UI/ClientApp/src/assets/resource/SGS-Menu.xml @@ -117,7 +117,7 @@ RoleType="Admin"></Menu> <Menu key="2.4.6" name="HR DashBoard" url="/core-hr/core-hr-dashboard" parentkey="2.4" action="add;edit;delete" RoleType="Admin"></Menu> - + <Menu key="2.5" name="Profile Update Request" url="/ess/profile-update-admin" parentkey="2" action="add;edit;delete" RoleType="Admin"></Menu> <Menu key="2.6" name="Grievance Management" url="" parentkey="2" action="add;edit;delete" RoleType="Admin"></Menu> @@ -156,7 +156,6 @@ <Menu key="7.1.7.4" name="Multiple Employee Leave Ledger" url="/reports/report-viewer/102" parentkey="7.1.7" action="add;edit;delete" RoleType="Admin"></Menu> - <Menu key="5" name="Attendance" url="" parentkey="" action="add;edit;delete" RoleType="Admin"></Menu> <Menu key="5.1" name="Master Data" url="" parentkey="5" action="add;edit;delete" RoleType="Admin"></Menu> <Menu key="5.1.1" name="Shift" url="/attendance/shift" parentkey="5.1" action="add;edit;delete" diff --git a/HRM.UI/Configurations/DI/ServiceCollectionExtensions.cs b/HRM.UI/Configurations/DI/ServiceCollectionExtensions.cs index 8eae82b..e2f57ad 100644 --- a/HRM.UI/Configurations/DI/ServiceCollectionExtensions.cs +++ b/HRM.UI/Configurations/DI/ServiceCollectionExtensions.cs @@ -318,6 +318,10 @@ namespace HRM.UI.Configuration.DI services.AddTransient<IMembersTransactionDetailsService, MembersTransactionDetailsService>(); services.AddTransient<IEmployeeTaxExcecptionService, EmployeeTaxExceptionService>(); + //OpenAI + services.AddTransient<IOpenAIQueryLogService, OpenAIQueryLogService>(); + + // services.AddTransient<IDataIntergrationRequestService, DataIntergrationRequestService>(); } diff --git a/HRM.UI/Controllers/OpenAIController/OpenAIController.cs b/HRM.UI/Controllers/OpenAIController/OpenAIController.cs new file mode 100644 index 0000000..b6d8a24 --- /dev/null +++ b/HRM.UI/Controllers/OpenAIController/OpenAIController.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using AutoMapper.Configuration; +using Ease.Core; +using HRM.BO; +using HRM.DA; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Extensions.Configuration; +using Payroll.BO; +using Payroll.Service; +using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration; + +namespace HRM.UI.Controllers +{ + [ApiController] + [Route("api/HRMApenAPIAI")] + [Authorize] + public class OpenAIController : ControllerBase + { + private readonly OpenAIService _openAIService; + private readonly IOpenAIQueryLogService _openAIQueryLogService; + + public OpenAIController(OpenAIService openAIService, IOpenAIQueryLogService openAIQueryLogService) + { + _openAIService = openAIService; + _openAIQueryLogService = openAIQueryLogService; + } + + [HttpPost] + [Route("AskOpenAI")] + public async Task<IActionResult> AskOpenAI(dynamic data) + { + CurrentUser currentUser = CurrentUser.GetCurrentUser(HttpContext.User); + var item = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(data)); + string userCommand = (string)item["userPrompt"].ToObject<string>(); + EnumReportGroupType reportGroupType = (EnumReportGroupType)item["reportGroup"].ToObject<EnumReportGroupType>(); + DataSet oEmpBasicInfos = new DataSet(); + try + { + + var response = await _openAIService.AskOpenAISqlQueryWithChartAsync(userCommand, (int)currentUser.UserID, reportGroupType, + currentUser.NextPayProcessDate ?? DateTime.Now); + return Ok(response); + } + catch (Exception ex) + { + return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); + } + } + + [HttpGet] + [Route("GetReportColumnDefinition/{reportGroupType}")] + public async Task<IActionResult> GetReportColumnDefinition(EnumReportGroupType reportGroupType) + { + CurrentUser currentUser = CurrentUser.GetCurrentUser(HttpContext.User); + try + { + var response = await _openAIService.GetColumnDefinitions(reportGroupType); + return Ok(response); + } + catch (Exception ex) + { + return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); + } + } + + [HttpGet] + [Route("GetCurrentUserAllQueryLog/{reportGroupType}")] + public ActionResult GetCurrentUserAllQueryLog(EnumReportGroupType reportGroupType) + { + CurrentUser currentUser = CurrentUser.GetCurrentUser(HttpContext.User); + List<OpenAIQueryLog> queryLogs = new List<OpenAIQueryLog>(); + try + { + queryLogs = _openAIQueryLogService.GetAllQueryLogByUserID((int)currentUser.UserID, reportGroupType); + } + catch (Exception ex) + { + return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); + } + return Ok(queryLogs); + } + + [HttpPost] + [Route("OpenAIPreviousQueryExecution")] + public async Task<IActionResult> OpenAIPreviousQueryExecution(dynamic data) + { + var item = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(data)); + OpenAIQueryLog userCommand = (OpenAIQueryLog)item["oQueryLog"].ToObject<OpenAIQueryLog>(); + try + { + var response = await _openAIService.OpenAIPreviousQueryExecution(userCommand); + return Ok(response); + } + catch (Exception ex) + { + return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); + } + } + + [HttpPost] + [Route("ChatShareWithOthers")] + public async Task<IActionResult> ChatShareWithOthers(dynamic data) + { + var item = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(data)); + OpenAIQueryLog userCommand = (OpenAIQueryLog)item["oQueryLog"].ToObject<OpenAIQueryLog>(); + try + { + _openAIQueryLogService.ChatShareWithOthers(userCommand); + return Ok(); + } + catch (Exception ex) + { + return StatusCode(StatusCodes.Status500InternalServerError, ex.Message); + } + } + + } +} diff --git a/HRM.UI/Startup.cs b/HRM.UI/Startup.cs index 3ae1b9e..7c944d1 100644 --- a/HRM.UI/Startup.cs +++ b/HRM.UI/Startup.cs @@ -202,6 +202,18 @@ namespace HRM.UI.Api #endregion + + #region AI Integration (OpenAI) + services.AddSingleton<OpenAIService>(provider => + { + var config = provider.GetRequiredService<IConfiguration>(); + var encryptedkey = config["OpenAI:ApiKey"]; + var model = config["OpenAI:Model"]; + int maxLimit = 5000; + return new OpenAIService(encryptedkey, model, maxLimit); + }); + #endregion + #region Business Service services.ConfigureBusinessServices(Configuration); From 21a06545cec82c8c228f22d2a65e48afca5a365b Mon Sep 17 00:00:00 2001 From: Sabbir Ahmed <sabbir@celimited.com> Date: Mon, 13 Jul 2026 09:11:18 +0600 Subject: [PATCH 2/3] sql query changed --- HRM.DA/DA/OpenAIDA/OpenAIDA.cs | 3 +-- .../hrmopen-ai-test/hrmopen-aiteast.component.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/HRM.DA/DA/OpenAIDA/OpenAIDA.cs b/HRM.DA/DA/OpenAIDA/OpenAIDA.cs index d124bf4..618b80e 100644 --- a/HRM.DA/DA/OpenAIDA/OpenAIDA.cs +++ b/HRM.DA/DA/OpenAIDA/OpenAIDA.cs @@ -84,8 +84,7 @@ namespace HRM.DA { if (reportGroupType == EnumReportGroupType.Salary) { - //sql = SQLParser.MakeSQL("select distinct(Head) as Head from vwEmployeeSalary"); - sql = SQLParser.MakeSQL("SELECT DISTINCT DESCRIPTION AS Head FROM SALARYMONTHLYDETAIL WHERE ITEMGROUP IN (1,2,3,5,8)"); + sql = SQLParser.MakeSQL("select distinct(Head) as Head from vwEmployeeSalary"); } else if(reportGroupType == EnumReportGroupType.Leave) { diff --git a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts index eea5cb9..58a031a 100644 --- a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts +++ b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts @@ -404,7 +404,7 @@ export class HRMOpenAITestComponent implements OnInit{ || this.selectedReportGroup === EnumReportGroupType.Training || this.selectedReportGroup === EnumReportGroupType.Attendance || this.selectedReportGroup === EnumReportGroupType.Leave)){ - this.loadingpanelService.ShowLoadingPanel = false; + this.loadingpanelService.ShowLoadingPanel = true; this.reportColumnDefinition = []; this.tableColumnDefinition = []; this.opeAiService.GetReportColumnDefinition(this.selectedReportGroup).subscribe( @@ -412,11 +412,11 @@ export class HRMOpenAITestComponent implements OnInit{ this.reportColumnDefinition = resp as any[]; }, (err: any) => { - this.loadingpanelService.ShowLoadingPanel = false; + this.loadingpanelService.ShowLoadingPanel = true; this.notificationService.showError(err.error, "Error"); }, () => { - this.loadingpanelService.ShowLoadingPanel = false; + this.loadingpanelService.ShowLoadingPanel = true; this.gridData = []; if (this.reportColumnDefinition && this.reportColumnDefinition.length > 0) { const excludedNormalizedCaptions = new Set(['employeeid']); @@ -452,7 +452,7 @@ export class HRMOpenAITestComponent implements OnInit{ let oQueryLog : OpenAIQueryLog = this.currentUserAllQueryLog.find(x => x.id === id) ?? undefined; if(oQueryLog != undefined){ this.isShowCurrentUserAllQueryLog = false - this.loadingpanelService.ShowLoadingPanel = false; + this.loadingpanelService.ShowLoadingPanel = true; this.inputUserCommand = oQueryLog.userPrompt; this.selectedReportGroup = oQueryLog.reportGroup; if(oQueryLog.title && oQueryLog.title !== ""){ @@ -481,12 +481,12 @@ export class HRMOpenAITestComponent implements OnInit{ }, (err: any) => { this.isShowCurrentUserAllQueryLog = false - this.loadingpanelService.ShowLoadingPanel = false; + this.loadingpanelService.ShowLoadingPanel = true; this.notificationService.showError(err.error, "Error"); }, () => { this.isShowCurrentUserAllQueryLog = false - this.loadingpanelService.ShowLoadingPanel = false; + this.loadingpanelService.ShowLoadingPanel = true; this.ProcessOpenAIData(); this.LoadCurrentUserAllQueryLog(this.selectedReportGroup); }); From f8e081d7d1e9ecb06c7f51152c078e2952597e72 Mon Sep 17 00:00:00 2001 From: Sabbir Ahmed <sabbir@celimited.com> Date: Mon, 13 Jul 2026 11:27:47 +0600 Subject: [PATCH 3/3] AI Integration successful --- .../hrmopen-aiteast.component.html | 29 ------------------- .../hrmopen-aiteast.component.ts | 14 ++++----- 2 files changed, 7 insertions(+), 36 deletions(-) diff --git a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.html b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.html index 000a4a9..d075b1c 100644 --- a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.html +++ b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.html @@ -17,32 +17,7 @@ <span *ngIf="item.isChart" class="chart-icon">📊</span> </li> </ul> - <!--<div class="flex justify-content-between align-items-center"> - <div class="flex p-gap-2 align-items-center"> - <kendo-dropdownlist - [data]="reportGroups" - [defaultItem]="{ description: 'Select AI Purpose....', value: null }" - [textField]="'description'" - [valueField]="'value'" - [valuePrimitive]="true" - [(ngModel)]="selectedReportGroup" - (valueChange)="onReportGroupChange($event)" - class="w-14rem"> - </kendo-dropdownlist> - <button (click)="openAvailableInfoDialog()" [disabled]="selectedReportGroup === null || selectedReportGroup === undefined" kendoButton themeColor="secondary">Field Column</button> - </div> - <div class="flex p-gap-2 align-items-center"> - <button [disabled]="selectedReportGroup === null || selectedReportGroup === undefined" - kendoButton themeColor="primary" - (click)="ShowAllQueryLog()">Chat History</button> - <button kendoButton themeColor="primary" - (click)="onCallOpenAI_One()" - (keydown.enter)="handleEnterKey($event)">Execute</button> - </div> - </div>--> <div class="toolbar-container"> - - <!-- Left Side --> <div class="toolbar-left"> <kendo-dropdownlist [data]="reportGroups" [defaultItem]="{ description: 'Select AI Purpose....', value: null }" @@ -57,20 +32,16 @@ Field Column </button> </div> - - <!-- Right Side --> <div class="toolbar-right"> <button [disabled]="selectedReportGroup === null || selectedReportGroup === undefined" kendoButton themeColor="primary" (click)="ShowAllQueryLog()"> Chat History </button> - <button kendoButton themeColor="primary" (click)="onCallOpenAI_One()" (keydown.enter)="handleEnterKey($event)"> Execute </button> </div> - </div> </div> diff --git a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts index 58a031a..cf7c092 100644 --- a/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts +++ b/HRM.UI/ClientApp/src/app/hrmopen-ai/hrmopen-ai-test/hrmopen-aiteast.component.ts @@ -122,7 +122,7 @@ export class HRMOpenAITestComponent implements OnInit{ } LoadCurrentUserAllQueryLog(reportGroupType : EnumReportGroupType){ - this.loadingpanelService.ShowLoadingPanel = false; + this.loadingpanelService.ShowLoadingPanel = true; this.opeAiService.GetCurrentUserAllQueryLog(reportGroupType).subscribe( (resp: any) => { this.currentUserAllQueryLog = resp; @@ -220,7 +220,7 @@ export class HRMOpenAITestComponent implements OnInit{ return; } this.isUserPromptIsNotRelated = false; - this.loadingpanelService.ShowLoadingPanel = false; + this.loadingpanelService.ShowLoadingPanel = true; this.opeAiService.AskOpenAI(data).subscribe( (resp: any) => { this.OpenAIResponce = JSON.parse(resp); @@ -412,11 +412,11 @@ export class HRMOpenAITestComponent implements OnInit{ this.reportColumnDefinition = resp as any[]; }, (err: any) => { - this.loadingpanelService.ShowLoadingPanel = true; + this.loadingpanelService.ShowLoadingPanel = false; this.notificationService.showError(err.error, "Error"); }, () => { - this.loadingpanelService.ShowLoadingPanel = true; + this.loadingpanelService.ShowLoadingPanel = false; this.gridData = []; if (this.reportColumnDefinition && this.reportColumnDefinition.length > 0) { const excludedNormalizedCaptions = new Set(['employeeid']); @@ -481,12 +481,12 @@ export class HRMOpenAITestComponent implements OnInit{ }, (err: any) => { this.isShowCurrentUserAllQueryLog = false - this.loadingpanelService.ShowLoadingPanel = true; + this.loadingpanelService.ShowLoadingPanel = false; this.notificationService.showError(err.error, "Error"); }, () => { this.isShowCurrentUserAllQueryLog = false - this.loadingpanelService.ShowLoadingPanel = true; + this.loadingpanelService.ShowLoadingPanel = false; this.ProcessOpenAIData(); this.LoadCurrentUserAllQueryLog(this.selectedReportGroup); }); @@ -549,7 +549,7 @@ export class HRMOpenAITestComponent implements OnInit{ const data = { oQueryLog : this.currentChat } - this.loadingpanelService.ShowLoadingPanel = false; + this.loadingpanelService.ShowLoadingPanel = true; this.opeAiService.ChatShareWithOthers(data).subscribe( (resp: any) => { },