vor ähnlichkeitsprüfung
This commit is contained in:
@@ -7,6 +7,7 @@ COPY settings.html /usr/share/nginx/html/
|
||||
COPY bookmarks.html /usr/share/nginx/html/
|
||||
COPY daily-bookmarks.html /usr/share/nginx/html/
|
||||
COPY automation.html /usr/share/nginx/html/
|
||||
COPY login.html /usr/share/nginx/html/
|
||||
COPY style.css /usr/share/nginx/html/
|
||||
COPY dashboard.css /usr/share/nginx/html/
|
||||
COPY settings.css /usr/share/nginx/html/
|
||||
@@ -17,6 +18,7 @@ COPY dashboard.js /usr/share/nginx/html/
|
||||
COPY settings.js /usr/share/nginx/html/
|
||||
COPY daily-bookmarks.js /usr/share/nginx/html/
|
||||
COPY automation.js /usr/share/nginx/html/
|
||||
COPY login.js /usr/share/nginx/html/
|
||||
COPY vendor /usr/share/nginx/html/vendor/
|
||||
COPY assets /usr/share/nginx/html/assets/
|
||||
|
||||
|
||||
93
web/app.js
93
web/app.js
@@ -1,4 +1,5 @@
|
||||
const API_URL = 'https://fb.srv.medeba-media.de/api';
|
||||
const LOGIN_PAGE = 'login.html';
|
||||
|
||||
let initialViewParam = null;
|
||||
try {
|
||||
@@ -37,6 +38,30 @@ const PROFILE_NAMES = {
|
||||
4: 'Profil 4',
|
||||
5: 'Profil 5'
|
||||
};
|
||||
|
||||
function redirectToLogin() {
|
||||
try {
|
||||
const redirect = encodeURIComponent(window.location.href);
|
||||
window.location.href = `${LOGIN_PAGE}?redirect=${redirect}`;
|
||||
} catch (_error) {
|
||||
window.location.href = LOGIN_PAGE;
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureAuthenticated() {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/session`, { credentials: 'include' });
|
||||
if (response.status === 401) {
|
||||
redirectToLogin();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.warn('Konnte Auth-Status nicht prüfen:', error);
|
||||
redirectToLogin();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function apiFetch(url, options = {}) {
|
||||
const config = {
|
||||
@@ -48,7 +73,36 @@ function apiFetch(url, options = {}) {
|
||||
config.headers = { ...options.headers };
|
||||
}
|
||||
|
||||
return fetch(url, config);
|
||||
return fetch(url, config).then((response) => {
|
||||
if (response.status === 401) {
|
||||
redirectToLogin();
|
||||
throw new Error('Nicht angemeldet');
|
||||
}
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
function markAppReady() {
|
||||
if (document && document.body) {
|
||||
document.body.classList.remove('auth-pending');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
try {
|
||||
await fetch(`${API_URL}/logout`, { method: 'POST', credentials: 'include' });
|
||||
} catch (error) {
|
||||
// ignore network errors on logout
|
||||
}
|
||||
redirectToLogin();
|
||||
}
|
||||
|
||||
function bindLogoutButton() {
|
||||
const btn = document.getElementById('logoutBtn');
|
||||
if (!btn) {
|
||||
return;
|
||||
}
|
||||
btn.addEventListener('click', handleLogout);
|
||||
}
|
||||
|
||||
function sortPostsByCreatedAt() {
|
||||
@@ -4544,16 +4598,27 @@ window.addEventListener('resize', () => {
|
||||
});
|
||||
|
||||
// Initialize
|
||||
initializeBookmarks();
|
||||
loadAutoRefreshSettings();
|
||||
initializeFocusParams();
|
||||
initializeTabFromUrl();
|
||||
updateMergeControlsUI();
|
||||
loadSortMode();
|
||||
resetManualPostForm();
|
||||
loadProfile();
|
||||
startProfilePolling();
|
||||
fetchPosts();
|
||||
checkAutoCheck();
|
||||
startUpdatesStream();
|
||||
applyAutoRefreshSettings();
|
||||
async function bootstrapApp() {
|
||||
const authenticated = await ensureAuthenticated();
|
||||
if (!authenticated) {
|
||||
return;
|
||||
}
|
||||
|
||||
markAppReady();
|
||||
bindLogoutButton();
|
||||
initializeBookmarks();
|
||||
loadAutoRefreshSettings();
|
||||
initializeFocusParams();
|
||||
initializeTabFromUrl();
|
||||
updateMergeControlsUI();
|
||||
loadSortMode();
|
||||
resetManualPostForm();
|
||||
loadProfile();
|
||||
startProfilePolling();
|
||||
fetchPosts();
|
||||
checkAutoCheck();
|
||||
startUpdatesStream();
|
||||
applyAutoRefreshSettings();
|
||||
}
|
||||
|
||||
bootstrapApp();
|
||||
|
||||
@@ -96,6 +96,18 @@
|
||||
let sse = null;
|
||||
let relativeTimer = null;
|
||||
|
||||
function handleUnauthorized(response) {
|
||||
if (response && response.status === 401) {
|
||||
if (typeof redirectToLogin === 'function') {
|
||||
redirectToLogin();
|
||||
} else {
|
||||
window.location.href = 'login.html';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function toDateTimeLocal(value) {
|
||||
if (!value) return '';
|
||||
const date = value instanceof Date ? value : new Date(value);
|
||||
@@ -120,6 +132,9 @@
|
||||
...(options.headers || {})
|
||||
};
|
||||
const response = await fetch(`${API_URL}${path}`, opts);
|
||||
if (handleUnauthorized(response)) {
|
||||
throw new Error('Nicht angemeldet');
|
||||
}
|
||||
if (!response.ok) {
|
||||
let message = 'Unbekannter Fehler';
|
||||
try {
|
||||
@@ -1335,4 +1350,9 @@
|
||||
activate,
|
||||
deactivate: cleanup
|
||||
};
|
||||
|
||||
const automationSection = document.querySelector('[data-view="automation"]');
|
||||
if (automationSection && automationSection.classList.contains('app-view--active')) {
|
||||
activate();
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -9,6 +9,19 @@
|
||||
const DEFAULT_BULK_COUNT = 5;
|
||||
const DEFAULT_SORT = { column: 'last_completed_at', direction: 'desc' };
|
||||
const AUTO_OPEN_DELAY_MS = 1500;
|
||||
const LOGIN_PAGE = 'login.html';
|
||||
|
||||
function handleUnauthorized(response) {
|
||||
if (response && response.status === 401) {
|
||||
if (typeof redirectToLogin === 'function') {
|
||||
redirectToLogin();
|
||||
} else {
|
||||
window.location.href = LOGIN_PAGE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const state = {
|
||||
dayKey: formatDayKey(new Date()),
|
||||
@@ -436,6 +449,10 @@
|
||||
}
|
||||
});
|
||||
|
||||
if (handleUnauthorized(response)) {
|
||||
throw new Error('Nicht angemeldet');
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const message = `Fehler: HTTP ${response.status}`;
|
||||
throw new Error(message);
|
||||
@@ -1479,4 +1496,9 @@
|
||||
activate,
|
||||
deactivate: cleanup
|
||||
};
|
||||
|
||||
const dailySection = document.querySelector('[data-view="daily-bookmarks"]');
|
||||
if (dailySection && dailySection.classList.contains('app-view--active')) {
|
||||
activate();
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(() => {
|
||||
const API_URL = 'https://fb.srv.medeba-media.de/api';
|
||||
const LOGIN_PAGE = 'login.html';
|
||||
|
||||
let posts = [];
|
||||
let filteredPosts = [];
|
||||
@@ -9,6 +10,18 @@ let currentProfileFilter = 'all';
|
||||
const DAY_IN_MS = 24 * 60 * 60 * 1000;
|
||||
const HOUR_IN_MS = 60 * 60 * 1000;
|
||||
|
||||
function handleUnauthorized(response) {
|
||||
if (response && response.status === 401) {
|
||||
if (typeof redirectToLogin === 'function') {
|
||||
redirectToLogin();
|
||||
} else {
|
||||
window.location.href = LOGIN_PAGE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function startOfDay(date) {
|
||||
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
}
|
||||
|
||||
110
web/index.html
110
web/index.html
@@ -7,13 +7,89 @@
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/app-icon-64.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/app-icon-192.png">
|
||||
<link rel="apple-touch-icon" href="assets/app-icon-192.png">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<link rel="stylesheet" href="dashboard.css">
|
||||
<link rel="stylesheet" href="settings.css">
|
||||
<link rel="stylesheet" href="automation.css">
|
||||
<link id="dailyBookmarksCss" rel="stylesheet" href="daily-bookmarks.css" disabled>
|
||||
<style>
|
||||
body.auth-pending {
|
||||
visibility: hidden;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
(function gateAssets() {
|
||||
const API_URL = 'https://fb.srv.medeba-media.de/api';
|
||||
const LOGIN_PAGE = 'login.html';
|
||||
const cssFiles = [
|
||||
{ href: 'style.css' },
|
||||
{ href: 'dashboard.css' },
|
||||
{ href: 'settings.css' },
|
||||
{ href: 'automation.css' },
|
||||
{ href: 'daily-bookmarks.css', id: 'dailyBookmarksCss', disabled: true }
|
||||
];
|
||||
const jsFiles = [
|
||||
'app.js',
|
||||
'dashboard.js',
|
||||
'settings.js',
|
||||
'vendor/list.min.js',
|
||||
'automation.js',
|
||||
'daily-bookmarks.js'
|
||||
];
|
||||
|
||||
function redirectToLogin() {
|
||||
try {
|
||||
const redirect = encodeURIComponent(window.location.href);
|
||||
window.location.replace(`${LOGIN_PAGE}?redirect=${redirect}`);
|
||||
} catch (_error) {
|
||||
window.location.replace(LOGIN_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
function loadStyles() {
|
||||
cssFiles.forEach(({ href, id, disabled }) => {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = href;
|
||||
if (id) link.id = id;
|
||||
if (disabled) link.disabled = true;
|
||||
document.head.appendChild(link);
|
||||
});
|
||||
}
|
||||
|
||||
function loadScriptsSequentially(list, index = 0) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (index >= list.length) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const script = document.createElement('script');
|
||||
script.src = list[index];
|
||||
script.onload = () => loadScriptsSequentially(list, index + 1).then(resolve).catch(reject);
|
||||
script.onerror = reject;
|
||||
document.body.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/session`, { credentials: 'include' });
|
||||
if (res.status === 401) {
|
||||
redirectToLogin();
|
||||
return;
|
||||
}
|
||||
loadStyles();
|
||||
await loadScriptsSequentially(jsFiles);
|
||||
} catch (_error) {
|
||||
redirectToLogin();
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||
bootstrap();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', bootstrap);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<body class="auth-pending">
|
||||
<div class="shell">
|
||||
<header class="site-header site-header--global">
|
||||
<div class="container">
|
||||
@@ -28,6 +104,7 @@
|
||||
<a class="site-nav__btn" data-view-target="bookmarks" href="bookmarks.html">🔖 Bookmarks</a>
|
||||
<a class="site-nav__btn" data-view-target="automation" href="index.html?view=automation">⚡️ Automationen</a>
|
||||
<a class="site-nav__btn" data-view-target="daily-bookmarks" href="index.html?view=daily-bookmarks">✅ Daily Bookmarks</a>
|
||||
<button class="site-nav__btn site-nav__btn--icon" id="logoutBtn" type="button" aria-label="Abmelden">🚪</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1305,12 +1382,6 @@
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="app.js"></script>
|
||||
<script src="dashboard.js"></script>
|
||||
<script src="settings.js"></script>
|
||||
<script src="vendor/list.min.js"></script>
|
||||
<script src="automation.js"></script>
|
||||
<script src="daily-bookmarks.js"></script>
|
||||
<script>
|
||||
(function() {
|
||||
const buttons = Array.from(document.querySelectorAll('[data-view-target]'));
|
||||
@@ -1320,11 +1391,25 @@
|
||||
const initialParam = params.get('view');
|
||||
const defaultView = viewMap[initialParam] ? initialParam : 'posts';
|
||||
const VIEW_CHANGE_EVENT = 'app:view-change';
|
||||
const BASE_TITLE = 'Post Tracker';
|
||||
const VIEW_TITLES = {
|
||||
posts: 'Beiträge',
|
||||
dashboard: 'Dashboard',
|
||||
settings: 'Einstellungen',
|
||||
bookmarks: 'Bookmarks',
|
||||
automation: 'Automationen',
|
||||
'daily-bookmarks': 'Daily Bookmarks'
|
||||
};
|
||||
|
||||
function dispatchViewChange(view) {
|
||||
window.dispatchEvent(new CustomEvent(VIEW_CHANGE_EVENT, { detail: { view } }));
|
||||
}
|
||||
|
||||
function updateDocumentTitle(view) {
|
||||
const title = VIEW_TITLES[view];
|
||||
document.title = title ? `${title} – ${BASE_TITLE}` : BASE_TITLE;
|
||||
}
|
||||
|
||||
function setView(view, options = { updateHistory: true }) {
|
||||
if (!viewMap[view]) {
|
||||
view = 'posts';
|
||||
@@ -1357,6 +1442,7 @@
|
||||
window.history.pushState({ view }, '', newUrl);
|
||||
}
|
||||
|
||||
updateDocumentTitle(view);
|
||||
dispatchViewChange(view);
|
||||
}
|
||||
|
||||
|
||||
111
web/login.html
Normal file
111
web/login.html
Normal file
@@ -0,0 +1,111 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login – Post Tracker</title>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/app-icon-64.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="assets/app-icon-192.png">
|
||||
<link rel="apple-touch-icon" href="assets/app-icon-192.png">
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
color: #0f172a;
|
||||
}
|
||||
.login-card {
|
||||
background: #fff;
|
||||
padding: 32px;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 20px 60px rgba(15, 23, 42, 0.15);
|
||||
width: min(420px, 90vw);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.login-card h1 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 24px;
|
||||
}
|
||||
.login-card p {
|
||||
margin: 0 0 24px;
|
||||
color: #475569;
|
||||
line-height: 1.4;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 600;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 10px;
|
||||
box-sizing: border-box;
|
||||
font-size: 16px;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
input:focus {
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
|
||||
outline: none;
|
||||
}
|
||||
.field {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
background: #2563eb;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.08s ease, box-shadow 0.2s ease, background 0.2s ease;
|
||||
box-shadow: 0 12px 30px rgba(37, 99, 235, 0.25);
|
||||
}
|
||||
button:hover {
|
||||
background: #1d4ed8;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 6px 20px rgba(37, 99, 235, 0.18);
|
||||
}
|
||||
.status {
|
||||
margin-top: 14px;
|
||||
min-height: 22px;
|
||||
color: #b91c1c;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<h1>📋 Post Tracker Login</h1>
|
||||
<p>Bitte melde dich an, um das Dashboard zu öffnen.</p>
|
||||
<form id="loginForm" novalidate>
|
||||
<div class="field">
|
||||
<label for="username">Benutzername</label>
|
||||
<input type="text" id="username" name="username" autocomplete="username" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="password">Passwort</label>
|
||||
<input type="password" id="password" name="password" autocomplete="current-password" required>
|
||||
</div>
|
||||
<button type="submit">Anmelden</button>
|
||||
<div id="status" class="status" role="status" aria-live="polite"></div>
|
||||
</form>
|
||||
</div>
|
||||
<script src="login.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
89
web/login.js
Normal file
89
web/login.js
Normal file
@@ -0,0 +1,89 @@
|
||||
const API_URL = 'https://fb.srv.medeba-media.de/api';
|
||||
|
||||
function getRedirectTarget() {
|
||||
try {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const redirect = params.get('redirect');
|
||||
if (redirect) {
|
||||
return decodeURIComponent(redirect);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Konnte Redirect-Parameter nicht lesen:', error);
|
||||
}
|
||||
return 'index.html';
|
||||
}
|
||||
|
||||
function updateStatus(message, isError = false) {
|
||||
const statusEl = document.getElementById('status');
|
||||
if (!statusEl) {
|
||||
return;
|
||||
}
|
||||
statusEl.textContent = message || '';
|
||||
statusEl.style.color = isError ? '#b91c1c' : '#15803d';
|
||||
}
|
||||
|
||||
async function checkExistingSession() {
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/session`, { credentials: 'include' });
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data && data.authenticated) {
|
||||
window.location.href = getRedirectTarget();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Konnte Session nicht prüfen:', error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function handleLogin(event) {
|
||||
event.preventDefault();
|
||||
const usernameInput = document.getElementById('username');
|
||||
const passwordInput = document.getElementById('password');
|
||||
|
||||
const username = usernameInput ? usernameInput.value.trim() : '';
|
||||
const password = passwordInput ? passwordInput.value : '';
|
||||
|
||||
if (!username || !password) {
|
||||
updateStatus('Bitte Benutzername und Passwort eingeben.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
updateStatus('Anmeldung läuft…', false);
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
const message = payload && payload.error ? payload.error : 'Anmeldung fehlgeschlagen';
|
||||
updateStatus(message, true);
|
||||
return;
|
||||
}
|
||||
|
||||
updateStatus('Erfolgreich angemeldet. Weiterleitung…', false);
|
||||
window.location.href = getRedirectTarget();
|
||||
} catch (error) {
|
||||
console.error('Login fehlgeschlagen:', error);
|
||||
updateStatus('Netzwerkfehler – bitte erneut versuchen.', true);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const alreadyLoggedIn = await checkExistingSession();
|
||||
if (alreadyLoggedIn) {
|
||||
return;
|
||||
}
|
||||
|
||||
const form = document.getElementById('loginForm');
|
||||
if (form) {
|
||||
form.addEventListener('submit', handleLogin);
|
||||
}
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
(() => {
|
||||
const API_URL = 'https://fb.srv.medeba-media.de/api';
|
||||
const LOGIN_PAGE = 'login.html';
|
||||
|
||||
const PROVIDER_MODELS = {
|
||||
gemini: [
|
||||
@@ -72,8 +73,25 @@ let moderationSettings = {
|
||||
sports_auto_hide_enabled: false
|
||||
};
|
||||
|
||||
function handleUnauthorized(response) {
|
||||
if (response && response.status === 401) {
|
||||
if (typeof redirectToLogin === 'function') {
|
||||
redirectToLogin();
|
||||
} else {
|
||||
window.location.href = LOGIN_PAGE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function apiFetch(url, options = {}) {
|
||||
return fetch(url, {...options, credentials: 'include'});
|
||||
return fetch(url, {...options, credentials: 'include'}).then((response) => {
|
||||
if (handleUnauthorized(response)) {
|
||||
throw new Error('Nicht angemeldet');
|
||||
}
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
function showToast(message, type = 'info') {
|
||||
|
||||
@@ -122,6 +122,12 @@ header {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.site-nav__btn--icon {
|
||||
padding: 10px 12px;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.site-nav__btn::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
||||
Reference in New Issue
Block a user