using _0_Framework.Application; using AccountManagement.Application.Contracts.Task; using AccountManagement.Application.Contracts.Ticket; using AccountManagement.Application.Contracts.TicketAccessAccount; using CompanyManagment.App.Contracts.Workshop; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using ServiceHost.BaseControllers; namespace ServiceHost.Areas.Admin.Controllers { /// /// API controller for admin ticket management functionality /// public class TicketController : AdminBaseController { private readonly ITicketApplication _ticketApplication; private readonly IWorkshopApplication _workshopApplication; private readonly IAuthHelper _authHelper; private readonly ITicketAccessAccountApplication _ticketAccessAccountApplication; /// /// Initialize a new instance of the TicketController /// public TicketController( ITicketApplication ticketApplication, IWorkshopApplication workshopApplication, IAuthHelper authHelper, ITicketAccessAccountApplication ticketAccessAccountApplication) { _ticketApplication = ticketApplication; _workshopApplication = workshopApplication; _authHelper = authHelper; _ticketAccessAccountApplication = ticketAccessAccountApplication; } private bool HasTicketAccess() { return _ticketAccessAccountApplication.HasTicketAccess(_authHelper.CurrentAccountId()); } /// /// Get ticket overview including types count and trash count /// /// Search criteria for tickets /// Ticket overview data including counts by type [HttpGet] public IActionResult GetTickets([FromQuery] TicketSearchModel searchModel) { if (!HasTicketAccess()) return Forbid(); var typesCount = _ticketApplication.GetTypesCountOfTicketForAdmin(); var trashCount = _ticketApplication.GetDeletedTicket().Count(); return Ok(new { success = true, data = new { typesCount, trashCount } }); } /// /// Get paginated ticket data with filtering options /// /// Page index for pagination (default: 1) /// Filter by ticket status /// Filter by ticket number /// General search term /// Paginated list of tickets matching the criteria [HttpGet("data")] public IActionResult GetTicketData([FromQuery] int pageIndex = 1, [FromQuery] string status = "", [FromQuery] string ticketNumber = "", [FromQuery] string generalSearch = "") { if (!HasTicketAccess()) return Forbid(); var searchModel = new TicketSearchModel() { PageIndex = pageIndex, Status = status, TicketNumber = generalSearch, GeneralSearch = generalSearch, }; List tickets; tickets = status != "زباله" ? _ticketApplication.GetAll(searchModel) : _ticketApplication.GetDeletedTicket(); return Ok(new { success = true, data = tickets, pageIndex = tickets.Count }); } /// /// Get detailed information about a specific ticket /// /// The ID of the ticket to retrieve /// Detailed ticket information including workshop name [HttpGet("{ticketId}/detail")] public IActionResult GetTicketDetail(long ticketId) { if (!HasTicketAccess()) return Forbid(); var ticket = _ticketApplication.GetDetails(ticketId); if (ticket == null) return NotFound(); ticket.WorkshopName = _workshopApplication.GetDetails(ticket.WorkshopId).WorkshopFullName; return Ok(new { success = true, data = ticket }); } /// /// Get all messages for a specific ticket /// /// The ID of the ticket /// Ticket details including all messages [HttpGet("{ticketId}/messages")] public IActionResult GetTicketMessages(long ticketId) { if (!HasTicketAccess()) return Forbid(); var ticketDetail = _ticketApplication.GetDetails(ticketId); if (ticketDetail == null) return NotFound(); return Ok(new { success = true, data = ticketDetail }); } /// /// Submit an admin response to a ticket /// /// Response details including ticket ID and response text /// Operation result indicating success or failure [HttpPost("response")] public IActionResult SaveAdminResponse([FromBody] ResponseTicket command) { if (!HasTicketAccess()) return Forbid(); command.AdminId = _authHelper.CurrentAccountId(); command.Response = command.Response?.Replace("\n", "
"); var result = _ticketApplication.AdminResponseTicket(command); return Ok(new { success = result.IsSuccedded, message = result.Message }); } /// /// Upload a media file for ticket /// /// The file to upload /// Upload result with file ID if successful [HttpPost("upload")] public IActionResult UploadFile(IFormFile media) { if (!HasTicketAccess()) return Forbid(); var accountId = _authHelper.CurrentAccountId(); var operation = _ticketApplication.UploadMedia(media, accountId); return Ok(new { success = operation.IsSuccedded, message = operation.Message, id = operation.SendId, }); } /// /// Delete a media file from the system /// /// The ID of the media file to delete /// Operation result indicating success or failure [HttpDelete("media/{mediaId}")] public IActionResult DeleteFile(long mediaId) { if (!HasTicketAccess()) return Forbid(); var operation = _ticketApplication.RemoveMedia(mediaId); return Ok(new { success = operation.IsSuccedded, message = operation.Message, }); } /// /// Remove all temporary uploaded files for the current user /// /// Operation result indicating success [HttpDelete("temp-files")] public IActionResult RemoveAllTempFiles() { if (!HasTicketAccess()) return Forbid(); var accId = _authHelper.CurrentAccountId(); _ticketApplication.RemoveTempUploadedFiles(accId); return Ok(new { success = true, message = "فایل‌های موقت با موفقیت حذف شدند" }); } /// /// Assign a task from a ticket to specific users /// /// Task assignment details including description, title, and assignees /// Operation result indicating success or failure [HttpPost("assign-task")] public IActionResult AssignTask([FromBody] CreateTaskRequest request) { if (!HasTicketAccess()) return Forbid(); var command = new CreateTask { SenderId = _authHelper.CurrentAccountId(), Description = request.Description?.Replace("\n", "
"), Title = request.Title, EndTaskDate = request.EndTaskDate, EndTaskTime = request.EndTaskTime, ReceiverId = request.ReceiverId ?? new List(), PositionId = request.PositionId ?? new List(), UploadedMedia = request.UploadedMedia ?? new List() }; var result = _ticketApplication.AssignTicket(command, request.TicketId); return Ok(new { success = result.IsSuccedded, message = result.Message, }); } /// /// Download or view a file from the system /// /// The file path on the server /// The file ID for naming purposes /// File content for download or viewing [HttpGet("file")] public IActionResult GetFile([FromQuery] string filePath, [FromQuery] long id) { if (!HasTicketAccess()) return Forbid(); if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath)) return NotFound(); var contentType = Tools.GetContentTypeImage(Path.GetExtension(filePath)); if (string.IsNullOrWhiteSpace(contentType)) { byte[] fileContent = System.IO.File.ReadAllBytes(filePath); var extension = Tools.GetContentType(Path.GetExtension(filePath)); return File(fileContent, extension, $"Task_{id}{Path.GetExtension(filePath)}"); } else { return PhysicalFile(filePath, contentType); } } /// /// Download or play a voice file /// /// The voice file path on the server /// Voice file content as audio/ogg [HttpGet("voice")] public IActionResult GetVoice([FromQuery] string filePath) { if (!HasTicketAccess()) return Forbid(); if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath)) return NotFound(); byte[] voiceContent = System.IO.File.ReadAllBytes(filePath); return File(voiceContent, "audio/ogg", "Task_voice.ogg"); } /// /// View a picture file /// /// The image file path on the server /// Image file content [HttpGet("picture")] public IActionResult GetPicture([FromQuery] string filePath) { if (!HasTicketAccess()) return Forbid(); if (string.IsNullOrEmpty(filePath) || !System.IO.File.Exists(filePath)) return NotFound(); var contentType = Tools.GetContentTypeImage(Path.GetExtension(filePath)); return PhysicalFile(filePath, contentType); } /// /// Accept a pending admin response /// /// The ID of the admin response to accept /// Operation result indicating success or failure [HttpPost("response/{adminResId}/accept")] public IActionResult AcceptPendingAdminResponse(long adminResId) { if (!HasTicketAccess()) return Forbid(); var result = _ticketApplication.AcceptPendingAdminResponse(adminResId); return Ok(new { success = result.IsSuccedded, message = result.Message }); } /// /// Reject a pending admin response /// /// The ID of the admin response to reject /// Operation result indicating success or failure [HttpPost("response/{adminResId}/reject")] public IActionResult RejectPendingAdminResponse(long adminResId) { if (!HasTicketAccess()) return Forbid(); var result = _ticketApplication.RejectPendingAdminResponse(adminResId); return Ok(new { success = result.IsSuccedded, message = result.Message }); } /// /// Edit and accept a pending admin response /// /// The ID of the admin response to edit /// The new response text /// Operation result indicating success or failure [HttpPut("response/{adminResId}")] public IActionResult EditPendingAdminResponse(long adminResId, [FromBody] EditResponseRequest request) { if (!HasTicketAccess()) return Forbid(); var result = _ticketApplication.EditAndAcceptPendingAdminResponse(adminResId, request.NewResponse.Replace("\n", "
")); return Ok(new { success = result.IsSuccedded, message = result.Message }); } /// /// Close a ticket (mark as closed) /// /// The ID of the ticket to close /// Operation result indicating success or failure [HttpPost("{ticketId}/close")] public IActionResult CloseTicket(long ticketId) { if (!HasTicketAccess()) return Forbid(); var result = _ticketApplication.CloseTicket(ticketId); return Ok(new { success = result.IsSuccedded, message = result.Message }); } /// /// Delete a ticket (move to trash) /// /// The ID of the ticket to delete /// Operation result indicating success or failure [HttpDelete("{ticketId}")] public IActionResult DeleteTicket(long ticketId) { if (!HasTicketAccess()) return Forbid(); var result = _ticketApplication.DeleteTicket(ticketId); return Ok(new { success = result.IsSuccedded, message = result.Message }); } /// /// Get current ticket counts by status (open, closed, answered, pending, etc.) /// /// Ticket counts grouped by status [HttpGet("counts")] public IActionResult GetTicketCounts() { if (!HasTicketAccess()) return Forbid(); var typesCount = _ticketApplication.GetTypesCountOfTicketForAdmin(); var deletedCount = _ticketApplication.GetDeletedTicket().Count(); return Ok(new { success = true, data = new { open = typesCount.Open, closed = typesCount.Closed, answered = typesCount.Answered, pending = typesCount.Pending, all = typesCount.All, deleted = deletedCount } }); } /// /// Restore a deleted ticket from trash /// /// The ID of the ticket to restore /// Operation result indicating success or failure [HttpPost("{ticketId}/restore")] public IActionResult RestoreDeletedTicket(long ticketId) { if (!HasTicketAccess()) return Forbid(); var result = _ticketApplication.RestoreDeletedTicket(ticketId); return Ok(new { success = result.IsSuccedded, message = result.Message }); } /// /// Check if the current user has access to ticket functionality /// /// Access status for the current user [HttpGet("access")] public IActionResult CheckTicketAccess() { var accountId = _authHelper.CurrentAccountId(); var hasAccess = _ticketAccessAccountApplication.HasTicketAccess(accountId); return Ok(new { success = true, hasAccess }); } } /// /// Request model for creating and assigning tasks from tickets /// public class CreateTaskRequest { /// /// Task description /// public string Description { get; set; } /// /// Task title /// public string Title { get; set; } /// /// Task end date /// public string EndTaskDate { get; set; } /// /// Task end time /// public string EndTaskTime { get; set; } /// /// List of receiver IDs for task assignment /// public List ReceiverId { get; set; } /// /// List of position IDs for task assignment /// public List PositionId { get; set; } /// /// List of uploaded media file IDs /// public List UploadedMedia { get; set; } /// /// The ticket ID to assign task from /// public long TicketId { get; set; } } /// /// Request model for editing admin responses /// public class EditResponseRequest { /// /// The new response text /// public string NewResponse { get; set; } } }