diff --git a/.eslintrc.js b/.eslintrc.js index 087887b..78b1547 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -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, } diff --git a/src/components/QuestionSearchResult.js b/src/components/QuestionSearchResult.js index b7055aa..5532c6e 100644 --- a/src/components/QuestionSearchResult.js +++ b/src/components/QuestionSearchResult.js @@ -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> + ) + } } diff --git a/src/components/possibleAnswers.js b/src/components/possibleAnswers.js index d46650c..24faf99 100644 --- a/src/components/possibleAnswers.js +++ b/src/components/possibleAnswers.js @@ -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() } diff --git a/src/components/questionAdder.js b/src/components/questionAdder.js index 326ce52..72cc946 100644 --- a/src/components/questionAdder.js +++ b/src/components/questionAdder.js @@ -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> - ) } diff --git a/src/components/questionView.js b/src/components/questionView.js index e409cec..423dcea 100644 --- a/src/components/questionView.js +++ b/src/components/questionView.js @@ -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 /> + } } diff --git a/src/components/subjectView.js b/src/components/subjectView.js index 0424934..7792e8a 100644 --- a/src/components/subjectView.js +++ b/src/components/subjectView.js @@ -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 /> + } } diff --git a/src/components/testView.js b/src/components/testView.js index 77cbc3f..6ec1f20 100644 --- a/src/components/testView.js +++ b/src/components/testView.js @@ -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> + ) } diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..2b785de --- /dev/null +++ b/src/constants.js @@ -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 diff --git a/src/constants.json b/src/constants.json deleted file mode 100644 index fd35ff2..0000000 --- a/src/constants.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "siteUrl": "https://qmining.frylabs.net/", - "apiUrl": "https://api.frylabs.net/", - "maxQuestionsToRender": 250 -} diff --git a/src/pages/index.js b/src/pages/index.js index b150fb4..6306c61 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -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> - ) }