mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: resolved a lot of ts issues, planning to migrate to v5 tanstack query
This commit is contained in:
47
frontend-v2/package-lock.json
generated
47
frontend-v2/package-lock.json
generated
@@ -42,9 +42,10 @@
|
||||
"@radix-ui/react-toast": "^1.2.3",
|
||||
"@radix-ui/react-tooltip": "^1.1.5",
|
||||
"@sindresorhus/slugify": "^2.2.1",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@tanstack/react-query": "^5.62.7",
|
||||
"@tanstack/react-router": "^1.87.9",
|
||||
"@tanstack/virtual-file-routes": "^1.87.6",
|
||||
"@tanstack/zod-adapter": "^1.91.0",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@ucast/mongo2js": "^1.3.4",
|
||||
"argon2-browser": "^1.18.0",
|
||||
@@ -3628,9 +3629,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "4.36.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.36.1.tgz",
|
||||
"integrity": "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==",
|
||||
"version": "5.62.7",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.62.7.tgz",
|
||||
"integrity": "sha512-fgpfmwatsrUal6V+8EC2cxZIQVl9xvL7qYa03gsdsCy985UTUlS4N+/3hCzwR0PclYDqisca2AqR1BVgJGpUDA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -3638,30 +3639,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "4.36.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.36.1.tgz",
|
||||
"integrity": "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==",
|
||||
"version": "5.62.7",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.62.7.tgz",
|
||||
"integrity": "sha512-+xCtP4UAFDTlRTYyEjLx0sRtWyr5GIk7TZjZwBu4YaNahi3Rt2oMyRqfpfVrtwsqY2sayP4iXVCwmC+ZqqFmuw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "4.36.1",
|
||||
"use-sync-external-store": "^1.2.0"
|
||||
"@tanstack/query-core": "5.62.7"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-native": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
}
|
||||
"react": "^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-router": {
|
||||
@@ -3855,6 +3845,23 @@
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/zod-adapter": {
|
||||
"version": "1.91.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/zod-adapter/-/zod-adapter-1.91.0.tgz",
|
||||
"integrity": "sha512-OccU1RwZ7svIyztcWrVreurWUwaTv98wAWTjzAOS6fe8Ajl9oLT9+mvj4vdi5gn5ycKyu3xkrIPhLIp5fGLZcQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/react-router": ">=1.43.2",
|
||||
"zod": "^3.23.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/argon2-browser": {
|
||||
"version": "1.18.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/argon2-browser/-/argon2-browser-1.18.4.tgz",
|
||||
|
||||
@@ -46,9 +46,10 @@
|
||||
"@radix-ui/react-toast": "^1.2.3",
|
||||
"@radix-ui/react-tooltip": "^1.1.5",
|
||||
"@sindresorhus/slugify": "^2.2.1",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@tanstack/react-query": "^5.62.7",
|
||||
"@tanstack/react-router": "^1.87.9",
|
||||
"@tanstack/virtual-file-routes": "^1.87.6",
|
||||
"@tanstack/zod-adapter": "^1.91.0",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@ucast/mongo2js": "^1.3.4",
|
||||
"argon2-browser": "^1.18.0",
|
||||
|
||||
@@ -90,7 +90,12 @@ export default function NavHeader({
|
||||
)}
|
||||
<FontAwesomeIcon icon={faAngleRight} className="ml-3 mr-3 text-sm text-gray-400" />
|
||||
{pageName === "Secrets" ? (
|
||||
<Link to={{ pathname: "/project/[id]/secrets/overview", query: { id: navigate.query.id } }}>
|
||||
<Link
|
||||
to={{
|
||||
pathname: "/project/$projectId/secrets/overview",
|
||||
params: { id: currentWorkspace?.id }
|
||||
}}
|
||||
>
|
||||
<a className="text-sm font-semibold text-primary/80 hover:text-primary">{pageName}</a>
|
||||
</Link>
|
||||
) : (
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { FC } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import z from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { Button, FormControl, Input, Modal, ModalContent } from "@app/components/v2";
|
||||
import { useCreateOrg, useSelectOrganization } from "@app/hooks/api";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
@@ -58,7 +58,7 @@ export const CreateOrgModal: FC<CreateOrgModalProps> = ({ isOpen, onClose }) =>
|
||||
});
|
||||
|
||||
navigate({
|
||||
to: `/organization/$organizationId/${ProjectType.SecretManager}` as const,
|
||||
to: `/organization/$organizationId/${ProjectType.SecretManager}/overview` as const,
|
||||
params: {
|
||||
organizationId: organization.id
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ const attemptChangePassword = ({ email, currentPassword, newPassword }: Params):
|
||||
const clientProof = clientOldPassword.getProof();
|
||||
|
||||
clientNewPassword.init({ username: email, password: newPassword }, async () => {
|
||||
clientNewPassword.createVerifier(async (err, result) => {
|
||||
clientNewPassword.createVerifier(async (_err, result) => {
|
||||
try {
|
||||
const derivedKey = await deriveArgonKey({
|
||||
password: newPassword,
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/* eslint-disable prefer-template */
|
||||
/* eslint-disable prefer-rest-params */
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
/* eslint-disable no-nested-ternary */
|
||||
/* eslint-disable no-unexpected-multiline */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable vars-on-top */
|
||||
/* eslint-disable no-var */
|
||||
/* eslint-disable func-names */
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-nocheck
|
||||
|
||||
import { INTERCOMid as APPid } from "@app/components/utilities/config";
|
||||
|
||||
// Loads Intercom with the snippet
|
||||
// This must be run before boot, it initializes window.Intercom
|
||||
|
||||
// prettier-ignore
|
||||
export const load = () => {
|
||||
(function(){
|
||||
var w=window;
|
||||
var ic=w.Intercom;
|
||||
|
||||
if(typeof ic==="function"){
|
||||
ic("reattach_activator");
|
||||
ic("update",w.intercomSettings);
|
||||
} else {
|
||||
var d=document;
|
||||
var i=function() {
|
||||
i.c(arguments);
|
||||
};
|
||||
i.q=[];
|
||||
i.c=function(args) {
|
||||
i.q.push(args);
|
||||
};
|
||||
w.Intercom=i;
|
||||
var l=function() {
|
||||
var s=d.createElement("script");
|
||||
s.type="text/javascript";
|
||||
s.async=true;
|
||||
s.src="https://widget.intercom.io/widget/" + APPid;
|
||||
var x=d.getElementsByTagName("script")[0];
|
||||
x.parentNode.insertBefore(s, x);
|
||||
};
|
||||
if (document.readyState==="complete") {
|
||||
l();
|
||||
} else if (w.attachEvent) {
|
||||
w.attachEvent("onload",l);
|
||||
} else {
|
||||
w.addEventListener("load",l,false);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
// Initializes Intercom
|
||||
export const boot = (options = {}) => {
|
||||
window && window.Intercom && window.Intercom("boot", { appid: APPid, ...options });
|
||||
};
|
||||
|
||||
export const update = () => {
|
||||
window && window.Intercom && window.Intercom("update");
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { useUser } from "@app/context";
|
||||
|
||||
import { boot as bootIntercom, load as loadIntercom, update as updateIntercom } from "./intercom";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const IntercomProvider = ({ children }: { children: any }) => {
|
||||
const { user } = useUser();
|
||||
const router = useRouter();
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
loadIntercom();
|
||||
bootIntercom({
|
||||
name: `${user?.firstName || ""} ${user?.lastName || ""}`,
|
||||
email: user?.email || "",
|
||||
created_at: Math.floor((new Date(user?.createdAt)?.getTime() || 0) / 1000)
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const handleRouteChange = (url: string) => {
|
||||
if (typeof window !== "undefined") {
|
||||
updateIntercom();
|
||||
}
|
||||
};
|
||||
|
||||
router.events.on("routeChangeStart", handleRouteChange);
|
||||
|
||||
// If the component is unmounted, unsubscribe
|
||||
// from the event with the `off` method:
|
||||
return () => {
|
||||
router.events.off("routeChangeStart", handleRouteChange);
|
||||
};
|
||||
}, [router.events]);
|
||||
|
||||
return children;
|
||||
};
|
||||
@@ -39,7 +39,7 @@ export const SecretPathInput = ({
|
||||
const workspaceId = currentWorkspace?.id || "";
|
||||
const { folderNames: folders } = useGetFoldersByEnv({
|
||||
path: secretPath,
|
||||
environments: [environment || currentWorkspace?.environments?.[0].slug],
|
||||
environments: [environment || currentWorkspace?.environments?.[0].slug || ""],
|
||||
projectId: workspaceId
|
||||
});
|
||||
|
||||
|
||||
@@ -1,54 +1,5 @@
|
||||
import { createContext, ReactNode } from "react";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
import { useGetUserOrgPermissions } from "@app/hooks/api";
|
||||
import { OrgUser } from "@app/hooks/api/types";
|
||||
|
||||
import { useOrganization } from "../OrganizationContext";
|
||||
import { TOrgPermission } from "./types";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const OrgPermissionContext = createContext<null | {
|
||||
permission: TOrgPermission;
|
||||
membership: OrgUser | null;
|
||||
}>(null);
|
||||
|
||||
export const OrgPermissionProvider = ({ children }: Props): JSX.Element => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { data: permission, isLoading } = useGetUserOrgPermissions({ orgId });
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-screen w-screen items-center justify-center bg-bunker-800">
|
||||
<img
|
||||
src="/images/loading/loading.gif"
|
||||
height={70}
|
||||
width={120}
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
alt="infisical loading indicator"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!permission) {
|
||||
return (
|
||||
<div className="flex h-screen w-screen items-center justify-center bg-bunker-800">
|
||||
Failed to load user permissions
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<OrgPermissionContext.Provider value={permission}>{children}</OrgPermissionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useOrgPermission = () => {
|
||||
const ctx = useRouteContext({
|
||||
from: "/_authenticate/_org_details",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { OrgPermissionProvider, useOrgPermission } from "./OrgPermissionContext";
|
||||
export { useOrgPermission } from "./OrgPermissionContext";
|
||||
export type { TOrgPermission } from "./types";
|
||||
export { OrgPermissionActions, OrgPermissionSubjects } from "./types";
|
||||
|
||||
@@ -1,40 +1,5 @@
|
||||
import { createContext, ReactNode, useContext, useMemo } from "react";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
import { useGetOrganizations } from "@app/hooks/api";
|
||||
import { Organization } from "@app/hooks/api/types";
|
||||
|
||||
type TOrgContext = {
|
||||
orgs?: Organization[];
|
||||
currentOrg?: Organization;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
const OrgContext = createContext<TOrgContext | null>(null);
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const OrgProvider = ({ children }: Props): JSX.Element => {
|
||||
const { data: userOrgs, isLoading } = useGetOrganizations();
|
||||
|
||||
// const currentWsOrgID = currentWorkspace?.organization;
|
||||
const currentWsOrgID = localStorage.getItem("orgData.id");
|
||||
|
||||
// memorize the workspace details for the context
|
||||
const value = useMemo<TOrgContext>(
|
||||
() => ({
|
||||
orgs: userOrgs,
|
||||
currentOrg: (userOrgs || []).find(({ id }) => id === currentWsOrgID),
|
||||
isLoading
|
||||
}),
|
||||
[currentWsOrgID, userOrgs, isLoading]
|
||||
);
|
||||
|
||||
return <OrgContext.Provider value={value}>{children}</OrgContext.Provider>;
|
||||
};
|
||||
|
||||
export const useOrganization = () => {
|
||||
const currentOrg = useRouteContext({
|
||||
from: "/_authenticate/_org_details",
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { OrgProvider, useOrganization } from "./OrganizationContext";
|
||||
export { useOrganization } from "./OrganizationContext";
|
||||
|
||||
@@ -1,86 +1,11 @@
|
||||
import { createContext, ReactNode, useEffect, useMemo } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useNavigate, useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
import { ContentLoader } from "@app/components/v2/ContentLoader";
|
||||
import { useGetServerConfig } from "@app/hooks/api";
|
||||
import { TServerConfig } from "@app/hooks/api/admin/types";
|
||||
|
||||
type TServerConfigContext = {
|
||||
config: TServerConfig;
|
||||
};
|
||||
|
||||
const ServerConfigContext = createContext<TServerConfigContext | null>(null);
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const ServerConfigProvider = ({ children }: Props): JSX.Element => {
|
||||
const navigate = useNavigate();
|
||||
const { data, isLoading } = useGetServerConfig();
|
||||
|
||||
// memorize the workspace details for the context
|
||||
const value = useMemo<TServerConfigContext>(() => {
|
||||
return {
|
||||
config: data!
|
||||
};
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && data && !data.initialized && !data.isMigrationModeOn) {
|
||||
navigate({ to: "/admin/signup" });
|
||||
}
|
||||
}, [isLoading, data]);
|
||||
|
||||
if (!isLoading && data?.isMigrationModeOn) {
|
||||
return (
|
||||
<div className="relative mx-auto flex h-screen w-full flex-col items-center justify-center space-y-8 bg-bunker-800 px-8 text-mineshaft-50 dark:[color-scheme:dark]">
|
||||
<Helmet>
|
||||
<title>Infisical Maintenance Mode</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Helmet>
|
||||
<img
|
||||
src="/images/maintenance.png"
|
||||
height={175}
|
||||
width={300}
|
||||
alt="maintenance mode"
|
||||
className="w-[40rem]"
|
||||
/>
|
||||
<p className="mx-8 mb-4 flex justify-center bg-gradient-to-tr from-mineshaft-300 to-white bg-clip-text text-4xl font-bold text-transparent md:mx-16">
|
||||
Scheduled Maintenance
|
||||
</p>
|
||||
<div className="mt-2 text-center text-lg text-bunker-300">
|
||||
Infisical is undergoing planned maintenance. <br /> No action is required on your end —
|
||||
your applications will continue to fetch secrets.
|
||||
<br /> If you have questions, please{" "}
|
||||
<a
|
||||
className="text-bunker-300 underline decoration-primary-800 underline-offset-4 duration-200 hover:text-mineshaft-100 hover:decoration-primary-600"
|
||||
href="https://infisical.com/slack"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
join our Slack community
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="bg-bunker-800">
|
||||
<ContentLoader text="Loading configurations" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <ServerConfigContext.Provider value={value}>{children}</ServerConfigContext.Provider>;
|
||||
};
|
||||
import { adminQueryKeys, fetchServerConfig } from "@app/hooks/api/admin/queries";
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
|
||||
export const useServerConfig = () => {
|
||||
const config = useRouteContext({ from: "__root__", select: (el) => el.serverConfig });
|
||||
const { data: config } = useSuspenseQuery({
|
||||
queryKey: adminQueryKeys.serverConfig(),
|
||||
queryFn: fetchServerConfig
|
||||
});
|
||||
|
||||
return { config };
|
||||
};
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { ServerConfigProvider, useServerConfig } from "./ServerConfigContext";
|
||||
export { useServerConfig } from "./ServerConfigContext";
|
||||
|
||||
@@ -1,41 +1,5 @@
|
||||
import { createContext, ReactNode, useContext, useMemo } from "react";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
import { useGetOrgSubscription } from "@app/hooks/api";
|
||||
import { SubscriptionPlan } from "@app/hooks/api/types";
|
||||
|
||||
import { useOrganization } from "../OrganizationContext";
|
||||
|
||||
type TSubscriptionContext = {
|
||||
subscription?: SubscriptionPlan;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
const SubscriptionContext = createContext<TSubscriptionContext | null>(null);
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const SubscriptionProvider = ({ children }: Props): JSX.Element => {
|
||||
const { currentOrg } = useOrganization();
|
||||
|
||||
const { data, isLoading } = useGetOrgSubscription({
|
||||
orgID: currentOrg?.id || ""
|
||||
});
|
||||
|
||||
// memorize the workspace details for the context
|
||||
const value = useMemo<TSubscriptionContext>(
|
||||
() => ({
|
||||
subscription: data,
|
||||
isLoading
|
||||
}),
|
||||
[data, isLoading]
|
||||
);
|
||||
|
||||
return <SubscriptionContext.Provider value={value}>{children}</SubscriptionContext.Provider>;
|
||||
};
|
||||
|
||||
export const useSubscription = () => {
|
||||
const subscription = useRouteContext({
|
||||
from: "/_authenticate/_org_details",
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { SubscriptionProvider, useSubscription } from "./SubscriptionContext";
|
||||
export { useSubscription } from "./SubscriptionContext";
|
||||
|
||||
@@ -1,49 +1,5 @@
|
||||
import { createContext, ReactNode, useContext, useMemo } from "react";
|
||||
import { useRouteContext } from "@tanstack/react-router";
|
||||
|
||||
import { useGetUser } from "@app/hooks/api";
|
||||
import { User, UserEnc } from "@app/hooks/api/types";
|
||||
|
||||
type TUserContext = {
|
||||
user: User & UserEnc;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
const UserContext = createContext<TUserContext | null>(null);
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const UserProvider = ({ children }: Props): JSX.Element => {
|
||||
const { data, isLoading } = useGetUser();
|
||||
|
||||
// memorize the workspace details for the context
|
||||
const value = useMemo<TUserContext>(() => {
|
||||
return {
|
||||
user: data!,
|
||||
isLoading
|
||||
};
|
||||
}, [data, isLoading]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-screen w-screen items-center justify-center bg-bunker-800">
|
||||
<img
|
||||
src="/images/loading/loading.gif"
|
||||
height={70}
|
||||
width={120}
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
alt="infisical loading indicator"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
|
||||
};
|
||||
|
||||
export const useUser = () => {
|
||||
const user = useRouteContext({ from: "/_authenticate", select: (el) => el.user })!;
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { UserProvider, useUser } from "./UserContext";
|
||||
export { useUser } from "./UserContext";
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
export { AuthProvider } from "./AuthContext";
|
||||
export { OrgProvider, useOrganization } from "./OrganizationContext";
|
||||
export { useOrganization } from "./OrganizationContext";
|
||||
export type { TOrgPermission } from "./OrgPermissionContext";
|
||||
export {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionProvider,
|
||||
OrgPermissionSubjects,
|
||||
useOrgPermission
|
||||
} from "./OrgPermissionContext";
|
||||
@@ -16,7 +15,7 @@ export {
|
||||
ProjectPermissionSub,
|
||||
useProjectPermission
|
||||
} from "./ProjectPermissionContext";
|
||||
export { ServerConfigProvider, useServerConfig } from "./ServerConfigContext";
|
||||
export { SubscriptionProvider, useSubscription } from "./SubscriptionContext";
|
||||
export { UserProvider, useUser } from "./UserContext";
|
||||
export { useServerConfig } from "./ServerConfigContext";
|
||||
export { useSubscription } from "./SubscriptionContext";
|
||||
export { useUser } from "./UserContext";
|
||||
export { useWorkspace, WorkspaceProvider } from "./WorkspaceContext";
|
||||
|
||||
@@ -60,16 +60,15 @@ export const initProjectHelper = async ({ projectName }: { projectName: string }
|
||||
|
||||
return project;
|
||||
};
|
||||
|
||||
export const getProjectHomePage = (workspace: Workspace) => {
|
||||
if (workspace.type === ProjectType.SecretManager) {
|
||||
return `/${workspace.type}/${workspace.id}/secrets/overview`;
|
||||
return `/${workspace.type}/$projectId/secrets/overview` as const;
|
||||
}
|
||||
if (workspace.type === ProjectType.CertificateManager) {
|
||||
return `/${workspace.type}/${workspace.id}/certificates`;
|
||||
return `/${workspace.type}/$projectId/certificates` as const;
|
||||
}
|
||||
|
||||
return `/${workspace.type}/${workspace.id}/kms`;
|
||||
return `/${workspace.type}/$projectId/kms` as const;
|
||||
};
|
||||
|
||||
export const getProjectTitle = (type: ProjectType) => {
|
||||
|
||||
@@ -89,10 +89,9 @@ export const useSelectOrganization = () => {
|
||||
return data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries([
|
||||
organizationKeys.getUserOrganizations,
|
||||
workspaceKeys.getAllUserWorkspace
|
||||
]);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [organizationKeys.getUserOrganizations, workspaceKeys.getAllUserWorkspace]
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -21,7 +21,9 @@ export const useImportEnvKey = () => {
|
||||
"Content-Type": "multipart/form-data"
|
||||
},
|
||||
onUploadProgress: (progressEvent) => {
|
||||
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
||||
const percentCompleted = Math.round(
|
||||
(progressEvent.loaded * 100) / (progressEvent?.total || 1)
|
||||
);
|
||||
console.log(`Upload Progress: ${percentCompleted}%`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -143,7 +143,7 @@ const fetchSecretApprovalRequestList = async ({
|
||||
export const useGetSecretApprovalRequests = ({
|
||||
workspaceId,
|
||||
environment,
|
||||
options = object,
|
||||
options = {},
|
||||
status,
|
||||
limit = 20,
|
||||
committer
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { apiRequest } from "@app/config/request";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { TSecretScanningGitRisks } from "./types";
|
||||
|
||||
export const secretScanningQueryKeys = {
|
||||
|
||||
@@ -49,9 +49,10 @@ export const useGetActiveSharedSecretById = ({
|
||||
hashedHex: string | null;
|
||||
password?: string;
|
||||
}) => {
|
||||
return useQuery<TViewSharedSecretResponse>(
|
||||
secretSharingKeys.getSecretById({ id: sharedSecretId, hashedHex, password }),
|
||||
async () => {
|
||||
return useQuery({
|
||||
queryKey: secretSharingKeys.getSecretById({ id: sharedSecretId, hashedHex, password }),
|
||||
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.post<TViewSharedSecretResponse>(
|
||||
`/api/v1/secret-sharing/public/${sharedSecretId}`,
|
||||
{
|
||||
@@ -61,9 +62,8 @@ export const useGetActiveSharedSecretById = ({
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
enabled: Boolean(sharedSecretId)
|
||||
}
|
||||
);
|
||||
}, {
|
||||
enabled: Boolean(sharedSecretId)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -148,14 +148,18 @@ export const usePerformSecretRollback = () => {
|
||||
return data;
|
||||
},
|
||||
onSuccess: (_, { workspaceId, environment, directory }) => {
|
||||
queryClient.invalidateQueries([
|
||||
{ workspaceId, environment, secretPath: directory },
|
||||
"secrets"
|
||||
]);
|
||||
queryClient.invalidateQueries([
|
||||
"secret-folders",
|
||||
{ projectId: workspaceId, environment, path: directory }
|
||||
]);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [
|
||||
{ workspaceId, environment, secretPath: directory },
|
||||
"secrets"
|
||||
]
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [
|
||||
"secret-folders",
|
||||
{ projectId: workspaceId, environment, path: directory }
|
||||
]
|
||||
});
|
||||
queryClient.invalidateQueries(
|
||||
secretSnapshotKeys.list({ workspaceId, environment, directory })
|
||||
);
|
||||
|
||||
@@ -148,9 +148,12 @@ export const useGetUserWorkspaces = ({
|
||||
includeRoles?: boolean;
|
||||
type?: ProjectType | "all";
|
||||
} = {}) =>
|
||||
useQuery(workspaceKeys.getAllUserWorkspace(type || ""), () =>
|
||||
fetchUserWorkspaces(includeRoles, type)
|
||||
);
|
||||
useQuery({
|
||||
queryKey: workspaceKeys.getAllUserWorkspace(type || ""),
|
||||
|
||||
queryFn: () =>
|
||||
fetchUserWorkspaces(includeRoles, type)
|
||||
});
|
||||
|
||||
const fetchUserWorkspaceMemberships = async (orgId: string) => {
|
||||
const { data } = await apiRequest.get<Record<string, Workspace[]>>(
|
||||
@@ -324,7 +327,9 @@ export const useDeleteWorkspace = () => {
|
||||
},
|
||||
onSuccess: (dto) => {
|
||||
queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace(dto.type));
|
||||
queryClient.invalidateQueries(["org-admin-projects"]);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["org-admin-projects"]
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -74,7 +74,9 @@ export const OrganizationLayout = () => {
|
||||
<div className="px-1">
|
||||
<Menu className="mt-4">
|
||||
<Link
|
||||
to={`/organization/$organizationId/${ProjectType.SecretManager}` as const}
|
||||
to={
|
||||
`/organization/$organizationId/${ProjectType.SecretManager}/overview` as const
|
||||
}
|
||||
params={{ organizationId: currentOrg.id }}
|
||||
activeOptions={{ exact: true }}
|
||||
>
|
||||
@@ -86,7 +88,7 @@ export const OrganizationLayout = () => {
|
||||
</Link>
|
||||
<Link
|
||||
to={
|
||||
`/organization/$organizationid/${ProjectType.CertificateManager}` as const
|
||||
`/organization/$organizationId/${ProjectType.CertificateManager}/overview` as const
|
||||
}
|
||||
params={{ organizationId: currentOrg.id }}
|
||||
activeOptions={{ exact: true }}
|
||||
@@ -98,7 +100,7 @@ export const OrganizationLayout = () => {
|
||||
)}
|
||||
</Link>
|
||||
<Link
|
||||
to={`/organization/$organizationId/${ProjectType.KMS}` as const}
|
||||
to={`/organization/$organizationId/${ProjectType.KMS}/overview` as const}
|
||||
params={{ organizationId: currentOrg.id }}
|
||||
activeOptions={{ exact: true }}
|
||||
>
|
||||
|
||||
@@ -32,7 +32,9 @@ export const PersonalSettingsLayout = () => {
|
||||
<nav className="items-between flex h-full flex-col justify-between overflow-y-auto dark:[color-scheme:dark]">
|
||||
<div className="flex-grow">
|
||||
<Link
|
||||
to={`/organization/$organizationId/${ProjectType.SecretManager}` as const}
|
||||
to={
|
||||
`/organization/$organizationId/${ProjectType.SecretManager}/overview` as const
|
||||
}
|
||||
params={{ organizationId }}
|
||||
>
|
||||
<div className="my-6 flex cursor-default items-center justify-center pr-2 text-sm text-mineshaft-300 hover:text-mineshaft-100">
|
||||
|
||||
@@ -30,7 +30,7 @@ const RootPage = () => {
|
||||
export const Route = createRootRouteWithContext<TRouterContext>()({
|
||||
component: RootPage,
|
||||
beforeLoad: async ({ context }) => {
|
||||
const serverConfig = await context.queryClient.fetchQuery({
|
||||
const serverConfig = await context.queryClient.ensureQueryData({
|
||||
queryKey: adminQueryKeys.serverConfig(),
|
||||
queryFn: fetchServerConfig
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { faEllipsis, faMagnifyingGlass, faSignIn } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { format } from "date-fns";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
@@ -31,7 +32,6 @@ import {
|
||||
import { withPermission } from "@app/hoc";
|
||||
import { useDebounce } from "@app/hooks";
|
||||
import { useOrgAdminAccessProject, useOrgAdminGetProjects } from "@app/hooks/api";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
export const OrgAdminProjects = withPermission(
|
||||
|
||||
@@ -1,38 +1,31 @@
|
||||
import { useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { useState } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { Tab, TabList, TabPanel, Tabs } from '@app/components/v2'
|
||||
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
||||
|
||||
import { OrgAdminProjects } from './-components/OrgAdminProjects'
|
||||
import { OrgAdminProjects } from "./-components/OrgAdminProjects";
|
||||
|
||||
enum TabSections {
|
||||
Projects = 'projects',
|
||||
Projects = "projects"
|
||||
}
|
||||
|
||||
const OrgAdminPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const [activeTab, setActiveTab] = useState<TabSections>(TabSections.Projects)
|
||||
const { t } = useTranslation();
|
||||
const [activeTab, setActiveTab] = useState<TabSections>(TabSections.Projects);
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{t('common.head-title', { title: t('settings.org.title') })}
|
||||
</title>
|
||||
<title>{t("common.head-title", { title: t("settings.org.title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Helmet>
|
||||
<div className="flex w-full justify-center bg-bunker-800 py-6 text-white">
|
||||
<div className="w-full max-w-6xl px-6">
|
||||
<div className="mb-4">
|
||||
<p className="text-3xl font-semibold text-gray-200">
|
||||
Organization Admin Console
|
||||
</p>
|
||||
<p className="text-3xl font-semibold text-gray-200">Organization Admin Console</p>
|
||||
</div>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onValueChange={(el) => setActiveTab(el as TabSections)}
|
||||
>
|
||||
<Tabs value={activeTab} onValueChange={(el) => setActiveTab(el as TabSections)}>
|
||||
<TabList>
|
||||
<Tab value={TabSections.Projects}>Projects</Tab>
|
||||
</TabList>
|
||||
@@ -43,11 +36,11 @@ const OrgAdminPage = () => {
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/admin/',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/admin/"
|
||||
)({
|
||||
component: OrgAdminPage,
|
||||
})
|
||||
component: OrgAdminPage
|
||||
});
|
||||
|
||||
@@ -1,52 +1,50 @@
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from '@app/context'
|
||||
import { withPermission } from '@app/hoc'
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
|
||||
import { BillingTabGroup } from './-components'
|
||||
import { BillingTabGroup } from "./-components";
|
||||
|
||||
const BillngPage = withPermission(
|
||||
() => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
|
||||
<div className="w-full max-w-7xl px-6">
|
||||
<div className="my-6">
|
||||
<p className="text-3xl font-semibold text-gray-200">
|
||||
{t('billing.title')}
|
||||
</p>
|
||||
<p className="text-3xl font-semibold text-gray-200">{t("billing.title")}</p>
|
||||
<div />
|
||||
</div>
|
||||
<BillingTabGroup />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
},
|
||||
{
|
||||
action: OrgPermissionActions.Read,
|
||||
subject: OrgPermissionSubjects.Billing,
|
||||
},
|
||||
)
|
||||
subject: OrgPermissionSubjects.Billing
|
||||
}
|
||||
);
|
||||
|
||||
const BillingRoute = () => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="h-full bg-bunker-800">
|
||||
<Helmet>
|
||||
<title>{t('common.head-title', { title: t('billing.title') })}</title>
|
||||
<title>{t("common.head-title", { title: t("billing.title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
<meta property="og:image" content="/images/message.png" />
|
||||
</Helmet>
|
||||
<BillngPage />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/billing/',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/billing/"
|
||||
)({
|
||||
component: BillingRoute,
|
||||
})
|
||||
component: BillingRoute
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { faChevronLeft, faEllipsis } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faChevronLeft, faEllipsis } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createFileRoute, useNavigate, useParams } from "@tanstack/react-router";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from '@app/components/notifications'
|
||||
import { OrgPermissionCan } from '@app/components/permissions'
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
@@ -16,81 +16,74 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
Spinner,
|
||||
Tooltip,
|
||||
UpgradePlanModal,
|
||||
} from '@app/components/v2'
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization,
|
||||
} from '@app/context'
|
||||
import { withPermission } from '@app/hoc'
|
||||
import { useDeleteGroup } from '@app/hooks/api'
|
||||
import { useGetGroupById } from '@app/hooks/api/groups/queries'
|
||||
import { usePopUp } from '@app/hooks/usePopUp'
|
||||
UpgradePlanModal
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
import { useDeleteGroup } from "@app/hooks/api";
|
||||
import { useGetGroupById } from "@app/hooks/api/groups/queries";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
import { GroupCreateUpdateModal } from './-components/GroupCreateUpdateModal'
|
||||
import { GroupMembersSection } from './-components/GroupMembersSection'
|
||||
import { GroupDetailsSection } from './-components'
|
||||
import { GroupCreateUpdateModal } from "./-components/GroupCreateUpdateModal";
|
||||
import { GroupMembersSection } from "./-components/GroupMembersSection";
|
||||
import { GroupDetailsSection } from "./-components";
|
||||
|
||||
export enum TabSections {
|
||||
Member = 'members',
|
||||
Groups = 'groups',
|
||||
Roles = 'roles',
|
||||
Identities = 'identities',
|
||||
Member = "members",
|
||||
Groups = "groups",
|
||||
Roles = "roles",
|
||||
Identities = "identities"
|
||||
}
|
||||
|
||||
const GroupPage = withPermission(
|
||||
() => {
|
||||
const navigate = useNavigate()
|
||||
const navigate = useNavigate();
|
||||
const params = useParams({
|
||||
from: '/_authenticate/_org_details/_org-layout/organization/$organizationId/groups/$groupId',
|
||||
})
|
||||
const groupId = params.groupId as string
|
||||
const { currentOrg } = useOrganization()
|
||||
from: "/_authenticate/_org_details/_org-layout/organization/$organizationId/groups/$groupId/"
|
||||
});
|
||||
const groupId = params.groupId as string;
|
||||
const { currentOrg } = useOrganization();
|
||||
|
||||
const { data, isLoading } = useGetGroupById(groupId)
|
||||
const { data, isLoading } = useGetGroupById(groupId);
|
||||
|
||||
const { mutateAsync: deleteMutateAsync } = useDeleteGroup()
|
||||
const { mutateAsync: deleteMutateAsync } = useDeleteGroup();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } =
|
||||
usePopUp(['groupCreateUpdate', 'deleteGroup', 'upgradePlan'] as const)
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"groupCreateUpdate",
|
||||
"deleteGroup",
|
||||
"upgradePlan"
|
||||
] as const);
|
||||
|
||||
const onDeleteGroupSubmit = async ({
|
||||
name,
|
||||
id,
|
||||
}: {
|
||||
name: string
|
||||
id: string
|
||||
}) => {
|
||||
const onDeleteGroupSubmit = async ({ name, id }: { name: string; id: string }) => {
|
||||
try {
|
||||
await deleteMutateAsync({
|
||||
id,
|
||||
})
|
||||
id
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully deleted the ${name} group`,
|
||||
type: 'success',
|
||||
})
|
||||
type: "success"
|
||||
});
|
||||
navigate({
|
||||
to: '/organization/$organizationId/members' as const,
|
||||
to: "/organization/$organizationId/members" as const,
|
||||
params: {
|
||||
organizationId: currentOrg.id,
|
||||
organizationId: currentOrg.id
|
||||
},
|
||||
search: {
|
||||
selectedTab: TabSections.Groups,
|
||||
},
|
||||
})
|
||||
selectedTab: TabSections.Groups
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: `Failed to delete the ${name} group`,
|
||||
type: 'error',
|
||||
})
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpClose('deleteGroup')
|
||||
}
|
||||
handlePopUpClose("deleteGroup");
|
||||
};
|
||||
|
||||
if (isLoading) return <Spinner size="sm" className="ml-2 mt-2" />
|
||||
if (isLoading) return <Spinner size="sm" className="ml-2 mt-2" />;
|
||||
|
||||
return (
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
@@ -102,23 +95,21 @@ const GroupPage = withPermission(
|
||||
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
|
||||
onClick={() => {
|
||||
navigate({
|
||||
to: '/organization/$organizationId/members' as const,
|
||||
to: "/organization/$organizationId/members" as const,
|
||||
params: {
|
||||
organizationId: currentOrg.id,
|
||||
organizationId: currentOrg.id
|
||||
},
|
||||
search: {
|
||||
selectedTab: TabSections.Groups,
|
||||
},
|
||||
})
|
||||
selectedTab: TabSections.Groups
|
||||
}
|
||||
});
|
||||
}}
|
||||
className="mb-4"
|
||||
>
|
||||
Groups
|
||||
</Button>
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<p className="text-3xl font-semibold text-white">
|
||||
{data.group.name}
|
||||
</p>
|
||||
<p className="text-3xl font-semibold text-white">{data.group.name}</p>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
|
||||
@@ -128,23 +119,19 @@ const GroupPage = withPermission(
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="p-1">
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Edit}
|
||||
a={OrgPermissionSubjects.Groups}
|
||||
>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Groups}>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
className={twMerge(
|
||||
!isAllowed &&
|
||||
'pointer-events-none cursor-not-allowed opacity-50',
|
||||
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async () => {
|
||||
handlePopUpOpen('groupCreateUpdate', {
|
||||
handlePopUpOpen("groupCreateUpdate", {
|
||||
groupId,
|
||||
name: data.group.name,
|
||||
slug: data.group.slug,
|
||||
role: data.group.role,
|
||||
})
|
||||
role: data.group.role
|
||||
});
|
||||
}}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
@@ -160,14 +147,14 @@ const GroupPage = withPermission(
|
||||
<DropdownMenuItem
|
||||
className={twMerge(
|
||||
isAllowed
|
||||
? 'hover:!bg-red-500 hover:!text-white'
|
||||
: 'pointer-events-none cursor-not-allowed opacity-50',
|
||||
? "hover:!bg-red-500 hover:!text-white"
|
||||
: "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async () => {
|
||||
handlePopUpOpen('deleteGroup', {
|
||||
handlePopUpOpen("deleteGroup", {
|
||||
id: groupId,
|
||||
name: data.group.name,
|
||||
})
|
||||
name: data.group.name
|
||||
});
|
||||
}}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
@@ -180,15 +167,9 @@ const GroupPage = withPermission(
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="mr-4 w-96">
|
||||
<GroupDetailsSection
|
||||
groupId={groupId}
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
/>
|
||||
<GroupDetailsSection groupId={groupId} handlePopUpOpen={handlePopUpOpen} />
|
||||
</div>
|
||||
<GroupMembersSection
|
||||
groupId={groupId}
|
||||
groupSlug={data.group.slug}
|
||||
/>
|
||||
<GroupMembersSection groupId={groupId} groupSlug={data.group.slug} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -200,46 +181,40 @@ const GroupPage = withPermission(
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteGroup.isOpen}
|
||||
title={`Are you sure want to delete the group named ${
|
||||
(popUp?.deleteGroup?.data as { name: string })?.name || ''
|
||||
(popUp?.deleteGroup?.data as { name: string })?.name || ""
|
||||
}?`}
|
||||
onChange={(isOpen) => handlePopUpToggle('deleteGroup', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteGroup", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={() =>
|
||||
onDeleteGroupSubmit(
|
||||
popUp?.deleteGroup?.data as { name: string; id: string },
|
||||
)
|
||||
onDeleteGroupSubmit(popUp?.deleteGroup?.data as { name: string; id: string })
|
||||
}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('upgradePlan', isOpen)}
|
||||
text={
|
||||
(popUp.upgradePlan?.data as { description: string })?.description
|
||||
}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text={(popUp.upgradePlan?.data as { description: string })?.description}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
},
|
||||
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.Groups },
|
||||
)
|
||||
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.Groups }
|
||||
);
|
||||
|
||||
const GroupDetailPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{t('common.head-title', { title: t('settings.org.title') })}
|
||||
</title>
|
||||
<title>{t("common.head-title", { title: t("settings.org.title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Helmet>
|
||||
<GroupPage />
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/groups/$groupId/',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/groups/$groupId/"
|
||||
)({
|
||||
component: GroupDetailPage,
|
||||
})
|
||||
component: GroupDetailPage
|
||||
});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { faChevronLeft, faEllipsis } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { useState } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faChevronLeft, faEllipsis } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createFileRoute, useNavigate, useParams } from "@tanstack/react-router";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from '@app/components/notifications'
|
||||
import { OrgPermissionCan } from '@app/components/permissions'
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
@@ -17,168 +17,155 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
Tooltip,
|
||||
UpgradePlanModal,
|
||||
} from '@app/components/v2'
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization,
|
||||
} from '@app/context'
|
||||
import { withPermission } from '@app/hoc'
|
||||
UpgradePlanModal
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
import {
|
||||
IdentityAuthMethod,
|
||||
useDeleteIdentity,
|
||||
useGetIdentityById,
|
||||
useRevokeIdentityTokenAuthToken,
|
||||
useRevokeIdentityUniversalAuthClientSecret,
|
||||
} from '@app/hooks/api'
|
||||
import { Identity } from '@app/hooks/api/identities/types'
|
||||
import { usePopUp } from '@app/hooks/usePopUp'
|
||||
import { TabSections } from '@app/types/org'
|
||||
useRevokeIdentityUniversalAuthClientSecret
|
||||
} from "@app/hooks/api";
|
||||
import { Identity } from "@app/hooks/api/identities/types";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
import { TabSections } from "@app/types/org";
|
||||
|
||||
import { IdentityAuthMethodModal } from '../../members/-components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal'
|
||||
import { IdentityModal } from '../../members/-components/OrgIdentityTab/components/IdentitySection/IdentityModal'
|
||||
import { IdentityUniversalAuthClientSecretModal } from '../../members/-components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthClientSecretModal'
|
||||
import { IdentityAuthMethodModal } from "../../members/-components/OrgIdentityTab/components/IdentitySection/IdentityAuthMethodModal";
|
||||
import { IdentityModal } from "../../members/-components/OrgIdentityTab/components/IdentitySection/IdentityModal";
|
||||
import { IdentityUniversalAuthClientSecretModal } from "../../members/-components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthClientSecretModal";
|
||||
import {
|
||||
IdentityAuthenticationSection,
|
||||
IdentityClientSecretModal,
|
||||
IdentityDetailsSection,
|
||||
IdentityProjectsSection,
|
||||
IdentityTokenListModal,
|
||||
IdentityTokenModal,
|
||||
} from './-components'
|
||||
IdentityTokenModal
|
||||
} from "./-components";
|
||||
|
||||
export const IdentitySection = withPermission(
|
||||
() => {
|
||||
const navigate = useNavigate()
|
||||
const navigate = useNavigate();
|
||||
const params = useParams({
|
||||
from: '/_authenticate/_org_details/_org-layout/organization/$organizationId/identities/$identityId',
|
||||
})
|
||||
const identityId = params.identityId as string
|
||||
const { currentOrg } = useOrganization()
|
||||
const orgId = currentOrg?.id || ''
|
||||
const { data } = useGetIdentityById(identityId)
|
||||
const { mutateAsync: deleteIdentity } = useDeleteIdentity()
|
||||
const { mutateAsync: revokeToken } = useRevokeIdentityTokenAuthToken()
|
||||
const { mutateAsync: revokeClientSecret } =
|
||||
useRevokeIdentityUniversalAuthClientSecret()
|
||||
from: "/_authenticate/_org_details/_org-layout/organization/$organizationId/identities/$identityId/"
|
||||
});
|
||||
const identityId = params.identityId as string;
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { data } = useGetIdentityById(identityId);
|
||||
const { mutateAsync: deleteIdentity } = useDeleteIdentity();
|
||||
const { mutateAsync: revokeToken } = useRevokeIdentityTokenAuthToken();
|
||||
const { mutateAsync: revokeClientSecret } = useRevokeIdentityUniversalAuthClientSecret();
|
||||
|
||||
const [selectedAuthMethod, setSelectedAuthMethod] = useState<
|
||||
Identity['authMethods'][number] | null
|
||||
>(null)
|
||||
Identity["authMethods"][number] | null
|
||||
>(null);
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } =
|
||||
usePopUp([
|
||||
'identity',
|
||||
'deleteIdentity',
|
||||
'identityAuthMethod',
|
||||
'revokeAuthMethod',
|
||||
'token',
|
||||
'tokenList',
|
||||
'revokeToken',
|
||||
'clientSecret',
|
||||
'revokeClientSecret',
|
||||
'universalAuthClientSecret', // list of client secrets
|
||||
'upgradePlan',
|
||||
] as const)
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"identity",
|
||||
"deleteIdentity",
|
||||
"identityAuthMethod",
|
||||
"revokeAuthMethod",
|
||||
"token",
|
||||
"tokenList",
|
||||
"revokeToken",
|
||||
"clientSecret",
|
||||
"revokeClientSecret",
|
||||
"universalAuthClientSecret", // list of client secrets
|
||||
"upgradePlan"
|
||||
] as const);
|
||||
|
||||
const onDeleteIdentitySubmit = async (id: string) => {
|
||||
try {
|
||||
await deleteIdentity({
|
||||
identityId: id,
|
||||
organizationId: orgId,
|
||||
})
|
||||
organizationId: orgId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: 'Successfully deleted identity',
|
||||
type: 'success',
|
||||
})
|
||||
text: "Successfully deleted identity",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose('deleteIdentity')
|
||||
handlePopUpClose("deleteIdentity");
|
||||
navigate({
|
||||
to: `/organization/${orgId}/members` as const,
|
||||
search: {
|
||||
selectedTab: TabSections.Identities,
|
||||
to: "/organization/$organizationId/members",
|
||||
params: {
|
||||
organizationId: orgId
|
||||
},
|
||||
})
|
||||
search: {
|
||||
selectedTab: TabSections.Identities
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
const error = err as any
|
||||
const text =
|
||||
error?.response?.data?.message ?? 'Failed to delete identity'
|
||||
console.error(err);
|
||||
const error = err as any;
|
||||
const text = error?.response?.data?.message ?? "Failed to delete identity";
|
||||
|
||||
createNotification({
|
||||
text,
|
||||
type: 'error',
|
||||
})
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onRevokeTokenSubmit = async ({
|
||||
identityId: parentIdentityId,
|
||||
tokenId,
|
||||
name,
|
||||
name
|
||||
}: {
|
||||
identityId: string
|
||||
tokenId: string
|
||||
name: string
|
||||
identityId: string;
|
||||
tokenId: string;
|
||||
name: string;
|
||||
}) => {
|
||||
try {
|
||||
await revokeToken({
|
||||
identityId: parentIdentityId,
|
||||
tokenId,
|
||||
})
|
||||
tokenId
|
||||
});
|
||||
|
||||
handlePopUpClose('revokeToken')
|
||||
handlePopUpClose("revokeToken");
|
||||
|
||||
createNotification({
|
||||
text: `Successfully revoked token ${name ?? ''}`,
|
||||
type: 'success',
|
||||
})
|
||||
text: `Successfully revoked token ${name ?? ""}`,
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
const error = err as any
|
||||
const text =
|
||||
error?.response?.data?.message ?? 'Failed to delete identity'
|
||||
console.error(err);
|
||||
const error = err as any;
|
||||
const text = error?.response?.data?.message ?? "Failed to delete identity";
|
||||
|
||||
createNotification({
|
||||
text,
|
||||
type: 'error',
|
||||
})
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDeleteClientSecretSubmit = async ({
|
||||
clientSecretId,
|
||||
}: {
|
||||
clientSecretId: string
|
||||
}) => {
|
||||
const onDeleteClientSecretSubmit = async ({ clientSecretId }: { clientSecretId: string }) => {
|
||||
try {
|
||||
if (
|
||||
!data?.identity.id ||
|
||||
selectedAuthMethod !== IdentityAuthMethod.UNIVERSAL_AUTH
|
||||
)
|
||||
return
|
||||
if (!data?.identity.id || selectedAuthMethod !== IdentityAuthMethod.UNIVERSAL_AUTH) return;
|
||||
|
||||
await revokeClientSecret({
|
||||
identityId: data?.identity.id,
|
||||
clientSecretId,
|
||||
})
|
||||
clientSecretId
|
||||
});
|
||||
|
||||
handlePopUpToggle('revokeClientSecret', false)
|
||||
handlePopUpToggle("revokeClientSecret", false);
|
||||
|
||||
createNotification({
|
||||
text: 'Successfully deleted client secret',
|
||||
type: 'success',
|
||||
})
|
||||
text: "Successfully deleted client secret",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: 'Failed to delete client secret',
|
||||
type: 'error',
|
||||
})
|
||||
text: "Failed to delete client secret",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
@@ -190,20 +177,21 @@ export const IdentitySection = withPermission(
|
||||
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
|
||||
onClick={() => {
|
||||
navigate({
|
||||
to: `/organization/${orgId}/members` as const,
|
||||
search: {
|
||||
selectedTab: TabSections.Identities,
|
||||
to: "/organization/$organizationId/members",
|
||||
params: {
|
||||
organizationId: orgId
|
||||
},
|
||||
})
|
||||
search: {
|
||||
selectedTab: TabSections.Identities
|
||||
}
|
||||
});
|
||||
}}
|
||||
className="mb-4"
|
||||
>
|
||||
Identities
|
||||
</Button>
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<p className="text-3xl font-semibold text-white">
|
||||
{data.identity.name}
|
||||
</p>
|
||||
<p className="text-3xl font-semibold text-white">{data.identity.name}</p>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
|
||||
@@ -220,16 +208,15 @@ export const IdentitySection = withPermission(
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
className={twMerge(
|
||||
!isAllowed &&
|
||||
'pointer-events-none cursor-not-allowed opacity-50',
|
||||
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async () => {
|
||||
handlePopUpOpen('identity', {
|
||||
handlePopUpOpen("identity", {
|
||||
identityId,
|
||||
name: data.identity.name,
|
||||
role: data.role,
|
||||
customRole: data.customRole,
|
||||
})
|
||||
customRole: data.customRole
|
||||
});
|
||||
}}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
@@ -244,15 +231,14 @@ export const IdentitySection = withPermission(
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
className={twMerge(
|
||||
!isAllowed &&
|
||||
'pointer-events-none cursor-not-allowed opacity-50',
|
||||
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async () => {
|
||||
handlePopUpOpen('identityAuthMethod', {
|
||||
handlePopUpOpen("identityAuthMethod", {
|
||||
identityId,
|
||||
name: data.identity.name,
|
||||
allAuthMethods: data.identity.authMethods,
|
||||
})
|
||||
allAuthMethods: data.identity.authMethods
|
||||
});
|
||||
}}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
@@ -268,14 +254,14 @@ export const IdentitySection = withPermission(
|
||||
<DropdownMenuItem
|
||||
className={twMerge(
|
||||
isAllowed
|
||||
? 'hover:!bg-red-500 hover:!text-white'
|
||||
: 'pointer-events-none cursor-not-allowed opacity-50',
|
||||
? "hover:!bg-red-500 hover:!text-white"
|
||||
: "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async () => {
|
||||
handlePopUpOpen('deleteIdentity', {
|
||||
handlePopUpOpen("deleteIdentity", {
|
||||
identityId,
|
||||
name: data.identity.name,
|
||||
})
|
||||
name: data.identity.name
|
||||
});
|
||||
}}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
@@ -288,10 +274,7 @@ export const IdentitySection = withPermission(
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="mr-4 w-96">
|
||||
<IdentityDetailsSection
|
||||
identityId={identityId}
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
/>
|
||||
<IdentityDetailsSection identityId={identityId} handlePopUpOpen={handlePopUpOpen} />
|
||||
<IdentityAuthenticationSection
|
||||
selectedAuthMethod={selectedAuthMethod}
|
||||
setSelectedAuthMethod={setSelectedAuthMethod}
|
||||
@@ -309,19 +292,13 @@ export const IdentitySection = withPermission(
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<IdentityTokenModal
|
||||
popUp={popUp}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<IdentityTokenModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<IdentityTokenListModal
|
||||
popUp={popUp}
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<IdentityClientSecretModal
|
||||
popUp={popUp}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<IdentityClientSecretModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<IdentityUniversalAuthClientSecretModal
|
||||
popUp={popUp}
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
@@ -329,87 +306,82 @@ export const IdentitySection = withPermission(
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('upgradePlan', isOpen)}
|
||||
text={
|
||||
(popUp.upgradePlan?.data as { description: string })?.description
|
||||
}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text={(popUp.upgradePlan?.data as { description: string })?.description}
|
||||
/>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteIdentity.isOpen}
|
||||
title={`Are you sure want to delete ${
|
||||
(popUp?.deleteIdentity?.data as { name: string })?.name || ''
|
||||
(popUp?.deleteIdentity?.data as { name: string })?.name || ""
|
||||
}?`}
|
||||
onChange={(isOpen) => handlePopUpToggle('deleteIdentity', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteIdentity", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={() =>
|
||||
onDeleteIdentitySubmit(
|
||||
(popUp?.deleteIdentity?.data as { identityId: string })
|
||||
?.identityId,
|
||||
(popUp?.deleteIdentity?.data as { identityId: string })?.identityId
|
||||
)
|
||||
}
|
||||
/>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.revokeToken.isOpen}
|
||||
title={`Are you sure want to revoke ${
|
||||
(popUp?.revokeToken?.data as { name: string })?.name || ''
|
||||
(popUp?.revokeToken?.data as { name: string })?.name || ""
|
||||
}?`}
|
||||
onChange={(isOpen) => handlePopUpToggle('revokeToken', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("revokeToken", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={() => {
|
||||
const revokeTokenData = popUp?.revokeToken?.data as {
|
||||
identityId: string
|
||||
tokenId: string
|
||||
name: string
|
||||
}
|
||||
identityId: string;
|
||||
tokenId: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
return onRevokeTokenSubmit(revokeTokenData)
|
||||
return onRevokeTokenSubmit(revokeTokenData);
|
||||
}}
|
||||
/>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.revokeClientSecret.isOpen}
|
||||
title={`Are you sure want to delete the client secret ${
|
||||
(popUp?.revokeClientSecret?.data as { clientSecretPrefix: string })
|
||||
?.clientSecretPrefix || ''
|
||||
?.clientSecretPrefix || ""
|
||||
}************?`}
|
||||
onChange={(isOpen) => handlePopUpToggle('revokeClientSecret', isOpen)}
|
||||
onChange={(isOpen) => handlePopUpToggle("revokeClientSecret", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={() => {
|
||||
const deleteClientSecretData = popUp?.revokeClientSecret?.data as {
|
||||
clientSecretId: string
|
||||
clientSecretPrefix: string
|
||||
}
|
||||
clientSecretId: string;
|
||||
clientSecretPrefix: string;
|
||||
};
|
||||
|
||||
return onDeleteClientSecretSubmit({
|
||||
clientSecretId: deleteClientSecretData.clientSecretId,
|
||||
})
|
||||
clientSecretId: deleteClientSecretData.clientSecretId
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
},
|
||||
{
|
||||
action: OrgPermissionActions.Read,
|
||||
subject: OrgPermissionSubjects.Identity,
|
||||
},
|
||||
)
|
||||
subject: OrgPermissionSubjects.Identity
|
||||
}
|
||||
);
|
||||
|
||||
const IdentityDetailPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{t('common.head-title', { title: t('settings.org.title') })}
|
||||
</title>
|
||||
<title>{t("common.head-title", { title: t("settings.org.title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Helmet>
|
||||
<IdentitySection />
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/identities/$identityId/',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/identities/$identityId/"
|
||||
)({
|
||||
component: IdentityDetailPage,
|
||||
})
|
||||
component: IdentityDetailPage
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { ProjectType } from '@app/hooks/api/workspace/types'
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
import { ProductOverview } from '../secret-manager/overview'
|
||||
import { ProductOverview } from "../secret-manager/overview";
|
||||
|
||||
const KeyManagerOverviewPage = () => <ProductOverview type={ProjectType.KMS} />
|
||||
const KeyManagerOverviewPage = () => <ProductOverview type={ProjectType.KMS} />;
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/kms/overview',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/kms/overview"
|
||||
)({
|
||||
component: KeyManagerOverviewPage,
|
||||
})
|
||||
component: KeyManagerOverviewPage
|
||||
});
|
||||
|
||||
@@ -15,4 +15,3 @@ export const OrgGroupsTab = () => {
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
faUsers
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -37,7 +38,6 @@ import { usePagination, useResetPageHelper } from "@app/hooks";
|
||||
import { useGetOrganizationGroups, useGetOrgRoles, useUpdateGroup } from "@app/hooks/api";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
type Props = {
|
||||
handlePopUpOpen: (
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { OrgGroupsSection } from "./OrgGroupsSection";
|
||||
export { OrgGroupsSection } from "./OrgGroupsSection";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { OrgGroupsSection } from "./OrgGroupsSection";
|
||||
export { OrgGroupsSection } from "./OrgGroupsSection";
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { OrgGroupsTab } from "./OrgGroupsTab";
|
||||
export { OrgGroupsTab } from "./OrgGroupsTab";
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
faServer
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -37,7 +38,6 @@ import { useGetIdentityMembershipOrgs, useGetOrgRoles, useUpdateIdentity } from
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { OrgIdentityOrderBy } from "@app/hooks/api/organization/types";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
type Props = {
|
||||
handlePopUpOpen: (
|
||||
@@ -254,7 +254,13 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate.push(`/org/${organizationId}/identities/${id}`);
|
||||
navigate({
|
||||
to: "/organization/$organizationId/identities/$identityId",
|
||||
params: {
|
||||
organizationId,
|
||||
identityId: id
|
||||
}
|
||||
});
|
||||
}}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
|
||||
@@ -1,54 +1,40 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { createFileRoute, useNavigate, useSearch } from '@tanstack/react-router'
|
||||
import { useEffect, useState } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createFileRoute, useNavigate, useSearch } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { z } from "zod";
|
||||
|
||||
import { Tab, TabList, TabPanel, Tabs } from '@app/components/v2'
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from '@app/context'
|
||||
import { withPermission } from '@app/hoc'
|
||||
import { TabSections } from '@app/types/org'
|
||||
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
import { TabSections } from "@app/types/org";
|
||||
|
||||
import {
|
||||
OrgGroupsTab,
|
||||
OrgIdentityTab,
|
||||
OrgMembersTab,
|
||||
OrgRoleTabSection,
|
||||
} from './-components'
|
||||
import { OrgGroupsTab, OrgIdentityTab, OrgMembersTab, OrgRoleTabSection } from "./-components";
|
||||
|
||||
export const MembersSection = withPermission(
|
||||
() => {
|
||||
const navigate = useNavigate({
|
||||
from: '/_authenticate/_org_details/_org-layout/organization/$organizationId/members',
|
||||
})
|
||||
const search = useSearch({
|
||||
from: '/_authenticate/_org_details/_org-layout/organization/$organizationId/members',
|
||||
})
|
||||
const selectedTab = search.selectedTab as TabSections
|
||||
const [activeTab, setActiveTab] = useState<TabSections>(TabSections.Member)
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
selectedTab &&
|
||||
Object.values(TabSections).includes(selectedTab as TabSections)
|
||||
) {
|
||||
setActiveTab(selectedTab)
|
||||
}
|
||||
}, [selectedTab])
|
||||
from: "/organization/$organizationId/members"
|
||||
});
|
||||
const selectedTab = useSearch({
|
||||
from: "/_authenticate/_org_details/_org-layout/organization/$organizationId/members/",
|
||||
select: (el) => el.selectedTab,
|
||||
structuralSharing: true
|
||||
});
|
||||
|
||||
const updateSelectedTab = (tab: string) => {
|
||||
navigate({
|
||||
search: { ...search, selectedTab: tab },
|
||||
})
|
||||
}
|
||||
search: { selectedTab: tab }
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
<div className="mx-auto mb-6 w-full max-w-7xl px-6 py-6">
|
||||
<p className="mb-4 mr-4 text-3xl font-semibold text-white">
|
||||
Organization Access Control
|
||||
</p>
|
||||
<Tabs value={activeTab} onValueChange={updateSelectedTab}>
|
||||
<p className="mb-4 mr-4 text-3xl font-semibold text-white">Organization Access Control</p>
|
||||
<Tabs value={selectedTab} onValueChange={updateSelectedTab}>
|
||||
<TabList>
|
||||
<Tab value={TabSections.Member}>Users</Tab>
|
||||
<Tab value={TabSections.Groups}>Groups</Tab>
|
||||
@@ -74,29 +60,32 @@ export const MembersSection = withPermission(
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
},
|
||||
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.Member },
|
||||
)
|
||||
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.Member }
|
||||
);
|
||||
|
||||
const MembersPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{t('common.head-title', { title: t('settings.org.title') })}
|
||||
</title>
|
||||
<title>{t("common.head-title", { title: t("settings.org.title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Helmet>
|
||||
<MembersSection />
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const MembersPageQuerySchema = z.object({
|
||||
selectedTab: z.string().catch(TabSections.Member)
|
||||
});
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/members/',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/members/"
|
||||
)({
|
||||
component: MembersPage,
|
||||
})
|
||||
validateSearch: zodValidator(MembersPageQuerySchema)
|
||||
});
|
||||
|
||||
@@ -7,6 +7,8 @@ import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@a
|
||||
import { withPermission } from "@app/hoc";
|
||||
import { OrgUser } from "@app/hooks/api/types";
|
||||
|
||||
import { LogsSection } from "../../../../audit-logs/-components";
|
||||
|
||||
type Props = {
|
||||
orgMembership: OrgUser;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useMemo } from "react";
|
||||
import { faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { IconButton, Tag, Td, Tooltip, Tr } from "@app/components/v2";
|
||||
@@ -8,7 +9,6 @@ import { useWorkspace } from "@app/context";
|
||||
import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
||||
import { TWorkspaceUser } from "@app/hooks/api/types";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { TabSections } from "@app/types/org";
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { ProjectType } from '@app/hooks/api/workspace/types'
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router'
|
||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/overview/',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/overview/"
|
||||
)({
|
||||
beforeLoad: ({ context }) => {
|
||||
throw redirect({
|
||||
to: `/organization/$organizationId/${ProjectType.SecretManager}` as const,
|
||||
to: `/organization/$organizationId/${ProjectType.SecretManager}/overview` as const,
|
||||
params: {
|
||||
organizationId: context.organizationId,
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
organizationId: context.organizationId
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -9,7 +10,6 @@ import { useOrganization } from "@app/context";
|
||||
import { useCreateOrgRole, useGetOrgRole, useUpdateOrgRole } from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
import { slugSchema } from "@app/lib/schemas";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
@@ -104,7 +104,7 @@ export const RoleModal = ({ popUp, handlePopUpToggle }: Props) => {
|
||||
|
||||
handlePopUpToggle("role", false);
|
||||
navigate({
|
||||
to: "/organization/$organizationId/roles/roleId",
|
||||
to: "/organization/$organizationId/roles/$roleId",
|
||||
params: {
|
||||
organizationId: orgId,
|
||||
roleId: newRole.id
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { faChevronLeft, faEllipsis } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { createFileRoute, useNavigate, useParams } from '@tanstack/react-router'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faChevronLeft, faEllipsis } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createFileRoute, useNavigate, useParams } from "@tanstack/react-router";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { createNotification } from '@app/components/notifications'
|
||||
import { OrgPermissionCan } from '@app/components/permissions'
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
@@ -14,79 +14,70 @@ import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
Tooltip,
|
||||
} from '@app/components/v2'
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization,
|
||||
} from '@app/context'
|
||||
import { withPermission } from '@app/hoc'
|
||||
import { useDeleteOrgRole, useGetOrgRole } from '@app/hooks/api'
|
||||
import { usePopUp } from '@app/hooks/usePopUp'
|
||||
import { TabSections } from '@app/types/org'
|
||||
Tooltip
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
import { useDeleteOrgRole, useGetOrgRole } from "@app/hooks/api";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
import { TabSections } from "@app/types/org";
|
||||
|
||||
import {
|
||||
RoleDetailsSection,
|
||||
RoleModal,
|
||||
RolePermissionsSection,
|
||||
} from './-components'
|
||||
import { RoleDetailsSection, RoleModal, RolePermissionsSection } from "./-components";
|
||||
|
||||
export const RolePage = withPermission(
|
||||
() => {
|
||||
const navigate = useNavigate()
|
||||
const navigate = useNavigate();
|
||||
const params = useParams({
|
||||
from: '/_authenticate/_org_details/_org-layout/organization/$organizationId/roles/$roleId',
|
||||
})
|
||||
const roleId = params.roleId as string
|
||||
const { currentOrg } = useOrganization()
|
||||
const orgId = currentOrg?.id || ''
|
||||
const { data } = useGetOrgRole(orgId, roleId)
|
||||
const { mutateAsync: deleteOrgRole } = useDeleteOrgRole()
|
||||
from: "/_authenticate/_org_details/_org-layout/organization/$organizationId/roles/$roleId/"
|
||||
});
|
||||
const roleId = params.roleId as string;
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { data } = useGetOrgRole(orgId, roleId);
|
||||
const { mutateAsync: deleteOrgRole } = useDeleteOrgRole();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } =
|
||||
usePopUp(['role', 'deleteOrgRole'] as const)
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"role",
|
||||
"deleteOrgRole"
|
||||
] as const);
|
||||
|
||||
const onDeleteOrgRoleSubmit = async () => {
|
||||
try {
|
||||
if (!orgId || !roleId) return
|
||||
if (!orgId || !roleId) return;
|
||||
|
||||
await deleteOrgRole({
|
||||
orgId,
|
||||
id: roleId,
|
||||
})
|
||||
id: roleId
|
||||
});
|
||||
|
||||
createNotification({
|
||||
text: 'Successfully deleted organization role',
|
||||
type: 'success',
|
||||
})
|
||||
text: "Successfully deleted organization role",
|
||||
type: "success"
|
||||
});
|
||||
|
||||
handlePopUpClose('deleteOrgRole')
|
||||
handlePopUpClose("deleteOrgRole");
|
||||
navigate({
|
||||
to: '/organization/$organizationId/members' as const,
|
||||
to: "/organization/$organizationId/members" as const,
|
||||
params: {
|
||||
organizationId: currentOrg.id,
|
||||
organizationId: currentOrg.id
|
||||
},
|
||||
search: {
|
||||
selectedTab: TabSections.Roles,
|
||||
},
|
||||
})
|
||||
selectedTab: TabSections.Roles
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
const error = err as any
|
||||
const text =
|
||||
error?.response?.data?.message ?? 'Failed to delete organization role'
|
||||
console.error(err);
|
||||
const error = err as any;
|
||||
const text = error?.response?.data?.message ?? "Failed to delete organization role";
|
||||
|
||||
createNotification({
|
||||
text,
|
||||
type: 'error',
|
||||
})
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const isCustomRole = !['admin', 'member', 'no-access'].includes(
|
||||
data?.slug ?? '',
|
||||
)
|
||||
const isCustomRole = !["admin", "member", "no-access"].includes(data?.slug ?? "");
|
||||
|
||||
return (
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
@@ -98,14 +89,14 @@ export const RolePage = withPermission(
|
||||
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
|
||||
onClick={() => {
|
||||
navigate({
|
||||
to: '/organization/$organizationId/members' as const,
|
||||
to: "/organization/$organizationId/members" as const,
|
||||
params: {
|
||||
organizationId: currentOrg.id,
|
||||
organizationId: currentOrg.id
|
||||
},
|
||||
search: {
|
||||
selectedTab: TabSections.Roles,
|
||||
},
|
||||
})
|
||||
selectedTab: TabSections.Roles
|
||||
}
|
||||
});
|
||||
}}
|
||||
className="mb-4"
|
||||
>
|
||||
@@ -123,20 +114,16 @@ export const RolePage = withPermission(
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="p-1">
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Edit}
|
||||
a={OrgPermissionSubjects.Role}
|
||||
>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Edit} a={OrgPermissionSubjects.Role}>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
className={twMerge(
|
||||
!isAllowed &&
|
||||
'pointer-events-none cursor-not-allowed opacity-50',
|
||||
!isAllowed && "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async () => {
|
||||
handlePopUpOpen('role', {
|
||||
roleId,
|
||||
})
|
||||
handlePopUpOpen("role", {
|
||||
roleId
|
||||
});
|
||||
}}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
@@ -152,11 +139,11 @@ export const RolePage = withPermission(
|
||||
<DropdownMenuItem
|
||||
className={twMerge(
|
||||
isAllowed
|
||||
? 'hover:!bg-red-500 hover:!text-white'
|
||||
: 'pointer-events-none cursor-not-allowed opacity-50',
|
||||
? "hover:!bg-red-500 hover:!text-white"
|
||||
: "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={async () => {
|
||||
handlePopUpOpen('deleteOrgRole')
|
||||
handlePopUpOpen("deleteOrgRole");
|
||||
}}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
@@ -170,10 +157,7 @@ export const RolePage = withPermission(
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="mr-4 w-96">
|
||||
<RoleDetailsSection
|
||||
roleId={roleId}
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
/>
|
||||
<RoleDetailsSection roleId={roleId} handlePopUpOpen={handlePopUpOpen} />
|
||||
</div>
|
||||
<RolePermissionsSection roleId={roleId} />
|
||||
</div>
|
||||
@@ -182,34 +166,32 @@ export const RolePage = withPermission(
|
||||
<RoleModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteOrgRole.isOpen}
|
||||
title={`Are you sure want to delete the organization role ${data?.name ?? ''}?`}
|
||||
onChange={(isOpen) => handlePopUpToggle('deleteOrgRole', isOpen)}
|
||||
title={`Are you sure want to delete the organization role ${data?.name ?? ""}?`}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteOrgRole", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={() => onDeleteOrgRoleSubmit()}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
},
|
||||
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.Role },
|
||||
)
|
||||
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.Role }
|
||||
);
|
||||
|
||||
const OrgRoleRoute = () => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{t('common.head-title', { title: t('settings.org.title') })}
|
||||
</title>
|
||||
<title>{t("common.head-title", { title: t("settings.org.title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Helmet>
|
||||
<RolePage />
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/roles/$roleId/',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/roles/$roleId/"
|
||||
)({
|
||||
component: OrgRoleRoute,
|
||||
})
|
||||
component: OrgRoleRoute
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// REFACTOR(akhilmhdh): This file needs to be split into multiple components too complex
|
||||
|
||||
import { ReactNode, useMemo, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { faFolderOpen, faStar } from '@fortawesome/free-regular-svg-icons'
|
||||
import { ReactNode, useMemo, useState } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faFolderOpen, faStar } from "@fortawesome/free-regular-svg-icons";
|
||||
import {
|
||||
faArrowDownAZ,
|
||||
faArrowRight,
|
||||
@@ -14,14 +14,14 @@ import {
|
||||
faMagnifyingGlass,
|
||||
faPlus,
|
||||
faSearch,
|
||||
faStar as faSolidStar,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { createFileRoute, useNavigate } from '@tanstack/react-router'
|
||||
faStar as faSolidStar
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||
|
||||
import { createNotification } from '@app/components/notifications'
|
||||
import { OrgPermissionCan } from '@app/components/permissions'
|
||||
import { NewProjectModal } from '@app/components/projects'
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { NewProjectModal } from "@app/components/projects";
|
||||
import {
|
||||
Button,
|
||||
IconButton,
|
||||
@@ -29,89 +29,86 @@ import {
|
||||
Pagination,
|
||||
Skeleton,
|
||||
Tooltip,
|
||||
UpgradePlanModal,
|
||||
} from '@app/components/v2'
|
||||
UpgradePlanModal
|
||||
} from "@app/components/v2";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization,
|
||||
useSubscription,
|
||||
} from '@app/context'
|
||||
import { getProjectHomePage } from '@app/helpers/project'
|
||||
import { usePagination, useResetPageHelper } from '@app/hooks'
|
||||
import { useGetUserWorkspaces } from '@app/hooks/api'
|
||||
import { OrderByDirection } from '@app/hooks/api/generic/types'
|
||||
useSubscription
|
||||
} from "@app/context";
|
||||
import { getProjectHomePage } from "@app/helpers/project";
|
||||
import { usePagination, useResetPageHelper } from "@app/hooks";
|
||||
import { useGetUserWorkspaces } from "@app/hooks/api";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
// import { fetchUserWsKey } from "@app/hooks/api/keys/queries";
|
||||
import { useFetchServerStatus } from '@app/hooks/api/serverDetails'
|
||||
import { Workspace } from '@app/hooks/api/types'
|
||||
import { useUpdateUserProjectFavorites } from '@app/hooks/api/users/mutation'
|
||||
import { useGetUserProjectFavorites } from '@app/hooks/api/users/queries'
|
||||
import { ProjectType } from '@app/hooks/api/workspace/types'
|
||||
import { usePopUp } from '@app/hooks/usePopUp'
|
||||
import { useFetchServerStatus } from "@app/hooks/api/serverDetails";
|
||||
import { Workspace } from "@app/hooks/api/types";
|
||||
import { useUpdateUserProjectFavorites } from "@app/hooks/api/users/mutation";
|
||||
import { useGetUserProjectFavorites } from "@app/hooks/api/users/queries";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
enum ProjectsViewMode {
|
||||
GRID = 'grid',
|
||||
LIST = 'list',
|
||||
GRID = "grid",
|
||||
LIST = "list"
|
||||
}
|
||||
|
||||
enum ProjectOrderBy {
|
||||
Name = 'name',
|
||||
Name = "name"
|
||||
}
|
||||
|
||||
const formatTitle = (type: ProjectType) => {
|
||||
if (type === ProjectType.SecretManager) return 'Secret Management'
|
||||
if (type === ProjectType.CertificateManager) return 'Cert Management'
|
||||
return 'Key Management'
|
||||
}
|
||||
if (type === ProjectType.SecretManager) return "Secret Management";
|
||||
if (type === ProjectType.CertificateManager) return "Cert Management";
|
||||
return "Key Management";
|
||||
};
|
||||
|
||||
const formatDescription = (type: ProjectType) => {
|
||||
if (type === ProjectType.SecretManager)
|
||||
return 'Securely store, manage, and rotate various application secrets, such as database credentials, API keys, etc.'
|
||||
return "Securely store, manage, and rotate various application secrets, such as database credentials, API keys, etc.";
|
||||
if (type === ProjectType.CertificateManager)
|
||||
return 'Manage your PKI infrastructure and issue digital certificates for services, applications, and devices.'
|
||||
return 'Centralize the management of keys for cryptographic operations, such as encryption and decryption.'
|
||||
}
|
||||
return "Manage your PKI infrastructure and issue digital certificates for services, applications, and devices.";
|
||||
return "Centralize the management of keys for cryptographic operations, such as encryption and decryption.";
|
||||
};
|
||||
|
||||
type Props = {
|
||||
type: ProjectType
|
||||
}
|
||||
type: ProjectType;
|
||||
};
|
||||
|
||||
// #TODO: Update all the workspaceIds
|
||||
export const ProductOverview = ({ type }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
|
||||
const navigate = useNavigate()
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { data: workspaces, isLoading: isWorkspaceLoading } =
|
||||
useGetUserWorkspaces({ type })
|
||||
const { currentOrg } = useOrganization()
|
||||
const orgWorkspaces = workspaces || []
|
||||
const { data: workspaces, isLoading: isWorkspaceLoading } = useGetUserWorkspaces({ type });
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgWorkspaces = workspaces || [];
|
||||
const { data: projectFavorites, isLoading: isProjectFavoritesLoading } =
|
||||
useGetUserProjectFavorites(currentOrg?.id)
|
||||
const { mutateAsync: updateUserProjectFavorites } =
|
||||
useUpdateUserProjectFavorites()
|
||||
useGetUserProjectFavorites(currentOrg?.id);
|
||||
const { mutateAsync: updateUserProjectFavorites } = useUpdateUserProjectFavorites();
|
||||
|
||||
const isProjectViewLoading = isWorkspaceLoading || isProjectFavoritesLoading
|
||||
const isProjectViewLoading = isWorkspaceLoading || isProjectFavoritesLoading;
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
||||
'addNewWs',
|
||||
'upgradePlan',
|
||||
] as const)
|
||||
"addNewWs",
|
||||
"upgradePlan"
|
||||
] as const);
|
||||
|
||||
const [searchFilter, setSearchFilter] = useState('')
|
||||
const { data: serverDetails } = useFetchServerStatus()
|
||||
const [searchFilter, setSearchFilter] = useState("");
|
||||
const { data: serverDetails } = useFetchServerStatus();
|
||||
const [projectsViewMode, setProjectsViewMode] = useState<ProjectsViewMode>(
|
||||
(localStorage.getItem('projectsViewMode') as ProjectsViewMode) ||
|
||||
ProjectsViewMode.GRID,
|
||||
)
|
||||
(localStorage.getItem("projectsViewMode") as ProjectsViewMode) || ProjectsViewMode.GRID
|
||||
);
|
||||
|
||||
const { subscription } = useSubscription()
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
const isAddingProjectsAllowed = subscription?.workspaceLimit
|
||||
? subscription.workspacesUsed < subscription.workspaceLimit
|
||||
: true
|
||||
: true;
|
||||
|
||||
const isWorkspaceEmpty = !isProjectViewLoading && orgWorkspaces?.length === 0
|
||||
const isWorkspaceEmpty = !isProjectViewLoading && orgWorkspaces?.length === 0;
|
||||
|
||||
const {
|
||||
setPage,
|
||||
@@ -121,76 +118,72 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
offset,
|
||||
limit,
|
||||
toggleOrderDirection,
|
||||
orderDirection,
|
||||
} = usePagination(ProjectOrderBy.Name, { initPerPage: 24 })
|
||||
orderDirection
|
||||
} = usePagination(ProjectOrderBy.Name, { initPerPage: 24 });
|
||||
|
||||
const filteredWorkspaces = useMemo(
|
||||
() =>
|
||||
orgWorkspaces
|
||||
.filter((ws) =>
|
||||
ws?.name?.toLowerCase().includes(searchFilter.toLowerCase()),
|
||||
)
|
||||
.filter((ws) => ws?.name?.toLowerCase().includes(searchFilter.toLowerCase()))
|
||||
.sort((a, b) =>
|
||||
orderDirection === OrderByDirection.ASC
|
||||
? a.name.toLowerCase().localeCompare(b.name.toLowerCase())
|
||||
: b.name.toLowerCase().localeCompare(a.name.toLowerCase()),
|
||||
: b.name.toLowerCase().localeCompare(a.name.toLowerCase())
|
||||
),
|
||||
[searchFilter, orderDirection, orgWorkspaces],
|
||||
)
|
||||
[searchFilter, orderDirection, orgWorkspaces]
|
||||
);
|
||||
|
||||
useResetPageHelper({
|
||||
setPage,
|
||||
offset,
|
||||
totalCount: filteredWorkspaces.length,
|
||||
})
|
||||
totalCount: filteredWorkspaces.length
|
||||
});
|
||||
|
||||
const { workspacesWithFaveProp } = useMemo(() => {
|
||||
const workspacesWithFav = filteredWorkspaces
|
||||
.map((w): Workspace & { isFavorite: boolean } => ({
|
||||
...w,
|
||||
isFavorite: Boolean(projectFavorites?.includes(w.id)),
|
||||
isFavorite: Boolean(projectFavorites?.includes(w.id))
|
||||
}))
|
||||
.sort((a, b) => Number(b.isFavorite) - Number(a.isFavorite))
|
||||
.slice(offset, limit * page)
|
||||
.slice(offset, limit * page);
|
||||
|
||||
return {
|
||||
workspacesWithFaveProp: workspacesWithFav,
|
||||
}
|
||||
}, [filteredWorkspaces, projectFavorites])
|
||||
workspacesWithFaveProp: workspacesWithFav
|
||||
};
|
||||
}, [filteredWorkspaces, projectFavorites]);
|
||||
|
||||
const addProjectToFavorites = async (projectId: string) => {
|
||||
try {
|
||||
if (currentOrg?.id) {
|
||||
await updateUserProjectFavorites({
|
||||
orgId: currentOrg?.id,
|
||||
projectFavorites: [...(projectFavorites || []), projectId],
|
||||
})
|
||||
projectFavorites: [...(projectFavorites || []), projectId]
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
createNotification({
|
||||
text: 'Failed to add project to favorites.',
|
||||
type: 'error',
|
||||
})
|
||||
text: "Failed to add project to favorites.",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const removeProjectFromFavorites = async (projectId: string) => {
|
||||
try {
|
||||
if (currentOrg?.id) {
|
||||
await updateUserProjectFavorites({
|
||||
orgId: currentOrg?.id,
|
||||
projectFavorites: [
|
||||
...(projectFavorites || []).filter((entry) => entry !== projectId),
|
||||
],
|
||||
})
|
||||
projectFavorites: [...(projectFavorites || []).filter((entry) => entry !== projectId)]
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
createNotification({
|
||||
text: 'Failed to remove project from favorites.',
|
||||
type: 'error',
|
||||
})
|
||||
text: "Failed to remove project from favorites.",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const renderProjectGridItem = (workspace: Workspace, isFavorite: boolean) => (
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
|
||||
@@ -198,24 +191,22 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
onClick={() => {
|
||||
// TODO(rbr): fix this
|
||||
navigate({
|
||||
to: getProjectHomePage(workspace),
|
||||
})
|
||||
localStorage.setItem('projectData.id', workspace.id)
|
||||
to: getProjectHomePage(workspace)
|
||||
});
|
||||
localStorage.setItem("projectData.id", workspace.id);
|
||||
}}
|
||||
key={workspace.id}
|
||||
className="flex h-40 min-w-72 cursor-pointer flex-col rounded-md border border-mineshaft-600 bg-mineshaft-800 p-4"
|
||||
>
|
||||
<div className="flex flex-row justify-between">
|
||||
<div className="mt-0 truncate text-lg text-mineshaft-100">
|
||||
{workspace.name}
|
||||
</div>
|
||||
<div className="mt-0 truncate text-lg text-mineshaft-100">{workspace.name}</div>
|
||||
{isFavorite ? (
|
||||
<FontAwesomeIcon
|
||||
icon={faSolidStar}
|
||||
className="text-sm text-yellow-600 hover:text-mineshaft-400"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
removeProjectFromFavorites(workspace.id)
|
||||
e.stopPropagation();
|
||||
removeProjectFromFavorites(workspace.id);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@@ -223,8 +214,8 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
icon={faStar}
|
||||
className="text-sm text-mineshaft-400 hover:text-mineshaft-300"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
addProjectToFavorites(workspace.id)
|
||||
e.stopPropagation();
|
||||
addProjectToFavorites(workspace.id);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -233,10 +224,10 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
<div
|
||||
className="mb-2.5 mt-1 grow text-sm text-mineshaft-300"
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
display: '-webkit-box',
|
||||
WebkitBoxOrient: 'vertical',
|
||||
WebkitLineClamp: 2,
|
||||
overflow: "hidden",
|
||||
display: "-webkit-box",
|
||||
WebkitBoxOrient: "vertical",
|
||||
WebkitLineClamp: 2
|
||||
}}
|
||||
>
|
||||
{workspace.description}
|
||||
@@ -250,7 +241,7 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
)}
|
||||
<button type="button">
|
||||
<div className="group ml-auto w-max cursor-pointer rounded-full border border-mineshaft-600 bg-mineshaft-900 px-4 py-2 text-sm text-mineshaft-300 transition-all hover:border-primary-500/80 hover:bg-primary-800/20 hover:text-mineshaft-200">
|
||||
Explore{' '}
|
||||
Explore{" "}
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowRight}
|
||||
className="pl-1.5 pr-0.5 duration-200 hover:pl-2 hover:pr-0"
|
||||
@@ -259,31 +250,25 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
const renderProjectListItem = (
|
||||
workspace: Workspace,
|
||||
isFavorite: boolean,
|
||||
index: number,
|
||||
) => (
|
||||
const renderProjectListItem = (workspace: Workspace, isFavorite: boolean, index: number) => (
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events
|
||||
<div
|
||||
onClick={() => {
|
||||
// TODO(rbr): fix this
|
||||
navigate({
|
||||
to: getProjectHomePage(workspace),
|
||||
})
|
||||
localStorage.setItem('projectData.id', workspace.id)
|
||||
to: getProjectHomePage(workspace)
|
||||
});
|
||||
localStorage.setItem("projectData.id", workspace.id);
|
||||
}}
|
||||
key={workspace.id}
|
||||
className={`group grid h-14 min-w-72 cursor-pointer grid-cols-6 border-l border-r border-t border-mineshaft-600 bg-mineshaft-800 px-6 hover:bg-mineshaft-700 ${
|
||||
index === 0 && 'rounded-t-md'
|
||||
index === 0 && "rounded-t-md"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center sm:col-span-3 lg:col-span-4">
|
||||
<div className="truncate text-sm text-mineshaft-100">
|
||||
{workspace.name}
|
||||
</div>
|
||||
<div className="truncate text-sm text-mineshaft-100">{workspace.name}</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-end sm:col-span-3 lg:col-span-2">
|
||||
<div className="text-center text-sm text-mineshaft-300">
|
||||
@@ -294,8 +279,8 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
icon={faSolidStar}
|
||||
className="ml-6 text-sm text-yellow-600 hover:text-mineshaft-400"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
removeProjectFromFavorites(workspace.id)
|
||||
e.stopPropagation();
|
||||
removeProjectFromFavorites(workspace.id);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@@ -303,16 +288,16 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
icon={faStar}
|
||||
className="ml-6 text-sm text-mineshaft-400 hover:text-mineshaft-300"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
addProjectToFavorites(workspace.id)
|
||||
e.stopPropagation();
|
||||
addProjectToFavorites(workspace.id);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
let projectsComponents: ReactNode
|
||||
let projectsComponents: ReactNode;
|
||||
|
||||
if (filteredWorkspaces.length || isProjectViewLoading) {
|
||||
switch (projectsViewMode) {
|
||||
@@ -339,14 +324,14 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
{!isProjectViewLoading && (
|
||||
<>
|
||||
{workspacesWithFaveProp.map((workspace) =>
|
||||
renderProjectGridItem(workspace, workspace.isFavorite),
|
||||
renderProjectGridItem(workspace, workspace.isFavorite)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
break
|
||||
break;
|
||||
case ProjectsViewMode.LIST:
|
||||
default:
|
||||
projectsComponents = (
|
||||
@@ -356,19 +341,19 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
<div
|
||||
key={`workspace-cards-loading-${i + 1}`}
|
||||
className={`group flex h-12 min-w-72 cursor-pointer flex-row items-center justify-between border border-mineshaft-600 bg-mineshaft-800 px-6 hover:bg-mineshaft-700 ${
|
||||
i === 0 && 'rounded-t-md'
|
||||
} ${i === 2 && 'rounded-b-md border-b'}`}
|
||||
i === 0 && "rounded-t-md"
|
||||
} ${i === 2 && "rounded-b-md border-b"}`}
|
||||
>
|
||||
<Skeleton className="w-full bg-mineshaft-600" />
|
||||
</div>
|
||||
))}
|
||||
{!isProjectViewLoading &&
|
||||
workspacesWithFaveProp.map((workspace, ind) =>
|
||||
renderProjectListItem(workspace, workspace.isFavorite, ind),
|
||||
renderProjectListItem(workspace, workspace.isFavorite, ind)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
break
|
||||
);
|
||||
break;
|
||||
}
|
||||
} else if (orgWorkspaces.length && searchFilter) {
|
||||
projectsComponents = (
|
||||
@@ -377,19 +362,15 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
icon={faSearch}
|
||||
className="mb-4 mt-2 w-full text-center text-5xl text-mineshaft-400"
|
||||
/>
|
||||
<div className="text-center font-light">
|
||||
No projects match search...
|
||||
</div>
|
||||
<div className="text-center font-light">No projects match search...</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex max-w-7xl flex-col justify-start bg-bunker-800 md:h-screen">
|
||||
<Helmet>
|
||||
<title>
|
||||
{t('common.head-title', { title: t('settings.members.title') })}
|
||||
</title>
|
||||
<title>{t("common.head-title", { title: t("settings.members.title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Helmet>
|
||||
{!serverDetails?.redisConfigured && (
|
||||
@@ -400,8 +381,8 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
icon={faExclamationCircle}
|
||||
className="mr-4 p-4 text-2xl text-mineshaft-50"
|
||||
/>
|
||||
Attention: Updated versions of Infisical now require Redis for full
|
||||
functionality. Learn how to configure it
|
||||
Attention: Updated versions of Infisical now require Redis for full functionality. Learn
|
||||
how to configure it
|
||||
<a
|
||||
href="https://infisical.com/docs/self-hosting/configuration/redis"
|
||||
rel="noopener noreferrer"
|
||||
@@ -417,9 +398,7 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
)}
|
||||
<div className="mb-4 flex flex-col items-start justify-start px-6 py-6 pb-0">
|
||||
<div className="flex w-full justify-between">
|
||||
<p className="mr-4 text-3xl font-semibold text-white">
|
||||
{formatTitle(type)}
|
||||
</p>
|
||||
<p className="mr-4 text-3xl font-semibold text-white">{formatTitle(type)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="mr-4 mt-2 text-gray-400">{formatDescription(type)}</p>
|
||||
@@ -437,9 +416,7 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
<IconButton
|
||||
className="min-w-[2.4rem] border-none hover:bg-mineshaft-600"
|
||||
ariaLabel={`Sort ${
|
||||
orderDirection === OrderByDirection.ASC
|
||||
? 'descending'
|
||||
: 'ascending'
|
||||
orderDirection === OrderByDirection.ASC ? "descending" : "ascending"
|
||||
}`}
|
||||
variant="plain"
|
||||
size="xs"
|
||||
@@ -447,11 +424,7 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
onClick={toggleOrderDirection}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={
|
||||
orderDirection === OrderByDirection.ASC
|
||||
? faArrowDownAZ
|
||||
: faArrowUpZA
|
||||
}
|
||||
icon={orderDirection === OrderByDirection.ASC ? faArrowDownAZ : faArrowUpZA}
|
||||
/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
@@ -460,15 +433,13 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
<IconButton
|
||||
variant="outline_bg"
|
||||
onClick={() => {
|
||||
localStorage.setItem('projectsViewMode', ProjectsViewMode.GRID)
|
||||
setProjectsViewMode(ProjectsViewMode.GRID)
|
||||
localStorage.setItem("projectsViewMode", ProjectsViewMode.GRID);
|
||||
setProjectsViewMode(ProjectsViewMode.GRID);
|
||||
}}
|
||||
ariaLabel="grid"
|
||||
size="xs"
|
||||
className={`${
|
||||
projectsViewMode === ProjectsViewMode.GRID
|
||||
? 'bg-mineshaft-500'
|
||||
: 'bg-transparent'
|
||||
projectsViewMode === ProjectsViewMode.GRID ? "bg-mineshaft-500" : "bg-transparent"
|
||||
} min-w-[2.4rem] border-none hover:bg-mineshaft-600`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faBorderAll} />
|
||||
@@ -476,24 +447,19 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
<IconButton
|
||||
variant="outline_bg"
|
||||
onClick={() => {
|
||||
localStorage.setItem('projectsViewMode', ProjectsViewMode.LIST)
|
||||
setProjectsViewMode(ProjectsViewMode.LIST)
|
||||
localStorage.setItem("projectsViewMode", ProjectsViewMode.LIST);
|
||||
setProjectsViewMode(ProjectsViewMode.LIST);
|
||||
}}
|
||||
ariaLabel="list"
|
||||
size="xs"
|
||||
className={`${
|
||||
projectsViewMode === ProjectsViewMode.LIST
|
||||
? 'bg-mineshaft-500'
|
||||
: 'bg-transparent'
|
||||
projectsViewMode === ProjectsViewMode.LIST ? "bg-mineshaft-500" : "bg-transparent"
|
||||
} min-w-[2.4rem] border-none hover:bg-mineshaft-600`}
|
||||
>
|
||||
<FontAwesomeIcon icon={faList} />
|
||||
</IconButton>
|
||||
</div>
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Create}
|
||||
an={OrgPermissionSubjects.Workspace}
|
||||
>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Create} an={OrgPermissionSubjects.Workspace}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
isDisabled={!isAllowed}
|
||||
@@ -501,9 +467,9 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => {
|
||||
if (isAddingProjectsAllowed) {
|
||||
handlePopUpOpen('addNewWs')
|
||||
handlePopUpOpen("addNewWs");
|
||||
} else {
|
||||
handlePopUpOpen('upgradePlan')
|
||||
handlePopUpOpen("upgradePlan");
|
||||
}
|
||||
}}
|
||||
className="ml-2"
|
||||
@@ -518,8 +484,8 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
<Pagination
|
||||
className={
|
||||
projectsViewMode === ProjectsViewMode.GRID
|
||||
? 'col-span-full !justify-start border-transparent bg-transparent pl-2'
|
||||
: 'rounded-b-md border border-mineshaft-600'
|
||||
? "col-span-full !justify-start border-transparent bg-transparent pl-2"
|
||||
: "rounded-b-md border border-mineshaft-600"
|
||||
}
|
||||
perPage={perPage}
|
||||
perPageList={[12, 24, 48, 96]}
|
||||
@@ -536,36 +502,34 @@ export const ProductOverview = ({ type }: Props) => {
|
||||
className="mb-4 mt-2 w-full text-center text-5xl text-mineshaft-400"
|
||||
/>
|
||||
<div className="text-center font-light">
|
||||
You are not part of any projects in this organization yet. When
|
||||
you are, they will appear here.
|
||||
You are not part of any projects in this organization yet. When you are, they will
|
||||
appear here.
|
||||
</div>
|
||||
<div className="mt-0.5 text-center font-light">
|
||||
Create a new project, or ask other organization members to give
|
||||
you necessary permissions.
|
||||
Create a new project, or ask other organization members to give you necessary
|
||||
permissions.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<NewProjectModal
|
||||
isOpen={popUp.addNewWs.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('addNewWs', isOpen)}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("addNewWs", isOpen)}
|
||||
projectType={type}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle('upgradePlan', isOpen)}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text="You have exceeded the number of projects allowed on the free plan."
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const SecretManagerOverviewPage = () => (
|
||||
<ProductOverview type={ProjectType.SecretManager} />
|
||||
)
|
||||
const SecretManagerOverviewPage = () => <ProductOverview type={ProjectType.SecretManager} />;
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/secret-manager/overview',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/secret-manager/overview"
|
||||
)({
|
||||
component: SecretManagerOverviewPage,
|
||||
})
|
||||
component: SecretManagerOverviewPage
|
||||
});
|
||||
|
||||
@@ -1,73 +1,68 @@
|
||||
import { useEffect } from 'react'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { createFileRoute, useSearch } from '@tanstack/react-router'
|
||||
import { useEffect } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { createFileRoute, useSearch } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { z } from "zod";
|
||||
|
||||
import { OrgPermissionCan } from '@app/components/permissions'
|
||||
import { Button, NoticeBanner } from '@app/components/v2'
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { Button, NoticeBanner } from "@app/components/v2";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization,
|
||||
useServerConfig,
|
||||
} from '@app/context'
|
||||
import { withPermission } from '@app/hoc'
|
||||
useServerConfig
|
||||
} from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
import {
|
||||
useCreateNewInstallationSession,
|
||||
useGetSecretScanningInstallationStatus,
|
||||
useLinkGitAppInstallationWithOrg,
|
||||
} from '@app/hooks/api/secretScanning'
|
||||
useLinkGitAppInstallationWithOrg
|
||||
} from "@app/hooks/api/secretScanning";
|
||||
|
||||
import { SecretScanningLogsTable } from './-components'
|
||||
import { SecretScanningLogsTable } from "./-components";
|
||||
|
||||
const SecretScanning = withPermission(
|
||||
() => {
|
||||
const queryParams = useSearch({
|
||||
from: '/_authenticate/_org_details/_org-layout/organization/$organizationId/secret-scanning',
|
||||
})
|
||||
const { config } = useServerConfig()
|
||||
const { currentOrg } = useOrganization()
|
||||
const organizationId = currentOrg.id
|
||||
from: "/_authenticate/_org_details/_org-layout/organization/$organizationId/secret-scanning/"
|
||||
});
|
||||
const { config } = useServerConfig();
|
||||
const { currentOrg } = useOrganization();
|
||||
const organizationId = currentOrg.id;
|
||||
|
||||
const { mutateAsync: linkGitAppInstallationWithOrganization } =
|
||||
useLinkGitAppInstallationWithOrg()
|
||||
const { mutateAsync: createNewIntegrationSession } =
|
||||
useCreateNewInstallationSession()
|
||||
const {
|
||||
data: installationStatus,
|
||||
isLoading: isSecretScanningInstatllationStatusLoading,
|
||||
} = useGetSecretScanningInstallationStatus(organizationId)
|
||||
useLinkGitAppInstallationWithOrg();
|
||||
const { mutateAsync: createNewIntegrationSession } = useCreateNewInstallationSession();
|
||||
const { data: installationStatus, isLoading: isSecretScanningInstatllationStatusLoading } =
|
||||
useGetSecretScanningInstallationStatus(organizationId);
|
||||
const integrationEnabled =
|
||||
!isSecretScanningInstatllationStatusLoading &&
|
||||
installationStatus?.appInstallationCompleted
|
||||
!isSecretScanningInstatllationStatusLoading && installationStatus?.appInstallationCompleted;
|
||||
|
||||
useEffect(() => {
|
||||
const linkInstallation = async () => {
|
||||
if (
|
||||
typeof queryParams.state === 'string' &&
|
||||
typeof queryParams.installation_id === 'string'
|
||||
) {
|
||||
if (queryParams.state && queryParams.installation_id) {
|
||||
try {
|
||||
const isLinked = await linkGitAppInstallationWithOrganization({
|
||||
installationId: queryParams.installation_id as string,
|
||||
sessionId: queryParams.state as string,
|
||||
})
|
||||
sessionId: queryParams.state as string
|
||||
});
|
||||
if (isLinked) {
|
||||
window.location.reload()
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
console.log('installation verification complete')
|
||||
console.log("installation verification complete");
|
||||
} catch (e) {
|
||||
console.log('app installation is stale, start new session', e)
|
||||
console.log("app installation is stale, start new session", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
linkInstallation()
|
||||
}, [queryParams.state, queryParams.installation_id])
|
||||
};
|
||||
linkInstallation();
|
||||
}, [queryParams.state, queryParams.installation_id]);
|
||||
|
||||
const generateNewIntegrationSession = async () => {
|
||||
const session = await createNewIntegrationSession({ organizationId })
|
||||
window.location.href = `https://github.com/apps/infisical-radar/installations/new?state=${session.sessionId}`
|
||||
}
|
||||
const session = await createNewIntegrationSession({ organizationId });
|
||||
window.location.href = `https://github.com/apps/infisical-radar/installations/new?state=${session.sessionId}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -78,26 +73,20 @@ const SecretScanning = withPermission(
|
||||
</Helmet>
|
||||
<div className="flex h-full w-full justify-center bg-bunker-800 text-white">
|
||||
<div className="w-full max-w-7xl px-6">
|
||||
<div className="mt-6 text-3xl font-semibold text-gray-200">
|
||||
Secret Scanning
|
||||
</div>
|
||||
<div className="mt-6 text-3xl font-semibold text-gray-200">Secret Scanning</div>
|
||||
<div className="mb-6 text-lg text-mineshaft-300">
|
||||
Automatically monitor your GitHub activity and prevent secret
|
||||
leaks
|
||||
Automatically monitor your GitHub activity and prevent secret leaks
|
||||
</div>
|
||||
{config.isSecretScanningDisabled && (
|
||||
<NoticeBanner
|
||||
title="Secret scanning is in maintenance"
|
||||
className="mb-4"
|
||||
>
|
||||
We are working on improving the performance of secret scanning
|
||||
due to increased usage.
|
||||
<NoticeBanner title="Secret scanning is in maintenance" className="mb-4">
|
||||
We are working on improving the performance of secret scanning due to increased
|
||||
usage.
|
||||
</NoticeBanner>
|
||||
)}
|
||||
<div className="relative mb-6 flex justify-between rounded-md border border-mineshaft-600 bg-mineshaft-800 p-6">
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="mb-1 flex flex-row">
|
||||
Secret Scanning Status:{' '}
|
||||
Secret Scanning Status:{" "}
|
||||
{integrationEnabled ? (
|
||||
<p className="ml-1.5 font-semibold text-green">Enabled</p>
|
||||
) : (
|
||||
@@ -107,8 +96,8 @@ const SecretScanning = withPermission(
|
||||
<div>
|
||||
{integrationEnabled ? (
|
||||
<p className="text-mineshaft-300">
|
||||
Your GitHub organization is connected to Infisical, and is
|
||||
being continuously monitored for secret leaks.
|
||||
Your GitHub organization is connected to Infisical, and is being continuously
|
||||
monitored for secret leaks.
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-mineshaft-300">
|
||||
@@ -148,16 +137,22 @@ const SecretScanning = withPermission(
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
},
|
||||
{
|
||||
action: OrgPermissionActions.Read,
|
||||
subject: OrgPermissionSubjects.SecretScanning,
|
||||
},
|
||||
)
|
||||
subject: OrgPermissionSubjects.SecretScanning
|
||||
}
|
||||
);
|
||||
|
||||
const SecretScanningQueryParams = z.object({
|
||||
state: z.string().catch(""),
|
||||
installation_id: z.string().catch("")
|
||||
});
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/secret-scanning/',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/secret-scanning/"
|
||||
)({
|
||||
component: SecretScanning,
|
||||
})
|
||||
validateSearch: zodValidator(SecretScanningQueryParams)
|
||||
});
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { ImportTab } from "./ImportTab";
|
||||
export { ImportTab } from "./ImportTab";
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Controller, useForm } from "react-hook-form";
|
||||
import { faUsers, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -35,7 +36,6 @@ import {
|
||||
useGetOrganizationGroups
|
||||
} from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
const schema = z.object({
|
||||
ldapGroupCN: z.string().min(1, "LDAP Group CN is required"),
|
||||
|
||||
@@ -24,7 +24,7 @@ const tabs = [
|
||||
];
|
||||
export const OrgTabGroup = () => {
|
||||
const search = useSearch({
|
||||
from: "/_authenticate/_org_details/_org-layout/organization/$organizationId/settings"
|
||||
from: "/_authenticate/_org_details/_org-layout/organization/$organizationId/settings/"
|
||||
});
|
||||
const [selectedTab, setSelectedTab] = useState(search.selectedTab || tabs[0].key);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { faSlack } from "@fortawesome/free-brands-svg-icons";
|
||||
import { faEllipsis, faGear, faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import axios from "axios";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
@@ -45,7 +44,6 @@ export const OrgWorkflowIntegrationTab = withPermission(
|
||||
] as const);
|
||||
|
||||
const { currentOrg } = useOrganization();
|
||||
const navigate = useNavigate();
|
||||
const { data: workflowIntegrations, isLoading: isWorkflowIntegrationsLoading } =
|
||||
useGetWorkflowIntegrations(currentOrg?.id);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import axios from "axios";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -14,7 +15,6 @@ import {
|
||||
useUpdateSlackIntegration
|
||||
} from "@app/hooks/api";
|
||||
import { slugSchema } from "@app/lib/schemas";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
|
||||
type Props = {
|
||||
id?: string;
|
||||
|
||||
@@ -1,33 +1,29 @@
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { z } from "zod";
|
||||
|
||||
import { OrgTabGroup } from './-components'
|
||||
import { OrgTabGroup } from "./-components";
|
||||
|
||||
const SettingsOrg = () => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>
|
||||
{t('common.head-title', { title: t('settings.org.title') })}
|
||||
</title>
|
||||
<title>{t("common.head-title", { title: t("settings.org.title") })}</title>
|
||||
<link rel="icon" href="/infisical.ico" />
|
||||
</Helmet>
|
||||
<div className="flex w-full justify-center bg-bunker-800 py-6 text-white">
|
||||
<div className="w-full max-w-4xl px-6">
|
||||
<div className="mb-4">
|
||||
<p className="text-3xl font-semibold text-gray-200">
|
||||
{t('settings.org.title')}
|
||||
</p>
|
||||
<p className="text-3xl font-semibold text-gray-200">{t("settings.org.title")}</p>
|
||||
</div>
|
||||
<div className="flex w-full justify-center bg-bunker-800 py-6 text-white">
|
||||
<div className="w-full max-w-4xl px-6">
|
||||
<div className="mb-4">
|
||||
<p className="text-3xl font-semibold text-gray-200">
|
||||
{t('settings.org.title')}
|
||||
</p>
|
||||
<p className="text-3xl font-semibold text-gray-200">{t("settings.org.title")}</p>
|
||||
</div>
|
||||
<OrgTabGroup />
|
||||
</div>
|
||||
@@ -35,11 +31,16 @@ const SettingsOrg = () => {
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const SettingsOrgQueryParams = z.object({
|
||||
selectedTab: z.string().catch("")
|
||||
});
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_authenticate/_org_details/_org-layout/organization/$organizationId/settings/',
|
||||
"/_authenticate/_org_details/_org-layout/organization/$organizationId/settings/"
|
||||
)({
|
||||
component: SettingsOrg,
|
||||
})
|
||||
validateSearch: zodValidator(SettingsOrgQueryParams)
|
||||
});
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
export const Route = createFileRoute("/_authenticate/_org_details/_org-layout/organization/")({
|
||||
beforeLoad: ({ context }) => {
|
||||
redirect({
|
||||
throw: true,
|
||||
to: "/organization/$organizationId/secret-manager",
|
||||
to: `/organization/$organizationId/${ProjectType.SecretManager}/overview` as const,
|
||||
params: {
|
||||
organizationId: String(context.organizationId)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useEffect } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
import { CreateOrgModal } from "@app/components/organization/CreateOrgModal";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
const NoneOrganization = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createFileRoute, redirect } from "@tanstack/react-router";
|
||||
|
||||
import { authKeys, fetchAuthToken } from "@app/hooks/api/auth/queries";
|
||||
import { setAuthToken } from "@app/hooks/api/reactQuery";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup")({
|
||||
beforeLoad: async ({ context }) => {
|
||||
@@ -20,7 +21,7 @@ export const Route = createFileRoute("/_restrict_login_signup")({
|
||||
throw redirect({ to: "/login/select-organization" });
|
||||
}
|
||||
throw redirect({
|
||||
to: "/organization/$organizationId/secret-manager",
|
||||
to: `/organization/$organizationId/${ProjectType.SecretManager}/overview` as const,
|
||||
params: {
|
||||
organizationId: data.organizationId
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const navigateUserToOrg = async (navigate: NavigateFn, organizationId?: s
|
||||
} else {
|
||||
// user is not part of any non-auth enforced orgs
|
||||
localStorage.removeItem("orgData.id");
|
||||
navigate({ to: "/org/none" });
|
||||
navigate({ to: "/organization/none" });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@ import { useEffect } from "react";
|
||||
import { createFileRoute, useSearch } from "@tanstack/react-router";
|
||||
|
||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
||||
import { z } from "zod";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
|
||||
const LoginProviderSuccess = () => {
|
||||
const search = useSearch({ from: "/login/provider/success" });
|
||||
const search = useSearch({ from: "/_restrict_login_signup/login/provider/success" });
|
||||
|
||||
useEffect(() => {
|
||||
SecurityClient.setProviderAuthToken(search.token);
|
||||
@@ -14,6 +16,11 @@ const LoginProviderSuccess = () => {
|
||||
return <div />;
|
||||
};
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup/login/provider/success")({
|
||||
component: LoginProviderSuccess
|
||||
const LoginProviderSuccessQuerySchema = z.object({
|
||||
token: z.string()
|
||||
});
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup/login/provider/success")({
|
||||
component: LoginProviderSuccess,
|
||||
validateSearch: zodValidator(LoginProviderSuccessQuerySchema)
|
||||
});
|
||||
|
||||
@@ -180,7 +180,7 @@ const SelectOrganizationPage = () => {
|
||||
// Case: User has no organizations.
|
||||
// This can happen if the user was previously a member, but the organization was deleted or the user was removed.
|
||||
if (organizations.data.length === 0) {
|
||||
navigate({ to: "/org/none" });
|
||||
navigate({ to: "/organization/none" });
|
||||
} else if (organizations.data.length === 1) {
|
||||
if (callbackPort) {
|
||||
handleCliRedirect();
|
||||
|
||||
@@ -2,13 +2,15 @@ import { useEffect, useState } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createFileRoute, Link, useSearch } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { z } from "zod";
|
||||
|
||||
import { PasswordStep } from "../-components";
|
||||
|
||||
const LoginSSOPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const search = useSearch({ from: "/login/sso" });
|
||||
const search = useSearch({ from: "/_restrict_login_signup/login/sso/" });
|
||||
const token = search.token as string;
|
||||
const [step, setStep] = useState(0);
|
||||
const [password, setPassword] = useState("");
|
||||
@@ -58,6 +60,11 @@ const LoginSSOPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup/login/sso/")({
|
||||
component: LoginSSOPage
|
||||
const LoginSSOQueryParamsSchema = z.object({
|
||||
token: z.string()
|
||||
});
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup/login/sso/")({
|
||||
component: LoginSSOPage,
|
||||
validateSearch: zodValidator(LoginSSOQueryParamsSchema)
|
||||
});
|
||||
|
||||
@@ -4,7 +4,9 @@ import { FormEvent, useState } from "react";
|
||||
import { faCheck, faX } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createFileRoute, useNavigate, useSearch } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import jsrp from "jsrp";
|
||||
import { z } from "zod";
|
||||
|
||||
import InputField from "@app/components/basic/InputField";
|
||||
import passwordCheck from "@app/components/utilities/checks/password/PasswordCheck";
|
||||
@@ -389,6 +391,12 @@ const PasswordResetPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup/password-reset")({
|
||||
component: PasswordResetPage
|
||||
const PasswordResetPageQueryParamsSchema = z.object({
|
||||
token: z.string(),
|
||||
to: z.string()
|
||||
});
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup/password-reset")({
|
||||
component: PasswordResetPage,
|
||||
validateSearch: zodValidator(PasswordResetPageQueryParamsSchema)
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import jsrp from "jsrp";
|
||||
import nacl from "tweetnacl";
|
||||
import { encodeBase64 } from "tweetnacl-util";
|
||||
|
||||
import { Mfa } from "@app/components/auth/Mfa";
|
||||
import Aes256Gcm from "@app/components/utilities/cryptography/aes-256-gcm";
|
||||
import { deriveArgonKey } from "@app/components/utilities/cryptography/crypto";
|
||||
import { saveTokenToLocalStorage } from "@app/components/utilities/saveTokenToLocalStorage";
|
||||
@@ -16,7 +17,6 @@ import { completeAccountSignup, useSelectOrganization } from "@app/hooks/api/aut
|
||||
import { MfaMethod } from "@app/hooks/api/auth/types";
|
||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
||||
import ProjectService from "@app/services/ProjectService";
|
||||
import { Mfa } from "@app/components/auth/Mfa";
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
const client = new jsrp.client();
|
||||
|
||||
@@ -2,7 +2,9 @@ import { useEffect, useState } from "react";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createFileRoute, useSearch } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BackupPDFStep } from "./-components/BackupPDFStep";
|
||||
import { EmailConfirmationStep } from "./-components/EmailConfirmationStep";
|
||||
@@ -10,7 +12,7 @@ import { UserInfoSSOStep } from "./-components/UserInfoSSOStep";
|
||||
|
||||
const SignupSSOPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const search = useSearch({ from: "/signup/sso/" });
|
||||
const search = useSearch({ from: "/_restrict_login_signup/signup/sso/" });
|
||||
const token = search.token as string;
|
||||
|
||||
const [step, setStep] = useState(0);
|
||||
@@ -85,6 +87,11 @@ const SignupSSOPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup/signup/sso/")({
|
||||
component: SignupSSOPage
|
||||
const SignupSSOPageQueryParamsSchema = z.object({
|
||||
token: z.string()
|
||||
});
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup/signup/sso/")({
|
||||
component: SignupSSOPage,
|
||||
validateSearch: zodValidator(SignupSSOPageQueryParamsSchema)
|
||||
});
|
||||
|
||||
@@ -7,9 +7,11 @@ import { Helmet } from "react-helmet";
|
||||
import { faWarning, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createFileRoute, Link, useNavigate, useSearch } from "@tanstack/react-router";
|
||||
import { zodValidator } from "@tanstack/zod-adapter";
|
||||
import jsrp from "jsrp";
|
||||
import nacl from "tweetnacl";
|
||||
import { encodeBase64 } from "tweetnacl-util";
|
||||
import { z } from "zod";
|
||||
|
||||
import { Mfa } from "@app/components/auth/Mfa";
|
||||
import InputField from "@app/components/basic/InputField";
|
||||
@@ -386,7 +388,7 @@ const SignupInvitePage = () => {
|
||||
setBackupKeyIssued
|
||||
});
|
||||
navigate({
|
||||
to: `/organization/$organizationId/${ProjectType.SecretManager}` as const,
|
||||
to: `/organization/$organizationId/${ProjectType.SecretManager}/overview` as const,
|
||||
params: {
|
||||
organizationId
|
||||
}
|
||||
@@ -427,6 +429,13 @@ const SignupInvitePage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup/signupinvite")({
|
||||
component: SignupInvitePage
|
||||
const SignupInvitePageQueryParamsSchema = z.object({
|
||||
token: z.string(),
|
||||
to: z.string(),
|
||||
organization_id: z.string()
|
||||
});
|
||||
|
||||
export const Route = createFileRoute("/_restrict_login_signup/signupinvite")({
|
||||
component: SignupInvitePage,
|
||||
validateSearch: zodValidator(SignupInvitePageQueryParamsSchema)
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user