vacancy detail meta

This commit is contained in:
2026-03-20 15:28:19 +03:00
parent 8299165460
commit cbe517ad24

View File

@@ -3,11 +3,80 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Вакансия | Rabota.Today</title>
<!-- Базовые SEO теги -->
<title id="pageTitle">Вакансия | Rabota.Today</title>
<meta name="description" id="metaDescription" content="Подробная информация о вакансии на Rabota.Today. Зарплата, требования, условия работы, контакты работодателя.">
<meta name="keywords" content="вакансия, работа, поиск работы, Rabota.Today, трудоустройство">
<meta name="author" content="Rabota.Today">
<meta name="robots" content="index, follow">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" id="ogUrl" content="https://yarmarka.rabota.today/">
<meta property="og:title" id="ogTitle" content="Вакансия | Rabota.Today">
<meta property="og:description" id="ogDescription" content="Подробная информация о вакансии на Rabota.Today">
<meta property="og:image" id="ogImage" content="https://yarmarka.rabota.today/static/images/og-image.jpg">
<meta property="og:site_name" content="Rabota.Today">
<meta property="og:locale" content="ru_RU">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" id="twitterTitle" content="Вакансия | Rabota.Today">
<meta name="twitter:description" id="twitterDescription" content="Подробная информация о вакансии на Rabota.Today">
<meta name="twitter:image" id="twitterImage" content="https://yarmarka.rabota.today/static/images/og-image.jpg">
<!-- Canonical URL -->
<link rel="canonical" id="canonicalUrl" href="https://yarmarka.rabota.today/">
<!-- Дополнительные SEO метатеги -->
<meta name="format-detection" content="telephone=no">
<meta name="theme-color" content="#0b1c34">
<!-- Структурированные данные (JSON-LD) -->
<script type="application/ld+json" id="structuredData">
{
"@context": "https://schema.org",
"@type": "JobPosting",
"title": "",
"description": "",
"datePosted": "",
"validThrough": "",
"employmentType": "FULL_TIME",
"hiringOrganization": {
"@type": "Organization",
"name": "",
"sameAs": "",
"logo": "https://yarmarka.rabota.today/static/images/logo.png"
},
"jobLocation": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"addressLocality": "Москва",
"addressCountry": "RU"
}
},
"baseSalary": {
"@type": "MonetaryAmount",
"currency": "RUB",
"value": {
"@type": "QuantitativeValue",
"value": 0,
"unitText": "MONTH"
}
},
"employmentType": "FULL_TIME",
"workHours": "Полный день"
}
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<!-- Подключаем библиотеку для генерации QR-кодов --> <!-- Подключаем библиотеку для генерации QR-кодов -->
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js"></script>
<style> <style>
/* ... все существующие стили ... */
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
@@ -475,11 +544,13 @@
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0,0,0,0.6); background: rgba(0,0,0,0.7);
align-items: center; align-items: flex-start;
justify-content: center; justify-content: center;
z-index: 1000; z-index: 1000;
backdrop-filter: blur(5px); backdrop-filter: blur(5px);
overflow-y: auto;
padding: 20px 0;
} }
.qr-modal.active { .qr-modal.active {
@@ -487,11 +558,6 @@
animation: fadeIn 0.3s; animation: fadeIn 0.3s;
} }
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.qr-modal-content { .qr-modal-content {
background: white; background: white;
border-radius: 40px; border-radius: 40px;
@@ -501,17 +567,17 @@
position: relative; position: relative;
box-shadow: 0 30px 60px rgba(0,0,0,0.3); box-shadow: 0 30px 60px rgba(0,0,0,0.3);
animation: slideUp 0.3s; animation: slideUp 0.3s;
} margin: auto;
max-height: 90vh;
@keyframes slideUp { overflow-y: auto;
from { transform: translateY(30px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
} }
.qr-modal-close { .qr-modal-close {
position: absolute; position: sticky;
top: 20px; top: 0;
right: 20px; right: 0;
margin-left: auto;
margin-bottom: 20px;
width: 40px; width: 40px;
height: 40px; height: 40px;
border-radius: 20px; border-radius: 20px;
@@ -524,21 +590,30 @@
justify-content: center; justify-content: center;
color: #4f7092; color: #4f7092;
transition: 0.2s; transition: 0.2s;
z-index: 10;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
} }
.qr-modal-close:hover { .qr-modal-close:hover {
background: #dbeafe; background: #dbeafe;
color: #0b1c34; color: #0b1c34;
transform: scale(1.1);
} }
.qr-modal h2 { .qr-modal h2 {
color: #0b1c34; color: #0b1c34;
margin-bottom: 10px; margin-bottom: 10px;
font-size: 28px; font-size: 24px;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
word-break: break-word; word-break: break-word;
padding-right: 40px;
position: sticky;
top: 0;
background: white;
padding-top: 0;
z-index: 5;
} }
.qr-modal h2 i { .qr-modal h2 i {
@@ -605,10 +680,13 @@
padding: 15px; padding: 15px;
background: #f0f7ff; background: #f0f7ff;
border-radius: 20px; border-radius: 20px;
flex-wrap: wrap;
gap: 10px;
} }
.qr-stat-item { .qr-stat-item {
text-align: center; text-align: center;
min-width: 80px;
} }
.qr-stat-value { .qr-stat-value {
@@ -626,7 +704,7 @@
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 12px; gap: 12px;
margin: 25px 0; margin: 20px 0;
} }
.qr-action-btn { .qr-action-btn {
@@ -672,6 +750,7 @@
margin-top: 20px; margin-top: 20px;
padding-top: 20px; padding-top: 20px;
border-top: 1px solid #dee9f5; border-top: 1px solid #dee9f5;
flex-wrap: wrap;
} }
.qr-share-btn { .qr-share-btn {
@@ -757,73 +836,25 @@
to { transform: translateX(0%); opacity: 1; } to { transform: translateX(0%); opacity: 1; }
} }
/* Улучшенные стили для модального окна с прокруткой */ @keyframes fadeIn {
.qr-modal { from { opacity: 0; }
display: none; to { opacity: 1; }
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 { @keyframes slideUp {
display: flex; from { transform: translateY(30px); opacity: 0; }
animation: fadeIn 0.3s; to { transform: translateY(0); 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;
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) { @media (max-width: 768px) {
.vacancy-title {
font-size: 28px;
}
.vacancy-salary {
font-size: 24px;
}
.qr-modal-content { .qr-modal-content {
padding: 30px 20px; padding: 30px 20px;
width: 95%; width: 95%;
@@ -837,161 +868,6 @@
box-shadow: 0 4px 15px rgba(0,0,0,0.2); 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> </style>
</head> </head>
<body> <body>
@@ -999,7 +875,7 @@
<div class="header"> <div class="header">
<div class="logo" onclick="window.location.href='/'"> <div class="logo" onclick="window.location.href='/'">
<i class="fas fa-briefcase"></i> <i class="fas fa-briefcase"></i>
МП.Ярмарка Rabota.Today
</div> </div>
<div class="nav" id="nav"> <div class="nav" id="nav">
<!-- Навигация будет заполнена динамически --> <!-- Навигация будет заполнена динамически -->
@@ -1104,6 +980,82 @@
.replace(/'/g, "&#039;"); .replace(/'/g, "&#039;");
} }
// Функция для обновления SEO тегов
function updateSEOTags(vacancy) {
const decodedTitle = decodeHtmlEntities(vacancy.title);
const decodedCompany = decodeHtmlEntities(vacancy.company_name || 'Компания');
const salary = vacancy.salary || 'Зарплата не указана';
const description = vacancy.description || 'Подробная информация о вакансии';
// Формируем описание для SEO
const shortDescription = description.length > 160 ? description.substring(0, 157) + '...' : description;
const seoDescription = `${decodedTitle} в компании ${decodedCompany}. ${salary}. ${shortDescription}`;
// Заголовок страницы
document.title = `${decodedTitle} в ${decodedCompany} | Rabota.Today`;
// Мета-теги
document.querySelector('meta[name="description"]')?.setAttribute('content', seoDescription);
document.querySelector('meta[name="keywords"]')?.setAttribute('content', `${decodedTitle}, работа, вакансия, ${decodedCompany}, трудоустройство, поиск работы`);
// Open Graph теги
document.querySelector('meta[property="og:title"]')?.setAttribute('content', `${decodedTitle} в ${decodedCompany}`);
document.querySelector('meta[property="og:description"]')?.setAttribute('content', seoDescription);
document.querySelector('meta[property="og:url"]')?.setAttribute('content', window.location.href);
// Twitter Card
document.querySelector('meta[name="twitter:title"]')?.setAttribute('content', `${decodedTitle} в ${decodedCompany}`);
document.querySelector('meta[name="twitter:description"]')?.setAttribute('content', seoDescription);
// Canonical URL
document.querySelector('link[rel="canonical"]')?.setAttribute('href', window.location.href);
// Структурированные данные (JSON-LD)
const salaryMatch = salary.match(/(\d+)/);
const salaryValue = salaryMatch ? parseInt(salaryMatch[0]) : 0;
const structuredData = {
"@context": "https://schema.org",
"@type": "JobPosting",
"title": decodedTitle,
"description": description,
"datePosted": vacancy.created_at,
"validThrough": new Date(new Date(vacancy.created_at).getTime() + 30 * 24 * 60 * 60 * 1000).toISOString(),
"employmentType": "FULL_TIME",
"hiringOrganization": {
"@type": "Organization",
"name": decodedCompany,
"sameAs": vacancy.company_website || "",
"logo": vacancy.company_logo || "https://yarmarka.rabota.today/static/images/logo.png"
},
"jobLocation": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"addressLocality": vacancy.company_address || "Москва",
"addressCountry": "RU"
}
},
"baseSalary": {
"@type": "MonetaryAmount",
"currency": "RUB",
"value": {
"@type": "QuantitativeValue",
"value": salaryValue,
"unitText": "MONTH"
}
},
"workHours": "Полный день"
};
const scriptElement = document.getElementById('structuredData');
if (scriptElement) {
scriptElement.textContent = JSON.stringify(structuredData, null, 2);
}
console.log('✅ SEO теги обновлены');
}
// Проверка авторизации // Проверка авторизации
async function checkAuth() { async function checkAuth() {
const token = localStorage.getItem('accessToken'); const token = localStorage.getItem('accessToken');
@@ -1175,6 +1127,10 @@
} }
currentVacancy = await response.json(); currentVacancy = await response.json();
// Обновляем SEO теги
updateSEOTags(currentVacancy);
renderVacancy(currentVacancy); renderVacancy(currentVacancy);
} catch (error) { } catch (error) {
@@ -1191,7 +1147,7 @@
} }
} }
// Отображение вакансии с QR-кнопкой // Отображение вакансии
function renderVacancy(vacancy) { function renderVacancy(vacancy) {
const container = document.getElementById('vacancyDetail'); const container = document.getElementById('vacancyDetail');
const token = localStorage.getItem('accessToken'); const token = localStorage.getItem('accessToken');
@@ -1346,7 +1302,6 @@
function openQRModal() { function openQRModal() {
if (!currentVacancy) return; if (!currentVacancy) return;
// Декодируем названия
const decodedTitle = decodeHtmlEntities(currentVacancy.title); const decodedTitle = decodeHtmlEntities(currentVacancy.title);
const decodedCompanyName = decodeHtmlEntities(currentVacancy.company_name || ''); const decodedCompanyName = decodeHtmlEntities(currentVacancy.company_name || '');
@@ -1355,43 +1310,31 @@
const vacancyUrl = window.location.origin + '/vacancy/' + vacancyId; const vacancyUrl = window.location.origin + '/vacancy/' + vacancyId;
document.getElementById('qrVacancyUrl').textContent = vacancyUrl.replace('https://', '').replace('http://', ''); document.getElementById('qrVacancyUrl').textContent = vacancyUrl.replace('https://', '').replace('http://', '');
// Обновляем статистику
document.getElementById('qrViewCount').textContent = ++qrViewCount; document.getElementById('qrViewCount').textContent = ++qrViewCount;
document.getElementById('qrViewBadge').textContent = qrViewCount; document.getElementById('qrViewBadge').textContent = qrViewCount;
// Информация о зарплате
document.getElementById('qrSalaryInfo').textContent = currentVacancy.salary || 'з/п не указана'; document.getElementById('qrSalaryInfo').textContent = currentVacancy.salary || 'з/п не указана';
// Название компании
document.getElementById('qrCompanyName').textContent = decodedCompanyName || '—'; document.getElementById('qrCompanyName').textContent = decodedCompanyName || '—';
// Генерируем QR-код
generateQRCodeWithLogo(vacancyUrl, currentVacancy.company_logo); generateQRCodeWithLogo(vacancyUrl, currentVacancy.company_logo);
document.getElementById('qrModal').classList.add('active'); document.getElementById('qrModal').classList.add('active');
} }
// Закрыть модальное окно
function closeQRModal() { function closeQRModal() {
document.getElementById('qrModal').classList.remove('active'); document.getElementById('qrModal').classList.remove('active');
} }
// Генерация QR-кода с логотипом компании
function generateQRCodeWithLogo(text, logoUrl) { function generateQRCodeWithLogo(text, logoUrl) {
const canvas = document.getElementById('qrCanvas'); const canvas = document.getElementById('qrCanvas');
const logoOverlay = document.getElementById('qrLogoOverlay'); const logoOverlay = document.getElementById('qrLogoOverlay');
const logoImg = document.getElementById('qrLogoImg'); const logoImg = document.getElementById('qrLogoImg');
const logoIcon = document.getElementById('qrLogoIcon'); const logoIcon = document.getElementById('qrLogoIcon');
// Настройки QR-кода
const options = { const options = {
width: 250, width: 250,
height: 250, height: 250,
color: { color: { dark: '#0b1c34', light: '#ffffff' },
dark: '#0b1c34', errorCorrectionLevel: 'H'
light: '#ffffff'
},
errorCorrectionLevel: 'H' // высокая коррекция ошибок для логотипа
}; };
QRCode.toCanvas(canvas, text, options, function(error) { QRCode.toCanvas(canvas, text, options, function(error) {
@@ -1399,25 +1342,18 @@
console.error('Error generating QR code:', error); console.error('Error generating QR code:', error);
showNotification('Ошибка генерации QR-кода', 'error'); showNotification('Ошибка генерации QR-кода', 'error');
} else { } else {
console.log('QR code generated successfully');
// Показываем оверлей с логотипом или иконкой
logoOverlay.style.display = 'flex'; logoOverlay.style.display = 'flex';
if (logoUrl) { if (logoUrl) {
// Если есть логотип компании, показываем его
logoImg.src = logoUrl; logoImg.src = logoUrl;
logoImg.style.display = 'block'; logoImg.style.display = 'block';
logoIcon.style.display = 'none'; logoIcon.style.display = 'none';
// Обработка ошибки загрузки логотипа
logoImg.onerror = function() { logoImg.onerror = function() {
logoImg.style.display = 'none'; logoImg.style.display = 'none';
logoIcon.style.display = 'block'; logoIcon.style.display = 'block';
logoIcon.className = 'fas fa-briefcase'; logoIcon.className = 'fas fa-briefcase';
}; };
} else { } else {
// Если нет логотипа, показываем иконку вакансии
logoImg.style.display = 'none'; logoImg.style.display = 'none';
logoIcon.style.display = 'block'; logoIcon.style.display = 'block';
logoIcon.className = 'fas fa-briefcase'; logoIcon.className = 'fas fa-briefcase';
@@ -1426,48 +1362,24 @@
}); });
} }
// Скачать QR-код
function downloadQR() { function downloadQR() {
const canvas = document.getElementById('qrCanvas'); const canvas = document.getElementById('qrCanvas');
const logoOverlay = document.getElementById('qrLogoOverlay');
const logoImg = document.getElementById('qrLogoImg');
const logoIcon = document.getElementById('qrLogoIcon');
// Создаем временный canvas для объединения QR и логотипа
const combinedCanvas = document.createElement('canvas'); const combinedCanvas = document.createElement('canvas');
combinedCanvas.width = canvas.width; combinedCanvas.width = canvas.width;
combinedCanvas.height = canvas.height; combinedCanvas.height = canvas.height;
const ctx = combinedCanvas.getContext('2d'); const ctx = combinedCanvas.getContext('2d');
// Рисуем QR-код
ctx.drawImage(canvas, 0, 0); ctx.drawImage(canvas, 0, 0);
// Рисуем логотип поверх
ctx.fillStyle = 'white'; ctx.fillStyle = 'white';
ctx.beginPath(); ctx.beginPath();
ctx.arc(125, 125, 35, 0, 2 * Math.PI); ctx.arc(125, 125, 35, 0, 2 * Math.PI);
ctx.fill(); ctx.fill();
ctx.fillStyle = '#3b82f6';
ctx.font = '30px "Font Awesome 6 Free"';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('', 125, 125);
if (logoImg.style.display === 'block' && logoImg.complete) {
// Если есть загруженный логотип
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 = '#3b82f6';
ctx.font = '30px "Font Awesome 6 Free"';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('', 125, 125); // Unicode для иконки briefcase
}
// Скачиваем
const link = document.createElement('a'); const link = document.createElement('a');
const decodedTitle = decodeHtmlEntities(currentVacancy.title); const decodedTitle = decodeHtmlEntities(currentVacancy.title);
const filename = `vacancy_${decodedTitle.toLowerCase().replace(/[^a-zа-я0-9]/g, '_')}.png`; const filename = `vacancy_${decodedTitle.toLowerCase().replace(/[^a-zа-я0-9]/g, '_')}.png`;
@@ -1478,7 +1390,6 @@
showNotification('QR-код скачан', 'success'); showNotification('QR-код скачан', 'success');
} }
// Копировать ссылку на вакансию
function copyVacancyLink() { function copyVacancyLink() {
const url = window.location.origin + '/vacancy/' + vacancyId; const url = window.location.origin + '/vacancy/' + vacancyId;
navigator.clipboard.writeText(url).then(() => { navigator.clipboard.writeText(url).then(() => {
@@ -1488,42 +1399,31 @@
}); });
} }
// Поделиться вакансией
function shareVacancy() { function shareVacancy() {
const url = window.location.href; const url = window.location.href;
if (navigator.share) { if (navigator.share) {
navigator.share({ navigator.share({ title: document.title, url: url }).catch(() => copyVacancyLink());
title: document.title,
url: url
}).catch(() => {
copyVacancyLink();
});
} else { } else {
copyVacancyLink(); copyVacancyLink();
} }
} }
// Распечатать QR
function printQR() { function printQR() {
const canvas = document.getElementById('qrCanvas'); const canvas = document.getElementById('qrCanvas');
const decodedTitle = decodeHtmlEntities(currentVacancy.title); const decodedTitle = decodeHtmlEntities(currentVacancy.title);
const decodedCompany = decodeHtmlEntities(currentVacancy.company_name || ''); const decodedCompany = decodeHtmlEntities(currentVacancy.company_name || '');
const printWindow = window.open('', '_blank'); const printWindow = window.open('', '_blank');
printWindow.document.write(` printWindow.document.write(`
<html> <html>
<head> <head><title>QR-код вакансии</title>
<title>QR-код вакансии</title> <style>
<style> body { display: flex; justify-content: center; align-items: center; height: 100vh; flex-direction: column; font-family: Arial; margin: 0; padding: 20px; }
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; }
h2 { color: #0b1c34; text-align: center; word-break: break-word; } h3 { color: #3b82f6; text-align: center; }
h3 { color: #3b82f6; text-align: center; } img { max-width: 300px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); border-radius: 20px; }
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; }
.url { color: #4f7092; margin-top: 20px; word-break: break-all; text-align: center; } </style>
.company { margin-bottom: 20px; color: #1f3f60; }
</style>
</head> </head>
<body> <body>
<h2>${escapeHtml(decodedTitle)}</h2> <h2>${escapeHtml(decodedTitle)}</h2>
@@ -1533,54 +1433,34 @@
</body> </body>
</html> </html>
`); `);
printWindow.document.close(); printWindow.document.close();
printWindow.focus(); printWindow.focus();
printWindow.print(); printWindow.print();
} }
// Поделиться QR в соцсетях
function shareQR(platform) { function shareQR(platform) {
const url = window.location.origin + '/vacancy/' + vacancyId; const url = window.location.origin + '/vacancy/' + vacancyId;
const decodedTitle = decodeHtmlEntities(currentVacancy.title); const decodedTitle = decodeHtmlEntities(currentVacancy.title);
const text = `Вакансия: ${decodedTitle} на Rabota.Today`; const text = `Вакансия: ${decodedTitle} на Rabota.Today`;
let shareUrl = ''; let shareUrl = '';
switch(platform) { switch(platform) {
case 'whatsapp': case 'whatsapp': shareUrl = `https://wa.me/?text=${encodeURIComponent(text + ' ' + url)}`; break;
shareUrl = `https://wa.me/?text=${encodeURIComponent(text + ' ' + url)}`; case 'telegram': shareUrl = `https://t.me/share/url?url=${encodeURIComponent(url)}&text=${encodeURIComponent(text)}`; break;
break; case 'email': shareUrl = `mailto:?subject=${encodeURIComponent('Вакансия ' + decodedTitle)}&body=${encodeURIComponent(text + '\n\n' + url)}`; break;
case 'telegram':
shareUrl = `https://t.me/share/url?url=${encodeURIComponent(url)}&text=${encodeURIComponent(text)}`;
break;
case 'email':
shareUrl = `mailto:?subject=${encodeURIComponent('Вакансия ' + decodedTitle)}&body=${encodeURIComponent(text + '\n\n' + url)}`;
break;
}
if (shareUrl) {
window.open(shareUrl, '_blank');
} }
if (shareUrl) window.open(shareUrl, '_blank');
} }
// Отклик на вакансию
async function applyForVacancy() { async function applyForVacancy() {
const token = localStorage.getItem('accessToken'); const token = localStorage.getItem('accessToken');
if (!token) { redirectToLogin(); return; }
if (!token) {
redirectToLogin();
return;
}
try { try {
const response = await fetch(`${API_BASE_URL}/vacancies/${vacancyId}/apply`, { const response = await fetch(`${API_BASE_URL}/vacancies/${vacancyId}/apply`, {
method: 'POST', method: 'POST',
headers: { headers: { 'Authorization': `Bearer ${token}` }
'Authorization': `Bearer ${token}`
}
}); });
if (response.ok) { if (response.ok) {
showNotification('Отклик отправлен! Работодатель свяжется с вами.', 'success'); showNotification('Отклик отправлен! Работодатель свяжется с вами.', 'success');
loadVacancy(); loadVacancy();
@@ -1593,52 +1473,37 @@
} }
} }
// Добавить в избранное
function saveToFavorites() { function saveToFavorites() {
const token = localStorage.getItem('accessToken'); const token = localStorage.getItem('accessToken');
if (!token) { if (!token) {
if (confirm('Для добавления в избранное нужно войти в систему. Перейти на страницу входа?')) { if (confirm('Для добавления в избранное нужно войти в систему. Перейти на страницу входа?')) {
window.location.href = '/login'; window.location.href = '/login';
} }
return; return;
} }
// Здесь будет логика добавления в избранное
showNotification('Вакансия добавлена в избранное', 'success'); showNotification('Вакансия добавлена в избранное', 'success');
} }
// Редирект на страницу входа
function redirectToLogin() { function redirectToLogin() {
if (confirm('Для отклика нужно войти в систему. Перейти на страницу входа?')) { if (confirm('Для отклика нужно войти в систему. Перейти на страницу входа?')) {
window.location.href = '/login'; window.location.href = '/login';
} }
} }
// Показать уведомление
function showNotification(message, type = 'info') { function showNotification(message, type = 'info') {
const notification = document.getElementById('notification'); const notification = document.getElementById('notification');
notification.className = `notification ${type}`; notification.className = `notification ${type}`;
notification.innerHTML = message; notification.innerHTML = message;
notification.style.display = 'block'; notification.style.display = 'block';
setTimeout(() => { notification.style.display = 'none'; }, 3000);
setTimeout(() => {
notification.style.display = 'none';
}, 3000);
} }
// Закрытие модального окна по клику вне
window.onclick = function(event) { window.onclick = function(event) {
const modal = document.getElementById('qrModal'); const modal = document.getElementById('qrModal');
if (event.target === modal) { if (event.target === modal) modal.classList.remove('active');
modal.classList.remove('active');
}
}; };
// Инициализация checkAuth().then(() => loadVacancy());
checkAuth().then(() => {
loadVacancy();
});
</script> </script>
</body> </body>
</html> </html>