Compare commits

..

No commits in common. "3f4f8a5fd4a926b59f65cf9c1f20d3644e9964dc" and "94c45fe3f4336a98389f935b16cdbe6dea687f4d" have entirely different histories.

8 changed files with 6 additions and 322 deletions

View File

@ -1,6 +1,6 @@
{
"license": "MIT",
"version": "1.1.0",
"version": "1.0.0",
"name": "@flaschengeist/users",
"author": "Ferdinand Thiessen <rpm@fthiessen.de>",
"homepage": "https://flaschengeist.dev/Flaschengeist",

View File

@ -1,66 +0,0 @@
<template>
<q-card>
<q-card-section class="text-h6"> Einstellungen Benutzer </q-card-section>
<q-card-section>
<q-select
v-model="displayNameMode"
:options="options"
label="Anzeige des Namens"
emit-value
map-options
input-debounce="0"
filled
/>
</q-card-section>
</q-card>
</template>
<script lang="ts">
import { defineComponent, onBeforeMount, computed } from 'vue';
import { useUserStore } from '@flaschengeist/api';
import { DisplayNameMode } from '../models';
export default defineComponent({
name: 'SettingWidget',
setup() {
const store = useUserStore();
onBeforeMount(() => {
void store.getDisplayNameModeSetting(true);
});
const displayNameMode = computed({
get: () => store.userSettings.display_name || DisplayNameMode.DISPLAYNAME,
set: (val) => {
console.log('set', val);
void store.setDisplayNameModeSetting(val);
},
});
const options = [
{
label: 'Anzeigename',
value: DisplayNameMode.DISPLAYNAME,
},
{
label: 'Vorname',
value: DisplayNameMode.FIRSTNAME,
},
{
label: 'Nachname',
value: DisplayNameMode.LASTNAME,
},
{
label: 'Vor- und Nachname',
value: DisplayNameMode.FIRSTNAME_LASTNAME,
},
{
label: 'Nachname, Vorname',
value: DisplayNameMode.LASTNAME_FIRSTNAME,
},
];
return {
displayNameMode,
options,
};
},
});
</script>

View File

@ -4,19 +4,15 @@
filled
:label="label"
:options="users"
:option-label="showName"
option-label="display_name"
option-value="userid"
map-options
use-input
input-debounce="0"
@filter="filterFn"
/>
</template>
<script lang="ts">
import { computed, defineComponent, PropType, onBeforeMount, ref } from 'vue';
import { computed, defineComponent, PropType, onBeforeMount } from 'vue';
import { useUserStore } from '@flaschengeist/api';
import { DisplayNameMode } from '../models';
export default defineComponent({
name: 'UserSelector',
@ -30,64 +26,17 @@ export default defineComponent({
onBeforeMount(() => {
void userStore.getUsers(false);
void userStore.getDisplayNameModeSetting(true);
});
const users = computed(() =>
userStore.users.filter((user) => {
let names = filter.value.toLowerCase().split(' ');
if (names.length < 1) {
return true;
}
if (names.length === 1) {
let name = names[0];
return (
user.lastname.toLowerCase().includes(name) ||
user.firstname.toLowerCase().includes(name)
);
}
if (names.length === 2) {
let name1 = names[0];
let name2 = names[1];
return (
(user.lastname.toLowerCase().includes(name1) &&
user.firstname.toLowerCase().includes(name2)) ||
(user.lastname.toLowerCase().includes(name2) &&
user.firstname.toLowerCase().includes(name1))
);
}
return true;
})
);
const filter = ref<string>('');
const filterFn = (val: string, update: () => void) => {
filter.value = val;
update();
};
const users = computed(() => userStore.users);
const selected = computed({
get: () => props.modelValue,
set: (value: FG.User | undefined) => (value ? emit('update:modelValue', value) : undefined),
});
function showName(user: FG.User) {
switch (userStore.userSettings.display_name) {
case DisplayNameMode.DISPLAYNAME:
return user.display_name;
case DisplayNameMode.FIRSTNAME:
return user.firstname;
case DisplayNameMode.LASTNAME:
return user.lastname;
case DisplayNameMode.FIRSTNAME_LASTNAME:
return `${user.firstname} ${user.lastname}`;
case DisplayNameMode.LASTNAME_FIRSTNAME:
return `${user.lastname}, ${user.firstname}`;
}
}
return {
selected,
users,
filterFn,
showName,
};
},
});

View File

@ -1,7 +1,6 @@
import { FG_Plugin } from '@flaschengeist/types';
import { defineAsyncComponent } from 'vue';
import routes from './routes';
import { DisplayNameMode } from './models';
const plugin: FG_Plugin.Plugin = {
id: 'users',
@ -17,15 +16,6 @@ const plugin: FG_Plugin.Plugin = {
widget: defineAsyncComponent(() => import('./components/Widget.vue')),
},
],
settingWidgets: [
{
priority: 1,
name: 'userSettings',
permissions: [],
widget: defineAsyncComponent(() => import('./components/SettingWidget.vue')),
},
],
};
export default plugin;
export { DisplayNameMode };

View File

@ -12,12 +12,3 @@ export interface LoginResponse {
export interface CurrentUserResponse extends FG.User {
permissions: FG.Permission[];
}
export enum DisplayNameMode {
FIRSTNAME = 'firstname',
LASTNAME = 'lastname',
FULLNAME = 'fullname',
DISPLAYNAME = 'display_name',
FIRSTNAME_LASTNAME = 'firstname_lastname',
LASTNAME_FIRSTNAME = 'lastname_firstname',
}

View File

@ -1,153 +0,0 @@
<template>
<q-page>
<q-table
:columns="cols"
:rows="users"
:filter="filter"
:grid="grid"
:pagination="initialPagination"
>
<template v-slot:body-cell-avatar="props">
<q-td :key="props.key" :props="props">
<user-avatar v-model="props.row" />
</q-td>
</template>
<template v-slot:top-right="props">
<q-input v-model="filter" filled dense debounce="300" placeholder="Suche">
<template v-slot:append>
<q-icon name="mdi-magnify" />
</template>
</q-input>
<q-btn
flat
round
dense
:icon="grid ? 'mdi-land-rows-horizontal' : 'mdi-card-account-details'"
@click="grid = !grid"
/>
</template>
<template v-slot:item="props">
<div class="q-pa-xs col-xs-12 col-sm-6 col-md-4">
<q-card bordered>
<div class="row justify-center">
<q-img :src="avatarURL(props.row.userid)">
<template #error>
<div
class="row fit justify-center items-center"
style="background-color: transparent"
>
<img src="no-image.svg" style="object-fit: contain; height: 100%" />
</div>
</template>
</q-img>
</div>
<q-card-section>
<div class="text-h6">{{ props.row.firstname }} {{ props.row.lastname }}</div>
<div class="text-caption">{{ props.row.display_name }}</div>
</q-card-section>
<q-card-section>
<div class="row items-center">
<q-btn
flat
dense
icon="mdi-email"
:href="'mailto:' + props.row.mail"
class="q-mr-xs"
no-caps
/>
<div class="text-caption">{{ props.row.mail }}</div>
</div>
<div class="row items-center">
<q-btn
flat
dense
icon="mdi-calendar"
class="q-mr-xs"
no-caps
v-if="props.row.birthday"
/>
<div class="text-caption" v-if="props.row.birthday">
{{ props.row.birthday.toLocaleDateString() }}
</div>
</div>
</q-card-section>
</q-card>
</div>
</template>
</q-table>
</q-page>
</template>
<script lang="ts">
import { defineComponent, onBeforeMount, computed, ref } from 'vue';
import { useUserStore, avatarURL } from '@flaschengeist/api';
import { UserAvatar } from '@flaschengeist/api/components';
const cols = [
{
name: 'avatar',
label: 'Profilbild',
sortable: false,
},
{
name: 'firstName',
label: 'Vorname',
field: 'firstname',
sortable: true,
},
{
name: 'lastName',
label: 'Nachname',
field: 'lastname',
sortable: true,
},
{
name: 'displayName',
label: 'Anzeigename',
field: 'display_name',
sortable: true,
},
{
name: 'mail',
label: 'E-Mail',
field: 'mail',
sortable: true,
},
{
name: 'birthday',
label: 'Geburtstag',
field: 'birthday',
sortable: true,
format: (val: Date | undefined) => {
return val?.toLocaleDateString();
},
},
];
export default defineComponent({
components: { UserAvatar },
setup() {
const userStore = useUserStore();
onBeforeMount(() => {
void userStore.getUsers(true);
});
const filter = ref('');
const users = computed(() => userStore.users);
const grid = ref(false);
return {
users,
cols,
filter,
grid,
avatarURL,
initialPagination: {
sortBy: 'lastName',
descending: false,
page: 1,
rowsPerPage: 0,
// rowsNumber: xx if getting data from a server
},
};
},
});
</script>

View File

@ -7,9 +7,6 @@
</q-card-section>
<MainUserSettings :user="currentUser" @update:user="updateUser" />
</q-card>
<div v-for="(item, index) in widgets" :key="index" class="full-height col-12">
<component :is="item.widget" />
</div>
<div class="col-12 text-left text-h6">Aktive Sessions:</div>
<user-session
v-for="(session, index) in sessions"
@ -17,17 +14,15 @@
v-model="sessions[index]"
@delete="removeSession(session)"
/>
<q-btn label="List Widgets" @click="listWidgets" />
</q-page>
</div>
</template>
<script lang="ts">
import { useMainStore, useUserStore, useSessionStore, hasPermissions } from '@flaschengeist/api';
import { useMainStore, useUserStore, useSessionStore } from '@flaschengeist/api';
import MainUserSettings from '../components/settings/MainUserSettings.vue';
import { defineComponent, onBeforeMount, ref, computed, inject } from 'vue';
import { defineComponent, onBeforeMount, ref } from 'vue';
import UserSession from '../components/settings/UserSession.vue';
import { FG_Plugin } from '@flaschengeist/types';
import { Notify } from 'quasar';
export default defineComponent({
@ -52,29 +47,17 @@ export default defineComponent({
progress: true,
actions: [{ icon: 'mdi-close', color: 'white' }],
});
console.log(widgets);
}
function removeSession(s: FG.Session) {
sessions.value = sessions.value.filter((ss) => ss.token !== s.token);
}
const flaschengeist = inject<FG_Plugin.Flaschengeist>('flaschengeist');
const widgets = computed(() =>
flaschengeist?.settingWidgets.filter((widget) => hasPermissions(widget.permissions))
);
function listWidgets() {
console.log(widgets);
}
return {
currentUser,
sessions,
updateUser,
removeSession,
widgets,
listWidgets,
};
},
});

View File

@ -10,16 +10,6 @@ const mainRoutes: FG_Plugin.MenuRoute[] = [
permissions: ['user'],
route: { path: 'user', name: 'user', redirect: { name: 'user-settings' } },
children: [
{
title: 'Mitglieder',
icon: 'mdi-account-multiple',
shortcut: true,
route: {
path: 'members',
name: 'user-members',
component: () => import('../pages/Members.vue'),
},
},
{
title: 'Einstellungen',
icon: 'mdi-account-edit',