import { useState, useEffect } from 'react' import { Plus, Search, Pencil, Trash2, Users, User as UserIcon, Loader2, FileCode, ArrowLeft } from 'lucide-react' import { Link } from 'react-router-dom' import { cn } from '@/lib/utils' import { usePermissions } from '@/hooks/usePermissions' import { scriptsApi } from '@/api' import type { ScriptTemplateListItem, ScriptCategoryResponse } from '@/types' const COMPLEXITY_CLASSES = { beginner: 'text-emerald-400 bg-emerald-400/10', intermediate: 'text-amber-400 bg-amber-400/10', advanced: 'text-rose-500 bg-rose-500/10', } as const interface Props { onEdit: (id: string) => void onCreate: () => void } export function ScriptTemplateListView({ onEdit, onCreate }: Props) { const [templates, setTemplates] = useState([]) const [categories, setCategories] = useState([]) const [isLoading, setIsLoading] = useState(true) const [searchQuery, setSearchQuery] = useState('') const [deleteConfirm, setDeleteConfirm] = useState(null) const { canManageScriptTemplate, canCreateScriptTemplate } = usePermissions() const loadData = async () => { setIsLoading(true) try { const [tpls, cats] = await Promise.all([ scriptsApi.getManagedTemplates(searchQuery ? { search: searchQuery } : undefined), scriptsApi.getCategories(), ]) setTemplates(tpls) setCategories(cats) } catch { // silently fail } finally { setIsLoading(false) } } useEffect(() => { loadData() }, []) // eslint-disable-line react-hooks/exhaustive-deps useEffect(() => { const timer = setTimeout(() => { loadData() }, 300) return () => clearTimeout(timer) }, [searchQuery]) // eslint-disable-line react-hooks/exhaustive-deps const handleDelete = async (id: string) => { try { await scriptsApi.deleteTemplate(id) setTemplates(prev => prev.filter(t => t.id !== id)) setDeleteConfirm(null) } catch { // silently fail } } const getCategoryName = (categoryId: string) => categories.find(c => c.id === categoryId)?.name ?? 'Unknown' return (
{/* Back link */} Back to Script Library {/* Header row */}

Manage Templates

Create and edit PowerShell script templates.

{canCreateScriptTemplate && ( )}
{/* Search */}
setSearchQuery(e.target.value)} placeholder="Search templates…" className="w-full pl-8 pr-3 py-1.5 text-sm rounded-md border border-[#1e2130] bg-[#14161d] text-[#e2e5eb] placeholder:text-[#848b9b] focus:outline-none focus:border-[rgba(6,182,212,0.3)] focus:ring-1 focus:ring-[rgba(6,182,212,0.2)]" />
{/* Template list */} {isLoading ? (
) : templates.length === 0 ? (

{searchQuery ? 'No templates match your search' : 'No templates yet. Create your first one!'}

) : (
{templates.map(t => ( ))}
Name Category Complexity Scope Uses Actions
{t.name} {t.description && (

{t.description}

)}
{getCategoryName(t.category_id)} {t.complexity} {t.team_id ? <> Team : <> Personal} {t.usage_count}
{canManageScriptTemplate(t) && ( <> {deleteConfirm === t.id ? (
) : ( )} )}
)}
) }