From 9b1dee101def57f9dfafe6a6b30cc84e054c7e09 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 6 Dec 2025 14:10:56 +0530 Subject: [PATCH] feat: completed request list page --- frontend/src/const/routes.ts | 6 + .../src/hooks/api/approvalPolicies/types.ts | 2 +- .../ApprovalRequestDetailPage.tsx | 3 + .../pam/ApprovalRequestDetailPage/route.tsx | 19 + .../pages/pam/ApprovalsPage/ApprovalsPage.tsx | 3 +- .../ApprovalRequestTab/ApprovalRequestTab.tsx | 5 + .../components/RequestsSection.tsx | 12 + .../components/RequestsTable.tsx | 387 ++++++++++++++++++ .../ApprovalRequestTab/components/index.tsx | 2 + .../components/ApprovalRequestTab/index.tsx | 1 + .../PolicyTab/components/PolicyModal.tsx | 12 +- .../PolicyTab/components/PolicySchema.tsx | 6 +- .../PolicySteps/PolicyDetailsStep.tsx | 10 +- .../PolicySteps/PolicyReviewStep.tsx | 4 +- frontend/src/routeTree.gen.ts | 29 ++ frontend/src/routes.ts | 3 +- 16 files changed, 485 insertions(+), 19 deletions(-) create mode 100644 frontend/src/pages/pam/ApprovalRequestDetailPage/ApprovalRequestDetailPage.tsx create mode 100644 frontend/src/pages/pam/ApprovalRequestDetailPage/route.tsx create mode 100644 frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/ApprovalRequestTab.tsx create mode 100644 frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsSection.tsx create mode 100644 frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsTable.tsx create mode 100644 frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/index.tsx create mode 100644 frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/index.tsx diff --git a/frontend/src/const/routes.ts b/frontend/src/const/routes.ts index 6046c8fc8..ffbac7eda 100644 --- a/frontend/src/const/routes.ts +++ b/frontend/src/const/routes.ts @@ -68,6 +68,12 @@ export const ROUTE_PATHS = Object.freeze({ "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/networking" ) }, + Pam: { + ApprovalRequestDetailPage: setRoute( + "/organizations/$orgId/projects/pam/$projectId/approval-requests/$approvalRequestId", + "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/approval-requests/$approvalRequestId" + ) + }, SecretManager: { ApprovalPage: setRoute( "/organizations/$orgId/projects/secret-management/$projectId/approval", diff --git a/frontend/src/hooks/api/approvalPolicies/types.ts b/frontend/src/hooks/api/approvalPolicies/types.ts index 1f86112d3..3843cc583 100644 --- a/frontend/src/hooks/api/approvalPolicies/types.ts +++ b/frontend/src/hooks/api/approvalPolicies/types.ts @@ -23,7 +23,7 @@ export type PamAccessPolicyConditions = { }[]; export type PamAccessPolicyConstraints = { - requestDurationHours: { + requestDurationSeconds: { min: number; max: number; }; diff --git a/frontend/src/pages/pam/ApprovalRequestDetailPage/ApprovalRequestDetailPage.tsx b/frontend/src/pages/pam/ApprovalRequestDetailPage/ApprovalRequestDetailPage.tsx new file mode 100644 index 000000000..415c68c65 --- /dev/null +++ b/frontend/src/pages/pam/ApprovalRequestDetailPage/ApprovalRequestDetailPage.tsx @@ -0,0 +1,3 @@ +export const ApprovalRequestDetailPage = () => { + return
Request detail
; +}; diff --git a/frontend/src/pages/pam/ApprovalRequestDetailPage/route.tsx b/frontend/src/pages/pam/ApprovalRequestDetailPage/route.tsx new file mode 100644 index 000000000..b39a2511c --- /dev/null +++ b/frontend/src/pages/pam/ApprovalRequestDetailPage/route.tsx @@ -0,0 +1,19 @@ +import { createFileRoute } from "@tanstack/react-router"; + +import { ApprovalRequestDetailPage } from "./ApprovalRequestDetailPage"; + +export const Route = createFileRoute( + "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/approval-requests/$approvalRequestId" +)({ + component: ApprovalRequestDetailPage, + beforeLoad: ({ context }) => { + return { + breadcrumbs: [ + ...context.breadcrumbs, + { + label: "Approval Request" + } + ] + }; + } +}); diff --git a/frontend/src/pages/pam/ApprovalsPage/ApprovalsPage.tsx b/frontend/src/pages/pam/ApprovalsPage/ApprovalsPage.tsx index 78550cf9d..d79a5969b 100644 --- a/frontend/src/pages/pam/ApprovalsPage/ApprovalsPage.tsx +++ b/frontend/src/pages/pam/ApprovalsPage/ApprovalsPage.tsx @@ -5,6 +5,7 @@ import { useOrganization, useProject } from "@app/context"; import { ApprovalControlTabs } from "@app/types/project"; import { useNavigate, useSearch } from "@tanstack/react-router"; import { PolicyTab } from "./components/PolicyTab"; +import { ApprovalRequestTab } from "./components/ApprovalRequestTab"; const Page = () => { const navigate = useNavigate(); @@ -47,7 +48,7 @@ const Page = () => { -
Hello
+
diff --git a/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/ApprovalRequestTab.tsx b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/ApprovalRequestTab.tsx new file mode 100644 index 000000000..3a7250f91 --- /dev/null +++ b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/ApprovalRequestTab.tsx @@ -0,0 +1,5 @@ +import { RequestsSection } from "./components"; + +export const ApprovalRequestTab = () => { + return ; +}; diff --git a/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsSection.tsx b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsSection.tsx new file mode 100644 index 000000000..621f04908 --- /dev/null +++ b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsSection.tsx @@ -0,0 +1,12 @@ +import { RequestsTable } from "./RequestsTable"; + +export const RequestsSection = () => { + return ( +
+
+

Approval Requests

+
+ +
+ ); +}; diff --git a/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsTable.tsx b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsTable.tsx new file mode 100644 index 000000000..0ff3ba005 --- /dev/null +++ b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/RequestsTable.tsx @@ -0,0 +1,387 @@ +import { useCallback, useMemo, useState } from "react"; +import { + faCheckCircle, + faChevronRight, + faExclamationCircle, + faFileCircleQuestion, + faFilter, + faMagnifyingGlass, + faSearch +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useNavigate } from "@tanstack/react-router"; +import { useQuery } from "@tanstack/react-query"; +import { formatDistance } from "date-fns"; +import { twMerge } from "tailwind-merge"; + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuTrigger, + DropdownSubMenu, + DropdownSubMenuContent, + DropdownSubMenuTrigger, + EmptyState, + IconButton, + Input, + Pagination, + Table, + TableContainer, + TableSkeleton, + TBody, + Td, + Th, + THead, + Tr +} from "@app/components/v2"; +import { Badge } from "@app/components/v3"; +import { useOrganization, useProject, useUser } from "@app/context"; +import { usePagination } from "@app/hooks"; +import { ApprovalPolicyType } from "@app/hooks/api/approvalPolicies"; +import { + approvalRequestQuery, + ApprovalRequestStatus, + ApprovalRequestStepStatus, + TApprovalRequest +} from "@app/hooks/api/approvalRequests"; +import { ApproverType } from "@app/hooks/api/approvalPolicies"; + +type Filter = { + status: "open" | "closed"; + closedStatuses: ApprovalRequestStatus[]; +}; + +const getStatusBadgeColor = (status: ApprovalRequestStatus) => { + switch (status) { + case ApprovalRequestStatus.Pending: + return "project"; + case ApprovalRequestStatus.Approved: + return "success"; + case ApprovalRequestStatus.Rejected: + return "danger"; + case ApprovalRequestStatus.Expired: + return "neutral"; + default: + return "neutral"; + } +}; + +const formatDuration = (seconds: number) => { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + + if (hours > 0) { + return `${hours}h${minutes > 0 ? ` ${minutes}m` : ""}`; + } + return `${minutes}m`; +}; + +const checkIfUserNeedsToApprove = (request: TApprovalRequest, userId: string): boolean => { + const currentStep = request.steps.find( + (step) => step.status === ApprovalRequestStepStatus.InProgress + ); + + if (!currentStep) return false; + + const isApprover = currentStep.approvers.some( + (approver) => approver.type === ApproverType.User && approver.id === userId + ); + + if (!isApprover) return false; + + const hasAlreadyApproved = currentStep.approvals.some( + (approval) => approval.approverId === userId + ); + + return !hasAlreadyApproved; +}; + +export const RequestsTable = () => { + const navigate = useNavigate(); + const { currentOrg } = useOrganization(); + const { currentProject } = useProject(); + const { user } = useUser(); + const [search, setSearch] = useState(""); + const [filter, setFilter] = useState({ + status: "open", + closedStatuses: [] + }); + + const projectId = currentProject?.id || ""; + const userId = user?.id || ""; + + const { data: requests = [], isPending: isRequestsLoading } = useQuery( + approvalRequestQuery.list({ + policyType: ApprovalPolicyType.PamAccess, + projectId + }) + ); + + const { page, perPage, setPage, setPerPage, offset } = usePagination(); + + const filteredRequests = useMemo(() => { + let filtered = requests; + + // Apply search filter + if (search) { + filtered = filtered.filter( + (request) => + request.requesterName?.toLowerCase().includes(search.toLowerCase()) || + request.requesterEmail?.toLowerCase().includes(search.toLowerCase()) || + request.justification?.toLowerCase().includes(search.toLowerCase()) || + request.requestData.requestData.resourceId.toLowerCase().includes(search.toLowerCase()) || + request.requestData.requestData.accountPath.toLowerCase().includes(search.toLowerCase()) + ); + } + + // Apply open/closed filter + if (filter.status === "open") { + filtered = filtered.filter((req) => req.status === ApprovalRequestStatus.Pending); + } else { + const closedStatuses = [ + ApprovalRequestStatus.Approved, + ApprovalRequestStatus.Rejected, + ApprovalRequestStatus.Expired + ]; + filtered = filtered.filter((req) => closedStatuses.includes(req.status)); + + // Apply specific closed status filter + if (filter.closedStatuses.length > 0) { + filtered = filtered.filter((req) => filter.closedStatuses.includes(req.status)); + } + } + + return filtered; + }, [requests, search, filter]); + + const paginatedRequests = useMemo( + () => filteredRequests.slice(offset, offset + perPage), + [filteredRequests, offset, perPage] + ); + + const handleRowClick = (requestId: string) => { + navigate({ + to: "/organizations/$orgId/projects/pam/$projectId/approval-requests/$approvalRequestId", + params: { + orgId: currentOrg.id, + projectId: currentProject.id, + approvalRequestId: requestId + } + }); + }; + + const handleStatusFilterToggle = useCallback( + () => + setFilter((state) => ({ + ...state, + status: state.status === "open" ? "closed" : "open", + closedStatuses: [] + })), + [] + ); + + const handleClosedStatusToggle = useCallback( + (status: ApprovalRequestStatus) => + setFilter((state) => { + const closedStatuses = state.closedStatuses || []; + + if (closedStatuses.includes(status)) { + return { ...state, closedStatuses: closedStatuses.filter((s) => s !== status) }; + } + return { ...state, closedStatuses: [...closedStatuses, status] }; + }), + [] + ); + + const isTableFiltered = filter.status === "closed" || filter.closedStatuses.length > 0; + + return ( +
+
+ + + + + + + + Filter By + { + evt.preventDefault(); + handleStatusFilterToggle(); + }} + icon={filter.status === "open" && } + iconPos="right" + > + Open Requests + + + } + > + Closed Requests + + + + Filter by Status + + { + evt.preventDefault(); + handleClosedStatusToggle(ApprovalRequestStatus.Approved); + }} + icon={ + filter.closedStatuses.includes(ApprovalRequestStatus.Approved) && ( + + ) + } + iconPos="right" + > + Approved + + { + evt.preventDefault(); + handleClosedStatusToggle(ApprovalRequestStatus.Rejected); + }} + icon={ + filter.closedStatuses.includes(ApprovalRequestStatus.Rejected) && ( + + ) + } + iconPos="right" + > + Rejected + + { + evt.preventDefault(); + handleClosedStatusToggle(ApprovalRequestStatus.Expired); + }} + icon={ + filter.closedStatuses.includes(ApprovalRequestStatus.Expired) && ( + + ) + } + iconPos="right" + > + Expired + + + + + + setSearch(e.target.value)} + leftIcon={} + placeholder="Search approval requests..." + /> +
+ + + + + + + + + + + + + {isRequestsLoading && } + {!isRequestsLoading && + paginatedRequests.map((request) => { + const needsApproval = checkIfUserNeedsToApprove(request, userId); + const { resourceId, accountPath, requestDurationSeconds } = + request.requestData.requestData; + + return ( + handleRowClick(request.id)} + > + + + + + + + ); + })} + +
RequesterResource / PathDurationStatusRequested
+
+
+ {request.requesterName || "Unknown"} +
+
{request.requesterEmail}
+
+
+
+
{accountPath}
+
+ {resourceId.slice(0, 8)}... +
+
+
+ + {formatDuration(requestDurationSeconds)} + + +
+ + {request.status} + + {needsApproval && ( +
+ + Approval Required +
+ )} +
+
+ + {formatDistance(new Date(request.createdAt), new Date(), { + addSuffix: true + })} + +
+ {Boolean(filteredRequests.length) && ( + + )} + {!isRequestsLoading && !filteredRequests?.length && ( + + )} +
+
+ ); +}; diff --git a/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/index.tsx b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/index.tsx new file mode 100644 index 000000000..35945d6a2 --- /dev/null +++ b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/components/index.tsx @@ -0,0 +1,2 @@ +export { RequestsSection } from "./RequestsSection"; +export { RequestsTable } from "./RequestsTable"; diff --git a/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/index.tsx b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/index.tsx new file mode 100644 index 000000000..3a0475784 --- /dev/null +++ b/frontend/src/pages/pam/ApprovalsPage/components/ApprovalRequestTab/index.tsx @@ -0,0 +1 @@ +export { ApprovalRequestTab } from "./ApprovalRequestTab"; diff --git a/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicyModal.tsx b/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicyModal.tsx index f81de5e49..a939ff7dd 100644 --- a/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicyModal.tsx +++ b/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicyModal.tsx @@ -48,9 +48,9 @@ export const PolicyModal = ({ popUp, handlePopUpToggle }: Props) => { maxRequestTtlSeconds: null, conditions: [{ resourceIds: [], accountPaths: [] }], constraints: { - requestDurationHours: { - min: 1, - max: 24 + requestDurationSeconds: { + min: 30, + max: 604800 } }, steps: [ @@ -88,9 +88,9 @@ export const PolicyModal = ({ popUp, handlePopUpToggle }: Props) => { maxRequestTtlSeconds: null, conditions: [{ resourceIds: [], accountPaths: [] }], constraints: { - requestDurationHours: { - min: 1, - max: 24 + requestDurationSeconds: { + min: 30, + max: 604800 } }, steps: [ diff --git a/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicySchema.tsx b/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicySchema.tsx index f1edbfbcf..7eff82a44 100644 --- a/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicySchema.tsx +++ b/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicySchema.tsx @@ -12,9 +12,9 @@ export const PolicyFormSchema = z.object({ .array() .min(1, "At least one condition is required"), constraints: z.object({ - requestDurationHours: z.object({ - min: z.number().min(0).max(168), - max: z.number().min(1).max(168) + requestDurationSeconds: z.object({ + min: z.number().min(0).max(604800), + max: z.number().min(1).max(604800) }) }), steps: z diff --git a/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicySteps/PolicyDetailsStep.tsx b/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicySteps/PolicyDetailsStep.tsx index 65f3a3415..017203ccc 100644 --- a/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicySteps/PolicyDetailsStep.tsx +++ b/frontend/src/pages/pam/ApprovalsPage/components/PolicyTab/components/PolicySteps/PolicyDetailsStep.tsx @@ -51,19 +51,19 @@ export const PolicyDetailsStep = () => { Request Duration Constraints

- Set minimum and maximum duration (in hours) for access requests + Set minimum and maximum duration (in seconds) for access requests

( { /> ( {
diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index aef323bdc..a538a02cf 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -151,6 +151,7 @@ import { Route as secretManagerSecretDashboardPageRouteImport } from './pages/se import { Route as secretManagerIntegrationsSelectIntegrationAuthPageRouteImport } from './pages/secret-manager/integrations/SelectIntegrationAuthPage/route' import { Route as secretManagerIntegrationsDetailsByIDPageRouteImport } from './pages/secret-manager/IntegrationsDetailsByIDPage/route' import { Route as pamPamSessionsByIDPageRouteImport } from './pages/pam/PamSessionsByIDPage/route' +import { Route as pamApprovalRequestDetailPageRouteImport } from './pages/pam/ApprovalRequestDetailPage/route' import { Route as certManagerPkiSubscriberDetailsByIDPageRouteImport } from './pages/cert-manager/PkiSubscriberDetailsByIDPage/route' import { Route as certManagerPkiSyncDetailsByIDPageRouteImport } from './pages/cert-manager/PkiSyncDetailsByIDPage/route' import { Route as certManagerCertAuthDetailsByIDPageRouteImport } from './pages/cert-manager/CertAuthDetailsByIDPage/route' @@ -1504,6 +1505,13 @@ const pamPamSessionsByIDPageRouteRoute = AuthenticateInjectOrgDetailsOrgLayoutOrganizationsOrgIdProjectsPamProjectIdPamLayoutSessionsRoute, } as any) +const pamApprovalRequestDetailPageRouteRoute = + pamApprovalRequestDetailPageRouteImport.update({ + id: '/approval-requests/$approvalRequestId', + path: '/approval-requests/$approvalRequestId', + getParentRoute: () => pamLayoutRoute, + } as any) + const certManagerPkiSubscriberDetailsByIDPageRouteRoute = certManagerPkiSubscriberDetailsByIDPageRouteImport.update({ id: '/$subscriberName', @@ -3217,6 +3225,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof certManagerPkiSubscriberDetailsByIDPageRouteImport parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationsOrgIdProjectsCertManagementProjectIdCertManagerLayoutSubscribersImport } + '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/approval-requests/$approvalRequestId': { + id: '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/approval-requests/$approvalRequestId' + path: '/approval-requests/$approvalRequestId' + fullPath: '/organizations/$orgId/projects/pam/$projectId/approval-requests/$approvalRequestId' + preLoaderRoute: typeof pamApprovalRequestDetailPageRouteImport + parentRoute: typeof pamLayoutImport + } '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId': { id: '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId' path: '/$sessionId' @@ -4234,6 +4249,7 @@ interface pamLayoutRouteChildren { projectAccessControlPageRoutePamRoute: typeof projectAccessControlPageRoutePamRoute projectAuditLogsPageRoutePamRoute: typeof projectAuditLogsPageRoutePamRoute AuthenticateInjectOrgDetailsOrgLayoutOrganizationsOrgIdProjectsPamProjectIdPamLayoutSessionsRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationsOrgIdProjectsPamProjectIdPamLayoutSessionsRouteWithChildren + pamApprovalRequestDetailPageRouteRoute: typeof pamApprovalRequestDetailPageRouteRoute projectGroupDetailsByIDPageRoutePamRoute: typeof projectGroupDetailsByIDPageRoutePamRoute projectIdentityDetailsByIDPageRoutePamRoute: typeof projectIdentityDetailsByIDPageRoutePamRoute projectMemberDetailsByIDPageRoutePamRoute: typeof projectMemberDetailsByIDPageRoutePamRoute @@ -4249,6 +4265,8 @@ const pamLayoutRouteChildren: pamLayoutRouteChildren = { projectAuditLogsPageRoutePamRoute: projectAuditLogsPageRoutePamRoute, AuthenticateInjectOrgDetailsOrgLayoutOrganizationsOrgIdProjectsPamProjectIdPamLayoutSessionsRoute: AuthenticateInjectOrgDetailsOrgLayoutOrganizationsOrgIdProjectsPamProjectIdPamLayoutSessionsRouteWithChildren, + pamApprovalRequestDetailPageRouteRoute: + pamApprovalRequestDetailPageRouteRoute, projectGroupDetailsByIDPageRoutePamRoute: projectGroupDetailsByIDPageRoutePamRoute, projectIdentityDetailsByIDPageRoutePamRoute: @@ -5219,6 +5237,7 @@ export interface FileRoutesByFullPath { '/organizations/$orgId/projects/cert-management/$projectId/ca/$caId': typeof certManagerCertAuthDetailsByIDPageRouteRoute '/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId': typeof certManagerPkiSyncDetailsByIDPageRouteRoute '/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute + '/organizations/$orgId/projects/pam/$projectId/approval-requests/$approvalRequestId': typeof pamApprovalRequestDetailPageRouteRoute '/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId': typeof pamPamSessionsByIDPageRouteRoute '/organizations/$orgId/projects/secret-management/$projectId/integrations/$integrationId': typeof secretManagerIntegrationsDetailsByIDPageRouteRoute '/organizations/$orgId/projects/secret-management/$projectId/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute @@ -5450,6 +5469,7 @@ export interface FileRoutesByTo { '/organizations/$orgId/projects/cert-management/$projectId/ca/$caId': typeof certManagerCertAuthDetailsByIDPageRouteRoute '/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId': typeof certManagerPkiSyncDetailsByIDPageRouteRoute '/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute + '/organizations/$orgId/projects/pam/$projectId/approval-requests/$approvalRequestId': typeof pamApprovalRequestDetailPageRouteRoute '/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId': typeof pamPamSessionsByIDPageRouteRoute '/organizations/$orgId/projects/secret-management/$projectId/integrations/$integrationId': typeof secretManagerIntegrationsDetailsByIDPageRouteRoute '/organizations/$orgId/projects/secret-management/$projectId/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute @@ -5704,6 +5724,7 @@ export interface FileRoutesById { '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId': typeof certManagerCertAuthDetailsByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations/$syncId': typeof certManagerPkiSyncDetailsByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers/$subscriberName': typeof certManagerPkiSubscriberDetailsByIDPageRouteRoute + '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/approval-requests/$approvalRequestId': typeof pamApprovalRequestDetailPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId': typeof pamPamSessionsByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/$integrationId': typeof secretManagerIntegrationsDetailsByIDPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/select-integration-auth': typeof secretManagerIntegrationsSelectIntegrationAuthPageRouteRoute @@ -5950,6 +5971,7 @@ export interface FileRouteTypes { | '/organizations/$orgId/projects/cert-management/$projectId/ca/$caId' | '/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId' | '/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName' + | '/organizations/$orgId/projects/pam/$projectId/approval-requests/$approvalRequestId' | '/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId' | '/organizations/$orgId/projects/secret-management/$projectId/integrations/$integrationId' | '/organizations/$orgId/projects/secret-management/$projectId/integrations/select-integration-auth' @@ -6180,6 +6202,7 @@ export interface FileRouteTypes { | '/organizations/$orgId/projects/cert-management/$projectId/ca/$caId' | '/organizations/$orgId/projects/cert-management/$projectId/integrations/$syncId' | '/organizations/$orgId/projects/cert-management/$projectId/subscribers/$subscriberName' + | '/organizations/$orgId/projects/pam/$projectId/approval-requests/$approvalRequestId' | '/organizations/$orgId/projects/pam/$projectId/sessions/$sessionId' | '/organizations/$orgId/projects/secret-management/$projectId/integrations/$integrationId' | '/organizations/$orgId/projects/secret-management/$projectId/integrations/select-integration-auth' @@ -6432,6 +6455,7 @@ export interface FileRouteTypes { | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/ca/$caId' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/integrations/$syncId' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers/$subscriberName' + | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/approval-requests/$approvalRequestId' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/$integrationId' | '/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/secret-management/$projectId/_secret-manager-layout/integrations/select-integration-auth' @@ -7086,6 +7110,7 @@ export const routeTree = rootRoute "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/access-management", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/audit-logs", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions", + "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/approval-requests/$approvalRequestId", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/groups/$groupId", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/identities/$identityId", "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/members/$membershipId", @@ -7454,6 +7479,10 @@ export const routeTree = rootRoute "filePath": "cert-manager/PkiSubscriberDetailsByIDPage/route.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/cert-management/$projectId/_cert-manager-layout/subscribers" }, + "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/approval-requests/$approvalRequestId": { + "filePath": "pam/ApprovalRequestDetailPage/route.tsx", + "parent": "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout" + }, "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions/$sessionId": { "filePath": "pam/PamSessionsByIDPage/route.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/organizations/$orgId/projects/pam/$projectId/_pam-layout/sessions" diff --git a/frontend/src/routes.ts b/frontend/src/routes.ts index 0da91ba60..41d1c319d 100644 --- a/frontend/src/routes.ts +++ b/frontend/src/routes.ts @@ -358,10 +358,11 @@ const pamRoutes = route("/organizations/$orgId/projects/pam/$projectId", [ route("/resources", "pam/PamResourcesPage/route.tsx"), route("/audit-logs", "project/AuditLogsPage/route-pam.tsx"), route("/settings", "pam/SettingsPage/route.tsx"), + route("/approvals", "pam/ApprovalsPage/route.tsx"), + route("/approval-requests/$approvalRequestId", "pam/ApprovalRequestDetailPage/route.tsx"), // Access Management route("/access-management", "project/AccessControlPage/route-pam.tsx"), - route("/approvals", "pam/ApprovalsPage/route.tsx"), route("/roles/$roleSlug", "project/RoleDetailsBySlugPage/route-pam.tsx"), route("/identities/$identityId", "project/IdentityDetailsByIDPage/route-pam.tsx"), route("/members/$membershipId", "project/MemberDetailsByIDPage/route-pam.tsx"),