Proof of concept (#6)

* Sample template created

* added findByName functionality for item

* Solve Cors errors and inhibit DefaultExposure

* changed project structure

* Added frontend

* Creation of base template (#1)

* changed base path of REST api and updated frontend api quering
This commit is contained in:
cato
2022-06-02 19:11:46 +02:00
committed by GitHub
parent edaf3c557e
commit f6385b40f6
32 changed files with 7031 additions and 0 deletions

23
frontend/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
frontend/README.md Normal file
View File

@@ -0,0 +1,24 @@
# frontend
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
frontend/babel.config.js Normal file
View File

@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

19
frontend/jsconfig.json Normal file
View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

45
frontend/package.json Normal file
View File

@@ -0,0 +1,45 @@
{
"name": "whattocook",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.27.2",
"core-js": "^3.8.3",
"vue": "^2.6.14",
"vuejs-logger": "^1.5.5"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"vue-template-compiler": "^2.6.14"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
frontend/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<link rel="stylesheet" type="text/css" href="<%= BASE_URL %>style.css">
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

20
frontend/src/Api.js Normal file
View File

@@ -0,0 +1,20 @@
import axios from 'axios'
const SERVER_URL = 'http://localhost:9000'
const instance = axios.create({
baseURL : SERVER_URL,
timeout: 1000
})
export default {
createNew: (name, quantity, unit) => instance.post("/api/v1/items", {name: name, quantity : quantity, unit : unit}),
getAll: () => instance.get('/api/v1/items', {
transformResponse: [function (data) {
return data? JSON.parse(data)._embedded.items : data;
}]
}),
removeForId: (id) => instance.delete('/api/v1/items/'+ id)
}

19
frontend/src/App.vue Normal file
View File

@@ -0,0 +1,19 @@
<template>
<div id="app">
<ItemModel />
</div>
</template>
<script>
import ItemModel from "./components/ItemModel.vue";
export default {
components: {
ItemModel
}
};
</script>
<style>
[v-cloak] {
display: none;
}
</style>

View File

@@ -0,0 +1,125 @@
<template>
<div>
<h1 class="title">Items</h1>
<h1 class="email">{{userEmail}}</h1>
<section class="itemapp">
<div v-if="loading">
<h1 class="loading">Loading...</h1>
</div>
<div v-else>
<header class="header">
<input class="newItemName"
autofocus autocomplete="off"
:placeholder="this.inputPlaceholder"
v-model="newItem"
@keyup.enter="addItem"/>
</header>
<section class="main" v-show="items.length" v-cloak>
<ul class="item-list">
<li v-for="item in items"
class="item"
:key="item.id">
<div class="view">
<label @dblclick="editItem(item)">{{ item.name }} {{ item.quantity }}{{ item.unit }}</label>
<button class="destroy" @click="removeItem(item)"></button>
</div>
</li>
</ul>
</section>
</div>
</section>
</div>
</template>
<script>
import api from '../Api';
const Items = {
name: 'Items',
props: {
activeUser: Object
},
// app initial state
data: function() {
return {
items: [],
newItem: '',
editedItem: null,
loading: true,
error: null,
id: 0
}
},
mounted() {
api.getAll()
.then(response => {
this.$log.debug("Data loaded: ", response.data)
this.items = response.data
})
.catch(error => {
this.$log.debug(error)
this.error = "Failed to load items"
})
.finally(() => this.loading = false)
},
computed: {
userEmail: function () {
return this.activeUser ? this.activeUser.email : ''
},
inputPlaceholder: function () {
return this.activeUser ? this.activeUser.given_name + ', what do you want to add?' : 'What needs to be added'
}
},
methods: {
addItem: function () {
var value = this.newItem && this.newItem.trim()
if (!value) {
return
}
var components = value.split(' ')
api.createNew(components[0],
parseInt(components[1].replace ( /[^\d.]/g, '' )),
components[1].replace(/[0-9]/g, '') === 'ml' ? 'MILLILETERS' : "GRAMMS"
).then( (response) => {
this.$log.debug("New item created:", response);
this.items.push({
id: response.data.id,
name: components[0],
quantity: parseInt(components[1].replace ( /[^\d.]/g, '' )),
unit: components[1].replace(/[0-9]/g, '') === 'MILLILETERS' ? 'ml' : 'g'
})
}).catch((error) => {
this.$log.debug(error);
this.error = "Failed to add item"
});
this.newItem = ''
},
removeItem: function (item) { // notice NOT using "=>" syntax
api.removeForId(item.id).then(() => {
this.$log.debug("Item removed:", item);
this.items.splice(this.items.indexOf(item), 1)
}).catch((error) => {
this.$log.debug(error);
this.error = "Failed to remove item"
})
}
},
directives: {
'item-focus': function (el, binding) {
if (binding.value) {
el.focus()
}
}
}
}
export default Items
</script>

25
frontend/src/main.js Normal file
View File

@@ -0,0 +1,25 @@
import Vue from 'vue'
import App from './App'
Vue.config.productionTip = false
import VueLogger from 'vuejs-logger';
const options = {
isEnabled: true,
logLevel : 'debug',
stringifyArguments : false,
showLogLevel : true,
showMethodName : false,
separator: '|',
showConsoleColors: true
};
Vue.use(VueLogger, options);
/* eslint-disable no-new */
new Vue({
el: '#app',
template: '<App/>',
components: { App }
});

5
frontend/vue.config.js Normal file
View File

@@ -0,0 +1,5 @@
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
runtimeCompiler: true
})

6052
frontend/yarn.lock Normal file

File diff suppressed because it is too large Load Diff