Compare commits

..

4 Commits

Author SHA1 Message Date
the-all-good
6ecc1dd7e0 MOAR STILE 2024-12-15 16:39:51 +11:00
the-all-good
8f8c4fb267 The man said unto me, "dont you mean create pool?" 2024-12-12 21:20:21 +11:00
the-all-good
4c4d977401 Big things for the stock portfolio 2024-12-12 21:06:19 +11:00
the-all-good
db7f75feb4 Include Tailwind & Start Component 2024-12-11 23:11:53 +11:00
24 changed files with 275 additions and 223 deletions

View File

@ -5,6 +5,6 @@
Bingo card web application Bingo card web application
Backend `Rust` @benjamyn Backend `Rust` @benjamyn
Frontend `React` @balgie Frontend `TBD` @balgie
Docs `MD files` @here Docs `MD files` @here

0
backend/.gitignore vendored
View File

View File

@ -6,8 +6,3 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
glob = "0.3.1"
serde = { version = "1.0.215", features = ["serde_derive"] }
serde_json = "1.0.133"
toml = "0.8.19"
untildify = "0.1.1"

View File

@ -1,18 +0,0 @@
use serde::Deserialize;
use untildify::untildify;
use crate::files;
#[derive(Deserialize)]
pub struct Config {
pub config_dir: String,
}
impl Config {
pub fn load() -> Config {
let config_contents = files::load_file_contents(untildify("~/.config/bingobongo.toml"));
let config: Config = toml::from_str(config_contents.as_str()).unwrap();
return config;
}
}

View File

@ -1,34 +0,0 @@
use glob::glob;
use std::fs::File;
use std::io::prelude::*;
pub fn load_file_contents(file_path: String) -> String {
let file = File::open(file_path);
match file {
Ok(mut f) => {
let mut contents = String::new();
f.read_to_string(&mut contents).unwrap();
return contents;
}
Err(e) => {
return e.to_string();
}
}
}
pub fn list_files(path: String, extension: String) -> Vec<String> {
let mut tmp_path = path.clone();
let mut files = Vec::<String>::new();
let glob_path = format!("{}/*.{}", tmp_path, extension);
// tmp_path.push_str(&glob_path);
// println!("{}", glob_path);
for entry in glob(&glob_path).unwrap() {
match entry {
Ok(p) => files.push(String::from(p.display().to_string())),
Err(e) => return vec![String::new()],
}
}
return files;
}

View File

@ -1,25 +1,3 @@
mod config;
use config::Config;
mod files;
use files::*;
mod structs;
use structs::*;
fn main() { fn main() {
let config = Config::load(); println!("Hello, world!");
let pool = structs::BingoPool::load_from_file(String::from(
"/home/ben/Documents/Projects/rust/BingoBongo/backend/workingdir/pools/testdata.toml",
));
// let pools = BingoPools::load_pools();
let files = files::list_files(
String::from("/home/ben/Documents/Projects/rust/BingoBongo/backend/workingdir/pools"),
String::from("toml"),
);
println!("{:?}", files);
println!("{}", pool);
} }

View File

@ -1,87 +0,0 @@
use core::fmt;
use serde::Deserialize;
use crate::files;
#[derive(Deserialize)]
pub struct BingoSquare {
option: String,
}
impl BingoSquare {
pub fn new(option: String) -> BingoSquare {
BingoSquare { option }
}
}
#[derive(Deserialize)]
pub struct Meta {
name: String,
description: String,
}
impl Meta {
pub fn new(name: String, description: String) -> Meta {
Meta { name, description }
}
}
#[derive(Deserialize)]
pub struct BingoPool {
meta: Meta,
squares: Vec<BingoSquare>,
}
impl fmt::Display for BingoPool {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Name: {}\n", self.meta.name).unwrap();
write!(f, "Description: {}\n\n", self.meta.description).unwrap();
for square in &self.squares {
write!(f, "Square: {}\n", square.option).unwrap();
}
write!(f, "")
}
}
impl BingoPool {
pub fn new() -> BingoPool {
BingoPool {
meta: Meta::new(String::from("Meme"), String::from("Funny")),
squares: Vec::<BingoSquare>::new(),
}
}
pub fn load_from_file(file_path: String) -> BingoPool {
let file_contents = files::load_file_contents(file_path);
let bingopool: BingoPool = toml::from_str(&file_contents).expect("Syntax error in toml");
return bingopool;
}
}
pub struct BingoPools {
pools: Vec<BingoPool>,
}
impl BingoPools {
pub fn load_pools() -> BingoPools {
BingoPools {
pools: Vec::<BingoPool>::new(),
}
}
}
pub struct BingoBoard {
size: i64,
squares: Vec<BingoSquare>,
}
impl BingoBoard {
pub fn new(size: i64) -> BingoBoard {
BingoBoard {
size: size,
squares: Vec::<BingoSquare>::new(),
}
}
pub fn init(&self) {}
}

View File

@ -1,18 +0,0 @@
[meta]
name = 'Name of pool'
description = 'Description of pool'
[[squares]]
option = 'First square'
[[squares]]
option = 'Second square'
[[squares]]
option = 'Third square'
[[squares]]
option = 'Fourth square'
[[squares]]
option = 'Fifth square'

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

View File

@ -13,8 +13,12 @@
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^7.0.2",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
},
"devDependencies": {
"tailwindcss": "^3.4.16"
} }
}, },
"node_modules/@adobe/css-tools": { "node_modules/@adobe/css-tools": {
@ -3716,6 +3720,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
"license": "MIT"
},
"node_modules/@types/eslint": { "node_modules/@types/eslint": {
"version": "8.56.12", "version": "8.56.12",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz",
@ -8122,9 +8132,9 @@
} }
}, },
"node_modules/express": { "node_modules/express": {
"version": "4.21.1", "version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
@ -8146,7 +8156,7 @@
"methods": "~1.1.2", "methods": "~1.1.2",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"path-to-regexp": "0.1.10", "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7", "proxy-addr": "~2.0.7",
"qs": "6.13.0", "qs": "6.13.0",
"range-parser": "~1.2.1", "range-parser": "~1.2.1",
@ -8161,6 +8171,10 @@
}, },
"engines": { "engines": {
"node": ">= 0.10.0" "node": ">= 0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/express/node_modules/debug": { "node_modules/express/node_modules/debug": {
@ -12285,9 +12299,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "0.1.10", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/path-type": { "node_modules/path-type": {
@ -14154,6 +14168,55 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-router": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.0.2.tgz",
"integrity": "sha512-m5AcPfTRUcjwmhBzOJGEl6Y7+Crqyju0+TgTQxoS4SO+BkWbhOrcfZNq6wSWdl2BBbJbsAoBUb8ZacOFT+/JlA==",
"license": "MIT",
"dependencies": {
"@types/cookie": "^0.6.0",
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0",
"turbo-stream": "2.4.0"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
},
"peerDependenciesMeta": {
"react-dom": {
"optional": true
}
}
},
"node_modules/react-router-dom": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.0.2.tgz",
"integrity": "sha512-VJOQ+CDWFDGaWdrG12Nl+d7yHtLaurNgAQZVgaIy7/Xd+DojgmYLosFfZdGz1wpxmjJIAkAMVTKWcvkx1oggAw==",
"license": "MIT",
"dependencies": {
"react-router": "7.0.2"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
"react": ">=18",
"react-dom": ">=18"
}
},
"node_modules/react-router/node_modules/cookie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/react-scripts": { "node_modules/react-scripts": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@ -15037,6 +15100,12 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
} }
}, },
"node_modules/set-cookie-parser": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"license": "MIT"
},
"node_modules/set-function-length": { "node_modules/set-function-length": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@ -15993,9 +16062,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "3.4.15", "version": "3.4.16",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.16.tgz",
"integrity": "sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw==", "integrity": "sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
@ -16007,7 +16076,7 @@
"glob-parent": "^6.0.2", "glob-parent": "^6.0.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"jiti": "^1.21.6", "jiti": "^1.21.6",
"lilconfig": "^2.1.0", "lilconfig": "^3.1.3",
"micromatch": "^4.0.8", "micromatch": "^4.0.8",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"object-hash": "^3.0.0", "object-hash": "^3.0.0",
@ -16029,6 +16098,18 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/tailwindcss/node_modules/lilconfig": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
"license": "MIT",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/antonk52"
}
},
"node_modules/tapable": { "node_modules/tapable": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@ -16357,6 +16438,12 @@
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/turbo-stream": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
"integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==",
"license": "ISC"
},
"node_modules/type-check": { "node_modules/type-check": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",

View File

@ -8,6 +8,7 @@
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^7.0.2",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
@ -34,5 +35,8 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"devDependencies": {
"tailwindcss": "^3.4.16"
} }
} }

View File

@ -29,15 +29,5 @@
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body> </body>
</html> </html>

View File

@ -2,6 +2,10 @@
text-align: center; text-align: center;
} }
html, body, #app, #app>div {
height: 100%
}
.App-logo { .App-logo {
height: 40vmin; height: 40vmin;
pointer-events: none; pointer-events: none;

View File

@ -1,3 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
body { body {
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',

View File

@ -1,18 +1,19 @@
import logo from '../resources/logo.svg'; import { BrowserRouter, Routes, Route } from 'react-router-dom';
import '../css/App.css'; import '../css/App.css';
import Leaderboard from './components/leaderboard'; import GameView from './pages/gameView';
import GameCard from './components/gameCard'; import Home from './pages/home';
import Button from './components/button'; import { GameProvider } from './contexts/GameContext';
function App() { function App() {
return ( return (
<div className="App"> <GameProvider>
<header className="App-header"> <BrowserRouter>
<Leaderboard /> <Routes>
<GameCard /> <Route path="/" element={<Home />} />
<Button displayValue="Click Me" /> <Route path="/game/:id" element={<GameView />} />
</header> </Routes>
</div> </BrowserRouter>
</GameProvider>
); );
} }

View File

@ -1,12 +1,22 @@
import React from 'react'; import React from 'react';
import { Link} from "react-router-dom";
function Button(props){ function Button(props){
return ( return (
<button> <Link
{console.log(props)} to={props.link}
disabled={props.disabled}
className={`p-2 m-2 rounded-2xl text-center font-bold border-2
${props.disabled ? 'pointer-events-none text-gray-400 bg-slate-700' : 'text-white hover:font-bold hover:text-black hover:bg-slate-400 border-black bg-slate-600'}`}>
{props.displayValue} {props.displayValue}
</button> </Link>
); );
} }
Button.defaultProps = {
disabled: false,
link: "/",
displayValue: "Missing displayValue"
}
export default Button; export default Button;

View File

@ -0,0 +1,17 @@
import React from 'react';
import ListGameCell from '../components/listGameCell';
function GameList(props){
return (
<div className="bg-gray-500 min-h-1/2 border-solid rounded-xl min-w-40 border-2 border-black w-1/3 p-4 m-4">
{props.games.map((game) => (
<ListGameCell
key={game.gameId}
data={game}
/>
))}
</div>
)
}
export default GameList;

View File

@ -1,7 +1,11 @@
import React from 'react'; import React from 'react';
function Leaderboard(){ function Leaderboard(){
return <h1>Leaderboard</h1>; return (
<div className="bg-gray-500 min-h-1/2 border-solid rounded-xl border-2 border-black min-w-40 w-1/3 p-4 m-4">
</div>
);
} }
export default Leaderboard; export default Leaderboard;

View File

@ -0,0 +1,23 @@
import React, { useContext } from 'react';
import { GameContext } from '../contexts/GameContext';
function ListGameCell(props){
const { selectedGame, setSelectedGame } = useContext(GameContext);
const isSelected = selectedGame === props.data.gameId;
return (
<div
className={`w-full p-2 my-1 border-2 rounded-sm border-black ${
isSelected ? 'bg-blue-400' : 'bg-slate-200 hover:bg-slate-400'
}`}
onClick={() => isSelected ? setSelectedGame(null) : setSelectedGame(props.data.gameId)}
>
<p>Game ID: {props.data.gameId}</p>
<p>
{props.data.completed}/{props.data.gameSize}
</p>
</div>
);
}
export default ListGameCell;

View File

@ -0,0 +1,16 @@
import React, { createContext, useState } from 'react';
// Create a context
export const GameContext = createContext(null);
// Context Provider
export function GameProvider({ children }) {
const [selectedGame, setSelectedGame] = useState({ selectedGame: null });
return (
<GameContext.Provider value={{ selectedGame, setSelectedGame }}>
{children}
</GameContext.Provider>
);
}

View File

@ -0,0 +1,20 @@
import React from 'react';
import {useParams} from 'react-router-dom';
import GameCard from '../components/gameCard';
import Button from '../components/button';
function GameView(){
const {id} = useParams();
return (
<div>
<h1>Game ID: {id}</h1>
<GameCard />
<div className="w-full flex flex-row-reverse">
<Button displayValue="Back" link="/"/>
<Button displayValue="BINGO" />
</div>
</div>
);
}
export default GameView;

View File

@ -0,0 +1,45 @@
import React, { useContext } from 'react';
import Leaderboard from '../components/leaderboard';
import GameList from '../components/gameList';
import Button from '../components/button';
import { GameContext } from '../contexts/GameContext';
function Home(){
let {selectedGame, setSelectedGame} = useContext(GameContext);
const games = [
{
"gameId": 1,
"completed": 3,
"gameSize": 9,
},{
"gameId": 2,
"completed": 16,
"gameSize": 25,
},{
"gameId": 4,
"completed": 14,
"gameSize": 25,
},{
"gameId": 7,
"completed": 3,
"gameSize": 9,
}
]
return (
<div className="min-h-full h-full flex flex-row">
<Leaderboard />
<GameList games={games}/>
<div className="flex flex-col">
<Button displayValue="New Game" link="/game/1"/>
<Button displayValue="Join Game" link={`/game/${selectedGame}`} disabled={selectedGame ? false : true}/>
<Button displayValue="List Pools"/>
<Button displayValue="Create Pool"/>
</div>
</div>
);
}
export default Home;

View File

@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}