AI analysis v1
This commit is contained in:
parent
b96dfdc8af
commit
89e735575c
207
assets/css/custom.css
Normal file
207
assets/css/custom.css
Normal file
@ -0,0 +1,207 @@
|
||||
/* General Body Styling */
|
||||
body {
|
||||
background-color: #F3F4F6;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
color: #111827;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Main Chat Container */
|
||||
.chat-container {
|
||||
width: 100%;
|
||||
max-width: 768px;
|
||||
height: 90vh;
|
||||
max-height: 800px;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Chat Header */
|
||||
.chat-header {
|
||||
padding: 1rem 1.5rem;
|
||||
background: linear-gradient(45deg, #4F46E5, #6366F1);
|
||||
color: white;
|
||||
border-bottom: 1px solid #E5E7EB;
|
||||
text-align: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.chat-header h1 {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Message List */
|
||||
.message-list {
|
||||
flex-grow: 1;
|
||||
padding: 1.5rem;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
/* Individual Message Styling */
|
||||
.message {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 0.75rem;
|
||||
max-width: 85%;
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.message .avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: #E5E7EB;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.message .message-content {
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* AI (Bot) Message */
|
||||
.message.bot {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.message.bot .avatar {
|
||||
background-color: #4F46E5;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.message.bot .message-content {
|
||||
background-color: #EEF2FF;
|
||||
color: #3730A3;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
/* User Message */
|
||||
.message.user {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.message.user .message-content {
|
||||
background-color: #10B981;
|
||||
color: white;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
.message.user .avatar {
|
||||
display: none; /* Hide avatar for user messages for a cleaner look */
|
||||
}
|
||||
|
||||
|
||||
/* Input Area */
|
||||
.input-area {
|
||||
padding: 1rem 1.5rem;
|
||||
border-top: 1px solid #E5E7EB;
|
||||
background-color: #F9FAFB;
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.input-area input {
|
||||
flex-grow: 1;
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid #D1D5DB;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.input-area input:focus {
|
||||
outline: none;
|
||||
border-color: #4F46E5;
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.2);
|
||||
}
|
||||
|
||||
.input-area button {
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: none;
|
||||
background-color: #4F46E5;
|
||||
color: white;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.input-area button:hover {
|
||||
background-color: #4338CA;
|
||||
}
|
||||
|
||||
.input-area button:disabled {
|
||||
background-color: #A5B4FC;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
#analyze-button {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
border: none;
|
||||
background-color: #10B981; /* Emerald Green */
|
||||
color: white;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
#analyze-button:hover {
|
||||
background-color: #059669;
|
||||
}
|
||||
|
||||
/* Typing indicator */
|
||||
.typing-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
}
|
||||
.typing-indicator span {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
background-color: #9E9E9E;
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
opacity: 0.4;
|
||||
animation: 1s blink infinite;
|
||||
}
|
||||
.typing-indicator span:nth-child(2) { animation-delay: .2s; }
|
||||
.typing-indicator span:nth-child(3) { animation-delay: .4s; }
|
||||
|
||||
@keyframes blink {
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
135
assets/js/main.js
Normal file
135
assets/js/main.js
Normal file
@ -0,0 +1,135 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const questions = [
|
||||
"I wake up feeling tired, even after a full night’s sleep.",
|
||||
"By midday, I already feel mentally drained and out of energy.",
|
||||
"I feel emotionally numb, detached, or “on autopilot” most of the time.",
|
||||
"Things that used to excite me now feel pointless or like a chore.",
|
||||
"I feel irritated or cynical about people I work or study with.",
|
||||
"I struggle to focus and constantly procrastinate, even on important tasks.",
|
||||
"I feel guilty for not doing “enough”, no matter how much I actually do.",
|
||||
"I often think about quitting everything for a while or disappearing from social media and work.",
|
||||
"I use caffeine, sugar, nicotine, alcohol, or scrolling to “numb out” instead of resting.",
|
||||
"I feel like my life is just surviving, not really living."
|
||||
];
|
||||
|
||||
const messageList = document.getElementById('message-list');
|
||||
const inputArea = document.getElementById('input-area');
|
||||
const userInput = document.getElementById('user-input');
|
||||
const sendButton = document.getElementById('send-button');
|
||||
|
||||
let currentQuestionIndex = 0;
|
||||
const userAnswers = [];
|
||||
|
||||
function addMessage(text, sender) {
|
||||
const messageElement = document.createElement('div');
|
||||
messageElement.classList.add('message', sender);
|
||||
|
||||
const avatar = document.createElement('div');
|
||||
avatar.classList.add('avatar');
|
||||
avatar.textContent = sender === 'bot' ? 'AI' : 'You';
|
||||
|
||||
const messageContent = document.createElement('div');
|
||||
messageContent.classList.add('message-content');
|
||||
messageContent.textContent = text;
|
||||
|
||||
if (sender === 'bot') {
|
||||
messageElement.appendChild(avatar);
|
||||
}
|
||||
messageElement.appendChild(messageContent);
|
||||
|
||||
messageList.appendChild(messageElement);
|
||||
messageList.scrollTop = messageList.scrollHeight;
|
||||
}
|
||||
|
||||
function showTypingIndicator() {
|
||||
const typingElement = document.createElement('div');
|
||||
typingElement.id = 'typing-indicator';
|
||||
typingElement.classList.add('message', 'bot');
|
||||
typingElement.innerHTML = `
|
||||
<div class="avatar">AI</div>
|
||||
<div class="message-content">
|
||||
<div class="typing-indicator">
|
||||
<span></span><span></span><span></span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
messageList.appendChild(typingElement);
|
||||
messageList.scrollTop = messageList.scrollHeight;
|
||||
}
|
||||
|
||||
function removeTypingIndicator() {
|
||||
const typingElement = document.getElementById('typing-indicator');
|
||||
if (typingElement) {
|
||||
typingElement.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function askNextQuestion() {
|
||||
if (currentQuestionIndex < questions.length) {
|
||||
showTypingIndicator();
|
||||
setTimeout(() => {
|
||||
removeTypingIndicator();
|
||||
addMessage(questions[currentQuestionIndex], 'bot');
|
||||
userInput.disabled = false;
|
||||
sendButton.disabled = false;
|
||||
userInput.focus();
|
||||
}, 1000); // Simulate AI "thinking"
|
||||
} else {
|
||||
showTypingIndicator();
|
||||
setTimeout(() => {
|
||||
removeTypingIndicator();
|
||||
addMessage("Thank you for your responses. Click the button below to see your analysis.", 'bot');
|
||||
|
||||
// Create a form to submit the results
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = 'results.php';
|
||||
|
||||
const hiddenInput = document.createElement('input');
|
||||
hiddenInput.type = 'hidden';
|
||||
hiddenInput.name = 'conversation';
|
||||
hiddenInput.value = JSON.stringify(userAnswers);
|
||||
form.appendChild(hiddenInput);
|
||||
|
||||
const submitButton = document.createElement('button');
|
||||
submitButton.type = 'submit';
|
||||
submitButton.textContent = 'Analyze My Results';
|
||||
submitButton.id = 'analyze-button';
|
||||
form.appendChild(submitButton);
|
||||
|
||||
inputArea.innerHTML = ''; // Clear the input field and send button
|
||||
inputArea.appendChild(form);
|
||||
inputArea.style.display = 'flex';
|
||||
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
|
||||
function handleUserInput() {
|
||||
const text = userInput.value.trim();
|
||||
if (text === '') return;
|
||||
|
||||
addMessage(text, 'user');
|
||||
userAnswers.push({ question: questions[currentQuestionIndex], answer: text });
|
||||
|
||||
userInput.value = '';
|
||||
userInput.disabled = true;
|
||||
sendButton.disabled = true;
|
||||
|
||||
currentQuestionIndex++;
|
||||
askNextQuestion();
|
||||
}
|
||||
|
||||
sendButton.addEventListener('click', handleUserInput);
|
||||
userInput.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleUserInput();
|
||||
}
|
||||
});
|
||||
|
||||
// Start the conversation
|
||||
setTimeout(() => {
|
||||
addMessage("Hello! I'm here to help you assess your level of burnout. I'll ask you 10 questions. Please answer them honestly.", 'bot');
|
||||
askNextQuestion();
|
||||
}, 500);
|
||||
});
|
||||
40
followup.php
Normal file
40
followup.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/ai/LocalAIApi.php';
|
||||
|
||||
$request_body = file_get_contents('php://input');
|
||||
$data = json_decode($request_body, true);
|
||||
|
||||
$conversation = $data['conversation'] ?? null;
|
||||
$analysis = $data['analysis'] ?? null;
|
||||
$follow_up_question = $data['follow_up_question'] ?? null;
|
||||
|
||||
if ($conversation && $analysis && $follow_up_question) {
|
||||
$prompt = "The user has received an initial burnout analysis and has a follow-up question. Here is the context:\n\n";
|
||||
$prompt .= "**Original Survey Answers:**\n";
|
||||
foreach ($conversation as $item) {
|
||||
$prompt .= "Q: " . $item['question'] . "\nA: " . $item['answer'] . "\n";
|
||||
}
|
||||
$prompt .= "\n**Initial AI Analysis:**\n" . strip_tags($analysis) . "\n";
|
||||
$prompt .= "\n**User's Follow-up Question:**\n" . $follow_up_question . "\n";
|
||||
$prompt .= "Please provide a concise and helpful response to the user's question, keeping the context in mind. Format the response as simple HTML.";
|
||||
|
||||
$resp = LocalAIApi::createResponse(
|
||||
[
|
||||
'input' => [
|
||||
['role' => 'system', 'content' => 'You are a helpful assistant specializing in mental health and burnout analysis.'],
|
||||
['role' => 'user', 'content' => $prompt],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($resp['success'])) {
|
||||
echo LocalAIApi::extractText($resp);
|
||||
} else {
|
||||
echo "Sorry, I couldn't process your request. Please try again later.";
|
||||
}
|
||||
} else {
|
||||
http_response_code(400);
|
||||
echo "Invalid request. Missing data.";
|
||||
}
|
||||
|
||||
176
index.php
176
index.php
@ -1,150 +1,42 @@
|
||||
<?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>
|
||||
?><!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<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>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Burnout Survey & Analysis</title>
|
||||
<meta name="description" content="AI-driven burnout survey that asks questions conversationally, analyzes responses, and provides structured results and charts. Built with Flatlogic Generator.">
|
||||
<meta name="keywords" content="burnout survey, mental health, ai chatbot, stress analysis, employee wellness, self-assessment, mental wellbeing, burnout prevention, ai health, personalized advice, Flatlogic Generator">
|
||||
|
||||
<meta property="og:title" content="Burnout Survey & Analysis">
|
||||
<meta property="og:description" content="AI-driven burnout survey that asks questions conversationally, analyzes responses, and provides structured results and charts.">
|
||||
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? ''); ?>">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? ''); ?>">
|
||||
|
||||
<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;600&display=swap" rel="stylesheet">
|
||||
|
||||
<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>
|
||||
<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 class="chat-container">
|
||||
<header class="chat-header">
|
||||
<h1>Burnout Assessment</h1>
|
||||
</header>
|
||||
<main id="message-list" class="message-list">
|
||||
<!-- Messages will be dynamically inserted here -->
|
||||
</main>
|
||||
<footer id="input-area" class="input-area">
|
||||
<input type="text" id="user-input" placeholder="Type your answer..." autocomplete="off" disabled>
|
||||
<button id="send-button" disabled>Send</button>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
254
results.php
Normal file
254
results.php
Normal file
@ -0,0 +1,254 @@
|
||||
<?php
|
||||
// results.php
|
||||
|
||||
require_once __DIR__ . '/ai/LocalAIApi.php';
|
||||
|
||||
$conversation_json = $_POST['conversation'] ?? '';
|
||||
$conversation = json_decode($conversation_json, true);
|
||||
|
||||
$ai_response_text = '';
|
||||
$chart_data_json = '';
|
||||
$error_message = '';
|
||||
|
||||
if ($conversation) {
|
||||
$prompt = "Analyze the following conversation for signs of burnout. The user was asked 10 questions related to burnout. Based on their answers, provide a thoughtful analysis and actionable recommendations. Return a JSON object with two keys: 'analysis_html' and 'chart_data'.\n\n1. `analysis_html`: An HTML string containing a detailed analysis, identifying key themes (e.g., exhaustion, cynicism, inefficacy), a summary of the user's burnout level, and 3-5 actionable recommendations. Use headings, lists, and bold text for readability. Do not include `<html>` or `<body>` tags.\n2. `chart_data`: A JSON object with two keys: `labels` (an array of strings for the chart categories, e.g., ['Exhaustion', 'Cynicism', 'Inefficacy']) and `scores` (an array of integers from 0 to 10 representing the user's score in each category).\n\nHere is the conversation:
|
||||
";
|
||||
foreach ($conversation as $item) {
|
||||
$prompt .= "Q: " . $item['question'] . "\nA: " . $item['answer'] . "\n\n";
|
||||
}
|
||||
|
||||
$resp = LocalAIApi::createResponse(
|
||||
[
|
||||
'input' => [
|
||||
['role' => 'system', 'content' => 'You are a helpful assistant specializing in mental health and burnout analysis. You always respond with a valid JSON object.'],
|
||||
['role' => 'user', 'content' => $prompt],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($resp['success'])) {
|
||||
$ai_response_raw = LocalAIApi::extractText($resp);
|
||||
$ai_response_data = json_decode($ai_response_raw, true);
|
||||
if ($ai_response_data && isset($ai_response_data['analysis_html']) && isset($ai_response_data['chart_data'])) {
|
||||
$ai_response_text = $ai_response_data['analysis_html'];
|
||||
$chart_data_json = json_encode($ai_response_data['chart_data']);
|
||||
} else {
|
||||
$error_message = 'AI response was not in the expected format. Please try again.';
|
||||
// For debugging: $error_message .= "<br>Raw response: " . htmlspecialchars($ai_response_raw);
|
||||
}
|
||||
} else {
|
||||
$error_message = 'Failed to get response from AI. Error: ' . ($resp['error'] ?? 'Unknown error');
|
||||
}
|
||||
} else {
|
||||
$error_message = 'No conversation data received.';
|
||||
}
|
||||
|
||||
?><!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Your Burnout Analysis Results</title>
|
||||
<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;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<style>
|
||||
.results-container {
|
||||
width: 100%;
|
||||
max-width: 768px;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
padding: 2rem;
|
||||
text-align: left;
|
||||
}
|
||||
.loader {
|
||||
border: 4px solid #f3f4f6;
|
||||
border-radius: 50%;
|
||||
border-top: 4px solid #4F46E5;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 2rem auto;
|
||||
}
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
.ai-content h2 {
|
||||
font-size: 1.5rem;
|
||||
color: #4F46E5;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.ai-content ul {
|
||||
list-style-position: inside;
|
||||
padding-left: 0;
|
||||
}
|
||||
.ai-content li {
|
||||
margin-bottom: 0.75rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
#burnoutChart {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="chat-container">
|
||||
<header class="chat-header">
|
||||
<h1>Your Burnout Analysis</h1>
|
||||
</header>
|
||||
<main class="message-list" id="results-main">
|
||||
<div class="results-container">
|
||||
<?php if ($error_message): ?>
|
||||
<p style="color: red;"><?php echo htmlspecialchars($error_message); ?></p>
|
||||
<?php elseif ($ai_response_text): ?>
|
||||
<canvas id="burnoutChart"></canvas>
|
||||
<div class="ai-content">
|
||||
<?php echo $ai_response_text; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p>Analyzing your responses...</p>
|
||||
<div class="loader"></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
<?php if (!$error_message && $ai_response_text): ?>
|
||||
<footer id="input-area" class="input-area">
|
||||
<input type="text" id="user-input" placeholder="Ask a follow-up question..." autocomplete="off">
|
||||
<button id="send-button">Send</button>
|
||||
</footer>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($chart_data_json): ?>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const chartData = <?php echo $chart_data_json; ?>;
|
||||
const ctx = document.getElementById('burnoutChart').getContext('2d');
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'radar',
|
||||
data: {
|
||||
labels: chartData.labels,
|
||||
datasets: [{
|
||||
label: 'Burnout Score',
|
||||
data: chartData.scores,
|
||||
backgroundColor: 'rgba(79, 70, 229, 0.2)',
|
||||
borderColor: '#4F46E5',
|
||||
pointBackgroundColor: '#4F46E5',
|
||||
pointBorderColor: '#fff',
|
||||
pointHoverBackgroundColor: '#fff',
|
||||
pointHoverBorderColor: '#4F46E5'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
scales: {
|
||||
r: {
|
||||
angleLines: {
|
||||
color: '#E5E7EB'
|
||||
},
|
||||
grid: {
|
||||
color: '#E5E7EB'
|
||||
},
|
||||
pointLabels: {
|
||||
font: {
|
||||
size: 14,
|
||||
family: 'Inter'
|
||||
},
|
||||
color: '#111827'
|
||||
},
|
||||
ticks: {
|
||||
backdropColor: '#f3f4f6',
|
||||
color: '#6B7280',
|
||||
stepSize: 2,
|
||||
beginAtZero: true,
|
||||
max: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!$error_message && $ai_response_text): ?>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const sendButton = document.getElementById('send-button');
|
||||
const userInput = document.getElementById('user-input');
|
||||
const mainContainer = document.getElementById('results-main');
|
||||
const originalConversation = <?php echo json_encode($conversation); ?>;
|
||||
const originalAnalysis = <?php echo json_encode($ai_response_text); ?>;
|
||||
|
||||
function addMessageToUI(text, sender) {
|
||||
const messageElement = document.createElement('div');
|
||||
messageElement.classList.add('message', sender);
|
||||
|
||||
const avatar = document.createElement('div');
|
||||
avatar.classList.add('avatar');
|
||||
avatar.textContent = sender === 'bot' ? 'AI' : 'You';
|
||||
|
||||
const messageContent = document.createElement('div');
|
||||
messageContent.classList.add('message-content');
|
||||
messageContent.innerHTML = text; // Use innerHTML to render formatted AI responses
|
||||
|
||||
if (sender === 'bot') {
|
||||
messageElement.appendChild(avatar);
|
||||
}
|
||||
messageElement.appendChild(messageContent);
|
||||
|
||||
mainContainer.appendChild(messageElement);
|
||||
mainContainer.scrollTop = mainContainer.scrollHeight;
|
||||
}
|
||||
|
||||
async function handleFollowUp() {
|
||||
const userText = userInput.value.trim();
|
||||
if (userText === '') return;
|
||||
|
||||
addMessageToUI(userText, 'user');
|
||||
userInput.value = '';
|
||||
sendButton.disabled = true;
|
||||
|
||||
const response = await fetch('followup.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
conversation: originalConversation,
|
||||
analysis: originalAnalysis,
|
||||
follow_up_question: userText
|
||||
})
|
||||
});
|
||||
|
||||
const aiResponseText = await response.text();
|
||||
addMessageToUI(aiResponseText, 'bot');
|
||||
sendButton.disabled = false;
|
||||
userInput.focus();
|
||||
}
|
||||
|
||||
sendButton.addEventListener('click', handleFollowUp);
|
||||
userInput.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleFollowUp();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user