🎨 format with prettier

This commit is contained in:
Fred KISSIE
2025-12-02 02:40:50 +01:00
parent 18db4a11c8
commit 342bedc012
2 changed files with 627 additions and 242 deletions

View File

@@ -89,7 +89,9 @@ export default function CreateInternalResourceDialog({
// mode: z.enum(["host", "cidr", "port"]), // mode: z.enum(["host", "cidr", "port"]),
mode: z.enum(["host", "cidr"]), mode: z.enum(["host", "cidr"]),
destination: z.string().min(1), destination: z.string().min(1),
siteId: z.int().positive(t("createInternalResourceDialogPleaseSelectSite")), siteId: z
.int()
.positive(t("createInternalResourceDialogPleaseSelectSite")),
// protocol: z.enum(["tcp", "udp"]), // protocol: z.enum(["tcp", "udp"]),
// proxyPort: z.int() // proxyPort: z.int()
// .positive() // .positive()
@@ -101,25 +103,31 @@ export default function CreateInternalResourceDialog({
// .max(65535, t("createInternalResourceDialogDestinationPortMax")) // .max(65535, t("createInternalResourceDialogDestinationPortMax"))
// .nullish(), // .nullish(),
alias: z.string().nullish(), alias: z.string().nullish(),
roles: z.array( roles: z
z.object({ .array(
id: z.string(), z.object({
text: z.string() id: z.string(),
}) text: z.string()
).optional(), })
users: z.array( )
z.object({ .optional(),
id: z.string(), users: z
text: z.string() .array(
}) z.object({
).optional(), id: z.string(),
clients: z.array( text: z.string()
z.object({ })
id: z.string(), )
text: z.string() .optional(),
}) clients: z
).optional() .array(
}) z.object({
id: z.string(),
text: z.string()
})
)
.optional()
});
// .refine( // .refine(
// (data) => { // (data) => {
// if (data.mode === "port") { // if (data.mode === "port") {
@@ -159,12 +167,24 @@ export default function CreateInternalResourceDialog({
type FormData = z.infer<typeof formSchema>; type FormData = z.infer<typeof formSchema>;
const [allRoles, setAllRoles] = useState<{ id: string; text: string }[]>([]); const [allRoles, setAllRoles] = useState<{ id: string; text: string }[]>(
const [allUsers, setAllUsers] = useState<{ id: string; text: string }[]>([]); []
const [allClients, setAllClients] = useState<{ id: string; text: string }[]>([]); );
const [activeRolesTagIndex, setActiveRolesTagIndex] = useState<number | null>(null); const [allUsers, setAllUsers] = useState<{ id: string; text: string }[]>(
const [activeUsersTagIndex, setActiveUsersTagIndex] = useState<number | null>(null); []
const [activeClientsTagIndex, setActiveClientsTagIndex] = useState<number | null>(null); );
const [allClients, setAllClients] = useState<
{ id: string; text: string }[]
>([]);
const [activeRolesTagIndex, setActiveRolesTagIndex] = useState<
number | null
>(null);
const [activeUsersTagIndex, setActiveUsersTagIndex] = useState<
number | null
>(null);
const [activeClientsTagIndex, setActiveClientsTagIndex] = useState<
number | null
>(null);
const [hasMachineClients, setHasMachineClients] = useState(false); const [hasMachineClients, setHasMachineClients] = useState(false);
const availableSites = sites.filter( const availableSites = sites.filter(
@@ -211,11 +231,18 @@ export default function CreateInternalResourceDialog({
useEffect(() => { useEffect(() => {
const fetchRolesUsersAndClients = async () => { const fetchRolesUsersAndClients = async () => {
try { try {
const [rolesResponse, usersResponse, clientsResponse] = await Promise.all([ const [rolesResponse, usersResponse, clientsResponse] =
api.get<AxiosResponse<ListRolesResponse>>(`/org/${orgId}/roles`), await Promise.all([
api.get<AxiosResponse<ListUsersResponse>>(`/org/${orgId}/users`), api.get<AxiosResponse<ListRolesResponse>>(
api.get<AxiosResponse<ListClientsResponse>>(`/org/${orgId}/clients?filter=machine&limit=1000`) `/org/${orgId}/roles`
]); ),
api.get<AxiosResponse<ListUsersResponse>>(
`/org/${orgId}/users`
),
api.get<AxiosResponse<ListClientsResponse>>(
`/org/${orgId}/clients?filter=machine&limit=1000`
)
]);
setAllRoles( setAllRoles(
rolesResponse.data.data.roles rolesResponse.data.data.roles
@@ -243,7 +270,10 @@ export default function CreateInternalResourceDialog({
setAllClients(machineClients); setAllClients(machineClients);
setHasMachineClients(machineClients.length > 0); setHasMachineClients(machineClients.length > 0);
} catch (error) { } catch (error) {
console.error("Error fetching roles, users, and clients:", error); console.error(
"Error fetching roles, users, and clients:",
error
);
} }
}; };
@@ -265,10 +295,19 @@ export default function CreateInternalResourceDialog({
// destinationPort: data.mode === "port" ? data.destinationPort : undefined, // destinationPort: data.mode === "port" ? data.destinationPort : undefined,
destination: data.destination, destination: data.destination,
enabled: true, enabled: true,
alias: data.alias && typeof data.alias === "string" && data.alias.trim() ? data.alias : undefined, alias:
roleIds: data.roles ? data.roles.map((r) => parseInt(r.id)) : [], data.alias &&
typeof data.alias === "string" &&
data.alias.trim()
? data.alias
: undefined,
roleIds: data.roles
? data.roles.map((r) => parseInt(r.id))
: [],
userIds: data.users ? data.users.map((u) => u.id) : [], userIds: data.users ? data.users.map((u) => u.id) : [],
clientIds: data.clients ? data.clients.map((c) => parseInt(c.id)) : [] clientIds: data.clients
? data.clients.map((c) => parseInt(c.id))
: []
} }
); );
@@ -295,7 +334,9 @@ export default function CreateInternalResourceDialog({
toast({ toast({
title: t("createInternalResourceDialogSuccess"), title: t("createInternalResourceDialogSuccess"),
description: t("createInternalResourceDialogInternalResourceCreatedSuccessfully"), description: t(
"createInternalResourceDialogInternalResourceCreatedSuccessfully"
),
variant: "default" variant: "default"
}); });
@@ -307,7 +348,9 @@ export default function CreateInternalResourceDialog({
title: t("createInternalResourceDialogError"), title: t("createInternalResourceDialogError"),
description: formatAxiosError( description: formatAxiosError(
error, error,
t("createInternalResourceDialogFailedToCreateInternalResource") t(
"createInternalResourceDialogFailedToCreateInternalResource"
)
), ),
variant: "destructive" variant: "destructive"
}); });
@@ -321,13 +364,19 @@ export default function CreateInternalResourceDialog({
<Credenza open={open} onOpenChange={setOpen}> <Credenza open={open} onOpenChange={setOpen}>
<CredenzaContent className="max-w-md"> <CredenzaContent className="max-w-md">
<CredenzaHeader> <CredenzaHeader>
<CredenzaTitle>{t("createInternalResourceDialogNoSitesAvailable")}</CredenzaTitle> <CredenzaTitle>
{t("createInternalResourceDialogNoSitesAvailable")}
</CredenzaTitle>
<CredenzaDescription> <CredenzaDescription>
{t("createInternalResourceDialogNoSitesAvailableDescription")} {t(
"createInternalResourceDialogNoSitesAvailableDescription"
)}
</CredenzaDescription> </CredenzaDescription>
</CredenzaHeader> </CredenzaHeader>
<CredenzaFooter> <CredenzaFooter>
<Button onClick={() => setOpen(false)}>{t("createInternalResourceDialogClose")}</Button> <Button onClick={() => setOpen(false)}>
{t("createInternalResourceDialogClose")}
</Button>
</CredenzaFooter> </CredenzaFooter>
</CredenzaContent> </CredenzaContent>
</Credenza> </Credenza>
@@ -338,9 +387,13 @@ export default function CreateInternalResourceDialog({
<Credenza open={open} onOpenChange={setOpen}> <Credenza open={open} onOpenChange={setOpen}>
<CredenzaContent className="max-w-2xl"> <CredenzaContent className="max-w-2xl">
<CredenzaHeader> <CredenzaHeader>
<CredenzaTitle>{t("createInternalResourceDialogCreateClientResource")}</CredenzaTitle> <CredenzaTitle>
{t("createInternalResourceDialogCreateClientResource")}
</CredenzaTitle>
<CredenzaDescription> <CredenzaDescription>
{t("createInternalResourceDialogCreateClientResourceDescription")} {t(
"createInternalResourceDialogCreateClientResourceDescription"
)}
</CredenzaDescription> </CredenzaDescription>
</CredenzaHeader> </CredenzaHeader>
<CredenzaBody> <CredenzaBody>
@@ -353,7 +406,9 @@ export default function CreateInternalResourceDialog({
{/* Resource Properties Form */} {/* Resource Properties Form */}
<div> <div>
<h3 className="text-lg font-semibold mb-4"> <h3 className="text-lg font-semibold mb-4">
{t("createInternalResourceDialogResourceProperties")} {t(
"createInternalResourceDialogResourceProperties"
)}
</h3> </h3>
<div className="space-y-4"> <div className="space-y-4">
<FormField <FormField
@@ -361,7 +416,11 @@ export default function CreateInternalResourceDialog({
name="name" name="name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("createInternalResourceDialogName")}</FormLabel> <FormLabel>
{t(
"createInternalResourceDialogName"
)}
</FormLabel>
<FormControl> <FormControl>
<Input {...field} /> <Input {...field} />
</FormControl> </FormControl>
@@ -375,7 +434,11 @@ export default function CreateInternalResourceDialog({
name="siteId" name="siteId"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col"> <FormItem className="flex flex-col">
<FormLabel>{t("createInternalResourceDialogSite")}</FormLabel> <FormLabel>
{t(
"createInternalResourceDialogSite"
)}
</FormLabel>
<Popover> <Popover>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<FormControl> <FormControl>
@@ -384,43 +447,71 @@ export default function CreateInternalResourceDialog({
role="combobox" role="combobox"
className={cn( className={cn(
"w-full justify-between", "w-full justify-between",
!field.value && "text-muted-foreground" !field.value &&
"text-muted-foreground"
)} )}
> >
{field.value {field.value
? availableSites.find( ? availableSites.find(
(site) => site.siteId === field.value (
site
) =>
site.siteId ===
field.value
)?.name )?.name
: t("createInternalResourceDialogSelectSite")} : t(
"createInternalResourceDialogSelectSite"
)}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button> </Button>
</FormControl> </FormControl>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-full p-0"> <PopoverContent className="w-full p-0">
<Command> <Command>
<CommandInput placeholder={t("createInternalResourceDialogSearchSites")} /> <CommandInput
placeholder={t(
"createInternalResourceDialogSearchSites"
)}
/>
<CommandList> <CommandList>
<CommandEmpty>{t("createInternalResourceDialogNoSitesFound")}</CommandEmpty> <CommandEmpty>
{t(
"createInternalResourceDialogNoSitesFound"
)}
</CommandEmpty>
<CommandGroup> <CommandGroup>
{availableSites.map((site) => ( {availableSites.map(
<CommandItem (
key={site.siteId} site
value={site.name} ) => (
onSelect={() => { <CommandItem
field.onChange(site.siteId); key={
}} site.siteId
> }
<Check value={
className={cn( site.name
"mr-2 h-4 w-4", }
field.value === site.siteId onSelect={() => {
? "opacity-100" field.onChange(
: "opacity-0" site.siteId
)} );
/> }}
{site.name} >
</CommandItem> <Check
))} className={cn(
"mr-2 h-4 w-4",
field.value ===
site.siteId
? "opacity-100"
: "opacity-0"
)}
/>
{
site.name
}
</CommandItem>
)
)}
</CommandGroup> </CommandGroup>
</CommandList> </CommandList>
</Command> </Command>
@@ -431,14 +522,20 @@ export default function CreateInternalResourceDialog({
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="mode" name="mode"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("createInternalResourceDialogMode")}</FormLabel> <FormLabel>
{t(
"createInternalResourceDialogMode"
)}
</FormLabel>
<Select <Select
onValueChange={field.onChange} onValueChange={
field.onChange
}
value={field.value} value={field.value}
> >
<FormControl> <FormControl>
@@ -448,15 +545,23 @@ export default function CreateInternalResourceDialog({
</FormControl> </FormControl>
<SelectContent> <SelectContent>
{/* <SelectItem value="port">{t("createInternalResourceDialogModePort")}</SelectItem> */} {/* <SelectItem value="port">{t("createInternalResourceDialogModePort")}</SelectItem> */}
<SelectItem value="host">{t("createInternalResourceDialogModeHost")}</SelectItem> <SelectItem value="host">
<SelectItem value="cidr">{t("createInternalResourceDialogModeCidr")}</SelectItem> {t(
"createInternalResourceDialogModeHost"
)}
</SelectItem>
<SelectItem value="cidr">
{t(
"createInternalResourceDialogModeCidr"
)}
</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
{/* {/*
{mode === "port" && ( {mode === "port" && (
<> <>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
@@ -521,7 +626,9 @@ export default function CreateInternalResourceDialog({
{/* Target Configuration Form */} {/* Target Configuration Form */}
<div> <div>
<h3 className="text-lg font-semibold mb-4"> <h3 className="text-lg font-semibold mb-4">
{t("createInternalResourceDialogTargetConfiguration")} {t(
"createInternalResourceDialogTargetConfiguration"
)}
</h3> </h3>
<div className="space-y-4"> <div className="space-y-4">
<FormField <FormField
@@ -530,14 +637,22 @@ export default function CreateInternalResourceDialog({
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel>
{t("createInternalResourceDialogDestination")} {t(
"createInternalResourceDialogDestination"
)}
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input {...field} /> <Input {...field} />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{mode === "host" && t("createInternalResourceDialogDestinationHostDescription")} {mode === "host" &&
{mode === "cidr" && t("createInternalResourceDialogDestinationCidrDescription")} t(
"createInternalResourceDialogDestinationHostDescription"
)}
{mode === "cidr" &&
t(
"createInternalResourceDialogDestinationCidrDescription"
)}
{/* {mode === "port" && t("createInternalResourceDialogDestinationIPDescription")} */} {/* {mode === "port" && t("createInternalResourceDialogDestinationIPDescription")} */}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
@@ -584,12 +699,23 @@ export default function CreateInternalResourceDialog({
name="alias" name="alias"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("createInternalResourceDialogAlias")}</FormLabel> <FormLabel>
{t(
"createInternalResourceDialogAlias"
)}
</FormLabel>
<FormControl> <FormControl>
<Input {...field} value={field.value ?? ""} /> <Input
{...field}
value={
field.value ?? ""
}
/>
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{t("createInternalResourceDialogAliasDescription")} {t(
"createInternalResourceDialogAliasDescription"
)}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -609,31 +735,53 @@ export default function CreateInternalResourceDialog({
name="roles" name="roles"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col items-start"> <FormItem className="flex flex-col items-start">
<FormLabel>{t("roles")}</FormLabel> <FormLabel>
{t("roles")}
</FormLabel>
<FormControl> <FormControl>
<TagInput <TagInput
{...field} {...field}
activeTagIndex={activeRolesTagIndex} activeTagIndex={
setActiveTagIndex={setActiveRolesTagIndex} activeRolesTagIndex
placeholder={t("accessRoleSelect2")} }
setActiveTagIndex={
setActiveRolesTagIndex
}
placeholder={t(
"accessRoleSelect2"
)}
size="sm" size="sm"
tags={form.getValues().roles || []} tags={
form.getValues()
.roles || []
}
setTags={(newRoles) => { setTags={(newRoles) => {
form.setValue( form.setValue(
"roles", "roles",
newRoles as [Tag, ...Tag[]] newRoles as [
Tag,
...Tag[]
]
); );
}} }}
enableAutocomplete={true} enableAutocomplete={
autocompleteOptions={allRoles} true
}
autocompleteOptions={
allRoles
}
allowDuplicates={false} allowDuplicates={false}
restrictTagsToAutocompleteOptions={true} restrictTagsToAutocompleteOptions={
true
}
sortTags={true} sortTags={true}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
<FormDescription> <FormDescription>
{t("resourceRoleDescription")} {t(
"resourceRoleDescription"
)}
</FormDescription> </FormDescription>
</FormItem> </FormItem>
)} )}
@@ -643,25 +791,45 @@ export default function CreateInternalResourceDialog({
name="users" name="users"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col items-start"> <FormItem className="flex flex-col items-start">
<FormLabel>{t("users")}</FormLabel> <FormLabel>
{t("users")}
</FormLabel>
<FormControl> <FormControl>
<TagInput <TagInput
{...field} {...field}
activeTagIndex={activeUsersTagIndex} activeTagIndex={
setActiveTagIndex={setActiveUsersTagIndex} activeUsersTagIndex
placeholder={t("accessUserSelect")} }
tags={form.getValues().users || []} setActiveTagIndex={
setActiveUsersTagIndex
}
placeholder={t(
"accessUserSelect"
)}
tags={
form.getValues()
.users || []
}
size="sm" size="sm"
setTags={(newUsers) => { setTags={(newUsers) => {
form.setValue( form.setValue(
"users", "users",
newUsers as [Tag, ...Tag[]] newUsers as [
Tag,
...Tag[]
]
); );
}} }}
enableAutocomplete={true} enableAutocomplete={
autocompleteOptions={allUsers} true
}
autocompleteOptions={
allUsers
}
allowDuplicates={false} allowDuplicates={false}
restrictTagsToAutocompleteOptions={true} restrictTagsToAutocompleteOptions={
true
}
sortTags={true} sortTags={true}
/> />
</FormControl> </FormControl>
@@ -675,31 +843,62 @@ export default function CreateInternalResourceDialog({
name="clients" name="clients"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col items-start"> <FormItem className="flex flex-col items-start">
<FormLabel>{t("clients")}</FormLabel> <FormLabel>
{t("clients")}
</FormLabel>
<FormControl> <FormControl>
<TagInput <TagInput
{...field} {...field}
activeTagIndex={activeClientsTagIndex} activeTagIndex={
setActiveTagIndex={setActiveClientsTagIndex} activeClientsTagIndex
placeholder={t("accessClientSelect") || "Select machine clients"} }
setActiveTagIndex={
setActiveClientsTagIndex
}
placeholder={
t(
"accessClientSelect"
) ||
"Select machine clients"
}
size="sm" size="sm"
tags={form.getValues().clients || []} tags={
setTags={(newClients) => { form.getValues()
.clients ||
[]
}
setTags={(
newClients
) => {
form.setValue( form.setValue(
"clients", "clients",
newClients as [Tag, ...Tag[]] newClients as [
Tag,
...Tag[]
]
); );
}} }}
enableAutocomplete={true} enableAutocomplete={
autocompleteOptions={allClients} true
allowDuplicates={false} }
restrictTagsToAutocompleteOptions={true} autocompleteOptions={
allClients
}
allowDuplicates={
false
}
restrictTagsToAutocompleteOptions={
true
}
sortTags={true} sortTags={true}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
<FormDescription> <FormDescription>
{t("resourceClientDescription") || "Machine clients that can access this resource"} {t(
"resourceClientDescription"
) ||
"Machine clients that can access this resource"}
</FormDescription> </FormDescription>
</FormItem> </FormItem>
)} )}

View File

@@ -81,32 +81,41 @@ export default function EditInternalResourceDialog({
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const formSchema = z.object({ const formSchema = z.object({
name: z.string().min(1, t("editInternalResourceDialogNameRequired")).max(255, t("editInternalResourceDialogNameMaxLength")), name: z
.string()
.min(1, t("editInternalResourceDialogNameRequired"))
.max(255, t("editInternalResourceDialogNameMaxLength")),
mode: z.enum(["host", "cidr", "port"]), mode: z.enum(["host", "cidr", "port"]),
// protocol: z.enum(["tcp", "udp"]).nullish(), // protocol: z.enum(["tcp", "udp"]).nullish(),
// proxyPort: z.int().positive().min(1, t("editInternalResourceDialogProxyPortMin")).max(65535, t("editInternalResourceDialogProxyPortMax")).nullish(), // proxyPort: z.int().positive().min(1, t("editInternalResourceDialogProxyPortMin")).max(65535, t("editInternalResourceDialogProxyPortMax")).nullish(),
destination: z.string().min(1), destination: z.string().min(1),
// destinationPort: z.int().positive().min(1, t("editInternalResourceDialogDestinationPortMin")).max(65535, t("editInternalResourceDialogDestinationPortMax")).nullish(), // destinationPort: z.int().positive().min(1, t("editInternalResourceDialogDestinationPortMin")).max(65535, t("editInternalResourceDialogDestinationPortMax")).nullish(),
alias: z.string().nullish(), alias: z.string().nullish(),
roles: z.array( roles: z
z.object({ .array(
id: z.string(), z.object({
text: z.string() id: z.string(),
}) text: z.string()
).optional(), })
users: z.array( )
z.object({ .optional(),
id: z.string(), users: z
text: z.string() .array(
}) z.object({
).optional(), id: z.string(),
clients: z.array( text: z.string()
z.object({ })
id: z.string(), )
text: z.string() .optional(),
}) clients: z
).optional() .array(
}) z.object({
id: z.string(),
text: z.string()
})
)
.optional()
});
// .refine( // .refine(
// (data) => { // (data) => {
// if (data.mode === "port") { // if (data.mode === "port") {
@@ -146,12 +155,24 @@ export default function EditInternalResourceDialog({
type FormData = z.infer<typeof formSchema>; type FormData = z.infer<typeof formSchema>;
const [allRoles, setAllRoles] = useState<{ id: string; text: string }[]>([]); const [allRoles, setAllRoles] = useState<{ id: string; text: string }[]>(
const [allUsers, setAllUsers] = useState<{ id: string; text: string }[]>([]); []
const [allClients, setAllClients] = useState<{ id: string; text: string }[]>([]); );
const [activeRolesTagIndex, setActiveRolesTagIndex] = useState<number | null>(null); const [allUsers, setAllUsers] = useState<{ id: string; text: string }[]>(
const [activeUsersTagIndex, setActiveUsersTagIndex] = useState<number | null>(null); []
const [activeClientsTagIndex, setActiveClientsTagIndex] = useState<number | null>(null); );
const [allClients, setAllClients] = useState<
{ id: string; text: string }[]
>([]);
const [activeRolesTagIndex, setActiveRolesTagIndex] = useState<
number | null
>(null);
const [activeUsersTagIndex, setActiveUsersTagIndex] = useState<
number | null
>(null);
const [activeClientsTagIndex, setActiveClientsTagIndex] = useState<
number | null
>(null);
const [loadingRolesUsers, setLoadingRolesUsers] = useState(false); const [loadingRolesUsers, setLoadingRolesUsers] = useState(false);
const [hasMachineClients, setHasMachineClients] = useState(false); const [hasMachineClients, setHasMachineClients] = useState(false);
@@ -183,22 +204,30 @@ export default function EditInternalResourceDialog({
resourceUsersResponse, resourceUsersResponse,
clientsResponse clientsResponse
] = await Promise.all([ ] = await Promise.all([
api.get<AxiosResponse<ListRolesResponse>>(`/org/${orgId}/roles`), api.get<AxiosResponse<ListRolesResponse>>(
`/org/${orgId}/roles`
),
api.get<AxiosResponse<ListSiteResourceRolesResponse>>( api.get<AxiosResponse<ListSiteResourceRolesResponse>>(
`/site-resource/${resource.id}/roles` `/site-resource/${resource.id}/roles`
), ),
api.get<AxiosResponse<ListUsersResponse>>(`/org/${orgId}/users`), api.get<AxiosResponse<ListUsersResponse>>(
`/org/${orgId}/users`
),
api.get<AxiosResponse<ListSiteResourceUsersResponse>>( api.get<AxiosResponse<ListSiteResourceUsersResponse>>(
`/site-resource/${resource.id}/users` `/site-resource/${resource.id}/users`
), ),
api.get<AxiosResponse<ListClientsResponse>>(`/org/${orgId}/clients?filter=machine&limit=1000`) api.get<AxiosResponse<ListClientsResponse>>(
`/org/${orgId}/clients?filter=machine&limit=1000`
)
]); ]);
let resourceClientsResponse: AxiosResponse<AxiosResponse<ListSiteResourceClientsResponse>>; let resourceClientsResponse: AxiosResponse<
AxiosResponse<ListSiteResourceClientsResponse>
>;
try { try {
resourceClientsResponse = await api.get<AxiosResponse<ListSiteResourceClientsResponse>>( resourceClientsResponse = await api.get<
`/site-resource/${resource.id}/clients` AxiosResponse<ListSiteResourceClientsResponse>
); >(`/site-resource/${resource.id}/clients`);
} catch { } catch {
resourceClientsResponse = { resourceClientsResponse = {
data: { data: {
@@ -255,16 +284,21 @@ export default function EditInternalResourceDialog({
})); }));
setAllClients(machineClients); setAllClients(machineClients);
const existingClients = resourceClientsResponse.data.data.clients.map((c: { clientId: number; name: string }) => ({ const existingClients =
id: c.clientId.toString(), resourceClientsResponse.data.data.clients.map(
text: c.name (c: { clientId: number; name: string }) => ({
})); id: c.clientId.toString(),
text: c.name
})
);
form.setValue("clients", existingClients); form.setValue("clients", existingClients);
// Show clients tag input if there are machine clients OR existing client access // Show clients tag input if there are machine clients OR existing client access
setHasMachineClients(machineClients.length > 0 || existingClients.length > 0); setHasMachineClients(
machineClients.length > 0 || existingClients.length > 0
);
} catch (error) { } catch (error) {
console.error("Error fetching roles, users, and clients:", error); console.error("Error fetching roles, users, and clients:", error);
} finally { } finally {
@@ -295,18 +329,26 @@ export default function EditInternalResourceDialog({
setIsSubmitting(true); setIsSubmitting(true);
try { try {
// Update the site resource // Update the site resource
await api.post(`/org/${orgId}/site/${resource.siteId}/resource/${resource.id}`, { await api.post(
name: data.name, `/org/${orgId}/site/${resource.siteId}/resource/${resource.id}`,
mode: data.mode, {
// protocol: data.mode === "port" ? data.protocol : null, name: data.name,
// proxyPort: data.mode === "port" ? data.proxyPort : null, mode: data.mode,
// destinationPort: data.mode === "port" ? data.destinationPort : null, // protocol: data.mode === "port" ? data.protocol : null,
destination: data.destination, // proxyPort: data.mode === "port" ? data.proxyPort : null,
alias: data.alias && typeof data.alias === "string" && data.alias.trim() ? data.alias : null, // destinationPort: data.mode === "port" ? data.destinationPort : null,
roleIds: (data.roles || []).map((r) => parseInt(r.id)), destination: data.destination,
userIds: (data.users || []).map((u) => u.id), alias:
clientIds: (data.clients || []).map((c) => parseInt(c.id)) data.alias &&
}); typeof data.alias === "string" &&
data.alias.trim()
? data.alias
: null,
roleIds: (data.roles || []).map((r) => parseInt(r.id)),
userIds: (data.users || []).map((u) => u.id),
clientIds: (data.clients || []).map((c) => parseInt(c.id))
}
);
// Update roles, users, and clients // Update roles, users, and clients
// await Promise.all([ // await Promise.all([
@@ -323,7 +365,9 @@ export default function EditInternalResourceDialog({
toast({ toast({
title: t("editInternalResourceDialogSuccess"), title: t("editInternalResourceDialogSuccess"),
description: t("editInternalResourceDialogInternalResourceUpdatedSuccessfully"), description: t(
"editInternalResourceDialogInternalResourceUpdatedSuccessfully"
),
variant: "default" variant: "default"
}); });
@@ -333,7 +377,12 @@ export default function EditInternalResourceDialog({
console.error("Error updating internal resource:", error); console.error("Error updating internal resource:", error);
toast({ toast({
title: t("editInternalResourceDialogError"), title: t("editInternalResourceDialogError"),
description: formatAxiosError(error, t("editInternalResourceDialogFailedToUpdateInternalResource")), description: formatAxiosError(
error,
t(
"editInternalResourceDialogFailedToUpdateInternalResource"
)
),
variant: "destructive" variant: "destructive"
}); });
} finally { } finally {
@@ -345,24 +394,41 @@ export default function EditInternalResourceDialog({
<Credenza open={open} onOpenChange={setOpen}> <Credenza open={open} onOpenChange={setOpen}>
<CredenzaContent className="max-w-2xl"> <CredenzaContent className="max-w-2xl">
<CredenzaHeader> <CredenzaHeader>
<CredenzaTitle>{t("editInternalResourceDialogEditClientResource")}</CredenzaTitle> <CredenzaTitle>
{t("editInternalResourceDialogEditClientResource")}
</CredenzaTitle>
<CredenzaDescription> <CredenzaDescription>
{t("editInternalResourceDialogUpdateResourceProperties", { resourceName: resource.name })} {t(
"editInternalResourceDialogUpdateResourceProperties",
{ resourceName: resource.name }
)}
</CredenzaDescription> </CredenzaDescription>
</CredenzaHeader> </CredenzaHeader>
<CredenzaBody> <CredenzaBody>
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-6" id="edit-internal-resource-form"> <form
onSubmit={form.handleSubmit(handleSubmit)}
className="space-y-6"
id="edit-internal-resource-form"
>
{/* Resource Properties Form */} {/* Resource Properties Form */}
<div> <div>
<h3 className="text-lg font-semibold mb-4">{t("editInternalResourceDialogResourceProperties")}</h3> <h3 className="text-lg font-semibold mb-4">
{t(
"editInternalResourceDialogResourceProperties"
)}
</h3>
<div className="space-y-4"> <div className="space-y-4">
<FormField <FormField
control={form.control} control={form.control}
name="name" name="name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("editInternalResourceDialogName")}</FormLabel> <FormLabel>
{t(
"editInternalResourceDialogName"
)}
</FormLabel>
<FormControl> <FormControl>
<Input {...field} /> <Input {...field} />
</FormControl> </FormControl>
@@ -376,9 +442,15 @@ export default function EditInternalResourceDialog({
name="mode" name="mode"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("editInternalResourceDialogMode")}</FormLabel> <FormLabel>
{t(
"editInternalResourceDialogMode"
)}
</FormLabel>
<Select <Select
onValueChange={field.onChange} onValueChange={
field.onChange
}
value={field.value} value={field.value}
> >
<FormControl> <FormControl>
@@ -388,8 +460,16 @@ export default function EditInternalResourceDialog({
</FormControl> </FormControl>
<SelectContent> <SelectContent>
{/* <SelectItem value="port">{t("editInternalResourceDialogModePort")}</SelectItem> */} {/* <SelectItem value="port">{t("editInternalResourceDialogModePort")}</SelectItem> */}
<SelectItem value="host">{t("editInternalResourceDialogModeHost")}</SelectItem> <SelectItem value="host">
<SelectItem value="cidr">{t("editInternalResourceDialogModeCidr")}</SelectItem> {t(
"editInternalResourceDialogModeHost"
)}
</SelectItem>
<SelectItem value="cidr">
{t(
"editInternalResourceDialogModeCidr"
)}
</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />
@@ -448,20 +528,34 @@ export default function EditInternalResourceDialog({
{/* Target Configuration Form */} {/* Target Configuration Form */}
<div> <div>
<h3 className="text-lg font-semibold mb-4">{t("editInternalResourceDialogTargetConfiguration")}</h3> <h3 className="text-lg font-semibold mb-4">
{t(
"editInternalResourceDialogTargetConfiguration"
)}
</h3>
<div className="space-y-4"> <div className="space-y-4">
<FormField <FormField
control={form.control} control={form.control}
name="destination" name="destination"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("editInternalResourceDialogDestination")}</FormLabel> <FormLabel>
{t(
"editInternalResourceDialogDestination"
)}
</FormLabel>
<FormControl> <FormControl>
<Input {...field} /> <Input {...field} />
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{mode === "host" && t("editInternalResourceDialogDestinationHostDescription")} {mode === "host" &&
{mode === "cidr" && t("editInternalResourceDialogDestinationCidrDescription")} t(
"editInternalResourceDialogDestinationHostDescription"
)}
{mode === "cidr" &&
t(
"editInternalResourceDialogDestinationCidrDescription"
)}
{/* {mode === "port" && t("editInternalResourceDialogDestinationIPDescription")} */} {/* {mode === "port" && t("editInternalResourceDialogDestinationIPDescription")} */}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
@@ -499,12 +593,23 @@ export default function EditInternalResourceDialog({
name="alias" name="alias"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>{t("editInternalResourceDialogAlias")}</FormLabel> <FormLabel>
{t(
"editInternalResourceDialogAlias"
)}
</FormLabel>
<FormControl> <FormControl>
<Input {...field} value={field.value ?? ""} /> <Input
{...field}
value={
field.value ?? ""
}
/>
</FormControl> </FormControl>
<FormDescription> <FormDescription>
{t("editInternalResourceDialogAliasDescription")} {t(
"editInternalResourceDialogAliasDescription"
)}
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -529,31 +634,57 @@ export default function EditInternalResourceDialog({
name="roles" name="roles"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col items-start"> <FormItem className="flex flex-col items-start">
<FormLabel>{t("roles")}</FormLabel> <FormLabel>
{t("roles")}
</FormLabel>
<FormControl> <FormControl>
<TagInput <TagInput
{...field} {...field}
activeTagIndex={activeRolesTagIndex} activeTagIndex={
setActiveTagIndex={setActiveRolesTagIndex} activeRolesTagIndex
placeholder={t("accessRoleSelect2")} }
setActiveTagIndex={
setActiveRolesTagIndex
}
placeholder={t(
"accessRoleSelect2"
)}
size="sm" size="sm"
tags={form.getValues().roles || []} tags={
setTags={(newRoles) => { form.getValues()
.roles || []
}
setTags={(
newRoles
) => {
form.setValue( form.setValue(
"roles", "roles",
newRoles as [Tag, ...Tag[]] newRoles as [
Tag,
...Tag[]
]
); );
}} }}
enableAutocomplete={true} enableAutocomplete={
autocompleteOptions={allRoles} true
allowDuplicates={false} }
restrictTagsToAutocompleteOptions={true} autocompleteOptions={
allRoles
}
allowDuplicates={
false
}
restrictTagsToAutocompleteOptions={
true
}
sortTags={true} sortTags={true}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
<FormDescription> <FormDescription>
{t("resourceRoleDescription")} {t(
"resourceRoleDescription"
)}
</FormDescription> </FormDescription>
</FormItem> </FormItem>
)} )}
@@ -563,25 +694,49 @@ export default function EditInternalResourceDialog({
name="users" name="users"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col items-start"> <FormItem className="flex flex-col items-start">
<FormLabel>{t("users")}</FormLabel> <FormLabel>
{t("users")}
</FormLabel>
<FormControl> <FormControl>
<TagInput <TagInput
{...field} {...field}
activeTagIndex={activeUsersTagIndex} activeTagIndex={
setActiveTagIndex={setActiveUsersTagIndex} activeUsersTagIndex
placeholder={t("accessUserSelect")} }
tags={form.getValues().users || []} setActiveTagIndex={
setActiveUsersTagIndex
}
placeholder={t(
"accessUserSelect"
)}
tags={
form.getValues()
.users || []
}
size="sm" size="sm"
setTags={(newUsers) => { setTags={(
newUsers
) => {
form.setValue( form.setValue(
"users", "users",
newUsers as [Tag, ...Tag[]] newUsers as [
Tag,
...Tag[]
]
); );
}} }}
enableAutocomplete={true} enableAutocomplete={
autocompleteOptions={allUsers} true
allowDuplicates={false} }
restrictTagsToAutocompleteOptions={true} autocompleteOptions={
allUsers
}
allowDuplicates={
false
}
restrictTagsToAutocompleteOptions={
true
}
sortTags={true} sortTags={true}
/> />
</FormControl> </FormControl>
@@ -589,42 +744,73 @@ export default function EditInternalResourceDialog({
</FormItem> </FormItem>
)} )}
/> />
{hasMachineClients && ( {hasMachineClients && (
<FormField <FormField
control={form.control} control={form.control}
name="clients" name="clients"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col items-start"> <FormItem className="flex flex-col items-start">
<FormLabel>{t("clients")}</FormLabel> <FormLabel>
<FormControl> {t("clients")}
<TagInput </FormLabel>
{...field} <FormControl>
activeTagIndex={activeClientsTagIndex} <TagInput
setActiveTagIndex={setActiveClientsTagIndex} {...field}
placeholder={t("accessClientSelect") || "Select machine clients"} activeTagIndex={
size="sm" activeClientsTagIndex
tags={form.getValues().clients || []} }
setTags={(newClients) => { setActiveTagIndex={
form.setValue( setActiveClientsTagIndex
"clients", }
newClients as [Tag, ...Tag[]] placeholder={
); t(
}} "accessClientSelect"
enableAutocomplete={true} ) ||
autocompleteOptions={allClients} "Select machine clients"
allowDuplicates={false} }
restrictTagsToAutocompleteOptions={true} size="sm"
sortTags={true} tags={
/> form.getValues()
</FormControl> .clients ||
<FormMessage /> []
<FormDescription> }
{t("resourceClientDescription") || "Machine clients that can access this resource"} setTags={(
</FormDescription> newClients
</FormItem> ) => {
)} form.setValue(
/> "clients",
)} newClients as [
Tag,
...Tag[]
]
);
}}
enableAutocomplete={
true
}
autocompleteOptions={
allClients
}
allowDuplicates={
false
}
restrictTagsToAutocompleteOptions={
true
}
sortTags={true}
/>
</FormControl>
<FormMessage />
<FormDescription>
{t(
"resourceClientDescription"
) ||
"Machine clients that can access this resource"}
</FormDescription>
</FormItem>
)}
/>
)}
</div> </div>
)} )}
</div> </div>
@@ -645,7 +831,7 @@ export default function EditInternalResourceDialog({
disabled={isSubmitting} disabled={isSubmitting}
loading={isSubmitting} loading={isSubmitting}
> >
{t("editInternalResourceDialogSaveResource")} {t("editInternalResourceDialogSaveResource")}
</Button> </Button>
</CredenzaFooter> </CredenzaFooter>
</CredenzaContent> </CredenzaContent>