'use client'
import { useState } from 'react'
import { Chip, ChipConfirmModal, ChipInput, Search } from '@sim/emcn'
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { ArrowRight, Plus } from 'lucide-react'
import { useParams } from 'next/navigation'
import { useQueryState } from 'nuqs'
import { SkillTile } from '@/app/workspace/[workspaceId]/components'
import { IntegrationTabsHeader } from '@/app/workspace/[workspaceId]/integrations/components/integration-tabs-header'
import { ShowcaseWithExplore } from '@/app/workspace/[workspaceId]/integrations/components/showcase-with-explore'
import { SkillModal } from '@/app/workspace/[workspaceId]/skills/components/skill-modal'
import { skillIdParam, skillIdUrlKeys } from '@/app/workspace/[workspaceId]/skills/search-params'
import { useDeleteSkill, useSkills } from '@/hooks/queries/skills'
const logger = createLogger('SkillsSettings')
const SKILLS_LABEL = 'Skills'
interface SkillItemProps {
name: string
description: string
onClick: () => void
}
function SkillItem({ name, description, onClick }: SkillItemProps) {
return (
)
}
interface SkillSectionProps {
label: string
children: React.ReactNode
}
function SkillSection({ label, children }: SkillSectionProps) {
return (
)
}
export function Skills() {
const params = useParams()
const workspaceId = (params?.workspaceId as string) || ''
const { data: skills = [], isLoading, error } = useSkills(workspaceId)
const deleteSkillMutation = useDeleteSkill()
const [searchTerm, setSearchTerm] = useState('')
const [editingSkillId, setEditingSkillId] = useQueryState(skillIdParam.key, {
...skillIdParam.parser,
...skillIdUrlKeys,
})
const [showAddForm, setShowAddForm] = useState(false)
const [skillToDelete, setSkillToDelete] = useState<{ id: string; name: string } | null>(null)
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
/** Derive the skill being edited from the loaded list — never store the object in the URL. */
const editingSkill = editingSkillId ? (skills.find((s) => s.id === editingSkillId) ?? null) : null
const filteredSkills = skills.filter((s) => {
if (!searchTerm.trim()) return true
const searchLower = searchTerm.toLowerCase()
return (
s.name.toLowerCase().includes(searchLower) ||
s.description.toLowerCase().includes(searchLower)
)
})
const handleDeleteClick = (skillId: string) => {
const s = skills.find((sk) => sk.id === skillId)
if (!s) return
setSkillToDelete({ id: skillId, name: s.name })
setShowDeleteDialog(true)
}
const handleDeleteSkill = async () => {
if (!skillToDelete) return
setShowDeleteDialog(false)
try {
await deleteSkillMutation.mutateAsync({
workspaceId,
skillId: skillToDelete.id,
})
logger.info(`Deleted skill: ${skillToDelete.id}`)
} catch (error) {
logger.error('Error deleting skill:', error)
} finally {
setSkillToDelete(null)
}
}
const handleSkillSaved = () => {
setShowAddForm(false)
setEditingSkillId(null)
}
const showNoResults = searchTerm.trim() && filteredSkills.length === 0
const handleOpenAddForm = () => {
setEditingSkillId(null)
setShowAddForm(true)
}
const addButton = (
Add to Sim
)
return (
setSearchTerm(e.target.value)}
disabled={isLoading}
className='flex-1'
/>
{error ? (
{getErrorMessage(error, 'Failed to load skills')}
) : filteredSkills.length > 0 ? (
{filteredSkills.map((s) => (
setEditingSkillId(s.id)}
/>
))}
) : showNoResults ? (
No skills found matching “{searchTerm}”
) : null}
{
if (!open) {
setShowAddForm(false)
setEditingSkillId(null)
}
}}
onSave={handleSkillSaved}
onDelete={(skillId) => {
setEditingSkillId(null)
handleDeleteClick(skillId)
}}
initialValues={editingSkill ?? undefined}
/>
)
}