Jake Shore 96e52666c5 MCPEngine full sync — studio scaffold, factory v2, server updates, state.json — 2026-02-12
=== NEW ===
- studio/ — MCPEngine Studio scaffold (Next.js monorepo, build plan)
- docs/FACTORY-V2.md — Factory v2 architecture doc
- docs/CALENDLY_MCP_BUILD_SUMMARY.md — Calendly MCP build report

=== UPDATED SERVERS ===
- fieldedge: Added jobs-tools, UI build script, main entry update
- lightspeed: Updated main + server entry points
- squarespace: Added collection-browser + page-manager apps
- toast: Added main + server entry points

=== INFRA ===
- infra/command-center/state.json — Updated pipeline state
- infra/command-center/FACTORY-V2.md — Factory v2 operator playbook
2026-02-12 17:58:33 -05:00

60 lines
1.4 KiB
TypeScript

import React from 'react';
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export interface EmptyStateProps {
icon?: React.ReactNode;
headline: string;
description?: string;
action?: {
label: string;
onClick: () => void;
};
className?: string;
}
export const EmptyState: React.FC<EmptyStateProps> = ({
icon,
headline,
description,
action,
className,
}) => {
return (
<div
className={twMerge(
clsx(
'flex flex-col items-center justify-center text-center py-16 px-6',
),
className,
)}
>
{icon && (
<div className="mb-4 text-gray-600">{icon}</div>
)}
<h3 className="text-lg font-semibold text-gray-200 mb-1">{headline}</h3>
{description && (
<p className="text-sm text-gray-500 max-w-sm mb-6">{description}</p>
)}
{action && (
<button
onClick={action.onClick}
className={clsx(
'inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg',
'bg-indigo-500 text-white hover:bg-indigo-400',
'transition-colors duration-150',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500/50 focus-visible:ring-offset-2 focus-visible:ring-offset-gray-900',
)}
>
{action.label}
</button>
)}
</div>
);
};
EmptyState.displayName = 'EmptyState';