fix: Implement Phase 2 major fixes

Database:
- Add missing indexes for api_key_hash, status, metrics queries
- New migration: 005_add_missing_indexes.sql

Server:
- Fix WebSocket Ping/Pong protocol (RFC 6455 compliance)
- Use separate channel for Pong responses

Agent:
- Replace format!() path construction with PathBuf::join()
- Replace todo!() macros with proper errors for macOS support

Dashboard:
- Fix duplicate filter values in Agents page (__unassigned__ sentinel)
- Add onError handlers to all mutations in Agents, Clients, Sites pages

All changes reviewed and approved.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-20 21:23:36 -07:00
parent 65086f4407
commit b298a8aa17
7 changed files with 127 additions and 26 deletions

View File

@@ -101,6 +101,10 @@ export function Agents() {
queryClient.invalidateQueries({ queryKey: ["agents"] });
setDeleteConfirm(null);
},
onError: (error: Error) => {
console.error("Failed to delete agent:", error);
alert(`Failed to delete agent: ${error.message}`);
},
});
const moveMutation = useMutation({
@@ -111,6 +115,10 @@ export function Agents() {
queryClient.invalidateQueries({ queryKey: ["sites"] });
setMovingAgent(null);
},
onError: (error: Error) => {
console.error("Failed to move agent:", error);
alert(`Failed to move agent: ${error.message}`);
},
});
// Get unique clients from agents
@@ -124,7 +132,17 @@ export function Agents() {
(agent.client_name && agent.client_name.toLowerCase().includes(search.toLowerCase())) ||
(agent.site_name && agent.site_name.toLowerCase().includes(search.toLowerCase()));
const matchesClient = !filterClient || agent.client_name === filterClient;
// Handle special __unassigned__ filter value
let matchesClient: boolean;
if (filterClient === "__unassigned__") {
// Show only agents without a site/client assignment
matchesClient = !agent.site_id;
} else if (filterClient) {
matchesClient = agent.client_name === filterClient;
} else {
matchesClient = true;
}
const matchesSite = !filterSite || agent.site_id === filterSite;
return matchesSearch && matchesClient && matchesSite;
@@ -173,7 +191,7 @@ export function Agents() {
className="px-3 py-2 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] text-sm"
>
<option value="">All Clients</option>
<option value="">-- Unassigned --</option>
<option value="__unassigned__">Unassigned Only</option>
{clients.map((client) => (
<option key={client} value={client || ""}>
{client}

View File

@@ -101,6 +101,10 @@ export function Clients() {
queryClient.invalidateQueries({ queryKey: ["clients"] });
setShowModal(false);
},
onError: (error: Error) => {
console.error("Failed to create client:", error);
alert(`Failed to create client: ${error.message}`);
},
});
const updateMutation = useMutation({
@@ -111,6 +115,10 @@ export function Clients() {
setShowModal(false);
setEditingClient(undefined);
},
onError: (error: Error) => {
console.error("Failed to update client:", error);
alert(`Failed to update client: ${error.message}`);
},
});
const deleteMutation = useMutation({
@@ -119,6 +127,10 @@ export function Clients() {
queryClient.invalidateQueries({ queryKey: ["clients"] });
setDeleteConfirm(null);
},
onError: (error: Error) => {
console.error("Failed to delete client:", error);
alert(`Failed to delete client: ${error.message}`);
},
});
const handleSave = (data: ClientFormData) => {

View File

@@ -195,6 +195,10 @@ export function Sites() {
const data = response.data as CreateSiteResponse;
setNewSiteApiKey({ apiKey: data.api_key, siteCode: data.site.site_code });
},
onError: (error: Error) => {
console.error("Failed to create site:", error);
alert(`Failed to create site: ${error.message}`);
},
});
const updateMutation = useMutation({
@@ -205,6 +209,10 @@ export function Sites() {
setShowModal(false);
setEditingSite(undefined);
},
onError: (error: Error) => {
console.error("Failed to update site:", error);
alert(`Failed to update site: ${error.message}`);
},
});
const deleteMutation = useMutation({
@@ -214,6 +222,10 @@ export function Sites() {
queryClient.invalidateQueries({ queryKey: ["clients"] });
setDeleteConfirm(null);
},
onError: (error: Error) => {
console.error("Failed to delete site:", error);
alert(`Failed to delete site: ${error.message}`);
},
});
const regenerateKeyMutation = useMutation({
@@ -226,6 +238,11 @@ export function Sites() {
});
setRegeneratingKey(null);
},
onError: (error: Error) => {
console.error("Failed to regenerate API key:", error);
alert(`Failed to regenerate API key: ${error.message}`);
setRegeneratingKey(null);
},
});
const handleSave = (data: SiteFormData) => {