Merge pull request #3872 from Infisical/feat/team-autonomy-product-migration

feat: project ui v3
This commit is contained in:
Maidul Islam
2025-07-01 21:09:43 -04:00
committed by GitHub
540 changed files with 8386 additions and 10521 deletions
+1 -1
View File
@@ -51,7 +51,7 @@ export type IdentityMembershipOrg = {
export type IdentityMembership = {
id: string;
identity: Identity;
project: Pick<Workspace, "id" | "name" | "type">;
project: Pick<Workspace, "id" | "name" | "defaultProduct">;
roles: Array<
{
id: string;
@@ -6,17 +6,14 @@ import {
TProjectTemplate,
TProjectTemplateResponse
} from "@app/hooks/api/projectTemplates/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
export const projectTemplateKeys = {
all: ["project-template"] as const,
list: (projectType?: ProjectType) =>
[...projectTemplateKeys.all, "list", ...(projectType ? [projectType] : [])] as const,
list: () => [...projectTemplateKeys.all, "list"] as const,
byId: (templateId: string) => [...projectTemplateKeys.all, templateId] as const
};
export const useListProjectTemplates = (
type?: ProjectType,
options?: Omit<
UseQueryOptions<
TProjectTemplate[],
@@ -28,11 +25,9 @@ export const useListProjectTemplates = (
>
) => {
return useQuery({
queryKey: projectTemplateKeys.list(type),
queryKey: projectTemplateKeys.list(),
queryFn: async () => {
const { data } = await apiRequest.get<TListProjectTemplates>("/api/v1/project-templates", {
params: { type }
});
const { data } = await apiRequest.get<TListProjectTemplates>("/api/v1/project-templates");
return data.projectTemplates;
},
@@ -1,10 +1,8 @@
import { TProjectRole } from "@app/hooks/api/roles/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
export type TProjectTemplate = {
id: string;
name: string;
type: ProjectType;
description?: string;
roles: Pick<TProjectRole, "slug" | "name" | "permissions">[];
environments?: { name: string; slug: string; position: number }[] | null;
@@ -16,7 +14,6 @@ export type TListProjectTemplates = { projectTemplates: TProjectTemplate[] };
export type TProjectTemplateResponse = { projectTemplate: TProjectTemplate };
export type TCreateProjectTemplateDTO = {
type: ProjectType;
name: string;
description?: string;
};
+15 -16
View File
@@ -154,16 +154,14 @@ export const useGetWorkspaceById = (
export const useGetUserWorkspaces = ({
includeRoles,
type = "all",
options = {}
}: {
includeRoles?: boolean;
type?: ProjectType | "all";
options?: { enabled?: boolean };
} = {}) =>
useQuery({
queryKey: workspaceKeys.getAllUserWorkspace(type),
queryFn: () => fetchUserWorkspaces(includeRoles, type),
queryKey: workspaceKeys.getAllUserWorkspace(),
queryFn: () => fetchUserWorkspaces(includeRoles),
...options
});
@@ -257,17 +255,16 @@ export const useCreateWorkspace = () => {
const queryClient = useQueryClient();
return useMutation<{ data: { project: Workspace } }, object, CreateWorkspaceDTO>({
mutationFn: async ({ projectName, projectDescription, kmsKeyId, template, type }) =>
mutationFn: async ({ projectName, projectDescription, kmsKeyId, template }) =>
createWorkspace({
projectName,
projectDescription,
kmsKeyId,
template,
type
template
}),
onSuccess: (dto) => {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: workspaceKeys.getAllUserWorkspace(dto.data.project.type)
queryKey: workspaceKeys.getAllUserWorkspace()
});
}
});
@@ -283,7 +280,8 @@ export const useUpdateProject = () => {
newProjectDescription,
newSlug,
secretSharing,
showSnapshotsLegacy
showSnapshotsLegacy,
defaultProduct
}) => {
const { data } = await apiRequest.patch<{ workspace: Workspace }>(
`/api/v1/workspace/${projectID}`,
@@ -291,6 +289,7 @@ export const useUpdateProject = () => {
name: newProjectName,
description: newProjectDescription,
slug: newSlug,
defaultProduct,
secretSharing,
showSnapshotsLegacy
}
@@ -351,8 +350,8 @@ export const useUpdateWorkspaceVersionLimit = () => {
});
return data.workspace;
},
onSuccess: (dto) => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.getAllUserWorkspace(dto.type) });
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.getAllUserWorkspace() });
}
});
};
@@ -370,8 +369,8 @@ export const useUpdateWorkspaceAuditLogsRetention = () => {
);
return data.workspace;
},
onSuccess: (dto) => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.getAllUserWorkspace(dto.type) });
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.getAllUserWorkspace() });
}
});
};
@@ -384,8 +383,8 @@ export const useDeleteWorkspace = () => {
const { data } = await apiRequest.delete(`/api/v1/workspace/${workspaceID}`);
return data.workspace;
},
onSuccess: (dto) => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.getAllUserWorkspace(dto.type) });
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: workspaceKeys.getAllUserWorkspace() });
queryClient.invalidateQueries({
queryKey: ["org-admin-projects"]
});
@@ -12,8 +12,7 @@ export const workspaceKeys = {
getWorkspaceMemberships: (orgId: string) => [{ orgId }, "workspace-memberships"],
getWorkspaceAuthorization: (workspaceId: string) => [{ workspaceId }, "workspace-authorizations"],
getWorkspaceIntegrations: (workspaceId: string) => [{ workspaceId }, "workspace-integrations"],
getAllUserWorkspace: (type?: string) =>
type ? ["workspaces", { type }] : (["workspaces"] as const),
getAllUserWorkspace: () => ["workspaces"] as const,
getWorkspaceAuditLogs: (workspaceId: string) =>
[{ workspaceId }, "workspace-audit-logs"] as const,
getWorkspaceUsers: (
+2 -3
View File
@@ -24,7 +24,7 @@ export type Workspace = {
__v: number;
id: string;
name: string;
type: ProjectType;
defaultProduct: ProjectType;
description?: string;
orgId: string;
version: ProjectVersion;
@@ -71,7 +71,6 @@ export type CreateWorkspaceDTO = {
projectDescription?: string;
kmsKeyId?: string;
template?: string;
type: ProjectType;
};
export type UpdateProjectDTO = {
@@ -81,6 +80,7 @@ export type UpdateProjectDTO = {
newSlug?: string;
secretSharing?: boolean;
showSnapshotsLegacy?: boolean;
defaultProduct?: ProjectType;
};
export type UpdatePitVersionLimitDTO = { projectSlug: string; pitVersionLimit: number };
@@ -181,7 +181,6 @@ export enum ProjectIdentityOrderBy {
Name = "name"
}
export type TSearchProjectsDTO = {
type?: ProjectType;
name?: string;
limit?: number;
offset?: number;
+1
View File
@@ -1,5 +1,6 @@
export { useDebounce } from "./useDebounce";
export * from "./useGetProjectTypeFromRoute";
export { useLocalStorageState } from "./useLocalStorageState";
export { usePagination } from "./usePagination";
export { usePersistentState } from "./usePersistentState";
export { usePopUp } from "./usePopUp";
@@ -8,15 +8,10 @@ export const useGetProjectTypeFromRoute = () => {
return useMemo(() => {
const segments = location.pathname.split("/");
const type = segments?.[2];
if (!type) return ProjectType.SecretManager;
let type: ProjectType | undefined;
// location of project type can vary in router path, so we need to check all possible values
segments.forEach((segment) => {
if (Object.values(ProjectType).includes(segment as ProjectType))
type = segment as ProjectType;
});
return type;
// second element would be /projects/$projectId/<type>
return Object.values(ProjectType).find((el) => el === type) || ProjectType.SecretManager;
}, [location]);
};
@@ -0,0 +1,70 @@
import { useCallback, useEffect, useSyncExternalStore } from "react";
type SetStateAction<T> = T | ((prevState: T) => T);
const dispatchStorageEvent = (key: string, newValue: string | null): void => {
window.dispatchEvent(new StorageEvent("storage", { key, newValue }));
};
const setLocalStorageItem = (key: string, value: unknown): void => {
const stringifiedValue = JSON.stringify(value);
window.localStorage.setItem(key, stringifiedValue);
dispatchStorageEvent(key, stringifiedValue);
};
const removeLocalStorageItem = (key: string): void => {
window.localStorage.removeItem(key);
dispatchStorageEvent(key, null);
};
const getLocalStorageItem = (key: string): string | null => {
return window.localStorage.getItem(key);
};
const useLocalStorageSubscribe = (callback: (e: StorageEvent) => void) => {
window.addEventListener("storage", callback);
return () => window.removeEventListener("storage", callback);
};
const getLocalStorageServerSnapshot = (): never => {
throw Error("useLocalStorage is a client-only hook");
};
export const useLocalStorageState = <T>(
key: string,
initialValue: T
): [T, (value: SetStateAction<T>) => void] => {
const getSnapshot = () => getLocalStorageItem(key);
const store = useSyncExternalStore(
useLocalStorageSubscribe,
getSnapshot,
getLocalStorageServerSnapshot
);
const setState = useCallback(
(v: SetStateAction<T>): void => {
try {
const nextState =
typeof v === "function" ? (v as (prevState: T) => T)(JSON.parse(store || "null")) : v;
if (nextState === undefined || nextState === null) {
removeLocalStorageItem(key);
} else {
setLocalStorageItem(key, nextState);
}
} catch (e) {
console.warn(e);
}
},
[key, store]
);
useEffect(() => {
if (getLocalStorageItem(key) === null && typeof initialValue !== "undefined") {
setLocalStorageItem(key, initialValue);
}
}, [key, initialValue]);
return [store ? JSON.parse(store) : initialValue, setState];
};