🔍 Code Extractor

class AdminPanel

Maturity: 27

Admin configuration interface component

File:
/tf/active/vicechatdev/CDocs/ui/admin_panel.py
Lines:
69 - 1697
Complexity:
moderate

Purpose

Admin configuration interface component

Source Code

class AdminPanel(param.Parameterized):
    """Admin configuration interface component"""
    
    current_tab = param.String(default='dashboard')
    
    def __init__(self, template, session_manager=None, parent_app=None, embedded=False, **params):
        super().__init__(**params)
        self.template = template
        self.session_manager = session_manager  # This may be None now
        self.parent_app = parent_app  # Store reference to parent app
        self.user = self._get_current_user()
        self.notification_area = pn.pane.Markdown("")
        self.embedded = embedded  # Flag for embedded mode
        
        # Add status_area attribute
        self.status_area = pn.Column(sizing_mode='stretch_width')
        
        # Set up the user interface
        if not embedded:
            # Only set up template components in standalone mode
            self._setup_header()
            self._setup_sidebar()
        
        # Set up the main content area
        self._setup_main_area()
        
        # Verify that the user has admin permissions
        if self.user and self._verify_admin_access():
            # Load initial dashboard
            self._load_admin_dashboard()
        else:
            self.notification_area.object = "**Error:** You do not have permission to access the admin panel."
    
    def _get_current_user(self):
        """Get current user from session"""
        # Try to get user from parent app first
        if hasattr(self, 'parent_app') and self.parent_app is not None:
            if hasattr(self.parent_app, 'current_user'):
                return self.parent_app.current_user
        
        # Fall back to session manager if available
        if hasattr(self, 'session_manager') and self.session_manager is not None:
            return self.session_manager.get_current_user()
        
        # Otherwise return None
        return None
    
    def _verify_admin_access(self) -> bool:
        """Verify that the user has admin permissions"""
        if not self.user:
            return False
        
        # First check direct admin permission
        has_admin_permission = permissions.user_has_permission(self.user, "ADMIN")
        
        # Also check if user has ADMIN role directly
        has_admin_role = False
        if hasattr(self.user, 'roles') and isinstance(self.user.roles, (list, tuple, set)):
            has_admin_role = 'ADMIN' in self.user.roles
        
        # Log the results
        logger.debug(f"User admin permission check: {has_admin_permission}, role check: {has_admin_role}")
        
        # Expensive operations - only do if necessary
        if not (has_admin_permission or has_admin_role):
            # Check system settings permissions as a fallback
            has_settings_permission = permissions.user_has_permission(self.user, "MANAGE_SYSTEM_SETTINGS")
            logger.debug(f"User settings permission check: {has_settings_permission}")
            return has_settings_permission
        
        return has_admin_permission or has_admin_role
    
    def _setup_header(self):
        """Set up the header with title and actions"""
        # Create back button
        back_btn = Button(
            name='Back to Dashboard',
            button_type='default',
            width=150
        )
        back_btn.on_click(self._navigate_back)
        
        # Create refresh button
        refresh_btn = Button(
            name='Refresh',
            button_type='default',
            width=100
        )
        refresh_btn.on_click(self._refresh_current_view)
        
        # Header with buttons
        header = Row(
            pn.pane.Markdown("# Admin Panel"),
            refresh_btn,
            back_btn,
            sizing_mode='stretch_width',
            align='end'
        )
        
        self.template.header.append(header)
    
    def _setup_sidebar(self):
        """Set up the sidebar with navigation options"""
        # Create navigation buttons
        dashboard_btn = Button(
            name='System Dashboard',
            button_type='primary',
            width=200
        )
        dashboard_btn.on_click(self._load_admin_dashboard)
        
        users_btn = Button(
            name='User Management',
            button_type='default',
            width=200
        )
        users_btn.on_click(self._load_user_management)
        
        # Add notifications button
        notifications_btn = Button(
            name='Notifications',
            button_type='default',
            width=200
        )
        notifications_btn.on_click(self._load_notification_management)
        
        departments_btn = Button(
            name='Departments',
            button_type='default',
            width=200
        )
        departments_btn.on_click(self._load_department_management)
        
        doc_types_btn = Button(
            name='Document Types',
            button_type='default',
            width=200
        )
        doc_types_btn.on_click(self._load_document_type_management)
        
        settings_btn = Button(
            name='System Settings',
            button_type='default',
            width=200
        )
        settings_btn.on_click(self._load_system_settings)
        
        backup_btn = Button(
            name='Backup & Restore',
            button_type='default',
            width=200
        )
        backup_btn.on_click(self._load_backup_restore)
        
        # Add to navigation area
        navigation = Column(
            Markdown("## Navigation"),
            dashboard_btn,
            users_btn,
            notifications_btn,  # Add notifications to navigation
            departments_btn,
            doc_types_btn,
            settings_btn,
            backup_btn,
            sizing_mode='fixed'
        )
        
        self.template.sidebar.append(navigation)
        
        # Add system status summary
        self.status_area = Column(
            Markdown("## System Status"),
            Markdown("*Loading status...*"),
            sizing_mode='fixed',
            styles={'background':'#f8f9fa'},
            css_classes=['p-3', 'border', 'rounded']
        )
        self.template.sidebar.append(self.status_area)
        
        # Update system status
        self._update_system_status()
    
    def _setup_main_area(self):
        """Set up the main content area."""
        try:
            # Create main content container
            if not hasattr(self, 'main_content'):
                self.main_content = pn.Column(sizing_mode='stretch_width')
                
            # Create tabs for navigation if not in embedded mode
            if not self.embedded:
                # Create tabs - add Notifications tab
                self.tabs = pn.Tabs(
                    ('Dashboard', pn.Column(sizing_mode='stretch_width')),
                    ('Users', pn.Column(sizing_mode='stretch_width')),
                    ('Notifications', pn.Column(sizing_mode='stretch_width')),  # Add this tab
                    ('Departments', pn.Column(sizing_mode='stretch_width')),
                    ('Document Types', pn.Column(sizing_mode='stretch_width')),
                    ('Settings', pn.Column(sizing_mode='stretch_width')),
                    ('Backup & Restore', pn.Column(sizing_mode='stretch_width')),
                    sizing_mode='stretch_width'
                )
                
                # Handle tab changes - update indices
                def handle_tab_change(event):
                    tab_index = event.new
                    if tab_index == 0:
                        self._load_admin_dashboard()
                    elif tab_index == 1:
                        self._load_user_management()
                    elif tab_index == 2:
                        self._load_notification_management()  # Add notification management
                    elif tab_index == 3:
                        self._load_department_management()
                    elif tab_index == 4:
                        self._load_document_type_management()
                    elif tab_index == 5:
                        self._load_system_settings()
                    elif tab_index == 6:
                        self._load_backup_restore()
                        
                # Register callback
                self.tabs.param.watch(handle_tab_change, 'active')
                
                # Add tabs to main content
                self.main_content.append(self.tabs)
                
        except Exception as e:
            logger.error(f"Error setting up main area: {e}")
    
    def _refresh_current_view(self, event=None):
        """Refresh the current view"""
        if self.current_tab == 'dashboard':
            self._load_admin_dashboard()
        elif self.current_tab == 'users':
            self._load_user_management()
        elif self.current_tab == 'notifications':  # Add this case
            self._load_notification_management()
        elif self.current_tab == 'departments':
            self._load_department_management()
        elif self.current_tab == 'doc_types':
            self._load_document_type_management()
        elif self.current_tab == 'settings':
            self._load_system_settings()
        elif self.current_tab == 'backup':
            self._load_backup_restore()
    
    def _navigate_back(self, event=None):
        """Navigate back to dashboard"""
        # Check if we have a parent app reference
        if hasattr(self, 'parent_app') and self.parent_app is not None:
            # Use parent app's load_dashboard method
            try:
                self.parent_app.load_dashboard()
                return
            except Exception as e:
                logger.error(f"Error navigating back to dashboard via parent app: {e}")
        
        # Fallback to direct state manipulation
        try:
            import panel as pn
            pn.state.execute("window.location.href = '/dashboard'")
        except Exception as e:
            logger.error(f"Error navigating back to dashboard: {e}")
    
    def _update_system_status(self):
        """Update system status information in the sidebar."""
        try:
            # Create status_area if it doesn't exist
            if not hasattr(self, 'status_area'):
                self.status_area = pn.Column(sizing_mode='stretch_width')
                
            # Update status area
            self.status_area.clear()
            
            # Get system stats
            try:
                from CDocs.controllers.admin_controller import get_system_stats
                stats = get_system_stats()
                
                # Format uptime
                uptime_seconds = stats.get('uptime', 0)
                days, remainder = divmod(uptime_seconds, 86400)
                hours, remainder = divmod(remainder, 3600)
                minutes, seconds = divmod(remainder, 60)
                uptime_str = f"{days}d {hours}h {minutes}m"
                
                # Create status summary
                status_info = f"""
                ### System Status
                
                **Users:** {stats.get('active_users', 0)}/{stats.get('total_users', 0)} active  
                **Documents:** {stats.get('active_documents', 0)}/{stats.get('total_documents', 0)} active  
                **Reviews:** {stats.get('pending_reviews', 0)} pending  
                **Approvals:** {stats.get('pending_approvals', 0)} pending  
                **Uptime:** {uptime_str}  
                """
                
                self.status_area.append(pn.pane.Markdown(status_info))
                
            except Exception as e:
                logger.warning(f"Error getting system stats: {e}")
                self.status_area.append(pn.pane.Markdown(
                    "### System Status\n\n*Status information unavailable*"
                ))
                
        except Exception as e:
            logger.error(f"Error updating system status: {e}")
    
    def _format_duration(self, seconds):
        """Format duration in seconds to readable string"""
        if seconds < 60:
            return f"{seconds} seconds"
            
        minutes, seconds = divmod(seconds, 60)
        if minutes < 60:
            return f"{minutes} minutes, {seconds} seconds"
            
        hours, minutes = divmod(minutes, 60)
        if hours < 24:
            return f"{hours} hours, {minutes} minutes"
            
        days, hours = divmod(hours, 24)
        return f"{days} days, {hours} hours"
    
    def _load_admin_dashboard(self, event=None):
        """Load the admin dashboard view"""
        try:
            self.current_tab = 'dashboard'
            self.notification_area.object = "Loading dashboard data..."
            self.main_content.clear()
            
            # Update parent sidebar if available
            self.update_parent_sidebar()
            
            # Get system statistics with fallback
            try:
                stats = get_system_stats()
            except Exception as e:
                logger.warning(f"Error fetching system stats: {e}")
                stats = {
                    'active_users': 0,
                    'total_users': 0,
                    'active_documents': 0,
                    'total_documents': 0,
                    'pending_reviews': 0,
                    'total_reviews': 0,
                    'pending_approvals': 0,
                    'total_approvals': 0,
                    'uptime': 0
                }
            
            # Get document statistics with fallback
            try:
                doc_stats = get_document_stats()
            except Exception as e:
                logger.warning(f"Error fetching document stats: {e}")
                doc_stats = {
                    'status_distribution': {'DRAFT': 0, 'IN_REVIEW': 0, 'APPROVED': 0, 'PUBLISHED': 0},
                    'type_distribution': {'SOP': 0, 'POLICY': 0, 'WORK_INSTRUCTION': 0}
                }
            
            # Get recent user activity with fallback
            try:
                user_activity = get_user_activity(limit=10)
            except Exception as e:
                logger.warning(f"Error fetching user activity: {e}")
                user_activity = []
            
            # Create dashboard container
            dashboard = Column(
                Markdown("# System Dashboard"),
                sizing_mode='stretch_width'
            )
            
            # Create activity summary cards
            active_users = stats.get('active_users', 0)
            total_users = stats.get('total_users', 0)
            user_percent = 0
            if total_users > 0:
                user_percent = int((active_users / total_users) * 100)
                
            active_docs = stats.get('active_documents', 0)
            total_docs = stats.get('total_documents', 0)
            doc_percent = 0
            if total_docs > 0:
                doc_percent = int((active_docs / total_docs) * 100)
            
            # Create metrics row
            metrics_row = Row(
                self._create_metric_card("Users", active_users, total_users, user_percent),
                self._create_metric_card("Documents", active_docs, total_docs, doc_percent),
                self._create_metric_card("Reviews", stats.get('pending_reviews', 0), stats.get('total_reviews', 0)),
                self._create_metric_card("Approvals", stats.get('pending_approvals', 0), stats.get('total_approvals', 0)),
                sizing_mode='stretch_width'
            )
            dashboard.append(metrics_row)
            
            # Create document status chart
            doc_status_chart = self._create_document_status_chart(doc_stats)
            
            # Create document type chart
            doc_type_chart = self._create_document_type_chart(doc_stats)
            
            # Create charts row
            charts_row = Row(
                Column(
                    Markdown("## Document Status"),
                    doc_status_chart,
                    styles={'background':'#f8f9fa'},
                    css_classes=['p-3', 'border', 'rounded']
                ),
                Column(
                    Markdown("## Document Types"),
                    doc_type_chart,
                    styles={'background':'#f8f9fa'},
                    css_classes=['p-3', 'border', 'rounded']
                ),
                sizing_mode='stretch_width'
            )
            dashboard.append(charts_row)
            
            # Create recent activity table
            activity_df = pd.DataFrame(user_activity)
            
            # Format date columns
            if 'timestamp' in activity_df.columns:
                activity_df['timestamp'] = pd.to_datetime(activity_df['timestamp']).dt.strftime('%Y-%m-%d %H:%M')
            
            # Select and rename columns
            display_columns = ['timestamp', 'user_name', 'action_type', 'action_detail']
            column_names = {
                'timestamp': 'Time',
                'user_name': 'User',
                'action_type': 'Action',
                'action_detail': 'Details'
            }
            
            # Filter and rename columns
            exist_columns = [col for col in display_columns if col in activity_df.columns]
            activity_df = activity_df[exist_columns]
            rename_dict = {col: column_names[col] for col in exist_columns if col in column_names}
            activity_df = activity_df.rename(columns=rename_dict)
            
            # Create activity table
            activity_table = Tabulator(
                activity_df,
                pagination='local',
                page_size=10,
                sizing_mode='stretch_width',
                height=300
            )
            
            # Add to dashboard
            activity_section = Column(
                Markdown("## Recent Activity"),
                activity_table,
                styles={'background':'#f8f9fa'},
                css_classes=['p-3', 'border', 'rounded'],
                sizing_mode='stretch_width'
            )
            dashboard.append(activity_section)
            
            # Add dashboard to main content
            self.main_content.append(dashboard)
            
            # Clear notification
            self.notification_area.object = ""
            
        except Exception as e:
            logger.error(f"Error loading admin dashboard: {e}")
            self.notification_area.object = f"**Error:** {str(e)}"
            self.main_content.clear()
            self.main_content.append(Markdown("# System Dashboard"))
            self.main_content.append(Markdown("*Error loading dashboard data*"))
    
    def _create_metric_card(self, title, value, total=None, percent=None):
        """Create a metric card for dashboard"""
        if total is not None:
            content = f"<h3>{value} / {total}</h3>"
        else:
            content = f"<h3>{value}</h3>"
            
        if percent is not None:
            progress_html = f"""
            <div style="width:100%; background-color:#e0e0e0; border-radius:5px; margin:10px 0;">
                <div style="width:{percent}%; background-color:#28a745; height:10px; border-radius:5px;"></div>
            </div>
            <div style="text-align:center;">{percent}%</div>
            """
            content += progress_html
            
        card = Column(
            HTML(f"<h2>{title}</h2>"),
            HTML(content),
            width=200,
            height=150,
            styles={'background':'#f8f9fa'},
            css_classes=['p-3', 'border', 'rounded', 'text-center']
        )
        
        return card
    
    def _create_document_status_chart(self, doc_stats):
        """Create a chart showing document status distribution"""
        try:
            # Extract status data
            status_data = doc_stats.get('status_distribution', {})
            
            # Convert to DataFrame for visualization
            status_df = pd.DataFrame({
                'Status': list(status_data.keys()),
                'Count': list(status_data.values())
            })
            
            # Sort by count
            status_df = status_df.sort_values('Count', ascending=False)
            
            # Create matplotlib figure
            fig = plt.figure(figsize=(8, 4))
            ax = fig.add_subplot(111)
            
            bars = ax.bar(status_df['Status'], status_df['Count'], color='steelblue')
            
            # Add value labels on top of bars
            for bar in bars:
                height = bar.get_height()
                ax.text(bar.get_x() + bar.get_width()/2., height + 0.1,
                       str(int(height)),
                       ha='center', va='bottom', rotation=0)
            
            ax.set_xlabel('Status')
            ax.set_ylabel('Count')
            ax.grid(axis='y', linestyle='--', alpha=0.7)
            
            # Rotate x-axis labels if many categories
            plt.setp(ax.get_xticklabels(), rotation=45, ha='right')
            
            fig.tight_layout()
            
            # Create the pane with the correct syntax
            status_chart = pn.pane.Matplotlib(fig, dpi=72)
            
            return status_chart
            
        except Exception as e:
            logger.error(f"Error creating status chart: {e}")
            # Return a placeholder if chart creation fails
            return pn.pane.Markdown("*Error creating chart*")

    def _create_document_type_chart(self, doc_stats):
        """Create a chart showing document type distribution"""
        try:
            # Extract type data
            type_data = doc_stats.get('type_distribution', {})
            
            # Convert to DataFrame for visualization
            type_df = pd.DataFrame({
                'Type': list(type_data.keys()),
                'Count': list(type_data.values())
            })
            
            # Sort by count
            type_df = type_df.sort_values('Count', ascending=False)
            
            # Create matplotlib figure
            fig = plt.figure(figsize=(8, 4))
            ax = fig.add_subplot(111)
            
            bars = ax.bar(type_df['Type'], type_df['Count'], color='forestgreen')
            
            # Add value labels on top of bars
            for bar in bars:
                height = bar.get_height()
                ax.text(bar.get_x() + bar.get_width()/2., height + 0.1,
                       str(int(height)),
                       ha='center', va='bottom', rotation=0)
            
            ax.set_xlabel('Document Type')
            ax.set_ylabel('Count')
            ax.grid(axis='y', linestyle='--', alpha=0.7)
            
            # Rotate x-axis labels if many categories
            plt.setp(ax.get_xticklabels(), rotation=45, ha='right')
            
            fig.tight_layout()
            
            # Create the pane with the correct syntax
            type_chart = pn.pane.Matplotlib(fig, dpi=72)
            
            return type_chart
            
        except Exception as e:
            logger.error(f"Error creating type chart: {e}")
            # Return a placeholder if chart creation fails
            return pn.pane.Markdown("*Error creating chart*")
    
    def _load_user_management(self, event=None):
        """Load the user management interface with improved error handling"""
        try:
            self.current_tab = 'users'
            self.notification_area.object = "Loading user data..."
            self.main_content.clear()
            
            # Update parent sidebar if available
            self.update_parent_sidebar()
            
            # Get user data
            users = get_users()
            
            # Ensure all users have required fields to prevent KeyError
            for user in users:
                # Ensure required fields exist (even if empty)
                required_fields = ['id', 'UID', 'Name', 'Mail', 'username', 'department', 'active']
                for field in required_fields:
                    if field not in user:
                        user[field] = ""
            
            # Fetch roles for each user directly from graph relationships
            for user in users:
                try:
                    # Create a DocUser instance to access roles
                    from CDocs.models.user_extensions import DocUser
                    from CDocs.db.db_operations import run_query
                    
                    # Get the user's UID to query relationships - safely handle missing fields
                    user_uid = user.get('id') or user.get('UID')
                    
                    if user_uid:
                        # Query Neo4j for role relationships
                        role_query = """
                        MATCH (u:User {UID: $uid})-[:HAS_ROLE]->(r:CDocs_Role)
                        RETURN r.name as role_name
                        """
                        role_results = run_query(role_query, {"uid": user_uid})
                        
                        # Extract role names from results
                        roles = [r.get('role_name', '') for r in role_results if r.get('role_name')]
                        
                        # Store roles in user data
                        user['roles'] = roles
                        user['roles_display'] = ", ".join(roles) if roles else "—"
                    else:
                        user['roles'] = []
                        user['roles_display'] = "—"
                except Exception as e:
                    logger.error(f"Error getting roles for user {user.get('id', 'unknown')}: {e}")
                    user['roles'] = []
                    user['roles_display'] = "—"
            
            # Create user management container
            user_mgmt = Column(
                Markdown("# User Management"),
                sizing_mode='stretch_width'
            )
            
            # Create user creation form
            create_form = self._create_user_form()
            
            # Create user table - wrap in try/except to catch DataFrame creation errors
            try:
                user_df = pd.DataFrame(users)
                
                # Standardize column names and ensure single source of truth for each attribute
                # This mapping preserves Neo4j naming conventions for field names
                field_mapping = {
                    'id': 'id',
                    'UID': 'id',
                    'username': 'username',
                    'Name': 'Name',     # Keep Neo4j naming convention
                    'Mail': 'Mail',     # Keep Neo4j naming convention
                    'email': 'Mail',    # Map email to Mail
                    'first_name': 'first_name',
                    'last_name': 'last_name',
                    #'Department': 'Department',
                    'department': 'department',  # Map department to Department
                    'role': 'roles_display',     # Consolidate all role columns
                    'roles': 'roles_display',    # Consolidate all role columns
                    'Roles': 'roles_display',    # Consolidate all role columns
                    'active': 'active'
                }
                
                # Standardize column names - handle safely in case columns don't exist
                for old_col, new_col in field_mapping.items():
                    if old_col in user_df.columns:
                        if new_col not in user_df.columns:
                            user_df[new_col] = user_df[old_col]
                        elif old_col != new_col:
                            # If the target column already exists, update it only for empty values
                            mask = user_df[new_col].isnull() | (user_df[new_col] == '')
                            user_df.loc[mask, new_col] = user_df.loc[mask, old_col]
                
                # Fill username from Mail or Name if missing - safely handle missing columns
                if 'username' in user_df.columns:
                    if 'Mail' in user_df.columns:
                        # For rows where username is empty but Mail exists
                        mail_mask = user_df['username'].isnull() & user_df['Mail'].notnull()
                        user_df.loc[mail_mask, 'username'] = user_df.loc[mail_mask, 'Mail']
                    
                    if 'Name' in user_df.columns:
                        # For rows where username is still empty but Name exists
                        name_mask = user_df['username'].isnull() & user_df['Name'].notnull()
                        user_df.loc[name_mask, 'username'] = user_df.loc[name_mask, 'Name']
                
                # Ensure we have a roles_display column and rename to 'Roles' for display
                if 'roles_display' in user_df.columns:
                    user_df = user_df.rename(columns={'roles_display': 'Roles'})
                
                # Define columns to display - only use columns that actually exist
                desired_columns = ['username', 'Name', 'Mail', 'department', 'Roles', 'active', 'id']
                available_columns = [col for col in desired_columns if col in user_df.columns]
                
                # Use only available columns
                if available_columns:
                    user_df = user_df[available_columns]
                
                # Create user table
                if not user_df.empty:
                    # Create user table WITHOUT the actions column first
                    user_table = Tabulator(
                        user_df,
                        pagination='local',
                        page_size=10,
                        sizing_mode='stretch_width',
                        height=400,
                        selectable=1,  # Allow single row selection
                        header_align='center',
                        hidden_columns=['id'],  # Hide id column but keep it for reference
                        show_index=False
                    )
                    
                    # Add edit and delete buttons below the table
                    edit_btn = Button(
                        name="Edit Selected",
                        button_type="primary",
                        width=120,
                        disabled=True  # Start disabled until a row is selected
                    )
                    
                    delete_btn = Button(
                        name="Delete Selected",
                        button_type="danger",
                        width=120,
                        disabled=True  # Start disabled until a row is selected
                    )
                    
                    # Enable buttons when a row is selected
                    def enable_buttons(event):
                        selected_rows = event.obj.selected_dataframe
                        edit_btn.disabled = len(selected_rows) == 0
                        delete_btn.disabled = len(selected_rows) == 0
                    
                    user_table.param.watch(enable_buttons, 'selection')
                    
                    # Set up button actions
                    def edit_selected(event):
                        selected_rows = user_table.selected_dataframe
                        if len(selected_rows) > 0 and 'id' in selected_rows.columns:
                            user_id = selected_rows.iloc[0]['id']
                            self._edit_user(user_id)
                    
                    def delete_selected(event):
                        selected_rows = user_table.selected_dataframe
                        if len(selected_rows) > 0 and 'id' in selected_rows.columns:
                            user_id = selected_rows.iloc[0]['id']
                            self._delete_user(user_id)
                    
                    edit_btn.on_click(edit_selected)
                    delete_btn.on_click(delete_selected)
                    
                    # Add instruction text and buttons
                    user_actions = Column(
                        Markdown("*Select a row to edit or delete a user*"),
                        Row(
                            edit_btn,
                            delete_btn,
                            align='end'
                        ),
                        align='end'
                    )
                
                else:
                    user_table = pn.pane.Markdown("No users found")
                    user_actions = pn.pane.Markdown("")
                
                # Add to user management
                user_section = Column(
                    Markdown("## User List"),
                    user_table,
                    user_actions,  # Add the actions below the table
                    styles={'background':'#f8f9fa'},
                    css_classes=['p-3', 'border', 'rounded'],
                    sizing_mode='stretch_width'
                )
                
                # Add form and table to main content
                user_mgmt.append(Row(
                    Column(
                        Markdown("## Create New User"),
                        create_form,
                        styles={'background':'#f8f9fa'},
                        css_classes=['p-3', 'border', 'rounded'],
                        width=400
                    ),
                    Column(width=20),  # spacing
                    user_section,
                    sizing_mode='stretch_width'
                ))
                
            except Exception as e:
                logger.error(f"Error creating user table: {e}")
                user_mgmt.append(Markdown(f"**Error creating user table:** {str(e)}"))
                user_mgmt.append(Markdown("User data appears to be missing required fields."))
            
            # Add user management to main content
            self.main_content.append(user_mgmt)
            
            # Clear notification
            self.notification_area.object = ""
            
        except Exception as e:
            logger.error(f"Error loading user management: {e}")
            self.notification_area.object = f"**Error:** {str(e)}"
            self.main_content.clear()
            self.main_content.append(Markdown("# User Management"))
            self.main_content.append(Markdown(f"*Error loading user data: {str(e)}*"))
    
    def _load_department_management(self, event=None):
        """Load the department management interface"""
        try:
            self.current_tab = 'departments'
            self.notification_area.object = "Loading department data..."
            self.main_content.clear()
            
            # Update parent sidebar if available
            self.update_parent_sidebar()
            
            # Get department data
            departments = get_departments()
            
            # Create department management container
            dept_mgmt = Column(
                Markdown("# Department Management"),
                Markdown("Departments are configured in settings and cannot be modified through the UI."),
                sizing_mode='stretch_width'
            )
            
            # Create department table
            dept_df = pd.DataFrame(departments)
            
            if not dept_df.empty:
                # Create department table
                dept_table = Tabulator(
                    dept_df,
                    pagination='local',
                    page_size=10,
                    sizing_mode='stretch_width',
                    height=400,
                    selectable=False,  # Read-only table
                    header_align='center'
                )
                
                dept_section = Column(
                    Markdown("## Departments"),
                    dept_table,
                    styles={'background':'#f8f9fa'},
                    css_classes=['p-3', 'border', 'rounded'],
                    sizing_mode='stretch_width'
                )
            else:
                dept_section = Column(
                    Markdown("## Departments"),
                    Markdown("No departments configured in settings."),
                    styles={'background':'#f8f9fa'},
                    css_classes=['p-3', 'border', 'rounded'],
                    sizing_mode='stretch_width'
                )
            
            # Add to main content
            dept_mgmt.append(dept_section)
            self.main_content.append(dept_mgmt)
            
            # Clear notification
            self.notification_area.object = ""
            
        except Exception as e:
            logger.error(f"Error loading department management: {e}")
            self.notification_area.object = f"**Error:** {str(e)}"
            self.main_content.clear()
            self.main_content.append(Markdown("# Department Management"))
            self.main_content.append(Markdown(f"*Error loading department data: {str(e)}*"))

    def _load_document_type_management(self, event=None):
        """Load the document type management interface"""
        try:
            self.current_tab = 'doc_types'
            self.notification_area.object = "Loading document type data..."
            self.main_content.clear()
            
            # Update parent sidebar if available
            self.update_parent_sidebar()
            
            # Get document type data
            #doc_types = get_document_types()
            
            # Create document type management container
            doc_type_mgmt = Column(
                Markdown("# Document Type Management"),
                Markdown("Document types are configured in settings and cannot be modified through the UI."),
                sizing_mode='stretch_width'
            )
            
            # Create document type table
            # doc_type_df = pd.DataFrame(doc_types)
            
            # if not doc_type_df.empty:
            #     # Create document type table
            #     doc_type_table = Tabulator(
            #         doc_type_df,
            #         pagination='local',
            #         page_size=10,
            #         sizing_mode='stretch_width',
            #         height=400,
            #         selectable=False,  # Read-only table
            #         header_align='center'
            #     )
                
            #     doc_type_section = Column(
            #         Markdown("## Document Types"),
            #         doc_type_table,
            #         styles={'background':'#f8f9fa'},
            #         css_classes=['p-3', 'border', 'rounded'],
            #         sizing_mode='stretch_width'
            #     )
            # else:
            #     doc_type_section = Column(
            #         Markdown("## Document Types"),
            #         Markdown("No document types configured in settings."),
            #         styles={'background':'#f8f9fa'},
            #         css_classes=['p-3', 'border', 'rounded'],
            #         sizing_mode='stretch_width'
            #     )
            
            # # Add to main content
            # doc_type_mgmt.append(doc_type_section)
            self.main_content.append(doc_type_mgmt)
            
            # Clear notification
            self.notification_area.object = ""
            
        except Exception as e:
            logger.error(f"Error loading document type management: {e}")
            self.notification_area.object = f"**Error:** {str(e)}"
            self.main_content.clear()
            self.main_content.append(Markdown("# Document Type Management"))
            self.main_content.append(Markdown(f"*Error loading document type data: {str(e)}*"))
    
    def _load_system_settings(self, event=None):
        """Load the system settings interface"""
        self.current_tab = 'settings'
        self.notification_area.object = "System settings interface not yet implemented"
        self.main_content.clear()
        
        # Update parent sidebar if available
        self.update_parent_sidebar()
        
        self.main_content.append(Markdown("# System Settings"))
        self.main_content.append(Markdown("*This feature is coming soon*"))
    
    def _load_backup_restore(self, event=None):
        """Load the backup and restore interface"""
        self.current_tab = 'backup'
        self.notification_area.object = "Backup and restore interface not yet implemented"
        self.main_content.clear()
        
        # Update parent sidebar if available
        self.update_parent_sidebar()
        
        self.main_content.append(Markdown("# Backup & Restore"))
        self.main_content.append(Markdown("*This feature is coming soon*"))

    def set_user(self, user):
        """Set the current user - needed for compatibility with main app"""
        try:
            self.user = user
            
            # Update parent sidebar if available (do this first to ensure admin button appears)
            self.update_parent_sidebar()
            
            # Verify admin access with the new user
            if not self._verify_admin_access():
                self.notification_area.object = "**Error:** You do not have permission to access the admin panel."
                return False
                
            # Update the UI with the new user
            try:
                # Create status_area if it doesn't exist
                if not hasattr(self, 'status_area'):
                    self.status_area = pn.Column(sizing_mode='stretch_width')
                    
                # Update system status in sidebar
                self._update_system_status()
                
                # Refresh the current view based on the active tab
                self._refresh_current_view()
                return True
            except Exception as e:
                logger.error(f"Error in set_user: {str(e)}")
                return False
        except Exception as e:
            logger.error(f"Error in set_user: {str(e)}")
            return False

    def get_admin_view(self):
        """Return the admin panel view for embedding in other panels"""
        container = pn.Column(
            self.notification_area,
            self.main_content,
            sizing_mode='stretch_width'
        )
        return container

    # Add this method to the AdminPanel class to update the parent app's sidebar
    def update_parent_sidebar(self):
        """Update the parent app's sidebar if available"""
        try:
            if hasattr(self, 'parent_app') and self.parent_app is not None:
                # Instead of calling parent_app._update_sidebar_buttons()
                # Use the specific admin sidebar setup method
                if hasattr(self.parent_app, '_setup_admin_sidebar'):
                    self.parent_app._setup_admin_sidebar()
                    return True
                elif hasattr(self.parent_app, '_update_sidebar_buttons'):
                    # Fallback to the general update method
                    self.parent_app._update_sidebar_buttons()
                    return True
            return False
        except Exception as e:
            logger.error(f"Error updating parent sidebar: {e}")
            return False

    def _edit_user(self, user_id):
        """Load user data for editing and show edit form"""
        try:
            # Get user data
            users = get_users(user_id=user_id)
            if not users:
                self.notification_area.object = f"**Error:** User not found"
                return
                
            user = users[0]
            logger.debug(f"Loaded user data for editing: {user}")
            
            # Make sure ID is available for the save function
            user['id'] = user.get('id', user.get('UID', user_id))
            
            # Update notification
            self.notification_area.object = f"Editing user: {user.get('username', user.get('Name', 'Unknown'))}"
            
            # Get user roles directly from Neo4j relationships
            try:
                from CDocs.db.db_operations import run_query
                
                # Query for user roles
                role_query = """
                MATCH (u:User {UID: $uid})-[:HAS_ROLE]->(r:CDocs_Role)
                RETURN r.name as role_name
                """
                role_results = run_query(role_query, {"uid": user_id})
                
                # Extract role names from results
                roles = [r.get('role_name', '') for r in role_results if r.get('role_name')]
                user['roles'] = roles
                logger.debug(f"Retrieved roles for user: {roles}")
            except Exception as e:
                logger.error(f"Error getting user roles from Neo4j: {e}")
                # If direct query fails, try DocUser model as fallback
                try:
                    from CDocs.models.user_extensions import DocUser
                    doc_user = DocUser(uid=user_id)
                    user['roles'] = doc_user.roles
                    logger.debug(f"Retrieved roles from DocUser: {user['roles']}")
                except Exception as nested_e:
                    logger.error(f"Error getting user roles via DocUser: {nested_e}")
                    user['roles'] = []
            
            # Debug what data we have before passing to form
            logger.debug(f"User data before form creation: {user}")
            
            # Ensure we have the Neo4j field mappings for the form
            if 'Mail' in user and not user.get('email'):
                user['email'] = user['Mail']
                
            # Extract first and last names from Name if not already available
            if 'Name' in user and (not user.get('first_name') or not user.get('last_name')):
                name_parts = user['Name'].split(' ', 1)
                if len(name_parts) > 0:
                    user['first_name'] = name_parts[0]
                if len(name_parts) > 1:
                    user['last_name'] = name_parts[1]
            
            # Create edit form with user data
            edit_form = self._create_user_form(user_data=user)
            
            # Create edit container
            edit_container = Column(
                Markdown(f"## Edit User: {user.get('username', user.get('Name', 'Unknown'))}"),
                edit_form,
                styles={'background':'#f9f9f9'},
                css_classes=['p-3', 'border', 'rounded'],
                width=500
            )
            
            # Replace main content with edit form
            self.main_content.clear()
            self.main_content.append(edit_container)
            
        except Exception as e:
            logger.error(f"Error editing user: {e}")
            logger.error(f"Stack trace: {traceback.format_exc()}")
            self.notification_area.object = f"**Error:** {str(e)}"

    def _create_user_form(self, user_data=None):
        """Create a form for user creation/editing with improved data mapping"""
        # Log what data we received
        if user_data:
            logger.debug(f"Creating form with user data: {user_data}")
        
        # Get data for dropdowns
        departments = settings.DEPARTMENTS
        
        # Create department options dict - format properly for Panel 1.6.1
        # Convert from list of tuples to dictionary {label: value}
        department_options = {name: code for name, code in departments.items()}
        
        # Extract name and email from Neo4j attributes if needed
        if user_data:
            # Map Mail to email if email is not present
            if not user_data.get('email') and user_data.get('Mail'):
                user_data['email'] = user_data['Mail']
                
            # Handle first/last name from the Name field if needed
            if not user_data.get('first_name') and not user_data.get('last_name') and user_data.get('Name'):
                name_parts = user_data['Name'].split(' ', 1)
                if len(name_parts) > 0:
                    user_data['first_name'] = name_parts[0]
                if len(name_parts) > 1:
                    user_data['last_name'] = name_parts[1]
        
        # Get all available roles from settings, or use default if not defined
        try:
            all_roles = getattr(settings, 'USER_ROLES', [
                'ADMIN', 'MANAGER', 'EDITOR', 'REVIEWER', 'APPROVER', 
                'QA', 'EXECUTIVE', 'USER', 'GUEST'
            ])
        except Exception as e:
            logger.warning(f"Could not get USER_ROLES from settings: {e}")
            all_roles = [
                'ADMIN', 'MANAGER', 'EDITOR', 'REVIEWER', 'APPROVER', 
                'QA', 'EXECUTIVE', 'USER', 'GUEST'
            ]
        
        # Format the roles for the checkbox group - use the roles as both label and value
        # In Panel 1.6.1, CheckBoxGroup expects list of values (not tuples or dicts)
        role_options = all_roles
        
        # Create roles checkbox group - for multiple role selection
        user_roles = user_data.get('roles', []) if user_data else []
        
        # Ensure roles are strings for comparison
        user_roles = [str(role) for role in user_roles if role]
        
        # Create CheckBoxGroup with proper value selection
        roles_group = CheckBoxGroup(
            name="Roles",
            options=role_options,  # Use simple list for CheckBoxGroup
            value=user_roles,
            inline=False
        )
        
        # Create form inputs with existing values if editing
        username_input = TextInput(
            name="Username", 
            placeholder="Enter username",
            value=user_data.get('username', '') if user_data else ''
        )
        
        # Use PasswordInput instead of TextInput with password=True
        password_input = pn.widgets.PasswordInput(
            name="Password", 
            placeholder="Leave blank to keep current password" if user_data else "Enter password",
        )
        
        # Handle both Mail and email fields
        email_input = TextInput(
            name="Email", 
            placeholder="Enter email",
            value=user_data.get('email', user_data.get('Mail', '')) if user_data else ''
        )
        
        first_name = TextInput(
            name="First Name", 
            placeholder="Enter first name",
            value=user_data.get('first_name', '') if user_data else ''
        )
        
        last_name = TextInput(
            name="Last Name", 
            placeholder="Enter last name",
            value=user_data.get('last_name', '') if user_data else ''
        )
        
        # Create department select with current value if editing
        # Handle both Department and department fields - use dict format for options
       # For editing an existing user, we need to set the value to the department code
        # that's already stored in the database (Department field in Neo4j)
        department_code = user_data.get('department', '') if user_data else ''

        department_select = Select(
            name="Department",
            options=department_options,  # Dictionary format {label: value}
            value=department_code  # This should be the code (e.g., 'HR'), not the name
        )
        
        # Create active switch with current value if editing
        active_switch = Switch(
            name="Active", 
            value=user_data.get('active', True) if user_data else True
        )
        
        # Create buttons
        save_btn = Button(
            name="Update User" if user_data else "Create User", 
            button_type="success"
        )
        
        cancel_btn = Button(
            name="Cancel", 
            button_type="default"
        )
        
        # Create form layout
        user_form = Column(
            username_input,
            password_input,
            email_input,
            first_name,
            last_name,
            Column(
                Markdown("### User Roles"),
                Markdown("Select one or more roles for this user:"),
                roles_group,
                styles={'background':'#f5f5f5'},
                css_classes=['p-2', 'border', 'rounded']
            ),
            department_select,
            Row(Markdown("**Active:**"), active_switch),
            Row(cancel_btn, save_btn, align='end')
        )
        
        # Set up event handlers for buttons
        user_id = user_data.get('id', None) if user_data else None
        
        save_btn.on_click(lambda event: self._save_user(
            user_id,  # may be None for new user
            username_input.value,
            password_input.value,
            email_input.value,
            first_name.value,
            last_name.value,
            roles_group.value,  # Now passing selected roles as a list
            department_select.value,
            active_switch.value
        ))
        
        cancel_btn.on_click(lambda event: self._load_user_management())
        
        return user_form

    def _save_user(self, user_id, username, password, email, first_name, last_name, roles, department, active):
        """Save user to database with proper role relationships"""
        try:
            # Create full name from first and last name
            full_name = f"{first_name} {last_name}".strip()
            
            # Prepare user data with correct Neo4j attribute schema
            user_data = {
                'username': username,
                'Name': full_name,  # Primary name field in Neo4j
                'Mail': email,      # Primary email field in Neo4j
                'first_name': first_name, 
                'last_name': last_name,
                'department': department,
                'active': active,
                'roles': roles
            }
            
            # Add password only if provided (for updates)
            if password:
                user_data['password'] = password
            
            # Create or update user
            if user_id:
                # Update existing user
                update_user(user_id, user_data)
                self.notification_area.object = f"User {username} updated successfully"
            else:
                # For new users, use a different approach to ensure proper relationship
                from CDocs.db.db_operations import run_query
                import uuid
                
                # First find the People node
                people_result = run_query("MATCH (p:People) RETURN p.UID as uid LIMIT 1")
                if not people_result or 'uid' not in people_result[0]:
                    # Create People node if it doesn't exist
                    people_uid = str(uuid.uuid4())
                    run_query(
                        """
                        CREATE (p:People {UID: $uid, Name: 'People'})
                        RETURN p.UID as uid
                        """,
                        {"uid": people_uid}
                    )
                else:
                    people_uid = people_result[0]['uid']
                
                # Create user with proper properties and relationship to People node
                new_user_uid = str(uuid.uuid4())
                user_props = {
                    'UID': new_user_uid,
                    'Name': full_name,
                    'Mail': email,
                    'username': username,
                    'department': department,
                    'active': active,
                    'createdDate': datetime.now().isoformat()
                }
                
                # Add password if provided (hash it for security)
                if password:
                    import hashlib
                    user_props['password'] = hashlib.sha256(password.encode()).hexdigest()
                
                # Create user node and relationship to People node
                run_query(
                    """
                    MATCH (p:People {UID: $people_uid})
                    CREATE (p)-[:DIRECTORY]->(u:User)
                    SET u = $props
                    RETURN u.UID as uid
                    """,
                    {"people_uid": people_uid, "props": user_props}
                )
                
                # Set up CDocs user extension (adds CDocs_User label and default VIEWER role)
                from CDocs.models.user_extensions import DocUser
                DocUser._setup_user_extension(new_user_uid)
                
                # Add roles if specified
                if roles:
                    doc_user = DocUser(uid=new_user_uid)
                    
                    # Get available roles from settings, or use a default set if not defined
                    try:
                        from CDocs.config import settings
                        available_roles = getattr(settings, 'USER_ROLES', [
                            'ADMIN', 'MANAGER', 'EDITOR', 'REVIEWER', 'APPROVER', 
                            'QA', 'EXECUTIVE', 'USER', 'GUEST'
                        ])
                    except Exception as e:
                        logger.warning(f"Could not get USER_ROLES from settings: {e}")
                        available_roles = [
                            'ADMIN', 'MANAGER', 'EDITOR', 'REVIEWER', 'APPROVER', 
                            'QA', 'EXECUTIVE', 'USER', 'GUEST'
                        ]
                    
                    for role in roles:
                        if role in available_roles:
                            try:
                                doc_user.add_role(role)
                            except Exception as role_err:
                                logger.error(f"Error adding role {role} to user: {role_err}")
                
                self.notification_area.object = f"User {username} created successfully"
            
            # Refresh the view
            self._load_user_management()
            
        except Exception as e:
            logger.error(f"Error saving user: {e}")
            logger.error(f"Stack trace: {traceback.format_exc()}")
            self.notification_area.object = f"**Error:** {str(e)}"

    def _load_notification_management(self, event=None):
        """Load notification management interface."""
        try:
            self.current_tab = 'notifications'
            self.notification_area.object = "Loading notification data..."
            self.main_content.clear()
            
            # Update parent sidebar if available
            self.update_parent_sidebar()
            
            # Get notification statistics
            notification_stats = self._get_notification_stats()
            
            # Create notification management interface
            stats_cards = pn.Row(
                self._create_metric_card("Total Notifications", notification_stats.get('total', 0)),
                self._create_metric_card("Unread", notification_stats.get('unread', 0)),
                self._create_metric_card("High Priority", notification_stats.get('high_priority', 0)),
                self._create_metric_card("Last 24 Hours", notification_stats.get('recent', 0)),
                sizing_mode='stretch_width'
            )
            
            # Create bulk action controls
            mark_all_read_btn = pn.widgets.Button(name="Mark All Read", button_type="primary", width=120)
            delete_old_btn = pn.widgets.Button(name="Delete Old (30+ days)", button_type="default", width=150)
            send_test_btn = pn.widgets.Button(name="Send Test Notification", button_type="default", width=150)
            
            # Set up button handlers
            mark_all_read_btn.on_click(self._mark_all_notifications_read)
            delete_old_btn.on_click(self._delete_old_notifications)
            send_test_btn.on_click(self._send_test_notification)
            
            bulk_controls = pn.Row(
                mark_all_read_btn,
                delete_old_btn,
                send_test_btn,
                align='start'
            )
            
            # Get recent notifications for display
            recent_notifications = self._get_recent_notifications()
            
            # Create notifications table
            if recent_notifications:
                notifications_df = pd.DataFrame(recent_notifications)
                
                # Format columns for display
                display_columns = ['created_date', 'user_name', 'notification_type', 'message', 'priority', 'read']
                available_columns = [col for col in display_columns if col in notifications_df.columns]
                
                if available_columns:
                    notifications_df = notifications_df[available_columns]
                    
                    # Rename columns for better display
                    column_names = {
                        'created_date': 'Date',
                        'user_name': 'User',
                        'notification_type': 'Type',
                        'message': 'Message',
                        'priority': 'Priority',
                        'read': 'Read'
                    }
                    notifications_df = notifications_df.rename(columns=column_names)
                    
                    # Format date column
                    if 'Date' in notifications_df.columns:
                        notifications_df['Date'] = pd.to_datetime(notifications_df['Date']).dt.strftime('%Y-%m-%d %H:%M')
                    
                    notifications_table = Tabulator(
                        notifications_df,
                        pagination='local',
                        page_size=15,
                        sizing_mode='stretch_width',
                        height=400,
                        show_index=False
                    )
                else:
                    notifications_table = pn.pane.Markdown("*No notification data available*")
            else:
                notifications_table = pn.pane.Markdown("*No recent notifications found*")
            
            # Create the complete notification management layout
            notification_management = pn.Column(
                pn.pane.Markdown("# Notification Management"),
                pn.pane.Markdown("Monitor and manage system notifications."),
                pn.Spacer(height=10),
                stats_cards,
                pn.Spacer(height=20),
                pn.pane.Markdown("## Actions"),
                bulk_controls,
                pn.Spacer(height=20),
                pn.pane.Markdown("## Recent Notifications"),
                notifications_table,
                sizing_mode='stretch_width'
            )
            
            self.main_content.append(notification_management)
            
            # Clear notification
            self.notification_area.object = ""
            
        except Exception as e:
            logger.error(f"Error loading notification management: {e}")
            self.notification_area.object = f"**Error:** {str(e)}"

    def _get_notification_stats(self) -> Dict[str, int]:
        """Get notification statistics."""
        try:
            notification_manager = NotificationManager()
            
            # Get total notifications
            from CDocs.db.db_operations import run_query
            
            # Count total notifications
            total_result = run_query("MATCH (n:CDocs_Notification) RETURN count(n) as total")
            total = total_result[0]['total'] if total_result else 0
            
            # Count unread notifications
            unread_result = run_query("MATCH (n:CDocs_Notification {read: false}) RETURN count(n) as unread")
            unread = unread_result[0]['unread'] if unread_result else 0
            
            # Count high priority notifications
            high_priority_result = run_query("MATCH (n:CDocs_Notification {priority: 'HIGH'}) RETURN count(n) as high_priority")
            high_priority = high_priority_result[0]['high_priority'] if high_priority_result else 0
            
            # Count recent notifications (last 24 hours)
            from datetime import datetime, timedelta
            yesterday = (datetime.now() - timedelta(days=1)).isoformat()
            recent_result = run_query(
                "MATCH (n:CDocs_Notification) WHERE n.created_date > $since RETURN count(n) as recent",
                {"since": yesterday}
            )
            recent = recent_result[0]['recent'] if recent_result else 0
            
            return {
                'total': total,
                'unread': unread,
                'high_priority': high_priority,
                'recent': recent
            }
            
        except Exception as e:
            logger.error(f"Error getting notification stats: {e}")
            return {'total': 0, 'unread': 0, 'high_priority': 0, 'recent': 0}

    def _get_recent_notifications(self, limit=50) -> List[Dict[str, Any]]:
        """Get recent notifications for display."""
        try:
            from CDocs.db.db_operations import run_query
            
            query = """
            MATCH (n:CDocs_Notification)
            OPTIONAL MATCH (n)-[:NOTIFIES]->(u:User)
            RETURN n.uid as uid, n.notification_type as notification_type, 
                   n.message as message, n.priority as priority, n.read as read,
                   n.created_date as created_date, u.username as user_name
            ORDER BY n.created_date DESC
            LIMIT $limit
            """
            
            results = run_query(query, {"limit": limit})
            return results
            
        except Exception as e:
            logger.error(f"Error getting recent notifications: {e}")
            return []

    def _mark_all_notifications_read(self, event=None):
        """Mark all notifications as read."""
        try:
            from CDocs.db.db_operations import run_query
            
            result = run_query("MATCH (n:CDocs_Notification {read: false}) SET n.read = true RETURN count(n) as updated")
            updated_count = result[0]['updated'] if result else 0
            
            self.notification_area.object = f"Marked {updated_count} notifications as read"
            
            # Refresh the view
            self._load_notification_management()
            
        except Exception as e:
            logger.error(f"Error marking notifications as read: {e}")
            self.notification_area.object = f"**Error:** {str(e)}"

    def _delete_old_notifications(self, event=None):
        """Delete notifications older than 30 days."""
        try:
            from CDocs.db.db_operations import run_query
            from datetime import datetime, timedelta
            
            thirty_days_ago = (datetime.now() - timedelta(days=30)).isoformat()
            
            result = run_query(
                "MATCH (n:CDocs_Notification) WHERE n.created_date < $cutoff DELETE n RETURN count(n) as deleted",
                {"cutoff": thirty_days_ago}
            )
            deleted_count = result[0]['deleted'] if result else 0
            
            self.notification_area.object = f"Deleted {deleted_count} old notifications"
            
            # Refresh the view
            self._load_notification_management()
            
        except Exception as e:
            logger.error(f"Error deleting old notifications: {e}")
            self.notification_area.object = f"**Error:** {str(e)}"

    def _send_test_notification(self, event=None):
        """Send a test notification to the current user."""
        try:
            if not self.user or not hasattr(self.user, 'uid'):
                self.notification_area.object = "**Error:** No user available for test notification"
                return
                
            notification_manager = NotificationManager()
            
            # Fix: Remove priority parameter and pass it in details instead
            notification_manager.create_notification(
                notification_type='SYSTEM_NOTIFICATION',
                user_uid=self.user.uid,
                message=f"Test notification sent at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
                details={
                    'test': True, 
                    'sent_by': 'admin_panel',
                    'priority': 'INFO'  # Move priority to details
                }
            )
            
            self.notification_area.object = "Test notification sent successfully"
            
            # Refresh the view
            self._load_notification_management()
            
        except Exception as e:
            logger.error(f"Error sending test notification: {e}")
            self.notification_area.object = f"**Error:** {str(e)}"

Parameters

Name Type Default Kind
bases param.Parameterized -

Parameter Details

bases: Parameter of type param.Parameterized

Return Value

Returns unspecified type

Class Interface

Methods

__init__(self, template, session_manager, parent_app, embedded)

Purpose: Internal method: init

Parameters:

  • template: Parameter
  • session_manager: Parameter
  • parent_app: Parameter
  • embedded: Parameter

Returns: None

_get_current_user(self)

Purpose: Get current user from session

Returns: None

_verify_admin_access(self) -> bool

Purpose: Verify that the user has admin permissions

Returns: Returns bool

_setup_header(self)

Purpose: Set up the header with title and actions

Returns: None

_setup_sidebar(self)

Purpose: Set up the sidebar with navigation options

Returns: None

_setup_main_area(self)

Purpose: Set up the main content area.

Returns: None

_refresh_current_view(self, event)

Purpose: Refresh the current view

Parameters:

  • event: Parameter

Returns: None

_navigate_back(self, event)

Purpose: Navigate back to dashboard

Parameters:

  • event: Parameter

Returns: None

_update_system_status(self)

Purpose: Update system status information in the sidebar.

Returns: None

_format_duration(self, seconds)

Purpose: Format duration in seconds to readable string

Parameters:

  • seconds: Parameter

Returns: None

_load_admin_dashboard(self, event)

Purpose: Load the admin dashboard view

Parameters:

  • event: Parameter

Returns: None

_create_metric_card(self, title, value, total, percent)

Purpose: Create a metric card for dashboard

Parameters:

  • title: Parameter
  • value: Parameter
  • total: Parameter
  • percent: Parameter

Returns: None

_create_document_status_chart(self, doc_stats)

Purpose: Create a chart showing document status distribution

Parameters:

  • doc_stats: Parameter

Returns: None

_create_document_type_chart(self, doc_stats)

Purpose: Create a chart showing document type distribution

Parameters:

  • doc_stats: Parameter

Returns: None

_load_user_management(self, event)

Purpose: Load the user management interface with improved error handling

Parameters:

  • event: Parameter

Returns: None

_load_department_management(self, event)

Purpose: Load the department management interface

Parameters:

  • event: Parameter

Returns: None

_load_document_type_management(self, event)

Purpose: Load the document type management interface

Parameters:

  • event: Parameter

Returns: None

_load_system_settings(self, event)

Purpose: Load the system settings interface

Parameters:

  • event: Parameter

Returns: None

_load_backup_restore(self, event)

Purpose: Load the backup and restore interface

Parameters:

  • event: Parameter

Returns: None

set_user(self, user)

Purpose: Set the current user - needed for compatibility with main app

Parameters:

  • user: Parameter

Returns: None

get_admin_view(self)

Purpose: Return the admin panel view for embedding in other panels

Returns: See docstring for return details

update_parent_sidebar(self)

Purpose: Update the parent app's sidebar if available

Returns: None

_edit_user(self, user_id)

Purpose: Load user data for editing and show edit form

Parameters:

  • user_id: Parameter

Returns: None

_create_user_form(self, user_data)

Purpose: Create a form for user creation/editing with improved data mapping

Parameters:

  • user_data: Parameter

Returns: None

_save_user(self, user_id, username, password, email, first_name, last_name, roles, department, active)

Purpose: Save user to database with proper role relationships

Parameters:

  • user_id: Parameter
  • username: Parameter
  • password: Parameter
  • email: Parameter
  • first_name: Parameter
  • last_name: Parameter
  • roles: Parameter
  • department: Parameter
  • active: Parameter

Returns: None

_load_notification_management(self, event)

Purpose: Load notification management interface.

Parameters:

  • event: Parameter

Returns: None

_get_notification_stats(self) -> Dict[str, int]

Purpose: Get notification statistics.

Returns: Returns Dict[str, int]

_get_recent_notifications(self, limit) -> List[Dict[str, Any]]

Purpose: Get recent notifications for display.

Parameters:

  • limit: Parameter

Returns: Returns List[Dict[str, Any]]

_mark_all_notifications_read(self, event)

Purpose: Mark all notifications as read.

Parameters:

  • event: Parameter

Returns: None

_delete_old_notifications(self, event)

Purpose: Delete notifications older than 30 days.

Parameters:

  • event: Parameter

Returns: None

_send_test_notification(self, event)

Purpose: Send a test notification to the current user.

Parameters:

  • event: Parameter

Returns: None

Required Imports

import logging
import traceback
from typing import Dict
from typing import List
from typing import Any

Usage Example

# Example usage:
# result = AdminPanel(bases)

Similar Components

AI-powered semantic similarity - components with related functionality:

  • class ApprovalPanel 70.3% similar

    Approval management interface component

    From: /tf/active/vicechatdev/CDocs/ui/approval_panel_bis.py
  • class ApprovalPanel_v1 68.5% similar

    Approval management interface component

    From: /tf/active/vicechatdev/CDocs/ui/approval_panel.py
  • class ReviewPanel 63.3% similar

    Review management interface component

    From: /tf/active/vicechatdev/CDocs/ui/review_panel.py
  • function create_admin_panel 55.7% similar

    Factory function that creates and returns a configured AdminPanel instance with optional template, session management, parent application reference, and embedding mode.

    From: /tf/active/vicechatdev/CDocs/ui/admin_panel.py
  • function create_embedded_admin_panel 52.3% similar

    Creates and returns an embedded AdminPanel instance configured for integration within other application interfaces without its own template or session manager.

    From: /tf/active/vicechatdev/CDocs/ui/admin_panel.py
← Back to Browse