Merge branch 'new_style'

This commit is contained in:
mrfry 2021-04-21 12:12:47 +02:00
commit 21fdbb57a3
72 changed files with 3153 additions and 6934 deletions

View file

@ -1,13 +1,12 @@
import React from 'react'
import styles from './button.module.css'
export default function Button (props) {
export default function Button(props) {
return (
<div>
<center>
<a href={props.href}>
<div className={styles.ircLink}>
{props.text}
</div>
<div className={styles.button}>{props.text}</div>
</a>
</center>
</div>

View file

@ -1,6 +1,6 @@
.load {
border: 4px solid #f3f3f3;
border-top: 4px solid #99f;
border-top: 4px solid var(--text-color);
border-radius: 50%;
width: 20px;
height: 20px;

View file

@ -1,33 +1,41 @@
import React, { PureComponent } from 'react'
import React from 'react'
class Question extends PureComponent {
render () {
const { question } = this.props
let qdata = question.data
if (typeof qdata === 'object' && qdata.type === 'simple') {
qdata = ''
}
if (qdata) {
try {
qdata = JSON.stringify(qdata)
} catch (e) {}
}
return (
<div className='questionContainer'>
<div className='question'>
{question.Q}
</div>
<div className='answer'>
{question.A}
</div>
<div className='data'>
{qdata || null}
</div>
</div>
)
}
function highlightText(text, toHighlight) {
const re = new RegExp(toHighlight, 'gi')
return text.replace(re, `<mark>${toHighlight}</mark>`)
}
export default Question
export default function Question({ question, searchTerm }) {
let qdata = question.data
if (typeof qdata === 'object' && qdata.type === 'simple') {
qdata = ''
}
if (qdata) {
try {
qdata = JSON.stringify(qdata)
} catch (e) {
//
}
}
const questionText = searchTerm
? highlightText(question.Q, searchTerm)
: question.Q
const answerText = searchTerm
? highlightText(question.A, searchTerm)
: question.A
return (
<div className="questionContainer">
<div
className="question"
dangerouslySetInnerHTML={{ __html: questionText }}
></div>
<div
className="answer"
dangerouslySetInnerHTML={{ __html: answerText }}
></div>
<div className="data">{qdata || null}</div>
</div>
)
}

View file

@ -1,4 +1,4 @@
import React, { PureComponent } from 'react'
import React from 'react'
import Questions from './Questions.js'
@ -8,77 +8,74 @@ const countReducer = (acc, subj) => {
return acc + subj.Questions.length
}
class QuestionSearchResult extends PureComponent {
render() {
const { data, searchTerm } = this.props
export default function QuestionSearchResult({ data, searchTerm }) {
let subjs = []
let results = -1
let subjs = []
let results = -1
if (searchTerm) {
subjs = data.reduce((acc, subj) => {
const resultQuestions = subj.Questions.reduce((qacc, question) => {
const keys = ['Q', 'A', 'data']
keys.some((key) => {
if (typeof question[key] !== 'string') {
return false
}
if (
question[key] &&
question[key].toLowerCase().includes(searchTerm.toLowerCase())
) {
qacc.push(question)
return true
}
})
return qacc
}, [])
if (resultQuestions.length > 0) {
acc.push({
Name: subj.Name,
Questions: resultQuestions,
})
}
return acc
if (searchTerm) {
subjs = data.reduce((acc, subj) => {
const resultQuestions = subj.Questions.reduce((qacc, question) => {
const keys = ['Q', 'A', 'data']
keys.some((key) => {
if (typeof question[key] !== 'string') {
return false
}
if (
question[key] &&
question[key].toLowerCase().includes(searchTerm.toLowerCase())
) {
qacc.push(question)
return true
}
})
return qacc
}, [])
results = subjs.reduce(countReducer, 0)
} else {
results = data.reduce(countReducer, 0)
}
if (resultQuestions.length > 0) {
acc.push({
Name: subj.Name,
Questions: resultQuestions,
})
}
return acc
}, [])
results = subjs.reduce(countReducer, 0)
} else {
results = data.reduce(countReducer, 0)
}
const renderCount = () => {
return (
<div>
const renderCount = () => {
return (
<div className="resultContainer">
{results === 0 && (
<div>
{searchTerm ? '' : 'Kezdj el írni kereséshez! '}
{searchTerm
? `${results} találat, ${subjs.length} tárgyból`
: `Keresés ${results} kérdés és ${data.length} tárgyból`}
{`${results} találat. Az általad keresett kifejezés nem található,
próbáld bővíteni a keresési feltételt!`}
</div>
{results === 0 && (
<div>
{
'Pontos egyezést keres a kereső, próbálj kisebb részletre keresni'
}
</div>
)}
</div>
)
}
)}
{results > 0 && <div>{`${results} találat.`}</div>}
</div>
)
}
if (results > constants.maxQuestionsToRender) {
return renderCount()
} else {
return (
<div>
<div>{renderCount()}</div>
<div>
<Questions subjs={subjs} />
if (results > constants.maxQuestionsToRender) {
return (
<div>
{searchTerm ? (
<div className="resultContainer">
Szűkítsd a keresési feltételeket a találatok megjelenítéséhez!
</div>
) : null}
<hr />
</div>
)
} else {
return (
<div>
<div>{renderCount()}</div>
<div>
<Questions subjs={subjs} searchTerm={searchTerm} />
</div>
)
}
</div>
)
}
}
export default QuestionSearchResult

View file

@ -5,22 +5,22 @@ import Question from './Question.js'
import styles from './Questions.module.css'
class Questions extends PureComponent {
render () {
const { subjs } = this.props
render() {
const { subjs, searchTerm } = this.props
return (
<div>
<hr />
{subjs.map((subj, i) => {
return (
<div key={i}>
<div className={styles.subjName}>
{subj.Name}
</div>
{ subj.Questions.map((question, i) => {
<div className={styles.questionBg} key={i}>
<div className={styles.subjName}>{subj.Name}</div>
{subj.Questions.map((question, i) => {
return (
<Question
key={i}
question={question}
searchTerm={searchTerm}
/>
)
})}

View file

@ -1,7 +1,14 @@
.subjName {
font-size: 24px;
background-color: #9999ff;
font-weight: 600;
background-color: var(--text-color);
color: black;
margin-top: 25px;
padding: 10px;
word-wrap: break-word;
}
.questionBg {
background-color: #1b1b1c;
padding-bottom: 5px;
}

View file

@ -3,26 +3,19 @@ import React, { PureComponent } from 'react'
import Question from './Question.js'
class Subject extends PureComponent {
render () {
render() {
const { subj } = this.props
if (subj) {
return (
<div >
<div>
{subj.Questions.map((question, i) => {
return (
<Question
key={i}
question={question}
/>
)
return <Question key={i} question={question} />
})}
</div>
)
} else {
return (
<div />
)
return <div />
}
}
}

View file

@ -1,10 +1,10 @@
import styles from './SubjectSelector.module.css'
export default function SubjectSelector (props) {
export default function SubjectSelector(props) {
const { activeSubjName, searchTerm, data, onSubjSelect } = props
return (
<div className='subjectSelector'>
<div className="subjectSelector">
{data.map((subj, i) => {
if (!subj.Name.toLowerCase().includes(searchTerm.toLowerCase())) {
return null
@ -12,16 +12,15 @@ export default function SubjectSelector (props) {
return (
<div
className={activeSubjName === subj.Name
? 'subjItem activeSubjItem'
: 'subjItem'
className={
activeSubjName === subj.Name
? 'subjItem activeSubjItem'
: 'subjItem'
}
key={i}
onClick={() => onSubjSelect(subj.Name)}
>
<span className={styles.subjName}>
{subj.Name}
</span>
<span className={styles.subjName}>{subj.Name}</span>
<span className={styles.questionCount}>
[ {subj.Questions.length} ]
</span>

View file

@ -1,4 +1,4 @@
.ircLink {
.button {
background-color: #9999ff;
border: none;
color: white;

View file

@ -22,14 +22,14 @@ function CommentInput({ onSubmit, onCancel }) {
onSubmit(val)
}}
>
Submit
Küldés
</span>
<span
onClick={() => {
onCancel()
}}
>
Cancel
Bezárás
</span>
</div>
</div>
@ -45,8 +45,8 @@ function Comment({ comment, index, onComment, onDelete, onReact, uid }) {
const commentStyle = admin
? styles.adminComment
: own
? styles.ownComment
: ''
? styles.ownComment
: ''
return (
<div className={styles.comment}>
@ -63,25 +63,27 @@ function Comment({ comment, index, onComment, onDelete, onReact, uid }) {
</div>
<div>User #{user}</div>
</div>
<div>{date}</div>
<div className={styles.commentDate}>{date}</div>
</div>
<div className={`${!displayed && styles.hidden}`}>
<div className={styles.commentText}> {content}</div>
<div className={'actions'}>
<span
className={styles.reply_bttn}
onClick={() => {
setCommenting(true)
}}
>
Reply...
Válasz...
</span>
{own && (
<span
className={styles.delete_bttn}
onClick={() => {
onDelete([index])
}}
>
Delete
Törlés
</span>
)}
<ReactButton
@ -179,11 +181,12 @@ export default function Comments({
{commentCount !== 0 ? (
<div className={'actions'}>
<span
className={styles.comment_bttn}
onClick={() => {
setAddingNewComment(true)
}}
>
New comment
Új komment
</span>
</div>
) : null}
@ -191,7 +194,7 @@ export default function Comments({
<CommentInput
onSubmit={(e) => {
if (!e) {
alert('Írj be valamit, hogy kommentelhess...')
alert('Írj be valamit a szövegdobozba, hogy kommentelhess!')
return
}
setAddingNewComment(false)
@ -207,18 +210,19 @@ export default function Comments({
) : null}
</Modal>
) : null}
<div className={'actions'}>
<span
onClick={() => {
setCommentsShowing(true)
if (commentCount === 0) {
setAddingNewComment(true)
}
}}
>
<div
className={'actions'}
onClick={() => {
setCommentsShowing(true)
if (commentCount === 0) {
setAddingNewComment(true)
}
}}
>
<span className={styles.comment_bttn}>
{commentCount === 0
? 'New comment'
: `Show ${commentCount} comment${commentCount > 1 ? 's' : ''}`}
? 'Új komment'
: `Kommentek mutatása (${commentCount})`}
</span>
</div>
</div>

View file

@ -1,21 +1,58 @@
.comment_bttn {
font-size: 15px;
padding-top: 4px !important;
transition: width 0.5s, height 0.5s, ease-in 0.5s;
}
.comment_bttn:hover {
transition: width 0.5s, height 0.5s, ease-out 0.5s;
color: gainsboro;
}
.reply_bttn {
font-size: 15px;
margin-top: 14.5px !important;
margin-left: 6px !important;
padding-top: 4px !important;
transition: width 0.5s, height 0.5s, ease-in 0.5s;
}
.reply_bttn:hover {
transition: width 0.5s, height 0.5s, ease-out 0.5s;
color: gainsboro;
}
.delete_bttn {
font-size: 15px;
margin-top: 14.5px !important;
margin-left: 6px !important;
padding-top: 4px !important;
transition: width 0.5s, height 0.5s, ease-in 0.5s;
}
.delete_bttn:hover {
transition: width 0.5s, height 0.5s, ease-out 0.5s;
color: gainsboro;
}
.comment {
margin-left: 25px;
margin-left: 10px;
margin-right: 30px;
padding: 8px 0px;
}
.commentData {
padding: 5px 2px;
border-left: 2px solid var(--text-color);
border-radius: 3px;
border-left: 3px solid azure;
}
.commentData:hover {
background-color: var(--hoover-color);
.commentDate {
text-align: right;
}
.commentHeader {
display: flex;
justify-content: space-between;
/*justify-content: space-between;*/
}
.commentHeader > div {
@ -38,7 +75,7 @@
}
.ownComment {
border-left: 2px solid green;
border-left: 5px dotted azure;
}
.showHide {
@ -58,5 +95,5 @@
}
.adminComment {
border-left: 2px solid yellow;
border-left: 3px solid gold;
}

View file

@ -4,37 +4,23 @@ import Modal from './modal'
import styles from './composer.module.css'
function FileUploader({ onChange }) {
return (
<div className={styles.inputArea}>
<div className={styles.textTitle}>Fájl csatolása</div>
<input
className={styles.fileInput}
type="file"
name="file"
onChange={onChange}
/>
</div>
)
}
export default function Composer({ onSubmit }) {
const [editorShowing, setEditorShowing] = useState(false)
const [val, setVal] = useState('')
const [type, setType] = useState('public')
const [title, setTitle] = useState('')
const [file, setFile] = useState()
return (
<>
<div
onClick={() => {
setEditorShowing(true)
}}
className={styles.new}
>
Új bejegyzés / feedback
</div>
<center>
<div
onClick={() => {
setEditorShowing(true)
}}
className={styles.new}
>
Bejegyzés írása...
</div>
</center>
{editorShowing && (
<Modal
closeClick={() => {
@ -42,73 +28,39 @@ export default function Composer({ onSubmit }) {
}}
>
<div className={styles.container}>
{type !== 'private' && (
<input
placeholder={'Téma'}
value={title}
onChange={(e) => {
setTitle(e.target.value)
}}
/>
)}
<input
placeholder={'Téma...'}
required
value={title}
onChange={(e) => {
setTitle(e.target.value)
}}
/>
<textarea
placeholder={'...'}
placeholder={'Írj ide valamit...'}
required
value={val}
onChange={(e) => {
setVal(e.target.value)
}}
/>
{type === 'private' && (
<FileUploader
onChange={(e) => {
setFile(e.target.files[0])
}}
/>
)}
<div className={styles.typeSelector}>
<div>Post típusa:</div>
<div title="Minden felhasználó látja főoldalon, és tudnak rá válaszolni">
<input
onChange={(e) => {
setType(e.target.value)
}}
type="radio"
name="type"
value="public"
defaultChecked
/>
Publikus
</div>
<div title="Csak a weboldal üzemeltetője látja">
<input
onChange={(e) => {
setType(e.target.value)
}}
type="radio"
name="type"
value="private"
/>
Privát
</div>
<div className={styles.tip}>
(Tartsd egered opciókra több infóért)
</div>
</div>
<div className={'actions'}>
<div className={styles.composerAction}>
<span
onClick={() => {
onSubmit(type, title, val, file)
if (!title) {
alert('Üres a tartalom!')
return
}
if (!val) {
alert('Üres a téma!')
return
}
onSubmit(title, val)
setEditorShowing(false)
}}
>
Post
</span>
<span
onClick={() => {
setEditorShowing(false)
}}
>
Cancel
Posztolás
</span>
</div>
</div>

View file

@ -1,32 +1,97 @@
.container {
display: flex;
flex-flow: column;
max-width: 400px;
width: 900px;
}
.container > input,
.container > textarea {
margin: 5px 0px;
padding: 4px;
width: 97%;
border-color: #5d5f61;
background-color: #1f2021;
border-radius: 5px;
font-size: 15px;
}
.container > textarea {
margin-left: 1px;
width: 99%;
resize: none;
}
.typeSelector {
display: inline-flex;
text-align: center;
margin-left: 6px;
}
.typeSelector > div {
display: flex;
align-items: center;
}
.tip {
font-size: 10px;
margin: 0px 10px;
font-size: 11px;
font-style: italic;
text-align: center;
margin: 15px 0px 2px 2px;
}
.new {
display: flex;
justify-content: center;
margin: 10px 0px;
height: 19px;
width: 30%;
display: flex;
background-color: var(--hoover-color);
border: none;
color: var(--text-color);
padding: 10px;
text-align: center;
border: 2px dashed var(--text-color);
border-radius: 3px;
font-size: 15px;
margin: 8px;
cursor: pointer;
text-shadow: 1px 1px 8px black;
transition: width 0.5s, height 0.5s, ease-in 0.5s;
text-align: center;
text-decoration: none;
}
.new:hover {
background-color: var(--hoover-color);
text-shadow: 2px 2px 8px black;
transition: width 0.5s, height 0.5s, ease-out 0.5s;
background-color: var(--text-color);
font-weight: bold;
color: black;
}
.composerAction {
display: flex;
align-items: center;
justify-content: center;
width: 36%;
height: 100%;
}
.composerAction > span {
margin: 2px 2px;
padding: 0px 10px;
border: 1px solid #444;
border-radius: 5px;
cursor: pointer;
user-select: none;
font-size: 15px;
margin-top: 14.5px !important;
padding-top: 4px !important;
transform: translate(109%, 10%);
transition: width 0.5s, height 0.5s, ease-in 0.5s;
}
.composerAction > span:hover {
background-color: var(--hoover-color);
transition: width 0.5s, height 0.5s, ease-out 0.5s;
color: var(--text-color);
text-shadow: 2px 1.5px 10px black;
}

View file

@ -1,23 +1,33 @@
.container {
display: flex;
flex-flow: column;
margin-top: 8px;
margin-bottom: 0px;
margin-right: 22%;
margin-left: 22%;
padding: 5px;
}
.listItem {
display: flex;
justify-content: center;
padding: 5px;
text-align: center;
padding-top: 10px;
padding-bottom: 10px;
margin: 3px;
cursor: pointer;
color: white;
color: #a1a1a1;
transition: width 0.5s, height 0.5s, ease-in 0.5s;
}
.listItem:hover {
background-color: var(--hoover-color);
transition: width 0.5s, height 0.5s, ease-out 0.5s;
text-shadow: 2px 1.5px 10px black;
color: var(--text-color);
}
.text {
text-align: center;
color: gainsboro;
}

View file

@ -0,0 +1,22 @@
import React from 'react'
export default function ExternalLinkIcon({ size }) {
return (
<span style={{ margin: '4px', display: 'inline-block' }}>
<svg
width={size}
height={size}
viewBox={`0 0 24 24`}
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
<polyline points="15 3 21 3 21 9"></polyline>
<line x1="10" y1="14" x2="21" y2="3"></line>
</svg>
</span>
)
}

View file

@ -0,0 +1,106 @@
import React, { useState } from 'react'
import styles from './feedbackArea.module.css'
import constants from '../constants.json'
function FileUploader({ onChange }) {
return (
<div className={styles.inputArea}>
<div className={styles.textTitle}>Fájl csatolása</div>
<input
className={styles.fileInput}
type="file"
name="file"
onChange={onChange}
/>
</div>
)
}
function submitFeedback(from, content, file) {
return new Promise((resolve) => {
const promises = [
fetch(constants.apiUrl + 'postfeedback', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
content: content,
from: from,
}),
}).then((res) => {
return res.json()
}),
]
if (file) {
const formData = new FormData() // eslint-disable-line
formData.append('file', file)
promises.push(
fetch(constants.apiUrl + 'postfeedbackfile', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
},
body: formData,
}).then((res) => {
return res.json()
})
)
}
Promise.all(promises).then((res) => {
resolve(res)
})
})
}
export default function FeedbackArea({ from, allowFile }) {
const [feedback, setFeedback] = useState('')
const [file, setFile] = useState()
return (
<div className={styles.inputArea}>
<center>
<textarea
placeholder={'Ide kezdhetsz el írni...'}
onChange={(event) => setFeedback(event.target.value)}
value={feedback}
className={styles.contactFeedback}
/>
{allowFile && (
<div className={styles.fileContainer}>
<FileUploader
onChange={(e) => {
setFile(e.target.files[0])
}}
/>
</div>
)}
</center>
<center>
<div className={`${styles.send} buttonContainer`}>
<div
onClick={() => {
if (feedback) {
submitFeedback(from, feedback, file).then(() => {
alert('Üzenet elküldve!')
setFeedback('')
})
} else {
alert('Üresen hagytad az üzenet szövegének helyét!')
}
}}
>
Küldés
</div>
</div>
</center>
</div>
)
}

View file

@ -0,0 +1,25 @@
.contactFeedback {
color: var(--text-color);
background-color: var(--background-color);
font-size: 14px;
width: 600px;
box-sizing: border-box;
height: 150px;
resize: none;
margin-top: 15px;
padding: 5px;
}
.send {
width: 20%;
margin-bottom: 60px;
}
.inputArea {
display: block;
}
.fileContainer {
width: 400px;
margin: 5px;
}

View file

@ -9,27 +9,120 @@ import tabs from '../data/tabs.json'
import constants from '../constants.json'
import BB from './b.js'
import styles from './layout.module.css'
const renderSnow = () => {
const date = new Date()
// if its december, and date is more than 5
return date.getMonth() === 11 && date.getDate() > 5
}
function Donate() {
return (
<div>
<div className={styles.modalHead}>Donate</div>
<div style={{ textAlign: 'justify', margin: '8px' }}>
Paypalen és Patreonon látszódik a neved, de Patreonon könnyen meg lehet
változtatni. Ha név nélkül szeretnél adakozni, akkor írd át egy
nickname-re. De akárhova adakozol, a neved sehova se lesz kiadva, és nem
látja 3. személy.
</div>
<div className={styles.donateLogoContainer}>
<a
href={`${constants.siteUrl}patreon`}
target="_blank"
rel="noreferrer"
>
<img
style={{
border: 'none',
margin: '15px',
}}
src={`${constants.siteUrl}img/patreon-logo.png`}
/>
</a>
<a href={`${constants.siteUrl}donate`} target="_blank" rel="noreferrer">
<img
style={{
border: 'none',
margin: '15px',
}}
src={`${constants.siteUrl}img/paypal-logo.png`}
/>
</a>
</div>
</div>
)
}
function MessageButton({
userSpecificMotd,
setShowMotdModal,
refetchGlobalData,
}) {
return (
<div className="msgs">
<div
onClick={() => {
if (!userSpecificMotd) {
return
}
setShowMotdModal(true)
if (userSpecificMotd.seen) {
return
}
fetch(constants.apiUrl + 'infos', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
userSpecificMotdSeen: true,
}),
})
.then((resp) => {
return resp.json()
})
.then(() => {
refetchGlobalData()
})
}}
style={{ cursor: userSpecificMotd ? 'pointer' : 'default' }}
title={
userSpecificMotd && !userSpecificMotd.seen ? 'Új üzeneted van!' : ''
}
>
{userSpecificMotd && !userSpecificMotd.seen ? '📬' : '📭'}
</div>
</div>
)
}
export default function Layout({
children,
route,
router,
globalData,
refetchGlobalData,
}) {
let href = route
const [sidebarOpen, setSidebarOpen] = useState(true)
const [windowSize, setWindowSize] = useState([100, 200])
const [showMotdModal, setShowMotdModal] = useState(false)
const [donateShowing, setDonateShowing] = useState(false)
const [showNewMsgModal, setShowNewMsgModal] = useState(true)
const userId = globalData.userId
const userSpecificMotd = globalData.userSpecificMotd
useEffect(
() => {
setDonateShowing(!!router.query.donate)
},
[router.query.donate]
)
let href = router.route
if (href === '/' || href === '') {
href = 'index'
}
@ -80,23 +173,33 @@ export default function Layout({
<div />
</span>
<div className="sidebarheader">
<img
style={{ maxWidth: '100%' }}
src={`${constants.siteUrl}img/frylabs-logo_small_transparent.png`}
alt="Frylabs"
/>
<Link href="/">
<a>
<img
style={{
maxWidth: '100%',
border: 'none',
margin: '1px',
}}
src={`${
constants.siteUrl
}img/frylabs-logo_small_transparent.png`}
alt="FryLabs"
/>
</a>
</Link>
</div>
</div>
{sidebarOpen ? (
<>
<div id="sideBarLinks" className="sidebarLinks">
<div id="sideBarLinks" className={styles.sidebarLinks}>
{Object.keys(tabs).map((key) => {
const item = tabs[key]
return (
<Link href={item.href} key={key}>
<a
onClick={closeSideBar}
className={href.includes(key) ? 'active' : undefined}
className={href.includes(key) ? styles.active : undefined}
id={item.id || undefined}
>
{item.text}
@ -105,68 +208,26 @@ export default function Layout({
)
})}
<a
href={`/dataeditor`}
onClick={closeSideBar}
target="_blank"
rel="noreferrer"
title={
'Kérdések szerkesztése, törlése, beküldése, és kitöltetlen tesztek'
}
>
Kérdés szerkesztő / kitöltetlen tesztek
</a>
<a
href={`/donate`}
className="donate"
onClick={closeSideBar}
target="_blank"
rel="noreferrer"
onClick={() => {
closeSideBar()
setDonateShowing(true)
}}
>
Donate
</a>
</div>
<div className="userStatus">
<div className="msgs">
<div
onClick={() => {
if (!userSpecificMotd) {
return
}
setShowMotdModal(true)
if (userSpecificMotd.seen) {
return
}
fetch(constants.apiUrl + 'infos', {
method: 'POST',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
userSpecificMotdSeen: true,
}),
})
.then((resp) => {
return resp.json()
})
.then(() => {
refetchGlobalData()
})
}}
style={{ cursor: userSpecificMotd ? 'pointer' : 'default' }}
title={
userSpecificMotd && !userSpecificMotd.seen
? "You've got Mail!"
: ''
}
>
{userSpecificMotd && !userSpecificMotd.seen ? '📬' : '📭'}
</div>
<div title="User ID">UID: {userId || '...'}</div>
<div className={'userStatus'}>
<MessageButton
userSpecificMotd={userSpecificMotd}
setShowMotdModal={setShowMotdModal}
refetchGlobalData={refetchGlobalData}
/>
<div className={'uid'} title="User ID">
UID: {userId || '...'}
</div>
<div
className="logout"
className={'logout'}
title="Kijelentkezés"
onClick={() => {
fetch(constants.apiUrl + 'logout', {
method: 'GET',
@ -182,31 +243,38 @@ export default function Layout({
Logout
</div>
</div>
{showMotdModal ? (
<Modal
closeClick={() => {
setShowMotdModal(false)
}}
>
<div style={{ textAlign: 'center' }}>Üzenet admintól:</div>
<div
dangerouslySetInnerHTML={{ __html: userSpecificMotd.msg }}
/>
</Modal>
) : null}
</>
) : null}
</div>
<div className="content">{children}</div>
{donateShowing ? (
<Modal
closeClick={() => {
setDonateShowing(false)
}}
>
<Donate />
</Modal>
) : null}
{showMotdModal ? (
<Modal
closeClick={() => {
setShowMotdModal(false)
}}
>
<div style={{ textAlign: 'center' }}>Üzenet admintól:</div>
<div dangerouslySetInnerHTML={{ __html: userSpecificMotd.msg }} />
</Modal>
) : null}
{userSpecificMotd && !userSpecificMotd.seen && showNewMsgModal ? (
<Modal
closeClick={() => {
setShowNewMsgModal(false)
}}
>
Új üzeneted van, kattints 📬-ra bal alul megtekintéséhez!
Új üzeneted van, kattints a 📬-ra bal alul a megtekintéséhez!
</Modal>
) : null}
<div className="content">{children}</div>
<BB />
</div>
)

View file

@ -0,0 +1,49 @@
.modalHead {
text-align: center;
font-size: 22px;
font-weight: 700;
padding-top: 0px;
margin-top: 0px;
padding-bottom: 20px;
letter-spacing: 3px;
}
.donateLogoContainer {
display: flex;
justify-content: center;
}
.donateLogoContainer img {
max-width: 100px;
margin: 5px;
border-radius: 5px;
}
.sidebarLinks > * {
display: block;
text-align: center;
color: black;
font-size: 108%;
padding: 14px;
margin-top: 4px;
margin-bottom: 4px;
text-decoration: none;
color: var(--bright-color);
transition: width 0.5s, height 0.5s, ease-in 0.5s;
cursor: pointer;
}
.sidebarLinks a.active {
border: 0.5px solid var(--text-color);
color: white;
text-shadow: 2px 2px 8px black;
font-weight: bold;
}
.sidebarLinks a:hover:not(.active) {
background-color: var(--text-color);
color: black;
font-weight: bold;
text-shadow: 2px 2px 8px black;
transition: width 0.5s, height 0.5s, ease-out 0.5s;
}

View file

@ -12,7 +12,9 @@
display: flex;
align-items: stretch;
max-height: 80%;
width: 80%;
width: 50%;
max-width: 52%;
width: auto;
position: fixed;
background: var(--background-color);
height: auto;
@ -20,24 +22,27 @@
left: 50%;
transform: translate(-50%, -50%);
border-radius: 8px;
padding: 20px 30px;
cursor: auto;
padding-top: 38px;
padding-bottom: 25px;
padding-right: 14px;
padding-left: 16px;
cursor: default;
}
.close {
cursor: pointer;
font-size: 18px;
position: absolute;
top: 10px;
font-size: 16.5px;
position: fixed;
top: 5px;
right: 10px;
margin-bottom: 10px;
text-align: right;
display: inline;
}
.children {
max-height: 100%;
width: 100%;
overflow-y: scroll;
overflow-x: hidden;
overflow-y: auto;
overflow-x: auto;
}

View file

@ -17,13 +17,17 @@ export default function NewsEntry({
const { reacts, title, content, user, comments, date, admin } = newsItem
return (
<div>
<div className={`${styles.newsContainer} ${admin && styles.adminPost}`}>
<div className={styles.newsRoot}>
<div
className={`${styles.newsContainer} ${admin &&
styles.adminPost} ${!admin && styles.userPost} ${uid == user &&
styles.ownPost} ${uid == user && admin && styles.adminPost}`}
>
<div className={styles.newsHeader}>
<div className={styles.newsTitle}>{title}</div>
<div className={styles.user}>
<div className={styles.userinfo}>
<div>User #{user}</div>
<div className={styles.newsDate}>{date}</div>
<div className={styles.newsDate}> @ {date}</div>
</div>
</div>
{admin ? (
@ -38,11 +42,12 @@ export default function NewsEntry({
<div className={'actions'}>
{uid === user ? (
<span
className={styles.delete_bttn}
onClick={() => {
onPostDelete()
}}
>
Delete
Törlés
</span>
) : null}
<ReactButton
@ -62,8 +67,6 @@ export default function NewsEntry({
onDelete={onDelete}
comments={comments}
/>
<hr />
<hr />
</div>
)
}

View file

@ -1,32 +1,51 @@
.newsBody {
margin: 0px 5px;
font-size: 18px;
margin: 0px 12px;
color: #fff;
white-space: pre-line;
text-align: justify;
}
.newsTitle {
font-size: 20px;
font-size: 25px;
color: var(--text-color);
margin: 0px 5px;
margin: 0px 11px;
padding-bottom: 10px;
}
.newsDate {
margin: 0px 5px;
margin-right: 12px;
margin-left: 14px;
}
.newsContainer {
margin: 5px 5px;
}
.delete_bttn {
font-size: 15px;
margin-top: 12px !important;
margin-left: 2px !important;
margin-right: 5px !important;
padding-top: 4px !important;
transition: width 0.5s, height 0.5s, ease-in 0.5s;
}
.adminPost {
border-left: 2px solid yellow;
border-radius: 5px;
border-left: 4px solid var(--text-color);
}
.userPost {
border-left: 4px solid azure;
}
.ownPost {
border-left: 4px dotted azure;
}
.newsContainer img {
max-width: 100%;
min-width: 200px;
border: 2px solid white;
}
.newsHeader {
@ -35,7 +54,17 @@
align-items: center;
}
.user {
.userinfo {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.newsRoot {
background-color: var(--dark-color);
margin-left: 8px;
margin-right: 8px;
margin-bottom: 16px;
margin-top: 16px;
padding: 10px;
}

View file

@ -23,7 +23,7 @@ function useOutsideAlerter(ref, action) {
function ExistingReacts({ existingReacts, onClick, uid }) {
return (
<div className={styles.reactionContainer}>
<div>React</div>
<div className={styles.react_bttn}>Reakció</div>
{existingReacts &&
Object.keys(existingReacts).map((key) => {
const currReact = existingReacts[key]
@ -33,7 +33,7 @@ function ExistingReacts({ existingReacts, onClick, uid }) {
}
return (
<div
title={currReact.join(', ')}
title={`'${key}': ${currReact.join(', ')}`}
className={`${currReact.includes(uid) && styles.reacted}`}
key={key}
onClick={(e) => {

View file

@ -1,6 +1,8 @@
.reactionContainer {
display: flex;
flex-wrap: wrap;
margin-top: 15px !important;
margin: 4px 2px;
}
.reactionContainer > input {
@ -20,10 +22,13 @@
border-radius: 6px;
cursor: pointer;
user-select: none;
transition: width 0.5s, height 0.5s, ease-in 0.5s;
}
.reactionContainer > div:hover {
background-color: var(--hoover-color);
color: gainsboro;
transition: width 0.5s, height 0.5s, ease-out 0.5s;
}
.break {
@ -32,9 +37,20 @@
}
.reacted {
color: yellow;
background-color: var(--text-color);
color: black;
}
.reacted:hover {
background-color: #96810b !important;
color: #42423e !important;
}
.reactContainer {
display: inline-block;
}
.react_bttn {
font-size: 15px;
padding-top: 3px !important;
}

View file

@ -1,20 +0,0 @@
import React from 'react'
import styles from './sidebar.module.css'
export default function Sidebar(props) {
const { onClose } = props
return (
<div className={styles.container}>
<div
className={styles.closeBorder}
onClick={() => {
onClose()
}}
>
{'⏩'}
</div>
<div className={styles.content}>{props.children}</div>
</div>
)
}

View file

@ -1,31 +0,0 @@
.container {
background-color: var(--background-color);
border-left: 3px solid #99f;
top: 0;
right: 0px;
height: 100%;
width: 400px;
max-width: 100%;
position: fixed;
display: flex;
}
.closeBorder {
font-size: 14px;
padding: 2px;
cursor: pointer;
display: flex;
flex-flow: column;
justify-content: center;
color: white;
background-color: #222;
}
.closeBorder:hover {
background-color: #333;
}
.content {
width: 100%;
}

View file

@ -1,6 +1,6 @@
import constants from '../constants.json'
export default function Sleep (props) {
export default function Sleep(props) {
const hours = new Date().getHours()
if (hours < 4 || hours > 23) {
return (
@ -9,9 +9,11 @@ export default function Sleep (props) {
<img
style={{
margin: '10px',
width: '300px'
width: '300px',
border: '2px solid white',
}}
src={constants.siteUrl + 'img/aludni.jpeg'}
title="Ezt a képet azert látod, mert ilyenkor már igazán nem ezen az oldalon kellene járnod"
/>
</center>
</div>

View file

@ -3,20 +3,7 @@
padding: 4px;
}
.groupContainer {
display: flex;
}
.group {
padding: 8px 16px;
cursor: pointer;
user-select: none;
}
.group:hover {
background-color: #333;
}
.selectedGroup {
background-color: #444;
.todoButtons {
height: 38px;
text-wrap: normal;
}

View file

@ -11,6 +11,7 @@
.categoryName {
text-align: center;
margin-top: 85px !important;
margin: 5px 0px;
font-size: 16px;
font-weight: bold;

View file

@ -11,7 +11,7 @@ export default function TodoCard(props) {
<div
className={styles.card}
style={{
border: `2px solid ${group || '#99f'}`,
border: `1px solid ${categories[category].color || '#f2cb05'}`,
}}
onClick={() => {
onClick(props.cardData)
@ -27,7 +27,7 @@ export default function TodoCard(props) {
backgroundColor: categories[category].color,
color: 'white',
borderRadius: '2px',
padding: '0px 2px',
padding: '3px',
}}
>
{categories[category].name}
@ -35,9 +35,9 @@ export default function TodoCard(props) {
</div>
<div className={styles.numbers}>
<div className={`${voted && styles.voted}`}>
<div>{`Votes: ${votes.length}`}</div>
<div>{`Szavazatok: ${votes.length}`}</div>
</div>
<div>{points}</div>
<div>{`Nehézség: ${points}`}</div>
</div>
</div>
)

View file

@ -1,12 +1,14 @@
.card {
border-radius: 2px;
padding: 5px;
margin: 6px 3px;
border-radius: 5px;
padding: 7px;
margin: 8px 4px;
cursor: pointer;
background-color: #171616;
}
.voted {
color: #cf9;
color: var(--text-color);
font-weight: 600;
}
.card:hover {
@ -15,7 +17,7 @@
}
.card > div {
margin: 5px 0px;
margin: 6px 4px;
}
.description {
@ -25,7 +27,7 @@
}
.category {
font-size: 10px;
font-size: 12px;
white-space: nowrap;
}
@ -39,6 +41,6 @@
}
.id {
margin: 0px 3px;
margin: 1px 6px 1px 1px;
color: #999;
}

View file

@ -6,6 +6,8 @@ export default function TodoRow(props) {
const { categories, userId, onClick } = props
const { name, category, votes, id, group } = props.rowData
const voted = votes.includes(userId)
const borderColor =
categories[category].borderColor || categories[category].color
return (
<div
@ -14,7 +16,8 @@ export default function TodoRow(props) {
}}
className={styles.row}
style={{
border: `2px solid ${group || '#99f'}`,
border: `2px dashed ${borderColor || 'white'}`,
borderRadius: '3px',
}}
>
<div className={styles.id}>{`#${id}`}</div>
@ -23,11 +26,11 @@ export default function TodoRow(props) {
<div
style={{
wordBreak: 'break-all',
fontSize: '10px',
fontSize: '12px',
backgroundColor: categories[category].color,
color: 'white',
borderRadius: '2px',
padding: '0px 2px',
padding: '3px',
}}
>
{categories[category].name}
@ -35,7 +38,7 @@ export default function TodoRow(props) {
</div>
<div
className={`${styles.votes} ${voted && styles.voted}`}
>{`Votes: ${votes.length}`}</div>
>{`Szavazatok: ${votes.length}`}</div>
</div>
)
}

View file

@ -1,10 +1,9 @@
.row {
display: flex;
border: 2px solid #99f;
border-radius: 2px;
padding: 5px;
margin: 6px 3px;
border-radius: 5px;
padding: 7px;
margin: 8px 4px;
cursor: pointer;
background-color: #171616;
}
.row:hover {
@ -13,12 +12,12 @@
}
.row > div {
margin: 0px 5px;
margin: 6px 4px;
}
.id {
flex: 0 20px;
margin: 0px 3px;
margin: 1px 6px 1px 1px;
color: #999;
}
@ -30,10 +29,14 @@
}
.votes {
display: flex;
justify-content: space-between;
font-size: 12px;
}
.voted {
color: #cf9;
color: var(--text-color);
font-weight: 600;
}
.catName {

View file

@ -25,18 +25,18 @@ export default function Todos({
// TODO: hide vote button if not voteable
return (
<div className={styles.container}>
<div className={styles.name}>
<div className={styles.title}>
<span className={styles.id}>#{id}</span>
{name}
<div className={'subtitle'} style={{ textAlign: 'left' }}>
{name}
</div>
<span
style={{
margin: '0px 5px',
fontSize: '10px',
backgroundColor: categories[category].color,
color: 'white',
fontSize: '12px',
borderRadius: '2px',
padding: '0px 2px',
whiteSpace: 'nowrap',
padding: '3px',
}}
>
{categories[category].name}
@ -45,7 +45,7 @@ export default function Todos({
<hr />
{group && (
<div className={styles.row}>
<div>Csoport:</div>
<div style={{ color: group }}>Csoport:</div>
<div style={{ color: group }}>
{namedGroups[group] ? namedGroups[group].name : group}
</div>
@ -64,31 +64,47 @@ export default function Todos({
<div>{votes.length}</div>
</div>
{gitlink && (
<div className={styles.row}>
<a href={gitlink} target={'_blank'} rel={'noreferrer'}>
Git link
</a>
</div>
<center>
<div>
<a
href={gitlink}
style={{ textDecoration: 'none' }}
target={'_blank'}
rel={'noreferrer'}
>
Git link
</a>
</div>
</center>
)}
<hr />
{description && (
<>
<div className={styles.title}>Leírás</div>
<div dangerouslySetInnerHTML={{ __html: description }} />
<div className={'subtitle'} style={{ textAlign: 'left' }}>
Leírás
</div>
<div
className={styles.description}
dangerouslySetInnerHTML={{ __html: description }}
/>
<hr />
</>
)}
<div className={styles.category}></div>
<div className={styles.category} />
{voteable && (
<div
className={`${styles.button} ${votes.includes(userId) &&
styles.voted}`}
onClick={() => {
voteOn(card)
}}
>
{votes.includes(userId) ? 'Szavazat visszavonása' : 'Szavazás'}
</div>
<center>
<div className={'buttonContainer'} style={{ width: '40%' }}>
<div
className={`${styles.button} ${votes.includes(userId) &&
styles.voted}`}
onClick={() => {
voteOn(card)
}}
>
{votes.includes(userId) ? 'Szavazat visszavonása' : 'Szavazás'}
</div>
</div>
</center>
)}
</div>
)

View file

@ -9,14 +9,17 @@
}
.title {
color: #99f;
color: var(--text-color);
font-size: 18px;
}
.name {
color: #99f;
.title {
color: var(--text-color);
font-size: 20px;
margin: 20px 0px;
margin: 5px 0px;
}
.name {
}
.category {
@ -25,18 +28,26 @@
}
.id {
font-size: 16px;
font-size: 20px;
margin: 0px 3px;
color: #999;
}
.description {
overflow-x: auto;
overflow-y: auto;
max-height: calc(38vh);
margin: 5px;
}
.row {
display: flex;
justify-content: space-between;
margin: 2px 10px;
}
.button {
border: 2px solid #99f;
border: 2px solid var(--text-color);
border-radius: 3px;
text-align: center;
cursor: pointer;

View file

@ -20,29 +20,31 @@ export default function TodoBoard(props) {
return (
<div className={styles.container} key={key}>
<div className={styles.title}>{table.name}</div>
{tableCards.map((card, i) => {
const shouldHide =
card.state !== key ||
(selectedGroup !== null &&
selectedGroup !== 'uncat' &&
card.group !== selectedGroup) ||
(selectedGroup === 'uncat' && card.group !== undefined)
<div className={styles.scroll}>
{tableCards.map((card, i) => {
const shouldHide =
card.state !== key ||
(selectedGroup !== null &&
selectedGroup !== 'uncat' &&
card.group !== selectedGroup) ||
(selectedGroup === 'uncat' && card.group !== undefined)
if (shouldHide) {
return null
}
if (shouldHide) {
return null
}
return (
<TodoRow
onClick={onClick}
key={i}
type={key}
rowData={card}
userId={userId}
categories={categories}
/>
)
})}
return (
<TodoRow
onClick={onClick}
key={i}
type={key}
rowData={card}
userId={userId}
categories={categories}
/>
)
})}
</div>
</div>
)
})}

View file

@ -11,6 +11,8 @@
.container {
flex-direction: column;
margin-top: 30px;
margin-bottom: 50px;
}
.title {
@ -18,3 +20,9 @@
font-size: 20px;
font-weight: bold;
}
.scroll {
max-height: 500px;
overflow-y: auto;
overflow-x: hidden;
}

View file

@ -4,11 +4,9 @@ import LoadingIndicator from '../LoadingIndicator.js'
import TodoBoard from './todoBoard.js'
import TodoTable from './todoTable.js'
import TodoSidebar from './todoSidebar.js'
import Sidebar from '../sidebar.js'
import Modal from '../modal.js'
import styles from './todo.module.css'
// import styles from './todos.module.css'
import constants from '../../constants.json'
const byVotes = (a, b) => {
@ -26,7 +24,6 @@ export default function Todos() {
const [selectedGroup, setSelectedGroup] = useState(null)
useEffect(() => {
console.info('Fetching todos')
fetch(`${constants.apiUrl}todos`, {
credentials: 'include',
})
@ -96,13 +93,14 @@ export default function Todos() {
const sg = namedGroups[selectedGroup]
return (
<>
<div className={styles.groupContainer}>
<div className={'buttonContainer'}>
{groups.map((group) => {
const namedGroup = namedGroups[group]
return (
<div
className={`${styles.group} ${group === selectedGroup &&
styles.selectedGroup}`}
className={`${'buttonContainer'}, ${
styles.todoButtons
} ${group === selectedGroup && 'activeButton'}`}
key={group}
onClick={() => {
setSelectedGroup(group)
@ -113,8 +111,9 @@ export default function Todos() {
)
})}
<div
className={`${styles.group} ${'uncat' === selectedGroup &&
styles.selectedGroup}`}
className={`${'buttonContainer'}, ${
styles.todoButtons
} ${'uncat' === selectedGroup && 'activeButton'}`}
onClick={() => {
setSelectedGroup('uncat')
}}
@ -122,7 +121,8 @@ export default function Todos() {
{'Kategorizálatlan'}
</div>
<div
className={styles.group}
className={`${'buttonContainer'}, ${styles.todoButtons} ${null ===
selectedGroup && 'activeButton'}`}
onClick={() => {
setSelectedGroup(null)
}}
@ -140,8 +140,8 @@ export default function Todos() {
return (
<div>
{sidebarCard && (
<Sidebar
onClose={() => {
<Modal
closeClick={() => {
setSidebarCard(null)
}}
>
@ -165,15 +165,10 @@ export default function Todos() {
})
}}
/>
</Sidebar>
</Modal>
)}
{renderGrouper()}
<div
style={{
// width: '100%',
marginRight: sidebarCard ? 380 : 0,
}}
>
<div>
<TodoBoard
columns={cols}
cards={bCards}