Files
mai-bot/dashboard/src/routes/plugins/InstallDialog.tsx
DrSmoothl feb2b89917 refactor(routes): split plugins.tsx into modular plugins/ directory
- Extract types.ts: Plugin types and category name mapping
- Extract PluginCard.tsx: Single plugin card component
- Extract MarketplaceTab.tsx: All plugins marketplace view
- Extract InstalledTab.tsx: Installed plugins view
- Extract InstallDialog.tsx: Plugin installation dialog with branch selection
- Create index.tsx: Main PluginsPage with WebSocket state management
- Delete original 1244-line plugins.tsx
- Maintain full functionality, zero logic changes
- Build verified: bun run build passes with zero errors
2026-03-01 19:30:47 +08:00

147 lines
5.8 KiB
TypeScript

import { useState } from 'react'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Download } from 'lucide-react'
import type { PluginInfo } from './types'
interface InstallDialogProps {
open: boolean
plugin: PluginInfo | null
onOpenChange: (open: boolean) => void
onInstall: (branch: string) => void
}
export function InstallDialog({ open, plugin, onOpenChange, onInstall }: InstallDialogProps) {
const [selectedBranch, setSelectedBranch] = useState('main')
const [customBranch, setCustomBranch] = useState('')
const [branchInputMode, setBranchInputMode] = useState<'preset' | 'custom'>('preset')
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false)
const handleInstall = () => {
const branch = branchInputMode === 'custom' ? customBranch : selectedBranch
if (!branch || branch.trim() === '') {
return
}
onInstall(branch)
onOpenChange(false)
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>
{plugin?.manifest.name}
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
{/* 基本信息 */}
<div>
<p className="text-sm text-muted-foreground">
: {plugin?.manifest.version}
</p>
<p className="text-sm text-muted-foreground">
: {typeof plugin?.manifest.author === 'string'
? plugin.manifest.author
: plugin?.manifest.author?.name}
</p>
</div>
{/* 高级选项开关 */}
<div className="flex items-center space-x-2">
<Checkbox
id="advanced-options"
checked={showAdvancedOptions}
onCheckedChange={(checked) => setShowAdvancedOptions(checked as boolean)}
/>
<label
htmlFor="advanced-options"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
</label>
</div>
{/* 高级选项内容 */}
{showAdvancedOptions && (
<div className="space-y-4 p-4 border rounded-lg">
<div className="space-y-2">
<label className="text-sm font-medium"></label>
<Tabs value={branchInputMode} onValueChange={(value) => setBranchInputMode(value as 'preset' | 'custom')}>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="preset" className="text-xs"></TabsTrigger>
<TabsTrigger value="custom" className="text-xs"></TabsTrigger>
</TabsList>
{/* 预设分支选择 */}
{branchInputMode === 'preset' && (
<div className="mt-3">
<Select value={selectedBranch} onValueChange={setSelectedBranch}>
<SelectTrigger>
<SelectValue placeholder="选择分支" />
</SelectTrigger>
<SelectContent>
<SelectItem value="main">main ()</SelectItem>
<SelectItem value="master">master</SelectItem>
<SelectItem value="dev">dev ()</SelectItem>
<SelectItem value="develop">develop</SelectItem>
<SelectItem value="beta">beta ()</SelectItem>
<SelectItem value="stable">stable ()</SelectItem>
</SelectContent>
</Select>
</div>
)}
{/* 自定义分支输入 */}
{branchInputMode === 'custom' && (
<div className="space-y-2 mt-3">
<input
type="text"
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
placeholder="输入分支名称,例如: feature/new-feature"
value={customBranch}
onChange={(e) => setCustomBranch(e.target.value)}
/>
<p className="text-xs text-muted-foreground">
Git
</p>
</div>
)}
</Tabs>
</div>
</div>
)}
{!showAdvancedOptions && (
<p className="text-sm text-muted-foreground">
(main)
</p>
)}
</div>
<DialogFooter>
<Button
variant="outline"
onClick={() => onOpenChange(false)}
>
</Button>
<Button onClick={handleInstall}>
<Download className="h-4 w-4 mr-2" />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}