diff --git a/app/Components/ResultsVyfuk/results.tsx b/app/Components/ResultsVyfuk/results.tsx index 25f794f5..e233d1cd 100644 --- a/app/Components/ResultsVyfuk/results.tsx +++ b/app/Components/ResultsVyfuk/results.tsx @@ -1,8 +1,6 @@ import React, { useState } from 'react'; import ReactDOM from 'react-dom'; import './style.scss'; -import { TranslatorProvider, useTranslator } from './resultsTranslator'; //Pokud bude natvrdo česky napsaný, tak není potřeba -import { s } from '@fullcalendar/core/internal-common'; interface Contestant { contestant: { @@ -29,468 +27,6 @@ type Tasks = { [series: number]: Array; }; -interface Props { - resultsData: { - submits: { - [key in Category]: Submits; - } - tasks: { - [key in Category]: Tasks; - } - }, - series: number[] -} - -function CategoryResults({ submits, tasks, isAllCategories = false, serie = 0 }: { submits: Submits, tasks: Tasks, isAllCategories?: boolean, serie?: number }) { - const { translate } = useTranslator(); - const [activeSeries, setActiveSeries] = useState<{ [key: string]: boolean }>({}); - const [sortColumn, setSortColumn] = useState('Category Rank'); - const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc'); - - const toggleSort = (column: string) => { - if (sortColumn === column) { - setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); - } else { - setSortColumn(column); - setSortDirection('asc'); - } - }; - - // calculate the sumSum for each contestant - for (const contestant of submits) { - let sumAll = 0; - for (const series in tasks) { - if (serie > 0 && serie != parseInt(series)) - continue - const tasksInSeries = tasks[series]; - let sum = 0; - for (const task of tasksInSeries) { - if (contestant.submits.hasOwnProperty(task.taskId)) { - if (contestant.submits[task.taskId] !== null) { - sum += contestant.submits[task.taskId]; - } - } - } - sumAll += sum; - } - contestant.sum = sumAll; - } - - - // if isAllCategories, calculate total rank, i.e. sort contestants by their points sum - if (isAllCategories) { - // create a copy of the array - const tempSubmits = [...submits]; - - tempSubmits.sort((a, b) => { - return b.sum - a.sum; - }); - - let currentRank = 0; - let sharedRankStart = 0; - - tempSubmits.forEach((contestant, index) => { - if (index === 0 || contestant.sum !== tempSubmits[index - 1].sum) { - if (index > 0 && sharedRankStart < index - 1) { - // Update shared ranks for previous group - for (let i = sharedRankStart; i < index; i++) { - tempSubmits[i].totalRank = [sharedRankStart + 1, index]; - } - } - currentRank = index + 1; - sharedRankStart = index; - } - contestant.totalRank = [currentRank, currentRank]; - }); - - // Handle the last group - if (sharedRankStart < tempSubmits.length - 1) { - for (let i = sharedRankStart; i < tempSubmits.length; i++) { - tempSubmits[i].totalRank = [sharedRankStart + 1, tempSubmits.length]; - } - } - } - - const sortedSubmits = React.useMemo(() => { - if (sortColumn) { - const sorted = [...submits]; - sorted.sort((a, b) => { - if (sortColumn === 'Name') { - const lastNameA = a.contestant.name.split(' ').pop(); - const lastNameB = b.contestant.name.split(' ').pop(); - return sortDirection === 'asc' ? lastNameA.localeCompare(lastNameB) : lastNameB.localeCompare(lastNameA); - } else if (sortColumn === 'School') { - const schoolA = a.contestant.school || ''; - const schoolB = b.contestant.school || ''; - return sortDirection === 'asc' ? schoolA.localeCompare(schoolB) : schoolB.localeCompare(schoolA); - } else if (sortColumn === 'Category Rank') { - if (isAllCategories) { - return sortDirection === 'asc' ? a.totalRank[0] - b.totalRank[0] : b.totalRank[0] - a.totalRank[0]; - } - return sortDirection === 'asc' ? a.rank[0] - b.rank[0] : b.rank[0] - a.rank[0]; - } else if (sortColumn === 'Category') { - return sortDirection === 'asc' ? a.category - b.category : b.category - a.category; - } - return 0; - }); - return sorted; - } - return submits; - }, [submits, sortColumn, sortDirection, serie]); - - return ( - - - - - {isAllCategories ? () : null} - - - {Object.entries(tasks).map(([series, tasksInSeries]) => { - if (serie > 0 && serie != parseInt(series)) - return (null); - return ( - - {(activeSeries[series] || serie > 0) && tasksInSeries.map((task, index) => ( - - ))} - - - ); - })} - {serie == 0 ? - : null - } - - - - {isAllCategories ? : null} - - {Object.entries(tasks).map(([series, tasksInSeries]) => { - if (serie > 0 && serie != parseInt(series)) - return (null); - const seriesMaxPoints = tasksInSeries.reduce((sum, task) => sum + (typeof task.points === 'number' ? task.points : 0), 0); - return ( - - {(activeSeries[series] || serie > 0) && tasksInSeries.map((task, index) => ( - - ))} - - - ); - })} - {serie == 0 ? - : null - } - - - - {sortedSubmits.map((contestant, index) => { - const seriesContainers = []; - let showContestant = false; - for (const series in tasks) { - if (serie > 0 && serie != parseInt(series)) - continue - const tasksInSeries = tasks[series]; - let [showPerSeriesContestant, element]: any = SeriesResults( - { - series: series, - tasks: tasksInSeries, - contestant: contestant, - show: activeSeries[series] || serie > 0 - }); - showContestant ||= showPerSeriesContestant; - seriesContainers.push(element); - } - return showContestant ? ( - - - {isAllCategories ? : null} - - - {seriesContainers} - { - serie == 0 ? - : null - } - - ) : null; - })} - -
toggleSort('Category Rank')} - > - {isAllCategories ? translate('totalRank') : translate('categoryRank')}  - {sortColumn === 'Category Rank' ? ( - - {sortDirection === 'asc' ? '↑' : '↓'} - - ) : ( - - ↓ - - )} - toggleSort('Category')} - > - {translate('category')}
- {sortColumn === 'Category' ? ( - - {sortDirection === 'asc' ? '↑' : '↓'} - - ) : ( - - ↓ - - )} -
toggleSort('Name')} - > - {translate('name')}
- {sortColumn === 'Name' ? ( - - {sortDirection === 'asc' ? '↑' : '↓'} - - ) : ( - - ↓ - - )} -
toggleSort('School')} - > - {translate('school')}
- {sortColumn === 'School' ? ( - - {sortDirection === 'asc' ? '↑' : '↓'} - - ) : ( - - ↓ - - )} -
- {task.label} - { - const newActiveSeries = { [series]: !activeSeries[series] }; - for (const s in activeSeries) { - if (s !== series) { - newActiveSeries[s] = false; - } - } - setActiveSeries(newActiveSeries); - }} - className="centered-cell clickable-header" - > - s{series}
- {serie > 0 ? null : (activeSeries[series] ? : )} -
toggleSort('Category Rank')} - > - {translate('totalPoints')} - {sortColumn === 'Category Rank' ? ( - - {sortDirection === 'asc' ? '↓' : '↑'} - - ) : ( - - ↓ - - )} -
{translate('maxNumPointsHeader')} - {task.points} - - {seriesMaxPoints} - - {Object.values(tasks).reduce((totalSum, tasksInSeries) => - totalSum + tasksInSeries.reduce((seriesSum, task) => seriesSum + (typeof task.points === 'number' ? task.points : 0), 0), 0 - )} -
{isAllCategories ? (contestant.totalRank[0] === contestant.totalRank[1] ? contestant.totalRank[0] : `${contestant.totalRank[0]}-${contestant.totalRank[1]}`) : (contestant.rank[0] === contestant.rank[1] ? contestant.rank[0] : `${contestant.rank[0]}-${contestant.rank[1]}`)}{contestant.category}{contestant.contestant.name}{contestant.contestant.school} - {contestant.sum} -
- ); -} - -interface SeriesResultsProps { - tasks: Array; - contestant: Contestant; - show: boolean; - series: string; -} - -function SeriesResults({ - tasks, - contestant, - show, - series, -}: SeriesResultsProps) { - let sum = 0; - let subTasks: JSX.Element[] = []; - let showContestant = false; - subTasks = tasks.map((task, index) => { - let badge; - if (contestant.submits.hasOwnProperty(task.taskId)) { - showContestant = true; - if (contestant.submits[task.taskId] === null) { - badge = ?; - } else { - sum += contestant.submits[task.taskId]; - badge = {contestant.submits[task.taskId]}; - } - } else { - badge = -; - } - const isFirstSeries = index === 0; - const isLastSeries = index === tasks.length - 1; - const classNames = `centered-cell ${isFirstSeries ? 'border-left' : ''} ${isLastSeries ? 'border-right' : ''}`; - return ( - - {badge} - - ); - }); - return ([showContestant, - <> - {show && subTasks} - - {sum} - - ] - ); -} - -function OLDResults({ resultsData, series }: Props) { - const { translate } = useTranslator(); - const [activeCategories, setActiveCategories] = useState<{ [key: string]: boolean }>({}); - const [selectedSerie, setSelectedSerie] = useState(0); - - const toggleCategory = (category: string) => { - setActiveCategories((prevActiveCategories) => ({ - ...prevActiveCategories, - [category]: !prevActiveCategories[category], - })); - }; - - const sortedCategories = Object.keys(resultsData.submits).sort((a, b) => { - const categoryNumberA = parseInt(a.slice(-1), 10); - const categoryNumberB = parseInt(b.slice(-1), 10); - return categoryNumberB - categoryNumberA; - }); - - // assign contestants to categories, it is the last character of the key of the submits object - // for category, values is submits -> for contestant in values -> contestant.category = key - for (const category in resultsData.submits) { - for (const contestant of resultsData.submits[category]) { - contestant.category = parseInt(category.slice(-1), 10); - } - } - - let categoryContainers = sortedCategories.map((category) => { - const categoryNumber = category.slice(-1); - return ( -
- -
- -
-
- ); - }); - let serieSelection = series.map((number) => { - if (number < 7) - return ( - - ) - else - return ( - - ) - }) - serieSelection.push( - - ) - categoryContainers = [ -
- {serieSelection} -
, - ...categoryContainers] - - // append total results, i.e. results for all categories - const allCategories = Object.values(resultsData.submits).reduce((allCategories, categorySubmits) => { - return allCategories.concat(categorySubmits); - }, []); - - const allTasks = resultsData.tasks[sortedCategories[0]]; - - categoryContainers.push( -
- -
- -
-
- ); - - - - return
{categoryContainers}
; -} - function Results({ data, series }: { data: { submits: { [key: string]: Submits; }, tasks: { [key: string]: Tasks; } }, series: number[] }) { const [selectedSerie, setSelectedSerie] = useState(0); const sortedCategories = Object.keys(data.submits).sort((a, b) => { @@ -548,7 +84,7 @@ function Results({ data, series }: { data: { submits: { [key: string]: Submits; taskLockup[t.taskId] = columns.length; columns.push({ colKey: "s" + serie + "." + t.label, label: t.label + "(" + t.points + "b.)", sortable: true, numerical: true }); } - columns.push({ colKey: "s" + serie, label: "s" + serie, sortable: true, numerical: true }); + columns.push({ colKey: "s" + serie, label: (parseInt(serie) > 6 ? String(parseInt(serie) - 7) + ".p" : serie) + ".s.", sortable: true, numerical: true }); } columns.push({ colKey: "sum", label: "Celkem\nbodů", sortable: true, numerical: true }); @@ -584,7 +120,26 @@ function Results({ data, series }: { data: { submits: { [key: string]: Submits; let tableManager: any = {}; [tableManager.sortColum, tableManager.setSortColum] = useState("sum"); [tableManager.sortAsc, tableManager.setSortAsc] = useState(false); - [tableManager.hideColum, tableManager.setHideColum] = useState([]); + [tableManager.hideColum, tableManager.setHideColum] = useState({}); + + let hidden: string[] = [] + let keys: string[] = tableDef.columns.map((col: ColumnDef) => { return col.colKey }); + if (selectedSerie < 1) { + hidden = keys.filter((v) => { + if (v == "name" || v == "school" || v == "sum" || v =="rank") return false; + return !/^s[0-9]$/.test(v); + }) + } else { + hidden = keys.filter((v) => { + if (v == "name" || v == "school" || v == "sum" || v =="rank") return false; + return !(new RegExp("s" + selectedSerie)).test(v); + }) + } + tableManager.hideColum = hidden.reduce((prev: any, c) => { + prev[c] = true; + return prev; + }, {}); + tableDef.tableManager = tableManager; categoryContainers.push( @@ -681,13 +236,11 @@ function SortTable({ tableDef }: { tableDef: TableDef }) { data = data.slice(0, 10); } let tableBody: JSX.Element[] = []; - console.log(data) for (let dat of data) { - console.log(dat) let dataRow: JSX.Element[] = []; let show = false; for (let c of tableDef.columns) { - if (!tableDef.tableManager.hideColum.hasOwnProperty(c.colKey) || tableDef.tableManager.hideColum[c.colKey]) { + if (!tableDef.tableManager.hideColum.hasOwnProperty(c.colKey) || !tableDef.tableManager.hideColum[c.colKey]) { show ||= typeof dat[c.colKey] == 'number' dataRow.push({dat[c.colKey]}) } diff --git a/app/Components/ResultsVyfuk/resultsTranslator.tsx b/app/Components/ResultsVyfuk/resultsTranslator.tsx deleted file mode 100644 index 72f42624..00000000 --- a/app/Components/ResultsVyfuk/resultsTranslator.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import React, { createContext, useContext, useState } from 'react'; - -type Lang = 'cs' | 'en'; - -interface Translations { - [key: string]: { - cs: string; - en: string; - }; -} - -const TranslatorContext = createContext<{ - translate: (key: string, replacements?: { [key: string]: string | number }) => React.ReactNode; - translateText: (key: string, replacements?: { [key: string]: string | number }) => string; - language: Lang; -}>({ - translate: () => '', - translateText: () => '', - language: 'en', -}); - -const translations: Translations = { - categoryRank: { - cs: 'Pořadí
v kategorii', - en: 'Category
Rank', - }, - name: { - cs: 'Jméno', - en: 'Name', - }, - school: { - cs: 'Škola', - en: 'School', - }, - totalPoints: { - cs: 'Celkem
bodů', - en: 'Total
Points', - }, - selectYear: { - cs: 'Vybrat ročník', - en: 'Select Year', - }, - categoryLabel: { - cs: 'Kategorie {categoryNumber}. ročníků', - en: 'Category of high-school year {categoryNumber}', - }, - maxNumPointsHeader: { - cs: 'Maximální počet bodů', - en: 'Max Number of Points', - }, - // sumOneToThree: { - // cs: 's​1-3', // Using zero-width space to allow break between 's' and '1-3' - // en: 's​1-3', // Using zero-width space to allow break between 's' and '1-3' - // }, - // sumFourToSix: { - // cs: 's​4-6', // Using zero-width space to allow break between 's' and '4-6' - // en: 's​4-6', // Using zero-width space to allow break between 's' and '4-6' - // }, - allCategories: { - cs: 'Všechny kategorie', - en: 'All Categories', - }, - totalRank: { - cs: 'Celkové
pořadí', - en: 'Total
Rank', - }, - category: { - cs: 'Kat.', - en: 'Cat.', - }, -}; - -export class Translator { - private language: Lang; - - constructor() { - const htmlElement = document.getElementsByTagName('html')[0]; - this.language = (htmlElement.lang as Lang) || 'en'; - } - - private applyReplacements(text: string, replacements: { [key: string]: string | number }): string { - return Object.entries(replacements).reduce((acc, [placeholder, value]) => { - return acc.replace(new RegExp(`{${placeholder}}`, 'g'), String(value)); - }, text); - } - - public translate(key: string, replacements: { [key: string]: string | number } = {}): React.ReactNode { - const text = translations[key]?.[this.language] || key; - const replacedText = this.applyReplacements(text, replacements); - - return ; - } - - public translateText(key: string, replacements: { [key: string]: string | number } = {}): string { - const text = translations[key]?.[this.language] || key; - return this.applyReplacements(text, replacements).replace(//gi, '\n'); - } - - public getLanguage(): Lang { - return this.language; - } -} - -export const TranslatorProvider: React.FC = ({ children }) => { - const [translator] = useState(() => new Translator()); - - const contextValue = { - translate: translator.translate.bind(translator), - translateText: translator.translateText.bind(translator), - language: translator.getLanguage(), - }; - - return ( - - {children} - - ); -}; - -export const useTranslator = () => useContext(TranslatorContext);