From 4e9c63c083753b4b461c4f7baa2f20263d8fe1dc Mon Sep 17 00:00:00 2001 From: HAFEEZA <161430887+hafe123@users.noreply.github.com> Date: Fri, 16 May 2025 16:41:17 +0400 Subject: [PATCH 1/9] Update SupportTicketController.cs --- Controllers/SupportTicketController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Controllers/SupportTicketController.cs b/Controllers/SupportTicketController.cs index fb3633d46..7876a5520 100644 --- a/Controllers/SupportTicketController.cs +++ b/Controllers/SupportTicketController.cs @@ -377,3 +377,4 @@ public void Comment(string tktno, string cmnt) } } + From 9b3db62f317b1757e876c839fb03a8e07cc10e78 Mon Sep 17 00:00:00 2001 From: hafe123 Date: Mon, 19 May 2025 16:38:05 +0400 Subject: [PATCH 2/9] Db client and support ticket related --- Controllers/DbClientController.cs | 219 +++++-- Controllers/SupportTicketController.cs | 224 ++++--- Views/DbClient/DbClient.cshtml | 10 +- .../DBClient/DbClientComponent.cshtml | 556 +++++++++++++++--- .../Components/ObjectDashboard/Default.cshtml | 6 +- .../PageHeaderCommon/Default.cshtml | 3 + Views/Shared/TicketRisingWindow.cshtml | 97 ++- 7 files changed, 872 insertions(+), 243 deletions(-) diff --git a/Controllers/DbClientController.cs b/Controllers/DbClientController.cs index 4d0213ee9..3887ca8be 100644 --- a/Controllers/DbClientController.cs +++ b/Controllers/DbClientController.cs @@ -26,7 +26,7 @@ public IActionResult DbClient(string clientSolnid) { try { - if (ViewBag.wc != RoutingConstants.DC) + if (ViewBag.wc == RoutingConstants.UC || ViewBag.wc == RoutingConstants.TC) return Redirect("/StatusCode/401"); //GetDbTablesResponse res = null; //if (ViewBag.cid == "admin" && this.LoggedInUser.Roles.Contains(SystemRoles.SolutionOwner.ToString()) || this.LoggedInUser.Roles.Contains(SystemRoles.SolutionAdmin.ToString())) @@ -58,9 +58,6 @@ public IActionResult DbClient(string clientSolnid) [HttpPost] public IActionResult DbClientt() { - if (ViewBag.wc != RoutingConstants.DC) - return Redirect("/StatusCode/401"); - GetDbTablesResponse res = null; if (ViewBag.cid == "admin") if (this.LoggedInUser.Roles.Contains(SystemRoles.SolutionOwner.ToString()) || this.LoggedInUser.Roles.Contains(SystemRoles.SolutionAdmin.ToString())) @@ -74,21 +71,21 @@ public IActionResult DbClientt() public IActionResult SearchSolution(string clientSolnid) { - if (ViewBag.wc != RoutingConstants.DC) + if (ViewBag.wc == RoutingConstants.UC || ViewBag.wc == RoutingConstants.TC) return Redirect("/StatusCode/401"); var user = this.LoggedInUser; return ViewComponent("DBClient", new { clientSolnid = clientSolnid, _user = user }); } public List ExecuteQuery(string Query, string solution, bool Isadmin) - { + { // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + throw new UnauthorizedAccessException("Unauthorized access"); + } solutionid = solution; IsAdmin = Isadmin; List responses = new List(); - - if (ViewBag.wc != RoutingConstants.DC) - return responses; - string[] QueryList = Query.Split(";"); try { @@ -96,13 +93,6 @@ public List ExecuteQuery(string Query, string solution, b { if (SplitQuery != string.Empty) { - if (Query.IndexOf("delete ", StringComparison.OrdinalIgnoreCase) >= 0 || - Query.IndexOf("drop ", StringComparison.OrdinalIgnoreCase) >= 0 || - Query.IndexOf("truncate ", StringComparison.OrdinalIgnoreCase) >= 0) - { - break; - } - if (Query.IndexOf("insert into ", StringComparison.OrdinalIgnoreCase) >= 0) { DbClientQueryResponse ress = new DbClientQueryResponse(); @@ -111,19 +101,37 @@ public List ExecuteQuery(string Query, string solution, b } else if (Query.IndexOf("select ", StringComparison.OrdinalIgnoreCase) >= 0) { - if (Query.IndexOf("eb_", StringComparison.OrdinalIgnoreCase) < 0 || IsAdmin) + // if (Query.IndexOf("eb_", StringComparison.OrdinalIgnoreCase) < 0 || IsAdmin) { DbClientQueryResponse ress = new DbClientQueryResponse(); ress = SelectQuery(SplitQuery); responses.Add(ress); } } + else if (Query.IndexOf("delete ", StringComparison.OrdinalIgnoreCase) >= 0) + { + DbClientQueryResponse ress = new DbClientQueryResponse(); + ress = DeleteQuery(SplitQuery); + responses.Add(ress); + } + else if (Query.IndexOf("drop ", StringComparison.OrdinalIgnoreCase) >= 0) + { + DbClientQueryResponse ress = new DbClientQueryResponse(); + ress = DropQuery(SplitQuery); + responses.Add(ress); + } else if (Query.IndexOf("alter table ", StringComparison.OrdinalIgnoreCase) >= 0) { DbClientQueryResponse ress = new DbClientQueryResponse(); ress = AlterQuery(SplitQuery); responses.Add(ress); } + else if (Query.IndexOf("truncate ", StringComparison.OrdinalIgnoreCase) >= 0) + { + DbClientQueryResponse ress = new DbClientQueryResponse(); + ress = TruncateQuery(SplitQuery); + responses.Add(ress); + } else if (Query.IndexOf("update ", StringComparison.OrdinalIgnoreCase) >= 0) { DbClientQueryResponse ress = new DbClientQueryResponse(); @@ -136,6 +144,7 @@ public List ExecuteQuery(string Query, string solution, b ress = CreateQuery(SplitQuery); responses.Add(ress); } + else { } } } } @@ -146,11 +155,20 @@ public List ExecuteQuery(string Query, string solution, b return responses; } + + + + + + + public DbClientQueryResponse SelectQuery(string Query) - { - DbClientQueryResponse ress = new DbClientQueryResponse(); + { // Check if the user is a developer if (ViewBag.wc != RoutingConstants.DC) - return ress; + { + throw new UnauthorizedAccessException("Unauthorized access"); + } + DbClientQueryResponse ress = new DbClientQueryResponse(); //bool containsSearchResult = Query.Contains("select"); ress = this.ServiceClient.Post(new DbClientSelectRequest { Query = Query, ClientSolnid = solutionid, IsAdminOwn = IsAdmin }); if (ress.Dataset != null) @@ -166,54 +184,187 @@ public DbClientQueryResponse SelectQuery(string Query) return ress; } - private DbClientQueryResponse InsertQuery(string Query) + + + + public ActionResult CreateIndex(string tableName, string indexName, string indexColumns) + { // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + return Unauthorized(); + } + + // Create the request object + DbClientIndexRequest request = new DbClientIndexRequest + { + TableName = tableName, + IndexName = indexName, + IndexColumns = indexColumns, + ClientSolnid = solutionid, + IsAdminOwn = IsAdmin + }; + + // Execute the request + DbClientIndexResponse response = this.ServiceClient.Post(request); + + // Return the appropriate ActionResult + if (response.Result == 0) // Assuming 0 indicates success, adjust as needed + { + return Json(new { success = true, message = "Index created successfully." }); + } + else + { + return Json(new { success = false, message = "Failed to create index.", error = response.Message }); + } + } + + + [HttpPost] + public ActionResult EditIndexName(string currentIndexName, string newIndexName, string tableName) + { // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + return Unauthorized(); + } + + // Create the request object + DbClientEditIndexRequest request = new DbClientEditIndexRequest + { + CurrentIndexName = currentIndexName, + NewIndexName = newIndexName, + TableName = tableName, + ClientSolnid = solutionid, + IsAdminOwn = IsAdmin + }; + + // Execute the request + DbClientEditIndexResponse response = this.ServiceClient.Post(request); + + // Return the appropriate ActionResult + if (response.Result == 0) // Assuming 0 indicates success, adjust as needed + { + return Json(new { success = true, message = "Index name updated successfully." }); + } + else + { + return Json(new { success = false, message = "Failed to update index name.", error = response.Message }); + } + } + + + + [HttpPost] + public ActionResult CreateConstraint(string tableName, string columnName, string constraintType, string constraintName) { + // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + return Unauthorized(); + } + + // Create the request object + DbClientConstraintRequest request = new DbClientConstraintRequest + { + TableName = tableName, + ColumnName = columnName, + ConstraintType = constraintType, + ConstraintName = constraintName, + ClientSolnid = solutionid, + IsAdminOwn = IsAdmin + }; + + // Execute the request + DbClientConstraintResponse response = this.ServiceClient.Post(request); + + // Ensure the message consistency + if (response.Message == "Constraint created successfully") + { + return Json(new { success = true, message = response.Message }); + } + else + { + string errorMessage = response.Message.StartsWith("Error: ") ? response.Message : "Failed to create constraint: " + response.Message; + return Json(new { success = false, message = errorMessage }); + } + } + + + public DbClientQueryResponse InsertQuery(string Query) + { // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + throw new UnauthorizedAccessException("Unauthorized access"); + } DbClientQueryResponse ress = new DbClientQueryResponse(); ress = this.ServiceClient.Post(new DbClientInsertRequest { Query = Query, ClientSolnid = solutionid, IsAdminOwn = IsAdmin }); return ress; } - private DbClientQueryResponse DropQuery(string Query) - { + public DbClientQueryResponse DropQuery(string Query) + { // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + throw new UnauthorizedAccessException("Unauthorized access"); + } DbClientQueryResponse ress = new DbClientQueryResponse(); return ress; } - private DbClientQueryResponse DeleteQuery(string Query) - { + public DbClientQueryResponse DeleteQuery(string Query) + { // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + throw new UnauthorizedAccessException("Unauthorized access"); + } DbClientQueryResponse ress = new DbClientQueryResponse(); ress = this.ServiceClient.Post(new DbClientDeleteRequest { Query = Query, ClientSolnid = solutionid, IsAdminOwn = IsAdmin }); return ress; } - private DbClientQueryResponse AlterQuery(string Query) - { + public DbClientQueryResponse AlterQuery(string Query) + { // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + throw new UnauthorizedAccessException("Unauthorized access"); + } DbClientQueryResponse ress = new DbClientQueryResponse(); ress = this.ServiceClient.Post(new DbClientAlterRequest { Query = Query, ClientSolnid = solutionid, IsAdminOwn = IsAdmin }); return ress; } private DbClientQueryResponse CreateQuery(string Query) - { + { // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + throw new UnauthorizedAccessException("Unauthorized access"); + } DbClientQueryResponse ress = new DbClientQueryResponse(); ress = this.ServiceClient.Post(new DbClientCreateRequest { Query = Query, ClientSolnid = solutionid, IsAdminOwn = IsAdmin }); return ress; } - private DbClientQueryResponse TruncateQuery(string Query) - { + public DbClientQueryResponse TruncateQuery(string Query) + { // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + throw new UnauthorizedAccessException("Unauthorized access"); + } DbClientQueryResponse ress = new DbClientQueryResponse(); return ress; } - private DbClientQueryResponse UpdateQuery(string Query) - { + public DbClientQueryResponse UpdateQuery(string Query) + { // Check if the user is a developer + if (ViewBag.wc != RoutingConstants.DC) + { + throw new UnauthorizedAccessException("Unauthorized access"); + } DbClientQueryResponse ress = new DbClientQueryResponse(); ress = this.ServiceClient.Post(new DbClientUpdateRequest { Query = Query, ClientSolnid = solutionid, IsAdminOwn = IsAdmin }); return ress; } - private DVColumnCollection ConvertColumns(ColumnColletion __columns) + public DVColumnCollection ConvertColumns(ColumnColletion __columns) { DVColumnCollection Columns = new DVColumnCollection(); foreach (EbDataColumn column in __columns) diff --git a/Controllers/SupportTicketController.cs b/Controllers/SupportTicketController.cs index fb3633d46..49080a457 100644 --- a/Controllers/SupportTicketController.cs +++ b/Controllers/SupportTicketController.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Http; using System.Collections.Specialized; using System.IO; +using ExpressBase.Common.LocationNSolution; // For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 @@ -48,21 +49,42 @@ public IActionResult BugTickets() FetchSupportResponse fsr = this.ServiceClient.Post(new FetchSupportRequest()); // Debugging: Log the count of tickets fetched - if (fsr.supporttkt != null) + Console.WriteLine($"Active Tickets Count: {fsr.ActiveTicket?.Count ?? 0}"); + Console.WriteLine($"Closed Tickets Count: {fsr.ClosedTicket?.Count ?? 0}"); + + // Return both Active and Closed tickets as JSON + var result = new + { + ActiveTickets = fsr.ActiveTicket ?? new List(), + ClosedTickets = fsr.ClosedTicket ?? new List() + }; + + return Json(result); + } + [HttpGet] + public IActionResult GetTicketById(string ticketId) + { + if (string.IsNullOrWhiteSpace(ticketId)) { - Console.WriteLine($"Fetched Tickets Count: {fsr.supporttkt.Count}"); + return BadRequest("Ticket ID is required."); + } + + // Call service to get the ticket details + var request = new GetTicketByIdRequest { TicketId = ticketId }; + GetTicketByIdResponse response = this.ServiceClient.Get(request); + + if (response?.Ticket != null) + { + return Json(response.Ticket); } else { - Console.WriteLine("No tickets found in the response."); + return NotFound("Ticket not found."); } - - // Directly return all tickets as JSON response - // No filtering is done here - return Json(fsr.supporttkt ?? new List()); } + public IActionResult EditTicket(string tktno) { if (ViewBag.wc.Equals("tc")) @@ -184,7 +206,7 @@ public void SaveBugDetails(string title, string stats, string descp, string prio } [HttpPost] - public SubmitTicketResponse SubmitTicketDetails(string title, string stats, string descp, string priority, string solid, string type, object fileCollection) + public SubmitTicketResponse SubmitTicketDetails(string title, string stats, string descp, string priority, string solid, string type) { var stresponse = new SubmitTicketResponse(); string usrtyp = null; @@ -214,43 +236,41 @@ public SubmitTicketResponse SubmitTicketDetails(string title, string stats, stri // File upload part if (httpreq.Files.Count > 0) { - byte[] fileData = null; + Console.WriteLine($"Total files received: {httpreq.Files.Count}"); - for (int i = 0; i < httpreq.Files.Count; i++) + foreach (var file in httpreq.Files) { - var file = httpreq.Files[i]; - if ((file.ContentType == "image/jpeg") || (file.ContentType == "image/jpg") || (file.ContentType == "image/png") || (file.ContentType == "application/pdf")) + Console.WriteLine($"Received File: {file.FileName} - {file.ContentType} - {file.Length} bytes"); + + if (file != null && file.Length > 0) { - if (file.Length < 2097152) // File size limit: 2MB + FileUploadCls flup = new FileUploadCls(); + using (var memoryStream = new MemoryStream()) { - FileUploadCls flup = new FileUploadCls(); - using (var memoryStream = new MemoryStream()) - { - file.CopyTo(memoryStream); - memoryStream.Seek(0, SeekOrigin.Begin); - fileData = new byte[memoryStream.Length]; - memoryStream.ReadAsync(fileData, 0, fileData.Length); - flup.Filecollection = fileData; - } - flup.FileName = file.FileName; - flup.ContentType = file.ContentType; - strequest.Fileuploadlst.Add(flup); + file.CopyTo(memoryStream); + flup.Filecollection = memoryStream.ToArray(); } + flup.FileName = file.FileName; + flup.ContentType = file.ContentType; + strequest.Fileuploadlst.Add(flup); } } } - // Populate the SubmitTicketRequest object with form data strequest.title = httpreq["title"].ToString(); strequest.description = httpreq["description"].ToString(); strequest.priority = httpreq["priority"].ToString(); - strequest.solutionid = solid; - strequest.type_b_f = httpreq["type"].ToString(); - strequest.status = httpreq["stats"].ToString(); + strequest.solutionid = solid; // Use the 'solid' parameter + strequest.type_b_f = httpreq["type_f_b"].ToString(); // Use type_f_b from js + strequest.status = httpreq["status"].ToString(); strequest.usertype = usrtyp; strequest.fullname = this.LoggedInUser.FullName; strequest.email = this.LoggedInUser.Email; + strequest.onBehalfOf = httpreq.ContainsKey("onBehalfOf") && int.TryParse(httpreq["onBehalfOf"], out int onBehalfValue) + ? onBehalfValue + : 0; // Default to 0 if no valid ID is provided + // Post the ticket request to the service stresponse = this.ServiceClient.Post(strequest); @@ -281,58 +301,110 @@ public SubmitTicketResponse SubmitTicketDetails(string title, string stats, stri return stresponse; } - public void UpdateTicket(string filedelet, string solu_id, string tktid, string updtkt) + [HttpGet] + public Dictionary GetUsers(string cid) { + + Eb_Solution s_obj = GetSolutionObject(cid); // Retrieve solution object + if (s_obj != null && s_obj.Users != null) + { + // var usersList = s_obj.Users.Select(user => new + // { + // Id = user.UserId, // Adjust according to actual user properties + // Name = user.UserName + // }).ToList(); + + // return Ok(usersList); + return s_obj.Users; + } + return null; + + + } - UpdateTicketRequest Uptkt = new UpdateTicketRequest(); - var httpreq = this.HttpContext.Request.Form; - Dictionary chngtkt = JsonConvert.DeserializeObject>(updtkt); - if (httpreq.Files.Count > 0) + + [HttpPost] + public IActionResult UpdateTicket() + { + try { - byte[] fileData = null; + var httpRequest = HttpContext.Request.Form; + UpdateTicketRequest uptkt = new UpdateTicketRequest + { + Fileuploadlst = new List() + }; - for (int i = 0; i < httpreq.Files.Count; i++) + // Check required fields + if (string.IsNullOrEmpty(httpRequest["updtkt"])) { + return Json(new { errorMessage = "Error: 'updtkt' field is missing in the request." }); + } + if (string.IsNullOrEmpty(httpRequest["ticketId"])) + { + return Json(new { errorMessage = "Error: 'ticketId' field is missing in the request." }); + } + if (string.IsNullOrEmpty(httpRequest["solu_id"])) + { + return Json(new { errorMessage = "Error: 'solu_id' field is missing in the request." }); + } - var file = httpreq.Files[i]; - if ((file.ContentType == "image/jpeg") || (file.ContentType == "image/jpg") || (file.ContentType == "image/png") || (file.ContentType == "application/pdf")) + // Deserialize JSON input + Dictionary changedTkt = JsonConvert.DeserializeObject>(httpRequest["updtkt"]); + uptkt.chngedtkt = changedTkt; + uptkt.ticketid = httpRequest["ticketId"]; + uptkt.solution_id = httpRequest["solu_id"]; + uptkt.usrname = this.LoggedInUser?.FullName ?? "Unknown User"; + + // Process uploaded files + foreach (var file in httpRequest.Files) + { + if (file.Length < 2097152) // Keep size restriction (2MB) { - if (file.Length < 2097152) + using (var memoryStream = new MemoryStream()) { - FileUploadCls flup = new FileUploadCls(); - using (var memoryStream = new MemoryStream()) + file.CopyTo(memoryStream); + byte[] fileData = memoryStream.ToArray(); + + uptkt.Fileuploadlst.Add(new FileUploadCls { - file.CopyTo(memoryStream); - memoryStream.Seek(0, SeekOrigin.Begin); - fileData = new byte[memoryStream.Length]; - memoryStream.ReadAsync(fileData, 0, fileData.Length); - flup.Filecollection = fileData; - } - flup.FileName = file.FileName; - flup.ContentType = file.ContentType; - Uptkt.Fileuploadlst.Add(flup); + Filecollection = fileData, + FileName = file.FileName, + ContentType = file.ContentType + }); } } } - } - Uptkt.Filedel = JsonConvert.DeserializeObject(filedelet); - Uptkt.usrname = this.LoggedInUser.FullName; - Uptkt.chngedtkt = chngtkt; - Uptkt.ticketid = tktid; - if (this.LoggedInUser.wc.Equals("tc")) - { - Uptkt.solution_id = solu_id; + + // Debugging logs + Console.WriteLine("Ticket update request received:"); + Console.WriteLine($"Ticket ID: {uptkt.ticketid}"); + Console.WriteLine($"Solution ID: {uptkt.solution_id}"); + Console.WriteLine($"Username: {uptkt.usrname}"); + Console.WriteLine($"Changes: {JsonConvert.SerializeObject(uptkt.chngedtkt)}"); + Console.WriteLine($"Attached Files: {uptkt.Fileuploadlst.Count}"); + + // Call service + UpdateTicketResponse upr = this.ServiceClient.Post(uptkt); + + if (upr.status) + { + return Json(new { successMessage = "Ticket updated successfully" }); + } + else + { + return Json(new { errorMessage = "Failed to update ticket" }); + } } - else + catch (Exception ex) { - Uptkt.solution_id = ViewBag.cid; + return Json(new { errorMessage = $"Error: {ex.Message}" }); } - UpdateTicketResponse upr = this.ServiceClient.Post(Uptkt); - } + + public void UpdateTicketAdmin(string updtkt, string tktid, string solid) { @@ -362,18 +434,36 @@ public void ChangeStatus(string tktno, string reason) } - public void Comment(string tktno, string cmnt) + [HttpPost] + public IActionResult Comment(string TicketNo, string Comments, string UserName, string Solution_id) { CommentResponse Cr = this.ServiceClient.Post(new CommentRequest { - TicketNo = tktno, - Comments = cmnt, - UserName = this.LoggedInUser.FullName, - Solution_id = ViewBag.cid + TicketNo = TicketNo, + Comments = Comments, + UserName = UserName, + Solution_id = Solution_id }); + return Json(Cr); + } + + + [HttpGet] + public IActionResult CommentsByTicket(string tktno) + { + var response = this.ServiceClient.Post( + new CommentListRequest + { + TicketNo = tktno + }); + return Json(response); } + + + + } } diff --git a/Views/DbClient/DbClient.cshtml b/Views/DbClient/DbClient.cshtml index 69c1b6fb6..e9b91ad7e 100644 --- a/Views/DbClient/DbClient.cshtml +++ b/Views/DbClient/DbClient.cshtml @@ -8,15 +8,15 @@ @await Component.InvokeAsync("PageHeaderCommon"); @**@ - + diff --git a/Views/Shared/Components/DBClient/DbClientComponent.cshtml b/Views/Shared/Components/DBClient/DbClientComponent.cshtml index 020bd360a..6fa048d0a 100644 --- a/Views/Shared/Components/DBClient/DbClientComponent.cshtml +++ b/Views/Shared/Components/DBClient/DbClientComponent.cshtml @@ -1,10 +1,21 @@ @using ExpressBase.Objects.ServiceStack_Artifacts; @using Newtonsoft.Json; + + +@using System.Collections.Generic; +@using System.Linq; + + @{ if (ViewBag.Message == null) { @@ -32,111 +43,494 @@
- @ViewBag.DB_Name + @ViewBag.DB_Name
-
- Tables ( @ViewBag.TableCount ) - @{ foreach (KeyValuePair Table_kvp in ViewBag.Tables.TableCollection) + Schemas + @{ + // Initialize a dictionary to group tables by schema + var schemaGroups = new Dictionary>>(); + var tableCollection = ViewBag.Tables.TableCollection as Dictionary; + + // Group tables by schema + foreach (var table_kvp in tableCollection) { -
- @* - *@ - @{ - EbDbExplorerTable table = Table_kvp.Value; - string schema = table.Schema; - List Indexes = table.Index; - List Columns = table.Columns; - - @Table_kvp.Key - -
- Schema ( @schema ) -
-
- Index (@Indexes.Count()) - @foreach (string Index in Indexes) - { -
@Index
- } -
-
- Column (@Columns.Count()) - @foreach (EbDbExplorerColumn Column in Columns) - { + var schema = table_kvp.Value.Schema; + if (!schemaGroups.ContainsKey(schema)) + { + schemaGroups[schema] = new List>(); + } + schemaGroups[schema].Add(table_kvp); + } + + // Get function collection + var functions = ViewBag.Tables.FunctionCollection as List ?? new List(); + + // Render the schema groups with tables and functions + foreach (var schemaGroup in schemaGroups) + { + var schema = schemaGroup.Key; + var tables = schemaGroup.Value; +
+ Schema: @schema +
+ Tables (@tables.Count) + @foreach (var Table_kvp in tables) + { +
+ @{ + EbDbExplorerTable table = Table_kvp.Value; + List Indexes = table.Index; + List Columns = table.Columns; + int constraintCount = Columns.Count(c => !string.IsNullOrEmpty(c.ColumnKey) && !string.IsNullOrEmpty(c.ConstraintName)); + + } + + @Table_kvp.Key + +
+ Indexes (@Indexes.Count) + @foreach (string Index in Indexes) + { +
+ @Index +
+ } +
+ +
- @{ if (Column.ColumnKey == "Primary key") - { } - else if (Column.ColumnKey == "Foreign key") - { } - if (Column.ColumnType == "integer" || Column.ColumnType == "numeric") - { - - } - else if (Column.ColumnType == "text" || Column.ColumnType == "character") - { - - } - if (Column.ColumnType == "timestamp without time zone") + + Columns (@Columns.Count) + + @foreach (var Column in Columns.OrderBy(c => c.ColumnName)) + { +
+ + @{ + if (Column.ColumnKey == "Primary key") + { + + } + else if (Column.ColumnKey == "Foreign key") + { + + } + + string iconClass = null; + if (Column.ColumnType == "integer") + { + iconClass = "fa fa-sort-numeric-asc"; + } + else if (Column.ColumnType == "numeric") + { + iconClass = "fa fa-hashtag"; + } + else if (Column.ColumnType == "text") + { + iconClass = "fa fa-font"; + } + else if (Column.ColumnType == "character") + { + iconClass = "fa fa-quote-left"; + } + else if (Column.ColumnType == "boolean") + { + iconClass = "fa fa-check-square"; + } + else if (Column.ColumnType == "character varying") + { + iconClass = "fa fa-text-width"; + } + else if (Column.ColumnType == "smallint") + { + iconClass = "fa fa-sort-amount-down"; + } + else if (Column.ColumnType == "bigint") + { + iconClass = "fa fa-sort-amount-up"; + } + else if (Column.ColumnType == "date") + { + iconClass = "fa fa-calendar-day"; + } + else if (Column.ColumnType == "timestamp without time zone") + { + iconClass = "fa fa-calendar"; + } + + if (iconClass != null) + { + + } + } + + @Column.ColumnName @Column.ColumnTable + +
+ } +
+ +
+ + Constraints (@constraintCount) + + @foreach (EbDbExplorerColumn Column in Columns) + { + if (!string.IsNullOrEmpty(Column.ColumnKey) && !string.IsNullOrEmpty(Column.ConstraintName)) { - +
+ + + @{ + if (Column.ColumnKey == "Primary key") + { + + } + else if (Column.ColumnKey == "Foreign key") + { + + } + else if (Column.ColumnKey == "Unique key") + { + + } + } + + @Column.ConstraintName (@Column.ColumnKey) + +
} - } @Column.ColumnName @Column.ColumnTable + }
- } -
- } -
- } - } -
-
- @{ List Functions = ViewBag.Tables.FunctionCollection; - Functions ( @Functions.Count() ) - foreach (EbDbExplorerFunctions Function in Functions) - { -
- @{ - @Function.FunctionName +
+ } +
+
+ @{ + List Functions = ViewBag.Tables.FunctionCollection; + } + + Functions (@Functions.Count) + + @foreach (var function in Functions) + { +
+ + @function.FunctionName + +
+ } +
-
- @Function.FunctionQuery -
- }
} }
+
+ + + + + + + + + + + } else { - - + + } } diff --git a/Views/Shared/Components/ObjectDashboard/Default.cshtml b/Views/Shared/Components/ObjectDashboard/Default.cshtml index b395bbd7b..54cc7d77a 100644 --- a/Views/Shared/Components/ObjectDashboard/Default.cshtml +++ b/Views/Shared/Components/ObjectDashboard/Default.cshtml @@ -30,6 +30,9 @@ @*dona*@ + + +
@@ -151,11 +154,12 @@ } if ((ViewBag.wc == "uc" || ViewBag.wc == "dc") && (ViewBag.Env != "Production" || ViewBag.email == "support@expressbase.com")) { - } + }
diff --git a/Views/Shared/Components/PageHeaderCommon/Default.cshtml b/Views/Shared/Components/PageHeaderCommon/Default.cshtml index 533cc9ee6..a61fa3494 100644 --- a/Views/Shared/Components/PageHeaderCommon/Default.cshtml +++ b/Views/Shared/Components/PageHeaderCommon/Default.cshtml @@ -3,6 +3,9 @@ @using Newtonsoft.Json; @using ExpressBase.Security; @using ExpressBase.Common.Helpers; + + + @{ string Logourl = "/images/logo/" + ViewBag.cid + ".png"; diff --git a/Views/Shared/TicketRisingWindow.cshtml b/Views/Shared/TicketRisingWindow.cshtml index 9d45f6d42..549e4fb02 100644 --- a/Views/Shared/TicketRisingWindow.cshtml +++ b/Views/Shared/TicketRisingWindow.cshtml @@ -1,6 +1,6 @@  -
+
@@ -32,7 +32,7 @@
-
-
+
@@ -166,7 +166,7 @@
-
+ -
+
@@ -196,11 +197,20 @@
- +
+
- +
@@ -208,12 +218,12 @@
+ style="width: 85px; padding: 5px; font-size: 14px;">
- +
@@ -221,7 +231,7 @@
+ style="width: 85px; padding: 5px; font-size: 14px;">
@@ -251,11 +261,10 @@
- - + - +
@@ -263,6 +272,7 @@ +
@@ -326,16 +336,17 @@
- @* *@ - @*
*@ - @*
*@ - @* *@ - @* *@ - @*
*@ - @*
*@ - @* *@ +
+ +
+
+ + +
+
+
@@ -362,7 +373,9 @@
+
+ From af2319c4f16f07f4b671a556ab34b0dd925a5e00 Mon Sep 17 00:00:00 2001 From: hafe123 Date: Thu, 29 May 2025 10:32:28 +0400 Subject: [PATCH 3/9] ticket & db-client update --- Controllers/SupportTicketController.cs | 7 +- Views/Shared/TicketRisingWindow.cshtml | 7 +- wwwroot/css/DBExplorerStyles.css | 301 +++-- .../css/EbCommonStyles/ObjectDashboard.css | 31 +- wwwroot/js/Common/eb-setup.js | 7 + wwwroot/js/Common/eb-ticket.js | 1180 ++++++++++++----- wwwroot/js/DBExplorer.js | 555 ++++++-- 7 files changed, 1485 insertions(+), 603 deletions(-) diff --git a/Controllers/SupportTicketController.cs b/Controllers/SupportTicketController.cs index b7bdcab87..3620182b1 100644 --- a/Controllers/SupportTicketController.cs +++ b/Controllers/SupportTicketController.cs @@ -435,14 +435,17 @@ public void ChangeStatus(string tktno, string reason) } [HttpPost] - public IActionResult Comment(string TicketNo, string Comments, string UserName, string Solution_id) + public IActionResult Comment(string TicketNo, string Comments, string UserName, string Solution_id,string currentUserid) { CommentResponse Cr = this.ServiceClient.Post(new CommentRequest { TicketNo = TicketNo, Comments = Comments, UserName = UserName, - Solution_id = Solution_id + Solution_id = Solution_id, + currentUserid=currentUserid + + }); return Json(Cr); diff --git a/Views/Shared/TicketRisingWindow.cshtml b/Views/Shared/TicketRisingWindow.cshtml index 549e4fb02..df5f63571 100644 --- a/Views/Shared/TicketRisingWindow.cshtml +++ b/Views/Shared/TicketRisingWindow.cshtml @@ -147,6 +147,8 @@ Solution ID:
@ViewBag.Cid
+ uid @ViewBag.Uid +
@@ -180,7 +182,10 @@
  • - Comments + Comments +
  • diff --git a/wwwroot/css/DBExplorerStyles.css b/wwwroot/css/DBExplorerStyles.css index 5ec6ffa4b..9feae7405 100644 --- a/wwwroot/css/DBExplorerStyles.css +++ b/wwwroot/css/DBExplorerStyles.css @@ -33,30 +33,30 @@ div .mytree div { display: none; cursor: pointer; } - .treecontextmenu:hover { - border: solid 1px #d5d5d5; - } - - div.mytree div.parent { - cursor: pointer; - background: transparent /*url(http://www3.telus.net/jianlu58/plus.gif)*/ no-repeat top left; - background-image: url('data:image/gif;base64,R0lGODlhDwANAJEAAP///2ZmZgAAAMDAwCH5BAEAAAMALAAAAAAPAA0AQAIlnI95AS0NgmIOSoVx2JxPIIRhNHVdhiIU1Fwq9LRfJScr66ZJAQA7'); - background-position-y: 4px; - } +.treecontextmenu:hover { + border: solid 1px #d5d5d5; +} +div.mytree div.parent { + cursor: pointer; + background: transparent /*url(http://www3.telus.net/jianlu58/plus.gif)*/ no-repeat top left; + background-image: url('data:image/gif;base64,R0lGODlhDwANAJEAAP///2ZmZgAAAMDAwCH5BAEAAAMALAAAAAAPAA0AQAIlnI95AS0NgmIOSoVx2JxPIIRhNHVdhiIU1Fwq9LRfJScr66ZJAQA7'); + background-position-y: 4px; +} - div.mytree div.expanded { - background: transparent /*url(http://www3.telus.net/jianlu58/minus.gif)*/ no-repeat top left; - background-image: url('data:image/gif;base64,R0lGODlhEwATAJEDAP///4CAgAAAAO7u7iH5BAEAAAMALAAAAAATABMAAAIonI+py+0PYwu0VheA3iBgvnkTqIlMJqRp95Hmkrkt+CrWLeX6zvdDAQA7'); - background-position-y: 2px; - } - div.mytree div.selected{ - background-color:#ffff; - } +div.mytree div.expanded { + background: transparent /*url(http://www3.telus.net/jianlu58/minus.gif)*/ no-repeat top left; + background-image: url('data:image/gif;base64,R0lGODlhEwATAJEDAP///4CAgAAAAO7u7iH5BAEAAAMALAAAAAATABMAAAIonI+py+0PYwu0VheA3iBgvnkTqIlMJqRp95Hmkrkt+CrWLeX6zvdDAQA7'); + background-position-y: 2px; +} - .db_client_toolbox { +div.mytree div.selected { + background-color: #ffff; +} + +.db_client_toolbox { background-color: #eee; border: 1px solid #ccc; padding: 5px; @@ -100,7 +100,6 @@ div .mytree div { } .table-box .t_drophead .tname { - max-width: 80%; text-overflow: ellipsis; white-space: nowrap; @@ -108,21 +107,21 @@ div .mytree div { display: inline-block; } .table-box .t_dropbdy { - padding: 5px; - line-height: 2.2rem; - overflow: scroll; - height: calc(100% - 25px); -} + padding: 5px; + line-height: 2.2rem; + overflow: scroll; + height: calc(100% - 25px); + } - .fa-key { +.fa-key { float: right; } - .data-type { +.data-type { font-size: smaller; } - .drag-box { +.drag-box { width: 150px; height: 150px; padding: 0.5em; @@ -133,13 +132,12 @@ div .mytree div { overflow-x: hidden; z-index: 10; } + .page_grid { background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImdyaWQiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTSAwIDEwIEwgNDAgMTAgTSAxMCAwIEwgMTAgNDAgTSAwIDIwIEwgNDAgMjAgTSAyMCAwIEwgMjAgNDAgTSAwIDMwIEwgNDAgMzAgTSAzMCAwIEwgMzAgNDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2UwZTBlMCIgb3BhY2l0eT0iMC4yIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNIDQwIDAgTCAwIDAgMCA0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIiBzdHJva2Utd2lkdGg9IjEiLz48L3BhdHRlcm4+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JpZCkiLz48L3N2Zz4="); - - } - .drop-box { +.drop-box { width: 100%; height: 546px; /*background: #d2cfcf75;*/ @@ -147,14 +145,27 @@ div .mytree div { /*padding: 10px;*/ } - .gold { +.gold { color: goldenrod; } - .fkey { +.fkey { color: #97948d; } +.Resize_toolbox { + resize: both; /* Allows resizing both horizontally and vertically */ + overflow: auto; /* Ensures the content is scrollable if it overflows */ + min-width: 150px; /* Optional: Minimum width to prevent resizing too small */ + min-height: 150px; /* Optional: Minimum height to prevent resizing too small */ + max-width: 100%; /* Optional: Maximum width to prevent overflow */ + max-height: 100vh; /* Optional: Maximum height relative to viewport */ + border: 1px solid #ccc; /* Optional: Add a border for visual indication */ + padding: 10px; /* Optional: Padding for internal content */ + box-sizing: border-box; /* Ensures padding and border are included in element's width/height */ +} + + #pannel { padding: 0px 10px 10px 10px; width: 75%; @@ -170,7 +181,7 @@ div .mytree div { } .Result_Cont { - margin-top:10px; + margin-top: 10px; overflow-y: auto; -ms-overflow-style: -ms-autohiding-scrollbar; height: 26rem; @@ -195,7 +206,9 @@ div .mytree div { top: 12px; height: 321px; /*z-index: auto;*/ -}*/ +} + +*/ .CodeMirror-wrap pre { word-wrap: break-word !important; @@ -210,6 +223,7 @@ div .mytree div { border-bottom: 4px solid #f2f2f2; /*padding: 0px 10px 10px 10px;*/ } + .nav-tabs { background-color: #f2f2f2; border-bottom: 2px solid #316396; @@ -227,7 +241,7 @@ div .mytree div { clip-path: polygon(0 0, 80% 0, 100% 100%, 0% 100%); } -.cetab{ +.cetab { background-color: transparent; width: 150px; padding: 4px 15px; @@ -239,8 +253,8 @@ div .mytree div { border-top-left-radius: 2px; } -.CodeMirror-gutters{ - z-index:auto; +.CodeMirror-gutters { + z-index: auto; } /*.tab-section-common .nav-tabs .active a { @@ -255,6 +269,7 @@ color: white; .tttab-session ul { height: 5vh; } + .tttab-session ul li { margin-top: 5px; margin-bottom: -7px; @@ -262,24 +277,28 @@ color: white; height: 100%; width: 200px; } + .tttab-session ul li a { display: flex; width: 200px; height: 100%; padding: 6px; } + .tttab-session ul li a p { font-size: 14px; width: 55%; text-align: left; padding-left: 15px; } - .tttab-session ul li a button { - padding: 6px; - height: 100%; - background: #fff; - border: solid 1px #c7c7c7; - } + + .tttab-session ul li a button { + padding: 6px; + height: 100%; + background: #fff; + border: solid 1px #c7c7c7; + } + .tttab-session ul li a i { padding: 5px; font-size: 14px; @@ -326,24 +345,25 @@ color: white; padding-left: 10px; } -.tab-content{ +.tab-content { +} +.Result_Cont { + width: 100%; } - .Result_Cont{ - width:100%; - } - .tab-content .dataTables_wrapper, .tab-content .dataTables_scroll { - height: 100%; - } - .tab-content .dataTables_scrollBody { - height: calc(74%) !important; - } +.tab-content .dataTables_wrapper, .tab-content .dataTables_scroll { + height: 100%; +} +.tab-content .dataTables_scrollBody { + height: calc(74%) !important; +} - .tab-content .dataTables_scrollHeadInner .table { - margin-top: 0px !important; - } + +.tab-content .dataTables_scrollHeadInner .table { + margin-top: 0px !important; +} .tab-content .dataTables_scrollBody::-webkit-scrollbar { @@ -351,25 +371,25 @@ color: white; height: 1.5vh; } - .tab-content .dataTables_scrollBody:hover::-webkit-scrollbar { - width: 1.5vh; - height: 1.5vh; - } +.tab-content .dataTables_scrollBody:hover::-webkit-scrollbar { + width: 1.5vh; + height: 1.5vh; +} - /* Track */ - .tab-content .dataTables_scrollBody::-webkit-scrollbar-track { - background: #f1f1f1 !important; - } +/* Track */ +.tab-content .dataTables_scrollBody::-webkit-scrollbar-track { + background: #f1f1f1 !important; +} - /* Handle */ - .tab-content .dataTables_scrollBody::-webkit-scrollbar-thumb { - background: #888 !important; - } +/* Handle */ +.tab-content .dataTables_scrollBody::-webkit-scrollbar-thumb { + background: #888 !important; +} - /* Handle on hover */ - .tab-content .dataTables_scrollBody::-webkit-scrollbar-thumb:hover { - background: #555 !important; - } + /* Handle on hover */ + .tab-content .dataTables_scrollBody::-webkit-scrollbar-thumb:hover { + background: #555 !important; + } .tab-content th { @@ -380,63 +400,63 @@ color: white; font-weight: 500; } - .tab-content .group td b { - font-weight: 400; - } +.tab-content .group td b { + font-weight: 400; +} - .tab-content td { - font-size: 16px; - text-overflow: ellipsis !important; - overflow: hidden !important; - white-space: nowrap !important; - font-size: 14px; - height: 5vh !important; - } +.tab-content td { + font-size: 16px; + text-overflow: ellipsis !important; + overflow: hidden !important; + white-space: nowrap !important; + font-size: 14px; + height: 5vh !important; +} - .tab-content tr:hover { - background-color: #00000040 !important; - } +.tab-content tr:hover { + background-color: #00000040 !important; +} + +.tab-content .group { + border-top: solid 1px #f1f1f1 !important; + background-color: #00000029 !important; +} - .tab-content .group { + .tab-content .group:hover { border-top: solid 1px #f1f1f1 !important; - background-color: #00000029 !important; + /*background-color: #00000029 !important;*/ } - .tab-content .group:hover { - border-top: solid 1px #f1f1f1 !important; - /*background-color: #00000029 !important;*/ - } - - .tab-content .group tr td { - background-color: #00000029 !important; - } - - .tab-content .group-sum td { + .tab-content .group tr td { background-color: #00000029 !important; - /*border-bottom: solid 3px #ffffff14 !important;*/ } - .tab-content tr { - background-color: transparent !important; - border-bottom: 1px !important; - border-bottom: solid #dcdcdc !important; - border-bottom-width: 1px !important; - height: 4vh !important; - } +.tab-content .group-sum td { + background-color: #00000029 !important; + /*border-bottom: solid 3px #ffffff14 !important;*/ +} - .tab-content table.dataTable thead .sorting:after { - opacity: 0.2; - content: "\e150"; - font-size: 12px; - } +.tab-content tr { + background-color: transparent !important; + border-bottom: 1px !important; + border-bottom: solid #dcdcdc !important; + border-bottom-width: 1px !important; + height: 4vh !important; +} - .tab-content .dataTables_scrollBody thead:first-child tr { - height: 0vh !important; - } +.tab-content table.dataTable thead .sorting:after { + opacity: 0.2; + content: "\e150"; + font-size: 12px; +} - .tab-content .odd { - background: rgba(0, 0, 0, 0.09) !important; - } +.tab-content .dataTables_scrollBody thead:first-child tr { + height: 0vh !important; +} + +.tab-content .odd { + background: rgba(0, 0, 0, 0.09) !important; +} .red-tooltip + .tooltip > .tooltip-inner { background-color: #f00; @@ -445,3 +465,48 @@ color: white; .red-tooltip + .tooltip > .tooltip-arrow { border-bottom-color: #f00; } + + +.constraintcontextmenu { + display: flex; /* Align items in a row */ + align-items: center; /* Center the items vertically */ + margin-bottom: 5px; /* Space between entries */ +} + +.constraint-item { + display: flex; /* Align icon and text in a row */ + align-items: center; /* Center the items vertically */ + gap: 5px; /* Space between icon and text */ +} + +.constraint-details { + white-space: nowrap; /* Prevents line breaks within the constraint details */ +} + +.fa-key { + margin-right: 5px; +} + + +.modal-fullscreen { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + max-width: 100%; +} + + .modal-fullscreen .modal-content { + height: auto; + min-height: 100%; + border-radius: 0; + } + +.functioncontextmenu .table-name { + display: flex; + align-items: center; +} + + .functioncontextmenu .table-name i { + margin-right: 5px; + } diff --git a/wwwroot/css/EbCommonStyles/ObjectDashboard.css b/wwwroot/css/EbCommonStyles/ObjectDashboard.css index ec6e08ccb..803cd5333 100644 --- a/wwwroot/css/EbCommonStyles/ObjectDashboard.css +++ b/wwwroot/css/EbCommonStyles/ObjectDashboard.css @@ -1360,6 +1360,18 @@ input:checked + .proslider:before { background: rgba(255, 255, 255, 1); } +#comments-tab { + height: calc(43vh - 80px); /* subtract footer height to fit within screen */ + overflow: hidden; +} +.tab-pane { + display: none; +} + + .tab-pane.active { + display: flex !important; /* use flex so comments layout still works */ + flex-direction: column; + } #tr-window .trcmnt_comments-container { display: flex; @@ -1372,33 +1384,29 @@ input:checked + .proslider:before { } #tr-window #comments-tab .trcmnt_form { - position: relative; /* Use relative positioning to stay above footer */ - bottom: 0; /* Align at the bottom of the parent container */ - left: 0; - right: 0; - padding: 10px; + position: sticky; + bottom: 0; background-color: #fff; + padding: 10px; border-top: 1px solid #ddd; - box-shadow: 0 -2px 5px rgba(0,0,0,0.1); - z-index: 10; /* Ensure it is above other content */ + z-index: 5; /* Ensure it is above other content */ } #tr-window #comments-tab .trcmnt_comments-display { - max-height: calc(100% - 60px); /* Adjust for footer height */ + flex-grow: 1; overflow-y: auto; padding: 10px; background-color: #f9f9f9; - margin-bottom: 10px; } #tr-window .trcmnt_input-group { display: flex; align-items: center; -} +} #tr-window .trcmnt_input { flex-grow: 1; - margin-right: 10px; + margin-right: 1rem; border-radius: 25px; border: 1px solid black; } @@ -1444,6 +1452,7 @@ input:checked + .proslider:before { background-color: #182E50; padding: 10px; display: flex; + height: 60px; /* used in calc above */ align-items: center; justify-content: space-between; z-index: 20; /* Make sure it's on top of the form */ diff --git a/wwwroot/js/Common/eb-setup.js b/wwwroot/js/Common/eb-setup.js index 325e666b1..e2a323eae 100644 --- a/wwwroot/js/Common/eb-setup.js +++ b/wwwroot/js/Common/eb-setup.js @@ -66,6 +66,13 @@ if (!this.tr_window.is(':visible')) { this.tr_fade.show(); this.tr_window.show('slide', { direction: 'right' }); + if (userContext === "dc") { + $('#create-ticket-button').hide(); + $('#create-ticket-button-closed').hide(); + } else { + $('#create-ticket-button').show(); + $('#create-ticket-button-closed').show(); + } } else { this.tr_fade.hide(); this.tr_window.hide(); diff --git a/wwwroot/js/Common/eb-ticket.js b/wwwroot/js/Common/eb-ticket.js index c689c49ee..4aec4d0e2 100644 --- a/wwwroot/js/Common/eb-ticket.js +++ b/wwwroot/js/Common/eb-ticket.js @@ -1,4 +1,17 @@ -document.addEventListener('DOMContentLoaded', function () { + +let selectedAttachments = []; // Store non-image attachments globally +let userMap = {}; // Global object to store ID -> Name mapping +let globalTicketId = null; +let lastCommentCount = 0; +let lastCommentSender = ""; +let pollingIntervalId = null; // ← track the interval globally +let currentConsoleType = "Unknown"; + + + + + +document.addEventListener('DOMContentLoaded', function () { // Define variables for different elements const activeTicketsContainer = document.getElementById('tr-container'); const closedTicketsContainer = document.getElementById('closedtr_container'); @@ -12,124 +25,360 @@ const submitTicketButton = document.getElementById('submit-ticket-button'); const confirmationMessage = document.getElementById('confirmation-message'); const viewActiveTicketButton = document.getElementById('view-active-ticket-button'); + const raisingYes = document.getElementById("tinf_raising_yes"); + const raisingNo = document.getElementById("tinf_raising_self"); + const namesDropdown = document.getElementById("tinf_names"); + let cachedUsers = null; + let currentUserName = document.getElementById('submitted-by')?.textContent.trim() || ""; + let currentUserid = document.getElementById('Uid')?.textContent.trim() || ""; + + + + + + // Function to fetch active bug tickets function getBugTickets() { - console.log("Fetching bug tickets..."); // Debugging line - $("#tr-container").html(`

    Loading...

    `); + console.log("Fetching bug tickets..."); + + $("#tr-container").html(`

    + Loading...

    `); $.ajax({ type: "GET", - url: "/SupportTicket/BugTickets", // Ensure this endpoint is correct + url: "/SupportTicket/BugTickets", // Make sure this matches your controller success: function (response) { - console.log("Fetched bug tickets:", response); // Debugging log to inspect the response + console.log("Fetched bug tickets:", response); + try { - var data = typeof response === "string" ? JSON.parse(response) : response; // Adjust parsing - onGetBugTicketsSuccess(data, true); + var data = typeof response === "string" ? JSON.parse(response) : response; + + var activeTickets = data.activeTickets || []; + var closedTickets = data.closedTickets || []; + + // You can pass both lists to your rendering function + onGetBugTicketsSuccess(activeTickets, closedTickets); + } catch (error) { - console.error("Failed to parse response:", error); // Error handling for parsing issues + console.error("Failed to parse response:", error); $("#tr-container").html(`

    Failed to load bug tickets.

    `); } }, error: function (xhr, status, error) { - console.error("Error fetching bug tickets:", status, error); // Error handling for AJAX call + console.error("Error fetching bug tickets:", status, error); $("#tr-container").html(`

    Failed to load bug tickets.

    `); } }); } + function getTicketsById(ticketId, callback) { + $.ajax({ + url: "/SupportTicket/GetTicketById", + type: "GET", + data: { ticketId: ticketId }, + success: function (response) { + if (response && response.ticketid) { + callback(response); + } else { + console.error("Ticket not found or incomplete:", response); + } + }, + error: function (xhr, status, error) { + console.error("Error fetching ticket:", status, error); + } + }); + } + + function fetchUsers() { + return new Promise((resolve, reject) => { + if (cachedUsers) { + console.log("Using cached users..."); + populateDropdown(cachedUsers); + resolve(); // ✅ immediately resolve + return; + } + + const cidElement = document.getElementById("soluid"); - // Handle successful fetching of bug tickets - function onGetBugTicketsSuccess(data, onload) { - // Check if data is valid - if (!data || !Array.isArray(data)) { - $("#tr-container").html(`

    No Active Bug Tickets

    `); + if (!cidElement) { + console.warn("CID element not found in the DOM."); + reject("CID element not found"); + return; + } + + const cidValue = cidElement.getAttribute("sol_id") ? cidElement.getAttribute("sol_id").trim() : ""; + + if (!cidValue) { + console.warn("CID is empty or undefined. Skipping fetch."); + reject("CID is empty"); + return; + } + + const dropdown = $("#tinf_names"); + dropdown.empty().append(''); + + $.ajax({ + url: "/SupportTicket/GetUsers", + type: "GET", + data: { cid: cidValue }, + success: function (data) { + console.log("Users fetched successfully:"); + cachedUsers = data; + populateDropdown(data); + if (document.getElementById("tinf_raising_yes").checked) { + dropdown.show(); + } + resolve(); // ✅ done fetching + }, + error: function (xhr, status, error) { + console.error("Error fetching users:", xhr.status, xhr.responseText); + alert("Failed to load user names."); + reject(error); + } + }); + }); + } + + function getTicketComments(ticketId) { + console.log("Fetching comments for Ticket ID:", ticketId); + + const commentsDisplay = document.getElementById('comments-display'); + + + commentsDisplay.innerHTML = `

    + Loading comments...

    `; + + $.ajax({ + type: "GET", + url: `/SupportTicket/CommentsByTicket?tktno=${ticketId}`, + success: function (response) { + console.log("Fetched comments:", response); + + commentsDisplay.innerHTML = ""; // Clear previous + + if (response.success && Array.isArray(response.comments)) { + if (response.comments.length === 0) { + lastCommentCount = response.comments.length; + commentsDisplay.innerHTML = `

    No comments found.

    `; + return; + } + + response.comments.forEach(comment => { + const timestamp = new Date(comment.createdAt).toLocaleString(); + + const commentBox = document.createElement('div'); + commentBox.className = 'trcmnt_comment-box'; + commentBox.innerHTML = ` + ${comment.userName} + ${timestamp} +

    ${comment.commentText}

    + `; + + commentsDisplay.appendChild(commentBox); + }); + setTimeout(() => { + commentsDisplay.scrollTop = commentsDisplay.scrollHeight; + }, 100); + } else { + commentsDisplay.innerHTML = `

    Failed to load comments.

    `; + } + }, + error: function (xhr, status, error) { + console.error("Error loading comments:", error); + commentsDisplay.innerHTML = `

    Error loading comments.

    `; + } + }); + }; + + + function pollForNewComments(ticketId, currentUserid) { + if (!ticketId || !currentUserid) return; + + if (pollingIntervalId) { + clearInterval(pollingIntervalId); + } + + pollingIntervalId = setInterval(() => { + $.ajax({ + type: "GET", + url: `/SupportTicket/CommentsByTicket?tktno=${ticketId}`, + success: function (response) { + if (response.success && Array.isArray(response.comments)) { + const newCount = response.comments.length; + + if (newCount > lastCommentCount) { + const latestComment = response.comments[response.comments.length - 1]; + + if (newCount > lastCommentCount) { + const latestComment = response.comments[response.comments.length - 1]; + + const isOwnComment = + latestComment.userId === currentUserid && + latestComment.consoleType === currentConsoleType; + + if (!isOwnComment) { + document.getElementById("new-comment-indicator").style.display = "inline"; + console.log("🔔 New comment from another user:", latestComment.userName); + } else { + console.log("💬 Ignored comment from same user or console."); + } + + lastCommentCount = newCount; + } + + } + } + }, + error: function (err) { + console.warn("Polling failed:", err); + } + }); + }, 5000); // every 5 sec + } + + + + + + // Event listener for radio buttons + document.querySelectorAll('input[name="tinf_raising"]').forEach((radio) => { + radio.addEventListener('change', function () { + const dropdown = document.getElementById("tinf_names"); + + if (this.value === "yes") { + dropdown.style.display = "block"; // Show dropdown when "Yes" is selected + fetchUsers(); // Fetch and populate users + } else { + dropdown.style.display = "none"; // Hide dropdown when "No" is selected + } + }); + }); + + function populateDropdown(data) { + if (!data || Object.keys(data).length === 0) { + console.warn("No users available to populate the dropdown."); return; } - // Store the fetched tickets globally - window.loadedTickets = data; + const dropdown = $("#tinf_names"); + dropdown.empty().append(''); + + $.each(data, function (userId, userName) { + dropdown.append(``); + userMap[userId] = userName; // Store ID-to-Name mapping + + }); - // Draw the tickets on the page - drawTickets(data, onload); + console.log("Dropdown updated with users:", $("#tinf_names").html()); } - function drawTickets(tickets, onload) { + + // Handle successful fetching of bug tickets + function onGetBugTicketsSuccess(activeTickets, closedTickets) { + + window.loadedTickets = [...activeTickets, ...closedTickets]; + + + // Handle Active Tickets + if (!activeTickets || !Array.isArray(activeTickets) || activeTickets.length === 0) { + $("#tr-container").html(`

    No Active Bug Tickets

    `); + $("#no-active-tickets").show(); + } else { + $("#no-active-tickets").hide(); + drawTickets("#tr-container", activeTickets, true); + } + + // Handle Closed Tickets + if (!closedTickets || !Array.isArray(closedTickets) || closedTickets.length === 0) { + $("#closedtr_container").html(`

    No Closed Bug Tickets

    `); + $("#no-closed-tickets").show(); + } else { + $("#no-closed-tickets").hide(); + drawTickets("#closedtr_container", closedTickets, true); + } + } + + const statusColors = { + "Assigned": "#4D86D6", + "Need more Info": "#D0463D", + "Rejected": "#505050", + "Resolved": "#2BB730", + "New": "#E0C04D", + "Reopened": "#8950B2", + "Deferred": "#282828" + }; + + function drawTickets(containerSelector, tickets, onload) { if (onload) { - $("#tr-container").empty(); + $(containerSelector).empty(); if (tickets.length > 0) { tickets.forEach(ticket => { const relativeTime = getRelativeTime(ticket.lstmodified); + const status = ticket.status || "New"; + const statusColor = statusColors[status] || "#999"; // fallback color - $("#tr-container").append(` + $(containerSelector).append(`
  • -
    ${ticket.title || "Untitled..."}
    +
    + ${ticket.title || "Untitled..."} +

    ${ticket.description || "No description available."}

    - - ${ticket.status || "New"} + + ${status} Raised by: ${ticket.fullname || "Unknown"}${relativeTime}
    -
  • `); }); } else { - $("#tr-container").append(`

    No Active Bug Tickets

    `); + $(containerSelector).append(`

    No Tickets

    `); } } updateTicketImages(); } + document.addEventListener('click', function (event) { // Handle click on the ticket tile const ticketTile = event.target.closest('.ticket-tile'); if (ticketTile) { // Extract ticket ID from the data attribute of the button within the ticket tile - const ticketId = ticketTile.querySelector('.view-details-btn').getAttribute('data-ticket-id'); + const btn = ticketTile.querySelector('.view-details-btn'); + if (!btn) { + console.warn('No .view-details-btn found inside .ticket-tile'); + return; + } + + const ticketId = btn.getAttribute('data-ticket-id'); + if (!ticketId) { + console.warn('No data-ticket-id found on .view-details-btn'); + return; + } - // Fetch the ticket data based on the ticket ID - const ticket = getTicketById(ticketId); // Implement this function to fetch the correct ticket details - console.log(ticket); // For debugging: Log the ticket data fetched + console.log("Clicked Ticket ID:", ticketId); // Debug - // Show the ticket form with the fetched ticket details - populateAndShowTicketForm(ticket); + // Fetch the ticket data from the backend asynchronously + getTicketsById(ticketId, function (ticket) { + console.log("Ticket fetched from backend:", ticket); // Debug + populateAndShowTicketForm(ticket); + }); } }); - function getTicketById(ticketId) { - const tickets = window.loadedTickets || []; // Ensure tickets are loaded - - // Debugging: Log the tickets and the ticketId to verify - console.log("Loaded Tickets:", tickets); - console.log("Clicked Ticket ID:", ticketId); - // Find the ticket with the matching ticket ID - const ticket = tickets.find(ticket => ticket.ticketid == ticketId); - // Debugging: Log the found ticket or the fallback object - console.log("Found Ticket:", ticket); - - // Return the found ticket or a default ticket if not found - return ticket || { - ticketid: ticketId, - title: "Untitled Ticket", - description: "No description available.", - status: "New", - comments: "No comments yet.", - priority: "medium", - type: "bug" - }; - } // Function to activate the Info tab function setActiveTab(tabId) { @@ -157,6 +406,9 @@ // Update the header to show the Ticket ID document.getElementById("ticket-id-header").textContent = `Ticket ID: ${ticket.ticketid || 'N/A'}`; + globalTicketId = ticket.ticketid; + pollForNewComments(globalTicketId, currentUserName); + // Set Info tab as active @@ -168,7 +420,7 @@ - //for comment tab visible + // for comment tab visible //const commentsTabLink = document.querySelector('a[href="#comments-tab"]'); //const commentsTab = document.getElementById("comments-tab"); @@ -201,12 +453,120 @@ document.getElementById("tinf_bug").checked = true; } + const dropdown = $("#tinf_names"); + const onbehalfValue = ticket.onbehalf; + + if (onbehalfValue && onbehalfValue !== "0" && onbehalfValue !== 0) { + document.getElementById("tinf_raising_yes").checked = true; + document.getElementById("tinf_raising_self").checked = false; + + const handleUserSelection = () => { + dropdown.show(); + dropdown.val(onbehalfValue); + const selectedName = userMap[onbehalfValue] || onbehalfValue; + $("#display-onbehalf-name").text(selectedName); + }; + + if (!cachedUsers) { + fetchUsers().then(handleUserSelection); + } else { + handleUserSelection(); + } + } else { + document.getElementById("tinf_raising_self").checked = true; + document.getElementById("tinf_raising_yes").checked = false; + + dropdown.hide(); + dropdown.val(""); + $("#display-onbehalf-name").text(""); + } // Clear existing attachments document.getElementById('screenshot-container').innerHTML = ''; document.getElementById('image-thumbnails').innerHTML = ''; document.getElementById('other-attachments-list').innerHTML = ''; + const isDeveloper = window.ViewBag && window.ViewBag.wc === "dc"; + const isUser = window.ViewBag && window.ViewBag.wc === "uc"; + console.log("Console Type:", isDeveloper ? "Developer" : isUser ? "User" : "Unknown"); + console.log("Current Username:", currentUserName); + if (isDeveloper) { + if (ticket.eta) { + // Convert to proper format + const etaDate = new Date(ticket.eta); + const formattedDate = etaDate.toISOString().split('T')[0]; // "YYYY-MM-DD" + document.getElementById("tinf_eta").value = formattedDate; + } + + document.getElementById("tinf_estimated_hours").value = ticket.estimated_hours || ""; + document.getElementById("tinf_actual_hours").value = ticket.actual_hours || ""; + } + const isResolved = ticket.status && ticket.status.toLowerCase() === "Resolved"; + const isUserConsole = window.ViewBag && window.ViewBag.wc === "uc"; + + + const resetReadOnlyFields = () => { + document.getElementById("ticket-title").disabled = false; + document.getElementById("ticket-description").removeAttribute("readonly"); + document.getElementById("tinf_status").disabled = false; + document.getElementById("tinf_comments").removeAttribute("readonly"); + document.getElementById("tinf_priority").disabled = false; + + document.getElementById("tinf_feature").disabled = false; + document.getElementById("tinf_bug").disabled = false; + + document.getElementById("tinf_raising_self").disabled = false; + document.getElementById("tinf_raising_yes").disabled = false; + $("#tinf_names").prop("disabled", false); + + document.getElementById("tinf_eta") && (document.getElementById("tinf_eta").disabled = false); + document.getElementById("tinf_estimated_hours") && (document.getElementById("tinf_estimated_hours").disabled = false); + document.getElementById("tinf_actual_hours") && (document.getElementById("tinf_actual_hours").disabled = false); + + document.getElementById("capture-screenshot").style.display = "inline-block"; + document.getElementById("upload-image-button").style.display = "inline-block"; + document.getElementById("other-attachments-button").style.display = "inline-block"; + + document.getElementById("update-ticket-button").style.display = "inline-block"; + + const header = document.getElementById("ticket-id-header"); + header.innerHTML = `Ticket ID: ${ticket.ticketid || 'N/A'}`; + }; + + const makeFormReadOnly = () => { + document.getElementById("ticket-title").disabled = true; + document.getElementById("ticket-description").setAttribute("readonly", true); + document.getElementById("tinf_status").disabled = true; + document.getElementById("tinf_comments").setAttribute("readonly", true); + document.getElementById("tinf_priority").disabled = true; + + document.getElementById("tinf_feature").disabled = true; + document.getElementById("tinf_bug").disabled = true; + + document.getElementById("tinf_raising_self").disabled = true; + document.getElementById("tinf_raising_yes").disabled = true; + $("#tinf_names").prop("disabled", true); + + document.getElementById("tinf_eta") && (document.getElementById("tinf_eta").disabled = true); + document.getElementById("tinf_estimated_hours") && (document.getElementById("tinf_estimated_hours").disabled = true); + document.getElementById("tinf_actual_hours") && (document.getElementById("tinf_actual_hours").disabled = true); + + document.getElementById("capture-screenshot").style.display = "none"; + document.getElementById("upload-image-button").style.display = "none"; + document.getElementById("other-attachments-button").style.display = "none"; + + document.getElementById("update-ticket-button").style.display = "none"; + + }; + + resetReadOnlyFields(); + + // Make form read-only and hide update button if it's from User Console + if (isUserConsole || isResolved) { + makeFormReadOnly(); + } + + // Create a popup element for displaying files const popup = document.createElement('div'); popup.className = 'popup'; @@ -258,90 +618,97 @@ if (Array.isArray(ticket.fileuploadlst) && ticket.fileuploadlst.length > 0) { ticket.fileuploadlst.forEach((file) => { if (file.contentType.startsWith('image/')) { - // If the file is an image, show it in the screenshot or image thumbnails + // Handle images const imgElement = document.createElement('img'); imgElement.src = `data:${file.contentType};base64,${file.filecollection}`; - imgElement.style = 'width: 100px; height: 100px; object-fit: cover; border-radius: 5px; margin: 5px; cursor: pointer;'; - + imgElement.style = 'width: 100px; height: 100px; object-fit: cover; margin: 5px; cursor: pointer;'; imgElement.onclick = function () { - const displayImage = document.createElement('img'); - displayImage.src = imgElement.src; - displayImage.style.width = '100%'; // Make the displayed image responsive - displayImage.style.height = 'auto'; - while (popupContent.firstChild) { - popupContent.removeChild(popupContent.firstChild); - } - popupContent.appendChild(closeButton); // Re-add close button - popupContent.appendChild(displayImage); + // Clear previous content before adding new image + popupContent.innerHTML = ''; + popupContent.appendChild(closeButton); + + // Create an image element for the popup + const popupImage = document.createElement('img'); + popupImage.src = imgElement.src; + popupImage.style.maxWidth = '90%'; // Ensures the image is not too wide + popupImage.style.maxHeight = '80vh'; // Limits height to prevent overflow + popupImage.style.objectFit = 'contain'; // Keeps the aspect ratio + + // Append image to the popup and show it + popupContent.appendChild(popupImage); popup.style.display = 'flex'; }; + if (file.fileName.startsWith('screenshot_')) { - // Append to screenshot container document.getElementById('screenshot-container').appendChild(imgElement); } else { - // Append to image thumbnails document.getElementById('image-thumbnails').appendChild(imgElement); } - } else { - // For other files, show them in the other attachments list - const fileItem = document.createElement('div'); - fileItem.className = 'attachment-item'; - fileItem.style = 'display: flex; align-items: center; gap: 10px;'; - - const fileIcon = document.createElement('i'); - fileIcon.className = 'fa fa-file'; - fileIcon.style = 'font-size: 24px;'; - + } else if (file.contentType === 'application/pdf') { + // Handle PDFs const fileLink = document.createElement('a'); fileLink.href = `data:${file.contentType};base64,${file.filecollection}`; fileLink.download = file.fileName; fileLink.textContent = file.fileName; - fileLink.style = 'text-decoration: none; color: #007BFF; cursor: pointer;'; - - fileLink.onclick = function (e) { - e.preventDefault(); // Prevent default action - const displayFile = document.createElement('iframe'); - displayFile.src = fileLink.href; - displayFile.style.width = '100%'; - displayFile.style.height = '400px'; // Adjust height as necessary - while (popupContent.firstChild) { - popupContent.removeChild(popupContent.firstChild); - } - popupContent.appendChild(closeButton); // Re-add close button - popupContent.appendChild(displayFile); - popup.style.display = 'flex'; - }; + fileLink.style = 'text-decoration: none; color: #007BFF; cursor: pointer; display: block;'; + + fileLink.onclick = function (e) { + e.preventDefault(); - fileItem.appendChild(fileIcon); - fileItem.appendChild(fileLink); - document.getElementById('other-attachments-list').appendChild(fileItem); + // Clear existing popup content (except close button) + while (popupContent.firstChild) { + popupContent.removeChild(popupContent.firstChild); + } + popupContent.appendChild(closeButton); // Ensure close button is always first + + // Create an iframe to display the PDF + const displayPdf = document.createElement('iframe'); + displayPdf.src = fileLink.href; + displayPdf.style.width = '100%'; + displayPdf.style.height = '500px'; + displayPdf.style.border = 'none'; + + popupContent.appendChild(displayPdf); + popup.style.display = 'flex'; +}; + + + document.getElementById('other-attachments-list').appendChild(fileLink); } }); } + document.querySelector('a[href="#comments-tab"]').addEventListener('click', function () { + const ticketId = document.getElementById('ticket-id-header').textContent.replace('Ticket ID: ', '').trim(); + if (ticketId) { + getTicketComments(ticketId); + document.getElementById("new-comment-indicator").style.display = "none"; + + } + }); // Add event listener for Open and Comment tabs - const openTab = document.querySelector('a[href="#open-tab"]'); - const commentsTab = document.querySelector('a[href="#comments-tab"]'); - - if (openTab) { - openTab.addEventListener('click', function () { - const openTabContent = document.getElementById("open-tab"); - if (openTabContent) { - openTabContent.innerHTML = "

    Coming Soon...

    "; - } - }); - } + //const openTab = document.querySelector('a[href="#open-tab"]'); + //const commentsTab = document.querySelector('a[href="#comments-tab"]'); + + //if (openTab) { + // openTab.addEventListener('click', function () { + // const openTabContent = document.getElementById("open-tab"); + // if (openTabContent) { + // openTabContent.innerHTML = "

    Coming Soon...

    "; + // } + // }); + //} - if (commentsTab) { - commentsTab.addEventListener('click', function () { - const commentsTabContent = document.getElementById("comments-tab"); - if (commentsTabContent) { - commentsTabContent.innerHTML = "

    Coming Soon...

    "; - } - }); - } + //if (commentsTab) { + // commentsTab.addEventListener('click', function () { + // const commentsTabContent = document.getElementById("comments-tab"); + // if (commentsTabContent) { + // commentsTabContent.innerHTML = "

    Coming Soon...

    "; + // } + // }); + //} // Close the popup when clicking outside of it popup.onclick = function (e) { @@ -400,8 +767,7 @@ const activeTicketsTab = tabContent.querySelector('#alltickets-tab'); activeTicketsTab.classList.add('active'); // Activate the active tickets tab tabContent.querySelector('#closedtickets-tab').classList.remove('active'); - } - + } // Event listeners createTicketButton.addEventListener('click', function (e) { e.preventDefault(); @@ -412,6 +778,13 @@ commentsTabLink.classList.add("disabled"); // Optionally add a disabled class for styling } + // Default to "No, Self" + document.getElementById("tinf_raising_self").checked = true; + document.getElementById("tinf_raising_yes").checked = false; + + // Hide the dropdown for selecting a name + document.getElementById("tinf_names").style.display = "none"; + // Set Info tab as active setActiveTab('#info-tab'); @@ -437,22 +810,25 @@ document.getElementById("image-thumbnails").innerHTML = ""; // Clear image thumbnails document.getElementById("other-attachments-list").innerHTML = ""; // Clear other attachments - // Hide the "Comments" tab + //// Hide the "Comments" tab //const commentsTabLink = document.querySelector('a[href="#comments-tab"]'); //const commentsTab = document.getElementById("comments-tab"); //if (commentsTabLink && commentsTab) { // console.log("Creating New Ticket - Hiding Comments Tab"); - // Hide tab link + // //Hide tab link //commentsTabLink.style.display = "none"; //commentsTabLink.classList.remove("active"); - // Hide tab content properly + //// Hide tab content properly // commentsTab.classList.remove("show", "active"); //} // Show the ticket form + + fetchUsers() + showTicketForm(); }); @@ -465,110 +841,147 @@ backButton.addEventListener('click', function () { showTicketsList(); }); + // Add event listener for the "Update Ticket" button - document.getElementById('update-ticket-button').addEventListener('click', function (event) { + document.getElementById('update-ticket-button').addEventListener('click', async function (event) { event.preventDefault(); - // Collect the ticket ID - const ticketId = document.getElementById("ticket-id-header").textContent.split(":")[1].trim(); + try { + // Collect the ticket ID + const ticketIdHeader = document.getElementById("ticket-id-header"); + if (!ticketIdHeader) { + alert("Error: Ticket ID not found."); + return; + } - // Get the form field values - const title = document.getElementById("ticket-title").value.trim(); - const description = document.getElementById("ticket-description").value.trim(); - let hasErrors = false; + const ticketId = ticketIdHeader.textContent.split(":")[1]?.trim(); + if (!ticketId) { + alert("Error: Invalid ticket ID."); + return; + } - // Clear previous error messages - document.getElementById("title-error").textContent = ""; - document.getElementById("description-error").textContent = ""; + // Get the form field values + const titleField = document.getElementById("ticket-title"); + const descriptionField = document.getElementById("ticket-description"); + const statusField = document.getElementById("tinf_status"); + const priorityField = document.getElementById("tinf_priority"); + const typeField = document.querySelector('input[name=tinf_type]:checked'); + const soluElement = document.getElementById("soluid"); + const etaField = document.getElementById("tinf_eta"); + const estimatedHoursField = document.getElementById("tinf_estimated_hours"); + const actualHoursField = document.getElementById("tinf_actual_hours"); + const commentsField = document.getElementById("tinf_comments"); + let hasErrors = false; + + // Clear previous error messages + document.getElementById("title-error").textContent = ""; + document.getElementById("description-error").textContent = ""; + + // Validate required fields + if (!titleField || titleField.value.trim() === "") { + document.getElementById("title-error").textContent = "Title is required."; + titleField.style.borderColor = "red"; + hasErrors = true; + } else { + titleField.style.borderColor = ""; + } - // Validate title and description - if (title === "") { - document.getElementById("title-error").textContent = "Title is required."; - document.getElementById("ticket-title").style.borderColor = "red"; - hasErrors = true; - } else { - document.getElementById("ticket-title").style.borderColor = ""; - } + if (!descriptionField || descriptionField.value.trim() === "") { + document.getElementById("description-error").textContent = "Description is required."; + descriptionField.style.borderColor = "red"; + hasErrors = true; + } else { + descriptionField.style.borderColor = ""; + } - if (description === "") { - document.getElementById("description-error").textContent = "Description is required."; - document.getElementById("ticket-description").style.borderColor = "red"; - hasErrors = true; - } else { - document.getElementById("ticket-description").style.borderColor = ""; - } + if (!statusField || !priorityField || !typeField) { + alert("Error: Some required fields are missing."); + return; + } - // Stop submission if there are errors - if (hasErrors) { - return; - } + // Stop submission if there are errors + if (hasErrors) { + return; + } - // Prepare the form data - let data = new FormData(); - data.append("ticketId", ticketId); // Add ticket ID to the form data - data.append("title", title); - data.append("description", description); - data.append("status", document.getElementById("tinf_status").value.trim()); - data.append("priority", document.getElementById("tinf_priority").value.trim()); - data.append("type_f_b", document.querySelector('input[name=tinf_type]:checked').value.trim()); - data.append("solid", document.getElementById("soluid").getAttribute('sol_id')); - - // Attach screenshots (if any) - const screenshotContainer = document.getElementById('screenshot-container').querySelectorAll('img'); - screenshotContainer.forEach((screenshot, index) => { - const blob = dataURLToBlob(screenshot.src); - data.append(`screenshot_${index}`, blob, `screenshot_${index}.png`); - }); + // Prepare the form data + let data = new FormData(); + data.append("ticketId", ticketId); + data.append("solu_id", soluElement ? soluElement.getAttribute('sol_id') : ""); + + let changedTkt = { + title: titleField.value.trim(), + description: descriptionField.value.trim(), + status: statusField.value.trim(), + priority: priorityField.value.trim(), + type_f_b: typeField.value.trim(), + eta: etaField?.value.trim() || "", + estimated_hours: estimatedHoursField?.value.trim() || "", + actual_hours: actualHoursField?.value.trim() || "", + comments: commentsField?.value.trim() || "" + }; - // Attach uploaded images (if any) - const imageThumbnails = document.getElementById('image-thumbnails').querySelectorAll('img'); - imageThumbnails.forEach((image, index) => { - const blob = dataURLToBlob(image.src); - data.append(`imageUpload_${index}`, blob, `image_${index}.png`); - }); - // Attach other files (if any) - const otherAttachments = document.getElementById('other-attachments-list').querySelectorAll('.attachment-item'); - otherAttachments.forEach((attachment, index) => { - const anchor = attachment.querySelector('a'); - if (anchor) { - const url = anchor.href; - const fileName = anchor.download; - data.append(`attachment_${index}`, url, fileName); + data.append("updtkt", JSON.stringify(changedTkt)); + + // Attach screenshots (if any) + document.querySelectorAll('#screenshot-container img').forEach((screenshot, index) => { + const blob = dataURLToBlob(screenshot.src); + data.append(`screenshot_${index}`, blob, `screenshot_${index}.png`); + }); + + // Attach uploaded images (if any) + document.querySelectorAll('#image-thumbnails img').forEach((image, index) => { + const blob = dataURLToBlob(image.src); + data.append(`imageUpload_${index}`, blob, `image_${index}.png`); + }); + + // Attach other files (if any) + if (selectedAttachments.length > 0) { + selectedAttachments.forEach((file, index) => { + console.log(`Appending file: ${file.name}, Type: ${file.type}, Size: ${file.size} bytes`); + data.append(`files[${index}]`, file); + }); + } else { + console.warn("No non-image files selected during update!"); + } + + // Debugging: Log FormData entries to confirm files are included + for (let pair of data.entries()) { + console.log(pair[0], pair[1]); } - }); - // Show loader - $("#eb_common_loader").EbLoader("show"); - - // Send the request to update the ticket - fetch(`/SupportTicket/UpdateTicketDetails/${ticketId}`, { // Update the backend URL as needed - method: 'POST', - body: data - }) - .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); - }) - .then(data => { - $("#eb_common_loader").EbLoader("hide"); - console.log("Ticket updated successfully:", data); // Debugging log - - if (data.successMessage) { - showConfirmationMessage(); - } else { - alert("Failed to update ticket."); - } - }) - .catch(error => { - console.error('Error:', error); - $("#eb_common_loader").EbLoader("hide"); - alert("Failed to update ticket."); + // Show loader + $("#eb_common_loader").EbLoader("show"); + + // Send the request to update the ticket + const response = await fetch(`/SupportTicket/UpdateTicket`, { + method: 'POST', + body: data }); + + $("#eb_common_loader").EbLoader("hide"); + + if (!response.ok) { + throw new Error('Network response was not ok'); + } + + const result = await response.json(); + console.log("Ticket update response:", result); + + if (result.successMessage) { + showConfirmationMessage(); + } else { + alert("Failed to update ticket."); + } + } catch (error) { + console.error('Error:', error); + $("#eb_common_loader").EbLoader("hide"); + alert("Failed to update ticket."); + } }); + // Utility function to convert dataURL to Blob function dataURLToBlob(dataURL) { const byteString = atob(dataURL.split(',')[1]); @@ -580,116 +993,137 @@ } return new Blob([ab], { type: mimeString }); } + + + document.addEventListener("DOMContentLoaded", function () { + + raisingYes.addEventListener("change", function () { + namesDropdown.style.display = "block"; // Show the dropdown + }); + + raisingNo.addEventListener("change", function () { + namesDropdown.style.display = "none"; // Hide the dropdown + namesDropdown.value = ""; // Reset the selected value + }); + + }); + + submitTicketButton.addEventListener('click', function (event) { - event.preventDefault(); + event.preventDefault(); - const title = document.getElementById("ticket-title").value.trim(); - const description = document.getElementById("ticket-description").value.trim(); - const statusInput = document.getElementById("tinf_status"); - statusInput.value = "New"; - const status = statusInput.value; // Ensure variable holds the correct value + const title = document.getElementById("ticket-title").value.trim(); + const description = document.getElementById("ticket-description").value.trim(); + const statusInput = document.getElementById("tinf_status"); + statusInput.value = "New"; + const status = statusInput.value; + const onBehalfOf = raisingYes.checked ? namesDropdown.value.trim() : ""; - let hasErrors = false; - document.getElementById("title-error").textContent = ""; - document.getElementById("description-error").textContent = ""; + let hasErrors = false; - if (title === "") { - document.getElementById("title-error").textContent = "Title is required."; - document.getElementById("ticket-title").style.borderColor = "red"; - hasErrors = true; - } else { - document.getElementById("ticket-title").style.borderColor = ""; - } + document.getElementById("title-error").textContent = ""; + document.getElementById("description-error").textContent = ""; - if (description === "") { - document.getElementById("description-error").textContent = "Description is required."; - document.getElementById("ticket-description").style.borderColor = "red"; - hasErrors = true; - } else { - document.getElementById("ticket-description").style.borderColor = ""; - } + if (title === "") { + document.getElementById("title-error").textContent = "Title is required."; + document.getElementById("ticket-title").style.borderColor = "red"; + hasErrors = true; + } else { + document.getElementById("ticket-title").style.borderColor = ""; + } - if (hasErrors) { - return; // Stop submission if there are errors - } + if (description === "") { + document.getElementById("description-error").textContent = "Description is required."; + document.getElementById("ticket-description").style.borderColor = "red"; + hasErrors = true; + } else { + document.getElementById("ticket-description").style.borderColor = ""; + } - let data = new FormData(); - data.append("title", title); - data.append("description", description); - data.append("status", status); - data.append("priority", document.getElementById("tinf_priority").value.trim()); - data.append("type_f_b", document.querySelector('input[name=tinf_type]:checked').value.trim()); - data.append("solid", document.getElementById("soluid").getAttribute('sol_id')); - - // Attach screenshots (if any) - const screenshotContainer = document.getElementById('screenshot-container').querySelectorAll('img'); - screenshotContainer.forEach((screenshot, index) => { - const blob = dataURLToBlob(screenshot.src); - data.append(`screenshot_${index}`, blob, `screenshot_${index}.png`); - }); + if (hasErrors) { + return; + } + + let data = new FormData(); + data.append("title", title); + data.append("description", description); + data.append("status", status); + data.append("priority", document.getElementById("tinf_priority").value.trim()); + data.append("type_f_b", document.querySelector('input[name=tinf_type]:checked').value.trim()); + data.append("solid", document.getElementById("soluid").getAttribute('sol_id')); + data.append("onBehalfOf", onBehalfOf); // Add "on behalf of" field + + // Attach screenshots (if any) + const screenshotContainer = document.getElementById('screenshot-container').querySelectorAll('img'); + screenshotContainer.forEach((screenshot, index) => { + const blob = dataURLToBlob(screenshot.src); + data.append(`screenshot_${index}`, blob, `screenshot_${index}.png`); + }); - // Attach uploaded images - const imageThumbnails = document.getElementById('image-thumbnails').querySelectorAll('img'); - imageThumbnails.forEach((image, index) => { + // Attach uploaded images (if any) + document.querySelectorAll('#image-thumbnails img').forEach((image, index) => { const blob = dataURLToBlob(image.src); data.append(`imageUpload_${index}`, blob, `image_${index}.png`); }); - // Attach other files (if any) - const otherAttachments = document.getElementById('other-attachments-list').querySelectorAll('.attachment-item'); - otherAttachments.forEach((attachment, index) => { - const anchor = attachment.querySelector('a'); - if (anchor) { - const url = anchor.href; - const fileName = anchor.download; - data.append(`attachment_${index}`, url, fileName); - } - }); - - $("#eb_common_loader").EbLoader("show"); - - fetch("/SupportTicket/SubmitTicketDetails", { - method: 'POST', - body: data - }) - .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); - }) - .then(data => { - $("#eb_common_loader").EbLoader("hide"); - console.log("Ticket submitted successfully:", data); // Debugging log - - if (data.successMessage) { - showConfirmationMessage(); - } else { - alert("Failed to submit ticket."); - } - }) - .catch(error => { - console.error('Error:', error); - $("#eb_common_loader").EbLoader("hide"); - alert("Failed to submit ticket."); + if (selectedAttachments.length > 0) { + selectedAttachments.forEach((file, index) => { + console.log(`Appending file: ${file.name}, Type: ${file.type}, Size: ${file.size} bytes`); + data.append(`files[${index}]`, file); // Append non-image attachments properly }); - }); - - // Utility function to convert dataURL to Blob - function dataURLToBlob(dataURL) { - const byteString = atob(dataURL.split(',')[1]); - const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0]; - const ab = new ArrayBuffer(byteString.length); - const ia = new Uint8Array(ab); - for (let i = 0; i < byteString.length; i++) { - ia[i] = byteString.charCodeAt(i); + } else { + console.warn("No non-image files selected!"); } - return new Blob([ab], { type: mimeString }); + + sendData(data); + + + + function sendData(data) { + $("#eb_common_loader").EbLoader("show"); + + fetch("/SupportTicket/SubmitTicketDetails", { + method: 'POST', + body: data + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + $("#eb_common_loader").EbLoader("hide"); + console.log("Ticket submitted successfully:", data); + + if (data.successMessage) { + showConfirmationMessage(); + } else { + alert("Failed to submit ticket."); + } + }) + .catch(error => { + console.error('Error:', error); + $("#eb_common_loader").EbLoader("hide"); + alert("Failed to submit ticket."); + }); } +}); +// Utility function to convert dataURL to Blob +function dataURLToBlob(dataURL) { + const byteString = atob(dataURL.split(',')[1]); + const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0]; + const ab = new ArrayBuffer(byteString.length); + const ia = new Uint8Array(ab); + for (let i = 0; i < byteString.length; i++) { + ia[i] = byteString.charCodeAt(i); + } + return new Blob([ab], { type: mimeString }); +} function showConfirmationMessage() { ticketForm.style.display = 'none'; confirmationMessage.style.display = 'block'; @@ -700,6 +1134,35 @@ // Trigger fetching of active bug tickets when the page loads getBugTickets(); + + + window.addEventListener("DOMContentLoaded", function () { + const isDeveloper = window.ViewBag && window.ViewBag.wc === "dc"; + + const statusInput = document.getElementById("tinf_status"); + const commentsTextarea = document.getElementById("tinf_comments"); + const estimatedHoursInput = document.getElementById("tinf_estimated_hours"); + const etaInput = document.getElementById("tinf_eta"); + const actualHoursInput = document.getElementById("tinf_actual_hours"); + + if (!isDeveloper) { + statusInput.disabled = true; + commentsTextarea.readOnly = true; + estimatedHoursInput.readOnly = true; + etaInput.readOnly = true; + actualHoursInput.readOnly = true; + + } + + // Optional: Hide create buttons if not a developer + const createTicketButton = document.getElementById("create-ticket-button"); + const createTicketButtonClosed = document.getElementById("create-ticket-button-closed"); + + if (isDeveloper) { + if (createTicketButton) createTicketButton.style.display = "none"; + if (createTicketButtonClosed) createTicketButtonClosed.style.display = "none"; + } + }); }); @@ -863,36 +1326,103 @@ document.addEventListener('DOMContentLoaded', function () { const files = event.target.files; for (let i = 0; i < files.length; i++) { const file = files[i]; - const fileName = file.name; - appendAttachment(URL.createObjectURL(file), otherAttachmentsList, 'attachment', fileName); + + // Prevent duplicate file selection + if (selectedAttachments.some(att => att.name === file.name)) { + alert(`File "${file.name}" is already selected.`); + continue; // Skip duplicate + } + + selectedAttachments.push(file); // Store file reference + appendAttachment(URL.createObjectURL(file), otherAttachmentsList, 'attachment', file.name); + } + otherAttachmentsInput.value = ''; // Clear input to allow re-selection + }); + + +}); + +document.addEventListener('DOMContentLoaded', function () { + document.getElementById('send-comment-btn').addEventListener('click', function (event) { + event.preventDefault(); + + const commentInput = document.getElementById('ticket-comments'); + const commentText = commentInput.value.trim(); + + const ticketId = document.getElementById('ticket-id-header')?.textContent.replace('Ticket ID: ', '').trim(); + const username = document.getElementById('current-username')?.value; + const solutionId = document.getElementById('soluid')?.textContent.trim(); // soluid is a , not an input + + console.log('Sending data to backend:', { + TicketNo: ticketId, + Comments: commentText, + UserName: username, + Solution_id: solutionId + }); + + if (commentText) { + // ✅ Log data before sending + console.log('Sending data to backend:', { + TicketNo: ticketId, + Comments: commentText, + UserName: username, + Solution_id: solutionId + }); + + $.ajax({ + url: '../SupportTicket/Comment', + type: 'POST', + contentType: 'application/x-www-form-urlencoded', + data: { + TicketNo: ticketId, + Comments: commentText, + UserName: username, + Solution_id: solutionId, + currentUserid: currentUserid +}, + success: function (response) { + if (response.CmntStatus === false && response.ErMsg) { + alert(response.ErMsg); + return; + } + + const timestamp = new Date().toLocaleString(); + + const commentBox = document.createElement('div'); + commentBox.className = 'trcmnt_comment-box'; + + const commentContent = ` + ${username} + ${timestamp} +

    ${commentText}

    + `; + + commentBox.innerHTML = commentContent; + document.getElementById('comments-display').appendChild(commentBox); + + setTimeout(() => { + const commentsDisplay = document.getElementById('comments-display'); + commentsDisplay.scrollTo({ top: commentsDisplay.scrollHeight, behavior: 'smooth' }); + }, 100); + + + commentInput.value = ''; + + lastCommentCount += 1; + + }, + error: function () { + alert('Failed to send comment. Please try again.'); + } + }); + } else { + alert('Comment field cannot be empty.'); } - otherAttachmentsInput.value = ''; // Reset input }); }); -//document.addEventListener('DOMContentLoaded', function () { -// document.getElementById('send-comment-btn').addEventListener('click', function (event) { -// event.preventDefault(); // Prevent any default form submission action -// const commentInput = document.getElementById('ticket-comments'); -// const commentText = commentInput.value.trim(); -// if (commentText) { -// const username = "User"; // Replace with dynamic username if available -// const timestamp = new Date().toLocaleString(); -// const commentBox = document.createElement('div'); -// commentBox.className = 'trcmnt_comment-box'; -// const commentContent = ` -// ${username} -// ${timestamp} -//

    ${commentText}

    -// `; -// commentBox.innerHTML = commentContent; -// document.getElementById('comments-display').appendChild(commentBox); -// commentInput.value = ''; // Clear the input field -// } -// }); -//}); diff --git a/wwwroot/js/DBExplorer.js b/wwwroot/js/DBExplorer.js index 8a1f0c350..db8856d3e 100644 --- a/wwwroot/js/DBExplorer.js +++ b/wwwroot/js/DBExplorer.js @@ -24,7 +24,6 @@ }); return false; }; - this.ajax_call = function (e) { e.preventDefault(); $(".show_loader").EbLoader("show"); @@ -49,12 +48,17 @@ success: function (result) { if (result.length != 0) { if (result[0].columnCollection != null) { - this.query_result(result); + if (isFunctionContextMenuCall) { + let functionCode = result[0].rowCollection[0][0][1]; + this.editor[exe_window].setValue(functionCode); + isFunctionContextMenuCall = false; // Reset the flag + } else { + this.query_result(result); + } $('#t' + (res - 1) + ' a').trigger('click'); } else if (result[0].message != "") { - //alert(result[0].message); EbPopBox("show", { - Message: "Failed : " + result[0].message, + Message: result[0].message, ButtonStyle: { Text: "Ok", Color: "white", @@ -76,7 +80,6 @@ } } }); - //alert('Oh Yes success :( : ' + result); } } $(".show_loader").EbLoader("hide"); @@ -96,8 +99,7 @@ $(".show_loader").EbLoader("hide"); } }); - } - else { + } else { EbPopBox("show", { Message: "Empty Query !!!", ButtonStyle: { @@ -113,6 +115,8 @@ } }.bind(this); + + this.searchSolution = function (e) { $("#eb_common_loader").EbLoader("show"); var searchValue = e.target.selectedOptions[0].text; @@ -141,56 +145,109 @@ }); }.bind(this); - this.query_result = function (result) { + this.query_result = function (result) { var exe_window = $("#TabAdderMain li.active a").attr("href"); exe_window = exe_window[exe_window.length - 1]; + + // Clear previous results + $(`#Result_Tab${exe_window}`).empty(); + $(`#resulttab${exe_window}`).empty(); + + // Ensure only one result tab at a time for (var Result_ in result) { $.each(result[Result_].columnCollection, function (i, columns) { - $(`#Result_Tab${exe_window}`).append('
  • Result ' + res + '

  • ') - $("#resulttab" + exe_window).append("
    ") - $("#resulttab" + exe_window).append(` -