diff --git a/gridwalk-backend/src/main.rs b/gridwalk-backend/src/main.rs index fdd8b9a..0244457 100644 --- a/gridwalk-backend/src/main.rs +++ b/gridwalk-backend/src/main.rs @@ -15,12 +15,14 @@ use tracing::info; #[tokio::main] async fn main() -> Result<()> { - // Initialize tracing + // Initialise tracing tracing_subscriber::fmt::init(); // Create DynamoDB client let table_name = env::var("DYNAMODB_TABLE").unwrap_or_else(|_| "gridwalk".to_string()); let app_db = Dynamodb::new(false, &table_name).await.unwrap(); + // FOR LOCAL DB DEV + // let app_db = Dynamodb::new(true, "gridwalk").await.unwrap(); // Create GeospatialConfig let geospatial_config = GeospatialConfig::new(); diff --git a/gridwalk-backend/src/routes/workspace.rs b/gridwalk-backend/src/routes/workspace.rs index 16e91e5..9fea54c 100644 --- a/gridwalk-backend/src/routes/workspace.rs +++ b/gridwalk-backend/src/routes/workspace.rs @@ -30,6 +30,12 @@ pub struct ReqAddWorkspaceMember { role: WorkspaceRole, } +#[derive(Debug, Deserialize)] +pub struct ReqRemoveWorkspaceMember { + workspace_id: String, + email: String, +} + #[derive(Debug, Serialize)] pub struct SimpleMemberResponse { role: WorkspaceRole, @@ -107,7 +113,7 @@ pub async fn add_workspace_member( pub async fn remove_workspace_member( State(state): State>, Extension(auth_user): Extension, - Json(req): Json, + Json(req): Json, ) -> Response { if let Some(req_user) = auth_user.user { // Get the workspace diff --git a/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/get_members.ts b/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/get_members.ts index 270913a..e0b54db 100644 --- a/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/get_members.ts +++ b/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/get_members.ts @@ -1,6 +1,6 @@ "use server"; + import { getAuthToken } from "../lib/auth"; -import { revalidatePath } from "next/cache"; type WorkspaceMember = { email: string; @@ -42,9 +42,6 @@ export async function getWorkspaceMembers( const members: WorkspaceMember[] = await response.json(); - // Revalidate the members list page/cache - revalidatePath(`/workspaces/${workspaceId}/members`); - return members; } catch (error) { console.error("Failed to fetch workspace members:", error); diff --git a/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/index.ts b/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/index.ts index fd61e49..e6ebfed 100644 --- a/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/index.ts +++ b/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/index.ts @@ -1,2 +1,4 @@ export * from "./members"; +export * from "./get_members"; +export * from "./remove_members"; export * from "./types"; diff --git a/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/remove_members.ts b/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/remove_members.ts new file mode 100644 index 0000000..e6927f7 --- /dev/null +++ b/gridwalk-ui/src/app/workspace/[workspaceId]/actions/workspace/remove_members.ts @@ -0,0 +1,43 @@ +"use server"; +import { getAuthToken } from "../lib/auth"; + +interface RemoveWorkspaceMemberRequest { + workspace_id: string; + email: string; +} + +export async function removeWorkspaceMember( + data: RemoveWorkspaceMemberRequest, +): Promise { + const token = await getAuthToken(); + + // Validation + if (!data.workspace_id) throw new Error("Workspace ID is required"); + if (!data.email) throw new Error("Email is required"); + + try { + const response = await fetch( + `${process.env.GRIDWALK_API}/workspace/${data.workspace_id}/members/${data.email}`, + { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + workspace_id: data.workspace_id, + email: data.email, + }), + }, + ); + + if (!response.ok) { + const errorText = await response.text(); + if (response.status === 401) throw new Error("Authentication failed"); + throw new Error(errorText || "Failed to remove workspace member"); + } + } catch (error) { + console.error("Failed to remove workspace member:", error); + throw error; + } +} diff --git a/gridwalk-ui/src/app/workspace/[workspaceId]/viewMembersModal.tsx b/gridwalk-ui/src/app/workspace/[workspaceId]/viewMembersModal.tsx index 8adf447..d917083 100644 --- a/gridwalk-ui/src/app/workspace/[workspaceId]/viewMembersModal.tsx +++ b/gridwalk-ui/src/app/workspace/[workspaceId]/viewMembersModal.tsx @@ -1,7 +1,9 @@ "use client"; import React, { useEffect, useState } from "react"; import { getWorkspaceMembers } from "./actions/workspace/get_members"; -import { Shield, Mail, Loader2 } from "lucide-react"; +import { Shield, Mail, Loader2, X } from "lucide-react"; +import { removeWorkspaceMember } from "./actions/workspace/remove_members"; +import { useRouter } from "next/navigation"; interface ViewWorkspaceMemberModalProps { isOpen: boolean; @@ -11,7 +13,7 @@ interface ViewWorkspaceMemberModalProps { type WorkspaceMember = { email: string; - role: "Admin" | "Read"; // Corrected to match your types + role: "Admin" | "Read"; }; export const ViewWorkspaceMemberModal: React.FC< @@ -20,6 +22,8 @@ export const ViewWorkspaceMemberModal: React.FC< const [members, setMembers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [removing, setRemoving] = useState(null); + const router = useRouter(); useEffect(() => { if (isOpen) { @@ -53,6 +57,22 @@ export const ViewWorkspaceMemberModal: React.FC< } }; + const handleRemoveMember = async (email: string) => { + try { + setRemoving(email); + await removeWorkspaceMember({ + workspace_id: workspaceId, + email: email, + }); + setMembers(members.filter((member) => member.email !== email)); + router.refresh(); + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to remove member"); + } finally { + setRemoving(null); + } + }; + return (
@@ -85,9 +105,12 @@ export const ViewWorkspaceMemberModal: React.FC< Email - + Role + + Remove + @@ -105,12 +128,33 @@ export const ViewWorkspaceMemberModal: React.FC<
{member.role}
+ + + ))} diff --git a/gridwalk-ui/src/app/workspace/[workspaceId]/workspaceProjects.tsx b/gridwalk-ui/src/app/workspace/[workspaceId]/workspaceProjects.tsx index b0ed7ac..a9087c5 100644 --- a/gridwalk-ui/src/app/workspace/[workspaceId]/workspaceProjects.tsx +++ b/gridwalk-ui/src/app/workspace/[workspaceId]/workspaceProjects.tsx @@ -74,10 +74,12 @@ export default function WorkspaceProjectsClient({
-

Projects

-

- Current Workspace: {currentWorkspace?.name || "Loading..."} -

+

+ Current Workspace:{" "} + + {currentWorkspace?.name || "Loading..."} + +