Update of the whole app
This commit is contained in:
parent
a5ed530081
commit
b57bdb6b7b
@ -3,7 +3,6 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>HungerGames</title>
|
<title>HungerGames</title>
|
||||||
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
2620
package-lock.json
generated
2620
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
51
package.json
51
package.json
@ -16,75 +16,60 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es6-promise": "^4.1.1",
|
"es6-promise": "^4.1.1",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"vue": "^2.5.2",
|
"material-design-icons": "^3.0.1",
|
||||||
|
"vue": "^2.5.8",
|
||||||
"vue-router": "^3.0.1",
|
"vue-router": "^3.0.1",
|
||||||
"vuetify": "^0.16.9",
|
"vuetify": "^0.17.2",
|
||||||
"vuex": "^3.0.0"
|
"vuex": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^7.1.6",
|
"autoprefixer": "^7.1.6",
|
||||||
"babel-core": "^6.22.1",
|
"babel-core": "^6.22.1",
|
||||||
"babel-eslint": "^8.0.1",
|
"babel-eslint": "^8.0.2",
|
||||||
"babel-loader": "^7.1.1",
|
"babel-loader": "^7.1.1",
|
||||||
"babel-plugin-istanbul": "^4.1.1",
|
"babel-plugin-istanbul": "^4.1.1",
|
||||||
"babel-plugin-transform-runtime": "^6.22.0",
|
"babel-plugin-transform-runtime": "^6.22.0",
|
||||||
"babel-preset-env": "^1.6.1",
|
"babel-preset-env": "^1.6.1",
|
||||||
"babel-preset-stage-2": "^6.22.0",
|
"babel-preset-stage-2": "^6.22.0",
|
||||||
"babel-register": "^6.22.0",
|
"babel-register": "^6.22.0",
|
||||||
"chai": "^4.1.2",
|
"connect-history-api-fallback": "^1.5.0",
|
||||||
"chalk": "^2.2.0",
|
"copy-webpack-plugin": "^4.2.1",
|
||||||
"chromedriver": "^2.33.2",
|
"cross-env": "^5.1.1",
|
||||||
"connect-history-api-fallback": "^1.4.0",
|
|
||||||
"copy-webpack-plugin": "^4.2.0",
|
|
||||||
"cross-env": "^5.1.0",
|
|
||||||
"cross-spawn": "^5.0.1",
|
"cross-spawn": "^5.0.1",
|
||||||
"css-loader": "^0.28.0",
|
"css-loader": "^0.28.0",
|
||||||
"cssnano": "^3.10.0",
|
"cssnano": "^3.10.0",
|
||||||
"eslint": "^4.9.0",
|
"eslint": "^4.11.0",
|
||||||
"eslint-config-airbnb-base": "^12.1.0",
|
"eslint-config-airbnb-base": "^12.1.0",
|
||||||
"eslint-friendly-formatter": "^3.0.0",
|
"eslint-friendly-formatter": "^3.0.0",
|
||||||
"eslint-import-resolver-webpack": "^0.8.1",
|
"eslint-import-resolver-webpack": "^0.8.1",
|
||||||
"eslint-loader": "^1.7.1",
|
"eslint-loader": "^1.7.1",
|
||||||
"eslint-plugin-html": "^3.0.0",
|
"eslint-plugin-html": "^4.0.0",
|
||||||
"eslint-plugin-import": "^2.8.0",
|
"eslint-plugin-import": "^2.8.0",
|
||||||
"eventsource-polyfill": "^0.9.6",
|
"eventsource-polyfill": "^0.9.6",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
"extract-text-webpack-plugin": "^3.0.1",
|
"extract-text-webpack-plugin": "^3.0.2",
|
||||||
"file-loader": "^1.1.5",
|
"file-loader": "^1.1.5",
|
||||||
"friendly-errors-webpack-plugin": "^1.1.3",
|
"friendly-errors-webpack-plugin": "^1.1.3",
|
||||||
"html-webpack-plugin": "^2.28.0",
|
"html-webpack-plugin": "^2.28.0",
|
||||||
"http-proxy-middleware": "^0.17.3",
|
"http-proxy-middleware": "^0.17.3",
|
||||||
"inject-loader": "^3.0.0",
|
"inject-loader": "^3.0.0",
|
||||||
"karma": "^1.4.1",
|
|
||||||
"karma-coverage": "^1.1.1",
|
|
||||||
"karma-mocha": "^1.3.0",
|
|
||||||
"karma-phantomjs-launcher": "^1.0.2",
|
|
||||||
"karma-phantomjs-shim": "^1.4.0",
|
|
||||||
"karma-sinon-chai": "^1.3.1",
|
|
||||||
"karma-sourcemap-loader": "^0.3.7",
|
|
||||||
"karma-spec-reporter": "0.0.31",
|
|
||||||
"karma-webpack": "^2.0.5",
|
|
||||||
"mocha": "^4.0.1",
|
|
||||||
"nightwatch": "^0.9.12",
|
|
||||||
"opn": "^5.1.0",
|
"opn": "^5.1.0",
|
||||||
"optimize-css-assets-webpack-plugin": "^3.2.0",
|
"optimize-css-assets-webpack-plugin": "^3.2.0",
|
||||||
"ora": "^1.2.0",
|
"ora": "^1.2.0",
|
||||||
"phantomjs-prebuilt": "^2.1.14",
|
"phantomjs-prebuilt": "^2.1.16",
|
||||||
"rimraf": "^2.6.0",
|
"rimraf": "^2.6.0",
|
||||||
"selenium-server": "^3.6.0",
|
"selenium-server": "^3.7.1",
|
||||||
"semver": "^5.3.0",
|
"semver": "^5.3.0",
|
||||||
"shelljs": "^0.7.6",
|
"shelljs": "^0.7.8",
|
||||||
"sinon": "^4.0.1",
|
|
||||||
"sinon-chai": "^2.14.0",
|
|
||||||
"url-loader": "^0.6.2",
|
"url-loader": "^0.6.2",
|
||||||
"vue-loader": "^13.3.0",
|
"vue-loader": "^13.5.0",
|
||||||
"vue-style-loader": "^3.0.1",
|
"vue-style-loader": "^3.0.1",
|
||||||
"vue-template-compiler": "^2.5.2",
|
"vue-template-compiler": "^2.5.8",
|
||||||
"webpack": "^3.8.1",
|
"webpack": "^3.8.1",
|
||||||
"webpack-bundle-analyzer": "^2.2.1",
|
"webpack-bundle-analyzer": "^2.9.1",
|
||||||
"webpack-dev-middleware": "^1.10.0",
|
"webpack-dev-middleware": "^1.10.0",
|
||||||
"webpack-hot-middleware": "^2.20.0",
|
"webpack-hot-middleware": "^2.20.0",
|
||||||
"webpack-merge": "^4.1.0"
|
"webpack-merge": "^4.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 4.0.0",
|
"node": ">= 4.0.0",
|
||||||
|
18
src/App.vue
18
src/App.vue
@ -1,19 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-app toolbar>
|
<v-app>
|
||||||
<NavigationDrawer v-model="isDrawerVisible"/>
|
<NavigationDrawer v-model="isDrawerVisible"/>
|
||||||
<v-toolbar color="indigo" dark fixed app clipped-left>
|
<v-toolbar color="indigo" dark fixed app clipped-left>
|
||||||
<v-toolbar-side-icon @click.stop="showHideDrawer"></v-toolbar-side-icon>
|
<v-toolbar-side-icon @click.stop="showHideDrawer"></v-toolbar-side-icon>
|
||||||
<v-toolbar-title>HungerGames</v-toolbar-title>
|
<v-toolbar-title>HungerGames</v-toolbar-title>
|
||||||
<v-spacer></v-spacer>
|
|
||||||
<v-btn icon>
|
|
||||||
<v-icon>skip_next</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</v-toolbar>
|
</v-toolbar>
|
||||||
<main>
|
<v-content class="max-width">
|
||||||
<v-content>
|
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</v-content>
|
</v-content>
|
||||||
</main>
|
|
||||||
</v-app>
|
</v-app>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -36,3 +30,11 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.max-width {
|
||||||
|
max-width: 1000px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -1,15 +1,33 @@
|
|||||||
require('es6-promise').polyfill();
|
require('es6-promise').polyfill();
|
||||||
require('isomorphic-fetch');
|
require('isomorphic-fetch');
|
||||||
|
|
||||||
export const BASEURL = 'https://world.openfoodfacts.org/';
|
export const BASE_URL = 'https://world.openfoodfacts.org';
|
||||||
|
export const BASE_FETCH_PARAMS = {
|
||||||
|
/*
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Basic ${btoa('off:off')}`,
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
export const USER_ID = '';
|
||||||
|
export const PASSWORD = '';
|
||||||
|
|
||||||
function _fetchFromOFFApi(filters) {
|
function _fetchFromOFFApi(filters) {
|
||||||
let url = BASEURL;
|
let url = BASE_URL;
|
||||||
filters.forEach((filter) => {
|
filters.forEach((filter) => {
|
||||||
url += `/${filter}`;
|
url += `/${filter}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
return fetch(`${url}.json`);
|
return fetch(`${url}.json`, BASE_FETCH_PARAMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sendToOFFApi(barcode, fields) {
|
||||||
|
let url = `${BASE_URL}/cgi/product_jqmp2.pl?code=${barcode}&user_id=${USER_ID}&password=${PASSWORD}`;
|
||||||
|
Object.keys(fields).forEach((field) => {
|
||||||
|
url += `&${field}=${fields[field]}`;
|
||||||
|
});
|
||||||
|
return fetch(url, BASE_FETCH_PARAMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
function missingCategories() {
|
function missingCategories() {
|
||||||
@ -60,8 +78,9 @@ function missingCategories() {
|
|||||||
|
|
||||||
|
|
||||||
function updateCategories(productId, categories) {
|
function updateCategories(productId, categories) {
|
||||||
// TODO
|
return _sendToOFFApi(productId, {
|
||||||
console.log(productId, categories);
|
categories: categories.join(','),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
24
src/components/About.vue
Normal file
24
src/components/About.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<template>
|
||||||
|
<v-container fluid>
|
||||||
|
<v-layout row>
|
||||||
|
<v-flex xs12 class="text-xs-center">
|
||||||
|
<h2>About</h2>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
<v-layout row>
|
||||||
|
<v-flex xs12>
|
||||||
|
<p>
|
||||||
|
This app is a quest-based app to help you update and
|
||||||
|
complete <a href="http://openfoodfacts.org/">OpenFoodFacts</a> data.
|
||||||
|
</p>
|
||||||
|
<p>This app is a free software, released under MIT license.</p>
|
||||||
|
<p>OpenFoodFacts database is a free database licensed under <a href="https://world.openfoodfacts.org/legal">Open Database License</a>.</p>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
};
|
||||||
|
</script>
|
@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-navigation-drawer
|
<v-navigation-drawer
|
||||||
persistent
|
fixed
|
||||||
clipped
|
clipped
|
||||||
app
|
app
|
||||||
enable-resize-watcher
|
disable-resize-watcher
|
||||||
v-model="isActive"
|
v-model="isActive"
|
||||||
>
|
>
|
||||||
<v-list>
|
<v-list>
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-container fluid grid-list-lg class="text-xs-center">
|
|
||||||
<v-layout row mb-3 align-center>
|
|
||||||
<v-flex xs5>
|
|
||||||
<v-dialog v-model="dialog" lazy absolute>
|
|
||||||
<v-avatar
|
|
||||||
size="100%"
|
|
||||||
class="pointable"
|
|
||||||
slot="activator"
|
|
||||||
>
|
|
||||||
<!-- TODO: Should be closable by ESC -->
|
|
||||||
<img class="icon" :src="data.icon" />
|
|
||||||
</v-avatar>
|
|
||||||
<v-card>
|
|
||||||
<v-card-text>
|
|
||||||
<!-- TODO: Preload images -->
|
|
||||||
<img style="width: 100%;" :src="data.icon" />
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</v-dialog>
|
|
||||||
</v-flex>
|
|
||||||
<v-flex xs7 class="text-xs-center">
|
|
||||||
<div>
|
|
||||||
<div class="headline">{{ data.brands || 'Unknown' }}</div>
|
|
||||||
<h2 class="title">{{ data.name || 'Unkown' }}</h2>
|
|
||||||
</div>
|
|
||||||
</v-flex>
|
|
||||||
</v-layout>
|
|
||||||
<v-divider />
|
|
||||||
<v-layout row>
|
|
||||||
<v-flex xs12>
|
|
||||||
<h2 class="title">Select all correct categories:</h2>
|
|
||||||
</v-flex>
|
|
||||||
</v-layout>
|
|
||||||
<v-layout row wrap>
|
|
||||||
<v-btn
|
|
||||||
v-for="(category, key) in data.predictedCategories" :key="key"
|
|
||||||
round dark :class="{ green: category.isOK, red: !category.isOK }"
|
|
||||||
@click.stop="data.predictedCategories[key].isOK = !data.predictedCategories[key].isOK"
|
|
||||||
>
|
|
||||||
{{ category.name }}
|
|
||||||
</v-btn>
|
|
||||||
</v-layout>
|
|
||||||
<v-layout row>
|
|
||||||
<v-flex xs12>
|
|
||||||
<v-btn @click="handleSubmit()">Submit</v-btn>
|
|
||||||
</v-flex>
|
|
||||||
</v-layout>
|
|
||||||
</v-container>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
data: Object,
|
|
||||||
onSubmit: Function,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
dialog: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleSubmit() {
|
|
||||||
const okCategories = [];
|
|
||||||
Object.keys(this.data.predictedCategories).forEach((key) => {
|
|
||||||
if (this.data.predictedCategories[key].isOk) {
|
|
||||||
okCategories.push(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.onSubmit(okCategories);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.pointable {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pointable:hover {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
66
src/components/Quests/QuestMissingCategories/index.vue
Normal file
66
src/components/Quests/QuestMissingCategories/index.vue
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<template>
|
||||||
|
<v-container fluid grid-list-lg class="text-xs-center">
|
||||||
|
<v-layout row mb-3 align-center>
|
||||||
|
<v-flex xs5>
|
||||||
|
<ZoomableImage :url="questData.icon" />
|
||||||
|
</v-flex>
|
||||||
|
<v-flex xs7 class="text-xs-center">
|
||||||
|
<div>
|
||||||
|
<div class="headline">{{ questData.brands || 'Unknown' }}</div>
|
||||||
|
<h2 class="title">{{ questData.name || 'Unkown' }}</h2>
|
||||||
|
</div>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
<v-divider />
|
||||||
|
<v-layout row>
|
||||||
|
<v-flex xs12>
|
||||||
|
<h2 class="title">Select all correct categories:</h2>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
<v-layout row wrap>
|
||||||
|
<v-btn
|
||||||
|
v-for="(category, key) in questData.predictedCategories" :key="key"
|
||||||
|
round dark :class="{ green: category.isOk, red: !category.isOk }"
|
||||||
|
@click.stop="handleClick(key)"
|
||||||
|
>
|
||||||
|
{{ category.name }}
|
||||||
|
</v-btn>
|
||||||
|
</v-layout>
|
||||||
|
<v-layout row>
|
||||||
|
<v-flex xs12>
|
||||||
|
<v-btn @click="handleSubmit()">Submit</v-btn>
|
||||||
|
or <a @click="onSkip()">Skip</a>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ZoomableImage from '@/components/ZoomableImage';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ZoomableImage,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
questData: Object,
|
||||||
|
onSubmit: Function,
|
||||||
|
onSkip: Function,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSubmit() {
|
||||||
|
const okCategories = [];
|
||||||
|
Object.keys(this.questData.predictedCategories).forEach((key) => {
|
||||||
|
if (this.questData.predictedCategories[key].isOk) {
|
||||||
|
okCategories.push(this.questData.predictedCategories[key].name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.onSubmit(okCategories);
|
||||||
|
},
|
||||||
|
handleClick(key) {
|
||||||
|
this.questData.predictedCategories[key].isOk =
|
||||||
|
!this.questData.predictedCategories[key].isOk;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
57
src/components/Quests/index.vue
Normal file
57
src/components/Quests/index.vue
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<v-layout row v-if="isLoading">
|
||||||
|
<v-flex xs12>
|
||||||
|
<p>Loading quests…</p>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
<template v-else>
|
||||||
|
<QuestMissingCategories v-if="questData" :questData="questData" :onSubmit="validateQuest" :onSkip="skipQuest" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import QuestMissingCategories from '@/components/Quests/QuestMissingCategories/index';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
QuestMissingCategories,
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.$store.dispatch('preloadQuests').then(() => {
|
||||||
|
this.isLoading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
validateQuest(solution) {
|
||||||
|
this.$store.dispatch('validateQuest', {
|
||||||
|
type: this.questData.type,
|
||||||
|
id: this.questData.id,
|
||||||
|
solution,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
skipQuest() {
|
||||||
|
this.$store.dispatch('skipQuest', {
|
||||||
|
type: this.questData.type,
|
||||||
|
id: this.questData.id,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
questData() {
|
||||||
|
return this.$store.getters.popQuest;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
72
src/components/ZoomableImage.vue
Normal file
72
src/components/ZoomableImage.vue
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog v-model="dialog">
|
||||||
|
<v-avatar
|
||||||
|
size="100%"
|
||||||
|
class="pointable"
|
||||||
|
slot="activator"
|
||||||
|
>
|
||||||
|
<!-- TODO: Should be closable by ESC -->
|
||||||
|
<img class="icon" :src="url" />
|
||||||
|
</v-avatar>
|
||||||
|
<v-card>
|
||||||
|
<v-card-text>
|
||||||
|
<!-- TODO: Preload images -->
|
||||||
|
<img class="zoomed-image" :src="url" />
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
url: String,
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.addEventListener('keydown', this.handleEscKey);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
document.removeEventListener('keydown', this.handleEscKey);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleEscKey(e) {
|
||||||
|
if (
|
||||||
|
this.dialog && (
|
||||||
|
e.key === 'Escape' ||
|
||||||
|
e.key === 'Esc' ||
|
||||||
|
e.keyCode === 27
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.dialog = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialog: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pointable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pointable:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zoomed-image {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
@ -3,6 +3,7 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Vuetify from 'vuetify';
|
import Vuetify from 'vuetify';
|
||||||
import 'vuetify/dist/vuetify.min.css';
|
import 'vuetify/dist/vuetify.min.css';
|
||||||
|
import 'material-design-icons/iconfont/material-icons.css';
|
||||||
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Router from 'vue-router';
|
import Router from 'vue-router';
|
||||||
import About from '@/views/About';
|
|
||||||
import Quest from '@/views/Quest';
|
import About from '@/components/About';
|
||||||
|
import Quests from '@/components/Quests';
|
||||||
|
|
||||||
Vue.use(Router);
|
Vue.use(Router);
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ export default new Router({
|
|||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
component: Quest,
|
component: Quests,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/about',
|
path: '/about',
|
||||||
|
@ -4,9 +4,9 @@ import quests from './quests';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
preloadQuests({ commit }) {
|
preloadQuests({ commit }) {
|
||||||
commit(types.IS_LOADING_QUESTS);
|
const apiRequests = [];
|
||||||
|
|
||||||
Object.keys(quests).forEach((quest) => {
|
Object.keys(quests).forEach((quest) => {
|
||||||
|
apiRequests.push(
|
||||||
quests[quest]().then(
|
quests[quest]().then(
|
||||||
items => commit(
|
items => commit(
|
||||||
types.STORE_QUESTS_ITEMS,
|
types.STORE_QUESTS_ITEMS,
|
||||||
@ -15,14 +15,17 @@ export default {
|
|||||||
items,
|
items,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return Promise.all(apiRequests);
|
||||||
},
|
},
|
||||||
|
|
||||||
validateQuest({ commit }, { type, id, solution }) {
|
validateQuest({ commit }, { type, id, solution }) {
|
||||||
if (type === 'missingCategories') {
|
if (type === 'missingCategories') {
|
||||||
updateCategories(id, solution).then(() => commit(
|
updateCategories(id, solution).then(() => commit(
|
||||||
types.VALIDATE_QUEST,
|
types.REMOVE_QUEST_ITEM,
|
||||||
{
|
{
|
||||||
type,
|
type,
|
||||||
id,
|
id,
|
||||||
@ -30,4 +33,14 @@ export default {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
skipQuest({ commit }, { type, id }) {
|
||||||
|
commit(
|
||||||
|
types.REMOVE_QUEST_ITEM,
|
||||||
|
{
|
||||||
|
type,
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
export const IS_LOADING_QUESTS = 'IS_LOADING_QUESTS';
|
|
||||||
export const STORE_QUESTS_ITEMS = 'STORE_QUESTS_ITEMS';
|
export const STORE_QUESTS_ITEMS = 'STORE_QUESTS_ITEMS';
|
||||||
export const VALIDATE_QUEST = 'VALIDATE_QUEST';
|
export const REMOVE_QUEST_ITEM = 'REMOVE_QUEST_ITEM';
|
||||||
|
@ -3,20 +3,19 @@ import Vue from 'vue';
|
|||||||
import * as types from './mutations-types';
|
import * as types from './mutations-types';
|
||||||
|
|
||||||
export const initialState = {
|
export const initialState = {
|
||||||
isLoading: false,
|
|
||||||
questsItems: {},
|
questsItems: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mutations = {
|
export const mutations = {
|
||||||
[types.IS_LOADING_QUESTS](state) {
|
|
||||||
state.isLoading = true;
|
|
||||||
},
|
|
||||||
[types.STORE_QUESTS_ITEMS](state, { type, items }) {
|
[types.STORE_QUESTS_ITEMS](state, { type, items }) {
|
||||||
Vue.set(state.questsItems, type, items);
|
Vue.set(state.questsItems, type, items);
|
||||||
},
|
},
|
||||||
[types.VALIDATE_QUEST](state, { id, type }) {
|
[types.REMOVE_QUEST_ITEM](state, { type, id }) {
|
||||||
const items = state.questsItems[type];
|
const items = state.questsItems[type];
|
||||||
delete items[id];
|
const index = items.findIndex(item => item.id === id);
|
||||||
|
if (index > -1) {
|
||||||
|
items.splice(index, 1);
|
||||||
|
}
|
||||||
Vue.set(state.questsItems, type, items);
|
Vue.set(state.questsItems, type, items);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-container fluid>
|
|
||||||
<v-layout row>
|
|
||||||
<v-flex xs12>
|
|
||||||
<h2>About</h2>
|
|
||||||
</v-flex>
|
|
||||||
</v-layout>
|
|
||||||
<v-layout row>
|
|
||||||
<v-flex xs12>
|
|
||||||
<p>TODO</p>
|
|
||||||
</v-flex>
|
|
||||||
</v-layout>
|
|
||||||
</v-container>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -1,35 +0,0 @@
|
|||||||
<template>
|
|
||||||
<QuestMissingCategories v-if="questData" :data="questData" :onSubmit="validateQuest" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import QuestMissingCategories from '@/components/QuestMissingCategories/index';
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Changing route should not flash old product
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
QuestMissingCategories,
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.fetchData();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fetchData() {
|
|
||||||
this.$store.dispatch('preloadQuests');
|
|
||||||
},
|
|
||||||
validateQuest(solution) {
|
|
||||||
this.$store.dispatch('validateQuest', {
|
|
||||||
type: this.questData.type,
|
|
||||||
id: this.questData.id,
|
|
||||||
solution,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
questData() {
|
|
||||||
return this.$store.getters.popQuest;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -1,24 +0,0 @@
|
|||||||
// A custom Nightwatch assertion.
|
|
||||||
// the name of the method is the filename.
|
|
||||||
// can be used in tests like this:
|
|
||||||
//
|
|
||||||
// browser.assert.elementCount(selector, count)
|
|
||||||
//
|
|
||||||
// for how to write custom assertions see
|
|
||||||
// http://nightwatchjs.org/guide#writing-custom-assertions
|
|
||||||
exports.assertion = function (selector, count) {
|
|
||||||
this.message = `Testing if element <${selector}> has count: ${count}`;
|
|
||||||
this.expected = count;
|
|
||||||
this.pass = function (val) {
|
|
||||||
return val === this.expected;
|
|
||||||
};
|
|
||||||
this.value = function (res) {
|
|
||||||
return res.value;
|
|
||||||
};
|
|
||||||
this.command = function (cb) {
|
|
||||||
const self = this;
|
|
||||||
return this.api.execute(selector => document.querySelectorAll(selector).length, [selector], (res) => {
|
|
||||||
cb.call(self, res);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,46 +0,0 @@
|
|||||||
require('babel-register');
|
|
||||||
const config = require('../../config');
|
|
||||||
|
|
||||||
// http://nightwatchjs.org/gettingstarted#settings-file
|
|
||||||
module.exports = {
|
|
||||||
src_folders: ['test/e2e/specs'],
|
|
||||||
output_folder: 'test/e2e/reports',
|
|
||||||
custom_assertions_path: ['test/e2e/custom-assertions'],
|
|
||||||
|
|
||||||
selenium: {
|
|
||||||
start_process: true,
|
|
||||||
server_path: require('selenium-server').path,
|
|
||||||
host: '127.0.0.1',
|
|
||||||
port: 4444,
|
|
||||||
cli_args: {
|
|
||||||
'webdriver.chrome.driver': require('chromedriver').path,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
test_settings: {
|
|
||||||
default: {
|
|
||||||
selenium_port: 4444,
|
|
||||||
selenium_host: 'localhost',
|
|
||||||
silent: true,
|
|
||||||
globals: {
|
|
||||||
devServerURL: `http://localhost:${process.env.PORT || config.dev.port}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
chrome: {
|
|
||||||
desiredCapabilities: {
|
|
||||||
browserName: 'chrome',
|
|
||||||
javascriptEnabled: true,
|
|
||||||
acceptSslCerts: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
firefox: {
|
|
||||||
desiredCapabilities: {
|
|
||||||
browserName: 'firefox',
|
|
||||||
javascriptEnabled: true,
|
|
||||||
acceptSslCerts: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,33 +0,0 @@
|
|||||||
// 1. start the dev server using production config
|
|
||||||
process.env.NODE_ENV = 'testing';
|
|
||||||
const server = require('../../build/dev-server.js');
|
|
||||||
|
|
||||||
server.ready.then(() => {
|
|
||||||
// 2. run the nightwatch test suite against it
|
|
||||||
// to run in additional browsers:
|
|
||||||
// 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings"
|
|
||||||
// 2. add it to the --env flag below
|
|
||||||
// or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
|
|
||||||
// For more information on Nightwatch's config file, see
|
|
||||||
// http://nightwatchjs.org/guide#settings-file
|
|
||||||
let opts = process.argv.slice(2);
|
|
||||||
if (opts.indexOf('--config') === -1) {
|
|
||||||
opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']);
|
|
||||||
}
|
|
||||||
if (opts.indexOf('--env') === -1) {
|
|
||||||
opts = opts.concat(['--env', 'chrome']);
|
|
||||||
}
|
|
||||||
|
|
||||||
const spawn = require('cross-spawn');
|
|
||||||
const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' });
|
|
||||||
|
|
||||||
runner.on('exit', (code) => {
|
|
||||||
server.close();
|
|
||||||
process.exit(code);
|
|
||||||
});
|
|
||||||
|
|
||||||
runner.on('error', (err) => {
|
|
||||||
server.close();
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,19 +0,0 @@
|
|||||||
// For authoring Nightwatch tests, see
|
|
||||||
// http://nightwatchjs.org/guide#usage
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
'default e2e tests': function test(browser) {
|
|
||||||
// automatically uses dev Server port from /config.index.js
|
|
||||||
// default: http://localhost:8080
|
|
||||||
// see nightwatch.conf.js
|
|
||||||
const devServer = browser.globals.devServerURL;
|
|
||||||
|
|
||||||
browser
|
|
||||||
.url(devServer)
|
|
||||||
.waitForElementVisible('#app', 5000)
|
|
||||||
.assert.elementPresent('.hello')
|
|
||||||
.assert.containsText('h1', 'Welcome to Your Vue.js App')
|
|
||||||
.assert.elementCount('img', 1)
|
|
||||||
.end();
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"env": {
|
|
||||||
"mocha": true
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"expect": true,
|
|
||||||
"sinon": true
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
|
||||||
|
|
||||||
// require all test files (files that ends with .spec.js)
|
|
||||||
const testsContext = require.context('./specs', true, /\.spec$/);
|
|
||||||
testsContext.keys().forEach(testsContext);
|
|
||||||
|
|
||||||
// require all src files except main.js for coverage.
|
|
||||||
// you can also change this to match only the subset of files that
|
|
||||||
// you want coverage for.
|
|
||||||
const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/);
|
|
||||||
srcContext.keys().forEach(srcContext);
|
|
@ -1,33 +0,0 @@
|
|||||||
// This is a karma config file. For more details see
|
|
||||||
// http://karma-runner.github.io/0.13/config/configuration-file.html
|
|
||||||
// we are also using it with karma-webpack
|
|
||||||
// https://github.com/webpack/karma-webpack
|
|
||||||
|
|
||||||
const webpackConfig = require('../../build/webpack.test.conf');
|
|
||||||
|
|
||||||
module.exports = function (config) {
|
|
||||||
config.set({
|
|
||||||
// to run in additional browsers:
|
|
||||||
// 1. install corresponding karma launcher
|
|
||||||
// http://karma-runner.github.io/0.13/config/browsers.html
|
|
||||||
// 2. add it to the `browsers` array below.
|
|
||||||
browsers: ['PhantomJS'],
|
|
||||||
frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
|
|
||||||
reporters: ['spec', 'coverage'],
|
|
||||||
files: ['./index.js'],
|
|
||||||
preprocessors: {
|
|
||||||
'./index.js': ['webpack', 'sourcemap'],
|
|
||||||
},
|
|
||||||
webpack: webpackConfig,
|
|
||||||
webpackMiddleware: {
|
|
||||||
noInfo: true,
|
|
||||||
},
|
|
||||||
coverageReporter: {
|
|
||||||
dir: './coverage',
|
|
||||||
reporters: [
|
|
||||||
{ type: 'lcov', subdir: '.' },
|
|
||||||
{ type: 'text-summary' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,11 +0,0 @@
|
|||||||
import Vue from 'vue';
|
|
||||||
import Hello from '@/components/Hello';
|
|
||||||
|
|
||||||
describe('Hello.vue', () => {
|
|
||||||
it('should render correct contents', () => {
|
|
||||||
const Constructor = Vue.extend(Hello);
|
|
||||||
const vm = new Constructor().$mount();
|
|
||||||
expect(vm.$el.querySelector('.hello h1').textContent)
|
|
||||||
.to.equal('Welcome to Your Vue.js App');
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user