diff --git a/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx b/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx index 9609d9da1..414133c86 100644 --- a/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx +++ b/src/app/[orgId]/settings/resources/proxy/[niceId]/authentication/page.tsx @@ -21,6 +21,14 @@ import { SwitchInput } from "@app/components/SwitchInput"; import { Tag, TagInput } from "@app/components/tags/tag-input"; import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert"; import { Button } from "@app/components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList +} from "@app/components/ui/command"; import { Form, FormControl, @@ -31,6 +39,11 @@ import { FormMessage } from "@app/components/ui/form"; import { InfoPopup } from "@app/components/ui/info-popup"; +import { + Popover, + PopoverContent, + PopoverTrigger +} from "@app/components/ui/popover"; import { Select, SelectContent, @@ -45,19 +58,25 @@ import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { useResourceContext } from "@app/hooks/useResourceContext"; import { toast } from "@app/hooks/useToast"; import { createApiClient, formatAxiosError } from "@app/lib/api"; +import { cn } from "@app/lib/cn"; import { getUserDisplayName } from "@app/lib/getUserDisplayName"; -import { orgQueries, resourceQueries } from "@app/lib/queries"; +import { + orgQueries, + resourcePolicyQueries, + resourceQueries +} from "@app/lib/queries"; import { ResourcePolicyContext, ResourcePolicyProvider } from "@app/providers/ResourcePolicyProvider"; import { zodResolver } from "@hookform/resolvers/zod"; +import { CaretSortIcon } from "@radix-ui/react-icons"; import { build } from "@server/build"; import { tierMatrix } from "@server/lib/billing/tierMatrix"; import { UserType } from "@server/types/UserTypes"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import SetResourcePasswordForm from "components/SetResourcePasswordForm"; -import { Binary, Bot, InfoIcon, Key } from "lucide-react"; +import { Binary, Bot, CheckIcon, InfoIcon, Key } from "lucide-react"; import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import { @@ -103,6 +122,41 @@ export default function ResourceAuthenticationPage() { }) ); + const form = useForm({ + resolver: zodResolver(resourceTypeSchema), + defaultValues: { + type: "inline" + } + }); + + const selectedResourceType = useWatch({ + control: form.control, + name: "type" + }); + + const [resourcePolicysearchQuery, setResourcePolicySearchQuery] = + useState(""); + + const { data: policiesList = [] } = useQuery({ + ...orgQueries.policies({ + orgId: org.org.orgId, + name: resourcePolicysearchQuery + }), + enabled: selectedResourceType === "shared" + }); + + const { data: sharedPolicy } = useQuery({ + ...resourcePolicyQueries.single({ + resourcePolicyId: resource.resourcePolicyId ?? 1 + }), + enabled: !!resource.resourcePolicyId + }); + + const [selectedPolicy, setSelectedPolicy] = useState<{ + name: string; + id: number; + } | null>(null); + const pageLoading = isLoadingPolicies || !defaultPolicy; const [ @@ -127,66 +181,12 @@ export default function ResourceAuthenticationPage() { } ]; - const form = useForm({ - resolver: zodResolver(resourceTypeSchema), - defaultValues: { - type: "inline" - } - }); - - const selectedResourceType = useWatch({ - control: form.control, - name: "type" - }); - if (pageLoading) { return <>; } return ( <> - {isSetPasswordOpen && ( - { - setIsSetPasswordOpen(false); - updateAuthInfo({ - password: true - }); - }} - /> - )} - - {isSetPincodeOpen && ( - { - setIsSetPincodeOpen(false); - updateAuthInfo({ - pincode: true - }); - }} - /> - )} - - {isSetHeaderAuthOpen && ( - { - setIsSetHeaderAuthOpen(false); - updateAuthInfo({ - headerAuth: true - }); - }} - /> - )} - @@ -206,20 +206,94 @@ export default function ResourceAuthenticationPage() { }} cols={2} /> + {selectedResourceType === "shared" && ( + + + + + + + + + + {t("siteNotFound")} + + + {policiesList.map((policy) => ( + + setSelectedPolicy({ + id: policy.resourcePolicyId, + name: policy.name + }) + } + > + + {policy.name} + + ))} + + + + + + )} - + - - - + {selectedResourceType === "inline" ? ( + + + + ) : ( + sharedPolicy && ( + + + + ) + )} ); diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 194d8b467..f53ddf9d0 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -61,12 +61,19 @@ export function SettingsSectionBody({ } export function SettingsSectionFooter({ - children + children, + className }: { children: React.ReactNode; + className?: string; }) { return ( -
+
{children}
); diff --git a/src/lib/queries.ts b/src/lib/queries.ts index 8f5ccab1c..022239006 100644 --- a/src/lib/queries.ts +++ b/src/lib/queries.ts @@ -30,6 +30,7 @@ import z from "zod"; import { remote } from "./api"; import { durationToMs } from "./durationToMs"; import { wait } from "./wait"; +import type { ListResourcePoliciesResponse } from "@server/routers/resource/types"; export type ProductUpdate = { link: string | null; @@ -196,6 +197,41 @@ export const orgQueries = { return res.data.data.resources; } + }), + policies: ({ orgId, name }: { orgId: string; name?: string }) => + queryOptions({ + queryKey: ["ORG", orgId, "RESOURCES_POLICIES", name] as const, + queryFn: async ({ signal, meta }) => { + const sp = new URLSearchParams({ + pageSize: "10" + }); + + if (name) { + sp.set("query", name); + } + + const res = await meta!.api.get< + AxiosResponse + >(`/org/${orgId}/resource-policies?${sp.toString()}`, { + signal + }); + + return res.data.data.policies; + } + }) +}; + +export const resourcePolicyQueries = { + single: ({ resourcePolicyId }: { resourcePolicyId: number }) => + queryOptions({ + queryKey: ["RESOURCE_POLICIES", resourcePolicyId] as const, + queryFn: async ({ signal, meta }) => { + const res = await meta!.api.get< + AxiosResponse + >(`/resource-policy/${resourcePolicyId}`, { signal }); + + return res.data.data; + } }) }; @@ -325,7 +361,7 @@ export const resourceQueries = { }), defaultPolicy: ({ resourceId }: { resourceId: number }) => queryOptions({ - queryKey: ["RESOURCES", resourceId, "POLICIES"] as const, + queryKey: ["RESOURCES", resourceId, "DEFAULT_POLICY"] as const, queryFn: async ({ signal, meta }) => { const res = await meta!.api.get< AxiosResponse