init
This commit is contained in:
commit
e1d9657e87
55
.eslintrc
Executable file
55
.eslintrc
Executable file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": false
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"arrow-spacing": "error",
|
||||
"block-scoped-var": "error",
|
||||
"block-spacing": "error",
|
||||
"brace-style": ["error", "stroustrup", {}],
|
||||
"camelcase": "error",
|
||||
"comma-dangle": ["error", "never"],
|
||||
"comma-spacing": ["error", { "before": false, "after": true }],
|
||||
"comma-style": [1, "last"],
|
||||
"consistent-this": [1, "_this"],
|
||||
"curly": [1, "multi"],
|
||||
"eol-last": 1,
|
||||
"eqeqeq": 1,
|
||||
"func-names": 1,
|
||||
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||
"lines-around-comment": ["error", { "beforeBlockComment": true, "allowArrayStart": true }],
|
||||
"max-len": [1, 180, 2, { "ignoreTemplateLiterals": true, "ignoreStrings": true, "ignoreUrls": true, "ignoreTrailingComments": true, "ignoreComments": true }], // 2 spaces per tab, max 80 chars per line
|
||||
"new-cap": 1,
|
||||
"newline-before-return": "error",
|
||||
"no-array-constructor": 1,
|
||||
"no-inner-declarations": [1, "both"],
|
||||
"no-mixed-spaces-and-tabs": 1,
|
||||
"no-multi-spaces": 2,
|
||||
"no-new-object": 1,
|
||||
"no-shadow-restricted-names": 1,
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"padded-blocks": ["error", { "blocks": "never", "switches": "always" }],
|
||||
"prefer-const": "error",
|
||||
"prefer-template": "error",
|
||||
"one-var": 0,
|
||||
"quote-props": ["error", "always"],
|
||||
"quotes": [1, "single"],
|
||||
"radix": 1,
|
||||
"semi": [1, "always"],
|
||||
"space-before-blocks": [1, "always"],
|
||||
"space-infix-ops": 1,
|
||||
"vars-on-top": 1,
|
||||
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1 }],
|
||||
"spaced-comment": ["error", "always", { "markers": ["/"] }]
|
||||
}
|
||||
|
||||
}
|
166
.gitignore
vendored
Executable file
166
.gitignore
vendored
Executable file
@ -0,0 +1,166 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
### macOS template
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
.idea/
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# CMake
|
||||
cmake-build-debug/
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
|
||||
|
||||
# artefacts/screenshots/*.png
|
||||
artefacts/*.txt
|
||||
artefacts/*.json
|
||||
# artefacts/*.html
|
||||
# artefacts/*
|
||||
|
||||
/tests/*.zip
|
||||
|
||||
/output/
|
||||
# /dist/
|
||||
!/tests/data/
|
||||
/tests/sink/
|
||||
/debug/
|
||||
/update.sh
|
||||
/setup/web/
|
||||
/backup/
|
||||
/db/
|
||||
|
||||
/archive.tar.gz
|
||||
/user/
|
||||
/zip
|
||||
!/artefacts/
|
||||
menu.db
|
||||
menu.db.backup
|
2
.prettierignore
Executable file
2
.prettierignore
Executable file
@ -0,0 +1,2 @@
|
||||
node_modules/*
|
||||
public
|
8
.prettierrc
Executable file
8
.prettierrc
Executable file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false
|
||||
}
|
50
README.md
Normal file
50
README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# FSB Backbone
|
||||
|
||||
Based upon the FSB React test. Built against Node v14.15.5.
|
||||
|
||||
When unpacked `cd` into the folder, run the command:
|
||||
|
||||
```shell
|
||||
npm install
|
||||
```
|
||||
|
||||
This will install all dependencies and build the application.
|
||||
|
||||
To run the application, run this following command:
|
||||
|
||||
```shell
|
||||
npm start:server
|
||||
```
|
||||
|
||||
This should start the server running on [http://localhost:3001](http://localhost:3001).
|
||||
|
||||
To rebuild the source code at any time, run the following command:
|
||||
|
||||
```shell
|
||||
npm build
|
||||
```
|
||||
|
||||
Built code is located in the live folder.
|
||||
|
||||
## Notes
|
||||
|
||||
I tried to stick to about 4 hours for this test and sadly did not get any user interaction working. I was concentrating on getting the data onto the page and updating from the Socket.
|
||||
|
||||
An issue was discovered with `server/selectionPriceChange.js`, this was sending `selection.price` as the id, which prevented the socket from updated correctly. Unsure if this was intentional or a 'gotcha'.
|
||||
|
||||
There was a note about making it all run from the same port, I have implemented that here. Building to `/live` and having the server use that as static content.
|
||||
|
||||
I have used Gulp here as I have more experience using Gulp with Bacbone than Grunt.
|
||||
|
||||
Uglify is turned off in the build so that compiled code can be seen.
|
||||
|
||||
|
||||
### Things to update
|
||||
|
||||
- Get the user interaction working.
|
||||
|
||||
- Write tests, I didn't have time to even start any simple tests.
|
||||
|
||||
- Everything else from the original test that I have missed because I don't have the 'test text'.
|
||||
|
||||
|
28
app/app.js
Normal file
28
app/app.js
Normal file
@ -0,0 +1,28 @@
|
||||
const $ = require('jquery');
|
||||
const _ = require('underscore');
|
||||
const Backbone = require('backbone');
|
||||
const io = require('socket.io-client');
|
||||
|
||||
const socket = io('http://localhost:3001');
|
||||
|
||||
const EventItem = require('./components/EventItem');
|
||||
const EventCollection = require('./components/EventCollection');
|
||||
const EventListModel = require('./components/EventListModel');
|
||||
const EventItemView = require('./components/EventItemView');
|
||||
const EventListView = require('./components/EventListView');
|
||||
|
||||
socket.on('selectionPriceUpdate', data => console.log('selectionPriceUpdate', data));
|
||||
socket.on('SelectionStateUpdate', data => console.log('selectionStateUpdate', data));
|
||||
socket.on('eventStateUpdate', data => console.log('eventStateUpdate', data));
|
||||
|
||||
(function() {
|
||||
console.log('Go!');
|
||||
|
||||
const eventCollection = new EventCollection({ 'model':EventItem });
|
||||
const eventList = new EventListModel({ 'eventCollection':eventCollection });
|
||||
const newsListView = new EventListView({ 'model': eventList, 'el':'#eventList' });
|
||||
|
||||
socket.on('selectionPriceUpdate', data => eventList.updatedItem( data));
|
||||
socket.on('SelectionStateUpdate', data => eventList.updatedState( data));
|
||||
socket.on('eventStateUpdate', data => eventList.updatedState( data));
|
||||
})();
|
7
app/components/EventCollection.js
Normal file
7
app/components/EventCollection.js
Normal file
@ -0,0 +1,7 @@
|
||||
const Backbone = require('backbone');
|
||||
|
||||
const EventCollection = Backbone.Collection.extend({
|
||||
|
||||
});
|
||||
|
||||
module.exports = EventCollection;
|
7
app/components/EventItem.js
Normal file
7
app/components/EventItem.js
Normal file
@ -0,0 +1,7 @@
|
||||
const Backbone = require('backbone');
|
||||
|
||||
const EventItem = Backbone.Model.extend({
|
||||
|
||||
});
|
||||
|
||||
module.exports = EventItem;
|
32
app/components/EventItemView.js
Normal file
32
app/components/EventItemView.js
Normal file
@ -0,0 +1,32 @@
|
||||
const _ = require('underscore');
|
||||
const Backbone = require('backbone');
|
||||
|
||||
const templates = require('./templates');
|
||||
|
||||
const EventItemView = Backbone.View.extend({
|
||||
'tagName': 'div',
|
||||
'className': 'itemRow mui-row',
|
||||
'template': _.template('<div>Not set</div>'),
|
||||
'initialize': function() {
|
||||
if (templates.hasOwnProperty(this.model.get('type')))
|
||||
this.template = templates[this.model.get('type')];
|
||||
this.listenTo(this.model, 'change', this.onChange);
|
||||
|
||||
this.render();
|
||||
},
|
||||
'onChange': function() {
|
||||
this.render();
|
||||
},
|
||||
'render': function() {
|
||||
this.$el.html(this.template(this.model.toJSON()));
|
||||
},
|
||||
'updateVisibility': function() {
|
||||
let visibleClass = '';
|
||||
if (active !== null)
|
||||
visibleClass = (active === true) ? 'active' : 'notActive' ;
|
||||
|
||||
this.model.set('visibleClass', visibleClass) ;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = EventItemView;
|
75
app/components/EventListModel.js
Normal file
75
app/components/EventListModel.js
Normal file
@ -0,0 +1,75 @@
|
||||
const Backbone = require('backbone');
|
||||
|
||||
const EventListModel = Backbone.Model.extend({
|
||||
'initialize': function(options) {
|
||||
this.eventCollection = options.eventCollection;
|
||||
this.listenTo(this, 'change', this.onChange);
|
||||
this.listenTo(this.eventCollection, 'change', this.onCollectionChange);
|
||||
this.getEvents();
|
||||
},
|
||||
'onChange': function() {
|
||||
// console.log('EventListModel Changed');
|
||||
},
|
||||
'onCollectionChange': function() {
|
||||
// console.log('eventCollection Changed');
|
||||
},
|
||||
'getEvents': async function () {
|
||||
const data = await fetch('http://localhost:3001/api/selections/')
|
||||
.then((response) => {
|
||||
if (response.ok)
|
||||
return response.json();
|
||||
|
||||
throw response;
|
||||
})
|
||||
.then((data) => {
|
||||
return data.hasOwnProperty('category') ? data.category : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error fetching');
|
||||
});
|
||||
|
||||
if (data && data.length > 0)
|
||||
this.store(data);
|
||||
},
|
||||
'store' : function(data) {
|
||||
const newItems = [];
|
||||
data.forEach((topItem) => {
|
||||
newItems.push({ ...this.getItem(topItem), 'type':'topItem' });
|
||||
|
||||
if (topItem.hasOwnProperty('subcat'))
|
||||
topItem.subcat.forEach((subItem) => {
|
||||
newItems.push({ ...this.getItem(subItem), 'type':'subItem' });
|
||||
|
||||
if (subItem.hasOwnProperty('event'))
|
||||
subItem.event.forEach((eventItem) => {
|
||||
newItems.push({ ...this.getItem(eventItem), 'type':'eventItem' });
|
||||
|
||||
if(eventItem.hasOwnProperty('selection'))
|
||||
eventItem.selection.forEach((selItem) => {
|
||||
newItems.push({ ...this.getItem(selItem), 'pid':eventItem.id, 'type':'selItem' });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.eventCollection.reset(newItems);
|
||||
},
|
||||
'getItem': function({ id, name, active = null, price = null }) {
|
||||
return { id, name, active, price };
|
||||
},
|
||||
'updatedItem': function(newItem) {
|
||||
const foundData = this.eventCollection.findWhere({ 'id':newItem.id });
|
||||
|
||||
if (foundData)
|
||||
foundData.set('price', newItem.newPrice);
|
||||
},
|
||||
'updatedState': function(newItem) {
|
||||
const foundData = this.eventCollection.findWhere({ 'id':newItem.id });
|
||||
|
||||
if (foundData)
|
||||
foundData.set({ 'active': newItem.active, 'visibleClass':(newItem.active === true) ? 'active' : 'notActive' });
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
module.exports = EventListModel;
|
32
app/components/EventListView.js
Normal file
32
app/components/EventListView.js
Normal file
@ -0,0 +1,32 @@
|
||||
const _ = require('underscore');
|
||||
const Backbone = require('backbone');
|
||||
|
||||
const EventItemView = require('./EventItemView');
|
||||
|
||||
const EventListView = Backbone.View.extend({
|
||||
'tagName': 'div',
|
||||
'className' : 'newsItem',
|
||||
'template': _.template('<div><%=name%></div>'),
|
||||
'initialize': function() {
|
||||
console.log('EventListView::Init');
|
||||
this.listenTo(this.model.eventCollection, 'change', this.onChange);
|
||||
this.model.eventCollection.bind('reset', this.render, this);
|
||||
},
|
||||
'render': function() {
|
||||
this.$el.html(this.template(this.model.toJSON()));
|
||||
|
||||
this.model.eventCollection.each(item => {
|
||||
const active = item.get('active');
|
||||
let visibleClass = '';
|
||||
if (active !== null)
|
||||
visibleClass = (active === true) ? 'active' : 'notActive' ;
|
||||
|
||||
item.set('visibleClass', visibleClass) ;
|
||||
|
||||
const eView = new EventItemView({ 'model': item, 'templates': this.templates });
|
||||
this.$el.append(eView.el);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = EventListView;
|
11
app/components/templates.js
Normal file
11
app/components/templates.js
Normal file
@ -0,0 +1,11 @@
|
||||
const _ = require('underscore');
|
||||
|
||||
const templates = {
|
||||
'empty': _.template('<div></div>'),
|
||||
'topItem': _.template('<div class="offset-1-col"><%=id%> <%=name%></div>'),
|
||||
'subItem': _.template('<div class="offset-2-col"><%=id%> <%=name%></div>'),
|
||||
'eventItem': _.template('<div class="offset-3-col <%=visibleClass%>"><%=id%> <%=name%></div>'),
|
||||
'selItem': _.template('<div class="offset-4-col <%=visibleClass%>"><%=id%> <%=name%></div>')
|
||||
};
|
||||
|
||||
module.exports = templates ;
|
473
app/css/index.css
Normal file
473
app/css/index.css
Normal file
@ -0,0 +1,473 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Roboto');
|
||||
|
||||
/* Global Styles */
|
||||
:root {
|
||||
--primary-color: #dc3545;
|
||||
--dark-color: #333333;
|
||||
--light-color: #f4f4f4;
|
||||
--danger-color: #dc3545;
|
||||
--success-color: #28a745;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
.container {
|
||||
max-width: 1100px;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
/* Text Styles*/
|
||||
.x-large {
|
||||
font-size: 4rem;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.large {
|
||||
font-size: 3rem;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.lead {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: var(--dark-color);
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Center All */
|
||||
.all-center {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
padding: 1rem;
|
||||
border: #ccc 1px dotted;
|
||||
margin: 0.7rem 0;
|
||||
}
|
||||
|
||||
/* List */
|
||||
.list {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.list li {
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
/* Padding */
|
||||
.p {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
.p-1 {
|
||||
padding: 1rem;
|
||||
}
|
||||
.p-2 {
|
||||
padding: 2rem;
|
||||
}
|
||||
.p-3 {
|
||||
padding: 3rem;
|
||||
}
|
||||
.py {
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
.py-1 {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
.py-2 {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
.py-3 {
|
||||
padding: 3rem 0;
|
||||
}
|
||||
|
||||
/* Margin */
|
||||
.m {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
.m-1 {
|
||||
margin: 1rem;
|
||||
}
|
||||
.m-2 {
|
||||
margin: 2rem;
|
||||
}
|
||||
.m-3 {
|
||||
margin: 3rem;
|
||||
}
|
||||
.my {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
.my-1 {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
.my-2 {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
.my-3 {
|
||||
margin: 3rem 0;
|
||||
}
|
||||
|
||||
/* Grid */
|
||||
.grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-gap: 1rem;
|
||||
}
|
||||
|
||||
.grid-3 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-gap: 1rem;
|
||||
}
|
||||
|
||||
.grid-4 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-gap: 1rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
background: var(--light-color);
|
||||
color: #333;
|
||||
padding: 0.4rem 1.3rem;
|
||||
font-size: 1rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
margin-right: 0.5rem;
|
||||
transition: opacity 0.2s ease-in;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.3rem 1rem;
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.2rem 0.7rem;
|
||||
text-align: center;
|
||||
margin: 0.3rem;
|
||||
background: var(--light-color);
|
||||
color: #333;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 0.7rem;
|
||||
margin: 1rem 0;
|
||||
opacity: 0.9;
|
||||
background: var(--light-color);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-primary,
|
||||
.bg-primary,
|
||||
.badge-primary,
|
||||
.alert-primary {
|
||||
background: var(--primary-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-light,
|
||||
.bg-light,
|
||||
.badge-light,
|
||||
.alert-light {
|
||||
background: var(--light-color);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-dark,
|
||||
.bg-dark,
|
||||
.badge-dark,
|
||||
.alert-dark {
|
||||
background: var(--dark-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-danger,
|
||||
.bg-danger,
|
||||
.badge-danger,
|
||||
.alert-danger {
|
||||
background: var(--danger-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-success,
|
||||
.bg-success,
|
||||
.badge-success,
|
||||
.alert-success {
|
||||
background: var(--success-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-white,
|
||||
.bg-white,
|
||||
.badge-white,
|
||||
.alert-white {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
border: #ccc solid 1px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.bg-light,
|
||||
.badge-light {
|
||||
border: #ccc solid 1px;
|
||||
}
|
||||
|
||||
.round-img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
input {
|
||||
margin: 1.2rem 0;
|
||||
}
|
||||
|
||||
.form-text {
|
||||
display: block;
|
||||
margin-top: 0.3rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
input[type='text'],
|
||||
input[type='email'],
|
||||
input[type='password'],
|
||||
input[type='date'],
|
||||
select,
|
||||
textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.4rem;
|
||||
font-size: 1.2rem;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
input[type='submit'],
|
||||
button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table th {
|
||||
background: var(--light-color);
|
||||
}
|
||||
|
||||
/* Navbar */
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.7rem 2rem;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.navbar ul {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.navbar a {
|
||||
color: #fff;
|
||||
padding: 0.45rem;
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
|
||||
.navbar a:hover {
|
||||
color: var(--light-color);
|
||||
}
|
||||
|
||||
.navbar .welcome span {
|
||||
margin-right: 0.6rem;
|
||||
}
|
||||
|
||||
/* Offsets */
|
||||
.offset-1-col {
|
||||
margin-left: 8.66666666667%;
|
||||
}
|
||||
|
||||
.offset-2-col {
|
||||
margin-left: 17.3333333333%;
|
||||
}
|
||||
|
||||
.offset-3-col {
|
||||
margin-left: 26%;
|
||||
}
|
||||
|
||||
.offset-4-col {
|
||||
margin-left: 34.6666666667%;
|
||||
}
|
||||
|
||||
.offset-5-col {
|
||||
margin-left: 43.3333333333%;
|
||||
}
|
||||
|
||||
.offset-6-col {
|
||||
margin-left: 52%;
|
||||
}
|
||||
|
||||
.offset-7-col {
|
||||
margin-left: 60.6666666667%;
|
||||
}
|
||||
|
||||
.offset-8-col {
|
||||
margin-left: 69.3333333333%;
|
||||
}
|
||||
|
||||
.offset-9-col {
|
||||
margin-left: 78.0%;
|
||||
}
|
||||
|
||||
.offset-10-col {
|
||||
margin-left: 86.6666666667%;
|
||||
}
|
||||
|
||||
.offset-11-col {
|
||||
margin-left: 95.3333333333%;
|
||||
}
|
||||
|
||||
.active {
|
||||
/*background-color: #28a745;*/
|
||||
}
|
||||
|
||||
.notActive {
|
||||
background-color: #f4f4f4;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.notActive > span.text-danger {
|
||||
background-color: #f4f4f4;
|
||||
color: var(--light-dark);
|
||||
}
|
||||
|
||||
/* Mobile Styles */
|
||||
@media (max-width: 700px) {
|
||||
.hide-sm {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.grid-2,
|
||||
.grid-3,
|
||||
.grid-4 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
/* Text Styles */
|
||||
.x-large {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.large {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.lead {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Navbar */
|
||||
.navbar {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.navbar ul {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
166
dev/app.js
Normal file
166
dev/app.js
Normal file
@ -0,0 +1,166 @@
|
||||
const $ = require('jquery');
|
||||
const _ = require('underscore');
|
||||
const Backbone = require('backbone');
|
||||
const io = require('socket.io-client');
|
||||
|
||||
const socket = io('http://localhost:3001');
|
||||
|
||||
socket.on('selectionPriceUpdate', data => console.log('selectionPriceUpdate', data));
|
||||
socket.on('SelectionStateUpdate', data => console.log('selectionStateUpdate', data));
|
||||
socket.on('eventStateUpdate', data => console.log('eventStateUpdate', data));
|
||||
|
||||
(function() {
|
||||
console.log('Go!');
|
||||
|
||||
const templates = {
|
||||
'empty': _.template('<div></div>'),
|
||||
'topItem': _.template('<div class="offset-1-col"><%=id%> <%=name%></div>'),
|
||||
'subItem': _.template('<div class="offset-2-col"><%=id%> <%=name%></div>'),
|
||||
'eventItem': _.template('<div class="offset-3-col <%=visibleClass%>"><%=id%> <%=name%></div>'),
|
||||
'selItem': _.template('<div class="offset-4-col <%=visibleClass%>"><%=id%> <%=name%> <span class="text-danger">Price:<%=price%></span></div>')
|
||||
};
|
||||
|
||||
console.log(templates);
|
||||
|
||||
const EventItem = Backbone.Model.extend({
|
||||
|
||||
});
|
||||
|
||||
const EventCollection = Backbone.Collection.extend({
|
||||
'model': EventItem
|
||||
});
|
||||
|
||||
const eventCollection = new EventCollection();
|
||||
|
||||
const EventListModel = Backbone.Model.extend({
|
||||
'initialize': function() {
|
||||
this.eventCollection = eventCollection;
|
||||
this.listenTo(this, 'change', this.onChange);
|
||||
this.listenTo(this.eventCollection, 'change', this.onCollectionChange);
|
||||
this.getEvents();
|
||||
},
|
||||
'onChange': function() {
|
||||
// console.log('EventListModel Changed');
|
||||
},
|
||||
'onCollectionChange': function() {
|
||||
// console.log('eventCollection Changed');
|
||||
},
|
||||
'getEvents': async function () {
|
||||
const data = await fetch('http://localhost:3001/api/selections/')
|
||||
.then((response) => {
|
||||
if (response.ok)
|
||||
return response.json();
|
||||
|
||||
throw response;
|
||||
})
|
||||
.then((data) => {
|
||||
return data.hasOwnProperty('category') ? data.category : [];
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error fetching');
|
||||
});
|
||||
|
||||
if (data && data.length > 0)
|
||||
this.store(data);
|
||||
},
|
||||
'store' : function(data) {
|
||||
const newItems = [];
|
||||
data.forEach((topItem) => {
|
||||
newItems.push({ ...this.getItem(topItem), 'type':'topItem' });
|
||||
|
||||
if (topItem.hasOwnProperty('subcat'))
|
||||
topItem.subcat.forEach((subItem) => {
|
||||
newItems.push({ ...this.getItem(subItem), 'type':'subItem' });
|
||||
|
||||
if (subItem.hasOwnProperty('event'))
|
||||
subItem.event.forEach((eventItem) => {
|
||||
newItems.push({ ...this.getItem(eventItem), 'type':'eventItem' });
|
||||
|
||||
if(eventItem.hasOwnProperty('selection'))
|
||||
eventItem.selection.forEach((selItem) => {
|
||||
newItems.push({ ...this.getItem(selItem), 'pid':eventItem.id, 'type':'selItem' });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
this.eventCollection.reset(newItems);
|
||||
},
|
||||
'getItem': function({ id, name, active = null, price = null }) {
|
||||
return { id, name, active, price };
|
||||
},
|
||||
'updatedItem': function(newItem) {
|
||||
const foundData = this.eventCollection.findWhere({ 'id':newItem.id });
|
||||
|
||||
if (foundData)
|
||||
foundData.set('price', newItem.newPrice);
|
||||
},
|
||||
'updatedState': function(newItem) {
|
||||
const foundData = this.eventCollection.findWhere({ 'id':newItem.id });
|
||||
|
||||
if (foundData)
|
||||
foundData.set({ 'active': newItem.active, 'visibleClass':(newItem.active === true) ? 'active' : 'notActive' });
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
const EventItemView = Backbone.View.extend({
|
||||
'tagName': 'div',
|
||||
'className': 'itemRow mui-row',
|
||||
'template': _.template('<div>Not set</div>'),
|
||||
'initialize': function() {
|
||||
if (templates.hasOwnProperty(this.model.get('type')))
|
||||
this.template = templates[this.model.get('type')];
|
||||
this.listenTo(this.model, 'change', this.onChange);
|
||||
|
||||
this.render();
|
||||
},
|
||||
'onChange': function() {
|
||||
this.render();
|
||||
},
|
||||
'render': function() {
|
||||
this.$el.html(this.template(this.model.toJSON()));
|
||||
},
|
||||
'updateVisibility': function() {
|
||||
let visibleClass = '';
|
||||
if (active !== null)
|
||||
visibleClass = (active === true) ? 'active' : 'notActive' ;
|
||||
|
||||
this.model.set('visibleClass', visibleClass) ;
|
||||
}
|
||||
});
|
||||
|
||||
const EventListView = Backbone.View.extend({
|
||||
'tagName': 'div',
|
||||
'className' : 'newsItem',
|
||||
'template': _.template('<div><%=name%></div>'),
|
||||
'initialize': function() {
|
||||
console.log('EventListView::Init');
|
||||
this.listenTo(this.model.eventCollection, 'change', this.onChange);
|
||||
this.model.eventCollection.bind('reset', this.render, this);
|
||||
},
|
||||
'render': function() {
|
||||
this.$el.html(this.template(this.model.toJSON()));
|
||||
|
||||
this.model.eventCollection.each(item => {
|
||||
const active = item.get('active');
|
||||
let visibleClass = '';
|
||||
if (active !== null)
|
||||
visibleClass = (active === true) ? 'active' : 'notActive' ;
|
||||
|
||||
item.set('visibleClass', visibleClass) ;
|
||||
|
||||
const eView = new EventItemView({ 'model': item });
|
||||
this.$el.append(eView.el);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const eventList = new EventListModel;
|
||||
|
||||
const newsListView = new EventListView({ 'model': eventList, 'el':'#eventList' });
|
||||
|
||||
socket.on('selectionPriceUpdate', data => eventList.updatedItem( data));
|
||||
socket.on('SelectionStateUpdate', data => eventList.updatedState( data));
|
||||
socket.on('eventStateUpdate', data => eventList.updatedState( data));
|
||||
})();
|
144
gulpfile.js
Normal file
144
gulpfile.js
Normal file
@ -0,0 +1,144 @@
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const browsersync = require('browser-sync').create();
|
||||
const cp = require('child_process');
|
||||
const cssnano = require('cssnano');
|
||||
const del = require('del');
|
||||
const eslint = require('gulp-eslint');
|
||||
const gulp = require('gulp');
|
||||
const imagemin = require('gulp-imagemin');
|
||||
const newer = require('gulp-newer');
|
||||
const plumber = require('gulp-plumber');
|
||||
const postcss = require('gulp-postcss');
|
||||
const rename = require('gulp-rename');
|
||||
const sass = require('gulp-sass');
|
||||
const webpack = require('webpack');
|
||||
const webpackconfig = require('./webpack.config.js');
|
||||
const webpackstream = require('webpack-stream');
|
||||
const uglify = require('gulp-uglify');
|
||||
|
||||
const browserify = require('browserify');
|
||||
const source = require('vinyl-source-stream');
|
||||
const buffer = require('vinyl-buffer');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const gutil = require('gulp-util');
|
||||
|
||||
// Using: https://gist.github.com/jeromecoupe/0b807b0c1050647eb340360902c3203a
|
||||
|
||||
// BrowserSync
|
||||
function browserSync(done) {
|
||||
browsersync.init({
|
||||
'server': {
|
||||
'baseDir': './live/'
|
||||
},
|
||||
'port': 3000
|
||||
});
|
||||
done();
|
||||
}
|
||||
|
||||
// BrowserSync Reload
|
||||
function browserSyncReload(done) {
|
||||
browsersync.reload();
|
||||
done();
|
||||
}
|
||||
|
||||
// Clean assets
|
||||
function clean() {
|
||||
return del(['./live/']);
|
||||
}
|
||||
|
||||
// Lint scripts
|
||||
function scriptsLint() {
|
||||
return gulp
|
||||
.src(['./app/**/*.js', './gulpfile.js'])
|
||||
.pipe(plumber())
|
||||
.pipe(eslint())
|
||||
.pipe(eslint.format())
|
||||
.pipe(eslint.failAfterError());
|
||||
}
|
||||
function bb() {
|
||||
const b = browserify({
|
||||
'debug': true,
|
||||
'entries': './app/app.js'
|
||||
});
|
||||
|
||||
return b.bundle().pipe(source('app.js'))
|
||||
.pipe(buffer())
|
||||
// .pipe(stripDebug())
|
||||
.pipe(rename('bundle.js'))
|
||||
|
||||
.pipe(sourcemaps.init({ 'loadMaps': true }))
|
||||
// Add transformation tasks to the pipeline here.
|
||||
// .pipe(uglify())
|
||||
.on('error', gutil.log)
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest('./live/js'));
|
||||
}
|
||||
|
||||
function scripts() {
|
||||
return (
|
||||
gulp
|
||||
.src(['./app/app.js'])
|
||||
.pipe(plumber())
|
||||
.pipe(webpackstream(webpackconfig), webpack)
|
||||
// .pipe(uglify())
|
||||
// folder only, filename is specified in webpack config
|
||||
.pipe(gulp.dest('./live/js/'))
|
||||
.pipe(browsersync.stream())
|
||||
);
|
||||
}
|
||||
|
||||
// CSS task
|
||||
function css() {
|
||||
return (
|
||||
gulp
|
||||
.src('./app/css/**/*.css')
|
||||
.pipe(plumber())
|
||||
.pipe(sass({ 'outputStyle': 'expanded' }))
|
||||
.pipe(gulp.dest('./live/css/'))
|
||||
.pipe(rename({ 'suffix': '.min' }))
|
||||
.pipe(postcss([autoprefixer(), cssnano()]))
|
||||
.pipe(gulp.dest('./live/css/'))
|
||||
.pipe(browsersync.stream())
|
||||
);
|
||||
}
|
||||
|
||||
function copy() {
|
||||
return(
|
||||
gulp.src(['./public/**/*']).pipe(gulp.dest('./live/'))
|
||||
);
|
||||
}
|
||||
// Watch files
|
||||
function watchFiles() {
|
||||
gulp.watch('./app/css/**/*', css);
|
||||
gulp.watch('./app/**/*.js', gulp.series(scriptsLint, bb));
|
||||
|
||||
/* gulp.watch(
|
||||
[
|
||||
"./_includes/!**!/!*",
|
||||
"./_layouts/!**!/!*",
|
||||
"./_pages/!**!/!*",
|
||||
"./_posts/!**!/!*",
|
||||
"./_projects/!**!/!*"
|
||||
],
|
||||
gulp.series(jekyll, browserSyncReload)
|
||||
);
|
||||
gulp.watch("./assets/img/!**!/!*", images);*/
|
||||
}
|
||||
|
||||
// define complex tasks
|
||||
const js = gulp.series(scriptsLint, bb);
|
||||
const build = gulp.series(clean, gulp.parallel(copy, css, js /* images, jekyll,*/ ));
|
||||
const watch = gulp.parallel(watchFiles, browserSync);
|
||||
|
||||
// export tasks
|
||||
// exports.images = images;
|
||||
exports.copy = copy;
|
||||
exports.css = css;
|
||||
exports.js = js;
|
||||
// exports.jekyll = jekyll;
|
||||
exports.clean = clean;
|
||||
exports.build = build;
|
||||
exports.watch = watch;
|
||||
exports.default = build;
|
||||
|
||||
exports.bb = bb;
|
480
live/css/index.css
Normal file
480
live/css/index.css
Normal file
@ -0,0 +1,480 @@
|
||||
@import url("https://fonts.googleapis.com/css?family=Roboto");
|
||||
/* Global Styles */
|
||||
:root {
|
||||
--primary-color: #dc3545;
|
||||
--dark-color: #333333;
|
||||
--light-color: #f4f4f4;
|
||||
--danger-color: #dc3545;
|
||||
--success-color: #28a745;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
.container {
|
||||
max-width: 1100px;
|
||||
margin: auto;
|
||||
overflow: hidden;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
/* Text Styles*/
|
||||
.x-large {
|
||||
font-size: 4rem;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.large {
|
||||
font-size: 3rem;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.lead {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.text-dark {
|
||||
color: var(--dark-color);
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Center All */
|
||||
.all-center {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
padding: 1rem;
|
||||
border: #ccc 1px dotted;
|
||||
margin: 0.7rem 0;
|
||||
}
|
||||
|
||||
/* List */
|
||||
.list {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.list li {
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
/* Padding */
|
||||
.p {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.p-1 {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.p-2 {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.p-3 {
|
||||
padding: 3rem;
|
||||
}
|
||||
|
||||
.py {
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.py-1 {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.py-2 {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.py-3 {
|
||||
padding: 3rem 0;
|
||||
}
|
||||
|
||||
/* Margin */
|
||||
.m {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.m-1 {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.m-2 {
|
||||
margin: 2rem;
|
||||
}
|
||||
|
||||
.m-3 {
|
||||
margin: 3rem;
|
||||
}
|
||||
|
||||
.my {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.my-1 {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.my-2 {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.my-3 {
|
||||
margin: 3rem 0;
|
||||
}
|
||||
|
||||
/* Grid */
|
||||
.grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-gap: 1rem;
|
||||
}
|
||||
|
||||
.grid-3 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-gap: 1rem;
|
||||
}
|
||||
|
||||
.grid-4 {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-gap: 1rem;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
background: var(--light-color);
|
||||
color: #333;
|
||||
padding: 0.4rem 1.3rem;
|
||||
font-size: 1rem;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
margin-right: 0.5rem;
|
||||
transition: opacity 0.2s ease-in;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.3rem 1rem;
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.2rem 0.7rem;
|
||||
text-align: center;
|
||||
margin: 0.3rem;
|
||||
background: var(--light-color);
|
||||
color: #333;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 0.7rem;
|
||||
margin: 1rem 0;
|
||||
opacity: 0.9;
|
||||
background: var(--light-color);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-primary,
|
||||
.bg-primary,
|
||||
.badge-primary,
|
||||
.alert-primary {
|
||||
background: var(--primary-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-light,
|
||||
.bg-light,
|
||||
.badge-light,
|
||||
.alert-light {
|
||||
background: var(--light-color);
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-dark,
|
||||
.bg-dark,
|
||||
.badge-dark,
|
||||
.alert-dark {
|
||||
background: var(--dark-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-danger,
|
||||
.bg-danger,
|
||||
.badge-danger,
|
||||
.alert-danger {
|
||||
background: var(--danger-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-success,
|
||||
.bg-success,
|
||||
.badge-success,
|
||||
.alert-success {
|
||||
background: var(--success-color);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-white,
|
||||
.bg-white,
|
||||
.badge-white,
|
||||
.alert-white {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
border: #ccc solid 1px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.bg-light,
|
||||
.badge-light {
|
||||
border: #ccc solid 1px;
|
||||
}
|
||||
|
||||
.round-img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Forms */
|
||||
input {
|
||||
margin: 1.2rem 0;
|
||||
}
|
||||
|
||||
.form-text {
|
||||
display: block;
|
||||
margin-top: 0.3rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
input[type='text'],
|
||||
input[type='email'],
|
||||
input[type='password'],
|
||||
input[type='date'],
|
||||
select,
|
||||
textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.4rem;
|
||||
font-size: 1.2rem;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
input[type='submit'],
|
||||
button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
padding: 1rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table th {
|
||||
background: var(--light-color);
|
||||
}
|
||||
|
||||
/* Navbar */
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.7rem 2rem;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
opacity: 0.9;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.navbar ul {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.navbar a {
|
||||
color: #fff;
|
||||
padding: 0.45rem;
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
|
||||
.navbar a:hover {
|
||||
color: var(--light-color);
|
||||
}
|
||||
|
||||
.navbar .welcome span {
|
||||
margin-right: 0.6rem;
|
||||
}
|
||||
|
||||
/* Offsets */
|
||||
.offset-1-col {
|
||||
margin-left: 8.66666666667%;
|
||||
}
|
||||
|
||||
.offset-2-col {
|
||||
margin-left: 17.3333333333%;
|
||||
}
|
||||
|
||||
.offset-3-col {
|
||||
margin-left: 26%;
|
||||
}
|
||||
|
||||
.offset-4-col {
|
||||
margin-left: 34.6666666667%;
|
||||
}
|
||||
|
||||
.offset-5-col {
|
||||
margin-left: 43.3333333333%;
|
||||
}
|
||||
|
||||
.offset-6-col {
|
||||
margin-left: 52%;
|
||||
}
|
||||
|
||||
.offset-7-col {
|
||||
margin-left: 60.6666666667%;
|
||||
}
|
||||
|
||||
.offset-8-col {
|
||||
margin-left: 69.3333333333%;
|
||||
}
|
||||
|
||||
.offset-9-col {
|
||||
margin-left: 78.0%;
|
||||
}
|
||||
|
||||
.offset-10-col {
|
||||
margin-left: 86.6666666667%;
|
||||
}
|
||||
|
||||
.offset-11-col {
|
||||
margin-left: 95.3333333333%;
|
||||
}
|
||||
|
||||
.active {
|
||||
/*background-color: #28a745;*/
|
||||
}
|
||||
|
||||
.notActive {
|
||||
background-color: #f4f4f4;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.notActive > span.text-danger {
|
||||
background-color: #f4f4f4;
|
||||
color: var(--light-dark);
|
||||
}
|
||||
|
||||
/* Mobile Styles */
|
||||
@media (max-width: 700px) {
|
||||
.hide-sm {
|
||||
display: none;
|
||||
}
|
||||
.grid-2,
|
||||
.grid-3,
|
||||
.grid-4 {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
/* Text Styles */
|
||||
.x-large {
|
||||
font-size: 3rem;
|
||||
}
|
||||
.large {
|
||||
font-size: 2rem;
|
||||
}
|
||||
.lead {
|
||||
font-size: 1rem;
|
||||
}
|
||||
/* Navbar */
|
||||
.navbar {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
.navbar ul {
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
1
live/css/index.min.css
vendored
Normal file
1
live/css/index.min.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
@import url("https://fonts.googleapis.com/css?family=Roboto");:root{--primary-color:#dc3545;--dark-color:#333;--light-color:#f4f4f4;--danger-color:#dc3545;--success-color:#28a745}*{box-sizing:border-box;margin:0;padding:0}body{font-family:Roboto,sans-serif;font-size:1rem;line-height:1.6;background-color:#fff;color:#333}a{color:var(--primary-color);text-decoration:none}a:hover{color:#666}ul{list-style:none}img{width:100%}.container{max-width:1100px;margin:auto;overflow:hidden;padding:0 2rem}.x-large{font-size:4rem}.large,.x-large{line-height:1.2;margin-bottom:1rem}.large{font-size:3rem}.lead{font-size:1.5rem;margin-bottom:1rem}.text-primary{color:var(--primary-color)}.text-dark{color:var(--dark-color)}.text-success{color:var(--success-color)}.text-danger{color:var(--danger-color)}.text-center{text-align:center}.text-right{text-align:right}.text-left{text-align:left}.all-center{display:flex;flex-direction:column;width:100%;margin:auto;justify-content:center;align-items:center;text-align:center}.card{padding:1rem;border:1px dotted #ccc;margin:.7rem 0}.list{margin:.5rem 0}.list li{padding-bottom:.3rem}.p{padding:.5rem}.p-1{padding:1rem}.p-2{padding:2rem}.p-3{padding:3rem}.py{padding:.5rem 0}.py-1{padding:1rem 0}.py-2{padding:2rem 0}.py-3{padding:3rem 0}.m{margin:.5rem}.m-1{margin:1rem}.m-2{margin:2rem}.m-3{margin:3rem}.my{margin:.5rem 0}.my-1{margin:1rem 0}.my-2{margin:2rem 0}.my-3{margin:3rem 0}.grid-2{display:grid;grid-template-columns:repeat(2,1fr);grid-gap:1rem}.grid-3{display:grid;grid-template-columns:repeat(3,1fr);grid-gap:1rem}.grid-4{display:grid;grid-template-columns:repeat(4,1fr);grid-gap:1rem}.btn{display:inline-block;background:var(--light-color);color:#333;padding:.4rem 1.3rem;font-size:1rem;border:none;cursor:pointer;margin-right:.5rem;transition:opacity .2s ease-in;outline:none}.btn-link{background:none;padding:0;margin:0}.btn-block{display:block;width:100%}.btn-sm{font-size:.8rem;padding:.3rem 1rem;margin-right:.2rem}.badge{display:inline-block;font-size:.8rem;padding:.2rem .7rem;text-align:center;margin:.3rem;border-radius:5px}.alert,.badge{background:var(--light-color);color:#333}.alert{padding:.7rem;margin:1rem 0;opacity:.9}.alert-primary,.badge-primary,.bg-primary,.btn-primary{background:var(--primary-color);color:#fff}.alert-light,.badge-light,.bg-light,.btn-light{background:var(--light-color);color:#333}.alert-dark,.badge-dark,.bg-dark,.btn-dark{background:var(--dark-color);color:#fff}.alert-danger,.badge-danger,.bg-danger,.btn-danger{background:var(--danger-color);color:#fff}.alert-success,.badge-success,.bg-success,.btn-success{background:var(--success-color);color:#fff}.alert-white,.badge-white,.bg-white,.btn-white{background:#fff;color:#333;border:1px solid #ccc}.btn:hover{opacity:.8}.badge-light,.bg-light{border:1px solid #ccc}.round-img{border-radius:50%}input{margin:1.2rem 0}.form-text{display:block;margin-top:.3rem;color:#888}input[type=date],input[type=email],input[type=password],input[type=text],select,textarea{display:block;width:100%;padding:.4rem;font-size:1.2rem;border:1px solid #ccc}button,input[type=submit]{font:inherit}table td,table th{padding:1rem;text-align:left}table th{background:var(--light-color)}.navbar{justify-content:space-between;align-items:center;padding:.7rem 2rem;z-index:1;width:100%;opacity:.9;margin-bottom:1rem}.navbar,.navbar ul{display:flex}.navbar a{color:#fff;padding:.45rem;margin:0 .25rem}.navbar a:hover{color:var(--light-color)}.navbar .welcome span{margin-right:.6rem}.offset-1-col{margin-left:8.66666666667%}.offset-2-col{margin-left:17.3333333333%}.offset-3-col{margin-left:26%}.offset-4-col{margin-left:34.6666666667%}.offset-5-col{margin-left:43.3333333333%}.offset-6-col{margin-left:52%}.offset-7-col{margin-left:60.6666666667%}.offset-8-col{margin-left:69.3333333333%}.offset-9-col{margin-left:78%}.offset-10-col{margin-left:86.6666666667%}.offset-11-col{margin-left:95.3333333333%}.notActive{background-color:#f4f4f4;color:#ccc}.notActive>span.text-danger{background-color:#f4f4f4;color:var(--light-dark)}@media (max-width:700px){.hide-sm{display:none}.grid-2,.grid-3,.grid-4{grid-template-columns:1fr}.x-large{font-size:3rem}.large{font-size:2rem}.lead{font-size:1rem}.navbar{display:block;text-align:center}.navbar ul{text-align:center;justify-content:center}}
|
24
live/index.html
Normal file
24
live/index.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>FSB</title>
|
||||
<link href="./css/index.css" rel="stylesheet" type="text/css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar bg-primary">
|
||||
<h1>
|
||||
FSB
|
||||
</h1>
|
||||
</nav>
|
||||
<div class="app">
|
||||
|
||||
<div id="eventList"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="./js/bundle.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
22185
live/js/bundle.js
Normal file
22185
live/js/bundle.js
Normal file
File diff suppressed because it is too large
Load Diff
1
live/js/bundle.js.map
Normal file
1
live/js/bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
7
npm-shrinkwrap
Normal file
7
npm-shrinkwrap
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"graceful-fs": {
|
||||
"version": "4.2.2"
|
||||
}
|
||||
}
|
||||
}
|
16577
package-lock.json
generated
Normal file
16577
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
53
package.json
Normal file
53
package.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "fsb_backbone",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "gulp default",
|
||||
"start:server": "node ./server/server.js",
|
||||
"preinstall": "npx npm-force-resolutions",
|
||||
"postinstall": "npm run build"
|
||||
},
|
||||
"resolutions": {
|
||||
"graceful-fs": "^4.2.4"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"backbone": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"amd-define-factory-patcher-loader": "^1.0.0",
|
||||
"autoprefixer": "^10.2.4",
|
||||
"browser-sync": "^2.26.14",
|
||||
"browserify": "^17.0.0",
|
||||
"child_process": "^1.0.2",
|
||||
"cssnano": "^4.1.10",
|
||||
"del": "^6.0.0",
|
||||
"eslint": "^7.20.0",
|
||||
"express": "^4.17.1",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-eslint": "^6.0.0",
|
||||
"gulp-imagemin": "^7.1.0",
|
||||
"gulp-newer": "^1.4.0",
|
||||
"gulp-plumber": "^1.2.1",
|
||||
"gulp-postcss": "^9.0.0",
|
||||
"gulp-rename": "^2.0.0",
|
||||
"gulp-sass": "^4.1.0",
|
||||
"gulp-sourcemaps": "^3.0.0",
|
||||
"gulp-uglify": "^3.0.2",
|
||||
"gulp-util": "^3.0.8",
|
||||
"jquery": "^3.5.1",
|
||||
"postcss": "^8.2.6",
|
||||
"prettier": "^2.2.1",
|
||||
"socket.io": "^3.1.2",
|
||||
"socket.io-client": "^3.1.2",
|
||||
"underscore": "^1.12.0",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0",
|
||||
"webpack": "^5.24.2",
|
||||
"webpack-stream": "^6.1.2"
|
||||
}
|
||||
}
|
24
public/index.html
Normal file
24
public/index.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>FSB</title>
|
||||
<link href="./css/index.css" rel="stylesheet" type="text/css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar bg-primary">
|
||||
<h1>
|
||||
FSB
|
||||
</h1>
|
||||
</nav>
|
||||
<div class="app">
|
||||
|
||||
<div id="eventList"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="./js/bundle.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
178
server/data.json
Normal file
178
server/data.json
Normal file
@ -0,0 +1,178 @@
|
||||
{
|
||||
"category": [
|
||||
{
|
||||
"subcat": [
|
||||
{
|
||||
"event": [
|
||||
{
|
||||
"selection": [
|
||||
{
|
||||
"price": 1.5,
|
||||
"id": 1111,
|
||||
"name": "KB Stars",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"price": 2.25,
|
||||
"id": 1112,
|
||||
"name": "Suwon Kepco Vixtorm",
|
||||
"active": true
|
||||
}
|
||||
],
|
||||
"id": 111,
|
||||
"name": "KB Stars v Suwon Kepco Vixtorm",
|
||||
"active" : true
|
||||
}
|
||||
],
|
||||
"id": 11,
|
||||
"name": "South Korea V League"
|
||||
}
|
||||
],
|
||||
"id": 1,
|
||||
"name": "Volleyball"
|
||||
},
|
||||
{
|
||||
"subcat": [
|
||||
{
|
||||
"event": [
|
||||
{
|
||||
"selection": [
|
||||
{
|
||||
"price": 11.0,
|
||||
"id": 2111,
|
||||
"name": "Sakellaridis M / Sakellaridis S",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"price": 1.01,
|
||||
"id": 2112,
|
||||
"name": "Hazawa S / Yamanaka T",
|
||||
"active": false
|
||||
}
|
||||
],
|
||||
"id": 211,
|
||||
"active": true,
|
||||
"name": "Sakellaridis M / Sakellaridis S v Hazawa S / Yamanaka T"
|
||||
}
|
||||
],
|
||||
"id": 21,
|
||||
"name": "ITF Turkey F1"
|
||||
},
|
||||
{
|
||||
"event": [
|
||||
{
|
||||
"selection": [
|
||||
{
|
||||
"price": 15.0,
|
||||
"id": 2211,
|
||||
"name": "Hazawa M / Sakellaridis",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"price": 1.61,
|
||||
"id": 2212,
|
||||
"name": "Hazawa S / Yamanaka T",
|
||||
"active": false
|
||||
}
|
||||
],
|
||||
"id": 221,
|
||||
"active": true,
|
||||
"name": "Sakellaridis M / Sakellaridis S v Hazawa S / Yamanaka T"
|
||||
}
|
||||
],
|
||||
"id": 22,
|
||||
"name": "ITF Turkey F2"
|
||||
},
|
||||
{
|
||||
"event": [
|
||||
{
|
||||
"selection": [
|
||||
{
|
||||
"price": 4.1,
|
||||
"id": 2311,
|
||||
"name": "Yamanaka M / Sakellaridis S",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"price": 3.01,
|
||||
"id": 2312,
|
||||
"name": "Sakellaridis S / Yamanaka T",
|
||||
"active": false
|
||||
}
|
||||
],
|
||||
"id": 231,
|
||||
"active": true,
|
||||
"name": "Yamanaka M / Sakellaridis S v Sakellaridis S / Yamanaka T"
|
||||
}
|
||||
],
|
||||
"id": 23,
|
||||
"name": "ITF Turkey F3"
|
||||
}
|
||||
],
|
||||
"id": 2,
|
||||
"name": "Tennis"
|
||||
},
|
||||
{
|
||||
"subcat": [
|
||||
{
|
||||
"event": [
|
||||
{
|
||||
"selection": [
|
||||
{
|
||||
"price": 1.8,
|
||||
"id": 3311,
|
||||
"name": "Morocco",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"price": 3.8,
|
||||
"id": 3312,
|
||||
"name": "Draw",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"price": 3.8,
|
||||
"id": 3313,
|
||||
"name": "Tunisia",
|
||||
"active": true
|
||||
}
|
||||
],
|
||||
"id": 331,
|
||||
"active": false,
|
||||
"name": "Morocco v Tunisia"
|
||||
},
|
||||
{
|
||||
"selection": [
|
||||
{
|
||||
"price": 1.8,
|
||||
"id": 3321,
|
||||
"name": "Portugal",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"price": 3.8,
|
||||
"id": 3322,
|
||||
"name": "Draw",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"price": 3.8,
|
||||
"id": 3323,
|
||||
"name": "Spain",
|
||||
"active": true
|
||||
}
|
||||
],
|
||||
"id": 332,
|
||||
"active": true,
|
||||
"name": "Portugal v Spain"
|
||||
}
|
||||
],
|
||||
"id": 33,
|
||||
"name": "World cup"
|
||||
}
|
||||
],
|
||||
"id": 3,
|
||||
"name": "Football"
|
||||
}
|
||||
]
|
||||
}
|
21
server/eventStateChange.js
Normal file
21
server/eventStateChange.js
Normal file
@ -0,0 +1,21 @@
|
||||
module.exports = (data, socket) => {
|
||||
|
||||
const events = [];
|
||||
|
||||
data.category.forEach(category => {
|
||||
category.subcat.forEach(subcat => {
|
||||
subcat.event.forEach(event => {
|
||||
events.push({
|
||||
active: Math.random() >= 0.5,
|
||||
id: event.id
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
events.forEach(event =>
|
||||
setTimeout(() => { socket.emit('eventStateUpdate', event) }, 1000 * (Math.floor(Math.random() * 25) + 1))
|
||||
);
|
||||
|
||||
};
|
||||
|
24
server/selectionPriceChange.js
Normal file
24
server/selectionPriceChange.js
Normal file
@ -0,0 +1,24 @@
|
||||
module.exports = (data, socket) => {
|
||||
const selections = [];
|
||||
|
||||
data.category.forEach(category => {
|
||||
category.subcat.forEach(subcat => {
|
||||
subcat.event.forEach(event => {
|
||||
event.selection.forEach(selection => {
|
||||
selections.push({
|
||||
'newPrice': Math.floor(Math.random() * 9) + 1,
|
||||
// id : selection.price
|
||||
'id' : selection.id
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
selections.forEach(selection =>
|
||||
setTimeout(() => {
|
||||
socket.emit('selectionPriceUpdate', selection);
|
||||
}, 1000 * (Math.floor(Math.random() * 15) + 1))
|
||||
);
|
||||
};
|
||||
|
25
server/selectionStateChange.js
Normal file
25
server/selectionStateChange.js
Normal file
@ -0,0 +1,25 @@
|
||||
module.exports = (data, socket) => {
|
||||
|
||||
const selections = [];
|
||||
|
||||
data.category.forEach(category => {
|
||||
category.subcat.forEach(subcat => {
|
||||
subcat.event.forEach(event => {
|
||||
event.selection.forEach(selection => {
|
||||
const { id, price } = selection;
|
||||
selections.push({
|
||||
active: Math.random() >= 0.5,
|
||||
price,
|
||||
id
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
selections.forEach(selection =>
|
||||
setTimeout(() => { socket.emit('selectionStateUpdate', selection) }, 1000 * (Math.floor(Math.random() * 25) + 1))
|
||||
);
|
||||
|
||||
};
|
||||
|
42
server/server.js
Normal file
42
server/server.js
Normal file
@ -0,0 +1,42 @@
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const http = require('http').Server(app);
|
||||
const io = require('socket.io')(http);
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const selectionPriceChange = require('./selectionPriceChange');
|
||||
const selectionStateChange = require('./selectionStateChange');
|
||||
const eventStateChange = require('./eventStateChange');
|
||||
|
||||
const sitePath = '../live';
|
||||
|
||||
const JSON_DATA = JSON.parse(fs.readFileSync('./server/data.json', 'utf-8'));
|
||||
|
||||
console.log(path.join(__dirname, sitePath));
|
||||
|
||||
app.use(express.static(path.join(__dirname, sitePath)));
|
||||
|
||||
app
|
||||
.use((req, res, next) => {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS,PUT');
|
||||
res.header('Access-Control-Allow-Credentials', 'true');
|
||||
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
|
||||
next();
|
||||
})
|
||||
.route('/api/selections').get((req, res) => res.status(200).json(JSON_DATA));
|
||||
|
||||
io.on('connection', socket => {
|
||||
setInterval(() => {
|
||||
selectionPriceChange(JSON_DATA, socket);
|
||||
}, 20000);
|
||||
setInterval(() => {
|
||||
selectionStateChange(JSON_DATA, socket);
|
||||
}, 30000);
|
||||
setInterval(() => {
|
||||
eventStateChange(JSON_DATA, socket);
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
http.listen(3001, () => console.log('running'));
|
31
server/stateChange.js
Normal file
31
server/stateChange.js
Normal file
@ -0,0 +1,31 @@
|
||||
const ramdonBoolean = () => Math.random() >= 0.5;
|
||||
|
||||
module.exports = (data, socket) => {
|
||||
|
||||
console.log(data)
|
||||
|
||||
};
|
||||
|
||||
|
||||
module.exports = (data, socket) => {
|
||||
|
||||
const selections = [];
|
||||
|
||||
data.category.forEach(category => {
|
||||
category.subcat.forEach(subcat => {
|
||||
subcat.event.forEach(event => {
|
||||
event.selection.forEach(selection => {
|
||||
const { id, price } = selection;
|
||||
selections.push({
|
||||
newPrice: Math.floor(Math.random() * 9) + 1,
|
||||
price,
|
||||
id
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
selections.forEach(selection => socket.emit('price-update', selection));
|
||||
};
|
||||
|
15
webpack.config.js
Normal file
15
webpack.config.js
Normal file
@ -0,0 +1,15 @@
|
||||
const path = require('path');
|
||||
|
||||
const config = {
|
||||
|
||||
'entry': {
|
||||
'app':['./app/app.js']
|
||||
},
|
||||
'output': {
|
||||
'path': path.resolve(__dirname, 'live/js'),
|
||||
'filename': 'bundle.js'
|
||||
},
|
||||
'mode': 'development'
|
||||
};
|
||||
|
||||
module.exports = config;
|
Loading…
Reference in New Issue
Block a user