Update memory service with working NocoDB configuration
- Set correct table ID: mx149yctebfwvys (ai_data_Memory) - Updated type column with SingleSelect options - Added all required columns for corrections, preferences, episodes, decisions, validation - Fixed column options for type, severity, status fields
This commit is contained in:
@@ -0,0 +1,416 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>IT Site Survey AI | Dashboard</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #0d1117;
|
||||
--bg-2: #161b22;
|
||||
--bg-3: #21262d;
|
||||
--border: #30363d;
|
||||
--text: #c9d1d9;
|
||||
--text-2: #8b949e;
|
||||
--accent: #58a6ff;
|
||||
--accent-2: #1f6feb;
|
||||
--success: #3fb950;
|
||||
--warning: #d29922;
|
||||
--danger: #f85149;
|
||||
--radius: 8px;
|
||||
}
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.logo {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.subtitle {
|
||||
color: var(--text-2);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.panel {
|
||||
background: var(--bg-2);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 25px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.panel-header h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.btn-primary {
|
||||
background: var(--accent-2);
|
||||
color: white;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: var(--accent);
|
||||
}
|
||||
.btn-secondary {
|
||||
background: var(--bg-3);
|
||||
color: var(--text);
|
||||
border-color: var(--border);
|
||||
text-decoration: none;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg-3);
|
||||
}
|
||||
.alert {
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.alert.info {
|
||||
background: rgba(88, 166, 255, 0.1);
|
||||
border: 1px solid rgba(88, 166, 255, 0.3);
|
||||
}
|
||||
.alert.error {
|
||||
background: rgba(248, 81, 73, 0.1);
|
||||
border: 1px solid rgba(248, 81, 73, 0.3);
|
||||
}
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
.response-card {
|
||||
background: var(--bg-3);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.response-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.response-meta {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.response-answers {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
.answer-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.answer-label {
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-2);
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.answer-value {
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.stat-card {
|
||||
background: var(--bg-3);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.stat-value {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.stat-label {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-2);
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 15px;
|
||||
}
|
||||
.response-answers {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.stats-grid {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<div class="logo">🏢</div>
|
||||
<h1>IT Site Survey AI</h1>
|
||||
<div class="subtitle">Network Infrastructure Planning & Recommendations</div>
|
||||
</header>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<h2>📊 Survey Dashboard</h2>
|
||||
<a href="/" class="btn btn-secondary">← Back to Survey</a>
|
||||
</div>
|
||||
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="totalSurveys">0</div>
|
||||
<div class="stat-label">Total Surveys</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="totalResponses">0</div>
|
||||
<div class="stat-label">Total Responses</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="todayResponses">0</div>
|
||||
<div class="stat-label">Today's Responses</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value" id="avgCompletion">0%</div>
|
||||
<div class="stat-label">Avg. Completion</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="loadingMessage" class="alert info">
|
||||
Loading survey responses...
|
||||
</div>
|
||||
|
||||
<div id="errorMessage" class="alert error hidden">
|
||||
Error loading survey responses. Please try again.
|
||||
</div>
|
||||
|
||||
<div id="responsesContainer">
|
||||
<!-- Responses will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Format date for display
|
||||
function formatDate(dateString) {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString();
|
||||
}
|
||||
|
||||
// Format response value for display
|
||||
function formatResponseValue(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.join(', ');
|
||||
}
|
||||
return value || 'Not answered';
|
||||
}
|
||||
|
||||
// Load all survey responses
|
||||
async function loadResponses() {
|
||||
const loadingMessage = document.getElementById('loadingMessage');
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
const responsesContainer = document.getElementById('responsesContainer');
|
||||
|
||||
try {
|
||||
// Fetch all responses
|
||||
const response = await fetch('/api/surveys/responses');
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load responses');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const responses = data.responses || [];
|
||||
|
||||
// Update stats
|
||||
updateStats(responses);
|
||||
|
||||
// Hide loading message
|
||||
loadingMessage.classList.add('hidden');
|
||||
|
||||
// Display responses
|
||||
if (responses.length === 0) {
|
||||
responsesContainer.innerHTML = '<div class="alert info">No survey responses yet. Complete a survey to see results here.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort responses by submission date (newest first)
|
||||
responses.sort((a, b) => new Date(b.submitted_at) - new Date(a.submitted_at));
|
||||
|
||||
// Generate HTML for responses
|
||||
let html = '';
|
||||
for (const response of responses) {
|
||||
html += `
|
||||
<div class="response-card">
|
||||
<div class="response-header">
|
||||
<div>
|
||||
<strong>Response #${response.id.substring(0, 8)}</strong>
|
||||
</div>
|
||||
<div class="response-meta">
|
||||
Submitted: ${formatDate(response.submitted_at)} by ${response.submitted_by || 'Anonymous'}
|
||||
</div>
|
||||
</div>
|
||||
<div class="response-answers">
|
||||
${generateAnswersHtml(response.responses)}
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" onclick="viewSurvey('${response.survey_id}')">View Survey</button>
|
||||
<button class="btn btn-secondary" onclick="analyzeResponse('${response.survey_id}')">Analyze</button>
|
||||
<button class="btn btn-secondary" onclick="generateQuote('${response.survey_id}')">Generate Quote</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
responsesContainer.innerHTML = html;
|
||||
} catch (error) {
|
||||
console.error('Error loading responses:', error);
|
||||
loadingMessage.classList.add('hidden');
|
||||
errorMessage.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Generate HTML for answers
|
||||
function generateAnswersHtml(responses) {
|
||||
let html = '';
|
||||
|
||||
// Define the order of questions based on the template
|
||||
const questionOrder = [
|
||||
'company_name', 'industry', 'site_size', 'building_type', 'square_footage',
|
||||
'network_topology', 'network_size', 'internet_bandwidth', 'isp_type',
|
||||
'wireless_standard', 'wireless_coverage', 'critical_applications',
|
||||
'device_types', 'user_density', 'performance_requirements',
|
||||
'security_requirements', 'compliance_needs', 'budget_range',
|
||||
'timeline', 'additional_notes'
|
||||
];
|
||||
|
||||
// Create a map of responses for easier access
|
||||
const responseMap = responses || {};
|
||||
|
||||
// Generate HTML for each question in order
|
||||
for (const questionId of questionOrder) {
|
||||
if (responseMap.hasOwnProperty(questionId)) {
|
||||
const value = responseMap[questionId];
|
||||
const label = getQuestionLabel(questionId);
|
||||
html += `
|
||||
<div class="answer-item">
|
||||
<div class="answer-label">${label}</div>
|
||||
<div class="answer-value">${formatResponseValue(value)}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// Get human-readable label for question ID
|
||||
function getQuestionLabel(questionId) {
|
||||
const labels = {
|
||||
'company_name': 'Company Name',
|
||||
'industry': 'Industry',
|
||||
'site_size': 'Site Size',
|
||||
'building_type': 'Building Type',
|
||||
'square_footage': 'Square Footage',
|
||||
'network_topology': 'Network Topology',
|
||||
'network_size': 'Network Size',
|
||||
'internet_bandwidth': 'Internet Bandwidth',
|
||||
'isp_type': 'ISP Type',
|
||||
'wireless_standard': 'Wireless Standard',
|
||||
'wireless_coverage': 'Wireless Coverage',
|
||||
'critical_applications': 'Critical Applications',
|
||||
'device_types': 'Device Types',
|
||||
'user_density': 'User Density',
|
||||
'performance_requirements': 'Performance Requirements',
|
||||
'security_requirements': 'Security Requirements',
|
||||
'compliance_needs': 'Compliance Needs',
|
||||
'budget_range': 'Budget Range',
|
||||
'timeline': 'Project Timeline',
|
||||
'additional_notes': 'Additional Notes'
|
||||
};
|
||||
|
||||
return labels[questionId] || questionId;
|
||||
}
|
||||
|
||||
// Update statistics
|
||||
function updateStats(responses) {
|
||||
document.getElementById('totalResponses').textContent = responses.length;
|
||||
|
||||
// For now, we'll set other stats to default values
|
||||
// In a more complete implementation, we would calculate these from the data
|
||||
document.getElementById('totalSurveys').textContent = responses.length > 0 ? '1' : '0';
|
||||
document.getElementById('todayResponses').textContent = '0';
|
||||
document.getElementById('avgCompletion').textContent = '100%';
|
||||
}
|
||||
|
||||
// View survey details
|
||||
function viewSurvey(surveyId) {
|
||||
window.location.href = `/?survey=${surveyId}`;
|
||||
}
|
||||
|
||||
// Analyze response
|
||||
function analyzeResponse(surveyId) {
|
||||
// In a full implementation, this would trigger the AI analysis
|
||||
alert('AI analysis would be triggered here for survey: ' + surveyId);
|
||||
}
|
||||
|
||||
// Generate quote
|
||||
function generateQuote(surveyId) {
|
||||
// In a full implementation, this would generate a quote
|
||||
alert('Quote generation would be triggered here for survey: ' + surveyId);
|
||||
}
|
||||
|
||||
// Load responses when page loads
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadResponses();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user