diff --git a/server.py b/server.py index c4c3f90..e20a35f 100644 --- a/server.py +++ b/server.py @@ -2161,6 +2161,125 @@ async def create_or_update_company( return dict(cursor.fetchone()) +@app.get("/api/admin/companies") +async def get_all_companies_admin( + user_id: int = Depends(get_current_user), + page: int = 1, + limit: int = 10, + search: str = None +): + """Получение всех компаний для админки""" + try: + print(f"👑 Админ {user_id} запрашивает список компаний") + + with get_db() as conn: + cursor = conn.cursor() + + # Проверка прав администратора + cursor.execute("SELECT is_admin FROM users WHERE id = ?", (user_id,)) + user = cursor.fetchone() + if not user or not user["is_admin"]: + raise HTTPException(status_code=403, detail="Доступ запрещен") + + # Базовый запрос + query = """ + SELECT + c.*, + u.full_name as owner_name, + (SELECT COUNT(*) FROM vacancies WHERE user_id = c.user_id AND is_active = 1) as vacancies_count + FROM companies c + JOIN users u ON c.user_id = u.id + WHERE 1=1 + """ + params = [] + + # Поиск + if search: + query += " AND (c.name LIKE ? OR c.email LIKE ? OR u.full_name LIKE ?)" + search_term = f"%{search}%" + params.extend([search_term, search_term, search_term]) + + # Сортировка + query += " ORDER BY c.created_at DESC" + + # Пагинация + offset = (page - 1) * limit + query += " LIMIT ? OFFSET ?" + params.extend([limit, offset]) + + cursor.execute(query, params) + companies = cursor.fetchall() + + # Получаем общее количество + count_query = "SELECT COUNT(*) FROM companies" + if search: + count_query += " WHERE name LIKE ? OR email LIKE ?" + cursor.execute(count_query, [search_term, search_term]) + else: + cursor.execute(count_query) + total = cursor.fetchone()[0] + + result = [dict(c) for c in companies] + + return { + "companies": result, + "total": total, + "page": page, + "total_pages": (total + limit - 1) // limit, + "limit": limit + } + + except HTTPException: + raise + except Exception as e: + print(f"❌ Ошибка при загрузке компаний: {e}") + traceback.print_exc() + raise HTTPException(status_code=500, detail=f"Внутренняя ошибка: {str(e)}") + + +@app.delete("/api/admin/companies/{company_id}") +async def delete_company_admin( + company_id: int, + user_id: int = Depends(get_current_user) +): + """Удаление компании (только для админа)""" + try: + print(f"👑 Админ {user_id} пытается удалить компанию {company_id}") + + with get_db() as conn: + cursor = conn.cursor() + + # Проверка прав администратора + cursor.execute("SELECT is_admin FROM users WHERE id = ?", (user_id,)) + user = cursor.fetchone() + if not user or not user["is_admin"]: + raise HTTPException(status_code=403, detail="Доступ запрещен") + + # Проверяем существование компании + cursor.execute("SELECT user_id FROM companies WHERE id = ?", (company_id,)) + company = cursor.fetchone() + if not company: + raise HTTPException(status_code=404, detail="Компания не найдена") + + # Удаляем вакансии компании + cursor.execute("DELETE FROM vacancies WHERE user_id = ?", (company["user_id"],)) + + # Удаляем компанию + cursor.execute("DELETE FROM companies WHERE id = ?", (company_id,)) + + conn.commit() + print(f"✅ Компания {company_id} успешно удалена") + + return {"message": "Компания успешно удалена"} + + except HTTPException: + raise + except Exception as e: + print(f"❌ Ошибка при удалении компании {company_id}: {e}") + traceback.print_exc() + raise HTTPException(status_code=500, detail=f"Внутренняя ошибка: {str(e)}") + + @app.get("/api/companies/{company_id}") async def get_company_by_id(company_id: int): """Получение информации о компании по ID (публичный эндпоинт)""" diff --git a/templates/admin.html b/templates/admin.html index ad81701..59b1f81 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -284,29 +284,23 @@ background: #bae6fd; } - .tags-cloud { - display: flex; - flex-wrap: wrap; - gap: 10px; - margin: 20px 0; - } - - .tag-item { + .company-logo { + width: 40px; + height: 40px; background: #eef4fa; - padding: 8px 16px; - border-radius: 30px; + border-radius: 10px; display: flex; align-items: center; - gap: 8px; - font-size: 14px; + justify-content: center; + font-size: 20px; + color: #3b82f6; + overflow: hidden; } - .tag-item .count { - background: #3b82f6; - color: white; - padding: 2px 8px; - border-radius: 20px; - font-size: 12px; + .company-logo img { + width: 100%; + height: 100%; + object-fit: cover; } .loading { @@ -402,26 +396,6 @@ color: #0b1c34; } - .experience-list, .education-list { - margin-top: 10px; - } - - .exp-item, .edu-item { - background: #f9fcff; - padding: 15px; - border-radius: 15px; - margin-bottom: 10px; - } - - .exp-item strong, .edu-item strong { - color: #0b1c34; - } - - .exp-item .period, .edu-item .year { - color: #4f7092; - font-size: 12px; - } - .notification { position: fixed; top: 20px; @@ -488,90 +462,6 @@ grid-template-columns: 1fr; } } - - /* Стили для ссылок на профили */ - .user-link { - display: flex; - align-items: center; - gap: 8px; - color: #0b1c34; - text-decoration: none; - font-weight: 600; - padding: 4px 8px; - border-radius: 30px; - transition: 0.2s; - } - - .user-link:hover { - background: #eef4fa; - color: #0b1c34; - } - - .user-link i { - color: #3b82f6; - font-size: 18px; - } - - .user-link .external-icon { - font-size: 12px; - color: #4f7092; - opacity: 0.5; - transition: 0.2s; - } - - .user-link:hover .external-icon { - opacity: 1; - color: #3b82f6; - } - - /* Стили для кнопок действий */ - .action-buttons { - display: flex; - gap: 5px; - justify-content: center; - } - - .btn-icon { - width: 32px; - height: 32px; - border-radius: 16px; - border: none; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: 0.2s; - background: transparent; - color: #4f7092; - } - - .btn-icon:hover { - background: #eef4fa; - transform: scale(1.1); - } - - .btn-icon.view:hover { - background: #dbeafe; - color: #1e40af; - } - - .btn-icon.delete:hover { - background: #fee2e2; - color: #b91c1c; - } - - /* Email ссылка */ - .email-link { - color: #3b82f6; - text-decoration: none; - display: inline-flex; - align-items: center; - gap: 5px; - } - - .email-link:hover { - text-decoration: underline; - } @@ -619,6 +509,7 @@ + @@ -709,11 +600,41 @@ + + +
- - + +
- -