Merge branch 'p2p-frontend' of gitlab.com:MrFry/qmining-page into p2p-frontend

This commit is contained in:
mrfry 2023-04-02 09:26:08 +02:00
commit b2a41bc160
48 changed files with 610 additions and 1249 deletions

16
constants.js Normal file
View file

@ -0,0 +1,16 @@
// eslint-disable-next-line no-undef
const useLocalhost = process && process.env.NODE_ENV === 'development'
const constants = {
siteUrl: !useLocalhost ? 'http://frylabs.net/' : 'http://localhost:8080/',
apiUrl: !useLocalhost
? 'http://frylabs.net/api/'
: 'http://localhost:8080/api/',
chatUrl: !useLocalhost ? 'http://frylabs.net/' : 'http://localhost:8080/',
mobileWindowWidth: 700,
maxQuestionsToRender: 250,
imageExts: ['gif', 'png', 'jpeg', 'jpg'],
videoExts: ['mp4', 'mkv', 'webm'],
}
export default constants

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

@ -2,7 +2,7 @@ import React from 'react'
import Questions from './Questions'
import constants from '../constants.json'
import constants from '../constants'
const countReducer = (acc, subj) => {
return acc + subj.Questions.length

View file

@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react'
import constants from '../constants.json'
import constants from '../constants'
const soundCount = 7
function GetRandom(min, max) {

View file

@ -3,7 +3,7 @@ import React, { useState } from 'react'
import Modal from './modal'
import styles from './composer.module.css'
import constants from '../constants.json'
import constants from '../constants'
export default function Composer({ onSubmit, allowFile, fileOnly }) {
const [editorShowing, setEditorShowing] = useState(false)

View file

@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react'
import Modal from './modal'
import constants from '../constants.json'
import constants from '../constants'
import styles from './dbSelector.module.css'

View file

@ -1,7 +1,7 @@
import React, { useState } from 'react'
import styles from './feedbackArea.module.css'
import constants from '../constants.json'
import constants from '../constants'
function FileUploader({ onChange }) {
return (

View file

@ -8,7 +8,7 @@ import Header from '../components/header'
import Modal from '../components/modal'
import styles from './forum.module.css'
import constants from '../constants.json'
import constants from '../constants'
const forumPostPerPage = 5

View file

@ -7,7 +7,7 @@ const Snowfall = dynamic(() => import('react-snowfall'), { ssr: false })
import LogoutIcon from './logoutIcon'
import Modal from './modal'
import constants from '../constants.json'
import constants from '../constants'
import BB from './b'
import styles from './layout.module.css'
@ -67,36 +67,35 @@ function TopBar({
unreads,
userId,
}) {
return <>
return (
<>
<MenuIcon setSidebarOpen={setSidebarOpen} sidebarOpen={sidebarOpen} />
<Link href="/">
<img
src={`${constants.siteUrl}img/frylabs-logo_small_transparent.png`}
alt="FryLabs"
/>
</Link>
<div className={styles.topBarLinks}>
{Object.keys(topBarLinks).map((key) => {
const item = topBarLinks[key]
return (
(<Link
<Link
key={key}
href={item.href}
onClick={closeSideBar}
className={href.includes(key) ? styles.active : undefined}>
className={href.includes(key) ? styles.active : undefined}
>
{item.text}
</Link>)
);
</Link>
)
})}
</div>
<div className={'seperator'} />
<UserStatus onClick={closeSideBar} unreads={unreads} userId={userId} />
</>;
</>
)
}
function SideBar({ sidebarOpen, closeSideBar, href, setDonateShowing }) {
@ -110,17 +109,16 @@ function SideBar({ sidebarOpen, closeSideBar, href, setDonateShowing }) {
}
return (
(<Link
<Link
href={item.href}
key={key}
onClick={closeSideBar}
className={href.includes(key) ? styles.active : undefined}
id={item.id || undefined}>
id={item.id || undefined}
>
{item.text}
</Link>)
);
</Link>
)
})}
<a
onClick={() => {
@ -132,7 +130,7 @@ function SideBar({ sidebarOpen, closeSideBar, href, setDonateShowing }) {
</a>
</div>
</>
) : null;
) : null
}
function Donate() {
@ -187,11 +185,10 @@ function UserStatus({ userId, unreads, onClick }) {
href="/chat"
onClick={onClick}
title={`Chat${unreadCount ? ' (' + unreadCount + ' új üzenet)' : ''}`}
className={styles.unreadNotification}>
className={styles.unreadNotification}
>
<span>💬</span>
{unreadCount ? <div>{unreadCount}</div> : null}
</Link>
<div
@ -215,7 +212,7 @@ function UserStatus({ userId, unreads, onClick }) {
<LogoutIcon size={28} />
</div>
</div>
);
)
}
function MenuIcon({ setSidebarOpen, sidebarOpen }) {

View file

@ -4,7 +4,7 @@ import ReactButton from './reactButton'
import Comments from './comments'
import Link from 'next/link'
import constants from '../constants.json'
import constants from '../constants'
import styles from './newsEntry.module.css'
@ -95,9 +95,9 @@ export default function NewsEntry({
<Link
href={`/chat?user=${user}`}
title={`Chat #${user}-el`}
className={'userId'}>
className={'userId'}
>
User #{user}
</Link>
<div className={styles.newsDate} title={dateObj.toLocaleString()}>
@
@ -145,5 +145,5 @@ export default function NewsEntry({
/>
</div>
</div>
);
)
}

View file

@ -1,5 +1,5 @@
import React from 'react'
import constants from '../constants.json'
import constants from '../constants'
export default function Sleep() {
const hours = new Date().getHours()

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>
)
}

15
src/constants.js Normal file
View file

@ -0,0 +1,15 @@
// eslint-disable-next-line no-undef
const useLocalhost = process && process.env.NODE_ENV === 'development'
const constants = {
siteUrl: !useLocalhost ? 'http://frylabs.net/' : 'http://localhost:8080/',
apiUrl: !useLocalhost
? 'http://frylabs.net/api/'
: 'http://localhost:8080/api/',
chatUrl: !useLocalhost ? 'http://frylabs.net/' : 'http://localhost:8080/',
mobileWindowWidth: 700,
maxQuestionsToRender: 250,
imageExts: ['gif', 'png', 'jpeg', 'jpg'],
videoExts: ['mp4', 'mkv', 'webm'],
}
export default constants

View file

@ -1,8 +0,0 @@
{
"siteUrl": "https://qmining.frylabs.net/",
"apiUrl": "https://api.frylabs.net/",
"mobileWindowWidth": 700,
"maxQuestionsToRender": 250,
"imageExts" : ["gif", "png", "jpeg", "jpg"],
"videoExts" : ["mp4", "mkv", "webm"]
}

View file

@ -1,11 +1,7 @@
{
"index": {
"href": "/",
"text": "Fórum"
},
"news": {
"href": "/news",
"text": "Hírek"
"text": "Főoldal"
},
"script": {
"href": "/script",
@ -26,5 +22,9 @@
"contact": {
"href": "/contact",
"text": "Kapcsolat"
},
"p2pinfo": {
"href": "/p2pinfo",
"text": "P2P infó"
}
}

View file

@ -1,12 +1,12 @@
{
"contribute": {
"href": "/contribute",
"text": "Teendők"
},
"pwRequest": {
"href": "/pwRequest",
"text": "Jelszó kérés"
},
"validation": {
"href": "/validation",
"text": "Validálás"
},
"ranklist": {
"href": "/ranklist",
"text": "Ranklista"

View file

@ -5,7 +5,7 @@ import Head from 'next/head'
import Layout from '../components/layout'
import '../defaultStyles.css'
import constants from '../constants.json'
import constants from '../constants'
export const queryClient = new QueryClient({
defaultOptions: {

View file

@ -10,7 +10,7 @@ import SearchBar from '../components/searchBar'
import styles from './allQuestions.module.css'
import constants from '../constants.json'
import constants from '../constants'
const countReducer = (acc, subj) => {
return acc + subj.Questions.length

View file

@ -2,7 +2,7 @@ import React from 'react'
import io from 'socket.io-client'
import linkifyString from 'linkify-string'
import constants from '../constants.json'
import constants from '../constants'
import LoadingIndicator from '../components/LoadingIndicator'
import { queryClient } from '../pages/_app'
import Header from '../components/header'
@ -202,7 +202,7 @@ export default class Chat extends React.Component {
return
}
// https://socket.io/docs/v4/handling-cors/#Configuration
const socket = io(`${constants.apiUrl}`, {
const socket = io(`${constants.chatUrl}`, {
withCredentials: true,
extraHeaders: {
'qmining-chat': 'qmining-chat',

View file

@ -3,7 +3,7 @@ import Link from 'next/link'
import Header from '../components/header'
import FeedbackArea from '../components/feedbackArea'
import constants from '../constants.json'
import constants from '../constants'
import LoadingIndicator from '../components/LoadingIndicator'
import styles from './contact.module.css'
@ -62,13 +62,10 @@ export default function Contact({ globalState, setGlobalState }) {
<div className={styles.container}>
{contacts ? (
<>
<div>
<div className={'subtitle'}>Alternatív módok</div>
<div className={styles.text}>
Az alábbi módokat is nyugodtan használhatod, a nevedet, e-mail
címedet, illetve semmilyen egyéb adatot nem adok ki harmadik fél
számára. (egyedül én fogom látni)
</div>
Figyelem! A szerver üzemeltetőjének kontaktját a
<Link href="/p2pinfo">{'"P2P infó"'}</Link>
részlegen találod!
</div>
<div className={styles.contactsContainer}>
{Object.keys(contacts).map((key) => {
@ -78,8 +75,7 @@ export default function Contact({ globalState, setGlobalState }) {
<div>{description}</div>
{href ? (
<a target="blank" rel="noreferrer" href={href}>
{' '}
{value}{' '}
{value}
</a>
) : (
<div>{value}</div>
@ -94,5 +90,5 @@ export default function Contact({ globalState, setGlobalState }) {
)}
</div>
</div>
);
)
}

View file

@ -1,91 +0,0 @@
import React, { useState } from 'react'
import Todos from '../components/todoStuff/todos'
import FeedbackArea from '../components/feedbackArea'
import Modal from '../components/modal'
import Header from '../components/header'
import constants from '../constants.json'
import styles from './contribute.module.css'
import repos from '../data/repos.json'
export default function Contribute({ globalState, setGlobalState }) {
const [showFeedback, setShowFeedback] = useState(false)
return (
<div>
<Header title={'Todos'} />
<div className={'pageHeader'}>
<h1>Teendők</h1>
</div>
<div className={'description'}>
<p>
Ezen az oldalon új ötleteket adhatsz hozzá megvalósításra a teendők
listájához.
</p>
<p>
Emellet ha hozzáértő vagy, adott feladatok megvalósításában is
segíthetsz. (lásd: <a href="#gitrepo">lentebb</a>)
<br /> Ha egy kártyára kattintasz, a megjelenő ablakban láthatod annak
részleteit, illetve{' '}
<b>
<i>szavazhatsz</i>
</b>{' '}
is a feladatra, annak érdekében, hogy minél hamarabb megvalósulhasson.
Minél több szavazat érkezik egy kártyára, annál magasabb lesz a
prioritása. (értsd: a legtöbb szavazatot kapó kártya teendője lesz
legelőször megvalósítva)
<br /> A kurzort az oszlopcímekre mozgatva, további információkat
olvashatsz a kategóriák tulajdonságairól.
</p>
</div>
<div className={`buttonContainer`}>
<div
onClick={() => {
setShowFeedback(true)
}}
>
Új feladat
</div>
</div>
<br />
<hr />
<Todos globalState={globalState} setGlobalState={setGlobalState} />
<hr />
<div id={'gitrepo'} className={styles.gitRepos}>
<div>
<div className={'subtitle'}>
<b>Git repos</b>
</div>
<div>
{Object.keys(repos.repos).map((key) => {
let repo = repos.repos[key]
return (
<div key={key}>
<ul>
<li>
<a key={key} href={repo.href}>
{repo.description}
</a>
</li>
</ul>
</div>
)
})}
</div>
</div>
<img src={`${constants.siteUrl}img/bug.png`} />
</div>
<hr />
{showFeedback && (
<Modal
closeClick={() => {
setShowFeedback(false)
}}
>
<FeedbackArea from={'contribute'} />
</Modal>
)}
</div>
)
}

View file

@ -1,32 +0,0 @@
.warning {
color: white;
padding: 10px;
font-size: 26px;
text-align: center;
}
.title {
color: var(--text-color);
font-size: 30px;
text-align: center;
}
.gitRepos {
display: flex;
justify-content: space-around;
}
@media screen and (max-width: 700px) {
.gitRepos {
flex-direction: column;
}
}
.gitRepos a {
margin: 0px 5px;
}
.gitRepos img {
width: 400px;
max-width: 90%;
}

View file

@ -3,7 +3,7 @@ import Link from 'next/link'
import Header from '../components/header'
import constants from '../constants.json'
import constants from '../constants'
import styles from './faq.module.css'
function PasswordSection() {
@ -90,11 +90,7 @@ function FAQSection() {
(így nicknevek sem kellenek), és még IP cím sincs sehol letárolva
szerver oldalon.
<br />
Több infó a{' '}
<Link href="/faq?tab=risk">
kockázatok résznél
</Link>
.
Több infó a <Link href="/faq?tab=risk">kockázatok résznél</Link>.
</li>
<li>
<b>Ki és miért csinálja ezt a weboldalt?</b>
@ -108,11 +104,12 @@ function FAQSection() {
</li>
</ul>
</div>
);
)
}
function RiskSection() {
return <>
return (
<>
Itt {'"én"'} alatt a szerver, weboldal és script egyedüli üzemeltetője
értendő, az egyetlen személy, aki hozzáfér akármilyen szolgáltatással
kapcsolatos adathoz, pl.: szerver statisztikák, szerver operációs
@ -181,11 +178,13 @@ function RiskSection() {
Pl.: ha a script rosszul menti le, vagy rossz kérdésre ad választ
</li>
</ul>
</>;
</>
)
}
function WebsiteSaveSection() {
return <>
return (
<>
<p>
Ha hibát találsz, kérlek jelents. Hogy a hibákat a saját gépemen
reprodukálni tudjam, és könnyen ki bírjam javítani, sokszor jól jön, ha
@ -216,14 +215,10 @@ function WebsiteSaveSection() {
nevedre, majd (csak a neved, ne a sort) töröld ki, és mentsd el. Nem
szoktam megnézni a neveket, nem is tudok velük mit kezdeni, és nem is
adom ki soha. Amit beküldesz, azt csak én látom.{' '}
<Link href="/faq?tab=risk">
Több infó itt
</Link>{' '}
<Link href="/faq?tab=risk">Több infó itt</Link>{' '}
</li>
<li>
<Link href="/contact">
Ide tudod feltölteni
</Link>{' '}
<Link href="/contact">Ide tudod feltölteni</Link>{' '}
</li>
</ul>
<p>
@ -231,7 +226,8 @@ function WebsiteSaveSection() {
hatékonyan tesztelni a scriptet. Ezért hatalmas segítség ha feltöltöd
azt az oldalt amin hibával találkozol.
</p>
</>;
</>
)
}
function ScriptSection() {
@ -240,9 +236,7 @@ function ScriptSection() {
<ul>
<li>
<b>Hogy kell a scriptet telepíteni, és milyen oldalakon működik? </b>
<Link href="/script">
Ezen az oldalon van leírva
</Link>{' '}
<Link href="/script">Ezen az oldalon van leírva</Link>{' '}
</li>
<li>
<b>Hogyan működik a script?</b>
@ -258,10 +252,7 @@ function ScriptSection() {
</li>
<li>
Más felhasználóknak köszönhetően már valószínűleg lesznek
megoldások a tesztjeidhez.{' '}
<Link href="/allQuestions">
Itt
</Link>{' '}
megoldások a tesztjeidhez. <Link href="/allQuestions">Itt</Link>{' '}
meg tudod tekinteni, hogy vannak-e válaszok a tárgyadhoz.
</li>
<li>
@ -378,11 +369,12 @@ function ScriptSection() {
</li>
</ul>
</div>
);
)
}
function AddSite() {
return <>
return (
<>
Jelenleg az, hogy a script melyik oldalakon fut bele van égetve a script
forráskódjába. Ez itt látható:{' '}
<a
@ -403,9 +395,7 @@ function AddSite() {
Egyetem moodle oldalának hozzáadása a scripthez kézzel:
<ul>
<li>
<Link href="/script">
Először telepítsd a scriptet rendesen
</Link>{' '}
<Link href="/script">Először telepítsd a scriptet rendesen</Link>{' '}
</li>
<li>
Böngészőben nyisd meg a Tampermonkey bővítményt <br />
@ -442,13 +432,11 @@ function AddSite() {
Ha a tesztek közben mégsem megy, akkor ellenőrizd, hogy a beírt moodle
cím egyezik-e a teszt közbenivel. Ha kell egy kis segítség, kérdésed
van, nem működik valami, vagy éppen működik, de alapból nincs
hozzáadva:{' '}
<Link href="/contact">
írj a kapcsolat oldalon!
</Link>
hozzáadva: <Link href="/contact">írj a kapcsolat oldalon!</Link>
</li>
</ul>
</>;
</>
)
}
const pages = {

View file

@ -25,12 +25,11 @@ export default function UserForum({
</div>
)}
<Forum
allowPost
router={router}
globalState={globalState}
setGlobalState={setGlobalState}
globalData={globalData}
forumName={'userForum'}
forumName={'frontpage'}
/>
</>
)

View file

@ -9,7 +9,7 @@ import Composer from '../components/composer'
import Modal from '../components/modal'
import styles from './memes.module.css'
import constants from '../constants.json'
import constants from '../constants'
const forumPostPerPage = 5
const frontpageForumName = 'memes'

View file

@ -1,20 +0,0 @@
import React from 'react'
import Forum from '../components/forum'
export default function Index({
router,
globalData,
globalState,
setGlobalState,
}) {
return (
<Forum
router={router}
globalState={globalState}
setGlobalState={setGlobalState}
globalData={globalData}
forumName={'frontpage'}
/>
)
}

View file

@ -1,21 +0,0 @@
.title {
color: var(--text-color);
font-size: 32px;
text-align: center;
letter-spacing: 2.5px;
}
.motd {
text-align: center;
font-size: 20px;
border: 2px dashed var(--text-color);
padding-top: 13px;
padding-bottom: 15px;
padding-left: 5px;
padding-right: 5px;
margin-top: 18px;
margin-bottom: 30px;
margin-left: 5px;
margin-right: 5px;
}

184
src/pages/p2pinfo.jsx Normal file
View file

@ -0,0 +1,184 @@
import React, { useState, useEffect } from 'react'
import Link from 'next/link'
import Header from '../components/header'
import constants from '../constants'
import LoadingIndicator from '../components/LoadingIndicator'
import styles from './p2pinfo.module.css'
const infos = [
{
title: 'Név',
key: 'name',
},
{
title: 'Kontakt',
key: 'contact',
},
{
title: 'Utolsó szinkronizálás',
key: 'lastSync',
type: 'date',
},
{
title: 'Kérdés DB-k',
key: 'questionDbCount',
type: 'number',
},
{
title: 'Tárgyak',
key: 'subjectCount',
type: 'number',
},
{
title: 'Kérdések',
key: 'questionCount',
type: 'number',
},
{
title: 'Script version',
key: 'scriptVersion',
},
{
title: 'Szerver build time',
key: 'serverBuildTime',
type: 'date',
},
{
title: 'Weboldal build time',
key: 'qminingPageBuildTime',
type: 'date',
},
{
title: 'Data editor build time',
key: 'dataEditorBuildTime',
type: 'date',
},
{
title: 'Szerver revision',
key: 'serverRevision',
},
{
title: 'Script revision',
key: 'scriptRevision',
},
{
title: 'Weboldal revision',
key: 'qminingPageRevision',
},
{
title: 'Data editor revision',
key: 'dataEditorRevision',
},
]
export default function P2PInfo({ globalState, setGlobalState }) {
const [p2pInfo, setP2pinfo] = useState()
const info = p2pInfo
? {
...p2pInfo,
...p2pInfo.selfInfo,
...p2pInfo.qdbInfo,
}
: {}
useEffect(() => {
if (globalState.p2pinfo) {
setP2pinfo(globalState.p2pinfo)
} else {
fetch(constants.apiUrl + 'p2pinfo', {
method: 'GET',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
.then((res) => {
return res.json()
})
.then((res) => {
setP2pinfo(res)
setGlobalState({
p2pinfo: res,
})
})
}
}, [])
if (!p2pInfo) return <LoadingIndicator />
return (
<>
<div className={'pageHeader'}>
<h1>Peer to peer infó</h1>
</div>
<div className={styles.container}>
<Header title={'P2P infó'} />
<div style={{ textAlign: 'center' }}>
A weboldal peer to peer (p2p) megoldást implementál, folyamatosan
megosztja az új kérdéseket a lent megadott regisztrált szerverekkel,
és hozzáadja a kapott új kérdéseket a helyi adatbázishoz.
<p />
Ha az egyik szerver kiesne, a script a megadott szerverek közül
választ egy újat. A felhasználók is szinkronizálva vannak, így a
jelenlegi jelszavaddal bármelyik másik oldalra be tudsz lépni
<p />A szervert akár te is hostolhatod, érdeklődj a lentebb megadott
kontakton
</div>
<hr />
<div className={styles.title}>Szerver P2P információja:</div>
<br />
{infos.map((x) => {
const { title, key, type } = x
let text = info[key]
switch (type) {
case 'date':
text = new Date(text).toLocaleString()
break
case 'number':
text = text.toLocaleString()
break
}
return (
<div key={key} className={styles.infoRow}>
<div>{title}</div>
<div>{text}</div>
</div>
)
})}
<hr />
<div className={styles.title}>Regisztrált peer-ek:</div>
<div className={styles.peerHeader}>
<div>Név</div>
<div>Host</div>
<div>Utolsó szinkronizálás</div>
</div>
{p2pInfo.myPeers.map((peer, i) => {
return (
<div key={i} className={styles.peerContainer}>
<div>{peer.name}</div>
<div>
<a
href={`https://${peer.host}:${peer.port}`}
target={'_blank'}
rel="noreferrer"
>
{peer.host}:{peer.port}
</a>
</div>
<div>{new Date(peer.lastSync).toLocaleString()}</div>
</div>
)
})}
{p2pInfo.myPeers.length === 0 && (
<div className={styles.peerContainer}>
Ennél a szervernél jelenleg nincs peer regisztrálva
</div>
)}
</div>
</>
)
}

View file

@ -0,0 +1,44 @@
.container {
padding: 10px;
}
.infoRow {
display: flex;
}
.infoRow > * {
padding: 0px 8px;
}
.infoRow :first-child {
flex: 1;
display: flex;
justify-content: flex-end;
}
.infoRow :last-child {
flex: 2;
color: var(--text-color);
}
.peerContainer, .peerHeader {
display: flex;
justify-content: space-around;
align-content: center;
padding: 5px 10px;
}
.peerContainer {
color: var(--text-color);
}
.peerContainer > *, .peerHeader > * {
flex: 1;
}
.title {
color: var(--text-color);
font-size: 20px;
text-align: center;
}

View file

@ -4,7 +4,7 @@ import Link from 'next/link'
import Header from '../components/header'
import styles from './pwRequest.module.css'
import constants from '../constants.json'
import constants from '../constants'
function fetchAvailablePWS() {
return new Promise((resolve) => {
@ -107,10 +107,7 @@ export default function PwRequest({ globalData, globalState, setGlobalState }) {
Minden felhasználó egyedi jelszót kap. Ne használjatok többen egy
jelszót, mert egy idő után -biztonsági okokból- kidob a rendszer,
ha ugyan az a felhasználó több helyen is belépve marad. A
jelszavakról bővebben a{' '}
<Link href="/faq?tab=pw">
GYIK
</Link>{' '}
jelszavakról bővebben a <Link href="/faq?tab=pw">GYIK</Link>{' '}
vonatkozó részében olvashatsz.
</p>
</div>
@ -191,5 +188,5 @@ export default function PwRequest({ globalData, globalState, setGlobalState }) {
</div>
</center>
</div>
);
)
}

View file

@ -4,7 +4,7 @@ import Header from '../components/header'
import LoadingIndicator from '../components/LoadingIndicator'
import styles from './ranklist.module.css'
import constants from '../constants.json'
import constants from '../constants'
const selectOptions = {
newQuestions: { name: 'Beküldött új kérdések' },

View file

@ -4,7 +4,7 @@ import { useQuery } from 'react-query'
import Header from '../components/header'
import Link from 'next/link'
import constants from '../constants.json'
import constants from '../constants'
import ExternalLinkIcon from '../components/externalLinkIcon'
import styles from './script.module.css'
@ -102,13 +102,9 @@ export default function Script() {
<li>
Ha be vagy jelentkezve, akkor a teszt oldalakon a megoldásokat
kellene látnod egy felugró ablakban felül. Ha nem, akkor{' '}
<Link href="/faq">
GYIK
</Link>
<Link href="/faq">GYIK</Link>
-be olvass bele, vagy{' '}
<Link href="/contact">
írj üzenetet mi nem működik
</Link>
<Link href="/contact">írj üzenetet mi nem működik</Link>
</li>
</ol>
<b>Példa felugró ablakokra:</b>
@ -161,5 +157,5 @@ export default function Script() {
</ul>
</div>
</div>
);
)
}

View file

@ -1,6 +1,6 @@
import React from 'react'
import styles from './thanks.module.css'
import constants from '../constants.json'
import constants from '../constants'
import Head from 'next/head'
export default function Thanks() {

View file

@ -8,7 +8,7 @@ import UpDownVote from '../components/upDownVote'
import Header from '../components/header'
import styles from './userfiles.module.css'
import constants from '../constants.json'
import constants from '../constants'
function vote(to, item) {
return new Promise((resolve, reject) => {
@ -382,7 +382,11 @@ export default function UserFiles({
<div className={`${styles.tableContainer} ${styles.rows}`}>
{currDir && (
<div
style={{ height: 40, justifyContent: "center", display: "flex" }}
style={{
height: 40,
justifyContent: 'center',
display: 'flex',
}}
onClick={() => {
goBack()
}}
@ -482,13 +486,14 @@ export default function UserFiles({
onClick={(e) => {
e.stopPropagation()
}}
className={'userId'}>
className={'userId'}
>
{`#${user}`}
</Link>
))}
</div>
</div>
);
)
})}
</>
) : (
@ -513,7 +518,7 @@ export default function UserFiles({
<LoadingIndicator />
)}
</div>
);
)
}
return (
@ -535,10 +540,8 @@ export default function UserFiles({
vállalás nincs.
</b>{' '}
Ha valami nem idevalót látsz, azt a{' '}
<Link href="/contact">
Kapcsolat
</Link>{' '}
oldalon jelezd kérlek. Tudatos károkozásért ban jár.
<Link href="/contact">Kapcsolat</Link> oldalon jelezd kérlek. Tudatos
károkozásért ban jár.
</div>
)}
<hr />
@ -602,5 +605,5 @@ export default function UserFiles({
</Modal>
)}
</div>
);
)
}

46
src/pages/validation.jsx Normal file
View file

@ -0,0 +1,46 @@
import React, { useState, useEffect } from 'react'
import Header from '../components/header'
import styles from './validation.module.css'
import constants from '../constants'
function fetchValidation() {
return new Promise((resolve) => {
fetch(`${constants.apiUrl}validationtoken`, {
credentials: 'include',
})
.then((resp) => {
return resp.json()
})
.then((res) => {
resolve(res)
})
})
}
export default function Validation() {
const [token, setToken] = React.useState('')
useEffect(() => {
fetchValidation().then((res) => {
setToken(res.key)
})
}, [])
return (
<div>
<Header title={'Validálás'} />
<div className={'pageHeader'}>
<h1>Validálás</h1>
</div>
<center>
<div className={styles.descrip}>
Különböző okokból kellhet, hogy bizonyítsd hogy az oldalhoz van
hozzáférésed. Ebben az esetben ezzel a tokennel tudod:
</div>
{token && <div className={styles.tokenContainer}>{token}</div>}
</center>
</div>
)
}

View file

@ -0,0 +1,16 @@
.descrip {
color: #acabab;
font-weight: bold;
padding-top: 4%;
padding-bottom: 4%;
font-size: 20px;
}
.tokenContainer {
font-family: 'Courier New', Courier, monospace;
text-align: center;
font-size: 24px;
color: white;
padding: 20px;
font-weight: bold;
}