mirror of
https://github.com/eduardolat/pgbackweb.git
synced 2026-04-26 06:08:12 -05:00
Run deno fmt
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
|
||||
This package includes a custom logger for the project.
|
||||
|
||||
Functions are exposed for logging messages easily; it is imperative that
|
||||
these functions are used throughout the project to maintain a standard in
|
||||
log messages.
|
||||
Functions are exposed for logging messages easily; it is imperative that these
|
||||
functions are used throughout the project to maintain a standard in log
|
||||
messages.
|
||||
|
||||
If necessary, the `logWriter` can be edited so that in addition to writing to
|
||||
stdout, it can also write to a file or send the logs to a server like
|
||||
Better Stack or New Relic.
|
||||
stdout, it can also write to a file or send the logs to a server like Better
|
||||
Stack or New Relic.
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
# paginateutil
|
||||
|
||||
This package provides a utility for paginating data. It should be used in conjunction with database queries to paginate results.
|
||||
This package provides a utility for paginating data. It should be used in
|
||||
conjunction with database queries to paginate results.
|
||||
|
||||
Use these utilities to paginate results and, above all, to return a common structure in the different places where pagination is performed, maintaining consistency throughout the project.
|
||||
Use these utilities to paginate results and, above all, to return a common
|
||||
structure in the different places where pagination is performed, maintaining
|
||||
consistency throughout the project.
|
||||
|
||||
## Usage
|
||||
|
||||
### 1. Define your queries:
|
||||
|
||||
- **PaginateCount:** This function should return the total number of records that match the query.
|
||||
- **PaginateCount:** This function should return the total number of records
|
||||
that match the query.
|
||||
- **Paginate:** This function should return the paginated records.
|
||||
|
||||
### 2. Create the offset from the request parameters:
|
||||
|
||||
Use the `CreateOffsetFromParams` function to create the offset from the request parameters needed to paginate the results.
|
||||
Use the `CreateOffsetFromParams` function to create the offset from the request
|
||||
parameters needed to paginate the results.
|
||||
|
||||
### 3. Define the signature of your pagination function:
|
||||
|
||||
@@ -38,9 +43,13 @@ func PaginateXYZ(
|
||||
|
||||
### Example
|
||||
|
||||
Refer to `internal/service/backups/paginate_backups.go` for an example of how to use the `paginateutil` package.
|
||||
Refer to `internal/service/backups/paginate_backups.go` for an example of how to
|
||||
use the `paginateutil` package.
|
||||
|
||||
## Notes
|
||||
|
||||
- **Default Values:** Ensure to set reasonable default values for pagination parameters (`page` and `limit`) if they are not provided.
|
||||
- **Common Response Structure:** Use `CreatePaginateResponse` to generate a common response structure that includes information about pagination, such as the total number of items, the current page, and the number of items per page.
|
||||
- **Default Values:** Ensure to set reasonable default values for pagination
|
||||
parameters (`page` and `limit`) if they are not provided.
|
||||
- **Common Response Structure:** Use `CreatePaginateResponse` to generate a
|
||||
common response structure that includes information about pagination, such as
|
||||
the total number of items, the current page, and the number of items per page.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Validate functions
|
||||
|
||||
All the functions in this directory are used to validate data, all of them _MUST BE PURE FUNCTIONS_.
|
||||
All the functions in this directory are used to validate data, all of them _MUST
|
||||
BE PURE FUNCTIONS_.
|
||||
|
||||
https://en.wikipedia.org/wiki/Pure_function
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
# reqctx
|
||||
|
||||
`reqctx` is a utility package designed to manage request-specific context values in this project. It helps to encapsulate authentication status and user information within the Echo request context in a type-safe manner.
|
||||
`reqctx` is a utility package designed to manage request-specific context values
|
||||
in this project. It helps to encapsulate authentication status and user
|
||||
information within the Echo request context in a type-safe manner.
|
||||
|
||||
## Purpose
|
||||
|
||||
When developing web applications, it is common to pass values such as authentication status and user information through the request lifecycle. Using Echo's built-in context (`echo.Context`) can lead to potential issues such as typographical errors, lack of type safety, and reduced code readability.
|
||||
When developing web applications, it is common to pass values such as
|
||||
authentication status and user information through the request lifecycle. Using
|
||||
Echo's built-in context (`echo.Context`) can lead to potential issues such as
|
||||
typographical errors, lack of type safety, and reduced code readability.
|
||||
|
||||
`reqctx` addresses these issues by providing a structured way to manage these context values.
|
||||
`reqctx` addresses these issues by providing a structured way to manage these
|
||||
context values.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[x-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ html {
|
||||
|
||||
.table tbody tr {
|
||||
@apply hover:bg-base-200;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
|
||||
.htmx-request.htmx-indicator {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
svg[data-nodxgo="lucide"]:not([class*="size-"]) {
|
||||
@apply size-4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
.notyf__toast {
|
||||
@apply rounded-btn break-all !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,4 +20,4 @@
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,4 +58,4 @@
|
||||
|
||||
.ss-value-delete svg path {
|
||||
@apply stroke-primary-content !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,4 @@ body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown),
|
||||
html.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) {
|
||||
height: 100% !important;
|
||||
overflow-y: visible !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@tailwind variants;
|
||||
@tailwind variants;
|
||||
|
||||
@@ -6,4 +6,4 @@
|
||||
@import "./partials/slim-select.css";
|
||||
@import "./partials/notyf.css";
|
||||
@import "./partials/scrollbar.css";
|
||||
@import "./partials/sweetalert2.css";
|
||||
@import "./partials/sweetalert2.css";
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { initThemeHelper } from './init-theme-helper.js'
|
||||
import { initSweetAlert2 } from './init-sweetalert2.js'
|
||||
import { initNotyf } from './init-notyf.js'
|
||||
import { initHTMX } from './init-htmx.js'
|
||||
import { initHelpers } from './init-helpers.js'
|
||||
import { initThemeHelper } from "./init-theme-helper.js";
|
||||
import { initSweetAlert2 } from "./init-sweetalert2.js";
|
||||
import { initNotyf } from "./init-notyf.js";
|
||||
import { initHTMX } from "./init-htmx.js";
|
||||
import { initHelpers } from "./init-helpers.js";
|
||||
|
||||
initThemeHelper()
|
||||
initSweetAlert2()
|
||||
initNotyf()
|
||||
initHTMX()
|
||||
initHelpers()
|
||||
initThemeHelper();
|
||||
initSweetAlert2();
|
||||
initNotyf();
|
||||
initHTMX();
|
||||
initHelpers();
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
export function initHelpers () {
|
||||
function debounce (fn, delayMilliseconds) {
|
||||
let timeoutInstance
|
||||
export function initHelpers() {
|
||||
function debounce(fn, delayMilliseconds) {
|
||||
let timeoutInstance;
|
||||
return function (...args) {
|
||||
clearTimeout(timeoutInstance)
|
||||
clearTimeout(timeoutInstance);
|
||||
timeoutInstance = setTimeout(() => {
|
||||
fn.apply(this, args)
|
||||
}, delayMilliseconds)
|
||||
}
|
||||
fn.apply(this, args);
|
||||
}, delayMilliseconds);
|
||||
};
|
||||
}
|
||||
|
||||
function copyToClipboard (textToCopy) {
|
||||
const successMessage = 'Text copied'
|
||||
const errorMessage = 'Error copying text'
|
||||
function copyToClipboard(textToCopy) {
|
||||
const successMessage = "Text copied";
|
||||
const errorMessage = "Error copying text";
|
||||
|
||||
/* First, try the modern approach */
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
return navigator.clipboard
|
||||
.writeText(textToCopy)
|
||||
.then(() => {
|
||||
toaster.success(successMessage)
|
||||
toaster.success(successMessage);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(errorMessage, err)
|
||||
toaster.error(errorMessage)
|
||||
})
|
||||
console.error(errorMessage, err);
|
||||
toaster.error(errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
/* Fallback: use execCommand("copy") method */
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.value = textToCopy
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = textToCopy;
|
||||
|
||||
textArea.style.position = 'fixed'
|
||||
textArea.style.left = '-9999px'
|
||||
textArea.style.top = '-9999px'
|
||||
document.body.appendChild(textArea)
|
||||
textArea.focus()
|
||||
textArea.select()
|
||||
textArea.style.position = "fixed";
|
||||
textArea.style.left = "-9999px";
|
||||
textArea.style.top = "-9999px";
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
const successful = document.execCommand('copy')
|
||||
const successful = document.execCommand("copy");
|
||||
if (successful) {
|
||||
toaster.success(successMessage)
|
||||
toaster.success(successMessage);
|
||||
} else {
|
||||
console.error('Fallback', errorMessage)
|
||||
toaster.error(errorMessage)
|
||||
console.error("Fallback", errorMessage);
|
||||
toaster.error(errorMessage);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Fallback', errorMessage, err)
|
||||
toaster.error(errorMessage)
|
||||
console.error("Fallback", errorMessage, err);
|
||||
toaster.error(errorMessage);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea)
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
function textareaAutoGrow (element) {
|
||||
if (!element?.style) return
|
||||
element.style.height = '1px'
|
||||
element.style.height = (element.scrollHeight + 2) + 'px'
|
||||
function textareaAutoGrow(element) {
|
||||
if (!element?.style) return;
|
||||
element.style.height = "1px";
|
||||
element.style.height = (element.scrollHeight + 2) + "px";
|
||||
}
|
||||
|
||||
function formatJson (inJSON) {
|
||||
if (typeof inJSON !== 'string' || inJSON === '') return inJSON
|
||||
function formatJson(inJSON) {
|
||||
if (typeof inJSON !== "string" || inJSON === "") return inJSON;
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(inJSON), null, 2)
|
||||
} catch (e) {
|
||||
return inJSON
|
||||
return JSON.stringify(JSON.parse(inJSON), null, 2);
|
||||
} catch {
|
||||
return inJSON;
|
||||
}
|
||||
}
|
||||
|
||||
window.debounce = debounce
|
||||
window.copyToClipboard = copyToClipboard
|
||||
window.textareaAutoGrow = textareaAutoGrow
|
||||
window.formatJson = formatJson
|
||||
window.debounce = debounce;
|
||||
window.copyToClipboard = copyToClipboard;
|
||||
window.textareaAutoGrow = textareaAutoGrow;
|
||||
window.formatJson = formatJson;
|
||||
}
|
||||
|
||||
@@ -1,72 +1,72 @@
|
||||
export function initHTMX () {
|
||||
export function initHTMX() {
|
||||
const triggers = {
|
||||
ctm_alert: function (evt) {
|
||||
const message = decodeURIComponent(evt.detail.value)
|
||||
window.swalAlert(message)
|
||||
const message = decodeURIComponent(evt.detail.value);
|
||||
window.swalAlert(message);
|
||||
},
|
||||
ctm_alert_with_refresh: function (evt) {
|
||||
const message = decodeURIComponent(evt.detail.value)
|
||||
const message = decodeURIComponent(evt.detail.value);
|
||||
window.swalAlert(message).then(() => {
|
||||
location.reload()
|
||||
})
|
||||
location.reload();
|
||||
});
|
||||
},
|
||||
ctm_alert_with_redirect: function (evt) {
|
||||
const payload = decodeURIComponent(evt.detail.value)
|
||||
const parts = payload.split('-::-::-')
|
||||
const payload = decodeURIComponent(evt.detail.value);
|
||||
const parts = payload.split("-::-::-");
|
||||
if (parts.length !== 2) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
const message = parts[0]
|
||||
const url = parts[1]
|
||||
const message = parts[0];
|
||||
const url = parts[1];
|
||||
|
||||
window.swalAlert(message).then(() => {
|
||||
location.href = url
|
||||
})
|
||||
location.href = url;
|
||||
});
|
||||
},
|
||||
ctm_toast_success: function (evt) {
|
||||
const message = decodeURIComponent(evt.detail.value)
|
||||
toaster.success(message)
|
||||
const message = decodeURIComponent(evt.detail.value);
|
||||
toaster.success(message);
|
||||
},
|
||||
ctm_toast_error: function (evt) {
|
||||
const message = decodeURIComponent(evt.detail.value)
|
||||
toaster.error(message)
|
||||
const message = decodeURIComponent(evt.detail.value);
|
||||
toaster.error(message);
|
||||
},
|
||||
ctm_toast_success_infinite: function (evt) {
|
||||
const message = decodeURIComponent(evt.detail.value)
|
||||
toaster.successInfinite(message)
|
||||
const message = decodeURIComponent(evt.detail.value);
|
||||
toaster.successInfinite(message);
|
||||
},
|
||||
ctm_toast_error_infinite: function (evt) {
|
||||
const message = decodeURIComponent(evt.detail.value)
|
||||
toaster.errorInfinite(message)
|
||||
}
|
||||
}
|
||||
const message = decodeURIComponent(evt.detail.value);
|
||||
toaster.errorInfinite(message);
|
||||
},
|
||||
};
|
||||
|
||||
for (const key in triggers) {
|
||||
document.addEventListener(key, triggers[key])
|
||||
document.addEventListener(key, triggers[key]);
|
||||
}
|
||||
|
||||
// Add trigger to use sweetalert2 for confirms
|
||||
document.addEventListener('htmx:confirm', function (e) {
|
||||
if (!e.detail.target.hasAttribute('hx-confirm')) return
|
||||
document.addEventListener("htmx:confirm", function (e) {
|
||||
if (!e.detail.target.hasAttribute("hx-confirm")) return;
|
||||
|
||||
e.preventDefault()
|
||||
e.preventDefault();
|
||||
window.swalConfirm(e.detail.question).then(function (result) {
|
||||
if (result.isConfirmed) e.detail.issueRequest(true)
|
||||
})
|
||||
})
|
||||
if (result.isConfirmed) e.detail.issueRequest(true);
|
||||
});
|
||||
});
|
||||
|
||||
// This fixes this issue:
|
||||
// https://stackoverflow.com/questions/73658449/htmx-request-not-firing-when-hx-attributes-are-added-dynamically-from-javascrip
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType === 1 && !node['htmx-internal-data']) {
|
||||
htmx.process(node)
|
||||
if (node.nodeType === 1 && !node["htmx-internal-data"]) {
|
||||
htmx.process(node);
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
observer.observe(document, { childList: true, subtree: true })
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
observer.observe(document, { childList: true, subtree: true });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
export function initNotyf () {
|
||||
let toastQueue = []
|
||||
export function initNotyf() {
|
||||
let toastQueue = [];
|
||||
|
||||
const toastCfg = {
|
||||
duration: 5000,
|
||||
ripple: false,
|
||||
position: { x: 'right', y: 'bottom' },
|
||||
dismissible: true
|
||||
}
|
||||
position: { x: "right", y: "bottom" },
|
||||
dismissible: true,
|
||||
};
|
||||
|
||||
const infiniteToastCfg = {
|
||||
...toastCfg,
|
||||
duration: 0
|
||||
}
|
||||
duration: 0,
|
||||
};
|
||||
|
||||
window.toaster = {
|
||||
success: (message) => {
|
||||
toastQueue.push({ type: 'success', message, config: toastCfg })
|
||||
toastQueue.push({ type: "success", message, config: toastCfg });
|
||||
},
|
||||
error: (message) => {
|
||||
toastQueue.push({ type: 'error', message, config: toastCfg })
|
||||
toastQueue.push({ type: "error", message, config: toastCfg });
|
||||
},
|
||||
successInfinite: (message) => {
|
||||
toastQueue.push({ type: 'success', message, config: infiniteToastCfg })
|
||||
toastQueue.push({ type: "success", message, config: infiniteToastCfg });
|
||||
},
|
||||
errorInfinite: (message) => {
|
||||
toastQueue.push({ type: 'error', message, config: infiniteToastCfg })
|
||||
}
|
||||
}
|
||||
toastQueue.push({ type: "error", message, config: infiniteToastCfg });
|
||||
},
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const notyf = new Notyf()
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const notyf = new Notyf();
|
||||
|
||||
toastQueue.forEach(item => {
|
||||
notyf.open({ type: item.type, message: item.message, ...item.config })
|
||||
})
|
||||
toastQueue.forEach((item) => {
|
||||
notyf.open({ type: item.type, message: item.message, ...item.config });
|
||||
});
|
||||
|
||||
toastQueue = []
|
||||
toastQueue = [];
|
||||
|
||||
window.toaster.success = (message) => {
|
||||
notyf.open({ type: 'success', message, ...toastCfg })
|
||||
}
|
||||
notyf.open({ type: "success", message, ...toastCfg });
|
||||
};
|
||||
window.toaster.error = (message) => {
|
||||
notyf.open({ type: 'error', message, ...toastCfg })
|
||||
}
|
||||
notyf.open({ type: "error", message, ...toastCfg });
|
||||
};
|
||||
window.toaster.successInfinite = (message) => {
|
||||
notyf.open({ type: 'success', message, ...infiniteToastCfg })
|
||||
}
|
||||
notyf.open({ type: "success", message, ...infiniteToastCfg });
|
||||
};
|
||||
window.toaster.errorInfinite = (message) => {
|
||||
notyf.open({ type: 'error', message, ...infiniteToastCfg })
|
||||
}
|
||||
})
|
||||
notyf.open({ type: "error", message, ...infiniteToastCfg });
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
export function initSweetAlert2 () {
|
||||
export function initSweetAlert2() {
|
||||
// Docs at https://sweetalert2.github.io/#configuration
|
||||
const defaultConfig = {
|
||||
icon: 'info',
|
||||
confirmButtonText: 'Okay',
|
||||
cancelButtonText: 'Cancel',
|
||||
icon: "info",
|
||||
confirmButtonText: "Okay",
|
||||
cancelButtonText: "Cancel",
|
||||
customClass: {
|
||||
popup: 'rounded-box bg-base-100 text-base-content',
|
||||
confirmButton: 'btn btn-primary',
|
||||
denyButton: 'btn btn-warning',
|
||||
cancelButton: 'btn btn-error'
|
||||
}
|
||||
}
|
||||
popup: "rounded-box bg-base-100 text-base-content",
|
||||
confirmButton: "btn btn-primary",
|
||||
denyButton: "btn btn-warning",
|
||||
cancelButton: "btn btn-error",
|
||||
},
|
||||
};
|
||||
|
||||
async function swalAlert (text) {
|
||||
return Swal.fire({
|
||||
async function swalAlert(text) {
|
||||
return await Swal.fire({
|
||||
...defaultConfig,
|
||||
title: text
|
||||
})
|
||||
}
|
||||
|
||||
async function swalConfirm (text) {
|
||||
return Swal.fire({
|
||||
...defaultConfig,
|
||||
icon: 'question',
|
||||
title: text,
|
||||
confirmButtonText: 'Confirm',
|
||||
showCancelButton: true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
window.swalAlert = swalAlert
|
||||
window.swalConfirm = swalConfirm
|
||||
async function swalConfirm(text) {
|
||||
return await Swal.fire({
|
||||
...defaultConfig,
|
||||
icon: "question",
|
||||
title: text,
|
||||
confirmButtonText: "Confirm",
|
||||
showCancelButton: true,
|
||||
});
|
||||
}
|
||||
|
||||
window.swalAlert = swalAlert;
|
||||
window.swalConfirm = swalConfirm;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
export function initThemeHelper () {
|
||||
function getTheme () {
|
||||
const theme = localStorage.getItem('theme')
|
||||
return theme || ''
|
||||
export function initThemeHelper() {
|
||||
function getTheme() {
|
||||
const theme = localStorage.getItem("theme");
|
||||
return theme || "";
|
||||
}
|
||||
|
||||
function setTheme (theme) {
|
||||
localStorage.setItem('theme', theme)
|
||||
document.documentElement.setAttribute('data-theme', theme)
|
||||
function setTheme(theme) {
|
||||
localStorage.setItem("theme", theme);
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
}
|
||||
|
||||
window.getTheme = getTheme
|
||||
window.setTheme = setTheme
|
||||
window.getTheme = getTheme;
|
||||
window.setTheme = setTheme;
|
||||
|
||||
const theme = getTheme()
|
||||
setTheme(theme)
|
||||
const theme = getTheme();
|
||||
setTheme(theme);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
window.alpineChangeThemeButton = function () {
|
||||
return {
|
||||
theme: '',
|
||||
theme: "",
|
||||
|
||||
loadTheme () {
|
||||
const theme = window.getTheme()
|
||||
this.theme = theme || 'system'
|
||||
loadTheme() {
|
||||
const theme = window.getTheme();
|
||||
this.theme = theme || "system";
|
||||
},
|
||||
|
||||
setTheme (theme) {
|
||||
window.setTheme(theme)
|
||||
this.theme = theme || 'system'
|
||||
setTheme(theme) {
|
||||
window.setTheme(theme);
|
||||
this.theme = theme || "system";
|
||||
},
|
||||
|
||||
init () {
|
||||
this.loadTheme()
|
||||
}
|
||||
}
|
||||
}
|
||||
init() {
|
||||
this.loadTheme();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -5,42 +5,43 @@ window.alpineOptionsDropdown = function () {
|
||||
contentEl: null,
|
||||
closeTimeout: null,
|
||||
|
||||
init () {
|
||||
this.buttonEl = this.$refs.button
|
||||
this.contentEl = this.$refs.content
|
||||
init() {
|
||||
this.buttonEl = this.$refs.button;
|
||||
this.contentEl = this.$refs.content;
|
||||
},
|
||||
|
||||
open () {
|
||||
this.isOpen = true
|
||||
this.contentEl.classList.remove('hidden')
|
||||
this.positionContent()
|
||||
open() {
|
||||
this.isOpen = true;
|
||||
this.contentEl.classList.remove("hidden");
|
||||
this.positionContent();
|
||||
|
||||
if (this.closeTimeout) {
|
||||
clearTimeout(this.closeTimeout)
|
||||
this.closeTimeout = null
|
||||
clearTimeout(this.closeTimeout);
|
||||
this.closeTimeout = null;
|
||||
}
|
||||
},
|
||||
|
||||
close () {
|
||||
close() {
|
||||
this.closeTimeout = setTimeout(() => {
|
||||
this.isOpen = false
|
||||
this.contentEl.classList.add('hidden')
|
||||
}, 200)
|
||||
this.isOpen = false;
|
||||
this.contentEl.classList.add("hidden");
|
||||
}, 200);
|
||||
},
|
||||
|
||||
positionContent () {
|
||||
const buttonRect = this.buttonEl.getBoundingClientRect()
|
||||
const contentHeight = this.contentEl.offsetHeight
|
||||
const windowHeight = window.innerHeight
|
||||
const moreSpaceBelow = (windowHeight - buttonRect.bottom) > buttonRect.top
|
||||
positionContent() {
|
||||
const buttonRect = this.buttonEl.getBoundingClientRect();
|
||||
const contentHeight = this.contentEl.offsetHeight;
|
||||
const windowHeight = window.innerHeight;
|
||||
const moreSpaceBelow =
|
||||
(windowHeight - buttonRect.bottom) > buttonRect.top;
|
||||
|
||||
this.contentEl.style.left = `${buttonRect.left}px`
|
||||
this.contentEl.style.left = `${buttonRect.left}px`;
|
||||
|
||||
if (moreSpaceBelow) {
|
||||
this.contentEl.style.top = `${buttonRect.bottom}px`
|
||||
this.contentEl.style.top = `${buttonRect.bottom}px`;
|
||||
} else {
|
||||
this.contentEl.style.top = `${buttonRect.top - contentHeight}px`
|
||||
this.contentEl.style.top = `${buttonRect.top - contentHeight}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,41 +2,41 @@ window.alpineStarOnGithub = function () {
|
||||
return {
|
||||
stars: null,
|
||||
|
||||
async init () {
|
||||
const stars = await this.getStars()
|
||||
async init() {
|
||||
const stars = await this.getStars();
|
||||
if (stars !== null) {
|
||||
this.stars = stars
|
||||
this.stars = stars;
|
||||
}
|
||||
},
|
||||
|
||||
async getStars () {
|
||||
const cacheKey = 'pbw-gh-stars'
|
||||
async getStars() {
|
||||
const cacheKey = "pbw-gh-stars";
|
||||
|
||||
const cachedJSON = localStorage.getItem(cacheKey)
|
||||
const cachedJSON = localStorage.getItem(cacheKey);
|
||||
if (cachedJSON) {
|
||||
const cached = JSON.parse(cachedJSON)
|
||||
const cached = JSON.parse(cachedJSON);
|
||||
if (Date.now() - cached.timestamp < 2 * 60 * 1000) {
|
||||
return cached.value
|
||||
return cached.value;
|
||||
}
|
||||
}
|
||||
|
||||
const url = 'https://api.github.com/repos/eduardolat/pgbackweb'
|
||||
const url = "https://api.github.com/repos/eduardolat/pgbackweb";
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
const data = await response.json()
|
||||
const value = data.stargazers_count
|
||||
const data = await response.json();
|
||||
const value = data.stargazers_count;
|
||||
const dataToCache = JSON.stringify({
|
||||
value,
|
||||
timestamp: Date.now()
|
||||
})
|
||||
localStorage.setItem(cacheKey, dataToCache)
|
||||
return value
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
localStorage.setItem(cacheKey, dataToCache);
|
||||
return value;
|
||||
} catch {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,20 +3,20 @@ window.alpineSummaryHowToSlider = function () {
|
||||
slidesQty: 4,
|
||||
currentSlide: 1,
|
||||
|
||||
get hasNextSlide () {
|
||||
return this.currentSlide < this.slidesQty
|
||||
get hasNextSlide() {
|
||||
return this.currentSlide < this.slidesQty;
|
||||
},
|
||||
|
||||
get hasPrevSlide () {
|
||||
return this.currentSlide > 1
|
||||
get hasPrevSlide() {
|
||||
return this.currentSlide > 1;
|
||||
},
|
||||
|
||||
nextSlide () {
|
||||
if (this.hasNextSlide) this.currentSlide++
|
||||
nextSlide() {
|
||||
if (this.hasNextSlide) this.currentSlide++;
|
||||
},
|
||||
|
||||
prevSlide () {
|
||||
if (this.hasPrevSlide) this.currentSlide--
|
||||
}
|
||||
}
|
||||
}
|
||||
prevSlide() {
|
||||
if (this.hasPrevSlide) this.currentSlide--;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const el = document.getElementById('dashboard-aside')
|
||||
const key = 'dashboard-aside-scroll-position'
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const el = document.getElementById("dashboard-aside");
|
||||
const key = "dashboard-aside-scroll-position";
|
||||
|
||||
if (!el) return
|
||||
if (!el) return;
|
||||
|
||||
const saveScrollPosition = window.debounce(
|
||||
() => {
|
||||
const scrollPosition = el.scrollTop
|
||||
localStorage.setItem(key, scrollPosition)
|
||||
const scrollPosition = el.scrollTop;
|
||||
localStorage.setItem(key, scrollPosition);
|
||||
},
|
||||
200
|
||||
)
|
||||
el.addEventListener('scroll', saveScrollPosition)
|
||||
200,
|
||||
);
|
||||
el.addEventListener("scroll", saveScrollPosition);
|
||||
|
||||
const scrollPosition = localStorage.getItem(key)
|
||||
const scrollPosition = localStorage.getItem(key);
|
||||
if (scrollPosition) {
|
||||
el.scrollTop = parseInt(scrollPosition, 10)
|
||||
el.scrollTop = parseInt(scrollPosition, 10);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
window.alpineDashboardAsideItem = function (link = '', strict = false) {
|
||||
window.alpineDashboardAsideItem = function (link = "", strict = false) {
|
||||
return {
|
||||
link,
|
||||
strict,
|
||||
is_active: false,
|
||||
|
||||
checkActive () {
|
||||
checkActive() {
|
||||
if (this.strict) {
|
||||
this.is_active = window.location.pathname === this.link
|
||||
return
|
||||
this.is_active = window.location.pathname === this.link;
|
||||
return;
|
||||
}
|
||||
|
||||
this.is_active = window.location.pathname.startsWith(this.link)
|
||||
this.is_active = window.location.pathname.startsWith(this.link);
|
||||
},
|
||||
|
||||
init () {
|
||||
this.checkActive()
|
||||
init() {
|
||||
this.checkActive();
|
||||
|
||||
const originalPushState = window.history.pushState
|
||||
const originalPushState = window.history.pushState;
|
||||
window.history.pushState = (...args) => {
|
||||
originalPushState.apply(window.history, args)
|
||||
this.checkActive()
|
||||
}
|
||||
originalPushState.apply(window.history, args);
|
||||
this.checkActive();
|
||||
};
|
||||
|
||||
const originalReplaceState = window.history.replaceState
|
||||
const originalReplaceState = window.history.replaceState;
|
||||
window.history.replaceState = (...args) => {
|
||||
originalReplaceState.apply(window.history, args)
|
||||
this.checkActive()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
originalReplaceState.apply(window.history, args);
|
||||
this.checkActive();
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,41 +2,42 @@ window.alpineDashboardHeaderUpdates = function () {
|
||||
return {
|
||||
latestRelease: null,
|
||||
|
||||
async init () {
|
||||
const latestRelease = await this.getLatestRelease()
|
||||
async init() {
|
||||
const latestRelease = await this.getLatestRelease();
|
||||
if (latestRelease !== null) {
|
||||
this.latestRelease = latestRelease
|
||||
this.latestRelease = latestRelease;
|
||||
}
|
||||
},
|
||||
|
||||
async getLatestRelease () {
|
||||
const cacheKey = 'pbw-gh-last-release'
|
||||
async getLatestRelease() {
|
||||
const cacheKey = "pbw-gh-last-release";
|
||||
|
||||
const cachedJSON = localStorage.getItem(cacheKey)
|
||||
const cachedJSON = localStorage.getItem(cacheKey);
|
||||
if (cachedJSON) {
|
||||
const cached = JSON.parse(cachedJSON)
|
||||
const cached = JSON.parse(cachedJSON);
|
||||
if (Date.now() - cached.timestamp < 2 * 60 * 1000) {
|
||||
return cached.value
|
||||
return cached.value;
|
||||
}
|
||||
}
|
||||
|
||||
const url = 'https://api.github.com/repos/eduardolat/pgbackweb/releases/latest'
|
||||
const url =
|
||||
"https://api.github.com/repos/eduardolat/pgbackweb/releases/latest";
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
const data = await response.json()
|
||||
const value = data.name
|
||||
const data = await response.json();
|
||||
const value = data.name;
|
||||
const dataToCache = JSON.stringify({
|
||||
value,
|
||||
timestamp: Date.now()
|
||||
})
|
||||
localStorage.setItem(cacheKey, dataToCache)
|
||||
return value
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
localStorage.setItem(cacheKey, dataToCache);
|
||||
return value;
|
||||
} catch {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user