Basic UI rework
@ -5,3 +5,4 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
.eslintrc.cjs
|
.eslintrc.cjs
|
||||||
/quasar.config.*.temporary.compiled*
|
/quasar.config.*.temporary.compiled*
|
||||||
|
/pocketbase
|
||||||
|
@ -26,14 +26,14 @@ module.exports = {
|
|||||||
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
||||||
|
|
||||||
'standard'
|
'standard'
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
|
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
|
||||||
// required to lint *.vue files
|
// required to lint *.vue files
|
||||||
'vue',
|
'vue',
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
globals: {
|
globals: {
|
||||||
@ -51,7 +51,7 @@ module.exports = {
|
|||||||
|
|
||||||
// add your custom rules here
|
// add your custom rules here
|
||||||
rules: {
|
rules: {
|
||||||
|
|
||||||
// allow async-await
|
// allow async-await
|
||||||
'generator-star-spacing': 'off',
|
'generator-star-spacing': 'off',
|
||||||
// allow paren-less arrow functions
|
// allow paren-less arrow functions
|
||||||
@ -68,8 +68,12 @@ module.exports = {
|
|||||||
'import/extensions': 'off',
|
'import/extensions': 'off',
|
||||||
'import/no-unresolved': 'off',
|
'import/no-unresolved': 'off',
|
||||||
'import/no-extraneous-dependencies': 'off',
|
'import/no-extraneous-dependencies': 'off',
|
||||||
|
|
||||||
'prefer-promise-reject-errors': 'off',
|
'prefer-promise-reject-errors': 'off',
|
||||||
|
'prefer-const': 'off',
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'no-trailing-spaces' : 'off',
|
||||||
|
'vue/multi-word-component-names' : 'off',
|
||||||
|
|
||||||
// allow debugger during development only
|
// allow debugger during development only
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||||
|
@ -20,7 +20,8 @@ module.exports = configure(function (/* ctx */) {
|
|||||||
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
// https://v2.quasar.dev/quasar-cli-vite/boot-files
|
||||||
boot: [
|
boot: [
|
||||||
|
|
||||||
'axios'
|
'axios',
|
||||||
|
'pocketbase'
|
||||||
],
|
],
|
||||||
|
|
||||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||||
@ -98,7 +99,12 @@ module.exports = configure(function (/* ctx */) {
|
|||||||
// directives: [],
|
// directives: [],
|
||||||
|
|
||||||
// Quasar plugins
|
// Quasar plugins
|
||||||
plugins: []
|
plugins: [
|
||||||
|
'Dialog',
|
||||||
|
'Notify',
|
||||||
|
'LocalStorage',
|
||||||
|
'Loading'
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
// animations: 'all', // --- includes all animations
|
// animations: 'all', // --- includes all animations
|
||||||
|
BIN
src/assets/gyoza.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
src/assets/gyoza_logo.jpg
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
src/assets/gyoza_logo_inline.jpg
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
src/assets/gyoza_logo_nobg.png
Normal file
After Width: | Height: | Size: 95 KiB |
BIN
src/assets/methmoji.png
Normal file
After Width: | Height: | Size: 134 KiB |
191
src/assets/quasar-logo-full.svg
Normal file
After Width: | Height: | Size: 13 KiB |
1
src/assets/sad.svg
Normal file
After Width: | Height: | Size: 8.4 KiB |
13
src/boot/pocketbase.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { boot } from 'quasar/wrappers'
|
||||||
|
import PocketBase from 'pocketbase'
|
||||||
|
const pb = new PocketBase('http://127.0.0.1:8090')
|
||||||
|
|
||||||
|
export default boot(({ app }) => {
|
||||||
|
pb.collection('_superusers').authWithPassword(
|
||||||
|
'gyoza@hfsplay.fr',
|
||||||
|
'gyozagyoza'
|
||||||
|
)
|
||||||
|
|
||||||
|
app.config.globalProperties.$pb = pb
|
||||||
|
})
|
||||||
|
export { pb }
|
75
src/components/Feed.vue
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<q-list highlight inset-separator sparse no-border>
|
||||||
|
<q-list-header>Quoi de neuf</q-list-header>
|
||||||
|
<q-item mulitline
|
||||||
|
link
|
||||||
|
v-for="history in lastHistory"
|
||||||
|
:key="history.timestamp"
|
||||||
|
:to="`item/${history.ref}`"
|
||||||
|
>
|
||||||
|
<q-item-side :icon="history.image ? 'image' : 'chat'" />
|
||||||
|
<q-item-main>
|
||||||
|
<q-item-tile lines="3" label>{{ history.text }}</q-item-tile>
|
||||||
|
<q-item-tile sublabel>
|
||||||
|
<span class="text-secondary">{{history.user}}</span>
|
||||||
|
, au sujet de <b>{{ history.item.name}}</b> #{{history.ref}}
|
||||||
|
</q-item-tile>
|
||||||
|
</q-item-main>
|
||||||
|
<q-item-side right :stamp="timeAgo(history.timestamp)"/>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { date } from 'quasar'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ActivityFeed',
|
||||||
|
data () {
|
||||||
|
return { }
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
stock () {
|
||||||
|
return this.$store.state.core.firebase.stock || []
|
||||||
|
},
|
||||||
|
history () {
|
||||||
|
return this.$store.state.core.firebase.history || []
|
||||||
|
},
|
||||||
|
username () { return this.$store.state.core.username },
|
||||||
|
lastHistory () {
|
||||||
|
let newHistory = JSON.parse(JSON.stringify(this.history)).slice(-15)
|
||||||
|
for (let history of newHistory) {
|
||||||
|
history.item = this.getStock(history.ref)
|
||||||
|
}
|
||||||
|
return newHistory.reverse()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
dateFormat (timestamp, format = 'DD/MM/YY - HH:ss') {
|
||||||
|
return date.formatDate(timestamp, format)
|
||||||
|
},
|
||||||
|
timeAgo (timestamp) {
|
||||||
|
let date1 = new Date(timestamp)
|
||||||
|
let date2 = Date.now()
|
||||||
|
let hoursDiff = date.getDateDiff(date2, date1, 'hours')
|
||||||
|
if (hoursDiff < 24) {
|
||||||
|
return `il y a ${hoursDiff} heure(s)`
|
||||||
|
} else {
|
||||||
|
let daysDiff = date.getDateDiff(date2, date1)
|
||||||
|
return `il y a ${daysDiff} jour(s)`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getStock (ref) {
|
||||||
|
return this.stock.find(item => {
|
||||||
|
return (item) ? item.ref === ref : false
|
||||||
|
}) || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
79
src/components/GlobalSearch.vue
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<q-input v-model="srcTxt"
|
||||||
|
@input="updateSearchUrl"
|
||||||
|
placeholder="Rechercher une référence ou un nom"
|
||||||
|
autofocus
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
<q-list highlight
|
||||||
|
no-border
|
||||||
|
v-if="searchResults.length > 0"
|
||||||
|
class="q-mb-lg"
|
||||||
|
>
|
||||||
|
<q-item-label>{{searchResults.length}} résultats de recherche pour "{{srcTxt}}"</q-item-label>
|
||||||
|
<q-item multiline
|
||||||
|
link
|
||||||
|
:class="{'text-red': result.deleted}"
|
||||||
|
v-for="result in searchResults"
|
||||||
|
:disabled="result.deleted"
|
||||||
|
:key="result.ref"
|
||||||
|
:to="`item/${result.ref}`"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-section label>{{ result.name }}</q-item-section>
|
||||||
|
<q-item-section sublabel>
|
||||||
|
#{{result.ref}}, dans <span class="text-secondary">{{result.type}}</span>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section right :stamp="`Etat: ${result.state}`"/>
|
||||||
|
</q-item>
|
||||||
|
<!-- <q-item-separator />-->
|
||||||
|
</q-list>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { date } from 'quasar'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'GlobalSearch',
|
||||||
|
data () {
|
||||||
|
return { srcTxt: null }
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
stock () {
|
||||||
|
return this.$store.state.core.firebase.stock || []
|
||||||
|
},
|
||||||
|
searchResults () {
|
||||||
|
if (!this.srcTxt || this.srcTxt.length < 3) return []
|
||||||
|
return this.stock.filter(item => {
|
||||||
|
return (item.ref + (item.name + '').toLowerCase()).search(this.srcTxt.toLowerCase()) !== -1
|
||||||
|
}) || []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (!this.srcTxt) this.srcTxt = this.$route.query.search || null
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
timeAgo (timestamp) {
|
||||||
|
let date1 = new Date(timestamp)
|
||||||
|
let date2 = Date.now()
|
||||||
|
let hoursDiff = date.getDateDiff(date2, date1, 'hours')
|
||||||
|
if (hoursDiff < 24) {
|
||||||
|
return `il y a ${hoursDiff} heure(s)`
|
||||||
|
} else {
|
||||||
|
let daysDiff = date.getDateDiff(date2, date1)
|
||||||
|
return `il y a ${daysDiff} jour(s)`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateSearchUrl () {
|
||||||
|
this.$router.replace({ path: this.$router.path, query: { search: this.srcTxt } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
@ -12,14 +12,24 @@
|
|||||||
// to match your app's branding.
|
// to match your app's branding.
|
||||||
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||||
|
|
||||||
$primary : #1976D2;
|
//$primary : #1976D2;
|
||||||
$secondary : #26A69A;
|
//$secondary : #26A69A;
|
||||||
$accent : #9C27B0;
|
//$accent : #9C27B0;
|
||||||
|
//
|
||||||
|
//$dark : #1D1D1D;
|
||||||
|
//$dark-page : #121212;
|
||||||
|
//
|
||||||
|
//$positive : #21BA45;
|
||||||
|
//$negative : #C10015;
|
||||||
|
//$info : #31CCEC;
|
||||||
|
//$warning : #F2C037;
|
||||||
|
|
||||||
$dark : #1D1D1D;
|
$primary: #027be3;
|
||||||
$dark-page : #121212;
|
$secondary: #26A69A;
|
||||||
|
$tertiary: #555;
|
||||||
|
|
||||||
$positive : #21BA45;
|
$neutral: #E0E1E2;
|
||||||
$negative : #C10015;
|
$positive: #21BA45;
|
||||||
$info : #31CCEC;
|
$negative: #DB2828;
|
||||||
$warning : #F2C037;
|
$info: #31CCEC;
|
||||||
|
$warning: #F2C037;
|
||||||
|
224
src/layouts/DefaultLayout.vue
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
<template>
|
||||||
|
<q-layout> <!-- Be sure to play with the Layout demo on docs -->
|
||||||
|
<q-header class="no-shadow text-center">
|
||||||
|
<q-toolbar color="secondary" inverted style="border-bottom:1px solid rgba(0,0,0,0.1)">
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
size="18px"
|
||||||
|
icon="menu"
|
||||||
|
@click="leftDrawer = !leftDrawer"
|
||||||
|
/>
|
||||||
|
<q-toolbar-title class="text-center">
|
||||||
|
<div class="q-px-md" @click="$router.push('/dashboard')" style="display:inline-block;cursor:pointer;max-width:250px">
|
||||||
|
<img src="~assets/gyoza_logo_inline.jpg" style="width:100%">
|
||||||
|
</div>
|
||||||
|
</q-toolbar-title>
|
||||||
|
<q-btn flat class="no-pointer-events">{{username}}</q-btn>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
size="18px"
|
||||||
|
icon="logout"
|
||||||
|
@click="unsetUser()"
|
||||||
|
/>
|
||||||
|
</q-toolbar>
|
||||||
|
</q-header>
|
||||||
|
<q-drawer
|
||||||
|
side="left"
|
||||||
|
v-model="leftDrawer"
|
||||||
|
>
|
||||||
|
<q-list highlight
|
||||||
|
no-border
|
||||||
|
inset-separator
|
||||||
|
>
|
||||||
|
<q-item-label>Catégories</q-item-label>
|
||||||
|
<q-item v-for="category in categories"
|
||||||
|
:key="category.code"
|
||||||
|
:to="'/category/' + category.code"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
{{ category.name }}
|
||||||
|
<q-chip square
|
||||||
|
dense
|
||||||
|
color="secondary"
|
||||||
|
class="q-ml-md"
|
||||||
|
>{{ counters[category.name] }}</q-chip>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-btn round
|
||||||
|
flat
|
||||||
|
size="md"
|
||||||
|
icon="add"
|
||||||
|
:title="`Ajouter un(e) ${category.name}`"
|
||||||
|
@click.stop.prevent="addItem(category)"
|
||||||
|
>
|
||||||
|
</q-btn>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<!--<q-item-separator />-->
|
||||||
|
<!--<q-list-header>Previous chats</q-list-header>-->
|
||||||
|
<!--<q-item>-->
|
||||||
|
<!--<q-item-side avatar="statics/guy-avatar.png" />-->
|
||||||
|
<!--<q-item-main label="Jack Doe" />-->
|
||||||
|
<!--</q-item>-->
|
||||||
|
</q-list>
|
||||||
|
</q-drawer>
|
||||||
|
|
||||||
|
<q-page-container>
|
||||||
|
<!-- This is where pages get injected -->
|
||||||
|
<router-view />
|
||||||
|
</q-page-container>
|
||||||
|
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { QSpinnerBars, date } from 'quasar'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DefaultLayout',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
leftDrawer: true,
|
||||||
|
loading: false,
|
||||||
|
dataRef: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
categories () {
|
||||||
|
return this.$store.state.core.firebase.categories
|
||||||
|
},
|
||||||
|
counters () {
|
||||||
|
let stock = this.$store.state.core.firebase.stock
|
||||||
|
let result = {}
|
||||||
|
if (!this.categories || !stock) return result
|
||||||
|
for (let cat of this.categories) {
|
||||||
|
let catStock = stock.filter(item => { return item.type === cat.name })
|
||||||
|
let lastRef = (catStock[catStock.length - 1] || {}).ref
|
||||||
|
let ref = lastRef ? parseInt(lastRef.split('-')[1]) : 0
|
||||||
|
result[cat.name] = ref
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
username () {
|
||||||
|
return this.$store.state.core.username
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.$q.loading.setDefaults({
|
||||||
|
spinner: QSpinnerBars,
|
||||||
|
message: 'OK, laisse moi juste le temps de charger...',
|
||||||
|
messageColor: 'white',
|
||||||
|
spinnerSize: 150, // in pixels
|
||||||
|
spinnerColor: 'white',
|
||||||
|
customClass: 'bg-secondary',
|
||||||
|
delay: 0
|
||||||
|
})
|
||||||
|
this.$q.loading.show()
|
||||||
|
this.loadAppData()
|
||||||
|
// Realtime reactivity to DB changes
|
||||||
|
// TODO: Fix this
|
||||||
|
// this.dataRef = Firebase.database().ref()
|
||||||
|
// this.dataRef.on('value', (snapshot) => {
|
||||||
|
// this.$store.dispatch('core/loadAppData')
|
||||||
|
// })
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
let username = this.$q.localStorage.getItem('username')
|
||||||
|
console.log('user', username)
|
||||||
|
if (!username) {
|
||||||
|
this.$q.dialog({
|
||||||
|
title: 'Hey salut !',
|
||||||
|
message: 'Moi c\'est Gyōza. C\'est quoi ton pseudo ?',
|
||||||
|
prompt: {
|
||||||
|
model: '',
|
||||||
|
type: 'text' // optional
|
||||||
|
},
|
||||||
|
cancel: false,
|
||||||
|
preventClose: true,
|
||||||
|
color: 'secondary'
|
||||||
|
}).onOk(username => {
|
||||||
|
this.$q.localStorage.set('username', username)
|
||||||
|
this.$store.commit('core/setUsername', username)
|
||||||
|
this.$q.notify({
|
||||||
|
message: `Enchanté ${username} !`,
|
||||||
|
color: 'green',
|
||||||
|
avatar: '/assets/gyoza_logo_nobg.png',
|
||||||
|
timeout: 2000
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.$store.commit('core/setUsername', username)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadAppData () {
|
||||||
|
this.$q.loading.show()
|
||||||
|
this.$store.dispatch('core/loadAppData').then(data => {
|
||||||
|
console.log(data)
|
||||||
|
this.$q.loading.hide()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
unsetUser () {
|
||||||
|
this.$q.dialog({
|
||||||
|
title: 'Une petite seconde...',
|
||||||
|
message: 'Tu vas être déconnecté(e) là. C\'est vraiment ça que tu veux ?',
|
||||||
|
color: 'primary',
|
||||||
|
ok: 'Bah oui, FDP',
|
||||||
|
cancel: 'Ah non en fait'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.$q.localStorage.remove('username')
|
||||||
|
this.$router.push('/')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
addItem (category) {
|
||||||
|
this.$q.loading.show()
|
||||||
|
let newItem = {
|
||||||
|
available: true,
|
||||||
|
comment: '',
|
||||||
|
name: '',
|
||||||
|
owner: 'HFS',
|
||||||
|
ref: null,
|
||||||
|
state: null,
|
||||||
|
type: category.name,
|
||||||
|
system: null
|
||||||
|
}
|
||||||
|
newItem.ref = `${category.code}-${String(this.counters[category.name] + 1).padStart(4, '0')}`
|
||||||
|
this.$store.dispatch('core/addToCollection', {
|
||||||
|
collection: 'stock',
|
||||||
|
data: newItem
|
||||||
|
}).then(response => {
|
||||||
|
// Insert history record
|
||||||
|
this.$store.dispatch('core/addToCollection', {
|
||||||
|
collection: 'history',
|
||||||
|
data: {
|
||||||
|
ref: newItem.ref,
|
||||||
|
text: 'Entrée en stock',
|
||||||
|
user: this.$store.state.core.username,
|
||||||
|
timestamp: Date.now()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Process to new item
|
||||||
|
this.$q.loading.hide()
|
||||||
|
this.$q.dialog({
|
||||||
|
title: `Item "${category.name}" créé`,
|
||||||
|
message: 'Tu veux aller éditer les détails de cet item ?',
|
||||||
|
color: 'primary',
|
||||||
|
ok: 'Oui, STP',
|
||||||
|
cancel: 'Osef'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.$router.push(`/item/${newItem.ref}`)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// Picked "Cancel" or dismissed
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
27
src/layouts/FullscreenLayout.vue
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<q-layout> <!-- Be sure to play with the Layout demo on docs -->
|
||||||
|
<q-page-container>
|
||||||
|
<router-view />
|
||||||
|
</q-page-container>
|
||||||
|
</q-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
// name: 'LayoutName',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
leftDrawer: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
let username = this.$q.localStorage.getItem('username')
|
||||||
|
if (username) {
|
||||||
|
this.$router.push('/dashboard')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
27
src/pages/Dashboard.vue
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="flex flex-center flex-">
|
||||||
|
<div v-if="username" class="q-py-lg">
|
||||||
|
<GlobalSearch />
|
||||||
|
<Feed />
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Feed from '../components/Feed.vue'
|
||||||
|
import GlobalSearch from '../components/GlobalSearch.vue'
|
||||||
|
export default {
|
||||||
|
name: 'DashboardPage',
|
||||||
|
components: { Feed, GlobalSearch },
|
||||||
|
computed: {
|
||||||
|
username () {
|
||||||
|
return this.$store.state.core.username
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
22
src/pages/Error404.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<div class="fixed-center text-center">
|
||||||
|
<p>
|
||||||
|
<img
|
||||||
|
src="~assets/methmoji.png"
|
||||||
|
style="width:30vw;max-width:150px;"
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p class="text-faded">Y'a rien ici, frêre...<strong>(404)</strong></p>
|
||||||
|
<q-btn
|
||||||
|
color="secondary"
|
||||||
|
style="width:200px;"
|
||||||
|
@click="$router.push('/')"
|
||||||
|
>Retour a la home</q-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'Error404'
|
||||||
|
}
|
||||||
|
</script>
|
18
src/pages/Index.vue
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="flex flex-center flex-">
|
||||||
|
<div class="text-center">
|
||||||
|
<img alt="Quasar logo" src="~assets/gyoza.jpg">
|
||||||
|
<br/>
|
||||||
|
<q-btn label="Go" color="primary" to="/dashboard"></q-btn>
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PageIndex'
|
||||||
|
}
|
||||||
|
</script>
|
308
src/pages/StockItem.vue
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="q-pa-md">
|
||||||
|
<q-chip class="float-right text-white" color="secondary">{{item.ref}}</q-chip>
|
||||||
|
<q-chip v-if="item.deleted" class="float-right q-mr-md" color="red">Supprimé</q-chip>
|
||||||
|
<h2 class="q-ma-none" :class="{'text-faded': !item.name}">{{item.name || '---- -- ----'}}</h2>
|
||||||
|
<h4 class="q-ma-none q-pb-md text-light">{{item.type}}</h4>
|
||||||
|
|
||||||
|
<div class="q-py-md">
|
||||||
|
<q-input v-model="item.name"
|
||||||
|
:disable="item.deleted"
|
||||||
|
type="text"
|
||||||
|
standout
|
||||||
|
stack-label label="Nom" class="q-mb-md"
|
||||||
|
@input="changed = true"/>
|
||||||
|
<q-input v-model="item.owner"
|
||||||
|
:disable="item.deleted"
|
||||||
|
color="secondary"
|
||||||
|
standout stack-label label="Propriétaire" class="q-mb-md"
|
||||||
|
@input="changed = true"/>
|
||||||
|
<q-field borderless stack-label label="Etat cosmétique" class="q-mb-md">
|
||||||
|
<q-btn-toggle
|
||||||
|
no-caps
|
||||||
|
:disable="item.deleted"
|
||||||
|
v-model="item.state"
|
||||||
|
@update:model-value="changed = true"
|
||||||
|
toggle-color="secondary"
|
||||||
|
text-color="black"
|
||||||
|
:options="[
|
||||||
|
{label: 'Neuf', value: 'Neuf'},
|
||||||
|
{label: 'Excellent', value: 'Excellent'},
|
||||||
|
{label: 'Bon', value: 'Bon'},
|
||||||
|
{label: 'Moyen', value: 'Moyen'},
|
||||||
|
{label: 'Abimé', value: 'Abimé'},
|
||||||
|
{label: 'Inutilisable', value: 'Inutilisable'}
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</q-field>
|
||||||
|
<q-field borderless stack-label label="Fonctionnel" class="q-mb-md">
|
||||||
|
<q-btn-toggle
|
||||||
|
no-caps
|
||||||
|
:disable="item.deleted"
|
||||||
|
v-model="item.working"
|
||||||
|
:toggle-color="item.working ? 'green' : 'red'"
|
||||||
|
:options="[
|
||||||
|
{label: '✔', value: true},
|
||||||
|
{label: '?', value: null},
|
||||||
|
{label: '✘', value: false}
|
||||||
|
]"
|
||||||
|
@input="changed = true"
|
||||||
|
/>
|
||||||
|
</q-field>
|
||||||
|
<q-field borderless stack-label label="En stock" class="q-mb-md">
|
||||||
|
<q-btn-toggle
|
||||||
|
no-caps
|
||||||
|
:disable="item.deleted"
|
||||||
|
v-model="item.available"
|
||||||
|
:toggle-color="item.available ? 'green' : 'red'"
|
||||||
|
:options="[
|
||||||
|
{label: '✔', value: true},
|
||||||
|
{label: '✘', value: false}
|
||||||
|
]"
|
||||||
|
@input="changed = true"
|
||||||
|
/>
|
||||||
|
</q-field>
|
||||||
|
<q-field borderless stack-label label="Supprimé" class="q-mb-md">
|
||||||
|
<q-btn-toggle
|
||||||
|
no-caps
|
||||||
|
v-model="item.deleted"
|
||||||
|
:toggle-color="item.deleted ? 'green' : 'red'"
|
||||||
|
:options="[
|
||||||
|
{label: 'Oui', value: true},
|
||||||
|
{label: 'Non', value: false}
|
||||||
|
]"
|
||||||
|
@input="changed = true"
|
||||||
|
/>
|
||||||
|
</q-field>
|
||||||
|
<q-input
|
||||||
|
:disable="item.deleted"
|
||||||
|
v-model="item.comment"
|
||||||
|
@update:model-value="changed = true"
|
||||||
|
type="textarea"
|
||||||
|
:max-height="100"
|
||||||
|
rows="4"
|
||||||
|
standout stack-label label="Commentaire" class="q-mb-md"
|
||||||
|
inverted
|
||||||
|
/>
|
||||||
|
<q-btn :disabled="!changed || originalItem.deleted"
|
||||||
|
@click="updateItem()"
|
||||||
|
color="primary"
|
||||||
|
:flat="!changed"
|
||||||
|
>Enregistrer</q-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<h4 class="q-ma-none q-pb-md text-light">Historique</h4>
|
||||||
|
<q-timeline color="secondary">
|
||||||
|
<!--<q-timeline-entry heading>-->
|
||||||
|
<!--Historique-->
|
||||||
|
<!--</q-timeline-entry>-->
|
||||||
|
<q-timeline-entry
|
||||||
|
title="Ajouter une entrée"
|
||||||
|
side="left"
|
||||||
|
icon="add"
|
||||||
|
color="primary"
|
||||||
|
@click="openHistoryForm()"
|
||||||
|
style="cursor:pointer"
|
||||||
|
>
|
||||||
|
<q-slide-transition>
|
||||||
|
<div v-show="showHistoryForm">
|
||||||
|
<q-input
|
||||||
|
v-model="pendingHistory"
|
||||||
|
type="textarea"
|
||||||
|
label="Description"
|
||||||
|
:max-height="100"
|
||||||
|
rows="3"
|
||||||
|
invertedd
|
||||||
|
/>
|
||||||
|
<q-input label="Date"
|
||||||
|
v-model="pendingHistoryDate"
|
||||||
|
type="datetime"
|
||||||
|
format24h />
|
||||||
|
<br/>
|
||||||
|
<q-uploader url=""
|
||||||
|
:upload-factory="uploadFile"
|
||||||
|
@add="hasFile = true"
|
||||||
|
ref="uploader"
|
||||||
|
:extensions="'.jpg,.png'"
|
||||||
|
auto-expand
|
||||||
|
hide-upload-button
|
||||||
|
stack-label="Uploader une photo"
|
||||||
|
/>
|
||||||
|
<br/>
|
||||||
|
<q-btn flat color="faded" @click.stop="showHistoryForm = false">Annuler</q-btn>
|
||||||
|
<q-btn flat color="primary" @click.stop="addHistory()">Ok</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-slide-transition>
|
||||||
|
</q-timeline-entry>
|
||||||
|
<q-timeline-entry
|
||||||
|
v-for="event in history.slice().reverse()"
|
||||||
|
:key="event.timestamp"
|
||||||
|
:subtitle="getDate(event.timestamp)"
|
||||||
|
:title="event.user"
|
||||||
|
side="left"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<q-icon name="format_quote"
|
||||||
|
color="light"
|
||||||
|
size="2rem"
|
||||||
|
/>
|
||||||
|
{{ event.text }}
|
||||||
|
<q-icon name="format_quote"
|
||||||
|
color="light"
|
||||||
|
size="2rem"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<q-img :src="getImageUrl(event, event.image_file)"
|
||||||
|
alt="photo"
|
||||||
|
v-if="event.image_file"
|
||||||
|
class="q-py-md"
|
||||||
|
style="max-height:300px;max-width:300px"
|
||||||
|
:fit="'contain'"
|
||||||
|
/>
|
||||||
|
<br/>
|
||||||
|
<q-btn flat
|
||||||
|
:disable="item.deleted"
|
||||||
|
icon="delete"
|
||||||
|
label="Supprimer cette entrée"
|
||||||
|
@click="removeHistory(event)"
|
||||||
|
/>
|
||||||
|
</q-timeline-entry>
|
||||||
|
</q-timeline>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { date } from 'quasar'
|
||||||
|
export default {
|
||||||
|
name: 'StockItem',
|
||||||
|
props: ['itemRef'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
showHistoryForm: false,
|
||||||
|
pendingHistory: null,
|
||||||
|
pendingHistoryDate: null,
|
||||||
|
changed: false,
|
||||||
|
hasFile: false,
|
||||||
|
username: null,
|
||||||
|
item: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
category () {
|
||||||
|
return this.item.type || ''
|
||||||
|
},
|
||||||
|
stock () {
|
||||||
|
return this.$store.state.core.firebase.stock || []
|
||||||
|
},
|
||||||
|
originalItem () {
|
||||||
|
return this.stock.find(item => {
|
||||||
|
return (item) ? item.ref === this.itemRef : false
|
||||||
|
}) || []
|
||||||
|
},
|
||||||
|
itemId () {
|
||||||
|
return this.$store.state.core.firebase.stock.findIndex(item => {
|
||||||
|
return (item) ? item.ref === this.itemRef : false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
history () {
|
||||||
|
return this.$store.state.core.firebase.history.filter(item => {
|
||||||
|
return item.ref === this.itemRef
|
||||||
|
}).sort((a, b) => {
|
||||||
|
return (a.timestamp > b.timestamp) ? 1 : -1
|
||||||
|
}) || []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created: function () {
|
||||||
|
let item = this.stock.find(item => {
|
||||||
|
return (item) ? item.ref === this.itemRef : false
|
||||||
|
}) || []
|
||||||
|
this.item = { ...item }
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.username = this.$q.localStorage.getItem('username')
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getDate (timestamp) {
|
||||||
|
return date.formatDate(timestamp, 'DD/MM/YY - HH:mm')
|
||||||
|
},
|
||||||
|
openHistoryForm () {
|
||||||
|
this.showHistoryForm = true
|
||||||
|
this.pendingHistoryDate = Date.now()
|
||||||
|
},
|
||||||
|
async addHistory () {
|
||||||
|
this.$q.loading.show()
|
||||||
|
let url = null
|
||||||
|
if (this.hasFile) {
|
||||||
|
let resp = await this.uploadFile(this.$refs.uploader.files[0])
|
||||||
|
url = resp
|
||||||
|
console.log(resp)
|
||||||
|
}
|
||||||
|
this.$store.dispatch('core/addToCollection', {
|
||||||
|
collection: 'history',
|
||||||
|
data: {
|
||||||
|
ref: this.itemRef,
|
||||||
|
text: this.pendingHistory,
|
||||||
|
user: this.$store.state.core.username,
|
||||||
|
image: url,
|
||||||
|
timestamp: this.pendingHistoryDate
|
||||||
|
}
|
||||||
|
}).then(response => {
|
||||||
|
// this.$speech(`ooo history desu, arigato !`)
|
||||||
|
this.showHistoryForm = false
|
||||||
|
this.pendingHistory = null
|
||||||
|
this.pendingHistoryDate = null
|
||||||
|
this.hasFile = false
|
||||||
|
this.$refs.uploader.reset()
|
||||||
|
this.$q.loading.hide()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeHistory (data) {
|
||||||
|
this.$q.dialog({
|
||||||
|
title: 'Supprimer ?',
|
||||||
|
message: "Il est possible que tu sois Meth, nous sommes donc légalement obligés de te demander : es-tu absolument sur d'être certain de vouloir effectuer cette action irréversible ?",
|
||||||
|
ok: 'Supprimer',
|
||||||
|
cancel: 'Annuler'
|
||||||
|
}).then(() => {
|
||||||
|
this.$store.dispatch('core/removeFromCollection', {
|
||||||
|
collection: 'history',
|
||||||
|
data
|
||||||
|
}).then(_ => this.$q.notify('Supprimé !'))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateItem () {
|
||||||
|
this.$q.loading.show()
|
||||||
|
this.$store.dispatch('core/updateItem', {
|
||||||
|
collection: 'stock',
|
||||||
|
itemId: this.itemId,
|
||||||
|
data: this.item
|
||||||
|
}).then(response => {
|
||||||
|
// this.$speech(`sugoï, domo arigato senpai !`)
|
||||||
|
this.$q.notify({
|
||||||
|
message: 'Enregistré !',
|
||||||
|
color: 'grey',
|
||||||
|
avatar: '~assets/gyoza_logo_nobg.png',
|
||||||
|
timeout: 2000
|
||||||
|
})
|
||||||
|
this.changed = false
|
||||||
|
this.$store.dispatch('core/loadAppData').then(_ => {
|
||||||
|
this.$q.loading.hide()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
uploadFile (file) {
|
||||||
|
return this.$store.dispatch('core/uploadFile', file).then(response => {
|
||||||
|
return response.ref.getDownloadURL()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getImageUrl (history, image) {
|
||||||
|
return this.$pb.files.getURL(history, image, { thumb: '300x300f' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
146
src/pages/StockItems.vue
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<template>
|
||||||
|
<q-page padding>
|
||||||
|
<h4 class="q-mt-none">{{category.name}}</h4>
|
||||||
|
<q-table
|
||||||
|
:title="category.name"
|
||||||
|
:rows="items"
|
||||||
|
:columns="dynamicColumns"
|
||||||
|
:pagination="pagination"
|
||||||
|
row-key="ref"
|
||||||
|
@update:pagination="updatePaginationUrl"
|
||||||
|
>
|
||||||
|
|
||||||
|
<template v-slot:top>
|
||||||
|
<q-toggle v-model="showDeleted"
|
||||||
|
label="Voir items supprimés"
|
||||||
|
class="q-mr-md"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
hide-underline
|
||||||
|
color="secondary"
|
||||||
|
v-model="search"
|
||||||
|
class="col-6"
|
||||||
|
@input="updateSearchUrl"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:body="props">
|
||||||
|
<q-tr
|
||||||
|
:props="props"
|
||||||
|
@click="goToDetail(props)"
|
||||||
|
:class="{'cursor-pointer': true, 'text-red': props.row.deleted}">
|
||||||
|
<q-td v-for="col in props.cols" :key="col.ref">
|
||||||
|
<div v-if="col.name === 'available'" class="text-center">
|
||||||
|
<span v-if="col.value" class="text-green">✔</span>
|
||||||
|
<span v-else class="text-red">✘</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="col.name === 'deleted'" class="text-center">
|
||||||
|
<span v-if="col.value" class="text-red">✘</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="col.name === 'state'" class="text-center">
|
||||||
|
<q-chip small :color="stateColor(col.value)" :title="col.value"></q-chip>
|
||||||
|
</div>
|
||||||
|
<span v-if="col.name !== 'deleted' && col.name !== 'available'&& col.name !== 'state'">
|
||||||
|
{{ col.value }}
|
||||||
|
</span>
|
||||||
|
</q-td>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</q-table>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { debounce } from 'quasar'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'StockItems',
|
||||||
|
props: ['categoryCode'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
search: null,
|
||||||
|
showDeleted: false,
|
||||||
|
pagination: {
|
||||||
|
sortBy: null,
|
||||||
|
descending: false,
|
||||||
|
page: 1,
|
||||||
|
rowsPerPage: 15
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{ label: 'Référence', align: 'left', field: 'ref', sortable: true, name: 'ref' },
|
||||||
|
{ label: 'Nom', align: 'left', field: 'name', sortable: true, name: 'name' },
|
||||||
|
{ label: 'Etat', align: 'left', field: 'state', sortable: true, name: 'state' },
|
||||||
|
{ label: 'Propriétaire', align: 'left', field: 'owner', sortable: true, name: 'owner' },
|
||||||
|
{ label: 'En stock', align: 'left', field: 'available', sortable: true, name: 'available' },
|
||||||
|
{ label: 'Supprimé', align: 'left', field: 'deleted', sortable: false, name: 'deleted', format: val => val ? '✔' : '' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
if (!this.search) this.search = this.$route.query.search || null
|
||||||
|
let paginationQuery = { ...this.$route.query }
|
||||||
|
delete paginationQuery.search
|
||||||
|
this.pagination = { ...this.pagination, ...paginationQuery }
|
||||||
|
console.log('__Pagination:', this.pagination)
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dynamicColumns () {
|
||||||
|
let columns = JSON.parse(JSON.stringify(this.columns))
|
||||||
|
if (!this.showDeleted) columns.pop()
|
||||||
|
return columns
|
||||||
|
},
|
||||||
|
category () {
|
||||||
|
return this.$store.state.core.firebase.categories.find(item => {
|
||||||
|
return parseInt(item.code) === parseInt(this.categoryCode)
|
||||||
|
}) || {}
|
||||||
|
},
|
||||||
|
items () {
|
||||||
|
let items = this.$store.state.core.firebase.stock.filter(item => {
|
||||||
|
return item.type === this.category.name &&
|
||||||
|
(this.search ? (item.ref + (item.name + '').toLowerCase()).search(this.search.toLowerCase()) !== -1 : true) &&
|
||||||
|
(this.showDeleted ? true : !item.deleted)
|
||||||
|
}).slice().reverse() || []
|
||||||
|
if (this.pagination.sortBy) {
|
||||||
|
console.log('__Sorting:', this.pagination)
|
||||||
|
return [...items].sort((a, b) => {
|
||||||
|
const x = this.pagination.descending ? b : a
|
||||||
|
const y = this.pagination.descending ? a : b
|
||||||
|
return x[this.pagination.sortBy] > y[this.pagination.sortBy]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
goToDetail (data) {
|
||||||
|
this.$router.push(`/item/${data.key}`)
|
||||||
|
},
|
||||||
|
stateColor (state) {
|
||||||
|
let colors = {
|
||||||
|
Neuf: 'green-14',
|
||||||
|
Excellent: 'green-12',
|
||||||
|
Bon: 'lime-13',
|
||||||
|
Moyen: 'orange',
|
||||||
|
Abimé: 'red',
|
||||||
|
Inutilisable: 'black'
|
||||||
|
}
|
||||||
|
return colors[state]
|
||||||
|
},
|
||||||
|
updateSearchUrl: debounce(function () {
|
||||||
|
this.$router.replace({ path: this.$router.path, query: { search: this.search } })
|
||||||
|
}, 300),
|
||||||
|
updatePaginationUrl: debounce(function () {
|
||||||
|
this.$router.replace({
|
||||||
|
path: this.$router.path,
|
||||||
|
query: {
|
||||||
|
...this.pagination
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
@ -1,17 +1,19 @@
|
|||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: () => import('layouts/MainLayout.vue'),
|
component: () => import('layouts/FullscreenLayout.vue'),
|
||||||
children: [
|
children: [
|
||||||
{ path: '', component: () => import('pages/IndexPage.vue') }
|
{ path: '', component: () => import('pages/Index.vue') }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
// Always leave this as last one,
|
|
||||||
// but you can also remove it
|
|
||||||
{
|
{
|
||||||
path: '/:catchAll(.*)*',
|
path: '/',
|
||||||
component: () => import('pages/ErrorNotFound.vue')
|
component: () => import('layouts/DefaultLayout.vue'),
|
||||||
|
children: [
|
||||||
|
{ path: 'dashboard', component: () => import('pages/Dashboard.vue') },
|
||||||
|
{ path: 'category/:categoryCode', props: true, component: () => import('pages/StockItems.vue') },
|
||||||
|
{ path: 'item/:itemRef', props: true, component: () => import('pages/StockItem.vue') }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
49
src/store/core/actions.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { pb } from 'src/boot/pocketbase'
|
||||||
|
//
|
||||||
|
// export function loadAppData (store) {
|
||||||
|
// return Firebase.database().ref().once('value').then((snapshot) => {
|
||||||
|
// store.commit('setFirebaseState', snapshot.val())
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
export async function loadAppData (store) {
|
||||||
|
return Promise.all([
|
||||||
|
pb.collection('stock').getFullList(),
|
||||||
|
pb.collection('history').getFullList(),
|
||||||
|
pb.collection('categories').getFullList()
|
||||||
|
]).then(([stock, history, categories]) => {
|
||||||
|
return store.commit('setFirebaseState', {
|
||||||
|
stock,
|
||||||
|
history,
|
||||||
|
categories
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// export function addToCollection (store, data) {
|
||||||
|
// return Firebase.database().ref('/' + data.collection).once('value').then((snapshot) => {
|
||||||
|
// let index = parseInt(Object.keys(snapshot.val()).reverse()[0]) + 1
|
||||||
|
// return Firebase.database().ref('/' + data.collection + '/' + index).set(data.data)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// export function removeFromCollection (store, data) {
|
||||||
|
// return Firebase.database().ref('/' + data.collection).once('value').then((snapshot) => {
|
||||||
|
// let index = snapshot.val().findIndex(item =>
|
||||||
|
// item.timestamp === data.data.timestamp && item.ref === data.data.ref
|
||||||
|
// )
|
||||||
|
// return Firebase.database().ref('/' + data.collection + '/' + index).remove()
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// export function updateItem (store, data) {
|
||||||
|
// return Firebase.database().ref(`/${data.collection}/${data.itemId}`).set(data.data)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// export function deleteItem (store, data) {
|
||||||
|
// return Firebase.database().ref(`/${data.collection}/${data.itemId}`).set(null)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// export function uploadFile (store, file) {
|
||||||
|
// return Firebase.storage().ref().child(Date.now() + file.name).put(file)
|
||||||
|
// }
|
4
src/store/core/getters.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/*
|
||||||
|
export function someGetter (state) {
|
||||||
|
}
|
||||||
|
*/
|
12
src/store/core/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import state from './state'
|
||||||
|
import * as getters from './getters'
|
||||||
|
import * as mutations from './mutations'
|
||||||
|
import * as actions from './actions'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state,
|
||||||
|
getters,
|
||||||
|
mutations,
|
||||||
|
actions
|
||||||
|
}
|
8
src/store/core/mutations.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export function setFirebaseState (state, data) {
|
||||||
|
state.firebase = data
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setUsername (state, data) {
|
||||||
|
state.username = data
|
||||||
|
}
|
8
src/store/core/state.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export default {
|
||||||
|
username: null,
|
||||||
|
firebase: {
|
||||||
|
categories: [],
|
||||||
|
history: [],
|
||||||
|
stock: []
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { store } from 'quasar/wrappers'
|
import { store } from 'quasar/wrappers'
|
||||||
import { createStore } from 'vuex'
|
import { createStore } from 'vuex'
|
||||||
|
import core from './core'
|
||||||
// import example from './module-example'
|
// import example from './module-example'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -15,7 +15,7 @@ import { createStore } from 'vuex'
|
|||||||
export default store(function (/* { ssrContext } */) {
|
export default store(function (/* { ssrContext } */) {
|
||||||
const Store = createStore({
|
const Store = createStore({
|
||||||
modules: {
|
modules: {
|
||||||
// example
|
core
|
||||||
},
|
},
|
||||||
|
|
||||||
// enable strict mode (adds overhead!)
|
// enable strict mode (adds overhead!)
|
||||||
|