Add login formatting

This commit is contained in:
Owen
2026-05-28 21:38:40 -07:00
parent c1ef5b4fbe
commit 9617eb2bd7
3 changed files with 323 additions and 189 deletions

View File

@@ -15,8 +15,14 @@ import type {
FileInfo
} from "@devolutions/iron-remote-desktop-rdp/dist";
import { GetBrowserTargetResponse } from "@server/routers/resource";
import { Card, CardContent } from "@app/components/ui/card";
import LoginCardHeader from "@app/components/LoginCardHeader";
import {
Card,
CardContent,
CardHeader,
CardTitle,
CardDescription
} from "@app/components/ui/card";
import Link from "next/link";
declare module "react" {
namespace JSX {
@@ -309,51 +315,87 @@ export default function RdpClient({
if (error) {
return (
<Card className="w-full">
<LoginCardHeader subtitle="RDP" />
<CardContent className="pt-6">
<p className="text-destructive text-sm">{error}</p>
</CardContent>
</Card>
<div>
<div className="text-center mb-2">
<span className="text-sm text-muted-foreground">
Powered by{" "}
<Link
href="https://pangolin.net/"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
Pangolin
</Link>
</span>
</div>
<Card className="w-full">
<CardHeader>
<CardTitle>RDP</CardTitle>
</CardHeader>
<CardContent>
<p className="text-destructive text-sm">{error}</p>
</CardContent>
</Card>
</div>
);
}
return (
<>
{showLogin && (
<Card className="w-full">
<LoginCardHeader subtitle="Connect via RDP" />
<CardContent className="pt-6">
<div className="space-y-4">
<Field label="Domain" id="domain">
<Input
id="domain"
value={form.domain}
onChange={(e) =>
update("domain", e.target.value)
}
/>
</Field>
<Field label="Username" id="username">
<Input
id="username"
value={form.username}
onChange={(e) =>
update("username", e.target.value)
}
/>
</Field>
<Field label="Password" id="password">
<Input
id="password"
type="password"
value={form.password}
onChange={(e) =>
update("password", e.target.value)
}
/>
</Field>
{/*
<div>
<div className="text-center mb-2">
<span className="text-sm text-muted-foreground">
Powered by{" "}
<Link
href="https://pangolin.net/"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
Pangolin
</Link>
</span>
</div>
<Card className="w-full">
<CardHeader>
<CardTitle>Sign in to Remote Desktop</CardTitle>
<CardDescription>
Enter Windows credentials to access xxxx
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<Field label="Domain" id="domain">
<Input
id="domain"
value={form.domain}
onChange={(e) =>
update("domain", e.target.value)
}
/>
</Field>
<Field label="Username" id="username">
<Input
id="username"
value={form.username}
onChange={(e) =>
update("username", e.target.value)
}
/>
</Field>
<Field label="Password" id="password">
<Input
id="password"
type="password"
value={form.password}
onChange={(e) =>
update("password", e.target.value)
}
/>
</Field>
{/*
<Field label="Pre Connection Blob (optional)" id="pcb">
<Input
id="pcb"
@@ -362,7 +404,7 @@ export default function RdpClient({
/>
</Field> */}
{/* <Field
{/* <Field
label="KDC Proxy URL (optional)"
id="kdcProxyUrl"
>
@@ -374,7 +416,7 @@ export default function RdpClient({
}
/>
</Field> */}
{/* <div className="flex items-center gap-2">
{/* <div className="flex items-center gap-2">
<Checkbox
id="enable_clipboard"
checked={form.enableClipboard}
@@ -386,17 +428,20 @@ export default function RdpClient({
Enable Clipboard
</Label>
</div> */}
<Button
onClick={startSession}
disabled={!moduleReady}
loading={connecting}
className="w-full"
>
{moduleReady ? "Connect" : "Loading module..."}
</Button>
</div>
</CardContent>
</Card>
<Button
onClick={startSession}
disabled={!moduleReady}
loading={connecting}
className="w-full"
>
{moduleReady
? "Connect"
: "Loading module..."}
</Button>
</div>
</CardContent>
</Card>
</div>
)}
<div

View File

@@ -8,8 +8,14 @@ import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import type { SignSshKeyResponse } from "@server/private/routers/ssh";
import { GetBrowserTargetResponse } from "@server/routers/resource";
import { Card, CardContent } from "@app/components/ui/card";
import LoginCardHeader from "@app/components/LoginCardHeader";
import {
Card,
CardContent,
CardHeader,
CardTitle,
CardDescription
} from "@app/components/ui/card";
import Link from "next/link";
type FormState = {
username: string;
@@ -300,126 +306,163 @@ export default function SshClient({
if (error) {
return (
<Card className="w-full">
<LoginCardHeader subtitle="SSH" />
<CardContent className="pt-6">
<p className="text-destructive text-sm">{error}</p>
</CardContent>
</Card>
<div>
<div className="text-center mb-2">
<span className="text-sm text-muted-foreground">
Powered by{" "}
<Link
href="https://pangolin.net/"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
Pangolin
</Link>
</span>
</div>
<Card className="w-full">
<CardHeader>
<CardTitle>SSH</CardTitle>
</CardHeader>
<CardContent>
<p className="text-destructive text-sm">{error}</p>
</CardContent>
</Card>
</div>
);
}
return (
<>
{!connected && (
<Card className="w-full">
<LoginCardHeader subtitle="Connect via SSH" />
<CardContent className="pt-6">
<div className="space-y-4">
<Field label="Username" id="username">
<Input
id="username"
value={form.username}
onChange={(e) =>
setForm({
...form,
username: e.target.value
})
}
placeholder="root"
/>
</Field>
<Field label="Password" id="password">
<Input
id="password"
type="password"
value={form.password}
onChange={(e) =>
setForm({
...form,
password: e.target.value
})
}
placeholder={
form.privateKey
? "Optional with key auth"
: ""
}
/>
</Field>
<Field
label="Private Key (optional)"
id="privateKey"
<div>
<div className="text-center mb-2">
<span className="text-sm text-muted-foreground">
Powered by{" "}
<Link
href="https://pangolin.net/"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
<Textarea
id="privateKey"
value={form.privateKey}
onChange={(e) =>
setForm({
...form,
privateKey: e.target.value
})
}
placeholder="Paste your private key here (PEM format)…"
rows={5}
className="font-mono text-xs"
/>
<div className="mt-1.5 flex items-center gap-2">
<Button
type="button"
variant="outline"
size="sm"
onClick={() =>
fileInputRef.current?.click()
Pangolin
</Link>
</span>
</div>
<Card className="w-full">
<CardHeader>
<CardTitle>Sign in to SSH</CardTitle>
<CardDescription>
Enter your credentials to access xxxx
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<Field label="Username" id="username">
<Input
id="username"
value={form.username}
onChange={(e) =>
setForm({
...form,
username: e.target.value
})
}
>
Upload key file
</Button>
{form.privateKey && (
<button
placeholder="root"
/>
</Field>
<Field label="Password" id="password">
<Input
id="password"
type="password"
value={form.password}
onChange={(e) =>
setForm({
...form,
password: e.target.value
})
}
placeholder={
form.privateKey
? "Optional with key auth"
: ""
}
/>
</Field>
<Field
label="Private Key (optional)"
id="privateKey"
>
<Textarea
id="privateKey"
value={form.privateKey}
onChange={(e) =>
setForm({
...form,
privateKey: e.target.value
})
}
placeholder="Paste your private key here (PEM format)…"
rows={5}
className="font-mono text-xs"
/>
<div className="mt-1.5 flex items-center gap-2">
<Button
type="button"
className="text-xs text-muted-foreground underline"
variant="outline"
size="sm"
onClick={() =>
setForm((prev) => ({
...prev,
privateKey: ""
}))
fileInputRef.current?.click()
}
>
Clear
</button>
)}
</div>
<input
ref={fileInputRef}
type="file"
className="hidden"
accept=".pem,.key,.pub,*"
onChange={handleKeyFile}
/>
</Field>
Upload key file
</Button>
{form.privateKey && (
<button
type="button"
className="text-xs text-muted-foreground underline"
onClick={() =>
setForm((prev) => ({
...prev,
privateKey: ""
}))
}
>
Clear
</button>
)}
</div>
<input
ref={fileInputRef}
type="file"
className="hidden"
accept=".pem,.key,.pub,*"
onChange={handleKeyFile}
/>
</Field>
{connectError && (
<p className="text-destructive text-sm">
{connectError}
</p>
)}
{connectError && (
<p className="text-destructive text-sm">
{connectError}
</p>
)}
<Button
onClick={() => connect()}
loading={connecting}
disabled={
!form.username ||
(!form.password && !form.privateKey)
}
className="w-full"
>
{connecting ? "Connecting..." : "Connect"}
</Button>
</div>
</CardContent>
</Card>
<Button
onClick={() => connect()}
loading={connecting}
disabled={
!form.username ||
(!form.password && !form.privateKey)
}
className="w-full"
>
{connecting ? "Connecting..." : "Connect"}
</Button>
</div>
</CardContent>
</Card>
</div>
)}
{connected && (

View File

@@ -6,8 +6,14 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { toast } from "@app/hooks/useToast";
import { GetBrowserTargetResponse } from "@server/routers/resource";
import { Card, CardContent } from "@app/components/ui/card";
import LoginCardHeader from "@app/components/LoginCardHeader";
import {
Card,
CardContent,
CardHeader,
CardTitle,
CardDescription
} from "@app/components/ui/card";
import Link from "next/link";
type FormState = {
password: string;
@@ -148,39 +154,79 @@ export default function VncClient({
if (error) {
return (
<Card className="w-full">
<LoginCardHeader subtitle="VNC" />
<CardContent className="pt-6">
<p className="text-destructive text-sm">{error}</p>
</CardContent>
</Card>
<div>
<div className="text-center mb-2">
<span className="text-sm text-muted-foreground">
Powered by{" "}
<Link
href="https://pangolin.net/"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
Pangolin
</Link>
</span>
</div>
<Card className="w-full">
<CardHeader>
<CardTitle>VNC</CardTitle>
</CardHeader>
<CardContent>
<p className="text-destructive text-sm">{error}</p>
</CardContent>
</Card>
</div>
);
}
return (
<>
{!connected && (
<Card className="w-full">
<LoginCardHeader subtitle="Connect via VNC" />
<CardContent className="pt-6">
<div className="space-y-4">
<Field label="Password (optional)" id="password">
<Input
<div>
<div className="text-center mb-2">
<span className="text-sm text-muted-foreground">
Powered by{" "}
<Link
href="https://pangolin.net/"
target="_blank"
rel="noopener noreferrer"
className="underline"
>
Pangolin
</Link>
</span>
</div>
<Card className="w-full">
<CardHeader>
<CardTitle>VNC</CardTitle>
<CardDescription>
Enter your credentials to connect
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<Field
label="Password (optional)"
id="password"
type="password"
value={form.password}
onChange={(e) =>
update("password", e.target.value)
}
/>
</Field>
>
<Input
id="password"
type="password"
value={form.password}
onChange={(e) =>
update("password", e.target.value)
}
/>
</Field>
<Button onClick={connect} className="w-full">
Connect
</Button>
</div>
</CardContent>
</Card>
<Button onClick={connect} className="w-full">
Connect
</Button>
</div>
</CardContent>
</Card>
</div>
)}
<div