Files
kbcrm/Client/Components/auth/userpage.ts
T

365 lines
15 KiB
TypeScript

import m from "mithril"
import {hasAppRole, iUser} from "./models/users"
import {iApplication} from "./models/applications"
import {iRole} from "./models/roles"
import {Treenode, iTreenode, Treetable, iTreetable, TopbarButton, iTopbarButton} from "../widgets/Treetable_V2"
import {gridMountpoints, maingrid} from "../mainlayout"
import {ModalView, ModalType, iModalView} from "../widgets/Modal"
import { iWrongEditSelectionMessage, WrongEditSelectionMessage, showServerError} from "./authmessages"
interface iTabelrow {
rowtype: any
col1: any
cols: any[]
info?: {
parentuserid: number
parentappid: number
roleid: number
ischecked: boolean
}
}
const localDataModel = {
userlist: [] as iUser[],
applist: [] as iApplication[],
usernodes: [] as iTreenode[],
startexpanded: false,
loadData: () => {
Promise.all([
m.request({
method: "GET",
url: "/auth/getusers"
}).then((result) => localDataModel.userlist = result as iUser[]),
m.request({
method: "GET",
url: "/auth/getapplications"
}).then((result) => localDataModel.applist = result as iApplication[]),
])
.then(() => {
localDataModel.usernodes = []
localDataModel.userlist.map((u) => {
let userrow: iTabelrow = {
rowtype: 'user',
col1: u.userid,
cols: [u.username, u.email, u.telefon]
}
let usernode: iTreenode = new Treenode(userrow, localDataModel.startexpanded, [])
localDataModel.applist.forEach((app: iApplication) => {
let approw: iTabelrow = {
rowtype: "app",
col1: app.appid,
cols: [app.name, app.minversion],
info: {
ischecked: false,
parentuserid: u.id,
parentappid: null,
roleid: null
}
}
let appnode: Treenode = new Treenode(approw, localDataModel.startexpanded, [])
app.roles.forEach((role: iRole) => {
let ischecked = hasAppRole(u, app.id, role.id)
let rolerow: iTabelrow = {
rowtype: "role",
col1: role.name,
cols: [role.roleid],
info: {
parentuserid: u.id,
parentappid: app.id,
ischecked: ischecked,
roleid: role.id
}
}
let rolenode: iTreenode = new Treenode(rolerow, localDataModel.startexpanded, [])
appnode.children.push(rolenode)
})
usernode.children.push(appnode)
})
localDataModel.usernodes.push(usernode)
})
})
.catch((e) => {
if (e.code == 401) {
m.route.set("/login")
}
})
},
updateUser: (user: iUser) => {
m.request ({
method: "POST", // Hier muss POST verwendet werden, weil GET keinen Body hat und Parameter nur im URL transportieren kann
url: "/auth/updateuser",
body: user
})
.catch((e) => {showServerError(e)})
.finally(() => localDataModel.loadData())
},
deleteUsers: (users: iUser[]) => {
m.request({
method: "POST",
url: "/auth/deleteusers",
body: users.map((u: iUser) => {return u.userid})
})
.catch((e) => {showServerError(e)})
.finally(() => localDataModel.loadData())
},
applyUserAppRole: (tablerow: iTabelrow) => {
m.request ({
method: "POST",
url: "/auth/applyuserapprole",
body: tablerow.info
})
.catch((e) => {showServerError(e); localDataModel.loadData()})
}
}
export const userlist: m.ClosureComponent = (vnode) => {
return {
oninit: (vnode) => {
localDataModel.loadData()
},
view: (vnode) => {
return m("div.w3-container.w3-content", {style: {'max-width': 'min-content', display: 'flex', 'flex-direction': 'column', height: 'inherit', 'overflow-y': 'inherit'}},
m("h1", "Benutzerliste"),
m("div", {style: {flex: '1', 'overflow-y': 'inherit'}},
m(Treetable,
{
startexpanded: localDataModel.startexpanded,
treenodes: localDataModel.usernodes,
columnmapper: (node: iTreenode) => {
let row: iTabelrow = node.nodevalue
let isChecked = hasCheckedChilds(node)
if (row.rowtype == "user") {
return [
m("strong", row.col1),
m("strong", row.cols[0]),
m(isChecked?"div.w3-text-green":"div.w3-text-red", {style: {display: 'flex'}},
// m("div", {style: {padding: '0px', height:'24px', width: '24px'}},
// m("i.material-icons", isChecked?null:"dangerous"),
// ),
m("div", isChecked?"aktiv":"inaktiv")
),
m("div.w3-small",
m("div", row.cols[1]),
m("div", row.cols[2])
),
m(EditUserButton, {userid: row.col1} as iEditUserButton),
m(DeleteUserButton, {userid: row.col1} as iDeleteUserButton)
]
}
else if (row.rowtype == "role") {
return [
m("span.w3-small", row.col1),
m("span.w3-button", {
style: {padding: '0px'},
onclick: () => {
row.info.ischecked = !row.info.ischecked
localDataModel.applyUserAppRole(row)
}
},
m("i.material-icons.w3-theme-light", row.info.ischecked?"check_box":"check_box_outline_blank")
),
m("span.w3-small", row.cols[0]),
m("span.w3-small", row.cols[1]),
m("span", ""),
m("span", "")
]
}
else {
// rowtype "app"
let isChecked = hasCheckedChilds(node)
return [
m(isChecked?"span":"strong.w3-text-red", row.col1),
m("div", {style: {padding: '0px', height:'24px', width: '24px'}},
m("i.material-icons", isChecked?"check":null)
),
m("span", row.cols[0]),
m("span", row.cols[1]),
m("span", ""),
m("span", "")
]
}
}
} as iTreetable,
m(TopbarButton, {
onclick: () => {
m.mount(gridMountpoints.modalhook, {
view: (vnode) => {
return m(UserForm, {user: {id: -1} as iUser})
}
})
}} as iTopbarButton,
m("i.material-icons", "add")
)
)
)
)
}
}
}
function hasCheckedChilds(parenttreenode: iTreenode): boolean {
return parenttreenode.children.some((treenode: iTreenode) => {
if (treenode.hasChildren) {
return hasCheckedChilds(treenode)
}
let tablerow = treenode.nodevalue as iTabelrow
return tablerow.info.ischecked
})
}
interface iUserForm {
user: iUser
}
const UserForm: m.ClosureComponent<iUserForm> = () => {
let form: HTMLFormElement = null;
let currentuser: iUser
function reset (e) {
form.reset()
}
function unmount () {
m.mount(gridMountpoints.modalhook, null)
}
function submit (e) {
e.preventDefault()
let formdata: FormData = new FormData(e.target as HTMLFormElement)
for (let element of formdata) {
currentuser[element[0]] = element[1]
}
localDataModel.updateUser(currentuser)
unmount()
}
return {
oninit: (vnode) => {
currentuser = (vnode.attrs.user === undefined) ? {} as iUser : vnode.attrs.user
},
view: (vnode) => {
return m(ModalView,
{
title: "Benutzer",
type: ModalType.MESSAGE,
width: 'max-content'
} as iModalView,
m("form.w3-padding-16", {oncreate: (vnode) => {form = vnode.dom as HTMLFormElement}, onsubmit: submit},
m("label", "ID (intern)"),
m("div.w3-padding-small", currentuser.id),
m("label", "Benutzer-ID"),
m("input.w3-input.w3-border", {name: 'userid', type: 'text', value: currentuser.userid}),
m("label", "Name"),
m("input.w3-input.w3-border", {name: 'username', type: 'text', value: currentuser.username}),
m("label", "E-Mail"),
m("input.w3-input.w3-border", {name: 'email', type: 'email', value: currentuser.email}),
m("label", "Telefon"),
m("input.w3-input.w3-border", {name: 'telefon', type: 'tel', value: currentuser.telefon}),
m("div.w3-bar.w3-padding-16",
[
m("button.w3-button.w3-theme.w3-right", {style: {'margin-left': '4px'}, type: "submit"}, "Speichern"),
m("button.w3-button.w3-theme.w3-right", {style: {'margin-left': '4px'}, onclick: unmount, type: "button"}, "Abbrechen"),
m("button.w3-button.w3-theme.w3-right", {onclick: reset, type: "button"}, "Zurücksetzen")
]
)
)
)
}
}
}
const WrongUserEditSelectionMessage: m.Component = {
view: () => {
return m(WrongEditSelectionMessage, {elementname: "Benutzer"} as iWrongEditSelectionMessage)
}
}
interface iDeleteUsersMessage {
userid: string
}
const DeleteUsersMessage: m.ClosureComponent<iDeleteUsersMessage> = (vnode) => {
let users: iUser[]
return {
oninit: (vnode) => {
users = localDataModel.userlist.filter((u: iUser) => {
return u.userid == vnode.attrs.userid
})
},
view: (vnode) => {
return (
m(ModalView,
{
title: "Benutzer löschen",
type: ModalType.MESSAGE,
width: '33%'
} as iModalView,
m("p", "Sie wollen den folgenden Benutzer löschen:"),
m("ul",
users.map((user: iUser) => {
return m("li", `${user.userid} (${user.username})`)
})
),
m("div.w3-bar.w3-padding-16",
[
m("button.w3-button.w3-theme.w3-right",
{
style: {'margin-left': '4px'},
onclick: () => {
localDataModel.deleteUsers(users);
m.mount(gridMountpoints.modalhook, null)
}}, "Löschen"),
m("button.w3-button.w3-theme.w3-right", {onclick: () => {m.mount(gridMountpoints.modalhook, null)}}, "Abbrechen"),
]
)
)
)
}
}
}
interface iDeleteUserButton {
userid: string
}
export const DeleteUserButton: m.ClosureComponent<iDeleteUserButton> = () => {
return {
view: (vnode) => {
return m("button.w3-button.w3-bar-item", {
style: {'min-width': '24px', height: '24px', padding: '0'},
onclick: () => {
m.mount(gridMountpoints.modalhook, {view: () => {
return m(DeleteUsersMessage, {userid: vnode.attrs.userid} as iDeleteUsersMessage)
}})
},
title: "Benutzer löschen"
},
m("i.material-icons", "delete_forever")
)
}
}
}
interface iEditUserButton {
userid: string
}
export const EditUserButton: m.ClosureComponent<iEditUserButton> = () => {
return {
view: (vnode) => {
return m("button.w3-button.w3-bar-item", {
style: {'min-width': '24px', height: '24px', padding: '0'},
onclick: () => {
let users: iUser[] = localDataModel.userlist.filter((u:iUser) => {
return u.userid == vnode.attrs.userid
})
m.mount(gridMountpoints.modalhook, {view: () => {
return m(UserForm, {user: users[0]} as iUserForm)
}})
},
title: "Benutzer bearbeiten"
},
m("i.material-icons", "edit")
)
}
}
}