Corp/backend/templates/base.html
Игорь Надеин f44a9ac23d feat: Complete CSS to SCSS migration (100%)
- Migrated all 28 CSS components to SCSS architecture
- Deleted 29 source CSS files (kept only compiled app.css & bootstrap-custom.css)
- Created 30 SCSS component modules in scss/components/
- Cleaned 24 templates from duplicate CSS links
- Updated main.scss with new imports (layout-spacing, spacing-utils, rightbar-calendar)
- Filled empty _navbar.scss with content
- Refactored css/ folder: removed obsolete files, updated README.md
- Created comprehensive documentation (migration complete, quick reference, reports)

Migration Statistics:
- Components migrated: 28 (~4,768 lines SCSS)
- CSS files deleted: 29
- Templates cleaned: 24
- app.css size: 95 KB (compiled from SCSS)
- bootstrap-custom.css: 301 KB
- Total: 396 KB

Phase 6 Complete: 100% SCSS architecture achieved ✅
2026-01-15 13:48:11 +03:00

169 lines
7.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% load static api_tags %}
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="color-scheme" content="light dark">
{% if user.is_authenticated %}
<meta name="api-access" content="{% api_access_token request %}">
<meta name="user-id" content="{{ user.id }}">
{% endif %}
{# КРИТИЧНО: Применяем тему ДО загрузки CSS для предотвращения FOUC #}
<script src="{% static 'js/utils/themeInitializer.js' %}"></script>
<title>{% block title %}HiRo{% endblock %}</title>
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'img/favicon-32.png' %}">
<link rel="apple-touch-icon" href="{% static 'img/apple-touch-icon.png' %}">
<link rel="icon" type="image/png" sizes="192x192" href="{% static 'img/logo-192.png' %}">
{# Preload критичного CSS для ускорения загрузки #}
<link rel="preload" href="{% static 'css/bootstrap-custom.css' %}" as="style">
<link rel="preload" href="{% static 'css/app.css' %}" as="style">
<!-- Bootstrap / Icons -->
<!-- Кастомный Bootstrap с нашей системой отступов + все CSS переменные -->
<link rel="stylesheet" href="{% static 'css/bootstrap-custom.css' %}">
<link href="{% static 'vendor/bootstrap-icons/bootstrap-icons.min.css' %}" rel="stylesheet">
<!-- Главные стили приложения (скомпилированные из SCSS) -->
<link rel="stylesheet" href="{% static 'css/app.css' %}">
{% block extra_css %}{% endblock %}
</head>
<body class="min-vh-100">
{# Устанавливаем глобальные переменные в самом начале #}
{% if user.is_authenticated %}
<script>
window.currentUserId = {{ user.id }};
</script>
{% endif %}
{% include "includes/navbar.html" %}
<div class="container-xxl">
<div class="app-layout {% if user.is_authenticated %}with-sidebar with-rightbar{% endif %}">
{% if user.is_authenticated %}
{% include "includes/sidebar.html" %}
{% endif %}
<main class="main">
{% block content %}{% endblock %}
</main>
{% if user.is_authenticated %}
{% include "includes/rightbar_calendar.html" %}
{% endif %}
</div>
</div>
<!-- JS -->
<!-- Bootstrap JS (локальная версия) -->
<script src="{% static 'vendor/bootstrap/bootstrap.bundle.min.js' %}"></script>
<!-- FullCalendar (локальные версии) -->
<script src="{% static 'vendor/fullcalendar/fullcalendar.min.js' %}" defer></script>
<script src="{% static 'vendor/fullcalendar/locales-all.min.js' %}" defer></script>
{# Универсальный модуль для автоматического расширения textarea #}
<script src="{% static 'js/components/textareaAutoExpand.js' %}" defer></script>
{# Импорт утилит из модулей #}
<script type="module">
import { esc, norm, escAttr, truncate, getInitials } from '{% static "js/utils/stringUtils.js" %}';
import { debounce, throttle, delay } from '{% static "js/utils/timing.js" %}';
import { smoothScrollTo, scrollToTop, isElementInViewport } from '{% static "js/utils/scroll.js" %}';
import { initNavbarHeight } from '{% static "js/components/navbarHeight.js" %}';
import { initLikeHandler } from '{% static "js/components/likeHandler.js" %}';
// Экспортируем в window для обратной совместимости с существующим кодом
window.esc = esc;
window.norm = norm;
window.escAttr = escAttr;
window.truncate = truncate;
window.getInitials = getInitials;
window.debounce = debounce;
window.throttle = throttle;
window.delay = delay;
window.smoothScrollTo = smoothScrollTo;
window.scrollToTop = scrollToTop;
window.isElementInViewport = isElementInViewport;
// Инициализация компонентов
document.addEventListener('DOMContentLoaded', () => {
const navbarHeight = initNavbarHeight();
const likeHandler = initLikeHandler();
if (navbarHeight) window.navbarHeight = navbarHeight;
if (likeHandler) window.likeHandler = likeHandler;
});
</script>
{# ========== UNIVERSAL USER WEBSOCKET ========== #}
{# Универсальное WebSocket-подключение для всех real-time обновлений #}
{% if user.is_authenticated %}
<script type="module">
import { initUserWebSocket } from "{% static 'js/components/userWebSocket.js' %}";
// Базовая инициализация WebSocket (без chat-специфичной логики)
const userWs = initUserWebSocket({
userId: {{ user.id }}
});
// Экспортируем в window для доступа из других модулей
if (userWs) {
window.userWebSocket = userWs;
window.dispatchEvent(new CustomEvent('user:ws-ready', {
detail: { api: userWs, ws: userWs.ws }
}));
}
</script>
<!-- Notification Manager -->
<script type="module" src="{% static 'js/notifications/notification-manager.js' %}"></script>
<!-- Web Push Notifications -->
<script type="module">
import { pushNotifications } from "{% static 'js/notifications/push-notifications.js' %}";
// Инициализация Web Push после загрузки страницы
document.addEventListener('DOMContentLoaded', async () => {
const initialized = await pushNotifications.init();
if (initialized) {
console.log('[WebPush] Инициализирован, подписка:', pushNotifications.isSubscribed);
// Если разрешение еще не спрошено - запрашиваем его
if (pushNotifications.getPermissionStatus() === 'default' && !pushNotifications.isSubscribed) {
console.log('[WebPush] Запрашиваем разрешение на уведомления...');
// Небольшая задержка, чтобы пользователь успел сориентироваться
setTimeout(async () => {
const permission = await pushNotifications.requestPermission();
if (permission === 'granted') {
console.log('[WebPush] Разрешение получено, подписываемся...');
await pushNotifications.subscribe();
} else {
console.log('[WebPush] Разрешение не получено');
}
}, 2000);
}
// Если разрешение дано, но нет подписки - подписываем автоматически
if (pushNotifications.getPermissionStatus() === 'granted' && !pushNotifications.isSubscribed) {
console.log('[WebPush] Разрешение есть, подписываемся...');
await pushNotifications.subscribe();
}
}
});
</script>
{% endif %}
{% block extra_js %}{% endblock %}
</body>
</html>