mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: added default product switch in project settings
This commit is contained in:
@@ -5,12 +5,14 @@ import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { Button, FormControl, Input, TextArea } from "@app/components/v2";
|
||||
import { Button, FormControl, Input, Select, SelectItem, TextArea } from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
|
||||
import { useUpdateProject } from "@app/hooks/api";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
const baseFormSchema = z.object({
|
||||
name: z.string().min(1, "Required").max(64, "Too long, maximum length is 64 characters"),
|
||||
defaultProduct: z.nativeEnum(ProjectType).default(ProjectType.SecretManager),
|
||||
description: z
|
||||
.string()
|
||||
.trim()
|
||||
@@ -50,6 +52,7 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) =
|
||||
reset({
|
||||
name: currentWorkspace.name,
|
||||
description: currentWorkspace.description ?? "",
|
||||
defaultProduct: currentWorkspace.defaultProduct,
|
||||
...(showSlugField && { slug: currentWorkspace.slug })
|
||||
});
|
||||
}
|
||||
@@ -63,6 +66,7 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) =
|
||||
projectID: currentWorkspace.id,
|
||||
newProjectName: data.name,
|
||||
newProjectDescription: data.description,
|
||||
defaultProduct: data.defaultProduct,
|
||||
...(showSlugField &&
|
||||
"slug" in data && {
|
||||
newSlug: data.slug !== currentWorkspace.slug ? data.slug : undefined
|
||||
@@ -212,6 +216,31 @@ export const ProjectOverviewChangeSection = ({ showSlugField = false }: Props) =
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
</div>
|
||||
<Controller
|
||||
control={control}
|
||||
name="defaultProduct"
|
||||
defaultValue={ProjectType.SecretManager}
|
||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Default Product"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Select
|
||||
defaultValue={field.value}
|
||||
{...field}
|
||||
onValueChange={(e) => onChange(e)}
|
||||
className="w-full max-w-md bg-mineshaft-800"
|
||||
>
|
||||
<SelectItem value={ProjectType.SecretManager}>Secrets Manager</SelectItem>
|
||||
<SelectItem value={ProjectType.CertificateManager}>PKI Manager</SelectItem>
|
||||
<SelectItem value={ProjectType.KMS}>KMS</SelectItem>
|
||||
<SelectItem value={ProjectType.SSH}>SSH</SelectItem>
|
||||
<SelectItem value={ProjectType.SecretScanning}>Secret Scanning</SelectItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
|
||||
@@ -120,7 +120,10 @@ const NewProjectForm = ({ onOpenChange }: NewProjectFormProps) => {
|
||||
createNotification({ text: "Project created", type: "success" });
|
||||
reset();
|
||||
onOpenChange(false);
|
||||
navigate({ to: getProjectHomePage(project.defaultType), params: { projectId: project.id } });
|
||||
navigate({
|
||||
to: getProjectHomePage(project.defaultProduct),
|
||||
params: { projectId: project.id }
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({ text: "Failed to create project", type: "error" });
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from "react";
|
||||
import { faEllipsis, faSlash, faSort } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faEllipsis, faSort } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link, ReactNode } from "@tanstack/react-router";
|
||||
import { LinkComponentProps } from "node_modules/@tanstack/react-router/dist/esm/link";
|
||||
@@ -40,7 +40,7 @@ const BreadcrumbItem = React.forwardRef<HTMLLIElement, React.ComponentPropsWitho
|
||||
({ className, ...props }, ref) => (
|
||||
<li
|
||||
ref={ref}
|
||||
className={twMerge("inline-flex text-sm items-center gap-1.5", className)}
|
||||
className={twMerge("inline-flex items-center gap-1.5 text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
@@ -79,9 +79,7 @@ BreadcrumbPage.displayName = "BreadcrumbPage";
|
||||
|
||||
const BreadcrumbSeparator = ({ children, className, ...props }: React.ComponentProps<"li">) => (
|
||||
<li role="presentation" aria-hidden="true" className={twMerge("", className)} {...props}>
|
||||
{children ?? (
|
||||
<p className="text-lg text-mineshaft-400/70 px-2">/</p>
|
||||
)}
|
||||
{children ?? <p className="px-2 text-lg text-mineshaft-400/70">/</p>}
|
||||
</li>
|
||||
);
|
||||
BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
|
||||
@@ -136,7 +134,7 @@ const BreadcrumbContainer = ({ breadcrumbs }: { breadcrumbs: TBreadcrumbFormat[]
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbSegment className="px-2 py-1 rounded-md py-2 hover:bg-mineshaft-600">
|
||||
<BreadcrumbSegment className="rounded-md px-2 py-1 py-2 hover:bg-mineshaft-600">
|
||||
{el.label} <FontAwesomeIcon icon={faSort} size="sm" />
|
||||
</BreadcrumbSegment>
|
||||
</BreadcrumbItem>
|
||||
|
||||
@@ -51,7 +51,7 @@ export type IdentityMembershipOrg = {
|
||||
export type IdentityMembership = {
|
||||
id: string;
|
||||
identity: Identity;
|
||||
project: Pick<Workspace, "id" | "name" | "defaultType">;
|
||||
project: Pick<Workspace, "id" | "name" | "defaultProduct">;
|
||||
roles: Array<
|
||||
{
|
||||
id: string;
|
||||
|
||||
@@ -280,7 +280,8 @@ export const useUpdateProject = () => {
|
||||
newProjectDescription,
|
||||
newSlug,
|
||||
secretSharing,
|
||||
showSnapshotsLegacy
|
||||
showSnapshotsLegacy,
|
||||
defaultProduct
|
||||
}) => {
|
||||
const { data } = await apiRequest.patch<{ workspace: Workspace }>(
|
||||
`/api/v1/workspace/${projectID}`,
|
||||
@@ -288,6 +289,7 @@ export const useUpdateProject = () => {
|
||||
name: newProjectName,
|
||||
description: newProjectDescription,
|
||||
slug: newSlug,
|
||||
defaultProduct,
|
||||
secretSharing,
|
||||
showSnapshotsLegacy
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export type Workspace = {
|
||||
__v: number;
|
||||
id: string;
|
||||
name: string;
|
||||
defaultType: ProjectType;
|
||||
defaultProduct: ProjectType;
|
||||
description?: string;
|
||||
orgId: string;
|
||||
version: ProjectVersion;
|
||||
@@ -80,6 +80,7 @@ export type UpdateProjectDTO = {
|
||||
newSlug?: string;
|
||||
secretSharing?: boolean;
|
||||
showSnapshotsLegacy?: boolean;
|
||||
defaultProduct?: ProjectType;
|
||||
};
|
||||
|
||||
export type UpdatePitVersionLimitDTO = { projectSlug: string; pitVersionLimit: number };
|
||||
|
||||
@@ -168,11 +168,10 @@ export const Navbar = () => {
|
||||
<p className="pl-2 pr-3 text-lg text-mineshaft-400/70">/</p>
|
||||
<DropdownMenu modal={false}>
|
||||
<Link to="/organization/projects">
|
||||
<div className="flex cursor-pointer items-center gap-2 text-sm text-white">
|
||||
<div className="flex cursor-pointer items-center gap-2 text-sm text-white transition-all duration-100 hover:text-primary-400">
|
||||
<div>
|
||||
<FontAwesomeIcon icon={faCubes} className="pr-1 text-xs" />
|
||||
</div>
|
||||
|
||||
<div className="max-w-32 overflow-hidden text-ellipsis">{currentOrg?.name}</div>
|
||||
<div className="mr-2 rounded border border-mineshaft-500 px-1 text-xs text-bunker-300">
|
||||
{getPlan(subscription)}
|
||||
|
||||
@@ -102,13 +102,13 @@ export const ProjectSelect = () => {
|
||||
<DropdownMenu modal={false}>
|
||||
<Link
|
||||
to={getProjectHomePage(
|
||||
getCurrentProductFromUrl(window.location.href) || ProjectType.SecretManager
|
||||
getCurrentProductFromUrl(window.location.href) || currentWorkspace.defaultProduct
|
||||
)}
|
||||
params={{
|
||||
projectId: currentWorkspace.id
|
||||
}}
|
||||
>
|
||||
<div className="flex cursor-pointer items-center gap-2 text-sm text-white">
|
||||
<div className="flex cursor-pointer items-center gap-2 text-sm text-white duration-100 hover:text-primary-400">
|
||||
<div>
|
||||
<FontAwesomeIcon icon={faCube} className="text-xs" />
|
||||
</div>
|
||||
@@ -156,7 +156,7 @@ export const ProjectSelect = () => {
|
||||
// to reproduce change this back to router.push and switch between two projects with different env count
|
||||
// look into this on dashboard revamp
|
||||
const url = linkOptions({
|
||||
to: getProjectHomePage(workspace.defaultType),
|
||||
to: getProjectHomePage(workspace.defaultProduct),
|
||||
params: {
|
||||
projectId: workspace.id
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ export const OrgAdminProjects = withPermission(
|
||||
<TBody>
|
||||
{isProjectsLoading && <TableSkeleton columns={4} innerKey="projects" />}
|
||||
{!isProjectsLoading &&
|
||||
projects?.map(({ name, slug, createdAt, id, defaultType }) => (
|
||||
projects?.map(({ name, slug, createdAt, id, defaultProduct }) => (
|
||||
<Tr key={`project-${id}`} className="group w-full">
|
||||
<Td>{name}</Td>
|
||||
<Td>{slug}</Td>
|
||||
@@ -126,7 +126,7 @@ export const OrgAdminProjects = withPermission(
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
handleAccessProject(defaultType, id);
|
||||
handleAccessProject(defaultProduct, id);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faSignIn} />}
|
||||
disabled={
|
||||
|
||||
@@ -418,10 +418,10 @@ export const LogsFilter = ({
|
||||
onChange(e);
|
||||
}}
|
||||
placeholder="All projects"
|
||||
options={workspacesInOrg.map(({ name, id, defaultType }) => ({
|
||||
options={workspacesInOrg.map(({ name, id, defaultProduct }) => ({
|
||||
name,
|
||||
id,
|
||||
type: defaultType
|
||||
type: defaultProduct
|
||||
}))}
|
||||
getOptionValue={(option) => option.id}
|
||||
getOptionLabel={(option) => option.name}
|
||||
|
||||
@@ -222,7 +222,7 @@ export const AllProjectView = ({
|
||||
onKeyDown={(evt) => {
|
||||
if (evt.key === "Enter" && workspace.isMember) {
|
||||
navigate({
|
||||
to: getProjectHomePage(workspace.defaultType),
|
||||
to: getProjectHomePage(workspace.defaultProduct),
|
||||
params: {
|
||||
projectId: workspace.id
|
||||
}
|
||||
@@ -232,7 +232,7 @@ export const AllProjectView = ({
|
||||
onClick={() => {
|
||||
if (workspace.isMember) {
|
||||
navigate({
|
||||
to: getProjectHomePage(workspace.defaultType),
|
||||
to: getProjectHomePage(workspace.defaultProduct),
|
||||
params: {
|
||||
projectId: workspace.id
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ export const MyProjectView = ({
|
||||
<div
|
||||
onClick={() => {
|
||||
navigate({
|
||||
to: getProjectHomePage(workspace.defaultType),
|
||||
to: getProjectHomePage(workspace.defaultProduct),
|
||||
params: {
|
||||
projectId: workspace.id
|
||||
}
|
||||
@@ -215,7 +215,7 @@ export const MyProjectView = ({
|
||||
<div
|
||||
onClick={() => {
|
||||
navigate({
|
||||
to: getProjectHomePage(workspace.defaultType),
|
||||
to: getProjectHomePage(workspace.defaultProduct),
|
||||
params: {
|
||||
projectId: workspace.id
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user