mirror of
https://gitlab.com/MrFry/qmining-page
synced 2025-04-01 20:23:44 +02:00
Merge branch 'new_style'
This commit is contained in:
commit
21fdbb57a3
72 changed files with 3153 additions and 6934 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
.next/
|
.next/
|
||||||
out/
|
out/
|
||||||
|
/.vs
|
||||||
public/
|
public/
|
||||||
|
|
5296
package-lock.json
generated
5296
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -13,6 +13,7 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint-plugin-react": "^7.21.5",
|
"eslint-plugin-react": "^7.21.5",
|
||||||
"next": "^10.0.3",
|
"next": "^10.0.3",
|
||||||
"react": "^16.13.0",
|
"react": "^16.13.0",
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
|
import React from 'react'
|
||||||
import styles from './button.module.css'
|
import styles from './button.module.css'
|
||||||
|
|
||||||
export default function Button (props) {
|
export default function Button(props) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<center>
|
<center>
|
||||||
<a href={props.href}>
|
<a href={props.href}>
|
||||||
<div className={styles.ircLink}>
|
<div className={styles.button}>{props.text}</div>
|
||||||
{props.text}
|
|
||||||
</div>
|
|
||||||
</a>
|
</a>
|
||||||
</center>
|
</center>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.load {
|
.load {
|
||||||
border: 4px solid #f3f3f3;
|
border: 4px solid #f3f3f3;
|
||||||
border-top: 4px solid #99f;
|
border-top: 4px solid var(--text-color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
|
|
@ -1,33 +1,41 @@
|
||||||
import React, { PureComponent } from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
class Question extends PureComponent {
|
function highlightText(text, toHighlight) {
|
||||||
render () {
|
const re = new RegExp(toHighlight, 'gi')
|
||||||
const { question } = this.props
|
return text.replace(re, `<mark>${toHighlight}</mark>`)
|
||||||
|
|
||||||
let qdata = question.data
|
|
||||||
if (typeof qdata === 'object' && qdata.type === 'simple') {
|
|
||||||
qdata = ''
|
|
||||||
}
|
|
||||||
if (qdata) {
|
|
||||||
try {
|
|
||||||
qdata = JSON.stringify(qdata)
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='questionContainer'>
|
|
||||||
<div className='question'>
|
|
||||||
{question.Q}
|
|
||||||
</div>
|
|
||||||
<div className='answer'>
|
|
||||||
{question.A}
|
|
||||||
</div>
|
|
||||||
<div className='data'>
|
|
||||||
{qdata || null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Question
|
export default function Question({ question, searchTerm }) {
|
||||||
|
let qdata = question.data
|
||||||
|
if (typeof qdata === 'object' && qdata.type === 'simple') {
|
||||||
|
qdata = ''
|
||||||
|
}
|
||||||
|
if (qdata) {
|
||||||
|
try {
|
||||||
|
qdata = JSON.stringify(qdata)
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const questionText = searchTerm
|
||||||
|
? highlightText(question.Q, searchTerm)
|
||||||
|
: question.Q
|
||||||
|
const answerText = searchTerm
|
||||||
|
? highlightText(question.A, searchTerm)
|
||||||
|
: question.A
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="questionContainer">
|
||||||
|
<div
|
||||||
|
className="question"
|
||||||
|
dangerouslySetInnerHTML={{ __html: questionText }}
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
className="answer"
|
||||||
|
dangerouslySetInnerHTML={{ __html: answerText }}
|
||||||
|
></div>
|
||||||
|
<div className="data">{qdata || null}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { PureComponent } from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import Questions from './Questions.js'
|
import Questions from './Questions.js'
|
||||||
|
|
||||||
|
@ -8,77 +8,74 @@ const countReducer = (acc, subj) => {
|
||||||
return acc + subj.Questions.length
|
return acc + subj.Questions.length
|
||||||
}
|
}
|
||||||
|
|
||||||
class QuestionSearchResult extends PureComponent {
|
export default function QuestionSearchResult({ data, searchTerm }) {
|
||||||
render() {
|
let subjs = []
|
||||||
const { data, searchTerm } = this.props
|
let results = -1
|
||||||
|
|
||||||
let subjs = []
|
if (searchTerm) {
|
||||||
let results = -1
|
subjs = data.reduce((acc, subj) => {
|
||||||
|
const resultQuestions = subj.Questions.reduce((qacc, question) => {
|
||||||
if (searchTerm) {
|
const keys = ['Q', 'A', 'data']
|
||||||
subjs = data.reduce((acc, subj) => {
|
keys.some((key) => {
|
||||||
const resultQuestions = subj.Questions.reduce((qacc, question) => {
|
if (typeof question[key] !== 'string') {
|
||||||
const keys = ['Q', 'A', 'data']
|
return false
|
||||||
keys.some((key) => {
|
}
|
||||||
if (typeof question[key] !== 'string') {
|
if (
|
||||||
return false
|
question[key] &&
|
||||||
}
|
question[key].toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
if (
|
) {
|
||||||
question[key] &&
|
qacc.push(question)
|
||||||
question[key].toLowerCase().includes(searchTerm.toLowerCase())
|
return true
|
||||||
) {
|
}
|
||||||
qacc.push(question)
|
})
|
||||||
return true
|
return qacc
|
||||||
}
|
|
||||||
})
|
|
||||||
return qacc
|
|
||||||
}, [])
|
|
||||||
if (resultQuestions.length > 0) {
|
|
||||||
acc.push({
|
|
||||||
Name: subj.Name,
|
|
||||||
Questions: resultQuestions,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}, [])
|
}, [])
|
||||||
results = subjs.reduce(countReducer, 0)
|
if (resultQuestions.length > 0) {
|
||||||
} else {
|
acc.push({
|
||||||
results = data.reduce(countReducer, 0)
|
Name: subj.Name,
|
||||||
}
|
Questions: resultQuestions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
results = subjs.reduce(countReducer, 0)
|
||||||
|
} else {
|
||||||
|
results = data.reduce(countReducer, 0)
|
||||||
|
}
|
||||||
|
|
||||||
const renderCount = () => {
|
const renderCount = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="resultContainer">
|
||||||
|
{results === 0 && (
|
||||||
<div>
|
<div>
|
||||||
{searchTerm ? '' : 'Kezdj el írni kereséshez! '}
|
{`${results} találat. Az általad keresett kifejezés nem található,
|
||||||
{searchTerm
|
próbáld bővíteni a keresési feltételt!`}
|
||||||
? `${results} találat, ${subjs.length} tárgyból`
|
|
||||||
: `Keresés ${results} kérdés és ${data.length} tárgyból`}
|
|
||||||
</div>
|
</div>
|
||||||
{results === 0 && (
|
)}
|
||||||
<div>
|
{results > 0 && <div>{`${results} találat.`}</div>}
|
||||||
{
|
</div>
|
||||||
'Pontos egyezést keres a kereső, próbálj kisebb részletre keresni'
|
)
|
||||||
}
|
}
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results > constants.maxQuestionsToRender) {
|
if (results > constants.maxQuestionsToRender) {
|
||||||
return renderCount()
|
return (
|
||||||
} else {
|
<div>
|
||||||
return (
|
{searchTerm ? (
|
||||||
<div>
|
<div className="resultContainer">
|
||||||
<div>{renderCount()}</div>
|
Szűkítsd a keresési feltételeket a találatok megjelenítéséhez!
|
||||||
<div>
|
|
||||||
<Questions subjs={subjs} />
|
|
||||||
</div>
|
</div>
|
||||||
|
) : null}
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>{renderCount()}</div>
|
||||||
|
<div>
|
||||||
|
<Questions subjs={subjs} searchTerm={searchTerm} />
|
||||||
</div>
|
</div>
|
||||||
)
|
</div>
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default QuestionSearchResult
|
|
||||||
|
|
|
@ -5,22 +5,22 @@ import Question from './Question.js'
|
||||||
import styles from './Questions.module.css'
|
import styles from './Questions.module.css'
|
||||||
|
|
||||||
class Questions extends PureComponent {
|
class Questions extends PureComponent {
|
||||||
render () {
|
render() {
|
||||||
const { subjs } = this.props
|
const { subjs, searchTerm } = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<hr />
|
||||||
{subjs.map((subj, i) => {
|
{subjs.map((subj, i) => {
|
||||||
return (
|
return (
|
||||||
<div key={i}>
|
<div className={styles.questionBg} key={i}>
|
||||||
<div className={styles.subjName}>
|
<div className={styles.subjName}>{subj.Name}</div>
|
||||||
{subj.Name}
|
{subj.Questions.map((question, i) => {
|
||||||
</div>
|
|
||||||
{ subj.Questions.map((question, i) => {
|
|
||||||
return (
|
return (
|
||||||
<Question
|
<Question
|
||||||
key={i}
|
key={i}
|
||||||
question={question}
|
question={question}
|
||||||
|
searchTerm={searchTerm}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -1,7 +1,14 @@
|
||||||
.subjName {
|
.subjName {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
background-color: #9999ff;
|
font-weight: 600;
|
||||||
|
background-color: var(--text-color);
|
||||||
color: black;
|
color: black;
|
||||||
|
margin-top: 25px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.questionBg {
|
||||||
|
background-color: #1b1b1c;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
|
@ -3,26 +3,19 @@ import React, { PureComponent } from 'react'
|
||||||
import Question from './Question.js'
|
import Question from './Question.js'
|
||||||
|
|
||||||
class Subject extends PureComponent {
|
class Subject extends PureComponent {
|
||||||
render () {
|
render() {
|
||||||
const { subj } = this.props
|
const { subj } = this.props
|
||||||
|
|
||||||
if (subj) {
|
if (subj) {
|
||||||
return (
|
return (
|
||||||
<div >
|
<div>
|
||||||
{subj.Questions.map((question, i) => {
|
{subj.Questions.map((question, i) => {
|
||||||
return (
|
return <Question key={i} question={question} />
|
||||||
<Question
|
|
||||||
key={i}
|
|
||||||
question={question}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return (
|
return <div />
|
||||||
<div />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import styles from './SubjectSelector.module.css'
|
import styles from './SubjectSelector.module.css'
|
||||||
|
|
||||||
export default function SubjectSelector (props) {
|
export default function SubjectSelector(props) {
|
||||||
const { activeSubjName, searchTerm, data, onSubjSelect } = props
|
const { activeSubjName, searchTerm, data, onSubjSelect } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='subjectSelector'>
|
<div className="subjectSelector">
|
||||||
{data.map((subj, i) => {
|
{data.map((subj, i) => {
|
||||||
if (!subj.Name.toLowerCase().includes(searchTerm.toLowerCase())) {
|
if (!subj.Name.toLowerCase().includes(searchTerm.toLowerCase())) {
|
||||||
return null
|
return null
|
||||||
|
@ -12,16 +12,15 @@ export default function SubjectSelector (props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={activeSubjName === subj.Name
|
className={
|
||||||
? 'subjItem activeSubjItem'
|
activeSubjName === subj.Name
|
||||||
: 'subjItem'
|
? 'subjItem activeSubjItem'
|
||||||
|
: 'subjItem'
|
||||||
}
|
}
|
||||||
key={i}
|
key={i}
|
||||||
onClick={() => onSubjSelect(subj.Name)}
|
onClick={() => onSubjSelect(subj.Name)}
|
||||||
>
|
>
|
||||||
<span className={styles.subjName}>
|
<span className={styles.subjName}>{subj.Name}</span>
|
||||||
{subj.Name}
|
|
||||||
</span>
|
|
||||||
<span className={styles.questionCount}>
|
<span className={styles.questionCount}>
|
||||||
[ {subj.Questions.length} ]
|
[ {subj.Questions.length} ]
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.ircLink {
|
.button {
|
||||||
background-color: #9999ff;
|
background-color: #9999ff;
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
@ -22,14 +22,14 @@ function CommentInput({ onSubmit, onCancel }) {
|
||||||
onSubmit(val)
|
onSubmit(val)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Submit
|
Küldés
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onCancel()
|
onCancel()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Cancel
|
Bezárás
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -45,8 +45,8 @@ function Comment({ comment, index, onComment, onDelete, onReact, uid }) {
|
||||||
const commentStyle = admin
|
const commentStyle = admin
|
||||||
? styles.adminComment
|
? styles.adminComment
|
||||||
: own
|
: own
|
||||||
? styles.ownComment
|
? styles.ownComment
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.comment}>
|
<div className={styles.comment}>
|
||||||
|
@ -63,25 +63,27 @@ function Comment({ comment, index, onComment, onDelete, onReact, uid }) {
|
||||||
</div>
|
</div>
|
||||||
<div>User #{user}</div>
|
<div>User #{user}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>{date}</div>
|
<div className={styles.commentDate}>{date}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={`${!displayed && styles.hidden}`}>
|
<div className={`${!displayed && styles.hidden}`}>
|
||||||
<div className={styles.commentText}> {content}</div>
|
<div className={styles.commentText}> {content}</div>
|
||||||
<div className={'actions'}>
|
<div className={'actions'}>
|
||||||
<span
|
<span
|
||||||
|
className={styles.reply_bttn}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCommenting(true)
|
setCommenting(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Reply...
|
Válasz...
|
||||||
</span>
|
</span>
|
||||||
{own && (
|
{own && (
|
||||||
<span
|
<span
|
||||||
|
className={styles.delete_bttn}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onDelete([index])
|
onDelete([index])
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
Törlés
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<ReactButton
|
<ReactButton
|
||||||
|
@ -179,11 +181,12 @@ export default function Comments({
|
||||||
{commentCount !== 0 ? (
|
{commentCount !== 0 ? (
|
||||||
<div className={'actions'}>
|
<div className={'actions'}>
|
||||||
<span
|
<span
|
||||||
|
className={styles.comment_bttn}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setAddingNewComment(true)
|
setAddingNewComment(true)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
New comment
|
Új komment
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -191,7 +194,7 @@ export default function Comments({
|
||||||
<CommentInput
|
<CommentInput
|
||||||
onSubmit={(e) => {
|
onSubmit={(e) => {
|
||||||
if (!e) {
|
if (!e) {
|
||||||
alert('Írj be valamit, hogy kommentelhess...')
|
alert('Írj be valamit a szövegdobozba, hogy kommentelhess!')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setAddingNewComment(false)
|
setAddingNewComment(false)
|
||||||
|
@ -207,18 +210,19 @@ export default function Comments({
|
||||||
) : null}
|
) : null}
|
||||||
</Modal>
|
</Modal>
|
||||||
) : null}
|
) : null}
|
||||||
<div className={'actions'}>
|
<div
|
||||||
<span
|
className={'actions'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCommentsShowing(true)
|
setCommentsShowing(true)
|
||||||
if (commentCount === 0) {
|
if (commentCount === 0) {
|
||||||
setAddingNewComment(true)
|
setAddingNewComment(true)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<span className={styles.comment_bttn}>
|
||||||
{commentCount === 0
|
{commentCount === 0
|
||||||
? 'New comment'
|
? 'Új komment'
|
||||||
: `Show ${commentCount} comment${commentCount > 1 ? 's' : ''}`}
|
: `Kommentek mutatása (${commentCount})`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,21 +1,58 @@
|
||||||
|
.comment_bttn {
|
||||||
|
font-size: 15px;
|
||||||
|
padding-top: 4px !important;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment_bttn:hover {
|
||||||
|
transition: width 0.5s, height 0.5s, ease-out 0.5s;
|
||||||
|
color: gainsboro;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply_bttn {
|
||||||
|
font-size: 15px;
|
||||||
|
margin-top: 14.5px !important;
|
||||||
|
margin-left: 6px !important;
|
||||||
|
padding-top: 4px !important;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply_bttn:hover {
|
||||||
|
transition: width 0.5s, height 0.5s, ease-out 0.5s;
|
||||||
|
color: gainsboro;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete_bttn {
|
||||||
|
font-size: 15px;
|
||||||
|
margin-top: 14.5px !important;
|
||||||
|
margin-left: 6px !important;
|
||||||
|
padding-top: 4px !important;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete_bttn:hover {
|
||||||
|
transition: width 0.5s, height 0.5s, ease-out 0.5s;
|
||||||
|
color: gainsboro;
|
||||||
|
}
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
margin-left: 25px;
|
margin-left: 10px;
|
||||||
|
margin-right: 30px;
|
||||||
padding: 8px 0px;
|
padding: 8px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.commentData {
|
.commentData {
|
||||||
padding: 5px 2px;
|
padding: 5px 2px;
|
||||||
border-left: 2px solid var(--text-color);
|
border-left: 3px solid azure;
|
||||||
border-radius: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.commentData:hover {
|
.commentDate {
|
||||||
background-color: var(--hoover-color);
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.commentHeader {
|
.commentHeader {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
/*justify-content: space-between;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.commentHeader > div {
|
.commentHeader > div {
|
||||||
|
@ -38,7 +75,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.ownComment {
|
.ownComment {
|
||||||
border-left: 2px solid green;
|
border-left: 5px dotted azure;
|
||||||
}
|
}
|
||||||
|
|
||||||
.showHide {
|
.showHide {
|
||||||
|
@ -58,5 +95,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.adminComment {
|
.adminComment {
|
||||||
border-left: 2px solid yellow;
|
border-left: 3px solid gold;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,37 +4,23 @@ import Modal from './modal'
|
||||||
|
|
||||||
import styles from './composer.module.css'
|
import styles from './composer.module.css'
|
||||||
|
|
||||||
function FileUploader({ onChange }) {
|
|
||||||
return (
|
|
||||||
<div className={styles.inputArea}>
|
|
||||||
<div className={styles.textTitle}>Fájl csatolása</div>
|
|
||||||
<input
|
|
||||||
className={styles.fileInput}
|
|
||||||
type="file"
|
|
||||||
name="file"
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Composer({ onSubmit }) {
|
export default function Composer({ onSubmit }) {
|
||||||
const [editorShowing, setEditorShowing] = useState(false)
|
const [editorShowing, setEditorShowing] = useState(false)
|
||||||
const [val, setVal] = useState('')
|
const [val, setVal] = useState('')
|
||||||
const [type, setType] = useState('public')
|
|
||||||
const [title, setTitle] = useState('')
|
const [title, setTitle] = useState('')
|
||||||
const [file, setFile] = useState()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<center>
|
||||||
onClick={() => {
|
<div
|
||||||
setEditorShowing(true)
|
onClick={() => {
|
||||||
}}
|
setEditorShowing(true)
|
||||||
className={styles.new}
|
}}
|
||||||
>
|
className={styles.new}
|
||||||
Új bejegyzés / feedback
|
>
|
||||||
</div>
|
Bejegyzés írása...
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
{editorShowing && (
|
{editorShowing && (
|
||||||
<Modal
|
<Modal
|
||||||
closeClick={() => {
|
closeClick={() => {
|
||||||
|
@ -42,73 +28,39 @@ export default function Composer({ onSubmit }) {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{type !== 'private' && (
|
<input
|
||||||
<input
|
placeholder={'Téma...'}
|
||||||
placeholder={'Téma'}
|
required
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setTitle(e.target.value)
|
setTitle(e.target.value)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
<textarea
|
<textarea
|
||||||
placeholder={'...'}
|
placeholder={'Írj ide valamit...'}
|
||||||
|
required
|
||||||
value={val}
|
value={val}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setVal(e.target.value)
|
setVal(e.target.value)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{type === 'private' && (
|
<div className={styles.composerAction}>
|
||||||
<FileUploader
|
|
||||||
onChange={(e) => {
|
|
||||||
setFile(e.target.files[0])
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className={styles.typeSelector}>
|
|
||||||
<div>Post típusa:</div>
|
|
||||||
<div title="Minden felhasználó látja főoldalon, és tudnak rá válaszolni">
|
|
||||||
<input
|
|
||||||
onChange={(e) => {
|
|
||||||
setType(e.target.value)
|
|
||||||
}}
|
|
||||||
type="radio"
|
|
||||||
name="type"
|
|
||||||
value="public"
|
|
||||||
defaultChecked
|
|
||||||
/>
|
|
||||||
Publikus
|
|
||||||
</div>
|
|
||||||
<div title="Csak a weboldal üzemeltetője látja">
|
|
||||||
<input
|
|
||||||
onChange={(e) => {
|
|
||||||
setType(e.target.value)
|
|
||||||
}}
|
|
||||||
type="radio"
|
|
||||||
name="type"
|
|
||||||
value="private"
|
|
||||||
/>
|
|
||||||
Privát
|
|
||||||
</div>
|
|
||||||
<div className={styles.tip}>
|
|
||||||
(Tartsd egered opciókra több infóért)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={'actions'}>
|
|
||||||
<span
|
<span
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onSubmit(type, title, val, file)
|
if (!title) {
|
||||||
|
alert('Üres a tartalom!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!val) {
|
||||||
|
alert('Üres a téma!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(title, val)
|
||||||
setEditorShowing(false)
|
setEditorShowing(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Post
|
Posztolás
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
onClick={() => {
|
|
||||||
setEditorShowing(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,32 +1,97 @@
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
|
max-width: 400px;
|
||||||
|
width: 900px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container > input,
|
.container > input,
|
||||||
.container > textarea {
|
.container > textarea {
|
||||||
margin: 5px 0px;
|
margin: 5px 0px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
width: 97%;
|
||||||
|
border-color: #5d5f61;
|
||||||
|
background-color: #1f2021;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container > textarea {
|
||||||
|
margin-left: 1px;
|
||||||
|
width: 99%;
|
||||||
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeSelector {
|
.typeSelector {
|
||||||
|
display: inline-flex;
|
||||||
|
text-align: center;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typeSelector > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tip {
|
.tip {
|
||||||
font-size: 10px;
|
font-size: 11px;
|
||||||
margin: 0px 10px;
|
font-style: italic;
|
||||||
|
text-align: center;
|
||||||
|
margin: 15px 0px 2px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.new {
|
.new {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 10px 0px;
|
||||||
|
height: 19px;
|
||||||
|
width: 30%;
|
||||||
|
display: flex;
|
||||||
|
background-color: var(--hoover-color);
|
||||||
|
border: none;
|
||||||
|
color: var(--text-color);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
text-align: center;
|
font-size: 15px;
|
||||||
border: 2px dashed var(--text-color);
|
margin: 8px;
|
||||||
border-radius: 3px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
text-shadow: 1px 1px 8px black;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.new:hover {
|
.new:hover {
|
||||||
background-color: var(--hoover-color);
|
text-shadow: 2px 2px 8px black;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-out 0.5s;
|
||||||
|
background-color: var(--text-color);
|
||||||
|
font-weight: bold;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.composerAction {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 36%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.composerAction > span {
|
||||||
|
margin: 2px 2px;
|
||||||
|
padding: 0px 10px;
|
||||||
|
border: 1px solid #444;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-top: 14.5px !important;
|
||||||
|
padding-top: 4px !important;
|
||||||
|
transform: translate(109%, 10%);
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.composerAction > span:hover {
|
||||||
|
background-color: var(--hoover-color);
|
||||||
|
transition: width 0.5s, height 0.5s, ease-out 0.5s;
|
||||||
|
color: var(--text-color);
|
||||||
|
text-shadow: 2px 1.5px 10px black;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,33 @@
|
||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-right: 22%;
|
||||||
|
margin-left: 22%;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.listItem {
|
.listItem {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
padding: 5px;
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin: 3px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: white;
|
color: #a1a1a1;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.listItem:hover {
|
.listItem:hover {
|
||||||
background-color: var(--hoover-color);
|
background-color: var(--hoover-color);
|
||||||
|
transition: width 0.5s, height 0.5s, ease-out 0.5s;
|
||||||
|
text-shadow: 2px 1.5px 10px black;
|
||||||
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
color: gainsboro;
|
||||||
}
|
}
|
||||||
|
|
22
src/components/externalLinkIcon.js
Normal file
22
src/components/externalLinkIcon.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function ExternalLinkIcon({ size }) {
|
||||||
|
return (
|
||||||
|
<span style={{ margin: '4px', display: 'inline-block' }}>
|
||||||
|
<svg
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
viewBox={`0 0 24 24`}
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
|
||||||
|
<polyline points="15 3 21 3 21 9"></polyline>
|
||||||
|
<line x1="10" y1="14" x2="21" y2="3"></line>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
106
src/components/feedbackArea.js
Normal file
106
src/components/feedbackArea.js
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import styles from './feedbackArea.module.css'
|
||||||
|
import constants from '../constants.json'
|
||||||
|
|
||||||
|
function FileUploader({ onChange }) {
|
||||||
|
return (
|
||||||
|
<div className={styles.inputArea}>
|
||||||
|
<div className={styles.textTitle}>Fájl csatolása</div>
|
||||||
|
<input
|
||||||
|
className={styles.fileInput}
|
||||||
|
type="file"
|
||||||
|
name="file"
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitFeedback(from, content, file) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const promises = [
|
||||||
|
fetch(constants.apiUrl + 'postfeedback', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
content: content,
|
||||||
|
from: from,
|
||||||
|
}),
|
||||||
|
}).then((res) => {
|
||||||
|
return res.json()
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
const formData = new FormData() // eslint-disable-line
|
||||||
|
formData.append('file', file)
|
||||||
|
|
||||||
|
promises.push(
|
||||||
|
fetch(constants.apiUrl + 'postfeedbackfile', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
},
|
||||||
|
body: formData,
|
||||||
|
}).then((res) => {
|
||||||
|
return res.json()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(promises).then((res) => {
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FeedbackArea({ from, allowFile }) {
|
||||||
|
const [feedback, setFeedback] = useState('')
|
||||||
|
const [file, setFile] = useState()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.inputArea}>
|
||||||
|
<center>
|
||||||
|
<textarea
|
||||||
|
placeholder={'Ide kezdhetsz el írni...'}
|
||||||
|
onChange={(event) => setFeedback(event.target.value)}
|
||||||
|
value={feedback}
|
||||||
|
className={styles.contactFeedback}
|
||||||
|
/>
|
||||||
|
{allowFile && (
|
||||||
|
<div className={styles.fileContainer}>
|
||||||
|
<FileUploader
|
||||||
|
onChange={(e) => {
|
||||||
|
setFile(e.target.files[0])
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</center>
|
||||||
|
<center>
|
||||||
|
<div className={`${styles.send} buttonContainer`}>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
if (feedback) {
|
||||||
|
submitFeedback(from, feedback, file).then(() => {
|
||||||
|
alert('Üzenet elküldve!')
|
||||||
|
setFeedback('')
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
alert('Üresen hagytad az üzenet szövegének helyét!')
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Küldés
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
25
src/components/feedbackArea.module.css
Normal file
25
src/components/feedbackArea.module.css
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
.contactFeedback {
|
||||||
|
color: var(--text-color);
|
||||||
|
background-color: var(--background-color);
|
||||||
|
font-size: 14px;
|
||||||
|
width: 600px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 150px;
|
||||||
|
resize: none;
|
||||||
|
margin-top: 15px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.send {
|
||||||
|
width: 20%;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputArea {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fileContainer {
|
||||||
|
width: 400px;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
|
@ -9,27 +9,120 @@ import tabs from '../data/tabs.json'
|
||||||
import constants from '../constants.json'
|
import constants from '../constants.json'
|
||||||
import BB from './b.js'
|
import BB from './b.js'
|
||||||
|
|
||||||
|
import styles from './layout.module.css'
|
||||||
|
|
||||||
const renderSnow = () => {
|
const renderSnow = () => {
|
||||||
const date = new Date()
|
const date = new Date()
|
||||||
// if its december, and date is more than 5
|
// if its december, and date is more than 5
|
||||||
return date.getMonth() === 11 && date.getDate() > 5
|
return date.getMonth() === 11 && date.getDate() > 5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Donate() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={styles.modalHead}>Donate</div>
|
||||||
|
<div style={{ textAlign: 'justify', margin: '8px' }}>
|
||||||
|
Paypalen és Patreonon látszódik a neved, de Patreonon könnyen meg lehet
|
||||||
|
változtatni. Ha név nélkül szeretnél adakozni, akkor írd át egy
|
||||||
|
nickname-re. De akárhova adakozol, a neved sehova se lesz kiadva, és nem
|
||||||
|
látja 3. személy.
|
||||||
|
</div>
|
||||||
|
<div className={styles.donateLogoContainer}>
|
||||||
|
<a
|
||||||
|
href={`${constants.siteUrl}patreon`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{
|
||||||
|
border: 'none',
|
||||||
|
margin: '15px',
|
||||||
|
}}
|
||||||
|
src={`${constants.siteUrl}img/patreon-logo.png`}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<a href={`${constants.siteUrl}donate`} target="_blank" rel="noreferrer">
|
||||||
|
<img
|
||||||
|
style={{
|
||||||
|
border: 'none',
|
||||||
|
margin: '15px',
|
||||||
|
}}
|
||||||
|
src={`${constants.siteUrl}img/paypal-logo.png`}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function MessageButton({
|
||||||
|
userSpecificMotd,
|
||||||
|
setShowMotdModal,
|
||||||
|
refetchGlobalData,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="msgs">
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
if (!userSpecificMotd) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setShowMotdModal(true)
|
||||||
|
if (userSpecificMotd.seen) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fetch(constants.apiUrl + 'infos', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
userSpecificMotdSeen: true,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((resp) => {
|
||||||
|
return resp.json()
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
refetchGlobalData()
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
style={{ cursor: userSpecificMotd ? 'pointer' : 'default' }}
|
||||||
|
title={
|
||||||
|
userSpecificMotd && !userSpecificMotd.seen ? 'Új üzeneted van!' : ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{userSpecificMotd && !userSpecificMotd.seen ? '📬' : '📭'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default function Layout({
|
export default function Layout({
|
||||||
children,
|
children,
|
||||||
route,
|
router,
|
||||||
globalData,
|
globalData,
|
||||||
refetchGlobalData,
|
refetchGlobalData,
|
||||||
}) {
|
}) {
|
||||||
let href = route
|
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(true)
|
const [sidebarOpen, setSidebarOpen] = useState(true)
|
||||||
const [windowSize, setWindowSize] = useState([100, 200])
|
const [windowSize, setWindowSize] = useState([100, 200])
|
||||||
const [showMotdModal, setShowMotdModal] = useState(false)
|
const [showMotdModal, setShowMotdModal] = useState(false)
|
||||||
|
const [donateShowing, setDonateShowing] = useState(false)
|
||||||
const [showNewMsgModal, setShowNewMsgModal] = useState(true)
|
const [showNewMsgModal, setShowNewMsgModal] = useState(true)
|
||||||
|
|
||||||
const userId = globalData.userId
|
const userId = globalData.userId
|
||||||
const userSpecificMotd = globalData.userSpecificMotd
|
const userSpecificMotd = globalData.userSpecificMotd
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
setDonateShowing(!!router.query.donate)
|
||||||
|
},
|
||||||
|
[router.query.donate]
|
||||||
|
)
|
||||||
|
|
||||||
|
let href = router.route
|
||||||
if (href === '/' || href === '') {
|
if (href === '/' || href === '') {
|
||||||
href = 'index'
|
href = 'index'
|
||||||
}
|
}
|
||||||
|
@ -80,23 +173,33 @@ export default function Layout({
|
||||||
<div />
|
<div />
|
||||||
</span>
|
</span>
|
||||||
<div className="sidebarheader">
|
<div className="sidebarheader">
|
||||||
<img
|
<Link href="/">
|
||||||
style={{ maxWidth: '100%' }}
|
<a>
|
||||||
src={`${constants.siteUrl}img/frylabs-logo_small_transparent.png`}
|
<img
|
||||||
alt="Frylabs"
|
style={{
|
||||||
/>
|
maxWidth: '100%',
|
||||||
|
border: 'none',
|
||||||
|
margin: '1px',
|
||||||
|
}}
|
||||||
|
src={`${
|
||||||
|
constants.siteUrl
|
||||||
|
}img/frylabs-logo_small_transparent.png`}
|
||||||
|
alt="FryLabs"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{sidebarOpen ? (
|
{sidebarOpen ? (
|
||||||
<>
|
<>
|
||||||
<div id="sideBarLinks" className="sidebarLinks">
|
<div id="sideBarLinks" className={styles.sidebarLinks}>
|
||||||
{Object.keys(tabs).map((key) => {
|
{Object.keys(tabs).map((key) => {
|
||||||
const item = tabs[key]
|
const item = tabs[key]
|
||||||
return (
|
return (
|
||||||
<Link href={item.href} key={key}>
|
<Link href={item.href} key={key}>
|
||||||
<a
|
<a
|
||||||
onClick={closeSideBar}
|
onClick={closeSideBar}
|
||||||
className={href.includes(key) ? 'active' : undefined}
|
className={href.includes(key) ? styles.active : undefined}
|
||||||
id={item.id || undefined}
|
id={item.id || undefined}
|
||||||
>
|
>
|
||||||
{item.text}
|
{item.text}
|
||||||
|
@ -105,68 +208,26 @@ export default function Layout({
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<a
|
<a
|
||||||
href={`/dataeditor`}
|
onClick={() => {
|
||||||
onClick={closeSideBar}
|
closeSideBar()
|
||||||
target="_blank"
|
setDonateShowing(true)
|
||||||
rel="noreferrer"
|
}}
|
||||||
title={
|
|
||||||
'Kérdések szerkesztése, törlése, beküldése, és kitöltetlen tesztek'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Kérdés szerkesztő / kitöltetlen tesztek
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href={`/donate`}
|
|
||||||
className="donate"
|
|
||||||
onClick={closeSideBar}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
>
|
||||||
Donate
|
Donate
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="userStatus">
|
<div className={'userStatus'}>
|
||||||
<div className="msgs">
|
<MessageButton
|
||||||
<div
|
userSpecificMotd={userSpecificMotd}
|
||||||
onClick={() => {
|
setShowMotdModal={setShowMotdModal}
|
||||||
if (!userSpecificMotd) {
|
refetchGlobalData={refetchGlobalData}
|
||||||
return
|
/>
|
||||||
}
|
<div className={'uid'} title="User ID">
|
||||||
setShowMotdModal(true)
|
UID: {userId || '...'}
|
||||||
if (userSpecificMotd.seen) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fetch(constants.apiUrl + 'infos', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
userSpecificMotdSeen: true,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then((resp) => {
|
|
||||||
return resp.json()
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
refetchGlobalData()
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
style={{ cursor: userSpecificMotd ? 'pointer' : 'default' }}
|
|
||||||
title={
|
|
||||||
userSpecificMotd && !userSpecificMotd.seen
|
|
||||||
? "You've got Mail!"
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{userSpecificMotd && !userSpecificMotd.seen ? '📬' : '📭'}
|
|
||||||
</div>
|
|
||||||
<div title="User ID">UID: {userId || '...'}</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="logout"
|
className={'logout'}
|
||||||
|
title="Kijelentkezés"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
fetch(constants.apiUrl + 'logout', {
|
fetch(constants.apiUrl + 'logout', {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
@ -182,31 +243,38 @@ export default function Layout({
|
||||||
Logout
|
Logout
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{showMotdModal ? (
|
|
||||||
<Modal
|
|
||||||
closeClick={() => {
|
|
||||||
setShowMotdModal(false)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ textAlign: 'center' }}>Üzenet admintól:</div>
|
|
||||||
<div
|
|
||||||
dangerouslySetInnerHTML={{ __html: userSpecificMotd.msg }}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
) : null}
|
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<div className="content">{children}</div>
|
{donateShowing ? (
|
||||||
|
<Modal
|
||||||
|
closeClick={() => {
|
||||||
|
setDonateShowing(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Donate />
|
||||||
|
</Modal>
|
||||||
|
) : null}
|
||||||
|
{showMotdModal ? (
|
||||||
|
<Modal
|
||||||
|
closeClick={() => {
|
||||||
|
setShowMotdModal(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ textAlign: 'center' }}>Üzenet admintól:</div>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: userSpecificMotd.msg }} />
|
||||||
|
</Modal>
|
||||||
|
) : null}
|
||||||
{userSpecificMotd && !userSpecificMotd.seen && showNewMsgModal ? (
|
{userSpecificMotd && !userSpecificMotd.seen && showNewMsgModal ? (
|
||||||
<Modal
|
<Modal
|
||||||
closeClick={() => {
|
closeClick={() => {
|
||||||
setShowNewMsgModal(false)
|
setShowNewMsgModal(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Új üzeneted van, kattints 📬-ra bal alul megtekintéséhez!
|
Új üzeneted van, kattints a 📬-ra bal alul a megtekintéséhez!
|
||||||
</Modal>
|
</Modal>
|
||||||
) : null}
|
) : null}
|
||||||
|
<div className="content">{children}</div>
|
||||||
<BB />
|
<BB />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
49
src/components/layout.module.css
Normal file
49
src/components/layout.module.css
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
.modalHead {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding-top: 0px;
|
||||||
|
margin-top: 0px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
letter-spacing: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.donateLogoContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.donateLogoContainer img {
|
||||||
|
max-width: 100px;
|
||||||
|
margin: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarLinks > * {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
color: black;
|
||||||
|
font-size: 108%;
|
||||||
|
padding: 14px;
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--bright-color);
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarLinks a.active {
|
||||||
|
border: 0.5px solid var(--text-color);
|
||||||
|
color: white;
|
||||||
|
text-shadow: 2px 2px 8px black;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarLinks a:hover:not(.active) {
|
||||||
|
background-color: var(--text-color);
|
||||||
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
|
text-shadow: 2px 2px 8px black;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-out 0.5s;
|
||||||
|
}
|
|
@ -12,7 +12,9 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
max-height: 80%;
|
max-height: 80%;
|
||||||
width: 80%;
|
width: 50%;
|
||||||
|
max-width: 52%;
|
||||||
|
width: auto;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
background: var(--background-color);
|
background: var(--background-color);
|
||||||
height: auto;
|
height: auto;
|
||||||
|
@ -20,24 +22,27 @@
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
padding-top: 38px;
|
||||||
padding: 20px 30px;
|
padding-bottom: 25px;
|
||||||
cursor: auto;
|
padding-right: 14px;
|
||||||
|
padding-left: 16px;
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close {
|
.close {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 18px;
|
font-size: 16.5px;
|
||||||
position: absolute;
|
position: fixed;
|
||||||
|
top: 5px;
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
right: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-align: right;
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.children {
|
.children {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,17 @@ export default function NewsEntry({
|
||||||
const { reacts, title, content, user, comments, date, admin } = newsItem
|
const { reacts, title, content, user, comments, date, admin } = newsItem
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={styles.newsRoot}>
|
||||||
<div className={`${styles.newsContainer} ${admin && styles.adminPost}`}>
|
<div
|
||||||
|
className={`${styles.newsContainer} ${admin &&
|
||||||
|
styles.adminPost} ${!admin && styles.userPost} ${uid == user &&
|
||||||
|
styles.ownPost} ${uid == user && admin && styles.adminPost}`}
|
||||||
|
>
|
||||||
<div className={styles.newsHeader}>
|
<div className={styles.newsHeader}>
|
||||||
<div className={styles.newsTitle}>{title}</div>
|
<div className={styles.newsTitle}>{title}</div>
|
||||||
<div className={styles.user}>
|
<div className={styles.userinfo}>
|
||||||
<div>User #{user}</div>
|
<div>User #{user}</div>
|
||||||
<div className={styles.newsDate}>{date}</div>
|
<div className={styles.newsDate}> @ {date}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{admin ? (
|
{admin ? (
|
||||||
|
@ -38,11 +42,12 @@ export default function NewsEntry({
|
||||||
<div className={'actions'}>
|
<div className={'actions'}>
|
||||||
{uid === user ? (
|
{uid === user ? (
|
||||||
<span
|
<span
|
||||||
|
className={styles.delete_bttn}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onPostDelete()
|
onPostDelete()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
Törlés
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
<ReactButton
|
<ReactButton
|
||||||
|
@ -62,8 +67,6 @@ export default function NewsEntry({
|
||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
comments={comments}
|
comments={comments}
|
||||||
/>
|
/>
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,51 @@
|
||||||
.newsBody {
|
.newsBody {
|
||||||
margin: 0px 5px;
|
margin: 0px 12px;
|
||||||
font-size: 18px;
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
white-space: pre-line;
|
white-space: pre-line;
|
||||||
|
text-align: justify;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newsTitle {
|
.newsTitle {
|
||||||
font-size: 20px;
|
font-size: 25px;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
margin: 0px 5px;
|
margin: 0px 11px;
|
||||||
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newsDate {
|
.newsDate {
|
||||||
margin: 0px 5px;
|
margin-right: 12px;
|
||||||
|
margin-left: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newsContainer {
|
.newsContainer {
|
||||||
margin: 5px 5px;
|
margin: 5px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.delete_bttn {
|
||||||
|
font-size: 15px;
|
||||||
|
margin-top: 12px !important;
|
||||||
|
margin-left: 2px !important;
|
||||||
|
margin-right: 5px !important;
|
||||||
|
padding-top: 4px !important;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
.adminPost {
|
.adminPost {
|
||||||
border-left: 2px solid yellow;
|
border-left: 4px solid var(--text-color);
|
||||||
border-radius: 5px;
|
}
|
||||||
|
|
||||||
|
.userPost {
|
||||||
|
border-left: 4px solid azure;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ownPost {
|
||||||
|
border-left: 4px dotted azure;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newsContainer img {
|
.newsContainer img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
|
border: 2px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newsHeader {
|
.newsHeader {
|
||||||
|
@ -35,7 +54,17 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user {
|
.userinfo {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newsRoot {
|
||||||
|
background-color: var(--dark-color);
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ function useOutsideAlerter(ref, action) {
|
||||||
function ExistingReacts({ existingReacts, onClick, uid }) {
|
function ExistingReacts({ existingReacts, onClick, uid }) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.reactionContainer}>
|
<div className={styles.reactionContainer}>
|
||||||
<div>React</div>
|
<div className={styles.react_bttn}>Reakció</div>
|
||||||
{existingReacts &&
|
{existingReacts &&
|
||||||
Object.keys(existingReacts).map((key) => {
|
Object.keys(existingReacts).map((key) => {
|
||||||
const currReact = existingReacts[key]
|
const currReact = existingReacts[key]
|
||||||
|
@ -33,7 +33,7 @@ function ExistingReacts({ existingReacts, onClick, uid }) {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
title={currReact.join(', ')}
|
title={`'${key}': ${currReact.join(', ')}`}
|
||||||
className={`${currReact.includes(uid) && styles.reacted}`}
|
className={`${currReact.includes(uid) && styles.reacted}`}
|
||||||
key={key}
|
key={key}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
.reactionContainer {
|
.reactionContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
margin-top: 15px !important;
|
||||||
|
margin: 4px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reactionContainer > input {
|
.reactionContainer > input {
|
||||||
|
@ -20,10 +22,13 @@
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reactionContainer > div:hover {
|
.reactionContainer > div:hover {
|
||||||
background-color: var(--hoover-color);
|
background-color: var(--hoover-color);
|
||||||
|
color: gainsboro;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-out 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.break {
|
.break {
|
||||||
|
@ -32,9 +37,20 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.reacted {
|
.reacted {
|
||||||
color: yellow;
|
background-color: var(--text-color);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reacted:hover {
|
||||||
|
background-color: #96810b !important;
|
||||||
|
color: #42423e !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reactContainer {
|
.reactContainer {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.react_bttn {
|
||||||
|
font-size: 15px;
|
||||||
|
padding-top: 3px !important;
|
||||||
|
}
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
import styles from './sidebar.module.css'
|
|
||||||
|
|
||||||
export default function Sidebar(props) {
|
|
||||||
const { onClose } = props
|
|
||||||
return (
|
|
||||||
<div className={styles.container}>
|
|
||||||
<div
|
|
||||||
className={styles.closeBorder}
|
|
||||||
onClick={() => {
|
|
||||||
onClose()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{'⏩'}
|
|
||||||
</div>
|
|
||||||
<div className={styles.content}>{props.children}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
.container {
|
|
||||||
background-color: var(--background-color);
|
|
||||||
border-left: 3px solid #99f;
|
|
||||||
top: 0;
|
|
||||||
right: 0px;
|
|
||||||
height: 100%;
|
|
||||||
width: 400px;
|
|
||||||
max-width: 100%;
|
|
||||||
position: fixed;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.closeBorder {
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column;
|
|
||||||
justify-content: center;
|
|
||||||
color: white;
|
|
||||||
background-color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
.closeBorder:hover {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import constants from '../constants.json'
|
import constants from '../constants.json'
|
||||||
|
|
||||||
export default function Sleep (props) {
|
export default function Sleep(props) {
|
||||||
const hours = new Date().getHours()
|
const hours = new Date().getHours()
|
||||||
if (hours < 4 || hours > 23) {
|
if (hours < 4 || hours > 23) {
|
||||||
return (
|
return (
|
||||||
|
@ -9,9 +9,11 @@ export default function Sleep (props) {
|
||||||
<img
|
<img
|
||||||
style={{
|
style={{
|
||||||
margin: '10px',
|
margin: '10px',
|
||||||
width: '300px'
|
width: '300px',
|
||||||
|
border: '2px solid white',
|
||||||
}}
|
}}
|
||||||
src={constants.siteUrl + 'img/aludni.jpeg'}
|
src={constants.siteUrl + 'img/aludni.jpeg'}
|
||||||
|
title="Ezt a képet azert látod, mert ilyenkor már igazán nem ezen az oldalon kellene járnod"
|
||||||
/>
|
/>
|
||||||
</center>
|
</center>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,20 +3,7 @@
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.groupContainer {
|
.todoButtons {
|
||||||
display: flex;
|
height: 38px;
|
||||||
}
|
text-wrap: normal;
|
||||||
|
|
||||||
.group {
|
|
||||||
padding: 8px 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.group:hover {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectedGroup {
|
|
||||||
background-color: #444;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
.categoryName {
|
.categoryName {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
margin-top: 85px !important;
|
||||||
margin: 5px 0px;
|
margin: 5px 0px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default function TodoCard(props) {
|
||||||
<div
|
<div
|
||||||
className={styles.card}
|
className={styles.card}
|
||||||
style={{
|
style={{
|
||||||
border: `2px solid ${group || '#99f'}`,
|
border: `1px solid ${categories[category].color || '#f2cb05'}`,
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onClick(props.cardData)
|
onClick(props.cardData)
|
||||||
|
@ -27,7 +27,7 @@ export default function TodoCard(props) {
|
||||||
backgroundColor: categories[category].color,
|
backgroundColor: categories[category].color,
|
||||||
color: 'white',
|
color: 'white',
|
||||||
borderRadius: '2px',
|
borderRadius: '2px',
|
||||||
padding: '0px 2px',
|
padding: '3px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{categories[category].name}
|
{categories[category].name}
|
||||||
|
@ -35,9 +35,9 @@ export default function TodoCard(props) {
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.numbers}>
|
<div className={styles.numbers}>
|
||||||
<div className={`${voted && styles.voted}`}>
|
<div className={`${voted && styles.voted}`}>
|
||||||
<div>{`Votes: ${votes.length}`}</div>
|
<div>{`Szavazatok: ${votes.length}`}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>{points}</div>
|
<div>{`Nehézség: ${points}`}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
.card {
|
.card {
|
||||||
border-radius: 2px;
|
border-radius: 5px;
|
||||||
padding: 5px;
|
padding: 7px;
|
||||||
margin: 6px 3px;
|
margin: 8px 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
background-color: #171616;
|
||||||
}
|
}
|
||||||
|
|
||||||
.voted {
|
.voted {
|
||||||
color: #cf9;
|
color: var(--text-color);
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card:hover {
|
.card:hover {
|
||||||
|
@ -15,7 +17,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.card > div {
|
.card > div {
|
||||||
margin: 5px 0px;
|
margin: 6px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
|
@ -25,7 +27,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.category {
|
.category {
|
||||||
font-size: 10px;
|
font-size: 12px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +41,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.id {
|
.id {
|
||||||
margin: 0px 3px;
|
margin: 1px 6px 1px 1px;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ export default function TodoRow(props) {
|
||||||
const { categories, userId, onClick } = props
|
const { categories, userId, onClick } = props
|
||||||
const { name, category, votes, id, group } = props.rowData
|
const { name, category, votes, id, group } = props.rowData
|
||||||
const voted = votes.includes(userId)
|
const voted = votes.includes(userId)
|
||||||
|
const borderColor =
|
||||||
|
categories[category].borderColor || categories[category].color
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -14,7 +16,8 @@ export default function TodoRow(props) {
|
||||||
}}
|
}}
|
||||||
className={styles.row}
|
className={styles.row}
|
||||||
style={{
|
style={{
|
||||||
border: `2px solid ${group || '#99f'}`,
|
border: `2px dashed ${borderColor || 'white'}`,
|
||||||
|
borderRadius: '3px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={styles.id}>{`#${id}`}</div>
|
<div className={styles.id}>{`#${id}`}</div>
|
||||||
|
@ -23,11 +26,11 @@ export default function TodoRow(props) {
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
wordBreak: 'break-all',
|
wordBreak: 'break-all',
|
||||||
fontSize: '10px',
|
fontSize: '12px',
|
||||||
backgroundColor: categories[category].color,
|
backgroundColor: categories[category].color,
|
||||||
color: 'white',
|
color: 'white',
|
||||||
borderRadius: '2px',
|
borderRadius: '2px',
|
||||||
padding: '0px 2px',
|
padding: '3px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{categories[category].name}
|
{categories[category].name}
|
||||||
|
@ -35,7 +38,7 @@ export default function TodoRow(props) {
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`${styles.votes} ${voted && styles.voted}`}
|
className={`${styles.votes} ${voted && styles.voted}`}
|
||||||
>{`Votes: ${votes.length}`}</div>
|
>{`Szavazatok: ${votes.length}`}</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
border-radius: 5px;
|
||||||
border: 2px solid #99f;
|
padding: 7px;
|
||||||
border-radius: 2px;
|
margin: 8px 4px;
|
||||||
padding: 5px;
|
|
||||||
margin: 6px 3px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
background-color: #171616;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row:hover {
|
.row:hover {
|
||||||
|
@ -13,12 +12,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.row > div {
|
.row > div {
|
||||||
margin: 0px 5px;
|
margin: 6px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.id {
|
.id {
|
||||||
flex: 0 20px;
|
flex: 0 20px;
|
||||||
margin: 0px 3px;
|
margin: 1px 6px 1px 1px;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,10 +29,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.votes {
|
.votes {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.voted {
|
.voted {
|
||||||
color: #cf9;
|
color: var(--text-color);
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.catName {
|
.catName {
|
||||||
|
|
|
@ -25,18 +25,18 @@ export default function Todos({
|
||||||
// TODO: hide vote button if not voteable
|
// TODO: hide vote button if not voteable
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.name}>
|
<div className={styles.title}>
|
||||||
<span className={styles.id}>#{id}</span>
|
<span className={styles.id}>#{id}</span>
|
||||||
{name}
|
<div className={'subtitle'} style={{ textAlign: 'left' }}>
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
margin: '0px 5px',
|
|
||||||
fontSize: '10px',
|
|
||||||
backgroundColor: categories[category].color,
|
backgroundColor: categories[category].color,
|
||||||
color: 'white',
|
color: 'white',
|
||||||
|
fontSize: '12px',
|
||||||
borderRadius: '2px',
|
borderRadius: '2px',
|
||||||
padding: '0px 2px',
|
padding: '3px',
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{categories[category].name}
|
{categories[category].name}
|
||||||
|
@ -45,7 +45,7 @@ export default function Todos({
|
||||||
<hr />
|
<hr />
|
||||||
{group && (
|
{group && (
|
||||||
<div className={styles.row}>
|
<div className={styles.row}>
|
||||||
<div>Csoport:</div>
|
<div style={{ color: group }}>Csoport:</div>
|
||||||
<div style={{ color: group }}>
|
<div style={{ color: group }}>
|
||||||
{namedGroups[group] ? namedGroups[group].name : group}
|
{namedGroups[group] ? namedGroups[group].name : group}
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,31 +64,47 @@ export default function Todos({
|
||||||
<div>{votes.length}</div>
|
<div>{votes.length}</div>
|
||||||
</div>
|
</div>
|
||||||
{gitlink && (
|
{gitlink && (
|
||||||
<div className={styles.row}>
|
<center>
|
||||||
<a href={gitlink} target={'_blank'} rel={'noreferrer'}>
|
<div>
|
||||||
Git link
|
<a
|
||||||
</a>
|
href={gitlink}
|
||||||
</div>
|
style={{ textDecoration: 'none' }}
|
||||||
|
target={'_blank'}
|
||||||
|
rel={'noreferrer'}
|
||||||
|
>
|
||||||
|
Git link
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
)}
|
)}
|
||||||
<hr />
|
<hr />
|
||||||
{description && (
|
{description && (
|
||||||
<>
|
<>
|
||||||
<div className={styles.title}>Leírás</div>
|
<div className={'subtitle'} style={{ textAlign: 'left' }}>
|
||||||
<div dangerouslySetInnerHTML={{ __html: description }} />
|
Leírás
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={styles.description}
|
||||||
|
dangerouslySetInnerHTML={{ __html: description }}
|
||||||
|
/>
|
||||||
<hr />
|
<hr />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className={styles.category}></div>
|
<div className={styles.category} />
|
||||||
{voteable && (
|
{voteable && (
|
||||||
<div
|
<center>
|
||||||
className={`${styles.button} ${votes.includes(userId) &&
|
<div className={'buttonContainer'} style={{ width: '40%' }}>
|
||||||
styles.voted}`}
|
<div
|
||||||
onClick={() => {
|
className={`${styles.button} ${votes.includes(userId) &&
|
||||||
voteOn(card)
|
styles.voted}`}
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
voteOn(card)
|
||||||
{votes.includes(userId) ? 'Szavazat visszavonása' : 'Szavazás'}
|
}}
|
||||||
</div>
|
>
|
||||||
|
{votes.includes(userId) ? 'Szavazat visszavonása' : 'Szavazás'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,14 +9,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
color: #99f;
|
color: var(--text-color);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name {
|
.title {
|
||||||
color: #99f;
|
color: var(--text-color);
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
margin: 20px 0px;
|
margin: 5px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
}
|
}
|
||||||
|
|
||||||
.category {
|
.category {
|
||||||
|
@ -25,18 +28,26 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.id {
|
.id {
|
||||||
font-size: 16px;
|
font-size: 20px;
|
||||||
margin: 0px 3px;
|
margin: 0px 3px;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: calc(38vh);
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
margin: 2px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
border: 2px solid #99f;
|
border: 2px solid var(--text-color);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -20,29 +20,31 @@ export default function TodoBoard(props) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.container} key={key}>
|
<div className={styles.container} key={key}>
|
||||||
<div className={styles.title}>{table.name}</div>
|
<div className={styles.title}>{table.name}</div>
|
||||||
{tableCards.map((card, i) => {
|
<div className={styles.scroll}>
|
||||||
const shouldHide =
|
{tableCards.map((card, i) => {
|
||||||
card.state !== key ||
|
const shouldHide =
|
||||||
(selectedGroup !== null &&
|
card.state !== key ||
|
||||||
selectedGroup !== 'uncat' &&
|
(selectedGroup !== null &&
|
||||||
card.group !== selectedGroup) ||
|
selectedGroup !== 'uncat' &&
|
||||||
(selectedGroup === 'uncat' && card.group !== undefined)
|
card.group !== selectedGroup) ||
|
||||||
|
(selectedGroup === 'uncat' && card.group !== undefined)
|
||||||
|
|
||||||
if (shouldHide) {
|
if (shouldHide) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TodoRow
|
<TodoRow
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
key={i}
|
key={i}
|
||||||
type={key}
|
type={key}
|
||||||
rowData={card}
|
rowData={card}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
categories={categories}
|
categories={categories}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
@ -18,3 +20,9 @@
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scroll {
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
|
@ -4,11 +4,9 @@ import LoadingIndicator from '../LoadingIndicator.js'
|
||||||
import TodoBoard from './todoBoard.js'
|
import TodoBoard from './todoBoard.js'
|
||||||
import TodoTable from './todoTable.js'
|
import TodoTable from './todoTable.js'
|
||||||
import TodoSidebar from './todoSidebar.js'
|
import TodoSidebar from './todoSidebar.js'
|
||||||
|
import Modal from '../modal.js'
|
||||||
import Sidebar from '../sidebar.js'
|
|
||||||
|
|
||||||
import styles from './todo.module.css'
|
import styles from './todo.module.css'
|
||||||
// import styles from './todos.module.css'
|
|
||||||
import constants from '../../constants.json'
|
import constants from '../../constants.json'
|
||||||
|
|
||||||
const byVotes = (a, b) => {
|
const byVotes = (a, b) => {
|
||||||
|
@ -26,7 +24,6 @@ export default function Todos() {
|
||||||
const [selectedGroup, setSelectedGroup] = useState(null)
|
const [selectedGroup, setSelectedGroup] = useState(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.info('Fetching todos')
|
|
||||||
fetch(`${constants.apiUrl}todos`, {
|
fetch(`${constants.apiUrl}todos`, {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
|
@ -96,13 +93,14 @@ export default function Todos() {
|
||||||
const sg = namedGroups[selectedGroup]
|
const sg = namedGroups[selectedGroup]
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.groupContainer}>
|
<div className={'buttonContainer'}>
|
||||||
{groups.map((group) => {
|
{groups.map((group) => {
|
||||||
const namedGroup = namedGroups[group]
|
const namedGroup = namedGroups[group]
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${styles.group} ${group === selectedGroup &&
|
className={`${'buttonContainer'}, ${
|
||||||
styles.selectedGroup}`}
|
styles.todoButtons
|
||||||
|
} ${group === selectedGroup && 'activeButton'}`}
|
||||||
key={group}
|
key={group}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedGroup(group)
|
setSelectedGroup(group)
|
||||||
|
@ -113,8 +111,9 @@ export default function Todos() {
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<div
|
<div
|
||||||
className={`${styles.group} ${'uncat' === selectedGroup &&
|
className={`${'buttonContainer'}, ${
|
||||||
styles.selectedGroup}`}
|
styles.todoButtons
|
||||||
|
} ${'uncat' === selectedGroup && 'activeButton'}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedGroup('uncat')
|
setSelectedGroup('uncat')
|
||||||
}}
|
}}
|
||||||
|
@ -122,7 +121,8 @@ export default function Todos() {
|
||||||
{'Kategorizálatlan'}
|
{'Kategorizálatlan'}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={styles.group}
|
className={`${'buttonContainer'}, ${styles.todoButtons} ${null ===
|
||||||
|
selectedGroup && 'activeButton'}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedGroup(null)
|
setSelectedGroup(null)
|
||||||
}}
|
}}
|
||||||
|
@ -140,8 +140,8 @@ export default function Todos() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{sidebarCard && (
|
{sidebarCard && (
|
||||||
<Sidebar
|
<Modal
|
||||||
onClose={() => {
|
closeClick={() => {
|
||||||
setSidebarCard(null)
|
setSidebarCard(null)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -165,15 +165,10 @@ export default function Todos() {
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Sidebar>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
{renderGrouper()}
|
{renderGrouper()}
|
||||||
<div
|
<div>
|
||||||
style={{
|
|
||||||
// width: '100%',
|
|
||||||
marginRight: sidebarCard ? 380 : 0,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TodoBoard
|
<TodoBoard
|
||||||
columns={cols}
|
columns={cols}
|
||||||
cards={bCards}
|
cards={bCards}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"siteUrl": "https://qmining.frylabs.net/",
|
"siteUrl": "https://qmining.frylabs.net/",
|
||||||
"apiUrl": "https://api.frylabs.net/",
|
"apiUrl": "https://api.frylabs.net/",
|
||||||
"devApiUrl": "http://localhost:8080/",
|
|
||||||
"mobileWindowWidth": 700,
|
"mobileWindowWidth": 700,
|
||||||
"maxQuestionsToRender": 250
|
"maxQuestionsToRender": 250
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
{
|
{
|
||||||
"header": [
|
"header": ["Miben", "Hogy"],
|
||||||
"Miben",
|
|
||||||
"Hogy"
|
|
||||||
],
|
|
||||||
"rows": {
|
"rows": {
|
||||||
"feedbacker": {
|
"feedbacker": {
|
||||||
"name": "Visszajelzésben",
|
"name": "Visszajelzésben",
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"install": {
|
|
||||||
"href": "/install",
|
|
||||||
"text": "Install"
|
|
||||||
},
|
|
||||||
"allqr": {
|
|
||||||
"href": "/allqr.txt",
|
|
||||||
"text": "Összes kérdés TXT"
|
|
||||||
},
|
|
||||||
"data": {
|
|
||||||
"href": "/data.json",
|
|
||||||
"text": "Összes kérdés JSON"
|
|
||||||
},
|
|
||||||
"irc": {
|
|
||||||
"href": "/irc?index",
|
|
||||||
"text": "IRC chat"
|
|
||||||
},
|
|
||||||
"dataeditor": {
|
|
||||||
"href": "/dataeditor?index",
|
|
||||||
"text": "Dataeditor"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +1,34 @@
|
||||||
{
|
{
|
||||||
"index": {
|
"index": {
|
||||||
"href": "/",
|
"href": "/",
|
||||||
"text": "Home"
|
"text": "Főoldal"
|
||||||
},
|
},
|
||||||
"manual": {
|
"script": {
|
||||||
"href": "/manual",
|
"href": "/script",
|
||||||
"text": "Manual"
|
"text": "Script"
|
||||||
},
|
},
|
||||||
"allQuestions": {
|
"allQuestions": {
|
||||||
"href": "/allQuestions",
|
"href": "/allQuestions",
|
||||||
"text": "Kérdések és tárgyak"
|
"text": "Kérdések és tárgyak"
|
||||||
},
|
},
|
||||||
"pwRequest": {
|
|
||||||
"href": "/pwRequest",
|
|
||||||
"text": "Jelszó kérés"
|
|
||||||
},
|
|
||||||
"contribute": {
|
"contribute": {
|
||||||
"href": "/contribute",
|
"href": "/contribute",
|
||||||
"text": "Todos, contribute"
|
"text": "Teendők"
|
||||||
},
|
},
|
||||||
"ranklist": {
|
"ranklist": {
|
||||||
"href": "/ranklist",
|
"href": "/ranklist",
|
||||||
"text": "Ranklista"
|
"text": "Ranklista"
|
||||||
|
},
|
||||||
|
"pwRequest": {
|
||||||
|
"href": "/pwRequest",
|
||||||
|
"text": "Jelszó generálás"
|
||||||
|
},
|
||||||
|
"faq": {
|
||||||
|
"href": "/faq",
|
||||||
|
"text": "GYIK"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"href": "/contact",
|
||||||
|
"text": "Kapcsolat"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,43 @@
|
||||||
:root {
|
:root {
|
||||||
--text-color: #9999ff;
|
--text-color: #f2cb05;
|
||||||
--primary-color: #9999ff;
|
--primary-color: #f2cb05;
|
||||||
--bright-color: #f2f2f2;
|
--bright-color: #f2f2f2;
|
||||||
--background-color: #222426;
|
--background-color: #222426;
|
||||||
--hoover-color: #202020;
|
--hoover-color: #393939;
|
||||||
|
--dark-color: #191919;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Kameron&family=Overpass+Mono:wght@300;400&display=swap');
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font: normal 14px Verdana;
|
font-family: 'Kameron', serif;
|
||||||
|
font-family: 'Overpass Mono', monospace;
|
||||||
color: #999999;
|
color: #999999;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin: 10px;
|
||||||
|
border: 2px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 2px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #c1c1c1;
|
||||||
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: 120px;
|
height: 140px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid #666;
|
border: 1px solid #666;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
@ -28,8 +46,16 @@ textarea {
|
||||||
input {
|
input {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
|
border: 1px solid #666;
|
||||||
|
border-radius: 5px;
|
||||||
|
width: 80%;
|
||||||
|
font-family: inherit;
|
||||||
border: 1px solid #444;
|
border: 1px solid #444;
|
||||||
border-radius: 3px;
|
width: 98%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
border: 0px solid #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
|
@ -45,7 +71,6 @@ input {
|
||||||
.sidebar {
|
.sidebar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
@ -57,25 +82,7 @@ input {
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
margin-left: 200px;
|
margin-left: 200px;
|
||||||
padding: 1px 16px;
|
padding: 1px 15px;
|
||||||
}
|
|
||||||
|
|
||||||
.sidebarLinks a {
|
|
||||||
display: block;
|
|
||||||
color: black;
|
|
||||||
padding: 16px;
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--bright-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebarLinks a.active {
|
|
||||||
background-color: var(--text-color);
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebarLinks a:hover:not(.active) {
|
|
||||||
background-color: #555;
|
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menuicon div {
|
.menuicon div {
|
||||||
|
@ -99,7 +106,11 @@ input {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 10px;
|
overflow: hidden;
|
||||||
|
padding-top: 15px;
|
||||||
|
padding-bottom: 17px;
|
||||||
|
padding-right: 2px;
|
||||||
|
padding-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 700px) {
|
@media screen and (max-width: 700px) {
|
||||||
|
@ -108,9 +119,11 @@ input {
|
||||||
height: auto;
|
height: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar a {
|
.sidebar a {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.content {
|
div.content {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
@ -139,6 +152,7 @@ input {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
padding-top: 20px;
|
||||||
transform: translateX(-50%) translateY(-50%);
|
transform: translateX(-50%) translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,8 +167,10 @@ input {
|
||||||
}
|
}
|
||||||
|
|
||||||
.rtfmImage {
|
.rtfmImage {
|
||||||
|
text-align: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
border: 2px solid white;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,19 +180,25 @@ input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.endofpage {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.questionContainer {
|
.questionContainer {
|
||||||
margin: 10px;
|
margin: 6px;
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.questionContainer:hover {
|
.questionContainer:hover {
|
||||||
background-color: var(--hoover-color);
|
background-color: #141414;
|
||||||
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.question {
|
.question {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
color: #ffffff;
|
color: gainsboro;
|
||||||
}
|
}
|
||||||
|
|
||||||
.answer {
|
.answer {
|
||||||
|
@ -193,7 +215,6 @@ input {
|
||||||
.loadingindicator {
|
.loadingindicator {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
}
|
}
|
||||||
|
@ -203,16 +224,22 @@ input {
|
||||||
}
|
}
|
||||||
|
|
||||||
.subjectSelector {
|
.subjectSelector {
|
||||||
overflow: scroll;
|
overflow: auto;
|
||||||
height: 350px;
|
height: auto;
|
||||||
|
max-height: 250px;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
|
padding: 5px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-left: 8px;
|
||||||
|
background-color: #141414;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subjItem {
|
.subjItem {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
|
padding-top: 5px;
|
||||||
|
margin-top: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
float: 1;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
@ -227,47 +254,83 @@ input {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.donate {
|
|
||||||
background-color: #222a26;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rtfmImage {
|
.rtfmImage {
|
||||||
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin: 0px 10px;
|
margin: 0px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning {
|
||||||
|
color: red;
|
||||||
|
font-weight: 100;
|
||||||
|
font-size: 17.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#manualWarn {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.manual_img {
|
||||||
|
padding: 20px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 23px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 100;
|
||||||
|
margin: 0px;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pageHeader {
|
||||||
|
background-color: var(--text-color);
|
||||||
|
height: 45px;
|
||||||
|
max-width: 100%;
|
||||||
|
color: black !important;
|
||||||
|
margin: 5px 0px 0px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pageHeader > h1 {
|
||||||
|
padding-top: 6px;
|
||||||
|
letter-spacing: 7px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.manualUsage {
|
.manualUsage {
|
||||||
|
margin-top: 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
.manualBody {
|
||||||
cursor: pointer;
|
text-align: justify;
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
select:hover {
|
|
||||||
border: 1px solid #99f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.userStatus {
|
.userStatus {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
background-color: #373737;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.msgs {
|
.msgs {
|
||||||
display: flex;
|
font-size: 15px;
|
||||||
align-items: center;
|
}
|
||||||
|
|
||||||
|
.uid {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout {
|
.logout {
|
||||||
padding: 7px;
|
padding: 6px;
|
||||||
|
margin-right: 7px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
font-size: 15.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logout:hover {
|
.logout:hover {
|
||||||
|
@ -275,11 +338,13 @@ select:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.msgs :first-child {
|
.msgs :first-child {
|
||||||
font-size: 35px;
|
font-size: 27px;
|
||||||
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.msgs > div {
|
.msgs > div {
|
||||||
margin: 0px 5px;
|
padding: 2px 6px;
|
||||||
|
font-size: 13.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
|
@ -299,3 +364,114 @@ select:hover {
|
||||||
.actions > span:hover {
|
.actions > span:hover {
|
||||||
background-color: var(--hoover-color);
|
background-color: var(--hoover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.buttonContainer {
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 10px 0px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonContainer > * {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 1;
|
||||||
|
background-color: var(--hoover-color);
|
||||||
|
border: none;
|
||||||
|
color: var(--text-color);
|
||||||
|
padding: 5px 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 15px;
|
||||||
|
margin: 8px 5px 2px 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-shadow: 1px 1px 8px black;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonContainer > *:hover {
|
||||||
|
text-shadow: 2px 2px 8px black;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-out 0.5s;
|
||||||
|
background-color: var(--text-color);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonContainer > .activeButton {
|
||||||
|
background-color: var(--text-color);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select:hover {
|
||||||
|
border: 1px solid #f2cb05;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: 10px 0px 0px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbContainer > input {
|
||||||
|
width: 5%;
|
||||||
|
background-color: #9c9c98;
|
||||||
|
color: azure;
|
||||||
|
font-family: inherit;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectContainer > select,
|
||||||
|
.selectContainer > input {
|
||||||
|
width: 20%;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1.5px solid white;
|
||||||
|
background-color: #9c9c98;
|
||||||
|
color: azure;
|
||||||
|
font-family: inherit;
|
||||||
|
margin: 6px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectContainer > select:hover,
|
||||||
|
.selectContainer > select:active {
|
||||||
|
border: 2px solid var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectContainer > div {
|
||||||
|
padding: 0px 5px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
padding-top: 1px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
text-align: center;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resultContainer {
|
||||||
|
color: gainsboro;
|
||||||
|
font-size: 18px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
export default function Custom404 () {
|
export default function Custom404() {
|
||||||
return (
|
return (
|
||||||
<center>
|
<center>
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
<iframe width='660' height='465' src='https://www.youtube-nocookie.com/embed/GOzwOeONBhQ' frameBorder='0' allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture' allowFullScreen />
|
<iframe
|
||||||
|
width="660"
|
||||||
|
height="465"
|
||||||
|
src="https://www.youtube-nocookie.com/embed/GOzwOeONBhQ"
|
||||||
|
frameBorder="0"
|
||||||
|
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
allowFullScreen
|
||||||
|
/>
|
||||||
</center>
|
</center>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ function MyApp({ Component, pageProps, router }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
route={router.route}
|
router={router}
|
||||||
globalData={globalData}
|
globalData={globalData}
|
||||||
refetchGlobalData={getGlobalProps}
|
refetchGlobalData={getGlobalProps}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
import Document, { Html, Head, Main, NextScript } from 'next/document'
|
||||||
|
|
||||||
class MyDocument extends Document {
|
class MyDocument extends Document {
|
||||||
static async getInitialProps (ctx) {
|
static async getInitialProps(ctx) {
|
||||||
const initialProps = await Document.getInitialProps(ctx)
|
const initialProps = await Document.getInitialProps(ctx)
|
||||||
return { ...initialProps }
|
return { ...initialProps }
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Html>
|
<Html>
|
||||||
<Head />
|
<Head />
|
||||||
<body bgcolor='#222426'>
|
<body bgcolor="#222426">
|
||||||
<Main />
|
<Main />
|
||||||
<NextScript />
|
<NextScript />
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
function Error ({ statusCode }) {
|
function Error({ statusCode }) {
|
||||||
const render404 = () => {
|
const render404 = () => {
|
||||||
return (
|
return (
|
||||||
<center>
|
<center>
|
||||||
<h1>404</h1>
|
<h1>404</h1>
|
||||||
<iframe width='100%' height='465' src='https://www.youtube-nocookie.com/embed/GOzwOeONBhQ' frameBorder='0' allow='accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture' allowFullScreen />
|
<iframe
|
||||||
|
width="100%"
|
||||||
|
height="465"
|
||||||
|
src="https://www.youtube-nocookie.com/embed/GOzwOeONBhQ"
|
||||||
|
frameBorder="0"
|
||||||
|
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
|
||||||
|
allowFullScreen
|
||||||
|
/>
|
||||||
</center>
|
</center>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import QuestionSearchResult from '../components/QuestionSearchResult.js'
|
||||||
import Subject from '../components/Subject.js'
|
import Subject from '../components/Subject.js'
|
||||||
import SubjectSelector from '../components/SubjectSelector.js'
|
import SubjectSelector from '../components/SubjectSelector.js'
|
||||||
import Sleep from '../components/sleep'
|
import Sleep from '../components/sleep'
|
||||||
|
import ExternalLinkIcon from '../components/externalLinkIcon'
|
||||||
|
|
||||||
import styles from './allQuestions.module.css'
|
import styles from './allQuestions.module.css'
|
||||||
|
|
||||||
|
@ -74,8 +75,10 @@ export default function AllQuestions({ router }) {
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [activeSubjName, setActiveSubjName] = useState('')
|
const [activeSubjName, setActiveSubjName] = useState('')
|
||||||
const [dbs, setDbs] = useState(null)
|
const [dbs, setDbs] = useState(null)
|
||||||
|
const [selectedDb, setSelectedDb] = useState('')
|
||||||
const [data, setData] = useState(null)
|
const [data, setData] = useState(null)
|
||||||
const [fetchingData, setFetchingData] = useState(false)
|
const [fetchingData, setFetchingData] = useState(false)
|
||||||
|
|
||||||
const subjectCount = data ? data.length : 0
|
const subjectCount = data ? data.length : 0
|
||||||
const questionCount = data ? data.reduce(countReducer, 0) : 0
|
const questionCount = data ? data.reduce(countReducer, 0) : 0
|
||||||
|
|
||||||
|
@ -87,6 +90,7 @@ export default function AllQuestions({ router }) {
|
||||||
const querySearch = router.query.question
|
const querySearch = router.query.question
|
||||||
? decodeURIComponent(router.query.question)
|
? decodeURIComponent(router.query.question)
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
console.log(querySearch)
|
console.log(querySearch)
|
||||||
|
|
||||||
fetchDbs().then((res) => {
|
fetchDbs().then((res) => {
|
||||||
|
@ -98,40 +102,65 @@ export default function AllQuestions({ router }) {
|
||||||
if (dbs) {
|
if (dbs) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<select
|
<div className={'pageHeader'}>
|
||||||
className={styles.select}
|
<h1>Kérdések és tárgyak</h1>
|
||||||
defaultValue={-1}
|
</div>
|
||||||
onChange={(event) => {
|
<Sleep />
|
||||||
const key = event.target.value
|
<div className={'description'}>
|
||||||
setData(null)
|
Ezen az oldalon tudsz manuálisan keresni a kérdések és a tárgyak
|
||||||
setFetchingData(true)
|
között, vagy ellenőrizni, hogy egy adott tárgy szerepel-e a kérdés-
|
||||||
if (key === 'all') {
|
és tárgyadatbázisban. Ezen kívül a kérdéseket le is töltheted
|
||||||
fetchAllData(dbs).then((res) => {
|
offline használatra. (txt formátumban)
|
||||||
setData(mergeData(res))
|
</div>
|
||||||
setFetchingData(false)
|
<center>
|
||||||
})
|
<div className={`buttonContainer ${styles.dataEditor}`}>
|
||||||
} else {
|
<div
|
||||||
fetchData(dbs[key]).then((res) => {
|
onClick={() => {
|
||||||
setData(res.data)
|
window.open(`${constants.siteUrl}dataeditor`, '_blank')
|
||||||
setFetchingData(false)
|
}}
|
||||||
})
|
>
|
||||||
}
|
Kérdés szerkesztő
|
||||||
}}
|
<ExternalLinkIcon size={15} />
|
||||||
>
|
</div>
|
||||||
<option disabled value={-1}>
|
</div>
|
||||||
{' -- Válassz egy kérdés adatbázist -- '}
|
</center>
|
||||||
</option>
|
<div className={'selectContainer'}>
|
||||||
{dbs.map((db, i) => {
|
<select
|
||||||
return (
|
defaultValue={-1}
|
||||||
<option value={i} key={db.path}>
|
onChange={(event) => {
|
||||||
{db.name}
|
const key = event.target.value
|
||||||
</option>
|
setData(null)
|
||||||
)
|
setFetchingData(true)
|
||||||
})}
|
if (key === 'all') {
|
||||||
<option value={'all'} key={'all'}>
|
setSelectedDb(key)
|
||||||
{'All'}
|
fetchAllData(dbs).then((res) => {
|
||||||
</option>
|
setData(mergeData(res))
|
||||||
</select>
|
setFetchingData(false)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
setSelectedDb(dbs[key].name)
|
||||||
|
fetchData(dbs[key]).then((res) => {
|
||||||
|
setData(res.data)
|
||||||
|
setFetchingData(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option disabled value={-1}>
|
||||||
|
{' Válassz egy adatbázist!'}
|
||||||
|
</option>
|
||||||
|
{dbs.map((db, i) => {
|
||||||
|
return (
|
||||||
|
<option value={i} key={db.path}>
|
||||||
|
{db.name}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<option value={'all'} key={'all'}>
|
||||||
|
{'Összes kérdés'}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -148,31 +177,28 @@ export default function AllQuestions({ router }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Head>
|
|
||||||
<title>Tárgyak - Qmining | Frylabs.net</title>
|
|
||||||
</Head>
|
|
||||||
{data ? (
|
{data ? (
|
||||||
<>
|
<>
|
||||||
<div className={styles.searchContainer}>
|
<center>
|
||||||
<input
|
<div className={styles.searchContainer}>
|
||||||
autoFocus
|
<input
|
||||||
placeholder="Keresés..."
|
placeholder="Kezdj el írni a kereséshez..."
|
||||||
className={styles.searchBar}
|
type="text"
|
||||||
type="text"
|
value={searchTerm}
|
||||||
value={searchTerm}
|
onChange={(event) => {
|
||||||
onChange={(event) => {
|
setSearchTerm(event.target.value)
|
||||||
setSearchTerm(event.target.value)
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
<button
|
||||||
<button
|
onClick={() => {
|
||||||
onClick={() => {
|
setSearchTerm('')
|
||||||
setSearchTerm('')
|
}}
|
||||||
}}
|
className={styles.clearButton}
|
||||||
className={styles.clearButton}
|
>
|
||||||
>
|
X
|
||||||
X
|
</button>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</center>
|
||||||
<hr />
|
<hr />
|
||||||
<SubjectSelector
|
<SubjectSelector
|
||||||
data={data}
|
data={data}
|
||||||
|
@ -182,10 +208,9 @@ export default function AllQuestions({ router }) {
|
||||||
setActiveSubjName(subjName)
|
setActiveSubjName(subjName)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<hr />
|
|
||||||
<div>{/*{qCount} kérdés, {sCount} tárgy */}</div>
|
|
||||||
<Sleep />
|
|
||||||
<div>
|
<div>
|
||||||
|
<hr />
|
||||||
<Subject subj={currSubj} />
|
<Subject subj={currSubj} />
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
@ -197,16 +222,11 @@ export default function AllQuestions({ router }) {
|
||||||
const renderQuestionBrowser = () => {
|
const renderQuestionBrowser = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Head>
|
|
||||||
<title>Qmining - Kérdés keresés | Frylabs.net</title>
|
|
||||||
</Head>
|
|
||||||
{data ? (
|
{data ? (
|
||||||
<>
|
<>
|
||||||
<div className={styles.searchContainer}>
|
<div className={styles.searchContainer}>
|
||||||
<input
|
<input
|
||||||
autoFocus
|
placeholder="Kezdj el írni a kereséshez..."
|
||||||
placeholder="Keresés..."
|
|
||||||
className={styles.searchBar}
|
|
||||||
type="text"
|
type="text"
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
|
@ -228,7 +248,6 @@ export default function AllQuestions({ router }) {
|
||||||
X
|
X
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
|
||||||
<div>
|
<div>
|
||||||
<QuestionSearchResult data={data} searchTerm={searchTerm} />
|
<QuestionSearchResult data={data} searchTerm={searchTerm} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -240,23 +259,43 @@ export default function AllQuestions({ router }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{dbs ? (
|
<Head>
|
||||||
|
<title>Kérdések és tárgyak - Qmining | Frylabs.net</title>
|
||||||
|
</Head>
|
||||||
|
{dbs ? <>{renderDbSelector()}</> : <LoadingIndicator />}
|
||||||
|
{dbs && data ? (
|
||||||
<>
|
<>
|
||||||
{renderDbSelector()}
|
<div className={styles.info}>
|
||||||
{data && `${questionCount} kérdés, ${subjectCount} tárgy`}
|
{`Összesen ${questionCount} kérdés, ${subjectCount} tárgyból`}
|
||||||
<div className={styles.typeSelector}>
|
</div>
|
||||||
|
<div className={'buttonContainer'}>
|
||||||
<div
|
<div
|
||||||
className={!subjectsShowing ? styles.activeTypeSelector : ''}
|
className={!subjectsShowing ? 'activeButton' : ''}
|
||||||
onClick={() => setSubjectsShowing(false)}
|
onClick={() => setSubjectsShowing(false)}
|
||||||
>
|
>
|
||||||
Kérdések
|
Kérdések
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={subjectsShowing ? styles.activeTypeSelector : ''}
|
className={subjectsShowing ? 'activeButton' : ''}
|
||||||
onClick={() => setSubjectsShowing(true)}
|
onClick={() => setSubjectsShowing(true)}
|
||||||
>
|
>
|
||||||
Tárgyak
|
Tárgyak
|
||||||
</div>
|
</div>
|
||||||
|
<a
|
||||||
|
onClick={() => {
|
||||||
|
if (selectedDb === 'all') {
|
||||||
|
window.open(`${constants.apiUrl}allqr.txt`, '_blank')
|
||||||
|
} else {
|
||||||
|
window.open(
|
||||||
|
`${constants.apiUrl}allqr.txt?db=${selectedDb}`,
|
||||||
|
'_blank'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{'Kérdések letöltése'}
|
||||||
|
<ExternalLinkIcon size={15} />
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{fetchingData ? (
|
{fetchingData ? (
|
||||||
<LoadingIndicator />
|
<LoadingIndicator />
|
||||||
|
@ -268,9 +307,9 @@ export default function AllQuestions({ router }) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : fetchingData ? (
|
||||||
<LoadingIndicator />
|
<LoadingIndicator />
|
||||||
)}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +1,29 @@
|
||||||
.searchBar {
|
|
||||||
margin: 10px;
|
|
||||||
color: white;
|
|
||||||
background-color: #222426;
|
|
||||||
border: none;
|
|
||||||
font-size: 18px;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.searchContainer {
|
.searchContainer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clearButton {
|
.clearButton {
|
||||||
width: 80px;
|
width: 50px;
|
||||||
background-color: var(--background-color);
|
background-color: var(--background-color);
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 23px;
|
font-size: 18px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: none;
|
border: none;
|
||||||
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeSelector {
|
.info {
|
||||||
margin: 10px 0px;
|
text-align: center;
|
||||||
height: 50px;
|
font-style: italic;
|
||||||
display: flex;
|
padding-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeSelector div {
|
.dataEditor {
|
||||||
display: flex;
|
margin-bottom: 25px;
|
||||||
align-items: center;
|
width: 25%;
|
||||||
justify-content: center;
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
font-size: 18px;
|
|
||||||
color: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.typeSelector div:hover {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.activeTypeSelector {
|
|
||||||
background-color: #444;
|
|
||||||
}
|
}
|
||||||
|
|
87
src/pages/contact.js
Normal file
87
src/pages/contact.js
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import Head from 'next/head'
|
||||||
|
|
||||||
|
import FeedbackArea from '../components/feedbackArea'
|
||||||
|
import constants from '../constants.json'
|
||||||
|
import LoadingIndicator from '../components/LoadingIndicator'
|
||||||
|
|
||||||
|
import Sleep from '../components/sleep'
|
||||||
|
|
||||||
|
import styles from './contact.module.css'
|
||||||
|
|
||||||
|
export default function Contact() {
|
||||||
|
const [contacts, setContacts] = useState()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch(constants.apiUrl + 'contacts.json', {
|
||||||
|
method: 'GET',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.json()
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
setContacts(res)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Head>
|
||||||
|
<title>Kapcsolat - Qmining | Frylabs.net</title>
|
||||||
|
</Head>
|
||||||
|
<div className={'pageHeader'}>
|
||||||
|
<h1>Kapcsolat</h1>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<Sleep />
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
<div className={'subtitle'}>Üzenet küldése</div>
|
||||||
|
<div className={styles.text}>
|
||||||
|
Weboldalon keresztüli üzenetküldés az adminnak (feedback). A válasz a
|
||||||
|
bal alsó postaládába (📬 ikon) fog érkezni.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<FeedbackArea from={'contact'} allowFile />
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<div className={styles.contactsContainer}>
|
||||||
|
{Object.keys(contacts).map((key) => {
|
||||||
|
const { description, value, href } = contacts[key]
|
||||||
|
return (
|
||||||
|
<div key={key}>
|
||||||
|
<div>{description}</div>
|
||||||
|
{href ? (
|
||||||
|
<a target="blank" rel="noreferrer" href={href}>
|
||||||
|
{' '}
|
||||||
|
{value}{' '}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<div>{value}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<LoadingIndicator />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
48
src/pages/contact.module.css
Normal file
48
src/pages/contact.module.css
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactsContainer {
|
||||||
|
margin: 5px;
|
||||||
|
max-width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactsContainer > * {
|
||||||
|
text-decoration: none;
|
||||||
|
color: gainsboro;
|
||||||
|
padding-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactsContainer > * > * {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactsContainer > * > *:nth-child(1) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactsContainer > * > *:nth-child(2) {
|
||||||
|
flex: 0 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-right: 100px;
|
||||||
|
padding-left: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text > div:nth-child(1) {
|
||||||
|
font-size: 20px;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactTable {
|
||||||
|
border-spacing: 15px;
|
||||||
|
}
|
|
@ -3,116 +3,98 @@ import Head from 'next/head'
|
||||||
|
|
||||||
import Sleep from '../components/sleep'
|
import Sleep from '../components/sleep'
|
||||||
import Todos from '../components/todoStuff/todos'
|
import Todos from '../components/todoStuff/todos'
|
||||||
|
import FeedbackArea from '../components/feedbackArea'
|
||||||
|
import Modal from '../components/modal'
|
||||||
|
|
||||||
import constants from '../constants.json'
|
import constants from '../constants.json'
|
||||||
import styles from './contribute.module.css'
|
import styles from './contribute.module.css'
|
||||||
import repos from '../data/repos.json'
|
import repos from '../data/repos.json'
|
||||||
|
|
||||||
export default function contribute() {
|
export default function contribute() {
|
||||||
const [newTask, setNewTask] = useState('')
|
const [showFeedback, setShowFeedback] = useState(false)
|
||||||
|
|
||||||
const submitNewTask = async () => {
|
|
||||||
if (!newTask) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(constants.apiUrl + 'postfeedback', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
newTask: newTask,
|
|
||||||
from: 'contribute',
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then((resp) => {
|
|
||||||
return resp.json()
|
|
||||||
})
|
|
||||||
.then((resp) => {
|
|
||||||
if (resp.success) {
|
|
||||||
alert('Elküldve')
|
|
||||||
setNewTask('')
|
|
||||||
} else {
|
|
||||||
alert('Hiba küldés közben')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
alert('Hiba küldés közben')
|
|
||||||
console.error(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderNewTaskArea = () => {
|
|
||||||
return (
|
|
||||||
<div className={styles.inputArea}>
|
|
||||||
<textarea
|
|
||||||
onChange={(event) => setNewTask(event.target.value)}
|
|
||||||
value={newTask || ''}
|
|
||||||
className={styles.feedback}
|
|
||||||
/>
|
|
||||||
<button className={styles.button} onClick={submitNewTask}>
|
|
||||||
Küldés
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={'endofpage'}>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Qmining - Todos | Frylabs.net</title>
|
<title>Todos - Qmining | Frylabs.net</title>
|
||||||
</Head>
|
</Head>
|
||||||
<div className={styles.description}>
|
<div className={'pageHeader'}>
|
||||||
Egy kártyára kattintva nézheted meg a részleteket, vagy szavazhatsz.
|
<h1>Teendők</h1>
|
||||||
Minél több szavazat érkezik egy kártyára, annál magasabb lesz a
|
|
||||||
pioritása. Jobb alsó szám minél több, annál nehezebb a feladat. A Done
|
|
||||||
oszlopban lévő feladatok kész vannak, de különböző okok miat még nem
|
|
||||||
lettek kiadva frissítésként. Ami az In Prod táblázatban van az van kint.
|
|
||||||
<br />
|
|
||||||
{
|
|
||||||
'Ha olyan taskot látsz amiben tudnál és szeretnél segíteni, akkor írj '
|
|
||||||
}
|
|
||||||
<a
|
|
||||||
href="http://qmining.frylabs.net/irc?contribute"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{'IRC'}
|
|
||||||
</a>
|
|
||||||
-n, és útbaigazítalak.
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.description}>
|
<Sleep />
|
||||||
Itt írhatsz új todo-ra ötleteket, vagy jelezhetsz hogy egyikben
|
<div className={'description'}>
|
||||||
segítenél
|
<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>
|
||||||
{renderNewTaskArea()}
|
<center>
|
||||||
|
<div className={`buttonContainer ${styles.newTaskButton}`}>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
setShowFeedback(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Új feladat
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
|
<br />
|
||||||
<hr />
|
<hr />
|
||||||
<Todos />
|
<Todos />
|
||||||
<Sleep />
|
|
||||||
<hr />
|
<hr />
|
||||||
<div className={styles.title}>Git repos</div>
|
<div className={'subtitle'}>
|
||||||
<hr />
|
<b>Git repos</b>
|
||||||
<hr />
|
</div>
|
||||||
<div className={styles.repos}>
|
<div className={styles.repos} style={{ float: 'left' }} id={'gitrepo'}>
|
||||||
{Object.keys(repos.repos).map((key) => {
|
{Object.keys(repos.repos).map((key) => {
|
||||||
let repo = repos.repos[key]
|
let repo = repos.repos[key]
|
||||||
return (
|
return (
|
||||||
<a key={key} href={repo.href}>
|
<div key={key}>
|
||||||
{repo.description}
|
<ul>
|
||||||
</a>
|
<li>
|
||||||
|
<a key={key} href={repo.href}>
|
||||||
|
{repo.description}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<hr />
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ textAlign: 'center' }}>
|
<div
|
||||||
|
style={{ textAlign: 'right', marginRight: '100px', marginTop: '25px' }}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
style={{ maxWidth: '100%', width: '400px' }}
|
style={{ maxWidth: '100%', width: '320px' }}
|
||||||
src={`${constants.siteUrl}img/bug.png`}
|
src={`${constants.siteUrl}img/bug.png`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<hr />
|
||||||
|
{showFeedback && (
|
||||||
|
<Modal
|
||||||
|
closeClick={() => {
|
||||||
|
setShowFeedback(false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FeedbackArea from={'contribute'} />
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,3 @@
|
||||||
.description {
|
|
||||||
font-size: 15px;
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
color: white;
|
color: white;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
@ -12,34 +5,28 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.feedback {
|
|
||||||
color: var(--text-color);
|
|
||||||
background-color: var(--background-color);
|
|
||||||
font-size: 14px;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
background-color: var(--text-color);
|
|
||||||
border: none;
|
|
||||||
padding: 5px 15px;
|
|
||||||
margin: 5px;
|
|
||||||
color: white;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputArea {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
color: #9999ff;
|
color: var(--text-color);
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.repos {
|
||||||
|
margin-top: 6%;
|
||||||
|
margin-left: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
.repos a {
|
.repos a {
|
||||||
margin: 0px 5px;
|
margin: 0px 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newTaskButton {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.imgContainer {
|
||||||
|
float: right;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 50px;
|
||||||
}
|
}
|
||||||
|
|
290
src/pages/faq.js
Normal file
290
src/pages/faq.js
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
import Sleep from '../components/sleep'
|
||||||
|
import Head from 'next/head'
|
||||||
|
|
||||||
|
function PasswordSection() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={'manualBody'}>
|
||||||
|
<p>
|
||||||
|
Ha ezt olvasod valszeg már neked is van. Azért lett bevezetve, hogy
|
||||||
|
nagyjából zárt legyen a felhasználók köre.
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>Minden felhasználónak más jelszava van.</li>
|
||||||
|
<li>
|
||||||
|
Elvileg elég csak 1 szer beírnod, és többet nem kell, de{' '}
|
||||||
|
<b>mentsd le biztos helyre a jelszót, hogy később is meglegyen</b>!
|
||||||
|
Ha többször kell megadnod, akkor az bug lesz. Ilyenkor ezt{' '}
|
||||||
|
<a
|
||||||
|
href="http://qmining.frylabs.net/feedback?man"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
jelentsd
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i>
|
||||||
|
Jelenleg nincs elfelejtett jelszó funkció, ha elfelejted, akkor az
|
||||||
|
örökre eltűnik!
|
||||||
|
</i>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Ha van jelszavad akkor <b>bizonyos határok között</b> te is{' '}
|
||||||
|
<a
|
||||||
|
href="https://qmining.frylabs.net/pwRequest?man"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
tudsz generálni
|
||||||
|
</a>{' '}
|
||||||
|
másoknak (ncore style).
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Saját jelszavadat ne oszd meg, mivel egyszerre egy helyen lehetsz
|
||||||
|
belépve, máshol automatikusan ki leszel jelentkeztetve. (meg minek,
|
||||||
|
ha tudsz adni másoknak az előző pont alapján)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Mivel senkinek sincs felhasználóneve, csak egy UserID (amit bal alul
|
||||||
|
találsz), így az egész teljesen anonim. Emiatt a jelszavakat nem
|
||||||
|
lehet megváltoztatni, hogy a szükséges komplexitás megmaradjon.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FAQSection() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={'manualBody'}>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>
|
||||||
|
Olyan helyeken fut le a script, ahol nem kellene, vagy
|
||||||
|
ideiglenesen ki akarod kapcsolni;
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
<i>
|
||||||
|
Tampermonkey bővitmény ikon -{'>'} click -{'>'} a scriptet
|
||||||
|
kapcsold ki. Csak ne felejtsd el visszakapcsolni ;)
|
||||||
|
</i>
|
||||||
|
</li>
|
||||||
|
<br />
|
||||||
|
<li>
|
||||||
|
<b>
|
||||||
|
Túl nagy a kérdést és a választ megjelenítő ablak, nem tudok a
|
||||||
|
válaszra kattintani;
|
||||||
|
</b>
|
||||||
|
<br />
|
||||||
|
<i>
|
||||||
|
A felugró ablakot ha minden jól megy akkor a szélénél fogva tudod
|
||||||
|
mozgatni, vagy egeret rajtatartva a görgővel tudod állítani az
|
||||||
|
áttetszőségét, vagy be tudod zárni jobb felül X-el, vagy egér
|
||||||
|
középső gombbal.
|
||||||
|
</i>
|
||||||
|
</li>
|
||||||
|
<br />
|
||||||
|
<li>
|
||||||
|
<b>Gombok, %-ok, számok;</b>
|
||||||
|
<br />
|
||||||
|
<img
|
||||||
|
style={{ maxWidth: '90%' }}
|
||||||
|
src="img/6.png"
|
||||||
|
alt="img"
|
||||||
|
className={'manual_img'}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function RiskSection() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b>Bármikor észrevehetik hogy használod a scriptet</b>
|
||||||
|
<br />
|
||||||
|
A weboldalt már kevésbé, de úgy nem menti el a kérdéseket a script,
|
||||||
|
mert nem fut. Később manuálisan is be lehet majd küldeni
|
||||||
|
kérdés-válaszokat.
|
||||||
|
<p />
|
||||||
|
A script shadow-root hoz teszi hozzá az összes megjelenített
|
||||||
|
elementet, így ezeket szinte lehetetlen detektálni. A moodle
|
||||||
|
semmiféleképpen nem látja, hogy milyen más oldalak vannak megnyitva a
|
||||||
|
böngésződben. Nem látja az XMLHttp requesteket se, amit a script
|
||||||
|
végez. Egy Matomo nevű script látja hogy milyen oldalarka navigálsz a
|
||||||
|
moodle-ről, de a script nem linkekkel irányít át, hanem javascript
|
||||||
|
eseménnyel, amit nem tud nyomon követni.
|
||||||
|
<p />
|
||||||
|
Aztán ki tudja ténylegesen hogy lehet
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Bármikor leállhat a szerver</b>
|
||||||
|
<br />
|
||||||
|
És akkor nem bírod megnézni a válaszokat. Erre van az{' '}
|
||||||
|
<a
|
||||||
|
href="http://qmining.frylabs.net/allqr.txt?man"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
összes kérdés TXT
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Akármelyik válasz rossz lehet</b>
|
||||||
|
<br />
|
||||||
|
Pl.: ha a script rosszul menti le, vagy rossz kérdésre ad választ
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<hr />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function WebsiteSaveSection() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
Ha hibát észlesz, 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
|
||||||
|
egy lementett weboldalt megkapok, amin a hiba történik. Így lehet
|
||||||
|
menteni egy oldalt:
|
||||||
|
</p>
|
||||||
|
<center>
|
||||||
|
<img
|
||||||
|
style={{ maxWidth: '90%' }}
|
||||||
|
src="img/websitesave.png"
|
||||||
|
alt="img"
|
||||||
|
className={'manual_img'}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<a href="/feedback" rel="noreferrer">
|
||||||
|
Ide tudod feltölteni
|
||||||
|
</a>
|
||||||
|
</center>
|
||||||
|
<p>
|
||||||
|
Mivel nincs hozzáférésem semmilyen egyetemi oldalhoz, így csak így tudom
|
||||||
|
hatékonyan tesztelni a scriptet. Ezért hatalmas segítség ha feltöltöd
|
||||||
|
azt az oldalt amin hibával találkozol.
|
||||||
|
</p>
|
||||||
|
<hr />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ScriptReinstallSection() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
Jelenleg két helyről lehet telepíteni a scriptet: greasyforkról és a
|
||||||
|
weboldalról. A greasyforkos telepítési lehetőség meg fog szűnni, így ha
|
||||||
|
onnan telepítetted, akkor nem lesznek frissítések elérhetők (amik nagyon
|
||||||
|
fontosak (de tényleg)). Ezért a következő rövid manővert kellene
|
||||||
|
végrehajtani, hogy minden zökkenőmentesen menjen:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>Böngésző bővítményeidnél kattints a tampermonkey-ra</li>
|
||||||
|
<li>Válaszd ki alulról második opciót, ami dashboard néven fut</li>
|
||||||
|
<li>
|
||||||
|
Ekkor új tabban felugranak telepített scriptjeid. Keresd meg a
|
||||||
|
Moodle/Elearning/KMOOC test help-et, és a sor végén kattints a kuka
|
||||||
|
gombra
|
||||||
|
</li>
|
||||||
|
<li>Ha megkérdezi mondd neki, hogy biztos törölni akarod</li>
|
||||||
|
<li>
|
||||||
|
Ezután simán kattints{' '}
|
||||||
|
<a
|
||||||
|
href="http://qmining.frylabs.net/install?man"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
ide
|
||||||
|
</a>{' '}
|
||||||
|
a script újratelepítéséhez a weboldalról.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Kész! Lehet megkérdezi újra, hogy elérheti-e a szervert, de azt csak
|
||||||
|
egyszer. Szokásos módon engedélyezd, hogy le bírja kérni a helyes
|
||||||
|
válaszokat
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
Ezzel semmi adat nem vész el, régi jelszó ugyanolyan jó marad (csak ne
|
||||||
|
felejtsd azt el)
|
||||||
|
<hr />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const pages = {
|
||||||
|
faq: { name: 'GYIK', component: FAQSection },
|
||||||
|
pw: { name: 'Jelszavak', component: PasswordSection },
|
||||||
|
risk: { name: 'Kockázatok', component: RiskSection },
|
||||||
|
websitedl: { name: 'Weboldal letöltése', component: WebsiteSaveSection },
|
||||||
|
reinstall: {
|
||||||
|
name: 'Script újratelepítése',
|
||||||
|
component: ScriptReinstallSection,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FAQ({ router }) {
|
||||||
|
const [currPage, setCurrPage] = useState(pages.faq)
|
||||||
|
|
||||||
|
const renderCurrPage = (page) => {
|
||||||
|
if (page) {
|
||||||
|
return <page.component />
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (router.query.tab) {
|
||||||
|
setCurrPage(pages[router.query.tab])
|
||||||
|
}
|
||||||
|
}, [router.query.tab])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Head>
|
||||||
|
<title>GYIK - Qmining | Frylabs.net</title>
|
||||||
|
</Head>
|
||||||
|
<div className={'pageHeader'}>
|
||||||
|
<h1>Gyakran Ismételt Kérdések</h1>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<Sleep />
|
||||||
|
<br />
|
||||||
|
<div className={'buttonContainer'}>
|
||||||
|
{Object.keys(pages).map((key) => {
|
||||||
|
const page = pages[key]
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`${page === currPage && 'activeButton'}`}
|
||||||
|
key={key}
|
||||||
|
onClick={() => {
|
||||||
|
setCurrPage(page)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{page.name}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
{renderCurrPage(currPage)}
|
||||||
|
|
||||||
|
<p className={'endofpage'} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,33 +1,30 @@
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import fetch from 'unfetch'
|
import fetch from 'unfetch'
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import Link from 'next/link'
|
|
||||||
|
|
||||||
import LoadingIndicator from '../components/LoadingIndicator'
|
import LoadingIndicator from '../components/LoadingIndicator'
|
||||||
import Sleep from '../components/sleep'
|
import Sleep from '../components/sleep'
|
||||||
import NewsEntry from '../components/newsEntry'
|
import NewsEntry from '../components/newsEntry'
|
||||||
import Composer from '../components/composer'
|
import Composer from '../components/composer'
|
||||||
import DbSelector from '../components/dbSelector.js'
|
|
||||||
|
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import constants from '../constants.json'
|
import constants from '../constants.json'
|
||||||
|
|
||||||
const links = {
|
const forumPostPerPage = 5
|
||||||
install: {
|
const frontpageForumName = 'frontpage'
|
||||||
href: '/install',
|
|
||||||
text: 'Install',
|
|
||||||
},
|
|
||||||
irc: {
|
|
||||||
href: '/irc',
|
|
||||||
text: 'IRC chat',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchNews() {
|
function fetchForum(from) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
fetch(`${constants.apiUrl}news.json`, {
|
fetch(
|
||||||
credentials: 'include',
|
`${
|
||||||
})
|
constants.apiUrl
|
||||||
|
}forumEntries?forumName=${frontpageForumName}&getContent=true${
|
||||||
|
from ? `&from=${from}` : ''
|
||||||
|
}&count=${forumPostPerPage}`,
|
||||||
|
{
|
||||||
|
credentials: 'include',
|
||||||
|
}
|
||||||
|
)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
return resp.json()
|
return resp.json()
|
||||||
})
|
})
|
||||||
|
@ -47,6 +44,7 @@ function addPost(title, content) {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
forumName: frontpageForumName,
|
||||||
title: title,
|
title: title,
|
||||||
content: content,
|
content: content,
|
||||||
}),
|
}),
|
||||||
|
@ -60,234 +58,226 @@ function addPost(title, content) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function postFeedback(content, file) {
|
function updateForumPost(forum, postKey, postData) {
|
||||||
const postText = (file) => {
|
return Object.keys(forum).reduce((acc, key) => {
|
||||||
return new Promise((resolve) => {
|
const entry = forum[key]
|
||||||
fetch(constants.apiUrl + 'postfeedback', {
|
if (key === postKey) {
|
||||||
method: 'POST',
|
acc = {
|
||||||
credentials: 'include',
|
...acc,
|
||||||
headers: {
|
[key]: postData,
|
||||||
Accept: 'application/json',
|
}
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
content: content,
|
|
||||||
file: file ? file.name : null,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
return res.json()
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
resolve(res)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
if (file) {
|
|
||||||
const formData = new FormData() // eslint-disable-line
|
|
||||||
formData.append('file', file)
|
|
||||||
|
|
||||||
fetch(constants.apiUrl + 'postfeedbackfile', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
},
|
|
||||||
body: formData,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
return res.json()
|
|
||||||
})
|
|
||||||
.then((fileres) => {
|
|
||||||
postText(file).then((textres) => {
|
|
||||||
resolve({ fileres, textres })
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
postText().then((res) => {
|
acc = {
|
||||||
resolve(res)
|
...acc,
|
||||||
})
|
[key]: entry,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
return acc
|
||||||
|
}, {})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Index({ globalData }) {
|
export default function Index({ globalData }) {
|
||||||
const userId = globalData.userId
|
const userId = globalData.userId
|
||||||
const motd = globalData.motd
|
const motd = globalData.motd
|
||||||
const [news, setNews] = useState(null)
|
const [news, setNews] = useState(null)
|
||||||
const [allQrSelector, setAllQrSelector] = useState(null)
|
const [nextEntryKey, setNextEntryKey] = useState()
|
||||||
// const userSpecificMotd = props.globalData.userSpecificMotd
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.info('Fetching news.json')
|
fetchForum().then((res) => {
|
||||||
fetchNews().then((res) => {
|
const { entries, nextKey } = res
|
||||||
setNews(res)
|
setNextEntryKey(nextKey)
|
||||||
|
setNews(entries)
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const renderNews = () => {
|
const renderNews = () => {
|
||||||
if (news) {
|
if (news) {
|
||||||
let newsItems = Object.keys(news)
|
let newsItems = Object.keys(news).map((postKey) => {
|
||||||
.map((key) => {
|
let newsEntryData = news[postKey]
|
||||||
let newsEntryData = news[key]
|
return (
|
||||||
return (
|
<NewsEntry
|
||||||
<NewsEntry
|
onPostDelete={() => {
|
||||||
onPostDelete={() => {
|
fetch(constants.apiUrl + 'rmPost', {
|
||||||
fetch(constants.apiUrl + 'rmPost', {
|
method: 'POST',
|
||||||
method: 'POST',
|
credentials: 'include',
|
||||||
credentials: 'include',
|
headers: {
|
||||||
headers: {
|
Accept: 'application/json',
|
||||||
Accept: 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Type': 'application/json',
|
},
|
||||||
},
|
body: JSON.stringify({
|
||||||
body: JSON.stringify({
|
forumName: frontpageForumName,
|
||||||
newsKey: key,
|
postKey: postKey,
|
||||||
}),
|
}),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.json()
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res.json()
|
const { success, msg } = res
|
||||||
})
|
if (success) {
|
||||||
.then((res) => {
|
setNews(
|
||||||
if (res.status === 'fail') {
|
Object.keys(news).reduce((acc, key) => {
|
||||||
alert(res.msg)
|
const entry = news[key]
|
||||||
} else {
|
if (key !== postKey) {
|
||||||
setNews(res.news)
|
acc = {
|
||||||
}
|
...acc,
|
||||||
})
|
[key]: entry,
|
||||||
}}
|
}
|
||||||
onNewsReact={({ reaction, isDelete }) => {
|
}
|
||||||
fetch(constants.apiUrl + 'react', {
|
return acc
|
||||||
method: 'POST',
|
}, {})
|
||||||
credentials: 'include',
|
)
|
||||||
headers: {
|
} else {
|
||||||
Accept: 'application/json',
|
alert(msg)
|
||||||
'Content-Type': 'application/json',
|
}
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
reaction: reaction,
|
|
||||||
newsKey: key,
|
|
||||||
isDelete: isDelete,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
.then((res) => {
|
}}
|
||||||
return res.json()
|
onNewsReact={({ reaction, isDelete }) => {
|
||||||
})
|
fetch(constants.apiUrl + 'react', {
|
||||||
.then((res) => {
|
method: 'POST',
|
||||||
console.log(res)
|
credentials: 'include',
|
||||||
setNews(res.news)
|
headers: {
|
||||||
})
|
Accept: 'application/json',
|
||||||
}}
|
'Content-Type': 'application/json',
|
||||||
onCommentReact={({ path, reaction, isDelete }) => {
|
},
|
||||||
fetch(constants.apiUrl + 'react', {
|
body: JSON.stringify({
|
||||||
method: 'POST',
|
reaction: reaction,
|
||||||
credentials: 'include',
|
postKey: postKey,
|
||||||
headers: {
|
isDelete: isDelete,
|
||||||
Accept: 'application/json',
|
forumName: frontpageForumName,
|
||||||
'Content-Type': 'application/json',
|
}),
|
||||||
},
|
})
|
||||||
body: JSON.stringify({
|
.then((res) => {
|
||||||
type: 'reaction',
|
return res.json()
|
||||||
newsKey: key,
|
|
||||||
path: path,
|
|
||||||
reaction: reaction,
|
|
||||||
isDelete: isDelete,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res.json()
|
setNews(res.news)
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
setNews(res.news)
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
onDelete={(path) => {
|
|
||||||
fetch(constants.apiUrl + 'comment', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
type: 'delete',
|
|
||||||
path: path,
|
|
||||||
newsKey: key,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
.then((res) => {
|
}}
|
||||||
return res.json()
|
onCommentReact={({ path, reaction, isDelete }) => {
|
||||||
})
|
fetch(constants.apiUrl + 'react', {
|
||||||
.then((res) => {
|
method: 'POST',
|
||||||
if (res.status === 'fail') {
|
credentials: 'include',
|
||||||
alert(res.msg)
|
headers: {
|
||||||
} else {
|
Accept: 'application/json',
|
||||||
setNews(res.news)
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
})
|
body: JSON.stringify({
|
||||||
}}
|
type: 'reaction',
|
||||||
onComment={(path, content) => {
|
postKey: postKey,
|
||||||
fetch(constants.apiUrl + 'comment', {
|
path: path,
|
||||||
method: 'POST',
|
reaction: reaction,
|
||||||
credentials: 'include',
|
isDelete: isDelete,
|
||||||
headers: {
|
forumName: frontpageForumName,
|
||||||
Accept: 'application/json',
|
}),
|
||||||
'Content-Type': 'application/json',
|
})
|
||||||
},
|
.then((res) => {
|
||||||
body: JSON.stringify({
|
return res.json()
|
||||||
type: 'add',
|
|
||||||
path: path,
|
|
||||||
content: content,
|
|
||||||
newsKey: key,
|
|
||||||
}),
|
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res.json()
|
const { success, postData, msg } = res
|
||||||
})
|
if (success) {
|
||||||
.then((res) => {
|
setNews(updateForumPost(news, postKey, postData))
|
||||||
setNews(res.news)
|
} else {
|
||||||
})
|
alert(msg)
|
||||||
}}
|
}
|
||||||
uid={userId}
|
})
|
||||||
key={key}
|
}}
|
||||||
newsKey={key}
|
onDelete={(path) => {
|
||||||
newsItem={newsEntryData}
|
fetch(constants.apiUrl + 'comment', {
|
||||||
/>
|
method: 'POST',
|
||||||
)
|
credentials: 'include',
|
||||||
})
|
headers: {
|
||||||
.reverse()
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
type: 'delete',
|
||||||
|
path: path,
|
||||||
|
postKey: postKey,
|
||||||
|
forumName: frontpageForumName,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.json()
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
const { success, postData, msg } = res
|
||||||
|
if (success) {
|
||||||
|
setNews(updateForumPost(news, postKey, postData))
|
||||||
|
} else {
|
||||||
|
alert(msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
onComment={(path, content) => {
|
||||||
|
fetch(constants.apiUrl + 'comment', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
type: 'add',
|
||||||
|
path: path,
|
||||||
|
content: content,
|
||||||
|
postKey: postKey,
|
||||||
|
forumName: frontpageForumName,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.json()
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
const { success, postData, msg } = res
|
||||||
|
if (success) {
|
||||||
|
setNews(updateForumPost(news, postKey, postData))
|
||||||
|
} else {
|
||||||
|
alert(msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
uid={userId}
|
||||||
|
key={postKey}
|
||||||
|
newsKey={postKey}
|
||||||
|
newsItem={newsEntryData}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.title}>Forum</div>
|
<div className={styles.title}>Fórum/Hírek</div>
|
||||||
<hr />
|
<hr />
|
||||||
<Composer
|
<Composer
|
||||||
onSubmit={(type, title, content, file) => {
|
onSubmit={(title, content) => {
|
||||||
if (!content) {
|
addPost(title, content).then((res) => {
|
||||||
alert('Üres a tartalom!')
|
const { success, newPostKey, newEntry, msg } = res
|
||||||
return
|
if (success) {
|
||||||
}
|
setNews({ [newPostKey]: newEntry, ...news })
|
||||||
console.log(type, title, content, file)
|
} else {
|
||||||
if (type === 'private') {
|
alert(msg)
|
||||||
postFeedback(content, file).then(
|
|
||||||
(/* { fileres, textres } */) => {
|
|
||||||
alert('Privát visszajelzés elküldve!')
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
if (!title) {
|
|
||||||
alert('Üres a téma!')
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
addPost(title, content).then((res) => {
|
})
|
||||||
setNews(res.news)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<hr />
|
|
||||||
<div>{newsItems}</div>
|
<div>{newsItems}</div>
|
||||||
|
{nextEntryKey && (
|
||||||
|
<div
|
||||||
|
className={styles.loadMoreButton}
|
||||||
|
onClick={() => {
|
||||||
|
fetchForum(nextEntryKey).then((res) => {
|
||||||
|
console.log(res)
|
||||||
|
const { entries, nextKey } = res
|
||||||
|
setNextEntryKey(nextKey)
|
||||||
|
setNews({ ...news, ...entries })
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Több bejegyzés betöltése
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -297,9 +287,8 @@ export default function Index({ globalData }) {
|
||||||
|
|
||||||
const renderMotd = () => {
|
const renderMotd = () => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={styles.motd_body}>
|
||||||
<div className={styles.title}>MOTD</div>
|
<div className={styles.title}>MOTD</div>
|
||||||
<hr />
|
|
||||||
{motd ? (
|
{motd ? (
|
||||||
<div
|
<div
|
||||||
className={styles.motd}
|
className={styles.motd}
|
||||||
|
@ -312,94 +301,15 @@ export default function Index({ globalData }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// const renderUserSpecificMotd = () => {
|
|
||||||
// return (
|
|
||||||
// <div>
|
|
||||||
// <hr />
|
|
||||||
// <div className={styles.subtitle}>Üzenet admintól:</div>
|
|
||||||
// {userSpecificMotd ? (
|
|
||||||
// <div
|
|
||||||
// className={styles.motd}
|
|
||||||
// dangerouslySetInnerHTML={{ __html: userSpecificMotd.msg }}
|
|
||||||
// />
|
|
||||||
// ) : (
|
|
||||||
// <div>...</div>
|
|
||||||
// )}
|
|
||||||
// </div>
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
const renderDbSelector = () => {
|
|
||||||
if (allQrSelector) {
|
|
||||||
return (
|
|
||||||
<DbSelector
|
|
||||||
text={`Válaszd ki melyik adatbázist szeretnéd letölteni (${allQrSelector}):`}
|
|
||||||
showAll={allQrSelector === 'txt'}
|
|
||||||
closeClick={() => {
|
|
||||||
setAllQrSelector(null)
|
|
||||||
}}
|
|
||||||
onDbSelect={(selectedDb) => {
|
|
||||||
if (allQrSelector === 'txt') {
|
|
||||||
if (selectedDb === 'all') {
|
|
||||||
window.open(`${constants.apiUrl}allqr.txt`, '_blank')
|
|
||||||
} else {
|
|
||||||
window.open(
|
|
||||||
`${constants.apiUrl}allqr.txt?db=${selectedDb.name}`,
|
|
||||||
'_blank'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else if (allQrSelector === 'json') {
|
|
||||||
window.open(`${constants.apiUrl}${selectedDb.path}`, '_blank')
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Qmining | Frylabs.net</title>
|
<title>Qmining | Frylabs.net</title>
|
||||||
</Head>
|
</Head>
|
||||||
<div className={styles.buttonContainer}>
|
|
||||||
{Object.keys(links).map((key) => {
|
|
||||||
let link = links[key]
|
|
||||||
return (
|
|
||||||
<Link key={key} href={link.href}>
|
|
||||||
<a className={styles.button} target="_blank">
|
|
||||||
{link.text}
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<a
|
|
||||||
onClick={() => {
|
|
||||||
setAllQrSelector('txt')
|
|
||||||
}}
|
|
||||||
className={styles.button}
|
|
||||||
>
|
|
||||||
{'Összes kérdés TXT'}
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
onClick={() => {
|
|
||||||
setAllQrSelector('json')
|
|
||||||
}}
|
|
||||||
className={styles.button}
|
|
||||||
>
|
|
||||||
{'Összes kérdés JSON'}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
{renderMotd()}
|
{renderMotd()}
|
||||||
{/*{userSpecificMotd && renderUserSpecificMotd()} */}
|
{/*{userSpecificMotd && renderUserSpecificMotd()} */}
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<Sleep />
|
<Sleep />
|
||||||
{renderNews()}
|
{renderNews()}
|
||||||
{renderDbSelector()}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,5 @@
|
||||||
.buttonContainer {
|
.hr {
|
||||||
display: flex;
|
width: 100%;
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
color: white;
|
|
||||||
background-color: #303030;
|
|
||||||
margin: 2px;
|
|
||||||
padding: 10px 5px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
word-wrap: break-word;
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button:hover {
|
|
||||||
background-color: #555;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.motd {
|
.motd {
|
||||||
|
@ -30,20 +8,54 @@
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.itemContainer {
|
||||||
|
width: 100%;
|
||||||
|
margin: 20px 5px;
|
||||||
|
background-color: var(--hoover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.newsBody {
|
||||||
|
margin: 0px 5px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
font-size: 17px;
|
||||||
|
color: #fff;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
.motd_body {
|
||||||
|
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;
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
color: #9999ff;
|
color: var(--text-color);
|
||||||
font-size: 30px;
|
font-size: 32px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
letter-spacing: 2.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
color: #9999ff;
|
color: var(--text-color);
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.newsTitle {
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 28px;
|
||||||
|
padding-left: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
.question {
|
.question {
|
||||||
font-weight: 'bold';
|
font-weight: bold;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
margin: 0px 5px;
|
margin: 0px 5px;
|
||||||
|
@ -53,7 +65,32 @@
|
||||||
margin: 0px 5px;
|
margin: 0px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.itemNumber {
|
||||||
|
color: #a7a7a7;
|
||||||
|
margin: 0px 5px;
|
||||||
|
font-size: 22px;
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-left: 13px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
.repos {
|
.repos {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loadMoreButton {
|
||||||
|
text-align: center;
|
||||||
|
background-color: var(--dark-color);
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadMoreButton:hover {
|
||||||
|
background-color: var(--hoover-color);
|
||||||
|
}
|
||||||
|
|
|
@ -1,320 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
|
|
||||||
import Sleep from '../components/sleep'
|
|
||||||
import Head from 'next/head'
|
|
||||||
|
|
||||||
export default function Manual() {
|
|
||||||
return <div>{renderMaual()}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderMaual() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Head>
|
|
||||||
<title>Manual - Qmining | Frylabs.net</title>
|
|
||||||
</Head>
|
|
||||||
<center>
|
|
||||||
<h1>Manual</h1>
|
|
||||||
</center>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<Sleep />
|
|
||||||
<center>
|
|
||||||
Ez a userscript Moodle/Elearnig/KMOOC tesztek megoldása során segítséget
|
|
||||||
jelenít meg.
|
|
||||||
<h2>
|
|
||||||
Ha az oldalt vagy a scriptet használod: akármikor észrevehetik,
|
|
||||||
leállhat a szerver, és rossz lehet az összes válasz
|
|
||||||
</h2>
|
|
||||||
Valószínűleg semmi baj nem lesz, de én szóltam. Ha ez iránt aggódsz,
|
|
||||||
olvasd el a kockázatok részt
|
|
||||||
</center>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<center>
|
|
||||||
<h1>Userscript használata</h1>
|
|
||||||
</center>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<div className={'manualUsage'}>
|
|
||||||
<div>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Tölts le egy userscript futtató kiegészítőt a böngésződhöz:{' '}
|
|
||||||
<a
|
|
||||||
href="https://www.tampermonkey.net/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Tampermonkey
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
href="http://qmining.frylabs.net/install?man"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Weboldalról
|
|
||||||
</a>{' '}
|
|
||||||
rakd fel a scriptet
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Script majd udvariasan megkéri, hogy hagy beszélgessen a
|
|
||||||
szerverrel, mert mással nem tud, ezt engedélyezd.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Ezután:
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Teszt oldalon a kérdésre a választ kell látnod felül egy
|
|
||||||
felugró ablakszerűben
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Teszt ellenőrzés oldalon a script beküldi a szervernek a
|
|
||||||
helyes válaszokat, az lementi az új kérdéseket, amik ezután
|
|
||||||
azonnal elérhetők lesznek
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
Egyéb fontos tudnivalók:
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Ezt ments sokszor akár minden nap:{' '}
|
|
||||||
<a
|
|
||||||
href="http://qmining.frylabs.net/allqr.txt?man"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
Összes kérdés TXT
|
|
||||||
</a>{' '}
|
|
||||||
(ha elszállna a szerver)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
href="https://qmining.frylabs.net/allQuestions.html"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Összes kérdés oldal
|
|
||||||
</a>
|
|
||||||
, ahol manuál tudsz keresni ha valami gáz lenne a scriptel
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p /> Egyéb funkciók:
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
Ha esetleg videókat nézel, akkor spaceval lehet play/pausolni, és
|
|
||||||
jobbra/balra gombbal ugrani a videóban.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div className={'rtfmImage'}>
|
|
||||||
<img
|
|
||||||
style={{ maxWidth: '100%', minWidth: '200px' }}
|
|
||||||
src="img/rtfm.jpg"
|
|
||||||
alt="img"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<center>
|
|
||||||
<h1>Jelszavak</h1>
|
|
||||||
</center>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
Ha ezt olvasod valszeg már van neked egy. Azért lett bevezetve, hogy
|
|
||||||
nagyjából zárt legyen a felhasználók köre
|
|
||||||
<ul>
|
|
||||||
<li>Minden felhasználónak más jelszava van</li>
|
|
||||||
<li>
|
|
||||||
Elvileg elég csak 1 szer beírnod, és nem kell többet, de{' '}
|
|
||||||
<b>mentsd le biztos helyre a jelszót, hogy később is meglegyen!</b> Ha
|
|
||||||
többször kell akkor az bug,{' '}
|
|
||||||
<a
|
|
||||||
href="http://qmining.frylabs.net/feedback?man"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
és szólj
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b>
|
|
||||||
Jelenleg nincs elfelejtett jelszó funkció, ha elfelejted akkor az
|
|
||||||
örökre eltűnik!
|
|
||||||
</b>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Ha van jelszavad akkor bizonyos határok között{' '}
|
|
||||||
<a
|
|
||||||
href="https://qmining.frylabs.net/pwRequest?man"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
te is tudsz generálni másoknak
|
|
||||||
</a>
|
|
||||||
(ncore style).
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Saját jelszavad ne oszd meg, belépésnél máshonnan azonnal ki leszel
|
|
||||||
jelentkeztetve, és minek ha tudsz adni amúgy is
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Mivel felhasználóneved nincs, így teljesen anoním az egész. Ez miatt
|
|
||||||
jelszót nem lehet megváltoztatni, hogy a szükséges komplexitás
|
|
||||||
megmaradjon
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<center>
|
|
||||||
<h1>Gyakran előforduló kérdések</h1>
|
|
||||||
</center>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<b>
|
|
||||||
Olyan helyeken fut le a script, ahol nem kellene, vagy ideiglenesen
|
|
||||||
ki akarod kapcsolni
|
|
||||||
</b>
|
|
||||||
<br /> Tampermonkey bővitmény ikon -{'>'} click -{'>'} scriptet
|
|
||||||
kapcsold ki. Csak ne felejtsd visszakapcsolni ;)
|
|
||||||
</li>
|
|
||||||
<p />
|
|
||||||
<li>
|
|
||||||
<b>
|
|
||||||
Túl nagy a kérdést és a választ megjelenítő ablak, nem tudok a
|
|
||||||
válaszra kattintani
|
|
||||||
</b>
|
|
||||||
<br /> Zommolj ki egy kicsit, vagy kapcsold ki addig a scriptet.
|
|
||||||
Továbbá középső egérgombra kattintva rá el bírod tüntetni az ablakot,
|
|
||||||
amíg újra nem töltöd az oldalt, vagy másikra ugrasz.
|
|
||||||
</li>
|
|
||||||
<p />
|
|
||||||
<li>
|
|
||||||
<b>Gombok, %-ok, számok</b>
|
|
||||||
<br />
|
|
||||||
<img style={{ maxWidth: '100%' }} src="img/6.png" alt="img" />
|
|
||||||
</li>
|
|
||||||
<p />
|
|
||||||
</ul>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<center>
|
|
||||||
<h1>Kockázatok</h1>
|
|
||||||
</center>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<b>Bármikor észrevehetik hogy használod a scriptet</b>
|
|
||||||
<br />
|
|
||||||
A weboldalt már kevésbé, de úgy nem menti el a kérdéseket a script,
|
|
||||||
mert nem fut. Később manuálisan is be lehet majd küldeni
|
|
||||||
kérdés-válaszokat.
|
|
||||||
<p />
|
|
||||||
Ha arra nem veszik a fáradságot, hogy a kérdéseket lecseréljék akkor
|
|
||||||
valószínűleg arra se hogy userscript futását detektáló kódot rakjanak
|
|
||||||
a weboldalra. A{' '}
|
|
||||||
<a href="https://moodle.org/" target="_blank" rel="noreferrer">
|
|
||||||
Moodle
|
|
||||||
</a>{' '}
|
|
||||||
egy nyílt forráskódú, valószínűleg self-hosted rendszer. Valószínűleg
|
|
||||||
az egyetem egy ezer éves debian szerverén fut, amihez senki se mer
|
|
||||||
nyúlni, nemhogy a moodle-t frissítse valaki.
|
|
||||||
<p />
|
|
||||||
A script shadow-root hoz teszi hozzá az összes megjelenített
|
|
||||||
elementet, így ezeket szinte lehetetlen detektálni. A moodle
|
|
||||||
semmiféleképpen nem látja, hogy milyen más oldalak vannak megnyitva a
|
|
||||||
böngésződben. Nem látja az XMLHttp requesteket se, amit a script
|
|
||||||
végez. Egy Matomo nevű script látja hogy milyen oldalarka navigálsz a
|
|
||||||
moodle-ről, de a script nem linkekkel irányít át, hanem javascript
|
|
||||||
eseménnyel, amit nem tud nyomon követni.
|
|
||||||
<p />
|
|
||||||
Aztán ki tudja ténylegesen hogy lehet
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b>Bármikor leállhat a szerver</b>
|
|
||||||
<br />
|
|
||||||
És akkor nem bírod megnézni a válaszokat. Erre van az{' '}
|
|
||||||
<a
|
|
||||||
href="http://qmining.frylabs.net/allqr.txt?man"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
{' '}
|
|
||||||
összes kérdés TXT
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b>Akármelyik válasz rossz lehet</b>
|
|
||||||
<br />
|
|
||||||
Pl.: ha a script rosszul menti le, vagy rossz kérdésre ad választ
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<center>
|
|
||||||
<h1>Egyéb</h1>
|
|
||||||
</center>
|
|
||||||
<hr />
|
|
||||||
<hr />
|
|
||||||
<h2 id="sitesave">Weboldal lementése</h2>
|
|
||||||
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 egy lementett weboldalt megkapok. Így lehet
|
|
||||||
letölteni egy oldalt:
|
|
||||||
<img style={{ maxWidth: '100%' }} src="img/websitesave.png" alt="img" />
|
|
||||||
<br />
|
|
||||||
<a
|
|
||||||
href="http://qmining.frylabs.net/feedback?man"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Ide tudod feltölteni
|
|
||||||
</a>
|
|
||||||
<br />
|
|
||||||
Mivel nincs hozzáférésem semmilyen egyetemi oldalhoz, így csak így tudom
|
|
||||||
hatékonyan tesztelni a scriptet. Ezért hatalmas segítség ha feltöltöd azt
|
|
||||||
az oldalt amin hibával találkozol.
|
|
||||||
<hr />
|
|
||||||
<h2 id="scriptreinstall">Script újratelepítése</h2>
|
|
||||||
Jelenleg két helyről lehet telepíteni a scriptet: greasyforkról és a
|
|
||||||
weboldalról. A greasyforkos telepítési lehetőség meg fog szűnni, így ha
|
|
||||||
onnan telepítetted, akkor nem lesznek frissítések elérhetők (amik nagyon
|
|
||||||
fontosak (de tényleg)). Ezért a következő rövid manővert kellene
|
|
||||||
végrehajtani, hogy minden zökkenőmentesen menjen:
|
|
||||||
<ul>
|
|
||||||
<li>Böngésző bővítményeidnél kattints a tampermonkey-ra</li>
|
|
||||||
<li>Válaszd ki alulról második opciót, ami dashboard néven fut</li>
|
|
||||||
<li>
|
|
||||||
Ekkor új tabban felugranak telepített scriptjeid. Keresd meg a
|
|
||||||
Moodle/Elearning/KMOOC test help-et, és a sor végén kattints a kuka
|
|
||||||
gombra
|
|
||||||
</li>
|
|
||||||
<li>Ha megkérdezi mondd neki, hogy biztos törölni akarod</li>
|
|
||||||
<li>
|
|
||||||
Ezután simán kattints{' '}
|
|
||||||
<a
|
|
||||||
href="http://qmining.frylabs.net/install?man"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
ide a script újratelepítéséhez a weboldalról.
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
Kész! Lehet megkérdezi újra, hogy elérheti-e a szervert, de azt csak
|
|
||||||
egyszer. Szokásos módon engedélyezd, hogy le bírja kérni a helyes
|
|
||||||
válaszokat
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
Ezzel semmi adat nem vész el, régi jelszó ugyanolyan jó marad (csak ne
|
|
||||||
felejtsd azt el)
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,45 +1,29 @@
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect } from 'react'
|
||||||
import fetch from 'unfetch'
|
import fetch from 'unfetch'
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import Sleep from '../components/sleep'
|
||||||
|
|
||||||
import styles from './pwRequest.module.css'
|
import styles from './pwRequest.module.css'
|
||||||
import constants from '../constants.json'
|
import constants from '../constants.json'
|
||||||
|
|
||||||
export default function PwRequest(props) {
|
function fetchAvailablePWS() {
|
||||||
const [result, setResult] = useState([])
|
return new Promise((resolve) => {
|
||||||
const [remaining, setRemaining] = useState('...')
|
|
||||||
const [requestedPWS, setRequestedPWS] = useState('...')
|
|
||||||
const [createDate, setCreateDate] = useState('...')
|
|
||||||
|
|
||||||
const [addPwPerDay, setAddPwPerDay] = useState('...')
|
|
||||||
const [daysAfterUserGetsPWs, setDaysAfterUserGetsPWs] = useState('...')
|
|
||||||
const [maxPWCount, setMaxPWCount] = useState('...')
|
|
||||||
const [addPWCount, setAddPWCount] = useState('...')
|
|
||||||
const [dayDiff, setDayDiff] = useState('...')
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.info('Fetching avaible pws')
|
|
||||||
fetch(`${constants.apiUrl}avaiblePWS`, {
|
fetch(`${constants.apiUrl}avaiblePWS`, {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
return resp.json()
|
return resp.json()
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((res) => {
|
||||||
setRemaining(data.avaiblePWS)
|
resolve(res)
|
||||||
setCreateDate(data.userCreated)
|
|
||||||
setRequestedPWS(data.requestedPWS)
|
|
||||||
|
|
||||||
setAddPwPerDay(data.addPWPerDay)
|
|
||||||
setAddPWCount(data.addPWCount)
|
|
||||||
setDaysAfterUserGetsPWs(data.daysAfterUserGetsPWs)
|
|
||||||
setMaxPWCount(data.maxPWCount)
|
|
||||||
setDayDiff(data.dayDiff)
|
|
||||||
})
|
})
|
||||||
}, [])
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
function requestPw() {
|
||||||
const rawResponse = await fetch(constants.apiUrl + 'getpw', {
|
return new Promise((resolve) => {
|
||||||
|
fetch(constants.apiUrl + 'getpw', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -48,85 +32,135 @@ export default function PwRequest(props) {
|
||||||
},
|
},
|
||||||
body: JSON.stringify({}),
|
body: JSON.stringify({}),
|
||||||
})
|
})
|
||||||
try {
|
.then((res) => {
|
||||||
rawResponse
|
return res.json()
|
||||||
.json()
|
})
|
||||||
.then((resp) => {
|
.then((res) => {
|
||||||
if (resp.result === 'success') {
|
resolve(res)
|
||||||
setResult([...result, resp.pw])
|
})
|
||||||
setRemaining(resp.remaining)
|
})
|
||||||
setRequestedPWS(resp.requestedPWS)
|
}
|
||||||
} else if (resp.result === 'success') {
|
|
||||||
setResult(['Nem vagy bejelentkezve!']) // this should never happpen
|
export default function PwRequest({ globalData }) {
|
||||||
} else {
|
const userId = globalData.userId || '...'
|
||||||
setResult([
|
const [result, setResult] = useState([])
|
||||||
...result,
|
const [data, setData] = useState({
|
||||||
'Jelszó kérési lehetőségeid elfogytak, nézz vissza később',
|
userCreated: '...',
|
||||||
])
|
availablePWS: '...',
|
||||||
setRemaining(0)
|
requestedPWS: '...',
|
||||||
}
|
maxPWCount: '...',
|
||||||
})
|
daysAfterUserGetsPWs: '...',
|
||||||
.catch((e) => {
|
addPWPerDay: '...',
|
||||||
setResult([...result, 'Szerver oldali hiba!'])
|
addPWCount: '...',
|
||||||
console.error(e)
|
dayDiff: '...',
|
||||||
})
|
userCount: '...',
|
||||||
} catch (e) {
|
})
|
||||||
setResult([...result, 'Szerver oldali hiba!'])
|
|
||||||
console.error(e)
|
const {
|
||||||
}
|
userCreated,
|
||||||
}
|
availablePWS,
|
||||||
|
requestedPWS,
|
||||||
|
maxPWCount,
|
||||||
|
daysAfterUserGetsPWs,
|
||||||
|
addPWPerDay,
|
||||||
|
addPWCount,
|
||||||
|
dayDiff,
|
||||||
|
userCount,
|
||||||
|
} = data
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchAvailablePWS().then((data) => {
|
||||||
|
setData(data)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// TODO: újrafogalmazás, remove hány nap után kapnak új jelszót először
|
|
||||||
<div>
|
<div>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Jelszó kérés - Qmining | Frylabs.net</title>
|
<title>Jelszó generálás - Qmining | Frylabs.net</title>
|
||||||
</Head>
|
</Head>
|
||||||
<div id="form">
|
<div className={'pageHeader'}>
|
||||||
<div className={styles.text}>
|
<h1>Jelszó generálás</h1>
|
||||||
Itt új jelszavakat tudsz kérni új felhasználóknak. Közös
|
|
||||||
jelszóhasználat nem ajánlott, mert ha valaki belép azzal a jelszóval
|
|
||||||
amit te használsz akkor téged kiléptet mindenhonnan. Szerintem van
|
|
||||||
elég jelszó hogy ne kelljen közös
|
|
||||||
</div>
|
|
||||||
<div className={styles.text}>
|
|
||||||
Új felhasználóknak
|
|
||||||
<span>{' ' + daysAfterUserGetsPWs + ' '}</span>
|
|
||||||
napot kell várni, míg kapnak lehetőséget jelszó generálásra.
|
|
||||||
</div>
|
|
||||||
<div className={styles.text}>
|
|
||||||
<span>{' ' + addPwPerDay + ' '}</span>
|
|
||||||
naponta
|
|
||||||
<span>{' ' + addPWCount + ' '}</span>
|
|
||||||
új lehetőség van jelszót generálni, maximum
|
|
||||||
<span>{' ' + maxPWCount + ' '}</span>
|
|
||||||
lehetőség gyűlhet össze
|
|
||||||
</div>
|
|
||||||
<div className={styles.text}>
|
|
||||||
Még kérhető jelszavak:
|
|
||||||
<span>{' ' + remaining}</span>. Felhasználó létrehozva:
|
|
||||||
<span>{' ' + createDate}</span>,<span>{' ' + dayDiff + ' '}</span>
|
|
||||||
napja. Eddig
|
|
||||||
<span>{' ' + requestedPWS + ' '}</span>
|
|
||||||
jelszót kértél.
|
|
||||||
</div>
|
|
||||||
<div className={styles.buttonContainer}>
|
|
||||||
<div onClick={handleSubmit} className={styles.button}>
|
|
||||||
Jelszó kérése
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{result ? (
|
|
||||||
<div className={styles.pwContainer}>
|
|
||||||
{result.map((r, i) => {
|
|
||||||
return (
|
|
||||||
<div key={i} className={styles.pw}>
|
|
||||||
{i + 1}.: {r}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
|
<center>
|
||||||
|
<Sleep />
|
||||||
|
<div id="form">
|
||||||
|
<div className={styles.text}>
|
||||||
|
<p className={styles.descrip}>
|
||||||
|
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">
|
||||||
|
<a>GYIK</a>
|
||||||
|
</Link>{' '}
|
||||||
|
vonatkozó részében olvashatsz.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className={styles.text}>
|
||||||
|
Minden <span>{' ' + daysAfterUserGetsPWs + ' '}</span> napnál
|
||||||
|
régebbi felhasználó generálhat magának{' '}
|
||||||
|
<span>{' ' + maxPWCount + 'db '}</span> jelszót. Miután lekértél{' '}
|
||||||
|
<span>{' ' + maxPWCount + 'db'}</span>-ot, a lehetőségeid nem
|
||||||
|
fogytak el, ismét lesz módod további{' '}
|
||||||
|
<span>{' ' + maxPWCount + 'db'}</span>-ot kérni, összességében
|
||||||
|
korlátlan mennyiségben. Az egyszerre rendelkezésre álló lehetőségeid
|
||||||
|
számát lentebb látod, ez maximum egyszerre{' '}
|
||||||
|
<span>{' ' + maxPWCount + 'db '}</span> lehet. Ezt követően{' '}
|
||||||
|
<span>{' ' + addPWPerDay + ' '}</span>
|
||||||
|
naponta kapsz további <span>{' ' + addPWCount + ' '}</span> új
|
||||||
|
lehetőséget generálásra, amíg a lehetőségeid száma ismét el nem éri
|
||||||
|
a <span>{' ' + maxPWCount + 'db'}</span>-ot. Eddig
|
||||||
|
<span>{' ' + requestedPWS + ' '}</span>
|
||||||
|
jelszót kértél le, vagyis jelen pillanatban
|
||||||
|
<span>{' ' + (availablePWS - requestedPWS) + 'db '}</span>
|
||||||
|
jelszót generálhatsz még.
|
||||||
|
<br />
|
||||||
|
<br />A jelenleg bejelentkezett felhasználó{' '}
|
||||||
|
<span>{' ' + dayDiff + ' '}</span>
|
||||||
|
napos,{' '}
|
||||||
|
<span>
|
||||||
|
{userCreated ? new Date(userCreated).toLocaleString() : '...'}
|
||||||
|
</span>
|
||||||
|
-kor lett létrehozva.
|
||||||
|
<br />
|
||||||
|
Az oldalnak jelenleg
|
||||||
|
<span>{' ' + userCount + ' '}</span>
|
||||||
|
felhasználója van.
|
||||||
|
</div>
|
||||||
|
<div className={`buttonContainer ${styles.pwButton}`}>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
requestPw().then((res) => {
|
||||||
|
setData(res)
|
||||||
|
if (res.success) {
|
||||||
|
setResult([...result, res.pw])
|
||||||
|
} else {
|
||||||
|
setResult([
|
||||||
|
...result,
|
||||||
|
'Jelszó kérési lehetőségeid elfogytak, nézz vissza később',
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Jelszó kérése
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{result ? (
|
||||||
|
<div className={styles.pwContainer}>
|
||||||
|
{result.map((res, i) => {
|
||||||
|
return (
|
||||||
|
<div key={i} className={styles.pw}>
|
||||||
|
{i + 1}.: {res}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</center>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,23 @@
|
||||||
.buttonContainer {
|
|
||||||
padding: 10px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: center;
|
|
||||||
background-color: var(--text-color);
|
|
||||||
border: none;
|
|
||||||
padding: 10px 30px;
|
|
||||||
color: white;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
text-align: center;
|
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.descrip {
|
||||||
|
color: #acabab;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-top: 4%;
|
||||||
|
padding-bottom: 4%;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.pw {
|
.pw {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pwContainer {
|
.pwContainer {
|
||||||
font-family: "Courier New", Courier, monospace;
|
font-family: 'Courier New', Courier, monospace;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: white;
|
color: white;
|
||||||
|
@ -42,3 +32,7 @@
|
||||||
.text span {
|
.text span {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pwButton {
|
||||||
|
width: 20% !important;
|
||||||
|
}
|
||||||
|
|
|
@ -85,20 +85,22 @@ function sortDataBy(data, key) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RankList() {
|
export default function RankList({ globalData }) {
|
||||||
|
const userId = globalData.userId
|
||||||
const [ranklist, setRanklist] = useState(null)
|
const [ranklist, setRanklist] = useState(null)
|
||||||
const [selfUserId, setSelfUserId] = useState('...')
|
const [selfUserId, setSelfUserId] = useState('...')
|
||||||
const [key, setKey] = useState('newQuestions')
|
const [key, setKey] = useState('newQuestions')
|
||||||
const [since, setSince] = useState('all')
|
const [since, setSince] = useState('all')
|
||||||
const [sum, setSum] = useState({})
|
const [sum, setSum] = useState()
|
||||||
|
const [ownEntryOnTop, setOwnEntryOnTop] = useState(false)
|
||||||
|
|
||||||
const getList = () => {
|
const getList = () => {
|
||||||
setSum({})
|
setSum()
|
||||||
setRanklist(null)
|
setRanklist(null)
|
||||||
getListFromServer(since)
|
getListFromServer(since)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setRanklist(data.list || [])
|
setRanklist(data.list || [])
|
||||||
setSum(data.sum || {})
|
setSum(data.sum)
|
||||||
if (data.selfuserId) {
|
if (data.selfuserId) {
|
||||||
setSelfUserId(data.selfuserId)
|
setSelfUserId(data.selfuserId)
|
||||||
}
|
}
|
||||||
|
@ -112,11 +114,22 @@ export default function RankList() {
|
||||||
getList()
|
getList()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(
|
||||||
getList()
|
() => {
|
||||||
}, [since])
|
getList()
|
||||||
|
},
|
||||||
|
[since]
|
||||||
|
)
|
||||||
|
|
||||||
const list = ranklist && sortDataBy(ranklist, key)
|
const list =
|
||||||
|
ranklist &&
|
||||||
|
sortDataBy(ranklist, key).reduce((acc, entry, i) => {
|
||||||
|
if (entry.userId === userId && ownEntryOnTop) {
|
||||||
|
return [{ rank: i, ...entry }, ...acc]
|
||||||
|
} else {
|
||||||
|
return [...acc, { rank: i, ...entry }]
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
const updateSince = (keyword) => {
|
const updateSince = (keyword) => {
|
||||||
setSince(keyword)
|
setSince(keyword)
|
||||||
|
@ -127,82 +140,119 @@ export default function RankList() {
|
||||||
<Head>
|
<Head>
|
||||||
<title>Ranklista - Qmining | Frylabs.net</title>
|
<title>Ranklista - Qmining | Frylabs.net</title>
|
||||||
</Head>
|
</Head>
|
||||||
<div>
|
<div className={styles.container}>
|
||||||
<div
|
<div>
|
||||||
className={styles.infoText}
|
<center>
|
||||||
>{`A felhasználó ID-d: #${selfUserId}`}</div>
|
<div className={'pageHeader'}>
|
||||||
<div className={styles.text}>
|
<h1>Ranklista</h1>
|
||||||
Az adatok kb 2020. április eleje óta vannak gyűjtve, és azonnal
|
</div>
|
||||||
frissülnek.
|
</center>
|
||||||
</div>
|
<div
|
||||||
<Sleep />
|
className={styles.infoText}
|
||||||
<div className={styles.sinceTable}>
|
>{`A felhasználó ID-d: #${selfUserId}`}</div>
|
||||||
<div className={styles.sinceHeaderRow}>
|
<div className={styles.text}>
|
||||||
{Object.keys(sinceOptions).map((key) => {
|
<p>
|
||||||
const val = sinceOptions[key]
|
Az adatok kb 2020. április eleje óta vannak gyűjtve, és azonnal
|
||||||
return (
|
frissülnek.
|
||||||
<div
|
</p>
|
||||||
key={key}
|
</div>
|
||||||
className={`${styles.clickable} ${key === since &&
|
<Sleep />
|
||||||
styles.selected}`}
|
<div className={'selectContainer'}>
|
||||||
onClick={() => {
|
<div>Megjelenítés: </div>
|
||||||
updateSince(key)
|
<select
|
||||||
}}
|
onChange={(e) => {
|
||||||
>
|
updateSince(e.target.value)
|
||||||
{val.name}
|
}}
|
||||||
</div>
|
>
|
||||||
)
|
{Object.keys(sinceOptions).map((key) => {
|
||||||
})}
|
const val = sinceOptions[key]
|
||||||
|
return (
|
||||||
|
<option key={key} value={key}>
|
||||||
|
{val.name}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className={'selectContainer'}>
|
||||||
|
<div>Rendezés: </div>
|
||||||
|
<select
|
||||||
|
value={key}
|
||||||
|
onChange={(e) => {
|
||||||
|
setKey(e.target.value)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Object.keys(selectOptions).map((key) => {
|
||||||
|
const val = selectOptions[key]
|
||||||
|
return (
|
||||||
|
<option key={key} value={key}>
|
||||||
|
{val.name}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className={'checkbContainer'}>
|
||||||
|
<div>Saját hely mutatása felül:</div>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={ownEntryOnTop}
|
||||||
|
onChange={(e) => {
|
||||||
|
setOwnEntryOnTop(e.target.checked)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.table}>
|
{sum && list ? (
|
||||||
<div className={styles.headerRow}>
|
<>
|
||||||
<div>{'Rank'}</div>
|
<div className={`${styles.sumRow}`}>
|
||||||
<div>{'Felhasználó ID'}</div>
|
<div />
|
||||||
{Object.keys(selectOptions).map((listKey) => {
|
<div>
|
||||||
const val = selectOptions[listKey]
|
<b>{'Összesen: '}</b>
|
||||||
return (
|
</div>
|
||||||
<div
|
<div>{sum.newQuestions.toLocaleString('hu')}</div>
|
||||||
className={`${styles.clickable} ${listKey === key &&
|
<div>{sum.allQuestions.toLocaleString('hu')}</div>
|
||||||
styles.selected}`}
|
<div>{sum.count.toLocaleString('hu')}</div>
|
||||||
key={listKey}
|
</div>
|
||||||
onClick={() => {
|
<div className={styles.headerRow}>
|
||||||
setKey(listKey)
|
<div>
|
||||||
}}
|
<b>{'Rank'}</b>
|
||||||
>
|
</div>
|
||||||
{val.name}
|
<div>{'Felhasználó ID'}</div>
|
||||||
</div>
|
{Object.keys(selectOptions).map((listKey) => {
|
||||||
)
|
const val = selectOptions[listKey]
|
||||||
})}
|
return (
|
||||||
</div>
|
<div
|
||||||
<div className={`${styles.row} ${styles.sumrow}`}>
|
className={`${listKey === key && styles.selected}`}
|
||||||
<div />
|
key={listKey}
|
||||||
<div>{'Összesen'}</div>
|
>
|
||||||
<div>{sum.newQuestions}</div>
|
{val.name}
|
||||||
<div>{sum.allQuestions}</div>
|
</div>
|
||||||
<div>{sum.count}</div>
|
)
|
||||||
</div>
|
})}
|
||||||
{list ? (
|
</div>
|
||||||
list.map((listItem, i) => {
|
<div className={styles.table}>
|
||||||
return (
|
{list.map((listItem, i) => {
|
||||||
<div
|
return (
|
||||||
className={`${styles.row} ${listItem.userId === selfUserId &&
|
<div
|
||||||
styles.selfRow}`}
|
className={`${styles.row} ${listItem.userId ===
|
||||||
key={i}
|
selfUserId && styles.selfRow}`}
|
||||||
>
|
key={i}
|
||||||
<div>{i + 1}</div>
|
>
|
||||||
<div>{'#' + listItem.userId}</div>
|
<div>{listItem.rank + 1}</div>
|
||||||
{Object.keys(selectOptions).map((listKey) => {
|
<div>{'#' + listItem.userId}</div>
|
||||||
const val = listItem[listKey]
|
{Object.keys(selectOptions).map((listKey) => {
|
||||||
return <div key={listKey}>{val}</div>
|
const val = listItem[listKey]
|
||||||
})}
|
return <div key={listKey}>{val}</div>
|
||||||
</div>
|
})}
|
||||||
)
|
</div>
|
||||||
})
|
)
|
||||||
) : (
|
})}
|
||||||
<LoadingIndicator />
|
</div>
|
||||||
)}
|
</>
|
||||||
</div>
|
) : (
|
||||||
|
<LoadingIndicator />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,47 +1,72 @@
|
||||||
.table,
|
.container {
|
||||||
.sinceTable {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-flow: column;
|
||||||
padding: 0px 10px;
|
height: calc(98vh);
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table :nth-child(1),
|
.container:first-child {
|
||||||
.sinceTable :nth-child(1) {
|
flex: 0 1 auto;
|
||||||
color: white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.row,
|
.table {
|
||||||
.headerRow,
|
flex: 1 1 auto;
|
||||||
.sinceHeaderRow {
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background-color: #1b1b1c;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sumRow {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
display: flex;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-right: 16px;
|
||||||
|
margin-top: 30px;
|
||||||
|
background-color: var(--hoover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerRow {
|
||||||
|
font-size: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
|
padding-right: 15px;
|
||||||
|
background-color: var(--hoover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
padding: 1.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row:hover {
|
.row:hover {
|
||||||
background-color: #333333;
|
background-color: #242323;
|
||||||
|
cursor: default;
|
||||||
|
text-shadow: 1px 1px 6px #969696;
|
||||||
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.row div,
|
.row div,
|
||||||
.headerRow div,
|
.headerRow div, .sumRow div {
|
||||||
.sinceHeaderRow div {
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0px 5px;
|
padding: 0px 5px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headerRow div,
|
.headerRow > div, .sumRow > div {
|
||||||
.sinceHeaderRow div {
|
padding: 5px;
|
||||||
padding: 15px 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.row :nth-child(1),
|
.row :nth-child(1),
|
||||||
.headerRow :nth-child(1) {
|
.headerRow :nth-child(1), .sumRow :nth-child(1) {
|
||||||
flex: 0 30px;
|
flex: 0 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row :nth-child(2),
|
.row :nth-child(2),
|
||||||
.headerRow :nth-child(2) {
|
.headerRow :nth-child(2), .sumRow :nth-child(2) {
|
||||||
flex: 0 100px;
|
flex: 0 130px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,26 +79,27 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.selfRow {
|
.selfRow {
|
||||||
background-color: #9999ff;
|
background-color: var(--text-color);
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selfRow:hover {
|
.selfRow:hover {
|
||||||
background-color: #9999ee;
|
background-color: #96810b;
|
||||||
|
color: gainsboro;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
font-style: italic;
|
||||||
|
color: var(--text-color);
|
||||||
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.infoText {
|
.infoText {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
padding-top: 20px;
|
||||||
|
|
||||||
.selected {
|
|
||||||
background-color: #9999ff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sumrow {
|
.sumrow {
|
||||||
|
|
113
src/pages/script.js
Normal file
113
src/pages/script.js
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import Sleep from '../components/sleep'
|
||||||
|
import Head from 'next/head'
|
||||||
|
|
||||||
|
import ExternalLinkIcon from '../components/externalLinkIcon'
|
||||||
|
|
||||||
|
export default function Script() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Head>
|
||||||
|
<title>Script - Qmining | Frylabs.net</title>
|
||||||
|
</Head>
|
||||||
|
<div className={'pageHeader'}>
|
||||||
|
<h1>Script</h1>
|
||||||
|
</div>
|
||||||
|
<div className={'buttonContainer'}>
|
||||||
|
<a href="https://www.tampermonkey.net/">
|
||||||
|
Ajánlott userscript kezelő bővítmény
|
||||||
|
<ExternalLinkIcon size={15} />
|
||||||
|
</a>
|
||||||
|
<a href="/install">
|
||||||
|
Script telepítése
|
||||||
|
<ExternalLinkIcon size={15} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<Sleep />
|
||||||
|
{renderManual()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderManual() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<center>
|
||||||
|
<h2 className={'subtitle'}>A userscript telepítése, és használata</h2>
|
||||||
|
</center>
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Ez a userscript Moodle/Elearnig/KMOOC tesztek megoldása során
|
||||||
|
segítséget jelenít meg.
|
||||||
|
</p>
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
Tölts le egy userscript futtató kiegészítőt a böngésződhöz: pl. a{' '}
|
||||||
|
<a
|
||||||
|
href="https://www.tampermonkey.net/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Tampermonkey
|
||||||
|
</a>
|
||||||
|
-t.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="http://qmining.frylabs.net/install"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
Kattints ide hogy felrakd a scriptet
|
||||||
|
</a>{' '}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
A script ezt követően udvariasan megkér, hogy hadd beszélgessen a
|
||||||
|
szerverrel, ezt engedélyezd neki. (Always allow domain)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
A támogatott oldalakon a script egy apró menü ablakot jelenít meg a
|
||||||
|
weboldal bal alsó részén
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Ezután a kitöltendő teszt oldalán a kérdésre a választ kell látnod
|
||||||
|
felül egy lebegő ablakban.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Teszt ellenőrzés oldalon a script beküldi a szervernek a helyes
|
||||||
|
válaszokat, az lementi az új kérdéseket, amik ezután azonnal
|
||||||
|
elérhetők lesznek (neked, és másoknak is)
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
Egyéb fontos tudnivalók:
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Ezt ments sokszor akár minden nap:{' '}
|
||||||
|
<a
|
||||||
|
href="http://qmining.frylabs.net/allqr.txt"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
Összes kérdés TXT
|
||||||
|
</a>{' '}
|
||||||
|
(az összes összegyűjtött kérdés, ha elszállna a szerver)
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Az{' '}
|
||||||
|
<a href="/allQuestions" rel="noreferrer">
|
||||||
|
összes kérdés oldal
|
||||||
|
</a>{' '}
|
||||||
|
az oldal, ahol manuálisan tudsz keresni, ha valami gáz lenne a
|
||||||
|
scripttel.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div>
|
||||||
|
Ha útközben elakadsz, vagy hibát észlelsz, akkor oldalt a Kapcsolat
|
||||||
|
résznél sok elérhetőséget találsz, amin segítséget kérhetsz.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
0
src/pages/script.module.css
Normal file
0
src/pages/script.module.css
Normal file
|
@ -3,13 +3,13 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Quicksand&display=swap" rel="stylesheet">
|
<title>Thank you! - Qmining | Frylabs.net</title>
|
||||||
<title>Thank you!</title>
|
|
||||||
<style>
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Kameron&family=Overpass+Mono:wght@300;400&display=swap');
|
||||||
:root{
|
:root{
|
||||||
--size: 100px;
|
--size: 100px;
|
||||||
--bgcolor: #222426;
|
--bgcolor: #222426;
|
||||||
--color: #fcff4f;
|
--color: #f2f2f2;
|
||||||
--shadow: rgba(30,2,5,.2);
|
--shadow: rgba(30,2,5,.2);
|
||||||
}
|
}
|
||||||
.surface {
|
.surface {
|
||||||
|
@ -51,21 +51,28 @@
|
||||||
|
|
||||||
|
|
||||||
html,body{
|
html,body{
|
||||||
height:100vh;
|
height: 95vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
|
font-family: 'Kameron', serif;
|
||||||
|
font-family: 'Overpass Mono', monospace;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: var(--bgcolor);
|
background-color: var(--bgcolor);
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
#qminer {
|
||||||
|
color: #f2cb05;
|
||||||
|
|
||||||
|
}
|
||||||
#text{
|
#text{
|
||||||
font-family: 'Quicksand', sans-serif;
|
font-weight: 100;
|
||||||
color: white;
|
font-size: 25px;
|
||||||
font-size: 24px;
|
color: #f2f2f2;
|
||||||
text-shadow: 1px 1px 2px rgba(0,0,0,0.6);
|
text-shadow: 1px 1px 2px black;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -74,16 +81,19 @@
|
||||||
}
|
}
|
||||||
#backbutton{
|
#backbutton{
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: white;
|
color: azure;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin: 10px;
|
margin: 25px;
|
||||||
background-color: #4bb9bd;
|
background-color: #f2cb05;
|
||||||
box-shadow: 0px 1px 3px rgba(0,0,0,0.6);
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 3px 10px;
|
padding: 3px 10px;
|
||||||
|
padding-top: 4px;
|
||||||
|
transition: width 0.5s, height 0.5s, ease-in 0.5s;
|
||||||
|
text-shadow: 1px 1px 2px black;
|
||||||
}
|
}
|
||||||
#backbutton:hover{
|
#backbutton:hover{
|
||||||
box-shadow: 0px 1px 2px rgba(0,0,0,0.6);
|
transition: width 0.5s, height 0.5s, ease-out 0.5s;
|
||||||
|
background-color: #96810b;
|
||||||
}
|
}
|
||||||
.coincontainer {
|
.coincontainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -103,12 +113,11 @@
|
||||||
<div class="shadow"></div>
|
<div class="shadow"></div>
|
||||||
</div></div>
|
</div></div>
|
||||||
<center>
|
<center>
|
||||||
<div id="text"><span>Thanks for the gold, kind question miner!</span><a id="backbutton" href="<%= siteurl %>">Return</a></div>
|
<div id="text"><span>Thanks for the gold, kind <span id="qminer"><b>question miner</b></span>!</span><a id="backbutton" href="<%= siteurl %>">Return</a></div>
|
||||||
</center>
|
</center>
|
||||||
<div class="coincontainer"><div class="surface">
|
<div class="coincontainer"><div class="surface">
|
||||||
<div class="coin"></div>
|
<div class="coin"></div>
|
||||||
<div class="shadow"></div>
|
<div class="shadow"></div>
|
||||||
</div></div>
|
</div></div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import styles from './thanks.module.css'
|
||||||
import constants from '../constants.json'
|
import constants from '../constants.json'
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
|
|
||||||
export default function Thanks () {
|
export default function Thanks() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Head>
|
<Head>
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
import React, { useState } from 'react'
|
|
||||||
import Head from 'next/head'
|
|
||||||
|
|
||||||
import Sleep from '../components/sleep'
|
|
||||||
import Todos from '../components/todoStuff/todos'
|
|
||||||
|
|
||||||
import constants from '../constants.json'
|
|
||||||
import styles from './todos.module.css'
|
|
||||||
|
|
||||||
export default function contribute() {
|
|
||||||
const [newTask, setNewTask] = useState('')
|
|
||||||
|
|
||||||
const submitNewTask = async () => {
|
|
||||||
if (!newTask) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(constants.apiUrl + 'postfeedback', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
newTask: newTask,
|
|
||||||
from: 'contribute',
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then((resp) => {
|
|
||||||
return resp.json()
|
|
||||||
})
|
|
||||||
.then((resp) => {
|
|
||||||
if (resp.success) {
|
|
||||||
alert('Elküldve')
|
|
||||||
setNewTask('')
|
|
||||||
} else {
|
|
||||||
alert('Hiba küldés közben')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
alert('Hiba küldés közben')
|
|
||||||
console.error(err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderNewTaskArea = () => {
|
|
||||||
return (
|
|
||||||
<div className={styles.inputArea}>
|
|
||||||
<textarea
|
|
||||||
onChange={(event) => setNewTask(event.target.value)}
|
|
||||||
value={newTask || ''}
|
|
||||||
className={styles.feedback}
|
|
||||||
/>
|
|
||||||
<button className={styles.button} onClick={submitNewTask}>
|
|
||||||
Küldés
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Head>
|
|
||||||
<title>Todos - Qmining | Frylabs.net</title>
|
|
||||||
</Head>
|
|
||||||
<div className={styles.description}>
|
|
||||||
Egy kártyára kattintva nézheted meg a részleteket, vagy szavazhatsz.
|
|
||||||
Minél több szavazat érkezik egy kártyára, annál magasabb lesz a
|
|
||||||
pioritása. Jobb alsó szám minél több, annál nehezebb a feladat. A Done
|
|
||||||
oszlopban lévő feladatok kész vannak, de különböző okok miat még nem
|
|
||||||
lettek kiadva frissítésként. Ami az In Prod táblázatban van az van kint.
|
|
||||||
</div>
|
|
||||||
<Todos />
|
|
||||||
<div className={styles.description}>
|
|
||||||
Itt írhatsz új todo-ra ötleteket, vagy jelezhetsz hogy egyikben
|
|
||||||
segítenél
|
|
||||||
</div>
|
|
||||||
{renderNewTaskArea()}
|
|
||||||
<Sleep />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
.description {
|
|
||||||
font-size: 15px;
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.warning {
|
|
||||||
color: white;
|
|
||||||
padding: 10px;
|
|
||||||
font-size: 26px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feedback {
|
|
||||||
color: var(--text-color);
|
|
||||||
background-color: var(--background-color);
|
|
||||||
font-size: 14px;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
background-color: var(--text-color);
|
|
||||||
border: none;
|
|
||||||
padding: 5px 15px;
|
|
||||||
margin: 5px;
|
|
||||||
color: white;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inputArea {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
color: #9999ff;
|
|
||||||
font-size: 30px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
330
src/pages/userFiles.js
Normal file
330
src/pages/userFiles.js
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
import Head from 'next/head'
|
||||||
|
|
||||||
|
import LoadingIndicator from '../components/LoadingIndicator'
|
||||||
|
import Modal from '../components/modal'
|
||||||
|
|
||||||
|
import styles from './userFiles.module.css'
|
||||||
|
import constants from '../constants.json'
|
||||||
|
|
||||||
|
function listUserDir(subdir) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
fetch(
|
||||||
|
`${constants.apiUrl}listUserDir${subdir ? `?subdir=${subdir}` : ''}`,
|
||||||
|
{
|
||||||
|
credentials: 'include',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((resp) => {
|
||||||
|
return resp.json()
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
resolve(res)
|
||||||
|
} else {
|
||||||
|
alert(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function FileUploader({ onChange }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>Fájl csatolása</div>
|
||||||
|
<input type="file" name="file" onChange={onChange} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteFile(currDir, name) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
fetch(constants.apiUrl + 'deleteUserFile', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
dir: currDir,
|
||||||
|
fname: name,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.json()
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
resolve(res)
|
||||||
|
} else {
|
||||||
|
alert(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function newSubj(name) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
fetch(constants.apiUrl + 'newUserDir', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: name,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.json()
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.success) {
|
||||||
|
resolve(res)
|
||||||
|
} else {
|
||||||
|
alert(res.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadFile(dir, file) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const formData = new FormData() // eslint-disable-line
|
||||||
|
formData.append('file', file)
|
||||||
|
formData.append('dir', dir)
|
||||||
|
|
||||||
|
fetch(constants.apiUrl + 'uploadUserFile', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
},
|
||||||
|
body: formData,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.json()
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
resolve(res)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UserFiles({ router, globalData }) {
|
||||||
|
const userId = globalData.userId
|
||||||
|
const [dirs, setDirs] = useState()
|
||||||
|
const [sortBy, setSortBy] = useState('name')
|
||||||
|
const [sortDirection, setSortDirection] = useState(true)
|
||||||
|
const [addingNew, setAddingNew] = useState()
|
||||||
|
const [newSubjName, setNewSubjName] = useState()
|
||||||
|
const [file, setFile] = useState()
|
||||||
|
|
||||||
|
const currDir = router.query.dir ? decodeURIComponent(router.query.dir) : ''
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const dir = router.query.dir ? decodeURIComponent(router.query.dir) : ''
|
||||||
|
setDirs(null)
|
||||||
|
|
||||||
|
if (router.query.dir) {
|
||||||
|
listUserDir(dir).then((res) => {
|
||||||
|
setDirs(res.files)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
listUserDir().then((res) => {
|
||||||
|
setDirs(res.dirs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [router.query.dir])
|
||||||
|
|
||||||
|
const dirSorter = (a, b) => {
|
||||||
|
if (a[sortBy] < b[sortBy]) {
|
||||||
|
return sortDirection ? -1 : 1
|
||||||
|
} else if (a[sortBy] > b[sortBy]) {
|
||||||
|
return sortDirection ? 1 : -1
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderDirList = (dirs) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{currDir && (
|
||||||
|
<div className={`buttonContainer ${styles.backButton}`}>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
router.back()
|
||||||
|
// FIXME: consistend going back with browser back button
|
||||||
|
// back button works like broser back button, unless it would result in site leave
|
||||||
|
// history: nothing > opened site/usrFiles?dir=...
|
||||||
|
// history: site > site/userFiles > site/usrFiles?dir=...
|
||||||
|
router.push(`${router.pathname}`, undefined, { shallow: true })
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Vissza
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={`${styles.tableContainer} ${styles.header}`}>
|
||||||
|
<div
|
||||||
|
onClick={(e) => {
|
||||||
|
const name = e.target.getAttribute('name')
|
||||||
|
if (name) {
|
||||||
|
if (sortBy === name) {
|
||||||
|
setSortDirection(!sortDirection)
|
||||||
|
} else {
|
||||||
|
setSortDirection(true)
|
||||||
|
}
|
||||||
|
setSortBy(name)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div name="name">Fájl név</div>
|
||||||
|
<div name="date">Feltöltés dátuma</div>
|
||||||
|
<div name="size">Méret</div>
|
||||||
|
<div name="user">Feltöltő user</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={`${styles.tableContainer} ${styles.rows}`}>
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
setAddingNew(currDir ? 'file' : 'dir')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>{currDir ? 'Új fájl feltöltése...' : 'Új tárgy...'}</div>
|
||||||
|
</div>
|
||||||
|
{dirs.length !== 0 && (
|
||||||
|
<>
|
||||||
|
{dirs.sort(dirSorter).map((dir) => {
|
||||||
|
const { name, date, path, size, user } = dir
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
title={name}
|
||||||
|
key={name}
|
||||||
|
onClick={() => {
|
||||||
|
if (path) {
|
||||||
|
window.open(`${constants.apiUrl}${path}`, '_blank')
|
||||||
|
} else {
|
||||||
|
router.push(
|
||||||
|
`${router.pathname}?dir=${encodeURIComponent(name)}`,
|
||||||
|
undefined,
|
||||||
|
{ shallow: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>{name}</div>
|
||||||
|
<div>{new Date(date).toDateString()}</div>
|
||||||
|
<div>
|
||||||
|
{currDir
|
||||||
|
? (size / 1000000).toFixed(2).toString() + ' MB'
|
||||||
|
: size + ' fájl'}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{user &&
|
||||||
|
user !== -1 &&
|
||||||
|
(userId === user ? (
|
||||||
|
<div
|
||||||
|
className={styles.deleteButton}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
if (confirm(`Biztos törlöd '${name}'-t ?`)) {
|
||||||
|
deleteFile(currDir, name).then(() => {
|
||||||
|
listUserDir(currDir).then((res) => {
|
||||||
|
setDirs(res.files)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Törlés
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
`#${user}`
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Head>
|
||||||
|
<title>Study Docs - Qmining | Frylabs.net</title>
|
||||||
|
</Head>
|
||||||
|
<div className="pageHeader">
|
||||||
|
<h1>Study Docs</h1>
|
||||||
|
</div>
|
||||||
|
<div className={styles.description}>
|
||||||
|
Ide tárgyanként lehet feltölteni
|
||||||
|
</div>
|
||||||
|
{dirs ? renderDirList(dirs) : <LoadingIndicator />}
|
||||||
|
{addingNew && (
|
||||||
|
<Modal
|
||||||
|
closeClick={() => {
|
||||||
|
setAddingNew(null)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className={styles.uploadContainer}>
|
||||||
|
{addingNew === 'file' ? (
|
||||||
|
<>
|
||||||
|
<FileUploader
|
||||||
|
onChange={(e) => {
|
||||||
|
setFile(e.target.files[0])
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="buttonContainer"
|
||||||
|
onClick={() => {
|
||||||
|
uploadFile(currDir, file).then(() => {
|
||||||
|
listUserDir(currDir).then((res) => {
|
||||||
|
setDirs(res.files)
|
||||||
|
setAddingNew(null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>Feltöltés</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div>Új tárgy neve</div>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
onChange={(e) => {
|
||||||
|
setNewSubjName(e.target.value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="buttonContainer"
|
||||||
|
onClick={() => {
|
||||||
|
newSubj(newSubjName).then(() => {
|
||||||
|
listUserDir().then((res) => {
|
||||||
|
setDirs(res.dirs)
|
||||||
|
setAddingNew(null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>OK</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
78
src/pages/userFiles.module.css
Normal file
78
src/pages/userFiles.module.css
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
.tableContainer {
|
||||||
|
user-select: none;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tableContainer > div {
|
||||||
|
padding: 15px 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tableContainer > div > div {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tableContainer > div > div:nth-child(1) {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tableContainer > div > div:nth-child(2) {
|
||||||
|
flex: 0 240px;
|
||||||
|
}
|
||||||
|
.tableContainer > div > div:nth-child(3) {
|
||||||
|
flex: 0 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tableContainer > div > div:nth-child(4) {
|
||||||
|
flex: 0 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rows > div {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rows > div:nth-child(2n + 1) {
|
||||||
|
background-color: var(--dark-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rows > div:hover {
|
||||||
|
background-color: var(--hoover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header > div > div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header > div > div:hover {
|
||||||
|
background-color: var(--hoover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadContainer > div {
|
||||||
|
padding: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteButton {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2px;
|
||||||
|
border: 1px solid var(--hoover-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteButton:hover {
|
||||||
|
background-color: var(--dark-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.backButton {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
padding: 5px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue