added dynamic domain

This commit is contained in:
mrfry 2023-04-08 11:03:23 +02:00
parent 39dfd7a0f4
commit 5a665bc766
10 changed files with 1537 additions and 1455 deletions

View file

@ -1,50 +1,36 @@
module.exports = {
env: {
browser: true,
es2021: true,
env: {
browser: true,
es2021: true,
},
parser: '@babel/eslint-parser',
parserOptions: {
sourceType: 'module',
},
settings: {
react: {
version: 'detect',
},
parser: '@babel/eslint-parser',
parserOptions: {
sourceType: 'module',
},
settings: {
react: {
version: 'detect',
},
},
extends: [
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:@next/next/recommended',
},
plugins: ['react'],
extends: [
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:@next/next/recommended',
],
rules: {
'react/prop-types': 0,
'no-undef': ['error'],
eqeqeq: ['warn', 'smart'],
'react/jsx-uses-vars': 'error',
'react/jsx-uses-react': 'error',
'@next/next/no-img-element': 'off',
'no-unused-vars': 'warn',
'no-prototype-builtins': 'off',
'id-length': [
'warn',
{ exceptions: ['x', 'i', 'j', 't', 'Q', 'A', 'C', 'q', 'a', 'b', 'e'] },
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
},
rules: {
'react/prop-types': 0,
'no-undef': ['error'],
eqeqeq: ['warn', 'smart'],
'no-unused-vars': 'warn',
'no-prototype-builtins': 'off',
'id-length': [
'warn',
{
exceptions: [
'x',
'i',
'j',
't',
'Q',
'A',
'C',
'q',
'a',
'b',
'e',
],
},
],
},
root: true,
},
root: true,
}

View file

@ -2,86 +2,95 @@ import React from 'react'
import Questions from './Questions.js'
import constants from '../constants.json'
import constants from '../constants.js'
const countReducer = (acc, subj) => {
return acc + subj.Questions.length
return acc + subj.Questions.length
}
export default function QuestionSearchResult(props) {
const { data, searchTerm, edits, onChange } = props
const { data, searchTerm, edits, onChange } = props
let subjs = []
let results = -1
let subjs = []
let results = -1
if (searchTerm || edits.length > 0) {
subjs = data.reduce((acc, subj) => {
const resultQuestions = subj.Questions.reduce((qacc, question, i) => {
const unsaved = edits.some((e) => {
if (e.subjName === subj.Name && e.index === i && e.type === 'edit') {
return true
}
})
if (unsaved) {
qacc.push({
q: question,
unsaved: unsaved,
})
return qacc
}
if (searchTerm || edits.length > 0) {
subjs = data.reduce((acc, subj) => {
const resultQuestions = subj.Questions.reduce(
(qacc, question, i) => {
const unsaved = edits.some((e) => {
if (
e.subjName === subj.Name &&
e.index === i &&
e.type === 'edit'
) {
return true
}
})
if (unsaved) {
qacc.push({
q: question,
unsaved: unsaved,
})
return qacc
}
const keys = ['Q', 'A', 'data']
keys.some((key) => {
if (typeof question[key] !== 'string') {
return false
}
if (
searchTerm &&
question[key] &&
question[key].toLowerCase().includes(searchTerm.toLowerCase())
) {
qacc.push({ q: question })
return true
}
})
return qacc
}, [])
if (resultQuestions.length > 0) {
acc.push({
Name: subj.Name,
Questions: resultQuestions,
ind: subj.ind,
})
}
return acc
}, [])
results = subjs.reduce(countReducer, 0)
} else {
results = data.reduce(countReducer, 0)
}
const keys = ['Q', 'A', 'data']
keys.some((key) => {
if (typeof question[key] !== 'string') {
return false
}
if (
searchTerm &&
question[key] &&
question[key]
.toLowerCase()
.includes(searchTerm.toLowerCase())
) {
qacc.push({ q: question })
return true
}
})
return qacc
},
[]
)
if (resultQuestions.length > 0) {
acc.push({
Name: subj.Name,
Questions: resultQuestions,
ind: subj.ind,
})
}
return acc
}, [])
results = subjs.reduce(countReducer, 0)
} else {
results = data.reduce(countReducer, 0)
}
console.log(subjs)
console.log(subjs)
const renderCount = () => {
return (
<div>
{searchTerm ? '' : 'Kezdj el írni kereséshez!'} {results}{' '}
{searchTerm ? 'találat' : 'kérdés'}{' '}
{searchTerm ? subjs.length : data.length} tárgy
</div>
)
}
const renderCount = () => {
return (
<div>
{searchTerm ? '' : 'Kezdj el írni kereséshez!'} {results}{' '}
{searchTerm ? 'találat' : 'kérdés'}{' '}
{searchTerm ? subjs.length : data.length} tárgy
</div>
)
}
if (results > constants.maxQuestionsToRender) {
return renderCount()
} else {
return (
<div>
<div>{renderCount()}</div>
<div>
<Questions subjs={subjs} onChange={onChange} />
</div>
</div>
)
}
if (results > constants.maxQuestionsToRender) {
return renderCount()
} else {
return (
<div>
<div>{renderCount()}</div>
<div>
<Questions subjs={subjs} onChange={onChange} />
</div>
</div>
)
}
}

View file

@ -4,257 +4,283 @@ import LoadingIndicator from '../components/LoadingIndicator'
import SearchBar from '../components/searchBar'
import TestView from '../components/testView'
import constants from '../constants.json'
import constants from '../constants.js'
import styles from './possibleAnswers.module.css'
const Infos = () => {
return (
<div className={styles.infoContainer}>
<div>
Itt azok a tesztek találhatók, amiknek a kitöltése után nem volt
ellenőrző oldal. A script így nem tudja, hogy melyik a helyes megoldás.
De ha ti igen, akkor jelöljétek be / írjátok be, és mentsétek el. Így
mikor legközelebb találkoztok a kérdéssel a script tudni fogja a helyes
választ. Ezzel másoknak is nagyon sokat segítetek.
</div>
<div>
Jelenleg azok a tesztek is megjelennek itt, amiknek elérhető a teszt eredmény oldala, és meg is
van hozzá a helyes válasz. A jövőben ezek ki lesznek automatikusan törölve.
</div>
</div>
)
return (
<div className={styles.infoContainer}>
<div>
Itt azok a tesztek találhatók, amiknek a kitöltése után nem volt
ellenőrző oldal. A script így nem tudja, hogy melyik a helyes
megoldás. De ha ti igen, akkor jelöljétek be / írjátok be, és
mentsétek el. Így mikor legközelebb találkoztok a kérdéssel a
script tudni fogja a helyes választ. Ezzel másoknak is nagyon
sokat segítetek.
</div>
<div>
Jelenleg azok a tesztek is megjelennek itt, amiknek elérhető a
teszt eredmény oldala, és meg is van hozzá a helyes válasz. A
jövőben ezek ki lesznek automatikusan törölve.
</div>
</div>
)
}
const fetchPossibleAnswers = () => {
return new Promise((resolve) => {
fetch(`${constants.apiUrl}possibleAnswers`, {
credentials: 'include',
return new Promise((resolve) => {
fetch(`${constants.apiUrl}possibleAnswers`, {
credentials: 'include',
})
.then((resp) => {
return resp.json()
})
.then((res) => {
resolve(res)
})
})
.then((resp) => {
return resp.json()
})
.then((res) => {
resolve(res)
})
})
}
const fetchSubject = (subj, savedQuestionsFileName) => {
return new Promise((resolve) => {
fetch(
`${constants.apiUrl}savedQuestions/${subj}/${savedQuestionsFileName}`,
{
credentials: 'include',
}
)
.then((resp) => {
return resp.json()
})
.then((resp) => {
resolve(resp)
})
})
return new Promise((resolve) => {
fetch(
`${constants.apiUrl}savedQuestions/${subj}/${savedQuestionsFileName}`,
{
credentials: 'include',
}
)
.then((resp) => {
return resp.json()
})
.then((resp) => {
resolve(resp)
})
})
}
const fetchTest = (subj, test) => {
return new Promise((resolve) => {
fetch(`${constants.apiUrl}savedQuestions/${subj}/${test}`, {
credentials: 'include',
return new Promise((resolve) => {
fetch(`${constants.apiUrl}savedQuestions/${subj}/${test}`, {
credentials: 'include',
})
.then((resp) => {
return resp.json()
})
.then((resp) => {
resolve(resp)
})
})
.then((resp) => {
return resp.json()
})
.then((resp) => {
resolve(resp)
})
})
}
export default function PossibleAnswers({ router, refetchDbs }) {
const [currSubjName, setCurrSubjName] = useState(null)
const [currTestName, setCurrTestName] = useState(null)
const [currSubjName, setCurrSubjName] = useState(null)
const [currTestName, setCurrTestName] = useState(null)
const [subjects, setSubjects] = useState([])
const [currSubj, setCurrSubj] = useState(null)
const [currTest, setCurrTest] = useState(null)
const [subjects, setSubjects] = useState([])
const [currSubj, setCurrSubj] = useState(null)
const [currTest, setCurrTest] = useState(null)
const [savedQuestionsFileName, setSavedQuestionsFileName] = useState(null)
const [searchTerm, setSearchTerm] = useState('')
const [savedQuestionsFileName, setSavedQuestionsFileName] = useState(null)
const [searchTerm, setSearchTerm] = useState('')
useEffect(() => {
fetchPossibleAnswers().then((resp) => {
setSubjects(resp.subjects)
setSavedQuestionsFileName(resp.savedQuestionsFileName)
useEffect(() => {
fetchPossibleAnswers().then((resp) => {
setSubjects(resp.subjects)
setSavedQuestionsFileName(resp.savedQuestionsFileName)
const subj = router.query.subj
? decodeURIComponent(router.query.subj)
: ''
const test = router.query.test
? decodeURIComponent(router.query.test)
: ''
const subj = router.query.subj
? decodeURIComponent(router.query.subj)
: ''
const test = router.query.test
? decodeURIComponent(router.query.test)
: ''
if (subj) {
fetchSubject(subj, resp.savedQuestionsFileName).then((resp) => {
setCurrSubj(resp)
setCurrSubjName(subj)
router.push(
`${router.pathname}?v=pa&subj=${encodeURIComponent(subj)}`,
undefined,
{ shallow: true }
)
if (subj && test) {
fetchTest(subj, test).then((resp) => {
setCurrTest(resp)
setCurrTestName(test)
router.push(
`${router.pathname}?v=pa&subj=${encodeURIComponent(
subj
)}&test=${encodeURIComponent(test)}`,
undefined,
{ shallow: true }
)
})
}
})
}
})
}, [])
const renderStuff = () => {
if (subjects && currSubj && currTest) {
return (
<>
<div
className={styles.backButton}
onClick={() => {
setCurrTest(null)
router.back()
}}
>
Vissza
</div>
<div>
{currTest && (
<TestView
subjName={currSubjName}
testName={currTestName}
test={currTest}
router={router}
onDelete={() => {
refetchDbs()
setCurrTest(null)
fetchSubject(currSubjName, savedQuestionsFileName).then(
(resp) => {
setCurrSubj(resp)
}
)
}}
/>
)}
</div>
</>
)
} else if (subjects && currSubj) {
return (
<>
<div className={styles.headerContainer}>
<div
className={styles.backButton}
onClick={() => {
setCurrSubj(null)
router.back()
}}
>
Vissza
</div>
<div className={styles.subjName}>{currSubjName}</div>
</div>
<div className={styles.tableContainer}>
<>
<div>
<div>Dátum</div>
<div>Felhasználó ID</div>
<div>Tárgy</div>
<div>Teszt URL</div>
</div>
{currSubj &&
currSubj.map((test, i) => {
return (
<div
className={styles.testContainer}
key={i}
onClick={() => {
setCurrTestName(test.fname)
router.push(
`${router.pathname}?v=pa&subj=${encodeURIComponent(
currSubjName
)}&test=${encodeURIComponent(test.fname)}`,
undefined,
{ shallow: true }
)
fetchTest(currSubjName, test.fname).then((resp) => {
setCurrTest(resp)
})
}}
>
<div>{new Date(test.date).toLocaleString()}</div>
<div>{test.userid}</div>
<div>{test.subj}</div>
<div>{test.testUrl}</div>
</div>
)
})}
</>
</div>
</>
)
} else if (subjects) {
return (
<>
<Infos />
<SearchBar value={searchTerm} onChange={(e) => setSearchTerm(e)} />
<div className={styles.tableContainer}>
<>
<div>Tárgy neve</div>
{subjects.map((subj, i) => {
if (
!subj.name.toLowerCase().includes(searchTerm.toLowerCase())
) {
return null
}
return (
<div
key={i}
onClick={() => {
setCurrSubjName(subj.name)
router.push(
if (subj) {
fetchSubject(subj, resp.savedQuestionsFileName).then((resp) => {
setCurrSubj(resp)
setCurrSubjName(subj)
router.push(
`${router.pathname}?v=pa&subj=${encodeURIComponent(
subj.name
subj
)}`,
undefined,
{ shallow: true }
)
)
if (subj && test) {
fetchTest(subj, test).then((resp) => {
setCurrTest(resp)
setCurrTestName(test)
router.push(
`${
router.pathname
}?v=pa&subj=${encodeURIComponent(
subj
)}&test=${encodeURIComponent(test)}`,
undefined,
{ shallow: true }
)
})
}
})
}
})
}, [])
fetchSubject(subj.name, savedQuestionsFileName).then(
(resp) => {
setCurrSubj(resp)
}
)
}}
>
{subj.name}
</div>
)
})}
</>
</div>
</>
)
} else {
return <LoadingIndicator />
const renderStuff = () => {
if (subjects && currSubj && currTest) {
return (
<>
<div
className={styles.backButton}
onClick={() => {
setCurrTest(null)
router.back()
}}
>
Vissza
</div>
<div>
{currTest && (
<TestView
subjName={currSubjName}
testName={currTestName}
test={currTest}
router={router}
onDelete={() => {
refetchDbs()
setCurrTest(null)
fetchSubject(
currSubjName,
savedQuestionsFileName
).then((resp) => {
setCurrSubj(resp)
})
}}
/>
)}
</div>
</>
)
} else if (subjects && currSubj) {
return (
<>
<div className={styles.headerContainer}>
<div
className={styles.backButton}
onClick={() => {
setCurrSubj(null)
router.back()
}}
>
Vissza
</div>
<div className={styles.subjName}>{currSubjName}</div>
</div>
<div className={styles.tableContainer}>
<>
<div>
<div>Dátum</div>
<div>Felhasználó ID</div>
<div>Tárgy</div>
<div>Teszt URL</div>
</div>
{currSubj &&
currSubj.map((test, i) => {
return (
<div
className={styles.testContainer}
key={i}
onClick={() => {
setCurrTestName(test.fname)
router.push(
`${
router.pathname
}?v=pa&subj=${encodeURIComponent(
currSubjName
)}&test=${encodeURIComponent(
test.fname
)}`,
undefined,
{ shallow: true }
)
fetchTest(
currSubjName,
test.fname
).then((resp) => {
setCurrTest(resp)
})
}}
>
<div>
{new Date(
test.date
).toLocaleString()}
</div>
<div>{test.userid}</div>
<div>{test.subj}</div>
<div>{test.testUrl}</div>
</div>
)
})}
</>
</div>
</>
)
} else if (subjects) {
return (
<>
<Infos />
<SearchBar
value={searchTerm}
onChange={(e) => setSearchTerm(e)}
/>
<div className={styles.tableContainer}>
<>
<div>Tárgy neve</div>
{subjects.map((subj, i) => {
if (
!subj.name
.toLowerCase()
.includes(searchTerm.toLowerCase())
) {
return null
}
return (
<div
key={i}
onClick={() => {
setCurrSubjName(subj.name)
router.push(
`${
router.pathname
}?v=pa&subj=${encodeURIComponent(
subj.name
)}`,
undefined,
{ shallow: true }
)
fetchSubject(
subj.name,
savedQuestionsFileName
).then((resp) => {
setCurrSubj(resp)
})
}}
>
{subj.name}
</div>
)
})}
</>
</div>
</>
)
} else {
return <LoadingIndicator />
}
}
}
return renderStuff()
return renderStuff()
}

View file

@ -3,312 +3,314 @@ import React, { useState, useEffect } from 'react'
import Question from '../components/Question'
import styles from './questionAdder.module.css'
import constants from '../constants.json'
import constants from '../constants.js'
import commonStyles from '../commonStyles.module.css'
const handleSubmit = async (form) => {
return new Promise((resolve, reject) => {
if (!form.subj) {
reject('nosubj')
return
}
let isValid = form.quiz.every((x) => {
return x.Q && x.A
return new Promise((resolve, reject) => {
if (!form.subj) {
reject('nosubj')
return
}
let isValid = form.quiz.every((x) => {
return x.Q && x.A
})
if (!isValid || form.quiz.length === 0) {
reject('notvalid')
return
}
const t = document.getElementById('cid').value
let cid = ''
let version = ''
if (t) {
cid = t.split('|')[0]
version = t.split('|')[1]
}
// console.log(form)
// {
// "quiz": [
// {
// "Q": "aaaaaaaaaaaa",
// "A": "bbbbbbbbbbbbbb",
// "data": {
// "type": "simple"
// }
// }
// ],
// "selectedDb": {
// "path": "questionDbs/elearning.uni-obuda.hu.json",
// "name": "elearning.uni-obuda.hu"
// },
// "subj": "Elektronika"
// }
const toSend = {
id: cid,
version: `WEBSITE${version ? ` (${version})` : ''}`,
location: `https://${form.selectedDb.name}`,
subj: form.subj,
quiz: form.quiz,
}
fetch(constants.apiUrl + 'isAdding', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(toSend),
})
.then((res) => {
return res.json()
})
.then((resp) => {
resolve(resp)
})
})
if (!isValid || form.quiz.length === 0) {
reject('notvalid')
return
}
const t = document.getElementById('cid').value
let cid = ''
let version = ''
if (t) {
cid = t.split('|')[0]
version = t.split('|')[1]
}
// console.log(form)
// {
// "quiz": [
// {
// "Q": "aaaaaaaaaaaa",
// "A": "bbbbbbbbbbbbbb",
// "data": {
// "type": "simple"
// }
// }
// ],
// "selectedDb": {
// "path": "questionDbs/elearning.uni-obuda.hu.json",
// "name": "elearning.uni-obuda.hu"
// },
// "subj": "Elektronika"
// }
const toSend = {
id: cid,
version: `WEBSITE${version ? ` (${version})` : ''}`,
location: `https://${form.selectedDb.name}`,
subj: form.subj,
quiz: form.quiz,
}
fetch(constants.apiUrl + 'isAdding', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(toSend),
})
.then((res) => {
return res.json()
})
.then((resp) => {
resolve(resp)
})
})
}
const renderUsage = () => {
return (
<ul>
<li>Ezek a kérdések ellenőrizve lesznek hogy megvannak-e már</li>
<li>
{
"Ha több válasz van, akkor ', '-vel válaszd el őket ( 'válasz1, válasz2, válasz3' )"
}
</li>
<li>
Kérdéseknél az utolsó sor (ahol a JSON cucc van) jelenleg nem
módosítható, csak olyan kérdéseket lehet beküldeni, amik sima
kérdés-válaszok, szóval pl nincs benne kép. Ez később bővül majd
</li>
<li>
Ha sok új kérdést küldesz be, akkor akár több percig is eltarthat a
dolog. Akárhány kérdést be lehet egyszerre küldeni, de max 10-15 az
ajánlott
</li>
<li>
Bármilyen szöveget beküldhettek, de ne tegyétek, más felhasználóknak és
magatoknak lesz rosz, ty!
</li>
</ul>
)
return (
<ul>
<li>Ezek a kérdések ellenőrizve lesznek hogy megvannak-e már</li>
<li>
{
"Ha több válasz van, akkor ', '-vel válaszd el őket ( 'válasz1, válasz2, válasz3' )"
}
</li>
<li>
Kérdéseknél az utolsó sor (ahol a JSON cucc van) jelenleg nem
módosítható, csak olyan kérdéseket lehet beküldeni, amik sima
kérdés-válaszok, szóval pl nincs benne kép. Ez később bővül majd
</li>
<li>
Ha sok új kérdést küldesz be, akkor akár több percig is
eltarthat a dolog. Akárhány kérdést be lehet egyszerre küldeni,
de max 10-15 az ajánlott
</li>
<li>
Bármilyen szöveget beküldhettek, de ne tegyétek, más
felhasználóknak és magatoknak lesz rosz, ty!
</li>
</ul>
)
}
const getDefaultQuestion = () => {
return {
Q: '',
A: '',
data: { type: 'simple' },
}
return {
Q: '',
A: '',
data: { type: 'simple' },
}
}
export default function QuestionAdder({ data, selectedDb, refetchDbs }) {
const [form, setForm] = useState({ quiz: [getDefaultQuestion()] })
const [subjects, setSubjects] = useState(null)
const [isSubmitting, setIsSubmitting] = useState(false)
const [isNewSubj, setIsNewSubj] = useState(false)
const [form, setForm] = useState({ quiz: [getDefaultQuestion()] })
const [subjects, setSubjects] = useState(null)
const [isSubmitting, setIsSubmitting] = useState(false)
const [isNewSubj, setIsNewSubj] = useState(false)
useEffect(() => {
if (selectedDb) {
setForm({
...form,
selectedDb: selectedDb,
})
}
}, [selectedDb])
useEffect(() => {
if (data) {
setSubjects(
data.map((subj) => {
return subj.Name
})
)
}
}, [data])
const handleQuestionChange = (index, newVal) => {
setForm({
...form,
quiz: form.quiz.map((q, i) => {
if (i !== index) {
return q
} else {
return newVal
useEffect(() => {
if (selectedDb) {
setForm({
...form,
selectedDb: selectedDb,
})
}
}),
})
}
}, [selectedDb])
const deleteQuestion = (index) => {
let quiz = form.quiz
useEffect(() => {
if (data) {
setSubjects(
data.map((subj) => {
return subj.Name
})
)
}
}, [data])
quiz.splice(index, 1)
const handleQuestionChange = (index, newVal) => {
setForm({
...form,
quiz: form.quiz.map((q, i) => {
if (i !== index) {
return q
} else {
return newVal
}
}),
})
}
setForm({
...form,
quiz: quiz,
})
}
const deleteQuestion = (index) => {
let quiz = form.quiz
const renderStuff = () => {
return (
<div>
{form.quiz.map((q, i) => {
return (
<React.Fragment key={i}>
<hr />
<Question
index={i}
question={form.quiz[i] || {}}
onChange={(newVal) => {
handleQuestionChange(i, newVal)
}}
/>
<div className={commonStyles.actions}>
<div
onClick={() => {
deleteQuestion(i)
}}
>
Törlés
</div>
</div>
</React.Fragment>
)
})}
<hr />
<div className={commonStyles.actions}>
<div
onClick={() => {
let quiz = form.quiz
quiz.push(getDefaultQuestion())
setForm({
...form,
quiz: quiz,
})
}}
>
Új kérdés
</div>
<div
className={`${isSubmitting ? styles.issubmitting : ''}`}
onClick={() => {
if (isSubmitting) {
return
}
setIsSubmitting(true)
handleSubmit(form)
.then((res) => {
// console.log(res)
// {
// "success": true,
// "newQuestions": [
// {
// "newQuestions": 1,
// "qdbName": "elearning.uni-obuda.hu"
// }
// ],
// "totalNewQuestions": 1
// }
quiz.splice(index, 1)
if (res.success) {
alert(
`Sikeres beküldés, ${res.totalNewQuestions} új kérdés`
setForm({
...form,
quiz: quiz,
})
}
const renderStuff = () => {
return (
<div>
{form.quiz.map((q, i) => {
return (
<React.Fragment key={i}>
<hr />
<Question
index={i}
question={form.quiz[i] || {}}
onChange={(newVal) => {
handleQuestionChange(i, newVal)
}}
/>
<div className={commonStyles.actions}>
<div
onClick={() => {
deleteQuestion(i)
}}
>
Törlés
</div>
</div>
</React.Fragment>
)
} else {
alert('Hiba beküldés közben :/')
}
refetchDbs()
setIsSubmitting(false)
})
.catch((res) => {
if (res === 'nosubj') {
alert('Nem választottál ki tantárgyat!') // eslint-disable-line
}
if (res === 'notvalid') {
alert('Kérdés kitöltése kötelező!') // eslint-disable-line
}
setIsSubmitting(false)
})
}}
>
{isSubmitting ? 'Beküldés folyamatban ...' : 'Kérdések beküldése'}
</div>
</div>
<input type="text" id="cid" name="cid" hidden />
</div>
)
}
})}
<hr />
<div className={commonStyles.actions}>
<div
onClick={() => {
let quiz = form.quiz
quiz.push(getDefaultQuestion())
setForm({
...form,
quiz: quiz,
})
}}
>
Új kérdés
</div>
<div
className={`${isSubmitting ? styles.issubmitting : ''}`}
onClick={() => {
if (isSubmitting) {
return
}
setIsSubmitting(true)
handleSubmit(form)
.then((res) => {
// console.log(res)
// {
// "success": true,
// "newQuestions": [
// {
// "newQuestions": 1,
// "qdbName": "elearning.uni-obuda.hu"
// }
// ],
// "totalNewQuestions": 1
// }
if (res.success) {
alert(
`Sikeres beküldés, ${res.totalNewQuestions} új kérdés`
)
} else {
alert('Hiba beküldés közben :/')
}
refetchDbs()
setIsSubmitting(false)
})
.catch((res) => {
if (res === 'nosubj') {
alert('Nem választottál ki tantárgyat!') // eslint-disable-line
}
if (res === 'notvalid') {
alert('Kérdés kitöltése kötelező!') // eslint-disable-line
}
setIsSubmitting(false)
})
}}
>
{isSubmitting
? 'Beküldés folyamatban ...'
: 'Kérdések beküldése'}
</div>
</div>
<input type="text" id="cid" name="cid" hidden />
</div>
)
}
const renderSubjSelector = () => {
return (
<div className={styles.subjSelectorContainer}>
{isNewSubj ? (
<input
placeholder="Új tárgy neve..."
type="text"
className={styles.questionInput}
onChange={(event) => {
setForm({
...form,
subj: event.target.value,
})
}}
/>
) : (
<select
onChange={(event) => {
setForm({
...form,
subj: subjects[event.target.value],
})
}}
>
<option key={-1} value={-1}>
Válassz egy tárgyat...
</option>
{subjects.map((subjName, i) => {
return (
<option key={i} value={i}>
{subjName}
</option>
)
})}
</select>
)}
<div
className={commonStyles.actions}
onClick={() => {
setIsNewSubj(!isNewSubj)
}}
>
<div>{isNewSubj ? 'Létező tárgy ...' : 'Új tárgy ...'}</div>
</div>
</div>
)
}
if (!data) {
return null
}
const renderSubjSelector = () => {
return (
<div className={styles.subjSelectorContainer}>
{isNewSubj ? (
<input
placeholder="Új tárgy neve..."
type="text"
className={styles.questionInput}
onChange={(event) => {
setForm({
...form,
subj: event.target.value,
})
}}
/>
) : (
<select
onChange={(event) => {
setForm({
...form,
subj: subjects[event.target.value],
})
}}
>
<option key={-1} value={-1}>
Válassz egy tárgyat...
</option>
{subjects.map((subjName, i) => {
return (
<option key={i} value={i}>
{subjName}
</option>
)
})}
</select>
)}
<div
className={commonStyles.actions}
onClick={() => {
setIsNewSubj(!isNewSubj)
}}
>
<div>{isNewSubj ? 'Létező tárgy ...' : 'Új tárgy ...'}</div>
<div>
<hr />
{renderUsage()}
{subjects ? (
<>
<hr />
{renderSubjSelector()}
{renderStuff()}
</>
) : null}
</div>
</div>
)
}
if (!data) {
return null
}
return (
<div>
<hr />
{renderUsage()}
{subjects ? (
<>
<hr />
{renderSubjSelector()}
{renderStuff()}
</>
) : null}
</div>
)
}

View file

@ -4,206 +4,211 @@ import LoadingIndicator from '../components/LoadingIndicator.js'
import QuestionSearchResult from '../components/QuestionSearchResult.js'
import SearchBar from '../components/searchBar'
import constants from '../constants.json'
import constants from '../constants.js'
// import styles from './questionView.module.css'
const updateQuestion = (e, selectedDb) => {
return new Promise((resolve) => {
fetch(constants.apiUrl + 'updateQuestion', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
...e,
selectedDb: selectedDb,
}),
return new Promise((resolve) => {
fetch(constants.apiUrl + 'updateQuestion', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
...e,
selectedDb: selectedDb,
}),
})
.then((res) => {
return res.json()
})
.then((res) => {
resolve(res)
})
})
.then((res) => {
return res.json()
})
.then((res) => {
resolve(res)
})
})
}
const rmQuestion = (e, selectedDb) => {
return new Promise((resolve) => {
fetch(constants.apiUrl + 'updateQuestion', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
...e,
selectedDb: selectedDb,
}),
return new Promise((resolve) => {
fetch(constants.apiUrl + 'updateQuestion', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
...e,
selectedDb: selectedDb,
}),
})
.then((res) => {
return res.json()
})
.then((res) => {
resolve(res)
})
})
.then((res) => {
return res.json()
})
.then((res) => {
resolve(res)
})
})
}
export default function questionView(props) {
const { selectedDb } = props
const [data, setData] = useState(props.data)
const [searchTerm, setSearchTerm] = useState('')
const [edits, setEdits] = useState([])
const { selectedDb } = props
const [data, setData] = useState(props.data)
const [searchTerm, setSearchTerm] = useState('')
const [edits, setEdits] = useState([])
useEffect(() => {
setData(props.data)
}, [props.data])
useEffect(() => {
setData(props.data)
}, [props.data])
const updateEdits = (e) => {
setEdits(
edits.map((edit) => {
if (edit.index === e.index) {
return e
} else {
return edit
}
})
)
}
const removeFromEdits = (e) => {
setEdits(
edits.filter((edit, i) => {
return i !== e.index
})
)
}
const onChange = (e) => {
const editIndex = edits.findIndex((edit) => {
return edit.subjName === e.subjName && edit.index === e.index
})
if (editIndex === -1) {
if (e.type === 'edit') {
if (editIndex === -1) {
setEdits([...edits, e])
}
}
if (e.type === 'delete') {
rmQuestion(e, selectedDb).then((res) => {
if (res.success) {
alert('Sikeres törlés')
} else {
alert('Hiba mentés közben :/')
}
setData(
data.map((subj) => {
if (subj.Name !== e.subjName) {
return subj
} else {
return {
...subj,
Questions: subj.Questions.filter((question, i) => {
return i !== e.index
}),
}
}
})
)
})
}
} else {
if (e.type === 'reset') {
// edits -> saves -> resets? => should do nothing, no reset after saving
removeFromEdits(e)
setData(
data.map((subj) => {
if (subj.Name !== e.subjName) {
return subj
} else {
return {
...subj,
Questions: subj.Questions.map((question, i) => {
if (i !== e.index) {
return question
} else {
const ps = props.data.find((subj) => {
return subj.Name === e.subjName
})
if (ps) {
return ps.Questions[i]
}
}
}),
}
}
})
)
}
if (e.type === 'save') {
updateQuestion(
edits.find((edit) => {
return edit.index === e.index
}),
selectedDb
).then((res) => {
if (res.success) {
alert('Sikeres mentés')
} else {
alert('Hiba mentés közben :/')
}
removeFromEdits(e)
})
}
if (e.type === 'edit') {
updateEdits(e)
}
}
if (e.type === 'edit') {
setData(
data.map((subj) => {
if (subj.Name !== e.subjName) {
return subj
} else {
return {
...subj,
Questions: subj.Questions.map((question, i) => {
if (i !== e.index) {
return question
const updateEdits = (e) => {
setEdits(
edits.map((edit) => {
if (edit.index === e.index) {
return e
} else {
return e.newVal
return edit
}
}),
}
}
})
)
})
)
}
}
if (data) {
return (
<div>
<SearchBar value={searchTerm} onChange={(e) => setSearchTerm(e)} />
<hr />
<div>
<QuestionSearchResult
onChange={onChange}
data={data}
searchTerm={searchTerm}
edits={edits}
/>
</div>
</div>
)
} else {
return <LoadingIndicator />
}
const removeFromEdits = (e) => {
setEdits(
edits.filter((edit, i) => {
return i !== e.index
})
)
}
const onChange = (e) => {
const editIndex = edits.findIndex((edit) => {
return edit.subjName === e.subjName && edit.index === e.index
})
if (editIndex === -1) {
if (e.type === 'edit') {
if (editIndex === -1) {
setEdits([...edits, e])
}
}
if (e.type === 'delete') {
rmQuestion(e, selectedDb).then((res) => {
if (res.success) {
alert('Sikeres törlés')
} else {
alert('Hiba mentés közben :/')
}
setData(
data.map((subj) => {
if (subj.Name !== e.subjName) {
return subj
} else {
return {
...subj,
Questions: subj.Questions.filter(
(question, i) => {
return i !== e.index
}
),
}
}
})
)
})
}
} else {
if (e.type === 'reset') {
// edits -> saves -> resets? => should do nothing, no reset after saving
removeFromEdits(e)
setData(
data.map((subj) => {
if (subj.Name !== e.subjName) {
return subj
} else {
return {
...subj,
Questions: subj.Questions.map((question, i) => {
if (i !== e.index) {
return question
} else {
const ps = props.data.find((subj) => {
return subj.Name === e.subjName
})
if (ps) {
return ps.Questions[i]
}
}
}),
}
}
})
)
}
if (e.type === 'save') {
updateQuestion(
edits.find((edit) => {
return edit.index === e.index
}),
selectedDb
).then((res) => {
if (res.success) {
alert('Sikeres mentés')
} else {
alert('Hiba mentés közben :/')
}
removeFromEdits(e)
})
}
if (e.type === 'edit') {
updateEdits(e)
}
}
if (e.type === 'edit') {
setData(
data.map((subj) => {
if (subj.Name !== e.subjName) {
return subj
} else {
return {
...subj,
Questions: subj.Questions.map((question, i) => {
if (i !== e.index) {
return question
} else {
return e.newVal
}
}),
}
}
})
)
}
}
if (data) {
return (
<div>
<SearchBar
value={searchTerm}
onChange={(e) => setSearchTerm(e)}
/>
<hr />
<div>
<QuestionSearchResult
onChange={onChange}
data={data}
searchTerm={searchTerm}
edits={edits}
/>
</div>
</div>
)
} else {
return <LoadingIndicator />
}
}

View file

@ -5,221 +5,224 @@ import Subject from '../components/Subject.js'
import SubjectSelector from '../components/SubjectSelector.js'
import SearchBar from '../components/searchBar'
import constants from '../constants.json'
import constants from '../constants.js'
// import styles from './subjectView.module.css'
import commonStyles from '../commonStyles.module.css'
const onSave = (subjName, changedQuestions, deletedQuestions, selectedDb) => {
return new Promise((resolve) => {
fetch(constants.apiUrl + 'updateQuestion', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
subjName: subjName,
changedQuestions: changedQuestions,
deletedQuestions: deletedQuestions,
type: 'subjEdit',
selectedDb: selectedDb,
}),
return new Promise((resolve) => {
fetch(constants.apiUrl + 'updateQuestion', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
subjName: subjName,
changedQuestions: changedQuestions,
deletedQuestions: deletedQuestions,
type: 'subjEdit',
selectedDb: selectedDb,
}),
})
.then((res) => {
return res.json()
})
.then((res) => {
resolve(res)
})
})
.then((res) => {
return res.json()
})
.then((res) => {
resolve(res)
})
})
}
export default function SubjectView(props) {
const { data, selectedDb } = props
const [activeSubjName, setActiveSubjName] = useState('')
const [searchTerm, setSearchTerm] = useState('')
const { data, selectedDb } = props
const [activeSubjName, setActiveSubjName] = useState('')
const [searchTerm, setSearchTerm] = useState('')
const [unsavedIndexes, setUnsavedIndexes] = useState([])
const [editedIndexes, setEditedIndexes] = useState([])
const [deletedIndexes, setDeletedIndexes] = useState([])
const [subj, setSubj] = useState(null)
const [unsavedIndexes, setUnsavedIndexes] = useState([])
const [editedIndexes, setEditedIndexes] = useState([])
const [deletedIndexes, setDeletedIndexes] = useState([])
const [subj, setSubj] = useState(null)
const hasChange = editedIndexes.length > 0 || deletedIndexes.length > 0
const hasChange = editedIndexes.length > 0 || deletedIndexes.length > 0
useEffect(() => {
let currSubj = data.find((subj) => {
return subj.Name === activeSubjName
})
setSubj(currSubj)
}, [activeSubjName])
const resetQuestion = (i) => {
if (deletedIndexes.includes(i)) {
setDeletedIndexes(
deletedIndexes.filter((ind) => {
return ind !== i
useEffect(() => {
let currSubj = data.find((subj) => {
return subj.Name === activeSubjName
})
)
}
setSubj(currSubj)
}, [activeSubjName])
if (editedIndexes.includes(i)) {
setEditedIndexes(
editedIndexes.filter((ind) => {
return ind !== i
})
)
}
if (unsavedIndexes.includes(i)) {
setUnsavedIndexes(
unsavedIndexes.filter((ind) => {
return ind !== i
})
)
}
let currSubj = data.find((subj) => {
return subj.Name === activeSubjName
})
setSubj({
...subj,
Questions: subj.Questions.map((q, j) => {
if (i === j) {
return currSubj.Questions[i]
} else {
return q
}
}),
})
}
const handleQuestionChange = (newq, i) => {
if (!unsavedIndexes.includes(i)) {
setUnsavedIndexes([...unsavedIndexes, i])
}
if (editedIndexes.includes(i)) {
setEditedIndexes(
editedIndexes.filter((ind) => {
return ind !== i
})
)
}
setSubj({
...subj,
Questions: subj.Questions.map((q, j) => {
if (i !== j) {
return q
} else {
return newq
}
}),
})
}
const saveQuestion = (i) => {
setUnsavedIndexes(
unsavedIndexes.filter((ind) => {
return ind !== i
})
)
setEditedIndexes([...editedIndexes, i])
}
const deleteQuestion = (i) => {
setDeletedIndexes([...deletedIndexes, i])
}
const handleSave = () => {
if (unsavedIndexes.length > 0) {
alert(
'Mentetlen módosításaid vannak, kérek mentsd, vagy állítsd vissza azokat'
)
return
}
if (editedIndexes.length === 0 && deletedIndexes.length === 0) {
alert('Nem módosítottál még semmit')
return
}
const changedQuestions = editedIndexes.map((ei) => {
return {
index: ei,
value: subj.Questions[ei],
}
})
const deletedQuestions = deletedIndexes
onSave(subj.Name, changedQuestions, deletedQuestions, selectedDb).then(
(res) => {
if (res.success) {
alert('Sikeres mentés')
} else {
alert('Hiba mentés közben :/')
const resetQuestion = (i) => {
if (deletedIndexes.includes(i)) {
setDeletedIndexes(
deletedIndexes.filter((ind) => {
return ind !== i
})
)
}
setUnsavedIndexes([])
setEditedIndexes([])
setDeletedIndexes([])
}
)
}
if (editedIndexes.includes(i)) {
setEditedIndexes(
editedIndexes.filter((ind) => {
return ind !== i
})
)
}
if (data) {
return (
<div>
<SearchBar value={searchTerm} onChange={(e) => setSearchTerm(e)} />
<hr />
<SubjectSelector
data={data}
activeSubjName={activeSubjName}
searchTerm={searchTerm}
onSubjSelect={(subjName) => {
if (
!hasChange ||
confirm(
'Mentetlen módosításaid vannak a tárgynál, biztos bezárod?'
)
) {
setActiveSubjName(subjName)
setUnsavedIndexes([])
setEditedIndexes([])
setDeletedIndexes([])
} else {
console.log('canceld')
if (unsavedIndexes.includes(i)) {
setUnsavedIndexes(
unsavedIndexes.filter((ind) => {
return ind !== i
})
)
}
let currSubj = data.find((subj) => {
return subj.Name === activeSubjName
})
setSubj({
...subj,
Questions: subj.Questions.map((q, j) => {
if (i === j) {
return currSubj.Questions[i]
} else {
return q
}
}),
})
}
const handleQuestionChange = (newq, i) => {
if (!unsavedIndexes.includes(i)) {
setUnsavedIndexes([...unsavedIndexes, i])
}
if (editedIndexes.includes(i)) {
setEditedIndexes(
editedIndexes.filter((ind) => {
return ind !== i
})
)
}
setSubj({
...subj,
Questions: subj.Questions.map((q, j) => {
if (i !== j) {
return q
} else {
return newq
}
}),
})
}
const saveQuestion = (i) => {
setUnsavedIndexes(
unsavedIndexes.filter((ind) => {
return ind !== i
})
)
setEditedIndexes([...editedIndexes, i])
}
const deleteQuestion = (i) => {
setDeletedIndexes([...deletedIndexes, i])
}
const handleSave = () => {
if (unsavedIndexes.length > 0) {
alert(
'Mentetlen módosításaid vannak, kérek mentsd, vagy állítsd vissza azokat'
)
return
}
if (editedIndexes.length === 0 && deletedIndexes.length === 0) {
alert('Nem módosítottál még semmit')
return
}
const changedQuestions = editedIndexes.map((ei) => {
return {
index: ei,
value: subj.Questions[ei],
}
}}
/>
<hr />
<div className={commonStyles.actions}>
{subj && (
<div
onClick={() => {
handleSave()
}}
>
Tárgy módosításainak mentése
})
const deletedQuestions = deletedIndexes
onSave(subj.Name, changedQuestions, deletedQuestions, selectedDb).then(
(res) => {
if (res.success) {
alert('Sikeres mentés')
} else {
alert('Hiba mentés közben :/')
}
setUnsavedIndexes([])
setEditedIndexes([])
setDeletedIndexes([])
}
)
}
if (data) {
return (
<div>
<SearchBar
value={searchTerm}
onChange={(e) => setSearchTerm(e)}
/>
<hr />
<SubjectSelector
data={data}
activeSubjName={activeSubjName}
searchTerm={searchTerm}
onSubjSelect={(subjName) => {
if (
!hasChange ||
confirm(
'Mentetlen módosításaid vannak a tárgynál, biztos bezárod?'
)
) {
setActiveSubjName(subjName)
setUnsavedIndexes([])
setEditedIndexes([])
setDeletedIndexes([])
} else {
console.log('canceld')
}
}}
/>
<hr />
<div className={commonStyles.actions}>
{subj && (
<div
onClick={() => {
handleSave()
}}
>
Tárgy módosításainak mentése
</div>
)}
</div>
<div>
{subj && (
<Subject
subj={subj}
unsavedIndexes={unsavedIndexes}
editedIndexes={editedIndexes}
deletedIndexes={deletedIndexes}
resetQuestion={resetQuestion}
handleQuestionChange={handleQuestionChange}
saveQuestion={saveQuestion}
deleteQuestion={deleteQuestion}
/>
)}
</div>
</div>
)}
</div>
<div>
{subj && (
<Subject
subj={subj}
unsavedIndexes={unsavedIndexes}
editedIndexes={editedIndexes}
deletedIndexes={deletedIndexes}
resetQuestion={resetQuestion}
handleQuestionChange={handleQuestionChange}
saveQuestion={saveQuestion}
deleteQuestion={deleteQuestion}
/>
)}
</div>
</div>
)
} else {
return <LoadingIndicator />
}
)
} else {
return <LoadingIndicator />
}
}

View file

@ -2,188 +2,199 @@ import React, { useState } from 'react'
import Question from '../components/Question'
import constants from '../constants.json'
import constants from '../constants.js'
import styles from './testView.module.css'
import commonStyles from '../commonStyles.module.css'
const rmTest = (subjName, testName) => {
return new Promise((resolve) => {
fetch(constants.apiUrl + 'rmPossibleAnswer', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
subj: subjName,
file: testName,
}),
return new Promise((resolve) => {
fetch(constants.apiUrl + 'rmPossibleAnswer', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
subj: subjName,
file: testName,
}),
})
.then((res) => {
return res.json()
})
.then((res) => {
resolve(res)
})
})
.then((res) => {
return res.json()
})
.then((res) => {
resolve(res)
})
})
}
export default function TestView(props) {
const { subjName, testName, router, onDelete } = props
const [test, setTest] = useState(props.test)
const { subjName, testName, router, onDelete } = props
const [test, setTest] = useState(props.test)
const renderActions = () => {
return (
<div className={styles.actions}>
<div
onClick={() => {
if (confirm('Biztos véglegesen törlöd ezt az egész tesztet?')) {
rmTest(subjName, testName).then((res) => {
if (res.res === 'ok') {
// alert('sikeres törlés')
router.back()
onDelete()
} else {
alert('hiba törlés közben!')
}
})
}
}}
>
Teszt törlése
</div>
<div
onClick={() => {
if (
confirm(
'Biztos beküldöd? Beküldés után törlődik a teszt, de a Kérdések/Tárgyak nézetnél megtalálhatóak lesznek'
)
) {
const questions = test.questions.map((q) => {
return {
Q: q.Q,
A: q.A,
data: {
...q.data,
...(q.possibleAnswers && {
possibleAnswers: q.possibleAnswers,
}),
},
}
})
const toSend = {
id: 'WEBSITE',
version: 'WEBSITE',
location: `https://${test.testUrl}`,
subj: test.subj,
quiz: questions,
}
fetch(constants.apiUrl + 'isAdding', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(toSend),
})
.then((res) => {
return res.json()
})
.then((res) => {
if (res.success) {
alert(
`Sikeres beküldés, ${res.totalNewQuestions} új kérdés`
)
rmTest(subjName, testName).then((res) => {
if (res.res === 'ok') {
router.back()
onDelete()
}
})
} else {
alert('Hiba beküldés közben :/')
}
})
}
}}
>
Teszt mentése
</div>
</div>
)
}
return (
<div>
<div className={styles.headerContainer}>
<div className={styles.header}>
<div>Tárgy neve</div>
<div>{test.subj}</div>
</div>
<div className={styles.header}>
<div>Teszt URL</div>
<div>{test.testUrl}</div>
</div>
<div className={styles.header}>
<div>Beküldő felhasználó ID</div>
<div>{test.userid}</div>{' '}
</div>
<div className={styles.header}>
<div>Beküldés ideje</div>
<div>{new Date(test.date).toLocaleString()}</div>{' '}
</div>
</div>
<hr />
{renderActions()}
<div className={styles.questionsContainer}>
{test.questions.map((question, i) => {
return (
<React.Fragment key={i}>
<hr key={`${i}hr`} />
<div key={i}>
<Question
index={i}
onChange={(newQ) => {
setTest({
...test,
questions: test.questions.map((q, j) => {
if (j === i) {
return newQ
}
return q
}),
})
}}
question={question}
/>
<div className={commonStyles.actions}>
<div
const renderActions = () => {
return (
<div className={styles.actions}>
<div
onClick={() => {
setTest({
...test,
questions: test.questions.filter((q, j) => {
return j !== i
}),
})
if (
confirm(
'Biztos véglegesen törlöd ezt az egész tesztet?'
)
) {
rmTest(subjName, testName).then((res) => {
if (res.res === 'ok') {
// alert('sikeres törlés')
router.back()
onDelete()
} else {
alert('hiba törlés közben!')
}
})
}
}}
>
Kérdés törlése
</div>
>
Teszt törlése
</div>
</div>
</React.Fragment>
)
})}
</div>
{test.questions.length > 2 ? (
<>
<hr />
{renderActions()}
</>
) : null}
</div>
)
<div
onClick={() => {
if (
confirm(
'Biztos beküldöd? Beküldés után törlődik a teszt, de a Kérdések/Tárgyak nézetnél megtalálhatóak lesznek'
)
) {
const questions = test.questions.map((q) => {
return {
Q: q.Q,
A: q.A,
data: {
...q.data,
...(q.possibleAnswers && {
possibleAnswers: q.possibleAnswers,
}),
},
}
})
const toSend = {
id: 'WEBSITE',
version: 'WEBSITE',
location: `https://${test.testUrl}`,
subj: test.subj,
quiz: questions,
}
fetch(constants.apiUrl + 'isAdding', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(toSend),
})
.then((res) => {
return res.json()
})
.then((res) => {
if (res.success) {
alert(
`Sikeres beküldés, ${res.totalNewQuestions} új kérdés`
)
rmTest(subjName, testName).then(
(res) => {
if (res.res === 'ok') {
router.back()
onDelete()
}
}
)
} else {
alert('Hiba beküldés közben :/')
}
})
}
}}
>
Teszt mentése
</div>
</div>
)
}
return (
<div>
<div className={styles.headerContainer}>
<div className={styles.header}>
<div>Tárgy neve</div>
<div>{test.subj}</div>
</div>
<div className={styles.header}>
<div>Teszt URL</div>
<div>{test.testUrl}</div>
</div>
<div className={styles.header}>
<div>Beküldő felhasználó ID</div>
<div>{test.userid}</div>{' '}
</div>
<div className={styles.header}>
<div>Beküldés ideje</div>
<div>{new Date(test.date).toLocaleString()}</div>{' '}
</div>
</div>
<hr />
{renderActions()}
<div className={styles.questionsContainer}>
{test.questions.map((question, i) => {
return (
<React.Fragment key={i}>
<hr key={`${i}hr`} />
<div key={i}>
<Question
index={i}
onChange={(newQ) => {
setTest({
...test,
questions: test.questions.map(
(q, j) => {
if (j === i) {
return newQ
}
return q
}
),
})
}}
question={question}
/>
<div className={commonStyles.actions}>
<div
onClick={() => {
setTest({
...test,
questions:
test.questions.filter(
(q, j) => {
return j !== i
}
),
})
}}
>
Kérdés törlése
</div>
</div>
</div>
</React.Fragment>
)
})}
</div>
{test.questions.length > 2 ? (
<>
<hr />
{renderActions()}
</>
) : null}
</div>
)
}

19
src/constants.js Normal file
View file

@ -0,0 +1,19 @@
// eslint-disable-next-line no-undef
const useLocalhost = process && process.env.NODE_ENV === 'development'
// eslint-disable-next-line no-undef
const domain = process && process.env.DOMAIN
if (!domain && !useLocalhost) {
throw new Error('Domain is not defined! Please set DOMAIN env variable!')
}
const constants = {
domain: domain || 'localhost',
siteUrl: useLocalhost ? 'http://localhost:8080/' : `https://${domain}/`,
apiUrl: useLocalhost
? 'http://localhost:8080/api/'
: `https://${domain}/api/`,
maxQuestionsToRender: 250,
}
export default constants

View file

@ -1,5 +0,0 @@
{
"siteUrl": "https://qmining.frylabs.net/",
"apiUrl": "https://api.frylabs.net/",
"maxQuestionsToRender": 250
}

View file

@ -11,302 +11,328 @@ import LoadingIndicator from '../components/LoadingIndicator'
import styles from './index.module.css'
import commonStyles from '../commonStyles.module.css'
import constants from '../constants.json'
import constants from '../constants.js'
const Infos = ({ onClick, renderOKButton }) => {
return (
<div className={commonStyles.infoContainer}>
<div className={commonStyles.infoHeader}>Kérdés szerkesztő</div>
<div>
Ezen az oldalon az éles adatbázisban levő kérdéseket tudod szerkeszteni,
vagy azokhoz tudsz adni.{' '}
<b>
A törléshez és módosításokhoz nem kér megerősítést, ezek azonnal
megtörténnek, és nem visszavonhatóak.
</b>
</div>
<div>
<i>Néhány dolog, amit kérlek tarts be szerkesztés közben:</i>
<div style={{ textAlign: 'left', width: '700px' }}>
<ul>
<li>
Ne rontsd el a kérdéseket sok törléssel / rossz válasz
megadásával. Sok más felhasználónak lesz rossz, és visszakereshető
/ tiltható a módosító
</li>
<li>
Arra is vigyázz, hogy véletlen se történjen ilyesmi, vagy ha mégis
valami baj történt, akkor azt{' '}
<a href={`${constants.siteUrl}irc`}>jelezd</a>. Van sok biztonsági
mentés
</li>
<li>
Ahhoz, hogy a script megtalálja a helyes választ a kérdés
szövegének <b>pontosan</b> olyannak kell lennie, mint a teszt
közben (elírásokkal, .... -okkal, meg mindennel)
</li>
</ul>
return (
<div className={commonStyles.infoContainer}>
<div className={commonStyles.infoHeader}>Kérdés szerkesztő</div>
<div>
Ezen az oldalon az éles adatbázisban levő kérdéseket tudod
szerkeszteni, vagy azokhoz tudsz adni.{' '}
<b>
A törléshez és módosításokhoz nem kér megerősítést, ezek
azonnal megtörténnek, és nem visszavonhatóak.
</b>
</div>
<div>
<i>Néhány dolog, amit kérlek tarts be szerkesztés közben:</i>
<div style={{ textAlign: 'left', width: '700px' }}>
<ul>
<li>
Ne rontsd el a kérdéseket sok törléssel / rossz
válasz megadásával. Sok más felhasználónak lesz
rossz, és visszakereshető / tiltható a módosító
</li>
<li>
Arra is vigyázz, hogy véletlen se történjen ilyesmi,
vagy ha mégis valami baj történt, akkor azt{' '}
<a href={`${constants.siteUrl}irc`}>jelezd</a>. Van
sok biztonsági mentés
</li>
<li>
Ahhoz, hogy a script megtalálja a helyes választ a
kérdés szövegének <b>pontosan</b> olyannak kell
lennie, mint a teszt közben (elírásokkal, ....
-okkal, meg mindennel)
</li>
</ul>
</div>
<div>
Ha akármi kérdés van{' '}
<a
href={`${constants.siteUrl}contact`}
target={'_blank'}
rel={'noreferrer'}
>
itt lehet üzenetet küldeni
</a>
.
</div>
</div>
{renderOKButton && (
<div className={commonStyles.infoReadButton} onClick={onClick}>
OK
</div>
)}
</div>
<div>
Ha akármi kérdés van{' '}
<a
href={`${constants.siteUrl}contact`}
target={'_blank'}
rel={'noreferrer'}
>
itt lehet üzenetet küldeni
</a>
.
</div>
</div>
{renderOKButton && (
<div className={commonStyles.infoReadButton} onClick={onClick}>
OK
</div>
)}
</div>
)
)
}
const fetchDbs = () => {
return new Promise((resolve) => {
fetch(`${constants.apiUrl}getDbs`, {
credentials: 'include',
return new Promise((resolve) => {
fetch(`${constants.apiUrl}getDbs`, {
credentials: 'include',
})
.then((resp) => {
return resp.json()
})
.then((resp) => {
resolve(resp)
})
})
.then((resp) => {
return resp.json()
})
.then((resp) => {
resolve(resp)
})
})
}
const fetchData = (selectedDb) => {
return new Promise((resolve) => {
const toFetch = `${constants.apiUrl}${selectedDb.path}`
fetch(toFetch, {
credentials: 'include',
return new Promise((resolve) => {
const toFetch = `${constants.apiUrl}${selectedDb.path}`
fetch(toFetch, {
credentials: 'include',
})
.then((resp) => {
return resp.json()
})
.then((resp) => {
resolve(resp)
})
})
.then((resp) => {
return resp.json()
})
.then((resp) => {
resolve(resp)
})
})
}
const views = {
welcome: 'w',
subject: 's',
question: 'q',
questionAdder: 'qa',
possibleAnswers: 'pa',
welcome: 'w',
subject: 's',
question: 'q',
questionAdder: 'qa',
possibleAnswers: 'pa',
}
export default function Index({ router }) {
const [infoRead, setInfoRead] = useState(false)
const [data, setData] = useState(null)
const [qdbs, setQdbs] = useState(null)
const [selectedDb, setSelectedDb] = useState(null)
const [view, setView] = useState(views.welcome)
const [infoRead, setInfoRead] = useState(false)
const [data, setData] = useState(null)
const [qdbs, setQdbs] = useState(null)
const [selectedDb, setSelectedDb] = useState(null)
const [view, setView] = useState(views.welcome)
const [error, setError] = useState(null)
const [error, setError] = useState(null)
useEffect(() => {
if (selectedDb) {
loadData()
} else {
setData(null)
}
}, [selectedDb])
useEffect(() => {
if (selectedDb) {
loadData()
} else {
setData(null)
}
}, [selectedDb])
useEffect(() => {
fetchDbs().then((resp) => {
setQdbs(resp)
useEffect(() => {
fetchDbs().then((resp) => {
setQdbs(resp)
const view = router.query.v
? decodeURIComponent(router.query.v)
: views.welcome
setView(view)
})
}, [])
const view = router.query.v
? decodeURIComponent(router.query.v)
: views.welcome
setView(view)
})
}, [])
const loadData = () => {
setData(null)
if (!selectedDb) {
alert('Válassz egy adatbázist!')
return
}
const loadData = () => {
setData(null)
if (!selectedDb) {
alert('Válassz egy adatbázist!')
return
}
fetchData(selectedDb)
.then((resp) => {
setData(resp)
})
.catch((error) => {
console.error(error)
console.error('Error while fetching data')
setError('Error while fetching data')
})
}
const refetchDbs = () => {
if (selectedDb) {
fetchData(selectedDb).then((resp) => {
setData(resp)
})
}
fetchDbs().then((resp) => {
setQdbs(resp)
})
}
const renderView = () => {
if (view === views.subject) {
return (
<>
<Head>
<title>Tárgyak - Data Editor | Frylabs.net</title>
</Head>
<DbSelector
qdbs={qdbs}
selectedDb={selectedDb}
onChange={setSelectedDb}
/>
{data && <SubjectView selectedDb={selectedDb} data={data} />}
</>
)
} else if (view === views.question) {
return (
<>
<Head>
<title>Kérdések - Data Editor | Frylabs.net</title>
</Head>
<DbSelector
qdbs={qdbs}
selectedDb={selectedDb}
onChange={setSelectedDb}
/>
{data && <QuestionView selectedDb={selectedDb} data={data} />}
</>
)
} else if (view === views.questionAdder) {
return (
<>
<Head>
<title>Kérdés beküldés - Data Editor | Frylabs.net</title>
</Head>
<DbSelector
hideLockedDbs
qdbs={qdbs}
selectedDb={selectedDb}
onChange={setSelectedDb}
/>
<QuestionAdder
data={data}
selectedDb={selectedDb}
refetchDbs={refetchDbs}
/>
</>
)
} else if (view === views.possibleAnswers) {
return (
<>
<Head>
<title>Kitöltetlen tesztek - Data Editor | Frylabs.net</title>
</Head>
<PossibleAnswers refetchDbs={refetchDbs} router={router} />
</>
)
} else if (view === views.welcome) {
return <Infos renderOKButton={false} />
} else {
return <div>No view!</div>
}
}
function renderViewSelector() {
return (
<div className={styles.viewButtonContainer}>
<div
className={view === views.question ? styles.activeView : undefined}
onClick={() => {
router.replace(
`${router.pathname}?v=${views.question}`,
undefined,
{ shallow: true }
)
setView(views.question)
}}
>
Kérdések
</div>
<div
title={
'Választott adatbázisban lévő tárgyak megjelenítése, és tárgyakon belüli kérdések szerkesztése'
}
className={view === views.subject ? styles.activeView : undefined}
onClick={() => {
router.replace(`${router.pathname}?v=${views.subject}`, undefined, {
shallow: true,
fetchData(selectedDb)
.then((resp) => {
setData(resp)
})
setView(views.subject)
}}
>
Tárgyak
</div>
<div
className={
view === views.questionAdder ? styles.activeView : undefined
}
onClick={() => {
router.replace(
`${router.pathname}?v=${views.questionAdder}`,
undefined,
{ shallow: true }
.catch((error) => {
console.error(error)
console.error('Error while fetching data')
setError('Error while fetching data')
})
}
const refetchDbs = () => {
if (selectedDb) {
fetchData(selectedDb).then((resp) => {
setData(resp)
})
}
fetchDbs().then((resp) => {
setQdbs(resp)
})
}
const renderView = () => {
if (view === views.subject) {
return (
<>
<Head>
<title>
Tárgyak - Data Editor | {constants.domain}
</title>
</Head>
<DbSelector
qdbs={qdbs}
selectedDb={selectedDb}
onChange={setSelectedDb}
/>
{data && (
<SubjectView selectedDb={selectedDb} data={data} />
)}
</>
)
setView(views.questionAdder)
setSelectedDb(null)
}}
>
Kérdés beküldés
</div>
<div
className={
view === views.possibleAnswers ? styles.activeView : undefined
}
onClick={() => {
router.replace(
`${router.pathname}?v=${views.possibleAnswers}`,
undefined,
{ shallow: true }
} else if (view === views.question) {
return (
<>
<Head>
<title>
Kérdések - Data Editor | {constants.domain}
</title>
</Head>
<DbSelector
qdbs={qdbs}
selectedDb={selectedDb}
onChange={setSelectedDb}
/>
{data && (
<QuestionView selectedDb={selectedDb} data={data} />
)}
</>
)
setView(views.possibleAnswers)
}}
>
Kitöltetlen tesztek
} else if (view === views.questionAdder) {
return (
<>
<Head>
<title>
Kérdés beküldés - Data Editor | {constants.domain}
</title>
</Head>
<DbSelector
hideLockedDbs
qdbs={qdbs}
selectedDb={selectedDb}
onChange={setSelectedDb}
/>
<QuestionAdder
data={data}
selectedDb={selectedDb}
refetchDbs={refetchDbs}
/>
</>
)
} else if (view === views.possibleAnswers) {
return (
<>
<Head>
<title>
Kitöltetlen tesztek - Data Editor |{' '}
{constants.domain}
</title>
</Head>
<PossibleAnswers refetchDbs={refetchDbs} router={router} />
</>
)
} else if (view === views.welcome) {
return <Infos renderOKButton={false} />
} else {
return <div>No view!</div>
}
}
function renderViewSelector() {
return (
<div className={styles.viewButtonContainer}>
<div
className={
view === views.question ? styles.activeView : undefined
}
onClick={() => {
router.replace(
`${router.pathname}?v=${views.question}`,
undefined,
{ shallow: true }
)
setView(views.question)
}}
>
Kérdések
</div>
<div
title={
'Választott adatbázisban lévő tárgyak megjelenítése, és tárgyakon belüli kérdések szerkesztése'
}
className={
view === views.subject ? styles.activeView : undefined
}
onClick={() => {
router.replace(
`${router.pathname}?v=${views.subject}`,
undefined,
{
shallow: true,
}
)
setView(views.subject)
}}
>
Tárgyak
</div>
<div
className={
view === views.questionAdder
? styles.activeView
: undefined
}
onClick={() => {
router.replace(
`${router.pathname}?v=${views.questionAdder}`,
undefined,
{ shallow: true }
)
setView(views.questionAdder)
setSelectedDb(null)
}}
>
Kérdés beküldés
</div>
<div
className={
view === views.possibleAnswers
? styles.activeView
: undefined
}
onClick={() => {
router.replace(
`${router.pathname}?v=${views.possibleAnswers}`,
undefined,
{ shallow: true }
)
setView(views.possibleAnswers)
}}
>
Kitöltetlen tesztek
</div>
</div>
)
}
if (error) {
return <div>{error}</div>
}
if (!infoRead) {
return <Infos onClick={() => setInfoRead(true)} renderOKButton />
}
if (!qdbs) {
return <LoadingIndicator />
}
return (
<div>
{renderViewSelector()}
{renderView()}
</div>
</div>
)
}
if (error) {
return <div>{error}</div>
}
if (!infoRead) {
return <Infos onClick={() => setInfoRead(true)} renderOKButton />
}
if (!qdbs) {
return <LoadingIndicator />
}
return (
<div>
{renderViewSelector()}
{renderView()}
</div>
)
}