2f76060c39
Build-Deploy-Server / build (base, debian, ) (push) Failing after 7s
Build-Docker-ARM64 / build (base, debian, ) (push) Failing after 11s
Build-Docker-ARM64 / build (base-debian13, debian13, -debian13) (push) Failing after 11s
Build-Docker-ARM64 / build (base-minimal, minimal, -minimal) (push) Failing after 10s
Deploy VitePress to Pages / build (push) Has been cancelled
Deploy VitePress to Pages / Deploy (push) Has been cancelled
- Dashboard: system stats with cards - Tasks: task management table with filters - Scripts: script management grid - Logs: execution log viewer - Interconnect: node management - Terminal: xterm.js integration - Settings: theme and site config - Login: authentication page
167 lines
5.1 KiB
TypeScript
167 lines
5.1 KiB
TypeScript
import { useScripts, useDeleteScript } from '@/api/hooks'
|
|
import { Plus, Trash2, FileCode, RefreshCw } from 'lucide-react'
|
|
import type { Script } from '@/api/types'
|
|
|
|
export default function Scripts() {
|
|
const { data: scripts, isLoading, refetch } = useScripts()
|
|
const deleteScript = useDeleteScript()
|
|
|
|
const handleDelete = async (id: number) => {
|
|
if (confirm('Delete this script?')) {
|
|
await deleteScript.mutateAsync(id)
|
|
}
|
|
}
|
|
|
|
if (isLoading) {
|
|
return <div style={{ color: 'var(--text-muted)', padding: 20 }}>Loading...</div>
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{/* Header */}
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
|
|
<h1 style={{ fontSize: 24, fontWeight: 600 }}>Scripts</h1>
|
|
<div style={{ display: 'flex', gap: 8 }}>
|
|
<button
|
|
onClick={() => refetch()}
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: 6,
|
|
padding: '8px 12px',
|
|
background: 'var(--bg-secondary)',
|
|
border: '1px solid var(--border)',
|
|
borderRadius: 6,
|
|
color: 'var(--text-secondary)',
|
|
cursor: 'pointer',
|
|
fontSize: 13,
|
|
}}
|
|
>
|
|
<RefreshCw size={14} strokeWidth={1.5} />
|
|
Refresh
|
|
</button>
|
|
<button
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: 6,
|
|
padding: '8px 12px',
|
|
background: 'var(--bg-accent)',
|
|
border: 'none',
|
|
borderRadius: 6,
|
|
color: 'white',
|
|
cursor: 'pointer',
|
|
fontSize: 13,
|
|
}}
|
|
>
|
|
<Plus size={14} strokeWidth={1.5} />
|
|
Add Script
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Grid */}
|
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 16 }}>
|
|
{scripts?.map(script => (
|
|
<ScriptCard key={script.id} script={script} onDelete={() => handleDelete(script.id)} />
|
|
))}
|
|
{(!scripts || scripts.length === 0) && (
|
|
<div style={{
|
|
gridColumn: '1 / -1',
|
|
padding: 48,
|
|
textAlign: 'center',
|
|
color: 'var(--text-muted)',
|
|
background: 'var(--bg-secondary)',
|
|
border: '1px solid var(--border)',
|
|
borderRadius: 8,
|
|
}}>
|
|
No scripts found
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function ScriptCard({ script, onDelete }: { script: Script; onDelete: () => void }) {
|
|
const formatDate = (date: string) => {
|
|
return new Date(date).toLocaleDateString()
|
|
}
|
|
|
|
const getLanguageColor = (lang: string) => {
|
|
const colors: Record<string, string> = {
|
|
python: '#3776ab',
|
|
javascript: '#f7df1e',
|
|
typescript: '#3178c6',
|
|
go: '#00add8',
|
|
bash: '#4eaa25',
|
|
shell: '#4eaa25',
|
|
}
|
|
return colors[lang?.toLowerCase()] || '#6b7280'
|
|
}
|
|
|
|
return (
|
|
<div style={{
|
|
background: 'var(--bg-secondary)',
|
|
border: '1px solid var(--border)',
|
|
borderRadius: 8,
|
|
padding: 16,
|
|
transition: 'border-color 0.15s',
|
|
}}>
|
|
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 12 }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
<div style={{
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: 6,
|
|
background: `${getLanguageColor(script.language)}15`,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
}}>
|
|
<FileCode size={20} strokeWidth={1.5} style={{ color: getLanguageColor(script.language) }} />
|
|
</div>
|
|
<div>
|
|
<div style={{ fontWeight: 500, marginBottom: 2 }}>{script.name}</div>
|
|
<div style={{ fontSize: 12, color: 'var(--text-muted)', fontFamily: 'monospace' }}>{script.filename}</div>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={onDelete}
|
|
title="Delete"
|
|
style={{
|
|
width: 28,
|
|
height: 28,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
background: 'transparent',
|
|
border: 'none',
|
|
borderRadius: 4,
|
|
color: 'var(--text-secondary)',
|
|
cursor: 'pointer',
|
|
}}
|
|
>
|
|
<Trash2 size={14} strokeWidth={1.5} />
|
|
</button>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
|
|
<span style={{
|
|
padding: '4px 8px',
|
|
background: `${getLanguageColor(script.language)}15`,
|
|
borderRadius: 4,
|
|
fontSize: 11,
|
|
color: getLanguageColor(script.language),
|
|
fontWeight: 500,
|
|
}}>
|
|
{script.language || 'Unknown'}
|
|
</span>
|
|
</div>
|
|
|
|
<div style={{ fontSize: 12, color: 'var(--text-muted)' }}>
|
|
Updated: {formatDate(script.updated_at)}
|
|
</div>
|
|
</div>
|
|
)
|
|
} |