This commit is contained in:
2026-03-16 18:57:22 +03:00
parent 65eca64a5f
commit 668e62d652
18 changed files with 6386 additions and 1347 deletions

View File

@@ -0,0 +1,748 @@
<!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">
<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;
}
.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;
}
.back-link i {
margin-right: 8px;
}
.back-link:hover {
color: #0b1c34;
}
/* Шапка компании */
.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;
}
.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;
}
.company-logo img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 30px;
}
.company-info {
flex: 1;
}
.company-name {
font-size: 36px;
font-weight: 700;
color: #0b1c34;
margin-bottom: 10px;
}
.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;
}
.company-website:hover {
text-decoration: underline;
}
/* Статистика компании */
.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);
}
.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;
}
.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;
}
/* Вакансии компании */
.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;
}
/* Социальные сети */
.company-social {
background: white;
border-radius: 40px;
padding: 40px;
box-shadow: 0 20px 40px rgba(0,20,40,0.1);
}
.social-links {
display: flex;
gap: 15px;
flex-wrap: wrap;
}
.social-link {
width: 50px;
height: 50px;
background: #eef4fa;
border-radius: 25px;
display: flex;
align-items: center;
justify-content: center;
color: #3b82f6;
font-size: 24px;
transition: 0.2s;
}
.social-link:hover {
background: #3b82f6;
color: white;
transform: translateY(-3px);
}
/* Загрузка */
.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;
}
/* Адаптивность */
@media (max-width: 768px) {
.company-header {
flex-direction: column;
text-align: center;
}
.company-meta {
justify-content: center;
}
.contacts-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<!-- Шапка -->
<div class="header">
<div class="logo">
<i class="fas fa-briefcase"></i>
Rabota.Today
</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>
<script>
const API_BASE_URL = window.location.protocol + '//' + window.location.host + '/api';
let currentUser = null;
// Получаем ID компании из URL
const pathParts = window.location.pathname.split('/');
const companyId = pathParts[pathParts.length - 1];
// Проверка авторизации
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> ${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('Компания не найдена');
}
const company = await response.json();
console.log('✅ Компания загружена:', company);
// Загружаем вакансии компании
const vacanciesResponse = await fetch(`${API_BASE_URL}/vacancies/all?company_id=${companyId}`);
const vacancies = vacanciesResponse.ok ? await vacanciesResponse.json() : { vacancies: [] };
renderCompany(company, vacancies.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>
`;
}
}
// Отображение компании
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;
container.innerHTML = `
<!-- Шапка компании -->
<div class="company-header">
<div class="company-logo">
${company.logo ?
`<img src="${escapeHtml(company.logo)}" alt="${escapeHtml(company.name)}">` :
`<i class="fas fa-building"></i>`
}
</div>
<div class="company-info">
<h1 class="company-name">${escapeHtml(company.name)}</h1>
<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">
<i class="fas fa-briefcase"></i>
<div class="stat-value">${vacancies.length}</div>
<div class="stat-label">Всего вакансий</div>
</div>
<div class="stat-card">
<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">
<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>
<!-- Социальные сети (заглушка, можно добавить позже) -->
<div class="company-social" style="display: none;">
<h2 class="section-title">
<i class="fas fa-share-alt"></i> Мы в соцсетях
</h2>
<div class="social-links">
<a href="#" class="social-link"><i class="fab fa-telegram"></i></a>
<a href="#" class="social-link"><i class="fab fa-vk"></i></a>
<a href="#" class="social-link"><i class="fab fa-linkedin"></i></a>
</div>
</div>
`;
}
// Экранирование 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;");
}
// Инициализация
checkAuth().then(() => {
loadCompany();
});
</script>
</body>
</html>