Port modular price, product, and PWA fixes into boursecu

Sync live quote handling, SSE product images, store hours, file URL normalization, PWA icon cache, TV monitor, about software, SuperAdmin menu gates, price alerts, and related UX from modular while preserving boursecu branding and config constraints.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-02 08:26:52 -04:00
co-authored by Cursor
parent e9012e26fb
commit 9356a2ab5e
112 changed files with 6044 additions and 583 deletions
+103 -20
View File
@@ -2,6 +2,65 @@
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8" />
<script>
(function () {
try {
var icon = ''
try {
icon = localStorage.getItem('PWA_INSTALL_ICON_URL') || ''
} catch (e) {}
var title = ''
try {
var raw = localStorage.getItem('PWA_MANIFEST_CACHE_V5')
if (raw) {
var payload = JSON.parse(raw)
var manifest = payload && payload.manifest
if (manifest) {
title = manifest.short_name || manifest.name || ''
if (!icon && manifest.icons && manifest.icons.length) {
var icons = manifest.icons
var pick = function (size) {
var target = size + 'x' + size
for (var i = 0; i < icons.length; i++) {
if (String(icons[i].sizes || '').indexOf(target) !== -1 && icons[i].src) {
return icons[i].src
}
}
return ''
}
icon = pick(180) || pick(192) || pick(512) || (icons[0] && icons[0].src) || ''
}
}
}
} catch (e) {}
if (title) {
document.title = title
var appleTitle = document.createElement('meta')
appleTitle.name = 'apple-mobile-web-app-title'
appleTitle.content = title
document.head.appendChild(appleTitle)
}
if (icon) {
var sized = document.createElement('link')
sized.rel = 'apple-touch-icon'
sized.setAttribute('data-dynamic-pwa', 'true')
sized.setAttribute('data-pwa-icon-src', icon)
sized.setAttribute('sizes', '180x180')
sized.href = icon
document.head.appendChild(sized)
var primary = document.createElement('link')
primary.rel = 'apple-touch-icon'
primary.setAttribute('data-dynamic-pwa', 'true')
primary.setAttribute('data-pwa-icon-src', icon)
primary.href = icon
document.head.appendChild(primary)
var img = new Image()
img.decoding = 'async'
img.src = icon
}
} catch (e) {}
})()
</script>
<meta name="enamad" content="44889195" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
@@ -343,30 +402,49 @@
return window.location.protocol === 'https:' ? 'https:' : 'http:'
}
function getVersionUrls() {
var protocol = getProtocol()
function resolveTenantDomain() {
var host = String(window.location.hostname || '').trim()
if (!host || host === 'localhost' || host === '127.0.0.1') {
return 'k.montzar.ir'
}
return host
}
function getSuperAdminBases() {
return [
'https://nerkh.gold/get_version',
'https://tablo.talasevin.ir/get_version',
'https://abshode-service.montgit.ir/server/get_version',
protocol + '//212.23.201.173:8381/get_version',
protocol + '//91.212.174.137:4040/server/get_version'
'https://adm.montzar.ir',
'https://adm.montech.ir',
'https://adm.montest.ir'
]
}
function getVersionUrls() {
var domain = encodeURIComponent(resolveTenantDomain())
return getSuperAdminBases().map(function (base) {
return base + '/get_version?domain=' + domain
})
}
function getConfigUrls() {
var protocol = getProtocol()
return [
protocol + '//nerkh.gold/get_user',
protocol + '//tablo.talasevin.ir/get_user',
protocol + '//abshode-service.montgit.ir/server/get_user',
protocol + '//212.23.201.173:8381/get_user',
protocol + '//91.212.174.137:4040/server/get_user'
]
var domain = encodeURIComponent(resolveTenantDomain())
return getSuperAdminBases().map(function (base) {
return base + '/get_user?domain=' + domain
})
}
// پاک‌کردن URL موفقیت قدیمی رجیستری (مثل nerkh.gold)
;(function clearLegacyConfigSuccess() {
var last = localStorage.getItem(CONFIG_SUCCESS_URL_KEY)
if (!last) return
var ok = getSuperAdminBases().some(function (base) {
return last.indexOf(base) === 0
})
if (!ok) {
localStorage.removeItem(CONFIG_SUCCESS_URL_KEY)
document.cookie = CONFIG_COOKIE_NAME + '=; Path=/; Max-Age=0; SameSite=Lax'
}
})()
function sortVersionUrls(urls) {
var lastSuccessUrl = localStorage.getItem(VERSION_SUCCESS_URL_KEY)
@@ -382,6 +460,11 @@
function sortByLastSuccess(urls) {
var lastSuccessUrl = localStorage.getItem(CONFIG_SUCCESS_URL_KEY)
if (lastSuccessUrl && urls.indexOf(lastSuccessUrl) === -1) {
localStorage.removeItem(CONFIG_SUCCESS_URL_KEY)
lastSuccessUrl = null
}
return urls.slice().sort(function (a, b) {
if (a === lastSuccessUrl) return -1
if (b === lastSuccessUrl) return 1
@@ -665,10 +748,10 @@
'<div style="font-size: 42px; margin-bottom: 12px;">⚠️</div>' +
'<h2 style="margin: 0 0 12px; font-size: 20px;">خطا در بارگذاری برنامه</h2>' +
'<p style="margin: 0 0 20px; color: var(--boot-muted); line-height: 2; font-size: 14px;">' +
'ارتباط با سرور برقرار نشد یا فایل‌های برنامه به‌درستی دریافت نشدند.' +
'برقراری ارتباط با سرور امکان‌پذیر نیست. لطفاً اتصال اینترنت خود را بررسی کرده و صفحه را تازه‌سازی نمایید.' +
'</p>' +
'<button onclick="window.__APP_BOOT_SIMPLE_RETRY__()" style="padding: 11px 22px; border: 0; border-radius: 12px; background: var(--boot-accent); color: white; cursor: pointer; font-weight: 700;">' +
'تلاش مجدد' +
'تازه‌سازی صفحه' +
'</button>' +
'<button onclick="window.__APP_BOOT_CLEAN_RELOAD__()" style="margin-right: 8px; padding: 11px 18px; border: 1px solid var(--boot-border); border-radius: 12px; background: transparent; color: var(--boot-text); cursor: pointer; font-weight: 700;">' +
'پاک‌سازی و تلاش مجدد' +
@@ -680,8 +763,8 @@
async function cleanFetchAndReload() {
renderRecovering('در حال دریافت تنظیمات جدید برنامه...')
var version = await loadRemoteVersion(5)
var freshConfigResult = await fetchFreshConfig(5)
var version = await loadRemoteVersion(3)
var freshConfigResult = await fetchFreshConfig(3)
clearReadableCookies()
// clearLocalStorageExceptToken()