173 lines
6.1 KiB
TypeScript
173 lines
6.1 KiB
TypeScript
import { useTranslation } from 'react-i18next';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
|
import { Plus, ChevronDown, Settings2, Trash2 } from 'lucide-react';
|
|
import { cn } from '@/lib/utils';
|
|
import type { BuildNode } from '@/api';
|
|
import type { EnvVar } from './types';
|
|
|
|
export interface AdvancedSettingsPanelProps {
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
buildNodeId: string;
|
|
onBuildNodeIdChange: (v: string) => void;
|
|
buildNodes: BuildNode[];
|
|
jobName: string;
|
|
onJobNameChange: (v: string) => void;
|
|
triggerMode: string;
|
|
onTriggerModeChange: (v: string) => void;
|
|
watchBranches: string;
|
|
onWatchBranchesChange: (v: string) => void;
|
|
cronExpr: string;
|
|
onCronExprChange: (v: string) => void;
|
|
envVars: EnvVar[];
|
|
onEnvVarsChange: (vars: EnvVar[]) => void;
|
|
}
|
|
|
|
export function AdvancedSettingsPanel({
|
|
open,
|
|
onOpenChange,
|
|
buildNodeId,
|
|
onBuildNodeIdChange,
|
|
buildNodes,
|
|
jobName,
|
|
onJobNameChange,
|
|
triggerMode,
|
|
onTriggerModeChange,
|
|
watchBranches,
|
|
onWatchBranchesChange,
|
|
cronExpr,
|
|
onCronExprChange,
|
|
envVars,
|
|
onEnvVarsChange,
|
|
}: AdvancedSettingsPanelProps) {
|
|
const { t } = useTranslation();
|
|
|
|
return (
|
|
<Collapsible open={open} onOpenChange={onOpenChange}>
|
|
<CollapsibleTrigger asChild>
|
|
<button
|
|
type="button"
|
|
className="flex items-center gap-1.5 text-xs text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)]"
|
|
>
|
|
<Settings2 className="h-3.5 w-3.5" />
|
|
{t('deploy.advancedSettings')}
|
|
<ChevronDown className={cn('h-3.5 w-3.5 transition-transform', open && 'rotate-180')} />
|
|
</button>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent className="mt-3 space-y-3 pt-3 border-t border-[var(--dg-border)]">
|
|
<div className="space-y-1.5">
|
|
<Label className="text-xs">{t('deploy.buildNode')}</Label>
|
|
<Select
|
|
value={buildNodeId || '__auto'}
|
|
onValueChange={(v) => onBuildNodeIdChange(v === '__auto' ? '' : v)}
|
|
>
|
|
<SelectTrigger className="h-8 text-xs dg-input">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="__auto">{t('deploy.autoSchedule')}</SelectItem>
|
|
{buildNodes.map((n) => (
|
|
<SelectItem key={n.id} value={String(n.id)}>
|
|
{n.name} ({n.running_count || 0}/{n.max_executors})
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<Label className="text-xs">
|
|
Job 名称 <span className="text-[var(--dg-text-muted)]">(Webhook)</span>
|
|
</Label>
|
|
<Input
|
|
value={jobName}
|
|
onChange={(e) => onJobNameChange(e.target.value)}
|
|
className="h-8 text-xs font-mono dg-input"
|
|
/>
|
|
</div>
|
|
<div className="space-y-1.5">
|
|
<Label className="text-xs">{t('deploy.triggerMethod')}</Label>
|
|
<Select value={triggerMode} onValueChange={onTriggerModeChange}>
|
|
<SelectTrigger className="h-8 text-xs dg-input">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="manual">{t('deploy.triggerManual')}</SelectItem>
|
|
<SelectItem value="git_push">{t('deploy.codePush')}</SelectItem>
|
|
<SelectItem value="cron">{t('deploy.triggerCron')}</SelectItem>
|
|
<SelectItem value="webhook">Webhook</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
{triggerMode === 'git_push' && (
|
|
<Input
|
|
value={watchBranches}
|
|
onChange={(e) => onWatchBranchesChange(e.target.value)}
|
|
placeholder="监听分支,如 main, release/*"
|
|
className="h-8 text-xs font-mono dg-input"
|
|
/>
|
|
)}
|
|
{triggerMode === 'cron' && (
|
|
<Input
|
|
value={cronExpr}
|
|
onChange={(e) => onCronExprChange(e.target.value)}
|
|
placeholder="0 2 * * *"
|
|
className="h-8 text-xs font-mono dg-input"
|
|
/>
|
|
)}
|
|
<div className="space-y-1.5">
|
|
<div className="flex items-center justify-between">
|
|
<Label className="text-xs">{t('deploy.envVars')}</Label>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
className="h-6 text-[10px]"
|
|
onClick={() => onEnvVarsChange([...envVars, { key: '', value: '' }])}
|
|
>
|
|
<Plus className="h-3 w-3 mr-0.5" />
|
|
{t('deploy.addVar')}
|
|
</Button>
|
|
</div>
|
|
{envVars.map((v, i) => (
|
|
<div key={i} className="flex gap-1.5">
|
|
<Input
|
|
value={v.key}
|
|
onChange={(e) => {
|
|
const nv = [...envVars];
|
|
nv[i] = { ...nv[i], key: e.target.value };
|
|
onEnvVarsChange(nv);
|
|
}}
|
|
placeholder="KEY"
|
|
className="h-7 text-xs font-mono dg-input flex-1"
|
|
/>
|
|
<Input
|
|
value={v.value}
|
|
onChange={(e) => {
|
|
const nv = [...envVars];
|
|
nv[i] = { ...nv[i], value: e.target.value };
|
|
onEnvVarsChange(nv);
|
|
}}
|
|
placeholder="value"
|
|
className="h-7 text-xs font-mono dg-input flex-[2]"
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
className="h-7 w-7 shrink-0"
|
|
onClick={() => onEnvVarsChange(envVars.filter((_, idx) => idx !== i))}
|
|
>
|
|
<Trash2 className="h-3.5 w-3.5 text-[var(--dg-red)]" />
|
|
</Button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</CollapsibleContent>
|
|
</Collapsible>
|
|
);
|
|
}
|