Add DeepSeek/xAI/OpenAI-compatible sentiment providers; custom dark dropdown
Deploy / lint (push) Successful in 5s
Deploy / test (push) Successful in 32s
Deploy / deploy (push) Successful in 22s

Providers (admin-switchable, no redeploy):
- DeepSeek and any OpenAI-compatible endpoint (OpenRouter, Together,
  Groq, local Ollama) via a generic Chat Completions adapter + base_url
- xAI Grok with Live Search (search_parameters web+X, citations) —
  grounded tier alongside OpenAI and Gemini
- DeepSeek / generic compatible endpoints are ungrounded (no web
  search); UI shows an amber warning and labels each provider's grounding
- Optional env fallbacks DEEPSEEK_API_KEY / XAI_API_KEY

UI: replace native <select> (unstyleable white popup on Windows) with a
custom dark Dropdown component everywhere — sentiment provider, scanner
filters, market sort, indicators, admin universe, user role.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 12:42:04 +02:00
parent d53ed972d1
commit 126c3b3c17
16 changed files with 521 additions and 98 deletions
+10 -4
View File
@@ -1,6 +1,7 @@
import { useState } from 'react';
import { useUsers, useCreateUser, useUpdateAccess, useResetPassword } from '../../hooks/useAdmin';
import { SkeletonTable } from '../ui/Skeleton';
import { Dropdown } from '../ui/Dropdown';
import type { AdminUser } from '../../lib/types';
export function UserTable() {
@@ -53,10 +54,15 @@ export function UserTable() {
</div>
<div className="flex flex-col gap-1.5">
<label className="text-xs text-gray-400">Role</label>
<select value={newRole} onChange={(e) => setNewRole(e.target.value)} className="input-glass px-3 py-2 text-sm">
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
<Dropdown
value={newRole}
onChange={setNewRole}
className="w-32"
options={[
{ value: 'user', label: 'User' },
{ value: 'admin', label: 'Admin' },
]}
/>
</div>
<label className="flex items-center gap-2 text-sm text-gray-300 pb-1">
<input type="checkbox" checked={newAccess} onChange={(e) => setNewAccess(e.target.checked)}