mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 13:28:34 +00:00
wip
This commit is contained in:
@@ -1064,6 +1064,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
type: z.nativeEnum(ProjectType).optional(),
|
type: z.nativeEnum(ProjectType).optional(),
|
||||||
orderBy: z.nativeEnum(SearchProjectSortBy).optional().default(SearchProjectSortBy.NAME),
|
orderBy: z.nativeEnum(SearchProjectSortBy).optional().default(SearchProjectSortBy.NAME),
|
||||||
orderDirection: z.nativeEnum(SortDirection).optional().default(SortDirection.ASC),
|
orderDirection: z.nativeEnum(SortDirection).optional().default(SortDirection.ASC),
|
||||||
|
projectIds: z.string().trim().array().optional(),
|
||||||
name: z
|
name: z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
|
|||||||
@@ -399,6 +399,7 @@ export const projectDALFactory = (db: TDbClient) => {
|
|||||||
name?: string;
|
name?: string;
|
||||||
sortBy?: SearchProjectSortBy;
|
sortBy?: SearchProjectSortBy;
|
||||||
sortDir?: SortDirection;
|
sortDir?: SortDirection;
|
||||||
|
projectIds?: string[];
|
||||||
}) => {
|
}) => {
|
||||||
const { limit = 20, offset = 0, sortBy = SearchProjectSortBy.NAME, sortDir = SortDirection.ASC } = dto;
|
const { limit = 20, offset = 0, sortBy = SearchProjectSortBy.NAME, sortDir = SortDirection.ASC } = dto;
|
||||||
|
|
||||||
@@ -454,6 +455,11 @@ export const projectDALFactory = (db: TDbClient) => {
|
|||||||
if (dto.name) {
|
if (dto.name) {
|
||||||
void query.whereILike(`${TableName.Project}.name`, `%${dto.name}%`);
|
void query.whereILike(`${TableName.Project}.name`, `%${dto.name}%`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dto.projectIds?.length) {
|
||||||
|
void query.whereIn(`${TableName.Project}.id`, dto.projectIds);
|
||||||
|
}
|
||||||
|
|
||||||
const docs = await query;
|
const docs = await query;
|
||||||
|
|
||||||
return { docs, totalCount: Number(docs?.[0]?.count ?? 0) };
|
return { docs, totalCount: Number(docs?.[0]?.count ?? 0) };
|
||||||
|
|||||||
@@ -1806,7 +1806,8 @@ export const projectServiceFactory = ({
|
|||||||
limit,
|
limit,
|
||||||
type,
|
type,
|
||||||
orderBy,
|
orderBy,
|
||||||
orderDirection
|
orderDirection,
|
||||||
|
projectIds
|
||||||
}: TSearchProjectsDTO) => {
|
}: TSearchProjectsDTO) => {
|
||||||
// check user belong to org
|
// check user belong to org
|
||||||
await permissionService.getOrgPermission(
|
await permissionService.getOrgPermission(
|
||||||
@@ -1822,6 +1823,7 @@ export const projectServiceFactory = ({
|
|||||||
offset,
|
offset,
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
|
projectIds,
|
||||||
orgId: permission.orgId,
|
orgId: permission.orgId,
|
||||||
actor: permission.type,
|
actor: permission.type,
|
||||||
actorId: permission.id,
|
actorId: permission.id,
|
||||||
|
|||||||
@@ -221,6 +221,7 @@ export type TSearchProjectsDTO = {
|
|||||||
limit?: number;
|
limit?: number;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
orderBy?: SearchProjectSortBy;
|
orderBy?: SearchProjectSortBy;
|
||||||
|
projectIds?: string[];
|
||||||
orderDirection?: SortDirection;
|
orderDirection?: SortDirection;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { Button, FormControl, Input, Modal, ModalClose, ModalContent } from "@app/components/v2";
|
||||||
|
import { useRequestProjectAccess } from "@app/hooks/api";
|
||||||
|
import { Workspace } from "@app/hooks/api/workspace/types";
|
||||||
|
|
||||||
|
type ContentProps = {
|
||||||
|
projectId: string;
|
||||||
|
onComplete: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Content = ({ projectId, onComplete }: ContentProps) => {
|
||||||
|
const form = useForm<{ note: string }>();
|
||||||
|
|
||||||
|
const requestProjectAccess = useRequestProjectAccess();
|
||||||
|
|
||||||
|
const onFormSubmit = ({ note }: { note: string }) => {
|
||||||
|
if (requestProjectAccess.isPending) return;
|
||||||
|
requestProjectAccess.mutate(
|
||||||
|
{
|
||||||
|
comment: note,
|
||||||
|
projectId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
createNotification({
|
||||||
|
type: "success",
|
||||||
|
title: "Project Access Request Sent",
|
||||||
|
text: "Project admins will receive an email of your request"
|
||||||
|
});
|
||||||
|
onComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
|
<FormControl label="Note">
|
||||||
|
<Input {...form.register("note")} />
|
||||||
|
</FormControl>
|
||||||
|
<div className="mt-4 flex items-center">
|
||||||
|
<Button className="mr-4" size="sm" type="submit" isLoading={form.formState.isSubmitting}>
|
||||||
|
Submit Request
|
||||||
|
</Button>
|
||||||
|
<ModalClose asChild>
|
||||||
|
<Button colorSchema="secondary" variant="plain">
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</ModalClose>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type RequestProjectAccessModalProps = {
|
||||||
|
isOpen: boolean;
|
||||||
|
onOpenChange: (isOpen: boolean) => void;
|
||||||
|
project?: Workspace;
|
||||||
|
onComplete?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RequestProjectAccessModal = ({
|
||||||
|
isOpen,
|
||||||
|
onOpenChange,
|
||||||
|
project,
|
||||||
|
onComplete
|
||||||
|
}: RequestProjectAccessModalProps) => {
|
||||||
|
if (!project) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||||
|
<ModalContent
|
||||||
|
title="Confirm Access Request"
|
||||||
|
subTitle={`Requesting access to project ${project?.name}. You may include an optional note for project admins to review your request.`}
|
||||||
|
>
|
||||||
|
<Content
|
||||||
|
onComplete={() => {
|
||||||
|
onOpenChange(false);
|
||||||
|
if (onComplete) onComplete();
|
||||||
|
}}
|
||||||
|
projectId={project?.id}
|
||||||
|
/>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1 +1,2 @@
|
|||||||
export { NewProjectModal } from "./NewProjectModal";
|
export { NewProjectModal } from "./NewProjectModal";
|
||||||
|
export * from "./RequestProjectAccessModal";
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ export type TSearchProjectsDTO = {
|
|||||||
name?: string;
|
name?: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
|
projectIds?: string[];
|
||||||
type?: ProjectType;
|
type?: ProjectType;
|
||||||
options?: { enabled?: boolean };
|
options?: { enabled?: boolean };
|
||||||
orderBy?: ProjectIdentityOrderBy;
|
orderBy?: ProjectIdentityOrderBy;
|
||||||
|
|||||||
+39
-35
@@ -38,11 +38,7 @@ import { OrgPermissionSubjects, ProjectPermissionSub } from "@app/context";
|
|||||||
import { OrgPermissionAppConnectionActions } from "@app/context/OrgPermissionContext/types";
|
import { OrgPermissionAppConnectionActions } from "@app/context/OrgPermissionContext/types";
|
||||||
import { ProjectPermissionAppConnectionActions } from "@app/context/ProjectPermissionContext/types";
|
import { ProjectPermissionAppConnectionActions } from "@app/context/ProjectPermissionContext/types";
|
||||||
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||||
import {
|
import { getUserTablePreference, PreferenceKey, setUserTablePreference } from "@app/helpers/userTablePreferences";
|
||||||
getUserTablePreference,
|
|
||||||
PreferenceKey,
|
|
||||||
setUserTablePreference
|
|
||||||
} from "@app/helpers/userTablePreferences";
|
|
||||||
import { usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
import { usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
||||||
import { TAppConnection, useListAppConnections } from "@app/hooks/api/appConnections";
|
import { TAppConnection, useListAppConnections } from "@app/hooks/api/appConnections";
|
||||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||||
@@ -311,38 +307,46 @@ export const AppConnectionsTable = ({ projectId, projectType }: Props) => {
|
|||||||
<FontAwesomeIcon icon={faFilter} />
|
<FontAwesomeIcon icon={faFilter} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="thin-scrollbar max-h-[70vh] overflow-y-auto" align="end">
|
<DropdownMenuContent
|
||||||
|
sideOffset={2}
|
||||||
|
className="thin-scrollbar max-h-[70vh] overflow-y-auto"
|
||||||
|
align="end"
|
||||||
|
>
|
||||||
<DropdownMenuLabel>Filter by Apps</DropdownMenuLabel>
|
<DropdownMenuLabel>Filter by Apps</DropdownMenuLabel>
|
||||||
{appConnections.length ? (
|
{appConnections.length ? (
|
||||||
[...new Set(appConnections.map(({ app }) => app))].map((app) => (
|
[...new Set(appConnections.map(({ app }) => app))]
|
||||||
<DropdownMenuItem
|
.sort((a, b) => {
|
||||||
onClick={(e) => {
|
return a.toLowerCase().localeCompare(b.toLowerCase());
|
||||||
e.preventDefault();
|
})
|
||||||
setFilters((prev) => ({
|
.map((app) => (
|
||||||
...prev,
|
<DropdownMenuItem
|
||||||
apps: prev.apps.includes(app)
|
onClick={(e) => {
|
||||||
? prev.apps.filter((a) => a !== app)
|
e.preventDefault();
|
||||||
: [...prev.apps, app]
|
setFilters((prev) => ({
|
||||||
}));
|
...prev,
|
||||||
}}
|
apps: prev.apps.includes(app)
|
||||||
key={app}
|
? prev.apps.filter((a) => a !== app)
|
||||||
icon={
|
: [...prev.apps, app]
|
||||||
filters.apps.includes(app) && (
|
}));
|
||||||
<FontAwesomeIcon className="text-primary" icon={faCheckCircle} />
|
}}
|
||||||
)
|
key={app}
|
||||||
}
|
icon={
|
||||||
iconPos="right"
|
filters.apps.includes(app) && (
|
||||||
>
|
<FontAwesomeIcon className="text-primary" icon={faCheckCircle} />
|
||||||
<div className="flex items-center gap-2">
|
)
|
||||||
<img
|
}
|
||||||
alt={`${APP_CONNECTION_MAP[app].name} integration`}
|
iconPos="right"
|
||||||
src={`/images/integrations/${APP_CONNECTION_MAP[app].image}`}
|
>
|
||||||
className="h-4 w-4"
|
<div className="flex items-center gap-2">
|
||||||
/>
|
<img
|
||||||
<span>{APP_CONNECTION_MAP[app].name}</span>
|
alt={`${APP_CONNECTION_MAP[app].name} integration`}
|
||||||
</div>
|
src={`/images/integrations/${APP_CONNECTION_MAP[app].image}`}
|
||||||
</DropdownMenuItem>
|
className="h-4 w-4"
|
||||||
))
|
/>
|
||||||
|
<span>{APP_CONNECTION_MAP[app].name}</span>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))
|
||||||
) : (
|
) : (
|
||||||
<DropdownMenuItem isDisabled>No Connections Configured</DropdownMenuItem>
|
<DropdownMenuItem isDisabled>No Connections Configured</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import {
|
import {
|
||||||
faArrowDownAZ,
|
faArrowDownAZ,
|
||||||
faBorderAll,
|
faBorderAll,
|
||||||
@@ -16,6 +15,7 @@ import { twMerge } from "tailwind-merge";
|
|||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
import { createNotification } from "@app/components/notifications";
|
||||||
import { OrgPermissionCan } from "@app/components/permissions";
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
|
import { RequestProjectAccessModal } from "@app/components/projects/RequestProjectAccessModal";
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
@@ -24,12 +24,9 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
FormControl,
|
|
||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
Lottie,
|
Lottie,
|
||||||
Modal,
|
|
||||||
ModalContent,
|
|
||||||
Pagination,
|
Pagination,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Tooltip
|
Tooltip
|
||||||
@@ -43,11 +40,7 @@ import {
|
|||||||
setUserTablePreference
|
setUserTablePreference
|
||||||
} from "@app/helpers/userTablePreferences";
|
} from "@app/helpers/userTablePreferences";
|
||||||
import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
||||||
import {
|
import { useOrgAdminAccessProject, useSearchProjects } from "@app/hooks/api";
|
||||||
useOrgAdminAccessProject,
|
|
||||||
useRequestProjectAccess,
|
|
||||||
useSearchProjects
|
|
||||||
} from "@app/hooks/api";
|
|
||||||
import { ProjectType, Workspace, WorkspaceEnv } from "@app/hooks/api/workspace/types";
|
import { ProjectType, Workspace, WorkspaceEnv } from "@app/hooks/api/workspace/types";
|
||||||
import {
|
import {
|
||||||
ProjectListToggle,
|
ProjectListToggle,
|
||||||
@@ -62,53 +55,6 @@ type Props = {
|
|||||||
onProjectListViewChange: (value: ProjectListView) => void;
|
onProjectListViewChange: (value: ProjectListView) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RequestAccessModalProps = {
|
|
||||||
projectId: string;
|
|
||||||
onPopUpToggle: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const RequestAccessModal = ({ projectId, onPopUpToggle }: RequestAccessModalProps) => {
|
|
||||||
const form = useForm<{ note: string }>();
|
|
||||||
|
|
||||||
const requestProjectAccess = useRequestProjectAccess();
|
|
||||||
|
|
||||||
const onFormSubmit = ({ note }: { note: string }) => {
|
|
||||||
if (requestProjectAccess.isPending) return;
|
|
||||||
requestProjectAccess.mutate(
|
|
||||||
{
|
|
||||||
comment: note,
|
|
||||||
projectId
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess: () => {
|
|
||||||
createNotification({
|
|
||||||
type: "success",
|
|
||||||
title: "Project Access Request Sent",
|
|
||||||
text: "Project admins will receive an email of your request"
|
|
||||||
});
|
|
||||||
onPopUpToggle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
|
||||||
<FormControl label="Note">
|
|
||||||
<Input {...form.register("note")} />
|
|
||||||
</FormControl>
|
|
||||||
<div className="mt-4 flex items-center">
|
|
||||||
<Button className="mr-4" size="sm" type="submit" isLoading={form.formState.isSubmitting}>
|
|
||||||
Submit Request
|
|
||||||
</Button>
|
|
||||||
<Button colorSchema="secondary" variant="plain" onClick={() => onPopUpToggle()}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AllProjectView = ({
|
export const AllProjectView = ({
|
||||||
onAddNewProject,
|
onAddNewProject,
|
||||||
onUpgradePlan,
|
onUpgradePlan,
|
||||||
@@ -419,20 +365,11 @@ export const AllProjectView = ({
|
|||||||
<div className="text-center font-light">No Projects Found</div>
|
<div className="text-center font-light">No Projects Found</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Modal
|
<RequestProjectAccessModal
|
||||||
isOpen={popUp.requestAccessConfirmation.isOpen}
|
isOpen={popUp.requestAccessConfirmation.isOpen}
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("requestAccessConfirmation", isOpen)}
|
onOpenChange={(isOpen) => handlePopUpToggle("requestAccessConfirmation", isOpen)}
|
||||||
>
|
project={requestedWorkspaceDetails}
|
||||||
<ModalContent
|
/>
|
||||||
title="Confirm Access Request"
|
|
||||||
subTitle={`Requesting access to project ${requestedWorkspaceDetails?.name}. You may include an optional note for project admins to review your request.`}
|
|
||||||
>
|
|
||||||
<RequestAccessModal
|
|
||||||
onPopUpToggle={() => handlePopUpToggle("requestAccessConfirmation")}
|
|
||||||
projectId={requestedWorkspaceDetails?.id}
|
|
||||||
/>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,17 @@ import { AxiosError } from "axios";
|
|||||||
|
|
||||||
import { Button } from "@app/components/v2";
|
import { Button } from "@app/components/v2";
|
||||||
|
|
||||||
|
import { ProjectAccessError } from "./components";
|
||||||
|
|
||||||
export const ErrorPage = ({ error }: ErrorComponentProps) => {
|
export const ErrorPage = ({ error }: ErrorComponentProps) => {
|
||||||
|
if (
|
||||||
|
error instanceof AxiosError &&
|
||||||
|
error.status === 403 &&
|
||||||
|
error.response?.data?.error === "User not a part of the specified project"
|
||||||
|
) {
|
||||||
|
return <ProjectAccessError />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen w-screen items-center justify-center bg-mineshaft-900">
|
<div className="flex h-screen w-screen items-center justify-center bg-mineshaft-900">
|
||||||
<div className="flex max-w-3xl flex-col rounded-md border border-mineshaft-600 bg-mineshaft-800 p-8 text-center text-mineshaft-200">
|
<div className="flex max-w-3xl flex-col rounded-md border border-mineshaft-600 bg-mineshaft-800 p-8 text-center text-mineshaft-200">
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { faHome } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { Link, useNavigate, useParams } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
|
import { RequestProjectAccessModal } from "@app/components/projects";
|
||||||
|
import { AccessRestrictedBanner, Button } from "@app/components/v2";
|
||||||
|
import { OrgPermissionSubjects } from "@app/context";
|
||||||
|
import { OrgPermissionAdminConsoleAction } from "@app/context/OrgPermissionContext/types";
|
||||||
|
import { getProjectHomePage } from "@app/helpers/project";
|
||||||
|
import { usePopUp } from "@app/hooks";
|
||||||
|
import { useOrgAdminAccessProject, useSearchProjects } from "@app/hooks/api";
|
||||||
|
|
||||||
|
export const ProjectAccessError = () => {
|
||||||
|
const orgAdminAccessProject = useOrgAdminAccessProject();
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const { popUp, handlePopUpToggle, handlePopUpOpen } = usePopUp([
|
||||||
|
"requestAccessConfirmation"
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const { projectId } = useParams({
|
||||||
|
strict: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data, isPending: isProjectLoading } = useSearchProjects({
|
||||||
|
projectIds: projectId ? [projectId] : [],
|
||||||
|
options: {
|
||||||
|
enabled: Boolean(projectId)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const [project] = data?.projects ?? [];
|
||||||
|
|
||||||
|
const handleAccessProject = async () => {
|
||||||
|
if (!project) return;
|
||||||
|
try {
|
||||||
|
await orgAdminAccessProject.mutateAsync({
|
||||||
|
projectId: project.id
|
||||||
|
});
|
||||||
|
await navigate({
|
||||||
|
to: getProjectHomePage(project.type, project.environments),
|
||||||
|
params: {
|
||||||
|
projectId: project.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
createNotification({
|
||||||
|
text: "Failed to access project",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<AccessRestrictedBanner
|
||||||
|
body={
|
||||||
|
<>
|
||||||
|
You are not currently a member of this project. Request access to join project.
|
||||||
|
<div className="mt-4 flex w-full justify-center gap-2">
|
||||||
|
<Link to="/organization/projects">
|
||||||
|
<Button variant="outline_bg">
|
||||||
|
<FontAwesomeIcon icon={faHome} className="mr-2" />
|
||||||
|
Back To Home
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<OrgPermissionCan
|
||||||
|
I={OrgPermissionAdminConsoleAction.AccessAllProjects}
|
||||||
|
an={OrgPermissionSubjects.AdminConsole}
|
||||||
|
>
|
||||||
|
{(isAllowed) =>
|
||||||
|
isAllowed ? (
|
||||||
|
<Button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
handleAccessProject();
|
||||||
|
}}
|
||||||
|
disabled={orgAdminAccessProject.isPending}
|
||||||
|
isLoading={isProjectLoading || orgAdminAccessProject.isPending}
|
||||||
|
>
|
||||||
|
Join Project as Admin
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
onClick={() => handlePopUpOpen("requestAccessConfirmation")}
|
||||||
|
isLoading={isProjectLoading}
|
||||||
|
>
|
||||||
|
Request Access to Project
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</OrgPermissionCan>
|
||||||
|
</div>
|
||||||
|
<RequestProjectAccessModal
|
||||||
|
isOpen={popUp.requestAccessConfirmation.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("requestAccessConfirmation", isOpen)}
|
||||||
|
project={project}
|
||||||
|
onComplete={() => {
|
||||||
|
navigate({
|
||||||
|
to: "/organization/projects"
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./ProjectAccessError";
|
||||||
Reference in New Issue
Block a user