import React, { useState, useEffect, useMemo } from 'react';
import SkillsPageContent from '../components/SkillsPageContent';
import { getSkills, createSkill, updateSkill, deleteSkill, getSkillFiles, getSkillFileContent, saveSkillFile, deleteSkillFile } from '../../../services/apiService';
export default function SkillsPage({ user, Icon }) {
const [skills, setSkills] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [searchQuery, setSearchQuery] = useState('');
const [activeFilter, setActiveFilter] = useState('all'); // all, system, mine, group
const [showSystemSkills, setShowSystemSkills] = useState(false);
const [errorModalMessage, setErrorModalMessage] = useState(null);
const [confirmDeleteId, setConfirmDeleteId] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingSkill, setEditingSkill] = useState(null);
const [formData, setFormData] = useState({
name: '',
description: '',
skill_type: 'local',
is_system: false,
is_enabled: true,
features: ['swarm_control'],
extra_metadata: { emoji: '⚙️' }
});
// VFS State
const [skillFiles, setSkillFiles] = useState([]);
const [activeFile, setActiveFile] = useState(null);
const [activeFileContent, setActiveFileContent] = useState('');
const [isSavingFile, setIsSavingFile] = useState(false);
const loadFiles = async (skillId) => {
try {
const files = await getSkillFiles(skillId);
setSkillFiles(files);
if (files.some(f => f.path === 'SKILL.md')) {
loadContent(skillId, 'SKILL.md');
} else if (files.length > 0) {
loadContent(skillId, files[0].path);
} else {
setActiveFile(null);
setActiveFileContent('');
}
} catch (e) {
console.error("Failed to load files", e);
}
};
const loadContent = async (skillId, path) => {
try {
const file = await getSkillFileContent(skillId, path);
setActiveFile(path);
setActiveFileContent(file.content || '');
} catch (e) {
console.error("Failed to load content", e);
}
};
const handleSelectFile = (path) => {
if (!editingSkill) return;
loadContent(editingSkill.id, path);
};
const handleAddFile = async (path) => {
if (!editingSkill) return;
try {
await saveSkillFile(editingSkill.id, path, '');
await loadFiles(editingSkill.id);
loadContent(editingSkill.id, path);
} catch (e) {
setErrorModalMessage("Failed to create file.");
}
};
const handleSaveActiveFile = async (contentOverride = null) => {
if (!activeFile || !editingSkill) return;
setIsSavingFile(true);
try {
const contentToSave = typeof contentOverride === 'string' ? contentOverride : activeFileContent;
await saveSkillFile(editingSkill.id, activeFile, contentToSave);
// Also explicitly ensure local state is updated just in case it was called directly via override
if (typeof contentOverride === 'string') {
setActiveFileContent(contentOverride);
}
} catch (err) {
setErrorModalMessage(`Failed to save ${activeFile}. Check permissions.`);
} finally {
setIsSavingFile(false);
}
};
const handleDeleteFile = async (path) => {
if (!editingSkill) return;
try {
await deleteSkillFile(editingSkill.id, path);
if (activeFile === path) {
setActiveFile(null);
setActiveFileContent('');
}
await loadFiles(editingSkill.id);
} catch (e) {
setErrorModalMessage("Failed to delete file.");
}
};
const fetchSkills = async () => {
try {
setLoading(true);
const data = await getSkills();
setSkills(data);
setError(null);
} catch (err) {
setError("Failed to load skills.");
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchSkills();
}, []);
const filteredSkills = useMemo(() => {
return skills.filter(skill => {
const matchesSearch = skill.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
(skill.description || '').toLowerCase().includes(searchQuery.toLowerCase());
if (!matchesSearch) return false;
if (!showSystemSkills && skill.is_system && activeFilter !== 'system') return false;
if (activeFilter === 'all') return true;
if (activeFilter === 'system') return skill.is_system;
if (activeFilter === 'mine') return !skill.is_system && skill.owner_id === user?.id;
if (activeFilter === 'group') return skill.group_id && !skill.is_system;
return true;
});
}, [skills, searchQuery, activeFilter, user, showSystemSkills]);
const stats = useMemo(() => ({
total: skills.filter(s => showSystemSkills || activeFilter === 'system' || !s.is_system).length,
system: skills.filter(s => s.is_system).length,
mine: skills.filter(s => !s.is_system && s.owner_id === user?.id).length,
group: skills.filter(s => s.group_id).length,
enabled: skills.filter(s => s.is_enabled).length
}), [skills, user, showSystemSkills, activeFilter]);
const openModal = (skill = null) => {
if (skill) {
setEditingSkill(skill);
if (skill.id) loadFiles(skill.id);
setFormData({
name: skill.name,
description: skill.description || '',
skill_type: skill.skill_type,
is_system: skill.is_system,
is_enabled: skill.is_enabled ?? true,
features: skill.features || ['swarm_control'],
extra_metadata: skill.extra_metadata || { emoji: '⚙️' }
});
} else {
setEditingSkill(null);
setSkillFiles([]);
setActiveFile(null);
setActiveFileContent('');
setFormData({
name: '',
description: '',
skill_type: 'local',
is_system: false,
is_enabled: true,
features: ['swarm_control'],
extra_metadata: { emoji: '⚙️' }
});
}
setIsModalOpen(true);
};
const handleClone = (skill) => {
setEditingSkill(null);
setFormData({
name: `${skill.name}_clone`,
description: skill.description || '',
skill_type: skill.skill_type,
is_system: false,
is_enabled: true,
features: skill.features || ['swarm_control'],
extra_metadata: skill.extra_metadata || { emoji: '⚙️' }
});
setIsModalOpen(true);
};
const closeModal = () => {
setIsModalOpen(false);
setEditingSkill(null);
};
const handleSave = async () => {
try {
const payload = { ...formData };
if (editingSkill) {
await updateSkill(editingSkill.id, payload);
fetchSkills(); // refresh the list softly
} else {
const newSkill = await createSkill(payload);
await fetchSkills();
// Close the creation modal overlay, but immediately open the IDE on the new folder
openModal(newSkill);
}
} catch (err) {
setErrorModalMessage("Error saving folder. Please check your permissions and try again.");
}
};
const handleDelete = (id) => {
setConfirmDeleteId(id);
};
const confirmDelete = async () => {
if (!confirmDeleteId) return;
try {
await deleteSkill(confirmDeleteId);
setConfirmDeleteId(null);
fetchSkills();
} catch (err) {
setErrorModalMessage("Error deleting skill. It might be in use or you may lack permissions.");
setConfirmDeleteId(null);
}
};
const isAdmin = user?.role === 'admin';
const context = {
user,
Icon,
loading,
error,
searchQuery,
setSearchQuery,
activeFilter,
setActiveFilter,
showSystemSkills,
setShowSystemSkills,
isModalOpen,
openModal,
closeModal,
handleClone,
handleSave,
handleDelete,
isAdmin,
filteredSkills,
stats,
editingSkill,
formData,
setFormData,
errorModalMessage,
setErrorModalMessage,
confirmDeleteId,
setConfirmDeleteId,
confirmDelete,
skillFiles,
activeFile,
activeFileContent,
setActiveFileContent,
isSavingFile,
handleSelectFile,
handleAddFile,
handleSaveActiveFile,
handleDeleteFile
};
return <SkillsPageContent context={context} />;
}