1732 lines
60 KiB
HTML
1732 lines
60 KiB
HTML
<!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;
|
||
}
|
||
|
||
.profile-grid {
|
||
display: grid;
|
||
grid-template-columns: 300px 1fr;
|
||
gap: 30px;
|
||
}
|
||
|
||
.profile-sidebar {
|
||
background: white;
|
||
border-radius: 40px;
|
||
padding: 30px;
|
||
text-align: center;
|
||
box-shadow: 0 20px 40px rgba(0,20,40,0.1);
|
||
height: fit-content;
|
||
}
|
||
|
||
.avatar {
|
||
width: 120px;
|
||
height: 120px;
|
||
background: #eef4fa;
|
||
border-radius: 60px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin: 0 auto 20px;
|
||
font-size: 48px;
|
||
color: #3b82f6;
|
||
}
|
||
|
||
.avatar img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.profile-name {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #0b1c34;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.profile-role-badge {
|
||
display: inline-block;
|
||
padding: 6px 16px;
|
||
background: #eef4fa;
|
||
border-radius: 30px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #1f3f60;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.profile-role-badge.employee {
|
||
background: #e0f2e0;
|
||
color: #166534;
|
||
}
|
||
|
||
.profile-role-badge.employer {
|
||
background: #dbeafe;
|
||
color: #1e40af;
|
||
}
|
||
|
||
.profile-role-badge.admin {
|
||
background: #fef3c7;
|
||
color: #92400e;
|
||
}
|
||
|
||
.profile-stats {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 15px;
|
||
margin: 20px 0;
|
||
padding: 20px 0;
|
||
border-top: 1px solid #dee9f5;
|
||
border-bottom: 1px solid #dee9f5;
|
||
}
|
||
|
||
.stat-item {
|
||
text-align: center;
|
||
}
|
||
|
||
.stat-value {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #0b1c34;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 12px;
|
||
color: #4f7092;
|
||
}
|
||
|
||
.contact-info {
|
||
text-align: left;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.contact-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 10px 0;
|
||
color: #1f3f60;
|
||
}
|
||
|
||
.contact-item i {
|
||
color: #3b82f6;
|
||
width: 20px;
|
||
}
|
||
|
||
.contact-item a {
|
||
color: #3b82f6;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.contact-item a:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.profile-content {
|
||
background: white;
|
||
border-radius: 40px;
|
||
padding: 30px;
|
||
box-shadow: 0 20px 40px rgba(0,20,40,0.1);
|
||
}
|
||
|
||
.content-tabs {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin-bottom: 30px;
|
||
border-bottom: 2px solid #dee9f5;
|
||
padding-bottom: 15px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.content-tab {
|
||
padding: 10px 20px;
|
||
cursor: pointer;
|
||
font-weight: 600;
|
||
color: #4f7092;
|
||
border-radius: 30px;
|
||
transition: 0.2s;
|
||
}
|
||
|
||
.content-tab:hover {
|
||
background: #eef4fa;
|
||
}
|
||
|
||
.content-tab.active {
|
||
background: #eef4fa;
|
||
color: #0b1c34;
|
||
}
|
||
|
||
.content-pane {
|
||
display: none;
|
||
}
|
||
|
||
.content-pane.active {
|
||
display: block;
|
||
}
|
||
|
||
.info-section {
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.info-section h3 {
|
||
color: #0b1c34;
|
||
font-size: 20px;
|
||
margin-bottom: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.info-section h3 i {
|
||
color: #3b82f6;
|
||
}
|
||
|
||
.info-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 15px;
|
||
}
|
||
|
||
.info-card {
|
||
background: #f9fcff;
|
||
padding: 15px;
|
||
border-radius: 20px;
|
||
}
|
||
|
||
.info-card .label {
|
||
font-size: 12px;
|
||
color: #4f7092;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.info-card .value {
|
||
font-weight: 600;
|
||
color: #0b1c34;
|
||
}
|
||
|
||
/* Для резюме */
|
||
.resume-preview {
|
||
background: #f9fcff;
|
||
border-radius: 30px;
|
||
padding: 25px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.resume-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
flex-wrap: wrap;
|
||
gap: 15px;
|
||
}
|
||
|
||
.resume-header h3 {
|
||
color: #0b1c34;
|
||
font-size: 22px;
|
||
}
|
||
|
||
.resume-salary {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #0f2b4f;
|
||
padding: 10px 15px;
|
||
background: white;
|
||
border-radius: 30px;
|
||
}
|
||
|
||
.resume-tags {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.tag {
|
||
background: #eef4fa;
|
||
padding: 6px 14px;
|
||
border-radius: 30px;
|
||
font-size: 13px;
|
||
color: #1f3f60;
|
||
}
|
||
|
||
.about-me {
|
||
background: white;
|
||
padding: 20px;
|
||
border-radius: 20px;
|
||
line-height: 1.6;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.experience-item, .education-item {
|
||
background: white;
|
||
border-radius: 20px;
|
||
padding: 20px;
|
||
margin-bottom: 15px;
|
||
border-left: 4px solid #3b82f6;
|
||
}
|
||
|
||
.item-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: 10px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.item-title {
|
||
font-weight: 700;
|
||
color: #0b1c34;
|
||
}
|
||
|
||
.item-subtitle {
|
||
color: #3b82f6;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.item-period {
|
||
color: #4f7092;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.item-description {
|
||
margin-top: 10px;
|
||
padding: 10px;
|
||
background: #f9fcff;
|
||
border-radius: 15px;
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* Для компании */
|
||
.company-info {
|
||
background: #f9fcff;
|
||
border-radius: 30px;
|
||
padding: 25px;
|
||
}
|
||
|
||
.company-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
margin-bottom: 20px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.company-logo {
|
||
width: 80px;
|
||
height: 80px;
|
||
background: #eef4fa;
|
||
border-radius: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 32px;
|
||
color: #3b82f6;
|
||
}
|
||
|
||
.company-logo img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
border-radius: 20px;
|
||
}
|
||
|
||
.company-name {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
color: #0b1c34;
|
||
}
|
||
|
||
.vacancies-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||
gap: 20px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.vacancy-card {
|
||
background: white;
|
||
border-radius: 20px;
|
||
padding: 20px;
|
||
cursor: pointer;
|
||
transition: 0.2s;
|
||
border: 1px solid #dee9f5;
|
||
}
|
||
|
||
.vacancy-card:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 10px 30px rgba(0,20,40,0.1);
|
||
}
|
||
|
||
.vacancy-card h4 {
|
||
color: #0b1c34;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.vacancy-salary {
|
||
color: #3b82f6;
|
||
font-weight: 600;
|
||
margin: 10px 0;
|
||
}
|
||
|
||
.vacancy-footer {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 10px;
|
||
font-size: 13px;
|
||
color: #4f7092;
|
||
}
|
||
|
||
.btn {
|
||
padding: 14px 28px;
|
||
border-radius: 40px;
|
||
border: none;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
transition: 0.2s;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: #0b1c34;
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background: #1b3f6b;
|
||
}
|
||
|
||
.btn-outline {
|
||
background: transparent;
|
||
border: 2px solid #3b82f6;
|
||
color: #0b1c34;
|
||
}
|
||
|
||
.btn-outline:hover {
|
||
background: #eef4fa;
|
||
}
|
||
|
||
/* Стили для QR-кнопки */
|
||
.qr-button {
|
||
width: 44px;
|
||
height: 44px;
|
||
background: linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%);
|
||
border-radius: 22px;
|
||
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: 20px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
border: none;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.qr-button:hover {
|
||
transform: translateY(-3px) scale(1.05);
|
||
box-shadow: 0 8px 20px rgba(59,130,246,0.4);
|
||
}
|
||
|
||
.qr-button:active {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
.qr-button i {
|
||
font-size: 20px;
|
||
}
|
||
|
||
.qr-view-count {
|
||
position: absolute;
|
||
top: -5px;
|
||
right: -5px;
|
||
background: #ef4444;
|
||
color: white;
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
padding: 2px 5px;
|
||
border-radius: 12px;
|
||
min-width: 18px;
|
||
text-align: center;
|
||
box-shadow: 0 2px 4px rgba(239,68,68,0.3);
|
||
}
|
||
|
||
/* Модальное окно для QR */
|
||
.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;
|
||
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;
|
||
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);
|
||
}
|
||
|
||
.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-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;
|
||
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);
|
||
}
|
||
|
||
.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; }
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from { transform: translateY(30px); opacity: 0; }
|
||
to { transform: translateY(0); opacity: 1; }
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.profile-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.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);
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<div class="logo" onclick="window.location.href='/'">
|
||
<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="profileContent">
|
||
<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()">×</button>
|
||
<h2><i class="fas fa-qrcode"></i> <span id="qrUserName"></span></h2>
|
||
<div class="qr-subtitle" id="qrUserUrl">yarmarka.rabota.today/user/</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="avatar" style="display: none;">
|
||
<i id="qrLogoIcon" class="fas fa-user"></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="qrUserRole">—</div>
|
||
<div class="qr-stat-label">роль</div>
|
||
</div>
|
||
<div class="qr-stat-item">
|
||
<div class="qr-stat-value" id="qrUserSince">—</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="copyProfileLink()">
|
||
<i class="fas fa-link"></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 currentProfileUser = null;
|
||
let qrViewCount = 0;
|
||
|
||
// Получаем ID пользователя из URL
|
||
const pathParts = window.location.pathname.split('/');
|
||
const userId = pathParts[pathParts.length - 1];
|
||
|
||
// Функции для работы с HTML
|
||
function escapeHtml(unsafe) {
|
||
if (!unsafe) return '';
|
||
return unsafe.toString()
|
||
.replace(/&/g, "&")
|
||
.replace(/</g, "<")
|
||
.replace(/>/g, ">")
|
||
.replace(/"/g, """)
|
||
.replace(/'/g, "'");
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
// Проверка авторизации
|
||
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();
|
||
console.log('✅ Текущий пользователь:', currentUser);
|
||
} 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 loadUserProfile() {
|
||
try {
|
||
console.log(`📥 Загрузка профиля пользователя ID: ${userId}`);
|
||
|
||
const token = localStorage.getItem('accessToken');
|
||
const headers = {};
|
||
|
||
if (token) {
|
||
headers['Authorization'] = `Bearer ${token}`;
|
||
}
|
||
|
||
const response = await fetch(`${API_BASE_URL}/users/${userId}`, {
|
||
headers: headers
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Пользователь не найден');
|
||
}
|
||
|
||
const user = await response.json();
|
||
console.log('✅ Профиль загружен:', user);
|
||
|
||
// Сохраняем данные пользователя для QR
|
||
currentProfileUser = user;
|
||
|
||
// Загружаем резюме если это соискатель
|
||
let resume = null;
|
||
if (user.role === 'employee') {
|
||
try {
|
||
const resumeResponse = await fetch(`${API_BASE_URL}/users/${userId}/resume`);
|
||
if (resumeResponse.ok) {
|
||
resume = await resumeResponse.json();
|
||
console.log('✅ Резюме загружено');
|
||
} else if (resumeResponse.status === 404) {
|
||
console.log('ℹ️ У пользователя нет резюме');
|
||
resume = null;
|
||
}
|
||
} catch (resumeError) {
|
||
console.error('❌ Ошибка при запросе резюме:', resumeError);
|
||
}
|
||
}
|
||
|
||
// Загружаем компанию если это работодатель
|
||
let company = null;
|
||
let vacancies = [];
|
||
if (user.role === 'employer') {
|
||
try {
|
||
const companyResponse = await fetch(`${API_BASE_URL}/users/${userId}/company`);
|
||
if (companyResponse.ok) {
|
||
company = await companyResponse.json();
|
||
console.log('✅ Компания загружена');
|
||
}
|
||
} catch (companyError) {
|
||
console.error('❌ Ошибка при запросе компании:', companyError);
|
||
}
|
||
|
||
try {
|
||
const vacanciesResponse = await fetch(`${API_BASE_URL}/users/${userId}/vacancies`);
|
||
if (vacanciesResponse.ok) {
|
||
vacancies = await vacanciesResponse.json();
|
||
console.log(`✅ Загружено ${vacancies.length} вакансий`);
|
||
}
|
||
} catch (vacanciesError) {
|
||
console.error('❌ Ошибка при запросе вакансий:', vacanciesError);
|
||
}
|
||
}
|
||
|
||
renderProfile(user, resume, company, vacancies);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Ошибка загрузки профиля:', error);
|
||
document.getElementById('profileContent').innerHTML = `
|
||
<div class="error-message">
|
||
<i class="fas fa-exclamation-circle"></i>
|
||
Пользователь не найден
|
||
</div>
|
||
`;
|
||
}
|
||
}
|
||
|
||
// Отображение профиля
|
||
function renderProfile(user, resume, company, vacancies) {
|
||
const container = document.getElementById('profileContent');
|
||
|
||
const isOwnProfile = currentUser && currentUser.id === user.id;
|
||
const canSeeContacts = isOwnProfile || (currentUser && currentUser.is_admin) || !user.contacts_hidden;
|
||
|
||
// Определяем роль
|
||
let roleText = '';
|
||
let roleClass = '';
|
||
if (user.role === 'employee') {
|
||
roleText = 'Соискатель';
|
||
roleClass = 'employee';
|
||
} else if (user.role === 'employer') {
|
||
roleText = 'Работодатель';
|
||
roleClass = 'employer';
|
||
} else if (user.role === 'admin') {
|
||
roleText = 'Администратор';
|
||
roleClass = 'admin';
|
||
}
|
||
|
||
// Статистика
|
||
let stats = '';
|
||
if (user.role === 'employee') {
|
||
if (resume) {
|
||
stats = `
|
||
<div class="profile-stats">
|
||
<div class="stat-item">
|
||
<div class="stat-value">${resume.work_experience?.length || 0}</div>
|
||
<div class="stat-label">мест работы</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-value">${resume.views || 0}</div>
|
||
<div class="stat-label">просмотров</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
} else {
|
||
stats = `
|
||
<div class="profile-stats">
|
||
<div class="stat-item">
|
||
<div class="stat-value">0</div>
|
||
<div class="stat-label">мест работы</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-value">0</div>
|
||
<div class="stat-label">просмотров</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
} else if (user.role === 'employer') {
|
||
stats = `
|
||
<div class="profile-stats">
|
||
<div class="stat-item">
|
||
<div class="stat-value">${vacancies.length}</div>
|
||
<div class="stat-label">вакансий</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-value">${vacancies.filter(v => v.is_active).length}</div>
|
||
<div class="stat-label">активных</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// Контакты
|
||
let contacts = '';
|
||
if (canSeeContacts) {
|
||
contacts = `
|
||
<div class="contact-item">
|
||
<i class="fas fa-envelope"></i>
|
||
<span>${escapeHtml(user.email) || '—'}</span>
|
||
</div>
|
||
<div class="contact-item">
|
||
<i class="fas fa-phone"></i>
|
||
<span>${escapeHtml(user.phone) || '—'}</span>
|
||
</div>
|
||
<div class="contact-item">
|
||
<i class="fab fa-telegram"></i>
|
||
<span>${escapeHtml(user.telegram) || '—'}</span>
|
||
</div>
|
||
`;
|
||
} else {
|
||
contacts = `
|
||
<div class="contact-item">
|
||
<i class="fas fa-lock"></i>
|
||
<span>Контакты скрыты</span>
|
||
</div>
|
||
<div class="contact-item" style="font-size: 12px; color: #4f7092;">
|
||
<i class="fas fa-info-circle"></i>
|
||
<span>Авторизуйтесь для просмотра контактов</span>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// Основной контент в зависимости от роли
|
||
let contentTabs = '';
|
||
let mainContent = '';
|
||
|
||
if (user.role === 'employee') {
|
||
if (resume) {
|
||
// У соискателя есть резюме
|
||
contentTabs = `
|
||
<div class="content-tab active" onclick="switchTab('main')">Главная</div>
|
||
<div class="content-tab" onclick="switchTab('resume')">Резюме</div>
|
||
`;
|
||
|
||
mainContent = `
|
||
<div class="content-pane active" id="mainPane">
|
||
<div class="info-section">
|
||
<h3><i class="fas fa-info-circle"></i> О себе</h3>
|
||
<div class="about-me">${escapeHtml(resume.about_me || 'Информация не заполнена')}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="content-pane" id="resumePane">
|
||
<div class="resume-preview">
|
||
<div class="resume-header">
|
||
<h3>${escapeHtml(resume.desired_position || 'Желаемая должность не указана')}</h3>
|
||
<div class="resume-salary">${escapeHtml(resume.desired_salary || 'з/п не указана')}</div>
|
||
</div>
|
||
|
||
<div class="resume-tags">
|
||
${(resume.tags || []).map(t => `<span class="tag">${escapeHtml(t.name)}</span>`).join('')}
|
||
</div>
|
||
|
||
<h4 style="margin: 20px 0 10px;">Опыт работы</h4>
|
||
${(resume.work_experience || []).length > 0 ?
|
||
(resume.work_experience || []).map(exp => `
|
||
<div class="experience-item">
|
||
<div class="item-header">
|
||
<span class="item-title">${escapeHtml(exp.position)}</span>
|
||
<span class="item-period">${escapeHtml(exp.period || '')}</span>
|
||
</div>
|
||
<div class="item-subtitle">${escapeHtml(exp.company)}</div>
|
||
${exp.description ? `<div class="item-description">${escapeHtml(exp.description)}</div>` : ''}
|
||
</div>
|
||
`).join('') :
|
||
'<p style="color: #4f7092; padding: 20px; text-align: center;">Опыт работы не указан</p>'
|
||
}
|
||
|
||
<h4 style="margin: 20px 0 10px;">Образование</h4>
|
||
${(resume.education || []).length > 0 ?
|
||
(resume.education || []).map(edu => `
|
||
<div class="education-item">
|
||
<div class="item-header">
|
||
<span class="item-title">${escapeHtml(edu.institution)}</span>
|
||
<span class="item-period">${escapeHtml(edu.graduation_year || '')}</span>
|
||
</div>
|
||
<div class="item-subtitle">${escapeHtml(edu.specialty || '')}</div>
|
||
</div>
|
||
`).join('') :
|
||
'<p style="color: #4f7092; padding: 20px; text-align: center;">Образование не указано</p>'
|
||
}
|
||
</div>
|
||
</div>
|
||
`;
|
||
} else {
|
||
// У соискателя нет резюме
|
||
contentTabs = `
|
||
<div class="content-tab active" onclick="switchTab('main')">Главная</div>
|
||
`;
|
||
|
||
mainContent = `
|
||
<div class="content-pane active" id="mainPane">
|
||
<div class="info-section" style="text-align: center; padding: 60px 20px;">
|
||
<i class="fas fa-file-alt" style="font-size: 64px; color: #3b82f6; margin-bottom: 20px;"></i>
|
||
<h3 style="color: #0b1c34; margin-bottom: 10px;">Резюме не заполнено</h3>
|
||
<p style="color: #4f7092;">Пользователь ещё не создал резюме</p>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
} else if (user.role === 'employer') {
|
||
if (company) {
|
||
// У работодателя есть компания
|
||
contentTabs = `
|
||
<div class="content-tab active" onclick="switchTab('main')">Главная</div>
|
||
<div class="content-tab" onclick="switchTab('company')">Компания</div>
|
||
<div class="content-tab" onclick="switchTab('vacancies')">Вакансии</div>
|
||
`;
|
||
|
||
mainContent = `
|
||
<div class="content-pane active" id="mainPane">
|
||
<div class="info-section">
|
||
<h3><i class="fas fa-building"></i> О компании</h3>
|
||
<div class="about-me">${escapeHtml(company?.description || 'Информация о компании не заполнена')}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="content-pane" id="companyPane">
|
||
<div class="company-info">
|
||
<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>
|
||
<div class="company-name">${escapeHtml(company?.name || 'Название не указано')}</div>
|
||
${company?.website ?
|
||
`<a href="${escapeHtml(company.website)}" target="_blank">${escapeHtml(company.website)}</a>` :
|
||
''
|
||
}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info-grid" style="margin-top: 20px;">
|
||
${company?.address ? `
|
||
<div class="info-card">
|
||
<div class="label">Адрес</div>
|
||
<div class="value">${escapeHtml(company.address)}</div>
|
||
</div>
|
||
` : ''}
|
||
${company?.phone ? `
|
||
<div class="info-card">
|
||
<div class="label">Телефон</div>
|
||
<div class="value">${escapeHtml(company.phone)}</div>
|
||
</div>
|
||
` : ''}
|
||
${company?.email ? `
|
||
<div class="info-card">
|
||
<div class="label">Email</div>
|
||
<div class="value">${escapeHtml(company.email)}</div>
|
||
</div>
|
||
` : ''}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="content-pane" id="vacanciesPane">
|
||
<h3 style="margin-bottom: 20px;">Вакансии компании</h3>
|
||
${vacancies.length > 0 ?
|
||
`<div class="vacancies-grid">
|
||
${vacancies.map(v => `
|
||
<div class="vacancy-card" onclick="window.location.href='/vacancy/${v.id}'">
|
||
<h4>${escapeHtml(v.title)}</h4>
|
||
<div class="vacancy-salary">${escapeHtml(v.salary || 'з/п не указана')}</div>
|
||
<div class="vacancy-footer">
|
||
<span><i class="fas fa-eye"></i> ${v.views || 0}</span>
|
||
<span>${v.is_active ? 'Активна' : 'Неактивна'}</span>
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
</div>` :
|
||
'<p style="color: #4f7092; text-align: center; padding: 40px;">Нет активных вакансий</p>'
|
||
}
|
||
</div>
|
||
`;
|
||
} else {
|
||
// У работодателя нет компании
|
||
contentTabs = `
|
||
<div class="content-tab active" onclick="switchTab('main')">Главная</div>
|
||
`;
|
||
|
||
mainContent = `
|
||
<div class="content-pane active" id="mainPane">
|
||
<div class="info-section" style="text-align: center; padding: 60px 20px;">
|
||
<i class="fas fa-building" style="font-size: 64px; color: #3b82f6; margin-bottom: 20px;"></i>
|
||
<h3 style="color: #0b1c34; margin-bottom: 10px;">Компания не зарегистрирована</h3>
|
||
<p style="color: #4f7092;">Пользователь ещё не добавил информацию о компании</p>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
}
|
||
|
||
container.innerHTML = `
|
||
<div class="profile-grid">
|
||
<!-- Боковая панель -->
|
||
<div class="profile-sidebar">
|
||
<div class="avatar">
|
||
<i class="fas fa-user-circle"></i>
|
||
</div>
|
||
<div style="display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap;">
|
||
<div class="profile-name">${escapeHtml(user.full_name)}</div>
|
||
<button class="qr-button" onclick="openQRModal()" title="QR-код профиля">
|
||
<i class="fas fa-qrcode"></i>
|
||
</button>
|
||
</div>
|
||
<div class="profile-role-badge ${roleClass}">${roleText}</div>
|
||
|
||
${stats}
|
||
|
||
<div class="contact-info">
|
||
${contacts}
|
||
</div>
|
||
|
||
${isOwnProfile ? `
|
||
<button class="btn btn-outline" style="width: 100%; margin-top: 20px;" onclick="window.location.href='/profile'">
|
||
<i class="fas fa-edit"></i> Редактировать профиль
|
||
</button>
|
||
` : ''}
|
||
</div>
|
||
|
||
<!-- Основной контент -->
|
||
<div class="profile-content">
|
||
<div class="content-tabs" id="contentTabs">
|
||
${contentTabs}
|
||
</div>
|
||
|
||
${mainContent}
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// Переключение табов
|
||
function switchTab(tab) {
|
||
document.querySelectorAll('.content-tab').forEach(t => t.classList.remove('active'));
|
||
document.querySelectorAll('.content-pane').forEach(p => p.classList.remove('active'));
|
||
|
||
const tabs = document.querySelectorAll('.content-tab');
|
||
const panes = document.querySelectorAll('.content-pane');
|
||
|
||
if (tab === 'main') {
|
||
tabs[0]?.classList.add('active');
|
||
document.getElementById('mainPane').classList.add('active');
|
||
} else if (tab === 'resume') {
|
||
tabs[1]?.classList.add('active');
|
||
document.getElementById('resumePane').classList.add('active');
|
||
} else if (tab === 'company') {
|
||
tabs[1]?.classList.add('active');
|
||
document.getElementById('companyPane').classList.add('active');
|
||
} else if (tab === 'vacancies') {
|
||
tabs[2]?.classList.add('active');
|
||
document.getElementById('vacanciesPane').classList.add('active');
|
||
}
|
||
}
|
||
|
||
// ========== ФУНКЦИИ ДЛЯ QR-КОДА ==========
|
||
|
||
// Открыть модальное окно с QR
|
||
function openQRModal() {
|
||
console.log('📱 Попытка открыть QR-модальное окно');
|
||
|
||
if (!currentProfileUser) {
|
||
console.error('❌ Данные пользователя не загружены');
|
||
showNotification('Данные пользователя не загружены', 'error');
|
||
return;
|
||
}
|
||
|
||
// Проверяем наличие модального окна
|
||
const modal = document.getElementById('qrModal');
|
||
if (!modal) {
|
||
console.error('❌ Модальное окно с id "qrModal" не найдено в DOM');
|
||
showNotification('Ошибка: модальное окно не найдено', 'error');
|
||
return;
|
||
}
|
||
|
||
// Получаем элементы
|
||
const qrUserName = document.getElementById('qrUserName');
|
||
const qrUserUrl = document.getElementById('qrUserUrl');
|
||
const qrViewCountElement = document.getElementById('qrViewCount'); // Переименовал, чтобы не конфликтовать
|
||
const qrUserRole = document.getElementById('qrUserRole');
|
||
const qrUserSince = document.getElementById('qrUserSince');
|
||
const qrCanvas = document.getElementById('qrCanvas');
|
||
|
||
// Проверяем наличие критически важных элементов
|
||
if (!qrUserName || !qrUserUrl || !qrViewCountElement || !qrUserRole || !qrUserSince || !qrCanvas) {
|
||
console.error('❌ Критические элементы модального окна не найдены');
|
||
console.log('qrUserName:', qrUserName);
|
||
console.log('qrUserUrl:', qrUserUrl);
|
||
console.log('qrViewCountElement:', qrViewCountElement);
|
||
console.log('qrUserRole:', qrUserRole);
|
||
console.log('qrUserSince:', qrUserSince);
|
||
console.log('qrCanvas:', qrCanvas);
|
||
showNotification('Ошибка отображения QR-кода', 'error');
|
||
return;
|
||
}
|
||
|
||
// Устанавливаем имя пользователя
|
||
qrUserName.textContent = escapeHtml(currentProfileUser.full_name);
|
||
|
||
// Формируем URL профиля
|
||
const profileUrl = window.location.origin + '/user/' + currentProfileUser.id;
|
||
qrUserUrl.textContent = profileUrl.replace('https://', '').replace('http://', '');
|
||
|
||
// Обновляем счетчик просмотров - используем отдельную переменную
|
||
if (typeof window.qrViewCounter === 'undefined') {
|
||
window.qrViewCounter = 0;
|
||
}
|
||
window.qrViewCounter++;
|
||
qrViewCountElement.textContent = window.qrViewCounter;
|
||
|
||
// Роль пользователя
|
||
let roleText = '';
|
||
if (currentProfileUser.role === 'employee') roleText = '👤 Соискатель';
|
||
else if (currentProfileUser.role === 'employer') roleText = '🏢 Работодатель';
|
||
else if (currentProfileUser.role === 'admin') roleText = '👑 Админ';
|
||
else roleText = '👤 Пользователь';
|
||
qrUserRole.textContent = roleText;
|
||
|
||
// Дата регистрации
|
||
if (currentProfileUser.created_at) {
|
||
try {
|
||
const date = new Date(currentProfileUser.created_at);
|
||
const now = new Date();
|
||
const months = Math.floor((now - date) / (1000 * 60 * 60 * 24 * 30));
|
||
qrUserSince.textContent = (months > 0 ? months : '< 1') + ' мес';
|
||
} catch (e) {
|
||
console.error('Ошибка при вычислении даты:', e);
|
||
qrUserSince.textContent = '—';
|
||
}
|
||
} else {
|
||
qrUserSince.textContent = '—';
|
||
}
|
||
|
||
// Генерируем QR-код
|
||
try {
|
||
generateQRCodeWithLogo(profileUrl);
|
||
} catch (e) {
|
||
console.error('Ошибка генерации QR-кода:', e);
|
||
showNotification('Ошибка генерации QR-кода', 'error');
|
||
}
|
||
|
||
// Показываем модальное окно
|
||
modal.classList.add('active');
|
||
console.log('✅ QR-модальное окно открыто, счетчик:', window.qrViewCounter);
|
||
}
|
||
|
||
// Закрыть модальное окно
|
||
function closeQRModal() {
|
||
const modal = document.getElementById('qrModal');
|
||
if (modal) {
|
||
modal.classList.remove('active');
|
||
}
|
||
}
|
||
|
||
// Генерация QR-кода с иконкой пользователя
|
||
function generateQRCodeWithLogo(text) {
|
||
const canvas = document.getElementById('qrCanvas');
|
||
const logoOverlay = document.getElementById('qrLogoOverlay');
|
||
const logoIcon = document.getElementById('qrLogoIcon');
|
||
|
||
if (!canvas) {
|
||
console.error('❌ Canvas для QR-кода не найден');
|
||
return;
|
||
}
|
||
|
||
// Настройки 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');
|
||
|
||
// Показываем оверлей с иконкой пользователя
|
||
if (logoOverlay) {
|
||
logoOverlay.style.display = 'flex';
|
||
}
|
||
if (logoIcon) {
|
||
logoIcon.style.display = 'block';
|
||
logoIcon.className = 'fas fa-user';
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// Скачать QR-код
|
||
function downloadQR() {
|
||
const canvas = document.getElementById('qrCanvas');
|
||
|
||
// Создаем временный 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);
|
||
|
||
// Рисуем иконку поверх
|
||
ctx.fillStyle = 'white';
|
||
ctx.beginPath();
|
||
ctx.arc(125, 125, 35, 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 для иконки user
|
||
|
||
// Скачиваем
|
||
const link = document.createElement('a');
|
||
const filename = `profile_${currentProfileUser.id}.png`;
|
||
link.download = filename;
|
||
link.href = combinedCanvas.toDataURL('image/png');
|
||
link.click();
|
||
|
||
showNotification('QR-код скачан', 'success');
|
||
}
|
||
|
||
// Копировать ссылку на профиль
|
||
function copyProfileLink() {
|
||
const url = window.location.origin + '/user/' + currentProfileUser.id;
|
||
navigator.clipboard.writeText(url).then(() => {
|
||
showNotification('Ссылка скопирована', 'success');
|
||
}).catch(() => {
|
||
showNotification('Ошибка копирования', 'error');
|
||
});
|
||
}
|
||
|
||
// Распечатать QR
|
||
function printQR() {
|
||
const canvas = document.getElementById('qrCanvas');
|
||
|
||
const printWindow = window.open('', '_blank');
|
||
|
||
printWindow.document.write(`
|
||
<html>
|
||
<head>
|
||
<title>QR-код профиля</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; }
|
||
.role { margin-bottom: 20px; color: #3b82f6; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h2>${escapeHtml(currentProfileUser.full_name)}</h2>
|
||
<div class="role">${currentProfileUser.role === 'employee' ? '👤 Соискатель' :
|
||
currentProfileUser.role === 'employer' ? '🏢 Работодатель' :
|
||
'👑 Администратор'}</div>
|
||
<img src="${canvas.toDataURL()}" />
|
||
<div class="url">${window.location.origin}/user/${currentProfileUser.id}</div>
|
||
</body>
|
||
</html>
|
||
`);
|
||
|
||
printWindow.document.close();
|
||
printWindow.focus();
|
||
printWindow.print();
|
||
}
|
||
|
||
// Поделиться QR в соцсетях
|
||
function shareQR(platform) {
|
||
const url = window.location.origin + '/user/' + currentProfileUser.id;
|
||
const text = `Профиль пользователя ${currentProfileUser.full_name} на 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('Профиль ' + currentProfileUser.full_name)}&body=${encodeURIComponent(text + '\n\n' + url)}`;
|
||
break;
|
||
}
|
||
|
||
if (shareUrl) {
|
||
window.open(shareUrl, '_blank');
|
||
}
|
||
}
|
||
|
||
// Закрытие модального окна по клику вне
|
||
window.onclick = function(event) {
|
||
const modal = document.getElementById('qrModal');
|
||
if (event.target === modal) {
|
||
modal.classList.remove('active');
|
||
}
|
||
};
|
||
|
||
// Инициализация
|
||
checkAuth().then(() => {
|
||
loadUserProfile();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |