Skip to content

Commit

Permalink
feat(frontend): implemented view connections modal [2024-12-15]
Browse files Browse the repository at this point in the history
  • Loading branch information
CHRISCARLON committed Dec 15, 2024
1 parent 57e466b commit 566e9cd
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -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<WorkspaceConnection[]> {
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;
}
}
140 changes: 140 additions & 0 deletions gridwalk-ui/src/app/workspace/[workspaceId]/viewConnectionsModal.tsx
Original file line number Diff line number Diff line change
@@ -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<WorkspaceConnection[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(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 (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-lg p-6 w-full max-w-2xl relative">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-bold text-gray-900">
Workspace Connections
</h2>
<button
onClick={onClose}
className="text-gray-500 hover:text-gray-700"
>
</button>
</div>

<div className="py-4">
{loading ? (
<div className="flex justify-center items-center py-8">
<Loader2 className="animate-spin h-6 w-6 text-gray-500" />
</div>
) : error ? (
<div className="text-red-500 text-center py-4">{error}</div>
) : connections.length === 0 ? (
<div className="text-gray-500 text-center py-4">
No connections found
</div>
) : (
<div className="overflow-hidden rounded-lg border border-gray-200">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Connection ID
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Name
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Type
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{connections.map((connection) => (
<tr key={connection.id} className="hover:bg-gray-50">
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<Database className="h-4 w-4 text-gray-400 mr-2" />
<span className="text-sm text-gray-900">
{connection.id}
</span>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className="text-sm text-gray-900">
{connection.name}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span
className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${getConnectorTypeColor(
connection.connector_type
)}`}
>
{connection.connector_type}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>

<div className="flex justify-end mt-4">
<button
onClick={onClose}
className="px-4 py-2 text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
>
Close
</button>
</div>
</div>
</div>
);
};
17 changes: 17 additions & 0 deletions gridwalk-ui/src/app/workspace/[workspaceId]/workspaceProjects.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"use client";

import React, { useState } from "react";
import {
Plus,
Expand All @@ -7,6 +8,7 @@ import {
ChevronDown,
Trash2,
HelpCircle,
Database,
} from "lucide-react";
import { CreateProjectModal, DeleteProjectModal } from "./projectModal";
import { HelpSupportModal } from "../supportModal";
Expand All @@ -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 {
Expand All @@ -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);
Expand Down Expand Up @@ -161,6 +165,13 @@ export default function WorkspaceProjectsClient({
<Plus size={16} />
New Project
</button>
<button
onClick={() => setIsConnectionDialogOpen(true)}
className="flex items-center gap-2 px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors w-full text-left"
>
<Database size={16} />
View Connections
</button>
</div>
</div>
)}
Expand Down Expand Up @@ -237,6 +248,12 @@ export default function WorkspaceProjectsClient({
workspaceId={workspaceId}
/>

<ViewWorkspaceConnectionsModal
isOpen={isConnectionDialogOpen}
onClose={() => setIsConnectionDialogOpen(false)}
workspaceId={workspaceId}
/>

<HelpSupportModal
isOpen={isHelpSupportModalOpen}
onClose={() => setIsHelpSupportModalOpen(false)}
Expand Down
2 changes: 1 addition & 1 deletion gridwalk-ui/src/app/workspace/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
>
<span>Show workspaces</span>
<span>View Workspaces</span>
<div
className={`rounded-full p-1 transition-colors duration-200 ${
mounted && isOpen ? "bg-green-500" : "bg-black"
Expand Down

0 comments on commit 566e9cd

Please sign in to comment.