mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-21 14:28:09 +00:00
Merge branch 'heads/main' into daniel/vault-import
This commit is contained in:
+3
-2
@@ -24,6 +24,7 @@ export const EditProjectTemplateSection = ({ template, onBack }: Props) => {
|
||||
initialData: template,
|
||||
enabled: !isInfisicalTemplate
|
||||
});
|
||||
const finalTemplate = isInfisicalTemplate ? template : projectTemplate;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -41,10 +42,10 @@ export const EditProjectTemplateSection = ({ template, onBack }: Props) => {
|
||||
<div className="flex h-[60vh] w-full items-center justify-center p-24">
|
||||
<Spinner />
|
||||
</div>
|
||||
) : projectTemplate ? (
|
||||
) : finalTemplate ? (
|
||||
<EditProjectTemplate
|
||||
isInfisicalTemplate={isInfisicalTemplate}
|
||||
projectTemplate={projectTemplate}
|
||||
projectTemplate={finalTemplate}
|
||||
onBack={onBack}
|
||||
/>
|
||||
) : (
|
||||
|
||||
+13
-6
@@ -5,8 +5,10 @@ import { createNotification } from "@app/components/notifications";
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import { Button, DeleteActionModal } from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { getProjectTitle } from "@app/helpers/project";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { TProjectTemplate, useDeleteProjectTemplate } from "@app/hooks/api/projectTemplates";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
|
||||
import { ProjectTemplateDetailsModal } from "../../ProjectTemplateDetailsModal";
|
||||
import { ProjectTemplateEnvironmentsForm } from "./ProjectTemplateEnvironmentsForm";
|
||||
@@ -24,7 +26,7 @@ export const EditProjectTemplate = ({ isInfisicalTemplate, projectTemplate, onBa
|
||||
"editDetails"
|
||||
] as const);
|
||||
|
||||
const { id: templateId, name, description } = projectTemplate;
|
||||
const { id: templateId, name, description, type } = projectTemplate;
|
||||
|
||||
const deleteProjectTemplate = useDeleteProjectTemplate();
|
||||
|
||||
@@ -53,7 +55,10 @@ export const EditProjectTemplate = ({ isInfisicalTemplate, projectTemplate, onBa
|
||||
<div className="mb-4 flex items-start justify-between border-b border-bunker-400 pb-4">
|
||||
<div className="flex-col">
|
||||
<h3 className="text-xl font-semibold">{name}</h3>
|
||||
<h2 className="text-sm text-mineshaft-400">{description || "Project Template"}</h2>
|
||||
<h2 className="text-sm text-mineshaft-400">
|
||||
{`${getProjectTitle(type)} - `}
|
||||
{description || "Project Template"}
|
||||
</h2>
|
||||
</div>
|
||||
{!isInfisicalTemplate && (
|
||||
<div className="flex gap-2">
|
||||
@@ -94,10 +99,12 @@ export const EditProjectTemplate = ({ isInfisicalTemplate, projectTemplate, onBa
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ProjectTemplateEnvironmentsForm
|
||||
isInfisicalTemplate={isInfisicalTemplate}
|
||||
projectTemplate={projectTemplate}
|
||||
/>
|
||||
{type === ProjectType.SecretManager && (
|
||||
<ProjectTemplateEnvironmentsForm
|
||||
isInfisicalTemplate={isInfisicalTemplate}
|
||||
projectTemplate={projectTemplate}
|
||||
/>
|
||||
)}
|
||||
<ProjectTemplateRolesSection
|
||||
isInfisicalTemplate={isInfisicalTemplate}
|
||||
projectTemplate={projectTemplate}
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ export const ProjectTemplateEditRoleForm = ({
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<AddPoliciesButton isDisabled={isDisabled} />
|
||||
<AddPoliciesButton isDisabled={isDisabled} projectType={projectTemplate.type} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
+69
-3
@@ -1,5 +1,6 @@
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
@@ -7,21 +8,25 @@ import {
|
||||
Button,
|
||||
FormControl,
|
||||
Input,
|
||||
Lottie,
|
||||
Modal,
|
||||
ModalClose,
|
||||
ModalContent,
|
||||
TextArea
|
||||
} from "@app/components/v2";
|
||||
import { getProjectLottieIcon } from "@app/helpers/project";
|
||||
import {
|
||||
TProjectTemplate,
|
||||
useCreateProjectTemplate,
|
||||
useUpdateProjectTemplate
|
||||
} from "@app/hooks/api/projectTemplates";
|
||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||
import { slugSchema } from "@app/lib/schemas";
|
||||
|
||||
const formSchema = z.object({
|
||||
name: slugSchema({ min: 1, max: 64, field: "Name" }),
|
||||
description: z.string().max(500).optional()
|
||||
description: z.string().max(500).optional(),
|
||||
type: z.nativeEnum(ProjectType).optional()
|
||||
});
|
||||
|
||||
export type FormData = z.infer<typeof formSchema>;
|
||||
@@ -38,6 +43,29 @@ type FormProps = {
|
||||
onComplete: (template: TProjectTemplate) => void;
|
||||
};
|
||||
|
||||
const PROJECT_TYPE_MENU_ITEMS = [
|
||||
{
|
||||
label: "Secrets Management",
|
||||
value: ProjectType.SecretManager
|
||||
},
|
||||
{
|
||||
label: "Certificates Management",
|
||||
value: ProjectType.CertificateManager
|
||||
},
|
||||
{
|
||||
label: "KMS",
|
||||
value: ProjectType.KMS
|
||||
},
|
||||
{
|
||||
label: "SSH",
|
||||
value: ProjectType.SSH
|
||||
},
|
||||
{
|
||||
label: "Secret Scanning",
|
||||
value: ProjectType.SecretScanning
|
||||
}
|
||||
];
|
||||
|
||||
const ProjectTemplateForm = ({ onComplete, projectTemplate }: FormProps) => {
|
||||
const createProjectTemplate = useCreateProjectTemplate();
|
||||
const updateProjectTemplate = useUpdateProjectTemplate();
|
||||
@@ -45,12 +73,14 @@ const ProjectTemplateForm = ({ onComplete, projectTemplate }: FormProps) => {
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
control,
|
||||
formState: { isSubmitting, errors }
|
||||
} = useForm<FormData>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
name: projectTemplate?.name,
|
||||
description: projectTemplate?.description
|
||||
description: projectTemplate?.description,
|
||||
type: ProjectType.SecretManager
|
||||
}
|
||||
});
|
||||
|
||||
@@ -90,6 +120,42 @@ const ProjectTemplateForm = ({ onComplete, projectTemplate }: FormProps) => {
|
||||
>
|
||||
<Input autoFocus placeholder="my-project-template" {...register("name")} />
|
||||
</FormControl>
|
||||
<Controller
|
||||
control={control}
|
||||
name="type"
|
||||
defaultValue={ProjectType.SecretManager}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Project Type"
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
className="flex-1"
|
||||
>
|
||||
<div className="mt-2 grid grid-cols-5 gap-4">
|
||||
{PROJECT_TYPE_MENU_ITEMS.map((el) => (
|
||||
<div
|
||||
key={el.value}
|
||||
className={twMerge(
|
||||
"flex cursor-pointer flex-col items-center gap-2 rounded border border-mineshaft-600 p-4 opacity-75 transition-all hover:border-primary-400 hover:bg-mineshaft-600",
|
||||
field.value === el.value && "border-primary-400 bg-mineshaft-600 opacity-100"
|
||||
)}
|
||||
onClick={() => field.onChange(el.value)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
field.onChange(el.value);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Lottie icon={getProjectLottieIcon(el.value)} className="h-8 w-8" />
|
||||
<div className="text-center text-xs">{el.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<FormControl
|
||||
label="Description (optional)"
|
||||
errorText={errors.description?.message}
|
||||
|
||||
+4
-22
@@ -23,6 +23,7 @@ import {
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects, useSubscription } from "@app/context";
|
||||
import { getProjectTitle } from "@app/helpers/project";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
import { TProjectTemplate, useListProjectTemplates } from "@app/hooks/api/projectTemplates";
|
||||
|
||||
@@ -66,8 +67,8 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th>Name</Th>
|
||||
<Th>Type</Th>
|
||||
<Th>Roles</Th>
|
||||
<Th>Environments</Th>
|
||||
<Th />
|
||||
</Tr>
|
||||
</THead>
|
||||
@@ -80,7 +81,7 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
|
||||
/>
|
||||
)}
|
||||
{filteredTemplates.map((template) => {
|
||||
const { id, name, roles, environments = [], description } = template;
|
||||
const { id, name, roles, description, type } = template;
|
||||
return (
|
||||
<Tr
|
||||
onClick={() => onEdit(template)}
|
||||
@@ -99,6 +100,7 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
|
||||
</Tooltip>
|
||||
)}
|
||||
</Td>
|
||||
<Td>{getProjectTitle(type)}</Td>
|
||||
<Td className="pl-8">
|
||||
{roles.length}
|
||||
{roles.length > 0 && (
|
||||
@@ -119,26 +121,6 @@ export const ProjectTemplatesTable = ({ onEdit }: Props) => {
|
||||
</Tooltip>
|
||||
)}
|
||||
</Td>
|
||||
<Td className="pl-14">
|
||||
{environments?.length || 0}
|
||||
{environments?.length && (
|
||||
<Tooltip
|
||||
content={
|
||||
<ul className="ml-2 list-disc">
|
||||
{environments
|
||||
?.sort((a, b) => (a.position > b.position ? 1 : -1))
|
||||
.map((env) => <li key={env.slug}>{env.name}</li>)}
|
||||
</ul>
|
||||
}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
size="sm"
|
||||
className="ml-2 text-mineshaft-400"
|
||||
icon={faCircleInfo}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Td>
|
||||
<Td className="w-5">
|
||||
{name !== "default" && (
|
||||
<OrgPermissionCan
|
||||
|
||||
Reference in New Issue
Block a user