mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 08:17:11 +00:00
feat(ui): add target controls to cliproxy variants
- add default target selectors in create/edit dialogs - include target in variant create/update payloads - display target badge column in variants table
This commit is contained in:
@@ -26,6 +26,7 @@ const singleProviderSchema = z.object({
|
||||
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
||||
model: z.string().optional(),
|
||||
account: z.string().optional(),
|
||||
target: z.enum(['claude', 'droid']).default('claude'),
|
||||
});
|
||||
|
||||
const compositeSchema = z.object({
|
||||
@@ -34,6 +35,7 @@ const compositeSchema = z.object({
|
||||
.min(1, 'Name is required')
|
||||
.regex(/^[a-zA-Z][a-zA-Z0-9._-]*$/, 'Invalid variant name'),
|
||||
default_tier: z.enum(['opus', 'sonnet', 'haiku'], { message: 'Default tier is required' }),
|
||||
target: z.enum(['claude', 'droid']).default('claude'),
|
||||
tiers: z.object({
|
||||
opus: z.object({
|
||||
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
||||
@@ -74,12 +76,14 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
||||
|
||||
const singleForm = useForm<SingleProviderFormData>({
|
||||
resolver: zodResolver(singleProviderSchema),
|
||||
defaultValues: { target: 'claude' },
|
||||
});
|
||||
|
||||
const compositeForm = useForm<CompositeFormData>({
|
||||
resolver: zodResolver(compositeSchema),
|
||||
defaultValues: {
|
||||
default_tier: 'opus',
|
||||
target: 'claude',
|
||||
tiers: {
|
||||
opus: { provider: 'gemini', model: '' },
|
||||
sonnet: { provider: 'gemini', model: '' },
|
||||
@@ -107,6 +111,7 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
||||
await createMutation.mutateAsync({
|
||||
name: data.name,
|
||||
provider: data.tiers[data.default_tier].provider,
|
||||
target: data.target,
|
||||
type: 'composite',
|
||||
default_tier: data.default_tier,
|
||||
tiers: data.tiers,
|
||||
@@ -200,6 +205,18 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
||||
<Input id="model" {...singleForm.register('model')} placeholder="gemini-2.5-pro" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="target">Default Target</Label>
|
||||
<select
|
||||
id="target"
|
||||
{...singleForm.register('target')}
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
>
|
||||
<option value="claude">Claude Code</option>
|
||||
<option value="droid">Factory Droid</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<Button type="button" variant="outline" onClick={onClose}>
|
||||
Cancel
|
||||
@@ -288,6 +305,18 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="composite-target">Default Target</Label>
|
||||
<select
|
||||
id="composite-target"
|
||||
{...compositeForm.register('target')}
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
>
|
||||
<option value="claude">Claude Code</option>
|
||||
<option value="droid">Factory Droid</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<Button type="button" variant="outline" onClick={onClose}>
|
||||
Cancel
|
||||
|
||||
@@ -20,10 +20,12 @@ const singleProviderSchema = z.object({
|
||||
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
||||
model: z.string().optional(),
|
||||
account: z.string().optional(),
|
||||
target: z.enum(['claude', 'droid']).default('claude'),
|
||||
});
|
||||
|
||||
const compositeSchema = z.object({
|
||||
default_tier: z.enum(['opus', 'sonnet', 'haiku'], { message: 'Default tier is required' }),
|
||||
target: z.enum(['claude', 'droid']).default('claude'),
|
||||
tiers: z.object({
|
||||
opus: z.object({
|
||||
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
||||
@@ -81,6 +83,7 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
||||
});
|
||||
compositeForm.reset({
|
||||
default_tier: variant.default_tier,
|
||||
target: variant.target || 'claude',
|
||||
tiers: {
|
||||
opus: mapTier(variant.tiers.opus),
|
||||
sonnet: mapTier(variant.tiers.sonnet),
|
||||
@@ -92,6 +95,7 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
||||
provider: variant.provider,
|
||||
model: variant.model ?? undefined,
|
||||
account: variant.account ?? undefined,
|
||||
target: variant.target || 'claude',
|
||||
});
|
||||
}
|
||||
}, [variant, isComposite, singleForm, compositeForm]);
|
||||
@@ -117,6 +121,7 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
||||
name: variant.name,
|
||||
data: {
|
||||
default_tier: data.default_tier,
|
||||
target: data.target,
|
||||
tiers: data.tiers,
|
||||
},
|
||||
});
|
||||
@@ -202,6 +207,18 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="edit-composite-target">Default Target</Label>
|
||||
<select
|
||||
id="edit-composite-target"
|
||||
{...compositeForm.register('target')}
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
>
|
||||
<option value="claude">Claude Code</option>
|
||||
<option value="droid">Factory Droid</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
||||
Cancel
|
||||
@@ -242,6 +259,18 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="edit-target">Default Target</Label>
|
||||
<select
|
||||
id="edit-target"
|
||||
{...singleForm.register('target')}
|
||||
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
||||
>
|
||||
<option value="claude">Claude Code</option>
|
||||
<option value="droid">Factory Droid</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
||||
Cancel
|
||||
|
||||
@@ -69,6 +69,15 @@ export function CliproxyTable({ data }: CliproxyTableProps) {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'target',
|
||||
header: 'Target',
|
||||
cell: ({ row }) => (
|
||||
<Badge variant="outline" className="text-[10px] h-5 px-1.5 uppercase">
|
||||
{row.original.target || 'claude'}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: 'settings',
|
||||
header: 'Settings Path',
|
||||
|
||||
Reference in New Issue
Block a user