mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-22 21:06:37 +00:00
Format
This commit is contained in:
@@ -66,4 +66,3 @@ export const ClientDownloadBanner = () => {
|
||||
};
|
||||
|
||||
export default ClientDownloadBanner;
|
||||
|
||||
|
||||
@@ -99,14 +99,12 @@ export default function ClientResourcesTable({
|
||||
siteId: number
|
||||
) => {
|
||||
try {
|
||||
await api
|
||||
.delete(`/site-resource/${resourceId}`)
|
||||
.then(() => {
|
||||
startTransition(() => {
|
||||
router.refresh();
|
||||
setIsDeleteModalOpen(false);
|
||||
});
|
||||
await api.delete(`/site-resource/${resourceId}`).then(() => {
|
||||
startTransition(() => {
|
||||
router.refresh();
|
||||
setIsDeleteModalOpen(false);
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(t("resourceErrorDelete"), e);
|
||||
toast({
|
||||
|
||||
@@ -87,7 +87,12 @@ const isValidPortRangeString = (val: string | undefined | null): boolean => {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (startPort < 1 || startPort > 65535 || endPort < 1 || endPort > 65535) {
|
||||
if (
|
||||
startPort < 1 ||
|
||||
startPort > 65535 ||
|
||||
endPort < 1 ||
|
||||
endPort > 65535
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -131,7 +136,10 @@ const getPortModeFromString = (val: string | undefined | null): PortMode => {
|
||||
};
|
||||
|
||||
// Helper to get the port string for API from mode and custom value
|
||||
const getPortStringFromMode = (mode: PortMode, customValue: string): string | undefined => {
|
||||
const getPortStringFromMode = (
|
||||
mode: PortMode,
|
||||
customValue: string
|
||||
): string | undefined => {
|
||||
if (mode === "all") return "*";
|
||||
if (mode === "blocked") return "";
|
||||
return customValue;
|
||||
@@ -1097,8 +1105,7 @@ export default function CreateInternalResourceDialog({
|
||||
size="sm"
|
||||
tags={
|
||||
form.getValues()
|
||||
.roles ||
|
||||
[]
|
||||
.roles || []
|
||||
}
|
||||
setTags={(
|
||||
newRoles
|
||||
@@ -1154,8 +1161,7 @@ export default function CreateInternalResourceDialog({
|
||||
)}
|
||||
tags={
|
||||
form.getValues()
|
||||
.users ||
|
||||
[]
|
||||
.users || []
|
||||
}
|
||||
size="sm"
|
||||
setTags={(
|
||||
@@ -1245,9 +1251,7 @@ export default function CreateInternalResourceDialog({
|
||||
restrictTagsToAutocompleteOptions={
|
||||
true
|
||||
}
|
||||
sortTags={
|
||||
true
|
||||
}
|
||||
sortTags={true}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
|
||||
@@ -95,4 +95,3 @@ export const DismissableBanner = ({
|
||||
};
|
||||
|
||||
export default DismissableBanner;
|
||||
|
||||
|
||||
@@ -501,13 +501,19 @@ export default function EditInternalResourceDialog({
|
||||
// ]);
|
||||
|
||||
await queryClient.invalidateQueries(
|
||||
resourceQueries.siteResourceRoles({ siteResourceId: resource.id })
|
||||
resourceQueries.siteResourceRoles({
|
||||
siteResourceId: resource.id
|
||||
})
|
||||
);
|
||||
await queryClient.invalidateQueries(
|
||||
resourceQueries.siteResourceUsers({ siteResourceId: resource.id })
|
||||
resourceQueries.siteResourceUsers({
|
||||
siteResourceId: resource.id
|
||||
})
|
||||
);
|
||||
await queryClient.invalidateQueries(
|
||||
resourceQueries.siteResourceClients({ siteResourceId: resource.id })
|
||||
resourceQueries.siteResourceClients({
|
||||
siteResourceId: resource.id
|
||||
})
|
||||
);
|
||||
|
||||
toast({
|
||||
|
||||
@@ -330,7 +330,7 @@ export default function ExitNodesTable({
|
||||
isRefreshing={isRefreshing}
|
||||
columnVisibility={{
|
||||
type: false,
|
||||
address: false,
|
||||
address: false
|
||||
}}
|
||||
enableColumnVisibility={true}
|
||||
/>
|
||||
|
||||
@@ -116,10 +116,12 @@ export function LayoutSidebar({
|
||||
isCollapsed={isSidebarCollapsed}
|
||||
/>
|
||||
</div>
|
||||
<div className={cn(
|
||||
"w-full border-b border-border",
|
||||
isSidebarCollapsed && "mb-2"
|
||||
)} />
|
||||
<div
|
||||
className={cn(
|
||||
"w-full border-b border-border",
|
||||
isSidebarCollapsed && "mb-2"
|
||||
)}
|
||||
/>
|
||||
<div className="flex-1 overflow-y-auto relative">
|
||||
<div className="px-2 pt-1">
|
||||
{!isAdminPage && user.serverAdmin && (
|
||||
|
||||
@@ -120,7 +120,9 @@ export default function LoginForm({
|
||||
const focusInput = () => {
|
||||
// Try using the ref first
|
||||
if (otpContainerRef.current) {
|
||||
const hiddenInput = otpContainerRef.current.querySelector('input') as HTMLInputElement;
|
||||
const hiddenInput = otpContainerRef.current.querySelector(
|
||||
"input"
|
||||
) as HTMLInputElement;
|
||||
if (hiddenInput) {
|
||||
hiddenInput.focus();
|
||||
return;
|
||||
@@ -128,17 +130,23 @@ export default function LoginForm({
|
||||
}
|
||||
|
||||
// Fallback: query the DOM
|
||||
const otpContainer = document.querySelector('[data-slot="input-otp"]');
|
||||
const otpContainer = document.querySelector(
|
||||
'[data-slot="input-otp"]'
|
||||
);
|
||||
if (!otpContainer) return;
|
||||
|
||||
const hiddenInput = otpContainer.querySelector('input') as HTMLInputElement;
|
||||
const hiddenInput = otpContainer.querySelector(
|
||||
"input"
|
||||
) as HTMLInputElement;
|
||||
if (hiddenInput) {
|
||||
hiddenInput.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// Last resort: click the first slot
|
||||
const firstSlot = otpContainer.querySelector('[data-slot="input-otp-slot"]') as HTMLElement;
|
||||
const firstSlot = otpContainer.querySelector(
|
||||
'[data-slot="input-otp-slot"]'
|
||||
) as HTMLElement;
|
||||
if (firstSlot) {
|
||||
firstSlot.click();
|
||||
}
|
||||
@@ -508,7 +516,10 @@ export default function LoginForm({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<div ref={otpContainerRef} className="flex justify-center">
|
||||
<div
|
||||
ref={otpContainerRef}
|
||||
className="flex justify-center"
|
||||
>
|
||||
<InputOTP
|
||||
maxLength={6}
|
||||
{...field}
|
||||
|
||||
@@ -11,9 +11,7 @@ type MachineClientsBannerProps = {
|
||||
orgId: string;
|
||||
};
|
||||
|
||||
export const MachineClientsBanner = ({
|
||||
orgId
|
||||
}: MachineClientsBannerProps) => {
|
||||
export const MachineClientsBanner = ({ orgId }: MachineClientsBannerProps) => {
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
@@ -57,4 +55,3 @@ export const MachineClientsBanner = ({
|
||||
};
|
||||
|
||||
export default MachineClientsBanner;
|
||||
|
||||
|
||||
@@ -39,4 +39,3 @@ export default function OrgInfoCard({}: OrgInfoCardProps) {
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -119,4 +119,3 @@ export default async function OrgLoginPage({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,4 +51,3 @@ export const PrivateResourcesBanner = ({
|
||||
};
|
||||
|
||||
export default PrivateResourcesBanner;
|
||||
|
||||
|
||||
@@ -81,10 +81,10 @@ export default function ProductUpdates({
|
||||
|
||||
const showNewVersionPopup = Boolean(
|
||||
latestVersion &&
|
||||
valid(latestVersion) &&
|
||||
valid(currentVersion) &&
|
||||
ignoredVersionUpdate !== latestVersion &&
|
||||
gt(latestVersion, currentVersion)
|
||||
valid(latestVersion) &&
|
||||
valid(currentVersion) &&
|
||||
ignoredVersionUpdate !== latestVersion &&
|
||||
gt(latestVersion, currentVersion)
|
||||
);
|
||||
|
||||
const filteredUpdates = data.updates.filter(
|
||||
|
||||
@@ -20,4 +20,3 @@ export const ProxyResourcesBanner = () => {
|
||||
};
|
||||
|
||||
export default ProxyResourcesBanner;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import {Button} from "@app/components/ui/button";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@@ -9,12 +9,12 @@ import {
|
||||
FormLabel,
|
||||
FormMessage
|
||||
} from "@app/components/ui/form";
|
||||
import {Input} from "@app/components/ui/input";
|
||||
import {toast} from "@app/hooks/useToast";
|
||||
import {zodResolver} from "@hookform/resolvers/zod";
|
||||
import {useEffect, useState} from "react";
|
||||
import {useForm} from "react-hook-form";
|
||||
import {z} from "zod";
|
||||
import { Input } from "@app/components/ui/input";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
Credenza,
|
||||
CredenzaBody,
|
||||
@@ -25,14 +25,14 @@ import {
|
||||
CredenzaHeader,
|
||||
CredenzaTitle
|
||||
} from "@app/components/Credenza";
|
||||
import {formatAxiosError} from "@app/lib/api";
|
||||
import {AxiosResponse} from "axios";
|
||||
import {Resource} from "@server/db";
|
||||
import {createApiClient} from "@app/lib/api";
|
||||
import {useEnvContext} from "@app/hooks/useEnvContext";
|
||||
import {useTranslations} from "next-intl";
|
||||
import {SwitchInput} from "@/components/SwitchInput";
|
||||
import {InfoPopup} from "@/components/ui/info-popup";
|
||||
import { formatAxiosError } from "@app/lib/api";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { Resource } from "@server/db";
|
||||
import { createApiClient } from "@app/lib/api";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { SwitchInput } from "@/components/SwitchInput";
|
||||
import { InfoPopup } from "@/components/ui/info-popup";
|
||||
|
||||
const setHeaderAuthFormSchema = z.object({
|
||||
user: z.string().min(4).max(100),
|
||||
@@ -56,11 +56,11 @@ type SetHeaderAuthFormProps = {
|
||||
};
|
||||
|
||||
export default function SetResourceHeaderAuthForm({
|
||||
open,
|
||||
setOpen,
|
||||
resourceId,
|
||||
onSetHeaderAuth
|
||||
}: SetHeaderAuthFormProps) {
|
||||
open,
|
||||
setOpen,
|
||||
resourceId,
|
||||
onSetHeaderAuth
|
||||
}: SetHeaderAuthFormProps) {
|
||||
const api = createApiClient(useEnvContext());
|
||||
const t = useTranslations();
|
||||
|
||||
@@ -82,11 +82,14 @@ export default function SetResourceHeaderAuthForm({
|
||||
async function onSubmit(data: SetHeaderAuthFormValues) {
|
||||
setLoading(true);
|
||||
|
||||
api.post<AxiosResponse<Resource>>(`/resource/${resourceId}/header-auth`, {
|
||||
user: data.user,
|
||||
password: data.password,
|
||||
extendedCompatibility: data.extendedCompatibility
|
||||
})
|
||||
api.post<AxiosResponse<Resource>>(
|
||||
`/resource/${resourceId}/header-auth`,
|
||||
{
|
||||
user: data.user,
|
||||
password: data.password,
|
||||
extendedCompatibility: data.extendedCompatibility
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
toast({
|
||||
title: t("resourceHeaderAuthSetup"),
|
||||
@@ -100,10 +103,10 @@ export default function SetResourceHeaderAuthForm({
|
||||
.catch((e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t('resourceErrorHeaderAuthSetup'),
|
||||
title: t("resourceErrorHeaderAuthSetup"),
|
||||
description: formatAxiosError(
|
||||
e,
|
||||
t('resourceErrorHeaderAuthSetupDescription')
|
||||
t("resourceErrorHeaderAuthSetupDescription")
|
||||
)
|
||||
});
|
||||
})
|
||||
@@ -139,7 +142,7 @@ export default function SetResourceHeaderAuthForm({
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="user"
|
||||
render={({field}) => (
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("user")}</FormLabel>
|
||||
<FormControl>
|
||||
@@ -149,14 +152,14 @@ export default function SetResourceHeaderAuthForm({
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="password"
|
||||
render={({field}) => (
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("password")}
|
||||
@@ -168,25 +171,31 @@ export default function SetResourceHeaderAuthForm({
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="extendedCompatibility"
|
||||
render={({field}) => (
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<SwitchInput
|
||||
id="header-auth-compatibility-toggle"
|
||||
label={t("headerAuthCompatibility")}
|
||||
info={t('headerAuthCompatibilityInfo')}
|
||||
label={t(
|
||||
"headerAuthCompatibility"
|
||||
)}
|
||||
info={t(
|
||||
"headerAuthCompatibilityInfo"
|
||||
)}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
onCheckedChange={
|
||||
field.onChange
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -91,10 +91,10 @@ export default function SetResourcePasswordForm({
|
||||
.catch((e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t('resourceErrorPasswordSetup'),
|
||||
title: t("resourceErrorPasswordSetup"),
|
||||
description: formatAxiosError(
|
||||
e,
|
||||
t('resourceErrorPasswordSetupDescription')
|
||||
t("resourceErrorPasswordSetupDescription")
|
||||
)
|
||||
});
|
||||
})
|
||||
|
||||
@@ -97,10 +97,10 @@ export default function SetResourcePincodeForm({
|
||||
.catch((e) => {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t('resourceErrorPincodeSetup'),
|
||||
title: t("resourceErrorPincodeSetup"),
|
||||
description: formatAxiosError(
|
||||
e,
|
||||
t('resourceErrorPincodeSetupDescription')
|
||||
t("resourceErrorPincodeSetupDescription")
|
||||
)
|
||||
});
|
||||
})
|
||||
|
||||
@@ -37,4 +37,3 @@ export const SitesBanner = () => {
|
||||
};
|
||||
|
||||
export default SitesBanner;
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import React from "react";
|
||||
import {Switch} from "./ui/switch";
|
||||
import {Label} from "./ui/label";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {Info} from "lucide-react";
|
||||
import {info} from "winston";
|
||||
import {Popover, PopoverContent, PopoverTrigger} from "@/components/ui/popover";
|
||||
import { Switch } from "./ui/switch";
|
||||
import { Label } from "./ui/label";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Info } from "lucide-react";
|
||||
import { info } from "winston";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger
|
||||
} from "@/components/ui/popover";
|
||||
|
||||
interface SwitchComponentProps {
|
||||
id: string;
|
||||
@@ -18,22 +22,22 @@ interface SwitchComponentProps {
|
||||
}
|
||||
|
||||
export function SwitchInput({
|
||||
id,
|
||||
label,
|
||||
description,
|
||||
info,
|
||||
disabled,
|
||||
checked,
|
||||
defaultChecked = false,
|
||||
onCheckedChange
|
||||
}: SwitchComponentProps) {
|
||||
id,
|
||||
label,
|
||||
description,
|
||||
info,
|
||||
disabled,
|
||||
checked,
|
||||
defaultChecked = false,
|
||||
onCheckedChange
|
||||
}: SwitchComponentProps) {
|
||||
const defaultTrigger = (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-6 w-6 rounded-full p-0"
|
||||
>
|
||||
<Info className="h-4 w-4"/>
|
||||
<Info className="h-4 w-4" />
|
||||
<span className="sr-only">Show info</span>
|
||||
</Button>
|
||||
);
|
||||
@@ -49,18 +53,20 @@ export function SwitchInput({
|
||||
disabled={disabled}
|
||||
/>
|
||||
{label && <Label htmlFor={id}>{label}</Label>}
|
||||
{info && <Popover>
|
||||
<PopoverTrigger asChild>
|
||||
{defaultTrigger}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-80">
|
||||
{info && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{info}
|
||||
</p>
|
||||
)}
|
||||
</PopoverContent>
|
||||
</Popover>}
|
||||
{info && (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
{defaultTrigger}
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-80">
|
||||
{info && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{info}
|
||||
</p>
|
||||
)}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
{description && (
|
||||
<span className="text-muted-foreground text-sm">
|
||||
|
||||
@@ -276,14 +276,15 @@ function AuthPageSettings({
|
||||
<>
|
||||
<SettingsSection>
|
||||
<SettingsSectionHeader>
|
||||
<SettingsSectionTitle>{t("customDomain")}</SettingsSectionTitle>
|
||||
<SettingsSectionTitle>
|
||||
{t("customDomain")}
|
||||
</SettingsSectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
{t("authPageDescription")}
|
||||
</SettingsSectionDescription>
|
||||
</SettingsSectionHeader>
|
||||
<SettingsSectionBody>
|
||||
<SettingsSectionForm>
|
||||
|
||||
<PaidFeaturesAlert />
|
||||
|
||||
<Form {...form}>
|
||||
|
||||
@@ -58,12 +58,7 @@ export default function IdpLoginButtons({
|
||||
|
||||
let redirectToUrl: string | undefined;
|
||||
try {
|
||||
console.log(
|
||||
"generating",
|
||||
idpId,
|
||||
redirect || "/",
|
||||
orgId
|
||||
);
|
||||
console.log("generating", idpId, redirect || "/", orgId);
|
||||
const safeRedirect = cleanRedirect(redirect || "/");
|
||||
const response = await generateOidcUrlProxy(
|
||||
idpId,
|
||||
|
||||
@@ -288,7 +288,10 @@ export function DataTable<TData, TValue>({
|
||||
useEffect(() => {
|
||||
if (persistPageSize && pagination.pageSize !== pageSize) {
|
||||
// Only store if user has actually changed it from initial value
|
||||
if (hasUserChangedPageSize.current && pagination.pageSize !== initialPageSize.current) {
|
||||
if (
|
||||
hasUserChangedPageSize.current &&
|
||||
pagination.pageSize !== initialPageSize.current
|
||||
) {
|
||||
setStoredPageSize(pagination.pageSize, tableId);
|
||||
}
|
||||
setPageSize(pagination.pageSize);
|
||||
@@ -298,7 +301,9 @@ export function DataTable<TData, TValue>({
|
||||
useEffect(() => {
|
||||
// Persist column visibility to localStorage when it changes (but not on initial mount)
|
||||
if (shouldPersistColumnVisibility) {
|
||||
const hasChanged = JSON.stringify(columnVisibility) !== JSON.stringify(initialColumnVisibilityState.current);
|
||||
const hasChanged =
|
||||
JSON.stringify(columnVisibility) !==
|
||||
JSON.stringify(initialColumnVisibilityState.current);
|
||||
if (hasChanged) {
|
||||
// Mark as user-initiated change and persist
|
||||
hasUserChangedColumnVisibility.current = true;
|
||||
|
||||
Reference in New Issue
Block a user