feat: add My Scripts/Team Scripts tabs and Build button to Script Library

- Add language field to ScriptTemplateListItem frontend type
- Add mine/shared params to scriptsApi.getTemplates
- Update scriptGeneratorStore.loadTemplates to accept filter params
- Reorganize ScriptLibraryPage with tab bar (My Scripts / Team Scripts)
- Add "Build a New Script" button linking to /script-builder

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-21 17:17:47 -04:00
parent 628761473f
commit 535284c4ce
4 changed files with 70 additions and 20 deletions

View File

@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { Terminal, Settings } from 'lucide-react'
import { Terminal, Settings, Wand2 } from 'lucide-react'
import { useScriptGeneratorStore } from '@/store/scriptGeneratorStore'
import { usePermissions } from '@/hooks/usePermissions'
import { ScriptFilterBar } from '@/components/scripts/ScriptFilterBar'
@@ -8,10 +8,13 @@ import { ScriptTemplateList } from '@/components/scripts/ScriptTemplateList'
import { ScriptConfigurePane } from '@/components/scripts/ScriptConfigurePane'
import { ScriptPreview } from '@/components/scripts/ScriptPreview'
type LibraryTab = 'mine' | 'team'
export default function ScriptLibraryPage() {
const [paneMode, setPaneMode] = useState<'browse' | 'configure'>('browse')
// inputValue owned here so it survives Configure ↔ Browse transitions
const [inputValue, setInputValue] = useState('')
const [activeTab, setActiveTab] = useState<LibraryTab>('mine')
const loadCategories = useScriptGeneratorStore(s => s.loadCategories)
const loadTemplates = useScriptGeneratorStore(s => s.loadTemplates)
@@ -24,8 +27,18 @@ export default function ScriptLibraryPage() {
const canGenerate = isEngineer
useEffect(() => {
loadCategories().then(() => loadTemplates())
}, [loadCategories, loadTemplates])
loadCategories().then(() => {
const filters = activeTab === 'mine' ? { mine: true } : { shared: true }
loadTemplates(filters)
})
}, [loadCategories, loadTemplates, activeTab])
const onTabChange = (tab: LibraryTab) => {
setActiveTab(tab)
setInputValue('')
setSearch('')
setPaneMode('browse')
}
const onClearSearch = () => {
setInputValue('')
@@ -42,23 +55,55 @@ export default function ScriptLibraryPage() {
setPaneMode('browse')
}
const tabClass = (tab: LibraryTab) =>
tab === activeTab
? 'border-b-2 border-primary text-foreground'
: 'text-muted-foreground hover:text-foreground'
return (
<div className="flex flex-col gap-4 p-6 h-full">
{/* Page header */}
<div>
<h1 className="text-2xl font-heading font-bold text-foreground">Script Library</h1>
<p className="text-sm text-muted-foreground mt-1">
Browse PowerShell templates, fill in parameters, and generate ready-to-run scripts.
</p>
{isEngineer && (
<Link
to="/scripts/manage"
className="inline-flex items-center gap-1.5 text-xs text-primary bg-primary/10 hover:bg-primary/15 px-2.5 py-1 rounded-full transition-colors mt-2 group"
>
<Settings size={12} className="group-hover:rotate-90 transition-transform duration-300" />
Manage Templates
</Link>
)}
<div className="flex items-start justify-between">
<div>
<h1 className="text-2xl font-heading font-bold text-foreground">Script Library</h1>
<p className="text-sm text-muted-foreground mt-1">
Browse PowerShell templates, fill in parameters, and generate ready-to-run scripts.
</p>
{isEngineer && (
<Link
to="/scripts/manage"
className="inline-flex items-center gap-1.5 text-xs text-primary bg-primary/10 hover:bg-primary/15 px-2.5 py-1 rounded-full transition-colors mt-2 group"
>
<Settings size={12} className="group-hover:rotate-90 transition-transform duration-300" />
Manage Templates
</Link>
)}
</div>
<Link
to="/script-builder"
className="inline-flex items-center gap-2 bg-gradient-brand text-[#101114] font-semibold rounded-[10px] px-4 py-2 shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] transition-all"
>
<Wand2 size={16} />
Build a New Script
</Link>
</div>
{/* Tab bar */}
<div className="flex gap-6 border-b border-border">
<button
type="button"
onClick={() => onTabChange('mine')}
className={`pb-2 text-sm font-medium transition-colors ${tabClass('mine')}`}
>
My Scripts
</button>
<button
type="button"
onClick={() => onTabChange('team')}
className={`pb-2 text-sm font-medium transition-colors ${tabClass('team')}`}
>
Team Scripts
</button>
</div>
{/* Two-column layout */}