login form okie

This commit is contained in:
2026-07-01 15:40:46 +07:00
parent e9909784c7
commit ba587a40cb
107 changed files with 5601 additions and 33 deletions
@@ -152,6 +152,16 @@ spring:
enabled: true
security:
oauth2:
client:
registration:
google:
client-id: 416910503373-de8dpds39j5klabamn7e9lqcku29okeh.apps.googleusercontent.com
client-secret: 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
authorization-uri: https://accounts.google.com/o/oauth2/v2/auth?prompt=select_account
scope:
- openid
- email
- profile
resourceserver:
jwt:
authority-prefix: ''
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd">
<changeSet id="20260629084709-1" author="antigravity">
<addColumn tableName="sis_page">
<column name="layout" type="varchar(20)" defaultValue="STANDARD">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<!--
Added WordPress-like roles to jhi_authority.
-->
<changeSet id="20260629163500-1" author="jhipster">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">SELECT COUNT(*) FROM jhi_authority WHERE name = 'ROLE_EDITOR'</sqlCheck>
</preConditions>
<insert tableName="jhi_authority">
<column name="name" value="ROLE_EDITOR"/>
</insert>
<insert tableName="jhi_authority">
<column name="name" value="ROLE_AUTHOR"/>
</insert>
<insert tableName="jhi_authority">
<column name="name" value="ROLE_CONTRIBUTOR"/>
</insert>
<insert tableName="jhi_authority">
<column name="name" value="ROLE_SUBSCRIBER"/>
</insert>
</changeSet>
</databaseChangeLog>
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
<changeSet id="20260629165300-1" author="jhipster">
<addColumn tableName="jhi_user">
<column name="using_2fa" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="totp_secret" type="varchar(255)"/>
</addColumn>
</changeSet>
</databaseChangeLog>
@@ -1,3 +1,8 @@
name
ROLE_ADMIN
ROLE_USER
ROLE_EDITOR
ROLE_AUTHOR
ROLE_CONTRIBUTOR
ROLE_SUBSCRIBER
ROLE_PRE_AUTH_2FA
1 name
2 ROLE_ADMIN
3 ROLE_USER
4 ROLE_EDITOR
5 ROLE_AUTHOR
6 ROLE_CONTRIBUTOR
7 ROLE_SUBSCRIBER
8 ROLE_PRE_AUTH_2FA
@@ -23,6 +23,9 @@
<include file="config/liquibase/changelog/20260625120000_add_page_type.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260628100000_add_post_category_tag.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260628151700_add_post_layout.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260629084709_add_page_layout.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260629163500_add_wordpress_roles.xml" relativeToChangelogFile="false"/>
<include file="config/liquibase/changelog/20260629165300_add_totp_to_user.xml" relativeToChangelogFile="false"/>
<!-- jhipster-needle-liquibase-add-changelog - JHipster will add liquibase changelogs here -->
<!-- jhipster-needle-liquibase-add-constraints-changelog - JHipster will add liquibase constraints changelogs here -->
<!-- jhipster-needle-liquibase-add-incremental-changelog - JHipster will add incremental liquibase changelogs here -->
File diff suppressed because one or more lines are too long
@@ -0,0 +1,300 @@
function ShowMessage(message) {
if (message != null) {
var jsMessage = $('#jsquickmessage');
var toInject = "<div class=\"Metronic-alerts alert alert-success fade in\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\"></button>" + message + "</div>";
jsMessage.html(toInject);
jsMessage.show();
$('div.alert').delay(2200).fadeOut();
}
}
function ShowSuccessNotification() {
$('span.ajaxsuccessshow').fadeIn().delay('800').fadeOut();
}
var showProgress = true;
//$(document).ajaxStart(function () {
// if (showProgress) {
// $("#AjaxProgress").show();
// }
//});
//$(document).ajaxStop(function () {
// $("#AjaxProgress").hide();
// showProgress = true;
//});
$(function () {
$('div.alert').delay(5000).fadeOut();
ChangeLanguage();
var app_base = '/';
//Language manage Start
// Find the encompassing element
function getParentElement(element, elementType) {
var target = element.parent();
var found = false;
while (target != undefined && !found) {
var tagName = target.get(0).tagName;
if (tagName == undefined) {
return undefined;
}
if (tagName.toLowerCase() != elementType) {
target = target.parent();
} else {
found = true;
}
}
return target;
}
function tableContext(element) {
this.Cell = getParentElement(element, "td");
this.Row = getParentElement(element, "tr");
}
// Handler for click on the resource edit button
$('span.editresource').click(function (e) {
e.preventDefault();
var tableInfo = new tableContext($(this));
tableInfo.Cell.find(".saveresource").show();
tableInfo.Row.find(".resourcevalueedit").show();
tableInfo.Row.find(".resourcevaluedisplay").hide();
tableInfo.Cell.find(".editresource").hide();
});
// Handler for click on the resource save button
$('span.saveresource').click(function (e) {
e.preventDefault();
var tableInfo = new tableContext($(this));
var inputfield = tableInfo.Row.find(".resourcevalueedit textarea");
var displayfield = tableInfo.Row.find(".resourcevaluedisplay");
// Ajax call setup
var languageid = inputfield.data('languageid');
var resourcekey = inputfield.data('resourcekey');
var oldvalue = inputfield.data('oldvalue');
var newvalue = inputfield.val();
// Don't allow a null/empty string value, and don't update if nothing changed
if ((newvalue != null && newvalue != "") && (newvalue != oldvalue)) {
// Make a view model instance
var ajaxEditLanguageValueViewModel = new Object();
ajaxEditLanguageValueViewModel.LanguageId = languageid;
ajaxEditLanguageValueViewModel.ResourceKey = resourcekey;
ajaxEditLanguageValueViewModel.NewValue = newvalue;
// Ajax call to post the view model to the controller
var strung = JSON.stringify(ajaxEditLanguageValueViewModel);
$.ajax({
url: app_base + 'ManageLanguage/UpdateResourceValue',
type: 'POST',
data: strung,
contentType: 'application/json; charset=utf-8',
error: function (xhr, ajaxOptions, thrownError) {
ShowUserMessage("Error: " + xhr.status + " " + thrownError);
}
});
displayfield.text(newvalue);
}
tableInfo.Cell.find(".editresource").show();
tableInfo.Row.find(".resourcevaluedisplay").show();
tableInfo.Cell.find(".saveresource").hide();
tableInfo.Row.find(".resourcevalueedit").hide();
});
// Handler for click on the resource key edit button
$('span.editresourcekey').click(function (e) {
e.preventDefault();
var tableInfo = new tableContext($(this));
tableInfo.Cell.find(".saveresourcekey").show();
tableInfo.Row.find(".resourcekeyvalueedit").show();
tableInfo.Row.find(".resourcekeyvaluedisplay").hide();
tableInfo.Cell.find(".editresourcekey").hide();
});
// Handler for click on the resource key save button
$('span.saveresourcekey').click(function (e) {
e.preventDefault();
var tableInfo = new tableContext($(this));
var inputfield = tableInfo.Row.find(".resourcekeyvalueedit input");
var displayfield = tableInfo.Row.find(".resourcekeyvaluedisplay");
// Ajax call setup
var resourcekeyid = inputfield.data('resourcekeyid');
var oldvalue = inputfield.data('oldvalue');
var newvalue = inputfield.val();
// Don't allow a null/empty string value, and don't update if nothing changed
if ((newvalue != null && newvalue != "") && (newvalue != oldvalue)) {
// Make a view model instance
var ajaxEditLanguageKeyViewModel = new Object();
ajaxEditLanguageKeyViewModel.ResourceKeyId = resourcekeyid;
ajaxEditLanguageKeyViewModel.NewName = newvalue;
// Ajax call to post the view model to the controller
var strung = JSON.stringify(ajaxEditLanguageKeyViewModel);
$.ajax({
url: app_base + 'ManageLanguage/UpdateResourceKey',
type: 'POST',
data: strung,
contentType: 'application/json; charset=utf-8',
error: function (xhr, ajaxOptions, thrownError) {
ShowUserMessage("Error: " + xhr.status + " " + thrownError);
}
});
displayfield.text(newvalue);
}
tableInfo.Cell.find(".editresourcekey").show();
tableInfo.Row.find(".resourcekeyvaluedisplay").show();
tableInfo.Cell.find(".saveresourcekey").hide();
tableInfo.Row.find(".resourcekeyvalueedit").hide();
});
$('.btn-file :file').on('fileselect', function (event, numFiles, label) {
var input = $(this).parents('.input-group').find(':text'),
log = numFiles > 1 ? numFiles + ' files selected' : label;
if (input.length) {
input.val(log);
} else {
if (log) alert(log);
}
});
});
$(document).on('change', '.btn-file :file', function () {
var input = $(this),
numFiles = input.get(0).files ? input.get(0).files.length : 1,
label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
input.trigger('fileselect', [numFiles, label]);
});
var isFirstLoad = true;
function ImportLanguage() {
$("#ImportForm").submit();
}
function Import_Complete() {
// Check to see if this is the first load of the iFrame
if (isFirstLoad == true) {
isFirstLoad = false;
return;
}
// Reset the import form so the file won't get uploaded again
document.getElementById("ImportForm").reset();
// Grab the content of the textarea we named jsonResult in the hidden iframe
var importResults = $.parseJSON($("#UploadTarget").contents().find("#jsonResult")[0].innerHTML);
var displayImportResults = "";
// Redirect as there are no errors
if (!importResults.HasErrors && !importResults.HasWarnings) {
displayImportResults += "<div>The import was successful.</div>";
}
if (importResults.HasErrors) {
displayImportResults += "<div>The import had the following errors:</div>";
displayImportResults += formatImportResults(importResults.Errors);
}
if (importResults.HasWarnings) {
displayImportResults += "<div>The import had the following warnings:</div>";
displayImportResults += formatImportResults(importResults.Warnings);
}
$("#ImportResults").html(displayImportResults);
}
function formatImportResults(jsonErrorsWarnings) {
var results = "";
var errorsWarnings = jQuery.parseJSON(jsonErrorsWarnings);
for (var i = 0; i < errorsWarnings.length; i++) {
results += "<div>" + errorsWarnings[i] + "</div>";
}
return results;
}
//Language manage End
function ChangeLanguage() {
var languageSelect = $(".languageselector select");
if (languageSelect.length > 0) {
languageSelect.change(function () {
var langVal = this.value;
$.ajax({
url: "/Language/ChangeLanguage",
type: "POST",
cache: false,
data: { lang: langVal },
success: function (data) {
location.reload();
},
error: function (xhr, ajaxOptions, thrownError) {
ShowUserMessage("Error: " + xhr.status + " " + thrownError);
}
});
});
}
}
function decodeHtml(html) {
var txt = document.createElement("textarea");
txt.innerHTML = html;
return txt.value;
}
function AjaxPost(url, data, successFunction, errorFunction) {
$.ajax({
type: "POST",
url: url,
data: data,
success: function (returnData, request) {
if (typeof (successFunction) == 'function') {
successFunction(returnData, url, data);
}
},
error: errorFunction ? errorFunction : DefaultErrorFunction
});
}
function AjaxGet(url, data, successFunction, errorFunction) {
$.ajax({
type: "GET",
url: url,
data: JSON.stringify(data),
success: function (returnData, request) {
if (typeof (successFunction) == 'function') {
successFunction(returnData, url, data);
}
},
error: errorFunction ? errorFunction : DefaultErrorFunction
});
}
function DefaultErrorFunction(err) {
alert(err.responseText);
console.log(err);
}
function ShowMessageControl(control, message) {
$(control).notify(message, { position: "bottom", className: "error" });
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,360 @@
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* hebrew */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* math */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* hebrew */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* math */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* hebrew */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* math */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSKmu1aB.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSumu1aB.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSOmu1aB.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSymu1aB.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* hebrew */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS2mu1aB.woff2) format('woff2');
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
/* math */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTVOmu1aB.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTUGmu1aB.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSCmu1aB.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTSGmu1aB.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
font-stretch: 100%;
src: url(https://fonts.gstatic.com/s/opensans/v44/memvYaGs126MiZpBA-UvWbX2vVnXBbObj2OVTS-muw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@@ -0,0 +1,420 @@
@media (min-width: 1400px){
.container, .container-sm, .container-md, .container-lg, .container-xl, .container-xxl {
max-width: 1500px!important;
}
}
.dropdown-user.lv-lang-box{
display:none;
}
.page-header.navbar .page-logo .logo-default, .brand-logo img {
max-height: 45px !important;
margin-top: 1px !important;
}
.top-menu .navbar-nav > li.dropdown-user .dropdown-toggle > img {
border-radius: 10px;
width: 20px;
}
.lv-notification-box .badge {
background-color: #FFD5D8;
color: #A85A5C;
border: 1px #EFDDDC solid;
}
.top-menu .link-chat, .classic-menu-dropdown.active, .header-menu .menu-nav > .menu-item:first-child {
display: none !important;
}
.active-sub {
background-color: #f5f5f9 !important;
}
.active-sub span {
color: #e7505a !important;
}
.input-icon-search {
display: unset;
right: 0px;
top: 6px;
cursor: pointer;
}
#kt_aside_menu{
padding-bottom: 50px;
overflow-y: auto!important;
}
.gutter-b{
margin-bottom: 9px;
}
.page-content {
margin-top: 55px;
}
@media (min-width: 992px){
.header-menu .menu-nav {
max-width: 700px;
}
}
.dashboard-stat .details .number{
font-size: 20px!important;
}
:root {
/*lacviet text color*/
/*lacviet button background, text color*/
/*lacviet button background hover, text color hover */
/*lacviet button background, text color third*/
/*lacviet border left search advance*/
/*lacviet bg button light*/
--primary-color-lv: #E74C3C;
--primary-button-color-lv: #5b5b5b;
--second-color-lv: #5b5b5b;
--third-color-lv: #C11200;
--border-color-lv: #8d8a8a;
--light-bg-lv: #E1F0FF;
/* --primary-color-lv: #B92A85;
--second-color-lv: #891B72;
--third-color-lv: #B31E8D;
--border-color-lv: #bd82a8;
--light-bg-lv: #f2f2f2;*/
}
.user-login-5 {
background: linear-gradient(#fff, #ccc)!important;
}
.goback a {
color: #696969 !important;
}
.login .mt-checkbox.mt-checkbox-outline>span{
border: 1px #696969 solid;
}
.rem-password .rememberme.font-white,
.user-login-5 .login-container > .login-content > h1,
.user-login-5 .login-container > .login-content p
{
color:#696969!important;
}
.table-scrollable .btn.btn-primary{
background-color: #337ab7!important;
border-color: #2e6da4!important;
}
/*module*/
.page-header.navbar .page-logo .logo-default, .contract-header #kt_brand img {
max-height: 45px !important;
height: 45px;
margin-top: 0px !important;
}
/*.have-chat .link-chat, .happy-brithday{
display: none;
}*/
.medium .modal-dialog .btn-success {
color: #fff !important;
}
#create-document-button.floating-button, .cd-stretchy-nav .stretchy-nav-bg, .login .btn.blue:not(.btn-outline),
.login .btn.blue, .login .btn.btn-success, .input-search-single .btn.green:not(.btn-outline)
{
background-color: var(--primary-color-lv) !important;
}
.login .btn.blue:not(.btn-outline),
.login .btn.blue, .login .btn.btn-success,
.user-login-5 .login-container > .login-content > .login-form .form-control,
.nav.nav-tabs.nav-tabs-line .nav-link:hover:not(.disabled), .nav.nav-tabs.nav-tabs-line .nav-link.active, .nav.nav-tabs.nav-tabs-line .show > .nav-link
{
border-color: var(--primary-color-lv) !important;
}
.page-header.navbar .hor-menu .navbar-nav > li.active > a, .page-header.navbar .hor-menu .navbar-nav > li.active > a, .page-header.navbar .hor-menu .navbar-nav > li.current > a, .page-header.navbar .hor-menu .navbar-nav > li.current > a,
.e-btn.e-info, .e-css.e-btn.e-info, .e-footer-content .e-btn.e-info, .e-upload .e-file-select-wrap .e-btn, .btn.blue:not(.btn-outline),
.add-menu, .edit-menu,
ul#navigation-left-filter-tree > li .title i,
#advanced-search-table-header th div,
.btn-success,
.lv-mainTop .pull-right > a,
.lv-mainTop .fa-ellipsis-v,
.lv-mainTop .pull-right > a, .lv-mainTop .pull-left > a, #downloadAllFiles,
#Schedules thead th {
background-color: var(--primary-button-color-lv) !important;
}
.fc-unthemed .fc-toolbar .fc-button.fc-button-active
{
background-color: var(--primary-button-color-lv) !important;
border-color: var(--primary-button-color-lv) !important;
}
.input-search-single .btn.green:not(.btn-outline){
border: 1px var(--primary-color-lv) solid !important;
}
.input-search-single .btn.green:not(.btn-outline) {
border-left: 1px var(--third-color-lv) solid !important;
}
.e-btn.e-info, .e-css.e-btn.e-info, .e-footer-content .e-btn.e-info, .e-upload .e-file-select-wrap .e-btn, .btn.blue:not(.btn-outline),
.add-menu, .edit-menu,
ul#navigation-left-filter-tree > li .title i,
.btn-success {
border-color: var(--primary-button-color-lv) !important;
}
.page-content-wrapper .e-btn.e-info:hover,
.page-content-wrapper .e-css.e-btn.e-info:hover,
.page-content-wrapper .e-footer-content .e-btn.e-info:hover,
.page-content-wrapper .e-upload .e-file-select-wrap .e-btn:hover .btn.blue:not(.btn-outline):hover,
.btn-success:hover,
.trinhky .e-btn.e-info:hover, .trinhky .e-css.e-btn.e-info:hover, .trinhky .e-footer-content .e-btn.e-info:hover,
.trinhky .e-upload .e-file-select-wrap .e-btn:hover .btn.blue:not(.btn-outline):hover,
.btn.green:not(.btn-outline):focus, .btn.green:not(.btn-outline):hover,
.fc-unthemed .fc-toolbar .fc-button.fc-button-active:hover {
background-color: var(--second-color-lv) !important;
border-color: var(--second-color-lv) !important;
}
.page-header.navbar .hor-menu .navbar-nav > li.active > a:hover {
background-color: var(--second-color-lv) !important;
}
.page-sidebar .page-sidebar-menu a.nav-toggle + .sub-menu > li > a:hover, .page-sidebar .page-sidebar-menu a.selected,
.page-sidebar .page-sidebar-menu .sub-menu > li > a.selected > i,
.page-sidebar .page-sidebar-menu a.nav-toggle + .sub-menu > li.selected > a,
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a.delete-document, .dropdown-menu > li > a.delete-document i,
.lv-search #SearchMainDocumentIcon,
#MainDocumentDetailContent .document-title,
.todo-tasklist-item-text,
.btn.btn-light-primary, .title-private-folder,
.nav .nav-link:hover:not(.disabled)
{
color: var(--primary-color-lv) !important;
}
#document-form .lv-btn-se .step-user, #document-form .lv-btn-se .step-user:focus {
background-color: transparent !important;
}
.active-sub span, .header .header-menu .menu-nav > .menu-item > .menu-link.active-sub .menu-text, #content-meetings .text-muted,
#content-meetings .card-header-meeting .btn.btn-light {
color: var(--primary-color-lv) !important;
}
/*Feature*/
.have-chat .link-chat {
display: none;
}
.btn.btn-primary, .lv-res-lang .search-box .input-search-single .btn-search-text-single:hover
{
background-color: var(--primary-button-color-lv) !important;
border-color: var(--primary-button-color-lv) !important;
}
.scrolltop,
#kt_header_menu .menu-link.menu-toggle1,
.page-header.navbar .hor-menu .navbar-nav > li.active > a, .page-header.navbar .hor-menu .navbar-nav > li.active > a, .page-header.navbar .hor-menu .navbar-nav > li.current > a, .page-header.navbar .hor-menu .navbar-nav > li.current > a,
.lv-res-lang .btn.green:not(.btn-outline), .search-box #searchStr + span > .btn-search,
.modal-content .split-container .portlet.light > .portlet-title,
.nav.nav-pills .show > .nav-link, .nav.nav-pills .nav-link.active ,
.sticky .btn.btn-danger,
.btn.btn-primary:hover:not(.btn-text):not(:disabled):not(.disabled),.btn.btn-primary:hover
{
background-color: var(--primary-color-lv) !important;
}
.sticky .btn.btn-danger,
.btn.btn-primary:hover:not(.btn-text):not(:disabled):not(.disabled), .btn.btn-primary:hover{
border-color: var(--primary-color-lv) !important;
}
.btn.btn-light-primary, .card-header-meeting .btn.btn-light,
.card.card-custom.card-more > .card-header .card-title .card-label {
background-color: var(--light-bg-lv) !important;
}
.card.card-custom.card-more > .card-header .card-title .card-label:after {
border-left-color: var(--light-bg-lv) !important;
}
.page-header.navbar .hor-menu .navbar-nav > li.active > a:hover {
background-color: var(--third-color-lv) !important;
}
.btn.blue:not(.btn-outline).active, .btn.blue:not(.btn-outline):active,
.open > .btn.blue:not(.btn-outline).dropdown-toggle,
.content-user .btn-info,.btn-info,
.doc-group-title a.btn, .doc-group-title button.btn, .content-user .add-role {
background-color: var(--primary-button-color-lv) !important;
border-color: var(--primary-button-color-lv) !important;
}
.layout-menu #kt_aside_menu_wrapper, .layout-menu .aside-menu.menu-system, .layout-menu .aside-menu{
background-color: var(--primary-button-color-lv) !important;
}
.menu-system .jstree-node.jstree-open ul, .menu-system .jstree-node{
border-color: #818181;
}
.btn.blue:not(.btn-outline):hover,
#create-document-button.floating-button:hover,
.sticky .btn.btn-danger:hover,
.btn.blue:not(.btn-outline).active.focus, .btn.blue:not(.btn-outline).active:focus, .btn.blue:not(.btn-outline).active:hover, .btn.blue:not(.btn-outline):active.focus, .btn.blue:not(.btn-outline):active:focus, .btn.blue:not(.btn-outline):active:hover, .open > .btn.blue:not(.btn-outline).dropdown-toggle.focus, .open > .btn.blue:not(.btn-outline).dropdown-toggle:focus, .open > .btn.blue:not(.btn-outline).dropdown-toggle:hover,
.btn.btn-primary:focus:not(.btn-text), .btn.btn-primary.focus:not(.btn-text),
.btn.btn-outline-primary:hover:not(.btn-text):not(:disabled):not(.disabled), .btn.btn-outline-primary:focus:not(.btn-text), .btn.btn-outline-primary.focus:not(.btn-text),
.content-user .btn-info:hover,.btn-info:hover,
.doc-group-title a.btn:hover, .content-user .add-role:hover,
.doc-group-title a.btn:focus, .content-user .add-role:focus,
.btn.btn-light-primary:hover,
.fc-unthemed .fc-toolbar .pull-right .fc-button:focus, .fc-unthemed .fc-toolbar .pull-right .fc-button:active, .fc-unthemed .fc-toolbar .pull-right .fc-button.fc-button-active
{
background-color: var(--primary-color-lv) !important;
border-color: var(--primary-color-lv) !important;
}
.btn.btn-default:hover:not(.btn-text):not(:disabled):not(.disabled), .btn.btn-default:focus:not(.btn-text), .btn.btn-default.focus:not(.btn-text),
.form-control-static.attachment:hover, .form-control-static.attachment:hover .fa,
#content-meetings table.scheduler-bg-table th,
#content-meetings table.resource-table th, #content-meetings table.scheduler-table th,
#content-meetings table.resource-table td, #content-meetings table.scheduler-bg-table td,
#content-meetings table.scheduler-table td,
#content-meetings .header3-text > div > div,
.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled),
.card-more .card-header .nav .nav-link:hover, .card-more .card-header .nav .show > .nav-link i, .card-more .card-header .nav .nav-link:hover:not(.disabled) i{
color: var(--primary-color-lv) !important;
}
.btn.btn-light-primary:hover, .card-more .card-header .nav .nav-link.active:hover,
#content-meetings .card-header-meeting .btn.btn-light:hover,
.more-btn .btn, .more-btn .btn:focus, .more-btn .btn:active
{
color: #fff !important;
}
.doc-group-title{
color: var(--primary-button-color-lv)!important;
}
.aside-menu .menu-nav > .menu-item .menu-submenu .menu-item:not(.menu-item-parent):not(.menu-item-open):not(.menu-item-here):not(.menu-item-active):hover > .menu-heading .menu-text,
a.text-hover-primary:hover, .text-hover-primary:hover,
.header-fixed .top-menu .navbar-nav > li.dropdown .dropdown-menu li a:hover {
color: var(--third-color-lv) !important;
}
.aside-menu .menu-nav > .menu-item .menu-submenu .menu-item:not(.menu-item-parent):not(.menu-item-open):not(.menu-item-here):not(.menu-item-active):hover > .menu-link .menu-text,
.aside-left a.menu-link:hover .menu-icon i, .aside-left .menu-item-open .menu-icon i, .aside-left .menu-icon i, .span-total {
color: var(--primary-color-lv) !important;
}
/*.card-birthday, .card-birthday + div, .sp-my-task + div {
display: none;
}*/
.sp-my-task + div {
display: none;
}
.lv-contact-detail .font-blue, .navi .navi-item .navi-link:hover,
.navi .navi-item .navi-link:hover .navi-icon i,
.navi .navi-item .navi-link:hover .navi-text,
#content-workspace .detail-dl > a button i {
color: var(--third-color-lv) !important;
}
.btn.btn-outline-primary {
color: var(--primary-color-lv);
background-color: transparent;
border-color: var(--primary-color-lv);
}
.btn.btn-hover-light-primary:hover:not(.btn-text):not(:disabled):not(.disabled), .btn.btn-hover-light-primary:focus:not(.btn-text), .btn.btn-hover-light-primary.focus:not(.btn-text) {
color: var(--primary-color-lv);
background-color: #fdf4fb;
border-color: var(--primary-color-lv);
}
.btn.btn-hover-light-primary:hover:not(.btn-text):not(:disabled):not(.disabled) i, .btn.btn-hover-light-primary:focus:not(.btn-text) i, .btn.btn-hover-light-primary.focus:not(.btn-text) i, .btn.btn-light-primary i,
.nav .nav-link.active {
color: var(--primary-color-lv);
}
.card-more-all .card.card-custom.card-more > .card-header .card-title .card-label, .aside-menu .menu-nav > .menu-item > .menu-link .menu-icon {
color: var(--primary-color-lv) !important;
}
.lv-res-lang .btn.green:not(.btn-outline), #txt-um-keyword:focus, .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled), .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled):first-child {
border-color: var(--border-color-lv) !important;
}
.lv-res-lang .search-box .input-search-single .btn-search-text-single, .lv-res-lang .search-box #searchStr + span > .btn-search {
border-right-color: var(--third-color-lv) !important;
}
#MainCtrl h3.card-title, .tree_collapse_all i{
color: var(--primary-button-color-lv)!important;
}
.brand-logo img {
max-height: 38px;
}
#Schedules button.new, #Schedules .btn-success.btn-send-mail {
background-color: transparent !important;
}
.medical-event-title{
line-height: 18px;
max-height: 54px;
overflow: hidden;
}
/*message***/
.lv-box-message.e-dialog .e-dlg-header-content {
background-color: var(--primary-color-lv) !important;
}
.lv-box-message.e-dialog .e-dlg-header {
color: #fff !important;
}
.lv-box-message.e-dialog .e-btn .e-btn-icon.e-icon-dlg-close {
color: #fff !important;
}
.lv-box-message .e-btn.e-flat.e-primary, .lv-box-message .e-css.e-btn.e-flat.e-primary {
color: var(--primary-color-lv) !important;
border-radius: 5px !important;
}
.lv-box-message .e-btn.e-flat.e-primary:focus, .lv-box-message .e-css.e-btn.e-flat.e-primary:focus {
background-color: #f2f2f2 !important;
}
.lv-box-message.e-dialog .e-btn.e-dlg-closeicon-btn:hover, .lv-box-message .e-dialog .e-btn.e-dlg-closeicon-btn:focus, .lv-box-message .e-dialog .e-btn.e-dlg-closeicon-btn:active {
background-color: transparent !important;
}
/*message*/
.card-messages .card-body .scroll-pull > div > div{
overflow:unset!important;
}
.card-messages .card-footer {
border-top: none;
}
.card-messages textarea {
overflow:hidden;
}
.card-messages .thumbs-container{
margin-top: 0px;
}
.card-messages div[class*="react-scroll-to-bottom-"] {
overflow-x: hidden;
}
.card-messages .align-items-start, .card-messages .align-items-end {
margin-left: 5px;
margin-right: 5px;
}
.card-messages .chat-message .message-text {
white-space: pre-wrap;
}
.card-messages .align-items-start .bg-light-success {
background-color: #F2F6F9 !important;
}
.card-messages .align-items-end .bg-light-success {
background-color: #DBF1FF !important;
}
.layout-task #hand-over-task,.layout-task #MainCtrl #kt_todo_list h3.card-title .dropdown{
display:none;
}
.layout-task .menuLeft .menu-icon i.fa-folder:before{
color: var(--color-i-menu) !important;
}
.layout-task .detail-tabs.nav-tabs.nav-tabs-line .nav-item{
margin-left: 0px;
}
.layout-task .nav.nav-tabs.nav-tabs-line .nav-item:first-child .nav-link {
margin-left: 10px;
margin-right: 5px;
}
.layout-task .nav.nav-tabs.nav-tabs-line .nav-item .nav-link {
margin-left: 5px;
}
/*ntvy*/
#open-support {
display: none;
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
/*! Backstretch - v2.0.4 - 2013-06-19
* http://srobbin.com/jquery-plugins/backstretch/
* Copyright (c) 2013 Scott Robbin; Licensed MIT */
(function(a,d,p){a.fn.backstretch=function(c,b){(c===p||0===c.length)&&a.error("No images were supplied for Backstretch");0===a(d).scrollTop()&&d.scrollTo(0,0);return this.each(function(){var d=a(this),g=d.data("backstretch");if(g){if("string"==typeof c&&"function"==typeof g[c]){g[c](b);return}b=a.extend(g.options,b);g.destroy(!0)}g=new q(this,c,b);d.data("backstretch",g)})};a.backstretch=function(c,b){return a("body").backstretch(c,b).data("backstretch")};a.expr[":"].backstretch=function(c){return a(c).data("backstretch")!==p};a.fn.backstretch.defaults={centeredX:!0,centeredY:!0,duration:5E3,fade:0};var r={left:0,top:0,overflow:"hidden",margin:0,padding:0,height:"100%",width:"100%",zIndex:-999999},s={position:"absolute",display:"none",margin:0,padding:0,border:"none",width:"auto",height:"auto",maxHeight:"none",maxWidth:"none",zIndex:-999999},q=function(c,b,e){this.options=a.extend({},a.fn.backstretch.defaults,e||{});this.images=a.isArray(b)?b:[b];a.each(this.images,function(){a("<img />")[0].src=this});this.isBody=c===document.body;this.$container=a(c);this.$root=this.isBody?l?a(d):a(document):this.$container;c=this.$container.children(".backstretch").first();this.$wrap=c.length?c:a('<div class="backstretch"></div>').css(r).appendTo(this.$container);this.isBody||(c=this.$container.css("position"),b=this.$container.css("zIndex"),this.$container.css({position:"static"===c?"relative":c,zIndex:"auto"===b?0:b,background:"none"}),this.$wrap.css({zIndex:-999998}));this.$wrap.css({position:this.isBody&&l?"fixed":"absolute"});this.index=0;this.show(this.index);a(d).on("resize.backstretch",a.proxy(this.resize,this)).on("orientationchange.backstretch",a.proxy(function(){this.isBody&&0===d.pageYOffset&&(d.scrollTo(0,1),this.resize())},this))};q.prototype={resize:function(){try{var a={left:0,top:0},b=this.isBody?this.$root.width():this.$root.innerWidth(),e=b,g=this.isBody?d.innerHeight?d.innerHeight:this.$root.height():this.$root.innerHeight(),j=e/this.$img.data("ratio"),f;j>=g?(f=(j-g)/2,this.options.centeredY&&(a.top="-"+f+"px")):(j=g,e=j*this.$img.data("ratio"),f=(e-b)/2,this.options.centeredX&&(a.left="-"+f+"px"));this.$wrap.css({width:b,height:g}).find("img:not(.deleteable)").css({width:e,height:j}).css(a)}catch(h){}return this},show:function(c){if(!(Math.abs(c)>this.images.length-1)){var b=this,e=b.$wrap.find("img").addClass("deleteable"),d={relatedTarget:b.$container[0]};b.$container.trigger(a.Event("backstretch.before",d),[b,c]);this.index=c;clearInterval(b.interval);b.$img=a("<img />").css(s).bind("load",function(f){var h=this.width||a(f.target).width();f=this.height||a(f.target).height();a(this).data("ratio",h/f);a(this).fadeIn(b.options.speed||b.options.fade,function(){e.remove();b.paused||b.cycle();a(["after","show"]).each(function(){b.$container.trigger(a.Event("backstretch."+this,d),[b,c])})});b.resize()}).appendTo(b.$wrap);b.$img.attr("src",b.images[c]);return b}},next:function(){return this.show(this.index<this.images.length-1?this.index+1:0)},prev:function(){return this.show(0===this.index?this.images.length-1:this.index-1)},pause:function(){this.paused=!0;return this},resume:function(){this.paused=!1;this.next();return this},cycle:function(){1<this.images.length&&(clearInterval(this.interval),this.interval=setInterval(a.proxy(function(){this.paused||this.next()},this),this.options.duration));return this},destroy:function(c){a(d).off("resize.backstretch orientationchange.backstretch");clearInterval(this.interval);c||this.$wrap.remove();this.$container.removeData("backstretch")}};var l,f=navigator.userAgent,m=navigator.platform,e=f.match(/AppleWebKit\/([0-9]+)/),e=!!e&&e[1],h=f.match(/Fennec\/([0-9]+)/),h=!!h&&h[1],n=f.match(/Opera Mobi\/([0-9]+)/),t=!!n&&n[1],k=f.match(/MSIE ([0-9]+)/),k=!!k&&k[1];l=!((-1<m.indexOf("iPhone")||-1<m.indexOf("iPad")||-1<m.indexOf("iPod"))&&e&&534>e||d.operamini&&"[object OperaMini]"==={}.toString.call(d.operamini)||n&&7458>t||-1<f.indexOf("Android")&&e&&533>e||h&&6>h||"palmGetResource"in d&&e&&534>e||-1<f.indexOf("MeeGo")&&-1<f.indexOf("NokiaBrowser/8.5.0")||k&&6>=k)})(jQuery,window);
File diff suppressed because one or more lines are too long
@@ -0,0 +1,117 @@
/*!
* jQuery Cookie Plugin v1.4.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
// Replace server-side written pluses with spaces.
// If we can't decode the cookie, ignore it, it's unusable.
// If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch(e) {}
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
// Write
if (value !== undefined && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setTime(+t + days * 864e+5);
}
return (document.cookie = [
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// Read
var result = key ? undefined : {};
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling $.cookie().
var cookies = document.cookie ? document.cookie.split('; ') : [];
for (var i = 0, l = cookies.length; i < l; i++) {
var parts = cookies[i].split('=');
var name = decode(parts.shift());
var cookie = parts.join('=');
if (key && key === name) {
// If second argument (value) is a function it's a converter...
result = read(cookie, value);
break;
}
// Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) === undefined) {
return false;
}
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));
File diff suppressed because one or more lines are too long
@@ -0,0 +1,9 @@
/*! Copyright (c) 2011 Piotr Rochala (http://rocha.la)
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
*
* Improved by keenthemes for Metronic Theme
* Version: 1.3.2
*
*/
!function(e){jQuery.fn.extend({slimScroll:function(i){var o={width:"auto",height:"250px",size:"7px",color:"#000",position:"right",distance:"1px",start:"top",opacity:.4,alwaysVisible:!1,disableFadeOut:!1,railVisible:!1,railColor:"#333",railOpacity:.2,railDraggable:!0,railClass:"slimScrollRail",barClass:"slimScrollBar",wrapperClass:"slimScrollDiv",allowPageScroll:!1,wheelStep:20,touchScrollStep:200,borderRadius:"7px",railBorderRadius:"7px",animate:!0},a=e.extend(o,i);return this.each(function(){function o(t){if(u){var t=t||window.event,i=0;t.wheelDelta&&(i=-t.wheelDelta/120),t.detail&&(i=t.detail/3);var o=t.target||t.srcTarget||t.srcElement;e(o).closest("."+a.wrapperClass).is(S.parent())&&r(i,!0),t.preventDefault&&!y&&t.preventDefault(),y||(t.returnValue=!1)}}function r(e,t,i){y=!1;var o=e,r=S.outerHeight()-M.outerHeight();if(t&&(o=parseInt(M.css("top"))+e*parseInt(a.wheelStep)/100*M.outerHeight(),o=Math.min(Math.max(o,0),r),o=e>0?Math.ceil(o):Math.floor(o),M.css({top:o+"px"})),v=parseInt(M.css("top"))/(S.outerHeight()-M.outerHeight()),o=v*(S[0].scrollHeight-S.outerHeight()),i){o=e;var s=o/S[0].scrollHeight*S.outerHeight();s=Math.min(Math.max(s,0),r),M.css({top:s+"px"})}"scrollTo"in a&&a.animate?S.animate({scrollTop:o}):S.scrollTop(o),S.trigger("slimscrolling",~~o),l(),c()}function s(){window.addEventListener?(this.addEventListener("DOMMouseScroll",o,!1),this.addEventListener("mousewheel",o,!1)):document.attachEvent("onmousewheel",o)}function n(){f=Math.max(S.outerHeight()/S[0].scrollHeight*S.outerHeight(),m),M.css({height:f+"px"});var e=f==S.outerHeight()?"none":"block";M.css({display:e})}function l(){if(n(),clearTimeout(p),v==~~v){if(y=a.allowPageScroll,b!=v){var e=0==~~v?"top":"bottom";S.trigger("slimscroll",e)}}else y=!1;return b=v,f>=S.outerHeight()?void(y=!0):(M.stop(!0,!0).fadeIn("fast"),void(a.railVisible&&H.stop(!0,!0).fadeIn("fast")))}function c(){a.alwaysVisible||(p=setTimeout(function(){a.disableFadeOut&&u||h||d||(M.fadeOut("slow"),H.fadeOut("slow"))},1e3))}var u,h,d,p,g,f,v,b,w="<div></div>",m=30,y=!1,S=e(this);if("ontouchstart"in window&&window.navigator.msPointerEnabled&&S.css("-ms-touch-action","none"),S.parent().hasClass(a.wrapperClass)){var E=S.scrollTop();if(M=S.parent().find("."+a.barClass),H=S.parent().find("."+a.railClass),n(),e.isPlainObject(i)){if("height"in i&&"auto"==i.height){S.parent().css("height","auto"),S.css("height","auto");var x=S.parent().parent().height();S.parent().css("height",x),S.css("height",x)}if("scrollTo"in i)E=parseInt(a.scrollTo);else if("scrollBy"in i)E+=parseInt(a.scrollBy);else if("destroy"in i)return M.remove(),H.remove(),void S.unwrap();r(E,!1,!0)}}else{a.height="auto"==i.height?S.parent().height():i.height;var C=e(w).addClass(a.wrapperClass).css({position:"relative",overflow:"hidden",width:a.width,height:a.height});S.css({overflow:"hidden",width:a.width,height:a.height});var H=e(w).addClass(a.railClass).css({width:a.size,height:"100%",position:"absolute",top:0,display:a.alwaysVisible&&a.railVisible?"block":"none","border-radius":a.railBorderRadius,background:a.railColor,opacity:a.railOpacity,zIndex:90}),M=e(w).addClass(a.barClass).css({background:a.color,width:a.size,position:"absolute",top:0,opacity:a.opacity,display:a.alwaysVisible?"block":"none","border-radius":a.borderRadius,BorderRadius:a.borderRadius,MozBorderRadius:a.borderRadius,WebkitBorderRadius:a.borderRadius,zIndex:99}),D="right"==a.position?{right:a.distance}:{left:a.distance};H.css(D),M.css(D),S.wrap(C),S.parent().append(M),S.parent().append(H),a.railDraggable&&M.bind("mousedown",function(i){var o=e(document);return d=!0,t=parseFloat(M.css("top")),pageY=i.pageY,o.bind("mousemove.slimscroll",function(e){currTop=t+e.pageY-pageY,M.css("top",currTop),r(0,M.position().top,!1)}),o.bind("mouseup.slimscroll",function(){d=!1,c(),o.unbind(".slimscroll")}),!1}).bind("selectstart.slimscroll",function(e){return e.stopPropagation(),e.preventDefault(),!1}),"ontouchstart"in window&&window.navigator.msPointerEnabled&&(S.bind("MSPointerDown",function(e){g=e.originalEvent.pageY}),S.bind("MSPointerMove",function(e){e.originalEvent.preventDefault();var t=(g-e.originalEvent.pageY)/a.touchScrollStep;r(t,!0),g=e.originalEvent.pageY})),H.hover(function(){l()},function(){c()}),M.hover(function(){h=!0},function(){h=!1}),S.hover(function(){u=!0,l(),c()},function(){u=!1,c()}),S.bind("touchstart",function(e){e.originalEvent.touches.length&&(g=e.originalEvent.touches[0].pageY)}),S.bind("touchmove",function(e){if(y||e.originalEvent.preventDefault(),e.originalEvent.touches.length){var t=(g-e.originalEvent.touches[0].pageY)/a.touchScrollStep;r(t,!0),g=e.originalEvent.touches[0].pageY}}),n(),"bottom"===a.start?(M.css({top:S.outerHeight()-M.outerHeight()}),r(0,!0)):"top"!==a.start&&(r(e(a.start).position().top,null,!0),a.alwaysVisible||M.hide()),s()}}),this}}),jQuery.fn.extend({slimscroll:jQuery.fn.slimScroll})}(jQuery);
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
/*!
* JavaScript Cookie v2.0.4
* https://github.com/js-cookie/js-cookie
*
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
* Released under the MIT license
*/
!function(e){if("function"==typeof define&&define.amd)define(e);else if("object"==typeof exports)module.exports=e();else{var n=window.Cookies,t=window.Cookies=e();t.noConflict=function(){return window.Cookies=n,t}}}(function(){function e(){for(var e=0,n={};e<arguments.length;e++){var t=arguments[e];for(var o in t)n[o]=t[o]}return n}function n(t){function o(n,r,i){var c;if(arguments.length>1){if(i=e({path:"/"},o.defaults,i),"number"==typeof i.expires){var s=new Date;s.setMilliseconds(s.getMilliseconds()+864e5*i.expires),i.expires=s}try{c=JSON.stringify(r),/^[\{\[]/.test(c)&&(r=c)}catch(a){}return r=encodeURIComponent(String(r)),r=r.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),n=encodeURIComponent(String(n)),n=n.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),n=n.replace(/[\(\)]/g,escape),document.cookie=[n,"=",r,i.expires&&"; expires="+i.expires.toUTCString(),i.path&&"; path="+i.path,i.domain&&"; domain="+i.domain,i.secure?"; secure":""].join("")}n||(c={});for(var p=document.cookie?document.cookie.split("; "):[],u=/(%[0-9A-Z]{2})+/g,d=0;d<p.length;d++){var f=p[d].split("="),l=f[0].replace(u,decodeURIComponent),m=f.slice(1).join("=");'"'===m.charAt(0)&&(m=m.slice(1,-1));try{if(m=t&&t(m,l)||m.replace(u,decodeURIComponent),this.json)try{m=JSON.parse(m)}catch(a){}if(n===l){c=m;break}n||(c[l]=m)}catch(a){}}return c}return o.get=o.set=o,o.getJSON=function(){return o.apply({json:!0},[].slice.call(arguments))},o.defaults={},o.remove=function(n,t){o(n,"",e(t,{expires:-1}))},o.withConverter=n,o}return n()});
@@ -0,0 +1,50 @@
var Login = function() {
var handleLogin = function() {
$('.login-form input').keypress(function(e) {
if (e.which == 13) {
if ($('.login-form').validate().form()) {
$('.login-form').submit(); //form validation success, call ajax form submit
}
return false;
}
});
$('.forget-form input').keypress(function(e) {
if (e.which == 13) {
if ($('.forget-form').validate().form()) {
$('.forget-form').submit();
}
return false;
}
});
}
return {
//main function to initiate the module
init: function() {
handleLogin();
// init background slide images
$('.login-bg').backstretch([
"../Content/theme/assets/pages/img/login/bg1.jpg",
"../Content/theme/assets/pages/img/login/bg2.jpg",
"../Content/theme/assets/pages/img/login/bg3.jpg"
], {
fade: 1000,
duration: 8000
}
);
}
};
}();
jQuery(document).ready(function() {
Login.init();
});
@@ -0,0 +1,456 @@
/* Cubic Bezier Transition */
.user-login-5 {
min-height: 100vh;
background: linear-gradient(#006fc7, #7dc2f8);
}
.user-login-5 .bs-reset {
margin: 0;
padding: 0;
}
.user-login-5 .forgot-password.text-right {
text-align: right;
float: right;
margin-top: 30px;
}
.user-login-5 .forgot-password.text-right a {
text-decoration: underline;
color: #fff !important;
}
.user-login-5 .forgot-password.text-right a:hover {
color: #ffff99 !important;
text-decoration: underline !important;
}
.user-login-5 .login-bg {
background-position: center;
background-size: cover;
background-repeat: no-repeat;
min-height: 100vh;
}
.backstretch img {
opacity: 0.8;
}
.user-login-5 .login-logo {
position: relative;
text-align: center;
margin-top: 0%;
margin-bottom: 8%;
}
.user-login-5 .login-logo.login-6 {
top: 80px;
left: 80px;
}
.user-login-5 .login-container {
position: relative;
text-align: center;
min-height: 100vh;
}
.user-login-5 .login-container .rem-password {
text-align: left;
}
.user-login-5 .login-container > .login-content,
.user-login-5 .login-container .login-social,
.user-login-5 .login-container .login-copyright {
padding: 0 80px;
}
.user-login-5 .login-container > .login-content {
margin-top: 10% !important;
}
.user-login-5 .login-container > .login-content > h1 {
font-size: 30px;
font-weight: 300;
color: #fff;
}
.user-login-5 .login-container > .login-content p {
color: #fff;
font-size: 15px;
line-height: 22px;
}
.user-login-5 .login-container > .login-content > .login-form {
margin-top: 40px;
color: #fff;
font-size: 15px;
}
.user-login-5 .login-container > .login-content > .login-form .form-control {
width: 100%;
padding: 0 40px 0 40px;
height: 45px;
line-height: 45px;
border: none;
border-bottom: 1px solid;
border-radius: 30px !important;
border-color: #3093e2;
color: #696969 !important;
font-size: 17px;
margin-bottom: 0px;
}
input {
background-color: rgb(255, 255, 255) !important;
}
.user-login-5 .login-container > .login-content > .login-form .form-control:focus {
outline: 0;
}
.user-login-5 .login-container > .login-content > .login-form .forgot-password,
.user-login-5 .login-container > .login-content > .login-form .login-button {
display: inline-block;
}
.user-login-5 .login-container > .login-content > .login-form .rem-password {
margin-bottom: 20px;
}
.user-login-5 .login-container > .login-content > .login-form .rem-password > p {
margin: 0;
}
.user-login-5 .login-container > .login-content > .login-form .rem-password > .rem-checkbox {
border-color: #a4aab2;
}
.user-login-5 .login-container > .login-content > .login-form .forgot-password {
margin-right: 1em;
}
.user-login-5 .login-container > .login-content > .login-form .forgot-password > a {
color: #a4aab2;
}
.user-login-5 .login-container > .login-content > .login-form .forgot-password > a:hover {
color: #337ab7;
text-decoration: none;
}
.user-login-5 .login-container > .login-content > .login-form .forgot-password > a:focus {
color: #a4aab2;
text-decoration: none;
}
.input-icon i {
padding: 4px 0 0 6px;
}
.mt-checkbox-outline {
float: left;
margin-bottom: 20px;
}
.dl-lblError {
padding-bottom: 10px;
display: block;
}
.user-login-5 .login-container > .login-footer {
position: absolute;
bottom: 0;
width: 100%;
padding-bottom: 10px;
}
.user-login-5 .login-container > .login-footer .login-social {
padding-right: 0;
}
.user-login-5 .login-container > .login-footer .login-social li {
display: inline-block;
list-style: none;
margin-right: 1em;
}
.user-login-5 .login-container > .login-footer .login-social a {
color: #a9b5be;
font-size: 18px;
}
.user-login-5 .login-container > .login-footer .login-social a:hover {
color: #337ab7;
text-decoration: none;
}
.user-login-5 .login-container > .login-footer .login-social a:focus {
color: #a9b5be;
}
.user-login-5 .login-container > .login-footer .login-copyright {
padding-left: 0;
margin-top: 6px;
}
.user-login-5 .login-container > .login-footer .login-copyright > p {
margin: 0;
font-size: 13px;
color: #a9b5be;
}
.user-login-5 .alert {
margin-top: -60px;
}
.user-login-5 .form-group.has-error {
border-bottom: 2px solid #ed6b75 !important;
}
.user-login-5 .form-group.valid {
border-bottom: 1px solid #a0a9b4 !important;
}
@media (max-width: 1365px) {
.user-login-5 .login-logo.login-6 {
top: 40px;
left: 40px;
}
.user-login-5 .login-container > .login-content,
.user-login-5 .login-container .login-social,
.user-login-5 .login-container .login-copyright {
padding: 0 30px;
}
.user-login-5 .login-container .login-social {
padding-right: 0;
}
.user-login-5 .login-container .login-copyright {
padding-left: 0;
}
}
@media (max-width: 767px) {
.user-login-5 {
/*min-height: 50vh;*/
}
.user-login-5 .login-bg {
min-height: 50vh;
}
.user-login-5 .login-logo.login-6 {
position: relative;
margin: 0 0 40px 0;
}
.user-login-5 .login-container {
min-height: 50vh;
}
.user-login-5 .login-container > .login-content {
margin-top: 10px;
}
.user-login-5 .login-container > .login-content .login-form {
margin-top: 40px;
}
.user-login-5 .login-container .rem-password {
margin-bottom: 1em;
}
.user-login-5 .login-container > .login-footer {
position: relative;
margin-top: 40px;
padding-bottom: 0;
}
.user-login-5 .login-container > .login-footer .login-social li {
margin-right: 0.5em;
}
.user-login-5 .alert {
margin-top: -20px;
}
}
@media (max-width: 640px) {
.user-login-5 .login-container > .login-content .text-right {
}
}
/*** login QLVB ***/
.btn.blue, .btn.btn-success {
height: 45px;
line-height: 45px !important;
padding: 0;
text-align: center;
width: 100%;
border-radius: 30px !important;
font-size: 16px;
background-color: #0573cb !important;
border-color: #0573cb !important;
}
.btn.blue.btn-forgot-pass {
width: 50%;
}
.btn.btn-outline.yellow-crusta {
line-height: 30px !important;
border: none !important;
}
.btn.blue:hover, .btn.btn-success:hover {
background-color: #0573cb !important;
border-color: #0573cb !important;
}
.user-login-5 h3.font-green {
display: none;
}
.goback {
margin-top: 20px;
}
.goback a {
text-decoration: underline;
color: #fff !important;
}
.goback a {
text-decoration: underline !important;
color: #ffff99 !important;
}
.validation-summary-errors ul, .validation-summary-errors li {
list-style-type: none;
padding: 0px;
}
.validation-summary-errors {
color: #ec4220 !important;
}
.alert.alert-success {
position: absolute;
right: 20px;
bottom: 0;
margin-top: 0;
}
.login-content .form-control {
width: 100%;
padding: 0 20px;
height: 45px;
line-height: 45px;
border: none;
border-bottom: 1px solid #ccc;
border-radius: 30px !important;
border-top: 1px solid #ccc;
background-color: #fff !important;
color: #696969 !important;
font-size: 17px;
margin-bottom: 25px;
overflow: hidden;
}
h1.titleChangePass {
margin-bottom: 30px;
}
@media (max-height: 767px) {
.user-login-5 .login-container > .login-content {
margin-top: 10px;
}
}
/*change pass*/
.change-pass .form-group {
text-align: left;
}
.change-pass .form-group label {
text-align: left;
color: #fff;
}
.login-content .change-pass .form-control {
border-radius: 0px 6px 6px 0px !important;
margin-bottom: 0px !important;
}
.login-content .change-pass span.input-group-addon {
padding: 0 16px !important;
}
.change-pass .btn.blue {
width: 47% !important;
float: right !important;
margin-left: 3%;
padding: 0 12px;
overflow: hidden;
}
#lblAccountInfo {
color: #EC141B;
font-size: 20px;
padding-top: 10px;
padding: 10px 0px;
display: block;
}
.input-group {
position: relative;
display: table;
border-collapse: separate;
}
.input-group-addon {
padding: 6px 12px;
font-size: 14px;
font-weight: 400;
line-height: 1;
color: #555;
text-align: center;
background-color: #eee;
border: 1px solid #ccc;
border-radius: 4px 0 0 4px;
height: 45px;
line-height: 43px;
}
@media (min-width: 1030px) {
.login-ees {
margin-left: 5px;
margin-bottom: 28px;
}
}
@media (max-width: 767px) {
#login-slide-show {
display: none;
}
.login-content {
margin-top: 10% !important;
}
}
@media (max-width: 1029px) {
.login-ees {
float: left;
margin-bottom: 15px;
clear: both;
}
}
.login-ees i {
font-size: 22px;
padding-right: 10px;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -92,12 +92,36 @@
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
System
</div>
<!-- Nav Item - Users Collapse Menu -->
<li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapseUsers"
aria-expanded="true" aria-controls="collapseUsers">
<i class="fas fa-fw fa-users"></i>
<span>Users</span>
</a>
<div id="collapseUsers" class="collapse" aria-labelledby="headingUsers" data-parent="#accordionSidebar">
<div class="bg-white py-2 collapse-inner rounded">
<h6 class="collapse-header">User Management:</h6>
<a class="collapse-item" th:href="@{/manage/users}">All Users</a>
<a class="collapse-item" th:href="@{/manage/users/new}">Add New</a>
</div>
</div>
</li>
<!-- Divider -->
<hr class="sidebar-divider">
<!-- Heading -->
<div class="sidebar-heading">
Interface
</div>
<!-- Nav Item - Pages Collapse Menu -->
<!-- Nav Item - Components Collapse Menu -->
<li class="nav-item">
<a class="nav-link collapsed" href="#" data-toggle="collapse" data-target="#collapseTwo"
aria-expanded="true" aria-controls="collapseTwo">
@@ -145,19 +169,22 @@
<li class="nav-item dropdown no-arrow">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="mr-2 d-none d-lg-inline text-gray-600 small">Admin User</span>
<span class="mr-2 d-none d-lg-inline text-gray-600 small" sec:authentication="name">Admin User</span>
<img class="img-profile rounded-circle"
src="https://startbootstrap.github.io/startbootstrap-sb-admin-2/img/undraw_profile.svg">
</a>
<!-- Dropdown - User Information -->
<div class="dropdown-menu dropdown-menu-right shadow animated--grow-in"
aria-labelledby="userDropdown">
<a class="dropdown-item" href="#">
<a class="dropdown-item" th:href="@{/manage/profile}">
<i class="fas fa-user fa-sm fa-fw mr-2 text-gray-400"></i>
Profile
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" data-toggle="modal" data-target="#logoutModal">
<form method="post" th:action="@{/manage/logout}" id="logoutForm" class="d-none">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
</form>
<a class="dropdown-item" href="#" onclick="document.getElementById('logoutForm').submit(); return false;">
<i class="fas fa-sign-out-alt fa-sm fa-fw mr-2 text-gray-400"></i>
Logout
</a>
@@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Cài đặt 2FA / 2FA Setup - SIS Vietnam</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1" name="viewport">
<link th:href="@{/login-assets/css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/bootstrap.min.css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/login-dl.css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/customer-style.css}" rel="stylesheet" type="text/css">
</head>
<body class="login">
<div class="user-login-5">
<div class="row bs-reset">
<div class="col-md-8 col-sm-6 hidden-xs bs-reset mt-login-5-bsfix">
<div class="login-bg" style="background: none; position: relative; z-index: 0;">
<div class="backstretch" style="left: 0px; top: 0px; overflow: hidden; margin: 0px; padding: 0px; height: 100vh; width: 100%; z-index: -999998; position: absolute;">
<img th:src="@{/login-assets/bg2.jpg}" style="position: absolute; margin: 0px; padding: 0px; border-width: medium; border-style: none; width: 100%; height: 100%; object-fit: cover; z-index: -999999; left: 0px; top: 0px;">
</div>
</div>
</div>
<div class="col-md-4 col-sm-6 login-container bs-reset mt-login-5-bsfix">
<div class="login-content">
<img class="login-logo" th:src="@{/login-assets/logo.png}" style="width:260px; margin-bottom: 10px;">
<h3 class="font-green">Cài đặt 2FA / 2FA Setup</h3>
<p>Hệ thống yêu cầu bạn phải cài đặt 2FA trước khi tiếp tục. Vui lòng sử dụng ứng dụng Google Authenticator để quét mã QR bên dưới.</p>
<p>The system requires you to set up 2FA. Please use Google Authenticator to scan the QR code below.</p>
<div style="text-align: center; margin-bottom: 20px;">
<img th:src="${qrCode}" alt="QR Code" style="width: 200px; border: 1px solid #ccc; padding: 10px; border-radius: 10px; background: white;" />
<br/><br/>
<small>Hoặc nhập mã bí mật thủ công / Or enter secret manually:</small><br/>
<strong th:text="${secret}"></strong>
</div>
<div th:if="${errorMessage}" class="alert alert-danger">
<span th:text="${errorMessage}"></span>
</div>
<form th:action="@{/2fa/setup}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<div class="form-group">
<input autocomplete="off" class="form-control form-control-solid h-auto py-6 px-6 rounded-lg" required id="code" name="code" placeholder="Mã 6 số / 6-digit Code" type="text" autofocus>
</div>
<div class="pb-lg-0 pb-5 d-flex align-items-center mt-4">
<button type="submit" class="btn blue mr-3">Xác nhận & Hoàn tất / Verify & Finish</button>
</div>
</form>
</div>
<div class="login-footer">
<div class="row bs-reset">
<div class="col-xs-12 bs-reset">
<div class="login-copyright text-right">
<p>Copyright &copy; SIS Vietnam 2026</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Xác minh 2FA / 2FA Verification - SIS Vietnam</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1" name="viewport">
<link th:href="@{/login-assets/css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/bootstrap.min.css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/login-dl.css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/customer-style.css}" rel="stylesheet" type="text/css">
</head>
<body class="login">
<div class="user-login-5">
<div class="row bs-reset">
<div class="col-md-8 col-sm-6 hidden-xs bs-reset mt-login-5-bsfix">
<div class="login-bg" style="background: none; position: relative; z-index: 0;">
<div class="backstretch" style="left: 0px; top: 0px; overflow: hidden; margin: 0px; padding: 0px; height: 100vh; width: 100%; z-index: -999998; position: absolute;">
<img th:src="@{/login-assets/bg2.jpg}" style="position: absolute; margin: 0px; padding: 0px; border-width: medium; border-style: none; width: 100%; height: 100%; object-fit: cover; z-index: -999999; left: 0px; top: 0px;">
</div>
</div>
</div>
<div class="col-md-4 col-sm-6 login-container bs-reset mt-login-5-bsfix">
<div class="login-content">
<img class="login-logo" th:src="@{/login-assets/logo.png}" style="width:260px; margin-bottom: 30px;">
<h3 class="font-green">Xác minh 2 bước / Two-Factor Authentication</h3>
<p>Vui lòng nhập mã 6 số từ ứng dụng Google Authenticator của bạn.</p>
<p>Please enter the 6-digit code from your Google Authenticator app.</p>
<div th:if="${errorMessage}" class="alert alert-danger">
<span th:text="${errorMessage}"></span>
</div>
<form th:action="@{/2fa}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<div class="form-group">
<input autocomplete="off" class="form-control form-control-solid h-auto py-6 px-6 rounded-lg" required id="code" name="code" placeholder="Mã 6 số / 6-digit Code" type="text" autofocus>
</div>
<div class="pb-lg-0 pb-5 d-flex align-items-center mt-4">
<button type="submit" class="btn blue mr-3">Xác minh / Verify</button>
</div>
</form>
</div>
<div class="login-footer">
<div class="row bs-reset">
<div class="col-xs-12 bs-reset">
<div class="login-copyright text-right">
<p>Copyright &copy; SIS Vietnam 2026</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,114 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Đăng nhập/Login - SIS Vietnam</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1" name="viewport">
<!-- BEGIN GLOBAL MANDATORY STYLES -->
<link th:href="@{/login-assets/css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/font-awesome.min.css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/simple-line-icons.min.css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/bootstrap.min.css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/bootstrap-switch.min.css}" rel="stylesheet" type="text/css">
<!-- END GLOBAL MANDATORY STYLES -->
<!-- BEGIN PAGE LEVEL PLUGINS -->
<link th:href="@{/login-assets/select2.min.css}" rel="stylesheet" type="text/css">
<link th:href="@{/login-assets/select2-bootstrap.min.css}" rel="stylesheet" type="text/css">
<!-- END PAGE LEVEL PLUGINS -->
<!-- BEGIN THEME GLOBAL STYLES -->
<link th:href="@{/login-assets/components.min.css}" rel="stylesheet" id="style_components" type="text/css">
<link th:href="@{/login-assets/plugins.min.css}" rel="stylesheet" type="text/css">
<!-- END THEME GLOBAL STYLES -->
<!-- BEGIN PAGE LEVEL STYLES -->
<link th:href="@{/login-assets/login-dl.css}" rel="stylesheet" type="text/css">
<!-- END PAGE LEVEL STYLES -->
<link rel="shortcut icon" th:href="@{/favicon.ico}">
<link th:href="@{/login-assets/customer-style.css}" rel="stylesheet" type="text/css">
</head>
<body class="login">
<div class="user-login-5">
<div class="row bs-reset">
<div class="col-md-8 col-sm-6 hidden-xs bs-reset mt-login-5-bsfix">
<div class="login-bg" style="background: none; position: relative; z-index: 0;">
<div class="backstretch" style="left: 0px; top: 0px; overflow: hidden; margin: 0px; padding: 0px; height: 100vh; width: 100%; z-index: -999998; position: absolute;">
<img th:src="@{/login-assets/bg2.jpg}" style="position: absolute; margin: 0px; padding: 0px; border-width: medium; border-style: none; width: 100%; height: 100%; object-fit: cover; z-index: -999999; left: 0px; top: 0px;">
</div>
</div>
</div>
<div class="col-md-4 col-sm-6 login-container bs-reset mt-login-5-bsfix">
<div class="login-content">
<img class="login-logo" th:src="@{/login-assets/logo.png}" style="width:260px; margin-bottom: 30px;">
<div th:if="${param.error}" class="alert alert-danger">
<span>Tên đăng nhập hoặc mật khẩu không đúng / Invalid username or password.</span>
</div>
<div th:if="${param.logout}" class="alert alert-info">
<span>Bạn đã đăng xuất thành công / You have been logged out.</span>
</div>
<form th:action="@{/manage/login}" class="form fv-plugins-bootstrap fv-plugins-framework" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<!--begin::Form group-->
<div class="form-group fv-plugins-icon-container">
<input autocomplete="username" class="form-control form-control-solid h-auto py-6 px-6 rounded-lg" required id="username" name="username" placeholder="Tên đăng nhập / Login Name" type="text" autofocus>
</div>
<!--end::Form group-->
<!--begin::Form group-->
<div class="form-group fv-plugins-icon-container">
<input autocomplete="current-password" class="form-control form-control-solid h-auto py-6 px-6 rounded-lg" required id="password" name="password" placeholder="Mật khẩu / Password" type="password">
</div>
<div class="form-group fv-plugins-icon-container">
<div class="rem-password">
<label class="rememberme mt-checkbox mt-checkbox-outline font-white">
<input id="remember-me" name="remember-me" type="checkbox" value="true"> Ghi nhớ / Remember
<span></span>
</label>
</div>
</div>
<!--end::Form group-->
<!--begin::Action-->
<div class="pb-lg-0 pb-5 d-flex align-items-center">
<button type="submit" class="btn blue mr-3">Đăng nhập / Login</button>
<a th:href="@{/oauth2/authorization/google}" class="btn btn-outline-danger">
<i class="fab fa-google"></i> Login with Google
</a>
</div>
<div class="form-group fv-plugins-icon-container mt-4">
<div class="pull-left">
<a th:href="@{/}" class="btn" style="padding-left: 0;">
<i class="fa fa-arrow-left"></i> Quay lại trang chủ / Back to Home
</a>
</div>
<div class="pull-right">
<label class="rememberme mt-checkbox mt-checkbox-outline goback">
<a th:href="@{/account/reset/request}" class="btn">
Quên mật khẩu / Forgot Password?
</a>
</label>
</div>
</div>
<!--end::Action-->
</form>
</div>
<div class="login-footer">
<div class="row bs-reset">
<div class="col-xs-12 bs-reset">
<div class="login-copyright text-right">
<p>Copyright &copy; SIS Vietnam 2026</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script th:src="@{/login-assets/jquery.min.js}" type="text/javascript"></script>
<script th:src="@{/login-assets/bootstrap.min.js}" type="text/javascript"></script>
</body>
</html>
@@ -82,6 +82,7 @@
<div class="card-body">
<form id="pageForm" th:action="${isNew} ? @{/manage/pages} : @{/manage/pages/{id}(id=${page.id})}"
th:object="${page}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<!-- Validation errors summary -->
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger">
@@ -118,7 +119,7 @@
<div class="row">
<!-- Page Type -->
<div class="col-md-4">
<div class="col-md-3">
<div class="form-group">
<label for="pageType" class="font-weight-bold">Page Type <span
class="text-danger">*</span></label>
@@ -128,8 +129,19 @@
</div>
</div>
<!-- Layout -->
<div class="col-md-3">
<div class="form-group">
<label for="pageLayout" class="font-weight-bold">Layout <span
class="text-danger">*</span></label>
<select class="form-control" id="pageLayout" th:field="*{layout}">
<option th:each="l : ${layouts}" th:value="${l}" th:text="${l}"></option>
</select>
</div>
</div>
<!-- Status -->
<div class="col-md-4">
<div class="col-md-3">
<div class="form-group">
<label for="pageStatus" class="font-weight-bold">Status <span
class="text-danger">*</span></label>
@@ -140,7 +152,7 @@
</div>
<!-- Display Order -->
<div class="col-md-4">
<div class="col-md-3">
<div class="form-group">
<label for="pageDisplayOrder" class="font-weight-bold">Display Order</label>
<input type="number" class="form-control" id="pageDisplayOrder"
@@ -72,6 +72,7 @@
</a>
<form th:action="@{/manage/pages/{id}/delete(id=${page.id})}" method="post" style="display:inline;"
onsubmit="return confirm('Are you sure you want to delete this page?');">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<button type="submit" class="btn btn-sm btn-danger" title="Delete">
<i class="fas fa-trash"></i> Delete
</button>
@@ -44,6 +44,7 @@
<div class="card-body">
<form th:action="${isNew} ? @{/manage/posts/categories} : @{/manage/posts/categories/{id}(id=${category.id})}"
th:object="${category}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<!-- Validation errors -->
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger alert-sm">
@@ -150,6 +151,7 @@
<form th:action="@{/manage/posts/categories/{id}/delete(id=${cat.id})}" method="post"
style="display:inline;"
onsubmit="return confirm('Are you sure you want to delete this category?');">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<button type="submit" class="btn btn-sm btn-outline-danger" title="Delete">
<i class="fas fa-trash"></i>
</button>
@@ -131,6 +131,7 @@
<form id="postForm" th:action="${isNew} ? @{/manage/posts/create} : @{/manage/posts/{id}(id=${post.id})}"
th:object="${post}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<!-- Validation errors summary -->
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger">
@@ -129,6 +129,7 @@
</a>
<form th:action="@{/manage/posts/{id}/delete(id=${post.id})}" method="post" style="display:inline;"
onsubmit="return confirm('Are you sure you want to delete this post?');">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<button type="submit" class="btn btn-sm btn-outline-danger" title="Delete">
<i class="fas fa-trash"></i>
</button>
@@ -44,6 +44,7 @@
<div class="card-body">
<form th:action="${isNew} ? @{/manage/posts/tags} : @{/manage/posts/tags/{id}(id=${tag.id})}"
th:object="${tag}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<!-- Validation errors -->
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger alert-sm">
@@ -134,6 +135,7 @@
<form th:action="@{/manage/posts/tags/{id}/delete(id=${t.id})}" method="post"
style="display:inline;"
onsubmit="return confirm('Are you sure you want to delete this tag?');">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<button type="submit" class="btn btn-sm btn-outline-danger" title="Delete">
<i class="fas fa-trash"></i>
</button>
@@ -0,0 +1,133 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{fragments/manage-layout}">
<head>
<title th:text="${isNew} ? 'Add New User' : 'Edit User'">User Form</title>
</head>
<body>
<div layout:fragment="content">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800" th:text="${isNew} ? 'Add New User' : 'Edit User'">User Form</h1>
<a th:href="@{/manage/users}" class="d-none d-sm-inline-block btn btn-sm btn-secondary shadow-sm">
<i class="fas fa-arrow-left fa-sm text-white-50"></i> Back to All Users
</a>
</div>
<!-- Form Card -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary" th:text="${isNew} ? 'Create a New User' : 'Update User Details'">Form</h6>
</div>
<div class="card-body">
<form th:action="${isNew} ? @{/manage/users} : @{/manage/users/{login}(login=${userDTO.login})}"
th:object="${userDTO}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<!-- Form Errors Summary -->
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger">
<ul class="mb-0">
<li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
</ul>
</div>
<div class="row">
<div class="col-md-6">
<!-- Login -->
<div class="form-group">
<label for="login" class="font-weight-bold">Login <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="login" th:field="*{login}"
th:classappend="${#fields.hasErrors('login')} ? 'is-invalid' : ''"
th:readonly="${!isNew}" placeholder="Username" required>
<div class="invalid-feedback" th:if="${#fields.hasErrors('login')}" th:errors="*{login}"></div>
<small class="form-text text-muted" th:if="${!isNew}">The login cannot be changed after creation.</small>
</div>
<!-- Email -->
<div class="form-group">
<label for="email" class="font-weight-bold">Email <span class="text-danger">*</span></label>
<input type="email" class="form-control" id="email" th:field="*{email}"
th:classappend="${#fields.hasErrors('email')} ? 'is-invalid' : ''"
placeholder="user@example.com" required>
<div class="invalid-feedback" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>
</div>
<div class="row">
<!-- First Name -->
<div class="col-md-6">
<div class="form-group">
<label for="firstName" class="font-weight-bold">First Name</label>
<input type="text" class="form-control" id="firstName" th:field="*{firstName}"
th:classappend="${#fields.hasErrors('firstName')} ? 'is-invalid' : ''">
<div class="invalid-feedback" th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}"></div>
</div>
</div>
<!-- Last Name -->
<div class="col-md-6">
<div class="form-group">
<label for="lastName" class="font-weight-bold">Last Name</label>
<input type="text" class="form-control" id="lastName" th:field="*{lastName}"
th:classappend="${#fields.hasErrors('lastName')} ? 'is-invalid' : ''">
<div class="invalid-feedback" th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}"></div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<!-- Activated -->
<div class="form-group">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="activated" th:field="*{activated}">
<label class="custom-control-label font-weight-bold" for="activated">Activated</label>
</div>
<small class="form-text text-muted">Deactivated users cannot log in.</small>
</div>
<!-- Language -->
<div class="form-group mt-3">
<label for="langKey" class="font-weight-bold">Language</label>
<select class="form-control" id="langKey" th:field="*{langKey}">
<option value="en">English</option>
<option value="vi">Vietnamese</option>
</select>
</div>
<!-- Roles / Authorities -->
<div class="form-group mt-3">
<label class="font-weight-bold">Profiles (Roles)</label>
<div>
<div th:each="auth : ${authorities}" class="custom-control custom-checkbox custom-control-inline">
<input type="checkbox" class="custom-control-input"
th:id="${'auth_' + auth}" th:value="${auth}" th:field="*{authorities}">
<label class="custom-control-label" th:for="${'auth_' + auth}" th:text="${auth}"></label>
</div>
</div>
</div>
<!-- Notice for new user -->
<div class="alert alert-info mt-3" th:if="${isNew}">
<i class="fas fa-info-circle"></i> A random password and reset key will be generated for new users. They must reset their password to log in.
</div>
</div>
</div>
<hr>
<div class="d-flex justify-content-between">
<a th:href="@{/manage/users}" class="btn btn-secondary">
<i class="fas fa-times"></i> Cancel
</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i>
<span th:text="${isNew} ? 'Create User' : 'Update User'">Save</span>
</button>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,126 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{fragments/manage-layout}">
<head>
<title>Users Management</title>
</head>
<body>
<div layout:fragment="content">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">User Management</h1>
<a th:href="@{/manage/users/new}" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm">
<i class="fas fa-plus fa-sm text-white-50"></i> Add New User
</a>
</div>
<!-- Alerts -->
<div th:if="${successMessage}" class="alert alert-success alert-dismissible fade show" role="alert">
<span th:text="${successMessage}"></span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div th:if="${errorMessage}" class="alert alert-danger alert-dismissible fade show" role="alert">
<span th:text="${errorMessage}"></span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<!-- DataTales Example -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">All Registered Users</h6>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Login</th>
<th>Email</th>
<th>Status</th>
<th>Language</th>
<th>Profiles</th>
<th>Created Date</th>
<th class="text-center" style="width: 150px;">Actions</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td><strong th:text="${user.login}">login</strong></td>
<td th:text="${user.email}">user@example.com</td>
<td>
<span th:if="${user.activated}" class="badge badge-success">Activated</span>
<span th:unless="${user.activated}" class="badge badge-danger">Deactivated</span>
</td>
<td th:text="${user.langKey}">en</td>
<td>
<span th:each="auth : ${user.authorities}" class="badge badge-info mr-1" th:text="${auth}">ROLE</span>
</td>
<td th:text="${#temporals.format(user.createdDate, 'dd-MM-yyyy HH:mm')}"></td>
<td class="text-center">
<a th:href="@{/manage/users/{login}/edit(login=${user.login})}" class="btn btn-primary btn-sm" title="Edit User">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-danger btn-sm" title="Delete User"
th:data-login="${user.login}"
onclick="openDeleteModal(this.getAttribute('data-login'))">
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
<tr th:if="${#lists.isEmpty(users)}">
<td colspan="7" class="text-center">No users found.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<section layout:fragment="scripts">
<!-- Delete Confirmation Modal -->
<div class="modal fade" id="deleteUserModal" tabindex="-1" role="dialog" aria-labelledby="deleteUserModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form id="deleteUserForm" method="post" action="">
<!-- JHipster/Spring Security CSRF token if needed -->
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<div class="modal-header">
<h5 class="modal-title" id="deleteUserModalLabel">Confirm Delete</h5>
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
Are you sure you want to delete user <strong id="deleteUserLoginDisplay"></strong>?
<br>
<span class="text-danger small">This action cannot be undone.</span>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" type="button" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-danger">Delete</button>
</div>
</form>
</div>
</div>
</div>
<script>
function openDeleteModal(login) {
document.getElementById('deleteUserLoginDisplay').textContent = login;
document.getElementById('deleteUserForm').action = '/manage/users/' + login + '/delete';
$('#deleteUserModal').modal('show');
}
</script>
</section>
</body>
</html>
@@ -0,0 +1,151 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{fragments/manage-layout}">
<head>
<title>My Profile</title>
</head>
<body>
<div layout:fragment="content">
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">My Profile</h1>
</div>
<!-- Success/Error Alerts for Profile -->
<div th:if="${successMessage}" class="alert alert-success alert-dismissible fade show" role="alert">
<span th:text="${successMessage}"></span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div th:if="${errorMessage}" class="alert alert-danger alert-dismissible fade show" role="alert">
<span th:text="${errorMessage}"></span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<!-- Success/Error Alerts for Password -->
<div th:if="${successPasswordMessage}" class="alert alert-success alert-dismissible fade show" role="alert">
<span th:text="${successPasswordMessage}"></span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div th:if="${errorPasswordMessage}" class="alert alert-danger alert-dismissible fade show" role="alert">
<span th:text="${errorPasswordMessage}"></span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="row">
<!-- Profile Info Column -->
<div class="col-lg-6 mb-4">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Personal Information</h6>
</div>
<div class="card-body">
<form th:action="@{/manage/profile/update}" th:object="${userDTO}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<!-- Form Errors Summary -->
<div th:if="${#fields.hasErrors('*')}" class="alert alert-danger">
<ul class="mb-0">
<li th:each="err : ${#fields.errors('*')}" th:text="${err}"></li>
</ul>
</div>
<div class="form-group">
<label class="font-weight-bold">Login (Username)</label>
<input type="text" class="form-control" th:field="*{login}" readonly>
</div>
<div class="form-group">
<label for="email" class="font-weight-bold">Email <span class="text-danger">*</span></label>
<input type="email" class="form-control" id="email" th:field="*{email}"
th:classappend="${#fields.hasErrors('email')} ? 'is-invalid' : ''" required>
<div class="invalid-feedback" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="firstName" class="font-weight-bold">First Name</label>
<input type="text" class="form-control" id="firstName" th:field="*{firstName}"
th:classappend="${#fields.hasErrors('firstName')} ? 'is-invalid' : ''">
<div class="invalid-feedback" th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}"></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="lastName" class="font-weight-bold">Last Name</label>
<input type="text" class="form-control" id="lastName" th:field="*{lastName}"
th:classappend="${#fields.hasErrors('lastName')} ? 'is-invalid' : ''">
<div class="invalid-feedback" th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}"></div>
</div>
</div>
</div>
<div class="form-group">
<label for="langKey" class="font-weight-bold">Display Language</label>
<select class="form-control" id="langKey" th:field="*{langKey}">
<option value="en">English</option>
<option value="vi">Vietnamese</option>
</select>
</div>
<hr>
<div class="text-right">
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Save Profile
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Password Column -->
<div class="col-lg-6 mb-4">
<div class="card shadow border-left-warning">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-warning">Change Password</h6>
</div>
<div class="card-body">
<form th:action="@{/manage/profile/password}" method="post">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<div class="form-group">
<label for="currentPassword" class="font-weight-bold">Current Password <span class="text-danger">*</span></label>
<input type="password" class="form-control" id="currentPassword" name="currentPassword" required>
</div>
<div class="form-group">
<label for="newPassword" class="font-weight-bold">New Password <span class="text-danger">*</span></label>
<input type="password" class="form-control" id="newPassword" name="newPassword" minlength="4" maxlength="50" required>
</div>
<div class="form-group">
<label for="confirmPassword" class="font-weight-bold">Confirm New Password <span class="text-danger">*</span></label>
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" minlength="4" maxlength="50" required>
</div>
<hr>
<div class="text-right">
<button type="submit" class="btn btn-warning">
<i class="fas fa-key"></i> Change Password
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>