diff --git a/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/get_connections.ts b/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/get_connections.ts new file mode 100644 index 0000000..fff9526 --- /dev/null +++ b/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/get_connections.ts @@ -0,0 +1,48 @@ +"use server"; +import { getAuthToken } from "../lib/auth"; + +export type WorkspaceConnection = { + id: string; + name: string; + connector_type: string; +}; + +export async function getWorkspaceConnections( + workspaceId: string +): Promise { + const token = await getAuthToken(); + + if (!workspaceId) { + throw new Error("Workspace ID is required"); + } + + try { + const response = await fetch( + `${process.env.GRIDWALK_API}/workspaces/${workspaceId}/connections`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + } + ); + + if (!response.ok) { + const errorText = await response.text(); + if (response.status === 401) { + throw new Error("Unauthorized to view workspace connections"); + } + if (response.status === 404) { + throw new Error("Workspace not found"); + } + throw new Error(errorText || "Failed to fetch workspace connections"); + } + + const connections: WorkspaceConnection[] = await response.json(); + return connections; + } catch (error) { + console.error("Failed to fetch workspace connections:", error); + throw error; + } +} diff --git a/gridwalk-ui/src/app/workspace/[workspaceId]/viewConnectionsModal.tsx b/gridwalk-ui/src/app/workspace/[workspaceId]/viewConnectionsModal.tsx new file mode 100644 index 0000000..abb5d2d --- /dev/null +++ b/gridwalk-ui/src/app/workspace/[workspaceId]/viewConnectionsModal.tsx @@ -0,0 +1,140 @@ +"use client"; +import React, { useEffect, useState } from "react"; +import { Database, Loader2 } from "lucide-react"; +import { + getWorkspaceConnections, + type WorkspaceConnection, +} from "./actions/workspace/get_connections"; + +interface ViewWorkspaceConnectionsModalProps { + isOpen: boolean; + onClose: () => void; + workspaceId: string; +} + +export const ViewWorkspaceConnectionsModal: React.FC< + ViewWorkspaceConnectionsModalProps +> = ({ isOpen, onClose, workspaceId }) => { + const [connections, setConnections] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + if (isOpen) { + const fetchConnections = async () => { + try { + setLoading(true); + setError(null); + const fetchedConnections = await getWorkspaceConnections(workspaceId); + setConnections(fetchedConnections); + } catch (err) { + setError( + err instanceof Error ? err.message : "Failed to fetch connections" + ); + } finally { + setLoading(false); + } + }; + + fetchConnections(); + } + }, [isOpen, workspaceId]); + + if (!isOpen) return null; + + const getConnectorTypeColor = (type: string) => { + switch (type.toLowerCase()) { + case "postgis": + return "bg-blue-100 text-blue-800"; + default: + return "bg-gray-100 text-gray-800"; + } + }; + + return ( +
+
+
+

+ Workspace Connections +

+ +
+ +
+ {loading ? ( +
+ +
+ ) : error ? ( +
{error}
+ ) : connections.length === 0 ? ( +
+ No connections found +
+ ) : ( +
+ + + + + + + + + + {connections.map((connection) => ( + + + + + + ))} + +
+ Connection ID + + Name + + Type +
+
+ + + {connection.id} + +
+
+ + {connection.name} + + + + {connection.connector_type} + +
+
+ )} +
+ +
+ +
+
+
+ ); +}; diff --git a/gridwalk-ui/src/app/workspace/[workspaceId]/workspaceProjects.tsx b/gridwalk-ui/src/app/workspace/[workspaceId]/workspaceProjects.tsx index a4ce223..9dcd016 100644 --- a/gridwalk-ui/src/app/workspace/[workspaceId]/workspaceProjects.tsx +++ b/gridwalk-ui/src/app/workspace/[workspaceId]/workspaceProjects.tsx @@ -1,4 +1,5 @@ "use client"; + import React, { useState } from "react"; import { Plus, @@ -7,6 +8,7 @@ import { ChevronDown, Trash2, HelpCircle, + Database, } from "lucide-react"; import { CreateProjectModal, DeleteProjectModal } from "./projectModal"; import { HelpSupportModal } from "../supportModal"; @@ -16,6 +18,7 @@ import { useWorkspaces } from "../workspaceContext"; import { createProject } from "./actions/projects/create"; import { deleteProject } from "./actions/projects/delete"; import { addWorkspaceMember } from "./actions/workspace"; +import { ViewWorkspaceConnectionsModal } from "./viewConnectionsModal"; import { useRouter } from "next/navigation"; interface Project { @@ -38,6 +41,7 @@ export default function WorkspaceProjectsClient({ const router = useRouter(); const { workspaces } = useWorkspaces(); const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false); + const [isConnectionDialogOpen, setIsConnectionDialogOpen] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [isHelpSupportModalOpen, setIsHelpSupportModalOpen] = useState(false); const [isMemberDialogOpen, setIsMemberDialogOpen] = useState(false); @@ -161,6 +165,13 @@ export default function WorkspaceProjectsClient({ New Project + )} @@ -237,6 +248,12 @@ export default function WorkspaceProjectsClient({ workspaceId={workspaceId} /> + setIsConnectionDialogOpen(false)} + workspaceId={workspaceId} + /> + setIsHelpSupportModalOpen(false)} diff --git a/gridwalk-ui/src/app/workspace/sidebar.tsx b/gridwalk-ui/src/app/workspace/sidebar.tsx index 631c046..092b1a6 100644 --- a/gridwalk-ui/src/app/workspace/sidebar.tsx +++ b/gridwalk-ui/src/app/workspace/sidebar.tsx @@ -82,7 +82,7 @@ const WorkspaceAccordion = ({ workspaces }: { workspaces: Workspaces }) => { onClick={() => setIsOpen(!isOpen)} className="w-full flex items-center justify-between rounded-lg bg-gray-100 dark:bg-gray-800 hover:bg-blue-500 hover:text-white dark:hover:bg-blue-800 transition-colors duration-200 focus:outline-none focus:ring-3 focus:ring-blue-500 font-medium text-sm" > - Show workspaces + View Workspaces