You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

184 lines
7.3 KiB
TypeScript

import { route } from 'preact-router'
import { useState } from 'preact/hooks'
import { isAdministrator, Solution as SolutionModel, SolutionId, User as UserModel } from '../../shared/model'
import { server } from '../api'
import { prependBaseUrl, sortByStringKey } from '../../shared/utils'
import { Header } from '../components/Header'
import { MarkdownEditor } from '../components/MarkdownEditor'
import { Select } from '../components/Select'
import { Solution } from '../components/Solution'
import { useListResource } from '../hooks'
import { useLoggedInUser } from '../hooks/useCurrentUser'
const CreateProblem = ({}) => {
const [source, setSource] = useState('')
const [title, setTitle] = useState('')
type ProblemFields = {
title?: string
content: string
}
const handleCreateProblem = async () => {
if (source.trim().length === 0) {
alert('Il campo di testo è vuoto!')
return
}
const id = await server.post<ProblemFields>('/api/problem', {
content: source,
title,
})
route(prependBaseUrl(`/problem/${id}`))
}
return (
<div class="create-problem-form">
<div class="col">
<label for="create-problem-title">Titolo</label>
<input
type="text"
id="create-problem-title"
value={title}
onInput={e => setTitle(e.target instanceof HTMLInputElement ? e.target.value : '')}
placeholder="Problema..."
/>
</div>
<div class="col">
<label>Testo</label>
<MarkdownEditor placeholder="Scrivi un nuovo problema..." {...{ source, setSource }} />
</div>
<button onClick={handleCreateProblem}>Aggiungi Problema</button>
</div>
)
}
type SortOrder = 'latest' | 'oldest'
export const AdminPage = ({}) => {
const [user, ready] = useLoggedInUser()
if (!ready) {
return <></>
}
if (user === null) {
route(prependBaseUrl('/login'), true)
return <></>
}
if (!isAdministrator(user.role)) {
route(prependBaseUrl('/'), true)
return <></>
}
const [solutions, refreshSolutions, setSolutionHeuristic] = useListResource<SolutionModel>(`/api/solutions`)
const [sortOrder, setSortOrder] = useState<SortOrder>('oldest')
const sortedSolutions = sortByStringKey(solutions, s => s.createdAt, sortOrder === 'oldest')
const [trackInteracted, setTrackedInteracted] = useState<Set<SolutionId>>(new Set())
const hasUntrackedPending = sortedSolutions.filter(s => s.status === 'pending' || trackInteracted.has(s.id)).length > 0
const [users] = useListResource<UserModel>(`/api/users`)
const [adminSuUsername, setAdminSuUsername] = useState<string>('')
const handleAdminSu = async () => {
await server.post(`/api/admin/su`, {
username: adminSuUsername,
})
route(prependBaseUrl('/u/' + adminSuUsername))
location.reload()
}
return (
<>
<Header />
<main class="page-admin">
<div class="subtitle">Nuovo problema</div>
<CreateProblem />
<hr />
<div class="subtitle">Soluzioni da correggere</div>
{hasUntrackedPending ? (
<div class="solution-list">
<div class="controls">
<div class="sort-order">
<Select
value={sortOrder}
setValue={setSortOrder}
options={{
latest: 'Prima più recenti',
oldest: 'Prima più antichi',
}}
/>
</div>
</div>
{sortedSolutions.map(
(s, index) =>
(s.status === 'pending' || trackInteracted.has(s.id)) && (
<Solution
adminControls
{...s}
setSolution={solFn => {
setTrackedInteracted(prev => new Set([...prev, s.id]))
setSolutionHeuristic(index, solFn)
}}
refreshSolution={refreshSolutions}
/>
)
)}
</div>
) : (
<>
<em>Nessuna soluzione ancora da correggere</em>
</>
)}
{user.role === 'admin' && (
<>
<hr />
<div class="subtitle">Controlli speciali solo per Admin</div>
{/* TODO: make this work */}
<div class="scrollable">
<div class="users-table" style={{ '--cols': 4 }}>
<div class="cell header">Username Ateneo</div>
<div class="cell header">Nome Utente</div>
<div class="cell header">Ruolo</div>
<div class="cell header last-col">Azioni</div>
{users.map(user => (
<>
<div class="cell">
<a href={prependBaseUrl(`/u/${user.id}`)}>@{user.id}</a>
</div>
<div class="cell">{user.fullName}</div>
<div class="cell">{user.role}</div>
<div class="cell last-col">
<button>Boh 1</button>
<button>Boh 2</button>
</div>
</>
))}
</div>
</div>
<div class="form">
<div class="fill subtitle mono">su $USER</div>
<p class="fill">Gli admin per fare prove possono immedesimarsi come un altro utente</p>
<label>User</label>
<input
type="text"
value={adminSuUsername}
onInput={e => setAdminSuUsername(e.target instanceof HTMLInputElement ? e.target.value : '')}
/>
<div class="fill">
<button onClick={() => handleAdminSu()}>Cambia Utente</button>
</div>
</div>
</>
)}
</main>
</>
)
}