3 Commits

Author SHA1 Message Date
Adrian Zürcher
9e460db854 new release
All checks were successful
Build Quasar SPA and Go Backend for memberApp / build-spa (push) Successful in 8m12s
Build Quasar SPA and Go Backend for memberApp / build-backend (amd64, , linux) (push) Successful in 3m53s
Build Quasar SPA and Go Backend for memberApp / build-backend (amd64, .exe, windows) (push) Successful in 3m37s
Build Quasar SPA and Go Backend for memberApp / build-backend (arm, 6, , linux) (push) Successful in 3m35s
Build Quasar SPA and Go Backend for memberApp / build-backend (arm64, , linux) (push) Successful in 3m39s
2026-02-23 21:23:49 +01:00
Adrian Zürcher
fa58872840 optimize open database calls 2026-02-23 21:23:14 +01:00
Adrian Zürcher
ec5893db57 add new workspace settings for users 2026-02-23 21:21:52 +01:00
25 changed files with 177 additions and 191 deletions

BIN
backend/gagag.dbaa Normal file

Binary file not shown.

View File

@@ -3,7 +3,7 @@ module backend
go 1.25.4 go 1.25.4
require ( require (
gitea.tecamino.com/paadi/access-handler v1.0.48 gitea.tecamino.com/paadi/access-handler v1.0.51
gitea.tecamino.com/paadi/memberDB v1.1.30 gitea.tecamino.com/paadi/memberDB v1.1.30
gitea.tecamino.com/paadi/tecamino-dbm v1.0.0 gitea.tecamino.com/paadi/tecamino-dbm v1.0.0
gitea.tecamino.com/paadi/tecamino-logger v0.2.1 gitea.tecamino.com/paadi/tecamino-logger v0.2.1

View File

@@ -1,5 +1,5 @@
gitea.tecamino.com/paadi/access-handler v1.0.48 h1:PYZvOwR9HCORAFpm7Nd5ZXvWwT5w04OvbcHhVHmPJlw= gitea.tecamino.com/paadi/access-handler v1.0.51 h1:kTPwN+0Zw/Uyfo6el1jl5ORzIrjQdC8PUlczoN9mBS4=
gitea.tecamino.com/paadi/access-handler v1.0.48/go.mod h1:0kUGU4Jw2jSvopCCwecuX/2QnVKS09Ec1KQNrBXvsFs= gitea.tecamino.com/paadi/access-handler v1.0.51/go.mod h1:0kUGU4Jw2jSvopCCwecuX/2QnVKS09Ec1KQNrBXvsFs=
gitea.tecamino.com/paadi/dbHandler v1.1.12 h1:F1ARSTUm0MZmF84FfD/g5RQNMYyDYXHYrB3cXPSi4qw= gitea.tecamino.com/paadi/dbHandler v1.1.12 h1:F1ARSTUm0MZmF84FfD/g5RQNMYyDYXHYrB3cXPSi4qw=
gitea.tecamino.com/paadi/dbHandler v1.1.12/go.mod h1:y/xn/POJg1DO++67uKvnO23lJQgh+XFQq7HZCS9Getw= gitea.tecamino.com/paadi/dbHandler v1.1.12/go.mod h1:y/xn/POJg1DO++67uKvnO23lJQgh+XFQq7HZCS9Getw=
gitea.tecamino.com/paadi/memberDB v1.1.30 h1:N+3V9A/+OAGIoJeUNVHj1qUuBcy6ADLYFIgCnp2Ggk4= gitea.tecamino.com/paadi/memberDB v1.1.30 h1:N+3V9A/+OAGIoJeUNVHj1qUuBcy6ADLYFIgCnp2Ggk4=

View File

@@ -1,6 +1,6 @@
{ {
"name": "lightcontrol", "name": "lightcontrol",
"version": "1.4.0", "version": "1.4.1",
"description": "A Tecamino App", "description": "A Tecamino App",
"productName": "Attendence Records", "productName": "Attendence Records",
"author": "A. Zuercher", "author": "A. Zuercher",

View File

@@ -1,29 +1,35 @@
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { appApi } from './axios';
import { createPinia } from 'pinia'; import { createPinia } from 'pinia';
import { useUserStore } from 'src/vueLib/login/userStore'; import { useUserStore } from 'src/vueLib/login/userStore';
import { useLogin } from 'src/vueLib/login/useLogin'; import { useLogin } from 'src/vueLib/login/useLogin';
import { Me, openDatabase } from 'src/vueLib/components/DatabaseCall';
const pinia = createPinia(); const pinia = createPinia();
export default boot(async ({ app }) => { export default boot(async ({ app }) => {
app.use(pinia); app.use(pinia);
const useStore = useUserStore(); const userStore = useUserStore();
const login = useLogin(); const login = useLogin();
await appApi const resp = await Me().catch(() =>
.get('/login/me') login.logout().catch((err) => {
.then((resp) => { console.error(err);
useStore return;
.setUser({ }),
id: resp.data.id, );
user: resp.data.username,
role: { role: resp.data.role, permissions: [] }, if (!resp) return;
})
.catch((err) => console.error(err)); await userStore
login.refresh().catch((err) => console.error(err)); .setUser({
id: resp.data.id,
user: resp.data.username,
role: { role: resp.data.role, permissions: [] },
workspaceId: resp.data.workspaceId,
settings: resp.data.settings,
}) })
.catch(() => { .catch((err) => console.error(err));
login.logout().catch((err) => console.error(err)); login.refresh().catch((err) => console.error(err));
});
await openDatabase().catch((err) => console.error(err));
}); });

View File

@@ -1,7 +1,7 @@
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { setQuasarInstance } from 'src/vueLib/utils/globalQ'; import { setQuasarInstance } from 'src/vueLib/utils/globalQ';
import { setRouterInstance } from 'src/vueLib/utils/globalRouter'; import { setRouterInstance } from 'src/vueLib/utils/globalRouter';
import { databaseName, logo, appName, workspace } from 'src/vueLib/models/settings'; import { logo, appName } from 'src/vueLib/models/settings';
import { Dark } from 'quasar'; import { Dark } from 'quasar';
import { getLocalDarkMode, getLocalSettings } from 'src/localstorage/localStorage'; import { getLocalDarkMode, getLocalSettings } from 'src/localstorage/localStorage';
@@ -20,8 +20,6 @@ export default boot(({ app, router }) => {
if (settings.appName) { if (settings.appName) {
appName.value = settings.appName; appName.value = settings.appName;
} }
databaseName.value = settings.databaseName;
workspace.value = settings.workspace?.uuid ?? '';
document.documentElement.style.setProperty('--q-primary', settings.primaryColor ?? '#1976d2'); document.documentElement.style.setProperty('--q-primary', settings.primaryColor ?? '#1976d2');
document.documentElement.style.setProperty( document.documentElement.style.setProperty(

View File

@@ -1,15 +1,14 @@
import { boot } from 'quasar/wrappers'; import { boot } from 'quasar/wrappers';
import { useUserStore } from 'src/vueLib/login/userStore'; import { useUserStore } from 'src/vueLib/login/userStore';
import { appApi } from './axios';
import { getLocalLastRoute, setLocalLastRoute } from 'src/localstorage/localStorage'; import { getLocalLastRoute, setLocalLastRoute } from 'src/localstorage/localStorage';
import { Me } from 'src/vueLib/components/DatabaseCall';
export default boot(async ({ router }) => { export default boot(async ({ router }) => {
const userStore = useUserStore(); const userStore = useUserStore();
// load user // load user
try { try {
const { data } = await appApi.get('/login/me'); const data = await Me();
userStore.setFirstLogin(data.newDatabase); userStore.setFirstLogin(data.newDatabase);
data.role.role = data.role; data.role.role = data.role;
await userStore.setUser(data); await userStore.setUser(data);
} catch { } catch {

View File

@@ -111,7 +111,7 @@ async function addAttendees() {
}); });
await updateAttendees(0); await updateAttendees(0);
updateEvents(); await updateEvents();
} }
let resolveNewEvent!: (value: Event) => void; let resolveNewEvent!: (value: Event) => void;

View File

@@ -1,12 +1,10 @@
import { Dark } from 'quasar'; import { Dark } from 'quasar';
import { appName, databaseName, workspace, type Settings } from 'src/vueLib/models/settings'; import { appName, type Settings } from 'src/vueLib/models/settings';
import { ref } from 'vue'; import { ref } from 'vue';
export function setLocalSettings(settings: Settings) { export function setLocalSettings(settings: Settings) {
localStorage.setItem('icon', settings.icon); if (settings.icon !== '') localStorage.setItem('icon', settings.icon);
localStorage.setItem('appName', settings.appName); if (settings.appName !== '') localStorage.setItem('appName', settings.appName);
localStorage.setItem('databaseName', settings.databaseName);
localStorage.setItem('workspace', settings.workspace?.uuid || '');
localStorage.setItem('primaryColor', settings.primaryColor); localStorage.setItem('primaryColor', settings.primaryColor);
localStorage.setItem('primaryColorText', settings.primaryColorText); localStorage.setItem('primaryColorText', settings.primaryColorText);
localStorage.setItem('secondaryColor', settings.secondaryColor); localStorage.setItem('secondaryColor', settings.secondaryColor);
@@ -18,27 +16,14 @@ export function getLocalSettings(): Settings {
if (name === undefined || name === 'undefined') { if (name === undefined || name === 'undefined') {
name = appName.value; name = appName.value;
} }
let db = localStorage.getItem('databaseName');
if (db === undefined || db === 'undefined') {
db = databaseName.value;
}
let iconName = localStorage.getItem('icon'); let iconName = localStorage.getItem('icon');
if (iconName === undefined || iconName === 'undefined') { if (iconName === undefined || iconName === 'undefined') {
iconName = ''; iconName = '';
} }
let ws = localStorage.getItem('workspace');
if (ws === undefined || ws === 'undefined') {
ws = workspace.value || '';
}
return <Settings>{ return <Settings>{
icon: iconName, icon: iconName,
appName: name, appName: name,
workspace: { name: '', description: '', uuid: ws },
databaseName: db,
primaryColor: localStorage.getItem('primaryColor'), primaryColor: localStorage.getItem('primaryColor'),
primaryColorText: localStorage.getItem('primaryColorText'), primaryColorText: localStorage.getItem('primaryColorText'),
secondaryColor: localStorage.getItem('secondaryColor'), secondaryColor: localStorage.getItem('secondaryColor'),
@@ -49,8 +34,6 @@ export function getLocalSettings(): Settings {
export function clearLocalStorage() { export function clearLocalStorage() {
localStorage.removeItem('icon'); localStorage.removeItem('icon');
localStorage.removeItem('appName'); localStorage.removeItem('appName');
localStorage.removeItem('workspace');
localStorage.removeItem('databaseName');
localStorage.removeItem('primaryColor'); localStorage.removeItem('primaryColor');
localStorage.removeItem('primaryColorText'); localStorage.removeItem('primaryColorText');
localStorage.removeItem('secondaryColor'); localStorage.removeItem('secondaryColor');

View File

@@ -147,7 +147,6 @@ import type { Group, Groups } from 'src/vueLib/models/group';
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage'; import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
import html2pdf from 'html2pdf.js'; import html2pdf from 'html2pdf.js';
import type { PageDefault } from 'src/vueLib/models/pageDefaults'; import type { PageDefault } from 'src/vueLib/models/pageDefaults';
import { openDatabase } from 'src/vueLib/components/DatabaseCall';
const filter = ref<string>(''); const filter = ref<string>('');
const group = ref<Group[]>([]); const group = ref<Group[]>([]);
@@ -180,14 +179,8 @@ const columns = computed(() => [
}, },
]); ]);
onMounted(async () => { onMounted(() => {
loading.value = true; loading.value = true;
await openDatabase()
.catch((err) => NotifyResponse(err, 'error'))
.finally(() => {
loading.value = false;
});
appApi appApi
.get('/groups') .get('/groups')
.then((resp) => (groups.value = resp.data)) .then((resp) => (groups.value = resp.data))

View File

@@ -38,7 +38,7 @@
:label="$t('workspaces')" :label="$t('workspaces')"
:options="localUser?.workspaces" :options="localUser?.workspaces"
option-label="name" option-label="name"
v-model="settings.workspace" v-model="localWorkspace"
@update:model-value="changeWorkspace" @update:model-value="changeWorkspace"
></q-select> ></q-select>
<q-select <q-select
@@ -187,7 +187,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { logo, appName, databaseName, workspace } from 'src/vueLib/models/settings'; import { logo, appName } from 'src/vueLib/models/settings';
import { onMounted, reactive, ref, watch } from 'vue'; import { onMounted, reactive, ref, watch } from 'vue';
import { appApi } from 'src/boot/axios'; import { appApi } from 'src/boot/axios';
import { useNotify } from 'src/vueLib/general/useNotify'; import { useNotify } from 'src/vueLib/general/useNotify';
@@ -195,9 +195,11 @@ import type { Settings } from 'src/vueLib/models/settings';
import { useUserStore } from 'src/vueLib/login/userStore'; import { useUserStore } from 'src/vueLib/login/userStore';
import { setLocalSettings } from 'src/localstorage/localStorage'; import { setLocalSettings } from 'src/localstorage/localStorage';
import SiteTitle from 'src/vueLib/general/SiteTitle.vue'; import SiteTitle from 'src/vueLib/general/SiteTitle.vue';
import type { User } from 'src/vueLib/models/user'; import { type User } from 'src/vueLib/models/user';
import { i18n } from 'src/boot/lang'; import { i18n } from 'src/boot/lang';
import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue'; import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue';
import type { Workspace } from 'src/vueLib/models/workspaces';
import { openDatabase } from '../vueLib/components/DatabaseCall';
const { NotifyResponse } = useNotify(); const { NotifyResponse } = useNotify();
const colorGroup = ref(false); const colorGroup = ref(false);
@@ -207,11 +209,11 @@ const newDatabase = ref<string>('');
const foundDatabases = ref<number>(0); const foundDatabases = ref<number>(0);
const localUser = ref<User>(); const localUser = ref<User>();
const databases = ref<string[]>([]); const databases = ref<string[]>([]);
const localWorkspace = ref<Workspace>();
const settings = reactive<Settings>({ const settings = reactive<Settings>({
appName: appName.value, appName: appName.value,
icon: logo.value, icon: logo.value,
databaseName: databaseName.value,
workspace: { name: '', description: '', uuid: workspace.value },
primaryColor: document.documentElement.style.getPropertyValue('--q-primary'), primaryColor: document.documentElement.style.getPropertyValue('--q-primary'),
primaryColorText: document.documentElement.style.getPropertyValue('--q-primary-text'), primaryColorText: document.documentElement.style.getPropertyValue('--q-primary-text'),
secondaryColor: document.documentElement.style.getPropertyValue('--q-secondary'), secondaryColor: document.documentElement.style.getPropertyValue('--q-secondary'),
@@ -224,11 +226,15 @@ onMounted(async () => {
.then((resp) => { .then((resp) => {
if (!resp.data) return; if (!resp.data) return;
localUser.value = resp.data[0]; localUser.value = resp.data[0];
settings.workspace = localUser.value?.settings?.workspace || null; localWorkspace.value = localUser.value?.workspaces?.find(
(w) => w.id === localUser.value?.workspaceId,
);
if (localUser.value) {
settings.databaseName = localUser.value.settings?.databaseName || '';
if (settings.workspace) {
appApi appApi
.post('workspaces/data', settings.workspace) .post('workspaces/data', localWorkspace.value)
.then((resp) => { .then((resp) => {
if (!resp.data) { if (!resp.data) {
settings.databaseName = ''; settings.databaseName = '';
@@ -239,8 +245,6 @@ onMounted(async () => {
foundDatabases.value = resp.data.data.length; foundDatabases.value = resp.data.data.length;
}) })
.catch((err) => NotifyResponse(err, 'error')); .catch((err) => NotifyResponse(err, 'error'));
} else {
settings.databaseName = '';
} }
}) })
.catch((err) => NotifyResponse(err, 'error')); .catch((err) => NotifyResponse(err, 'error'));
@@ -249,7 +253,6 @@ onMounted(async () => {
watch(settings, (newSettings) => { watch(settings, (newSettings) => {
logo.value = newSettings.icon; logo.value = newSettings.icon;
appName.value = newSettings.appName; appName.value = newSettings.appName;
databaseName.value = newSettings.databaseName;
}); });
function resetColors() { function resetColors() {
@@ -265,7 +268,7 @@ function resetColors() {
function changeWorkspace() { function changeWorkspace() {
appApi appApi
.post('workspaces/data', settings.workspace) .post('workspaces/data', localWorkspace.value)
.then((resp) => { .then((resp) => {
if (resp.data) { if (resp.data) {
databases.value = [i18n.global.t('addNewDatabase')]; databases.value = [i18n.global.t('addNewDatabase')];
@@ -277,8 +280,10 @@ function changeWorkspace() {
} }
function changeDatabase() { function changeDatabase() {
if (databases.value.indexOf(settings.databaseName) === 0) { if (settings.databaseName) {
addDatabaseRef.value?.open(); if (databases.value.indexOf(settings.databaseName) === 0) {
addDatabaseRef.value?.open();
}
} }
} }
@@ -297,7 +302,7 @@ function addNewDatabase() {
addDatabaseRef.value.close(); addDatabaseRef.value.close();
} }
function save() { async function save() {
document.documentElement.style.setProperty('--q-primary', settings.primaryColor); document.documentElement.style.setProperty('--q-primary', settings.primaryColor);
document.documentElement.style.setProperty('--q-primary-text', settings.primaryColorText); document.documentElement.style.setProperty('--q-primary-text', settings.primaryColorText);
document.documentElement.style.setProperty('--q-secondary', settings.secondaryColor); document.documentElement.style.setProperty('--q-secondary', settings.secondaryColor);
@@ -309,8 +314,12 @@ function save() {
} }
setLocalSettings(settings); setLocalSettings(settings);
if (localUser.value) {
await user.setUser(localUser.value);
localUser.value.workspaceId = localWorkspace.value?.id;
}
appApi await appApi
.post('users/update', localUser.value) .post('users/update', localUser.value)
.then(() => .then(() =>
NotifyResponse( NotifyResponse(
@@ -324,5 +333,7 @@ function save() {
), ),
) )
.catch((err) => NotifyResponse(err, 'error')); .catch((err) => NotifyResponse(err, 'error'));
await openDatabase().catch((err) => console.error(err));
} }
</script> </script>

View File

@@ -52,7 +52,7 @@ import { appApi } from 'src/boot/axios';
import { i18n } from 'src/boot/lang'; import { i18n } from 'src/boot/lang';
import SiteTitle from 'src/vueLib/general/SiteTitle.vue'; import SiteTitle from 'src/vueLib/general/SiteTitle.vue';
import { useNotify } from 'src/vueLib/general/useNotify'; import { useNotify } from 'src/vueLib/general/useNotify';
import { databaseName, workspace } from 'src/vueLib/models/settings'; import { useUserStore } from 'src/vueLib/login/userStore';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
const stats = ref(); const stats = ref();
@@ -71,8 +71,11 @@ const amounts = ref<{
const { NotifyResponse } = useNotify(); const { NotifyResponse } = useNotify();
onMounted(async () => { onMounted(async () => {
let path = databaseName.value; const user = useUserStore().user;
if (workspace.value !== '') path = workspace.value + '/' + path; const workspaceUuid = useUserStore().getWorkspaceUuid;
let path = user?.settings?.databaseName;
if (workspaceUuid !== undefined) path = workspaceUuid + '/' + path;
stats.value = await appApi stats.value = await appApi
.post('/stats', { database: path }) .post('/stats', { database: path })
.then((resp) => { .then((resp) => {

View File

@@ -1,10 +1,22 @@
import { appApi } from 'src/boot/axios'; import { appApi } from 'src/boot/axios';
import { databaseName, workspace } from '../models/settings'; import { useUserStore } from '../login/userStore';
import type { User } from '../models/users';
import { useNotify } from '../general/useNotify';
export async function openDatabase() { export async function openDatabase() {
let path = databaseName.value; const user = useUserStore().user;
if (workspace.value !== '') {
path = workspace.value + '/' + path; if (!user) return;
const tempUser = (await getUser(user?.id)) as User | void;
if (!tempUser) return;
let path = '';
path = tempUser.settings?.databaseName || '';
if (tempUser.workspaces) {
path = tempUser.workspaces.find((w) => w.id === tempUser.workspaceId)?.uuid || '';
path = path + '/' + tempUser.settings?.databaseName;
} }
return appApi.post('database/open', { return appApi.post('database/open', {
@@ -12,3 +24,20 @@ export async function openDatabase() {
create: true, create: true,
}); });
} }
async function getUser(id: number): Promise<User | null> {
const { NotifyResponse } = useNotify();
return appApi
.get(`/users?id=${id}`)
.then((resp) => {
return resp.data[0];
})
.catch((err) => {
NotifyResponse(err, 'error');
return null;
});
}
export async function Me() {
return appApi.get('/login/me');
}

View File

@@ -5,6 +5,7 @@ import type { Settings } from '../models/settings';
import { appName, logo } from '../models/settings'; import { appName, logo } from '../models/settings';
import { clearLocalStorage, setLocalSettings } from 'src/localstorage/localStorage'; import { clearLocalStorage, setLocalSettings } from 'src/localstorage/localStorage';
import { routerInstance } from 'src/router'; import { routerInstance } from 'src/router';
import { Me } from '../components/DatabaseCall';
const refreshTime = 10000; const refreshTime = 10000;
let intervalId: ReturnType<typeof setInterval> | null = null; let intervalId: ReturnType<typeof setInterval> | null = null;
@@ -31,12 +32,14 @@ export function useLogin() {
setLocalSettings(sets); setLocalSettings(sets);
}); });
const resp = await appApi.get('/login/me'); const resp = await Me();
await userStore await userStore
.setUser({ .setUser({
id: resp.data.id, id: resp.data.id,
user: resp.data.user, user: resp.data.user,
role: { role: resp.data.role, permissions: [] }, role: { role: resp.data.role, permissions: [] },
workspaceId: resp.data.workspace,
}) })
.catch((err) => NotifyResponse(err, 'error')); .catch((err) => NotifyResponse(err, 'error'));
@@ -60,31 +63,31 @@ export function useLogin() {
} }
async function refresh() { async function refresh() {
await appApi await appApi.post('login/refresh', {}, { withCredentials: true }).catch(() => {
.post('login/refresh', {}, { withCredentials: true }) userStore.clearUser();
.then(() => { return;
appApi });
.get('/login/me')
.then((resp) => { const resp = await Me();
userStore if (!resp) {
.setUser({ stopRefreshInterval();
id: resp.data.id, return false;
user: resp.data.user, }
role: { role: resp.data.role, permissions: [] }, userStore
}) .setUser({
.catch((err) => NotifyResponse(err, 'error')); id: resp.data.id,
if (!intervalId) { user: resp.data.user,
startRefreshInterval(); role: { role: resp.data.role, permissions: [] },
} workspaceId: resp.data.workspace,
return true; settings: resp.data.settings,
})
.catch(() => {});
}) })
.catch(() => { .catch((err) => NotifyResponse(err, 'error'));
userStore.clearUser();
}); if (!intervalId) {
stopRefreshInterval(); startRefreshInterval();
return false; }
return true;
} }
function startRefreshInterval() { function startRefreshInterval() {

View File

@@ -50,6 +50,9 @@ export const useUserStore = defineStore('user', {
} }
}; };
}, },
getWorkspaceUuid: (state: UserState) => {
return state.user?.workspaces?.find((w) => w.id === state.user?.workspaceId)?.uuid;
},
}, },
actions: { actions: {
setFirstLogin(b: boolean) { setFirstLogin(b: boolean) {

View File

@@ -1,16 +1,12 @@
import { ref } from 'vue'; import { ref } from 'vue';
import type { Workspace } from './workspaces';
export const logo = ref<string>(''); export const logo = ref<string>('');
export const appName = ref<string>('Attendance Records'); export const appName = ref<string>('Attendance Records');
export const databaseName = ref<string>('members.dba');
export const workspace = ref<string>('');
export type Settings = { export type Settings = {
appName: string; appName: string;
icon: string; icon: string;
databaseName: string; databaseName?: string;
workspace: Workspace | null;
primaryColor: string; primaryColor: string;
primaryColorText: string; primaryColorText: string;
secondaryColor: string; secondaryColor: string;
@@ -21,8 +17,6 @@ export function DefaultSettings(): Settings {
return { return {
appName: 'Attendance Records', appName: 'Attendance Records',
icon: '', icon: '',
databaseName: 'members.dba',
workspace: { name: '', description: '', uuid: workspace.value },
primaryColor: document.documentElement.style.getPropertyValue('--q-primary-text'), primaryColor: document.documentElement.style.getPropertyValue('--q-primary-text'),
primaryColorText: document.documentElement.style.getPropertyValue('--q-primary'), primaryColorText: document.documentElement.style.getPropertyValue('--q-primary'),
secondaryColor: document.documentElement.style.getPropertyValue('--q-secondary'), secondaryColor: document.documentElement.style.getPropertyValue('--q-secondary'),

View File

@@ -1,6 +1,7 @@
import type { Permissions } from '../checkboxes/permissions'; import type { Permissions } from '../checkboxes/permissions';
import type { Role } from './roles'; import type { Role } from './roles';
import type { Settings } from './settings'; import type { Settings } from './settings';
import type { Workspaces } from './workspaces';
export interface User { export interface User {
id: number; id: number;
@@ -9,7 +10,8 @@ export interface User {
permissions?: Permissions; permissions?: Permissions;
settings?: Settings; settings?: Settings;
newDatabase?: boolean; newDatabase?: boolean;
workspaces?: string[]; workspaceId?: number | undefined;
workspaces?: Workspaces;
} }
export interface UserState { export interface UserState {

View File

@@ -12,6 +12,7 @@ export interface User {
password?: string; password?: string;
newPassword?: string; newPassword?: string;
settings?: Settings; settings?: Settings;
workspaceId?: number;
workspaces?: Workspace[]; workspaces?: Workspace[];
} }

View File

@@ -63,10 +63,10 @@ export function useEventTable() {
const loading = ref(false); const loading = ref(false);
//updates Event list from database //updates Event list from database
function updateEvents() { async function updateEvents() {
loading.value = true; loading.value = true;
appApi await appApi
.get('events') .get('events')
.then((resp) => { .then((resp) => {
if (resp.data === null) { if (resp.data === null) {

View File

@@ -116,7 +116,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { appApi } from 'src/boot/axios'; import { appApi } from 'src/boot/axios';
import { ref, onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import type { Event, Events } from 'src/vueLib/models/event'; import type { Event, Events } from 'src/vueLib/models/event';
import EditOneDialog from 'src/components/EditOneDialog.vue'; import EditOneDialog from 'src/components/EditOneDialog.vue';
import EditAllDialog from 'src/components/EventEditAllDialog.vue'; import EditAllDialog from 'src/components/EventEditAllDialog.vue';
@@ -129,7 +129,6 @@ import type { Members } from 'src/vueLib/models/member';
import { i18n } from 'src/boot/lang'; import { i18n } from 'src/boot/lang';
import SearchableInput from '../components/SearchableInput.vue'; import SearchableInput from '../components/SearchableInput.vue';
import TopButtonGroup from '../components/TopButtonGroup.vue'; import TopButtonGroup from '../components/TopButtonGroup.vue';
import { openDatabase } from 'src/vueLib/components/DatabaseCall';
export interface EventDialog { export interface EventDialog {
getSelected: () => Events; getSelected: () => Events;
@@ -149,19 +148,8 @@ const user = useUserStore();
const { Events, pagination, loading, columns, updateEvents } = useEventTable(); const { Events, pagination, loading, columns, updateEvents } = useEventTable();
//load on mounting page
onMounted(async () => { onMounted(async () => {
loading.value = true; await updateEvents();
await openDatabase()
.then(() => {
updateEvents();
})
.catch((err) => NotifyResponse(err, 'error'))
.finally(() => {
loading.value = false;
});
}); });
// opens dialog for all Event values // opens dialog for all Event values
@@ -193,17 +181,17 @@ function openAttendees(eventArray: number, attendees: Members | null) {
} }
//remove Event from database //remove Event from database
function removeEvent(...removeEvents: Events) { async function removeEvent(...removeEvents: Events) {
const EventIds: number[] = []; const EventIds: number[] = [];
removeEvents.forEach((Event: Event) => { removeEvents.forEach((Event: Event) => {
EventIds.push(Event.id); EventIds.push(Event.id);
}); });
appApi await appApi
.post('events/delete', { ids: EventIds }) .post('events/delete', { ids: EventIds })
.then(() => { .then(() => {
updateEvents(); updateEvents().catch((err) => NotifyResponse(err, 'error'));
selected.value = []; selected.value = [];
}) })
.catch((err) => NotifyResponse(err, 'error')) .catch((err) => NotifyResponse(err, 'error'))

View File

@@ -123,7 +123,6 @@ import SearchableInput from '../components/SearchableInput.vue';
import TopButtonGroup from '../components/TopButtonGroup.vue'; import TopButtonGroup from '../components/TopButtonGroup.vue';
import { getAllMembers } from '../members/MembersTable'; import { getAllMembers } from '../members/MembersTable';
import type { Members } from 'src/vueLib/models/member'; import type { Members } from 'src/vueLib/models/member';
import { openDatabase } from 'src/vueLib/components/DatabaseCall';
const { NotifyResponse } = useNotify(); const { NotifyResponse } = useNotify();
const groupDialog = ref(); const groupDialog = ref();
@@ -146,18 +145,8 @@ onMounted(async () => {
loading.value = true; loading.value = true;
members.value = await getAllMembers(); members.value = await getAllMembers();
await updateGroups();
await openDatabase() loading.value = false;
.then(() => {
updateGroups().catch((err) => {
NotifyResponse(err, 'error');
});
})
.catch((err) => NotifyResponse(err, 'error'))
.finally(() => {
loading.value = false;
});
}); });
//opens dialog for one value //opens dialog for one value

View File

@@ -215,7 +215,6 @@ import FilterSelect from '../components/FilterSelect.vue';
import TopButtonGroup from '../components/TopButtonGroup.vue'; import TopButtonGroup from '../components/TopButtonGroup.vue';
import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage'; import { getLocalPageDefaults, setLocalPageDefaults } from 'src/localstorage/localStorage';
import type { PageDefault } from 'src/vueLib/models/pageDefaults'; import type { PageDefault } from 'src/vueLib/models/pageDefaults';
import { openDatabase } from 'src/vueLib/components/DatabaseCall';
const inProps = defineProps({ const inProps = defineProps({
addAttendees: { type: Boolean }, addAttendees: { type: Boolean },
@@ -295,15 +294,10 @@ onMounted(async () => {
// set custom filter // set custom filter
setNewFilter(selectedColumnFilter.value, ...selectedColumnOptions.value); setNewFilter(selectedColumnFilter.value, ...selectedColumnOptions.value);
await openDatabase() await updateMembers(localCompareMembers.value, inProps.addResponsible).catch((err) =>
.then(() => { NotifyResponse(err, 'error'),
updateTable().catch((err) => NotifyResponse(err, 'error')); );
}) loading.value = false;
.catch((err) => NotifyResponse(err, 'error'))
.finally(() => {
loading.value = false;
});
}); });
async function updateTable(add?: Members) { async function updateTable(add?: Members) {

View File

@@ -101,7 +101,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { appApi } from 'src/boot/axios'; import { appApi } from 'src/boot/axios';
import { ref, onMounted } from 'vue'; import { onMounted, ref } from 'vue';
import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue'; import DialogFrame from 'src/vueLib/dialog/DialogFrame.vue';
import MembersTable from '../members/MembersTable.vue'; import MembersTable from '../members/MembersTable.vue';
import type { Members } from 'src/vueLib/models/member'; import type { Members } from 'src/vueLib/models/member';
@@ -113,7 +113,6 @@ import { i18n } from 'src/boot/lang';
import type { Responsible, Responsibles } from 'src/vueLib/models/responsible'; import type { Responsible, Responsibles } from 'src/vueLib/models/responsible';
import SearchableInput from '../components/SearchableInput.vue'; import SearchableInput from '../components/SearchableInput.vue';
import TopButtonGroup from '../components/TopButtonGroup.vue'; import TopButtonGroup from '../components/TopButtonGroup.vue';
import { openDatabase } from 'src/vueLib/components/DatabaseCall';
const { NotifyResponse } = useNotify(); const { NotifyResponse } = useNotify();
const responsibleDialog = ref(); const responsibleDialog = ref();
@@ -128,21 +127,8 @@ const user = useUserStore();
const { responsibleMember, pagination, loading, columns, updateResponsibles } = const { responsibleMember, pagination, loading, columns, updateResponsibles } =
useResponsibleTable(); useResponsibleTable();
//load on mounting page
onMounted(async () => { onMounted(async () => {
loading.value = true; await updateResponsibles();
await openDatabase()
.then(() => {
updateResponsibles().catch((err) => {
NotifyResponse(err, 'error');
});
})
.catch((err) => NotifyResponse(err, 'error'))
.finally(() => {
loading.value = false;
});
}); });
//opens dialog for one value //opens dialog for one value

View File

@@ -5,6 +5,7 @@ import { i18n } from 'boot/lang';
import type { Roles } from 'src/vueLib/models/roles'; import type { Roles } from 'src/vueLib/models/roles';
import { useUserStore } from 'src/vueLib/login/userStore'; import { useUserStore } from 'src/vueLib/login/userStore';
import { useLogin } from 'src/vueLib/login/useLogin'; import { useLogin } from 'src/vueLib/login/useLogin';
import { Me } from 'src/vueLib/components/DatabaseCall';
export const roles = ref<Roles>([]); export const roles = ref<Roles>([]);
@@ -64,21 +65,27 @@ export function useRoleTable() {
.finally(() => { .finally(() => {
loading.value = false; loading.value = false;
}); });
await appApi
.get('/login/me') const resp = await Me().catch(() => {
.then((resp) => { login.logout().catch((err) => {
userStore NotifyResponse(err, 'error');
.setUser({ return;
id: resp.data.id,
user: resp.data.user,
role: { role: resp.data.role, permissions: [] },
})
.catch((err) => NotifyResponse(err, 'error'));
login.refresh().catch((err) => NotifyResponse(err, 'error'));
})
.catch(() => {
login.logout().catch((err) => NotifyResponse(err, 'error'));
}); });
});
if (!resp) return;
await userStore
.setUser({
id: resp.data.id,
user: resp.data.user,
role: { role: resp.data.role, permissions: [] },
workspaceId: resp.data.workspaceId,
settings: resp.data.settings,
})
.catch((err) => NotifyResponse(err, 'error'));
login.refresh().catch((err) => NotifyResponse(err, 'error'));
} }
return { return {
roles, roles,

View File

@@ -129,8 +129,7 @@ import { i18n } from 'src/boot/lang';
import { QTable } from 'quasar'; import { QTable } from 'quasar';
import { useUserStore } from 'src/vueLib/login/userStore'; import { useUserStore } from 'src/vueLib/login/userStore';
import SearchableInput from '../components/SearchableInput.vue'; import SearchableInput from '../components/SearchableInput.vue';
import type { User } from 'src/vueLib/models/user'; import { type User } from 'src/vueLib/models/user';
import { workspace } from 'src/vueLib/models/settings';
const { NotifyResponse } = useNotify(); const { NotifyResponse } = useNotify();
const editOneDialog = ref(); const editOneDialog = ref();
@@ -179,8 +178,11 @@ function openRemoveDialog(...workspaces: Workspaces) {
function removeWorkspace(...removeWorkspaces: Workspaces) { function removeWorkspace(...removeWorkspaces: Workspaces) {
const workspaces: Workspace[] = []; const workspaces: Workspace[] = [];
const user = useUserStore().user;
const usedWorkspaceId = user?.workspaces?.find((w) => w.uuid === user.workspaceId)?.id;
removeWorkspaces.forEach((workspace: Workspace) => { removeWorkspaces.forEach((workspace: Workspace) => {
if (workspace.id === currentUser.value?.settings?.workspace) { if (workspace.id === usedWorkspaceId) {
NotifyResponse(i18n.global.t('notPossibleToDeleteUsedWorkspace'), 'error'); NotifyResponse(i18n.global.t('notPossibleToDeleteUsedWorkspace'), 'error');
} else if (workspace.id) { } else if (workspace.id) {
workspaces.push(workspace); workspaces.push(workspace);
@@ -192,11 +194,6 @@ function removeWorkspace(...removeWorkspaces: Workspaces) {
workspaces: workspaces, workspaces: workspaces,
}) })
.then(() => { .then(() => {
const storageWorkspace = localStorage.getItem('workspace');
if (workspaces.some((w) => w.uuid === storageWorkspace)) {
workspace.value = '';
localStorage.removeItem('workspace');
}
updateWorkspaces().catch((err) => NotifyResponse(err, 'error')); updateWorkspaces().catch((err) => NotifyResponse(err, 'error'));
if (workspaces.length === 1) { if (workspaces.length === 1) {
NotifyResponse("'" + workspaces[0]?.name + "' " + i18n.global.t('deleted'), 'warning'); NotifyResponse("'" + workspaces[0]?.name + "' " + i18n.global.t('deleted'), 'warning');