Commit 5c891950 authored by Eliot Berriot's avatar Eliot Berriot

Better layout, more compact and readable

parent 6b565d40
......@@ -16,6 +16,7 @@
"dependencies": {
"jquery": "^3.2.1",
"js-logger": "^1.4.1",
"lodash.merge": "^4.6.0",
"node-sass": "^4.5.3",
"promise-polyfill": "^6.0.2",
"sass-loader": "^6.0.6",
......
<template>
<div id="app">
<router-view></router-view>
<div class="ui fixed inverted menu">
<div class="ui container">
<div class="ui inverted menu">
<a href="#" class="header item">
IdleBits
</a>
</div>
<div class="ui right floating inverted menu">
<a
class="item"
@click="$store.commit('settings/set', {setting: 'buyAmount', value: nextBuyAmountStep.value})">
Buy {{ buyAmount }}
</a>
<popup-wrapper :tag="'a'" class="item">
<span @click="$store.dispatch('settings/save')">
<i class="save icon"></i>
Save
</span>
<popup>
<template v-if="lastSaveSecond !== undefined">
Last save occured {{ lastSaveSecond }} ago
</template>
<template v-else>
No save for current game.
</template>
<br />The game is autosaving every {{ autosaveInterval }} seconds.
</popup>
</popup-wrapper>
</div>
</div>
</div>
<div class="page ui container">
<router-view></router-view>
</div>
</div>
</template>
<script>
import Loop from '../services/loop'
import { mapState, mapGetters } from 'vuex'
import Game from '../services/game'
import Popup from '@/components/semantic/Popup'
import PopupWrapper from '@/components/semantic/PopupWrapper'
import data from '@/data'
export default {
components: {
Popup,
PopupWrapper
},
data () {
return {
amountSteps: data.settings.amountSteps
}
},
computed: {
...mapState({
lastSave: state => state.settings.internal.lastSave,
lastTick: state => state.loop.lastTick,
buyAmount: state => state.settings.buyAmount,
autosaveInterval: state => state.settings.autosaveInterval / 1000
}),
...mapGetters({
throughput: 'storage/throughput'
}),
lastSaveSecond () {
if (this.lastTick) {
// we use last tick as our dependency to ensure the getter
// is recomputed when tick change, because Date is not reactive in
// itself
if (this.lastSave) {
return parseInt((new Date() - this.lastSave) / 1000)
}
}
},
currentBuyAmountIndex () {
const c = this.buyAmount
for (let i = 0; i < this.amountSteps.length; i++) {
if (this.amountSteps[i].value === c) {
return i
}
}
},
nextBuyAmountStep () {
if (this.currentBuyAmountIndex + 1 >= this.amountSteps.length) {
return this.amountSteps[0]
}
return this.amountSteps[this.currentBuyAmountIndex + 1]
}
},
mounted () {
Loop.init()
Game.start()
},
destroyed () {
Loop.clear()
Game.stop()
}
}
</script>
......@@ -27,6 +111,7 @@ export default {
.page {
padding: 1rem;
margin-top: 3.5rem;
}
.column {
margin-top: 0! important
......
<template>
<div>
<div class="ui inverted segment console">
<h2 class="ui center aligned dividing header">
Console
</h2>
<h2 class="ui center aligned header">
Console
<div class="sub header">This is real.</div>
</h2>
<div class="ui segment console">
<div ref="rows" class="rows">
<p v-for="row in rows" :class="['console-row', row.type]">
<a
......@@ -18,8 +19,8 @@
</template>
</p>
</div>
<div class="ui inverted divider"></div>
<div class="ui inverted transparent fluid huge action input">
<div class="ui divider"></div>
<div class="ui transparent fluid huge action input">
<input
v-on:keyup.enter="submit(command)"
v-on:keydown.tab.prevent="autocomplete()"
......@@ -32,7 +33,7 @@
<button
@click="submit(command)"
:disabled="command.trim().length === 0"
class="ui inverted button">execute</button>
class="ui button">execute</button>
</div>
</div>
</div>
......
<template>
<div>
<h2 class="ui center aligned header">
Exchange
<div class="sub header">Sell your information for other currencies at your exchange.</div>
</h2>
<div class="ui segment">
<h2 class="ui center aligned dividing header">
Exchange
<div class="sub header">Sell your information for other currencies at your exchange.</div>
</h2>
<popup-wrapper :tag="'div'" class="ui mini labeled button" tabindex="0">
<div class="ui mini basic button">
<i class="book icon"></i> information
</div>
<a class="ui label">
{{ information | idleNumber }}
</a>
<popup>
<div class="header">Information</div>
Your analytics center process your data and store the result as information.
</popup>
</popup-wrapper>
<div class="ui divided items">
<div class="item">
<div class="middle aligned content">
......@@ -29,9 +41,14 @@
<script>
import { mapState } from 'vuex'
import Popup from '@/components/semantic/Popup'
import PopupWrapper from '@/components/semantic/PopupWrapper'
export default {
components: {
PopupWrapper,
Popup
},
computed: {
...mapState({
exchange: state => state.exchange,
......
<template>
<div>
<h3 class="ui dividing header">
<slot name="header"></slot>
</h3>
<div>
<slot name="sub"></slot>
</div>
<div class="ui">
<h3 class="ui header">
<slot name="header"></slot>
</h3>
<div>
<slot name="sub"></slot>
</div>
<div
v-for="unit in unitData"
@click="$store.dispatch('units/purchase', {group: group, subgroup: subgroup, id: unit.id, amount: unitAmount(unit.id)})"
class="unit-button">
<div class="ui top attached segment">
<div class="content">
<div class="ui small header">{{ unit.name }}</div>
<div
v-for="unit in unitData"
@click="$store.dispatch('units/purchase', {group: group, subgroup: subgroup, id: unit.id, amount: unitAmount(unit.id)})"
class="unit-button">
<div class="ui top attached segment">
<div class="content">
<div class="ui small header">{{ unit.name }}</div>
</div>
</div>
<div class="ui bottom attached mini borderless menu">
<span class="ui item">
<i class="users icon"></i>
{{ unitState[unit.id].count | idleNumber }}
</span>
<span class="ui item">
<i :class="[productionIcon, 'icon']"></i>
<template v-if="group === 'storage'">
{{ unit.size | idleNumber}} {{ productionUnit}}
</template>
<template v-else>
{{ unit[productionUnit] | idleNumber}} {{ productionUnit}}/s
</template>
</span>
<span :class="['ui', 'right', 'floating', {'red active': exchange.dollars < unitPrice(unit.id) | unitAmount(unit.id) === 0}, 'item']">
Buy {{ unitAmount(unit.id) }} for ${{ unitPrice(unit.id) | idleNumber }}
</span>
</div>
</div>
<div class="ui bottom attached mini menu">
<span class="ui item">
<i class="users icon"></i>
{{ unitState[unit.id].count | idleNumber }}
</span>
<span class="ui item">
<i :class="[productionIcon, 'icon']"></i>
<template v-if="group === 'storage'">
{{ unit.size | idleNumber}} {{ productionUnit}}
</template>
<template v-else>
{{ unit[productionUnit] | idleNumber}} {{ productionUnit}}/s
</template>
</span>
<span :class="['ui', 'right', 'floating', {'red active': exchange.dollars < unitPrice(unit.id) | unitAmount(unit.id) === 0}, 'item']">
Buy {{ unitAmount(unit.id) }} for ${{ unitPrice(unit.id) | idleNumber }}
</span>
</div>
</div>
</div>
......
<template>
<div>
<h2 class="ui center aligned dividing header">
<h2 class="ui center aligned header">
Units
<div class="sub header">Your network produce raw data (bits) and send it to your datacenter.</div>
</h2>
<div class="ui buttons">
<div
class="ui button"
:disabled="step.value === buyAmount"
v-for="step in amountSteps"
@click="$store.commit('settings/set', {setting: 'buyAmount', value: step.value})">
{{ step.value }}
</div>
<div class="ui fluid three item menu">
<a :class="[{active: currentTab === 'network'}, 'item']" @click="show('network')">
Network
</a>
<a :class="[{active: currentTab === 'storage'}, 'item']" @click="show('storage')">
Datacenter
<div :class="['ui', 'label']">
{{ storageUsagePercent }}%
</div>
</a>
<a :class="[{active: currentTab === 'processing'}, 'item']" @click="show('processing')">
Analytics center
</a>
</div>
<div class="ui stackable three column grid">
<popup-wrapper :tag="'div'" class="ui mini labeled button" tabindex="0">
<div :class="['ui', 'mini', {'yellow': isNetworkBottleneck}, 'basic', 'button']">
<i class="feed icon"></i> bps
</div>
<a class="ui basic label">
{{ bps | idleNumber }}
</a>
<popup>
<div class="header">BPS</div>
Your bits per second: how much data you can gather each second.
<template v-if="isNetworkBottleneck">
<div class="ui divider"></div>
<div class="header"><i class="warning sign icon"></i> Bottleneck</div>
Your network is slower than the rest of your infrastructure. Improve it to increase your throughput.
</template>
</popup>
</popup-wrapper>
<popup-wrapper :tag="'div'" class="ui mini labeled button" tabindex="0">
<div :class="['ui', 'mini', {'yellow': isStorageBottleneck}, 'basic', 'button']">
<i class="database icon"></i> storage
</div>
<a class="ui basic label">
{{ bits | idleNumber }} / {{ storage | idleNumber}}
</a>
<popup>
<div class="header">Storage status</div>
Your storage status is made of the following values: "bits in your datacenter"/"total storage size"/"occupation ratio"
<template v-if="isStorageBottleneck">
<div class="ui divider"></div>
<div class="header"><i class="warning sign icon"></i> Bottleneck</div>
Your datacenter cannot handle all your data. Improve it to increase your throughput.
</template>
</popup>
</popup-wrapper>
<popup-wrapper :tag="'div'" class="ui mini labeled button" tabindex="0">
<div :class="['ui', 'mini', {'yellow': isProcessingBottleneck}, 'basic', 'button']">
<i class="microchip icon"></i> pps
</div>
<a class="ui basic label">
{{ pps | idleNumber }}
</a>
<popup>
<div class="header">PPS</div>
Your processing per second: how much data you can process each second.
<template v-if="isProcessingBottleneck">
<div class="ui divider"></div>
<div class="header"><i class="warning sign icon"></i> Bottleneck</div>
There is a backlog of unprocessed data in your datacenter. Buy more processing units to get rid of it.
</template>
</popup>
</popup-wrapper>
<popup-wrapper :tag="'div'" class="ui mini labeled right floated button" tabindex="0">
<div class="ui basic mini button">
<i class="recycle icon"></i> tps
</div>
<a class="ui basic label">
{{ throughput | idleNumber }}
</a>
<popup>
<div class="header">TPS</div>
Your throughput per second: how much real data you actually process each second.
</popup>
</popup-wrapper>
<div class="ui divider"></div>
<div class="ui container">
<unit-buttons
class="column"
v-if="currentTab === 'network'"
:group="'network'"
:subgroup="'collectors'"
:unit-data="collectorsData"
......@@ -25,35 +95,17 @@
>
<template slot="header">
Network
<popup-wrapper v-if="bps === 0 | bps < pps" :tag="'div'" class="ui circular yellow label">
Bottleneck
<popup>
<div class="header">Network bottleneck</div>
Your network is slower than the rest of your infrastructure. Improve it to increase your throughput.
</popup>
</popup-wrapper>
</template>
<template slot="sub">
<div class="ui one mini horizontal statistics">
<popup-wrapper :tag="'div'" class="statistic">
<div class="value">
<i class="feed icon"></i>
{{ bps | idleNumber }} bps
</div>
<popup>
<div class="header">TPS</div>
Your bits per second: how much data you can gather each second
</popup>
</popup-wrapper>
</div>
<div
@click="$store.dispatch('network/bits.collect', 1)"
:disabled="remainingSpace === 0"
class="ui fluid green basic button action-button" tabindex="0">Gather data</div>
class="ui teal mini basic right floated button action-button" tabindex="0">Gather data</div>
</template>
</unit-buttons>
<unit-buttons
class="column"
v-if="currentTab === 'storage'"
:group="'storage'"
:subgroup="'devices'"
:unit-data="devicesData"
......@@ -63,40 +115,11 @@
>
<template slot="header">
Datacenter
<popup-wrapper v-if="storage < bps" :tag="'div'" class="ui circular yellow label">
Bottleneck
<popup>
<div class="header">Storage bottleneck</div>
Your datacenter cannot handle all your data. Improve it to increase your throughput.
</popup>
</popup-wrapper>
</template>
<template slot="sub">
<div class="ui two mini horizontal statistics">
<popup-wrapper :tag="'div'" class="statistic">
<div class="value">
<i class="recycle icon"></i>
{{ throughput | idleNumber }} tps
</div>
<popup>
<div class="header">TPS</div>
Your throughput per second: how much real data you actually process each second
</popup>
</popup-wrapper>
<div class="statistic">
<div class="value">
<i class="database icon"></i> {{ bits | idleNumber }}/{{ storage | idleNumber}} storage
</div>
</div>
</div>
<div ref="progress" class="ui small warning progress">
<div class="bar">
</div>
</div>
</template>
</unit-buttons>
<unit-buttons
class="column"
v-if="currentTab === 'processing'"
:group="'processing'"
:subgroup="'processors'"
:unit-data="processorsData"
......@@ -106,36 +129,12 @@
>
<template slot="header">
Analytics center
<popup-wrapper v-if="pps === throughput && pps < bps" :tag="'div'" class="ui circular yellow label">
Bottleneck
<popup>
<div class="header">Processing bottleneck</div>
There is a backlog of unprocessed data in your datacenter. Buy more processing units to get rid of it.
</popup>
</popup-wrapper>
</template>
<template slot="sub">
<div class="ui two mini horizontal statistics">
<popup-wrapper :tag="'div'" class="statistic">
<div class="value">
<i class="microchip icon"></i>
{{ pps | idleNumber }} pps
</div>
<popup>
<div class="header">PPS</div>
Your processing per second: how much data you can process each second
</popup>
</popup-wrapper>
<div class="statistic">
<div class="value">
<i class="book icon"></i> {{ information | idleNumber}} information
</div>
</div>
</div>
<div
@click="$store.dispatch('processing/process', 1)"
:disabled="bits === 0"
class="ui fluid blue basic button action-button" tabindex="0">Process data</div>
class="ui blue mini basic right floated button action-button" tabindex="0">Process data</div>
</template>
</unit-buttons>
</div>
......@@ -143,8 +142,6 @@
</template>
<script>
import $ from 'jquery'
import { mapState, mapGetters } from 'vuex'
import UnitButtons from './UnitButtons'
import Popup from '@/components/semantic/Popup'
......@@ -162,7 +159,7 @@ export default {
processorsData: data.processing.processors,
devicesData: data.storage.devices,
collectorsData: data.network.collectors,
amountSteps: data.settings.amountSteps
currentTab: 'network'
}
},
computed: {
......@@ -181,11 +178,20 @@ export default {
bps: 'network/bps',
storageUsagePercent: 'storage/usagePercent',
remainingSpace: 'storage/remainingSpace'
})
}),
isNetworkBottleneck () {
return this.bps === 0 | this.bps < this.pps
},
isStorageBottleneck () {
return this.storage < this.bps
},
isProcessingBottleneck () {
return this.pps === this.throughput && this.pps < this.bps
}
},
watch: {
storageUsagePercent: function (v) {
$(this.$refs.progress).progress('update progress', v)
methods: {
show (tab) {
this.currentTab = tab
}
}
}
......
import store from '../store'
import Loop from './loop'
import logger from '@/logging'
import merge from 'lodash.merge'
export default {
start () {
// we check for saved game and load its state if needed
this.loadPreviousSave()
console.log('previous save loaded')
Loop.init()
},
stop () {
Loop.clear()
},
loadPreviousSave () {
let previousSave = this.getPreviousSave()
if (typeof previousSave === 'object') {
let newState = merge({}, store.state, previousSave)
logger.default.info('Loading previous save', newState)
store.replaceState(newState)
} else {
logger.default.info('No previous save was found')
}
},
getPreviousSave () {
const value = window.localStorage.getItem('save')
try {
return value && value !== 'undefined' ? JSON.parse(value) : undefined
} catch (err) {
logger.default.error('Cannot load previous save', err)
return undefined
}
}
}
......@@ -27,6 +27,8 @@ export const actions = {
// bar
existing = Math.min(existing + Math.min(added, storage) - processed, storage)
commit('network/bits.set', existing, {root: true})
dispatch('settings/autosave', null, { root: true })
}
}
......
import logger from '@/logging'
export const state = {
buyAmount: 1
autosaveInterval: 10 * 1000,
buyAmount: 1,
internal: {
lastSave: null
}
}
export const mutations = {
'set' (state, payload) {
state[payload.setting] = payload.value
},
'set.internal' (state, payload) {
state.internal[payload.setting] = payload.value
}
}
export const actions = {}
export const actions = {
'save' ({commit, state, rootState}) {
commit('set.internal', {setting: 'lastSave', value: new Date()})
window.localStorage.setItem('save', JSON.stringify(rootState))
logger.default.info('saving')
},
'autosave' ({dispatch, state}) {
const now = new Date()
let lastSave = new Date(state.internal.lastSave)
let shouldSave = true
if (lastSave) {
shouldSave = now - lastSave > state.autosaveInterval
}
if (shouldSave) {
dispatch('save')
}
}
}
export const getters = {}
......
<template>
<div class="page">
<h1 class="ui center aligned header">IdleBits</h1>
<div>
<div class="ui stackable grid">
<console class="four wide column"></console>
<units class="eight wide column"></units>
<exchange class="four wide column"></exchange>
<units class="ten wide column"></units>
<div class="six wide column">
<console></console>
<div class="ui hidden divider"></div>
<exchange></exchange>
</div>
</div>
</div>
</template>
......
......@@ -17,8 +17,7 @@ describe('App.vue', () => {
it('should render correct contents', () => {
Constructor = Vue.extend(App)
vm = new Constructor({store, router}).$mount()
console.log(vm.$root.$el)
expect(vm.$root.$el.querySelector('h1').textContent)
expect(vm.$root.$el.querySelector('.header.item').textContent.trim())
.to.equal('IdleBits')
})
it('should start loop', () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment