Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7deee2be61 | ||
|
|
ea73d15eaf | ||
|
|
0948a8da24 |
No files matched your search
+1
-1
@@ -140,7 +140,7 @@ try {
|
|||||||
$installedVerData = file_exists("{$appDir}/version.json")
|
$installedVerData = file_exists("{$appDir}/version.json")
|
||||||
? json_decode((string)file_get_contents("{$appDir}/version.json"), true)
|
? json_decode((string)file_get_contents("{$appDir}/version.json"), true)
|
||||||
: [];
|
: [];
|
||||||
$installedVer = $installedVerData['version'] ?? '1.3.9';
|
$installedVer = $installedVerData['version'] ?? '1.4.2';
|
||||||
|
|
||||||
echo json_encode([
|
echo json_encode([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
+63
-19
@@ -266,20 +266,28 @@ function renderCompanyBranding() {
|
|||||||
|
|
||||||
if (loginBox) {
|
if (loginBox) {
|
||||||
loginBox.classList.remove('hidden');
|
loginBox.classList.remove('hidden');
|
||||||
|
loginBox.classList.add('flex');
|
||||||
if (logo && loginLogo) {
|
if (logo && loginLogo) {
|
||||||
loginLogo.src = logo;
|
loginLogo.src = logo;
|
||||||
loginLogo.classList.remove('hidden');
|
loginLogo.classList.remove('hidden');
|
||||||
} else if (loginLogo) {
|
} else if (loginLogo) {
|
||||||
|
loginLogo.src = '';
|
||||||
loginLogo.classList.add('hidden');
|
loginLogo.classList.add('hidden');
|
||||||
}
|
}
|
||||||
if (loginName) loginName.textContent = name;
|
if (loginName) {
|
||||||
|
loginName.textContent = name;
|
||||||
|
loginName.classList.toggle('hidden', !name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
emptyBox?.classList.remove('hidden');
|
emptyBox?.classList.remove('hidden');
|
||||||
filledBox?.classList.add('hidden');
|
filledBox?.classList.add('hidden');
|
||||||
if (logoImg) { logoImg.src = ''; logoImg.classList.add('hidden'); }
|
if (logoImg) { logoImg.src = ''; logoImg.classList.add('hidden'); }
|
||||||
if (nameEl) { nameEl.textContent = ''; nameEl.classList.add('hidden'); }
|
if (nameEl) { nameEl.textContent = ''; nameEl.classList.add('hidden'); }
|
||||||
loginBox?.classList.add('hidden');
|
if (loginBox) {
|
||||||
|
loginBox.classList.add('hidden');
|
||||||
|
loginBox.classList.remove('flex');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -629,20 +637,56 @@ function drawDualAreaChart(svgId, seriesA, seriesB, maxVal, colorA, colorB, unit
|
|||||||
const padTop = 14;
|
const padTop = 14;
|
||||||
const padBottom = 18;
|
const padBottom = 18;
|
||||||
const plotH = height - padTop - padBottom;
|
const plotH = height - padTop - padBottom;
|
||||||
|
const baselineY = padTop + plotH;
|
||||||
const safeMax = maxVal > 0 ? maxVal : 100;
|
const safeMax = maxVal > 0 ? maxVal : 100;
|
||||||
|
|
||||||
const toPoints = (arr) => arr.map((val, idx) => {
|
const clampY = (y) => Math.max(padTop, Math.min(baselineY, y));
|
||||||
const x = (idx / (arr.length - 1)) * width;
|
|
||||||
const ratio = Math.max(0, Math.min(1, val / safeMax));
|
|
||||||
const y = padTop + plotH - (ratio * plotH);
|
|
||||||
return `${x.toFixed(1)},${y.toFixed(1)}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
const ptsA = toPoints(seriesA);
|
// Génération de courbes splines adoucies (Catmull-Rom / Bézier cubique) pour éliminer les angles pointus
|
||||||
const ptsB = toPoints(seriesB);
|
const computeSmoothPaths = (arr, tension = 0.2) => {
|
||||||
|
if (!arr || arr.length === 0) return { line: '', area: '' };
|
||||||
|
|
||||||
const areaA = `0,${padTop + plotH} ` + ptsA.join(' ') + ` ${width},${padTop + plotH}`;
|
const points = arr.map((val, idx) => {
|
||||||
const areaB = `0,${padTop + plotH} ` + ptsB.join(' ') + ` ${width},${padTop + plotH}`;
|
const x = (idx / (arr.length - 1)) * width;
|
||||||
|
const ratio = Math.max(0, Math.min(1, val / safeMax));
|
||||||
|
const y = baselineY - (ratio * plotH);
|
||||||
|
return { x, y };
|
||||||
|
});
|
||||||
|
|
||||||
|
if (points.length === 1) {
|
||||||
|
return {
|
||||||
|
line: `M ${points[0].x.toFixed(1)},${points[0].y.toFixed(1)}`,
|
||||||
|
area: `M 0,${baselineY} L ${points[0].x.toFixed(1)},${points[0].y.toFixed(1)} L ${width},${baselineY} Z`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let line = `M ${points[0].x.toFixed(1)},${points[0].y.toFixed(1)}`;
|
||||||
|
|
||||||
|
for (let i = 0; i < points.length - 1; i++) {
|
||||||
|
const p0 = points[i === 0 ? i : i - 1];
|
||||||
|
const p1 = points[i];
|
||||||
|
const p2 = points[i + 1];
|
||||||
|
const p3 = points[i + 2 < points.length ? i + 2 : i + 1];
|
||||||
|
|
||||||
|
let cp1x = p1.x + (p2.x - p0.x) * tension;
|
||||||
|
let cp1y = p1.y + (p2.y - p0.y) * tension;
|
||||||
|
|
||||||
|
let cp2x = p2.x - (p3.x - p1.x) * tension;
|
||||||
|
let cp2y = p2.y - (p3.y - p1.y) * tension;
|
||||||
|
|
||||||
|
// Clamping vertical pour garantir que la courbe ne déborde pas sous l'axe ni au-dessus du max
|
||||||
|
cp1y = clampY(cp1y);
|
||||||
|
cp2y = clampY(cp2y);
|
||||||
|
|
||||||
|
line += ` C ${cp1x.toFixed(1)},${cp1y.toFixed(1)} ${cp2x.toFixed(1)},${cp2y.toFixed(1)} ${p2.x.toFixed(1)},${p2.y.toFixed(1)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const area = `${line} L ${width},${baselineY} L 0,${baselineY} Z`;
|
||||||
|
return { line, area };
|
||||||
|
};
|
||||||
|
|
||||||
|
const pathA = computeSmoothPaths(seriesA);
|
||||||
|
const pathB = computeSmoothPaths(seriesB);
|
||||||
|
|
||||||
const gradIdA = `${svgId}-grad-a`;
|
const gradIdA = `${svgId}-grad-a`;
|
||||||
const gradIdB = `${svgId}-grad-b`;
|
const gradIdB = `${svgId}-grad-b`;
|
||||||
@@ -662,18 +706,18 @@ function drawDualAreaChart(svgId, seriesA, seriesB, maxVal, colorA, colorB, unit
|
|||||||
<!-- Lignes de grille horizontales -->
|
<!-- Lignes de grille horizontales -->
|
||||||
<line x1="0" y1="${padTop}" x2="${width}" y2="${padTop}" stroke="#2c323f" stroke-dasharray="3,3" stroke-width="1"/>
|
<line x1="0" y1="${padTop}" x2="${width}" y2="${padTop}" stroke="#2c323f" stroke-dasharray="3,3" stroke-width="1"/>
|
||||||
<line x1="0" y1="${padTop + plotH / 2}" x2="${width}" y2="${padTop + plotH / 2}" stroke="#2c323f" stroke-dasharray="3,3" stroke-width="1"/>
|
<line x1="0" y1="${padTop + plotH / 2}" x2="${width}" y2="${padTop + plotH / 2}" stroke="#2c323f" stroke-dasharray="3,3" stroke-width="1"/>
|
||||||
<line x1="0" y1="${padTop + plotH}" x2="${width}" y2="${padTop + plotH}" stroke="#3d4657" stroke-width="1"/>
|
<line x1="0" y1="${baselineY}" x2="${width}" y2="${baselineY}" stroke="#3d4657" stroke-width="1"/>
|
||||||
|
|
||||||
<text x="6" y="${padTop + 10}" fill="#94a3b8" font-size="10" font-family="monospace">${Math.round(safeMax)} ${unitLabel}</text>
|
<text x="6" y="${padTop + 10}" fill="#94a3b8" font-size="10" font-family="monospace">${Math.round(safeMax)} ${unitLabel}</text>
|
||||||
<text x="6" y="${padTop + plotH / 2 - 4}" fill="#64748b" font-size="10" font-family="monospace">${Math.round(safeMax / 2)} ${unitLabel}</text>
|
<text x="6" y="${padTop + plotH / 2 - 4}" fill="#64748b" font-size="10" font-family="monospace">${Math.round(safeMax / 2)} ${unitLabel}</text>
|
||||||
|
|
||||||
<!-- Courbe B -->
|
<!-- Courbe B lissée avec dégradé -->
|
||||||
<polygon points="${areaB}" fill="url(#${gradIdB})"/>
|
<path d="${pathB.area}" fill="url(#${gradIdB})"/>
|
||||||
<polyline fill="none" stroke="${colorB}" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" points="${ptsB.join(' ')}"/>
|
<path d="${pathB.line}" fill="none" stroke="${colorB}" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
|
||||||
<!-- Courbe A -->
|
<!-- Courbe A lissée avec dégradé -->
|
||||||
<polygon points="${areaA}" fill="url(#${gradIdA})"/>
|
<path d="${pathA.area}" fill="url(#${gradIdA})"/>
|
||||||
<polyline fill="none" stroke="${colorA}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" points="${ptsA.join(' ')}"/>
|
<path d="${pathA.line}" fill="none" stroke="${colorA}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
+21
-10
@@ -11,8 +11,14 @@ Database::getConnection();
|
|||||||
$isAuth = Auth::isAuthenticated();
|
$isAuth = Auth::isAuthenticated();
|
||||||
$currentUser = Auth::getUsername();
|
$currentUser = Auth::getUsername();
|
||||||
|
|
||||||
|
$stmtSettings = Database::getConnection()->query('SELECT company_name, company_logo FROM settings WHERE id = 1 LIMIT 1');
|
||||||
|
$branding = $stmtSettings ? $stmtSettings->fetch(\PDO::FETCH_ASSOC) : null;
|
||||||
|
$companyName = trim($branding['company_name'] ?? '');
|
||||||
|
$companyLogo = trim($branding['company_logo'] ?? '');
|
||||||
|
$hasBranding = !empty($companyName) || !empty($companyLogo);
|
||||||
|
|
||||||
$appVerData = file_exists(__DIR__ . '/../version.json') ? json_decode((string)file_get_contents(__DIR__ . '/../version.json'), true) : [];
|
$appVerData = file_exists(__DIR__ . '/../version.json') ? json_decode((string)file_get_contents(__DIR__ . '/../version.json'), true) : [];
|
||||||
$appVersion = $appVerData['version'] ?? '1.3.9';
|
$appVersion = $appVerData['version'] ?? '1.4.2';
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr" class="h-full">
|
<html lang="fr" class="h-full">
|
||||||
@@ -144,8 +150,13 @@ $appVersion = $appVerData['version'] ?? '1.3.9';
|
|||||||
<div id="toast-container" class="fixed bottom-5 right-5 z-50 flex flex-col gap-2.5 max-w-md w-full pointer-events-none px-4 sm:px-0"></div>
|
<div id="toast-container" class="fixed bottom-5 right-5 z-50 flex flex-col gap-2.5 max-w-md w-full pointer-events-none px-4 sm:px-0"></div>
|
||||||
|
|
||||||
<!-- ÉCRAN DE CONNEXION -->
|
<!-- ÉCRAN DE CONNEXION -->
|
||||||
<div id="login-screen" class="<?= $isAuth ? 'hidden' : 'flex' ?> min-h-screen w-full items-center justify-center p-4 bg-gradient-to-br from-anthracite-950 via-anthracite-900 to-indigo-950/40">
|
<div id="login-screen" class="<?= $isAuth ? 'hidden' : 'flex' ?> min-h-screen w-full items-center justify-center p-4 relative overflow-hidden bg-anthracite-950">
|
||||||
<div class="w-full max-w-md bg-anthracite-800 border border-anthracite-700 rounded-2xl shadow-2xl p-8">
|
<!-- Arrière-plan personnalisé / Image de fond avec overlay sombre et léger flou -->
|
||||||
|
<div id="login-bg-layer" class="absolute inset-0 bg-cover bg-center bg-no-repeat" style="background-image: url('/assets/login-bg.jpg?v=<?= file_exists(__DIR__ . '/assets/login-bg.jpg') ? filemtime(__DIR__ . '/assets/login-bg.jpg') : 1 ?>');"></div>
|
||||||
|
<div class="absolute inset-0 bg-gradient-to-tr from-anthracite-950/70 via-anthracite-950/50 to-indigo-950/30 backdrop-blur-[1px]"></div>
|
||||||
|
|
||||||
|
<!-- Carte de connexion flottante avec effet Glassmorphism transparent et aéré -->
|
||||||
|
<div class="relative z-10 w-full max-w-md bg-anthracite-900/60 backdrop-blur-2xl border border-white/15 rounded-3xl shadow-2xl shadow-black/50 p-8 sm:p-9 transition-all">
|
||||||
<div class="flex items-center gap-3.5 mb-6">
|
<div class="flex items-center gap-3.5 mb-6">
|
||||||
<div class="w-12 h-12 rounded-xl bg-indigo-500/20 border border-indigo-400/40 flex items-center justify-center text-indigo-300 shadow-inner">
|
<div class="w-12 h-12 rounded-xl bg-indigo-500/20 border border-indigo-400/40 flex items-center justify-center text-indigo-300 shadow-inner">
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -158,25 +169,25 @@ $appVersion = $appVerData['version'] ?? '1.3.9';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Aperçu Logo Entreprise sur l'écran de connexion (si configuré) -->
|
<!-- Aperçu Logo Entreprise sur l'écran de connexion (rendu serveur natif) -->
|
||||||
<div id="login-company-box" class="hidden mb-5 p-3 rounded-xl bg-anthracite-950/90 border border-anthracite-700 flex items-center justify-center gap-3">
|
<div id="login-company-box" class="<?= $hasBranding ? 'flex' : 'hidden' ?> mb-5 p-3 rounded-xl bg-anthracite-950/45 border border-white/10 backdrop-blur-md items-center justify-center gap-3">
|
||||||
<img id="login-company-logo" src="" alt="Logo Entreprise" class="max-h-10 max-w-[160px] object-contain hidden">
|
<img id="login-company-logo" src="<?= htmlspecialchars($companyLogo) ?>" alt="Logo Entreprise" class="max-h-10 max-w-[160px] object-contain <?= !empty($companyLogo) ? '' : 'hidden' ?>">
|
||||||
<span id="login-company-name" class="text-xs font-semibold text-slate-200"></span>
|
<span id="login-company-name" class="text-xs font-semibold text-slate-200 <?= !empty($companyName) ? '' : 'hidden' ?>"><?= htmlspecialchars($companyName) ?></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="login-form" class="space-y-4">
|
<form id="login-form" class="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold uppercase tracking-wider text-slate-300 mb-1.5">Identifiant</label>
|
<label class="block text-xs font-semibold uppercase tracking-wider text-slate-300 mb-1.5">Identifiant</label>
|
||||||
<input type="text" name="username" value="admin" required
|
<input type="text" name="username" value="admin" required
|
||||||
class="w-full px-3.5 py-2.5 rounded-lg bg-anthracite-950 border border-anthracite-700 text-white focus:outline-none focus:border-indigo-400 text-sm">
|
class="w-full px-3.5 py-2.5 rounded-xl bg-anthracite-950/50 backdrop-blur-md border border-white/10 text-white placeholder-slate-400 focus:outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-500/20 text-sm transition">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-semibold uppercase tracking-wider text-slate-300 mb-1.5">Mot de passe</label>
|
<label class="block text-xs font-semibold uppercase tracking-wider text-slate-300 mb-1.5">Mot de passe</label>
|
||||||
<input type="password" name="password" placeholder="••••••••" required
|
<input type="password" name="password" placeholder="••••••••" required
|
||||||
class="w-full px-3.5 py-2.5 rounded-lg bg-anthracite-950 border border-anthracite-700 text-white focus:outline-none focus:border-indigo-400 text-sm">
|
class="w-full px-3.5 py-2.5 rounded-xl bg-anthracite-950/50 backdrop-blur-md border border-white/10 text-white placeholder-slate-400 focus:outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-500/20 text-sm transition">
|
||||||
</div>
|
</div>
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
class="w-full py-2.5 px-4 bg-indigo-600 hover:bg-indigo-500 active:bg-indigo-700 text-white font-semibold rounded-lg shadow-lg shadow-indigo-600/25 transition text-sm">
|
class="w-full py-2.5 px-4 bg-gradient-to-r from-indigo-600 to-indigo-500 hover:from-indigo-500 hover:to-indigo-400 active:from-indigo-700 active:to-indigo-600 text-white font-semibold rounded-xl shadow-lg shadow-indigo-500/25 transition text-sm">
|
||||||
Se connecter au PBX
|
Se connecter au PBX
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+5
-5
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"version": "1.3.9",
|
"version": "1.4.2",
|
||||||
"codename": "Helios",
|
"codename": "Helios",
|
||||||
"release_date": "2026-11-04",
|
"release_date": "2026-11-04",
|
||||||
"min_php_version": "8.2",
|
"min_php_version": "8.2",
|
||||||
"changelog": [
|
"changelog": [
|
||||||
"Mise à jour instantanée du numéro de version sur tous les badges sans aucun délai de cache navigateur",
|
"Rendu serveur natif garanti du logo et nom de l'exploitant sur la page de connexion",
|
||||||
"Invalidation automatique du cache local et rechargement propre anti-cache après chaque mise à jour",
|
"Boîte de connexion allégée en style verre dépoli transparent (Glassmorphism backdrop-blur)",
|
||||||
"Affichage serveur natif du numéro de version installée directement dans le HTML généré",
|
"Nouveau fond d'écran immersif haute définition (réseau constellation)",
|
||||||
"Suppression définitive de l'indication des identifiants et mot de passe par défaut sur l'écran de connexion"
|
"Lissage élégant des graphiques CPU/RAM et Réseau (splines Bézier cubiques sans angles vifs)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user