import React, { useState, useEffect, useRef } from 'react';
import { useEngine } from '../hooks/useEngine';
interface ProgramManagerProps {}
export const ProgramManager: React.FC<ProgramManagerProps> = () => {
const {
program,
currentProgramId,
newProgram,
saveCurrentProgram,
loadProgramById,
listPrograms,
deleteProgramById,
renameCurrentProgram,
} = useEngine();
const [programs, setPrograms] = useState<{ id: string; name: string; lastModified: number }[]>([]);
const [isListOpen, setIsListOpen] = useState(false);
const [isRenaming, setIsRenaming] = useState(false);
const [newName, setNewName] = useState('');
const [isCreating, setIsCreating] = useState(false);
const [creationName, setCreationName] = useState('');
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
refreshList();
}, [listPrograms]);
// Close dropdown when clicking outside
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsListOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const refreshList = async () => {
const list = await listPrograms();
setPrograms(list);
};
const handleExport = async () => {
if (!program) return;
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(program, null, 2));
const downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", `${program.name.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.json`);
document.body.appendChild(downloadAnchorNode);
downloadAnchorNode.click();
downloadAnchorNode.remove();
};
const handleSave = async () => {
await saveCurrentProgram();
await refreshList();
};
const handleDelete = async (e: React.MouseEvent, id: string) => {
e.stopPropagation();
if (window.confirm('Are you sure you want to delete this program?')) {
await deleteProgramById(id);
await refreshList();
}
};
const handleRename = async (e: React.MouseEvent, id: string) => {
e.stopPropagation();
setNewName(program?.name || '');
setIsRenaming(true);
};
const confirmRename = async () => {
if (currentProgramId) {
await renameCurrentProgram(newName);
setIsRenaming(false);
await refreshList();
}
};
const handleConfirmCreate = () => {
const name = creationName.trim() || 'Untitled Program';
newProgram(name);
setIsCreating(false);
};
return (
<div className="program-manager" ref={dropdownRef}>
<button className="btn" onClick={() => setIsListOpen(!isListOpen)}>
{isListOpen ? '✕ Close' : '📁 Programs'}
</button>
{isListOpen && (
<div className="program-list-dropdown">
<div className="list-header">
<span className="list-title">Programs</span>
<button className="btn btn-sm" onClick={() => {}}>+ New</button>
</div>
<div className="list-items">
{programs.length === 0 ? (
<div className="empty-list">No programs saved</div>
) : (
programs.map((p) => (
<div key={p.id} className="list-item" onClick={() => loadProgramById(p.id)}>
<span className={`item-name ${currentProgramId === p.id ? 'active' : ''}`}>
{p.name}
</span>
<div className="item-actions">
{currentProgramId === p.id && (
<>
<button className="btn-icon" onClick={(e) => handleRename(e, p.id)}>✏️</button>
<button className="btn-icon" onClick={(e) => handleDelete(e, p.id)}>🗑️</button>
</>
)}
</div>
</div>
))
)}
</div>
{program && (
<div className="list-footer">
<button className="btn btn-sm" onClick={handleSave}>Save Current</button>
<button className="btn btn-sm" onClick={handleExport} style={{ marginLeft: '8px' }}>Export File</button>
</div>
)}
</div>
)}
{isRenaming && (
<div className="rename-dialog">
<h4>Rename Program</h4>
<input
type="text"
value={newName}
onChange={(e) => setNewName(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && confirmRename()}
autoFocus
/>
<div className="dialog-actions">
<button className="btn" onClick={confirmRename}>Save</button>
<button className="btn" onClick={() => setIsRenaming(false)}>Cancel</button>
</div>
</div>
)}
{isCreating && (
<div className="rename-dialog">
<h4>New Program</h4>
<input
type="text"
value={creationName}
onChange={(e) => setCreationName(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleConfirmCreate()}
autoFocus
/>
<div className="dialog-actions">
<button className="btn" onClick={handleConfirmCreate}>Create</button>
<button className="btn" onClick={() => setIsCreating(false)}>Cancel</button>
</div>
</div>
)}
</div>
);
};