feat: All service fields editable inline

Service cards now have full inline editing:
- Backend address: editable input, updates on blur
- Public/Private: toggle switch, updates immediately
- Subdomains: toggle switch, updates immediately
- TLS (Central TLS / Passthrough): toggle switch, updates
  both tls mode and backend type together
- Deregister button

Add form also uses toggle for TLS instead of dropdown.
Removed unused Badge and Select imports.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-09 23:53:20 +00:00
parent 98385449b4
commit 7c9e8e51d7

View File

@@ -5,9 +5,7 @@ import { Button } from './ui/button';
import { Input, Label } from './ui';
import { Alert, AlertDescription } from './ui/alert';
import { Textarea } from './ui/textarea';
import { Badge } from './ui/badge';
import { Switch } from './ui/switch';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from './ui/collapsible';
import {
Globe, Loader2, AlertCircle, CheckCircle, Play, RotateCw,
@@ -159,16 +157,10 @@ export function ServicesComponent() {
<Switch checked={newSubdomains} onCheckedChange={setNewSubdomains} />
Include subdomains
</label>
<div className="flex items-center gap-2 text-sm">
<span className="text-muted-foreground">TLS:</span>
<Select value={newTls} onValueChange={setNewTls}>
<SelectTrigger className="h-8 w-36"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="passthrough">Passthrough</SelectItem>
<SelectItem value="terminate">Terminate</SelectItem>
</SelectContent>
</Select>
</div>
<label className="flex items-center gap-2 text-sm">
<Switch checked={newTls === 'terminate'} onCheckedChange={v => setNewTls(v ? 'terminate' : 'passthrough')} />
Central handles TLS
</label>
</div>
<div className="flex gap-2 justify-end">
<Button variant="outline" size="sm" onClick={() => setShowAddForm(false)}><X className="h-3 w-3 mr-1" />Cancel</Button>
@@ -237,7 +229,19 @@ export function ServicesComponent() {
<div className="flex flex-wrap items-center gap-x-6 gap-y-3 py-2">
<div>
<Label className="text-xs text-muted-foreground">Backend</Label>
<div className="font-mono text-sm mt-0.5">{service.backend.address}</div>
<Input
defaultValue={service.backend.address}
className="mt-0.5 h-8 font-mono text-sm w-48"
onBlur={e => {
const val = e.target.value.trim();
if (val && val !== service.backend.address) {
updateMutation.mutate({
domain: service.domain,
updates: { backend: { ...service.backend, address: val } },
});
}
}}
/>
</div>
<div className="flex items-center gap-2">
@@ -259,10 +263,18 @@ export function ServicesComponent() {
</div>
<div className="flex items-center gap-2">
<Label className="text-xs text-muted-foreground">TLS</Label>
<Badge variant={isPassthrough ? 'secondary' : 'default'} className="text-xs">
{isPassthrough ? 'Passthrough' : 'Terminate'}
</Badge>
<Switch
checked={!isPassthrough}
onCheckedChange={v => updateMutation.mutate({
domain: service.domain,
updates: {
tls: v ? 'terminate' : 'passthrough',
backend: { ...service.backend, type: v ? 'http' : 'tcp-passthrough' },
},
})}
disabled={updateMutation.isPending}
/>
<Label className="text-sm">{isPassthrough ? 'TLS Passthrough' : 'Central TLS'}</Label>
</div>
</div>