copilot
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import pluginReact from 'eslint-plugin-react'
|
||||
import reactPlugin from 'eslint-plugin-react'
|
||||
import pluginJsxA11y from 'eslint-plugin-jsx-a11y'
|
||||
import pluginImport from 'eslint-plugin-import'
|
||||
import { defineConfig } from 'eslint/config'
|
||||
@@ -12,12 +12,8 @@ export default defineConfig([
|
||||
{
|
||||
files: ['**/*.{js,mjs,cjs,jsx}'],
|
||||
languageOptions: {
|
||||
// parser: babelParser,
|
||||
parserOptions: {
|
||||
requireConfigFile: false,
|
||||
// babelOptions: {
|
||||
// presets: ['@babel/preset-react'],
|
||||
// },
|
||||
ecmaFeatures: { jsx: true },
|
||||
ecmaVersion: 2022,
|
||||
sourceType: 'module',
|
||||
@@ -26,7 +22,7 @@ export default defineConfig([
|
||||
},
|
||||
plugins: {
|
||||
js,
|
||||
react: pluginReact,
|
||||
react: reactPlugin,
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
'jsx-a11y': pluginJsxA11y,
|
||||
@@ -34,14 +30,15 @@ export default defineConfig([
|
||||
},
|
||||
extends: [
|
||||
js.configs.recommended,
|
||||
// pluginReact.configs.recommended,
|
||||
// pluginReact.configs['jsx-runtime'],
|
||||
// pluginReactHooks.configs.recommended,
|
||||
// reactPlugin.configs.recommended,
|
||||
// reactPlugin.configs['jsx-runtime'],
|
||||
// reactHooks.configs.recommended,
|
||||
// pluginJsxA11y.configs.recommended,
|
||||
// pluginImport.configs.recommended,
|
||||
// pluginImport.configs.typescript,
|
||||
],
|
||||
rules: {
|
||||
...reactPlugin.configs['jsx-runtime'].rules,
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
|
||||
'react/jsx-uses-react': 'error',
|
||||
|
||||
16
src/App.css
16
src/App.css
@@ -46,24 +46,28 @@ footer a:hover {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
/* Belt styles */
|
||||
.black-belt {
|
||||
color: white !important;
|
||||
background-color: black !important;
|
||||
background-color: rgba(0, 0, 0, 0.8) !important;
|
||||
}
|
||||
.brown-belt {
|
||||
background-color: saddlebrown !important;
|
||||
background-color: rgba(168, 88, 27, 0.5) !important;
|
||||
}
|
||||
.violet-belt {
|
||||
background-color: blueviolet !important;
|
||||
background-color: rgba(138, 43, 226, 0.5) !important;
|
||||
}
|
||||
.green-belt {
|
||||
background-color: green !important;
|
||||
background-color: rgba(0, 128, 0, 0.5) !important;
|
||||
}
|
||||
.orange-belt {
|
||||
background-color: orange !important;
|
||||
background-color: rgba(255, 135, 18, 0.6) !important;
|
||||
}
|
||||
.yellow-belt {
|
||||
background-color: yellow !important;
|
||||
background-color: rgba(255, 255, 0, 0.6) !important;
|
||||
}
|
||||
.white-belt {
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
/* On screens that are 600px or less */
|
||||
|
||||
87
src/App.jsx
87
src/App.jsx
@@ -1,9 +1,9 @@
|
||||
import ExitToApp from '@mui/icons-material/ExitToApp'
|
||||
import { lazy, Suspense, useEffect, useState } from 'react'
|
||||
import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
||||
import { Button, createTheme, ThemeProvider } from '@mui/material'
|
||||
import { StyledEngineProvider } from '@mui/material/styles'
|
||||
import { lazy, Suspense, useEffect, useState } from 'react'
|
||||
import ExitToApp from '@mui/icons-material/ExitToApp'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
||||
import './App.css'
|
||||
import { DialogContext } from './components/Dialog/Context'
|
||||
import AppBar from './components/AppBar/AppBar'
|
||||
@@ -14,6 +14,7 @@ import Spinner from './components/Spinner/Spinner'
|
||||
import LoadParticipants from './components/UseFetch/LoadParticipants'
|
||||
import useFetch from './components/UseFetch/UseFetch'
|
||||
import LoginDialog from './components/LoginDialog/LoginDialog'
|
||||
import { getStoredToken, storeToken, checkToken } from './api/account'
|
||||
|
||||
const Tournaments = lazy(() => import('./components/Tournaments/Tournaments'))
|
||||
const Registration = lazy(() => import('./pages/Registration'))
|
||||
@@ -45,8 +46,6 @@ const theme = createTheme({
|
||||
})
|
||||
|
||||
export default function App() {
|
||||
// const apiServer = 'http://localhost:8000'
|
||||
// const apiServer = 'http://api.kt.wsl'
|
||||
const apiServer = import.meta.env.VITE_API_SERVER
|
||||
// console.log(import.meta.env)
|
||||
const [token, setToken] = useState(null)
|
||||
@@ -66,67 +65,31 @@ export default function App() {
|
||||
// console.log('env',import.meta.env)
|
||||
|
||||
useEffect(() => {
|
||||
!loadingFetchedTournaments && setTournaments(fetchedTournaments)
|
||||
if (!loadingFetchedTournaments) setTournaments(fetchedTournaments)
|
||||
}, [loadingFetchedTournaments, fetchedTournaments])
|
||||
|
||||
const checkToken = (token) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const response = fetch(apiServer + '/checkToken' + token)
|
||||
response
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
const account = response.headers.get('x-kt-account')
|
||||
const admin = response.headers.get('x-kt-admin')
|
||||
setUser({ account, admin })
|
||||
resolve(true)
|
||||
} else {
|
||||
console.log('error: ', response)
|
||||
localStorage.removeItem('token is invalid')
|
||||
reject(false)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log('error: ', error)
|
||||
reject(false)
|
||||
})
|
||||
} catch (error) {
|
||||
console.log('error: ', error)
|
||||
reject(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const ls = localStorage.getItem('token')
|
||||
const ls = getStoredToken()
|
||||
if (ls) {
|
||||
checkToken(ls)
|
||||
.then((valid) => {
|
||||
if (valid) {
|
||||
setToken(ls)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(`Token valid: ${error}`)
|
||||
})
|
||||
console.log('APP Stored token found:', ls)
|
||||
checkToken(apiServer, ls).then((result) => {
|
||||
if (result.valid) {
|
||||
setUser({ account: result.account, admin: result.admin })
|
||||
setToken(ls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
}, [apiServer])
|
||||
|
||||
const handleToken = (account, admin, token) => {
|
||||
const handleToken = (account, admin, tokenValue) => {
|
||||
setUser({ account, admin })
|
||||
setToken('?token=' + token)
|
||||
localStorage.setItem('token', '?token=' + token)
|
||||
const tokenStr = '?token=' + tokenValue
|
||||
setToken(tokenStr)
|
||||
storeToken(tokenStr)
|
||||
}
|
||||
|
||||
const handleDialogOpen = () => {
|
||||
setDialogOpen(true)
|
||||
// const newDialog = {...dialog, open:true}
|
||||
// setDialog(newDialog);
|
||||
}
|
||||
|
||||
const handleDialogClose = () => {
|
||||
setDialogOpen(false)
|
||||
}
|
||||
const handleDialogOpen = () => setDialogOpen(true)
|
||||
const handleDialogClose = () => setDialogOpen(false)
|
||||
|
||||
useEffect(() => {
|
||||
setDialog({
|
||||
@@ -145,9 +108,6 @@ export default function App() {
|
||||
dialog.setOpen()
|
||||
}
|
||||
|
||||
// console.log('dialog',dialog.open, dialog.content)
|
||||
// console.log('dialogOpen',dialogOpen)
|
||||
|
||||
if (loadingGroups && loadingFetchedTournaments) {
|
||||
return <Spinner />
|
||||
}
|
||||
@@ -162,13 +122,15 @@ export default function App() {
|
||||
{/* // TODO: Alle Rechnungen des Turniers generieren. */}
|
||||
<AppBar openLoginDialog={openLoginDialog} handleToken={handleToken} apiServer={apiServer} token={token} user={user} />
|
||||
<main className="content">
|
||||
{token && <LoadParticipants setParticipants={setParticipants} apiServer={apiServer} token={token} />}
|
||||
<Suspense fallback={<Spinner />}>
|
||||
<Routes>
|
||||
<Route
|
||||
path="registration/:tid"
|
||||
element={
|
||||
participants &&
|
||||
tournaments && <Registration participants={participants} groups={groups} tournaments={tournaments} apiServer={apiServer} token={token} user={user} />
|
||||
tournaments &&
|
||||
groups && <Registration apiServer={apiServer} token={token} user={user} participants={participants} groups={groups} tournaments={tournaments} />
|
||||
}
|
||||
/>
|
||||
<Route path="results/:tid" element={<Results apiServer={apiServer} token={token} user={user} />} />
|
||||
@@ -196,14 +158,13 @@ export default function App() {
|
||||
setParticipants={setParticipants}
|
||||
/>
|
||||
)}
|
||||
{token && <LoadParticipants setParticipants={setParticipants} apiServer={apiServer} token={token} />}
|
||||
<br />
|
||||
<br />
|
||||
{!participants && (
|
||||
<>
|
||||
<h3>{t('headline.participants')}</h3>
|
||||
<p>{t('no-login')}</p>
|
||||
<Button onClick={openLoginDialog.bind(this, null)} color="primary" variant="outlined" startIcon={<ExitToApp />}>
|
||||
<Button onClick={() => openLoginDialog(null)} color="primary" variant="outlined" startIcon={<ExitToApp />}>
|
||||
{t('login')}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -61,16 +61,18 @@ export async function signUpEdit(apiServer, token, newData) {
|
||||
}
|
||||
|
||||
export async function login(apiServer, email, password, fakeID) {
|
||||
console.log('Logging in with email:', email, 'and fakeID:', fakeID)
|
||||
console.log('Using API server:', apiServer)
|
||||
const url = apiServer + '/authentications'
|
||||
const data = { login: { email, password, fakeID } }
|
||||
|
||||
console.log('Request URL:', url)
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
mode: 'cors',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
|
||||
console.log('Response status:', response.status)
|
||||
if (response.status === 200) {
|
||||
return {
|
||||
account: response.headers.get('x-kt-account'),
|
||||
@@ -85,6 +87,8 @@ export async function login(apiServer, email, password, fakeID) {
|
||||
}
|
||||
|
||||
export async function register(apiServer, token, newData) {
|
||||
console.log('Registering with data:', newData)
|
||||
|
||||
const response = await fetch(apiServer + newData.register + token, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(newData),
|
||||
@@ -94,3 +98,30 @@ export async function register(apiServer, token, newData) {
|
||||
if (!response.ok) throw new Error('Fehler bei der Registrierung')
|
||||
return response.json()
|
||||
}
|
||||
export async function checkToken(apiServer, token) {
|
||||
try {
|
||||
const response = await fetch(apiServer + '/checkToken' + token)
|
||||
console.log(response.status, response.ok, response.headers.get('x-kt-account'), response.headers.get('x-kt-admin'))
|
||||
if (response.ok) {
|
||||
const account = response.headers.get('x-kt-account')
|
||||
const admin = response.headers.get('x-kt-admin')
|
||||
console.log('Token check successful:', account, admin)
|
||||
return { valid: true, account, admin }
|
||||
} else {
|
||||
localStorage.removeItem('token')
|
||||
console.warn('Token check failed, removing token')
|
||||
return { valid: false }
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Token check error:', error)
|
||||
return { valid: false }
|
||||
}
|
||||
}
|
||||
|
||||
export function getStoredToken() {
|
||||
return localStorage.getItem('token')
|
||||
}
|
||||
|
||||
export function storeToken(token) {
|
||||
localStorage.setItem('token', token)
|
||||
}
|
||||
@@ -85,6 +85,7 @@ export async function splitGroup(apiServer, token, newData) {
|
||||
}
|
||||
|
||||
export async function moveToGroup(apiServer, token, data) {
|
||||
console.log('moveToGroup', data)
|
||||
const response = await fetch(apiServer + '/moveEncounterToGroup' + token, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
@@ -92,5 +93,6 @@ export async function moveToGroup(apiServer, token, data) {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
if (!response.ok) throw new Error('Fehler beim Verschieben in Gruppe')
|
||||
console.log('moveToGroup response', response.ok)
|
||||
return response.json()
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ const classes = {
|
||||
headerTitle: `${PREFIX}-headerTitle`,
|
||||
}
|
||||
|
||||
const Root = styled(AppBar)(({ theme }) => ({
|
||||
const Root = styled(AppBar)(() => ({
|
||||
flexGrow: 1,
|
||||
[`& .${classes.title}`]: {
|
||||
flexGrow: 1,
|
||||
|
||||
@@ -13,7 +13,6 @@ const classes = {
|
||||
|
||||
const Root = styled('div')({
|
||||
[`& .${classes.row}`]: {
|
||||
// display: 'flex',
|
||||
display: 'table',
|
||||
margin: '2rem',
|
||||
alignItems: 'center',
|
||||
@@ -29,25 +28,22 @@ const Root = styled('div')({
|
||||
},
|
||||
})
|
||||
|
||||
export default function PointsView(props) {
|
||||
const { apiServer, token, groupData, allParticipants, allTeams } = props
|
||||
export default function PointsView({ apiServer, token, groupData, allParticipants, allTeams }) {
|
||||
const { data: groupEncounters, loading } = useFetch(apiServer + '/group/encounters/' + groupData.id + token)
|
||||
const [encounter, setEncounter] = useState(null)
|
||||
const [judgets, setJudgets] = useState(5)
|
||||
const [judges, setJudges] = useState(5)
|
||||
const [average, setAverage] = useState('')
|
||||
const inputRef = useRef()
|
||||
|
||||
console.log('ref', inputRef)
|
||||
useEffect(() => {
|
||||
!loading && setEncounter(groupEncounters)
|
||||
}, [groupEncounters, loading])
|
||||
|
||||
const encounterData = []
|
||||
encounter &&
|
||||
encounter.forEach((element) => {
|
||||
console.log('element', element)
|
||||
// Hilfsfunktion: Teilnehmerdaten für die Anzeige aufbereiten
|
||||
const getEncounterData = () => {
|
||||
if (!encounter) return []
|
||||
return encounter.map((element) => {
|
||||
let aka, shiro
|
||||
|
||||
if (groupData.discipline.includes('Team')) {
|
||||
aka = allTeams.find((x) => x.id === element.aka)
|
||||
shiro = allTeams.find((x) => x.id === element.shiro)
|
||||
@@ -55,8 +51,7 @@ export default function PointsView(props) {
|
||||
aka = allParticipants.find((x) => x.id === element.aka)
|
||||
shiro = allParticipants.find((x) => x.id === element.shiro)
|
||||
}
|
||||
|
||||
const data = {
|
||||
return {
|
||||
encounters: {
|
||||
id: element.id,
|
||||
encounter: element.begegnung,
|
||||
@@ -77,106 +72,83 @@ export default function PointsView(props) {
|
||||
results: element?.wertungen,
|
||||
},
|
||||
}
|
||||
encounterData.push(data)
|
||||
})
|
||||
console.log('encounterData', encounterData)
|
||||
}
|
||||
|
||||
const Participant = (el) => {
|
||||
// console.log(el)
|
||||
if (el.participants.id === 0) return null
|
||||
const encounterData = getEncounterData()
|
||||
|
||||
// Handler für Änderung der Judges/average
|
||||
const changeValue = ({ target: { name, value } }) => {
|
||||
if (name === 'judges') setJudges(value)
|
||||
if (name === 'average') setAverage(value)
|
||||
}
|
||||
|
||||
// Handler für Punkteänderung
|
||||
const handlePointsChange = ({ target: { name, value } }) => {
|
||||
const [id, number] = name.split('/')
|
||||
let index = encounter.findIndex((i) => i.shiro === +id)
|
||||
if (index === -1) index = encounter.findIndex((i) => i.aka === +id)
|
||||
if (index === -1) return
|
||||
|
||||
// Kopie für State-Update!
|
||||
const newEncounter = [...encounter]
|
||||
if (!newEncounter[index].wertungen) newEncounter[index].wertungen = []
|
||||
newEncounter[index].wertungen[parseInt(number)] = value
|
||||
|
||||
// Durchschnitt berechnen (ohne bestes/schlechtestes Ergebnis)
|
||||
const sumArr = [...newEncounter[index].wertungen].map(Number).sort((a, b) => a - b)
|
||||
if (sumArr.length > 2) {
|
||||
sumArr.shift()
|
||||
sumArr.pop()
|
||||
}
|
||||
let avg = sumArr.reduce((acc, el) => acc + el, 0)
|
||||
newEncounter[index].wertungen.sum = avg
|
||||
setEncounter(newEncounter)
|
||||
}
|
||||
|
||||
// Teilnehmer-Komponente
|
||||
const Participant = ({ participants }) => {
|
||||
if (participants.id === 0) return null
|
||||
return (
|
||||
<div className={classes.row}>
|
||||
<div>{el.participants.id}</div>
|
||||
<div>{participants.id}</div>
|
||||
<div className={classes.club}>
|
||||
{!groupData.discipline.includes('Team') && <span>{el.participants.prename + ' ' + el.participants.name}</span>}
|
||||
<strong>{el.participants.club}</strong>
|
||||
{!groupData.discipline.includes('Team') && <span>{participants.prename + ' ' + participants.name}</span>}
|
||||
<strong>{participants.club}</strong>
|
||||
</div>
|
||||
{/* <div>{el.participants.kata}</div> */}
|
||||
<KataSelect val={el.participants.kata} />
|
||||
<Points num={judgets} average={average} id={el.participants.id} results={el.participants?.results} myRef={inputRef} handlePointsChange={handlePointsChange} />
|
||||
<KataSelect val={participants.kata} />
|
||||
<Points num={judges} average={average} id={participants.id} results={participants?.results} myRef={inputRef} handlePointsChange={handlePointsChange} />
|
||||
<FormControl sx={{ m: 1, maxWidth: 80 }} size="small">
|
||||
<TextField label="Sum" variant="filled" value={el.participants?.results ? el.participants.results.sum : ''} disabled size="small" />
|
||||
<TextField label="Sum" variant="filled" value={participants?.results ? participants.results.sum : ''} disabled size="small" />
|
||||
</FormControl>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const changeValue = ({ target: { name, value } }) => {
|
||||
name === 'judgets' && setJudgets(value)
|
||||
name === 'average' && setAverage(value)
|
||||
}
|
||||
|
||||
const handlePointsChange = ({ target: { name, value } }) => {
|
||||
const [id, number] = name.split('/')
|
||||
console.log('name:', name, value)
|
||||
console.log('tmp', id, number)
|
||||
let index = encounter.findIndex((i) => i.shiro === +id)
|
||||
if (index === -1) {
|
||||
index = encounter.findIndex((i) => i.aka === +id)
|
||||
}
|
||||
console.log('tmp', index)
|
||||
console.log('encounter[index]', encounter[index])
|
||||
if (encounter[index]?.wertungen === null) {
|
||||
encounter[index].wertungen = []
|
||||
}
|
||||
encounter[index].wertungen[parseInt(number)] = value
|
||||
|
||||
//calc average value
|
||||
const sum = [...encounter[index].wertungen]
|
||||
console.log('sum1', sum)
|
||||
sum.sort((a, b) => {
|
||||
return a - b
|
||||
})
|
||||
sum.shift()
|
||||
sum.pop()
|
||||
|
||||
let average = 0
|
||||
sum.map((el) => (average += parseInt(el)))
|
||||
console.log('average', average)
|
||||
encounter[index].wertungen.sum = average
|
||||
setEncounter(encounter)
|
||||
}
|
||||
|
||||
// const participants = {
|
||||
// id: 1044,
|
||||
// prename: 'Mario',
|
||||
// name: 'Peters',
|
||||
// club: 'WAT',
|
||||
// kata: 'Heian Nidan',
|
||||
// results: [5.2, 5.5, 5.5, 5.4, 5.7],
|
||||
// }
|
||||
|
||||
return (
|
||||
<Root>
|
||||
{/* judges */}
|
||||
{/* Judges-Auswahl */}
|
||||
<FormControl sx={{ m: 1, minWidth: 120 }} size="small">
|
||||
<InputLabel id="judgets">judgets</InputLabel>
|
||||
<Select labelId="judgets" id="judgets" name="judgets" value="" label="judgets" onChange={changeValue}>
|
||||
{[3, 4, 5, 6, 7].map((el) => {
|
||||
return (
|
||||
<MenuItem key={'judgets' + el} value={el}>
|
||||
{el}
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
<InputLabel id="judges">judges</InputLabel>
|
||||
<Select labelId="judges" id="judges" name="judges" value={judges} label="judges" onChange={changeValue}>
|
||||
{[3, 4, 5, 6, 7].map((el) => (
|
||||
<MenuItem key={'judges' + el} value={el}>
|
||||
{el}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{/* average */}
|
||||
{/* Average */}
|
||||
<FormControl sx={{ m: 1, maxWidth: 100 }}>
|
||||
<TextField name="average" type="number" label="average" defaultValue={5.5} size="small" onChange={changeValue} />
|
||||
<TextField name="average" type="number" label="average" value={average} size="small" onChange={changeValue} />
|
||||
</FormControl>
|
||||
{/* data */}
|
||||
{encounterData.map((el, i) => {
|
||||
// console.log('el', el)
|
||||
return (
|
||||
<>
|
||||
<Participant key={'a' + i} participants={el.aka}></Participant>
|
||||
<Participant key={'s' + i} participants={el.shiro}></Participant>
|
||||
</>
|
||||
)
|
||||
})}
|
||||
{/* <Participant participants={participants}></Participant> */}
|
||||
{/* {console.log('aa', inputRef.current.value)} */}
|
||||
{/* Begegnungen */}
|
||||
{encounterData.map((el, i) => (
|
||||
<div key={i}>
|
||||
<Participant participants={el.aka} />
|
||||
<Participant participants={el.shiro} />
|
||||
</div>
|
||||
))}
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import CloseIcon from '@mui/icons-material/Close'
|
||||
import { lazy, useEffect, useState, useRef } from 'react'
|
||||
import { AppBar, Button, Dialog, IconButton, styled, Toolbar, Typography, TextField, DialogContent, DialogTitle, DialogActions } from '@mui/material'
|
||||
import PointsView from '../Competition/PointsView'
|
||||
import GroupInfo from '../Groups/GroupInfo'
|
||||
import KoView from '../Groups/KoView'
|
||||
import useFetch from '../UseFetch/UseFetch'
|
||||
import { generateRounds } from '../../utilities/Rounds'
|
||||
import { AppBar, Button, Dialog, IconButton, styled, Toolbar, Typography, TextField } from '@mui/material'
|
||||
const ListPDFTemplate = lazy(() => import('../PDF/ListPDFTemplate'))
|
||||
import { pdf } from '@react-pdf/renderer'
|
||||
import { savePdf } from '../../utilities/PDF'
|
||||
import { changeEncounter, setWinner, addResults, lateRegistration } from '../../api/encounter'
|
||||
|
||||
const PREFIX = 'EncounterDialog'
|
||||
|
||||
const classes = {
|
||||
appBar: `${PREFIX}-appBar`,
|
||||
title: `${PREFIX}-title`,
|
||||
@@ -36,7 +35,7 @@ export default function EncounterDialog(props) {
|
||||
const inputRef = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
!loading && setEncounter(groupEncounters)
|
||||
if (!loading) setEncounter(groupEncounters)
|
||||
}, [groupEncounters, loading])
|
||||
|
||||
useEffect(() => {
|
||||
@@ -45,9 +44,7 @@ export default function EncounterDialog(props) {
|
||||
}
|
||||
}, [groupData])
|
||||
|
||||
const toggleKoView = () => {
|
||||
setKoView(!koView)
|
||||
}
|
||||
const toggleKoView = () => setKoView((prev) => !prev)
|
||||
|
||||
const showPDF = async () => {
|
||||
const gid = groupData.gid + '-' + groupData.id
|
||||
@@ -55,16 +52,13 @@ export default function EncounterDialog(props) {
|
||||
try {
|
||||
const blob = await pdf(<ListPDFTemplate {...props} tournament={tournamentData} rounds={rounds} />).toBlob()
|
||||
url = URL.createObjectURL(blob)
|
||||
|
||||
const response = await fetch(url)
|
||||
const blobData = await response.blob()
|
||||
const blobUrl = window.URL.createObjectURL(blobData)
|
||||
|
||||
const link = document.createElement('a')
|
||||
link.href = blobUrl
|
||||
link.download = `${gid}.pdf`
|
||||
link.click()
|
||||
|
||||
blob && savePdf(blob, 'lists', gid, apiServer, token, tournamentData)
|
||||
} catch (error) {
|
||||
console.error('Error in download process:', error)
|
||||
@@ -78,7 +72,6 @@ export default function EncounterDialog(props) {
|
||||
newArr[data.src.encounter][data.src.color] = data.dest.id
|
||||
newArr[data.dest.encounter][data.dest.color] = data.src.id
|
||||
setEncounter(newArr)
|
||||
|
||||
try {
|
||||
await changeEncounter(apiServer, token, data, groupData.id)
|
||||
console.log('participant changed')
|
||||
@@ -91,12 +84,10 @@ export default function EncounterDialog(props) {
|
||||
const targetEncounter = Math.floor(encounterNum / 2)
|
||||
const rem = encounterNum % 2
|
||||
const index = encounter.findIndex((i) => i.begegnung === targetEncounter)
|
||||
|
||||
//set winner local
|
||||
// Sieger lokal setzen
|
||||
const oldIndex = encounter.findIndex((i) => i.begegnung === encounterNum)
|
||||
encounter[oldIndex].sieger = data.id
|
||||
|
||||
//write winner to next round
|
||||
// Sieger in nächste Runde schreiben
|
||||
const color = rem ? 'aka' : 'shiro'
|
||||
if (encounterNum > 1) {
|
||||
encounter[index][color] = data.id
|
||||
@@ -115,11 +106,8 @@ export default function EncounterDialog(props) {
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
// Ergebnisse speichern, falls Finale
|
||||
if (encounterNum === 1 && groupData.pool === 'Final') {
|
||||
console.log('beg', encounterNum)
|
||||
console.log('group', groupData)
|
||||
|
||||
let result
|
||||
if (encounter.length === 1) {
|
||||
result = {
|
||||
@@ -138,10 +126,6 @@ export default function EncounterDialog(props) {
|
||||
p4: encounter[0].aka !== encounter[0].sieger ? encounter[0].aka : encounter[0].shiro,
|
||||
}
|
||||
}
|
||||
|
||||
console.log('result', result)
|
||||
|
||||
// write results to db
|
||||
try {
|
||||
await addResults(apiServer, token, result)
|
||||
console.log('Ergebnisse hinzugefügt')
|
||||
@@ -152,23 +136,17 @@ export default function EncounterDialog(props) {
|
||||
}
|
||||
|
||||
const handleClick = async (e) => {
|
||||
e = e.target.attributes
|
||||
console.log('attributes', e)
|
||||
console.log('groupData', groupData)
|
||||
|
||||
if (e['data-id']?.value == 0) {
|
||||
const encounter = {
|
||||
color: e['class'].value.includes('aka') ? 'aka' : 'shiro',
|
||||
encounter: e['data-encounter'].value,
|
||||
const attrs = e.target.attributes
|
||||
if (attrs['data-id']?.value == 0) {
|
||||
const encounterObj = {
|
||||
color: attrs['class'].value.includes('aka') ? 'aka' : 'shiro',
|
||||
encounter: attrs['data-encounter'].value,
|
||||
gid: groupData.id,
|
||||
team: groupData.discipline.includes('Team') ? true : false,
|
||||
team: groupData.discipline.includes('Team'),
|
||||
tid: inputRef.current.value,
|
||||
}
|
||||
console.log('encounter', encounter)
|
||||
|
||||
// write results to db
|
||||
try {
|
||||
await lateRegistration(apiServer, token, encounter)
|
||||
await lateRegistration(apiServer, token, encounterObj)
|
||||
console.log(inputRef.current.value + ' nachgemeldet')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
@@ -176,10 +154,8 @@ export default function EncounterDialog(props) {
|
||||
}
|
||||
}
|
||||
|
||||
const lateRegistrationHandler = (e) => {
|
||||
console.log('e', e)
|
||||
const lateRegistrationHandler = () => {
|
||||
document.addEventListener('click', handleClick, true)
|
||||
//
|
||||
}
|
||||
|
||||
const rounds = generateRounds(encounter, groupData, allTeams, allParticipants, change, setWinnerHandler)
|
||||
@@ -200,7 +176,6 @@ export default function EncounterDialog(props) {
|
||||
<Button autoFocus color="inherit" onClick={toggleKoView}>
|
||||
toggle
|
||||
</Button>
|
||||
{/* //TODO: Late registration */}
|
||||
{user?.admin > 4 && (
|
||||
<>
|
||||
<TextField id="tid" inputProps={{ ref: inputRef }} label="tid" type="number" variant="filled" size="small" />
|
||||
@@ -211,7 +186,6 @@ export default function EncounterDialog(props) {
|
||||
)}
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
{/* Body */}
|
||||
<GroupInfo groupData={groupData} />
|
||||
{koView && <KoView rounds={rounds} />}
|
||||
{!koView && <PointsView groupData={groupData} token={token} apiServer={apiServer} allParticipants={allParticipants} allTeams={allTeams} />}
|
||||
|
||||
@@ -1,119 +1,100 @@
|
||||
import { useContext, useState } from 'react';
|
||||
import Button from '@mui/material/Button';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import DialogActions from '@mui/material/DialogActions';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import Radio from '@mui/material/Radio';
|
||||
import RadioGroup from '@mui/material/RadioGroup';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import FormLabel from '@mui/material/FormLabel';
|
||||
import Autocomplete from '@mui/material/Autocomplete';
|
||||
import useFetch from '../UseFetch/UseFetch';
|
||||
import { DialogContext } from './Context';
|
||||
import { useContext, useState } from 'react'
|
||||
import { Button, TextField, DialogActions, DialogContent, DialogTitle, Radio, RadioGroup, FormControlLabel, FormControl, FormLabel, Autocomplete } from '@mui/material'
|
||||
import useFetch from '../UseFetch/UseFetch'
|
||||
import { DialogContext } from './Context'
|
||||
|
||||
export default function ProfileDialog({ apiServer, edit, t, token }) {
|
||||
const { data: clubs = [], loading } = useFetch(apiServer + '/getClubs' + token)
|
||||
const dialog = useContext(DialogContext)
|
||||
|
||||
export default function ProfileDialog({apiServer, edit, t, token}) {
|
||||
const { data: clubs, loading } = useFetch(apiServer + "/getClubs" + token);
|
||||
const dialog = useContext(DialogContext);
|
||||
|
||||
const initialFormData = Object.freeze({
|
||||
gender: 'M',
|
||||
name: '',
|
||||
prename: '',
|
||||
club: '',
|
||||
email: '',
|
||||
email2: '',
|
||||
password: '',
|
||||
password2: '',
|
||||
phone: '',
|
||||
});
|
||||
const [formData, setFormData] = useState(initialFormData);
|
||||
|
||||
// useEffect(() => {
|
||||
// edit && setFormData(edit)
|
||||
// }, [edit]);
|
||||
|
||||
const handleClose = () => {
|
||||
dialog.onClose()
|
||||
setFormData(initialFormData)
|
||||
// setEdit(null)
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
edit ? setFormData(edit) : setFormData(initialFormData)
|
||||
}
|
||||
|
||||
const handleChange = (e) => {
|
||||
let targetKey = e.target.name;
|
||||
let targetValue = e.target.value;
|
||||
|
||||
// Fix for Autocomplete
|
||||
if (e.target?.classList?.contains('MuiAutocomplete-input')) {
|
||||
targetKey = 'club';
|
||||
const initialFormData = {
|
||||
gender: 'M',
|
||||
name: '',
|
||||
prename: '',
|
||||
club: '',
|
||||
email: '',
|
||||
email2: '',
|
||||
password: '',
|
||||
password2: '',
|
||||
phone: '',
|
||||
}
|
||||
if (e.target?.classList?.contains('MuiAutocomplete-option')) {
|
||||
targetKey = 'club';
|
||||
targetValue = e.target.innerText;
|
||||
const [formData, setFormData] = useState(initialFormData)
|
||||
|
||||
// Wenn edit übergeben wird, initialisiere das Formular damit
|
||||
// useEffect(() => {
|
||||
// if (edit) setFormData(edit)
|
||||
// }, [edit])
|
||||
|
||||
const handleClose = () => {
|
||||
dialog.onClose()
|
||||
setFormData(initialFormData)
|
||||
}
|
||||
|
||||
setFormData({
|
||||
...formData,
|
||||
[targetKey]: targetValue
|
||||
});
|
||||
};
|
||||
const handleReset = () => {
|
||||
setFormData(edit ? edit : initialFormData)
|
||||
}
|
||||
|
||||
const handleSave = (e) => {
|
||||
e.preventDefault();
|
||||
// if (edit) {
|
||||
// const index = participants.findIndex(i => i.id === edit.id);
|
||||
// participants[index] = formData;
|
||||
// editParticipant(formData)
|
||||
// } else {
|
||||
// addParticipant(formData);
|
||||
// }
|
||||
dialog.onClose();
|
||||
};
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}))
|
||||
}
|
||||
|
||||
return !loading && <>
|
||||
<DialogTitle id="dialog-title">{t('profile.myAccount')}</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
<FormControl component="fieldset">
|
||||
<FormLabel component="legend">{t('profile.gender')}</FormLabel>
|
||||
<RadioGroup aria-label="gender" name="gender" value={formData.gender} onChange={handleChange}>
|
||||
<FormControlLabel value="W" control={<Radio />} label={t('profile.female')} />
|
||||
<FormControlLabel value="M" control={<Radio color="primary" />} label={t('profile.male')} />
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<TextField autoFocus margin="dense" name="name" variant="standard" label={t('participant.name')} type="text" fullWidth value={formData.name} onChange={handleChange} />
|
||||
<TextField margin="dense" name="prename" variant="standard" label={t('participant.forename')} type="text" fullWidth value={formData.prename} onChange={handleChange} />
|
||||
<Autocomplete
|
||||
id="club"
|
||||
freeSolo={true}
|
||||
options={clubs}
|
||||
value={formData.verein}
|
||||
renderInput={(params) => <TextField {...params} variant="standard" label={t('participant.club')} margin="dense" onBlur={handleChange} />}
|
||||
/>
|
||||
// Spezieller Handler für Autocomplete (verein)
|
||||
const handleClubChange = (event, newValue) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
club: newValue || '',
|
||||
}))
|
||||
}
|
||||
|
||||
<TextField margin="dense" name="email" variant="standard" label={t('email')} type="text" fullWidth value={formData.email} onChange={handleChange} />
|
||||
<TextField margin="dense" name="password" variant="standard" label={t('password')} type="password" fullWidth value={formData.password} onChange={handleChange} />
|
||||
<TextField margin="dense" name="password2" variant="standard" label={t('password2')} type="password" fullWidth value={formData.password2} onChange={handleChange} />
|
||||
<TextField margin="dense" name="phone" variant="standard" label={t('phone')} type="text" fullWidth value={formData.phone} onChange={handleChange} />
|
||||
const handleSave = (e) => {
|
||||
e.preventDefault()
|
||||
// Hier könntest du add/edit-Logik einbauen
|
||||
dialog.onClose()
|
||||
}
|
||||
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button onClick={handleReset} color="secondary">
|
||||
reset
|
||||
</Button>
|
||||
<Button onClick={handleClose} color="secondary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleSave} variant="outlined" color="primary">
|
||||
OK
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</>;
|
||||
return (
|
||||
!loading && (
|
||||
<>
|
||||
<DialogTitle id="dialog-title">{t('profile.myAccount')}</DialogTitle>
|
||||
<DialogContent>
|
||||
<FormControl component="fieldset" sx={{ mb: 2 }}>
|
||||
<FormLabel component="legend">{t('profile.gender')}</FormLabel>
|
||||
<RadioGroup aria-label="gender" name="gender" value={formData.gender} onChange={handleChange} row>
|
||||
<FormControlLabel value="W" control={<Radio />} label={t('profile.female')} />
|
||||
<FormControlLabel value="M" control={<Radio color="primary" />} label={t('profile.male')} />
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<TextField autoFocus margin="dense" name="name" variant="standard" label={t('participant.name')} type="text" fullWidth value={formData.name} onChange={handleChange} />
|
||||
<TextField margin="dense" name="prename" variant="standard" label={t('participant.forename')} type="text" fullWidth value={formData.prename} onChange={handleChange} />
|
||||
<Autocomplete
|
||||
id="club"
|
||||
freeSolo
|
||||
options={clubs}
|
||||
value={formData.club}
|
||||
onChange={handleClubChange}
|
||||
renderInput={(params) => <TextField {...params} variant="standard" label={t('participant.club')} margin="dense" name="club" onChange={handleChange} />}
|
||||
/>
|
||||
<TextField margin="dense" name="email" variant="standard" label={t('email')} type="email" fullWidth value={formData.email} onChange={handleChange} />
|
||||
<TextField margin="dense" name="password" variant="standard" label={t('password')} type="password" fullWidth value={formData.password} onChange={handleChange} />
|
||||
<TextField margin="dense" name="password2" variant="standard" label={t('password2')} type="password" fullWidth value={formData.password2} onChange={handleChange} />
|
||||
<TextField margin="dense" name="phone" variant="standard" label={t('phone')} type="text" fullWidth value={formData.phone} onChange={handleChange} />
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleReset} color="secondary">
|
||||
reset
|
||||
</Button>
|
||||
<Button onClick={handleClose} color="secondary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleSave} variant="outlined" color="primary">
|
||||
OK
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,18 +24,11 @@ export default function AddGroups({ token, apiServer, tid }) {
|
||||
|
||||
const handleClose = () => {
|
||||
dialog.onClose()
|
||||
setFormData([initialFormData]) //TODO: nötig ?
|
||||
setFormData([initialFormData])
|
||||
}
|
||||
|
||||
// console.log('formData up', formData[0].gid)
|
||||
// console.log('[initialFormData]', [initialFormData][0].gid);
|
||||
// console.log('[initialFormData]', formData[0].gid === '');
|
||||
|
||||
const handleNewGroup = () => {
|
||||
const newFormData = [...formData, initialFormData]
|
||||
setFormData(newFormData)
|
||||
dialog.setContent(<AddGroups token={token} apiServer={apiServer} tid={tid} />)
|
||||
console.log('formData-handleNewGroup', newFormData)
|
||||
setFormData((prev) => [...prev, { ...initialFormData }])
|
||||
}
|
||||
|
||||
const handleSave = async (e) => {
|
||||
@@ -52,14 +45,9 @@ export default function AddGroups({ token, apiServer, tid }) {
|
||||
<>
|
||||
<DialogTitle id="form-dialog-title">{t('groups.create')}</DialogTitle>
|
||||
<DialogContent>
|
||||
{/* add new groups */}
|
||||
{/* {formData.length === 0 && <OneGroup group={formData} key={'new'} />} */}
|
||||
|
||||
{formData.length > 0 &&
|
||||
formData.map((group, index) => {
|
||||
return <OneGroup key={index} myKey={index} token={token} apiServer={apiServer} formData={formData} setFormData={setFormData} />
|
||||
})}
|
||||
|
||||
{formData.map((group, index) => (
|
||||
<OneGroup key={index} myKey={index} token={token} apiServer={apiServer} formData={formData} setFormData={setFormData} />
|
||||
))}
|
||||
<IconButton aria-label="add group" onClick={handleNewGroup} size="large">
|
||||
<Add />
|
||||
</IconButton>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useContext, useEffect, useState, Suspense, lazy } from 'react'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
const OneGroup = lazy(() => import('./OneGroup'))
|
||||
import { editGroups } from '../../api/groups'
|
||||
|
||||
import { Button, DialogActions, DialogContent, DialogTitle, CircularProgress } from '@mui/material'
|
||||
|
||||
const OneGroup = lazy(() => import('./OneGroup'))
|
||||
|
||||
export default function EditGroups({ token, apiServer, groups, tid }) {
|
||||
const initialFormData = Object.freeze({
|
||||
gid: '',
|
||||
@@ -22,8 +22,9 @@ export default function EditGroups({ token, apiServer, groups, tid }) {
|
||||
const dialog = useContext(DialogContext)
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Wenn Gruppen übergeben werden, setze sie als formData
|
||||
useEffect(() => {
|
||||
groups?.length > 0 && setFormData(groups)
|
||||
if (groups?.length > 0) setFormData(groups)
|
||||
}, [groups])
|
||||
|
||||
const handleClose = () => {
|
||||
@@ -34,9 +35,6 @@ export default function EditGroups({ token, apiServer, groups, tid }) {
|
||||
const handleReset = () => {
|
||||
setFormData(groups)
|
||||
}
|
||||
// console.log('formData up', formData[0].gid)
|
||||
// console.log('[initialFormData]', [initialFormData][0].gid);
|
||||
// console.log('[initialFormData]', formData[0].gid === '');
|
||||
|
||||
const handleSave = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
@@ -9,21 +9,20 @@ const StyledCard = styled(Card)({
|
||||
backgroundColor: 'rgb(64 80 181 / 40%)',
|
||||
})
|
||||
|
||||
export default function GroupInfo(props) {
|
||||
const { groupData } = props
|
||||
// console.log('groupData',groupData);
|
||||
export default function GroupInfo({ groupData = {} }) {
|
||||
const { gid = '', id = '', count = '', age = '', discipline = '', belt = '', gender = '', pool = '' } = groupData
|
||||
|
||||
return (
|
||||
<StyledCard>
|
||||
<CardContent>
|
||||
<p>
|
||||
Gruppe: {groupData.gid} - ({groupData.id}) #{groupData.count}
|
||||
Gruppe: {gid} - ({id}){count && ` #${count}`}
|
||||
</p>
|
||||
<p>Alter: {groupData.age}</p>
|
||||
<p>Disziplin: {groupData.discipline}</p>
|
||||
<p>Gurt: {groupData.belt}</p>
|
||||
<p>Geschlecht: {groupData.gender}</p>
|
||||
<p>Pool: {groupData.pool}</p>
|
||||
<p>Alter: {age}</p>
|
||||
<p>Disziplin: {discipline}</p>
|
||||
<p>Gurt: {belt}</p>
|
||||
<p>Geschlecht: {gender}</p>
|
||||
<p>Pool: {pool}</p>
|
||||
</CardContent>
|
||||
</StyledCard>
|
||||
)
|
||||
|
||||
@@ -5,7 +5,6 @@ import { styled } from '@mui/material/styles'
|
||||
import { beltColor } from '../../utilities/Belt'
|
||||
|
||||
const PREFIX = 'GroupsParticipants'
|
||||
|
||||
const classes = {
|
||||
id: `${PREFIX}-id`,
|
||||
}
|
||||
@@ -20,48 +19,51 @@ const Root = styled('li')(({ theme }) => ({
|
||||
}))
|
||||
|
||||
export default function GroupParticipants({ apiServer, token, groupId, participants, allParticipants, user }) {
|
||||
const { data: groupParticipants, loading } = useFetch(apiServer + '/group/' + groupId + '/participants' + token)
|
||||
|
||||
// Move Participants to another group
|
||||
const drag = (ev) => {
|
||||
const tid = ev.target.getAttribute('data-tid')
|
||||
const gid = ev.target.getAttribute('data-gid')
|
||||
const { data: groupParticipants = [], loading } = useFetch(apiServer + '/group/' + groupId + '/participants' + token)
|
||||
|
||||
// Teilnehmer für Drag & Drop vorbereiten
|
||||
const handleDragStart = (ev) => {
|
||||
const tid = ev.currentTarget.getAttribute('data-tid')
|
||||
const gid = ev.currentTarget.getAttribute('data-gid')
|
||||
ev.dataTransfer.setData('gid', gid)
|
||||
ev.dataTransfer.setData('tid', tid)
|
||||
ev.dataTransfer.setData('table', 'teilnehmer')
|
||||
}
|
||||
|
||||
//const ownTournament = checkOwnTournament(accountId, tournamentId)
|
||||
|
||||
const showParticipants = () => {
|
||||
const result = []
|
||||
groupParticipants.forEach((element, index) => {
|
||||
let item
|
||||
if (user?.admin > 9) {
|
||||
item = allParticipants?.find((x) => x.id === element.id)
|
||||
}
|
||||
// TODO: hier muss geprüft werden, ob der account auch das turnier erstellt hat
|
||||
else if (user?.admin > 4) {
|
||||
item = element
|
||||
} else {
|
||||
item = participants.find((x) => x.id === element.id)
|
||||
}
|
||||
item?.name &&
|
||||
result.push(
|
||||
<Root key={index} data-tid={item?.id} data-gid={groupId} draggable onDragStart={drag}>
|
||||
<span className={classes.id}>{index + 1}.</span>
|
||||
<Chip size="small" label={item?.gurt} style={{ background: beltColor(item?.gurt) }}></Chip>
|
||||
<strong>
|
||||
{item?.vorname} {item?.name}
|
||||
</strong>{' '}
|
||||
({getAge(item?.gebDatum)}) <i>[{item?.verein}]</i>({item?.id})
|
||||
</Root>,
|
||||
)
|
||||
})
|
||||
|
||||
return result
|
||||
// Teilnehmer anhand der Admin-Stufe auswählen
|
||||
const findParticipant = (element) => {
|
||||
if (user?.admin > 9) {
|
||||
return allParticipants?.find((x) => x.id === element.id)
|
||||
} else if (user?.admin > 4) {
|
||||
return element
|
||||
} else {
|
||||
return participants.find((x) => x.id === element.id)
|
||||
}
|
||||
}
|
||||
|
||||
return <>{loading ? <div>...loading</div> : showParticipants()}</>
|
||||
return (
|
||||
<>
|
||||
{loading ? (
|
||||
<div>...loading</div>
|
||||
) : (
|
||||
groupParticipants?.length > 0 &&
|
||||
groupParticipants
|
||||
.map((element, index) => {
|
||||
const item = findParticipant(element)
|
||||
if (!item?.name) return null
|
||||
return (
|
||||
<Root key={item.id || index} data-tid={item.id} data-gid={groupId} draggable onDragStart={handleDragStart}>
|
||||
<span className={classes.id}>{index + 1}.</span>
|
||||
<Chip size="small" label={item.gurt} style={{ background: beltColor(item.gurt) }} />
|
||||
<strong>
|
||||
{item.vorname} {item.name}
|
||||
</strong>
|
||||
({getAge(item.gebDatum)}) <i>[{item.verein}]</i>({item.id})
|
||||
</Root>
|
||||
)
|
||||
})
|
||||
.filter(Boolean)
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,44 +1,41 @@
|
||||
import { styled } from '@mui/material/styles'
|
||||
import useFetch from '../UseFetch/UseFetch'
|
||||
|
||||
const PREFIX = 'GroupsTeams'
|
||||
|
||||
const PREFIX = 'GroupTeams'
|
||||
const classes = {
|
||||
id: `${PREFIX}-id`,
|
||||
}
|
||||
|
||||
const Root = styled('li')(({ theme }) => ({
|
||||
const Root = styled('li')({
|
||||
display: 'flex',
|
||||
gap: '0.6rem',
|
||||
marginBottom: '0.2rem',
|
||||
[`& .${classes.id}`]: {
|
||||
minWidth: '1rem',
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
export default function GroupTeams(props) {
|
||||
const { apiServer, token, groupId } = props
|
||||
const { data: groupTeams, loading } = useFetch(`${apiServer}/group/${groupId}/teams${token}`)
|
||||
export default function GroupTeams({ apiServer, token, groupId }) {
|
||||
const { data: groupTeams = [], loading } = useFetch(`${apiServer}/group/${groupId}/teams${token}`)
|
||||
|
||||
// Move Teams to another group
|
||||
const drag = (ev) => {
|
||||
const tid = ev.target.getAttribute('data-tid')
|
||||
const gid = ev.target.getAttribute('data-gid')
|
||||
|
||||
ev.dataTransfer.setData('gid', gid)
|
||||
const handleDragStart = (ev) => {
|
||||
const tid = ev.currentTarget.getAttribute('data-tid')
|
||||
ev.dataTransfer.setData('gid', groupId)
|
||||
ev.dataTransfer.setData('tid', tid)
|
||||
ev.dataTransfer.setData('table', 'team')
|
||||
}
|
||||
|
||||
const showTeams = () => {
|
||||
if (!groupTeams || !Array.isArray(groupTeams)) return null;
|
||||
return groupTeams.map((el, index) => (
|
||||
<Root key={el.id} data-tid={el.id} data-gid={groupId} draggable onDragStart={drag}>
|
||||
<span className={classes.id}>{index + 1}.</span>
|
||||
<strong>{el.teamName}</strong>({el.id})
|
||||
</Root>
|
||||
));
|
||||
}
|
||||
if (loading) return <div>...loading</div>
|
||||
if (!groupTeams?.length) return null
|
||||
|
||||
return <>{loading ? <div>...loading</div> : showTeams()}</>
|
||||
return (
|
||||
<>
|
||||
{groupTeams.map((el, index) => (
|
||||
<Root key={el.id} data-tid={el.id} data-gid={groupId} draggable onDragStart={handleDragStart}>
|
||||
<span className={classes.id}>{index + 1}.</span>
|
||||
<strong>{el.teamName}</strong>({el.id})
|
||||
</Root>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,35 +4,24 @@ import EncounterDialog from '../Dialog/EncounterDialog'
|
||||
import GroupsTable from './GroupsTable'
|
||||
import { triggerGroup } from '../../api/groups'
|
||||
|
||||
export default function Groups(props) {
|
||||
const { apiServer, token, tournamentGroups, tournamentData, user, participants } = props
|
||||
export default function Groups({ apiServer, token, tournamentGroups, tournamentData, user, participants }) {
|
||||
const [dataTournamentGroup, setDataTournamentGroup] = useState(tournamentGroups)
|
||||
const [open, setOpen] = useState(false)
|
||||
const [groupData, setGroupData] = useState()
|
||||
const [groupData, setGroupData] = useState(null)
|
||||
|
||||
// Alle Teilnehmer und Teams laden
|
||||
const { data: allParticipants } = useFetch(apiServer + '/allParticipants' + token)
|
||||
const { data: allTeams } = useFetch(apiServer + '/teams')
|
||||
|
||||
// KO View Dialog Start
|
||||
// Öffnet den KO-Dialog mit den Gruppendaten
|
||||
const handleClickOpen = (id, gid, age, belt, discipline, gender, pool, count) => {
|
||||
setGroupData({
|
||||
id,
|
||||
gid,
|
||||
age,
|
||||
belt,
|
||||
discipline,
|
||||
gender,
|
||||
pool,
|
||||
count,
|
||||
})
|
||||
setGroupData({ id, gid, age, belt, discipline, gender, pool, count })
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
// Dialog End
|
||||
const handleClose = () => setOpen(false)
|
||||
|
||||
// Gruppe triggern (API-Aufruf)
|
||||
const handleTriggerGroup = async (id, discipline) => {
|
||||
try {
|
||||
await triggerGroup(apiServer, token, id, discipline)
|
||||
|
||||
@@ -27,7 +27,6 @@ import SearchBar from '../Common/SearchBar'
|
||||
import { deleteGroup, triggerFinalists, splitGroup, moveToGroup } from '../../api/groups'
|
||||
|
||||
const PREFIX = 'GroupsTable'
|
||||
|
||||
const classes = {
|
||||
participants: `${PREFIX}-participants`,
|
||||
participantsIcon: `${PREFIX}-participantsIcon`,
|
||||
@@ -51,8 +50,6 @@ const Root = styled('div')(({ theme }) => ({
|
||||
[`& .${classes.expandOpen}`]: {
|
||||
transform: 'rotate(180deg)',
|
||||
},
|
||||
// maxWidth: '600px',
|
||||
// display: 'inline-block',
|
||||
[`& div.${classes.participants}:nth-of-type(2n)`]: {
|
||||
backgroundColor: 'rgba(0,0,0,0.1)',
|
||||
},
|
||||
@@ -66,11 +63,6 @@ const Root = styled('div')(({ theme }) => ({
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: '0.3em',
|
||||
position: 'static',
|
||||
// padding: '0.3em',
|
||||
// borderRadius: '0.3em',
|
||||
// borderWidth: '3px',
|
||||
// borderStyle: 'solid',
|
||||
// borderImage: 'linear-gradient(to bottom, red, rgba(0, 0, 0, 0)) 1 100%',
|
||||
},
|
||||
[`& .${classes.groupHeaderActions}`]: {
|
||||
display: 'flex',
|
||||
@@ -140,14 +132,13 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
|
||||
const [confirmOpen, setConfirmOpen] = useState(false)
|
||||
const [toDelete, setToDelete] = useState(null)
|
||||
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
|
||||
const [page, setPage] = useState(1)
|
||||
const [search, setSearch] = useState('')
|
||||
const [allOpen, setAllOpen] = useState(false)
|
||||
const [openAccordions, setOpenAccordions] = useState([])
|
||||
const groupsPerPage = 20 // Anzahl pro Seite, anpassbar
|
||||
|
||||
const groupsPerPage = 20
|
||||
const filteredGroups = dataTournamentGroup.filter(
|
||||
(el) =>
|
||||
(el.disziplin || '').toLowerCase().includes(search.toLowerCase()) ||
|
||||
@@ -159,10 +150,9 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
const pageCount = Math.ceil(filteredGroups.length / groupsPerPage)
|
||||
const paginatedGroups = filteredGroups.slice((page - 1) * groupsPerPage, page * groupsPerPage)
|
||||
|
||||
|
||||
|
||||
// Löschen einer Gruppe
|
||||
const handleDeleteConfirm = async () => {
|
||||
const groupsRemoved = dataTournamentGroup.filter((x) => x.id != toDelete.id)
|
||||
const groupsRemoved = dataTournamentGroup.filter((x) => x.id !== toDelete.id)
|
||||
setDataTournamentGroup(groupsRemoved)
|
||||
try {
|
||||
await deleteGroup(apiServer, token, toDelete.id)
|
||||
@@ -173,10 +163,9 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
setConfirmOpen(false)
|
||||
}
|
||||
|
||||
const handleDeleteCancel = () => {
|
||||
setConfirmOpen(false)
|
||||
}
|
||||
const handleDeleteCancel = () => setConfirmOpen(false)
|
||||
|
||||
// Hilfsfunktion für Finalisten
|
||||
const getPreroundGroups = (gid) => {
|
||||
const result = []
|
||||
let discipline
|
||||
@@ -195,6 +184,7 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
}
|
||||
}
|
||||
|
||||
// Finalisten triggern
|
||||
const handleTriggerFinalists = async (id, gid) => {
|
||||
const { groups, pools, discipline } = getPreroundGroups(gid)
|
||||
try {
|
||||
@@ -210,7 +200,6 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
dialog.setOpen()
|
||||
}
|
||||
|
||||
|
||||
const handleGroupDrop = (ev, gid) => {
|
||||
ev.preventDefault()
|
||||
setIsDragging(false) // <--- Highlighting nach Drop zurücksetzen
|
||||
@@ -232,13 +221,11 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
gid: +gid,
|
||||
},
|
||||
}
|
||||
moveToGroup(result)
|
||||
moveToGroup(apiServer, token, result)
|
||||
}
|
||||
}
|
||||
|
||||
// const dataTest = [{id: 1068, gid: 2, turnier_id: 54, geschlecht: "M", gurtVon: "6. Kyu", gurtBis: "6. Kyu",}]
|
||||
|
||||
// Toggle alle Accordions
|
||||
// Alle Accordions öffnen/schließen
|
||||
const handleToggleAll = useCallback(() => {
|
||||
if (allOpen) {
|
||||
setOpenAccordions([])
|
||||
@@ -249,7 +236,7 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
}
|
||||
}, [allOpen, paginatedGroups])
|
||||
|
||||
// Einzelnes Accordion toggeln (optional, falls du das Verhalten anpassen willst)
|
||||
// Einzelnes Accordion toggeln
|
||||
const handleAccordionChange = (id) => (event, expanded) => {
|
||||
setOpenAccordions((prev) => (expanded ? [...prev, id] : prev.filter((openId) => openId !== id)))
|
||||
}
|
||||
@@ -260,7 +247,6 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
<IconButton className={classes.expand + (allOpen ? ' ' + classes.expandOpen : '')} onClick={handleToggleAll} aria-expanded={allOpen} aria-label="show more" size="large">
|
||||
<ExpandIcon />
|
||||
</IconButton>
|
||||
|
||||
<SearchBar
|
||||
value={search}
|
||||
onChange={(e) => {
|
||||
@@ -281,16 +267,14 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
return (
|
||||
<Accordion
|
||||
slotProps={{ heading: { component: 'div' } }}
|
||||
key={index}
|
||||
key={el.id}
|
||||
expanded={openAccordions.includes(el.id)}
|
||||
onChange={handleAccordionChange(el.id)}
|
||||
data-tid={el.id}
|
||||
data-gid={el.gid}
|
||||
className={classes.participants}
|
||||
onDragStart={() => setIsDragging(true)}
|
||||
onDragOver={(ev) => {
|
||||
ev.preventDefault()
|
||||
}}
|
||||
onDragOver={(ev) => ev.preventDefault()}
|
||||
onDrop={(ev) => handleGroupDrop(ev, el.id)}
|
||||
style={{
|
||||
transition: 'background 0.2s, border 0.2s',
|
||||
@@ -299,37 +283,34 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
borderRadius: '1em',
|
||||
}}
|
||||
>
|
||||
{/* TODO: use AccordionSummary from mui aber rendert ein button */}
|
||||
<AccordionSummary component="div" className={classes.accordionSummary} expandIcon={<ExpandMoreIcon />} aria-controls={`group${el.id}`} id={`panel${el.id}-header`}>
|
||||
<div className={classes.participantsIcon}>
|
||||
<Tooltip title={el.id}>{isSingle ? <Person color={gender} fontSize="large" /> : <Group color={gender} fontSize="large" />}</Tooltip>
|
||||
{el.gid}
|
||||
</div>
|
||||
|
||||
<div className={classes.groupData}>
|
||||
<Chip className={classes.disziplin} size="medium" variant="outlined" color="primary" label={isKata ? (isMixed ? 'Kata-Mixed' : 'Kata') : 'Kumite'} />
|
||||
|
||||
<div className={classes.ageGrade}>
|
||||
<Chip size="small" variant="outlined" color="primary" label={el.altervon + ' - ' + el.alterbis + ' ' + t('participant.years')} />
|
||||
|
||||
<Chip
|
||||
size="small"
|
||||
variant="filled"
|
||||
color="primary"
|
||||
label={el.gurtVon + ' ' + el.gurtBis}
|
||||
style={{ backgroundImage: `linear-gradient(90deg, ${beltColor(el.gurtVon)} 50%, ${beltColor(el.gurtBis)} 50%)` }}
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(90deg, ${beltColor(el.gurtVon)} 50%, ${beltColor(el.gurtBis)} 50%)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Chip size="medium" variant="outlined" color="primary" icon={<GridView />} label={el.pool} />
|
||||
</div>
|
||||
|
||||
{user?.admin > 4 && (
|
||||
<div className={classes.actions}>
|
||||
<Tooltip title="show list">
|
||||
<IconButton
|
||||
size="large"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation() // Prevent accordion toggle
|
||||
e.stopPropagation()
|
||||
handleClickOpen(
|
||||
el.id,
|
||||
el.gid,
|
||||
@@ -356,19 +337,17 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
<AccountTree />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="get Finalists">
|
||||
<IconButton
|
||||
size="large"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
triggerFinalists(el.id, el.gid)
|
||||
handleTriggerFinalists(el.id, el.gid)
|
||||
}}
|
||||
>
|
||||
<Pin />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="split group">
|
||||
<IconButton
|
||||
size="large"
|
||||
@@ -407,8 +386,11 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
{!user?.account ? null : (
|
||||
<AccordionDetails>
|
||||
<ol style={{ margin: 0, padding: 0 }}>
|
||||
{isSingle && <GroupParticipants groupId={el.id} token={token} apiServer={apiServer} participants={participants} user={user} allParticipants={allParticipants} />}
|
||||
{!isSingle && <GroupTeams groupId={el.id} token={token} apiServer={apiServer} />}
|
||||
{isSingle ? (
|
||||
<GroupParticipants groupId={el.id} token={token} apiServer={apiServer} participants={participants} user={user} allParticipants={allParticipants} />
|
||||
) : (
|
||||
<GroupTeams groupId={el.id} token={token} apiServer={apiServer} />
|
||||
)}
|
||||
</ol>
|
||||
</AccordionDetails>
|
||||
)}
|
||||
@@ -419,6 +401,7 @@ export default function GroupsTable({ dataTournamentGroup, setDataTournamentGrou
|
||||
{/* Pagination */}
|
||||
<Pagination count={pageCount} page={page} onChange={(_, value) => setPage(value)} sx={{ mt: 2, display: 'flex', justifyContent: 'center' }} />
|
||||
|
||||
{/* Bestätigungsdialog für Löschen */}
|
||||
<Dialog open={confirmOpen} onClose={handleDeleteCancel}>
|
||||
<DialogTitle>Löschen bestätigen</DialogTitle>
|
||||
<DialogContent>
|
||||
|
||||
@@ -2,9 +2,7 @@ import KoEncounterItem from './KoEncounterItem'
|
||||
import { styled, Card, CardContent } from '@mui/material'
|
||||
|
||||
const PREFIX = 'KoEncounter'
|
||||
|
||||
const classes = {
|
||||
root: `${PREFIX}-root`,
|
||||
cardContent: `${PREFIX}-cardContent`,
|
||||
}
|
||||
|
||||
@@ -17,17 +15,14 @@ const StyledCard = styled(Card)({
|
||||
},
|
||||
})
|
||||
|
||||
export default function KoEncounter(props) {
|
||||
const { encounterData, change, num, setWinner } = props
|
||||
|
||||
const allowDrop = (ev) => {
|
||||
ev.preventDefault()
|
||||
}
|
||||
export default function KoEncounter({ encounterData, change, num, setWinner }) {
|
||||
const allowDrop = (ev) => ev.preventDefault()
|
||||
|
||||
const drop = (ev) => {
|
||||
ev.preventDefault()
|
||||
if (!ev.target.getAttribute('data-id')) {
|
||||
ev.target = ev.target.parentNode
|
||||
let target = ev.target
|
||||
if (!target.getAttribute('data-id') && target.parentNode) {
|
||||
target = target.parentNode
|
||||
}
|
||||
|
||||
const result = {
|
||||
@@ -38,23 +33,20 @@ export default function KoEncounter(props) {
|
||||
color: ev.dataTransfer.getData('color'),
|
||||
},
|
||||
dest: {
|
||||
id: +ev.target.getAttribute('data-id'),
|
||||
encounter: num - ev.target.getAttribute('data-encounter'),
|
||||
color: ev.target.className.includes('aka') ? 'aka' : 'shiro',
|
||||
id: +target.getAttribute('data-id'),
|
||||
encounter: num - target.getAttribute('data-encounter'),
|
||||
color: target.className.includes('aka') ? 'aka' : 'shiro',
|
||||
},
|
||||
}
|
||||
|
||||
// console.log('ev.target', ev.target); // target
|
||||
console.log('result', result) // target
|
||||
|
||||
change(result)
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledCard>
|
||||
<CardContent className={classes.cardContent} onDragOver={allowDrop} onDrop={drop}>
|
||||
<KoEncounterItem color={'aka'} data={encounterData.aka} encounter={encounterData.encounters.encounter} setWinner={setWinner} />
|
||||
<KoEncounterItem color={'shiro'} data={encounterData.shiro} encounter={encounterData.encounters.encounter} setWinner={setWinner} />
|
||||
<KoEncounterItem color="aka" data={encounterData.aka} encounter={encounterData.encounters.encounter} setWinner={setWinner} />
|
||||
<KoEncounterItem color="shiro" data={encounterData.shiro} encounter={encounterData.encounters.encounter} setWinner={setWinner} />
|
||||
</CardContent>
|
||||
</StyledCard>
|
||||
)
|
||||
|
||||
@@ -1,82 +1,71 @@
|
||||
import EmojiEventsIcon from '@mui/icons-material/EmojiEvents'
|
||||
import { styled, IconButton } from '@mui/material'
|
||||
|
||||
export default function KoEncounterItem(props) {
|
||||
const { color, data, encounter, setWinner } = props
|
||||
const PREFIX = 'KoEncounterItem'
|
||||
const classes = {
|
||||
aka: `${PREFIX}-aka`,
|
||||
shiro: `${PREFIX}-shiro`,
|
||||
rotate: `${PREFIX}-rotate`,
|
||||
encounter: `${PREFIX}-encounter`,
|
||||
}
|
||||
|
||||
const PREFIX = 'KoEncounterItem'
|
||||
const Root = styled('div')({
|
||||
[`&.${classes.aka}`]: {
|
||||
color: 'rgba(255,255,255,0.8)',
|
||||
backgroundColor: 'darkred',
|
||||
display: 'flex',
|
||||
minHeight: 50,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
[`&.${classes.shiro}`]: {
|
||||
color: 'rgba(0,0,0,0.8)',
|
||||
backgroundColor: 'lightgrey',
|
||||
display: 'flex',
|
||||
minHeight: 50,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
[`& .${classes.rotate}`]: {
|
||||
transform: 'rotate(-90deg)',
|
||||
paddingTop: 10,
|
||||
},
|
||||
[`& .${classes.encounter}`]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
alignSelf: 'center',
|
||||
},
|
||||
})
|
||||
|
||||
const classes = {
|
||||
aka: `${PREFIX}-aka`,
|
||||
shiro: `${PREFIX}-shiro`,
|
||||
rotate: `${PREFIX}-rotate`,
|
||||
encounter: `${PREFIX}-encounter`,
|
||||
}
|
||||
const WinButton = styled(IconButton)({
|
||||
color: '#ab9419cf',
|
||||
})
|
||||
|
||||
const Root = styled('div')({
|
||||
[`&.${classes.aka}`]: {
|
||||
color: 'rgba(255,255,255,0.8)',
|
||||
backgroundColor: 'darkred',
|
||||
display: 'flex',
|
||||
minHeight: 50,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
[`&.${classes.shiro}`]: {
|
||||
color: 'rgba(0,0,0,0.8)',
|
||||
backgroundColor: 'lightgrey',
|
||||
display: 'flex',
|
||||
minHeight: 50,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
[`& .${classes.rotate}`]: {
|
||||
transform: 'rotate(-90deg)',
|
||||
paddingTop: 10,
|
||||
},
|
||||
[`& .${classes.encounter}`]: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
[`& .${classes.win}`]: {
|
||||
color: '#FFF',
|
||||
backgroundColor: 'green',
|
||||
},
|
||||
})
|
||||
const WinButton = styled(IconButton)(({ theme }) => ({
|
||||
color: '#ab9419cf',
|
||||
}))
|
||||
export default function KoEncounterItem({ color, data, encounter, setWinner }) {
|
||||
const drag = (ev) => {
|
||||
const id = ev.target.getAttribute('data-id')
|
||||
const encounter = ev.target.getAttribute('data-encounter')
|
||||
const color = ev.target.getAttribute('class').includes('aka') ? 'aka' : 'shiro'
|
||||
const id = ev.currentTarget.getAttribute('data-id')
|
||||
const encounterVal = ev.currentTarget.getAttribute('data-encounter')
|
||||
const colorVal = ev.currentTarget.className.includes('aka') ? 'aka' : 'shiro'
|
||||
ev.dataTransfer.setData('id', id)
|
||||
ev.dataTransfer.setData('encounter', encounter)
|
||||
ev.dataTransfer.setData('color', color)
|
||||
}
|
||||
const EncounterData = () => {
|
||||
if (data.name) {
|
||||
return (
|
||||
<div>
|
||||
{data.prename} {data.name}
|
||||
<br />
|
||||
<strong>{data.club}</strong>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div className={classes.encounter}>
|
||||
<strong>{data.club}</strong>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
ev.dataTransfer.setData('encounter', encounterVal)
|
||||
ev.dataTransfer.setData('color', colorVal)
|
||||
}
|
||||
|
||||
return (
|
||||
<Root data-id={data.id} data-encounter={encounter} draggable onDragStart={drag} className={classes[color]}>
|
||||
<div className={classes.rotate}>{data.id === 0 ? '0000' : data.id}</div>
|
||||
<EncounterData />
|
||||
<WinButton onClick={setWinner.bind(this, data, encounter)} className={classes.win}>
|
||||
<EmojiEventsIcon className={classes.win} />
|
||||
<div className={classes.encounter}>
|
||||
{data.name ? (
|
||||
<>
|
||||
{data.prename} {data.name}
|
||||
<strong>{data.club}</strong>
|
||||
</>
|
||||
) : (
|
||||
<strong>{data.club}</strong>
|
||||
)}
|
||||
</div>
|
||||
<WinButton onClick={() => setWinner(data, encounter)}>
|
||||
<EmojiEventsIcon />
|
||||
</WinButton>
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { styled } from '@mui/material'
|
||||
|
||||
const PREFIX = 'KoView'
|
||||
|
||||
const classes = {
|
||||
roundsContainer: `${PREFIX}-roundsContainer`,
|
||||
round1: `${PREFIX}-round1`,
|
||||
@@ -10,7 +9,6 @@ const classes = {
|
||||
round4: `${PREFIX}-round4`,
|
||||
}
|
||||
|
||||
// TODO jss-to-styled codemod: The Fragment root was replaced by div. Change the tag if needed.
|
||||
const Root = styled('div')({
|
||||
[`& .${classes.roundsContainer}`]: {
|
||||
display: 'flex',
|
||||
@@ -18,24 +16,16 @@ const Root = styled('div')({
|
||||
height: 1100,
|
||||
},
|
||||
[`& .${classes.round1}`]: {
|
||||
'& .MuiCard-root': {
|
||||
margin: '20px 10px',
|
||||
},
|
||||
'& .MuiCard-root': { margin: '20px 10px' },
|
||||
},
|
||||
[`& .${classes.round2}`]: {
|
||||
'& .MuiCard-root': {
|
||||
margin: '140px 10px',
|
||||
},
|
||||
'& .MuiCard-root': { margin: '140px 10px' },
|
||||
},
|
||||
[`& .${classes.round3}`]: {
|
||||
'& .MuiCard-root': {
|
||||
margin: '360px 10px',
|
||||
},
|
||||
'& .MuiCard-root': { margin: '360px 10px' },
|
||||
},
|
||||
[`& .${classes.round4}`]: {
|
||||
'& .MuiCard-root': {
|
||||
margin: '0 10px',
|
||||
},
|
||||
'& .MuiCard-root': { margin: '0 10px' },
|
||||
},
|
||||
})
|
||||
|
||||
@@ -43,10 +33,11 @@ export default function KoView({ rounds }) {
|
||||
return (
|
||||
<Root>
|
||||
<div className={classes.roundsContainer}>
|
||||
<div className={classes.round1}>{rounds.r1}</div>
|
||||
<div className={classes.round2}>{rounds.r2}</div>
|
||||
<div className={classes.round3}>{rounds.r3}</div>
|
||||
<div className={classes.round4}>{rounds.r4}</div>
|
||||
{['round1', 'round2', 'round3', 'round4'].map((round, idx) => (
|
||||
<div key={round} className={classes[`round${idx + 1}`]}>
|
||||
{rounds[`r${idx + 1}`]}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Root>
|
||||
)
|
||||
|
||||
@@ -3,20 +3,18 @@ import { DialogActions, DialogContent, DialogTitle, FormControl, IconButton, Inp
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Delete } from '@mui/icons-material'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import AddGroups from '../Groups/AddGroups'
|
||||
import AddGroups from './AddGroups'
|
||||
import { deleteGroup } from '../../api/groups'
|
||||
|
||||
const PREFIX = 'OneGroup'
|
||||
|
||||
const classes = {
|
||||
formGroup: `${PREFIX}-formGroup`,
|
||||
flex: `${PREFIX}-flex`,
|
||||
s: `${PREFIX}-s`,
|
||||
m: `${PREFIX}-m`,
|
||||
l: `${PREFIX}-l`,
|
||||
}
|
||||
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
const Root = styled('div')(() => ({
|
||||
margin: '0.3rem 0',
|
||||
backgroundColor: '#ebebeb',
|
||||
padding: '0.5rem',
|
||||
@@ -24,50 +22,29 @@ const Root = styled('div')(({ theme }) => ({
|
||||
[`& .${classes.flex}`]: {
|
||||
display: 'flex',
|
||||
},
|
||||
[`& .${classes.s}`]: {
|
||||
width: '4rem',
|
||||
},
|
||||
[`& .${classes.m}`]: {
|
||||
width: '6rem',
|
||||
},
|
||||
[`& .${classes.l}`]: {
|
||||
width: '8rem',
|
||||
},
|
||||
[`& .${classes.s}`]: { width: '4rem' },
|
||||
[`& .${classes.m}`]: { width: '6rem' },
|
||||
[`& .${classes.l}`]: { width: '8rem' },
|
||||
}))
|
||||
|
||||
const BELTS = ['9. Kyu', '8. Kyu', '7. Kyu', '6. Kyu', '5. Kyu', '4. Kyu', '3. Kyu', '2. Kyu', '1. Kyu', 'DAN']
|
||||
|
||||
export default function OneGroup({ token, apiServer, formData, setFormData, myKey, isEditMode }) {
|
||||
const { t } = useTranslation('common')
|
||||
const dialog = useContext(DialogContext)
|
||||
|
||||
// console.log('formData', formData)
|
||||
// console.log('myKey', myKey)
|
||||
|
||||
const handleChange = (e) => {
|
||||
const targetKey = e.target.name
|
||||
const targetValue = e.target.value
|
||||
|
||||
// Neues Array und neues Objekt erzeugen!
|
||||
const { name, value } = e.target
|
||||
const newFormData = [...formData]
|
||||
newFormData[myKey] = { ...formData[myKey], [targetKey]: targetValue }
|
||||
newFormData[myKey] = { ...formData[myKey], [name]: value }
|
||||
setFormData(newFormData)
|
||||
}
|
||||
|
||||
// console.log('formData-one', formData);
|
||||
const generateAgeMenu = () => {
|
||||
const result = []
|
||||
for (let i = 6; i < 100; i++) {
|
||||
result.push(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const ageMenu = useMemo(() => generateAgeMenu(), [])
|
||||
const ageMenu = useMemo(() => Array.from({ length: 94 }, (_, i) => i + 6), [])
|
||||
|
||||
const deleteGroupHandler = async (id) => {
|
||||
try {
|
||||
await deleteGroup(apiServer, token, id)
|
||||
console.log('removed')
|
||||
// TODO: Snackbar
|
||||
dialog.onClose()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
@@ -79,11 +56,11 @@ export default function OneGroup({ token, apiServer, formData, setFormData, myKe
|
||||
const id = formData[myKey]?.id
|
||||
dialog.setContent(
|
||||
<Root>
|
||||
<DialogTitle id="form-dialog-title">Delete Group</DialogTitle>
|
||||
<DialogContent>Wollen Sie die Gruppe {id} wirklich löschen ?</DialogContent>
|
||||
<DialogTitle id="form-dialog-title">{t('groups.delete')}</DialogTitle>
|
||||
<DialogContent>{t('groups.deleteConfirm', { id })}</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={dialog.onClose} color="secondary">
|
||||
Cancel
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button onClick={() => deleteGroupHandler(id)} variant="outlined" color="primary">
|
||||
OK
|
||||
@@ -92,34 +69,32 @@ export default function OneGroup({ token, apiServer, formData, setFormData, myKe
|
||||
</Root>,
|
||||
)
|
||||
} else {
|
||||
console.log('myKey', myKey)
|
||||
console.log('formData', formData)
|
||||
formData.splice(myKey, 1)
|
||||
console.log('formData', formData)
|
||||
setFormData(formData)
|
||||
const newFormData = [...formData]
|
||||
newFormData.splice(myKey, 1)
|
||||
setFormData(newFormData)
|
||||
dialog.setContent(<AddGroups token={token} apiServer={apiServer} tid={formData[0].tid} />)
|
||||
}
|
||||
}
|
||||
|
||||
const group = formData[myKey]
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<IconButton aria-label="add group" onClick={handleDeleteGroup.bind(this, myKey)} size="large">
|
||||
<IconButton aria-label="delete group" onClick={() => handleDeleteGroup(myKey)} size="large">
|
||||
<Delete />
|
||||
</IconButton>
|
||||
<TextField variant="standard" required autoFocus className={classes.s} name="gid" label={t('gid')} type="number" value={formData[myKey].gid} onChange={handleChange} />
|
||||
|
||||
<TextField variant="standard" required autoFocus className={classes.s} name="gid" label={t('gid')} type="number" value={group.gid} onChange={handleChange} />
|
||||
<FormControl variant="standard" required className={classes.m}>
|
||||
<InputLabel id="gender-label">Gender</InputLabel>
|
||||
<Select labelId="gender-label" id="gender" name="geschlecht" value={formData[myKey].geschlecht} onChange={handleChange} className={classes.select}>
|
||||
<MenuItem value={'M'}>Male</MenuItem>
|
||||
<MenuItem value={'W'}>Female</MenuItem>
|
||||
<MenuItem value={'D'}>Mixed</MenuItem>
|
||||
<InputLabel id="gender-label">{t('gender')}</InputLabel>
|
||||
<Select labelId="gender-label" id="gender" name="geschlecht" value={group.geschlecht} onChange={handleChange}>
|
||||
<MenuItem value={'M'}>{t('male')}</MenuItem>
|
||||
<MenuItem value={'W'}>{t('female')}</MenuItem>
|
||||
<MenuItem value={'D'}>{t('mixed')}</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl variant="standard" required className={classes.l}>
|
||||
<InputLabel id="dicipline-label">disziplin</InputLabel>
|
||||
<Select labelId="dicipline-label" id="dicipline" name="disziplin" value={formData[myKey].disziplin} onChange={handleChange} className={classes.select}>
|
||||
<InputLabel id="discipline-label">{t('discipline')}</InputLabel>
|
||||
<Select labelId="discipline-label" id="discipline" name="disziplin" value={group.disziplin} onChange={handleChange}>
|
||||
<MenuItem value={'Kata-Einzel'}>Kata Einzel</MenuItem>
|
||||
<MenuItem value={'Kumite-Einzel'}>Kumite Einzel</MenuItem>
|
||||
<MenuItem value={'Kata-Team'}>Kata Team</MenuItem>
|
||||
@@ -127,112 +102,59 @@ export default function OneGroup({ token, apiServer, formData, setFormData, myKe
|
||||
<MenuItem value={'Kumite-Team'}>Kumite Team</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl variant="standard" required className={classes.m}>
|
||||
<InputLabel id="age-from-label">Alter von</InputLabel>
|
||||
<Select labelId="age-from-label" id="age-from" name="altervon" value={formData[myKey].altervon} onChange={handleChange} className={classes.select}>
|
||||
{ageMenu.map((i) => {
|
||||
return (
|
||||
<MenuItem key={i} value={i}>
|
||||
{i}
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
<InputLabel id="age-from-label">{t('ageFrom')}</InputLabel>
|
||||
<Select labelId="age-from-label" id="age-from" name="altervon" value={group.altervon} onChange={handleChange}>
|
||||
{ageMenu.map((i) => (
|
||||
<MenuItem key={i} value={i}>
|
||||
{i}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl variant="standard" required className={classes.m}>
|
||||
<InputLabel id="age-till-label">Alter bis</InputLabel>
|
||||
<Select labelId="age-till-label" id="age-till" name="alterbis" value={formData[myKey].alterbis} onChange={handleChange} className={classes.select}>
|
||||
{ageMenu.map((i) => {
|
||||
return (
|
||||
<MenuItem key={i} value={i}>
|
||||
{i}
|
||||
</MenuItem>
|
||||
)
|
||||
})}
|
||||
<InputLabel id="age-till-label">{t('ageTo')}</InputLabel>
|
||||
<Select labelId="age-till-label" id="age-till" name="alterbis" value={group.alterbis} onChange={handleChange}>
|
||||
{ageMenu.map((i) => (
|
||||
<MenuItem key={i} value={i}>
|
||||
{i}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl variant="standard" required className={classes.l}>
|
||||
<InputLabel id="belt-from-label">{t('participant.belt')} von</InputLabel>
|
||||
<Select labelId="belt-from-label" id="belt-from" name="gurtVon" value={formData[myKey].gurtVon} onChange={handleChange} className={classes.select}>
|
||||
<MenuItem value={'9. Kyu'}>9. Kyu</MenuItem>
|
||||
<MenuItem className="yellow-belt" value={'8. Kyu'}>
|
||||
8. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="orange-belt" value={'7. Kyu'}>
|
||||
7. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="green-belt" value={'6. Kyu'}>
|
||||
6. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="violet-belt" value={'5. Kyu'}>
|
||||
5. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="violet-belt" value={'4. Kyu'}>
|
||||
4. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="brown-belt" value={'3. Kyu'}>
|
||||
3. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="brown-belt" value={'2. Kyu'}>
|
||||
2. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="brown-belt" value={'1. Kyu'}>
|
||||
1. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="black-belt" value={'DAN'}>
|
||||
DAN
|
||||
</MenuItem>
|
||||
<InputLabel id="belt-from-label">
|
||||
{t('participant.belt')} {t('from')}
|
||||
</InputLabel>
|
||||
<Select labelId="belt-from-label" id="belt-from" name="gurtVon" value={group.gurtVon} onChange={handleChange}>
|
||||
{BELTS.map((belt) => (
|
||||
<MenuItem key={belt} value={belt}>
|
||||
{belt}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormControl variant="standard" required className={classes.l}>
|
||||
<InputLabel id="belt-till-label">{t('participant.belt')} bis</InputLabel>
|
||||
<Select labelId="belt-till-label" id="belt-till" name="gurtBis" value={formData[myKey].gurtBis} onChange={handleChange} className={classes.select}>
|
||||
<MenuItem value={'9. Kyu'}>9. Kyu</MenuItem>
|
||||
<MenuItem className="yellow-belt" value={'8. Kyu'}>
|
||||
8. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="orange-belt" value={'7. Kyu'}>
|
||||
7. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="green-belt" value={'6. Kyu'}>
|
||||
6. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="violet-belt" value={'5. Kyu'}>
|
||||
5. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="violet-belt" value={'4. Kyu'}>
|
||||
4. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="brown-belt" value={'3. Kyu'}>
|
||||
3. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="brown-belt" value={'2. Kyu'}>
|
||||
2. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="brown-belt" value={'1. Kyu'}>
|
||||
1. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="black-belt" value={'DAN'}>
|
||||
DAN
|
||||
</MenuItem>
|
||||
<InputLabel id="belt-till-label">
|
||||
{t('participant.belt')} {t('to')}
|
||||
</InputLabel>
|
||||
<Select labelId="belt-till-label" id="belt-till" name="gurtBis" value={group.gurtBis} onChange={handleChange}>
|
||||
{BELTS.map((belt) => (
|
||||
<MenuItem key={belt} value={belt}>
|
||||
{belt}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
{isEditMode && (
|
||||
<FormControl variant="standard" className={classes.m}>
|
||||
<InputLabel id="pool-label">pool</InputLabel>
|
||||
<Select labelId="pool-label" id="pool" name="pool" value={formData[myKey].pool} onChange={handleChange}>
|
||||
<MenuItem value={'A'}>A</MenuItem>
|
||||
<MenuItem value={'B'}>B</MenuItem>
|
||||
<MenuItem value={'C'}>C</MenuItem>
|
||||
<MenuItem value={'D'}>D</MenuItem>
|
||||
<MenuItem value={'E'}>E</MenuItem>
|
||||
<MenuItem value={'F'}>F</MenuItem>
|
||||
<MenuItem value={'Final'}>FINAL</MenuItem>
|
||||
<InputLabel id="pool-label">Pool</InputLabel>
|
||||
<Select labelId="pool-label" id="pool" name="pool" value={group.pool} onChange={handleChange}>
|
||||
{['A', 'B', 'C', 'D', 'E', 'F', 'Final'].map((pool) => (
|
||||
<MenuItem key={pool} value={pool}>
|
||||
{pool}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
@@ -1,51 +1,59 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { FormControl, InputLabel, MenuItem, Select } from '@mui/material'
|
||||
|
||||
export default function KataSelect({ val }) {
|
||||
const [kata, setKata] = useState(``)
|
||||
const KATAS = [
|
||||
'Heian Shodan',
|
||||
'Heian Nidan',
|
||||
'Heian Sandan',
|
||||
'Heian Yondan',
|
||||
'Heian Godan',
|
||||
'Tekki Shodan',
|
||||
'Tekki Nidan',
|
||||
'Tekki Sandan',
|
||||
'Bassai Dai',
|
||||
'Bassai Sho',
|
||||
'Empi',
|
||||
'Jion',
|
||||
'Hangetsu',
|
||||
'Kanku Dai',
|
||||
'Kanku Sho',
|
||||
'Jitte',
|
||||
'Jiin',
|
||||
'Nijūshiho',
|
||||
'Gankaku',
|
||||
'Chinte',
|
||||
'Sōchin',
|
||||
'Wankan',
|
||||
'Meikyō',
|
||||
'Gojūshiho Dai',
|
||||
'Gojūshiho Sho',
|
||||
'Unsu',
|
||||
]
|
||||
|
||||
export default function KataSelect({ val = '', onChange }) {
|
||||
const [kata, setKata] = useState(val)
|
||||
|
||||
useEffect(() => {
|
||||
val && setKata(val)
|
||||
setKata(val || '')
|
||||
}, [val])
|
||||
|
||||
const handleChange = (event) => {
|
||||
setKata(event.target.value)
|
||||
onChange && onChange(event.target.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<FormControl sx={{ m: 1, minWidth: 160 }} size="small">
|
||||
<InputLabel id="kata-select">Kata</InputLabel>
|
||||
<Select labelId="kata-select" id="kata-select" value={kata} label="Kata" onChange={handleChange}>
|
||||
<InputLabel id="kata-select-label">Kata</InputLabel>
|
||||
<Select labelId="kata-select-label" id="kata-select" value={kata} label="Kata" onChange={handleChange}>
|
||||
<MenuItem value="">
|
||||
<em>None</em>
|
||||
</MenuItem>
|
||||
<MenuItem value={'Heian Shodan'}>Heian Shodan</MenuItem>
|
||||
<MenuItem value={'Heian Nidan'}>Heian Nidan</MenuItem>
|
||||
<MenuItem value={'Heian Sandan'}>Heian Sandan</MenuItem>
|
||||
<MenuItem value={'Heian Yondan'}>Heian Yondan</MenuItem>
|
||||
<MenuItem value={'Heian Godan'}>Heian Godan</MenuItem>
|
||||
<MenuItem value={'Tekki Shodan'}>Tekki Shodan</MenuItem>
|
||||
<MenuItem value={'Tekki Nidan'}>Tekki Nidan</MenuItem>
|
||||
<MenuItem value={'Tekki Sandan'}>Tekki Sandan</MenuItem>
|
||||
<MenuItem value={'Bassai Dai'}>Bassai Dai</MenuItem>
|
||||
<MenuItem value={'Bassai Sho'}>Bassai Sho</MenuItem>
|
||||
<MenuItem value={'Empi'}>Empi</MenuItem>
|
||||
<MenuItem value={'Jion'}>Jion</MenuItem>
|
||||
<MenuItem value={'Hangetsu'}>Hangetsu</MenuItem>
|
||||
<MenuItem value={'Kanku Dai'}>Kanku Dai</MenuItem>
|
||||
<MenuItem value={'Kanku Sho'}>Kanku Sho</MenuItem>
|
||||
<MenuItem value={'Jitte'}>Jitte</MenuItem>
|
||||
<MenuItem value={'Jiin'}>Jiin</MenuItem>
|
||||
<MenuItem value={'Nijūshiho'}>Nijūshiho</MenuItem>
|
||||
<MenuItem value={'Gankaku'}>Gankaku</MenuItem>
|
||||
<MenuItem value={'Chinte'}>Chinte</MenuItem>
|
||||
<MenuItem value={'Sōchin'}>Sōchin</MenuItem>
|
||||
<MenuItem value={'Wankan'}>Wankan</MenuItem>
|
||||
<MenuItem value={'Meikyō'}>Meikyō</MenuItem>
|
||||
<MenuItem value={'Gojūshiho Dai'}>Gojūshiho Dai</MenuItem>
|
||||
<MenuItem value={'Gojūshiho Sho'}>Gojūshiho Sho</MenuItem>
|
||||
<MenuItem value={'Unsu'}>Unsu</MenuItem>
|
||||
{KATAS.map((k) => (
|
||||
<MenuItem key={k} value={k}>
|
||||
{k}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)
|
||||
|
||||
@@ -2,32 +2,24 @@ import { useContext, useState } from 'react'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import { DialogContent, Button, Container, CssBaseline, Grid, Link, TextField } from '@mui/material'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { login as loginApi } from '../../api/account'
|
||||
import { login as loginApi } from '../../api/account.js'
|
||||
|
||||
const LoginContext = (props) => {
|
||||
const { handleToken, apiServer, user, setState } = props
|
||||
export default function LoginContent({ handleToken, apiServer, user, setState }) {
|
||||
const [email, setEmail] = useState('')
|
||||
const [fakeID, setFakeID] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const dialog = useContext(DialogContext)
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const switchDialog = (dialog) => {
|
||||
setState(dialog)
|
||||
}
|
||||
const switchDialog = (dialogName) => setState(dialogName)
|
||||
|
||||
const validateForm = () => {
|
||||
return email.length > 0 && password.length > 0
|
||||
}
|
||||
const validateForm = () => email.length > 0 && password.length > 0
|
||||
|
||||
const handleChange = (event) => {
|
||||
if (event.target.id === 'email') {
|
||||
setEmail(event.target.value.toLowerCase())
|
||||
} else if (event.target.id === 'password') {
|
||||
setPassword(event.target.value)
|
||||
} else if (event.target.id === 'fakeID') {
|
||||
setFakeID(event.target.value)
|
||||
}
|
||||
const { id, value } = event.target
|
||||
if (id === 'email') setEmail(value.toLowerCase())
|
||||
else if (id === 'password') setPassword(value)
|
||||
else if (id === 'fakeID') setFakeID(value)
|
||||
}
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
@@ -38,6 +30,7 @@ const LoginContext = (props) => {
|
||||
handleToken(result.account, result.admin, result.token)
|
||||
dialog.onClose()
|
||||
} else {
|
||||
// Optional: Snackbar oder Fehleranzeige
|
||||
console.log('no data found')
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -49,65 +42,41 @@ const LoginContext = (props) => {
|
||||
<DialogContent>
|
||||
<Container component="main" maxWidth="xs">
|
||||
<CssBaseline />
|
||||
<div>
|
||||
<form noValidate>
|
||||
<TextField
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
id="email"
|
||||
label="Email Address"
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
autoFocus
|
||||
value={email}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
name="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
id="password"
|
||||
autoComplete="current-password"
|
||||
value={password}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{user?.admin > 9 && (
|
||||
<TextField variant="outlined" margin="normal" fullWidth name="fakeID" label="fakeID" id="fakeID" autoComplete="fakeID" value={fakeID} onChange={handleChange} />
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<Button type="button" fullWidth variant="contained" color="primary" disabled={!validateForm()} onClick={handleSubmit}>
|
||||
Sign In
|
||||
</Button>
|
||||
<Grid container>
|
||||
<Grid item xs>
|
||||
{/* <Link href="https://old.karateturniere.de/anmeldung/passwort_vergessen.php" variant="body2" target="_blank">
|
||||
Forgot password?
|
||||
</Link> */}
|
||||
<Link onClick={switchDialog.bind(this, t('signUp.pwReset'))} variant="body2" href="#">
|
||||
Forgot password?
|
||||
</Link>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
{/* <Link href="https://old.karateturniere.de/anmeldung/register.php" variant="body2" target="_blank">
|
||||
{"Don't have an account? Sign Up"}
|
||||
</Link> */}
|
||||
<Link onClick={switchDialog.bind(this, t('signUp.signUp'))} variant="body2" href="#">
|
||||
{"Don't have an account? Sign Up"}
|
||||
</Link>
|
||||
</Grid>
|
||||
<form noValidate onSubmit={handleSubmit}>
|
||||
<TextField variant="outlined" margin="normal" required fullWidth id="email" label={t('email')} name="email" autoComplete="email" autoFocus value={email} onChange={handleChange} />
|
||||
<TextField
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
required
|
||||
fullWidth
|
||||
name="password"
|
||||
label={t('password')}
|
||||
type="password"
|
||||
id="password"
|
||||
autoComplete="current-password"
|
||||
value={password}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{user?.admin > 9 && (
|
||||
<TextField variant="outlined" margin="normal" fullWidth name="fakeID" label="fakeID" id="fakeID" autoComplete="fakeID" value={fakeID} onChange={handleChange} />
|
||||
)}
|
||||
<Button type="submit" fullWidth variant="contained" color="primary" disabled={!validateForm()} sx={{ mt: 2, mb: 2 }}>
|
||||
{t('signIn')}
|
||||
</Button>
|
||||
<Grid container>
|
||||
<Grid item xs>
|
||||
<Link onClick={() => switchDialog(t('signUp.pwReset'))} variant="body2" href="#" underline="hover" sx={{ cursor: 'pointer' }}>
|
||||
{t('forgotPassword')}
|
||||
</Link>
|
||||
</Grid>
|
||||
</form>
|
||||
</div>
|
||||
<Grid item>
|
||||
<Link onClick={() => switchDialog(t('signUp.signUp'))} variant="body2" href="#" underline="hover" sx={{ cursor: 'pointer' }}>
|
||||
{t('signUp.noAccount')}
|
||||
</Link>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</Container>
|
||||
</DialogContent>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginContext
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
import { Close } from '@mui/icons-material'
|
||||
import LoginContent from './LoginContent'
|
||||
import { useContext, useState } from 'react'
|
||||
import { DialogTitle, IconButton } from '@mui/material'
|
||||
import { Close } from '@mui/icons-material'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import LoginContent from './LoginContent'
|
||||
import { PwResetContent } from './PwResetContent'
|
||||
import { SignUpEditContent } from './SignUpEditContent'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { DialogTitle, IconButton } from '@mui/material'
|
||||
|
||||
export default function LoginDialog(props) {
|
||||
const { handleToken, apiServer, user, view, token } = props
|
||||
export default function LoginDialog({ handleToken, apiServer, user, view, token }) {
|
||||
const dialog = useContext(DialogContext)
|
||||
// console.log('view', view)
|
||||
const [state, setState] = useState(view ? view : 'Login')
|
||||
const { t } = useTranslation('common')
|
||||
const [state, setState] = useState(view || 'Login')
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<DialogTitle id="form-dialog-title">{state}</DialogTitle>
|
||||
<IconButton aria-owns={dialog.open ? 'menu-appbar' : null} aria-haspopup="true" onClick={dialog.onClose} color="inherit" size="large">
|
||||
<IconButton aria-label="close" onClick={dialog.onClose} color="inherit" size="large">
|
||||
<Close />
|
||||
</IconButton>
|
||||
</div>
|
||||
@@ -27,7 +24,6 @@ export default function LoginDialog(props) {
|
||||
{state === 'Login' && <LoginContent handleToken={handleToken} apiServer={apiServer} user={user} setState={setState} />}
|
||||
{(state === t('signUp.signUp') || state === 'EditAccount') && <SignUpEditContent token={token} apiServer={apiServer} user={user} state={state} setState={setState} />}
|
||||
{state === t('signUp.pwReset') && <PwResetContent handleToken={handleToken} apiServer={apiServer} user={user} setState={setState} />}
|
||||
|
||||
<br />
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -2,30 +2,19 @@ import { Button, DialogActions, DialogContent, TextField } from '@mui/material'
|
||||
import { useContext, useState } from 'react'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { pwReset } from '../../api/account'
|
||||
import { pwReset } from '../../api/account.js'
|
||||
|
||||
export const PwResetContent = (props) => {
|
||||
const { apiServer } = props
|
||||
const [formData, setFormData] = useState({ login: '' })
|
||||
export const PwResetContent = ({ apiServer }) => {
|
||||
const [login, setLogin] = useState('')
|
||||
const dialog = useContext(DialogContext)
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const handleChange = (e) => {
|
||||
// console.log(e)
|
||||
let targetKey = e.target.name
|
||||
let targetValue = e.target.value
|
||||
|
||||
setFormData({
|
||||
...formData,
|
||||
[targetKey]: targetValue,
|
||||
})
|
||||
// console.log(formData)
|
||||
}
|
||||
const handleChange = (e) => setLogin(e.target.value)
|
||||
|
||||
const handleSave = async (e) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
await pwReset(apiServer, formData)
|
||||
await pwReset(apiServer, { login })
|
||||
handleClose()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
@@ -34,15 +23,14 @@ export const PwResetContent = (props) => {
|
||||
|
||||
const handleClose = () => {
|
||||
dialog.onClose()
|
||||
setFormData('')
|
||||
setLogin('')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogContent>
|
||||
<TextField required margin="dense" name="login" variant="standard" label={t('email')} type="text" fullWidth value={formData.login} onChange={handleChange} />
|
||||
<TextField required margin="dense" name="login" variant="standard" label={t('email')} type="text" fullWidth value={login} onChange={handleChange} />
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} color="secondary">
|
||||
Cancel
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
import { Button, TextField, DialogActions, DialogContent, Radio, RadioGroup, FormControlLabel, FormControl, FormLabel, Autocomplete } from '@mui/material'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useFetch from '../UseFetch/UseFetch'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { getAccount, checkAccountExist, signUp, signUpEdit } from '../../api/account'
|
||||
|
||||
import { Button, TextField, DialogActions, DialogContent, Radio, RadioGroup, FormControlLabel, FormControl, FormLabel, Autocomplete } from '@mui/material'
|
||||
import { getAccount, checkAccountExist, signUp, signUpEdit } from '../../api/account.js'
|
||||
|
||||
export const SignUpEditContent = ({ token, apiServer, state, setState }) => {
|
||||
const token2 = token === undefined ? '' : token
|
||||
const { data: clubs, loading } = useFetch(apiServer + '/getClubs')
|
||||
const dialog = useContext(DialogContext)
|
||||
const { t } = useTranslation('common')
|
||||
const [pwError, setPwError] = useState(false)
|
||||
const [mailError, setMailError] = useState(false)
|
||||
const [account, setAccount] = useState(null)
|
||||
const dialog = useContext(DialogContext)
|
||||
const isEdit = state === 'EditAccount'
|
||||
const token2 = token || ''
|
||||
const { data: clubs = [], loading } = useFetch(apiServer + '/getClubs')
|
||||
|
||||
const initialFormData = Object.freeze({
|
||||
const initialFormData = {
|
||||
anrede: 'Herr',
|
||||
name: '',
|
||||
vorname: '',
|
||||
@@ -26,24 +22,25 @@ export const SignUpEditContent = ({ token, apiServer, state, setState }) => {
|
||||
passwort: '',
|
||||
passwort2: '',
|
||||
telefon: '',
|
||||
})
|
||||
const [formData, setFormData] = useState(initialFormData)
|
||||
|
||||
const getAccountHandler = async () => {
|
||||
try {
|
||||
const responseData = await getAccount(apiServer, token2)
|
||||
setAccount(responseData[0])
|
||||
setFormData(responseData[0])
|
||||
setState('EditAccount')
|
||||
return responseData
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
const [formData, setFormData] = useState(initialFormData)
|
||||
const [account, setAccount] = useState(null)
|
||||
const [pwError, setPwError] = useState(false)
|
||||
const [mailError, setMailError] = useState(false)
|
||||
|
||||
// Account laden, falls Bearbeitung
|
||||
useEffect(() => {
|
||||
token2 && getAccountHandler()
|
||||
}, [])
|
||||
if (isEdit && token2) {
|
||||
getAccount(apiServer, token2)
|
||||
.then((responseData) => {
|
||||
setAccount(responseData[0])
|
||||
setFormData(responseData[0])
|
||||
setState('EditAccount')
|
||||
})
|
||||
.catch(console.error)
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [isEdit, token2])
|
||||
|
||||
const handleClose = () => {
|
||||
dialog.onClose()
|
||||
@@ -52,93 +49,59 @@ export const SignUpEditContent = ({ token, apiServer, state, setState }) => {
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
account ? setFormData(account) : setFormData(initialFormData)
|
||||
setFormData(account || initialFormData)
|
||||
}
|
||||
|
||||
const handleChange = (e, value) => {
|
||||
// Für Autocomplete
|
||||
if (typeof value === 'string') {
|
||||
setFormData((prev) => ({ ...prev, verein: value }))
|
||||
return
|
||||
}
|
||||
const { name, value: val } = e.target
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: val,
|
||||
}))
|
||||
}
|
||||
|
||||
const checkEmail = async (e) => {
|
||||
e.preventDefault()
|
||||
const mail = e.target.value
|
||||
try {
|
||||
const data = await checkAccountExist(apiServer, mail)
|
||||
setMailError(data === 'true' ? false : true)
|
||||
setMailError(data !== 'true')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
const checkPw = (e) => {
|
||||
// console.log(e)
|
||||
e.preventDefault()
|
||||
let targetValue = e.target.value
|
||||
|
||||
const pw = document.getElementById('pw').value
|
||||
// console.log('pw', pw)
|
||||
if (targetValue !== pw) {
|
||||
console.log('pw unterschiedlich')
|
||||
setPwError(true)
|
||||
} else {
|
||||
console.log('pw ok')
|
||||
setPwError(false)
|
||||
}
|
||||
const pw = formData.passwort
|
||||
const pw2 = e.target.value
|
||||
setPwError(pw !== pw2)
|
||||
}
|
||||
|
||||
const handleChange = (e) => {
|
||||
// console.log(e)
|
||||
const handleSave = async (e) => {
|
||||
e.preventDefault()
|
||||
let targetKey = e.target.name
|
||||
let targetValue = e.target.value
|
||||
|
||||
// Fix for Autocomplete
|
||||
if (e.target?.classList?.contains('MuiAutocomplete-input')) {
|
||||
targetKey = 'verein'
|
||||
}
|
||||
if (e.target?.classList?.contains('MuiAutocomplete-option')) {
|
||||
targetKey = 'verein'
|
||||
targetValue = e.target.innerText
|
||||
}
|
||||
|
||||
setFormData({
|
||||
...formData,
|
||||
[targetKey]: targetValue,
|
||||
})
|
||||
// console.log(formData)
|
||||
}
|
||||
|
||||
const signUpAdd = async (newData) => {
|
||||
try {
|
||||
await signUp(apiServer, newData)
|
||||
console.log('signup saved')
|
||||
if (account) {
|
||||
await signUpEdit(apiServer, token, formData)
|
||||
} else {
|
||||
await signUp(apiServer, formData)
|
||||
}
|
||||
dialog.onClose()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
const signUpEditHandler = async (newData) => {
|
||||
try {
|
||||
await signUpEdit(apiServer, token, newData)
|
||||
console.log('signup edited')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSave = (e) => {
|
||||
e.preventDefault()
|
||||
if (account) {
|
||||
signUpEditHandler(formData)
|
||||
} else {
|
||||
signUpAdd(formData)
|
||||
}
|
||||
dialog.onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
!loading && (
|
||||
<>
|
||||
<DialogContent>
|
||||
<FormControl component="fieldset">
|
||||
<FormLabel component="legend">{t('salutation')}</FormLabel>
|
||||
<RadioGroup row aria-label="gender" name="anrede" value={formData.anrede} onChange={handleChange}>
|
||||
<RadioGroup row name="anrede" value={formData.anrede} onChange={handleChange}>
|
||||
<FormControlLabel value="Frau" control={<Radio color="secondary" />} label={t('woman')} />
|
||||
<FormControlLabel value="Herr" control={<Radio />} label={t('man')} />
|
||||
</RadioGroup>
|
||||
@@ -147,10 +110,11 @@ export const SignUpEditContent = ({ token, apiServer, state, setState }) => {
|
||||
<TextField required margin="dense" name="vorname" variant="standard" label={t('forename')} type="text" fullWidth value={formData.vorname} onChange={handleChange} />
|
||||
<Autocomplete
|
||||
id="club"
|
||||
freeSolo={true}
|
||||
freeSolo
|
||||
options={clubs}
|
||||
value={formData.verein}
|
||||
renderInput={(params) => <TextField required {...params} variant="standard" label={t('club')} margin="dense" onBlur={handleChange} />}
|
||||
onInputChange={(e, value) => setFormData((prev) => ({ ...prev, verein: value }))}
|
||||
renderInput={(params) => <TextField {...params} required variant="standard" label={t('club')} margin="dense" />}
|
||||
/>
|
||||
<TextField required margin="dense" name="ort" variant="standard" label={t('city')} type="text" fullWidth value={formData.ort} onChange={handleChange} />
|
||||
{!isEdit && (
|
||||
@@ -166,10 +130,9 @@ export const SignUpEditContent = ({ token, apiServer, state, setState }) => {
|
||||
value={formData.login}
|
||||
onChange={handleChange}
|
||||
onBlur={checkEmail}
|
||||
helperText={mailError ? 'mail is registered' : ''}
|
||||
helperText={mailError ? t('signUp.mailRegistered') : ''}
|
||||
/>
|
||||
)}
|
||||
{/* // TODO: $('#mail').html('Bitte benutzen Sie die <a href="passwort_vergessen.php">Passwort vergessen</a> Funktion.') */}
|
||||
<TextField
|
||||
required
|
||||
id="pw"
|
||||
@@ -182,7 +145,7 @@ export const SignUpEditContent = ({ token, apiServer, state, setState }) => {
|
||||
fullWidth
|
||||
value={formData.passwort || ''}
|
||||
onChange={handleChange}
|
||||
helperText={pwError ? "passwords doesn't match" : '8 - 30 ' + t('signUp.sign')}
|
||||
helperText={pwError ? t('signUp.pwNoMatch') : '8 - 30 ' + t('signUp.sign')}
|
||||
inputProps={{ minLength: 8, maxLength: 30 }}
|
||||
/>
|
||||
<TextField
|
||||
@@ -197,19 +160,18 @@ export const SignUpEditContent = ({ token, apiServer, state, setState }) => {
|
||||
fullWidth
|
||||
value={formData.passwort2 || ''}
|
||||
onChange={handleChange}
|
||||
helperText={pwError ? "passwords doesn't match" : '8 - 30 ' + t('signUp.sign')}
|
||||
onBlur={checkPw}
|
||||
helperText={pwError ? t('signUp.pwNoMatch') : '8 - 30 ' + t('signUp.sign')}
|
||||
inputProps={{ minLength: 8, maxLength: 30 }}
|
||||
/>
|
||||
<TextField margin="dense" name="telefon" variant="standard" label={t('phone')} type="text" fullWidth value={formData.telefon} onChange={handleChange} />
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button onClick={handleReset} color="secondary">
|
||||
reset
|
||||
{t('reset')}
|
||||
</Button>
|
||||
<Button onClick={handleClose} color="secondary">
|
||||
Cancel
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button disabled={mailError || pwError} onClick={handleSave} variant="outlined" color="primary">
|
||||
OK
|
||||
|
||||
@@ -1,37 +1,49 @@
|
||||
import { PDFDownloadLink } from '@react-pdf/renderer'
|
||||
// import { PDFDownloadLink } from '@react-pdf/renderer'
|
||||
import InvoicePDF from './InvoicePDF'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { savePdf } from '../../api/pdf'
|
||||
import { savePdf } from '../../utilities/PDF'
|
||||
import { pdf } from '@react-pdf/renderer'
|
||||
import Button from '@mui/material/Button'
|
||||
import { Download } from '@mui/icons-material'
|
||||
|
||||
export default function PDF(props) {
|
||||
const { tournament, user, apiServer, token } = props
|
||||
const { t } = useTranslation('common')
|
||||
const filename = 'invoice-' + user?.account + '.pdf'
|
||||
const filename = `invoice-${user?.account}-${tournament?.id}.pdf`
|
||||
|
||||
const savePdfHandler = async (blob, categorie) => {
|
||||
const reader = new FileReader()
|
||||
reader.onloadend = async () => {
|
||||
const base64 = reader.result
|
||||
try {
|
||||
// Richtige Reihenfolge: apiServer, token, base64, categorie, gid, tid
|
||||
await savePdf(apiServer, token, base64, categorie, undefined, tournament.id)
|
||||
console.log('PDF gespeichert')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
const showPDF = async () => {
|
||||
let url = ''
|
||||
try {
|
||||
const blob = await pdf(<InvoicePDF {...props} t={t} />).toBlob()
|
||||
url = URL.createObjectURL(blob)
|
||||
const response = await fetch(url)
|
||||
const blobData = await response.blob()
|
||||
const blobUrl = window.URL.createObjectURL(blobData)
|
||||
const link = document.createElement('a')
|
||||
link.href = blobUrl
|
||||
link.download = filename
|
||||
link.click()
|
||||
blob && savePdf(blob, 'lists', 123, apiServer, token, { tid: tournament?.id, categorie: 'invoice', gid: user?.account })
|
||||
} catch (error) {
|
||||
console.error('Error in download process:', error)
|
||||
} finally {
|
||||
if (url) URL.revokeObjectURL(url)
|
||||
}
|
||||
reader.readAsBinaryString(blob)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<PDFDownloadLink document={<InvoicePDF {...props} t={t} />} fileName={filename}>
|
||||
{/* {showPDF()} */}
|
||||
<Button variant="outlined" onClick={showPDF} className="btn btn-primary" startIcon={<Download />}>
|
||||
Download Rechnung
|
||||
</Button>
|
||||
|
||||
{/* <PDFDownloadLink document={<InvoicePDF {...props} t={t} />} fileName={filename}>
|
||||
{({ blob, url, loading, error }) => {
|
||||
if (blob) savePdfHandler(blob, 'invoice')
|
||||
return loading ? 'Loading document...' : t('registration.download-invoice')
|
||||
}}
|
||||
</PDFDownloadLink>
|
||||
|
||||
</PDFDownloadLink> */}
|
||||
{/* <BlobProvider document={<InvoicePDF {...props} />}>
|
||||
{({ blob, url, loading, error }) => {
|
||||
// Do whatever you need with blob here
|
||||
@@ -39,7 +51,6 @@ export default function PDF(props) {
|
||||
return <div>There's something going on on the fly</div>
|
||||
}}
|
||||
</BlobProvider> */}
|
||||
|
||||
{/* render to document
|
||||
{ReactPDF.render(<MyDocument />, `invoice/{filename}`)} */}
|
||||
{/* render as iframe
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
import useFetch from '../UseFetch/UseFetch'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
|
||||
import { Button, TextField, DialogActions, DialogContent, DialogTitle, Radio, RadioGroup, FormControlLabel, FormControl, FormLabel, InputLabel, MenuItem, Select, Autocomplete } from '@mui/material'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { addParticipant, editParticipant } from '../../api/participants'
|
||||
import { BeltClass } from '../../utilities/Belt'
|
||||
|
||||
const BELTS = ['9. Kyu', '8. Kyu', '7. Kyu', '6. Kyu', '5. Kyu', '4. Kyu', '3. Kyu', '2. Kyu', '1. Kyu', 'DAN']
|
||||
|
||||
export default function AddEditParticipant({ token, apiServer, edit, setEdit, participants }) {
|
||||
const { t } = useTranslation('common')
|
||||
const { data: clubs, loading } = useFetch(apiServer + '/getClubs' + token)
|
||||
const { data: clubs = [], loading } = useFetch(apiServer + '/getClubs' + token)
|
||||
const dialog = useContext(DialogContext)
|
||||
|
||||
const initialFormData = Object.freeze({
|
||||
const initialFormData = {
|
||||
name: '',
|
||||
vorname: '',
|
||||
verein: '',
|
||||
gurt: '',
|
||||
gebDatum: '',
|
||||
geschlecht: 'M',
|
||||
})
|
||||
}
|
||||
const [formData, setFormData] = useState(initialFormData)
|
||||
|
||||
useEffect(() => {
|
||||
edit && setFormData(edit)
|
||||
if (edit) setFormData(edit)
|
||||
}, [edit])
|
||||
|
||||
const handleClose = () => {
|
||||
dialog.onClose()
|
||||
setFormData(initialFormData)
|
||||
setEdit(null)
|
||||
setEdit && setEdit(null)
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
edit ? setFormData(edit) : setFormData(initialFormData)
|
||||
setFormData(edit ? edit : initialFormData)
|
||||
}
|
||||
|
||||
const handleChange = (e) => {
|
||||
let targetKey = e.target.name
|
||||
let targetValue = e.target.value
|
||||
|
||||
// Fix for Autocomplete
|
||||
if (e.target?.classList?.contains('MuiAutocomplete-input')) {
|
||||
targetKey = 'verein'
|
||||
}
|
||||
if (e.target?.classList?.contains('MuiAutocomplete-option')) {
|
||||
targetKey = 'verein'
|
||||
targetValue = e.target.innerText
|
||||
}
|
||||
|
||||
setFormData({
|
||||
...formData,
|
||||
[targetKey]: targetValue,
|
||||
})
|
||||
const { name, value } = e.target
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}))
|
||||
}
|
||||
|
||||
const handleSave = (e) => {
|
||||
const handleClubChange = (event, newValue) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
verein: newValue || '',
|
||||
}))
|
||||
}
|
||||
|
||||
const handleSave = async (e) => {
|
||||
e.preventDefault()
|
||||
if (edit) {
|
||||
const index = participants.findIndex((i) => i.id === edit.id)
|
||||
participants[index] = formData
|
||||
editParticipant(apiServer, token, formData)
|
||||
} else {
|
||||
addParticipant(apiServer, token, formData)
|
||||
try {
|
||||
if (edit) {
|
||||
await editParticipant(apiServer, token, formData)
|
||||
} else {
|
||||
await addParticipant(apiServer, token, formData)
|
||||
}
|
||||
dialog.onClose()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
dialog.onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -75,45 +75,22 @@ export default function AddEditParticipant({ token, apiServer, edit, setEdit, pa
|
||||
<TextField margin="dense" name="vorname" variant="standard" label={t('participant.forename')} type="text" fullWidth value={formData.vorname} onChange={handleChange} />
|
||||
<Autocomplete
|
||||
id="club"
|
||||
freeSolo={true}
|
||||
freeSolo
|
||||
options={clubs}
|
||||
value={formData.verein}
|
||||
renderInput={(params) => <TextField {...params} variant="standard" label={t('participant.club')} margin="dense" onBlur={handleChange} />}
|
||||
onChange={handleClubChange}
|
||||
renderInput={(params) => <TextField {...params} variant="standard" label={t('participant.club')} margin="dense" />}
|
||||
/>
|
||||
<FormControl variant="standard" style={{ minWidth: '100%' }}>
|
||||
<FormControl variant="standard" fullWidth margin="dense">
|
||||
<InputLabel id="gurt-label">{t('participant.belt')}</InputLabel>
|
||||
<Select labelId="gurt-label" id="gurt" name="gurt" value={formData.gurt} onChange={handleChange}>
|
||||
<MenuItem value={'9. Kyu'}>9. Kyu</MenuItem>
|
||||
<MenuItem className="yellow-belt" value={'8. Kyu'}>
|
||||
8. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="orange-belt" value={'7. Kyu'}>
|
||||
7. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="green-belt" value={'6. Kyu'}>
|
||||
6. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="violet-belt" value={'5. Kyu'}>
|
||||
5. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="violet-belt" value={'4. Kyu'}>
|
||||
4. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="brown-belt" value={'3. Kyu'}>
|
||||
3. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="brown-belt" value={'2. Kyu'}>
|
||||
2. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="brown-belt" value={'1. Kyu'}>
|
||||
1. Kyu
|
||||
</MenuItem>
|
||||
<MenuItem className="black-belt" value={'DAN'}>
|
||||
DAN
|
||||
</MenuItem>
|
||||
{BELTS.map((belt) => (
|
||||
<MenuItem key={belt} value={belt} className={BeltClass(belt)}>
|
||||
{belt}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<TextField
|
||||
margin="dense"
|
||||
name="gebDatum"
|
||||
@@ -125,9 +102,9 @@ export default function AddEditParticipant({ token, apiServer, edit, setEdit, pa
|
||||
value={formData.gebDatum}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<FormControl component="fieldset">
|
||||
<FormControl component="fieldset" margin="dense">
|
||||
<FormLabel component="legend">{t('participant.gender')}</FormLabel>
|
||||
<RadioGroup aria-label="gender" name="geschlecht" value={formData.geschlecht} onChange={handleChange}>
|
||||
<RadioGroup aria-label="gender" name="geschlecht" value={formData.geschlecht} onChange={handleChange} row>
|
||||
<FormControlLabel value="W" control={<Radio />} label={t('participant.female')} />
|
||||
<FormControlLabel value="M" control={<Radio color="primary" />} label={t('participant.male')} />
|
||||
</RadioGroup>
|
||||
@@ -135,10 +112,10 @@ export default function AddEditParticipant({ token, apiServer, edit, setEdit, pa
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleReset} color="secondary">
|
||||
reset
|
||||
{t('reset')}
|
||||
</Button>
|
||||
<Button onClick={handleClose} color="secondary">
|
||||
Cancel
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button onClick={handleSave} variant="outlined" color="primary">
|
||||
OK
|
||||
|
||||
@@ -1,32 +1,26 @@
|
||||
import { useContext } from 'react'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
|
||||
import { Button, DialogActions, DialogTitle } from '@mui/material'
|
||||
|
||||
export default function DeleteDialog(props) {
|
||||
const { deleteParticipant, data, setData, participant } = props
|
||||
export default function DeleteDialog({ deleteParticipant, data, setData, participant }) {
|
||||
const dialog = useContext(DialogContext)
|
||||
|
||||
const deleteRow = () => {
|
||||
const participantRemoved = data.filter((x) => {
|
||||
return x.id != participant.id
|
||||
})
|
||||
setData(participantRemoved)
|
||||
const handleDelete = () => {
|
||||
setData(data.filter((x) => x.id !== participant.id))
|
||||
deleteParticipant(participant.id)
|
||||
dialog.onClose()
|
||||
}
|
||||
console.log('participant', participant)
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogTitle>
|
||||
Do you want to delete {participant.vorname} {participant.name}
|
||||
Do you want to delete {participant.vorname} {participant.name}?
|
||||
</DialogTitle>
|
||||
<DialogActions>
|
||||
<Button onClick={dialog.onClose} color="primary">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={deleteRow} color="primary" autoFocus>
|
||||
<Button onClick={handleDelete} color="primary" autoFocus>
|
||||
Delete
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import AddEditParticipant from './AddEditParticipant'
|
||||
import ParticipantsMobile from './ParticipantsMobile'
|
||||
import { useContext, useState } from 'react'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import { addParticipant, editParticipant, deleteParticipant } from '../../api/participants'
|
||||
|
||||
import { styled, Button } from '@mui/material'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import AddEditParticipant from './AddEditParticipant'
|
||||
import ParticipantsMobile from './ParticipantsMobile'
|
||||
import { addParticipant, editParticipant, deleteParticipant } from '../../api/participants'
|
||||
|
||||
const PREFIX = 'Participants'
|
||||
|
||||
const classes = {
|
||||
participantsContainer: `${PREFIX}-participantsContainer`,
|
||||
}
|
||||
@@ -22,19 +20,16 @@ const Root = styled('div')(({ theme }) => ({
|
||||
},
|
||||
}))
|
||||
|
||||
export default function Participants(props) {
|
||||
const { apiServer, token, participants } = props
|
||||
export default function Participants({ apiServer, token, participants }) {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// TODO: statt data direkt participants nutzen
|
||||
const [data, setData] = useState(participants)
|
||||
const [edit, setEdit] = useState(null)
|
||||
const dialog = useContext(DialogContext)
|
||||
|
||||
const handleAdd = async (newData) => {
|
||||
try {
|
||||
await addParticipant(apiServer, token, newData)
|
||||
setData([...data, newData])
|
||||
const added = await addParticipant(apiServer, token, newData)
|
||||
setData((prev) => [...prev, added])
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
@@ -42,8 +37,8 @@ export default function Participants(props) {
|
||||
|
||||
const handleEdit = async (newData) => {
|
||||
try {
|
||||
await editParticipant(apiServer, token, newData)
|
||||
// ggf. data aktualisieren
|
||||
const updated = await editParticipant(apiServer, token, newData)
|
||||
setData((prev) => prev.map((item) => (item.id === updated.id ? updated : item)))
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
@@ -52,7 +47,7 @@ export default function Participants(props) {
|
||||
const handleDelete = async (id) => {
|
||||
try {
|
||||
await deleteParticipant(apiServer, token, id)
|
||||
// ggf. data aktualisieren
|
||||
setData((prev) => prev.filter((item) => item.id !== id))
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
@@ -72,27 +67,24 @@ export default function Participants(props) {
|
||||
/>,
|
||||
)
|
||||
dialog.setOpen()
|
||||
// console.log('participants ', dialog.open, dialog.content)
|
||||
}
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<h3>{t('headline.participants')}</h3>
|
||||
{participants && (
|
||||
<div className={classes.participantsContainer}>
|
||||
<ParticipantsMobile
|
||||
data={data}
|
||||
setData={setData}
|
||||
deleteParticipant={handleDelete}
|
||||
addParticipant={handleAdd}
|
||||
editParticipant={handleEdit}
|
||||
token={token}
|
||||
apiServer={apiServer}
|
||||
edit={edit}
|
||||
setEdit={setEdit}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.participantsContainer}>
|
||||
<ParticipantsMobile
|
||||
data={data}
|
||||
setData={setData}
|
||||
deleteParticipant={handleDelete}
|
||||
addParticipant={handleAdd}
|
||||
editParticipant={handleEdit}
|
||||
token={token}
|
||||
apiServer={apiServer}
|
||||
edit={edit}
|
||||
setEdit={setEdit}
|
||||
/>
|
||||
</div>
|
||||
<br />
|
||||
<Button variant="contained" color="primary" onClick={handleDialog}>
|
||||
{t('btn.add-participants')}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import { useContext, useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Avatar, Chip, FormControlLabel, IconButton, styled, Switch, Pagination } from '@mui/material'
|
||||
import { DeleteOutline, Edit } from '@mui/icons-material'
|
||||
import PersonIcon from '@mui/icons-material/Person'
|
||||
import { useContext } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { beltColor } from '../../utilities/Belt'
|
||||
import { getAge } from '../../utilities/Date'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import AddEditParticipant from './AddEditParticipant'
|
||||
import DeleteDialog from './DeleteDialog'
|
||||
import { useState } from 'react'
|
||||
import SearchBar from '../Common/SearchBar'
|
||||
import { Avatar, Chip, FormControlLabel, IconButton, styled, Switch, Pagination } from '@mui/material'
|
||||
|
||||
const PREFIX = 'ParticipantsMobile'
|
||||
|
||||
const classes = {
|
||||
participants: `${PREFIX}-participants`,
|
||||
participantsIcon: `${PREFIX}-participantsIcon`,
|
||||
@@ -107,25 +105,21 @@ const Root = styled('div')(({ theme }) => ({
|
||||
},
|
||||
}))
|
||||
|
||||
export default function ParticipantsMobile(props) {
|
||||
const { action, checkSingleRegistered, countSingle, deleteParticipant, data, setData, setEdit, addParticipant, editParticipant, token, apiServer, switchRef } = props
|
||||
export default function ParticipantsMobile({ action, checkSingleRegistered, countSingle, deleteParticipant, data, setData, setEdit, addParticipant, editParticipant, token, apiServer, switchRef }) {
|
||||
const dialog = useContext(DialogContext)
|
||||
const { tid } = useParams()
|
||||
const [page, setPage] = useState(1)
|
||||
const rowsPerPage = 10 // Anzahl der Einträge pro Seite
|
||||
const [search, setSearch] = useState('')
|
||||
const rowsPerPage = 10
|
||||
|
||||
const handleDeleteDialogOpen = (el) => {
|
||||
dialog.setContent(<DeleteDialog deleteParticipant={deleteParticipant} data={data} setData={setData} participant={el} />)
|
||||
const handleDeleteDialogOpen = (participant) => {
|
||||
dialog.setContent(<DeleteDialog deleteParticipant={deleteParticipant} data={data} setData={setData} participant={participant} />)
|
||||
dialog.setOpen()
|
||||
}
|
||||
|
||||
// console.log('dialog', dialog);
|
||||
const handleEditClick = (el) => {
|
||||
const date = new Date(el.gebDatum).toLocaleDateString('en-CA')
|
||||
el.gebDatum = date
|
||||
setEdit(el)
|
||||
|
||||
const handleEditClick = (participant) => {
|
||||
const date = new Date(participant.gebDatum).toLocaleDateString('en-CA')
|
||||
setEdit({ ...participant, gebDatum: date })
|
||||
dialog.setContent(
|
||||
<AddEditParticipant
|
||||
participants={data}
|
||||
@@ -134,11 +128,10 @@ export default function ParticipantsMobile(props) {
|
||||
editParticipant={editParticipant}
|
||||
token={token}
|
||||
apiServer={apiServer}
|
||||
edit={el}
|
||||
edit={{ ...participant, gebDatum: date }}
|
||||
setEdit={setEdit}
|
||||
/>,
|
||||
)
|
||||
// console.log('el', el);
|
||||
dialog.setOpen()
|
||||
}
|
||||
|
||||
@@ -153,7 +146,7 @@ export default function ParticipantsMobile(props) {
|
||||
const paginatedData = filteredData.slice((page - 1) * rowsPerPage, page * rowsPerPage)
|
||||
|
||||
return (
|
||||
<Root className={classes.root}>
|
||||
<Root>
|
||||
<div style={{ display: 'flex', justifyContent: 'end', margin: '0.5em 0' }}>
|
||||
<SearchBar
|
||||
value={search}
|
||||
@@ -166,17 +159,15 @@ export default function ParticipantsMobile(props) {
|
||||
</div>
|
||||
{paginatedData.map((el, index) => {
|
||||
const gender = el.geschlecht === 'M' ? 'primary' : 'secondary'
|
||||
|
||||
return (
|
||||
<div key={el.id || index} data-pid={el.id} className={classes.participants}>
|
||||
<div className={classes.participantsIcon}>
|
||||
<PersonIcon color={gender} />
|
||||
{el.id}
|
||||
</div>
|
||||
|
||||
<div className={classes.participantsInfo}>
|
||||
<div className={classes.chips}>
|
||||
<Chip size="small" label={el.gurt} className={classes.chip} style={{ backgroundColor: `${beltColor(el.gurt)}` }}></Chip>
|
||||
<Chip size="small" label={el.gurt} className={classes.chip} style={{ backgroundColor: beltColor(el.gurt) }} />
|
||||
<Chip size="small" className={classes.chip} label={new Date(el.gebDatum).toLocaleDateString()} avatar={<Avatar>{getAge(el.gebDatum).toString()}</Avatar>} />
|
||||
</div>
|
||||
<div className={classes.name}>
|
||||
@@ -187,19 +178,17 @@ export default function ParticipantsMobile(props) {
|
||||
<i>{el.verein}</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{!tid && (
|
||||
<div className={classes.actions}>
|
||||
<IconButton color="inherit" onClick={handleEditClick.bind(this, el)} size="large">
|
||||
<IconButton color="inherit" onClick={() => handleEditClick(el)} size="large">
|
||||
<Edit />
|
||||
</IconButton>
|
||||
<IconButton color="inherit" onClick={handleDeleteDialogOpen.bind(this, el)} size="large">
|
||||
<IconButton color="inherit" onClick={() => handleDeleteDialogOpen(el)} size="large">
|
||||
<DeleteOutline />
|
||||
</IconButton>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{countSingle && (
|
||||
<>
|
||||
<FormControlLabel
|
||||
|
||||
@@ -53,6 +53,9 @@ const Root = styled('table')(({ theme }) => ({
|
||||
|
||||
export default function Invoice({ countSingle, countTeams, singleCosts, teamCosts }) {
|
||||
const { t } = useTranslation('common')
|
||||
const sumSingle = countSingle * singleCosts
|
||||
const sumTeams = countTeams * teamCosts
|
||||
const total = sumSingle + sumTeams
|
||||
|
||||
return (
|
||||
<TableWrapper>
|
||||
@@ -68,13 +71,13 @@ export default function Invoice({ countSingle, countTeams, singleCosts, teamCost
|
||||
<td>{t('registration.single-registrations')}</td>
|
||||
<td>{countSingle}</td>
|
||||
<td>{singleCosts} €</td>
|
||||
<td>{countSingle * singleCosts} €</td>
|
||||
<td>{sumSingle} €</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t('registration.team-registrations')}</td>
|
||||
<td>{countTeams}</td>
|
||||
<td>{teamCosts} €</td>
|
||||
<td>{countTeams * teamCosts} €</td>
|
||||
<td>{sumTeams} €</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -83,7 +86,7 @@ export default function Invoice({ countSingle, countTeams, singleCosts, teamCost
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>
|
||||
<strong>{countSingle * singleCosts + countTeams * teamCosts} €</strong>
|
||||
<strong>{total} €</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { Add, Remove } from '@mui/icons-material'
|
||||
import PersonIcon from '@mui/icons-material/Person'
|
||||
import { useRef } from 'react'
|
||||
|
||||
import { Avatar, Chip, IconButton, styled } from '@mui/material'
|
||||
|
||||
const PREFIX = 'TeamRegistrationMobile'
|
||||
|
||||
const classes = {
|
||||
teams: `${PREFIX}-teams`,
|
||||
disziplin: `${PREFIX}-disziplin`,
|
||||
teamsIcon: `${PREFIX}-teamsIcon`,
|
||||
teamsInfo: `${PREFIX}-teamsInfo`,
|
||||
counter: `${PREFIX}-counter`,
|
||||
actions: `${PREFIX}-actions`,
|
||||
}
|
||||
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
@@ -29,67 +28,59 @@ const Root = styled('div')(({ theme }) => ({
|
||||
marginBottom: '0.3em',
|
||||
padding: '0.3em',
|
||||
borderRadius: '0.3em',
|
||||
gap: '1em',
|
||||
},
|
||||
[`& .${classes.teamsIcon}`]: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
minWidth: 50,
|
||||
},
|
||||
[`& .${classes.teamsInfo}`]: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '1em',
|
||||
minWidth: 180,
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
display: 'block',
|
||||
minWidth: 0,
|
||||
},
|
||||
},
|
||||
[`& .${classes.disziplin}`]: {
|
||||
fontWeight: 'bold',
|
||||
minWidth: '8em',
|
||||
display: 'inline-block',
|
||||
},
|
||||
[`& .${classes.teamsIcon}`]: {
|
||||
[`& .${classes.counter}`]: {
|
||||
minWidth: 40,
|
||||
textAlign: 'center',
|
||||
fontWeight: 600,
|
||||
fontSize: '1.1em',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
[`& .${classes.teamsInfo}`]: {
|
||||
[`& .${classes.actions}`]: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
display: 'block',
|
||||
},
|
||||
gap: '0.3em',
|
||||
},
|
||||
}))
|
||||
|
||||
export default function TeamRegistrationMobile(props) {
|
||||
const { checkTeamsRegistered, teamGroups, register, setSnackMessage, account, snackOpen, tooLate } = props
|
||||
const counterRef = useRef([])
|
||||
|
||||
const deRegisterTeam = (id) => {
|
||||
export default function TeamRegistrationMobile({ checkTeamsRegistered, teamGroups, register, setSnackMessage, account, snackOpen, tooLate }) {
|
||||
const handleRegister = (id, type) => {
|
||||
if (tooLate) {
|
||||
setSnackMessage('too late')
|
||||
snackOpen()
|
||||
return
|
||||
}
|
||||
const countField = counterRef.current[id]
|
||||
let counter = parseInt(countField.innerHTML)
|
||||
if (counter <= 0) {
|
||||
counter = 0
|
||||
} else {
|
||||
register({
|
||||
register: '/deregisterTeam',
|
||||
teamName: account[0].verein,
|
||||
turniergruppeId: id,
|
||||
})
|
||||
setSnackMessage(`Team ${account[0].verein} deregistered for group ${id}`)
|
||||
counter--
|
||||
}
|
||||
countField.innerHTML = counter
|
||||
}
|
||||
|
||||
const registerTeam = (id) => {
|
||||
if (tooLate) {
|
||||
setSnackMessage('too late')
|
||||
snackOpen()
|
||||
return
|
||||
}
|
||||
const countField = counterRef.current[id]
|
||||
let counter = parseInt(countField.innerHTML) + 1
|
||||
const action = type === 'register' ? '/registerTeam' : '/deregisterTeam'
|
||||
register({
|
||||
register: '/registerTeam',
|
||||
register: action,
|
||||
teamName: account[0].verein,
|
||||
turniergruppeId: id,
|
||||
})
|
||||
setSnackMessage(`Team ${account[0].verein} registered for group ${id}`)
|
||||
countField.innerHTML = counter
|
||||
setSnackMessage(`Team ${account[0].verein} ${type === 'register' ? 'registered' : 'deregistered'} for group ${id}`)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -98,26 +89,19 @@ export default function TeamRegistrationMobile(props) {
|
||||
<div key={el.id} className={classes.teams}>
|
||||
<div className={classes.teamsIcon}>
|
||||
<PersonIcon color={el.geschlecht === 'M' ? 'primary' : 'secondary'} />
|
||||
{el.id}
|
||||
<span>{el.id}</span>
|
||||
</div>
|
||||
|
||||
<div className={classes.teamsInfo}>
|
||||
<span className={classes.disziplin}>{el.disziplin}</span>
|
||||
<div>
|
||||
<Chip size="small" label={el.gurtVon + ' - ' + el.gurtBis} style={{ margin: '0 1em' }} />
|
||||
<Chip size="small" label={el.altervon + ' - ' + el.alterbis} avatar={<Avatar>age</Avatar>} />
|
||||
</div>
|
||||
<Chip size="small" label={`${el.gurtVon} - ${el.gurtBis}`} style={{ margin: '0 1em' }} />
|
||||
<Chip size="small" label={`${el.altervon} - ${el.alterbis}`} avatar={<Avatar>age</Avatar>} />
|
||||
</div>
|
||||
|
||||
<div className="counter" ref={(elem) => (counterRef.current[el.id] = elem)}>
|
||||
{checkTeamsRegistered(el.id)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<IconButton aria-label="deregister Team" onClick={deRegisterTeam.bind(this, el.id)} size="large">
|
||||
<div className={classes.counter}>{checkTeamsRegistered(el.id)}</div>
|
||||
<div className={classes.actions}>
|
||||
<IconButton aria-label="deregister Team" onClick={() => handleRegister(el.id, 'deregister')} size="large">
|
||||
<Remove />
|
||||
</IconButton>
|
||||
<IconButton aria-label="register Team" onClick={registerTeam.bind(this, el.id)} size="large">
|
||||
<IconButton aria-label="register Team" onClick={() => handleRegister(el.id, 'register')} size="large">
|
||||
<Add />
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
@@ -1,37 +1,31 @@
|
||||
import { TextField, Button } from '@mui/material'
|
||||
import { useState } from 'react'
|
||||
import { TextField, Button } from '@mui/material'
|
||||
import { addResults } from '../../api/results'
|
||||
|
||||
export default function PostResult({ apiServer, token, groups, setGroups }) {
|
||||
const initialFormData = Object.freeze({
|
||||
gid: '',
|
||||
p1: '',
|
||||
p2: '',
|
||||
p3: '',
|
||||
p4: '',
|
||||
})
|
||||
const initialFormData = { gid: '', p1: '', p2: '', p3: '', p4: '' }
|
||||
const [formData, setFormData] = useState(initialFormData)
|
||||
|
||||
const handleChange = (e) => {
|
||||
// console.log(e.target);
|
||||
let targetKey = e.target.name
|
||||
let targetValue = e.target.value
|
||||
|
||||
setFormData({
|
||||
...formData,
|
||||
[targetKey]: targetValue,
|
||||
})
|
||||
const { name, value } = e.target
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}))
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await addResults(apiServer, token, formData)
|
||||
console.log('Ergebnisse hinzugefügt')
|
||||
|
||||
const groupIndex = groups.findIndex((x) => x.id == formData.gid)
|
||||
const escapedFormdata = JSON.stringify(formData)
|
||||
groups[groupIndex]['results'] = escapedFormdata
|
||||
setGroups([...groups]) // Array-Kopie für sauberes State-Update
|
||||
const groupIndex = groups.findIndex((x) => String(x.id) === String(formData.gid))
|
||||
if (groupIndex !== -1) {
|
||||
const updatedGroups = [...groups]
|
||||
updatedGroups[groupIndex] = {
|
||||
...updatedGroups[groupIndex],
|
||||
results: JSON.stringify(formData),
|
||||
}
|
||||
setGroups(updatedGroups)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { styled } from '@mui/material'
|
||||
const PREFIX = 'ResultsEntry'
|
||||
|
||||
const PREFIX = 'ResultsEntry'
|
||||
const classes = {
|
||||
place: `${PREFIX}-place`,
|
||||
participant: `${PREFIX}-participant`,
|
||||
name: `${PREFIX}-name`,
|
||||
}
|
||||
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
[`&`]: {
|
||||
backgroundColor: 'rgba(0,0,0,0.2)',
|
||||
margin: '0.2rem 0',
|
||||
padding: '0 0.4rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
const Root = styled('div')({
|
||||
backgroundColor: 'rgba(0,0,0,0.2)',
|
||||
margin: '0.2rem 0',
|
||||
padding: '0 0.4rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
[`& .${classes.place}`]: {
|
||||
backgroundColor: 'rgba(0,0,0,0.2)',
|
||||
padding: '0.2rem 0.6rem',
|
||||
@@ -24,38 +22,21 @@ const Root = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
},
|
||||
[`& .${classes.name}`]: {
|
||||
fontWeight: '600',
|
||||
fontWeight: 600,
|
||||
marginRight: '0.4rem',
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
export default function ResultsEntry(props) {
|
||||
const { p, place, team } = props
|
||||
export default function ResultsEntry({ p, place, team }) {
|
||||
if (!p?.id || p.id <= 0) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
{p?.id > 0 && (
|
||||
<Root>
|
||||
<div className={classes.place}>{place}</div>
|
||||
|
||||
{/* Participants */}
|
||||
{!team && (
|
||||
<div className={classes.participant}>
|
||||
<div className={classes.name}>
|
||||
{p?.vorname} {p?.name}
|
||||
</div>
|
||||
<div>[{p?.verein}]</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Teams */}
|
||||
{team && (
|
||||
<div className={classes.participant}>
|
||||
<div className={classes.name}>{p?.teamName}</div>
|
||||
</div>
|
||||
)}
|
||||
</Root>
|
||||
)}
|
||||
</>
|
||||
<Root>
|
||||
<div className={classes.place}>{place}</div>
|
||||
<div className={classes.participant}>
|
||||
<div className={classes.name}>{team ? p?.teamName : `${p?.vorname} ${p?.name}`}</div>
|
||||
{!team && <div>[{p?.verein}]</div>}
|
||||
</div>
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import ResultsEntry from './ResultsEntry'
|
||||
import { Chip, styled, Avatar } from '@mui/material'
|
||||
const PREFIX = 'ResultsEntryContainer'
|
||||
|
||||
const PREFIX = 'ResultsEntryContainer'
|
||||
const classes = {
|
||||
groupResult: `${PREFIX}-groupResult`,
|
||||
groupHeader: `${PREFIX}-groupHeader`,
|
||||
@@ -33,25 +33,27 @@ const Root = styled('div')(({ theme }) => ({
|
||||
},
|
||||
}))
|
||||
|
||||
export default function ResultsEntryContainer(props) {
|
||||
const { el, team, place, user } = props
|
||||
|
||||
export default function ResultsEntryContainer({ el, team, place, user }) {
|
||||
const gender = el.geschlecht === 'M' ? 'primary' : 'secondary'
|
||||
const places = [
|
||||
{ p: place.p1, place: 1 },
|
||||
{ p: place.p2, place: 2 },
|
||||
{ p: place.p3, place: 3 },
|
||||
{ p: place.p4, place: 3 },
|
||||
]
|
||||
|
||||
return (
|
||||
<Root className={classes.groupResult}>
|
||||
<div className={classes.groupHeader}>
|
||||
<Chip className={classes.chip} color={gender} label={el.disziplin} avatar={<Avatar>{el.gid}</Avatar>}></Chip>
|
||||
<Chip className={classes.chip} label={el.altervon + ' - ' + el.alterbis} avatar={<Avatar>age</Avatar>}></Chip>
|
||||
<Chip className={classes.chip} label={el.gurtVon + ' - ' + el.gurtBis}></Chip>
|
||||
<Chip className={classes.chip} color={gender} label={el.disziplin} avatar={<Avatar>{el.gid}</Avatar>} />
|
||||
<Chip className={classes.chip} label={`${el.altervon} - ${el.alterbis}`} avatar={<Avatar>age</Avatar>} />
|
||||
<Chip className={classes.chip} label={`${el.gurtVon} - ${el.gurtBis}`} />
|
||||
</div>
|
||||
|
||||
<div className={classes.groupBody}>
|
||||
{user?.admin > 2 && <span>{el.id}</span>}
|
||||
<ResultsEntry p={place.p1} place={1} team={team} />
|
||||
<ResultsEntry p={place.p2} place={2} team={team} />
|
||||
<ResultsEntry p={place.p3} place={3} team={team} />
|
||||
<ResultsEntry p={place.p4} place={3} team={team} />
|
||||
{places.map(({ p, place }, idx) => (
|
||||
<ResultsEntry key={idx} p={p} place={place} team={team} />
|
||||
))}
|
||||
</div>
|
||||
</Root>
|
||||
)
|
||||
|
||||
@@ -1,37 +1,22 @@
|
||||
import ResultsEntryContainer from '../Results/ResultsEntryContainer'
|
||||
|
||||
export default function TournamentResults({ groups, user, participants, teams }) {
|
||||
const result = []
|
||||
const getParticipant = (id) => {
|
||||
return participants.find((x) => x.id == id)
|
||||
}
|
||||
const getTeams = (id) => {
|
||||
return teams.find((x) => x.id == id)
|
||||
}
|
||||
const getParticipant = (id) => participants.find((x) => x.id == id)
|
||||
const getTeam = (id) => teams.find((x) => x.id == id)
|
||||
|
||||
groups.forEach((el) => {
|
||||
if (el.platz1 || el.platz1team > 0 || el.results) {
|
||||
let place, team
|
||||
const parseResults = el.results && JSON.parse(el.results)
|
||||
if (el.disziplin.includes('Team')) {
|
||||
team = true
|
||||
place = {
|
||||
p1: el.results ? getTeams(parseResults.p1) : getTeams(el.platz1team),
|
||||
p2: el.results ? getTeams(parseResults.p2) : getTeams(el.platz2team),
|
||||
p3: el.results ? getTeams(parseResults.p3) : getTeams(el.platz3team),
|
||||
p4: el.results ? getTeams(parseResults.p4) : getTeams(el.platz4team),
|
||||
}
|
||||
} else {
|
||||
team = false
|
||||
place = {
|
||||
p1: el.results ? getParticipant(parseResults.p1) : getParticipant(el.platz1),
|
||||
p2: el.results ? getParticipant(parseResults.p2) : getParticipant(el.platz2),
|
||||
p3: el.results ? getParticipant(parseResults.p3) : getParticipant(el.platz3),
|
||||
p4: el.results ? getParticipant(parseResults.p4) : getParticipant(el.platz4),
|
||||
}
|
||||
return groups
|
||||
.filter((el) => el.platz1 || el.platz1team > 0 || el.results)
|
||||
.map((el) => {
|
||||
const isTeam = el.disziplin?.includes('Team')
|
||||
const results = el.results ? JSON.parse(el.results) : null
|
||||
|
||||
const place = {
|
||||
p1: results ? (isTeam ? getTeam(results.p1) : getParticipant(results.p1)) : isTeam ? getTeam(el.platz1team) : getParticipant(el.platz1),
|
||||
p2: results ? (isTeam ? getTeam(results.p2) : getParticipant(results.p2)) : isTeam ? getTeam(el.platz2team) : getParticipant(el.platz2),
|
||||
p3: results ? (isTeam ? getTeam(results.p3) : getParticipant(results.p3)) : isTeam ? getTeam(el.platz3team) : getParticipant(el.platz3),
|
||||
p4: results ? (isTeam ? getTeam(results.p4) : getParticipant(results.p4)) : isTeam ? getTeam(el.platz4team) : getParticipant(el.platz4),
|
||||
}
|
||||
result.push(<ResultsEntryContainer key={el.id} el={el} team={team} place={place} user={user} />)
|
||||
}
|
||||
})
|
||||
return result
|
||||
|
||||
return <ResultsEntryContainer key={el.id} el={el} team={isTeam} place={place} user={user} />
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,7 +2,15 @@ import { Box, CircularProgress } from '@mui/material'
|
||||
|
||||
export default function Spinner() {
|
||||
return (
|
||||
<Box sx={{ display: 'flex', minWidth: '100%', minHeight: '100vh', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
minWidth: '100%',
|
||||
minHeight: '100vh',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<CircularProgress size={60} />
|
||||
</Box>
|
||||
)
|
||||
|
||||
@@ -5,13 +5,11 @@ import { useTranslation } from 'react-i18next'
|
||||
import { addTournament, editTournament } from '../../api/tournaments'
|
||||
|
||||
const PREFIX = 'AddEditTournament'
|
||||
|
||||
const classes = {
|
||||
formGroup: `${PREFIX}-formGroup`,
|
||||
halfSize: `${PREFIX}-halfSize`,
|
||||
}
|
||||
|
||||
// TODO jss-to-styled codemod: The Fragment root was replaced by div. Change the tag if needed.
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
[`& .${classes.formGroup}`]: {
|
||||
margin: '1rem 0',
|
||||
@@ -43,15 +41,25 @@ const initialFormData = Object.freeze({
|
||||
streams: '',
|
||||
})
|
||||
|
||||
export default function AddEditTournament(props) {
|
||||
const { token, apiServer, tournament, tournaments, setTournaments } = props
|
||||
const { t } = useTranslation('common')
|
||||
function toDateInput(val) {
|
||||
if (!val) return ''
|
||||
// Falls schon im richtigen Format, zurückgeben
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(val)) return val
|
||||
try {
|
||||
return new Date(val).toISOString().slice(0, 10)
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export default function AddEditTournament({ token, apiServer, tournament, tournaments, setTournaments }) {
|
||||
const { t } = useTranslation('common')
|
||||
const [formData, setFormData] = useState(initialFormData)
|
||||
const dialog = useContext(DialogContext)
|
||||
|
||||
useEffect(() => {
|
||||
tournament && setFormData(tournament)
|
||||
if (tournament) setFormData(tournament)
|
||||
else setFormData(initialFormData)
|
||||
}, [tournament])
|
||||
|
||||
const handleClose = () => {
|
||||
@@ -60,28 +68,23 @@ export default function AddEditTournament(props) {
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
tournament ? setFormData(tournament) : setFormData(initialFormData)
|
||||
setFormData(tournament ? tournament : initialFormData)
|
||||
}
|
||||
|
||||
const handleChange = (e) => {
|
||||
// console.log(e.target);
|
||||
const targetKey = e.target.name
|
||||
const targetValue = e.target.value
|
||||
|
||||
setFormData({
|
||||
...formData,
|
||||
[targetKey]: targetValue,
|
||||
})
|
||||
const { name, value } = e.target
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
}))
|
||||
}
|
||||
|
||||
const handleSave = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
try {
|
||||
if (tournament) {
|
||||
const index = tournaments.findIndex((i) => i.id === tournament.id)
|
||||
tournaments[index] = formData
|
||||
setTournaments([...tournaments])
|
||||
const updatedTournaments = tournaments.map((i) => (i.id === tournament.id ? formData : i))
|
||||
setTournaments(updatedTournaments)
|
||||
await editTournament(apiServer, token, formData)
|
||||
} else {
|
||||
setTournaments([...tournaments, formData])
|
||||
@@ -109,7 +112,7 @@ export default function AddEditTournament(props) {
|
||||
InputLabelProps={{ shrink: true }}
|
||||
label={t('date')}
|
||||
type="date"
|
||||
value={new Date(formData.veranstaltungsDatum).toLocaleDateString('en-CA')}
|
||||
value={toDateInput(formData.veranstaltungsDatum)}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
@@ -120,7 +123,7 @@ export default function AddEditTournament(props) {
|
||||
InputLabelProps={{ shrink: true }}
|
||||
label={t('closing-date')}
|
||||
type="date"
|
||||
value={new Date(formData.meldeschluss).toLocaleDateString('en-CA')}
|
||||
value={toDateInput(formData.meldeschluss)}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
@@ -172,10 +175,10 @@ export default function AddEditTournament(props) {
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleReset} color="secondary">
|
||||
reset
|
||||
{t('reset')}
|
||||
</Button>
|
||||
<Button onClick={handleClose} color="secondary">
|
||||
Cancel
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
<Button onClick={handleSave} variant="outlined" color="primary">
|
||||
OK
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import { useContext } from 'react'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import { deleteTournament } from '../../api/tournaments'
|
||||
|
||||
import { Button, DialogActions, DialogTitle } from '@mui/material'
|
||||
|
||||
export default function DeleteTournamentDialog(props) {
|
||||
const { apiServer, token, tid, tournaments, setTournaments } = props
|
||||
export default function DeleteTournamentDialog({ apiServer, token, tid, tournaments, setTournaments }) {
|
||||
const dialog = useContext(DialogContext)
|
||||
|
||||
const handleDeleteTournament = async () => {
|
||||
const tournamentsRemoved = tournaments.filter((x) => x.id != tid)
|
||||
setTournaments(tournamentsRemoved)
|
||||
setTournaments(tournaments.filter((x) => x.id !== tid))
|
||||
try {
|
||||
await deleteTournament(apiServer, token, tid)
|
||||
console.log(`Tournament ${tid} deleted`)
|
||||
@@ -22,7 +19,7 @@ export default function DeleteTournamentDialog(props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<DialogTitle>Do you want to delete Tournament {tid}</DialogTitle>
|
||||
<DialogTitle>Do you want to delete Tournament {tid}?</DialogTitle>
|
||||
<DialogActions>
|
||||
<Button onClick={dialog.onClose} color="primary">
|
||||
Cancel
|
||||
|
||||
@@ -2,11 +2,11 @@ import { useContext } from 'react'
|
||||
import { Menu, MenuItem, IconButton } from '@mui/material'
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert'
|
||||
import { Link as RouterLink } from 'react-router-dom'
|
||||
import DeleteTournamentDialog from './DeleteTournamentDialog'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import AddGroups from '../Groups/AddGroups'
|
||||
import EditGroups from '../Groups/EditGroups'
|
||||
import AddEditTournament from './AddEditTournament'
|
||||
import DeleteTournamentDialog from './DeleteTournamentDialog'
|
||||
import TriggerGroupsDialog from './TriggerGroupsDialog'
|
||||
import { createPools, createFinalGroups } from '../../api/tournaments'
|
||||
|
||||
@@ -32,35 +32,14 @@ export default function TournamentMenu({ anchorEl, open, onOpen, onClose, user,
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
const openTriggerGroupDialog = (tid) => {
|
||||
|
||||
const openDialog = (Component, props = {}, maxWidth) => {
|
||||
handleClose()
|
||||
dialog.setContent(<TriggerGroupsDialog tid={tid} tournamentGroups={tournamentGroups} token={token} apiServer={apiServer} />)
|
||||
if (maxWidth) dialog.setMaxWidth(maxWidth)
|
||||
dialog.setContent(<Component {...props} />)
|
||||
dialog.setOpen()
|
||||
}
|
||||
|
||||
const openAddGroupDialog = (tid) => {
|
||||
handleClose()
|
||||
dialog.setMaxWidth('lg')
|
||||
dialog.setContent(<AddGroups tid={tid} token={token} apiServer={apiServer} />)
|
||||
dialog.setOpen()
|
||||
}
|
||||
|
||||
const openEditGroupDialog = (tid) => {
|
||||
handleClose()
|
||||
dialog.setMaxWidth('lg')
|
||||
dialog.setContent(<EditGroups tid={tid} token={token} apiServer={apiServer} groups={tournamentGroups} />)
|
||||
dialog.setOpen()
|
||||
}
|
||||
const openEditTournamentDialog = () => {
|
||||
handleClose()
|
||||
dialog.setContent(<AddEditTournament tournaments={tournaments} setTournaments={setTournaments} token={token} apiServer={apiServer} tournament={tournament} />)
|
||||
dialog.setOpen()
|
||||
}
|
||||
const openDeleteTournamentDialog = (tid) => {
|
||||
handleClose()
|
||||
dialog.setContent(<DeleteTournamentDialog tournaments={tournaments} setTournaments={setTournaments} tid={tid} token={token} apiServer={apiServer} />)
|
||||
dialog.setOpen()
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<IconButton aria-label="more" aria-controls="simple-menu" aria-haspopup="true" onClick={onOpen} size="large">
|
||||
@@ -68,17 +47,17 @@ export default function TournamentMenu({ anchorEl, open, onOpen, onClose, user,
|
||||
</IconButton>
|
||||
<Menu id="simple-menu" anchorEl={anchorEl} keepMounted open={open} onClose={onClose}>
|
||||
<MenuItem onClick={handleCreatePools}>Pools erstellen</MenuItem>
|
||||
<MenuItem onClick={openTriggerGroupDialog.bind(this, tournament.id)}>Gruppen auslosen</MenuItem>
|
||||
<MenuItem onClick={() => openDialog(TriggerGroupsDialog, { tid: tournament.id, tournamentGroups, token, apiServer })}>Gruppen auslosen</MenuItem>
|
||||
<MenuItem onClick={handleCreateFinalGroups}>Finalgruppen anlegen</MenuItem>
|
||||
<MenuItem onClick={openAddGroupDialog.bind(this, tournament.id)}>Gruppen hinzufügen</MenuItem>
|
||||
<MenuItem onClick={openEditGroupDialog.bind(this, tournament.id)}>Gruppen bearbeiten</MenuItem>
|
||||
<MenuItem onClick={openEditTournamentDialog.bind(this, tournament.id)}>Turnier bearbeiten</MenuItem>
|
||||
<MenuItem onClick={openDeleteTournamentDialog.bind(this, tournament.id)}>Turnier löschen</MenuItem>
|
||||
<RouterLink to={'/pdf/lists/' + tournament.id} style={{ textDecoration: 'inherit', color: 'inherit' }}>
|
||||
<MenuItem onClick={() => openDialog(AddGroups, { tid: tournament.id, token, apiServer }, 'lg')}>Gruppen hinzufügen</MenuItem>
|
||||
<MenuItem onClick={() => openDialog(EditGroups, { tid: tournament.id, token, apiServer, groups: tournamentGroups }, 'lg')}>Gruppen bearbeiten</MenuItem>
|
||||
<MenuItem onClick={() => openDialog(AddEditTournament, { tournaments, setTournaments, token, apiServer, tournament })}>Turnier bearbeiten</MenuItem>
|
||||
<MenuItem onClick={() => openDialog(DeleteTournamentDialog, { tournaments, setTournaments, tid: tournament.id, token, apiServer })}>Turnier löschen</MenuItem>
|
||||
<RouterLink to={`/pdf/lists/${tournament.id}`} style={{ textDecoration: 'inherit', color: 'inherit' }}>
|
||||
<MenuItem>PDF erstellen</MenuItem>
|
||||
</RouterLink>
|
||||
{user?.admin > 9 && (
|
||||
<RouterLink to={'/mail/' + tournament.id} style={{ textDecoration: 'inherit', color: 'inherit' }}>
|
||||
<RouterLink to={`/mail/${tournament.id}`} style={{ textDecoration: 'inherit', color: 'inherit' }}>
|
||||
<MenuItem>Email senden</MenuItem>
|
||||
</RouterLink>
|
||||
)}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { SpeedDial, SpeedDialAction, SpeedDialIcon, styled } from '@mui/material
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
|
||||
const PREFIX = 'Tournaments'
|
||||
|
||||
const classes = {
|
||||
olderEntry: `${PREFIX}-olderEntry`,
|
||||
speedDial: `${PREFIX}-speedDial`,
|
||||
@@ -33,18 +32,12 @@ const Root = styled('div')(({ theme }) => ({
|
||||
},
|
||||
}))
|
||||
|
||||
export default function Tournaments(props) {
|
||||
const { apiServer, groups, tournaments, setTournaments, token, user, participants, setParticipants } = props
|
||||
export default function Tournaments({ apiServer, groups, tournaments, setTournaments, token, user, participants, setParticipants }) {
|
||||
const [speedDialOpen, setSpeedDialOpen] = useState(false)
|
||||
const dialog = useContext(DialogContext)
|
||||
|
||||
const handleClose = () => {
|
||||
setSpeedDialOpen(false)
|
||||
}
|
||||
|
||||
const handleOpen = () => {
|
||||
setSpeedDialOpen(true)
|
||||
}
|
||||
const handleClose = () => setSpeedDialOpen(false)
|
||||
const handleOpen = () => setSpeedDialOpen(true)
|
||||
|
||||
const handleAddPerson = () => {
|
||||
dialog.setContent(<AddEditParticipant participants={participants} setParticipants={setParticipants} token={token} apiServer={apiServer} />)
|
||||
@@ -63,52 +56,41 @@ export default function Tournaments(props) {
|
||||
{ icon: <PersonAdd />, name: 'Add Participant', action: handleAddPerson },
|
||||
]
|
||||
|
||||
const showTournaments = (tournaments) => {
|
||||
let result = []
|
||||
const today = new Date().getFullYear()
|
||||
for (let i = 0; i < tournaments.length; i++) {
|
||||
const date = new Date(tournaments[i].veranstaltungsDatum).getFullYear()
|
||||
if (date >= today - 1) {
|
||||
// Turniere ab vorletzen Jahr
|
||||
result.push(
|
||||
<div key={tournaments[i]['id']}>
|
||||
<TournamentsCard
|
||||
tournaments={tournaments}
|
||||
setTournaments={setTournaments}
|
||||
user={user}
|
||||
participants={participants}
|
||||
tournament={tournaments[i]}
|
||||
tournamentGroups={filterObject('turnier_id', tournaments[i]['id'], groups)}
|
||||
apiServer={apiServer}
|
||||
token={token}
|
||||
/>
|
||||
</div>,
|
||||
)
|
||||
} else {
|
||||
result.push(
|
||||
<div className={classes.olderEntry} key={tournaments[i]['id']}>
|
||||
<TournamentsCard
|
||||
tournaments={tournaments}
|
||||
setTournaments={setTournaments}
|
||||
user={user}
|
||||
tournament={tournaments[i]}
|
||||
tournamentGroups={filterObject('turnier_id', tournaments[i]['id'], groups)}
|
||||
apiServer={apiServer}
|
||||
token={token}
|
||||
/>
|
||||
</div>,
|
||||
)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
const today = new Date().getFullYear()
|
||||
const currentTournaments = tournaments.filter((t) => new Date(t.veranstaltungsDatum).getFullYear() >= today - 1)
|
||||
const olderTournaments = tournaments.filter((t) => new Date(t.veranstaltungsDatum).getFullYear() < today - 1)
|
||||
|
||||
return (
|
||||
<Root>
|
||||
{showTournaments(tournaments)}
|
||||
{/* // TODO: activate SpeedDial auch für turnierersteller. */}
|
||||
{currentTournaments.map((t) => (
|
||||
<div key={t.id}>
|
||||
<TournamentsCard
|
||||
tournaments={tournaments}
|
||||
setTournaments={setTournaments}
|
||||
user={user}
|
||||
participants={participants}
|
||||
tournament={t}
|
||||
tournamentGroups={filterObject('turnier_id', t.id, groups)}
|
||||
apiServer={apiServer}
|
||||
token={token}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{olderTournaments.map((t) => (
|
||||
<div className={classes.olderEntry} key={t.id}>
|
||||
<TournamentsCard
|
||||
tournaments={tournaments}
|
||||
setTournaments={setTournaments}
|
||||
user={user}
|
||||
tournament={t}
|
||||
tournamentGroups={filterObject('turnier_id', t.id, groups)}
|
||||
apiServer={apiServer}
|
||||
token={token}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{user?.admin > 4 && (
|
||||
<SpeedDial ariaLabel="SpeedDial example" className={classes.speedDial} icon={<SpeedDialIcon />} onClose={handleClose} onOpen={handleOpen} open={speedDialOpen} direction={'up'}>
|
||||
<SpeedDial ariaLabel="SpeedDial example" className={classes.speedDial} icon={<SpeedDialIcon />} onClose={handleClose} onOpen={handleOpen} open={speedDialOpen} direction="up">
|
||||
{actions.map((action) => (
|
||||
<SpeedDialAction key={action.name} icon={action.icon} tooltipTitle={action.name} onClick={action.action} />
|
||||
))}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import { Avatar, Button, Card, CardActions, CardContent, CardHeader, Collapse, IconButton, Tooltip, styled } from '@mui/material'
|
||||
import { Avatar, Button, Card, CardActions, CardContent, CardHeader, Collapse, IconButton, Tooltip, styled, Box } from '@mui/material'
|
||||
import { grey, red } from '@mui/material/colors'
|
||||
import {
|
||||
DateRange as DateRangeIcon,
|
||||
@@ -13,14 +13,10 @@ import {
|
||||
} from '@mui/icons-material'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Link as RouterLink } from 'react-router-dom'
|
||||
|
||||
import Groups from '../Groups/Groups'
|
||||
|
||||
import Box from '@mui/material/Box'
|
||||
import TournamentMenu from './TournamentMenu'
|
||||
|
||||
const PREFIX = 'TournamentsCard'
|
||||
|
||||
const classes = {
|
||||
card: `${PREFIX}-card`,
|
||||
cardHeader: `${PREFIX}-cardHeader`,
|
||||
@@ -35,7 +31,6 @@ const classes = {
|
||||
titleContainer: `${PREFIX}-titleContainer`,
|
||||
titleRight: `${PREFIX}-titleRight`,
|
||||
titleSide: `${PREFIX}-titleSide`,
|
||||
subTitle: `${PREFIX}-subTitle`,
|
||||
routerLink: `${PREFIX}-routerLink`,
|
||||
titleAction: `${PREFIX}-titleAction`,
|
||||
}
|
||||
@@ -150,7 +145,6 @@ const StyledCard = styled(Card)(({ theme }) => ({
|
||||
[`& .${classes.routerLink}`]: {
|
||||
textDecoration: 'inherit',
|
||||
},
|
||||
// Zusätzliche Anpassungen für Buttons und Icons
|
||||
[`& .MuiButton-root`]: {
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
fontSize: '0.9rem',
|
||||
@@ -176,7 +170,7 @@ const StyledCard = styled(Card)(({ theme }) => ({
|
||||
},
|
||||
[`& .${classes.media}`]: {
|
||||
height: 0,
|
||||
paddingTop: '56.25%', // 16:9
|
||||
paddingTop: '56.25%',
|
||||
},
|
||||
[`& .${classes.expand}`]: {
|
||||
transform: 'rotate(0deg)',
|
||||
@@ -189,23 +183,14 @@ const StyledCard = styled(Card)(({ theme }) => ({
|
||||
},
|
||||
}))
|
||||
|
||||
export default function TournamentsCard(props) {
|
||||
const { apiServer, tournament, tournaments, setTournaments, tournamentGroups, token, user, participants } = props
|
||||
export default function TournamentsCard({ apiServer, tournament, tournaments, setTournaments, tournamentGroups, token, user, participants }) {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const handleExpandClick = () => {
|
||||
setExpanded(!expanded)
|
||||
}
|
||||
|
||||
const handleClick = (event) => {
|
||||
setAnchorEl(event.currentTarget)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null)
|
||||
}
|
||||
const handleExpandClick = () => setExpanded((prev) => !prev)
|
||||
const handleMenuOpen = (event) => setAnchorEl(event.currentTarget)
|
||||
const handleMenuClose = () => setAnchorEl(null)
|
||||
|
||||
const tournamentData = {
|
||||
id: tournament.id,
|
||||
@@ -216,7 +201,8 @@ export default function TournamentsCard(props) {
|
||||
account_id: tournament.account_id,
|
||||
}
|
||||
|
||||
const closingdate = t('closing-date') + ' ' + new Date(tournament.meldeschluss).toLocaleDateString()
|
||||
const closingDate = `${t('closing-date')} ${new Date(tournament.meldeschluss).toLocaleDateString()}`
|
||||
|
||||
return (
|
||||
<StyledCard className={classes.card}>
|
||||
<CardHeader
|
||||
@@ -229,13 +215,8 @@ export default function TournamentsCard(props) {
|
||||
action={
|
||||
<div className={classes.titleAction}>
|
||||
{user?.account && (
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: 'none', sm: 'flex' },
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<RouterLink to={'registration/' + tournament.id} className={classes.routerLink}>
|
||||
<Box sx={{ display: { xs: 'none', sm: 'flex' }, alignItems: 'center' }}>
|
||||
<RouterLink to={`registration/${tournament.id}`} className={classes.routerLink}>
|
||||
<Button color="primary" variant="contained">
|
||||
{t('tournament.registration')}
|
||||
</Button>
|
||||
@@ -246,8 +227,8 @@ export default function TournamentsCard(props) {
|
||||
<TournamentMenu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onOpen={handleClick}
|
||||
onClose={handleClose}
|
||||
onOpen={handleMenuOpen}
|
||||
onClose={handleMenuClose}
|
||||
user={user}
|
||||
tournament={tournament}
|
||||
tournamentGroups={tournamentGroups}
|
||||
@@ -257,9 +238,7 @@ export default function TournamentsCard(props) {
|
||||
token={token}
|
||||
tournamentData={tournamentData}
|
||||
participants={participants}
|
||||
handleClose={handleClose}
|
||||
|
||||
|
||||
handleClose={handleMenuClose}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -282,7 +261,7 @@ export default function TournamentsCard(props) {
|
||||
{tournament.ort}
|
||||
</div>
|
||||
<div>
|
||||
<Tooltip title={closingdate}>
|
||||
<Tooltip title={closingDate}>
|
||||
<IconButton size="large">
|
||||
<DateRangeIcon />
|
||||
</IconButton>
|
||||
@@ -303,17 +282,9 @@ export default function TournamentsCard(props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Registrierungs-Button: mobil unterhalb des Headers */}
|
||||
{user?.account && (
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: 'block', sm: 'none' },
|
||||
px: 2,
|
||||
mt: 1,
|
||||
mb: 1,
|
||||
}}
|
||||
>
|
||||
<RouterLink to={'registration/' + tournament.id} className={classes.routerLink}>
|
||||
<Box sx={{ display: { xs: 'block', sm: 'none' }, px: 2, mt: 1, mb: 1 }}>
|
||||
<RouterLink to={`registration/${tournament.id}`} className={classes.routerLink}>
|
||||
<Button color="primary" variant="contained" fullWidth>
|
||||
{t('tournament.registration')}
|
||||
</Button>
|
||||
@@ -323,31 +294,30 @@ export default function TournamentsCard(props) {
|
||||
|
||||
<CardActions className={classes.cardAction}>
|
||||
<div className={classes.cardActionBlock}>
|
||||
<IconButton className={classes.expand + (expanded ? ' ' + classes.expandOpen : '')} onClick={handleExpandClick} aria-expanded={expanded} aria-label="show more" size="large">
|
||||
<IconButton className={`${classes.expand}${expanded ? ' ' + classes.expandOpen : ''}`} onClick={handleExpandClick} aria-expanded={expanded} aria-label="show more" size="large">
|
||||
<ExpandMoreIcon />
|
||||
</IconButton>
|
||||
</div>
|
||||
<h4>{t('headline.groups')}</h4>
|
||||
<div className={classes.cardActionBlockRight}>
|
||||
{/* //TODO: hier Rechnung generieren */}
|
||||
<Tooltip title="Infos">
|
||||
<RouterLink to={'info/' + tournament.id}>
|
||||
<IconButton aria-label="emoji_events" size="large">
|
||||
<RouterLink to={`info/${tournament.id}`}>
|
||||
<IconButton aria-label="info" size="large">
|
||||
<InfoIcon />
|
||||
</IconButton>
|
||||
</RouterLink>
|
||||
</Tooltip>
|
||||
<Tooltip title="result">
|
||||
<RouterLink to={'results/' + tournament.id}>
|
||||
<IconButton aria-label="emoji_events" size="large">
|
||||
<RouterLink to={`results/${tournament.id}`}>
|
||||
<IconButton aria-label="results" size="large">
|
||||
<EmojiEventsIcon />
|
||||
</IconButton>
|
||||
</RouterLink>
|
||||
</Tooltip>
|
||||
{tournamentData?.streams != undefined && (
|
||||
{tournamentData?.streams && (
|
||||
<Tooltip title="Stream">
|
||||
<RouterLink to={'stream/' + tournament.id}>
|
||||
<IconButton aria-label="emoji_events" size="large">
|
||||
<RouterLink to={`stream/${tournament.id}`}>
|
||||
<IconButton aria-label="stream" size="large">
|
||||
<VideocamIcon />
|
||||
</IconButton>
|
||||
</RouterLink>
|
||||
@@ -357,7 +327,6 @@ export default function TournamentsCard(props) {
|
||||
</CardActions>
|
||||
<Collapse in={expanded} timeout="auto" unmountOnExit>
|
||||
<CardContent>
|
||||
{/* // TODO: Gruppen hier laden */}
|
||||
<Groups user={user} tournamentGroups={tournamentGroups} token={token} apiServer={apiServer} tournamentData={tournamentData} participants={participants} />
|
||||
</CardContent>
|
||||
</Collapse>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { render, renderHook } from '@testing-library/react'
|
||||
import { render } from '@testing-library/react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import { vi, describe, it, expect } from 'vitest'
|
||||
import { vi, describe, it } from 'vitest'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import TournamentsCard from './TournamentsCard'
|
||||
import AddGroups from '../Groups/AddGroups'
|
||||
@@ -9,11 +9,9 @@ import Groups from '../Groups/Groups'
|
||||
import AddEditTournament from './AddEditTournament'
|
||||
import DeleteTournamentDialog from './DeleteTournamentDialog'
|
||||
import TriggerGroupsDialog from './TriggerGroupsDialog'
|
||||
import { dataTournament } from './__mocks__/dataTournament'
|
||||
import { dataGroup } from '../Groups/__mocks__/dataGroup'
|
||||
import useFetch from '../../components/UseFetch/UseFetch'
|
||||
|
||||
// Mocks für i18n und DialogContext
|
||||
// i18n und DialogContext Mock
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key) => key,
|
||||
@@ -29,7 +27,6 @@ const mockDialogContextValue = {
|
||||
onClose: () => {},
|
||||
open: false,
|
||||
dialog: { onClose: () => {}, tid: 1 },
|
||||
// ...weitere Werte nach Bedarf...
|
||||
}
|
||||
|
||||
describe('Komponenten Smoke-Tests', () => {
|
||||
|
||||
@@ -3,21 +3,18 @@ import { useContext } from 'react'
|
||||
import { DialogContext } from '../Dialog/Context'
|
||||
import { triggerGroup } from '../../api/groups'
|
||||
|
||||
export default function TriggerGroupsDialog(props) {
|
||||
const { apiServer, token, tournamentGroups } = props
|
||||
export default function TriggerGroupsDialog({ apiServer, token, tournamentGroups }) {
|
||||
const dialog = useContext(DialogContext)
|
||||
|
||||
const handleDialogClose = () => {
|
||||
dialog.onClose()
|
||||
}
|
||||
const handleClose = () => dialog.onClose()
|
||||
|
||||
const triggerTournament = async () => {
|
||||
const handleTrigger = async () => {
|
||||
try {
|
||||
for (const tournament of tournamentGroups) {
|
||||
await triggerGroup(apiServer, token, tournament.id, tournament.disziplin)
|
||||
console.log(`groups of tournament ${tournament.id} triggered`)
|
||||
for (const group of tournamentGroups) {
|
||||
await triggerGroup(apiServer, token, group.id, group.disziplin)
|
||||
console.log(`groups of tournament ${group.id} triggered`)
|
||||
}
|
||||
handleDialogClose()
|
||||
handleClose()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
@@ -27,13 +24,13 @@ export default function TriggerGroupsDialog(props) {
|
||||
<>
|
||||
<DialogTitle id="dialog-title">Alle Gruppen neu auslosen?</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="dialog-description">Alle Gruppe werden neu Ausgelost!</DialogContentText>
|
||||
<DialogContentText id="dialog-description">Alle Gruppen werden neu ausgelost!</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleDialogClose} color="primary" autoFocus>
|
||||
<Button onClick={handleClose} color="primary" autoFocus>
|
||||
abbrechen
|
||||
</Button>
|
||||
<Button onClick={triggerTournament} color="secondary">
|
||||
<Button onClick={handleTrigger} color="secondary">
|
||||
Gruppen auslosen
|
||||
</Button>
|
||||
</DialogActions>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useEffect } from 'react'
|
||||
import useFetch from './UseFetch'
|
||||
|
||||
export default function LoadParticipants(props) {
|
||||
const { token, apiServer, setParticipants } = props
|
||||
const { data: participants, loading } = useFetch(apiServer + '/participants' + token)
|
||||
export default function LoadParticipants({ token, apiServer, setParticipants }) {
|
||||
const url = `${apiServer}/participants${token ? token : ''}`
|
||||
const { data: participants, loading } = useFetch(url)
|
||||
|
||||
useEffect(() => {
|
||||
!loading && setParticipants(participants)
|
||||
if (!loading && participants) setParticipants(participants)
|
||||
}, [participants, loading, setParticipants])
|
||||
|
||||
return <br />
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,22 +1,38 @@
|
||||
import {useState, useEffect} from 'react';
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export default function useFetch(url) {
|
||||
const [data,setData] = useState(null);
|
||||
const [loading,setLoading] = useState(true);
|
||||
const [data, setData] = useState(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
const abortCtrl = new AbortController();
|
||||
async function fetchData(url) {
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
// console.log('data: ', data);
|
||||
setData(data);
|
||||
setLoading(false);
|
||||
}
|
||||
fetchData(url);
|
||||
// return () => fetchData.abort();
|
||||
return () => abortCtrl.abort();
|
||||
}, [url]);
|
||||
useEffect(() => {
|
||||
if (!url) {
|
||||
setData(null)
|
||||
setLoading(false)
|
||||
setError(null)
|
||||
return
|
||||
}
|
||||
|
||||
return {data, loading};
|
||||
}
|
||||
const abortCtrl = new AbortController()
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
|
||||
async function fetchData() {
|
||||
try {
|
||||
const response = await fetch(url, { signal: abortCtrl.signal })
|
||||
if (!response.ok) throw new Error('Network response was not ok')
|
||||
const json = await response.json()
|
||||
setData(json)
|
||||
} catch (err) {
|
||||
if (err.name !== 'AbortError') setError(err)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
fetchData()
|
||||
return () => abortCtrl.abort()
|
||||
}, [url])
|
||||
|
||||
return { data, loading, error }
|
||||
}
|
||||
|
||||
44
src/i18n.js
44
src/i18n.js
@@ -1,7 +1,7 @@
|
||||
import i18n from 'i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
|
||||
// TODO: die übersetzungen nicht bundlen
|
||||
// TODO: Übersetzungen nicht bundlen, sondern dynamisch laden
|
||||
|
||||
import common_de from './translations/de/common.json'
|
||||
import common_en from './translations/en/common.json'
|
||||
@@ -11,31 +11,21 @@ import common_it from './translations/it/common.json'
|
||||
import common_cs from './translations/cs/common.json'
|
||||
import common_nn from './translations/nn/common.json'
|
||||
|
||||
const resources = {
|
||||
de: { common: common_de },
|
||||
en: { common: common_en },
|
||||
fr: { common: common_fr },
|
||||
es: { common: common_es },
|
||||
it: { common: common_it },
|
||||
cs: { common: common_cs },
|
||||
nn: { common: common_nn },
|
||||
}
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
interpolation: { escapeValue: false }, // React already does escaping
|
||||
lng: 'de', // language to use
|
||||
fallbackLng: 'en', // fallback: ;
|
||||
resources: {
|
||||
en: {
|
||||
common: common_en, // 'common' is our custom namespace
|
||||
},
|
||||
de: {
|
||||
common: common_de,
|
||||
},
|
||||
fr: {
|
||||
common: common_fr,
|
||||
},
|
||||
es: {
|
||||
common: common_es,
|
||||
},
|
||||
it: {
|
||||
common: common_it,
|
||||
},
|
||||
cs: {
|
||||
common: common_cs,
|
||||
},
|
||||
nn: {
|
||||
common: common_nn,
|
||||
},
|
||||
},
|
||||
interpolation: { escapeValue: false }, // React macht Escaping bereits
|
||||
lng: 'de', // Standardsprache
|
||||
fallbackLng: 'en',
|
||||
resources,
|
||||
})
|
||||
|
||||
export default i18n
|
||||
|
||||
@@ -9,23 +9,21 @@ code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
|
||||
/* Tabellen-Design für Material-UI */
|
||||
tr.MuiTableRow-root:nth-of-type(even) {
|
||||
background-color: rgb(238, 238, 238);
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
tr.MuiTableRow-root:nth-of-type(odd) {
|
||||
background-color: rgb(250, 250, 250);
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
tr.MuiTableRow-root:hover {
|
||||
background-color: rgba(0, 0, 0, 0.17);
|
||||
}
|
||||
|
||||
th.MuiTableCell-root.MuiTableCell-head {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
/* material-icons-regular - latin */
|
||||
/* Material Icons Font */
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
@@ -33,12 +31,11 @@ th.MuiTableCell-root.MuiTableCell-head {
|
||||
src: url('../src/assets/fonts/material-icons-v143-latin-regular.woff2') format('woff2');
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px; /* oder gewünschte Größe */
|
||||
font-size: 24px;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
letter-spacing: normal;
|
||||
@@ -50,7 +47,7 @@ th.MuiTableCell-root.MuiTableCell-head {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* roboto-regular - latin */
|
||||
/* Roboto Font */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
import './i18n.js'
|
||||
import './i18n'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
/* eslint-disable react/no-unescaped-entities */
|
||||
import { Button, styled } from '@mui/material'
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import { Button, styled } from '@mui/material'
|
||||
|
||||
const PREFIX = 'Dataprotection'
|
||||
|
||||
const classes = {
|
||||
routerLink: `${PREFIX}-routerLink`,
|
||||
}
|
||||
|
||||
// TODO jss-to-styled codemod: The Fragment root was replaced by div. Change the tag if needed.
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
[`& .${classes.routerLink}`]: {
|
||||
textDecoration: 'inherit',
|
||||
@@ -585,6 +581,8 @@ export default function Dataprotection() {
|
||||
{t('back-to-page')}
|
||||
</Button>
|
||||
</Link>
|
||||
<br />
|
||||
<div style={{ marginTop: 32, color: '#888', fontSize: '0.9em' }}>Version 1.0 – Stand: 08.06.2025</div>
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
/* eslint-disable react/no-unescaped-entities */
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { Button, styled } from '@mui/material'
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
const PREFIX = 'Impress'
|
||||
|
||||
const classes = {
|
||||
routerLink: `${PREFIX}-routerLink`,
|
||||
}
|
||||
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
const Root = styled('div')(() => ({
|
||||
[`& .${classes.routerLink}`]: {
|
||||
textDecoration: 'inherit',
|
||||
},
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useParams, Link } from 'react-router-dom'
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
|
||||
import useFetch from '../components/UseFetch/UseFetch'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { styled, Button } from '@mui/material'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const PREFIX = 'Streams'
|
||||
|
||||
const PREFIX = 'Info'
|
||||
const classes = {
|
||||
info: `${PREFIX}-stream`,
|
||||
info: `${PREFIX}-info`,
|
||||
routerLink: `${PREFIX}-routerLink`,
|
||||
}
|
||||
|
||||
// TODO jss-to-styled codemod: The Fragment root was replaced by div. Change the tag if needed.
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
[`& .${classes.stream}`]: {
|
||||
[`& .${classes.info}`]: {
|
||||
backgroundColor: 'rgba(0,0,0,0.2)',
|
||||
margin: '1rem',
|
||||
padding: '1rem',
|
||||
@@ -23,42 +20,53 @@ const Root = styled('div')(({ theme }) => ({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-around',
|
||||
},
|
||||
[`& .${classes.routerLink}`]: {
|
||||
textDecoration: 'inherit',
|
||||
},
|
||||
}))
|
||||
|
||||
export default function Info(props) {
|
||||
const { apiServer } = props
|
||||
export default function Info({ apiServer }) {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const { tid } = useParams()
|
||||
const { data: tournament } = useFetch(apiServer + '/tournament/' + tid)
|
||||
const { data: tournament } = useFetch(`${apiServer}/tournament/${tid}`)
|
||||
|
||||
const infos = tournament?.info ? JSON.parse(tournament?.info) : { 'Keine Infos vorhanden': null }
|
||||
let infos = {}
|
||||
try {
|
||||
infos = tournament?.info ? JSON.parse(tournament.info) : {}
|
||||
} catch {
|
||||
infos = {}
|
||||
}
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<h1>{tournament?.name}</h1>
|
||||
<h2>
|
||||
{new Date(tournament?.veranstaltungsDatum).toLocaleDateString()} @ {tournament?.ort}
|
||||
{tournament?.veranstaltungsDatum && new Date(tournament.veranstaltungsDatum).toLocaleDateString()} @ {tournament?.ort}
|
||||
</h2>
|
||||
<br />
|
||||
<h2>Infos:</h2>
|
||||
<br />
|
||||
|
||||
{infos !== null &&
|
||||
Object.entries(infos).map((row) => {
|
||||
return (
|
||||
<div className={classes.info} key={row}>
|
||||
<h3>
|
||||
<a href={row[1]} target="_blank" rel="noreferrer">
|
||||
{row[0]}
|
||||
{Object.keys(infos).length > 0 ? (
|
||||
Object.entries(infos).map(([label, url]) => (
|
||||
<div className={classes.info} key={label}>
|
||||
<h3>
|
||||
{url ? (
|
||||
<a href={url} target="_blank" rel="noreferrer">
|
||||
{label}
|
||||
</a>
|
||||
</h3>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
) : (
|
||||
label
|
||||
)}
|
||||
</h3>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className={classes.info}>
|
||||
<h3>Keine Infos vorhanden</h3>
|
||||
</div>
|
||||
)}
|
||||
<br />
|
||||
<br />
|
||||
<Link to={'/'}>
|
||||
<Link to={'/'} className={classes.routerLink}>
|
||||
<Button variant="outlined" startIcon={<ArrowBackIcon />}>
|
||||
{t('back-to-page')}
|
||||
</Button>
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { styled, Paper } from '@mui/material'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const PREFIX = 'Live'
|
||||
|
||||
const classes = {
|
||||
container: `${PREFIX}-container`,
|
||||
participant: `${PREFIX}-participant`,
|
||||
}
|
||||
|
||||
const Container = styled('div')(({ theme }) => ({
|
||||
const Container = styled('div')({
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
}))
|
||||
})
|
||||
|
||||
const Tatami = styled(Paper)(({ theme }) => ({
|
||||
width: '20rem',
|
||||
@@ -31,94 +30,61 @@ const Tatami = styled(Paper)(({ theme }) => ({
|
||||
},
|
||||
}))
|
||||
|
||||
export default function Live(props) {
|
||||
const { apiServer, token, user } = props
|
||||
function Encounters({ encounters }) {
|
||||
return encounters.map((el, index) => (
|
||||
<Tatami elevation={3} key={index}>
|
||||
<div className={classes.container}>
|
||||
<div className={classes.participant}>{el.aka}</div>
|
||||
<div>{el.pool}</div>
|
||||
<div className={classes.participant}>{el.shiro}</div>
|
||||
</div>
|
||||
</Tatami>
|
||||
))
|
||||
}
|
||||
|
||||
export default function Live({ apiServer, token, user }) {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const { tid } = useParams()
|
||||
const initData = {
|
||||
tid: 65,
|
||||
encounter: [
|
||||
{
|
||||
pool: 'A',
|
||||
aka: 333,
|
||||
shiro: 444,
|
||||
},
|
||||
{
|
||||
pool: 'B',
|
||||
aka: 111,
|
||||
shiro: 123,
|
||||
},
|
||||
{
|
||||
pool: 'C',
|
||||
aka: 111,
|
||||
shiro: 123,
|
||||
},
|
||||
{
|
||||
pool: 'D',
|
||||
aka: 111,
|
||||
shiro: 123,
|
||||
},
|
||||
{
|
||||
pool: 'F',
|
||||
aka: 111,
|
||||
shiro: 123,
|
||||
},
|
||||
],
|
||||
}
|
||||
const [data, setData] = useState({
|
||||
tid: tid || 65,
|
||||
encounter: [],
|
||||
})
|
||||
const wsRef = useRef(null)
|
||||
|
||||
const wsUrl = `ws://api.karateturniere.de/ws${token}`
|
||||
const [data, setData] = useState(initData)
|
||||
useEffect(() => {
|
||||
const ws = new WebSocket(
|
||||
// 'ws://localhost:8000/ws?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im1hcGVAd2F0dHNjaGUuZGUiLCJpZCI6MTE0MCwiYWRtaW4iOiIiLCJpYXQiOjE2NTAxMTE1NTEsImV4cCI6MTY1MDE5Nzk1MX0.gko7Im54rBLM0V4Yiwgx1KBUgvOrDImQQ7mSAGLoH30',
|
||||
wsUrl,
|
||||
)
|
||||
const wsUrl = `ws://api.karateturniere.de/ws${token ? '?token=' + token : ''}`
|
||||
const ws = new WebSocket(wsUrl)
|
||||
wsRef.current = ws
|
||||
|
||||
ws.onopen = (event) => {
|
||||
ws.onopen = () => {
|
||||
ws.send('PING')
|
||||
console.log('onopen')
|
||||
console.log('WebSocket geöffnet')
|
||||
}
|
||||
|
||||
ws.onmessage = function (event) {
|
||||
const json = JSON.parse(event.data)
|
||||
console.log('onMessage 1', json)
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
if (event.type == 'message') {
|
||||
const json = JSON.parse(event.data)
|
||||
if (event.type === 'message' && json.data) {
|
||||
setData(json.data)
|
||||
console.log('data', data)
|
||||
console.log('json.data', json.data)
|
||||
// console.log('onMessage', json.data);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
console.error('WebSocket Fehler:', err)
|
||||
}
|
||||
}
|
||||
//clean up function
|
||||
return () => ws.close()
|
||||
}, [])
|
||||
|
||||
const Encounters = () => {
|
||||
console.log('enc', data)
|
||||
const result = data?.encounter.map((el, index) => {
|
||||
// console.log(el)
|
||||
return (
|
||||
<Tatami elevation={3} key={index}>
|
||||
<div className={classes.container}>
|
||||
<div className={classes.participant}>{el.aka}</div>
|
||||
<div>{el.pool}</div>
|
||||
<div className={classes.participant}>{el.shiro}</div>
|
||||
</div>
|
||||
</Tatami>
|
||||
)
|
||||
})
|
||||
return result
|
||||
}
|
||||
ws.onerror = (err) => {
|
||||
console.error('WebSocket Fehler:', err)
|
||||
}
|
||||
|
||||
return () => {
|
||||
ws.close()
|
||||
}
|
||||
}, [token])
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Turnier: {data?.tid}</h1>
|
||||
<Container>{data?.encounter && <Encounters></Encounters>}</Container>
|
||||
<Container>{data?.encounter && data.encounter.length > 0 ? <Encounters encounters={data.encounter} /> : <div>Keine Begegnungen vorhanden</div>}</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { InputLabel, MenuItem, Select, FormControl, styled, TextField } from '@mui/material'
|
||||
import { InputLabel, MenuItem, Select, FormControl, styled, TextField, Button } from '@mui/material'
|
||||
import ArrowForwardIcon from '@mui/icons-material/ArrowForward'
|
||||
import { useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { sendMail } from '../api/mail'
|
||||
|
||||
const PREFIX = 'Mail'
|
||||
|
||||
const classes = {
|
||||
stream: `${PREFIX}-stream`,
|
||||
routerLink: `${PREFIX}-routerLink`,
|
||||
@@ -14,92 +14,22 @@ const Root = styled('div')(({ theme }) => ({
|
||||
[`& .${classes.stream}`]: {
|
||||
backgroundColor: 'rgba(0,0,0,0.2)',
|
||||
},
|
||||
margin: '2rem',
|
||||
maxWidth: 600,
|
||||
}))
|
||||
|
||||
export default function Mail(props) {
|
||||
const { apiServer, token, tournaments } = props
|
||||
const { tid } = useParams()
|
||||
const [mail, setMail] = useState({
|
||||
from: 'm.peters@karatenw.de',
|
||||
to: 'mario@wattsche.de',
|
||||
subject: 'DM 22',
|
||||
body: 'test',
|
||||
tid,
|
||||
})
|
||||
|
||||
const sendMailHandler = async () => {
|
||||
try {
|
||||
await sendMail(apiServer, token, { ...mail, body: nl2br(mail.body, true) })
|
||||
console.log('Email gesendet')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
const test = (data) => {
|
||||
console.log('mail: ', mail, data)
|
||||
}
|
||||
|
||||
const nl2br = (str, replaceMode) => {
|
||||
var breakTag = '<br />'
|
||||
var replaceStr = replaceMode ? '$1' + breakTag : '$1' + breakTag + '$2'
|
||||
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, replaceStr)
|
||||
}
|
||||
|
||||
const br2nl = (str, replaceMode) => {
|
||||
var replaceStr = replaceMode ? '\n' : ''
|
||||
// Includes <br>, <BR>, <br />, </br>
|
||||
return str.replace(/<\s*\/?br\s*[\/]?>/gi, replaceStr)
|
||||
}
|
||||
|
||||
const changeValue = ({ target: { name, value } }) => {
|
||||
setMail(() => ({ ...mail, [name]: value }))
|
||||
// setMail(() => ({ ...mail, [name]: value.trim() }));
|
||||
}
|
||||
|
||||
const changeTemplate = ({ target: { value } }) => {
|
||||
setMail(() => ({ ...mail, body: br2nl(value, true) }))
|
||||
}
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<h1>Email senden:</h1>
|
||||
<FormControl>
|
||||
<TextField id="from" name="from" label="from" variant="filled" defaultValue="m.peters@karatenw.de" onChange={changeValue} />
|
||||
</FormControl>
|
||||
<TextField id="to" name="to" label="to" variant="filled" defaultValue="mario@wattsche.de" onChange={changeValue} />
|
||||
<FormControl>
|
||||
<InputLabel id="tid">Tournament</InputLabel>
|
||||
<Select labelId="tid" id="tid" name="tid" value={mail.tid} label="Tournament" onChange={changeValue}>
|
||||
<MenuItem key={0} value="Einzelmail">
|
||||
Einzelmail
|
||||
</MenuItem>
|
||||
{/* Alle Turniere seit 1 Jahr */}
|
||||
{tournaments.map((t) => {
|
||||
const dateOffset = 24 * 60 * 60 * 1000 * 365 // 1 Jahr
|
||||
return (
|
||||
new Date(t.veranstaltungsDatum) > Date.now() - dateOffset && (
|
||||
<MenuItem key={t.id} value={t.id}>
|
||||
{t.name}
|
||||
</MenuItem>
|
||||
)
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<InputLabel id="templates">Templates</InputLabel>
|
||||
<Select labelId="templates" id="templates" name="templates" value="" label="templates" onChange={changeTemplate}>
|
||||
<MenuItem key={0} value={''}>
|
||||
reset
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
key={1}
|
||||
value={`Hallo Zusammen,
|
||||
const templates = [
|
||||
{
|
||||
label: 'reset',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
label: 'Listen Online',
|
||||
value: `Hallo Zusammen,
|
||||
|
||||
Die Listen und den Ablaufplan findet Ihr unter https://old.karateturniere.de/dm22/
|
||||
|
||||
Ebenfalls könnt ihr Euch nun wieder eine Rechnung erstellen lassen und hr seht direkt, auf welchen Pool Eure Teilnehmer starten.
|
||||
Ebenfalls könnt ihr Euch nun wieder eine Rechnung erstellen lassen und ihr seht direkt, auf welchen Pool Eure Teilnehmer starten.
|
||||
|
||||
Auch ein Livestream wird es wieder geben.
|
||||
Am einfachsten ist es, wenn Ihr meinen YouTube Kanal abonniert. (https://www.youtube.com/channel/UCsg32mrh2kesSVFQT9adh1A)
|
||||
@@ -109,34 +39,91 @@ Die Links werde ich im Anschluss auch auf https://karateturniere.de veröffentli
|
||||
|
||||
Hier der direkte Link zu den Streams für die DM: https://karateturniere.de/stream/59
|
||||
|
||||
|
||||
Grüße,
|
||||
Mario Peters`}
|
||||
>
|
||||
Listen Online
|
||||
Mario Peters`,
|
||||
},
|
||||
]
|
||||
|
||||
function nl2br(str) {
|
||||
return (str + '').replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1<br />')
|
||||
}
|
||||
|
||||
function br2nl(str) {
|
||||
return str.replace(/<\s*\/?br\s*[\/]?>/gi, '\n')
|
||||
}
|
||||
|
||||
export default function Mail({ apiServer, token, tournaments }) {
|
||||
const { tid } = useParams()
|
||||
const [mail, setMail] = useState({
|
||||
from: 'm.peters@karatenw.de',
|
||||
to: 'mario@wattsche.de',
|
||||
subject: 'DM 22',
|
||||
body: 'test',
|
||||
tid,
|
||||
})
|
||||
|
||||
const handleChange = ({ target: { name, value } }) => {
|
||||
setMail((prev) => ({ ...prev, [name]: value }))
|
||||
}
|
||||
|
||||
const handleTemplateChange = ({ target: { value } }) => {
|
||||
setMail((prev) => ({ ...prev, body: br2nl(value) }))
|
||||
}
|
||||
|
||||
const sendMailHandler = async () => {
|
||||
try {
|
||||
await sendMail(apiServer, token, { ...mail, body: nl2br(mail.body) })
|
||||
console.log('Email gesendet')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Nur Turniere aus dem letzten Jahr
|
||||
const dateOffset = 365 * 24 * 60 * 60 * 1000
|
||||
const recentTournaments = tournaments.filter((t) => new Date(t.veranstaltungsDatum) > Date.now() - dateOffset)
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<h1>Email senden:</h1>
|
||||
<FormControl fullWidth margin="normal">
|
||||
<TextField id="from" name="from" label="Absender" variant="filled" value={mail.from} onChange={handleChange} />
|
||||
</FormControl>
|
||||
<FormControl fullWidth margin="normal">
|
||||
<TextField id="to" name="to" label="Empfänger" variant="filled" value={mail.to} onChange={handleChange} />
|
||||
</FormControl>
|
||||
<FormControl fullWidth margin="normal">
|
||||
<InputLabel id="tid">Turnier</InputLabel>
|
||||
<Select labelId="tid" id="tid" name="tid" value={mail.tid} label="Turnier" onChange={handleChange}>
|
||||
<MenuItem key={0} value="Einzelmail">
|
||||
Einzelmail
|
||||
</MenuItem>
|
||||
{recentTournaments.map((t) => (
|
||||
<MenuItem key={t.id} value={t.id}>
|
||||
{t.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl fullWidth>
|
||||
<TextField id="subject" name="subject" label="subject" variant="filled" defaultValue="DM 2022" onChange={changeValue} />
|
||||
<FormControl fullWidth margin="normal">
|
||||
<InputLabel id="templates">Vorlagen</InputLabel>
|
||||
<Select labelId="templates" id="templates" name="templates" value="" label="Vorlagen" onChange={handleTemplateChange}>
|
||||
{templates.map((tpl, idx) => (
|
||||
<MenuItem key={idx} value={tpl.value}>
|
||||
{tpl.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl fullWidth>
|
||||
<TextField
|
||||
id="body"
|
||||
label="body"
|
||||
name="body"
|
||||
multiline
|
||||
minRows={4}
|
||||
value={mail.body}
|
||||
variant="filled"
|
||||
// inputProps={{ wrap: "physical" }}
|
||||
// style={{ wordWrap: "break-word" }}
|
||||
onChange={changeValue}
|
||||
/>
|
||||
<FormControl fullWidth margin="normal">
|
||||
<TextField id="subject" name="subject" label="Betreff" variant="filled" value={mail.subject} onChange={handleChange} />
|
||||
</FormControl>
|
||||
<br />
|
||||
<button onClick={test}>test</button>
|
||||
<button onClick={sendMailHandler}>senden</button>
|
||||
<FormControl fullWidth margin="normal">
|
||||
<TextField id="body" label="Nachricht" name="body" multiline minRows={6} value={mail.body} variant="filled" onChange={handleChange} />
|
||||
</FormControl>
|
||||
<Button variant="contained" color="primary" endIcon={<ArrowForwardIcon />} onClick={sendMailHandler} sx={{ mt: 2 }}>
|
||||
Senden
|
||||
</Button>
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { PDFDownloadLink } from '@react-pdf/renderer'
|
||||
// import { PDFDownloadLink, PDFViewer } from '@react-pdf/renderer'
|
||||
import ListPDFTemplate from '../components/PDF/ListPDFTemplate'
|
||||
import { savePdf } from '../utilities/PDF'
|
||||
import Spinner from '../components/Spinner/Spinner'
|
||||
@@ -8,56 +7,50 @@ import useFetch from '../components/UseFetch/UseFetch'
|
||||
import { generateRounds } from '../utilities/Rounds'
|
||||
import { countGroups, generateGroupData } from '../utilities/Groups'
|
||||
|
||||
export default function OnePDFList(props) {
|
||||
const { apiServer, token } = props
|
||||
export default function OnePDFList({ apiServer, token }) {
|
||||
const { tid, gid } = useParams()
|
||||
// console.log('tid: ', tid)
|
||||
// console.log('gid: ', gid)
|
||||
const { data: tournament, loading: tournamentLoading } = useFetch(apiServer + '/tournament/' + tid + token)
|
||||
const { data: tournamentGroups, loading: tournamentGroupsLoading } = useFetch(apiServer + '/group/' + tid + token)
|
||||
const { data: allParticipants, loading: allParticipantsLoading } = useFetch(apiServer + '/tournament/' + tid + '/participants' + token)
|
||||
const { data: allTeams, loading: allTeamsLoading } = useFetch(apiServer + '/teams')
|
||||
const tokenParam = token ? '?token=' + token : ''
|
||||
|
||||
// console.log('tournamentGroups: ', tournamentGroups)
|
||||
const groupIndex = tournamentGroups?.findIndex((x) => x.id === +gid)
|
||||
// console.log('groupIndex: ', groupIndex)
|
||||
const { data: tournament, loading: tournamentLoading } = useFetch(`${apiServer}/tournament/${tid}${tokenParam}`)
|
||||
const { data: tournamentGroups, loading: tournamentGroupsLoading } = useFetch(`${apiServer}/group/${tid}${tokenParam}`)
|
||||
const { data: allParticipants, loading: allParticipantsLoading } = useFetch(`${apiServer}/tournament/${tid}/participants${tokenParam}`)
|
||||
const { data: allTeams, loading: allTeamsLoading } = useFetch(`${apiServer}/teams`)
|
||||
const { data: encounter, loading: encounterLoading } = useFetch(`${apiServer}/group/encounters/${gid}${tokenParam}`)
|
||||
|
||||
const groupData =
|
||||
!tournamentGroupsLoading &&
|
||||
generateGroupData(
|
||||
tournamentGroups[groupIndex].id,
|
||||
tournamentGroups[groupIndex].gid,
|
||||
tournamentGroups[groupIndex].altervon + '-' + tournamentGroups[groupIndex].alterbis,
|
||||
tournamentGroups[groupIndex].gurtVon + '-' + tournamentGroups[groupIndex].gurtBis,
|
||||
tournamentGroups[groupIndex].disziplin,
|
||||
tournamentGroups[groupIndex].geschlecht,
|
||||
tournamentGroups[groupIndex].pool,
|
||||
countGroups(tournamentGroups[groupIndex].gid, tournamentGroups),
|
||||
)
|
||||
// console.log('groupData: ', groupData)
|
||||
|
||||
const { data: encounter, loading: encounterLoading } = useFetch(apiServer + '/group/encounters/' + gid + token)
|
||||
|
||||
if (tournamentGroupsLoading || tournamentLoading || allParticipantsLoading | allTeamsLoading || encounterLoading) {
|
||||
if (tournamentGroupsLoading || tournamentLoading || allParticipantsLoading || allTeamsLoading || encounterLoading) {
|
||||
return <Spinner />
|
||||
}
|
||||
|
||||
const rounds = !tournamentGroupsLoading && generateRounds(encounter, groupData, allTeams, allParticipants, null, null)
|
||||
const groupIndex = tournamentGroups?.findIndex((x) => x.id === +gid)
|
||||
const group = tournamentGroups?.[groupIndex]
|
||||
|
||||
const groupData =
|
||||
group &&
|
||||
generateGroupData(
|
||||
group.id,
|
||||
group.gid,
|
||||
`${group.altervon}-${group.alterbis}`,
|
||||
`${group.gurtVon}-${group.gurtBis}`,
|
||||
group.disziplin,
|
||||
group.geschlecht,
|
||||
group.pool,
|
||||
countGroups(group.gid, tournamentGroups),
|
||||
)
|
||||
|
||||
const rounds = groupData && generateRounds(encounter, groupData, allTeams, allParticipants, null, null)
|
||||
|
||||
return (
|
||||
<>
|
||||
{!tournamentGroupsLoading && groupData && (
|
||||
<PDFDownloadLink document={<ListPDFTemplate {...props} groupData={groupData} tournament={tournament} rounds={rounds} allParticipants={allParticipants} allTeams={allTeams} />}>
|
||||
{({ blob, url, loading, error }) => {
|
||||
console.log(blob, url, loading, error)
|
||||
// url && window.open(url, '_blank')
|
||||
const gid = groupData.gid + ' - ' + groupData.id
|
||||
// const gid = '1-1588'
|
||||
console.log('groupData: ', groupData)
|
||||
console.log('loading: ', loading)
|
||||
blob && savePdf(blob, 'lists', gid, apiServer, token, tid)
|
||||
const result = loading ? 'loading' : 'done'
|
||||
return result
|
||||
{groupData && (
|
||||
<PDFDownloadLink
|
||||
document={
|
||||
<ListPDFTemplate apiServer={apiServer} token={token} groupData={groupData} tournament={tournament} rounds={rounds} allParticipants={allParticipants} allTeams={allTeams} />
|
||||
}
|
||||
>
|
||||
{({ blob, url, loading }) => {
|
||||
const filename = `${groupData.gid} - ${groupData.id}`
|
||||
if (blob) savePdf(blob, 'lists', filename, apiServer, token, tid)
|
||||
return loading ? 'loading' : 'done'
|
||||
}}
|
||||
</PDFDownloadLink>
|
||||
)}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
|
||||
import CloseIcon from '@mui/icons-material/Close'
|
||||
import { lazy, useEffect, useRef, useState } from 'react'
|
||||
import { Link, useParams } from 'react-router-dom'
|
||||
import ParticipantsMobile from '../components/Participants/ParticipantsMobile'
|
||||
import Invoice from '../components/Registration/Invoice'
|
||||
import TeamRegistrationMobile from '../components/Registration/TeamRegistrationMobile'
|
||||
import { filterObject, findGroup, splitGroups } from '../utilities/Groups'
|
||||
import { Button, IconButton, Snackbar, styled } from '@mui/material'
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
|
||||
import CloseIcon from '@mui/icons-material/Close'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ParticipantsMobile from '../components/Participants/ParticipantsMobile'
|
||||
import TeamRegistrationMobile from '../components/Registration/TeamRegistrationMobile'
|
||||
import Invoice from '../components/Registration/Invoice'
|
||||
import { filterObject, findGroup, splitGroups } from '../utilities/Groups'
|
||||
import { getAccount, getRegisteredSingle, getRegisteredTeams, register as registerApi } from '../api/account'
|
||||
|
||||
const PDF = lazy(() => import('../components/PDF/PDF'))
|
||||
|
||||
const PREFIX = 'Registration'
|
||||
|
||||
const classes = {
|
||||
routerLink: `${PREFIX}-routerLink`,
|
||||
}
|
||||
@@ -23,136 +23,64 @@ const Root = styled('div')(({ theme }) => ({
|
||||
},
|
||||
}))
|
||||
|
||||
export default function Registration(props) {
|
||||
const { participants, groups, apiServer, token, tournaments, user } = props
|
||||
export default function Registration({ participants, groups, apiServer, token, tournaments, user }) {
|
||||
const { t } = useTranslation('common')
|
||||
const [account, setAccount] = useState(null)
|
||||
const [countSingle, setCountSingle] = useState(null)
|
||||
const [countTeams, setCountTeams] = useState(null)
|
||||
const { tid } = useParams()
|
||||
const [account, setAccount] = useState(null)
|
||||
const [countSingle, setCountSingle] = useState([])
|
||||
const [countTeams, setCountTeams] = useState([])
|
||||
const [open, setOpen] = useState(false)
|
||||
const [invoice, setInvoice] = useState(false)
|
||||
const [snackMessage, setSnackMessage] = useState(false)
|
||||
|
||||
const [snackMessage, setSnackMessage] = useState('')
|
||||
const switchRef = useRef([])
|
||||
const tGroups = filterObject('turnier_id', parseInt(tid), groups)
|
||||
const { singleGroups, teamGroups } = splitGroups(tGroups)
|
||||
const switchRef = useRef([])
|
||||
const tournament = filterObject('id', parseInt(tid), tournaments)[0]
|
||||
|
||||
const getAccountHandler = () => {
|
||||
getAccount(apiServer, token)
|
||||
.then((responseData) => {
|
||||
setAccount(responseData)
|
||||
return responseData
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
const getRegisteredSingleHandler = () => {
|
||||
getRegisteredSingle(apiServer, tid, token)
|
||||
.then((responseData) => {
|
||||
setCountSingle(responseData)
|
||||
return responseData
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
const getRegisteredTeamsHandler = () => {
|
||||
getRegisteredTeams(apiServer, tid, token)
|
||||
.then((responseData) => {
|
||||
setCountTeams(responseData)
|
||||
return responseData
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
// Daten laden
|
||||
useEffect(() => {
|
||||
getAccountHandler()
|
||||
getRegisteredSingleHandler()
|
||||
getRegisteredTeamsHandler()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
getAccount(apiServer, token).then(setAccount).catch(console.error)
|
||||
getRegisteredSingle(apiServer, tid, token).then(setCountSingle).catch(console.error)
|
||||
getRegisteredTeams(apiServer, tid, token).then(setCountTeams).catch(console.error)
|
||||
}, [apiServer, tid, token])
|
||||
|
||||
// Registrierung
|
||||
const register = (newData) => {
|
||||
registerApi(apiServer, token, newData)
|
||||
.then(() => {
|
||||
if (newData.register === '/registerParticipant' || newData.register === '/deregisterParticipant') {
|
||||
getRegisteredSingleHandler()
|
||||
} else if (newData.register === '/registerTeam' || newData.register === '/deregisterTeam') {
|
||||
getRegisteredTeamsHandler()
|
||||
if (['/registerParticipant', '/deregisterParticipant'].includes(newData.register)) {
|
||||
getRegisteredSingle(apiServer, tid, token).then(setCountSingle)
|
||||
} else if (['/registerTeam', '/deregisterTeam'].includes(newData.register)) {
|
||||
getRegisteredTeams(apiServer, tid, token).then(setCountTeams)
|
||||
}
|
||||
snackOpen()
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
const checkSingleRegistered = (id, discipline) => {
|
||||
let result = false
|
||||
countSingle.forEach((row) => {
|
||||
if (row.disziplin === discipline && row.teilnehmer_id === id) {
|
||||
result = true
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
// Hilfsfunktionen
|
||||
const checkSingleRegistered = (id, discipline) => countSingle.some((row) => row.disziplin === discipline && row.teilnehmer_id === id)
|
||||
|
||||
const checkTeamsRegistered = (id) => {
|
||||
let result = 0
|
||||
countTeams.forEach((row) => {
|
||||
if (row.id === id) {
|
||||
return (result = row.count)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
const checkTeamsRegistered = (id) => countTeams.find((row) => row.id === id)?.count || 0
|
||||
|
||||
const countTeamRegistrations = () => {
|
||||
let result = 0
|
||||
countTeams &&
|
||||
countTeams.forEach((element) => {
|
||||
result += element.count
|
||||
})
|
||||
return result
|
||||
}
|
||||
const countTeamRegistrations = () => countTeams.reduce((sum, el) => sum + (el.count || 0), 0)
|
||||
|
||||
// Snackbar
|
||||
const snackOpen = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
const snackOpen = () => setOpen(true)
|
||||
const snackClose = (event, reason) => {
|
||||
if (reason === 'clickaway') {
|
||||
return
|
||||
}
|
||||
setOpen(false)
|
||||
if (reason !== 'clickaway') setOpen(false)
|
||||
}
|
||||
|
||||
const tooLate = new Date(tournament.meldeschluss).getTime() + 86400000 - Date.now() < 0
|
||||
|
||||
const showInvoice = () => {
|
||||
setInvoice(true)
|
||||
}
|
||||
const tooLate = new Date(tournament?.meldeschluss).getTime() + 86400000 - Date.now() < 0
|
||||
|
||||
const generateAction = (discipline) => {
|
||||
const disciplineName = discipline === 'kata' ? 'Kata-Einzel' : 'Kumite-Einzel'
|
||||
return {
|
||||
icon: discipline,
|
||||
tooltip: discipline.charAt(0).toUpperCase() + discipline.slice(1),
|
||||
registered: (rowData) => {
|
||||
const checked = checkSingleRegistered(rowData.id, disciplineName)
|
||||
return checked
|
||||
},
|
||||
registered: (rowData) => checkSingleRegistered(rowData.id, disciplineName),
|
||||
onClick: (event, rowData) => {
|
||||
if (tooLate) {
|
||||
// TODO: make global
|
||||
setSnackMessage('too late')
|
||||
snackOpen()
|
||||
return
|
||||
@@ -161,22 +89,19 @@ export default function Registration(props) {
|
||||
if (turniergruppeId === 'error') {
|
||||
setSnackMessage('no group found for ' + disciplineName)
|
||||
snackOpen()
|
||||
} else {
|
||||
if (turniergruppeId) {
|
||||
const myRef = switchRef.current[rowData.id + discipline]
|
||||
let registerPath
|
||||
myRef?.classList.contains('Mui-checked') ? (registerPath = '/deregisterParticipant') : (registerPath = '/registerParticipant')
|
||||
register({
|
||||
register: registerPath,
|
||||
teilnehmerId: rowData.id,
|
||||
turniergruppeId: turniergruppeId,
|
||||
})
|
||||
if (registerPath === '/deregisterParticipant') {
|
||||
setSnackMessage(`You deregistred ${rowData.vorname} ${rowData.name} for ${disciplineName}`)
|
||||
} else {
|
||||
setSnackMessage(`You registred ${rowData.vorname} ${rowData.name} for ${disciplineName}`)
|
||||
}
|
||||
}
|
||||
} else if (turniergruppeId) {
|
||||
const myRef = switchRef.current[rowData.id + discipline]
|
||||
const registerPath = myRef?.classList.contains('Mui-checked') ? '/deregisterParticipant' : '/registerParticipant'
|
||||
register({
|
||||
register: registerPath,
|
||||
teilnehmerId: rowData.id,
|
||||
turniergruppeId,
|
||||
})
|
||||
setSnackMessage(
|
||||
registerPath === '/deregisterParticipant'
|
||||
? `You deregistred ${rowData.vorname} ${rowData.name} for ${disciplineName}`
|
||||
: `You registred ${rowData.vorname} ${rowData.name} for ${disciplineName}`,
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -196,7 +121,6 @@ export default function Registration(props) {
|
||||
<h3>
|
||||
{t('registration.closing-date')} {new Date(tournament.meldeschluss).toLocaleDateString()}
|
||||
</h3>
|
||||
|
||||
<ParticipantsMobile
|
||||
switchRef={switchRef}
|
||||
data={participants}
|
||||
@@ -207,9 +131,8 @@ export default function Registration(props) {
|
||||
countSingle={countSingle}
|
||||
getRegisteredSingle={getRegisteredSingle}
|
||||
/>
|
||||
|
||||
<br></br>
|
||||
<br></br>
|
||||
<br />
|
||||
<br />
|
||||
{teamGroups.length === 0 && <h3>Keine Teamgruppen vorhanden</h3>}
|
||||
{countTeams && teamGroups.length > 0 && (
|
||||
<>
|
||||
@@ -226,8 +149,8 @@ export default function Registration(props) {
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<br></br>
|
||||
<br></br>
|
||||
<br />
|
||||
<br />
|
||||
{countTeams && (
|
||||
<Invoice
|
||||
tournaments={tournaments}
|
||||
@@ -237,29 +160,26 @@ export default function Registration(props) {
|
||||
teamCosts={tournament.startgebuehrTeam}
|
||||
/>
|
||||
)}
|
||||
<br></br>
|
||||
<br></br>
|
||||
{invoice && (
|
||||
<PDF
|
||||
apiServer={apiServer}
|
||||
token={token}
|
||||
tournament={tournament}
|
||||
countSingle={countSingle}
|
||||
countTeams={countTeams}
|
||||
countTeamRegistrations={countTeamRegistrations()}
|
||||
singleCosts={tournament.startgebuehr}
|
||||
teamCosts={tournament.startgebuehrTeam}
|
||||
user={user}
|
||||
/>
|
||||
)}
|
||||
<Button onClick={showInvoice} variant="outlined" startIcon={<ArrowBackIcon />}>
|
||||
{t('registration.generate-invoice')}
|
||||
</Button>
|
||||
<br />
|
||||
<br />
|
||||
<Link to={'/'} className={classes.routerLink}>
|
||||
<Button variant="outlined" startIcon={<ArrowBackIcon />}>
|
||||
<Button variant="outlined" startIcon={<ArrowBackIcon />} style={{ marginRight: '2rem' }}>
|
||||
{t('back-to-page')}
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<PDF
|
||||
apiServer={apiServer}
|
||||
token={token}
|
||||
tournament={tournament}
|
||||
countSingle={countSingle}
|
||||
countTeams={countTeams}
|
||||
countTeamRegistrations={countTeamRegistrations()}
|
||||
singleCosts={tournament.startgebuehr}
|
||||
teamCosts={tournament.startgebuehrTeam}
|
||||
user={user}
|
||||
/>
|
||||
|
||||
{/* // TODO: make global */}
|
||||
<Snackbar
|
||||
anchorOrigin={{
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import useFetch from '../components/UseFetch/UseFetch'
|
||||
import { useParams, Link } from 'react-router-dom'
|
||||
import { styled, Button } from '@mui/material'
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useFetch from '../components/UseFetch/UseFetch'
|
||||
import PostResult from '../components/Results/PostResult'
|
||||
import TournamentResults from '../components/Results/TournamentResults'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import { styled, Button } from '@mui/material'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
// TODO: farben anpassen im theme (chips, grautöne)
|
||||
|
||||
const PREFIX = 'Results'
|
||||
|
||||
const classes = {
|
||||
routerLink: `${PREFIX}-routerLink`,
|
||||
}
|
||||
@@ -24,53 +19,37 @@ const Root = styled('div')(() => ({
|
||||
},
|
||||
}))
|
||||
|
||||
export default function Results(props) {
|
||||
const { apiServer, token, user } = props
|
||||
export default function Results({ apiServer, token, user }) {
|
||||
const { t } = useTranslation('common')
|
||||
const { tid } = useParams()
|
||||
const { data: participants } = useFetch(apiServer + '/tournament/' + tid + '/participants')
|
||||
const { data: teams } = useFetch(apiServer + '/teams')
|
||||
const { data: tournament } = useFetch(apiServer + '/tournament/' + tid)
|
||||
const { data: fetchedGroups } = useFetch(apiServer + '/group/' + tid)
|
||||
|
||||
const { data: participants } = useFetch(`${apiServer}/tournament/${tid}/participants`)
|
||||
const { data: teams } = useFetch(`${apiServer}/teams`)
|
||||
const { data: tournament } = useFetch(`${apiServer}/tournament/${tid}`)
|
||||
const { data: fetchedGroups } = useFetch(`${apiServer}/group/${tid}`)
|
||||
const [groups, setGroups] = useState(null)
|
||||
|
||||
console.log('groups', groups)
|
||||
// console.log('participants', participants)
|
||||
// console.log('teams', teams)
|
||||
// console.log('tournament', tournament)
|
||||
|
||||
// console.log('fetchedGroups', fetchedGroups)
|
||||
// console.log('loadingParticipants', loadingParticipants)
|
||||
// console.log('loadingTeams', loadingTeams)
|
||||
// console.log('loadingTournament', loadingTournament)
|
||||
// console.log('loadingFetchedGroups', loadingFetchedGroups)
|
||||
|
||||
useEffect(() => {
|
||||
setGroups(fetchedGroups)
|
||||
}, [fetchedGroups])
|
||||
|
||||
return (
|
||||
fetchedGroups &&
|
||||
tournament &&
|
||||
participants &&
|
||||
teams &&
|
||||
groups && (
|
||||
<Root>
|
||||
<h1>{tournament?.name}</h1>
|
||||
<h2>
|
||||
{new Date(tournament?.veranstaltungsDatum).toLocaleDateString()} @ {tournament?.ort}
|
||||
</h2>
|
||||
<br />
|
||||
<h3>Ergebnisse:</h3>
|
||||
{user?.admin > 2 && token && <PostResult apiServer={apiServer} token={token} groups={groups} setGroups={setGroups} />}
|
||||
<TournamentResults groups={groups} participants={participants} teams={teams} user={user} />
|
||||
if (!(fetchedGroups && tournament && participants && teams && groups)) return null
|
||||
|
||||
<Link to={'/'} className={classes.routerLink}>
|
||||
<Button variant="outlined" startIcon={<ArrowBackIcon />}>
|
||||
{t('back-to-page')}
|
||||
</Button>
|
||||
</Link>
|
||||
</Root>
|
||||
)
|
||||
return (
|
||||
<Root>
|
||||
<h1>{tournament?.name}</h1>
|
||||
<h2>
|
||||
{new Date(tournament?.veranstaltungsDatum).toLocaleDateString()} @ {tournament?.ort}
|
||||
</h2>
|
||||
<br />
|
||||
<h3>Ergebnisse:</h3>
|
||||
{user?.admin > 2 && token && <PostResult apiServer={apiServer} token={token} groups={groups} setGroups={setGroups} />}
|
||||
<TournamentResults groups={groups} participants={participants} teams={teams} user={user} />
|
||||
<Link to={'/'} className={classes.routerLink}>
|
||||
<Button variant="outlined" startIcon={<ArrowBackIcon />}>
|
||||
{t('back-to-page')}
|
||||
</Button>
|
||||
</Link>
|
||||
</Root>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useParams, Link } from 'react-router-dom'
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack'
|
||||
import useFetch from '../components/UseFetch/UseFetch'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { styled, Button } from '@mui/material'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const PREFIX = 'Streams'
|
||||
|
||||
const classes = {
|
||||
stream: `${PREFIX}-stream`,
|
||||
routerLink: `${PREFIX}-routerLink`,
|
||||
}
|
||||
|
||||
// TODO jss-to-styled codemod: The Fragment root was replaced by div. Change the tag if needed.
|
||||
const Root = styled('div')(({ theme }) => ({
|
||||
[`& .${classes.stream}`]: {
|
||||
backgroundColor: 'rgba(0,0,0,0.2)',
|
||||
@@ -29,17 +26,43 @@ const Root = styled('div')(({ theme }) => ({
|
||||
},
|
||||
}))
|
||||
|
||||
export default function Streams(props) {
|
||||
const { apiServer } = props
|
||||
function ShowStreams({ streams }) {
|
||||
if (!streams || Object.keys(streams).length === 0) {
|
||||
return (
|
||||
<div className={classes.stream}>
|
||||
<h2>Kein Stream vorhanden</h2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return Object.entries(streams).map(([key, value]) => (
|
||||
<div className={classes.stream} key={key}>
|
||||
<h2>{key}:</h2>
|
||||
<h3>
|
||||
{value ? (
|
||||
<a href={value} target="_blank" rel="noreferrer">
|
||||
{value}
|
||||
</a>
|
||||
) : (
|
||||
'Kein Link'
|
||||
)}
|
||||
</h3>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
|
||||
export default function Streams({ apiServer }) {
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const { tid } = useParams()
|
||||
const { data: tournament } = useFetch(apiServer + '/tournament/' + tid)
|
||||
const { data: tournament } = useFetch(`${apiServer}/tournament/${tid}`)
|
||||
|
||||
const noStreams = { 'Kein Stream vorhanden': '' }
|
||||
const streams = tournament?.streams !== undefined ? JSON.parse(tournament?.streams) : noStreams
|
||||
let streams = {}
|
||||
try {
|
||||
streams = tournament?.streams ? JSON.parse(tournament.streams) : {}
|
||||
} catch {
|
||||
streams = {}
|
||||
}
|
||||
|
||||
// order streams by key
|
||||
// Streams nach Key sortieren
|
||||
const streamsOrdered = Object.keys(streams)
|
||||
.sort()
|
||||
.reduce((obj, key) => {
|
||||
@@ -47,32 +70,15 @@ export default function Streams(props) {
|
||||
return obj
|
||||
}, {})
|
||||
|
||||
const ShowStreams = () => {
|
||||
const result = []
|
||||
for (const [key, value] of Object.entries(streamsOrdered)) {
|
||||
result.push(
|
||||
<div className={classes.stream} key={key}>
|
||||
<h2>{key}:</h2>
|
||||
<h3>
|
||||
<a href={value} target="_blank" rel="noreferrer">
|
||||
{value}
|
||||
</a>
|
||||
</h3>
|
||||
</div>,
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<h1>{tournament?.name}</h1>
|
||||
<h2>
|
||||
{new Date(tournament?.veranstaltungsDatum).toLocaleDateString()} @ {tournament?.ort}
|
||||
{tournament?.veranstaltungsDatum && new Date(tournament.veranstaltungsDatum).toLocaleDateString()} @ {tournament?.ort}
|
||||
</h2>
|
||||
<br />
|
||||
<h2>Streams:</h2>
|
||||
<ShowStreams />
|
||||
<ShowStreams streams={streamsOrdered} />
|
||||
<Link to={'/'} className={classes.routerLink}>
|
||||
<Button variant="outlined" startIcon={<ArrowBackIcon />}>
|
||||
{t('back-to-page')}
|
||||
|
||||
@@ -1,53 +1,39 @@
|
||||
import { Link, useParams } from 'react-router-dom'
|
||||
import useFetch from '../components/UseFetch/UseFetch'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export default function TournamentPDFLists(props) {
|
||||
const { apiServer, token } = props
|
||||
export default function TournamentPDFLists({ apiServer, token }) {
|
||||
const { tid } = useParams()
|
||||
const { data: tournamentGroups, loading: tournamentGroupsLoading } = useFetch(apiServer + '/group/' + tid + token)
|
||||
const tokenParam = token ? '?token=' + token : ''
|
||||
const { data: tournamentGroups, loading } = useFetch(`${apiServer}/group/${tid}${tokenParam}`)
|
||||
|
||||
if (tournamentGroupsLoading) {
|
||||
return <>loading</>
|
||||
}
|
||||
|
||||
console.log('tournamentGroups: ', tournamentGroups)
|
||||
const groupIds = []
|
||||
tournamentGroups?.forEach((group) => {
|
||||
groupIds.push(group.id)
|
||||
})
|
||||
console.log('groupId[0]: ', groupIds[0])
|
||||
|
||||
const GenLinks = () => {
|
||||
const result = []
|
||||
groupIds?.forEach((id) => {
|
||||
result.push(
|
||||
<>
|
||||
<iframe src={`/pdf/lists/${tid}/one/${id}`} title={id} />
|
||||
<Link to={`./one/${id}`}>
|
||||
<p key={id}>{id}</p>
|
||||
</Link>
|
||||
</>,
|
||||
)
|
||||
})
|
||||
|
||||
//TODO: ohne den code, werden keine pdfs generiert
|
||||
fetch('http://localhost:3000/pdf/lists/60/one/1538')
|
||||
.then((response) => {
|
||||
return response.json()
|
||||
useEffect(() => {
|
||||
// PDF-Generierung für alle Gruppen anstoßen
|
||||
if (tournamentGroups && tournamentGroups.length > 0) {
|
||||
tournamentGroups.forEach((group) => {
|
||||
fetch(`/pdf/lists/${tid}/one/${group.id}`)
|
||||
.then(() => {})
|
||||
.catch((err) => console.error(err))
|
||||
})
|
||||
.then((responseData) => {
|
||||
// console.log('getAccount: ', responseData);
|
||||
return responseData
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
return result
|
||||
}
|
||||
}, [tournamentGroups, tid])
|
||||
|
||||
if (loading) return <>loading</>
|
||||
|
||||
if (!tournamentGroups || tournamentGroups.length === 0) {
|
||||
return <div>Keine Gruppen gefunden</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<GenLinks />
|
||||
{tournamentGroups.map((group) => (
|
||||
<div key={group.id} style={{ marginBottom: 24 }}>
|
||||
<iframe src={`/pdf/lists/${tid}/one/${group.id}`} title={`PDF Gruppe ${group.id}`} style={{ width: '100%', height: 400, border: '1px solid #ccc' }} />
|
||||
<Link to={`./one/${group.id}`}>
|
||||
<p>{group.id}</p>
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,38 +1,33 @@
|
||||
const beltClassMap = {
|
||||
DAN: 'black-belt',
|
||||
'1. Kyu': 'brown-belt',
|
||||
'2. Kyu': 'brown-belt',
|
||||
'3. Kyu': 'brown-belt',
|
||||
'4. Kyu': 'violet-belt',
|
||||
'5. Kyu': 'violet-belt',
|
||||
'6. Kyu': 'green-belt',
|
||||
'7. Kyu': 'orange-belt',
|
||||
'8. Kyu': 'yellow-belt',
|
||||
'9. Kyu': 'white-belt',
|
||||
}
|
||||
|
||||
const beltColorMap = {
|
||||
DAN: 'rgba(0, 0, 0, 0.50)',
|
||||
'1. Kyu': 'rgba(168, 88, 27, 0.5)',
|
||||
'2. Kyu': 'rgba(168, 88, 27, 0.5)',
|
||||
'3. Kyu': 'rgba(168, 88, 27, 0.5)',
|
||||
'4. Kyu': 'rgba(138, 43, 226, 0.5)',
|
||||
'5. Kyu': 'rgba(138, 43, 226, 0.5)',
|
||||
'6. Kyu': 'rgba(0, 128, 0, 0.5)',
|
||||
'7. Kyu': 'rgba(255, 135, 18, 0.6)',
|
||||
'8. Kyu': 'rgba(255, 255, 0, 0.6)',
|
||||
'9. Kyu': 'white',
|
||||
}
|
||||
|
||||
export function BeltClass(belt) {
|
||||
let beltClass
|
||||
if (belt === 'DAN') {
|
||||
beltClass = 'black-belt'
|
||||
} else if (belt === '1. Kyu' || belt === '2. Kyu' || belt === '3. Kyu') {
|
||||
beltClass = 'brown-belt'
|
||||
} else if (belt === '4. Kyu' || belt === '5. Kyu') {
|
||||
beltClass = 'violet-belt'
|
||||
} else if (belt === '6. Kyu') {
|
||||
beltClass = 'green-belt'
|
||||
} else if (belt === '7. Kyu') {
|
||||
beltClass = 'orange-belt'
|
||||
} else if (belt === '8. Kyu') {
|
||||
beltClass = 'yellow-belt'
|
||||
} else if (belt === '9. Kyu') {
|
||||
beltClass = 'white-belt'
|
||||
}
|
||||
return beltClass
|
||||
return beltClassMap[belt] || ''
|
||||
}
|
||||
|
||||
export function beltColor(belt) {
|
||||
let color
|
||||
if (belt === 'DAN') {
|
||||
color = 'rgba(0, 0, 0, 0.50)'
|
||||
} else if (belt === '1. Kyu' || belt === '2. Kyu' || belt === '3. Kyu') {
|
||||
color = 'rgba(168, 88, 27, 0.5)'
|
||||
} else if (belt === '4. Kyu' || belt === '5. Kyu') {
|
||||
color = 'rgba(138, 43, 226, 0.5)'
|
||||
} else if (belt === '6. Kyu') {
|
||||
color = 'rgba(0, 128, 0, 0.5)'
|
||||
} else if (belt === '7. Kyu') {
|
||||
color = 'rgba(255, 135, 18, 0.6)'
|
||||
} else if (belt === '8. Kyu') {
|
||||
color = 'rgba(255, 255, 0, 0.6)'
|
||||
} else if (belt === '9. Kyu') {
|
||||
color = 'white'
|
||||
}
|
||||
return color
|
||||
return beltColorMap[belt] || ''
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
export function convertDate(date) {
|
||||
const result = new Date(date)
|
||||
return result.getDay() + '.' + result.getMonth() + '.' + result.getFullYear()
|
||||
const d = new Date(date)
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const year = d.getFullYear()
|
||||
return `${day}.${month}.${year}`
|
||||
}
|
||||
|
||||
export function getAge(birthDate, tournamentDate) {
|
||||
const birth = new Date(birthDate)
|
||||
const tournament = (tournamentDate && new Date(tournamentDate)) || new Date() // Fallback auf das aktuelle Datum, falls kein Turnierdatum angegeben ist
|
||||
|
||||
// Jahre direkt vergleichen
|
||||
const tournament = tournamentDate ? new Date(tournamentDate) : new Date()
|
||||
let age = tournament.getFullYear() - birth.getFullYear()
|
||||
|
||||
// Monate und Tage vergleichen, um zu prüfen, ob der Geburtstag im Zieljahr schon war
|
||||
const monthDifference = tournament.getMonth() - birth.getMonth()
|
||||
|
||||
if (monthDifference < 0 || (monthDifference === 0 && tournament.getDate() < birth.getDate())) {
|
||||
age-- // Wenn der Geburtstag noch nicht war, ein Jahr abziehen
|
||||
const m = tournament.getMonth() - birth.getMonth()
|
||||
if (m < 0 || (m === 0 && tournament.getDate() < birth.getDate())) {
|
||||
age--
|
||||
}
|
||||
|
||||
return age
|
||||
}
|
||||
|
||||
@@ -1,33 +1,23 @@
|
||||
import { getAge } from './Date'
|
||||
|
||||
/**
|
||||
* Aus alle Gruppen wird eine bestimmte Gruppe für das Turnier
|
||||
*
|
||||
*
|
||||
* @param {string} key - A string param
|
||||
* @param {string} val - A string param
|
||||
* @param {object} groups - A string param
|
||||
*
|
||||
* @return {string} A good string
|
||||
*
|
||||
* Filtert ein Array von Objekten nach einem bestimmten Key/Value.
|
||||
* @param {string} key
|
||||
* @param {string|number} val
|
||||
* @param {Array} arr
|
||||
* @returns {Array}
|
||||
*/
|
||||
export const filterObject = (key, val, object) => {
|
||||
let result = []
|
||||
if (object) {
|
||||
object.forEach((row) => {
|
||||
if (row[key] == val) {
|
||||
result.push(row)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
}
|
||||
export const filterObject = (key, val, arr) => (Array.isArray(arr) ? arr.filter((row) => row[key] == val) : [])
|
||||
|
||||
// Gruppen nach Einzel / Team aufsplitten
|
||||
export const splitGroups = (group) => {
|
||||
let singleGroups = []
|
||||
let teamGroups = []
|
||||
group.forEach((row) => {
|
||||
/**
|
||||
* Teilt Gruppen in Einzel- und Teamgruppen auf.
|
||||
* @param {Array} groups
|
||||
* @returns {{singleGroups: Array, teamGroups: Array}}
|
||||
*/
|
||||
export const splitGroups = (groups = []) => {
|
||||
const singleGroups = []
|
||||
const teamGroups = []
|
||||
groups.forEach((row) => {
|
||||
if (row.disziplin === 'Kumite-Team' || row.disziplin === 'Kata-Team' || row.disziplin === 'Kata-Team-Mixed') {
|
||||
teamGroups.push(row)
|
||||
} else {
|
||||
@@ -37,50 +27,40 @@ export const splitGroups = (group) => {
|
||||
return { singleGroups, teamGroups }
|
||||
}
|
||||
|
||||
// Passende Gruppe finden um einen Teilnehmer zu melden.
|
||||
export const findGroup = (participant, groups, dicipline, tournamentDate) => {
|
||||
let result = []
|
||||
/**
|
||||
* Findet passende Gruppe für einen Teilnehmer.
|
||||
* @param {object} participant
|
||||
* @param {Array} groups
|
||||
* @param {string} discipline
|
||||
* @param {string|Date} tournamentDate
|
||||
* @returns {Array|String}
|
||||
*/
|
||||
export const findGroup = (participant, groups, discipline, tournamentDate) => {
|
||||
const age = getAge(participant.gebDatum, tournamentDate)
|
||||
const belt = parseInt(participant.gurt.charAt(0)) ? parseInt(participant.gurt.charAt(0)) : 0
|
||||
groups.forEach((row) => {
|
||||
if (
|
||||
row.disziplin === dicipline &&
|
||||
row.geschlecht === participant.geschlecht &&
|
||||
(parseInt(row.gurtVon.charAt(0)) ? parseInt(row.gurtVon.charAt(0)) : 0) >= belt &&
|
||||
belt >= (parseInt(row.gurtBis.charAt(0)) ? parseInt(row.gurtBis.charAt(0)) : 0) &&
|
||||
row.altervon <= age &&
|
||||
age <= row.alterbis
|
||||
) {
|
||||
result.push(row)
|
||||
}
|
||||
const belt = parseInt(participant.gurt) || 0
|
||||
const result = groups.filter((row) => {
|
||||
const gurtVon = parseInt(row.gurtVon) || 0
|
||||
const gurtBis = parseInt(row.gurtBis) || 0
|
||||
return row.disziplin === discipline && row.geschlecht === participant.geschlecht && gurtVon >= belt && belt >= gurtBis && row.altervon <= age && age <= row.alterbis
|
||||
})
|
||||
if (result.length > 0) {
|
||||
console.log('passende Gruppen', result)
|
||||
return result
|
||||
} else {
|
||||
return 'error'
|
||||
}
|
||||
return result.length > 0 ? result : 'error'
|
||||
}
|
||||
|
||||
export const generateGroupData = (id, gid, age, belt, discipline, gender, pool, count) => {
|
||||
return {
|
||||
id,
|
||||
gid,
|
||||
age,
|
||||
belt,
|
||||
discipline,
|
||||
gender,
|
||||
pool,
|
||||
count,
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Erstellt ein Gruppen-Objekt für PDFs etc.
|
||||
*/
|
||||
export const generateGroupData = (id, gid, age, belt, discipline, gender, pool, count) => ({
|
||||
id,
|
||||
gid,
|
||||
age,
|
||||
belt,
|
||||
discipline,
|
||||
gender,
|
||||
pool,
|
||||
count,
|
||||
})
|
||||
|
||||
export const countGroups = (gid, tournamentGroups) => {
|
||||
let count = 0
|
||||
for (let i in tournamentGroups) {
|
||||
if (tournamentGroups[i].gid === gid && tournamentGroups[i].pool !== 'Final') {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
/**
|
||||
* Zählt die Anzahl der Gruppen mit gleichem gid (außer Final).
|
||||
*/
|
||||
export const countGroups = (gid, tournamentGroups = []) => tournamentGroups.filter((g) => g.gid === gid && g.pool !== 'Final').length
|
||||
|
||||
@@ -2,16 +2,14 @@ import { savePdf as savePdfApi } from '../api/pdf'
|
||||
|
||||
export const savePdf = (blob, categorie, gid, apiServer, token, tid) => {
|
||||
const reader = new FileReader()
|
||||
blob.text().then(() => {
|
||||
reader.onloadend = async () => {
|
||||
const base64 = reader.result
|
||||
try {
|
||||
await savePdfApi(apiServer, token, base64, categorie, gid, tid)
|
||||
console.log('PDF gespeichert')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
reader.onloadend = async () => {
|
||||
const base64 = reader.result.split(',')[1] // Nur der Base64-Teil
|
||||
try {
|
||||
await savePdfApi(apiServer, token, base64, categorie, gid, tid)
|
||||
console.log('PDF gespeichert')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
reader.readAsBinaryString(blob)
|
||||
})
|
||||
}
|
||||
reader.readAsDataURL(blob)
|
||||
}
|
||||
|
||||
@@ -3,48 +3,49 @@ import KoEncounter from '../components/Groups/KoEncounter'
|
||||
export const generateRounds = (encounter, groupData, allTeams, allParticipants, change, setWinner) => {
|
||||
const rounds = { r1: [], r2: [], r3: [], r4: [] }
|
||||
|
||||
encounter &&
|
||||
encounter.forEach((element) => {
|
||||
let aka, shiro
|
||||
if (!encounter) return rounds
|
||||
|
||||
if (groupData.discipline.includes('Team')) {
|
||||
aka = allTeams.find((x) => x.id === element.aka)
|
||||
shiro = allTeams.find((x) => x.id === element.shiro)
|
||||
} else {
|
||||
aka = allParticipants.find((x) => x.id === element.aka)
|
||||
shiro = allParticipants.find((x) => x.id === element.shiro)
|
||||
}
|
||||
encounter.forEach((element) => {
|
||||
let aka, shiro
|
||||
|
||||
const encounterData = {
|
||||
encounters: {
|
||||
id: element.id,
|
||||
encounter: element.begegnung,
|
||||
},
|
||||
aka: {
|
||||
id: element.aka,
|
||||
prename: aka?.vorname,
|
||||
name: aka?.name,
|
||||
club: aka?.verein ?? aka?.teamName,
|
||||
},
|
||||
shiro: {
|
||||
id: element.shiro,
|
||||
prename: shiro?.vorname,
|
||||
name: shiro?.name,
|
||||
club: shiro?.verein ?? shiro?.teamName,
|
||||
},
|
||||
}
|
||||
if (groupData.discipline.includes('Team')) {
|
||||
aka = allTeams.find((x) => x.id === element.aka)
|
||||
shiro = allTeams.find((x) => x.id === element.shiro)
|
||||
} else {
|
||||
aka = allParticipants.find((x) => x.id === element.aka)
|
||||
shiro = allParticipants.find((x) => x.id === element.shiro)
|
||||
}
|
||||
|
||||
if (element?.begegnung >= 8) {
|
||||
rounds.r1.push(<KoEncounter key={element?.begegnung} encounterData={encounterData} change={change} num={15} setWinner={setWinner} />)
|
||||
} else if (3 < element?.begegnung && element?.begegnung < 8) {
|
||||
rounds.r2.push(<KoEncounter key={element?.begegnung} encounterData={encounterData} change={change} num={7} setWinner={setWinner} />)
|
||||
} else if (1 < element?.begegnung && element?.begegnung < 4) {
|
||||
rounds.r3.push(<KoEncounter key={element?.begegnung} encounterData={encounterData} change={change} num={3} setWinner={setWinner} />)
|
||||
} else if (element?.begegnung === 1) {
|
||||
rounds.r4.push(<KoEncounter key={element?.begegnung} encounterData={encounterData} change={change} num={1} setWinner={setWinner} />)
|
||||
}
|
||||
})
|
||||
const encounterData = {
|
||||
encounters: {
|
||||
id: element.id,
|
||||
encounter: element.begegnung,
|
||||
},
|
||||
aka: {
|
||||
id: element.aka,
|
||||
prename: aka?.vorname,
|
||||
name: aka?.name,
|
||||
club: aka?.verein ?? aka?.teamName,
|
||||
},
|
||||
shiro: {
|
||||
id: element.shiro,
|
||||
prename: shiro?.vorname,
|
||||
name: shiro?.name,
|
||||
club: shiro?.verein ?? shiro?.teamName,
|
||||
},
|
||||
}
|
||||
|
||||
// Runde bestimmen
|
||||
if (element?.begegnung >= 8) {
|
||||
rounds.r1.push(<KoEncounter key={element?.begegnung} encounterData={encounterData} change={change} num={15} setWinner={setWinner} />)
|
||||
} else if (element?.begegnung > 3 && element?.begegnung < 8) {
|
||||
rounds.r2.push(<KoEncounter key={element?.begegnung} encounterData={encounterData} change={change} num={7} setWinner={setWinner} />)
|
||||
} else if (element?.begegnung > 1 && element?.begegnung < 4) {
|
||||
rounds.r3.push(<KoEncounter key={element?.begegnung} encounterData={encounterData} change={change} num={3} setWinner={setWinner} />)
|
||||
} else if (element?.begegnung === 1) {
|
||||
rounds.r4.push(<KoEncounter key={element?.begegnung} encounterData={encounterData} change={change} num={1} setWinner={setWinner} />)
|
||||
}
|
||||
})
|
||||
|
||||
// console.log('rounds', rounds)
|
||||
return rounds
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user