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 (
+
+ );
+};
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..."
+ />
+
+
+
+
+
+ | Requester |
+ Resource / Path |
+ Duration |
+ Status |
+ Requested |
+
+
+
+ {isRequestsLoading && }
+ {!isRequestsLoading &&
+ paginatedRequests.map((request) => {
+ const needsApproval = checkIfUserNeedsToApprove(request, userId);
+ const { resourceId, accountPath, requestDurationSeconds } =
+ request.requestData.requestData;
+
+ return (
+ handleRowClick(request.id)}
+ >
+
+
+
+ {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"),