feat: Add comprehensive admin user management system
- Add user status toggle (activate/deactivate) functionality - Add user deletion with post/comment transfer to admin - Implement safety checks for admin protection - Add interactive JavaScript for user management actions - Update admin users interface with action buttons - Add confirmation dialogs for destructive operations - Update README with new admin features and capabilities - Add database migration utility for future updates Features: - Toggle user active/inactive status - Delete users with content preservation - Transfer all posts/comments to admin on user deletion - Prevent admin self-modification and deletion - AJAX-powered interface with real-time feedback - Comprehensive error handling and user notifications
This commit is contained in:
322
README.md
322
README.md
@@ -1,54 +1,118 @@
|
||||
# Motorcycle Adventure Community
|
||||
# Motorcycle Adventure Community 🏍️
|
||||
|
||||
A Flask-based web application for motorcycle enthusiasts featuring adventure stories, route sharing, and accommodation recommendations.
|
||||
A modern Flask-based web application for motorcycle enthusiasts featuring adventure stories, interactive GPS route sharing, and community engagement.
|
||||
|
||||
## Features
|
||||
## ✨ Features
|
||||
|
||||
- **Landing Page**: Hero section with call-to-action, featured adventures, and accommodation promotion
|
||||
- **User Authentication**: Registration and login system with Flask-Login
|
||||
- **Community Section**: Share stories, tracks, and experiences
|
||||
- **Admin Panel**: Content management for administrators
|
||||
- **Responsive Design**: Built with Tailwind CSS for mobile-first design
|
||||
- **Error Handling**: Custom 404 and 500 error pages
|
||||
### 🎨 Modern UI/UX Design
|
||||
- **Tailwind CSS Integration**: Complete redesign with modern gradient backgrounds and glass morphism effects
|
||||
- **Responsive Design**: Mobile-first approach with optimized layouts for all screen sizes
|
||||
- **Interactive Components**: Smooth animations, hover effects, and modern card-based layouts
|
||||
- **Consistent Design Language**: Unified color scheme and component patterns throughout the app
|
||||
|
||||
## Technology Stack
|
||||
### 🗺️ Advanced GPS & Route Features
|
||||
- **Interactive Maps**: Leaflet.js integration with OpenStreetMap tiles
|
||||
- **GPX File Support**: Upload, download, and visualize GPS tracks
|
||||
- **Route Statistics**: Distance, elevation gain, max elevation, and track points
|
||||
- **Start/End Markers**: Visual indicators with custom icons for route endpoints
|
||||
- **Map Fitting**: Automatic zoom and centering to display complete routes
|
||||
|
||||
- **Backend**: Python Flask 3.0.0
|
||||
- **Database**: SQLAlchemy with PostgreSQL support
|
||||
- **Authentication**: Flask-Login
|
||||
- **Email**: Flask-Mail
|
||||
- **Forms**: WTForms with validation
|
||||
- **Styling**: Tailwind CSS (CDN)
|
||||
- **Deployment**: Docker with Gunicorn
|
||||
### 📸 Enhanced Media Management
|
||||
- **Photo Gallery**: Grid-based layout with modal view and zoom functionality
|
||||
- **Image Thumbnails**: Automatic thumbnail generation for optimized loading
|
||||
- **Cover Image System**: Designated cover photos for adventure posts
|
||||
- **Image Descriptions**: Support for photo captions and descriptions
|
||||
- **Hover Effects**: Interactive image previews with smooth transitions
|
||||
|
||||
## Project Structure
|
||||
### 👥 Community & Social Features
|
||||
- **Adventure Posts**: Rich content creation with titles, subtitles, and detailed stories
|
||||
- **Comment System**: Community discussions on adventure posts
|
||||
- **Like System**: Engagement tracking with real-time updates
|
||||
- **User Profiles**: Personal dashboards with adventure statistics
|
||||
- **Difficulty Ratings**: 5-star system for adventure difficulty assessment
|
||||
- **Publication Workflow**: Admin approval system for content moderation
|
||||
|
||||
### 🔐 User Management & Authentication
|
||||
- **Secure Authentication**: Flask-Login with session management
|
||||
- **User Profiles**: Personal information and adventure history
|
||||
- **Role-Based Access**: User and admin permissions
|
||||
- **Adventure Statistics**: Published vs pending post counts
|
||||
- **Registration System**: Email-based user registration
|
||||
|
||||
### 🛠️ Admin Panel & Analytics
|
||||
- **Comprehensive Dashboard**: User and post management interface
|
||||
- **Content Moderation**: Review and approve community posts
|
||||
- **User Analytics**: User engagement and activity metrics
|
||||
- **Post Management**: Bulk operations and detailed post information
|
||||
- **System Configuration**: Admin-only settings and controls
|
||||
|
||||
### 📱 Mobile-Optimized Experience
|
||||
- **Touch-Friendly Interface**: Optimized buttons and interactions for mobile devices
|
||||
- **Responsive Grids**: Adaptive layouts that work perfectly on phones and tablets
|
||||
- **Progressive Enhancement**: Graceful degradation for older browsers
|
||||
- **Fast Loading**: Optimized images and efficient CSS delivery
|
||||
|
||||
## 🚀 Technology Stack
|
||||
|
||||
- **Backend**: Python Flask 3.0.0 with Blueprint architecture
|
||||
- **Database**: SQLAlchemy with PostgreSQL/SQLite support
|
||||
- **Authentication**: Flask-Login with secure session management
|
||||
- **Email**: Flask-Mail for notifications and verification
|
||||
- **Forms**: WTForms with comprehensive validation
|
||||
- **Frontend**: Tailwind CSS 3.x with custom components
|
||||
- **Maps**: Leaflet.js with OpenStreetMap integration
|
||||
- **File Handling**: Secure media uploads with thumbnail generation
|
||||
- **Deployment**: Docker with Gunicorn WSGI server
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
```
|
||||
/
|
||||
├── app/
|
||||
│ ├── __init__.py # Flask app factory
|
||||
│ ├── models.py # Database models
|
||||
│ ├── forms.py # WTForm definitions
|
||||
│ ├── routes/ # Route blueprints
|
||||
│ │ ├── main.py # Main routes (index, about)
|
||||
│ │ ├── auth.py # Authentication routes
|
||||
│ │ └── community.py # Community features
|
||||
│ ├── templates/ # Jinja2 templates
|
||||
│ │ ├── base.html # Base template
|
||||
│ │ ├── index.html # Landing page
|
||||
│ │ ├── auth/ # Auth templates
|
||||
│ │ └── errors/ # Error pages
|
||||
│ └── static/ # Static assets
|
||||
│ ├── __init__.py # Flask app factory with extensions
|
||||
│ ├── models.py # Database models (User, Post, Image, GPX, Comment, Like)
|
||||
│ ├── forms.py # WTForm definitions for all features
|
||||
│ ├── extensions.py # Flask extensions configuration
|
||||
│ ├── media_config.py # Media handling and upload configuration
|
||||
│ ├── routes/ # Route blueprints
|
||||
│ │ ├── main.py # Landing page and general routes
|
||||
│ │ ├── auth.py # Authentication and user management
|
||||
│ │ ├── community.py # Community features and post management
|
||||
│ │ └── admin.py # Admin panel and analytics
|
||||
│ ├── templates/ # Jinja2 templates
|
||||
│ │ ├── base.html # Base template with navigation
|
||||
│ │ ├── index.html # Landing page with hero section
|
||||
│ │ ├── auth/ # Authentication templates
|
||||
│ │ ├── community/ # Community feature templates
|
||||
│ │ │ ├── index.html # Community homepage
|
||||
│ │ │ ├── new_post.html # Adventure creation form
|
||||
│ │ │ ├── edit_post.html # Adventure editing interface
|
||||
│ │ │ ├── post_detail.html # Detailed adventure view with maps
|
||||
│ │ │ └── profile.html # User profile dashboard
|
||||
│ │ ├── admin/ # Admin panel templates
|
||||
│ │ │ ├── dashboard.html # Analytics dashboard
|
||||
│ │ │ ├── posts.html # Post management
|
||||
│ │ │ └── users.html # User management
|
||||
│ │ └── errors/ # Error pages (404, 500)
|
||||
│ └── static/ # Static assets and media
|
||||
│ ├── favicon.ico
|
||||
│ └── images/
|
||||
├── config.py # Application configuration
|
||||
├── requirements.txt # Python dependencies
|
||||
├── run.py # Application entry point
|
||||
├── Dockerfile # Docker configuration
|
||||
└── docker-compose.yml # Docker Compose setup
|
||||
│ ├── images/ # App images and logos
|
||||
│ └── media/ # User-uploaded content
|
||||
│ └── posts/ # Post media organized by ID
|
||||
├── instance/ # Instance-specific files
|
||||
│ ├── uploads/ # Legacy upload directory
|
||||
│ └── *.db # SQLite database files
|
||||
├── migrations/ # Database migration scripts
|
||||
├── config.py # Multi-environment configuration
|
||||
├── requirements.txt # Python dependencies
|
||||
├── run.py # Application entry point
|
||||
├── manage_media.py # Media management utilities
|
||||
├── test_media.py # Media system tests
|
||||
├── Dockerfile # Docker configuration
|
||||
└── docker-compose.yml # Docker Compose setup
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Local Development
|
||||
|
||||
@@ -74,7 +138,7 @@ A Flask-based web application for motorcycle enthusiasts featuring adventure sto
|
||||
export FLASK_APP=run.py
|
||||
export FLASK_ENV=development
|
||||
export SECRET_KEY="your-secret-key-here"
|
||||
export DATABASE_URL="sqlite:///motorcycle_adventures.db"
|
||||
export DATABASE_URL="sqlite:///instance/moto_adventure_dev.db"
|
||||
```
|
||||
|
||||
5. **Initialize database**
|
||||
@@ -84,12 +148,23 @@ A Flask-based web application for motorcycle enthusiasts featuring adventure sto
|
||||
flask db upgrade
|
||||
```
|
||||
|
||||
6. **Run the application**
|
||||
6. **Create media directories**
|
||||
```bash
|
||||
mkdir -p app/static/media/posts
|
||||
mkdir -p instance/uploads
|
||||
```
|
||||
|
||||
7. **Run the application**
|
||||
```bash
|
||||
python run.py
|
||||
```
|
||||
|
||||
### Docker Deployment
|
||||
8. **Access the application**
|
||||
- Open http://localhost:5000 in your browser
|
||||
- Register a new account to start sharing adventures
|
||||
- Upload GPX files and photos to test the full functionality
|
||||
|
||||
### 🐳 Docker Deployment
|
||||
|
||||
1. **Build and run with Docker Compose**
|
||||
```bash
|
||||
@@ -100,60 +175,143 @@ A Flask-based web application for motorcycle enthusiasts featuring adventure sto
|
||||
- Web application: http://localhost:5000
|
||||
- PostgreSQL database: localhost:5432
|
||||
|
||||
## Configuration
|
||||
### 📱 Testing Features
|
||||
|
||||
1. **Create an Adventure Post**
|
||||
- Register and login to your account
|
||||
- Click "Share New Adventure"
|
||||
- Upload photos and GPX files
|
||||
- Add title, subtitle, and adventure story
|
||||
|
||||
2. **Explore Interactive Maps**
|
||||
- View adventure posts with GPS tracks
|
||||
- See route statistics and elevation data
|
||||
- Download GPX files for your own use
|
||||
|
||||
3. **Admin Panel Access**
|
||||
- Create admin user in database
|
||||
- Access /admin for content management
|
||||
- Review and approve community posts
|
||||
- PostgreSQL database: localhost:5432
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
The application supports multiple environments through environment variables:
|
||||
|
||||
### Core Settings
|
||||
- `FLASK_ENV`: development, testing, production
|
||||
- `SECRET_KEY`: Flask secret key for sessions
|
||||
- `DATABASE_URL`: Database connection string
|
||||
- `MAIL_SERVER`: SMTP server for email
|
||||
- `MAIL_USERNAME`: Email username
|
||||
- `MAIL_PASSWORD`: Email password
|
||||
- `SECRET_KEY`: Flask secret key for sessions and CSRF protection
|
||||
- `DATABASE_URL`: Database connection string (SQLite/PostgreSQL)
|
||||
|
||||
## Features Overview
|
||||
### Media & Upload Settings
|
||||
- `UPLOAD_FOLDER`: Directory for user uploads (default: instance/uploads)
|
||||
- `MEDIA_FOLDER`: Directory for processed media (default: app/static/media)
|
||||
- `MAX_CONTENT_LENGTH`: Maximum file upload size (default: 16MB)
|
||||
|
||||
### Landing Page
|
||||
- Hero section with stunning mountain panorama background
|
||||
- Call-to-action for adventure community
|
||||
- Featured adventures showcase
|
||||
- Pensiune BuonGusto accommodation promotion
|
||||
### Email Configuration
|
||||
- `MAIL_SERVER`: SMTP server for email notifications
|
||||
- `MAIL_USERNAME`: Email username for authentication
|
||||
- `MAIL_PASSWORD`: Email password or app-specific password
|
||||
- `MAIL_USE_TLS`: Enable TLS encryption (default: True)
|
||||
|
||||
### Authentication System
|
||||
- User registration with email validation
|
||||
- Secure login/logout functionality
|
||||
- Password hashing with Werkzeug
|
||||
- Session management with Flask-Login
|
||||
### Development Settings
|
||||
- `FLASK_DEBUG`: Enable debug mode (development only)
|
||||
- `WTF_CSRF_ENABLED`: Enable CSRF protection (default: True)
|
||||
|
||||
### Community Features
|
||||
- Story and track sharing
|
||||
- User profiles
|
||||
- Image uploads for adventures
|
||||
- GPX file support for route sharing
|
||||
- Comment system
|
||||
- Like/rating system
|
||||
## 🌟 Features Overview
|
||||
|
||||
### Admin Panel
|
||||
- User management
|
||||
- Content moderation
|
||||
- Analytics dashboard
|
||||
- System configuration
|
||||
### 🏠 Enhanced Landing Page
|
||||
- **Hero Section**: Stunning Transalpina panorama background with glass morphism overlay
|
||||
- **Featured Adventures**: Carousel showcasing community highlights
|
||||
- **Statistics Dashboard**: Live counters for users, adventures, and community engagement
|
||||
- **Call-to-Action**: Prominent registration and exploration prompts
|
||||
- **Pensiune BuonGusto**: Integrated accommodation promotion
|
||||
|
||||
## Contributing
|
||||
### 🔐 Advanced Authentication System
|
||||
- **Secure Registration**: Email validation with password strength requirements
|
||||
- **Session Management**: Persistent login with "Remember Me" functionality
|
||||
- **Password Security**: Bcrypt hashing with salt for maximum security
|
||||
- **User Profiles**: Comprehensive profile management with statistics
|
||||
- **Role-Based Access**: Granular permissions for users and administrators
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
||||
4. Push to the branch (`git push origin feature/amazing-feature`)
|
||||
5. Open a Pull Request
|
||||
### 🗺️ Interactive Community Features
|
||||
- **Rich Post Creation**: Multi-step form with media upload and GPS integration
|
||||
- **Adventure Gallery**: Beautiful grid layout with filtering and search
|
||||
- **Real-time Engagement**: Like system with instant feedback
|
||||
- **Threaded Comments**: Nested discussion system for each adventure
|
||||
- **Social Sharing**: Easy sharing of favorite adventures
|
||||
- **Content Moderation**: Admin approval workflow for quality control
|
||||
|
||||
## License
|
||||
### 📊 Comprehensive Admin Panel
|
||||
- **Analytics Dashboard**: User growth, post statistics, and engagement metrics
|
||||
- **User Management**: Detailed user profiles with activity tracking
|
||||
- **Content Review**: Streamlined post approval and moderation tools
|
||||
- **System Health**: Monitoring and configuration management
|
||||
- **Bulk Operations**: Efficient management of multiple users or posts
|
||||
|
||||
### 🎨 Modern Design System
|
||||
- **Tailwind CSS**: Utility-first CSS framework with custom components
|
||||
- **Glass Morphism**: Modern translucent effects and backdrop blur
|
||||
- **Gradient Backgrounds**: Beautiful color transitions throughout the app
|
||||
- **Micro-interactions**: Smooth animations and hover effects
|
||||
- **Accessibility**: WCAG compliant design with keyboard navigation support
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
We welcome contributions from the motorcycle community! Here's how you can help:
|
||||
|
||||
1. **Fork the repository**
|
||||
2. **Create a feature branch** (`git checkout -b feature/amazing-feature`)
|
||||
3. **Commit your changes** (`git commit -m 'Add some amazing feature'`)
|
||||
4. **Push to the branch** (`git push origin feature/amazing-feature`)
|
||||
5. **Open a Pull Request**
|
||||
|
||||
### Development Guidelines
|
||||
- Follow PEP 8 style guidelines for Python code
|
||||
- Use semantic commit messages
|
||||
- Add tests for new features
|
||||
- Update documentation for any changes
|
||||
- Ensure mobile responsiveness for UI changes
|
||||
|
||||
### Reporting Issues
|
||||
- Use the GitHub issue tracker
|
||||
- Include steps to reproduce the problem
|
||||
- Provide browser and device information
|
||||
- Include screenshots for UI issues
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the MIT License - see the LICENSE file for details.
|
||||
|
||||
## Acknowledgments
|
||||
## 🙏 Acknowledgments
|
||||
|
||||
- Beautiful Transalpina panorama background image
|
||||
- Tailwind CSS for responsive design
|
||||
- Flask community for excellent documentation
|
||||
- Bootstrap icons for UI elements
|
||||
- **Beautiful Transalpina Panorama**: Stunning background image showcasing Romania's scenic highway
|
||||
- **Tailwind CSS**: For providing an excellent utility-first CSS framework
|
||||
- **Leaflet.js**: For powerful and flexible mapping capabilities
|
||||
- **Flask Community**: For comprehensive documentation and support
|
||||
- **OpenStreetMap**: For providing free and open map data
|
||||
- **Font Awesome**: For beautiful and consistent iconography
|
||||
- **Motorcycle Community**: For inspiration and feedback during development
|
||||
|
||||
## 🚧 Roadmap
|
||||
|
||||
### Upcoming Features
|
||||
- [ ] **Real-time Chat**: Live messaging between community members
|
||||
- [ ] **Event System**: Motorcycle meetups and group ride organization
|
||||
- [ ] **Weather Integration**: Route weather conditions and forecasts
|
||||
- [ ] **Mobile App**: Native iOS and Android applications
|
||||
- [ ] **Advanced Search**: Filter adventures by location, difficulty, and tags
|
||||
- [ ] **Social Features**: Follow users and curated adventure feeds
|
||||
- [ ] **Offline Support**: PWA capabilities for offline map viewing
|
||||
|
||||
### Performance Improvements
|
||||
- [ ] **Image Optimization**: WebP conversion and lazy loading
|
||||
- [ ] **Caching**: Redis integration for improved performance
|
||||
- [ ] **CDN Integration**: Asset delivery optimization
|
||||
- [ ] **Database Optimization**: Query optimization and indexing
|
||||
|
||||
---
|
||||
|
||||
**Built with ❤️ for the motorcycle adventure community**
|
||||
|
||||
For support or questions, please open an issue or contact the development team.
|
||||
|
||||
@@ -190,6 +190,80 @@ def user_detail(user_id):
|
||||
user_posts=user_posts,
|
||||
user_comments=user_comments)
|
||||
|
||||
@admin.route('/users/<int:user_id>/toggle-status', methods=['POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def toggle_user_status(user_id):
|
||||
"""Toggle user active/inactive status"""
|
||||
user = User.query.get_or_404(user_id)
|
||||
|
||||
# Prevent self-modification if current user is admin
|
||||
if user.is_admin and user.id == current_user.id:
|
||||
return jsonify({'success': False, 'error': 'Cannot deactivate your own admin account'})
|
||||
|
||||
# Toggle status
|
||||
user.is_active = not user.is_active
|
||||
status = "activated" if user.is_active else "deactivated"
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'User {user.nickname} has been {status}',
|
||||
'is_active': user.is_active
|
||||
})
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'success': False, 'error': f'Failed to update user status: {str(e)}'})
|
||||
|
||||
@admin.route('/users/<int:user_id>/delete', methods=['DELETE', 'POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def delete_user(user_id):
|
||||
"""Delete user and transfer their posts to admin"""
|
||||
user = User.query.get_or_404(user_id)
|
||||
|
||||
# Prevent self-deletion
|
||||
if user.id == current_user.id:
|
||||
return jsonify({'success': False, 'error': 'Cannot delete your own account'})
|
||||
|
||||
# Prevent deletion of other admins
|
||||
if user.is_admin:
|
||||
return jsonify({'success': False, 'error': 'Cannot delete admin accounts'})
|
||||
|
||||
try:
|
||||
# Transfer all user's posts to current admin
|
||||
user_posts = Post.query.filter_by(author_id=user.id).all()
|
||||
for post in user_posts:
|
||||
post.author_id = current_user.id
|
||||
|
||||
# Transfer all user's comments to current admin
|
||||
user_comments = Comment.query.filter_by(author_id=user.id).all()
|
||||
for comment in user_comments:
|
||||
comment.author_id = current_user.id
|
||||
|
||||
# Delete user's likes (they will be orphaned)
|
||||
Like.query.filter_by(user_id=user.id).delete()
|
||||
|
||||
# Delete user's page views
|
||||
PageView.query.filter_by(user_id=user.id).delete()
|
||||
|
||||
username = user.nickname
|
||||
user_posts_count = len(user_posts)
|
||||
|
||||
# Delete the user
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'User {username} has been deleted. {user_posts_count} posts transferred to {current_user.nickname}.'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
return jsonify({'success': False, 'error': f'Failed to delete user: {str(e)}'})
|
||||
|
||||
@admin.route('/analytics')
|
||||
@login_required
|
||||
@admin_required
|
||||
|
||||
@@ -74,8 +74,17 @@
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
{% if not user.is_admin or current_user.id != user.id %}
|
||||
<button class="btn btn-sm btn-outline-warning" title="Toggle Status" disabled>
|
||||
<i class="fas fa-toggle-on"></i>
|
||||
<button class="btn btn-sm btn-outline-warning"
|
||||
title="{{ 'Deactivate User' if user.is_active else 'Activate User' }}"
|
||||
onclick="toggleUserStatus({{ user.id }}, '{{ user.nickname }}', {{ user.is_active|lower }})">
|
||||
<i class="fas fa-{{ 'toggle-on' if user.is_active else 'toggle-off' }}"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if not user.is_admin and current_user.id != user.id %}
|
||||
<button class="btn btn-sm btn-outline-danger"
|
||||
title="Delete User"
|
||||
onclick="confirmDeleteUser({{ user.id }}, '{{ user.nickname }}', {{ user.posts.count() }})">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -134,4 +143,88 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
function toggleUserStatus(userId, nickname, currentStatus) {
|
||||
const action = currentStatus ? 'deactivate' : 'activate';
|
||||
const message = `Are you sure you want to ${action} user "${nickname}"?`;
|
||||
|
||||
if (confirm(message)) {
|
||||
fetch(`/admin/users/${userId}/toggle-status`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showAlert(data.message, 'success');
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
showAlert(data.message || 'An error occurred', 'danger');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showAlert('An error occurred while updating user status', 'danger');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function confirmDeleteUser(userId, nickname, postsCount) {
|
||||
let message = `Are you sure you want to delete user "${nickname}"?`;
|
||||
if (postsCount > 0) {
|
||||
message += `\n\nThis user has ${postsCount} post(s) that will be transferred to you as the admin.`;
|
||||
}
|
||||
message += '\n\nThis action cannot be undone.';
|
||||
|
||||
if (confirm(message)) {
|
||||
fetch(`/admin/users/${userId}/delete`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showAlert(data.message, 'success');
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
showAlert(data.message || 'An error occurred', 'danger');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
showAlert('An error occurred while deleting user', 'danger');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function showAlert(message, type) {
|
||||
// Create alert container if it doesn't exist
|
||||
let alertContainer = document.querySelector('.alert-container');
|
||||
if (!alertContainer) {
|
||||
alertContainer = document.createElement('div');
|
||||
alertContainer.className = 'alert-container mt-3';
|
||||
document.querySelector('.container-fluid').insertBefore(
|
||||
alertContainer,
|
||||
document.querySelector('.container-fluid').firstChild
|
||||
);
|
||||
}
|
||||
|
||||
alertContainer.innerHTML = `
|
||||
<div class="alert alert-${type} alert-dismissible fade show" role="alert">
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
0
migrate_database.py
Normal file
0
migrate_database.py
Normal file
Reference in New Issue
Block a user