Skip to content

Commit

Permalink
Go realtime !
Browse files Browse the repository at this point in the history
  • Loading branch information
seballot committed Sep 29, 2024
1 parent c2ddd47 commit a127aec
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 30 deletions.
6 changes: 5 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,12 @@ export default {
user(newUser, oldUser) {
// New login
if (newUser && !oldUser) {
supabase.removeAllChannels()
this.resetData()
setTimeout(() => this.fetchData(), 0)
setTimeout(() => {
this.fetchData()
this.listenDbChanges(newUser)
}, 0)
}
},
help() {
Expand Down
122 changes: 108 additions & 14 deletions src/services/db-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,140 @@ import supabase from '@/services/supabase'
export default {
install: (app) => {
app.config.globalProperties.$db = supabase

app.mixin({
methods: {
async dbCreate(dbName, object, onSuccess) {
listenDbChanges(user) {
if (!user) return
// Don't know why, but having a loop with a object of tableName does not work...
supabase.channel(`products-changes-${user.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'products', filter: `user_id=eq.${user.id}`,
}, this.handleUpdateOrInsert).subscribe()
supabase.channel(`recipies-changes-${user.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'recipies', filter: `user_id=eq.${user.id}`,
}, this.handleUpdateOrInsert).subscribe()
supabase.channel(`suppliers-changes-${user.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'suppliers', filter: `user_id=eq.${user.id}`,
}, this.handleUpdateOrInsert).subscribe()
supabase.channel(`categories-changes-${user.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'categories', filter: `user_id=eq.${user.id}`,
}, this.handleUpdateOrInsert).subscribe()
supabase.channel(`notes-changes-${user.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'notes', filter: `user_id=eq.${user.id}`,
}, this.handleUpdateOrInsert).subscribe()
supabase.channel(`inventories-changes-${user.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'inventories', filter: `user_id=eq.${user.id}`,
}, this.handleSessionChange).subscribe()
supabase.channel(`orders-changes-${user.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'orders', filter: `user_id=eq.${user.id}`,
}, this.handleUpdateOrInsert).subscribe()
supabase.channel(`sessions-changes-${user.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'sessions', filter: `user_id=eq.${user.id}`,
}, this.handleSessionChange).subscribe()

// Watch delete events separatly because filtering by user_id does not work
// so listening to all deletes
supabase.channel(`db-deletion-${user.id}`).on('postgres_changes', {
event: 'DELETE', schema: 'public',
}, (payload) => {
// console.log(payload.eventType, payload.table, payload.old.id)
delete this[payload.table][payload.old.id]
}).subscribe()
},
handleUpdateOrInsert(payload) {
// console.log(payload.eventType, payload)
if (payload.new.user_id !== this.$root.user.id) {
console.error('receive changes from different user')
} else {
const newObject = this.fixData(payload.new, payload.table)
this.$root[payload.table][payload.new.id] = newObject
}
},
handleSessionChange(payload) {
const newObject = this.fixData(payload.new, payload.table)
if (payload.eventType == 'INSERT') {
this.$root.sessions[newObject.id] = newObject
} else if (payload.eventType == 'UPDATE') {
const oldObject = this.fixData(payload.old, payload.table)
const currentObject = this.$root.sessions[newObject.id]
let attrs = ['name', 'events', 'rows']
attrs.forEach((attr) => {
if (!this.deepEqual(oldObject[attr], newObject[attr])) {
currentObject[attr] = newObject[attr]
}
})
attrs = ['buys', 'realStocks']
attrs.forEach((attr) => {
const oldVal = oldObject[attr]
const newVal = newObject[attr]
const currentVal = currentObject[attr]
Object.entries(newVal).forEach(([productId, values]) => {
// new produtId added
if (!oldVal[productId]) currentVal[productId] = newObject.buys[productId]
else {
// check if one day value have changed
Object.entries(values).forEach(([dayId, dayValue]) => {
if (oldVal[productId][dayId] != dayValue) currentVal[productId][dayId] = dayValue
})
}
})
})
}
},
deepEqual(obj1, obj2) {
if (obj1 === obj2) return true

if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 === null || obj2 === null) {
return false
}

const keys1 = Object.keys(obj1)
const keys2 = Object.keys(obj2)

if (keys1.length !== keys2.length) return false

for (const key of keys1) {
if (!this.deepEqual(obj1[key], obj2[key])) return false
}

return true
},
async dbCreate(tableName, object, onSuccess) {
this.loading = true
let { data, error } = await this.$db.from(dbName).insert([this.addUserId(object)]).select()
let { data, error } = await this.$db.from(tableName).insert([this.addUserId(object)]).select()
data = data[0] // don't know why but .single() does not work here, so getting first element
if (error) this.toastError(error)
else {
data = this.fixData(data, dbName)
this.$root[dbName][data.id] = data
data = this.fixData(data, tableName)
this.$root[tableName][data.id] = data
this.toastSuccess(data, 'created')
if (onSuccess) onSuccess(data)
}
this.loading = false
return data
},
async dbUpdate(dbName, object) {
async dbUpdate(tableName, object) {
this.loading = true

let { data, error } = await this.$db.from(dbName)
let { data, error } = await this.$db.from(tableName)
.update(this.addUserId(object))
.eq('id', object.id)
.select()
data = data[0] // don't know why but .single() does not work here, so getting first element
if (error) this.toastError(error)
else {
data = this.fixData(data, dbName)
this.$root[dbName][object.id] = data
data = this.fixData(data, tableName)
this.$root[tableName][object.id] = data
this.toastSuccess(data, 'updated')
}
this.loading = false
},
async dbDestroy(dbName, object) {
async dbDestroy(tableName, object) {
this.loading = true

// Delete the object
const { error } = await this.$db.from(dbName).delete().eq('id', object.id)
const { error } = await this.$db.from(tableName).delete().eq('id', object.id)
if (error) this.toastError(error)
else delete this.$root[dbName][object.id]
else delete this.$root[tableName][object.id]

this.loading = false
},
Expand All @@ -65,8 +159,8 @@ export default {
severity: 'success', summary: 'Success', detail: message, life: 4000,
})
},
fixData(data, dbName = '') {
if (dbName === 'sessions') (data.events || []).forEach((e) => { e.start_date = new Date(e.start_date) })
fixData(data, tableName = '') {
if (tableName === 'sessions') (data.events || []).forEach((e) => { e.start_date = new Date(e.start_date) })
return data
},
},
Expand Down
14 changes: 5 additions & 9 deletions src/services/debounce.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
export function debounce(fn, delay) {
let timeoutID = null
return function () {
clearTimeout(timeoutID)
const args = arguments
const that = this
timeoutID = setTimeout(() => {
fn.apply(that, args)
}, delay)
export function debounce(func, delay) {
let timeout
return function (...args) {
clearTimeout(timeout)
timeout = setTimeout(() => func.apply(this, args), delay)
}
}

Expand Down
31 changes: 29 additions & 2 deletions src/services/stocks-mixin.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { convertToUnit } from '@/services/units'
import supabase from '@/services/supabase'
import { debounce } from '@/services/debounce'

export default {
inject: ['sessionDays', 'stockDays', 'sessionInventories'],
Expand All @@ -9,8 +11,27 @@ export default {
}
},
mounted() {
this.calculateSessionProducts()
this.calculateAllStocks()
this.reCalculateAll()

const onNewDataRetrieved = debounce(() => {
this.reCalculateAll()
}, 300)
// we listen for current session change so we recalculate stock if needed
supabase.channel(`session-changes-${this.session.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'sessions', filter: `id=eq.${this.session.id}`,
}, onNewDataRetrieved).subscribe()
supabase.channel(`session-orders-${this.session.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'orders', filter: `session_id=eq.${this.session.id}`,
}, onNewDataRetrieved).subscribe()
supabase.channel(`session-inventories-${this.session.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'inventories', filter: `session_id=eq.${this.session.id}`,
}, onNewDataRetrieved).subscribe()
supabase.channel(`products-callback-${this.$root.user.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'products', filter: `user_id=eq.${this.$root.user.id}`,
}, onNewDataRetrieved).subscribe()
supabase.channel(`recipies-callback-${this.$root.user.id}`).on('postgres_changes', {
event: '*', schema: '*', table: 'recipies', filter: `user_id=eq.${this.$root.user.id}`,
}, onNewDataRetrieved).subscribe()
},
computed: {
session() {
Expand Down Expand Up @@ -39,6 +60,12 @@ export default {
},
},
methods: {
reCalculateAll() {
this.stocks = []
this.sessionProducts = []
this.calculateSessionProducts()
this.calculateAllStocks()
},
calculateSessionProducts() {
const result = new Set()
this.session.rows.forEach((row) => {
Expand Down
10 changes: 6 additions & 4 deletions src/views/sessions/SessionStocks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
You can adjust real values per day
</p>
<p>
Everything you buy should be displayed here. You can either enter manually bought amount on each
Everything you buy should be displayed here. You can either enter manually bought amount on
each
cell, or use the
<strong>orders</strong> feature to help you manage them
</p>
Expand Down Expand Up @@ -77,7 +78,8 @@
:class="`cell-stock editor-sm ${day.class} ${day.id}`">
<!-- Cell View -->
<template #body="{ data, field }">
<div class="cell-content" :class="{ 'negative-value': data.values[field].value.round() < 0 }">
<div class="cell-content"
:class="{ 'negative-value': data.values[field].value.round() < 0 }">
<span class="stock-value-container d-flex flex-column align-items-center">
<span class="stock-value consumption" v-if="data.values[field].consumption > 0"
:title="data.values[field].consumptionLabels.join(' | ')">
Expand All @@ -93,7 +95,7 @@
:style="data.values[field].stockDiff > 0.2 && 'color: var(--orange-600) !important'"
:title="data.values[field].stockDiff > 0.2 ? `Theoric stock was ${data.values[field].theoric.round()} ${data.product_unit}` : null">
<template v-if="day.id == 'initial'">{{ (data.values[field].real || 0).round()
}}</template>
}}</template>
<template v-else>{{ data.values[field].value.round() }}</template>
</span>
<span class="stock-value-container"></span>
Expand Down Expand Up @@ -132,7 +134,7 @@

<template #groupheader="{ data }">
<span style="position: sticky; left: .7rem">{{ (data[options.groupBy] || {}).name || "Others"
}}</span>
}}</span>
</template>

</DataTable>
Expand Down

0 comments on commit a127aec

Please sign in to comment.