Migrate frontend to pnpm+parcel with JS modules
This commit is contained in:
parent
4d8b7d6e62
commit
0b8fbea6c3
30 changed files with 2289 additions and 404 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,4 +1,6 @@
|
||||||
node_modules
|
node_modules
|
||||||
|
.parcel-cache
|
||||||
|
static/assets
|
||||||
.env
|
.env
|
||||||
*.exe
|
*.exe
|
||||||
pop-camarades
|
pop-camarades
|
||||||
|
|
|
||||||
55
frontend/index.css
Normal file
55
frontend/index.css
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
@import "npm:bootstrap/dist/css/bootstrap.css";
|
||||||
|
@import "npm:bootstrap-icons/font/bootstrap-icons.css";
|
||||||
|
|
||||||
|
img#header-logo {
|
||||||
|
width: 89px;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#login-card {
|
||||||
|
margin: auto;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-tile {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-photo {
|
||||||
|
background-color: #555;
|
||||||
|
color: #fff;
|
||||||
|
display: inline-block;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-click {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive th,
|
||||||
|
.table-responsive td {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#licenses h2 {
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#licenses p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
6
frontend/index.js
Normal file
6
frontend/index.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import "./index.css";
|
||||||
|
import "./src/tooltip";
|
||||||
|
import "./src/color-mode";
|
||||||
|
import "./src/search";
|
||||||
|
import "./src/account-manage";
|
||||||
|
import "./src/fields";
|
||||||
22
frontend/src/account-manage.js
Normal file
22
frontend/src/account-manage.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import $ from "jquery";
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
var path = window.location.pathname;
|
||||||
|
if (path === "/account/manage") {
|
||||||
|
initModule();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function initModule() {
|
||||||
|
$("#password").on("input", function () {
|
||||||
|
var enable = false;
|
||||||
|
if ($(this).val().length > 0) {
|
||||||
|
enable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#password-verify").prop("disabled", !enable);
|
||||||
|
$("#password-verify").prop("required", enable);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#password").trigger("input");
|
||||||
|
}
|
||||||
52
frontend/src/color-mode.js
Normal file
52
frontend/src/color-mode.js
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
import $ from "jquery";
|
||||||
|
|
||||||
|
// Function to toggle light/dark mode
|
||||||
|
function setColorMode(color) {
|
||||||
|
if (color == "dark") {
|
||||||
|
$(".toggle-dark-mode").children("i").removeClass("bi-moon");
|
||||||
|
$(".toggle-dark-mode").children("i").addClass("bi-sun");
|
||||||
|
} else {
|
||||||
|
$(".toggle-dark-mode").children("i").removeClass("bi-sun");
|
||||||
|
$(".toggle-dark-mode").children("i").addClass("bi-moon");
|
||||||
|
}
|
||||||
|
|
||||||
|
$("html").attr("data-bs-theme", color);
|
||||||
|
localStorage.setItem("color-mode", color);
|
||||||
|
$.post("/set-color-mode", { color_mode: color });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for click on light/dark mode button
|
||||||
|
$(() => {
|
||||||
|
$(".toggle-dark-mode").on("click", () => {
|
||||||
|
var currentState = $("html").attr("data-bs-theme");
|
||||||
|
|
||||||
|
var newState = "dark";
|
||||||
|
if (currentState === "dark") {
|
||||||
|
newState = "light";
|
||||||
|
}
|
||||||
|
|
||||||
|
setColorMode(newState);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// On page load: automatic light/dark toggle
|
||||||
|
$(() => {
|
||||||
|
var currentState = $("html").attr("data-bs-theme");
|
||||||
|
var storedState = localStorage.getItem("color-mode");
|
||||||
|
|
||||||
|
if (storedState && storedState !== currentState) {
|
||||||
|
return setColorMode(storedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentState === "auto") {
|
||||||
|
var newState = "light";
|
||||||
|
if (
|
||||||
|
window.matchMedia &&
|
||||||
|
window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
|
) {
|
||||||
|
newState = "dark";
|
||||||
|
}
|
||||||
|
|
||||||
|
return setColorMode(newState);
|
||||||
|
}
|
||||||
|
});
|
||||||
26
frontend/src/fields.js
Normal file
26
frontend/src/fields.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import $ from "jquery";
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
var path = window.location.pathname;
|
||||||
|
if (path.startsWith("/admin/fields/")) {
|
||||||
|
initModule();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function initModule() {
|
||||||
|
$("#field_type").on("change", function () {
|
||||||
|
var enable = false;
|
||||||
|
if ($(this).val() === "list") {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
$("#list").children(".list-disabled").remove();
|
||||||
|
} else {
|
||||||
|
$("#list").append(`<option class="list-disabled" selected></option>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#list").prop("disabled", !enable);
|
||||||
|
$("#list").prop("required", enable);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#field_type").trigger("change");
|
||||||
|
}
|
||||||
233
frontend/src/search.js
Normal file
233
frontend/src/search.js
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
import $ from "jquery";
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
var path = window.location.pathname;
|
||||||
|
if (path === "/members" || path === "/contacts") {
|
||||||
|
initSearch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function initSearch() {
|
||||||
|
$("#search").on("click", () => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#advanced").on("click", function () {
|
||||||
|
if ($(this).data("state") === "true") {
|
||||||
|
$(this).find("i").removeClass("bi-arrow-up");
|
||||||
|
$(this).find("i").addClass("bi-arrow-down");
|
||||||
|
$("#advanced-section").addClass("d-none");
|
||||||
|
$(this).data("state", "false");
|
||||||
|
} else {
|
||||||
|
$(this).find("i").removeClass("bi-arrow-down");
|
||||||
|
$(this).find("i").addClass("bi-arrow-up");
|
||||||
|
$("#advanced-section").removeClass("d-none");
|
||||||
|
$(this).data("state", "true");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#reset-search").on("click", function () {
|
||||||
|
$("#search-json").val("");
|
||||||
|
$("#search-page").val("1");
|
||||||
|
$("#search-form").trigger("submit");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#search-container").on("change", ".field-select", function () {
|
||||||
|
var lastField = $(this).attr("data-last-field");
|
||||||
|
var isInitial = $(this).attr("data-is-initial");
|
||||||
|
var initialValue = $(this).attr("data-initial-value");
|
||||||
|
|
||||||
|
var field = $(this).val();
|
||||||
|
if (field == "") {
|
||||||
|
$(this).parents(".row").remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(lastField === undefined || lastField.length === 0) &&
|
||||||
|
(isInitial === undefined || isInitial !== "yes")
|
||||||
|
) {
|
||||||
|
createFieldSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this).attr("data-last-field", field);
|
||||||
|
|
||||||
|
$(this).parents(".row").find(".end-col").find("input").remove();
|
||||||
|
$(this).parents(".row").find(".end-col").find("select").remove();
|
||||||
|
|
||||||
|
$(elem)
|
||||||
|
.parents(".row")
|
||||||
|
.find(".end-col")
|
||||||
|
.append(
|
||||||
|
$("<input>", {
|
||||||
|
class: "form-control",
|
||||||
|
type: "text",
|
||||||
|
disabled: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
var elem = $(this);
|
||||||
|
$.getJSON("/fields/" + field, function (data) {
|
||||||
|
$(this).parents(".row").find(".end-col").find("input").remove();
|
||||||
|
|
||||||
|
if (data.FieldType === "list") {
|
||||||
|
var select = $("<select>", {
|
||||||
|
class: "form-select",
|
||||||
|
"data-optional-field": field,
|
||||||
|
"data-search-type": "int",
|
||||||
|
});
|
||||||
|
$(elem).parents(".row").find(".end-col").append(select);
|
||||||
|
|
||||||
|
for (const [_, value] of Object.entries(data.List.ListItems)) {
|
||||||
|
$(select).append(
|
||||||
|
$("<option>", {
|
||||||
|
value: value.ID,
|
||||||
|
text: value.Value,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (data.FieldType === "number") {
|
||||||
|
$(elem)
|
||||||
|
.parents(".row")
|
||||||
|
.find(".end-col")
|
||||||
|
.append(
|
||||||
|
$("<input>", {
|
||||||
|
class: "form-control",
|
||||||
|
type: "number",
|
||||||
|
"data-optional-field": field,
|
||||||
|
"data-search-type": "int",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else if (data.FieldType === "date") {
|
||||||
|
$(elem)
|
||||||
|
.parents(".row")
|
||||||
|
.find(".end-col")
|
||||||
|
.append(
|
||||||
|
$("<input>", {
|
||||||
|
class: "form-control",
|
||||||
|
type: "date",
|
||||||
|
"data-optional-field": field,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$(elem)
|
||||||
|
.parents(".row")
|
||||||
|
.find(".end-col")
|
||||||
|
.append(
|
||||||
|
$("<input>", {
|
||||||
|
class: "form-control",
|
||||||
|
type: "text",
|
||||||
|
"data-optional-field": field,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initialValue !== undefined && initialValue.length > 0) {
|
||||||
|
$(elem).parents(".row").find("[data-optional-field]").val(initialValue);
|
||||||
|
$(elem).attr("data-initial-value", null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#search-container").on("keyup", "input[type=text]", function (e) {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
search();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var json = $("#search-json").val();
|
||||||
|
if (json.length > 0) {
|
||||||
|
var searchData = JSON.parse(json);
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(searchData)) {
|
||||||
|
if (key === "advanced") {
|
||||||
|
if (value) $("#advanced").trigger("click");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key === "fields") {
|
||||||
|
for (const [field_id, field_values] of Object.entries(value)) {
|
||||||
|
for (const field_value of field_values) {
|
||||||
|
createFieldSelection(field_id, field_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === "boolean") {
|
||||||
|
$("[data-search-field=" + key + "]").prop("checked", value);
|
||||||
|
} else {
|
||||||
|
$("[data-search-field=" + key + "]").val(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createFieldSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFieldSelection(field, value) {
|
||||||
|
var elem = $("#search-fields-model").clone().appendTo("#search-fields");
|
||||||
|
$(elem).attr("id", null);
|
||||||
|
$(elem).removeClass("d-none");
|
||||||
|
$(elem).addClass("search-fields-count");
|
||||||
|
|
||||||
|
if (field !== undefined && field.length > 0) {
|
||||||
|
$(elem).find(".field-select").attr("data-initial-value", value);
|
||||||
|
$(elem).find(".field-select").attr("data-is-initial", "yes");
|
||||||
|
$(elem).find(".field-select").val(field);
|
||||||
|
$(elem).find(".field-select").trigger("change");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function search() {
|
||||||
|
var advancedSearch = $("#advanced").data("state") === "true";
|
||||||
|
var searchData = {
|
||||||
|
advanced: advancedSearch,
|
||||||
|
};
|
||||||
|
|
||||||
|
$("[data-search-field]").each(function () {
|
||||||
|
var advancedField = $(this).data("search-advanced");
|
||||||
|
if (!advancedSearch && advancedField) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = $(this).data("search-field");
|
||||||
|
var value = $(this).val();
|
||||||
|
|
||||||
|
if ($(this).data("search-type") === "int") {
|
||||||
|
value = parseInt(value);
|
||||||
|
if (isNaN(value)) return;
|
||||||
|
} else if ($(this).attr("type") === "checkbox") {
|
||||||
|
value = $(this).prop("checked");
|
||||||
|
}
|
||||||
|
|
||||||
|
searchData[index] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
var fields = {};
|
||||||
|
$("[data-optional-field]:not(:disabled)").each(function () {
|
||||||
|
if (!advancedSearch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = $(this).attr("data-optional-field");
|
||||||
|
var value = $(this).val();
|
||||||
|
|
||||||
|
if ($(this).data("search-type") === "int") {
|
||||||
|
value = parseInt(value);
|
||||||
|
if (isNaN(value)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fields[index] !== undefined) {
|
||||||
|
fields[index].push(value);
|
||||||
|
} else {
|
||||||
|
fields[index] = [value];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
searchData["fields"] = fields;
|
||||||
|
|
||||||
|
var json = JSON.stringify(searchData);
|
||||||
|
$("#search-json").val(json);
|
||||||
|
$("#search-page").val("1");
|
||||||
|
$("#search-form").trigger("submit");
|
||||||
|
}
|
||||||
11
frontend/src/tooltip.js
Normal file
11
frontend/src/tooltip.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import $ from "jquery";
|
||||||
|
import { Tooltip } from "bootstrap";
|
||||||
|
|
||||||
|
$(() => {
|
||||||
|
const tooltipTriggerList = document.querySelectorAll(
|
||||||
|
'[data-bs-toggle="tooltip"]',
|
||||||
|
);
|
||||||
|
const tooltipList = [...tooltipTriggerList].map(
|
||||||
|
(tooltipTriggerEl) => new Tooltip(tooltipTriggerEl),
|
||||||
|
);
|
||||||
|
});
|
||||||
15
helpers/version.go
Normal file
15
helpers/version.go
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import "runtime/debug"
|
||||||
|
|
||||||
|
func GetVCSRevision() string {
|
||||||
|
if info, ok := debug.ReadBuildInfo(); ok {
|
||||||
|
for _, setting := range info.Settings {
|
||||||
|
if setting.Key == "vcs.revision" {
|
||||||
|
return setting.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ type TemplatesGlobals struct {
|
||||||
ColorMode string
|
ColorMode string
|
||||||
AllowMembersPage bool
|
AllowMembersPage bool
|
||||||
AllowContactsPage bool
|
AllowContactsPage bool
|
||||||
|
CacheBusting string
|
||||||
}
|
}
|
||||||
|
|
||||||
func TemplatesMiddleware(c *fiber.Ctx) error {
|
func TemplatesMiddleware(c *fiber.Ctx) error {
|
||||||
|
|
@ -90,6 +91,19 @@ func TemplatesMiddleware(c *fiber.Ctx) error {
|
||||||
globals.TotpVerified = true
|
globals.TotpVerified = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config, err := helpers.GetConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the git commit tag for cache busting
|
||||||
|
// If we're in dev mode, just use the unix timestamp
|
||||||
|
if config.DevMode {
|
||||||
|
globals.CacheBusting = fmt.Sprintf("%d", time.Now().UnixMilli())
|
||||||
|
} else {
|
||||||
|
globals.CacheBusting = helpers.GetVCSRevision()
|
||||||
|
}
|
||||||
|
|
||||||
c.Bind(fiber.Map{
|
c.Bind(fiber.Map{
|
||||||
"Globals": globals,
|
"Globals": globals,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
16
package.json
16
package.json
|
|
@ -8,17 +8,31 @@
|
||||||
"url": "https://git.readonly.ch/bouzoure/pop-camarades"
|
"url": "https://git.readonly.ch/bouzoure/pop-camarades"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"parcel": "^2.15.0",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-jinja-template": "^2.1.0"
|
"prettier-plugin-jinja-template": "^2.1.0"
|
||||||
},
|
},
|
||||||
|
"source": "frontend/index.js",
|
||||||
|
"targets": {
|
||||||
|
"default": {
|
||||||
|
"distDir": "static/assets"
|
||||||
|
}
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"jswatch": "pnpx parcel watch",
|
||||||
|
"jsbuild": "pnpx parcel build",
|
||||||
"godeps": "go get -u && go mod tidy",
|
"godeps": "go get -u && go mod tidy",
|
||||||
"postgodeps": "pnpm run licenses",
|
"postgodeps": "pnpm run licenses",
|
||||||
"gotidy": "go mod tidy",
|
"gotidy": "go mod tidy",
|
||||||
"prelicenses": "./licenses/install_tool.sh",
|
"prelicenses": "./licenses/install_tool.sh",
|
||||||
"licenses": "./licenses/fetch_licenses.sh",
|
"licenses": "./licenses/fetch_licenses.sh",
|
||||||
"prettify": "pnpx prettier --plugin=prettier-plugin-jinja-template --parser=jinja-template --write ./views/*.html --write ./views/**/*.html",
|
"prettify": "pnpx prettier --plugin=prettier-plugin-jinja-template --parser=jinja-template --write ./views/*.html --write ./views/**/*.html",
|
||||||
"prebuild": "pnpm run gotidy && pnpm run licenses && pnpm run prettify",
|
"prebuild": "pnpm run gotidy && pnpm run licenses && pnpm run prettify && pnpm run jsbuild",
|
||||||
"build": "go build"
|
"build": "go build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bootstrap": "^5.3.6",
|
||||||
|
"bootstrap-icons": "^1.13.1",
|
||||||
|
"jquery": "^3.7.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1806
pnpm-lock.yaml
generated
1806
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
5
pnpm-workspace.yaml
Normal file
5
pnpm-workspace.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
onlyBuiltDependencies:
|
||||||
|
- '@parcel/watcher'
|
||||||
|
- '@swc/core'
|
||||||
|
- lmdb
|
||||||
|
- msgpackr-extract
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
6
static/bootstrap/css/bootstrap.min.css
vendored
6
static/bootstrap/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7
static/bootstrap/js/bootstrap.bundle.min.js
vendored
7
static/bootstrap/js/bootstrap.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,52 +0,0 @@
|
||||||
$(document).ready(function() {
|
|
||||||
$(".toggle-dark-mode").on("click", function() {
|
|
||||||
var currentState = $("html").attr("data-bs-theme");
|
|
||||||
|
|
||||||
var newState = "dark";
|
|
||||||
if (currentState === "dark") {
|
|
||||||
newState = "light";
|
|
||||||
}
|
|
||||||
|
|
||||||
setColorMode(newState);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
var currentState = $("html").attr("data-bs-theme");
|
|
||||||
var storedState = localStorage.getItem("color-mode");
|
|
||||||
|
|
||||||
if (storedState && storedState !== currentState) {
|
|
||||||
return setColorMode(storedState);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentState === "auto") {
|
|
||||||
var newState = "light";
|
|
||||||
if (
|
|
||||||
window.matchMedia &&
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
||||||
) {
|
|
||||||
newState = "dark";
|
|
||||||
}
|
|
||||||
|
|
||||||
return setColorMode(newState);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function setColorMode(color) {
|
|
||||||
if (color == "dark") {
|
|
||||||
$(".toggle-dark-mode").children("i").removeClass("bi-moon");
|
|
||||||
$(".toggle-dark-mode").children("i").addClass("bi-sun");
|
|
||||||
} else {
|
|
||||||
$(".toggle-dark-mode").children("i").removeClass("bi-sun");
|
|
||||||
$(".toggle-dark-mode").children("i").addClass("bi-moon");
|
|
||||||
}
|
|
||||||
|
|
||||||
$("html").attr("data-bs-theme", color);
|
|
||||||
localStorage.setItem("color-mode", color);
|
|
||||||
$.post("/set-color-mode", {color_mode: color});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
|
||||||
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
|
|
||||||
});
|
|
||||||
2
static/jquery/jquery.min.js
vendored
2
static/jquery/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,43 +0,0 @@
|
||||||
img#header-logo {
|
|
||||||
width: 89px;
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-card {
|
|
||||||
margin: auto;
|
|
||||||
max-width: 600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-tile {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-photo {
|
|
||||||
background-color: #555;
|
|
||||||
color: #fff;
|
|
||||||
display: inline-block;
|
|
||||||
width: 25px;
|
|
||||||
height: 25px;
|
|
||||||
border-radius: 50%;
|
|
||||||
font-weight: bold;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 12px;
|
|
||||||
padding-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-click {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-bold {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-responsive th,
|
|
||||||
.table-responsive td {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
199
static/search.js
199
static/search.js
|
|
@ -1,199 +0,0 @@
|
||||||
$(document).ready(function() {
|
|
||||||
$("#search").on("click", function() {
|
|
||||||
search();
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#advanced").on("click", function() {
|
|
||||||
if ($(this).data("state") === "true") {
|
|
||||||
$(this).find("i").removeClass("bi-arrow-up");
|
|
||||||
$(this).find("i").addClass("bi-arrow-down");
|
|
||||||
$("#advanced-section").addClass("d-none");
|
|
||||||
$(this).data("state", "false");
|
|
||||||
} else {
|
|
||||||
$(this).find("i").removeClass("bi-arrow-down");
|
|
||||||
$(this).find("i").addClass("bi-arrow-up");
|
|
||||||
$("#advanced-section").removeClass("d-none");
|
|
||||||
$(this).data("state", "true");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#reset-search").on("click", function() {
|
|
||||||
$("#search-json").val("");
|
|
||||||
$("#search-page").val("1");
|
|
||||||
$("#search-form").submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#search-container").on("change", ".field-select", function() {
|
|
||||||
var lastField = $(this).attr("data-last-field");
|
|
||||||
var isInitial = $(this).attr("data-is-initial");
|
|
||||||
var initialValue = $(this).attr("data-initial-value");
|
|
||||||
|
|
||||||
var field = $(this).val();
|
|
||||||
if (field == "") {
|
|
||||||
$(this).parents(".row").remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((lastField === undefined || lastField.length === 0) && (isInitial === undefined || isInitial !== "yes")) {
|
|
||||||
createFieldSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
$(this).attr("data-last-field", field);
|
|
||||||
|
|
||||||
$(this).parents(".row").find(".end-col").find("input").remove();
|
|
||||||
$(this).parents(".row").find(".end-col").find("select").remove();
|
|
||||||
|
|
||||||
$(elem).parents(".row").find(".end-col").append($("<input>", {
|
|
||||||
class: "form-control",
|
|
||||||
type: "text",
|
|
||||||
disabled: true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
var elem = $(this);
|
|
||||||
$.getJSON("/fields/" + field, function(data) {
|
|
||||||
$(this).parents(".row").find(".end-col").find("input").remove();
|
|
||||||
|
|
||||||
if (data.FieldType === "list") {
|
|
||||||
var select = $("<select>", {
|
|
||||||
class: "form-select",
|
|
||||||
"data-optional-field": field,
|
|
||||||
"data-search-type": "int"
|
|
||||||
});
|
|
||||||
$(elem).parents(".row").find(".end-col").append(select);
|
|
||||||
|
|
||||||
for (const [_, value] of Object.entries(data.List.ListItems)) {
|
|
||||||
$(select).append($("<option>", {
|
|
||||||
value: value.ID,
|
|
||||||
text: value.Value
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
} else if (data.FieldType === "number") {
|
|
||||||
$(elem).parents(".row").find(".end-col").append($("<input>", {
|
|
||||||
class: "form-control",
|
|
||||||
type: "number",
|
|
||||||
"data-optional-field": field,
|
|
||||||
"data-search-type": "int"
|
|
||||||
}));
|
|
||||||
} else if (data.FieldType === "date") {
|
|
||||||
$(elem).parents(".row").find(".end-col").append($("<input>", {
|
|
||||||
class: "form-control",
|
|
||||||
type: "date",
|
|
||||||
"data-optional-field": field
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
$(elem).parents(".row").find(".end-col").append($("<input>", {
|
|
||||||
class: "form-control",
|
|
||||||
type: "text",
|
|
||||||
"data-optional-field": field
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (initialValue !== undefined && initialValue.length > 0) {
|
|
||||||
$(elem).parents(".row").find("[data-optional-field]").val(initialValue);
|
|
||||||
$(elem).attr("data-initial-value", null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#search-container").on("keyup", "input[type=text]", function(e) {
|
|
||||||
if (e.keyCode === 13) {
|
|
||||||
search();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var json = $("#search-json").val();
|
|
||||||
if (json.length > 0) {
|
|
||||||
var searchData = JSON.parse(json);
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(searchData)) {
|
|
||||||
if (key === "advanced") {
|
|
||||||
if (value) $("#advanced").trigger("click");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key === "fields") {
|
|
||||||
for (const [field_id, field_values] of Object.entries(value)) {
|
|
||||||
for (const field_value of field_values) {
|
|
||||||
createFieldSelection(field_id, field_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === "boolean") {
|
|
||||||
$("[data-search-field=" + key + "]").prop("checked", value);
|
|
||||||
} else {
|
|
||||||
$("[data-search-field=" + key + "]").val(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createFieldSelection();
|
|
||||||
});
|
|
||||||
|
|
||||||
function createFieldSelection(field, value) {
|
|
||||||
var elem = $("#search-fields-model").clone().appendTo("#search-fields");
|
|
||||||
$(elem).attr("id", null);
|
|
||||||
$(elem).removeClass("d-none");
|
|
||||||
$(elem).addClass("search-fields-count");
|
|
||||||
|
|
||||||
if (field !== undefined && field.length > 0) {
|
|
||||||
$(elem).find(".field-select").attr("data-initial-value", value);
|
|
||||||
$(elem).find(".field-select").attr("data-is-initial", "yes");
|
|
||||||
$(elem).find(".field-select").val(field);
|
|
||||||
$(elem).find(".field-select").trigger("change");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function search() {
|
|
||||||
var advancedSearch = ($("#advanced").data("state") === "true");
|
|
||||||
var searchData = {
|
|
||||||
advanced: advancedSearch
|
|
||||||
};
|
|
||||||
|
|
||||||
$("[data-search-field]").each(function() {
|
|
||||||
var advancedField = $(this).data("search-advanced");
|
|
||||||
if (!advancedSearch && advancedField) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = $(this).data("search-field");
|
|
||||||
var value = $(this).val();
|
|
||||||
|
|
||||||
if ($(this).data("search-type") === "int") {
|
|
||||||
value = parseInt(value);
|
|
||||||
if (isNaN(value)) return;
|
|
||||||
} else if ($(this).attr("type") === "checkbox") {
|
|
||||||
value = $(this).prop("checked");
|
|
||||||
}
|
|
||||||
|
|
||||||
searchData[index] = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
fields = {}
|
|
||||||
$("[data-optional-field]:not(:disabled)").each(function() {
|
|
||||||
if (!advancedSearch) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = $(this).attr("data-optional-field");
|
|
||||||
var value = $(this).val();
|
|
||||||
|
|
||||||
if ($(this).data("search-type") === "int") {
|
|
||||||
value = parseInt(value);
|
|
||||||
if (isNaN(value)) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fields[index] !== undefined) {
|
|
||||||
fields[index].push(value);
|
|
||||||
} else {
|
|
||||||
fields[index] = [value];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
searchData["fields"] = fields;
|
|
||||||
|
|
||||||
var json = JSON.stringify(searchData);
|
|
||||||
$("#search-json").val(json);
|
|
||||||
$("#search-page").val("1");
|
|
||||||
$("#search-form").submit();
|
|
||||||
}
|
|
||||||
|
|
@ -92,21 +92,3 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script>
|
|
||||||
$(document).ready(function () {
|
|
||||||
$("#password").on("input", function () {
|
|
||||||
var enable = false;
|
|
||||||
if ($(this).val().length > 0) {
|
|
||||||
enable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#password-verify").prop("disabled", !enable);
|
|
||||||
$("#password-verify").prop("required", enable);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#password").trigger("input");
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
||||||
|
|
@ -118,25 +118,3 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script>
|
|
||||||
$(document).ready(function () {
|
|
||||||
$("#field_type").on("change", function () {
|
|
||||||
var enable = false;
|
|
||||||
if ($(this).val() == "list") {
|
|
||||||
enable = true;
|
|
||||||
|
|
||||||
$("#list-disabled").remove();
|
|
||||||
} else {
|
|
||||||
$("#list").append('<option id="list-disabled" selected></option>');
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#list").prop("disabled", !enable);
|
|
||||||
$("#list").prop("required", enable);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#field_type").trigger("change");
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,13 @@
|
||||||
|
|
||||||
<link
|
<link
|
||||||
rel="shortcut icon"
|
rel="shortcut icon"
|
||||||
href="/static/images/favicon.jpg"
|
href="/static/images/favicon.jpg?cb={{ Globals.CacheBusting }}"
|
||||||
type="image/jpg"
|
type="image/jpg"
|
||||||
/>
|
/>
|
||||||
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css" />
|
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="/static/bootstrap-icons/bootstrap-icons.min.css"
|
href="/static/assets/index.css?cb={{ Globals.CacheBusting }}"
|
||||||
/>
|
/>
|
||||||
<link rel="stylesheet" href="/static/main.css" />
|
|
||||||
{% block stylesheet %}{% endblock %}
|
|
||||||
</head>
|
</head>
|
||||||
<body class="d-flex flex-column h-100">
|
<body class="d-flex flex-column h-100">
|
||||||
<main class="flex-shrink-0">
|
<main class="flex-shrink-0">
|
||||||
|
|
@ -64,9 +61,9 @@
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script src="/static/jquery/jquery.min.js"></script>
|
<script
|
||||||
<script src="/static/bootstrap/js/bootstrap.bundle.min.js"></script>
|
src="/static/assets/index.js?cb={{ Globals.CacheBusting }}"
|
||||||
<script src="/static/functions.js"></script>
|
type="module"
|
||||||
{% block javascript %}{% endblock %}
|
></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,3 @@
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div id="licenses" class="container-fluid p-3">{{ Markdown|safe }}</div>
|
<div id="licenses" class="container-fluid p-3">{{ Markdown|safe }}</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block stylesheet %}
|
|
||||||
<style>
|
|
||||||
#licenses h2 {
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#licenses p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
||||||
|
|
@ -367,7 +367,3 @@
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block javascript %}
|
|
||||||
<script src="/static/search.js"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue