Browse Source

Added stats to admin UI #11

development
Rohan Sircar 4 years ago
parent
commit
05ba6fbacc
  1. 11
      src/main/frontend/admin/main.ts
  2. 21
      src/main/frontend/admin/pages/Home.ts
  3. 9
      src/main/frontend/admin/pages/user/ChangePassphrase.ts
  4. 33
      src/main/frontend/common/ajax/Messages.ts
  5. 9
      src/main/frontend/common/ajax/util.ts
  6. 6
      src/main/frontend/common/dto/StatsDTO.ts
  7. 3
      src/main/frontend/common/routes/Stats.ts
  8. 7
      src/main/frontend/common/util/Passphrase.ts
  9. 11
      src/main/resources/templates/admin/change-passphrase.html
  10. 150
      src/main/resources/templates/admin/home.html
  11. 13
      src/main/resources/templates/fragments/admin.html
  12. 2
      src/main/resources/templates/fragments/navbar.html

11
src/main/frontend/admin/main.ts

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

21
src/main/frontend/admin/pages/Home.ts

@ -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);
}

9
src/main/frontend/admin/pages/user/ChangePassphrase.ts

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

33
src/main/frontend/common/ajax/messages.ts → src/main/frontend/common/ajax/Messages.ts

@ -4,27 +4,21 @@ import { ReencryptionDTO } from "../dto/ReencryptionDTO";
import { ChatMessageDTO } from "../dto/ChatMessageDTO";
import { JsonAPI } from "../../chat/singleton/JsonAPI";
import * as log from "loglevel";
import { StatsDTO } from "../dto/StatsDTO";
import { createApiHeaders } from "./util";
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}`, {
method: "GET",
headers: headers,
headers: createApiHeaders(authToken),
});
return response.json() as Promise<ReencryptionDTO[]>;
}
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}`, {
method: "GET",
headers: headers,
headers: createApiHeaders(authToken),
});
let data = (await response.json()) as string[];
return data;
@ -49,19 +43,14 @@ export async function sendReencryptedMessages(
export async function getOneMessage(
toUser: string,
page: number
page: number,
authToken: string
): 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);
log.debug(url);
const response = await fetch(url, {
method: "GET",
headers: headers,
headers: createApiHeaders(authToken),
});
log.debug(response.clone());
// if (fetchErrorHandler(response.clone(), this._notificationService)) {
@ -80,3 +69,11 @@ export async function getOneMessage(
const data2 = func(data);
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;
}

9
src/main/frontend/common/ajax/util.ts

@ -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;
}

6
src/main/frontend/common/dto/StatsDTO.ts

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

3
src/main/frontend/common/routes/Stats.ts

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

7
src/main/frontend/common/util/passphrase.ts → src/main/frontend/common/util/Passphrase.ts

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

11
src/main/resources/templates/admin/change-passphrase.html

@ -8,14 +8,9 @@
<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 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">
<link th:href="@{/css/admin-custom.css}" href="../../static/css/admin-custom.css" rel="stylesheet">
<th:block th:include="fragments/admin :: headFragment"></th:block>
</head>
<!-- TODO
Make user admin / remove user from admin

150
src/main/resources/templates/admin/home.html

@ -6,14 +6,10 @@
<title id="pageTitle">Admin Home</title>
</div>
<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 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"></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">
<link th:href="@{/css/admin-custom.css}" href="../../static/css/admin-custom.css" rel="stylesheet">
<script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js"
data-template-prefix="../" defer="defer" th:if="false"></script>
<th:block th:include="fragments/admin :: headFragment"></th:block>
</head>
<!-- TODO
Make user admin / remove user from admin
@ -21,13 +17,13 @@
Delete Messages
-->
<!-- <div th:include="fragments/admin :: admin-sidebar"></div> -->
<!-- <div th:include="fragments/admin :: admin-sideb ar"></div> -->
<body id="page-top">
<!-- Page Wrapper -->
<div id="wrapper">
<th:block th:include="fragments/admin :: sidebar-fragment"></th:block>
@ -36,7 +32,7 @@
<!-- Main Content -->
<div id="content">
<div th:include="fragments/admin :: topbar-fragment"></div>
<!-- Begin Page Content -->
@ -45,7 +41,8 @@
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<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>
<!-- Content Row -->
@ -53,15 +50,18 @@
<!-- Earnings (Monthly) Card Example -->
<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="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary-dark text-uppercase mb-1">Earnings (Monthly)</div>
<div class="h5 mb-0 font-weight-bold ">$40,000</div>
<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 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>
@ -70,15 +70,17 @@
<!-- Earnings (Monthly) Card Example -->
<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="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">Earnings (Annual)</div>
<div class="h5 mb-0 font-weight-bold text-light">$215,000</div>
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">
Total Messages</div>
<div class="h5 mb-0 font-weight-bold text-light" id="totalMessages">0</div>
</div>
<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>
@ -91,20 +93,23 @@
<div class="card-body">
<div class="row no-gutters align-items-center">
<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="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 class="col">
<!-- <div class="col">
<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"
style="width: 50%" aria-valuenow="50" aria-valuemin="0"
aria-valuemax="100"></div>
</div>
</div>
</div> -->
</div>
</div>
<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>
@ -113,15 +118,17 @@
<!-- Pending Requests Card Example -->
<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="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Pending Requests</div>
<div class="h5 mb-0 font-weight-bold text-white">18</div>
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">
Messages Today</div>
<div class="h5 mb-0 font-weight-bold text-white">0</div>
</div>
<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>
@ -137,13 +144,16 @@
<div class="col-xl-8 col-lg-7">
<div class="card bg-dark border border-dark text-white shadow mb-4">
<!-- 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>
<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>
</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>
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
@ -165,13 +175,16 @@
<div class="col-xl-4 col-lg-5">
<div class="card bg-dark border border-dark text-white shadow mb-4">
<!-- 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>
<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>
</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>
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
@ -187,14 +200,14 @@
</div>
<div class="mt-4 text-center small">
<span class="mr-2">
<i class="fas fa-circle text-primary"></i> Direct
</span>
<i class="fas fa-circle text-primary"></i> Direct
</span>
<span class="mr-2">
<i class="fas fa-circle text-success"></i> Social
</span>
<i class="fas fa-circle text-success"></i> Social
</span>
<span class="mr-2">
<i class="fas fa-circle text-info"></i> Referral
</span>
<i class="fas fa-circle text-info"></i> Referral
</span>
</div>
</div>
</div>
@ -213,25 +226,35 @@
<h6 class="m-0 font-weight-bold text-white">Projects</h6>
</div>
<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-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>
<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-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>
<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-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>
<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-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>
<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-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>
@ -299,11 +322,17 @@
</div>
<div class="card-body">
<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>
<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>
<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>
@ -313,8 +342,11 @@
<h6 class="m-0 font-weight-bold text-white">Development Approach</h6>
</div>
<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 class="mb-0">Before working with this theme, you should become familiar with the Bootstrap framework, especially the utility classes.</p>
<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 class="mb-0">Before working with this theme, you should become familiar with the
Bootstrap framework, especially the utility classes.</p>
</div>
</div>
@ -336,7 +368,9 @@
<!-- End of Page Wrapper -->
<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 -->
<script th:src="@{/js/demo/chart-area-demo.js}" src="../../static/js/demo/chart-area-demo.js"></script>

13
src/main/resources/templates/fragments/admin.html

@ -13,6 +13,19 @@
<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">
<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/admin-custom.css" rel="stylesheet" th:if="false">
</head>

2
src/main/resources/templates/fragments/navbar.html

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

Loading…
Cancel
Save