feat(#91): implement Connection Manager UI for federation

Implemented comprehensive UI for managing federation connections:

Features:
- View existing federation connections grouped by status
- Initiate new connections to remote instances
- Accept/reject pending connection requests
- Disconnect active connections
- Display connection status, metadata, and capabilities
- PDA-friendly design throughout (no demanding language)

Components:
- ConnectionCard: Display individual connections with actions
- ConnectionList: Grouped list view with status sections
- InitiateConnectionDialog: Modal for connecting to new instances
- Connections page: Main management interface

Implementation:
- Full test coverage (42 tests, 100% passing)
- TypeScript strict mode compliance
- ESLint passing with no warnings
- Mock data for development (ready for backend integration)
- Proper error handling and loading states
- PDA-friendly language (calm, supportive, stress-free)

Status indicators:
- 🟢 Active (soft green)
- 🔵 Pending (soft blue)
- ⏸️ Disconnected (soft yellow)
-  Rejected (light gray)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-03 14:03:44 -06:00
parent ca4f5ec011
commit 5cf02e824b
9 changed files with 1500 additions and 0 deletions

View File

@@ -0,0 +1,245 @@
/**
* Federation API Client
* Handles federation connection management API requests
*/
import { apiGet, apiPost } from "./client";
/**
* Federation connection status
*/
export enum FederationConnectionStatus {
PENDING = "PENDING",
ACTIVE = "ACTIVE",
DISCONNECTED = "DISCONNECTED",
REJECTED = "REJECTED",
}
/**
* Federation capabilities
*/
export interface FederationCapabilities {
supportsQuery: boolean;
supportsCommand: boolean;
supportsEvent: boolean;
supportsAgentSpawn: boolean;
protocolVersion: string;
}
/**
* Connection details
*/
export interface ConnectionDetails {
id: string;
workspaceId: string;
remoteInstanceId: string;
remoteUrl: string;
remotePublicKey: string;
remoteCapabilities: FederationCapabilities;
status: FederationConnectionStatus;
metadata: Record<string, unknown>;
createdAt: string;
updatedAt: string;
connectedAt: string | null;
disconnectedAt: string | null;
}
/**
* Public instance identity
*/
export interface PublicInstanceIdentity {
id: string;
instanceId: string;
name: string;
url: string;
publicKey: string;
capabilities: FederationCapabilities;
metadata: Record<string, unknown>;
createdAt: string;
updatedAt: string;
}
/**
* Initiate connection request
*/
export interface InitiateConnectionRequest {
remoteUrl: string;
}
/**
* Accept connection request
*/
export interface AcceptConnectionRequest {
metadata?: Record<string, unknown>;
}
/**
* Reject connection request
*/
export interface RejectConnectionRequest {
reason: string;
}
/**
* Disconnect connection request
*/
export interface DisconnectConnectionRequest {
reason?: string;
}
/**
* Fetch all connections
*/
export async function fetchConnections(
status?: FederationConnectionStatus
): Promise<ConnectionDetails[]> {
const params = new URLSearchParams();
if (status) {
params.append("status", status);
}
const queryString = params.toString();
const endpoint = queryString
? `/api/v1/federation/connections?${queryString}`
: "/api/v1/federation/connections";
return apiGet<ConnectionDetails[]>(endpoint);
}
/**
* Fetch a single connection
*/
export async function fetchConnection(connectionId: string): Promise<ConnectionDetails> {
return apiGet<ConnectionDetails>(`/api/v1/federation/connections/${connectionId}`);
}
/**
* Initiate a new connection
*/
export async function initiateConnection(
request: InitiateConnectionRequest
): Promise<ConnectionDetails> {
return apiPost<ConnectionDetails>("/api/v1/federation/connections/initiate", request);
}
/**
* Accept a pending connection
*/
export async function acceptConnection(
connectionId: string,
request?: AcceptConnectionRequest
): Promise<ConnectionDetails> {
return apiPost<ConnectionDetails>(
`/api/v1/federation/connections/${connectionId}/accept`,
request ?? {}
);
}
/**
* Reject a pending connection
*/
export async function rejectConnection(
connectionId: string,
request: RejectConnectionRequest
): Promise<ConnectionDetails> {
return apiPost<ConnectionDetails>(
`/api/v1/federation/connections/${connectionId}/reject`,
request
);
}
/**
* Disconnect an active connection
*/
export async function disconnectConnection(
connectionId: string,
request?: DisconnectConnectionRequest
): Promise<ConnectionDetails> {
return apiPost<ConnectionDetails>(
`/api/v1/federation/connections/${connectionId}/disconnect`,
request ?? {}
);
}
/**
* Get this instance's public identity
*/
export async function fetchInstanceIdentity(): Promise<PublicInstanceIdentity> {
return apiGet<PublicInstanceIdentity>("/api/v1/federation/instance");
}
/**
* Mock connections for development
*/
export const mockConnections: ConnectionDetails[] = [
{
id: "conn-1",
workspaceId: "workspace-1",
remoteInstanceId: "instance-work-001",
remoteUrl: "https://mosaic.work.example.com",
remotePublicKey: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
remoteCapabilities: {
supportsQuery: true,
supportsCommand: true,
supportsEvent: true,
supportsAgentSpawn: true,
protocolVersion: "1.0",
},
status: FederationConnectionStatus.ACTIVE,
metadata: {
name: "Work Instance",
description: "Corporate Mosaic instance",
},
createdAt: new Date("2026-02-01").toISOString(),
updatedAt: new Date("2026-02-01").toISOString(),
connectedAt: new Date("2026-02-01").toISOString(),
disconnectedAt: null,
},
{
id: "conn-2",
workspaceId: "workspace-1",
remoteInstanceId: "instance-partner-001",
remoteUrl: "https://mosaic.partner.example.com",
remotePublicKey: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
remoteCapabilities: {
supportsQuery: true,
supportsCommand: false,
supportsEvent: true,
supportsAgentSpawn: false,
protocolVersion: "1.0",
},
status: FederationConnectionStatus.PENDING,
metadata: {
name: "Partner Instance",
description: "Shared project collaboration",
},
createdAt: new Date("2026-02-02").toISOString(),
updatedAt: new Date("2026-02-02").toISOString(),
connectedAt: null,
disconnectedAt: null,
},
{
id: "conn-3",
workspaceId: "workspace-1",
remoteInstanceId: "instance-old-001",
remoteUrl: "https://mosaic.old.example.com",
remotePublicKey: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
remoteCapabilities: {
supportsQuery: true,
supportsCommand: true,
supportsEvent: false,
supportsAgentSpawn: false,
protocolVersion: "1.0",
},
status: FederationConnectionStatus.DISCONNECTED,
metadata: {
name: "Previous Instance",
description: "No longer in use",
},
createdAt: new Date("2026-01-15").toISOString(),
updatedAt: new Date("2026-01-30").toISOString(),
connectedAt: new Date("2026-01-15").toISOString(),
disconnectedAt: new Date("2026-01-30").toISOString(),
},
];