All page types are there and look mostly correct

This commit is contained in:
Owen
2026-05-22 17:37:37 -07:00
parent 7c54df7ed1
commit 4c1e1daf07
11 changed files with 767 additions and 225 deletions

View File

@@ -27,6 +27,7 @@ type MultiSiteProps = {
export type BrowserGatewayTargetFormProps = {
orgId: string;
destination: string;
defaultPort: number;
destinationPort: string;
onDestinationChange: (v: string) => void;
onDestinationPortChange: (v: string) => void;
@@ -115,7 +116,7 @@ export function BrowserGatewayTargetForm(props: BrowserGatewayTargetFormProps) {
<label className="text-sm font-semibold">{t("port")}</label>
<Input
type="number"
placeholder="22"
placeholder={props.defaultPort.toString()}
value={props.destinationPort}
onChange={(e) =>
props.onDestinationPortChange(e.target.value)
@@ -123,7 +124,7 @@ export function BrowserGatewayTargetForm(props: BrowserGatewayTargetFormProps) {
/>
</div>
</div>
{props.multiSite && (
{props.multiSite === true && props.selectedSites.length > 1 && (
<p className="text-sm text-muted-foreground">
{t("bgTargetMultiSiteDisclaimer")}{" "}
<a

View File

@@ -4,11 +4,9 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import {
ShieldCheck,
ShieldOff,
Eye,
EyeOff,
CheckCircle2,
XCircle,
Clock
XCircle
} from "lucide-react";
import { useResourceContext } from "@app/hooks/useResourceContext";
import CopyToClipboard from "@app/components/CopyToClipboard";
@@ -32,19 +30,40 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
const fullUrl = `${resource.ssl ? "https" : "http"}://${toUnicode(resource.fullDomain || "")}`;
const showCertificate = !!(
resource.http &&
resource.domainId &&
resource.fullDomain &&
build != "oss"
);
const showType = !!(resource.http && resource.browserAccessType);
const showHealth =
!["ssh", "rdp", "vnc"].includes(resource.browserAccessType || "") &&
!!resource.health &&
resource.health !== "unknown";
const showVisibility = !resource.enabled;
const numSections = [
true, // URL or Protocol
true, // Authentication or Port
showType,
showCertificate,
showHealth,
showVisibility
].filter(Boolean).length;
return (
<Alert>
<AlertDescription>
{/* 4 cols because of the certs */}
<InfoSections cols={resource.http && build != "oss" ? 6 : 5}>
<InfoSection>
<InfoSections cols={numSections}>
{/* <InfoSection>
<InfoSectionTitle>{t("identifier")}</InfoSectionTitle>
<InfoSectionContent>
<span className="inline-flex items-center">
{resource.niceId}
</span>
</InfoSectionContent>
</InfoSection>
</InfoSection> */}
{resource.http ? (
<>
<InfoSection>
@@ -62,6 +81,18 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
)}
</InfoSectionContent>
</InfoSection>
{showType && (
<InfoSection>
<InfoSectionTitle>
{t("type")}
</InfoSectionTitle>
<InfoSectionContent>
<span className="inline-flex items-center">
{resource.browserAccessType!.toUpperCase()}
</span>
</InfoSectionContent>
</InfoSection>
)}
<InfoSection>
<InfoSectionTitle>
{t("authentication")}
@@ -84,24 +115,6 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
)}
</InfoSectionContent>
</InfoSection>
{/* {isEnabled && (
<InfoSection>
<InfoSectionTitle>Socket</InfoSectionTitle>
<InfoSectionContent>
{isAvailable ? (
<span className="flex items-center space-x-2">
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
<span>Online</span>
</span>
) : (
<span className="flex items-center space-x-2">
<div className="w-2 h-2 bg-neutral-500 rounded-full"></div>
<span>Offline</span>
</span>
)}
</InfoSectionContent>
</InfoSection>
)} */}
</>
) : (
<>
@@ -149,74 +162,69 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) {
{/* </InfoSectionContent> */}
{/* </InfoSection> */}
{/* Certificate Status Column */}
{resource.http &&
resource.domainId &&
resource.fullDomain &&
build != "oss" && (
<InfoSection>
<InfoSectionTitle>
{t("certificateStatus", {
defaultValue: "Certificate"
})}
</InfoSectionTitle>
<InfoSectionContent>
<CertificateStatus
orgId={resource.orgId}
domainId={resource.domainId}
fullDomain={resource.fullDomain}
autoFetch={true}
showLabel={false}
polling={true}
/>
</InfoSectionContent>
</InfoSection>
)}
<InfoSection>
<InfoSectionTitle>{t("health")}</InfoSectionTitle>
<InfoSectionContent>
{resource.health === "healthy" && (
<div className="flex items-center space-x-2">
<CheckCircle2 className="w-4 h-4 flex-shrink-0 text-green-500" />
<span>{t("resourcesTableHealthy")}</span>
</div>
)}
{resource.health === "degraded" && (
<div className="flex items-center space-x-2">
<CheckCircle2 className="w-4 h-4 flex-shrink-0 text-yellow-500" />
<span>{t("resourcesTableDegraded")}</span>
</div>
)}
{resource.health === "unhealthy" && (
<div className="flex items-center space-x-2">
<XCircle className="w-4 h-4 flex-shrink-0 text-destructive" />
<span>{t("resourcesTableUnhealthy")}</span>
</div>
)}
{(!resource.health ||
resource.health === "unknown") && (
<div className="flex items-center space-x-2">
<Clock className="w-4 h-4 flex-shrink-0" />
<span>{t("resourcesTableUnknown")}</span>
</div>
)}
</InfoSectionContent>
</InfoSection>
<InfoSection>
<InfoSectionTitle>{t("visibility")}</InfoSectionTitle>
<InfoSectionContent>
{resource.enabled ? (
<div className="flex items-center space-x-2">
<Eye className="w-4 h-4 flex-shrink-0 text-green-500" />
<span>{t("enabled")}</span>
</div>
) : (
{showCertificate && (
<InfoSection>
<InfoSectionTitle>
{t("certificateStatus", {
defaultValue: "Certificate"
})}
</InfoSectionTitle>
<InfoSectionContent>
<CertificateStatus
orgId={resource.orgId}
domainId={resource.domainId!}
fullDomain={resource.fullDomain!}
autoFetch={true}
showLabel={false}
polling={true}
/>
</InfoSectionContent>
</InfoSection>
)}
{showHealth && (
<InfoSection>
<InfoSectionTitle>{t("health")}</InfoSectionTitle>
<InfoSectionContent>
{resource.health === "healthy" && (
<div className="flex items-center space-x-2">
<CheckCircle2 className="w-4 h-4 flex-shrink-0 text-green-500" />
<span>
{t("resourcesTableHealthy")}
</span>
</div>
)}
{resource.health === "degraded" && (
<div className="flex items-center space-x-2">
<CheckCircle2 className="w-4 h-4 flex-shrink-0 text-yellow-500" />
<span>
{t("resourcesTableDegraded")}
</span>
</div>
)}
{resource.health === "unhealthy" && (
<div className="flex items-center space-x-2">
<XCircle className="w-4 h-4 flex-shrink-0 text-destructive" />
<span>
{t("resourcesTableUnhealthy")}
</span>
</div>
)}
</InfoSectionContent>
</InfoSection>
)}
{showVisibility && (
<InfoSection>
<InfoSectionTitle>
{t("visibility")}
</InfoSectionTitle>
<InfoSectionContent>
<div className="flex items-center space-x-2">
<EyeOff className="w-4 h-4 flex-shrink-0 text-neutral-500" />
<span>{t("disabled")}</span>
</div>
)}
</InfoSectionContent>
</InfoSection>
</InfoSectionContent>
</InfoSection>
)}
</InfoSections>
</AlertDescription>
</Alert>

View File

@@ -22,13 +22,24 @@ export function SettingsSectionHeader({
export function SettingsSectionForm({
children,
className
className,
variant = "compact"
}: {
children: React.ReactNode;
variant?: "half" | "compact";
className?: string;
}) {
return (
<div className={cn("max-w-xl space-y-4", className)}>{children}</div>
<div
className={cn(
variant === "half"
? "max-w-3xl space-y-4"
: "max-w-xl space-y-4",
className
)}
>
{children}
</div>
);
}