This commit is contained in:
2026-06-24 15:10:50 +02:00
commit a3e7512f95
212 changed files with 212927 additions and 0 deletions
@@ -0,0 +1,90 @@
import { useState, useEffect } from "react"
import Select from 'react-select'
import { customTheme } from "../../constants"
import { isObject } from '../../helpers/isObject'
import './style.css'
export const EditableSelector = (props) => {
const { defaultSelectValue, onUpdate, style, options, defaultValue, multi, components, styles, placeholder, isSearchable } = props
const [value, setValue] = useState(defaultValue)
useEffect(() => {
setValue(defaultValue)
}, [defaultValue])
useEffect(() => {
onUpdate(value)
}, [value])
const updateValue = (val) => {
if (multi) {
if (Array.isArray(defaultValue)) {
if (val != undefined && val != null) {
if (val.length == 0) {
setValue([])
}
else {
let tempValue = []
val.forEach(item => {
tempValue.push(item.value)
})
setValue(tempValue)
}
}
}
else if (isObject(defaultValue)) {
if (val != undefined && val != null) {
if (val.length == 0) {
setValue({})
}
else {
let newValue = {}
val.forEach(item => {
Object.keys(item.value).forEach(key => {
newValue[`${key}`] = item.value[`${key}`]
})
})
setValue(newValue)
}
}
}
}
else {
if (val == undefined || val == null) {
setValue("")
}
else {
setValue(val.value)
}
}
}
return (
<>
{
multi ?
<Select className="editable_selector"
isMulti={true}
theme={customTheme}
style={style}
options={options}
defaultValue={defaultSelectValue}
onChange={updateValue}
components={components}
styles={styles}
isSearchable={isSearchable} //false
placeholder={placeholder}
/>
:
<Select className="editable_selector"
isMulti={false}
theme={customTheme}
style={style}
options={options}
defaultValue={defaultSelectValue}
onChange={updateValue}
isClearable={true}
components={components}
styles={styles}
placeholder={placeholder}
isSearchable={isSearchable}
/>
}
</>
)
}
@@ -0,0 +1,7 @@
.editable_selector {
width: 100%;
}
.editable_selector input {
font-family: "Lora", serif;
font-size: 0.7rem !important;
}
@@ -0,0 +1,25 @@
import { useState, useEffect, useRef } from "react"
import _ from "lodash"
import './style.css'
export const EditableText = (props) => {
const { defaultValue, onUpdate, style } = props;
const [value, setValue] = useState(defaultValue)
const onUpdateWithDebounce = _.debounce(onUpdate, 500)
const inputRef = useRef(null)
const unfocusInput = (e) => {
if (e.keyCode == 13) inputRef.current.blur();
}
useEffect(() => {
setValue(defaultValue)
}, [defaultValue])
useEffect(() => {
onUpdateWithDebounce(value);
}, [value])
useEffect(() => {
inputRef && inputRef.current &&
inputRef.current.addEventListener('keyup', unfocusInput);
}, [inputRef])
return (
<input ref={inputRef} className="editable_text" type="text" spellCheck={false} value={value} style={style} onChange={(e) => setValue(e.target.value)} />
)
}
@@ -0,0 +1,13 @@
.editable_text {
border: 1px solid transparent;
outline: none;
width: 100%;
/* display: flex;
justify-content: center;
align-items: center; */
font-family: "Lora", serif;
}
/* .editable_text:focus {
border: 1px solid black;
outline: none;
} */
@@ -0,0 +1,26 @@
import { useState, useEffect, useRef } from "react"
import TextareaAutosize from "react-textarea-autosize"
import _ from "lodash"
import './style.css'
export const EditableTextArea = (props) => {
const { defaultValue, onUpdate, style } = props;
const [value, setValue] = useState(defaultValue)
const onUpdateWithDebounce = _.debounce(onUpdate, 500)
const inputRef = useRef(null)
const unfocusInput = (e) => {
if (e.keyCode == 13) inputRef.current.blur();
}
useEffect(() => {
setValue(defaultValue)
}, [defaultValue])
useEffect(() => {
onUpdateWithDebounce(value);
}, [value])
useEffect(() => {
inputRef && inputRef.current &&
inputRef.current.addEventListener('keyup', unfocusInput);
}, [inputRef])
return (
<TextareaAutosize spellCheck={false} ref={inputRef} className="editable_text_area" type="text" value={value} style={style} onChange={(e) => setValue(e.target.value)} />
)
}
@@ -0,0 +1,13 @@
.editable_text_area {
border: 1px solid transparent;
outline: none;
width: 100%;
height: 100%;
font-family: "Lora", serif;
resize: none;
overflow: hidden;
}
/* .editable_text_area:focus {
border: 1px solid black;
outline: none;
} */
@@ -0,0 +1,68 @@
import { isBrowser } from 'react-device-detect'
import { useStylesPC } from './stylePC'
import { useStylesMobile } from './styleMobile'
export const InformationContainer = () => {
const classesPC = useStylesPC()
const classesMobile = useStylesMobile()
return (
<div
className={
isBrowser
? classesPC.informationContainer
: classesMobile.informationContainer
}
>
<div className='topLane'>
<button>
<a href='/about'>ABOUT</a>
</button>
</div>
<div className='midLane'>
{/* I no longer use Facebook. */}
<a href='about:blank'>
<button>
<i className='fab fa-facebook-square' />
</button>
</a>
{/* I no longer use Twitter. */}
<a href='about:blank'>
<button>
<i className='fab fa-twitter-square' />
</button>
</a>
<a href='https://gitea.elliot-at-zuri.ch/admin'>
<button>
<i className='fab fa-github-square' />
</button>
</a>
{/* I no longer use Patreon. */}
<a href='about:blank'>
<button>
<i className='fab fa-patreon' />
</button>
</a>
</div>
<div className='botLane'>
<div className='introText'>
<div>
Debaters' toolkit is an open-source software licensed under the{' '}
<a href='https://choosealicense.com/licenses/mit/'>
<span>MIT license</span>
</a>{' '}
that aims to be useful to all debaters. Our motions are collected
from various sources. While we strive to update the database as
regularly as possible, we cannot warrant absolute correctness for
all motions. If you have any issue with our content or detect any
bug in our app, please contact us at{' '}
<a href='mailto: quyanh.nguyen@helsinki'>
<span>quyanh.nguyen@helsinki</span>
</a>
.
</div>
<div className='aboutSubHeader'>© 2021 [Quy Anh] «Elliot» Nguyen.</div>
</div>
</div>
</div>
)
}
@@ -0,0 +1,128 @@
.informationContainer {
width: 100%;
display: flex;
flex-direction: column;
background-color: #282a35 !important;
height: 38vh;
min-height: 14.3rem;
font-family: "Source Sans Pro", sans-serif;
margin-top: auto;
.topLane {
width: 100%;
height: 20%;
display: flex;
justify-content: center;
align-items: center;
button {
padding: 0.3rem;
border-radius: 5px;
border: 1px solid white;
background-color: transparent;
font-weight: 500;
a {
text-decoration: none;
color: white;
}
}
button:hover {
background-color: white;
a {
color: black;
}
}
}
.midLane {
width: 100%;
height: 20%;
display: flex;
justify-content: center;
align-items: center;
button {
background-color: #282a35;
border: 1px solid white;
border-radius: 5px;
padding: 0.4rem;
margin-left: 0.2rem;
margin-right: 0.2rem;
i {
color: white;
font-size: 2rem;
}
}
button:hover {
background-color: white;
i {
color: black;
}
}
}
.botLane {
width: 100%;
height: 60%;
display: flex;
justify-content: center;
align-items: center;
.introText {
color: white !important;
display: flex;
flex-direction: column;
align-items: center;
width: 90%;
div {
margin: 0.5rem;
text-align: center;
a {
color: white;
span {
text-decoration: underline;
}
span:hover {
color: #4caf50;
}
}
}
}
}
}
@media only screen and (max-width: 379px) {
.informationContainer {
height: 47vh;
.topLane {
height: 12%;
}
.midLane {
height: 10%;
}
.botLane {
height: 78%;
margin-bottom: 0.5rem;
}
}
}
@media only screen and (max-width: 425px) and (min-width: 380px) {
.informationContainer {
height: 40vh;
.topLane {
height: 17%;
}
.midLane {
height: 13%;
}
.botLane {
height: 70%;
margin-bottom: 0.5rem;
}
}
}
@media only screen and (max-width: 768px) and (min-width: 426px) {
.informationContainer {
height: 25vh;
.topLane {
}
.midLane {
}
.botLane {
}
}
}
@@ -0,0 +1,126 @@
import { makeStyles } from '@mui/styles'
export const useStylesMobile = makeStyles({
'informationContainer': {
width: '100%',
display: 'flex',
flexDirection: 'column',
backgroundColor: '#282a35 !important',
height: '38vh',
minHeight: '14.3rem',
fontFamily: '"Source Sans Pro", sans-serif',
marginTop: 'auto',
'& .topLane': {
width: '100%',
height: '20%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
'& button': {
padding: '0.3rem',
borderRadius: '5px',
border: '1px solid white',
backgroundColor: 'transparent',
fontWeight: 500,
'& a': {
textDecoration: 'none',
color: 'white'
},
'&:hover': {
backgroundColor: 'white',
'& a': {
color: 'black'
}
}
}
},
'& .midLane': {
width: '100%',
height: '20%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
'& button': {
backgroundColor: '#282a35',
border: '1px solid white',
borderRadius: '5px',
padding: '0.4rem',
marginLeft: '0.2rem',
marginRight: '0.2rem',
'& i': {
color: 'white',
fontSize: '2rem'
},
'&:hover': {
backgroundColor: 'white',
'& i': {
color: 'black'
}
}
}
},
'& .botLane': {
width: '100%',
height: '60%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
'& .introText': {
color: 'white !important',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
width: '90%',
'& div': {
margin: '0.5rem',
textAlign: 'center',
'& a': {
color: 'white',
'& span': {
textDecoration: 'underline',
'&:hover': {
color: '#4caf50'
}
}
}
}
}
}
},
'@media only screen and (max-width: 379px)': {
'informationContainer': {
height: '47vh',
'& .topLane': {
height: '12%'
},
'& .midLane': {
height: '10%',
},
'& .botLane': {
height: '78%',
marginBottom: '0.5rem'
}
}
},
'@media only screen and (max-width: 425px) and (min-width: 380px)': {
'informationContainer': {
height: '40vh',
'& .topLane': {
height: '17%',
},
'& .midLane': {
height: '13%',
},
'& .botLane': {
height: '70%',
marginBottom: '0.5rem'
}
}
},
'@media only screen and (max-width: 768px) and (min-width: 426px)': {
'informationContainer': {
height: '25vh'
}
}
})
@@ -0,0 +1,138 @@
import { makeStyles } from '@mui/styles'
export const useStylesPC = makeStyles({
'informationContainer': {
width: '100%',
display: 'flex',
flexDirection: 'column',
backgroundColor: '#282a35 !important',
height: '38vh',
minHeight: '14.3rem',
fontFamily: '"Source Sans Pro", sans-serif',
marginTop: 'auto',
'& .topLane': {
width: '100%',
height: '20%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
'& button': {
padding: '0.3rem',
borderRadius: '5px',
border: '1px solid white',
backgroundColor: 'transparent',
fontWeight: 500,
'& a': {
textDecoration: 'none',
color: 'white'
},
'&:hover': {
backgroundColor: 'white',
'& a': {
color: 'black'
}
}
}
},
'& .midLane': {
width: '100%',
height: '20%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
'& button': {
backgroundColor: '#282a35',
border: '1px solid white',
borderRadius: '5px',
padding: '0.4rem',
marginLeft: '0.2rem',
marginRight: '0.2rem',
'& i': {
color: 'white',
fontSize: '2rem'
},
'&:hover': {
backgroundColor: 'white',
'& i': {
color: 'black'
}
}
}
},
'& .botLane': {
width: '100%',
height: '60%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
'& .introText': {
color: 'white !important',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
width: '90%',
fontSize: '1rem',
'& div': {
margin: '0.5rem',
textAlign: 'center',
'& a': {
color: 'white',
'& span': {
textDecoration: 'underline',
'&:hover': {
color: '#4caf50'
}
}
}
}
}
}
},
'@media only screen and (max-width: 379px)': {
'informationContainer': {
height: '47vh',
'& .topLane': {
height: '12%'
},
'& .midLane': {
height: '10%',
},
'& .botLane': {
height: '78%',
marginBottom: '0.5rem',
'& .introText': {
fontSize: '0.8rem'
}
}
}
},
'@media only screen and (max-width: 425px) and (min-width: 380px)': {
'informationContainer': {
height: '40vh',
'& .topLane': {
height: '17%',
},
'& .midLane': {
height: '13%',
},
'& .botLane': {
height: '70%',
marginBottom: '0.5rem',
'& .introText': {
fontSize: '0.8rem'
}
}
}
},
'@media only screen and (max-width: 768px) and (min-width: 426px)': {
'informationContainer': {
height: '25vh',
'& .botLane': {
'& .introText': {
fontSize: '0.8rem'
}
}
}
}
})
+44
View File
@@ -0,0 +1,44 @@
import './style.css'
import { useState, useEffect } from 'react';
export const Message = (props) => {
const { status, successMessage, failureMessage } = props;
const [show, setShow] = useState(false)
useEffect(() => {
if (status != undefined) {
setShow(true)
}
}, [status])
useEffect(() => {
const resetShow = () => { setShow(false) }
if (show) setTimeout(resetShow, 1500)
return(() => {
clearTimeout(resetShow)
})
}, [show])
return (
<>
{show &&
<div className='message'>
{
<div>{status == true ? <i className="fas fa-check-circle statusIcon" id="successIcon" color="#abe491" /> : <i className="fas fa-times-circle statusIcon" id="failureIcon" color="#e49191" />}</div>
}
<div className="messageBox">
{
<div>{
status == true ?
<div>
<div>{successMessage}</div>
</div>
:
<div>
<div>{failureMessage}</div>
</div>
}</div>
}
</div>
</div>
}
</>
)
}
+31
View File
@@ -0,0 +1,31 @@
.successLineOne {
color: #abe491;
}
.successLineTwo {
color: #b9b9b9;
font-size: 0.8rem;
}
.failureLineOne {
color: #e49191;
}
.failureLineTwo {
color: #b9b9b9;
font-size: 0.8rem;
}
.message {
font-weight: bolder;
font-family: "Source Sans Pro", sans-serif;
display: flex;
width: 100%;
align-items: center;
justify-content: center;
}
#successIcon {
color: #abe491;
}
#failureIcon {
color: #e49191;
}
.statusIcon {
margin-right: 0.6rem;
}
+58
View File
@@ -0,0 +1,58 @@
import { useState, useEffect } from 'react'
import { NavBarItem } from './navBarItem'
import { isBrowser } from 'react-device-detect'
import { useStylesPC } from './stylePC'
import { useStylesMobile } from './styleMobile'
const navBarConfig = [
{
tabID: 'home', to: '/', specificTabName: "home", children:
<>
<i className="fas fa-home" />
</>
},
{
tabID: 'motionGenerator', to: '/generator', children:
<>
<div>Motion </div>
<div>Generator</div>
</>
},
{
tabID: 'database', to: '/database', children:
<>
<div>Motion </div>
<div>Database</div>
</>
},
{
tabID: 'breakCalc', to: '/break_calculator', children:
<>
<div>Break</div>
<div>Calculator</div>
</>
},
{
tabID: 'keeper', to: '/keeper/bp', children:
<>
<div>Timekeeper</div>
</>
}
]
export const NavBar = () => {
const [activeTab, setActiveTab] = useState(`/`)
useEffect(() => {
setActiveTab(window.location.pathname)
}, [window.location.pathname])
const classesPC = useStylesPC()
const classesMobile = useStylesMobile()
return (
<div className={isBrowser ? classesPC.navBar : classesMobile.navBar}>
{navBarConfig.map(config => {
return (
<NavBarItem specificTabName={config.specificTabName} isActive={activeTab === config.to} to={config.to} tabID={config.tabID} setActiveTab={setActiveTab}>{config.children}</NavBarItem>
)
})}
</div>
)
}
+10
View File
@@ -0,0 +1,10 @@
import { Link } from 'react-router-dom'
export const NavBarItem = (props) => {
const { to, tabID, setActiveTab, children, isActive, specificTabName } = props // setActiveTab(to)
return (
<Link to={to} className={`anchor ${specificTabName} ${isActive ? 'active' : ''}`} id={tabID} onClick={() => { setActiveTab(to) }}>
{children}
</Link>
)
}
+77
View File
@@ -0,0 +1,77 @@
import { makeStyles } from '@mui/styles'
export const useStylesMobile = makeStyles({
'navBar': {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#282a35',
fontFamily: '"Source Sans Pro", sans-serif',
height: '7vh',
width: '100%',
'& .anchor': {
color: 'white',
textDecoration: 'none',
display: 'flex',
fontWeight: 'bolder',
fontSize: '0.8rem',
height: '100%',
width: '24vw',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
'&:hover': {
backgroundColor: '#000000'
}
},
'& .home': {
width: '4vw !important',
'& i': {
fontSize: '1.2rem'
}
},
'& .active': {
backgroundColor: '#000000'
}
},
'@media only screen and (max-width: 379px)': {
'navBar': {
'& .anchor': {
fontSize: '0.7rem',
width: '22vw',
},
'& .home': {
width: '12vw !important',
'& i': {
fontSize: '1rem !important'
}
}
}
},
'@media only screen and (max-width: 425px) and (min-width: 380px)': {
'navBar': {
'& .anchor': {
fontSize: '0.8rem',
width: '22vw'
},
'& .home': {
width: '12vw !important',
}
}
},
'@media only screen and (max-width: 768px) and (min-width: 426px)': {
'navBar': {
'& .anchor': {
fontSize: '1.1rem',
width: '22.5vw',
},
'& .home': {
width: '10vw !important',
'& i': {
fontSize: '2rem !important'
}
}
}
}
})
+81
View File
@@ -0,0 +1,81 @@
import { makeStyles } from '@mui/styles'
export const useStylesPC = makeStyles({
'navBar': {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#282a35',
fontFamily: '"Source Sans Pro", sans-serif',
height: '7vh',
minHeight: '2.63rem', /**/
width: '100%',
minWidth: '48.125rem', /**/
'& .anchor': {
color: 'white',
textDecoration: 'none',
display: 'flex',
fontWeight: 'bolder',
fontSize: '0.8rem !important',
height: '100%',
width: '24vw',
minWidth: '13.5rem', /**/
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
'&:hover': {
backgroundColor: '#000000'
}
},
'& .home': {
width: '4vw !important',
minWidth: '3.375rem !important', /**/
'& i': {
fontSize: '1.2rem !important'
}
},
'& .active': {
backgroundColor: '#000000'
}
},
'@media only screen and (max-width: 379px)': {
'navBar': {
'& .anchor': {
fontSize: '0.7rem',
width: '22vw',
},
'& .home': {
width: '12vw !important',
'& i': {
fontSize: '1rem !important'
}
}
}
},
'@media only screen and (max-width: 425px) and (min-width: 380px)': {
'navBar': {
'& .anchor': {
fontSize: '0.8rem',
width: '22vw'
},
'& .home': {
width: '12vw !important'
}
}
},
'@media only screen and (max-width: 768px) and (min-width: 426px)': {
'navBar': {
'& .anchor': {
fontSize: '1.1rem',
width: '22.5vw',
},
'& .home': {
width: '10vw !important',
'& i': {
fontSize: '2rem'
}
}
}
}
})
@@ -0,0 +1,8 @@
import { components } from 'react-select'
export const ClearIndicator = ({ children, ...props }) => {
return (
<components.ClearIndicator {...props}>
{children}
</components.ClearIndicator>
);
};
@@ -0,0 +1,8 @@
import { components } from 'react-select'
export const DropdownIndicator = ({ children, ...props }) => {
return (
<components.DropdownIndicator {...props}>
{children}
</components.DropdownIndicator>
);
};
@@ -0,0 +1,6 @@
import { components } from 'react-select'
export const Input = props => {
return (
<components.Input {...props} />
);
};
@@ -0,0 +1,6 @@
import { components } from 'react-select'
export const MultiValueContainer = props => {
return (
<components.MultiValueContainer {...props} />
);
};
@@ -0,0 +1,6 @@
import { components } from 'react-select'
export const Option = props => {
return (
<components.Option {...props} />
);
};
@@ -0,0 +1,4 @@
import { components } from 'react-select'
export const Placeholder = props => {
return <components.Placeholder {...props} />;
};
@@ -0,0 +1,8 @@
import { components } from 'react-select'
export const SelectContainer = ({ children, ...props }) => {
return (
<components.SelectContainer {...props}>
{children}
</components.SelectContainer>
);
};
@@ -0,0 +1,4 @@
import { components } from 'react-select'
export const SingleValue = ({ children, ...props }) => (
<components.SingleValue {...props}>{children}</components.SingleValue>
);
@@ -0,0 +1,4 @@
import { components } from 'react-select'
export const ValueContainer = ({ children, ...props }) => (
<components.ValueContainer {...props}>{children}</components.ValueContainer>
);
@@ -0,0 +1,9 @@
export * from './ClearIndicator'
export * from './DropdownIndicator'
export * from './MultiValueContainer'
export * from './SelectContainer'
export * from './ValueContainer'
export * from './Placeholder'
export * from './Option'
export * from './SingleValue'
export * from './Input'
+47
View File
@@ -0,0 +1,47 @@
export const Table = (props) => {
const { dataSource, columns, showActions, names, ref } = props
return (
<>
{
dataSource.length != 0 ?
<table className={names.tableName} ref={ref}>
<tr className={names.headerName}>
{
columns.map(columnItem => {
if (columnItem.type != "action") {
return (
<th className={names.headerCellName} style={{
width: columnItem.width
}}>{columnItem.name}</th>
)
}
})
}
{
showActions && <th width={columns[columns.length - 1].width} className={names.emptyHeaderCellName}></th>
}
</tr>
{
dataSource.map(item => {
return (
<tr className={names.rowName}>
{
columns.map(columnItem => {
return (
<td style={{
width: columnItem.width
}} className={`${names.rowCellName} ${columnItem.type == "action" ? `${names.actionCellName}` : ""}`}>{columnItem.render(item)}</td>
)
})
}
</tr>
)
})
}
</table>
: <></>
}
</>
)
}
+7
View File
@@ -0,0 +1,7 @@
export * from './NavBar'
export * from './EditableSelector'
export * from './EditableText'
export * from './EditableTextArea'
export * from './InformationContainer'
export * from './Message'
export * from './Table'
File diff suppressed because it is too large Load Diff
+237
View File
@@ -0,0 +1,237 @@
[
{
"name": "BY Online Debate Open",
"format": "BP",
"year": "2021"
},
{
"name": "Uhuru Worlds",
"format": "BP",
"year": "2021"
},
{
"name": "Cambridge Asia BP",
"format": "BP",
"year": "2021"
},
{
"name": "Asian English Olympics",
"format": "BP",
"year": "2021"
},
{
"name": "Beihang International Winter Online BP Open",
"format": "BP",
"year": "2021"
},
{
"name": "Philippines Queer Open",
"format": "BP",
"year": "2021"
},
{
"name": "UMT Parliamentary Debate Open",
"format": "BP",
"year": "2021"
},
{
"name": "Trouvaille Debate Open",
"format": "BP",
"year": "2021"
},
{
"name": "DAV IR Cup",
"format": "BP",
"year": "2021"
},
{
"name": "HWS Round Robin",
"format": "BP",
"year": "2021"
},
{
"name": "Korea WUDC",
"format": "BP",
"year": "2021"
},
{
"name": "DTU Parliamentary Debate",
"format": "AP",
"year": "2021"
},
{
"name": "Vietnam University Debating Championship (VUDC)",
"format": "AP",
"year": "2021"
},
{
"name": "NEU Debate Open",
"format": "AP",
"year": "2021"
},
{
"name": "Cogic Debate Online (CODO)",
"format": "AP",
"year": "2021"
},
{
"name": "The Anime Open",
"format": "AP",
"year": "2021"
},
{
"name": "Netflix International Debate",
"format": "AP",
"year": "2021"
},
{
"name": "Da Nang Debate Open",
"format": "AP",
"year": "2021"
},
{
"name": "Asian Online Debating Championship (AODC) - WSDC",
"format": "WSDC",
"year": "2021"
},
{
"name": "Oldham Cup International League",
"format": "WSDC",
"year": "2021"
},
{
"name": "Nanjing Debate Open",
"format": "WSDC",
"year": "2021"
},
{
"name": "The Tabate",
"format": "WSDC",
"year": "2021"
},
{
"name": "FLSS Debate Tournament",
"format": "WSDC",
"year": "2021"
},
{
"name": "Hanoi Debate Tournament (HDT)",
"format": "WSDC",
"year": "2021"
},
{
"name": "Lychee Debate Open",
"format": "WSDC",
"year": "2021"
},
{
"name": "Canopus Debate Championship",
"format": "WSDC",
"year": "2021"
},
{
"name": "Gấu Debate Tournament",
"format": "BP",
"year": "2021"
},
{
"name": "Vietname BP Championship (BP)",
"format": "BP",
"year": "2020"
},
{
"name": "Gấu Online Debating Championship",
"format": "BP",
"year": "2020"
},
{
"name": "Beihang International Online Debating Championship",
"format": "BP",
"year": "2020"
},
{
"name": "Japan BP",
"format": "BP",
"year": "2020"
},
{
"name": "Melbourne Mini",
"format": "BP",
"year": "2020"
},
{
"name": "PKU Pro-Am",
"format": "BP",
"year": "2020"
},
{
"name": "Asian Online Debating Championship (AODC) - WSDC",
"format": "WSDC",
"year": "2020"
},
{
"name": "Taiwan Online Debate Open",
"format": "AP",
"year": "2020"
},
{
"name": "Northern Coast Debate Open",
"format": "AP",
"year": "2020"
},
{
"name": "Hòa Vang Debate Online",
"format": "AP",
"year": "2020"
},
{
"name": "Teen X Debate Online",
"format": "AP",
"year": "2020"
},
{
"name": "Gấu Online Debate Open",
"format": "AP",
"year": "2020"
},
{
"name": "Hong Kong Debate Open",
"format": "WSDC",
"year": "2020"
},
{
"name": "UPenn World Schools Online Debating Tournament",
"format": "WSDC",
"year": "2020"
},
{
"name": "The Debaters VTV7",
"format": "",
"year": "2020"
},
{
"name": "WSDC",
"format": "WSDC",
"year": "2019"
},
{
"name": "Vietnam Schools Debating Championship (VSDC)",
"format": "WSDC",
"year": "2019"
},
{
"name": "Ka Paio Debate Open",
"format": "WSDC",
"year": "2019"
},
{
"name": "Ka Paio Online Debate Open",
"format": "WSDC",
"year": "2020"
},
{
"name": "WSDC",
"format": "WSDC",
"year": "2018"
}
]
File diff suppressed because it is too large Load Diff
+97
View File
@@ -0,0 +1,97 @@
[
{
"name": "Hong Kong Parliamentary Debating Society (HKPDS)",
"format": "BP",
"year": "2020"
},
{
"name": "Asian Online Debating Championship (AODC) - BP",
"format": "BP",
"year": "2020"
},
{
"name": "6th Shanghai International Debate Open (SIDO)",
"format": "BP",
"year": "2020"
},
{
"name": "Trường Teen",
"format": "",
"year": "2020"
},
{
"name": "Cogic Debate Online (CODO)",
"format": "AP",
"year": "2020"
},
{
"name": "Spring KNC",
"format": "AP",
"year": "2020"
},
{
"name": "CNH Debate Open (CDO)",
"format": "WSDC",
"year": "2020"
},
{
"name": "Hanoi Debate Tournament (HDT)",
"format": "WSDC",
"year": "2020"
},
{
"name": "Southern Debate Open (SDO)",
"format": "AP",
"year": "2020"
},
{
"name": "Hong Kong Schools Debate Open (HKSDO)",
"format": "WSDC",
"year": "2020"
},
{
"name": "DAV Debate Open (DDO)",
"format": "AP",
"year": "2020"
},
{
"name": "Vietnam Debate Online (VNDO)",
"format": "WSDC",
"year": "2020"
},
{
"name": "Vietnam Debate Online (VNDO) - BP",
"format": "BP",
"year": "2020"
},
{
"name": "Nghe Tinh Debate Open (NTDO)",
"format": "WSDC",
"year": "2020"
},
{
"name": "Vietnam BP Championship (VBC)",
"format": "BP",
"year": "2021"
},
{
"name": "Pre VBC",
"format": "BP",
"year": "2021"
},
{
"name": "Online WSDC",
"format": "WSDC",
"year": "2020"
},
{
"name": "6th Oldham Cup",
"format": "WSDC",
"year": "2020"
},
{
"name": "MOE Invitational Debating Championship (MIDC)",
"format": "WSDC",
"year": "2020"
}
]
+10
View File
@@ -0,0 +1,10 @@
export function customTheme(theme) {
return {
...theme,
colors: {
...theme.colors,
primary25: 'grey',
primary: 'black',
}
}
}
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
export const formats = [
{ value: 'BP', label: 'BP' },
{ value: 'AP', label: 'AP' },
{ value: 'WSDC', label: 'WSDC' },
{ value: 'Others', label: 'Others' }
]
+19
View File
@@ -0,0 +1,19 @@
export * as englishIDs from './englishIDs.json'
export * as vietnameseIDs from './vietnameseIDs.json'
export * from './topics'
export * from './topicsForMotions'
export * from './formats'
export * from './languages'
export * from './customTheme'
export * from './tourneys.json'
export * from './PAtourneys.json'
export * from './MOJItourneys.json'
export * as tournamentsFromDatabase from './tournamentsFromDatabase.json'
export * as tournamentOptions from './tournamentOptions.json'
export * from './tournamentData.json'
export * from './motions.json'
export * from './motionDataRaw.json'
export * as motionsFromDatabase from './motionsFromDatabase.json'
export * from './PAmotions.json'
export * from './MOJImotions.json'
export * from './tableClassNames'
+4
View File
@@ -0,0 +1,4 @@
export const languages = [
{ value: 'English', label: 'English' },
{ value: 'Vietnamese', label: 'Vietnamese' }
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+40
View File
@@ -0,0 +1,40 @@
export const tableClassNames = {
adminLoadTournaments: {
"tableName": "loadedTournamentsTable",
"headerName": "tournamentsTableHeaderRow",
"headerCellName": "tournamentsTableHeader",
"emptyHeaderCellName": "emptyTournamentsTableHeaderCell",
"rowName": "tournamentTableRow",
"rowCellName": "tournamentTableCell",
"actionCellName": "deleteTournamentCell"
},
adminLoadMotions: {
"tableName": "loadedMotionsTable",
"headerName": "motionsTableHeaderRow",
"headerCellName": "motionsTableHeader",
"emptyHeaderCellName": "emptyMotionsTableHeaderCell",
"rowName": "motionTableRow",
"rowCellName": "motionTableCell",
"actionCellName": "deleteMotionCell"
},
adminPendingRequests: {
"tableName": "loadedRequestsTable",
"headerName": "requestsTableHeaderRow",
"headerCellName": "requestsTableHeader",
"emptyHeaderCellName": "emptyRequestsTableHeaderCell",
"rowName": "requestTableRow",
"rowCellName": "requestTableCell",
"actionCellName": "actionRequestCell"
},
userLoadMotions: {
"tableName": "tableDatabase",
"headerName": "databaseHeaderRow",
"headerCellName": "databaseTableHeader",
"emptyHeaderCellName": "",
"rowName": "databaseTableRow",
"rowCellName": "databaseTableCell",
"actionCellName": ""
}
}
+37
View File
@@ -0,0 +1,37 @@
export const topics = [
{ value: 'aac', label: 'Art & Culture' },
{ value: 'ar', label: "Animals' rights" },
{ value: 'business', label: 'Business' },
{ value: 'cjs', label: 'Criminal Justice System' },
{ value: 'development', label: 'Development' },
{ value: 'economics', label: 'Economics' },
{ value: 'education', label: 'Education' },
{ value: 'entertainment', label: 'Entertainment'},
{ value: 'environment', label: 'Environment' },
{ value: 'family', label: 'Family' },
{ value: 'feminism', label: 'Feminism' },
{ value: 'freedoms', label: 'Freedoms' },
{ value: 'fiction', label: 'Fiction' },
{ value: 'funny', label: 'Funny' },
{ value: 'history', label: 'History' },
{ value: 'hr', label: 'Human Relationships' },
{ value: 'ir', label: 'International Relations' },
{ value: 'law', label: 'Law'},
{ value: 'lgbtqia+', label: 'LGBTQIA+' },
{ value: 'media', label: 'Media' },
{ value: 'me', label: 'Medical Ethics' },
{ value: 'mc', label: 'Minority Communities' },
{ value: 'morality', label: 'Morality' },
{ value: 'philosophy', label: 'Philosophy' },
{ value: 'politics', label: 'Politics' },
{ value: 'religion', label: 'Religion' },
{ value: 'sat', label: 'Science & Technology' },
{ value: 'swm', label: 'Security, War and Military' },
{ value: 'sp', label: 'Social Policy' },
{ value: 'sm', label: 'Social Movements' },
{ value: 'sports', label: 'Sports' },
{ value: 'terrorism', label: 'Terrorism' },
{ value: 'the', label: 'The Human Experience' },
{ value: 'others', label: 'Others' },
]
// module.exports = topics
+308
View File
@@ -0,0 +1,308 @@
export const topicsForMotions = [
{
"value": {
"aac": {
"check": true,
"title": "Art & Culture"
}
},
"label": "Art & Culture"
},
{
"value": {
"ar": {
"check": true,
"title": "Animals' rights"
}
},
"label": "Animals' rights"
},
{
"value": {
"business": {
"check": true,
"title": "Business"
}
},
"label": "Business"
},
{
"value": {
"cjs": {
"check": true,
"title": "Criminal Justice System"
}
},
"label": "Criminal Justice System"
},
{
"value": {
"development": {
"check": true,
"title": "Development"
}
},
"label": "Development"
},
{
"value": {
"economics": {
"check": true,
"title": "Economics"
}
},
"label": "Economics"
},
{
"value": {
"education": {
"check": true,
"title": "Education"
}
},
"label": "Education"
},
{
"value": {
"entertainment": {
"check": true,
"title": "Entertainment"
}
},
"label": "Entertainment"
},
{
"value": {
"environment": {
"check": true,
"title": "Environment"
}
},
"label": "Environment"
},
{
"value": {
"family": {
"check": true,
"title": "Family"
}
},
"label": "Family"
},
{
"value": {
"feminism": {
"check": true,
"title": "Feminism"
}
},
"label": "Feminism"
},
{
"value": {
"freedoms": {
"check": true,
"title": "Freedoms"
}
},
"label": "Freedoms"
},
{
"value": {
"fiction": {
"check": true,
"title": "Fiction"
}
},
"label": "Fiction"
},
{
"value": {
"funny": {
"check": true,
"title": "Funny"
}
},
"label": "Funny"
},
{
"value": {
"history": {
"check": true,
"title": "History"
}
},
"label": "History"
},
{
"value": {
"hr": {
"check": true,
"title": "Human Relationships"
}
},
"label": "Human Relationships"
},
{
"value": {
"ir": {
"check": true,
"title": "International Relations"
}
},
"label": "International Relations"
},
{
"value": {
"law": {
"check": true,
"title": "Law"
}
},
"label": "Law"
},
{
"value": {
"lgbtqia+": {
"check": true,
"title": "LGBTQIA+"
}
},
"label": "LGBTQIA+"
},
{
"value": {
"media": {
"check": true,
"title": "Media"
}
},
"label": "Media"
},
{
"value": {
"me": {
"check": true,
"title": "Medical Ethics"
}
},
"label": "Medical Ethics"
},
{
"value": {
"mc": {
"check": true,
"title": "Minority Communities"
}
},
"label": "Minority Communities"
},
{
"value": {
"morality": {
"check": true,
"title": "Morality"
}
},
"label": "Morality"
},
{
"value": {
"philosophy": {
"check": true,
"title": "Philosophy"
}
},
"label": "Philosophy"
},
{
"value": {
"politics": {
"check": true,
"title": "Politics"
}
},
"label": "Politics"
},
{
"value": {
"religion": {
"check": true,
"title": "Religion"
}
},
"label": "Religion"
},
{
"value": {
"sat": {
"check": true,
"title": "Science & Technology"
}
},
"label": "Science & Technology"
},
{
"value": {
"swm": {
"check": true,
"title": "Security, War and Military"
}
},
"label": "Security, War and Military"
},
{
"value": {
"sp": {
"check": true,
"title": "Social Policy"
}
},
"label": "Social Policy"
},
{
"value": {
"sm": {
"check": true,
"title": "Social Movements"
}
},
"label": "Social Movements"
},
{
"value": {
"sports": {
"check": true,
"title": "Sports"
}
},
"label": "Sports"
},
{
"value": {
"terrorism": {
"check": true,
"title": "Terrorism"
}
},
"label": "Terrorism"
},
{
"value": {
"the": {
"check": true,
"title": "The Human Experience"
}
},
"label": "The Human Experience"
},
{
"value": {
"others": {
"check": true,
"title": "Others"
}
},
"label": "Others"
}
]
+87
View File
@@ -0,0 +1,87 @@
[
{
"name": "HKPDS",
"year": "2020",
"format": "BP"
},
{
"name": "AODC",
"year": "2020",
"format": "BP"
},
{
"name": "6th SIDO",
"year": "2020",
"format": "BP"
},
{
"name": "Trường Teen",
"year": "2020",
"format": ""
},
{
"name": "CDO (CNH Debat Open)",
"year": "2020",
"format": "WSDC"
},
{
"name": "Spring KNC",
"year": "2020",
"format": "AP"
},
{
"name": "CODO (Cogic Debate Online)",
"year": "2020",
"format": "AP"
},
{
"name": "HDT",
"year": "2020",
"format": "WSDC"
},
{
"name": "SDO",
"year": "2020",
"format": "BP"
},
{
"name": "HKSDO",
"year": "2020",
"format": "WSDC"
},
{
"name": "DDO",
"year": "2020",
"format": "AP"
},
{
"name": "VNDO",
"year": "2020",
"format": "BP"
},
{
"name": "NTDO (Nghe Tinh Debate Open)",
"year": "2020",
"format": "WSDC"
},
{
"name": "VBC",
"year": "2020",
"format": "BP"
},
{
"name": "Pre VBC",
"year": "2021",
"format": "BP"
},
{
"name": "Online WSDC",
"year": "2020",
"format": "WSDC"
},
{
"name": "6th Oldham Cup",
"year": "2020",
"format": "WSDC"
}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+74
View File
@@ -0,0 +1,74 @@
export function calculateBreaks({format, teamNumber, roundNumber, breakNumber}){
var teamarr1 = [];
var teamarr2 = [];
//All teams at zero points in the beginning.
for (var i=0; i<teamNumber; i++) {
teamarr1.push(0);
teamarr2.push(0);
}
switch(format) {
case 'ap':
for (var j = 0; j < roundNumber; j++) {
for (var i = 0; i < teamNumber; i++) {
if (i % 2 === 0) { //Case 1 : Where all pull ups win
teamarr1[i]+=1;
} else { //Case 2 : Where all pull ups lose
teamarr2[i]+=1;
}
}
teamarr1.sort();
teamarr2.sort();
}
break;
case 'bp':
for (var j = 0; j < roundNumber; j++) {
for (var i = 0; i < teamNumber; i++) {
//Case 1 : Where all pull ups win
if(i%4 === 0){
teamarr1[i]+=3;
teamarr1[i+1]+=2;
teamarr1[i+2]+=1;
}
//Case 2 : Where all pull ups lose
if(i%4 === 0){
teamarr2[i+1]+=1;
teamarr2[i+2]+=2;
teamarr2[i+3]+=3;
}
}
teamarr1.sort();
teamarr2.sort();
}
break;
default:
return false;
}
return({
pullUpLose: output(teamarr1, breakNumber, teamNumber),
pullUpWin: output(teamarr2, breakNumber, teamNumber)
})
}
export function output(teamarr, num_break, num_teams){
var breakTeams = teamarr.slice(-num_break);
var breakMin = breakTeams[0];
var breakMax = breakTeams[num_break-1];
var breakCount = {};
var totalCount = {};
for(var i = breakMin;i <= breakMax;i++) {
breakCount[i] = 0;
totalCount[i] = 0;
}
for (var i = 0;i<breakTeams.length;i++) {
breakCount[breakTeams[i]]++;
}
for (var i = 0;i<num_teams;i++){
if (teamarr[i]>=breakMin){
totalCount[teamarr[i]]++;;
}
}
return({
breakCount, totalCount
})
}
+12
View File
@@ -0,0 +1,12 @@
const motionsFromDatabase = require('./data/motionsFromDatabase.json')
let eng = 0, vn = 0;
motionsFromDatabase.forEach(motion => {
if (motion.language == "English") {
eng++;
}
else {
vn++;
}
})
console.log(`Number of English motions: ${eng}`)
console.log(`Number of Vietnamese motions: ${vn}`)
@@ -0,0 +1,46 @@
const fs = require('fs');
const tournaments = require('../constants/tournamentsFromDatabase.json');
const optionsUnsorted = []
const labels = []
tournaments.forEach(tournament => {
const name = tournament.name
const year = tournament.year
const id = tournament.id
let label
if (year == "") {
label = name
}
else {
label = `${name} ${year}`
}
labels.push(label)
optionsUnsorted.push({
value: id,
label: label
})
})
labels.sort()
let optionsSorted = []
labels.forEach(label => {
let id = undefined
optionsUnsorted.forEach(option => {
if (option.label == label) {
id = option.value
}
})
optionsSorted.push({
value: id,
label: label
})
})
optionsSorted = optionsSorted.filter((obj, index, self) =>
index === self.findIndex((el) => el.value === obj.value)
);
const optionsJSON = JSON.stringify(optionsSorted)
fs.writeFile(`../constants/tournamentOptions.json`, optionsJSON, 'utf8', function (err) {
if (err) {
console.log("An error occured while writing JSON Object to File.");
return console.log(err);
}
console.log("JSON file has been saved.");
});
+8
View File
@@ -0,0 +1,8 @@
export const endWithDot = (str) => {
if (str.slice(-1) == ".") {
return (str)
}
else {
return (str + ".")
}
}
+21
View File
@@ -0,0 +1,21 @@
// const fetch = require("node-fetch");
const fs = require('fs');
const run = async () => {
let numCount = 1;
let firstPart = 'https://spreadsheets.google.com/feeds/cells/10_KEaM4jA5tnMPp4OD9eXnR7n_zx3rOtPnw6YW2ww5M/'
let secondPart = '/public/full?alt=json'
for (let i = 1; i<=13; i++) {
const fullURL = `${firstPart}${i}${secondPart}`
const jsonResponse = await fetch(`${fullURL}`);
const jsonData = await jsonResponse.json();
const jsonString = JSON.stringify(jsonData);
fs.writeFile(`data/${i}.json`, jsonString, 'utf8', function (err) {
if (err) {
console.log("An error occured while writing JSON Object to File.");
return console.log(err);
}
console.log("JSON file has been saved.");
});
}
}
run();
@@ -0,0 +1,15 @@
import { topics } from '../constants/topics'
export const getFormattedTopicsFromValues = (topicValues) => {
let topicArray = {}
topicValues.forEach(topicValue => {
topics.forEach(topicItem => {
if (topicItem.value == topicValue) {
topicArray[`${topicValue}`] = {
check: true,
title: topicItem.label
}
}
})
})
return topicArray
}
+27
View File
@@ -0,0 +1,27 @@
const fs = require('fs');
const motions = require("./data/motionsFromDatabase.json")
const vietnameseIDs = [], englishIDs = [];
motions.forEach(motion => {
if (motion.language == "English") {
englishIDs.push(motion.id);
}
else if (motion.language == "Vietnamese") {
vietnameseIDs.push (motion.id);
}
})
let englishIDsJSON = JSON.stringify(englishIDs);
let vietnameseIDsJSON = JSON.stringify(vietnameseIDs);
fs.writeFile(`data/vietnameseIDs.json`, vietnameseIDsJSON, 'utf8', function (err) {
if (err) {
console.log("An error occured while writing JSON Object to File.");
return console.log(err);
}
console.log("JSON file has been saved.");
});
fs.writeFile(`data/englishIDs.json`, englishIDsJSON, 'utf8', function (err) {
if (err) {
console.log("An error occured while writing JSON Object to File.");
return console.log(err);
}
console.log("JSON file has been saved.");
});
+19
View File
@@ -0,0 +1,19 @@
import { firebaseFirestore } from "../../firebase"
export const getTourneyID = async (name, year, format) => {
let tournamentsRef = firebaseFirestore.collection('tournaments').where('name', '==', name)
if (year != '') {
tournamentsRef = tournamentsRef.where('year', '==', year)
}
if (format != '') {
tournamentsRef = tournamentsRef.where('format', '==', format)
}
const tournamentData = await tournamentsRef.get()
if (tournamentData.docs.length == 0) {
await firebaseFirestore.collection("tournaments").add({ name: name, year: year, format: format })
const newTournamentData = await tournamentsRef.get()
return newTournamentData.docs[0].id
}
else {
return tournamentData.docs[0].id
}
}
+18
View File
@@ -0,0 +1,18 @@
import { firebaseFirestore } from "../../firebase"
export const getTourneyInfo = async (id) => {
const tournamentsRef = firebaseFirestore.collection('tournaments').doc(id)
const tournamentData = await tournamentsRef.get();
if (tournamentData.exists == false) {
return {}
}
else {
const name = tournamentData.data().name
const year = tournamentData.data().year
const format = tournamentData.data().format
return {
name,
year,
format
}
}
}
+6
View File
@@ -0,0 +1,6 @@
export * from './breakCalculator'
export * from './endWithDot'
export * from './getFormattedTopicsFromValues'
export * from './getTourneyID'
export * from './getTourneyInfo'
export * from './isObject'
+3
View File
@@ -0,0 +1,3 @@
export function isObject(value) {
return value && typeof value === 'object' && value.constructor === Object;
}
+41
View File
@@ -0,0 +1,41 @@
const fs = require('fs');
const motionDataRaw = require('./data/motionDataRaw.json');
const endWithDot = (str) => {
if (str.slice(-1) == ".") {
return (str)
}
else {
return (str + ".")
}
}
let motions = [];
motionDataRaw.forEach(async (motion) => {
let date = new Date(motion.Date);
let year = date.getFullYear().toString();
if (year == "NaN") {
year = ""
}
let tournament = motion.Tournament;
let lastFour = tournament.slice(-4)
if (!isNaN(lastFour)) {
year = tournament.slice(-4)
tournament = tournament.slice(0, -5)
}
let id = ""
let content = endWithDot(motion.Motion)
let infoSlide = ''
if (motion.InfoSlide != undefined) {
infoSlide = endWithDot(motion.InfoSlide)
}
const curMot = { tournament: tournament, year: year, format: "", content: content, infoSlide: infoSlide, division: '', language: 'English', link: '', round: motion.Round, topic: {}, tournamentID: id }
motions.push(curMot)
})
let motionJSON = JSON.stringify(motions)
fs.writeFile(`data/motions.json`, motionJSON, 'utf8', function (err) {
if (err) {
console.log("An error occured while writing JSON Object to File.");
return console.log(err);
}
console.log("JSON file has been saved.");
});
+25
View File
@@ -0,0 +1,25 @@
const topics = require('./data/topics')
const fs = require('fs');
const topicsForMotions = []
topics.forEach(topic => {
const topicForMotion = {
value: {
[`${topic.value}`]: {
"check": true,
"title": `${topic.label}`
}
},
label: `${topic.label}`
}
topicsForMotions.push(topicForMotion)
})
console.log(topicsForMotions)
let topicsForMotionsJSON = JSON.stringify(topicsForMotions, null, 4)
fs.writeFile(`data/topicsForMotions.js`,`export const topicsForMotions = ${topicsForMotionsJSON}`, 'utf8', function (err) {
if (err) {
console.log("An error occured while writing JSON Object to File.");
return console.log(err);
}
console.log("JSON file has been saved.");
});
+64
View File
@@ -0,0 +1,64 @@
//motionDataRaw.json was scraped from hellomotions.com which belongs to Jessica Yung
/*
MIT License
Copyright (c) 2016 Jessica Yung.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
const fs = require('fs');
const _ = require('lodash');
const motionDataRaw = require('./data/motionDataRaw.json');
let tourneys = [];
for (let motion of motionDataRaw) {
let date = new Date(motion.Date);
let year = date.getFullYear().toString();
if (year == "NaN") {
year = ""
}
let tournament = motion.Tournament;
let firstFour = tournament.slice(0,4)
let lastFour = tournament.slice(-4)
if (!isNaN(lastFour)) {
year = lastFour
tournament = tournament.slice(0, -5)
}
if (!isNaN(firstFour)) {
year = firstFour
tournament = tournament.slice(5)
}
let newTournament = {
"name": tournament,
"format": "",
"year": year
}
tourneys.push(newTournament)
}
const uniqueTourneys = _.uniqBy(tourneys, (tourney) => {
return tourney.name + tourney.year
})
let tourneyJSON = JSON.stringify(uniqueTourneys)
fs.writeFile(`data/tourneys.json`, tourneyJSON, 'utf8', function (err) {
if (err) {
console.log("An error occured while writing JSON Object to File.");
return console.log(err);
}
console.log("JSON file has been saved.");
});
+4
View File
@@ -0,0 +1,4 @@
export * from './useForm'
export * from './useDeviceBreakPoint'
export * from './useDocumentTitle'
export * from './usePageTracker'
+7
View File
@@ -0,0 +1,7 @@
import { useMediaQuery } from 'react-responsive'
export const useDeviceBreakPoint = () => {
const isExtraSmall = useMediaQuery({ maxWidth: 379 })
const isPhone = useMediaQuery({ minWidth: 380, maxWidth: 425 })
const isTablet = useMediaQuery({ minWidth: 426, maxWidth: 768 })
return { isPhone, isTablet, isExtraSmall }
}
+7
View File
@@ -0,0 +1,7 @@
import { useEffect } from 'react'
export const useDocumentTitle = (title) => {
useEffect(() => {
document.title = title
}, [])
}
+15
View File
@@ -0,0 +1,15 @@
import { useState } from 'react'
export const useForm = (defaultFormValues) => {
const [formValue, setFormValue] = useState(defaultFormValues)
function changeFormValue(fieldName, fieldValue) {
setFormValue({ ...formValue, [fieldName]: fieldValue });
}
function resetFormValue() {
setFormValue(defaultFormValues)
}
return ({
formValue,
changeFormValue,
resetFormValue,
})
}
+10
View File
@@ -0,0 +1,10 @@
import { useEffect } from 'react'
import ReactGA4 from 'react-ga4' // for GA
import ReactGA from 'react-ga' // for UA (old)
export const usePageTracker = () => {
useEffect(() => {
ReactGA4.send('pageview')
ReactGA.pageview(window.location.pathname)
}, [])
}