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 = () => { 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 = (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 = () => { 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 = () => { 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") ) } } }