Initial commit
This commit is contained in:
80
frontend/src/pages/Modlists.tsx
Normal file
80
frontend/src/pages/Modlists.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { modlistsApi } from '../api/client'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { useRef, useState } from 'react'
|
||||
|
||||
export default function Modlists() {
|
||||
const qc = useQueryClient()
|
||||
const navigate = useNavigate()
|
||||
const fileRef = useRef<HTMLInputElement>(null)
|
||||
const { data: modlists, isLoading } = useQuery({ queryKey: ['modlists'], queryFn: modlistsApi.list })
|
||||
const [showCreate, setShowCreate] = useState(false)
|
||||
const [name, setName] = useState('')
|
||||
|
||||
const createMut = useMutation({
|
||||
mutationFn: () => modlistsApi.create(name),
|
||||
onSuccess: (res) => { qc.invalidateQueries({ queryKey: ['modlists'] }); setShowCreate(false); setName(''); navigate(`/modlists/${res.id}`) },
|
||||
})
|
||||
|
||||
const deleteMut = useMutation({
|
||||
mutationFn: (id: string) => modlistsApi.delete(id),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['modlists'] }),
|
||||
})
|
||||
|
||||
const duplicateMut = useMutation({
|
||||
mutationFn: (id: string) => {
|
||||
const orig = modlists?.find(m => m.id === id)
|
||||
return modlistsApi.duplicate(id, `${orig?.name ?? 'copy'} (copy)`)
|
||||
},
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['modlists'] }),
|
||||
})
|
||||
|
||||
const importMut = useMutation({
|
||||
mutationFn: (file: File) => modlistsApi.importHtml(file),
|
||||
onSuccess: (res) => { qc.invalidateQueries({ queryKey: ['modlists'] }); navigate(`/modlists/${res.id}`) },
|
||||
})
|
||||
|
||||
const handleImport = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0]
|
||||
if (file) importMut.mutate(file)
|
||||
e.target.value = ''
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input ref={fileRef} type="file" accept=".html" onChange={handleImport} className="hidden" />
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-2xl font-semibold">Modlists</h2>
|
||||
<div className="flex gap-2">
|
||||
<button onClick={() => fileRef.current?.click()} disabled={importMut.isPending} className="px-4 py-2 bg-amber-700 hover:bg-amber-600 disabled:opacity-50 rounded-md text-sm font-medium">Import HTML</button>
|
||||
<button onClick={() => setShowCreate(!showCreate)} className="px-4 py-2 bg-blue-600 hover:bg-blue-500 rounded-md text-sm font-medium">+ New Modlist</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showCreate && (
|
||||
<div className="mb-6 p-4 bg-neutral-900 border border-neutral-800 rounded-lg flex gap-3">
|
||||
<input value={name} onChange={e => setName(e.target.value)} placeholder="Modlist name" className="bg-neutral-800 border border-neutral-700 rounded px-3 py-2 text-sm flex-1" />
|
||||
<button onClick={() => createMut.mutate()} disabled={!name || createMut.isPending} className="px-4 py-2 bg-green-700 hover:bg-green-600 disabled:opacity-50 rounded text-sm">Create</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoading && <p className="text-neutral-500">Loading...</p>}
|
||||
|
||||
<div className="space-y-2">
|
||||
{modlists?.map(m => (
|
||||
<div key={m.id} className="flex items-center justify-between p-4 bg-neutral-900 border border-neutral-800 rounded-lg">
|
||||
<div>
|
||||
<Link to={`/modlists/${m.id}`} className="font-medium hover:text-blue-400 transition-colors">{m.name}</Link>
|
||||
<span className="text-xs text-neutral-500 ml-2">{m.mod_count} mods</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-3 text-sm">
|
||||
<button onClick={() => duplicateMut.mutate(m.id)} className="text-neutral-400 hover:text-white">Duplicate</button>
|
||||
<button onClick={() => deleteMut.mutate(m.id)} className="text-red-500 hover:text-red-400">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{modlists?.length === 0 && <p className="text-neutral-500 text-sm">No modlists yet.</p>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user