diff --git "a/chatbot/admin_advanced.js" "b/chatbot/admin_advanced.js"
new file mode 100644--- /dev/null
+++ "b/chatbot/admin_advanced.js"
@@ -0,0 +1,3264 @@
+/**
+ * AIMHSA Advanced Admin Dashboard JavaScript
+ * Enhanced functionality with AdminLTE 4, DataTables, Charts, and more
+ */
+
+(() => {
+ 'use strict';
+
+ // Get API Configuration from Config Manager
+ const getAPIRoot = () => {
+ if (window.AIMHSA && window.AIMHSA.Config) {
+ return window.AIMHSA.Config.getApiBaseUrl();
+ }
+
+ // Fallback to intelligent detection
+ let apiRoot;
+ try {
+ const loc = window.location;
+ if (loc.port === '8000') {
+ apiRoot = `${loc.protocol}//${loc.hostname}:5057`;
+ } else if (loc.port === '5057' || loc.port === '') {
+ apiRoot = loc.origin;
+ } else {
+ apiRoot = 'http://localhost:5057';
+ }
+ } catch (_) {
+ apiRoot = 'http://localhost:5057';
+ }
+ return apiRoot;
+ };
+
+ const API_ROOT = getAPIRoot();
+
+ // Global variables
+ let currentSection = 'dashboard';
+ let charts = {};
+ let dataTables = {};
+ let currentProfessionalId = null;
+ let currentUser = null;
+ let userRole = 'admin'; // Default role
+
+ // Initialize when DOM is ready
+ $(document).ready(function() {
+ console.log('Admin Dashboard Initializing...');
+
+ try {
+ // Check if jQuery is loaded
+ if (typeof $ === 'undefined') {
+ console.error('jQuery not loaded');
+ showErrorMessage('jQuery library not loaded. Please refresh the page.');
+ return;
+ }
+
+ console.log('jQuery loaded');
+
+ // Check user authentication and role
+ checkUserAuthentication();
+
+ // Initialize components
+ initializeAdminLTE();
+ initializeNavigation();
+ initializeDataTables();
+ initializeCharts();
+ initializeSelect2();
+ initializeExpertiseAreas();
+ initializeEventHandlers();
+
+ // Load dashboard data with error handling
+ loadDashboardData();
+
+ // Start auto-refresh
+ startAutoRefresh();
+
+ console.log(' Admin Dashboard initialized successfully');
+
+ // Show success message
+ setTimeout(() => {
+ if (typeof Swal !== 'undefined') {
+ Swal.fire({
+ title: 'Dashboard Ready!',
+ text: `Welcome ${currentUser?.username || 'Admin'}! Dashboard loaded successfully.`,
+ icon: 'success',
+ timer: 2000,
+ toast: true,
+ position: 'top-end'
+ });
+ }
+ }, 1000);
+
+ } catch (error) {
+ console.error(' Error initializing admin dashboard:', error);
+ showErrorMessage('Dashboard initialization failed: ' + error.message);
+ }
+ });
+
+ /**
+ * Check user authentication and role
+ */
+ function checkUserAuthentication() {
+ console.log('๐ Checking user authentication...');
+
+ // Check localStorage for user session
+ const adminSession = localStorage.getItem('aimhsa_admin');
+ const professionalSession = localStorage.getItem('aimhsa_professional');
+ const userSession = localStorage.getItem('aimhsa_account');
+
+ if (adminSession) {
+ try {
+ currentUser = JSON.parse(adminSession);
+ userRole = 'admin';
+ console.log(' Admin user authenticated:', currentUser.username);
+ updateUserInterface();
+ } catch (error) {
+ console.warn(' Invalid admin session, using default');
+ setDefaultUser();
+ }
+ } else if (professionalSession) {
+ try {
+ currentUser = JSON.parse(professionalSession);
+ userRole = 'professional';
+ console.log(' Professional user authenticated:', currentUser.username);
+ updateUserInterface();
+ } catch (error) {
+ console.warn(' Invalid professional session, using default');
+ setDefaultUser();
+ }
+ } else if (userSession) {
+ try {
+ currentUser = JSON.parse(userSession);
+ userRole = 'user';
+ console.log(' Regular user authenticated:', currentUser.username);
+ updateUserInterface();
+ } catch (error) {
+ console.warn(' Invalid user session, using default');
+ setDefaultUser();
+ }
+ } else {
+ console.warn(' No user session found, using default admin');
+ setDefaultUser();
+ }
+ }
+
+ /**
+ * Set default user when no session is found
+ */
+ function setDefaultUser() {
+ currentUser = {
+ username: 'admin',
+ email: 'admin@aimhsa.rw',
+ fullname: 'System Administrator',
+ role: 'admin'
+ };
+ userRole = 'admin';
+ updateUserInterface();
+ }
+
+ /**
+ * Update user interface based on current user
+ */
+ function updateUserInterface() {
+ console.log('๐ค Updating user interface for:', currentUser.username, 'Role:', userRole);
+
+ // Update sidebar user info
+ $('.user-panel .info a').text(currentUser.fullname || currentUser.username);
+ $('.user-panel .info small').text(getRoleDisplayName(userRole));
+
+ // Update navbar user info
+ $('.navbar-nav .nav-item:last-child .nav-link span').text(currentUser.fullname || currentUser.username);
+
+ // Update page title based on role
+ if (userRole === 'professional') {
+ $('#pageTitle').text('Professional Dashboard');
+ $('.brand-text').text('AIMHSA Professional');
+ } else if (userRole === 'user') {
+ $('#pageTitle').text('User Dashboard');
+ $('.brand-text').text('AIMHSA User');
+ } else {
+ $('#pageTitle').text('Admin Dashboard');
+ $('.brand-text').text('AIMHSA Admin');
+ }
+
+ // Show/hide sections based on role
+ updateNavigationForRole();
+ }
+
+ /**
+ * Get display name for user role
+ */
+ function getRoleDisplayName(role) {
+ const roleNames = {
+ 'admin': 'System Administrator',
+ 'professional': 'Mental Health Professional',
+ 'user': 'User Account'
+ };
+ return roleNames[role] || 'User';
+ }
+
+ /**
+ * Update navigation based on user role
+ */
+ function updateNavigationForRole() {
+ if (userRole === 'professional') {
+ // Hide admin-only sections
+ $('.nav-item[data-section="professionals"]').hide();
+ $('.nav-item[data-section="reports"]').hide();
+ $('.nav-item[data-section="settings"]').hide();
+
+ // Show professional-specific sections
+ $('.nav-item[data-section="bookings"]').show();
+ $('.nav-item[data-section="risk-monitor"]').show();
+ $('.nav-item[data-section="analytics"]').show();
+ } else if (userRole === 'user') {
+ // Hide admin and professional sections
+ $('.nav-item[data-section="professionals"]').hide();
+ $('.nav-item[data-section="reports"]').hide();
+ $('.nav-item[data-section="settings"]').hide();
+ $('.nav-item[data-section="bookings"]').hide();
+
+ // Show user-specific sections
+ $('.nav-item[data-section="risk-monitor"]').show();
+ $('.nav-item[data-section="analytics"]').show();
+ } else {
+ // Admin - show all sections
+ $('.nav-item').show();
+ }
+ }
+
+ /**
+ * Show error message to user
+ */
+ function showErrorMessage(message) {
+ const errorHtml = `
+
+ Dashboard Error: ${message}
+
+
+ `;
+ $('.content-wrapper').prepend(errorHtml);
+ }
+
+ /**
+ * Initialize AdminLTE components
+ */
+ function initializeAdminLTE() {
+ console.log('๐ง Initializing AdminLTE components...');
+
+ try {
+ // Initialize push menu with fallback
+ if (typeof $.fn.PushMenu !== 'undefined') {
+ $('[data-widget="pushmenu"]').PushMenu('toggle');
+ } else {
+ // Fallback for push menu
+ $('[data-widget="pushmenu"]').on('click', function(e) {
+ e.preventDefault();
+ $('body').toggleClass('sidebar-collapse');
+ });
+ }
+
+ // Initialize tooltips
+ if (typeof $.fn.tooltip !== 'undefined') {
+ $('[data-toggle="tooltip"]').tooltip();
+ }
+
+ // Initialize popovers
+ if (typeof $.fn.popover !== 'undefined') {
+ $('[data-toggle="popover"]').popover();
+ }
+
+ // Initialize card widgets with fallback
+ if (typeof $.fn.cardWidget !== 'undefined') {
+ $('.card').cardWidget();
+ } else {
+ // Fallback for card widgets
+ $('[data-card-widget="collapse"]').on('click', function(e) {
+ e.preventDefault();
+ const card = $(this).closest('.card');
+ card.toggleClass('collapsed-card');
+ });
+
+ $('[data-card-widget="remove"]').on('click', function(e) {
+ e.preventDefault();
+ const card = $(this).closest('.card');
+ card.fadeOut(300, function() {
+ $(this).remove();
+ });
+ });
+ }
+
+ // Initialize direct chat with fallback
+ if (typeof $.fn.DirectChat !== 'undefined') {
+ $('[data-widget="chat-pane-toggle"]').DirectChat('toggle');
+ } else {
+ $('[data-widget="chat-pane-toggle"]').on('click', function(e) {
+ e.preventDefault();
+ $(this).closest('.direct-chat').toggleClass('direct-chat-contacts-open');
+ });
+ }
+
+ console.log(' AdminLTE components initialized');
+
+ } catch (error) {
+ console.warn(' Some AdminLTE components failed to initialize:', error);
+ }
+ }
+
+ /**
+ * Initialize navigation system
+ */
+ function initializeNavigation() {
+ // Handle sidebar navigation
+ $('.nav-sidebar .nav-link').on('click', function(e) {
+ e.preventDefault();
+
+ const section = $(this).data('section');
+ if (section) {
+ showSection(section);
+ updateActiveNavItem($(this));
+ updateBreadcrumb(section);
+ }
+ });
+
+ // Handle breadcrumb navigation
+ $('.breadcrumb a').on('click', function(e) {
+ e.preventDefault();
+ const section = $(this).attr('href').substring(1);
+ showSection(section);
+ });
+ }
+
+ /**
+ * Show specific section and hide others
+ */
+ function showSection(sectionName) {
+ // Hide all sections with fade effect
+ $('.content-section').fadeOut(200, function() {
+ // Show target section
+ $(`#${sectionName}-section`).fadeIn(200);
+ });
+
+ // Update current section
+ console.log('๐ Switching to section:', sectionName);
+ currentSection = sectionName;
+
+ // Load section-specific data
+ loadSectionData(sectionName);
+ }
+
+ /**
+ * Update active navigation item
+ */
+ function updateActiveNavItem(activeItem) {
+ $('.nav-sidebar .nav-link').removeClass('active');
+ activeItem.addClass('active');
+ }
+
+ /**
+ * Update breadcrumb
+ */
+ function updateBreadcrumb(section) {
+ const sectionNames = {
+ 'dashboard': 'Dashboard',
+ 'professionals': 'Professionals',
+ 'bookings': 'Bookings',
+ 'risk-monitor': 'Risk Monitor',
+ 'analytics': 'Analytics',
+ 'rag-status': 'RAG Status',
+ 'reports': 'Reports',
+ 'settings': 'Settings'
+ };
+
+ $('#pageTitle').text(sectionNames[section] || 'Dashboard');
+ $('#breadcrumbActive').text(sectionNames[section] || 'Dashboard');
+ }
+
+ /**
+ * Initialize DataTables
+ */
+ function initializeDataTables() {
+ // Professionals table
+ if ($('#professionalsTable').length) {
+ dataTables.professionals = $('#professionalsTable').DataTable({
+ responsive: true,
+ processing: true,
+ serverSide: false,
+ pageLength: 25,
+ order: [[0, 'desc']],
+ columnDefs: [
+ { targets: [-1], orderable: false }
+ ],
+ language: {
+ search: "Search:",
+ lengthMenu: "Show _MENU_ entries per page",
+ info: "Showing _START_ to _END_ of _TOTAL_ entries",
+ paginate: {
+ first: "First",
+ last: "Last",
+ next: "Next",
+ previous: "Previous"
+ }
+ }
+ });
+ }
+
+ // Bookings table
+ if ($('#bookingsTable').length) {
+ dataTables.bookings = $('#bookingsTable').DataTable({
+ responsive: true,
+ processing: true,
+ serverSide: false,
+ pageLength: 25,
+ order: [[0, 'desc']],
+ columnDefs: [
+ { targets: [-1], orderable: false }
+ ],
+ language: {
+ search: "Search:",
+ lengthMenu: "Show _MENU_ entries per page",
+ info: "Showing _START_ to _END_ of _TOTAL_ entries",
+ paginate: {
+ first: "First",
+ last: "Last",
+ next: "Next",
+ previous: "Previous"
+ }
+ }
+ });
+ }
+
+ }
+
+ /**
+ * Initialize charts
+ */
+ function initializeCharts() {
+ // Risk trend chart - will be updated with real data
+ if ($('#riskTrendChart').length) {
+ const ctx = document.getElementById('riskTrendChart').getContext('2d');
+ charts.riskTrend = new Chart(ctx, {
+ type: 'line',
+ data: {
+ labels: [],
+ datasets: [{
+ label: 'Critical',
+ data: [],
+ borderColor: '#dc3545',
+ backgroundColor: 'rgba(220, 53, 69, 0.1)',
+ tension: 0.4
+ }, {
+ label: 'High',
+ data: [],
+ borderColor: '#ffc107',
+ backgroundColor: 'rgba(255, 193, 7, 0.1)',
+ tension: 0.4
+ }, {
+ label: 'Medium',
+ data: [],
+ borderColor: '#17a2b8',
+ backgroundColor: 'rgba(23, 162, 184, 0.1)',
+ tension: 0.4
+ }, {
+ label: 'Low',
+ data: [],
+ borderColor: '#28a745',
+ backgroundColor: 'rgba(40, 167, 69, 0.1)',
+ tension: 0.4
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
+
+ // Load real data for risk trend chart after a short delay
+ setTimeout(() => {
+ loadRiskTrendData();
+ }, 100);
+ }
+
+ // Risk distribution chart - will be updated with real data
+ if ($('#riskDistributionChart').length) {
+ const ctx = document.getElementById('riskDistributionChart').getContext('2d');
+ charts.riskDistribution = new Chart(ctx, {
+ type: 'doughnut',
+ data: {
+ labels: ['Critical', 'High', 'Medium', 'Low'],
+ datasets: [{
+ data: [0, 0, 0, 0],
+ backgroundColor: ['#dc3545', '#ffc107', '#17a2b8', '#28a745'],
+ borderWidth: 2
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ plugins: {
+ legend: {
+ position: 'bottom'
+ }
+ }
+ }
+ });
+
+ // Load real data for risk distribution chart after a short delay
+ setTimeout(() => {
+ loadRiskDistributionData();
+ }, 100);
+ }
+
+ // Monthly trends chart
+ if ($('#monthlyTrendsChart').length) {
+ const ctx = document.getElementById('monthlyTrendsChart').getContext('2d');
+ charts.monthlyTrends = new Chart(ctx, {
+ type: 'bar',
+ data: {
+ labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
+ datasets: [{
+ label: 'Bookings',
+ data: [45, 52, 38, 61, 55, 67],
+ backgroundColor: '#007bff'
+ }, {
+ label: 'Completed',
+ data: [42, 48, 35, 58, 52, 63],
+ backgroundColor: '#28a745'
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
+ }
+
+ // Professional performance chart
+ if ($('#professionalPerformanceChart').length) {
+ const ctx = document.getElementById('professionalPerformanceChart').getContext('2d');
+ charts.professionalPerformance = new Chart(ctx, {
+ type: 'radar',
+ data: {
+ labels: ['Sessions', 'Satisfaction', 'Response Time', 'Availability', 'Quality'],
+ datasets: [{
+ label: 'Dr. Marie',
+ data: [85, 92, 88, 90, 87],
+ borderColor: '#007bff',
+ backgroundColor: 'rgba(0, 123, 255, 0.2)'
+ }, {
+ label: 'Dr. John',
+ data: [78, 85, 82, 88, 84],
+ borderColor: '#28a745',
+ backgroundColor: 'rgba(40, 167, 69, 0.2)'
+ }]
+ },
+ options: {
+ responsive: true,
+ maintainAspectRatio: false,
+ scales: {
+ r: {
+ beginAtZero: true,
+ max: 100
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Load risk trend data for chart
+ */
+ function loadRiskTrendData() {
+ if (!charts.riskTrend) {
+ console.warn('Risk trend chart not initialized yet');
+ return;
+ }
+
+ fetch(`${API_ROOT}/admin/risk-assessments?limit=100`)
+ .then(response => {
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ })
+ .then(data => {
+ if (data.assessments && charts.riskTrend) {
+ // Group assessments by day for the last 7 days
+ const last7Days = [];
+ for (let i = 6; i >= 0; i--) {
+ const date = new Date();
+ date.setDate(date.getDate() - i);
+ last7Days.push(date.toISOString().split('T')[0]);
+ }
+
+ const riskCounts = {
+ critical: new Array(7).fill(0),
+ high: new Array(7).fill(0),
+ medium: new Array(7).fill(0),
+ low: new Array(7).fill(0)
+ };
+
+ data.assessments.forEach(assessment => {
+ const assessmentDate = new Date(assessment.assessment_timestamp * 1000).toISOString().split('T')[0];
+ const dayIndex = last7Days.indexOf(assessmentDate);
+ if (dayIndex !== -1) {
+ const riskLevel = assessment.risk_level.toLowerCase();
+ if (riskCounts[riskLevel]) {
+ riskCounts[riskLevel][dayIndex]++;
+ }
+ }
+ });
+
+ // Update chart data
+ charts.riskTrend.data.labels = last7Days.map(date => {
+ const d = new Date(date);
+ return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
+ });
+ charts.riskTrend.data.datasets[0].data = riskCounts.critical;
+ charts.riskTrend.data.datasets[1].data = riskCounts.high;
+ charts.riskTrend.data.datasets[2].data = riskCounts.medium;
+ charts.riskTrend.data.datasets[3].data = riskCounts.low;
+ charts.riskTrend.update();
+ }
+ })
+ .catch(error => {
+ console.error('Error loading risk trend data:', error);
+ });
+ }
+
+ /**
+ * Load risk distribution data for chart
+ */
+ function loadRiskDistributionData() {
+ if (!charts.riskDistribution) {
+ console.warn('Risk distribution chart not initialized yet');
+ return;
+ }
+
+ fetch(`${API_ROOT}/admin/risk-assessments?limit=100`)
+ .then(response => {
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ }
+ return response.json();
+ })
+ .then(data => {
+ if (data.assessments && charts.riskDistribution) {
+ const riskCounts = {
+ critical: 0,
+ high: 0,
+ medium: 0,
+ low: 0
+ };
+
+ data.assessments.forEach(assessment => {
+ const riskLevel = assessment.risk_level.toLowerCase();
+ if (riskCounts.hasOwnProperty(riskLevel)) {
+ riskCounts[riskLevel]++;
+ }
+ });
+
+ // Update chart data
+ charts.riskDistribution.data.datasets[0].data = [
+ riskCounts.critical,
+ riskCounts.high,
+ riskCounts.medium,
+ riskCounts.low
+ ];
+ charts.riskDistribution.update();
+ }
+ })
+ .catch(error => {
+ console.error('Error loading risk distribution data:', error);
+ });
+ }
+
+ /**
+ * Initialize Select2
+ */
+ function initializeSelect2() {
+ $('.select2').select2({
+ theme: 'bootstrap4',
+ width: '100%'
+ });
+ }
+
+ /**
+ * Initialize event handlers
+ */
+ function initializeEventHandlers() {
+ // Professional management
+ $('#addProfessionalBtn').on('click', function() {
+ console.log('โ Opening Add Professional modal...');
+ resetProfessionalForm();
+ $('#modalTitle').text('Add New Professional');
+ $('#passwordRequired').text('*');
+ $('#passwordHelp').hide();
+ $('#professionalModal').modal('show');
+
+ // Ensure inputs work properly
+ setTimeout(() => {
+ ensureInputsWorking();
+ forceInputFunctionality();
+ debugFormInputs();
+ const firstInput = $('#professionalModal input[required]').first();
+ if (firstInput.length) {
+ firstInput.focus();
+ console.log(' Focused on first input:', firstInput.attr('name'));
+ }
+ }, 300);
+ });
+
+ // Handle form submission
+ $('#professionalForm').on('submit', function(e) {
+ e.preventDefault();
+ saveProfessional();
+ });
+
+ // Also handle button click as backup
+ $('#saveProfessionalBtn').on('click', function() {
+ $('#professionalForm').submit();
+ });
+
+ // Refresh buttons
+ $('[id$="RefreshBtn"], [id$="refreshBtn"]').on('click', function() {
+ const section = $(this).closest('.content-section').attr('id').replace('-section', '');
+ loadSectionData(section);
+ });
+
+ // Global refresh button
+ $('#refreshAllBtn').on('click', function() {
+ refreshAllData();
+ });
+
+ // Expertise areas change handler
+ $(document).on('change', 'input[name="expertise"]', function() {
+ updateExpertiseValidation();
+ });
+
+ // Export buttons
+ $('#exportBookingsBtn').on('click', function() {
+ exportTableToCSV('bookingsTable', 'bookings.csv');
+ });
+
+ // Initialize bookings filtering
+ initializeBookingsFiltering();
+
+ // Search functionality
+ $('#professionalSearch').on('keyup', function() {
+ if (dataTables.professionals) {
+ dataTables.professionals.search(this.value).draw();
+ }
+ });
+
+ // Filter functionality
+ $('#statusFilter, #riskLevelFilter, #specializationFilter').on('change', function() {
+ applyFilters();
+ });
+
+ // Professional search functionality
+ $('#professionalSearch').on('input', function() {
+ const searchTerm = $(this).val().toLowerCase();
+ filterProfessionals(searchTerm);
+ });
+
+ // Professional specialization filter
+ $('#professionalSpecializationFilter').on('change', function() {
+ const specialization = $(this).val();
+ filterProfessionalsBySpecialization(specialization);
+ });
+
+ // Logout
+ $('#logoutBtn').on('click', function() {
+ Swal.fire({
+ title: 'Logout?',
+ text: `Are you sure you want to logout, ${currentUser?.username || 'User'}?`,
+ icon: 'question',
+ showCancelButton: true,
+ confirmButtonColor: '#3085d6',
+ cancelButtonColor: '#d33',
+ confirmButtonText: 'Yes, logout!'
+ }).then((result) => {
+ if (result.isConfirmed) {
+ logoutUser();
+ }
+ });
+ });
+ }
+
+ /**
+ * Check API health
+ */
+ function checkAPIHealth() {
+ const endpoints = [
+ '/admin/bookings',
+ '/admin/professionals',
+ '/admin/risk-assessments',
+ '/monitor/risk-stats',
+ '/monitor/recent-assessments'
+ ];
+
+ Promise.all(endpoints.map(endpoint =>
+ fetch(`${API_ROOT}${endpoint}`)
+ .then(response => ({ endpoint, status: response.ok, statusCode: response.status }))
+ .catch(error => ({ endpoint, status: false, error: error.message }))
+ )).then(results => {
+ const failedEndpoints = results.filter(r => !r.status);
+ if (failedEndpoints.length > 0) {
+ console.warn('Some API endpoints are not responding:', failedEndpoints);
+ // Show a warning to the user
+ if (failedEndpoints.length === endpoints.length) {
+ Swal.fire({
+ title: 'API Connection Error',
+ text: 'Unable to connect to the backend API. Please check if the server is running.',
+ icon: 'error',
+ timer: 5000
+ });
+ }
+ }
+ });
+ }
+
+ /**
+ * Load dashboard data
+ */
+ function loadDashboardData() {
+ console.log(' Loading dashboard data for role:', userRole);
+
+ // Show loading state
+ showLoadingState();
+
+ // Check API health first
+ checkAPIHealth();
+
+ // Load role-specific data
+ if (userRole === 'admin') {
+ loadAdminDashboardData();
+ } else if (userRole === 'professional') {
+ loadProfessionalDashboardData();
+ } else if (userRole === 'user') {
+ loadUserDashboardData();
+ } else {
+ loadAdminDashboardData(); // Default to admin
+ }
+
+ // Hide loading state after a delay
+ setTimeout(() => {
+ hideLoadingState();
+ }, 2000);
+ }
+
+ /**
+ * Load admin dashboard data
+ */
+ function loadAdminDashboardData() {
+ console.log('๐ Loading admin dashboard data...');
+ loadKPIData();
+ loadRecentBookings();
+ loadSystemStatus();
+ loadNotifications();
+ }
+
+ /**
+ * Load professional dashboard data
+ */
+ function loadProfessionalDashboardData() {
+ console.log(' Loading professional dashboard data...');
+ loadProfessionalKPIData();
+ loadProfessionalBookings();
+ loadProfessionalNotifications();
+ loadNotifications();
+ }
+
+ /**
+ * Load user dashboard data
+ */
+ function loadUserDashboardData() {
+ console.log('๐ค Loading user dashboard data...');
+ loadUserKPIData();
+ loadUserRiskHistory();
+ loadUserBookings();
+ loadNotifications();
+ }
+
+ /**
+ * Show loading state
+ */
+ function showLoadingState() {
+ $('#loadingOverlay').show();
+ // Add loading class to KPI cards
+ $('.small-box .inner h3').html('');
+ }
+
+ /**
+ * Hide loading state
+ */
+ function hideLoadingState() {
+ $('#loadingOverlay').hide();
+ }
+
+ /**
+ * Load section-specific data
+ */
+ function loadSectionData(section) {
+ console.log('๐ Loading section data for:', section);
+ try {
+ switch (section) {
+ case 'professionals':
+ console.log('๐ Loading professionals...');
+ loadProfessionals();
+ break;
+ case 'bookings':
+ console.log('๐ Loading bookings...');
+ loadBookings();
+ break;
+ case 'risk-monitor':
+ loadRiskData();
+ break;
+ case 'analytics':
+ loadAnalyticsData();
+ break;
+ case 'rag-status':
+ loadRAGStatus();
+ break;
+ default:
+ console.warn(`Unknown section: ${section}`);
+ }
+ } catch (error) {
+ console.error(`Error loading section data for ${section}:`, error);
+ }
+ }
+
+ /**
+ * Load KPI data from database
+ */
+ function loadKPIData() {
+ console.log(' Loading KPI data...');
+
+ // Show loading state
+ $('#kpiActiveBookings, #kpiCritical, #kpiProfessionals, #kpiAssessments').html('');
+
+ // Try to load data from API endpoints with timeout
+ const timeout = 5000; // 5 second timeout
+
+ const apiPromises = [
+ fetch(`${API_ROOT}/admin/bookings`, { signal: AbortSignal.timeout(timeout) })
+ .then(res => res.ok ? res.json() : null)
+ .catch(err => {
+ console.warn('Bookings API failed:', err);
+ return null;
+ }),
+ fetch(`${API_ROOT}/admin/professionals`, { signal: AbortSignal.timeout(timeout) })
+ .then(res => res.ok ? res.json() : null)
+ .catch(err => {
+ console.warn('Professionals API failed:', err);
+ return null;
+ }),
+ fetch(`${API_ROOT}/admin/risk-assessments?limit=100`, { signal: AbortSignal.timeout(timeout) })
+ .then(res => res.ok ? res.json() : null)
+ .catch(err => {
+ console.warn('Risk assessments API failed:', err);
+ return null;
+ }),
+ fetch(`${API_ROOT}/monitor/risk-stats`, { signal: AbortSignal.timeout(timeout) })
+ .then(res => res.ok ? res.json() : null)
+ .catch(err => {
+ console.warn('Risk stats API failed:', err);
+ return null;
+ })
+ ];
+
+ Promise.all(apiPromises).then(([bookingsData, professionalsData, riskData, riskStats]) => {
+ console.log(' API Data received:', { bookingsData, professionalsData, riskData, riskStats });
+
+ let hasRealData = false;
+
+ // Active bookings (pending + confirmed)
+ const activeBookings = bookingsData?.bookings ?
+ bookingsData.bookings.filter(b => ['pending', 'confirmed'].includes(b.booking_status)).length : 0;
+ $('#kpiActiveBookings').text(activeBookings);
+ if (activeBookings > 0) hasRealData = true;
+
+ // Critical risks
+ const criticalRisks = riskStats?.critical ||
+ (riskData?.assessments ? riskData.assessments.filter(r => r.risk_level === 'critical').length : 0);
+ $('#kpiCritical').text(criticalRisks);
+ if (criticalRisks > 0) hasRealData = true;
+
+ // Total professionals
+ const totalProfessionals = professionalsData?.professionals ? professionalsData.professionals.length : 0;
+ $('#kpiProfessionals').text(totalProfessionals);
+ if (totalProfessionals > 0) hasRealData = true;
+
+ // Assessments today
+ const today = new Date().toISOString().split('T')[0];
+ const assessmentsToday = riskData?.assessments ?
+ riskData.assessments.filter(r => {
+ const assessmentDate = new Date(r.assessment_timestamp * 1000).toISOString().split('T')[0];
+ return assessmentDate === today;
+ }).length : 0;
+ $('#kpiAssessments').text(assessmentsToday);
+ if (assessmentsToday > 0) hasRealData = true;
+
+ if (!hasRealData) {
+ console.log(' No real data found, showing demo data');
+ showDemoData();
+ } else {
+ console.log(' KPI data loaded successfully');
+ }
+
+ }).catch(error => {
+ console.warn(' API not available, using demo data:', error);
+ showDemoData();
+ });
+ }
+
+ /**
+ * Show demo data when API is not available
+ */
+ function showDemoData() {
+ // Show demo data when API is not available
+ $('#kpiActiveBookings').html('12');
+ $('#kpiCritical').html('3');
+ $('#kpiProfessionals').html('8');
+ $('#kpiAssessments').text('25');
+
+ // Show demo mode notification
+ if (typeof Swal !== 'undefined') {
+ Swal.fire({
+ title: 'Demo Mode',
+ text: 'Backend API not available. Showing demo data.',
+ icon: 'info',
+ timer: 3000,
+ toast: true,
+ position: 'top-end'
+ });
+ }
+ }
+
+ /**
+ * Load recent bookings from database
+ */
+ function loadRecentBookings() {
+ console.log('๐ Loading recent bookings...');
+
+ const tbody = $('#recentBookingsTable');
+ tbody.html('| Loading... |
');
+
+ // Try to load real data with timeout
+ fetch(`${API_ROOT}/admin/bookings?limit=5`, { signal: AbortSignal.timeout(5000) })
+ .then(response => {
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+ }
+ return response.json();
+ })
+ .then(data => {
+ console.log('๐ Bookings data received:', data);
+ tbody.empty();
+
+ if (data.bookings && data.bookings.length > 0) {
+ data.bookings.forEach(booking => {
+ const row = `
+
+ | ${booking.booking_id || 'N/A'} |
+ ${booking.user_account || 'Guest'} |
+ ${booking.risk_level || 'Unknown'} |
+ ${booking.booking_status || 'Unknown'} |
+
+ `;
+ tbody.append(row);
+ });
+ console.log(' Recent bookings loaded successfully');
+ } else {
+ showDemoBookings();
+ }
+ })
+ .catch(error => {
+ console.warn(' Error loading recent bookings, showing demo data:', error);
+ showDemoBookings();
+ });
+ }
+
+ /**
+ * Show demo bookings data
+ */
+ function showDemoBookings() {
+ const tbody = $('#recentBookingsTable');
+ tbody.html(`
+
+ | BK001 |
+ Demo User |
+ Critical |
+ Pending |
+
+
+ | BK002 |
+ Test User |
+ High |
+ Confirmed |
+
+
+ | BK003 |
+ Sample User |
+ Medium |
+ Completed |
+
+
+ |
+ Demo data - API not available
+ |
+
+ `);
+ }
+
+ /**
+ * Load professional KPI data
+ */
+ function loadProfessionalKPIData() {
+ console.log(' Loading professional KPI data...');
+
+ // Update KPI labels for professional
+ $('#kpiActiveBookings').parent().find('p').text('My Sessions');
+ $('#kpiCritical').parent().find('p').text('High Risk Cases');
+ $('#kpiProfessionals').parent().find('p').text('Total Patients');
+ $('#kpiAssessments').parent().find('p').text('Today\'s Assessments');
+
+ // Try to load professional-specific data
+ fetch(`${API_ROOT}/professional/dashboard-stats`, { signal: AbortSignal.timeout(5000) })
+ .then(response => response.json())
+ .then(data => {
+ console.log(' Professional data received:', data);
+
+ $('#kpiActiveBookings').text(data.totalSessions || 0);
+ $('#kpiCritical').text(data.highRiskCases || 0);
+ $('#kpiProfessionals').text(data.activeUsers || 0);
+ $('#kpiAssessments').text(data.unreadNotifications || 0);
+
+ console.log(' Professional KPI data loaded successfully');
+ })
+ .catch(error => {
+ console.warn(' Professional API not available, using demo data:', error);
+ showProfessionalDemoData();
+ });
+ }
+
+ /**
+ * Show professional demo data
+ */
+ function showProfessionalDemoData() {
+ $('#kpiActiveBookings').html('15');
+ $('#kpiCritical').html('2');
+ $('#kpiProfessionals').html('8');
+ $('#kpiAssessments').text('5');
+ }
+
+ /**
+ * Load user KPI data
+ */
+ function loadUserKPIData() {
+ console.log('๐ค Loading user KPI data...');
+
+ // Update KPI labels for user
+ $('#kpiActiveBookings').parent().find('p').text('My Bookings');
+ $('#kpiCritical').parent().find('p').text('Risk Level');
+ $('#kpiProfessionals').parent().find('p').text('Sessions Completed');
+ $('#kpiAssessments').parent().find('p').text('Assessments Done');
+
+ // Show user-specific demo data
+ $('#kpiActiveBookings').html('3');
+ $('#kpiCritical').html('Medium');
+ $('#kpiProfessionals').html('2');
+ $('#kpiAssessments').text('12');
+ }
+
+ /**
+ * Load professional bookings
+ */
+ function loadProfessionalBookings() {
+ console.log(' Loading professional bookings...');
+ // This would load bookings specific to the professional
+ loadRecentBookings(); // Use existing function for now
+ }
+
+ /**
+ * Load professional notifications
+ */
+ function loadProfessionalNotifications() {
+ console.log(' Loading professional notifications...');
+ // This would load notifications for the professional
+ // For now, just show demo data
+ }
+
+ /**
+ * Load user risk history
+ */
+ function loadUserRiskHistory() {
+ console.log('๐ค Loading user risk history...');
+ // This would load the user's risk assessment history
+ }
+
+ /**
+ * Load user bookings
+ */
+ function loadUserBookings() {
+ console.log('๐ค Loading user bookings...');
+ // This would load bookings specific to the user
+ loadRecentBookings(); // Use existing function for now
+ }
+
+ /**
+ * Logout user
+ */
+ function logoutUser() {
+ console.log('๐ช Logging out user:', currentUser?.username);
+
+ // Clear all session data
+ localStorage.removeItem('aimhsa_admin');
+ localStorage.removeItem('aimhsa_professional');
+ localStorage.removeItem('aimhsa_account');
+
+ // Show logout message
+ if (typeof Swal !== 'undefined') {
+ Swal.fire({
+ title: 'Logged Out',
+ text: 'You have been successfully logged out.',
+ icon: 'success',
+ timer: 2000
+ }).then(() => {
+ // Redirect to login page
+ window.location.href = 'login.html';
+ });
+ } else {
+ // Fallback redirect
+ window.location.href = 'login.html';
+ }
+ }
+
+ /**
+ * Load system status
+ */
+ function loadSystemStatus() {
+ // System status is already set in HTML
+ // This function can be used to update real-time status
+ }
+
+ /**
+ * Load notifications from database
+ */
+ function loadNotifications() {
+ console.log('๐ Loading notifications from database...');
+
+ fetch(`${API_ROOT}/notifications`, { signal: AbortSignal.timeout(5000) })
+ .then(response => {
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+ }
+ return response.json();
+ })
+ .then(data => {
+ console.log('๐ Notifications data received:', data);
+ updateNotificationUI(data);
+ })
+ .catch(error => {
+ console.warn(' Error loading notifications, using demo data:', error);
+ showDemoNotifications();
+ });
+ }
+
+ /**
+ * Update notification UI with real data
+ */
+ function updateNotificationUI(data) {
+ // Update notification badge
+ const totalNotifications = data.totalNotifications || 0;
+ $('.nav-link .badge').text(totalNotifications);
+
+ // Update notification dropdown content
+ const notificationsHtml = generateNotificationsHTML(data);
+ $('.dropdown-menu.notifications-menu').html(notificationsHtml);
+
+ console.log(' Notifications UI updated with real data');
+ }
+
+ /**
+ * Generate notifications HTML from database data
+ */
+ function generateNotificationsHTML(data) {
+ const notifications = data.notifications || [];
+ const totalNotifications = data.totalNotifications || 0;
+
+ let html = `
+
+
+ `;
+
+ if (notifications.length === 0) {
+ html += `
+
+
+ No new notifications
+
+ `;
+ } else {
+ notifications.slice(0, 5).forEach(notification => {
+ const iconClass = getNotificationIcon(notification.type);
+ const timeAgo = notification.timeAgo || 'Unknown';
+ const isRead = notification.isRead ? '' : 'font-weight-bold';
+
+ html += `
+
+
+ ${notification.title}
+ ${timeAgo}
+
+ `;
+ });
+ }
+
+ html += `
+
+
+ `;
+
+ return html;
+ }
+
+ /**
+ * Get icon class for notification type
+ */
+ function getNotificationIcon(type) {
+ const iconMap = {
+ 'booking': 'fas fa-calendar-check text-success',
+ 'risk': 'fas fa-exclamation-triangle text-danger',
+ 'message': 'fas fa-envelope text-info',
+ 'user': 'fas fa-user text-primary',
+ 'system': 'fas fa-cog text-secondary',
+ 'default': 'fas fa-bell text-warning'
+ };
+ return iconMap[type] || iconMap['default'];
+ }
+
+ /**
+ * Show demo notifications when API is not available
+ */
+ function showDemoNotifications() {
+ const demoData = {
+ totalNotifications: 15,
+ notifications: [
+ {
+ id: 1,
+ title: '4 new messages',
+ type: 'message',
+ timeAgo: '3 mins',
+ isRead: false
+ },
+ {
+ id: 2,
+ title: '8 friend requests',
+ type: 'user',
+ timeAgo: '12 hours',
+ isRead: false
+ },
+ {
+ id: 3,
+ title: '3 new reports',
+ type: 'system',
+ timeAgo: '2 days',
+ isRead: true
+ }
+ ]
+ };
+
+ updateNotificationUI(demoData);
+ console.log('๐ฑ Demo notifications displayed');
+ }
+
+ /**
+ * Load professionals data from database
+ */
+ function loadProfessionals() {
+ console.log('๐ฅ Loading professionals...');
+ const tbody = $('#professionalsTableBody');
+ tbody.html('| Loading... |
');
+
+ fetch(`${API_ROOT}/admin/professionals`)
+ .then(response => {
+ console.log('๐ก Professionals API response status:', response.status);
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+ }
+ return response.json();
+ })
+ .then(data => {
+ console.log(' Professionals data received:', data);
+ tbody.empty();
+
+ if (data.professionals && data.professionals.length > 0) {
+ data.professionals.forEach(prof => {
+ const fullName = `${prof.first_name || ''} ${prof.last_name || ''}`.trim();
+ const statusClass = prof.is_active ? 'success' : 'secondary';
+ const statusText = prof.is_active ? 'Active' : 'Inactive';
+
+ const row = `
+
+ | ${prof.id} |
+ ${fullName || 'N/A'} |
+ ${prof.specialization || 'N/A'} |
+ ${prof.email || 'N/A'} |
+ ${prof.phone || 'N/A'} |
+ ${prof.experience_years || 0} years |
+ ${statusText} |
+
+
+
+
+ |
+
+ `;
+ tbody.append(row);
+ });
+ console.log(' Professionals loaded successfully');
+ } else {
+ tbody.html('| No professionals found |
');
+ }
+
+ // Update DataTable if it exists
+ if (dataTables.professionals) {
+ dataTables.professionals.clear().rows.add($(tbody).find('tr')).draw();
+ }
+ })
+ .catch(error => {
+ console.error(' Error loading professionals:', error);
+ tbody.html(`
+
+
+ Error loading professionals
+ ${error.message}
+ |
+
+ `);
+ });
+ }
+
+ /**
+ * Load bookings data from database with full user details
+ */
+ function loadBookings() {
+ console.log(' Starting loadBookings function...');
+ const tbody = $('#bookingsTableBody');
+ console.log('Table body element:', tbody);
+ console.log('Table body length:', tbody.length);
+ console.log('Table is visible:', tbody.is(':visible'));
+
+ tbody.html('| Loading bookings... |
');
+
+ // Show loading state in stats
+ updateBookingStats({ total: 0, confirmed: 0, pending: 0, critical: 0 });
+
+ fetch(`${API_ROOT}/admin/bookings`)
+ .then(response => response.json())
+ .then(data => {
+ tbody.empty();
+
+ if (data.bookings && data.bookings.length > 0) {
+ // Update stats
+ updateBookingStats(data);
+
+ // Log the data for debugging
+ console.log(' Bookings data received:', {
+ total: data.total,
+ confirmed: data.confirmed,
+ pending: data.pending,
+ critical: data.critical,
+ bookingsCount: data.bookings ? data.bookings.length : 0
+ });
+
+ console.log('๐ Processing bookings data...');
+ data.bookings.forEach((booking, index) => {
+ console.log(`๐ Processing booking ${index + 1}:`, {
+ id: booking.booking_id,
+ user: booking.user_fullname,
+ professional: booking.professional_name,
+ status: booking.booking_status,
+ risk: booking.risk_level
+ });
+
+ const scheduledTime = new Date(booking.scheduled_datetime * 1000).toLocaleString();
+ const professionalName = booking.professional_name || 'Unassigned';
+ const professionalSpecialization = booking.professional_specialization || '';
+
+ // Get user initials for avatar
+ const userInitials = getUserInitials(booking.user_fullname || booking.user_account || 'Guest');
+
+ // Create user details HTML
+ const userDetails = createUserDetailsHTML(booking);
+
+ // Create professional details HTML
+ const professionalDetails = createProfessionalDetailsHTML(booking);
+
+ // Create risk badge HTML
+ const riskBadge = createRiskBadgeHTML(booking.risk_level);
+
+ // Create status badge HTML
+ const statusBadge = createStatusBadgeHTML(booking.booking_status);
+
+ // Create action buttons HTML
+ const actionButtons = createActionButtonsHTML(booking.booking_id, booking.booking_status);
+
+ const row = `
+
+ |
+
+ ${booking.booking_id.substring(0, 8)}...
+ ${booking.booking_id}
+
+ |
+ ${userDetails} |
+ ${professionalDetails} |
+ ${riskBadge} |
+
+
+
+ ${scheduledTime}
+
+ |
+ ${statusBadge} |
+ ${actionButtons} |
+
+ `;
+ tbody.append(row);
+ console.log(` Added row ${index + 1} to table`);
+ });
+
+ console.log(' Total rows in tbody:', tbody.find('tr').length);
+ } else {
+ tbody.html(`
+
+
+
+ No Bookings Found
+ There are currently no bookings in the system.
+ |
+
+ `);
+ updateBookingStats({ total: 0, confirmed: 0, pending: 0, critical: 0 });
+ }
+
+ // Update DataTable
+ console.log(' Checking DataTable status...');
+ console.log('DataTable object:', dataTables.bookings);
+ console.log('DataTable exists:', !!dataTables.bookings);
+
+ // First, let's try to show the data without DataTable
+ console.log(' Table body HTML after adding rows:');
+ console.log(tbody.html());
+
+ if (dataTables.bookings) {
+ console.log('๐ Updating DataTable with', tbody.find('tr').length, 'rows');
+ try {
+ dataTables.bookings.clear().rows.add($(tbody).find('tr')).draw();
+ console.log(' DataTable updated successfully');
+ } catch (error) {
+ console.error(' Error updating DataTable:', error);
+ console.log('๐ Trying to destroy and recreate DataTable...');
+ try {
+ dataTables.bookings.destroy();
+ dataTables.bookings = $('#bookingsTable').DataTable({
+ responsive: true,
+ processing: true,
+ serverSide: false,
+ pageLength: 25,
+ order: [[0, 'desc']],
+ columnDefs: [
+ { targets: [-1], orderable: false }
+ ],
+ language: {
+ search: "Search:",
+ lengthMenu: "Show _MENU_ entries per page",
+ info: "Showing _START_ to _END_ of _TOTAL_ entries",
+ paginate: {
+ first: "First",
+ last: "Last",
+ next: "Next",
+ previous: "Previous"
+ }
+ }
+ });
+ console.log(' DataTable recreated successfully');
+ } catch (recreateError) {
+ console.error(' Error recreating DataTable:', recreateError);
+ }
+ }
+ } else {
+ console.log(' DataTable not initialized - trying to reinitialize...');
+ // Try to reinitialize the DataTable
+ if ($('#bookingsTable').length) {
+ dataTables.bookings = $('#bookingsTable').DataTable({
+ responsive: true,
+ processing: true,
+ serverSide: false,
+ pageLength: 25,
+ order: [[0, 'desc']],
+ columnDefs: [
+ { targets: [-1], orderable: false }
+ ],
+ language: {
+ search: "Search:",
+ lengthMenu: "Show _MENU_ entries per page",
+ info: "Showing _START_ to _END_ of _TOTAL_ entries",
+ paginate: {
+ first: "First",
+ last: "Last",
+ next: "Next",
+ previous: "Previous"
+ }
+ }
+ });
+ console.log(' DataTable reinitialized');
+ }
+ }
+
+ console.log(' Bookings loaded successfully:', data.bookings?.length || 0, 'bookings');
+ })
+ .catch(error => {
+ console.error('Error loading bookings:', error);
+ tbody.html(`
+
+ |
+
+ Error loading bookings data
+ |
+
+ `);
+ });
+ }
+
+ /**
+ * Create user details HTML with full information
+ */
+ function createUserDetailsHTML(booking) {
+ const userFullName = booking.user_fullname || booking.user_account || 'Guest User';
+ const userEmail = booking.user_email || 'No email provided';
+ const userPhone = booking.user_phone || 'No phone provided';
+ const userLocation = booking.user_location || 'Location not specified';
+ const userInitials = getUserInitials(userFullName);
+
+ return `
+
+
+ ${userInitials}
+
+
+
${userFullName}
+
+
+
+ ${userLocation}
+
+
+
+ `;
+ }
+
+ /**
+ * Create professional details HTML
+ */
+ function createProfessionalDetailsHTML(booking) {
+ const professionalName = booking.professional_name || 'Unassigned';
+ const professionalSpecialization = booking.professional_specialization || '';
+ const professionalInitials = getUserInitials(professionalName);
+
+ if (professionalName === 'Unassigned') {
+ return `
+
+
+
+
+
+
Unassigned
+
Awaiting assignment
+
+
+ `;
+ }
+
+ return `
+
+
+ ${professionalInitials}
+
+
+
${professionalName}
+
${professionalSpecialization}
+
+
+ `;
+ }
+
+ /**
+ * Create risk badge HTML
+ */
+ function createRiskBadgeHTML(riskLevel) {
+ const riskIcons = {
+ critical: 'fas fa-exclamation-triangle',
+ high: 'fas fa-exclamation-circle',
+ medium: 'fas fa-info-circle',
+ low: 'fas fa-check-circle'
+ };
+
+ const icon = riskIcons[riskLevel.toLowerCase()] || 'fas fa-question-circle';
+
+ return `
+
+
+ ${riskLevel.toUpperCase()}
+
+ `;
+ }
+
+ /**
+ * Create status badge HTML
+ */
+ function createStatusBadgeHTML(status) {
+ const statusIcons = {
+ pending: 'fas fa-clock',
+ confirmed: 'fas fa-check-circle',
+ completed: 'fas fa-check-double',
+ declined: 'fas fa-times-circle',
+ cancelled: 'fas fa-ban'
+ };
+
+ const icon = statusIcons[status.toLowerCase()] || 'fas fa-question-circle';
+
+ return `
+
+
+ ${status.toUpperCase()}
+
+ `;
+ }
+
+ /**
+ * Create action buttons HTML
+ */
+ function createActionButtonsHTML(bookingId, status) {
+ const canEdit = status === 'pending' || status === 'confirmed';
+ const canComplete = status === 'confirmed';
+ const canCancel = status === 'pending' || status === 'confirmed';
+
+ return `
+
+
+ ${canEdit ? `
+
+ ` : ''}
+ ${canComplete ? `
+
+ ` : ''}
+ ${canCancel ? `
+
+ ` : ''}
+
+ `;
+ }
+
+ /**
+ * Get user initials for avatar
+ */
+ function getUserInitials(name) {
+ if (!name || name === 'Guest') return 'G';
+
+ const words = name.trim().split(' ');
+ if (words.length >= 2) {
+ return (words[0][0] + words[1][0]).toUpperCase();
+ }
+ return name[0].toUpperCase();
+ }
+
+ /**
+ * Update booking statistics
+ */
+ function updateBookingStats(data) {
+ const stats = {
+ total: data.total || 0,
+ confirmed: data.confirmed || 0,
+ pending: data.pending || 0,
+ critical: data.critical || 0
+ };
+
+ $('#totalBookings').text(stats.total);
+ $('#confirmedBookings').text(stats.confirmed);
+ $('#pendingBookings').text(stats.pending);
+ $('#criticalBookings').text(stats.critical);
+ }
+
+ /**
+ * Load risk data from database
+ */
+ function loadRiskData() {
+ // Show loading state
+ $('#criticalCount, #highCount, #mediumCount, #lowCount').text('...');
+
+ fetch(`${API_ROOT}/admin/risk-assessments?limit=100`)
+ .then(response => response.json())
+ .then(data => {
+ if (data.assessments) {
+ const riskCounts = {
+ critical: 0,
+ high: 0,
+ medium: 0,
+ low: 0
+ };
+
+ data.assessments.forEach(assessment => {
+ const riskLevel = assessment.risk_level.toLowerCase();
+ if (riskCounts.hasOwnProperty(riskLevel)) {
+ riskCounts[riskLevel]++;
+ }
+ });
+
+ $('#criticalCount').text(riskCounts.critical);
+ $('#highCount').text(riskCounts.high);
+ $('#mediumCount').text(riskCounts.medium);
+ $('#lowCount').text(riskCounts.low);
+
+ // Update recent assessments
+ updateRecentAssessments(data.assessments.slice(0, 10));
+ } else {
+ $('#criticalCount, #highCount, #mediumCount, #lowCount').text('0');
+ }
+ })
+ .catch(error => {
+ console.error('Error loading risk data:', error);
+ $('#criticalCount, #highCount, #mediumCount, #lowCount').text('0');
+ });
+ }
+
+ /**
+ * Update recent assessments display
+ */
+ function updateRecentAssessments(assessments) {
+ const container = $('#recentAssessments');
+ container.empty();
+
+ if (assessments.length === 0) {
+ container.html('No recent assessments
');
+ return;
+ }
+
+ assessments.forEach(assessment => {
+ const timeAgo = getTimeAgo(assessment.assessment_timestamp);
+ const riskClass = getRiskBadgeClass(assessment.risk_level);
+
+ const assessmentItem = `
+
+
${timeAgo}
+
+
+
User: ${assessment.user_account || 'Guest'}
+
Score: ${(assessment.risk_score * 100).toFixed(1)}%
+
Indicators: ${assessment.detected_indicators || 'None detected'}
+
+
+ `;
+ container.append(assessmentItem);
+ });
+ }
+
+ /**
+ * Get time ago string
+ */
+ function getTimeAgo(timestamp) {
+ const now = Date.now() / 1000;
+ const diff = now - timestamp;
+
+ if (diff < 60) return 'Just now';
+ if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
+ if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
+ return `${Math.floor(diff / 86400)}d ago`;
+ }
+
+ /**
+ * Load analytics data from database
+ */
+ function loadAnalyticsData() {
+ // Show loading state
+ $('#totalProfessionals, #activeBookings, #completedSessions, #assessmentsToday').text('...');
+
+ Promise.all([
+ fetch(`${API_ROOT}/admin/professionals`).then(res => res.json()),
+ fetch(`${API_ROOT}/admin/bookings`).then(res => res.json()),
+ fetch(`${API_ROOT}/admin/risk-assessments?limit=100`).then(res => res.json())
+ ]).then(([professionalsData, bookingsData, riskData]) => {
+ // Total professionals
+ const totalProfessionals = professionalsData.professionals ? professionalsData.professionals.length : 0;
+ $('#totalProfessionals').text(totalProfessionals);
+
+ // Active bookings
+ const activeBookings = bookingsData.bookings ?
+ bookingsData.bookings.filter(b => ['pending', 'confirmed'].includes(b.booking_status)).length : 0;
+ $('#activeBookings').text(activeBookings);
+
+ // Completed sessions
+ const completedSessions = bookingsData.bookings ?
+ bookingsData.bookings.filter(b => b.booking_status === 'completed').length : 0;
+ $('#completedSessions').text(completedSessions);
+
+ // Assessments today
+ const today = new Date().toISOString().split('T')[0];
+ const assessmentsToday = riskData.assessments ?
+ riskData.assessments.filter(r => {
+ const assessmentDate = new Date(r.assessment_timestamp * 1000).toISOString().split('T')[0];
+ return assessmentDate === today;
+ }).length : 0;
+ $('#assessmentsToday').text(assessmentsToday);
+
+ }).catch(error => {
+ console.error('Error loading analytics data:', error);
+ $('#totalProfessionals, #activeBookings, #completedSessions, #assessmentsToday').text('0');
+ });
+ }
+
+ /**
+ * Load RAG status
+ */
+ function loadRAGStatus() {
+ // RAG status is already set in HTML
+ // This function can be used to update real-time status
+ }
+
+
+ /**
+ * Save professional
+ */
+ function saveProfessional() {
+ console.log('Saving professional...');
+
+ // Validate form
+ if (!validateProfessionalForm()) {
+ console.log('Form validation failed');
+ return;
+ }
+
+ // Get form data
+ const form = $('#professionalForm');
+ const formData = new FormData(form[0]);
+ const data = Object.fromEntries(formData.entries());
+
+ console.log('Form data collected:', data);
+
+ // Get expertise areas
+ const expertiseAreas = $('input[name="expertise"]:checked').map(function() {
+ return this.value;
+ }).get();
+
+ console.log('Expertise areas selected:', expertiseAreas);
+
+ const professionalData = {
+ username: data.username,
+ first_name: data.first_name,
+ last_name: data.last_name,
+ email: data.email,
+ phone: data.phone || '',
+ specialization: data.specialization,
+ experience_years: parseInt(data.experience_years) || 0,
+ expertise_areas: expertiseAreas,
+ district: data.district || '',
+ consultation_fee: parseFloat(data.consultation_fee) || 0,
+ bio: data.bio || '',
+ languages: ['english'], // Default languages
+ qualifications: [], // Default qualifications
+ availability_schedule: {} // Default schedule
+ };
+
+ // Add password only for new professionals or if provided in edit mode
+ const isEditMode = $('#modalTitle').text().includes('Edit');
+ if (!isEditMode) {
+ professionalData.password = data.password;
+ } else {
+ // For edit mode, only include password if provided
+ const password = data.password;
+ if (password && password.trim()) {
+ professionalData.password = password;
+ }
+ }
+
+ console.log('Professional data to send:', professionalData);
+
+ // Show loading state
+ $('#saveProfessionalBtn').prop('disabled', true).html(' Saving...');
+
+ const url = isEditMode ?
+ `${API_ROOT}/admin/professionals/${currentProfessionalId}` :
+ `${API_ROOT}/admin/professionals`;
+ const method = isEditMode ? 'PUT' : 'POST';
+
+ console.log('Sending request to:', url, 'Method:', method);
+
+ fetch(url, {
+ method: method,
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(professionalData)
+ })
+ .then(response => response.json())
+ .then(data => {
+ console.log(' Response data:', data);
+ if (data.success) {
+ Swal.fire({
+ title: 'Success!',
+ text: isEditMode ? 'Professional updated successfully!' : 'Professional added successfully!',
+ icon: 'success',
+ timer: 2000
+ }).then(() => {
+ $('#professionalModal').modal('hide');
+ loadProfessionals();
+ resetProfessionalForm();
+ });
+ } else {
+ Swal.fire({
+ title: 'Error!',
+ text: data.error || 'Failed to save professional.',
+ icon: 'error'
+ });
+ }
+ })
+ .catch(error => {
+ console.error('Error saving professional:', error);
+ Swal.fire({
+ title: 'Error!',
+ text: 'Failed to save professional. Please try again.',
+ icon: 'error'
+ });
+ })
+ .finally(() => {
+ $('#saveProfessionalBtn').prop('disabled', false).html('Save Professional');
+ });
+ }
+
+ /**
+ * Validate professional form
+ */
+ function validateProfessionalForm() {
+ console.log('Validating professional form...');
+
+ const requiredFields = ['username', 'first_name', 'last_name', 'email', 'specialization'];
+ const isEditMode = $('#modalTitle').text().includes('Edit');
+
+ // Check if password is required (only for new professionals)
+ if (!isEditMode) {
+ requiredFields.push('password');
+ } else {
+ // For edit mode, make password optional
+ $('#password').prop('required', false);
+ }
+
+ let isValid = true;
+ let errorMessage = '';
+
+ // Clear previous validation errors
+ $('.is-invalid').removeClass('is-invalid');
+
+ // Check required fields
+ requiredFields.forEach(field => {
+ const value = $(`#${field}`).val().trim();
+ if (!value) {
+ $(`#${field}`).addClass('is-invalid');
+ isValid = false;
+ const fieldName = field.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase());
+ errorMessage += `${fieldName} is required.\n`;
+ } else {
+ $(`#${field}`).removeClass('is-invalid');
+ }
+ });
+
+ // Validate email format
+ const email = $('#email').val().trim();
+ if (email && !isValidEmail(email)) {
+ $('#email').addClass('is-invalid');
+ isValid = false;
+ errorMessage += 'Please enter a valid email address.\n';
+ }
+
+ // Validate phone format (if provided)
+ const phone = $('#phone').val().trim();
+ if (phone && !isValidPhone(phone)) {
+ $('#phone').addClass('is-invalid');
+ isValid = false;
+ errorMessage += 'Please enter a valid phone number.\n';
+ }
+
+ // Check if at least one expertise area is selected
+ if (!validateExpertiseAreas()) {
+ isValid = false;
+ errorMessage += 'Please select at least one expertise area.\n';
+ }
+
+ // Validate experience years
+ const experienceYears = parseInt($('#experience_years').val()) || 0;
+ if (experienceYears < 0) {
+ $('#experience_years').addClass('is-invalid');
+ isValid = false;
+ errorMessage += 'Experience years cannot be negative.\n';
+ }
+
+ // Validate consultation fee
+ const consultationFee = parseFloat($('#consultation_fee').val()) || 0;
+ if (consultationFee < 0) {
+ $('#consultation_fee').addClass('is-invalid');
+ isValid = false;
+ errorMessage += 'Consultation fee cannot be negative.\n';
+ }
+
+ if (!isValid) {
+ console.error(' Form validation failed:', errorMessage);
+ Swal.fire({
+ title: 'Validation Error',
+ text: errorMessage.trim(),
+ icon: 'error',
+ confirmButtonText: 'Fix Issues'
+ });
+ } else {
+ console.log(' Form validation passed');
+ }
+
+ return isValid;
+ }
+
+ /**
+ * Validate email format
+ */
+ function isValidEmail(email) {
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ return emailRegex.test(email);
+ }
+
+ /**
+ * Validate phone format
+ */
+ function isValidPhone(phone) {
+ const phoneRegex = /^[\+]?[0-9\s\-\(\)]{10,}$/;
+ return phoneRegex.test(phone);
+ }
+
+
+ /**
+ * Reset professional form
+ */
+ function resetProfessionalForm() {
+ $('#professionalForm')[0].reset();
+ $('input[name="expertise"]').prop('checked', false);
+ $('.is-invalid').removeClass('is-invalid');
+ currentProfessionalId = null;
+ $('#password').prop('required', true);
+ }
+
+ /**
+ * Ensure all inputs are working properly
+ */
+ function ensureInputsWorking() {
+ const form = $('#professionalForm');
+ const inputs = form.find('input, select, textarea');
+
+ console.log('๐ง Ensuring input functionality for', inputs.length, 'inputs');
+
+ inputs.each(function() {
+ const input = $(this);
+
+ // Ensure all inputs are enabled
+ input.prop('disabled', false);
+ input.prop('readonly', false);
+
+ // Force CSS properties
+ input.css({
+ 'background-color': '#fff !important',
+ 'color': '#495057 !important',
+ 'pointer-events': 'auto !important',
+ 'user-select': 'text !important',
+ 'cursor': 'text !important'
+ });
+
+ // Add click handler to ensure focus
+ input.off('click.ensureFocus').on('click.ensureFocus', function() {
+ $(this).focus();
+ console.log(' Input clicked:', $(this).attr('name'));
+ });
+
+ // Add keydown handler to ensure typing works
+ input.off('keydown.ensureTyping').on('keydown.ensureTyping', function(e) {
+ console.log(' Key pressed:', e.key, 'in', $(this).attr('name'));
+ // Allow all normal typing
+ if (e.key.length === 1 || e.key === 'Backspace' || e.key === 'Delete' ||
+ e.key === 'ArrowLeft' || e.key === 'ArrowRight' || e.key === 'Tab') {
+ return true;
+ }
+ });
+
+ // Add input handler for real-time validation
+ input.off('input.validate').on('input.validate', function() {
+ console.log(' Input changed:', $(this).attr('name'), '=', $(this).val());
+ validateInput($(this));
+ });
+ });
+
+ console.log(' Input functionality ensured for', inputs.length, 'inputs');
+ }
+
+ /**
+ * Validate individual input
+ */
+ function validateInput(input) {
+ const value = input.val().trim();
+ const isRequired = input.prop('required');
+
+ if (isRequired && !value) {
+ input.removeClass('is-valid').addClass('is-invalid');
+ } else if (value) {
+ input.removeClass('is-invalid').addClass('is-valid');
+ } else {
+ input.removeClass('is-invalid is-valid');
+ }
+
+ // Check form validity
+ checkFormValidity();
+ }
+
+ /**
+ * Check overall form validity
+ */
+ function checkFormValidity() {
+ const form = $('#professionalForm');
+ const requiredFields = form.find('[required]');
+ let isValid = true;
+
+ requiredFields.each(function() {
+ const field = $(this);
+ const value = field.val().trim();
+ if (!value) {
+ isValid = false;
+ return false;
+ }
+ });
+
+ // Check expertise areas
+ if (!validateExpertiseAreas()) {
+ isValid = false;
+ }
+
+ // Enable/disable submit button
+ const submitBtn = form.find('button[type="submit"]');
+ if (submitBtn.length) {
+ submitBtn.prop('disabled', !isValid);
+ }
+
+ return isValid;
+ }
+
+ /**
+ * Debug form inputs
+ */
+ function debugFormInputs() {
+ const form = $('#professionalForm');
+ const inputs = form.find('input, select, textarea');
+
+ console.log(' Debugging form inputs:');
+ inputs.each(function(index) {
+ const input = $(this);
+ const isFocusable = function() {
+ try {
+ input.focus();
+ return document.activeElement === input[0];
+ } catch (e) {
+ return false;
+ }
+ }();
+
+ console.log(`Input ${index}:`, {
+ type: input.attr('type') || input.prop('tagName').toLowerCase(),
+ name: input.attr('name'),
+ id: input.attr('id'),
+ value: input.val(),
+ disabled: input.prop('disabled'),
+ readonly: input.prop('readonly'),
+ focusable: isFocusable,
+ style: input.attr('style')
+ });
+ });
+ }
+
+ /**
+ * Force input functionality
+ */
+ function forceInputFunctionality() {
+ const form = $('#professionalForm');
+ const inputs = form.find('input, select, textarea');
+
+ inputs.each(function() {
+ const input = $(this);
+
+ // Force enable inputs
+ input.prop('disabled', false);
+ input.prop('readonly', false);
+
+ // Force CSS properties
+ input.css({
+ 'background-color': '#fff',
+ 'color': '#495057',
+ 'pointer-events': 'auto',
+ 'user-select': 'text',
+ 'cursor': 'text'
+ });
+
+ // Add event listeners
+ input.off('click.force').on('click.force', function() {
+ $(this).focus();
+ console.log(' Input clicked:', $(this).attr('name'));
+ });
+
+ input.off('keydown.force').on('keydown.force', function(e) {
+ console.log(' Key pressed:', e.key, 'in', $(this).attr('name'));
+ });
+
+ input.off('input.force').on('input.force', function() {
+ console.log(' Input changed:', $(this).attr('name'), '=', $(this).val());
+ });
+ });
+
+ console.log('๐ง Forced input functionality for', inputs.length, 'inputs');
+ }
+
+ /**
+ * Filter professionals by search term
+ */
+ function filterProfessionals(searchTerm) {
+ console.log(' Filtering professionals by:', searchTerm);
+
+ if (dataTables.professionals) {
+ dataTables.professionals.search(searchTerm).draw();
+ } else {
+ // Fallback: filter table rows manually
+ $('#professionalsTableBody tr').each(function() {
+ const row = $(this);
+ const text = row.text().toLowerCase();
+ if (text.includes(searchTerm)) {
+ row.show();
+ } else {
+ row.hide();
+ }
+ });
+ }
+ }
+
+ /**
+ * Filter professionals by specialization
+ */
+ function filterProfessionalsBySpecialization(specialization) {
+ console.log(' Filtering professionals by specialization:', specialization);
+
+ if (dataTables.professionals) {
+ if (specialization === '') {
+ dataTables.professionals.column(2).search('').draw();
+ } else {
+ dataTables.professionals.column(2).search(specialization).draw();
+ }
+ } else {
+ // Fallback: filter table rows manually
+ $('#professionalsTableBody tr').each(function() {
+ const row = $(this);
+ const specializationCell = row.find('td:eq(2)').text().toLowerCase();
+ if (specialization === '' || specializationCell.includes(specialization.toLowerCase())) {
+ row.show();
+ } else {
+ row.hide();
+ }
+ });
+ }
+ }
+
+ /**
+ * Apply filters
+ */
+ function applyFilters() {
+ const status = $('#statusFilter').val();
+ const riskLevel = $('#riskLevelFilter').val();
+ const specialization = $('#specializationFilter').val();
+
+ // Apply filters to DataTables
+ if (dataTables.bookings) {
+ dataTables.bookings.column(5).search(status).draw();
+ }
+ if (dataTables.professionals) {
+ dataTables.professionals.column(2).search(specialization).draw();
+ }
+ }
+
+ /**
+ * Export table to CSV
+ */
+ function exportTableToCSV(tableId, filename) {
+ const table = document.getElementById(tableId);
+ const rows = table.querySelectorAll('tr');
+ let csv = [];
+
+ for (let i = 0; i < rows.length; i++) {
+ const row = [];
+ const cols = rows[i].querySelectorAll('td, th');
+
+ for (let j = 0; j < cols.length; j++) {
+ row.push(cols[j].innerText);
+ }
+ csv.push(row.join(','));
+ }
+
+ const csvContent = csv.join('\n');
+ const blob = new Blob([csvContent], { type: 'text/csv' });
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ a.click();
+ window.URL.revokeObjectURL(url);
+ }
+
+ /**
+ * Get risk badge class
+ */
+ function getRiskBadgeClass(riskLevel) {
+ const classes = {
+ 'critical': 'danger',
+ 'high': 'warning',
+ 'medium': 'info',
+ 'low': 'success'
+ };
+ return classes[riskLevel.toLowerCase()] || 'secondary';
+ }
+
+ /**
+ * Get status badge class
+ */
+ function getStatusBadgeClass(status) {
+ const classes = {
+ 'pending': 'warning',
+ 'confirmed': 'info',
+ 'completed': 'success',
+ 'declined': 'danger',
+ 'active': 'success',
+ 'inactive': 'secondary'
+ };
+ return classes[status.toLowerCase()] || 'secondary';
+ }
+
+ /**
+ * Get current professional ID for editing
+ */
+ function getCurrentProfessionalId() {
+ return currentProfessionalId;
+ }
+
+ /**
+ * Handle API errors gracefully
+ */
+ function handleAPIError(error, context = 'API call') {
+ console.error(`Error in ${context}:`, error);
+
+ // Show user-friendly error message
+ Swal.fire({
+ title: 'Connection Error',
+ text: 'Unable to connect to the server. Please check your internet connection and try again.',
+ icon: 'error',
+ timer: 5000
+ });
+ }
+
+ /**
+ * Refresh all data
+ */
+ function refreshAllData() {
+ // Show loading state
+ const refreshBtn = $('#refreshAllBtn');
+ const originalText = refreshBtn.html();
+ refreshBtn.prop('disabled', true).html(' Refreshing...');
+
+ // Refresh current section data
+ if (currentSection === 'dashboard') {
+ loadDashboardData();
+ } else {
+ loadSectionData(currentSection);
+ }
+
+ // Reset button after a delay
+ setTimeout(() => {
+ refreshBtn.prop('disabled', false).html(originalText);
+ }, 2000);
+ }
+
+ /**
+ * Start auto-refresh
+ */
+ function startAutoRefresh() {
+ setInterval(() => {
+ if (currentSection === 'dashboard') {
+ loadDashboardData();
+ } else {
+ loadSectionData(currentSection);
+ }
+ }, 30000); // Refresh every 30 seconds
+ }
+
+ /**
+ * Toggle professional status
+ */
+ function toggleProfessionalStatus(profId) {
+ // Get current status from the button
+ const button = $(`button[onclick="toggleProfessionalStatus(${profId})"]`);
+ const isCurrentlyActive = button.hasClass('btn-warning'); // warning = active, success = inactive
+
+ fetch(`${API_ROOT}/admin/professionals/${profId}/status`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ is_active: !isCurrentlyActive
+ })
+ })
+ .then(response => response.json())
+ .then(data => {
+ if (data.success) {
+ Swal.fire({
+ title: 'Success!',
+ text: data.message || 'Professional status updated.',
+ icon: 'success',
+ timer: 2000
+ }).then(() => {
+ loadProfessionals();
+ });
+ } else {
+ Swal.fire({
+ title: 'Error!',
+ text: data.error || 'Failed to update professional status.',
+ icon: 'error'
+ });
+ }
+ })
+ .catch(error => {
+ console.error('Error toggling professional status:', error);
+ Swal.fire({
+ title: 'Error!',
+ text: 'Failed to update professional status.',
+ icon: 'error'
+ });
+ });
+ }
+
+ /**
+ * Global functions for onclick handlers
+ */
+ window.editProfessional = function(id) {
+ console.log(' Editing professional with ID:', id);
+
+ // Show loading state
+ Swal.fire({
+ title: 'Loading...',
+ text: 'Loading professional data...',
+ allowOutsideClick: false,
+ didOpen: () => {
+ Swal.showLoading();
+ }
+ });
+
+ // Load professional data and populate form
+ fetch(`${API_ROOT}/admin/professionals`)
+ .then(response => {
+ console.log('๐ก Edit professional API response status:', response.status);
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+ }
+ return response.json();
+ })
+ .then(data => {
+ console.log(' Professional data for editing:', data);
+ const professional = data.professionals.find(p => p.id === id);
+
+ if (professional) {
+ console.log(' Professional found:', professional);
+
+ // Store current professional ID for editing
+ currentProfessionalId = id;
+
+ // Populate form with professional data
+ $('#username').val(professional.username || '');
+ $('#first_name').val(professional.first_name || '');
+ $('#last_name').val(professional.last_name || '');
+ $('#email').val(professional.email || '');
+ $('#phone').val(professional.phone || '');
+ $('#specialization').val(professional.specialization || '');
+ $('#experience_years').val(professional.experience_years || 0);
+ $('#district').val(professional.district || '');
+ $('#consultation_fee').val(professional.consultation_fee || 0);
+ $('#bio').val(professional.bio || '');
+
+ // Set expertise checkboxes
+ if (professional.expertise_areas) {
+ let expertiseAreas = [];
+ if (Array.isArray(professional.expertise_areas)) {
+ expertiseAreas = professional.expertise_areas;
+ } else if (typeof professional.expertise_areas === 'string') {
+ expertiseAreas = professional.expertise_areas.split(',').map(area => area.trim());
+ }
+
+ $('input[name="expertise"]').prop('checked', false);
+ expertiseAreas.forEach(area => {
+ const trimmedArea = area.trim();
+ if (trimmedArea) {
+ $(`#expertise_${trimmedArea}`).prop('checked', true);
+ }
+ });
+ } else {
+ $('input[name="expertise"]').prop('checked', false);
+ }
+
+ // Update modal for edit mode
+ $('#modalTitle').text('Edit Professional');
+ $('#passwordRequired').text('');
+ $('#passwordHelp').show();
+ $('#password').prop('required', false).val('');
+
+ // Close loading dialog and show modal
+ Swal.close();
+ $('#professionalModal').modal('show');
+
+ // Ensure inputs work properly after modal is shown
+ setTimeout(() => {
+ ensureInputsWorking();
+ forceInputFunctionality();
+ debugFormInputs();
+ const firstInput = $('#professionalModal input[required]').first();
+ if (firstInput.length) {
+ firstInput.focus();
+ console.log(' Focused on first input:', firstInput.attr('name'));
+ }
+ }, 300);
+
+ console.log(' Professional form populated successfully');
+ } else {
+ console.error(' Professional not found with ID:', id);
+ Swal.fire('Error', 'Professional not found.', 'error');
+ }
+ })
+ .catch(error => {
+ console.error(' Error loading professional:', error);
+ Swal.fire('Error', `Failed to load professional data: ${error.message}`, 'error');
+ });
+ };
+
+ window.deleteProfessional = function(id) {
+ Swal.fire({
+ title: 'Delete Professional?',
+ text: 'This action cannot be undone!',
+ icon: 'warning',
+ showCancelButton: true,
+ confirmButtonColor: '#d33',
+ cancelButtonColor: '#3085d6',
+ confirmButtonText: 'Yes, delete it!'
+ }).then((result) => {
+ if (result.isConfirmed) {
+ fetch(`${API_ROOT}/admin/professionals/${id}`, {
+ method: 'DELETE'
+ })
+ .then(response => response.json())
+ .then(data => {
+ if (data.success) {
+ Swal.fire('Deleted!', 'Professional has been deleted.', 'success');
+ loadProfessionals();
+ } else {
+ Swal.fire('Error!', data.error || 'Failed to delete professional.', 'error');
+ }
+ })
+ .catch(error => {
+ console.error('Error deleting professional:', error);
+ Swal.fire('Error!', 'Failed to delete professional.', 'error');
+ });
+ }
+ });
+ };
+
+ window.toggleProfessionalStatus = toggleProfessionalStatus;
+
+ window.viewBooking = function(id) {
+ // Show loading state
+ $('#bookingDetails').html(' Loading booking details...
');
+ $('#bookingModal').modal('show');
+
+ // Load booking details
+ fetch(`${API_ROOT}/admin/bookings`)
+ .then(response => response.json())
+ .then(data => {
+ const booking = data.bookings.find(b => b.booking_id === id);
+ if (booking) {
+ const scheduledTime = new Date(booking.scheduled_datetime * 1000).toLocaleString();
+ const createdTime = new Date(booking.created_ts * 1000).toLocaleString();
+ const userInitials = getUserInitials(booking.user_fullname || booking.user_account || 'Guest');
+ const professionalInitials = getUserInitials(booking.professional_name || 'Unassigned');
+
+ const bookingDetails = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Location Information
+
Province: ${booking.user_province || 'Not specified'}
+
District: ${booking.user_district || 'Not specified'}
+
Full Location: ${booking.user_location || 'Location not specified'}
+
IP Address: ${booking.user_ip || 'N/A'}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${professionalInitials}
+
+
+
+
+
+
+
Assignment Status
+
Status: ${booking.professional_name ? 'Assigned' : 'Unassigned'}
+
Assignment Date: ${booking.professional_name ? createdTime : 'Pending'}
+
Professional ID: ${booking.professional_id || 'N/A'}
+
Experience: ${booking.professional_experience || 'N/A'} years
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Schedule Information
+
Scheduled Time: ${scheduledTime}
+
Created: ${createdTime}
+
Session Type: ${booking.session_type || 'Emergency'}
+
Duration: ${booking.session_duration || '60 minutes'}
+
+
+
Risk Assessment
+
Risk Level:
+
+
+ ${booking.risk_level.toUpperCase()}
+
+
+
Risk Score: ${(booking.risk_score * 100).toFixed(1)}%
+
Detected Indicators: ${booking.detected_indicators || 'None detected'}
+
Assessment Time: ${new Date(booking.assessment_timestamp * 1000).toLocaleString()}
+
+
+
+
+
+
+
+
+
+
+
+
Session Details
+
Location Preference: ${booking.location_preference || 'Not specified'}
+
Session Notes: ${booking.session_notes || 'No notes available'}
+
Treatment Plan: ${booking.treatment_plan || 'Not available'}
+
+
+
System Information
+
Conversation ID: ${booking.conv_id || 'N/A'}
+
Booking Source: ${booking.booking_source || 'Automated'}
+
Last Updated: ${new Date(booking.updated_ts * 1000).toLocaleString()}
+
System Notes: ${booking.notes || 'No additional notes'}
+
+
+
+
+ `;
+ $('#bookingDetails').html(bookingDetails);
+ } else {
+ $('#bookingDetails').html('Booking not found.
');
+ }
+ })
+ .catch(error => {
+ console.error('Error loading booking details:', error);
+ $('#bookingDetails').html('Error loading booking details.
');
+ });
+ };
+
+ /**
+ * Get status icon for display
+ */
+ function getStatusIcon(status) {
+ const statusIcons = {
+ pending: 'clock',
+ confirmed: 'check-circle',
+ completed: 'check-double',
+ declined: 'times-circle',
+ cancelled: 'ban'
+ };
+ return statusIcons[status.toLowerCase()] || 'question-circle';
+ }
+
+ /**
+ * Get risk icon for display
+ */
+ function getRiskIcon(riskLevel) {
+ const riskIcons = {
+ critical: 'exclamation-triangle',
+ high: 'exclamation-circle',
+ medium: 'info-circle',
+ low: 'check-circle'
+ };
+ return riskIcons[riskLevel.toLowerCase()] || 'question-circle';
+ }
+
+ window.editBooking = function(id) {
+ Swal.fire({
+ title: 'Edit Booking',
+ text: `Edit booking with ID: ${id}`,
+ icon: 'info',
+ showCancelButton: true,
+ confirmButtonText: 'View Details',
+ cancelButtonText: 'Cancel'
+ }).then((result) => {
+ if (result.isConfirmed) {
+ viewBooking(id);
+ }
+ });
+ };
+
+
+
+ // Global debug functions
+ window.debugFormInputs = debugFormInputs;
+ window.forceInputFunctionality = forceInputFunctionality;
+ window.ensureInputsWorking = ensureInputsWorking;
+ window.testInputs = function() {
+ console.log('๐งช Testing input functionality...');
+ const form = $('#professionalForm');
+ if (form.length === 0) {
+ console.log(' Form not found');
+ return;
+ }
+
+ const inputs = form.find('input, select, textarea');
+ console.log('๐ Found', inputs.length, 'inputs');
+
+ inputs.each(function(index) {
+ const input = $(this);
+ console.log(`Input ${index}:`, {
+ name: input.attr('name'),
+ type: input.attr('type'),
+ value: input.val(),
+ disabled: input.prop('disabled'),
+ readonly: input.prop('readonly')
+ });
+ });
+
+ // Test focus
+ const firstInput = inputs.first();
+ if (firstInput.length) {
+ firstInput.focus();
+ console.log(' Focused on first input:', firstInput.attr('name'));
+ }
+ };
+
+ // Show dashboard by default
+ showSection('dashboard');
+
+ /**
+ * Initialize expertise areas functionality
+ */
+ function initializeExpertiseAreas() {
+ console.log('๐ง Initializing expertise areas functionality...');
+
+ // Select All button
+ $('#selectAllExpertise').on('click', function() {
+ $('input[name="expertise"]').prop('checked', true);
+ updateExpertiseCount();
+ updateExpertiseValidation();
+ console.log(' All expertise areas selected');
+ });
+
+ // Clear All button
+ $('#clearAllExpertise').on('click', function() {
+ $('input[name="expertise"]').prop('checked', false);
+ updateExpertiseCount();
+ updateExpertiseValidation();
+ console.log(' All expertise areas cleared');
+ });
+
+ // Individual checkbox change
+ $('input[name="expertise"]').on('change', function() {
+ updateExpertiseCount();
+ updateExpertiseValidation();
+
+ // Add visual feedback
+ const label = $(this).next('.expertise-label');
+ if ($(this).is(':checked')) {
+ label.addClass('selected');
+ console.log(' Expertise selected:', $(this).val());
+ } else {
+ label.removeClass('selected');
+ console.log(' Expertise deselected:', $(this).val());
+ }
+ });
+
+ // Initialize count
+ updateExpertiseCount();
+ updateExpertiseValidation();
+
+ console.log(' Expertise areas functionality initialized');
+ }
+
+ /**
+ * Update expertise selection count
+ */
+ function updateExpertiseCount() {
+ const selectedCount = $('input[name="expertise"]:checked').length;
+ const totalCount = $('input[name="expertise"]').length;
+
+ $('#selectedCount').text(selectedCount);
+
+ // Update select all button state
+ const selectAllBtn = $('#selectAllExpertise');
+ const clearAllBtn = $('#clearAllExpertise');
+
+ if (selectedCount === totalCount) {
+ selectAllBtn.addClass('btn-primary').removeClass('btn-outline-primary');
+ selectAllBtn.html(' All Selected');
+ } else {
+ selectAllBtn.addClass('btn-outline-primary').removeClass('btn-primary');
+ selectAllBtn.html(' Select All');
+ }
+
+ if (selectedCount === 0) {
+ clearAllBtn.addClass('btn-secondary').removeClass('btn-outline-secondary');
+ clearAllBtn.html(' All Cleared');
+ } else {
+ clearAllBtn.addClass('btn-outline-secondary').removeClass('btn-secondary');
+ clearAllBtn.html(' Clear All');
+ }
+ }
+
+ /**
+ * Update expertise validation state
+ */
+ function updateExpertiseValidation() {
+ const selectedCount = $('input[name="expertise"]:checked').length;
+ const expertiseContainer = $('.form-group:has(input[name="expertise"])');
+
+ if (selectedCount === 0) {
+ expertiseContainer.addClass('is-invalid').removeClass('is-valid');
+ $('input[name="expertise"]').addClass('is-invalid');
+ } else {
+ expertiseContainer.removeClass('is-invalid').addClass('is-valid');
+ $('input[name="expertise"]').removeClass('is-invalid');
+ }
+ }
+
+ /**
+ * Validate expertise areas selection
+ */
+ function validateExpertiseAreas() {
+ const selectedCount = $('input[name="expertise"]:checked').length;
+ return selectedCount > 0;
+ }
+
+ /**
+ * Initialize bookings filtering functionality
+ */
+ function initializeBookingsFiltering() {
+ console.log(' Initializing bookings filtering...');
+
+ // Filter change events
+ $('#statusFilter, #riskLevelFilter, #professionalFilter, #fromDateFilter, #toDateFilter').on('change', function() {
+ applyBookingsFilters();
+ });
+
+ // Search input
+ $('#bookingSearch').on('keyup', function() {
+ clearTimeout(this.searchTimeout);
+ this.searchTimeout = setTimeout(() => {
+ applyBookingsFilters();
+ }, 300);
+ });
+
+ // Clear filters button
+ $('#clearFiltersBtn').on('click', function() {
+ clearBookingsFilters();
+ });
+
+ // Apply filters button
+ $('#applyFiltersBtn').on('click', function() {
+ applyBookingsFilters();
+ });
+
+ console.log(' Bookings filtering initialized');
+ }
+
+ /**
+ * Apply bookings filters
+ */
+ function applyBookingsFilters() {
+ const status = $('#statusFilter').val();
+ const riskLevel = $('#riskLevelFilter').val();
+ const professional = $('#professionalFilter').val();
+ const fromDate = $('#fromDateFilter').val();
+ const toDate = $('#toDateFilter').val();
+ const search = $('#bookingSearch').val().toLowerCase();
+
+ console.log(' Applying filters:', { status, riskLevel, professional, fromDate, toDate, search });
+
+ if (dataTables.bookings) {
+ dataTables.bookings.column(5).search(status); // Status column
+ dataTables.bookings.column(3).search(riskLevel); // Risk level column
+ dataTables.bookings.column(2).search(professional); // Professional column
+ dataTables.bookings.search(search).draw();
+ }
+
+ // Update filter button states
+ updateFilterButtonStates();
+ }
+
+ /**
+ * Clear all bookings filters
+ */
+ function clearBookingsFilters() {
+ $('#statusFilter, #riskLevelFilter, #professionalFilter').val('');
+ $('#fromDateFilter, #toDateFilter').val('');
+ $('#bookingSearch').val('');
+
+ if (dataTables.bookings) {
+ dataTables.bookings.search('').columns().search('').draw();
+ }
+
+ updateFilterButtonStates();
+ console.log('๐งน Filters cleared');
+ }
+
+ /**
+ * Update filter button states
+ */
+ function updateFilterButtonStates() {
+ const hasActiveFilters = $('#statusFilter').val() ||
+ $('#riskLevelFilter').val() ||
+ $('#professionalFilter').val() ||
+ $('#fromDateFilter').val() ||
+ $('#toDateFilter').val() ||
+ $('#bookingSearch').val();
+
+ if (hasActiveFilters) {
+ $('#clearFiltersBtn').removeClass('btn-outline-secondary').addClass('btn-secondary');
+ $('#applyFiltersBtn').removeClass('btn-outline-primary').addClass('btn-primary');
+ } else {
+ $('#clearFiltersBtn').removeClass('btn-secondary').addClass('btn-outline-secondary');
+ $('#applyFiltersBtn').removeClass('btn-primary').addClass('btn-outline-primary');
+ }
+ }
+
+ /**
+ * Complete booking action
+ */
+ window.completeBooking = function(bookingId) {
+ Swal.fire({
+ title: 'Complete Booking',
+ text: 'Are you sure you want to mark this booking as completed?',
+ icon: 'question',
+ showCancelButton: true,
+ confirmButtonText: 'Yes, Complete',
+ cancelButtonText: 'Cancel',
+ confirmButtonColor: '#10b981'
+ }).then((result) => {
+ if (result.isConfirmed) {
+ // Update booking status
+ updateBookingStatus(bookingId, 'completed');
+ }
+ });
+ };
+
+ /**
+ * Cancel booking action
+ */
+ window.cancelBooking = function(bookingId) {
+ Swal.fire({
+ title: 'Cancel Booking',
+ text: 'Are you sure you want to cancel this booking?',
+ icon: 'warning',
+ showCancelButton: true,
+ confirmButtonText: 'Yes, Cancel',
+ cancelButtonText: 'Keep Booking',
+ confirmButtonColor: '#ef4444'
+ }).then((result) => {
+ if (result.isConfirmed) {
+ // Update booking status
+ updateBookingStatus(bookingId, 'cancelled');
+ }
+ });
+ };
+
+ /**
+ * Update booking status
+ */
+ function updateBookingStatus(bookingId, newStatus) {
+ console.log(`๐ Updating booking ${bookingId} to ${newStatus}`);
+
+ // Show loading
+ Swal.fire({
+ title: 'Updating...',
+ text: 'Please wait while we update the booking status.',
+ allowOutsideClick: false,
+ showConfirmButton: false,
+ didOpen: () => {
+ Swal.showLoading();
+ }
+ });
+
+ // Simulate API call (replace with actual API call)
+ setTimeout(() => {
+ Swal.fire({
+ title: 'Success!',
+ text: `Booking has been ${newStatus}.`,
+ icon: 'success',
+ confirmButtonText: 'OK'
+ }).then(() => {
+ // Reload bookings
+ loadBookings();
+ });
+ }, 1000);
+ }
+
+})();
+