From 295a8b50250845acb86afb533fdb6f822b1ae455 Mon Sep 17 00:00:00 2001 From: Rashmika998 Date: Wed, 25 Feb 2026 11:53:50 +0530 Subject: [PATCH 1/5] Update time card stats endpoint and add conversation stats endpoint --- .../backend/modules/entity/types.bal | 9 +-- .../backend/modules/types/types.bal | 42 ++++++++++ apps/customer-portal/backend/service.bal | 81 ++++++++++++++----- apps/customer-portal/backend/utils.bal | 40 +++++++++ 4 files changed, 147 insertions(+), 25 deletions(-) diff --git a/apps/customer-portal/backend/modules/entity/types.bal b/apps/customer-portal/backend/modules/entity/types.bal index 50f50e1d24..0ac3f6bf85 100644 --- a/apps/customer-portal/backend/modules/entity/types.bal +++ b/apps/customer-portal/backend/modules/entity/types.bal @@ -474,12 +474,9 @@ public type ProjectCaseStatsResponse record {| # Project conversation statistics response. public type ProjectConversationStatsResponse record {| - # Active chat count - int activeCount; - # Session count - int sessionCount; - # Resolved count - int resolvedCount; + # Count of conversations by state + ChoiceListItem[] stateCount; + json...; |}; # Project deployment statistics response. diff --git a/apps/customer-portal/backend/modules/types/types.bal b/apps/customer-portal/backend/modules/types/types.bal index 3c603008aa..ed150590bd 100644 --- a/apps/customer-portal/backend/modules/types/types.bal +++ b/apps/customer-portal/backend/modules/types/types.bal @@ -993,3 +993,45 @@ public type ConversationResponse record {| string updatedBy; json...; |}; + +# Conversation state IDs. +public type ConverstaionStateIds record {| + # Open state ID + int open; + # Active state ID + int active; + # Converted state ID + int converted; + # Resolved state ID + int resolved; + # Abandoned state ID + int abandonded; +|}; + +# Overall conversation statistics for a project. +public type OverallConversationStats record {| + # Chat sessions count + int sessionCount?; + # Resolved chats count + int resolvedCount?; + # Open chats count + int openCount?; + # Abandoned chats count + int abandondedCount?; + # Total messages count + int totalCount?; + # Active chats count + int activeCount?; + # Converted chats count + int convertedCount?; +|}; + +# Conversation statistics by state for a project. +public type ConversationStats record {| + # Resolved chats count + int resolvedCount?; + # Open chats count + int openCount?; + # Abandoned chats count + int abandondedCount?; +|}; diff --git a/apps/customer-portal/backend/service.bal b/apps/customer-portal/backend/service.bal index 5a5746931f..23bf4f4614 100644 --- a/apps/customer-portal/backend/service.bal +++ b/apps/customer-portal/backend/service.bal @@ -607,6 +607,7 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { log:printError(ERR_MSG_CONVERSATION_STATISTICS, conversationStats); // To return other stats even if conversation stats retrieval fails, error will not be returned. } + types:OverallConversationStats mappedConversationStats = getConversationStats(conversationStats); // Fetch deployment stats entity:ProjectDeploymentStatsResponse|error deploymentStats = @@ -627,8 +628,7 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { projectStats: { openCases: caseStats is entity:ProjectCaseStatsResponse ? getOpenCasesCountFromProjectCasesStats(caseStats) : (), - activeChats: conversationStats is entity:ProjectConversationStatsResponse ? - conversationStats.activeCount : (), + activeChats: mappedConversationStats.activeCount, deployments: deploymentStats is entity:ProjectDeploymentStatsResponse ? deploymentStats.totalCount : (), slaStatus: projectActivityStats is entity:ProjectStatsResponse ? projectActivityStats.slaStatus : () }, @@ -673,6 +673,61 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { return mapCaseStats(caseStats); } + # Get conversation statistics for a project by ID. + # + # + id - ID of the project + # + return - Conversation statistics overview or error response + resource function get projects/[entity:IdString id]/stats/conversations(http:RequestContext ctx, + string[]? caseTypes) + returns types:ConversationStats|http:Unauthorized|http:Forbidden|http:InternalServerError { + + authorization:UserInfoPayload|error userInfo = ctx.getWithType(authorization:HEADER_USER_INFO); + if userInfo is error { + return { + body: { + message: ERR_MSG_USER_INFO_HEADER_NOT_FOUND + } + }; + } + + entity:ProjectConversationStatsResponse|error conversationStats = + entity:getConversationStatsForProject(userInfo.idToken, id); + if conversationStats is error { + if getStatusCode(conversationStats) == http:STATUS_UNAUTHORIZED { + log:printWarn(string `User: ${userInfo.userId} is not authorized to access the customer portal!`); + return { + body: { + message: ERR_MSG_UNAUTHORIZED_ACCESS + } + }; + } + + if getStatusCode(conversationStats) == http:STATUS_FORBIDDEN { + log:printWarn(string `Access to conversation statistics is forbidden for user: ${userInfo.userId}`); + return { + body: { + message: "Access to conversation statistics is forbidden!" + } + }; + } + string customError = "Failed to retrieve conversation statistics for the project."; + log:printError(customError, conversationStats); + return { + body: { + message: customError + } + }; + } + + types:OverallConversationStats mappedConversationStats = getConversationStats(conversationStats); + + return { + openCount: mappedConversationStats.openCount, + resolvedCount: mappedConversationStats.resolvedCount, + abandondedCount: mappedConversationStats.abandondedCount + }; + } + # Get project support statistics by ID. # # + id - ID of the project @@ -704,15 +759,13 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { log:printError(ERR_MSG_CONVERSATION_STATISTICS, conversationStats); // To return other stats even if conversation stats retrieval fails, error will not be returned. } + types:OverallConversationStats mappedConversationStats = getConversationStats(conversationStats); return { totalCases: caseStats is entity:ProjectCaseStatsResponse ? caseStats.totalCount : (), - activeChats: conversationStats is entity:ProjectConversationStatsResponse ? - conversationStats.activeCount : (), - sessionChats: conversationStats is entity:ProjectConversationStatsResponse ? - conversationStats.sessionCount : (), - resolvedChats: conversationStats is entity:ProjectConversationStatsResponse ? - conversationStats.resolvedCount : () + activeChats: mappedConversationStats.activeCount, + sessionChats: mappedConversationStats.sessionCount, + resolvedChats: mappedConversationStats.resolvedCount }; } @@ -1409,16 +1462,6 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { } }; } - if getStatusCode(commentsResponse) == http:STATUS_FORBIDDEN { - log:printWarn(string `User: ${userInfo.userId} is forbidden to access comments for case with ID: ${ - id}!`); - return { - body: { - message: "You're not authorized to access the comments for the requested case. " + - "Please check your access permissions or contact support." - } - }; - } string customError = "Failed to retrieve comments."; log:printError(customError, commentsResponse); return { @@ -2791,7 +2834,7 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { # + startDate - Start date for the statistics (optional) # + endDate - End date for the statistics (optional) # + return - Time card statistics for the project or an error - resource function get projects/[string id]/time\-cards/stats(http:RequestContext ctx, entity:Date? startDate, + resource function get projects/[string id]/stats/time\-cards(http:RequestContext ctx, entity:Date? startDate, entity:Date? endDate) returns entity:ProjectTimeCardStatsResponse|http:Unauthorized|http:Forbidden|http:InternalServerError { diff --git a/apps/customer-portal/backend/utils.bal b/apps/customer-portal/backend/utils.bal index c628444bda..994bf7af7c 100644 --- a/apps/customer-portal/backend/utils.bal +++ b/apps/customer-portal/backend/utils.bal @@ -20,6 +20,7 @@ import ballerina/http; import ballerina/log; configurable int stateIdOpen = 1; +configurable types:ConverstaionStateIds conversationStateIds = {open: 1, active: 2, resolved: 3, converted: 4, abandonded: 5}; # Search cases for a given project. # @@ -565,3 +566,42 @@ public isolated function mapConversationResponse(entity:ConversationResponse res updatedOn: response.updatedOn }; } + +# Get conversation stats from project conversation stats response. +# +# + response - Project conversation stats response from the entity service +# + return - Conversation stats with counts for each conversation state +public isolated function getConversationStats(entity:ProjectConversationStatsResponse|error response) + returns types:OverallConversationStats { + + if response is entity:ProjectConversationStatsResponse { + types:ReferenceItem[] mappedConversationStats = from entity:ChoiceListItem item in response.stateCount + select {id: item.id.toString(), label: item.label, count: item.count}; + + types:ReferenceItem[] openCases = mappedConversationStats.filter(stat => + stat.id == conversationStateIds.open.toString()); + types:ReferenceItem[] activeCases = mappedConversationStats.filter(stat => + stat.id == conversationStateIds.active.toString()); + types:ReferenceItem[] resolvedCases = mappedConversationStats.filter(stat => + stat.id == conversationStateIds.resolved.toString()); + types:ReferenceItem[] convertedCases = mappedConversationStats.filter(stat => + stat.id == conversationStateIds.converted.toString()); + types:ReferenceItem[] abandondedCases = mappedConversationStats.filter(stat => + stat.id == conversationStateIds.abandonded.toString()); + + return { + openCount: openCases.length() > 0 ? openCases[0].count : (), + activeCount: activeCases.length() > 0 ? activeCases[0].count : (), + resolvedCount: resolvedCases.length() > 0 ? resolvedCases[0].count : (), + convertedCount: convertedCases.length() > 0 ? convertedCases[0].count : (), + abandondedCount: abandondedCases.length() > 0 ? abandondedCases[0].count : () + }; + } + return { + openCount: (), + activeCount: (), + resolvedCount: (), + convertedCount: (), + abandondedCount: () + }; +} From 906511b2820958b031fa8ec4f9792e8a4bf5f241 Mon Sep 17 00:00:00 2001 From: Rashmika998 Date: Wed, 25 Feb 2026 13:33:02 +0530 Subject: [PATCH 2/5] Update ongoing cases count --- .../backend/modules/entity/constants.bal | 5 ---- .../backend/modules/entity/types.bal | 18 ++++++++++++ .../backend/modules/entity/utils.bal | 13 ++++++++- .../backend/modules/types/types.bal | 4 +-- apps/customer-portal/backend/service.bal | 6 ++-- apps/customer-portal/backend/utils.bal | 28 +++++++++++++++++-- 6 files changed, 62 insertions(+), 12 deletions(-) diff --git a/apps/customer-portal/backend/modules/entity/constants.bal b/apps/customer-portal/backend/modules/entity/constants.bal index 467f4c382c..4dd26d8622 100644 --- a/apps/customer-portal/backend/modules/entity/constants.bal +++ b/apps/customer-portal/backend/modules/entity/constants.bal @@ -22,11 +22,6 @@ const DEFAULT_LIMIT = 20; public const PENDING_ON_WSO2 = 2; public const CANCELED = 6; -# Valid case state values -const WAITING_ON_WSO2 = 1003; -const CLOSED = 3; -const REOPENED = 1006; - # Valid conversation state values public const ACTIVE = 2; public const RESOLVED = 3; diff --git a/apps/customer-portal/backend/modules/entity/types.bal b/apps/customer-portal/backend/modules/entity/types.bal index 0ac3f6bf85..abc1ca0b5b 100644 --- a/apps/customer-portal/backend/modules/entity/types.bal +++ b/apps/customer-portal/backend/modules/entity/types.bal @@ -1290,3 +1290,21 @@ public type ProjectTimeCardStatsResponse record {| decimal nonBillableHours; json...; |}; + +# case state IDs. +public type CaseStateIds record {| + # Open state ID + int open; + # Close state ID + int closed; + # Solution proposed ID + int solutionProposed; + # Work in progress state ID + int workInProgress; + # Awaiting info ID + int awaitingInfo; + # Waiting on WSO2 ID + int waitingOnWso2; + # Reopened state ID + int reopened; +|}; diff --git a/apps/customer-portal/backend/modules/entity/utils.bal b/apps/customer-portal/backend/modules/entity/utils.bal index b06f899594..53dece5329 100644 --- a/apps/customer-portal/backend/modules/entity/utils.bal +++ b/apps/customer-portal/backend/modules/entity/utils.bal @@ -15,6 +15,16 @@ // under the License. import ballerina/time; +public final CaseStateIds & readonly caseStateIds = { + open: 1, + closed: 3, + waitingOnWso2: 1003, + reopened: 1006, + awaitingInfo: 18, + solutionProposed: 6, + workInProgress: 10 +}; + # Generate authorization headers. # # + token - ID token for authorization @@ -142,7 +152,8 @@ public isolated function validateDeploymentUpdatePayload(DeploymentUpdatePayload # + payload - Case update payload # + return - Error message if validation fails, nil otherwise public isolated function validateCaseUpdatePayload(CaseUpdatePayload payload) returns string? { - if payload.stateKey != CLOSED && payload.stateKey != REOPENED && payload.stateKey != WAITING_ON_WSO2 { + if payload.stateKey != caseStateIds.closed && payload.stateKey != caseStateIds.reopened && + payload.stateKey != caseStateIds.waitingOnWso2 { return "Invalid status. Allowed values are Waiting on WSO2, Closed, or Reopened."; } return; diff --git a/apps/customer-portal/backend/modules/types/types.bal b/apps/customer-portal/backend/modules/types/types.bal index ed150590bd..310859303c 100644 --- a/apps/customer-portal/backend/modules/types/types.bal +++ b/apps/customer-portal/backend/modules/types/types.bal @@ -260,8 +260,8 @@ public type ProjectCaseStats record {| # Project support statistics. public type ProjectSupportStats record {| - # Total cases count - int totalCases?; + # Ongoing cases count + int? ongoingCases?; # Active chats count int activeChats?; # Session chats count diff --git a/apps/customer-portal/backend/service.bal b/apps/customer-portal/backend/service.bal index 23bf4f4614..828e5a6a9b 100644 --- a/apps/customer-portal/backend/service.bal +++ b/apps/customer-portal/backend/service.bal @@ -674,7 +674,7 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { } # Get conversation statistics for a project by ID. - # + # # + id - ID of the project # + return - Conversation statistics overview or error response resource function get projects/[entity:IdString id]/stats/conversations(http:RequestContext ctx, @@ -761,8 +761,10 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { } types:OverallConversationStats mappedConversationStats = getConversationStats(conversationStats); + int? ongoingCases = getOngoingCasesCount(caseStats); + return { - totalCases: caseStats is entity:ProjectCaseStatsResponse ? caseStats.totalCount : (), + ongoingCases: ongoingCases is int ? ongoingCases : (), activeChats: mappedConversationStats.activeCount, sessionChats: mappedConversationStats.sessionCount, resolvedChats: mappedConversationStats.resolvedCount diff --git a/apps/customer-portal/backend/utils.bal b/apps/customer-portal/backend/utils.bal index 994bf7af7c..594283fb96 100644 --- a/apps/customer-portal/backend/utils.bal +++ b/apps/customer-portal/backend/utils.bal @@ -20,7 +20,8 @@ import ballerina/http; import ballerina/log; configurable int stateIdOpen = 1; -configurable types:ConverstaionStateIds conversationStateIds = {open: 1, active: 2, resolved: 3, converted: 4, abandonded: 5}; +configurable types:ConverstaionStateIds conversationStateIds = + {open: 1, active: 2, resolved: 3, converted: 4, abandonded: 5}; # Search cases for a given project. # @@ -567,6 +568,28 @@ public isolated function mapConversationResponse(entity:ConversationResponse res }; } +# Get ongoing cases count from project case stats response. +# +# + response - Project case stats response from the entity service +# + return - Count of ongoing cases, or 0 if not available +public isolated function getOngoingCasesCount(entity:ProjectCaseStatsResponse|error response) returns int? { + if response is entity:ProjectCaseStatsResponse { + types:ReferenceItem[] stateCount = from entity:ChoiceListItem item in response.stateCount + where item.id != entity:caseStateIds.closed && item.id != entity:caseStateIds.solutionProposed + select {id: item.id.toString(), label: item.label, count: item.count}; + if stateCount.length() > 0 { + int ongoingCasesCount = 0; + foreach types:ReferenceItem stat in stateCount { + int statCount = stat.count ?: 0; + ongoingCasesCount += statCount; + } + return ongoingCasesCount; + } + return 0; + } + return; +} + # Get conversation stats from project conversation stats response. # # + response - Project conversation stats response from the entity service @@ -602,6 +625,7 @@ public isolated function getConversationStats(entity:ProjectConversationStatsRes activeCount: (), resolvedCount: (), convertedCount: (), - abandondedCount: () + abandondedCount: (), + sessionCount: () }; } From 9ce8ad8ffa57a71f570c1c3909838b7674b5fdd6 Mon Sep 17 00:00:00 2001 From: Rashmika998 Date: Wed, 25 Feb 2026 13:53:09 +0530 Subject: [PATCH 3/5] Fix typos and update default configurables --- .../backend/modules/entity/types.bal | 14 ++++++++++++++ .../backend/modules/entity/utils.bal | 4 +++- .../backend/modules/types/types.bal | 18 ++---------------- apps/customer-portal/backend/service.bal | 2 +- apps/customer-portal/backend/utils.bal | 17 ++++++++--------- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/apps/customer-portal/backend/modules/entity/types.bal b/apps/customer-portal/backend/modules/entity/types.bal index abc1ca0b5b..90d54b7dba 100644 --- a/apps/customer-portal/backend/modules/entity/types.bal +++ b/apps/customer-portal/backend/modules/entity/types.bal @@ -1308,3 +1308,17 @@ public type CaseStateIds record {| # Reopened state ID int reopened; |}; + +# Conversation state IDs. +public type ConverstaionStateIds record {| + # Open state ID + int open; + # Active state ID + int active; + # Converted state ID + int converted; + # Resolved state ID + int resolved; + # Abandoned state ID + int abandonded; +|}; diff --git a/apps/customer-portal/backend/modules/entity/utils.bal b/apps/customer-portal/backend/modules/entity/utils.bal index 53dece5329..ccbe25998f 100644 --- a/apps/customer-portal/backend/modules/entity/utils.bal +++ b/apps/customer-portal/backend/modules/entity/utils.bal @@ -15,7 +15,7 @@ // under the License. import ballerina/time; -public final CaseStateIds & readonly caseStateIds = { +public configurable CaseStateIds & readonly caseStateIds = { open: 1, closed: 3, waitingOnWso2: 1003, @@ -24,6 +24,8 @@ public final CaseStateIds & readonly caseStateIds = { solutionProposed: 6, workInProgress: 10 }; +public configurable ConverstaionStateIds & readonly conversationStateIds = + {open: 1, active: 2, resolved: 3, converted: 4, abandonded: 5}; # Generate authorization headers. # diff --git a/apps/customer-portal/backend/modules/types/types.bal b/apps/customer-portal/backend/modules/types/types.bal index 310859303c..90063c21c4 100644 --- a/apps/customer-portal/backend/modules/types/types.bal +++ b/apps/customer-portal/backend/modules/types/types.bal @@ -994,20 +994,6 @@ public type ConversationResponse record {| json...; |}; -# Conversation state IDs. -public type ConverstaionStateIds record {| - # Open state ID - int open; - # Active state ID - int active; - # Converted state ID - int converted; - # Resolved state ID - int resolved; - # Abandoned state ID - int abandonded; -|}; - # Overall conversation statistics for a project. public type OverallConversationStats record {| # Chat sessions count @@ -1017,7 +1003,7 @@ public type OverallConversationStats record {| # Open chats count int openCount?; # Abandoned chats count - int abandondedCount?; + int abandonedCount?; # Total messages count int totalCount?; # Active chats count @@ -1033,5 +1019,5 @@ public type ConversationStats record {| # Open chats count int openCount?; # Abandoned chats count - int abandondedCount?; + int abandonedCount?; |}; diff --git a/apps/customer-portal/backend/service.bal b/apps/customer-portal/backend/service.bal index 828e5a6a9b..898b8198e6 100644 --- a/apps/customer-portal/backend/service.bal +++ b/apps/customer-portal/backend/service.bal @@ -724,7 +724,7 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { return { openCount: mappedConversationStats.openCount, resolvedCount: mappedConversationStats.resolvedCount, - abandondedCount: mappedConversationStats.abandondedCount + abandonedCount: mappedConversationStats.abandonedCount }; } diff --git a/apps/customer-portal/backend/utils.bal b/apps/customer-portal/backend/utils.bal index 594283fb96..cd7eedcac3 100644 --- a/apps/customer-portal/backend/utils.bal +++ b/apps/customer-portal/backend/utils.bal @@ -20,8 +20,6 @@ import ballerina/http; import ballerina/log; configurable int stateIdOpen = 1; -configurable types:ConverstaionStateIds conversationStateIds = - {open: 1, active: 2, resolved: 3, converted: 4, abandonded: 5}; # Search cases for a given project. # @@ -602,22 +600,23 @@ public isolated function getConversationStats(entity:ProjectConversationStatsRes select {id: item.id.toString(), label: item.label, count: item.count}; types:ReferenceItem[] openCases = mappedConversationStats.filter(stat => - stat.id == conversationStateIds.open.toString()); + stat.id == entity:conversationStateIds.open.toString()); types:ReferenceItem[] activeCases = mappedConversationStats.filter(stat => - stat.id == conversationStateIds.active.toString()); + stat.id == entity:conversationStateIds.active.toString()); types:ReferenceItem[] resolvedCases = mappedConversationStats.filter(stat => - stat.id == conversationStateIds.resolved.toString()); + stat.id == entity:conversationStateIds.resolved.toString()); types:ReferenceItem[] convertedCases = mappedConversationStats.filter(stat => - stat.id == conversationStateIds.converted.toString()); + stat.id == entity:conversationStateIds.converted.toString()); types:ReferenceItem[] abandondedCases = mappedConversationStats.filter(stat => - stat.id == conversationStateIds.abandonded.toString()); + stat.id == entity:conversationStateIds.abandonded.toString()); + // TODO: Add session chats after entity service supports it return { openCount: openCases.length() > 0 ? openCases[0].count : (), activeCount: activeCases.length() > 0 ? activeCases[0].count : (), resolvedCount: resolvedCases.length() > 0 ? resolvedCases[0].count : (), convertedCount: convertedCases.length() > 0 ? convertedCases[0].count : (), - abandondedCount: abandondedCases.length() > 0 ? abandondedCases[0].count : () + abandonedCount: abandondedCases.length() > 0 ? abandondedCases[0].count : () }; } return { @@ -625,7 +624,7 @@ public isolated function getConversationStats(entity:ProjectConversationStatsRes activeCount: (), resolvedCount: (), convertedCount: (), - abandondedCount: (), + abandonedCount: (), sessionCount: () }; } From dba2d7a1bd921674727126fab4fab469e1750627 Mon Sep 17 00:00:00 2001 From: Rashmika998 Date: Wed, 25 Feb 2026 14:02:21 +0530 Subject: [PATCH 4/5] Refactor code --- apps/customer-portal/backend/service.bal | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/customer-portal/backend/service.bal b/apps/customer-portal/backend/service.bal index 898b8198e6..66a61f271c 100644 --- a/apps/customer-portal/backend/service.bal +++ b/apps/customer-portal/backend/service.bal @@ -677,8 +677,7 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { # # + id - ID of the project # + return - Conversation statistics overview or error response - resource function get projects/[entity:IdString id]/stats/conversations(http:RequestContext ctx, - string[]? caseTypes) + resource function get projects/[entity:IdString id]/stats/conversations(http:RequestContext ctx) returns types:ConversationStats|http:Unauthorized|http:Forbidden|http:InternalServerError { authorization:UserInfoPayload|error userInfo = ctx.getWithType(authorization:HEADER_USER_INFO); @@ -2768,7 +2767,7 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { # + id - ID of the project # + payload - Time card search payload containing filters and pagination info # + return - List of time cards matching the criteria or an error - resource function post projects/[string id]/time\-cards/search(http:RequestContext ctx, + resource function post projects/[entity:IdString id]/time\-cards/search(http:RequestContext ctx, types:TimeCardSearchPayload payload) returns http:Ok|http:BadRequest|http:Unauthorized|http:Forbidden|http:InternalServerError { From c49cc9bec0fa69c243154801b9b2b558bd223f9e Mon Sep 17 00:00:00 2001 From: Rashmika998 Date: Wed, 25 Feb 2026 14:18:10 +0530 Subject: [PATCH 5/5] Update returned objects --- apps/customer-portal/backend/service.bal | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/customer-portal/backend/service.bal b/apps/customer-portal/backend/service.bal index 66a61f271c..c11e8c7613 100644 --- a/apps/customer-portal/backend/service.bal +++ b/apps/customer-portal/backend/service.bal @@ -718,13 +718,10 @@ service http:InterceptableService / on new http:Listener(9090, listenerConf) { }; } - types:OverallConversationStats mappedConversationStats = getConversationStats(conversationStats); + types:OverallConversationStats { openCount, resolvedCount, abandonedCount} = + getConversationStats(conversationStats); - return { - openCount: mappedConversationStats.openCount, - resolvedCount: mappedConversationStats.resolvedCount, - abandonedCount: mappedConversationStats.abandonedCount - }; + return { openCount, resolvedCount, abandonedCount }; } # Get project support statistics by ID.