Files
yarmarka/templates/company_detail.html
2026-03-17 20:01:50 +03:00

1645 lines
53 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.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Компания | Rabota.Today</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<!-- Подключаем библиотеку для генерации QR-кодов -->
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
body {
background: linear-gradient(145deg, #eef5fa 0%, #e0eaf5 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* Шапка */
.header {
background: #0b1c34;
color: white;
padding: 20px 40px;
border-radius: 40px;
margin-bottom: 40px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 20px;
}
.logo {
font-size: 28px;
font-weight: 700;
display: flex;
align-items: center;
gap: 15px;
cursor: pointer;
}
.logo i {
color: #3b82f6;
background: rgba(255,255,255,0.1);
padding: 12px;
border-radius: 20px;
}
.nav {
display: flex;
gap: 15px;
align-items: center;
flex-wrap: wrap;
}
.nav a {
color: white;
text-decoration: none;
padding: 10px 20px;
border-radius: 30px;
transition: 0.2s;
}
.nav a:hover {
background: rgba(255,255,255,0.1);
}
.nav .active {
background: #3b82f6;
}
.profile-link {
display: flex;
align-items: center;
gap: 8px;
background: #3b82f6;
padding: 8px 20px !important;
}
.admin-badge {
background: #f59e0b;
color: white;
padding: 2px 8px;
border-radius: 20px;
font-size: 12px;
margin-left: 5px;
}
.back-link {
display: inline-block;
margin-bottom: 20px;
color: #4f7092;
text-decoration: none;
font-size: 16px;
transition: 0.2s;
}
.back-link i {
margin-right: 8px;
}
.back-link:hover {
color: #0b1c34;
}
/* Шапка компании с QR */
.company-header {
background: white;
border-radius: 40px;
padding: 40px;
margin-bottom: 30px;
box-shadow: 0 20px 40px rgba(0,20,40,0.1);
display: flex;
gap: 40px;
flex-wrap: wrap;
align-items: center;
position: relative;
}
.company-logo {
width: 120px;
height: 120px;
background: #eef4fa;
border-radius: 30px;
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
color: #3b82f6;
flex-shrink: 0;
overflow: hidden;
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
.company-logo img {
width: 100%;
height: 100%;
object-fit: cover;
}
.company-info {
flex: 1;
}
.company-info-header {
display: flex;
align-items: center;
gap: 20px;
flex-wrap: wrap;
margin-bottom: 10px;
}
.company-name {
font-size: 36px;
font-weight: 700;
color: #0b1c34;
}
/* Стили для QR-кода в шапке */
.qr-button {
width: 50px;
height: 50px;
background: linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%);
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 12px rgba(59,130,246,0.3);
color: white;
font-size: 24px;
position: relative;
overflow: hidden;
border: none;
margin-left: auto;
}
.qr-button:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(59,130,246,0.4);
}
.qr-button:active {
transform: scale(0.95);
}
.qr-button i {
font-size: 24px;
}
.qr-view-count {
position: absolute;
top: -8px;
right: -8px;
background: #ef4444;
color: white;
font-size: 11px;
font-weight: 600;
padding: 2px 6px;
border-radius: 20px;
min-width: 20px;
text-align: center;
box-shadow: 0 2px 4px rgba(239,68,68,0.3);
}
.company-meta {
display: flex;
gap: 20px;
color: #4f7092;
font-size: 16px;
flex-wrap: wrap;
}
.company-meta i {
color: #3b82f6;
margin-right: 5px;
}
.company-website {
display: inline-block;
margin-top: 15px;
color: #3b82f6;
text-decoration: none;
font-weight: 600;
transition: 0.2s;
}
.company-website:hover {
text-decoration: underline;
}
/* Модальное окно для QR */
.qr-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.6);
align-items: center;
justify-content: center;
z-index: 1000;
backdrop-filter: blur(5px);
}
.qr-modal.active {
display: flex;
animation: fadeIn 0.3s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.qr-modal-content {
background: white;
border-radius: 40px;
padding: 40px;
max-width: 500px;
width: 90%;
position: relative;
box-shadow: 0 30px 60px rgba(0,0,0,0.3);
animation: slideUp 0.3s;
}
@keyframes slideUp {
from { transform: translateY(30px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.qr-modal-close {
position: absolute;
top: 20px;
right: 20px;
width: 40px;
height: 40px;
border-radius: 20px;
background: #eef4fa;
border: none;
font-size: 24px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: #4f7092;
transition: 0.2s;
}
.qr-modal-close:hover {
background: #dbeafe;
color: #0b1c34;
}
.qr-modal h2 {
color: #0b1c34;
margin-bottom: 10px;
font-size: 28px;
display: flex;
align-items: center;
gap: 10px;
word-break: break-word;
}
.qr-modal h2 i {
color: #3b82f6;
}
.qr-subtitle {
color: #4f7092;
margin-bottom: 25px;
font-size: 16px;
word-break: break-all;
}
.qr-container {
display: flex;
justify-content: center;
margin: 30px 0;
padding: 20px;
background: #f9fcff;
border-radius: 30px;
position: relative;
}
#qrCanvas {
width: 250px;
height: 250px;
image-rendering: crisp-edges;
display: block;
}
.qr-logo-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 60px;
height: 60px;
background: white;
border-radius: 15px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 10px rgba(0,0,0,0.2);
border: 3px solid white;
pointer-events: none;
}
.qr-logo-overlay img {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 12px;
}
.qr-logo-overlay i {
font-size: 30px;
color: #3b82f6;
}
.qr-stats {
display: flex;
justify-content: space-around;
margin: 20px 0;
padding: 15px;
background: #f0f7ff;
border-radius: 20px;
}
.qr-stat-item {
text-align: center;
}
.qr-stat-value {
font-size: 20px;
font-weight: 700;
color: #0b1c34;
}
.qr-stat-label {
font-size: 12px;
color: #4f7092;
}
.qr-actions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px;
margin: 25px 0;
}
.qr-action-btn {
padding: 14px;
border-radius: 30px;
border: none;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: 0.2s;
font-size: 14px;
}
.qr-action-btn.primary {
background: #0b1c34;
color: white;
}
.qr-action-btn.primary:hover {
background: #1b3f6b;
}
.qr-action-btn.secondary {
background: #eef4fa;
color: #1f3f60;
}
.qr-action-btn.secondary:hover {
background: #dbeafe;
}
.qr-action-btn i {
font-size: 16px;
}
.qr-share-buttons {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #dee9f5;
}
.qr-share-btn {
width: 44px;
height: 44px;
border-radius: 22px;
border: none;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
cursor: pointer;
transition: 0.2s;
}
.qr-share-btn.whatsapp {
background: #25D366;
color: white;
}
.qr-share-btn.telegram {
background: #0088cc;
color: white;
}
.qr-share-btn.email {
background: #ea4335;
color: white;
}
.qr-share-btn:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
/* Статистика компании */
.company-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
border-radius: 30px;
padding: 25px;
text-align: center;
box-shadow: 0 10px 20px rgba(0,20,40,0.05);
transition: 0.2s;
cursor: pointer;
}
.stat-card:hover {
transform: translateY(-3px);
box-shadow: 0 15px 30px rgba(0,20,40,0.1);
}
.stat-card i {
font-size: 32px;
color: #3b82f6;
margin-bottom: 10px;
}
.stat-value {
font-size: 28px;
font-weight: 700;
color: #0b1c34;
}
.stat-label {
color: #4f7092;
font-size: 14px;
}
/* Описание компании */
.company-description {
background: white;
border-radius: 40px;
padding: 40px;
margin-bottom: 30px;
box-shadow: 0 20px 40px rgba(0,20,40,0.1);
}
.section-title {
font-size: 24px;
color: #0b1c34;
margin-bottom: 20px;
font-weight: 600;
display: flex;
align-items: center;
gap: 10px;
}
.section-title i {
color: #3b82f6;
}
.description-text {
line-height: 1.8;
color: #1f3f60;
font-size: 16px;
white-space: pre-line;
}
/* Контакты */
.company-contacts {
background: white;
border-radius: 40px;
padding: 40px;
margin-bottom: 30px;
box-shadow: 0 20px 40px rgba(0,20,40,0.1);
}
.contacts-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.contact-item {
display: flex;
align-items: center;
gap: 15px;
padding: 15px;
background: #f9fcff;
border-radius: 20px;
transition: 0.2s;
}
.contact-item:hover {
background: #eef4fa;
transform: translateX(5px);
}
.contact-item i {
width: 40px;
height: 40px;
background: #eef4fa;
border-radius: 20px;
display: flex;
align-items: center;
justify-content: center;
color: #3b82f6;
font-size: 18px;
}
.contact-info {
flex: 1;
}
.contact-label {
font-size: 12px;
color: #4f7092;
margin-bottom: 4px;
}
.contact-value {
font-weight: 600;
color: #0b1c34;
word-break: break-word;
}
.contact-value a {
color: #3b82f6;
text-decoration: none;
}
.contact-value a:hover {
text-decoration: underline;
}
/* Вакансии компании */
.company-vacancies {
background: white;
border-radius: 40px;
padding: 40px;
margin-bottom: 30px;
box-shadow: 0 20px 40px rgba(0,20,40,0.1);
}
.vacancies-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
flex-wrap: wrap;
gap: 20px;
}
.vacancies-header h2 {
font-size: 24px;
color: #0b1c34;
display: flex;
align-items: center;
gap: 10px;
}
.vacancies-header h2 i {
color: #3b82f6;
}
.vacancies-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
}
.vacancy-card {
background: #f9fcff;
border: 1px solid #dee9f5;
border-radius: 30px;
padding: 25px;
transition: 0.2s;
cursor: pointer;
position: relative;
}
.vacancy-card:hover {
background: white;
box-shadow: 0 10px 30px rgba(0,20,40,0.1);
transform: translateY(-3px);
}
.vacancy-card h3 {
color: #0b1c34;
margin-bottom: 10px;
font-size: 18px;
}
.vacancy-salary {
color: #3b82f6;
font-weight: 700;
font-size: 18px;
margin: 10px 0;
}
.vacancy-tags {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin: 10px 0;
}
.tag {
background: #eef4fa;
padding: 4px 10px;
border-radius: 20px;
font-size: 12px;
color: #1f3f60;
}
.vacancy-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 15px;
font-size: 14px;
color: #4f7092;
}
/* Загрузка */
.loading {
text-align: center;
padding: 60px;
color: #4f7092;
font-size: 18px;
}
/* Ошибка */
.error-message {
background: #fee2e2;
color: #b91c1c;
padding: 20px;
border-radius: 30px;
text-align: center;
margin: 40px 0;
}
/* Уведомления */
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 16px 24px;
border-radius: 30px;
background: white;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
z-index: 9999;
animation: slideIn 0.3s;
max-width: 350px;
display: none;
}
.notification.success {
background: #10b981;
color: white;
}
.notification.error {
background: #ef4444;
color: white;
}
.notification.info {
background: #3b82f6;
color: white;
}
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0%); opacity: 1; }
}
/* Адаптивность */
@media (max-width: 768px) {
.company-header {
flex-direction: column;
text-align: center;
}
.company-meta {
justify-content: center;
}
.contacts-grid {
grid-template-columns: 1fr;
}
.qr-button {
margin: 0 auto;
}
.company-info-header {
justify-content: center;
}
}
/* Улучшенные стили для модального окна с прокруткой */
.qr-modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
align-items: flex-start; /* Изменено с center на flex-start */
justify-content: center;
z-index: 1000;
backdrop-filter: blur(5px);
overflow-y: auto; /* Добавляем прокрутку для всего модального окна */
padding: 20px 0; /* Отступы сверху и снизу */
}
.qr-modal.active {
display: flex;
animation: fadeIn 0.3s;
}
.qr-modal-content {
background: white;
border-radius: 40px;
padding: 40px;
max-width: 500px;
width: 90%;
position: relative;
box-shadow: 0 30px 60px rgba(0,0,0,0.3);
animation: slideUp 0.3s;
margin: auto; /* Центрирование по вертикали при возможности */
max-height: 90vh; /* Ограничиваем высоту */
overflow-y: auto; /* Внутренняя прокрутка если контент не помещается */
}
/* Улучшенная кнопка закрытия - всегда видима */
.qr-modal-close {
position: sticky; /* Меняем с absolute на sticky */
top: 0;
right: 0;
margin-left: auto;
margin-bottom: 20px;
width: 40px;
height: 40px;
border-radius: 20px;
background: #eef4fa;
border: none;
font-size: 24px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: #4f7092;
transition: 0.2s;
z-index: 10;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
.qr-modal-close:hover {
background: #dbeafe;
color: #0b1c34;
transform: scale(1.1);
}
/* Фиксированная кнопка закрытия для мобильных */
@media (max-width: 768px) {
.qr-modal-content {
padding: 30px 20px;
width: 95%;
}
.qr-modal-close {
position: fixed;
top: 10px;
right: 10px;
background: white;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
}
/* Заголовок с фиксацией */
.qr-modal h2 {
color: #0b1c34;
margin-bottom: 10px;
font-size: 24px;
display: flex;
align-items: center;
gap: 10px;
word-break: break-word;
padding-right: 40px; /* Место для кнопки закрытия */
position: sticky;
top: 0;
background: white;
padding-top: 0;
z-index: 5;
}
/* Контейнер для QR с центрированием */
.qr-container {
display: flex;
justify-content: center;
margin: 20px 0;
padding: 20px;
background: #f9fcff;
border-radius: 30px;
position: relative;
}
/* Статистика */
.qr-stats {
display: flex;
justify-content: space-around;
margin: 20px 0;
padding: 15px;
background: #f0f7ff;
border-radius: 20px;
flex-wrap: wrap;
gap: 10px;
}
.qr-stat-item {
text-align: center;
min-width: 80px;
}
.qr-stat-value {
font-size: 20px;
font-weight: 700;
color: #0b1c34;
}
.qr-stat-label {
font-size: 12px;
color: #4f7092;
}
/* Сетка действий */
.qr-actions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px;
margin: 20px 0;
}
.qr-action-btn {
padding: 14px;
border-radius: 30px;
border: none;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: 0.2s;
font-size: 14px;
}
.qr-action-btn.primary {
background: #0b1c34;
color: white;
}
.qr-action-btn.primary:hover {
background: #1b3f6b;
}
.qr-action-btn.secondary {
background: #eef4fa;
color: #1f3f60;
}
.qr-action-btn.secondary:hover {
background: #dbeafe;
}
.qr-action-btn i {
font-size: 16px;
}
/* Кнопки шеринга */
.qr-share-buttons {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #dee9f5;
flex-wrap: wrap;
}
.qr-share-btn {
width: 44px;
height: 44px;
border-radius: 22px;
border: none;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
cursor: pointer;
transition: 0.2s;
}
.qr-share-btn.whatsapp {
background: #25D366;
color: white;
}
.qr-share-btn.telegram {
background: #0088cc;
color: white;
}
.qr-share-btn.email {
background: #ea4335;
color: white;
}
.qr-share-btn:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
/* Анимации */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from { transform: translateY(30px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
</style>
</head>
<body>
<div class="container">
<!-- Шапка -->
<div class="header">
<div class="logo" onclick="window.location.href='/'">
<i class="fas fa-briefcase"></i>
МП.Ярмарка
</div>
<div class="nav" id="nav">
<!-- Навигация будет заполнена динамически -->
</div>
</div>
<a href="javascript:history.back()" class="back-link"><i class="fas fa-arrow-left"></i> Назад</a>
<!-- Контент компании -->
<div id="companyContent">
<div class="loading">Загрузка информации о компании...</div>
</div>
</div>
<!-- Модальное окно для QR-кода -->
<div class="qr-modal" id="qrModal">
<div class="qr-modal-content">
<button class="qr-modal-close" onclick="closeQRModal()">&times;</button>
<h2><i class="fas fa-qrcode"></i> <span id="qrCompanyName"></span></h2>
<div class="qr-subtitle" id="qrCompanyUrl">yarmarka.rabota.today/company/</div>
<div class="qr-container" id="qrContainer">
<canvas id="qrCanvas" width="250" height="250"></canvas>
<div class="qr-logo-overlay" id="qrLogoOverlay" style="display: none;">
<img id="qrLogoImg" src="" alt="logo" style="display: none;">
<i id="qrLogoIcon" class="fas fa-building"></i>
</div>
</div>
<div class="qr-stats">
<div class="qr-stat-item">
<div class="qr-stat-value" id="qrViewCount">0</div>
<div class="qr-stat-label">просмотров</div>
</div>
<div class="qr-stat-item">
<div class="qr-stat-value" id="qrVacancyCount">0</div>
<div class="qr-stat-label">вакансий</div>
</div>
<div class="qr-stat-item">
<div class="qr-stat-value" id="qrFavoriteCount">0</div>
<div class="qr-stat-label">в избранном</div>
</div>
</div>
<div class="qr-actions">
<button class="qr-action-btn primary" onclick="downloadQR()">
<i class="fas fa-download"></i> Скачать PNG
</button>
<button class="qr-action-btn secondary" onclick="copyCompanyLink()">
<i class="fas fa-link"></i> Копировать ссылку
</button>
<button class="qr-action-btn secondary" onclick="saveToContacts()">
<i class="fas fa-address-card"></i> В контакты
</button>
<button class="qr-action-btn secondary" onclick="printQR()">
<i class="fas fa-print"></i> Распечатать
</button>
</div>
<div class="qr-share-buttons">
<button class="qr-share-btn whatsapp" onclick="shareQR('whatsapp')">
<i class="fab fa-whatsapp"></i>
</button>
<button class="qr-share-btn telegram" onclick="shareQR('telegram')">
<i class="fab fa-telegram"></i>
</button>
<button class="qr-share-btn email" onclick="shareQR('email')">
<i class="fas fa-envelope"></i>
</button>
</div>
</div>
</div>
<!-- Уведомления -->
<div class="notification" id="notification"></div>
<script>
const API_BASE_URL = window.location.protocol + '//' + window.location.host + '/api';
let currentUser = null;
let currentCompany = null;
let qrViewCount = 0;
// Получаем ID компании из URL
const pathParts = window.location.pathname.split('/');
const companyId = pathParts[pathParts.length - 1];
// Функция для декодирования HTML-сущностей
function decodeHtmlEntities(text) {
if (!text) return '';
const textarea = document.createElement('textarea');
textarea.innerHTML = text;
return textarea.value;
}
// Функция для экранирования HTML (для безопасного отображения)
function escapeHtml(unsafe) {
if (!unsafe) return '';
return unsafe.toString()
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
// Проверка авторизации
async function checkAuth() {
const token = localStorage.getItem('accessToken');
if (token) {
try {
const response = await fetch(`${API_BASE_URL}/user`, {
headers: { 'Authorization': `Bearer ${token}` }
});
if (response.ok) {
currentUser = await response.json();
} else {
localStorage.removeItem('accessToken');
}
} catch (error) {
console.error('Error checking auth:', error);
}
}
updateNavigation();
}
// Обновление навигации
function updateNavigation() {
const nav = document.getElementById('nav');
if (currentUser) {
const firstName = currentUser.full_name.split(' ')[0];
const adminBadge = currentUser.is_admin ? '<span class="admin-badge">Admin</span>' : '';
nav.innerHTML = `
<a href="/">Главная</a>
<a href="/vacancies">Вакансии</a>
<a href="/resumes">Резюме</a>
<a href="/favorites">Избранное</a>
<a href="/applications">Отклики</a>
<a href="/profile" class="profile-link">
<i class="fas fa-user-circle"></i> ${escapeHtml(firstName)} ${adminBadge}
</a>
`;
} else {
nav.innerHTML = `
<a href="/">Главная</a>
<a href="/vacancies">Вакансии</a>
<a href="/resumes">Резюме</a>
<a href="/login">Войти</a>
<a href="/register">Регистрация</a>
`;
}
}
// Загрузка информации о компании
async function loadCompany() {
try {
console.log('📥 Загрузка компании ID:', companyId);
const response = await fetch(`${API_BASE_URL}/companies/${companyId}`);
if (!response.ok) {
throw new Error('Компания не найдена');
}
currentCompany = await response.json();
console.log('✅ Компания загружена:', currentCompany);
// Загружаем вакансии компании
const vacanciesResponse = await fetch(`${API_BASE_URL}/vacancies/all?company_id=${companyId}`);
const vacanciesData = vacanciesResponse.ok ? await vacanciesResponse.json() : { vacancies: [] };
renderCompany(currentCompany, vacanciesData.vacancies || []);
} catch (error) {
console.error('❌ Ошибка загрузки компании:', error);
document.getElementById('companyContent').innerHTML = `
<div class="error-message">
<i class="fas fa-exclamation-circle"></i>
Компания не найдена или была удалена
</div>
<div style="text-align: center; margin-top: 20px;">
<a href="/" class="btn btn-primary">На главную</a>
</div>
`;
}
}
// Отображение компании с QR-кнопкой
function renderCompany(company, vacancies) {
const container = document.getElementById('companyContent');
const createdDate = new Date(company.created_at).toLocaleDateString('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
const activeVacancies = vacancies.filter(v => v.is_active).length;
// Декодируем название компании для отображения
const decodedName = decodeHtmlEntities(company.name);
container.innerHTML = `
<!-- Шапка компании с QR -->
<div class="company-header">
<div class="company-logo">
${company.logo ?
`<img src="${escapeHtml(company.logo)}" alt="${escapeHtml(decodedName)}">` :
`<i class="fas fa-building"></i>`
}
</div>
<div class="company-info">
<div class="company-info-header">
<h1 class="company-name">${escapeHtml(decodedName)}</h1>
<button class="qr-button" onclick="openQRModal()" title="QR-код компании">
<i class="fas fa-qrcode"></i>
<span class="qr-view-count" id="qrViewBadge">0</span>
</button>
</div>
<div class="company-meta">
<span><i class="fas fa-calendar"></i> На рынке с ${createdDate}</span>
${company.website ?
`<span><i class="fas fa-globe"></i> <a href="${escapeHtml(company.website)}" target="_blank">${escapeHtml(company.website)}</a></span>` :
''
}
</div>
</div>
</div>
<!-- Статистика компании -->
<div class="company-stats">
<div class="stat-card" onclick="window.location.href='#vacancies'">
<i class="fas fa-briefcase"></i>
<div class="stat-value">${vacancies.length}</div>
<div class="stat-label">Всего вакансий</div>
</div>
<div class="stat-card" onclick="window.location.href='#vacancies'">
<i class="fas fa-check-circle"></i>
<div class="stat-value">${activeVacancies}</div>
<div class="stat-label">Активных</div>
</div>
<div class="stat-card">
<i class="fas fa-eye"></i>
<div class="stat-value">${vacancies.reduce((sum, v) => sum + (v.views || 0), 0)}</div>
<div class="stat-label">Просмотров</div>
</div>
</div>
<!-- Описание компании -->
${company.description ? `
<div class="company-description">
<h2 class="section-title">
<i class="fas fa-info-circle"></i> О компании
</h2>
<div class="description-text">${escapeHtml(company.description).replace(/\n/g, '<br>')}</div>
</div>
` : ''}
<!-- Контактная информация -->
<div class="company-contacts">
<h2 class="section-title">
<i class="fas fa-address-card"></i> Контактная информация
</h2>
<div class="contacts-grid">
${company.email ? `
<div class="contact-item">
<i class="fas fa-envelope"></i>
<div class="contact-info">
<div class="contact-label">Email</div>
<div class="contact-value">
<a href="mailto:${escapeHtml(company.email)}">${escapeHtml(company.email)}</a>
</div>
</div>
</div>
` : ''}
${company.phone ? `
<div class="contact-item">
<i class="fas fa-phone"></i>
<div class="contact-info">
<div class="contact-label">Телефон</div>
<div class="contact-value">
<a href="tel:${escapeHtml(company.phone)}">${escapeHtml(company.phone)}</a>
</div>
</div>
</div>
` : ''}
${company.address ? `
<div class="contact-item">
<i class="fas fa-map-marker-alt"></i>
<div class="contact-info">
<div class="contact-label">Адрес</div>
<div class="contact-value">${escapeHtml(company.address)}</div>
</div>
</div>
` : ''}
${company.website ? `
<div class="contact-item">
<i class="fas fa-globe"></i>
<div class="contact-info">
<div class="contact-label">Сайт</div>
<div class="contact-value">
<a href="${escapeHtml(company.website)}" target="_blank">${escapeHtml(company.website)}</a>
</div>
</div>
</div>
` : ''}
</div>
</div>
<!-- Вакансии компании -->
<div class="company-vacancies" id="vacancies">
<div class="vacancies-header">
<h2>
<i class="fas fa-briefcase"></i> Вакансии компании
</h2>
<span style="color: #4f7092;">${vacancies.length} вакансий</span>
</div>
${vacancies.length > 0 ? `
<div class="vacancies-grid">
${vacancies.map(v => `
<div class="vacancy-card" onclick="window.location.href='/vacancy/${v.id}'">
<h3>${escapeHtml(v.title)}</h3>
<div class="vacancy-salary">${escapeHtml(v.salary || 'з/п не указана')}</div>
<div class="vacancy-tags">
${(v.tags || []).map(t => `<span class="tag">${escapeHtml(t.name)}</span>`).join('')}
</div>
<div class="vacancy-footer">
<span><i class="fas fa-eye"></i> ${v.views || 0}</span>
<span><i class="fas fa-calendar"></i> ${new Date(v.created_at).toLocaleDateString()}</span>
</div>
</div>
`).join('')}
</div>
` : `
<p style="text-align: center; color: #4f7092; padding: 40px;">
У компании пока нет активных вакансий
</p>
`}
</div>
`;
}
// ========== ФУНКЦИИ ДЛЯ QR-КОДА ==========
// Открыть модальное окно с QR
function openQRModal() {
if (!currentCompany) return;
// Декодируем название компании
const decodedName = decodeHtmlEntities(currentCompany.name);
document.getElementById('qrCompanyName').textContent = decodedName;
const companyUrl = window.location.origin + '/company/' + companyId;
document.getElementById('qrCompanyUrl').textContent = companyUrl.replace('https://', '').replace('http://', '');
// Обновляем статистику
document.getElementById('qrViewCount').textContent = ++qrViewCount;
document.getElementById('qrViewBadge').textContent = qrViewCount;
// Загружаем количество вакансий
fetch(`${API_BASE_URL}/vacancies/all?company_id=${companyId}&limit=1`)
.then(r => r.json())
.then(data => {
document.getElementById('qrVacancyCount').textContent = data.total || 0;
});
// Генерируем QR-код с логотипом
generateQRCodeWithLogo(companyUrl, currentCompany.logo);
document.getElementById('qrModal').classList.add('active');
}
// Закрыть модальное окно
function closeQRModal() {
document.getElementById('qrModal').classList.remove('active');
}
// Генерация QR-кода с логотипом
function generateQRCodeWithLogo(text, logoUrl) {
const canvas = document.getElementById('qrCanvas');
const logoOverlay = document.getElementById('qrLogoOverlay');
const logoImg = document.getElementById('qrLogoImg');
const logoIcon = document.getElementById('qrLogoIcon');
// Настройки QR-кода
const options = {
width: 250,
height: 250,
color: {
dark: '#0b1c34',
light: '#ffffff'
},
errorCorrectionLevel: 'H' // высокая коррекция ошибок для логотипа
};
QRCode.toCanvas(canvas, text, options, function(error) {
if (error) {
console.error('Error generating QR code:', error);
showNotification('Ошибка генерации QR-кода', 'error');
} else {
console.log('QR code generated successfully');
// Показываем оверлей с логотипом или иконкой
logoOverlay.style.display = 'flex';
if (logoUrl) {
// Если есть логотип, показываем его
logoImg.src = logoUrl;
logoImg.style.display = 'block';
logoIcon.style.display = 'none';
// Обработка ошибки загрузки логотипа
logoImg.onerror = function() {
logoImg.style.display = 'none';
logoIcon.style.display = 'block';
};
} else {
// Если нет логотипа, показываем иконку
logoImg.style.display = 'none';
logoIcon.style.display = 'block';
}
}
});
}
// Скачать QR-код
function downloadQR() {
const canvas = document.getElementById('qrCanvas');
const logoOverlay = document.getElementById('qrLogoOverlay');
// Создаем временный canvas для объединения QR и логотипа
const combinedCanvas = document.createElement('canvas');
combinedCanvas.width = canvas.width;
combinedCanvas.height = canvas.height;
const ctx = combinedCanvas.getContext('2d');
// Рисуем QR-код
ctx.drawImage(canvas, 0, 0);
// Рисуем логотип поверх
const logoImg = document.getElementById('qrLogoImg');
const logoIcon = document.getElementById('qrLogoIcon');
if (logoImg.style.display === 'block' && logoImg.complete) {
// Если есть загруженный логотип
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(125, 125, 35, 0, 2 * Math.PI);
ctx.fill();
ctx.save();
ctx.beginPath();
ctx.arc(125, 125, 30, 0, 2 * Math.PI);
ctx.clip();
const logoSize = 50;
ctx.drawImage(logoImg, 125 - logoSize/2, 125 - logoSize/2, logoSize, logoSize);
ctx.restore();
} else {
// Если нет логотипа, рисуем иконку
ctx.fillStyle = 'white';
ctx.beginPath();
ctx.arc(125, 125, 30, 0, 2 * Math.PI);
ctx.fill();
ctx.fillStyle = '#3b82f6';
ctx.font = '30px "Font Awesome 6 Free"';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('', 125, 125); // Unicode для иконки building
}
// Скачиваем
const link = document.createElement('a');
const decodedName = decodeHtmlEntities(currentCompany.name);
const filename = `qr_${decodedName.toLowerCase().replace(/[^a-zа-я0-9]/g, '_')}.png`;
link.download = filename;
link.href = combinedCanvas.toDataURL('image/png');
link.click();
showNotification('QR-код скачан', 'success');
}
// Копировать ссылку на компанию
function copyCompanyLink() {
const url = window.location.origin + '/company/' + companyId;
navigator.clipboard.writeText(url).then(() => {
showNotification('Ссылка скопирована', 'success');
}).catch(() => {
showNotification('Ошибка копирования', 'error');
});
}
// Сохранить в контакты (vCard)
function saveToContacts() {
if (!currentCompany) return;
const decodedName = decodeHtmlEntities(currentCompany.name);
// Создаем vCard
const vCard = `BEGIN:VCARD
VERSION:3.0
FN:${decodedName}
ORG:${decodedName}
${currentCompany.phone ? `TEL:${currentCompany.phone}` : ''}
${currentCompany.email ? `EMAIL:${currentCompany.email}` : ''}
${currentCompany.website ? `URL:${currentCompany.website}` : ''}
${currentCompany.address ? `ADR:${currentCompany.address}` : ''}
NOTE:${currentCompany.description ? currentCompany.description.substring(0, 100) + '...' : ''}
END:VCARD`;
const blob = new Blob([vCard], { type: 'text/vcard' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${decodedName}.vcf`;
link.click();
URL.revokeObjectURL(url);
showNotification('Контакт сохранен', 'success');
}
// Распечатать QR
function printQR() {
const canvas = document.getElementById('qrCanvas');
const decodedName = decodeHtmlEntities(currentCompany.name);
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<html>
<head>
<title>QR-код ${decodedName}</title>
<style>
body { display: flex; justify-content: center; align-items: center; height: 100vh; flex-direction: column; font-family: Arial; margin: 0; padding: 20px; }
h2 { color: #0b1c34; text-align: center; word-break: break-word; }
img { max-width: 300px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); border-radius: 20px; }
.url { color: #4f7092; margin-top: 20px; word-break: break-all; text-align: center; }
.logo { width: 100px; height: 100px; margin-bottom: 20px; }
</style>
</head>
<body>
${currentCompany.logo ? `<img src="${escapeHtml(currentCompany.logo)}" class="logo" onerror="this.style.display='none'">` : ''}
<h2>${escapeHtml(decodedName)}</h2>
<img src="${canvas.toDataURL()}" />
<div class="url">${window.location.origin}/company/${companyId}</div>
</body>
</html>
`);
printWindow.document.close();
printWindow.focus();
printWindow.print();
}
// Поделиться QR
function shareQR(platform) {
const url = window.location.origin + '/company/' + companyId;
const decodedName = decodeHtmlEntities(currentCompany.name);
const text = `Вакансии компании ${decodedName} на Rabota.Today`;
let shareUrl = '';
switch(platform) {
case 'whatsapp':
shareUrl = `https://wa.me/?text=${encodeURIComponent(text + ' ' + url)}`;
break;
case 'telegram':
shareUrl = `https://t.me/share/url?url=${encodeURIComponent(url)}&text=${encodeURIComponent(text)}`;
break;
case 'email':
shareUrl = `mailto:?subject=${encodeURIComponent('Вакансии ' + decodedName)}&body=${encodeURIComponent(text + '\n\n' + url)}`;
break;
}
if (shareUrl) {
window.open(shareUrl, '_blank');
}
}
// Показать уведомление
function showNotification(message, type = 'info') {
const notification = document.getElementById('notification');
notification.className = `notification ${type}`;
notification.innerHTML = message;
notification.style.display = 'block';
setTimeout(() => {
notification.style.display = 'none';
}, 3000);
}
// Закрытие модального окна по клику вне
window.onclick = function(event) {
const modal = document.getElementById('qrModal');
if (event.target === modal) {
modal.classList.remove('active');
}
};
// Инициализация
checkAuth().then(() => {
loadCompany();
});
</script>
</body>
</html>