mirror of
https://github.com/ellite/Wallos.git
synced 2026-07-30 17:55:53 -04:00
feat: option for the week to start on sunday feat: redesign login / registration pages feat: more statistics feat: declarative oidc settings feat: grid view for subscriptions feat: subscription details popup feat: translate categories with ai feat: google image search with serpapi feat: selfh.st image search feat: dashboard icons image search fix: improve background removal feature for logos feat: v2.0 api - write endpoints fix: include todays subscriptions on amount due this month fix: calendar occurrences to respect subscription start date fix: honor configured outbound proxy for logo search without reopening httpoxy SSRF bypass fix: remove hardcode string from the admin page fix: ssrf via http proxy env var in payments logo search fix: require cron auth guard on storetotalyearlycost.php fix: validate per-user smtp host against ssrf fix: escape iCal property values to prevent crlf injection
137 lines
4.7 KiB
JavaScript
137 lines
4.7 KiB
JavaScript
let isDropdownOpen = false;
|
|
|
|
// Shared loading/empty/error states for the logo & payment icon search popups
|
|
function showSearchState(container, state) {
|
|
if (state === 'loading') {
|
|
container.innerHTML = '<div class="search-state"><i class="fa-solid fa-spinner fa-spin"></i></div>';
|
|
return;
|
|
}
|
|
|
|
const isError = state === 'error';
|
|
const icon = isError ? 'fa-circle-exclamation' : 'fa-magnifying-glass';
|
|
const message = isError
|
|
? translate('error_fetching_image_results').replace(/:\s*$/, '')
|
|
: translate('no_results_found');
|
|
|
|
const stateElement = document.createElement('div');
|
|
stateElement.className = 'search-state' + (isError ? ' error' : '');
|
|
stateElement.innerHTML = `<i class="fa-solid ${icon}"></i>`;
|
|
stateElement.appendChild(document.createTextNode(message));
|
|
|
|
container.innerHTML = '';
|
|
container.appendChild(stateElement);
|
|
}
|
|
|
|
function toggleDropdown() {
|
|
const dropdown = document.querySelector('.dropdown');
|
|
dropdown.classList.toggle('is-open');
|
|
isDropdownOpen = !isDropdownOpen;
|
|
}
|
|
|
|
function showErrorMessage(message) {
|
|
const toast = document.querySelector(".toast#errorToast");
|
|
const closeIcon = document.querySelector(".close-error");
|
|
const errorMessage = document.querySelector(".errorMessage");
|
|
const progress = document.querySelector(".progress.error");
|
|
let timer1, timer2;
|
|
errorMessage.textContent = message;
|
|
toast.classList.add("active");
|
|
progress.classList.add("active");
|
|
timer1 = setTimeout(() => {
|
|
toast.classList.remove("active");
|
|
closeIcon.removeEventListener("click", () => { });
|
|
}, 5000);
|
|
|
|
timer2 = setTimeout(() => {
|
|
progress.classList.remove("active");
|
|
}, 5300);
|
|
|
|
closeIcon.addEventListener("click", () => {
|
|
toast.classList.remove("active");
|
|
|
|
setTimeout(() => {
|
|
progress.classList.remove("active");
|
|
}, 300);
|
|
|
|
clearTimeout(timer1);
|
|
clearTimeout(timer2);
|
|
closeIcon.removeEventListener("click", () => { });
|
|
});
|
|
}
|
|
|
|
function showSuccessMessage(message) {
|
|
const toast = document.querySelector(".toast#successToast");
|
|
const closeIcon = document.querySelector(".close-success");
|
|
const successMessage = document.querySelector(".successMessage");
|
|
const progress = document.querySelector(".progress.success");
|
|
let timer1, timer2;
|
|
successMessage.textContent = message;
|
|
toast.classList.add("active");
|
|
progress.classList.add("active");
|
|
timer1 = setTimeout(() => {
|
|
toast.classList.remove("active");
|
|
closeIcon.removeEventListener("click", () => { });
|
|
}, 5000);
|
|
|
|
timer2 = setTimeout(() => {
|
|
progress.classList.remove("active");
|
|
}, 5300);
|
|
|
|
closeIcon.addEventListener("click", () => {
|
|
toast.classList.remove("active");
|
|
|
|
setTimeout(() => {
|
|
progress.classList.remove("active");
|
|
}, 300);
|
|
|
|
clearTimeout(timer1);
|
|
clearTimeout(timer2);
|
|
closeIcon.removeEventListener("click", () => { });
|
|
});
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
const userLocale = navigator.language || navigator.languages[0];
|
|
document.cookie = `user_locale=${userLocale}; expires=Fri, 31 Dec 9999 23:59:59 GMT; SameSite=Lax`;
|
|
|
|
if (window.update_theme_settings) {
|
|
const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
const themePreference = prefersDarkMode ? 'dark' : 'light';
|
|
const darkThemeCss = document.querySelector("#dark-theme");
|
|
darkThemeCss.disabled = themePreference === 'light';
|
|
|
|
// Preserve existing classes on the body tag
|
|
const existingClasses = document.body.className.split(' ').filter(cls => cls !== 'dark' && cls !== 'light');
|
|
document.body.className = [...existingClasses, themePreference].join(' ');
|
|
|
|
document.cookie = `inUseTheme=${themePreference}; expires=Fri, 31 Dec 9999 23:59:59 GMT; SameSite=Lax`;
|
|
const themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
|
|
themeColorMetaTag.setAttribute('content', themePreference === 'dark' ? '#12151C' : '#FFFFFF');
|
|
}
|
|
|
|
document.addEventListener('mousedown', function (event) {
|
|
var dropdown = document.querySelector('.dropdown');
|
|
var dropdownContent = document.querySelector('.dropdown-content');
|
|
|
|
if (!dropdown.contains(event.target) && isDropdownOpen) {
|
|
dropdown.classList.remove('is-open');
|
|
isDropdownOpen = false;
|
|
}
|
|
});
|
|
|
|
document.querySelector('.dropdown-content').addEventListener('focus', function () {
|
|
isDropdownOpen = true;
|
|
});
|
|
});
|
|
|
|
function getCookie(name) {
|
|
const cookies = document.cookie.split(';');
|
|
for (let cookie of cookies) {
|
|
cookie = cookie.trim();
|
|
if (cookie.startsWith(`${name}=`)) {
|
|
return cookie.substring(name.length + 1);
|
|
}
|
|
}
|
|
return null;
|
|
} |