Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e7a4b25
Add react-idle-timer dependency
dileepapeiris Feb 16, 2026
cbee98c
Mock AuthApiContext in vitest setup
dileepapeiris Feb 16, 2026
8f9e3e9
Wrap AppLayout with IdleTimeoutProvider
dileepapeiris Feb 16, 2026
460b68d
Add React import and update test mocks
dileepapeiris Feb 16, 2026
ffd7148
Add IdleTimeoutProvider for session warnings
dileepapeiris Feb 16, 2026
d89c492
Remove BackgroundTokenRefresh component
dileepapeiris Feb 16, 2026
e67c90f
Add authenticatedFetch utility for API calls
dileepapeiris Feb 16, 2026
348c81a
Add AuthApiContext provider and hook
dileepapeiris Feb 16, 2026
7a45efb
Add SessionWarningDialog component
dileepapeiris Feb 16, 2026
9fbeee3
Use auth API client in case hooks
dileepapeiris Feb 16, 2026
4558298
Use auth API client for backend fetches
dileepapeiris Feb 16, 2026
cc5a32d
Use auth API client for fetches
dileepapeiris Feb 16, 2026
2babfa9
Use auth api client fetchFn for API calls
dileepapeiris Feb 16, 2026
db8bf81
Use auth API client for project stats fetch
dileepapeiris Feb 16, 2026
d673abf
Use auth API client in project API hooks
dileepapeiris Feb 16, 2026
78c800e
Use auth API client in project hooks
dileepapeiris Feb 16, 2026
28077af
Use auth API client for fetch requests
dileepapeiris Feb 16, 2026
93ae5ee
Use auth API client for project APIs
dileepapeiris Feb 16, 2026
c85ae3d
Use AuthApiClient for authenticated fetch
dileepapeiris Feb 16, 2026
883ce5a
Use AuthApiContext fetch client for project stats
dileepapeiris Feb 16, 2026
e6c6304
Use AuthApiContext client for POST requests
dileepapeiris Feb 16, 2026
e8d605d
Wrap app providers with AuthApiProvider
dileepapeiris Feb 16, 2026
a464ce9
Add authConstants for idle timeout
dileepapeiris Feb 16, 2026
fd5f284
Add silent sign-in recovery to authenticatedFetch
dileepapeiris Feb 16, 2026
6ad9d95
Add silent sign-in support to AuthApiContext
dileepapeiris Feb 16, 2026
f8c2e01
Move navigation after attachment upload finishes
dileepapeiris Feb 16, 2026
c4dbab2
Use mockImplementation for useAuthApiClient mock
dileepapeiris Feb 16, 2026
de2e80f
Await signOut; preserve refresh error; mock auth
dileepapeiris Feb 16, 2026
1932d7b
Sign out and redirect on idle
dileepapeiris Feb 16, 2026
83f52c0
Always navigate after signOut; safer HTML parsing
dileepapeiris Feb 16, 2026
4734859
Sanitize image URLs, guard toolbar, add cancel
dileepapeiris Feb 16, 2026
a6c7169
Merge branch 'customer-portal-milestone-1' into fix/m1-auth-issues
dileepapeiris Feb 16, 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
1 change: 1 addition & 0 deletions apps/customer-portal/webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"prismjs": "^1.30.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-idle-timer": "^5.7.2",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"react-router": "^7.1.5",
"zod": "^4.3.6"
},
Expand Down
14 changes: 14 additions & 0 deletions apps/customer-portal/webapp/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions apps/customer-portal/webapp/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import CaseDetailsPage from "@pages/CaseDetailsPage";
import NoveraChatPage from "@pages/NoveraChatPage";
import CreateCasePage from "@pages/CreateCasePage";
import AppLayout from "@layouts/AppLayout";
import { AuthApiProvider } from "@context/AuthApiContext";
import { ErrorBannerProvider } from "@context/error-banner/ErrorBannerContext";
import { SuccessBannerProvider } from "@context/success-banner/SuccessBannerContext";
import { LoaderProvider } from "@context/linear-loader/LoaderContext";
Expand All @@ -36,10 +37,11 @@ import AuthGuard from "./AuthGuard";
export default function App(): JSX.Element {
return (
<BrowserRouter>
<LoaderProvider>
<ErrorBannerProvider>
<SuccessBannerProvider>
<Routes>
<AuthApiProvider>
<LoaderProvider>
<ErrorBannerProvider>
<SuccessBannerProvider>
<Routes>
{/* Public Route */}
<Route path="/login" element={<LoginPage />} />

Expand Down Expand Up @@ -111,6 +113,7 @@ export default function App(): JSX.Element {
</SuccessBannerProvider>
</ErrorBannerProvider>
</LoaderProvider>
</AuthApiProvider>
</BrowserRouter>
);
}
11 changes: 4 additions & 7 deletions apps/customer-portal/webapp/src/api/useGetCaseAttachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useMockConfig } from "@providers/MockConfigProvider";
import { useLogger } from "@hooks/useLogger";
import { mockCaseAttachments } from "@models/mockData";
import { ApiQueryKeys, API_MOCK_DELAY } from "@constants/apiConstants";
import { addApiHeaders } from "@utils/apiUtils";
import { useAuthApiClient } from "@context/AuthApiContext";
import type { CaseAttachmentsResponse } from "@models/responses";

export interface UseGetCaseAttachmentsOptions {
Expand All @@ -41,7 +41,8 @@ export default function useGetCaseAttachments(
options?: UseGetCaseAttachmentsOptions,
): UseQueryResult<CaseAttachmentsResponse, Error> {
const logger = useLogger();
const { getIdToken, isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const { isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const fetchFn = useAuthApiClient();
const { isMockEnabled } = useMockConfig();
const { limit = 50, offset = 0, enabled: optionsEnabled = true } = options ?? {};

Expand Down Expand Up @@ -75,15 +76,11 @@ export default function useGetCaseAttachments(
throw new Error("CUSTOMER_PORTAL_BACKEND_BASE_URL is not configured");
}

const idToken = await getIdToken();
const params = new URLSearchParams();
params.set("limit", String(limit));
params.set("offset", String(offset));
const requestUrl = `${baseUrl}/cases/${caseId}/attachments?${params.toString()}`;
const response = await fetch(requestUrl, {
method: "GET",
headers: addApiHeaders(idToken),
});
const response = await fetchFn(requestUrl, { method: "GET" });

if (!response.ok) {
throw new Error(
Expand Down
11 changes: 4 additions & 7 deletions apps/customer-portal/webapp/src/api/useGetCaseComments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useMockConfig } from "@providers/MockConfigProvider";
import { useLogger } from "@hooks/useLogger";
import { mockCaseComments } from "@models/mockData";
import { ApiQueryKeys, API_MOCK_DELAY } from "@constants/apiConstants";
import { addApiHeaders } from "@utils/apiUtils";
import { useAuthApiClient } from "@context/AuthApiContext";
import type { CaseCommentsResponse } from "@models/responses";

export interface UseGetCaseCommentsOptions {
Expand All @@ -42,7 +42,8 @@ export default function useGetCaseComments(
options?: UseGetCaseCommentsOptions,
): UseQueryResult<CaseCommentsResponse, Error> {
const logger = useLogger();
const { getIdToken, isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const { isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const fetchFn = useAuthApiClient();
const { isMockEnabled } = useMockConfig();
const { offset = 0, limit = 20 } = options ?? {};

Expand Down Expand Up @@ -77,16 +78,12 @@ export default function useGetCaseComments(
throw new Error("CUSTOMER_PORTAL_BACKEND_BASE_URL is not configured");
}

const idToken = await getIdToken();
const params = new URLSearchParams();
if (offset !== undefined) params.set("offset", String(offset));
if (limit !== undefined) params.set("limit", String(limit));
const query = params.toString();
const requestUrl = `${baseUrl}/cases/${caseId}/comments${query ? `?${query}` : ""}`;
const response = await fetch(requestUrl, {
method: "GET",
headers: addApiHeaders(idToken),
});
const response = await fetchFn(requestUrl, { method: "GET" });

if (!response.ok) {
throw new Error(
Expand Down
11 changes: 4 additions & 7 deletions apps/customer-portal/webapp/src/api/useGetCaseDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useMockConfig } from "@providers/MockConfigProvider";
import { useLogger } from "@hooks/useLogger";
import { mockCaseDetails } from "@models/mockData";
import { ApiQueryKeys, API_MOCK_DELAY } from "@constants/apiConstants";
import { addApiHeaders } from "@utils/apiUtils";
import { useAuthApiClient } from "@context/AuthApiContext";
import type { CaseDetails } from "@models/responses";

/**
Expand All @@ -35,7 +35,8 @@ export default function useGetCaseDetails(
caseId: string,
): UseQueryResult<CaseDetails, Error> {
const logger = useLogger();
const { getIdToken, isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const { isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const fetchFn = useAuthApiClient();
const { isMockEnabled } = useMockConfig();

return useQuery<CaseDetails, Error>({
Expand All @@ -56,12 +57,8 @@ export default function useGetCaseDetails(
throw new Error("CUSTOMER_PORTAL_BACKEND_BASE_URL is not configured");
}

const idToken = await getIdToken();
const requestUrl = `${baseUrl}/cases/${caseId}`;
const response = await fetch(requestUrl, {
method: "GET",
headers: addApiHeaders(idToken),
});
const response = await fetchFn(requestUrl, { method: "GET" });

if (!response.ok) {
throw new Error(
Expand Down
11 changes: 4 additions & 7 deletions apps/customer-portal/webapp/src/api/useGetCasesFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { useMockConfig } from "@providers/MockConfigProvider";
import { useLogger } from "@hooks/useLogger";
import { mockCaseMetadata } from "@models/mockData";
import { ApiQueryKeys, API_MOCK_DELAY } from "@constants/apiConstants";
import { addApiHeaders } from "@utils/apiUtils";
import { useAuthApiClient } from "@context/AuthApiContext";
import type { CaseMetadataResponse } from "@models/responses";

/**
Expand All @@ -33,7 +33,8 @@ export default function useGetCasesFilters(
projectId: string,
): UseQueryResult<CaseMetadataResponse, Error> {
const logger = useLogger();
const { getIdToken, isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const { isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const fetchFn = useAuthApiClient();
const { isMockEnabled } = useMockConfig();

return useQuery<CaseMetadataResponse, Error>({
Expand All @@ -53,7 +54,6 @@ export default function useGetCasesFilters(
}

try {
const idToken = await getIdToken();
const baseUrl = window.config?.CUSTOMER_PORTAL_BACKEND_BASE_URL;

if (!baseUrl) {
Expand All @@ -62,10 +62,7 @@ export default function useGetCasesFilters(

const requestUrl = `${baseUrl}/projects/${projectId}/cases/filters`;

const response = await fetch(requestUrl, {
method: "GET",
headers: addApiHeaders(idToken),
});
const response = await fetchFn(requestUrl, { method: "GET" });

if (!response.ok) {
throw new Error(
Expand Down
11 changes: 4 additions & 7 deletions apps/customer-portal/webapp/src/api/useGetChatHistory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { getMockChatHistory } from "@models/mockFunctions";
import { useMockConfig } from "@providers/MockConfigProvider";
import { useLogger } from "@hooks/useLogger";
import { ApiQueryKeys, API_MOCK_DELAY } from "@constants/apiConstants";
import { addApiHeaders } from "@utils/apiUtils";
import { useAuthApiClient } from "@context/AuthApiContext";
import type { ChatHistoryResponse } from "@models/responses";

/**
Expand All @@ -33,7 +33,8 @@ export function useGetChatHistory(
projectId: string,
): UseQueryResult<ChatHistoryResponse, Error> {
const logger = useLogger();
const { getIdToken, isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const { isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const fetchFn = useAuthApiClient();
const { isMockEnabled } = useMockConfig();

return useQuery<ChatHistoryResponse, Error>({
Expand All @@ -54,7 +55,6 @@ export function useGetChatHistory(
}

try {
const idToken = await getIdToken();
const baseUrl = window.config?.CUSTOMER_PORTAL_BACKEND_BASE_URL;

if (!baseUrl) {
Expand All @@ -63,10 +63,7 @@ export function useGetChatHistory(

const requestUrl = `${baseUrl}/projects/${projectId}/chat-history`;

const response = await fetch(requestUrl, {
method: "GET",
headers: addApiHeaders(idToken),
});
const response = await fetchFn(requestUrl, { method: "GET" });

logger.debug(
`[useGetChatHistory] Response status: ${response.status}`,
Expand Down
11 changes: 4 additions & 7 deletions apps/customer-portal/webapp/src/api/useGetDeployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { getMockDeployments } from "@models/mockFunctions";
import { useMockConfig } from "@providers/MockConfigProvider";
import { useLogger } from "@hooks/useLogger";
import { ApiQueryKeys, API_MOCK_DELAY } from "@constants/apiConstants";
import { addApiHeaders } from "@utils/apiUtils";
import { useAuthApiClient } from "@context/AuthApiContext";
import type { DeploymentsResponse } from "@models/responses";

/**
Expand All @@ -33,7 +33,8 @@ export function useGetDeployments(
projectId: string,
): UseQueryResult<DeploymentsResponse, Error> {
const logger = useLogger();
const { getIdToken, isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const { isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const fetchFn = useAuthApiClient();
const { isMockEnabled } = useMockConfig();

return useQuery<DeploymentsResponse, Error>({
Expand All @@ -54,7 +55,6 @@ export function useGetDeployments(
}

try {
const idToken = await getIdToken();
const baseUrl = window.config?.CUSTOMER_PORTAL_BACKEND_BASE_URL;

if (!baseUrl) {
Expand All @@ -63,10 +63,7 @@ export function useGetDeployments(

const requestUrl = `${baseUrl}/projects/${projectId}/deployments`;

const response = await fetch(requestUrl, {
method: "GET",
headers: addApiHeaders(idToken),
});
const response = await fetchFn(requestUrl, { method: "GET" });

logger.debug(`[useGetDeployments] Response status: ${response.status}`);

Expand Down
21 changes: 10 additions & 11 deletions apps/customer-portal/webapp/src/api/useGetDeploymentsProducts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@

import { useQuery, type UseQueryResult } from "@tanstack/react-query";
import { useAsgardeo } from "@asgardeo/react";
import { useAuthApiClient } from "@context/AuthApiContext";
import { useMockConfig } from "@providers/MockConfigProvider";
import { useLogger } from "@hooks/useLogger";
import { ApiQueryKeys, API_MOCK_DELAY } from "@constants/apiConstants";
import { addApiHeaders } from "@utils/apiUtils";
import { getMockDeploymentProducts } from "@models/mockFunctions";
import type { DeploymentProductItem } from "@models/responses";

export type FetchFn = (url: string, init?: RequestInit) => Promise<Response>;

export interface FetchDeploymentProductsOptions {
getIdToken: () => Promise<string>;
fetchFn: FetchFn;
isMockEnabled: boolean;
}

Expand All @@ -33,21 +35,20 @@ export interface FetchDeploymentProductsOptions {
* useQueries when fetching products for multiple deployments.
*
* @param {string} deploymentId - The deployment ID.
* @param {FetchDeploymentProductsOptions} options - getIdToken and isMockEnabled.
* @param {FetchDeploymentProductsOptions} options - fetchFn and isMockEnabled.
* @returns {Promise<DeploymentProductItem[]>} Deployment products array.
*/
export async function fetchDeploymentProducts(
deploymentId: string,
options: FetchDeploymentProductsOptions,
): Promise<DeploymentProductItem[]> {
const { getIdToken, isMockEnabled } = options;
const { fetchFn, isMockEnabled } = options;

if (isMockEnabled) {
await new Promise((resolve) => setTimeout(resolve, API_MOCK_DELAY));
return getMockDeploymentProducts(deploymentId);
}

const idToken = await getIdToken();
const baseUrl = window.config?.CUSTOMER_PORTAL_BACKEND_BASE_URL;

if (!baseUrl) {
Expand All @@ -56,10 +57,7 @@ export async function fetchDeploymentProducts(

const requestUrl = `${baseUrl}/deployments/${deploymentId}/products`;

const response = await fetch(requestUrl, {
method: "GET",
headers: addApiHeaders(idToken),
});
const response = await fetchFn(requestUrl, { method: "GET" });

if (!response.ok) {
throw new Error(
Expand All @@ -83,8 +81,9 @@ export function useGetDeploymentsProducts(
deploymentId: string,
): UseQueryResult<DeploymentProductItem[], Error> {
const logger = useLogger();
const { getIdToken, isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const { isSignedIn, isLoading: isAuthLoading } = useAsgardeo();
const { isMockEnabled } = useMockConfig();
const fetchFn = useAuthApiClient();

return useQuery<DeploymentProductItem[], Error>({
queryKey: [ApiQueryKeys.DEPLOYMENT_PRODUCTS, deploymentId, isMockEnabled],
Expand All @@ -93,7 +92,7 @@ export function useGetDeploymentsProducts(
`Fetching deployment products for deployment ID: ${deploymentId}, mock: ${isMockEnabled}`,
);
const data = await fetchDeploymentProducts(deploymentId, {
getIdToken,
fetchFn,
isMockEnabled,
});
logger.debug(
Expand Down
Loading