• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacklok / codegate-ui / 13328677054

14 Feb 2025 11:56AM UTC coverage: 68.087% (+0.1%) from 67.986%
13328677054

push

github

web-flow
feat: add support muxing rules v2 (#311)

* chore: add dnd-kit, uuid and upgrade ui-kit

* feat: create SortableArea generic component

* feat: replacing zustand store with useFormState, rename filename

* feat: replace drag icon

* feat: add models dropdown

* feat: create a remove queries utility fn

* refactor: simplify logic for handling dirty and reset form state

* feat: update muxing rules

* feat: show remove button if multiple rules

* feat: add link to doc and manager providers link

* refactor: remove unneeded div wrapper

* feat: use form buttons

* leftover

* feat: change notfication message

* chore: update msw handlers

* fix: update rules and add tests case

* refactor: rename filename

* refactor: revert changes

* chore: update openapi

* fix: matcher_type for filename

* feat: set the default muxing rule as catch all

* fix: update workspace name and models

* test: update muxing model

* fix: type

* fix: drag sortable area

* fix: catch all rule spacing

* fix: fields alignment

* fix: models unique identifier

* fix: invalidate models

* test: update muxing logic

* prettier

* refactor: review

391 of 646 branches covered (60.53%)

Branch coverage included in aggregate %.

102 of 121 new or added lines in 14 files covered. (84.3%)

9 existing lines in 4 files now uncovered.

823 of 1137 relevant lines covered (72.38%)

67.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.55
/src/features/workspace/components/workspace-models-dropdown.tsx
1
import {
2
  ModelByProvider,
3
  MuxRule,
4
  V1ListAllModelsForAllProvidersResponse,
5
} from '@/api/generated'
6
import {
7
  DialogTrigger,
8
  Button,
9
  Popover,
10
  SearchField,
11
  ListBox,
12
  Input,
13
  OptionRenderer,
14
  OptionsSchema,
15
} from '@stacklok/ui-kit'
16
import { ChevronDown, SearchMd } from '@untitled-ui/icons-react'
17
import { map, groupBy } from 'lodash'
18
import { useState } from 'react'
19

20
type Props = {
21
  rule: MuxRule & { id: string }
22
  isArchived: boolean
23
  models: V1ListAllModelsForAllProvidersResponse
24
  onChange: ({
25
    model,
26
    provider_id,
27
  }: {
28
    model: string
29
    provider_id: string
30
  }) => void
31
}
32

33
function groupModelsByProviderName(
34
  models: ModelByProvider[]
35
): OptionsSchema<'listbox', string>[] {
36
  return map(groupBy(models, 'provider_name'), (items, providerName) => ({
35✔
37
    id: providerName,
38
    textValue: providerName,
39
    items: items.map((item) => ({
85✔
40
      id: `${item.provider_id}/${item.name}`,
41
      textValue: item.name,
42
    })),
43
  }))
44
}
45

46
function filterModels({
47
  groupedModels,
48
  searchItem,
49
}: {
50
  searchItem: string
51
  groupedModels: OptionsSchema<'listbox', string>[]
52
}) {
53
  const test = groupedModels
35✔
54
    .map((modelData) => {
55
      if (!searchItem) return modelData
34!
NEW
56
      const filteredModels = modelData.items?.filter((item) => {
×
NEW
57
        return item.textValue.includes(searchItem)
×
58
      })
59

NEW
60
      const data = {
×
61
        ...modelData,
62
        items: filteredModels,
63
      }
NEW
64
      return data
×
65
    })
66
    .filter((item) => (item.items ? item.items.length > 0 : false))
34!
67

68
  return test
35✔
69
}
70

71
export function WorkspaceModelsDropdown({
72
  rule,
73
  isArchived,
74
  models = [],
×
75
  onChange,
76
}: Props) {
77
  const [isOpen, setIsOpen] = useState(false)
35✔
78
  const [searchItem, setSearchItem] = useState('')
35✔
79
  const groupedModels = groupModelsByProviderName(models)
35✔
80
  const currentProvider = models.find((p) => p.provider_id === rule.provider_id)
70✔
81
  const currentModel =
82
    currentProvider && rule.model
35✔
83
      ? `${currentProvider?.provider_name}/${rule.model}`
84
      : ''
85
  const selectedKey = `${rule.provider_id}/${rule.model}`
35✔
86

87
  return (
88
    <div className="flex w-full">
89
      <DialogTrigger isOpen={isOpen} onOpenChange={(test) => setIsOpen(test)}>
3✔
90
        <Button
91
          variant="secondary"
92
          isDisabled={isArchived}
93
          data-testid="workspace-models-dropdown"
94
          className="flex w-full cursor-pointer justify-between border-gray-400 bg-gray-25
95
            font-normal shadow-none"
96
        >
97
          <span>{currentModel || 'Select a model'}</span>
63✔
98
          <ChevronDown className="shrink-0" />
99
        </Button>
100

101
        <Popover className="w-[32rem] p-3" placement="top end">
102
          <SearchField onChange={setSearchItem} autoFocus aria-label="search">
103
            <Input icon={<SearchMd />} />
104
          </SearchField>
105

106
          <ListBox
107
            aria-label="models"
108
            items={filterModels({ searchItem, groupedModels })}
109
            selectionMode="single"
110
            selectionBehavior="replace"
111
            selectedKeys={selectedKey ? [selectedKey] : []}
35!
112
            onSelectionChange={(v) => {
113
              if (v === 'all') {
3!
NEW
114
                return
×
115
              }
116
              const selectedValue = v.values().next().value
3✔
117
              if (!selectedValue && typeof selectedValue !== 'string') return
3!
118
              if (typeof selectedValue === 'string') {
3!
119
                const [provider_id, modelName] = selectedValue.split('/')
3✔
120
                if (!provider_id || !modelName) return
3!
121
                onChange({
3✔
122
                  model: modelName,
123
                  provider_id,
124
                })
125
                setIsOpen(false)
3✔
126
              }
127
            }}
128
            className="-mx-1 mt-2 max-h-72 overflow-auto"
129
            renderEmptyState={() => (
130
              <p className="text-center">No models found</p>
131
            )}
132
          >
133
            {({ items, id, textValue }) => (
134
              <OptionRenderer
135
                items={items}
136
                id={id}
137
                textValue={textValue}
138
                type="listbox"
139
              />
140
            )}
141
          </ListBox>
142
        </Popover>
143
      </DialogTrigger>
144
    </div>
145
  )
146
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc