Revert to version c584dca

This commit is contained in:
Flatlogic Bot 2025-09-28 00:21:32 +00:00
parent 935f41f960
commit fb3f1d33ed
6 changed files with 413 additions and 126 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules/
*/node_modules/
*/build/
.env

110
api.php Normal file
View File

@ -0,0 +1,110 @@
<?php
// api.php
header('Content-Type: application/json');
// --- Helper function to get environment variables ---
function get_env($key, $default = null) {
$path = __DIR__ . '/.env';
if (!file_exists($path)) {
return $default;
}
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
if (strpos(trim($line), '#') === 0) {
continue;
}
list($name, $value) = explode('=', $line, 2);
$name = trim($name);
$value = trim($value);
if ($name === $key) {
return $value;
}
}
return $default;
}
// --- Main Logic ---
// 1. Get API Key
$apiKey = get_env('GEMINI_API_KEY');
if (!$apiKey) {
echo json_encode(['reply' => 'Ошибка: API-ключ не найден. Пожалуйста, добавьте его в файл .env']);
exit;
}
// 2. Get user message from POST request
$input = json_decode(file_get_contents('php://input'), true);
$userMessage = trim($input['message'] ?? '');
if (empty($userMessage)) {
echo json_encode(['reply' => 'Пожалуйста, введите сообщение.']);
exit;
}
// 3. Load the knowledge base
$knowledgeBasePath = __DIR__ . '/db/knowledge_base.txt';
if (!file_exists($knowledgeBasePath)) {
echo json_encode(['reply' => 'Ошибка: База знаний не найдена.']);
exit;
}
$knowledgeBase = file_get_contents($knowledgeBasePath);
// 4. Prepare the prompt for the AI
$prompt = "Ты — ИИ-ассистент, специалист по внутренней системе управления складом под названием HUB. Твоя задача — отвечать на вопросы пользователя, основываясь ИСКЛЮЧИТЕЛЬНО на предоставленной базе знаний. Не придумывай ничего от себя. Если ответа в базе знаний нет, вежливо сообщи, что ты можешь отвечать только на вопросы, связанные с системой HUB.\n\nВот база знаний:\n---\n" . $knowledgeBase . "\n---\n\nВопрос пользователя: \"" . $userMessage . "\"";
// 5. Call the Gemini API
$url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=' . $apiKey;
$data = [
'contents' => [
[
'parts' => [
['text' => $prompt]
]
]
]
];
$options = [
'http' => [
'header' => "Content-type: application/json\r\n",
'method' => 'POST',
'content' => json_encode($data),
'ignore_errors' => true // To see the error message from the API
]
];
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
$http_response_header = $http_response_header ?? [];
// 6. Process the response
if ($response === FALSE) {
$error = 'Не удалось связаться с API. ';
// Check for more specific errors if possible
$last_error = error_get_last();
if ($last_error) {
$error .= $last_error['message'];
}
// Try to get the HTTP status and response body
$status_line = $http_response_header[0] ?? 'HTTP/1.1 500 Internal Server Error';
preg_match('{HTTP/\S+\s(\d+)}', $status_line, $match);
$status = $match[1] ?? 500;
error_log("Gemini API Error: Status $status, Response: $response");
echo json_encode(['reply' => "Ошибка при обращении к сервису ИИ. Статус: $status. Пожалуйста, проверьте ключ API и настройки сервера."]);
} else {
$result = json_decode($response, true);
if (isset($result['candidates'][0]['content']['parts'][0]['text'])) {
$reply = $result['candidates'][0]['content']['parts'][0]['text'];
echo json_encode(['reply' => $reply]);
} else {
// Log the actual error response from the API for debugging
error_log("Gemini API - Unexpected response structure: " . $response);
echo json_encode(['reply' => 'Получен неожиданный ответ от сервиса ИИ. Возможно, проблема с конфигурацией или ключом API.']);
}
}

163
assets/css/custom.css Normal file
View File

@ -0,0 +1,163 @@
:root {
--bg-color: #121212;
--surface-color: #1E1E1E;
--primary-color: #00FF9B;
--secondary-color: #00C2FF;
--text-color: #E0E0E0;
--text-muted-color: #888;
--border-radius-md: 12px;
--border-radius-sm: 8px;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: 'Roboto Mono', 'Consolas', 'Monaco', monospace;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}
.chat-container {
width: 100%;
max-width: 800px;
height: 100vh;
max-height: 100vh; /* For mobile browsers */
display: flex;
flex-direction: column;
background-color: var(--bg-color);
border-radius: 0;
box-shadow: 0 0 40px rgba(0, 255, 155, 0.1);
overflow: hidden;
}
.chat-header {
padding: 16px 24px;
background-color: var(--surface-color);
border-bottom: 1px solid #333;
text-align: center;
}
.chat-header h1 {
font-size: 1.5rem;
font-weight: 500;
margin: 0;
background: linear-gradient(90deg, var(--primary-color), var(--secondary-color));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
.chat-header .text-muted {
font-size: 0.85rem;
color: var(--text-muted-color);
margin: 0;
}
.chat-messages {
flex-grow: 1;
padding: 24px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 16px;
}
/* Custom scrollbar for webkit browsers */
.chat-messages::-webkit-scrollbar {
width: 6px;
}
.chat-messages::-webkit-scrollbar-track {
background: var(--bg-color);
}
.chat-messages::-webkit-scrollbar-thumb {
background-color: var(--surface-color);
border-radius: 6px;
}
.message {
display: flex;
max-width: 75%;
}
.message-content {
padding: 12px 16px;
border-radius: var(--border-radius-md);
line-height: 1.5;
}
.message-content p {
margin: 0;
}
.bot-message {
align-self: flex-start;
}
.bot-message .message-content {
background-color: var(--surface-color);
border-top-left-radius: 0;
}
.user-message {
align-self: flex-end;
}
.user-message .message-content {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: #000;
border-top-right-radius: 0;
}
.chat-input-area {
padding: 16px 24px;
background-color: var(--surface-color);
border-top: 1px solid #333;
}
#chat-form .form-control {
background-color: #333;
border: 1px solid #444;
color: var(--text-color);
border-radius: var(--border-radius-sm);
padding: 12px 16px;
height: 48px;
}
#chat-form .form-control:focus {
background-color: #333;
color: var(--text-color);
box-shadow: 0 0 0 2px var(--primary-color);
border-color: var(--primary-color);
}
#chat-form .form-control::placeholder {
color: var(--text-muted-color);
}
#chat-form .btn {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
border: none;
border-radius: var(--border-radius-sm);
margin-left: 12px;
width: 48px;
height: 48px;
display: flex;
justify-content: center;
align-items: center;
transition: transform 0.2s ease;
}
#chat-form .btn:hover {
transform: scale(1.05);
}
#chat-form .btn svg {
color: #000;
}

84
assets/js/main.js Normal file
View File

@ -0,0 +1,84 @@
document.addEventListener('DOMContentLoaded', () => {
const chatForm = document.getElementById('chat-form');
const messageInput = document.getElementById('message-input');
const chatMessages = document.getElementById('chat-messages');
chatForm.addEventListener('submit', (e) => {
e.preventDefault();
const messageText = messageInput.value.trim();
if (messageText) {
appendMessage(messageText, 'user');
messageInput.value = '';
showBotTyping();
// Send message to backend
fetch('api.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ message: messageText })
})
.then(response => response.json())
.then(data => {
removeBotTyping();
appendMessage(data.reply, 'bot');
})
.catch(error => {
removeBotTyping();
console.error('Error:', error);
appendMessage('Извините, произошла ошибка. Попробуйте еще раз.', 'bot');
});
}
});
function appendMessage(text, sender) {
const messageWrapper = document.createElement('div');
messageWrapper.classList.add('message', `${sender}-message`);
const messageContent = document.createElement('div');
messageContent.classList.add('message-content');
const paragraph = document.createElement('p');
paragraph.innerHTML = text; // Use innerHTML to render potential HTML tags from response
messageContent.appendChild(paragraph);
messageWrapper.appendChild(messageContent);
chatMessages.appendChild(messageWrapper);
scrollToBottom();
}
function showBotTyping() {
const typingIndicator = document.createElement('div');
typingIndicator.id = 'typing-indicator';
typingIndicator.classList.add('message', 'bot-message');
const content = document.createElement('div');
content.classList.add('message-content');
const p = document.createElement('p');
p.textContent = '...';
content.appendChild(p);
typingIndicator.appendChild(content);
chatMessages.appendChild(typingIndicator);
scrollToBottom();
}
function removeBotTyping() {
const typingIndicator = document.getElementById('typing-indicator');
if (typingIndicator) {
typingIndicator.remove();
}
}
function scrollToBottom() {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// Initial scroll to bottom if content is already there
scrollToBottom();
});

1
db/knowledge_base.txt Normal file
View File

@ -0,0 +1 @@
Это БАЗА знаний 1. Введение в систему HUB Цель: Понять назначение HUB и его роль в управлении складскими данными. HUB — это система для управления складскими предметами (грузами, контейнерами, коробками и т.д.) и процессами их перемещения, хранения и инвентаризации. Она позволяет отслеживать состояние предметов, их историю, взаимодействие с контейнерами и логистическими точками, а также проводить пересчеты и настраивать складскую инфраструктуру. Ключевые понятия: Предмет: Объект хранения (например, ТЯ, коробка, отправление). Логистический контейнер (ЛК): Емкость для хранения/перемещения предметов (статусы: "Формируется", "Сформирован" и др.). Инвентаризация/пересчет: Проверка фактического наличия предметов на складе против учетных данных. КМО: Внутренний термин, предположительно связанный с документацией о материальных ошибках/недостачах (например, "Книга Материальных Остатков"). ЦМН: Цель доставки (место назначения предмета). Тайм-слот: Временной интервал для доставки. 2. Основы интерфейса HUB и карточка предмета Цель: Знакомство с интерфейсом HUB и структурой карточки предмета. HUB содержит главное меню, схему склада и таблицу со списком предметов. Основная работа ведется через карточку предмета — детализированное окно с информацией о конкретном объекте. 2.1 Структура карточки предмета Карточка доступна при клике на ID предмета в таблице "Предметы". Она включает следующие вкладки: Вкладка Описание Основные данные «О предмете» Основная информация о предмете. Тип, статус, ID, номенклатура, принадлежность, стоимость, адрес хранения. «Состав» Текущий и исторический состав контейнера (для предметов, являющихся контейнерами). Список предметов внутри, даты добавления/удаления, статусы элементов состава. «История (перемещения)» История перемещений предмета между ячейками, контейнерами и логистическими точками. Тип события (принято, перемещено, вошло/вышло из контейнера), дата, источник/цель. «Отгрузки» Запись о выдаче предмета (отгрузке) со склада. Дата отгрузки, получате

168
index.php
View File

@ -1,131 +1,59 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<html lang="en">
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Your Logistic Koresh</title>
<meta name="description" content="AI Assistant for HUB System">
<meta name="robots" content="noindex, nofollow">
<!-- Open Graph -->
<meta property="og:title" content="Your Logistic Koresh">
<meta property="og:description" content="AI Assistant for HUB System">
<meta property="og:type" content="website">
<meta property="og:url" content=""> <!-- Will be the current URL -->
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<!-- Custom CSS -->
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
<div class="chat-container">
<header class="chat-header">
<h1>Your Logistic Koresh</h1>
<p class="text-muted">AI Assistant for HUB System</p>
</header>
<main class="chat-messages" id="chat-messages">
<!-- Messages will be appended here by JS -->
<div class="message bot-message">
<div class="message-content">
<p>Привет. Я твой логистический кореш. Я здесь, чтобы помочь тебе с вопросами по системе HUB. Спрашивай, не стесняйся.</p>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWiZZy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
<footer class="chat-input-area">
<form id="chat-form" class="d-flex">
<input type="text" id="message-input" class="form-control" placeholder="Введите ваш вопрос..." autocomplete="off" required>
<button type="submit" class="btn btn-primary" id="send-button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-send"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
</button>
</form>
</footer>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<!-- Custom JS -->
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>