Rollen neu geschnitten, Seitenzahl korrigiert, frühere Konten
Der Forums-Abgleich las von 25 Archivseiten nur zwei. WoltLab verlinkt in der Blätterleiste nur die Nachbarseiten und führt die Gesamtzahl in einem eigenen Element; ausgewertet wurden aber die Links. Alles jenseits von Seite zwei war damit unsichtbar - etwa ein Antrag auf Seite vier, der deshalb nie ankam. Die Rollen verlaufen jetzt zwischen erfassen und gewähren statt zwischen Alltag und Verwaltung: Editoren erfassen Käufe und führen Datei-Import, GExtension- und Forums-Abgleich samt Entscheidung über Anträge durch. Kulanz zu vergeben bleibt Administratoren, ebenso das Auflösen unklarer Pakete, weil dabei Kulanz entstehen kann. Dazu lassen sich frühere Steam- und Forumskonten am Spieler hinterlegen. Abgleich und Import prüfen sie mit, sodass ein Kontowechsel die Zuordnung nicht abreißen lässt. Steam-Kennungen werden dabei auf dieselbe Form gebracht wie die Hauptangabe, und eine Kennung kann nicht zwei Spielern gehören.
This commit is contained in:
@@ -101,7 +101,7 @@ export default function Dashboard() {
|
||||
<div className="page-head">
|
||||
<h1>Spieler</h1>
|
||||
<div className="head-actions">
|
||||
{can('ADMIN') && (
|
||||
{can('EDITOR') && (
|
||||
<button type="button" className="btn" disabled={syncing} onClick={() => void runSync()}>
|
||||
{syncing ? 'Gleiche ab …' : 'GExtension abgleichen'}
|
||||
</button>
|
||||
|
||||
@@ -16,7 +16,7 @@ import type {
|
||||
|
||||
export default function Forum() {
|
||||
const { can } = useSession();
|
||||
const admin = can('ADMIN');
|
||||
const darfEntscheiden = can('EDITOR');
|
||||
|
||||
const [threads, setThreads] = useState<ForumThread[]>([]);
|
||||
const [notices, setNotices] = useState<ForumThread[]>([]);
|
||||
@@ -88,7 +88,7 @@ export default function Forum() {
|
||||
die hier mit passendem Forum-Namen erfasst sind.
|
||||
</p>
|
||||
</div>
|
||||
{admin && (
|
||||
{darfEntscheiden && (
|
||||
<button type="button" className="btn btn-primary" disabled={busy} onClick={() => void scan()}>
|
||||
{busy ? 'Lese Forum …' : 'Forum abgleichen'}
|
||||
</button>
|
||||
@@ -124,7 +124,7 @@ export default function Forum() {
|
||||
<a className="btn btn-small btn-ghost" href={thread.url} target="_blank" rel="noreferrer">
|
||||
Thread öffnen
|
||||
</a>
|
||||
{admin && (
|
||||
{darfEntscheiden && (
|
||||
<button type="button" className="btn btn-small" onClick={() => void dismiss(thread)}>
|
||||
Erledigt
|
||||
</button>
|
||||
@@ -141,7 +141,7 @@ export default function Forum() {
|
||||
|
||||
{threads.length === 0 ? (
|
||||
<p className="muted">
|
||||
Keine offenen Anträge. {admin && 'Über „Forum abgleichen" nach neuen suchen.'}
|
||||
Keine offenen Anträge. {darfEntscheiden && 'Über „Forum abgleichen" nach neuen suchen.'}
|
||||
</p>
|
||||
) : (
|
||||
<ul className="cc-list">
|
||||
@@ -170,12 +170,12 @@ export default function Forum() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="cc-actions">
|
||||
{admin && !thread.player && (
|
||||
{darfEntscheiden && !thread.player && (
|
||||
<button type="button" className="btn btn-small" onClick={() => setAssigning(thread)}>
|
||||
Spieler zuordnen
|
||||
</button>
|
||||
)}
|
||||
{admin && (
|
||||
{darfEntscheiden && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-small btn-primary"
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||
import BalanceCards from '../components/BalanceCards';
|
||||
import UnmatchedBox from '../components/UnmatchedBox';
|
||||
import AliasBox from '../components/AliasBox';
|
||||
import Modal from '../components/Modal';
|
||||
import Notice from '../components/Notice';
|
||||
import { ApiError, api } from '../lib/api';
|
||||
@@ -192,14 +193,17 @@ export default function PlayerDetail() {
|
||||
>
|
||||
+ Redesign-Paket
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn"
|
||||
disabled={busy}
|
||||
onClick={() => setDialog({ type: 'addCredit', kind: 'kulanz' })}
|
||||
>
|
||||
+ Kulanz
|
||||
</button>
|
||||
{/* Kulanz vergibt das Team, sie steht in keinem Kauf - daher nur Admins. */}
|
||||
{can('ADMIN') && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn"
|
||||
disabled={busy}
|
||||
onClick={() => setDialog({ type: 'addCredit', kind: 'kulanz' })}
|
||||
>
|
||||
+ Kulanz
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary"
|
||||
@@ -208,7 +212,7 @@ export default function PlayerDetail() {
|
||||
>
|
||||
Neuer CC
|
||||
</button>
|
||||
{can('ADMIN') && detail.player.steamId && (
|
||||
{can('EDITOR') && detail.player.steamId && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn"
|
||||
@@ -340,6 +344,13 @@ export default function PlayerDetail() {
|
||||
)}
|
||||
</section>
|
||||
|
||||
<AliasBox
|
||||
playerId={playerId}
|
||||
aliases={detail.aliases ?? []}
|
||||
canEdit={editor}
|
||||
onChange={() => void load()}
|
||||
/>
|
||||
|
||||
<section className="block">
|
||||
<h2>Guthaben-Historie</h2>
|
||||
<div className="table-wrap">
|
||||
|
||||
Reference in New Issue
Block a user