# UI Duplication Audit — v0.10.5 ✅ IMPLEMENTED Systematic catalog of every repeated pattern in the frontend. Each section: what's duplicated, where, and what's inconsistent. --- ## 1. Provider Form (CREATE + EDIT) — 3 copies The same "configure a provider" concept exists in **3 places** with different field IDs, different features, and different behaviors. ### Where | Context | Create handler | Edit handler | HTML form IDs | |---------|---------------|-------------|---------------| | **User BYOK** | `handleCreateProvider()` settings-handlers.js:88 | same fn (dual-mode via `UI._editingProviderId`) | `providerName`, `providerType`, `providerEndpoint`, `providerApiKey`, `providerDefaultModel` | | **Admin Global** | `createGlobalProvider()` admin-handlers.js:122 | same fn (dual-mode via `_editingProviderId`) | `adminProvName`, `adminProvType`, `adminProvEndpoint`, `adminProvKey`, `adminProvModel`, `adminProvPrivate` | | **Team** | inline anon listener settings-handlers.js:571 | `settingsTeamEditProviderSubmit` listener :597 | Create: `teamProviderName`, `teamProviderType`, `teamProviderEndpoint`, `teamProviderKey`, `teamProviderPrivate` / Edit: `teamProviderEditName`, `teamProviderEditEndpoint`, `teamProviderEditKey`, `teamProviderEditDefault`, `teamProviderEditPrivate` | ### Inconsistencies | Feature | User BYOK | Admin | Team Create | Team Edit | |---------|-----------|-------|-------------|-----------| | **Endpoint auto-fill on type change** | ✅ | ❌ MISSING | ✅ | ❌ N/A (no type field) | | **"Private" checkbox** | ❌ | ✅ | ✅ | ✅ | | **"Default Model" field** | ✅ | ✅ | ❌ MISSING | ✅ | | **Provider type dropdown** | Static HTML | Static HTML | Dynamic JS (same labels) | N/A (no type change) | | **Provider types defined in** | index.html:408 | index.html:626 | settings-handlers.js:553 | — | | **Endpoint defaults defined in** | settings-handlers.js:480 | NOWHERE | settings-handlers.js:542 | — | | **API key placeholder** | "sk-..." / "(unchanged)" | "sk-..." / "(unchanged)" | "sk-..." | "(unchanged)" | | **Post-save refresh** | `loadProviderList()` + `fetchModels()` | `loadAdminProviders()` (no fetchModels) | `loadTeamManageProviders()` + `fetchModels()` | `loadTeamManageProviders()` (no fetchModels) | | **Create/Edit dual-mode** | Single fn, flag | Single fn, flag | Separate forms + separate listeners | Separate forms + separate listeners | | **Form show/hide** | `UI.showProviderForm()`/`hideProviderForm()` | inline `.style.display` | inline `.style.display` | inline `.style.display` | ### Constants duplicated ``` Provider types: 3× — index.html (2 static selects) + settings-handlers.js (1 dynamic build) Provider labels: 3× — { openai: 'OpenAI-compatible', anthropic: 'Anthropic', venice: 'Venice.ai', openrouter: 'OpenRouter' } Endpoint map: 2× — settings-handlers.js:480 and :542 (admin has ZERO) ``` --- ## 2. Provider List (READ) — 3 copies | Context | Function | File:Line | API call | |---------|----------|-----------|----------| | **User BYOK** | `UI.loadProviderList()` | ui-settings.js:534 | `API.listConfigs()` | | **Admin Global** | `UI.loadAdminProviders()` | ui-admin.js:343 | `API.adminListGlobalConfigs()` | | **Team** | `UI.loadTeamManageProviders(teamId)` | ui-settings.js:191 | `API.teamListProviders(teamId)` | ### Inconsistencies | Feature | User BYOK | Admin | Team | |---------|-----------|-------|------| | Shows endpoint | ❌ | ✅ | ❌ | | Shows provider type | ✅ (in meta) | ✅ (in meta) | ✅ (in meta) | | Shows has_key indicator | ✅ 🔑/⚠️ | ❌ | ✅ 🔑 | | Shows active/inactive toggle | ❌ | ❌ | ✅ | | Shows private badge | ❌ | ✅ | ✅ | | Edit button | ✅ | ✅ | ✅ | | Delete button | ✅ | ✅ | ✅ | | Refresh models button | ✅ | ❌ (separate bulk) | ❌ | | Row CSS class | `provider-row` | `admin-provider-row` | `admin-preset-row` (!) | | Empty state msg | different | different | different | | Provider cache | ❌ | ✅ (`UI._providerCache`) | ❌ | --- ## 3. Role Configuration — 2 copies | Context | Render function | Provider change handler | Save handler | |---------|----------------|------------------------|-------------| | **Admin Global** | `UI.loadAdminRoles()` ui-admin.js:134 | `adminRoleProviderChanged()` admin-handlers.js:199 | `adminSaveRole()` admin-handlers.js:216 | | **User Personal** | `UI.loadUserRoles()` ui-settings.js:356 | `userRoleProviderChanged()` settings-handlers.js:214 | `saveUserRole()` settings-handlers.js:231 | ### Inconsistencies | Feature | Admin | User | |---------|-------|------| | Fallback slot | ✅ primary + fallback | ❌ primary only | | Test button | ✅ | ❌ | | Clear/remove button | ❌ | ✅ | | Models source | `window._adminModelList` (from admin API) | `App.models` (from enabled API) | | Model ID field | `m.model_id` | `m.baseModelId` | | Reload on save | ✅ (added in 0.10.4) | ✅ (always did) | | BYOK gate check | ❌ (admin sees always) | ✅ (hidden if no BYOK) | --- ## 4. Capability Badges — 3 copies The same badge rendering logic exists in 3 places with different levels of detail: | Context | File:Line | Badges shown | |---------|-----------|-------------| | **Model selector** (chat) | ui-core.js:759-774 | max_output, max_context, tools, vision, thinking, reasoning, code, search — with titles and labels | | **Admin model list** | ui-admin.js:375-378 | max_output, tools, vision, thinking — compact, no titles | | **User model list** | ui-settings.js:596-599 | max_output, tools, vision, thinking — compact, no titles | Admin and User are identical copies. Model selector has more detail. --- ## 5. Usage Dashboard — 3 copies | Context | Function | File:Line | API call | |---------|----------|-----------|----------| | **Admin** | `UI.loadAdminUsage()` | ui-admin.js:190 | `API.adminGetUsage()` | | **User (My Usage)** | `UI.loadMyUsage()` | ui-settings.js:246 | `API.getMyUsage()` | | **Team** | `UI.loadTeamUsage()` | ui-settings.js:297 | `API.teamGetUsage()` | ### Inconsistencies | Feature | Admin | User | Team | |---------|-------|------|------| | Pricing table | ✅ | ❌ | ❌ | | "group by user" column header | ✅ | ❌ | ✅ | | Stats card styling | default padding | `padding:8px; font-size:16px` | `padding:8px; font-size:16px` | | Stats label font | default | `font-size:10px` | `font-size:10px` | | Empty state check | `results.length === 0` | `!t.requests` | `!t.requests` | | Grid layout | default | `grid-template-columns:repeat(4,1fr)` | `grid-template-columns:repeat(4,1fr)` | | Table font size | default | `font-size:12px` | `font-size:12px` | User and Team are near-identical. Admin is a separate fork. --- ## 6. Provider Type / Endpoint Constants — scattered everywhere The "known provider types" concept is defined in **5 places**: | # | Location | Format | |---|----------|--------| | 1 | index.html:408 | `