removed forum, todos, added p2p info page & lots of minor changes/fixes

This commit is contained in:
mrfry 2023-03-26 19:16:56 +02:00
parent 32522097c0
commit e2d304c130
28 changed files with 303 additions and 954 deletions

View file

@ -6,7 +6,15 @@ function highlightText(text, toHighlight) {
}
try {
const re = new RegExp(toHighlight, 'gi')
return text.replace(re, `<mark>${toHighlight}</mark>`)
const splitText = text.split(toHighlight)
console.log(splitText)
return (
<>
{splitText[0]}
<span style={{ color: '#99f' }}>{toHighlight}</span>
{splitText[1]}
</>
)
} catch (e) {
return text
}

View file

@ -1,7 +0,0 @@
.groupDescription {
font-size: 16px;
padding: 4px;
}
.todoButtons {
}

View file

@ -1,59 +0,0 @@
import React from 'react'
import TodoCard from './todoCard'
import styles from './todoBoard.module.css'
export default function TodoBoard(props) {
const { columns, cards, userId, categories, onCardClick, selectedGroup } =
props
const clickableTypes = Object.keys(columns).reduce((acc, key) => {
const col = columns[key]
if (col.clickable) {
acc.push(key)
}
return acc
}, [])
return (
<div className={styles.tableContainer}>
<div className={styles.table}>
{Object.keys(columns).map((key) => {
const category = columns[key]
const cardsToShow = cards.filter((card) => {
const shouldHide =
card.state !== key ||
(selectedGroup !== null &&
selectedGroup !== 'uncat' &&
card.group !== selectedGroup) ||
(selectedGroup === 'uncat' && card.group !== undefined)
return !shouldHide
})
return (
<div className={styles.tableCol} key={key}>
<div className={styles.categoryName}>{category.name}</div>
{cardsToShow.length === 0 && (
<div className={styles.empty}>Üres</div>
)}
{cardsToShow.map((card, i) => {
return (
<TodoCard
key={i}
cardData={card}
userId={userId}
categories={categories}
onClick={onCardClick}
clickable={clickableTypes.includes(key)}
/>
)
})}
</div>
)
})}
</div>
</div>
)
}

View file

@ -1,28 +0,0 @@
.tableContainer {
overflow-y: hidden;
overflow-x: auto;
margin: 5px 0px;
}
.table {
display: flex;
}
.categoryName {
text-align: center;
margin: 5px 0px;
font-size: 16px;
font-weight: bold;
color: white;
white-space: nowrap;
}
.tableCol {
flex: 1;
}
.empty {
display: flex;
justify-content: center;
padding: 10px 0px;
}

View file

@ -1,44 +0,0 @@
import React from 'react'
import styles from './todoCard.module.css'
export default function TodoCard(props) {
const { categories, onClick, userId } = props
const { name, category, points, votes, id, group } = props.cardData
const voted = votes.includes(userId)
return (
<div
className={styles.card}
style={{
border: `1px solid ${group || '#f2cb05'}`,
}}
onClick={() => {
onClick(props.cardData)
}}
>
<div className={styles.description}>
<span className={styles.id}>{`#${id}`}</span>
{name}
</div>
<div className={styles.category}>
<span
style={{
backgroundColor: categories[category].color,
color: 'white',
borderRadius: '2px',
padding: '3px',
}}
>
{categories[category].name}
</span>
</div>
<div className={styles.numbers}>
<div
className={`${voted && styles.voted}`}
>{`Szavazatok: ${votes.length}`}</div>
<div>{`Nehézség: ${points}`}</div>
</div>
</div>
)
}

View file

@ -1,47 +0,0 @@
.card {
border-radius: 5px;
padding: 7px;
margin: 8px 4px;
cursor: pointer;
background-color: #171616;
}
.voted {
color: var(--text-color);
font-weight: 600;
}
.card:hover {
background-color: #333;
border: 2px solid #f99;
}
.card > div {
margin: 6px 4px;
}
.description {
word-break: normal;
font-size: 14px;
color: white;
}
.category {
font-size: 12px;
white-space: nowrap;
}
.numbers {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.numbers > div {
font-size: 12px;
}
.id {
margin: 1px 6px 1px 1px;
color: #999;
}

View file

@ -1,44 +0,0 @@
import React from 'react'
import styles from './todoRow.module.css'
export default function TodoRow(props) {
const { categories, userId, onClick } = props
const { name, category, votes, id, group } = props.rowData
const voted = votes.includes(userId)
const borderColor =
categories[category].borderColor || categories[category].color
return (
<div
onClick={() => {
onClick(props.rowData)
}}
className={styles.row}
style={{
border: `2px dashed ${borderColor || 'white'}`,
borderRadius: '3px',
}}
>
<div className={styles.id}>{`#${id}`}</div>
<div className={styles.description}>{name}</div>
<div className={styles.catName}>
<div
style={{
wordBreak: 'break-all',
fontSize: '12px',
backgroundColor: categories[category].color,
color: 'white',
borderRadius: '2px',
padding: '3px',
}}
>
{categories[category].name}
</div>
</div>
<div
className={`${styles.votes} ${voted && styles.voted}`}
>{`Szavazatok: ${votes.length}`}</div>
</div>
)
}

View file

@ -1,49 +0,0 @@
.row {
border-radius: 5px;
padding: 7px;
margin: 8px 4px;
cursor: pointer;
background-color: #171616;
}
.row:hover {
background-color: #333;
border: 2px solid #f99;
}
.row > div {
margin: 6px 4px;
}
.id {
flex: 0 20px;
margin: 1px 6px 1px 1px;
color: #999;
}
.description {
flex: 1;
word-break: normal;
font-size: 14px;
color: white;
}
.votes {
display: flex;
justify-content: space-between;
font-size: 12px;
}
.voted {
color: var(--text-color);
font-weight: 600;
}
.catName {
display: flex;
}
.category {
font-size: 10px;
white-space: nowrap;
}

View file

@ -1,111 +0,0 @@
import React from 'react'
import styles from './todoSidebar.module.css'
export default function Todos({
card,
userId,
categories,
columns,
voteOn,
namedGroups,
}) {
const {
name,
description,
category,
points,
votes,
id,
group,
gitlink,
} = card
const voteable = columns[card.state].clickable
// TODO: hide vote button if not voteable
return (
<div className={styles.container}>
<div className={styles.title}>
<span className={styles.id}>#{id}</span>
<div className={'subtitle'} style={{ textAlign: 'left' }}>
{name}
</div>
<span
style={{
backgroundColor: categories[category].color,
color: 'white',
fontSize: '12px',
borderRadius: '2px',
padding: '3px',
}}
>
{categories[category].name}
</span>
</div>
<hr />
{group && (
<div className={styles.row}>
<div style={{ color: group }}>Csoport:</div>
<div style={{ color: group }}>
{namedGroups[group] ? namedGroups[group].name : group}
</div>
</div>
)}
<div className={styles.row} title={'Minél több a pont, annál nehezebb'}>
<div>Nehézség:</div>
<div>{points}</div>
</div>
<div
className={`${styles.row} ${votes.includes(userId) &&
styles.votedtext}`}
title={'Hány felhasználó szavazott a feladatra'}
>
<div>Szavazatok:</div>
<div>{votes.length}</div>
</div>
{gitlink && (
<center>
<div>
<a
href={gitlink}
style={{ textDecoration: 'none' }}
target={'_blank'}
rel={'noreferrer'}
>
Git link
</a>
</div>
</center>
)}
<hr />
{description && (
<>
<div className={'subtitle'} style={{ textAlign: 'left' }}>
Leírás
</div>
<div
className={styles.description}
dangerouslySetInnerHTML={{ __html: description }}
/>
<hr />
</>
)}
<div className={styles.category} />
{voteable && (
<center>
<div className={'buttonContainer'} style={{ width: '40%' }}>
<div
className={`${styles.button} ${votes.includes(userId) &&
styles.voted}`}
onClick={() => {
voteOn(card)
}}
>
{votes.includes(userId) ? 'Szavazat visszavonása' : 'Szavazás'}
</div>
</div>
</center>
)}
</div>
)
}

View file

@ -1,66 +0,0 @@
.container {
display: flex;
flex-flow: column;
margin: 5px;
}
.container > hr {
width: 100%;
}
.title {
color: var(--text-color);
font-size: 18px;
}
.title {
color: var(--text-color);
font-size: 20px;
margin: 5px 0px;
}
.name {
}
.category {
word-break: break-all;
font-size: 10px;
}
.id {
font-size: 20px;
margin: 0px 3px;
color: #999;
}
.description {
overflow-x: auto;
overflow-y: auto;
max-height: calc(38vh);
margin: 5px;
}
.row {
display: flex;
justify-content: space-between;
margin: 2px 10px;
flex-wrap: wrap;
}
.button {
border: 2px solid var(--text-color);
border-radius: 3px;
text-align: center;
cursor: pointer;
}
.button:hover {
background-color: #333;
}
.votedtext {
color: #cf9;
}
.voted {
border: 2px solid #cf9;
}

View file

@ -1,57 +0,0 @@
import React from 'react'
import TodoRow from './todoRow'
import styles from './todoTable.module.css'
export default function TodoBoard(props) {
const { tables, cards, userId, categories, onClick, selectedGroup } = props
return (
<div className={styles.tableContainer}>
<div className={styles.table}>
{Object.keys(tables).map((key) => {
const table = tables[key]
const tableCards = cards.filter((card) => {
return card.state === key
})
const tableCardsToShow = tableCards.filter((card) => {
const shouldHide =
card.state !== key ||
(selectedGroup !== null &&
selectedGroup !== 'uncat' &&
card.group !== selectedGroup) ||
(selectedGroup === 'uncat' && card.group !== undefined)
return !shouldHide
})
return (
<div className={styles.container} key={key}>
<div className={styles.title}>{table.name}</div>
<div className={styles.scroll}>
{tableCardsToShow.length === 0 && (
<div className={styles.empty}>Üres</div>
)}
{tableCardsToShow.map((card, i) => {
return (
<TodoRow
onClick={onClick}
key={i}
type={key}
rowData={card}
userId={userId}
categories={categories}
/>
)
})}
</div>
</div>
)
})}
</div>
</div>
)
}

View file

@ -1,33 +0,0 @@
.tableContainer {
overflow-y: hidden;
overflow-x: auto;
margin: 5px 0px;
}
.table {
justify-content: center;
}
.container {
flex-direction: column;
margin-top: 30px;
margin-bottom: 50px;
}
.title {
text-align: center;
font-size: 20px;
font-weight: bold;
}
.scroll {
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
}
.empty {
display: flex;
justify-content: center;
padding: 10px 0px;
}

View file

@ -1,227 +0,0 @@
import React, { useState, useEffect } from 'react'
import LoadingIndicator from '../LoadingIndicator'
import TodoBoard from './todoBoard'
import TodoTable from './todoTable'
import TodoSidebar from './todoSidebar'
import Modal from '../modal'
import styles from './todo.module.css'
import constants from '../../constants.json'
const byVotes = (a, b) => {
return b.votes.length - a.votes.length
}
export default function Todos({ globalState, setGlobalState }) {
const [loaded, setLoaded] = useState(false)
const [columns, setColumns] = useState(null)
const [cards, setCards] = useState(null)
const [categories, setCategories] = useState(null)
const [userId, setUserId] = useState(null)
const [sidebarCard, setSidebarCard] = useState(null)
const [namedGroups, setGroups] = useState(null)
const [selectedGroup, setSelectedGroup] = useState(null)
const [activeGroups, setActiveGroups] = useState(null)
useEffect(() => {
if (globalState.todos) {
setTodos(globalState.todos)
} else {
fetch(`${constants.apiUrl}todos`, {
credentials: 'include',
})
.then((resp) => {
return resp.json()
})
.then((data) => {
setTodos(data)
setGlobalState({
todos: data,
})
})
}
}, [])
const setTodos = (data) => {
setCategories(data.todos.categories)
setGroups(data.todos.groups)
setUserId(data.userId)
setCards(data.todos.cards)
setColumns(data.todos.columns)
setLoaded(true)
const notTables = Object.keys(data.todos.columns).reduce((acc, key) => {
const col = data.todos.columns[key]
if (col.type !== 'table') {
acc.push(key)
}
return acc
}, [])
setActiveGroups(
data.todos.cards.reduce((acc, card) => {
if (card.group && notTables.includes(card.state)) {
if (!acc.includes(card.group)) acc.push(card.group)
}
return acc
}, [])
)
}
const onClick = (card) => {
setSidebarCard(card.id)
}
if (!loaded) {
return <LoadingIndicator />
}
let bCards = []
let tCards = []
cards.forEach((card) => {
if (columns[card.state].type === 'table') {
tCards.push(card)
} else {
bCards.push(card)
}
})
bCards = bCards.sort(byVotes)
tCards = tCards.sort(byVotes)
let cols = {}
let tables = {}
Object.keys(columns).forEach((key) => {
const col = columns[key]
if (col.type !== 'table') {
cols = {
...cols,
[key]: col,
}
} else {
tables = {
...tables,
[key]: col,
}
}
})
const groups = cards.reduce((acc, card) => {
if (!acc.includes(card.group) && card.group) {
acc.push(card.group)
}
return acc
}, [])
const renderGrouper = () => {
const sg = namedGroups[selectedGroup]
return (
<>
<div className={'buttonContainer'}>
{groups.map((group) => {
const namedGroup = namedGroups[group]
const shouldSkip = activeGroups && !activeGroups.includes(group)
if (shouldSkip) {
return null
}
return (
<div
className={`${'buttonContainer'}, ${styles.todoButtons} ${
group === selectedGroup && 'activeButton'
}`}
key={group}
onClick={() => {
setSelectedGroup(group)
}}
>
{namedGroup ? namedGroup.name : group}
</div>
)
})}
<div
className={`${'buttonContainer'}, ${styles.todoButtons} ${
'uncat' === selectedGroup && 'activeButton'
}`}
onClick={() => {
setSelectedGroup('uncat')
}}
>
{'Kategorizálatlan'}
</div>
<div
className={`${'buttonContainer'}, ${styles.todoButtons} ${
null === selectedGroup && 'activeButton'
}`}
onClick={() => {
setSelectedGroup(null)
}}
>
{'Összes'}
</div>
</div>
{sg && sg.description ? (
<div className={styles.groupDescription}>{sg.description}</div>
) : null}
</>
)
}
return (
<div>
{sidebarCard && (
<Modal
closeClick={() => {
setSidebarCard(null)
}}
>
<TodoSidebar
card={cards.find((card) => {
return card.id === sidebarCard
})}
userId={userId}
categories={categories}
columns={columns}
namedGroups={namedGroups}
voteOn={(card) => {
fetch(`${constants.apiUrl}voteTodo?id=${card.id}`, {
credentials: 'include',
})
.then((resp) => {
return resp.json()
})
.then((data) => {
setTodos(data)
setGlobalState({
todos: data,
})
})
}}
/>
</Modal>
)}
{renderGrouper()}
<div>
<TodoBoard
columns={cols}
cards={bCards}
userId={userId}
categories={categories}
onCardClick={onClick}
selectedGroup={selectedGroup}
/>
<TodoTable
tables={tables}
cards={tCards}
userId={userId}
categories={categories}
onClick={onClick}
selectedGroup={selectedGroup}
/>
</div>
</div>
)
}