Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions packages/frontend/src/app/admin/roles/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { useAuth } from '@/lib/auth-context';
import { roles, connectors, tools as toolsApi, users } from '@/lib/api';
import { NavBar } from '@/components/nav-bar';
import { AppSelect } from '@/components/ui/select';
import { Footer } from '@/components/footer';

interface RoleItem {
Expand Down Expand Up @@ -54,7 +55,7 @@
const [savingTools, setSavingTools] = useState(false);

// User assignment
const [assigningUser, setAssigningUser] = useState(false);

Check warning on line 58 in packages/frontend/src/app/admin/roles/page.tsx

View workflow job for this annotation

GitHub Actions / Frontend (lint, typecheck, build)

'setAssigningUser' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 58 in packages/frontend/src/app/admin/roles/page.tsx

View workflow job for this annotation

GitHub Actions / Frontend (lint, typecheck, build)

'assigningUser' is assigned a value but never used. Allowed unused vars must match /^_/u

const loadData = async () => {
if (!token) return;
Expand Down Expand Up @@ -85,7 +86,7 @@

useEffect(() => {
loadData();
}, [token]);

Check warning on line 89 in packages/frontend/src/app/admin/roles/page.tsx

View workflow job for this annotation

GitHub Actions / Frontend (lint, typecheck, build)

React Hook useEffect has a missing dependency: 'loadData'. Either include it or remove the dependency array

const handleCreate = async () => {
if (!token || !newName.trim()) return;
Expand Down Expand Up @@ -429,16 +430,15 @@
{u.role === 'ADMIN' ? (
<span className="text-xs text-[var(--muted-foreground)]">Full access (admin)</span>
) : (
<select
<AppSelect
value={u.mcpRoleId || ''}
onChange={(e) => handleAssignRole(u.id, e.target.value || null)}
onValueChange={(v) => handleAssignRole(u.id, v || null)}
className="border border-[var(--input)] rounded px-2 py-1 text-xs bg-[var(--background)]"
>
<option value="">No restriction (full access)</option>
{roleList.map((r) => (
<option key={r.id} value={r.id}>{r.name}</option>
))}
</select>
options={[
{ value: '', label: 'No restriction (full access)' },
...roleList.map((r) => ({ value: r.id, label: r.name })),
]}
/>
)}
</td>
</tr>
Expand Down
38 changes: 16 additions & 22 deletions packages/frontend/src/app/admin/users/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { useAuth } from '@/lib/auth-context';
import { users, auth, roles } from '@/lib/api';
import { NavBar } from '@/components/nav-bar';
import { AppSelect } from '@/components/ui/select';
import { Footer } from '@/components/footer';

const ROLES = ['ADMIN', 'EDITOR', 'VIEWER'] as const;
Expand Down Expand Up @@ -42,7 +43,7 @@

useEffect(() => {
loadData();
}, [token]);

Check warning on line 46 in packages/frontend/src/app/admin/users/page.tsx

View workflow job for this annotation

GitHub Actions / Frontend (lint, typecheck, build)

React Hook useEffect has a missing dependency: 'loadData'. Either include it or remove the dependency array

const handleRoleChange = async (userId: string, newRole: string) => {
if (!token) return;
Expand Down Expand Up @@ -152,28 +153,24 @@
</div>
<div>
<label className="block text-sm font-medium mb-1">App Role</label>
<select
<AppSelect
value={inviteRole}
onChange={(e) => setInviteRole(e.target.value)}
onValueChange={setInviteRole}
className="w-full border border-[var(--input)] rounded-md px-3 py-2 text-sm bg-[var(--background)]"
>
{ROLES.map((r) => (
<option key={r} value={r}>{r}</option>
))}
</select>
options={ROLES.map((r) => ({ value: r, label: r }))}
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">MCP Tool Role</label>
<select
<AppSelect
value={inviteMcpRoleId}
onChange={(e) => setInviteMcpRoleId(e.target.value)}
onValueChange={setInviteMcpRoleId}
className="w-full border border-[var(--input)] rounded-md px-3 py-2 text-sm bg-[var(--background)]"
>
<option value="">No restriction (full access)</option>
{roleList.map((r) => (
<option key={r.id} value={r.id}>{r.name}</option>
))}
</select>
options={[
{ value: '', label: 'No restriction (full access)' },
...roleList.map((r) => ({ value: r.id, label: r.name })),
]}
/>
</div>
</div>

Expand Down Expand Up @@ -227,15 +224,12 @@
{u.id === currentUser?.id ? (
<span className="text-xs font-medium bg-[var(--muted)] px-2 py-1 rounded">{u.role}</span>
) : (
<select
<AppSelect
value={u.role}
onChange={(e) => handleRoleChange(u.id, e.target.value)}
onValueChange={(v) => handleRoleChange(u.id, v)}
className="border border-[var(--input)] rounded px-2 py-1 text-xs bg-[var(--background)]"
>
{ROLES.map((r) => (
<option key={r} value={r}>{r}</option>
))}
</select>
options={ROLES.map((r) => ({ value: r, label: r }))}
/>
)}
</td>
<td className="px-4 py-3">
Expand Down
20 changes: 11 additions & 9 deletions packages/frontend/src/app/connectors/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { Footer } from '@/components/footer';
import { ToolEditor } from '@/components/tool-editor';
import { McpAssignModal } from '@/components/mcp-assign-modal';
import { AppSelect } from '@/components/ui/select';

const IMPORT_SOURCES = [
{ id: 'openapi', label: 'OpenAPI / Swagger', placeholder: 'Paste OpenAPI JSON/YAML or enter URL...' },
Expand Down Expand Up @@ -125,7 +126,7 @@
}

fetchConnector();
}, [token, id]);

Check warning on line 129 in packages/frontend/src/app/connectors/[id]/page.tsx

View workflow job for this annotation

GitHub Actions / Frontend (lint, typecheck, build)

React Hook useEffect has missing dependencies: 'fetchConnector' and 'searchParams'. Either include them or remove the dependency array

const buildAuthConfig = () => {
// Only send authConfig if the user filled in credential fields;
Expand Down Expand Up @@ -564,17 +565,18 @@
)}
<div>
<label className="block text-sm font-medium mb-1">Authentication</label>
<select
<AppSelect
value={editAuthType}
onChange={(e) => { setEditAuthType(e.target.value); setEditAuthKey(''); setEditAuthValue(''); }}
onValueChange={(v) => { setEditAuthType(v); setEditAuthKey(''); setEditAuthValue(''); }}
className="w-full border border-[var(--input)] rounded-md px-3 py-2 text-sm bg-[var(--background)]"
>
<option value="NONE">None</option>
<option value="API_KEY">API Key</option>
<option value="BEARER_TOKEN">Bearer Token</option>
<option value="BASIC_AUTH">Basic Auth</option>
<option value="OAUTH2">OAuth 2.0</option>
</select>
options={[
{ value: 'NONE', label: 'None' },
{ value: 'API_KEY', label: 'API Key' },
{ value: 'BEARER_TOKEN', label: 'Bearer Token' },
{ value: 'BASIC_AUTH', label: 'Basic Auth' },
{ value: 'OAUTH2', label: 'OAuth 2.0' },
]}
/>
</div>
{editAuthType === 'API_KEY' && (
<div className="grid grid-cols-2 gap-4">
Expand Down
20 changes: 11 additions & 9 deletions packages/frontend/src/app/connectors/new/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { connectors } from '@/lib/api';
import { NavBar } from '@/components/nav-bar';
import { Footer } from '@/components/footer';
import { McpAssignModal } from '@/components/mcp-assign-modal';
import { AppSelect } from '@/components/ui/select';

const CONNECTOR_TYPES = [
{ id: 'REST', name: 'REST API', description: 'Connect to any REST API. Import from OpenAPI/Swagger spec or configure manually.', color: 'bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-500/10 dark:text-blue-400 dark:border-blue-500/30', iconBg: 'bg-blue-50 dark:bg-blue-500/10 text-blue-600 dark:text-blue-400' },
Expand Down Expand Up @@ -277,17 +278,18 @@ export default function NewConnectorPage() {

<div>
<label className="block text-sm font-medium mb-1">Authentication</label>
<select
<AppSelect
value={authType}
onChange={(e) => setAuthType(e.target.value)}
onValueChange={setAuthType}
className="w-full border border-[var(--input)] rounded-md px-3 py-2 text-sm bg-[var(--background)]"
>
<option value="NONE">None</option>
<option value="API_KEY">API Key</option>
<option value="BEARER_TOKEN">Bearer Token</option>
<option value="BASIC_AUTH">Basic Auth</option>
<option value="OAUTH2">OAuth 2.0</option>
</select>
options={[
{ value: 'NONE', label: 'None' },
{ value: 'API_KEY', label: 'API Key' },
{ value: 'BEARER_TOKEN', label: 'Bearer Token' },
{ value: 'BASIC_AUTH', label: 'Basic Auth' },
{ value: 'OAUTH2', label: 'OAuth 2.0' },
]}
/>
</div>

{selectedType === 'MCP' && authType === 'OAUTH2' && (
Expand Down
23 changes: 12 additions & 11 deletions packages/frontend/src/app/connectors/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { connectors } from '@/lib/api';
import { NavBar } from '@/components/nav-bar';
import { Footer } from '@/components/footer';
import * as Dialog from '@radix-ui/react-dialog';
import { AppSelect } from '@/components/ui/select';

type HealthStatus = { total: number; healthy: number; unhealthy: number; connectors: any[] } | null;

Expand Down Expand Up @@ -378,19 +379,19 @@ export default function ConnectorsPage() {
className="w-full border border-[var(--input)] rounded-md pl-9 pr-3 py-2 text-sm bg-[var(--background)]"
/>
</div>
<select
<AppSelect
value={typeFilter}
onChange={(e) => setTypeFilter(e.target.value)}
aria-label="Filter by connector type"
onValueChange={setTypeFilter}
className="border border-[var(--input)] rounded-md px-3 py-2 text-sm bg-[var(--background)]"
>
<option value="">All types</option>
<option value="REST">REST</option>
<option value="SOAP">SOAP</option>
<option value="GRAPHQL">GraphQL</option>
<option value="MCP">MCP</option>
<option value="DATABASE">Database</option>
</select>
options={[
{ value: '', label: 'All types' },
{ value: 'REST', label: 'REST' },
{ value: 'SOAP', label: 'SOAP' },
{ value: 'GRAPHQL', label: 'GraphQL' },
{ value: 'MCP', label: 'MCP' },
{ value: 'DATABASE', label: 'Database' },
]}
/>
<span className="text-sm text-[var(--muted-foreground)]">
{filtered.length} connector{filtered.length !== 1 ? 's' : ''}
</span>
Expand Down
15 changes: 0 additions & 15 deletions packages/frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,6 @@ button:not(:disabled), select, summary, [role="button"] {
cursor: pointer;
}

/* Custom chevron arrow for native select elements.
appearance-none removes the browser's built-in arrow so we can control spacing.
padding-right !important overrides Tailwind px-* utilities to make room for the arrow. */
main select {
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='2' stroke='%236b7280'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m19 9-7 7-7-7'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.5rem center;
background-size: 1.25em 1.25em;
padding-right: 2rem !important;
}

.dark main select {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='2' stroke='%239ca3af'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m19 9-7 7-7-7'/%3E%3C/svg%3E");
}
button:disabled {
cursor: not-allowed;
}
Expand Down
48 changes: 24 additions & 24 deletions packages/frontend/src/app/logs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useAuth } from '@/lib/auth-context';
import { audit, connectors as connectorsApi, mcpServers } from '@/lib/api';
import { NavBar } from '@/components/nav-bar';
import { Footer } from '@/components/footer';
import { AppSelect } from '@/components/ui/select';

const PAGE_SIZE = 50;

Expand Down Expand Up @@ -142,36 +143,35 @@ export default function LogsPage() {
className="w-full border border-[var(--input)] rounded-md pl-10 pr-3 py-2 text-sm bg-[var(--background)]"
/>
</div>
<select
<AppSelect
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
onValueChange={setStatusFilter}
className="border border-[var(--input)] rounded-md px-3 py-2 text-sm bg-[var(--background)]"
>
<option value="">All statuses</option>
<option value="SUCCESS">Success</option>
<option value="ERROR">Error</option>
<option value="TIMEOUT">Timeout</option>
</select>
<select
options={[
{ value: '', label: 'All statuses' },
{ value: 'SUCCESS', label: 'Success' },
{ value: 'ERROR', label: 'Error' },
{ value: 'TIMEOUT', label: 'Timeout' },
]}
/>
<AppSelect
value={connectorFilter}
onChange={(e) => setConnectorFilter(e.target.value)}
onValueChange={setConnectorFilter}
className="border border-[var(--input)] rounded-md px-3 py-2 text-sm bg-[var(--background)]"
>
<option value="">All connectors</option>
{connectors.map((c) => (
<option key={c.id} value={c.id}>{c.name}</option>
))}
</select>
<select
options={[
{ value: '', label: 'All connectors' },
...connectors.map((c) => ({ value: c.id, label: c.name })),
]}
/>
<AppSelect
value={mcpServerFilter}
onChange={(e) => setMcpServerFilter(e.target.value)}
onValueChange={setMcpServerFilter}
className="border border-[var(--input)] rounded-md px-3 py-2 text-sm bg-[var(--background)]"
>
<option value="">All MCP servers</option>
{servers.map((s) => (
<option key={s.id} value={s.id}>{s.name}</option>
))}
</select>
options={[
{ value: '', label: 'All MCP servers' },
...servers.map((s) => ({ value: s.id, label: s.name })),
]}
/>
<button
onClick={fetchLogs}
disabled={loading}
Expand Down
16 changes: 9 additions & 7 deletions packages/frontend/src/app/mcp-server/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useAuth } from '@/lib/auth-context';
import { mcpServers } from '@/lib/api';
import { NavBar } from '@/components/nav-bar';
import { Footer } from '@/components/footer';
import { AppSelect } from '@/components/ui/select';

export default function McpServerListPage() {
const { token } = useAuth();
Expand Down Expand Up @@ -126,15 +127,16 @@ export default function McpServerListPage() {
className="w-full border border-[var(--input)] rounded-md pl-10 pr-3 py-2 text-sm bg-[var(--background)]"
/>
</div>
<select
<AppSelect
value={statusFilter}
onChange={(e) => setStatusFilter(e.target.value)}
onValueChange={setStatusFilter}
className="border border-[var(--input)] rounded-md px-3 py-2 text-sm bg-[var(--background)]"
>
<option value="">All statuses</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
options={[
{ value: '', label: 'All statuses' },
{ value: 'active', label: 'Active' },
{ value: 'inactive', label: 'Inactive' },
]}
/>
</div>
)}

Expand Down
16 changes: 8 additions & 8 deletions packages/frontend/src/app/settings/roles/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useEffect, useState } from 'react';
import Link from 'next/link';
import { useAuth } from '@/lib/auth-context';
import { roles, connectors, tools as toolsApi, users } from '@/lib/api';
import { AppSelect } from '@/components/ui/select';

interface RoleItem {
id: string;
Expand Down Expand Up @@ -531,16 +532,15 @@ export default function SettingsRolesPage() {
{u.role === 'ADMIN' ? (
<span className="text-xs text-[var(--muted-foreground)]">Full access (admin)</span>
) : (
<select
<AppSelect
value={u.mcpRoleId || ''}
onChange={(e) => handleAssignRole(u.id, e.target.value || null)}
onValueChange={(v) => handleAssignRole(u.id, v || null)}
className="border border-[var(--input)] rounded px-2 py-1 text-xs bg-[var(--background)]"
>
<option value="">No restriction (full access)</option>
{roleList.map((r) => (
<option key={r.id} value={r.id}>{r.name}</option>
))}
</select>
options={[
{ value: '', label: 'No restriction (full access)' },
...roleList.map((r) => ({ value: r.id, label: r.name })),
]}
/>
)}
</td>
</tr>
Expand Down
Loading
Loading