diff --git a/apps/Backend/src/routes/ai-settings.ts b/apps/Backend/src/routes/ai-settings.ts index 9dce8c5b..948e5d91 100644 --- a/apps/Backend/src/routes/ai-settings.ts +++ b/apps/Backend/src/routes/ai-settings.ts @@ -1,5 +1,6 @@ import express, { Request, Response } from "express"; import fs from "fs"; +import os from "os"; import path from "path"; import { storage } from "../storage"; import { classifyInternalChat } from "../ai/internal-chat-graph"; @@ -14,6 +15,24 @@ function getChatHistoryPath(userId: number): string { const router = express.Router(); +// GET /api/ai/network-info — local LAN IP address(es) of this server +router.get("/network-info", async (req: Request, res: Response): Promise => { + try { + const interfaces = os.networkInterfaces(); + const addresses: string[] = []; + for (const name of Object.keys(interfaces)) { + for (const iface of interfaces[name] ?? []) { + if (iface.family === "IPv4" && !iface.internal) { + addresses.push(iface.address); + } + } + } + return res.status(200).json({ ipAddress: addresses[0] ?? null, addresses }); + } catch (err) { + return res.status(500).json({ error: "Failed to determine network info", details: String(err) }); + } +}); + // GET /api/ai/settings router.get("/settings", async (req: Request, res: Response): Promise => { try { diff --git a/apps/Frontend/src/App.tsx b/apps/Frontend/src/App.tsx index ddb34c16..f7a0be72 100755 --- a/apps/Frontend/src/App.tsx +++ b/apps/Frontend/src/App.tsx @@ -30,7 +30,9 @@ const CloudStoragePage = lazy(() => import("./pages/cloud-storage-page")); const JobMonitorPage = lazy(() => import("./pages/job-monitor-page")); const ChartPage = lazy(() => import("./pages/chart-page")); const DentalEducationPage = lazy(() => import("./pages/dental-education-page")); -const AiInputAgentPage = lazy(() => import("./pages/ai-input-agent-page")); +const AiCopyAgentPage = lazy(() => import("./pages/ai-copy-agent-page")); +const AiTypeAgentPage = lazy(() => import("./pages/ai-type-agent-page")); +const AiAgentSettingsPage = lazy(() => import("./pages/ai-agent-settings-page")); const DentalShoppingSearchTagPage = lazy(() => import("./pages/dental-shopping-search-tag-page")); const DentalShoppingLoginInfoPage = lazy(() => import("./pages/dental-shopping-login-info-page")); const ActivationPage = lazy(() => import("./pages/activation-page")); @@ -67,7 +69,10 @@ function Router() { } /> } /> } /> - } /> + } /> + } /> + } /> + } /> } /> } /> } adminOnly /> diff --git a/apps/Frontend/src/components/layout/sidebar.tsx b/apps/Frontend/src/components/layout/sidebar.tsx index 2ff8d3a4..507a2812 100755 --- a/apps/Frontend/src/components/layout/sidebar.tsx +++ b/apps/Frontend/src/components/layout/sidebar.tsx @@ -35,6 +35,8 @@ import { ShoppingCart, Search, KeyRound, + Copy, + Keyboard, } from "lucide-react"; import { cn } from "@/lib/utils"; import { useMemo, useState, useEffect } from "react"; @@ -68,6 +70,7 @@ export function Sidebar() { if (location.startsWith("/chart")) s.add("/chart"); if (location.startsWith("/settings")) s.add("/settings"); if (location.startsWith("/dental-shopping")) s.add("/dental-shopping"); + if (location.startsWith("/ai-input-agent")) s.add("/ai-input-agent"); return s; }); @@ -81,6 +84,9 @@ export function Sidebar() { if (location.startsWith("/dental-shopping")) { setExpandedPaths((prev) => new Set([...prev, "/dental-shopping"])); } + if (location.startsWith("/ai-input-agent")) { + setExpandedPaths((prev) => new Set([...prev, "/ai-input-agent"])); + } }, [location]); const togglePath = (path: string) => { @@ -177,9 +183,26 @@ export function Sidebar() { icon: , }, { - name: "AI Input Agent", + name: "AI Copy/Type Agent", path: "/ai-input-agent", icon: , + children: [ + { + name: "Copy Agent", + path: "/ai-input-agent/copy-agent", + icon: , + }, + { + name: "Type Agent", + path: "/ai-input-agent/type-agent", + icon: , + }, + { + name: "Settings", + path: "/ai-input-agent/settings", + icon: , + }, + ], }, { name: "AI Dental Shopping", diff --git a/apps/Frontend/src/pages/ai-agent-settings-page.tsx b/apps/Frontend/src/pages/ai-agent-settings-page.tsx new file mode 100644 index 00000000..ca637c85 --- /dev/null +++ b/apps/Frontend/src/pages/ai-agent-settings-page.tsx @@ -0,0 +1,63 @@ +import { useQuery } from "@tanstack/react-query"; +import { apiRequest } from "@/lib/queryClient"; +import { Settings as SettingsIcon, Network, Loader2 } from "lucide-react"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; + +interface NetworkInfo { + ipAddress: string | null; + addresses: string[]; +} + +export default function AiAgentSettingsPage() { + const { data: networkInfo, isLoading } = useQuery({ + queryKey: ["/api/ai/network-info"], + queryFn: async () => { + const res = await apiRequest("GET", "/api/ai/network-info"); + return res.json(); + }, + }); + + return ( +
+
+
+ +
+

Copy/Type Agent Settings

+

+ Configuration options for the Copy Agent and Type Agent will appear here. +

+ + Coming Soon + +
+ + + + + + IP Address + + + + {isLoading ? ( +
+ + Detecting IP address... +
+ ) : networkInfo?.ipAddress ? ( +

+ {networkInfo.ipAddress} +

+ ) : ( +

Unable to detect an IP address.

+ )} +

+ This is the app's current network address. It changes depending on which office + the app is running in. +

+
+
+
+ ); +} diff --git a/apps/Frontend/src/pages/ai-copy-agent-page.tsx b/apps/Frontend/src/pages/ai-copy-agent-page.tsx new file mode 100644 index 00000000..064860da --- /dev/null +++ b/apps/Frontend/src/pages/ai-copy-agent-page.tsx @@ -0,0 +1,81 @@ +import { Copy, FileCheck, CreditCard, Shield, Zap } from "lucide-react"; +import { Card, CardContent } from "@/components/ui/card"; + +const CAPABILITIES = [ + { + icon: , + title: "Eligibility Results", + description: + "Automatically read insurance eligibility information — coverage status, plan details, deductibles, and co-pays — from the insurance portal.", + }, + { + icon: , + title: "Claim Information", + description: + "Extract claim numbers, claim status updates, and denial reasons from the insurance portal so they can be reused elsewhere without manual re-entry.", + }, + { + icon: , + title: "Insurance Payments", + description: + "Capture ERA / EOB payment details — amounts, adjustments, patient responsibility — straight from the source document or portal.", + }, +]; + +export default function AiCopyAgentPage() { + return ( +
+ + {/* Hero */} +
+
+ +
+

Copy Agent

+

+ An AI agent reads and copies data including images and brings them to this app + automatically. No manual input required. +

+ + Coming Soon + +
+ + {/* What it will copy */} +
+

What it will copy

+
+ {CAPABILITIES.map((cap) => ( + + +
+ {cap.icon} + {cap.title} +
+

+ {cap.description} +

+
+
+ ))} +
+
+ + {/* Why */} + + + +
+

Why this matters

+

+ Staff currently check eligibility or look up a claim on an insurance portal, then + manually copy every value into this app. This agent eliminates that step entirely — + one click pulls the data straight in. +

+
+
+
+ +
+ ); +} diff --git a/apps/Frontend/src/pages/ai-input-agent-page.tsx b/apps/Frontend/src/pages/ai-type-agent-page.tsx similarity index 95% rename from apps/Frontend/src/pages/ai-input-agent-page.tsx rename to apps/Frontend/src/pages/ai-type-agent-page.tsx index ed3edfd6..30580fe3 100644 --- a/apps/Frontend/src/pages/ai-input-agent-page.tsx +++ b/apps/Frontend/src/pages/ai-type-agent-page.tsx @@ -1,4 +1,4 @@ -import { Bot, Keyboard, FileCheck, CreditCard, Shield, Zap, ArrowRight } from "lucide-react"; +import { Keyboard, FileCheck, CreditCard, Shield, Zap, ArrowRight } from "lucide-react"; import { Card, CardContent } from "@/components/ui/card"; const SOFTWARE_TARGETS = [ @@ -29,16 +29,16 @@ const CAPABILITIES = [ }, ]; -export default function AiInputAgentPage() { +export default function AiTypeAgentPage() { return (
{/* Hero */}
- +
-

AI Input Agent

+

Type Agent

An AI-powered agent that reads data from this app and types it directly into your current dental management software — no copy-paste, no manual re-entry.