Added stats to admin UI #11

This commit is contained in:
Rohan Sircar 2020-07-22 12:35:46 +05:30
parent 77bff3924c
commit 05ba6fbacc
12 changed files with 181 additions and 92 deletions

View File

@ -2,11 +2,20 @@ import { changePassphrase } from "./pages/user/ChangePassphrase";
import { EncryptionServiceFactory } from "../common/service/EncryptionServiceFactory"; import { EncryptionServiceFactory } from "../common/service/EncryptionServiceFactory";
import log from "loglevel"; import log from "loglevel";
import { AlertifyNotificationService } from "../common/service/AlertifyNotificationService"; import { AlertifyNotificationService } from "../common/service/AlertifyNotificationService";
import { stats } from "./pages/Home";
import { Credentials } from "../common/global/Credentials";
log.setLevel("TRACE"); log.setLevel("TRACE");
const es = EncryptionServiceFactory.getEncryptionService(); const es = EncryptionServiceFactory.getEncryptionService();
const ns = new AlertifyNotificationService(); const ns = new AlertifyNotificationService();
const authToken = Credentials.authToken
$("#changePassphraseForm").on("submit", (event) => { $("#changePassphraseForm").on("submit", (event) => {
event.preventDefault(); event.preventDefault();
changePassphrase(es, ns); changePassphrase(es, ns, authToken);
}); });
const pathMatcher = (path: string) => window.location.pathname == path
if(pathMatcher("/admin")) {
stats()
}

View File

@ -0,0 +1,21 @@
import { getStats } from "../../common/ajax/Messages";
import { Credentials } from "../../common/global/Credentials";
import log from "loglevel";
export async function stats(): Promise<void> {
const stats = await getStats(Credentials.authToken);
$("#totalMessages").each((i, el) => {
el.textContent = stats.totalMessages.toString();
});
$("#totalUsers").each((i, el) => {
el.textContent = stats.totalUsers.toString();
});
$("#totalOnlineUsers").each((i, el) => {
el.textContent = stats.totalOnlineUsers.toString();
});
$("#numMessagesToday").each((i, el) => {
el.textContent = stats.numMessagesToday.toString();
});
log.debug(stats);
}

View File

@ -7,15 +7,16 @@ import { Credentials } from "../../../common/global/Credentials";
import { MessageCipher } from "../../../common/entity/MessageCipher"; import { MessageCipher } from "../../../common/entity/MessageCipher";
import log from "loglevel"; import log from "loglevel";
import { NotificationService } from "../../../common/service/NotificationService"; import { NotificationService } from "../../../common/service/NotificationService";
import { isPassphraseValid } from "../../../common/util/passphrase"; import { isPassphraseValid } from "../../../common/util/Passphrase";
import { import {
getAllMessages, getAllMessages,
sendReencryptedMessages, sendReencryptedMessages,
} from "../../../common/ajax/messages"; } from "../../../common/ajax/Messages";
export async function changePassphrase( export async function changePassphrase(
es: EncryptionService, es: EncryptionService,
ns: NotificationService ns: NotificationService,
authToken: string
): Promise<void> { ): Promise<void> {
// $("#changePassphraseForm").val(); // $("#changePassphraseForm").val();
@ -26,7 +27,7 @@ export async function changePassphrase(
const passphraseOld: string = $("#passphraseOld").val() as string; const passphraseOld: string = $("#passphraseOld").val() as string;
const passphraseNew: string = $("#passphraseNew").val() as string; const passphraseNew: string = $("#passphraseNew").val() as string;
const valid = await isPassphraseValid(passphraseOld, user, es); const valid = await isPassphraseValid(passphraseOld, user, es, authToken);
if (!valid) { if (!valid) {
log.error("Please check your passphrase"); log.error("Please check your passphrase");

View File

@ -4,27 +4,21 @@ import { ReencryptionDTO } from "../dto/ReencryptionDTO";
import { ChatMessageDTO } from "../dto/ChatMessageDTO"; import { ChatMessageDTO } from "../dto/ChatMessageDTO";
import { JsonAPI } from "../../chat/singleton/JsonAPI"; import { JsonAPI } from "../../chat/singleton/JsonAPI";
import * as log from "loglevel"; import * as log from "loglevel";
import { StatsDTO } from "../dto/StatsDTO";
import { createApiHeaders } from "./util";
export async function getAllMessages(user: string, authToken: string) { export async function getAllMessages(user: string, authToken: string) {
let headers = new Headers();
// headers.append('Accept','application/json')
// headers.append('Content-Type', 'application/json');
headers.append("X-AUTH-TOKEN", authToken);
let response = await fetch(`${Routes.Admin.getAllMessagesURL}${user}`, { let response = await fetch(`${Routes.Admin.getAllMessagesURL}${user}`, {
method: "GET", method: "GET",
headers: headers, headers: createApiHeaders(authToken),
}); });
return response.json() as Promise<ReencryptionDTO[]>; return response.json() as Promise<ReencryptionDTO[]>;
} }
async function getAllRegularUsers(authToken: string) { async function getAllRegularUsers(authToken: string) {
let headers = new Headers();
// headers.append('Accept','application/json')
// headers.append('Content-Type', 'application/json');
headers.append("X-AUTH-TOKEN", authToken);
let response = await fetch(`${Routes.Admin.getAllRegularUsersURL}`, { let response = await fetch(`${Routes.Admin.getAllRegularUsersURL}`, {
method: "GET", method: "GET",
headers: headers, headers: createApiHeaders(authToken),
}); });
let data = (await response.json()) as string[]; let data = (await response.json()) as string[];
return data; return data;
@ -49,19 +43,14 @@ export async function sendReencryptedMessages(
export async function getOneMessage( export async function getOneMessage(
toUser: string, toUser: string,
page: number page: number,
authToken: string
): Promise<ChatMessageDTO[]> { ): Promise<ChatMessageDTO[]> {
const headers = new Headers();
if (JsonAPI.authToken == null) {
log.error("authToken null");
return [];
}
headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
const url = Sprintf(JsonAPI.CHAT_MESSAGE_PAGE_GET, toUser, page, 1); const url = Sprintf(JsonAPI.CHAT_MESSAGE_PAGE_GET, toUser, page, 1);
log.debug(url); log.debug(url);
const response = await fetch(url, { const response = await fetch(url, {
method: "GET", method: "GET",
headers: headers, headers: createApiHeaders(authToken),
}); });
log.debug(response.clone()); log.debug(response.clone());
// if (fetchErrorHandler(response.clone(), this._notificationService)) { // if (fetchErrorHandler(response.clone(), this._notificationService)) {
@ -80,3 +69,11 @@ export async function getOneMessage(
const data2 = func(data); const data2 = func(data);
return data2; return data2;
} }
export async function getStats(authToken: string) {
const response = await fetch("/api/stats/", {
headers: createApiHeaders(authToken),
method: "GET",
})
return (await response.json()) as StatsDTO;
}

View File

@ -0,0 +1,9 @@
export function createApiHeaders(authToken: string): Headers {
const headers = new Headers();
// headers.append("Content-Type", "application/json");
headers.append("X-AUTH-TOKEN", authToken);
return headers;
}

View File

@ -0,0 +1,6 @@
export interface StatsDTO {
totalMessages: number;
totalOnlineUsers: number;
totalUsers: number;
numMessagesToday: number;
}

View File

@ -0,0 +1,3 @@
export namespace Stats {
export const rootStats = "/api/stats/"
}

View File

@ -1,14 +1,15 @@
import { EncryptionService } from "../service/EncryptionService"; import { EncryptionService } from "../service/EncryptionService";
import { ChatMessageDTO } from "../dto/ChatMessageDTO"; import { ChatMessageDTO } from "../dto/ChatMessageDTO";
import * as log from "loglevel"; import * as log from "loglevel";
import { getOneMessage } from "../ajax/messages"; import { getOneMessage } from "../ajax/Messages";
export async function isPassphraseValid( export async function isPassphraseValid(
passphrase: string, passphrase: string,
userName: string, userName: string,
es: EncryptionService es: EncryptionService,
authToken: string
): Promise<boolean> { ): Promise<boolean> {
const messages: ChatMessageDTO[] = await getOneMessage(userName, 0); const messages: ChatMessageDTO[] = await getOneMessage(userName, 0, authToken);
if (messages.length === 0) return true; if (messages.length === 0) return true;
try { try {
es.decrypt(passphrase, messages[0].messageCipher); es.decrypt(passphrase, messages[0].messageCipher);

View File

@ -8,14 +8,9 @@
<script src="https://code.jquery.com/jquery-2.1.4.min.js" th:if="false"></script> <script src="https://code.jquery.com/jquery-2.1.4.min.js" th:if="false"></script>
<script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js" <script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js"
data-template-prefix="../" defer="defer" th:if="false"></script> data-template-prefix="../" defer="defer" th:if="false"></script>
<!-- <script th:src="@{/js/admin.js}" src="../../static/js/admin.js" defer="defer"></script> -->
<script th:src="@{/js/adminBundle.js}" src="../../static/js/adminBundle.js" defer="defer"></script>
<script th:src="@{/js/sb-admin-2.js}" src="../../static/js/sb-admin-2.js" defer="defer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.js"></script>
<link th:href="@{/css/sb-admin-2.css}" href="../../static/css/sb-admin-2.css" rel="stylesheet"> <th:block th:include="fragments/admin :: headFragment"></th:block>
<link th:href="@{/css/admin-custom.css}" href="../../static/css/admin-custom.css" rel="stylesheet">
</head> </head>
<!-- TODO <!-- TODO
Make user admin / remove user from admin Make user admin / remove user from admin

View File

@ -6,14 +6,10 @@
<title id="pageTitle">Admin Home</title> <title id="pageTitle">Admin Home</title>
</div> </div>
<script src="https://code.jquery.com/jquery-2.1.4.min.js" th:if="false"></script> <script src="https://code.jquery.com/jquery-2.1.4.min.js" th:if="false"></script>
<script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js" data-template-prefix="../" defer="defer" th:if="false"></script> <script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js"
<script th:src="@{js/admin.js}" src="../../static/js/admin.js" defer="defer"></script> data-template-prefix="../" defer="defer" th:if="false"></script>
<script th:src="@{js/sb-admin-2.js}" src="../../static/js/sb-admin-2.js" defer="defer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.js"></script>
<link th:href="@{/css/sb-admin-2.css}" href="../../static/css/sb-admin-2.css" rel="stylesheet"> <th:block th:include="fragments/admin :: headFragment"></th:block>
<link th:href="@{/css/admin-custom.css}" href="../../static/css/admin-custom.css" rel="stylesheet">
</head> </head>
<!-- TODO <!-- TODO
Make user admin / remove user from admin Make user admin / remove user from admin
@ -45,7 +41,8 @@
<!-- Page Heading --> <!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4"> <div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-light">Dashboard</h1> <h1 class="h3 mb-0 text-light">Dashboard</h1>
<a href="#" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm"><i class="fas fa-download fa-sm text-white-50"></i> Generate Report</a> <a href="#" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm"><i
class="fas fa-download fa-sm text-white-50"></i> Generate Report</a>
</div> </div>
<!-- Content Row --> <!-- Content Row -->
@ -53,15 +50,18 @@
<!-- Earnings (Monthly) Card Example --> <!-- Earnings (Monthly) Card Example -->
<div class="col-xl-3 col-md-6 mb-4"> <div class="col-xl-3 col-md-6 mb-4">
<div class="card text-light border border-dark bg-dark border-left-primary-dark shadow h-100 py-2"> <div
class="card text-light border border-dark bg-dark border-left-primary-dark shadow h-100 py-2">
<div class="card-body"> <div class="card-body">
<div class="row no-gutters align-items-center"> <div class="row no-gutters align-items-center">
<div class="col mr-2"> <div class="col mr-2">
<div class="text-xs font-weight-bold text-primary-dark text-uppercase mb-1">Earnings (Monthly)</div> <div
<div class="h5 mb-0 font-weight-bold ">$40,000</div> class="text-xs font-weight-bold text-primary-dark text-uppercase mb-1">
Total Users</div>
<div class="h5 mb-0 font-weight-bold " id="totalUsers">0</div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<i class="fas fa-calendar fa-2x text-gray-300"></i> <i class="fas fa-users fa-2x text-gray-300"></i>
</div> </div>
</div> </div>
</div> </div>
@ -70,15 +70,17 @@
<!-- Earnings (Monthly) Card Example --> <!-- Earnings (Monthly) Card Example -->
<div class="col-xl-3 col-md-6 mb-4"> <div class="col-xl-3 col-md-6 mb-4">
<div class="card text-light border border-dark bg-dark border-left-success shadow h-100 py-2"> <div
class="card text-light border border-dark bg-dark border-left-success shadow h-100 py-2">
<div class="card-body"> <div class="card-body">
<div class="row no-gutters align-items-center"> <div class="row no-gutters align-items-center">
<div class="col mr-2"> <div class="col mr-2">
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Earnings (Annual)</div> <div class="text-xs font-weight-bold text-success text-uppercase mb-1">
<div class="h5 mb-0 font-weight-bold text-light">$215,000</div> Total Messages</div>
<div class="h5 mb-0 font-weight-bold text-light" id="totalMessages">0</div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<i class="fas fa-dollar-sign fa-2x text-gray-300"></i> <i class="fas fa-comments fa-2x text-gray-300"></i>
</div> </div>
</div> </div>
</div> </div>
@ -91,20 +93,23 @@
<div class="card-body"> <div class="card-body">
<div class="row no-gutters align-items-center"> <div class="row no-gutters align-items-center">
<div class="col mr-2"> <div class="col mr-2">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Tasks</div> <div class="text-xs font-weight-bold text-info text-uppercase mb-1">Users Online
</div>
<div class="row no-gutters align-items-center"> <div class="row no-gutters align-items-center">
<div class="col-auto"> <div class="col-auto">
<div class="h5 mb-0 mr-3 font-weight-bold text-white">50%</div> <div class="h5 mb-0 mr-3 font-weight-bold text-white" id="totalOnlineUsers">0</div>
</div> </div>
<div class="col"> <!-- <div class="col">
<div class="progress progress-sm mr-2"> <div class="progress progress-sm mr-2">
<div class="progress-bar bg-info" role="progressbar" style="width: 50%" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100"></div> <div class="progress-bar bg-info" role="progressbar"
</div> style="width: 50%" aria-valuenow="50" aria-valuemin="0"
aria-valuemax="100"></div>
</div> </div>
</div> -->
</div> </div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<i class="fas fa-clipboard-list fa-2x text-gray-300"></i> <i class="fas fa-user fa-2x text-gray-300"></i>
</div> </div>
</div> </div>
</div> </div>
@ -113,15 +118,17 @@
<!-- Pending Requests Card Example --> <!-- Pending Requests Card Example -->
<div class="col-xl-3 col-md-6 mb-4"> <div class="col-xl-3 col-md-6 mb-4">
<div class="card text-light border border-dark bg-dark border-left-warning shadow h-100 py-2"> <div
class="card text-light border border-dark bg-dark border-left-warning shadow h-100 py-2">
<div class="card-body"> <div class="card-body">
<div class="row no-gutters align-items-center"> <div class="row no-gutters align-items-center">
<div class="col mr-2"> <div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Pending Requests</div> <div class="text-xs font-weight-bold text-warning text-uppercase mb-1">
<div class="h5 mb-0 font-weight-bold text-white">18</div> Messages Today</div>
<div class="h5 mb-0 font-weight-bold text-white">0</div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<i class="fas fa-comments fa-2x text-gray-300"></i> <i class="fas fa-comment fa-2x text-gray-300"></i>
</div> </div>
</div> </div>
</div> </div>
@ -137,13 +144,16 @@
<div class="col-xl-8 col-lg-7"> <div class="col-xl-8 col-lg-7">
<div class="card bg-dark border border-dark text-white shadow mb-4"> <div class="card bg-dark border border-dark text-white shadow mb-4">
<!-- Card Header - Dropdown --> <!-- Card Header - Dropdown -->
<div class="card-header bg-secondary border border-secondary py-3 d-flex flex-row align-items-center justify-content-between"> <div
class="card-header bg-secondary border border-secondary py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-white">Earnings Overview</h6> <h6 class="m-0 font-weight-bold text-white">Earnings Overview</h6>
<div class="dropdown no-arrow"> <div class="dropdown no-arrow">
<a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-ellipsis-v fa-sm fa-fw text-white"></i> <i class="fas fa-ellipsis-v fa-sm fa-fw text-white"></i>
</a> </a>
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in" aria-labelledby="dropdownMenuLink"> <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"
aria-labelledby="dropdownMenuLink">
<div class="dropdown-header">Dropdown Header:</div> <div class="dropdown-header">Dropdown Header:</div>
<a class="dropdown-item" href="#">Action</a> <a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a> <a class="dropdown-item" href="#">Another action</a>
@ -165,13 +175,16 @@
<div class="col-xl-4 col-lg-5"> <div class="col-xl-4 col-lg-5">
<div class="card bg-dark border border-dark text-white shadow mb-4"> <div class="card bg-dark border border-dark text-white shadow mb-4">
<!-- Card Header - Dropdown --> <!-- Card Header - Dropdown -->
<div class="card-header bg-secondary border border-secondary py-3 d-flex flex-row align-items-center justify-content-between"> <div
class="card-header bg-secondary border border-secondary py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-white">Revenue Sources</h6> <h6 class="m-0 font-weight-bold text-white">Revenue Sources</h6>
<div class="dropdown no-arrow"> <div class="dropdown no-arrow">
<a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-ellipsis-v fa-sm fa-fw text-white"></i> <i class="fas fa-ellipsis-v fa-sm fa-fw text-white"></i>
</a> </a>
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in" aria-labelledby="dropdownMenuLink"> <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"
aria-labelledby="dropdownMenuLink">
<div class="dropdown-header">Dropdown Header:</div> <div class="dropdown-header">Dropdown Header:</div>
<a class="dropdown-item" href="#">Action</a> <a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a> <a class="dropdown-item" href="#">Another action</a>
@ -213,25 +226,35 @@
<h6 class="m-0 font-weight-bold text-white">Projects</h6> <h6 class="m-0 font-weight-bold text-white">Projects</h6>
</div> </div>
<div class="card-body"> <div class="card-body">
<h4 class="small font-weight-bold">Server Migration <span class="float-right">20%</span></h4> <h4 class="small font-weight-bold">Server Migration <span
class="float-right">20%</span></h4>
<div class="progress mb-4"> <div class="progress mb-4">
<div class="progress-bar bg-danger" role="progressbar" style="width: 20%" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></div> <div class="progress-bar bg-danger" role="progressbar" style="width: 20%"
aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></div>
</div> </div>
<h4 class="small font-weight-bold">Sales Tracking <span class="float-right">40%</span></h4> <h4 class="small font-weight-bold">Sales Tracking <span
class="float-right">40%</span></h4>
<div class="progress mb-4"> <div class="progress mb-4">
<div class="progress-bar bg-warning" role="progressbar" style="width: 40%" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div> <div class="progress-bar bg-warning" role="progressbar" style="width: 40%"
aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div>
</div> </div>
<h4 class="small font-weight-bold">Customer Database <span class="float-right">60%</span></h4> <h4 class="small font-weight-bold">Customer Database <span
class="float-right">60%</span></h4>
<div class="progress mb-4"> <div class="progress mb-4">
<div class="progress-bar" role="progressbar" style="width: 60%" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"></div> <div class="progress-bar" role="progressbar" style="width: 60%"
aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"></div>
</div> </div>
<h4 class="small font-weight-bold">Payout Details <span class="float-right">80%</span></h4> <h4 class="small font-weight-bold">Payout Details <span
class="float-right">80%</span></h4>
<div class="progress mb-4"> <div class="progress mb-4">
<div class="progress-bar bg-info" role="progressbar" style="width: 80%" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></div> <div class="progress-bar bg-info" role="progressbar" style="width: 80%"
aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></div>
</div> </div>
<h4 class="small font-weight-bold">Account Setup <span class="float-right">Complete!</span></h4> <h4 class="small font-weight-bold">Account Setup <span
class="float-right">Complete!</span></h4>
<div class="progress"> <div class="progress">
<div class="progress-bar bg-success" role="progressbar" style="width: 100%" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div> <div class="progress-bar bg-success" role="progressbar" style="width: 100%"
aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
</div> </div>
</div> </div>
</div> </div>
@ -299,11 +322,17 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="text-center"> <div class="text-center">
<img class="img-fluid px-3 px-sm-4 mt-3 mb-4" style="width: 25rem;" th:src="@{/img/undraw_posting_photo.svg}" src="../../static/img/undraw_posting_photo.svg" alt=""> <img class="img-fluid px-3 px-sm-4 mt-3 mb-4" style="width: 25rem;"
th:src="@{/img/undraw_posting_photo.svg}"
src="../../static/img/undraw_posting_photo.svg" alt="">
</div> </div>
<p>Add some quality, svg illustrations to your project courtesy of <a target="_blank" rel="nofollow" href="https://undraw.co/">unDraw</a>, a constantly updated collection of beautiful svg images that you can use completely <p>Add some quality, svg illustrations to your project courtesy of <a
target="_blank" rel="nofollow" href="https://undraw.co/">unDraw</a>, a
constantly updated collection of beautiful svg images that you can use
completely
free and without attribution!</p> free and without attribution!</p>
<a target="_blank" rel="nofollow" href="https://undraw.co/">Browse Illustrations on unDraw &rarr;</a> <a target="_blank" rel="nofollow" href="https://undraw.co/">Browse Illustrations on
unDraw &rarr;</a>
</div> </div>
</div> </div>
@ -313,8 +342,11 @@
<h6 class="m-0 font-weight-bold text-white">Development Approach</h6> <h6 class="m-0 font-weight-bold text-white">Development Approach</h6>
</div> </div>
<div class="card-body"> <div class="card-body">
<p>SB Admin 2 makes extensive use of Bootstrap 4 utility classes in order to reduce CSS bloat and poor page performance. Custom CSS classes are used to create custom components and custom utility classes.</p> <p>SB Admin 2 makes extensive use of Bootstrap 4 utility classes in order to reduce
<p class="mb-0">Before working with this theme, you should become familiar with the Bootstrap framework, especially the utility classes.</p> CSS bloat and poor page performance. Custom CSS classes are used to create
custom components and custom utility classes.</p>
<p class="mb-0">Before working with this theme, you should become familiar with the
Bootstrap framework, especially the utility classes.</p>
</div> </div>
</div> </div>
@ -336,7 +368,9 @@
<!-- End of Page Wrapper --> <!-- End of Page Wrapper -->
<th:block th:include="fragments/admin :: modal"></th:block> <th:block th:include="fragments/admin :: modal"></th:block>
<div th:if="false"><th:block th:include="admin :: modal"></th:block></div> <div th:if="false">
<th:block th:include="admin :: modal"></th:block>
</div>
<!-- Page level custom scripts --> <!-- Page level custom scripts -->
<script th:src="@{/js/demo/chart-area-demo.js}" src="../../static/js/demo/chart-area-demo.js"></script> <script th:src="@{/js/demo/chart-area-demo.js}" src="../../static/js/demo/chart-area-demo.js"></script>

View File

@ -13,6 +13,19 @@
<script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js" defer="defer" th:if="false"></script> <script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js" defer="defer" th:if="false"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" th:if="false"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" th:if="false">
<th:block th:fragment="headFragment">
<script th:src="@{/js/adminBundle.js}" src="../../static/js/adminBundle.js" defer="defer"></script>
<script th:src="@{/js/admin.js}" src="../../static/js/admin.js" defer="defer"></script>
<script th:src="@{/js/sb-admin-2.js}" src="../../static/js/sb-admin-2.js" defer="defer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.js" defer></script>
<link th:href="@{/css/sb-admin-2.css}" href="../../static/css/sb-admin-2.css" rel="stylesheet">
<link th:href="@{/css/admin-custom.css}" href="../../static/css/admin-custom.css" rel="stylesheet">
</th:block>
<link href="../../static/css/sb-admin-2.css" rel="stylesheet" th:if="false"> <link href="../../static/css/sb-admin-2.css" rel="stylesheet" th:if="false">
<link href="../../static/css/admin-custom.css" rel="stylesheet" th:if="false"> <link href="../../static/css/admin-custom.css" rel="stylesheet" th:if="false">
</head> </head>

View File

@ -81,7 +81,7 @@
<a href="user/home.html" sec:authorize="isFullyAuthenticated()" th:href="@{/user}" class="nav-link">User Area</a> <a href="user/home.html" sec:authorize="isFullyAuthenticated()" th:href="@{/user}" class="nav-link">User Area</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a th:href="chat" href="chat.html" class="nav-link">Chat</a> <a th:href="@{/chat}" href="chat.html" class="nav-link">Chat</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a th:href="login" sec:authorize="!isFullyAuthenticated()" href="login.html" class="nav-link">Login</a> <a th:href="login" sec:authorize="!isFullyAuthenticated()" href="login.html" class="nav-link">Login</a>