mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-10 04:36:38 +00:00
Small ui adjustments
This commit is contained in:
@@ -109,7 +109,12 @@ import {
|
|||||||
PathRewriteModal
|
PathRewriteModal
|
||||||
} from "@app/components/PathMatchRenameModal";
|
} from "@app/components/PathMatchRenameModal";
|
||||||
import { Badge } from "@app/components/ui/badge";
|
import { Badge } from "@app/components/ui/badge";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@app/components/ui/tooltip";
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger
|
||||||
|
} from "@app/components/ui/tooltip";
|
||||||
|
|
||||||
const addTargetSchema = z
|
const addTargetSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -517,7 +522,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
pathMatchType: null,
|
pathMatchType: null,
|
||||||
rewritePath: null,
|
rewritePath: null,
|
||||||
rewritePathType: null,
|
rewritePathType: null,
|
||||||
priority: 100,
|
priority: 100
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -695,7 +700,12 @@ export default function ReverseProxyTargets(props: {
|
|||||||
<Info className="h-4 w-4 text-muted-foreground" />
|
<Info className="h-4 w-4 text-muted-foreground" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent className="max-w-xs">
|
<TooltipContent className="max-w-xs">
|
||||||
<p>Higher priority routes are evaluated first. Priority = 100 means automatic ordering (system decides). Use another number to enforce manual priority.</p>
|
<p>
|
||||||
|
Higher priority routes are evaluated first.
|
||||||
|
Priority = 100 means automatic ordering
|
||||||
|
(system decides). Use another number to
|
||||||
|
enforce manual priority.
|
||||||
|
</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
@@ -770,8 +780,13 @@ export default function ReverseProxyTargets(props: {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{row.original.siteType === "newt" ? (
|
{row.original.siteType === "newt" ? (
|
||||||
<Button variant="outline"
|
<Button
|
||||||
className="flex items-center gap-2 p-2 max-w-md w-full text-left cursor-pointer">
|
variant="outline"
|
||||||
|
className="flex items-center gap-2 p-2 max-w-md w-full text-left cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
openHealthCheckDialog(row.original)
|
||||||
|
}
|
||||||
|
>
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
<Badge variant={getStatusColor(status)}>
|
<Badge variant={getStatusColor(status)}>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
@@ -779,22 +794,21 @@ export default function ReverseProxyTargets(props: {
|
|||||||
{getStatusText(status)}
|
{getStatusText(status)}
|
||||||
</div>
|
</div>
|
||||||
</Badge>
|
</Badge>
|
||||||
<Button
|
|
||||||
variant="text"
|
|
||||||
size="sm"
|
|
||||||
onClick={() =>
|
|
||||||
openHealthCheckDialog(row.original)
|
|
||||||
}
|
|
||||||
className="h-6 w-6 p-0"
|
|
||||||
>
|
|
||||||
<Settings className="h-4 w-4" />
|
<Settings className="h-4 w-4" />
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
disabled={true}
|
||||||
|
className="flex items-center gap-2 p-2 max-w-md w-full text-left cursor-pointer"
|
||||||
|
>
|
||||||
|
<div className="flex items-center space-x-1">
|
||||||
<Badge variant="secondary">
|
<Badge variant="secondary">
|
||||||
{t("healthCheckNotAvailable")}
|
{t("healthCheckNotAvailable")}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -865,7 +879,10 @@ export default function ReverseProxyTargets(props: {
|
|||||||
(site) => site.siteId === row.original.siteId
|
(site) => site.siteId === row.original.siteId
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleContainerSelectForTarget = (hostname: string, port?: number) => {
|
const handleContainerSelectForTarget = (
|
||||||
|
hostname: string,
|
||||||
|
port?: number
|
||||||
|
) => {
|
||||||
updateTarget(row.original.targetId, {
|
updateTarget(row.original.targetId, {
|
||||||
...row.original,
|
...row.original,
|
||||||
ip: hostname
|
ip: hostname
|
||||||
@@ -880,7 +897,10 @@ export default function ReverseProxyTargets(props: {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Button variant={"outline"} className="w-full justify-start py-0 space-x-2 px-0 hover:bg-card cursor-default">
|
<Button
|
||||||
|
variant={"outline"}
|
||||||
|
className="w-full justify-start py-0 space-x-2 px-0 hover:bg-card cursor-default"
|
||||||
|
>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
@@ -888,31 +908,46 @@ export default function ReverseProxyTargets(props: {
|
|||||||
role="combobox"
|
role="combobox"
|
||||||
className={cn(
|
className={cn(
|
||||||
"min-w-[90px] justify-between text-sm font-medium border-r pr-4 rounded-none h-8 hover:bg-transparent",
|
"min-w-[90px] justify-between text-sm font-medium border-r pr-4 rounded-none h-8 hover:bg-transparent",
|
||||||
!row.original.siteId && "text-muted-foreground"
|
!row.original.siteId &&
|
||||||
|
"text-muted-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{row.original.siteId ? selectedSite?.name : t("siteSelect")}
|
{row.original.siteId
|
||||||
|
? selectedSite?.name
|
||||||
|
: t("siteSelect")}
|
||||||
<CaretSortIcon className="ml-2h-4 w-4 shrink-0 opacity-50" />
|
<CaretSortIcon className="ml-2h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="p-0 w-[180px]">
|
<PopoverContent className="p-0 w-[180px]">
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput placeholder={t("siteSearch")} />
|
<CommandInput
|
||||||
|
placeholder={t("siteSearch")}
|
||||||
|
/>
|
||||||
<CommandList>
|
<CommandList>
|
||||||
<CommandEmpty>{t("siteNotFound")}</CommandEmpty>
|
<CommandEmpty>
|
||||||
|
{t("siteNotFound")}
|
||||||
|
</CommandEmpty>
|
||||||
<CommandGroup>
|
<CommandGroup>
|
||||||
{sites.map((site) => (
|
{sites.map((site) => (
|
||||||
<CommandItem
|
<CommandItem
|
||||||
key={site.siteId}
|
key={site.siteId}
|
||||||
value={`${site.siteId}:${site.name}`}
|
value={`${site.siteId}:${site.name}`}
|
||||||
onSelect={() =>
|
onSelect={() =>
|
||||||
updateTarget(row.original.targetId, { siteId: site.siteId })
|
updateTarget(
|
||||||
|
row.original
|
||||||
|
.targetId,
|
||||||
|
{
|
||||||
|
siteId: site.siteId
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CheckIcon
|
<CheckIcon
|
||||||
className={cn(
|
className={cn(
|
||||||
"mr-2 h-4 w-4",
|
"mr-2 h-4 w-4",
|
||||||
site.siteId === row.original.siteId
|
site.siteId ===
|
||||||
|
row.original
|
||||||
|
.siteId
|
||||||
? "opacity-100"
|
? "opacity-100"
|
||||||
: "opacity-0"
|
: "opacity-0"
|
||||||
)}
|
)}
|
||||||
@@ -935,7 +970,9 @@ export default function ReverseProxyTargets(props: {
|
|||||||
<ContainersSelector
|
<ContainersSelector
|
||||||
site={selectedSite}
|
site={selectedSite}
|
||||||
containers={dockerState.containers}
|
containers={dockerState.containers}
|
||||||
isAvailable={dockerState.isAvailable}
|
isAvailable={
|
||||||
|
dockerState.isAvailable
|
||||||
|
}
|
||||||
onContainerSelect={
|
onContainerSelect={
|
||||||
handleContainerSelectForTarget
|
handleContainerSelectForTarget
|
||||||
}
|
}
|
||||||
@@ -953,7 +990,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
onValueChange={(value) =>
|
onValueChange={(value) =>
|
||||||
updateTarget(row.original.targetId, {
|
updateTarget(row.original.targetId, {
|
||||||
...row.original,
|
...row.original,
|
||||||
method: value,
|
method: value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -977,13 +1014,16 @@ export default function ReverseProxyTargets(props: {
|
|||||||
className="min-w-[130px] border-none placeholder-gray-400"
|
className="min-w-[130px] border-none placeholder-gray-400"
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
const input = e.target.value.trim();
|
const input = e.target.value.trim();
|
||||||
const hasProtocol = /^(https?|h2c):\/\//.test(input);
|
const hasProtocol =
|
||||||
|
/^(https?|h2c):\/\//.test(input);
|
||||||
const hasPort = /:\d+(?:\/|$)/.test(input);
|
const hasPort = /:\d+(?:\/|$)/.test(input);
|
||||||
|
|
||||||
if (hasProtocol || hasPort) {
|
if (hasProtocol || hasPort) {
|
||||||
const parsed = parseHostTarget(input);
|
const parsed = parseHostTarget(input);
|
||||||
if (parsed) {
|
if (parsed) {
|
||||||
updateTarget(row.original.targetId, {
|
updateTarget(
|
||||||
|
row.original.targetId,
|
||||||
|
{
|
||||||
...row.original,
|
...row.original,
|
||||||
method: hasProtocol
|
method: hasProtocol
|
||||||
? parsed.protocol
|
? parsed.protocol
|
||||||
@@ -992,12 +1032,16 @@ export default function ReverseProxyTargets(props: {
|
|||||||
port: hasPort
|
port: hasPort
|
||||||
? parsed.port
|
? parsed.port
|
||||||
: row.original.port
|
: row.original.port
|
||||||
});
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
updateTarget(row.original.targetId, {
|
updateTarget(
|
||||||
|
row.original.targetId,
|
||||||
|
{
|
||||||
...row.original,
|
...row.original,
|
||||||
ip: input
|
ip: input
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updateTarget(row.original.targetId, {
|
updateTarget(row.original.targetId, {
|
||||||
@@ -1013,7 +1057,7 @@ export default function ReverseProxyTargets(props: {
|
|||||||
<Input
|
<Input
|
||||||
placeholder="Port"
|
placeholder="Port"
|
||||||
defaultValue={row.original.port}
|
defaultValue={row.original.port}
|
||||||
className="min-w-[60px] pl-0 border-none placeholder-gray-400"
|
className="w-[120px] pl-0 border-none placeholder-gray-400"
|
||||||
onBlur={(e) =>
|
onBlur={(e) =>
|
||||||
updateTarget(row.original.targetId, {
|
updateTarget(row.original.targetId, {
|
||||||
...row.original,
|
...row.original,
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ export function PathMatchDisplay({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2 w-full text-left">
|
<div className="flex items-center gap-2 w-full text-left">
|
||||||
<Badge variant="secondary" className="font-mono text-xs shrink-0">
|
<Badge variant="secondary" className="text-xs shrink-0">
|
||||||
{getTypeLabel(value.pathMatchType)}
|
{getTypeLabel(value.pathMatchType)}
|
||||||
</Badge>
|
</Badge>
|
||||||
<code className="text-sm flex-1 truncate" title={value.path}>
|
<code className="text-sm flex-1 truncate" title={value.path}>
|
||||||
@@ -281,7 +281,7 @@ export function PathRewriteDisplay({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2 w-full text-left">
|
<div className="flex items-center gap-2 w-full text-left">
|
||||||
<Badge variant="secondary" className="font-mono text-xs shrink-0">
|
<Badge variant="secondary" className="text-xs shrink-0">
|
||||||
{getTypeLabel(value.rewritePathType)}
|
{getTypeLabel(value.rewritePathType)}
|
||||||
</Badge>
|
</Badge>
|
||||||
<code className="text-sm flex-1 truncate" title={value.rewritePath || ""}>
|
<code className="text-sm flex-1 truncate" title={value.rewritePath || ""}>
|
||||||
|
|||||||
Reference in New Issue
Block a user