Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7de31f8
Simplify Grid props in CasesOverviewStatCard
dileepapeiris Feb 9, 2026
15bbc45
Remove bottom margin from NoveraChatBanner
dileepapeiris Feb 9, 2026
3c6b4f5
Add tests for ChatHistoryList component
dileepapeiris Feb 9, 2026
d581b00
Add tests for support utils
dileepapeiris Feb 9, 2026
447e96a
Add Chat History overview to Support page
dileepapeiris Feb 9, 2026
25aff2a
tests: mock deps and add SupportPage test
dileepapeiris Feb 9, 2026
3c7e347
Add SUPPORT_OVERVIEW_CHAT_LIMIT constant
dileepapeiris Feb 9, 2026
c09a9c9
Expose sx prop and make card full-size
dileepapeiris Feb 9, 2026
807fda0
Make outstanding cases skeleton full width
dileepapeiris Feb 9, 2026
acc3ab3
Truncate case titles and set container width
dileepapeiris Feb 9, 2026
5106887
Add ChatHistorySkeleton component
dileepapeiris Feb 9, 2026
c2a74b2
Add ChatHistoryList component
dileepapeiris Feb 9, 2026
9fe189e
Add chat status helpers & tweak styles
dileepapeiris Feb 9, 2026
b060787
Update tests to use getChatStatusColor
dileepapeiris Feb 9, 2026
975b116
Merge branch 'customer-portal-milestone-1' into feat/add-support-nove…
dileepapeiris Feb 9, 2026
fbe4a12
Fix imports and pluralization in customer portal
dileepapeiris Feb 9, 2026
87c5930
Merge branch 'feat/add-support-novera-chat-history' of https://github…
dileepapeiris Feb 9, 2026
d4bf1cb
SupportOverviewCard: accept array sx; update tests
dileepapeiris Feb 9, 2026
e62a34f
Add ChatActionState type and use in getChatActionColor
dileepapeiris Feb 9, 2026
180241f
Switch to Card components and add Chat types
dileepapeiris Feb 9, 2026
0e23e42
Lowercase ChatStatus checks in getChatStatusColor
dileepapeiris Feb 9, 2026
e8ee2b8
Use Form.CardButton instead of Card in chat list
dileepapeiris Feb 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/customer-portal/webapp/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import CreateCasePage from "@pages/CreateCasePage";
import AppLayout from "@layouts/AppLayout";
import { LoaderProvider } from "@context/linear-loader/LoaderContext";
import LoginPage from "@pages/LoginPage";
import AuthGuard from "@/AuthGuard";
import AuthGuard from "./AuthGuard";

export default function App(): JSX.Element {
return (
Expand Down
2 changes: 1 addition & 1 deletion apps/customer-portal/webapp/src/AppWithConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type { JSX } from "react";
import { OxygenUIThemeProvider } from "@wso2/oxygen-ui";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import App from "@/App";
import App from "./App";
import { AsgardeoProvider } from "@asgardeo/react";
import { themeConfig } from "@config/themeConfig";
import { loggerConfig } from "@config/loggerConfig";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { render, screen } from "@testing-library/react";
import { useAsgardeo } from "@asgardeo/react";
import { MemoryRouter, Route, Routes } from "react-router";
import AuthGuard from "@AuthGuard";
import AuthGuard from "../AuthGuard";
import { describe, it, expect, vi, beforeEach } from "vitest";
import React from "react";
import { useLoader } from "@context/linear-loader/LoaderContext";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,7 @@ export default function CasesOverviewStatCard({
}: CasesOverviewStatCardProps): JSX.Element {
return (
<Box>
<Grid
container
spacing={2}
sx={{
mb: 3,
}}
>
<Grid container spacing={2}>
{SUPPORT_STAT_CONFIGS.map((stat) => {
const SecondaryIcon = stat.secondaryIcon;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export default function NoveraChatBanner(): JSX.Element {
return (
<Paper
sx={{
mb: 8,
p: 3,
display: "flex",
alignItems: "center",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright (c) 2026 WSO2 LLC. (https://www.wso2.com).
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import {
Box,
Button,
CardActions,
CardContent,
Chip,
Form,
Stack,
Typography,
alpha,
type Theme,
} from "@wso2/oxygen-ui";
import {
Bot,
CircleCheck,
Clock,
ExternalLink,
Play,
} from "@wso2/oxygen-ui-icons-react";
import type { JSX } from "react";
import type { ChatHistoryItem } from "@models/responses";
import { ChatAction } from "@constants/supportConstants";
import {
getChatActionColor,
getChatStatusAction,
getChatStatusColor,
resolveColorFromTheme,
} from "@utils/support";
import ChatHistorySkeleton from "@/components/support/support-overview-cards/ChatHistorySkeleton";
import { CardButton } from "@wso2/oxygen-ui/components/Form/CardButton";

export interface ChatHistoryListProps {
items: ChatHistoryItem[];
isLoading?: boolean;
onItemAction?: (chatId: string, action: ChatAction) => void;
}

/**
* Renders a list of chat history rows for the support overview card.
*
* @param {ChatHistoryListProps} props - Items and optional action handler.
* @returns {JSX.Element} The list of chat rows.
*/
export default function ChatHistoryList({
items,
isLoading,
onItemAction,
}: ChatHistoryListProps): JSX.Element {
if (isLoading) {
return <ChatHistorySkeleton />;
}

if (items.length === 0) {
return (
<Typography variant="body2" color="text.secondary">
No chat history.
</Typography>
);
}

return (
<Box
sx={{ display: "flex", flexDirection: "column", gap: 1.5, width: "100%" }}
>
{items.map((item) => {
const action = getChatStatusAction(item.status);
const StatusIcon = action === ChatAction.VIEW ? CircleCheck : Clock;
const chipColorPath = getChatStatusColor(item.status);

return (
<Form.CardButton
key={item.chatId}
onClick={() => onItemAction?.(item.chatId, action)}
sx={{
p: 2,
display: "flex",
flexDirection: "column",
alignItems: "stretch",
gap: 1,
cursor: "pointer",
"&:hover": {
bgcolor: "action.hover",
},
}}
>
<CardContent
sx={{
p: 0,
display: "flex",
flexDirection: "column",
gap: 0.5,
}}
>
<Box sx={{ display: "flex", gap: 1.5, alignItems: "center" }}>
<Box sx={{ display: "flex", flexShrink: 0 }}>
<Bot size={20} />
</Box>
<Box sx={{ minWidth: 0, flexGrow: 1 }}>
<Typography
variant="body2"
fontWeight={500}
color="text.primary"
sx={{
overflow: "hidden",
textOverflow: "ellipsis",
display: "-webkit-box",
WebkitLineClamp: 1,
WebkitBoxOrient: "vertical",
}}
>
{item.title}
</Typography>
</Box>
</Box>
<Box sx={{ pl: 4 }}>
<Stack direction="row" spacing={1} alignItems="center">
<Typography variant="caption" color="text.secondary">
{item.startedTime}
</Typography>
<Typography variant="caption" color="text.secondary">
•
</Typography>
<Typography variant="caption" color="text.secondary">
{item.messages}{" "}
{item.messages === 1 ? "message" : "messages"}
</Typography>
<Typography variant="caption" color="text.secondary">
•
</Typography>
<Typography variant="caption" color="text.secondary">
Comment thread
dileepapeiris marked this conversation as resolved.
{item.kbArticles}{" "}
{item.kbArticles === 1 ? "KB article" : "KB articles"}
</Typography>
</Stack>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</Box>
</CardContent>

<CardActions sx={{ p: 0, justifyContent: "space-between" }}>
<Chip
size="small"
variant="outlined"
label={item.status}
sx={{
px: 0.5,
bgcolor: (theme: Theme) =>
alpha(resolveColorFromTheme(chipColorPath, theme), 0.1),
color: chipColorPath,
"& .MuiChip-icon": {
color: "inherit",
},
}}
icon={<StatusIcon size={12} />}
/>
<Button
size="small"
variant="text"
color={getChatActionColor(action)}
disableRipple
onClick={(e) => {
e.stopPropagation();
onItemAction?.(item.chatId, action);
}}
startIcon={
action === ChatAction.VIEW ? (
<ExternalLink size={12} />
) : (
<Play size={12} />
)
}
sx={{
textTransform: "none",
fontWeight: 500,
minWidth: 0,
p: 0,
}}
>
{action === ChatAction.VIEW ? "View" : "Resume"}
</Button>
</CardActions>
</Form.CardButton>
);
})}
</Box>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) 2026 WSO2 LLC. (https://www.wso2.com).
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import { Box, Form, Skeleton } from "@wso2/oxygen-ui";
import { type JSX } from "react";
import { SUPPORT_OVERVIEW_CHAT_LIMIT } from "@constants/supportConstants";

/**
* Renders a list of loading skeletons for the chat history list.
*
* @returns {JSX.Element} The skeleton list.
*/
export default function ChatHistorySkeleton(): JSX.Element {
return (
<Box
sx={{ display: "flex", flexDirection: "column", gap: 1.5, width: "100%" }}
>
{Array.from({ length: SUPPORT_OVERVIEW_CHAT_LIMIT }).map((_, index) => (
<Form.CardButton
key={`skeleton-${index}`}
sx={{
p: 2,
display: "flex",
flexDirection: "column",
alignItems: "stretch",
gap: 1.5,
pointerEvents: "none",
}}
>
<Form.CardContent
sx={{
p: 0,
display: "flex",
flexDirection: "column",
gap: 0.5,
}}
>
<Box sx={{ display: "flex", gap: 1.5, alignItems: "center" }}>
<Box sx={{ display: "flex", flexShrink: 0 }}>
<Skeleton variant="circular" width={20} height={20} />
</Box>
<Box sx={{ flexGrow: 1 }}>
<Skeleton variant="text" width="90%" height={24} />
</Box>
</Box>
<Box sx={{ pl: 4 }}>
<Box sx={{ display: "flex", gap: 1, alignItems: "center" }}>
<Skeleton variant="text" width={40} height={16} />
<Skeleton variant="text" width={4} height={16} />
<Skeleton variant="text" width={60} height={16} />
</Box>
</Box>
</Form.CardContent>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<Skeleton variant="rounded" width={80} height={20} />
<Skeleton variant="text" width={60} height={16} />
</Box>
</Form.CardButton>
))}
</Box>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ export default function OutstandingCasesList({
}

return (
<Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
<Box
sx={{ display: "flex", flexDirection: "column", gap: 1.5, width: "100%" }}
>
{cases.map((c) => {
const colorPath = getStatusColor(c.status?.label);
const resolvedColor = resolveColorFromTheme(colorPath, theme);
Expand Down Expand Up @@ -105,15 +107,21 @@ export default function OutstandingCasesList({
/>

<Form.CardContent sx={{ p: 0 }}>
<Typography
variant="body2"
color="text.primary"
sx={{
overflow: "hidden",
}}
>
{c.title}
</Typography>
<Box sx={{ minWidth: 0 }}>
<Typography
variant="body2"
color="text.primary"
sx={{
overflow: "hidden",
textOverflow: "ellipsis",
display: "-webkit-box",
WebkitLineClamp: 1,
WebkitBoxOrient: "vertical",
}}
>
{c.title}
</Typography>
</Box>
</Form.CardContent>

<Form.CardActions sx={{ p: 0, justifyContent: "space-between" }}>
Expand All @@ -126,7 +134,6 @@ export default function OutstandingCasesList({
px: 0.5,
bgcolor: alpha(resolvedColor, 0.1),
color: resolvedColor,
borderColor: "transparent",
"& .MuiChip-icon": {
color: "inherit",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import { type JSX } from "react";
*/
export default function OutstandingCasesSkeleton(): JSX.Element {
return (
<Box sx={{ display: "flex", flexDirection: "column", gap: 1.5 }}>
<Box
sx={{ display: "flex", flexDirection: "column", gap: 1.5, width: "100%" }}
>
{Array.from({ length: SUPPORT_OVERVIEW_CASES_LIMIT }).map((_, index) => (
<Form.CardButton
key={`skeleton-${index}`}
Expand Down
Loading