Files
stack/apps/web/src/components/workspace/WorkspaceCard.tsx
Jason Woltje 214139f4d5
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
fix(CQ-WEB-8): Add React.memo to performance-sensitive components
Wrap 7 list-item/card components with React.memo to prevent unnecessary
re-renders when parent components update but props remain unchanged:
- TaskItem (task lists)
- EventCard (calendar views)
- EntryCard (knowledge base)
- WorkspaceCard (workspace list)
- TeamCard (team list)
- DomainItem (domain list)
- ConnectionCard (federation connections)

All are pure components rendered inside .map() loops that depend solely
on their props for rendering output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 18:28:08 -06:00

63 lines
2.2 KiB
TypeScript

import React from "react";
import type { Workspace } from "@mosaic/shared";
import { WorkspaceMemberRole } from "@mosaic/shared";
import Link from "next/link";
interface WorkspaceCardProps {
workspace: Workspace;
userRole: WorkspaceMemberRole;
memberCount: number;
}
const roleColors: Record<WorkspaceMemberRole, string> = {
[WorkspaceMemberRole.OWNER]: "bg-purple-100 text-purple-700",
[WorkspaceMemberRole.ADMIN]: "bg-blue-100 text-blue-700",
[WorkspaceMemberRole.MEMBER]: "bg-green-100 text-green-700",
[WorkspaceMemberRole.GUEST]: "bg-gray-100 text-gray-700",
};
const roleLabels: Record<WorkspaceMemberRole, string> = {
[WorkspaceMemberRole.OWNER]: "Owner",
[WorkspaceMemberRole.ADMIN]: "Admin",
[WorkspaceMemberRole.MEMBER]: "Member",
[WorkspaceMemberRole.GUEST]: "Guest",
};
export const WorkspaceCard = React.memo(function WorkspaceCard({
workspace,
userRole,
memberCount,
}: WorkspaceCardProps): React.JSX.Element {
return (
<Link
href={`/settings/workspaces/${workspace.id}`}
className="block bg-white rounded-lg shadow-sm border border-gray-200 p-6 hover:shadow-md transition-shadow"
>
<div className="flex items-start justify-between">
<div className="flex-1">
<h3 className="text-lg font-semibold text-gray-900 mb-2">{workspace.name}</h3>
<div className="flex items-center gap-3 text-sm text-gray-600">
<span className="flex items-center gap-1">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"
/>
</svg>
{memberCount} {memberCount === 1 ? "member" : "members"}
</span>
</div>
</div>
<span className={`px-3 py-1 rounded-full text-xs font-medium ${roleColors[userRole]}`}>
{roleLabels[userRole]}
</span>
</div>
<div className="mt-4 text-sm text-gray-500">
Created {new Date(workspace.createdAt).toLocaleDateString()}
</div>
</Link>
);
});