Browse Source

Version 1

master
PrivateGER 3 years ago
commit
bbd39b00d0
Signed by: PrivateGER GPG Key ID: CAE625C962F94C67
  1. 13
      .editorconfig
  2. 45
      .github/workflows/ci.yml
  3. 90
      .gitignore
  4. 4
      .prettierrc
  5. 69
      README.md
  6. 4
      assets/variables.scss
  7. 36
      components/claimedwishes.vue
  8. 84
      components/publicwishlist.vue
  9. 48
      components/wishlist.vue
  10. 9
      constants.js
  11. 111
      layouts/default.vue
  12. 44
      layouts/error.vue
  13. 88
      nuxt.config.js
  14. 27748
      package-lock.json
  15. 28
      package.json
  16. 61
      pages/addwish.vue
  17. 25
      pages/index.vue
  18. 17
      pages/publicwishes.vue
  19. BIN
      static/favicon.ico
  20. BIN
      static/icon.png
  21. BIN
      static/v.png
  22. 1
      static/vuetify-logo.svg
  23. 10
      store/README.md
  24. 5
      store/index.js
  25. 24
      store/users.js
  26. 53
      store/wishes.js

13
.editorconfig

@ -0,0 +1,13 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

45
.github/workflows/ci.yml

@ -0,0 +1,45 @@
name: ci
on:
push:
branches:
- main
- master
pull_request:
branches:
- main
- master
jobs:
ci:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node: [14]
steps:
- name: Checkout 🛎
uses: actions/checkout@master
- name: Setup node env 🏗
uses: actions/setup-node@v2.1.5
with:
node-version: ${{ matrix.node }}
check-latest: true
- name: Cache node_modules 📦
uses: actions/cache@v2.1.4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies 👨🏻‍💻
run: npm ci --prefer-offline --no-audit
- name: Run linter 👀
run: npm run lint

90
.gitignore

@ -0,0 +1,90 @@
# 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
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp

4
.prettierrc

@ -0,0 +1,4 @@
{
"semi": false,
"singleQuote": true
}

69
README.md

@ -0,0 +1,69 @@
# Wunschliste
## Build Setup
```bash
# install dependencies
$ npm install
# serve with hot reload at localhost:3000
$ npm run dev
# build for production and launch server
$ npm run build
$ npm run start
# generate static project
$ npm run generate
```
For detailed explanation on how things work, check out the [documentation](https://nuxtjs.org).
## Special Directories
You can create the following extra directories, some of which have special behaviors. Only `pages` is required; you can delete them if you don't want to use their functionality.
### `assets`
The assets directory contains your uncompiled assets such as Stylus or Sass files, images, or fonts.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/assets).
### `components`
The components directory contains your Vue.js components. Components make up the different parts of your page and can be reused and imported into your pages, layouts and even other components.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/components).
### `layouts`
Layouts are a great help when you want to change the look and feel of your Nuxt app, whether you want to include a sidebar or have distinct layouts for mobile and desktop.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/layouts).
### `pages`
This directory contains your application views and routes. Nuxt will read all the `*.vue` files inside this directory and setup Vue Router automatically.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/get-started/routing).
### `plugins`
The plugins directory contains JavaScript plugins that you want to run before instantiating the root Vue.js Application. This is the place to add Vue plugins and to inject functions or constants. Every time you need to use `Vue.use()`, you should create a file in `plugins/` and add its path to plugins in `nuxt.config.js`.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/plugins).
### `static`
This directory contains your static files. Each file inside this directory is mapped to `/`.
Example: `/static/robots.txt` is mapped as `/robots.txt`.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/static).
### `store`
This directory contains your Vuex store files. Creating a file in this directory automatically activates Vuex.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/store).

4
assets/variables.scss

@ -0,0 +1,4 @@
// Ref: https://github.com/nuxt-community/vuetify-module#customvariables
//
// The variables you want to modify
// $font-size-root: 20px;

36
components/claimedwishes.vue

@ -0,0 +1,36 @@
<template>
<v-card>
<v-card-title>Von dir reservierte Wünsche</v-card-title>
<v-list three-line>
<v-list-item v-for='item in this.$store.state.wishes.claimed' v-bind:key='item.id'>
<v-list-item-content>
<v-list-item-title>
Wunsch von {{ getUser(item.owner_id).name }}
</v-list-item-title>
<v-list-item-subtitle>
{{ item.wish }}
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</v-list>
</v-card>
</template>
<script>
export default {
name: 'claimedwishes',
methods: {
getUser(id) {
let searchedUser = this.$store.state.users.users
.find(user => user.id === id)
console.log(searchedUser)
return searchedUser !== undefined ? searchedUser : {"id": 45, "name": "Lädt..."}
}
}
}
</script>
<style scoped>
</style>

84
components/publicwishlist.vue

@ -0,0 +1,84 @@
<template>
<div>
<v-card>
<v-card-title>Öffentliche Wunschliste</v-card-title>
<v-card-text>
Alle hier sichtbaren Wünsche sind noch frei.<br />
Nutze den orangenen Knopf um einen Wunsch zu reservieren.
</v-card-text>
</v-card>
<v-card class='mt-2' v-for='user in $store.state.users.users' v-bind:key='user.id+user.name'>
<v-card-title>{{ user.name }}</v-card-title>
<div>
<v-list three-line>
<v-list-item v-for='wish in userWishes(user.id)' v-bind:key='wish.id+wish.wish' class='pa-2'>
<v-list-item-content>
<v-list-item-subtitle class='pl-4'>{{ wish.wish }}</v-list-item-subtitle>
</v-list-item-content>
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-btn v-on:click='claimWish(wish.id)' outlined color='secondary'><v-icon>mdi-playlist-check</v-icon></v-btn>
</template>
<span>Reservieren</span>
</v-tooltip>
</v-list-item>
</v-list>
</div>
</v-card>
</div>
</template>
<script>
export default {
name: 'publicwishlist',
methods: {
userWishes(id) {
console.log(this.$store.state.wishes.public)
return this.$store.state.wishes.public.filter((wish) => {
return wish.owner_id === id;
});
},
claimWish(id) {
let response = this.$swal({
title: 'Wunsch reservieren?',
html: 'Du kannst Wünsche <b>NICHT</b> wieder freigeben!<br />Bitte sei dir sicher <b>bevor</b> du reservierst!',
type: 'warning',
showCancelButton: true,
cancelButtonText: "Abbrechen",
confirmButtonText: 'Reservieren',
}).value;
if(!response) {
return;
}
this.$store.dispatch("wishes/claimWish", id).then(() => {
this.$swal({
title: 'Erfolgreich reserviert',
type: 'success',
showConfirmButton: false,
timerProgressBar: true,
timer: 1500,
toast: true,
position: 'bottom-center',
iconColor: 'white'
})
});
}
}
}
</script>
<style scoped>
.justfuckingwrapthegoddamntextthisissodamnstupid {
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
white-space: pre-wrap; /* css-3 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
white-space: -webkit-pre-wrap; /* Newer versions of Chrome/Safari*/
word-break: break-all;
white-space: normal;
}
</style>

48
components/wishlist.vue

@ -0,0 +1,48 @@
<template>
<v-card>
<v-card-title>Deine Wünsche</v-card-title>
<v-card-subtitle>Sollte ein Wunsch abgeschnitten sein, versuch mal dein Gerät zu kippen.</v-card-subtitle>
<div class='pa-4'>
<v-list-item v-for='item in this.$store.state.wishes.personal' :key='item.wish+item.id'>
<v-list-item-content>
<v-list-item-title>{{ item.wish }}</v-list-item-title>
</v-list-item-content>
<v-list-item-action>
<v-btn style='float: right' v-on:click='deleteWish(item.id)' outlined color='primary'><v-icon>mdi-delete</v-icon>Löschen</v-btn>
</v-list-item-action>
</v-list-item>
<hr />
<NuxtLink to='/addwish'>
<v-btn class='mt-4' block color='primary'><v-icon>mdi-plus</v-icon> Wunsch hinzufügen</v-btn>
</NuxtLink>
</div>
</v-card>
</template>
<script>
export default {
name: 'wishlist',
methods: {
deleteWish(id) {
this.$swal({
title: 'Wunsch löschen?',
text: 'Wunsch wirklich löschen?',
type: 'warning',
showCancelButton: true,
cancelButtonText: "Abbrechen",
confirmButtonText: 'Löschen',
}).then((result) => {
if (result.value) {
this.$store.dispatch('wishes/deleteWish', id)
this.$store.dispatch("wishes/fetchPersonal")
}
});
}
}
}
</script>
<style scoped>
</style>

9
constants.js

@ -0,0 +1,9 @@
export const BASE_API_URL = "http://localhost.local:8081/api"
export const getAxiosConfig = (token) => {
return {
headers: {
Authorization: `Bearer ${token}`
}
};
}

111
layouts/default.vue

@ -0,0 +1,111 @@
<template>
<v-app dark>
<v-navigation-drawer
v-model="drawer"
:mini-variant="miniVariant"
fixed
app
>
<v-list>
<v-list-item
v-for="(item, i) in items"
:key="i"
:to="item.to"
router
exact
>
<v-list-item-action>
<v-icon>{{ item.icon }}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title v-text="item.title" />
</v-list-item-content>
</v-list-item>
</v-list>
</v-navigation-drawer>
<v-app-bar
fixed
app
>
<v-app-bar-nav-icon @click.stop="drawer = !drawer" />
</v-app-bar>
<v-main>
<v-container>
<Nuxt />
</v-container>
</v-main>
<v-footer
:absolute="!fixed"
app
>
<span>&copy; {{ new Date().getFullYear() }} CoffeeCup Developments - Letzte Synchronisierung: {{ getLastSync }} </span>
</v-footer>
</v-app>
</template>
<script>
import { getAxiosConfig } from '@/constants'
export default {
data () {
return {
drawer: false,
fixed: false,
items: [
{
icon: 'mdi-apps',
title: 'Meine Wünsche',
to: '/'
},
{
icon: 'mdi-chart-bubble',
title: 'Wunschliste',
to: '/publicwishes'
}
],
miniVariant: false,
title: 'Vuetify.js'
}
},
computed: {
getLastSync() {
let dateObject = new Date(this.$store.state.wishes.lastSync);
return this.formatTime(dateObject);
}
},
methods: {
leadingZero(num) {
return `0${num}`.slice(-2)
},
formatTime(date) {
return [date.getHours(), date.getMinutes(), date.getSeconds()]
.map(this.leadingZero)
.join(':');
}
},
mounted() {
if(this.$route.query.token) {
localStorage.setItem('token', this.$route.query.token)
}
this.$store.dispatch("wishes/fetchPersonal")
this.$store.dispatch("wishes/fetchClaimed")
this.$store.dispatch("wishes/fetchPublic")
this.$store.dispatch("users/fetchUsers")
setInterval(() => {
this.$store.dispatch("wishes/fetchPersonal")
this.$store.dispatch("wishes/fetchClaimed")
this.$store.dispatch("wishes/fetchPublic")
this.$store.dispatch("users/fetchUsers")
}, 15000)
}
}
</script>
<style scoped>
</style>

44
layouts/error.vue

@ -0,0 +1,44 @@
<template>
<v-app dark>
<h1 v-if="error.statusCode === 404">
{{ pageNotFound }}
</h1>
<h1 v-else>
{{ otherError }}
</h1>
<NuxtLink to="/">
Home page
</NuxtLink>
</v-app>
</template>
<script>
export default {
layout: 'empty',
props: {
error: {
type: Object,
default: null
}
},
data () {
return {
pageNotFound: '404 Not Found',
otherError: 'An error occurred'
}
},
head () {
const title =
this.error.statusCode === 404 ? this.pageNotFound : this.otherError
return {
title
}
}
}
</script>
<style scoped>
h1 {
font-size: 20px;
}
</style>

88
nuxt.config.js

@ -0,0 +1,88 @@
import colors from 'vuetify/es5/util/colors'
export default {
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
titleTemplate: '%s - Wunschliste',
title: 'Wunschliste',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' },
{ name: 'format-detection', content: 'telephone=no' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules: [
// https://go.nuxtjs.dev/vuetify
'@nuxtjs/vuetify',
],
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
// https://go.nuxtjs.dev/axios
'@nuxtjs/axios',
// https://go.nuxtjs.dev/pwa
'@nuxtjs/pwa',
'nuxt-sweetalert2',
'nuxt-logrocket',
],
logRocket: {
// configure LogRocket
logRocketId: 'privateger/weihnachten',
devModeAllowed: false,
config: {
//
}
},
// Axios module configuration: https://go.nuxtjs.dev/config-axios
axios: {},
// PWA module configuration: https://go.nuxtjs.dev/pwa
pwa: {
manifest: {
lang: 'en'
},
},
// Vuetify module configuration: https://go.nuxtjs.dev/config-vuetify
vuetify: {
customVariables: ['~/assets/variables.scss'],
theme: {
dark: true,
themes: {
dark: {
primary: colors.blue.darken2,
accent: colors.grey.darken3,
secondary: colors.amber.darken3,
info: colors.teal.lighten1,
warning: colors.amber.base,
error: colors.deepOrange.accent4,
success: colors.green.accent3
}
}
}
},
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
},
}

27748
package-lock.json

File diff suppressed because it is too large

28
package.json

@ -0,0 +1,28 @@
{
"name": "Wunschliste",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate"
},
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
"@nuxtjs/pwa": "^3.3.5",
"core-js": "^3.15.1",
"firebase": "^9.4.1",
"nuxt": "^2.15.7",
"nuxt-logrocket": "^1.5.2",
"nuxt-sweetalert2": "^1.0.0",
"qs": "^6.10.1",
"vuetify": "^2.5.5"
},
"devDependencies": {
"@nuxt/types": "~2.15.0",
"@nuxtjs/vuetify": "^1.12.1",
"eslint-config-prettier": "^8.3.0",
"prettier": "^2.3.2"
}
}

61
pages/addwish.vue

@ -0,0 +1,61 @@
<template>
<v-card>
<v-card-title class="headline">
Wunsch hinzufügen
</v-card-title>
<v-card-subtitle>
<NuxtLink to='/'>Oder zurück</NuxtLink>
</v-card-subtitle>
<v-card-text>
<v-form ref='addform'>
<v-text-field
v-model="wish"
label="Wunsch hier eingeben..."
required
:rules="[rules.required]"
></v-text-field>
<v-btn class='mt-4' v-on:click='submitWish' color='primary'>
<v-icon>mdi-plus</v-icon> Hinzufügen
</v-btn>
</v-form>
</v-card-text>
</v-card>
</template>
<script>
import { BASE_API_URL, getAxiosConfig } from '@/constants'
export default {
name: 'addwish',
data () {
return {
wish: "",
rules: {
required: value => !!value || 'Bitte einen Wunsch eingeben!',
}
}
},
methods: {
submitWish() {
if(!this.$refs.addform.validate()) {
return
}
this.$axios.post(BASE_API_URL + "/wishes/create", {
wish: this.wish,
}, getAxiosConfig(localStorage.getItem("token"))).then(async response => {
await this.$store.dispatch("wishes/fetchPersonal")
await this.$refs.addform.reset()
await this.$router.push('/')
}).catch(error => {
this.$swal("Fehler", "Überprüfe deine Eingaben und versuchs nochmal. Fehler:<br />" + JSON.stringify(error.response.data), "error")
})
}
}
}
</script>
<style scoped>
</style>

25
pages/index.vue

@ -0,0 +1,25 @@
<template>
<v-row justify="center" align="center">
<v-col cols="12" sm="8" md="6">
<v-card>
<v-card-title class="headline">
Willkommen bei der Wunschliste 2021.
</v-card-title>
<v-card-text>
Hier ist dein persönlicher Bereich. Um die Wünsche anderer zu sehen, musst du die Sektion wechseln (oben links).
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" sm="12" md="12">
<wishlist />
</v-col>
<v-col cols="12" sm="12" md="12">
<claimedwishes />
</v-col>
</v-row>
</template>
<script>
export default {
}
</script>

17
pages/publicwishes.vue

@ -0,0 +1,17 @@
<template>
<v-row justify="center" align="center">
<v-col cols="12" sm="12" md="12">
<publicwishlist />
</v-col>
</v-row>
</template>
<script>
export default {
name: 'publicwishes'
}
</script>
<style scoped>
</style>

BIN
static/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
static/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
static/v.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

1
static/vuetify-logo.svg

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>

After

Width:  |  Height:  |  Size: 539 B

10
store/README.md

@ -0,0 +1,10 @@
# STORE
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your Vuex Store files.
Vuex Store option is implemented in the Nuxt.js framework.
Creating a file in this directory automatically activates the option in the framework.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store).

5
store/index.js

@ -0,0 +1,5 @@
export const state = () => ({
})
export const mutations = {
}

24
store/users.js

@ -0,0 +1,24 @@
import { BASE_API_URL, getAxiosConfig } from '@/constants'
export const state = () => ({
users: [],
lastSync: 0
})
export const mutations = {
setUsers(state, users) {
state.users = users
},
}
export const getters = {
}
export const actions = {
fetchUsers({ commit }) {
return this.$axios.$get(BASE_API_URL + '/users', getAxiosConfig(localStorage.getItem("token"))).then(users => {
commit('setUsers', users)
})
},
}

53
store/wishes.js

@ -0,0 +1,53 @@
import { BASE_API_URL, getAxiosConfig } from '@/constants'
export const state = () => ({
personal: [],
public: [],
claimed: [],
lastSync: 0
})
export const mutations = {
setPersonal(state, wishes) {
state.personal = wishes
state.lastSync = Date.now()
},
setPublic(state, wishes) {
state.public = wishes
state.lastSync = Date.now()
},
setClaimed(state, wishes) {
state.claimed = wishes
state.lastSync = Date.now()
}
}
export const actions = {
fetchPersonal({ commit }) {
return this.$axios.$get(BASE_API_URL + '/wishes/me', getAxiosConfig(localStorage.getItem("token"))).then(wishes => {
commit('setPersonal', wishes)
})
},
fetchPublic({ commit }) {
return this.$axios.$get(BASE_API_URL + '/wishes/list', getAxiosConfig(localStorage.getItem("token"))).then(wishes => {
commit('setPublic', wishes)
})
},
fetchClaimed({ commit }) {
return this.$axios.$get(BASE_API_URL + '/wishes/claimed', getAxiosConfig(localStorage.getItem("token"))).then(wishes => {
commit('setClaimed', wishes)
})
},
deleteWish({ commit, state }, wishid) {
return this.$axios.$delete(BASE_API_URL + '/wishes/' + wishid, getAxiosConfig(localStorage.getItem("token"))).then(() => {
commit('setPersonal', state.personal.filter(w => w.id !== wishid))
})
},
claimWish({ commit, state }, wishid) {
return this.$axios.$post(BASE_API_URL + '/wishes/' + wishid + '/claim', {}, getAxiosConfig(localStorage.getItem("token"))).then(async () => {
commit('setPublic', state.public.filter(w => w.id !== wishid))
await this.dispatch('wishes/fetchPublic')
await this.dispatch('wishes/fetchClaimed')
})
},
}
Loading…
Cancel
Save