Added Frontend
This commit is contained in:
168
frontend/stores/auth.ts
Normal file
168
frontend/stores/auth.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import { defineStore } from "pinia"
|
||||
import router from "#app/plugins/router";
|
||||
import {Preferences} from "@capacitor/preferences";
|
||||
|
||||
export const useAuthStore = defineStore("auth", {
|
||||
state: () => ({
|
||||
user: null as null | { user_id: string; email: string; tenant_id?: string; role?: string },
|
||||
profile: null as null | any,
|
||||
tenants: [] as { tenant_id: string; role: string; tenants: { id: string; name: string } }[],
|
||||
permissions: [] as string[],
|
||||
activeTenant: null as any,
|
||||
activeTenantData: null as any,
|
||||
loading: true as boolean,
|
||||
notLoggedIn: true,
|
||||
}),
|
||||
|
||||
actions: {
|
||||
async persist(token) {
|
||||
if(useCapacitor().getIsNative()) {
|
||||
console.log("On Native")
|
||||
await Preferences.set({
|
||||
key:"token",
|
||||
value: token,
|
||||
})
|
||||
useCookie("token").value = token // persistieren
|
||||
|
||||
} else {
|
||||
console.log("On Web")
|
||||
useCookie("token").value = token // persistieren
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
async initStore() {
|
||||
console.log("Auth initStore")
|
||||
await this.fetchMe()
|
||||
|
||||
if(this.activeTenant > 0) {
|
||||
this.loading = false
|
||||
if(useCapacitor().getIsNative()) {
|
||||
navigateTo("/mobile")
|
||||
} else {
|
||||
navigateTo("/")
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async init(token=null) {
|
||||
console.log("Auth init")
|
||||
await this.fetchMe(token)
|
||||
|
||||
const tempStore = useTempStore()
|
||||
|
||||
if(this.profile.temp_config) tempStore.setStoredTempConfig(this.profile.temp_config)
|
||||
if(this.activeTenant > 0) {
|
||||
this.loading = false
|
||||
if(useCapacitor().getIsNative()) {
|
||||
navigateTo("/mobile")
|
||||
} else {
|
||||
navigateTo("/")
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async login(email: string, password: string) {
|
||||
try {
|
||||
console.log("Auth login")
|
||||
const { token } = await useNuxtApp().$api("/auth/login", {
|
||||
method: "POST",
|
||||
body: { email, password }
|
||||
})
|
||||
console.log("Token: " + token)
|
||||
await this.fetchMe(token)
|
||||
} catch (e) {
|
||||
console.log("login error:" + e)
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
async logout() {
|
||||
console.log("Auth logout")
|
||||
try {
|
||||
await useNuxtApp().$api("/auth/logout", { method: "POST" })
|
||||
} catch (e) {
|
||||
console.error("Logout fehlgeschlagen:", e)
|
||||
}
|
||||
this.user = null
|
||||
this.permissions = []
|
||||
this.profile = null
|
||||
this.activeTenant = null
|
||||
this.tenants = []
|
||||
if(useCapacitor().getIsNative()) {
|
||||
await Preferences.remove({ key: 'token' });
|
||||
useCookie("token").value = null
|
||||
} else {
|
||||
useCookie("token").value = null
|
||||
}
|
||||
|
||||
navigateTo("/login")
|
||||
},
|
||||
|
||||
async fetchMe(jwt= null) {
|
||||
console.log("Auth fetchMe")
|
||||
const tempStore = useTempStore()
|
||||
|
||||
try {
|
||||
const me = await useNuxtApp().$api("/api/me", {
|
||||
headers: { Authorization: `Bearer ${jwt}`,
|
||||
context: {
|
||||
jwt
|
||||
}}
|
||||
})
|
||||
console.log(me)
|
||||
this.user = me.user
|
||||
this.permissions = me.permissions
|
||||
this.tenants = me.tenants
|
||||
|
||||
this.tenants.sort(function (a, b) {
|
||||
if (a.id < b.id) return -1
|
||||
if (a.id > b.id) return 1
|
||||
})
|
||||
|
||||
this.profile = me.profile
|
||||
|
||||
if(this.profile.temp_config) tempStore.setStoredTempConfig(this.profile.temp_config)
|
||||
|
||||
if(me.activeTenant > 0) {
|
||||
this.activeTenant = me.activeTenant
|
||||
this.activeTenantData = me.tenants.find(i => i.id === me.activeTenant)
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
|
||||
} catch (err: any) {
|
||||
if (err?.response?.status === 401) this.logout()
|
||||
}
|
||||
},
|
||||
|
||||
async switchTenant(tenant_id: string) {
|
||||
console.log("Auth switchTenant")
|
||||
this.loading = true
|
||||
const res = await useNuxtApp().$api("/api/tenant/switch", {
|
||||
method: "POST",
|
||||
body: { tenant_id }
|
||||
})
|
||||
console.log(res)
|
||||
|
||||
const {token} = res
|
||||
|
||||
if(useCapacitor().getIsNative()) {
|
||||
await Preferences.set({
|
||||
key:"token",
|
||||
value: token,
|
||||
})
|
||||
} else {
|
||||
useCookie("token").value = token // persistieren
|
||||
|
||||
}
|
||||
await this.init(token)
|
||||
},
|
||||
|
||||
hasPermission(key: string) {
|
||||
return this.permissions.includes(key)
|
||||
}
|
||||
}
|
||||
})
|
||||
2639
frontend/stores/data.js
Normal file
2639
frontend/stores/data.js
Normal file
File diff suppressed because it is too large
Load Diff
190
frontend/stores/labelPrinter.ts
Normal file
190
frontend/stores/labelPrinter.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
import { defineStore } from "pinia"
|
||||
import {
|
||||
Utils,
|
||||
RequestCommandId,
|
||||
ResponseCommandId,
|
||||
NiimbotBluetoothClient,
|
||||
NiimbotSerialClient
|
||||
} from "@mmote/niimbluelib"
|
||||
import { useToast } from "#imports"
|
||||
|
||||
export const useLabelPrinterStore = defineStore("labelPrinter", {
|
||||
state: () => ({
|
||||
client: null as NiimbotBluetoothClient | NiimbotSerialClient | null,
|
||||
connected: false,
|
||||
connectLoading: false,
|
||||
transportLastUsed: "",
|
||||
printProgress: 0,
|
||||
info: {} as any
|
||||
}),
|
||||
|
||||
actions: {
|
||||
|
||||
/** Logging Helper */
|
||||
logger(...args: any[]) {
|
||||
console.debug("[Printer]", ...args)
|
||||
},
|
||||
|
||||
/** --- Client erzeugen --- */
|
||||
newClient(transport: "ble" | "serial" = "serial") {
|
||||
const toast = useToast()
|
||||
|
||||
// alten Client trennen
|
||||
if (this.client) {
|
||||
try { this.client.disconnect() } catch {}
|
||||
}
|
||||
|
||||
// neuen Client erzeugen
|
||||
this.client =
|
||||
transport === "ble"
|
||||
? new NiimbotBluetoothClient()
|
||||
: new NiimbotSerialClient()
|
||||
|
||||
/** Events registrieren */
|
||||
|
||||
this.client.on("printerinfofetched", (e) => {
|
||||
console.log("printerInfoFetched")
|
||||
console.log(e.info)
|
||||
this.info = e.info
|
||||
})
|
||||
|
||||
this.client.on("connect", () => {
|
||||
this.connected = true
|
||||
toast.add({ title: "Drucker verbunden" })
|
||||
this.logger("connected")
|
||||
})
|
||||
|
||||
this.client.on("disconnect", () => {
|
||||
this.connected = false
|
||||
toast.add({ title: "Drucker getrennt" })
|
||||
this.logger("disconnected")
|
||||
})
|
||||
|
||||
this.client.on("printprogress", (e) => {
|
||||
if (e.pagePrintProgress) this.printProgress = e.pagePrintProgress
|
||||
this.logger(
|
||||
`Page ${e.page}/${e.pagesTotal}, Page print ${e.pagePrintProgress}%, Page feed ${e.pageFeedProgress}%`
|
||||
)
|
||||
})
|
||||
|
||||
return this.client
|
||||
},
|
||||
|
||||
/** --- Verbinden --- */
|
||||
async connect(transport: "ble" | "serial" = "serial") {
|
||||
const toast = useToast()
|
||||
|
||||
this.connectLoading = true
|
||||
|
||||
this.newClient(transport)
|
||||
|
||||
try {
|
||||
await this.client!.connect()
|
||||
this.transportLastUsed = transport
|
||||
this.connectLoading = false
|
||||
} catch (err) {
|
||||
console.error("[Printer] Connect failed:", err)
|
||||
toast.add({ title: "Verbindung fehlgeschlagen", color: "red" })
|
||||
this.connectLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
/** --- Trennen --- */
|
||||
async disconnect({ forget = false } = {}) {
|
||||
const toast = useToast()
|
||||
this.logger("Disconnect requested…")
|
||||
|
||||
if (!this.client) return
|
||||
|
||||
try {
|
||||
// Timer stoppen
|
||||
try {
|
||||
if (this.client.heartbeatTimer) {
|
||||
clearInterval(this.client.heartbeatTimer)
|
||||
this.client.heartbeatTimer = null
|
||||
}
|
||||
if (this.client.abstraction?.statusPollTimer) {
|
||||
clearInterval(this.client.abstraction.statusPollTimer)
|
||||
this.client.abstraction.statusPollTimer = null
|
||||
}
|
||||
} catch {}
|
||||
|
||||
await this.client.disconnect?.()
|
||||
|
||||
// Serial-Port schließen
|
||||
const port = (this.client as any).port
|
||||
if (port) {
|
||||
try {
|
||||
if (port.readable) port.readable.cancel?.()
|
||||
if (port.writable) await port.writable.abort?.()
|
||||
await port.close?.()
|
||||
|
||||
if (forget && navigator.serial?.forgetPort) {
|
||||
await navigator.serial.forgetPort(port)
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger("Error closing port:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// BLE GATT
|
||||
if (
|
||||
this.client instanceof NiimbotBluetoothClient &&
|
||||
this.client.device?.gatt?.connected
|
||||
) {
|
||||
try {
|
||||
this.client.device.gatt.disconnect()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
this.connected = false
|
||||
this.client = null
|
||||
toast.add({ title: "Drucker getrennt" })
|
||||
} catch (err) {
|
||||
console.error("[Printer] Disconnect error", err)
|
||||
toast.add({ title: "Fehler beim Trennen", color: "red" })
|
||||
}
|
||||
},
|
||||
|
||||
/** Hilfsfunktion: EncodedImage reparieren */
|
||||
reviveEncodedImage(encoded: any) {
|
||||
if (!encoded?.rowsData) return encoded
|
||||
for (const row of encoded.rowsData) {
|
||||
if (row.rowData && !(row.rowData instanceof Uint8Array)) {
|
||||
row.rowData = new Uint8Array(Object.values(row.rowData))
|
||||
}
|
||||
}
|
||||
return encoded
|
||||
},
|
||||
|
||||
/** --- Drucken --- */
|
||||
async print(encoded: any, options?: { density?: number; pages?: number }) {
|
||||
const toast = useToast()
|
||||
|
||||
if (!this.client) throw new Error("Kein Drucker verbunden")
|
||||
|
||||
const fixed = this.reviveEncodedImage(encoded)
|
||||
const taskName = this.client.getPrintTaskType() ?? "B1"
|
||||
|
||||
const task = this.client.abstraction.newPrintTask(taskName, {
|
||||
totalPages: options?.pages ?? 1,
|
||||
statusPollIntervalMs: 100,
|
||||
statusTimeoutMs: 8000,
|
||||
density: options?.density ?? 5
|
||||
})
|
||||
|
||||
try {
|
||||
this.printProgress = 0
|
||||
await task.printInit()
|
||||
await task.printPage(fixed, options?.pages ?? 1)
|
||||
await task.waitForFinished()
|
||||
toast.add({ title: "Druck abgeschlossen" })
|
||||
} catch (e) {
|
||||
console.error("[Printer] print error", e)
|
||||
toast.add({ title: "Druckfehler", color: "red" })
|
||||
} finally {
|
||||
await task.printEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
162
frontend/stores/profile.js
Normal file
162
frontend/stores/profile.js
Normal file
@@ -0,0 +1,162 @@
|
||||
import {defineStore} from 'pinia'
|
||||
import OneSignal from "onesignal-cordova-plugin";
|
||||
import {Capacitor} from "@capacitor/core";
|
||||
// @ts-ignore
|
||||
export const useProfileStore = defineStore('profile', () => {
|
||||
|
||||
const supabase = useSupabaseClient()
|
||||
const dataStore = useDataStore()
|
||||
const user = useSupabaseUser()
|
||||
const toast = useToast()
|
||||
const router = useRouter()
|
||||
|
||||
const loaded = ref(false)
|
||||
const showProfileSelection = ref(false)
|
||||
const ownTenant = ref({
|
||||
calendarConfig: {
|
||||
eventTypes: []
|
||||
},
|
||||
timeConfig: {
|
||||
timeTypes: []
|
||||
},
|
||||
tags: {
|
||||
documents: [] ,
|
||||
products: []
|
||||
},
|
||||
measures: []
|
||||
})
|
||||
|
||||
|
||||
const profiles = ref([])
|
||||
const ownProfiles = ref([])
|
||||
const activeProfile = ref([])
|
||||
const tenants = ref([])
|
||||
const currentTenant = ref(null)
|
||||
|
||||
|
||||
async function initializeData (userId) {
|
||||
|
||||
let profileconnections = (await supabase.from("profileconnections").select()).data
|
||||
let profiles = (await supabase.from("profiles").select("*, role(*)")).data
|
||||
let activeProfileConnection = profileconnections.find(i => i.active)
|
||||
if(activeProfileConnection) {
|
||||
if(!await useCapacitor().getIsPhone()) {
|
||||
toast.add({title: 'Aktives Profil ausgewählt'})
|
||||
}
|
||||
|
||||
console.log("Active Profile selected")
|
||||
activeProfile.value = profiles.find(i => i.id === activeProfileConnection.profile_id)
|
||||
currentTenant.value = activeProfile.value.tenant
|
||||
|
||||
if(Capacitor.getPlatform() === "ios") {
|
||||
OneSignal.initialize("1295d5ff-28f8-46a6-9c62-fe5d090016d7");
|
||||
OneSignal.Location.setShared(false)
|
||||
OneSignal.Notifications.requestPermission();
|
||||
OneSignal.login(activeProfileConnection.user_id)
|
||||
}
|
||||
|
||||
|
||||
await fetchData()
|
||||
await dataStore.fetchData()
|
||||
} else {
|
||||
toast.add({title: 'Kein aktives Profil', color: 'orange'})
|
||||
await fetchOwnProfiles()
|
||||
await fetchTenants()
|
||||
showProfileSelection.value = true
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
async function changeProfile(newActiveProfileId) {
|
||||
loaded.value = false
|
||||
|
||||
let profileconnections = (await supabase.from("profileconnections").select()).data
|
||||
|
||||
let oldActiveProfileConnection = profileconnections.find(i => i.active)
|
||||
|
||||
const {error} = await supabase.from("profileconnections").update({active: true}).eq("profile_id", newActiveProfileId)
|
||||
|
||||
if(error) {
|
||||
console.log(error)
|
||||
} else {
|
||||
if(oldActiveProfileConnection){
|
||||
const {error} = await supabase.from("profileconnections").update({active: false}).eq("profile_id", oldActiveProfileConnection.profile_id)
|
||||
}
|
||||
|
||||
reloadNuxtApp({
|
||||
path:"/",
|
||||
ttl: 10000
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchData () {
|
||||
await fetchOwnProfiles()
|
||||
await fetchProfiles()
|
||||
|
||||
await fetchTenants()
|
||||
await fetchOwnTenant()
|
||||
|
||||
loaded.value = true
|
||||
console.log("Finished Loading")
|
||||
}
|
||||
|
||||
function clearStore () {
|
||||
loaded.value = false
|
||||
ownTenant.value = {}
|
||||
profiles.value = []
|
||||
ownProfiles.value = []
|
||||
tenants.value = []
|
||||
}
|
||||
|
||||
async function fetchOwnTenant () {
|
||||
ownTenant.value = (await supabase.from("tenants").select().eq('id', currentTenant.value).single()).data
|
||||
}
|
||||
|
||||
async function fetchProfiles () {
|
||||
profiles.value = (await supabase.from("profiles").select().eq("tenant",currentTenant.value).order("lastName")).data
|
||||
}
|
||||
|
||||
async function fetchOwnProfiles () {
|
||||
let profiles = (await supabase.from("profiles").select().order("tenant")).data
|
||||
let conns = (await supabase.from("profileconnections").select()).data.map(i => i.profile_id)
|
||||
ownProfiles.value = profiles.filter(i => conns.includes(i.id))
|
||||
}
|
||||
|
||||
async function fetchTenants () {
|
||||
tenants.value = (await supabase.from("tenants").select().order("id",{ascending: true})).data
|
||||
}
|
||||
|
||||
const getOwnProfile = computed(() => {
|
||||
return profiles.value.find(i => i.id === user.value.id)
|
||||
|
||||
})
|
||||
|
||||
const getProfileById = computed(() => (itemId) => {
|
||||
return profiles.value.find(item => item.id === itemId)
|
||||
})
|
||||
|
||||
return {
|
||||
//General
|
||||
currentTenant,
|
||||
loaded,
|
||||
showProfileSelection,
|
||||
ownTenant,
|
||||
initializeData,
|
||||
changeProfile,
|
||||
|
||||
//Data
|
||||
profiles,
|
||||
ownProfiles,
|
||||
activeProfile,
|
||||
tenants,
|
||||
fetchData,
|
||||
clearStore,
|
||||
fetchOwnTenant,
|
||||
getOwnProfile,
|
||||
getProfileById
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
86
frontend/stores/temp.js
Normal file
86
frontend/stores/temp.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import {defineStore} from 'pinia'
|
||||
|
||||
// @ts-ignore
|
||||
export const useTempStore = defineStore('temp', () => {
|
||||
|
||||
const auth = useAuthStore()
|
||||
|
||||
const searchStrings = ref({})
|
||||
const filters = ref({})
|
||||
const columns = ref({})
|
||||
const pages = ref({})
|
||||
const settings = ref({})
|
||||
|
||||
const storeTempConfig = async () => {
|
||||
const config = {
|
||||
searchStrings: searchStrings.value,
|
||||
columns: columns.value,
|
||||
pages: pages.value,
|
||||
settings: settings.value,
|
||||
filters: filters.value
|
||||
}
|
||||
|
||||
await useNuxtApp().$api(`/api/profiles/${auth.profile.id}`,{
|
||||
method: 'PUT',
|
||||
body: {temp_config: config}
|
||||
})
|
||||
}
|
||||
|
||||
function setStoredTempConfig (config) {
|
||||
searchStrings.value = config.searchStrings
|
||||
columns.value = config.columns
|
||||
pages.value = config.pages
|
||||
settings.value = config.settings
|
||||
filters.value = config.filters || {}
|
||||
}
|
||||
|
||||
function modifySearchString(type,input) {
|
||||
searchStrings.value[type] = input
|
||||
storeTempConfig()
|
||||
}
|
||||
|
||||
function clearSearchString(type) {
|
||||
searchStrings.value[type] = ""
|
||||
storeTempConfig()
|
||||
}
|
||||
|
||||
function modifyFilter(domain,type,input) {
|
||||
if(!filters.value[domain]) filters.value[domain] = {}
|
||||
|
||||
filters.value[domain][type] = input
|
||||
storeTempConfig()
|
||||
}
|
||||
|
||||
function modifyColumns(type,input) {
|
||||
columns.value[type] = input
|
||||
storeTempConfig()
|
||||
}
|
||||
|
||||
function modifyPages(type,input) {
|
||||
pages.value[type] = input
|
||||
storeTempConfig()
|
||||
}
|
||||
|
||||
function modifySettings(type,input) {
|
||||
settings.value[type] = input
|
||||
storeTempConfig()
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
setStoredTempConfig,
|
||||
searchStrings,
|
||||
modifySearchString,
|
||||
clearSearchString,
|
||||
filters,
|
||||
modifyFilter,
|
||||
columns,
|
||||
modifyColumns,
|
||||
modifyPages,
|
||||
pages,
|
||||
modifySettings,
|
||||
settings
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
Reference in New Issue
Block a user