mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-10 06:28:50 +00:00
feat: completed frontend
This commit is contained in:
@@ -45,7 +45,11 @@ export const ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(
|
|||||||
)}
|
)}
|
||||||
style={{ maxHeight: "90%" }}
|
style={{ maxHeight: "90%" }}
|
||||||
>
|
>
|
||||||
{title && <CardTitle subTitle={subTitle}>{title}</CardTitle>}
|
{title && (
|
||||||
|
<DialogPrimitive.Title>
|
||||||
|
<CardTitle subTitle={subTitle}>{title}</CardTitle>
|
||||||
|
</DialogPrimitive.Title>
|
||||||
|
)}
|
||||||
<CardBody
|
<CardBody
|
||||||
className={twMerge("overflow-y-auto overflow-x-hidden", bodyClassName)}
|
className={twMerge("overflow-y-auto overflow-x-hidden", bodyClassName)}
|
||||||
style={{ maxHeight: "90%" }}
|
style={{ maxHeight: "90%" }}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export type TDynamicSecretProvider =
|
|||||||
revocationStatement: string;
|
revocationStatement: string;
|
||||||
renewStatement?: string;
|
renewStatement?: string;
|
||||||
ca?: string | undefined;
|
ca?: string | undefined;
|
||||||
|
gatewayId?: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export { useDeleteGateway } from "./mutation";
|
export { useDeleteGatewayById, useUpdateGatewayById } from "./mutation";
|
||||||
export { gatewaysQueryKeys } from "./queries";
|
export { gatewaysQueryKeys } from "./queries";
|
||||||
|
|||||||
@@ -3,15 +3,28 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { gatewaysQueryKeys } from "./queries";
|
import { gatewaysQueryKeys } from "./queries";
|
||||||
|
import { TUpdateGatewayDTO } from "./types";
|
||||||
|
|
||||||
export const useDeleteGateway = () => {
|
export const useDeleteGatewayById = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (id: string) => {
|
mutationFn: (id: string) => {
|
||||||
return apiRequest.delete(`/api/v1/gateways/${id}`);
|
return apiRequest.delete(`/api/v1/gateways/${id}`);
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries(gatewaysQueryKeys.list());
|
queryClient.invalidateQueries(gatewaysQueryKeys.list({}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateGatewayById = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({ id, name }: TUpdateGatewayDTO) => {
|
||||||
|
return apiRequest.patch(`/api/v1/gateways/${id}`, { name });
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries(gatewaysQueryKeys.list({}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,16 +2,22 @@ import { queryOptions } from "@tanstack/react-query";
|
|||||||
|
|
||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { TGateway } from "./types";
|
import { TGateway, TListGatewayDTO } from "./types";
|
||||||
|
|
||||||
export const gatewaysQueryKeys = {
|
export const gatewaysQueryKeys = {
|
||||||
allKey: () => ["gateways"],
|
allKey: () => ["gateways"],
|
||||||
listKey: () => [...gatewaysQueryKeys.allKey(), "list"],
|
listKey: ({ projectId }: TListGatewayDTO) => [
|
||||||
list: () =>
|
...gatewaysQueryKeys.allKey(),
|
||||||
|
"list",
|
||||||
|
{ projectId }
|
||||||
|
],
|
||||||
|
list: ({ projectId }: TListGatewayDTO = {}) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: gatewaysQueryKeys.listKey(),
|
queryKey: gatewaysQueryKeys.listKey({ projectId }),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await apiRequest.get<{ gateways: TGateway[] }>("/api/v1/gateways");
|
const { data } = await apiRequest.get<{ gateways: TGateway[] }>("/api/v1/gateways", {
|
||||||
|
params: { projectId }
|
||||||
|
});
|
||||||
return data.gateways;
|
return data.gateways;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,6 +12,15 @@ export type TGateway = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TUpdateGatewayDTO = {
|
||||||
|
id: string;
|
||||||
|
name?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type TDeleteGatewayDTO = {
|
export type TDeleteGatewayDTO = {
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TListGatewayDTO = {
|
||||||
|
projectId?: string;
|
||||||
|
};
|
||||||
|
|||||||
@@ -3,17 +3,30 @@ import { Helmet } from "react-helmet";
|
|||||||
import {
|
import {
|
||||||
faArrowUpRightFromSquare,
|
faArrowUpRightFromSquare,
|
||||||
faBookOpen,
|
faBookOpen,
|
||||||
|
faEdit,
|
||||||
|
faEllipsisV,
|
||||||
faMagnifyingGlass,
|
faMagnifyingGlass,
|
||||||
faPlug,
|
faPlug,
|
||||||
faSearch
|
faSearch,
|
||||||
|
faTrash
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { OrgPermissionCan } from "@app/components/permissions";
|
||||||
import {
|
import {
|
||||||
|
DeleteActionModal,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
PageHeader,
|
PageHeader,
|
||||||
Table,
|
Table,
|
||||||
TableContainer,
|
TableContainer,
|
||||||
@@ -22,20 +35,44 @@ import {
|
|||||||
Td,
|
Td,
|
||||||
Th,
|
Th,
|
||||||
THead,
|
THead,
|
||||||
|
Tooltip,
|
||||||
Tr
|
Tr
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
|
OrgGatewayPermissionActions,
|
||||||
OrgPermissionAppConnectionActions,
|
OrgPermissionAppConnectionActions,
|
||||||
OrgPermissionSubjects
|
OrgPermissionSubjects
|
||||||
} from "@app/context/OrgPermissionContext/types";
|
} from "@app/context/OrgPermissionContext/types";
|
||||||
import { withPermission } from "@app/hoc";
|
import { withPermission } from "@app/hoc";
|
||||||
import { gatewaysQueryKeys } from "@app/hooks/api/gateways";
|
import { usePopUp } from "@app/hooks";
|
||||||
|
import { gatewaysQueryKeys, useDeleteGatewayById } from "@app/hooks/api/gateways";
|
||||||
|
|
||||||
|
import { EditGatewayDetailsModal } from "./components/EditGatewayDetailsModal";
|
||||||
|
|
||||||
export const GatewayListPage = withPermission(
|
export const GatewayListPage = withPermission(
|
||||||
() => {
|
() => {
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const { data: gateways, isPending: isGatewayLoading } = useQuery(gatewaysQueryKeys.list());
|
const { data: gateways, isPending: isGatewayLoading } = useQuery(gatewaysQueryKeys.list());
|
||||||
|
|
||||||
|
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([
|
||||||
|
"deleteGateway",
|
||||||
|
"editDetails"
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const deleteGatewayById = useDeleteGatewayById();
|
||||||
|
|
||||||
|
const handleDeleteGateway = async () => {
|
||||||
|
await deleteGatewayById.mutateAsync((popUp.deleteGateway.data as { id: string }).id, {
|
||||||
|
onSuccess: () => {
|
||||||
|
handlePopUpToggle("deleteGateway");
|
||||||
|
createNotification({
|
||||||
|
type: "success",
|
||||||
|
text: "Successfully delete gateway"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const filteredGateway = gateways?.filter((el) =>
|
const filteredGateway = gateways?.filter((el) =>
|
||||||
el.name.toLowerCase().includes(search.toLowerCase())
|
el.name.toLowerCase().includes(search.toLowerCase())
|
||||||
);
|
);
|
||||||
@@ -100,13 +137,70 @@ export const GatewayListPage = withPermission(
|
|||||||
{filteredGateway?.map((el) => (
|
{filteredGateway?.map((el) => (
|
||||||
<Tr key={el.id}>
|
<Tr key={el.id}>
|
||||||
<Td>{el.name}</Td>
|
<Td>{el.name}</Td>
|
||||||
<Td>{format(new Date(el.issuedAt), "yyyy-MM-dd")}</Td>
|
<Td>{format(new Date(el.issuedAt), "yyyy-MM-dd hh:mm:ss aaa")}</Td>
|
||||||
<Td>{el.identity.name}</Td>
|
<Td>{el.identity.name}</Td>
|
||||||
<Td />
|
<Td className="w-5">
|
||||||
|
<Tooltip className="max-w-sm text-center" content="Options">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="Options"
|
||||||
|
colorSchema="secondary"
|
||||||
|
className="w-6"
|
||||||
|
variant="plain"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faEllipsisV} />
|
||||||
|
</IconButton>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
<OrgPermissionCan
|
||||||
|
I={OrgGatewayPermissionActions.Edit}
|
||||||
|
a={OrgPermissionSubjects.Gateway}
|
||||||
|
>
|
||||||
|
{(isAllowed: boolean) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
icon={<FontAwesomeIcon icon={faEdit} />}
|
||||||
|
onClick={() => handlePopUpOpen("editDetails", el)}
|
||||||
|
>
|
||||||
|
Edit Details
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
|
<OrgPermissionCan
|
||||||
|
I={OrgPermissionAppConnectionActions.Delete}
|
||||||
|
a={OrgPermissionSubjects.AppConnections}
|
||||||
|
>
|
||||||
|
{(isAllowed: boolean) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
isDisabled={!isAllowed}
|
||||||
|
icon={<FontAwesomeIcon icon={faTrash} />}
|
||||||
|
className="text-red"
|
||||||
|
onClick={() => handlePopUpOpen("deleteGateway", el)}
|
||||||
|
>
|
||||||
|
Delete Gateway
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</OrgPermissionCan>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Tooltip>
|
||||||
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
))}
|
))}
|
||||||
</TBody>
|
</TBody>
|
||||||
</Table>
|
</Table>
|
||||||
|
<Modal
|
||||||
|
isOpen={popUp.editDetails.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("editDetails", isOpen)}
|
||||||
|
>
|
||||||
|
<ModalContent title="Edit Gateway">
|
||||||
|
<EditGatewayDetailsModal
|
||||||
|
gatewayDetails={popUp.editDetails.data}
|
||||||
|
onClose={() => handlePopUpToggle("editDetails")}
|
||||||
|
/>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
{!isGatewayLoading && !filteredGateway?.length && (
|
{!isGatewayLoading && !filteredGateway?.length && (
|
||||||
<EmptyState
|
<EmptyState
|
||||||
title={
|
title={
|
||||||
@@ -117,6 +211,15 @@ export const GatewayListPage = withPermission(
|
|||||||
icon={gateways?.length ? faSearch : faPlug}
|
icon={gateways?.length ? faSearch : faPlug}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp.deleteGateway.isOpen}
|
||||||
|
title={`Are you sure want to delete gateway ${
|
||||||
|
(popUp?.deleteGateway?.data as { name: string })?.name || ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("deleteGateway", isOpen)}
|
||||||
|
deleteKey="confirm"
|
||||||
|
onDeleteApproved={() => handleDeleteGateway()}
|
||||||
|
/>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import { Button, FormControl, Input } from "@app/components/v2";
|
||||||
|
import { useUpdateGatewayById } from "@app/hooks/api";
|
||||||
|
import { TGateway } from "@app/hooks/api/gateways/types";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
gatewayDetails: TGateway;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
name: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type FormData = z.infer<typeof schema>;
|
||||||
|
|
||||||
|
export const EditGatewayDetailsModal = ({ gatewayDetails, onClose }: Props) => {
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { isSubmitting }
|
||||||
|
} = useForm<FormData>({
|
||||||
|
resolver: zodResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
name: gatewayDetails?.name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateGatewayById = useUpdateGatewayById();
|
||||||
|
|
||||||
|
const onFormSubmit = ({ name }: FormData) => {
|
||||||
|
if (isSubmitting) return;
|
||||||
|
updateGatewayById.mutate(
|
||||||
|
{
|
||||||
|
id: gatewayDetails.id,
|
||||||
|
name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
createNotification({
|
||||||
|
type: "success",
|
||||||
|
text: "Successfully updated gateway"
|
||||||
|
});
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="name"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl label="Name" isError={Boolean(error)} errorText={error?.message} isRequired>
|
||||||
|
<Input {...field} placeholder="db-subnet-1" />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<div className="mt-4 flex items-center">
|
||||||
|
<Button className="mr-4" size="sm" type="submit" isLoading={isSubmitting}>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
<Button colorSchema="secondary" variant="plain" onClick={() => onClose()}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
+6
@@ -14,6 +14,7 @@ import {
|
|||||||
TFormSchema
|
TFormSchema
|
||||||
} from "../OrgRoleModifySection.utils";
|
} from "../OrgRoleModifySection.utils";
|
||||||
import { OrgPermissionAdminConsoleRow } from "./OrgPermissionAdminConsoleRow";
|
import { OrgPermissionAdminConsoleRow } from "./OrgPermissionAdminConsoleRow";
|
||||||
|
import { OrgGatewayPermissionRow } from "./OrgPermissionGatewayRow";
|
||||||
import { OrgPermissionKmipRow } from "./OrgPermissionKmipRow";
|
import { OrgPermissionKmipRow } from "./OrgPermissionKmipRow";
|
||||||
import { OrgRoleWorkspaceRow } from "./OrgRoleWorkspaceRow";
|
import { OrgRoleWorkspaceRow } from "./OrgRoleWorkspaceRow";
|
||||||
import { RolePermissionRow } from "./RolePermissionRow";
|
import { RolePermissionRow } from "./RolePermissionRow";
|
||||||
@@ -171,6 +172,11 @@ export const RolePermissionsSection = ({ roleId }: Props) => {
|
|||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
isEditable={isCustomRole}
|
isEditable={isCustomRole}
|
||||||
/>
|
/>
|
||||||
|
<OrgGatewayPermissionRow
|
||||||
|
control={control}
|
||||||
|
setValue={setValue}
|
||||||
|
isEditable={isCustomRole}
|
||||||
|
/>
|
||||||
<OrgRoleWorkspaceRow
|
<OrgRoleWorkspaceRow
|
||||||
control={control}
|
control={control}
|
||||||
setValue={setValue}
|
setValue={setValue}
|
||||||
|
|||||||
+40
-2
@@ -1,5 +1,6 @@
|
|||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -18,7 +19,8 @@ import {
|
|||||||
SelectItem,
|
SelectItem,
|
||||||
TextArea
|
TextArea
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useCreateDynamicSecret } from "@app/hooks/api";
|
import { useWorkspace } from "@app/context";
|
||||||
|
import { gatewaysQueryKeys, useCreateDynamicSecret } from "@app/hooks/api";
|
||||||
import { DynamicSecretProviders, SqlProviders } from "@app/hooks/api/dynamicSecret/types";
|
import { DynamicSecretProviders, SqlProviders } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
@@ -32,7 +34,8 @@ const formSchema = z.object({
|
|||||||
creationStatement: z.string().min(1),
|
creationStatement: z.string().min(1),
|
||||||
revocationStatement: z.string().min(1),
|
revocationStatement: z.string().min(1),
|
||||||
renewStatement: z.string().optional(),
|
renewStatement: z.string().optional(),
|
||||||
ca: z.string().optional()
|
ca: z.string().optional(),
|
||||||
|
gatewayId: z.string().optional()
|
||||||
}),
|
}),
|
||||||
defaultTTL: z.string().superRefine((val, ctx) => {
|
defaultTTL: z.string().superRefine((val, ctx) => {
|
||||||
const valMs = ms(val);
|
const valMs = ms(val);
|
||||||
@@ -124,6 +127,8 @@ export const SqlDatabaseInputForm = ({
|
|||||||
secretPath,
|
secretPath,
|
||||||
projectSlug
|
projectSlug
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
setValue,
|
setValue,
|
||||||
@@ -137,6 +142,9 @@ export const SqlDatabaseInputForm = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const createDynamicSecret = useCreateDynamicSecret();
|
const createDynamicSecret = useCreateDynamicSecret();
|
||||||
|
const { data: projectGateways, isPending: isProjectGatewaysLoading } = useQuery(
|
||||||
|
gatewaysQueryKeys.list({ projectId: currentWorkspace.id })
|
||||||
|
);
|
||||||
|
|
||||||
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
const handleCreateDynamicSecret = async ({ name, maxTTL, provider, defaultTTL }: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
@@ -222,6 +230,36 @@ export const SqlDatabaseInputForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="provider.gatewayId"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error?.message)}
|
||||||
|
errorText={error?.message}
|
||||||
|
label="Gateway"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
value={value}
|
||||||
|
onValueChange={onChange}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
dropdownContainerClassName="max-w-none"
|
||||||
|
isLoading={isProjectGatewaysLoading}
|
||||||
|
placeholder="Select gateway"
|
||||||
|
position="popper"
|
||||||
|
>
|
||||||
|
{projectGateways?.map((el) => (
|
||||||
|
<SelectItem value={el.id} key={el.id}>
|
||||||
|
{el.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-4 mt-4 border-b border-mineshaft-500 pb-2 pl-1 font-medium text-mineshaft-200">
|
<div className="mb-4 mt-4 border-b border-mineshaft-500 pb-2 pl-1 font-medium text-mineshaft-200">
|
||||||
Configuration
|
Configuration
|
||||||
|
|||||||
+46
-3
@@ -1,5 +1,6 @@
|
|||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
@@ -18,7 +19,8 @@ import {
|
|||||||
SelectItem,
|
SelectItem,
|
||||||
TextArea
|
TextArea
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useUpdateDynamicSecret } from "@app/hooks/api";
|
import { useWorkspace } from "@app/context";
|
||||||
|
import { gatewaysQueryKeys, useUpdateDynamicSecret } from "@app/hooks/api";
|
||||||
import { SqlProviders, TDynamicSecret } from "@app/hooks/api/dynamicSecret/types";
|
import { SqlProviders, TDynamicSecret } from "@app/hooks/api/dynamicSecret/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
@@ -33,7 +35,8 @@ const formSchema = z.object({
|
|||||||
creationStatement: z.string().min(1),
|
creationStatement: z.string().min(1),
|
||||||
revocationStatement: z.string().min(1),
|
revocationStatement: z.string().min(1),
|
||||||
renewStatement: z.string().optional(),
|
renewStatement: z.string().optional(),
|
||||||
ca: z.string().optional()
|
ca: z.string().optional(),
|
||||||
|
gatewayId: z.string().optional()
|
||||||
})
|
})
|
||||||
.partial(),
|
.partial(),
|
||||||
defaultTTL: z.string().superRefine((val, ctx) => {
|
defaultTTL: z.string().superRefine((val, ctx) => {
|
||||||
@@ -81,6 +84,7 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
watch,
|
||||||
formState: { isSubmitting },
|
formState: { isSubmitting },
|
||||||
handleSubmit
|
handleSubmit
|
||||||
} = useForm<TForm>({
|
} = useForm<TForm>({
|
||||||
@@ -94,8 +98,14 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
const { data: projectGateways, isPending: isProjectGatewaysLoading } = useQuery(
|
||||||
|
gatewaysQueryKeys.list({ projectId: currentWorkspace.id })
|
||||||
|
);
|
||||||
|
|
||||||
const updateDynamicSecret = useUpdateDynamicSecret();
|
const updateDynamicSecret = useUpdateDynamicSecret();
|
||||||
|
const selectedGatewayId = watch("inputs.gatewayId");
|
||||||
|
const isGatewayInActive = projectGateways?.findIndex((el) => el.id === selectedGatewayId) === -1;
|
||||||
|
|
||||||
const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => {
|
const handleUpdateDynamicSecret = async ({ inputs, maxTTL, defaultTTL, newName }: TForm) => {
|
||||||
// wait till previous request is finished
|
// wait till previous request is finished
|
||||||
@@ -109,7 +119,7 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
data: {
|
data: {
|
||||||
maxTTL: maxTTL || undefined,
|
maxTTL: maxTTL || undefined,
|
||||||
defaultTTL,
|
defaultTTL,
|
||||||
inputs,
|
inputs: { ...inputs, gatewayId: isGatewayInActive ? null : inputs.gatewayId },
|
||||||
newName: newName === dynamicSecret.name ? undefined : newName
|
newName: newName === dynamicSecret.name ? undefined : newName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -176,6 +186,39 @@ export const EditDynamicSecretSqlProviderForm = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="inputs.gatewayId"
|
||||||
|
defaultValue=""
|
||||||
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error?.message) || isGatewayInActive}
|
||||||
|
errorText={
|
||||||
|
isGatewayInActive ? `Gateway ${selectedGatewayId} is removed` : error?.message
|
||||||
|
}
|
||||||
|
label="Gateway"
|
||||||
|
helperText=""
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
value={value}
|
||||||
|
onValueChange={onChange}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
dropdownContainerClassName="max-w-none"
|
||||||
|
isLoading={isProjectGatewaysLoading}
|
||||||
|
placeholder="Select gateway"
|
||||||
|
position="popper"
|
||||||
|
>
|
||||||
|
{projectGateways?.map((el) => (
|
||||||
|
<SelectItem value={el.id} key={el.id}>
|
||||||
|
{el.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-4 border-b border-b-mineshaft-600 pb-2">Configuration</div>
|
<div className="mb-4 border-b border-b-mineshaft-600 pb-2">Configuration</div>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
|
|||||||
Reference in New Issue
Block a user