final
This commit is contained in:
parent
5c6abe5686
commit
bbf5acbb6b
@ -11,6 +11,7 @@ public class CustomerMessage
|
||||
public bool IsUrgent { get; set; }
|
||||
public string? OrderReference { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public int Direction { get; set; } // 0 = AdminToCustomer, 1 = CustomerToAdmin
|
||||
}
|
||||
|
||||
public enum MessageType
|
||||
|
||||
@ -8,4 +8,5 @@ public interface IMessageService
|
||||
Task<bool> MarkMessageAsSentAsync(Guid messageId, string? platformMessageId = null);
|
||||
Task<bool> MarkMessageAsFailedAsync(Guid messageId, string reason);
|
||||
Task<bool> CreateCustomerMessageAsync(object messageData);
|
||||
Task<List<CustomerMessage>?> GetCustomerMessagesAsync(Guid customerId);
|
||||
}
|
||||
@ -6,6 +6,7 @@ public interface IOrderService
|
||||
{
|
||||
Task<ApiResponse<Order>> CreateOrderAsync(CreateOrderRequest request);
|
||||
Task<ApiResponse<List<Order>>> GetOrdersByIdentityAsync(string identityReference);
|
||||
Task<ApiResponse<List<Order>>> GetOrdersByCustomerIdAsync(Guid customerId);
|
||||
Task<ApiResponse<Order>> GetOrderByIdAsync(Guid id);
|
||||
Task<ApiResponse<CryptoPayment>> CreatePaymentAsync(Guid orderId, int currency);
|
||||
Task<ApiResponse<List<CryptoPayment>>> GetOrderPaymentsAsync(Guid orderId);
|
||||
|
||||
@ -84,4 +84,26 @@ public class MessageService : IMessageService
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<CustomerMessage>?> GetCustomerMessagesAsync(Guid customerId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetAsync($"api/bot/messages/customer/{customerId}");
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var messages = await response.Content.ReadFromJsonAsync<List<CustomerMessage>>();
|
||||
return messages ?? new List<CustomerMessage>();
|
||||
}
|
||||
|
||||
_logger.LogWarning("Failed to get customer messages: {StatusCode}", response.StatusCode);
|
||||
return new List<CustomerMessage>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting customer messages");
|
||||
return new List<CustomerMessage>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,6 +65,30 @@ public class OrderService : IOrderService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<List<Order>>> GetOrdersByCustomerIdAsync(Guid customerId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetAsync($"api/orders/by-customer/{customerId}");
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var orders = await response.Content.ReadFromJsonAsync<List<Order>>();
|
||||
return ApiResponse<List<Order>>.Success(orders ?? new List<Order>());
|
||||
}
|
||||
|
||||
var error = await response.Content.ReadAsStringAsync();
|
||||
return ApiResponse<List<Order>>.Failure(error, response.StatusCode);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to get orders for customer {CustomerId}", customerId);
|
||||
return ApiResponse<List<Order>>.Failure(
|
||||
ex.Message,
|
||||
System.Net.HttpStatusCode.InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ApiResponse<Order>> GetOrderByIdAsync(Guid id)
|
||||
{
|
||||
try
|
||||
|
||||
@ -11,11 +11,13 @@ namespace LittleShop.Areas.Admin.Controllers;
|
||||
public class MessagesController : Controller
|
||||
{
|
||||
private readonly ICustomerMessageService _messageService;
|
||||
private readonly ICustomerService _customerService;
|
||||
private readonly ILogger<MessagesController> _logger;
|
||||
|
||||
public MessagesController(ICustomerMessageService messageService, ILogger<MessagesController> logger)
|
||||
public MessagesController(ICustomerMessageService messageService, ICustomerService customerService, ILogger<MessagesController> logger)
|
||||
{
|
||||
_messageService = messageService;
|
||||
_customerService = customerService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -28,9 +30,42 @@ public class MessagesController : Controller
|
||||
public async Task<IActionResult> Customer(Guid id)
|
||||
{
|
||||
var conversation = await _messageService.GetMessageThreadAsync(id);
|
||||
|
||||
// If no conversation exists yet, create an empty one for this customer
|
||||
if (conversation == null)
|
||||
{
|
||||
return NotFound();
|
||||
// Check if customer exists
|
||||
var customerExists = await _messageService.ValidateCustomerExistsAsync(id);
|
||||
if (!customerExists)
|
||||
{
|
||||
TempData["Error"] = "Customer not found.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
// Get customer information
|
||||
var customer = await _customerService.GetCustomerByIdAsync(id);
|
||||
if (customer == null)
|
||||
{
|
||||
TempData["Error"] = "Customer information not available.";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
// Create empty conversation structure for new conversation
|
||||
conversation = new MessageThreadDto
|
||||
{
|
||||
ThreadId = id,
|
||||
Subject = customer.DisplayName,
|
||||
CustomerId = id,
|
||||
CustomerName = customer.DisplayName,
|
||||
OrderId = null,
|
||||
OrderReference = null,
|
||||
StartedAt = DateTime.UtcNow,
|
||||
LastMessageAt = DateTime.UtcNow,
|
||||
MessageCount = 0,
|
||||
HasUnreadMessages = false,
|
||||
RequiresResponse = false,
|
||||
Messages = new List<CustomerMessageDto>()
|
||||
};
|
||||
}
|
||||
|
||||
return View(conversation);
|
||||
|
||||
@ -4,6 +4,15 @@
|
||||
ViewData["Title"] = $"Conversation with {Model.CustomerName}";
|
||||
}
|
||||
|
||||
@{
|
||||
// Get customer info if name is not loaded
|
||||
if (Model.CustomerName == "Loading...")
|
||||
{
|
||||
// This would need to be loaded via a service call
|
||||
Model.CustomerName = "Customer"; // Fallback
|
||||
}
|
||||
}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
@ -41,6 +50,17 @@
|
||||
</small>
|
||||
</div>
|
||||
<div class="card-body" style="max-height: 500px; overflow-y: auto;">
|
||||
@if (!Model.Messages.Any())
|
||||
{
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-comments fa-3x mb-3"></i>
|
||||
<h5>No messages yet</h5>
|
||||
<p>This is the start of your conversation with @Model.CustomerName.</p>
|
||||
<p class="small">Send a message below to begin the conversation.</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var message in Model.Messages.OrderBy(m => m.CreatedAt))
|
||||
{
|
||||
<div class="mb-3 @(message.Direction == 0 ? "ms-4" : "me-4")">
|
||||
@ -85,6 +105,7 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -118,7 +139,7 @@
|
||||
<!-- Reply Form -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6><i class="fas fa-reply"></i> Send Reply</h6>
|
||||
<h6><i class="fas fa-@(Model.MessageCount == 0 ? "comment" : "reply")"></i> @(Model.MessageCount == 0 ? "Start Conversation" : "Send Reply")</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="@Url.Action("Reply")">
|
||||
@ -126,7 +147,7 @@
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="content" class="form-label">Message</label>
|
||||
<textarea class="form-control" id="content" name="content" rows="4" required placeholder="Type your reply here..."></textarea>
|
||||
<textarea class="form-control" id="content" name="content" rows="4" required placeholder="@(Model.MessageCount == 0 ? "Start the conversation with this customer..." : "Type your reply here...")"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
@ -137,7 +158,7 @@
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-paper-plane"></i> Send Reply
|
||||
<i class="fas fa-paper-plane"></i> @(Model.MessageCount == 0 ? "Send Message" : "Send Reply")
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@ -110,6 +110,13 @@ public class BotMessagesController : ControllerBase
|
||||
return BadRequest($"Error creating customer message: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("customer/{customerId}")]
|
||||
public async Task<ActionResult<IEnumerable<CustomerMessageDto>>> GetCustomerMessages(Guid customerId)
|
||||
{
|
||||
var messages = await _messageService.GetCustomerMessagesAsync(customerId);
|
||||
return Ok(messages);
|
||||
}
|
||||
}
|
||||
|
||||
// TEMPORARY DTO FOR TESTING
|
||||
|
||||
@ -64,6 +64,14 @@ public class OrdersController : ControllerBase
|
||||
return Ok(orders);
|
||||
}
|
||||
|
||||
[HttpGet("by-customer/{customerId}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult<IEnumerable<OrderDto>>> GetOrdersByCustomerId(Guid customerId)
|
||||
{
|
||||
var orders = await _orderService.GetOrdersByCustomerIdAsync(customerId);
|
||||
return Ok(orders);
|
||||
}
|
||||
|
||||
[HttpGet("by-identity/{identityReference}/{id}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult<OrderDto>> GetOrderByIdentity(string identityReference, Guid id)
|
||||
|
||||
@ -6,6 +6,7 @@ public interface IOrderService
|
||||
{
|
||||
Task<IEnumerable<OrderDto>> GetAllOrdersAsync();
|
||||
Task<IEnumerable<OrderDto>> GetOrdersByIdentityAsync(string identityReference);
|
||||
Task<IEnumerable<OrderDto>> GetOrdersByCustomerIdAsync(Guid customerId);
|
||||
Task<OrderDto?> GetOrderByIdAsync(Guid id);
|
||||
Task<OrderDto> CreateOrderAsync(CreateOrderDto createOrderDto);
|
||||
Task<bool> UpdateOrderStatusAsync(Guid id, UpdateOrderStatusDto updateOrderStatusDto);
|
||||
|
||||
@ -46,6 +46,20 @@ public class OrderService : IOrderService
|
||||
return orders.Select(MapToDto);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<OrderDto>> GetOrdersByCustomerIdAsync(Guid customerId)
|
||||
{
|
||||
var orders = await _context.Orders
|
||||
.Include(o => o.Customer)
|
||||
.Include(o => o.Items)
|
||||
.ThenInclude(oi => oi.Product)
|
||||
.Include(o => o.Payments)
|
||||
.Where(o => o.CustomerId == customerId)
|
||||
.OrderByDescending(o => o.CreatedAt)
|
||||
.ToListAsync();
|
||||
|
||||
return orders.Select(MapToDto);
|
||||
}
|
||||
|
||||
public async Task<OrderDto?> GetOrderByIdAsync(Guid id)
|
||||
{
|
||||
var order = await _context.Orders
|
||||
|
||||
2447
LittleShop/TestAgent_Results/authentication_analysis.json
Normal file
2447
LittleShop/TestAgent_Results/authentication_analysis.json
Normal file
File diff suppressed because it is too large
Load Diff
6861
LittleShop/TestAgent_Results/coverage_analysis.json
Normal file
6861
LittleShop/TestAgent_Results/coverage_analysis.json
Normal file
File diff suppressed because it is too large
Load Diff
2940
LittleShop/TestAgent_Results/endpoint_discovery.json
Normal file
2940
LittleShop/TestAgent_Results/endpoint_discovery.json
Normal file
File diff suppressed because it is too large
Load Diff
1386
LittleShop/TestAgent_Results/error_detection.json
Normal file
1386
LittleShop/TestAgent_Results/error_detection.json
Normal file
File diff suppressed because it is too large
Load Diff
31
LittleShop/TestAgent_Results/executive_summary.json
Normal file
31
LittleShop/TestAgent_Results/executive_summary.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"ProjectPath": "C:\\Production\\Source\\LittleShop\\LittleShop",
|
||||
"ProjectType": "Project (ASP.NET Core)",
|
||||
"TotalEndpoints": 115,
|
||||
"AuthenticatedEndpoints": 78,
|
||||
"TestableStates": 3,
|
||||
"IdentifiedGaps": 224,
|
||||
"SuggestedTests": 190,
|
||||
"DeadLinks": 0,
|
||||
"HttpErrors": 97,
|
||||
"VisualIssues": 0,
|
||||
"SecurityInsights": 1,
|
||||
"PerformanceInsights": 1,
|
||||
"OverallTestCoverage": 16.956521739130434,
|
||||
"VisualConsistencyScore": 0,
|
||||
"CriticalRecommendations": [
|
||||
"CRITICAL: Test coverage is only 17.0% - implement comprehensive test suite",
|
||||
"HIGH: Address 97 HTTP errors in the application",
|
||||
"MEDIUM: Improve visual consistency - current score 0.0%",
|
||||
"HIGH: Address 224 testing gaps for comprehensive coverage"
|
||||
],
|
||||
"GeneratedFiles": [
|
||||
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\project_structure.json",
|
||||
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\authentication_analysis.json",
|
||||
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\endpoint_discovery.json",
|
||||
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\coverage_analysis.json",
|
||||
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\error_detection.json",
|
||||
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\visual_testing.json",
|
||||
"C:\\Production\\Source\\LittleShop\\LittleShop\\TestAgent_Results\\intelligent_analysis.json"
|
||||
]
|
||||
}
|
||||
79
LittleShop/TestAgent_Results/intelligent_analysis.json
Normal file
79
LittleShop/TestAgent_Results/intelligent_analysis.json
Normal file
@ -0,0 +1,79 @@
|
||||
{
|
||||
"BusinessLogicInsights": [
|
||||
{
|
||||
"Component": "Claude CLI Integration",
|
||||
"Insight": "Error analyzing business logic: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
|
||||
"Complexity": "Unknown",
|
||||
"PotentialIssues": [],
|
||||
"TestingRecommendations": [],
|
||||
"Priority": "Medium"
|
||||
}
|
||||
],
|
||||
"TestScenarioSuggestions": [
|
||||
{
|
||||
"ScenarioName": "Claude CLI Integration Error",
|
||||
"Description": "Error generating test scenarios: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
|
||||
"TestType": "",
|
||||
"Steps": [],
|
||||
"ExpectedOutcomes": [],
|
||||
"Priority": "Medium",
|
||||
"RequiredData": [],
|
||||
"Dependencies": []
|
||||
}
|
||||
],
|
||||
"SecurityInsights": [
|
||||
{
|
||||
"VulnerabilityType": "Analysis Error",
|
||||
"Location": "",
|
||||
"Description": "Error analyzing security: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
|
||||
"Severity": "Medium",
|
||||
"Recommendations": [],
|
||||
"TestingApproaches": []
|
||||
}
|
||||
],
|
||||
"PerformanceInsights": [
|
||||
{
|
||||
"Component": "Analysis Error",
|
||||
"PotentialBottleneck": "Error analyzing performance: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
|
||||
"Impact": "Unknown",
|
||||
"OptimizationSuggestions": [],
|
||||
"TestingStrategies": []
|
||||
}
|
||||
],
|
||||
"ArchitecturalRecommendations": [
|
||||
{
|
||||
"Category": "Analysis Error",
|
||||
"Recommendation": "Error generating architectural recommendations: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
|
||||
"Rationale": "",
|
||||
"Impact": "Unknown",
|
||||
"ImplementationSteps": []
|
||||
}
|
||||
],
|
||||
"GeneratedTestCases": [
|
||||
{
|
||||
"TestName": "Claude CLI Integration Error",
|
||||
"TestCategory": "Error",
|
||||
"Description": "Error generating test cases: Failed to execute Claude CLI: An error occurred trying to start process \u0027claude\u0027 with working directory \u0027C:\\Production\\Source\\TestAgent\u0027. The system cannot find the file specified.",
|
||||
"TestCode": "",
|
||||
"TestData": [],
|
||||
"ExpectedOutcome": "",
|
||||
"Reasoning": ""
|
||||
}
|
||||
],
|
||||
"Summary": {
|
||||
"TotalInsights": 4,
|
||||
"HighPriorityItems": 0,
|
||||
"GeneratedTestCases": 1,
|
||||
"SecurityIssuesFound": 1,
|
||||
"PerformanceOptimizations": 1,
|
||||
"KeyFindings": [
|
||||
"Performance optimization opportunities identified"
|
||||
],
|
||||
"NextSteps": [
|
||||
"Review and prioritize security recommendations",
|
||||
"Implement generated test cases",
|
||||
"Address high-priority business logic testing gaps",
|
||||
"Consider architectural improvements for better testability"
|
||||
]
|
||||
}
|
||||
}
|
||||
1669
LittleShop/TestAgent_Results/project_structure.json
Normal file
1669
LittleShop/TestAgent_Results/project_structure.json
Normal file
File diff suppressed because it is too large
Load Diff
17
LittleShop/TestAgent_Results/visual_testing.json
Normal file
17
LittleShop/TestAgent_Results/visual_testing.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"ConsistencyTests": [],
|
||||
"AuthStateComparisons": [],
|
||||
"ResponsiveTests": [],
|
||||
"ComponentTests": [],
|
||||
"Regressions": [],
|
||||
"Summary": {
|
||||
"TotalTests": 0,
|
||||
"PassedTests": 0,
|
||||
"FailedTests": 0,
|
||||
"ConsistencyViolations": 0,
|
||||
"ResponsiveIssues": 0,
|
||||
"VisualRegressions": 0,
|
||||
"OverallScore": 0,
|
||||
"Recommendations": []
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user