- Create ComingSoon component for production placeholders - Federation connections page shows Coming Soon in production - Workspaces settings page shows Coming Soon in production - Teams page shows Coming Soon in production - Add comprehensive tests for environment-based rendering Refs #338 Co-Authored-By: Claude Opus 4.5 <[email protected]>
174 lines
6.0 KiB
TypeScript
174 lines
6.0 KiB
TypeScript
"use client";
|
|
|
|
import type { ReactElement } from "react";
|
|
|
|
import { useState } from "react";
|
|
import { WorkspaceCard } from "@/components/workspace/WorkspaceCard";
|
|
import { ComingSoon } from "@/components/ui/ComingSoon";
|
|
import { WorkspaceMemberRole } from "@mosaic/shared";
|
|
import Link from "next/link";
|
|
|
|
// Check if we're in development mode
|
|
const isDevelopment = process.env.NODE_ENV === "development";
|
|
|
|
// Mock data - TODO: Replace with real API calls (development only)
|
|
const mockWorkspaces = [
|
|
{
|
|
id: "ws-1",
|
|
name: "Personal Workspace",
|
|
ownerId: "user-1",
|
|
settings: {},
|
|
createdAt: new Date("2024-01-15"),
|
|
updatedAt: new Date("2024-01-15"),
|
|
},
|
|
{
|
|
id: "ws-2",
|
|
name: "Team Alpha",
|
|
ownerId: "user-2",
|
|
settings: {},
|
|
createdAt: new Date("2024-01-20"),
|
|
updatedAt: new Date("2024-01-20"),
|
|
},
|
|
];
|
|
|
|
const mockMemberships = [
|
|
{ workspaceId: "ws-1", role: WorkspaceMemberRole.OWNER, memberCount: 1 },
|
|
{ workspaceId: "ws-2", role: WorkspaceMemberRole.MEMBER, memberCount: 5 },
|
|
];
|
|
|
|
/**
|
|
* Workspaces Page Content - Development Only
|
|
* Shows mock workspace data for development purposes
|
|
*/
|
|
function WorkspacesPageContent(): ReactElement {
|
|
const [isCreating, setIsCreating] = useState(false);
|
|
const [newWorkspaceName, setNewWorkspaceName] = useState("");
|
|
|
|
// TODO: Replace with real API call
|
|
const workspacesWithRoles = mockWorkspaces.map((workspace) => {
|
|
const membership = mockMemberships.find((m) => m.workspaceId === workspace.id);
|
|
return {
|
|
...workspace,
|
|
userRole: membership?.role ?? WorkspaceMemberRole.GUEST,
|
|
memberCount: membership?.memberCount ?? 0,
|
|
};
|
|
});
|
|
|
|
const handleCreateWorkspace = async (e: React.SyntheticEvent<HTMLFormElement>): Promise<void> => {
|
|
e.preventDefault();
|
|
if (!newWorkspaceName.trim()) return;
|
|
|
|
setIsCreating(true);
|
|
try {
|
|
// TODO: Replace with real API call
|
|
console.log("Creating workspace:", newWorkspaceName);
|
|
await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API call
|
|
alert(`Workspace "${newWorkspaceName}" created successfully!`);
|
|
setNewWorkspaceName("");
|
|
} catch (_error) {
|
|
console.error("Failed to create workspace:", _error);
|
|
alert("Failed to create workspace");
|
|
} finally {
|
|
setIsCreating(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<main className="container mx-auto px-4 py-8 max-w-5xl">
|
|
<div className="mb-8">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<h1 className="text-3xl font-bold text-gray-900">Workspaces</h1>
|
|
<Link href="/settings" className="text-sm text-blue-600 hover:text-blue-700">
|
|
← Back to Settings
|
|
</Link>
|
|
</div>
|
|
<p className="text-gray-600">Manage your workspaces and collaborate with your team</p>
|
|
</div>
|
|
|
|
{/* Create New Workspace */}
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-6">
|
|
<h2 className="text-lg font-semibold text-gray-900 mb-4">Create New Workspace</h2>
|
|
<form onSubmit={handleCreateWorkspace} className="flex gap-3">
|
|
<input
|
|
type="text"
|
|
value={newWorkspaceName}
|
|
onChange={(e) => {
|
|
setNewWorkspaceName(e.target.value);
|
|
}}
|
|
placeholder="Enter workspace name..."
|
|
disabled={isCreating}
|
|
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:bg-gray-100"
|
|
/>
|
|
<button
|
|
type="submit"
|
|
disabled={isCreating || !newWorkspaceName.trim()}
|
|
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed font-medium"
|
|
>
|
|
{isCreating ? "Creating..." : "Create Workspace"}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
{/* Workspace List */}
|
|
<div className="space-y-4">
|
|
<h2 className="text-xl font-semibold text-gray-900">
|
|
Your Workspaces ({workspacesWithRoles.length})
|
|
</h2>
|
|
{workspacesWithRoles.length === 0 ? (
|
|
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-12 text-center">
|
|
<svg
|
|
className="mx-auto h-12 w-12 text-gray-400 mb-4"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"
|
|
/>
|
|
</svg>
|
|
<h3 className="text-lg font-medium text-gray-900 mb-2">No workspaces yet</h3>
|
|
<p className="text-gray-600">Create your first workspace to get started</p>
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{workspacesWithRoles.map((workspace) => (
|
|
<WorkspaceCard
|
|
key={workspace.id}
|
|
workspace={workspace}
|
|
userRole={workspace.userRole}
|
|
memberCount={workspace.memberCount}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Workspaces Page Entry Point
|
|
* Shows development content or Coming Soon based on environment
|
|
*/
|
|
export default function WorkspacesPage(): ReactElement {
|
|
// In production, show Coming Soon placeholder
|
|
if (!isDevelopment) {
|
|
return (
|
|
<ComingSoon
|
|
feature="Workspace Management"
|
|
description="Create and manage workspaces to organize your projects and collaborate with your team. This feature is currently under development."
|
|
>
|
|
<Link href="/settings" className="text-sm text-blue-600 hover:text-blue-700">
|
|
Back to Settings
|
|
</Link>
|
|
</ComingSoon>
|
|
);
|
|
}
|
|
|
|
// In development, show the full page with mock data
|
|
return <WorkspacesPageContent />;
|
|
}
|