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

78 lines
2.0 KiB
TypeScript

'use client';
import { useState, useEffect, useRef, useCallback } from 'react';
import { Search, X } from 'lucide-react';
interface TemplateSearchProps {
onSearch: (query: string) => void;
placeholder?: string;
initialValue?: string;
}
export function TemplateSearch({
onSearch,
placeholder = 'Search templates...',
initialValue = '',
}: TemplateSearchProps) {
const [value, setValue] = useState(initialValue);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
const debouncedSearch = useCallback(
(q: string) => {
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => {
onSearch(q);
}, 300);
},
[onSearch],
);
useEffect(() => {
return () => {
if (timerRef.current) clearTimeout(timerRef.current);
};
}, []);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const q = e.target.value;
setValue(q);
debouncedSearch(q);
};
const handleClear = () => {
setValue('');
onSearch('');
inputRef.current?.focus();
};
return (
<div className="relative w-full">
<Search className="absolute left-4 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500 pointer-events-none" />
<input
ref={inputRef}
type="text"
value={value}
onChange={handleChange}
placeholder={placeholder}
className="
w-full pl-11 pr-10 py-3 rounded-full
bg-gray-800 border border-gray-700
text-gray-100 placeholder-gray-500
focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent
transition-all duration-200
"
/>
{value && (
<button
onClick={handleClear}
className="absolute right-3 top-1/2 -translate-y-1/2 p-1 rounded-full
text-gray-500 hover:text-gray-300 hover:bg-gray-700 transition-colors"
>
<X className="h-4 w-4" />
</button>
)}
</div>
);
}