Deprecated webapp (old stats stack)

This commit is contained in:
Anthony Mineo 2020-09-29 14:43:26 -04:00
parent aed14743e6
commit c1fcb61731
54 changed files with 0 additions and 22410 deletions

View file

@ -1,6 +0,0 @@
{
"plugins": [
"macros",
"@babel/plugin-proposal-class-properties"
]
}

View file

@ -1,9 +0,0 @@
root = true
[*]
indent_size = 2
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

12
app/webapp/.gitignore vendored
View file

@ -1,12 +0,0 @@
# Adonis directory for storing tmp files
tmp
# Don't store build
build
# Environment variables, never commit this file
.env
public/hot
public/mix-manifest.json
public/dist

View file

@ -1,9 +0,0 @@
# Adonis Slim App
The Adonis slim app is the tinest boilerplate to create Adonisjs applications with minimal footprint and you get all the goodies of Adonis IoC container, autoloading, ace commands etc.
## What's next?
This project structure can scale as you go, simply execute the `ace` commands to create **Controllers**, **Models**, etc for you.
Also make sure to read the [guides](http://dev.adonisjs.com/docs/4.0/installation)

View file

@ -1,21 +0,0 @@
'use strict'
/*
|--------------------------------------------------------------------------
| Ace Commands
|--------------------------------------------------------------------------
|
| The ace file is just a regular Javascript file but with no extension. You
| can call `node ace` followed by the command name and it just works.
|
| Also you can use `adonis` followed by the command name, since the adonis
| global proxy all the ace commands.
|
*/
const { Ignitor } = require('@adonisjs/ignitor')
new Ignitor(require('@adonisjs/fold'))
.appRoot(__dirname)
.fireAce()
.catch(console.error)

View file

@ -1,64 +0,0 @@
'use strict';
const Database = use('Database');
class GameController {
// /games
async index({ inertia }) {
const pageTitle = `Latest Games`;
// const gamesQry = await Database.raw(`
// SELECT distinct game_id,map,gametype, datestamp
// FROM games
// WHERE (stats->>'score' IS NOT NULL) AND (game_id <> 0)
// ORDER BY game_id desc
// LIMIT 2000;
// `);
// const gamesQry = await Database.raw(`
// WITH gamelist AS (
// SELECT DISTINCT p.game_id, p.map, p.gametype, p.datestamp, count(*) OVER (PARTITION BY game_id) as count from games p
// )
// SELECT * from gamelist
// WHERE (count > 1) AND (game_id <> 0)
// ORDER BY game_id desc
// LIMIT 2000
// `);
// // filter out duplicate game_ids (https://dev.to/marinamosti/removing-duplicates-in-an-array-of-objects-in-js-with-sets-3fep)
// const games = gamesQry.rows.reduce((game, current) => {
// const x = game.find(item => item.game_id === current.game_id);
// if (!x) {
// return game.concat([current]);
// } else {
// return game;
// }
// }, []);
const games = await Database.from('games').orderBy('game_id', 'desc').limit(200);
// const CTFgames = await Database.from('games').where({gametype: 'CTFGame'}).orderBy('game_id', 'desc').limit(120);
// const LAKgames = await Database.from('games').where({gametype: 'LakRabbitGame'}).orderBy('game_id', 'desc').limit(120);
// move the 0 score display logic here
return inertia.render('Games/Main', { pageTitle, games }, { edgeVar: 'server-variable' });
}
// game/:game_id
async game({ inertia, request }) {
const gameInfo = await Database.from('game_detail')
.select('game_id', 'map', 'player_name', 'player_guid', 'gametype', 'stats', 'datestamp')
.where({ game_id: request.params.game_id })
.orderByRaw("stats->>'scoreTG' desc");
const pageTitle = {
name: gameInfo[0]['map'],
gametype: gameInfo[0]['gametype']
};
return inertia.render('Games/Game', { pageTitle, gameInfo }, { edgeVar: 'server-variable' });
}
}
module.exports = GameController;

View file

@ -1,12 +0,0 @@
'use strict'
class IndexController {
index({ inertia }) {
const pageTitle = "Home!"
return inertia.render('Index', { pageTitle }, { edgeVar: 'server-variable' })
}
}
module.exports = IndexController

View file

@ -1,82 +0,0 @@
'use strict';
const Database = use('Database');
class PlayerController {
// Main Players List
async index({ inertia }) {
const pageTitle = 'All Players';
const players = await Database.table('players')
.distinct(
'player_guid',
'player_name',
'total_games_ctfgame',
'total_games_dmgame',
'total_games_lakrabbitgame',
'total_games_sctfgame',
'updated_at'
)
.groupBy('player_guid')
.orderBy('player_name', 'asc')
.limit(1000);
return inertia.render('Players/Main', { pageTitle, players }, { edgeVar: 'server-variable' });
}
// Player Detail
async player({ inertia, request }) {
const playerInfo = await Database.from('players')
.distinct(
'player_guid',
'player_name',
'total_games_ctfgame',
'total_games_dmgame',
'total_games_lakrabbitgame',
'total_games_sctfgame'
)
.where({ player_guid: request.params.player_guid })
.limit(50);
const playerStatData = await Database.from('game_detail')
.select('game_id', 'gametype', 'stats')
.where({ player_guid: request.params.player_guid })
.orderBy('game_id', 'desc')
.limit(50);
// Dynamically generate and sum the stats object
let playerStatTotals = {},
statKeys = Object.keys(playerStatData[0].stats);
for (let i = 0; i < statKeys.length; i++) {
if (statKeys[i] === 'map' || statKeys[i] === 'dateStamp' || statKeys[i] === 'timeDayMonth') {
continue;
}
playerStatTotals[statKeys[i]] = 0;
}
// Loop through the playerStatsData query from the DB
playerStatData.map((statLine) => {
// look through each object in playerStatsData array
for (let [ key, value ] of Object.entries(statLine.stats)) {
// console.log(`${key}: ${value}`);
// If the stat item exists, add it -- if not create a new key in playerStatTotals
if (playerStatTotals.hasOwnProperty(key) === true) {
playerStatTotals[key] = playerStatTotals[key] + Number(value);
} else {
playerStatTotals[key] = Number(value);
}
}
});
let playerData = {
player: playerInfo[0],
stats: playerStatData,
totals: playerStatTotals
};
const pageTitle = playerData.player.player_name;
return inertia.render('Players/Player', { pageTitle, playerData }, { edgeVar: 'server-variable' });
}
}
module.exports = PlayerController;

View file

@ -1,10 +0,0 @@
// babel-plugin-macros.config.js
module.exports = {
twin: {
config: './tailwind.config.js',
styled: '@emotion/styled',
format: 'auto',
hasSuggestions: true,
debug: false
}
}

View file

@ -1,243 +0,0 @@
'use strict'
/** @type {import('@adonisjs/framework/src/Env')} */
const Env = use('Env')
module.exports = {
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
|
| This value is the name of your application and can used when you
| need to place the application's name in a email, view or
| other location.
|
*/
name: Env.get('APP_NAME', 'AdonisJs'),
/*
|--------------------------------------------------------------------------
| App Key
|--------------------------------------------------------------------------
|
| App key is a randomly generated 16 or 32 characters long string required
| to encrypted cookies, sessions and other sensitive data.
|
*/
appKey: Env.getOrFail('APP_KEY'),
http: {
/*
|--------------------------------------------------------------------------
| Allow Method Spoofing
|--------------------------------------------------------------------------
|
| Method spoofing allows to make requests by spoofing the http verb.
| Which means you can make a GET request but instruct the server to
| treat as a POST or PUT request. If you want this feature, set the
| below value to true.
|
*/
allowMethodSpoofing: true,
/*
|--------------------------------------------------------------------------
| Trust Proxy
|--------------------------------------------------------------------------
|
| Trust proxy defines whether X-Forwaded-* headers should be trusted or not.
| When your application is behind a proxy server like nginx, these values
| are set automatically and should be trusted. Apart from setting it
| to true or false Adonis supports handful or ways to allow proxy
| values. Read documentation for that.
|
*/
trustProxy: false,
/*
|--------------------------------------------------------------------------
| Subdomains
|--------------------------------------------------------------------------
|
| Offset to be used for returning subdomains for a given request.For
| majority of applications it will be 2, until you have nested
| sudomains.
| cheatsheet.adonisjs.com - offset - 2
| virk.cheatsheet.adonisjs.com - offset - 3
|
*/
subdomainOffset: 2,
/*
|--------------------------------------------------------------------------
| JSONP Callback
|--------------------------------------------------------------------------
|
| Default jsonp callback to be used when callback query string is missing
| in request url.
|
*/
jsonpCallback: 'callback',
/*
|--------------------------------------------------------------------------
| Etag
|--------------------------------------------------------------------------
|
| Set etag on all HTTP response. In order to disable for selected routes,
| you can call the `response.send` with an options object as follows.
|
| response.send('Hello', { ignoreEtag: true })
|
*/
etag: false
},
views: {
/*
|--------------------------------------------------------------------------
| Cache Views
|--------------------------------------------------------------------------
|
| Define whether or not to cache the compiled view. Set it to true in
| production to optimize view loading time.
|
*/
cache: Env.get('CACHE_VIEWS', true)
},
static: {
/*
|--------------------------------------------------------------------------
| Dot Files
|--------------------------------------------------------------------------
|
| Define how to treat dot files when trying to server static resources.
| By default it is set to ignore, which will pretend that dotfiles
| does not exists.
|
| Can be one of the following
| ignore, deny, allow
|
*/
dotfiles: 'ignore',
/*
|--------------------------------------------------------------------------
| ETag
|--------------------------------------------------------------------------
|
| Enable or disable etag generation
|
*/
etag: true,
/*
|--------------------------------------------------------------------------
| Extensions
|--------------------------------------------------------------------------
|
| Set file extension fallbacks. When set, if a file is not found, the given
| extensions will be added to the file name and search for. The first
| that exists will be served. Example: ['html', 'htm'].
|
*/
extensions: false
},
locales: {
/*
|--------------------------------------------------------------------------
| Loader
|--------------------------------------------------------------------------
|
| The loader to be used for fetching and updating locales. Below is the
| list of available options.
|
| file, database
|
*/
loader: 'file',
/*
|--------------------------------------------------------------------------
| Default Locale
|--------------------------------------------------------------------------
|
| Default locale to be used by Antl provider. You can always switch drivers
| in runtime or use the official Antl middleware to detect the driver
| based on HTTP headers/query string.
|
*/
locale: 'en'
},
logger: {
/*
|--------------------------------------------------------------------------
| Transport
|--------------------------------------------------------------------------
|
| Transport to be used for logging messages. You can have multiple
| transports using same driver.
|
| Available drivers are: `file` and `console`.
|
*/
transport: 'console',
/*
|--------------------------------------------------------------------------
| Console Transport
|--------------------------------------------------------------------------
|
| Using `console` driver for logging. This driver writes to `stdout`
| and `stderr`
|
*/
console: {
driver: 'console',
name: 'adonis-app',
level: 'info'
},
/*
|--------------------------------------------------------------------------
| File Transport
|--------------------------------------------------------------------------
|
| File transport uses file driver and writes log messages for a given
| file inside `tmp` directory for your app.
|
| For a different directory, set an absolute path for the filename.
|
*/
file: {
driver: 'file',
name: 'adonis-app',
filename: 'adonis.log',
level: 'info'
}
},
/*
|--------------------------------------------------------------------------
| Generic Cookie Options
|--------------------------------------------------------------------------
|
| The following cookie options are generic settings used by AdonisJs to create
| cookies. However, some parts of the application like `sessions` can have
| seperate settings for cookies inside `config/session.js`.
|
*/
cookie: {
httpOnly: true,
sameSite: false,
path: '/',
maxAge: 7200
}
}

View file

@ -1,157 +0,0 @@
'use strict'
module.exports = {
/*
|--------------------------------------------------------------------------
| JSON Parser
|--------------------------------------------------------------------------
|
| Below settings are applied when the request body contains a JSON payload.
| If you want body parser to ignore JSON payloads, then simply set `types`
| to an empty array.
*/
json: {
/*
|--------------------------------------------------------------------------
| limit
|--------------------------------------------------------------------------
|
| Defines the limit of JSON that can be sent by the client. If payload
| is over 1mb it will not be processed.
|
*/
limit: '1mb',
/*
|--------------------------------------------------------------------------
| strict
|--------------------------------------------------------------------------
|
| When `strict` is set to true, body parser will only parse Arrays and
| Object. Otherwise everything parseable by `JSON.parse` is parsed.
|
*/
strict: true,
/*
|--------------------------------------------------------------------------
| types
|--------------------------------------------------------------------------
|
| Which content types are processed as JSON payloads. You are free to
| add your own types here, but the request body should be parseable
| by `JSON.parse` method.
|
*/
types: [
'application/json',
'application/json-patch+json',
'application/vnd.api+json',
'application/csp-report'
]
},
/*
|--------------------------------------------------------------------------
| Raw Parser
|--------------------------------------------------------------------------
|
|
|
*/
raw: {
types: [
'text/*'
]
},
/*
|--------------------------------------------------------------------------
| Form Parser
|--------------------------------------------------------------------------
|
|
|
*/
form: {
types: [
'application/x-www-form-urlencoded'
]
},
/*
|--------------------------------------------------------------------------
| Files Parser
|--------------------------------------------------------------------------
|
|
|
*/
files: {
types: [
'multipart/form-data'
],
/*
|--------------------------------------------------------------------------
| Max Size
|--------------------------------------------------------------------------
|
| Below value is the max size of all the files uploaded to the server. It
| is validated even before files have been processed and hard exception
| is thrown.
|
| Consider setting a reasonable value here, otherwise people may upload GB's
| of files which will keep your server busy.
|
| Also this value is considered when `autoProcess` is set to true.
|
*/
maxSize: '20mb',
/*
|--------------------------------------------------------------------------
| Auto Process
|--------------------------------------------------------------------------
|
| Whether or not to auto-process files. Since HTTP servers handle files via
| couple of specific endpoints. It is better to set this value off and
| manually process the files when required.
|
| This value can contain a boolean or an array of route patterns
| to be autoprocessed.
*/
autoProcess: true,
/*
|--------------------------------------------------------------------------
| Process Manually
|--------------------------------------------------------------------------
|
| The list of routes that should not process files and instead rely on
| manual process. This list should only contain routes when autoProcess
| is to true. Otherwise everything is processed manually.
|
*/
processManually: []
/*
|--------------------------------------------------------------------------
| Temporary file name
|--------------------------------------------------------------------------
|
| Define a function, which should return a string to be used as the
| tmp file name.
|
| If not defined, Bodyparser will use `uuid` as the tmp file name.
|
| To be defined as. If you are defining the function, then do make sure
| to return a value from it.
|
| tmpFileName () {
| return 'some-unique-value'
| }
|
*/
}
}

View file

@ -1,87 +0,0 @@
'use strict'
module.exports = {
/*
|--------------------------------------------------------------------------
| Origin
|--------------------------------------------------------------------------
|
| Set a list of origins to be allowed. The value can be one of the following
|
| Boolean: true - Allow current request origin
| Boolean: false - Disallow all
| String - Comma seperated list of allowed origins
| Array - An array of allowed origins
| String: * - A wildcard to allow current request origin
| Function - Receives the current origin and should return one of the above values.
|
*/
origin: ['playt2.com'],
/*
|--------------------------------------------------------------------------
| Methods
|--------------------------------------------------------------------------
|
| HTTP methods to be allowed. The value can be one of the following
|
| String - Comma seperated list of allowed methods
| Array - An array of allowed methods
|
*/
methods: ['GET', 'PUT', 'POST', 'OPTIONS', 'HEAD'],
/*
|--------------------------------------------------------------------------
| Headers
|--------------------------------------------------------------------------
|
| List of headers to be allowed via Access-Control-Request-Headers header.
| The value can be on of the following.
|
| Boolean: true - Allow current request headers
| Boolean: false - Disallow all
| String - Comma seperated list of allowed headers
| Array - An array of allowed headers
| String: * - A wildcard to allow current request headers
| Function - Receives the current header and should return one of the above values.
|
*/
headers: true,
/*
|--------------------------------------------------------------------------
| Expose Headers
|--------------------------------------------------------------------------
|
| A list of headers to be exposed via `Access-Control-Expose-Headers`
| header. The value can be on of the following.
|
| Boolean: false - Disallow all
| String: Comma seperated list of allowed headers
| Array - An array of allowed headers
|
*/
exposeHeaders: false,
/*
|--------------------------------------------------------------------------
| Credentials
|--------------------------------------------------------------------------
|
| Define Access-Control-Allow-Credentials header. It should always be a
| boolean.
|
*/
credentials: false,
/*
|--------------------------------------------------------------------------
| MaxAge
|--------------------------------------------------------------------------
|
| Define Access-Control-Max-Age
|
*/
maxAge: 90
}

View file

@ -1,43 +0,0 @@
'use strict'
/** @type {import('@adonisjs/framework/src/Env')} */
const Env = use('Env')
/** @type {import('@adonisjs/ignitor/src/Helpers')} */
const Helpers = use('Helpers')
module.exports = {
/*
|--------------------------------------------------------------------------
| Default Connection
|--------------------------------------------------------------------------
|
| Connection defines the default connection settings to be used while
| interacting with SQL databases.
|
*/
connection: Env.get('DB_CONNECTION', 'pg'),
/*
|--------------------------------------------------------------------------
| PostgreSQL
|--------------------------------------------------------------------------
|
| Here we define connection settings for PostgreSQL database.
|
| npm i --save pg
|
*/
pg: {
client: 'pg',
connection: {
host: Env.get('DB_HOST', 'localhost'),
port: Env.get('DB_PORT', ''),
user: Env.get('DB_USER', 'root'),
password: Env.get('DB_PASSWORD', ''),
database: Env.get('DB_DATABASE', 'adonis')
},
pool: { min: 0, max: 10 },
debug: Env.get('DB_DEBUG', false)
}
}

View file

@ -1,49 +0,0 @@
'use strict'
/** @type {import('@adonisjs/framework/src/Env')} */
const Env = use('Env')
module.exports = {
/*
|--------------------------------------------------------------------------
| Driver
|--------------------------------------------------------------------------
|
| Driver to be used for hashing values. The same driver is used by the
| auth module too.
|
*/
driver: Env.get('HASH_DRIVER', 'bcrypt'),
/*
|--------------------------------------------------------------------------
| Bcrypt
|--------------------------------------------------------------------------
|
| Config related to bcrypt hashing. https://www.npmjs.com/package/bcrypt
| package is used internally.
|
*/
bcrypt: {
rounds: 10
},
/*
|--------------------------------------------------------------------------
| Argon
|--------------------------------------------------------------------------
|
| Config related to argon. https://www.npmjs.com/package/argon2 package is
| used internally.
|
| Since argon is optional, you will have to install the dependency yourself
|
|============================================================================
| npm i argon2
|============================================================================
|
*/
argon: {
type: 1
}
}

View file

@ -1,19 +0,0 @@
module.exports = {
apps : [{
name: process.env.APP_NAME,
script: 'server.js',
// Options reference: https://pm2.keymetrics.io/docs/usage/application-declaration/
instances: 1,
autorestart: true,
watch: true,
ignore_watch:['node_modules','public','resources/js','resources/scss']
},
{
name: process.env.APP_NAME + '_react',
script: 'node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js "--watch"',
// Options reference: https://pm2.keymetrics.io/docs/usage/application-declaration/
instances: 1,
autorestart: false,
watch: false,
}]
};

View file

@ -1,19 +0,0 @@
module.exports = {
apps : [{
name: process.env.APP_NAME,
script: 'server.js',
// Options reference: https://pm2.keymetrics.io/docs/usage/application-declaration/
instances: 1,
autorestart: true,
watch: true,
ignore_watch:['node_modules','public','resources/js','resources/scss']
},
{
name: process.env.APP_NAME + '_react_hot',
script: 'node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js',
// Options reference: https://pm2.keymetrics.io/docs/usage/application-declaration/
instances: 1,
autorestart: false,
watch: false,
}]
};

View file

@ -1,10 +0,0 @@
module.exports = {
apps: [
{
name: process.env.APP_NAME,
script: 'server.js',
autorestart: true,
instances: 1
}
]
};

File diff suppressed because it is too large Load diff

View file

@ -1,80 +0,0 @@
{
"name": "adonis-inertia-react-starter",
"version": "4.1.0",
"adonis-version": "4.1.0",
"description": "Adonis, Inertia, React, Emotion, TailwindCSS, Webpack Mix, PostCSS",
"main": "index.js",
"scripts": {
"start": "node server.js",
"test": "node ace test",
"webpack-server": "/opt/node_modules/webpack/bin/webpack.js --progress --hide-modules --config=/opt/node_modules/laravel-mix/setup/webpack.config.js",
"watch": "npm run webpack-server -- --watch"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"keywords": [
"adonisjs",
"adonis-app",
"inertajs",
"react"
],
"author": "",
"license": "UNLICENSED",
"private": true,
"dependencies": {
"@adonisjs/ace": "^5.0.8",
"@adonisjs/bodyparser": "^2.0.9",
"@adonisjs/cors": "^1.0.7",
"@adonisjs/fold": "^4.0.9",
"@adonisjs/framework": "^5.0.9",
"@adonisjs/ignitor": "^2.0.8",
"@adonisjs/lucid": "^6.1.3",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@inertiajs/inertia": "^0.1.7",
"@inertiajs/inertia-react": "^0.1.4",
"@tailwindcss/ui": "^0.1.3",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"inertia-adonis": "^0.1.2",
"node-cookie": "^2.1.2",
"pg": "^7.18.2",
"react": "^16.13.0",
"react-dom": "^16.13.0",
"react-scripts": "3.4.0",
"recharts": "^1.8.5",
"tailwindcss": "^1.2.0"
},
"devDependencies": {
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-transform-react-jsx": "^7.9.1",
"@babel/preset-react": "^7.9.1",
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
"autoprefixer": "^9.7.4",
"babel-plugin-macros": "^2.8.0",
"laravel-mix": "^5.0.4",
"laravel-mix-purgecss": "^5.0.0-rc.1",
"laravel-mix-tailwind": "^0.1.0",
"postcss-cli": "^7.1.0",
"postcss-loader": "^3.0.0",
"sass": "^1.26.3",
"twin.macro": "^1.0.0-alpha.7"
},
"autoload": {
"App": "./app"
}
}

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="36px" height="33px" viewBox="0 0 36 33" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(0 .5)" fill="none" fill-rule="evenodd"><path d="M20 2.236L5.618 31h28.764L20 2.236z" stroke="#FFFFFF" stroke-width="2"/><path fill="#FFFFFF" d="M12 2l12 24H0"/></g></svg>

Before

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="186px" height="31px" viewBox="0 0 186 31" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M20.25 6.106V.1H.72v6.006h5.712v17.388H.72V29.5h19.53v-6.006h-5.712V6.106h5.712zm18.774 7.35v-5.46h-6.846V1.528h-7.182v3.108c0 2.226-1.218 3.36-3.444 3.36h-.084v5.46h3.528v8.904c0 4.578 3.528 7.686 8.82 7.686 1.932 0 3.276-.126 5.208-1.05v-5.964c-2.016.882-2.982.966-3.822.966-1.806 0-3.024-1.008-3.024-2.898v-7.644h6.846zm37.17-5.46l-3.612 13.692L68.76 7.996H63.3l-3.822 13.692-3.654-13.692h-7.308L55.068 29.5h7.266l3.696-12.516L69.684 29.5h7.308l6.552-21.504h-7.35zM95.43 7.45c6.846 0 11.424 4.746 11.424 11.256 0 6.552-4.578 11.34-11.424 11.34-6.888 0-11.466-4.788-11.466-11.34 0-6.51 4.578-11.256 11.466-11.256zm0 5.628c-2.898 0-4.62 2.352-4.62 5.628 0 3.318 1.722 5.712 4.62 5.712 2.856 0 4.578-2.394 4.578-5.712 0-3.276-1.722-5.628-4.578-5.628zm22.092.714V7.996h-7.182V29.5h7.182v-7.518c0-5.376 1.89-7.728 8.946-6.972V7.534c-4.788 0-7.686 1.806-8.946 6.258zM145.158 29.5h8.4l-7.812-11.886 7.14-9.618h-8.316l-4.284 6.552h-3.612V.1h-7.182v29.4h7.182v-8.442h3.612l4.872 8.442zm22.092-14.196h6.384c-.462-5.46-4.326-7.854-9.87-7.854-6.09 0-9.534 2.436-9.534 6.804 0 4.998 4.494 5.586 8.19 6.426 3.822.882 4.704 1.134 4.704 2.478 0 1.512-1.428 1.932-2.982 1.932-2.394 0-3.822-.966-4.074-3.486h-6.384c.336 5.88 4.536 8.442 10.542 8.442 6.132 0 9.66-2.688 9.66-7.14 0-4.998-4.41-5.628-8.736-6.594-3.234-.672-4.326-.882-4.326-2.31 0-1.134 1.176-1.848 2.856-1.848 2.268 0 3.276.882 3.57 3.15zm11.424 4.536h6.258L185.94.1h-8.316l1.05 19.74zm-.63 9.66h7.518v-6.51h-7.518v6.51z" fill="#FFFFFF" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,16 +0,0 @@
import React from 'react'
const FrameHeading = (props) => {
return (
<header className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto py-4 px-4 sm:px-6 lg:px-8">
<h1 className="text-lg leading-6 font-semibold text-gray-900">
{props.heading}
</h1>
{ props.gametype ? props.gametype : ''}
</div>
</header>
);
};
export default FrameHeading;

View file

@ -1 +0,0 @@
export { default } from './FrameHeading';

View file

@ -1,71 +0,0 @@
import React from 'react'
const returnTotalSumGames = (player) => {
let sum = Number(player.ctf) +
Number(player.dm) +
Number(player.lak) +
Number(player.spawnctf);
return sum
};
const GameTypesPlayedBoxes = (props) => {
return (
<>
<h3 className="text-md leading-6 font-medium text-gray-900">
Games Played {returnTotalSumGames(props)}
</h3>
<div className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-4">
<div className="bg-white overflow-hidden shadow rounded-lg">
<div className="px-4 py-5 sm:p-6">
<dl>
<dt className="text-sm leading-5 font-medium text-gray-500 truncate">
Capture the Flag
</dt>
<dd className="mt-1 text-3xl leading-9 font-semibold text-gray-900">
{props.ctf}
</dd>
</dl>
</div>
</div>
<div className="bg-white overflow-hidden shadow rounded-lg">
<div className="px-4 py-5 sm:p-6">
<dl>
<dt className="text-sm leading-5 font-medium text-gray-500 truncate">
LAK Rabbit
</dt>
<dd className="mt-1 text-3xl leading-9 font-semibold text-gray-900">
{props.lak}
</dd>
</dl>
</div>
</div>
<div className="bg-white overflow-hidden shadow rounded-lg">
<div className="px-4 py-5 sm:p-6">
<dl>
<dt className="text-sm leading-5 font-medium text-gray-500 truncate">
Spawn CTF
</dt>
<dd className="mt-1 text-3xl leading-9 font-semibold text-gray-900">
{props.spawnctf}
</dd>
</dl>
</div>
</div>
<div className="bg-white overflow-hidden shadow rounded-lg">
<div className="px-4 py-5 sm:p-6">
<dl>
<dt className="text-sm leading-5 font-medium text-gray-500 truncate">
Deathmatch
</dt>
<dd className="mt-1 text-3xl leading-9 font-semibold text-gray-900">
{props.dm}
</dd>
</dl>
</div>
</div>
</div>
</>
);
};
export default GameTypesPlayedBoxes;

View file

@ -1 +0,0 @@
export { default } from './GameTypesPlayedBoxes';

View file

@ -1,58 +0,0 @@
import React from 'react'
const returnTotalSumGames = (player) => {
let sum = Number(player.ctf) +
Number(player.dm) +
Number(player.lak) +
Number(player.spawnctf);
return sum
};
const GameTypesPlayedCols = (props) => {
return (
<>
<dl className="grid grid-cols-1 col-gap-4 row-gap-8 sm:grid-cols-4">
<div className="sm:col-span-1">
<dt className="text-sm leading-5 font-medium text-gray-500">
CTF
</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900">
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-gray-100 text-gray-800">{props.ctf} </span>
</dd>
</div><div className="sm:col-span-1">
<dt className="text-sm leading-5 font-medium text-gray-500">
LAK Rabbit
</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900">
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-gray-100 text-gray-800">{props.lak}</span>
</dd>
</div><div className="sm:col-span-1">
<dt className="text-sm leading-5 font-medium text-gray-500">
Spawn CTF
</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900">
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-gray-100 text-gray-800">{props.spawnctf}</span>
</dd>
</div><div className="sm:col-span-1">
<dt className="text-sm leading-5 font-medium text-gray-500">
Deathmatch
</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900">
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-gray-100 text-gray-800">{props.dm}</span>
</dd>
</div>
</dl>
<div className="mt-5">
<dt className="text-sm leading-5 font-medium text-gray-500">
Total
</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900">
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-gray-100 text-gray-800">{returnTotalSumGames(props)}</span>
</dd>
</div>
</>
);
};
export default GameTypesPlayedCols;

View file

@ -1 +0,0 @@
export { default } from './GameTypesPlayedCols';

View file

@ -1,40 +0,0 @@
import React from 'react'
import { InertiaLink } from '@inertiajs/inertia-react'
const navItems = () =>
<div className="ml-10 flex items-baseline">
{/* <InertiaLink href="/" className="px-3 py-2 rounded-md text-sm font-medium text-white bg-gray-900 focus:outline-none focus:text-white focus:bg-gray-700">Home</InertiaLink> */}
<InertiaLink href="/games" className="ml-4 px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700">Games</InertiaLink>
<InertiaLink href="/players" className="ml-4 px-3 py-2 rounded-md text-sm font-medium text-gray-300 hover:text-white hover:bg-gray-700 focus:outline-none focus:text-white focus:bg-gray-700">Players</InertiaLink>
</div>;
const TopNav = () => {
return (
<nav className="bg-gray-800">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="flex items-center">
<div className="flex-shrink-0">
<img className="h-8 w-8" src="https://d33wubrfki0l68.cloudfront.net/1699fc97aa9b1cb851a6c0039162a9241724e1fb/7289f/images/logo.png" alt="Tribes 2 Stats" />
</div>
<div className="md:block">
{ navItems() }
</div>
</div>
<div className="md:block">
<div className="ml-4 flex items-center md:ml-6">
<span className="inline-flex rounded-md shadow-sm">
<a href="https://www.playt2.com/discord" className="inline-flex items-center px-2.5 py-1.5 border border-gray-300 text-xs leading-4 font-medium rounded text-gray-700 bg-white hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:text-gray-800 active:bg-gray-50 transition ease-in-out duration-150">
Join Discord
</a>
</span>
</div>
</div>
</div>
</div>
</nav>
);
};
export default TopNav;

View file

@ -1 +0,0 @@
export { default } from './TopNav';

View file

@ -1,171 +0,0 @@
import React, { PureComponent } from 'react';
import { InertiaLink } from '@inertiajs/inertia-react';
import Layout from '@/Shared/Layout';
import { PieChart, Pie, Sector } from 'recharts';
const renderActiveShape = (props) => {
const RADIAN = Math.PI / 180;
const { cx, cy, midAngle, innerRadius, outerRadius, startAngle, endAngle, fill, payload, percent, value } = props;
const sin = Math.sin(-RADIAN * midAngle);
const cos = Math.cos(-RADIAN * midAngle);
const sx = cx + (outerRadius + 10) * cos;
const sy = cy + (outerRadius + 10) * sin;
const mx = cx + (outerRadius + 30) * cos;
const my = cy + (outerRadius + 30) * sin;
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
const ey = my;
const textAnchor = cos >= 0 ? 'start' : 'end';
return (
<g>
<text x={cx} y={cy} dy={8} textAnchor="middle" fill="#5850ec">
{payload.name}
</text>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
fill="#8884d8"
/>
<Sector
cx={cx}
cy={cy}
startAngle={startAngle}
endAngle={endAngle}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill="#6761d6"
/>
<path d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`} stroke={fill} fill="none" />
<circle cx={ex} cy={ey} r={2} fill={fill} stroke="none" />
<text x={ex + (cos >= 0 ? 1 : -1) * 12} y={ey} textAnchor={textAnchor} fill="#333">{`${value}`}</text>
<text x={ex + (cos >= 0 ? 1 : -1) * 12} y={ey} dy={18} textAnchor={textAnchor} fill="#999">
{`(${(percent * 100).toFixed(2)}%)`}
</text>
</g>
);
};
export class TwoLevelPieChart extends PureComponent {
state = {
activeIndex: this.props.data.oScore >= this.props.data.dScore ? 0 : 1,
data: [ { name: 'Offense', value: this.props.data.oScore }, { name: 'Defense', value: this.props.data.dScore } ]
};
onPieEnter = (data, index) => {
this.setState({
activeIndex: index
});
};
render() {
return (
<PieChart width={400} height={400}>
<Pie
activeIndex={this.state.activeIndex}
activeShape={renderActiveShape}
data={this.state.data}
cx={200}
cy={200}
innerRadius={60}
outerRadius={80}
fill="#ccc"
dataKey="value"
onMouseEnter={this.onPieEnter}
className="text-xs"
/>
</PieChart>
);
}
}
const PlayerRow = (player, index) => {
// dont show scoreless players
// if (Number(player.stats.scoreTG) <= 0) {
// return;
// }
return (
<div className="flex flex-col rounded-lg shadow-lg overflow-hidden" key={index}>
<div className="bg-white shadow overflow-hidden sm:rounded-lg">
<div className="px-4 py-5 border-b border-gray-200 sm:px-6">
<h3 className="text-lg leading-6 font-medium">
<InertiaLink
href={`/player/${player.player_guid}`}
className="text-indigo-600 hover:text-indigo-500 transition duration-150 ease-in-out"
>
{player.player_name}
</InertiaLink>
</h3>
</div>
<div>
<dl>
<div className="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm leading-5 font-medium text-gray-500">Total Score</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{player.stats.scoreTG}
</dd>
</div>
<div className="bg-gray-50 flex items-center justify-center">
{player.gametype == 'CTFGame' || player.gametype == 'SCtFGame' ? (
<TwoLevelPieChart
data={{
oScore: Number(player.stats.offenseScoreTG[0]),
dScore: Number(player.stats.defenseScoreTG[0])
}}
/>
) : (
''
)}
</div>
<div className="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm leading-5 font-medium text-gray-500">Kills / Assists</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{player.stats.killsTG} / {player.stats.assistTG}
</dd>
</div>
<div className="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm leading-5 font-medium text-gray-500">MAs</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{player.stats.totalMATG}
</dd>
</div>
<div className="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm leading-5 font-medium text-gray-500">Flag Grabs / Caps</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{player.stats.flagGrabsTG} / {player.stats.flagCapsTG}
</dd>
</div>
<div className="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm leading-5 font-medium text-gray-500">
Flag Defends / Capper Kills / Returns
</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{player.stats.flagDefendsTG} / {player.stats.carrierKillsTG} /{' '}
{player.stats.flagReturnsTG}
</dd>
</div>
</dl>
</div>
</div>
</div>
);
};
export default function Game(props) {
return (
<Layout title={props.pageTitle.name} gametype={props.pageTitle.gametype}>
<div className="mt-2 grid gap-5 max-w-lg mx-auto lg:grid-cols-3 lg:max-w-none">
{props.gameInfo.map((game, index) => PlayerRow(game, index))}
</div>
{/*
<div className="bg-white shadow overflow-hidden sm:rounded-md">
<div className="py-10 px-10"><code> {JSON.stringify(props.gameInfo)}</code></div>
</div> */}
</Layout>
);
}

View file

@ -1,53 +0,0 @@
import React from 'react'
import { InertiaLink } from '@inertiajs/inertia-react'
import Layout from '@/Shared/Layout'
const GameRow = (game, index) => {
return <li key={index}>
<InertiaLink href={`/game/${game.game_id}`} className="block hover:bg-gray-50 focus:outline-none focus:bg-gray-50 transition duration-150 ease-in-out">
<div className="flex items-center px-4 py-4 sm:px-6">
<div className="min-w-0 flex-1 flex items-center">
<div className="min-w-0 flex-1 md:grid md:grid-cols-2 md:gap-4">
<div>
<div className="text-sm leading-5 font-medium text-indigo-600 truncate">{game.map}</div>
<div className="mt-2 flex items-center text-sm leading-5 text-gray-500">
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-gray-100 text-gray-800">Played: {game.datestamp.split(/[T]/)[0]}</span>
</div>
</div>
<div className="hidden md:block">
<div>
<div className="text-sm leading-5 text-gray-900">
</div>
<div className="mt-2 flex items-center">
<span className="inline-flex items-center px-3 py-0.5 rounded-full text-xs font-medium leading-5 bg-indigo-100 text-indigo-800">{game.gametype}</span>
</div>
</div>
</div>
</div>
</div>
<div>
<svg className="h-5 w-5 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd"/>
</svg>
</div>
</div>
</InertiaLink>
</li>;
}
export default function Games(props) {
return (
<Layout title={props.pageTitle}>
<div className="bg-white shadow overflow-hidden sm:rounded-md">
<ul>
{ props.games.map((game, index) => GameRow(game, index)) }
</ul>
</div>
</Layout>
)
}

View file

@ -1,13 +0,0 @@
import React from 'react'
import Layout from '@/Shared/Layout'
export default function Index(props) {
return (
<Layout title={props.pageTitle}>
<div className="border-4 border-dashed border-gray-300 rounded-lg h-96">
</div>
</Layout>
)
}

View file

@ -1,65 +0,0 @@
import React from 'react'
import { InertiaLink } from '@inertiajs/inertia-react'
import Layout from '@/Shared/Layout'
const returnTotalSumGames = (player) => {
let sum = Number(player.total_games_ctfgame) +
Number(player.total_games_dmgame) +
Number(player.total_games_lakrabbitgame) +
Number(player.total_games_sctfgame);
return sum
};
const PlayerRow = (player, index) => {
let totalGames = returnTotalSumGames(player);
// only display players that have games associated
if (totalGames === 0){return}
return <li key={index}>
<InertiaLink href={`/player/${player.player_guid}`} className="block hover:bg-gray-50 focus:outline-none focus:bg-gray-50 transition duration-150 ease-in-out">
<div className="flex items-center px-4 py-4 sm:px-6">
<div className="min-w-0 flex-1 flex items-center">
<div className="min-w-0 flex-1 md:grid md:grid-cols-2 md:gap-4">
<div>
<div className="text-sm leading-5 font-medium text-indigo-600 truncate">{player.player_name}</div>
<div className="mt-2 flex items-center text-sm leading-5 text-gray-500">
<span className="truncate">Last Active: {player.updated_at.split(/[T]/)[0]}</span>
</div>
</div>
<div className="hidden md:block">
<div>
<div className="text-sm leading-5 text-gray-900">
Total Games Played
</div>
<div className="mt-2 flex items-center">
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-gray-100 text-gray-800">{ totalGames }</span>
</div>
</div>
</div>
</div>
</div>
<div>
<svg className="h-5 w-5 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd"/>
</svg>
</div>
</div>
</InertiaLink>
</li>;
}
export default function Players(props) {
return (
<Layout title={props.pageTitle}>
<div className="bg-white shadow overflow-hidden sm:rounded-md">
<ul>
{ props.players.map((player, index) => PlayerRow(player, index)) }
</ul>
</div>
</Layout>
)
}

View file

@ -1,170 +0,0 @@
import React from 'react';
import { InertiaLink } from '@inertiajs/inertia-react';
import Layout from '@/Shared/Layout';
import GameTypesPlayedCols from '@/Components/GameTypesPlayedCols';
import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis } from 'recharts';
const returnWeaponTotals = (statTotals) => {
let totals = [
{ weapon: 'Chaingun', val: statTotals['cgKillsTG'] },
{ weapon: 'Disc', val: statTotals['discKillsTG'] },
{ weapon: 'Grenade Launcher', val: statTotals['grenadeKillsTG'] },
{ weapon: 'Shocklance', val: statTotals['shockKillsTG'] },
{ weapon: 'Laser Rifle', val: statTotals['laserKillsTG'] },
{ weapon: 'Blaster', val: statTotals['blasterKillsTG'] },
{ weapon: 'Plasma Rifle', val: statTotals['plasmaKillsTG'] },
{ weapon: 'Mortar Launcher', val: statTotals['mortarKillsTG'] },
{ weapon: 'Missile Launcher', val: statTotals['missileKillsTG'] },
{ weapon: 'Hand Grenade', val: statTotals['hGrenadeKillsTG'] },
{ weapon: 'Mine', val: statTotals['mineKillsTG'] },
{ weapon: 'Satchel', val: statTotals['satchelKillsTG'] }
];
// dont return if the val is 0
return totals.filter(function(el) {
return el.val > 0;
});
};
const GameCard = (player, index) => {
// only display card if player has score
// if (Number(player.stats.score) <= 0){return}
return (
<div key={index} className="bg-white shadow overflow-hidden sm:rounded-lg mb-5">
<div className="px-4 py-5 border-b border-gray-200 sm:px-6">
<div className="-ml-4 -mt-4 flex justify-between items-center flex-wrap sm:flex-no-wrap">
<div className="ml-4 mt-4">
<h3 className="text-lg leading-6 font-medium text-gray-900">
<InertiaLink
href={`/game/${player.game_id}`}
className="text-indigo-600 hover:text-indigo-500 transition duration-150 ease-in-out"
>
{player.stats.map}
</InertiaLink>
</h3>
<p className="mt-1 max-w-2xl text-sm leading-5 text-gray-500">
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium leading-4 bg-gray-100 text-gray-800">
{player.stats.dateStamp}
</span>
</p>
</div>
<div className="ml-4 mt-4 flex-shrink-0">
<span className="inline-flex items-center px-3 py-0.5 rounded-full text-xs font-medium leading-5 bg-indigo-100 text-indigo-800">
{player.gametype}
</span>
</div>
</div>
</div>
<div className="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm leading-5 font-medium text-gray-500">Total Score</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">{player.stats.scoreTG}</dd>
</div>
<div className="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm leading-5 font-medium text-gray-500">Kills / Assists</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{player.stats.killsTG} / {player.stats.assistTG}
</dd>
</div>
<div className="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm leading-5 font-medium text-gray-500">MAs</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">{player.stats.totalMATG}</dd>
</div>
<div className="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm leading-5 font-medium text-gray-500">Flag Grabs / Caps</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{player.stats.flagGrabsTG} / {player.stats.flagCapsTG}
</dd>
</div>
<div className="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<dt className="text-sm leading-5 font-medium text-gray-500">Flag Defends / Carrier Kills / Returns</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
{player.stats.flagDefendsTG} / {player.stats.carrierKillsTG} / {player.stats.flagReturnsTG}
</dd>
</div>
</div>
);
};
export default function Player(props) {
return (
<Layout title={props.pageTitle}>
<div className="md:grid md:grid-cols-4 md:gap-6">
<div className="md:col-span-1">
<div className="px-4 sm:px-0">
<h3 className="text-lg font-medium leading-6 text-gray-900">Aggregate</h3>
<p className="mt-1 text-sm leading-5 text-gray-500">Stat Totals</p>
</div>
</div>
<div className="mt-5 md:mt-0 md:col-span-3">
<div className="bg-white shadow overflow-hidden sm:rounded-lg">
<div className="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
<div className="text-sm leading-5 font-medium text-gray-500">Games Played</div>
<div className="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
<GameTypesPlayedCols
ctf={props.playerData.player.total_games_ctfgame}
dm={props.playerData.player.total_games_dmgame}
lak={props.playerData.player.total_games_lakrabbitgame}
spawnctf={props.playerData.player.total_games_sctfgame}
/>
</div>
</div>
<div className="bg-gray-50 px-4 py-5">
<dt className="text-sm leading-5 font-medium text-gray-500">Weapon Usage</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900 flex items-center justify-center">
<RadarChart
cx={300}
cy={250}
outerRadius={150}
width={600}
height={500}
data={
returnWeaponTotals(props.playerData.totals).length ? (
returnWeaponTotals(props.playerData.totals)
) : (
[ { weapon: 'No Data', val: 1 } ]
)
}
className="text-xs"
>
<PolarGrid />
<PolarAngleAxis dataKey="weapon" />
<PolarRadiusAxis />
<Radar
name={props.playerData.player.player_name}
dataKey="val"
stroke="#8884d8"
fill="#8884d8"
fillOpacity={0.6}
/>
</RadarChart>
</dd>
</div>
</div>
</div>
</div>
<div className="md:grid md:grid-cols-4 md:gap-6 mt-10">
<div className="md:col-span-1">
<div className="px-4 sm:px-0">
<h3 className="text-lg font-medium leading-6 text-gray-900">Game History</h3>
<p className="mt-1 text-sm leading-5 text-gray-500">
Stats for past {props.playerData.stats.length} games
</p>
</div>
</div>
<div className="mt-5 md:mt-0 md:col-span-3">
{props.playerData.stats.map((player, index) => GameCard(player, index))}
</div>
</div>
{/*
<div className="bg-white shadow overflow-hidden sm:rounded-md">
<div className="py-10 px-10"><code> {JSON.stringify(props.playerData.stats)}</code></div>
</div> */}
</Layout>
);
}

View file

@ -1,25 +0,0 @@
import React, { useEffect } from 'react'
import TopNav from '../Components/TopNav';
import FrameHeading from '../Components/FrameHeading'
export default function Layout({ title, gametype, children }) {
useEffect(() => {
document.title = title + ' - Tribes 2 Stats';
}, [title])
return (
<>
<TopNav />
<FrameHeading heading={title} gametype={gametype} />
<main className="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
<div className="px-4 py-4 sm:px-0">
{ children }
</div>
</main>
</>
)
}

View file

@ -1,18 +0,0 @@
import { InertiaApp } from '@inertiajs/inertia-react'
import React from 'react'
import { render } from 'react-dom'
const app = document.getElementById('app')
render(
<InertiaApp
initialPage={JSON.parse(app.dataset.page)}
resolveComponent={name => import(`@/Pages/${name}`).then(module => module.default)}
/>,
app
)
// This is required to enable HMR
if (module.hot) {
module.hot.accept();
}

View file

@ -1,5 +0,0 @@
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

View file

@ -1,60 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Tribes 2 Stats</title>
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
@if(node_env().env === "production")
{{ style(versioncss('main')) }}
@else
{{ style('dist/css/main') }}
@endif
</head>
<body class="antialiased font-sans bg-gray-200">
<div id="popup" class="relative bg-indigo-600">
<div class="max-w-screen-xl mx-auto py-3 px-3 sm:px-6 lg:px-8">
<div class="pr-16 sm:text-center sm:px-16">
<p class="font-medium text-white">
Hey! This is a preview. Things aren't quite finished yet. Stay tuned!
</p>
</div>
<div class="absolute inset-y-0 right-0 pt-1 pr-1 flex items-start sm:pt-1 sm:pr-2 sm:items-start">
<button type="button" class="flex p-2 rounded-md hover:bg-indigo-500 focus:outline-none focus:bg-indigo-500 transition ease-in-out duration-150" onclick="closePopUp()">
<svg class="h-6 w-6 text-white" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</div>
{{{ startTag }}}
{{-- {{ edgeVar }} --}}
@if(node_env().env === "production" || node_env().hmr === "false")
{{ script(versionjs('app')) }}
@else
{{ script('http://localhost:8081/dist/js/app.js') }}
@endif
<script>
function closePopUp(){
document.getElementById("popup").remove();
};
if(top != self){
setInterval(() => {
parent.postMessage(document.getElementById('app').clientHeight, '*');
}, 1000);
}
</script>
</body>
</html>

View file

@ -1,25 +0,0 @@
'use strict'
/*
|--------------------------------------------------------------------------
| Http server
|--------------------------------------------------------------------------
|
| This file bootstrap Adonisjs to start the HTTP server. You are free to
| customize the process of booting the http server.
|
| """ Loading ace commands """
| At times you may want to load ace commands when starting the HTTP server.
| Same can be done by chaining `loadCommands()` method after
|
| """ Preloading files """
| Also you can preload files by calling `preLoad('path/to/file')` method.
| Make sure to pass relative path from the project root.
*/
const { Ignitor } = require('@adonisjs/ignitor')
new Ignitor(require('@adonisjs/fold'))
.appRoot(__dirname)
.fireHttpServer()
.catch(console.error)

View file

@ -1,57 +0,0 @@
'use strict'
/*
|--------------------------------------------------------------------------
| Providers
|--------------------------------------------------------------------------
|
| Providers are building blocks for your Adonis app. Anytime you install
| a new Adonis specific package, chances are you will register the
| provider here.
|
*/
const providers = [
'@adonisjs/framework/providers/AppProvider',
'@adonisjs/framework/providers/ViewProvider',
'@adonisjs/lucid/providers/LucidProvider',
'@adonisjs/cors/providers/CorsProvider',
'inertia-adonis/providers/InertiaProvider'
]
/*
|--------------------------------------------------------------------------
| Ace Providers
|--------------------------------------------------------------------------
|
| Ace providers are required only when running ace commands. For example
| Providers for migrations, tests etc.
|
*/
const aceProviders = [
]
/*
|--------------------------------------------------------------------------
| Aliases
|--------------------------------------------------------------------------
|
| Aliases are short unique names for IoC container bindings. You are free
| to create your own aliases.
|
| For example:
| { Route: 'Adonis/Src/Route' }
|
*/
const aliases = {}
/*
|--------------------------------------------------------------------------
| Commands
|--------------------------------------------------------------------------
|
| Here you store ace commands for your package
|
*/
const commands = []
module.exports = { providers, aceProviders, aliases, commands }

View file

@ -1,37 +0,0 @@
const { hooks } = require('@adonisjs/ignitor')
const { version } = require('../package.json')
const Helpers = use('Helpers')
const mixManifest = require(Helpers.publicPath('mix-manifest.json'))
hooks.after.providersBooted(async () => {
const View = use('View')
View.global('node_env', () => {
return { "env": process.env.NODE_ENV, "hmr": process.env.WEBPACK_HMR }
})
View.global('versionjs', (filename) => {
filename = `/dist/js/${filename}.js`
if (!mixManifest.hasOwnProperty(filename)) {
throw new Error('Could not find asset for versioning' + filename)
}
return mixManifest[filename]
})
View.global('versioncss', (filename) => {
filename = `/dist/css/${filename}.css`
if (!mixManifest.hasOwnProperty(filename)) {
throw new Error('Could not find asset for versioning' + filename)
}
return mixManifest[filename]
})
const Inertia = use('Adonis/Addons/Inertia')
// these vars get pushed to the inertia frontend
Inertia.setVersion(version)
Inertia.share('app.name', process.env.APP_NAME)
})

View file

@ -1,44 +0,0 @@
'use strict'
/** @type {import('@adonisjs/framework/src/Server')} */
const Server = use('Server')
/*
|--------------------------------------------------------------------------
| Global Middleware
|--------------------------------------------------------------------------
|
| Global middleware are executed on each http request only when the routes
| match.
|
*/
const globalMiddleware = [
'Adonis/Middleware/Inertia'
]
/*
|--------------------------------------------------------------------------
| Named Middleware
|--------------------------------------------------------------------------
|
| Named middleware is key/value object to conditionally add middleware on
| specific routes or group of routes.
|
| // define
| {
| auth: 'Adonis/Middleware/Auth'
| }
|
| // use
| Route.get().middleware('auth')
|
*/
const namedMiddleware = {}
Server
.registerGlobal(globalMiddleware)
.registerNamed(namedMiddleware)
.use([
'Adonis/Middleware/Static',
'Adonis/Middleware/Cors'
])

View file

@ -1,37 +0,0 @@
'use strict'
/*
|--------------------------------------------------------------------------
| Routes
|--------------------------------------------------------------------------
|
| Http routes are entry points to your web application. You can create
| routes for different URLs and bind Controller actions to them.
|
| A complete guide on routing is available here.
| http://adonisjs.com/docs/4.0/routing
|
*/
/** @type {typeof import('@adonisjs/framework/src/Route/Manager')} */
const Route = use('Route')
// [ Routes ]
//Route.get('/', 'IndexController.index').as('home')
//temp set games as home
Route.get('/', 'GameController.index').as('home')
// [ player ]
Route.get('/players', 'PlayerController.index')
Route.get('/player/:player_guid', 'PlayerController.player')
// [ game ]
Route.get('/games', 'GameController.index')
Route.get('/game/:game_id', 'GameController.game')
// Container Healthcheck route
Route.get('/healthz', () => {
return { status: "healthy" };
})

View file

@ -1,15 +0,0 @@
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
},
},
},
variants: {},
plugins: [
require('@tailwindcss/ui'),
]
}

View file

@ -1,134 +0,0 @@
const mix = require('laravel-mix');
//require('laravel-mix-tailwind');
require('laravel-mix-purgecss');
const tailwindcss = require('tailwindcss');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
// transpiling, babelling, minifying and creating the public/js/main.js out of our assets
mix.setPublicPath('public')
.react('resources/js/app.js', 'public/dist/js')
// .sass('resources/scss/main.scss', 'public/dist/css')
.postCss('resources/scss/main.css', 'public/dist/css')
// .tailwind('./tailwind.config.js');
mix.webpackConfig({
output: {
chunkFilename: './dist/js/[name].[contenthash].js'
},
module: {
rules: [
{
test: /\.(css)/,
use: [
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
tailwindcss('./tailwind.config.js'),
require('autoprefixer'),
],
},
},
],
}
],
},
resolve: {
alias: {
"@": path.resolve(
__dirname,
"resources/js"
),
"@sass": path.resolve(
__dirname,
"resources/scss"
)
}
},
devServer: {
proxy: {
host: '0.0.0.0', // host machine ip
port: 8080,
}
}
});
mix.options({
hmrOptions: {
host: '0.0.0.0', // site's host name
port: 8081
}
});
if (mix.inProduction()) {
mix.purgeCss({
content: [
'./resources/**/*.edge',
'./resources/**/*.js',
'./resources/**/*.jsx',
'./resources/**/*.ts',
'./resources/**/*.tsx'
],
// default in mix
//defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || [],
// default tailwind
defaultExtractor: content => content.match(/[\w-/.:]+(?<!:)/g) || [],
whitelistPatterns: [/-active$/, /-enter$/, /-leave-to$/]
});
mix.version();
}else{
// use SourceMaps on dev
mix.sourceMaps();
}
// Full mix API
// mix.js(src, output);
// mix.react(src, output); <-- Identical to mix.js(), but registers React Babel compilation.
// mix.preact(src, output); <-- Identical to mix.js(), but registers Preact compilation.
// mix.coffee(src, output); <-- Identical to mix.js(), but registers CoffeeScript compilation.
// mix.ts(src, output); <-- TypeScript support. Requires tsconfig.json to exist in the same folder as webpack.mix.js
// mix.extract(vendorLibs);
// mix.sass(src, output);
// mix.standaloneSass('src', output); <-- Faster, but isolated from Webpack.
// mix.fastSass('src', output); <-- Alias for mix.standaloneSass().
// mix.less(src, output);
// mix.stylus(src, output);
// mix.postCss(src, output, [require('postcss-some-plugin')()]);
// mix.browserSync('my-site.test');
// mix.combine(files, destination);
// mix.babel(files, destination); <-- Identical to mix.combine(), but also includes Babel compilation.
// mix.copy(from, to);
// mix.copyDirectory(fromDir, toDir);
// mix.minify(file);
// mix.sourceMaps(); // Enable sourcemaps
// mix.version(); // Enable versioning.
// mix.disableNotifications();
// mix.setPublicPath('path/to/public');
// mix.setResourceRoot('prefix/for/resource/locators');
// mix.autoload({}); <-- Will be passed to Webpack's ProvidePlugin.
// mix.webpackConfig({}); <-- Override webpack.config.js, without editing the file directly.
// mix.babelConfig({}); <-- Merge extra Babel configuration (plugins, etc.) with Mix's default.
// mix.then(function () {}) <-- Will be triggered each time Webpack finishes building.
// mix.extend(name, handler) <-- Extend Mix's API with your own components.
// mix.options({
// extractVueStyles: false, // Extract .vue component styling to file, rather than inline.
// globalVueStyles: file, // Variables file to be imported in every component.
// processCssUrls: true, // Process/optimize relative stylesheet url()'s. Set to false, if you don't want them touched.
// purifyCss: false, // Remove unused CSS selectors.
// uglify: {}, // Uglify-specific options. https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
// postCss: [] // Post-CSS options: https://github.com/postcss/postcss/blob/master/docs/plugins.md
// });

View file

@ -1,3 +0,0 @@
.git
node_modules
public/dist

View file

@ -1,67 +0,0 @@
# [ Stage 1 ] - install node modules
FROM node:12.2-alpine as builder
RUN apk update
WORKDIR /build
COPY ./app/webapp/package.json /build/package.json
COPY ./app/webapp/package-lock.json /build/package-lock.json
RUN npm install
RUN npm i --global @adonisjs/cli
RUN npx adonis key:generate --echo
# ============================
# ============================
# [ Stage 2 ]
FROM node:12.2-alpine
LABEL maintainer="Anthony Mineo <anthonymineo@gmail.com>"
RUN apk update && apk add --no-cache bash curl
# Default envs as prod
ENV ENV_SILENT=true \
HOST=0.0.0.0 \
PORT=8080 \
HASH_DRIVER=bcrypt \
NODE_ENV=production \
WEBPACK_HMR=false \
CACHE_VIEWS=true \
APP_NAME=AdonisJs
ENV APP_URL=http://${HOST}:${PORT}
#ENV APP_KEY=you-need-to-generate-this
# secrets path _FILE prefix or ENV K=V
# Setup pm2 as our node process manager
# https://pm2.keymetrics.io/docs/usage/docker-pm2-nodejs/
RUN npm install pm2 -g
# Set node modules outside our app to keep it clean
COPY --from=builder /build/node_modules/ /opt/node_modules/
ENV PATH /opt/node_modules/.bin:$PATH
# Start script and config
COPY ./build/webapp/entrypoint.sh /entrypoint.sh
# Our App
WORKDIR /app
COPY ./app/webapp /app
RUN ln -nsf /opt/node_modules /app
# Bake prod assets into image
RUN rm -rf /app/public/dist && npm run webpack-server
EXPOSE 8080
HEALTHCHECK --interval=20s --timeout=30s --start-period=5s --retries=5 \
CMD curl -f http://localhost:8080/healthz || exit 1
ENTRYPOINT [ "/entrypoint.sh" ]

View file

@ -1,57 +0,0 @@
#!/bin/bash
echo "Node ENV: $NODE_ENV"
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ "${!var:-}" ]; then
val="${!var}"
elif [ "${!fileVar:-}" ]; then
val="$(< "${!fileVar}")"
fi
export "$var"="$val"
unset "$fileVar"
}
file_env 'APP_KEY'
# Exit if APP_KEY is missing, this key is important!
if [ -z "$APP_KEY" ]
then
echo >&2 "[ERROR] No ENV for APP_KEY or APP_KEY_FILE found!"
echo >&2 "Run the ./generate-adonis-app-key.sh script or provide one at runtime"
exit 1
fi
if [[ "$NODE_ENV" == "development" ]]; then
echo "Prepping container for dev environment"
echo "Setting a symlink for ./node_modules -> /opt/node_modules"
ln -nsf /opt/node_modules /app
echo "Cleaning dist assets..."
rm -rf /app/public/dist
npm run webpack-server
fi
# Generate a dist assets if they don't exist -- (If they were deleted on the volume mount)
[ ! -d "/app/public/dist" ] && npm run webpack-server
if [[ "$NODE_ENV" == "production" ]]; then
pm2-runtime start /app/ecosystem._PROD_.config.js
elif [[ "$WEBPACK_HMR" == "true" ]]; then
pm2-runtime start /app/ecosystem._DEV_HOT_.config.js
else
pm2-runtime start /app/ecosystem._DEV_.config.js
fi

View file

@ -21,19 +21,6 @@ services:
volumes:
- ./app/t2-stat-parser:/app
# web:
# environment:
# DB_USER: "dev"
# DB_PASSWORD: "dev"
# CACHE_VIEWS: "false"
# NODE_ENV: "development" # auto-reloads app on save, dev builds
# WEBPACK_HMR: "true" # default is false -- this is useful for when you're focused on Frontend Dev
# ports:
# - "8080:8080" # adonis http port
# - "8081:8081" # webpack-dev-server port
# volumes:
# - ./app/webapp:/app
api:
environment:
NODE_ENV: "development" # auto-reloads app on save

View file

@ -57,35 +57,6 @@ services:
- internal
- external
# web:
# image: "amineo/t2-stats-web:v0.1.0-rc6"
# build:
# context: .
# dockerfile: ./build/webapp/Dockerfile
# environment:
# NODE_ENV: "production" # set as default in image
# CACHE_VIEWS: "true" # set as default in image
# APP_NAME: "Web" # set as default in image
# # APP_KEY: "You-need-to-generate-this (npx adonis key:generate --echo)"
# APP_KEY_FILE: /run/secrets/adonis.appkey
# DB_HOST: "db"
# DB_PORT: 5432
# DB_USER: ""
# DB_PASSWORD: ""
# DB_DATABASE: t2_stats
# secrets:
# - adonis.appkey
# ports:
# - "8080:8080"
# networks:
# - internal
# - external
# deploy:
# # labels:
# # - traefik.enable=false
# mode: replicated
# replicas: 1
volumes:
psqldata:

View file

@ -1,28 +0,0 @@
#!/bin/bash
echo "Checking to see if @adonisjs/cli has been installed..."
package='@adonisjs/cli'
if [ `npm list -g | grep -c $package` -eq 0 ]; then
while true; do
echo "-----------------------------------------------"
echo " Adonis CLI not installed"
echo "-----------------------------------------------"
read -p "Would you like to install this now? [Y/N] " yn
case $yn in
[Yy]* ) npm i --global @adonisjs/cli; break;;
[Nn]* ) break;;
* ) echo "Please answer yes or no.";;
esac
done
else
echo "Looks good!"
fi
echo "Generating key..."
APPKEY=$(npx adonis key:generate --echo)
echo -n "${APPKEY/APP_KEY=}" > ./docker-secrets/adonis.appkey.v1
echo "Done! Don't share it with anyone!"
echo "The key is located here: ./docker-secrets/adonis.appkey.v1"