mirror of
https://github.com/fosrl/pangolin.git
synced 2026-09-25 23:39:06 +02:00
Fix type in SelectedSite type and filter newts only on ssh creation
This commit is contained in:
@@ -24,7 +24,7 @@ import { useActionState, useMemo, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { PrivateResourceSshFields } from "@app/components/PrivateResourceSshFields";
|
||||
import type { Selectedsite } from "@app/components/site-selector";
|
||||
import type { SelectedSite } from "@app/components/site-selector";
|
||||
import { useSaveSiteResource } from "@app/hooks/useSaveSiteResource";
|
||||
import {
|
||||
asAnyControl,
|
||||
@@ -84,7 +84,7 @@ export default function PrivateResourceSshPage() {
|
||||
setSelectedSites(first);
|
||||
form.setValue(
|
||||
"siteIds",
|
||||
first.map((s: Selectedsite) => s.siteId),
|
||||
first.map((s: SelectedSite) => s.siteId),
|
||||
{ shouldValidate: true }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
FormMessage
|
||||
} from "@app/components/ui/form";
|
||||
import { Input } from "@app/components/ui/input";
|
||||
import type { Selectedsite } from "@app/components/site-selector";
|
||||
import type { SelectedSite } from "@app/components/site-selector";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||
@@ -83,7 +83,7 @@ export default function CreatePrivateResourcePage() {
|
||||
? Number(siteIdParam)
|
||||
: null;
|
||||
|
||||
const [selectedSites, setSelectedSites] = useState<Selectedsite[]>([]);
|
||||
const [selectedSites, setSelectedSites] = useState<SelectedSite[]>([]);
|
||||
const [selectedProviders, setSelectedProviders] = useState<
|
||||
SelectedAiProvider[]
|
||||
>([]);
|
||||
@@ -124,10 +124,10 @@ export default function CreatePrivateResourcePage() {
|
||||
.then((res) => {
|
||||
const site = res.data.data;
|
||||
if (!site || site.orgId !== orgId) return;
|
||||
const selected: Selectedsite = {
|
||||
const selected: SelectedSite = {
|
||||
siteId: site.siteId,
|
||||
name: site.name,
|
||||
type: site.type as Selectedsite["type"]
|
||||
type: site.type as SelectedSite["type"]
|
||||
};
|
||||
setSelectedSites([selected]);
|
||||
form.setValue("siteIds", [site.siteId]);
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
import { BrowserGatewayTargetForm } from "@app/components/BrowserGatewayTargetForm";
|
||||
import {
|
||||
SitesSelector,
|
||||
type Selectedsite
|
||||
type SelectedSite
|
||||
} from "@app/components/site-selector";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import {
|
||||
@@ -253,7 +253,7 @@ export default function Page() {
|
||||
"site" | "remote"
|
||||
>("site");
|
||||
const [nativeSelectedSite, setNativeSelectedSite] =
|
||||
useState<Selectedsite | null>(null);
|
||||
useState<SelectedSite | null>(null);
|
||||
const [nativeSiteOpen, setNativeSiteOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -1226,6 +1226,7 @@ export default function Page() {
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
|
||||
<SitesSelector
|
||||
filterTypes={["newt"]}
|
||||
orgId={
|
||||
orgId as string
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
MultiSitesSelector,
|
||||
formatMultiSitesSelectorLabel
|
||||
} from "./multi-site-selector";
|
||||
import { SitesSelector, type Selectedsite } from "./site-selector";
|
||||
import { SitesSelector, type SelectedSite } from "./site-selector";
|
||||
import { Button } from "./ui/button";
|
||||
import {
|
||||
FormControl,
|
||||
@@ -42,8 +42,7 @@ type SingleSiteFormProps<T extends FieldValues> = BaseProps<T> & {
|
||||
};
|
||||
|
||||
export type BrowserGatewayTargetFormProps<T extends FieldValues = FieldValues> =
|
||||
| MultiSiteFormProps<T>
|
||||
| SingleSiteFormProps<T>;
|
||||
MultiSiteFormProps<T> | SingleSiteFormProps<T>;
|
||||
|
||||
export function BrowserGatewayTargetForm<T extends FieldValues>(
|
||||
props: BrowserGatewayTargetFormProps<T>
|
||||
@@ -90,7 +89,7 @@ export function BrowserGatewayTargetForm<T extends FieldValues>(
|
||||
|
||||
const showMultiSiteDisclaimer =
|
||||
props.multiSite === true &&
|
||||
((watchedSites as Selectedsite[] | undefined)?.length ?? 0) > 1;
|
||||
((watchedSites as SelectedSite[] | undefined)?.length ?? 0) > 1;
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
@@ -112,7 +111,7 @@ export function BrowserGatewayTargetForm<T extends FieldValues>(
|
||||
"aria-invalid:border-destructive aria-invalid:ring-destructive/20",
|
||||
props.multiSite === true
|
||||
? (
|
||||
field.value as Selectedsite[]
|
||||
field.value as SelectedSite[]
|
||||
)?.length === 0 &&
|
||||
"text-muted-foreground"
|
||||
: !field.value &&
|
||||
@@ -122,12 +121,12 @@ export function BrowserGatewayTargetForm<T extends FieldValues>(
|
||||
<span className="truncate">
|
||||
{props.multiSite === true
|
||||
? formatMultiSitesSelectorLabel(
|
||||
(field.value as Selectedsite[]) ??
|
||||
(field.value as SelectedSite[]) ??
|
||||
[],
|
||||
t
|
||||
)
|
||||
: ((
|
||||
field.value as Selectedsite | null
|
||||
field.value as SelectedSite | null
|
||||
)?.name ??
|
||||
t("siteSelect"))}
|
||||
</span>
|
||||
@@ -140,7 +139,7 @@ export function BrowserGatewayTargetForm<T extends FieldValues>(
|
||||
<MultiSitesSelector
|
||||
orgId={props.orgId}
|
||||
selectedSites={
|
||||
(field.value as Selectedsite[]) ??
|
||||
(field.value as SelectedSite[]) ??
|
||||
[]
|
||||
}
|
||||
onSelectionChange={field.onChange}
|
||||
@@ -150,7 +149,7 @@ export function BrowserGatewayTargetForm<T extends FieldValues>(
|
||||
<SitesSelector
|
||||
orgId={props.orgId}
|
||||
selectedSite={
|
||||
field.value as Selectedsite | null
|
||||
field.value as SelectedSite | null
|
||||
}
|
||||
onSelectSite={(site) => {
|
||||
field.onChange(site);
|
||||
@@ -179,8 +178,7 @@ export function BrowserGatewayTargetForm<T extends FieldValues>(
|
||||
onChange={field.onChange}
|
||||
value={
|
||||
(watchedDestination as
|
||||
| string
|
||||
| undefined) ?? ""
|
||||
string | undefined) ?? ""
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -205,9 +203,7 @@ export function BrowserGatewayTargetForm<T extends FieldValues>(
|
||||
onChange={field.onChange}
|
||||
value={
|
||||
(watchedDestinationPort as
|
||||
| string
|
||||
| number
|
||||
| undefined) ?? ""
|
||||
string | number | undefined) ?? ""
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
PopoverTrigger
|
||||
} from "@/components/ui/popover";
|
||||
import { SitesSelector } from "@app/components/site-selector";
|
||||
import type { Selectedsite } from "@app/components/site-selector";
|
||||
import type { SelectedSite } from "@app/components/site-selector";
|
||||
import { CaretSortIcon } from "@radix-ui/react-icons";
|
||||
import { cn } from "@app/lib/cn";
|
||||
import { SwitchInput } from "@app/components/SwitchInput";
|
||||
@@ -144,7 +144,7 @@ export function HealthCheckCredenza(props: HealthCheckCredenzaProps) {
|
||||
const t = useTranslations();
|
||||
const api = createApiClient(useEnvContext());
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedSite, setSelectedSite] = useState<Selectedsite | null>(null);
|
||||
const [selectedSite, setSelectedSite] = useState<SelectedSite | null>(null);
|
||||
|
||||
const healthCheckSchema = z
|
||||
.object({
|
||||
@@ -183,11 +183,9 @@ export function HealthCheckCredenza(props: HealthCheckCredenzaProps) {
|
||||
{ message: t("healthCheckPortInvalid") }
|
||||
),
|
||||
hcFollowRedirects: z.boolean(),
|
||||
hcHostname: z
|
||||
.string()
|
||||
.refine((val) => !/\s/.test(val), {
|
||||
message: t("healthCheckHostnameInvalid")
|
||||
}),
|
||||
hcHostname: z.string().refine((val) => !/\s/.test(val), {
|
||||
message: t("healthCheckHostnameInvalid")
|
||||
}),
|
||||
hcMode: z.string(),
|
||||
hcUnhealthyInterval: z.int().positive().min(5),
|
||||
hcTlsServerName: z.string(),
|
||||
|
||||
@@ -28,7 +28,7 @@ import { Switch } from "@app/components/ui/switch";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { createApiClient, formatAxiosError } from "@app/lib/api";
|
||||
import { Selectedsite, SitesSelector } from "@app/components/site-selector";
|
||||
import { SelectedSite, SitesSelector } from "@app/components/site-selector";
|
||||
import {
|
||||
ResourceSelector,
|
||||
SelectedResource
|
||||
@@ -59,7 +59,7 @@ type StandaloneHealthChecksTableProps = {
|
||||
healthChecks: HealthCheckRow[];
|
||||
rowCount: number;
|
||||
pagination: PaginationState;
|
||||
initialFilterSite?: Selectedsite | null;
|
||||
initialFilterSite?: SelectedSite | null;
|
||||
initialFilterResource?: SelectedResource | null;
|
||||
};
|
||||
|
||||
@@ -117,7 +117,7 @@ export default function HealthChecksTable({
|
||||
|
||||
const siteIdQ = searchParams.get("siteId");
|
||||
const siteIdNum = siteIdQ ? parseInt(siteIdQ, 10) : NaN;
|
||||
const selectedSite: Selectedsite | null = useMemo(() => {
|
||||
const selectedSite: SelectedSite | null = useMemo(() => {
|
||||
if (!siteIdQ || !Number.isInteger(siteIdNum) || siteIdNum <= 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -227,7 +227,7 @@ export default function HealthChecksTable({
|
||||
setResourceFilterOpen(false);
|
||||
};
|
||||
|
||||
const onPickSite = (site: Selectedsite) => {
|
||||
const onPickSite = (site: SelectedSite) => {
|
||||
handleFilterChange("siteId", String(site.siteId));
|
||||
setSiteFilterOpen(false);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
formatMultiSitesSelectorLabel
|
||||
} from "@app/components/multi-site-selector";
|
||||
import { SitesSelector } from "@app/components/site-selector";
|
||||
import type { Selectedsite } from "@app/components/site-selector";
|
||||
import type { SelectedSite } from "@app/components/site-selector";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import {
|
||||
FormControl,
|
||||
@@ -28,8 +28,8 @@ import { PrivateResourceMultiSiteRoutingHelp } from "@app/components/PrivateReso
|
||||
type PrivateResourceSitesFieldProps<T extends FieldValues> = {
|
||||
control: Control<T>;
|
||||
orgId: string;
|
||||
selectedSites: Selectedsite[];
|
||||
onSelectedSitesChange: (sites: Selectedsite[]) => void;
|
||||
selectedSites: SelectedSite[];
|
||||
onSelectedSitesChange: (sites: SelectedSite[]) => void;
|
||||
siteIdsFieldName?: FieldPath<T>;
|
||||
singleSite?: boolean;
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ import { tierMatrix } from "@server/lib/billing/tierMatrix";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState, type ReactNode } from "react";
|
||||
import type { Control, UseFormSetValue, UseFormWatch } from "react-hook-form";
|
||||
import type { Selectedsite } from "@app/components/site-selector";
|
||||
import type { SelectedSite } from "@app/components/site-selector";
|
||||
|
||||
type PrivateResourceSshFieldsProps = {
|
||||
control: Control<any>;
|
||||
@@ -33,8 +33,8 @@ type PrivateResourceSshFieldsProps = {
|
||||
watch: UseFormWatch<any>;
|
||||
orgId?: string;
|
||||
disabled?: boolean;
|
||||
selectedSites: Selectedsite[];
|
||||
onSelectedSitesChange: (sites: Selectedsite[]) => void;
|
||||
selectedSites: SelectedSite[];
|
||||
onSelectedSitesChange: (sites: SelectedSite[]) => void;
|
||||
labelPrefix?: "create" | "edit";
|
||||
showSshSettings?: boolean;
|
||||
layout?: "default" | "wizard";
|
||||
@@ -101,7 +101,7 @@ export function PrivateResourceSshFields({
|
||||
onSelectedSitesChange(first);
|
||||
setValue(
|
||||
"siteIds",
|
||||
first.map((s: Selectedsite) => s.siteId),
|
||||
first.map((s: SelectedSite) => s.siteId),
|
||||
{ shouldValidate: true }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
ResourceSitesStatusCell,
|
||||
type ResourceSiteRow
|
||||
} from "@app/components/ResourceSitesStatusCell";
|
||||
import { Selectedsite, SitesSelector } from "@app/components/site-selector";
|
||||
import { SelectedSite, SitesSelector } from "@app/components/site-selector";
|
||||
import { Badge } from "@app/components/ui/badge";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { ExtendedColumnDef } from "@app/components/ui/data-table";
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
ResourceSitesStatusCell,
|
||||
type ResourceSiteRow
|
||||
} from "@app/components/ResourceSitesStatusCell";
|
||||
import { Selectedsite } from "@app/components/site-selector";
|
||||
import { SelectedSite } from "@app/components/site-selector";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import { ExtendedColumnDef } from "@app/components/ui/data-table";
|
||||
import {
|
||||
@@ -105,7 +105,7 @@ type ProxyResourcesTableProps = {
|
||||
orgId: string;
|
||||
pagination: PaginationState;
|
||||
rowCount: number;
|
||||
initialFilterSite?: Selectedsite | null;
|
||||
initialFilterSite?: SelectedSite | null;
|
||||
/** Certificates prefetched on the server, keyed by full domain. */
|
||||
initialCertificates?: GetBatchedCertificateResponse;
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||
import { cn } from "@app/lib/cn";
|
||||
import { dataTableFilterPopoverContentClassName } from "@app/lib/dataTableFilterPopover";
|
||||
import { CheckIcon, Funnel } from "lucide-react";
|
||||
import { SiteOnlineStatus, type Selectedsite } from "./site-selector";
|
||||
import { SiteOnlineStatus, type SelectedSite } from "./site-selector";
|
||||
import { Button } from "./ui/button";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Badge } from "./ui/badge";
|
||||
@@ -62,7 +62,7 @@ export function SitesColumnFilterButton({
|
||||
|
||||
// always include the selected site in the list of sites shown
|
||||
const sitesShown = useMemo(() => {
|
||||
const allSites: Array<Selectedsite> = [...sites];
|
||||
const allSites: Array<SelectedSite> = [...sites];
|
||||
if (
|
||||
debouncedQuery.trim().length === 0 &&
|
||||
selectedSite &&
|
||||
|
||||
@@ -12,12 +12,12 @@ import {
|
||||
import { Checkbox } from "./ui/checkbox";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useDebounce } from "use-debounce";
|
||||
import { SiteOnlineStatus, type Selectedsite } from "./site-selector";
|
||||
import { SiteOnlineStatus, type SelectedSite } from "./site-selector";
|
||||
|
||||
export type MultiSitesSelectorProps = {
|
||||
orgId: string;
|
||||
selectedSites: Selectedsite[];
|
||||
onSelectionChange: (sites: Selectedsite[]) => void;
|
||||
selectedSites: SelectedSite[];
|
||||
onSelectionChange: (sites: SelectedSite[]) => void;
|
||||
filterTypes?: string[];
|
||||
scope?: "org" | "launcher";
|
||||
onClear?: () => void;
|
||||
@@ -25,7 +25,7 @@ export type MultiSitesSelectorProps = {
|
||||
};
|
||||
|
||||
export function formatMultiSitesSelectorLabel(
|
||||
selectedSites: Selectedsite[],
|
||||
selectedSites: SelectedSite[],
|
||||
t: (key: string, values?: { count: number }) => string
|
||||
): string {
|
||||
if (selectedSites.length === 0) {
|
||||
@@ -91,7 +91,7 @@ export function MultiSitesSelector({
|
||||
[selectedSites]
|
||||
);
|
||||
|
||||
const toggleSite = (site: Selectedsite) => {
|
||||
const toggleSite = (site: SelectedSite) => {
|
||||
if (selectedIds.has(site.siteId)) {
|
||||
onSelectionChange(
|
||||
selectedSites.filter((s) => s.siteId !== site.siteId)
|
||||
|
||||
@@ -23,13 +23,13 @@ import { useQuery } from "@tanstack/react-query";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { ChevronsUpDown, Funnel } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import type { Selectedsite } from "@app/components/site-selector";
|
||||
import type { SelectedSite } from "@app/components/site-selector";
|
||||
|
||||
type LauncherFilterPopoverProps = {
|
||||
orgId: string;
|
||||
selectedSites: Selectedsite[];
|
||||
selectedSites: SelectedSite[];
|
||||
selectedLabels: SelectedLabel[];
|
||||
onSitesChange: (sites: Selectedsite[]) => void;
|
||||
onSitesChange: (sites: SelectedSite[]) => void;
|
||||
onLabelsChange: (labels: SelectedLabel[]) => void;
|
||||
};
|
||||
|
||||
@@ -58,7 +58,7 @@ export function LauncherFilterPopover({
|
||||
})
|
||||
);
|
||||
|
||||
const resolvedSelectedSites: Selectedsite[] = useMemo(
|
||||
const resolvedSelectedSites: SelectedSite[] = useMemo(
|
||||
() =>
|
||||
selectedSites.map((selected) => {
|
||||
const found = sites.find(
|
||||
|
||||
@@ -63,7 +63,7 @@ import {
|
||||
useTransition
|
||||
} from "react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
import type { Selectedsite } from "@app/components/site-selector";
|
||||
import type { SelectedSite } from "@app/components/site-selector";
|
||||
import type { SelectedLabel } from "@app/components/labels-selector";
|
||||
import { useMediaQuery } from "@app/hooks/useMediaQuery";
|
||||
import { cn } from "@app/lib/cn";
|
||||
@@ -256,7 +256,7 @@ export default function ResourceLauncher({
|
||||
(Boolean(defaultViewOverrides.personal) ||
|
||||
(isAdmin && Boolean(defaultViewOverrides.orgWide)));
|
||||
|
||||
const selectedSites: Selectedsite[] = useMemo(
|
||||
const selectedSites: SelectedSite[] = useMemo(
|
||||
() =>
|
||||
config.siteIds.map((siteId) => ({
|
||||
siteId,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { CheckIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useDebounce } from "use-debounce";
|
||||
|
||||
export type Selectedsite = Pick<
|
||||
export type SelectedSite = Pick<
|
||||
ListSitesResponse["sites"][number],
|
||||
"name" | "siteId" | "type"
|
||||
> & {
|
||||
@@ -24,8 +24,8 @@ export type Selectedsite = Pick<
|
||||
};
|
||||
|
||||
type SiteOnlineStatusProps = {
|
||||
type: Selectedsite["type"];
|
||||
online: Selectedsite["online"];
|
||||
type: SelectedSite["type"];
|
||||
online: SelectedSite["online"];
|
||||
};
|
||||
|
||||
/** Dot-only indicator matching `SitesTable` colors (newt/wireguard only; nothing for local or missing status). */
|
||||
@@ -57,8 +57,8 @@ export function SiteOnlineStatus({ type, online }: SiteOnlineStatusProps) {
|
||||
|
||||
export type SitesSelectorProps = {
|
||||
orgId: string;
|
||||
selectedSite?: Selectedsite | null;
|
||||
onSelectSite: (selected: Selectedsite) => void;
|
||||
selectedSite?: SelectedSite | null;
|
||||
onSelectSite: (selected: SelectedSite) => void;
|
||||
filterTypes?: string[];
|
||||
};
|
||||
|
||||
@@ -82,7 +82,7 @@ export function SitesSelector({
|
||||
|
||||
// always include the selected site in the list of sites shown
|
||||
const sitesShown = useMemo(() => {
|
||||
const allSites: Array<Selectedsite> = filterTypes
|
||||
const allSites: Array<SelectedSite> = filterTypes
|
||||
? sites.filter((s) => filterTypes.includes(s.type))
|
||||
: [...sites];
|
||||
if (
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import type { Selectedsite } from "@app/components/site-selector";
|
||||
import type { SelectedSite } from "@app/components/site-selector";
|
||||
import type { SiteResourceData } from "@app/lib/privateResourceForm";
|
||||
|
||||
export function buildSelectedSitesForResource(
|
||||
resource: Pick<SiteResourceData, "siteIds" | "siteNames">
|
||||
): Selectedsite[] {
|
||||
): SelectedSite[] {
|
||||
return resource.siteIds.map((siteId, idx) => ({
|
||||
name: resource.siteNames[idx] ?? "",
|
||||
siteId,
|
||||
|
||||
Reference in New Issue
Block a user