mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-03 08:18:23 +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' }),
|
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
||||||
model: z.string().optional(),
|
model: z.string().optional(),
|
||||||
account: z.string().optional(),
|
account: z.string().optional(),
|
||||||
|
target: z.enum(['claude', 'droid']).default('claude'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const compositeSchema = z.object({
|
const compositeSchema = z.object({
|
||||||
@@ -34,6 +35,7 @@ const compositeSchema = z.object({
|
|||||||
.min(1, 'Name is required')
|
.min(1, 'Name is required')
|
||||||
.regex(/^[a-zA-Z][a-zA-Z0-9._-]*$/, 'Invalid variant name'),
|
.regex(/^[a-zA-Z][a-zA-Z0-9._-]*$/, 'Invalid variant name'),
|
||||||
default_tier: z.enum(['opus', 'sonnet', 'haiku'], { message: 'Default tier is required' }),
|
default_tier: z.enum(['opus', 'sonnet', 'haiku'], { message: 'Default tier is required' }),
|
||||||
|
target: z.enum(['claude', 'droid']).default('claude'),
|
||||||
tiers: z.object({
|
tiers: z.object({
|
||||||
opus: z.object({
|
opus: z.object({
|
||||||
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
||||||
@@ -74,12 +76,14 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
|||||||
|
|
||||||
const singleForm = useForm<SingleProviderFormData>({
|
const singleForm = useForm<SingleProviderFormData>({
|
||||||
resolver: zodResolver(singleProviderSchema),
|
resolver: zodResolver(singleProviderSchema),
|
||||||
|
defaultValues: { target: 'claude' },
|
||||||
});
|
});
|
||||||
|
|
||||||
const compositeForm = useForm<CompositeFormData>({
|
const compositeForm = useForm<CompositeFormData>({
|
||||||
resolver: zodResolver(compositeSchema),
|
resolver: zodResolver(compositeSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
default_tier: 'opus',
|
default_tier: 'opus',
|
||||||
|
target: 'claude',
|
||||||
tiers: {
|
tiers: {
|
||||||
opus: { provider: 'gemini', model: '' },
|
opus: { provider: 'gemini', model: '' },
|
||||||
sonnet: { provider: 'gemini', model: '' },
|
sonnet: { provider: 'gemini', model: '' },
|
||||||
@@ -107,6 +111,7 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
|||||||
await createMutation.mutateAsync({
|
await createMutation.mutateAsync({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
provider: data.tiers[data.default_tier].provider,
|
provider: data.tiers[data.default_tier].provider,
|
||||||
|
target: data.target,
|
||||||
type: 'composite',
|
type: 'composite',
|
||||||
default_tier: data.default_tier,
|
default_tier: data.default_tier,
|
||||||
tiers: data.tiers,
|
tiers: data.tiers,
|
||||||
@@ -200,6 +205,18 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
|||||||
<Input id="model" {...singleForm.register('model')} placeholder="gemini-2.5-pro" />
|
<Input id="model" {...singleForm.register('model')} placeholder="gemini-2.5-pro" />
|
||||||
</div>
|
</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">
|
<div className="flex justify-end gap-2 pt-2">
|
||||||
<Button type="button" variant="outline" onClick={onClose}>
|
<Button type="button" variant="outline" onClick={onClose}>
|
||||||
Cancel
|
Cancel
|
||||||
@@ -288,6 +305,18 @@ export function CliproxyDialog({ open, onClose }: CliproxyDialogProps) {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</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">
|
<div className="flex justify-end gap-2 pt-2">
|
||||||
<Button type="button" variant="outline" onClick={onClose}>
|
<Button type="button" variant="outline" onClick={onClose}>
|
||||||
Cancel
|
Cancel
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ const singleProviderSchema = z.object({
|
|||||||
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
||||||
model: z.string().optional(),
|
model: z.string().optional(),
|
||||||
account: z.string().optional(),
|
account: z.string().optional(),
|
||||||
|
target: z.enum(['claude', 'droid']).default('claude'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const compositeSchema = z.object({
|
const compositeSchema = z.object({
|
||||||
default_tier: z.enum(['opus', 'sonnet', 'haiku'], { message: 'Default tier is required' }),
|
default_tier: z.enum(['opus', 'sonnet', 'haiku'], { message: 'Default tier is required' }),
|
||||||
|
target: z.enum(['claude', 'droid']).default('claude'),
|
||||||
tiers: z.object({
|
tiers: z.object({
|
||||||
opus: z.object({
|
opus: z.object({
|
||||||
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
provider: z.enum(CLIPROXY_PROVIDERS, { message: 'Provider is required' }),
|
||||||
@@ -81,6 +83,7 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
|||||||
});
|
});
|
||||||
compositeForm.reset({
|
compositeForm.reset({
|
||||||
default_tier: variant.default_tier,
|
default_tier: variant.default_tier,
|
||||||
|
target: variant.target || 'claude',
|
||||||
tiers: {
|
tiers: {
|
||||||
opus: mapTier(variant.tiers.opus),
|
opus: mapTier(variant.tiers.opus),
|
||||||
sonnet: mapTier(variant.tiers.sonnet),
|
sonnet: mapTier(variant.tiers.sonnet),
|
||||||
@@ -92,6 +95,7 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
|||||||
provider: variant.provider,
|
provider: variant.provider,
|
||||||
model: variant.model ?? undefined,
|
model: variant.model ?? undefined,
|
||||||
account: variant.account ?? undefined,
|
account: variant.account ?? undefined,
|
||||||
|
target: variant.target || 'claude',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [variant, isComposite, singleForm, compositeForm]);
|
}, [variant, isComposite, singleForm, compositeForm]);
|
||||||
@@ -117,6 +121,7 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
|||||||
name: variant.name,
|
name: variant.name,
|
||||||
data: {
|
data: {
|
||||||
default_tier: data.default_tier,
|
default_tier: data.default_tier,
|
||||||
|
target: data.target,
|
||||||
tiers: data.tiers,
|
tiers: data.tiers,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -202,6 +207,18 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</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">
|
<div className="flex justify-end gap-2 pt-2">
|
||||||
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
||||||
Cancel
|
Cancel
|
||||||
@@ -242,6 +259,18 @@ export function CliproxyEditDialog({ variant, open, onOpenChange }: CliproxyEdit
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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">
|
<div className="flex justify-end gap-2 pt-2">
|
||||||
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
|
||||||
Cancel
|
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',
|
accessorKey: 'settings',
|
||||||
header: 'Settings Path',
|
header: 'Settings Path',
|
||||||
|
|||||||
Reference in New Issue
Block a user