From e569420ca5f23ccd4623ceee77b2f2276bfc91ef Mon Sep 17 00:00:00 2001 From: Rohan Sircar Date: Fri, 24 Jul 2020 11:31:11 +0530 Subject: [PATCH] added paginated users table --- package.json | 3 +- src/main/frontend/admin/main.ts | 16 +- .../frontend/admin/pages/user/ViewUsers.ts | 35 ++++ src/main/frontend/common/ajax/Messages.ts | 12 +- src/main/frontend/common/ajax/Users.ts | 15 ++ src/main/frontend/common/dto/AdminUserDTO.ts | 6 + src/main/frontend/common/routes/Admin.ts | 6 +- src/main/frontend/common/util/Util.ts | 16 ++ .../resources/static/css/admin-custom.css | 42 ++-- src/main/resources/static/css/admin-table.css | 8 + .../static/css/dataTables.bootstrap4.min.css | 1 + src/main/resources/templates/admin/users.html | 180 ++++-------------- .../resources/templates/fragments/admin.html | 51 +++-- yarn.lock | 7 + 14 files changed, 213 insertions(+), 185 deletions(-) create mode 100644 src/main/frontend/admin/pages/user/ViewUsers.ts create mode 100644 src/main/frontend/common/ajax/Users.ts create mode 100644 src/main/frontend/common/dto/AdminUserDTO.ts create mode 100644 src/main/frontend/common/util/Util.ts create mode 100644 src/main/resources/static/css/admin-table.css create mode 100644 src/main/resources/static/css/dataTables.bootstrap4.min.css diff --git a/package.json b/package.json index b9a7265..8080137 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "dependencies": { "@types/bootbox": "^5.2.0", + "@types/datatables.net": "^1.10.19", "@types/dompurify": "^2.0.0", "@types/jquery": "^3.3.31", "@types/markdown-it": "^0.0.9", @@ -58,4 +59,4 @@ "watch": "watchify src/main/frontend/chat/main.ts -p [ tsify --target ES6 --noImplicitAny ] --debug -o src/main/resources/static/js/bundle.js", "watch-admin": "watchify src/main/frontend/admin/main.ts -p [ tsify --target ES6 --noImplicitAny ] --debug -o src/main/resources/static/js/adminBundle.js" } -} \ No newline at end of file +} diff --git a/src/main/frontend/admin/main.ts b/src/main/frontend/admin/main.ts index 8e5dc98..2709c1c 100644 --- a/src/main/frontend/admin/main.ts +++ b/src/main/frontend/admin/main.ts @@ -4,18 +4,24 @@ import log from "loglevel"; import { AlertifyNotificationService } from "../common/service/AlertifyNotificationService"; import { stats } from "./pages/Home"; import { Credentials } from "../common/global/Credentials"; +import { getOtherUsers } from "../common/ajax/Users"; +import { viewUsers } from "./pages/user/ViewUsers"; log.setLevel("TRACE"); const es = EncryptionServiceFactory.getEncryptionService(); const ns = new AlertifyNotificationService(); -const authToken = Credentials.authToken +const authToken = Credentials.authToken; $("#changePassphraseForm").on("submit", (event) => { event.preventDefault(); changePassphrase(es, ns, authToken); }); -const pathMatcher = (path: string) => window.location.pathname == path +const pathMatcher = (path: string) => window.location.pathname == path; -if(pathMatcher("/admin")) { - stats() -} \ No newline at end of file +if (pathMatcher("/admin")) { + stats(); +} +if (pathMatcher("/admin/users")) { + // $("#usersTableBody").html(""); + viewUsers(authToken); +} diff --git a/src/main/frontend/admin/pages/user/ViewUsers.ts b/src/main/frontend/admin/pages/user/ViewUsers.ts new file mode 100644 index 0000000..4625d53 --- /dev/null +++ b/src/main/frontend/admin/pages/user/ViewUsers.ts @@ -0,0 +1,35 @@ +import moment from "moment"; +import { capitalize } from "../../../common/util/Util"; + +export async function viewUsers(authToken: string) { + // const users = await getOtherUsers(authToken); + + const usersTable = $("#usersTable").DataTable({ + ajax: { + url: "/api/admin/get/users", + headers: { + "X-AUTH-TOKEN": authToken, + }, + dataSrc: "", + }, + columns: [ + { data: "id" }, + { data: "userName" }, + { + data: "role", + render: (data: string) => { + return capitalize(data.replace("_", " ").toLowerCase()); + }, + }, + { + data: "joinDate", + render: (data: string, type) => { + return type === "sort" + ? data + : moment.utc(data).format("DD/MM/YY").toString(); + }, + }, + ], + lengthMenu: [2, 10, 25, 50, 75, 100], + }); +} diff --git a/src/main/frontend/common/ajax/Messages.ts b/src/main/frontend/common/ajax/Messages.ts index 8173746..eb7492c 100644 --- a/src/main/frontend/common/ajax/Messages.ts +++ b/src/main/frontend/common/ajax/Messages.ts @@ -8,7 +8,7 @@ import { StatsDTO } from "../dto/StatsDTO"; import { createApiHeaders } from "./util"; export async function getAllMessages(user: string, authToken: string) { - let response = await fetch(`${Routes.Admin.getAllMessagesURL}${user}`, { + let response = await fetch(`${Routes.Admin.getAllMessages}${user}`, { method: "GET", headers: createApiHeaders(authToken), }); @@ -16,7 +16,7 @@ export async function getAllMessages(user: string, authToken: string) { } async function getAllRegularUsers(authToken: string) { - let response = await fetch(`${Routes.Admin.getAllRegularUsersURL}`, { + let response = await fetch(`${Routes.Admin.getOtherUsers}`, { method: "GET", headers: createApiHeaders(authToken), }); @@ -34,7 +34,7 @@ export async function sendReencryptedMessages( // headers.append('Accept','application/json') headers.append("Content-Type", "application/json"); headers.append("X-AUTH-TOKEN", authToken); - fetch(Routes.Admin.reencryptURL, { + fetch(Routes.Admin.reencrypt, { method: "POST", headers: headers, body: JSON.stringify(rencryptionDTOs), @@ -74,6 +74,6 @@ export async function getStats(authToken: string) { const response = await fetch("/api/stats/", { headers: createApiHeaders(authToken), method: "GET", - }) - return (await response.json()) as StatsDTO; -} \ No newline at end of file + }); + return (await response.json()) as StatsDTO; +} diff --git a/src/main/frontend/common/ajax/Users.ts b/src/main/frontend/common/ajax/Users.ts new file mode 100644 index 0000000..2562ca2 --- /dev/null +++ b/src/main/frontend/common/ajax/Users.ts @@ -0,0 +1,15 @@ +import { Routes } from "../routes/Routes"; +import { createApiHeaders } from "./util"; +import { AdminUserDTO } from "../dto/AdminUserDTO"; + +export async function getOtherUsers( + authToken: string +): Promise { + const response = await fetch(Routes.Admin.getOtherUsers, { + method: "GET", + headers: createApiHeaders(authToken), + }); + + let data = (await response.json()) as AdminUserDTO[]; + return data; +} diff --git a/src/main/frontend/common/dto/AdminUserDTO.ts b/src/main/frontend/common/dto/AdminUserDTO.ts new file mode 100644 index 0000000..c087bea --- /dev/null +++ b/src/main/frontend/common/dto/AdminUserDTO.ts @@ -0,0 +1,6 @@ +export interface AdminUserDTO { + id: number; + userName: string; + role: string; + joinDate: string; +} diff --git a/src/main/frontend/common/routes/Admin.ts b/src/main/frontend/common/routes/Admin.ts index 6a5e2b7..8d3ae93 100644 --- a/src/main/frontend/common/routes/Admin.ts +++ b/src/main/frontend/common/routes/Admin.ts @@ -1,5 +1,5 @@ export namespace Admin { - export const getAllMessagesURL = `/api/admin/get/messages/`; //hostAddress set in thymeleaf backend - export const reencryptURL = `/api/admin/post/re-encrypt`; - export const getAllRegularUsersURL = `/api/admin/get/users`; + export const getAllMessages = `/api/admin/get/messages/`; //hostAddress set in thymeleaf backend + export const reencrypt = `/api/admin/post/re-encrypt`; + export const getOtherUsers = `/api/admin/get/users`; } diff --git a/src/main/frontend/common/util/Util.ts b/src/main/frontend/common/util/Util.ts new file mode 100644 index 0000000..dc3c114 --- /dev/null +++ b/src/main/frontend/common/util/Util.ts @@ -0,0 +1,16 @@ +/** + * Capitalizes first letters of words in string. + * @param {string} str String to be modified + * @param {boolean=false} lower Whether all other letters should be lowercased + * @return {string} + * @usage + * capitalize('fix this string'); // -> 'Fix This String' + * capitalize('javaSCrIPT'); // -> 'JavaSCrIPT' + * capitalize('javaSCrIPT', true); // -> 'Javascript' + */ +export function capitalize(str: string, lower = false): string { + return (lower ? str.toLowerCase() : str).replace( + /(?:^|\s|["'([{])+\S/g, + (match) => match.toUpperCase() + ); +} diff --git a/src/main/resources/static/css/admin-custom.css b/src/main/resources/static/css/admin-custom.css index 080ab42..7c13b0d 100644 --- a/src/main/resources/static/css/admin-custom.css +++ b/src/main/resources/static/css/admin-custom.css @@ -1,33 +1,33 @@ .text-primary-dark { - /* color: #67b2fd; */ - color: #7accff; + /* color: #67b2fd; */ + color: #7accff; } .border-left-primary-dark { - border-left: 0.25rem solid #7accff !important; + border-left: 0.25rem solid #7accff !important; } .sidebar .nav-item .collapse .collapse-inner .collapse-item, .sidebar .nav-item .collapsing .collapse-inner .collapse-item { - padding: 0.5rem 1rem; - margin: 0 0.5rem; - display: block; - color: #fff; - text-decoration: none; - border-radius: 0.35rem; - white-space: nowrap; + padding: 0.5rem 1rem; + margin: 0 0.5rem; + display: block; + color: #fff; + text-decoration: none; + border-radius: 0.35rem; + white-space: nowrap; } .sidebar-dark .nav-item .nav-link i { - color: rgba(255, 255, 255, 0.7); + color: rgba(255, 255, 255, 0.7); } .sidebar-dark .nav-item .nav-link { - color: rgba(255, 255, 255, 0.95); + color: rgba(255, 255, 255, 0.95); } .sidebar-dark .nav-item .nav-link[data-toggle="collapse"]::after { - color: rgba(255, 255, 255, 0.8); + color: rgba(255, 255, 255, 0.8); } .sidebar .nav-item .collapse .collapse-inner .collapse-item:hover, @@ -40,7 +40,7 @@ } */ .text-info { - color: #5bd6e8 !important; + color: #5bd6e8 !important; } /* :root { @@ -48,14 +48,18 @@ } */ .sidebar-dark hr.sidebar-divider { - border-top: 1px solid rgba(255, 255, 255, 0.5); + border-top: 1px solid rgba(255, 255, 255, 0.5); } body { - background: #333; - color: #fff; + background: #333; + color: #fff; } #wrapper #content-wrapper { - background-color: #333; -} \ No newline at end of file + background-color: #333; +} + +table.table-bordered.dataTable { + border-right-width: 1px; +} diff --git a/src/main/resources/static/css/admin-table.css b/src/main/resources/static/css/admin-table.css new file mode 100644 index 0000000..78ec3e4 --- /dev/null +++ b/src/main/resources/static/css/admin-table.css @@ -0,0 +1,8 @@ +label, +#usersTable_info, +.dataTables_paginate, +.paginate_button, +#usersTable_next, +#usersTable_previous { + color: white !important; +} diff --git a/src/main/resources/static/css/dataTables.bootstrap4.min.css b/src/main/resources/static/css/dataTables.bootstrap4.min.css new file mode 100644 index 0000000..f1930be --- /dev/null +++ b/src/main/resources/static/css/dataTables.bootstrap4.min.css @@ -0,0 +1 @@ +table.dataTable{clear:both;margin-top:6px !important;margin-bottom:6px !important;max-width:none !important;border-collapse:separate !important;border-spacing:0}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;box-sizing:content-box}table.dataTable td.dataTables_empty,table.dataTable th.dataTables_empty{text-align:center}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}div.dataTables_wrapper div.dataTables_length label{font-weight:normal;text-align:left;white-space:nowrap}div.dataTables_wrapper div.dataTables_length select{width:auto;display:inline-block}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter label{font-weight:normal;white-space:nowrap;text-align:left}div.dataTables_wrapper div.dataTables_filter input{margin-left:0.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_info{padding-top:0.85em;white-space:nowrap}div.dataTables_wrapper div.dataTables_paginate{margin:0;white-space:nowrap;text-align:right}div.dataTables_wrapper div.dataTables_paginate ul.pagination{margin:2px 0;white-space:nowrap;justify-content:flex-end}div.dataTables_wrapper div.dataTables_processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-26px;text-align:center;padding:1em 0}table.dataTable thead>tr>th.sorting_asc,table.dataTable thead>tr>th.sorting_desc,table.dataTable thead>tr>th.sorting,table.dataTable thead>tr>td.sorting_asc,table.dataTable thead>tr>td.sorting_desc,table.dataTable thead>tr>td.sorting{padding-right:30px}table.dataTable thead>tr>th:active,table.dataTable thead>tr>td:active{outline:none}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{cursor:pointer;position:relative}table.dataTable thead .sorting:before,table.dataTable thead .sorting:after,table.dataTable thead .sorting_asc:before,table.dataTable thead .sorting_asc:after,table.dataTable thead .sorting_desc:before,table.dataTable thead .sorting_desc:after,table.dataTable thead .sorting_asc_disabled:before,table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:before,table.dataTable thead .sorting_desc_disabled:after{position:absolute;bottom:0.9em;display:block;opacity:0.3}table.dataTable thead .sorting:before,table.dataTable thead .sorting_asc:before,table.dataTable thead .sorting_desc:before,table.dataTable thead .sorting_asc_disabled:before,table.dataTable thead .sorting_desc_disabled:before{right:1em;content:"\2191"}table.dataTable thead .sorting:after,table.dataTable thead .sorting_asc:after,table.dataTable thead .sorting_desc:after,table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:after{right:0.5em;content:"\2193"}table.dataTable thead .sorting_asc:before,table.dataTable thead .sorting_desc:after{opacity:1}table.dataTable thead .sorting_asc_disabled:before,table.dataTable thead .sorting_desc_disabled:after{opacity:0}div.dataTables_scrollHead table.dataTable{margin-bottom:0 !important}div.dataTables_scrollBody table{border-top:none;margin-top:0 !important;margin-bottom:0 !important}div.dataTables_scrollBody table thead .sorting:before,div.dataTables_scrollBody table thead .sorting_asc:before,div.dataTables_scrollBody table thead .sorting_desc:before,div.dataTables_scrollBody table thead .sorting:after,div.dataTables_scrollBody table thead .sorting_asc:after,div.dataTables_scrollBody table thead .sorting_desc:after{display:none}div.dataTables_scrollBody table tbody tr:first-child th,div.dataTables_scrollBody table tbody tr:first-child td{border-top:none}div.dataTables_scrollFoot>.dataTables_scrollFootInner{box-sizing:content-box}div.dataTables_scrollFoot>.dataTables_scrollFootInner>table{margin-top:0 !important;border-top:none}@media screen and (max-width: 767px){div.dataTables_wrapper div.dataTables_length,div.dataTables_wrapper div.dataTables_filter,div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_paginate{text-align:center}}table.dataTable.table-sm>thead>tr>th{padding-right:20px}table.dataTable.table-sm .sorting:before,table.dataTable.table-sm .sorting_asc:before,table.dataTable.table-sm .sorting_desc:before{top:5px;right:0.85em}table.dataTable.table-sm .sorting:after,table.dataTable.table-sm .sorting_asc:after,table.dataTable.table-sm .sorting_desc:after{top:5px}table.table-bordered.dataTable th,table.table-bordered.dataTable td{border-left-width:0}table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable td:last-child,table.table-bordered.dataTable td:last-child{border-right-width:0}table.table-bordered.dataTable tbody th,table.table-bordered.dataTable tbody td{border-bottom-width:0}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0}div.table-responsive>div.dataTables_wrapper>div.row{margin:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^="col-"]:first-child{padding-left:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^="col-"]:last-child{padding-right:0} diff --git a/src/main/resources/templates/admin/users.html b/src/main/resources/templates/admin/users.html index 4eec3aa..bcc0f87 100644 --- a/src/main/resources/templates/admin/users.html +++ b/src/main/resources/templates/admin/users.html @@ -12,9 +12,9 @@
-
+
@@ -57,156 +57,56 @@
Users
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
User IDUser NameRoleRegistration DateAction
1Item 1$1
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
2Item 2$2
+
+ + + + + + + + + + + + + +
User IDUser NameRoleRegistration Date
+
+
+ - + - - +
-
+ + - + - - - - -
- -
+ +
+ +
- + diff --git a/src/main/resources/templates/fragments/admin.html b/src/main/resources/templates/fragments/admin.html index 59a114a..deaffa8 100644 --- a/src/main/resources/templates/fragments/admin.html +++ b/src/main/resources/templates/fragments/admin.html @@ -17,33 +17,62 @@ - - - - - - - + + + - + + + + + + + + + + + + + + - + - + + + + + + + + + + diff --git a/yarn.lock b/yarn.lock index 2c39c40..821efd1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,6 +9,13 @@ dependencies: "@types/jquery" "*" +"@types/datatables.net@^1.10.19": + version "1.10.19" + resolved "https://registry.yarnpkg.com/@types/datatables.net/-/datatables.net-1.10.19.tgz#17c5f94433f761086131c6c8dc055a0e1099d1f9" + integrity sha512-WuzgytEmsIpVYZbkce+EvK1UqBI7/cwcC/WgYeAtXdq2zi+yWzJwMT5Yb6irAiOi52DBjeAEeRt3bYzFYvHWCQ== + dependencies: + "@types/jquery" "*" + "@types/dompurify@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.0.0.tgz#9616caa5bf2569aea2e4889d4f929d968c081b40"