mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-16 05:24:18 +00:00
🚧 wip
This commit is contained in:
@@ -21,6 +21,14 @@ import { SwitchInput } from "@app/components/SwitchInput";
|
|||||||
import { Tag, TagInput } from "@app/components/tags/tag-input";
|
import { Tag, TagInput } from "@app/components/tags/tag-input";
|
||||||
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
|
import { Alert, AlertDescription, AlertTitle } from "@app/components/ui/alert";
|
||||||
import { Button } from "@app/components/ui/button";
|
import { Button } from "@app/components/ui/button";
|
||||||
|
import {
|
||||||
|
Command,
|
||||||
|
CommandEmpty,
|
||||||
|
CommandGroup,
|
||||||
|
CommandInput,
|
||||||
|
CommandItem,
|
||||||
|
CommandList
|
||||||
|
} from "@app/components/ui/command";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -31,6 +39,11 @@ import {
|
|||||||
FormMessage
|
FormMessage
|
||||||
} from "@app/components/ui/form";
|
} from "@app/components/ui/form";
|
||||||
import { InfoPopup } from "@app/components/ui/info-popup";
|
import { InfoPopup } from "@app/components/ui/info-popup";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger
|
||||||
|
} from "@app/components/ui/popover";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
@@ -45,19 +58,25 @@ import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
|||||||
import { useResourceContext } from "@app/hooks/useResourceContext";
|
import { useResourceContext } from "@app/hooks/useResourceContext";
|
||||||
import { toast } from "@app/hooks/useToast";
|
import { toast } from "@app/hooks/useToast";
|
||||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||||
|
import { cn } from "@app/lib/cn";
|
||||||
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
|
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
|
||||||
import { orgQueries, resourceQueries } from "@app/lib/queries";
|
import {
|
||||||
|
orgQueries,
|
||||||
|
resourcePolicyQueries,
|
||||||
|
resourceQueries
|
||||||
|
} from "@app/lib/queries";
|
||||||
import {
|
import {
|
||||||
ResourcePolicyContext,
|
ResourcePolicyContext,
|
||||||
ResourcePolicyProvider
|
ResourcePolicyProvider
|
||||||
} from "@app/providers/ResourcePolicyProvider";
|
} from "@app/providers/ResourcePolicyProvider";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { CaretSortIcon } from "@radix-ui/react-icons";
|
||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||||
import { UserType } from "@server/types/UserTypes";
|
import { UserType } from "@server/types/UserTypes";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import SetResourcePasswordForm from "components/SetResourcePasswordForm";
|
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 { useTranslations } from "next-intl";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import {
|
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 pageLoading = isLoadingPolicies || !defaultPolicy;
|
||||||
|
|
||||||
const [
|
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) {
|
if (pageLoading) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isSetPasswordOpen && (
|
|
||||||
<SetResourcePasswordForm
|
|
||||||
open={isSetPasswordOpen}
|
|
||||||
setOpen={setIsSetPasswordOpen}
|
|
||||||
resourceId={resource.resourceId}
|
|
||||||
onSetPassword={() => {
|
|
||||||
setIsSetPasswordOpen(false);
|
|
||||||
updateAuthInfo({
|
|
||||||
password: true
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isSetPincodeOpen && (
|
|
||||||
<SetResourcePincodeForm
|
|
||||||
open={isSetPincodeOpen}
|
|
||||||
setOpen={setIsSetPincodeOpen}
|
|
||||||
resourceId={resource.resourceId}
|
|
||||||
onSetPincode={() => {
|
|
||||||
setIsSetPincodeOpen(false);
|
|
||||||
updateAuthInfo({
|
|
||||||
pincode: true
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isSetHeaderAuthOpen && (
|
|
||||||
<SetResourceHeaderAuthForm
|
|
||||||
open={isSetHeaderAuthOpen}
|
|
||||||
setOpen={setIsSetHeaderAuthOpen}
|
|
||||||
resourceId={resource.resourceId}
|
|
||||||
onSetHeaderAuth={() => {
|
|
||||||
setIsSetHeaderAuthOpen(false);
|
|
||||||
updateAuthInfo({
|
|
||||||
headerAuth: true
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<SettingsContainer>
|
<SettingsContainer>
|
||||||
<SettingsSection>
|
<SettingsSection>
|
||||||
<SettingsSectionHeader>
|
<SettingsSectionHeader>
|
||||||
@@ -206,20 +206,94 @@ export default function ResourceAuthenticationPage() {
|
|||||||
}}
|
}}
|
||||||
cols={2}
|
cols={2}
|
||||||
/>
|
/>
|
||||||
|
{selectedResourceType === "shared" && (
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
role="combobox"
|
||||||
|
className={
|
||||||
|
"w-full md:w-1/2 justify-between"
|
||||||
|
// "w-45 justify-between text-sm border-r pr-4 rounded-none h-8 hover:bg-transparent",
|
||||||
|
// "rounded-l-md rounded-r-xs"
|
||||||
|
// !proxyTarget.siteId && "text-muted-foreground"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="truncate max-w-37.5">
|
||||||
|
{selectedPolicy
|
||||||
|
? selectedPolicy.name
|
||||||
|
: t("resourcePolicySelect")}
|
||||||
|
</span>
|
||||||
|
<CaretSortIcon className="ml-2h-4 w-4 shrink-0 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="p-0 w-45">
|
||||||
|
<Command shouldFilter={false}>
|
||||||
|
<CommandInput
|
||||||
|
placeholder={t("siteSearch")}
|
||||||
|
value={resourcePolicysearchQuery}
|
||||||
|
onValueChange={
|
||||||
|
setResourcePolicySearchQuery
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>
|
||||||
|
{t("siteNotFound")}
|
||||||
|
</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{policiesList.map((policy) => (
|
||||||
|
<CommandItem
|
||||||
|
key={
|
||||||
|
policy.resourcePolicyId
|
||||||
|
}
|
||||||
|
value={policy.resourcePolicyId.toString()}
|
||||||
|
onSelect={() =>
|
||||||
|
setSelectedPolicy({
|
||||||
|
id: policy.resourcePolicyId,
|
||||||
|
name: policy.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<CheckIcon
|
||||||
|
className={cn(
|
||||||
|
"mr-2 h-4 w-4",
|
||||||
|
policy.resourcePolicyId ===
|
||||||
|
selectedPolicy?.id
|
||||||
|
? "opacity-100"
|
||||||
|
: "opacity-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
{policy.name}
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
</SettingsSectionBody>
|
</SettingsSectionBody>
|
||||||
<SettingsSectionFooter>
|
<SettingsSectionFooter className="justify-start">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
onClick={() => {
|
||||||
disabled
|
//...
|
||||||
form="policies-type-form"
|
}}
|
||||||
>
|
>
|
||||||
{t("resourceUsersRolesSubmit")}
|
{t("resourcePolicyTypeSave")}
|
||||||
</Button>
|
</Button>
|
||||||
</SettingsSectionFooter>
|
</SettingsSectionFooter>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
<ResourcePolicyProvider policy={defaultPolicy}>
|
{selectedResourceType === "inline" ? (
|
||||||
<EditPolicyForm hidePolicyNameForm />
|
<ResourcePolicyProvider policy={defaultPolicy}>
|
||||||
</ResourcePolicyProvider>
|
<EditPolicyForm hidePolicyNameForm />
|
||||||
|
</ResourcePolicyProvider>
|
||||||
|
) : (
|
||||||
|
sharedPolicy && (
|
||||||
|
<ResourcePolicyProvider policy={sharedPolicy}>
|
||||||
|
<EditPolicyForm readonly />
|
||||||
|
</ResourcePolicyProvider>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</SettingsContainer>
|
</SettingsContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -61,12 +61,19 @@ export function SettingsSectionBody({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SettingsSectionFooter({
|
export function SettingsSectionFooter({
|
||||||
children
|
children,
|
||||||
|
className
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col md:flex-row justify-end space-y-2 md:space-y-0 md:space-x-2 mt-auto pt-6">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col md:flex-row justify-end space-y-2 md:space-y-0 md:space-x-2 mt-auto pt-6",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import z from "zod";
|
|||||||
import { remote } from "./api";
|
import { remote } from "./api";
|
||||||
import { durationToMs } from "./durationToMs";
|
import { durationToMs } from "./durationToMs";
|
||||||
import { wait } from "./wait";
|
import { wait } from "./wait";
|
||||||
|
import type { ListResourcePoliciesResponse } from "@server/routers/resource/types";
|
||||||
|
|
||||||
export type ProductUpdate = {
|
export type ProductUpdate = {
|
||||||
link: string | null;
|
link: string | null;
|
||||||
@@ -196,6 +197,41 @@ export const orgQueries = {
|
|||||||
|
|
||||||
return res.data.data.resources;
|
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<ListResourcePoliciesResponse>
|
||||||
|
>(`/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<GetDefaultResourcePolicyResponse>
|
||||||
|
>(`/resource-policy/${resourcePolicyId}`, { signal });
|
||||||
|
|
||||||
|
return res.data.data;
|
||||||
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -325,7 +361,7 @@ export const resourceQueries = {
|
|||||||
}),
|
}),
|
||||||
defaultPolicy: ({ resourceId }: { resourceId: number }) =>
|
defaultPolicy: ({ resourceId }: { resourceId: number }) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: ["RESOURCES", resourceId, "POLICIES"] as const,
|
queryKey: ["RESOURCES", resourceId, "DEFAULT_POLICY"] as const,
|
||||||
queryFn: async ({ signal, meta }) => {
|
queryFn: async ({ signal, meta }) => {
|
||||||
const res = await meta!.api.get<
|
const res = await meta!.api.get<
|
||||||
AxiosResponse<GetDefaultResourcePolicyResponse>
|
AxiosResponse<GetDefaultResourcePolicyResponse>
|
||||||
|
|||||||
Reference in New Issue
Block a user