AAAAAAAAAAAAGH

This commit is contained in:
Aquaticholic
2022-02-02 05:37:10 +00:00
parent 285eeb11d4
commit 54b3353a0b
33 changed files with 675 additions and 420 deletions
-6
View File
@@ -1,6 +0,0 @@
{
"prettier.printWidth": 400,
"prettier.semi": false,
"prettier.singleQuote": true
}
+1 -1
View File
@@ -1,3 +1,3 @@
{ {
"prettier.configPath": "${workspaceFolder}/.prettierrc.json" "prettier.printWidth": 800
} }
+14
View File
@@ -0,0 +1,14 @@
# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file.
ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
+8
View File
@@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----
+26 -4
View File
@@ -2,7 +2,7 @@ version: "3"
services: services:
scouting-webserver-prod: scouting-webserver-prod:
container_name: "scouting-webserver-production" container_name: "production"
restart: unless-stopped restart: unless-stopped
env_file: env_file:
- .env - .env
@@ -10,9 +10,23 @@ services:
context: ./webserver context: ./webserver
dockerfile: ./Dockerfile.prod dockerfile: ./Dockerfile.prod
expose: expose:
- 80 - 8080
ports: ports:
- "80:80/tcp" - "8080:8080/tcp"
# - "80:80/tcp"
# - "443:443/tcp"
volumes:
- ./webserver/nginx:/etc/nginx/:ro
ssl-proxy:
image: fsouza/docker-ssl-proxy
ports:
- "80:80"
- "443:443"
environment:
- DOMAIN=10.43.88.1
- TARGET_PORT=8080
- TARGET_HOST=scouting-webserver-prod
- SSL_PORT:443
couchdb: couchdb:
container_name: "scouting-couchdb" container_name: "scouting-couchdb"
restart: unless-stopped restart: unless-stopped
@@ -33,4 +47,12 @@ services:
volumes: volumes:
- ./couchdb/db.local.ini:/opt/couchdb/etc/local.ini - ./couchdb/db.local.ini:/opt/couchdb/etc/local.ini
- ./couchdb/data:/opt/couchdb/data - ./couchdb/data:/opt/couchdb/data
couch-ssl-proxy:
image: fsouza/docker-ssl-proxy
ports:
- "5985:5985"
environment:
- DOMAIN=10.43.88.1
- TARGET_PORT=5984
- TARGET_HOST=scouting-couchdb
- SSL_PORT:5985
+1
View File
@@ -0,0 +1 @@
docker-compose run certonly --webroot --webroot-path /var/www/certbot/ --dry-run -d scouting.local
+80
View File
@@ -0,0 +1,80 @@
#!/bin/bash
if ! [ -x "$(command -v docker-compose)" ]; then
echo 'Error: docker-compose is not installed.' >&2
exit 1
fi
domains=scouting.local
rsa_key_size=4096
data_path="./data/certbot"
email="" # Adding a valid address is strongly recommended
staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits
if [ -d "$data_path" ]; then
read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
exit
fi
fi
if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
echo "### Downloading recommended TLS parameters ..."
mkdir -p "$data_path/conf"
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
echo
fi
echo "### Creating dummy certificate for $domains ..."
path="/etc/letsencrypt/live/$domains"
mkdir -p "$data_path/conf/live/$domains"
docker-compose run --rm --entrypoint "\
openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\
-keyout '$path/privkey.pem' \
-out '$path/fullchain.pem' \
-subj '/CN=localhost'" certbot
echo
echo "### Starting nginx ..."
docker-compose up --force-recreate -d nginx
echo
echo "### Deleting dummy certificate for $domains ..."
docker-compose run --rm --entrypoint "\
rm -Rf /etc/letsencrypt/live/$domains && \
rm -Rf /etc/letsencrypt/archive/$domains && \
rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot
echo
echo "### Requesting Let's Encrypt certificate for $domains ..."
#Join $domains to -d args
domain_args=""
for domain in "${domains[@]}"; do
domain_args="$domain_args -d $domain"
done
# Select appropriate email arg
case "$email" in
"") email_arg="--register-unsafely-without-email" ;;
*) email_arg="--email $email" ;;
esac
# Enable staging mode if needed
if [ $staging != "0" ]; then staging_arg="--staging"; fi
docker-compose run --rm --entrypoint "\
certbot certonly --webroot -w /var/www/certbot \
$staging_arg \
$email_arg \
$domain_args \
--rsa-key-size $rsa_key_size \
--agree-tos \
--force-renewal" certbot
echo
echo "### Reloading nginx ..."
docker-compose exec nginx nginx -s reload
+1 -1
View File
@@ -1 +1 @@
docker-compose down docker-compose down --remove-orphans
+8 -4
View File
@@ -1,9 +1,12 @@
FROM node:16-alpine as build-step FROM node:16-alpine as build-step
RUN mkdir /app/ RUN mkdir /app/
RUN mkdir /app/node_modules
RUN chown -R node:node /app/node_modules
#RUN mkdir /app/node_module/.bin
WORKDIR /app/ WORKDIR /app/
ENV PATH /app/node_modules/.bin:$PATH ENV PATH /app/node_modules/.bin:$PATH
RUN chown -R node:node /app/node_modules COPY package.json ./
COPY package*.json ./ COPY package-lock.json ./
RUN npm install RUN npm install
COPY . ./ COPY . ./
# USER node # USER node
@@ -12,6 +15,7 @@ RUN npm run build
FROM nginx:1-alpine FROM nginx:1-alpine
COPY --from=build-step /app/build /usr/share/nginx/html COPY --from=build-step /app/build /usr/share/nginx/html
RUN rm /etc/nginx/conf.d/default.conf RUN rm /etc/nginx/conf.d/default.conf
COPY nginx/nginx.conf /etc/nginx/conf.d # COPY nginx/nginx.conf /etc/nginx/conf.d
EXPOSE 80 EXPOSE 8080
# EXPOSE 443
CMD ["nginx", "-g", "daemon off;"] CMD ["nginx", "-g", "daemon off;"]
+42
View File
@@ -0,0 +1,42 @@
server {
listen 8080;
listen [::]:8080;
; server_name 10.43.88.1;
; server_tokens off;
; location /.well-known/acme-challenge/{
; root /var/www/certbot;
; }
location / {
root /usr/share/nginx/html/;
index index.html index.htm;
try_files $uri $uri/ /index.html;
; return 301 https://$host$request_uri;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
; error_page 500 502 503 504 /50x.html;
; location = /50x.html {
; root /usr/share/nginx/html;
; }
}
; server {
; listen 443 default_server ssl;
; listen [::]:443 ssl;
; server_name 10.43.88.1;
; ssl_certificate /etc/nginx/ssl/live/10.43.88.1/fullchain.pem;
; ssl_certificate_key /etc/nginx/ssl/live/10.43.88.1/privkey.pem;
; include /etc/letsencrypt/options-ssl-nginx.conf;
; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
; location / {
; root /usr/share/nginx/html/;
; index index.html index.htm;
; try_files $uri $uri/ /index.html;
; }
; }
+16 -11
View File
@@ -1,13 +1,18 @@
events {
server { worker_connections 4096; ## Default: 1024
listen 80; }
location / { http {
root /usr/share/nginx/html/; server {
index index.html index.htm; listen 8080;
try_files $uri $uri/ /index.html; listen [::]:8080;
} location / {
error_page 500 502 503 504 /50x.html; root /usr/share/nginx/html/;
location = /50x.html { index index.html index.htm;
root /usr/share/nginx/html; try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
} }
} }
+13 -1
View File
@@ -19,7 +19,19 @@
"react-scripts": "5.0.0", "react-scripts": "5.0.0",
"web-vitals": "^2.1.3", "web-vitals": "^2.1.3",
"@material-ui/core": "^4.12.3", "@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2" "@material-ui/icons": "^4.11.2",
"workbox-background-sync": "^5.1.4",
"workbox-broadcast-update": "^5.1.4",
"workbox-cacheable-response": "^5.1.4",
"workbox-core": "^5.1.4",
"workbox-expiration": "^5.1.4",
"workbox-google-analytics": "^5.1.4",
"workbox-navigation-preload": "^5.1.4",
"workbox-precaching": "^5.1.4",
"workbox-range-requests": "^5.1.4",
"workbox-routing": "^5.1.4",
"workbox-strategies": "^5.1.4",
"workbox-streams": "^5.1.4"
}, },
"scripts": { "scripts": {
+2 -1
View File
@@ -1,3 +1,4 @@
.App { .App {
/* height: 100%; */ height: 100%;
width: 100%;
} }
+11 -17
View File
@@ -16,7 +16,7 @@ import NotFoundPage from "./Pages/NotFoundPage";
import DashboardPage from "./Pages/DashboardPage/DashboardPage"; import DashboardPage from "./Pages/DashboardPage/DashboardPage";
import WelcomePage from "./Pages/WelcomePage"; import WelcomePage from "./Pages/WelcomePage";
import InputPage from "./Pages/InputPage"; import InputPage from "./Pages/InputPage";
import { createMuiTheme, ThemeProvider } from '@material-ui/core' import { createTheme, ThemeProvider } from '@material-ui/core/styles'
import { Formik, Field, Form } from 'formik'; import { Formik, Field, Form } from 'formik';
import { TextField, Button, Grid, FormRow, Checkbox, Radio, FormControlLabel, FormControl, FormLabel, RadioGroup, IconButton, InputAdornment } from "@material-ui/core"; import { TextField, Button, Grid, FormRow, Checkbox, Radio, FormControlLabel, FormControl, FormLabel, RadioGroup, IconButton, InputAdornment } from "@material-ui/core";
import { AddCircleOutline, RemoveCircleOutline } from "@material-ui/icons"; import { AddCircleOutline, RemoveCircleOutline } from "@material-ui/icons";
@@ -25,8 +25,7 @@ import { ProcessedDataBucketProvider } from "./ProcessedDataBucketContext";
function App() { function App() {
const darkTheme = createMuiTheme({ const darkTheme = createTheme({
// Theme settings // Theme settings
palette: { palette: {
type: "dark", type: "dark",
@@ -35,18 +34,13 @@ function App() {
fontSize: 18 fontSize: 18
} }
}); });
const styles = {
bigbution: {
}
}
return ( return (
<div className="App"> <ThemeProvider theme={darkTheme}>
<ThemeProvider theme={darkTheme}> <ProcessedDataBucketProvider>
<DbProvider> <DbProvider>
<ProcessedDataBucketProvider> <Router>
<Router> <Navigation />
<Navigation /> <div className="App">
<Routes> <Routes>
<Route path="/" element={<WelcomePage />} /> <Route path="/" element={<WelcomePage />} />
<Route path="/Dashboard" element={<DashboardPage />} /> <Route path="/Dashboard" element={<DashboardPage />} />
@@ -54,11 +48,11 @@ function App() {
<Route path="/404" element={<NotFoundPage />} /> <Route path="/404" element={<NotFoundPage />} />
<Route path="*" element={<NotFoundPage />} /> <Route path="*" element={<NotFoundPage />} />
</Routes> </Routes>
</Router> </div>
</ProcessedDataBucketProvider> </Router>
</DbProvider> </DbProvider>
</ThemeProvider> </ProcessedDataBucketProvider>
</div> </ThemeProvider>
); );
} }
+4 -12
View File
@@ -14,6 +14,7 @@ export function useRemoteDb() {
} }
export function DbProvider({ children }) { export function DbProvider({ children }) {
const pdb = React.useContext(ProcessedDataBucketContext);
const [localdb, setLocaldb] = useState(new PouchDB("testdata")); const [localdb, setLocaldb] = useState(new PouchDB("testdata"));
//used in development server //used in development server
const [remotedb, setRemotedb] = useState( const [remotedb, setRemotedb] = useState(
@@ -25,25 +26,16 @@ export function DbProvider({ children }) {
}, },
}) })
); );
console.log(remotedb);
// const [remotedb, setRemotedb] = useState(new PouchDB(window.location.protocol + "//" + window.location.hostname + ":5984/kcmt2021", {skip_setup: true}))
//Login to the Remote Database
// remotedb.logIn('2021', 'Ridgebotics').then(function () {
// console.log("CouchDb Login Successful!");
// }).catch(function (err) {
// console.log("Unable to login to CouchDb!");
// console.log(err);
// });
pdb.updateData(localdb);
localdb localdb
.sync(remotedb, { .sync(remotedb, {
live: true, live: true,
retry: true, retry: true,
}) })
.on("change", function (change) { .on("change", function (change) {
const pdb = React.useContext(ProcessedDataBucketContext); console.log('DB CHANGED');
console.log(pdb); pdb.updateData(localdb);
}) })
.on("paused", function (info) { }) .on("paused", function (info) { })
.on("active", function (info) { }) .on("active", function (info) { })
+9 -9
View File
@@ -1,13 +1,13 @@
.maxwidth { .maxwidth {
display: block; display: block;
/* margin-left: auto; margin-left: auto;
margin-right: auto; */ margin-right: auto;
width: 100%; width: 100%;
text-align: center; text-align: center;
margin: auto; margin: auto;
max-width: fit-content; max-width: fit-content;
/* max-width: 100% */ /* max-width: 100% */
} }
.smallfeild { .smallfeild {
max-width: 25%; max-width: 25%;
} }
+63 -238
View File
@@ -13,6 +13,7 @@ import {
Button, Button,
Grid, Grid,
FormRow, FormRow,
Divider,
Checkbox, Checkbox,
Radio, Radio,
FormControlLabel, FormControlLabel,
@@ -26,9 +27,8 @@ import { AddCircleOutline, RemoveCircleOutline } from "@material-ui/icons";
const InputPage = () => { const InputPage = () => {
const localdb = useLocalDb(); const localdb = useLocalDb();
console.log(localdb);
return ( return (
<div class="maxwidth"> <div>
<br /> <br />
<Formik <Formik
initialValues={{ initialValues={{
@@ -39,17 +39,19 @@ const InputPage = () => {
team_abilities_cant: "", team_abilities_cant: "",
fouls: "0", fouls: "0",
fouls_tech: "0", fouls_tech: "0",
flips: "0", flipped: false,
red_cards: "0", red_cards: "0",
yellow_cards: "0", yellow_cards: "0",
disabled: false, disabled: false,
taxi_auto: false,
upper_hub_auto: "0", upper_hub_auto: "0",
lower_hub_auto: "0", lower_hub_auto: "0",
upper_hub: "0", upper_hub_teleop: "0",
lower_hub: "0", lower_hub_teleop: "0",
climb_level: "", climb_level: "",
alliance: "", alliance: "",
defence: "" defence: "0",
disabled: false
}} }}
onSubmit={(values, { setSubmitting, resetForm }) => { onSubmit={(values, { setSubmitting, resetForm }) => {
setTimeout(() => { setTimeout(() => {
@@ -82,31 +84,13 @@ const InputPage = () => {
handleSubmit, handleSubmit,
isSubmitting, isSubmitting,
}) => ( }) => (
<Form > <Form >
<Grid <Grid container direction="row" justifyContent="center" alignItems="flex-end" spacing={3}>
container
direction="row"
justify="center"
alignItems="flex-start"
spacing={3}>
<Grid item> <Grid item>
{" "} <Field type="input" as={TextField} name="team_number" label="Team #" />
<Field
type="input"
as={TextField}
name="team_number"
label="Team #"
/>{" "}
</Grid> </Grid>
<Grid item> <Grid item>
{" "} <Field type="input" as={TextField} name="match_number" label="Match Number" />
<Field
type="input"
as={TextField}
name="match_number"
label="Match Number"
/>{" "}
</Grid> </Grid>
<Grid item> <Grid item>
<FormControl component="fieldset"> <FormControl component="fieldset">
@@ -114,25 +98,13 @@ const InputPage = () => {
<RadioGroup aria-label="Alliance" name="alliance" row> <RadioGroup aria-label="Alliance" name="alliance" row>
<FormControlLabel <FormControlLabel
control={ control={
<Field <Field as={Radio} type="radio" name="alliance" value="red" style={{ fontSize: 50 }} />
as={Radio}
type="radio"
name="alliance"
value="red"
style={{ fontSize: 50 }}
/>
} }
label="Red" label="Red"
/> />
<FormControlLabel <FormControlLabel
control={ control={
<Field <Field as={Radio} type="radio" name="alliance" value="blue" />
as={Radio}
type="radio"
name="alliance"
value="blue"
/>
} }
label="Blue" label="Blue"
/> />
@@ -140,118 +112,62 @@ const InputPage = () => {
</FormControl> </FormControl>
</Grid> </Grid>
</Grid> </Grid>
<div /> <div />
<Grid <Grid container direction="row" justifyContent="center" alignItems="flex-end" spacing={3}>
container
direction="row"
justify="center"
alignItems="flex-start"
spacing={3}>
<Grid item> <Grid item>
<Grid <Grid container direction="column" justifyContent="flex-end" alignItems="center" spacing={3}>
container
direction="column"
justify="flex-start"
alignItems="center"
spacing={3}>
<Grid item> <Grid item>
<InputNumberField <InputNumberField name="upper_hub_auto" label="Upper Hub Auto" />
name="upper_hub_auto"
label="Upper Hub Auto"
/>
</Grid> </Grid>
<Grid item> <Grid item>
<InputNumberField <InputNumberField name="lower_hub_auto" label="Lower Hub Auto" />
name="lower_hub_auto"
label="Lower Hub Auto"
/>
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>
<Grid item> <Grid item>
{" "} <Grid container direction="column" justifyContent="flex-end" alignItems="center" spacing={3}>
<Grid
container
direction="column"
justify="flex-start"
alignItems="center"
spacing={3}>
<Grid item> <Grid item>
{" "} <InputNumberField name="upper_hub_teleop" label="Upper Hub Teleop" />
<InputNumberField name="upper_hub" label="Upper Hub" />{" "}
</Grid> </Grid>
<Grid item> <Grid item>
{" "} <InputNumberField name="lower_hub_teleop" label="Lower Hub Teleop" />
<InputNumberField name="lower_hub" label="Lower Hub" />{" "}
</Grid> </Grid>
</Grid>{" "} </Grid>
</Grid> </Grid>
</Grid> </Grid>
<div /> <div />
<Grid <Grid container direction="row" justifyContent="center" alignItems="flex-end" spacing={3}>
container
direction="row"
justify="center"
alignItems="flex-start"
spacing={3}>
<Grid item> <Grid item>
<FormControl component="fieldset"> <FormControl component="fieldset">
<FormLabel component="legend">Climbing</FormLabel> <FormLabel component="legend">Climbing</FormLabel>
<RadioGroup aria-label="Climbing" name="climb_level" row> <RadioGroup aria-label="Climbing" name="climb_level" row>
<FormControlLabel <FormControlLabel
control={ control={
<Field <Field as={Radio} type="radio" name="climb_level" value="0" />
as={Radio}
type="radio"
name="climb_level"
value="0"
/>
} }
label="None" label="None"
/> />
<FormControlLabel <FormControlLabel
control={ control={
<Field <Field as={Radio} type="radio" name="climb_level" value="1" />
as={Radio}
type="radio"
name="climb_level"
value="1"
/>
} }
label="Low" label="Low"
/> />
<FormControlLabel <FormControlLabel
control={ control={
<Field <Field as={Radio} type="radio" name="climb_level" value="2" />
as={Radio}
type="radio"
name="climb_level"
value="2"
/>
} }
label="Mid" label="Mid"
/> />
<FormControlLabel <FormControlLabel
control={ control={
<Field <Field as={Radio} type="radio" name="climb_level" value="3" />
as={Radio}
type="radio"
name="climb_level"
value="3"
/>
} }
label="High" label="High"
/> />
<FormControlLabel <FormControlLabel
control={ control={
<Field <Field as={Radio} type="radio" name="climb_level" value="4" />
as={Radio}
type="radio"
name="climb_level"
value="4"
/>
} }
label="Traversal" label="Traversal"
/> />
@@ -259,168 +175,77 @@ const InputPage = () => {
</FormControl> </FormControl>
</Grid> </Grid>
</Grid> </Grid>
<div /> <div />
<Grid <Grid container direction="row" justifyContent="center" alignItems="flex-end" spacing={3}>
container
direction="row"
justify="center"
alignItems="flex-start"
spacing={3}>
<Grid item> <Grid item>
<Grid <Grid container direction="column" justifyContent="flex-end" alignItems="center" spacing={3}>
container
direction="column"
justify="flex-start"
alignItems="center"
spacing={3}>
<Grid item> <Grid item>
<InputNumberField name="fouls" label="Fouls" />{" "} <InputNumberField name="fouls" label="Fouls" />
</Grid>{" "} </Grid>
<Grid item> <Grid item>
<InputNumberField name="fouls_tech" label="Tech Fouls" />{" "} <InputNumberField name="fouls_tech" label="Tech Fouls" />
</Grid>{" "} </Grid>
</Grid> </Grid>
</Grid> </Grid>
<Grid item> <Grid item>
<Grid <Grid container direction="column" justifyContent="flex-end" alignItems="center" spacing={3}>
container
direction="column"
justify="flex-start"
alignItems="center"
spacing={3}>
<Grid item> <Grid item>
<InputNumberField name="red_cards" label="Red Cards" />{" "} <InputNumberField name="red_cards" label="Red Cards" />
</Grid>{" "}
<Grid item>
<InputNumberField name="yellow_cards" label="Yellow Cards" />{" "}
</Grid> </Grid>
<Grid item> <Grid item>
{/* <InputNumberField name="disables" label="Disables" />{" "} */} <InputNumberField name="yellow_cards" label="Yellow Cards" />
</Grid>{" "} </Grid>
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>
<div /> <div />
<Grid <Grid container direction="row" justifyContent="center" alignItems="flex-end" spacing={3}>
container
direction="row"
justify="center"
alignItems="flex-start"
spacing={3}>
<Grid item> <Grid item>
<FormControl component="fieldset"> <FormControl component="fieldset">
<FormLabel component="legend">Defense</FormLabel> <FormLabel component="legend">Defense</FormLabel>
<RadioGroup aria-label="Defense" name="defence" row> <RadioGroup aria-label="Defense" name="defence" row>
<FormControlLabel <FormControlLabel
control={ control={<Field as={Radio} type="radio" name="defence" value="0" />}
<Field label="None"
as={Radio}
type="radio"
name="defence"
value="0"
/>
}
label="N/A"
/> />
{/* <Divider orientation="vertical" flexItem middle /> */}
<FormControlLabel <FormControlLabel
control={ control={<Field as={Radio} type="radio" name="defence" value="1" />}
<Field
as={Radio}
type="radio"
name="defence"
value="1"
/>
}
label="Poor" label="Poor"
/> />
<FormControlLabel <FormControlLabel
control={ control={<Field as={Radio} type="radio" name="defence" value="2" />}
<Field
as={Radio}
type="radio"
name="defence"
value="2"
/>
}
label="Good" label="Good"
/> /> <FormControlLabel
<FormControlLabel control={<Field as={Radio} type="radio" name="defence" value="3" />}
control={
<Field
as={Radio}
type="radio"
name="defence"
value="3"
/>
}
label="Exceptional" label="Exceptional"
/> />
</RadioGroup> </RadioGroup>
</FormControl> </FormControl>
</Grid> </Grid>
</Grid>
<div />
<Grid
container
direction="row"
justify="center"
alignItems="flex-start"
spacing={3}>
<Grid item> <Grid item>
{" "} <FormControlLabel
<Field control={<Field as={Checkbox} type="checkbox" name="disabled" />}
type="input" label="Disabled"
as={TextField} />
multiline
rows={3}
rowsMax={7}
labelWidth={60}
name="team_abilities_well"
label="What they did Well"
/>{" "}
</Grid>
<Grid item>
{" "}
<Field
type="input"
as={TextField}
multiline
rows={3}
rowsMax={7}
labelWidth={60}
name="team_abilities_struggle"
label="What they struggled with"
/>{" "}
</Grid>
<Grid item>
{" "}
<Field
type="input"
as={TextField}
multiline
rows={3}
rowsMax={7}
labelWidth={60}
name="team_abilities_cant"
label="What they can't do"
/>{" "}
</Grid> </Grid>
</Grid> </Grid>
<div /> <div />
<Grid <Grid container direction="row" justifyContent="center" alignItems="flex-end" spacing={3}>
container <Grid item>
direction="row" <Field type="input" as={TextField} multiline rows={3} name="team_abilities_well" label="What they did Well" />
justify="center" </Grid>
alignItems="flex-start" <Grid item>
spacing={3}> <Field type="input" as={TextField} multiline rows={3} name="team_abilities_struggle" label="What they struggled with" />
</Grid>
<Grid item>
<Field type="input" as={TextField} multiline rows={3} name="team_abilities_cant" label="What they can't do" />
</Grid>
</Grid>
<div />
<Grid container direction="row" justifyContent="center" alignItems="flex-end" spacing={3}>
<Button type="submit" disabled={isSubmitting}> <Button type="submit" disabled={isSubmitting}>
{" "} Submit
Submit{" "}
</Button> </Button>
</Grid> </Grid>
</Form> </Form>
+6 -6
View File
@@ -1,10 +1,10 @@
.welcome { .welcome {
display: block; display: block;
/* margin-left: auto; margin-left: auto;
margin-right: auto; */ margin-right: auto;
width: 100%; width: 100%;
text-align: center; text-align: center;
margin: auto; margin: auto;
} }
.welcome img { .welcome img {
+2 -2
View File
@@ -3,9 +3,9 @@ import "./WelcomePage.css";
import "../App.css"; import "../App.css";
const WelcomePage = () => { const WelcomePage = () => {
return ( return (
<div class = "welcome"> <div className="welcome">
<h1>Welcome to Ridgebotics Scouting Web Application 2022</h1> <h1>Welcome to Ridgebotics Scouting Web Application 2022</h1>
<img src = "/WelcomePageImage.webp"/> <img src="/WelcomePageImage.webp" />
</div> </div>
) )
} }
+91 -47
View File
@@ -1,8 +1,13 @@
export class ProcessedDataBucket { export class ProcessedDataBucket {
constructor() { constructor() {
this.teamData = null; this.teamData = {};
this.matchData = null; this.matchData = {};
this.updateData = (db) => { this.updateData = (db) => {
//reset data
this.teamData = {};
this.matchData = {};
console.log(db);
db.allDocs({ db.allDocs({
include_docs: true, include_docs: true,
}) })
@@ -11,55 +16,94 @@ export class ProcessedDataBucket {
result.rows.forEach((dbentry) => { result.rows.forEach((dbentry) => {
let doc = dbentry.doc; let doc = dbentry.doc;
console.log(doc); console.log(doc);
//if there's no processed data on a team yet, create a default data entry
if (typeof this.teamData[doc.team_name] === "undefined") { // //if there's no processed data on a team yet, create a default data entry
this.teamData[doc.team_name] = { // if (typeof this.teamData[doc.team_number] === "undefined") {
team_name: doc.team_name, // this.teamData[doc.team_number] = {
alliance: doc.alliance, // team_number: doc.team_number,
games_played: 0, // games_played: 0,
climbs_none: 0, // data_sets: {
climbs_low: 0, // upper_hub_auto: [],
climbs_mid: 0, // lower_hub_auto: [],
climbs_high: 0, // upper_hub: [],
climbs_transverse: 0, // lower_hub: [],
points: 0, // match_points: []
point_average: 0, // },
num_disables: 0, // climbs_none: 0,
disables_average: 0, // climbs_low: 0,
num_flips: 0, // climbs_mid: 0,
flips_average: 0, // climbs_high: 0,
fouls: 0, // climbs_transverse: 0,
fouls_average: 0, // points_average: 0,
fouls_tech: 0, // num_disables: 0,
fouls_tech_average: 0, // num_flips: 0,
}; // fouls: 0,
} // fouls_tech: 0,
let thisTeamData = this.teamData[doc.team_name]; // red_cards: 0,
console.log(thisTeamData); // yellow_cards: 0
let new_team_data = { // };
...thisTeamData, // }
games_played: thisTeamData.games_played + 1,
num_climbs: thisTeamData.num_climbs + (doc.climb == true ? 1 : 0), // //add this game's data to the respective team data:
num_disables: thisTeamData.num_disables + (doc.disabled == true ? 1 : 0), // let thisTeamData = this.teamData[doc.team_number];
num_flips: thisTeamData.num_flips + (doc.flipped_over == true ? 1 : 0), // thisTeamData.games_played++;
fouls: thisTeamData.fouls + parseInt(doc.fouls),
fouls_tech: thisTeamData.fouls_tech + parseInt(doc.fouls_tech), // let match_points
inner_port: thisTeamData.inner_port + parseInt(doc.inner_port), // = parseInt(doc.taxi_auto)
outer_port: thisTeamData.outer_port + parseInt(doc.outer_port), // + parseInt(doc.upper_hub_auto) * 4
lower_port: thisTeamData.lower_port + parseInt(doc.lower_port), // + parseInt(doc.lower_hub_auto) * 2
}; // + parseInt(doc.upper_hub_teleop) * 2
console.log(new_team_data); // + parseInt(doc.lower_hub_teleop) * 1
// + (parseInt(doc.climb_level) == 0 ? 4 : 0)
// + (parseInt(doc.climb_level) == 1 ? 6 : 0)
// + (parseInt(doc.climb_level) == 2 ? 10 : 0)
// + (parseInt(doc.climb_level) == 3 ? 15 : 0)
// ;
// //data sets
// thisTeamData.data_sets.upper_hub_auto.push(parseInt(doc.upper_hub_auto));
// thisTeamData.data_sets.lower_hub_auto.push(parseInt(doc.lower_hub_auto));
// thisTeamData.data_sets.upper_hub.push(parseInt(doc.upper_hub));
// thisTeamData.data_sets.lower_hub.push(parseInt(doc.lower_hub));
// thisTeamData.data_sets.match_points.push(match_points);
// //climb data
// switch (parseInt(doc.climb_level)) {
// case 0:
// thisTeamData.climbs_none++;
// break;
// case 1:
// thisTeamData.climbs_low++;
// break;
// case 2:
// thisTeamData.climbs_mid++;
// break;
// case 3:
// thisTeamData.climbs_high++;
// break;
// case 4:
// thisTeamData.climbs_transverse++;
// break;
// default:
// console.error('Invalid Climb Level (how did this even happen lol?): ' + doc.climb_level);
// break;
// }
// //misc data
// thisTeamData.num_disables += doc.disabled ? 1 : 0;
// thisTeamData.num_flips += doc.flipped ? 1 : 0;
// thisTeamData.fouls += parseInt(doc.fouls);
// thisTeamData.fouls_tech += parseInt(doc.fouls_tech);
// thisTeamData.red_cards += parseInt(doc.red_cards);
// thisTeamData.yellow_cards += parseInt(doc.yellow_cards);
// console.log(thisTeamData);
}); });
}) })
.catch((err) => { .catch((err) => {
console.log("Error Fetching Docs from Database!"); console.log("Error while processing data!");
console.log(err); console.error(err);
}); });
let datasets = [
{
data: [],
},
];
}; };
} }
} }
@@ -1,5 +1,4 @@
import React, { useContext, useState } from "react"; import React, { useContext, useState } from "react";
import { useLocalDb } from "./DbContext";
import { ProcessedDataBucket } from "./ProcessedDataBucket.jsx" import { ProcessedDataBucket } from "./ProcessedDataBucket.jsx"
export const ProcessedDataBucketContext = React.createContext(); export const ProcessedDataBucketContext = React.createContext();
@@ -25,16 +25,15 @@ const InputNumberField = (props) => {
name={props.name} name={props.name}
label={props.label} label={props.label}
InputProps={{ InputProps={{
style: { fontSize: 40, "max-width": 300 }, style: { fontSize: 40, "maxWidth": 300 },
startAdornment: ( startAdornment: (
<InputAdornment position="start"> <InputAdornment position="start">
<IconButton <IconButton
onClick={() => { onClick={() => {
setFieldValue(props.name, Math.max(parseInt(values[props.name]) - 1, 0)); setFieldValue(props.name, Math.max(parseInt(values[props.name]) - 1, 0));
}} }}
iconStyle={{ width: '48px', height: '48px' }}
style={{ width: '96px', height: '96px', padding: '24px' }} style={{ width: '96px', height: '96px', padding: '24px' }}
touch={true} touch="true"
> >
<RemoveCircleOutline style={{ fontSize: 50 }} /> <RemoveCircleOutline style={{ fontSize: 50 }} />
</IconButton> </IconButton>
@@ -46,11 +45,8 @@ const InputNumberField = (props) => {
onClick={() => { onClick={() => {
setFieldValue(props.name, Math.max(parseInt(values[props.name]) + 1, 0)); setFieldValue(props.name, Math.max(parseInt(values[props.name]) + 1, 0));
}} }}
iconStyle={{ width: '100px', height: '100px' }}
style={{ width: '96px', height: '96px', padding: '0' }} style={{ width: '96px', height: '96px', padding: '0' }}
touch={true} touch="true"
size="large"
> >
<AddCircleOutline style={{ fontSize: 50 }} /> <AddCircleOutline style={{ fontSize: 50 }} />
</IconButton> </IconButton>
@@ -0,0 +1,8 @@
.Navigation {
position: -webkit-sticky; /*for safari*/
position: sticky;
top: 0;
left: 0;
width: 100%;
z-index: 1;
}
@@ -1,6 +1,7 @@
import React, {Component} from 'react' import React, {Component} from 'react'
import Toolbar from './Toolbar/Toolbar' import Toolbar from './Toolbar/Toolbar'
import SideDrawer from './Drawer/SideDrawer' import SideDrawer from './Drawer/SideDrawer'
import './Navigation.css'
class Navigation extends Component { class Navigation extends Component {
state = { state = {
@@ -15,7 +16,7 @@ class Navigation extends Component {
render() { render() {
return ( return (
<div> <div className="Navigation">
<Toolbar drawerClickHandler={this.drawerToggleClickHandler} /> <Toolbar drawerClickHandler={this.drawerToggleClickHandler} />
<SideDrawer show={this.state.sideDrawerOpen} drawerClickHandler={this.drawerToggleClickHandler}/> <SideDrawer show={this.state.sideDrawerOpen} drawerClickHandler={this.drawerToggleClickHandler}/>
</div> </div>
@@ -1,63 +1,60 @@
.toolbar { .toolbar {
position: sticky; width: 100%;
top: 0px; background: #00a65a;
left: 0; height: 56px;
width: 100%;
background: #00a65a;
height: 56px
} }
.toolbar_navigation { .toolbar_navigation {
display: flex; display: flex;
height: 100%; height: 100%;
align-items: center; align-items: center;
padding: 0 1rem; padding: 0 1rem;
} }
.toolbar_logo { .toolbar_logo {
margin-left: 0.5rem; margin-left: 0.5rem;
} }
.toolbar_logo a { .toolbar_logo a {
color: white; color: white;
text-decoration: none; text-decoration: none;
font-size: 2.0rem; font-size: 2rem;
} }
.toolbar_spacer { .toolbar_spacer {
flex: 1; flex: 1;
} }
.toolbar_items ul { .toolbar_items ul {
list-style: none; list-style: none;
margin: 0; margin: 0;
padding: 0 0; padding: 0 0;
display: flex; display: flex;
} }
.toolbar_items li { .toolbar_items li {
color: white; color: white;
background-color: #00000000; background-color: #00000000;
text-decoration: none; text-decoration: none;
font-size: 1.5rem; font-size: 1.5rem;
padding: 0 0.6rem; padding: 0 0.6rem;
} }
.toolbar_items a { .toolbar_items a {
color: white; color: white;
text-decoration: none; text-decoration: none;
padding-top: 0.8rem; padding-top: 0.8rem;
padding-bottom: 0.8rem; padding-bottom: 0.8rem;
} }
.toolbar_items a:hover, .toolbar_items a:hover,
.toolbar_items a:active { .toolbar_items a:active {
color: #ff3b76; color: #ff3b76;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.toolbar_items { .toolbar_items {
display: none; display: none;
} }
} }
@@ -10,7 +10,7 @@ const Toolbar = props => {
<header className="toolbar"> <header className="toolbar">
<nav className="toolbar_navigation"> <nav className="toolbar_navigation">
<DrawerButton click={props.drawerClickHandler} /> <DrawerButton click={props.drawerClickHandler} />
<div className = "toolbar_logo"><Link to="/">Ridgebotics Scouting</Link></div> <div className = "toolbar_logo"><Link to="/">Ridgebotics Scouting ;)</Link></div>
<div className = "toolbar_spacer" /> <div className = "toolbar_spacer" />
<div className = "toolbar_items"> <div className = "toolbar_items">
<PagesList /> <PagesList />
+8 -7
View File
@@ -1,12 +1,13 @@
body { body {
background-color: rgb(20, 26, 24); background-color: rgb(20, 26, 24);
color: white; color: white;
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
overflow-x: hidden;
} }
code { code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
} }
+7 -1
View File
@@ -2,7 +2,8 @@ import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import "./index.css"; import "./index.css";
import App from "./App"; import App from "./App";
import reportWebVitals from "./reportWebVitals"; import * as serviceWorkerRegistration from './serviceWorkerRegistration';
import reportWebVitals from './reportWebVitals';
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
@@ -11,6 +12,11 @@ ReactDOM.render(
document.getElementById("root") document.getElementById("root")
); );
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.register();
// If you want to start measuring performance in your app, pass a function // If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log)) // to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
+72
View File
@@ -0,0 +1,72 @@
/* eslint-disable no-restricted-globals */
// This service worker can be customized!
// See https://developers.google.com/web/tools/workbox/modules
// for the list of available Workbox modules, or add any other
// code you'd like.
// You can also remove this file if you'd prefer not to use a
// service worker, and the Workbox build step will be skipped.
import { clientsClaim } from 'workbox-core';
import { ExpirationPlugin } from 'workbox-expiration';
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';
clientsClaim();
// Precache all of the assets generated by your build process.
// Their URLs are injected into the manifest variable below.
// This variable must be present somewhere in your service worker file,
// even if you decide not to use precaching. See https://cra.link/PWA
precacheAndRoute(self.__WB_MANIFEST);
// Set up App Shell-style routing, so that all navigation requests
// are fulfilled with your index.html shell. Learn more at
// https://developers.google.com/web/fundamentals/architecture/app-shell
const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$');
registerRoute(
// Return false to exempt requests from being fulfilled by index.html.
({ request, url }) => {
// If this isn't a navigation, skip.
if (request.mode !== 'navigate') {
return false;
} // If this is a URL that starts with /_, skip.
if (url.pathname.startsWith('/_')) {
return false;
} // If this looks like a URL for a resource, because it contains // a file extension, skip.
if (url.pathname.match(fileExtensionRegexp)) {
return false;
} // Return true to signal that we want to use the handler.
return true;
},
createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
);
// An example runtime caching route for requests that aren't handled by the
// precache, in this case same-origin .png requests like those from in public/
registerRoute(
// Add in any other file extensions or routing criteria as needed.
({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), // Customize this strategy as needed, e.g., by changing to CacheFirst.
new StaleWhileRevalidate({
cacheName: 'images',
plugins: [
// Ensure that once this runtime cache reaches a maximum size the
// least-recently used images are removed.
new ExpirationPlugin({ maxEntries: 50 }),
],
})
);
// This allows the web app to trigger skipWaiting via
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
// Any other custom service worker logic can go here.
+137
View File
@@ -0,0 +1,137 @@
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://cra.link/PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://cra.link/PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then((registration) => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://cra.link/PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch((error) => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' },
})
.then((response) => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then((registration) => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log('No internet connection found. App is running in offline mode.');
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready
.then((registration) => {
registration.unregister();
})
.catch((error) => {
console.error(error.message);
});
}
}